kaizen-agent 1.41.0 → 1.41.2

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/install.ts CHANGED
@@ -576,7 +576,8 @@ if (upgrade) {
576
576
  // is not a failed upgrade: the version on disk is what decides.
577
577
  if (after !== latest) {
578
578
  console.log(`\n ${c.bold(`could not install kaizen ${latest}`)}`);
579
- console.log(` ${c.dim("try:")} ${c.cyan(`bunx kaizen-agent@${latest}`)}\n`);
579
+ // npm names a new version before it serves it (its review takes minutes).
580
+ console.log(` ${c.dim("npm may still be publishing it: try again in a few minutes, or run")} ${c.cyan(`bunx kaizen-agent@${latest}`)}\n`);
580
581
  process.exit(1);
581
582
  }
582
583
  console.log(`\n ${c.bold("Upgraded")} ${c.dim(now)} → ${c.green(after)}\n ${c.dim("Restart Claude Code to pick up any new slash commands.")}\n`);
package/cli/state.test.ts CHANGED
@@ -93,6 +93,22 @@ test("boardCards: a started idea stays until its run has a state.json", () => {
93
93
  rmSync(join(dir, ".."), { recursive: true, force: true });
94
94
  });
95
95
 
96
+ test("boardCards: a started idea retires into the run its launch's session wrote, however reworded", () => {
97
+ const dir = join(mkdtempSync(join(tmpdir(), "kaizen-")), ".kaizen");
98
+ const r = join(dir, "runs", "2026-01-01-reworded");
99
+ mkdirSync(r, { recursive: true });
100
+ mkdirSync(join(dir, "sessions"));
101
+ writeInbox(dir, [{ status: "started", text: "revision" }]);
102
+ writeFileSync(join(r, "00-request.md"), "- add a form for the icebox\n");
103
+ writeFileSync(join(dir, "sessions", "1-a.json"), JSON.stringify({ run: null, text: "revision", cmd: "claude", sessionId: "abc", started: Date.now() }));
104
+ const cards = () => boardCards([dir], Date.now()).map((k) => k.kind);
105
+ writeFileSync(join(r, "state.json"), JSON.stringify({ id: "2026-01-01-reworded", stage: "build", awaiting: null }));
106
+ expect(cards()).toEqual(["idea", "run"]);
107
+ writeFileSync(join(r, "state.json"), JSON.stringify({ id: "2026-01-01-reworded", stage: "build", awaiting: null, session: "abc" }));
108
+ expect(cards()).toEqual(["run"]);
109
+ rmSync(join(dir, ".."), { recursive: true, force: true });
110
+ });
111
+
96
112
  test("boardCards: a run older than the idea does not retire it", () => {
97
113
  const dir = join(mkdtempSync(join(tmpdir(), "kaizen-")), ".kaizen");
98
114
  const r = join(dir, "runs", "2026-01-01-old");
package/cli/state.ts CHANGED
@@ -616,6 +616,7 @@ export type Card = {
616
616
  awaiting: string | null;
617
617
  dim: boolean;
618
618
  moved?: number; // runs only: state.json mtime
619
+ added?: number; // ideas only: when the inbox line first appeared (ms)
619
620
  title?: string; // runs only: first line of the request
620
621
  archived: boolean; // hidden from the board unless asked for
621
622
  branch?: string; // runs only: the run's own branch while it has a worktree
@@ -1001,6 +1002,9 @@ export function boardCards(states: string[], now: number): Card[] {
1001
1002
  for (const l of b.split("\n")) lines.push({ at, text: normalise(l.replace(/^[\s>*"'`-]+|[\s"'`.]+$/g, "")) });
1002
1003
  }
1003
1004
  }
1005
+ // A launched idea whose run reworded the request: the board's own launch record,
1006
+ // owned by a run (session id in its state.json, see ownerOf), retires it all the same.
1007
+ const launchedInto = (st: string, t: string) => readSessions(st).some((s) => !s.run && normalise(s.text.replace(/^(lite|full|auto|plan)\s+/i, "")) === t.replace(/^(lite|full|auto|plan)\s+/i, "") && ownerOf(st, s) !== null);
1004
1008
  for (const st of states) {
1005
1009
  const where = label(st);
1006
1010
  const archive = readArchive(st);
@@ -1019,10 +1023,10 @@ export function boardCards(states: string[], now: number): Card[] {
1019
1023
  // titled "fix this bugs" must not swallow a new idea with the same words.
1020
1024
  // ponytail: 2s slack for coarse filesystem clocks.
1021
1025
  const after = (r: { at: number }) => !it.added || r.at >= it.added - 2000;
1022
- if (it.status === "started" ? requests.some((r) => after(r) && r.text.includes(t)) : lines.some((l) => after(l) && (l.text === t || l.text.endsWith(" " + t)))) continue;
1026
+ if (it.status === "started" ? requests.some((r) => after(r) && r.text.includes(t)) || launchedInto(st, t) : lines.some((l) => after(l) && (l.text === t || l.text.endsWith(" " + t)))) continue;
1023
1027
  next.push(it.status === "started"
1024
1028
  ? { kind: "idea", status: "starting", text: it.text, state: st, where, column: 1, awaiting: null, dim: true, archived: false, notes: it.notes, parent: parentOf(it.notes) }
1025
- : { kind: "idea", status: "idea", text: it.text, state: st, where, column: 0, awaiting: null, dim: false, archived: it.status === "archived", notes: it.notes, parent: parentOf(it.notes) });
1029
+ : { kind: "idea", status: "idea", text: it.text, state: st, where, column: 0, awaiting: null, dim: false, archived: it.status === "archived", notes: it.notes, parent: parentOf(it.notes), added: it.added });
1026
1030
  }
1027
1031
  // Runs started in Yolo. Only yolo records are matched to runs, so a project that
1028
1032
  // never used it pays one directory read.
package/cli/web.ts CHANGED
@@ -815,6 +815,15 @@ export async function web(repoDir: string, opts: Opts = {}) {
815
815
  } catch (e) { return bad((e as Error).message, 500); }
816
816
  }
817
817
 
818
+ // Files added with a hand-written icebox item: saved beside the state, linked in its notes.
819
+ const iceFiles = (state: string, files: unknown) => {
820
+ const fs: { name: string; data: string }[] = Array.isArray(files) ? files : [];
821
+ if (fs.reduce((t, f) => t + (f.data?.length ?? 0), 0) > 20_000_000) throw new Error("attachments over 15MB");
822
+ return fs.map((f) => {
823
+ const rel = saveAttachment(state, null, String(f.name ?? "file"), Buffer.from(String(f.data).replace(/^data:[^,]*,/, ""), "base64"));
824
+ return (/\.(png|jpe?g|gif|webp|svg)$/i.test(rel) ? "!" : "") + `[${String(f.name ?? "file")}](${rel})`;
825
+ });
826
+ };
818
827
  // The icebox: the only way gathered text becomes an idea, one explicit click
819
828
  // (or agent call) at a time. An idea is made; no run starts.
820
829
  if (path === "/icebox") {
@@ -822,7 +831,7 @@ export async function web(repoDir: string, opts: Opts = {}) {
822
831
  const ids = Array.isArray(body.ids) ? body.ids.filter((i: unknown) => typeof i === "string") : [];
823
832
  try {
824
833
  const why = body.op === "promote" ? promote(body.dir, ids, typeof body.text === "string" ? body.text : undefined, typeof body.details === "string" ? body.details : undefined)
825
- : body.op === "add" ? addItem(body.dir, typeof body.text === "string" ? body.text : "", typeof body.details === "string" ? body.details : "")
834
+ : body.op === "add" ? addItem(body.dir, typeof body.text === "string" ? body.text : "", [typeof body.details === "string" ? body.details : "", ...iceFiles(body.dir, body.files)].filter(Boolean).join("\n"))
826
835
  : body.op === "dismiss" ? dismiss(body.dir, ids) : body.op === "restore" ? restore(body.dir, ids) : "unknown op";
827
836
  if (why) return bad(why, why === "no such item" ? 404 : why === "unknown op" ? 400 : 409);
828
837
  } catch (e) { return bad((e as Error).message, 500); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-agent",
3
- "version": "1.41.0",
3
+ "version": "1.41.2",
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": {
@@ -341,7 +341,9 @@ changing because a setting moved:
341
341
  ```
342
342
 
343
343
  Every run writes all nine keys, from intake on: `id`, `mode`, `runner`, `agent`,
344
- `track`, `stage`, `awaiting`, `iteration` (0 until the fix loop runs), `updated`. The
344
+ `track`, `stage`, `awaiting`, `iteration` (0 until the fix loop runs), `updated`. Where
345
+ the environment has `CLAUDE_CODE_SESSION_ID`, also `session` with its value: without it
346
+ a card launched from the board can stay stuck in planning beside its own run. The
345
347
  legal values of `stage` and `awaiting` are in [`spec.md`](spec.md).
346
348
 
347
349
  In a git repository, `init` also adds a gitignore entry that ignores run state but
@@ -2,7 +2,7 @@ import { store } from "./store.ts";
2
2
  import { where } from "./agent-term.ts";
3
3
  import { load, post } from "./api.ts";
4
4
  import { mic, stopDictation } from "./dictation.ts";
5
- import { $, esc, ico } from "./dom.ts";
5
+ import { $, dataUrls, esc, ico } from "./dom.ts";
6
6
  import { draft, undraft } from "./drafts.ts";
7
7
  import { COLS, ICON, MARKS, NAMES, WORD, age, d, need } from "./labels.ts";
8
8
  import { brief } from "./markdown.ts";
@@ -502,13 +502,15 @@ export function render() {
502
502
  (archive || !nested(c)) &&
503
503
  (!q || `${c.title || ""} ${c.text || ""} ${c.id || ""}`.toLowerCase().includes(q)),
504
504
  );
505
- // Runs age by their last move, ideas by inbox position (new ones are appended).
505
+ // Runs age by their last move, ideas by when they were added, across every project;
506
+ // an idea from before the stamp falls back to its inbox position.
506
507
  if (ci < 4) {
507
- const at = (c) => (c.kind === "run" ? c.moved || 0 : store.S.cards.indexOf(c)),
508
- old = order[ci] === "old";
508
+ const at = (c) => (c.kind === "run" ? c.moved || 0 : c.added || 0),
509
+ old = order[ci] === "old",
510
+ pos = (c) => store.S.cards.indexOf(c);
509
511
  // Runs waiting on you go first, then the column's own order.
510
512
  const w = (c) => (c.status === "waiting" ? 1 : 0);
511
- list.sort((a, b) => w(b) - w(a) || (old ? at(a) - at(b) : at(b) - at(a)));
513
+ list.sort((a, b) => w(b) - w(a) || (old ? at(a) - at(b) || pos(a) - pos(b) : at(b) - at(a) || pos(b) - pos(a)));
512
514
  }
513
515
  const clearable = ci === 4 && !archive && list.length;
514
516
  const olderThan = (days) => list.filter((c) => store.S.now - (c.moved || store.S.now) > days * d);
@@ -607,16 +609,7 @@ export function render() {
607
609
  toast("Idea added");
608
610
  // The idea exists first: a failed upload leaves it as plain text.
609
611
  if (files.length) {
610
- const enc = await Promise.all(
611
- files.map(
612
- (x) =>
613
- new Promise((res) => {
614
- const rd = new FileReader();
615
- rd.onload = () => res({ name: x.name, data: rd.result });
616
- rd.readAsDataURL(x);
617
- }),
618
- ),
619
- );
612
+ const enc = await dataUrls(files);
620
613
  const n = await post("/note", { dir, for: text, text: "", files: enc });
621
614
  if (!n.ok) toast("Idea added, files not attached");
622
615
  }
package/web/board/dom.ts CHANGED
@@ -9,3 +9,26 @@ export const SKEL =
9
9
  '<span class="skel" style="height:16px;width:35%;margin:20px 0 14px"></span>' +
10
10
  '<span class="skel" style="height:44px;margin-bottom:10px"></span>'.repeat(4) +
11
11
  "</div>";
12
+ // Files as {name, data-URL}, the shape /note and /icebox take.
13
+ export const dataUrls = (files: File[]) =>
14
+ Promise.all(
15
+ files.map(
16
+ (x) =>
17
+ new Promise<{ name: string; data: string }>((res) => {
18
+ const rd = new FileReader();
19
+ rd.onload = () => res({ name: x.name, data: rd.result as string });
20
+ rd.readAsDataURL(x);
21
+ }),
22
+ ),
23
+ );
24
+ // A modal form: the native <dialog>, removed from the page once closed.
25
+ export function modal(html: string) {
26
+ const dl = document.createElement("dialog");
27
+ dl.className = "kz";
28
+ dl.innerHTML = `<form class="inl" method="dialog">${html}</form>`;
29
+ dl.onclose = () => dl.remove();
30
+ dl.onclick = (e) => e.target === dl && dl.close(); // a tap on the backdrop
31
+ document.body.appendChild(dl);
32
+ dl.showModal();
33
+ return dl;
34
+ }
@@ -2,9 +2,9 @@ import { store } from "./store.ts";
2
2
  import { termStop } from "./agent-term.ts";
3
3
  import { load, post } from "./api.ts";
4
4
  import { via } from "./cards.ts";
5
- import { stopDictation } from "./dictation.ts";
6
- import { $, esc, ico, SKEL } from "./dom.ts";
7
- import { plogo, setShown, short, showable } from "./projects.ts";
5
+ import { mic, stopDictation } from "./dictation.ts";
6
+ import { $, dataUrls, esc, ico, modal, SKEL } from "./dom.ts";
7
+ import { ideaCombo, newDir, plogo, setShown, short, showable } from "./projects.ts";
8
8
  import { toast } from "./toast.ts";
9
9
  import { go, view } from "./views.ts";
10
10
 
@@ -988,13 +988,57 @@ export async function openSources() {
988
988
  };
989
989
  }
990
990
  if ($("ice-new"))
991
- $("ice-new").onclick = async () => {
992
- const to = dir || showable()[0]?.dir;
993
- if (!to) return toast("Pick a project first");
994
- const text = prompt(dir ? "New request for the icebox:" : `New request for ${short(showable()[0].label)}:`)?.trim();
995
- if (!text) return;
996
- const j = await post("/icebox", { op: "add", dir: to, text });
997
- if (j.ok) toast("Added to the icebox");
991
+ $("ice-new").onclick = () => {
992
+ const all = !dir;
993
+ if (!dir && !showable().length) return toast("Pick a project first");
994
+ // The new-idea form, cut down: the request, files, dictation and, in All projects, the project.
995
+ const dl = modal(
996
+ `${all ? `<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="New request for the icebox" 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">${ico("clip")}</button><button type="button" class="btn ghost" value="">Cancel</button><button class="btn pri">Add</button></div>`,
997
+ );
998
+ const f = dl.querySelector("form"),
999
+ inp = f.querySelector("textarea"),
1000
+ files = [];
1001
+ const add = (x) => {
1002
+ files.push(x);
1003
+ f.querySelector(".att").textContent = files.map((y) => y.name).join(" · ");
1004
+ };
1005
+ f.querySelector("#newpick").onclick = () => f.querySelector("input[type=file]").click();
1006
+ f.querySelector("input[type=file]").onchange = (e) => {
1007
+ [...e.target.files].forEach(add);
1008
+ e.target.value = "";
1009
+ };
1010
+ inp.onpaste = (e) => {
1011
+ for (const it of e.clipboardData.items)
1012
+ if (it.kind === "file") {
1013
+ const x = it.getAsFile();
1014
+ add(new File([x], x.name === "image.png" ? `paste-${Date.now()}.png` : x.name, { type: x.type }));
1015
+ e.preventDefault();
1016
+ }
1017
+ };
1018
+ f.querySelector(".btn.ghost").onclick = () => dl.close();
1019
+ if (all) ideaCombo(f);
1020
+ const m = mic([inp]);
1021
+ if (m) f.querySelector(".row").prepend(m);
1022
+ dl.addEventListener("close", () => stopDictation(false));
1023
+ inp.onkeydown = (e) => {
1024
+ if (e.key === "Enter" && !e.shiftKey) {
1025
+ e.preventDefault();
1026
+ f.requestSubmit();
1027
+ }
1028
+ };
1029
+ f.onsubmit = async (e) => {
1030
+ e.preventDefault();
1031
+ const text = inp.value.trim(),
1032
+ to = dir || newDir;
1033
+ if (!text) return inp.focus();
1034
+ if (!to) return toast("Pick a project first");
1035
+ const j = await post("/icebox", { op: "add", dir: to, text, files: await dataUrls(files) });
1036
+ if (j.ok) {
1037
+ toast("Added to the icebox");
1038
+ dl.close();
1039
+ }
1040
+ };
1041
+ inp.focus();
998
1042
  };
999
1043
  if ($("src-go"))
1000
1044
  $("src-go").onclick = () => {
@@ -1,7 +1,7 @@
1
1
  import { store } from "./store.ts";
2
2
  import { load, post } from "./api.ts";
3
3
  import { render } from "./cards.ts";
4
- import { $, esc, ico } from "./dom.ts";
4
+ import { $, esc, ico, modal } from "./dom.ts";
5
5
  import { openSources, srcOpen } from "./icebox.ts";
6
6
  import { toast } from "./toast.ts";
7
7
  import { view } from "./views.ts";
@@ -56,24 +56,49 @@ async function pick(v) {
56
56
  const j = await post("/find", {});
57
57
  if (j.ok) toast(j.added ? `Found ${j.added} new project${j.added === 1 ? "" : "s"}` : "No new projects found");
58
58
  } else if (v === "__clone") {
59
- const url = prompt("GitHub link to clone (https or ssh):")?.trim();
60
- if (!url) return;
61
- const d = await post("/clone", { url, check: true });
62
- if (!d.ok) return;
63
- let to = prompt("Clone into (a new folder is created). Leave empty to pick a folder:", d.to);
64
- if (to === null) return;
65
- if (!to.trim()) {
66
- const f = await post("/pick-folder", {});
67
- if (!f.path) return;
68
- to = f.path.replace(/[\\/]+$/, "") + (f.path.includes("\\") ? "\\" : "/") + d.name;
69
- }
70
- toast("Cloning…");
71
- const j = await post("/clone", { url, to });
72
- if (!j.ok) return;
73
- await post("/cd", { dir: j.dir });
59
+ // One form: the link, then where it goes. The folder fills in once the link names a repo.
60
+ const dl = modal(
61
+ `<b>Clone from GitHub</b><input name="url" placeholder="https://github.com/owner/repo or git@github.com:owner/repo" aria-label="GitHub link (https or ssh)" spellcheck="false" autocomplete="off"><div class="row"><input name="to" placeholder="Clone into (a new folder is made)" 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>`,
62
+ );
63
+ const f = dl.querySelector("form"),
64
+ url = f.querySelector("[name=url]"),
65
+ to = f.querySelector("[name=to]");
66
+ let name = "",
67
+ auto = ""; // the folder this form filled in; one the user typed is left alone
68
+ const check = async () => {
69
+ const u = url.value.trim();
70
+ if (!u) return null;
71
+ const d = await post("/clone", { url: u, check: true });
72
+ if (!d.ok) return null;
73
+ name = d.name;
74
+ if (!to.value.trim() || to.value === auto) to.value = auto = d.to;
75
+ return d;
76
+ };
77
+ url.onchange = check;
78
+ f.querySelector("[data-a=x]").onclick = () => dl.close();
79
+ f.querySelector("[data-a=pick]").onclick = async () => {
80
+ if (!(await check())) return url.focus();
81
+ const p = await post("/pick-folder", {});
82
+ if (p.path) to.value = p.path.replace(/[\\/]+$/, "") + (p.path.includes("\\") ? "\\" : "/") + name;
83
+ };
84
+ url.focus();
85
+ const dir = await new Promise((done) => {
86
+ dl.addEventListener("close", () => done(null));
87
+ f.onsubmit = async (e) => {
88
+ e.preventDefault();
89
+ if (!(await check())) return url.focus();
90
+ toast("Cloning…");
91
+ const j = await post("/clone", { url: url.value.trim(), to: to.value.trim() || undefined });
92
+ if (!j.ok) return;
93
+ done(j.dir);
94
+ dl.close();
95
+ };
96
+ });
97
+ if (!dir) return;
98
+ await post("/cd", { dir });
74
99
  store.all = false;
75
- store.dir = j.dir;
76
- keep(j.dir);
100
+ store.dir = dir;
101
+ keep(dir);
77
102
  toast("Cloned");
78
103
  } else if (v === "all") {
79
104
  store.all = true;
package/web/board.html CHANGED
@@ -219,6 +219,7 @@ section li.hint{display:block;padding:14px;border:1px dashed var(--line);border-
219
219
  .inl input::placeholder,.inl textarea::placeholder,.notes textarea::placeholder{color:var(--dim)}
220
220
  .inl .row,.notes .row{display:flex;gap:6px;justify-content:flex-end;align-items:center}
221
221
  .inl .row .mic{margin-right:0}.inl .row #newpick{margin-right:auto}.inl .row .btn{white-space:nowrap}.inl .att{font-size:12px;color:var(--dim)}.inl .att{font-size:12px;color:var(--dim)}.inl .att:empty{display:none}
222
+ dialog.kz{border:0;padding:0;background:none;width:min(480px,calc(100vw - 32px));max-height:calc(100dvh - 32px);overflow:visible;color:var(--ink)}dialog.kz::backdrop{background:rgb(0 0 0/.45)}dialog.kz .row input{flex:1;min-width:0}dialog.kz #ndc{align-self:flex-start}
222
223
  .inl .run-actions{display:grid;gap:8px}.inl .run-actions .row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.inl .run-actions .pri{grid-column:1/-1}
223
224
  .notes{display:flex;flex-direction:column;gap:8px}
224
225
  #pbody img{max-width:100%;border-radius:var(--r-s)}
package/web/board.js CHANGED
@@ -1,5 +1,5 @@
1
- /* kaizen board 2ce23536754b7b8b -- generated by bun run build:web from web/board/main.ts; do not edit */
2
- (()=>{var a={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,wantTerm:null,setQ:"",pageGen:0,PL:[],shown:new Set((()=>{try{return JSON.parse(localStorage.getItem("kz-show"))||[]}catch(e){return[]}})())};var n=(e)=>document.getElementById(e),p=(e)=>String(e).replace(/[&<>"]/g,(t)=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t]),q=(e)=>`<svg><use href="#i-${e}"/></svg>`,ht='<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>";var bn;function S(e,t){let o=n("toast");if(o.textContent=e,o.classList.toggle("rep",!!t),t){let r=document.createElement("a");r.href="/issue?"+new URLSearchParams(t),r.target="_blank",r.rel="noopener",r.textContent="Report",o.append(" ",r)}o.classList.add("on"),clearTimeout(bn),bn=setTimeout(()=>o.classList.remove("on"),t?8000:4000)}function ye(){try{if(typeof Notification<"u"&&Notification.permission==="default")Notification.requestPermission()}catch(e){}}function yn(e){try{if(typeof Notification<"u"&&Notification.permission==="granted")new Notification("kaizen",{body:e,icon:"/logo.png"})}catch(t){}}var fs="https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0",kn={tiny:"onnx-community/whisper-tiny",base:"onnx-community/whisper-base",small:"onnx-community/whisper-small","large-v3-turbo":"onnx-community/whisper-large-v3-turbo"},Nt=null,hs=`import{pipeline}from"${fs}";let asr=null;
1
+ /* kaizen board 4c283dae7e7513b1 -- generated by bun run build:web from web/board/main.ts; do not edit */
2
+ (()=>{var a={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,wantTerm:null,setQ:"",pageGen:0,PL:[],shown:new Set((()=>{try{return JSON.parse(localStorage.getItem("kz-show"))||[]}catch(e){return[]}})())};var n=(e)=>document.getElementById(e),p=(e)=>String(e).replace(/[&<>"]/g,(t)=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t]),q=(e)=>`<svg><use href="#i-${e}"/></svg>`,ht='<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>",mt=(e)=>Promise.all(e.map((t)=>new Promise((o)=>{let r=new FileReader;r.onload=()=>o({name:t.name,data:r.result}),r.readAsDataURL(t)})));function gt(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=(o)=>o.target===t&&t.close(),document.body.appendChild(t),t.showModal(),t}var kn;function S(e,t){let o=n("toast");if(o.textContent=e,o.classList.toggle("rep",!!t),t){let r=document.createElement("a");r.href="/issue?"+new URLSearchParams(t),r.target="_blank",r.rel="noopener",r.textContent="Report",o.append(" ",r)}o.classList.add("on"),clearTimeout(kn),kn=setTimeout(()=>o.classList.remove("on"),t?8000:4000)}function ve(){try{if(typeof Notification<"u"&&Notification.permission==="default")Notification.requestPermission()}catch(e){}}function wn(e){try{if(typeof Notification<"u"&&Notification.permission==="granted")new Notification("kaizen",{body:e,icon:"/logo.png"})}catch(t){}}var ms="https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0",$n={tiny:"onnx-community/whisper-tiny",base:"onnx-community/whisper-base",small:"onnx-community/whisper-small","large-v3-turbo":"onnx-community/whisper-large-v3-turbo"},Ot=null,gs=`import{pipeline}from"${ms}";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})};
5
5
  // WebGPU first, with the encoder in fp16 only where the adapter has shader-f16; any GPU failure falls back to the CPU.
@@ -8,27 +8,27 @@ 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)})}}`,ms='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)',gs=!!(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&window.AudioWorkletNode&&window.WebAssembly&&window.Worker),ke=null,Ce=null,mt=()=>{},pe=null,Ie=!1,gt=new Set;async function bs(){let e="small";try{e=((await fetch("/settings").then((o)=>o.json())).rows.find((o)=>o.key==="dictation.model")||{}).value||e}catch(t){}if(!kn[e])e="small";if(Ce&&Nt!==e){try{ke.terminate()}catch(t){}ke=null,Ce=null,gt.clear(),pe=null}if(Ce)return Ce;return Nt=e,Ce=new Promise(async(t,o)=>{ke=new Worker(URL.createObjectURL(new Blob([hs],{type:"text/javascript"})),{type:"module"});let r={};ke.onmessage=(c)=>{let g=c.data;if(g.type==="progress"){r[g.file]=g.pct;let y=Object.values(r);mt(Math.round(y.reduce((v,h)=>v+h,0)/y.length))}else if(g.type==="ready")t();else if(g.type==="fallback")S("GPU could not run the model, using the CPU: "+g.why);else if(g.type==="error"&&g.seq==null)o(Error(g.error));for(let y of gt)y.onmsg(g)},ke.onerror=(c)=>o(Error(c.message||"worker failed"));let l=null;try{l=navigator.gpu&&await navigator.gpu.requestAdapter()}catch(c){}if(!l)S("No WebGPU: dictation will run on the CPU and be slower");ke.postMessage({type:"load",model:kn[Nt],webgpu:!!l,f16:!!(l&&l.features.has("shader-f16"))})}),Ce.catch(()=>{Ce=null;try{ke.terminate()}catch(t){}ke=null}),Ce}function fe(e){if(pe&&(e||!pe.field.isConnected))pe.stop()}function et(e){if(!gs)return null;let t=document.createElement("button");t.className="ib sm mic",t.title="Dictate",t.setAttribute("aria-label","Dictate"),t.type="button";let o=()=>{t.innerHTML=q("mic"),t.classList.remove("on","busy")};o();let r=e[0];return e.forEach((l)=>l.addEventListener("focus",()=>r=l)),t.onclick=async(l)=>{if(l.preventDefault(),l.stopPropagation(),pe){let E=pe.btn===t;if(pe.stop(),E)return}if(Ie)return;Ie=!0,t.classList.add("busy"),mt=(E)=>{t.innerHTML=`<i>${E}%</i>`};try{await bs()}catch(E){o(),mt=()=>{},Ie=!1,S("Dictation model could not load: "+E.message);return}if(mt=()=>{},t.innerHTML=q("mic"),!t.isConnected){o(),Ie=!1;return}let c;try{c=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,noiseSuppression:!0,echoCancellation:!0}})}catch(E){o(),Ie=!1,S("Microphone not available: "+(E.name==="NotAllowedError"?"permission denied":E.message));return}if(pe)pe.stop();let g=new AudioContext({sampleRate:16000});await g.audioWorklet.addModule(URL.createObjectURL(new Blob([ms],{type:"text/javascript"})));let y=g.createMediaStreamSource(c),v=new AudioWorkletNode(g,"kz-tap");y.connect(v);let h=r,k=h.selectionStart??h.value.length,u=document.createElement("span");u.className="vu",u.setAttribute("aria-hidden","true"),u.innerHTML=[0.5,0.9,1.3,0.8,0.45].map((E)=>`<i style="--w:${E}"></i>`).join("");let f=[],d=0,m=0,x=0,N=0,T=0,I=new Map,O=!1,ge=16000,C=6*ge,H=0.8*ge,U=1*ge,G=(E)=>{if(!E||!h.isConnected)return;let F=h.value;if(document.activeElement===h)k=h.selectionStart;k=Math.min(k,F.length);let te=F.slice(0,k),re=te&&!/\s$/.test(te)?" ":"";h.value=te+re+E+F.slice(k),k=te.length+re.length+E.length,h.selectionStart=h.selectionEnd=k,h.dispatchEvent(new Event("input",{bubbles:!0}))},W=()=>{if(!d||m<U){f=[],d=0,m=0,x=0;return}let E=new Float32Array(d),F=0;for(let te of f)E.set(te,F),F+=te.length;f=[],d=0,m=0,x=0,I.set(N,null),ke.postMessage({type:"run",sid:D,seq:N,audio:E},[E.buffer]),N++,t.classList.add("busy")},K=()=>{while(I.has(T)&&I.get(T)!==null)G(I.get(T)),I.delete(T),T++;if(!I.size){if(t.classList.remove("busy"),O)o(),gt.delete(ae)}};v.port.onmessage=(E)=>{let F=E.data;f.push(F),d+=F.length;let te=0;for(let le=0;le<F.length;le++)te+=F[le]*F[le];let re=Math.sqrt(te/F.length);if(re>0.01)m+=F.length,x=0;else x+=F.length;{let le=Math.min(1,re*8).toFixed(2);h.style.setProperty("--lvl",le),u.style.setProperty("--lvl",le)}if(d>=C||m>=U&&x>=H)W()};let D=Math.random().toString(36).slice(2),ae={btn:t,field:h,onmsg:(E)=>{if(E.sid!==D)return;if(E.type==="text")I.set(E.seq,E.text),K();else if(E.type==="error"&&E.seq!=null)I.set(E.seq,""),S("Dictation failed: "+E.error),K()},stop:()=>{if(O)return;O=!0,t.classList.remove("on"),h.classList.remove("dictating"),h.style.removeProperty("--lvl"),t.innerHTML=q("mic");try{v.port.onmessage=null,y.disconnect(),v.disconnect(),c.getTracks().forEach((E)=>E.stop()),g.close()}catch(E){}pe=null,W(),K()}};gt.add(ae),pe=ae,Ie=!1,t.classList.remove("busy"),t.classList.add("on"),h.classList.add("dictating"),t.replaceChildren(u),h.focus()},t}var vn="kz-d:";function De(e,t){try{let o=sessionStorage.getItem("kz-d:"+t);if(o!=null)e.value=o}catch(o){}e.addEventListener("input",()=>{try{sessionStorage.setItem("kz-d:"+t,e.value)}catch(o){}})}function wn(e){try{Object.keys(sessionStorage).forEach((t)=>{if(t.startsWith("kz-d:"+e))sessionStorage.removeItem(t)})}catch(t){}}var $n=86400000,xn=(e,t)=>{let o=t-e;return o<3600000?Math.round(o/60000)+"m":o<86400000?Math.round(o/3600000)+"h":Math.round(o/86400000)+"d"},Ht={claude:"Claude Code",codex:"Codex",agy:"Antigravity",opencode:"OpenCode",gemini:"Gemini",cursor:"Cursor",yolo:"yolo",unrechecked:"fix not rechecked"},Sn={claude:1,codex:1,agy:1,opencode:1,gemini:1,cursor:1,full:1,lite:1},qn=["Ideas","Planning","Building","Review","Done"],Ae={idea:"idea",starting:"start",running:"run",stalled:"stall",waiting:"wait",done:"done",abandoned:"aband"},ys={"approvals.plan":"plan needs your approval","approvals.review":"review needs your approval"},tt=(e)=>ys[e]||e,Oe={idea:"idea",starting:"starting",running:"running",stalled:"stalled",waiting:"waiting on you",done:"done",abandoned:"abandoned"};function Ln(e,t){let o=e&&e.match(new RegExp(`^##\\s+${t}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$(?![\\s\\S]))`,"m"));return o?o[1].trim():null}var bt=(e)=>Ln(e,"Preview"),yt=(e,t)=>e.replace(/(!?)\[([^\]]*)\]\(([^)\s]+)\)/g,(o,r,l,c)=>{if(/^attachments\/[^)]+$/.test(c))return`${r}[${l}](/file?dir=${encodeURIComponent(t.state)}&p=${encodeURIComponent((t.kind==="run"?`runs/${t.id}/`:"")+c)})`;if(/^([a-z][\w+.-]*:|\/|#)/i.test(c))return o;return`${l} (\`${c}\`)`}),Cn=(e)=>(Ln(e,"Commit")||"").replace(/^```\w*\n|\n```$/g,"").trim();function An(e){let t=(e||"").split(`
12
- `),o=t.findIndex((v)=>/^#+\s*(\d+\.\s*)?open questions/i.test(v.trim()));if(o<0)return[];let r=/^#+/.exec(t[o].trim())[0].length,l=[],c=null,g=!1;for(let v of t.slice(o+1)){let h=v.trim(),k=/^#+(?=\s)/.exec(h);if(k&&k[0].length<=r)break;if(!k&&h&&!/^```/.test(h)&&!/^none\.?$/i.test(h))g=!0;let u=/^\**Q\d*\s*[:.)]\**\s*(.+)$/.exec(h);if(u){l.push(c={q:u[1].trim(),multi:/multi-?select/i.test(u[1]),options:[]});continue}let f=/^[-*]\s+(.+)$/.exec(h);if(f&&c){let d=f[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 y=l.filter((v)=>v.options.length);return y.length||!g?y:null}function je(e){let t=/(^|[.!?)]\s+|\s)([A-Z][\w-]*(?: [a-z][\w-]*){0,3}):\s+/g,o=[],r;while(r=t.exec(e))if(!/^\s+$/.test(r[1])||!o.length&&!r[2].includes(" "))o.push([r.index+r[1].length,r[2],t.lastIndex]);if(o.length<2)return{head:e,parts:[]};let l=o.map(([,c,g],y)=>{let v=e.slice(g,y+1<o.length?o[y+1][0]:e.length).trim(),h=v.split(/\s*\((\d+)\)\s+/);if(h.length<5)return{label:c,body:v};let k=[];for(let f=1;f<h.length;f+=2)k.push([h[f],h[f+1]]);let u=k.every(([f],d)=>+f===d+1);return{label:c,body:(h[0]?h[0]+`
13
- `:"")+k.map(([f,d],m)=>u?`${m+1}. ${d}`:`- (${f}) ${d}`).join(`
14
- `)}});return{head:e.slice(0,o[0][0]).trim()||`${l[0].label}: ${e.slice(o[0][2],o[1][0]).trim()}`,parts:l}}function Ee(e,t){let o=e.split(`
15
- `),r=[],l=0,c=null,g=(v)=>p(v).replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\*\*([^*]+)\*\*/g,"<b>$1</b>").replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g,'<a href="$2" target="_blank"><img src="$2" alt="$1"></a>').replace(/\[([^\]]+)\]\(([^)\s]+)\)/g,'<a href="$2" target="_blank">$1</a>'),y=()=>{if(c)r.push(`</${c}>`),c=null};while(l<o.length){let v=o[l];if(/^```/.test(v)){y();let f=[];l++;while(l<o.length&&!/^```/.test(o[l]))f.push(o[l++]);r.push(`<pre>${p(f.join(`
16
- `))}</pre>`),l++;continue}let h=/^(#{1,6})\s+(.*)$/.exec(v);if(h){y(),r.push(`<h${Math.min(h[1].length,3)}>${g(h[2])}</h${Math.min(h[1].length,3)}>`),l++;continue}if(/^---+$/.test(v.trim())){y(),r.push("<hr>"),l++;continue}if(/^\|/.test(v)){y();let f=[];while(l<o.length&&/^\|/.test(o[l]))f.push(o[l++]);let d=(m)=>m.replace(/^\||\|$/g,"").split("|").map((x)=>g(x.trim()));r.push("<table>"+f.filter((m)=>!/^\|\s*-/.test(m)).map((m,x)=>`<tr>${d(m).map((N)=>x?`<td>${N}</td>`:`<th>${N}</th>`).join("")}</tr>`).join("")+"</table>");continue}let k=/^\s*(?:[-*]|\d+\.)\s+(.*)$/.exec(v);if(k){let f=/^\s*\d+\./.test(v)?"ol":"ul";if(c!==f)y(),r.push(`<${f}>`),c=f;let d=t&&/^(open|done|rejected):\s*(.*)$/.exec(k[1]);r.push(d?`<li class="tk${d[1]==="rejected"?" rej":""}"><label><input type="checkbox" tabindex="-1" aria-disabled="true" onclick="return false"${d[1]==="done"?" checked":""} aria-label="${d[1]}"><span>${g(d[2])}</span></label></li>`:`<li>${g(k[1])}</li>`),l++;continue}if(!v.trim()){y(),l++;continue}y();let u=[v];l++;while(l<o.length&&o[l].trim()&&!/^(#|```|\||\s*[-*]\s|\s*\d+\.\s|---)/.test(o[l]))u.push(o[l++]);r.push(`<p>${g(u.join(" "))}</p>`)}return y(),r.join("")}function jn(e,t){let o=[...e.matchAll(/^\s*-\s*(todo|doing|done):\s*(.*)$/gm)].map((g)=>[g[1],g[2]]);if(!o.length)return"";let r=o.filter((g)=>g[0]==="done").length,l=Math.round(r/o.length*100),c={done:"✓",doing:'<span class="pulse"></span>',todo:""};return`<div class="prog"><div class="ph"><b>${r} of ${o.length} done</b><span>${l}%</span></div><div class="pbar"><i style="width:${l}%"></i></div><ol>${o.map(([g,y],v)=>`<li class="p-${g}${t&&t[v]&&t[v]!==g?" chg":""}"><span class="pi">${c[g]}</span><span>${p(y).replace(/`([^`]+)`/g,"<code>$1</code>")}</span></li>`).join("")}</ol></div>`}var ie=null,It=null;function En(e){It=e,ie=e.update?e:null;let t=document.querySelector('.rail a[data-view="settings"]');return t.classList.toggle("upd",!!ie),t.title=ie?`Settings: kaizen ${e.latest} is available`:"Settings",!!ie}function Ot(){let e=document.createElement("div");if(e.className=`updbar${ie?"":" quiet"}`,!ie)e.innerHTML=`<span id="upd-msg" aria-live="polite">${It?`kaizen ${p(It.version)}`:"kaizen"}</span><button class="btn" id="upd-chk">Check for updates</button>`;else{let r=(ie.notes||[]).map((c)=>`<li><b>${p(c.v)}</b> ${p(c.text)}</li>`).join(""),l=r?`<span class="updnew"><button class="btn" aria-describedby="upd-notes">What's new</button><span class="updpop" id="upd-notes" role="tooltip"><ul>${r}</ul><a href="https://github.com/hfadhlullah/kaizen/releases" target="_blank" rel="noopener">All release notes</a></span></span>`:"";e.innerHTML=`<span>kaizen <b>${p(ie.latest)}</b> is available. You are running ${p(ie.version)}.</span><span class="updact">${l}<button class="btn" id="upd-chk">Check for updates</button><button class="btn pri" id="upd-go">Update kaizen</button></span>`}n("pbody").prepend(e);let t=n("upd-chk");if(t.onclick=async()=>{t.disabled=!0,t.textContent="Checking…";let r=await fetch("/version?fresh=1").then((l)=>l.json()).catch(()=>null);if(!e.isConnected)return;if(t.disabled=!1,!r||!r.latest){if(ie)t.textContent="Could not check. Try again";else n("upd-msg").textContent="Could not reach npm. Try again later.";return}if(En(r),e.remove(),Ot(),ie)n("upd-go").focus();else n("upd-msg").textContent=`kaizen ${r.version} is up to date.`,n("upd-chk").focus()},!ie)return;let o=n("upd-go");o.onclick=async()=>{if(!confirm(`Update kaizen to ${ie.latest}? The board restarts, and agents running inside cards stop.`))return;if(o.disabled=!0,o.textContent="Updating…",!(await L("/upgrade",{})).ok){o.disabled=!1,o.textContent="Update kaizen";return}o.textContent="Restarting…",setTimeout(()=>location.reload(),20000)}}function Tn(){fetch("/version").then((e)=>e.json()).then((e)=>{if(En(e)&&B==="settings")ve()}).catch(()=>{})}var ks=(e)=>{let t=new Map;for(let o of e){if(!t.has(o.group))t.set(o.group,[]);t.get(o.group).push(o)}return[...t]};async function ve(){if(B!=="settings")return;let e=++a.pageGen;if(fe(!0),!n("panel").classList.contains("open")||n("ptitle").textContent!=="Settings")n("pbody").innerHTML=ht,n("psub").textContent="";we(),a.openKey=null,a.runDoc=null,n("pterm").hidden=!0,n("panel").classList.add("open"),n("scrim").classList.add("on"),n("ptitle").textContent="Settings",n("ptabs").innerHTML="",n("pfoot").innerHTML="";let t=document.documentElement.dataset.theme||"system",o=await fetch("/settings").then((u)=>u.json()).catch(()=>null),r=await fetch("/google").then((u)=>u.json()).catch(()=>({}));if(e!==a.pageGen)return;n("psub").textContent="",n("psub").title="";let l=(u,f,d)=>{if(u==="s-preset")return`<span class="bar" id="${u}" role="group" aria-label="Preset">${f.filter((m)=>m!=="custom").map((m)=>`<button id="${u}-${m}" data-v="${m}" class="${m===d?"on":""}" aria-pressed="${m===d}">${m}</button>`).join("")}</span>`;if(f.length===2&&f.includes("true")&&f.includes("false")){let m=d==="true";return`<button class="chk${m?" on":""}" id="${u}" data-v="${m?"false":"true"}" role="switch" aria-checked="${m}" title="${m?"On":"Off"}"></button>`}if(f.every((m)=>/^\d+$/.test(m)))return`<span class="bar" id="${u}" role="group">${f.map((m)=>`<button data-v="${m}" class="${m===d?"on":""}" aria-pressed="${m===d}">${m}</button>`).join("")}</span>`;return`<select class="sel" id="${u}">${f.map((m)=>`<option value="${p(m)}"${m===d?" selected":""}>${m===""?"—":p(m)}</option>`).join("")}</select>`},c=(u,f,d,m,x)=>`<div class="srow" data-q="${p(x.toLowerCase())}"><label for="${m}">${p(u)}</label>${f}${d?`<p class="hint">${d}</p>`:""}</div>`,g=document.activeElement?.id==="sq",y=typeof Notification>"u"?null:Notification.permission,v=y?y==="denied"?"Blocked by this browser":"":"Not supported in this browser";n("pbody").innerHTML=`<div class="sq"><input class="sel" id="sq" type="search" placeholder="Search settings" aria-label="Search settings" spellcheck="false" autocomplete="off" value="${p(a.setQ)}"></div>
17
- ${o?ks(o.rows).map(([u,f])=>`<h3 class="sg">${p(u)}</h3><div class="sgrp">${f.map((d)=>{let m="s-"+d.key.replace(/\./g,"-");return c(d.label,d.text?d.folder?`<span class="fpick"><input class="sel" id="${m}" value="${p(d.value)}" placeholder="not set" spellcheck="false"><button class="btn" id="${m}-pick" type="button">Browse…</button></span>`:`<input class="sel" id="${m}" value="${p(d.value)}" placeholder="not set" spellcheck="false">`:l(m,d.values,d.value),p(d.help),m,[d.label,d.help,d.key,u,...d.values].join(" "))}).join("")}</div>`).join(""):'<p class="snone">Settings could not be read. Run <code>kaizen settings</code>.</p>'}
11
+ }catch(err){postMessage({type:"error",sid:m.sid,seq:m.seq,error:String(err&&err.message||err)})}}`,bs='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)',ys=!!(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&window.AudioWorkletNode&&window.WebAssembly&&window.Worker),ke=null,je=null,bt=()=>{},fe=null,ze=!1,yt=new Set;async function vs(){let e="small";try{e=((await fetch("/settings").then((o)=>o.json())).rows.find((o)=>o.key==="dictation.model")||{}).value||e}catch(t){}if(!$n[e])e="small";if(je&&Ot!==e){try{ke.terminate()}catch(t){}ke=null,je=null,yt.clear(),fe=null}if(je)return je;return Ot=e,je=new Promise(async(t,o)=>{ke=new Worker(URL.createObjectURL(new Blob([gs],{type:"text/javascript"})),{type:"module"});let r={};ke.onmessage=(c)=>{let m=c.data;if(m.type==="progress"){r[m.file]=m.pct;let v=Object.values(r);bt(Math.round(v.reduce((k,f)=>k+f,0)/v.length))}else if(m.type==="ready")t();else if(m.type==="fallback")S("GPU could not run the model, using the CPU: "+m.why);else if(m.type==="error"&&m.seq==null)o(Error(m.error));for(let v of yt)v.onmsg(m)},ke.onerror=(c)=>o(Error(c.message||"worker failed"));let l=null;try{l=navigator.gpu&&await navigator.gpu.requestAdapter()}catch(c){}if(!l)S("No WebGPU: dictation will run on the CPU and be slower");ke.postMessage({type:"load",model:$n[Ot],webgpu:!!l,f16:!!(l&&l.features.has("shader-f16"))})}),je.catch(()=>{je=null;try{ke.terminate()}catch(t){}ke=null}),je}function ce(e){if(fe&&(e||!fe.field.isConnected))fe.stop()}function Ke(e){if(!ys)return null;let t=document.createElement("button");t.className="ib sm mic",t.title="Dictate",t.setAttribute("aria-label","Dictate"),t.type="button";let o=()=>{t.innerHTML=q("mic"),t.classList.remove("on","busy")};o();let r=e[0];return e.forEach((l)=>l.addEventListener("focus",()=>r=l)),t.onclick=async(l)=>{if(l.preventDefault(),l.stopPropagation(),fe){let E=fe.btn===t;if(fe.stop(),E)return}if(ze)return;ze=!0,t.classList.add("busy"),bt=(E)=>{t.innerHTML=`<i>${E}%</i>`};try{await vs()}catch(E){o(),bt=()=>{},ze=!1,S("Dictation model could not load: "+E.message);return}if(bt=()=>{},t.innerHTML=q("mic"),!t.isConnected){o(),ze=!1;return}let c;try{c=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,noiseSuppression:!0,echoCancellation:!0}})}catch(E){o(),ze=!1,S("Microphone not available: "+(E.name==="NotAllowedError"?"permission denied":E.message));return}if(fe)fe.stop();let m=new AudioContext({sampleRate:16000});await m.audioWorklet.addModule(URL.createObjectURL(new Blob([bs],{type:"text/javascript"})));let v=m.createMediaStreamSource(c),k=new AudioWorkletNode(m,"kz-tap");v.connect(k);let f=r,y=f.selectionStart??f.value.length,u=document.createElement("span");u.className="vu",u.setAttribute("aria-hidden","true"),u.innerHTML=[0.5,0.9,1.3,0.8,0.45].map((E)=>`<i style="--w:${E}"></i>`).join("");let h=[],d=0,g=0,x=0,H=0,T=0,z=new Map,U=!1,be=16000,A=6*be,I=0.8*be,G=1*be,Z=(E)=>{if(!E||!f.isConnected)return;let _=f.value;if(document.activeElement===f)y=f.selectionStart;y=Math.min(y,_.length);let ne=_.slice(0,y),ye=ne&&!/\s$/.test(ne)?" ":"";f.value=ne+ye+E+_.slice(y),y=ne.length+ye.length+E.length,f.selectionStart=f.selectionEnd=y,f.dispatchEvent(new Event("input",{bubbles:!0}))},N=()=>{if(!d||g<G){h=[],d=0,g=0,x=0;return}let E=new Float32Array(d),_=0;for(let ne of h)E.set(ne,_),_+=ne.length;h=[],d=0,g=0,x=0,z.set(H,null),ke.postMessage({type:"run",sid:P,seq:H,audio:E},[E.buffer]),H++,t.classList.add("busy")},D=()=>{while(z.has(T)&&z.get(T)!==null)Z(z.get(T)),z.delete(T),T++;if(!z.size){if(t.classList.remove("busy"),U)o(),yt.delete(ie)}};k.port.onmessage=(E)=>{let _=E.data;h.push(_),d+=_.length;let ne=0;for(let me=0;me<_.length;me++)ne+=_[me]*_[me];let ye=Math.sqrt(ne/_.length);if(ye>0.01)g+=_.length,x=0;else x+=_.length;{let me=Math.min(1,ye*8).toFixed(2);f.style.setProperty("--lvl",me),u.style.setProperty("--lvl",me)}if(d>=A||g>=G&&x>=I)N()};let P=Math.random().toString(36).slice(2),ie={btn:t,field:f,onmsg:(E)=>{if(E.sid!==P)return;if(E.type==="text")z.set(E.seq,E.text),D();else if(E.type==="error"&&E.seq!=null)z.set(E.seq,""),S("Dictation failed: "+E.error),D()},stop:()=>{if(U)return;U=!0,t.classList.remove("on"),f.classList.remove("dictating"),f.style.removeProperty("--lvl"),t.innerHTML=q("mic");try{k.port.onmessage=null,v.disconnect(),k.disconnect(),c.getTracks().forEach((E)=>E.stop()),m.close()}catch(E){}fe=null,N(),D()}};yt.add(ie),fe=ie,ze=!1,t.classList.remove("busy"),t.classList.add("on"),f.classList.add("dictating"),t.replaceChildren(u),f.focus()},t}var xn="kz-d:";function Ne(e,t){try{let o=sessionStorage.getItem("kz-d:"+t);if(o!=null)e.value=o}catch(o){}e.addEventListener("input",()=>{try{sessionStorage.setItem("kz-d:"+t,e.value)}catch(o){}})}function Sn(e){try{Object.keys(sessionStorage).forEach((t)=>{if(t.startsWith("kz-d:"+e))sessionStorage.removeItem(t)})}catch(t){}}var qn=86400000,Ln=(e,t)=>{let o=t-e;return o<3600000?Math.round(o/60000)+"m":o<86400000?Math.round(o/3600000)+"h":Math.round(o/86400000)+"d"},zt={claude:"Claude Code",codex:"Codex",agy:"Antigravity",opencode:"OpenCode",gemini:"Gemini",cursor:"Cursor",yolo:"yolo",unrechecked:"fix not rechecked"},Cn={claude:1,codex:1,agy:1,opencode:1,gemini:1,cursor:1,full:1,lite:1},An=["Ideas","Planning","Building","Review","Done"],Ee={idea:"idea",starting:"start",running:"run",stalled:"stall",waiting:"wait",done:"done",abandoned:"aband"},ks={"approvals.plan":"plan needs your approval","approvals.review":"review needs your approval"},nt=(e)=>ks[e]||e,Ue={idea:"idea",starting:"starting",running:"running",stalled:"stalled",waiting:"waiting on you",done:"done",abandoned:"abandoned"};function jn(e,t){let o=e&&e.match(new RegExp(`^##\\s+${t}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$(?![\\s\\S]))`,"m"));return o?o[1].trim():null}var vt=(e)=>jn(e,"Preview"),kt=(e,t)=>e.replace(/(!?)\[([^\]]*)\]\(([^)\s]+)\)/g,(o,r,l,c)=>{if(/^attachments\/[^)]+$/.test(c))return`${r}[${l}](/file?dir=${encodeURIComponent(t.state)}&p=${encodeURIComponent((t.kind==="run"?`runs/${t.id}/`:"")+c)})`;if(/^([a-z][\w+.-]*:|\/|#)/i.test(c))return o;return`${l} (\`${c}\`)`}),En=(e)=>(jn(e,"Commit")||"").replace(/^```\w*\n|\n```$/g,"").trim();function Tn(e){let t=(e||"").split(`
12
+ `),o=t.findIndex((k)=>/^#+\s*(\d+\.\s*)?open questions/i.test(k.trim()));if(o<0)return[];let r=/^#+/.exec(t[o].trim())[0].length,l=[],c=null,m=!1;for(let k of t.slice(o+1)){let f=k.trim(),y=/^#+(?=\s)/.exec(f);if(y&&y[0].length<=r)break;if(!y&&f&&!/^```/.test(f)&&!/^none\.?$/i.test(f))m=!0;let u=/^\**Q\d*\s*[:.)]\**\s*(.+)$/.exec(f);if(u){l.push(c={q:u[1].trim(),multi:/multi-?select/i.test(u[1]),options:[]});continue}let h=/^[-*]\s+(.+)$/.exec(f);if(h&&c){let d=h[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 v=l.filter((k)=>k.options.length);return v.length||!m?v:null}function Te(e){let t=/(^|[.!?)]\s+|\s)([A-Z][\w-]*(?: [a-z][\w-]*){0,3}):\s+/g,o=[],r;while(r=t.exec(e))if(!/^\s+$/.test(r[1])||!o.length&&!r[2].includes(" "))o.push([r.index+r[1].length,r[2],t.lastIndex]);if(o.length<2)return{head:e,parts:[]};let l=o.map(([,c,m],v)=>{let k=e.slice(m,v+1<o.length?o[v+1][0]:e.length).trim(),f=k.split(/\s*\((\d+)\)\s+/);if(f.length<5)return{label:c,body:k};let y=[];for(let h=1;h<f.length;h+=2)y.push([f[h],f[h+1]]);let u=y.every(([h],d)=>+h===d+1);return{label:c,body:(f[0]?f[0]+`
13
+ `:"")+y.map(([h,d],g)=>u?`${g+1}. ${d}`:`- (${h}) ${d}`).join(`
14
+ `)}});return{head:e.slice(0,o[0][0]).trim()||`${l[0].label}: ${e.slice(o[0][2],o[1][0]).trim()}`,parts:l}}function Me(e,t){let o=e.split(`
15
+ `),r=[],l=0,c=null,m=(k)=>p(k).replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\*\*([^*]+)\*\*/g,"<b>$1</b>").replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g,'<a href="$2" target="_blank"><img src="$2" alt="$1"></a>').replace(/\[([^\]]+)\]\(([^)\s]+)\)/g,'<a href="$2" target="_blank">$1</a>'),v=()=>{if(c)r.push(`</${c}>`),c=null};while(l<o.length){let k=o[l];if(/^```/.test(k)){v();let h=[];l++;while(l<o.length&&!/^```/.test(o[l]))h.push(o[l++]);r.push(`<pre>${p(h.join(`
16
+ `))}</pre>`),l++;continue}let f=/^(#{1,6})\s+(.*)$/.exec(k);if(f){v(),r.push(`<h${Math.min(f[1].length,3)}>${m(f[2])}</h${Math.min(f[1].length,3)}>`),l++;continue}if(/^---+$/.test(k.trim())){v(),r.push("<hr>"),l++;continue}if(/^\|/.test(k)){v();let h=[];while(l<o.length&&/^\|/.test(o[l]))h.push(o[l++]);let d=(g)=>g.replace(/^\||\|$/g,"").split("|").map((x)=>m(x.trim()));r.push("<table>"+h.filter((g)=>!/^\|\s*-/.test(g)).map((g,x)=>`<tr>${d(g).map((H)=>x?`<td>${H}</td>`:`<th>${H}</th>`).join("")}</tr>`).join("")+"</table>");continue}let y=/^\s*(?:[-*]|\d+\.)\s+(.*)$/.exec(k);if(y){let h=/^\s*\d+\./.test(k)?"ol":"ul";if(c!==h)v(),r.push(`<${h}>`),c=h;let d=t&&/^(open|done|rejected):\s*(.*)$/.exec(y[1]);r.push(d?`<li class="tk${d[1]==="rejected"?" rej":""}"><label><input type="checkbox" tabindex="-1" aria-disabled="true" onclick="return false"${d[1]==="done"?" checked":""} aria-label="${d[1]}"><span>${m(d[2])}</span></label></li>`:`<li>${m(y[1])}</li>`),l++;continue}if(!k.trim()){v(),l++;continue}v();let u=[k];l++;while(l<o.length&&o[l].trim()&&!/^(#|```|\||\s*[-*]\s|\s*\d+\.\s|---)/.test(o[l]))u.push(o[l++]);r.push(`<p>${m(u.join(" "))}</p>`)}return v(),r.join("")}function Mn(e,t){let o=[...e.matchAll(/^\s*-\s*(todo|doing|done):\s*(.*)$/gm)].map((m)=>[m[1],m[2]]);if(!o.length)return"";let r=o.filter((m)=>m[0]==="done").length,l=Math.round(r/o.length*100),c={done:"✓",doing:'<span class="pulse"></span>',todo:""};return`<div class="prog"><div class="ph"><b>${r} of ${o.length} done</b><span>${l}%</span></div><div class="pbar"><i style="width:${l}%"></i></div><ol>${o.map(([m,v],k)=>`<li class="p-${m}${t&&t[k]&&t[k]!==m?" chg":""}"><span class="pi">${c[m]}</span><span>${p(v).replace(/`([^`]+)`/g,"<code>$1</code>")}</span></li>`).join("")}</ol></div>`}var re=null,Ut=null;function Dn(e){Ut=e,re=e.update?e:null;let t=document.querySelector('.rail a[data-view="settings"]');return t.classList.toggle("upd",!!re),t.title=re?`Settings: kaizen ${e.latest} is available`:"Settings",!!re}function Gt(){let e=document.createElement("div");if(e.className=`updbar${re?"":" quiet"}`,!re)e.innerHTML=`<span id="upd-msg" aria-live="polite">${Ut?`kaizen ${p(Ut.version)}`:"kaizen"}</span><button class="btn" id="upd-chk">Check for updates</button>`;else{let r=(re.notes||[]).map((c)=>`<li><b>${p(c.v)}</b> ${p(c.text)}</li>`).join(""),l=r?`<span class="updnew"><button class="btn" aria-describedby="upd-notes">What's new</button><span class="updpop" id="upd-notes" role="tooltip"><ul>${r}</ul><a href="https://github.com/hfadhlullah/kaizen/releases" target="_blank" rel="noopener">All release notes</a></span></span>`:"";e.innerHTML=`<span>kaizen <b>${p(re.latest)}</b> is available. You are running ${p(re.version)}.</span><span class="updact">${l}<button class="btn" id="upd-chk">Check for updates</button><button class="btn pri" id="upd-go">Update kaizen</button></span>`}n("pbody").prepend(e);let t=n("upd-chk");if(t.onclick=async()=>{t.disabled=!0,t.textContent="Checking…";let r=await fetch("/version?fresh=1").then((l)=>l.json()).catch(()=>null);if(!e.isConnected)return;if(t.disabled=!1,!r||!r.latest){if(re)t.textContent="Could not check. Try again";else n("upd-msg").textContent="Could not reach npm. Try again later.";return}if(Dn(r),e.remove(),Gt(),re)n("upd-go").focus();else n("upd-msg").textContent=`kaizen ${r.version} is up to date.`,n("upd-chk").focus()},!re)return;let o=n("upd-go");o.onclick=async()=>{if(!confirm(`Update kaizen to ${re.latest}? The board restarts, and agents running inside cards stop.`))return;if(o.disabled=!0,o.textContent="Updating…",!(await C("/upgrade",{})).ok){o.disabled=!1,o.textContent="Update kaizen";return}o.textContent="Restarting…",setTimeout(()=>location.reload(),20000)}}function Pn(){fetch("/version").then((e)=>e.json()).then((e)=>{if(Dn(e)&&Q==="settings")we()}).catch(()=>{})}var ws=(e)=>{let t=new Map;for(let o of e){if(!t.has(o.group))t.set(o.group,[]);t.get(o.group).push(o)}return[...t]};async function we(){if(Q!=="settings")return;let e=++a.pageGen;if(ce(!0),!n("panel").classList.contains("open")||n("ptitle").textContent!=="Settings")n("pbody").innerHTML=ht,n("psub").textContent="";$e(),a.openKey=null,a.runDoc=null,n("pterm").hidden=!0,n("panel").classList.add("open"),n("scrim").classList.add("on"),n("ptitle").textContent="Settings",n("ptabs").innerHTML="",n("pfoot").innerHTML="";let t=document.documentElement.dataset.theme||"system",o=await fetch("/settings").then((u)=>u.json()).catch(()=>null),r=await fetch("/google").then((u)=>u.json()).catch(()=>({}));if(e!==a.pageGen)return;n("psub").textContent="",n("psub").title="";let l=(u,h,d)=>{if(u==="s-preset")return`<span class="bar" id="${u}" role="group" aria-label="Preset">${h.filter((g)=>g!=="custom").map((g)=>`<button id="${u}-${g}" data-v="${g}" class="${g===d?"on":""}" aria-pressed="${g===d}">${g}</button>`).join("")}</span>`;if(h.length===2&&h.includes("true")&&h.includes("false")){let g=d==="true";return`<button class="chk${g?" on":""}" id="${u}" data-v="${g?"false":"true"}" role="switch" aria-checked="${g}" title="${g?"On":"Off"}"></button>`}if(h.every((g)=>/^\d+$/.test(g)))return`<span class="bar" id="${u}" role="group">${h.map((g)=>`<button data-v="${g}" class="${g===d?"on":""}" aria-pressed="${g===d}">${g}</button>`).join("")}</span>`;return`<select class="sel" id="${u}">${h.map((g)=>`<option value="${p(g)}"${g===d?" selected":""}>${g===""?"—":p(g)}</option>`).join("")}</select>`},c=(u,h,d,g,x)=>`<div class="srow" data-q="${p(x.toLowerCase())}"><label for="${g}">${p(u)}</label>${h}${d?`<p class="hint">${d}</p>`:""}</div>`,m=document.activeElement?.id==="sq",v=typeof Notification>"u"?null:Notification.permission,k=v?v==="denied"?"Blocked by this browser":"":"Not supported in this browser";n("pbody").innerHTML=`<div class="sq"><input class="sel" id="sq" type="search" placeholder="Search settings" aria-label="Search settings" spellcheck="false" autocomplete="off" value="${p(a.setQ)}"></div>
17
+ ${o?ws(o.rows).map(([u,h])=>`<h3 class="sg">${p(u)}</h3><div class="sgrp">${h.map((d)=>{let g="s-"+d.key.replace(/\./g,"-");return c(d.label,d.text?d.folder?`<span class="fpick"><input class="sel" id="${g}" value="${p(d.value)}" placeholder="not set" spellcheck="false"><button class="btn" id="${g}-pick" type="button">Browse…</button></span>`:`<input class="sel" id="${g}" value="${p(d.value)}" placeholder="not set" spellcheck="false">`:l(g,d.values,d.value),p(d.help),g,[d.label,d.help,d.key,u,...d.values].join(" "))}).join("")}</div>`).join(""):'<p class="snone">Settings could not be read. Run <code>kaizen settings</code>.</p>'}
18
18
  <h3 class="sg">Google</h3><div class="sgrp">${c("OAuth client id",`<input class="sel" id="gg-id" placeholder="${r.hasClient?"saved; type to replace":"from Google Cloud, Desktop app"}" spellcheck="false" autocomplete="off">`,"","gg-id","google oauth client id sheets write back")}
19
19
  ${c("OAuth client secret",`<span class="fpick"><input class="sel" id="gg-secret" type="password" placeholder="${r.hasClient?"saved; type to replace":""}" spellcheck="false" autocomplete="off"><button class="btn" id="gg-save" type="button">Save</button></span>`,"","gg-secret","google oauth client secret")}
20
20
  ${c("Google account",`<button class="btn${r.connected?"":" pri"}" id="gg-conn" type="button"${r.hasClient||r.connected?"":" disabled"}>${r.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",l("th",["system","light","dark"],t),"","th","theme light dark system browser")}
22
- ${c("Notifications",`<button class="chk${y==="granted"?" on":""}" id="nt" role="switch" aria-checked="${y==="granted"}"${y&&y!=="denied"?"":" disabled"}></button>`,v,"nt","notifications "+v+" browser")}</div>
23
- <p class="snone" id="snone" hidden></p>`,Ot();let h=[...n("pbody").querySelectorAll(".sg")],k=()=>{let u=a.setQ.trim().toLowerCase(),f=!1;h.forEach((d)=>{let m=!1;for(let x of d.nextElementSibling.children)x.hidden=!!u&&!x.dataset.q.includes(u),m||=!x.hidden;d.hidden=!m,f||=m}),n("snone").hidden=f,n("snone").textContent=`No setting matches “${a.setQ.trim()}”.`};if(n("sq").oninput=(u)=>{a.setQ=u.target.value,k()},n("sq").onkeydown=(u)=>{if(u.key!=="Escape")return;if(u.preventDefault(),a.setQ)a.setQ="",u.target.value="",k();else Z()},k(),g){let u=n("sq");u.focus(),u.setSelectionRange(u.value.length,u.value.length)}if(n("gg-save").onclick=async()=>{let u=await L("/google",{op:"client",id:n("gg-id").value,secret:n("gg-secret").value});if(u.ok)S("Saved");else S(u.error||"Enter both the client id and the secret");ve()},n("gg-conn").onclick=async()=>{if(r.connected)return await L("/google",{op:"disconnect"}),ve();let u=window.open("","_blank"),f=await L("/google",{op:"connect"});if(f.ok&&u)u.location=f.url;else u?.close(),S(f.error||"Could not start Google sign-in")},n("th").onchange=(u)=>setTheme(u.target.value),n("nt").onclick=()=>{ye(),setTimeout(ve,300)},o)o.rows.forEach((u)=>{let f=n("s-"+u.key.replace(/\./g,"-")),d=async(m)=>{let x=document.activeElement?.id;if((await L("/settings",{key:u.key,value:m})).ok)S("Saved");if(await ve(),x?.startsWith("s-preset-"))n(x)?.focus()};if(f.tagName==="SELECT"||f.tagName==="INPUT")f.onchange=async()=>{if(await d(f.value.trim()),f.tagName==="INPUT")R()};else f.querySelectorAll("[data-v]").forEach((m)=>m.onclick=()=>d(m.dataset.v));if(f.matches(".chk"))f.onclick=()=>d(f.dataset.v);if(u.folder)n(f.id+"-pick").onclick=async()=>{let m=await L("/pick-folder",{});if(m.path)await d(m.path),R()}})}var B="board",zt=(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":"Board"};function ce(e,t){if(a.pageGen++,e==="icebox")e="sources";if(!["settings","sources"].includes(e))e="board";let o=B;if(B=e,zt(e),document.body.classList.toggle("paged",e!=="board"),document.body.classList.toggle("ice",e==="sources"),t&&e!==o)history.pushState(null,"",e==="board"?location.pathname:"?view="+(e==="sources"?"icebox":e));let r=n("panel");if(e==="board"){if(o==="board")return;r.style.transition="none",r.classList.remove("open","docked"),r.offsetWidth,r.style.transition="",Z();return}if(r.classList.add("docked"),e==="settings")ve();else P()}function Mn(){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")a.setQ="";ce(e.dataset.view,!0)}}),document.querySelector(".rail .home").onclick=(e)=>{if(e.ctrlKey||e.metaKey||e.shiftKey||e.button)return;e.preventDefault(),ce("board",!0)},addEventListener("popstate",()=>ce(new URLSearchParams(location.search).get("view"))),addEventListener("kz-theme",()=>{if(B==="settings")ve()})}var Pn="Icebox",se=null,ot={},Gt=(e)=>{if(!ot[e])try{ot[e]=localStorage.getItem("kz-reply-style:"+e)}catch(t){}return ot[e]||"casual"},vs={casual:"Casual",fun:"Fun",helpful:"Helpful",promo:"Promo"},oe=null,$e=null,de=null,ue="",ee="",ze="link",nt=null,Ge=!1,kt=!1,vt=null,Q=new Set,st={x:"X",tiktok:"TikTok",instagram:"Instagram",threads:"Threads"},J=()=>n("panel").classList.contains("open")&&n("ptitle").textContent===Pn,Pe=(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 Dn(e,t){let o=n(e);if(!o)return;let r=o.querySelector("button.sel"),l=o.querySelector(".menu-pop"),c=l.querySelector("input"),g=l.querySelector(".pick-list"),y=(h)=>{if(l.hidden=!h,r.setAttribute("aria-expanded",h),h)c.value="",v(),c.focus()},v=()=>{let h=c.value.trim().toLowerCase(),k=null,u=!1;for(let f of g.children)if(f.classList.contains("grp")){if(k)k.hidden=!u;k=f,u=!1}else{let d=!h||f.dataset.q.includes(h)||k&&k.dataset.g.includes(h);f.hidden=!d,u||=d}if(k)k.hidden=!u};r.onclick=()=>y(l.hidden),c.oninput=v,c.onkeydown=(h)=>{if(h.key==="Escape")h.stopPropagation(),y(!1),r.focus();else if(h.key==="Enter")h.preventDefault(),g.querySelector("button:not([hidden])")?.click()},g.onclick=(h)=>{let k=h.target.closest("button[data-v]");if(!k)return;y(!1),t(k.dataset.v)}}async function P(){if(B!=="sources")return;let e=++a.pageGen;fe(!0);let t=J()?[n("src-url")?.value||"",n("src-label")?.value||"",document.activeElement?.id,n("src-dir")?.value,n("src-cat")?.value||"",n("src-plat")?.value,n("src-q")?.value||""]:["",""],o=new Map(J()?[...n("pbody").querySelectorAll("[data-mv]")].map((s)=>[s.dataset.k,s.value]):[]),r=J()&&document.activeElement?.closest?.(".ice-row"),l=r?[r.dataset.k,document.activeElement.id.replace(/-\d+$/,"")]:null,c=J()&&n("ice-title")?[n("ice-title").dataset.k,n("ice-title").value,n("ice-details").value]:null,g=new Map(J()?[...n("pbody").querySelectorAll("[data-ct]")].map((s)=>[s.dataset.k,s.value]):[]);if(!J())n("pbody").innerHTML=ht;we(),a.openKey=null,a.runDoc=null,n("pterm").hidden=!0,n("panel").classList.add("open"),n("scrim").classList.add("on"),n("ptitle").textContent=Pn,n("ptabs").innerHTML="",n("pfoot").innerHTML="",n("psub").textContent="",n("psub").title="";let y=a.all?null:a.S.dir,v=y?"?dir="+encodeURIComponent(y):"",h=await fetch("/sources"+v).then((s)=>s.json()).catch(()=>null);if(e!==a.pageGen||!J())return;let k=(s)=>{try{return new URL(s).hostname}catch{return s}},u=(s,i,b)=>{if(st[b])return`<span class="fav">${q("b-"+b)}</span>`;let w;try{let z=new URL(s);if(/^https?:$/.test(z.protocol))w=z.origin}catch{}let A=!w?"":s.includes("docs.google.com/spreadsheets")?"https://ssl.gstatic.com/docs/spreadsheets/favicon3.ico":s.includes("docs.google.com/document")?"https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico":w+"/favicon.ico";return`<span class="fav">${A?`<img src="${p(A)}" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.remove()">`:""}${q(i)}</span>`},f=(s)=>st[s?.social]||(s?k(s.url).replace(/^www\./,""):"")||"Other",d=(s)=>{if(s.social)return"social";let i=s.url;if(i.includes("docs.google.com/spreadsheets")||i.endsWith(".csv")||i.endsWith(".tsv"))return"sheet";if(i.includes("docs.google.com/document"))return"doc";return"link"},m=h&&h.sources||[],x=[...h&&h.icebox||[],...h&&h.dismissed||[]],N=(h&&h.icebox||[]).length,T=h&&h.gatherer||{},I=T.use==="chatgpt"&&T.connected,O=(s,i)=>(s&&!a.S.projects.some((b)=>b.dir===s)?`<option value="${p(s)}" title="${p(i||s)}" selected>${p(me(i||s))}</option>`:"")+a.S.projects.map((b)=>`<option value="${p(b.dir)}" title="${p(b.label)}"${b.dir===s?" selected":""}>${p(me(b.label))}</option>`).join(""),ge=m.map((s,i)=>{let b=d(s),w=s.label||k(s.url),A=s.social&&(!s.last||!s.last.ok&&/gathered by your agent/i.test(s.last.note))?`<span class="src-badge dim">${q("at")}${I?"Gathered with ChatGPT":"Gathered by your agent"}</span>`:!s.last?'<span class="src-badge dim">Not gathered yet</span>':s.last.ok?`<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>${p(s.last.note)}</span><span class="hint" style="margin:0">${Pe(s.last.at)}</span>`:`<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: ${p(s.last.note)}</span><span class="hint" style="margin:0">${Pe(s.last.at)}</span>`,z=b==="sheet"?"sheet":b==="doc"?"doc":b==="social"?"at":"link",Y=s.url.replace(/^https?:\/\/(www\.)?/,"");return`<div class="src-card" id="src-card-${i}"><div class="src-hd"><span class="src-ico" title="${b==="social"?p(st[s.social]||"social"):b}">${u(s.url,z,s.social)}</span><div class="src-info"><span class="src-title" title="${p(w)}">${p(w)}</span><a class="src-url" href="${p(s.url)}" target="_blank" rel="noopener noreferrer" title="${p(s.url)}"><span>${p(Y)}</span>${q("ext")}</a></div><div class="src-acts">${s.social?`<button class="ib sm" id="src-ag-${i}" data-ag="${i}" aria-label="Gather ${p(w)} with your agent" title="${I?"Gather with your ChatGPT plan":"Gather: opens your agent on /kaizen gather"}">${q("refresh")}</button>`:`<button class="ib sm" id="src-chk-${i}" data-chk="${i}" aria-label="Check ${p(w)} now" title="Check now">${q("refresh")}</button>`}<button class="ib sm danger" id="src-rm-${i}" data-rm="${i}" aria-label="Remove ${p(w)}" title="Remove source">${q("trash")}</button></div></div><div class="src-status">${A}</div><div class="src-fields"><label class="src-f" title="Category"><span>Category</span><input class="sel" id="src-ct-${i}" data-ct="${i}" data-k="${p(s.dir+" "+s.url)}" value="${p(s.category||"")}" placeholder="${p(f(s))}" maxlength="40" aria-label="Category of ${p(w)}"><button class="btn sm pri" id="src-cs-${i}" aria-label="Save the category of ${p(w)}" disabled>Save</button></label><label class="src-f" title="Project"><span>Project</span>${Ue(s.dir,s.project||"")}<select class="sel" id="src-mv-${i}" data-mv="${i}" data-k="${p(s.dir+" "+s.url)}" aria-label="Project of ${p(w)}">${O(s.dir,s.project)}</select><button class="btn sm pri" id="src-mb-${i}" aria-label="Move ${p(w)} to the chosen project" disabled>Move</button></label>${b==="sheet"||/github\.com/.test(s.url)?`<label class="src-wb"><input type="checkbox" id="src-wb-${i}" data-wb="${i}"${s.writeback?" checked":""}> Write back</label>`:""}</div></div>`}).join(""),C=(h&&h.failed||[]).map((s)=>`<p style="color:var(--dim)">The sources of ${p(s)} could not be read.</p>`).join(""),H=`<div class="src-empty"><div class="src-empty-ico">${q("link")}</div><h4>No sources yet</h4></div>`,U=ze==="social",G=!m.length||Ge||[t[0],t[1],t[4],t[6]].some(Boolean),W=`<div class="src-add" id="src-addbox"${G?"":" 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="${!U}">Link</button><button id="src-k-social" data-kind="social" aria-pressed="${U}">Social</button></div></div><label for="src-url"${U?" hidden":""}><span>Link</span><input class="sel" id="src-url" placeholder="Link" spellcheck="false" autocomplete="off"></label><div class="two plat"${U?"":" hidden"}><label for="src-plat"><span>Platform</span><select class="sel" id="src-plat">${Object.entries(st).map(([s,i])=>`<option value="${s}">${i}</option>`).join("")}</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="${U?"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="${U?"e.g. Social":"e.g. Bugs"}" maxlength="40"></label></div><label for="src-dir"><span>Project</span><select class="sel" id="src-dir">${y?"":'<option value="">Choose project…</option>'}${O(y)}</select></label><div class="acts"><span class="end"></span><button class="btn pri" id="src-add">Add source</button></div></div>`,K=(s)=>{let i=m.find((b)=>b.dir===s.dir&&b.url===s.source);return i?i.label||k(i.url):k(s.source)},D=(s)=>s.dir+" "+s.id,ae=(s)=>s.dir+" "+s.source,E=(s)=>m.find((i)=>i.dir===s.dir&&i.url===s.source),F=(s)=>s.status==="dismissed"?"Dismissed":s.status==="done"?"Done":s.category||E(s)?.category||f(E(s)||{url:s.source}),te=(s,i)=>{let b=new Map;return s.forEach((w)=>{let A=i(w);b.set(A,{it:w,n:(b.get(A)?.n||0)+1})}),b},re=y?new Map:te(x,(s)=>s.dir);if(!y){for(let s of a.shown)if(!re.has(s)){let i=xe().find((b)=>b.dir===s);if(i)re.set(s,{it:{dir:s,project:i.label},n:0})}}let le=(s)=>y||!a.shown.size||a.shown.has(s.dir),He=x.filter(le),Je=te(He,F);if(ee&&!Je.has(ee))ee="";let ts=He.filter((s)=>!ee||F(s)===ee),Ve=te(ts,ae);if(ue&&!Ve.has(ue))ue="";let ns=new Set(x.map(D));if(oe&&!ns.has(oe))oe=null;if($e!==oe)$e=null;let Ye=new Map;x.forEach((s,i)=>{if(!le(s)||(ee?F(s)!==ee:s.status==="dismissed"||s.status==="done")||ue&&ae(s)!==ue)return;let b=F(s);Ye.has(b)||Ye.set(b,[]),Ye.get(b).push(i)});let _=[...Ye.values()].flat();{let s=new Set(_.map((i)=>D(x[i])));[...Q].forEach((i)=>s.has(i)||Q.delete(i))}if(!_.some((s)=>D(x[s])===de))de=_.length?D(x[_[0]]):null;let Xe=_.filter((s)=>Q.has(D(x[s]))).length,ss=re.size>1,rn=(s,i,b,w)=>`<div class="combo ice-combo" id="${s}"><button class="sel" aria-label="${i}" aria-haspopup="listbox" aria-expanded="false">${b}</button><div class="menu-pop" hidden><input class="sel" aria-label="Search ${i.toLowerCase()}" placeholder="Search…" spellcheck="false" autocomplete="off"><div class="pick-list" role="listbox">${w}</div></div></div>`,lt=(s,i,b,w,A="")=>`<button type="button" role="option" data-v="${p(s)}" data-q="${p(w.toLowerCase())}" class="${i?"on":""}${A}" aria-selected="${i}">${b}</button>`,ct=new Map;for(let[s,{it:i}]of Ve){let b=f(E(i)||{url:i.source});ct.has(b)||ct.set(b,[]),ct.get(b).push([s,i])}let Dt=(s)=>u(s.source,"link",E(s)?.social),Pt=ue&&[...Ve].find(([s])=>s===ue)?.[1].it,ln=(Je.size>1||Je.has("Dismissed")||Je.has("Done")?rn("ice-fc","Category",`<span>${p(ee||"All categories")}</span>`,lt("",!ee,"<span>All categories</span>","all categories")+[...Je].map(([s,{n:i}])=>lt(s,ee===s,`<span>${p(s)}</span><i>${i}</i>`,s)).join("")):"")+(Ve.size>1?rn("ice-fs","Source",Pt?`${Dt(Pt)}<span>${p(K(Pt))}</span>`:"<span>All sources</span>",lt("",!ue,"<span>All sources</span>","all sources")+[...ct].map(([s,i])=>`<div class="grp" data-g="${p(s.toLowerCase())}">${Dt(i[0][1])}${p(s)}</div>`+i.map(([b,w])=>lt(b,ue===b,`${Dt(w)}<span>${p(K(w).toLowerCase().startsWith(s.toLowerCase()+":")?K(w).slice(s.length+1).trim():K(w))}</span><i>${Ve.get(b).n}</i>`,s+" "+K(w)," sub")).join("")).join("")):""),os=`<div class="ice-bar">${_.length?`<input type="checkbox" id="ice-all" aria-label="Select every request shown"${Xe&&Xe===_.length?" checked":""}>`:""}<span class="count">${ee==="Dismissed"||ee==="Done"?`${_.length} ${ee.toLowerCase()}`:`${_.length<N?`${_.length} of ${N}`:N} waiting`}</span><span class="end"></span>${ln?`<details class="ice-filt"><summary class="ib${ee||ue?" on":""}" aria-label="Filter requests" title="Filter requests">${q("filter")}</summary><div class="ice-fpop">${ln}</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"${m.length?"":" disabled"}>${q("refresh")} Gather all</button><button class="btn ghost sm" id="src-tog" aria-expanded="${nt??!m.length}" aria-controls="src-sec">${q("link")} Sources · ${m.length}</button></div>`,cn=Q.size?`<div class="ice-bulk" role="toolbar" aria-label="Selected requests"><span>${Q.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>`:"",as=(s)=>`<div class="ice-form"><label for="ice-title" class="hint" style="margin:0">Idea title</label><input class="sel" id="ice-title" data-k="${p(D(s))}" maxlength="200" value="${p(s.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>`,is=(s)=>{if(!s.post)return"";if(s.replied)return`<span class="hint" style="margin:0">You replied to this post ${Pe(s.replied)}.</span>`;let i=se&&se.k===D(s)?se:null,b=(w)=>`<select class="sel" id="ice-rst" aria-label="Reply style"${w?" disabled":""}>${Object.entries(vs).map(([A,z])=>`<option value="${A}"${A===Gt(s.dir)?" selected":""}>${z}</option>`).join("")}</select>`;if(!i)return`<div class="ice-form-acts"><span class="hint">You review it before it posts.</span>${T.connected?b(!1):""}<button class="btn sm" id="ice-rp">Reply</button></div>`;if(i.wait)return'<div class="ice-form-acts"><span class="hint">Drafting…</span></div>';return`<div class="ice-form"><label for="ice-reply" class="hint" style="margin:0">Your reply${i.note?` · ${p(i.note)}`:""}</label><textarea class="sel" id="ice-reply" rows="3" maxlength="2000" aria-label="Your reply"${i.busy||i.again?" disabled":""}>${p(i.text)}</textarea><span class="hint" id="ice-rn" style="margin:0">${i.again?"Writing another…":`${i.text.length} chars`}</span><div class="ice-form-acts"><span class="end"></span>${T.connected?`${b(i.busy||i.again)}<button class="btn ghost sm" id="ice-rg"${i.busy||i.again?" disabled":""}>Regenerate</button>`:""}<button class="btn ghost sm" id="ice-rc"${i.busy?" disabled":""}>Cancel</button><button class="btn pri sm" id="ice-rs"${i.busy||i.again||!i.text.trim()?" disabled":""}>${i.busy?"Sending…":"Send reply"}</button></div></div>`},rs=(s)=>{let i=x[s],b=D(i),w=oe===b;return`<div class="ice-row${de===b?" cur":""}${Q.has(b)?" sel":""}" data-i="${s}" data-k="${p(b)}"><input type="checkbox" id="ice-ck-${s}" data-ck="${s}"${Q.has(b)?" checked":""} aria-label="Select: ${p(i.text)}"><span class="ice-src" title="${p(K(i))}">${u(i.source,"link",m.find((A)=>A.dir===i.dir&&A.url===i.source)?.social)}</span><button class="ice-t" id="ice-o-${s}" data-o="${s}" aria-expanded="${w}" title="${p(i.text)}">${p(i.text)}</button><span class="ice-meta">${ss?`<span class="nm" title="${p(i.project||"")}">${Ue(i.dir,i.project||"")}${p(me(i.project||""))}</span>`:""}${i.status==="done"&&i.written?`<span title="Written back ${p(Pe(i.written))}">written back</span>`:""}${i.status==="done"&&i.writeError?`<span title="${p(i.writeError)}">write-back failed</span>`:""}${i.status==="promoted"?`<span title="Promoted to Ideas: ${p(i.idea||"")}">promoted</span>`:""}${i.replied?`<span title="Replied ${p(Pe(i.replied))}">replied</span>`:""}<span class="ice-age">${Pe(i.at)}</span><span class="ice-ra">${i.status==="promoted"||i.status==="done"?"":`<button class="btn ghost sm" id="ice-pr-${s}" data-pr="${s}" title="Promote to Ideas (p)" aria-label="Promote to Ideas: ${p(i.text)}">${q("plus")}</button>`}${i.status==="promoted"||i.status==="done"?"":i.status==="dismissed"?`<button class="btn ghost sm" id="ice-rs-${s}" data-rs="${s}" title="Restore to waiting" aria-label="Restore: ${p(i.text)}">Restore</button>`:`<button class="btn ghost sm" id="ice-ds-${s}" data-ds="${s}" title="Dismiss (d)" aria-label="Dismiss: ${p(i.text)}">${q("x")}</button>`}</span></span></div>`+(w?`<div class="ice-open">${i.notes?`<div class="ice-notes">${p(i.notes)}</div>`:""}${(i.images||[]).filter((A)=>/^https:\/\//.test(A)).length?`<details class="ice-imgs"><summary>Show images (${i.images.filter((A)=>/^https:\/\//.test(A)).length}) — loads them from the source's site</summary>${i.images.filter((A)=>/^https:\/\//.test(A)).map((A,z)=>{let Y=`/ice-image?dir=${encodeURIComponent(i.dir)}&id=${encodeURIComponent(i.id)}&i=${z}`;return`<a href="${p(Y)}" target="_blank" rel="noopener noreferrer"><img data-src="${p(Y)}" alt="" referrerpolicy="no-referrer"></a>`}).join("")}</details>`:""}<span class="hint" style="margin:0">From ${p(K(i))} · ${Pe(i.at)}${i.post?` · <a href="${p(i.post)}" target="_blank" rel="noopener">open post</a>`:""}</span>${is(i)}${$e===b?as(i):""}</div>`:"")},ls=N&&!_.length?'<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>':_.length?`<div class="ice-list">${[...Ye].map(([s,i])=>`<div class="ice-cat">${p(s)}<i>${i.length}</i></div>${i.map(rs).join("")}`).join("")}</div>`:'<div class="ice-zero"><h4>Nothing waiting</h4></div>',dt=nt??!m.length,Rt=dt&&!n("src-sec")?.classList.contains("open"),cs=`<div class="src-scrim${dt&&!Rt?" on":""}" id="src-scrim"></div><aside class="src-sec${dt&&!Rt?" open":""}" id="src-sec" aria-label="Sources"${dt?"":" inert"}><header><div class="t"><b>Sources</b><div class="src-chips"><a id="src-to-settings" class="schip" role="button" tabindex="0" title="${h&&h.every&&h.every!=="off"?`Checked every ${p(h.every)}`:"Auto-check off"}: change in Settings">${q("clock")}${h&&h.every&&h.every!=="off"?p(h.every):"Off"}</a>${T.connected?`<span class="schip on" title="ChatGPT${T.email?": "+p(T.email):""}">${q("ai")}ChatGPT<a id="cg-out" role="button" tabindex="0" aria-label="Disconnect ChatGPT" title="Disconnect">${q("x")}</a></span>`:`<a id="cg-in" class="schip" role="button" tabindex="0" title="Connect ChatGPT">${q("ai")}ChatGPT</a>`}${(h.google||{}).connected?`<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>`:`<a id="gg-in" class="schip" role="button" tabindex="0" title="Connect Google">${q("google")}Google</a>`}</div></div><div class="src-hda">${m.length?`<button class="btn sm" id="src-new" aria-expanded="${G}" 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">${W}${m.length?`<div class="src-list">${ge}</div>`:H}</div></aside>`,ds=T.use==="ask"&&!T.connected?'<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>':"",us=n("src-sec")?.querySelector(".body")?.scrollTop??0;if(n("pbody").innerHTML=h&&h.error?`<p style="color:var(--dim)">${p(h.error)}</p>`:!h?'<p style="color:var(--dim)">The icebox could not be read.</p>':ds+os+cn+C+ls+cs,!h||h.error)return;let dn=n("src-sec")?.querySelector(".body");if(dn)dn.scrollTop=us;if(c&&c[0]===oe&&n("ice-title"))n("ice-title").value=c[1],n("ice-details").value=c[2];if(n("src-add")){if(t[5])n("src-plat").value=t[5];if(n("src-q").value=t[6]||"",n("src-url").value=t[0],n("src-label").value=t[1],n("src-cat").value=t[4]||"",t[3]!==void 0&&n("src-dir"))n("src-dir").value=t[3]}if(n("src-to-settings"))n("src-to-settings").onclick=()=>{a.setQ="sources",ce("settings",!0)};let ps=async()=>{let s=window.open("","_blank"),i=await L("/chatgpt",{op:"connect"});if(i.ok&&s)s.location=i.url;else if(s?.close(),i.ok)location.href=i.url};for(let s of["cg-go","cg-in"])if(n(s))n(s).onclick=ps;if(n("gg-in"))n("gg-in").onclick=async()=>{if(!(h.google||{}).hasClient){S("Add your Google client in Settings first"),a.setQ="google",ce("settings",!0);return}let s=window.open("","_blank"),i=await L("/google",{op:"connect"});if(i.ok&&s)s.location=i.url;else if(s?.close(),i.ok)location.href=i.url;else S(i.error||"Could not start Google sign-in")};if(n("gg-out"))n("gg-out").onclick=async()=>{if(!confirm("Disconnect Google? Done requests stop being written to sheets."))return;if((await L("/google",{op:"disconnect"})).ok)S("Google disconnected"),P()};if(n("cg-skip"))n("cg-skip").onclick=async()=>{if((await L("/chatgpt",{op:"skip"})).ok)P()};if(n("cg-out"))n("cg-out").onclick=async()=>{if(!confirm("Disconnect ChatGPT? Gather goes back to your agent."))return;if((await L("/chatgpt",{op:"disconnect"})).ok)S("ChatGPT disconnected"),P()};if(Rt)n("src-sec").offsetWidth,n("src-sec").classList.add("open"),n("src-scrim").classList.add("on");let ut=()=>{nt=!1,n("src-sec").classList.remove("open"),n("src-scrim").classList.remove("on"),n("src-sec").inert=!0,n("src-tog").setAttribute("aria-expanded","false"),n("src-tog").focus()};if(n("src-x").onclick=n("src-scrim").onclick=ut,n("src-new"))n("src-new").onclick=()=>{let s=n("src-addbox");if(Ge=s.hidden,s.hidden=!Ge,n("src-new").setAttribute("aria-expanded",Ge),Ge)n(ze==="social"?"src-q":"src-url").focus()};if(n("src-sec").onkeydown=(s)=>{if(s.key==="Escape")s.stopPropagation(),ut()},n("src-tog").onclick=()=>{if(n("src-sec").classList.contains("open"))return ut();nt=!0,P().then(()=>nt&&n("src-url")?.focus())},Dn("ice-fs",(s)=>{ue=s,P()}),Dn("ice-fc",(s)=>{ee=s,P()}),n("ice-unf"))n("ice-unf").onclick=()=>{if(ee=ue="",!y)a.shown.clear();wt(),P()};n("pbody").querySelectorAll("[data-kind]").forEach((s)=>s.onclick=()=>{if(ze===s.dataset.kind)return;ze=s.dataset.kind,P().then(()=>n(ze==="social"?"src-q":"src-url")?.focus())});let Me=async(s,i)=>{let b=await L("/sources",s);if(b.ok)i(b),R();if(J())P()};n("pbody").querySelectorAll("[data-mv]").forEach((s)=>{let i=m[+s.dataset.mv],b=n("src-mb-"+s.dataset.mv),w=o.get(s.dataset.k);if(w&&[...s.options].some((A)=>A.value===w))s.value=w;(s.onchange=()=>{b.disabled=s.value===i.dir})(),b.onclick=()=>Me({op:"move",dir:i.dir,to:s.value,url:i.url},()=>S("Source moved"))}),n("pbody").querySelectorAll("[data-wb]").forEach((s)=>{let i=m[+s.dataset.wb];s.onchange=()=>{if(s.checked&&i.social===void 0&&/docs\.google\.com/.test(i.url)&&!(h.google||{}).connected){alert("Connect Google first (Sources header), so kaizen can write to the sheet."),s.checked=!1;return}if(s.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?")){s.checked=!1;return}Me({op:"writeback",dir:i.dir,url:i.url,on:s.checked},()=>S(s.checked?"Write back on":"Write back off"))}}),n("pbody").querySelectorAll("[data-ct]").forEach((s)=>{let i=m[+s.dataset.ct],b=n("src-cs-"+s.dataset.ct),w=g.get(s.dataset.k);if(w!==void 0)s.value=w;(s.oninput=()=>{b.disabled=s.value.trim()===(i.category||"")})(),s.onkeydown=(A)=>{if(A.key==="Enter"&&!b.disabled)b.click()},b.onclick=()=>{b.disabled=!0,Me({op:"category",dir:i.dir,url:i.url,category:s.value.trim()},()=>S(s.value.trim()?`New requests from ${i.label||k(i.url)} go under ${s.value.trim()}`:"Category cleared"))}});let un=()=>{if(!document.activeElement||document.activeElement===document.body)n("pbody").querySelector(".ice-row.cur .ice-t")?.focus()},pn=(s)=>{let i=_.findIndex((A)=>D(x[A])===de);if(i<0||!s.has(de))return;let b=(A)=>!s.has(D(x[A])),w=_.slice(i+1).find(b)??_.slice(0,i).reverse().find(b);de=w===void 0?null:D(x[w])},Kt=(s,i,b,w={})=>(async()=>{if(pn(new Set([D(i)])),(await L("/icebox",{op:s,dir:i.dir,ids:[i.id],...w})).ok){if(s==="promote"&&oe===D(i))oe=$e=null;S(b),R()}if(J())P().then(un)})(),fn=async(s)=>{let i=x.filter((w)=>Q.has(D(w)));if(!i.length)return;pn(new Set(i.map(D))),kt=!0,n("ice-bp").disabled=n("ice-bd").disabled=!0;let b=0;try{if(s==="dismiss")for(let w of new Set(i.map((A)=>A.dir))){let A=i.filter((z)=>z.dir===w).map((z)=>z.id);if((await L("/icebox",{op:s,dir:w,ids:A})).ok)b+=A.length}else for(let w of i)if((await L("/icebox",{op:s,dir:w.dir,ids:[w.id]})).ok)b++}finally{kt=!1}if(Q.clear(),b)S(`${b} ${s==="promote"?"promoted to Ideas":"dismissed"}${b<i.length?`, ${i.length-b} not`:""}`);if(R(),J())P().then(un)};if(cn)n("ice-bp").onclick=()=>fn("promote"),n("ice-bd").onclick=()=>fn("dismiss"),n("ice-bc").onclick=()=>{Q.clear(),P()};if(n("ice-all"))n("ice-all").onchange=()=>{let s=_.map((i)=>D(x[i]));if(Xe===_.length)s.forEach((i)=>Q.delete(i));else s.forEach((i)=>Q.add(i));P()};if(n("ice-all"))n("ice-all").indeterminate=Xe>0&&Xe<_.length;n("pbody").querySelectorAll("[data-ck]").forEach((s)=>s.onchange=()=>{let i=D(x[+s.dataset.ck]);de=i,s.checked?Q.add(i):Q.delete(i),P()});let hn=(s)=>{let i=D(x[s]);de=i,oe=oe===i?null:i,$e=null,P().then(()=>n("ice-o-"+s)?.focus())};n("pbody").querySelectorAll(".ice-row").forEach((s)=>s.onclick=(i)=>{if(!i.target.closest("input,button"))hn(+s.dataset.i)}),n("pbody").querySelectorAll("[data-o]").forEach((s)=>s.onclick=()=>hn(+s.dataset.o)),n("pbody").querySelectorAll("details.ice-imgs").forEach((s)=>s.ontoggle=()=>s.open&&s.querySelectorAll("img[data-src]").forEach((i)=>i.src=i.dataset.src)),n("pbody").querySelectorAll("[data-pr]").forEach((s)=>s.onclick=()=>{de=oe=$e=D(x[+s.dataset.pr]),P().then(()=>{n("ice-title")?.focus(),n("ice-title")?.select()})}),n("pbody").querySelectorAll("[data-ds]").forEach((s)=>s.onclick=()=>{s.disabled=!0,Kt("dismiss",x[+s.dataset.ds],"Dismissed")}),n("pbody").querySelectorAll("[data-rs]").forEach((s)=>s.onclick=()=>{s.disabled=!0,Kt("restore",x[+s.dataset.rs],"Restored")});let Ze=x.find((s)=>D(s)===oe);if(n("ice-rp"))n("ice-rp").onclick=async()=>{let s=Ze,i=D(s);se={k:i,text:"",wait:!0},P();let b=await L("/reply",{op:"draft",dir:s.dir,id:s.id,style:Gt(s.dir)});if(!se||se.k!==i)return;se={k:i,text:b.text||"",note:b.ok?b.note||"":"",wait:!1},P().then(()=>n("ice-reply")?.focus())};if(n("ice-rst"))n("ice-rst").onchange=(s)=>{let i=Ze.dir;ot[i]=s.target.value;try{localStorage.setItem("kz-reply-style:"+i,ot[i])}catch(b){}};if(n("ice-rg"))n("ice-rg").onclick=async()=>{let s=Ze,i=se;i.again=!0,P();let b=await L("/reply",{op:"draft",dir:s.dir,id:s.id,style:Gt(s.dir),avoid:i.text});if(se!==i)return;if(i.again=!1,b.text)i.text=b.text;i.note=b.ok?b.note||"":"",P().then(()=>n("ice-reply")?.focus())};if(n("ice-reply")){let s=n("ice-reply");s.oninput=()=>{se.text=s.value,n("ice-rn").textContent=`${s.value.length} chars`,n("ice-rs").disabled=!s.value.trim()},s.onkeydown=(i)=>{if(i.key==="Escape")i.stopPropagation(),n("ice-rc").click()},n("ice-rc").onclick=()=>{se=null,P()},n("ice-rs").onclick=async()=>{let i=Ze,b=se;b.busy=!0,P();let w=await L("/reply",{op:"send",dir:i.dir,id:i.id,text:b.text});if(w.ok)se=null,S("Reply posted");else if(w.sent)se=null;else b.busy=!1;if(R(),J())P()}}if(n("ice-ok")){let s=()=>{let i=n("ice-title").value.trim();if(!i){n("ice-title").focus();return}n("ice-ok").disabled=!0,Kt("promote",Ze,"Promoted to Ideas",{text:i,details:n("ice-details").value})};n("ice-ok").onclick=s,n("ice-cancel").onclick=()=>{$e=null,P()},n("ice-title").onkeydown=(i)=>{if(i.key==="Enter")s();if(i.key==="Escape")n("ice-cancel").click()},n("ice-details").onkeydown=(i)=>{if(i.key==="Enter"&&(i.ctrlKey||i.metaKey))s();if(i.key==="Escape")n("ice-cancel").click()}}vt=(s)=>{if(s.altKey||s.ctrlKey||s.metaKey)return!1;let i=s.key,b=_.findIndex((z)=>D(x[z])===de),w=_[b];if(i==="Escape"){if(n("src-sec")?.classList.contains("open"))return ut(),!0;if(oe){if($e)$e=null;else oe=null;return P(),!0}if(Q.size)return Q.clear(),P(),!0;return!1}if(b<0)return!1;if(i==="j"||i==="k"){s.preventDefault();let z=_[Math.max(0,Math.min(_.length-1,b+(i==="j"?1:-1)))];return de=D(x[z]),n("pbody").querySelectorAll(".ice-row").forEach((Y)=>Y.classList.toggle("cur",+Y.dataset.i===z)),n("pbody").querySelector(`.ice-row[data-i="${z}"]`)?.scrollIntoView({block:"nearest"}),!0}let A=i==="o"?n("ice-o-"+w):i==="x"?n("ice-ck-"+w):i==="p"?Q.size?n("ice-bp"):n("ice-pr-"+w):i==="d"?Q.size?n("ice-bd"):n("ice-ds-"+w):null;if(!A||A.disabled)return!1;return s.preventDefault(),A.click(),!0},n("pbody").querySelectorAll("[data-chk]").forEach((s)=>{let i=m[+s.dataset.chk];s.onclick=()=>{s.disabled=!0,s.innerHTML=q("refresh")+" Checking…",Me({op:"gather",dir:i.dir,url:i.url},(b)=>{let w=b.results&&b.results[0];S(w?w.ok?`${i.label||k(i.url)}: ${w.note}${w.added?" in the icebox":""}`:`Failed: ${w.note}`:"Checked")})}}),n("pbody").querySelectorAll("[data-ag]").forEach((s)=>{let i=m[+s.dataset.ag];s.onclick=()=>{if(s.disabled=!0,I)s.innerHTML=q("refresh")+" Gathering…";Me({op:"agent",dir:i.dir,url:i.url},(b)=>{let w=b.results&&b.results[0];S(b.via==="chatgpt"?w&&w.ok?`${i.label||k(i.url)}: ${w.note}${w.added?" in the icebox":""}`:`Failed: ${w?w.note:"no answer"}`:"/kaizen gather opened in your terminal: "+Te(b.agent))})}}),n("pbody").querySelectorAll("[data-rm]").forEach((s)=>{let i=m[+s.dataset.rm];s.onclick=()=>{if(!confirm(`Remove ${i.label||k(i.url)}? What it gathered stays; added again later, it may bring back ones you deleted or dismissed.`))return;Me({op:"remove",dir:m[+s.dataset.rm].dir,url:m[+s.dataset.rm].url},()=>S("Source removed"))}});let mn=x.findIndex((s)=>D(s)===de);if(l){let s=x.findIndex((i)=>D(i)===l[0]);(s>=0?n(l[1]+"-"+s):mn>=0?n("ice-o-"+mn):null)?.focus()}let pt=!l&&t[2]&&n(t[2]);if(pt)(pt.disabled?n(pt.id.replace("src-mb-","src-mv-").replace("src-cs-","src-ct-")):pt)?.focus();if(n("src-add"))n("src-add").onclick=()=>{let s=ze==="social",i=n("src-url").value.trim(),b=n("src-q").value.trim(),w=n("src-plat").value;if(s?!b:!i){n(s?"src-q":"src-url").focus();return}let A=n("src-dir").value;if(!A){n("src-dir").focus();return}n("src-add").disabled=!0,n("src-add").textContent="Adding…",(async()=>{let z=n("src-label").value.trim()||(s?`${st[w]}: ${b}`:""),Y=n("src-cat").value.trim(),be=await L("/sources",s?{op:"add",dir:A,platform:w,query:b,label:z,category:Y}:{op:"add",dir:A,url:i,label:z,category:Y});if(be.ok&&s)n("src-q").value=n("src-label").value=n("src-cat").value="",S("Source added"),R();else if(be.ok){n("src-url").value=n("src-label").value=n("src-cat").value="";let gn=await L("/sources",{op:"gather",dir:A,url:i}),ft=gn.results&&gn.results[0];S(ft?ft.ok?`Source added: ${ft.note} in the icebox`:`Source added, but: ${ft.note}`:"Source added"),R()}else S(be.error||"Could not add source");if(be.ok)Ge=!1;if(J())P()})()},n("src-url").onkeydown=n("src-q").onkeydown=n("src-label").onkeydown=n("src-cat").onkeydown=(s)=>{if(s.key==="Enter")n("src-add").click()};if(n("ice-new"))n("ice-new").onclick=async()=>{let s=y||xe()[0]?.dir;if(!s)return S("Pick a project first");let i=prompt(y?"New request for the icebox:":`New request for ${me(xe()[0].label)}:`)?.trim();if(!i)return;if((await L("/icebox",{op:"add",dir:s,text:i})).ok)S("Added to the icebox")};if(n("src-go"))n("src-go").onclick=()=>{n("src-go").disabled=!0,n("src-go").innerHTML=q("refresh")+" Gathering…",Me({op:"gather",dir:y||void 0},(s)=>{let i=s.results||[],b=(Y)=>m.some((be)=>be.url===Y.url&&be.social),w=i.reduce((Y,be)=>Y+be.added,0),A=i.filter((Y)=>!Y.ok&&!b(Y)).length,z=i.filter(b).length;S((w?`${w} new in the icebox`:"Nothing new")+(A?`, ${A} source${A===1?"":"s"} could not be read`:"")+(z?`; ${z} social for /kaizen gather`:""))})}}var Ut=new URLSearchParams(location.search).has("all");function Ke(e){if(Ut)return;try{e?localStorage.setItem("kz-proj",e):localStorage.removeItem("kz-proj")}catch(t){}}var me=(e)=>e.split(/[\\/]/).pop()||e,Ue=(e,t="")=>{let o=`<span class="plogo" aria-hidden="true">${p((me(t)||"?").charAt(0).toUpperCase())}</span>`;return a.S?.projects?.some((r)=>r.dir===e&&r.logo)?`<img class="plogo" src="/project-logo?dir=${encodeURIComponent(e)}" alt="" loading="lazy" onerror="this.outerHTML=this.dataset.f" data-f="${p(o)}">`:o},xt=(e,t)=>`${Ue(e,t)}<span>${p(me(t))}</span>`;function qt(){let e=n("projq").value.trim().toLowerCase(),t=0;n("projs").innerHTML=a.PL.map((r,l)=>r[0].toLowerCase().includes(e)?(r[2]&&!t++?'<div class="grp">In your projects folder</div>':"")+`<button role="option" data-i="${l}" title="${p(r[0])}">${r[1]==="all"?`<span class="plogo" aria-hidden="true">${q("board")}</span><span>${p(r[0])}</span>`:r[1]==="__find"||r[1]==="__clone"?`<span class="plogo" aria-hidden="true">${q("plus")}</span><span>${p(r[0])}</span>`:xt(r[1],r[0])}</button>`:"").join("")||'<div class="grp">No match</div>';let o=n("projs").querySelector("button");if(o)o.classList.add("on")}function $t(e){if(n("projpop").hidden=!e,n("proj").setAttribute("aria-expanded",e),e)n("projq").value="",qt(),n("projq").focus()}async function ws(e){if(e==="__find"){S("Searching…");let t=await L("/find",{});if(t.ok)S(t.added?`Found ${t.added} new project${t.added===1?"":"s"}`:"No new projects found")}else if(e==="__clone"){let t=prompt("GitHub link to clone (https or ssh):")?.trim();if(!t)return;let o=await L("/clone",{url:t,check:!0});if(!o.ok)return;let r=prompt("Clone into (a new folder is created). Leave empty to pick a folder:",o.to);if(r===null)return;if(!r.trim()){let c=await L("/pick-folder",{});if(!c.path)return;r=c.path.replace(/[\\/]+$/,"")+(c.path.includes("\\")?"\\":"/")+o.name}S("Cloning…");let l=await L("/clone",{url:t,to:r});if(!l.ok)return;await L("/cd",{dir:l.dir}),a.all=!1,a.dir=l.dir,Ke(l.dir),S("Cloned")}else if(e==="all")a.all=!0,Ke();else if((await L("/cd",{dir:e})).ok)a.all=!1,a.dir=e,Ke(e);if(await R(),B==="sources")P()}var Re=null;function Rn(e){let t=$s(),o=e.querySelector("#newdir"),r=e.querySelector("#ndc .menu-pop"),l=r.querySelector("input"),c=e.querySelector("#nds");if(!t.some((h)=>h.dir===Re))Re=t[0]?.dir??null;let g=()=>{let h=t.find((k)=>k.dir===Re);o.innerHTML=h?xt(h.dir,h.label):"Choose project"},y=()=>{let h=l.value.trim().toLowerCase();c.innerHTML=t.filter((k)=>k.label.toLowerCase().includes(h)).map((k)=>`<button type="button" role="option" data-d="${p(k.dir)}" title="${p(k.label)}" class="${k.dir===Re?"on":""}">${xt(k.dir,k.label)}</button>`).join("")||'<div class="grp">No match</div>',c.querySelectorAll("button").forEach((k)=>k.onclick=()=>{Re=k.dataset.d,g(),v(!1),e.querySelector("#newidea").focus()})},v=(h)=>{if(r.hidden=!h,o.setAttribute("aria-expanded",h),h)l.value="",y(),l.focus()};o.onclick=(h)=>{h.preventDefault(),v(r.hidden)},l.oninput=y,l.onkeydown=(h)=>{if(h.key==="Escape")h.stopPropagation(),v(!1);if(h.key==="Enter")h.preventDefault(),c.querySelector("button")?.click()},g()}function $s(){let e=xe(),t=e.filter((o)=>a.shown.has(o.dir));return t.length?t:e}function xe(){let e=[...a.S.projects];for(let t of a.S.cards)if(!e.some((o)=>o.dir===t.state))e.push({dir:t.state,label:t.where});return e}var Fe=[];function St(){let e=n("showq").value.trim().toLowerCase();Fe=xe(),n("shows").innerHTML=Fe.map((o,r)=>o.label.toLowerCase().includes(e)?`<button type="button" role="menuitemcheckbox" data-i="${r}" aria-checked="${a.shown.has(o.dir)}" title="${p(o.label)}">${xt(o.dir,o.label)}<svg class="ck"><use href="#i-check"/></svg></button>`:"").join("")||'<div class="grp">No match</div>';let t=Fe.filter((o)=>a.shown.has(o.dir)).length;n("showcount").textContent=t?`${t} of ${Fe.length} shown`:`All ${Fe.length} shown`}function wt(){try{localStorage.setItem("kz-show",JSON.stringify([...a.shown]))}catch(e){}if(M(),J())P()}function Kn(){n("proj").onclick=()=>$t(n("projpop").hidden),n("projq").oninput=qt,n("projq").onkeydown=(e)=>{let t=[...n("projs").querySelectorAll("button")],o=t.findIndex((r)=>r.classList.contains("on"));if(e.key==="Escape")$t(!1),n("proj").focus();else if(e.key==="Enter"){if(t[o])t[o].click()}else if((e.key==="ArrowDown"||e.key==="ArrowUp")&&t[o]){e.preventDefault();let r=t[(o+(e.key==="ArrowDown"?1:t.length-1))%t.length];t[o].classList.remove("on"),r.classList.add("on"),r.scrollIntoView({block:"nearest"})}},n("projs").onclick=(e)=>{let t=e.target.closest("button");if(t)$t(!1),ws(a.PL[t.dataset.i][1])},document.addEventListener("click",(e)=>{if(!e.target.closest("#combo"))$t(!1)}),n("show").onclick=()=>{let e=n("showpop").hidden;if(n("showpop").hidden=!e,n("show").setAttribute("aria-expanded",e),e)n("showq").value="",St(),n("showq").focus()},n("showq").oninput=St,n("showq").onkeydown=(e)=>{if(e.key==="Escape")n("showpop").hidden=!0,n("show").setAttribute("aria-expanded",!1),n("show").focus()},n("shows").onclick=(e)=>{let t=e.target.closest("button[data-i]"),o=t?.dataset.i,r=Fe[o]?.dir;if(!r)return;a.shown.has(r)?a.shown.delete(r):a.shown.add(r),wt(),n("shows").querySelector(`[data-i="${o}"]`)?.focus()},n("showall").onclick=()=>{a.shown.clear(),wt()},document.addEventListener("click",(e)=>{if(!e.target.closest("#showc"))n("showpop").hidden=!0,n("show").setAttribute("aria-expanded",!1)}),n("newtop").onclick=()=>{a.mode="new",M(),n("board").scrollTo(0,0)}}var Lt=(e)=>e.status==="stalled"||!!(e.pty&&!e.pty.alive);async function Ct(e,t){t.disabled=!0,ye();let o=await L("/resume",{dir:e.state,id:e.id});if(!o.ok){t.disabled=!1;return}if(S("Resumed, "+Ne(o)+": "+Te(o.agent)),o.pty)a.wantTerm=j(e);if(a.openKey===j(e))n("pfoot").innerHTML="",X(e,!0)}var Ft={"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."},_e=new Set,We=(e)=>j(e)+":"+e.awaiting+":"+e.moved;async function At(e,t,o,r){t.disabled=!0,ye();let l=await L("/decide",{dir:e.state,id:e.id,awaiting:e.awaiting,...o===void 0?{}:{why:o},...r?{answers:r}:{}});if(!l.ok){t.disabled=!1;return}if(_e.add(We(e)),S((o===void 0?"Approved":"Sent back")+", "+Ne(l)+": "+Te(l.agent)),l.pty)a.wantTerm=j(e);if(a.openKey===j(e))n("pfoot").innerHTML="",X(e,!0)}function Nn(e){let t=n("pfoot"),o=e.awaiting==="approvals.plan";t.innerHTML=`<div class="inl" style="flex:1"><div class="q">${o?"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.${o?" 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 r=t.querySelector("textarea");r.focus(),t.querySelector("[data-a=c]").onclick=()=>{t.innerHTML="",X(e,!0)},t.querySelector("[data-a=s]").onclick=(l)=>{let c=r.value.trim();if(!c){S("Say what should change"),r.focus();return}At(e,l.target,c)}}function Hn(e,t){let o=n("pfoot");o.innerHTML=`<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((r,l)=>`<fieldset><legend>${p(r.q)}</legend>${r.options.map((c,g)=>`<label><input type="${r.multi?"checkbox":"radio"}" name="q${l}" value="${g}"${c.rec||!r.multi&&!g&&!r.options.some((y)=>y.rec)?" checked":""}> ${p(c.text)}</label>`).join("")}<input type="text" data-n="${l}" placeholder="Note (optional)" aria-label="Note: ${p(r.q)}"></fieldset>`).join("")}<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>`,o.querySelector("[data-a=c]").onclick=()=>{o.innerHTML="",X(e,!0)},o.querySelector("[data-a=s]").onclick=(r)=>At(e,r.target,void 0,t.map((l,c)=>{let g=[...o.querySelectorAll(`[name=q${c}]:checked`)].map((v)=>`"${l.options[+v.value].label}"`).join(", ")||"none of the options",y=o.querySelector(`[data-n="${c}"]`).value.trim();return`${l.q} — ${g}${y?"; "+y:""}`}))}var Se=null,Bt="",_t=6,jt=new Set,at=(()=>{try{return JSON.parse(localStorage.getItem("kz-order"))||{}}catch(e){return{}}})(),V=!1,it=!1,he=new Set,j=(e)=>e._k||(e.kind==="idea"?"idea:"+e.state+":"+e.text:"run:"+e.state+":"+e.id),xs=(e)=>e.kind==="run"?a.S.cards.filter((t)=>t.parent===e.id&&t.state===e.state):[],Ss=(e)=>!!e.parent&&a.S.cards.some((t)=>t.kind==="run"&&t.id===e.parent&&t.state===e.state&&!t.archived&&t.column!==4),Wt=new Set,In=(e)=>_e.has(We(e))?"Sent to agent":e.status!=="waiting"?"Open":{"approvals.plan":"Review plan",findings:"See findings","approvals.review":"Review work"}[e.awaiting]||"Open";function qs(e){let t=j(e),o=document.createElement("div");o.className="card"+(t===a.openKey?" open":"")+(t===a.menuKey?" menu":"")+(e.archived?" archived":""),o.tabIndex=0,o.dataset.k=t,o.setAttribute("role","button");let r=Ae[e.status]||"idea",l=e.kind==="idea"?"":e.text.replace(/-/g," "),c=e.status==="waiting"?tt(e.awaiting)||"needs you":e.status||"idea",g=[],y=[],v=(d,m,x="")=>`<span class="mk${x}" role="img" title="${p(m)}" aria-label="${p(m)}"><svg><use href="#${d}"/></svg></span>`;if(a.all){let d=e.where.split("/").filter(Boolean).pop()||e.where;g.push(`<span class="chip proj" title="${p(e.where)}">${p(d)}</span>`)}for(let d of e.tags||[])if(Sn[d])y.push(v("c-"+d,d==="full"?"Full run: separate reviewer":d==="lite"?"Lite run: one session":Ht[d]||d,d==="full"||d==="lite"?" accent":""));else if(d==="yolo")y.push('<span class="mk warn" title="Started in Yolo: the agent runs commands without asking">yolo</span>');else g.push(`<span class="chip">${p(Ht[d]||d)}</span>`);if(e.pty&&e.pty.alive&&e.pty.asking)y.push(v("i-ask","The agent is asking you something: open it, terminal button at the top"," warn ask"));else if(e.pty&&e.pty.alive)y.push(v("i-term","The agent is running in this card: open it, terminal button at the top"," run"));let h=e.notes&&/^\[[^\]]*\] (?:Gathered from|Promoted from the icebox:) (.+?)\. Outside text/.exec(e.notes);if(e.parent)g.push(`<span class="chip link" title="Filed by ${p(e.parent)}">${p(e.parent.replace(/^\d{4}-\d\d-\d\d-/,"").replace(/-/g," "))}</span>`);if(e.branch)g.push(`<span class="chip" title="Built on its own branch, ${p(e.branch)}">branch</span>`);if(h)g.push(`<span class="chip" title="From ${p(h[1])}">gathered</span>`);else if(e.notes)g.push('<span class="chip">notes</span>');if(e.git)y.push(v("c-git",e.git==="pushed"?"Committed & pushed":"Not committed & pushed yet",e.git==="pushed"?" ok":" warn"));if(e.open)y.push(v("i-bug",`${e.open} open finding${e.open===1?"":"s"} to fix`," warn"));let k=it&&!V&&e.column===4;if(e.column===4&&!V)o.classList.add("compact");let u=e.kind==="run"&&!V&&!k?e.status==="waiting"?`<button class="btn sm${_e.has(We(e))?"":" pri"} act" data-a="open">${In(e)}</button>`:e.status==="stalled"&&Lt(e)?'<button class="btn sm act" data-a="resume">Resume</button>':"":"";o.innerHTML=(k?`<input type="checkbox" class="pick" ${he.has(t)?"checked":""} aria-label="Select">`:"")+`<div class="c-id"><span class="st d-${r}" role="img" title="${p(c)}" aria-label="${p(c)}"><svg><use href="#s-${r}"/></svg></span><span class="idt">${p(l)}</span><span class="age">${e.kind==="run"?xn(e.moved||a.S.now,a.S.now):""}</span><button class="ib sm more" data-a="menu" title="Actions" aria-label="Actions" aria-haspopup="menu" aria-expanded="${t===a.menuKey}">${q("more")}</button></div>
24
- <div class="c-title"${e.kind==="idea"&&je(e.text).parts.length?` title="${p(e.text)}"`:""}>${p(e.kind==="idea"?je(e.text).head:e.title||e.id)}</div>
22
+ ${c("Notifications",`<button class="chk${v==="granted"?" on":""}" id="nt" role="switch" aria-checked="${v==="granted"}"${v&&v!=="denied"?"":" disabled"}></button>`,k,"nt","notifications "+k+" browser")}</div>
23
+ <p class="snone" id="snone" hidden></p>`,Gt();let f=[...n("pbody").querySelectorAll(".sg")],y=()=>{let u=a.setQ.trim().toLowerCase(),h=!1;f.forEach((d)=>{let g=!1;for(let x of d.nextElementSibling.children)x.hidden=!!u&&!x.dataset.q.includes(u),g||=!x.hidden;d.hidden=!g,h||=g}),n("snone").hidden=h,n("snone").textContent=`No setting matches “${a.setQ.trim()}”.`};if(n("sq").oninput=(u)=>{a.setQ=u.target.value,y()},n("sq").onkeydown=(u)=>{if(u.key!=="Escape")return;if(u.preventDefault(),a.setQ)a.setQ="",u.target.value="",y();else ee()},y(),m){let u=n("sq");u.focus(),u.setSelectionRange(u.value.length,u.value.length)}if(n("gg-save").onclick=async()=>{let u=await C("/google",{op:"client",id:n("gg-id").value,secret:n("gg-secret").value});if(u.ok)S("Saved");else S(u.error||"Enter both the client id and the secret");we()},n("gg-conn").onclick=async()=>{if(r.connected)return await C("/google",{op:"disconnect"}),we();let u=window.open("","_blank"),h=await C("/google",{op:"connect"});if(h.ok&&u)u.location=h.url;else u?.close(),S(h.error||"Could not start Google sign-in")},n("th").onchange=(u)=>setTheme(u.target.value),n("nt").onclick=()=>{ve(),setTimeout(we,300)},o)o.rows.forEach((u)=>{let h=n("s-"+u.key.replace(/\./g,"-")),d=async(g)=>{let x=document.activeElement?.id;if((await C("/settings",{key:u.key,value:g})).ok)S("Saved");if(await we(),x?.startsWith("s-preset-"))n(x)?.focus()};if(h.tagName==="SELECT"||h.tagName==="INPUT")h.onchange=async()=>{if(await d(h.value.trim()),h.tagName==="INPUT")K()};else h.querySelectorAll("[data-v]").forEach((g)=>g.onclick=()=>d(g.dataset.v));if(h.matches(".chk"))h.onclick=()=>d(h.dataset.v);if(u.folder)n(h.id+"-pick").onclick=async()=>{let g=await C("/pick-folder",{});if(g.path)await d(g.path),K()}})}var Q="board",Ft=(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":"Board"};function de(e,t){if(a.pageGen++,e==="icebox")e="sources";if(!["settings","sources"].includes(e))e="board";let o=Q;if(Q=e,Ft(e),document.body.classList.toggle("paged",e!=="board"),document.body.classList.toggle("ice",e==="sources"),t&&e!==o)history.pushState(null,"",e==="board"?location.pathname:"?view="+(e==="sources"?"icebox":e));let r=n("panel");if(e==="board"){if(o==="board")return;r.style.transition="none",r.classList.remove("open","docked"),r.offsetWidth,r.style.transition="",ee();return}if(r.classList.add("docked"),e==="settings")we();else R()}function Rn(){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")a.setQ="";de(e.dataset.view,!0)}}),document.querySelector(".rail .home").onclick=(e)=>{if(e.ctrlKey||e.metaKey||e.shiftKey||e.button)return;e.preventDefault(),de("board",!0)},addEventListener("popstate",()=>de(new URLSearchParams(location.search).get("view"))),addEventListener("kz-theme",()=>{if(Q==="settings")we()})}var Nn="Icebox",oe=null,at={},_t=(e)=>{if(!at[e])try{at[e]=localStorage.getItem("kz-reply-style:"+e)}catch(t){}return at[e]||"casual"},$s={casual:"Casual",fun:"Fun",helpful:"Helpful",promo:"Promo"},ae=null,xe=null,ue=null,pe="",te="",Ge="link",st=null,Fe=!1,wt=!1,$t=null,J=new Set,ot={x:"X",tiktok:"TikTok",instagram:"Instagram",threads:"Threads"},V=()=>n("panel").classList.contains("open")&&n("ptitle").textContent===Nn,He=(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 Kn(e,t){let o=n(e);if(!o)return;let r=o.querySelector("button.sel"),l=o.querySelector(".menu-pop"),c=l.querySelector("input"),m=l.querySelector(".pick-list"),v=(f)=>{if(l.hidden=!f,r.setAttribute("aria-expanded",f),f)c.value="",k(),c.focus()},k=()=>{let f=c.value.trim().toLowerCase(),y=null,u=!1;for(let h of m.children)if(h.classList.contains("grp")){if(y)y.hidden=!u;y=h,u=!1}else{let d=!f||h.dataset.q.includes(f)||y&&y.dataset.g.includes(f);h.hidden=!d,u||=d}if(y)y.hidden=!u};r.onclick=()=>v(l.hidden),c.oninput=k,c.onkeydown=(f)=>{if(f.key==="Escape")f.stopPropagation(),v(!1),r.focus();else if(f.key==="Enter")f.preventDefault(),m.querySelector("button:not([hidden])")?.click()},m.onclick=(f)=>{let y=f.target.closest("button[data-v]");if(!y)return;v(!1),t(y.dataset.v)}}async function R(){if(Q!=="sources")return;let e=++a.pageGen;ce(!0);let t=V()?[n("src-url")?.value||"",n("src-label")?.value||"",document.activeElement?.id,n("src-dir")?.value,n("src-cat")?.value||"",n("src-plat")?.value,n("src-q")?.value||""]:["",""],o=new Map(V()?[...n("pbody").querySelectorAll("[data-mv]")].map((s)=>[s.dataset.k,s.value]):[]),r=V()&&document.activeElement?.closest?.(".ice-row"),l=r?[r.dataset.k,document.activeElement.id.replace(/-\d+$/,"")]:null,c=V()&&n("ice-title")?[n("ice-title").dataset.k,n("ice-title").value,n("ice-details").value]:null,m=new Map(V()?[...n("pbody").querySelectorAll("[data-ct]")].map((s)=>[s.dataset.k,s.value]):[]);if(!V())n("pbody").innerHTML=ht;$e(),a.openKey=null,a.runDoc=null,n("pterm").hidden=!0,n("panel").classList.add("open"),n("scrim").classList.add("on"),n("ptitle").textContent=Nn,n("ptabs").innerHTML="",n("pfoot").innerHTML="",n("psub").textContent="",n("psub").title="";let v=a.all?null:a.S.dir,k=v?"?dir="+encodeURIComponent(v):"",f=await fetch("/sources"+k).then((s)=>s.json()).catch(()=>null);if(e!==a.pageGen||!V())return;let y=(s)=>{try{return new URL(s).hostname}catch{return s}},u=(s,i,b)=>{if(ot[b])return`<span class="fav">${q("b-"+b)}</span>`;let w;try{let O=new URL(s);if(/^https?:$/.test(O.protocol))w=O.origin}catch{}let L=!w?"":s.includes("docs.google.com/spreadsheets")?"https://ssl.gstatic.com/docs/spreadsheets/favicon3.ico":s.includes("docs.google.com/document")?"https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico":w+"/favicon.ico";return`<span class="fav">${L?`<img src="${p(L)}" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.remove()">`:""}${q(i)}</span>`},h=(s)=>ot[s?.social]||(s?y(s.url).replace(/^www\./,""):"")||"Other",d=(s)=>{if(s.social)return"social";let i=s.url;if(i.includes("docs.google.com/spreadsheets")||i.endsWith(".csv")||i.endsWith(".tsv"))return"sheet";if(i.includes("docs.google.com/document"))return"doc";return"link"},g=f&&f.sources||[],x=[...f&&f.icebox||[],...f&&f.dismissed||[]],H=(f&&f.icebox||[]).length,T=f&&f.gatherer||{},z=T.use==="chatgpt"&&T.connected,U=(s,i)=>(s&&!a.S.projects.some((b)=>b.dir===s)?`<option value="${p(s)}" title="${p(i||s)}" selected>${p(Se(i||s))}</option>`:"")+a.S.projects.map((b)=>`<option value="${p(b.dir)}" title="${p(b.label)}"${b.dir===s?" selected":""}>${p(Se(b.label))}</option>`).join(""),be=g.map((s,i)=>{let b=d(s),w=s.label||y(s.url),L=s.social&&(!s.last||!s.last.ok&&/gathered by your agent/i.test(s.last.note))?`<span class="src-badge dim">${q("at")}${z?"Gathered with ChatGPT":"Gathered by your agent"}</span>`:!s.last?'<span class="src-badge dim">Not gathered yet</span>':s.last.ok?`<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>${p(s.last.note)}</span><span class="hint" style="margin:0">${He(s.last.at)}</span>`:`<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: ${p(s.last.note)}</span><span class="hint" style="margin:0">${He(s.last.at)}</span>`,O=b==="sheet"?"sheet":b==="doc"?"doc":b==="social"?"at":"link",B=s.url.replace(/^https?:\/\/(www\.)?/,"");return`<div class="src-card" id="src-card-${i}"><div class="src-hd"><span class="src-ico" title="${b==="social"?p(ot[s.social]||"social"):b}">${u(s.url,O,s.social)}</span><div class="src-info"><span class="src-title" title="${p(w)}">${p(w)}</span><a class="src-url" href="${p(s.url)}" target="_blank" rel="noopener noreferrer" title="${p(s.url)}"><span>${p(B)}</span>${q("ext")}</a></div><div class="src-acts">${s.social?`<button class="ib sm" id="src-ag-${i}" data-ag="${i}" aria-label="Gather ${p(w)} with your agent" title="${z?"Gather with your ChatGPT plan":"Gather: opens your agent on /kaizen gather"}">${q("refresh")}</button>`:`<button class="ib sm" id="src-chk-${i}" data-chk="${i}" aria-label="Check ${p(w)} now" title="Check now">${q("refresh")}</button>`}<button class="ib sm danger" id="src-rm-${i}" data-rm="${i}" aria-label="Remove ${p(w)}" title="Remove source">${q("trash")}</button></div></div><div class="src-status">${L}</div><div class="src-fields"><label class="src-f" title="Category"><span>Category</span><input class="sel" id="src-ct-${i}" data-ct="${i}" data-k="${p(s.dir+" "+s.url)}" value="${p(s.category||"")}" placeholder="${p(h(s))}" maxlength="40" aria-label="Category of ${p(w)}"><button class="btn sm pri" id="src-cs-${i}" aria-label="Save the category of ${p(w)}" disabled>Save</button></label><label class="src-f" title="Project"><span>Project</span>${_e(s.dir,s.project||"")}<select class="sel" id="src-mv-${i}" data-mv="${i}" data-k="${p(s.dir+" "+s.url)}" aria-label="Project of ${p(w)}">${U(s.dir,s.project)}</select><button class="btn sm pri" id="src-mb-${i}" aria-label="Move ${p(w)} to the chosen project" disabled>Move</button></label>${b==="sheet"||/github\.com/.test(s.url)?`<label class="src-wb"><input type="checkbox" id="src-wb-${i}" data-wb="${i}"${s.writeback?" checked":""}> Write back</label>`:""}</div></div>`}).join(""),A=(f&&f.failed||[]).map((s)=>`<p style="color:var(--dim)">The sources of ${p(s)} could not be read.</p>`).join(""),I=`<div class="src-empty"><div class="src-empty-ico">${q("link")}</div><h4>No sources yet</h4></div>`,G=Ge==="social",Z=!g.length||Fe||[t[0],t[1],t[4],t[6]].some(Boolean),N=`<div class="src-add" id="src-addbox"${Z?"":" 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="${!G}">Link</button><button id="src-k-social" data-kind="social" aria-pressed="${G}">Social</button></div></div><label for="src-url"${G?" hidden":""}><span>Link</span><input class="sel" id="src-url" placeholder="Link" spellcheck="false" autocomplete="off"></label><div class="two plat"${G?"":" hidden"}><label for="src-plat"><span>Platform</span><select class="sel" id="src-plat">${Object.entries(ot).map(([s,i])=>`<option value="${s}">${i}</option>`).join("")}</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="${G?"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="${G?"e.g. Social":"e.g. Bugs"}" maxlength="40"></label></div><label for="src-dir"><span>Project</span><select class="sel" id="src-dir">${v?"":'<option value="">Choose project…</option>'}${U(v)}</select></label><div class="acts"><span class="end"></span><button class="btn pri" id="src-add">Add source</button></div></div>`,D=(s)=>{let i=g.find((b)=>b.dir===s.dir&&b.url===s.source);return i?i.label||y(i.url):y(s.source)},P=(s)=>s.dir+" "+s.id,ie=(s)=>s.dir+" "+s.source,E=(s)=>g.find((i)=>i.dir===s.dir&&i.url===s.source),_=(s)=>s.status==="dismissed"?"Dismissed":s.status==="done"?"Done":s.category||E(s)?.category||h(E(s)||{url:s.source}),ne=(s,i)=>{let b=new Map;return s.forEach((w)=>{let L=i(w);b.set(L,{it:w,n:(b.get(L)?.n||0)+1})}),b},ye=v?new Map:ne(x,(s)=>s.dir);if(!v){for(let s of a.shown)if(!ye.has(s)){let i=De().find((b)=>b.dir===s);if(i)ye.set(s,{it:{dir:s,project:i.label},n:0})}}let me=(s)=>v||!a.shown.size||a.shown.has(s.dir),dn=x.filter(me),Ye=ne(dn,_);if(te&&!Ye.has(te))te="";let ss=dn.filter((s)=>!te||_(s)===te),Xe=ne(ss,ie);if(pe&&!Xe.has(pe))pe="";let os=new Set(x.map(P));if(ae&&!os.has(ae))ae=null;if(xe!==ae)xe=null;let Ze=new Map;x.forEach((s,i)=>{if(!me(s)||(te?_(s)!==te:s.status==="dismissed"||s.status==="done")||pe&&ie(s)!==pe)return;let b=_(s);Ze.has(b)||Ze.set(b,[]),Ze.get(b).push(i)});let W=[...Ze.values()].flat();{let s=new Set(W.map((i)=>P(x[i])));[...J].forEach((i)=>s.has(i)||J.delete(i))}if(!W.some((s)=>P(x[s])===ue))ue=W.length?P(x[W[0]]):null;let et=W.filter((s)=>J.has(P(x[s]))).length,as=ye.size>1,un=(s,i,b,w)=>`<div class="combo ice-combo" id="${s}"><button class="sel" aria-label="${i}" aria-haspopup="listbox" aria-expanded="false">${b}</button><div class="menu-pop" hidden><input class="sel" aria-label="Search ${i.toLowerCase()}" placeholder="Search…" spellcheck="false" autocomplete="off"><div class="pick-list" role="listbox">${w}</div></div></div>`,ct=(s,i,b,w,L="")=>`<button type="button" role="option" data-v="${p(s)}" data-q="${p(w.toLowerCase())}" class="${i?"on":""}${L}" aria-selected="${i}">${b}</button>`,dt=new Map;for(let[s,{it:i}]of Xe){let b=h(E(i)||{url:i.source});dt.has(b)||dt.set(b,[]),dt.get(b).push([s,i])}let Kt=(s)=>u(s.source,"link",E(s)?.social),Nt=pe&&[...Xe].find(([s])=>s===pe)?.[1].it,pn=(Ye.size>1||Ye.has("Dismissed")||Ye.has("Done")?un("ice-fc","Category",`<span>${p(te||"All categories")}</span>`,ct("",!te,"<span>All categories</span>","all categories")+[...Ye].map(([s,{n:i}])=>ct(s,te===s,`<span>${p(s)}</span><i>${i}</i>`,s)).join("")):"")+(Xe.size>1?un("ice-fs","Source",Nt?`${Kt(Nt)}<span>${p(D(Nt))}</span>`:"<span>All sources</span>",ct("",!pe,"<span>All sources</span>","all sources")+[...dt].map(([s,i])=>`<div class="grp" data-g="${p(s.toLowerCase())}">${Kt(i[0][1])}${p(s)}</div>`+i.map(([b,w])=>ct(b,pe===b,`${Kt(w)}<span>${p(D(w).toLowerCase().startsWith(s.toLowerCase()+":")?D(w).slice(s.length+1).trim():D(w))}</span><i>${Xe.get(b).n}</i>`,s+" "+D(w)," sub")).join("")).join("")):""),is=`<div class="ice-bar">${W.length?`<input type="checkbox" id="ice-all" aria-label="Select every request shown"${et&&et===W.length?" checked":""}>`:""}<span class="count">${te==="Dismissed"||te==="Done"?`${W.length} ${te.toLowerCase()}`:`${W.length<H?`${W.length} of ${H}`:H} waiting`}</span><span class="end"></span>${pn?`<details class="ice-filt"><summary class="ib${te||pe?" on":""}" aria-label="Filter requests" title="Filter requests">${q("filter")}</summary><div class="ice-fpop">${pn}</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"${g.length?"":" disabled"}>${q("refresh")} Gather all</button><button class="btn ghost sm" id="src-tog" aria-expanded="${st??!g.length}" aria-controls="src-sec">${q("link")} Sources · ${g.length}</button></div>`,fn=J.size?`<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>`:"",rs=(s)=>`<div class="ice-form"><label for="ice-title" class="hint" style="margin:0">Idea title</label><input class="sel" id="ice-title" data-k="${p(P(s))}" maxlength="200" value="${p(s.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>`,ls=(s)=>{if(!s.post)return"";if(s.replied)return`<span class="hint" style="margin:0">You replied to this post ${He(s.replied)}.</span>`;let i=oe&&oe.k===P(s)?oe:null,b=(w)=>`<select class="sel" id="ice-rst" aria-label="Reply style"${w?" disabled":""}>${Object.entries($s).map(([L,O])=>`<option value="${L}"${L===_t(s.dir)?" selected":""}>${O}</option>`).join("")}</select>`;if(!i)return`<div class="ice-form-acts"><span class="hint">You review it before it posts.</span>${T.connected?b(!1):""}<button class="btn sm" id="ice-rp">Reply</button></div>`;if(i.wait)return'<div class="ice-form-acts"><span class="hint">Drafting…</span></div>';return`<div class="ice-form"><label for="ice-reply" class="hint" style="margin:0">Your reply${i.note?` · ${p(i.note)}`:""}</label><textarea class="sel" id="ice-reply" rows="3" maxlength="2000" aria-label="Your reply"${i.busy||i.again?" disabled":""}>${p(i.text)}</textarea><span class="hint" id="ice-rn" style="margin:0">${i.again?"Writing another…":`${i.text.length} chars`}</span><div class="ice-form-acts"><span class="end"></span>${T.connected?`${b(i.busy||i.again)}<button class="btn ghost sm" id="ice-rg"${i.busy||i.again?" disabled":""}>Regenerate</button>`:""}<button class="btn ghost sm" id="ice-rc"${i.busy?" disabled":""}>Cancel</button><button class="btn pri sm" id="ice-rs"${i.busy||i.again||!i.text.trim()?" disabled":""}>${i.busy?"Sending…":"Send reply"}</button></div></div>`},cs=(s)=>{let i=x[s],b=P(i),w=ae===b;return`<div class="ice-row${ue===b?" cur":""}${J.has(b)?" sel":""}" data-i="${s}" data-k="${p(b)}"><input type="checkbox" id="ice-ck-${s}" data-ck="${s}"${J.has(b)?" checked":""} aria-label="Select: ${p(i.text)}"><span class="ice-src" title="${p(D(i))}">${u(i.source,"link",g.find((L)=>L.dir===i.dir&&L.url===i.source)?.social)}</span><button class="ice-t" id="ice-o-${s}" data-o="${s}" aria-expanded="${w}" title="${p(i.text)}">${p(i.text)}</button><span class="ice-meta">${as?`<span class="nm" title="${p(i.project||"")}">${_e(i.dir,i.project||"")}${p(Se(i.project||""))}</span>`:""}${i.status==="done"&&i.written?`<span title="Written back ${p(He(i.written))}">written back</span>`:""}${i.status==="done"&&i.writeError?`<span title="${p(i.writeError)}">write-back failed</span>`:""}${i.status==="promoted"?`<span title="Promoted to Ideas: ${p(i.idea||"")}">promoted</span>`:""}${i.replied?`<span title="Replied ${p(He(i.replied))}">replied</span>`:""}<span class="ice-age">${He(i.at)}</span><span class="ice-ra">${i.status==="promoted"||i.status==="done"?"":`<button class="btn ghost sm" id="ice-pr-${s}" data-pr="${s}" title="Promote to Ideas (p)" aria-label="Promote to Ideas: ${p(i.text)}">${q("plus")}</button>`}${i.status==="promoted"||i.status==="done"?"":i.status==="dismissed"?`<button class="btn ghost sm" id="ice-rs-${s}" data-rs="${s}" title="Restore to waiting" aria-label="Restore: ${p(i.text)}">Restore</button>`:`<button class="btn ghost sm" id="ice-ds-${s}" data-ds="${s}" title="Dismiss (d)" aria-label="Dismiss: ${p(i.text)}">${q("x")}</button>`}</span></span></div>`+(w?`<div class="ice-open">${i.notes?`<div class="ice-notes">${p(i.notes)}</div>`:""}${(i.images||[]).filter((L)=>/^https:\/\//.test(L)).length?`<details class="ice-imgs"><summary>Show images (${i.images.filter((L)=>/^https:\/\//.test(L)).length}) — loads them from the source's site</summary>${i.images.filter((L)=>/^https:\/\//.test(L)).map((L,O)=>{let B=`/ice-image?dir=${encodeURIComponent(i.dir)}&id=${encodeURIComponent(i.id)}&i=${O}`;return`<a href="${p(B)}" target="_blank" rel="noopener noreferrer"><img data-src="${p(B)}" alt="" referrerpolicy="no-referrer"></a>`}).join("")}</details>`:""}<span class="hint" style="margin:0">From ${p(D(i))} · ${He(i.at)}${i.post?` · <a href="${p(i.post)}" target="_blank" rel="noopener">open post</a>`:""}</span>${ls(i)}${xe===b?rs(i):""}</div>`:"")},ds=H&&!W.length?'<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>':W.length?`<div class="ice-list">${[...Ze].map(([s,i])=>`<div class="ice-cat">${p(s)}<i>${i.length}</i></div>${i.map(cs).join("")}`).join("")}</div>`:'<div class="ice-zero"><h4>Nothing waiting</h4></div>',ut=st??!g.length,Ht=ut&&!n("src-sec")?.classList.contains("open"),us=`<div class="src-scrim${ut&&!Ht?" on":""}" id="src-scrim"></div><aside class="src-sec${ut&&!Ht?" open":""}" id="src-sec" aria-label="Sources"${ut?"":" inert"}><header><div class="t"><b>Sources</b><div class="src-chips"><a id="src-to-settings" class="schip" role="button" tabindex="0" title="${f&&f.every&&f.every!=="off"?`Checked every ${p(f.every)}`:"Auto-check off"}: change in Settings">${q("clock")}${f&&f.every&&f.every!=="off"?p(f.every):"Off"}</a>${T.connected?`<span class="schip on" title="ChatGPT${T.email?": "+p(T.email):""}">${q("ai")}ChatGPT<a id="cg-out" role="button" tabindex="0" aria-label="Disconnect ChatGPT" title="Disconnect">${q("x")}</a></span>`:`<a id="cg-in" class="schip" role="button" tabindex="0" title="Connect ChatGPT">${q("ai")}ChatGPT</a>`}${(f.google||{}).connected?`<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>`:`<a id="gg-in" class="schip" role="button" tabindex="0" title="Connect Google">${q("google")}Google</a>`}</div></div><div class="src-hda">${g.length?`<button class="btn sm" id="src-new" aria-expanded="${Z}" 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">${N}${g.length?`<div class="src-list">${be}</div>`:I}</div></aside>`,ps=T.use==="ask"&&!T.connected?'<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>':"",fs=n("src-sec")?.querySelector(".body")?.scrollTop??0;if(n("pbody").innerHTML=f&&f.error?`<p style="color:var(--dim)">${p(f.error)}</p>`:!f?'<p style="color:var(--dim)">The icebox could not be read.</p>':ps+is+fn+A+ds+us,!f||f.error)return;let hn=n("src-sec")?.querySelector(".body");if(hn)hn.scrollTop=fs;if(c&&c[0]===ae&&n("ice-title"))n("ice-title").value=c[1],n("ice-details").value=c[2];if(n("src-add")){if(t[5])n("src-plat").value=t[5];if(n("src-q").value=t[6]||"",n("src-url").value=t[0],n("src-label").value=t[1],n("src-cat").value=t[4]||"",t[3]!==void 0&&n("src-dir"))n("src-dir").value=t[3]}if(n("src-to-settings"))n("src-to-settings").onclick=()=>{a.setQ="sources",de("settings",!0)};let hs=async()=>{let s=window.open("","_blank"),i=await C("/chatgpt",{op:"connect"});if(i.ok&&s)s.location=i.url;else if(s?.close(),i.ok)location.href=i.url};for(let s of["cg-go","cg-in"])if(n(s))n(s).onclick=hs;if(n("gg-in"))n("gg-in").onclick=async()=>{if(!(f.google||{}).hasClient){S("Add your Google client in Settings first"),a.setQ="google",de("settings",!0);return}let s=window.open("","_blank"),i=await C("/google",{op:"connect"});if(i.ok&&s)s.location=i.url;else if(s?.close(),i.ok)location.href=i.url;else S(i.error||"Could not start Google sign-in")};if(n("gg-out"))n("gg-out").onclick=async()=>{if(!confirm("Disconnect Google? Done requests stop being written to sheets."))return;if((await C("/google",{op:"disconnect"})).ok)S("Google disconnected"),R()};if(n("cg-skip"))n("cg-skip").onclick=async()=>{if((await C("/chatgpt",{op:"skip"})).ok)R()};if(n("cg-out"))n("cg-out").onclick=async()=>{if(!confirm("Disconnect ChatGPT? Gather goes back to your agent."))return;if((await C("/chatgpt",{op:"disconnect"})).ok)S("ChatGPT disconnected"),R()};if(Ht)n("src-sec").offsetWidth,n("src-sec").classList.add("open"),n("src-scrim").classList.add("on");let pt=()=>{st=!1,n("src-sec").classList.remove("open"),n("src-scrim").classList.remove("on"),n("src-sec").inert=!0,n("src-tog").setAttribute("aria-expanded","false"),n("src-tog").focus()};if(n("src-x").onclick=n("src-scrim").onclick=pt,n("src-new"))n("src-new").onclick=()=>{let s=n("src-addbox");if(Fe=s.hidden,s.hidden=!Fe,n("src-new").setAttribute("aria-expanded",Fe),Fe)n(Ge==="social"?"src-q":"src-url").focus()};if(n("src-sec").onkeydown=(s)=>{if(s.key==="Escape")s.stopPropagation(),pt()},n("src-tog").onclick=()=>{if(n("src-sec").classList.contains("open"))return pt();st=!0,R().then(()=>st&&n("src-url")?.focus())},Kn("ice-fs",(s)=>{pe=s,R()}),Kn("ice-fc",(s)=>{te=s,R()}),n("ice-unf"))n("ice-unf").onclick=()=>{if(te=pe="",!v)a.shown.clear();St(),R()};n("pbody").querySelectorAll("[data-kind]").forEach((s)=>s.onclick=()=>{if(Ge===s.dataset.kind)return;Ge=s.dataset.kind,R().then(()=>n(Ge==="social"?"src-q":"src-url")?.focus())});let Re=async(s,i)=>{let b=await C("/sources",s);if(b.ok)i(b),K();if(V())R()};n("pbody").querySelectorAll("[data-mv]").forEach((s)=>{let i=g[+s.dataset.mv],b=n("src-mb-"+s.dataset.mv),w=o.get(s.dataset.k);if(w&&[...s.options].some((L)=>L.value===w))s.value=w;(s.onchange=()=>{b.disabled=s.value===i.dir})(),b.onclick=()=>Re({op:"move",dir:i.dir,to:s.value,url:i.url},()=>S("Source moved"))}),n("pbody").querySelectorAll("[data-wb]").forEach((s)=>{let i=g[+s.dataset.wb];s.onchange=()=>{if(s.checked&&i.social===void 0&&/docs\.google\.com/.test(i.url)&&!(f.google||{}).connected){alert("Connect Google first (Sources header), so kaizen can write to the sheet."),s.checked=!1;return}if(s.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?")){s.checked=!1;return}Re({op:"writeback",dir:i.dir,url:i.url,on:s.checked},()=>S(s.checked?"Write back on":"Write back off"))}}),n("pbody").querySelectorAll("[data-ct]").forEach((s)=>{let i=g[+s.dataset.ct],b=n("src-cs-"+s.dataset.ct),w=m.get(s.dataset.k);if(w!==void 0)s.value=w;(s.oninput=()=>{b.disabled=s.value.trim()===(i.category||"")})(),s.onkeydown=(L)=>{if(L.key==="Enter"&&!b.disabled)b.click()},b.onclick=()=>{b.disabled=!0,Re({op:"category",dir:i.dir,url:i.url,category:s.value.trim()},()=>S(s.value.trim()?`New requests from ${i.label||y(i.url)} go under ${s.value.trim()}`:"Category cleared"))}});let mn=()=>{if(!document.activeElement||document.activeElement===document.body)n("pbody").querySelector(".ice-row.cur .ice-t")?.focus()},gn=(s)=>{let i=W.findIndex((L)=>P(x[L])===ue);if(i<0||!s.has(ue))return;let b=(L)=>!s.has(P(x[L])),w=W.slice(i+1).find(b)??W.slice(0,i).reverse().find(b);ue=w===void 0?null:P(x[w])},It=(s,i,b,w={})=>(async()=>{if(gn(new Set([P(i)])),(await C("/icebox",{op:s,dir:i.dir,ids:[i.id],...w})).ok){if(s==="promote"&&ae===P(i))ae=xe=null;S(b),K()}if(V())R().then(mn)})(),bn=async(s)=>{let i=x.filter((w)=>J.has(P(w)));if(!i.length)return;gn(new Set(i.map(P))),wt=!0,n("ice-bp").disabled=n("ice-bd").disabled=!0;let b=0;try{if(s==="dismiss")for(let w of new Set(i.map((L)=>L.dir))){let L=i.filter((O)=>O.dir===w).map((O)=>O.id);if((await C("/icebox",{op:s,dir:w,ids:L})).ok)b+=L.length}else for(let w of i)if((await C("/icebox",{op:s,dir:w.dir,ids:[w.id]})).ok)b++}finally{wt=!1}if(J.clear(),b)S(`${b} ${s==="promote"?"promoted to Ideas":"dismissed"}${b<i.length?`, ${i.length-b} not`:""}`);if(K(),V())R().then(mn)};if(fn)n("ice-bp").onclick=()=>bn("promote"),n("ice-bd").onclick=()=>bn("dismiss"),n("ice-bc").onclick=()=>{J.clear(),R()};if(n("ice-all"))n("ice-all").onchange=()=>{let s=W.map((i)=>P(x[i]));if(et===W.length)s.forEach((i)=>J.delete(i));else s.forEach((i)=>J.add(i));R()};if(n("ice-all"))n("ice-all").indeterminate=et>0&&et<W.length;n("pbody").querySelectorAll("[data-ck]").forEach((s)=>s.onchange=()=>{let i=P(x[+s.dataset.ck]);ue=i,s.checked?J.add(i):J.delete(i),R()});let yn=(s)=>{let i=P(x[s]);ue=i,ae=ae===i?null:i,xe=null,R().then(()=>n("ice-o-"+s)?.focus())};n("pbody").querySelectorAll(".ice-row").forEach((s)=>s.onclick=(i)=>{if(!i.target.closest("input,button"))yn(+s.dataset.i)}),n("pbody").querySelectorAll("[data-o]").forEach((s)=>s.onclick=()=>yn(+s.dataset.o)),n("pbody").querySelectorAll("details.ice-imgs").forEach((s)=>s.ontoggle=()=>s.open&&s.querySelectorAll("img[data-src]").forEach((i)=>i.src=i.dataset.src)),n("pbody").querySelectorAll("[data-pr]").forEach((s)=>s.onclick=()=>{ue=ae=xe=P(x[+s.dataset.pr]),R().then(()=>{n("ice-title")?.focus(),n("ice-title")?.select()})}),n("pbody").querySelectorAll("[data-ds]").forEach((s)=>s.onclick=()=>{s.disabled=!0,It("dismiss",x[+s.dataset.ds],"Dismissed")}),n("pbody").querySelectorAll("[data-rs]").forEach((s)=>s.onclick=()=>{s.disabled=!0,It("restore",x[+s.dataset.rs],"Restored")});let tt=x.find((s)=>P(s)===ae);if(n("ice-rp"))n("ice-rp").onclick=async()=>{let s=tt,i=P(s);oe={k:i,text:"",wait:!0},R();let b=await C("/reply",{op:"draft",dir:s.dir,id:s.id,style:_t(s.dir)});if(!oe||oe.k!==i)return;oe={k:i,text:b.text||"",note:b.ok?b.note||"":"",wait:!1},R().then(()=>n("ice-reply")?.focus())};if(n("ice-rst"))n("ice-rst").onchange=(s)=>{let i=tt.dir;at[i]=s.target.value;try{localStorage.setItem("kz-reply-style:"+i,at[i])}catch(b){}};if(n("ice-rg"))n("ice-rg").onclick=async()=>{let s=tt,i=oe;i.again=!0,R();let b=await C("/reply",{op:"draft",dir:s.dir,id:s.id,style:_t(s.dir),avoid:i.text});if(oe!==i)return;if(i.again=!1,b.text)i.text=b.text;i.note=b.ok?b.note||"":"",R().then(()=>n("ice-reply")?.focus())};if(n("ice-reply")){let s=n("ice-reply");s.oninput=()=>{oe.text=s.value,n("ice-rn").textContent=`${s.value.length} chars`,n("ice-rs").disabled=!s.value.trim()},s.onkeydown=(i)=>{if(i.key==="Escape")i.stopPropagation(),n("ice-rc").click()},n("ice-rc").onclick=()=>{oe=null,R()},n("ice-rs").onclick=async()=>{let i=tt,b=oe;b.busy=!0,R();let w=await C("/reply",{op:"send",dir:i.dir,id:i.id,text:b.text});if(w.ok)oe=null,S("Reply posted");else if(w.sent)oe=null;else b.busy=!1;if(K(),V())R()}}if(n("ice-ok")){let s=()=>{let i=n("ice-title").value.trim();if(!i){n("ice-title").focus();return}n("ice-ok").disabled=!0,It("promote",tt,"Promoted to Ideas",{text:i,details:n("ice-details").value})};n("ice-ok").onclick=s,n("ice-cancel").onclick=()=>{xe=null,R()},n("ice-title").onkeydown=(i)=>{if(i.key==="Enter")s();if(i.key==="Escape")n("ice-cancel").click()},n("ice-details").onkeydown=(i)=>{if(i.key==="Enter"&&(i.ctrlKey||i.metaKey))s();if(i.key==="Escape")n("ice-cancel").click()}}$t=(s)=>{if(s.altKey||s.ctrlKey||s.metaKey)return!1;let i=s.key,b=W.findIndex((O)=>P(x[O])===ue),w=W[b];if(i==="Escape"){if(n("src-sec")?.classList.contains("open"))return pt(),!0;if(ae){if(xe)xe=null;else ae=null;return R(),!0}if(J.size)return J.clear(),R(),!0;return!1}if(b<0)return!1;if(i==="j"||i==="k"){s.preventDefault();let O=W[Math.max(0,Math.min(W.length-1,b+(i==="j"?1:-1)))];return ue=P(x[O]),n("pbody").querySelectorAll(".ice-row").forEach((B)=>B.classList.toggle("cur",+B.dataset.i===O)),n("pbody").querySelector(`.ice-row[data-i="${O}"]`)?.scrollIntoView({block:"nearest"}),!0}let L=i==="o"?n("ice-o-"+w):i==="x"?n("ice-ck-"+w):i==="p"?J.size?n("ice-bp"):n("ice-pr-"+w):i==="d"?J.size?n("ice-bd"):n("ice-ds-"+w):null;if(!L||L.disabled)return!1;return s.preventDefault(),L.click(),!0},n("pbody").querySelectorAll("[data-chk]").forEach((s)=>{let i=g[+s.dataset.chk];s.onclick=()=>{s.disabled=!0,s.innerHTML=q("refresh")+" Checking…",Re({op:"gather",dir:i.dir,url:i.url},(b)=>{let w=b.results&&b.results[0];S(w?w.ok?`${i.label||y(i.url)}: ${w.note}${w.added?" in the icebox":""}`:`Failed: ${w.note}`:"Checked")})}}),n("pbody").querySelectorAll("[data-ag]").forEach((s)=>{let i=g[+s.dataset.ag];s.onclick=()=>{if(s.disabled=!0,z)s.innerHTML=q("refresh")+" Gathering…";Re({op:"agent",dir:i.dir,url:i.url},(b)=>{let w=b.results&&b.results[0];S(b.via==="chatgpt"?w&&w.ok?`${i.label||y(i.url)}: ${w.note}${w.added?" in the icebox":""}`:`Failed: ${w?w.note:"no answer"}`:"/kaizen gather opened in your terminal: "+Pe(b.agent))})}}),n("pbody").querySelectorAll("[data-rm]").forEach((s)=>{let i=g[+s.dataset.rm];s.onclick=()=>{if(!confirm(`Remove ${i.label||y(i.url)}? What it gathered stays; added again later, it may bring back ones you deleted or dismissed.`))return;Re({op:"remove",dir:g[+s.dataset.rm].dir,url:g[+s.dataset.rm].url},()=>S("Source removed"))}});let vn=x.findIndex((s)=>P(s)===ue);if(l){let s=x.findIndex((i)=>P(i)===l[0]);(s>=0?n(l[1]+"-"+s):vn>=0?n("ice-o-"+vn):null)?.focus()}let ft=!l&&t[2]&&n(t[2]);if(ft)(ft.disabled?n(ft.id.replace("src-mb-","src-mv-").replace("src-cs-","src-ct-")):ft)?.focus();if(n("src-add"))n("src-add").onclick=()=>{let s=Ge==="social",i=n("src-url").value.trim(),b=n("src-q").value.trim(),w=n("src-plat").value;if(s?!b:!i){n(s?"src-q":"src-url").focus();return}let L=n("src-dir").value;if(!L){n("src-dir").focus();return}n("src-add").disabled=!0,n("src-add").textContent="Adding…",(async()=>{let O=n("src-label").value.trim()||(s?`${ot[w]}: ${b}`:""),B=n("src-cat").value.trim(),F=await C("/sources",s?{op:"add",dir:L,platform:w,query:b,label:O,category:B}:{op:"add",dir:L,url:i,label:O,category:B});if(F.ok&&s)n("src-q").value=n("src-label").value=n("src-cat").value="",S("Source added"),K();else if(F.ok){n("src-url").value=n("src-label").value=n("src-cat").value="";let ge=await C("/sources",{op:"gather",dir:L,url:i}),le=ge.results&&ge.results[0];S(le?le.ok?`Source added: ${le.note} in the icebox`:`Source added, but: ${le.note}`:"Source added"),K()}else S(F.error||"Could not add source");if(F.ok)Fe=!1;if(V())R()})()},n("src-url").onkeydown=n("src-q").onkeydown=n("src-label").onkeydown=n("src-cat").onkeydown=(s)=>{if(s.key==="Enter")n("src-add").click()};if(n("ice-new"))n("ice-new").onclick=()=>{let s=!v;if(!v&&!De().length)return S("Pick a project first");let i=gt(`${s?'<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="New request for the icebox" 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>`),b=i.querySelector("form"),w=b.querySelector("textarea"),L=[],O=(F)=>{L.push(F),b.querySelector(".att").textContent=L.map((ge)=>ge.name).join(" · ")};if(b.querySelector("#newpick").onclick=()=>b.querySelector("input[type=file]").click(),b.querySelector("input[type=file]").onchange=(F)=>{[...F.target.files].forEach(O),F.target.value=""},w.onpaste=(F)=>{for(let ge of F.clipboardData.items)if(ge.kind==="file"){let le=ge.getAsFile();O(new File([le],le.name==="image.png"?`paste-${Date.now()}.png`:le.name,{type:le.type})),F.preventDefault()}},b.querySelector(".btn.ghost").onclick=()=>i.close(),s)xt(b);let B=Ke([w]);if(B)b.querySelector(".row").prepend(B);i.addEventListener("close",()=>ce(!1)),w.onkeydown=(F)=>{if(F.key==="Enter"&&!F.shiftKey)F.preventDefault(),b.requestSubmit()},b.onsubmit=async(F)=>{F.preventDefault();let ge=w.value.trim(),le=v||qe;if(!ge)return w.focus();if(!le)return S("Pick a project first");if((await C("/icebox",{op:"add",dir:le,text:ge,files:await mt(L)})).ok)S("Added to the icebox"),i.close()},w.focus()};if(n("src-go"))n("src-go").onclick=()=>{n("src-go").disabled=!0,n("src-go").innerHTML=q("refresh")+" Gathering…",Re({op:"gather",dir:v||void 0},(s)=>{let i=s.results||[],b=(B)=>g.some((F)=>F.url===B.url&&F.social),w=i.reduce((B,F)=>B+F.added,0),L=i.filter((B)=>!B.ok&&!b(B)).length,O=i.filter(b).length;S((w?`${w} new in the icebox`:"Nothing new")+(L?`, ${L} source${L===1?"":"s"} could not be read`:"")+(O?`; ${O} social for /kaizen gather`:""))})}}var Wt=new URLSearchParams(location.search).has("all");function Ie(e){if(Wt)return;try{e?localStorage.setItem("kz-proj",e):localStorage.removeItem("kz-proj")}catch(t){}}var Se=(e)=>e.split(/[\\/]/).pop()||e,_e=(e,t="")=>{let o=`<span class="plogo" aria-hidden="true">${p((Se(t)||"?").charAt(0).toUpperCase())}</span>`;return a.S?.projects?.some((r)=>r.dir===e&&r.logo)?`<img class="plogo" src="/project-logo?dir=${encodeURIComponent(e)}" alt="" loading="lazy" onerror="this.outerHTML=this.dataset.f" data-f="${p(o)}">`:o},Lt=(e,t)=>`${_e(e,t)}<span>${p(Se(t))}</span>`;function At(){let e=n("projq").value.trim().toLowerCase(),t=0;n("projs").innerHTML=a.PL.map((r,l)=>r[0].toLowerCase().includes(e)?(r[2]&&!t++?'<div class="grp">In your projects folder</div>':"")+`<button role="option" data-i="${l}" title="${p(r[0])}">${r[1]==="all"?`<span class="plogo" aria-hidden="true">${q("board")}</span><span>${p(r[0])}</span>`:r[1]==="__find"||r[1]==="__clone"?`<span class="plogo" aria-hidden="true">${q("plus")}</span><span>${p(r[0])}</span>`:Lt(r[1],r[0])}</button>`:"").join("")||'<div class="grp">No match</div>';let o=n("projs").querySelector("button");if(o)o.classList.add("on")}function qt(e){if(n("projpop").hidden=!e,n("proj").setAttribute("aria-expanded",e),e)n("projq").value="",At(),n("projq").focus()}async function xs(e){if(e==="__find"){S("Searching…");let t=await C("/find",{});if(t.ok)S(t.added?`Found ${t.added} new project${t.added===1?"":"s"}`:"No new projects found")}else if(e==="__clone"){let t=gt('<b>Clone from GitHub</b><input name="url" placeholder="https://github.com/owner/repo or git@github.com:owner/repo" aria-label="GitHub link (https or ssh)" spellcheck="false" autocomplete="off"><div class="row"><input name="to" placeholder="Clone into (a new folder is made)" 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>'),o=t.querySelector("form"),r=o.querySelector("[name=url]"),l=o.querySelector("[name=to]"),c="",m="",v=async()=>{let f=r.value.trim();if(!f)return null;let y=await C("/clone",{url:f,check:!0});if(!y.ok)return null;if(c=y.name,!l.value.trim()||l.value===m)l.value=m=y.to;return y};r.onchange=v,o.querySelector("[data-a=x]").onclick=()=>t.close(),o.querySelector("[data-a=pick]").onclick=async()=>{if(!await v())return r.focus();let f=await C("/pick-folder",{});if(f.path)l.value=f.path.replace(/[\\/]+$/,"")+(f.path.includes("\\")?"\\":"/")+c},r.focus();let k=await new Promise((f)=>{t.addEventListener("close",()=>f(null)),o.onsubmit=async(y)=>{if(y.preventDefault(),!await v())return r.focus();S("Cloning…");let u=await C("/clone",{url:r.value.trim(),to:l.value.trim()||void 0});if(!u.ok)return;f(u.dir),t.close()}});if(!k)return;await C("/cd",{dir:k}),a.all=!1,a.dir=k,Ie(k),S("Cloned")}else if(e==="all")a.all=!0,Ie();else if((await C("/cd",{dir:e})).ok)a.all=!1,a.dir=e,Ie(e);if(await K(),Q==="sources")R()}var qe=null;function xt(e){let t=Ss(),o=e.querySelector("#newdir"),r=e.querySelector("#ndc .menu-pop"),l=r.querySelector("input"),c=e.querySelector("#nds");if(!t.some((f)=>f.dir===qe))qe=t[0]?.dir??null;let m=()=>{let f=t.find((y)=>y.dir===qe);o.innerHTML=f?Lt(f.dir,f.label):"Choose project"},v=()=>{let f=l.value.trim().toLowerCase();c.innerHTML=t.filter((y)=>y.label.toLowerCase().includes(f)).map((y)=>`<button type="button" role="option" data-d="${p(y.dir)}" title="${p(y.label)}" class="${y.dir===qe?"on":""}">${Lt(y.dir,y.label)}</button>`).join("")||'<div class="grp">No match</div>',c.querySelectorAll("button").forEach((y)=>y.onclick=()=>{qe=y.dataset.d,m(),k(!1),e.querySelector("#newidea").focus()})},k=(f)=>{if(r.hidden=!f,o.setAttribute("aria-expanded",f),f)l.value="",v(),l.focus()};o.onclick=(f)=>{f.preventDefault(),k(r.hidden)},l.oninput=v,l.onkeydown=(f)=>{if(f.key==="Escape")f.stopPropagation(),k(!1);if(f.key==="Enter")f.preventDefault(),c.querySelector("button")?.click()},m()}function Ss(){let e=De(),t=e.filter((o)=>a.shown.has(o.dir));return t.length?t:e}function De(){let e=[...a.S.projects];for(let t of a.S.cards)if(!e.some((o)=>o.dir===t.state))e.push({dir:t.state,label:t.where});return e}var We=[];function Ct(){let e=n("showq").value.trim().toLowerCase();We=De(),n("shows").innerHTML=We.map((o,r)=>o.label.toLowerCase().includes(e)?`<button type="button" role="menuitemcheckbox" data-i="${r}" aria-checked="${a.shown.has(o.dir)}" title="${p(o.label)}">${Lt(o.dir,o.label)}<svg class="ck"><use href="#i-check"/></svg></button>`:"").join("")||'<div class="grp">No match</div>';let t=We.filter((o)=>a.shown.has(o.dir)).length;n("showcount").textContent=t?`${t} of ${We.length} shown`:`All ${We.length} shown`}function St(){try{localStorage.setItem("kz-show",JSON.stringify([...a.shown]))}catch(e){}if(M(),V())R()}function Hn(){n("proj").onclick=()=>qt(n("projpop").hidden),n("projq").oninput=At,n("projq").onkeydown=(e)=>{let t=[...n("projs").querySelectorAll("button")],o=t.findIndex((r)=>r.classList.contains("on"));if(e.key==="Escape")qt(!1),n("proj").focus();else if(e.key==="Enter"){if(t[o])t[o].click()}else if((e.key==="ArrowDown"||e.key==="ArrowUp")&&t[o]){e.preventDefault();let r=t[(o+(e.key==="ArrowDown"?1:t.length-1))%t.length];t[o].classList.remove("on"),r.classList.add("on"),r.scrollIntoView({block:"nearest"})}},n("projs").onclick=(e)=>{let t=e.target.closest("button");if(t)qt(!1),xs(a.PL[t.dataset.i][1])},document.addEventListener("click",(e)=>{if(!e.target.closest("#combo"))qt(!1)}),n("show").onclick=()=>{let e=n("showpop").hidden;if(n("showpop").hidden=!e,n("show").setAttribute("aria-expanded",e),e)n("showq").value="",Ct(),n("showq").focus()},n("showq").oninput=Ct,n("showq").onkeydown=(e)=>{if(e.key==="Escape")n("showpop").hidden=!0,n("show").setAttribute("aria-expanded",!1),n("show").focus()},n("shows").onclick=(e)=>{let t=e.target.closest("button[data-i]"),o=t?.dataset.i,r=We[o]?.dir;if(!r)return;a.shown.has(r)?a.shown.delete(r):a.shown.add(r),St(),n("shows").querySelector(`[data-i="${o}"]`)?.focus()},n("showall").onclick=()=>{a.shown.clear(),St()},document.addEventListener("click",(e)=>{if(!e.target.closest("#showc"))n("showpop").hidden=!0,n("show").setAttribute("aria-expanded",!1)}),n("newtop").onclick=()=>{a.mode="new",M(),n("board").scrollTo(0,0)}}var jt=(e)=>e.status==="stalled"||!!(e.pty&&!e.pty.alive);async function Et(e,t){t.disabled=!0,ve();let o=await C("/resume",{dir:e.state,id:e.id});if(!o.ok){t.disabled=!1;return}if(S("Resumed, "+Oe(o)+": "+Pe(o.agent)),o.pty)a.wantTerm=j(e);if(a.openKey===j(e))n("pfoot").innerHTML="",X(e,!0)}var Bt={"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."},Be=new Set,Qe=(e)=>j(e)+":"+e.awaiting+":"+e.moved;async function Tt(e,t,o,r){t.disabled=!0,ve();let l=await C("/decide",{dir:e.state,id:e.id,awaiting:e.awaiting,...o===void 0?{}:{why:o},...r?{answers:r}:{}});if(!l.ok){t.disabled=!1;return}if(Be.add(Qe(e)),S((o===void 0?"Approved":"Sent back")+", "+Oe(l)+": "+Pe(l.agent)),l.pty)a.wantTerm=j(e);if(a.openKey===j(e))n("pfoot").innerHTML="",X(e,!0)}function In(e){let t=n("pfoot"),o=e.awaiting==="approvals.plan";t.innerHTML=`<div class="inl" style="flex:1"><div class="q">${o?"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.${o?" 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 r=t.querySelector("textarea");r.focus(),t.querySelector("[data-a=c]").onclick=()=>{t.innerHTML="",X(e,!0)},t.querySelector("[data-a=s]").onclick=(l)=>{let c=r.value.trim();if(!c){S("Say what should change"),r.focus();return}Tt(e,l.target,c)}}function On(e,t){let o=n("pfoot");o.innerHTML=`<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((r,l)=>`<fieldset><legend>${p(r.q)}</legend>${r.options.map((c,m)=>`<label><input type="${r.multi?"checkbox":"radio"}" name="q${l}" value="${m}"${c.rec||!r.multi&&!m&&!r.options.some((v)=>v.rec)?" checked":""}> ${p(c.text)}</label>`).join("")}<input type="text" data-n="${l}" placeholder="Note (optional)" aria-label="Note: ${p(r.q)}"></fieldset>`).join("")}<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>`,o.querySelector("[data-a=c]").onclick=()=>{o.innerHTML="",X(e,!0)},o.querySelector("[data-a=s]").onclick=(r)=>Tt(e,r.target,void 0,t.map((l,c)=>{let m=[...o.querySelectorAll(`[name=q${c}]:checked`)].map((k)=>`"${l.options[+k.value].label}"`).join(", ")||"none of the options",v=o.querySelector(`[data-n="${c}"]`).value.trim();return`${l.q} — ${m}${v?"; "+v:""}`}))}var Le=null,Vt="",Qt=6,Mt=new Set,it=(()=>{try{return JSON.parse(localStorage.getItem("kz-order"))||{}}catch(e){return{}}})(),Y=!1,rt=!1,he=new Set,j=(e)=>e._k||(e.kind==="idea"?"idea:"+e.state+":"+e.text:"run:"+e.state+":"+e.id),qs=(e)=>e.kind==="run"?a.S.cards.filter((t)=>t.parent===e.id&&t.state===e.state):[],Ls=(e)=>!!e.parent&&a.S.cards.some((t)=>t.kind==="run"&&t.id===e.parent&&t.state===e.state&&!t.archived&&t.column!==4),Jt=new Set,zn=(e)=>Be.has(Qe(e))?"Sent to agent":e.status!=="waiting"?"Open":{"approvals.plan":"Review plan",findings:"See findings","approvals.review":"Review work"}[e.awaiting]||"Open";function Cs(e){let t=j(e),o=document.createElement("div");o.className="card"+(t===a.openKey?" open":"")+(t===a.menuKey?" menu":"")+(e.archived?" archived":""),o.tabIndex=0,o.dataset.k=t,o.setAttribute("role","button");let r=Ee[e.status]||"idea",l=e.kind==="idea"?"":e.text.replace(/-/g," "),c=e.status==="waiting"?nt(e.awaiting)||"needs you":e.status||"idea",m=[],v=[],k=(d,g,x="")=>`<span class="mk${x}" role="img" title="${p(g)}" aria-label="${p(g)}"><svg><use href="#${d}"/></svg></span>`;if(a.all){let d=e.where.split("/").filter(Boolean).pop()||e.where;m.push(`<span class="chip proj" title="${p(e.where)}">${p(d)}</span>`)}for(let d of e.tags||[])if(Cn[d])v.push(k("c-"+d,d==="full"?"Full run: separate reviewer":d==="lite"?"Lite run: one session":zt[d]||d,d==="full"||d==="lite"?" accent":""));else if(d==="yolo")v.push('<span class="mk warn" title="Started in Yolo: the agent runs commands without asking">yolo</span>');else m.push(`<span class="chip">${p(zt[d]||d)}</span>`);if(e.pty&&e.pty.alive&&e.pty.asking)v.push(k("i-ask","The agent is asking you something: open it, terminal button at the top"," warn ask"));else if(e.pty&&e.pty.alive)v.push(k("i-term","The agent is running in this card: open it, terminal button at the top"," run"));let f=e.notes&&/^\[[^\]]*\] (?:Gathered from|Promoted from the icebox:) (.+?)\. Outside text/.exec(e.notes);if(e.parent)m.push(`<span class="chip link" title="Filed by ${p(e.parent)}">${p(e.parent.replace(/^\d{4}-\d\d-\d\d-/,"").replace(/-/g," "))}</span>`);if(e.branch)m.push(`<span class="chip" title="Built on its own branch, ${p(e.branch)}">branch</span>`);if(f)m.push(`<span class="chip" title="From ${p(f[1])}">gathered</span>`);else if(e.notes)m.push('<span class="chip">notes</span>');if(e.git)v.push(k("c-git",e.git==="pushed"?"Committed & pushed":"Not committed & pushed yet",e.git==="pushed"?" ok":" warn"));if(e.open)v.push(k("i-bug",`${e.open} open finding${e.open===1?"":"s"} to fix`," warn"));let y=rt&&!Y&&e.column===4;if(e.column===4&&!Y)o.classList.add("compact");let u=e.kind==="run"&&!Y&&!y?e.status==="waiting"?`<button class="btn sm${Be.has(Qe(e))?"":" pri"} act" data-a="open">${zn(e)}</button>`:e.status==="stalled"&&jt(e)?'<button class="btn sm act" data-a="resume">Resume</button>':"":"";o.innerHTML=(y?`<input type="checkbox" class="pick" ${he.has(t)?"checked":""} aria-label="Select">`:"")+`<div class="c-id"><span class="st d-${r}" role="img" title="${p(c)}" aria-label="${p(c)}"><svg><use href="#s-${r}"/></svg></span><span class="idt">${p(l)}</span><span class="age">${e.kind==="run"?Ln(e.moved||a.S.now,a.S.now):""}</span><button class="ib sm more" data-a="menu" title="Actions" aria-label="Actions" aria-haspopup="menu" aria-expanded="${t===a.menuKey}">${q("more")}</button></div>
24
+ <div class="c-title"${e.kind==="idea"&&Te(e.text).parts.length?` title="${p(e.text)}"`:""}>${p(e.kind==="idea"?Te(e.text).head:e.title||e.id)}</div>
25
25
  ${u}
26
- ${g.length||y.length?`<div class="c-foot"><span class="c-chips">${g.join("")}</span><span class="c-marks">${y.join("")}</span></div>`:""}`;let f=xs(e);if(f.length){let d=Wt.has(t),m=f.filter((T)=>T.status==="done").length,x=f.some((T)=>T.status==="waiting"),N=document.createElement("div");N.className="kids",N.innerHTML=`<button class="kids-t" aria-expanded="${d}">${d?"▾":"▸"} ${f.length} ${f.length===1?"child":"children"}${m?` · ${m} done`:""}${x?'<span class="kids-w" title="A child is waiting on you"></span>':""}</button>`+(d?`<ul>${f.map((T,I)=>{let O=Ae[T.status]||"idea";return`<li data-i="${I}" tabindex="0" role="button" title="Open"><span class="st d-${O}"><svg><use href="#s-${O}"/></svg></span><span class="kids-s">${p(Oe[T.status]||T.status)}</span><span class="kids-n" title="${p(T.title||T.text||T.id)}">${p(T.kind==="idea"?je(T.text).head:(T.text||T.id||"").replace(/-/g," "))}</span></li>`}).join("")}</ul>`:""),N.querySelector(".kids-t").onclick=(T)=>{T.stopPropagation(),d?Wt.delete(t):Wt.add(t),M()},N.querySelectorAll("li").forEach((T)=>{let I=(O)=>{O.stopPropagation(),qe(f[+T.dataset.i])};T.onclick=I,T.onkeydown=(O)=>(O.key==="Enter"||O.key===" ")&&(O.preventDefault(),I(O))}),o.appendChild(N)}if(t===a.menuKey){let d=[];if(V){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==="idea"&&e.status==="idea")d.push(["run","Run",q("play")],["edit","Edit",q("pen")],...a.S.projects.some((m)=>m.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",In(e),q("open")]),d.push(["archive","Archive",q("archive")]),e.status==="waiting"||e.status==="running"||e.status==="stalled")d.push(null,["reject","Abandon",q("x"),"danger"]);o.innerHTML+=`<div class="menu-pop" role="menu">${d.map((m)=>m?`<button role="menuitem" data-a="${m[0]}" class="${m[3]||""}">${m[2]}${m[1]}</button>`:"<hr>").join("")}</div>`}if(o.querySelectorAll("[data-a]").forEach((d)=>d.onclick=async(m)=>{m.stopPropagation();let x=d.dataset.a,N=a.menuKey;if(a.menuKey=null,x==="menu"){a.menuKey=N===t?null:t,M();return}if(x==="open"){qe(e);return}if(x==="resume"){Ct(e,d);return}if(x==="archive"||x==="restore"){Et(e,x==="archive");return}a.mode=x,a.modeKey=t,M()}),k)return o.classList.toggle("picked",he.has(t)),o.onclick=()=>{he.has(t)?he.delete(t):he.add(t),M()},o;return o.onclick=()=>{if(a.menuKey){a.menuKey=null,M();return}qe(e)},o.onkeydown=(d)=>{if(d.key==="Enter"||d.key===" ")d.preventDefault(),qe(e)},o}function Ls(e){let t=document.createElement("li");t.className="inl";let o=e.state;if(a.mode==="edit")t.innerHTML=`<input value="${p(e.text)}"><textarea placeholder="Notes: links, paths, background">${p(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 l=t.querySelector("input").value.trim(),c=t.querySelector("textarea").value.trim();if(a.mode=null,l&&(l!==e.text||c!==(e.notes||"")))await L("/inbox",{op:"edit",dir:o,text:e.text,next:l,notes:c});R()};if(a.mode==="reject"){let l=e.kind==="idea";t.innerHTML=`<div class="q">${l?"Reject this idea?":"Abandon this run?"} The reason is recorded.</div><input placeholder="Reason"><div class="row"><button class="btn ghost" data-a="c">Keep it</button><button class="btn warn" data-a="s">${l?"Reject":"Abandon"}</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{let c=t.querySelector("input").value.trim();if(!c){S("A reason is required");return}if(a.mode=null,(l?await L("/inbox",{op:"reject",dir:o,text:e.text,reason:c}):await L("/abort",{dir:o,id:e.id,why:c})).ok)S(l?"Rejected":"Abandoned, reason recorded");R()}}if(a.mode==="delete")t.innerHTML=`<div class="q">Delete "${p(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(a.mode=null,(await L("/inbox",{op:"delete",dir:o,text:e.text})).ok)S("Deleted");R()};if(a.mode==="move"){let l=a.S.projects.filter((c)=>c.dir!==o);t.innerHTML=`<div class="q">Move "${p(e.text)}" to</div><select class="sel" aria-label="Project">${l.map((c)=>`<option value="${p(c.dir)}" title="${p(c.label)}">${p(me(c.label))}</option>`).join("")}</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"),g=c.value,y=c.selectedOptions[0]?.textContent;if(a.mode=null,(await L("/inbox",{op:"move",dir:o,to:g,text:e.text})).ok)S(`Moved to ${y}`);R()}}if(a.mode==="run")t.innerHTML=`<div class="q">Run "${p(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=()=>On(e,"full"),t.querySelector("[data-a=l]").onclick=()=>On(e,"lite"),Cs(t,e);if(t.querySelector("[data-a=c]").onclick=()=>{a.mode=null,M()},t.querySelectorAll("input,textarea").forEach((l,c)=>De(l,"f:"+a.mode+":"+a.modeKey+":"+c)),a.mode!=="run"){let l=et([...t.querySelectorAll("input,textarea")]);if(l)t.querySelector(".row").prepend(l)}let r=t.querySelector("input");if(r)r.onkeydown=(l)=>{if(l.key==="Enter")t.querySelector("[data-a=s]").click();if(l.key==="Escape")a.mode=null,M()},setTimeout(()=>r.focus());return t}var ne=null,Te=(e)=>e.name+(e.note?". "+e.note:"");async function Cs(e,t){let o=j(t),r=e.querySelector("#run-agent"),l=await fetch("/agents?dir="+encodeURIComponent(t.state)).then((v)=>v.json()).catch(()=>null);if(!l||!l.agents||!l.agents.length){e.querySelector("#run-pick").hidden=!0;return}if(l.agents.some((v)=>v.cmd==="agy"))l.agents=l.agents.filter((v)=>v.cmd!=="gemini");if(!ne||ne.k!==o)ne={k:o,agent:l.agents.some((v)=>v.cmd===l.default)?l.default:l.agents[0].cmd};r.innerHTML=l.agents.map((v)=>`<button type="button" class="agent" role="radio" aria-checked="${v.cmd===ne.agent}" data-cmd="${p(v.cmd)}"><svg><use href="#c-${p(v.cmd)}"/></svg>${p(v.name)}</button>`).join("");let c=e.querySelector("#run-yolo"),g=c.querySelector(".chk"),y=()=>{let v=!!l.agents.find((k)=>k.cmd===ne.agent)?.yolo,h=!!ne.yolo;c.hidden=!v,g.classList.toggle("on",h),g.setAttribute("aria-checked",h)};r.querySelectorAll(".agent").forEach((v)=>v.onclick=()=>{ne.agent=v.dataset.cmd,r.querySelectorAll(".agent").forEach((h)=>h.setAttribute("aria-checked",h===v)),y()}),g.onclick=()=>{ne.yolo=!ne.yolo,y()},y(),r.onkeydown=g.onkeydown=(v)=>{if(v.key==="Escape")a.mode=null,M()}}async function On(e,t){a.mode=null,ye();let o=ne&&ne.k===j(e)?ne:null;ne=null;let r=!!(o&&o.yolo&&!n("run-yolo")?.hidden),l=await L("/run",{dir:e.state,text:e.text,kind:t,agent:o?o.agent:void 0,yolo:r});if(l.ok){if(S((t==="full"?"Full":"Lite")+(r?" yolo":"")+" run "+Ne(l)+": "+Te(l.agent)),l.pty)a.wantTerm=j(e)}R()}var As={1:"Ideas you run are planned here",2:"Approved plans are built here",3:"Finished builds are checked here"};function M(){if(a.mode&&a.mode!=="new"&&!a.S.cards.some((u)=>j(u)===a.modeKey))a.mode=null;if(a.mode!=="run"&&ne)ne.yolo=!1;if(!a.mode)wn("f:");try{sessionStorage.setItem("kz-form",JSON.stringify(["new","edit","reject"].includes(a.mode)?[a.mode,a.modeKey]:null))}catch(u){}let e=Qt(),t=a.all&&a.shown.size&&xe().some((u)=>a.shown.has(u.dir)),o=a.S.cards.filter((u)=>u.archived===V&&(!t||a.shown.has(u.state))),r=n("board");r.innerHTML="",r.removeAttribute("aria-busy");let l=a.S.cards.filter((u)=>u.archived).length,c=(u)=>o.filter((f)=>f.status===u).length,g=[];for(let u of["waiting","running","stalled","starting","idea","done","abandoned"]){let f=c(u);if(!f&&u!=="running")continue;let d=u==="running"?'<span class="d-run"><span class="pulse"></span></span>':`<span class="st d-${Ae[u]}"><svg><use href="#s-${Ae[u]}"/></svg></span>`;g.push(`<button class="${u==="waiting"?"attn ":""}${Se===u?"on":""}" data-f="${u}" title="Show only ${Oe[u]}">${d}${f} ${Oe[u]}${u==="idea"&&f!==1?"s":""}</button>`)}g.push(`<button class="${V?"on":""}" data-f="__archive" title="${o.filter((u)=>u.kind==="idea").length} ideas · ${o.filter((u)=>u.kind==="run").length} runs on the board">${V?"Back to board":"Archive "+l}</button>`),n("strip").innerHTML=g.join(""),n("fbtn").hidden=!1,n("fbtn").classList.toggle("on",!!Se||V),n("fbtn").classList.toggle("wait",!Se&&!V&&c("waiting")>0),n("strip").querySelectorAll("button").forEach((u)=>u.onclick=()=>{if(u.dataset.f==="__archive"){V=!V,Se=null,M();return}Se=Se===u.dataset.f?null:u.dataset.f,M()}),a.PL=[["All projects","all"],...a.S.projects.map((u)=>[u.label,u.dir]),...a.S.fresh.map((u)=>[u.label,u.dir,1]),["Find projects on this PC…","__find"],["Clone from GitHub…","__clone"]];let y=a.all||!a.S.dir?"All projects":(a.PL.find((u)=>u[1]===a.S.dir)||["All projects"])[0];if(n("proj").innerHTML=a.all||!a.S.dir?`<span class="plogo" aria-hidden="true">${q("board")}</span><span>${p(y)}</span>`:`${Ue(a.S.dir,y)}<span>${p(me(y))}</span>`,n("proj").title=y,!n("projpop").hidden)qt();n("showc").hidden=!a.all;let v=xe(),h=v.filter((u)=>a.shown.has(u.dir)).length;if(n("show").textContent=h?`Showing ${h} of ${v.length}`:"Showing all",!n("showpop").hidden)St();if(!o.length&&a.mode!=="new"){if(r.innerHTML=V?'<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>',!V)n("newempty").onclick=()=>{a.mode="new",M()}}qn.forEach((u,f)=>{let d=document.createElement("section"),m=Bt.trim().toLowerCase(),x=o.filter((C)=>C.column===f&&(!Se||C.status===Se)&&(V||!Ss(C))&&(!m||`${C.title||""} ${C.text||""} ${C.id||""}`.toLowerCase().includes(m)));if(f<4){let C=(G)=>G.kind==="run"?G.moved||0:a.S.cards.indexOf(G),H=at[f]==="old",U=(G)=>G.status==="waiting"?1:0;x.sort((G,W)=>U(W)-U(G)||(H?C(G)-C(W):C(W)-C(G)))}let N=f===4&&!V&&x.length,T=(C)=>x.filter((H)=>a.S.now-(H.moved||a.S.now)>C*$n),I=`<h2>${u}<i>${x.length||""}</i>${f<4&&x.length>1?`<button class="btn sm ghost ord" title="Sort order">${at[f]==="old"?"Oldest":"Newest"}</button>`:""}${f===0&&!V?`<button class="ib sm" title="New idea" aria-label="New idea">${q("plus")}</button>`:""}`;if(N&&it)I+=`<span class="pickbar"><button class="btn sm ghost" data-p="cancel">Cancel</button><button class="btn sm pri" data-p="go" ${he.size?"":"disabled"}>Archive ${he.size||""}</button></span>`;else if(N)I+='<button class="btn sm ghost clear" title="Archive finished runs">Clear ▾</button>';if(N&&a.clearMenu)I+=`<div class="menu-pop clear-pop"><button data-c="7">Older than a week <i>${T(7).length}</i></button><button data-c="3">Older than 3 days <i>${T(3).length}</i></button><button data-c="0">Everything <i>${x.length}</i></button><hr><button data-c="pick">Pick which…</button></div>`;d.innerHTML=I+"</h2>";{let C=d.querySelector(".ord");if(C)C.onclick=(H)=>{H.stopPropagation(),at[f]=at[f]==="old"?"new":"old";try{localStorage.setItem("kz-order",JSON.stringify(at))}catch(U){}M()}}if(f===0&&!V)d.querySelector(".ib").onclick=()=>{a.mode="new",M()};if(N){let C=d.querySelector(".clear");if(C)C.onclick=(H)=>{H.stopPropagation(),a.clearMenu=!a.clearMenu,M()};d.querySelectorAll("[data-c]").forEach((H)=>H.onclick=async(U)=>{U.stopPropagation(),a.clearMenu=!1;let G=H.dataset.c;if(G==="pick"){it=!0,he.clear(),M();return}let W=T(+G);if(!W.length){S("Nothing that old"),M();return}for(let K of W)await L("/archive",{dir:K.state,id:K.id});S("Archived "+W.length),R()}),d.querySelectorAll("[data-p]").forEach((H)=>H.onclick=async(U)=>{if(U.stopPropagation(),H.dataset.p==="cancel"){it=!1,he.clear(),M();return}let G=x.filter((W)=>he.has(j(W)));it=!1,he.clear();for(let W of G)await L("/archive",{dir:W.state,id:W.id});S("Archived "+G.length),R()})}let O=document.createElement("ul");if(f===0&&a.mode==="new"){let C=document.createElement("li");C.className="inl",C.innerHTML=`${a.all?'<div class="combo" id="ndc"><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 would you like done?"><textarea id="newnotes" placeholder="Notes: links, paths, background (optional)"></textarea><div class="att" id="newatt"></div><input type="file" id="newfile" multiple hidden><div class="row"><button class="ib" id="newpick" aria-label="Attach files" title="Attach files">${q("clip")}</button><button class="btn ghost" id="nc">Cancel</button><button class="btn pri" id="ns">Add</button></div>`,O.appendChild(C);let H=C.querySelector("#newidea"),U=[];De(H,"f:new:0"),De(C.querySelector("textarea"),"f:new:1");let G=async()=>{let K=H.value.trim();if(!K)return;let D=C.querySelector("textarea").value.trim();a.mode=null;let ae=a.all?Re:null;if((await L("/inbox",ae?{op:"add",dir:ae,text:K,notes:D}:{op:"add",text:K,notes:D})).ok){if(S("Idea added"),U.length){let F=await Promise.all(U.map((re)=>new Promise((le)=>{let He=new FileReader;He.onload=()=>le({name:re.name,data:He.result}),He.readAsDataURL(re)})));if(!(await L("/note",{dir:ae,for:K,text:"",files:F})).ok)S("Idea added, files not attached")}}R()},W=(K)=>{U.push(K),C.querySelector("#newatt").textContent=U.map((D)=>D.name).join(" · ")};C.querySelector("#newpick").onclick=()=>C.querySelector("#newfile").click(),C.querySelector("#newfile").onchange=(K)=>{[...K.target.files].forEach(W),K.target.value=""};for(let K of[H,C.querySelector("textarea")])K.onpaste=(D)=>{for(let ae of D.clipboardData.items)if(ae.kind==="file"){let E=ae.getAsFile();W(new File([E],E.name==="image.png"?`paste-${Date.now()}.png`:E.name,{type:E.type})),D.preventDefault()}};if(a.all)Rn(C);C.querySelector("#ns").onclick=G,C.querySelector("#nc").onclick=()=>{a.mode=null,M()};{let K=et([H,C.querySelector("textarea")]);if(K)C.querySelector(".row").prepend(K)}H.onkeydown=(K)=>{if(K.key==="Enter")G();if(K.key==="Escape")a.mode=null,M()},setTimeout(()=>H.focus())}let ge=jt.has(f)||x.length<=_t+1?x:x.slice(0,_t);if(ge.forEach((C)=>{if(a.modeKey===j(C)&&a.mode&&a.mode!=="new"){O.appendChild(Ls(C));return}let H=document.createElement("li");H.appendChild(qs(C)),O.appendChild(H)}),ge.length<x.length){let C=document.createElement("li");C.innerHTML=`<button class="more-btn">Show ${x.length-ge.length} more</button>`,C.firstChild.onclick=()=>{jt.add(f),M()},O.appendChild(C)}else if(jt.has(f)&&x.length>_t+1){let C=document.createElement("li");C.innerHTML='<button class="more-btn">Show less</button>',C.firstChild.onclick=()=>{jt.delete(f),M()},O.appendChild(C)}if(!x.length&&f>=1&&f<=3&&o.length&&!Se&&!m&&!V)O.innerHTML=`<li class="hint">${As[f]}</li>`;d.appendChild(O),r.appendChild(d)});let k=a.S.cards.filter((u)=>u.kind==="run"&&u.status==="waiting"&&!u.archived).length;if(document.title=k?`(${k}) kaizen`:"kaizen",fe(!1),e&&document.activeElement===document.body)Jt(e)?.focus({preventScroll:!0})}function zn(){let e=n("bq");De(e,"bq"),Bt=e.value,e.addEventListener("input",()=>{Bt=e.value,M()}),e.addEventListener("keydown",(t)=>{if(t.key==="Escape")t.stopPropagation(),e.value="",e.dispatchEvent(new Event("input")),e.blur()})}function Gn(){let e=n("fbtn"),t=document.querySelector(".ftr"),o=(r)=>{if(t.classList.toggle("on",r),e.setAttribute("aria-expanded",r),r)t.style.top=e.getBoundingClientRect().bottom+6+"px"};e.onclick=()=>o(!t.classList.contains("on")),document.addEventListener("pointerdown",(r)=>{if(t.classList.contains("on")&&!t.contains(r.target)&&!e.contains(r.target))o(!1)}),document.addEventListener("keydown",(r)=>r.key==="Escape"&&t.classList.contains("on")&&o(!1))}var Qt=()=>document.activeElement?.closest?.("#board .card")?.dataset.k,Jt=(e)=>e&&n("board").querySelector('.card[data-k="'+CSS.escape(e)+'"]');function Un(e,t){let o=[...n("board").querySelectorAll("section")].map((y)=>[...y.querySelectorAll(".card")]).filter((y)=>y.length);if(!o.length)return;let r=document.activeElement.closest(".card"),l=o.findIndex((y)=>y.includes(r)),c=0;if(l<0)l=0;else if(c=o[l].indexOf(r),t)c=Math.max(0,Math.min(o[l].length-1,c+t));else l=Math.max(0,Math.min(o.length-1,l+e)),c=Math.min(c,o[l].length-1);let g=o[l][c];g.focus({preventScroll:!0}),g.scrollIntoView({block:"nearest",inline:"nearest"})}var Fn=()=>a.dir?{"x-kz-dir":a.dir}:{};async function R(e){let t;try{t=await fetch("/state"+(a.all?"?all=1":""),{headers:Fn()})}catch{S(_n);return}let o=a.openKey&&a.S.cards.find((r)=>j(r)===a.openKey);a.S=await t.json();{let r=new Map;for(let l of a.S.cards){let c=j(l),g=r.get(c)||0;r.set(c,g+1),l._k=g?c+"#"+g:c}}for(let r of a.S.cards){if(r.kind!=="run"||r.archived)continue;if(due(j(r),r.notice,r.status==="done",a.S.now))yn(r.notice)}if(!(e&&a.mode&&n("board").querySelector(".inl input")))M();if(a.wantTerm&&!a.openKey&&!a.mode){let r=a.S.cards.find((l)=>j(l)===a.wantTerm&&l.pty);if(r){a.wantTerm=null,qe(r);return}}if(a.openKey){let r=a.S.cards.find((c)=>j(c)===a.openKey),l=n("pbody").querySelector(".notes textarea");if(l&&document.activeElement===l)return;if(!r){if(o&&o.kind==="idea")js(o);return}if(a.runDoc)X(r,!0,!0);else if(r.kind==="idea")Vt(r)}}async function js(e){let t=j(e),o=(l)=>l.toLowerCase().replace(/\s+/g," ").trim(),r=o(e.text);for(let l of a.S.cards.filter((c)=>c.kind==="run"&&c.state===e.state).sort((c,g)=>(g.moved??0)-(c.moved??0)).slice(0,5)){let c=await fetch(`/run/${encodeURIComponent(l.id)}?dir=${encodeURIComponent(l.state)}`).then((g)=>g.json()).catch(()=>null);if(a.openKey!==t)return;if(o(c?.request||"").includes(r)){qe(l);return}}if(a.openKey===t)Z()}var _n="The board server is not running — start it with kaizen web (or the shortcut), then reload.";async function L(e,t){let o;try{o=await fetch(e,{method:"POST",headers:{"content-type":"application/json",...Fn()},body:JSON.stringify(t)})}catch{return S(_n),{ok:!1}}let r=await o.json().catch(()=>({}));if(!o.ok){let l=r.error||r.why||"That did not work";if(S(l,{title:l.slice(0,80),what:`POST ${e} failed (HTTP ${o.status}): ${l}`}),r.manual)console.log("run manually:",r.manual)}return r}var Tt=null,Mt=null,Wn=null,Ne=(e)=>e.pty?"running in its card":"opened in your terminal",Es=()=>Wn||=new Promise((e,t)=>{let o=document.createElement("link");o.rel="stylesheet",o.href="/term.css",document.head.append(o);let r=document.createElement("script");r.src="/term.js",r.onload=()=>e(window.kzTerm),r.onerror=()=>{Wn=null,t()},document.head.append(r)});function we(){if(Mt)Mt();Mt=null,Tt=null,n("panel").classList.remove("wide")}async function Yt(e){let t=e.pty.key;if(Tt===t&&n("pbody").querySelector(".term"))return;we(),Tt=t,n("panel").classList.add("wide"),n("pbody").innerHTML='<div class="term" aria-label="Agent terminal"></div>';let o;try{o=await Es()}catch{n("pbody").innerHTML='<p style="color:var(--dim)">The terminal could not load.</p>';return}if(Tt!==t)return;Mt=o(n("pbody").querySelector(".term"),t,()=>R(!0))}function Ts(e){let t=[];for(let o of(e||"").split(`
26
+ ${m.length||v.length?`<div class="c-foot"><span class="c-chips">${m.join("")}</span><span class="c-marks">${v.join("")}</span></div>`:""}`;let h=qs(e);if(h.length){let d=Jt.has(t),g=h.filter((T)=>T.status==="done").length,x=h.some((T)=>T.status==="waiting"),H=document.createElement("div");H.className="kids",H.innerHTML=`<button class="kids-t" aria-expanded="${d}">${d?"▾":"▸"} ${h.length} ${h.length===1?"child":"children"}${g?` · ${g} done`:""}${x?'<span class="kids-w" title="A child is waiting on you"></span>':""}</button>`+(d?`<ul>${h.map((T,z)=>{let U=Ee[T.status]||"idea";return`<li data-i="${z}" tabindex="0" role="button" title="Open"><span class="st d-${U}"><svg><use href="#s-${U}"/></svg></span><span class="kids-s">${p(Ue[T.status]||T.status)}</span><span class="kids-n" title="${p(T.title||T.text||T.id)}">${p(T.kind==="idea"?Te(T.text).head:(T.text||T.id||"").replace(/-/g," "))}</span></li>`}).join("")}</ul>`:""),H.querySelector(".kids-t").onclick=(T)=>{T.stopPropagation(),d?Jt.delete(t):Jt.add(t),M()},H.querySelectorAll("li").forEach((T)=>{let z=(U)=>{U.stopPropagation(),Ce(h[+T.dataset.i])};T.onclick=z,T.onkeydown=(U)=>(U.key==="Enter"||U.key===" ")&&(U.preventDefault(),z(U))}),o.appendChild(H)}if(t===a.menuKey){let d=[];if(Y){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==="idea"&&e.status==="idea")d.push(["run","Run",q("play")],["edit","Edit",q("pen")],...a.S.projects.some((g)=>g.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",zn(e),q("open")]),d.push(["archive","Archive",q("archive")]),e.status==="waiting"||e.status==="running"||e.status==="stalled")d.push(null,["reject","Abandon",q("x"),"danger"]);o.innerHTML+=`<div class="menu-pop" role="menu">${d.map((g)=>g?`<button role="menuitem" data-a="${g[0]}" class="${g[3]||""}">${g[2]}${g[1]}</button>`:"<hr>").join("")}</div>`}if(o.querySelectorAll("[data-a]").forEach((d)=>d.onclick=async(g)=>{g.stopPropagation();let x=d.dataset.a,H=a.menuKey;if(a.menuKey=null,x==="menu"){a.menuKey=H===t?null:t,M();return}if(x==="open"){Ce(e);return}if(x==="resume"){Et(e,d);return}if(x==="archive"||x==="restore"){Dt(e,x==="archive");return}a.mode=x,a.modeKey=t,M()}),y)return o.classList.toggle("picked",he.has(t)),o.onclick=()=>{he.has(t)?he.delete(t):he.add(t),M()},o;return o.onclick=()=>{if(a.menuKey){a.menuKey=null,M();return}Ce(e)},o.onkeydown=(d)=>{if(d.key==="Enter"||d.key===" ")d.preventDefault(),Ce(e)},o}function As(e){let t=document.createElement("li");t.className="inl";let o=e.state;if(a.mode==="edit")t.innerHTML=`<input value="${p(e.text)}"><textarea placeholder="Notes: links, paths, background">${p(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 l=t.querySelector("input").value.trim(),c=t.querySelector("textarea").value.trim();if(a.mode=null,l&&(l!==e.text||c!==(e.notes||"")))await C("/inbox",{op:"edit",dir:o,text:e.text,next:l,notes:c});K()};if(a.mode==="reject"){let l=e.kind==="idea";t.innerHTML=`<div class="q">${l?"Reject this idea?":"Abandon this run?"} The reason is recorded.</div><input placeholder="Reason"><div class="row"><button class="btn ghost" data-a="c">Keep it</button><button class="btn warn" data-a="s">${l?"Reject":"Abandon"}</button></div>`,t.querySelector("[data-a=s]").onclick=async()=>{let c=t.querySelector("input").value.trim();if(!c){S("A reason is required");return}if(a.mode=null,(l?await C("/inbox",{op:"reject",dir:o,text:e.text,reason:c}):await C("/abort",{dir:o,id:e.id,why:c})).ok)S(l?"Rejected":"Abandoned, reason recorded");K()}}if(a.mode==="delete")t.innerHTML=`<div class="q">Delete "${p(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(a.mode=null,(await C("/inbox",{op:"delete",dir:o,text:e.text})).ok)S("Deleted");K()};if(a.mode==="move"){let l=a.S.projects.filter((c)=>c.dir!==o);t.innerHTML=`<div class="q">Move "${p(e.text)}" to</div><select class="sel" aria-label="Project">${l.map((c)=>`<option value="${p(c.dir)}" title="${p(c.label)}">${p(Se(c.label))}</option>`).join("")}</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,v=c.selectedOptions[0]?.textContent;if(a.mode=null,(await C("/inbox",{op:"move",dir:o,to:m,text:e.text})).ok)S(`Moved to ${v}`);K()}}if(a.mode==="run")t.innerHTML=`<div class="q">Run "${p(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=()=>Un(e,"full"),t.querySelector("[data-a=l]").onclick=()=>Un(e,"lite"),js(t,e);if(t.querySelector("[data-a=c]").onclick=()=>{a.mode=null,M()},t.querySelectorAll("input,textarea").forEach((l,c)=>Ne(l,"f:"+a.mode+":"+a.modeKey+":"+c)),a.mode!=="run"){let l=Ke([...t.querySelectorAll("input,textarea")]);if(l)t.querySelector(".row").prepend(l)}let r=t.querySelector("input");if(r)r.onkeydown=(l)=>{if(l.key==="Enter")t.querySelector("[data-a=s]").click();if(l.key==="Escape")a.mode=null,M()},setTimeout(()=>r.focus());return t}var se=null,Pe=(e)=>e.name+(e.note?". "+e.note:"");async function js(e,t){let o=j(t),r=e.querySelector("#run-agent"),l=await fetch("/agents?dir="+encodeURIComponent(t.state)).then((k)=>k.json()).catch(()=>null);if(!l||!l.agents||!l.agents.length){e.querySelector("#run-pick").hidden=!0;return}if(l.agents.some((k)=>k.cmd==="agy"))l.agents=l.agents.filter((k)=>k.cmd!=="gemini");if(!se||se.k!==o)se={k:o,agent:l.agents.some((k)=>k.cmd===l.default)?l.default:l.agents[0].cmd};r.innerHTML=l.agents.map((k)=>`<button type="button" class="agent" role="radio" aria-checked="${k.cmd===se.agent}" data-cmd="${p(k.cmd)}"><svg><use href="#c-${p(k.cmd)}"/></svg>${p(k.name)}</button>`).join("");let c=e.querySelector("#run-yolo"),m=c.querySelector(".chk"),v=()=>{let k=!!l.agents.find((y)=>y.cmd===se.agent)?.yolo,f=!!se.yolo;c.hidden=!k,m.classList.toggle("on",f),m.setAttribute("aria-checked",f)};r.querySelectorAll(".agent").forEach((k)=>k.onclick=()=>{se.agent=k.dataset.cmd,r.querySelectorAll(".agent").forEach((f)=>f.setAttribute("aria-checked",f===k)),v()}),m.onclick=()=>{se.yolo=!se.yolo,v()},v(),r.onkeydown=m.onkeydown=(k)=>{if(k.key==="Escape")a.mode=null,M()}}async function Un(e,t){a.mode=null,ve();let o=se&&se.k===j(e)?se:null;se=null;let r=!!(o&&o.yolo&&!n("run-yolo")?.hidden),l=await C("/run",{dir:e.state,text:e.text,kind:t,agent:o?o.agent:void 0,yolo:r});if(l.ok){if(S((t==="full"?"Full":"Lite")+(r?" yolo":"")+" run "+Oe(l)+": "+Pe(l.agent)),l.pty)a.wantTerm=j(e)}K()}var Es={1:"Ideas you run are planned here",2:"Approved plans are built here",3:"Finished builds are checked here"};function M(){if(a.mode&&a.mode!=="new"&&!a.S.cards.some((u)=>j(u)===a.modeKey))a.mode=null;if(a.mode!=="run"&&se)se.yolo=!1;if(!a.mode)Sn("f:");try{sessionStorage.setItem("kz-form",JSON.stringify(["new","edit","reject"].includes(a.mode)?[a.mode,a.modeKey]:null))}catch(u){}let e=Yt(),t=a.all&&a.shown.size&&De().some((u)=>a.shown.has(u.dir)),o=a.S.cards.filter((u)=>u.archived===Y&&(!t||a.shown.has(u.state))),r=n("board");r.innerHTML="",r.removeAttribute("aria-busy");let l=a.S.cards.filter((u)=>u.archived).length,c=(u)=>o.filter((h)=>h.status===u).length,m=[];for(let u of["waiting","running","stalled","starting","idea","done","abandoned"]){let h=c(u);if(!h&&u!=="running")continue;let d=u==="running"?'<span class="d-run"><span class="pulse"></span></span>':`<span class="st d-${Ee[u]}"><svg><use href="#s-${Ee[u]}"/></svg></span>`;m.push(`<button class="${u==="waiting"?"attn ":""}${Le===u?"on":""}" data-f="${u}" title="Show only ${Ue[u]}">${d}${h} ${Ue[u]}${u==="idea"&&h!==1?"s":""}</button>`)}m.push(`<button class="${Y?"on":""}" data-f="__archive" title="${o.filter((u)=>u.kind==="idea").length} ideas · ${o.filter((u)=>u.kind==="run").length} runs on the board">${Y?"Back to board":"Archive "+l}</button>`),n("strip").innerHTML=m.join(""),n("fbtn").hidden=!1,n("fbtn").classList.toggle("on",!!Le||Y),n("fbtn").classList.toggle("wait",!Le&&!Y&&c("waiting")>0),n("strip").querySelectorAll("button").forEach((u)=>u.onclick=()=>{if(u.dataset.f==="__archive"){Y=!Y,Le=null,M();return}Le=Le===u.dataset.f?null:u.dataset.f,M()}),a.PL=[["All projects","all"],...a.S.projects.map((u)=>[u.label,u.dir]),...a.S.fresh.map((u)=>[u.label,u.dir,1]),["Find projects on this PC…","__find"],["Clone from GitHub…","__clone"]];let v=a.all||!a.S.dir?"All projects":(a.PL.find((u)=>u[1]===a.S.dir)||["All projects"])[0];if(n("proj").innerHTML=a.all||!a.S.dir?`<span class="plogo" aria-hidden="true">${q("board")}</span><span>${p(v)}</span>`:`${_e(a.S.dir,v)}<span>${p(Se(v))}</span>`,n("proj").title=v,!n("projpop").hidden)At();n("showc").hidden=!a.all;let k=De(),f=k.filter((u)=>a.shown.has(u.dir)).length;if(n("show").textContent=f?`Showing ${f} of ${k.length}`:"Showing all",!n("showpop").hidden)Ct();if(!o.length&&a.mode!=="new"){if(r.innerHTML=Y?'<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>',!Y)n("newempty").onclick=()=>{a.mode="new",M()}}An.forEach((u,h)=>{let d=document.createElement("section"),g=Vt.trim().toLowerCase(),x=o.filter((A)=>A.column===h&&(!Le||A.status===Le)&&(Y||!Ls(A))&&(!g||`${A.title||""} ${A.text||""} ${A.id||""}`.toLowerCase().includes(g)));if(h<4){let A=(N)=>N.kind==="run"?N.moved||0:N.added||0,I=it[h]==="old",G=(N)=>a.S.cards.indexOf(N),Z=(N)=>N.status==="waiting"?1:0;x.sort((N,D)=>Z(D)-Z(N)||(I?A(N)-A(D)||G(N)-G(D):A(D)-A(N)||G(D)-G(N)))}let H=h===4&&!Y&&x.length,T=(A)=>x.filter((I)=>a.S.now-(I.moved||a.S.now)>A*qn),z=`<h2>${u}<i>${x.length||""}</i>${h<4&&x.length>1?`<button class="btn sm ghost ord" title="Sort order">${it[h]==="old"?"Oldest":"Newest"}</button>`:""}${h===0&&!Y?`<button class="ib sm" title="New idea" aria-label="New idea">${q("plus")}</button>`:""}`;if(H&&rt)z+=`<span class="pickbar"><button class="btn sm ghost" data-p="cancel">Cancel</button><button class="btn sm pri" data-p="go" ${he.size?"":"disabled"}>Archive ${he.size||""}</button></span>`;else if(H)z+='<button class="btn sm ghost clear" title="Archive finished runs">Clear ▾</button>';if(H&&a.clearMenu)z+=`<div class="menu-pop clear-pop"><button data-c="7">Older than a week <i>${T(7).length}</i></button><button data-c="3">Older than 3 days <i>${T(3).length}</i></button><button data-c="0">Everything <i>${x.length}</i></button><hr><button data-c="pick">Pick which…</button></div>`;d.innerHTML=z+"</h2>";{let A=d.querySelector(".ord");if(A)A.onclick=(I)=>{I.stopPropagation(),it[h]=it[h]==="old"?"new":"old";try{localStorage.setItem("kz-order",JSON.stringify(it))}catch(G){}M()}}if(h===0&&!Y)d.querySelector(".ib").onclick=()=>{a.mode="new",M()};if(H){let A=d.querySelector(".clear");if(A)A.onclick=(I)=>{I.stopPropagation(),a.clearMenu=!a.clearMenu,M()};d.querySelectorAll("[data-c]").forEach((I)=>I.onclick=async(G)=>{G.stopPropagation(),a.clearMenu=!1;let Z=I.dataset.c;if(Z==="pick"){rt=!0,he.clear(),M();return}let N=T(+Z);if(!N.length){S("Nothing that old"),M();return}for(let D of N)await C("/archive",{dir:D.state,id:D.id});S("Archived "+N.length),K()}),d.querySelectorAll("[data-p]").forEach((I)=>I.onclick=async(G)=>{if(G.stopPropagation(),I.dataset.p==="cancel"){rt=!1,he.clear(),M();return}let Z=x.filter((N)=>he.has(j(N)));rt=!1,he.clear();for(let N of Z)await C("/archive",{dir:N.state,id:N.id});S("Archived "+Z.length),K()})}let U=document.createElement("ul");if(h===0&&a.mode==="new"){let A=document.createElement("li");A.className="inl",A.innerHTML=`${a.all?'<div class="combo" id="ndc"><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 would you like done?"><textarea id="newnotes" placeholder="Notes: links, paths, background (optional)"></textarea><div class="att" id="newatt"></div><input type="file" id="newfile" multiple hidden><div class="row"><button class="ib" id="newpick" aria-label="Attach files" title="Attach files">${q("clip")}</button><button class="btn ghost" id="nc">Cancel</button><button class="btn pri" id="ns">Add</button></div>`,U.appendChild(A);let I=A.querySelector("#newidea"),G=[];Ne(I,"f:new:0"),Ne(A.querySelector("textarea"),"f:new:1");let Z=async()=>{let D=I.value.trim();if(!D)return;let P=A.querySelector("textarea").value.trim();a.mode=null;let ie=a.all?qe:null;if((await C("/inbox",ie?{op:"add",dir:ie,text:D,notes:P}:{op:"add",text:D,notes:P})).ok){if(S("Idea added"),G.length){let _=await mt(G);if(!(await C("/note",{dir:ie,for:D,text:"",files:_})).ok)S("Idea added, files not attached")}}K()},N=(D)=>{G.push(D),A.querySelector("#newatt").textContent=G.map((P)=>P.name).join(" · ")};A.querySelector("#newpick").onclick=()=>A.querySelector("#newfile").click(),A.querySelector("#newfile").onchange=(D)=>{[...D.target.files].forEach(N),D.target.value=""};for(let D of[I,A.querySelector("textarea")])D.onpaste=(P)=>{for(let ie of P.clipboardData.items)if(ie.kind==="file"){let E=ie.getAsFile();N(new File([E],E.name==="image.png"?`paste-${Date.now()}.png`:E.name,{type:E.type})),P.preventDefault()}};if(a.all)xt(A);A.querySelector("#ns").onclick=Z,A.querySelector("#nc").onclick=()=>{a.mode=null,M()};{let D=Ke([I,A.querySelector("textarea")]);if(D)A.querySelector(".row").prepend(D)}I.onkeydown=(D)=>{if(D.key==="Enter")Z();if(D.key==="Escape")a.mode=null,M()},setTimeout(()=>I.focus())}let be=Mt.has(h)||x.length<=Qt+1?x:x.slice(0,Qt);if(be.forEach((A)=>{if(a.modeKey===j(A)&&a.mode&&a.mode!=="new"){U.appendChild(As(A));return}let I=document.createElement("li");I.appendChild(Cs(A)),U.appendChild(I)}),be.length<x.length){let A=document.createElement("li");A.innerHTML=`<button class="more-btn">Show ${x.length-be.length} more</button>`,A.firstChild.onclick=()=>{Mt.add(h),M()},U.appendChild(A)}else if(Mt.has(h)&&x.length>Qt+1){let A=document.createElement("li");A.innerHTML='<button class="more-btn">Show less</button>',A.firstChild.onclick=()=>{Mt.delete(h),M()},U.appendChild(A)}if(!x.length&&h>=1&&h<=3&&o.length&&!Le&&!g&&!Y)U.innerHTML=`<li class="hint">${Es[h]}</li>`;d.appendChild(U),r.appendChild(d)});let y=a.S.cards.filter((u)=>u.kind==="run"&&u.status==="waiting"&&!u.archived).length;if(document.title=y?`(${y}) kaizen`:"kaizen",ce(!1),e&&document.activeElement===document.body)Xt(e)?.focus({preventScroll:!0})}function Gn(){let e=n("bq");Ne(e,"bq"),Vt=e.value,e.addEventListener("input",()=>{Vt=e.value,M()}),e.addEventListener("keydown",(t)=>{if(t.key==="Escape")t.stopPropagation(),e.value="",e.dispatchEvent(new Event("input")),e.blur()})}function Fn(){let e=n("fbtn"),t=document.querySelector(".ftr"),o=(r)=>{if(t.classList.toggle("on",r),e.setAttribute("aria-expanded",r),r)t.style.top=e.getBoundingClientRect().bottom+6+"px"};e.onclick=()=>o(!t.classList.contains("on")),document.addEventListener("pointerdown",(r)=>{if(t.classList.contains("on")&&!t.contains(r.target)&&!e.contains(r.target))o(!1)}),document.addEventListener("keydown",(r)=>r.key==="Escape"&&t.classList.contains("on")&&o(!1))}var Yt=()=>document.activeElement?.closest?.("#board .card")?.dataset.k,Xt=(e)=>e&&n("board").querySelector('.card[data-k="'+CSS.escape(e)+'"]');function _n(e,t){let o=[...n("board").querySelectorAll("section")].map((v)=>[...v.querySelectorAll(".card")]).filter((v)=>v.length);if(!o.length)return;let r=document.activeElement.closest(".card"),l=o.findIndex((v)=>v.includes(r)),c=0;if(l<0)l=0;else if(c=o[l].indexOf(r),t)c=Math.max(0,Math.min(o[l].length-1,c+t));else l=Math.max(0,Math.min(o.length-1,l+e)),c=Math.min(c,o[l].length-1);let m=o[l][c];m.focus({preventScroll:!0}),m.scrollIntoView({block:"nearest",inline:"nearest"})}var Wn=()=>a.dir?{"x-kz-dir":a.dir}:{};async function K(e){let t;try{t=await fetch("/state"+(a.all?"?all=1":""),{headers:Wn()})}catch{S(Bn);return}let o=a.openKey&&a.S.cards.find((r)=>j(r)===a.openKey);a.S=await t.json();{let r=new Map;for(let l of a.S.cards){let c=j(l),m=r.get(c)||0;r.set(c,m+1),l._k=m?c+"#"+m:c}}for(let r of a.S.cards){if(r.kind!=="run"||r.archived)continue;if(due(j(r),r.notice,r.status==="done",a.S.now))wn(r.notice)}if(!(e&&a.mode&&n("board").querySelector(".inl input")))M();if(a.wantTerm&&!a.openKey&&!a.mode){let r=a.S.cards.find((l)=>j(l)===a.wantTerm&&l.pty);if(r){a.wantTerm=null,Ce(r);return}}if(a.openKey){let r=a.S.cards.find((c)=>j(c)===a.openKey),l=n("pbody").querySelector(".notes textarea");if(l&&document.activeElement===l)return;if(!r){if(o&&o.kind==="idea")Ts(o);return}if(a.runDoc)X(r,!0,!0);else if(r.kind==="idea")Zt(r)}}async function Ts(e){let t=j(e),o=(l)=>l.toLowerCase().replace(/\s+/g," ").trim(),r=o(e.text);for(let l of a.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(l.id)}?dir=${encodeURIComponent(l.state)}`).then((m)=>m.json()).catch(()=>null);if(a.openKey!==t)return;if(o(c?.request||"").includes(r)){Ce(l);return}}if(a.openKey===t)ee()}var Bn="The board server is not running — start it with kaizen web (or the shortcut), then reload.";async function C(e,t){let o;try{o=await fetch(e,{method:"POST",headers:{"content-type":"application/json",...Wn()},body:JSON.stringify(t)})}catch{return S(Bn),{ok:!1}}let r=await o.json().catch(()=>({}));if(!o.ok){let l=r.error||r.why||"That did not work";if(S(l,{title:l.slice(0,80),what:`POST ${e} failed (HTTP ${o.status}): ${l}`}),r.manual)console.log("run manually:",r.manual)}return r}var Pt=null,Rt=null,Qn=null,Oe=(e)=>e.pty?"running in its card":"opened in your terminal",Ms=()=>Qn||=new Promise((e,t)=>{let o=document.createElement("link");o.rel="stylesheet",o.href="/term.css",document.head.append(o);let r=document.createElement("script");r.src="/term.js",r.onload=()=>e(window.kzTerm),r.onerror=()=>{Qn=null,t()},document.head.append(r)});function $e(){if(Rt)Rt();Rt=null,Pt=null,n("panel").classList.remove("wide")}async function en(e){let t=e.pty.key;if(Pt===t&&n("pbody").querySelector(".term"))return;$e(),Pt=t,n("panel").classList.add("wide"),n("pbody").innerHTML='<div class="term" aria-label="Agent terminal"></div>';let o;try{o=await Ms()}catch{n("pbody").innerHTML='<p style="color:var(--dim)">The terminal could not load.</p>';return}if(Pt!==t)return;Rt=o(n("pbody").querySelector(".term"),t,()=>K(!0))}function Ds(e){let t=[];for(let o of(e||"").split(`
27
27
  `)){let r=/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2})\]\s?(.*)$/.exec(o);if(r){t.push({at:r[1],text:r[2]});continue}let l=t[t.length-1];if(l)l.text+=`
28
- `+o.replace(/^\s{2}/,"");else if(o.trim())t.push({at:"",text:o})}return t.map((o)=>({...o,text:o.text.trim()})).filter((o)=>o.text)}var Xt=null;function Zt(e,t,o){let r=(f)=>yt(f,e),l=Ts(t),c=e.kind==="idea"?je(e.text):{parts:[]},g=(c.parts.length?`<section class="desc">${Ee(c.parts.map((f)=>`### ${f.label}
29
- ${f.body}`).join(`
28
+ `+o.replace(/^\s{2}/,"");else if(o.trim())t.push({at:"",text:o})}return t.map((o)=>({...o,text:o.text.trim()})).filter((o)=>o.text)}var tn=null;function nn(e,t,o){let r=(h)=>kt(h,e),l=Ds(t),c=e.kind==="idea"?Te(e.text):{parts:[]},m=(c.parts.length?`<section class="desc">${Me(c.parts.map((h)=>`### ${h.label}
29
+ ${h.body}`).join(`
30
30
 
31
- `))}</section>`:"")+`<div class="chat">${l.map((f)=>`<div class="msg">${f.at?`<time>${p(f.at)}</time>`:""}${Ee(r(f.text))}</div>`).join("")||'<p style="color:var(--dim)">No notes yet.</p>'}</div>`;if(!o)g+='<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(n("pbody").innerHTML=g,o)return;let y=n("pbody").querySelector("textarea"),v=[];if(De(y,"n:"+j(e)),Xt===j(e))Xt=null,y.focus();{let f=et([y]);if(f)n("npick").before(f)}let h=()=>{n("natt").textContent=v.map((f)=>f.name).join(" · ")},k=(f)=>{v.push(f),h()};n("npick").onclick=()=>n("nfile").click(),n("nfile").onchange=(f)=>{[...f.target.files].forEach(k),f.target.value=""},y.onpaste=(f)=>{for(let d of f.clipboardData.items)if(d.kind==="file"){let m=d.getAsFile();k(new File([m],m.name==="image.png"?`paste-${Date.now()}.png`:m.name,{type:m.type})),f.preventDefault()}};let u=async()=>{fe(!0);let f=y.value.trim();if(!f&&!v.length)return;let d=await Promise.all(v.map((N)=>new Promise((T)=>{let I=new FileReader;I.onload=()=>T({name:N.name,data:I.result}),I.readAsDataURL(N)}))),m=e.kind==="run"?{dir:e.state,id:e.id,text:f,files:d}:{dir:e.state,for:e.text,text:f,files:d};if((await L("/note",m)).ok){v.length=0,y.value="";try{sessionStorage.removeItem(vn+"n:"+j(e))}catch(N){}S("Noted"),Xt=j(e),y.blur(),R()}};n("nsend").onclick=u,y.onkeydown=(f)=>{if(f.key==="Enter"&&!f.shiftKey)f.preventDefault(),u()},n("pbody").scrollTop=0}var Bn="kz-sent",Le=new Set,en=new Set((()=>{try{return JSON.parse(sessionStorage.getItem(Bn))||[]}catch(e){return[]}})());function Qn(e){let t=(k)=>e.id+":"+k,o=(k)=>en.has(t(k.n)),r=a.runDoc.findings||[],l=r.filter((k)=>!k.done&&!o(k));if(e.archived||!r.some((k)=>!k.done)||!(e.status==="done"||e.awaiting==="findings"||e.awaiting==="approvals.review"))return;let c=document.createElement("div");if(c.className="inl fix",c.innerHTML=`<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"?'<div class="q dim">A session is already waiting on this run in a terminal; this opens another one.</div>':""}`+r.map((k)=>{let u=k.done?" (closed)":o(k)?" (sent)":"";return`<label class="${k.done?"off done":u?"off":""}"><input type="checkbox" data-n="${k.n}" ${u?"checked disabled":Le.has(t(k.n))?"":"checked"}><span>${k.n}. ${Ee(k.text).replace(/^<p>|<\/p>$/g,"")}${u}</span></label>`}).join("")+(l.length?'<div class="row"><button class="btn ghost" data-a="all"></button><button class="btn pri" data-a="s"></button></div>':""),!l.length)return n("pbody").appendChild(c);let g=c.querySelector("[data-a=s]"),y=c.querySelector("[data-a=all]"),v=()=>l.filter((k)=>!Le.has(t(k.n))).map((k)=>k.n),h=()=>{let k=v().length;g.textContent=k===l.length?"Fix all "+k:"Fix "+k+" selected",g.disabled=!k,y.textContent=k===l.length?"Select none":"Select all",c.querySelectorAll("input:not(:disabled)").forEach((u)=>u.checked=!Le.has(t(u.dataset.n)))};c.onchange=(k)=>{let u=t(k.target.dataset.n);k.target.checked?Le.delete(u):Le.add(u),h()},y.onclick=()=>{let k=v().length===l.length;l.forEach((u)=>k?Le.add(t(u.n)):Le.delete(t(u.n))),h()},g.onclick=async()=>{let k=v();g.disabled=!0,ye();let u=await L("/fix",{dir:e.state,id:e.id,nums:k});if(u.ok){if(S("Fixing "+k.length+" finding"+(k.length===1?"":"s")+", "+Ne(u)+": "+Te(u.agent)),u.pty)a.wantTerm=j(e);k.forEach((f)=>en.add(t(f)));try{sessionStorage.setItem(Bn,JSON.stringify([...en]))}catch(f){}X(e,!0)}else h()},h(),n("pbody").appendChild(c)}function tn(e){let t=a.runDoc.git,o=a.runDoc.commit;if(e.archived||!t||!t.repo||!(e.status==="done"||e.awaiting==="approvals.review"))return"";if(o&&o.pushed)return`<button class="btn" disabled title="${p(o.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 o||a.runDoc.impl?'<button class="btn" disabled>Committed &amp; pushed</button>':""}function nn(e,t){let o=a.runDoc.git,r=n("pfoot");r.innerHTML=`<div class="inl" style="flex:1"><div class="q">Commit ${o.files.length} changed file${o.files.length===1?"":"s"} on ${p(o.branch||"this branch")} and push to ${p(o.remote)}?</div><div class="q dim">Only the files this run's report names.${o.others?` ${o.others} other changed file${o.others===1?" is":"s are"} left alone.`:""}</div><pre class="files">${p(o.files.join(`
31
+ `))}</section>`:"")+`<div class="chat">${l.map((h)=>`<div class="msg">${h.at?`<time>${p(h.at)}</time>`:""}${Me(r(h.text))}</div>`).join("")||'<p style="color:var(--dim)">No notes yet.</p>'}</div>`;if(!o)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(n("pbody").innerHTML=m,o)return;let v=n("pbody").querySelector("textarea"),k=[];if(Ne(v,"n:"+j(e)),tn===j(e))tn=null,v.focus();{let h=Ke([v]);if(h)n("npick").before(h)}let f=()=>{n("natt").textContent=k.map((h)=>h.name).join(" · ")},y=(h)=>{k.push(h),f()};n("npick").onclick=()=>n("nfile").click(),n("nfile").onchange=(h)=>{[...h.target.files].forEach(y),h.target.value=""},v.onpaste=(h)=>{for(let d of h.clipboardData.items)if(d.kind==="file"){let g=d.getAsFile();y(new File([g],g.name==="image.png"?`paste-${Date.now()}.png`:g.name,{type:g.type})),h.preventDefault()}};let u=async()=>{ce(!0);let h=v.value.trim();if(!h&&!k.length)return;let d=await Promise.all(k.map((H)=>new Promise((T)=>{let z=new FileReader;z.onload=()=>T({name:H.name,data:z.result}),z.readAsDataURL(H)}))),g=e.kind==="run"?{dir:e.state,id:e.id,text:h,files:d}:{dir:e.state,for:e.text,text:h,files:d};if((await C("/note",g)).ok){k.length=0,v.value="";try{sessionStorage.removeItem(xn+"n:"+j(e))}catch(H){}S("Noted"),tn=j(e),v.blur(),K()}};n("nsend").onclick=u,v.onkeydown=(h)=>{if(h.key==="Enter"&&!h.shiftKey)h.preventDefault(),u()},n("pbody").scrollTop=0}var Jn="kz-sent",Ae=new Set,sn=new Set((()=>{try{return JSON.parse(sessionStorage.getItem(Jn))||[]}catch(e){return[]}})());function Vn(e){let t=(y)=>e.id+":"+y,o=(y)=>sn.has(t(y.n)),r=a.runDoc.findings||[],l=r.filter((y)=>!y.done&&!o(y));if(e.archived||!r.some((y)=>!y.done)||!(e.status==="done"||e.awaiting==="findings"||e.awaiting==="approvals.review"))return;let c=document.createElement("div");if(c.className="inl fix",c.innerHTML=`<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"?'<div class="q dim">A session is already waiting on this run in a terminal; this opens another one.</div>':""}`+r.map((y)=>{let u=y.done?" (closed)":o(y)?" (sent)":"";return`<label class="${y.done?"off done":u?"off":""}"><input type="checkbox" data-n="${y.n}" ${u?"checked disabled":Ae.has(t(y.n))?"":"checked"}><span>${y.n}. ${Me(y.text).replace(/^<p>|<\/p>$/g,"")}${u}</span></label>`}).join("")+(l.length?'<div class="row"><button class="btn ghost" data-a="all"></button><button class="btn pri" data-a="s"></button></div>':""),!l.length)return n("pbody").appendChild(c);let m=c.querySelector("[data-a=s]"),v=c.querySelector("[data-a=all]"),k=()=>l.filter((y)=>!Ae.has(t(y.n))).map((y)=>y.n),f=()=>{let y=k().length;m.textContent=y===l.length?"Fix all "+y:"Fix "+y+" selected",m.disabled=!y,v.textContent=y===l.length?"Select none":"Select all",c.querySelectorAll("input:not(:disabled)").forEach((u)=>u.checked=!Ae.has(t(u.dataset.n)))};c.onchange=(y)=>{let u=t(y.target.dataset.n);y.target.checked?Ae.delete(u):Ae.add(u),f()},v.onclick=()=>{let y=k().length===l.length;l.forEach((u)=>y?Ae.add(t(u.n)):Ae.delete(t(u.n))),f()},m.onclick=async()=>{let y=k();m.disabled=!0,ve();let u=await C("/fix",{dir:e.state,id:e.id,nums:y});if(u.ok){if(S("Fixing "+y.length+" finding"+(y.length===1?"":"s")+", "+Oe(u)+": "+Pe(u.agent)),u.pty)a.wantTerm=j(e);y.forEach((h)=>sn.add(t(h)));try{sessionStorage.setItem(Jn,JSON.stringify([...sn]))}catch(h){}X(e,!0)}else f()},f(),n("pbody").appendChild(c)}function on(e){let t=a.runDoc.git,o=a.runDoc.commit;if(e.archived||!t||!t.repo||!(e.status==="done"||e.awaiting==="approvals.review"))return"";if(o&&o.pushed)return`<button class="btn" disabled title="${p(o.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 o||a.runDoc.impl?'<button class="btn" disabled>Committed &amp; pushed</button>':""}function an(e,t){let o=a.runDoc.git,r=n("pfoot");r.innerHTML=`<div class="inl" style="flex:1"><div class="q">Commit ${o.files.length} changed file${o.files.length===1?"":"s"} on ${p(o.branch||"this branch")} and push to ${p(o.remote)}?</div><div class="q dim">Only the files this run's report names.${o.others?` ${o.others} other changed file${o.others===1?" is":"s are"} left alone.`:""}</div><pre class="files">${p(o.files.join(`
32
32
  `))}</pre>${o.shared&&o.shared.length?`<div class="q">These files also hold other runs' changes, and a file is committed whole:</div><pre class="files">${p(o.shared.join(`
33
- `))}</pre>`:""}<textarea aria-label="Commit message" placeholder="feat(scope): what changed">${p(t||Cn(a.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 l=r.querySelector("textarea"),c=r.querySelector("[data-a=s]");l.focus();let g=()=>{l.style.height="auto",l.style.height=Math.min(l.scrollHeight+2,innerHeight/2)+"px",n("clong").hidden=l.value.split(`
34
- `)[0].length<=72};l.oninput=g,g(),r.querySelector("[data-a=c]").onclick=()=>{r.innerHTML="",X(e,!0)},c.onclick=()=>{if(!l.value.trim()){S("A commit message is required");return}sn(e,l.value,c)},l.onkeydown=(y)=>{if(y.key==="Enter"&&!y.shiftKey)y.preventDefault(),c.click()}}async function sn(e,t,o){o.disabled=!0,o.dataset.busy=1,o.textContent="Pushing…";let r=await L("/commit",{dir:e.state,id:e.id,message:t,files:a.runDoc.git.files});if(r.ok)S("Committed & pushed "+r.sha);if(n("pfoot").innerHTML="",a.openKey!==j(e))return;if(await X(e,!1),!r.ok&&t&&a.openKey===j(e)&&a.runDoc.git.files&&a.runDoc.git.files.length)nn(e,t)}var Jn=[["Request","request"],["Notes","notes"],["Plan","plan"],["Backlog","backlog"],["Work","impl"],["Review","review"],["Preview","preview"]],on=(e)=>e.charAt(0).toUpperCase()+e.slice(1),Ms=(e)=>/\s/.test(e)?e:on(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 qe(e,t){if(B!=="board")ce("board",!0);if(fe(!0),Le.clear(),a.openKey=j(e),n("panel").classList.add("open"),n("scrim").classList.add("on"),n("ptitle").textContent=e.kind==="idea"?je(e.text).head:Ms(e.title||e.text),n("ptitle").title=e.kind==="idea"?"":e.id||"",M(),n("pterm").hidden=!0,e.kind==="idea"){a.runDoc=null,n("psub").innerHTML=`<span class="pst"><span class="st d-idea"><svg><use href="#s-idea"/></svg></span>Idea</span><span class="pst dim">${p(e.where.split(/[\\/]/).pop()||e.where)}</span>`,n("ptabs").innerHTML="",Vt(e);let o=n("pfoot");o.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>`,o.querySelectorAll("[data-a]").forEach((r)=>r.onclick=()=>{let l=r.dataset.a;if(l==="archive"||l==="restore"){Et(e,l==="archive");return}let c=j(e);Z(),a.mode=l,a.modeKey=c,M()});return}if(!t)a.lastTab=a.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)a.tab="term";X(e,!1)}var Be=null;function Ds(e){let t=e.column===2&&a.runDoc.progress,o=Be&&Be.id===e.id?Be:null,r=t?jn(t,o&&o.st):"";if(!r)return;Be={id:e.id,st:[...t.matchAll(/^\s*-\s*(todo|doing|done):/gm)].map((c)=>c[1]),w:+/width:(\d+)%/.exec(r)[1]},n("pbody").innerHTML=r+(a.runDoc.backlog?"<h3>Backlog</h3>"+Ee(a.runDoc.backlog,!0):"");let l=n("pbody").querySelector(".pbar i");if(o&&o.w!==Be.w)l.style.width=o.w+"%",l.offsetWidth,l.style.width=Be.w+"%"}function Vt(e){if(e.pty)Yt(e);else we(),Zt(e,e.notes,e.archived||e.status!=="idea")}async function X(e,t,o){if(!t||!a.runDoc||a.runDoc.id!==e.id||o){let d=await fetch(`/run/${encodeURIComponent(e.id)}?dir=${encodeURIComponent(e.state)}`).then((m)=>m.json());if(t&&n("pfoot").querySelector(".inl,[data-busy],.more[open]"))return;a.runDoc=d}if(a.openKey!==j(e))return;e=a.S.cards.find((d)=>j(d)===a.openKey)||e;let r=n("pbody").querySelector(".notes textarea");if(t&&r&&document.activeElement===r)return;n("psub").innerHTML=`<span class="pst"><span class="st d-${Ae[e.status]||"idea"}"><svg><use href="#s-${Ae[e.status]||"idea"}"/></svg></span>${p(on(Oe[e.status]||e.status))}</span>`+(e.awaiting?`<span class="pst dim">${p(on(tt(e.awaiting)))}</span>`:"")+(e.parent?`<span class="pst link" title="Filed by this run">${p(e.parent)}</span>`:"");let l=Jn.filter((d)=>d[1]==="request"||d[1]==="notes"||(d[1]==="preview"?bt(a.runDoc.impl):a.runDoc[d[1]]||d[1]==="backlog"&&e.column===2&&a.runDoc.progress));if(e.pty&&a.wantTerm===j(e))a.wantTerm=null,a.tab="term";if(a.tab==="term"&&!e.pty)a.tab=a.lastTab;if(a.tab!=="term"&&!l.some((d)=>d[1]===a.tab))a.tab=[...l].reverse().find((d)=>d[1]!=="notes")[1];if(a.tab!=="term")a.lastTab=a.tab;n("panel").classList.toggle("wide",a.tab==="term");let c=n("pterm");c.hidden=!e.pty,c.classList.toggle("on",a.tab==="term"),c.setAttribute("aria-pressed",a.tab==="term"),c.title=c.ariaLabel=a.tab==="term"?"Close terminal":"Terminal",c.onclick=()=>{a.tab=a.tab==="term"?a.lastTab:"term",X(e,!0)},n("ptabs").innerHTML=l.map((d)=>`<button class="${d[1]===a.tab?"on":""}" data-k="${d[1]}">${d[0]}</button>`).join(""),[...n("ptabs").children].forEach((d)=>d.onclick=()=>{a.tab=d.dataset.k,X(e,!0)});let g=a.tab,y=g==="preview"?bt(a.runDoc.impl)&&yt(bt(a.runDoc.impl),e):a.runDoc[g];if(g!=="term")we();if(g==="term")Yt(e);else if(g==="notes"){let d=e.archived||e.status==="done"||e.status==="abandoned";Zt(e,y,d)}else n("pbody").innerHTML=y?Ee(y,g==="backlog"):`<p style="color:var(--dim)">No ${{request:"request",plan:"plan",impl:"work report",preview:"preview",review:"review",backlog:"backlog"}[g]} yet.</p>`;if(g==="backlog")Ds(e);if(g==="review")Qn(e);if(g==="notes"&&a.runDoc.linkedFrom&&a.runDoc.linkedFrom.length)n("pbody").insertAdjacentHTML("afterbegin",`<h3>Linked from notes</h3><ul class="lnk">${a.runDoc.linkedFrom.map((d)=>`<li><a href="/notebook?dir=${encodeURIComponent(e.state)}&amp;note=${encodeURIComponent(d)}">${p(d)}</a></li>`).join("")}</ul>`);if(Qe&&!t)if(Qe.k===j(e))Ps(Qe);else Qe=null;let v=n("pfoot");if(t&&v.querySelector(".inl,[data-busy],.more[open]"))return;let h=tn(e)+(e.archived?'<button class="btn pri" data-a="restore">Restore</button>':'<button class="btn ghost" data-a="archive">Archive</button>'),k=e.status==="waiting"&&e.awaiting==="approvals.plan"?An(a.runDoc.plan):[],u=k||[],f=e.pty&&!e.pty.alive?'<button class="btn" data-a="resume">Resume session</button>':"";if(e.archived)v.innerHTML=h;else if((e.status==="waiting"||e.awaiting==="findings")&&Ft[e.awaiting]){let d=_e.has(We(e)),m=!k?" It has open questions the board cannot show: answer them in the terminal running this run, or approve and your agent asks them.":u.length?` It asks ${u.length===1?"one question":u.length+" questions"}: Approve opens ${u.length===1?"it":"them"}.`:"",x=tn(e).replace(/class="btn( pri)?"/g,"")+'<button data-a="archive">Archive</button><hr><button data-a="ab" class="danger">Abandon</button>';v.innerHTML=`<span class="note">${d?"Sent to your agent.":Ft[e.awaiting]+m+" Answering here opens a new agent session."}</span><details class="more"><summary class="btn ghost">More</summary><div class="menu-pop">${x}</div></details>${f}<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")v.innerHTML=`<span class="note">Waiting on you: ${p(tt(e.awaiting))}. Answer in the ${e.pty&&e.pty.alive?"terminal (button at the top)":"terminal running this run"}.</span>${h}<button class="btn ghost" data-a="ab">Abandon</button>${f}`;else if(e.status==="done")v.innerHTML=h+f;else if(e.status==="abandoned")v.innerHTML=h;else v.innerHTML=`<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>${h}<button class="btn ghost" data-a="ab">Abandon run</button>${Lt(e)?'<button class="btn pri" data-a="resume">Resume</button>':""}`;v.querySelectorAll("[data-a]").forEach((d)=>d.onclick=()=>{let m=d.dataset.a;if(m==="archive"||m==="restore"){Et(e,m==="archive");return}if(m==="push"){sn(e,"",d);return}if(m==="commit"){nn(e);return}if(m==="approve"){if(u.length)Hn(e,u);else At(e,d);return}if(m==="revise"){Nn(e);return}if(m==="resume"){Ct(e,d);return}Z(),a.mode="reject",a.modeKey=j(e),M()})}var Qe=null;function Vn(){let e=new URLSearchParams(location.search),t=e.get("dir"),o=e.get("run"),r=e.get("idea");if(!o&&r===null)return;history.replaceState(null,"",location.pathname+"?all=1");let l=a.S.cards.find((v)=>v.state===t&&(o?v.kind==="run"&&v.id===o:v.kind==="idea"&&v.text===r));if(!l){S(o?"That run is not on this board":"That idea is not on this board");return}let c=/^(finding|backlog)-(\d+)$/.exec(e.get("item")||""),g=e.get("tab")||(c?c[1]==="finding"?"review":"backlog":""),y=Jn.find((v)=>v[1]===(g==="work"?"impl":g));if(y)a.tab=y[1];Qe=c?{type:c[1],n:+c[2],k:j(l)}:null,qe(l,!!y)}function Ps(e){Qe=null;let t=(g)=>String(g).replace(/!?\[([^\]]*)\]\([^)\s]+\)/g,"$1").replace(/[`*]/g,"").replace(/\s+/g," ").trim(),o=[...(a.runDoc.backlog||"").matchAll(/^\s*-\s*(?:open|done|rejected):\s*(.*)$/gm)],r=e.type==="finding"?(a.runDoc.findings||[]).find((g)=>g.n===e.n)?.text:o[e.n-1]?.[1];if(!r){S("That item is not in this run");return}let l=t(r).slice(0,60),c=[...n("pbody").querySelectorAll("li,p")].find((g)=>!g.closest(".prog")&&t(g.textContent).includes(l));if(!c)return;c.scrollIntoView({block:"center"}),c.classList.add("hit"),setTimeout(()=>c.classList.remove("hit"),2400)}async function Et(e,t){if((await L("/archive",e.kind==="idea"?{dir:e.state,text:e.text,archived:t}:{dir:e.state,id:e.id,archived:t})).ok)S(t?"Archived":"Restored");Z(),R()}function Z(){if(B!=="board")return;let e=a.openKey;if(fe(!0),we(),a.openKey=null,a.runDoc=null,n("panel").classList.remove("open"),n("scrim").classList.remove("on"),M(),e)Jt(e)?.focus({preventScroll:!0})}function Yn(){document.addEventListener("click",(e)=>{for(let t of document.querySelectorAll(".more[open]"))if(!t.contains(e.target))t.open=!1}),n("pclose").onclick=Z,n("scrim").onclick=Z}function Xn(){n("kbd").onclick=()=>n("help").classList.add("on"),n("help").onclick=()=>n("help").classList.remove("on"),document.addEventListener("click",()=>{if(a.menuKey||a.clearMenu)a.menuKey=null,a.clearMenu=!1,M()}),document.onkeydown=(e)=>{if(B==="sources"&&vt&&J()&&!n("help").classList.contains("on")&&!e.target.matches("input:not([type=checkbox]),select,textarea,[contenteditable]")&&!e.target.closest("#src-sec")&&vt(e))return;if(e.target.matches("input,select,textarea,[contenteditable]"))return;if(e.key==="?"){n("help").classList.toggle("on");return}if(e.key==="/"&&n("sq")&&n("panel").classList.contains("open")){e.preventDefault(),n("sq").focus();return}if(e.key==="/"&&B==="board"&&n("bq")){e.preventDefault(),n("bq").focus();return}if(e.key==="Escape"){if(n("help").classList.remove("on"),a.mode)a.mode=null,M();else Z();return}if(e.key==="n"){if(B!=="board")ce("board",!0);a.mode="new",M()}if(e.key==="a")a.all=!a.all,Ke(a.all?null:a.S.dir),R();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],o=t&&a.openKey&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&a.S.cards.find((c)=>j(c)===a.openKey&&c.kind==="run");if(o&&n("ptabs").children.length){if(e.preventDefault(),t[0]){let c=[...n("ptabs").children].map((v)=>v.dataset.k).concat(n("pterm").hidden?[]:["term"]),g=c.indexOf(a.tab),y=c[g<0?t[0]>0?0:c.length-1:Math.max(0,Math.min(c.length-1,g+t[0]))];if(y!==a.tab)a.tab=y,X(o,!0)}else n("pbody").scrollBy({top:t[1]*40});return}if(t&&!n("panel").classList.contains("open")&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.target===document.body||e.target.matches("#board .card"))){e.preventDefault(),Un(...t);return}let r=a.openKey||Qt(),l=r&&a.S.cards.find((c)=>j(c)===r);if(!l)return;if(e.key==="e"&&l.kind==="idea")Z(),a.mode="edit",a.modeKey=j(l),M();if(e.key==="x"&&!["done","abandoned","starting"].includes(l.status))Z(),a.mode="reject",a.modeKey=j(l),M();if(e.key==="r"&&l.kind==="idea")Z(),a.mode="run",a.modeKey=j(l),M()}}var an=null;function Zn(){let e=new EventSource("/events");e.onmessage=(t)=>{if(t.data==="changed"){if(R(!0),J()&&!kt&&!n("src-go")?.disabled&&!n("src-add")?.disabled)P()}else if(t.data==="reload")location.reload();else if(t.data.startsWith("hello ")){let o=t.data.slice(6);if(an&&o!==an)location.reload();an=o}},e.onerror=()=>{e.close(),setTimeout(Zn,3000)}}var rt=((e)=>e==="icebox"?"sources":e)(new URLSearchParams(location.search).get("view"));function es(){addEventListener("focus",()=>R(!0)),zt(["settings","sources"].includes(rt)?rt:"board"),document.body.classList.toggle("paged",["settings","sources"].includes(rt)),document.body.classList.toggle("ice",rt==="sources"),(async()=>{let e;try{e=localStorage.getItem("kz-proj")}catch(t){}if(e&&!Ut)try{if((await fetch("/cd",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({dir:e})})).ok)a.all=!1,a.dir=e;else Ke()}catch(t){}try{let t=JSON.parse(sessionStorage.getItem("kz-form"));if(t)[a.mode,a.modeKey]=t}catch(t){}R().then(()=>{ce(rt),Vn()}),Zn()})()}Yn();Mn();Kn();Tn();Xn();es();zn();Gn();document.addEventListener("pointerdown",(e)=>{let t=e.target;for(let o of document.querySelectorAll(".menu-pop:not([hidden])")){let r=o.closest(".combo,details")||o.parentElement;if(!r||r.contains(t))continue;if(r instanceof HTMLDetailsElement)r.open=!1;else if(r.classList.contains("combo"))o.hidden=!0,r.querySelector("[aria-expanded]")?.setAttribute("aria-expanded","false");else o.remove(),r.classList.remove("menu"),a.menuKey=null,a.clearMenu=!1}},!0);})();
33
+ `))}</pre>`:""}<textarea aria-label="Commit message" placeholder="feat(scope): what changed">${p(t||En(a.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 l=r.querySelector("textarea"),c=r.querySelector("[data-a=s]");l.focus();let m=()=>{l.style.height="auto",l.style.height=Math.min(l.scrollHeight+2,innerHeight/2)+"px",n("clong").hidden=l.value.split(`
34
+ `)[0].length<=72};l.oninput=m,m(),r.querySelector("[data-a=c]").onclick=()=>{r.innerHTML="",X(e,!0)},c.onclick=()=>{if(!l.value.trim()){S("A commit message is required");return}rn(e,l.value,c)},l.onkeydown=(v)=>{if(v.key==="Enter"&&!v.shiftKey)v.preventDefault(),c.click()}}async function rn(e,t,o){o.disabled=!0,o.dataset.busy=1,o.textContent="Pushing…";let r=await C("/commit",{dir:e.state,id:e.id,message:t,files:a.runDoc.git.files});if(r.ok)S("Committed & pushed "+r.sha);if(n("pfoot").innerHTML="",a.openKey!==j(e))return;if(await X(e,!1),!r.ok&&t&&a.openKey===j(e)&&a.runDoc.git.files&&a.runDoc.git.files.length)an(e,t)}var Yn=[["Request","request"],["Notes","notes"],["Plan","plan"],["Backlog","backlog"],["Work","impl"],["Review","review"],["Preview","preview"]],ln=(e)=>e.charAt(0).toUpperCase()+e.slice(1),Ps=(e)=>/\s/.test(e)?e:ln(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 Ce(e,t){if(Q!=="board")de("board",!0);if(ce(!0),Ae.clear(),a.openKey=j(e),n("panel").classList.add("open"),n("scrim").classList.add("on"),n("ptitle").textContent=e.kind==="idea"?Te(e.text).head:Ps(e.title||e.text),n("ptitle").title=e.kind==="idea"?"":e.id||"",M(),n("pterm").hidden=!0,e.kind==="idea"){a.runDoc=null,n("psub").innerHTML=`<span class="pst"><span class="st d-idea"><svg><use href="#s-idea"/></svg></span>Idea</span><span class="pst dim">${p(e.where.split(/[\\/]/).pop()||e.where)}</span>`,n("ptabs").innerHTML="",Zt(e);let o=n("pfoot");o.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>`,o.querySelectorAll("[data-a]").forEach((r)=>r.onclick=()=>{let l=r.dataset.a;if(l==="archive"||l==="restore"){Dt(e,l==="archive");return}let c=j(e);ee(),a.mode=l,a.modeKey=c,M()});return}if(!t)a.lastTab=a.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)a.tab="term";X(e,!1)}var Je=null;function Rs(e){let t=e.column===2&&a.runDoc.progress,o=Je&&Je.id===e.id?Je:null,r=t?Mn(t,o&&o.st):"";if(!r)return;Je={id:e.id,st:[...t.matchAll(/^\s*-\s*(todo|doing|done):/gm)].map((c)=>c[1]),w:+/width:(\d+)%/.exec(r)[1]},n("pbody").innerHTML=r+(a.runDoc.backlog?"<h3>Backlog</h3>"+Me(a.runDoc.backlog,!0):"");let l=n("pbody").querySelector(".pbar i");if(o&&o.w!==Je.w)l.style.width=o.w+"%",l.offsetWidth,l.style.width=Je.w+"%"}function Zt(e){if(e.pty)en(e);else $e(),nn(e,e.notes,e.archived||e.status!=="idea")}async function X(e,t,o){if(!t||!a.runDoc||a.runDoc.id!==e.id||o){let d=await fetch(`/run/${encodeURIComponent(e.id)}?dir=${encodeURIComponent(e.state)}`).then((g)=>g.json());if(t&&n("pfoot").querySelector(".inl,[data-busy],.more[open]"))return;a.runDoc=d}if(a.openKey!==j(e))return;e=a.S.cards.find((d)=>j(d)===a.openKey)||e;let r=n("pbody").querySelector(".notes textarea");if(t&&r&&document.activeElement===r)return;n("psub").innerHTML=`<span class="pst"><span class="st d-${Ee[e.status]||"idea"}"><svg><use href="#s-${Ee[e.status]||"idea"}"/></svg></span>${p(ln(Ue[e.status]||e.status))}</span>`+(e.awaiting?`<span class="pst dim">${p(ln(nt(e.awaiting)))}</span>`:"")+(e.parent?`<span class="pst link" title="Filed by this run">${p(e.parent)}</span>`:"");let l=Yn.filter((d)=>d[1]==="request"||d[1]==="notes"||(d[1]==="preview"?vt(a.runDoc.impl):a.runDoc[d[1]]||d[1]==="backlog"&&e.column===2&&a.runDoc.progress));if(e.pty&&a.wantTerm===j(e))a.wantTerm=null,a.tab="term";if(a.tab==="term"&&!e.pty)a.tab=a.lastTab;if(a.tab!=="term"&&!l.some((d)=>d[1]===a.tab))a.tab=[...l].reverse().find((d)=>d[1]!=="notes")[1];if(a.tab!=="term")a.lastTab=a.tab;n("panel").classList.toggle("wide",a.tab==="term");let c=n("pterm");c.hidden=!e.pty,c.classList.toggle("on",a.tab==="term"),c.setAttribute("aria-pressed",a.tab==="term"),c.title=c.ariaLabel=a.tab==="term"?"Close terminal":"Terminal",c.onclick=()=>{a.tab=a.tab==="term"?a.lastTab:"term",X(e,!0)},n("ptabs").innerHTML=l.map((d)=>`<button class="${d[1]===a.tab?"on":""}" data-k="${d[1]}">${d[0]}</button>`).join(""),[...n("ptabs").children].forEach((d)=>d.onclick=()=>{a.tab=d.dataset.k,X(e,!0)});let m=a.tab,v=m==="preview"?vt(a.runDoc.impl)&&kt(vt(a.runDoc.impl),e):a.runDoc[m];if(m!=="term")$e();if(m==="term")en(e);else if(m==="notes"){let d=e.archived||e.status==="done"||e.status==="abandoned";nn(e,v,d)}else n("pbody").innerHTML=v?Me(v,m==="backlog"):`<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")Rs(e);if(m==="review")Vn(e);if(m==="notes"&&a.runDoc.linkedFrom&&a.runDoc.linkedFrom.length)n("pbody").insertAdjacentHTML("afterbegin",`<h3>Linked from notes</h3><ul class="lnk">${a.runDoc.linkedFrom.map((d)=>`<li><a href="/notebook?dir=${encodeURIComponent(e.state)}&amp;note=${encodeURIComponent(d)}">${p(d)}</a></li>`).join("")}</ul>`);if(Ve&&!t)if(Ve.k===j(e))Ks(Ve);else Ve=null;let k=n("pfoot");if(t&&k.querySelector(".inl,[data-busy],.more[open]"))return;let f=on(e)+(e.archived?'<button class="btn pri" data-a="restore">Restore</button>':'<button class="btn ghost" data-a="archive">Archive</button>'),y=e.status==="waiting"&&e.awaiting==="approvals.plan"?Tn(a.runDoc.plan):[],u=y||[],h=e.pty&&!e.pty.alive?'<button class="btn" data-a="resume">Resume session</button>':"";if(e.archived)k.innerHTML=f;else if((e.status==="waiting"||e.awaiting==="findings")&&Bt[e.awaiting]){let d=Be.has(Qe(e)),g=!y?" It has open questions the board cannot show: answer them in the terminal running this run, or approve and your agent asks them.":u.length?` It asks ${u.length===1?"one question":u.length+" questions"}: Approve opens ${u.length===1?"it":"them"}.`:"",x=on(e).replace(/class="btn( pri)?"/g,"")+'<button data-a="archive">Archive</button><hr><button data-a="ab" class="danger">Abandon</button>';k.innerHTML=`<span class="note">${d?"Sent to your agent.":Bt[e.awaiting]+g+" Answering here opens a new agent session."}</span><details class="more"><summary class="btn ghost">More</summary><div class="menu-pop">${x}</div></details>${h}<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")k.innerHTML=`<span class="note">Waiting on you: ${p(nt(e.awaiting))}. Answer in the ${e.pty&&e.pty.alive?"terminal (button at the top)":"terminal running this run"}.</span>${f}<button class="btn ghost" data-a="ab">Abandon</button>${h}`;else if(e.status==="done")k.innerHTML=f+h;else if(e.status==="abandoned")k.innerHTML=f;else k.innerHTML=`<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>${f}<button class="btn ghost" data-a="ab">Abandon run</button>${jt(e)?'<button class="btn pri" data-a="resume">Resume</button>':""}`;k.querySelectorAll("[data-a]").forEach((d)=>d.onclick=()=>{let g=d.dataset.a;if(g==="archive"||g==="restore"){Dt(e,g==="archive");return}if(g==="push"){rn(e,"",d);return}if(g==="commit"){an(e);return}if(g==="approve"){if(u.length)On(e,u);else Tt(e,d);return}if(g==="revise"){In(e);return}if(g==="resume"){Et(e,d);return}ee(),a.mode="reject",a.modeKey=j(e),M()})}var Ve=null;function Xn(){let e=new URLSearchParams(location.search),t=e.get("dir"),o=e.get("run"),r=e.get("idea");if(!o&&r===null)return;history.replaceState(null,"",location.pathname+"?all=1");let l=a.S.cards.find((k)=>k.state===t&&(o?k.kind==="run"&&k.id===o:k.kind==="idea"&&k.text===r));if(!l){S(o?"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":""),v=Yn.find((k)=>k[1]===(m==="work"?"impl":m));if(v)a.tab=v[1];Ve=c?{type:c[1],n:+c[2],k:j(l)}:null,Ce(l,!!v)}function Ks(e){Ve=null;let t=(m)=>String(m).replace(/!?\[([^\]]*)\]\([^)\s]+\)/g,"$1").replace(/[`*]/g,"").replace(/\s+/g," ").trim(),o=[...(a.runDoc.backlog||"").matchAll(/^\s*-\s*(?:open|done|rejected):\s*(.*)$/gm)],r=e.type==="finding"?(a.runDoc.findings||[]).find((m)=>m.n===e.n)?.text:o[e.n-1]?.[1];if(!r){S("That item is not in this run");return}let l=t(r).slice(0,60),c=[...n("pbody").querySelectorAll("li,p")].find((m)=>!m.closest(".prog")&&t(m.textContent).includes(l));if(!c)return;c.scrollIntoView({block:"center"}),c.classList.add("hit"),setTimeout(()=>c.classList.remove("hit"),2400)}async function Dt(e,t){if((await C("/archive",e.kind==="idea"?{dir:e.state,text:e.text,archived:t}:{dir:e.state,id:e.id,archived:t})).ok)S(t?"Archived":"Restored");ee(),K()}function ee(){if(Q!=="board")return;let e=a.openKey;if(ce(!0),$e(),a.openKey=null,a.runDoc=null,n("panel").classList.remove("open"),n("scrim").classList.remove("on"),M(),e)Xt(e)?.focus({preventScroll:!0})}function Zn(){document.addEventListener("click",(e)=>{for(let t of document.querySelectorAll(".more[open]"))if(!t.contains(e.target))t.open=!1}),n("pclose").onclick=ee,n("scrim").onclick=ee}function es(){n("kbd").onclick=()=>n("help").classList.add("on"),n("help").onclick=()=>n("help").classList.remove("on"),document.addEventListener("click",()=>{if(a.menuKey||a.clearMenu)a.menuKey=null,a.clearMenu=!1,M()}),document.onkeydown=(e)=>{if(Q==="sources"&&$t&&V()&&!n("help").classList.contains("on")&&!e.target.matches("input:not([type=checkbox]),select,textarea,[contenteditable]")&&!e.target.closest("#src-sec")&&$t(e))return;if(e.target.matches("input,select,textarea,[contenteditable]"))return;if(e.key==="?"){n("help").classList.toggle("on");return}if(e.key==="/"&&n("sq")&&n("panel").classList.contains("open")){e.preventDefault(),n("sq").focus();return}if(e.key==="/"&&Q==="board"&&n("bq")){e.preventDefault(),n("bq").focus();return}if(e.key==="Escape"){if(n("help").classList.remove("on"),a.mode)a.mode=null,M();else ee();return}if(e.key==="n"){if(Q!=="board")de("board",!0);a.mode="new",M()}if(e.key==="a")a.all=!a.all,Ie(a.all?null:a.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],o=t&&a.openKey&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&a.S.cards.find((c)=>j(c)===a.openKey&&c.kind==="run");if(o&&n("ptabs").children.length){if(e.preventDefault(),t[0]){let c=[...n("ptabs").children].map((k)=>k.dataset.k).concat(n("pterm").hidden?[]:["term"]),m=c.indexOf(a.tab),v=c[m<0?t[0]>0?0:c.length-1:Math.max(0,Math.min(c.length-1,m+t[0]))];if(v!==a.tab)a.tab=v,X(o,!0)}else n("pbody").scrollBy({top:t[1]*40});return}if(t&&!n("panel").classList.contains("open")&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.target===document.body||e.target.matches("#board .card"))){e.preventDefault(),_n(...t);return}let r=a.openKey||Yt(),l=r&&a.S.cards.find((c)=>j(c)===r);if(!l)return;if(e.key==="e"&&l.kind==="idea")ee(),a.mode="edit",a.modeKey=j(l),M();if(e.key==="x"&&!["done","abandoned","starting"].includes(l.status))ee(),a.mode="reject",a.modeKey=j(l),M();if(e.key==="r"&&l.kind==="idea")ee(),a.mode="run",a.modeKey=j(l),M()}}var cn=null;function ts(){let e=new EventSource("/events");e.onmessage=(t)=>{if(t.data==="changed"){if(K(!0),V()&&!wt&&!n("src-go")?.disabled&&!n("src-add")?.disabled)R()}else if(t.data==="reload")location.reload();else if(t.data.startsWith("hello ")){let o=t.data.slice(6);if(cn&&o!==cn)location.reload();cn=o}},e.onerror=()=>{e.close(),setTimeout(ts,3000)}}var lt=((e)=>e==="icebox"?"sources":e)(new URLSearchParams(location.search).get("view"));function ns(){addEventListener("focus",()=>K(!0)),Ft(["settings","sources"].includes(lt)?lt:"board"),document.body.classList.toggle("paged",["settings","sources"].includes(lt)),document.body.classList.toggle("ice",lt==="sources"),(async()=>{let e;try{e=localStorage.getItem("kz-proj")}catch(t){}if(e&&!Wt)try{if((await fetch("/cd",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({dir:e})})).ok)a.all=!1,a.dir=e;else Ie()}catch(t){}try{let t=JSON.parse(sessionStorage.getItem("kz-form"));if(t)[a.mode,a.modeKey]=t}catch(t){}K().then(()=>{de(lt),Xn()}),ts()})()}Zn();Rn();Hn();Pn();es();ns();Gn();Fn();document.addEventListener("pointerdown",(e)=>{let t=e.target;for(let o of document.querySelectorAll(".menu-pop:not([hidden])")){let r=o.closest(".combo,details")||o.parentElement;if(!r||r.contains(t))continue;if(r instanceof HTMLDetailsElement)r.open=!1;else if(r.classList.contains("combo"))o.hidden=!0,r.querySelector("[aria-expanded]")?.setAttribute("aria-expanded","false");else o.remove(),r.classList.remove("menu"),a.menuKey=null,a.clearMenu=!1}},!0);})();
package/web/notebook.js CHANGED
@@ -1,4 +1,4 @@
1
- /* kaizen notebook 2ce23536754b7b8b -- generated by bun run build:web from web/notebook.ts; do not edit */
1
+ /* kaizen notebook 4c283dae7e7513b1 -- 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 2ce23536754b7b8b -- generated by bun run build:web from web/term.ts; do not edit */
1
+ /* kaizen terminal 4c283dae7e7513b1 -- 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+`