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.
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/cli/commands/doctor.js +10 -2
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/open.js +49 -12
- package/dist/cli/commands/open.js.map +1 -1
- package/dist/cli/commands/profile.js +191 -0
- package/dist/cli/commands/profile.js.map +1 -0
- package/dist/cli/commands/run.js +36 -5
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/setup.js +175 -23
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/config/config.js +82 -2
- package/dist/config/config.js.map +1 -1
- package/dist/daemon/daemon.js +1 -0
- package/dist/daemon/daemon.js.map +1 -1
- package/dist/drivers/codex/parser.js +14 -10
- package/dist/drivers/codex/parser.js.map +1 -1
- package/dist/open/contract.js.map +1 -1
- package/dist/open/launchers/claude.js +8 -2
- package/dist/open/launchers/claude.js.map +1 -1
- package/dist/plugin/agents/auditor.md +2 -0
- package/dist/plugin/agents/general.md +10 -2
- package/dist/plugin/agents/orchestrator-edit.md +10 -2
- package/dist/plugin/agents/orchestrator-read.md +10 -2
- package/dist/plugin/agents/orchestrator.md +18 -3
- package/dist/plugin/prompts/_partials/dispatch.md +2 -0
- package/dist/plugin/prompts/_partials/reports-dispatch.md +5 -0
- package/dist/plugin/prompts/_partials/reports-general.md +5 -0
- package/dist/plugin/prompts/_partials/spec-gates.md +6 -0
- package/dist/plugin/prompts/_partials/worktree.md +1 -1
- package/dist/plugin/prompts/roles/general.md +1 -0
- package/dist/plugin/prompts/roles/orchestrator-edit.md +1 -0
- package/dist/plugin/prompts/roles/orchestrator-read.md +1 -0
- package/dist/plugin/prompts/roles/orchestrator.md +3 -1
- package/dist/web/canvas-page.js +79 -33
- package/dist/web/canvas-page.js.map +1 -1
- package/dist/web/review-page.js +121 -22
- package/dist/web/review-page.js.map +1 -1
- 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
|
|
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
|
|
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.
|
|
@@ -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.
|
package/dist/web/canvas-page.js
CHANGED
|
@@ -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:
|
|
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:
|
|
48
|
-
#feed li { font-size:
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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(
|
|
194
|
-
return one.length >
|
|
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 || "
|
|
200
|
-
if (name === "bash" || name === "Bash") return snip ?
|
|
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
|
-
|
|
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),
|
|
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), "
|
|
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
|
-
|
|
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
|
-
|
|
628
|
+
// turn completion is recorded on node activity
|
|
584
629
|
} else if (ev.type === "file.changed") {
|
|
585
|
-
feed(agentName(n.agent), "
|
|
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), "
|
|
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
|
|
944
|
-
["Esforço", s.effort || "
|
|
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(
|
|
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("
|
|
1025
|
+
if (bad) chat.appendChild(chatEvt("falhou: " + toolLabel(ev.tool)));
|
|
980
1026
|
} else if (ev.type === "permission.requested") {
|
|
981
|
-
chat.appendChild(chatEvt("
|
|
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("
|
|
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
|
|
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"}
|
package/dist/web/review-page.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
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",
|
|
486
|
-
var
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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.
|
|
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
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
|
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