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")
})