codedeck 0.5.0 → 0.5.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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -0
  3. package/dist/cli/commands/doctor.js +10 -2
  4. package/dist/cli/commands/doctor.js.map +1 -1
  5. package/dist/cli/commands/open.js +49 -12
  6. package/dist/cli/commands/open.js.map +1 -1
  7. package/dist/cli/commands/profile.js +191 -0
  8. package/dist/cli/commands/profile.js.map +1 -0
  9. package/dist/cli/commands/run.js +36 -5
  10. package/dist/cli/commands/run.js.map +1 -1
  11. package/dist/cli/commands/setup.js +175 -23
  12. package/dist/cli/commands/setup.js.map +1 -1
  13. package/dist/cli/index.js +2 -0
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/config/config.js +82 -2
  16. package/dist/config/config.js.map +1 -1
  17. package/dist/daemon/daemon.js +1 -0
  18. package/dist/daemon/daemon.js.map +1 -1
  19. package/dist/drivers/codex/parser.js +14 -10
  20. package/dist/drivers/codex/parser.js.map +1 -1
  21. package/dist/open/contract.js.map +1 -1
  22. package/dist/open/launchers/claude.js +8 -2
  23. package/dist/open/launchers/claude.js.map +1 -1
  24. package/dist/plugin/agents/auditor.md +2 -0
  25. package/dist/plugin/agents/general.md +10 -2
  26. package/dist/plugin/agents/orchestrator-edit.md +10 -2
  27. package/dist/plugin/agents/orchestrator-read.md +10 -2
  28. package/dist/plugin/agents/orchestrator.md +18 -3
  29. package/dist/plugin/prompts/_partials/dispatch.md +2 -0
  30. package/dist/plugin/prompts/_partials/reports-dispatch.md +5 -0
  31. package/dist/plugin/prompts/_partials/reports-general.md +5 -0
  32. package/dist/plugin/prompts/_partials/spec-gates.md +6 -0
  33. package/dist/plugin/prompts/_partials/worktree.md +1 -1
  34. package/dist/plugin/prompts/roles/general.md +1 -0
  35. package/dist/plugin/prompts/roles/orchestrator-edit.md +1 -0
  36. package/dist/plugin/prompts/roles/orchestrator-read.md +1 -0
  37. package/dist/plugin/prompts/roles/orchestrator.md +3 -1
  38. package/dist/web/canvas-page.js +79 -33
  39. package/dist/web/canvas-page.js.map +1 -1
  40. package/dist/web/review-page.js +121 -22
  41. package/dist/web/review-page.js.map +1 -1
  42. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  ---
2
- # DO NOT EDIT: generated from roles/orchestrator.md + partials (worktree, dispatch, proof).
2
+ # DO NOT EDIT: generated from roles/orchestrator.md + partials (worktree, dispatch, proof, spec-gates, reports-dispatch).
3
3
  # Do not hand-edit; edit the manifest or partials and rebuild.
4
4
  name: orchestrator
5
5
  description: Coordinate CodeDeck workers and track their state without doing the work yourself.
@@ -7,17 +7,32 @@ tools: Bash
7
7
  ---
8
8
 
9
9
  - Use `codedeck run --role <role> --worktree "<briefing>"` so the worker has an attributable worktree, diff, and role contract.
10
- - Worktree is a choice, not a default. `--worktree` is a fresh checkout of the current repo at HEAD, so it cannot reach another repository or an uncommitted working tree elsewhere. A slice that reproduces or fixes a bug in place, or that touches a different repo, runs `--no-worktree --cwd <target>` instead, on a harness whose file access can reach that target.
10
+ - Worktree is a choice, not a default. `--worktree` is a fresh checkout of the current repo at HEAD, so it cannot reach another repository, an uncommitted working tree, or gitignored/untracked files (e.g. `.specs/`) elsewhere. A slice that reproduces or fixes a bug in place, that touches a different repo, or names gitignored/untracked files runs `--no-worktree --cwd <target>` instead, on a harness whose file access can reach that target.
11
11
 
12
12
  - Always include `--role`. It selects the harness and model the human configured for that role. It also loads that role's contract into the worker prompt, including for non-Claude harnesses.
13
13
  - Without `--role`, `codedeck run` uses the default harness and sends only a loose briefing. It ignores the user's role binding and gives a more expensive worker less direction.
14
14
  - The role owns the harness and the model. `--agent` and `--model` are ignored for a bound role (run warns and keeps the binding), so you cannot swap the worker onto another harness. Changing the pairing is a `codedeck setup` decision, not a dispatch flag.
15
+ - Dispatch only via `run --role ... --bg --json`; `open` is interactive, never dispatch.
16
+ - `doctor` shows Role bindings; never probe `dist/` or `setup --help`, and an unbound role warns.
15
17
  - Slice by ownership. A worker owns its files end to end. Two workers in one file is a merge you will pay for.
16
18
  - Workers start with none of this context. The briefing carries the goal, the files it owns, the interface it must produce, what is out of scope, and how it verifies itself. Never write "see the conversation".
17
19
 
18
20
  - Verify before you claim.
19
21
  - Read `codedeck diff <id>` yourself before believing any worker. The artifact is authoritative, the success message is not.
20
22
 
23
+ ## Spec gates
24
+
25
+ - Ask for testable specs: each acceptance criterion holds one behavior, names a SHALL, and uses the shape that fits (WHEN trigger THEN response, WHILE state, WHERE flag, IF fault THEN handling, or a plain invariant). Send back criteria that bundle two behaviors or use vague words with no measurable outcome.
26
+ - Ask for gated tasks: each task points to its spec requirement and fills Tests plus Gate. Tests ship inside the task that writes the code, never parked in a later task. Tests none holds only for a layer the coverage matrix marks none.
27
+ - Ask for a short coverage matrix before Execute: one row per code layer touched, with test type, where the tests live, and the command that runs them. Treat the confirmed matrix as the authority for the run.
28
+ - Close each slice with proof: the spec named tests pass, plus one behavior fault in a scratch copy that the tests catch. Discard the scratch. Log kills and survivors in the closing report; survivors turn into fix slices.
29
+
30
+ ## Reports
31
+
32
+ - Offer a polished HTML technical report when the human asks for a report, whitepaper, architecture memo, or printable document, or when dense findings deserve a document instead of a long chat answer. Ask once, then dispatch it.
33
+ - Dispatch a general worker to build it with the `create-report` skill by name when its harness offers it, otherwise to produce the same single self-contained HTML file directly: inline CSS and SVG, no external dependencies, printable to PDF. Track the slice in the registry and confirm the artifact with `codedeck diff <id> --stat`.
34
+ - When the report covers this run, the briefing requires grounding in the accepted slices: session ids, diff stats, and quoted test output, never success messages alone.
35
+
21
36
  You are the CodeDeck orchestrator, and you run on the most capable and most expensive model in the chain. That is the whole reason you must not do the work. Every file you would read, every failure you would debug, every fix you would type is a token spent at the highest rate on something a cheaper general worker does just as well. Your value is coordination: plan the work, turn the request into briefings, fan out workers, verify what they report, integrate the slices, and decide what happens next. You dispatch the work. You never do it.
22
37
 
23
38
  ## Bash is your dispatch console, not a shell
@@ -77,7 +92,7 @@ You are the CodeDeck orchestrator, and you run on the most capable and most expe
77
92
  - Drive the whole run without being asked for each phase. The human asked for the outcome once. Phase transitions are your call, so never pause between them for confirmation.
78
93
  - Size it from the request, then commit to the size. Trivial (a couple of files, an obvious change): straight to implement plus verify plus the final review round. Anything shaped like a feature: the full loop below.
79
94
  - Specify: dispatch a worker to write `.specs/features/<slug>/spec.md` with the goal, the acceptance criteria, and what is out of scope. Design and Tasks go the same way when the work needs them: `design.md` for architecture calls, `tasks.md` for atomic tasks that each carry their Tests and Gate. You cannot write files, so workers write every artifact and you track each one in the registry.
80
- - Execute: dispatch the tasks in dependency order. Every briefing names the spec and task files as the source of truth, and tells the worker to activate the `tlc-spec-driven` skill by name when its harness offers it, otherwise to follow the briefing steps exactly.
95
+ - Execute: dispatch the tasks in dependency order. Every briefing names the spec and task files as the source of truth, and tells the worker to activate the `tlc-spec-driven` skill by name when its harness offers it, otherwise to follow the lean briefing steps exactly without pasting skill text.
81
96
  - Verify: a slice is done only when its spec-named tests pass and a bounded mutation probe passes with them. The probe: the worker injects a handful of behavior-level faults in scratch copies, confirms the tests kill each one, discards the scratch, and reports kills plus survivors. Survivors become fix slices, not excuses.
82
97
  - Review: run the final round yourself with `codedeck run --role reviewer --no-worktree "<briefing>" --bg --json` over the finished scope. Slice self-review never replaces it. Remediate every confirmed finding as a new slice, then at most one re-review. After that, report whatever still stands instead of looping.
83
98
  - Record decisions as you go: what you sized, what you scoped out, what the probes killed. They land in the closing report in one batch, never as questions mid-run.
@@ -1,5 +1,7 @@
1
1
  - Always include `--role`. It selects the harness and model the human configured for that role. It also loads that role's contract into the worker prompt, including for non-Claude harnesses.
2
2
  - Without `--role`, `codedeck run` uses the default harness and sends only a loose briefing. It ignores the user's role binding and gives a more expensive worker less direction.
3
3
  - The role owns the harness and the model. `--agent` and `--model` are ignored for a bound role (run warns and keeps the binding), so you cannot swap the worker onto another harness. Changing the pairing is a `codedeck setup` decision, not a dispatch flag.
4
+ - Dispatch only via `run --role ... --bg --json`; `open` is interactive, never dispatch.
5
+ - `doctor` shows Role bindings; never probe `dist/` or `setup --help`, and an unbound role warns.
4
6
  - Slice by ownership. A worker owns its files end to end. Two workers in one file is a merge you will pay for.
5
7
  - Workers start with none of this context. The briefing carries the goal, the files it owns, the interface it must produce, what is out of scope, and how it verifies itself. Never write "see the conversation".
@@ -0,0 +1,5 @@
1
+ ## Reports
2
+
3
+ - Offer a polished HTML technical report when the human asks for a report, whitepaper, architecture memo, or printable document, or when dense findings deserve a document instead of a long chat answer. Ask once, then dispatch it.
4
+ - Dispatch a general worker to build it with the `create-report` skill by name when its harness offers it, otherwise to produce the same single self-contained HTML file directly: inline CSS and SVG, no external dependencies, printable to PDF. Track the slice in the registry and confirm the artifact with `codedeck diff <id> --stat`.
5
+ - When the report covers this run, the briefing requires grounding in the accepted slices: session ids, diff stats, and quoted test output, never success messages alone.
@@ -0,0 +1,5 @@
1
+ ## Reports
2
+
3
+ - Offer a polished HTML technical report when the human asks for a report, whitepaper, architecture memo, or printable document, or when dense findings deserve a document instead of a long chat answer. Ask once, then build it.
4
+ - Use the `create-report` skill by name when the harness offers it, otherwise produce the same single self-contained HTML file directly: inline CSS and SVG, no external dependencies, printable to PDF.
5
+ - When the report covers codedeck work, ground it in `codedeck diff --stat`, log lines, and quoted test output, never in success messages alone.
@@ -0,0 +1,6 @@
1
+ ## Spec gates
2
+
3
+ - Ask for testable specs: each acceptance criterion holds one behavior, names a SHALL, and uses the shape that fits (WHEN trigger THEN response, WHILE state, WHERE flag, IF fault THEN handling, or a plain invariant). Send back criteria that bundle two behaviors or use vague words with no measurable outcome.
4
+ - Ask for gated tasks: each task points to its spec requirement and fills Tests plus Gate. Tests ship inside the task that writes the code, never parked in a later task. Tests none holds only for a layer the coverage matrix marks none.
5
+ - Ask for a short coverage matrix before Execute: one row per code layer touched, with test type, where the tests live, and the command that runs them. Treat the confirmed matrix as the authority for the run.
6
+ - Close each slice with proof: the spec named tests pass, plus one behavior fault in a scratch copy that the tests catch. Discard the scratch. Log kills and survivors in the closing report; survivors turn into fix slices.
@@ -1,2 +1,2 @@
1
1
  - Use `codedeck run --role <role> --worktree "<briefing>"` so the worker has an attributable worktree, diff, and role contract.
2
- - Worktree is a choice, not a default. `--worktree` is a fresh checkout of the current repo at HEAD, so it cannot reach another repository or an uncommitted working tree elsewhere. A slice that reproduces or fixes a bug in place, or that touches a different repo, runs `--no-worktree --cwd <target>` instead, on a harness whose file access can reach that target.
2
+ - Worktree is a choice, not a default. `--worktree` is a fresh checkout of the current repo at HEAD, so it cannot reach another repository, an uncommitted working tree, or gitignored/untracked files (e.g. `.specs/`) elsewhere. A slice that reproduces or fixes a bug in place, that touches a different repo, or names gitignored/untracked files runs `--no-worktree --cwd <target>` instead, on a harness whose file access can reach that target.
@@ -9,6 +9,7 @@ includes:
9
9
  - rename-run
10
10
  - commit
11
11
  - pr-writer
12
+ - reports-general
12
13
  ---
13
14
 
14
15
  You are the CodeDeck general session. You do the work yourself, here. Delegation is a tool you reach for when slices are genuinely independent, not a rule you follow.
@@ -6,6 +6,7 @@ includes:
6
6
  - worktree
7
7
  - dispatch
8
8
  - proof
9
+ - reports-dispatch
9
10
  ---
10
11
 
11
12
  You are the CodeDeck orchestrator, and you run on the most capable and most expensive model in the chain. Your job is to coordinate the request: plan the work, turn it into briefings, dispatch workers, verify what they report, integrate the slices, and decide what happens next. Keep the whole run moving and make sure the requested result is complete and evidenced.
@@ -6,6 +6,7 @@ includes:
6
6
  - worktree
7
7
  - dispatch
8
8
  - proof
9
+ - reports-dispatch
9
10
  ---
10
11
 
11
12
  You are the CodeDeck orchestrator, and you run on the most capable and most expensive model in the chain. Your job is to coordinate the request: plan the work, turn it into briefings, dispatch workers, verify what they report, integrate the slices, and decide what happens next. Keep the whole run moving and make sure the requested result is complete and evidenced.
@@ -6,6 +6,8 @@ includes:
6
6
  - worktree
7
7
  - dispatch
8
8
  - proof
9
+ - spec-gates
10
+ - reports-dispatch
9
11
  ---
10
12
 
11
13
  You are the CodeDeck orchestrator, and you run on the most capable and most expensive model in the chain. That is the whole reason you must not do the work. Every file you would read, every failure you would debug, every fix you would type is a token spent at the highest rate on something a cheaper general worker does just as well. Your value is coordination: plan the work, turn the request into briefings, fan out workers, verify what they report, integrate the slices, and decide what happens next. You dispatch the work. You never do it.
@@ -67,7 +69,7 @@ You are the CodeDeck orchestrator, and you run on the most capable and most expe
67
69
  - Drive the whole run without being asked for each phase. The human asked for the outcome once. Phase transitions are your call, so never pause between them for confirmation.
68
70
  - Size it from the request, then commit to the size. Trivial (a couple of files, an obvious change): straight to implement plus verify plus the final review round. Anything shaped like a feature: the full loop below.
69
71
  - Specify: dispatch a worker to write `.specs/features/<slug>/spec.md` with the goal, the acceptance criteria, and what is out of scope. Design and Tasks go the same way when the work needs them: `design.md` for architecture calls, `tasks.md` for atomic tasks that each carry their Tests and Gate. You cannot write files, so workers write every artifact and you track each one in the registry.
70
- - Execute: dispatch the tasks in dependency order. Every briefing names the spec and task files as the source of truth, and tells the worker to activate the `tlc-spec-driven` skill by name when its harness offers it, otherwise to follow the briefing steps exactly.
72
+ - Execute: dispatch the tasks in dependency order. Every briefing names the spec and task files as the source of truth, and tells the worker to activate the `tlc-spec-driven` skill by name when its harness offers it, otherwise to follow the lean briefing steps exactly without pasting skill text.
71
73
  - Verify: a slice is done only when its spec-named tests pass and a bounded mutation probe passes with them. The probe: the worker injects a handful of behavior-level faults in scratch copies, confirms the tests kill each one, discards the scratch, and reports kills plus survivors. Survivors become fix slices, not excuses.
72
74
  - Review: run the final round yourself with `codedeck run --role reviewer --no-worktree "<briefing>" --bg --json` over the finished scope. Slice self-review never replaces it. Remediate every confirmed finding as a new slice, then at most one re-review. After that, report whatever still stands instead of looping.
73
75
  - Record decisions as you go: what you sized, what you scoped out, what the probes killed. They land in the closing report in one batch, never as questions mid-run.
@@ -42,12 +42,17 @@ export const CANVAS_PAGE = `<!doctype html>
42
42
  .dot.resting { background: #636366; }
43
43
  .dot.done { background: #3a3a3c; }
44
44
  .dot.failed { background: #ff453a; box-shadow: 0 0 8px rgba(255,69,58,.8); }
45
- #feed { left: 16px; top: 112px; width: 300px; }
45
+ #feed { left: 16px; top: 112px; width: 330px; }
46
46
  #feed h2 { font-size: 11px; letter-spacing: .06em; color: #98989f; margin: 0 0 8px; }
47
- #feed ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
48
- #feed li { font-size: 12.5px; color: #98989f; display: flex; gap: 8px; animation: feedIn .3s ease-out; }
49
- #feed li b { color: #f5f5f7; white-space: nowrap; }
47
+ #feed ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
48
+ #feed li { font-size: 12px; color: #98989f; display: flex; align-items: baseline; gap: 7px; animation: feedIn .3s ease-out; line-height: 1.35; }
49
+ #feed li b { color: #f5f5f7; white-space: nowrap; font-weight: 600; font-size: 12px; }
50
50
  #feed li.fresh { color: #f5f5f7; }
51
+ #feed li.warn { color: #ff9f0a; }
52
+ #feed li.bad { color: #ff453a; }
53
+ #feed .feed-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 11.5px; }
54
+ #feed .feed-count { font-size: 11px; color: #636366; font-weight: 600; margin-left: 2px; white-space: nowrap; flex-shrink: 0; font-family: system-ui, sans-serif; }
55
+ #feed li.fresh .feed-count { color: #8e8e93; }
51
56
  @keyframes feedIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; } }
52
57
  #hint { left: 50%; transform: translateX(-50%); bottom: 16px; font-size: 12px; color: #98989f; white-space: nowrap; }
53
58
  #detail { position: fixed; top: 0; right: 0; bottom: 0; width: min(400px, 94vw); z-index: 20;
@@ -97,14 +102,15 @@ export const CANVAS_PAGE = `<!doctype html>
97
102
  .node:active { cursor: grabbing; }
98
103
  .node { transform-origin: 0 0; }
99
104
  .node.orch { width: 228px; border-color: rgba(255,255,255,.18); }
100
- .node .row { display: flex; align-items: center; gap: 8px; }
105
+ .node .row { display: flex; align-items: center; gap: 8px; min-width: 0; }
106
+ .node .row .dot { flex-shrink: 0; }
101
107
  .node .logo { width: 22px; height: 22px; display: inline-flex; flex-shrink: 0; }
102
108
  .node .logo svg { width: 22px; height: 22px; fill: #f5f5f7; }
103
109
  .node.orch .logo { width: 26px; height: 26px; }
104
110
  .node.orch .logo svg { width: 26px; height: 26px; }
105
- .node .name { font-size: 13.5px; font-weight: 650; }
106
- .node .sub { font-size: 12px; color: #98989f; margin-top: 3px; }
107
- .node .act { font-size: 12px; color: #98989f; margin-top: 5px; min-height: 15px; }
111
+ .node .name { font-size: 13.5px; font-weight: 650; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
112
+ .node .sub { font-size: 12px; color: #98989f; margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
113
+ .node .act { font-size: 12px; color: #98989f; margin-top: 5px; min-height: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
108
114
  .node .act b { color: #f5f5f7; }
109
115
  .node.alert { border-color: rgba(255,159,10,.65); animation: alertPulse 1.6s ease-in-out infinite; }
110
116
  @keyframes alertPulse { 0%,100% { border-color: rgba(255,159,10,.65); } 50% { border-color: rgba(255,159,10,1); } }
@@ -176,28 +182,41 @@ function toolHuman(name) {
176
182
  if (name === "bash" || name === "Bash") return "terminal";
177
183
  return name;
178
184
  }
185
+ function shortPath(s) {
186
+ if (!s || typeof s !== "string") return "";
187
+ var clean = s.replace(/\\\\/g, "/").replace(/\\/\\/+$/, "");
188
+ var parts = clean.split("/").filter(Boolean);
189
+ if (parts.length > 2) return parts.slice(-2).join("/");
190
+ return parts.join("/") || s;
191
+ }
179
192
  /* Pedaco do input real da ferramenta: primeira string interessante, cortada.
180
193
  "terminal" sozinho nao diz nada; "podman ps" diz. */
181
194
  function inputSnippet(input) {
182
195
  if (!input) return "";
183
196
  if (typeof input === "string") return clip(input);
184
197
  if (typeof input !== "object") return "";
185
- var keys = ["command", "cmd", "file", "path", "query", "pattern", "message", "text", "url"];
186
- for (var i = 0; i < keys.length; i++) {
187
- var v = input[keys[i]];
188
- if (typeof v === "string" && v.trim() !== "") return clip(v);
198
+ if (Array.isArray(input.todos)) return "(" + input.todos.length + " tarefas)";
199
+ var pathKeys = ["filePath", "file_path", "filename", "file", "path", "target", "dest", "directory", "dir"];
200
+ for (var p = 0; p < pathKeys.length; p++) {
201
+ var pv = input[pathKeys[p]];
202
+ if (typeof pv === "string" && pv.trim() !== "") return clip(shortPath(pv.trim()));
203
+ }
204
+ var textKeys = ["command", "cmd", "script", "query", "pattern", "regex", "title", "message", "text", "prompt", "url"];
205
+ for (var t = 0; t < textKeys.length; t++) {
206
+ var tv = input[textKeys[t]];
207
+ if (typeof tv === "string" && tv.trim() !== "") return clip(tv.trim());
189
208
  }
190
209
  return "";
191
210
  }
192
211
  function clip(s) {
193
- var one = String(s).replace(/\s+/g, " ").trim();
194
- return one.length > 42 ? one.slice(0, 42) + "…" : one;
212
+ var one = String(s).replace(/\\s+/g, " ").trim();
213
+ return one.length > 40 ? one.slice(0, 40) + "…" : one;
195
214
  }
196
215
  function toolLabel(tool) {
197
216
  var name = (tool && tool.name) || "";
198
- var snip = inputSnippet(tool && tool.input);
199
- if (!name) return snip || "uma tarefa";
200
- if (name === "bash" || name === "Bash") return snip ? '"' + snip + '"' : "terminal";
217
+ var snip = (tool && tool.title && typeof tool.title === "string") ? clip(tool.title) : inputSnippet(tool && tool.input);
218
+ if (!name) return snip || "tarefa";
219
+ if (name === "bash" || name === "Bash") return snip ? "$ " + snip : "terminal";
201
220
  return snip ? name + " " + snip : name;
202
221
  }
203
222
  function timeAgo(iso) {
@@ -317,17 +336,42 @@ function paintNode(n) {
317
336
  b.textContent = STATUS_HUMAN[n.status] || "Encerrada";
318
337
  n.actEl.appendChild(b);
319
338
  n.actEl.appendChild(document.createTextNode(" · " + n.activity));
339
+ n.actEl.title = (STATUS_HUMAN[n.status] || "") + (n.activity ? " · " + n.activity : "");
320
340
  n.el.classList.toggle("orch", !!n.isOrch);
321
341
  n.el.classList.toggle("alert", n.status === "needs_input");
322
342
  n.el.title = "sessão " + n.id + (n.label ? " · " + n.label : "");
323
343
  }
324
- function feed(agent, text) {
344
+ function feed(agent, text, kind) {
345
+ if (!text) return;
346
+ var top = feedList.firstElementChild;
347
+ if (top && top._agent === agent && top._text === text) {
348
+ var count = (top._count || 1) + 1;
349
+ top._count = count;
350
+ var badge = top.querySelector(".feed-count");
351
+ if (!badge) {
352
+ badge = document.createElement("span");
353
+ badge.className = "feed-count";
354
+ top.appendChild(badge);
355
+ }
356
+ badge.textContent = "×" + count;
357
+ top.classList.remove("fresh");
358
+ void top.offsetWidth;
359
+ top.classList.add("fresh");
360
+ return;
361
+ }
325
362
  var li = document.createElement("li");
326
- li.className = "fresh";
363
+ li.className = "fresh" + (kind ? " " + kind : "");
364
+ li._agent = agent;
365
+ li._text = text;
366
+ li._count = 1;
367
+ li.title = agent + ": " + text;
327
368
  var b = document.createElement("b");
328
369
  b.textContent = agent;
329
370
  li.appendChild(b);
330
- li.appendChild(document.createTextNode(text));
371
+ var span = document.createElement("span");
372
+ span.className = "feed-text";
373
+ span.textContent = text;
374
+ li.appendChild(span);
331
375
  feedList.insertBefore(li, feedList.firstChild);
332
376
  while (feedList.children.length > 6) feedList.removeChild(feedList.lastChild);
333
377
  var items = feedList.children;
@@ -563,15 +607,16 @@ function onEvent(n, ev) {
563
607
  spawnRing(n, "#0a84ff");
564
608
  spawnFlow(n, "#0a84ff", 1);
565
609
  touch(n, toolLabel(ev.tool));
566
- feed(agentName(n.agent), "começou: " + toolLabel(ev.tool));
610
+ feed(agentName(n.agent), toolLabel(ev.tool));
567
611
  } else if (ev.type === "tool.completed") {
568
612
  var bad = ev.tool && ev.tool.success === false;
569
613
  touch(n, toolLabel(ev.tool) + (bad ? " (com erro)" : " ✓"));
570
- if (bad) feed(agentName(n.agent), "ferramenta falhou");
614
+ if (bad) feed(agentName(n.agent), "falhou: " + toolLabel(ev.tool), "bad");
571
615
  } else if (ev.type === "message") {
572
616
  if (ev.role === "assistant") {
573
617
  touch(n, "respondendo");
574
- feed(agentName(n.agent), String(ev.content || "").slice(0, 90));
618
+ var textSnippet = clip(ev.content || "");
619
+ if (textSnippet) feed(agentName(n.agent), textSnippet);
575
620
  }
576
621
  } else if (ev.type === "message.queued") {
577
622
  touch(n, "mensagem na fila");
@@ -580,20 +625,20 @@ function onEvent(n, ev) {
580
625
  } else if (ev.type === "turn.started") {
581
626
  if (n.id && n.id === detailId && detailPending) { detailPending = null; paintSendState(); }
582
627
  } else if (ev.type === "turn.completed") {
583
- feed(agentName(n.agent), "etapa concluída");
628
+ // turn completion is recorded on node activity
584
629
  } else if (ev.type === "file.changed") {
585
- feed(agentName(n.agent), "mudou " + (ev.path || "um arquivo"));
630
+ feed(agentName(n.agent), "salvou " + (shortPath(ev.path === "unknown" ? "" : ev.path) || "arquivo"));
586
631
  } else if (ev.type === "permission.requested") {
587
632
  n.status = "needs_input";
588
633
  touch(n, "quer sua aprovação para continuar");
589
634
  paintSummary();
590
635
  spawnRing(n, "#ff9f0a");
591
- feed(agentName(n.agent), "pedindo sua aprovação");
636
+ feed(agentName(n.agent), "precisa de aprovação", "warn");
592
637
  } else if (ev.type === "session.completed" || ev.type === "session.failed") {
593
638
  n.status = ev.type === "session.failed" ? "failed" : "completed";
594
639
  touch(n, ev.type === "session.failed" ? ("falhou: " + (ev.error || "")) : "concluída");
595
640
  paintSummary();
596
- feed(agentName(n.agent), ev.type === "session.failed" ? "falhou" : "concluída");
641
+ feed(agentName(n.agent), ev.type === "session.failed" ? "falhou" : "concluída", ev.type === "session.failed" ? "bad" : "");
597
642
  // Stop/release cancels the queue server-side: drop a stale "na fila"
598
643
  // hint on the open detail, mirroring the turn.started branch.
599
644
  if (n.id && n.id === detailId && detailPending) { detailPending = null; paintSendState(); }
@@ -671,6 +716,7 @@ function frame() {
671
716
  if (nodes[i].runId === n.runId && nodes[i].isOrch) { o = nodes[i]; break; }
672
717
  }
673
718
  if (!o) return;
719
+ if (hideDone && isDone(o.status)) return;
674
720
  var e = edgeCurve(n, o);
675
721
  var pa = w2s(e.ax, e.ay), pb = w2s(e.bx, e.by), pc = w2s(e.cx, e.cy);
676
722
  ctx.beginPath();
@@ -940,8 +986,8 @@ function selectSession(id) {
940
986
  var repo = s.repository ? String(s.repository).split("/").pop() : "";
941
987
  var items = [
942
988
  ["Modelo", s.model || "—"],
943
- // effort so existe quando a sessao nasceu com --effort; ausente = padrao.
944
- ["Esforço", s.effort || "padrão"],
989
+ // effort chega sempre para claude/codex/omp/antigravity; ausente = opencode (ignora) ou sessão legada.
990
+ ["Esforço", s.effort || "n/a"],
945
991
  ["Repo", repo || "—"],
946
992
  ["Atividade", s.lastEvent ? (lastEventHuman(s.lastEvent) || s.lastEvent) : "—"],
947
993
  ];
@@ -973,14 +1019,14 @@ function selectSession(id) {
973
1019
  } else if (ev.type === "message" && ev.role === "assistant" && ev.content) {
974
1020
  chat.appendChild(chatMsg(agentName(s.agent), String(ev.content), "assistant"));
975
1021
  } else if (ev.type === "tool.started") {
976
- chat.appendChild(chatEvt("começou: " + toolLabel(ev.tool)));
1022
+ chat.appendChild(chatEvt(toolLabel(ev.tool)));
977
1023
  } else if (ev.type === "tool.completed") {
978
1024
  var bad = ev.tool && ev.tool.success === false;
979
- chat.appendChild(chatEvt("terminou: " + toolLabel(ev.tool) + (bad ? " (com erro)" : "")));
1025
+ if (bad) chat.appendChild(chatEvt("falhou: " + toolLabel(ev.tool)));
980
1026
  } else if (ev.type === "permission.requested") {
981
- chat.appendChild(chatEvt("pediu aprovação: " + (ev.tool || "uma ação")));
1027
+ chat.appendChild(chatEvt("precisa de aprovação: " + (ev.tool || "uma ação")));
982
1028
  } else if (ev.type === "file.changed") {
983
- chat.appendChild(chatEvt("mudou " + (ev.path || "um arquivo")));
1029
+ chat.appendChild(chatEvt("salvou " + (shortPath(ev.path === "unknown" ? "" : ev.path) || "arquivo")));
984
1030
  }
985
1031
  }
986
1032
  if (!chat.children.length) {
@@ -1 +1 @@
1
- {"version":3,"file":"canvas-page.js","sourceRoot":"","sources":["../../src/web/canvas-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAuiC3B,CAAC"}
1
+ {"version":3,"file":"canvas-page.js","sourceRoot":"","sources":["../../src/web/canvas-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAqlC3B,CAAC"}
@@ -58,6 +58,16 @@ export const REVIEW_PAGE = `<!doctype html>
58
58
  tr.add td.code { background: rgba(48,209,88,.10); }
59
59
  tr.del td.code { background: rgba(255,69,58,.10); }
60
60
  tr.add td.no.new { color: #30d158; } tr.del td.no.old { color: #ff453a; }
61
+ .tok-k { color: #ff7b72; }
62
+ .tok-s { color: #a5d6ff; }
63
+ .tok-c { color: #8b949e; font-style: italic; }
64
+ .tok-n { color: #79c0ff; }
65
+ .tok-f { color: #d2a8ff; }
66
+ .sign { color: #636366; user-select: none; }
67
+ tr.chg td.code { background: rgba(255,159,10,.06); }
68
+ .wd, .wd-del { border-radius: 3px; }
69
+ .wd { background: rgba(48,209,88,.38); }
70
+ .wd-del { background: rgba(255,69,58,.38); text-decoration: line-through; }
61
71
  tr.cbox td { padding: 8px 12px 10px 54px; background: rgba(10,132,255,.07); }
62
72
  .cbox textarea { width: 100%; min-height: 64px; background: rgba(255,255,255,.06);
63
73
  border: 1px solid rgba(255,255,255,.14); border-radius: 8px; color: #f5f5f7;
@@ -67,7 +77,7 @@ export const REVIEW_PAGE = `<!doctype html>
67
77
  border-radius: 8px; padding: 6px 12px; cursor: pointer; font-family: inherit; font-size: 12px; }
68
78
  .cbox button.ghost { background: transparent; }
69
79
  .cbox .ok { font-size: 11.5px; color: #30d158; }
70
- .hasNote td.no.new, .hasNote td.no.old { text-decoration: underline dotted; text-underline-offset: 3px; }
80
+ .hasNote td.no { text-decoration: underline dotted; text-underline-offset: 3px; }
71
81
  tr.sel td.code { background: rgba(10,132,255,.20); }
72
82
  tr.sel td.no { background: rgba(10,132,255,.35); color: #fff; }
73
83
  tr.thread td { padding: 6px 12px 10px 54px; }
@@ -128,6 +138,75 @@ function langOf(p) {
128
138
  xml: "xml", json: "json", yaml: "yaml", yml: "yaml", toml: "toml", md: "md", sh: "bash", sql: "sql" };
129
139
  return map[ext] || "";
130
140
  }
141
+ /* Minimal inline syntax highlight (kept dependency-free so the page stays
142
+ * self-contained/offline): single-pass tokenizer, strings before comments so
143
+ * "//" inside a string is not a comment. md/plain stays uncolored. */
144
+ var HI_C = /(\\/\\*[\\s\\S]*?\\*\\/|<!--[\\s\\S]*?-->)|("(?:[^"\\\\\\n]|\\\\.)*"|'(?:[^'\\\\\\n]|\\\\.)*'|\`(?:[^\`\\\\]|\\\\.)*\`)|(\\/\\/[^\\n]*)|\\b(\\d+(?:\\.\\d+)?)\\b|\\b(break|case|catch|class|const|continue|def|delete|do|else|elif|enum|export|extends|false|finally|for|from|function|if|import|in|instanceof|interface|let|new|null|return|struct|switch|this|throw|true|try|type|typeof|var|void|while|with|as|async|await|fn|impl|match|mut|pub|self|package|func|go|defer|nil|lambda|pass|raise|yield|private|public|protected|static|final|int|float|double|char|boolean|string|number|select|chan)\\b|([A-Za-z_$][\\w$]*)(?=\\()/g;
145
+ var HI_H = /("""[\\s\\S]*?"""|"(?:[^"\\\\\\n]|\\\\.)*"|'(?:[^'\\\\\\n]|\\\\.)*'|\`(?:[^\`\\\\]|\\\\.)*\`)|(#[^\\n]*)|\\b(\\d+(?:\\.\\d+)?)\\b|\\b(break|case|catch|class|const|continue|def|delete|do|else|elif|enum|export|extends|false|finally|for|from|function|if|import|in|instanceof|interface|let|new|null|return|struct|switch|this|throw|true|try|type|typeof|var|void|while|with|as|async|await|fn|impl|match|mut|pub|self|package|func|go|defer|nil|lambda|pass|raise|yield|private|public|protected|static|final|int|float|double|char|boolean|string|number|select|chan)\\b|([A-Za-z_$][\\w$]*)(?=\\()/g;
146
+ function hi(code, lang) {
147
+ if (!code) return "";
148
+ if (!lang || lang === "md") return esc(code);
149
+ var hash = lang === "py" || lang === "bash" || lang === "yaml" || lang === "toml" || lang === "ruby" || lang === "dockerfile";
150
+ var re = hash ? HI_H : HI_C;
151
+ re.lastIndex = 0;
152
+ var out = "", last = 0, m, cls;
153
+ while ((m = re.exec(code))) {
154
+ out += esc(code.slice(last, m.index));
155
+ if (hash) cls = m[1] ? "tok-s" : m[2] ? "tok-c" : m[3] ? "tok-n" : m[4] ? "tok-k" : "tok-f";
156
+ else cls = m[1] ? "tok-c" : m[2] ? "tok-s" : m[3] ? "tok-c" : m[4] ? "tok-n" : m[5] ? "tok-k" : "tok-f";
157
+ out += '<span class="' + cls + '">' + esc(m[0]) + "</span>";
158
+ last = m.index + m[0].length;
159
+ }
160
+ return out + esc(code.slice(last));
161
+ }
162
+ /* Merged word-diff for paired del/add lines (same count in one change block):
163
+ * one row per line number: removed tokens struck (.wd-del), added (.wd).
164
+ * Token-count cap falls back to plain highlight on minified-line pairs. */
165
+ function toks(s) {
166
+ var parts = String(s).match(/\\w+|\\s+|[^\\w\\s]/g) || [];
167
+ var out = [], pos = 0, k;
168
+ for (k = 0; k < parts.length; k++) { out.push({ t: parts[k], s: pos, e: pos + parts[k].length }); pos += parts[k].length; }
169
+ return out;
170
+ }
171
+ function mergeWords(oldCode, newCode, lang) {
172
+ var A = toks(oldCode), B = toks(newCode);
173
+ if (!A.length || !B.length) return hi(newCode, lang);
174
+ if (A.length * B.length > 10000) return hi(newCode, lang);
175
+ var n = A.length, m = B.length, i, j;
176
+ var len = [];
177
+ for (i = 0; i <= n; i++) { len.push([]); for (j = 0; j <= m; j++) len[i].push(0); }
178
+ for (i = n - 1; i >= 0; i--) {
179
+ for (j = m - 1; j >= 0; j--) {
180
+ len[i][j] = A[i].t === B[j].t ? len[i + 1][j + 1] + 1 : Math.max(len[i + 1][j], len[i][j + 1]);
181
+ }
182
+ }
183
+ var fa = [], fb = [];
184
+ for (i = 0; i < n; i++) fa.push(true);
185
+ for (j = 0; j < m; j++) fb.push(true);
186
+ i = 0; j = 0;
187
+ while (i < n && j < m) {
188
+ if (A[i].t === B[j].t) { fa[i] = false; fb[j] = false; i++; j++; }
189
+ else if (len[i + 1][j] >= len[i][j + 1]) i++;
190
+ else j++;
191
+ }
192
+ var out = "";
193
+ i = 0; j = 0;
194
+ while (i < n || j < m) {
195
+ if (i < n && j < m && !fa[i] && !fb[j]) {
196
+ var ei = i, ej = j;
197
+ while (ei < n && ej < m && !fa[ei] && !fb[ej]) { ei++; ej++; }
198
+ out += hi(newCode.slice(B[j].s, B[ej - 1].e), lang);
199
+ i = ei; j = ej;
200
+ } else {
201
+ var oi = i, oj = j;
202
+ while (i < n && fa[i]) i++;
203
+ while (j < m && fb[j]) j++;
204
+ if (oi < i) out += '<span class="wd-del">' + hi(oldCode.slice(A[oi].s, A[i - 1].e), lang) + "</span>";
205
+ if (oj < j) out += '<span class="wd">' + hi(newCode.slice(B[oj].s, B[j - 1].e), lang) + "</span>";
206
+ }
207
+ }
208
+ return out;
209
+ }
131
210
  /* Drafts: key codedeck-review:<file>:<line>[-<endLine>] -> { code, body, deleted } */
132
211
  function draftKey(file, line, endLine) { return "codedeck-review:" + file + ":" + line + (endLine && endLine !== line ? "-" + endLine : ""); }
133
212
  function loadDraft(file, line, endLine) {
@@ -294,7 +373,7 @@ function commentRow(file, line, code, deleted, endLine, onClose) {
294
373
  var tr = document.createElement("tr");
295
374
  tr.className = "cbox";
296
375
  var td = document.createElement("td");
297
- td.colSpan = 3;
376
+ td.colSpan = 2;
298
377
  var spec = endLine && endLine !== line ? line + "-" + endLine : String(line);
299
378
  var ta = document.createElement("textarea");
300
379
  ta.placeholder = "comentar " + file + ":" + spec + "… (shift+enter envia)";
@@ -376,7 +455,7 @@ function renderThread(d) {
376
455
  tr.setAttribute("data-start", String(lo));
377
456
  tr.setAttribute("data-end", String(hi));
378
457
  var td = document.createElement("td");
379
- td.colSpan = 3;
458
+ td.colSpan = 2;
380
459
  var box = document.createElement("div");
381
460
  box.className = "threadbox";
382
461
  var head = document.createElement("div");
@@ -452,6 +531,7 @@ function render(data) {
452
531
  var sec = document.createElement("section");
453
532
  sec.className = "file";
454
533
  sec.id = "f" + idx;
534
+ var lang = langOf(f.path);
455
535
  var h = document.createElement("h3");
456
536
  var title = f.status === "renamed" ? f.oldPath + " → " + f.path : f.path;
457
537
  h.innerHTML = '<span class="st ' + f.status[0].toUpperCase() + '">' + f.status[0].toUpperCase() + "</span><span>" + esc(title) + "</span>" +
@@ -475,25 +555,47 @@ function render(data) {
475
555
  sec.appendChild(hh);
476
556
  var table = document.createElement("table");
477
557
  table.className = "diff";
478
- (hu.lines || []).forEach(function (ln) {
558
+ var wls = hu.lines || [], pmap = {}, pskip = {}, pb = 0;
559
+ while (pb < wls.length) {
560
+ if (wls[pb].type !== "add" && wls[pb].type !== "del") { pb++; continue; }
561
+ var pe = pb, pd = [], pa = [];
562
+ while (pe < wls.length && (wls[pe].type === "add" || wls[pe].type === "del")) {
563
+ if (wls[pe].type === "del") pd.push(pe); else pa.push(pe);
564
+ pe++;
565
+ }
566
+ if (pd.length && pd.length === pa.length) {
567
+ for (var pk = 0; pk < pd.length; pk++) {
568
+ pmap[pd[pk]] = pa[pk];
569
+ pskip[pa[pk]] = true;
570
+ }
571
+ }
572
+ pb = pe;
573
+ }
574
+ (hu.lines || []).forEach(function (ln, li) {
575
+ if (pskip[li]) return;
576
+ var pm = pmap[li] !== undefined ? wls[pmap[li]] : null;
479
577
  var tr = document.createElement("tr");
480
- if (ln.type === "add") tr.className = "add";
578
+ if (pm) tr.className = "chg";
579
+ else if (ln.type === "add") tr.className = "add";
481
580
  else if (ln.type === "del") tr.className = "del";
482
- var anchorLine = ln.newNo != null ? ln.newNo : ln.oldNo;
581
+ var anchorLine = pm ? pm.newNo : (ln.newNo != null ? ln.newNo : ln.oldNo);
582
+ var showNew = pm ? pm.newNo : ln.newNo;
583
+ var showCode = pm ? pm.text : ln.text;
483
584
  tr.setAttribute("data-file", f.path);
484
585
  tr.setAttribute("data-line", String(anchorLine));
485
- tr.setAttribute("data-code", ln.text);
486
- var tdO = document.createElement("td");
487
- tdO.className = "no old";
488
- tdO.textContent = ln.oldNo != null ? String(ln.oldNo) : "";
489
- tdO.title = "arraste para selecionar · clique comenta a linha";
490
- var tdN = document.createElement("td");
491
- tdN.className = "no new";
492
- tdN.textContent = ln.newNo != null ? String(ln.newNo) : "";
493
- tdN.title = "arraste para selecionar · clique comenta a linha";
586
+ tr.setAttribute("data-code", showCode);
587
+ var tdG = document.createElement("td");
588
+ tdG.className = "no" + (pm ? "" : ln.type === "add" ? " new" : ln.type === "del" ? " old" : "");
589
+ var showNo = showNew != null ? showNew : ln.oldNo;
590
+ tdG.textContent = showNo != null ? String(showNo) : "";
591
+ tdG.title = "arraste para selecionar · clique comenta a linha" + (pm && ln.oldNo !== showNo ? " · era " + ln.oldNo + " no ref" : "");
494
592
  var tdC = document.createElement("td");
495
593
  tdC.className = "code";
496
- tdC.textContent = (ln.type === "add" ? "+" : ln.type === "del" ? "-" : " ") + ln.text;
594
+ if (pm) tdC.innerHTML = mergeWords(ln.text, showCode, lang);
595
+ else {
596
+ var sign = ln.type === "add" ? "+" : ln.type === "del" ? "-" : " ";
597
+ tdC.innerHTML = '<span class="sign">' + esc(sign) + "</span>" + hi(ln.text, lang);
598
+ }
497
599
  function dragStart(ev) {
498
600
  if (ev.button !== undefined && ev.button !== 0) return;
499
601
  ev.preventDefault();
@@ -509,12 +611,9 @@ function render(data) {
509
611
  var slice = rowsBetween(sec, f.path, selAnchor.tr, tr);
510
612
  slice.forEach(function (r) { r.classList.add("sel"); });
511
613
  }
512
- tdO.addEventListener("mousedown", dragStart);
513
- tdN.addEventListener("mousedown", dragStart);
514
- tdO.addEventListener("mouseover", dragOver);
515
- tdN.addEventListener("mouseover", dragOver);
516
- tr.appendChild(tdO);
517
- tr.appendChild(tdN);
614
+ tdG.addEventListener("mousedown", dragStart);
615
+ tdG.addEventListener("mouseover", dragOver);
616
+ tr.appendChild(tdG);
518
617
  tr.appendChild(tdC);
519
618
  table.appendChild(tr);
520
619
  });
@@ -1 +1 @@
1
- {"version":3,"file":"review-page.js","sourceRoot":"","sources":["../../src/web/review-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4iB3B,CAAC"}
1
+ {"version":3,"file":"review-page.js","sourceRoot":"","sources":["../../src/web/review-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA+oB3B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codedeck",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "CodeDeck, a local runtime for coding agents with session management, process supervision, event normalization, and git isolation",
5
5
  "type": "module",
6
6
  "bin": {