kaizen-agent 1.43.3 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/pty.test.ts CHANGED
@@ -5,7 +5,7 @@ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, realpa
5
5
  import { tmpdir } from "node:os";
6
6
  import { join } from "node:path";
7
7
  import { launchMode, closeSessions, readSessions, sweepSessions, CLOSE_GRACE_MS } from "./state.ts";
8
- import { loginEnv, startPty, writePty, attachPty, ptyState } from "./pty.ts";
8
+ import { loginEnv, startPty, writePty, attachPty, ptyState, exitPty } from "./pty.ts";
9
9
  import { conptyHost, innerCommand, resizeSeq } from "./conpty.ts";
10
10
 
11
11
  const root = realpathSync(mkdtempSync(join(tmpdir(), "kaizen-pty-")));
@@ -198,6 +198,18 @@ test.skipIf(process.platform === "win32")("the done sweep keeps a session the us
198
198
  expect(await gone(looked)).toBe(true);
199
199
  });
200
200
 
201
+ test.skipIf(process.platform === "win32")("exitPty types /exit, and ends an agent that ignores it", async () => {
202
+ const wait = async (k: string) => { for (let i = 0; i < 100 && ptyState(k)!.alive; i++) await Bun.sleep(30); return ptyState(k)!.alive; };
203
+ startPty("polite", ["sh", "-c", "while read l; do case $l in */exit*) exit 0;; esac; done"], root);
204
+ expect(exitPty("polite", 60_000)).toBe(true);
205
+ expect(await wait("polite")).toBe(false);
206
+ startPty("stubborn", ["sh", "-c", "trap '' INT; while read l; do :; done"], root);
207
+ await Bun.sleep(100);
208
+ exitPty("stubborn", 300);
209
+ expect(await wait("stubborn")).toBe(false);
210
+ expect(exitPty("stubborn")).toBe(false);
211
+ });
212
+
201
213
  test("asking: Claude Code and Codex prompts count as a question, ordinary output does not", async () => {
202
214
  const { asking } = await import("./pty.ts");
203
215
  expect(asking("\x1b[1mWould you like to run the following command?\x1b[0m")).toBe(true);
package/cli/pty.ts CHANGED
@@ -120,6 +120,17 @@ const send = (p: Pty, d: string) => {
120
120
  try { (p.proc.stdin as Bun.FileSink).write(d); (p.proc.stdin as Bun.FileSink).flush(); } catch { /* host gone */ }
121
121
  };
122
122
 
123
+ // The tile's ×: ask the agent to quit as the user would (/exit), then end it if it is
124
+ // still there after `grace`. Escape first clears whatever is half-typed at its prompt.
125
+ export function exitPty(key: string, grace = 5000): boolean {
126
+ const p = ptys.get(key);
127
+ if (!p || p.exit !== null) return false;
128
+ send(p, "\x1b");
129
+ setTimeout(() => send(p, "/exit\r"), 100);
130
+ setTimeout(() => { if (p.exit === null) p.proc.kill(); }, grace).unref();
131
+ return true;
132
+ }
133
+
123
134
  export function resizePty(key: string, cols: number, rows: number) {
124
135
  const p = ptys.get(key);
125
136
  const c = Math.max(10, Math.min(500, cols | 0)), r = Math.max(5, Math.min(300, rows | 0));
@@ -0,0 +1,12 @@
1
+ import { test, expect } from "bun:test";
2
+ import { nextVersion, levelOf, summaryOf } from "./release.ts";
3
+
4
+ test("release picks the bump from the commits and names it from them", () => {
5
+ expect(levelOf("fix(board): x\ndocs: y")).toBe("patch");
6
+ expect(levelOf("docs: y\nfeat(board): x")).toBe("minor");
7
+ expect(levelOf("feat: x\n\nBREAKING: config moved")).toBe("major");
8
+ expect(nextVersion("1.43.3", "patch")).toBe("1.43.4");
9
+ expect(nextVersion("1.43.3", "minor")).toBe("1.44.0");
10
+ expect(nextVersion("1.43.3", "major")).toBe("2.0.0");
11
+ expect(summaryOf(["feat(board): a", "docs(notes): b", "fix: c"])).toBe("a; c");
12
+ });
package/cli/release.ts ADDED
@@ -0,0 +1,67 @@
1
+ // `bun run release [patch|minor|major] [summary]`: bump, commit, test, publish, push.
2
+ // With no level, the commits since the last tag decide it: a `feat` makes a new minor
3
+ // (1.43.3 -> 1.44.0), anything else a patch; "BREAKING" anywhere makes a major.
4
+ // The summary defaults to those commits' subjects, prefixes dropped. A bump commit
5
+ // already made but never published (HEAD is "Release <version>:" with no tag) is reused.
6
+ import { readFileSync, writeFileSync } from "node:fs";
7
+
8
+ const sh = (cmd: string[]) => {
9
+ const r = Bun.spawnSync(cmd, { stdout: "pipe", stderr: "inherit" });
10
+ if (r.exitCode !== 0) throw new Error(`${cmd.join(" ")} failed`);
11
+ return r.stdout.toString().trim();
12
+ };
13
+ const run = (cmd: string[]) => {
14
+ if (Bun.spawnSync(cmd, { stdio: ["inherit", "inherit", "inherit"] }).exitCode !== 0) process.exit(1);
15
+ };
16
+
17
+ export function nextVersion(v: string, level: string) {
18
+ const [a, b, c] = v.split(".").map(Number);
19
+ return level === "major" ? `${a + 1}.0.0` : level === "minor" ? `${a}.${b + 1}.0` : `${a}.${b}.${c + 1}`;
20
+ }
21
+ export function levelOf(log: string) {
22
+ if (/BREAKING/.test(log)) return "major";
23
+ return /^feat(\(.*?\))?!?:/m.test(log) ? "minor" : "patch";
24
+ }
25
+ export function summaryOf(subjects: string[]) {
26
+ return subjects
27
+ .filter((s) => /^(feat|fix|perf)(\(.*?\))?!?:/.test(s))
28
+ .map((s) => s.replace(/^\w+(\(.*?\))?!?:\s*/, ""))
29
+ .join("; ");
30
+ }
31
+
32
+ if (import.meta.main) {
33
+ const args = process.argv.slice(2);
34
+ const forced = ["patch", "minor", "major"].includes(args[0]) ? args.shift() : null;
35
+ const pkg = JSON.parse(readFileSync("package.json", "utf8"));
36
+ const tagged = (v: string) => sh(["git", "tag", "-l", `v${v}`]) !== "";
37
+ const head = sh(["git", "log", "-1", "--format=%s"]);
38
+ if (sh(["git", "status", "--porcelain", "--untracked-files=no"])) {
39
+ console.error("release: commit or drop your changes first");
40
+ process.exit(1);
41
+ }
42
+ if (head.startsWith(`Release ${pkg.version}:`) && !tagged(pkg.version) && !forced) {
43
+ console.log(`release: reusing the unpublished bump commit for ${pkg.version}`);
44
+ } else {
45
+ const last = sh(["git", "describe", "--tags", "--abbrev=0"]);
46
+ const log = sh(["git", "log", "--format=%s%n%b", `${last}..HEAD`]);
47
+ if (!log) {
48
+ console.error(`release: nothing since ${last}`);
49
+ process.exit(1);
50
+ }
51
+ const level = forced ?? levelOf(log);
52
+ const v = nextVersion(pkg.version, level);
53
+ const summary = args.join(" ") || summaryOf(sh(["git", "log", "--reverse", "--format=%s", `${last}..HEAD`]).split("\n")) || "maintenance";
54
+ writeFileSync("package.json", readFileSync("package.json", "utf8").replace(`"version": "${pkg.version}"`, `"version": "${v}"`));
55
+ const note = ".kaizen/notes/Project/Release and testing.md";
56
+ try {
57
+ writeFileSync(note, readFileSync(note, "utf8").replace(`version ${pkg.version} in this checkout`, `version ${v} in this checkout`));
58
+ } catch {}
59
+ sh(["git", "add", "package.json", note]);
60
+ sh(["git", "commit", "-q", "-m", `Release ${v}: ${summary}`]);
61
+ console.log(`release: ${pkg.version} -> ${v} (${level})`);
62
+ }
63
+ // Tests run in prepublishOnly; a failure stops here with the commit unpushed.
64
+ run(["bun", "publish"]);
65
+ run(["git", "push", "origin", "HEAD"]);
66
+ run(["bun", "run", "publish:gh"]);
67
+ }
package/cli/web.ts CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  reviewFindings, pickFindings, backlogCards, backlogItem, startedBacklog, closeBacklog, markBacklogDone, fixPrompt, approvalPrompt, canApproveAwaiting, nativePath, closeSessions, sweepSessions, issueUrl,
10
10
  launchMode, readSessions, ownerOf, runIndex, resumePrompt, runAgent, firstSession, webHosts,
11
11
  } from "./state.ts";
12
- import { attachPty, writePty, resizePty, ptyState, ptyKeys, adoptLoginEnv } from "./pty.ts";
12
+ import { attachPty, writePty, resizePty, exitPty, ptyState, ptyKeys, adoptLoginEnv } from "./pty.ts";
13
13
  import { readSources, addSource, removeSource, moveSource, setCategory, setWriteback, readIcebox, promote, dismiss, restore, addItem, githubRepo, markDone, markReplied, gatherSource, freshImages, gatherAll, gatherDirs, due, socialUrl, socialOf } from "./sources.ts";
14
14
  import { projectLogo, LOGO_TYPES } from "./logo.ts";
15
15
  import * as chatgpt from "./chatgpt.ts";
@@ -389,6 +389,16 @@ export async function web(repoDir: string, opts: Opts = {}) {
389
389
  return bad("websocket expected", 426);
390
390
  }
391
391
 
392
+ // The Terminal page's ×: the agent is asked to /exit, and ended if it will not.
393
+ if (req.method === "POST" && path === "/pty/exit") {
394
+ if (!local(req)) return bad("cross-origin write refused", 403);
395
+ let body: any;
396
+ try { body = await req.json(); } catch { return bad("json body expected"); }
397
+ const key = String(body.key ?? "");
398
+ if (!/^[\w-]+$/.test(key) || !ptyState(key)) return bad("no such terminal", 404);
399
+ return json({ ok: exitPty(key) });
400
+ }
401
+
392
402
  // Every note of one project, text included: search, tags and backlinks are the page's.
393
403
  if (req.method === "GET" && path === "/vault") {
394
404
  const dir = url.searchParams.get("dir") ?? state ?? join(home, ".kaizen");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-agent",
3
- "version": "1.43.3",
3
+ "version": "1.44.0",
4
4
  "description": "Staged workflow for AI agents — code, writing, research, operations: plan, human approval, build, independent review, bounded fix loop.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,7 +28,7 @@
28
28
  "prepublishOnly": "bun test",
29
29
  "postpublish": "sh cli/release.sh",
30
30
  "publish:gh": "sh cli/publish-gh.sh",
31
- "release": "bun publish && bun run publish:gh"
31
+ "release": "bun run cli/release.ts"
32
32
  },
33
33
  "repository": {
34
34
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  import { store } from "./store.ts";
2
2
  import { $, html, ico } from "./dom.ts";
3
- import { load } from "./api.ts";
3
+ import { load, post } from "./api.ts";
4
4
  import { termLoad } from "./agent-term.ts";
5
5
  import { open } from "./panel.ts";
6
6
  import { resume } from "./approvals.ts";
@@ -86,7 +86,9 @@ export function openTerms() {
86
86
  }
87
87
  // An ended session closed with × has nothing to come back to: no chip for it.
88
88
  const hid = [...all.keys()].filter((k) => lay.hidden.includes(k) && all.get(k).pty.alive);
89
- bar!.innerHTML = html`<span class="bar" role="group" aria-label="Layout">${MODES.map(([v, l]) => html`<button data-mode="${v}" class="${lay.mode === v ? "on" : ""}" aria-pressed="${String(lay.mode === v)}">${l}</button>`)}</span>${hid.length ? html`<span class="dim">Hidden</span>${hid.map((k) => html`<button class="chip" data-show="${k}" title="Show this terminal">${ico("plus")}${nameOf(all.get(k))} - ${projOf(all.get(k))}</button>`)}` : ""}${phone.matches && keys.length > 1 ? html`<span class="ttabs" role="group" aria-label="Terminals">${keys.map((k) => html`<button class="${k === one ? "on" : ""}" data-pick="${k}" aria-pressed="${String(k === one)}"><span class="tdot ${tst(all.get(k))}"></span>${nameOf(all.get(k))} - ${projOf(all.get(k))}</button>`)}</span>` : ""}`.s;
89
+ // The layout switch sits at the right of the page header.
90
+ $("tmodes").innerHTML = html`${MODES.map(([v, l]) => html`<button data-mode="${v}" class="${lay.mode === v ? "on" : ""}" aria-pressed="${String(lay.mode === v)}">${l}</button>`)}`.s;
91
+ bar!.innerHTML = html`${hid.length ? html`<span class="dim">Hidden</span>${hid.map((k) => html`<button class="chip" data-show="${k}" title="Show this terminal">${ico("plus")}${nameOf(all.get(k))} - ${projOf(all.get(k))}</button>`)}` : ""}${phone.matches && keys.length > 1 ? html`<span class="ttabs" role="group" aria-label="Terminals">${keys.map((k) => html`<button class="${k === one ? "on" : ""}" data-pick="${k}" aria-pressed="${String(k === one)}"><span class="tdot ${tst(all.get(k))}"></span>${nameOf(all.get(k))} - ${projOf(all.get(k))}</button>`)}</span>` : ""}`.s;
90
92
  bar!.querySelectorAll<HTMLElement>("[data-pick]").forEach(
91
93
  (b) =>
92
94
  (b.onclick = () => {
@@ -95,7 +97,7 @@ export function openTerms() {
95
97
  }),
96
98
  );
97
99
  bar!.querySelector(".ttabs .on")?.scrollIntoView({ block: "nearest", inline: "nearest" });
98
- bar!.querySelectorAll<HTMLElement>("[data-mode]").forEach(
100
+ $("tmodes").querySelectorAll<HTMLElement>("[data-mode]").forEach(
99
101
  (b) =>
100
102
  (b.onclick = () => {
101
103
  lay.mode = b.dataset.mode!;
@@ -140,7 +142,7 @@ export function openTerms() {
140
142
  const hd = t.el.querySelector("header")!;
141
143
  hd.title = (c.kind === "run" ? c.id : c.text) + " · " + c.where;
142
144
  // An ended run can be resumed from here; an idea whose agent ended has no run to resume.
143
- hd.innerHTML = html`<span class="tdot ${st}" role="img" title="${st}" aria-label="${st}"></span><b>${nameOf(c)} - ${projOf(c)}</b>${off ? html`<span class="tend">ended</span>` : ""}<span class="sp"></span>${off && c.kind === "run" ? html`<button class="btn sm" data-a="resume" title="Continue this run's agent">Resume</button>` : ""}<button class="ib sm" data-a="card" title="Open card" aria-label="Open card">${ico("open")}</button><button class="ib sm" data-a="max" title="${lay.max === k ? "Restore" : "Maximise"}" aria-label="${lay.max === k ? "Restore" : "Maximise"}">${ico(lay.max === k ? "min" : "max")}</button><button class="ib sm" data-a="hide" title="${off ? "Close" : "Hide"}" aria-label="${off ? "Close" : "Hide"}">${ico("close")}</button>`.s;
145
+ hd.innerHTML = html`<span class="tdot ${st}" role="img" title="${st}" aria-label="${st}"></span><b>${nameOf(c)} - ${projOf(c)}</b>${off ? html`<span class="tend">ended</span>` : ""}<span class="sp"></span>${off && c.kind === "run" ? html`<button class="btn sm" data-a="resume" title="Continue this run's agent">Resume</button>` : ""}<button class="ib sm" data-a="card" title="Open card" aria-label="Open card">${ico("open")}</button><button class="ib sm" data-a="max" title="${lay.max === k ? "Restore" : "Maximise"}" aria-label="${lay.max === k ? "Restore" : "Maximise"}">${ico(lay.max === k ? "min" : "max")}</button>${off ? "" : html`<button class="ib sm" data-a="hide" title="Hide" aria-label="Hide">${ico("dash")}</button>`}<button class="ib sm" data-a="${off ? "hide" : "exit"}" title="${off ? "Close" : "Exit session"}" aria-label="${off ? "Close" : "Exit session"}">${ico("close")}</button>`.s;
144
146
  t.el.classList.toggle("ended", off);
145
147
  t.el.classList.toggle("off", !!one && one !== k);
146
148
  t.el.style.gridColumn = "";
@@ -180,6 +182,10 @@ function wire(el: HTMLElement) {
180
182
  else if (a === "hide") {
181
183
  lay.hidden.push(k);
182
184
  if (lay.max === k) lay.max = null;
185
+ } else if (a === "exit") {
186
+ // The tile stays, turning "ended" with its Resume button when the agent is gone.
187
+ if (confirm("End this agent session? A run can be resumed from its tile or card.")) post("/pty/exit", { key: k }).then(() => load(true));
188
+ return;
183
189
  } else if (a === "card") {
184
190
  const c = sessions().get(k);
185
191
  if (c) open(c);
package/web/board.html CHANGED
@@ -272,7 +272,7 @@ body.paged:not(.ice) .hdr,body.ice aside.docked>header{display:none}body.ice #ne
272
272
  /* The Terminal page: the project bar stays (it picks whose terminals show), the board gives way to the tiles */
273
273
  #terms{display:none}body.termv.paged .hdr{display:flex}body.termv #newtop,body.termv #fbtn{display:none!important}
274
274
  body.termv #terms{flex:1;min-height:0;display:flex;flex-direction:column;gap:8px;padding:10px 20px 14px 28px}
275
- .tbar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;font-size:12px}.tbar .bar{margin-right:8px}.tbar .bar button{padding:0 10px;font-size:12px}.tbar .chip{cursor:pointer;background:none;border:0;font:inherit;font-size:11px}.tbar .chip:hover{color:var(--ink)}.tbar svg{width:12px;height:12px}
275
+ .tbar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;font-size:12px}#tmodes{display:none}body.termv #tmodes{display:inline-flex}#tmodes button{padding:0 10px;font-size:12px}.tbar .chip{cursor:pointer;background:none;border:0;font:inherit;font-size:11px}.tbar .chip:hover{color:var(--ink)}.tbar svg{width:12px;height:12px}
276
276
  .tgrid{position:relative;flex:1;min-height:0;display:grid;gap:6px}
277
277
  .tile{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--line);border-radius:var(--r-s);background:var(--bg);overflow:hidden}.tile.off{display:none}.tile.drop{outline:2px solid var(--accent);outline-offset:-2px}
278
278
  .tile header{display:flex;align-items:center;gap:8px;min-height:30px;padding:0 4px 0 10px;border-bottom:1px solid var(--line);background:var(--bg-2);cursor:grab;user-select:none}
@@ -285,7 +285,7 @@ body.tdrag{user-select:none}body.tdrag .tile{pointer-events:none}
285
285
  @media(max-width:900px){body.termv #terms{padding-inline:16px}}
286
286
  /* A phone has no room for a grid or a stack: one terminal fills the page, and tabs above pick which */
287
287
  .ttabs{display:none}
288
- @media(max-width:640px){.tbar .bar,.tile [data-a=max],.tsplit{display:none}.ttabs{display:flex;order:-1;flex:1 0 100%;min-width:0;gap:6px;overflow-x:auto;scrollbar-width:none}.ttabs button{flex:none;display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 10px;border:1px solid var(--line);border-radius:var(--r-pill);background:none;color:var(--dim);font:inherit;font-size:12px;white-space:nowrap}.ttabs button.on{color:var(--ink);background:var(--hover);border-color:var(--accent)}.tgrid{display:flex;flex-direction:column}.tile{flex:1}.tile.off{display:none}}
288
+ @media(max-width:640px){body.termv #tmodes,.tile [data-a=max],.tsplit{display:none}.ttabs{display:flex;order:-1;flex:1 0 100%;min-width:0;gap:6px;overflow-x:auto;scrollbar-width:none}.ttabs button{flex:none;display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 10px;border:1px solid var(--line);border-radius:var(--r-pill);background:none;color:var(--dim);font:inherit;font-size:12px;white-space:nowrap}.ttabs button.on{color:var(--ink);background:var(--hover);border-color:var(--accent)}.tgrid{display:flex;flex-direction:column}.tile{flex:1}.tile.off{display:none}}
289
289
  aside.docked{position:static;z-index:auto;flex:1;min-height:0;width:auto;transform:none;visibility:visible;transition:none;box-shadow:none;border:0;background:none}
290
290
  aside.docked>header,aside.docked>.body{padding-inline:28px}
291
291
  aside.docked>header{border-bottom:0;padding-top:28px;padding-bottom:12px}
@@ -535,6 +535,7 @@ a.schip:hover{color:var(--ink);border-color:color-mix(in srgb,var(--line) 60%,va
535
535
  <symbol id="i-term" viewBox="0 0 24 24"><path d="M4 17l6-5-6-5M12 19h8"/></symbol>
536
536
  <symbol id="i-max" viewBox="0 0 24 24"><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></symbol>
537
537
  <symbol id="i-min" viewBox="0 0 24 24"><path d="M9 4v5H4M15 4v5h5M9 20v-5H4M15 20v-5h5"/></symbol>
538
+ <symbol id="i-dash" viewBox="0 0 24 24"><path d="M5 12h14"/></symbol>
538
539
  <symbol id="i-close" viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></symbol>
539
540
  <symbol id="i-more" viewBox="0 0 24 24"><circle cx="5" cy="12" r="1.2" fill="currentColor"/><circle cx="12" cy="12" r="1.2" fill="currentColor"/><circle cx="19" cy="12" r="1.2" fill="currentColor"/></symbol>
540
541
  <symbol id="i-open" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M15 3v18"/></symbol>
@@ -606,6 +607,7 @@ a.schip:hover{color:var(--ink);border-color:color-mix(in srgb,var(--line) 60%,va
606
607
  <button class="ib" id="fbtn" aria-label="Filter by status" title="Filter by status" aria-expanded="false" hidden><svg><use href="#i-filter"/></svg></button>
607
608
  <input type="search" class="sel" id="bq" placeholder="Filter cards (press /)" aria-label="Filter cards" spellcheck="false" autocomplete="off">
608
609
  <span class="sp"></span>
610
+ <span class="bar" id="tmodes" role="group" aria-label="Layout"></span>
609
611
  <button class="btn pri" id="newtop">New idea</button>
610
612
  </div>
611
613
  <main id="board" aria-live="polite" aria-busy="true"><section aria-hidden="true"><span class="skel" style="height:14px;width:40%;margin:4px 0 12px"></span><span class="skel" style="height:72px;margin-bottom:8px"></span><span class="skel" style="height:72px"></span></section><section aria-hidden="true"><span class="skel" style="height:14px;width:40%;margin:4px 0 12px"></span><span class="skel" style="height:72px;margin-bottom:8px"></span><span class="skel" style="height:72px"></span></section><section aria-hidden="true"><span class="skel" style="height:14px;width:40%;margin:4px 0 12px"></span><span class="skel" style="height:72px;margin-bottom:8px"></span><span class="skel" style="height:72px"></span></section><section aria-hidden="true"><span class="skel" style="height:14px;width:40%;margin:4px 0 12px"></span><span class="skel" style="height:72px;margin-bottom:8px"></span><span class="skel" style="height:72px"></span></section><section aria-hidden="true"><span class="skel" style="height:14px;width:40%;margin:4px 0 12px"></span><span class="skel" style="height:72px;margin-bottom:8px"></span><span class="skel" style="height:72px"></span></section></main>
package/web/board.js CHANGED
@@ -1,4 +1,4 @@
1
- /* kaizen board e04c49402aa5e486 -- generated by bun run build:web from web/board/main.ts; do not edit */
1
+ /* kaizen board e843bd059f54f131 -- generated by bun run build:web from web/board/main.ts; do not edit */
2
2
  (()=>{var i={S:{project:null,projects:[],cards:[],now:Date.now(),all:!1},all:new URLSearchParams(location.search).get("all")!=="0",dir:null,openKey:null,mode:null,modeKey:null,menuKey:null,tab:"request",lastTab:"request",runDoc:null,clearMenu:!1,sevMenu:!1,wantTerm:null,setQ:"",pageGen:0,PL:[],shown:new Set((()=>{try{return JSON.parse(localStorage.getItem("kz-show"))||[]}catch(e){return[]}})())};var s=(e)=>document.getElementById(e),Ge=(e)=>String(e).replace(/[&<>"]/g,(t)=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t]);class en{s;constructor(e){this.s=e}toString(){return this.s}}var _=(e)=>new en(String(e)),Un=(e)=>e instanceof en?e.s:Array.isArray(e)?e.map(Un).join(""):e==null||e===!1?"":Ge(e),f=(e,...t)=>_(e.reduce((n,a,r)=>n+Un(t[r-1])+a)),q=(e)=>_(`<svg><use href="#i-${e}"/></svg>`),Et=_('<div aria-busy="true" aria-label="Loading"><span class="skel" style="height:16px;width:35%;margin:20px 0 14px"></span>'+'<span class="skel" style="height:44px;margin-bottom:10px"></span>'.repeat(4)+"</div>"),At=(e)=>Promise.all(e.map((t)=>new Promise((n)=>{let a=new FileReader;a.onload=()=>n({name:t.name,data:a.result}),a.readAsDataURL(t)})));function Ue(e){let t=document.createElement("dialog");return t.className="kz",t.innerHTML=`<form class="inl" method="dialog">${e}</form>`,t.onclose=()=>t.remove(),t.onclick=(n)=>{if(n.target===t)t.close()},document.body.appendChild(t),t.showModal(),t}var Fn;function C(e,t){let n=s("toast");if(n.textContent=e,n.classList.toggle("rep",!!t),t){let a=document.createElement("a");a.href="/issue?"+new URLSearchParams(t),a.target="_blank",a.rel="noopener",a.textContent="Report",n.append(" ",a)}n.classList.add("on"),clearTimeout(Fn),Fn=setTimeout(()=>n.classList.remove("on"),t?8000:4000)}function Se(){try{if(typeof Notification<"u"&&Notification.permission==="default")Notification.requestPermission()}catch(e){}}function _n(e){try{if(typeof Notification<"u"&&Notification.permission==="granted")new Notification("kaizen",{body:e,icon:"/logo.png"})}catch(t){}}var Bs="https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0",Wn={tiny:"onnx-community/whisper-tiny",base:"onnx-community/whisper-base",small:"onnx-community/whisper-small","large-v3-turbo":"onnx-community/whisper-large-v3-turbo"},tn=null,Qs=`import{pipeline}from"${Bs}";let asr=null;
3
3
  onmessage=async e=>{const m=e.data;try{
4
4
  if(m.type==="load"){const prog=p=>{if(p.status==="progress")postMessage({type:"progress",file:p.file,pct:p.progress||0})};
@@ -8,7 +8,7 @@ onmessage=async e=>{const m=e.data;try{
8
8
  catch(e){if(gpu)postMessage({type:"fallback",why:String(e&&e.message||e)});asr=await pipeline("automatic-speech-recognition",m.model,{...cpu,progress_callback:prog})}
9
9
  postMessage({type:"ready"})}
10
10
  if(m.type==="run"){const r=await asr(m.audio,{chunk_length_s:30,return_timestamps:false});postMessage({type:"text",sid:m.sid,seq:m.seq,text:(r.text||"").trim()})}
11
- }catch(err){postMessage({type:"error",sid:m.sid,seq:m.seq,error:String(err&&err.message||err)})}}`,Vs='class T extends AudioWorkletProcessor{process(i){const c=i[0]&&i[0][0];if(c)this.port.postMessage(c.slice(0));return true}}registerProcessor("kz-tap",T)',Js=!!(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&window.AudioWorkletNode&&window.WebAssembly&&window.Worker),Le=null,Re=null,Tt=()=>{},ge=null,Ve=!1,Mt=new Set;async function Ys(){let e="small";try{e=((await fetch("/settings").then((n)=>n.json())).rows.find((n)=>n.key==="dictation.model")||{}).value||e}catch(t){}if(!Wn[e])e="small";if(Re&&tn!==e){try{Le.terminate()}catch(t){}Le=null,Re=null,Mt.clear(),ge=null}if(Re)return Re;return tn=e,Re=new Promise(async(t,n)=>{Le=new Worker(URL.createObjectURL(new Blob([Qs],{type:"text/javascript"})),{type:"module"});let a={};Le.onmessage=(c)=>{let m=c.data;if(m.type==="progress"){a[m.file]=m.pct;let u=Object.values(a);Tt(Math.round(u.reduce((h,p)=>h+p,0)/u.length))}else if(m.type==="ready")t();else if(m.type==="fallback")C("GPU could not run the model, using the CPU: "+m.why);else if(m.type==="error"&&m.seq==null)n(Error(m.error));for(let u of Mt)u.onmsg(m)},Le.onerror=(c)=>n(Error(c.message||"worker failed"));let r=null;try{r=navigator.gpu&&await navigator.gpu.requestAdapter()}catch(c){}if(!r)C("No WebGPU: dictation will run on the CPU and be slower");Le.postMessage({type:"load",model:Wn[tn],webgpu:!!r,f16:!!(r&&r.features.has("shader-f16"))})}),Re.catch(()=>{Re=null;try{Le.terminate()}catch(t){}Le=null}),Re}function de(e){if(ge&&(e||!ge.field.isConnected))ge.stop()}function Fe(e){if(!Js)return null;let t=document.createElement("button");t.className="ib sm mic",t.title="Dictate",t.setAttribute("aria-label","Dictate"),t.type="button";let n=()=>{t.innerHTML=q("mic"),t.classList.remove("on","busy")};n();let a=e[0];return e.forEach((r)=>r.addEventListener("focus",()=>a=r)),t.onclick=async(r)=>{if(r.preventDefault(),r.stopPropagation(),ge){let T=ge.btn===t;if(ge.stop(),T)return}if(Ve)return;Ve=!0,t.classList.add("busy"),Tt=(T)=>{t.innerHTML=`<i>${T}%</i>`};try{await Ys()}catch(T){n(),Tt=()=>{},Ve=!1,C("Dictation model could not load: "+T.message);return}if(Tt=()=>{},t.innerHTML=q("mic"),!t.isConnected){n(),Ve=!1;return}let c;try{c=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,noiseSuppression:!0,echoCancellation:!0}})}catch(T){n(),Ve=!1,C("Microphone not available: "+(T.name==="NotAllowedError"?"permission denied":T.message));return}if(ge)ge.stop();let m=new AudioContext({sampleRate:16000});await m.audioWorklet.addModule(URL.createObjectURL(new Blob([Vs],{type:"text/javascript"})));let u=m.createMediaStreamSource(c),h=new AudioWorkletNode(m,"kz-tap");u.connect(h);let p=a,g=p.selectionStart??p.value.length,y=document.createElement("span");y.className="vu",y.setAttribute("aria-hidden","true"),y.innerHTML=[0.5,0.9,1.3,0.8,0.45].map((T)=>`<i style="--w:${T}"></i>`).join("");let x=[],d=0,v=0,w=0,E=0,b=0,M=new Map,R=!1,Q=16000,re=6*Q,Pe=0.8*Q,X=1*Q,xe=(T)=>{if(!T||!p.isConnected)return;let O=p.value;if(document.activeElement===p)g=p.selectionStart;g=Math.min(g,O.length);let K=O.slice(0,g),V=K&&!/\s$/.test(K)?" ":"";p.value=K+V+T+O.slice(g),g=K.length+V.length+T.length,p.selectionStart=p.selectionEnd=g,p.dispatchEvent(new Event("input",{bubbles:!0}))},le=()=>{if(!d||v<X){x=[],d=0,v=0,w=0;return}let T=new Float32Array(d),O=0;for(let K of x)T.set(K,O),O+=K.length;x=[],d=0,v=0,w=0,M.set(E,null),Le.postMessage({type:"run",sid:L,seq:E,audio:T},[T.buffer]),E++,t.classList.add("busy")},se=()=>{while(M.has(b)&&M.get(b)!==null)xe(M.get(b)),M.delete(b),b++;if(!M.size){if(t.classList.remove("busy"),R)n(),Mt.delete(N)}};h.port.onmessage=(T)=>{let O=T.data;x.push(O),d+=O.length;let K=0;for(let ve=0;ve<O.length;ve++)K+=O[ve]*O[ve];let V=Math.sqrt(K/O.length);if(V>0.01)v+=O.length,w=0;else w+=O.length;{let ve=Math.min(1,V*8).toFixed(2);p.style.setProperty("--lvl",ve),y.style.setProperty("--lvl",ve)}if(d>=re||v>=X&&w>=Pe)le()};let L=Math.random().toString(36).slice(2),N={btn:t,field:p,onmsg:(T)=>{if(T.sid!==L)return;if(T.type==="text")M.set(T.seq,T.text),se();else if(T.type==="error"&&T.seq!=null)M.set(T.seq,""),C("Dictation failed: "+T.error),se()},stop:()=>{if(R)return;R=!0,t.classList.remove("on"),p.classList.remove("dictating"),p.style.removeProperty("--lvl"),t.innerHTML=q("mic");try{h.port.onmessage=null,u.disconnect(),h.disconnect(),c.getTracks().forEach((T)=>T.stop()),m.close()}catch(T){}ge=null,le(),se()}};Mt.add(N),ge=N,Ve=!1,t.classList.remove("busy"),t.classList.add("on"),p.classList.add("dictating"),t.replaceChildren(y),p.focus()},t}var Bn="kz-d:";function _e(e,t){try{let n=sessionStorage.getItem("kz-d:"+t);if(n!=null)e.value=n}catch(n){}e.addEventListener("input",()=>{try{sessionStorage.setItem("kz-d:"+t,e.value)}catch(n){}})}function Qn(e){try{Object.keys(sessionStorage).forEach((t)=>{if(t.startsWith("kz-d:"+e))sessionStorage.removeItem(t)})}catch(t){}}var Vn=86400000,Jn=(e,t)=>{let n=t-e;return n<3600000?Math.round(n/60000)+"m":n<86400000?Math.round(n/3600000)+"h":Math.round(n/86400000)+"d"},nn={claude:"Claude Code",codex:"Codex",agy:"Antigravity",opencode:"OpenCode",gemini:"Gemini",cursor:"Cursor",yolo:"yolo",unrechecked:"fix not rechecked"},Yn={claude:1,codex:1,agy:1,opencode:1,gemini:1,cursor:1,full:1,lite:1},Zn=["Ideas","Planning","Building","Review","Done"],He={idea:"idea",starting:"start",running:"run",stalled:"stall",waiting:"wait",done:"done",abandoned:"aband"},Zs={"approvals.plan":"plan needs your approval","approvals.review":"review needs your approval"},ft=(e)=>Zs[e]||e,Je={idea:"idea",starting:"starting",running:"running",stalled:"stalled",waiting:"waiting on you",done:"done",abandoned:"abandoned"};function Xn(e,t){let n=e&&e.match(new RegExp(`^##\\s+${t}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$(?![\\s\\S]))`,"m"));return n?n[1].trim():null}var jt=(e)=>Xn(e,"Preview"),Dt=(e,t)=>e.replace(/(!?)\[([^\]]*)\]\(([^)\s]+)\)/g,(n,a,r,c)=>{if(/^attachments\/[^)]+$/.test(c))return`${a}[${r}](/file?dir=${encodeURIComponent(t.state)}&p=${encodeURIComponent((t.kind==="run"?`runs/${t.id}/`:"")+c)})`;if(/^([a-z][\w+.-]*:|\/|#)/i.test(c))return n;return`${r} (\`${c}\`)`}),es=(e)=>(Xn(e,"Commit")||"").replace(/^```\w*\n|\n```$/g,"").trim();function ts(e){let t=(e||"").split(`
11
+ }catch(err){postMessage({type:"error",sid:m.sid,seq:m.seq,error:String(err&&err.message||err)})}}`,Vs='class T extends AudioWorkletProcessor{process(i){const c=i[0]&&i[0][0];if(c)this.port.postMessage(c.slice(0));return true}}registerProcessor("kz-tap",T)',Js=!!(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&window.AudioWorkletNode&&window.WebAssembly&&window.Worker),Le=null,Re=null,Tt=()=>{},ge=null,Ve=!1,Mt=new Set;async function Ys(){let e="small";try{e=((await fetch("/settings").then((n)=>n.json())).rows.find((n)=>n.key==="dictation.model")||{}).value||e}catch(t){}if(!Wn[e])e="small";if(Re&&tn!==e){try{Le.terminate()}catch(t){}Le=null,Re=null,Mt.clear(),ge=null}if(Re)return Re;return tn=e,Re=new Promise(async(t,n)=>{Le=new Worker(URL.createObjectURL(new Blob([Qs],{type:"text/javascript"})),{type:"module"});let a={};Le.onmessage=(c)=>{let m=c.data;if(m.type==="progress"){a[m.file]=m.pct;let u=Object.values(a);Tt(Math.round(u.reduce((h,p)=>h+p,0)/u.length))}else if(m.type==="ready")t();else if(m.type==="fallback")C("GPU could not run the model, using the CPU: "+m.why);else if(m.type==="error"&&m.seq==null)n(Error(m.error));for(let u of Mt)u.onmsg(m)},Le.onerror=(c)=>n(Error(c.message||"worker failed"));let r=null;try{r=navigator.gpu&&await navigator.gpu.requestAdapter()}catch(c){}if(!r)C("No WebGPU: dictation will run on the CPU and be slower");Le.postMessage({type:"load",model:Wn[tn],webgpu:!!r,f16:!!(r&&r.features.has("shader-f16"))})}),Re.catch(()=>{Re=null;try{Le.terminate()}catch(t){}Le=null}),Re}function de(e){if(ge&&(e||!ge.field.isConnected))ge.stop()}function Fe(e){if(!Js)return null;let t=document.createElement("button");t.className="ib sm mic",t.title="Dictate",t.setAttribute("aria-label","Dictate"),t.type="button";let n=()=>{t.innerHTML=q("mic"),t.classList.remove("on","busy")};n();let a=e[0];return e.forEach((r)=>r.addEventListener("focus",()=>a=r)),t.onclick=async(r)=>{if(r.preventDefault(),r.stopPropagation(),ge){let T=ge.btn===t;if(ge.stop(),T)return}if(Ve)return;Ve=!0,t.classList.add("busy"),Tt=(T)=>{t.innerHTML=`<i>${T}%</i>`};try{await Ys()}catch(T){n(),Tt=()=>{},Ve=!1,C("Dictation model could not load: "+T.message);return}if(Tt=()=>{},t.innerHTML=q("mic"),!t.isConnected){n(),Ve=!1;return}let c;try{c=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,noiseSuppression:!0,echoCancellation:!0}})}catch(T){n(),Ve=!1,C("Microphone not available: "+(T.name==="NotAllowedError"?"permission denied":T.message));return}if(ge)ge.stop();let m=new AudioContext({sampleRate:16000});await m.audioWorklet.addModule(URL.createObjectURL(new Blob([Vs],{type:"text/javascript"})));let u=m.createMediaStreamSource(c),h=new AudioWorkletNode(m,"kz-tap");u.connect(h);let p=a,g=p.selectionStart??p.value.length,y=document.createElement("span");y.className="vu",y.setAttribute("aria-hidden","true"),y.innerHTML=[0.5,0.9,1.3,0.8,0.45].map((T)=>`<i style="--w:${T}"></i>`).join("");let x=[],d=0,v=0,w=0,E=0,b=0,M=new Map,R=!1,Q=16000,se=6*Q,Pe=0.8*Q,X=1*Q,xe=(T)=>{if(!T||!p.isConnected)return;let O=p.value;if(document.activeElement===p)g=p.selectionStart;g=Math.min(g,O.length);let I=O.slice(0,g),V=I&&!/\s$/.test(I)?" ":"";p.value=I+V+T+O.slice(g),g=I.length+V.length+T.length,p.selectionStart=p.selectionEnd=g,p.dispatchEvent(new Event("input",{bubbles:!0}))},le=()=>{if(!d||v<X){x=[],d=0,v=0,w=0;return}let T=new Float32Array(d),O=0;for(let I of x)T.set(I,O),O+=I.length;x=[],d=0,v=0,w=0,M.set(E,null),Le.postMessage({type:"run",sid:L,seq:E,audio:T},[T.buffer]),E++,t.classList.add("busy")},oe=()=>{while(M.has(b)&&M.get(b)!==null)xe(M.get(b)),M.delete(b),b++;if(!M.size){if(t.classList.remove("busy"),R)n(),Mt.delete(N)}};h.port.onmessage=(T)=>{let O=T.data;x.push(O),d+=O.length;let I=0;for(let ve=0;ve<O.length;ve++)I+=O[ve]*O[ve];let V=Math.sqrt(I/O.length);if(V>0.01)v+=O.length,w=0;else w+=O.length;{let ve=Math.min(1,V*8).toFixed(2);p.style.setProperty("--lvl",ve),y.style.setProperty("--lvl",ve)}if(d>=se||v>=X&&w>=Pe)le()};let L=Math.random().toString(36).slice(2),N={btn:t,field:p,onmsg:(T)=>{if(T.sid!==L)return;if(T.type==="text")M.set(T.seq,T.text),oe();else if(T.type==="error"&&T.seq!=null)M.set(T.seq,""),C("Dictation failed: "+T.error),oe()},stop:()=>{if(R)return;R=!0,t.classList.remove("on"),p.classList.remove("dictating"),p.style.removeProperty("--lvl"),t.innerHTML=q("mic");try{h.port.onmessage=null,u.disconnect(),h.disconnect(),c.getTracks().forEach((T)=>T.stop()),m.close()}catch(T){}ge=null,le(),oe()}};Mt.add(N),ge=N,Ve=!1,t.classList.remove("busy"),t.classList.add("on"),p.classList.add("dictating"),t.replaceChildren(y),p.focus()},t}var Bn="kz-d:";function _e(e,t){try{let n=sessionStorage.getItem("kz-d:"+t);if(n!=null)e.value=n}catch(n){}e.addEventListener("input",()=>{try{sessionStorage.setItem("kz-d:"+t,e.value)}catch(n){}})}function Qn(e){try{Object.keys(sessionStorage).forEach((t)=>{if(t.startsWith("kz-d:"+e))sessionStorage.removeItem(t)})}catch(t){}}var Vn=86400000,Jn=(e,t)=>{let n=t-e;return n<3600000?Math.round(n/60000)+"m":n<86400000?Math.round(n/3600000)+"h":Math.round(n/86400000)+"d"},nn={claude:"Claude Code",codex:"Codex",agy:"Antigravity",opencode:"OpenCode",gemini:"Gemini",cursor:"Cursor",yolo:"yolo",unrechecked:"fix not rechecked"},Yn={claude:1,codex:1,agy:1,opencode:1,gemini:1,cursor:1,full:1,lite:1},Zn=["Ideas","Planning","Building","Review","Done"],He={idea:"idea",starting:"start",running:"run",stalled:"stall",waiting:"wait",done:"done",abandoned:"aband"},Zs={"approvals.plan":"plan needs your approval","approvals.review":"review needs your approval"},ft=(e)=>Zs[e]||e,Je={idea:"idea",starting:"starting",running:"running",stalled:"stalled",waiting:"waiting on you",done:"done",abandoned:"abandoned"};function Xn(e,t){let n=e&&e.match(new RegExp(`^##\\s+${t}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$(?![\\s\\S]))`,"m"));return n?n[1].trim():null}var jt=(e)=>Xn(e,"Preview"),Dt=(e,t)=>e.replace(/(!?)\[([^\]]*)\]\(([^)\s]+)\)/g,(n,a,r,c)=>{if(/^attachments\/[^)]+$/.test(c))return`${a}[${r}](/file?dir=${encodeURIComponent(t.state)}&p=${encodeURIComponent((t.kind==="run"?`runs/${t.id}/`:"")+c)})`;if(/^([a-z][\w+.-]*:|\/|#)/i.test(c))return n;return`${r} (\`${c}\`)`}),es=(e)=>(Xn(e,"Commit")||"").replace(/^```\w*\n|\n```$/g,"").trim();function ts(e){let t=(e||"").split(`
12
12
  `),n=t.findIndex((h)=>/^#+\s*(\d+\.\s*)?open questions/i.test(h.trim()));if(n<0)return[];let a=/^#+/.exec(t[n].trim())[0].length,r=[],c=null,m=!1;for(let h of t.slice(n+1)){let p=h.trim(),g=/^#+(?=\s)/.exec(p);if(g&&g[0].length<=a)break;if(!g&&p&&!/^```/.test(p)&&!/^none\.?$/i.test(p))m=!0;let y=/^\**Q\d*\s*[:.)]\**\s*(.+)$/.exec(p);if(y){r.push(c={q:y[1].trim(),multi:/multi-?select/i.test(y[1]),options:[]});continue}let x=/^[-*]\s+(.+)$/.exec(p);if(x&&c){let d=x[1].trim();c.options.push({text:d,rec:/\(recommended\)/i.test(d),label:d.replace(/\s*\(recommended\)/i,"").split(/\s+[—–-]\s+|:\s/)[0].trim()})}else if(c&&c.options.length)c=null}let u=r.filter((h)=>h.options.length);return u.length||!m?u:null}function qe(e){let t=/(^|[.!?)]\s+|\s)((?:[A-Z][A-Z-]+(?: [A-Z][A-Z-]+){1,3}|[A-Z][\w-]*(?: [a-z][\w-]*){0,3})(?: \([^)]*\))?):\s+/g,n=[],a;while(a=t.exec(e))if(!/^\s+$/.test(a[1])||!n.length&&!a[2].includes(" "))n.push([a.index+a[1].length,a[2],t.lastIndex]);if(n.length<2)return{head:e,parts:[]};let r=n.map(([,c,m],u)=>{let h=e.slice(m,u+1<n.length?n[u+1][0]:e.length).trim(),p=h.split(/\s*\((\d+)\)\s+/);if(p.length<5)return{label:c,body:h};let g=[];for(let x=1;x<p.length;x+=2)g.push([p[x],p[x+1]]);let y=g.every(([x],d)=>+x===d+1);return{label:c,body:(p[0]?p[0]+`
13
13
  `:"")+g.map(([x,d],v)=>y?`${v+1}. ${d}`:`- (${x}) ${d}`).join(`
14
14
  `)}});return{head:e.slice(0,n[0][0]).trim()||`${r[0].label}: ${e.slice(n[0][2],n[1][0]).trim()}`,parts:r}}function ke(e,t){let n=e.split(`
@@ -20,11 +20,11 @@ onmessage=async e=>{const m=e.data;try{
20
20
  ${c("Google account",f`<button class="btn${a.connected?"":" pri"}" id="gg-conn" type="button"${a.hasClient||a.connected?"":" disabled"}>${a.connected?"Disconnect":"Connect"}</button>`,"","gg-conn","google account connect sheets write back")}</div>
21
21
  <h3 class="sg">Browser</h3><div class="sgrp">${c("Theme",r("th",["system","light","dark"],t),"","th","theme light dark system browser")}
22
22
  ${c("Notifications",f`<button class="chk${u==="granted"?" on":""}" id="nt" role="switch" aria-checked="${String(u==="granted")}"${u&&u!=="denied"?"":" disabled"}></button>`,h,"nt","notifications "+h+" browser")}</div>
23
- <p class="snone" id="snone" hidden></p>`,sn();let p=[...s("pbody").querySelectorAll(".sg")],g=()=>{let d=i.setQ.trim().toLowerCase(),v=!1;p.forEach((w)=>{let E=!1;for(let b of w.nextElementSibling.children)b.hidden=!!d&&!b.dataset.q.includes(d),E||=!b.hidden;w.hidden=!E,v||=E}),s("snone").hidden=v,s("snone").textContent=`No setting matches “${i.setQ.trim()}”.`};if(s("sq").oninput=(d)=>{i.setQ=d.target.value,g()},s("sq").onkeydown=(d)=>{if(d.key!=="Escape")return;if(d.preventDefault(),i.setQ)i.setQ="",d.target.value="",g();else ee()},g(),m){let d=s("sq");d.focus(),d.setSelectionRange(d.value.length,d.value.length)}s("gg-save").onclick=async()=>{let d=await A("/google",{op:"client",id:s("gg-id").value,secret:s("gg-secret").value});if(d.ok)C("Saved");else C(d.error||"Enter both the client id and the secret");Ce()},s("gg-conn").onclick=async()=>{if(a.connected)return await A("/google",{op:"disconnect"}),Ce();let d=window.open("","_blank"),v=await A("/google",{op:"connect"});if(v.ok&&d)d.location=v.url;else d?.close(),C(v.error||"Could not start Google sign-in")};let y=(d,v,w,E)=>{let b=Math.max(v.indexOf(w),0),M=(R)=>{b=(b+R+v.length)%v.length,d.querySelector("output").textContent=v[b]===""?"—":v[b],E(v[b])};s(d.id+"-prev").onclick=()=>M(-1),s(d.id+"-next").onclick=()=>M(1),d.onkeydown=(R)=>{if(R.key!=="ArrowLeft"&&R.key!=="ArrowRight")return;R.preventDefault();let Q=s(d.id+(R.key==="ArrowLeft"?"-prev":"-next"));Q.focus(),Q.click()}},x=async(d)=>{if(await Ce(),d&&/^s-preset-|-(prev|next)$/.test(d))s(d)?.focus()};if(y(s("th"),["system","light","dark"],t,setTheme),s("nt").onclick=()=>{Se(),setTimeout(Ce,300)},n)n.rows.forEach((d)=>{let v=s("s-"+d.key.replace(/\./g,"-")),w=async(b)=>{let M=document.activeElement?.id;if((await A("/settings",{key:d.key,value:b})).ok)C("Saved");await x(M)};if(v.tagName==="INPUT")v.onchange=async()=>{await w(v.value.trim()),I()};else v.querySelectorAll("[data-v]").forEach((b)=>b.onclick=()=>w(b.dataset.v));if(v.matches(".chk"))v.onclick=()=>w(v.dataset.v);let E;if(v.matches(".step"))y(v,d.values,d.value,(b)=>{clearTimeout(E),E=setTimeout(()=>w(b),300)});if(d.folder)s(v.id+"-pick").onclick=async()=>{let b=await A("/pick-folder",{});if(b.path)await w(b.path),I()}})}var Pt=(e)=>e.status==="stalled"||!!(e.pty&&!e.pty.alive);async function Ze(e,t){t.disabled=!0,Se();let n=await A("/resume",{dir:e.state,id:e.id});if(!n.ok){t.disabled=!1;return}if(C("Resumed, "+We(n)+": "+Ne(n.agent)),n.pty)i.wantTerm=j(e);if(i.openKey===j(e))s("pfoot").innerHTML="",te(e,!0)}var on={"approvals.plan":"The plan is ready. Read it in the Plan tab, then approve or send it back.","approvals.review":"The work is ready to check. Approve to finish it, or revise with what to change.",findings:"The review has open findings. Approve as-is to accept them and finish, or fix selected findings first."},Xe=new Set,et=(e)=>j(e)+":"+e.awaiting+":"+e.moved;async function Rt(e,t,n,a){t.disabled=!0,Se();let r=await A("/decide",{dir:e.state,id:e.id,awaiting:e.awaiting,...n===void 0?{}:{why:n},...a?{answers:a}:{}});if(!r.ok){t.disabled=!1;return}if(Xe.add(et(e)),C((n===void 0?"Approved":"Sent back")+", "+We(r)+": "+Ne(r.agent)),r.pty)i.wantTerm=j(e);if(i.openKey===j(e))s("pfoot").innerHTML="",te(e,!0)}function as(e){let t=s("pfoot"),n=e.awaiting==="approvals.plan";t.innerHTML=f`<div class="inl" style="flex:1"><div class="q">${n?"What should the plan do differently?":"What should change in the work?"}</div><div class="q dim">Opens your agent on this run with your comment.${n?" It revises the plan and comes back here for approval.":" It changes the work, rechecks it and comes back here."}</div><textarea aria-label="Revise comment"></textarea><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Send back</button></div></div>`;let a=t.querySelector("textarea");a.focus(),t.querySelector("[data-a=c]").onclick=()=>{t.innerHTML="",te(e,!0)},t.querySelector("[data-a=s]").onclick=(r)=>{let c=a.value.trim();if(!c){C("Say what should change"),a.focus();return}Rt(e,r.target,c)}}function is(e,t){let n=s("pfoot");n.innerHTML=f`<div class="inl qa" style="flex:1"><div class="q">The plan asks ${t.length===1?"one question":t.length+" questions"}.</div><div class="q dim">Your answers go to your agent with the approval. It records them and builds the plan.</div>${t.map((a,r)=>f`<fieldset><legend>${a.q}</legend>${a.options.map((c,m)=>f`<label><input type="${a.multi?"checkbox":"radio"}" name="q${r}" value="${m}"${c.rec||!a.multi&&!m&&!a.options.some((u)=>u.rec)?" checked":""}> ${c.text}</label>`)}<input type="text" data-n="${r}" placeholder="Note (optional)" aria-label="Note: ${a.q}"></fieldset>`)}<div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Approve with these answers</button></div></div>`,n.querySelector("[data-a=c]").onclick=()=>{n.innerHTML="",te(e,!0)},n.querySelector("[data-a=s]").onclick=(a)=>Rt(e,a.target,void 0,t.map((r,c)=>{let m=[...n.querySelectorAll(`[name=q${c}]:checked`)].map((h)=>`"${r.options[+h.value].label}"`).join(", ")||"none of the options",u=n.querySelector(`[data-n="${c}"]`).value.trim();return`${r.q} — ${m}${u?"; "+u:""}`}))}var eo=[["grid","Grid"],["cols","Side by side"],["rows","Stacked"]],ht=6,P={mode:"grid",order:[],hidden:[],max:null,sizes:{}};try{P={...P,...JSON.parse(localStorage.getItem("kz-terms")||"{}")}}catch(e){}var tt=()=>{P.order=P.order.slice(-100),P.hidden=P.hidden.slice(-100);try{localStorage.setItem("kz-terms",JSON.stringify(P))}catch(e){}},Ke=new Map,Nt=!1,mt=null,Ht=null,an=(e)=>e.kind==="run"?String(e.id||e.text).replace(/^\d{4}-\d\d-\d\d-/,"").replace(/-/g," "):String(e.text).split(`
24
- `)[0].slice(0,80),rs=(e)=>!e.pty.alive?"ended":e.pty.asking?"asking":"running",rn=(e)=>String(e.where||"").split(/[\\/]/).filter(Boolean).pop()||e.where||"";function cn(){let e=new Map;for(let t of i.S.cards)if(t.pty&&!t.archived)e.set(t.pty.key,t);return e}function ls(){Nt=!1;for(let e of Ke.values())e.off?.();Ke.clear(),s("terms").innerHTML=""}function be(){if(Nt=!0,!mt)(mt=matchMedia("(max-width:640px)")).onchange=()=>Nt&&be();if(document.body.classList.contains("tdrag"))return;let e=s("terms"),t=cn();for(let[b,M]of Ke)if(!t.has(b)||P.hidden.includes(b))M.off?.(),M.el.remove(),Ke.delete(b);let n=(b)=>P.order.includes(b)?P.order.indexOf(b):1e9,a=(b)=>t.get(b).pty.alive?0:1,r=[...t.keys()].filter((b)=>!P.hidden.includes(b)).sort((b,M)=>a(b)-a(M)||n(b)-n(M));for(let b of r)if(!P.order.includes(b))P.order.push(b);if(P.max&&!r.includes(P.max))P.max=null;let c=mt.matches?Ht&&r.includes(Ht)?Ht:r[0]:P.max,m=e.querySelector(".tgrid"),u=e.querySelector(".tbar");if(!m)e.innerHTML='<div class="tbar"></div><div class="tgrid"></div>',m=e.querySelector(".tgrid"),u=e.querySelector(".tbar");let h=[...t.keys()].filter((b)=>P.hidden.includes(b)&&t.get(b).pty.alive);if(u.innerHTML=f`<span class="bar" role="group" aria-label="Layout">${eo.map(([b,M])=>f`<button data-mode="${b}" class="${P.mode===b?"on":""}" aria-pressed="${String(P.mode===b)}">${M}</button>`)}</span>${h.length?f`<span class="dim">Hidden</span>${h.map((b)=>f`<button class="chip" data-show="${b}" title="Show this terminal">${q("plus")}${an(t.get(b))} - ${rn(t.get(b))}</button>`)}`:""}${mt.matches&&r.length>1?f`<span class="ttabs" role="group" aria-label="Terminals">${r.map((b)=>f`<button class="${b===c?"on":""}" data-pick="${b}" aria-pressed="${String(b===c)}"><span class="tdot ${rs(t.get(b))}"></span>${an(t.get(b))} - ${rn(t.get(b))}</button>`)}</span>`:""}`.s,u.querySelectorAll("[data-pick]").forEach((b)=>b.onclick=()=>{Ht=b.dataset.pick,be()}),u.querySelector(".ttabs .on")?.scrollIntoView({block:"nearest",inline:"nearest"}),u.querySelectorAll("[data-mode]").forEach((b)=>b.onclick=()=>{P.mode=b.dataset.mode,P.max=null,tt(),be()}),u.querySelectorAll("[data-show]").forEach((b)=>b.onclick=()=>{P.hidden=P.hidden.filter((M)=>M!==b.dataset.show),tt(),be()}),m.querySelector(".tempty")?.remove(),!r.length){m.style.cssText="",m.querySelectorAll(".tsplit").forEach((b)=>b.remove()),m.insertAdjacentHTML("beforeend",f`<div class="empty tempty"><div class="mascot"></div><b>${h.length?"Every terminal is hidden":"No terminals running"}</b><p>Runs started with the agent in its card show here, from Ideas to Done.</p></div>`.s);return}for(let b of r){let M=t.get(b),R=Ke.get(b);if(!R){let X=document.createElement("section");X.className="tile",X.dataset.k=b,X.innerHTML='<header draggable="true"></header><div class="term" aria-label="Agent terminal"></div>',R={el:X,off:null},Ke.set(b,R),to(b,R),no(X)}let Q=rs(M),re=Q==="ended",Pe=R.el.querySelector("header");Pe.title=(M.kind==="run"?M.id:M.text)+" · "+M.where,Pe.innerHTML=f`<span class="tdot ${Q}" role="img" title="${Q}" aria-label="${Q}"></span><b>${an(M)} - ${rn(M)}</b>${re?f`<span class="tend">ended</span>`:""}<span class="sp"></span>${re&&M.kind==="run"?f`<button class="btn sm" data-a="resume" title="Continue this run's agent">Resume</button>`:""}<button class="ib sm" data-a="card" title="Open card" aria-label="Open card">${q("open")}</button><button class="ib sm" data-a="max" title="${P.max===b?"Restore":"Maximise"}" aria-label="${P.max===b?"Restore":"Maximise"}">${q(P.max===b?"min":"max")}</button><button class="ib sm" data-a="hide" title="${re?"Close":"Hide"}" aria-label="${re?"Close":"Hide"}">${q("close")}</button>`.s,R.el.classList.toggle("ended",re),R.el.classList.toggle("off",!!c&&c!==b),R.el.style.gridColumn="",m.append(R.el)}let p=c?1:r.length,g=P.mode==="cols"?p:P.mode==="rows"?1:Math.ceil(Math.sqrt(p)),y=Math.ceil(p/g),x=g;if(!c&&g*y>p)Ke.get(r[r.length-1]).el.style.gridColumn=`span ${g*y-p+1}`,x=p-(y-1)*g-1;let d=`${P.mode}:${g}x${y}`,v=P.sizes[d],w=v?.c?.length===g?v.c:Array(g).fill(1),E=v?.r?.length===y?v.r:Array(y).fill(1);so(m,w,E,d,x,!c)}function to(e,t){dn().then((n)=>{if(Ke.get(e)!==t)return;t.off=n(t.el.querySelector(".term"),e,()=>I(!0))}).catch(()=>t.el.querySelector(".term").textContent="The terminal could not load.")}function no(e){let t=e.dataset.k;e.querySelector("header").onclick=(n)=>{let a=n.target.closest("[data-a]")?.dataset.a;if(a==="max")P.max=P.max===t?null:t;else if(a==="hide"){if(P.hidden.push(t),P.max===t)P.max=null}else if(a==="card"){let r=cn().get(t);if(r)we(r);return}else if(a==="resume"){let r=cn().get(t);if(r)Ze(r,n.target.closest("button")).then(()=>i.wantTerm=null);return}else return;tt(),be()},e.querySelector("header").ondblclick=(n)=>{if(n.target.closest("button")||mt?.matches)return;P.max=P.max===t?null:t,tt(),be()},e.querySelector("header").ondragstart=(n)=>{n.dataTransfer.setData("text/kz-term",t),n.dataTransfer.effectAllowed="move"},e.ondragover=(n)=>{if(!n.dataTransfer.types.includes("text/kz-term"))return;n.preventDefault(),e.classList.add("drop")},e.ondragleave=()=>e.classList.remove("drop"),e.ondrop=(n)=>{e.classList.remove("drop");let a=n.dataTransfer.getData("text/kz-term");if(!a||a===t)return;n.preventDefault();let r=P.order.indexOf(a),c=P.order.indexOf(t);if(r<0||c<0)return;[P.order[r],P.order[c]]=[P.order[c],P.order[r]],tt(),be()}}var ln=(e,t)=>{let n=e.reduce((r,c)=>r+c,0),a=e.slice(0,t+1).reduce((r,c)=>r+c,0);return`calc((100% - ${(e.length-1)*ht}px) * ${a/n} + ${t*ht+ht/2}px)`};function so(e,t,n,a,r,c){e.querySelectorAll(".tsplit").forEach((u)=>u.remove());let m=()=>{e.style.gridTemplateColumns=t.map((u)=>u+"fr").join(" "),e.style.gridTemplateRows=n.map((u)=>u+"fr").join(" "),e.querySelectorAll(".tsplit").forEach((u)=>{let h=+u.dataset.i;if(u.dataset.axis==="r")return void(u.style.top=ln(n,h));u.style.left=ln(t,h),u.style.bottom=h>=r?`calc(100% - ${ln(n,n.length-2)} + ${ht/2}px)`:"0px"})};if(c)for(let[u,h]of[["c",t],["r",n]])for(let p=0;p<h.length-1;p++){let g=document.createElement("div");g.className="tsplit "+u,g.dataset.axis=u,g.dataset.i=String(p),g.setAttribute("role","separator"),g.setAttribute("aria-orientation",u==="c"?"vertical":"horizontal"),g.title="Drag to resize, double-click to even out",oo(g,e,u,h,p,m,()=>{P.sizes[a]={c:[...t],r:[...n]},tt()}),e.append(g)}m()}function oo(e,t,n,a,r,c,m){e.ondblclick=()=>{a.fill(1),m(),c()},e.onpointerdown=(u)=>{u.preventDefault(),e.setPointerCapture(u.pointerId),document.body.classList.add("tdrag");let h=a.reduce((v,w)=>v+w,0),p=(n==="c"?t.clientWidth:t.clientHeight)-(a.length-1)*ht,g=h*0.1,y=n==="c"?u.clientX:u.clientY,x=a[r],d=a[r+1];e.onpointermove=(v)=>{let w=Math.max(g-x,Math.min(d-g,((n==="c"?v.clientX:v.clientY)-y)/p*h));a[r]=x+w,a[r+1]=d-w,c()},e.onpointerup=e.onpointercancel=()=>{e.onpointermove=e.onpointerup=e.onpointercancel=null,document.body.classList.remove("tdrag"),m()}}}var cs=()=>Nt;var W="board",un=(e)=>{document.querySelectorAll(".rail a.it").forEach((t)=>t.dataset.view===e?t.setAttribute("aria-current","page"):t.removeAttribute("aria-current")),document.querySelector(".hdr h1").textContent=e==="sources"?"Icebox":e==="terminal"?"Terminal":"Board"};function fe(e,t){if(i.pageGen++,e==="icebox")e="sources";if(!["settings","sources","terminal"].includes(e))e="board";let n=W;if(W=e,un(e),document.body.classList.toggle("paged",e!=="board"),document.body.classList.toggle("ice",e==="sources"),document.body.classList.toggle("termv",e==="terminal"),t&&e!==n)history.pushState(null,"",e==="board"?location.pathname:"?view="+(e==="sources"?"icebox":e));let a=s("panel");if(e==="terminal")be();else ls();if(e==="board"||e==="terminal"){if(n==="board"||n==="terminal"){if(e==="terminal"&&n==="board")W="board",ee(),W=e;return}a.style.transition="none",a.classList.remove("open","docked"),a.offsetWidth,a.style.transition="",W="board",ee(),W=e;return}if(a.classList.add("docked"),e==="settings")Ce();else z()}function ds(){document.querySelectorAll(".rail a[data-view]").forEach((e)=>{if(e.dataset.view==="notes")return;e.onclick=(t)=>{if(t.ctrlKey||t.metaKey||t.shiftKey||t.button)return;if(t.preventDefault(),e.dataset.view==="settings")i.setQ="";fe(e.dataset.view,!0)}}),document.querySelector(".rail .home").onclick=(e)=>{if(e.ctrlKey||e.metaKey||e.shiftKey||e.button)return;e.preventDefault(),fe("board",!0)},addEventListener("popstate",()=>fe(new URLSearchParams(location.search).get("view"))),addEventListener("kz-theme",()=>{if(W==="settings")Ce()})}var ps="Icebox",ae=null,yt={},pn=(e)=>{if(!yt[e])try{yt[e]=localStorage.getItem("kz-reply-style:"+e)}catch(t){}return yt[e]||"casual"},ao={casual:"Casual",fun:"Fun",helpful:"Helpful",promo:"Promo"},ie=null,Ae=null,me=null,he="",ne="",nt="link",gt=null,st=!1,Kt=!1,It=null,J=new Set,bt={x:"X",tiktok:"TikTok",instagram:"Instagram",threads:"Threads"},Y=()=>s("panel").classList.contains("open")&&s("ptitle").textContent===ps,Be=(e)=>{let t=Math.round((Date.now()-Date.parse(e))/60000);return t<1?"just now":t<60?t+" min ago":t<1440?Math.round(t/60)+" h ago":Math.round(t/1440)+" d ago"};function us(e,t){let n=s(e);if(!n)return;let a=n.querySelector("button.sel"),r=n.querySelector(".menu-pop"),c=r.querySelector("input"),m=r.querySelector(".pick-list"),u=(p)=>{if(r.hidden=!p,a.setAttribute("aria-expanded",p),p)c.value="",h(),c.focus()},h=()=>{let p=c.value.trim().toLowerCase(),g=null,y=!1;for(let x of m.children)if(x.classList.contains("grp")){if(g)g.hidden=!y;g=x,y=!1}else{let d=!p||x.dataset.q.includes(p)||g&&g.dataset.g.includes(p);x.hidden=!d,y||=d}if(g)g.hidden=!y};a.onclick=()=>u(r.hidden),c.oninput=h,c.onkeydown=(p)=>{if(p.key==="Escape")p.stopPropagation(),u(!1),a.focus();else if(p.key==="Enter")p.preventDefault(),m.querySelector("button:not([hidden])")?.click()},m.onclick=(p)=>{let g=p.target.closest("button[data-v]");if(!g)return;u(!1),t(g.dataset.v)}}async function z(){if(W!=="sources")return;let e=++i.pageGen;de(!0);let t=Y()?[s("src-url")?.value||"",s("src-label")?.value||"",document.activeElement?.id,s("src-dir")?.value,s("src-cat")?.value||"",s("src-plat")?.value,s("src-q")?.value||""]:["",""],n=new Map(Y()?[...s("pbody").querySelectorAll("[data-mv]")].map((o)=>[o.dataset.k,o.value]):[]),a=Y()&&document.activeElement?.closest?.(".ice-row"),r=a?[a.dataset.k,document.activeElement.id.replace(/-\d+$/,"")]:null,c=Y()&&s("ice-title")?[s("ice-title").dataset.k,s("ice-title").value,s("ice-details").value]:null,m=new Map(Y()?[...s("pbody").querySelectorAll("[data-ct]")].map((o)=>[o.dataset.k,o.value]):[]);if(!Y())s("pbody").innerHTML=Et;Ee(),i.openKey=null,i.runDoc=null,s("pterm").hidden=!0,s("panel").classList.add("open"),s("scrim").classList.add("on"),s("ptitle").textContent=ps,s("ptabs").innerHTML="",s("pfoot").innerHTML="",s("psub").textContent="",s("psub").title="";let u=i.all?null:i.S.dir,h=u?"?dir="+encodeURIComponent(u):"",p=await fetch("/sources"+h).then((o)=>o.json()).catch(()=>null);if(e!==i.pageGen||!Y())return;let g=(o)=>{try{return new URL(o).hostname}catch{return o}},y=(o,l,k)=>{if(bt[k])return f`<span class="fav">${q("b-"+k)}</span>`;let S;try{let G=new URL(o);if(/^https?:$/.test(G.protocol))S=G.origin}catch{}let D=!S?"":o.includes("docs.google.com/spreadsheets")?"https://ssl.gstatic.com/docs/spreadsheets/favicon3.ico":o.includes("docs.google.com/document")?"https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico":S+"/favicon.ico";return f`<span class="fav">${D?f`<img src="${D}" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.remove()">`:""}${q(l)}</span>`},x=(o)=>bt[o?.social]||(o?g(o.url).replace(/^www\./,""):"")||"Other",d=(o)=>{if(o.social)return"social";let l=o.url;if(l.includes("docs.google.com/spreadsheets")||l.endsWith(".csv")||l.endsWith(".tsv"))return"sheet";if(l.includes("docs.google.com/document"))return"doc";return"link"},v=p&&p.sources||[],w=[...p&&p.icebox||[],...p&&p.dismissed||[]],E=(p&&p.icebox||[]).length,b=p&&p.gatherer||{},M=b.use==="chatgpt"&&b.connected,R=(o,l)=>f`${o&&!i.S.projects.some((k)=>k.dir===o)?f`<option value="${o}" title="${l||o}" selected>${Te(l||o)}</option>`:""}${i.S.projects.map((k)=>f`<option value="${k.dir}" title="${k.label}"${k.dir===o?" selected":""}>${Te(k.label)}</option>`)}`,Q=v.map((o,l)=>{let k=d(o),S=o.label||g(o.url),D=o.social&&(!o.last||!o.last.ok&&/gathered by your agent/i.test(o.last.note))?f`<span class="src-badge dim">${q("at")}${M?"Gathered with ChatGPT":"Gathered by your agent"}</span>`:!o.last?f`<span class="src-badge dim">Not gathered yet</span>`:o.last.ok?f`<span class="src-badge ok"><svg viewBox="0 0 16 16"><path d="M3.5 8.5l3 3 6-6" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>${o.last.note}</span><span class="hint" style="margin:0">${Be(o.last.at)}</span>`:f`<span class="src-badge warn"><svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="6" stroke="currentColor" fill="none" stroke-width="1.8"/><path d="M8 5v3.5M8 11v.1" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round"/></svg>Failed: ${o.last.note}</span><span class="hint" style="margin:0">${Be(o.last.at)}</span>`,G=k==="sheet"?"sheet":k==="doc"?"doc":k==="social"?"at":"link",U=o.url.replace(/^https?:\/\/(www\.)?/,"");return f`<div class="src-card" id="src-card-${l}"><div class="src-hd"><span class="src-ico" title="${k==="social"?bt[o.social]||"social":k}">${y(o.url,G,o.social)}</span><div class="src-info"><span class="src-title" title="${S}">${S}</span><a class="src-url" href="${o.url}" target="_blank" rel="noopener noreferrer" title="${o.url}"><span>${U}</span>${q("ext")}</a></div><div class="src-acts">${o.social?f`<button class="ib sm" id="src-ag-${l}" data-ag="${l}" aria-label="Gather ${S} with your agent" title="${M?"Gather with your ChatGPT plan":"Gather: opens your agent on /kaizen gather"}">${q("refresh")}</button>`:f`<button class="ib sm" id="src-chk-${l}" data-chk="${l}" aria-label="Check ${S} now" title="Check now">${q("refresh")}</button>`}<button class="ib sm danger" id="src-rm-${l}" data-rm="${l}" aria-label="Remove ${S}" title="Remove source">${q("trash")}</button></div></div><div class="src-status">${D}</div><div class="src-fields"><label class="src-f" title="Category"><span>Category</span><input class="sel" id="src-ct-${l}" data-ct="${l}" data-k="${o.dir+" "+o.url}" value="${o.category||""}" placeholder="${x(o)}" maxlength="40" aria-label="Category of ${S}"><button class="btn sm pri" id="src-cs-${l}" aria-label="Save the category of ${S}" disabled>Save</button></label><label class="src-f" title="Project"><span>Project</span>${ot(o.dir,o.project||"")}<select class="sel" id="src-mv-${l}" data-mv="${l}" data-k="${o.dir+" "+o.url}" aria-label="Project of ${S}">${R(o.dir,o.project)}</select><button class="btn sm pri" id="src-mb-${l}" aria-label="Move ${S} to the chosen project" disabled>Move</button></label>${k==="sheet"||/github\.com/.test(o.url)?f`<label class="src-wb"><input type="checkbox" id="src-wb-${l}" data-wb="${l}"${o.writeback?" checked":""}> Write back</label>`:""}</div></div>`}),re=(p&&p.failed||[]).map((o)=>f`<p style="color:var(--dim)">The sources of ${o} could not be read.</p>`),Pe=f`<div class="src-empty"><div class="src-empty-ico">${q("link")}</div><h4>No sources yet</h4></div>`,X=nt==="social",xe=!v.length||st||[t[0],t[1],t[4],t[6]].some(Boolean),le=f`<div class="src-add" id="src-addbox"${xe?"":" hidden"}><div class="hd"><h4>Add a source</h4><div class="src-kind" role="group" aria-label="Kind of source"><button id="src-k-link" data-kind="link" aria-pressed="${String(!X)}">Link</button><button id="src-k-social" data-kind="social" aria-pressed="${String(X)}">Social</button></div></div><label for="src-url"${X?" hidden":""}><span>Link</span><input class="sel" id="src-url" placeholder="Link" spellcheck="false" autocomplete="off"></label><div class="two plat"${X?"":" hidden"}><label for="src-plat"><span>Platform</span><select class="sel" id="src-plat">${Object.entries(bt).map(([o,l])=>f`<option value="${o}">${l}</option>`)}</select></label><label for="src-q"><span>Follow</span><input class="sel" id="src-q" placeholder="keyword, #hashtag or @account" maxlength="100" spellcheck="false" autocomplete="off"></label></div><div class="two"><label for="src-label"><span>Label <i>optional</i></span><input class="sel" id="src-label" placeholder="${X?"e.g. Mentions on X":"e.g. Feedback Q4"}"></label><label for="src-cat"><span>Category <i>optional</i></span><input class="sel" id="src-cat" placeholder="${X?"e.g. Social":"e.g. Bugs"}" maxlength="40"></label></div><label for="src-dir"><span>Project</span><select class="sel" id="src-dir">${u?"":f`<option value="">Choose project…</option>`}${R(u)}</select></label><div class="acts"><span class="end"></span><button class="btn pri" id="src-add">Add source</button></div></div>`,se=(o)=>{let l=v.find((k)=>k.dir===o.dir&&k.url===o.source);return l?l.label||g(l.url):g(o.source)},L=(o)=>o.dir+" "+o.id,N=(o)=>o.dir+" "+o.source,T=(o)=>v.find((l)=>l.dir===o.dir&&l.url===o.source),O=(o)=>o.status==="dismissed"?"Dismissed":o.status==="done"?"Done":o.category||T(o)?.category||x(T(o)||{url:o.source}),K=(o,l)=>{let k=new Map;return o.forEach((S)=>{let D=l(S);k.set(D,{it:S,n:(k.get(D)?.n||0)+1})}),k},V=u?new Map:K(w,(o)=>o.dir);if(!u){for(let o of i.shown)if(!V.has(o)){let l=Ie().find((k)=>k.dir===o);if(l)V.set(o,{it:{dir:o,project:l.label},n:0})}}let ve=(o)=>u||!i.shown.size||i.shown.has(o.dir),Dn=w.filter(ve),lt=K(Dn,O);if(ne&&!lt.has(ne))ne="";let Rs=Dn.filter((o)=>!ne||O(o)===ne),ct=K(Rs,N);if(he&&!ct.has(he))he="";let Hs=new Set(w.map(L));if(ie&&!Hs.has(ie))ie=null;if(Ae!==ie)Ae=null;let dt=new Map;w.forEach((o,l)=>{if(!ve(o)||(ne?O(o)!==ne:o.status==="dismissed"||o.status==="done")||he&&N(o)!==he)return;let k=O(o);dt.has(k)||dt.set(k,[]),dt.get(k).push(l)});let B=[...dt.values()].flat();{let o=new Set(B.map((l)=>L(w[l])));[...J].forEach((l)=>o.has(l)||J.delete(l))}if(!B.some((o)=>L(w[o])===me))me=B.length?L(w[B[0]]):null;let ut=B.filter((o)=>J.has(L(w[o]))).length,Ns=V.size>1,Pn=(o,l,k,S)=>f`<div class="combo ice-combo" id="${o}"><button class="sel" aria-label="${l}" aria-haspopup="listbox" aria-expanded="false">${k}</button><div class="menu-pop" hidden><input class="sel" aria-label="Search ${l.toLowerCase()}" placeholder="Search…" spellcheck="false" autocomplete="off"><div class="pick-list" role="listbox">${S}</div></div></div>`,xt=(o,l,k,S,D="")=>f`<button type="button" role="option" data-v="${o}" data-q="${S.toLowerCase()}" class="${l?"on":""}${D}" aria-selected="${String(l)}">${k}</button>`,St=new Map;for(let[o,{it:l}]of ct){let k=x(T(l)||{url:l.source});St.has(k)||St.set(k,[]),St.get(k).push([o,l])}let Jt=(o)=>y(o.source,"link",T(o)?.social),Yt=he&&[...ct].find(([o])=>o===he)?.[1].it,Rn=f`${lt.size>1||lt.has("Dismissed")||lt.has("Done")?Pn("ice-fc","Category",f`<span>${ne||"All categories"}</span>`,f`${xt("",!ne,f`<span>All categories</span>`,"all categories")}${[...lt].map(([o,{n:l}])=>xt(o,ne===o,f`<span>${o}</span><i>${l}</i>`,o))}`):""}${ct.size>1?Pn("ice-fs","Source",Yt?f`${Jt(Yt)}<span>${se(Yt)}</span>`:f`<span>All sources</span>`,f`${xt("",!he,f`<span>All sources</span>`,"all sources")}${[...St].map(([o,l])=>f`<div class="grp" data-g="${o.toLowerCase()}">${Jt(l[0][1])}${o}</div>${l.map(([k,S])=>xt(k,he===k,f`${Jt(S)}<span>${se(S).toLowerCase().startsWith(o.toLowerCase()+":")?se(S).slice(o.length+1).trim():se(S)}</span><i>${ct.get(k).n}</i>`,o+" "+se(S)," sub"))}`)}`):""}`,Ks=f`<div class="ice-bar">${B.length?f`<input type="checkbox" id="ice-all" aria-label="Select every request shown"${ut&&ut===B.length?" checked":""}>`:""}<span class="count">${ne==="Dismissed"||ne==="Done"?`${B.length} ${ne.toLowerCase()}`:`${B.length<E?`${B.length} of ${E}`:E} waiting`}</span><span class="end"></span>${String(Rn)?f`<details class="ice-filt"><summary class="ib${ne||he?" on":""}" aria-label="Filter requests" title="Filter requests">${q("filter")}</summary><div class="ice-fpop">${Rn}</div></details>`:""}<button class="btn ghost sm" id="ice-new" title="Add a request by hand">${q("plus")} Add</button><button class="btn sm" id="src-go"${v.length?"":" disabled"}>${q("refresh")} Gather all</button><button class="btn ghost sm" id="src-tog" aria-expanded="${String(gt??!v.length)}" aria-controls="src-sec">${q("link")} Sources · ${v.length}</button></div>`,Hn=J.size?f`<div class="ice-bulk" role="toolbar" aria-label="Selected requests"><span>${J.size} selected</span><span class="end"></span><button class="btn sm" id="ice-bp" title="Promote (p)">${q("plus")} Promote</button><button class="btn ghost sm" id="ice-bd" title="Dismiss (d)">${q("x")} Dismiss</button><button class="btn ghost sm" id="ice-bc" title="Clear selection (Esc)">Clear</button></div>`:"",Is=(o)=>f`<div class="ice-form"><label for="ice-title" class="hint" style="margin:0">Idea title</label><input class="sel" id="ice-title" data-k="${L(o)}" maxlength="200" value="${o.text}" aria-label="Idea title"><label for="ice-details" class="hint" style="margin:0">Details</label><textarea class="sel" id="ice-details" rows="3" aria-label="Details"></textarea><div class="ice-form-acts"><span class="end"></span><button class="btn ghost sm" id="ice-cancel">Cancel</button><button class="btn pri sm" id="ice-ok">Promote</button></div></div>`,Os=(o)=>{if(!o.post)return"";if(o.replied)return f`<span class="hint" style="margin:0">You replied to this post ${Be(o.replied)}.</span>`;let l=ae&&ae.k===L(o)?ae:null,k=(S)=>f`<select class="sel" id="ice-rst" aria-label="Reply style"${S?" disabled":""}>${Object.entries(ao).map(([D,G])=>f`<option value="${D}"${D===pn(o.dir)?" selected":""}>${G}</option>`)}</select>`;if(!l)return f`<div class="ice-form-acts"><span class="hint">You review it before it posts.</span>${b.connected?k(!1):""}<button class="btn sm" id="ice-rp">Reply</button></div>`;if(l.wait)return f`<div class="ice-form-acts"><span class="hint">Drafting…</span></div>`;return f`<div class="ice-form"><label for="ice-reply" class="hint" style="margin:0">Your reply${l.note?` · ${l.note}`:""}</label><textarea class="sel" id="ice-reply" rows="3" maxlength="2000" aria-label="Your reply"${l.busy||l.again?" disabled":""}>${l.text}</textarea><span class="hint" id="ice-rn" style="margin:0">${l.again?"Writing another…":`${l.text.length} chars`}</span><div class="ice-form-acts"><span class="end"></span>${b.connected?f`${k(l.busy||l.again)}<button class="btn ghost sm" id="ice-rg"${l.busy||l.again?" disabled":""}>Regenerate</button>`:""}<button class="btn ghost sm" id="ice-rc"${l.busy?" disabled":""}>Cancel</button><button class="btn pri sm" id="ice-rs"${l.busy||l.again||!l.text.trim()?" disabled":""}>${l.busy?"Sending…":"Send reply"}</button></div></div>`},zs=(o)=>{let l=w[o],k=L(l),S=ie===k,D=f`<div class="ice-row${me===k?" cur":""}${J.has(k)?" sel":""}" data-i="${o}" data-k="${k}"><input type="checkbox" id="ice-ck-${o}" data-ck="${o}"${J.has(k)?" checked":""} aria-label="Select: ${l.text}"><span class="ice-src" title="${se(l)}">${y(l.source,"link",v.find((U)=>U.dir===l.dir&&U.url===l.source)?.social)}</span><button class="ice-t" id="ice-o-${o}" data-o="${o}" aria-expanded="${String(S)}" title="${l.text}">${l.text}</button><span class="ice-meta">${Ns?f`<span class="nm" title="${l.project||""}">${ot(l.dir,l.project||"")}${Te(l.project||"")}</span>`:""}${l.status==="done"&&l.written?f`<span title="Written back ${Be(l.written)}">written back</span>`:""}${l.status==="done"&&l.writeError?f`<span title="${l.writeError}">write-back failed</span>`:""}${l.status==="promoted"?f`<span title="Promoted to Ideas: ${l.idea||""}">promoted</span>`:""}${l.replied?f`<span title="Replied ${Be(l.replied)}">replied</span>`:""}<span class="ice-age">${Be(l.at)}</span><span class="ice-ra">${l.status==="promoted"||l.status==="done"?"":f`<button class="btn ghost sm" id="ice-pr-${o}" data-pr="${o}" title="Promote to Ideas (p)" aria-label="Promote to Ideas: ${l.text}">${q("plus")}</button>`}${l.status==="promoted"||l.status==="done"?"":l.status==="dismissed"?f`<button class="btn ghost sm" id="ice-rs-${o}" data-rs="${o}" title="Restore to waiting" aria-label="Restore: ${l.text}">Restore</button>`:f`<button class="btn ghost sm" id="ice-ds-${o}" data-ds="${o}" title="Dismiss (d)" aria-label="Dismiss: ${l.text}">${q("x")}</button>`}</span></span></div>`,G=S?f`<div class="ice-open">${l.notes?f`<div class="ice-notes">${l.notes}</div>`:""}${(l.images||[]).filter((U)=>/^https:\/\//.test(U)).length?f`<details class="ice-imgs"><summary>Show images (${l.images.filter((U)=>/^https:\/\//.test(U)).length}) — loads them from the source's site</summary>${l.images.filter((U)=>/^https:\/\//.test(U)).map((U,F)=>{let ce=`/ice-image?dir=${encodeURIComponent(l.dir)}&id=${encodeURIComponent(l.id)}&i=${F}`;return f`<a href="${ce}" target="_blank" rel="noopener noreferrer"><img data-src="${ce}" alt="" referrerpolicy="no-referrer"></a>`})}</details>`:""}<span class="hint" style="margin:0">From ${se(l)} · ${Be(l.at)}${l.post?f` · <a href="${l.post}" target="_blank" rel="noopener">open post</a>`:""}</span>${Os(l)}${Ae===k?Is(l):""}</div>`:"";return f`${D}${G}`},Gs=E&&!B.length?f`<div class="ice-zero"><h4>No matches</h4><p style="margin-top:10px"><button class="btn sm" id="ice-unf">Clear filters</button></p></div>`:B.length?f`<div class="ice-list">${[...dt].map(([o,l])=>f`<div class="ice-cat">${o}<i>${l.length}</i></div>${l.map(zs)}`)}</div>`:f`<div class="ice-zero"><h4>Nothing waiting</h4></div>`,Lt=gt??!v.length,Zt=Lt&&!s("src-sec")?.classList.contains("open"),Us=f`<div class="src-scrim${Lt&&!Zt?" on":""}" id="src-scrim"></div><aside class="src-sec${Lt&&!Zt?" open":""}" id="src-sec" aria-label="Sources"${Lt?"":" inert"}><header><div class="t"><b>Sources</b><div class="src-chips"><a id="src-to-settings" class="schip" role="button" tabindex="0" title="${p&&p.every&&p.every!=="off"?`Checked every ${p.every}`:"Auto-check off"}: change in Settings">${q("clock")}${p&&p.every&&p.every!=="off"?p.every:"Off"}</a>${b.connected?f`<span class="schip on" title="ChatGPT${b.email?": "+b.email:""}">${q("ai")}ChatGPT<a id="cg-out" role="button" tabindex="0" aria-label="Disconnect ChatGPT" title="Disconnect">${q("x")}</a></span>`:f`<a id="cg-in" class="schip" role="button" tabindex="0" title="Connect ChatGPT">${q("ai")}ChatGPT</a>`}${(p.google||{}).connected?f`<span class="schip on" title="Google connected">${q("google")}Google<a id="gg-out" role="button" tabindex="0" aria-label="Disconnect Google" title="Disconnect">${q("x")}</a></span>`:f`<a id="gg-in" class="schip" role="button" tabindex="0" title="Connect Google">${q("google")}Google</a>`}</div></div><div class="src-hda">${v.length?f`<button class="btn sm" id="src-new" aria-expanded="${String(xe)}" aria-controls="src-addbox">${q("plus")} Add</button>`:""}<button class="ib" id="src-x" aria-label="Close sources" title="Close">${q("close")}</button></div></header><div class="body">${le}${v.length?f`<div class="src-list">${Q}</div>`:Pe}</div></aside>`,Fs=b.use==="ask"&&!b.connected?f`<div class="cg-onb"><div class="acts"><b>Gather social posts with ChatGPT?</b><span class="end"></span><button class="btn ghost" id="cg-skip">Skip</button><button class="btn pri" id="cg-go">Connect</button></div></div>`:"",_s=s("src-sec")?.querySelector(".body")?.scrollTop??0;if(s("pbody").innerHTML=p&&p.error?f`<p style="color:var(--dim)">${p.error}</p>`:!p?f`<p style="color:var(--dim)">The icebox could not be read.</p>`:f`${Fs}${Ks}${Hn}${re}${Gs}${Us}`,!p||p.error)return;let Nn=s("src-sec")?.querySelector(".body");if(Nn)Nn.scrollTop=_s;if(c&&c[0]===ie&&s("ice-title"))s("ice-title").value=c[1],s("ice-details").value=c[2];if(s("src-add")){if(t[5])s("src-plat").value=t[5];if(s("src-q").value=t[6]||"",s("src-url").value=t[0],s("src-label").value=t[1],s("src-cat").value=t[4]||"",t[3]!==void 0&&s("src-dir"))s("src-dir").value=t[3]}if(s("src-to-settings"))s("src-to-settings").onclick=()=>{i.setQ="sources",fe("settings",!0)};let Ws=async()=>{let o=window.open("","_blank"),l=await A("/chatgpt",{op:"connect"});if(l.ok&&o)o.location=l.url;else if(o?.close(),l.ok)location.href=l.url};for(let o of["cg-go","cg-in"])if(s(o))s(o).onclick=Ws;if(s("gg-in"))s("gg-in").onclick=async()=>{if(!(p.google||{}).hasClient){C("Add your Google client in Settings first"),i.setQ="google",fe("settings",!0);return}let o=window.open("","_blank"),l=await A("/google",{op:"connect"});if(l.ok&&o)o.location=l.url;else if(o?.close(),l.ok)location.href=l.url;else C(l.error||"Could not start Google sign-in")};if(s("gg-out"))s("gg-out").onclick=async()=>{if(!confirm("Disconnect Google? Done requests stop being written to sheets."))return;if((await A("/google",{op:"disconnect"})).ok)C("Google disconnected"),z()};if(s("cg-skip"))s("cg-skip").onclick=async()=>{if((await A("/chatgpt",{op:"skip"})).ok)z()};if(s("cg-out"))s("cg-out").onclick=async()=>{if(!confirm("Disconnect ChatGPT? Gather goes back to your agent."))return;if((await A("/chatgpt",{op:"disconnect"})).ok)C("ChatGPT disconnected"),z()};if(Zt)s("src-sec").offsetWidth,s("src-sec").classList.add("open"),s("src-scrim").classList.add("on");let qt=()=>{gt=!1,s("src-sec").classList.remove("open"),s("src-scrim").classList.remove("on"),s("src-sec").inert=!0,s("src-tog").setAttribute("aria-expanded","false"),s("src-tog").focus()};if(s("src-x").onclick=s("src-scrim").onclick=qt,s("src-new"))s("src-new").onclick=()=>{let o=s("src-addbox");if(st=o.hidden,o.hidden=!st,s("src-new").setAttribute("aria-expanded",st),st)s(nt==="social"?"src-q":"src-url").focus()};if(s("src-sec").onkeydown=(o)=>{if(o.key==="Escape")o.stopPropagation(),qt()},s("src-tog").onclick=()=>{if(s("src-sec").classList.contains("open"))return qt();gt=!0,z().then(()=>gt&&s("src-url")?.focus())},us("ice-fs",(o)=>{he=o,z()}),us("ice-fc",(o)=>{ne=o,z()}),s("ice-unf"))s("ice-unf").onclick=()=>{if(ne=he="",!u)i.shown.clear();zt(),z()};s("pbody").querySelectorAll("[data-kind]").forEach((o)=>o.onclick=()=>{if(nt===o.dataset.kind)return;nt=o.dataset.kind,z().then(()=>s(nt==="social"?"src-q":"src-url")?.focus())});let ze=async(o,l)=>{let k=await A("/sources",o);if(k.ok)l(k),I();if(Y())z()};s("pbody").querySelectorAll("[data-mv]").forEach((o)=>{let l=v[+o.dataset.mv],k=s("src-mb-"+o.dataset.mv),S=n.get(o.dataset.k);if(S&&[...o.options].some((D)=>D.value===S))o.value=S;(o.onchange=()=>{k.disabled=o.value===l.dir})(),k.onclick=()=>ze({op:"move",dir:l.dir,to:o.value,url:l.url},()=>C("Source moved"))}),s("pbody").querySelectorAll("[data-wb]").forEach((o)=>{let l=v[+o.dataset.wb];o.onchange=()=>{if(o.checked&&l.social===void 0&&/docs\.google\.com/.test(l.url)&&!(p.google||{}).connected){alert("Connect Google first (Sources header), so kaizen can write to the sheet."),o.checked=!1;return}if(o.checked&&!confirm("When a promoted request's run is done, kaizen will write that back to this source. A closed issue or a changed cell is visible to others. Turn on?")){o.checked=!1;return}ze({op:"writeback",dir:l.dir,url:l.url,on:o.checked},()=>C(o.checked?"Write back on":"Write back off"))}}),s("pbody").querySelectorAll("[data-ct]").forEach((o)=>{let l=v[+o.dataset.ct],k=s("src-cs-"+o.dataset.ct),S=m.get(o.dataset.k);if(S!==void 0)o.value=S;(o.oninput=()=>{k.disabled=o.value.trim()===(l.category||"")})(),o.onkeydown=(D)=>{if(D.key==="Enter"&&!k.disabled)k.click()},k.onclick=()=>{k.disabled=!0,ze({op:"category",dir:l.dir,url:l.url,category:o.value.trim()},()=>C(o.value.trim()?`New requests from ${l.label||g(l.url)} go under ${o.value.trim()}`:"Category cleared"))}});let Kn=()=>{if(!document.activeElement||document.activeElement===document.body)s("pbody").querySelector(".ice-row.cur .ice-t")?.focus()},In=(o)=>{let l=B.findIndex((D)=>L(w[D])===me);if(l<0||!o.has(me))return;let k=(D)=>!o.has(L(w[D])),S=B.slice(l+1).find(k)??B.slice(0,l).reverse().find(k);me=S===void 0?null:L(w[S])},Xt=(o,l,k,S={})=>(async()=>{if(In(new Set([L(l)])),(await A("/icebox",{op:o,dir:l.dir,ids:[l.id],...S})).ok){if(o==="promote"&&ie===L(l))ie=Ae=null;C(k),I()}if(Y())z().then(Kn)})(),On=async(o)=>{let l=w.filter((S)=>J.has(L(S)));if(!l.length)return;In(new Set(l.map(L))),Kt=!0,s("ice-bp").disabled=s("ice-bd").disabled=!0;let k=0;try{if(o==="dismiss")for(let S of new Set(l.map((D)=>D.dir))){let D=l.filter((G)=>G.dir===S).map((G)=>G.id);if((await A("/icebox",{op:o,dir:S,ids:D})).ok)k+=D.length}else for(let S of l)if((await A("/icebox",{op:o,dir:S.dir,ids:[S.id]})).ok)k++}finally{Kt=!1}if(J.clear(),k)C(`${k} ${o==="promote"?"promoted to Ideas":"dismissed"}${k<l.length?`, ${l.length-k} not`:""}`);if(I(),Y())z().then(Kn)};if(Hn)s("ice-bp").onclick=()=>On("promote"),s("ice-bd").onclick=()=>On("dismiss"),s("ice-bc").onclick=()=>{J.clear(),z()};if(s("ice-all"))s("ice-all").onchange=()=>{let o=B.map((l)=>L(w[l]));if(ut===B.length)o.forEach((l)=>J.delete(l));else o.forEach((l)=>J.add(l));z()};if(s("ice-all"))s("ice-all").indeterminate=ut>0&&ut<B.length;s("pbody").querySelectorAll("[data-ck]").forEach((o)=>o.onchange=()=>{let l=L(w[+o.dataset.ck]);me=l,o.checked?J.add(l):J.delete(l),z()});let zn=(o)=>{let l=L(w[o]);me=l,ie=ie===l?null:l,Ae=null,z().then(()=>s("ice-o-"+o)?.focus())};s("pbody").querySelectorAll(".ice-row").forEach((o)=>o.onclick=(l)=>{if(!l.target.closest("input,button"))zn(+o.dataset.i)}),s("pbody").querySelectorAll("[data-o]").forEach((o)=>o.onclick=()=>zn(+o.dataset.o)),s("pbody").querySelectorAll("details.ice-imgs").forEach((o)=>o.ontoggle=()=>o.open&&o.querySelectorAll("img[data-src]").forEach((l)=>l.src=l.dataset.src)),s("pbody").querySelectorAll("[data-pr]").forEach((o)=>o.onclick=()=>{me=ie=Ae=L(w[+o.dataset.pr]),z().then(()=>{s("ice-title")?.focus(),s("ice-title")?.select()})}),s("pbody").querySelectorAll("[data-ds]").forEach((o)=>o.onclick=()=>{o.disabled=!0,Xt("dismiss",w[+o.dataset.ds],"Dismissed")}),s("pbody").querySelectorAll("[data-rs]").forEach((o)=>o.onclick=()=>{o.disabled=!0,Xt("restore",w[+o.dataset.rs],"Restored")});let pt=w.find((o)=>L(o)===ie);if(s("ice-rp"))s("ice-rp").onclick=async()=>{let o=pt,l=L(o);ae={k:l,text:"",wait:!0},z();let k=await A("/reply",{op:"draft",dir:o.dir,id:o.id,style:pn(o.dir)});if(!ae||ae.k!==l)return;ae={k:l,text:k.text||"",note:k.ok?k.note||"":"",wait:!1},z().then(()=>s("ice-reply")?.focus())};if(s("ice-rst"))s("ice-rst").onchange=(o)=>{let l=pt.dir;yt[l]=o.target.value;try{localStorage.setItem("kz-reply-style:"+l,yt[l])}catch(k){}};if(s("ice-rg"))s("ice-rg").onclick=async()=>{let o=pt,l=ae;l.again=!0,z();let k=await A("/reply",{op:"draft",dir:o.dir,id:o.id,style:pn(o.dir),avoid:l.text});if(ae!==l)return;if(l.again=!1,k.text)l.text=k.text;l.note=k.ok?k.note||"":"",z().then(()=>s("ice-reply")?.focus())};if(s("ice-reply")){let o=s("ice-reply");o.oninput=()=>{ae.text=o.value,s("ice-rn").textContent=`${o.value.length} chars`,s("ice-rs").disabled=!o.value.trim()},o.onkeydown=(l)=>{if(l.key==="Escape")l.stopPropagation(),s("ice-rc").click()},s("ice-rc").onclick=()=>{ae=null,z()},s("ice-rs").onclick=async()=>{let l=pt,k=ae;k.busy=!0,z();let S=await A("/reply",{op:"send",dir:l.dir,id:l.id,text:k.text});if(S.ok)ae=null,C("Reply posted");else if(S.sent)ae=null;else k.busy=!1;if(I(),Y())z()}}if(s("ice-ok")){let o=()=>{let l=s("ice-title").value.trim();if(!l){s("ice-title").focus();return}s("ice-ok").disabled=!0,Xt("promote",pt,"Promoted to Ideas",{text:l,details:s("ice-details").value})};s("ice-ok").onclick=o,s("ice-cancel").onclick=()=>{Ae=null,z()},s("ice-title").onkeydown=(l)=>{if(l.key==="Enter")o();if(l.key==="Escape")s("ice-cancel").click()},s("ice-details").onkeydown=(l)=>{if(l.key==="Enter"&&(l.ctrlKey||l.metaKey))o();if(l.key==="Escape")s("ice-cancel").click()}}It=(o)=>{if(o.altKey||o.ctrlKey||o.metaKey)return!1;let l=o.key,k=B.findIndex((G)=>L(w[G])===me),S=B[k];if(l==="Escape"){if(s("src-sec")?.classList.contains("open"))return qt(),!0;if(ie){if(Ae)Ae=null;else ie=null;return z(),!0}if(J.size)return J.clear(),z(),!0;return!1}if(k<0)return!1;if(l==="j"||l==="k"){o.preventDefault();let G=B[Math.max(0,Math.min(B.length-1,k+(l==="j"?1:-1)))];return me=L(w[G]),s("pbody").querySelectorAll(".ice-row").forEach((U)=>U.classList.toggle("cur",+U.dataset.i===G)),s("pbody").querySelector(`.ice-row[data-i="${G}"]`)?.scrollIntoView({block:"nearest"}),!0}let D=l==="o"?s("ice-o-"+S):l==="x"?s("ice-ck-"+S):l==="p"?J.size?s("ice-bp"):s("ice-pr-"+S):l==="d"?J.size?s("ice-bd"):s("ice-ds-"+S):null;if(!D||D.disabled)return!1;return o.preventDefault(),D.click(),!0},s("pbody").querySelectorAll("[data-chk]").forEach((o)=>{let l=v[+o.dataset.chk];o.onclick=()=>{o.disabled=!0,o.innerHTML=q("refresh")+" Checking…",ze({op:"gather",dir:l.dir,url:l.url},(k)=>{let S=k.results&&k.results[0];C(S?S.ok?`${l.label||g(l.url)}: ${S.note}${S.added?" in the icebox":""}`:`Failed: ${S.note}`:"Checked")})}}),s("pbody").querySelectorAll("[data-ag]").forEach((o)=>{let l=v[+o.dataset.ag];o.onclick=()=>{if(o.disabled=!0,M)o.innerHTML=q("refresh")+" Gathering…";ze({op:"agent",dir:l.dir,url:l.url},(k)=>{let S=k.results&&k.results[0];C(k.via==="chatgpt"?S&&S.ok?`${l.label||g(l.url)}: ${S.note}${S.added?" in the icebox":""}`:`Failed: ${S?S.note:"no answer"}`:"/kaizen gather opened in your terminal: "+Ne(k.agent))})}}),s("pbody").querySelectorAll("[data-rm]").forEach((o)=>{let l=v[+o.dataset.rm];o.onclick=()=>{if(!confirm(`Remove ${l.label||g(l.url)}? What it gathered stays; added again later, it may bring back ones you deleted or dismissed.`))return;ze({op:"remove",dir:v[+o.dataset.rm].dir,url:v[+o.dataset.rm].url},()=>C("Source removed"))}});let Gn=w.findIndex((o)=>L(o)===me);if(r){let o=w.findIndex((l)=>L(l)===r[0]);(o>=0?s(r[1]+"-"+o):Gn>=0?s("ice-o-"+Gn):null)?.focus()}let Ct=!r&&t[2]&&s(t[2]);if(Ct)(Ct.disabled?s(Ct.id.replace("src-mb-","src-mv-").replace("src-cs-","src-ct-")):Ct)?.focus();if(s("src-add"))s("src-add").onclick=()=>{let o=nt==="social",l=s("src-url").value.trim(),k=s("src-q").value.trim(),S=s("src-plat").value;if(o?!k:!l){s(o?"src-q":"src-url").focus();return}let D=s("src-dir").value;if(!D){s("src-dir").focus();return}s("src-add").disabled=!0,s("src-add").textContent="Adding…",(async()=>{let G=s("src-label").value.trim()||(o?`${bt[S]}: ${k}`:""),U=s("src-cat").value.trim(),F=await A("/sources",o?{op:"add",dir:D,platform:S,query:k,label:G,category:U}:{op:"add",dir:D,url:l,label:G,category:U});if(F.ok&&o)s("src-q").value=s("src-label").value=s("src-cat").value="",C("Source added"),I();else if(F.ok){s("src-url").value=s("src-label").value=s("src-cat").value="";let ce=await A("/sources",{op:"gather",dir:D,url:l}),pe=ce.results&&ce.results[0];C(pe?pe.ok?`Source added: ${pe.note} in the icebox`:`Source added, but: ${pe.note}`:"Source added"),I()}else C(F.error||"Could not add source");if(F.ok)st=!1;if(Y())z()})()},s("src-url").onkeydown=s("src-q").onkeydown=s("src-label").onkeydown=s("src-cat").onkeydown=(o)=>{if(o.key==="Enter")s("src-add").click()};if(s("ice-new"))s("ice-new").onclick=()=>{let o=!u;if(!u&&!Ie().length)return C("Pick a project first");let l=Ue(`${o?'<div class="combo" id="ndc"><button type="button" class="sel" id="newdir" aria-label="Project for this request" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>':""}<textarea id="newidea" placeholder="What did someone ask for?" aria-label="Request"></textarea><div class="att"></div><input type="file" multiple hidden><div class="row"><button type="button" class="ib" id="newpick" aria-label="Attach files" title="Attach files">${q("clip")}</button><button type="button" class="btn ghost" value="">Cancel</button><button class="btn pri">Add</button></div>`),k=l.querySelector("form"),S=k.querySelector("textarea"),D=[],G=(F)=>{D.push(F),k.querySelector(".att").textContent=D.map((ce)=>ce.name).join(" · ")};if(k.querySelector("#newpick").onclick=()=>k.querySelector("input[type=file]").click(),k.querySelector("input[type=file]").onchange=(F)=>{[...F.target.files].forEach(G),F.target.value=""},S.onpaste=(F)=>{for(let ce of F.clipboardData.items)if(ce.kind==="file"){let pe=ce.getAsFile();G(new File([pe],pe.name==="image.png"?`paste-${Date.now()}.png`:pe.name,{type:pe.type})),F.preventDefault()}},k.querySelector(".btn.ghost").onclick=()=>l.close(),o)Ot(k);let U=Fe([S]);if(U)k.querySelector(".row").prepend(U);l.addEventListener("close",()=>de(!1)),S.onkeydown=(F)=>{if(F.key==="Enter"&&!F.shiftKey)F.preventDefault(),k.requestSubmit()},k.onsubmit=async(F)=>{F.preventDefault();let ce=S.value.trim(),pe=u||Me;if(!ce)return S.focus();if(!pe)return C("Pick a project first");if((await A("/icebox",{op:"add",dir:pe,text:ce,files:await At(D)})).ok)C("Added to the icebox"),l.close()},S.focus()};if(s("src-go"))s("src-go").onclick=()=>{s("src-go").disabled=!0,s("src-go").innerHTML=q("refresh")+" Gathering…",ze({op:"gather",dir:u||void 0},(o)=>{let l=o.results||[],k=(U)=>v.some((F)=>F.url===U.url&&F.social),S=l.reduce((U,F)=>U+F.added,0),D=l.filter((U)=>!U.ok&&!k(U)).length,G=l.filter(k).length;C((S?`${S} new in the icebox`:"Nothing new")+(D?`, ${D} source${D===1?"":"s"} could not be read`:"")+(G?`; ${G} social for /kaizen gather`:""))})}}var fn=new URLSearchParams(location.search).has("all");function Oe(e){if(fn)return;try{e?localStorage.setItem("kz-proj",e):localStorage.removeItem("kz-proj")}catch(t){}}var Te=(e)=>e.split(/[\\/]/).pop()||e,ot=(e,t="")=>{let n=f`<span class="plogo" aria-hidden="true">${(Te(t)||"?").charAt(0).toUpperCase()}</span>`;return i.S?.projects?.some((a)=>a.dir===e&&a.logo)?f`<img class="plogo" src="/project-logo?dir=${encodeURIComponent(e)}" alt="" loading="lazy" onerror="this.outerHTML=this.dataset.f" data-f="${String(n)}">`:n},Ut=(e,t)=>f`${ot(e,t)}<span>${Te(t)}</span>`;function _t(){let e=s("projq").value.trim().toLowerCase(),t=0;s("projs").innerHTML=i.PL.map((a,r)=>a[0].toLowerCase().includes(e)?f`${a[2]&&!t++?f`<div class="grp">In your projects folder</div>`:""}<button role="option" data-i="${r}" title="${a[0]}">${a[1]==="all"?f`<span class="plogo" aria-hidden="true">${q("board")}</span><span>${a[0]}</span>`:a[1]==="__new"||a[1]==="__find"||a[1]==="__clone"?f`<span class="plogo" aria-hidden="true">${q("plus")}</span><span>${a[0]}</span>`:Ut(a[1],a[0])}</button>`:"").join("")||'<div class="grp">No match</div>';let n=s("projs").querySelector("button");if(n)n.classList.add("on")}function Gt(e){if(s("projpop").hidden=!e,s("proj").setAttribute("aria-expanded",e),e)s("projq").value="",_t(),s("projq").focus()}async function io(e){if(e==="__find"){C("Searching…");let t=await A("/find",{});if(t.ok)C(t.added?`Found ${t.added} new project${t.added===1?"":"s"}`:"No new projects found")}else if(e==="__new"){let t=Ue('<b>New project</b><input name="name" placeholder="Project name" aria-label="Project name" spellcheck="false" autocomplete="off"><div class="row"><input name="to" placeholder="Folder to create" aria-label="Create in" spellcheck="false" autocomplete="off"><button type="button" class="btn ghost" data-a="pick">Pick…</button></div><div class="row"><button type="button" class="btn ghost" data-a="x">Cancel</button><button class="btn pri">Create</button></div>'),n=t.querySelector("form"),a=n.querySelector("[name=name]"),r=n.querySelector("[name=to]"),c="";a.oninput=async()=>{let u=a.value.trim();if(!u||/[\\/:*?"<>|]/.test(u)||r.value.trim()&&r.value!==c)return;let h=await A("/new-project",{name:u,check:!0});if(h.ok&&a.value.trim()===u)r.value=c=h.to},n.querySelector("[data-a=x]").onclick=()=>t.close(),n.querySelector("[data-a=pick]").onclick=async()=>{let u=await A("/pick-folder",{});if(u.path)r.value=u.path.replace(/[\\/]+$/,"")+(u.path.includes("\\")?"\\":"/")+(a.value.trim()||"")},a.focus();let m=await new Promise((u)=>{t.addEventListener("close",()=>u(null)),n.onsubmit=async(h)=>{if(h.preventDefault(),!a.value.trim())return a.focus();let p=await A("/new-project",{name:a.value.trim(),to:r.value.trim()||void 0});if(!p.ok)return;u(p.dir),t.close()}});if(!m)return;await A("/cd",{dir:m}),i.all=!1,i.dir=m,Oe(m),C("Project created")}else if(e==="__clone"){let t=Ue('<b>Clone from GitHub</b><input name="url" placeholder="https://github.com/owner/repo" aria-label="GitHub link (https or ssh)" spellcheck="false" autocomplete="off"><div class="row"><input name="to" placeholder="Folder to clone into" aria-label="Clone into" spellcheck="false" autocomplete="off"><button type="button" class="btn ghost" data-a="pick">Pick…</button></div><div class="row"><button type="button" class="btn ghost" data-a="x">Cancel</button><button class="btn pri">Clone</button></div>'),n=t.querySelector("form"),a=n.querySelector("[name=url]"),r=n.querySelector("[name=to]"),c="",m="",u=async()=>{let p=a.value.trim();if(!p)return null;let g=await A("/clone",{url:p,check:!0});if(!g.ok)return null;if(c=g.name,!r.value.trim()||r.value===m)r.value=m=g.to;return g};a.onchange=u,n.querySelector("[data-a=x]").onclick=()=>t.close(),n.querySelector("[data-a=pick]").onclick=async()=>{if(!await u())return a.focus();let p=await A("/pick-folder",{});if(p.path)r.value=p.path.replace(/[\\/]+$/,"")+(p.path.includes("\\")?"\\":"/")+c},a.focus();let h=await new Promise((p)=>{t.addEventListener("close",()=>p(null)),n.onsubmit=async(g)=>{if(g.preventDefault(),!await u())return a.focus();C("Cloning…");let y=await A("/clone",{url:a.value.trim(),to:r.value.trim()||void 0});if(!y.ok)return;p(y.dir),t.close()}});if(!h)return;await A("/cd",{dir:h}),i.all=!1,i.dir=h,Oe(h),C("Cloned")}else if(e==="all")i.all=!0,Oe();else if((await A("/cd",{dir:e})).ok)i.all=!1,i.dir=e,Oe(e);if(await I(),W==="sources")z()}var Me=null;function Ot(e){let t=ro(),n=e.querySelector("#newdir"),a=e.querySelector("#ndc .menu-pop"),r=a.querySelector("input"),c=e.querySelector("#nds");if(!t.some((p)=>p.dir===Me))Me=t[0]?.dir??null;let m=()=>{let p=t.find((g)=>g.dir===Me);n.innerHTML=p?Ut(p.dir,p.label):"Choose project"},u=()=>{let p=r.value.trim().toLowerCase();c.innerHTML=t.filter((g)=>g.label.toLowerCase().includes(p)).map((g)=>f`<button type="button" role="option" data-d="${g.dir}" title="${g.label}" class="${g.dir===Me?"on":""}">${Ut(g.dir,g.label)}</button>`).join("")||'<div class="grp">No match</div>',c.querySelectorAll("button").forEach((g)=>g.onclick=()=>{Me=g.dataset.d,m(),h(!1),e.querySelector("#newidea").focus()})},h=(p)=>{if(a.hidden=!p,n.setAttribute("aria-expanded",p),p)r.value="",u(),r.focus()};n.onclick=(p)=>{p.preventDefault(),h(a.hidden)},r.oninput=u,r.onkeydown=(p)=>{if(p.key==="Escape")p.stopPropagation(),h(!1);if(p.key==="Enter")p.preventDefault(),c.querySelector("button")?.click()},m()}function ro(){let e=Ie(),t=e.filter((n)=>i.shown.has(n.dir));return t.length?t:e}function Ie(){let e=[...i.S.projects];for(let t of i.S.cards)if(!e.some((n)=>n.dir===t.state))e.push({dir:t.state,label:t.where});return e}var at=[];function Ft(){let e=s("showq").value.trim().toLowerCase();at=Ie(),s("shows").innerHTML=at.map((n,a)=>n.label.toLowerCase().includes(e)?f`<button type="button" role="menuitemcheckbox" data-i="${a}" aria-checked="${String(i.shown.has(n.dir))}" title="${n.label}">${Ut(n.dir,n.label)}<svg class="ck"><use href="#i-check"/></svg></button>`:"").join("")||'<div class="grp">No match</div>';let t=at.filter((n)=>i.shown.has(n.dir)).length;s("showcount").textContent=t?`${t} of ${at.length} shown`:`All ${at.length} shown`}function zt(){try{localStorage.setItem("kz-show",JSON.stringify([...i.shown]))}catch(e){}if(H(),Y())z()}function fs(){s("proj").onclick=()=>Gt(s("projpop").hidden),s("projq").oninput=_t,s("projq").onkeydown=(e)=>{let t=[...s("projs").querySelectorAll("button")],n=t.findIndex((a)=>a.classList.contains("on"));if(e.key==="Escape")Gt(!1),s("proj").focus();else if(e.key==="Enter"){if(t[n])t[n].click()}else if((e.key==="ArrowDown"||e.key==="ArrowUp")&&t[n]){e.preventDefault();let a=t[(n+(e.key==="ArrowDown"?1:t.length-1))%t.length];t[n].classList.remove("on"),a.classList.add("on"),a.scrollIntoView({block:"nearest"})}},s("projs").onclick=(e)=>{let t=e.target.closest("button");if(t)Gt(!1),io(i.PL[t.dataset.i][1])},document.addEventListener("click",(e)=>{if(!e.target.closest("#combo"))Gt(!1)}),s("show").onclick=()=>{let e=s("showpop").hidden;if(s("showpop").hidden=!e,s("show").setAttribute("aria-expanded",e),e)s("showq").value="",Ft(),s("showq").focus()},s("showq").oninput=Ft,s("showq").onkeydown=(e)=>{if(e.key==="Escape")s("showpop").hidden=!0,s("show").setAttribute("aria-expanded",!1),s("show").focus()},s("shows").onclick=(e)=>{let t=e.target.closest("button[data-i]"),n=t?.dataset.i,a=at[n]?.dir;if(!a)return;i.shown.has(a)?i.shown.delete(a):i.shown.add(a),zt(),s("shows").querySelector(`[data-i="${n}"]`)?.focus()},s("showall").onclick=()=>{i.shown.clear(),zt()},document.addEventListener("click",(e)=>{if(!e.target.closest("#showc"))s("showpop").hidden=!0,s("show").setAttribute("aria-expanded",!1)}),s("newtop").onclick=vt}var $e=null,vn="",mn=6,Wt=new Set,kt=(()=>{try{return JSON.parse(localStorage.getItem("kz-order"))||{}}catch(e){return{}}})(),Z=!1,wt=(()=>{try{return localStorage.getItem("kz-ideas-tab")==="backlog"?"backlog":"ideas"}catch(e){return"ideas"}})(),je=null,hn=[["high","High+",(e)=>e==="critical"||e==="high"],["medium","Medium",(e)=>e==="medium"],["low","Low",(e)=>e==="low"],["none","No severity",(e)=>!e]],gn=()=>(i.S.backlog||[]).map((e)=>({...e,kind:"backlog",status:e.starting?"starting":"idea",title:e.text,column:0,archived:!1,_k:"backlog:"+e.state+":"+e.source+"#"+e.n})),bn=(e)=>{if(e.kind!=="backlog")return we(e);let t=i.S.cards.find((n)=>n.kind==="run"&&n.id===e.source&&n.state===e.state);if(!t)return C(e.source==="orphan"?"Kept from a run that was pruned":"That run is not on this board");vs(t,"backlog",e.n)},$t=!1,ye=new Set,j=(e)=>e._k||(e.kind==="idea"?"idea:"+e.state+":"+e.text:"run:"+e.state+":"+e.id),lo=(e)=>e.kind==="run"?i.S.cards.filter((t)=>t.parent===e.id&&t.state===e.state):[],co=(e)=>!!e.parent&&i.S.cards.some((t)=>t.kind==="run"&&t.id===e.parent&&t.state===e.state&&!t.archived&&t.column!==4),yn=new Set,ms=(e)=>Xe.has(et(e))?"Sent to agent":e.status!=="waiting"?"Open":{"approvals.plan":"Review plan",findings:"See findings","approvals.review":"Review work"}[e.awaiting]||"Open";function uo(e){let t=j(e),n=document.createElement("div");n.className="card"+(t===i.openKey?" open":"")+(t===i.menuKey?" menu":"")+(e.archived?" archived":""),n.tabIndex=0,n.dataset.k=t,n.setAttribute("role","button");let a=He[e.status]||"idea",r=e.kind==="run"?e.text.replace(/-/g," "):"",c=e.status==="waiting"?ft(e.awaiting)||"needs you":e.status||"idea",m=[],u=[],h=(d,v,w="")=>f`<span class="mk${w}" role="img" title="${v}" aria-label="${v}"><svg><use href="#${d}"/></svg></span>`;if(i.all){let d=e.where.split("/").filter(Boolean).pop()||e.where;m.push(f`<span class="chip proj" title="${e.where}">${d}</span>`)}for(let d of e.tags||[])if(Yn[d])u.push(h("c-"+d,d==="full"?"Full run: separate reviewer":d==="lite"?"Lite run: one session":nn[d]||d,d==="full"||d==="lite"?" accent":""));else if(d==="yolo")u.push(f`<span class="mk warn" title="Started in Yolo: the agent runs commands without asking">yolo</span>`);else m.push(f`<span class="chip">${nn[d]||d}</span>`);if(e.pty&&e.pty.alive&&e.pty.asking)u.push(h("i-ask","The agent is asking you something: open it, terminal button at the top"," warn ask"));else if(e.pty&&e.pty.alive)u.push(h("i-term","The agent is running in this card: open it, terminal button at the top"," run"));let p=e.notes&&/^\[[^\]]*\] (?:Gathered from|Promoted from the icebox:) (.+?)\. Outside text/.exec(e.notes);if(e.parent)m.push(f`<span class="chip link" title="Filed by ${e.parent}">${e.parent.replace(/^\d{4}-\d\d-\d\d-/,"").replace(/-/g," ")}</span>`);if(e.kind==="backlog"){if(e.severity)m.push(f`<span class="chip${e.severity==="critical"||e.severity==="high"?" warn":""}"><i></i>${e.severity}</span>`);m.push(e.source==="orphan"?f`<span class="chip" title="Kept from a pruned run">orphaned</span>`:f`<span class="chip link" title="Left by ${e.source}${e.at?" · "+e.at:""}">${e.source.replace(/^\d{4}-\d\d-\d\d-/,"").replace(/-/g," ")}</span>`)}if(e.branch)m.push(f`<span class="chip" title="Built on its own branch, ${e.branch}">branch</span>`);if(p)m.push(f`<span class="chip" title="From ${p[1]}">gathered</span>`);else if(e.notes)m.push(f`<span class="chip">notes</span>`);if(e.git)u.push(h("c-git",e.git==="pushed"?"Committed & pushed":"Not committed & pushed yet",e.git==="pushed"?" ok":" warn"));if(e.open)u.push(h("i-bug",`${e.open} open finding${e.open===1?"":"s"} to fix`," warn"));let g=$t&&!Z&&e.column===4;if(e.column===4&&!Z)n.classList.add("compact");let y=e.kind==="run"&&!Z&&!g?e.status==="waiting"?f`<button class="btn sm${Xe.has(et(e))?"":" pri"} act" data-a="open">${ms(e)}</button>`:e.status==="stalled"&&Pt(e)?f`<button class="btn sm act" data-a="resume">Resume</button>`:"":"";n.innerHTML=(g?f`<input type="checkbox" class="pick" ${_(ye.has(t)?"checked":"")} aria-label="Select">`:"")+f`<div class="c-id"><span class="st d-${a}" role="img" title="${c}" aria-label="${c}"><svg><use href="#s-${a}"/></svg></span><span class="idt">${r}</span><span class="age">${e.kind==="run"?Jn(e.moved||i.S.now,i.S.now):""}</span><button class="ib sm more" data-a="menu" title="Actions" aria-label="Actions" aria-haspopup="menu" aria-expanded="${String(t===i.menuKey)}">${q("more")}</button></div>
23
+ <p class="snone" id="snone" hidden></p>`,sn();let p=[...s("pbody").querySelectorAll(".sg")],g=()=>{let d=i.setQ.trim().toLowerCase(),v=!1;p.forEach((w)=>{let E=!1;for(let b of w.nextElementSibling.children)b.hidden=!!d&&!b.dataset.q.includes(d),E||=!b.hidden;w.hidden=!E,v||=E}),s("snone").hidden=v,s("snone").textContent=`No setting matches “${i.setQ.trim()}”.`};if(s("sq").oninput=(d)=>{i.setQ=d.target.value,g()},s("sq").onkeydown=(d)=>{if(d.key!=="Escape")return;if(d.preventDefault(),i.setQ)i.setQ="",d.target.value="",g();else ee()},g(),m){let d=s("sq");d.focus(),d.setSelectionRange(d.value.length,d.value.length)}s("gg-save").onclick=async()=>{let d=await A("/google",{op:"client",id:s("gg-id").value,secret:s("gg-secret").value});if(d.ok)C("Saved");else C(d.error||"Enter both the client id and the secret");Ce()},s("gg-conn").onclick=async()=>{if(a.connected)return await A("/google",{op:"disconnect"}),Ce();let d=window.open("","_blank"),v=await A("/google",{op:"connect"});if(v.ok&&d)d.location=v.url;else d?.close(),C(v.error||"Could not start Google sign-in")};let y=(d,v,w,E)=>{let b=Math.max(v.indexOf(w),0),M=(R)=>{b=(b+R+v.length)%v.length,d.querySelector("output").textContent=v[b]===""?"—":v[b],E(v[b])};s(d.id+"-prev").onclick=()=>M(-1),s(d.id+"-next").onclick=()=>M(1),d.onkeydown=(R)=>{if(R.key!=="ArrowLeft"&&R.key!=="ArrowRight")return;R.preventDefault();let Q=s(d.id+(R.key==="ArrowLeft"?"-prev":"-next"));Q.focus(),Q.click()}},x=async(d)=>{if(await Ce(),d&&/^s-preset-|-(prev|next)$/.test(d))s(d)?.focus()};if(y(s("th"),["system","light","dark"],t,setTheme),s("nt").onclick=()=>{Se(),setTimeout(Ce,300)},n)n.rows.forEach((d)=>{let v=s("s-"+d.key.replace(/\./g,"-")),w=async(b)=>{let M=document.activeElement?.id;if((await A("/settings",{key:d.key,value:b})).ok)C("Saved");await x(M)};if(v.tagName==="INPUT")v.onchange=async()=>{await w(v.value.trim()),K()};else v.querySelectorAll("[data-v]").forEach((b)=>b.onclick=()=>w(b.dataset.v));if(v.matches(".chk"))v.onclick=()=>w(v.dataset.v);let E;if(v.matches(".step"))y(v,d.values,d.value,(b)=>{clearTimeout(E),E=setTimeout(()=>w(b),300)});if(d.folder)s(v.id+"-pick").onclick=async()=>{let b=await A("/pick-folder",{});if(b.path)await w(b.path),K()}})}var Pt=(e)=>e.status==="stalled"||!!(e.pty&&!e.pty.alive);async function Ze(e,t){t.disabled=!0,Se();let n=await A("/resume",{dir:e.state,id:e.id});if(!n.ok){t.disabled=!1;return}if(C("Resumed, "+We(n)+": "+Ne(n.agent)),n.pty)i.wantTerm=j(e);if(i.openKey===j(e))s("pfoot").innerHTML="",te(e,!0)}var on={"approvals.plan":"The plan is ready. Read it in the Plan tab, then approve or send it back.","approvals.review":"The work is ready to check. Approve to finish it, or revise with what to change.",findings:"The review has open findings. Approve as-is to accept them and finish, or fix selected findings first."},Xe=new Set,et=(e)=>j(e)+":"+e.awaiting+":"+e.moved;async function Rt(e,t,n,a){t.disabled=!0,Se();let r=await A("/decide",{dir:e.state,id:e.id,awaiting:e.awaiting,...n===void 0?{}:{why:n},...a?{answers:a}:{}});if(!r.ok){t.disabled=!1;return}if(Xe.add(et(e)),C((n===void 0?"Approved":"Sent back")+", "+We(r)+": "+Ne(r.agent)),r.pty)i.wantTerm=j(e);if(i.openKey===j(e))s("pfoot").innerHTML="",te(e,!0)}function as(e){let t=s("pfoot"),n=e.awaiting==="approvals.plan";t.innerHTML=f`<div class="inl" style="flex:1"><div class="q">${n?"What should the plan do differently?":"What should change in the work?"}</div><div class="q dim">Opens your agent on this run with your comment.${n?" It revises the plan and comes back here for approval.":" It changes the work, rechecks it and comes back here."}</div><textarea aria-label="Revise comment"></textarea><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Send back</button></div></div>`;let a=t.querySelector("textarea");a.focus(),t.querySelector("[data-a=c]").onclick=()=>{t.innerHTML="",te(e,!0)},t.querySelector("[data-a=s]").onclick=(r)=>{let c=a.value.trim();if(!c){C("Say what should change"),a.focus();return}Rt(e,r.target,c)}}function is(e,t){let n=s("pfoot");n.innerHTML=f`<div class="inl qa" style="flex:1"><div class="q">The plan asks ${t.length===1?"one question":t.length+" questions"}.</div><div class="q dim">Your answers go to your agent with the approval. It records them and builds the plan.</div>${t.map((a,r)=>f`<fieldset><legend>${a.q}</legend>${a.options.map((c,m)=>f`<label><input type="${a.multi?"checkbox":"radio"}" name="q${r}" value="${m}"${c.rec||!a.multi&&!m&&!a.options.some((u)=>u.rec)?" checked":""}> ${c.text}</label>`)}<input type="text" data-n="${r}" placeholder="Note (optional)" aria-label="Note: ${a.q}"></fieldset>`)}<div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Approve with these answers</button></div></div>`,n.querySelector("[data-a=c]").onclick=()=>{n.innerHTML="",te(e,!0)},n.querySelector("[data-a=s]").onclick=(a)=>Rt(e,a.target,void 0,t.map((r,c)=>{let m=[...n.querySelectorAll(`[name=q${c}]:checked`)].map((h)=>`"${r.options[+h.value].label}"`).join(", ")||"none of the options",u=n.querySelector(`[data-n="${c}"]`).value.trim();return`${r.q} — ${m}${u?"; "+u:""}`}))}var eo=[["grid","Grid"],["cols","Side by side"],["rows","Stacked"]],ht=6,P={mode:"grid",order:[],hidden:[],max:null,sizes:{}};try{P={...P,...JSON.parse(localStorage.getItem("kz-terms")||"{}")}}catch(e){}var tt=()=>{P.order=P.order.slice(-100),P.hidden=P.hidden.slice(-100);try{localStorage.setItem("kz-terms",JSON.stringify(P))}catch(e){}},Ke=new Map,Nt=!1,mt=null,Ht=null,an=(e)=>e.kind==="run"?String(e.id||e.text).replace(/^\d{4}-\d\d-\d\d-/,"").replace(/-/g," "):String(e.text).split(`
24
+ `)[0].slice(0,80),rs=(e)=>!e.pty.alive?"ended":e.pty.asking?"asking":"running",rn=(e)=>String(e.where||"").split(/[\\/]/).filter(Boolean).pop()||e.where||"";function cn(){let e=new Map;for(let t of i.S.cards)if(t.pty&&!t.archived)e.set(t.pty.key,t);return e}function ls(){Nt=!1;for(let e of Ke.values())e.off?.();Ke.clear(),s("terms").innerHTML=""}function be(){if(Nt=!0,!mt)(mt=matchMedia("(max-width:640px)")).onchange=()=>Nt&&be();if(document.body.classList.contains("tdrag"))return;let e=s("terms"),t=cn();for(let[b,M]of Ke)if(!t.has(b)||P.hidden.includes(b))M.off?.(),M.el.remove(),Ke.delete(b);let n=(b)=>P.order.includes(b)?P.order.indexOf(b):1e9,a=(b)=>t.get(b).pty.alive?0:1,r=[...t.keys()].filter((b)=>!P.hidden.includes(b)).sort((b,M)=>a(b)-a(M)||n(b)-n(M));for(let b of r)if(!P.order.includes(b))P.order.push(b);if(P.max&&!r.includes(P.max))P.max=null;let c=mt.matches?Ht&&r.includes(Ht)?Ht:r[0]:P.max,m=e.querySelector(".tgrid"),u=e.querySelector(".tbar");if(!m)e.innerHTML='<div class="tbar"></div><div class="tgrid"></div>',m=e.querySelector(".tgrid"),u=e.querySelector(".tbar");let h=[...t.keys()].filter((b)=>P.hidden.includes(b)&&t.get(b).pty.alive);if(s("tmodes").innerHTML=f`${eo.map(([b,M])=>f`<button data-mode="${b}" class="${P.mode===b?"on":""}" aria-pressed="${String(P.mode===b)}">${M}</button>`)}`.s,u.innerHTML=f`${h.length?f`<span class="dim">Hidden</span>${h.map((b)=>f`<button class="chip" data-show="${b}" title="Show this terminal">${q("plus")}${an(t.get(b))} - ${rn(t.get(b))}</button>`)}`:""}${mt.matches&&r.length>1?f`<span class="ttabs" role="group" aria-label="Terminals">${r.map((b)=>f`<button class="${b===c?"on":""}" data-pick="${b}" aria-pressed="${String(b===c)}"><span class="tdot ${rs(t.get(b))}"></span>${an(t.get(b))} - ${rn(t.get(b))}</button>`)}</span>`:""}`.s,u.querySelectorAll("[data-pick]").forEach((b)=>b.onclick=()=>{Ht=b.dataset.pick,be()}),u.querySelector(".ttabs .on")?.scrollIntoView({block:"nearest",inline:"nearest"}),s("tmodes").querySelectorAll("[data-mode]").forEach((b)=>b.onclick=()=>{P.mode=b.dataset.mode,P.max=null,tt(),be()}),u.querySelectorAll("[data-show]").forEach((b)=>b.onclick=()=>{P.hidden=P.hidden.filter((M)=>M!==b.dataset.show),tt(),be()}),m.querySelector(".tempty")?.remove(),!r.length){m.style.cssText="",m.querySelectorAll(".tsplit").forEach((b)=>b.remove()),m.insertAdjacentHTML("beforeend",f`<div class="empty tempty"><div class="mascot"></div><b>${h.length?"Every terminal is hidden":"No terminals running"}</b><p>Runs started with the agent in its card show here, from Ideas to Done.</p></div>`.s);return}for(let b of r){let M=t.get(b),R=Ke.get(b);if(!R){let X=document.createElement("section");X.className="tile",X.dataset.k=b,X.innerHTML='<header draggable="true"></header><div class="term" aria-label="Agent terminal"></div>',R={el:X,off:null},Ke.set(b,R),to(b,R),no(X)}let Q=rs(M),se=Q==="ended",Pe=R.el.querySelector("header");Pe.title=(M.kind==="run"?M.id:M.text)+" · "+M.where,Pe.innerHTML=f`<span class="tdot ${Q}" role="img" title="${Q}" aria-label="${Q}"></span><b>${an(M)} - ${rn(M)}</b>${se?f`<span class="tend">ended</span>`:""}<span class="sp"></span>${se&&M.kind==="run"?f`<button class="btn sm" data-a="resume" title="Continue this run's agent">Resume</button>`:""}<button class="ib sm" data-a="card" title="Open card" aria-label="Open card">${q("open")}</button><button class="ib sm" data-a="max" title="${P.max===b?"Restore":"Maximise"}" aria-label="${P.max===b?"Restore":"Maximise"}">${q(P.max===b?"min":"max")}</button>${se?"":f`<button class="ib sm" data-a="hide" title="Hide" aria-label="Hide">${q("dash")}</button>`}<button class="ib sm" data-a="${se?"hide":"exit"}" title="${se?"Close":"Exit session"}" aria-label="${se?"Close":"Exit session"}">${q("close")}</button>`.s,R.el.classList.toggle("ended",se),R.el.classList.toggle("off",!!c&&c!==b),R.el.style.gridColumn="",m.append(R.el)}let p=c?1:r.length,g=P.mode==="cols"?p:P.mode==="rows"?1:Math.ceil(Math.sqrt(p)),y=Math.ceil(p/g),x=g;if(!c&&g*y>p)Ke.get(r[r.length-1]).el.style.gridColumn=`span ${g*y-p+1}`,x=p-(y-1)*g-1;let d=`${P.mode}:${g}x${y}`,v=P.sizes[d],w=v?.c?.length===g?v.c:Array(g).fill(1),E=v?.r?.length===y?v.r:Array(y).fill(1);so(m,w,E,d,x,!c)}function to(e,t){dn().then((n)=>{if(Ke.get(e)!==t)return;t.off=n(t.el.querySelector(".term"),e,()=>K(!0))}).catch(()=>t.el.querySelector(".term").textContent="The terminal could not load.")}function no(e){let t=e.dataset.k;e.querySelector("header").onclick=(n)=>{let a=n.target.closest("[data-a]")?.dataset.a;if(a==="max")P.max=P.max===t?null:t;else if(a==="hide"){if(P.hidden.push(t),P.max===t)P.max=null}else if(a==="exit"){if(confirm("End this agent session? A run can be resumed from its tile or card."))A("/pty/exit",{key:t}).then(()=>K(!0));return}else if(a==="card"){let r=cn().get(t);if(r)we(r);return}else if(a==="resume"){let r=cn().get(t);if(r)Ze(r,n.target.closest("button")).then(()=>i.wantTerm=null);return}else return;tt(),be()},e.querySelector("header").ondblclick=(n)=>{if(n.target.closest("button")||mt?.matches)return;P.max=P.max===t?null:t,tt(),be()},e.querySelector("header").ondragstart=(n)=>{n.dataTransfer.setData("text/kz-term",t),n.dataTransfer.effectAllowed="move"},e.ondragover=(n)=>{if(!n.dataTransfer.types.includes("text/kz-term"))return;n.preventDefault(),e.classList.add("drop")},e.ondragleave=()=>e.classList.remove("drop"),e.ondrop=(n)=>{e.classList.remove("drop");let a=n.dataTransfer.getData("text/kz-term");if(!a||a===t)return;n.preventDefault();let r=P.order.indexOf(a),c=P.order.indexOf(t);if(r<0||c<0)return;[P.order[r],P.order[c]]=[P.order[c],P.order[r]],tt(),be()}}var ln=(e,t)=>{let n=e.reduce((r,c)=>r+c,0),a=e.slice(0,t+1).reduce((r,c)=>r+c,0);return`calc((100% - ${(e.length-1)*ht}px) * ${a/n} + ${t*ht+ht/2}px)`};function so(e,t,n,a,r,c){e.querySelectorAll(".tsplit").forEach((u)=>u.remove());let m=()=>{e.style.gridTemplateColumns=t.map((u)=>u+"fr").join(" "),e.style.gridTemplateRows=n.map((u)=>u+"fr").join(" "),e.querySelectorAll(".tsplit").forEach((u)=>{let h=+u.dataset.i;if(u.dataset.axis==="r")return void(u.style.top=ln(n,h));u.style.left=ln(t,h),u.style.bottom=h>=r?`calc(100% - ${ln(n,n.length-2)} + ${ht/2}px)`:"0px"})};if(c)for(let[u,h]of[["c",t],["r",n]])for(let p=0;p<h.length-1;p++){let g=document.createElement("div");g.className="tsplit "+u,g.dataset.axis=u,g.dataset.i=String(p),g.setAttribute("role","separator"),g.setAttribute("aria-orientation",u==="c"?"vertical":"horizontal"),g.title="Drag to resize, double-click to even out",oo(g,e,u,h,p,m,()=>{P.sizes[a]={c:[...t],r:[...n]},tt()}),e.append(g)}m()}function oo(e,t,n,a,r,c,m){e.ondblclick=()=>{a.fill(1),m(),c()},e.onpointerdown=(u)=>{u.preventDefault(),e.setPointerCapture(u.pointerId),document.body.classList.add("tdrag");let h=a.reduce((v,w)=>v+w,0),p=(n==="c"?t.clientWidth:t.clientHeight)-(a.length-1)*ht,g=h*0.1,y=n==="c"?u.clientX:u.clientY,x=a[r],d=a[r+1];e.onpointermove=(v)=>{let w=Math.max(g-x,Math.min(d-g,((n==="c"?v.clientX:v.clientY)-y)/p*h));a[r]=x+w,a[r+1]=d-w,c()},e.onpointerup=e.onpointercancel=()=>{e.onpointermove=e.onpointerup=e.onpointercancel=null,document.body.classList.remove("tdrag"),m()}}}var cs=()=>Nt;var W="board",un=(e)=>{document.querySelectorAll(".rail a.it").forEach((t)=>t.dataset.view===e?t.setAttribute("aria-current","page"):t.removeAttribute("aria-current")),document.querySelector(".hdr h1").textContent=e==="sources"?"Icebox":e==="terminal"?"Terminal":"Board"};function fe(e,t){if(i.pageGen++,e==="icebox")e="sources";if(!["settings","sources","terminal"].includes(e))e="board";let n=W;if(W=e,un(e),document.body.classList.toggle("paged",e!=="board"),document.body.classList.toggle("ice",e==="sources"),document.body.classList.toggle("termv",e==="terminal"),t&&e!==n)history.pushState(null,"",e==="board"?location.pathname:"?view="+(e==="sources"?"icebox":e));let a=s("panel");if(e==="terminal")be();else ls();if(e==="board"||e==="terminal"){if(n==="board"||n==="terminal"){if(e==="terminal"&&n==="board")W="board",ee(),W=e;return}a.style.transition="none",a.classList.remove("open","docked"),a.offsetWidth,a.style.transition="",W="board",ee(),W=e;return}if(a.classList.add("docked"),e==="settings")Ce();else z()}function ds(){document.querySelectorAll(".rail a[data-view]").forEach((e)=>{if(e.dataset.view==="notes")return;e.onclick=(t)=>{if(t.ctrlKey||t.metaKey||t.shiftKey||t.button)return;if(t.preventDefault(),e.dataset.view==="settings")i.setQ="";fe(e.dataset.view,!0)}}),document.querySelector(".rail .home").onclick=(e)=>{if(e.ctrlKey||e.metaKey||e.shiftKey||e.button)return;e.preventDefault(),fe("board",!0)},addEventListener("popstate",()=>fe(new URLSearchParams(location.search).get("view"))),addEventListener("kz-theme",()=>{if(W==="settings")Ce()})}var ps="Icebox",ie=null,yt={},pn=(e)=>{if(!yt[e])try{yt[e]=localStorage.getItem("kz-reply-style:"+e)}catch(t){}return yt[e]||"casual"},ao={casual:"Casual",fun:"Fun",helpful:"Helpful",promo:"Promo"},re=null,Ae=null,me=null,he="",ne="",nt="link",gt=null,st=!1,Kt=!1,It=null,J=new Set,bt={x:"X",tiktok:"TikTok",instagram:"Instagram",threads:"Threads"},Y=()=>s("panel").classList.contains("open")&&s("ptitle").textContent===ps,Be=(e)=>{let t=Math.round((Date.now()-Date.parse(e))/60000);return t<1?"just now":t<60?t+" min ago":t<1440?Math.round(t/60)+" h ago":Math.round(t/1440)+" d ago"};function us(e,t){let n=s(e);if(!n)return;let a=n.querySelector("button.sel"),r=n.querySelector(".menu-pop"),c=r.querySelector("input"),m=r.querySelector(".pick-list"),u=(p)=>{if(r.hidden=!p,a.setAttribute("aria-expanded",p),p)c.value="",h(),c.focus()},h=()=>{let p=c.value.trim().toLowerCase(),g=null,y=!1;for(let x of m.children)if(x.classList.contains("grp")){if(g)g.hidden=!y;g=x,y=!1}else{let d=!p||x.dataset.q.includes(p)||g&&g.dataset.g.includes(p);x.hidden=!d,y||=d}if(g)g.hidden=!y};a.onclick=()=>u(r.hidden),c.oninput=h,c.onkeydown=(p)=>{if(p.key==="Escape")p.stopPropagation(),u(!1),a.focus();else if(p.key==="Enter")p.preventDefault(),m.querySelector("button:not([hidden])")?.click()},m.onclick=(p)=>{let g=p.target.closest("button[data-v]");if(!g)return;u(!1),t(g.dataset.v)}}async function z(){if(W!=="sources")return;let e=++i.pageGen;de(!0);let t=Y()?[s("src-url")?.value||"",s("src-label")?.value||"",document.activeElement?.id,s("src-dir")?.value,s("src-cat")?.value||"",s("src-plat")?.value,s("src-q")?.value||""]:["",""],n=new Map(Y()?[...s("pbody").querySelectorAll("[data-mv]")].map((o)=>[o.dataset.k,o.value]):[]),a=Y()&&document.activeElement?.closest?.(".ice-row"),r=a?[a.dataset.k,document.activeElement.id.replace(/-\d+$/,"")]:null,c=Y()&&s("ice-title")?[s("ice-title").dataset.k,s("ice-title").value,s("ice-details").value]:null,m=new Map(Y()?[...s("pbody").querySelectorAll("[data-ct]")].map((o)=>[o.dataset.k,o.value]):[]);if(!Y())s("pbody").innerHTML=Et;Ee(),i.openKey=null,i.runDoc=null,s("pterm").hidden=!0,s("panel").classList.add("open"),s("scrim").classList.add("on"),s("ptitle").textContent=ps,s("ptabs").innerHTML="",s("pfoot").innerHTML="",s("psub").textContent="",s("psub").title="";let u=i.all?null:i.S.dir,h=u?"?dir="+encodeURIComponent(u):"",p=await fetch("/sources"+h).then((o)=>o.json()).catch(()=>null);if(e!==i.pageGen||!Y())return;let g=(o)=>{try{return new URL(o).hostname}catch{return o}},y=(o,l,k)=>{if(bt[k])return f`<span class="fav">${q("b-"+k)}</span>`;let S;try{let G=new URL(o);if(/^https?:$/.test(G.protocol))S=G.origin}catch{}let D=!S?"":o.includes("docs.google.com/spreadsheets")?"https://ssl.gstatic.com/docs/spreadsheets/favicon3.ico":o.includes("docs.google.com/document")?"https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico":S+"/favicon.ico";return f`<span class="fav">${D?f`<img src="${D}" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.remove()">`:""}${q(l)}</span>`},x=(o)=>bt[o?.social]||(o?g(o.url).replace(/^www\./,""):"")||"Other",d=(o)=>{if(o.social)return"social";let l=o.url;if(l.includes("docs.google.com/spreadsheets")||l.endsWith(".csv")||l.endsWith(".tsv"))return"sheet";if(l.includes("docs.google.com/document"))return"doc";return"link"},v=p&&p.sources||[],w=[...p&&p.icebox||[],...p&&p.dismissed||[]],E=(p&&p.icebox||[]).length,b=p&&p.gatherer||{},M=b.use==="chatgpt"&&b.connected,R=(o,l)=>f`${o&&!i.S.projects.some((k)=>k.dir===o)?f`<option value="${o}" title="${l||o}" selected>${Te(l||o)}</option>`:""}${i.S.projects.map((k)=>f`<option value="${k.dir}" title="${k.label}"${k.dir===o?" selected":""}>${Te(k.label)}</option>`)}`,Q=v.map((o,l)=>{let k=d(o),S=o.label||g(o.url),D=o.social&&(!o.last||!o.last.ok&&/gathered by your agent/i.test(o.last.note))?f`<span class="src-badge dim">${q("at")}${M?"Gathered with ChatGPT":"Gathered by your agent"}</span>`:!o.last?f`<span class="src-badge dim">Not gathered yet</span>`:o.last.ok?f`<span class="src-badge ok"><svg viewBox="0 0 16 16"><path d="M3.5 8.5l3 3 6-6" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>${o.last.note}</span><span class="hint" style="margin:0">${Be(o.last.at)}</span>`:f`<span class="src-badge warn"><svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="6" stroke="currentColor" fill="none" stroke-width="1.8"/><path d="M8 5v3.5M8 11v.1" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round"/></svg>Failed: ${o.last.note}</span><span class="hint" style="margin:0">${Be(o.last.at)}</span>`,G=k==="sheet"?"sheet":k==="doc"?"doc":k==="social"?"at":"link",U=o.url.replace(/^https?:\/\/(www\.)?/,"");return f`<div class="src-card" id="src-card-${l}"><div class="src-hd"><span class="src-ico" title="${k==="social"?bt[o.social]||"social":k}">${y(o.url,G,o.social)}</span><div class="src-info"><span class="src-title" title="${S}">${S}</span><a class="src-url" href="${o.url}" target="_blank" rel="noopener noreferrer" title="${o.url}"><span>${U}</span>${q("ext")}</a></div><div class="src-acts">${o.social?f`<button class="ib sm" id="src-ag-${l}" data-ag="${l}" aria-label="Gather ${S} with your agent" title="${M?"Gather with your ChatGPT plan":"Gather: opens your agent on /kaizen gather"}">${q("refresh")}</button>`:f`<button class="ib sm" id="src-chk-${l}" data-chk="${l}" aria-label="Check ${S} now" title="Check now">${q("refresh")}</button>`}<button class="ib sm danger" id="src-rm-${l}" data-rm="${l}" aria-label="Remove ${S}" title="Remove source">${q("trash")}</button></div></div><div class="src-status">${D}</div><div class="src-fields"><label class="src-f" title="Category"><span>Category</span><input class="sel" id="src-ct-${l}" data-ct="${l}" data-k="${o.dir+" "+o.url}" value="${o.category||""}" placeholder="${x(o)}" maxlength="40" aria-label="Category of ${S}"><button class="btn sm pri" id="src-cs-${l}" aria-label="Save the category of ${S}" disabled>Save</button></label><label class="src-f" title="Project"><span>Project</span>${ot(o.dir,o.project||"")}<select class="sel" id="src-mv-${l}" data-mv="${l}" data-k="${o.dir+" "+o.url}" aria-label="Project of ${S}">${R(o.dir,o.project)}</select><button class="btn sm pri" id="src-mb-${l}" aria-label="Move ${S} to the chosen project" disabled>Move</button></label>${k==="sheet"||/github\.com/.test(o.url)?f`<label class="src-wb"><input type="checkbox" id="src-wb-${l}" data-wb="${l}"${o.writeback?" checked":""}> Write back</label>`:""}</div></div>`}),se=(p&&p.failed||[]).map((o)=>f`<p style="color:var(--dim)">The sources of ${o} could not be read.</p>`),Pe=f`<div class="src-empty"><div class="src-empty-ico">${q("link")}</div><h4>No sources yet</h4></div>`,X=nt==="social",xe=!v.length||st||[t[0],t[1],t[4],t[6]].some(Boolean),le=f`<div class="src-add" id="src-addbox"${xe?"":" hidden"}><div class="hd"><h4>Add a source</h4><div class="src-kind" role="group" aria-label="Kind of source"><button id="src-k-link" data-kind="link" aria-pressed="${String(!X)}">Link</button><button id="src-k-social" data-kind="social" aria-pressed="${String(X)}">Social</button></div></div><label for="src-url"${X?" hidden":""}><span>Link</span><input class="sel" id="src-url" placeholder="Link" spellcheck="false" autocomplete="off"></label><div class="two plat"${X?"":" hidden"}><label for="src-plat"><span>Platform</span><select class="sel" id="src-plat">${Object.entries(bt).map(([o,l])=>f`<option value="${o}">${l}</option>`)}</select></label><label for="src-q"><span>Follow</span><input class="sel" id="src-q" placeholder="keyword, #hashtag or @account" maxlength="100" spellcheck="false" autocomplete="off"></label></div><div class="two"><label for="src-label"><span>Label <i>optional</i></span><input class="sel" id="src-label" placeholder="${X?"e.g. Mentions on X":"e.g. Feedback Q4"}"></label><label for="src-cat"><span>Category <i>optional</i></span><input class="sel" id="src-cat" placeholder="${X?"e.g. Social":"e.g. Bugs"}" maxlength="40"></label></div><label for="src-dir"><span>Project</span><select class="sel" id="src-dir">${u?"":f`<option value="">Choose project…</option>`}${R(u)}</select></label><div class="acts"><span class="end"></span><button class="btn pri" id="src-add">Add source</button></div></div>`,oe=(o)=>{let l=v.find((k)=>k.dir===o.dir&&k.url===o.source);return l?l.label||g(l.url):g(o.source)},L=(o)=>o.dir+" "+o.id,N=(o)=>o.dir+" "+o.source,T=(o)=>v.find((l)=>l.dir===o.dir&&l.url===o.source),O=(o)=>o.status==="dismissed"?"Dismissed":o.status==="done"?"Done":o.category||T(o)?.category||x(T(o)||{url:o.source}),I=(o,l)=>{let k=new Map;return o.forEach((S)=>{let D=l(S);k.set(D,{it:S,n:(k.get(D)?.n||0)+1})}),k},V=u?new Map:I(w,(o)=>o.dir);if(!u){for(let o of i.shown)if(!V.has(o)){let l=Ie().find((k)=>k.dir===o);if(l)V.set(o,{it:{dir:o,project:l.label},n:0})}}let ve=(o)=>u||!i.shown.size||i.shown.has(o.dir),Dn=w.filter(ve),lt=I(Dn,O);if(ne&&!lt.has(ne))ne="";let Rs=Dn.filter((o)=>!ne||O(o)===ne),ct=I(Rs,N);if(he&&!ct.has(he))he="";let Hs=new Set(w.map(L));if(re&&!Hs.has(re))re=null;if(Ae!==re)Ae=null;let dt=new Map;w.forEach((o,l)=>{if(!ve(o)||(ne?O(o)!==ne:o.status==="dismissed"||o.status==="done")||he&&N(o)!==he)return;let k=O(o);dt.has(k)||dt.set(k,[]),dt.get(k).push(l)});let B=[...dt.values()].flat();{let o=new Set(B.map((l)=>L(w[l])));[...J].forEach((l)=>o.has(l)||J.delete(l))}if(!B.some((o)=>L(w[o])===me))me=B.length?L(w[B[0]]):null;let ut=B.filter((o)=>J.has(L(w[o]))).length,Ns=V.size>1,Pn=(o,l,k,S)=>f`<div class="combo ice-combo" id="${o}"><button class="sel" aria-label="${l}" aria-haspopup="listbox" aria-expanded="false">${k}</button><div class="menu-pop" hidden><input class="sel" aria-label="Search ${l.toLowerCase()}" placeholder="Search…" spellcheck="false" autocomplete="off"><div class="pick-list" role="listbox">${S}</div></div></div>`,xt=(o,l,k,S,D="")=>f`<button type="button" role="option" data-v="${o}" data-q="${S.toLowerCase()}" class="${l?"on":""}${D}" aria-selected="${String(l)}">${k}</button>`,St=new Map;for(let[o,{it:l}]of ct){let k=x(T(l)||{url:l.source});St.has(k)||St.set(k,[]),St.get(k).push([o,l])}let Jt=(o)=>y(o.source,"link",T(o)?.social),Yt=he&&[...ct].find(([o])=>o===he)?.[1].it,Rn=f`${lt.size>1||lt.has("Dismissed")||lt.has("Done")?Pn("ice-fc","Category",f`<span>${ne||"All categories"}</span>`,f`${xt("",!ne,f`<span>All categories</span>`,"all categories")}${[...lt].map(([o,{n:l}])=>xt(o,ne===o,f`<span>${o}</span><i>${l}</i>`,o))}`):""}${ct.size>1?Pn("ice-fs","Source",Yt?f`${Jt(Yt)}<span>${oe(Yt)}</span>`:f`<span>All sources</span>`,f`${xt("",!he,f`<span>All sources</span>`,"all sources")}${[...St].map(([o,l])=>f`<div class="grp" data-g="${o.toLowerCase()}">${Jt(l[0][1])}${o}</div>${l.map(([k,S])=>xt(k,he===k,f`${Jt(S)}<span>${oe(S).toLowerCase().startsWith(o.toLowerCase()+":")?oe(S).slice(o.length+1).trim():oe(S)}</span><i>${ct.get(k).n}</i>`,o+" "+oe(S)," sub"))}`)}`):""}`,Ks=f`<div class="ice-bar">${B.length?f`<input type="checkbox" id="ice-all" aria-label="Select every request shown"${ut&&ut===B.length?" checked":""}>`:""}<span class="count">${ne==="Dismissed"||ne==="Done"?`${B.length} ${ne.toLowerCase()}`:`${B.length<E?`${B.length} of ${E}`:E} waiting`}</span><span class="end"></span>${String(Rn)?f`<details class="ice-filt"><summary class="ib${ne||he?" on":""}" aria-label="Filter requests" title="Filter requests">${q("filter")}</summary><div class="ice-fpop">${Rn}</div></details>`:""}<button class="btn ghost sm" id="ice-new" title="Add a request by hand">${q("plus")} Add</button><button class="btn sm" id="src-go"${v.length?"":" disabled"}>${q("refresh")} Gather all</button><button class="btn ghost sm" id="src-tog" aria-expanded="${String(gt??!v.length)}" aria-controls="src-sec">${q("link")} Sources · ${v.length}</button></div>`,Hn=J.size?f`<div class="ice-bulk" role="toolbar" aria-label="Selected requests"><span>${J.size} selected</span><span class="end"></span><button class="btn sm" id="ice-bp" title="Promote (p)">${q("plus")} Promote</button><button class="btn ghost sm" id="ice-bd" title="Dismiss (d)">${q("x")} Dismiss</button><button class="btn ghost sm" id="ice-bc" title="Clear selection (Esc)">Clear</button></div>`:"",Is=(o)=>f`<div class="ice-form"><label for="ice-title" class="hint" style="margin:0">Idea title</label><input class="sel" id="ice-title" data-k="${L(o)}" maxlength="200" value="${o.text}" aria-label="Idea title"><label for="ice-details" class="hint" style="margin:0">Details</label><textarea class="sel" id="ice-details" rows="3" aria-label="Details"></textarea><div class="ice-form-acts"><span class="end"></span><button class="btn ghost sm" id="ice-cancel">Cancel</button><button class="btn pri sm" id="ice-ok">Promote</button></div></div>`,Os=(o)=>{if(!o.post)return"";if(o.replied)return f`<span class="hint" style="margin:0">You replied to this post ${Be(o.replied)}.</span>`;let l=ie&&ie.k===L(o)?ie:null,k=(S)=>f`<select class="sel" id="ice-rst" aria-label="Reply style"${S?" disabled":""}>${Object.entries(ao).map(([D,G])=>f`<option value="${D}"${D===pn(o.dir)?" selected":""}>${G}</option>`)}</select>`;if(!l)return f`<div class="ice-form-acts"><span class="hint">You review it before it posts.</span>${b.connected?k(!1):""}<button class="btn sm" id="ice-rp">Reply</button></div>`;if(l.wait)return f`<div class="ice-form-acts"><span class="hint">Drafting…</span></div>`;return f`<div class="ice-form"><label for="ice-reply" class="hint" style="margin:0">Your reply${l.note?` · ${l.note}`:""}</label><textarea class="sel" id="ice-reply" rows="3" maxlength="2000" aria-label="Your reply"${l.busy||l.again?" disabled":""}>${l.text}</textarea><span class="hint" id="ice-rn" style="margin:0">${l.again?"Writing another…":`${l.text.length} chars`}</span><div class="ice-form-acts"><span class="end"></span>${b.connected?f`${k(l.busy||l.again)}<button class="btn ghost sm" id="ice-rg"${l.busy||l.again?" disabled":""}>Regenerate</button>`:""}<button class="btn ghost sm" id="ice-rc"${l.busy?" disabled":""}>Cancel</button><button class="btn pri sm" id="ice-rs"${l.busy||l.again||!l.text.trim()?" disabled":""}>${l.busy?"Sending…":"Send reply"}</button></div></div>`},zs=(o)=>{let l=w[o],k=L(l),S=re===k,D=f`<div class="ice-row${me===k?" cur":""}${J.has(k)?" sel":""}" data-i="${o}" data-k="${k}"><input type="checkbox" id="ice-ck-${o}" data-ck="${o}"${J.has(k)?" checked":""} aria-label="Select: ${l.text}"><span class="ice-src" title="${oe(l)}">${y(l.source,"link",v.find((U)=>U.dir===l.dir&&U.url===l.source)?.social)}</span><button class="ice-t" id="ice-o-${o}" data-o="${o}" aria-expanded="${String(S)}" title="${l.text}">${l.text}</button><span class="ice-meta">${Ns?f`<span class="nm" title="${l.project||""}">${ot(l.dir,l.project||"")}${Te(l.project||"")}</span>`:""}${l.status==="done"&&l.written?f`<span title="Written back ${Be(l.written)}">written back</span>`:""}${l.status==="done"&&l.writeError?f`<span title="${l.writeError}">write-back failed</span>`:""}${l.status==="promoted"?f`<span title="Promoted to Ideas: ${l.idea||""}">promoted</span>`:""}${l.replied?f`<span title="Replied ${Be(l.replied)}">replied</span>`:""}<span class="ice-age">${Be(l.at)}</span><span class="ice-ra">${l.status==="promoted"||l.status==="done"?"":f`<button class="btn ghost sm" id="ice-pr-${o}" data-pr="${o}" title="Promote to Ideas (p)" aria-label="Promote to Ideas: ${l.text}">${q("plus")}</button>`}${l.status==="promoted"||l.status==="done"?"":l.status==="dismissed"?f`<button class="btn ghost sm" id="ice-rs-${o}" data-rs="${o}" title="Restore to waiting" aria-label="Restore: ${l.text}">Restore</button>`:f`<button class="btn ghost sm" id="ice-ds-${o}" data-ds="${o}" title="Dismiss (d)" aria-label="Dismiss: ${l.text}">${q("x")}</button>`}</span></span></div>`,G=S?f`<div class="ice-open">${l.notes?f`<div class="ice-notes">${l.notes}</div>`:""}${(l.images||[]).filter((U)=>/^https:\/\//.test(U)).length?f`<details class="ice-imgs"><summary>Show images (${l.images.filter((U)=>/^https:\/\//.test(U)).length}) — loads them from the source's site</summary>${l.images.filter((U)=>/^https:\/\//.test(U)).map((U,F)=>{let ce=`/ice-image?dir=${encodeURIComponent(l.dir)}&id=${encodeURIComponent(l.id)}&i=${F}`;return f`<a href="${ce}" target="_blank" rel="noopener noreferrer"><img data-src="${ce}" alt="" referrerpolicy="no-referrer"></a>`})}</details>`:""}<span class="hint" style="margin:0">From ${oe(l)} · ${Be(l.at)}${l.post?f` · <a href="${l.post}" target="_blank" rel="noopener">open post</a>`:""}</span>${Os(l)}${Ae===k?Is(l):""}</div>`:"";return f`${D}${G}`},Gs=E&&!B.length?f`<div class="ice-zero"><h4>No matches</h4><p style="margin-top:10px"><button class="btn sm" id="ice-unf">Clear filters</button></p></div>`:B.length?f`<div class="ice-list">${[...dt].map(([o,l])=>f`<div class="ice-cat">${o}<i>${l.length}</i></div>${l.map(zs)}`)}</div>`:f`<div class="ice-zero"><h4>Nothing waiting</h4></div>`,Lt=gt??!v.length,Zt=Lt&&!s("src-sec")?.classList.contains("open"),Us=f`<div class="src-scrim${Lt&&!Zt?" on":""}" id="src-scrim"></div><aside class="src-sec${Lt&&!Zt?" open":""}" id="src-sec" aria-label="Sources"${Lt?"":" inert"}><header><div class="t"><b>Sources</b><div class="src-chips"><a id="src-to-settings" class="schip" role="button" tabindex="0" title="${p&&p.every&&p.every!=="off"?`Checked every ${p.every}`:"Auto-check off"}: change in Settings">${q("clock")}${p&&p.every&&p.every!=="off"?p.every:"Off"}</a>${b.connected?f`<span class="schip on" title="ChatGPT${b.email?": "+b.email:""}">${q("ai")}ChatGPT<a id="cg-out" role="button" tabindex="0" aria-label="Disconnect ChatGPT" title="Disconnect">${q("x")}</a></span>`:f`<a id="cg-in" class="schip" role="button" tabindex="0" title="Connect ChatGPT">${q("ai")}ChatGPT</a>`}${(p.google||{}).connected?f`<span class="schip on" title="Google connected">${q("google")}Google<a id="gg-out" role="button" tabindex="0" aria-label="Disconnect Google" title="Disconnect">${q("x")}</a></span>`:f`<a id="gg-in" class="schip" role="button" tabindex="0" title="Connect Google">${q("google")}Google</a>`}</div></div><div class="src-hda">${v.length?f`<button class="btn sm" id="src-new" aria-expanded="${String(xe)}" aria-controls="src-addbox">${q("plus")} Add</button>`:""}<button class="ib" id="src-x" aria-label="Close sources" title="Close">${q("close")}</button></div></header><div class="body">${le}${v.length?f`<div class="src-list">${Q}</div>`:Pe}</div></aside>`,Fs=b.use==="ask"&&!b.connected?f`<div class="cg-onb"><div class="acts"><b>Gather social posts with ChatGPT?</b><span class="end"></span><button class="btn ghost" id="cg-skip">Skip</button><button class="btn pri" id="cg-go">Connect</button></div></div>`:"",_s=s("src-sec")?.querySelector(".body")?.scrollTop??0;if(s("pbody").innerHTML=p&&p.error?f`<p style="color:var(--dim)">${p.error}</p>`:!p?f`<p style="color:var(--dim)">The icebox could not be read.</p>`:f`${Fs}${Ks}${Hn}${se}${Gs}${Us}`,!p||p.error)return;let Nn=s("src-sec")?.querySelector(".body");if(Nn)Nn.scrollTop=_s;if(c&&c[0]===re&&s("ice-title"))s("ice-title").value=c[1],s("ice-details").value=c[2];if(s("src-add")){if(t[5])s("src-plat").value=t[5];if(s("src-q").value=t[6]||"",s("src-url").value=t[0],s("src-label").value=t[1],s("src-cat").value=t[4]||"",t[3]!==void 0&&s("src-dir"))s("src-dir").value=t[3]}if(s("src-to-settings"))s("src-to-settings").onclick=()=>{i.setQ="sources",fe("settings",!0)};let Ws=async()=>{let o=window.open("","_blank"),l=await A("/chatgpt",{op:"connect"});if(l.ok&&o)o.location=l.url;else if(o?.close(),l.ok)location.href=l.url};for(let o of["cg-go","cg-in"])if(s(o))s(o).onclick=Ws;if(s("gg-in"))s("gg-in").onclick=async()=>{if(!(p.google||{}).hasClient){C("Add your Google client in Settings first"),i.setQ="google",fe("settings",!0);return}let o=window.open("","_blank"),l=await A("/google",{op:"connect"});if(l.ok&&o)o.location=l.url;else if(o?.close(),l.ok)location.href=l.url;else C(l.error||"Could not start Google sign-in")};if(s("gg-out"))s("gg-out").onclick=async()=>{if(!confirm("Disconnect Google? Done requests stop being written to sheets."))return;if((await A("/google",{op:"disconnect"})).ok)C("Google disconnected"),z()};if(s("cg-skip"))s("cg-skip").onclick=async()=>{if((await A("/chatgpt",{op:"skip"})).ok)z()};if(s("cg-out"))s("cg-out").onclick=async()=>{if(!confirm("Disconnect ChatGPT? Gather goes back to your agent."))return;if((await A("/chatgpt",{op:"disconnect"})).ok)C("ChatGPT disconnected"),z()};if(Zt)s("src-sec").offsetWidth,s("src-sec").classList.add("open"),s("src-scrim").classList.add("on");let qt=()=>{gt=!1,s("src-sec").classList.remove("open"),s("src-scrim").classList.remove("on"),s("src-sec").inert=!0,s("src-tog").setAttribute("aria-expanded","false"),s("src-tog").focus()};if(s("src-x").onclick=s("src-scrim").onclick=qt,s("src-new"))s("src-new").onclick=()=>{let o=s("src-addbox");if(st=o.hidden,o.hidden=!st,s("src-new").setAttribute("aria-expanded",st),st)s(nt==="social"?"src-q":"src-url").focus()};if(s("src-sec").onkeydown=(o)=>{if(o.key==="Escape")o.stopPropagation(),qt()},s("src-tog").onclick=()=>{if(s("src-sec").classList.contains("open"))return qt();gt=!0,z().then(()=>gt&&s("src-url")?.focus())},us("ice-fs",(o)=>{he=o,z()}),us("ice-fc",(o)=>{ne=o,z()}),s("ice-unf"))s("ice-unf").onclick=()=>{if(ne=he="",!u)i.shown.clear();zt(),z()};s("pbody").querySelectorAll("[data-kind]").forEach((o)=>o.onclick=()=>{if(nt===o.dataset.kind)return;nt=o.dataset.kind,z().then(()=>s(nt==="social"?"src-q":"src-url")?.focus())});let ze=async(o,l)=>{let k=await A("/sources",o);if(k.ok)l(k),K();if(Y())z()};s("pbody").querySelectorAll("[data-mv]").forEach((o)=>{let l=v[+o.dataset.mv],k=s("src-mb-"+o.dataset.mv),S=n.get(o.dataset.k);if(S&&[...o.options].some((D)=>D.value===S))o.value=S;(o.onchange=()=>{k.disabled=o.value===l.dir})(),k.onclick=()=>ze({op:"move",dir:l.dir,to:o.value,url:l.url},()=>C("Source moved"))}),s("pbody").querySelectorAll("[data-wb]").forEach((o)=>{let l=v[+o.dataset.wb];o.onchange=()=>{if(o.checked&&l.social===void 0&&/docs\.google\.com/.test(l.url)&&!(p.google||{}).connected){alert("Connect Google first (Sources header), so kaizen can write to the sheet."),o.checked=!1;return}if(o.checked&&!confirm("When a promoted request's run is done, kaizen will write that back to this source. A closed issue or a changed cell is visible to others. Turn on?")){o.checked=!1;return}ze({op:"writeback",dir:l.dir,url:l.url,on:o.checked},()=>C(o.checked?"Write back on":"Write back off"))}}),s("pbody").querySelectorAll("[data-ct]").forEach((o)=>{let l=v[+o.dataset.ct],k=s("src-cs-"+o.dataset.ct),S=m.get(o.dataset.k);if(S!==void 0)o.value=S;(o.oninput=()=>{k.disabled=o.value.trim()===(l.category||"")})(),o.onkeydown=(D)=>{if(D.key==="Enter"&&!k.disabled)k.click()},k.onclick=()=>{k.disabled=!0,ze({op:"category",dir:l.dir,url:l.url,category:o.value.trim()},()=>C(o.value.trim()?`New requests from ${l.label||g(l.url)} go under ${o.value.trim()}`:"Category cleared"))}});let Kn=()=>{if(!document.activeElement||document.activeElement===document.body)s("pbody").querySelector(".ice-row.cur .ice-t")?.focus()},In=(o)=>{let l=B.findIndex((D)=>L(w[D])===me);if(l<0||!o.has(me))return;let k=(D)=>!o.has(L(w[D])),S=B.slice(l+1).find(k)??B.slice(0,l).reverse().find(k);me=S===void 0?null:L(w[S])},Xt=(o,l,k,S={})=>(async()=>{if(In(new Set([L(l)])),(await A("/icebox",{op:o,dir:l.dir,ids:[l.id],...S})).ok){if(o==="promote"&&re===L(l))re=Ae=null;C(k),K()}if(Y())z().then(Kn)})(),On=async(o)=>{let l=w.filter((S)=>J.has(L(S)));if(!l.length)return;In(new Set(l.map(L))),Kt=!0,s("ice-bp").disabled=s("ice-bd").disabled=!0;let k=0;try{if(o==="dismiss")for(let S of new Set(l.map((D)=>D.dir))){let D=l.filter((G)=>G.dir===S).map((G)=>G.id);if((await A("/icebox",{op:o,dir:S,ids:D})).ok)k+=D.length}else for(let S of l)if((await A("/icebox",{op:o,dir:S.dir,ids:[S.id]})).ok)k++}finally{Kt=!1}if(J.clear(),k)C(`${k} ${o==="promote"?"promoted to Ideas":"dismissed"}${k<l.length?`, ${l.length-k} not`:""}`);if(K(),Y())z().then(Kn)};if(Hn)s("ice-bp").onclick=()=>On("promote"),s("ice-bd").onclick=()=>On("dismiss"),s("ice-bc").onclick=()=>{J.clear(),z()};if(s("ice-all"))s("ice-all").onchange=()=>{let o=B.map((l)=>L(w[l]));if(ut===B.length)o.forEach((l)=>J.delete(l));else o.forEach((l)=>J.add(l));z()};if(s("ice-all"))s("ice-all").indeterminate=ut>0&&ut<B.length;s("pbody").querySelectorAll("[data-ck]").forEach((o)=>o.onchange=()=>{let l=L(w[+o.dataset.ck]);me=l,o.checked?J.add(l):J.delete(l),z()});let zn=(o)=>{let l=L(w[o]);me=l,re=re===l?null:l,Ae=null,z().then(()=>s("ice-o-"+o)?.focus())};s("pbody").querySelectorAll(".ice-row").forEach((o)=>o.onclick=(l)=>{if(!l.target.closest("input,button"))zn(+o.dataset.i)}),s("pbody").querySelectorAll("[data-o]").forEach((o)=>o.onclick=()=>zn(+o.dataset.o)),s("pbody").querySelectorAll("details.ice-imgs").forEach((o)=>o.ontoggle=()=>o.open&&o.querySelectorAll("img[data-src]").forEach((l)=>l.src=l.dataset.src)),s("pbody").querySelectorAll("[data-pr]").forEach((o)=>o.onclick=()=>{me=re=Ae=L(w[+o.dataset.pr]),z().then(()=>{s("ice-title")?.focus(),s("ice-title")?.select()})}),s("pbody").querySelectorAll("[data-ds]").forEach((o)=>o.onclick=()=>{o.disabled=!0,Xt("dismiss",w[+o.dataset.ds],"Dismissed")}),s("pbody").querySelectorAll("[data-rs]").forEach((o)=>o.onclick=()=>{o.disabled=!0,Xt("restore",w[+o.dataset.rs],"Restored")});let pt=w.find((o)=>L(o)===re);if(s("ice-rp"))s("ice-rp").onclick=async()=>{let o=pt,l=L(o);ie={k:l,text:"",wait:!0},z();let k=await A("/reply",{op:"draft",dir:o.dir,id:o.id,style:pn(o.dir)});if(!ie||ie.k!==l)return;ie={k:l,text:k.text||"",note:k.ok?k.note||"":"",wait:!1},z().then(()=>s("ice-reply")?.focus())};if(s("ice-rst"))s("ice-rst").onchange=(o)=>{let l=pt.dir;yt[l]=o.target.value;try{localStorage.setItem("kz-reply-style:"+l,yt[l])}catch(k){}};if(s("ice-rg"))s("ice-rg").onclick=async()=>{let o=pt,l=ie;l.again=!0,z();let k=await A("/reply",{op:"draft",dir:o.dir,id:o.id,style:pn(o.dir),avoid:l.text});if(ie!==l)return;if(l.again=!1,k.text)l.text=k.text;l.note=k.ok?k.note||"":"",z().then(()=>s("ice-reply")?.focus())};if(s("ice-reply")){let o=s("ice-reply");o.oninput=()=>{ie.text=o.value,s("ice-rn").textContent=`${o.value.length} chars`,s("ice-rs").disabled=!o.value.trim()},o.onkeydown=(l)=>{if(l.key==="Escape")l.stopPropagation(),s("ice-rc").click()},s("ice-rc").onclick=()=>{ie=null,z()},s("ice-rs").onclick=async()=>{let l=pt,k=ie;k.busy=!0,z();let S=await A("/reply",{op:"send",dir:l.dir,id:l.id,text:k.text});if(S.ok)ie=null,C("Reply posted");else if(S.sent)ie=null;else k.busy=!1;if(K(),Y())z()}}if(s("ice-ok")){let o=()=>{let l=s("ice-title").value.trim();if(!l){s("ice-title").focus();return}s("ice-ok").disabled=!0,Xt("promote",pt,"Promoted to Ideas",{text:l,details:s("ice-details").value})};s("ice-ok").onclick=o,s("ice-cancel").onclick=()=>{Ae=null,z()},s("ice-title").onkeydown=(l)=>{if(l.key==="Enter")o();if(l.key==="Escape")s("ice-cancel").click()},s("ice-details").onkeydown=(l)=>{if(l.key==="Enter"&&(l.ctrlKey||l.metaKey))o();if(l.key==="Escape")s("ice-cancel").click()}}It=(o)=>{if(o.altKey||o.ctrlKey||o.metaKey)return!1;let l=o.key,k=B.findIndex((G)=>L(w[G])===me),S=B[k];if(l==="Escape"){if(s("src-sec")?.classList.contains("open"))return qt(),!0;if(re){if(Ae)Ae=null;else re=null;return z(),!0}if(J.size)return J.clear(),z(),!0;return!1}if(k<0)return!1;if(l==="j"||l==="k"){o.preventDefault();let G=B[Math.max(0,Math.min(B.length-1,k+(l==="j"?1:-1)))];return me=L(w[G]),s("pbody").querySelectorAll(".ice-row").forEach((U)=>U.classList.toggle("cur",+U.dataset.i===G)),s("pbody").querySelector(`.ice-row[data-i="${G}"]`)?.scrollIntoView({block:"nearest"}),!0}let D=l==="o"?s("ice-o-"+S):l==="x"?s("ice-ck-"+S):l==="p"?J.size?s("ice-bp"):s("ice-pr-"+S):l==="d"?J.size?s("ice-bd"):s("ice-ds-"+S):null;if(!D||D.disabled)return!1;return o.preventDefault(),D.click(),!0},s("pbody").querySelectorAll("[data-chk]").forEach((o)=>{let l=v[+o.dataset.chk];o.onclick=()=>{o.disabled=!0,o.innerHTML=q("refresh")+" Checking…",ze({op:"gather",dir:l.dir,url:l.url},(k)=>{let S=k.results&&k.results[0];C(S?S.ok?`${l.label||g(l.url)}: ${S.note}${S.added?" in the icebox":""}`:`Failed: ${S.note}`:"Checked")})}}),s("pbody").querySelectorAll("[data-ag]").forEach((o)=>{let l=v[+o.dataset.ag];o.onclick=()=>{if(o.disabled=!0,M)o.innerHTML=q("refresh")+" Gathering…";ze({op:"agent",dir:l.dir,url:l.url},(k)=>{let S=k.results&&k.results[0];C(k.via==="chatgpt"?S&&S.ok?`${l.label||g(l.url)}: ${S.note}${S.added?" in the icebox":""}`:`Failed: ${S?S.note:"no answer"}`:"/kaizen gather opened in your terminal: "+Ne(k.agent))})}}),s("pbody").querySelectorAll("[data-rm]").forEach((o)=>{let l=v[+o.dataset.rm];o.onclick=()=>{if(!confirm(`Remove ${l.label||g(l.url)}? What it gathered stays; added again later, it may bring back ones you deleted or dismissed.`))return;ze({op:"remove",dir:v[+o.dataset.rm].dir,url:v[+o.dataset.rm].url},()=>C("Source removed"))}});let Gn=w.findIndex((o)=>L(o)===me);if(r){let o=w.findIndex((l)=>L(l)===r[0]);(o>=0?s(r[1]+"-"+o):Gn>=0?s("ice-o-"+Gn):null)?.focus()}let Ct=!r&&t[2]&&s(t[2]);if(Ct)(Ct.disabled?s(Ct.id.replace("src-mb-","src-mv-").replace("src-cs-","src-ct-")):Ct)?.focus();if(s("src-add"))s("src-add").onclick=()=>{let o=nt==="social",l=s("src-url").value.trim(),k=s("src-q").value.trim(),S=s("src-plat").value;if(o?!k:!l){s(o?"src-q":"src-url").focus();return}let D=s("src-dir").value;if(!D){s("src-dir").focus();return}s("src-add").disabled=!0,s("src-add").textContent="Adding…",(async()=>{let G=s("src-label").value.trim()||(o?`${bt[S]}: ${k}`:""),U=s("src-cat").value.trim(),F=await A("/sources",o?{op:"add",dir:D,platform:S,query:k,label:G,category:U}:{op:"add",dir:D,url:l,label:G,category:U});if(F.ok&&o)s("src-q").value=s("src-label").value=s("src-cat").value="",C("Source added"),K();else if(F.ok){s("src-url").value=s("src-label").value=s("src-cat").value="";let ce=await A("/sources",{op:"gather",dir:D,url:l}),pe=ce.results&&ce.results[0];C(pe?pe.ok?`Source added: ${pe.note} in the icebox`:`Source added, but: ${pe.note}`:"Source added"),K()}else C(F.error||"Could not add source");if(F.ok)st=!1;if(Y())z()})()},s("src-url").onkeydown=s("src-q").onkeydown=s("src-label").onkeydown=s("src-cat").onkeydown=(o)=>{if(o.key==="Enter")s("src-add").click()};if(s("ice-new"))s("ice-new").onclick=()=>{let o=!u;if(!u&&!Ie().length)return C("Pick a project first");let l=Ue(`${o?'<div class="combo" id="ndc"><button type="button" class="sel" id="newdir" aria-label="Project for this request" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>':""}<textarea id="newidea" placeholder="What did someone ask for?" aria-label="Request"></textarea><div class="att"></div><input type="file" multiple hidden><div class="row"><button type="button" class="ib" id="newpick" aria-label="Attach files" title="Attach files">${q("clip")}</button><button type="button" class="btn ghost" value="">Cancel</button><button class="btn pri">Add</button></div>`),k=l.querySelector("form"),S=k.querySelector("textarea"),D=[],G=(F)=>{D.push(F),k.querySelector(".att").textContent=D.map((ce)=>ce.name).join(" · ")};if(k.querySelector("#newpick").onclick=()=>k.querySelector("input[type=file]").click(),k.querySelector("input[type=file]").onchange=(F)=>{[...F.target.files].forEach(G),F.target.value=""},S.onpaste=(F)=>{for(let ce of F.clipboardData.items)if(ce.kind==="file"){let pe=ce.getAsFile();G(new File([pe],pe.name==="image.png"?`paste-${Date.now()}.png`:pe.name,{type:pe.type})),F.preventDefault()}},k.querySelector(".btn.ghost").onclick=()=>l.close(),o)Ot(k);let U=Fe([S]);if(U)k.querySelector(".row").prepend(U);l.addEventListener("close",()=>de(!1)),S.onkeydown=(F)=>{if(F.key==="Enter"&&!F.shiftKey)F.preventDefault(),k.requestSubmit()},k.onsubmit=async(F)=>{F.preventDefault();let ce=S.value.trim(),pe=u||Me;if(!ce)return S.focus();if(!pe)return C("Pick a project first");if((await A("/icebox",{op:"add",dir:pe,text:ce,files:await At(D)})).ok)C("Added to the icebox"),l.close()},S.focus()};if(s("src-go"))s("src-go").onclick=()=>{s("src-go").disabled=!0,s("src-go").innerHTML=q("refresh")+" Gathering…",ze({op:"gather",dir:u||void 0},(o)=>{let l=o.results||[],k=(U)=>v.some((F)=>F.url===U.url&&F.social),S=l.reduce((U,F)=>U+F.added,0),D=l.filter((U)=>!U.ok&&!k(U)).length,G=l.filter(k).length;C((S?`${S} new in the icebox`:"Nothing new")+(D?`, ${D} source${D===1?"":"s"} could not be read`:"")+(G?`; ${G} social for /kaizen gather`:""))})}}var fn=new URLSearchParams(location.search).has("all");function Oe(e){if(fn)return;try{e?localStorage.setItem("kz-proj",e):localStorage.removeItem("kz-proj")}catch(t){}}var Te=(e)=>e.split(/[\\/]/).pop()||e,ot=(e,t="")=>{let n=f`<span class="plogo" aria-hidden="true">${(Te(t)||"?").charAt(0).toUpperCase()}</span>`;return i.S?.projects?.some((a)=>a.dir===e&&a.logo)?f`<img class="plogo" src="/project-logo?dir=${encodeURIComponent(e)}" alt="" loading="lazy" onerror="this.outerHTML=this.dataset.f" data-f="${String(n)}">`:n},Ut=(e,t)=>f`${ot(e,t)}<span>${Te(t)}</span>`;function _t(){let e=s("projq").value.trim().toLowerCase(),t=0;s("projs").innerHTML=i.PL.map((a,r)=>a[0].toLowerCase().includes(e)?f`${a[2]&&!t++?f`<div class="grp">In your projects folder</div>`:""}<button role="option" data-i="${r}" title="${a[0]}">${a[1]==="all"?f`<span class="plogo" aria-hidden="true">${q("board")}</span><span>${a[0]}</span>`:a[1]==="__new"||a[1]==="__find"||a[1]==="__clone"?f`<span class="plogo" aria-hidden="true">${q("plus")}</span><span>${a[0]}</span>`:Ut(a[1],a[0])}</button>`:"").join("")||'<div class="grp">No match</div>';let n=s("projs").querySelector("button");if(n)n.classList.add("on")}function Gt(e){if(s("projpop").hidden=!e,s("proj").setAttribute("aria-expanded",e),e)s("projq").value="",_t(),s("projq").focus()}async function io(e){if(e==="__find"){C("Searching…");let t=await A("/find",{});if(t.ok)C(t.added?`Found ${t.added} new project${t.added===1?"":"s"}`:"No new projects found")}else if(e==="__new"){let t=Ue('<b>New project</b><input name="name" placeholder="Project name" aria-label="Project name" spellcheck="false" autocomplete="off"><div class="row"><input name="to" placeholder="Folder to create" aria-label="Create in" spellcheck="false" autocomplete="off"><button type="button" class="btn ghost" data-a="pick">Pick…</button></div><div class="row"><button type="button" class="btn ghost" data-a="x">Cancel</button><button class="btn pri">Create</button></div>'),n=t.querySelector("form"),a=n.querySelector("[name=name]"),r=n.querySelector("[name=to]"),c="";a.oninput=async()=>{let u=a.value.trim();if(!u||/[\\/:*?"<>|]/.test(u)||r.value.trim()&&r.value!==c)return;let h=await A("/new-project",{name:u,check:!0});if(h.ok&&a.value.trim()===u)r.value=c=h.to},n.querySelector("[data-a=x]").onclick=()=>t.close(),n.querySelector("[data-a=pick]").onclick=async()=>{let u=await A("/pick-folder",{});if(u.path)r.value=u.path.replace(/[\\/]+$/,"")+(u.path.includes("\\")?"\\":"/")+(a.value.trim()||"")},a.focus();let m=await new Promise((u)=>{t.addEventListener("close",()=>u(null)),n.onsubmit=async(h)=>{if(h.preventDefault(),!a.value.trim())return a.focus();let p=await A("/new-project",{name:a.value.trim(),to:r.value.trim()||void 0});if(!p.ok)return;u(p.dir),t.close()}});if(!m)return;await A("/cd",{dir:m}),i.all=!1,i.dir=m,Oe(m),C("Project created")}else if(e==="__clone"){let t=Ue('<b>Clone from GitHub</b><input name="url" placeholder="https://github.com/owner/repo" aria-label="GitHub link (https or ssh)" spellcheck="false" autocomplete="off"><div class="row"><input name="to" placeholder="Folder to clone into" aria-label="Clone into" spellcheck="false" autocomplete="off"><button type="button" class="btn ghost" data-a="pick">Pick…</button></div><div class="row"><button type="button" class="btn ghost" data-a="x">Cancel</button><button class="btn pri">Clone</button></div>'),n=t.querySelector("form"),a=n.querySelector("[name=url]"),r=n.querySelector("[name=to]"),c="",m="",u=async()=>{let p=a.value.trim();if(!p)return null;let g=await A("/clone",{url:p,check:!0});if(!g.ok)return null;if(c=g.name,!r.value.trim()||r.value===m)r.value=m=g.to;return g};a.onchange=u,n.querySelector("[data-a=x]").onclick=()=>t.close(),n.querySelector("[data-a=pick]").onclick=async()=>{if(!await u())return a.focus();let p=await A("/pick-folder",{});if(p.path)r.value=p.path.replace(/[\\/]+$/,"")+(p.path.includes("\\")?"\\":"/")+c},a.focus();let h=await new Promise((p)=>{t.addEventListener("close",()=>p(null)),n.onsubmit=async(g)=>{if(g.preventDefault(),!await u())return a.focus();C("Cloning…");let y=await A("/clone",{url:a.value.trim(),to:r.value.trim()||void 0});if(!y.ok)return;p(y.dir),t.close()}});if(!h)return;await A("/cd",{dir:h}),i.all=!1,i.dir=h,Oe(h),C("Cloned")}else if(e==="all")i.all=!0,Oe();else if((await A("/cd",{dir:e})).ok)i.all=!1,i.dir=e,Oe(e);if(await K(),W==="sources")z()}var Me=null;function Ot(e){let t=ro(),n=e.querySelector("#newdir"),a=e.querySelector("#ndc .menu-pop"),r=a.querySelector("input"),c=e.querySelector("#nds");if(!t.some((p)=>p.dir===Me))Me=t[0]?.dir??null;let m=()=>{let p=t.find((g)=>g.dir===Me);n.innerHTML=p?Ut(p.dir,p.label):"Choose project"},u=()=>{let p=r.value.trim().toLowerCase();c.innerHTML=t.filter((g)=>g.label.toLowerCase().includes(p)).map((g)=>f`<button type="button" role="option" data-d="${g.dir}" title="${g.label}" class="${g.dir===Me?"on":""}">${Ut(g.dir,g.label)}</button>`).join("")||'<div class="grp">No match</div>',c.querySelectorAll("button").forEach((g)=>g.onclick=()=>{Me=g.dataset.d,m(),h(!1),e.querySelector("#newidea").focus()})},h=(p)=>{if(a.hidden=!p,n.setAttribute("aria-expanded",p),p)r.value="",u(),r.focus()};n.onclick=(p)=>{p.preventDefault(),h(a.hidden)},r.oninput=u,r.onkeydown=(p)=>{if(p.key==="Escape")p.stopPropagation(),h(!1);if(p.key==="Enter")p.preventDefault(),c.querySelector("button")?.click()},m()}function ro(){let e=Ie(),t=e.filter((n)=>i.shown.has(n.dir));return t.length?t:e}function Ie(){let e=[...i.S.projects];for(let t of i.S.cards)if(!e.some((n)=>n.dir===t.state))e.push({dir:t.state,label:t.where});return e}var at=[];function Ft(){let e=s("showq").value.trim().toLowerCase();at=Ie(),s("shows").innerHTML=at.map((n,a)=>n.label.toLowerCase().includes(e)?f`<button type="button" role="menuitemcheckbox" data-i="${a}" aria-checked="${String(i.shown.has(n.dir))}" title="${n.label}">${Ut(n.dir,n.label)}<svg class="ck"><use href="#i-check"/></svg></button>`:"").join("")||'<div class="grp">No match</div>';let t=at.filter((n)=>i.shown.has(n.dir)).length;s("showcount").textContent=t?`${t} of ${at.length} shown`:`All ${at.length} shown`}function zt(){try{localStorage.setItem("kz-show",JSON.stringify([...i.shown]))}catch(e){}if(H(),Y())z()}function fs(){s("proj").onclick=()=>Gt(s("projpop").hidden),s("projq").oninput=_t,s("projq").onkeydown=(e)=>{let t=[...s("projs").querySelectorAll("button")],n=t.findIndex((a)=>a.classList.contains("on"));if(e.key==="Escape")Gt(!1),s("proj").focus();else if(e.key==="Enter"){if(t[n])t[n].click()}else if((e.key==="ArrowDown"||e.key==="ArrowUp")&&t[n]){e.preventDefault();let a=t[(n+(e.key==="ArrowDown"?1:t.length-1))%t.length];t[n].classList.remove("on"),a.classList.add("on"),a.scrollIntoView({block:"nearest"})}},s("projs").onclick=(e)=>{let t=e.target.closest("button");if(t)Gt(!1),io(i.PL[t.dataset.i][1])},document.addEventListener("click",(e)=>{if(!e.target.closest("#combo"))Gt(!1)}),s("show").onclick=()=>{let e=s("showpop").hidden;if(s("showpop").hidden=!e,s("show").setAttribute("aria-expanded",e),e)s("showq").value="",Ft(),s("showq").focus()},s("showq").oninput=Ft,s("showq").onkeydown=(e)=>{if(e.key==="Escape")s("showpop").hidden=!0,s("show").setAttribute("aria-expanded",!1),s("show").focus()},s("shows").onclick=(e)=>{let t=e.target.closest("button[data-i]"),n=t?.dataset.i,a=at[n]?.dir;if(!a)return;i.shown.has(a)?i.shown.delete(a):i.shown.add(a),zt(),s("shows").querySelector(`[data-i="${n}"]`)?.focus()},s("showall").onclick=()=>{i.shown.clear(),zt()},document.addEventListener("click",(e)=>{if(!e.target.closest("#showc"))s("showpop").hidden=!0,s("show").setAttribute("aria-expanded",!1)}),s("newtop").onclick=vt}var $e=null,vn="",mn=6,Wt=new Set,kt=(()=>{try{return JSON.parse(localStorage.getItem("kz-order"))||{}}catch(e){return{}}})(),Z=!1,wt=(()=>{try{return localStorage.getItem("kz-ideas-tab")==="backlog"?"backlog":"ideas"}catch(e){return"ideas"}})(),je=null,hn=[["high","High+",(e)=>e==="critical"||e==="high"],["medium","Medium",(e)=>e==="medium"],["low","Low",(e)=>e==="low"],["none","No severity",(e)=>!e]],gn=()=>(i.S.backlog||[]).map((e)=>({...e,kind:"backlog",status:e.starting?"starting":"idea",title:e.text,column:0,archived:!1,_k:"backlog:"+e.state+":"+e.source+"#"+e.n})),bn=(e)=>{if(e.kind!=="backlog")return we(e);let t=i.S.cards.find((n)=>n.kind==="run"&&n.id===e.source&&n.state===e.state);if(!t)return C(e.source==="orphan"?"Kept from a run that was pruned":"That run is not on this board");vs(t,"backlog",e.n)},$t=!1,ye=new Set,j=(e)=>e._k||(e.kind==="idea"?"idea:"+e.state+":"+e.text:"run:"+e.state+":"+e.id),lo=(e)=>e.kind==="run"?i.S.cards.filter((t)=>t.parent===e.id&&t.state===e.state):[],co=(e)=>!!e.parent&&i.S.cards.some((t)=>t.kind==="run"&&t.id===e.parent&&t.state===e.state&&!t.archived&&t.column!==4),yn=new Set,ms=(e)=>Xe.has(et(e))?"Sent to agent":e.status!=="waiting"?"Open":{"approvals.plan":"Review plan",findings:"See findings","approvals.review":"Review work"}[e.awaiting]||"Open";function uo(e){let t=j(e),n=document.createElement("div");n.className="card"+(t===i.openKey?" open":"")+(t===i.menuKey?" menu":"")+(e.archived?" archived":""),n.tabIndex=0,n.dataset.k=t,n.setAttribute("role","button");let a=He[e.status]||"idea",r=e.kind==="run"?e.text.replace(/-/g," "):"",c=e.status==="waiting"?ft(e.awaiting)||"needs you":e.status||"idea",m=[],u=[],h=(d,v,w="")=>f`<span class="mk${w}" role="img" title="${v}" aria-label="${v}"><svg><use href="#${d}"/></svg></span>`;if(i.all){let d=e.where.split("/").filter(Boolean).pop()||e.where;m.push(f`<span class="chip proj" title="${e.where}">${d}</span>`)}for(let d of e.tags||[])if(Yn[d])u.push(h("c-"+d,d==="full"?"Full run: separate reviewer":d==="lite"?"Lite run: one session":nn[d]||d,d==="full"||d==="lite"?" accent":""));else if(d==="yolo")u.push(f`<span class="mk warn" title="Started in Yolo: the agent runs commands without asking">yolo</span>`);else m.push(f`<span class="chip">${nn[d]||d}</span>`);if(e.pty&&e.pty.alive&&e.pty.asking)u.push(h("i-ask","The agent is asking you something: open it, terminal button at the top"," warn ask"));else if(e.pty&&e.pty.alive)u.push(h("i-term","The agent is running in this card: open it, terminal button at the top"," run"));let p=e.notes&&/^\[[^\]]*\] (?:Gathered from|Promoted from the icebox:) (.+?)\. Outside text/.exec(e.notes);if(e.parent)m.push(f`<span class="chip link" title="Filed by ${e.parent}">${e.parent.replace(/^\d{4}-\d\d-\d\d-/,"").replace(/-/g," ")}</span>`);if(e.kind==="backlog"){if(e.severity)m.push(f`<span class="chip${e.severity==="critical"||e.severity==="high"?" warn":""}"><i></i>${e.severity}</span>`);m.push(e.source==="orphan"?f`<span class="chip" title="Kept from a pruned run">orphaned</span>`:f`<span class="chip link" title="Left by ${e.source}${e.at?" · "+e.at:""}">${e.source.replace(/^\d{4}-\d\d-\d\d-/,"").replace(/-/g," ")}</span>`)}if(e.branch)m.push(f`<span class="chip" title="Built on its own branch, ${e.branch}">branch</span>`);if(p)m.push(f`<span class="chip" title="From ${p[1]}">gathered</span>`);else if(e.notes)m.push(f`<span class="chip">notes</span>`);if(e.git)u.push(h("c-git",e.git==="pushed"?"Committed & pushed":"Not committed & pushed yet",e.git==="pushed"?" ok":" warn"));if(e.open)u.push(h("i-bug",`${e.open} open finding${e.open===1?"":"s"} to fix`," warn"));let g=$t&&!Z&&e.column===4;if(e.column===4&&!Z)n.classList.add("compact");let y=e.kind==="run"&&!Z&&!g?e.status==="waiting"?f`<button class="btn sm${Xe.has(et(e))?"":" pri"} act" data-a="open">${ms(e)}</button>`:e.status==="stalled"&&Pt(e)?f`<button class="btn sm act" data-a="resume">Resume</button>`:"":"";n.innerHTML=(g?f`<input type="checkbox" class="pick" ${_(ye.has(t)?"checked":"")} aria-label="Select">`:"")+f`<div class="c-id"><span class="st d-${a}" role="img" title="${c}" aria-label="${c}"><svg><use href="#s-${a}"/></svg></span><span class="idt">${r}</span><span class="age">${e.kind==="run"?Jn(e.moved||i.S.now,i.S.now):""}</span><button class="ib sm more" data-a="menu" title="Actions" aria-label="Actions" aria-haspopup="menu" aria-expanded="${String(t===i.menuKey)}">${q("more")}</button></div>
25
25
  <div class="c-title"${e.kind==="idea"&&qe(e.text).parts.length?f` title="${e.text}"`:""}>${e.kind==="idea"?qe(e.text).head:e.title||e.id}</div>
26
26
  ${y}
27
- ${m.length||u.length?f`<div class="c-foot"><span class="c-chips">${m}</span><span class="c-marks">${u}</span></div>`:""}`;let x=lo(e);if(x.length){let d=yn.has(t),v=x.filter((b)=>b.status==="done").length,w=x.some((b)=>b.status==="waiting"),E=document.createElement("div");E.className="kids",E.innerHTML=f`<button class="kids-t" aria-expanded="${String(d)}">${d?"▾":"▸"} ${x.length} ${x.length===1?"child":"children"}${v?` · ${v} done`:""}${w?f`<span class="kids-w" title="A child is waiting on you"></span>`:""}</button>`+(d?f`<ul>${x.map((b,M)=>{let R=He[b.status]||"idea";return f`<li data-i="${M}" tabindex="0" role="button" title="Open"><span class="st d-${R}"><svg><use href="#s-${R}"/></svg></span><span class="kids-s">${Je[b.status]||b.status}</span><span class="kids-n" title="${b.title||b.text||b.id}">${b.kind==="idea"?qe(b.text).head:(b.text||b.id||"").replace(/-/g," ")}</span></li>`})}</ul>`:""),E.querySelector(".kids-t").onclick=(b)=>{b.stopPropagation(),d?yn.delete(t):yn.add(t),H()},E.querySelectorAll("li").forEach((b)=>{let M=(R)=>{R.stopPropagation(),we(x[+b.dataset.i])};b.onclick=M,b.onkeydown=(R)=>(R.key==="Enter"||R.key===" ")&&(R.preventDefault(),M(R))}),n.appendChild(E)}if(t===i.menuKey){let d=[];if(Z){if(d.push(["restore","Restore",q("restore")]),d.push(["open","Open",q("open")]),e.kind==="idea")d.push(null,["delete","Delete",q("trash"),"danger"])}else if(e.kind==="backlog")d.push(["run",e.status==="starting"?"Run again":"Run",q("play")],["open","Open its run",q("open")],null,["reject","Reject",q("x"),"danger"]);else if(e.kind==="idea"&&e.status==="idea")d.push(["run","Run",q("play")],["edit","Edit",q("pen")],...i.S.projects.some((v)=>v.dir!==e.state)?[["move","Move to project",q("ext")]]:[],["archive","Archive",q("archive")],null,["reject","Reject",q("x")],["delete","Delete",q("trash"),"danger"]);else if(e.kind==="idea")d.push(["open","Open",q("open")],["run","Run again",q("play")],["archive","Archive",q("archive")],null,["delete","Remove",q("trash"),"danger"]);else{if(d.push(["open",ms(e),q("open")]),d.push(["archive","Archive",q("archive")]),e.column===1&&e.status!=="abandoned")d.push(["toidea","Back to idea",q("restore")]);if(e.status==="waiting"||e.status==="running"||e.status==="stalled")d.push(null,["reject","Abandon",q("x"),"danger"])}n.innerHTML+=f`<div class="menu-pop" role="menu">${d.map((v)=>v?f`<button role="menuitem" data-a="${v[0]}" class="${v[3]||""}">${v[2]}${v[1]}</button>`:_("<hr>"))}</div>`}if(n.querySelectorAll("[data-a]").forEach((d)=>d.onclick=async(v)=>{v.stopPropagation();let w=d.dataset.a,E=i.menuKey;if(i.menuKey=null,w==="menu"){i.menuKey=E===t?null:t,H();return}if(w==="open"){bn(e);return}if(w==="resume"){Ze(e,d);return}if(w==="archive"||w==="restore"){Bt(e,w==="archive");return}if(w==="toidea"){if((await A("/to-idea",{dir:e.state,id:e.id})).ok)C("Back in Ideas");I();return}i.mode=w,i.modeKey=t,H()}),g)return n.classList.toggle("picked",ye.has(t)),n.onclick=()=>{ye.has(t)?ye.delete(t):ye.add(t),H()},n;return n.onclick=()=>{if(i.menuKey){i.menuKey=null,H();return}bn(e)},n.onkeydown=(d)=>{if(d.key==="Enter"||d.key===" ")d.preventDefault(),bn(e)},n}function po(e){let t=document.createElement("li");t.className="inl";let n=e.state;if(i.mode==="edit")t.innerHTML=f`<input value="${e.text}"><textarea placeholder="Anything the planner should know">${e.notes||""}</textarea><div class="q dim" id="clong" hidden>The first line is over 72 characters; logs will cut it short.</div><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Save</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{let r=t.querySelector("input").value.trim(),c=t.querySelector("textarea").value.trim();if(i.mode=null,r&&(r!==e.text||c!==(e.notes||"")))await A("/inbox",{op:"edit",dir:n,text:e.text,next:r,notes:c});I()};if(i.mode==="reject"){let r=e.kind!=="run";t.innerHTML=f`<div class="q">${e.kind==="backlog"?"Not doing this backlog item?":r?"Reject this idea?":"Abandon this run?"} The reason is recorded.</div><input placeholder="Why not?"><div class="row"><button class="btn ghost" data-a="c">Keep it</button><button class="btn warn" data-a="s">${r?"Reject":"Abandon"}</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{let c=t.querySelector("input").value.trim();if(!c){C("A reason is required");return}if(i.mode=null,(e.kind==="backlog"?await A("/backlog",{op:"reject",dir:n,source:e.source,n:e.n,reason:c}):r?await A("/inbox",{op:"reject",dir:n,text:e.text,reason:c}):await A("/abort",{dir:n,id:e.id,why:c})).ok)C(r?"Rejected":"Abandoned, reason recorded");I()}}if(i.mode==="delete")t.innerHTML=f`<div class="q">Delete "${e.text}"?</div><div class="q dim">Not recorded anywhere.</div><div class="row"><button class="btn ghost" data-a="c">Keep it</button><button class="btn danger" data-a="s">Delete</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{if(i.mode=null,(await A("/inbox",{op:"delete",dir:n,text:e.text})).ok)C("Deleted");I()};if(i.mode==="move"){let r=i.S.projects.filter((c)=>c.dir!==n);t.innerHTML=f`<div class="q">Move "${e.text}" to</div><select class="sel" aria-label="Project">${r.map((c)=>f`<option value="${c.dir}" title="${c.label}">${Te(c.label)}</option>`)}</select><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Move</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{let c=t.querySelector("select"),m=c.value,u=c.selectedOptions[0]?.textContent;if(i.mode=null,(await A("/inbox",{op:"move",dir:n,to:m,text:e.text})).ok)C(`Moved to ${u}`);I()}}if(i.mode==="run")t.innerHTML=f`<div class="q">Run "${e.text}"</div><div class="q dim">Opens your agent with this request in a terminal.</div><div class="q dim">Full plans, builds, and checks with a separate reviewer. Lite does it in one session.</div><div id="run-pick" style="display:grid;gap:6px"><div id="run-agent" class="agents" role="radiogroup" aria-label="Agent"></div></div><div class="run-actions"><label class="yolo" id="run-yolo" title="Yolo: the agent runs every command and edit without asking" hidden><button type="button" class="chk" role="switch" aria-checked="false"></button>Yolo</label><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn" data-a="l">Lite</button><button class="btn pri" data-a="s">Full run</button></div></div>`,t.querySelector("[data-a=s]").onclick=()=>kn(e,"full"),t.querySelector("[data-a=l]").onclick=()=>kn(e,"lite"),fo(t,e);if(t.querySelector("[data-a=c]").onclick=()=>{i.mode=null,H()},t.querySelectorAll("input,textarea").forEach((r,c)=>_e(r,"f:"+i.mode+":"+i.modeKey+":"+c)),i.mode!=="run"){let r=Fe([...t.querySelectorAll("input,textarea")]);if(r)t.querySelector(".row").prepend(r)}let a=t.querySelector("input");if(a)a.onkeydown=(r)=>{if(r.key==="Enter")t.querySelector("[data-a=s]").click();if(r.key==="Escape")i.mode=null,H()},setTimeout(()=>a.focus());return t}var oe=null,Ne=(e)=>e.name+(e.note?". "+e.note:"");async function fo(e,t){let n=j(t),a=e.querySelector("#run-agent"),r=await fetch("/agents?dir="+encodeURIComponent(t.state)).then((h)=>h.json()).catch(()=>null);if(!r||!r.agents||!r.agents.length){e.querySelector("#run-pick").hidden=!0;return}if(r.agents.some((h)=>h.cmd==="agy"))r.agents=r.agents.filter((h)=>h.cmd!=="gemini");if(!oe||oe.k!==n)oe={k:n,agent:r.agents.some((h)=>h.cmd===r.default)?r.default:r.agents[0].cmd};a.innerHTML=r.agents.map((h)=>f`<button type="button" class="agent" role="radio" aria-checked="${String(h.cmd===oe.agent)}" data-cmd="${h.cmd}"><svg><use href="#c-${h.cmd}"/></svg>${h.name}</button>`).join("");let c=e.querySelector("#run-yolo"),m=c.querySelector(".chk"),u=()=>{let h=!!r.agents.find((g)=>g.cmd===oe.agent)?.yolo,p=!!oe.yolo;c.hidden=!h,m.classList.toggle("on",p),m.setAttribute("aria-checked",p)};a.querySelectorAll(".agent").forEach((h)=>h.onclick=()=>{oe.agent=h.dataset.cmd,a.querySelectorAll(".agent").forEach((p)=>p.setAttribute("aria-checked",p===h)),u()}),m.onclick=()=>{oe.yolo=!oe.yolo,u()},u(),a.onkeydown=m.onkeydown=(h)=>{if(h.key==="Escape")i.mode=null,H()}}async function kn(e,t){i.mode=null,Se();let n=oe&&oe.k===j(e)?oe:null;oe=null;let a=!!(n&&n.yolo&&!s("run-yolo")?.hidden),r=e.kind==="backlog"?{source:e.source,n:e.n}:void 0,c=await A("/run",{dir:e.state,text:e.text,backlog:r,kind:t,agent:n?n.agent:void 0,yolo:a});if(c.ok){if(C((t==="full"?"Full":t==="lite"?"Lite":"Started")+(a?" yolo":"")+" run "+We(c)+": "+Ne(c.agent)),c.pty&&!r)i.wantTerm=j(e)}I()}var mo={1:"Ideas you run are planned here",2:"Approved plans are built here",3:"Finished builds are checked here"};function hs(e,t){e.innerHTML=f`${i.all?f`<div class="combo" id="ndc"><button type="button" class="sel" id="newdir" aria-label="Project for this idea" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>`:""}<input id="newidea" placeholder="What do you want to build?"><textarea id="newnotes" placeholder="Anything the planner should know (optional)"></textarea><div class="att" id="newatt"></div><input type="file" id="newfile" multiple hidden><div class="row"><button type="button" class="ib" id="newpick" aria-label="Attach files" title="Attach files">${q("clip")}</button><button type="button" class="btn ghost" id="nc">Cancel</button><div class="split"><button type="button" class="btn pri" id="ns">Add</button><button type="button" class="btn pri" id="nm" aria-label="More ways to add" aria-haspopup="menu" aria-expanded="false">${q("chev")}</button><div class="menu-pop" role="menu" hidden><button type="button" role="menuitem" id="nr" title="Add the idea and run it now">${q("play")}Start run</button></div></div></div>`;let n=e.querySelector("#newidea"),a=[];_e(n,"f:new:0"),_e(e.querySelector("textarea"),"f:new:1");let r=async(m)=>{let u=n.value.trim();if(!u)return;let h=e.querySelector("textarea").value.trim();t();let p=i.all?Me:null;if((await A("/inbox",p?{op:"add",dir:p,text:u,notes:h}:{op:"add",text:u,notes:h})).ok){if(C("Idea added"),a.length){let y=await At(a);if(!(await A("/note",{dir:p,for:u,text:"",files:y})).ok)C("Idea added, files not attached")}if(m){await I();let y=i.S.cards.find((x)=>x.kind==="idea"&&x.text===u&&(!p||x.state===p));if(y)return kn(y)}}I()},c=(m)=>{a.push(m),e.querySelector("#newatt").textContent=a.map((u)=>u.name).join(" · ")};e.querySelector("#newpick").onclick=()=>e.querySelector("#newfile").click(),e.querySelector("#newfile").onchange=(m)=>{[...m.target.files].forEach(c),m.target.value=""};for(let m of[n,e.querySelector("textarea")])m.onpaste=(u)=>{for(let h of u.clipboardData.items)if(h.kind==="file"){let p=h.getAsFile();c(new File([p],p.name==="image.png"?`paste-${Date.now()}.png`:p.name,{type:p.type})),u.preventDefault()}};if(i.all)Ot(e);e.querySelector("#ns").onclick=()=>r(!1),e.querySelector("#nr").onclick=()=>r(!0);{let m=e.querySelector("#nm"),u=e.querySelector(".split .menu-pop"),h=(g,y)=>{if(u.hidden=!g,m.setAttribute("aria-expanded",String(g)),g&&y)setTimeout(()=>u.querySelector("button").focus())};m.onclick=(g)=>{g.stopPropagation(),h(u.hidden,g.detail===0)},u.onkeydown=(g)=>{if(g.key==="Escape")g.preventDefault(),g.stopPropagation(),h(!1),m.focus()},e.addEventListener("click",(g)=>{if(!u.hidden&&!g.target.closest(".split"))h(!1)});let p=e.querySelector(".split");p.addEventListener("focusout",(g)=>{if(!p.contains(g.relatedTarget))h(!1)})}e.querySelector("#nc").onclick=t;{let m=Fe([n,e.querySelector("textarea")]);if(m)e.querySelector(".row").prepend(m)}n.onkeydown=(m)=>{if(m.key==="Enter")m.preventDefault(),r(!1);if(m.key==="Escape")m.preventDefault(),t()},setTimeout(()=>n.focus())}function vt(){if(i.mode==="new")i.mode=null,H();let e=Ue(""),t=e.querySelector("form");t.onsubmit=(n)=>n.preventDefault(),e.addEventListener("close",()=>de(!1)),hs(t,()=>e.close())}function H(){if(i.mode&&i.mode!=="new"&&![...i.S.cards,...gn()].some((y)=>j(y)===i.modeKey))i.mode=null;if(i.mode!=="run"&&oe)oe.yolo=!1;if(!i.mode)Qn("f:");try{sessionStorage.setItem("kz-form",JSON.stringify(["new","edit","reject"].includes(i.mode)?[i.mode,i.modeKey]:null))}catch(y){}let e=wn(),t=i.all&&i.shown.size&&Ie().some((y)=>i.shown.has(y.dir)),n=i.S.cards.filter((y)=>y.archived===Z&&(!t||i.shown.has(y.state))),a=s("board");a.innerHTML="",a.removeAttribute("aria-busy");let r=i.S.cards.filter((y)=>y.archived).length,c=(y)=>n.filter((x)=>x.status===y).length,m=[];for(let y of["waiting","running","stalled","starting","idea","done","abandoned"]){let x=c(y);if(!x&&y!=="running")continue;let d=y==="running"?'<span class="d-run"><span class="pulse"></span></span>':`<span class="st d-${He[y]}"><svg><use href="#s-${He[y]}"/></svg></span>`;m.push(`<button class="${y==="waiting"?"attn ":""}${$e===y?"on":""}" data-f="${y}" title="Show only ${Je[y]}">${d}${x} ${Je[y]}${y==="idea"&&x!==1?"s":""}</button>`)}m.push(`<button class="${Z?"on":""}" data-f="__archive" title="${n.filter((y)=>y.kind==="idea").length} ideas · ${n.filter((y)=>y.kind==="run").length} runs on the board">${Z?"Back to board":"Archive "+r}</button>`),s("strip").innerHTML=m.join(""),s("fbtn").hidden=!1,s("fbtn").classList.toggle("on",!!$e||Z),s("fbtn").classList.toggle("wait",!$e&&!Z&&c("waiting")>0),s("strip").querySelectorAll("button").forEach((y)=>y.onclick=()=>{if(y.dataset.f==="__archive"){Z=!Z,$e=null,H();return}$e=$e===y.dataset.f?null:y.dataset.f,H()}),i.PL=[["All projects","all"],...i.S.projects.map((y)=>[y.label,y.dir]),...i.S.fresh.map((y)=>[y.label,y.dir,1]),["New project…","__new"],["Find projects on this PC…","__find"],["Clone from GitHub…","__clone"]];let u=i.all||!i.S.dir?"All projects":(i.PL.find((y)=>y[1]===i.S.dir)||["All projects"])[0];if(s("proj").innerHTML=i.all||!i.S.dir?f`<span class="plogo" aria-hidden="true">${q("board")}</span><span>${u}</span>`:f`${ot(i.S.dir,u)}<span>${Te(u)}</span>`,s("proj").title=u,!s("projpop").hidden)_t();s("showc").hidden=!i.all;let h=Ie(),p=h.filter((y)=>i.shown.has(y.dir)).length;if(s("show").textContent=p?`Showing ${p} of ${h.length}`:"Showing all",!s("showpop").hidden)Ft();if(!n.length&&i.mode!=="new"){if(a.innerHTML=Z?'<div class="empty"><div class="mascot"></div><b>Nothing archived</b></div>':'<div class="empty"><div class="mascot"></div><b>Nothing here yet</b><p>Add an idea and kaizen will plan it, build it, and check the result.</p><button class="btn pri" id="newempty">New idea</button></div>',!Z)s("newempty").onclick=vt}Zn.forEach((y,x)=>{let d=document.createElement("section"),v=vn.trim().toLowerCase(),w=x===0&&!Z&&wt==="backlog",b=(w?gn().filter((L)=>!t||i.shown.has(L.state)):n).filter((L)=>L.column===x&&(!w||!je||hn.find((N)=>N[0]===je)[2](L.severity))&&(!$e||L.status===$e)&&(Z||!co(L))&&(!v||`${L.title||""} ${L.text||""} ${L.id||""}`.toLowerCase().includes(v)));if(x<4&&!w){let L=(K)=>K.kind==="run"?K.moved||0:K.added||0,N=kt[x]==="old",T=(K)=>i.S.cards.indexOf(K),O=(K)=>K.status==="waiting"?1:0;b.sort((K,V)=>O(V)-O(K)||(N?L(K)-L(V)||T(K)-T(V):L(V)-L(K)||T(V)-T(K)))}let M=x===4&&!Z&&b.length,R=(L)=>b.filter((N)=>i.S.now-(N.moved||i.S.now)>L*Vn),Q=x===0&&!Z,re=Q?gn().filter((L)=>!t||i.shown.has(L.state)):[],Pe=Q?`<span class="itabs" role="tablist" aria-label="Ideas column">${[["ideas","Ideas",'<svg><use href="#s-idea"/></svg>',n.filter((L)=>L.column===0).length],["backlog","Backlog: what finished runs left undone",q("clock"),re.length]].map(([L,N,T,O])=>`<button role="tab" aria-selected="${String(wt===L)}" data-t="${L}" title="${N}" aria-label="${N}, ${O}">${T}<i>${O}</i></button>`).join("")}</span>`:`${y}<i>${b.length||""}</i>`,X=w?`<button class="ib sm sevf${je?" on":""}" data-sv="1" title="${je?"Severity: "+hn.find((L)=>L[0]===je)[1]:"Filter by severity"}" aria-label="Filter by severity" aria-haspopup="menu" aria-expanded="${String(i.sevMenu)}">${q("filter")}</button>`+(i.sevMenu?`<div class="menu-pop sev-pop" role="menu"><button role="menuitemradio" aria-checked="${String(!je)}" data-s="">All severities <i>${re.length}</i></button><hr>${hn.map(([L,N,T])=>`<button role="menuitemradio" aria-checked="${String(je===L)}" data-s="${L}">${N} <i>${re.filter((O)=>T(O.severity)).length}</i></button>`).join("")}</div>`:""):"",xe=`<h2>${Pe}${x<4&&!w&&b.length>1?`<button class="btn sm ghost ord" title="Sort order">${kt[x]==="old"?"Oldest":"Newest"}</button>`:""}${X}${Q?`<button class="ib sm newi" title="New idea" aria-label="New idea">${q("plus")}</button>`:""}`;if(M&&$t)xe+=`<span class="pickbar"><button class="btn sm ghost" data-p="cancel">Cancel</button><button class="btn sm pri" data-p="go" ${ye.size?"":"disabled"}>Archive ${ye.size||""}</button></span>`;else if(M)xe+='<button class="btn sm ghost clear" title="Archive finished runs">Clear ▾</button>';if(M&&i.clearMenu)xe+=`<div class="menu-pop clear-pop"><button data-c="7">Older than a week <i>${R(7).length}</i></button><button data-c="3">Older than 3 days <i>${R(3).length}</i></button><button data-c="0">Everything <i>${b.length}</i></button><hr><button data-c="pick">Pick which…</button></div>`;if(d.innerHTML=xe+"</h2>",Q){d.querySelectorAll("[data-t]").forEach((N)=>N.onclick=()=>{wt=N.dataset.t,i.sevMenu=!1;try{localStorage.setItem("kz-ideas-tab",wt)}catch(T){}H()});let L=d.querySelector("[data-sv]");if(L)L.onclick=(N)=>{N.stopPropagation(),i.sevMenu=!i.sevMenu,H()};d.querySelectorAll("[data-s]").forEach((N)=>N.onclick=(T)=>{T.stopPropagation(),je=N.dataset.s||null,i.sevMenu=!1,H()})}{let L=d.querySelector(".ord");if(L)L.onclick=(N)=>{N.stopPropagation(),kt[x]=kt[x]==="old"?"new":"old";try{localStorage.setItem("kz-order",JSON.stringify(kt))}catch(T){}H()}}if(Q)d.querySelector(".newi").onclick=()=>{i.mode="new",wt="ideas",H()};if(M){let L=d.querySelector(".clear");if(L)L.onclick=(N)=>{N.stopPropagation(),i.clearMenu=!i.clearMenu,H()};d.querySelectorAll("[data-c]").forEach((N)=>N.onclick=async(T)=>{T.stopPropagation(),i.clearMenu=!1;let O=N.dataset.c;if(O==="pick"){$t=!0,ye.clear(),H();return}let K=R(+O);if(!K.length){C("Nothing that old"),H();return}for(let V of K)await A("/archive",{dir:V.state,id:V.id});C("Archived "+K.length),I()}),d.querySelectorAll("[data-p]").forEach((N)=>N.onclick=async(T)=>{if(T.stopPropagation(),N.dataset.p==="cancel"){$t=!1,ye.clear(),H();return}let O=b.filter((K)=>ye.has(j(K)));$t=!1,ye.clear();for(let K of O)await A("/archive",{dir:K.state,id:K.id});C("Archived "+O.length),I()})}let le=document.createElement("ul");if(x===0&&i.mode==="new"){let L=document.createElement("li");L.className="inl",le.appendChild(L),hs(L,()=>{i.mode=null,H()})}let se=Wt.has(x)||b.length<=mn+1?b:b.slice(0,mn);if(se.forEach((L)=>{if(i.modeKey===j(L)&&i.mode&&i.mode!=="new"){le.appendChild(po(L));return}let N=document.createElement("li");N.appendChild(uo(L)),le.appendChild(N)}),se.length<b.length){let L=document.createElement("li");L.innerHTML=`<button class="more-btn">Show ${b.length-se.length} more</button>`,L.firstChild.onclick=()=>{Wt.add(x),H()},le.appendChild(L)}else if(Wt.has(x)&&b.length>mn+1){let L=document.createElement("li");L.innerHTML='<button class="more-btn">Show less</button>',L.firstChild.onclick=()=>{Wt.delete(x),H()},le.appendChild(L)}if(!b.length&&x>=1&&x<=3&&n.length&&!$e&&!v&&!Z)le.innerHTML=`<li class="hint">${mo[x]}</li>`;if(w&&!b.length&&!$e&&!v)le.innerHTML=`<li class="hint">${je?"Nothing at this severity":"What finished runs left undone shows here"}</li>`;d.appendChild(le),a.appendChild(d)});let g=i.S.cards.filter((y)=>y.kind==="run"&&y.status==="waiting"&&!y.archived).length;if(document.title=g?`(${g}) kaizen`:"kaizen",de(!1),e&&document.activeElement===document.body)$n(e)?.focus({preventScroll:!0})}function gs(){let e=s("bq");_e(e,"bq"),vn=e.value,e.addEventListener("input",()=>{vn=e.value,H()}),e.addEventListener("keydown",(t)=>{if(t.key==="Escape")t.stopPropagation(),e.value="",e.dispatchEvent(new Event("input")),e.blur()})}function bs(){let e=s("fbtn"),t=document.querySelector(".ftr"),n=(a)=>{if(t.classList.toggle("on",a),e.setAttribute("aria-expanded",a),a)t.style.top=e.getBoundingClientRect().bottom+6+"px"};e.onclick=()=>n(!t.classList.contains("on")),document.addEventListener("pointerdown",(a)=>{if(t.classList.contains("on")&&!t.contains(a.target)&&!e.contains(a.target))n(!1)}),document.addEventListener("keydown",(a)=>a.key==="Escape"&&t.classList.contains("on")&&n(!1))}var wn=()=>document.activeElement?.closest?.("#board .card")?.dataset.k,$n=(e)=>e&&s("board").querySelector('.card[data-k="'+CSS.escape(e)+'"]');function ys(e,t){let n=[...s("board").querySelectorAll("section")].map((u)=>[...u.querySelectorAll(".card")]).filter((u)=>u.length);if(!n.length)return;let a=document.activeElement.closest(".card"),r=n.findIndex((u)=>u.includes(a)),c=0;if(r<0)r=0;else if(c=n[r].indexOf(a),t)c=Math.max(0,Math.min(n[r].length-1,c+t));else r=Math.max(0,Math.min(n.length-1,r+e)),c=Math.min(c,n[r].length-1);let m=n[r][c];m.focus({preventScroll:!0}),m.scrollIntoView({block:"nearest",inline:"nearest"})}var ks=()=>i.dir?{"x-kz-dir":i.dir}:{};async function I(e){let t;try{t=await fetch("/state"+(i.all?"?all=1":""),{headers:ks()})}catch{C(ws);return}let n=i.openKey&&i.S.cards.find((a)=>j(a)===i.openKey);i.S=await t.json();{let a=new Map;for(let r of i.S.cards){let c=j(r),m=a.get(c)||0;a.set(c,m+1),r._k=m?c+"#"+m:c}}for(let a of i.S.cards){if(a.kind!=="run"||a.archived)continue;if(due(j(a),a.notice,a.status==="done",i.S.now))_n(a.notice)}if(!(e&&i.mode&&s("board").querySelector(".inl input")))H();if(cs())be();if(i.wantTerm&&!i.openKey&&!i.mode){let a=i.S.cards.find((r)=>j(r)===i.wantTerm&&r.pty);if(a){i.wantTerm=null,we(a);return}}if(i.openKey){let a=i.S.cards.find((c)=>j(c)===i.openKey),r=s("pbody").querySelector(".notes textarea");if(r&&document.activeElement===r)return;if(!a){if(n&&n.kind==="idea")ho(n);return}if(i.runDoc)te(a,!0,!0);else if(a.kind==="idea")xn(a)}}async function ho(e){let t=j(e),n=(r)=>r.toLowerCase().replace(/\s+/g," ").trim(),a=n(e.text);for(let r of i.S.cards.filter((c)=>c.kind==="run"&&c.state===e.state).sort((c,m)=>(m.moved??0)-(c.moved??0)).slice(0,5)){let c=await fetch(`/run/${encodeURIComponent(r.id)}?dir=${encodeURIComponent(r.state)}`).then((m)=>m.json()).catch(()=>null);if(i.openKey!==t)return;if(n(c?.request||"").includes(a)){we(r);return}}if(i.openKey===t)ee()}var ws="The board server is not running — start it with kaizen web (or the shortcut), then reload.";async function A(e,t){let n;try{n=await fetch(e,{method:"POST",headers:{"content-type":"application/json",...ks()},body:JSON.stringify(t)})}catch{return C(ws),{ok:!1}}let a=await n.json().catch(()=>({}));if(!n.ok){let r=a.error||a.why||"That did not work";if(C(r,{title:r.slice(0,80),what:`POST ${e} failed (HTTP ${n.status}): ${r}`}),a.manual)console.log("run manually:",a.manual)}return a}var Qt=null,Vt=null,$s=null,We=(e)=>e.pty?"running in its card":"opened in your terminal",dn=()=>$s||=new Promise((e,t)=>{let n=document.createElement("link");n.rel="stylesheet",n.href="/term.css",document.head.append(n);let a=document.createElement("script");a.src="/term.js",a.onload=()=>e(window.kzTerm),a.onerror=()=>{$s=null,t()},document.head.append(a)});function Ee(){if(Vt)Vt();Vt=null,Qt=null,s("panel").classList.remove("wide")}async function Sn(e){let t=e.pty.key;if(Qt===t&&s("pbody").querySelector(".term"))return;Ee(),Qt=t,s("panel").classList.add("wide"),s("pbody").innerHTML='<div class="term" aria-label="Agent terminal"></div>';let n;try{n=await dn()}catch{s("pbody").innerHTML='<p style="color:var(--dim)">The terminal could not load.</p>';return}if(Qt!==t)return;Vt=n(s("pbody").querySelector(".term"),t,()=>I(!0))}var xs=/\u200b/g;function Ss(e){let t=(c)=>{if(c.nodeType===3)return c.textContent.replace(xs,"").replace(/\u00a0/g," ").replace(/\n/g,`
27
+ ${m.length||u.length?f`<div class="c-foot"><span class="c-chips">${m}</span><span class="c-marks">${u}</span></div>`:""}`;let x=lo(e);if(x.length){let d=yn.has(t),v=x.filter((b)=>b.status==="done").length,w=x.some((b)=>b.status==="waiting"),E=document.createElement("div");E.className="kids",E.innerHTML=f`<button class="kids-t" aria-expanded="${String(d)}">${d?"▾":"▸"} ${x.length} ${x.length===1?"child":"children"}${v?` · ${v} done`:""}${w?f`<span class="kids-w" title="A child is waiting on you"></span>`:""}</button>`+(d?f`<ul>${x.map((b,M)=>{let R=He[b.status]||"idea";return f`<li data-i="${M}" tabindex="0" role="button" title="Open"><span class="st d-${R}"><svg><use href="#s-${R}"/></svg></span><span class="kids-s">${Je[b.status]||b.status}</span><span class="kids-n" title="${b.title||b.text||b.id}">${b.kind==="idea"?qe(b.text).head:(b.text||b.id||"").replace(/-/g," ")}</span></li>`})}</ul>`:""),E.querySelector(".kids-t").onclick=(b)=>{b.stopPropagation(),d?yn.delete(t):yn.add(t),H()},E.querySelectorAll("li").forEach((b)=>{let M=(R)=>{R.stopPropagation(),we(x[+b.dataset.i])};b.onclick=M,b.onkeydown=(R)=>(R.key==="Enter"||R.key===" ")&&(R.preventDefault(),M(R))}),n.appendChild(E)}if(t===i.menuKey){let d=[];if(Z){if(d.push(["restore","Restore",q("restore")]),d.push(["open","Open",q("open")]),e.kind==="idea")d.push(null,["delete","Delete",q("trash"),"danger"])}else if(e.kind==="backlog")d.push(["run",e.status==="starting"?"Run again":"Run",q("play")],["open","Open its run",q("open")],null,["reject","Reject",q("x"),"danger"]);else if(e.kind==="idea"&&e.status==="idea")d.push(["run","Run",q("play")],["edit","Edit",q("pen")],...i.S.projects.some((v)=>v.dir!==e.state)?[["move","Move to project",q("ext")]]:[],["archive","Archive",q("archive")],null,["reject","Reject",q("x")],["delete","Delete",q("trash"),"danger"]);else if(e.kind==="idea")d.push(["open","Open",q("open")],["run","Run again",q("play")],["archive","Archive",q("archive")],null,["delete","Remove",q("trash"),"danger"]);else{if(d.push(["open",ms(e),q("open")]),d.push(["archive","Archive",q("archive")]),e.column===1&&e.status!=="abandoned")d.push(["toidea","Back to idea",q("restore")]);if(e.status==="waiting"||e.status==="running"||e.status==="stalled")d.push(null,["reject","Abandon",q("x"),"danger"])}n.innerHTML+=f`<div class="menu-pop" role="menu">${d.map((v)=>v?f`<button role="menuitem" data-a="${v[0]}" class="${v[3]||""}">${v[2]}${v[1]}</button>`:_("<hr>"))}</div>`}if(n.querySelectorAll("[data-a]").forEach((d)=>d.onclick=async(v)=>{v.stopPropagation();let w=d.dataset.a,E=i.menuKey;if(i.menuKey=null,w==="menu"){i.menuKey=E===t?null:t,H();return}if(w==="open"){bn(e);return}if(w==="resume"){Ze(e,d);return}if(w==="archive"||w==="restore"){Bt(e,w==="archive");return}if(w==="toidea"){if((await A("/to-idea",{dir:e.state,id:e.id})).ok)C("Back in Ideas");K();return}i.mode=w,i.modeKey=t,H()}),g)return n.classList.toggle("picked",ye.has(t)),n.onclick=()=>{ye.has(t)?ye.delete(t):ye.add(t),H()},n;return n.onclick=()=>{if(i.menuKey){i.menuKey=null,H();return}bn(e)},n.onkeydown=(d)=>{if(d.key==="Enter"||d.key===" ")d.preventDefault(),bn(e)},n}function po(e){let t=document.createElement("li");t.className="inl";let n=e.state;if(i.mode==="edit")t.innerHTML=f`<input value="${e.text}"><textarea placeholder="Anything the planner should know">${e.notes||""}</textarea><div class="q dim" id="clong" hidden>The first line is over 72 characters; logs will cut it short.</div><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Save</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{let r=t.querySelector("input").value.trim(),c=t.querySelector("textarea").value.trim();if(i.mode=null,r&&(r!==e.text||c!==(e.notes||"")))await A("/inbox",{op:"edit",dir:n,text:e.text,next:r,notes:c});K()};if(i.mode==="reject"){let r=e.kind!=="run";t.innerHTML=f`<div class="q">${e.kind==="backlog"?"Not doing this backlog item?":r?"Reject this idea?":"Abandon this run?"} The reason is recorded.</div><input placeholder="Why not?"><div class="row"><button class="btn ghost" data-a="c">Keep it</button><button class="btn warn" data-a="s">${r?"Reject":"Abandon"}</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{let c=t.querySelector("input").value.trim();if(!c){C("A reason is required");return}if(i.mode=null,(e.kind==="backlog"?await A("/backlog",{op:"reject",dir:n,source:e.source,n:e.n,reason:c}):r?await A("/inbox",{op:"reject",dir:n,text:e.text,reason:c}):await A("/abort",{dir:n,id:e.id,why:c})).ok)C(r?"Rejected":"Abandoned, reason recorded");K()}}if(i.mode==="delete")t.innerHTML=f`<div class="q">Delete "${e.text}"?</div><div class="q dim">Not recorded anywhere.</div><div class="row"><button class="btn ghost" data-a="c">Keep it</button><button class="btn danger" data-a="s">Delete</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{if(i.mode=null,(await A("/inbox",{op:"delete",dir:n,text:e.text})).ok)C("Deleted");K()};if(i.mode==="move"){let r=i.S.projects.filter((c)=>c.dir!==n);t.innerHTML=f`<div class="q">Move "${e.text}" to</div><select class="sel" aria-label="Project">${r.map((c)=>f`<option value="${c.dir}" title="${c.label}">${Te(c.label)}</option>`)}</select><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Move</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{let c=t.querySelector("select"),m=c.value,u=c.selectedOptions[0]?.textContent;if(i.mode=null,(await A("/inbox",{op:"move",dir:n,to:m,text:e.text})).ok)C(`Moved to ${u}`);K()}}if(i.mode==="run")t.innerHTML=f`<div class="q">Run "${e.text}"</div><div class="q dim">Opens your agent with this request in a terminal.</div><div class="q dim">Full plans, builds, and checks with a separate reviewer. Lite does it in one session.</div><div id="run-pick" style="display:grid;gap:6px"><div id="run-agent" class="agents" role="radiogroup" aria-label="Agent"></div></div><div class="run-actions"><label class="yolo" id="run-yolo" title="Yolo: the agent runs every command and edit without asking" hidden><button type="button" class="chk" role="switch" aria-checked="false"></button>Yolo</label><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn" data-a="l">Lite</button><button class="btn pri" data-a="s">Full run</button></div></div>`,t.querySelector("[data-a=s]").onclick=()=>kn(e,"full"),t.querySelector("[data-a=l]").onclick=()=>kn(e,"lite"),fo(t,e);if(t.querySelector("[data-a=c]").onclick=()=>{i.mode=null,H()},t.querySelectorAll("input,textarea").forEach((r,c)=>_e(r,"f:"+i.mode+":"+i.modeKey+":"+c)),i.mode!=="run"){let r=Fe([...t.querySelectorAll("input,textarea")]);if(r)t.querySelector(".row").prepend(r)}let a=t.querySelector("input");if(a)a.onkeydown=(r)=>{if(r.key==="Enter")t.querySelector("[data-a=s]").click();if(r.key==="Escape")i.mode=null,H()},setTimeout(()=>a.focus());return t}var ae=null,Ne=(e)=>e.name+(e.note?". "+e.note:"");async function fo(e,t){let n=j(t),a=e.querySelector("#run-agent"),r=await fetch("/agents?dir="+encodeURIComponent(t.state)).then((h)=>h.json()).catch(()=>null);if(!r||!r.agents||!r.agents.length){e.querySelector("#run-pick").hidden=!0;return}if(r.agents.some((h)=>h.cmd==="agy"))r.agents=r.agents.filter((h)=>h.cmd!=="gemini");if(!ae||ae.k!==n)ae={k:n,agent:r.agents.some((h)=>h.cmd===r.default)?r.default:r.agents[0].cmd};a.innerHTML=r.agents.map((h)=>f`<button type="button" class="agent" role="radio" aria-checked="${String(h.cmd===ae.agent)}" data-cmd="${h.cmd}"><svg><use href="#c-${h.cmd}"/></svg>${h.name}</button>`).join("");let c=e.querySelector("#run-yolo"),m=c.querySelector(".chk"),u=()=>{let h=!!r.agents.find((g)=>g.cmd===ae.agent)?.yolo,p=!!ae.yolo;c.hidden=!h,m.classList.toggle("on",p),m.setAttribute("aria-checked",p)};a.querySelectorAll(".agent").forEach((h)=>h.onclick=()=>{ae.agent=h.dataset.cmd,a.querySelectorAll(".agent").forEach((p)=>p.setAttribute("aria-checked",p===h)),u()}),m.onclick=()=>{ae.yolo=!ae.yolo,u()},u(),a.onkeydown=m.onkeydown=(h)=>{if(h.key==="Escape")i.mode=null,H()}}async function kn(e,t){i.mode=null,Se();let n=ae&&ae.k===j(e)?ae:null;ae=null;let a=!!(n&&n.yolo&&!s("run-yolo")?.hidden),r=e.kind==="backlog"?{source:e.source,n:e.n}:void 0,c=await A("/run",{dir:e.state,text:e.text,backlog:r,kind:t,agent:n?n.agent:void 0,yolo:a});if(c.ok){if(C((t==="full"?"Full":t==="lite"?"Lite":"Started")+(a?" yolo":"")+" run "+We(c)+": "+Ne(c.agent)),c.pty&&!r)i.wantTerm=j(e)}K()}var mo={1:"Ideas you run are planned here",2:"Approved plans are built here",3:"Finished builds are checked here"};function hs(e,t){e.innerHTML=f`${i.all?f`<div class="combo" id="ndc"><button type="button" class="sel" id="newdir" aria-label="Project for this idea" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>`:""}<input id="newidea" placeholder="What do you want to build?"><textarea id="newnotes" placeholder="Anything the planner should know (optional)"></textarea><div class="att" id="newatt"></div><input type="file" id="newfile" multiple hidden><div class="row"><button type="button" class="ib" id="newpick" aria-label="Attach files" title="Attach files">${q("clip")}</button><button type="button" class="btn ghost" id="nc">Cancel</button><div class="split"><button type="button" class="btn pri" id="ns">Add</button><button type="button" class="btn pri" id="nm" aria-label="More ways to add" aria-haspopup="menu" aria-expanded="false">${q("chev")}</button><div class="menu-pop" role="menu" hidden><button type="button" role="menuitem" id="nr" title="Add the idea and run it now">${q("play")}Start run</button></div></div></div>`;let n=e.querySelector("#newidea"),a=[];_e(n,"f:new:0"),_e(e.querySelector("textarea"),"f:new:1");let r=async(m)=>{let u=n.value.trim();if(!u)return;let h=e.querySelector("textarea").value.trim();t();let p=i.all?Me:null;if((await A("/inbox",p?{op:"add",dir:p,text:u,notes:h}:{op:"add",text:u,notes:h})).ok){if(C("Idea added"),a.length){let y=await At(a);if(!(await A("/note",{dir:p,for:u,text:"",files:y})).ok)C("Idea added, files not attached")}if(m){await K();let y=i.S.cards.find((x)=>x.kind==="idea"&&x.text===u&&(!p||x.state===p));if(y)return kn(y)}}K()},c=(m)=>{a.push(m),e.querySelector("#newatt").textContent=a.map((u)=>u.name).join(" · ")};e.querySelector("#newpick").onclick=()=>e.querySelector("#newfile").click(),e.querySelector("#newfile").onchange=(m)=>{[...m.target.files].forEach(c),m.target.value=""};for(let m of[n,e.querySelector("textarea")])m.onpaste=(u)=>{for(let h of u.clipboardData.items)if(h.kind==="file"){let p=h.getAsFile();c(new File([p],p.name==="image.png"?`paste-${Date.now()}.png`:p.name,{type:p.type})),u.preventDefault()}};if(i.all)Ot(e);e.querySelector("#ns").onclick=()=>r(!1),e.querySelector("#nr").onclick=()=>r(!0);{let m=e.querySelector("#nm"),u=e.querySelector(".split .menu-pop"),h=(g,y)=>{if(u.hidden=!g,m.setAttribute("aria-expanded",String(g)),g&&y)setTimeout(()=>u.querySelector("button").focus())};m.onclick=(g)=>{g.stopPropagation(),h(u.hidden,g.detail===0)},u.onkeydown=(g)=>{if(g.key==="Escape")g.preventDefault(),g.stopPropagation(),h(!1),m.focus()},e.addEventListener("click",(g)=>{if(!u.hidden&&!g.target.closest(".split"))h(!1)});let p=e.querySelector(".split");p.addEventListener("focusout",(g)=>{if(!p.contains(g.relatedTarget))h(!1)})}e.querySelector("#nc").onclick=t;{let m=Fe([n,e.querySelector("textarea")]);if(m)e.querySelector(".row").prepend(m)}n.onkeydown=(m)=>{if(m.key==="Enter")m.preventDefault(),r(!1);if(m.key==="Escape")m.preventDefault(),t()},setTimeout(()=>n.focus())}function vt(){if(i.mode==="new")i.mode=null,H();let e=Ue(""),t=e.querySelector("form");t.onsubmit=(n)=>n.preventDefault(),e.addEventListener("close",()=>de(!1)),hs(t,()=>e.close())}function H(){if(i.mode&&i.mode!=="new"&&![...i.S.cards,...gn()].some((y)=>j(y)===i.modeKey))i.mode=null;if(i.mode!=="run"&&ae)ae.yolo=!1;if(!i.mode)Qn("f:");try{sessionStorage.setItem("kz-form",JSON.stringify(["new","edit","reject"].includes(i.mode)?[i.mode,i.modeKey]:null))}catch(y){}let e=wn(),t=i.all&&i.shown.size&&Ie().some((y)=>i.shown.has(y.dir)),n=i.S.cards.filter((y)=>y.archived===Z&&(!t||i.shown.has(y.state))),a=s("board");a.innerHTML="",a.removeAttribute("aria-busy");let r=i.S.cards.filter((y)=>y.archived).length,c=(y)=>n.filter((x)=>x.status===y).length,m=[];for(let y of["waiting","running","stalled","starting","idea","done","abandoned"]){let x=c(y);if(!x&&y!=="running")continue;let d=y==="running"?'<span class="d-run"><span class="pulse"></span></span>':`<span class="st d-${He[y]}"><svg><use href="#s-${He[y]}"/></svg></span>`;m.push(`<button class="${y==="waiting"?"attn ":""}${$e===y?"on":""}" data-f="${y}" title="Show only ${Je[y]}">${d}${x} ${Je[y]}${y==="idea"&&x!==1?"s":""}</button>`)}m.push(`<button class="${Z?"on":""}" data-f="__archive" title="${n.filter((y)=>y.kind==="idea").length} ideas · ${n.filter((y)=>y.kind==="run").length} runs on the board">${Z?"Back to board":"Archive "+r}</button>`),s("strip").innerHTML=m.join(""),s("fbtn").hidden=!1,s("fbtn").classList.toggle("on",!!$e||Z),s("fbtn").classList.toggle("wait",!$e&&!Z&&c("waiting")>0),s("strip").querySelectorAll("button").forEach((y)=>y.onclick=()=>{if(y.dataset.f==="__archive"){Z=!Z,$e=null,H();return}$e=$e===y.dataset.f?null:y.dataset.f,H()}),i.PL=[["All projects","all"],...i.S.projects.map((y)=>[y.label,y.dir]),...i.S.fresh.map((y)=>[y.label,y.dir,1]),["New project…","__new"],["Find projects on this PC…","__find"],["Clone from GitHub…","__clone"]];let u=i.all||!i.S.dir?"All projects":(i.PL.find((y)=>y[1]===i.S.dir)||["All projects"])[0];if(s("proj").innerHTML=i.all||!i.S.dir?f`<span class="plogo" aria-hidden="true">${q("board")}</span><span>${u}</span>`:f`${ot(i.S.dir,u)}<span>${Te(u)}</span>`,s("proj").title=u,!s("projpop").hidden)_t();s("showc").hidden=!i.all;let h=Ie(),p=h.filter((y)=>i.shown.has(y.dir)).length;if(s("show").textContent=p?`Showing ${p} of ${h.length}`:"Showing all",!s("showpop").hidden)Ft();if(!n.length&&i.mode!=="new"){if(a.innerHTML=Z?'<div class="empty"><div class="mascot"></div><b>Nothing archived</b></div>':'<div class="empty"><div class="mascot"></div><b>Nothing here yet</b><p>Add an idea and kaizen will plan it, build it, and check the result.</p><button class="btn pri" id="newempty">New idea</button></div>',!Z)s("newempty").onclick=vt}Zn.forEach((y,x)=>{let d=document.createElement("section"),v=vn.trim().toLowerCase(),w=x===0&&!Z&&wt==="backlog",b=(w?gn().filter((L)=>!t||i.shown.has(L.state)):n).filter((L)=>L.column===x&&(!w||!je||hn.find((N)=>N[0]===je)[2](L.severity))&&(!$e||L.status===$e)&&(Z||!co(L))&&(!v||`${L.title||""} ${L.text||""} ${L.id||""}`.toLowerCase().includes(v)));if(x<4&&!w){let L=(I)=>I.kind==="run"?I.moved||0:I.added||0,N=kt[x]==="old",T=(I)=>i.S.cards.indexOf(I),O=(I)=>I.status==="waiting"?1:0;b.sort((I,V)=>O(V)-O(I)||(N?L(I)-L(V)||T(I)-T(V):L(V)-L(I)||T(V)-T(I)))}let M=x===4&&!Z&&b.length,R=(L)=>b.filter((N)=>i.S.now-(N.moved||i.S.now)>L*Vn),Q=x===0&&!Z,se=Q?gn().filter((L)=>!t||i.shown.has(L.state)):[],Pe=Q?`<span class="itabs" role="tablist" aria-label="Ideas column">${[["ideas","Ideas",'<svg><use href="#s-idea"/></svg>',n.filter((L)=>L.column===0).length],["backlog","Backlog: what finished runs left undone",q("clock"),se.length]].map(([L,N,T,O])=>`<button role="tab" aria-selected="${String(wt===L)}" data-t="${L}" title="${N}" aria-label="${N}, ${O}">${T}<i>${O}</i></button>`).join("")}</span>`:`${y}<i>${b.length||""}</i>`,X=w?`<button class="ib sm sevf${je?" on":""}" data-sv="1" title="${je?"Severity: "+hn.find((L)=>L[0]===je)[1]:"Filter by severity"}" aria-label="Filter by severity" aria-haspopup="menu" aria-expanded="${String(i.sevMenu)}">${q("filter")}</button>`+(i.sevMenu?`<div class="menu-pop sev-pop" role="menu"><button role="menuitemradio" aria-checked="${String(!je)}" data-s="">All severities <i>${se.length}</i></button><hr>${hn.map(([L,N,T])=>`<button role="menuitemradio" aria-checked="${String(je===L)}" data-s="${L}">${N} <i>${se.filter((O)=>T(O.severity)).length}</i></button>`).join("")}</div>`:""):"",xe=`<h2>${Pe}${x<4&&!w&&b.length>1?`<button class="btn sm ghost ord" title="Sort order">${kt[x]==="old"?"Oldest":"Newest"}</button>`:""}${X}${Q?`<button class="ib sm newi" title="New idea" aria-label="New idea">${q("plus")}</button>`:""}`;if(M&&$t)xe+=`<span class="pickbar"><button class="btn sm ghost" data-p="cancel">Cancel</button><button class="btn sm pri" data-p="go" ${ye.size?"":"disabled"}>Archive ${ye.size||""}</button></span>`;else if(M)xe+='<button class="btn sm ghost clear" title="Archive finished runs">Clear ▾</button>';if(M&&i.clearMenu)xe+=`<div class="menu-pop clear-pop"><button data-c="7">Older than a week <i>${R(7).length}</i></button><button data-c="3">Older than 3 days <i>${R(3).length}</i></button><button data-c="0">Everything <i>${b.length}</i></button><hr><button data-c="pick">Pick which…</button></div>`;if(d.innerHTML=xe+"</h2>",Q){d.querySelectorAll("[data-t]").forEach((N)=>N.onclick=()=>{wt=N.dataset.t,i.sevMenu=!1;try{localStorage.setItem("kz-ideas-tab",wt)}catch(T){}H()});let L=d.querySelector("[data-sv]");if(L)L.onclick=(N)=>{N.stopPropagation(),i.sevMenu=!i.sevMenu,H()};d.querySelectorAll("[data-s]").forEach((N)=>N.onclick=(T)=>{T.stopPropagation(),je=N.dataset.s||null,i.sevMenu=!1,H()})}{let L=d.querySelector(".ord");if(L)L.onclick=(N)=>{N.stopPropagation(),kt[x]=kt[x]==="old"?"new":"old";try{localStorage.setItem("kz-order",JSON.stringify(kt))}catch(T){}H()}}if(Q)d.querySelector(".newi").onclick=()=>{i.mode="new",wt="ideas",H()};if(M){let L=d.querySelector(".clear");if(L)L.onclick=(N)=>{N.stopPropagation(),i.clearMenu=!i.clearMenu,H()};d.querySelectorAll("[data-c]").forEach((N)=>N.onclick=async(T)=>{T.stopPropagation(),i.clearMenu=!1;let O=N.dataset.c;if(O==="pick"){$t=!0,ye.clear(),H();return}let I=R(+O);if(!I.length){C("Nothing that old"),H();return}for(let V of I)await A("/archive",{dir:V.state,id:V.id});C("Archived "+I.length),K()}),d.querySelectorAll("[data-p]").forEach((N)=>N.onclick=async(T)=>{if(T.stopPropagation(),N.dataset.p==="cancel"){$t=!1,ye.clear(),H();return}let O=b.filter((I)=>ye.has(j(I)));$t=!1,ye.clear();for(let I of O)await A("/archive",{dir:I.state,id:I.id});C("Archived "+O.length),K()})}let le=document.createElement("ul");if(x===0&&i.mode==="new"){let L=document.createElement("li");L.className="inl",le.appendChild(L),hs(L,()=>{i.mode=null,H()})}let oe=Wt.has(x)||b.length<=mn+1?b:b.slice(0,mn);if(oe.forEach((L)=>{if(i.modeKey===j(L)&&i.mode&&i.mode!=="new"){le.appendChild(po(L));return}let N=document.createElement("li");N.appendChild(uo(L)),le.appendChild(N)}),oe.length<b.length){let L=document.createElement("li");L.innerHTML=`<button class="more-btn">Show ${b.length-oe.length} more</button>`,L.firstChild.onclick=()=>{Wt.add(x),H()},le.appendChild(L)}else if(Wt.has(x)&&b.length>mn+1){let L=document.createElement("li");L.innerHTML='<button class="more-btn">Show less</button>',L.firstChild.onclick=()=>{Wt.delete(x),H()},le.appendChild(L)}if(!b.length&&x>=1&&x<=3&&n.length&&!$e&&!v&&!Z)le.innerHTML=`<li class="hint">${mo[x]}</li>`;if(w&&!b.length&&!$e&&!v)le.innerHTML=`<li class="hint">${je?"Nothing at this severity":"What finished runs left undone shows here"}</li>`;d.appendChild(le),a.appendChild(d)});let g=i.S.cards.filter((y)=>y.kind==="run"&&y.status==="waiting"&&!y.archived).length;if(document.title=g?`(${g}) kaizen`:"kaizen",de(!1),e&&document.activeElement===document.body)$n(e)?.focus({preventScroll:!0})}function gs(){let e=s("bq");_e(e,"bq"),vn=e.value,e.addEventListener("input",()=>{vn=e.value,H()}),e.addEventListener("keydown",(t)=>{if(t.key==="Escape")t.stopPropagation(),e.value="",e.dispatchEvent(new Event("input")),e.blur()})}function bs(){let e=s("fbtn"),t=document.querySelector(".ftr"),n=(a)=>{if(t.classList.toggle("on",a),e.setAttribute("aria-expanded",a),a)t.style.top=e.getBoundingClientRect().bottom+6+"px"};e.onclick=()=>n(!t.classList.contains("on")),document.addEventListener("pointerdown",(a)=>{if(t.classList.contains("on")&&!t.contains(a.target)&&!e.contains(a.target))n(!1)}),document.addEventListener("keydown",(a)=>a.key==="Escape"&&t.classList.contains("on")&&n(!1))}var wn=()=>document.activeElement?.closest?.("#board .card")?.dataset.k,$n=(e)=>e&&s("board").querySelector('.card[data-k="'+CSS.escape(e)+'"]');function ys(e,t){let n=[...s("board").querySelectorAll("section")].map((u)=>[...u.querySelectorAll(".card")]).filter((u)=>u.length);if(!n.length)return;let a=document.activeElement.closest(".card"),r=n.findIndex((u)=>u.includes(a)),c=0;if(r<0)r=0;else if(c=n[r].indexOf(a),t)c=Math.max(0,Math.min(n[r].length-1,c+t));else r=Math.max(0,Math.min(n.length-1,r+e)),c=Math.min(c,n[r].length-1);let m=n[r][c];m.focus({preventScroll:!0}),m.scrollIntoView({block:"nearest",inline:"nearest"})}var ks=()=>i.dir?{"x-kz-dir":i.dir}:{};async function K(e){let t;try{t=await fetch("/state"+(i.all?"?all=1":""),{headers:ks()})}catch{C(ws);return}let n=i.openKey&&i.S.cards.find((a)=>j(a)===i.openKey);i.S=await t.json();{let a=new Map;for(let r of i.S.cards){let c=j(r),m=a.get(c)||0;a.set(c,m+1),r._k=m?c+"#"+m:c}}for(let a of i.S.cards){if(a.kind!=="run"||a.archived)continue;if(due(j(a),a.notice,a.status==="done",i.S.now))_n(a.notice)}if(!(e&&i.mode&&s("board").querySelector(".inl input")))H();if(cs())be();if(i.wantTerm&&!i.openKey&&!i.mode){let a=i.S.cards.find((r)=>j(r)===i.wantTerm&&r.pty);if(a){i.wantTerm=null,we(a);return}}if(i.openKey){let a=i.S.cards.find((c)=>j(c)===i.openKey),r=s("pbody").querySelector(".notes textarea");if(r&&document.activeElement===r)return;if(!a){if(n&&n.kind==="idea")ho(n);return}if(i.runDoc)te(a,!0,!0);else if(a.kind==="idea")xn(a)}}async function ho(e){let t=j(e),n=(r)=>r.toLowerCase().replace(/\s+/g," ").trim(),a=n(e.text);for(let r of i.S.cards.filter((c)=>c.kind==="run"&&c.state===e.state).sort((c,m)=>(m.moved??0)-(c.moved??0)).slice(0,5)){let c=await fetch(`/run/${encodeURIComponent(r.id)}?dir=${encodeURIComponent(r.state)}`).then((m)=>m.json()).catch(()=>null);if(i.openKey!==t)return;if(n(c?.request||"").includes(a)){we(r);return}}if(i.openKey===t)ee()}var ws="The board server is not running — start it with kaizen web (or the shortcut), then reload.";async function A(e,t){let n;try{n=await fetch(e,{method:"POST",headers:{"content-type":"application/json",...ks()},body:JSON.stringify(t)})}catch{return C(ws),{ok:!1}}let a=await n.json().catch(()=>({}));if(!n.ok){let r=a.error||a.why||"That did not work";if(C(r,{title:r.slice(0,80),what:`POST ${e} failed (HTTP ${n.status}): ${r}`}),a.manual)console.log("run manually:",a.manual)}return a}var Qt=null,Vt=null,$s=null,We=(e)=>e.pty?"running in its card":"opened in your terminal",dn=()=>$s||=new Promise((e,t)=>{let n=document.createElement("link");n.rel="stylesheet",n.href="/term.css",document.head.append(n);let a=document.createElement("script");a.src="/term.js",a.onload=()=>e(window.kzTerm),a.onerror=()=>{$s=null,t()},document.head.append(a)});function Ee(){if(Vt)Vt();Vt=null,Qt=null,s("panel").classList.remove("wide")}async function Sn(e){let t=e.pty.key;if(Qt===t&&s("pbody").querySelector(".term"))return;Ee(),Qt=t,s("panel").classList.add("wide"),s("pbody").innerHTML='<div class="term" aria-label="Agent terminal"></div>';let n;try{n=await dn()}catch{s("pbody").innerHTML='<p style="color:var(--dim)">The terminal could not load.</p>';return}if(Qt!==t)return;Vt=n(s("pbody").querySelector(".term"),t,()=>K(!0))}var xs=/\u200b/g;function Ss(e){let t=(c)=>{if(c.nodeType===3)return c.textContent.replace(xs,"").replace(/\u00a0/g," ").replace(/\n/g,`
28
28
  `);if(c.nodeType!==1)return"";let m=c.nodeName.toLowerCase(),u=()=>[...c.childNodes].map(t).join("");if(m==="br")return`
29
29
  `;if(m==="b"||m==="strong")return((h)=>h.trim()?`**${h}**`:h)(u());if(m==="i"||m==="em")return((h)=>h.trim()?`*${h}*`:h)(u());if(m==="code")return((h)=>h?"`"+h+"`":h)(c.textContent.replace(xs,""));if(m==="img")return`![${c.getAttribute("alt")||""}](${c.getAttribute("src")})`;if(m==="a"){let h=[...c.childNodes].find((p)=>p.nodeName.toLowerCase()==="img");return h?t(h):`[${u()}](${c.getAttribute("href")})`}return u()},n=[],a="",r=()=>{if(a.trim())n.push(a.replace(/^\n+|\s*\n+$/g,""));a=""};for(let c of e.childNodes){let m=c.nodeType===1?c.nodeName.toLowerCase():"";if(/^h[1-6]$/.test(m))r(),n.push("#".repeat(+m[1])+" "+t(c).trim());else if(m==="ul"||m==="ol"){r();let u=[...c.childNodes].filter((h)=>h.nodeName.toLowerCase()==="li");n.push(u.map((h,p)=>(m==="ol"?`${p+1}. `:"- ")+t(h).trim()).join(`
30
30
  `))}else if(m==="pre")r(),n.push("```\n"+c.textContent+"\n```");else if(m==="p"||m==="div")r(),a=Ss(c),r();else a+=t(c)}return r(),n.join(`
@@ -44,7 +44,7 @@ ${d.body}`).join(`
44
44
  `)))}
45
45
  </section>`:"")+f`<div class="chat">
46
46
  ${r.map((d)=>f`<div class="msg">${d.at?f`<time>${d.at}</time>`:""}${_(ke(a(vo(d.text))))}</div>`)}${r.length?"":f`<p style="color:var(--dim)">No notes yet.</p>`}
47
- </div>`;if(!n)m+='<div class="notes"><textarea placeholder="Add a note. Paste a screenshot, or attach files."></textarea><div class="att" id="natt"></div><div class="tools"><input type="file" id="nfile" multiple hidden><button class="btn ghost" id="npick">Attach</button><span>Enter sends, Shift+Enter newline</span><button class="btn pri" id="nsend">Send</button></div></div>';if(s("pbody").innerHTML=_(m),n)return;let u=s("pbody").querySelector("textarea"),h=[];_e(u,"n:"+j(e));let p=Ls(u);if(Ln===j(e))Ln=null,p.focus();{let d=Fe([u]);if(d)s("npick").before(d)}let g=()=>{s("natt").textContent=h.map((d)=>d.name).join(" · ")},y=(d)=>{h.push(d),g()};s("npick").onclick=()=>s("nfile").click(),s("nfile").onchange=(d)=>{[...d.target.files].forEach(y),d.target.value=""},p.onpaste=(d)=>{for(let v of d.clipboardData.items)if(v.kind==="file"){let w=v.getAsFile();y(new File([w],w.name==="image.png"?`paste-${Date.now()}.png`:w.name,{type:w.type})),d.preventDefault()}};let x=async()=>{de(!0);let d=u.value.trim();if(!d&&!h.length)return;let v=await Promise.all(h.map((b)=>new Promise((M)=>{let R=new FileReader;R.onload=()=>M({name:b.name,data:R.result}),R.readAsDataURL(b)}))),w=e.kind==="run"?{dir:e.state,id:e.id,text:d,files:v}:{dir:e.state,for:e.text,text:d,files:v};if((await A("/note",w)).ok){h.length=0,u.value="";try{sessionStorage.removeItem(Bn+"n:"+j(e))}catch(b){}C("Noted"),Ln=j(e),p.blur(),I()}};s("nsend").onclick=x,p.onkeydown=(d)=>{if(d.key==="Enter"&&!d.shiftKey)d.preventDefault(),x()},s("pbody").scrollTop=0}var qs="kz-sent",De=new Set,Cn=new Set((()=>{try{return JSON.parse(sessionStorage.getItem(qs))||[]}catch(e){return[]}})());function Cs(e){let t=(g)=>e.id+":"+g,n=(g)=>Cn.has(t(g.n)),a=i.runDoc.findings||[],r=a.filter((g)=>!g.done&&!n(g));if(e.archived||!a.some((g)=>!g.done)||!(e.status==="done"||e.awaiting==="findings"||e.awaiting==="approvals.review"))return;let c=document.createElement("div");if(c.className="inl fix",c.innerHTML=f`<div class="q">Fix findings</div><div class="q dim">Opens your agent in this project, told to fix the ticked findings and recheck them.</div>${e.status==="waiting"?f`<div class="q dim">A session is already waiting on this run in a terminal; this opens another one.</div>`:""}${a.map((g)=>{let y=g.done?" (closed)":n(g)?" (sent)":"";return f`<label class="${g.done?"off done":y?"off":""}"><input type="checkbox" data-n="${g.n}" ${y?"checked disabled":De.has(t(g.n))?"":"checked"}><span>${g.n}. ${_(ke(g.text).replace(/^<p>|<\/p>$/g,""))}${y}</span></label>`})}${r.length?f`<div class="row"><button class="btn ghost" data-a="all"></button><button class="btn pri" data-a="s"></button></div>`:""}`,!r.length)return s("pbody").appendChild(c);let m=c.querySelector("[data-a=s]"),u=c.querySelector("[data-a=all]"),h=()=>r.filter((g)=>!De.has(t(g.n))).map((g)=>g.n),p=()=>{let g=h().length;m.textContent=g===r.length?"Fix all "+g:"Fix "+g+" selected",m.disabled=!g,u.textContent=g===r.length?"Select none":"Select all",c.querySelectorAll("input:not(:disabled)").forEach((y)=>y.checked=!De.has(t(y.dataset.n)))};c.onchange=(g)=>{let y=t(g.target.dataset.n);g.target.checked?De.delete(y):De.add(y),p()},u.onclick=()=>{let g=h().length===r.length;r.forEach((y)=>g?De.add(t(y.n)):De.delete(t(y.n))),p()},m.onclick=async()=>{let g=h();m.disabled=!0,Se();let y=await A("/fix",{dir:e.state,id:e.id,nums:g});if(y.ok){if(C("Fixing "+g.length+" finding"+(g.length===1?"":"s")+", "+We(y)+": "+Ne(y.agent)),y.pty)i.wantTerm=j(e);g.forEach((x)=>Cn.add(t(x)));try{sessionStorage.setItem(qs,JSON.stringify([...Cn]))}catch(x){}te(e,!0)}else p()},p(),s("pbody").appendChild(c)}function En(e){let t=i.runDoc.git,n=i.runDoc.commit;if(e.archived||!t||!t.repo||!(e.status==="done"||e.awaiting==="approvals.review"))return"";if(n&&n.pushed)return f`<button class="btn" disabled title="${n.sha}">Committed &amp; pushed</button>`+(t.files.length?'<button class="btn" data-a="commit">Commit new changes</button>':t.ahead>0?'<button class="btn" data-a="push">Push</button>':"");if(t.files.length)return'<button class="btn pri" data-a="commit">Commit &amp; push</button>';if(t.ahead>0)return'<button class="btn pri" data-a="push">Push</button>';return n||i.runDoc.impl?'<button class="btn" disabled>Committed &amp; pushed</button>':""}function An(e,t){let n=i.runDoc.git,a=s("pfoot");a.innerHTML=f`<div class="inl" style="flex:1"><div class="q">Commit ${n.files.length} changed file${n.files.length===1?"":"s"} on ${n.branch||"this branch"} and push to ${n.remote}?</div><div class="q dim">Only the files this run's report names.${n.others?` ${n.others} other changed file${n.others===1?" is":"s are"} left alone.`:""}</div><pre class="files">${n.files.join(`
47
+ </div>`;if(!n)m+='<div class="notes"><textarea placeholder="Add a note. Paste a screenshot, or attach files."></textarea><div class="att" id="natt"></div><div class="tools"><input type="file" id="nfile" multiple hidden><button class="btn ghost" id="npick">Attach</button><span>Enter sends, Shift+Enter newline</span><button class="btn pri" id="nsend">Send</button></div></div>';if(s("pbody").innerHTML=_(m),n)return;let u=s("pbody").querySelector("textarea"),h=[];_e(u,"n:"+j(e));let p=Ls(u);if(Ln===j(e))Ln=null,p.focus();{let d=Fe([u]);if(d)s("npick").before(d)}let g=()=>{s("natt").textContent=h.map((d)=>d.name).join(" · ")},y=(d)=>{h.push(d),g()};s("npick").onclick=()=>s("nfile").click(),s("nfile").onchange=(d)=>{[...d.target.files].forEach(y),d.target.value=""},p.onpaste=(d)=>{for(let v of d.clipboardData.items)if(v.kind==="file"){let w=v.getAsFile();y(new File([w],w.name==="image.png"?`paste-${Date.now()}.png`:w.name,{type:w.type})),d.preventDefault()}};let x=async()=>{de(!0);let d=u.value.trim();if(!d&&!h.length)return;let v=await Promise.all(h.map((b)=>new Promise((M)=>{let R=new FileReader;R.onload=()=>M({name:b.name,data:R.result}),R.readAsDataURL(b)}))),w=e.kind==="run"?{dir:e.state,id:e.id,text:d,files:v}:{dir:e.state,for:e.text,text:d,files:v};if((await A("/note",w)).ok){h.length=0,u.value="";try{sessionStorage.removeItem(Bn+"n:"+j(e))}catch(b){}C("Noted"),Ln=j(e),p.blur(),K()}};s("nsend").onclick=x,p.onkeydown=(d)=>{if(d.key==="Enter"&&!d.shiftKey)d.preventDefault(),x()},s("pbody").scrollTop=0}var qs="kz-sent",De=new Set,Cn=new Set((()=>{try{return JSON.parse(sessionStorage.getItem(qs))||[]}catch(e){return[]}})());function Cs(e){let t=(g)=>e.id+":"+g,n=(g)=>Cn.has(t(g.n)),a=i.runDoc.findings||[],r=a.filter((g)=>!g.done&&!n(g));if(e.archived||!a.some((g)=>!g.done)||!(e.status==="done"||e.awaiting==="findings"||e.awaiting==="approvals.review"))return;let c=document.createElement("div");if(c.className="inl fix",c.innerHTML=f`<div class="q">Fix findings</div><div class="q dim">Opens your agent in this project, told to fix the ticked findings and recheck them.</div>${e.status==="waiting"?f`<div class="q dim">A session is already waiting on this run in a terminal; this opens another one.</div>`:""}${a.map((g)=>{let y=g.done?" (closed)":n(g)?" (sent)":"";return f`<label class="${g.done?"off done":y?"off":""}"><input type="checkbox" data-n="${g.n}" ${y?"checked disabled":De.has(t(g.n))?"":"checked"}><span>${g.n}. ${_(ke(g.text).replace(/^<p>|<\/p>$/g,""))}${y}</span></label>`})}${r.length?f`<div class="row"><button class="btn ghost" data-a="all"></button><button class="btn pri" data-a="s"></button></div>`:""}`,!r.length)return s("pbody").appendChild(c);let m=c.querySelector("[data-a=s]"),u=c.querySelector("[data-a=all]"),h=()=>r.filter((g)=>!De.has(t(g.n))).map((g)=>g.n),p=()=>{let g=h().length;m.textContent=g===r.length?"Fix all "+g:"Fix "+g+" selected",m.disabled=!g,u.textContent=g===r.length?"Select none":"Select all",c.querySelectorAll("input:not(:disabled)").forEach((y)=>y.checked=!De.has(t(y.dataset.n)))};c.onchange=(g)=>{let y=t(g.target.dataset.n);g.target.checked?De.delete(y):De.add(y),p()},u.onclick=()=>{let g=h().length===r.length;r.forEach((y)=>g?De.add(t(y.n)):De.delete(t(y.n))),p()},m.onclick=async()=>{let g=h();m.disabled=!0,Se();let y=await A("/fix",{dir:e.state,id:e.id,nums:g});if(y.ok){if(C("Fixing "+g.length+" finding"+(g.length===1?"":"s")+", "+We(y)+": "+Ne(y.agent)),y.pty)i.wantTerm=j(e);g.forEach((x)=>Cn.add(t(x)));try{sessionStorage.setItem(qs,JSON.stringify([...Cn]))}catch(x){}te(e,!0)}else p()},p(),s("pbody").appendChild(c)}function En(e){let t=i.runDoc.git,n=i.runDoc.commit;if(e.archived||!t||!t.repo||!(e.status==="done"||e.awaiting==="approvals.review"))return"";if(n&&n.pushed)return f`<button class="btn" disabled title="${n.sha}">Committed &amp; pushed</button>`+(t.files.length?'<button class="btn" data-a="commit">Commit new changes</button>':t.ahead>0?'<button class="btn" data-a="push">Push</button>':"");if(t.files.length)return'<button class="btn pri" data-a="commit">Commit &amp; push</button>';if(t.ahead>0)return'<button class="btn pri" data-a="push">Push</button>';return n||i.runDoc.impl?'<button class="btn" disabled>Committed &amp; pushed</button>':""}function An(e,t){let n=i.runDoc.git,a=s("pfoot");a.innerHTML=f`<div class="inl" style="flex:1"><div class="q">Commit ${n.files.length} changed file${n.files.length===1?"":"s"} on ${n.branch||"this branch"} and push to ${n.remote}?</div><div class="q dim">Only the files this run's report names.${n.others?` ${n.others} other changed file${n.others===1?" is":"s are"} left alone.`:""}</div><pre class="files">${n.files.join(`
48
48
  `)}</pre>${n.shared&&n.shared.length?f`<div class="q">These files also hold other runs' changes, and a file is committed whole:</div><pre class="files">${n.shared.join(`
49
49
  `)}</pre>`:""}<textarea aria-label="Commit message" placeholder="Describe what changed">${t||es(i.runDoc.impl)||e.title||e.text}</textarea><div class="q dim" id="clong" hidden>The first line is over 72 characters; logs will cut it short.</div><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Commit &amp; push</button></div></div>`;let r=a.querySelector("textarea"),c=a.querySelector("[data-a=s]");r.focus();let m=()=>{r.style.height="auto",r.style.height=Math.min(r.scrollHeight+2,innerHeight/2)+"px",s("clong").hidden=r.value.split(`
50
- `)[0].length<=72};r.oninput=m,m(),a.querySelector("[data-a=c]").onclick=()=>{a.innerHTML="",te(e,!0)},c.onclick=()=>{if(!r.value.trim()){C("A commit message is required");return}Tn(e,r.value,c)},r.onkeydown=(u)=>{if(u.key==="Enter"&&!u.shiftKey)u.preventDefault(),c.click()}}async function Tn(e,t,n){n.disabled=!0,n.dataset.busy=1,n.textContent="Pushing…";let a=await A("/commit",{dir:e.state,id:e.id,message:t,files:i.runDoc.git.files});if(a.ok)C("Committed & pushed "+a.sha);if(s("pfoot").innerHTML="",i.openKey!==j(e))return;if(await te(e,!1),!a.ok&&t&&i.openKey===j(e)&&i.runDoc.git.files&&i.runDoc.git.files.length)An(e,t)}var Es=[["Request","request"],["Notes","notes"],["Plan","plan"],["Backlog","backlog"],["Work","impl"],["Review","review"],["Preview","preview"]],Mn=(e)=>e.charAt(0).toUpperCase()+e.slice(1),ko=(e)=>/\s/.test(e)?e:Mn(e.replace(/^\d{4}-\d{2}-\d{2}-/,"").split(/[-_]/).map((t)=>/^(ui|ux|api|cli|ai|ci|id|url|pr|tui|ios|pwa)$/i.test(t)?t.toUpperCase():t).join(" "));function we(e,t){if(W!=="board")fe("board",!0);if(de(!0),De.clear(),i.openKey=j(e),s("panel").classList.add("open"),s("scrim").classList.add("on"),s("ptitle").textContent=e.kind==="idea"?qe(e.text).head:ko(e.title||e.text),s("ptitle").title=e.kind==="idea"?"":e.id||"",H(),s("pterm").hidden=!0,e.kind==="idea"){i.runDoc=null,s("psub").innerHTML=f`<span class="pst"><span class="st d-idea"><svg><use href="#s-idea"/></svg></span>Idea</span><span class="pst dim">${e.where.split(/[\\/]/).pop()||e.where}</span>`,s("ptabs").innerHTML="",xn(e);let n=s("pfoot");n.innerHTML=e.archived?'<button class="btn pri" data-a="restore">Restore</button>':e.status==="idea"?'<button class="btn ghost danger" data-a="delete">Delete</button><button class="btn ghost" data-a="archive">Archive</button><span class="dec"><button class="btn" data-a="reject">Reject</button><button class="btn" data-a="edit">Edit</button><button class="btn pri" data-a="run">Run</button></span>':`<span class="note">${e.pty?"Starting: the agent runs below.":"Starting: a run was launched for this idea. No terminal with your agent? Run it again."}</span><span class="dec"><button class="btn" data-a="run">Run again</button></span>`,n.querySelectorAll("[data-a]").forEach((a)=>a.onclick=()=>{let r=a.dataset.a;if(r==="archive"||r==="restore"){Bt(e,r==="archive");return}let c=j(e);ee(),i.mode=r,i.modeKey=c,H()});return}if(!t)i.lastTab=i.tab=e.status==="abandoned"?"plan":{1:"plan",2:"backlog",3:"review",4:"preview"}[e.column]||"request";if(!t&&e.pty&&e.pty.alive&&e.column>=1&&e.column<=3)i.tab="term";te(e,!1)}var it=null;function wo(e){let t=e.column===2&&i.runDoc.progress,n=it&&it.id===e.id?it:null,a=t?ns(t,n&&n.st):"";if(!a)return;it={id:e.id,st:[...t.matchAll(/^\s*-\s*(todo|doing|done):/gm)].map((c)=>c[1]),w:+/width:(\d+)%/.exec(a)[1]},s("pbody").innerHTML=f`${_(a)}${i.runDoc.backlog?f`<h3>Backlog</h3>${_(ke(i.runDoc.backlog,!0))}`:""}`;let r=s("pbody").querySelector(".pbar i");if(n&&n.w!==it.w)r.style.width=n.w+"%",r.offsetWidth,r.style.width=it.w+"%"}function xn(e){if(e.pty)Sn(e);else Ee(),qn(e,e.notes,e.archived||e.status!=="idea")}async function te(e,t,n){if(!t||!i.runDoc||i.runDoc.id!==e.id||n){let d=await fetch(`/run/${encodeURIComponent(e.id)}?dir=${encodeURIComponent(e.state)}`).then((v)=>v.json());if(t&&s("pfoot").querySelector(".inl,[data-busy],.more[open]"))return;i.runDoc=d}if(i.openKey!==j(e))return;e=i.S.cards.find((d)=>j(d)===i.openKey)||e;let a=s("pbody").querySelector(".notes textarea");if(t&&a&&document.activeElement===a)return;s("psub").innerHTML=f`<span class="pst"><span class="st d-${He[e.status]||"idea"}"><svg><use href="#s-${He[e.status]||"idea"}"/></svg></span>${Mn(Je[e.status]||e.status)}</span>`+(e.awaiting?f`<span class="pst dim">${Mn(ft(e.awaiting))}</span>`:"")+(e.parent?f`<span class="pst link" title="Filed by this run">${e.parent}</span>`:"");let r=Es.filter((d)=>d[1]==="request"||d[1]==="notes"||(d[1]==="preview"?jt(i.runDoc.impl):i.runDoc[d[1]]||d[1]==="backlog"&&e.column===2&&i.runDoc.progress));if(e.pty&&i.wantTerm===j(e))i.wantTerm=null,i.tab="term";if(i.tab==="term"&&!e.pty)i.tab=i.lastTab;if(i.tab!=="term"&&!r.some((d)=>d[1]===i.tab))i.tab=[...r].reverse().find((d)=>d[1]!=="notes")[1];if(i.tab!=="term")i.lastTab=i.tab;s("panel").classList.toggle("wide",i.tab==="term");let c=s("pterm");c.hidden=!e.pty,c.classList.toggle("on",i.tab==="term"),c.setAttribute("aria-pressed",i.tab==="term"),c.title=c.ariaLabel=i.tab==="term"?"Close terminal":"Terminal",c.onclick=()=>{i.tab=i.tab==="term"?i.lastTab:"term",te(e,!0)},s("ptabs").innerHTML=r.map((d)=>f`<button class="${d[1]===i.tab?"on":""}" data-k="${d[1]}">${d[0]}</button>`).join(""),[...s("ptabs").children].forEach((d)=>d.onclick=()=>{i.tab=d.dataset.k,te(e,!0)});let m=i.tab,u=m==="preview"?jt(i.runDoc.impl)&&Dt(jt(i.runDoc.impl),e):i.runDoc[m];if(m!=="term")Ee();if(m==="term")Sn(e);else if(m==="notes"){let d=e.archived||e.status==="done"||e.status==="abandoned";qn(e,u,d)}else s("pbody").innerHTML=u?_(ke(u,m==="backlog")):f`<p style="color:var(--dim)">No ${{request:"request",plan:"plan",impl:"work report",preview:"preview",review:"review",backlog:"backlog"}[m]} yet.</p>`;if(m==="backlog")wo(e);if(m==="review")Cs(e);if(m==="notes"&&i.runDoc.linkedFrom&&i.runDoc.linkedFrom.length)s("pbody").insertAdjacentHTML("afterbegin",f`<h3>Linked from notes</h3><ul class="lnk">${i.runDoc.linkedFrom.map((d)=>f`<li><a href="/notebook?dir=${encodeURIComponent(e.state)}&amp;note=${encodeURIComponent(d)}">${d}</a></li>`)}</ul>`);if(Qe&&!t)if(Qe.k===j(e))$o(Qe);else Qe=null;let h=s("pfoot");if(t&&h.querySelector(".inl,[data-busy],.more[open]"))return;let p=En(e)+(e.archived?'<button class="btn pri" data-a="restore">Restore</button>':'<button class="btn ghost" data-a="archive">Archive</button>'),g=e.status==="waiting"&&e.awaiting==="approvals.plan"?ts(i.runDoc.plan):[],y=g||[],x=e.pty&&!e.pty.alive?'<button class="btn" data-a="resume">Resume session</button>':"";if(e.archived)h.innerHTML=_(p);else if((e.status==="waiting"||e.awaiting==="findings")&&on[e.awaiting]){let d=Xe.has(et(e)),v=!g?" It has open questions the board cannot show: answer them in the terminal running this run, or approve and your agent asks them.":y.length?` It asks ${y.length===1?"one question":y.length+" questions"}: Approve opens ${y.length===1?"it":"them"}.`:"",w=En(e).replace(/class="btn( pri)?"/g,"")+'<button data-a="archive">Archive</button><hr><button data-a="ab" class="danger">Abandon</button>';h.innerHTML=f`<span class="note">${d?"Sent to your agent.":on[e.awaiting]+v+" Answering here opens a new agent session."}</span><details class="more"><summary class="btn ghost">More</summary><div class="menu-pop">${_(w)}</div></details>${_(x)}<span class="dec"><button class="btn" data-a="revise"${d?" disabled":""}>Revise</button><button class="btn pri" data-a="approve"${d?" disabled":""}>${e.awaiting==="findings"?"Approve as-is":"Approve"}</button></span>`}else if(e.status==="waiting")h.innerHTML=f`<span class="note">Waiting on you: ${ft(e.awaiting)}. Answer in the ${e.pty&&e.pty.alive?"terminal (button at the top)":"terminal running this run"}.</span>${_(p)}<button class="btn ghost" data-a="ab">Abandon</button>${_(x)}`;else if(e.status==="done")h.innerHTML=_(p+x);else if(e.status==="abandoned")h.innerHTML=_(p);else h.innerHTML=f`<span class="note">${e.pty&&!e.pty.alive?"Its agent ended before the stage finished. Resume continues it here.":e.status==="stalled"?"No progress for a while. Resume it, or abandon it.":e.pty?"":"Running in your agent."}</span>${_(p)}<button class="btn ghost" data-a="ab">Abandon run</button>${Pt(e)?f`<button class="btn pri" data-a="resume">Resume</button>`:""}`;h.querySelectorAll("[data-a]").forEach((d)=>d.onclick=()=>{let v=d.dataset.a;if(v==="archive"||v==="restore"){Bt(e,v==="archive");return}if(v==="push"){Tn(e,"",d);return}if(v==="commit"){An(e);return}if(v==="approve"){if(y.length)is(e,y);else Rt(e,d);return}if(v==="revise"){as(e);return}if(v==="resume"){Ze(e,d);return}ee(),i.mode="reject",i.modeKey=j(e),H()})}var Qe=null;function As(){let e=new URLSearchParams(location.search),t=e.get("dir"),n=e.get("run"),a=e.get("idea");if(!n&&a===null)return;history.replaceState(null,"",location.pathname+"?all=1");let r=i.S.cards.find((h)=>h.state===t&&(n?h.kind==="run"&&h.id===n:h.kind==="idea"&&h.text===a));if(!r){C(n?"That run is not on this board":"That idea is not on this board");return}let c=/^(finding|backlog)-(\d+)$/.exec(e.get("item")||""),m=e.get("tab")||(c?c[1]==="finding"?"review":"backlog":""),u=Es.find((h)=>h[1]===(m==="work"?"impl":m));if(u)i.tab=u[1];Qe=c?{type:c[1],n:+c[2],k:j(r)}:null,we(r,!!u)}function vs(e,t,n){i.tab=t==="finding"?"review":"backlog",Qe={type:t,n,k:j(e)},we(e,!0)}function $o(e){Qe=null;let t=(m)=>String(m).replace(/!?\[([^\]]*)\]\([^)\s]+\)/g,"$1").replace(/[`*]/g,"").replace(/\s+/g," ").trim(),n=[...(i.runDoc.backlog||"").matchAll(/^\s*-\s*(?:open|done|rejected):\s*(.*)$/gm)],a=e.type==="finding"?(i.runDoc.findings||[]).find((m)=>m.n===e.n)?.text:n[e.n-1]?.[1];if(!a){C("That item is not in this run");return}let r=t(a).slice(0,60),c=[...s("pbody").querySelectorAll("li,p")].find((m)=>!m.closest(".prog")&&t(m.textContent).includes(r));if(!c)return;c.scrollIntoView({block:"center"}),c.classList.add("hit"),setTimeout(()=>c.classList.remove("hit"),2400)}async function Bt(e,t){if((await A("/archive",e.kind==="idea"?{dir:e.state,text:e.text,archived:t}:{dir:e.state,id:e.id,archived:t})).ok)C(t?"Archived":"Restored");ee(),I()}function ee(){if(W!=="board")return;let e=i.openKey;if(de(!0),Ee(),i.openKey=null,i.runDoc=null,s("panel").classList.remove("open"),s("scrim").classList.remove("on"),H(),e)$n(e)?.focus({preventScroll:!0})}function Ts(){document.addEventListener("click",(e)=>{for(let t of document.querySelectorAll(".more[open]"))if(!t.contains(e.target))t.open=!1}),s("pclose").onclick=ee,s("scrim").onclick=ee}function Ms(){s("kbd").onclick=()=>s("help").classList.add("on"),s("help").onclick=()=>s("help").classList.remove("on"),document.addEventListener("click",()=>{if(i.menuKey||i.clearMenu||i.sevMenu)i.menuKey=null,i.clearMenu=!1,i.sevMenu=!1,H()}),document.onkeydown=(e)=>{if(W==="sources"&&It&&Y()&&!s("help").classList.contains("on")&&!e.target.matches("input:not([type=checkbox]),select,textarea,[contenteditable]")&&!e.target.closest("#src-sec")&&It(e))return;if(e.target.matches("input,select,textarea,[contenteditable]")||e.target.closest("dialog"))return;if(e.key==="?"){s("help").classList.toggle("on");return}if(e.key==="/"&&s("sq")&&s("panel").classList.contains("open")){e.preventDefault(),s("sq").focus();return}if(e.key==="/"&&W==="board"&&s("bq")){e.preventDefault(),s("bq").focus();return}if(e.key==="Escape"){if(s("help").classList.remove("on"),i.mode)i.mode=null,H();else ee();return}if(W==="terminal")return;if(e.key==="n"&&!document.querySelector("dialog[open]")){if(W!=="board")fe("board",!0);vt()}if(e.key==="a")i.all=!i.all,Oe(i.all?null:i.S.dir),I();let t={ArrowUp:[0,-1],k:[0,-1],ArrowDown:[0,1],j:[0,1],ArrowLeft:[-1,0],h:[-1,0],ArrowRight:[1,0],l:[1,0]}[e.key],n=t&&i.openKey&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&i.S.cards.find((c)=>j(c)===i.openKey&&c.kind==="run");if(n&&s("ptabs").children.length){if(e.preventDefault(),t[0]){let c=[...s("ptabs").children].map((h)=>h.dataset.k).concat(s("pterm").hidden?[]:["term"]),m=c.indexOf(i.tab),u=c[m<0?t[0]>0?0:c.length-1:Math.max(0,Math.min(c.length-1,m+t[0]))];if(u!==i.tab)i.tab=u,te(n,!0)}else s("pbody").scrollBy({top:t[1]*40});return}if(t&&!s("panel").classList.contains("open")&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.target===document.body||e.target.matches("#board .card"))){e.preventDefault(),ys(...t);return}let a=i.openKey||wn(),r=a&&i.S.cards.find((c)=>j(c)===a);if(!r)return;if(e.key==="e"&&r.kind==="idea")ee(),i.mode="edit",i.modeKey=j(r),H();if(e.key==="x"&&!["done","abandoned","starting"].includes(r.status))ee(),i.mode="reject",i.modeKey=j(r),H();if(e.key==="r"&&r.kind==="idea")ee(),i.mode="run",i.modeKey=j(r),H()}}var jn=null;function js(){let e=new EventSource("/events");e.onmessage=(t)=>{if(t.data==="changed"){if(I(!0),Y()&&!Kt&&!s("src-go")?.disabled&&!s("src-add")?.disabled)z()}else if(t.data==="reload")location.reload();else if(t.data.startsWith("hello ")){let n=t.data.slice(6);if(jn&&n!==jn)location.reload();jn=n}},e.onerror=()=>{e.close(),setTimeout(js,3000)}}var rt=((e)=>e==="icebox"?"sources":e)(new URLSearchParams(location.search).get("view"));function Ds(){addEventListener("focus",()=>I(!0)),un(["settings","sources","terminal"].includes(rt)?rt:"board"),document.body.classList.toggle("paged",["settings","sources","terminal"].includes(rt)),document.body.classList.toggle("ice",rt==="sources"),document.body.classList.toggle("termv",rt==="terminal"),(async()=>{let e;try{e=localStorage.getItem("kz-proj")}catch(t){}if(e&&!fn)try{if((await fetch("/cd",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({dir:e})})).ok)i.all=!1,i.dir=e;else Oe()}catch(t){}try{let t=JSON.parse(sessionStorage.getItem("kz-form"));if(t)[i.mode,i.modeKey]=t}catch(t){}I().then(()=>{fe(rt),As()}),js()})()}var xo=/\.(png|jpe?g|gif|webp|svg|bmp|avif|ico)$/i;function Ps(){let e=document.createElement("dialog");e.className="kz viewer",document.body.append(e),e.addEventListener("click",(t)=>t.target===e&&e.close()),e.addEventListener("close",()=>e.replaceChildren()),document.addEventListener("click",(t)=>{if(t.defaultPrevented||t.button!==0||t.ctrlKey||t.metaKey||t.shiftKey||t.altKey)return;let a=t.target.closest?.("a[href]")?.getAttribute("href")??"";if(!/^\/(file|ice-image)\?/.test(a))return;t.preventDefault();let r=new URLSearchParams(a.split("?")[1]).get("p")?.split("/").pop()??"Image",c=document.createElement("div");c.className="viewer-hd";let m=document.createElement("span");m.textContent=r;let u=document.createElement("a");u.href=a,u.target="_blank",u.rel="noopener",u.textContent="Open in new tab";let h=document.createElement("button");h.className="btn sm",h.textContent="Close",h.onclick=()=>e.close(),c.append(m,u,h);let p=a.startsWith("/ice-image")||xo.test(r)?document.createElement("img"):document.createElement("iframe");if(p.src=a,p instanceof HTMLImageElement)p.alt=r,p.referrerPolicy="no-referrer";e.replaceChildren(c,p),e.showModal()})}Ts();ds();fs();os();Ms();Ds();Ps();gs();bs();document.addEventListener("pointerdown",(e)=>{let t=e.target;for(let n of document.querySelectorAll(".menu-pop:not([hidden])")){let a=n.closest(".combo,details")||n.parentElement;if(!a||a.contains(t))continue;if(a instanceof HTMLDetailsElement)a.open=!1;else if(a.classList.contains("combo"))n.hidden=!0,a.querySelector("[aria-expanded]")?.setAttribute("aria-expanded","false");else n.remove(),a.classList.remove("menu"),i.menuKey=null,i.clearMenu=!1,i.sevMenu=!1}},!0);})();
50
+ `)[0].length<=72};r.oninput=m,m(),a.querySelector("[data-a=c]").onclick=()=>{a.innerHTML="",te(e,!0)},c.onclick=()=>{if(!r.value.trim()){C("A commit message is required");return}Tn(e,r.value,c)},r.onkeydown=(u)=>{if(u.key==="Enter"&&!u.shiftKey)u.preventDefault(),c.click()}}async function Tn(e,t,n){n.disabled=!0,n.dataset.busy=1,n.textContent="Pushing…";let a=await A("/commit",{dir:e.state,id:e.id,message:t,files:i.runDoc.git.files});if(a.ok)C("Committed & pushed "+a.sha);if(s("pfoot").innerHTML="",i.openKey!==j(e))return;if(await te(e,!1),!a.ok&&t&&i.openKey===j(e)&&i.runDoc.git.files&&i.runDoc.git.files.length)An(e,t)}var Es=[["Request","request"],["Notes","notes"],["Plan","plan"],["Backlog","backlog"],["Work","impl"],["Review","review"],["Preview","preview"]],Mn=(e)=>e.charAt(0).toUpperCase()+e.slice(1),ko=(e)=>/\s/.test(e)?e:Mn(e.replace(/^\d{4}-\d{2}-\d{2}-/,"").split(/[-_]/).map((t)=>/^(ui|ux|api|cli|ai|ci|id|url|pr|tui|ios|pwa)$/i.test(t)?t.toUpperCase():t).join(" "));function we(e,t){if(W!=="board")fe("board",!0);if(de(!0),De.clear(),i.openKey=j(e),s("panel").classList.add("open"),s("scrim").classList.add("on"),s("ptitle").textContent=e.kind==="idea"?qe(e.text).head:ko(e.title||e.text),s("ptitle").title=e.kind==="idea"?"":e.id||"",H(),s("pterm").hidden=!0,e.kind==="idea"){i.runDoc=null,s("psub").innerHTML=f`<span class="pst"><span class="st d-idea"><svg><use href="#s-idea"/></svg></span>Idea</span><span class="pst dim">${e.where.split(/[\\/]/).pop()||e.where}</span>`,s("ptabs").innerHTML="",xn(e);let n=s("pfoot");n.innerHTML=e.archived?'<button class="btn pri" data-a="restore">Restore</button>':e.status==="idea"?'<button class="btn ghost danger" data-a="delete">Delete</button><button class="btn ghost" data-a="archive">Archive</button><span class="dec"><button class="btn" data-a="reject">Reject</button><button class="btn" data-a="edit">Edit</button><button class="btn pri" data-a="run">Run</button></span>':`<span class="note">${e.pty?"Starting: the agent runs below.":"Starting: a run was launched for this idea. No terminal with your agent? Run it again."}</span><span class="dec"><button class="btn" data-a="run">Run again</button></span>`,n.querySelectorAll("[data-a]").forEach((a)=>a.onclick=()=>{let r=a.dataset.a;if(r==="archive"||r==="restore"){Bt(e,r==="archive");return}let c=j(e);ee(),i.mode=r,i.modeKey=c,H()});return}if(!t)i.lastTab=i.tab=e.status==="abandoned"?"plan":{1:"plan",2:"backlog",3:"review",4:"preview"}[e.column]||"request";if(!t&&e.pty&&e.pty.alive&&e.column>=1&&e.column<=3)i.tab="term";te(e,!1)}var it=null;function wo(e){let t=e.column===2&&i.runDoc.progress,n=it&&it.id===e.id?it:null,a=t?ns(t,n&&n.st):"";if(!a)return;it={id:e.id,st:[...t.matchAll(/^\s*-\s*(todo|doing|done):/gm)].map((c)=>c[1]),w:+/width:(\d+)%/.exec(a)[1]},s("pbody").innerHTML=f`${_(a)}${i.runDoc.backlog?f`<h3>Backlog</h3>${_(ke(i.runDoc.backlog,!0))}`:""}`;let r=s("pbody").querySelector(".pbar i");if(n&&n.w!==it.w)r.style.width=n.w+"%",r.offsetWidth,r.style.width=it.w+"%"}function xn(e){if(e.pty)Sn(e);else Ee(),qn(e,e.notes,e.archived||e.status!=="idea")}async function te(e,t,n){if(!t||!i.runDoc||i.runDoc.id!==e.id||n){let d=await fetch(`/run/${encodeURIComponent(e.id)}?dir=${encodeURIComponent(e.state)}`).then((v)=>v.json());if(t&&s("pfoot").querySelector(".inl,[data-busy],.more[open]"))return;i.runDoc=d}if(i.openKey!==j(e))return;e=i.S.cards.find((d)=>j(d)===i.openKey)||e;let a=s("pbody").querySelector(".notes textarea");if(t&&a&&document.activeElement===a)return;s("psub").innerHTML=f`<span class="pst"><span class="st d-${He[e.status]||"idea"}"><svg><use href="#s-${He[e.status]||"idea"}"/></svg></span>${Mn(Je[e.status]||e.status)}</span>`+(e.awaiting?f`<span class="pst dim">${Mn(ft(e.awaiting))}</span>`:"")+(e.parent?f`<span class="pst link" title="Filed by this run">${e.parent}</span>`:"");let r=Es.filter((d)=>d[1]==="request"||d[1]==="notes"||(d[1]==="preview"?jt(i.runDoc.impl):i.runDoc[d[1]]||d[1]==="backlog"&&e.column===2&&i.runDoc.progress));if(e.pty&&i.wantTerm===j(e))i.wantTerm=null,i.tab="term";if(i.tab==="term"&&!e.pty)i.tab=i.lastTab;if(i.tab!=="term"&&!r.some((d)=>d[1]===i.tab))i.tab=[...r].reverse().find((d)=>d[1]!=="notes")[1];if(i.tab!=="term")i.lastTab=i.tab;s("panel").classList.toggle("wide",i.tab==="term");let c=s("pterm");c.hidden=!e.pty,c.classList.toggle("on",i.tab==="term"),c.setAttribute("aria-pressed",i.tab==="term"),c.title=c.ariaLabel=i.tab==="term"?"Close terminal":"Terminal",c.onclick=()=>{i.tab=i.tab==="term"?i.lastTab:"term",te(e,!0)},s("ptabs").innerHTML=r.map((d)=>f`<button class="${d[1]===i.tab?"on":""}" data-k="${d[1]}">${d[0]}</button>`).join(""),[...s("ptabs").children].forEach((d)=>d.onclick=()=>{i.tab=d.dataset.k,te(e,!0)});let m=i.tab,u=m==="preview"?jt(i.runDoc.impl)&&Dt(jt(i.runDoc.impl),e):i.runDoc[m];if(m!=="term")Ee();if(m==="term")Sn(e);else if(m==="notes"){let d=e.archived||e.status==="done"||e.status==="abandoned";qn(e,u,d)}else s("pbody").innerHTML=u?_(ke(u,m==="backlog")):f`<p style="color:var(--dim)">No ${{request:"request",plan:"plan",impl:"work report",preview:"preview",review:"review",backlog:"backlog"}[m]} yet.</p>`;if(m==="backlog")wo(e);if(m==="review")Cs(e);if(m==="notes"&&i.runDoc.linkedFrom&&i.runDoc.linkedFrom.length)s("pbody").insertAdjacentHTML("afterbegin",f`<h3>Linked from notes</h3><ul class="lnk">${i.runDoc.linkedFrom.map((d)=>f`<li><a href="/notebook?dir=${encodeURIComponent(e.state)}&amp;note=${encodeURIComponent(d)}">${d}</a></li>`)}</ul>`);if(Qe&&!t)if(Qe.k===j(e))$o(Qe);else Qe=null;let h=s("pfoot");if(t&&h.querySelector(".inl,[data-busy],.more[open]"))return;let p=En(e)+(e.archived?'<button class="btn pri" data-a="restore">Restore</button>':'<button class="btn ghost" data-a="archive">Archive</button>'),g=e.status==="waiting"&&e.awaiting==="approvals.plan"?ts(i.runDoc.plan):[],y=g||[],x=e.pty&&!e.pty.alive?'<button class="btn" data-a="resume">Resume session</button>':"";if(e.archived)h.innerHTML=_(p);else if((e.status==="waiting"||e.awaiting==="findings")&&on[e.awaiting]){let d=Xe.has(et(e)),v=!g?" It has open questions the board cannot show: answer them in the terminal running this run, or approve and your agent asks them.":y.length?` It asks ${y.length===1?"one question":y.length+" questions"}: Approve opens ${y.length===1?"it":"them"}.`:"",w=En(e).replace(/class="btn( pri)?"/g,"")+'<button data-a="archive">Archive</button><hr><button data-a="ab" class="danger">Abandon</button>';h.innerHTML=f`<span class="note">${d?"Sent to your agent.":on[e.awaiting]+v+" Answering here opens a new agent session."}</span><details class="more"><summary class="btn ghost">More</summary><div class="menu-pop">${_(w)}</div></details>${_(x)}<span class="dec"><button class="btn" data-a="revise"${d?" disabled":""}>Revise</button><button class="btn pri" data-a="approve"${d?" disabled":""}>${e.awaiting==="findings"?"Approve as-is":"Approve"}</button></span>`}else if(e.status==="waiting")h.innerHTML=f`<span class="note">Waiting on you: ${ft(e.awaiting)}. Answer in the ${e.pty&&e.pty.alive?"terminal (button at the top)":"terminal running this run"}.</span>${_(p)}<button class="btn ghost" data-a="ab">Abandon</button>${_(x)}`;else if(e.status==="done")h.innerHTML=_(p+x);else if(e.status==="abandoned")h.innerHTML=_(p);else h.innerHTML=f`<span class="note">${e.pty&&!e.pty.alive?"Its agent ended before the stage finished. Resume continues it here.":e.status==="stalled"?"No progress for a while. Resume it, or abandon it.":e.pty?"":"Running in your agent."}</span>${_(p)}<button class="btn ghost" data-a="ab">Abandon run</button>${Pt(e)?f`<button class="btn pri" data-a="resume">Resume</button>`:""}`;h.querySelectorAll("[data-a]").forEach((d)=>d.onclick=()=>{let v=d.dataset.a;if(v==="archive"||v==="restore"){Bt(e,v==="archive");return}if(v==="push"){Tn(e,"",d);return}if(v==="commit"){An(e);return}if(v==="approve"){if(y.length)is(e,y);else Rt(e,d);return}if(v==="revise"){as(e);return}if(v==="resume"){Ze(e,d);return}ee(),i.mode="reject",i.modeKey=j(e),H()})}var Qe=null;function As(){let e=new URLSearchParams(location.search),t=e.get("dir"),n=e.get("run"),a=e.get("idea");if(!n&&a===null)return;history.replaceState(null,"",location.pathname+"?all=1");let r=i.S.cards.find((h)=>h.state===t&&(n?h.kind==="run"&&h.id===n:h.kind==="idea"&&h.text===a));if(!r){C(n?"That run is not on this board":"That idea is not on this board");return}let c=/^(finding|backlog)-(\d+)$/.exec(e.get("item")||""),m=e.get("tab")||(c?c[1]==="finding"?"review":"backlog":""),u=Es.find((h)=>h[1]===(m==="work"?"impl":m));if(u)i.tab=u[1];Qe=c?{type:c[1],n:+c[2],k:j(r)}:null,we(r,!!u)}function vs(e,t,n){i.tab=t==="finding"?"review":"backlog",Qe={type:t,n,k:j(e)},we(e,!0)}function $o(e){Qe=null;let t=(m)=>String(m).replace(/!?\[([^\]]*)\]\([^)\s]+\)/g,"$1").replace(/[`*]/g,"").replace(/\s+/g," ").trim(),n=[...(i.runDoc.backlog||"").matchAll(/^\s*-\s*(?:open|done|rejected):\s*(.*)$/gm)],a=e.type==="finding"?(i.runDoc.findings||[]).find((m)=>m.n===e.n)?.text:n[e.n-1]?.[1];if(!a){C("That item is not in this run");return}let r=t(a).slice(0,60),c=[...s("pbody").querySelectorAll("li,p")].find((m)=>!m.closest(".prog")&&t(m.textContent).includes(r));if(!c)return;c.scrollIntoView({block:"center"}),c.classList.add("hit"),setTimeout(()=>c.classList.remove("hit"),2400)}async function Bt(e,t){if((await A("/archive",e.kind==="idea"?{dir:e.state,text:e.text,archived:t}:{dir:e.state,id:e.id,archived:t})).ok)C(t?"Archived":"Restored");ee(),K()}function ee(){if(W!=="board")return;let e=i.openKey;if(de(!0),Ee(),i.openKey=null,i.runDoc=null,s("panel").classList.remove("open"),s("scrim").classList.remove("on"),H(),e)$n(e)?.focus({preventScroll:!0})}function Ts(){document.addEventListener("click",(e)=>{for(let t of document.querySelectorAll(".more[open]"))if(!t.contains(e.target))t.open=!1}),s("pclose").onclick=ee,s("scrim").onclick=ee}function Ms(){s("kbd").onclick=()=>s("help").classList.add("on"),s("help").onclick=()=>s("help").classList.remove("on"),document.addEventListener("click",()=>{if(i.menuKey||i.clearMenu||i.sevMenu)i.menuKey=null,i.clearMenu=!1,i.sevMenu=!1,H()}),document.onkeydown=(e)=>{if(W==="sources"&&It&&Y()&&!s("help").classList.contains("on")&&!e.target.matches("input:not([type=checkbox]),select,textarea,[contenteditable]")&&!e.target.closest("#src-sec")&&It(e))return;if(e.target.matches("input,select,textarea,[contenteditable]")||e.target.closest("dialog"))return;if(e.key==="?"){s("help").classList.toggle("on");return}if(e.key==="/"&&s("sq")&&s("panel").classList.contains("open")){e.preventDefault(),s("sq").focus();return}if(e.key==="/"&&W==="board"&&s("bq")){e.preventDefault(),s("bq").focus();return}if(e.key==="Escape"){if(s("help").classList.remove("on"),i.mode)i.mode=null,H();else ee();return}if(W==="terminal")return;if(e.key==="n"&&!document.querySelector("dialog[open]")){if(W!=="board")fe("board",!0);vt()}if(e.key==="a")i.all=!i.all,Oe(i.all?null:i.S.dir),K();let t={ArrowUp:[0,-1],k:[0,-1],ArrowDown:[0,1],j:[0,1],ArrowLeft:[-1,0],h:[-1,0],ArrowRight:[1,0],l:[1,0]}[e.key],n=t&&i.openKey&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&i.S.cards.find((c)=>j(c)===i.openKey&&c.kind==="run");if(n&&s("ptabs").children.length){if(e.preventDefault(),t[0]){let c=[...s("ptabs").children].map((h)=>h.dataset.k).concat(s("pterm").hidden?[]:["term"]),m=c.indexOf(i.tab),u=c[m<0?t[0]>0?0:c.length-1:Math.max(0,Math.min(c.length-1,m+t[0]))];if(u!==i.tab)i.tab=u,te(n,!0)}else s("pbody").scrollBy({top:t[1]*40});return}if(t&&!s("panel").classList.contains("open")&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.target===document.body||e.target.matches("#board .card"))){e.preventDefault(),ys(...t);return}let a=i.openKey||wn(),r=a&&i.S.cards.find((c)=>j(c)===a);if(!r)return;if(e.key==="e"&&r.kind==="idea")ee(),i.mode="edit",i.modeKey=j(r),H();if(e.key==="x"&&!["done","abandoned","starting"].includes(r.status))ee(),i.mode="reject",i.modeKey=j(r),H();if(e.key==="r"&&r.kind==="idea")ee(),i.mode="run",i.modeKey=j(r),H()}}var jn=null;function js(){let e=new EventSource("/events");e.onmessage=(t)=>{if(t.data==="changed"){if(K(!0),Y()&&!Kt&&!s("src-go")?.disabled&&!s("src-add")?.disabled)z()}else if(t.data==="reload")location.reload();else if(t.data.startsWith("hello ")){let n=t.data.slice(6);if(jn&&n!==jn)location.reload();jn=n}},e.onerror=()=>{e.close(),setTimeout(js,3000)}}var rt=((e)=>e==="icebox"?"sources":e)(new URLSearchParams(location.search).get("view"));function Ds(){addEventListener("focus",()=>K(!0)),un(["settings","sources","terminal"].includes(rt)?rt:"board"),document.body.classList.toggle("paged",["settings","sources","terminal"].includes(rt)),document.body.classList.toggle("ice",rt==="sources"),document.body.classList.toggle("termv",rt==="terminal"),(async()=>{let e;try{e=localStorage.getItem("kz-proj")}catch(t){}if(e&&!fn)try{if((await fetch("/cd",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({dir:e})})).ok)i.all=!1,i.dir=e;else Oe()}catch(t){}try{let t=JSON.parse(sessionStorage.getItem("kz-form"));if(t)[i.mode,i.modeKey]=t}catch(t){}K().then(()=>{fe(rt),As()}),js()})()}var xo=/\.(png|jpe?g|gif|webp|svg|bmp|avif|ico)$/i;function Ps(){let e=document.createElement("dialog");e.className="kz viewer",document.body.append(e),e.addEventListener("click",(t)=>t.target===e&&e.close()),e.addEventListener("close",()=>e.replaceChildren()),document.addEventListener("click",(t)=>{if(t.defaultPrevented||t.button!==0||t.ctrlKey||t.metaKey||t.shiftKey||t.altKey)return;let a=t.target.closest?.("a[href]")?.getAttribute("href")??"";if(!/^\/(file|ice-image)\?/.test(a))return;t.preventDefault();let r=new URLSearchParams(a.split("?")[1]).get("p")?.split("/").pop()??"Image",c=document.createElement("div");c.className="viewer-hd";let m=document.createElement("span");m.textContent=r;let u=document.createElement("a");u.href=a,u.target="_blank",u.rel="noopener",u.textContent="Open in new tab";let h=document.createElement("button");h.className="btn sm",h.textContent="Close",h.onclick=()=>e.close(),c.append(m,u,h);let p=a.startsWith("/ice-image")||xo.test(r)?document.createElement("img"):document.createElement("iframe");if(p.src=a,p instanceof HTMLImageElement)p.alt=r,p.referrerPolicy="no-referrer";e.replaceChildren(c,p),e.showModal()})}Ts();ds();fs();os();Ms();Ds();Ps();gs();bs();document.addEventListener("pointerdown",(e)=>{let t=e.target;for(let n of document.querySelectorAll(".menu-pop:not([hidden])")){let a=n.closest(".combo,details")||n.parentElement;if(!a||a.contains(t))continue;if(a instanceof HTMLDetailsElement)a.open=!1;else if(a.classList.contains("combo"))n.hidden=!0,a.querySelector("[aria-expanded]")?.setAttribute("aria-expanded","false");else n.remove(),a.classList.remove("menu"),i.menuKey=null,i.clearMenu=!1,i.sevMenu=!1}},!0);})();
package/web/notebook.js CHANGED
@@ -1,4 +1,4 @@
1
- /* kaizen notebook e04c49402aa5e486 -- generated by bun run build:web from web/notebook.ts; do not edit */
1
+ /* kaizen notebook e843bd059f54f131 -- generated by bun run build:web from web/notebook.ts; do not edit */
2
2
  var Ss=[],ua=[];(()=>{let e="lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,1n,9,16,o,,x,1i,3,,i,,7,a,2,t,3,1k,,,7,2,2,2,3,9,,a,2,q,,2,3,1k,,,5,4,2,2,3,3,,u,2,3,,b,3,1k,,,8,,3,,3,k,2,m,6,,3,1k,,,7,2,2,2,3,7,3,a,2,u,,1n,5,3,3,,4,9,,14,5,1j,,,7,,3,,4,7,2,b,2,t,3,1k,,,7,,3,,4,7,2,b,2,f,,c,4,1j,2,,7,,3,,4,9,,a,2,t,3,1y,,4,6,,,,8,i,2,1p,,,8,c,8,2q,,,a,b,7,21,2,r,,,,,,4,2,1d,k,,2,5,b,,10,9,,2u,b,,6,n,4,4,3,g,4,d,,,3,6,,f,,jj,3,qa,4,s,3,t,2,u,2,1s,w,9,,19,3,,,39,2,y,,3a,c,4,c,63,5,1l,a,,,,,2,o,2,,1c,1a,2,c,k,5,1b,h,12,9,c,3,u,d,1k,e,1c,k,48,3,,l,4,,6,,2,3,5i,1s,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,n,5,4,,2b,2,1e,i,q,i,d,,12,8,p,d,18,4,1b,e,10,,1v,e,c,,8,2,1a,,1f,,,3,2,2,5,2,,,15,5,5,2,6k,8,,2,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,1t,5,8t,2,25,6,1y,b,1d,4,3e,3,1h,f,15,,2,2,a,4,19,b,7,,1p,3,10,e,g,2,18,,c,3,1c,e,8,4,,2,2k,c,6,,2,,4d,c,l,4,1j,2,,7,2,2,2,3,9,,a,2,2,7,3,5,1v,9,,,2,,,4,,5,,,e,2,2a,i,n,,29,k,6j,7,2,9,r,2,2a,h,2y,d,2t,3,2,a,74,f,6t,6,,2,2,4,,,,2,3x,7,2,7,3,,s,a,14,7,,4,8,,9,b,1a,g,5i,8,5j,8,,8,2a,m,,e,3e,6,3,,,2,,7,,,1u,5,,2,,5,9n,4,9,2,,,1c,7,3,5,n,,44l,,6,f,8ug,i,1xc,5,1n,7,t4,,,1j,7,4,29,,b,2,f57,2,3mp,1a,2,n,f2,5,3,6,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,2s,,4g,7,af,,1p,4,e4,4,72,2,6r,,2,,7,2,5,,d6,7,31,7,240,5".split(",").map((t)=>t?parseInt(t,36):1);for(let t=0,i=0;t<e.length;t++)(t%2?ua:Ss).push(i=i+e[t])})();function op(e){if(e<768)return!1;for(let t=0,i=Ss.length;;){let n=t+i>>1;if(e<Ss[n])i=n;else if(e>=ua[n])t=n+1;else return!0;if(t==i)return!1}}function ha(e){return e>=127462&&e<=127487}var fa=8205;function da(e,t,i=!0,n=!0){return(i?Oa:lp)(e,t,n)}function Oa(e,t,i){if(t==e.length)return t;if(t&&pa(e.charCodeAt(t))&&ma(e.charCodeAt(t-1)))t--;let n=bs(e,t);t+=ca(n);while(t<e.length){let r=bs(e,t);if(n==fa||r==fa||i&&op(r))t+=ca(r),n=r;else if(ha(r)){let s=0,o=t-2;while(o>=0&&ha(bs(e,o)))s++,o-=2;if(s%2==0)break;else t+=2}else break}return t}function lp(e,t,i){while(t>1){let n=Oa(e,t-2,i);if(n<t)return n;t--}return 0}function bs(e,t){let i=e.charCodeAt(t);if(!ma(i)||t+1==e.length)return i;let n=e.charCodeAt(t+1);if(!pa(n))return i;return(i-55296<<10)+(n-56320)+65536}function pa(e){return e>=56320&&e<57344}function ma(e){return e>=55296&&e<56320}function ca(e){return e<65536?1:2}class _{lineAt(e){if(e<0||e>this.length)throw RangeError(`Invalid position ${e} in document of length ${this.length}`);return this.lineInner(e,!1,1,0)}line(e){if(e<1||e>this.lines)throw RangeError(`Invalid line number ${e} in ${this.lines}-line document`);return this.lineInner(e,!0,1,0)}replace(e,t,i){[e,t]=vi(this,e,t);let n=[];if(this.decompose(0,e,n,2),i.length)i.decompose(0,i.length,n,3);return this.decompose(t,this.length,n,1),ft.from(n,this.length-(t-e)+i.length)}append(e){return this.replace(this.length,this.length,e)}slice(e,t=this.length){[e,t]=vi(this,e,t);let i=[];return this.decompose(e,t,i,0),ft.from(i,t-e)}eq(e){if(e==this)return!0;if(e.length!=this.length||e.lines!=this.lines)return!1;let t=this.scanIdentical(e,1),i=this.length-this.scanIdentical(e,-1),n=new wi(this),r=new wi(e);for(let s=t,o=t;;){if(n.next(s),r.next(s),s=0,n.lineBreak!=r.lineBreak||n.done!=r.done||n.value!=r.value)return!1;if(o+=n.value.length,n.done||o>=i)return!0}}iter(e=1){return new wi(this,e)}iterRange(e,t=this.length){return new Ts(this,e,t)}iterLines(e,t){let i;if(e==null)i=this.iter();else{if(t==null)t=this.lines+1;let n=this.line(e).from;i=this.iterRange(n,Math.max(n,t==this.lines+1?this.length:t<=1?0:this.line(t-1).to))}return new As(i)}toString(){return this.sliceString(0)}toJSON(){let e=[];return this.flatten(e),e}constructor(){}static of(e){if(e.length==0)throw RangeError("A document must have at least one line");if(e.length==1&&!e[0])return _.empty;return e.length<=32?new oe(e):ft.from(oe.split(e,[]))}}class oe extends _{constructor(e,t=ap(e)){super();this.text=e,this.length=t}get lines(){return this.text.length}get children(){return null}lineInner(e,t,i,n){for(let r=0;;r++){let s=this.text[r],o=n+s.length;if((t?i:o)>=e)return new ka(n,o,i,s);n=o+1,i++}}decompose(e,t,i,n){let r=e<=0&&t>=this.length?this:new oe(ga(this.text,e,t),Math.min(t,this.length)-Math.max(0,e));if(n&1){let s=i.pop(),o=cr(r.text,s.text.slice(),0,r.length);if(o.length<=32)i.push(new oe(o,s.length+r.length));else{let l=o.length>>1;i.push(new oe(o.slice(0,l)),new oe(o.slice(l)))}}else i.push(r)}replace(e,t,i){if(!(i instanceof oe))return super.replace(e,t,i);[e,t]=vi(this,e,t);let n=cr(this.text,cr(i.text,ga(this.text,0,e)),t),r=this.length+i.length-(t-e);if(n.length<=32)return new oe(n,r);return ft.from(oe.split(n,[]),r)}sliceString(e,t=this.length,i=`
3
3
  `){[e,t]=vi(this,e,t);let n="";for(let r=0,s=0;r<=t&&s<this.text.length;s++){let o=this.text[s],l=r+o.length;if(r>e&&s)n+=i;if(e<l&&t>r)n+=o.slice(Math.max(0,e-r),t-r);r=l+1}return n}flatten(e){for(let t of this.text)e.push(t)}scanIdentical(){return 0}static split(e,t){let i=[],n=-1;for(let r of e)if(i.push(r),n+=r.length+1,i.length==32)t.push(new oe(i,n)),i=[],n=-1;if(n>-1)t.push(new oe(i,n));return t}}class ft extends _{constructor(e,t){super();this.children=e,this.length=t,this.lines=0;for(let i of e)this.lines+=i.lines}lineInner(e,t,i,n){for(let r=0;;r++){let s=this.children[r],o=n+s.length,l=i+s.lines-1;if((t?l:o)>=e)return s.lineInner(e,t,i,n);n=o+1,i=l+1}}decompose(e,t,i,n){for(let r=0,s=0;s<=t&&r<this.children.length;r++){let o=this.children[r],l=s+o.length;if(e<=l&&t>=s){let a=n&((s<=e?1:0)|(l>=t?2:0));if(s>=e&&l<=t&&!a)i.push(o);else o.decompose(e-s,t-s,i,a)}s=l+1}}replace(e,t,i){if([e,t]=vi(this,e,t),i.lines<this.lines)for(let n=0,r=0;n<this.children.length;n++){let s=this.children[n],o=r+s.length;if(e>=r&&t<=o){let l=s.replace(e-r,t-r,i),a=this.lines-s.lines+l.lines;if(l.lines<a>>4&&l.lines>a>>6){let h=this.children.slice();return h[n]=l,new ft(h,this.length-(t-e)+i.length)}return super.replace(r,o,l)}r=o+1}return super.replace(e,t,i)}sliceString(e,t=this.length,i=`
4
4
  `){[e,t]=vi(this,e,t);let n="";for(let r=0,s=0;r<this.children.length&&s<=t;r++){let o=this.children[r],l=s+o.length;if(s>e&&r)n+=i;if(e<l&&t>s)n+=o.sliceString(e-s,t-s,i);s=l+1}return n}flatten(e){for(let t of this.children)t.flatten(e)}scanIdentical(e,t){if(!(e instanceof ft))return 0;let i=0,[n,r,s,o]=t>0?[0,0,this.children.length,e.children.length]:[this.children.length-1,e.children.length-1,-1,-1];for(;;n+=t,r+=t){if(n==s||r==o)return i;let l=this.children[n],a=e.children[r];if(l!=a)return i+l.scanIdentical(a,t);i+=l.length+1}}static from(e,t=e.reduce((i,n)=>i+n.length+1,-1)){let i=0;for(let u of e)i+=u.lines;if(i<32){let u=[];for(let d of e)d.flatten(u);return new oe(u,t)}let n=Math.max(32,i>>5),r=n<<1,s=n>>1,o=[],l=0,a=-1,h=[];function f(u){let d;if(u.lines>r&&u instanceof ft)for(let O of u.children)f(O);else if(u.lines>s&&(l>s||!l))c(),o.push(u);else if(u instanceof oe&&l&&(d=h[h.length-1])instanceof oe&&u.lines+d.lines<=32)l+=u.lines,a+=u.length+1,h[h.length-1]=new oe(d.text.concat(u.text),d.length+1+u.length);else{if(l+u.lines>n)c();l+=u.lines,a+=u.length+1,h.push(u)}}function c(){if(l==0)return;o.push(h.length==1?h[0]:ft.from(h,a)),a=-1,l=h.length=0}for(let u of e)f(u);return c(),o.length==1?o[0]:new ft(o,t)}}_.empty=new oe([""],0);function ap(e){let t=-1;for(let i of e)t+=i.length+1;return t}function cr(e,t,i=0,n=1e9){for(let r=0,s=0,o=!0;s<e.length&&r<=n;s++){let l=e[s],a=r+l.length;if(a>=i){if(a>n)l=l.slice(0,n-r);if(r<i)l=l.slice(i-r);if(o)t[t.length-1]+=l,o=!1;else t.push(l)}r=a+1}return t}function ga(e,t,i){return cr(e,[""],t,i)}class wi{constructor(e,t=1){this.dir=t,this.done=!1,this.lineBreak=!1,this.value="",this.nodes=[e],this.offsets=[t>0?1:(e instanceof oe?e.text.length:e.children.length)<<1]}nextInner(e,t){this.done=this.lineBreak=!1;for(;;){let i=this.nodes.length-1,n=this.nodes[i],r=this.offsets[i],s=r>>1,o=n instanceof oe?n.text.length:n.children.length;if(s==(t>0?o:0)){if(i==0)return this.done=!0,this.value="",this;if(t>0)this.offsets[i-1]++;this.nodes.pop(),this.offsets.pop()}else if((r&1)==(t>0?0:1)){if(this.offsets[i]+=t,e==0)return this.lineBreak=!0,this.value=`
package/web/term.js CHANGED
@@ -1,4 +1,4 @@
1
- /* kaizen terminal e04c49402aa5e486 -- generated by bun run build:web from web/term.ts; do not edit */
1
+ /* kaizen terminal e843bd059f54f131 -- generated by bun run build:web from web/term.ts; do not edit */
2
2
  (()=>{var{defineProperty:io,getOwnPropertyDescriptor:ih}=Object,sh=(e,t)=>{for(var i in t)io(e,i,{get:t[i],enumerable:!0})},re=(e,t,i,s)=>{for(var r=s>1?void 0:s?ih(t,i):t,n=e.length-1,o;n>=0;n--)(o=e[n])&&(r=(s?o(t,i,r):o(r))||r);return s&&r&&io(t,i,r),r},T=(e,t)=>(i,s)=>t(i,s,e),Jr="Terminal input",Fs={get:()=>Jr,set:(e)=>Jr=e},Qr="Too much output to announce, navigate to rows manually to read",Ws={get:()=>Qr,set:(e)=>Qr=e};function rh(e){return e.replace(/\r?\n/g,"\r")}function nh(e,t){return t?"\x1B[200~"+e+"\x1B[201~":e}function oh(e,t){e.clipboardData&&e.clipboardData.setData("text/plain",t.selectionText),e.preventDefault()}function ah(e,t,i,s){if(e.stopPropagation(),e.clipboardData){let r=e.clipboardData.getData("text/plain");so(r,t,i,s)}}function so(e,t,i,s){e=rh(e),e=nh(e,i.decPrivateModes.bracketedPasteMode&&s.rawOptions.ignoreBracketedPasteMode!==!0),i.triggerDataEvent(e,!0),t.value=""}function ro(e,t,i){let s=i.getBoundingClientRect(),r=e.clientX-s.left-10,n=e.clientY-s.top-10;t.style.width="20px",t.style.height="20px",t.style.left=`${r}px`,t.style.top=`${n}px`,t.style.zIndex="1000",t.focus()}function en(e,t,i,s,r){ro(e,t,i),r&&s.rightClickSelect(e),t.value=s.selectionText,t.select()}function Lt(e){return e>65535?(e-=65536,String.fromCharCode((e>>10)+55296)+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)}function es(e,t=0,i=e.length){let s="";for(let r=t;r<i;++r){let n=e[r];n>65535?(n-=65536,s+=String.fromCharCode((n>>10)+55296)+String.fromCharCode(n%1024+56320)):s+=String.fromCharCode(n)}return s}var hh=class{constructor(){this._interim=0}clear(){this._interim=0}decode(e,t){let i=e.length;if(!i)return 0;let s=0,r=0;if(this._interim){let n=e.charCodeAt(r++);56320<=n&&n<=57343?t[s++]=(this._interim-55296)*1024+n-56320+65536:(t[s++]=this._interim,t[s++]=n),this._interim=0}for(let n=r;n<i;++n){let o=e.charCodeAt(n);if(55296<=o&&o<=56319){if(++n>=i)return this._interim=o,s;let a=e.charCodeAt(n);56320<=a&&a<=57343?t[s++]=(o-55296)*1024+a-56320+65536:(t[s++]=o,t[s++]=a);continue}o!==65279&&(t[s++]=o)}return s}},lh=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(e,t){let i=e.length;if(!i)return 0;let s=0,r,n,o,a,l=0,h=0;if(this.interim[0]){let u=!1,_=this.interim[0];_&=(_&224)===192?31:(_&240)===224?15:7;let g=0,v;for(;(v=this.interim[++g]&63)&&g<4;)_<<=6,_|=v;let k=(this.interim[0]&224)===192?2:(this.interim[0]&240)===224?3:4,A=k-g;for(;h<A;){if(h>=i)return 0;if(v=e[h++],(v&192)!==128){h--,u=!0;break}else this.interim[g++]=v,_<<=6,_|=v&63}u||(k===2?_<128?h--:t[s++]=_:k===3?_<2048||_>=55296&&_<=57343||_===65279||(t[s++]=_):_<65536||_>1114111||(t[s++]=_)),this.interim.fill(0)}let c=i-4,d=h;for(;d<i;){for(;d<c&&!((r=e[d])&128)&&!((n=e[d+1])&128)&&!((o=e[d+2])&128)&&!((a=e[d+3])&128);)t[s++]=r,t[s++]=n,t[s++]=o,t[s++]=a,d+=4;if(r=e[d++],r<128)t[s++]=r;else if((r&224)===192){if(d>=i)return this.interim[0]=r,s;if(n=e[d++],(n&192)!==128){d--;continue}if(l=(r&31)<<6|n&63,l<128){d--;continue}t[s++]=l}else if((r&240)===224){if(d>=i)return this.interim[0]=r,s;if(n=e[d++],(n&192)!==128){d--;continue}if(d>=i)return this.interim[0]=r,this.interim[1]=n,s;if(o=e[d++],(o&192)!==128){d--;continue}if(l=(r&15)<<12|(n&63)<<6|o&63,l<2048||l>=55296&&l<=57343||l===65279)continue;t[s++]=l}else if((r&248)===240){if(d>=i)return this.interim[0]=r,s;if(n=e[d++],(n&192)!==128){d--;continue}if(d>=i)return this.interim[0]=r,this.interim[1]=n,s;if(o=e[d++],(o&192)!==128){d--;continue}if(d>=i)return this.interim[0]=r,this.interim[1]=n,this.interim[2]=o,s;if(a=e[d++],(a&192)!==128){d--;continue}if(l=(r&7)<<18|(n&63)<<12|(o&63)<<6|a&63,l<65536||l>1114111)continue;t[s++]=l}}return s}},no="",Et=" ",Si=class e{constructor(){this.fg=0,this.bg=0,this.extended=new qi}static toColorRGB(t){return[t>>>16&255,t>>>8&255,t&255]}static fromColorRGB(t){return(t[0]&255)<<16|(t[1]&255)<<8|t[2]&255}clone(){let t=new e;return t.fg=this.fg,t.bg=this.bg,t.extended=this.extended.clone(),t}isInverse(){return this.fg&67108864}isBold(){return this.fg&134217728}isUnderline(){return this.hasExtendedAttrs()&&this.extended.underlineStyle!==0?1:this.fg&268435456}isBlink(){return this.fg&536870912}isInvisible(){return this.fg&1073741824}isItalic(){return this.bg&67108864}isDim(){return this.bg&134217728}isStrikethrough(){return this.fg&2147483648}isProtected(){return this.bg&536870912}isOverline(){return this.bg&1073741824}getFgColorMode(){return this.fg&50331648}getBgColorMode(){return this.bg&50331648}isFgRGB(){return(this.fg&50331648)===50331648}isBgRGB(){return(this.bg&50331648)===50331648}isFgPalette(){return(this.fg&50331648)===16777216||(this.fg&50331648)===33554432}isBgPalette(){return(this.bg&50331648)===16777216||(this.bg&50331648)===33554432}isFgDefault(){return(this.fg&50331648)===0}isBgDefault(){return(this.bg&50331648)===0}isAttributeDefault(){return this.fg===0&&this.bg===0}getFgColor(){switch(this.fg&50331648){case 16777216:case 33554432:return this.fg&255;case 50331648:return this.fg&16777215;default:return-1}}getBgColor(){switch(this.bg&50331648){case 16777216:case 33554432:return this.bg&255;case 50331648:return this.bg&16777215;default:return-1}}hasExtendedAttrs(){return this.bg&268435456}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(this.bg&268435456&&~this.extended.underlineColor)switch(this.extended.underlineColor&50331648){case 16777216:case 33554432:return this.extended.underlineColor&255;case 50331648:return this.extended.underlineColor&16777215;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return this.bg&268435456&&~this.extended.underlineColor?this.extended.underlineColor&50331648:this.getFgColorMode()}isUnderlineColorRGB(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)===50331648:this.isFgRGB()}isUnderlineColorPalette(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)===16777216||(this.extended.underlineColor&50331648)===33554432:this.isFgPalette()}isUnderlineColorDefault(){return this.bg&268435456&&~this.extended.underlineColor?(this.extended.underlineColor&50331648)===0:this.isFgDefault()}getUnderlineStyle(){return this.fg&268435456?this.bg&268435456?this.extended.underlineStyle:1:0}getUnderlineVariantOffset(){return this.extended.underlineVariantOffset}},qi=class e{constructor(t=0,i=0){this._ext=0,this._urlId=0,this._ext=t,this._urlId=i}get ext(){return this._urlId?this._ext&-469762049|this.underlineStyle<<26:this._ext}set ext(t){this._ext=t}get underlineStyle(){return this._urlId?5:(this._ext&469762048)>>26}set underlineStyle(t){this._ext&=-469762049,this._ext|=t<<26&469762048}get underlineColor(){return this._ext&67108863}set underlineColor(t){this._ext&=-67108864,this._ext|=t&67108863}get urlId(){return this._urlId}set urlId(t){this._urlId=t}get underlineVariantOffset(){let t=(this._ext&3758096384)>>29;return t<0?t^4294967288:t}set underlineVariantOffset(t){this._ext&=536870911,this._ext|=t<<29&3758096384}clone(){return new e(this._ext,this._urlId)}isEmpty(){return this.underlineStyle===0&&this._urlId===0}},Ve=class e extends Si{constructor(){super(...arguments);this.content=0,this.fg=0,this.bg=0,this.extended=new qi,this.combinedData=""}static fromCharData(t){let i=new e;return i.setFromCharData(t),i}isCombined(){return this.content&2097152}getWidth(){return this.content>>22}getChars(){return this.content&2097152?this.combinedData:this.content&2097151?Lt(this.content&2097151):""}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):this.content&2097151}setFromCharData(t){this.fg=t[0],this.bg=0;let i=!1;if(t[1].length>2)i=!0;else if(t[1].length===2){let s=t[1].charCodeAt(0);if(55296<=s&&s<=56319){let r=t[1].charCodeAt(1);56320<=r&&r<=57343?this.content=(s-55296)*1024+r-56320+65536|t[2]<<22:i=!0}else i=!0}else this.content=t[1].charCodeAt(0)|t[2]<<22;i&&(this.combinedData=t[1],this.content=2097152|t[2]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}},tn="di$target",zs="di$dependencies",Cs=new Map;function ch(e){return e[zs]||[]}function we(e){if(Cs.has(e))return Cs.get(e);let t=function(i,s,r){if(arguments.length!==3)throw Error("@IServiceName-decorator can only be used to decorate a parameter");dh(t,i,r)};return t._id=e,Cs.set(e,t),t}function dh(e,t,i){t[tn]===t?t[zs].push({id:e,index:i}):(t[zs]=[{id:e,index:i}],t[tn]=t)}var De=we("BufferService"),oo=we("CoreMouseService"),zt=we("CoreService"),uh=we("CharsetService"),fr=we("InstantiationService"),ao=we("LogService"),Ae=we("OptionsService"),ho=we("OscLinkService"),_h=we("UnicodeService"),wi=we("DecorationService"),Hs=class{constructor(e,t,i){this._bufferService=e,this._optionsService=t,this._oscLinkService=i}provideLinks(e,t){let i=this._bufferService.buffer.lines.get(e-1);if(!i){t(void 0);return}let s=[],r=this._optionsService.rawOptions.linkHandler,n=new Ve,o=i.getTrimmedLength(),a=-1,l=-1,h=!1;for(let c=0;c<o;c++)if(!(l===-1&&!i.hasContent(c))){if(i.loadCell(c,n),n.hasExtendedAttrs()&&n.extended.urlId)if(l===-1){l=c,a=n.extended.urlId;continue}else h=n.extended.urlId!==a;else l!==-1&&(h=!0);if(h||l!==-1&&c===o-1){let d=this._oscLinkService.getLinkData(a)?.uri;if(d){let u={start:{x:l+1,y:e},end:{x:c+(!h&&c===o-1?1:0),y:e}},_=!1;if(!r?.allowNonHttpProtocols)try{let g=new URL(d);["http:","https:"].includes(g.protocol)||(_=!0)}catch{_=!0}_||s.push({text:d,range:u,activate:(g,v)=>r?r.activate(g,v,u):fh(g,v),hover:(g,v)=>r?.hover?.(g,v,u),leave:(g,v)=>r?.leave?.(g,v,u)})}h=!1,n.hasExtendedAttrs()&&n.extended.urlId?(l=c,a=n.extended.urlId):(l=-1,a=-1)}}t(s)}};Hs=re([T(0,De),T(1,Ae),T(2,ho)],Hs);function fh(e,t){if(confirm(`Do you want to navigate to ${t}?
3
3
 
4
4
  WARNING: This link could potentially be dangerous`)){let i=window.open();if(i){try{i.opener=null}catch{}i.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}}var ts=we("CharSizeService"),ct=we("CoreBrowserService"),gr=we("MouseService"),dt=we("RenderService"),gh=we("SelectionService"),lo=we("CharacterJoinerService"),Vt=we("ThemeService"),co=we("LinkProviderService"),ph=class{constructor(){this.listeners=[],this.unexpectedErrorHandler=function(e){setTimeout(()=>{throw e.stack?sn.isErrorNoTelemetry(e)?new sn(e.message+`