const test = require("node:test") const assert = require("node:assert") const childProcess = require("node:child_process") const fs = require("node:fs") const os = require("node:os") const path = require("node:path") const P = require("./load.js") // A stand-in for Hyprland's HyprlandToplevel. The shapes here are copied from // what the compositor actually sent during the capture spike, ugly Chrome // classes and all. function win(opts) { const o = opts || {} return { address: o.address || "0x1", title: o.title === undefined ? "" : o.title, urgent: o.urgent === true, activated: o.activated === true, workspace: o.workspace === null ? null : { id: o.ws === undefined ? 1 : o.ws, name: o.wsName }, lastIpcObject: o.ipc === null ? null : Object.assign( { class: o.cls === undefined ? "foot" : o.cls, size: o.size === null ? null : (o.size || [1600, 1000]), at: o.at === null ? null : (o.at || [0, 0]) }, o.ipc || {} ) } } // --------------------------------------------------------------- safe reads test("ipc reads survive missing and malformed compositor data", () => { // A throw here would take down the whole omarchy-shell process. assert.strictEqual(P.windowClass(null), "") assert.strictEqual(P.windowClass({}), "") assert.strictEqual(P.windowClass({ lastIpcObject: null }), "") assert.strictEqual(P.windowClass({ lastIpcObject: "not-an-object" }), "") assert.strictEqual(P.windowSize(win({ size: [0, 100] })), null) assert.strictEqual(P.windowSize(win({ size: ["a", "b"] })), null) assert.strictEqual(P.windowSize(win({ size: [100] })), null) assert.strictEqual(P.windowSize(win({ size: null })), null) assert.strictEqual(P.windowRect(win({ at: null })), null) }) test("aspect ratio falls back to a plausible window shape, never a square", () => { assert.strictEqual(P.aspectRatio(null), 1.6) assert.strictEqual(P.aspectRatio(win({ size: [1600, 1000] })), 1.6) // Extreme ratios are clamped so one freak window cannot break a row. assert.strictEqual(P.aspectRatio(win({ size: [10000, 100] })), 6.0) assert.strictEqual(P.aspectRatio(win({ size: [100, 10000] })), 0.25) }) test("configuration reads accept only a bounded number of bytes", (t) => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "switchboard-config-")) t.after(() => fs.rmSync(dir, { recursive: true, force: true })) const maxBytes = 12 * 1024 const valid = path.join(dir, "valid.json") fs.writeFileSync(valid, '{"liveCaptures":4}') function spawnReader(file) { const command = P.configReadCommand(file, maxBytes) return childProcess.spawnSync(command[0], command.slice(1), { encoding: "utf8", maxBuffer: 64 * 1024, timeout: 3000 }) } function read(file) { const result = spawnReader(file) assert.strictEqual(result.status, 0, result.stderr) return P.classifyConfigPayload(result.stdout, maxBytes) } const accepted = read(valid) assert.strictEqual(accepted.status, "data") assert.strictEqual(Buffer.from(accepted.encoded, "base64").toString("utf8"), '{"liveCaptures":4}') const oversized = path.join(dir, "oversized.json") fs.writeFileSync(oversized, "x".repeat(1024 * 1024)) assert.strictEqual(read(oversized).status, "oversized") const linked = path.join(dir, "linked.json") fs.symlinkSync(oversized, linked) assert.strictEqual(read(linked).status, "oversized") const fifo = path.join(dir, "blocking.json") const mkfifo = childProcess.spawnSync("mkfifo", [fifo]) assert.strictEqual(mkfifo.status, 0, mkfifo.stderr && mkfifo.stderr.toString()) const started = Date.now() assert.strictEqual(read(fifo).status, "error") assert.ok(Date.now() - started < 2500, "blocking targets must time out") // The child emits only maxBytes + 1 source bytes, even for a much larger // target. Base64 is the fixed 4/3 expansion plus the one-byte protocol tag. const raw = spawnReader(linked) assert.ok(raw.stdout.length <= 1 + 4 * Math.ceil((maxBytes + 1) / 3)) }) // ------------------------------------------------------------ display naming test("chrome web-app classes become names a person would use", () => { // Both of these came verbatim off this machine during the spike. assert.strictEqual(P.prettyClass("chrome-x.com__-Default"), "X") assert.strictEqual( P.prettyClass("chrome-__home_bmw_Work_pocket_docs_demo_pocket-demo.html-Default"), "Pocket demo" ) assert.strictEqual(P.prettyClass("chrome-github.com__-Default"), "Github") assert.strictEqual(P.prettyClass("chrome-mail.google.com__-Profile 2"), "Mail") // A generic host prefix is skipped in favour of the label that names the app. assert.strictEqual(P.prettyClass("chrome-app.slack.com__-Default"), "Slack") assert.strictEqual(P.prettyClass("chrome-web.whatsapp.com__-Default"), "Whatsapp") }) test("ordinary classes are tidied without being mangled", () => { assert.strictEqual(P.prettyClass("foot"), "Foot") assert.strictEqual(P.prettyClass("chromium"), "Chromium") assert.strictEqual(P.prettyClass("org.gnome.Nautilus"), "Nautilus") assert.strictEqual(P.prettyClass(""), "") assert.strictEqual(P.prettyClass(null), "") // Deliberate internal casing is left alone rather than "corrected". assert.strictEqual(P.prettyClass("VLC"), "VLC") assert.strictEqual(P.prettyClass("jetbrains-idea"), "Jetbrains idea") }) // -------------------------------------------------------------- fuzzy filter test("an empty query matches everything with no highlight", () => { const r = P.fuzzyMatch("", "Firefox") assert.strictEqual(r.matched, true) assert.deepStrictEqual(r.ranges, []) }) test("substring hits beat scattered hits and report exact ranges", () => { const direct = P.fuzzyMatch("fire", "Firefox") assert.strictEqual(direct.matched, true) assert.deepStrictEqual(direct.ranges, [[0, 4]]) const scattered = P.fuzzyMatch("ffx", "Firefox") assert.strictEqual(scattered.matched, true) assert.ok(direct.score > scattered.score, "a prefix hit must outrank a subsequence hit") }) test("subsequence matching finds initials inside a name", () => { const r = P.fuzzyMatch("chgpt", "ChatGPT") assert.strictEqual(r.matched, true) assert.ok(r.ranges.length > 0) }) test("a query with a character the text lacks does not match", () => { assert.strictEqual(P.fuzzyMatch("fzz", "Firefox").matched, false) assert.strictEqual(P.fuzzyMatch("anything", "").matched, false) }) test("highlight ranges stay inside the string and never overlap", () => { const text = "Firefox Developer Edition" for (const q of ["fire", "fde", "dev", "editon", "ffx"]) { const r = P.fuzzyMatch(q, text) if (!r.matched) continue let prevEnd = -1 for (const [start, end] of r.ranges) { assert.ok(start >= 0 && end <= text.length, `${q}: range out of bounds`) assert.ok(start < end, `${q}: empty or inverted range`) assert.ok(start >= prevEnd, `${q}: ranges overlap or run backwards`) prevEnd = end } } }) test("the app name outranks the window title", () => { // Typing "fire" should find Firefox itself before a page about forest fires. const browser = win({ cls: "firefox", title: "Hacker News" }) const article = win({ cls: "foot", title: "wildfire report" }) const a = P.matchWindow("fire", browser) const b = P.matchWindow("fire", article) assert.strictEqual(a.matched, true) assert.strictEqual(b.matched, true) assert.ok(a.score > b.score, "app-name match must score above a title match") assert.strictEqual(a.matchField === undefined ? a.field : a.field, "app") }) test("the raw class is a last-resort match for renamed web apps", () => { // prettyClass turns this into "X", so "chrome" only survives via raw class. const webApp = win({ cls: "chrome-x.com__-Default", title: "Home / X" }) assert.strictEqual(P.matchWindow("chrome", webApp).matched, true) // ...and it contributes no highlight, since the chip never shows raw class. assert.deepStrictEqual(P.matchWindow("chrome", webApp).ranges, []) }) // ------------------------------------------------------------------ grouping test("windows without a workspace are not real desktop windows", () => { // The capture spike saw exactly one of these: the shell's own surface. assert.strictEqual(P.isRealWindow(win({ workspace: null })), false) assert.strictEqual(P.isRealWindow(win({ ws: 0 })), false) assert.strictEqual(P.isRealWindow(null), false) assert.strictEqual(P.isRealWindow(win({ ws: 3 })), true) assert.strictEqual(P.isRealWindow(win({ ws: -99 })), true, "scratchpads are real") }) test("scratchpad workspaces sink below numbered ones", () => { const sorted = [{ id: 3 }, { id: -99 }, { id: 1 }].sort(P.compareWorkspaces) assert.deepStrictEqual(sorted.map((w) => w.id), [1, 3, -99]) }) test("workspace labels only show a name when it adds information", () => { assert.strictEqual(P.workspaceLabel({ id: 3, name: "3" }), "Workspace 3") assert.strictEqual(P.workspaceLabel({ id: 3, name: "" }), "Workspace 3") assert.strictEqual(P.workspaceLabel({ id: 3, name: "code" }), "code") assert.strictEqual(P.workspaceLabel({ id: -99, name: "special:magic" }), "magic") assert.strictEqual(P.workspaceLabel(null), "Workspace") }) test("buildModel groups by workspace in display order", () => { const model = P.buildModel([ win({ address: "0xa", ws: 3, cls: "foot" }), win({ address: "0xb", ws: 1, cls: "firefox" }), win({ address: "0xc", ws: -99, cls: "slack" }), win({ address: "0xd", ws: 1, cls: "chromium" }), win({ address: "0xe", workspace: null, cls: "" }) ], 3) assert.deepStrictEqual(model.sections.map((s) => s.id), [1, 3, -99]) assert.strictEqual(model.total, 4, "the workspace-less surface is excluded") assert.strictEqual(model.sections[0].windows.length, 2) assert.strictEqual(model.sections[1].active, true, "workspace 3 is active") assert.strictEqual(model.sections[0].active, false) }) test("buildModel carries no query state, so delegates survive typing", () => { // Structure and match state are split precisely so that a keystroke cannot // force QML to rebuild delegates and restart every live capture on screen. const model = P.buildModel([win({ address: "0xa", ws: 1 })], 1) const entry = model.sections[0].windows[0] assert.strictEqual(entry.matched, undefined) assert.strictEqual(entry.badge, undefined) assert.ok(entry.toplevel, "the toplevel handle rides along for capture") }) test("scratchpad workspaces can be excluded entirely", () => { const windows = [win({ address: "0xa", ws: 1 }), win({ address: "0xb", ws: -99 })] assert.strictEqual(P.buildModel(windows, 1).total, 2) assert.strictEqual(P.buildModel(windows, 1, { includeScratchpad: false }).total, 1) assert.deepStrictEqual( P.buildModel(windows, 1, { includeScratchpad: false }).sections.map((s) => s.id), [1] ) }) test("badges number matches in reading order and stop at nine", () => { const many = [] for (let i = 0; i < 12; i++) many.push(win({ address: "0x" + i, ws: 1, cls: "foot" })) const state = P.applyQuery(P.buildModel(many, 1), "") const badges = many.map((w) => state.states[w.address].badge) assert.deepStrictEqual(badges, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0]) }) test("a badge digit resolves back to exactly one window", () => { const model = P.buildModel([ win({ address: "0xa", ws: 1, cls: "foot" }), win({ address: "0xb", ws: 1, cls: "firefox" }) ], 1) const state = P.applyQuery(model, "") assert.strictEqual(P.addressForBadge(state, 1), "0xa") assert.strictEqual(P.addressForBadge(state, 2), "0xb") assert.strictEqual(P.addressForBadge(state, 7), "", "an unassigned digit does nothing") assert.strictEqual(P.addressForBadge(null, 1), "") }) test("filtered-out windows keep their place but lose their badge", () => { const model = P.buildModel([ win({ address: "0xa", ws: 1, cls: "foot", title: "terminal" }), win({ address: "0xb", ws: 1, cls: "firefox", title: "news" }), win({ address: "0xc", ws: 1, cls: "foot", title: "logs" }) ], 1) const state = P.applyQuery(model, "fire") const windows = model.sections[0].windows assert.strictEqual(model.total, 3, "every window still renders") assert.strictEqual(state.matched, 1) // Position is preserved — this is what protects the user's spatial memory. assert.strictEqual(windows[1].address, "0xb") assert.strictEqual(state.states["0xb"].matched, true) assert.strictEqual(state.states["0xb"].badge, 1, "the only match takes badge 1") assert.strictEqual(state.states["0xa"].matched, false) assert.strictEqual(state.states["0xa"].badge, 0, "a dimmed tile must not offer a key") assert.strictEqual(state.states["0xc"].badge, 0) }) test("every window gets a state entry, matched or not", () => { // A tile with no entry would fall back to neutral and become invisibly // reachable; the overlay relies on this being total. const model = P.buildModel([ win({ address: "0xa", ws: 1, cls: "foot" }), win({ address: "0xb", ws: 2, cls: "firefox" }) ], 1) const state = P.applyQuery(model, "zzzz") assert.deepStrictEqual(Object.keys(state.states).sort(), ["0xa", "0xb"]) assert.strictEqual(state.matched, 0) assert.deepStrictEqual(state.order, []) }) test("the neutral state is visible and reachable", () => { const n = P.neutralState() assert.strictEqual(n.matched, true) assert.strictEqual(n.badge, 0) assert.deepStrictEqual(n.ranges, []) }) test("cursor traversal visits matches only, in reading order", () => { const model = P.buildModel([ win({ address: "0xa", ws: 2, cls: "firefox" }), win({ address: "0xb", ws: 1, cls: "foot" }), win({ address: "0xc", ws: 1, cls: "firefox" }) ], 1) // Workspace 1 sorts before 2, so 0xc precedes 0xa despite input order. assert.deepStrictEqual(P.applyQuery(model, "fire").order, ["0xc", "0xa"]) assert.deepStrictEqual(P.applyQuery(model, "").order, ["0xb", "0xc", "0xa"]) assert.deepStrictEqual(P.applyQuery(null, "").order, []) }) test("buildModel tolerates junk input without throwing", () => { assert.strictEqual(P.buildModel(null, 1).total, 0) assert.strictEqual(P.buildModel([null, undefined, {}], 1).total, 0) assert.strictEqual(P.applyQuery(P.buildModel(null, 1), "x").matched, 0) }) // ------------------------------------------------------- justified row layout test("a full row fills the container width exactly", () => { const items = [] for (let i = 0; i < 4; i++) items.push({ aspect: 1.6, item: i }) const rows = P.justify(items, 1000, { targetHeight: 200, gap: 12 }) assert.strictEqual(rows.length, 1) const row = rows[0] const used = row.cells.reduce((sum, c) => sum + c.width, 0) + 12 * (row.cells.length - 1) assert.strictEqual(used, 1000, "rounding drift must be absorbed, not left as a gap") }) test("windows keep their true aspect ratio within a row", () => { const rows = P.justify([ { aspect: 0.6, item: "tall-terminal" }, { aspect: 2.4, item: "wide-browser" } ], 1000, { targetHeight: 200, gap: 12 }) const cells = rows[0].cells const h = rows[0].height // This is the whole point of justified layout: a tall window stays tall. assert.ok(Math.abs(cells[0].width / h - 0.6) < 0.05) assert.ok(Math.abs(cells[1].width / h - 2.4) < 0.05) assert.ok(cells[1].width > cells[0].width * 3) }) test("a short last row is not stretched to full width", () => { const items = [] for (let i = 0; i < 5; i++) items.push({ aspect: 1.6, item: i }) const rows = P.justify(items, 1000, { targetHeight: 200, gap: 12 }) assert.ok(rows.length >= 2) const last = rows[rows.length - 1] const used = last.cells.reduce((sum, c) => sum + c.width, 0) assert.ok(used < 1000, "a stretched last row is the classic gallery bug") assert.ok(last.height <= 200, "the last row never exceeds the target height") }) test("row heights stay within the clamp so no tile dominates", () => { const items = [] for (let i = 0; i < 9; i++) items.push({ aspect: 1.6, item: i }) const rows = P.justify(items, 900, { targetHeight: 200, gap: 12, minHeight: 124, maxHeight: 290 }) for (const row of rows) { assert.ok(row.height >= 124 && row.height <= 290, `row height ${row.height} escaped the clamp`) } }) test("every input window appears exactly once in the output", () => { const items = [] for (let i = 0; i < 17; i++) items.push({ aspect: 0.5 + (i % 5) * 0.5, item: i }) const rows = P.justify(items, 1400, { targetHeight: 210, gap: 14 }) // Each cell carries the whole input object it came from, not just its payload. const seen = rows.flatMap((r) => r.cells.map((c) => c.item.item)) assert.strictEqual(seen.length, 17, "no window may be dropped or duplicated") assert.deepStrictEqual(seen.slice().sort((a, b) => a - b), items.map((i) => i.item)) }) test("layout degenerates safely", () => { assert.deepStrictEqual(P.justify([], 1000, {}), []) assert.deepStrictEqual(P.justify(null, 1000, {}), []) // A zero-width container happens for one frame during overlay open. assert.deepStrictEqual(P.justify([{ aspect: 1.6 }], 0, {}), []) // A single very wide window must still produce one usable cell. const rows = P.justify([{ aspect: 6.0, item: "ultrawide" }], 800, { targetHeight: 200, gap: 12 }) assert.strictEqual(rows.length, 1) assert.ok(rows[0].cells[0].width > 0 && rows[0].height > 0) }) // --------------------------------------------------------------- highlighting test("highlight markup escapes everything a window title can contain", () => { // A page controls its own title, and that title reaches a rich-text renderer. const evil = ' & "quoted"' const out = P.highlightHtml(evil, [], "#ff0000") assert.ok(!out.includes(" { const out = P.highlightHtml("Firefox", [[0, 4]], "#abcdef") assert.strictEqual(out, 'Firefox') }) test("highlight handles multiple, empty, and out-of-bounds ranges", () => { assert.strictEqual(P.highlightHtml("abcdef", [], "#fff"), "abcdef") assert.strictEqual(P.highlightHtml("", [[0, 3]], "#fff"), "") // A stale range from a previous query must clamp, not slice past the end. const clamped = P.highlightHtml("abc", [[1, 99]], "#fff") assert.strictEqual(clamped, 'abc') const two = P.highlightHtml("abcdef", [[0, 1], [3, 4]], "#fff") assert.strictEqual(two, 'abcdef') }) test("highlight ignores malformed range data instead of throwing", () => { assert.strictEqual(P.highlightHtml("abc", [null, "x", [0, 1]], "#fff"), 'abc') assert.strictEqual(P.highlightHtml("abc", "not-an-array", "#fff"), "abc") }) test("titles are stripped of spinner and progress churn", () => { // Verbatim from this machine during the capture spike. assert.strictEqual(P.cleanTitle("⠋ - Waiting for response… - grok"), "Waiting for response… - grok") assert.strictEqual(P.cleanTitle("◑ Omarchy plugin design"), "Omarchy plugin design") assert.strictEqual(P.cleanTitle(" spaced out "), "spaced out") assert.strictEqual(P.cleanTitle(null), "") assert.strictEqual(P.cleanTitle("Normal Title"), "Normal Title") }) // ------------------------------------------------------- structure signature test("the signature is stable for an unchanged desktop", () => { // Stability is the whole point: a new signature rebuilds every delegate and // restarts every live capture on screen. const windows = [win({ address: "0xa", ws: 1 }), win({ address: "0xb", ws: 2 })] assert.strictEqual( P.structureSignature(windows, 1), P.structureSignature(windows, 1) ) // Reordering the same windows is not a change. assert.strictEqual( P.structureSignature(windows, 1), P.structureSignature([windows[1], windows[0]], 1) ) // Nor is a title changing — spinner characters tick several times a second. const renamed = [win({ address: "0xa", ws: 1, title: "new title" }), windows[1]] assert.strictEqual(P.structureSignature(windows, 1), P.structureSignature(renamed, 1)) }) test("the signature changes for anything that moves a tile", () => { const base = [win({ address: "0xa", ws: 1 })] const sig = P.structureSignature(base, 1) assert.notStrictEqual(sig, P.structureSignature(base, 2), "active workspace") assert.notStrictEqual(sig, P.structureSignature([win({ address: "0xa", ws: 5 })], 1), "moved workspace") assert.notStrictEqual(sig, P.structureSignature([win({ address: "0xa", ws: 1, size: [800, 600] })], 1), "resized") assert.notStrictEqual(sig, P.structureSignature([win({ address: "0xa", ws: 1, urgent: true })], 1), "urgency") assert.notStrictEqual(sig, P.structureSignature(base.concat([win({ address: "0xb", ws: 1 })]), 1), "opened") assert.notStrictEqual(sig, P.structureSignature([], 1), "closed") }) test("a toplevel gaining its workspace late registers as a change", () => { // Hyprland delivers the toplevel before its workspace; the capture spike saw // exactly this. Without it registering, the overview would stay empty. const pending = [win({ address: "0xa", workspace: null })] const settled = [win({ address: "0xa", ws: 3 })] assert.notStrictEqual(P.structureSignature(pending, 3), P.structureSignature(settled, 3)) }) test("the signature tolerates junk without throwing", () => { assert.strictEqual(typeof P.structureSignature(null, 1), "string") assert.strictEqual(typeof P.structureSignature([null, {}, undefined], 1), "string") }) // ----------------------------------------------------------- QML boundary test("array-like model values are accepted, not silently dropped", () => { // Quickshell's UntypedObjectModel.values is indexable and has a length, but // fails Array.isArray. Rejecting it produced an overview that was reliably, // silently empty on a desktop full of windows. const arrayLike = { length: 2, 0: win({ address: "0xa", ws: 1 }), 1: win({ address: "0xb", ws: 2 }) } assert.strictEqual(P.toArray(arrayLike).length, 2) assert.strictEqual(P.buildModel(arrayLike, 1).total, 2) assert.notStrictEqual(P.structureSignature(arrayLike, 1), P.structureSignature([], 1)) const rows = P.justify({ length: 2, 0: { aspect: 1.6 }, 1: { aspect: 1.6 } }, 1000, { targetHeight: 200 }) assert.strictEqual(rows.length, 1) assert.strictEqual(rows[0].cells.length, 2) }) test("toArray rejects things that are not lists", () => { assert.deepStrictEqual(P.toArray(null), []) assert.deepStrictEqual(P.toArray(undefined), []) assert.deepStrictEqual(P.toArray(42), []) assert.deepStrictEqual(P.toArray({ nope: true }), []) const real = [1, 2] assert.strictEqual(P.toArray(real), real, "a real array passes through untouched") }) // ------------------------------------------------------ compositor commands test("addresses are normalised to the form Hyprland selectors require", () => { // Quickshell drops the 0x; Hyprland needs it. An unmatched selector is not // an error, so getting this wrong makes every action fail silently. assert.strictEqual(P.hyprAddress("556b5b461e50"), "0x556b5b461e50") assert.strictEqual(P.hyprAddress("0x556b5b461e50"), "0x556b5b461e50") assert.strictEqual(P.hyprAddress(" 556b "), "0x556b") assert.strictEqual(P.hyprAddress(""), "") assert.strictEqual(P.hyprAddress(null), "") }) test("focus uses the Lua dispatcher form Hyprland 0.56 expects", () => { // Verified against the running compositor: the old // "focuswindow address:0x…" string form is a Lua parse error now. assert.strictEqual( P.focusCommand("556b5b461e50"), 'hl.dsp.focus({ window = "address:0x556b5b461e50" })' ) assert.strictEqual(P.focusCommand(""), "", "an empty address builds no command") }) test("move targets one window and does not drag the view along", () => { assert.strictEqual( P.moveCommand("556b5b461e50", 3), 'hl.dsp.window.move({ window = "address:0x556b5b461e50", workspace = "3", follow = false })' ) // Scratchpads are addressed by name, not number. assert.ok(P.moveCommand("0xabc", "special:scratchpad").includes('workspace = "special:scratchpad"')) assert.strictEqual(P.moveCommand("", 3), "") assert.strictEqual(P.moveCommand("0xabc", ""), "") assert.strictEqual(P.moveCommand("0xabc", null), "") }) // -------------------------------------------------- packer invariants test("no row is ever wider than its container", () => { // Clamping a row's height above its exact fit made rows overflow, and the // drift absorber then hid it by squashing one tile's width — which silently // destroyed that window's aspect ratio. const aspects = [0.4, 0.7, 1.0, 1.6, 2.4, 3.5, 5.0] for (const width of [260, 400, 829, 1200, 3384]) { for (const target of [110, 200, 320, 487, 640]) { const items = aspects.map((a, i) => ({ aspect: a, item: i })) for (const rows of [P.justify(items, width, { targetHeight: target, gap: 14 })]) { for (const row of rows) { const used = row.cells.reduce((s, c) => s + c.width, 0) + 14 * (row.cells.length - 1) assert.ok(used <= width, `w=${width} target=${target}: row of ${row.cells.length} used ${used}px`) } } } } }) test("every tile keeps the aspect ratio of its window", () => { const items = [ { aspect: 1.7, item: "a" }, { aspect: 1.7, item: "b" }, { aspect: 1.7, item: "c" }, { aspect: 1.7, item: "d" } ] // The exact case that produced a 302x302 tile from a 1.7-aspect window. const rows = P.justify(items, 829, { targetHeight: 487, gap: 14 }) for (const row of rows) { for (const cell of row.cells) { const ratio = cell.width / cell.height assert.ok(Math.abs(ratio - 1.7) < 0.06, `expected ~1.7, got ${ratio.toFixed(2)} (${cell.width}x${cell.height})`) } } }) test("window geometry survives Qt's array-like list values", () => { // lastIpcObject.size and .at arrive as QVariantLists: indexable, with a // length, but not real Arrays. Rejecting them made every window fall back to // the placeholder 1.6 aspect, so every tile rendered the same shape and the // captures letterboxed inside frames that did not match them. const arrayLike = (a, b) => ({ length: 2, 0: a, 1: b }) const tl = { address: "0xa", workspace: { id: 1 }, lastIpcObject: { class: "chromium", size: arrayLike(1390, 1390), at: arrayLike(10, 20) } } assert.deepStrictEqual(P.windowSize(tl), { width: 1390, height: 1390 }) assert.strictEqual(P.aspectRatio(tl), 1.0, "a square window must report a square aspect") assert.deepStrictEqual(P.windowRect(tl), { x: 10, y: 20, width: 1390, height: 1390 }) }) test("differently shaped windows produce differently shaped tiles", () => { const shaped = (w, h) => ({ address: "0x" + w, workspace: { id: 1 }, lastIpcObject: { class: "app", size: { length: 2, 0: w, 1: h }, at: { length: 2, 0: 0, 1: 0 } } }) const model = P.buildModel([shaped(1390, 1390), shaped(2560, 1080)], 1) const aspects = model.sections[0].windows.map((w) => w.aspect) assert.ok(Math.abs(aspects[0] - 1.0) < 0.01) assert.ok(Math.abs(aspects[1] - 2.37) < 0.02) assert.notStrictEqual(aspects[0], aspects[1], "tiles must not all collapse to one shape") }) // ------------------------------------------------------ workspace columns function wins(...aspects) { return aspects.map((a, i) => ({ address: "0x" + i, aspect: a })) } test("workspaces divide the width into columns", () => { assert.strictEqual(P.columnWidth(4, 3384), 829) assert.strictEqual(P.columnWidth(1, 3384), 3384) assert.strictEqual(P.columnWidth(0, 3384), 0) assert.strictEqual(P.columnWidth(2, 0), 0) }) test("columns stop shrinking at a legible minimum and scroll instead", () => { const min = P.defaultMetrics().minColumnWidth assert.strictEqual(P.columnWidth(40, 3384), min) assert.ok(P.columnWidth(12, 3384) >= min) }) test("a column uses its height instead of stopping half way", () => { // Greedy row-filling put these two side by side at half height and left two // thirds of the column empty, purely because a third window would not fit. const packed = P.packColumn(wins(1.22, 1.22), 829, 1316) assert.strictEqual(packed.rows.length, 2, "two windows should stack, not pair up") assert.ok(packed.rows[0].height > 500, `tiles only ${packed.rows[0].height}px tall`) assert.ok(packed.height <= 1316) }) test("a single window fills its column without overflowing it", () => { const packed = P.packColumn(wins(1.0), 829, 1316) assert.strictEqual(packed.rows.length, 1) const cell = packed.rows[0].cells[0] assert.ok(cell.width <= 829, `tile ${cell.width}px wide in an 829px column`) assert.ok(packed.height <= 1316) }) test("a very tall window is bounded by the column height", () => { // 0.25 aspect in an 829px column wants 3316px of height. const packed = P.packColumn(wins(0.25), 400, 800) assert.ok(packed.height <= 800, `column ran to ${packed.height}px`) assert.ok(packed.rows[0].cells[0].width <= 400) }) test("no row is wider than its column, at any density", () => { const metrics = P.defaultMetrics() for (const colW of [260, 400, 829, 1600]) { for (let n = 1; n <= 12; n++) { const aspects = Array.from({ length: n }, (_, i) => [0.4, 1.0, 1.6, 2.4, 3.5][i % 5]) const packed = P.packColumn(wins(...aspects), colW, 1316) for (const row of packed.rows) { const used = row.cells.reduce((s, c) => s + c.width, 0) + metrics.gap * (row.cells.length - 1) assert.ok(used <= colW, `col=${colW} n=${n}: row used ${used}px of ${colW}`) } } } }) test("every window is placed exactly once", () => { const windows = wins(1.6, 0.7, 2.4, 1.0, 3.0, 1.2, 0.9) const packed = P.packColumn(windows, 700, 1316) const placed = packed.rows.flatMap((r) => r.cells.map((c) => c.item.address)) assert.strictEqual(placed.length, windows.length) assert.deepStrictEqual(placed.slice().sort(), windows.map((w) => w.address).sort()) }) test("tiles keep the aspect ratio of their window", () => { const packed = P.packColumn(wins(1.7, 1.7, 1.7, 1.7), 829, 1316) for (const row of packed.rows) { for (const cell of row.cells) { const ratio = cell.width / cell.height assert.ok(Math.abs(ratio - 1.7) < 0.06, `got ${ratio.toFixed(2)}`) } } }) test("an explicit ceiling caps tile growth", () => { const capped = P.packColumn(wins(1.0), 829, 1316, { maxTileHeight: 300 }) assert.ok(capped.rows[0].height <= 300) const uncapped = P.packColumn(wins(1.0), 829, 1316) assert.ok(uncapped.rows[0].height > 300, "0 must mean no ceiling") }) test("column packing degenerates safely", () => { assert.deepStrictEqual(P.packColumn([], 800, 800).rows, []) assert.deepStrictEqual(P.packColumn(null, 800, 800).rows, []) assert.deepStrictEqual(P.packColumn(wins(1.6), 0, 800).rows, []) // A column shorter than its own chrome must still place the window. const tiny = P.packColumn(wins(1.6), 300, 10) assert.strictEqual(tiny.rows.length, 1) assert.ok(tiny.rows[0].height > 0) assert.ok(tiny.rows[0].cells[0].width <= 300, "fitting the width still wins over any floor") }) // ------------------------------------------------------------------ dragging test("a pointer resolves to the workspace column under it", () => { assert.strictEqual(P.columnIndexAt(0, 4, 829), 0) assert.strictEqual(P.columnIndexAt(828, 4, 829), 0) assert.strictEqual(P.columnIndexAt(851, 4, 829), 1) assert.strictEqual(P.columnIndexAt(3382, 4, 829), 3) }) test("dropping in the gap between columns targets nothing", () => { // Guessing a workspace from an ambiguous drop would move a window somewhere // the user did not point at, and the move is silent — they might not notice. const gap = P.defaultMetrics().columnGap for (let d = 1; d < gap; d++) { assert.strictEqual(P.columnIndexAt(829 + d, 4, 829), -1, `offset ${d} into the gap`) } }) test("a pointer outside the strip targets nothing", () => { assert.strictEqual(P.columnIndexAt(-1, 4, 829), -1) assert.strictEqual(P.columnIndexAt(99999, 4, 829), -1) assert.strictEqual(P.columnIndexAt(100, 0, 829), -1) assert.strictEqual(P.columnIndexAt(100, 4, 0), -1) assert.strictEqual(P.columnIndexAt(NaN, 4, 829), -1) }) test("every column is reachable across the whole strip", () => { const n = 6 const colWidth = P.columnWidth(n, 3384) const seen = new Set() for (let x = 0; x < 3384; x += 3) { const i = P.columnIndexAt(x, n, colWidth) if (i >= 0) seen.add(i) } assert.strictEqual(seen.size, n, "some workspace column could not be dropped on") })