kaizen-agent 1.34.0 → 1.34.1

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.
@@ -7,7 +7,7 @@ import { tmpdir } from "node:os";
7
7
  import { readInbox, writeInbox, replaceIdea, parseItem } from "./state.ts";
8
8
  import {
9
9
  readSources, addSource, removeSource, ingest, checkUrl, resolvesPublic, exportUrl, fetchText, split, sift,
10
- gatherSource, gatherAll, due, takeImages, sheetImages, docImages, imagesUrl, SIGN_IN, UNREADABLE, CAP, moveSource, gatherDirs, writeSources,
10
+ gatherSource, freshImages, gatherAll, due, takeImages, sheetImages, docImages, imagesUrl, SIGN_IN, UNREADABLE, CAP, moveSource, gatherDirs, writeSources,
11
11
  readIcebox, writeIcebox, promote, dismiss, markReplied, setCategory, socialUrl, socialOf, AGENT_ONLY, PLATFORMS,
12
12
  } from "./sources.ts";
13
13
 
@@ -764,3 +764,28 @@ test("sheet images placed over the grid land on their anchored row", () => {
764
764
  + `<script>posObj('0', 'embed_11', 1, 7, 14, 9);posObj('0', 'embed_22', 1, 7, 7, 316);</script>`;
765
765
  expect(sheetImages(html)).toEqual(new Map([[2, ["https://docs.google.com/sheets-images-rt/A=s2048", "https://docs.google.com/sheets-images-rt/B=s2048"]]]));
766
766
  });
767
+
768
+ test("an item's images are read again from its source, since Google's links expire", async () => {
769
+ addSource(state, SHEET);
770
+ ingest(state, SHEET, [{ text: "Fix login", images: ["https://docs.google.com/sheets-images-rt/OLD=s2048"] }]);
771
+ const id = readIcebox(state)[0]!.id;
772
+ const html = `<table><tr><th id="0R0"><div>1</div></th><td>Request</td></tr><tr><th id="0R1"><div>2</div></th><td>Fix login</td><td><img src="https://docs.google.com/sheets-images-rt/NEW=s2048"></td></tr></table>`;
773
+ const page: any = async (u: string) => u.includes("htmlview")
774
+ ? new Response(html, { headers: { "content-type": "text/html" } })
775
+ : new Response("Request\nFix login\n", { headers: { "content-type": "text/plain" } });
776
+ expect(await freshImages(state, id, page, pub)).toEqual(["https://docs.google.com/sheets-images-rt/NEW=s2048"]);
777
+ // The source cannot be read: the kept links come back.
778
+ expect(await freshImages(state, id, (async () => new Response("", { status: 500 })) as any, pub)).toEqual(["https://docs.google.com/sheets-images-rt/OLD=s2048"]);
779
+ expect(await freshImages(state, "nope", page, pub)).toEqual([]);
780
+ });
781
+
782
+ test("a fresh image link on a host the item never used is not taken", async () => {
783
+ addSource(state, SHEET);
784
+ ingest(state, SHEET, [{ text: "Fix login", images: ["https://docs.google.com/sheets-images-rt/OLD=s2048"] }]);
785
+ const id = readIcebox(state)[0]!.id;
786
+ const html = `<table><tr><th id="0R1"><div>2</div></th><td>Fix login</td><td><img src="https://evil.example/x.png"></td></tr></table>`;
787
+ const page: any = async (u: string) => u.includes("htmlview")
788
+ ? new Response(html, { headers: { "content-type": "text/html" } })
789
+ : new Response("Request\nFix login\n", { headers: { "content-type": "text/plain" } });
790
+ expect(await freshImages(state, id, page, pub)).toEqual(["https://docs.google.com/sheets-images-rt/OLD=s2048"]);
791
+ });
package/cli/sources.ts CHANGED
@@ -182,6 +182,15 @@ export function takeImages(notes = ""): { notes: string; images: string[] } {
182
182
 
183
183
  // The single place outside text enters kaizen, and it enters the icebox, never Ideas.
184
184
  // Synchronous on purpose: nothing may run between reading the files and writing them.
185
+ // A gathered item as the icebox keeps it, keyed the way dedup sees it.
186
+ function shape(it: Item) {
187
+ const got = takeImages(it.notes);
188
+ const c = clean(it.text, got.notes);
189
+ if (!c) return null;
190
+ const notes = c.lines.join("\n").slice(0, 2000);
191
+ return { c, notes, images: pics([...(it.images ?? []), ...got.images]), key: rowKey(c.text, notes) };
192
+ }
193
+
185
194
  export function ingest(state: string, url: string, items: Item[]): { added: number; held: number } {
186
195
  const sources = readSources(state);
187
196
  const src = sources.find((s) => s.url === url);
@@ -192,12 +201,9 @@ export function ingest(state: string, url: string, items: Item[]): { added: numb
192
201
  const seen: string[] = [];
193
202
  let held = 0, refreshed = false;
194
203
  for (const it of items) {
195
- const got = takeImages(it.notes);
196
- const images = pics([...(it.images ?? []), ...got.images]);
197
- const c = clean(it.text, got.notes);
198
- if (!c) continue;
199
- const notes = c.lines.join("\n").slice(0, 2000);
200
- const key = rowKey(c.text, notes);
204
+ const s = shape(it);
205
+ if (!s) continue;
206
+ const { c, notes, images, key } = s;
201
207
  if (known.has(key)) {
202
208
  seen.push(key);
203
209
  // Image links expire (social CDNs) or arrive late (items gathered before images
@@ -518,6 +524,39 @@ export function record(state: string, url: string, r: { added: number; held: num
518
524
  return { url, ...last };
519
525
  }
520
526
 
527
+ // An item's images read again from its source now. Google signs sheet and doc image
528
+ // links with a token that expires, so a link kept since the gather answers 403; this
529
+ // reads the page again and returns the same row's links. Falls back to the kept ones.
530
+ // A source's page is read once a minute however many items and images ask, and a
531
+ // fresh link is only taken on a host the kept links already used.
532
+ const pages = new Map<string, { at: number; p: Promise<Map<string, string[]> | null> }>();
533
+ async function readPage(source: string, doFetch?: Fetch, lookup?: Lookup) {
534
+ const target = exportUrl(source), at = imagesUrl(source)!;
535
+ const [got, page] = await Promise.all([fetchText(target.url, doFetch, lookup), fetchText(at, doFetch, lookup, "html")]);
536
+ if (!got.ok || !page.ok) return null;
537
+ const found = target.kind === "sheet" ? sheetImages(page.body) : docImages(page.body);
538
+ const byKey = new Map<string, string[]>();
539
+ for (const x of sift(target.kind, got.body, found as Map<number | string, string[]>).items) {
540
+ const s = shape(x);
541
+ if (s?.images.length) byKey.set(s.key, s.images);
542
+ }
543
+ return byKey;
544
+ }
545
+ export async function freshImages(state: string, id: string, doFetch?: Fetch, lookup?: Lookup): Promise<string[]> {
546
+ const it = readIcebox(state).find((s) => s.id === id);
547
+ if (!it) return [];
548
+ const kept = it.images ?? [];
549
+ if (!imagesUrl(it.source)) return kept;
550
+ const now = Date.now();
551
+ for (const [k, v] of pages) if (now - v.at >= 60_000) pages.delete(k);
552
+ const hit = doFetch ? undefined : pages.get(it.source); // a test's own fetch is never cached
553
+ const p = hit?.p ?? readPage(it.source, doFetch, lookup).catch(() => null);
554
+ if (!hit && !doFetch) pages.set(it.source, { at: now, p });
555
+ const hosts = new Set(kept.map((u) => new URL(u).hostname));
556
+ const fresh = ((await p)?.get(rowKey(it.text, it.notes)) ?? []).filter((u) => hosts.has(new URL(u).hostname));
557
+ return fresh.length ? fresh : kept;
558
+ }
559
+
521
560
  // Never throws. A failure is recorded on the source and marks nothing seen.
522
561
  export async function gatherSource(state: string, url: string, doFetch?: Fetch, lookup?: Lookup): Promise<Result> {
523
562
  if (socialOf(url)) {
package/cli/web.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  launchMode, readSessions, ownerOf, resumePrompt, runAgent, firstSession,
11
11
  } from "./state.ts";
12
12
  import { attachPty, writePty, resizePty, ptyState, ptyKeys, adoptLoginEnv } from "./pty.ts";
13
- import { readSources, addSource, removeSource, moveSource, setCategory, readIcebox, promote, dismiss, restore, markReplied, gatherSource, gatherAll, gatherDirs, due, socialUrl, socialOf } from "./sources.ts";
13
+ import { readSources, addSource, removeSource, moveSource, setCategory, readIcebox, promote, dismiss, restore, markReplied, gatherSource, freshImages, gatherAll, gatherDirs, due, socialUrl, socialOf } from "./sources.ts";
14
14
  import * as chatgpt from "./chatgpt.ts";
15
15
  import { postLink, sendReply } from "./browser.ts";
16
16
  import { listNotes, saveNote, renameNote, deleteNote, boardIndex, linksTo } from "./notes.ts";
@@ -91,10 +91,10 @@ export async function web(repoDir: string, opts: Opts = {}) {
91
91
  // Before anything looks for an agent on PATH: the board starts agents itself, and
92
92
  // from a launcher it would otherwise have none of the user's shell setup.
93
93
  adoptLoginEnv();
94
- // Not fixed for the life of the board: picking a project in the page moves here.
95
- let state = locate();
96
- if (state && dirname(state) !== home) remember(dirname(state));
97
- let project = state ? label(state) : null;
94
+ // Where the board started. Each page picks its own project and names it in the
95
+ // x-kz-dir header, so a phone choosing another project never moves this tab.
96
+ const boardState = locate();
97
+ if (boardState && dirname(boardState) !== home) remember(dirname(boardState));
98
98
  // The "Projects folder" setting, resolved; empty when unset.
99
99
  const projectsRoot = async () => {
100
100
  const { listSettings } = await import("./settings.ts");
@@ -125,9 +125,9 @@ export async function web(repoDir: string, opts: Opts = {}) {
125
125
  .filter((d) => existsSync(join(d, ".kaizen")) ? (remember(d), false) : true).sort();
126
126
  };
127
127
 
128
- const states = (all: boolean) => (all || !state
128
+ const states = (all: boolean, cur = boardState) => (all || !cur
129
129
  ? [...knownProjects().map((d) => join(d, ".kaizen")), join(home, ".kaizen")]
130
- : [state]
130
+ : [cur]
131
131
  ).filter((d, i, arr) => arr.indexOf(d) === i && existsSync(d));
132
132
 
133
133
  // Hostnames the user put in KAIZEN_WEB_HOSTS (e.g. a `tailscale serve` name):
@@ -290,6 +290,9 @@ export async function web(repoDir: string, opts: Opts = {}) {
290
290
  if (hostname !== "127.0.0.1" && hostname !== "localhost" && !extraHosts.has(hostname)) return new Response("forbidden", { status: 403 });
291
291
  const url = new URL(req.url);
292
292
  const path = url.pathname;
293
+ const asked = req.headers.get("x-kz-dir");
294
+ const state = asked && states(true).includes(asked) ? asked : boardState;
295
+ const project = state ? label(state) : null;
293
296
 
294
297
  if (req.method === "GET" && path === "/") {
295
298
  const page = served(PAGE);
@@ -373,7 +376,7 @@ export async function web(repoDir: string, opts: Opts = {}) {
373
376
  const all = url.searchParams.get("all") === "1";
374
377
  // Before the project list: it registers folders that turn out to be set up.
375
378
  const unset = await fresh();
376
- const dirs = states(all);
379
+ const dirs = states(all, state);
377
380
  // With a projects folder set, the picker offers only real projects inside it,
378
381
  // and always the one open now so it never hides where the board is.
379
382
  const root = await projectsRoot();
@@ -572,6 +575,16 @@ export async function web(repoDir: string, opts: Opts = {}) {
572
575
  return new Response(Bun.file(f), { headers: { "cache-control": "private, max-age=3600" } });
573
576
  }
574
577
 
578
+ // An icebox image, sent on to a link read fresh from its source: the one kept
579
+ // since the gather may have expired. Only an https link the item holds is followed.
580
+ if (req.method === "GET" && path === "/ice-image") {
581
+ const dir = url.searchParams.get("dir") ?? "";
582
+ if (!states(true).includes(dir)) return bad("unknown state dir", 404);
583
+ const u = (await freshImages(dir, url.searchParams.get("id") ?? ""))[Number(url.searchParams.get("i"))];
584
+ if (!u || !/^https:\/\//.test(u)) return bad("no such image", 404);
585
+ return new Response(null, { status: 302, headers: { location: u, "cache-control": "no-store", "referrer-policy": "no-referrer" } });
586
+ }
587
+
575
588
  if (req.method === "GET" && path === "/events") {
576
589
  let ctrl: ReadableStreamDefaultController;
577
590
  const stream = new ReadableStream({
@@ -612,14 +625,10 @@ export async function web(repoDir: string, opts: Opts = {}) {
612
625
  }
613
626
  if (!dir || !states(true).includes(dir)) return bad("unknown state dir", 404);
614
627
 
615
- // The board moves to the chosen project: new ideas and runs default to it,
616
- // and the process follows so anything resolved from cwd agrees.
628
+ // Only checks the project: the page that picked it sends it back in x-kz-dir.
617
629
  if (path === "/cd") {
618
- state = dir;
619
- project = label(dir);
620
- try { process.chdir(dirname(dir)); } catch { /* the state dir is what matters */ }
621
630
  changed();
622
- return json({ ok: true, project });
631
+ return json({ ok: true, project: label(dir) });
623
632
  }
624
633
 
625
634
  if (path === "/inbox") {
@@ -941,7 +950,7 @@ export async function web(repoDir: string, opts: Opts = {}) {
941
950
 
942
951
  const url = `http://127.0.0.1:${server.port}/`;
943
952
  console.log(`\n kaizen web ${url}`);
944
- console.log(` ${project ? `project ${project}` : "no .kaizen here; showing every known project"}`);
953
+ console.log(` ${boardState ? `project ${label(boardState)}` : "no .kaizen here; showing every known project"}`);
945
954
  console.log(` ctrl-c to stop\n`);
946
955
  if (opts.open !== false) await openApp(url);
947
956
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-agent",
3
- "version": "1.34.0",
3
+ "version": "1.34.1",
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": {
package/web/board/api.ts CHANGED
@@ -7,11 +7,14 @@ import { notifyUser, toast } from "./toast.ts";
7
7
  // The server writes notifier() from state.ts over a marker in board.html: one rule for both UIs.
8
8
  declare const due: (key: string, say: string | null, done: boolean, now: number) => boolean;
9
9
 
10
+ // This page's project, sent with each request: the server keeps none, so another
11
+ // tab or a phone picking a project never moves this one.
12
+ export const dirHeader = () => (store.dir ? { "x-kz-dir": store.dir } : {});
10
13
  // quiet: the server said something changed, nobody asked for a redraw.
11
14
  export async function load(quiet) {
12
15
  let r;
13
16
  try {
14
- r = await fetch("/state" + (store.all ? "?all=1" : ""));
17
+ r = await fetch("/state" + (store.all ? "?all=1" : ""), { headers: dirHeader() });
15
18
  } catch {
16
19
  toast(GONE);
17
20
  return;
@@ -85,7 +88,7 @@ export async function post(path, body) {
85
88
  try {
86
89
  r = await fetch(path, {
87
90
  method: "POST",
88
- headers: { "content-type": "application/json" },
91
+ headers: { "content-type": "application/json", ...dirHeader() },
89
92
  body: JSON.stringify(body),
90
93
  });
91
94
  } catch {
@@ -279,7 +279,7 @@ export async function openSources() {
279
279
  return (
280
280
  `<div class="ice-row${iceCur === k ? " cur" : ""}${iceSel.has(k) ? " sel" : ""}" data-i="${i}" data-k="${esc(k)}"><input type="checkbox" id="ice-ck-${i}" data-ck="${i}"${iceSel.has(k) ? " checked" : ""} aria-label="Select: ${esc(it.text)}"><span class="ice-src" title="${esc(srcName(it))}">${fav(it.source, "link", list.find((x) => x.dir === it.dir && x.url === it.source)?.social)}</span><button class="ice-t" id="ice-o-${i}" data-o="${i}" aria-expanded="${open}" title="${esc(it.text)}">${esc(it.text)}</button><span class="ice-meta">${many ? `<span class="nm">${esc(it.project || "")}</span>` : ""}${it.replied ? `<span title="Replied ${esc(ago(it.replied))}">replied</span>` : ""}<span class="ice-age">${ago(it.at)}</span><span class="ice-ra"><button class="btn ghost sm" id="ice-pr-${i}" data-pr="${i}" title="Promote to Ideas as gathered (p)" aria-label="Promote to Ideas: ${esc(it.text)}">${ico("plus")}</button>${it.status === "dismissed" ? `<button class="btn ghost sm" id="ice-rs-${i}" data-rs="${i}" title="Restore to waiting" aria-label="Restore: ${esc(it.text)}">Restore</button>` : `<button class="btn ghost sm" id="ice-ds-${i}" data-ds="${i}" title="Dismiss (d)" aria-label="Dismiss: ${esc(it.text)}">${ico("x")}</button>`}</span></span></div>` +
281
281
  (open
282
- ? `<div class="ice-open">${it.notes ? `<div class="ice-notes">${esc(it.notes)}</div>` : ""}${(it.images || []).filter((u) => /^https:\/\//.test(u)).length ? `<details class="ice-imgs"><summary>Show images (${it.images.filter((u) => /^https:\/\//.test(u)).length}) — loads them from the source's site</summary>${it.images.filter((u) => /^https:\/\//.test(u)).map((u) => `<a href="${esc(u)}" target="_blank" rel="noopener noreferrer"><img data-src="${esc(u)}" alt="" referrerpolicy="no-referrer"></a>`).join("")}</details>` : ""}<span class="hint" style="margin:0">From ${esc(srcName(it))} · ${ago(it.at)}${it.post ? ` · <a href="${esc(it.post)}" target="_blank" rel="noopener">open post</a>` : ""}</span>${replyBox(it)}${form(it)}</div>`
282
+ ? `<div class="ice-open">${it.notes ? `<div class="ice-notes">${esc(it.notes)}</div>` : ""}${(it.images || []).filter((u) => /^https:\/\//.test(u)).length ? `<details class="ice-imgs"><summary>Show images (${it.images.filter((u) => /^https:\/\//.test(u)).length}) — loads them from the source's site</summary>${it.images.filter((u) => /^https:\/\//.test(u)).map((u, n) => { const src = `/ice-image?dir=${encodeURIComponent(it.dir)}&id=${encodeURIComponent(it.id)}&i=${n}`; return `<a href="${esc(src)}" target="_blank" rel="noopener noreferrer"><img data-src="${esc(src)}" alt="" referrerpolicy="no-referrer"></a>`; }).join("")}</details>` : ""}<span class="hint" style="margin:0">From ${esc(srcName(it))} · ${ago(it.at)}${it.post ? ` · <a href="${esc(it.post)}" target="_blank" rel="noopener">open post</a>` : ""}</span>${replyBox(it)}${form(it)}</div>`
283
283
  : "")
284
284
  );
285
285
  };
package/web/board/live.ts CHANGED
@@ -52,8 +52,10 @@ export function initLive() {
52
52
  body: JSON.stringify({ dir: d }),
53
53
  })
54
54
  ).ok
55
- )
55
+ ) {
56
56
  store.all = false;
57
+ store.dir = d;
58
+ }
57
59
  else keep();
58
60
  } catch (e) {}
59
61
  try {
@@ -52,6 +52,7 @@ async function pick(v) {
52
52
  const j = await post("/cd", { dir: v });
53
53
  if (j.ok) {
54
54
  store.all = false;
55
+ store.dir = v;
55
56
  keep(v);
56
57
  }
57
58
  }
@@ -4,6 +4,8 @@
4
4
  export const store = {
5
5
  S: { project: null, projects: [], cards: [], now: Date.now(), all: false },
6
6
  all: new URLSearchParams(location.search).get("all") !== "0",
7
+ // The project this page picked; null is wherever the board started.
8
+ dir: null,
7
9
  openKey: null,
8
10
  mode: null,
9
11
  modeKey: null,
package/web/board.js CHANGED
@@ -1,5 +1,5 @@
1
- /* kaizen board d387a44b8ead9df1 -- 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",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 t=(e)=>document.getElementById(e),m=(e)=>String(e).replace(/[&<>"]/g,(n)=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[n]),j=(e)=>`<svg><use href="#i-${e}"/></svg>`;var en;function S(e,n){let o=t("toast");if(o.textContent=e,o.classList.toggle("rep",!!n),n){let l=document.createElement("a");l.href="/issue?"+new URLSearchParams(n),l.target="_blank",l.rel="noopener",l.textContent="Report",o.append(" ",l)}o.classList.add("on"),clearTimeout(en),en=setTimeout(()=>o.classList.remove("on"),n?8000:4000)}function ue(){try{if(typeof Notification<"u"&&Notification.permission==="default")Notification.requestPermission()}catch(e){}}function tn(e){try{if(typeof Notification<"u"&&Notification.permission==="granted")new Notification("kaizen",{body:e,icon:"/logo.png"})}catch(n){}}var Qn="https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0",nn={tiny:"onnx-community/whisper-tiny",base:"onnx-community/whisper-base",small:"onnx-community/whisper-small","large-v3-turbo":"onnx-community/whisper-large-v3-turbo"},$t=null,Vn=`import{pipeline}from"${Qn}";let asr=null;
1
+ /* kaizen board 002970592c17182f -- 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 t=(e)=>document.getElementById(e),m=(e)=>String(e).replace(/[&<>"]/g,(n)=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[n]),j=(e)=>`<svg><use href="#i-${e}"/></svg>`;var en;function S(e,n){let o=t("toast");if(o.textContent=e,o.classList.toggle("rep",!!n),n){let l=document.createElement("a");l.href="/issue?"+new URLSearchParams(n),l.target="_blank",l.rel="noopener",l.textContent="Report",o.append(" ",l)}o.classList.add("on"),clearTimeout(en),en=setTimeout(()=>o.classList.remove("on"),n?8000:4000)}function ue(){try{if(typeof Notification<"u"&&Notification.permission==="default")Notification.requestPermission()}catch(e){}}function tn(e){try{if(typeof Notification<"u"&&Notification.permission==="granted")new Notification("kaizen",{body:e,icon:"/logo.png"})}catch(n){}}var Vn="https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0",nn={tiny:"onnx-community/whisper-tiny",base:"onnx-community/whisper-base",small:"onnx-community/whisper-small","large-v3-turbo":"onnx-community/whisper-large-v3-turbo"},$t=null,Jn=`import{pipeline}from"${Vn}";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,23 +8,23 @@ 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)})}}`,Jn='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)',Xn=!!(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&window.AudioWorkletNode&&window.WebAssembly&&window.Worker),pe=null,ye=null,st=()=>{},oe=null,Ae=!1,ot=new Set;async function Zn(){let e="small";try{e=((await fetch("/settings").then((o)=>o.json())).rows.find((o)=>o.key==="dictation.model")||{}).value||e}catch(n){}if(!nn[e])e="small";if(ye&&$t!==e){try{pe.terminate()}catch(n){}pe=null,ye=null,ot.clear(),oe=null}if(ye)return ye;return $t=e,ye=new Promise(async(n,o)=>{pe=new Worker(URL.createObjectURL(new Blob([Vn],{type:"text/javascript"})),{type:"module"});let l={};pe.onmessage=(c)=>{let f=c.data;if(f.type==="progress"){l[f.file]=f.pct;let k=Object.values(l);st(Math.round(k.reduce((v,h)=>v+h,0)/k.length))}else if(f.type==="ready")n();else if(f.type==="fallback")S("GPU could not run the model, using the CPU: "+f.why);else if(f.type==="error"&&f.seq==null)o(Error(f.error));for(let k of ot)k.onmsg(f)},pe.onerror=(c)=>o(Error(c.message||"worker failed"));let r=null;try{r=navigator.gpu&&await navigator.gpu.requestAdapter()}catch(c){}if(!r)S("No WebGPU: dictation will run on the CPU and be slower");pe.postMessage({type:"load",model:nn[$t],webgpu:!!r,f16:!!(r&&r.features.has("shader-f16"))})}),ye.catch(()=>{ye=null;try{pe.terminate()}catch(n){}pe=null}),ye}function ae(e){if(oe&&(e||!oe.field.isConnected))oe.stop()}function Ue(e){if(!Xn)return null;let n=document.createElement("button");n.className="ib sm mic",n.title="Dictate",n.setAttribute("aria-label","Dictate"),n.type="button";let o=()=>{n.innerHTML=j("mic"),n.classList.remove("on","busy")};o();let l=e[0];return e.forEach((r)=>r.addEventListener("focus",()=>l=r)),n.onclick=async(r)=>{if(r.preventDefault(),r.stopPropagation(),oe){let M=oe.btn===n;if(oe.stop(),M)return}if(Ae)return;Ae=!0,n.classList.add("busy"),st=(M)=>{n.innerHTML=`<i>${M}%</i>`};try{await Zn()}catch(M){o(),st=()=>{},Ae=!1,S("Dictation model could not load: "+M.message);return}if(st=()=>{},n.innerHTML=j("mic"),!n.isConnected){o(),Ae=!1;return}let c;try{c=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,noiseSuppression:!0,echoCancellation:!0}})}catch(M){o(),Ae=!1,S("Microphone not available: "+(M.name==="NotAllowedError"?"permission denied":M.message));return}if(oe)oe.stop();let f=new AudioContext({sampleRate:16000});await f.audioWorklet.addModule(URL.createObjectURL(new Blob([Jn],{type:"text/javascript"})));let k=f.createMediaStreamSource(c),v=new AudioWorkletNode(f,"kz-tap");k.connect(v);let h=l,u=h.selectionStart??h.value.length,g=document.createElement("span");g.className="vu",g.setAttribute("aria-hidden","true"),g.innerHTML=[0.5,0.9,1.3,0.8,0.45].map((M)=>`<i style="--w:${M}"></i>`).join("");let p=[],d=0,b=0,x=0,T=0,P=0,H=new Map,L=!1,K=16000,G=6*K,N=0.8*K,W=1*K,ce=(M)=>{if(!M||!h.isConnected)return;let U=h.value;if(document.activeElement===h)u=h.selectionStart;u=Math.min(u,U.length);let Z=U.slice(0,u),be=Z&&!/\s$/.test(Z)?" ":"";h.value=Z+be+M+U.slice(u),u=Z.length+be.length+M.length,h.selectionStart=h.selectionEnd=u,h.dispatchEvent(new Event("input",{bubbles:!0}))},$e=()=>{if(!d||b<W){p=[],d=0,b=0,x=0;return}let M=new Float32Array(d),U=0;for(let Z of p)M.set(Z,U),U+=Z.length;p=[],d=0,b=0,x=0,H.set(T,null),pe.postMessage({type:"run",sid:Ie,seq:T,audio:M},[M.buffer]),T++,n.classList.add("busy")},I=()=>{while(H.has(P)&&H.get(P)!==null)ce(H.get(P)),H.delete(P),P++;if(!H.size){if(n.classList.remove("busy"),L)o(),ot.delete(ge)}};v.port.onmessage=(M)=>{let U=M.data;p.push(U),d+=U.length;let Z=0;for(let se=0;se<U.length;se++)Z+=U[se]*U[se];let be=Math.sqrt(Z/U.length);if(be>0.01)b+=U.length,x=0;else x+=U.length;{let se=Math.min(1,be*8).toFixed(2);h.style.setProperty("--lvl",se),g.style.setProperty("--lvl",se)}if(d>=G||b>=W&&x>=N)$e()};let Ie=Math.random().toString(36).slice(2),ge={btn:n,field:h,onmsg:(M)=>{if(M.sid!==Ie)return;if(M.type==="text")H.set(M.seq,M.text),I();else if(M.type==="error"&&M.seq!=null)H.set(M.seq,""),S("Dictation failed: "+M.error),I()},stop:()=>{if(L)return;L=!0,n.classList.remove("on"),h.classList.remove("dictating"),h.style.removeProperty("--lvl"),n.innerHTML=j("mic");try{v.port.onmessage=null,k.disconnect(),v.disconnect(),c.getTracks().forEach((M)=>M.stop()),f.close()}catch(M){}oe=null,$e(),I()}};ot.add(ge),oe=ge,Ae=!1,n.classList.remove("busy"),n.classList.add("on"),h.classList.add("dictating"),n.replaceChildren(g),h.focus()},n}var sn="kz-d:";function Te(e,n){try{let o=sessionStorage.getItem("kz-d:"+n);if(o!=null)e.value=o}catch(o){}e.addEventListener("input",()=>{try{sessionStorage.setItem("kz-d:"+n,e.value)}catch(o){}})}function on(e){try{Object.keys(sessionStorage).forEach((n)=>{if(n.startsWith("kz-d:"+e))sessionStorage.removeItem(n)})}catch(n){}}var an=86400000,rn=(e,n)=>{let o=n-e;return o<3600000?Math.round(o/60000)+"m":o<86400000?Math.round(o/3600000)+"h":Math.round(o/86400000)+"d"},xt={claude:"Claude Code",codex:"Codex",agy:"Antigravity",opencode:"OpenCode",gemini:"Gemini",cursor:"Cursor",yolo:"yolo",unrechecked:"fix not rechecked"},ln={claude:1,codex:1,agy:1,opencode:1,gemini:1,cursor:1,full:1,lite:1},cn=["Ideas","Planning","Building","Review","Done"],at={idea:"idea",starting:"start",running:"run",stalled:"stall",waiting:"wait",done:"done",abandoned:"aband"},es={"approvals.plan":"plan needs your approval","approvals.review":"review needs your approval"},Ge=(e)=>es[e]||e,Fe={idea:"idea",starting:"starting",running:"running",stalled:"stalled",waiting:"waiting on you",done:"done",abandoned:"abandoned"};function dn(e,n){let o=e&&e.match(new RegExp(`^##\\s+${n}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$(?![\\s\\S]))`,"m"));return o?o[1].trim():null}var it=(e)=>dn(e,"Preview"),rt=(e,n)=>e.replace(/\]\((attachments\/[^)]+)\)/g,(o,l)=>`](/file?dir=${encodeURIComponent(n.state)}&p=${encodeURIComponent((n.kind==="run"?`runs/${n.id}/`:"")+l)})`),un=(e)=>(dn(e,"Commit")||"").replace(/^```\w*\n|\n```$/g,"").trim();function pn(e){let n=(e||"").split(`
11
+ }catch(err){postMessage({type:"error",sid:m.sid,seq:m.seq,error:String(err&&err.message||err)})}}`,Xn='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)',Zn=!!(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&window.AudioWorkletNode&&window.WebAssembly&&window.Worker),pe=null,ye=null,st=()=>{},oe=null,Ae=!1,ot=new Set;async function es(){let e="small";try{e=((await fetch("/settings").then((o)=>o.json())).rows.find((o)=>o.key==="dictation.model")||{}).value||e}catch(n){}if(!nn[e])e="small";if(ye&&$t!==e){try{pe.terminate()}catch(n){}pe=null,ye=null,ot.clear(),oe=null}if(ye)return ye;return $t=e,ye=new Promise(async(n,o)=>{pe=new Worker(URL.createObjectURL(new Blob([Jn],{type:"text/javascript"})),{type:"module"});let l={};pe.onmessage=(c)=>{let f=c.data;if(f.type==="progress"){l[f.file]=f.pct;let k=Object.values(l);st(Math.round(k.reduce((v,h)=>v+h,0)/k.length))}else if(f.type==="ready")n();else if(f.type==="fallback")S("GPU could not run the model, using the CPU: "+f.why);else if(f.type==="error"&&f.seq==null)o(Error(f.error));for(let k of ot)k.onmsg(f)},pe.onerror=(c)=>o(Error(c.message||"worker failed"));let r=null;try{r=navigator.gpu&&await navigator.gpu.requestAdapter()}catch(c){}if(!r)S("No WebGPU: dictation will run on the CPU and be slower");pe.postMessage({type:"load",model:nn[$t],webgpu:!!r,f16:!!(r&&r.features.has("shader-f16"))})}),ye.catch(()=>{ye=null;try{pe.terminate()}catch(n){}pe=null}),ye}function ae(e){if(oe&&(e||!oe.field.isConnected))oe.stop()}function Ue(e){if(!Zn)return null;let n=document.createElement("button");n.className="ib sm mic",n.title="Dictate",n.setAttribute("aria-label","Dictate"),n.type="button";let o=()=>{n.innerHTML=j("mic"),n.classList.remove("on","busy")};o();let l=e[0];return e.forEach((r)=>r.addEventListener("focus",()=>l=r)),n.onclick=async(r)=>{if(r.preventDefault(),r.stopPropagation(),oe){let M=oe.btn===n;if(oe.stop(),M)return}if(Ae)return;Ae=!0,n.classList.add("busy"),st=(M)=>{n.innerHTML=`<i>${M}%</i>`};try{await es()}catch(M){o(),st=()=>{},Ae=!1,S("Dictation model could not load: "+M.message);return}if(st=()=>{},n.innerHTML=j("mic"),!n.isConnected){o(),Ae=!1;return}let c;try{c=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,noiseSuppression:!0,echoCancellation:!0}})}catch(M){o(),Ae=!1,S("Microphone not available: "+(M.name==="NotAllowedError"?"permission denied":M.message));return}if(oe)oe.stop();let f=new AudioContext({sampleRate:16000});await f.audioWorklet.addModule(URL.createObjectURL(new Blob([Xn],{type:"text/javascript"})));let k=f.createMediaStreamSource(c),v=new AudioWorkletNode(f,"kz-tap");k.connect(v);let h=l,u=h.selectionStart??h.value.length,g=document.createElement("span");g.className="vu",g.setAttribute("aria-hidden","true"),g.innerHTML=[0.5,0.9,1.3,0.8,0.45].map((M)=>`<i style="--w:${M}"></i>`).join("");let p=[],d=0,b=0,x=0,T=0,R=0,H=new Map,L=!1,K=16000,G=6*K,N=0.8*K,W=1*K,ce=(M)=>{if(!M||!h.isConnected)return;let U=h.value;if(document.activeElement===h)u=h.selectionStart;u=Math.min(u,U.length);let ee=U.slice(0,u),be=ee&&!/\s$/.test(ee)?" ":"";h.value=ee+be+M+U.slice(u),u=ee.length+be.length+M.length,h.selectionStart=h.selectionEnd=u,h.dispatchEvent(new Event("input",{bubbles:!0}))},$e=()=>{if(!d||b<W){p=[],d=0,b=0,x=0;return}let M=new Float32Array(d),U=0;for(let ee of p)M.set(ee,U),U+=ee.length;p=[],d=0,b=0,x=0,H.set(T,null),pe.postMessage({type:"run",sid:Ie,seq:T,audio:M},[M.buffer]),T++,n.classList.add("busy")},I=()=>{while(H.has(R)&&H.get(R)!==null)ce(H.get(R)),H.delete(R),R++;if(!H.size){if(n.classList.remove("busy"),L)o(),ot.delete(ge)}};v.port.onmessage=(M)=>{let U=M.data;p.push(U),d+=U.length;let ee=0;for(let se=0;se<U.length;se++)ee+=U[se]*U[se];let be=Math.sqrt(ee/U.length);if(be>0.01)b+=U.length,x=0;else x+=U.length;{let se=Math.min(1,be*8).toFixed(2);h.style.setProperty("--lvl",se),g.style.setProperty("--lvl",se)}if(d>=G||b>=W&&x>=N)$e()};let Ie=Math.random().toString(36).slice(2),ge={btn:n,field:h,onmsg:(M)=>{if(M.sid!==Ie)return;if(M.type==="text")H.set(M.seq,M.text),I();else if(M.type==="error"&&M.seq!=null)H.set(M.seq,""),S("Dictation failed: "+M.error),I()},stop:()=>{if(L)return;L=!0,n.classList.remove("on"),h.classList.remove("dictating"),h.style.removeProperty("--lvl"),n.innerHTML=j("mic");try{v.port.onmessage=null,k.disconnect(),v.disconnect(),c.getTracks().forEach((M)=>M.stop()),f.close()}catch(M){}oe=null,$e(),I()}};ot.add(ge),oe=ge,Ae=!1,n.classList.remove("busy"),n.classList.add("on"),h.classList.add("dictating"),n.replaceChildren(g),h.focus()},n}var sn="kz-d:";function Te(e,n){try{let o=sessionStorage.getItem("kz-d:"+n);if(o!=null)e.value=o}catch(o){}e.addEventListener("input",()=>{try{sessionStorage.setItem("kz-d:"+n,e.value)}catch(o){}})}function on(e){try{Object.keys(sessionStorage).forEach((n)=>{if(n.startsWith("kz-d:"+e))sessionStorage.removeItem(n)})}catch(n){}}var an=86400000,rn=(e,n)=>{let o=n-e;return o<3600000?Math.round(o/60000)+"m":o<86400000?Math.round(o/3600000)+"h":Math.round(o/86400000)+"d"},xt={claude:"Claude Code",codex:"Codex",agy:"Antigravity",opencode:"OpenCode",gemini:"Gemini",cursor:"Cursor",yolo:"yolo",unrechecked:"fix not rechecked"},ln={claude:1,codex:1,agy:1,opencode:1,gemini:1,cursor:1,full:1,lite:1},cn=["Ideas","Planning","Building","Review","Done"],at={idea:"idea",starting:"start",running:"run",stalled:"stall",waiting:"wait",done:"done",abandoned:"aband"},ts={"approvals.plan":"plan needs your approval","approvals.review":"review needs your approval"},Ge=(e)=>ts[e]||e,Fe={idea:"idea",starting:"starting",running:"running",stalled:"stalled",waiting:"waiting on you",done:"done",abandoned:"abandoned"};function dn(e,n){let o=e&&e.match(new RegExp(`^##\\s+${n}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$(?![\\s\\S]))`,"m"));return o?o[1].trim():null}var it=(e)=>dn(e,"Preview"),rt=(e,n)=>e.replace(/\]\((attachments\/[^)]+)\)/g,(o,l)=>`](/file?dir=${encodeURIComponent(n.state)}&p=${encodeURIComponent((n.kind==="run"?`runs/${n.id}/`:"")+l)})`),un=(e)=>(dn(e,"Commit")||"").replace(/^```\w*\n|\n```$/g,"").trim();function pn(e){let n=(e||"").split(`
12
12
  `),o=n.findIndex((v)=>/^#+\s*(\d+\.\s*)?open questions/i.test(v.trim()));if(o<0)return[];let l=/^#+/.exec(n[o].trim())[0].length,r=[],c=null,f=!1;for(let v of n.slice(o+1)){let h=v.trim(),u=/^#+(?=\s)/.exec(h);if(u&&u[0].length<=l)break;if(!u&&h&&!/^```/.test(h)&&!/^none\.?$/i.test(h))f=!0;let g=/^\**Q\d*\s*[:.)]\**\s*(.+)$/.exec(h);if(g){r.push(c={q:g[1].trim(),multi:/multi-?select/i.test(g[1]),options:[]});continue}let p=/^[-*]\s+(.+)$/.exec(h);if(p&&c){let d=p[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 k=r.filter((v)=>v.options.length);return k.length||!f?k:null}function xe(e){let n=/(^|[.!?)]\s+|\s)([A-Z][\w-]*(?: [a-z][\w-]*){0,3}):\s+/g,o=[],l;while(l=n.exec(e))if(!/^\s+$/.test(l[1])||!o.length&&!l[2].includes(" "))o.push([l.index+l[1].length,l[2],n.lastIndex]);if(o.length<2)return{head:e,parts:[]};let r=o.map(([,c,f],k)=>{let v=e.slice(f,k+1<o.length?o[k+1][0]:e.length).trim(),h=v.split(/\s*\((\d+)\)\s+/);if(h.length<5)return{label:c,body:v};let u=[];for(let p=1;p<h.length;p+=2)u.push([h[p],h[p+1]]);let g=u.every(([p],d)=>+p===d+1);return{label:c,body:(h[0]?h[0]+`
13
13
  `:"")+u.map(([p,d],b)=>g?`${b+1}. ${d}`:`- (${p}) ${d}`).join(`
14
14
  `)}});return{head:e.slice(0,o[0][0]).trim()||`${r[0].label}: ${e.slice(o[0][2],o[1][0]).trim()}`,parts:r}}function ve(e,n){let o=e.split(`
15
15
  `),l=[],r=0,c=null,f=(v)=>m(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>'),k=()=>{if(c)l.push(`</${c}>`),c=null};while(r<o.length){let v=o[r];if(/^```/.test(v)){k();let p=[];r++;while(r<o.length&&!/^```/.test(o[r]))p.push(o[r++]);l.push(`<pre>${m(p.join(`
16
- `))}</pre>`),r++;continue}let h=/^(#{1,6})\s+(.*)$/.exec(v);if(h){k(),l.push(`<h${Math.min(h[1].length,3)}>${f(h[2])}</h${Math.min(h[1].length,3)}>`),r++;continue}if(/^---+$/.test(v.trim())){k(),l.push("<hr>"),r++;continue}if(/^\|/.test(v)){k();let p=[];while(r<o.length&&/^\|/.test(o[r]))p.push(o[r++]);let d=(b)=>b.replace(/^\||\|$/g,"").split("|").map((x)=>f(x.trim()));l.push("<table>"+p.filter((b)=>!/^\|\s*-/.test(b)).map((b,x)=>`<tr>${d(b).map((T)=>x?`<td>${T}</td>`:`<th>${T}</th>`).join("")}</tr>`).join("")+"</table>");continue}let u=/^\s*(?:[-*]|\d+\.)\s+(.*)$/.exec(v);if(u){let p=/^\s*\d+\./.test(v)?"ol":"ul";if(c!==p)k(),l.push(`<${p}>`),c=p;let d=n&&/^(open|done|rejected):\s*(.*)$/.exec(u[1]);l.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>${f(d[2])}</span></label></li>`:`<li>${f(u[1])}</li>`),r++;continue}if(!v.trim()){k(),r++;continue}k();let g=[v];r++;while(r<o.length&&o[r].trim()&&!/^(#|```|\||\s*[-*]\s|\s*\d+\.\s|---)/.test(o[r]))g.push(o[r++]);l.push(`<p>${f(g.join(" "))}</p>`)}return k(),l.join("")}function fn(e,n){let o=[...e.matchAll(/^\s*-\s*(todo|doing|done):\s*(.*)$/gm)].map((f)=>[f[1],f[2]]);if(!o.length)return"";let l=o.filter((f)=>f[0]==="done").length,r=Math.round(l/o.length*100),c={done:"✓",doing:'<span class="pulse"></span>',todo:""};return`<div class="prog"><div class="ph"><b>${l} of ${o.length} done</b><span>${r}%</span></div><div class="pbar"><i style="width:${r}%"></i></div><ol>${o.map(([f,k],v)=>`<li class="p-${f}${n&&n[v]&&n[v]!==f?" chg":""}"><span class="pi">${c[f]}</span><span>${m(k).replace(/`([^`]+)`/g,"<code>$1</code>")}</span></li>`).join("")}</ol></div>`}var Be=null,St=null;function mn(e){if(St=e,!e.update)return!1;Be=e;let n=document.querySelector('.rail a[data-view="settings"]');return n.classList.add("upd"),n.title=`Settings: kaizen ${e.latest} is available`,!0}function qt(){if(!Be){let o=document.createElement("div");o.className="updbar quiet",o.innerHTML=`<span id="upd-msg" aria-live="polite">${St?`You are running kaizen ${m(St.version)}.`:"kaizen"}</span><button class="btn" id="upd-chk">Check for updates</button>`,t("pbody").prepend(o);let l=t("upd-chk");l.onclick=async()=>{l.disabled=!0,l.textContent="Checking…";let r=await fetch("/version?fresh=1").then((c)=>c.json()).catch(()=>null);if(!o.isConnected)return;if(l.disabled=!1,l.textContent="Check for updates",r&&mn(r)){o.remove(),qt(),t("upd-go").focus();return}t("upd-msg").textContent=!r||!r.latest?"Could not reach npm. Try again later.":`kaizen ${r.version} is up to date.`};return}let e=document.createElement("div");e.className="updbar",e.innerHTML=`<span>kaizen <b>${m(Be.latest)}</b> is available. You are running ${m(Be.version)}.</span><button class="btn pri" id="upd-go">Update kaizen</button>`,t("pbody").prepend(e);let n=t("upd-go");n.onclick=async()=>{if(!confirm(`Update kaizen to ${Be.latest}? The board restarts, and agents running inside cards stop.`))return;if(n.disabled=!0,n.textContent="Updating…",!(await A("/upgrade",{})).ok){n.disabled=!1,n.textContent="Update kaizen";return}n.textContent="Restarting…",setTimeout(()=>location.reload(),20000)}}function hn(){fetch("/version").then((e)=>e.json()).then((e)=>{if(mn(e)&&_==="settings")Se()}).catch(()=>{})}var gn=(e)=>{let n=new Map;for(let o of e){if(!n.has(o.group))n.set(o.group,[]);n.get(o.group).push(o)}return[...n]};async function Se(){if(_!=="settings")return;let e=++a.pageGen;if(ae(!0),!t("panel").classList.contains("open")||t("ptitle").textContent!=="Settings")t("pbody").innerHTML='<p class="snone">Loading…</p>',t("psub").textContent="";fe(),a.openKey=null,a.runDoc=null,t("pterm").hidden=!0,t("panel").classList.add("open"),t("scrim").classList.add("on"),t("ptitle").textContent="Settings",t("ptabs").innerHTML="",t("pfoot").innerHTML="";let n=document.documentElement.dataset.theme||"system",o=await fetch("/settings").then((p)=>p.json()).catch(()=>null);if(e!==a.pageGen)return;t("psub").textContent="Theme applies to this browser. Everything else is saved to your kaizen settings.",t("psub").title=o?o.file:"";let l=(p,d,b)=>{if(p==="s-preset"){let x=d.filter((H)=>H!=="custom"),T=x.indexOf(b),P=(H)=>x[T<0?H>0?0:x.length-1:(T+H+x.length)%x.length];return`<span class="car" id="${p}" role="group" aria-label="Preset"><button id="${p}-prev" data-v="${P(-1)}" aria-label="Previous preset: ${P(-1)}" title="${P(-1)}">◁</button><span aria-live="polite">${m(b)}<i aria-hidden="true">${x.map((H)=>`<u class="${H===b?"on":""}"></u>`).join("")}</i></span><button id="${p}-next" data-v="${P(1)}" aria-label="Next preset: ${P(1)}" title="${P(1)}">▷</button></span>`}if(d.length===2&&d.includes("true")&&d.includes("false")){let x=b==="true";return`<button class="chk${x?" on":""}" id="${p}" data-v="${x?"false":"true"}" role="switch" aria-checked="${x}" title="${x?"On":"Off"}"></button>`}if(d.every((x)=>/^\d+$/.test(x)))return`<span class="bar" id="${p}" role="group">${d.map((x)=>`<button data-v="${x}" class="${x===b?"on":""}" aria-pressed="${x===b}">${x}</button>`).join("")}</span>`;return`<select class="sel" id="${p}">${d.map((x)=>`<option value="${m(x)}"${x===b?" selected":""}>${x===""?"—":m(x)}</option>`).join("")}</select>`},r=(p,d,b,x,T)=>`<div class="srow" data-q="${m(T.toLowerCase())}"><label for="${x}">${m(p)}</label>${d}<p class="hint">${b}</p></div>`,c=document.activeElement?.id==="sq",f=typeof Notification>"u"?null:Notification.permission,k="A desktop notification when a run starts waiting on you."+(f?f==="denied"?" Blocked in this browser's site settings.":"":" Not supported in this browser."),v=[...o?gn(o.rows).map(([p])=>p):[],"Browser"];t("pbody").innerHTML=`<div class="sq"><input class="sel" id="sq" type="search" placeholder="Search settings" aria-label="Search settings" spellcheck="false" autocomplete="off" value="${m(a.setQ)}"><div class="sjump" role="group" aria-label="Jump to section">${v.map((p)=>`<button data-g="${m(p)}">${m(p)}</button>`).join("")}</div></div>
16
+ `))}</pre>`),r++;continue}let h=/^(#{1,6})\s+(.*)$/.exec(v);if(h){k(),l.push(`<h${Math.min(h[1].length,3)}>${f(h[2])}</h${Math.min(h[1].length,3)}>`),r++;continue}if(/^---+$/.test(v.trim())){k(),l.push("<hr>"),r++;continue}if(/^\|/.test(v)){k();let p=[];while(r<o.length&&/^\|/.test(o[r]))p.push(o[r++]);let d=(b)=>b.replace(/^\||\|$/g,"").split("|").map((x)=>f(x.trim()));l.push("<table>"+p.filter((b)=>!/^\|\s*-/.test(b)).map((b,x)=>`<tr>${d(b).map((T)=>x?`<td>${T}</td>`:`<th>${T}</th>`).join("")}</tr>`).join("")+"</table>");continue}let u=/^\s*(?:[-*]|\d+\.)\s+(.*)$/.exec(v);if(u){let p=/^\s*\d+\./.test(v)?"ol":"ul";if(c!==p)k(),l.push(`<${p}>`),c=p;let d=n&&/^(open|done|rejected):\s*(.*)$/.exec(u[1]);l.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>${f(d[2])}</span></label></li>`:`<li>${f(u[1])}</li>`),r++;continue}if(!v.trim()){k(),r++;continue}k();let g=[v];r++;while(r<o.length&&o[r].trim()&&!/^(#|```|\||\s*[-*]\s|\s*\d+\.\s|---)/.test(o[r]))g.push(o[r++]);l.push(`<p>${f(g.join(" "))}</p>`)}return k(),l.join("")}function fn(e,n){let o=[...e.matchAll(/^\s*-\s*(todo|doing|done):\s*(.*)$/gm)].map((f)=>[f[1],f[2]]);if(!o.length)return"";let l=o.filter((f)=>f[0]==="done").length,r=Math.round(l/o.length*100),c={done:"✓",doing:'<span class="pulse"></span>',todo:""};return`<div class="prog"><div class="ph"><b>${l} of ${o.length} done</b><span>${r}%</span></div><div class="pbar"><i style="width:${r}%"></i></div><ol>${o.map(([f,k],v)=>`<li class="p-${f}${n&&n[v]&&n[v]!==f?" chg":""}"><span class="pi">${c[f]}</span><span>${m(k).replace(/`([^`]+)`/g,"<code>$1</code>")}</span></li>`).join("")}</ol></div>`}var Be=null,St=null;function mn(e){if(St=e,!e.update)return!1;Be=e;let n=document.querySelector('.rail a[data-view="settings"]');return n.classList.add("upd"),n.title=`Settings: kaizen ${e.latest} is available`,!0}function qt(){if(!Be){let o=document.createElement("div");o.className="updbar quiet",o.innerHTML=`<span id="upd-msg" aria-live="polite">${St?`You are running kaizen ${m(St.version)}.`:"kaizen"}</span><button class="btn" id="upd-chk">Check for updates</button>`,t("pbody").prepend(o);let l=t("upd-chk");l.onclick=async()=>{l.disabled=!0,l.textContent="Checking…";let r=await fetch("/version?fresh=1").then((c)=>c.json()).catch(()=>null);if(!o.isConnected)return;if(l.disabled=!1,l.textContent="Check for updates",r&&mn(r)){o.remove(),qt(),t("upd-go").focus();return}t("upd-msg").textContent=!r||!r.latest?"Could not reach npm. Try again later.":`kaizen ${r.version} is up to date.`};return}let e=document.createElement("div");e.className="updbar",e.innerHTML=`<span>kaizen <b>${m(Be.latest)}</b> is available. You are running ${m(Be.version)}.</span><button class="btn pri" id="upd-go">Update kaizen</button>`,t("pbody").prepend(e);let n=t("upd-go");n.onclick=async()=>{if(!confirm(`Update kaizen to ${Be.latest}? The board restarts, and agents running inside cards stop.`))return;if(n.disabled=!0,n.textContent="Updating…",!(await A("/upgrade",{})).ok){n.disabled=!1,n.textContent="Update kaizen";return}n.textContent="Restarting…",setTimeout(()=>location.reload(),20000)}}function hn(){fetch("/version").then((e)=>e.json()).then((e)=>{if(mn(e)&&_==="settings")Se()}).catch(()=>{})}var gn=(e)=>{let n=new Map;for(let o of e){if(!n.has(o.group))n.set(o.group,[]);n.get(o.group).push(o)}return[...n]};async function Se(){if(_!=="settings")return;let e=++a.pageGen;if(ae(!0),!t("panel").classList.contains("open")||t("ptitle").textContent!=="Settings")t("pbody").innerHTML='<p class="snone">Loading…</p>',t("psub").textContent="";fe(),a.openKey=null,a.runDoc=null,t("pterm").hidden=!0,t("panel").classList.add("open"),t("scrim").classList.add("on"),t("ptitle").textContent="Settings",t("ptabs").innerHTML="",t("pfoot").innerHTML="";let n=document.documentElement.dataset.theme||"system",o=await fetch("/settings").then((p)=>p.json()).catch(()=>null);if(e!==a.pageGen)return;t("psub").textContent="Theme applies to this browser. Everything else is saved to your kaizen settings.",t("psub").title=o?o.file:"";let l=(p,d,b)=>{if(p==="s-preset"){let x=d.filter((H)=>H!=="custom"),T=x.indexOf(b),R=(H)=>x[T<0?H>0?0:x.length-1:(T+H+x.length)%x.length];return`<span class="car" id="${p}" role="group" aria-label="Preset"><button id="${p}-prev" data-v="${R(-1)}" aria-label="Previous preset: ${R(-1)}" title="${R(-1)}">◁</button><span aria-live="polite">${m(b)}<i aria-hidden="true">${x.map((H)=>`<u class="${H===b?"on":""}"></u>`).join("")}</i></span><button id="${p}-next" data-v="${R(1)}" aria-label="Next preset: ${R(1)}" title="${R(1)}">▷</button></span>`}if(d.length===2&&d.includes("true")&&d.includes("false")){let x=b==="true";return`<button class="chk${x?" on":""}" id="${p}" data-v="${x?"false":"true"}" role="switch" aria-checked="${x}" title="${x?"On":"Off"}"></button>`}if(d.every((x)=>/^\d+$/.test(x)))return`<span class="bar" id="${p}" role="group">${d.map((x)=>`<button data-v="${x}" class="${x===b?"on":""}" aria-pressed="${x===b}">${x}</button>`).join("")}</span>`;return`<select class="sel" id="${p}">${d.map((x)=>`<option value="${m(x)}"${x===b?" selected":""}>${x===""?"—":m(x)}</option>`).join("")}</select>`},r=(p,d,b,x,T)=>`<div class="srow" data-q="${m(T.toLowerCase())}"><label for="${x}">${m(p)}</label>${d}<p class="hint">${b}</p></div>`,c=document.activeElement?.id==="sq",f=typeof Notification>"u"?null:Notification.permission,k="A desktop notification when a run starts waiting on you."+(f?f==="denied"?" Blocked in this browser's site settings.":"":" Not supported in this browser."),v=[...o?gn(o.rows).map(([p])=>p):[],"Browser"];t("pbody").innerHTML=`<div class="sq"><input class="sel" id="sq" type="search" placeholder="Search settings" aria-label="Search settings" spellcheck="false" autocomplete="off" value="${m(a.setQ)}"><div class="sjump" role="group" aria-label="Jump to section">${v.map((p)=>`<button data-g="${m(p)}">${m(p)}</button>`).join("")}</div></div>
17
17
  ${o?gn(o.rows).map(([p,d])=>`<h3 class="sg">${m(p)}</h3>${d.map((b)=>{let x="s-"+b.key.replace(/\./g,"-");return r(b.label,b.text?b.folder?`<span class="fpick"><input class="sel" id="${x}" value="${m(b.value)}" placeholder="not set" spellcheck="false"><button class="btn" id="${x}-pick" type="button">Browse…</button></span>`:`<input class="sel" id="${x}" value="${m(b.value)}" placeholder="not set" spellcheck="false">`:l(x,b.values,b.value),m(b.help),x,[b.label,b.help,b.key,p,...b.values].join(" "))}).join("")}`).join(""):'<p class="snone">Settings could not be read. Run <code>kaizen settings</code>.</p>'}
18
18
  <h3 class="sg">Browser</h3>${r("Theme",l("th",["system","light","dark"],n),"Light or dark for this browser only; system follows your device.","th","theme light dark system browser")}
19
19
  ${r("Notifications",`<button class="chk${f==="granted"?" on":""}" id="nt" role="switch" aria-checked="${f==="granted"}"${f&&f!=="denied"?"":" disabled"}></button>`,k,"nt","notifications "+k+" browser")}
20
- <p class="snone" id="snone" hidden></p>`,qt();let h=[...t("pbody").querySelectorAll(".sg")],u=[...t("pbody").querySelectorAll(".sjump button")],g=()=>{let p=a.setQ.trim().toLowerCase(),d=!1;h.forEach((b,x)=>{let T=b.nextElementSibling,P=!1;for(;T&&T.matches(".srow");T=T.nextElementSibling)T.hidden=!!p&&!T.dataset.q.includes(p),P||=!T.hidden;if(b.hidden=!P,u[x])u[x].hidden=!P;d||=P}),t("snone").hidden=d,t("snone").textContent=`No setting matches “${a.setQ.trim()}”.`};if(u.forEach((p,d)=>p.onclick=()=>{let b=t("pbody");b.scrollTo({top:b.scrollTop+h[d].getBoundingClientRect().top-b.querySelector(".sq").getBoundingClientRect().bottom-8,behavior:"smooth"})}),t("sq").oninput=(p)=>{a.setQ=p.target.value,g()},t("sq").onkeydown=(p)=>{if(p.key!=="Escape")return;if(p.preventDefault(),a.setQ)a.setQ="",p.target.value="",g();else Q()},g(),c){let p=t("sq");p.focus(),p.setSelectionRange(p.value.length,p.value.length)}if(t("th").onchange=(p)=>setTheme(p.target.value),t("nt").onclick=()=>{ue(),setTimeout(Se,300)},o)o.rows.forEach((p)=>{let d=t("s-"+p.key.replace(/\./g,"-")),b=async(x)=>{let T=document.activeElement?.id;if((await A("/settings",{key:p.key,value:x})).ok)S("Saved");if(await Se(),T?.startsWith("s-preset-"))t(T)?.focus()};if(d.tagName==="SELECT"||d.tagName==="INPUT")d.onchange=async()=>{if(await b(d.value.trim()),d.tagName==="INPUT")D()};else d.querySelectorAll("[data-v]").forEach((x)=>x.onclick=()=>b(x.dataset.v));if(d.matches(".chk"))d.onclick=()=>b(d.dataset.v);if(p.folder)t(d.id+"-pick").onclick=async()=>{let x=await A("/pick-folder",{});if(x.path)await b(x.path),D()}})}var _="board",Lt=(e)=>{document.querySelectorAll(".rail a.it").forEach((n)=>n.dataset.view===e?n.setAttribute("aria-current","page"):n.removeAttribute("aria-current")),document.querySelector(".hdr h1").textContent=e==="sources"?"Icebox":"Board"};function ie(e,n){if(a.pageGen++,e==="icebox")e="sources";if(!["settings","sources"].includes(e))e="board";let o=_;if(_=e,Lt(e),document.body.classList.toggle("paged",e!=="board"),document.body.classList.toggle("ice",e==="sources"),n&&e!==o)history.pushState(null,"",e==="board"?location.pathname:"?view="+(e==="sources"?"icebox":e));let l=t("panel");if(e==="board"){if(o==="board")return;l.style.transition="none",l.classList.remove("open","docked"),l.offsetWidth,l.style.transition="",Q();return}if(l.classList.add("docked"),e==="settings")Se();else R()}function bn(){document.querySelectorAll(".rail a[data-view]").forEach((e)=>{if(e.dataset.view==="notes")return;e.onclick=(n)=>{if(n.ctrlKey||n.metaKey||n.shiftKey||n.button)return;if(n.preventDefault(),e.dataset.view==="settings")a.setQ="";ie(e.dataset.view,!0)}}),document.querySelector(".rail .home").onclick=(e)=>{if(e.ctrlKey||e.metaKey||e.shiftKey||e.button)return;e.preventDefault(),ie("board",!0)},addEventListener("popstate",()=>ie(new URLSearchParams(location.search).get("view"))),addEventListener("kz-theme",()=>{if(_==="settings")Se()})}var yn="Icebox",J=null,_e={},Ct=(e)=>{if(!_e[e])try{_e[e]=localStorage.getItem("kz-reply-style:"+e)}catch(n){}return _e[e]||"casual"},ts={casual:"Casual",fun:"Fun",helpful:"Helpful",promo:"Promo"},X=null,re=null,me="",te="",Ee="link",We=null,Me=!1,ct=!1,dt=null,F=new Set,lt={x:"X",tiktok:"TikTok",instagram:"Instagram",threads:"Threads"},B=()=>t("panel").classList.contains("open")&&t("ptitle").textContent===yn,De=(e)=>{let n=Math.round((Date.now()-Date.parse(e))/60000);return n<1?"just now":n<60?n+" min ago":n<1440?Math.round(n/60)+" h ago":Math.round(n/1440)+" d ago"};async function R(){if(_!=="sources")return;let e=++a.pageGen;ae(!0);let n=B()?[t("src-url")?.value||"",t("src-label")?.value||"",document.activeElement?.id,t("src-dir")?.value,t("src-cat")?.value||"",t("src-plat")?.value,t("src-q")?.value||""]:["",""],o=new Map(B()?[...t("pbody").querySelectorAll("[data-mv]")].map((s)=>[s.dataset.k,s.value]):[]),l=B()&&document.activeElement?.closest?.(".ice-row"),r=l?[l.dataset.k,document.activeElement.id.replace(/-\d+$/,"")]:null,c=B()&&t("ice-title")?[t("ice-title").dataset.k,t("ice-title").value,t("ice-details").value]:null,f=new Map(B()?[...t("pbody").querySelectorAll("[data-ct]")].map((s)=>[s.dataset.k,s.value]):[]);if(!B())t("pbody").innerHTML='<p class="snone">Loading…</p>';fe(),a.openKey=null,a.runDoc=null,t("pterm").hidden=!0,t("panel").classList.add("open"),t("scrim").classList.add("on"),t("ptitle").textContent=yn,t("ptabs").innerHTML="",t("pfoot").innerHTML="",t("psub").textContent="",t("psub").title="";let k=a.all?null:a.S.dir,v=k?"?dir="+encodeURIComponent(k):"",h=await fetch("/sources"+v).then((s)=>s.json()).catch(()=>null);if(e!==a.pageGen||!B())return;let u=(s)=>{try{return new URL(s).hostname}catch{return s}},g=(s,i,y)=>{if(lt[y])return`<span class="fav">${j("b-"+y)}</span>`;let w;try{let z=new URL(s);if(/^https?:$/.test(z.protocol))w=z.origin}catch{}let q=!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">${q?`<img src="${m(q)}" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.remove()">`:""}${j(i)}</span>`},p=(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"},d=h&&h.sources||[],b=[...h&&h.icebox||[],...h&&h.dismissed||[]],x=(h&&h.icebox||[]).length,T=h&&h.gatherer||{},P=T.use==="chatgpt"&&T.connected,H=(s,i)=>(s&&!a.S.projects.some((y)=>y.dir===s)?`<option value="${m(s)}" selected>${m(i||s)}</option>`:"")+a.S.projects.map((y)=>`<option value="${m(y.dir)}"${y.dir===s?" selected":""}>${m(y.label)}</option>`).join(""),L=d.map((s,i)=>{let y=p(s),w=s.label||u(s.url),q=s.social&&(!s.last||!s.last.ok&&/gathered by your agent/i.test(s.last.note))?`<span class="src-badge dim">${j("at")}${P?"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>${m(s.last.note)}</span><span class="hint" style="margin:0">${De(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: ${m(s.last.note)}</span><span class="hint" style="margin:0">${De(s.last.at)}</span>`;return`<div class="src-card" id="src-card-${i}"><div class="src-hd"><div class="src-info"><span class="src-ico" title="${y==="social"?m(lt[s.social]||"social"):y}">${g(s.url,y==="sheet"?"sheet":y==="doc"?"doc":y==="social"?"at":"link",s.social)}</span><span class="src-title" title="${m(w)}">${m(w)}</span></div><div class="src-acts">${s.social?`<button class="btn ghost sm" id="src-ag-${i}" data-ag="${i}" aria-label="Gather ${m(w)} with your agent" title="${P?"Read this source with your ChatGPT plan":"Open your agent to run /kaizen gather for this project"}">${j("refresh")} Gather</button>`:`<button class="btn ghost sm" id="src-chk-${i}" data-chk="${i}" aria-label="Check ${m(w)} now" title="Check this source now">${j("refresh")} Check</button>`}<button class="btn ghost danger sm" id="src-rm-${i}" data-rm="${i}" aria-label="Remove ${m(w)}" title="Remove source">${j("trash")}</button></div></div><div class="src-url"><a href="${m(s.url)}" target="_blank" rel="noopener noreferrer" title="${m(s.url)}"><span>${m(s.url)}</span>${j("ext")}</a></div><div class="src-status">${q}</div><div class="src-dest"><span>Category</span><input class="sel" id="src-ct-${i}" data-ct="${i}" data-k="${m(s.dir+" "+s.url)}" value="${m(s.category||"")}" placeholder="e.g. Feedback" maxlength="40" aria-label="Category of ${m(w)}"><button class="btn sm" id="src-cs-${i}" aria-label="Save the category of ${m(w)}" disabled>Save</button></div><div class="src-dest"><span>Project</span><select class="sel" id="src-mv-${i}" data-mv="${i}" data-k="${m(s.dir+" "+s.url)}" aria-label="Project of ${m(w)}">${H(s.dir,s.project)}</select><button class="btn sm" id="src-mb-${i}" aria-label="Move ${m(w)} to the chosen project" disabled>Move</button></div></div>`}).join(""),K=(h&&h.failed||[]).map((s)=>`<p style="color:var(--dim)">The sources of ${m(s)} could not be read.</p>`).join(""),G=`<div class="src-empty"><div class="src-empty-ico">${j("link")}</div><h4>No request sources yet</h4><p>Connect shared links to gather requests into the icebox, where you choose which become ideas. Nothing runs automatically until you start it.</p><div class="src-empty-grid"><div class="src-empty-item"><strong>Google Sheets</strong>Public sheets with idea or task rows. Rows marked Done are skipped.</div><div class="src-empty-item"><strong>Google Docs</strong>Shared public docs with items or notes written line by line.</div><div class="src-empty-item"><strong>Public links</strong>TSV, CSV, or raw text links containing requests.</div><div class="src-empty-item"><strong>Social media</strong>A keyword, #hashtag or @account on X, TikTok, Instagram or Threads, read by your agent.</div></div></div>`,N=Ee==="social",W=!d.length||Me||[n[0],n[1],n[4],n[6]].some(Boolean),ce=`<div class="src-add" id="src-addbox"${W?"":" 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="${!N}">Link</button><button id="src-k-social" data-kind="social" aria-pressed="${N}">Social</button></div></div><label for="src-url"${N?" hidden":""}><span>Link</span><input class="sel" id="src-url" placeholder="https://docs.google.com/spreadsheets/d/… or any public link" spellcheck="false" autocomplete="off"></label><div class="two plat"${N?"":" hidden"}><label for="src-plat"><span>Platform</span><select class="sel" id="src-plat">${Object.entries(lt).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="${N?"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="${N?"e.g. Social":"e.g. Bugs"}" maxlength="40"></label></div><label for="src-dir"><span>Project</span><select class="sel" id="src-dir">${k?"":'<option value="">Choose project…</option>'}${H(k)}</select></label><div class="acts"><span class="hint">${N?P?"Your ChatGPT plan reads public posts when you press Gather on the source.":"Kaizen signs in nowhere. Your agent reads public posts when you press Gather on the source.":"Public links only. For private docs, run /kaizen gather."}</span><button class="btn pri" id="src-add">Add source</button></div></div>`,$e=(s)=>{let i=d.find((y)=>y.dir===s.dir&&y.url===s.source);return i?i.label||u(i.url):u(s.source)},I=(s)=>s.dir+" "+s.id,Ie=(s)=>s.dir+" "+s.source,ge=(s)=>s.status==="dismissed"?"Dismissed":s.category||"Uncategorised",M=(s,i)=>{let y=new Map;return s.forEach((w)=>{let q=i(w);y.set(q,{it:w,n:(y.get(q)?.n||0)+1})}),y},U=k?new Map:M(b,(s)=>s.dir);if(!k){for(let s of a.shown)if(!U.has(s)){let i=qe().find((y)=>y.dir===s);if(i)U.set(s,{it:{dir:s,project:i.label},n:0})}}let Z=(s)=>k||!a.shown.size||a.shown.has(s.dir),be=b.filter(Z),se=M(be,ge);if(te&&!se.has(te))te="";let Nn=be.filter((s)=>!te||ge(s)===te),kt=M(Nn,Ie);if(me&&!kt.has(me))me="";let In=new Set(b.map(I));if(X&&!In.has(X))X=null;let He=new Map;b.forEach((s,i)=>{if(!Z(s)||(te?ge(s)!==te:s.status==="dismissed")||me&&Ie(s)!==me)return;let y=ge(s);He.has(y)||He.set(y,[]),He.get(y).push(i)});let O=[...He.values()].flat();{let s=new Set(O.map((i)=>I(b[i])));[...F].forEach((i)=>s.has(i)||F.delete(i))}if(!O.some((s)=>I(b[s])===re))re=O.length?I(b[O[0]]):null;let Oe=O.filter((s)=>F.has(I(b[s]))).length,Hn=U.size>1,Je=(s,i,y)=>`<option value="${m(s)}"${i?" selected":""}>${m(y)}</option>`,Wt=(s,i,y)=>`<select class="sel ice-sel" id="${s}" aria-label="${i}">${y.join("")}</select>`,On=(se.size>1?Wt("ice-fc","Category",[Je("",!te,"All categories"),...[...se.keys()].map((s)=>Je(s,te===s,s))]):"")+(kt.size>1?Wt("ice-fs","Source",[Je("",!me,"All sources"),...[...kt].map(([s,{it:i}])=>Je(s,me===s,$e(i)))]):""),zn=`<div class="ice-bar">${O.length?`<input type="checkbox" id="ice-all" aria-label="Select every request shown"${Oe&&Oe===O.length?" checked":""}>`:""}<span class="count">${te==="Dismissed"?`${O.length} dismissed`:`${O.length<x?`${O.length} of ${x}`:x} waiting`}</span>${On}<span class="end"></span><button class="btn sm" id="src-go"${d.length?"":" disabled"}>${j("refresh")} Gather all</button><button class="btn ghost sm" id="src-tog" aria-expanded="${We??!d.length}" aria-controls="src-sec">${j("link")} Sources · ${d.length}</button></div>`,_t=F.size?`<div class="ice-bulk" role="toolbar" aria-label="Selected requests"><span>${F.size} selected</span><span class="end"></span><button class="btn sm" id="ice-bp" title="Each becomes its own idea, titled as gathered (p)">${j("plus")} Promote</button><button class="btn ghost sm" id="ice-bd" title="Dismiss (d)">${j("x")} Dismiss</button><button class="btn ghost sm" id="ice-bc" title="Clear selection (Esc)">Clear</button></div>`:"",Un=(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="${m(I(s))}" maxlength="200" value="${m(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" placeholder="What, for whom, why" aria-label="Details"></textarea><div class="ice-form-acts"><span class="hint">Source goes into the notes.</span><button class="btn ghost sm" id="ice-cancel">Cancel</button><button class="btn pri sm" id="ice-ok">Promote</button></div></div>`,Gn=(s)=>{if(!s.post)return"";if(s.replied)return`<span class="hint" style="margin:0">You replied to this post ${De(s.replied)}.</span>`;let i=J&&J.k===I(s)?J:null,y=(w)=>`<select class="sel" id="ice-rst" aria-label="Reply style" title="Promo names kaizen; Helpful may mention it when it fits; Casual and Fun never do"${w?" disabled":""}>${Object.entries(ts).map(([q,z])=>`<option value="${q}"${q===Ct(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?y(!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?` · ${m(i.note)}`:""}</label><textarea class="sel" id="ice-reply" rows="3" maxlength="2000" aria-label="Your reply"${i.busy||i.again?" disabled":""}>${m(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?`${y(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>`},Fn=(s)=>{let i=b[s],y=I(i),w=X===y;return`<div class="ice-row${re===y?" cur":""}${F.has(y)?" sel":""}" data-i="${s}" data-k="${m(y)}"><input type="checkbox" id="ice-ck-${s}" data-ck="${s}"${F.has(y)?" checked":""} aria-label="Select: ${m(i.text)}"><span class="ice-src" title="${m($e(i))}">${g(i.source,"link",d.find((q)=>q.dir===i.dir&&q.url===i.source)?.social)}</span><button class="ice-t" id="ice-o-${s}" data-o="${s}" aria-expanded="${w}" title="${m(i.text)}">${m(i.text)}</button><span class="ice-meta">${Hn?`<span class="nm">${m(i.project||"")}</span>`:""}${i.replied?`<span title="Replied ${m(De(i.replied))}">replied</span>`:""}<span class="ice-age">${De(i.at)}</span><span class="ice-ra"><button class="btn ghost sm" id="ice-pr-${s}" data-pr="${s}" title="Promote to Ideas as gathered (p)" aria-label="Promote to Ideas: ${m(i.text)}">${j("plus")}</button>${i.status==="dismissed"?`<button class="btn ghost sm" id="ice-rs-${s}" data-rs="${s}" title="Restore to waiting" aria-label="Restore: ${m(i.text)}">Restore</button>`:`<button class="btn ghost sm" id="ice-ds-${s}" data-ds="${s}" title="Dismiss (d)" aria-label="Dismiss: ${m(i.text)}">${j("x")}</button>`}</span></span></div>`+(w?`<div class="ice-open">${i.notes?`<div class="ice-notes">${m(i.notes)}</div>`:""}${(i.images||[]).filter((q)=>/^https:\/\//.test(q)).length?`<details class="ice-imgs"><summary>Show images (${i.images.filter((q)=>/^https:\/\//.test(q)).length}) — loads them from the source's site</summary>${i.images.filter((q)=>/^https:\/\//.test(q)).map((q)=>`<a href="${m(q)}" target="_blank" rel="noopener noreferrer"><img data-src="${m(q)}" alt="" referrerpolicy="no-referrer"></a>`).join("")}</details>`:""}<span class="hint" style="margin:0">From ${m($e(i))} · ${De(i.at)}${i.post?` · <a href="${m(i.post)}" target="_blank" rel="noopener">open post</a>`:""}</span>${Gn(i)}${Un(i)}</div>`:"")},Bn=x&&!O.length?`<div class="ice-zero"><h4>Nothing matches</h4><p>${x} waiting outside these filters.</p><p style="margin-top:10px"><button class="btn sm" id="ice-unf">Clear filters</button></p></div>`:O.length?`<div class="ice-list">${[...He].map(([s,i])=>`<div class="ice-cat">${m(s)}<i>${i.length}</i></div>${i.map(Fn).join("")}`).join("")}</div>`:'<div class="ice-zero"><h4>Inbox zero</h4><p>Nothing waiting. Gather all checks your sources for new requests; /kaizen discover in your agent sorts what comes in.</p></div>',Xe=We??!d.length,wt=Xe&&!t("src-sec")?.classList.contains("open"),Wn=`<div class="src-scrim${Xe&&!wt?" on":""}" id="src-scrim"></div><aside class="src-sec${Xe&&!wt?" open":""}" id="src-sec" aria-label="Sources"${Xe?"":" inert"}><header><div class="t"><b>Sources</b><div class="sub">${h&&h.every&&h.every!=="off"?`Auto-check every ${m(h.every)}`:"Auto-check off"} · <a id="src-to-settings" role="button" tabindex="0">Settings</a> · ${T.connected?`ChatGPT${T.email?" "+m(T.email):""} <a id="cg-out" role="button" tabindex="0">Disconnect</a>`:'<a id="cg-in" role="button" tabindex="0">Connect ChatGPT</a>'}</div></div><div class="src-hda">${d.length?`<button class="btn sm" id="src-new" aria-expanded="${W}" aria-controls="src-addbox">${j("plus")} Add</button>`:""}<button class="ib" id="src-x" aria-label="Close sources" title="Close">${j("close")}</button></div></header><div class="body">${ce}${d.length?`<div class="src-list">${L}</div>`:G}</div></aside>`,_n=T.use==="ask"&&!T.connected?'<div class="cg-onb"><b>Gather social posts with your ChatGPT plan</b><p>Connect ChatGPT Plus or Pro and Gather reads X, TikTok, Instagram and Threads sources right here, counted against your plan, with no agent to open. Skip it and Gather keeps using your agent. You can change this in Settings.</p><div class="acts"><button class="btn ghost" id="cg-skip">Skip for now</button><button class="btn pri" id="cg-go">Continue with ChatGPT</button></div></div>':"";if(t("pbody").innerHTML=h&&h.error?`<p style="color:var(--dim)">${m(h.error)}</p>`:!h?'<p style="color:var(--dim)">The icebox could not be read.</p>':_n+zn+_t+K+Bn+Wn,!h||h.error)return;if(c&&c[0]===X&&t("ice-title"))t("ice-title").value=c[1],t("ice-details").value=c[2];if(t("src-add")){if(n[5])t("src-plat").value=n[5];if(t("src-q").value=n[6]||"",t("src-url").value=n[0],t("src-label").value=n[1],t("src-cat").value=n[4]||"",n[3]!==void 0&&t("src-dir"))t("src-dir").value=n[3]}if(t("src-to-settings"))t("src-to-settings").onclick=()=>{a.setQ="sources",ie("settings",!0)};let Yn=async()=>{let s=window.open("","_blank"),i=await A("/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(t(s))t(s).onclick=Yn;if(t("cg-skip"))t("cg-skip").onclick=async()=>{if((await A("/chatgpt",{op:"skip"})).ok)S("Gather keeps using your agent"),R()};if(t("cg-out"))t("cg-out").onclick=async()=>{if(!confirm("Disconnect ChatGPT? Gather goes back to your agent."))return;if((await A("/chatgpt",{op:"disconnect"})).ok)S("ChatGPT disconnected"),R()};if(wt)t("src-sec").offsetWidth,t("src-sec").classList.add("open"),t("src-scrim").classList.add("on");let Ze=()=>{We=!1,t("src-sec").classList.remove("open"),t("src-scrim").classList.remove("on"),t("src-sec").inert=!0,t("src-tog").setAttribute("aria-expanded","false"),t("src-tog").focus()};if(t("src-x").onclick=t("src-scrim").onclick=Ze,t("src-new"))t("src-new").onclick=()=>{let s=t("src-addbox");if(Me=s.hidden,s.hidden=!Me,t("src-new").setAttribute("aria-expanded",Me),Me)t(Ee==="social"?"src-q":"src-url").focus()};if(t("src-sec").onkeydown=(s)=>{if(s.key==="Escape")s.stopPropagation(),Ze()},t("src-tog").onclick=()=>{if(t("src-sec").classList.contains("open"))return Ze();We=!0,R().then(()=>We&&t("src-url")?.focus())},t("ice-fs"))t("ice-fs").onchange=(s)=>{me=s.target.value,R()};if(t("ice-fc"))t("ice-fc").onchange=(s)=>{te=s.target.value,R()};if(t("ice-unf"))t("ice-unf").onclick=()=>{if(te=me="",!k)a.shown.clear();ut(),R()};t("pbody").querySelectorAll("[data-kind]").forEach((s)=>s.onclick=()=>{if(Ee===s.dataset.kind)return;Ee=s.dataset.kind,R().then(()=>t(Ee==="social"?"src-q":"src-url")?.focus())});let je=async(s,i)=>{let y=await A("/sources",s);if(y.ok)i(y),D();if(B())R()};t("pbody").querySelectorAll("[data-mv]").forEach((s)=>{let i=d[+s.dataset.mv],y=t("src-mb-"+s.dataset.mv),w=o.get(s.dataset.k);if(w&&[...s.options].some((q)=>q.value===w))s.value=w;(s.onchange=()=>{y.disabled=s.value===i.dir})(),y.onclick=()=>je({op:"move",dir:i.dir,to:s.value,url:i.url},()=>S(`Source moved. Requests already gathered stay in ${i.project||"the old project"}.`))}),t("pbody").querySelectorAll("[data-ct]").forEach((s)=>{let i=d[+s.dataset.ct],y=t("src-cs-"+s.dataset.ct),w=f.get(s.dataset.k);if(w!==void 0)s.value=w;(s.oninput=()=>{y.disabled=s.value.trim()===(i.category||"")})(),s.onkeydown=(q)=>{if(q.key==="Enter"&&!y.disabled)y.click()},y.onclick=()=>{y.disabled=!0,je({op:"category",dir:i.dir,url:i.url,category:s.value.trim()},()=>S(s.value.trim()?`New requests from ${i.label||u(i.url)} go under ${s.value.trim()}`:"Category cleared"))}});let Yt=()=>{if(!document.activeElement||document.activeElement===document.body)t("pbody").querySelector(".ice-row.cur .ice-t")?.focus()},Qt=(s)=>{let i=O.findIndex((q)=>I(b[q])===re);if(i<0||!s.has(re))return;let y=(q)=>!s.has(I(b[q])),w=O.slice(i+1).find(y)??O.slice(0,i).reverse().find(y);re=w===void 0?null:I(b[w])},et=(s,i,y,w={})=>(async()=>{if(Qt(new Set([I(i)])),(await A("/icebox",{op:s,dir:i.dir,ids:[i.id],...w})).ok){if(s==="promote"&&X===I(i))X=null;S(y),D()}if(B())R().then(Yt)})(),Vt=async(s)=>{let i=b.filter((w)=>F.has(I(w)));if(!i.length)return;Qt(new Set(i.map(I))),ct=!0,t("ice-bp").disabled=t("ice-bd").disabled=!0;let y=0;try{if(s==="dismiss")for(let w of new Set(i.map((q)=>q.dir))){let q=i.filter((z)=>z.dir===w).map((z)=>z.id);if((await A("/icebox",{op:s,dir:w,ids:q})).ok)y+=q.length}else for(let w of i)if((await A("/icebox",{op:s,dir:w.dir,ids:[w.id]})).ok)y++}finally{ct=!1}if(F.clear(),y)S(`${y} ${s==="promote"?"promoted to Ideas":"dismissed"}${y<i.length?`, ${i.length-y} not`:""}`);if(D(),B())R().then(Yt)};if(_t)t("ice-bp").onclick=()=>Vt("promote"),t("ice-bd").onclick=()=>Vt("dismiss"),t("ice-bc").onclick=()=>{F.clear(),R()};if(t("ice-all"))t("ice-all").onchange=()=>{let s=O.map((i)=>I(b[i]));if(Oe===O.length)s.forEach((i)=>F.delete(i));else s.forEach((i)=>F.add(i));R()};if(t("ice-all"))t("ice-all").indeterminate=Oe>0&&Oe<O.length;t("pbody").querySelectorAll("[data-ck]").forEach((s)=>s.onchange=()=>{let i=I(b[+s.dataset.ck]);re=i,s.checked?F.add(i):F.delete(i),R()});let Jt=(s)=>{let i=I(b[s]);re=i,X=X===i?null:i,R().then(()=>{let y=t("ice-title");if(y&&X===i)y.focus(),y.select();else t("ice-o-"+s)?.focus()})};t("pbody").querySelectorAll(".ice-row").forEach((s)=>s.onclick=(i)=>{if(!i.target.closest("input,button"))Jt(+s.dataset.i)}),t("pbody").querySelectorAll("[data-o]").forEach((s)=>s.onclick=()=>Jt(+s.dataset.o)),t("pbody").querySelectorAll("details.ice-imgs").forEach((s)=>s.ontoggle=()=>s.open&&s.querySelectorAll("img[data-src]").forEach((i)=>i.src=i.dataset.src)),t("pbody").querySelectorAll("[data-pr]").forEach((s)=>s.onclick=()=>{s.disabled=!0,et("promote",b[+s.dataset.pr],"Promoted to Ideas")}),t("pbody").querySelectorAll("[data-ds]").forEach((s)=>s.onclick=()=>{s.disabled=!0,et("dismiss",b[+s.dataset.ds],"Dismissed")}),t("pbody").querySelectorAll("[data-rs]").forEach((s)=>s.onclick=()=>{s.disabled=!0,et("restore",b[+s.dataset.rs],"Restored")});let ze=b.find((s)=>I(s)===X);if(t("ice-rp"))t("ice-rp").onclick=async()=>{let s=ze,i=I(s);J={k:i,text:"",wait:!0},R();let y=await A("/reply",{op:"draft",dir:s.dir,id:s.id,style:Ct(s.dir)});if(!J||J.k!==i)return;J={k:i,text:y.text||"",note:y.ok?y.note||"":"",wait:!1},R().then(()=>t("ice-reply")?.focus())};if(t("ice-rst"))t("ice-rst").onchange=(s)=>{let i=ze.dir;_e[i]=s.target.value;try{localStorage.setItem("kz-reply-style:"+i,_e[i])}catch(y){}};if(t("ice-rg"))t("ice-rg").onclick=async()=>{let s=ze,i=J;i.again=!0,R();let y=await A("/reply",{op:"draft",dir:s.dir,id:s.id,style:Ct(s.dir),avoid:i.text});if(J!==i)return;if(i.again=!1,y.text)i.text=y.text;i.note=y.ok?y.note||"":"",R().then(()=>t("ice-reply")?.focus())};if(t("ice-reply")){let s=t("ice-reply");s.oninput=()=>{J.text=s.value,t("ice-rn").textContent=`${s.value.length} chars`,t("ice-rs").disabled=!s.value.trim()},s.onkeydown=(i)=>{if(i.key==="Escape")i.stopPropagation(),t("ice-rc").click()},t("ice-rc").onclick=()=>{J=null,R()},t("ice-rs").onclick=async()=>{let i=ze,y=J;y.busy=!0,R();let w=await A("/reply",{op:"send",dir:i.dir,id:i.id,text:y.text});if(w.ok)J=null,S("Reply posted");else if(w.sent)J=null;else y.busy=!1;if(D(),B())R()}}if(t("ice-ok")){let s=()=>{let i=t("ice-title").value.trim();if(!i){t("ice-title").focus();return}t("ice-ok").disabled=!0,et("promote",ze,"Promoted to Ideas",{text:i,details:t("ice-details").value})};t("ice-ok").onclick=s,t("ice-cancel").onclick=()=>{X=null,R()},t("ice-title").onkeydown=(i)=>{if(i.key==="Enter")s();if(i.key==="Escape")t("ice-cancel").click()},t("ice-details").onkeydown=(i)=>{if(i.key==="Enter"&&(i.ctrlKey||i.metaKey))s();if(i.key==="Escape")t("ice-cancel").click()}}dt=(s)=>{if(s.altKey||s.ctrlKey||s.metaKey)return!1;let i=s.key,y=O.findIndex((z)=>I(b[z])===re),w=O[y];if(i==="Escape"){if(t("src-sec")?.classList.contains("open"))return Ze(),!0;if(X)return X=null,R(),!0;if(F.size)return F.clear(),R(),!0;return!1}if(y<0)return!1;if(i==="j"||i==="k"){s.preventDefault();let z=O[Math.max(0,Math.min(O.length-1,y+(i==="j"?1:-1)))];return re=I(b[z]),t("pbody").querySelectorAll(".ice-row").forEach((ee)=>ee.classList.toggle("cur",+ee.dataset.i===z)),t("pbody").querySelector(`.ice-row[data-i="${z}"]`)?.scrollIntoView({block:"nearest"}),!0}let q=i==="o"?t("ice-o-"+w):i==="x"?t("ice-ck-"+w):i==="p"?F.size?t("ice-bp"):t("ice-pr-"+w):i==="d"?F.size?t("ice-bd"):t("ice-ds-"+w):null;if(!q||q.disabled)return!1;return s.preventDefault(),q.click(),!0},t("pbody").querySelectorAll("[data-chk]").forEach((s)=>{let i=d[+s.dataset.chk];s.onclick=()=>{s.disabled=!0,s.innerHTML=j("refresh")+" Checking…",je({op:"gather",dir:i.dir,url:i.url},(y)=>{let w=y.results&&y.results[0];S(w?w.ok?`${i.label||u(i.url)}: ${w.note}${w.added?" in the icebox":""}`:`Failed: ${w.note}`:"Checked")})}}),t("pbody").querySelectorAll("[data-ag]").forEach((s)=>{let i=d[+s.dataset.ag];s.onclick=()=>{if(s.disabled=!0,P)s.innerHTML=j("refresh")+" Gathering…";je({op:"agent",dir:i.dir,url:i.url},(y)=>{let w=y.results&&y.results[0];S(y.via==="chatgpt"?w&&w.ok?`${i.label||u(i.url)}: ${w.note}${w.added?" in the icebox":""}`:`Failed: ${w?w.note:"no answer"}`:"/kaizen gather opened in your terminal: "+ke(y.agent))})}}),t("pbody").querySelectorAll("[data-rm]").forEach((s)=>{let i=d[+s.dataset.rm];s.onclick=()=>{if(!confirm(`Remove ${i.label||u(i.url)}? What it gathered stays; added again later, it may bring back ones you deleted or dismissed.`))return;je({op:"remove",dir:d[+s.dataset.rm].dir,url:d[+s.dataset.rm].url},()=>S("Source removed"))}});let Xt=b.findIndex((s)=>I(s)===re);if(r){let s=b.findIndex((i)=>I(i)===r[0]);(s>=0?t(r[1]+"-"+s):Xt>=0?t("ice-o-"+Xt):null)?.focus()}let tt=!r&&n[2]&&t(n[2]);if(tt)(tt.disabled?t(tt.id.replace("src-mb-","src-mv-").replace("src-cs-","src-ct-")):tt)?.focus();if(t("src-add"))t("src-add").onclick=()=>{let s=Ee==="social",i=t("src-url").value.trim(),y=t("src-q").value.trim(),w=t("src-plat").value;if(s?!y:!i){t(s?"src-q":"src-url").focus();return}let q=t("src-dir").value;if(!q){t("src-dir").focus();return}t("src-add").disabled=!0,t("src-add").textContent="Adding…",(async()=>{let z=t("src-label").value.trim()||(s?`${lt[w]}: ${y}`:""),ee=t("src-cat").value.trim(),de=await A("/sources",s?{op:"add",dir:q,platform:w,query:y,label:z,category:ee}:{op:"add",dir:q,url:i,label:z,category:ee});if(de.ok&&s)t("src-q").value=t("src-label").value=t("src-cat").value="",S("Source added. Click Gather on it to have your agent read its posts."),D();else if(de.ok){t("src-url").value=t("src-label").value=t("src-cat").value="";let Zt=await A("/sources",{op:"gather",dir:q,url:i}),nt=Zt.results&&Zt.results[0];S(nt?nt.ok?`Source added: ${nt.note} in the icebox`:`Source added, but: ${nt.note}`:"Source added"),D()}else S(de.error||"Could not add source");if(de.ok)Me=!1;if(B())R()})()},t("src-url").onkeydown=t("src-q").onkeydown=t("src-label").onkeydown=t("src-cat").onkeydown=(s)=>{if(s.key==="Enter")t("src-add").click()};if(t("src-go"))t("src-go").onclick=()=>{t("src-go").disabled=!0,t("src-go").innerHTML=j("refresh")+" Gathering…",je({op:"gather",dir:k||void 0},(s)=>{let i=s.results||[],y=(ee)=>d.some((de)=>de.url===ee.url&&de.social),w=i.reduce((ee,de)=>ee+de.added,0),q=i.filter((ee)=>!ee.ok&&!y(ee)).length,z=i.filter(y).length;S((w?`${w} new in the icebox`:"Nothing new")+(q?`, ${q} source${q===1?"":"s"} could not be read`:"")+(z?`; ${z} social for /kaizen gather`:""))})}}var At=new URLSearchParams(location.search).has("all");function Pe(e){if(At)return;try{e?localStorage.setItem("kz-proj",e):localStorage.removeItem("kz-proj")}catch(n){}}var Tt=(e)=>e.split(/[\\/]/).pop()||e;function mt(){let e=t("projq").value.trim().toLowerCase(),n=0;t("projs").innerHTML=a.PL.map((l,r)=>l[0].toLowerCase().includes(e)?(l[2]&&!n++?'<div class="grp">In your projects folder</div>':"")+`<button role="option" data-i="${r}" title="${m(l[0])}">${m(Tt(l[0]))}</button>`:"").join("")||'<div class="grp">No match</div>';let o=t("projs").querySelector("button");if(o)o.classList.add("on")}function pt(e){if(t("projpop").hidden=!e,t("proj").setAttribute("aria-expanded",e),e)t("projq").value="",mt(),t("projq").focus()}async function ns(e){if(e==="__find"){S("Searching…");let n=await A("/find",{});if(n.ok)S(n.added?`Found ${n.added} new project${n.added===1?"":"s"}`:"No new projects found")}else if(e==="all")a.all=!0,Pe();else if((await A("/cd",{dir:e})).ok)a.all=!1,Pe(e);if(await D(),_==="sources")R()}var Le=null;function vn(e){let n=ss(),o=e.querySelector("#newdir"),l=e.querySelector("#ndc .menu-pop"),r=l.querySelector("input"),c=e.querySelector("#nds");if(!n.some((h)=>h.dir===Le))Le=n[0]?.dir??null;let f=()=>{o.textContent=(n.find((h)=>h.dir===Le)||{label:"Choose project"}).label},k=()=>{let h=r.value.trim().toLowerCase();c.innerHTML=n.filter((u)=>u.label.toLowerCase().includes(h)).map((u)=>`<button type="button" role="option" data-d="${m(u.dir)}" title="${m(u.label)}" class="${u.dir===Le?"on":""}">${m(u.label)}</button>`).join("")||'<div class="grp">No match</div>',c.querySelectorAll("button").forEach((u)=>u.onclick=()=>{Le=u.dataset.d,f(),v(!1),e.querySelector("#newidea").focus()})},v=(h)=>{if(l.hidden=!h,o.setAttribute("aria-expanded",h),h)r.value="",k(),r.focus()};o.onclick=(h)=>{h.preventDefault(),v(l.hidden)},r.oninput=k,r.onkeydown=(h)=>{if(h.key==="Escape")h.stopPropagation(),v(!1);if(h.key==="Enter")h.preventDefault(),c.querySelector("button")?.click()},f()}function ss(){let e=qe(),n=e.filter((o)=>a.shown.has(o.dir));return n.length?n:e}function qe(){let e=[...a.S.projects];for(let n of a.S.cards)if(!e.some((o)=>o.dir===n.state))e.push({dir:n.state,label:n.where});return e}var jt=[];function ft(){let e=t("showq").value.trim().toLowerCase();jt=qe(),t("shows").innerHTML=jt.map((n,o)=>n.label.toLowerCase().includes(e)?`<label title="${m(n.label)}"><input type="checkbox" data-i="${o}" ${a.shown.has(n.dir)?"checked":""}>${m(n.label)}</label>`:"").join("")||'<div class="grp">No match</div>'}function ut(){try{localStorage.setItem("kz-show",JSON.stringify([...a.shown]))}catch(e){}if(E(),B())R()}function kn(){t("proj").onclick=()=>pt(t("projpop").hidden),t("projq").oninput=mt,t("projq").onkeydown=(e)=>{let n=[...t("projs").querySelectorAll("button")],o=n.findIndex((l)=>l.classList.contains("on"));if(e.key==="Escape")pt(!1),t("proj").focus();else if(e.key==="Enter"){if(n[o])n[o].click()}else if((e.key==="ArrowDown"||e.key==="ArrowUp")&&n[o]){e.preventDefault();let l=n[(o+(e.key==="ArrowDown"?1:n.length-1))%n.length];n[o].classList.remove("on"),l.classList.add("on"),l.scrollIntoView({block:"nearest"})}},t("projs").onclick=(e)=>{let n=e.target.closest("button");if(n)pt(!1),ns(a.PL[n.dataset.i][1])},document.addEventListener("click",(e)=>{if(!e.target.closest("#combo"))pt(!1)}),t("show").onclick=()=>{let e=t("showpop").hidden;if(t("showpop").hidden=!e,t("show").setAttribute("aria-expanded",e),e)t("showq").value="",ft(),t("showq").focus()},t("showq").oninput=ft,t("showq").onkeydown=(e)=>{if(e.key==="Escape")t("showpop").hidden=!0,t("show").setAttribute("aria-expanded",!1),t("show").focus()},t("shows").onchange=(e)=>{let n=e.target.dataset.i,o=jt[n]?.dir;if(!o)return;e.target.checked?a.shown.add(o):a.shown.delete(o),ut(),t("shows").querySelector(`[data-i="${n}"]`)?.focus()},t("showall").onclick=()=>{a.shown.clear(),ut()},document.addEventListener("click",(e)=>{if(!e.target.closest("#showc"))t("showpop").hidden=!0,t("show").setAttribute("aria-expanded",!1)}),t("newtop").onclick=()=>{a.mode="new",E(),t("board").scrollTo(0,0)}}var Re=null,Et=6,ht=new Set,Ye=(()=>{try{return JSON.parse(localStorage.getItem("kz-order"))||{}}catch(e){return{}}})(),ne=!1,Qe=!1,le=new Set,C=(e)=>e._k||(e.kind==="idea"?"idea:"+e.state+":"+e.text:"run:"+e.state+":"+e.id);function os(e){let n=C(e),o=document.createElement("div");o.className="card"+(n===a.openKey?" open":"")+(n===a.menuKey?" menu":"")+(e.archived?" archived":""),o.tabIndex=0,o.dataset.k=n,o.setAttribute("role","button");let l=at[e.status]||"idea",r=e.kind==="idea"?"idea":e.text,c=e.status==="waiting"?Ge(e.awaiting)||"needs you":e.status||"idea",f=[],k=[],v=(g,p,d="")=>`<span class="mk${d}" role="img" title="${m(p)}" aria-label="${m(p)}"><svg><use href="#${g}"/></svg></span>`;if(a.all){let g=e.where.split("/").filter(Boolean).pop()||e.where;f.push(`<span class="chip proj" title="${m(e.where)}">${m(g)}</span>`)}for(let g of e.tags||[])if(ln[g])k.push(v("c-"+g,g==="full"?"Full run: separate reviewer":g==="lite"?"Lite run: one session":xt[g]||g,g==="full"||g==="lite"?" accent":""));else if(g==="yolo")k.push('<span class="mk warn" title="Started in Yolo: the agent runs commands without asking">yolo</span>');else f.push(`<span class="chip">${m(xt[g]||g)}</span>`);if(e.pty&&e.pty.alive)k.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(h)f.push(`<span class="chip" title="From ${m(h[1])}">gathered</span>`);else if(e.notes)f.push('<span class="chip">notes</span>');if(e.git)k.push(v("c-git",e.git==="pushed"?"Committed & pushed":"Not committed & pushed yet",e.git==="pushed"?" ok":" warn"));let u=Qe&&!ne&&e.column===4;if(o.innerHTML=(u?`<input type="checkbox" class="pick" ${le.has(n)?"checked":""} aria-label="Select">`:"")+`<div class="c-id"><span class="st d-${l}" role="img" title="${m(c)}" aria-label="${m(c)}"><svg><use href="#s-${l}"/></svg></span><span class="idt">${m(r)}</span><span class="age">${e.kind==="run"?rn(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="${n===a.menuKey}">${j("more")}</button></div>
20
+ <p class="snone" id="snone" hidden></p>`,qt();let h=[...t("pbody").querySelectorAll(".sg")],u=[...t("pbody").querySelectorAll(".sjump button")],g=()=>{let p=a.setQ.trim().toLowerCase(),d=!1;h.forEach((b,x)=>{let T=b.nextElementSibling,R=!1;for(;T&&T.matches(".srow");T=T.nextElementSibling)T.hidden=!!p&&!T.dataset.q.includes(p),R||=!T.hidden;if(b.hidden=!R,u[x])u[x].hidden=!R;d||=R}),t("snone").hidden=d,t("snone").textContent=`No setting matches “${a.setQ.trim()}”.`};if(u.forEach((p,d)=>p.onclick=()=>{let b=t("pbody");b.scrollTo({top:b.scrollTop+h[d].getBoundingClientRect().top-b.querySelector(".sq").getBoundingClientRect().bottom-8,behavior:"smooth"})}),t("sq").oninput=(p)=>{a.setQ=p.target.value,g()},t("sq").onkeydown=(p)=>{if(p.key!=="Escape")return;if(p.preventDefault(),a.setQ)a.setQ="",p.target.value="",g();else Q()},g(),c){let p=t("sq");p.focus(),p.setSelectionRange(p.value.length,p.value.length)}if(t("th").onchange=(p)=>setTheme(p.target.value),t("nt").onclick=()=>{ue(),setTimeout(Se,300)},o)o.rows.forEach((p)=>{let d=t("s-"+p.key.replace(/\./g,"-")),b=async(x)=>{let T=document.activeElement?.id;if((await A("/settings",{key:p.key,value:x})).ok)S("Saved");if(await Se(),T?.startsWith("s-preset-"))t(T)?.focus()};if(d.tagName==="SELECT"||d.tagName==="INPUT")d.onchange=async()=>{if(await b(d.value.trim()),d.tagName==="INPUT")D()};else d.querySelectorAll("[data-v]").forEach((x)=>x.onclick=()=>b(x.dataset.v));if(d.matches(".chk"))d.onclick=()=>b(d.dataset.v);if(p.folder)t(d.id+"-pick").onclick=async()=>{let x=await A("/pick-folder",{});if(x.path)await b(x.path),D()}})}var _="board",Lt=(e)=>{document.querySelectorAll(".rail a.it").forEach((n)=>n.dataset.view===e?n.setAttribute("aria-current","page"):n.removeAttribute("aria-current")),document.querySelector(".hdr h1").textContent=e==="sources"?"Icebox":"Board"};function ie(e,n){if(a.pageGen++,e==="icebox")e="sources";if(!["settings","sources"].includes(e))e="board";let o=_;if(_=e,Lt(e),document.body.classList.toggle("paged",e!=="board"),document.body.classList.toggle("ice",e==="sources"),n&&e!==o)history.pushState(null,"",e==="board"?location.pathname:"?view="+(e==="sources"?"icebox":e));let l=t("panel");if(e==="board"){if(o==="board")return;l.style.transition="none",l.classList.remove("open","docked"),l.offsetWidth,l.style.transition="",Q();return}if(l.classList.add("docked"),e==="settings")Se();else P()}function bn(){document.querySelectorAll(".rail a[data-view]").forEach((e)=>{if(e.dataset.view==="notes")return;e.onclick=(n)=>{if(n.ctrlKey||n.metaKey||n.shiftKey||n.button)return;if(n.preventDefault(),e.dataset.view==="settings")a.setQ="";ie(e.dataset.view,!0)}}),document.querySelector(".rail .home").onclick=(e)=>{if(e.ctrlKey||e.metaKey||e.shiftKey||e.button)return;e.preventDefault(),ie("board",!0)},addEventListener("popstate",()=>ie(new URLSearchParams(location.search).get("view"))),addEventListener("kz-theme",()=>{if(_==="settings")Se()})}var yn="Icebox",X=null,_e={},Ct=(e)=>{if(!_e[e])try{_e[e]=localStorage.getItem("kz-reply-style:"+e)}catch(n){}return _e[e]||"casual"},ns={casual:"Casual",fun:"Fun",helpful:"Helpful",promo:"Promo"},Z=null,re=null,me="",te="",Ee="link",We=null,Me=!1,ct=!1,dt=null,F=new Set,lt={x:"X",tiktok:"TikTok",instagram:"Instagram",threads:"Threads"},B=()=>t("panel").classList.contains("open")&&t("ptitle").textContent===yn,De=(e)=>{let n=Math.round((Date.now()-Date.parse(e))/60000);return n<1?"just now":n<60?n+" min ago":n<1440?Math.round(n/60)+" h ago":Math.round(n/1440)+" d ago"};async function P(){if(_!=="sources")return;let e=++a.pageGen;ae(!0);let n=B()?[t("src-url")?.value||"",t("src-label")?.value||"",document.activeElement?.id,t("src-dir")?.value,t("src-cat")?.value||"",t("src-plat")?.value,t("src-q")?.value||""]:["",""],o=new Map(B()?[...t("pbody").querySelectorAll("[data-mv]")].map((s)=>[s.dataset.k,s.value]):[]),l=B()&&document.activeElement?.closest?.(".ice-row"),r=l?[l.dataset.k,document.activeElement.id.replace(/-\d+$/,"")]:null,c=B()&&t("ice-title")?[t("ice-title").dataset.k,t("ice-title").value,t("ice-details").value]:null,f=new Map(B()?[...t("pbody").querySelectorAll("[data-ct]")].map((s)=>[s.dataset.k,s.value]):[]);if(!B())t("pbody").innerHTML='<p class="snone">Loading…</p>';fe(),a.openKey=null,a.runDoc=null,t("pterm").hidden=!0,t("panel").classList.add("open"),t("scrim").classList.add("on"),t("ptitle").textContent=yn,t("ptabs").innerHTML="",t("pfoot").innerHTML="",t("psub").textContent="",t("psub").title="";let k=a.all?null:a.S.dir,v=k?"?dir="+encodeURIComponent(k):"",h=await fetch("/sources"+v).then((s)=>s.json()).catch(()=>null);if(e!==a.pageGen||!B())return;let u=(s)=>{try{return new URL(s).hostname}catch{return s}},g=(s,i,y)=>{if(lt[y])return`<span class="fav">${j("b-"+y)}</span>`;let w;try{let z=new URL(s);if(/^https?:$/.test(z.protocol))w=z.origin}catch{}let q=!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">${q?`<img src="${m(q)}" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.remove()">`:""}${j(i)}</span>`},p=(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"},d=h&&h.sources||[],b=[...h&&h.icebox||[],...h&&h.dismissed||[]],x=(h&&h.icebox||[]).length,T=h&&h.gatherer||{},R=T.use==="chatgpt"&&T.connected,H=(s,i)=>(s&&!a.S.projects.some((y)=>y.dir===s)?`<option value="${m(s)}" selected>${m(i||s)}</option>`:"")+a.S.projects.map((y)=>`<option value="${m(y.dir)}"${y.dir===s?" selected":""}>${m(y.label)}</option>`).join(""),L=d.map((s,i)=>{let y=p(s),w=s.label||u(s.url),q=s.social&&(!s.last||!s.last.ok&&/gathered by your agent/i.test(s.last.note))?`<span class="src-badge dim">${j("at")}${R?"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>${m(s.last.note)}</span><span class="hint" style="margin:0">${De(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: ${m(s.last.note)}</span><span class="hint" style="margin:0">${De(s.last.at)}</span>`;return`<div class="src-card" id="src-card-${i}"><div class="src-hd"><div class="src-info"><span class="src-ico" title="${y==="social"?m(lt[s.social]||"social"):y}">${g(s.url,y==="sheet"?"sheet":y==="doc"?"doc":y==="social"?"at":"link",s.social)}</span><span class="src-title" title="${m(w)}">${m(w)}</span></div><div class="src-acts">${s.social?`<button class="btn ghost sm" id="src-ag-${i}" data-ag="${i}" aria-label="Gather ${m(w)} with your agent" title="${R?"Read this source with your ChatGPT plan":"Open your agent to run /kaizen gather for this project"}">${j("refresh")} Gather</button>`:`<button class="btn ghost sm" id="src-chk-${i}" data-chk="${i}" aria-label="Check ${m(w)} now" title="Check this source now">${j("refresh")} Check</button>`}<button class="btn ghost danger sm" id="src-rm-${i}" data-rm="${i}" aria-label="Remove ${m(w)}" title="Remove source">${j("trash")}</button></div></div><div class="src-url"><a href="${m(s.url)}" target="_blank" rel="noopener noreferrer" title="${m(s.url)}"><span>${m(s.url)}</span>${j("ext")}</a></div><div class="src-status">${q}</div><div class="src-dest"><span>Category</span><input class="sel" id="src-ct-${i}" data-ct="${i}" data-k="${m(s.dir+" "+s.url)}" value="${m(s.category||"")}" placeholder="e.g. Feedback" maxlength="40" aria-label="Category of ${m(w)}"><button class="btn sm" id="src-cs-${i}" aria-label="Save the category of ${m(w)}" disabled>Save</button></div><div class="src-dest"><span>Project</span><select class="sel" id="src-mv-${i}" data-mv="${i}" data-k="${m(s.dir+" "+s.url)}" aria-label="Project of ${m(w)}">${H(s.dir,s.project)}</select><button class="btn sm" id="src-mb-${i}" aria-label="Move ${m(w)} to the chosen project" disabled>Move</button></div></div>`}).join(""),K=(h&&h.failed||[]).map((s)=>`<p style="color:var(--dim)">The sources of ${m(s)} could not be read.</p>`).join(""),G=`<div class="src-empty"><div class="src-empty-ico">${j("link")}</div><h4>No request sources yet</h4><p>Connect shared links to gather requests into the icebox, where you choose which become ideas. Nothing runs automatically until you start it.</p><div class="src-empty-grid"><div class="src-empty-item"><strong>Google Sheets</strong>Public sheets with idea or task rows. Rows marked Done are skipped.</div><div class="src-empty-item"><strong>Google Docs</strong>Shared public docs with items or notes written line by line.</div><div class="src-empty-item"><strong>Public links</strong>TSV, CSV, or raw text links containing requests.</div><div class="src-empty-item"><strong>Social media</strong>A keyword, #hashtag or @account on X, TikTok, Instagram or Threads, read by your agent.</div></div></div>`,N=Ee==="social",W=!d.length||Me||[n[0],n[1],n[4],n[6]].some(Boolean),ce=`<div class="src-add" id="src-addbox"${W?"":" 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="${!N}">Link</button><button id="src-k-social" data-kind="social" aria-pressed="${N}">Social</button></div></div><label for="src-url"${N?" hidden":""}><span>Link</span><input class="sel" id="src-url" placeholder="https://docs.google.com/spreadsheets/d/… or any public link" spellcheck="false" autocomplete="off"></label><div class="two plat"${N?"":" hidden"}><label for="src-plat"><span>Platform</span><select class="sel" id="src-plat">${Object.entries(lt).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="${N?"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="${N?"e.g. Social":"e.g. Bugs"}" maxlength="40"></label></div><label for="src-dir"><span>Project</span><select class="sel" id="src-dir">${k?"":'<option value="">Choose project…</option>'}${H(k)}</select></label><div class="acts"><span class="hint">${N?R?"Your ChatGPT plan reads public posts when you press Gather on the source.":"Kaizen signs in nowhere. Your agent reads public posts when you press Gather on the source.":"Public links only. For private docs, run /kaizen gather."}</span><button class="btn pri" id="src-add">Add source</button></div></div>`,$e=(s)=>{let i=d.find((y)=>y.dir===s.dir&&y.url===s.source);return i?i.label||u(i.url):u(s.source)},I=(s)=>s.dir+" "+s.id,Ie=(s)=>s.dir+" "+s.source,ge=(s)=>s.status==="dismissed"?"Dismissed":s.category||"Uncategorised",M=(s,i)=>{let y=new Map;return s.forEach((w)=>{let q=i(w);y.set(q,{it:w,n:(y.get(q)?.n||0)+1})}),y},U=k?new Map:M(b,(s)=>s.dir);if(!k){for(let s of a.shown)if(!U.has(s)){let i=qe().find((y)=>y.dir===s);if(i)U.set(s,{it:{dir:s,project:i.label},n:0})}}let ee=(s)=>k||!a.shown.size||a.shown.has(s.dir),be=b.filter(ee),se=M(be,ge);if(te&&!se.has(te))te="";let In=be.filter((s)=>!te||ge(s)===te),kt=M(In,Ie);if(me&&!kt.has(me))me="";let Hn=new Set(b.map(I));if(Z&&!Hn.has(Z))Z=null;let He=new Map;b.forEach((s,i)=>{if(!ee(s)||(te?ge(s)!==te:s.status==="dismissed")||me&&Ie(s)!==me)return;let y=ge(s);He.has(y)||He.set(y,[]),He.get(y).push(i)});let O=[...He.values()].flat();{let s=new Set(O.map((i)=>I(b[i])));[...F].forEach((i)=>s.has(i)||F.delete(i))}if(!O.some((s)=>I(b[s])===re))re=O.length?I(b[O[0]]):null;let Oe=O.filter((s)=>F.has(I(b[s]))).length,On=U.size>1,Je=(s,i,y)=>`<option value="${m(s)}"${i?" selected":""}>${m(y)}</option>`,Wt=(s,i,y)=>`<select class="sel ice-sel" id="${s}" aria-label="${i}">${y.join("")}</select>`,zn=(se.size>1?Wt("ice-fc","Category",[Je("",!te,"All categories"),...[...se.keys()].map((s)=>Je(s,te===s,s))]):"")+(kt.size>1?Wt("ice-fs","Source",[Je("",!me,"All sources"),...[...kt].map(([s,{it:i}])=>Je(s,me===s,$e(i)))]):""),Un=`<div class="ice-bar">${O.length?`<input type="checkbox" id="ice-all" aria-label="Select every request shown"${Oe&&Oe===O.length?" checked":""}>`:""}<span class="count">${te==="Dismissed"?`${O.length} dismissed`:`${O.length<x?`${O.length} of ${x}`:x} waiting`}</span>${zn}<span class="end"></span><button class="btn sm" id="src-go"${d.length?"":" disabled"}>${j("refresh")} Gather all</button><button class="btn ghost sm" id="src-tog" aria-expanded="${We??!d.length}" aria-controls="src-sec">${j("link")} Sources · ${d.length}</button></div>`,_t=F.size?`<div class="ice-bulk" role="toolbar" aria-label="Selected requests"><span>${F.size} selected</span><span class="end"></span><button class="btn sm" id="ice-bp" title="Each becomes its own idea, titled as gathered (p)">${j("plus")} Promote</button><button class="btn ghost sm" id="ice-bd" title="Dismiss (d)">${j("x")} Dismiss</button><button class="btn ghost sm" id="ice-bc" title="Clear selection (Esc)">Clear</button></div>`:"",Gn=(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="${m(I(s))}" maxlength="200" value="${m(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" placeholder="What, for whom, why" aria-label="Details"></textarea><div class="ice-form-acts"><span class="hint">Source goes into the notes.</span><button class="btn ghost sm" id="ice-cancel">Cancel</button><button class="btn pri sm" id="ice-ok">Promote</button></div></div>`,Fn=(s)=>{if(!s.post)return"";if(s.replied)return`<span class="hint" style="margin:0">You replied to this post ${De(s.replied)}.</span>`;let i=X&&X.k===I(s)?X:null,y=(w)=>`<select class="sel" id="ice-rst" aria-label="Reply style" title="Promo names kaizen; Helpful may mention it when it fits; Casual and Fun never do"${w?" disabled":""}>${Object.entries(ns).map(([q,z])=>`<option value="${q}"${q===Ct(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?y(!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?` · ${m(i.note)}`:""}</label><textarea class="sel" id="ice-reply" rows="3" maxlength="2000" aria-label="Your reply"${i.busy||i.again?" disabled":""}>${m(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?`${y(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>`},Bn=(s)=>{let i=b[s],y=I(i),w=Z===y;return`<div class="ice-row${re===y?" cur":""}${F.has(y)?" sel":""}" data-i="${s}" data-k="${m(y)}"><input type="checkbox" id="ice-ck-${s}" data-ck="${s}"${F.has(y)?" checked":""} aria-label="Select: ${m(i.text)}"><span class="ice-src" title="${m($e(i))}">${g(i.source,"link",d.find((q)=>q.dir===i.dir&&q.url===i.source)?.social)}</span><button class="ice-t" id="ice-o-${s}" data-o="${s}" aria-expanded="${w}" title="${m(i.text)}">${m(i.text)}</button><span class="ice-meta">${On?`<span class="nm">${m(i.project||"")}</span>`:""}${i.replied?`<span title="Replied ${m(De(i.replied))}">replied</span>`:""}<span class="ice-age">${De(i.at)}</span><span class="ice-ra"><button class="btn ghost sm" id="ice-pr-${s}" data-pr="${s}" title="Promote to Ideas as gathered (p)" aria-label="Promote to Ideas: ${m(i.text)}">${j("plus")}</button>${i.status==="dismissed"?`<button class="btn ghost sm" id="ice-rs-${s}" data-rs="${s}" title="Restore to waiting" aria-label="Restore: ${m(i.text)}">Restore</button>`:`<button class="btn ghost sm" id="ice-ds-${s}" data-ds="${s}" title="Dismiss (d)" aria-label="Dismiss: ${m(i.text)}">${j("x")}</button>`}</span></span></div>`+(w?`<div class="ice-open">${i.notes?`<div class="ice-notes">${m(i.notes)}</div>`:""}${(i.images||[]).filter((q)=>/^https:\/\//.test(q)).length?`<details class="ice-imgs"><summary>Show images (${i.images.filter((q)=>/^https:\/\//.test(q)).length}) — loads them from the source's site</summary>${i.images.filter((q)=>/^https:\/\//.test(q)).map((q,z)=>{let V=`/ice-image?dir=${encodeURIComponent(i.dir)}&id=${encodeURIComponent(i.id)}&i=${z}`;return`<a href="${m(V)}" target="_blank" rel="noopener noreferrer"><img data-src="${m(V)}" alt="" referrerpolicy="no-referrer"></a>`}).join("")}</details>`:""}<span class="hint" style="margin:0">From ${m($e(i))} · ${De(i.at)}${i.post?` · <a href="${m(i.post)}" target="_blank" rel="noopener">open post</a>`:""}</span>${Fn(i)}${Gn(i)}</div>`:"")},Wn=x&&!O.length?`<div class="ice-zero"><h4>Nothing matches</h4><p>${x} waiting outside these filters.</p><p style="margin-top:10px"><button class="btn sm" id="ice-unf">Clear filters</button></p></div>`:O.length?`<div class="ice-list">${[...He].map(([s,i])=>`<div class="ice-cat">${m(s)}<i>${i.length}</i></div>${i.map(Bn).join("")}`).join("")}</div>`:'<div class="ice-zero"><h4>Inbox zero</h4><p>Nothing waiting. Gather all checks your sources for new requests; /kaizen discover in your agent sorts what comes in.</p></div>',Xe=We??!d.length,wt=Xe&&!t("src-sec")?.classList.contains("open"),_n=`<div class="src-scrim${Xe&&!wt?" on":""}" id="src-scrim"></div><aside class="src-sec${Xe&&!wt?" open":""}" id="src-sec" aria-label="Sources"${Xe?"":" inert"}><header><div class="t"><b>Sources</b><div class="sub">${h&&h.every&&h.every!=="off"?`Auto-check every ${m(h.every)}`:"Auto-check off"} · <a id="src-to-settings" role="button" tabindex="0">Settings</a> · ${T.connected?`ChatGPT${T.email?" "+m(T.email):""} <a id="cg-out" role="button" tabindex="0">Disconnect</a>`:'<a id="cg-in" role="button" tabindex="0">Connect ChatGPT</a>'}</div></div><div class="src-hda">${d.length?`<button class="btn sm" id="src-new" aria-expanded="${W}" aria-controls="src-addbox">${j("plus")} Add</button>`:""}<button class="ib" id="src-x" aria-label="Close sources" title="Close">${j("close")}</button></div></header><div class="body">${ce}${d.length?`<div class="src-list">${L}</div>`:G}</div></aside>`,Yn=T.use==="ask"&&!T.connected?'<div class="cg-onb"><b>Gather social posts with your ChatGPT plan</b><p>Connect ChatGPT Plus or Pro and Gather reads X, TikTok, Instagram and Threads sources right here, counted against your plan, with no agent to open. Skip it and Gather keeps using your agent. You can change this in Settings.</p><div class="acts"><button class="btn ghost" id="cg-skip">Skip for now</button><button class="btn pri" id="cg-go">Continue with ChatGPT</button></div></div>':"";if(t("pbody").innerHTML=h&&h.error?`<p style="color:var(--dim)">${m(h.error)}</p>`:!h?'<p style="color:var(--dim)">The icebox could not be read.</p>':Yn+Un+_t+K+Wn+_n,!h||h.error)return;if(c&&c[0]===Z&&t("ice-title"))t("ice-title").value=c[1],t("ice-details").value=c[2];if(t("src-add")){if(n[5])t("src-plat").value=n[5];if(t("src-q").value=n[6]||"",t("src-url").value=n[0],t("src-label").value=n[1],t("src-cat").value=n[4]||"",n[3]!==void 0&&t("src-dir"))t("src-dir").value=n[3]}if(t("src-to-settings"))t("src-to-settings").onclick=()=>{a.setQ="sources",ie("settings",!0)};let Qn=async()=>{let s=window.open("","_blank"),i=await A("/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(t(s))t(s).onclick=Qn;if(t("cg-skip"))t("cg-skip").onclick=async()=>{if((await A("/chatgpt",{op:"skip"})).ok)S("Gather keeps using your agent"),P()};if(t("cg-out"))t("cg-out").onclick=async()=>{if(!confirm("Disconnect ChatGPT? Gather goes back to your agent."))return;if((await A("/chatgpt",{op:"disconnect"})).ok)S("ChatGPT disconnected"),P()};if(wt)t("src-sec").offsetWidth,t("src-sec").classList.add("open"),t("src-scrim").classList.add("on");let Ze=()=>{We=!1,t("src-sec").classList.remove("open"),t("src-scrim").classList.remove("on"),t("src-sec").inert=!0,t("src-tog").setAttribute("aria-expanded","false"),t("src-tog").focus()};if(t("src-x").onclick=t("src-scrim").onclick=Ze,t("src-new"))t("src-new").onclick=()=>{let s=t("src-addbox");if(Me=s.hidden,s.hidden=!Me,t("src-new").setAttribute("aria-expanded",Me),Me)t(Ee==="social"?"src-q":"src-url").focus()};if(t("src-sec").onkeydown=(s)=>{if(s.key==="Escape")s.stopPropagation(),Ze()},t("src-tog").onclick=()=>{if(t("src-sec").classList.contains("open"))return Ze();We=!0,P().then(()=>We&&t("src-url")?.focus())},t("ice-fs"))t("ice-fs").onchange=(s)=>{me=s.target.value,P()};if(t("ice-fc"))t("ice-fc").onchange=(s)=>{te=s.target.value,P()};if(t("ice-unf"))t("ice-unf").onclick=()=>{if(te=me="",!k)a.shown.clear();ut(),P()};t("pbody").querySelectorAll("[data-kind]").forEach((s)=>s.onclick=()=>{if(Ee===s.dataset.kind)return;Ee=s.dataset.kind,P().then(()=>t(Ee==="social"?"src-q":"src-url")?.focus())});let je=async(s,i)=>{let y=await A("/sources",s);if(y.ok)i(y),D();if(B())P()};t("pbody").querySelectorAll("[data-mv]").forEach((s)=>{let i=d[+s.dataset.mv],y=t("src-mb-"+s.dataset.mv),w=o.get(s.dataset.k);if(w&&[...s.options].some((q)=>q.value===w))s.value=w;(s.onchange=()=>{y.disabled=s.value===i.dir})(),y.onclick=()=>je({op:"move",dir:i.dir,to:s.value,url:i.url},()=>S(`Source moved. Requests already gathered stay in ${i.project||"the old project"}.`))}),t("pbody").querySelectorAll("[data-ct]").forEach((s)=>{let i=d[+s.dataset.ct],y=t("src-cs-"+s.dataset.ct),w=f.get(s.dataset.k);if(w!==void 0)s.value=w;(s.oninput=()=>{y.disabled=s.value.trim()===(i.category||"")})(),s.onkeydown=(q)=>{if(q.key==="Enter"&&!y.disabled)y.click()},y.onclick=()=>{y.disabled=!0,je({op:"category",dir:i.dir,url:i.url,category:s.value.trim()},()=>S(s.value.trim()?`New requests from ${i.label||u(i.url)} go under ${s.value.trim()}`:"Category cleared"))}});let Yt=()=>{if(!document.activeElement||document.activeElement===document.body)t("pbody").querySelector(".ice-row.cur .ice-t")?.focus()},Qt=(s)=>{let i=O.findIndex((q)=>I(b[q])===re);if(i<0||!s.has(re))return;let y=(q)=>!s.has(I(b[q])),w=O.slice(i+1).find(y)??O.slice(0,i).reverse().find(y);re=w===void 0?null:I(b[w])},et=(s,i,y,w={})=>(async()=>{if(Qt(new Set([I(i)])),(await A("/icebox",{op:s,dir:i.dir,ids:[i.id],...w})).ok){if(s==="promote"&&Z===I(i))Z=null;S(y),D()}if(B())P().then(Yt)})(),Vt=async(s)=>{let i=b.filter((w)=>F.has(I(w)));if(!i.length)return;Qt(new Set(i.map(I))),ct=!0,t("ice-bp").disabled=t("ice-bd").disabled=!0;let y=0;try{if(s==="dismiss")for(let w of new Set(i.map((q)=>q.dir))){let q=i.filter((z)=>z.dir===w).map((z)=>z.id);if((await A("/icebox",{op:s,dir:w,ids:q})).ok)y+=q.length}else for(let w of i)if((await A("/icebox",{op:s,dir:w.dir,ids:[w.id]})).ok)y++}finally{ct=!1}if(F.clear(),y)S(`${y} ${s==="promote"?"promoted to Ideas":"dismissed"}${y<i.length?`, ${i.length-y} not`:""}`);if(D(),B())P().then(Yt)};if(_t)t("ice-bp").onclick=()=>Vt("promote"),t("ice-bd").onclick=()=>Vt("dismiss"),t("ice-bc").onclick=()=>{F.clear(),P()};if(t("ice-all"))t("ice-all").onchange=()=>{let s=O.map((i)=>I(b[i]));if(Oe===O.length)s.forEach((i)=>F.delete(i));else s.forEach((i)=>F.add(i));P()};if(t("ice-all"))t("ice-all").indeterminate=Oe>0&&Oe<O.length;t("pbody").querySelectorAll("[data-ck]").forEach((s)=>s.onchange=()=>{let i=I(b[+s.dataset.ck]);re=i,s.checked?F.add(i):F.delete(i),P()});let Jt=(s)=>{let i=I(b[s]);re=i,Z=Z===i?null:i,P().then(()=>{let y=t("ice-title");if(y&&Z===i)y.focus(),y.select();else t("ice-o-"+s)?.focus()})};t("pbody").querySelectorAll(".ice-row").forEach((s)=>s.onclick=(i)=>{if(!i.target.closest("input,button"))Jt(+s.dataset.i)}),t("pbody").querySelectorAll("[data-o]").forEach((s)=>s.onclick=()=>Jt(+s.dataset.o)),t("pbody").querySelectorAll("details.ice-imgs").forEach((s)=>s.ontoggle=()=>s.open&&s.querySelectorAll("img[data-src]").forEach((i)=>i.src=i.dataset.src)),t("pbody").querySelectorAll("[data-pr]").forEach((s)=>s.onclick=()=>{s.disabled=!0,et("promote",b[+s.dataset.pr],"Promoted to Ideas")}),t("pbody").querySelectorAll("[data-ds]").forEach((s)=>s.onclick=()=>{s.disabled=!0,et("dismiss",b[+s.dataset.ds],"Dismissed")}),t("pbody").querySelectorAll("[data-rs]").forEach((s)=>s.onclick=()=>{s.disabled=!0,et("restore",b[+s.dataset.rs],"Restored")});let ze=b.find((s)=>I(s)===Z);if(t("ice-rp"))t("ice-rp").onclick=async()=>{let s=ze,i=I(s);X={k:i,text:"",wait:!0},P();let y=await A("/reply",{op:"draft",dir:s.dir,id:s.id,style:Ct(s.dir)});if(!X||X.k!==i)return;X={k:i,text:y.text||"",note:y.ok?y.note||"":"",wait:!1},P().then(()=>t("ice-reply")?.focus())};if(t("ice-rst"))t("ice-rst").onchange=(s)=>{let i=ze.dir;_e[i]=s.target.value;try{localStorage.setItem("kz-reply-style:"+i,_e[i])}catch(y){}};if(t("ice-rg"))t("ice-rg").onclick=async()=>{let s=ze,i=X;i.again=!0,P();let y=await A("/reply",{op:"draft",dir:s.dir,id:s.id,style:Ct(s.dir),avoid:i.text});if(X!==i)return;if(i.again=!1,y.text)i.text=y.text;i.note=y.ok?y.note||"":"",P().then(()=>t("ice-reply")?.focus())};if(t("ice-reply")){let s=t("ice-reply");s.oninput=()=>{X.text=s.value,t("ice-rn").textContent=`${s.value.length} chars`,t("ice-rs").disabled=!s.value.trim()},s.onkeydown=(i)=>{if(i.key==="Escape")i.stopPropagation(),t("ice-rc").click()},t("ice-rc").onclick=()=>{X=null,P()},t("ice-rs").onclick=async()=>{let i=ze,y=X;y.busy=!0,P();let w=await A("/reply",{op:"send",dir:i.dir,id:i.id,text:y.text});if(w.ok)X=null,S("Reply posted");else if(w.sent)X=null;else y.busy=!1;if(D(),B())P()}}if(t("ice-ok")){let s=()=>{let i=t("ice-title").value.trim();if(!i){t("ice-title").focus();return}t("ice-ok").disabled=!0,et("promote",ze,"Promoted to Ideas",{text:i,details:t("ice-details").value})};t("ice-ok").onclick=s,t("ice-cancel").onclick=()=>{Z=null,P()},t("ice-title").onkeydown=(i)=>{if(i.key==="Enter")s();if(i.key==="Escape")t("ice-cancel").click()},t("ice-details").onkeydown=(i)=>{if(i.key==="Enter"&&(i.ctrlKey||i.metaKey))s();if(i.key==="Escape")t("ice-cancel").click()}}dt=(s)=>{if(s.altKey||s.ctrlKey||s.metaKey)return!1;let i=s.key,y=O.findIndex((z)=>I(b[z])===re),w=O[y];if(i==="Escape"){if(t("src-sec")?.classList.contains("open"))return Ze(),!0;if(Z)return Z=null,P(),!0;if(F.size)return F.clear(),P(),!0;return!1}if(y<0)return!1;if(i==="j"||i==="k"){s.preventDefault();let z=O[Math.max(0,Math.min(O.length-1,y+(i==="j"?1:-1)))];return re=I(b[z]),t("pbody").querySelectorAll(".ice-row").forEach((V)=>V.classList.toggle("cur",+V.dataset.i===z)),t("pbody").querySelector(`.ice-row[data-i="${z}"]`)?.scrollIntoView({block:"nearest"}),!0}let q=i==="o"?t("ice-o-"+w):i==="x"?t("ice-ck-"+w):i==="p"?F.size?t("ice-bp"):t("ice-pr-"+w):i==="d"?F.size?t("ice-bd"):t("ice-ds-"+w):null;if(!q||q.disabled)return!1;return s.preventDefault(),q.click(),!0},t("pbody").querySelectorAll("[data-chk]").forEach((s)=>{let i=d[+s.dataset.chk];s.onclick=()=>{s.disabled=!0,s.innerHTML=j("refresh")+" Checking…",je({op:"gather",dir:i.dir,url:i.url},(y)=>{let w=y.results&&y.results[0];S(w?w.ok?`${i.label||u(i.url)}: ${w.note}${w.added?" in the icebox":""}`:`Failed: ${w.note}`:"Checked")})}}),t("pbody").querySelectorAll("[data-ag]").forEach((s)=>{let i=d[+s.dataset.ag];s.onclick=()=>{if(s.disabled=!0,R)s.innerHTML=j("refresh")+" Gathering…";je({op:"agent",dir:i.dir,url:i.url},(y)=>{let w=y.results&&y.results[0];S(y.via==="chatgpt"?w&&w.ok?`${i.label||u(i.url)}: ${w.note}${w.added?" in the icebox":""}`:`Failed: ${w?w.note:"no answer"}`:"/kaizen gather opened in your terminal: "+ke(y.agent))})}}),t("pbody").querySelectorAll("[data-rm]").forEach((s)=>{let i=d[+s.dataset.rm];s.onclick=()=>{if(!confirm(`Remove ${i.label||u(i.url)}? What it gathered stays; added again later, it may bring back ones you deleted or dismissed.`))return;je({op:"remove",dir:d[+s.dataset.rm].dir,url:d[+s.dataset.rm].url},()=>S("Source removed"))}});let Xt=b.findIndex((s)=>I(s)===re);if(r){let s=b.findIndex((i)=>I(i)===r[0]);(s>=0?t(r[1]+"-"+s):Xt>=0?t("ice-o-"+Xt):null)?.focus()}let tt=!r&&n[2]&&t(n[2]);if(tt)(tt.disabled?t(tt.id.replace("src-mb-","src-mv-").replace("src-cs-","src-ct-")):tt)?.focus();if(t("src-add"))t("src-add").onclick=()=>{let s=Ee==="social",i=t("src-url").value.trim(),y=t("src-q").value.trim(),w=t("src-plat").value;if(s?!y:!i){t(s?"src-q":"src-url").focus();return}let q=t("src-dir").value;if(!q){t("src-dir").focus();return}t("src-add").disabled=!0,t("src-add").textContent="Adding…",(async()=>{let z=t("src-label").value.trim()||(s?`${lt[w]}: ${y}`:""),V=t("src-cat").value.trim(),de=await A("/sources",s?{op:"add",dir:q,platform:w,query:y,label:z,category:V}:{op:"add",dir:q,url:i,label:z,category:V});if(de.ok&&s)t("src-q").value=t("src-label").value=t("src-cat").value="",S("Source added. Click Gather on it to have your agent read its posts."),D();else if(de.ok){t("src-url").value=t("src-label").value=t("src-cat").value="";let Zt=await A("/sources",{op:"gather",dir:q,url:i}),nt=Zt.results&&Zt.results[0];S(nt?nt.ok?`Source added: ${nt.note} in the icebox`:`Source added, but: ${nt.note}`:"Source added"),D()}else S(de.error||"Could not add source");if(de.ok)Me=!1;if(B())P()})()},t("src-url").onkeydown=t("src-q").onkeydown=t("src-label").onkeydown=t("src-cat").onkeydown=(s)=>{if(s.key==="Enter")t("src-add").click()};if(t("src-go"))t("src-go").onclick=()=>{t("src-go").disabled=!0,t("src-go").innerHTML=j("refresh")+" Gathering…",je({op:"gather",dir:k||void 0},(s)=>{let i=s.results||[],y=(V)=>d.some((de)=>de.url===V.url&&de.social),w=i.reduce((V,de)=>V+de.added,0),q=i.filter((V)=>!V.ok&&!y(V)).length,z=i.filter(y).length;S((w?`${w} new in the icebox`:"Nothing new")+(q?`, ${q} source${q===1?"":"s"} could not be read`:"")+(z?`; ${z} social for /kaizen gather`:""))})}}var At=new URLSearchParams(location.search).has("all");function Re(e){if(At)return;try{e?localStorage.setItem("kz-proj",e):localStorage.removeItem("kz-proj")}catch(n){}}var Tt=(e)=>e.split(/[\\/]/).pop()||e;function mt(){let e=t("projq").value.trim().toLowerCase(),n=0;t("projs").innerHTML=a.PL.map((l,r)=>l[0].toLowerCase().includes(e)?(l[2]&&!n++?'<div class="grp">In your projects folder</div>':"")+`<button role="option" data-i="${r}" title="${m(l[0])}">${m(Tt(l[0]))}</button>`:"").join("")||'<div class="grp">No match</div>';let o=t("projs").querySelector("button");if(o)o.classList.add("on")}function pt(e){if(t("projpop").hidden=!e,t("proj").setAttribute("aria-expanded",e),e)t("projq").value="",mt(),t("projq").focus()}async function ss(e){if(e==="__find"){S("Searching…");let n=await A("/find",{});if(n.ok)S(n.added?`Found ${n.added} new project${n.added===1?"":"s"}`:"No new projects found")}else if(e==="all")a.all=!0,Re();else if((await A("/cd",{dir:e})).ok)a.all=!1,a.dir=e,Re(e);if(await D(),_==="sources")P()}var Le=null;function vn(e){let n=os(),o=e.querySelector("#newdir"),l=e.querySelector("#ndc .menu-pop"),r=l.querySelector("input"),c=e.querySelector("#nds");if(!n.some((h)=>h.dir===Le))Le=n[0]?.dir??null;let f=()=>{o.textContent=(n.find((h)=>h.dir===Le)||{label:"Choose project"}).label},k=()=>{let h=r.value.trim().toLowerCase();c.innerHTML=n.filter((u)=>u.label.toLowerCase().includes(h)).map((u)=>`<button type="button" role="option" data-d="${m(u.dir)}" title="${m(u.label)}" class="${u.dir===Le?"on":""}">${m(u.label)}</button>`).join("")||'<div class="grp">No match</div>',c.querySelectorAll("button").forEach((u)=>u.onclick=()=>{Le=u.dataset.d,f(),v(!1),e.querySelector("#newidea").focus()})},v=(h)=>{if(l.hidden=!h,o.setAttribute("aria-expanded",h),h)r.value="",k(),r.focus()};o.onclick=(h)=>{h.preventDefault(),v(l.hidden)},r.oninput=k,r.onkeydown=(h)=>{if(h.key==="Escape")h.stopPropagation(),v(!1);if(h.key==="Enter")h.preventDefault(),c.querySelector("button")?.click()},f()}function os(){let e=qe(),n=e.filter((o)=>a.shown.has(o.dir));return n.length?n:e}function qe(){let e=[...a.S.projects];for(let n of a.S.cards)if(!e.some((o)=>o.dir===n.state))e.push({dir:n.state,label:n.where});return e}var jt=[];function ft(){let e=t("showq").value.trim().toLowerCase();jt=qe(),t("shows").innerHTML=jt.map((n,o)=>n.label.toLowerCase().includes(e)?`<label title="${m(n.label)}"><input type="checkbox" data-i="${o}" ${a.shown.has(n.dir)?"checked":""}>${m(n.label)}</label>`:"").join("")||'<div class="grp">No match</div>'}function ut(){try{localStorage.setItem("kz-show",JSON.stringify([...a.shown]))}catch(e){}if(E(),B())P()}function kn(){t("proj").onclick=()=>pt(t("projpop").hidden),t("projq").oninput=mt,t("projq").onkeydown=(e)=>{let n=[...t("projs").querySelectorAll("button")],o=n.findIndex((l)=>l.classList.contains("on"));if(e.key==="Escape")pt(!1),t("proj").focus();else if(e.key==="Enter"){if(n[o])n[o].click()}else if((e.key==="ArrowDown"||e.key==="ArrowUp")&&n[o]){e.preventDefault();let l=n[(o+(e.key==="ArrowDown"?1:n.length-1))%n.length];n[o].classList.remove("on"),l.classList.add("on"),l.scrollIntoView({block:"nearest"})}},t("projs").onclick=(e)=>{let n=e.target.closest("button");if(n)pt(!1),ss(a.PL[n.dataset.i][1])},document.addEventListener("click",(e)=>{if(!e.target.closest("#combo"))pt(!1)}),t("show").onclick=()=>{let e=t("showpop").hidden;if(t("showpop").hidden=!e,t("show").setAttribute("aria-expanded",e),e)t("showq").value="",ft(),t("showq").focus()},t("showq").oninput=ft,t("showq").onkeydown=(e)=>{if(e.key==="Escape")t("showpop").hidden=!0,t("show").setAttribute("aria-expanded",!1),t("show").focus()},t("shows").onchange=(e)=>{let n=e.target.dataset.i,o=jt[n]?.dir;if(!o)return;e.target.checked?a.shown.add(o):a.shown.delete(o),ut(),t("shows").querySelector(`[data-i="${n}"]`)?.focus()},t("showall").onclick=()=>{a.shown.clear(),ut()},document.addEventListener("click",(e)=>{if(!e.target.closest("#showc"))t("showpop").hidden=!0,t("show").setAttribute("aria-expanded",!1)}),t("newtop").onclick=()=>{a.mode="new",E(),t("board").scrollTo(0,0)}}var Pe=null,Et=6,ht=new Set,Ye=(()=>{try{return JSON.parse(localStorage.getItem("kz-order"))||{}}catch(e){return{}}})(),ne=!1,Qe=!1,le=new Set,C=(e)=>e._k||(e.kind==="idea"?"idea:"+e.state+":"+e.text:"run:"+e.state+":"+e.id);function as(e){let n=C(e),o=document.createElement("div");o.className="card"+(n===a.openKey?" open":"")+(n===a.menuKey?" menu":"")+(e.archived?" archived":""),o.tabIndex=0,o.dataset.k=n,o.setAttribute("role","button");let l=at[e.status]||"idea",r=e.kind==="idea"?"idea":e.text,c=e.status==="waiting"?Ge(e.awaiting)||"needs you":e.status||"idea",f=[],k=[],v=(g,p,d="")=>`<span class="mk${d}" role="img" title="${m(p)}" aria-label="${m(p)}"><svg><use href="#${g}"/></svg></span>`;if(a.all){let g=e.where.split("/").filter(Boolean).pop()||e.where;f.push(`<span class="chip proj" title="${m(e.where)}">${m(g)}</span>`)}for(let g of e.tags||[])if(ln[g])k.push(v("c-"+g,g==="full"?"Full run: separate reviewer":g==="lite"?"Lite run: one session":xt[g]||g,g==="full"||g==="lite"?" accent":""));else if(g==="yolo")k.push('<span class="mk warn" title="Started in Yolo: the agent runs commands without asking">yolo</span>');else f.push(`<span class="chip">${m(xt[g]||g)}</span>`);if(e.pty&&e.pty.alive)k.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(h)f.push(`<span class="chip" title="From ${m(h[1])}">gathered</span>`);else if(e.notes)f.push('<span class="chip">notes</span>');if(e.git)k.push(v("c-git",e.git==="pushed"?"Committed & pushed":"Not committed & pushed yet",e.git==="pushed"?" ok":" warn"));let u=Qe&&!ne&&e.column===4;if(o.innerHTML=(u?`<input type="checkbox" class="pick" ${le.has(n)?"checked":""} aria-label="Select">`:"")+`<div class="c-id"><span class="st d-${l}" role="img" title="${m(c)}" aria-label="${m(c)}"><svg><use href="#s-${l}"/></svg></span><span class="idt">${m(r)}</span><span class="age">${e.kind==="run"?rn(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="${n===a.menuKey}">${j("more")}</button></div>
21
21
  <div class="c-title"${e.kind==="idea"&&xe(e.text).parts.length?` title="${m(e.text)}"`:""}>${m(e.kind==="idea"?xe(e.text).head:e.title||e.id)}</div>
22
- ${f.length||k.length?`<div class="c-foot"><span class="c-chips">${f.join("")}</span><span class="c-marks">${k.join("")}</span></div>`:""}`,n===a.menuKey){let g=[];if(ne){if(g.push(["restore","Restore",j("restore")]),g.push(["open","Open",j("open")]),e.kind==="idea")g.push(null,["delete","Delete",j("trash"),"danger"])}else if(e.kind==="idea"&&e.status==="idea")g.push(["run","Run",j("play")],["edit","Edit",j("pen")],...a.S.projects.some((p)=>p.dir!==e.state)?[["move","Move to project",j("ext")]]:[],["archive","Archive",j("archive")],null,["reject","Reject",j("x")],["delete","Delete",j("trash"),"danger"]);else if(e.kind==="idea")g.push(["open","Open",j("open")],["run","Run again",j("play")],["archive","Archive",j("archive")],null,["delete","Remove",j("trash"),"danger"]);else{let p=e.status==="waiting"?e.awaiting==="approvals.plan"?"Review plan":e.awaiting==="findings"?"See findings":e.awaiting==="approvals.review"?"Review work":"Open":"Open";if(g.push(["open",p,j("open")]),g.push(["archive","Archive",j("archive")]),e.status==="waiting"||e.status==="running"||e.status==="stalled")g.push(null,["reject","Abandon",j("x"),"danger"])}o.innerHTML+=`<div class="menu-pop" role="menu">${g.map((p)=>p?`<button role="menuitem" data-a="${p[0]}" class="${p[3]||""}">${p[2]}${p[1]}</button>`:"<hr>").join("")}</div>`}if(o.querySelectorAll("[data-a]").forEach((g)=>g.onclick=async(p)=>{p.stopPropagation();let d=g.dataset.a,b=a.menuKey;if(a.menuKey=null,d==="menu"){a.menuKey=b===n?null:n,E();return}if(d==="open"){we(e);return}if(d==="archive"||d==="restore"){gt(e,d==="archive");return}a.mode=d,a.modeKey=n,E()}),u)return o.classList.toggle("picked",le.has(n)),o.onclick=()=>{le.has(n)?le.delete(n):le.add(n),E()},o;return o.onclick=()=>{if(a.menuKey){a.menuKey=null,E();return}we(e)},o.onkeydown=(g)=>{if(g.key==="Enter"||g.key===" ")g.preventDefault(),we(e)},o}function as(e){let n=document.createElement("li");n.className="inl";let o=e.state;if(a.mode==="edit")n.innerHTML=`<input value="${m(e.text)}"><textarea placeholder="Notes: links, paths, background">${m(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>`,n.querySelector("[data-a=s]").onclick=async()=>{let r=n.querySelector("input").value.trim(),c=n.querySelector("textarea").value.trim();if(a.mode=null,r&&(r!==e.text||c!==(e.notes||"")))await A("/inbox",{op:"edit",dir:o,text:e.text,next:r,notes:c});D()};if(a.mode==="reject"){let r=e.kind==="idea";n.innerHTML=`<div class="q">${r?"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">${r?"Reject":"Abandon"}</button></div>`,n.querySelector("[data-a=s]").onclick=async()=>{let c=n.querySelector("input").value.trim();if(!c){S("A reason is required");return}if(a.mode=null,(r?await A("/inbox",{op:"reject",dir:o,text:e.text,reason:c}):await A("/abort",{dir:o,id:e.id,why:c})).ok)S(r?"Rejected":"Abandoned, reason recorded");D()}}if(a.mode==="delete")n.innerHTML=`<div class="q">Delete "${m(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>`,n.querySelector("[data-a=s]").onclick=async()=>{if(a.mode=null,(await A("/inbox",{op:"delete",dir:o,text:e.text})).ok)S("Deleted");D()};if(a.mode==="move"){let r=a.S.projects.filter((c)=>c.dir!==o);n.innerHTML=`<div class="q">Move "${m(e.text)}" to</div><select class="sel" aria-label="Project">${r.map((c)=>`<option value="${m(c.dir)}">${m(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>`,n.querySelector("[data-a=s]").onclick=async()=>{let c=n.querySelector("select"),f=c.value,k=c.selectedOptions[0]?.textContent;if(a.mode=null,(await A("/inbox",{op:"move",dir:o,to:f,text:e.text})).ok)S(`Moved to ${k}`);D()}}if(a.mode==="run")n.innerHTML=`<div class="q">Run "${m(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>`,n.querySelector("[data-a=s]").onclick=()=>wn(e,"full"),n.querySelector("[data-a=l]").onclick=()=>wn(e,"lite"),is(n,e);if(n.querySelector("[data-a=c]").onclick=()=>{a.mode=null,E()},n.querySelectorAll("input,textarea").forEach((r,c)=>Te(r,"f:"+a.mode+":"+a.modeKey+":"+c)),a.mode!=="run"){let r=Ue([...n.querySelectorAll("input,textarea")]);if(r)n.querySelector(".row").prepend(r)}let l=n.querySelector("input");if(l)l.onkeydown=(r)=>{if(r.key==="Enter")n.querySelector("[data-a=s]").click();if(r.key==="Escape")a.mode=null,E()},setTimeout(()=>l.focus());return n}var V=null,ke=(e)=>e.name+(e.note?". "+e.note:"");async function is(e,n){let o=C(n),l=e.querySelector("#run-agent"),r=await fetch("/agents?dir="+encodeURIComponent(n.state)).then((v)=>v.json()).catch(()=>null);if(!r||!r.agents||!r.agents.length){e.querySelector("#run-pick").hidden=!0;return}if(r.agents.some((v)=>v.cmd==="agy"))r.agents=r.agents.filter((v)=>v.cmd!=="gemini");if(!V||V.k!==o)V={k:o,agent:r.agents.some((v)=>v.cmd===r.default)?r.default:r.agents[0].cmd};l.innerHTML=r.agents.map((v)=>`<button type="button" class="agent" role="radio" aria-checked="${v.cmd===V.agent}" data-cmd="${m(v.cmd)}"><svg><use href="#c-${m(v.cmd)}"/></svg>${m(v.name)}</button>`).join("");let c=e.querySelector("#run-yolo"),f=c.querySelector(".chk"),k=()=>{let v=!!r.agents.find((u)=>u.cmd===V.agent)?.yolo,h=!!V.yolo;c.hidden=!v,f.classList.toggle("on",h),f.setAttribute("aria-checked",h)};l.querySelectorAll(".agent").forEach((v)=>v.onclick=()=>{V.agent=v.dataset.cmd,l.querySelectorAll(".agent").forEach((h)=>h.setAttribute("aria-checked",h===v)),k()}),f.onclick=()=>{V.yolo=!V.yolo,k()},k(),l.onkeydown=f.onkeydown=(v)=>{if(v.key==="Escape")a.mode=null,E()}}async function wn(e,n){a.mode=null,ue();let o=V&&V.k===C(e)?V:null;V=null;let l=!!(o&&o.yolo&&!t("run-yolo")?.hidden),r=await A("/run",{dir:e.state,text:e.text,kind:n,agent:o?o.agent:void 0,yolo:l});if(r.ok){if(S((n==="full"?"Full":"Lite")+(l?" yolo":"")+" run "+Ce(r)+": "+ke(r.agent)),r.pty)a.wantTerm=C(e)}D()}function E(){if(a.mode&&a.mode!=="new"&&!a.S.cards.some((u)=>C(u)===a.modeKey))a.mode=null;if(a.mode!=="run"&&V)V.yolo=!1;if(!a.mode)on("f:");try{sessionStorage.setItem("kz-form",JSON.stringify(["new","edit","reject"].includes(a.mode)?[a.mode,a.modeKey]:null))}catch(u){}let e=Mt(),n=a.all&&a.shown.size&&qe().some((u)=>a.shown.has(u.dir)),o=a.S.cards.filter((u)=>u.archived===ne&&(!n||a.shown.has(u.state))),l=t("board");l.innerHTML="";let r=a.S.cards.filter((u)=>u.archived).length,c=(u)=>o.filter((g)=>g.status===u).length,f=[];for(let u of["waiting","running","stalled","starting","idea","done","abandoned"]){let g=c(u);if(!g&&u!=="running")continue;let p=u==="running"?'<span class="d-run"><span class="pulse"></span></span>':`<span class="st d-${at[u]}"><svg><use href="#s-${at[u]}"/></svg></span>`;f.push(`<button class="${u==="waiting"?"attn ":""}${Re===u?"on":""}" data-f="${u}" title="Show only ${Fe[u]}">${p}${g} ${Fe[u]}${u==="idea"&&g!==1?"s":""}</button>`)}f.push(`<button class="${ne?"on":""}" data-f="__archive" title="${o.filter((u)=>u.kind==="idea").length} ideas · ${o.filter((u)=>u.kind==="run").length} runs on the board">${ne?"Back to board":"Archive "+r}</button>`),t("strip").innerHTML=f.join(""),t("strip").querySelectorAll("button").forEach((u)=>u.onclick=()=>{if(u.dataset.f==="__archive"){ne=!ne,Re=null,E();return}Re=Re===u.dataset.f?null:u.dataset.f,E()}),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"]];let k=a.all||!a.S.dir?"All projects":(a.PL.find((u)=>u[1]===a.S.dir)||["All projects"])[0];if(t("proj").textContent=Tt(k),t("proj").title=k,!t("projpop").hidden)mt();t("showc").hidden=!a.all;let v=qe(),h=v.filter((u)=>a.shown.has(u.dir)).length;if(t("show").textContent=h?`Showing ${h} of ${v.length}`:"Showing all",!t("showpop").hidden)ft();if(!o.length&&a.mode!=="new"){if(l.innerHTML=ne?'<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>',!ne)t("newempty").onclick=()=>{a.mode="new",E()}}if(cn.forEach((u,g)=>{let p=document.createElement("section"),d=o.filter((L)=>L.column===g&&(!Re||L.status===Re));if(g<4){let L=(G)=>G.kind==="run"?G.moved||0:a.S.cards.indexOf(G),K=Ye[g]==="old";d.sort((G,N)=>K?L(G)-L(N):L(N)-L(G))}let b=g===4&&!ne&&d.length,x=(L)=>d.filter((K)=>a.S.now-(K.moved||a.S.now)>L*an),T=`<h2>${u}<i>${d.length||""}</i>${g<4&&d.length>1?`<button class="btn sm ghost ord" title="Sort order">${Ye[g]==="old"?"Oldest":"Newest"}</button>`:""}${g===0&&!ne?`<button class="ib sm" title="New idea" aria-label="New idea">${j("plus")}</button>`:""}`;if(b&&Qe)T+=`<span class="pickbar"><button class="btn sm ghost" data-p="cancel">Cancel</button><button class="btn sm pri" data-p="go" ${le.size?"":"disabled"}>Archive ${le.size||""}</button></span>`;else if(b)T+='<button class="btn sm ghost clear" title="Archive finished runs">Clear ▾</button>';if(b&&a.clearMenu)T+=`<div class="menu-pop clear-pop"><button data-c="7">Older than a week <i>${x(7).length}</i></button><button data-c="3">Older than 3 days <i>${x(3).length}</i></button><button data-c="0">Everything <i>${d.length}</i></button><hr><button data-c="pick">Pick which…</button></div>`;p.innerHTML=T+"</h2>";{let L=p.querySelector(".ord");if(L)L.onclick=(K)=>{K.stopPropagation(),Ye[g]=Ye[g]==="old"?"new":"old";try{localStorage.setItem("kz-order",JSON.stringify(Ye))}catch(G){}E()}}if(g===0&&!ne)p.querySelector(".ib").onclick=()=>{a.mode="new",E()};if(b){let L=p.querySelector(".clear");if(L)L.onclick=(K)=>{K.stopPropagation(),a.clearMenu=!a.clearMenu,E()};p.querySelectorAll("[data-c]").forEach((K)=>K.onclick=async(G)=>{G.stopPropagation(),a.clearMenu=!1;let N=K.dataset.c;if(N==="pick"){Qe=!0,le.clear(),E();return}let W=x(+N);if(!W.length){S("Nothing that old"),E();return}for(let ce of W)await A("/archive",{dir:ce.state,id:ce.id});S("Archived "+W.length),D()}),p.querySelectorAll("[data-p]").forEach((K)=>K.onclick=async(G)=>{if(G.stopPropagation(),K.dataset.p==="cancel"){Qe=!1,le.clear(),E();return}let N=d.filter((W)=>le.has(C(W)));Qe=!1,le.clear();for(let W of N)await A("/archive",{dir:W.state,id:W.id});S("Archived "+N.length),D()})}let P=document.createElement("ul");if(g===0&&a.mode==="new"){let L=document.createElement("li");L.className="inl",L.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="row"><button class="btn ghost" id="nc">Cancel</button><button class="btn pri" id="ns">Add idea</button></div>`,P.appendChild(L);let K=L.querySelector("#newidea");Te(K,"f:new:0"),Te(L.querySelector("textarea"),"f:new:1");let G=async()=>{let N=K.value.trim();if(!N)return;let W=L.querySelector("textarea").value.trim();a.mode=null;let ce=a.all?Le:null;if((await A("/inbox",ce?{op:"add",dir:ce,text:N,notes:W}:{op:"add",text:N,notes:W})).ok)S("Idea added");D()};if(a.all)vn(L);L.querySelector("#ns").onclick=G,L.querySelector("#nc").onclick=()=>{a.mode=null,E()};{let N=Ue([K,L.querySelector("textarea")]);if(N)L.querySelector(".row").prepend(N)}K.onkeydown=(N)=>{if(N.key==="Enter")G();if(N.key==="Escape")a.mode=null,E()},setTimeout(()=>K.focus())}let H=ht.has(g)||d.length<=Et+1?d:d.slice(0,Et);if(H.forEach((L)=>{if(a.modeKey===C(L)&&a.mode&&a.mode!=="new"){P.appendChild(as(L));return}let K=document.createElement("li");K.appendChild(os(L)),P.appendChild(K)}),H.length<d.length){let L=document.createElement("li");L.innerHTML=`<button class="more-btn">Show ${d.length-H.length} more</button>`,L.firstChild.onclick=()=>{ht.add(g),E()},P.appendChild(L)}else if(ht.has(g)&&d.length>Et+1){let L=document.createElement("li");L.innerHTML='<button class="more-btn">Show less</button>',L.firstChild.onclick=()=>{ht.delete(g),E()},P.appendChild(L)}p.appendChild(P),l.appendChild(p)}),ae(!1),e&&document.activeElement===document.body)Dt(e)?.focus({preventScroll:!0})}var Mt=()=>document.activeElement?.closest?.("#board .card")?.dataset.k,Dt=(e)=>e&&t("board").querySelector('.card[data-k="'+CSS.escape(e)+'"]');function $n(e,n){let o=[...t("board").querySelectorAll("section")].map((k)=>[...k.querySelectorAll(".card")]).filter((k)=>k.length);if(!o.length)return;let l=document.activeElement.closest(".card"),r=o.findIndex((k)=>k.includes(l)),c=0;if(r<0)r=0;else if(c=o[r].indexOf(l),n)c=Math.max(0,Math.min(o[r].length-1,c+n));else r=Math.max(0,Math.min(o.length-1,r+e)),c=Math.min(c,o[r].length-1);let f=o[r][c];f.focus({preventScroll:!0}),f.scrollIntoView({block:"nearest",inline:"nearest"})}async function D(e){let n;try{n=await fetch("/state"+(a.all?"?all=1":""))}catch{S(xn);return}let o=a.openKey&&a.S.cards.find((l)=>C(l)===a.openKey);a.S=await n.json();{let l=new Map;for(let r of a.S.cards){let c=C(r),f=l.get(c)||0;l.set(c,f+1),r._k=f?c+"#"+f:c}}for(let l of a.S.cards){if(l.kind!=="run"||l.archived)continue;if(due(C(l),l.notice,l.status==="done",a.S.now))tn(l.notice)}if(!(e&&a.mode&&t("board").querySelector(".inl input")))E();if(a.wantTerm&&!a.openKey&&!a.mode){let l=a.S.cards.find((r)=>C(r)===a.wantTerm&&r.pty);if(l){a.wantTerm=null,we(l);return}}if(a.openKey){let l=a.S.cards.find((c)=>C(c)===a.openKey),r=t("pbody").querySelector(".notes textarea");if(r&&document.activeElement===r)return;if(!l){if(o&&o.kind==="idea")rs(o);return}if(a.runDoc)Y(l,!0,!0);else if(l.kind==="idea")Pt(l)}}async function rs(e){let n=C(e),o=(r)=>r.toLowerCase().replace(/\s+/g," ").trim(),l=o(e.text);for(let r of a.S.cards.filter((c)=>c.kind==="run"&&c.state===e.state).sort((c,f)=>(f.moved??0)-(c.moved??0)).slice(0,5)){let c=await fetch(`/run/${encodeURIComponent(r.id)}?dir=${encodeURIComponent(r.state)}`).then((f)=>f.json()).catch(()=>null);if(a.openKey!==n)return;if(o(c?.request||"").includes(l)){we(r);return}}if(a.openKey===n)Q()}var xn="The board server is not running — start it with kaizen web (or the shortcut), then reload.";async function A(e,n){let o;try{o=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}catch{return S(xn),{ok:!1}}let l=await o.json().catch(()=>({}));if(!o.ok){let r=l.error||l.why||"That did not work";if(S(r,{title:r.slice(0,80),what:`POST ${e} failed (HTTP ${o.status}): ${r}`}),l.manual)console.log("run manually:",l.manual)}return l}var bt=null,yt=null,Sn=null,Ce=(e)=>e.pty?"running in its card":"opened in your terminal",ls=()=>Sn||=new Promise((e,n)=>{let o=document.createElement("link");o.rel="stylesheet",o.href="/term.css",document.head.append(o);let l=document.createElement("script");l.src="/term.js",l.onload=()=>e(window.kzTerm),l.onerror=()=>{Sn=null,n()},document.head.append(l)});function fe(){if(yt)yt();yt=null,bt=null,t("panel").classList.remove("wide")}async function Rt(e){let n=e.pty.key;if(bt===n&&t("pbody").querySelector(".term"))return;fe(),bt=n,t("panel").classList.add("wide"),t("pbody").innerHTML='<div class="term" aria-label="Agent terminal"></div>';let o;try{o=await ls()}catch{t("pbody").innerHTML='<p style="color:var(--dim)">The terminal could not load.</p>';return}if(bt!==n)return;yt=o(t("pbody").querySelector(".term"),n,()=>D(!0))}var qn=(e)=>e.status==="stalled"||!!(e.pty&&!e.pty.alive);async function Ln(e,n){n.disabled=!0,ue();let o=await A("/resume",{dir:e.state,id:e.id});if(!o.ok){n.disabled=!1;return}if(S("Resumed, "+Ce(o)+": "+ke(o.agent)),o.pty)a.wantTerm=C(e);if(a.openKey===C(e))t("pfoot").innerHTML="",Y(e,!0)}var Kt={"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."},Nt=new Set,It=(e)=>C(e)+":"+e.awaiting+":"+e.moved;async function vt(e,n,o,l){n.disabled=!0,ue();let r=await A("/decide",{dir:e.state,id:e.id,awaiting:e.awaiting,...o===void 0?{}:{why:o},...l?{answers:l}:{}});if(!r.ok){n.disabled=!1;return}if(Nt.add(It(e)),S((o===void 0?"Approved":"Sent back")+", "+Ce(r)+": "+ke(r.agent)),r.pty)a.wantTerm=C(e);if(a.openKey===C(e))t("pfoot").innerHTML="",Y(e,!0)}function Cn(e){let n=t("pfoot"),o=e.awaiting==="approvals.plan";n.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 l=n.querySelector("textarea");l.focus(),n.querySelector("[data-a=c]").onclick=()=>{n.innerHTML="",Y(e,!0)},n.querySelector("[data-a=s]").onclick=(r)=>{let c=l.value.trim();if(!c){S("Say what should change"),l.focus();return}vt(e,r.target,c)}}function jn(e,n){let o=t("pfoot");o.innerHTML=`<div class="inl qa" style="flex:1"><div class="q">The plan asks ${n.length===1?"one question":n.length+" questions"}.</div><div class="q dim">Your answers go to your agent with the approval. It records them and builds the plan.</div>${n.map((l,r)=>`<fieldset><legend>${m(l.q)}</legend>${l.options.map((c,f)=>`<label><input type="${l.multi?"checkbox":"radio"}" name="q${r}" value="${f}"${c.rec||!l.multi&&!f&&!l.options.some((k)=>k.rec)?" checked":""}> ${m(c.text)}</label>`).join("")}<input type="text" data-n="${r}" placeholder="Note (optional)" aria-label="Note: ${m(l.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="",Y(e,!0)},o.querySelector("[data-a=s]").onclick=(l)=>vt(e,l.target,void 0,n.map((r,c)=>{let f=[...o.querySelectorAll(`[name=q${c}]:checked`)].map((v)=>`"${r.options[+v.value].label}"`).join(", ")||"none of the options",k=o.querySelector(`[data-n="${c}"]`).value.trim();return`${r.q} — ${f}${k?"; "+k:""}`}))}function cs(e){let n=[];for(let o of(e||"").split(`
22
+ ${f.length||k.length?`<div class="c-foot"><span class="c-chips">${f.join("")}</span><span class="c-marks">${k.join("")}</span></div>`:""}`,n===a.menuKey){let g=[];if(ne){if(g.push(["restore","Restore",j("restore")]),g.push(["open","Open",j("open")]),e.kind==="idea")g.push(null,["delete","Delete",j("trash"),"danger"])}else if(e.kind==="idea"&&e.status==="idea")g.push(["run","Run",j("play")],["edit","Edit",j("pen")],...a.S.projects.some((p)=>p.dir!==e.state)?[["move","Move to project",j("ext")]]:[],["archive","Archive",j("archive")],null,["reject","Reject",j("x")],["delete","Delete",j("trash"),"danger"]);else if(e.kind==="idea")g.push(["open","Open",j("open")],["run","Run again",j("play")],["archive","Archive",j("archive")],null,["delete","Remove",j("trash"),"danger"]);else{let p=e.status==="waiting"?e.awaiting==="approvals.plan"?"Review plan":e.awaiting==="findings"?"See findings":e.awaiting==="approvals.review"?"Review work":"Open":"Open";if(g.push(["open",p,j("open")]),g.push(["archive","Archive",j("archive")]),e.status==="waiting"||e.status==="running"||e.status==="stalled")g.push(null,["reject","Abandon",j("x"),"danger"])}o.innerHTML+=`<div class="menu-pop" role="menu">${g.map((p)=>p?`<button role="menuitem" data-a="${p[0]}" class="${p[3]||""}">${p[2]}${p[1]}</button>`:"<hr>").join("")}</div>`}if(o.querySelectorAll("[data-a]").forEach((g)=>g.onclick=async(p)=>{p.stopPropagation();let d=g.dataset.a,b=a.menuKey;if(a.menuKey=null,d==="menu"){a.menuKey=b===n?null:n,E();return}if(d==="open"){we(e);return}if(d==="archive"||d==="restore"){gt(e,d==="archive");return}a.mode=d,a.modeKey=n,E()}),u)return o.classList.toggle("picked",le.has(n)),o.onclick=()=>{le.has(n)?le.delete(n):le.add(n),E()},o;return o.onclick=()=>{if(a.menuKey){a.menuKey=null,E();return}we(e)},o.onkeydown=(g)=>{if(g.key==="Enter"||g.key===" ")g.preventDefault(),we(e)},o}function is(e){let n=document.createElement("li");n.className="inl";let o=e.state;if(a.mode==="edit")n.innerHTML=`<input value="${m(e.text)}"><textarea placeholder="Notes: links, paths, background">${m(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>`,n.querySelector("[data-a=s]").onclick=async()=>{let r=n.querySelector("input").value.trim(),c=n.querySelector("textarea").value.trim();if(a.mode=null,r&&(r!==e.text||c!==(e.notes||"")))await A("/inbox",{op:"edit",dir:o,text:e.text,next:r,notes:c});D()};if(a.mode==="reject"){let r=e.kind==="idea";n.innerHTML=`<div class="q">${r?"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">${r?"Reject":"Abandon"}</button></div>`,n.querySelector("[data-a=s]").onclick=async()=>{let c=n.querySelector("input").value.trim();if(!c){S("A reason is required");return}if(a.mode=null,(r?await A("/inbox",{op:"reject",dir:o,text:e.text,reason:c}):await A("/abort",{dir:o,id:e.id,why:c})).ok)S(r?"Rejected":"Abandoned, reason recorded");D()}}if(a.mode==="delete")n.innerHTML=`<div class="q">Delete "${m(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>`,n.querySelector("[data-a=s]").onclick=async()=>{if(a.mode=null,(await A("/inbox",{op:"delete",dir:o,text:e.text})).ok)S("Deleted");D()};if(a.mode==="move"){let r=a.S.projects.filter((c)=>c.dir!==o);n.innerHTML=`<div class="q">Move "${m(e.text)}" to</div><select class="sel" aria-label="Project">${r.map((c)=>`<option value="${m(c.dir)}">${m(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>`,n.querySelector("[data-a=s]").onclick=async()=>{let c=n.querySelector("select"),f=c.value,k=c.selectedOptions[0]?.textContent;if(a.mode=null,(await A("/inbox",{op:"move",dir:o,to:f,text:e.text})).ok)S(`Moved to ${k}`);D()}}if(a.mode==="run")n.innerHTML=`<div class="q">Run "${m(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>`,n.querySelector("[data-a=s]").onclick=()=>wn(e,"full"),n.querySelector("[data-a=l]").onclick=()=>wn(e,"lite"),rs(n,e);if(n.querySelector("[data-a=c]").onclick=()=>{a.mode=null,E()},n.querySelectorAll("input,textarea").forEach((r,c)=>Te(r,"f:"+a.mode+":"+a.modeKey+":"+c)),a.mode!=="run"){let r=Ue([...n.querySelectorAll("input,textarea")]);if(r)n.querySelector(".row").prepend(r)}let l=n.querySelector("input");if(l)l.onkeydown=(r)=>{if(r.key==="Enter")n.querySelector("[data-a=s]").click();if(r.key==="Escape")a.mode=null,E()},setTimeout(()=>l.focus());return n}var J=null,ke=(e)=>e.name+(e.note?". "+e.note:"");async function rs(e,n){let o=C(n),l=e.querySelector("#run-agent"),r=await fetch("/agents?dir="+encodeURIComponent(n.state)).then((v)=>v.json()).catch(()=>null);if(!r||!r.agents||!r.agents.length){e.querySelector("#run-pick").hidden=!0;return}if(r.agents.some((v)=>v.cmd==="agy"))r.agents=r.agents.filter((v)=>v.cmd!=="gemini");if(!J||J.k!==o)J={k:o,agent:r.agents.some((v)=>v.cmd===r.default)?r.default:r.agents[0].cmd};l.innerHTML=r.agents.map((v)=>`<button type="button" class="agent" role="radio" aria-checked="${v.cmd===J.agent}" data-cmd="${m(v.cmd)}"><svg><use href="#c-${m(v.cmd)}"/></svg>${m(v.name)}</button>`).join("");let c=e.querySelector("#run-yolo"),f=c.querySelector(".chk"),k=()=>{let v=!!r.agents.find((u)=>u.cmd===J.agent)?.yolo,h=!!J.yolo;c.hidden=!v,f.classList.toggle("on",h),f.setAttribute("aria-checked",h)};l.querySelectorAll(".agent").forEach((v)=>v.onclick=()=>{J.agent=v.dataset.cmd,l.querySelectorAll(".agent").forEach((h)=>h.setAttribute("aria-checked",h===v)),k()}),f.onclick=()=>{J.yolo=!J.yolo,k()},k(),l.onkeydown=f.onkeydown=(v)=>{if(v.key==="Escape")a.mode=null,E()}}async function wn(e,n){a.mode=null,ue();let o=J&&J.k===C(e)?J:null;J=null;let l=!!(o&&o.yolo&&!t("run-yolo")?.hidden),r=await A("/run",{dir:e.state,text:e.text,kind:n,agent:o?o.agent:void 0,yolo:l});if(r.ok){if(S((n==="full"?"Full":"Lite")+(l?" yolo":"")+" run "+Ce(r)+": "+ke(r.agent)),r.pty)a.wantTerm=C(e)}D()}function E(){if(a.mode&&a.mode!=="new"&&!a.S.cards.some((u)=>C(u)===a.modeKey))a.mode=null;if(a.mode!=="run"&&J)J.yolo=!1;if(!a.mode)on("f:");try{sessionStorage.setItem("kz-form",JSON.stringify(["new","edit","reject"].includes(a.mode)?[a.mode,a.modeKey]:null))}catch(u){}let e=Mt(),n=a.all&&a.shown.size&&qe().some((u)=>a.shown.has(u.dir)),o=a.S.cards.filter((u)=>u.archived===ne&&(!n||a.shown.has(u.state))),l=t("board");l.innerHTML="";let r=a.S.cards.filter((u)=>u.archived).length,c=(u)=>o.filter((g)=>g.status===u).length,f=[];for(let u of["waiting","running","stalled","starting","idea","done","abandoned"]){let g=c(u);if(!g&&u!=="running")continue;let p=u==="running"?'<span class="d-run"><span class="pulse"></span></span>':`<span class="st d-${at[u]}"><svg><use href="#s-${at[u]}"/></svg></span>`;f.push(`<button class="${u==="waiting"?"attn ":""}${Pe===u?"on":""}" data-f="${u}" title="Show only ${Fe[u]}">${p}${g} ${Fe[u]}${u==="idea"&&g!==1?"s":""}</button>`)}f.push(`<button class="${ne?"on":""}" data-f="__archive" title="${o.filter((u)=>u.kind==="idea").length} ideas · ${o.filter((u)=>u.kind==="run").length} runs on the board">${ne?"Back to board":"Archive "+r}</button>`),t("strip").innerHTML=f.join(""),t("strip").querySelectorAll("button").forEach((u)=>u.onclick=()=>{if(u.dataset.f==="__archive"){ne=!ne,Pe=null,E();return}Pe=Pe===u.dataset.f?null:u.dataset.f,E()}),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"]];let k=a.all||!a.S.dir?"All projects":(a.PL.find((u)=>u[1]===a.S.dir)||["All projects"])[0];if(t("proj").textContent=Tt(k),t("proj").title=k,!t("projpop").hidden)mt();t("showc").hidden=!a.all;let v=qe(),h=v.filter((u)=>a.shown.has(u.dir)).length;if(t("show").textContent=h?`Showing ${h} of ${v.length}`:"Showing all",!t("showpop").hidden)ft();if(!o.length&&a.mode!=="new"){if(l.innerHTML=ne?'<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>',!ne)t("newempty").onclick=()=>{a.mode="new",E()}}if(cn.forEach((u,g)=>{let p=document.createElement("section"),d=o.filter((L)=>L.column===g&&(!Pe||L.status===Pe));if(g<4){let L=(G)=>G.kind==="run"?G.moved||0:a.S.cards.indexOf(G),K=Ye[g]==="old";d.sort((G,N)=>K?L(G)-L(N):L(N)-L(G))}let b=g===4&&!ne&&d.length,x=(L)=>d.filter((K)=>a.S.now-(K.moved||a.S.now)>L*an),T=`<h2>${u}<i>${d.length||""}</i>${g<4&&d.length>1?`<button class="btn sm ghost ord" title="Sort order">${Ye[g]==="old"?"Oldest":"Newest"}</button>`:""}${g===0&&!ne?`<button class="ib sm" title="New idea" aria-label="New idea">${j("plus")}</button>`:""}`;if(b&&Qe)T+=`<span class="pickbar"><button class="btn sm ghost" data-p="cancel">Cancel</button><button class="btn sm pri" data-p="go" ${le.size?"":"disabled"}>Archive ${le.size||""}</button></span>`;else if(b)T+='<button class="btn sm ghost clear" title="Archive finished runs">Clear ▾</button>';if(b&&a.clearMenu)T+=`<div class="menu-pop clear-pop"><button data-c="7">Older than a week <i>${x(7).length}</i></button><button data-c="3">Older than 3 days <i>${x(3).length}</i></button><button data-c="0">Everything <i>${d.length}</i></button><hr><button data-c="pick">Pick which…</button></div>`;p.innerHTML=T+"</h2>";{let L=p.querySelector(".ord");if(L)L.onclick=(K)=>{K.stopPropagation(),Ye[g]=Ye[g]==="old"?"new":"old";try{localStorage.setItem("kz-order",JSON.stringify(Ye))}catch(G){}E()}}if(g===0&&!ne)p.querySelector(".ib").onclick=()=>{a.mode="new",E()};if(b){let L=p.querySelector(".clear");if(L)L.onclick=(K)=>{K.stopPropagation(),a.clearMenu=!a.clearMenu,E()};p.querySelectorAll("[data-c]").forEach((K)=>K.onclick=async(G)=>{G.stopPropagation(),a.clearMenu=!1;let N=K.dataset.c;if(N==="pick"){Qe=!0,le.clear(),E();return}let W=x(+N);if(!W.length){S("Nothing that old"),E();return}for(let ce of W)await A("/archive",{dir:ce.state,id:ce.id});S("Archived "+W.length),D()}),p.querySelectorAll("[data-p]").forEach((K)=>K.onclick=async(G)=>{if(G.stopPropagation(),K.dataset.p==="cancel"){Qe=!1,le.clear(),E();return}let N=d.filter((W)=>le.has(C(W)));Qe=!1,le.clear();for(let W of N)await A("/archive",{dir:W.state,id:W.id});S("Archived "+N.length),D()})}let R=document.createElement("ul");if(g===0&&a.mode==="new"){let L=document.createElement("li");L.className="inl",L.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="row"><button class="btn ghost" id="nc">Cancel</button><button class="btn pri" id="ns">Add idea</button></div>`,R.appendChild(L);let K=L.querySelector("#newidea");Te(K,"f:new:0"),Te(L.querySelector("textarea"),"f:new:1");let G=async()=>{let N=K.value.trim();if(!N)return;let W=L.querySelector("textarea").value.trim();a.mode=null;let ce=a.all?Le:null;if((await A("/inbox",ce?{op:"add",dir:ce,text:N,notes:W}:{op:"add",text:N,notes:W})).ok)S("Idea added");D()};if(a.all)vn(L);L.querySelector("#ns").onclick=G,L.querySelector("#nc").onclick=()=>{a.mode=null,E()};{let N=Ue([K,L.querySelector("textarea")]);if(N)L.querySelector(".row").prepend(N)}K.onkeydown=(N)=>{if(N.key==="Enter")G();if(N.key==="Escape")a.mode=null,E()},setTimeout(()=>K.focus())}let H=ht.has(g)||d.length<=Et+1?d:d.slice(0,Et);if(H.forEach((L)=>{if(a.modeKey===C(L)&&a.mode&&a.mode!=="new"){R.appendChild(is(L));return}let K=document.createElement("li");K.appendChild(as(L)),R.appendChild(K)}),H.length<d.length){let L=document.createElement("li");L.innerHTML=`<button class="more-btn">Show ${d.length-H.length} more</button>`,L.firstChild.onclick=()=>{ht.add(g),E()},R.appendChild(L)}else if(ht.has(g)&&d.length>Et+1){let L=document.createElement("li");L.innerHTML='<button class="more-btn">Show less</button>',L.firstChild.onclick=()=>{ht.delete(g),E()},R.appendChild(L)}p.appendChild(R),l.appendChild(p)}),ae(!1),e&&document.activeElement===document.body)Dt(e)?.focus({preventScroll:!0})}var Mt=()=>document.activeElement?.closest?.("#board .card")?.dataset.k,Dt=(e)=>e&&t("board").querySelector('.card[data-k="'+CSS.escape(e)+'"]');function $n(e,n){let o=[...t("board").querySelectorAll("section")].map((k)=>[...k.querySelectorAll(".card")]).filter((k)=>k.length);if(!o.length)return;let l=document.activeElement.closest(".card"),r=o.findIndex((k)=>k.includes(l)),c=0;if(r<0)r=0;else if(c=o[r].indexOf(l),n)c=Math.max(0,Math.min(o[r].length-1,c+n));else r=Math.max(0,Math.min(o.length-1,r+e)),c=Math.min(c,o[r].length-1);let f=o[r][c];f.focus({preventScroll:!0}),f.scrollIntoView({block:"nearest",inline:"nearest"})}var xn=()=>a.dir?{"x-kz-dir":a.dir}:{};async function D(e){let n;try{n=await fetch("/state"+(a.all?"?all=1":""),{headers:xn()})}catch{S(Sn);return}let o=a.openKey&&a.S.cards.find((l)=>C(l)===a.openKey);a.S=await n.json();{let l=new Map;for(let r of a.S.cards){let c=C(r),f=l.get(c)||0;l.set(c,f+1),r._k=f?c+"#"+f:c}}for(let l of a.S.cards){if(l.kind!=="run"||l.archived)continue;if(due(C(l),l.notice,l.status==="done",a.S.now))tn(l.notice)}if(!(e&&a.mode&&t("board").querySelector(".inl input")))E();if(a.wantTerm&&!a.openKey&&!a.mode){let l=a.S.cards.find((r)=>C(r)===a.wantTerm&&r.pty);if(l){a.wantTerm=null,we(l);return}}if(a.openKey){let l=a.S.cards.find((c)=>C(c)===a.openKey),r=t("pbody").querySelector(".notes textarea");if(r&&document.activeElement===r)return;if(!l){if(o&&o.kind==="idea")ls(o);return}if(a.runDoc)Y(l,!0,!0);else if(l.kind==="idea")Rt(l)}}async function ls(e){let n=C(e),o=(r)=>r.toLowerCase().replace(/\s+/g," ").trim(),l=o(e.text);for(let r of a.S.cards.filter((c)=>c.kind==="run"&&c.state===e.state).sort((c,f)=>(f.moved??0)-(c.moved??0)).slice(0,5)){let c=await fetch(`/run/${encodeURIComponent(r.id)}?dir=${encodeURIComponent(r.state)}`).then((f)=>f.json()).catch(()=>null);if(a.openKey!==n)return;if(o(c?.request||"").includes(l)){we(r);return}}if(a.openKey===n)Q()}var Sn="The board server is not running — start it with kaizen web (or the shortcut), then reload.";async function A(e,n){let o;try{o=await fetch(e,{method:"POST",headers:{"content-type":"application/json",...xn()},body:JSON.stringify(n)})}catch{return S(Sn),{ok:!1}}let l=await o.json().catch(()=>({}));if(!o.ok){let r=l.error||l.why||"That did not work";if(S(r,{title:r.slice(0,80),what:`POST ${e} failed (HTTP ${o.status}): ${r}`}),l.manual)console.log("run manually:",l.manual)}return l}var bt=null,yt=null,qn=null,Ce=(e)=>e.pty?"running in its card":"opened in your terminal",cs=()=>qn||=new Promise((e,n)=>{let o=document.createElement("link");o.rel="stylesheet",o.href="/term.css",document.head.append(o);let l=document.createElement("script");l.src="/term.js",l.onload=()=>e(window.kzTerm),l.onerror=()=>{qn=null,n()},document.head.append(l)});function fe(){if(yt)yt();yt=null,bt=null,t("panel").classList.remove("wide")}async function Pt(e){let n=e.pty.key;if(bt===n&&t("pbody").querySelector(".term"))return;fe(),bt=n,t("panel").classList.add("wide"),t("pbody").innerHTML='<div class="term" aria-label="Agent terminal"></div>';let o;try{o=await cs()}catch{t("pbody").innerHTML='<p style="color:var(--dim)">The terminal could not load.</p>';return}if(bt!==n)return;yt=o(t("pbody").querySelector(".term"),n,()=>D(!0))}var Ln=(e)=>e.status==="stalled"||!!(e.pty&&!e.pty.alive);async function Cn(e,n){n.disabled=!0,ue();let o=await A("/resume",{dir:e.state,id:e.id});if(!o.ok){n.disabled=!1;return}if(S("Resumed, "+Ce(o)+": "+ke(o.agent)),o.pty)a.wantTerm=C(e);if(a.openKey===C(e))t("pfoot").innerHTML="",Y(e,!0)}var Kt={"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."},Nt=new Set,It=(e)=>C(e)+":"+e.awaiting+":"+e.moved;async function vt(e,n,o,l){n.disabled=!0,ue();let r=await A("/decide",{dir:e.state,id:e.id,awaiting:e.awaiting,...o===void 0?{}:{why:o},...l?{answers:l}:{}});if(!r.ok){n.disabled=!1;return}if(Nt.add(It(e)),S((o===void 0?"Approved":"Sent back")+", "+Ce(r)+": "+ke(r.agent)),r.pty)a.wantTerm=C(e);if(a.openKey===C(e))t("pfoot").innerHTML="",Y(e,!0)}function jn(e){let n=t("pfoot"),o=e.awaiting==="approvals.plan";n.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 l=n.querySelector("textarea");l.focus(),n.querySelector("[data-a=c]").onclick=()=>{n.innerHTML="",Y(e,!0)},n.querySelector("[data-a=s]").onclick=(r)=>{let c=l.value.trim();if(!c){S("Say what should change"),l.focus();return}vt(e,r.target,c)}}function An(e,n){let o=t("pfoot");o.innerHTML=`<div class="inl qa" style="flex:1"><div class="q">The plan asks ${n.length===1?"one question":n.length+" questions"}.</div><div class="q dim">Your answers go to your agent with the approval. It records them and builds the plan.</div>${n.map((l,r)=>`<fieldset><legend>${m(l.q)}</legend>${l.options.map((c,f)=>`<label><input type="${l.multi?"checkbox":"radio"}" name="q${r}" value="${f}"${c.rec||!l.multi&&!f&&!l.options.some((k)=>k.rec)?" checked":""}> ${m(c.text)}</label>`).join("")}<input type="text" data-n="${r}" placeholder="Note (optional)" aria-label="Note: ${m(l.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="",Y(e,!0)},o.querySelector("[data-a=s]").onclick=(l)=>vt(e,l.target,void 0,n.map((r,c)=>{let f=[...o.querySelectorAll(`[name=q${c}]:checked`)].map((v)=>`"${r.options[+v.value].label}"`).join(", ")||"none of the options",k=o.querySelector(`[data-n="${c}"]`).value.trim();return`${r.q} — ${f}${k?"; "+k:""}`}))}function ds(e){let n=[];for(let o of(e||"").split(`
23
23
  `)){let l=/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2})\]\s?(.*)$/.exec(o);if(l){n.push({at:l[1],text:l[2]});continue}let r=n[n.length-1];if(r)r.text+=`
24
- `+o.replace(/^\s{2}/,"");else if(o.trim())n.push({at:"",text:o})}return n.map((o)=>({...o,text:o.text.trim()})).filter((o)=>o.text)}var Ht=null;function Ot(e,n,o){let l=(p)=>rt(p,e),r=cs(n),c=e.kind==="idea"?xe(e.text):{parts:[]},f=(c.parts.length?`<section class="desc">${ve(c.parts.map((p)=>`### ${p.label}
24
+ `+o.replace(/^\s{2}/,"");else if(o.trim())n.push({at:"",text:o})}return n.map((o)=>({...o,text:o.text.trim()})).filter((o)=>o.text)}var Ht=null;function Ot(e,n,o){let l=(p)=>rt(p,e),r=ds(n),c=e.kind==="idea"?xe(e.text):{parts:[]},f=(c.parts.length?`<section class="desc">${ve(c.parts.map((p)=>`### ${p.label}
25
25
  ${p.body}`).join(`
26
26
 
27
- `))}</section>`:"")+`<div class="chat">${r.map((p)=>`<div class="msg">${p.at?`<time>${m(p.at)}</time>`:""}${ve(l(p.text))}</div>`).join("")||'<p style="color:var(--dim)">No notes yet.</p>'}</div>`;if(!o)f+='<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(t("pbody").innerHTML=f,o)return;let k=t("pbody").querySelector("textarea"),v=[];if(Te(k,"n:"+C(e)),Ht===C(e))Ht=null,k.focus();{let p=Ue([k]);if(p)t("npick").before(p)}let h=()=>{t("natt").textContent=v.map((p)=>p.name).join(" · ")},u=(p)=>{v.push(p),h()};t("npick").onclick=()=>t("nfile").click(),t("nfile").onchange=(p)=>{[...p.target.files].forEach(u),p.target.value=""},k.onpaste=(p)=>{for(let d of p.clipboardData.items)if(d.kind==="file"){let b=d.getAsFile();u(new File([b],b.name==="image.png"?`paste-${Date.now()}.png`:b.name,{type:b.type})),p.preventDefault()}};let g=async()=>{ae(!0);let p=k.value.trim();if(!p&&!v.length)return;let d=await Promise.all(v.map((T)=>new Promise((P)=>{let H=new FileReader;H.onload=()=>P({name:T.name,data:H.result}),H.readAsDataURL(T)}))),b=e.kind==="run"?{dir:e.state,id:e.id,text:p,files:d}:{dir:e.state,for:e.text,text:p,files:d};if((await A("/note",b)).ok){v.length=0,k.value="";try{sessionStorage.removeItem(sn+"n:"+C(e))}catch(T){}S("Noted"),Ht=C(e),k.blur(),D()}};t("nsend").onclick=g,k.onkeydown=(p)=>{if(p.key==="Enter"&&!p.shiftKey)p.preventDefault(),g()},t("pbody").scrollTop=0}var An="kz-sent",he=new Set,zt=new Set((()=>{try{return JSON.parse(sessionStorage.getItem(An))||[]}catch(e){return[]}})());function Tn(e){let n=(u)=>e.id+":"+u,o=(u)=>zt.has(n(u.n)),l=a.runDoc.findings||[],r=l.filter((u)=>!u.done&&!o(u));if(e.archived||!l.some((u)=>!u.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>':""}`+l.map((u)=>{let g=u.done?" (closed)":o(u)?" (sent)":"";return`<label class="${g?"off":""}"><input type="checkbox" data-n="${u.n}" ${g?"checked disabled":he.has(n(u.n))?"":"checked"}><span>${u.n}. ${ve(u.text).replace(/^<p>|<\/p>$/g,"")}${g}</span></label>`}).join("")+(r.length?'<div class="row"><button class="btn ghost" data-a="all"></button><button class="btn pri" data-a="s"></button></div>':""),!r.length)return t("pbody").appendChild(c);let f=c.querySelector("[data-a=s]"),k=c.querySelector("[data-a=all]"),v=()=>r.filter((u)=>!he.has(n(u.n))).map((u)=>u.n),h=()=>{let u=v().length;f.textContent=u===r.length?"Fix all "+u:"Fix "+u+" selected",f.disabled=!u,k.textContent=u===r.length?"Select none":"Select all",c.querySelectorAll("input:not(:disabled)").forEach((g)=>g.checked=!he.has(n(g.dataset.n)))};c.onchange=(u)=>{let g=n(u.target.dataset.n);u.target.checked?he.delete(g):he.add(g),h()},k.onclick=()=>{let u=v().length===r.length;r.forEach((g)=>u?he.add(n(g.n)):he.delete(n(g.n))),h()},f.onclick=async()=>{let u=v();f.disabled=!0,ue();let g=await A("/fix",{dir:e.state,id:e.id,nums:u});if(g.ok){if(S("Fixing "+u.length+" finding"+(u.length===1?"":"s")+", "+Ce(g)+": "+ke(g.agent)),g.pty)a.wantTerm=C(e);u.forEach((p)=>zt.add(n(p)));try{sessionStorage.setItem(An,JSON.stringify([...zt]))}catch(p){}Y(e,!0)}else h()},h(),t("pbody").appendChild(c)}function Ut(e){let n=a.runDoc.git,o=a.runDoc.commit;if(e.archived||!n||!n.repo||!(e.status==="done"||e.awaiting==="approvals.review"))return"";if(o&&o.pushed)return`<button class="btn" disabled title="${m(o.sha)}">Committed &amp; pushed</button>`+(n.files.length?'<button class="btn" data-a="commit">Commit new changes</button>':n.ahead>0?'<button class="btn" data-a="push">Push</button>':"");if(n.files.length)return'<button class="btn pri" data-a="commit">Commit &amp; push</button>';if(n.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 Gt(e,n){let o=a.runDoc.git,l=t("pfoot");l.innerHTML=`<div class="inl" style="flex:1"><div class="q">Commit ${o.files.length} changed file${o.files.length===1?"":"s"} on ${m(o.branch||"this branch")} and push to ${m(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">${m(o.files.join(`
27
+ `))}</section>`:"")+`<div class="chat">${r.map((p)=>`<div class="msg">${p.at?`<time>${m(p.at)}</time>`:""}${ve(l(p.text))}</div>`).join("")||'<p style="color:var(--dim)">No notes yet.</p>'}</div>`;if(!o)f+='<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(t("pbody").innerHTML=f,o)return;let k=t("pbody").querySelector("textarea"),v=[];if(Te(k,"n:"+C(e)),Ht===C(e))Ht=null,k.focus();{let p=Ue([k]);if(p)t("npick").before(p)}let h=()=>{t("natt").textContent=v.map((p)=>p.name).join(" · ")},u=(p)=>{v.push(p),h()};t("npick").onclick=()=>t("nfile").click(),t("nfile").onchange=(p)=>{[...p.target.files].forEach(u),p.target.value=""},k.onpaste=(p)=>{for(let d of p.clipboardData.items)if(d.kind==="file"){let b=d.getAsFile();u(new File([b],b.name==="image.png"?`paste-${Date.now()}.png`:b.name,{type:b.type})),p.preventDefault()}};let g=async()=>{ae(!0);let p=k.value.trim();if(!p&&!v.length)return;let d=await Promise.all(v.map((T)=>new Promise((R)=>{let H=new FileReader;H.onload=()=>R({name:T.name,data:H.result}),H.readAsDataURL(T)}))),b=e.kind==="run"?{dir:e.state,id:e.id,text:p,files:d}:{dir:e.state,for:e.text,text:p,files:d};if((await A("/note",b)).ok){v.length=0,k.value="";try{sessionStorage.removeItem(sn+"n:"+C(e))}catch(T){}S("Noted"),Ht=C(e),k.blur(),D()}};t("nsend").onclick=g,k.onkeydown=(p)=>{if(p.key==="Enter"&&!p.shiftKey)p.preventDefault(),g()},t("pbody").scrollTop=0}var Tn="kz-sent",he=new Set,zt=new Set((()=>{try{return JSON.parse(sessionStorage.getItem(Tn))||[]}catch(e){return[]}})());function En(e){let n=(u)=>e.id+":"+u,o=(u)=>zt.has(n(u.n)),l=a.runDoc.findings||[],r=l.filter((u)=>!u.done&&!o(u));if(e.archived||!l.some((u)=>!u.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>':""}`+l.map((u)=>{let g=u.done?" (closed)":o(u)?" (sent)":"";return`<label class="${g?"off":""}"><input type="checkbox" data-n="${u.n}" ${g?"checked disabled":he.has(n(u.n))?"":"checked"}><span>${u.n}. ${ve(u.text).replace(/^<p>|<\/p>$/g,"")}${g}</span></label>`}).join("")+(r.length?'<div class="row"><button class="btn ghost" data-a="all"></button><button class="btn pri" data-a="s"></button></div>':""),!r.length)return t("pbody").appendChild(c);let f=c.querySelector("[data-a=s]"),k=c.querySelector("[data-a=all]"),v=()=>r.filter((u)=>!he.has(n(u.n))).map((u)=>u.n),h=()=>{let u=v().length;f.textContent=u===r.length?"Fix all "+u:"Fix "+u+" selected",f.disabled=!u,k.textContent=u===r.length?"Select none":"Select all",c.querySelectorAll("input:not(:disabled)").forEach((g)=>g.checked=!he.has(n(g.dataset.n)))};c.onchange=(u)=>{let g=n(u.target.dataset.n);u.target.checked?he.delete(g):he.add(g),h()},k.onclick=()=>{let u=v().length===r.length;r.forEach((g)=>u?he.add(n(g.n)):he.delete(n(g.n))),h()},f.onclick=async()=>{let u=v();f.disabled=!0,ue();let g=await A("/fix",{dir:e.state,id:e.id,nums:u});if(g.ok){if(S("Fixing "+u.length+" finding"+(u.length===1?"":"s")+", "+Ce(g)+": "+ke(g.agent)),g.pty)a.wantTerm=C(e);u.forEach((p)=>zt.add(n(p)));try{sessionStorage.setItem(Tn,JSON.stringify([...zt]))}catch(p){}Y(e,!0)}else h()},h(),t("pbody").appendChild(c)}function Ut(e){let n=a.runDoc.git,o=a.runDoc.commit;if(e.archived||!n||!n.repo||!(e.status==="done"||e.awaiting==="approvals.review"))return"";if(o&&o.pushed)return`<button class="btn" disabled title="${m(o.sha)}">Committed &amp; pushed</button>`+(n.files.length?'<button class="btn" data-a="commit">Commit new changes</button>':n.ahead>0?'<button class="btn" data-a="push">Push</button>':"");if(n.files.length)return'<button class="btn pri" data-a="commit">Commit &amp; push</button>';if(n.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 Gt(e,n){let o=a.runDoc.git,l=t("pfoot");l.innerHTML=`<div class="inl" style="flex:1"><div class="q">Commit ${o.files.length} changed file${o.files.length===1?"":"s"} on ${m(o.branch||"this branch")} and push to ${m(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">${m(o.files.join(`
28
28
  `))}</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">${m(o.shared.join(`
29
29
  `))}</pre>`:""}<textarea aria-label="Commit message" placeholder="feat(scope): what changed">${m(n||un(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 r=l.querySelector("textarea"),c=l.querySelector("[data-a=s]");r.focus();let f=()=>{r.style.height="auto",r.style.height=Math.min(r.scrollHeight+2,innerHeight/2)+"px",t("clong").hidden=r.value.split(`
30
- `)[0].length<=72};r.oninput=f,f(),l.querySelector("[data-a=c]").onclick=()=>{l.innerHTML="",Y(e,!0)},c.onclick=()=>{if(!r.value.trim()){S("A commit message is required");return}Ft(e,r.value,c)},r.onkeydown=(k)=>{if(k.key==="Enter"&&!k.shiftKey)k.preventDefault(),c.click()}}async function Ft(e,n,o){o.disabled=!0,o.dataset.busy=1,o.textContent="Pushing…";let l=await A("/commit",{dir:e.state,id:e.id,message:n,files:a.runDoc.git.files});if(l.ok)S("Committed & pushed "+l.sha);if(t("pfoot").innerHTML="",a.openKey!==C(e))return;if(await Y(e,!1),!l.ok&&n&&a.openKey===C(e)&&a.runDoc.git.files&&a.runDoc.git.files.length)Gt(e,n)}var En=[["Request","request"],["Notes","notes"],["Plan","plan"],["Backlog","backlog"],["Work","impl"],["Review","review"],["Preview","preview"]];function we(e,n){if(_!=="board")ie("board",!0);if(ae(!0),he.clear(),a.openKey=C(e),t("panel").classList.add("open"),t("scrim").classList.add("on"),t("ptitle").textContent=e.kind==="idea"?xe(e.text).head:e.text,E(),t("pterm").hidden=!0,e.kind==="idea"){a.runDoc=null,t("psub").textContent="Idea · "+e.where,t("ptabs").innerHTML="",Pt(e);let o=t("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((l)=>l.onclick=()=>{let r=l.dataset.a;if(r==="archive"||r==="restore"){gt(e,r==="archive");return}let c=C(e);Q(),a.mode=r,a.modeKey=c,E()});return}if(!n)a.lastTab=a.tab=e.status==="abandoned"?"plan":{1:"plan",2:"backlog",3:"review",4:"preview"}[e.column]||"request";if(!n&&e.pty&&e.pty.alive&&e.column>=1&&e.column<=3)a.tab="term";Y(e,!1)}var Ke=null;function ds(e){let n=e.column===2&&a.runDoc.progress,o=Ke&&Ke.id===e.id?Ke:null,l=n?fn(n,o&&o.st):"";if(!l)return;Ke={id:e.id,st:[...n.matchAll(/^\s*-\s*(todo|doing|done):/gm)].map((c)=>c[1]),w:+/width:(\d+)%/.exec(l)[1]},t("pbody").innerHTML=l+(a.runDoc.backlog?"<h3>Backlog</h3>"+ve(a.runDoc.backlog,!0):"");let r=t("pbody").querySelector(".pbar i");if(o&&o.w!==Ke.w)r.style.width=o.w+"%",r.offsetWidth,r.style.width=Ke.w+"%"}function Pt(e){if(e.pty)Rt(e);else fe(),Ot(e,e.notes,e.archived||e.status!=="idea")}async function Y(e,n,o){if(!n||!a.runDoc||a.runDoc.id!==e.id||o){let d=await fetch(`/run/${encodeURIComponent(e.id)}?dir=${encodeURIComponent(e.state)}`).then((b)=>b.json());if(n&&t("pfoot").querySelector(".inl,[data-busy],.more[open]"))return;a.runDoc=d}if(a.openKey!==C(e))return;e=a.S.cards.find((d)=>C(d)===a.openKey)||e;let l=t("pbody").querySelector(".notes textarea");if(n&&l&&document.activeElement===l)return;t("psub").textContent=e.id+" · "+(Fe[e.status]||e.status)+(e.awaiting?" · "+Ge(e.awaiting):"");let r=En.filter((d)=>d[1]==="request"||d[1]==="notes"||(d[1]==="preview"?it(a.runDoc.impl):a.runDoc[d[1]]||d[1]==="backlog"&&e.column===2&&a.runDoc.progress));if(e.pty&&a.wantTerm===C(e))a.wantTerm=null,a.tab="term";if(a.tab==="term"&&!e.pty)a.tab=a.lastTab;if(a.tab!=="term"&&!r.some((d)=>d[1]===a.tab))a.tab=[...r].reverse().find((d)=>d[1]!=="notes")[1];if(a.tab!=="term")a.lastTab=a.tab;t("panel").classList.toggle("wide",a.tab==="term");let c=t("pterm");c.hidden=!e.pty,c.classList.toggle("on",a.tab==="term"),c.setAttribute("aria-pressed",a.tab==="term"),c.onclick=()=>{a.tab=a.tab==="term"?a.lastTab:"term",Y(e,!0)},t("ptabs").innerHTML=r.map((d)=>`<button class="${d[1]===a.tab?"on":""}" data-k="${d[1]}">${d[0]}</button>`).join(""),[...t("ptabs").children].forEach((d)=>d.onclick=()=>{a.tab=d.dataset.k,Y(e,!0)});let f=a.tab,k=f==="preview"?it(a.runDoc.impl)&&rt(it(a.runDoc.impl),e):a.runDoc[f];if(f!=="term")fe();if(f==="term")Rt(e);else if(f==="notes"){let d=e.archived||e.status==="done"||e.status==="abandoned";Ot(e,k,d)}else t("pbody").innerHTML=k?ve(k,f==="backlog"):`<p style="color:var(--dim)">No ${{request:"request",plan:"plan",impl:"work report",preview:"preview",review:"review",backlog:"backlog"}[f]} yet.</p>`;if(f==="backlog")ds(e);if(f==="review")Tn(e);if(f==="notes"&&a.runDoc.linkedFrom&&a.runDoc.linkedFrom.length)t("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)}">${m(d)}</a></li>`).join("")}</ul>`);if(Ne&&!n)if(Ne.k===C(e))us(Ne);else Ne=null;let v=t("pfoot");if(n&&v.querySelector(".inl,[data-busy],.more[open]"))return;let h=Ut(e)+(e.archived?'<button class="btn pri" data-a="restore">Restore</button>':'<button class="btn ghost" data-a="archive">Archive</button>'),u=e.status==="waiting"&&e.awaiting==="approvals.plan"?pn(a.runDoc.plan):[],g=u||[],p=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")&&Kt[e.awaiting]){let d=Nt.has(It(e)),b=!u?" It has open questions the board cannot show: answer them in the terminal running this run, or approve and your agent asks them.":g.length?` It asks ${g.length===1?"one question":g.length+" questions"}: Approve opens ${g.length===1?"it":"them"}.`:"",x=Ut(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.":Kt[e.awaiting]+b+" Answering here opens a new agent session."}</span><details class="more"><summary class="btn ghost">More</summary><div class="menu-pop">${x}</div></details>${p}<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: ${m(Ge(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>${p}`;else if(e.status==="done")v.innerHTML=h+p;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>${qn(e)?'<button class="btn pri" data-a="resume">Resume</button>':""}`;v.querySelectorAll("[data-a]").forEach((d)=>d.onclick=()=>{let b=d.dataset.a;if(b==="archive"||b==="restore"){gt(e,b==="archive");return}if(b==="push"){Ft(e,"",d);return}if(b==="commit"){Gt(e);return}if(b==="approve"){if(g.length)jn(e,g);else vt(e,d);return}if(b==="revise"){Cn(e);return}if(b==="resume"){Ln(e,d);return}Q(),a.mode="reject",a.modeKey=C(e),E()})}var Ne=null;function Mn(){let e=new URLSearchParams(location.search),n=e.get("dir"),o=e.get("run"),l=e.get("idea");if(!o&&l===null)return;history.replaceState(null,"",location.pathname+"?all=1");let r=a.S.cards.find((v)=>v.state===n&&(o?v.kind==="run"&&v.id===o:v.kind==="idea"&&v.text===l));if(!r){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")||""),f=e.get("tab")||(c?c[1]==="finding"?"review":"backlog":""),k=En.find((v)=>v[1]===(f==="work"?"impl":f));if(k)a.tab=k[1];Ne=c?{type:c[1],n:+c[2],k:C(r)}:null,we(r,!!k)}function us(e){Ne=null;let n=(f)=>String(f).replace(/!?\[([^\]]*)\]\([^)\s]+\)/g,"$1").replace(/[`*]/g,"").replace(/\s+/g," ").trim(),o=[...(a.runDoc.backlog||"").matchAll(/^\s*-\s*(?:open|done|rejected):\s*(.*)$/gm)],l=e.type==="finding"?(a.runDoc.findings||[]).find((f)=>f.n===e.n)?.text:o[e.n-1]?.[1];if(!l){S("That item is not in this run");return}let r=n(l).slice(0,60),c=[...t("pbody").querySelectorAll("li,p")].find((f)=>!f.closest(".prog")&&n(f.textContent).includes(r));if(!c)return;c.scrollIntoView({block:"center"}),c.classList.add("hit"),setTimeout(()=>c.classList.remove("hit"),2400)}async function gt(e,n){if((await A("/archive",e.kind==="idea"?{dir:e.state,text:e.text,archived:n}:{dir:e.state,id:e.id,archived:n})).ok)S(n?"Archived":"Restored");Q(),D()}function Q(){if(_!=="board")return;let e=a.openKey;if(ae(!0),fe(),a.openKey=null,a.runDoc=null,t("panel").classList.remove("open"),t("scrim").classList.remove("on"),E(),e)Dt(e)?.focus({preventScroll:!0})}function Dn(){document.addEventListener("click",(e)=>{for(let n of document.querySelectorAll(".more[open]"))if(!n.contains(e.target))n.open=!1}),t("pclose").onclick=Q,t("scrim").onclick=Q}function Pn(){t("kbd").onclick=()=>t("help").classList.add("on"),t("help").onclick=()=>t("help").classList.remove("on"),document.addEventListener("click",()=>{if(a.menuKey||a.clearMenu)a.menuKey=null,a.clearMenu=!1,E()}),document.onkeydown=(e)=>{if(_==="sources"&&dt&&B()&&!t("help").classList.contains("on")&&!e.target.matches("input:not([type=checkbox]),select,textarea,[contenteditable]")&&!e.target.closest("#src-sec")&&dt(e))return;if(e.target.matches("input,select,textarea,[contenteditable]"))return;if(e.key==="?"){t("help").classList.toggle("on");return}if(e.key==="/"&&t("sq")&&t("panel").classList.contains("open")){e.preventDefault(),t("sq").focus();return}if(e.key==="Escape"){if(t("help").classList.remove("on"),a.mode)a.mode=null,E();else Q();return}if(e.key==="n"){if(_!=="board")ie("board",!0);a.mode="new",E()}if(e.key==="a")a.all=!a.all,Pe(a.all?null:a.S.dir),D();let n={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=n&&a.openKey&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&a.S.cards.find((c)=>C(c)===a.openKey&&c.kind==="run");if(o&&t("ptabs").children.length){if(e.preventDefault(),n[0]){let c=[...t("ptabs").children].map((v)=>v.dataset.k).concat(t("pterm").hidden?[]:["term"]),f=c.indexOf(a.tab),k=c[f<0?n[0]>0?0:c.length-1:Math.max(0,Math.min(c.length-1,f+n[0]))];if(k!==a.tab)a.tab=k,Y(o,!0)}else t("pbody").scrollBy({top:n[1]*40});return}if(n&&!t("panel").classList.contains("open")&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.target===document.body||e.target.matches("#board .card"))){e.preventDefault(),$n(...n);return}let l=a.openKey||Mt(),r=l&&a.S.cards.find((c)=>C(c)===l);if(!r)return;if(e.key==="e"&&r.kind==="idea")Q(),a.mode="edit",a.modeKey=C(r),E();if(e.key==="x"&&!["done","abandoned","starting"].includes(r.status))Q(),a.mode="reject",a.modeKey=C(r),E();if(e.key==="r"&&r.kind==="idea")Q(),a.mode="run",a.modeKey=C(r),E()}}var Bt=null;function Rn(){let e=new EventSource("/events");e.onmessage=(n)=>{if(n.data==="changed"){if(D(!0),B()&&!ct&&!t("src-go")?.disabled&&!t("src-add")?.disabled)R()}else if(n.data==="reload")location.reload();else if(n.data.startsWith("hello ")){let o=n.data.slice(6);if(Bt&&o!==Bt)location.reload();Bt=o}},e.onerror=()=>{e.close(),setTimeout(Rn,3000)}}var Ve=((e)=>e==="icebox"?"sources":e)(new URLSearchParams(location.search).get("view"));function Kn(){addEventListener("focus",()=>D(!0)),Lt(["settings","sources"].includes(Ve)?Ve:"board"),document.body.classList.toggle("paged",["settings","sources"].includes(Ve)),document.body.classList.toggle("ice",Ve==="sources"),(async()=>{let e;try{e=localStorage.getItem("kz-proj")}catch(n){}if(e&&!At)try{if((await fetch("/cd",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({dir:e})})).ok)a.all=!1;else Pe()}catch(n){}try{let n=JSON.parse(sessionStorage.getItem("kz-form"));if(n)[a.mode,a.modeKey]=n}catch(n){}D().then(()=>{ie(Ve),Mn()}),Rn()})()}Dn();bn();kn();hn();Pn();Kn();})();
30
+ `)[0].length<=72};r.oninput=f,f(),l.querySelector("[data-a=c]").onclick=()=>{l.innerHTML="",Y(e,!0)},c.onclick=()=>{if(!r.value.trim()){S("A commit message is required");return}Ft(e,r.value,c)},r.onkeydown=(k)=>{if(k.key==="Enter"&&!k.shiftKey)k.preventDefault(),c.click()}}async function Ft(e,n,o){o.disabled=!0,o.dataset.busy=1,o.textContent="Pushing…";let l=await A("/commit",{dir:e.state,id:e.id,message:n,files:a.runDoc.git.files});if(l.ok)S("Committed & pushed "+l.sha);if(t("pfoot").innerHTML="",a.openKey!==C(e))return;if(await Y(e,!1),!l.ok&&n&&a.openKey===C(e)&&a.runDoc.git.files&&a.runDoc.git.files.length)Gt(e,n)}var Mn=[["Request","request"],["Notes","notes"],["Plan","plan"],["Backlog","backlog"],["Work","impl"],["Review","review"],["Preview","preview"]];function we(e,n){if(_!=="board")ie("board",!0);if(ae(!0),he.clear(),a.openKey=C(e),t("panel").classList.add("open"),t("scrim").classList.add("on"),t("ptitle").textContent=e.kind==="idea"?xe(e.text).head:e.text,E(),t("pterm").hidden=!0,e.kind==="idea"){a.runDoc=null,t("psub").textContent="Idea · "+e.where,t("ptabs").innerHTML="",Rt(e);let o=t("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((l)=>l.onclick=()=>{let r=l.dataset.a;if(r==="archive"||r==="restore"){gt(e,r==="archive");return}let c=C(e);Q(),a.mode=r,a.modeKey=c,E()});return}if(!n)a.lastTab=a.tab=e.status==="abandoned"?"plan":{1:"plan",2:"backlog",3:"review",4:"preview"}[e.column]||"request";if(!n&&e.pty&&e.pty.alive&&e.column>=1&&e.column<=3)a.tab="term";Y(e,!1)}var Ke=null;function us(e){let n=e.column===2&&a.runDoc.progress,o=Ke&&Ke.id===e.id?Ke:null,l=n?fn(n,o&&o.st):"";if(!l)return;Ke={id:e.id,st:[...n.matchAll(/^\s*-\s*(todo|doing|done):/gm)].map((c)=>c[1]),w:+/width:(\d+)%/.exec(l)[1]},t("pbody").innerHTML=l+(a.runDoc.backlog?"<h3>Backlog</h3>"+ve(a.runDoc.backlog,!0):"");let r=t("pbody").querySelector(".pbar i");if(o&&o.w!==Ke.w)r.style.width=o.w+"%",r.offsetWidth,r.style.width=Ke.w+"%"}function Rt(e){if(e.pty)Pt(e);else fe(),Ot(e,e.notes,e.archived||e.status!=="idea")}async function Y(e,n,o){if(!n||!a.runDoc||a.runDoc.id!==e.id||o){let d=await fetch(`/run/${encodeURIComponent(e.id)}?dir=${encodeURIComponent(e.state)}`).then((b)=>b.json());if(n&&t("pfoot").querySelector(".inl,[data-busy],.more[open]"))return;a.runDoc=d}if(a.openKey!==C(e))return;e=a.S.cards.find((d)=>C(d)===a.openKey)||e;let l=t("pbody").querySelector(".notes textarea");if(n&&l&&document.activeElement===l)return;t("psub").textContent=e.id+" · "+(Fe[e.status]||e.status)+(e.awaiting?" · "+Ge(e.awaiting):"");let r=Mn.filter((d)=>d[1]==="request"||d[1]==="notes"||(d[1]==="preview"?it(a.runDoc.impl):a.runDoc[d[1]]||d[1]==="backlog"&&e.column===2&&a.runDoc.progress));if(e.pty&&a.wantTerm===C(e))a.wantTerm=null,a.tab="term";if(a.tab==="term"&&!e.pty)a.tab=a.lastTab;if(a.tab!=="term"&&!r.some((d)=>d[1]===a.tab))a.tab=[...r].reverse().find((d)=>d[1]!=="notes")[1];if(a.tab!=="term")a.lastTab=a.tab;t("panel").classList.toggle("wide",a.tab==="term");let c=t("pterm");c.hidden=!e.pty,c.classList.toggle("on",a.tab==="term"),c.setAttribute("aria-pressed",a.tab==="term"),c.onclick=()=>{a.tab=a.tab==="term"?a.lastTab:"term",Y(e,!0)},t("ptabs").innerHTML=r.map((d)=>`<button class="${d[1]===a.tab?"on":""}" data-k="${d[1]}">${d[0]}</button>`).join(""),[...t("ptabs").children].forEach((d)=>d.onclick=()=>{a.tab=d.dataset.k,Y(e,!0)});let f=a.tab,k=f==="preview"?it(a.runDoc.impl)&&rt(it(a.runDoc.impl),e):a.runDoc[f];if(f!=="term")fe();if(f==="term")Pt(e);else if(f==="notes"){let d=e.archived||e.status==="done"||e.status==="abandoned";Ot(e,k,d)}else t("pbody").innerHTML=k?ve(k,f==="backlog"):`<p style="color:var(--dim)">No ${{request:"request",plan:"plan",impl:"work report",preview:"preview",review:"review",backlog:"backlog"}[f]} yet.</p>`;if(f==="backlog")us(e);if(f==="review")En(e);if(f==="notes"&&a.runDoc.linkedFrom&&a.runDoc.linkedFrom.length)t("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)}">${m(d)}</a></li>`).join("")}</ul>`);if(Ne&&!n)if(Ne.k===C(e))ps(Ne);else Ne=null;let v=t("pfoot");if(n&&v.querySelector(".inl,[data-busy],.more[open]"))return;let h=Ut(e)+(e.archived?'<button class="btn pri" data-a="restore">Restore</button>':'<button class="btn ghost" data-a="archive">Archive</button>'),u=e.status==="waiting"&&e.awaiting==="approvals.plan"?pn(a.runDoc.plan):[],g=u||[],p=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")&&Kt[e.awaiting]){let d=Nt.has(It(e)),b=!u?" It has open questions the board cannot show: answer them in the terminal running this run, or approve and your agent asks them.":g.length?` It asks ${g.length===1?"one question":g.length+" questions"}: Approve opens ${g.length===1?"it":"them"}.`:"",x=Ut(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.":Kt[e.awaiting]+b+" Answering here opens a new agent session."}</span><details class="more"><summary class="btn ghost">More</summary><div class="menu-pop">${x}</div></details>${p}<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: ${m(Ge(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>${p}`;else if(e.status==="done")v.innerHTML=h+p;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>${Ln(e)?'<button class="btn pri" data-a="resume">Resume</button>':""}`;v.querySelectorAll("[data-a]").forEach((d)=>d.onclick=()=>{let b=d.dataset.a;if(b==="archive"||b==="restore"){gt(e,b==="archive");return}if(b==="push"){Ft(e,"",d);return}if(b==="commit"){Gt(e);return}if(b==="approve"){if(g.length)An(e,g);else vt(e,d);return}if(b==="revise"){jn(e);return}if(b==="resume"){Cn(e,d);return}Q(),a.mode="reject",a.modeKey=C(e),E()})}var Ne=null;function Dn(){let e=new URLSearchParams(location.search),n=e.get("dir"),o=e.get("run"),l=e.get("idea");if(!o&&l===null)return;history.replaceState(null,"",location.pathname+"?all=1");let r=a.S.cards.find((v)=>v.state===n&&(o?v.kind==="run"&&v.id===o:v.kind==="idea"&&v.text===l));if(!r){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")||""),f=e.get("tab")||(c?c[1]==="finding"?"review":"backlog":""),k=Mn.find((v)=>v[1]===(f==="work"?"impl":f));if(k)a.tab=k[1];Ne=c?{type:c[1],n:+c[2],k:C(r)}:null,we(r,!!k)}function ps(e){Ne=null;let n=(f)=>String(f).replace(/!?\[([^\]]*)\]\([^)\s]+\)/g,"$1").replace(/[`*]/g,"").replace(/\s+/g," ").trim(),o=[...(a.runDoc.backlog||"").matchAll(/^\s*-\s*(?:open|done|rejected):\s*(.*)$/gm)],l=e.type==="finding"?(a.runDoc.findings||[]).find((f)=>f.n===e.n)?.text:o[e.n-1]?.[1];if(!l){S("That item is not in this run");return}let r=n(l).slice(0,60),c=[...t("pbody").querySelectorAll("li,p")].find((f)=>!f.closest(".prog")&&n(f.textContent).includes(r));if(!c)return;c.scrollIntoView({block:"center"}),c.classList.add("hit"),setTimeout(()=>c.classList.remove("hit"),2400)}async function gt(e,n){if((await A("/archive",e.kind==="idea"?{dir:e.state,text:e.text,archived:n}:{dir:e.state,id:e.id,archived:n})).ok)S(n?"Archived":"Restored");Q(),D()}function Q(){if(_!=="board")return;let e=a.openKey;if(ae(!0),fe(),a.openKey=null,a.runDoc=null,t("panel").classList.remove("open"),t("scrim").classList.remove("on"),E(),e)Dt(e)?.focus({preventScroll:!0})}function Rn(){document.addEventListener("click",(e)=>{for(let n of document.querySelectorAll(".more[open]"))if(!n.contains(e.target))n.open=!1}),t("pclose").onclick=Q,t("scrim").onclick=Q}function Pn(){t("kbd").onclick=()=>t("help").classList.add("on"),t("help").onclick=()=>t("help").classList.remove("on"),document.addEventListener("click",()=>{if(a.menuKey||a.clearMenu)a.menuKey=null,a.clearMenu=!1,E()}),document.onkeydown=(e)=>{if(_==="sources"&&dt&&B()&&!t("help").classList.contains("on")&&!e.target.matches("input:not([type=checkbox]),select,textarea,[contenteditable]")&&!e.target.closest("#src-sec")&&dt(e))return;if(e.target.matches("input,select,textarea,[contenteditable]"))return;if(e.key==="?"){t("help").classList.toggle("on");return}if(e.key==="/"&&t("sq")&&t("panel").classList.contains("open")){e.preventDefault(),t("sq").focus();return}if(e.key==="Escape"){if(t("help").classList.remove("on"),a.mode)a.mode=null,E();else Q();return}if(e.key==="n"){if(_!=="board")ie("board",!0);a.mode="new",E()}if(e.key==="a")a.all=!a.all,Re(a.all?null:a.S.dir),D();let n={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=n&&a.openKey&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&a.S.cards.find((c)=>C(c)===a.openKey&&c.kind==="run");if(o&&t("ptabs").children.length){if(e.preventDefault(),n[0]){let c=[...t("ptabs").children].map((v)=>v.dataset.k).concat(t("pterm").hidden?[]:["term"]),f=c.indexOf(a.tab),k=c[f<0?n[0]>0?0:c.length-1:Math.max(0,Math.min(c.length-1,f+n[0]))];if(k!==a.tab)a.tab=k,Y(o,!0)}else t("pbody").scrollBy({top:n[1]*40});return}if(n&&!t("panel").classList.contains("open")&&!e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.target===document.body||e.target.matches("#board .card"))){e.preventDefault(),$n(...n);return}let l=a.openKey||Mt(),r=l&&a.S.cards.find((c)=>C(c)===l);if(!r)return;if(e.key==="e"&&r.kind==="idea")Q(),a.mode="edit",a.modeKey=C(r),E();if(e.key==="x"&&!["done","abandoned","starting"].includes(r.status))Q(),a.mode="reject",a.modeKey=C(r),E();if(e.key==="r"&&r.kind==="idea")Q(),a.mode="run",a.modeKey=C(r),E()}}var Bt=null;function Kn(){let e=new EventSource("/events");e.onmessage=(n)=>{if(n.data==="changed"){if(D(!0),B()&&!ct&&!t("src-go")?.disabled&&!t("src-add")?.disabled)P()}else if(n.data==="reload")location.reload();else if(n.data.startsWith("hello ")){let o=n.data.slice(6);if(Bt&&o!==Bt)location.reload();Bt=o}},e.onerror=()=>{e.close(),setTimeout(Kn,3000)}}var Ve=((e)=>e==="icebox"?"sources":e)(new URLSearchParams(location.search).get("view"));function Nn(){addEventListener("focus",()=>D(!0)),Lt(["settings","sources"].includes(Ve)?Ve:"board"),document.body.classList.toggle("paged",["settings","sources"].includes(Ve)),document.body.classList.toggle("ice",Ve==="sources"),(async()=>{let e;try{e=localStorage.getItem("kz-proj")}catch(n){}if(e&&!At)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 Re()}catch(n){}try{let n=JSON.parse(sessionStorage.getItem("kz-form"));if(n)[a.mode,a.modeKey]=n}catch(n){}D().then(()=>{ie(Ve),Dn()}),Kn()})()}Rn();bn();kn();hn();Pn();Nn();})();
package/web/notebook.js CHANGED
@@ -1,4 +1,4 @@
1
- /* kaizen notebook d387a44b8ead9df1 -- generated by bun run build:web from web/notebook.ts; do not edit */
1
+ /* kaizen notebook 002970592c17182f -- 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 d387a44b8ead9df1 -- generated by bun run build:web from web/term.ts; do not edit */
1
+ /* kaizen terminal 002970592c17182f -- generated by bun run build:web from web/term.ts; do not edit */
2
2
  (()=>{var{defineProperty:fr,getOwnPropertyDescriptor:tn}=Object,sn=(e,t)=>{for(var i in t)fr(e,i,{get:t[i],enumerable:!0})},V=(e,t,i,s)=>{for(var r=s>1?void 0:s?tn(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&&fr(t,i,r),r},b=(e,t)=>(i,s)=>t(i,s,e),ds="Terminal input",xi={get:()=>ds,set:(e)=>ds=e},_s="Too much output to announce, navigate to rows manually to read",Ei={get:()=>_s,set:(e)=>_s=e};function rn(e){return e.replace(/\r?\n/g,"\r")}function nn(e,t){return t?"\x1B[200~"+e+"\x1B[201~":e}function on(e,t){e.clipboardData&&e.clipboardData.setData("text/plain",t.selectionText),e.preventDefault()}function hn(e,t,i,s){if(e.stopPropagation(),e.clipboardData){let r=e.clipboardData.getData("text/plain");pr(r,t,i,s)}}function pr(e,t,i,s){e=rn(e),e=nn(e,i.decPrivateModes.bracketedPasteMode&&s.rawOptions.ignoreBracketedPasteMode!==!0),i.triggerDataEvent(e,!0),t.value=""}function gr(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 us(e,t,i,s,r){gr(e,t,i),r&&s.rightClickSelect(e),t.value=s.selectionText,t.select()}function Ke(e){return e>65535?(e-=65536,String.fromCharCode((e>>10)+55296)+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)}function ti(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 an=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 h=e.charCodeAt(n);56320<=h&&h<=57343?t[s++]=(o-55296)*1024+h-56320+65536:(t[s++]=o,t[s++]=h);continue}o!==65279&&(t[s++]=o)}return s}},ln=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,h,l=0,a=0;if(this.interim[0]){let _=!1,f=this.interim[0];f&=(f&224)===192?31:(f&240)===224?15:7;let u=0,p;for(;(p=this.interim[++u]&63)&&u<4;)f<<=6,f|=p;let C=(this.interim[0]&224)===192?2:(this.interim[0]&240)===224?3:4,D=C-u;for(;a<D;){if(a>=i)return 0;if(p=e[a++],(p&192)!==128){a--,_=!0;break}else this.interim[u++]=p,f<<=6,f|=p&63}_||(C===2?f<128?a--:t[s++]=f:C===3?f<2048||f>=55296&&f<=57343||f===65279||(t[s++]=f):f<65536||f>1114111||(t[s++]=f)),this.interim.fill(0)}let d=i-4,c=a;for(;c<i;){for(;c<d&&!((r=e[c])&128)&&!((n=e[c+1])&128)&&!((o=e[c+2])&128)&&!((h=e[c+3])&128);)t[s++]=r,t[s++]=n,t[s++]=o,t[s++]=h,c+=4;if(r=e[c++],r<128)t[s++]=r;else if((r&224)===192){if(c>=i)return this.interim[0]=r,s;if(n=e[c++],(n&192)!==128){c--;continue}if(l=(r&31)<<6|n&63,l<128){c--;continue}t[s++]=l}else if((r&240)===224){if(c>=i)return this.interim[0]=r,s;if(n=e[c++],(n&192)!==128){c--;continue}if(c>=i)return this.interim[0]=r,this.interim[1]=n,s;if(o=e[c++],(o&192)!==128){c--;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(c>=i)return this.interim[0]=r,s;if(n=e[c++],(n&192)!==128){c--;continue}if(c>=i)return this.interim[0]=r,this.interim[1]=n,s;if(o=e[c++],(o&192)!==128){c--;continue}if(c>=i)return this.interim[0]=r,this.interim[1]=n,this.interim[2]=o,s;if(h=e[c++],(h&192)!==128){c--;continue}if(l=(r&7)<<18|(n&63)<<12|(o&63)<<6|h&63,l<65536||l>1114111)continue;t[s++]=l}}return s}},vr="",Ue=" ",Bt=class e{constructor(){this.fg=0,this.bg=0,this.extended=new Vt}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}},Vt=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}},Se=class e extends Bt{constructor(){super(...arguments);this.content=0,this.fg=0,this.bg=0,this.extended=new Vt,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?Ke(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()]}},fs="di$target",Di="di$dependencies",li=new Map;function cn(e){return e[Di]||[]}function re(e){if(li.has(e))return li.get(e);let t=function(i,s,r){if(arguments.length!==3)throw Error("@IServiceName-decorator can only be used to decorate a parameter");dn(t,i,r)};return t._id=e,li.set(e,t),t}function dn(e,t,i){t[fs]===t?t[Di].push({id:e,index:i}):(t[Di]=[{id:e,index:i}],t[fs]=t)}var ce=re("BufferService"),mr=re("CoreMouseService"),et=re("CoreService"),_n=re("CharsetService"),ts=re("InstantiationService"),Sr=re("LogService"),de=re("OptionsService"),wr=re("OscLinkService"),un=re("UnicodeService"),Rt=re("DecorationService"),Bi=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 Se,o=i.getTrimmedLength(),h=-1,l=-1,a=!1;for(let d=0;d<o;d++)if(!(l===-1&&!i.hasContent(d))){if(i.loadCell(d,n),n.hasExtendedAttrs()&&n.extended.urlId)if(l===-1){l=d,h=n.extended.urlId;continue}else a=n.extended.urlId!==h;else l!==-1&&(a=!0);if(a||l!==-1&&d===o-1){let c=this._oscLinkService.getLinkData(h)?.uri;if(c){let _={start:{x:l+1,y:e},end:{x:d+(!a&&d===o-1?1:0),y:e}},f=!1;if(!r?.allowNonHttpProtocols)try{let u=new URL(c);["http:","https:"].includes(u.protocol)||(f=!0)}catch{f=!0}f||s.push({text:c,range:_,activate:(u,p)=>r?r.activate(u,p,_):fn(u,p),hover:(u,p)=>r?.hover?.(u,p,_),leave:(u,p)=>r?.leave?.(u,p,_)})}a=!1,n.hasExtendedAttrs()&&n.extended.urlId?(l=d,h=n.extended.urlId):(l=-1,h=-1)}}t(s)}};Bi=V([b(0,ce),b(1,de),b(2,wr)],Bi);function fn(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 ii=re("CharSizeService"),Ae=re("CoreBrowserService"),is=re("MouseService"),Oe=re("RenderService"),pn=re("SelectionService"),br=re("CharacterJoinerService"),rt=re("ThemeService"),yr=re("LinkProviderService"),gn=class{constructor(){this.listeners=[],this.unexpectedErrorHandler=function(e){setTimeout(()=>{throw e.stack?ps.isErrorNoTelemetry(e)?new ps(e.message+`