codedeck 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/cli/charts/stacked-bar.js +2 -1
  2. package/dist/cli/charts/stacked-bar.js.map +1 -1
  3. package/dist/cli/commands/open.js +137 -30
  4. package/dist/cli/commands/open.js.map +1 -1
  5. package/dist/cli/commands/web.js +22 -1
  6. package/dist/cli/commands/web.js.map +1 -1
  7. package/dist/cli/usage/dashboard.js +12 -7
  8. package/dist/cli/usage/dashboard.js.map +1 -1
  9. package/dist/core/roles.js +28 -5
  10. package/dist/core/roles.js.map +1 -1
  11. package/dist/git/review.js +357 -0
  12. package/dist/git/review.js.map +1 -0
  13. package/dist/open/contract.js +6 -2
  14. package/dist/open/contract.js.map +1 -1
  15. package/dist/open/launchers/claude.js +8 -2
  16. package/dist/open/launchers/claude.js.map +1 -1
  17. package/dist/open/launchers/codex.js +236 -0
  18. package/dist/open/launchers/codex.js.map +1 -0
  19. package/dist/plugin/agents/auditor.md +13 -5
  20. package/dist/plugin/agents/general.md +355 -15
  21. package/dist/plugin/agents/orchestrator-edit.md +16 -5
  22. package/dist/plugin/agents/orchestrator-read.md +16 -5
  23. package/dist/plugin/agents/orchestrator.md +16 -5
  24. package/dist/plugin/agents/reviewer.md +6 -2
  25. package/dist/plugin/prompts/_partials/commit.md +145 -0
  26. package/dist/plugin/prompts/_partials/core.md +9 -0
  27. package/dist/plugin/prompts/_partials/dispatch.md +5 -0
  28. package/dist/plugin/prompts/_partials/pr-writer.md +186 -0
  29. package/dist/plugin/prompts/_partials/proof.md +2 -0
  30. package/dist/plugin/prompts/_partials/rename-run.md +1 -0
  31. package/dist/plugin/prompts/_partials/review-self.md +4 -0
  32. package/dist/plugin/prompts/_partials/worktree.md +2 -0
  33. package/dist/plugin/prompts/roles/auditor.md +35 -0
  34. package/dist/plugin/prompts/roles/general.md +29 -0
  35. package/dist/plugin/prompts/roles/orchestrator-edit.md +68 -0
  36. package/dist/plugin/prompts/roles/orchestrator-read.md +68 -0
  37. package/dist/plugin/prompts/roles/orchestrator.md +90 -0
  38. package/dist/plugin/prompts/roles/reviewer.md +37 -0
  39. package/dist/web/canvas-page.js +8 -5
  40. package/dist/web/canvas-page.js.map +1 -1
  41. package/dist/web/review-page.js +565 -0
  42. package/dist/web/review-page.js.map +1 -0
  43. package/package.json +1 -1
@@ -0,0 +1,565 @@
1
+ /** Local staged/unstaged review UI served by `codedeck web` at `/review`.
2
+ *
3
+ * Self-contained page (no external requests besides `api/review`): file
4
+ * tree on the left, unified diff on the right, per-line comment boxes
5
+ * with a "copy markdown" export (`file:line` + quoted code + free text).
6
+ * Drafts persist in localStorage; nothing is sent anywhere.
7
+ */
8
+ export const REVIEW_PAGE = `<!doctype html>
9
+ <html lang="pt-BR">
10
+ <head>
11
+ <meta charset="utf-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1">
13
+ <title>CodeDeck review local</title>
14
+ <link rel="icon" href="data:,">
15
+ <style>
16
+ :root { color-scheme: dark; }
17
+ * { box-sizing: border-box; }
18
+ body { font-family: system-ui, sans-serif; margin: 0; background: #08090c; color: #f5f5f7; }
19
+ header { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; gap: 12px;
20
+ padding: 12px 16px; background: rgba(10,12,16,.94); border-bottom: 1px solid rgba(255,255,255,.1);
21
+ backdrop-filter: blur(10px); flex-wrap: wrap; }
22
+ header h1 { font-size: 15px; margin: 0; }
23
+ header h1 .live { display: inline-block; width: 8px; height: 8px; border-radius: 99px; background: #0a84ff; margin-right: 8px; }
24
+ #meta { font-size: 12px; color: #98989f; }
25
+ #meta b { color: #f5f5f7; }
26
+ header .spacer { flex: 1; }
27
+ header input[type="search"] { background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.12);
28
+ border-radius: 8px; color: #f5f5f7; padding: 6px 10px; font-size: 12.5px; font-family: inherit; width: 200px; }
29
+ header button { border: 1px solid rgba(255,255,255,.14); background: rgba(10,132,255,.25); color: #f5f5f7;
30
+ border-radius: 8px; padding: 7px 12px; cursor: pointer; font-family: inherit; font-size: 12.5px; }
31
+ header button.ghost { background: transparent; }
32
+ header button:hover { border-color: rgba(255,255,255,.3); }
33
+ #layout { display: flex; align-items: flex-start; }
34
+ #files { position: sticky; top: 57px; width: 280px; max-height: calc(100vh - 57px); overflow-y: auto;
35
+ border-right: 1px solid rgba(255,255,255,.08); padding: 12px; flex-shrink: 0; }
36
+ #files h2 { font-size: 11px; letter-spacing: .06em; color: #98989f; margin: 0 0 8px; }
37
+ #files ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
38
+ #files li a { display: flex; gap: 8px; align-items: baseline; font-size: 12.5px; color: #c7c7cc;
39
+ text-decoration: none; padding: 5px 8px; border-radius: 7px; word-break: break-all; }
40
+ #files li a:hover { background: rgba(255,255,255,.06); }
41
+ #files li a.hasComments { color: #f5f5f7; }
42
+ .st { font-size: 10.5px; font-weight: 700; width: 14px; flex-shrink: 0; text-align: center; }
43
+ .st.M { color: #ff9f0a; } .st.A { color: #30d158; } .st.D { color: #ff453a; } .st.R { color: #0a84ff; }
44
+ .cnt { margin-left: auto; font-size: 10.5px; background: rgba(10,132,255,.3); border-radius: 99px;
45
+ padding: 1px 7px; flex-shrink: 0; }
46
+ #main { flex: 1; min-width: 0; padding: 16px 20px 60px; }
47
+ .file { border: 1px solid rgba(255,255,255,.1); border-radius: 12px; margin-bottom: 18px; overflow: hidden; }
48
+ .file > h3 { margin: 0; padding: 10px 14px; font-size: 13px; background: rgba(255,255,255,.04);
49
+ display: flex; gap: 10px; align-items: baseline; word-break: break-all; }
50
+ .file > h3 .old { color: #636366; font-weight: 400; font-size: 12px; }
51
+ .hunkhead { padding: 4px 14px; font-size: 11.5px; color: #636366; background: rgba(255,255,255,.02);
52
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
53
+ table.diff { width: 100%; border-collapse: collapse; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
54
+ font-size: 12px; line-height: 1.55; }
55
+ table.diff td { padding: 0 10px; vertical-align: top; white-space: pre-wrap; word-break: break-word; }
56
+ td.no { width: 44px; min-width: 44px; text-align: right; color: #636366; user-select: none; cursor: pointer; }
57
+ td.no:hover { color: #f5f5f7; background: rgba(10,132,255,.25); }
58
+ tr.add td.code { background: rgba(48,209,88,.10); }
59
+ tr.del td.code { background: rgba(255,69,58,.10); }
60
+ tr.add td.no.new { color: #30d158; } tr.del td.no.old { color: #ff453a; }
61
+ tr.cbox td { padding: 8px 12px 10px 54px; background: rgba(10,132,255,.07); }
62
+ .cbox textarea { width: 100%; min-height: 64px; background: rgba(255,255,255,.06);
63
+ border: 1px solid rgba(255,255,255,.14); border-radius: 8px; color: #f5f5f7;
64
+ padding: 8px 10px; font-size: 12.5px; font-family: inherit; resize: vertical; }
65
+ .cbox .row { display: flex; gap: 8px; margin-top: 8px; align-items: center; flex-wrap: wrap; }
66
+ .cbox button { border: 1px solid rgba(255,255,255,.14); background: rgba(10,132,255,.25); color: #f5f5f7;
67
+ border-radius: 8px; padding: 6px 12px; cursor: pointer; font-family: inherit; font-size: 12px; }
68
+ .cbox button.ghost { background: transparent; }
69
+ .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; }
71
+ tr.sel td.code { background: rgba(10,132,255,.20); }
72
+ tr.sel td.no { background: rgba(10,132,255,.35); color: #fff; }
73
+ tr.thread td { padding: 6px 12px 10px 54px; }
74
+ .threadbox { border: 1px solid rgba(255,255,255,.12); border-radius: 10px; background: rgba(255,255,255,.03); overflow: hidden; }
75
+ .threadbox .thead { padding: 7px 12px; font-size: 11.5px; color: #98989f; border-bottom: 1px solid rgba(255,255,255,.08); }
76
+ .threadbox .tbody { padding: 9px 12px; font-size: 12.5px; white-space: pre-wrap; word-break: break-word; }
77
+ .threadbox .trow { display: flex; gap: 8px; padding: 0 12px 10px; }
78
+ .threadbox button { border: 1px solid rgba(255,255,255,.14); background: transparent; color: #c7c7cc;
79
+ border-radius: 7px; padding: 4px 10px; cursor: pointer; font-family: inherit; font-size: 11.5px; }
80
+ .threadbox button:hover { color: #f5f5f7; border-color: rgba(255,255,255,.3); }
81
+ #empty, #error { max-width: 640px; margin: 60px auto; text-align: center; color: #98989f; font-size: 14px; }
82
+ #error { color: #ff453a; }
83
+ #toast { position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%); background: #1c1e22;
84
+ border: 1px solid rgba(255,255,255,.15); border-radius: 10px; padding: 8px 16px; font-size: 12.5px;
85
+ display: none; z-index: 50; }
86
+ @media (max-width: 800px) { #files { display: none; } }
87
+ </style>
88
+ </head>
89
+ <body>
90
+ <header>
91
+ <h1><span class="live"></span>Review local</h1>
92
+ <span id="meta">carregando…</span>
93
+ <span class="spacer"></span>
94
+ <input id="filter" type="search" placeholder="filtrar arquivos…" aria-label="Filtrar arquivos">
95
+ <button id="copyAll" type="button" title="Copiar todos os comentários em markdown">Copiar todos (<span id="nComments">0</span>)</button>
96
+ <a href="/" style="color:#98989f;font-size:12.5px">canvas</a>
97
+ </header>
98
+ <div id="layout">
99
+ <nav id="files" aria-label="Arquivos"><h2>ARQUIVOS</h2><ul id="fileList"></ul></nav>
100
+ <div id="main"></div>
101
+ </div>
102
+ <div id="toast" role="status"></div>
103
+ <script>
104
+ "use strict";
105
+ var main = document.getElementById("main");
106
+ var meta = document.getElementById("meta");
107
+ var fileList = document.getElementById("fileList");
108
+ var toastEl = document.getElementById("toast");
109
+ var toastTimer = null;
110
+ function toast(msg) {
111
+ toastEl.textContent = msg;
112
+ toastEl.style.display = "block";
113
+ if (toastTimer) clearTimeout(toastTimer);
114
+ toastTimer = setTimeout(function () { toastEl.style.display = "none"; }, 2200);
115
+ }
116
+ function esc(s) {
117
+ return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
118
+ }
119
+ function langOf(p) {
120
+ var b = p.split("/").pop() || p;
121
+ if (b === "Dockerfile" || b.indexOf("Dockerfile.") === 0) return "dockerfile";
122
+ var i = b.lastIndexOf(".");
123
+ if (i < 0) return "";
124
+ var ext = b.slice(i + 1).toLowerCase();
125
+ var map = { ts: "ts", tsx: "tsx", js: "js", jsx: "jsx", mjs: "js", cjs: "js", mts: "ts", cts: "ts",
126
+ py: "py", go: "go", rs: "rust", java: "java", rb: "ruby", php: "php", swift: "swift", kt: "kotlin",
127
+ cs: "csharp", c: "c", h: "c", cpp: "cpp", hpp: "cpp", css: "css", scss: "scss", html: "html",
128
+ xml: "xml", json: "json", yaml: "yaml", yml: "yaml", toml: "toml", md: "md", sh: "bash", sql: "sql" };
129
+ return map[ext] || "";
130
+ }
131
+ /* Drafts: key codedeck-review:<file>:<line>[-<endLine>] -> { code, body, deleted } */
132
+ function draftKey(file, line, endLine) { return "codedeck-review:" + file + ":" + line + (endLine && endLine !== line ? "-" + endLine : ""); }
133
+ function loadDraft(file, line, endLine) {
134
+ try {
135
+ var raw = localStorage.getItem(draftKey(file, line, endLine));
136
+ return raw ? JSON.parse(raw) : null;
137
+ } catch (e) { return null; }
138
+ }
139
+ function saveDraft(file, line, val, endLine) {
140
+ try {
141
+ if (!val || !val.body) localStorage.removeItem(draftKey(file, line, endLine));
142
+ else localStorage.setItem(draftKey(file, line, endLine), JSON.stringify(val));
143
+ } catch (e) {}
144
+ }
145
+ function allDrafts() {
146
+ var out = [];
147
+ try {
148
+ for (var i = 0; i < localStorage.length; i++) {
149
+ var k = localStorage.key(i);
150
+ if (!k || k.indexOf("codedeck-review:") !== 0) continue;
151
+ var v = JSON.parse(localStorage.getItem(k));
152
+ if (v && v.body) {
153
+ var rest = k.slice("codedeck-review:".length);
154
+ var li = rest.lastIndexOf(":");
155
+ var spec = rest.slice(li + 1).split("-");
156
+ var d = { file: rest.slice(0, li), line: Number(spec[0]), code: v.code, body: v.body, deleted: !!v.deleted };
157
+ if (spec[1]) d.endLine = Number(spec[1]);
158
+ out.push(d);
159
+ }
160
+ }
161
+ } catch (e) {}
162
+ out.sort(function (a, b) { return a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line; });
163
+ return out;
164
+ }
165
+ function formatComment(c) {
166
+ var lang = langOf(c.file);
167
+ var quoted = String(c.code).replace(/\\n$/, "").split("\\n").map(function (l) { return "> " + l; }).join("\\n");
168
+ var mark = c.deleted ? "> - (deleted line)\\n" : "";
169
+ var anchor = c.endLine && c.endLine !== c.line ? c.file + ":" + c.line + "-" + c.endLine : c.file + ":" + c.line;
170
+ return "\`" + anchor + "\`\\n" + mark + "> \`\`\`" + lang + "\\n" + quoted + "\\n> \`\`\`\\n" + String(c.body).trim();
171
+ }
172
+ function copyText(t, okMsg) {
173
+ function done() { toast(okMsg || "markdown copiado"); refreshCounts(); }
174
+ if (navigator.clipboard && navigator.clipboard.writeText) {
175
+ navigator.clipboard.writeText(t).then(done, function () { fallback(); });
176
+ } else fallback();
177
+ function fallback() {
178
+ var ta = document.createElement("textarea");
179
+ ta.value = t;
180
+ document.body.appendChild(ta);
181
+ ta.select();
182
+ try { document.execCommand("copy"); done(); } catch (e) { toast("falha ao copiar"); }
183
+ document.body.removeChild(ta);
184
+ }
185
+ }
186
+ function refreshCounts() {
187
+ var drafts = allDrafts();
188
+ document.getElementById("nComments").textContent = String(drafts.length);
189
+ var byFile = {};
190
+ drafts.forEach(function (d) { byFile[d.file] = (byFile[d.file] || 0) + 1; });
191
+ Array.prototype.forEach.call(fileList.children, function (li) {
192
+ var a = li.firstChild;
193
+ var f = a.getAttribute("data-file");
194
+ var badge = a.querySelector(".cnt");
195
+ if (byFile[f]) {
196
+ a.classList.add("hasComments");
197
+ if (!badge) {
198
+ badge = document.createElement("span");
199
+ badge.className = "cnt";
200
+ a.appendChild(badge);
201
+ }
202
+ badge.textContent = String(byFile[f]);
203
+ } else {
204
+ a.classList.remove("hasComments");
205
+ if (badge) badge.remove();
206
+ }
207
+ });
208
+ Array.prototype.forEach.call(document.querySelectorAll("tr[data-file]"), function (tr) {
209
+ var f = tr.getAttribute("data-file"), ln = Number(tr.getAttribute("data-line"));
210
+ var has = drafts.some(function (d) {
211
+ if (d.file !== f) return false;
212
+ if (d.endLine) return ln >= Math.min(d.line, d.endLine) && ln <= Math.max(d.line, d.endLine);
213
+ return d.line === ln;
214
+ });
215
+ tr.classList.toggle("hasNote", has);
216
+ });
217
+ }
218
+ /* Drag selection: mousedown on a gutter number starts it, mouseover extends,
219
+ * mouseup opens the box (single line, or range when dragged). selAnchor lives
220
+ * only between down and up; .sel paint stays while the box is open. */
221
+ var selAnchor = null;
222
+ function closeBoxes(root) {
223
+ Array.prototype.forEach.call(root.querySelectorAll("tr.cbox"), function (b) { b.remove(); });
224
+ }
225
+ function clearSel(sec) {
226
+ if (!sec) return;
227
+ Array.prototype.forEach.call(sec.querySelectorAll("tr.sel"), function (r) { r.classList.remove("sel"); });
228
+ }
229
+ function rowsBetween(sec, file, tra, trb) {
230
+ var rows = Array.prototype.filter.call(
231
+ sec.querySelectorAll("tr[data-file]"),
232
+ function (r) { return r.getAttribute("data-file") === file; },
233
+ );
234
+ var ia = rows.indexOf(tra), ib = rows.indexOf(trb);
235
+ if (ia < 0 || ib < 0) return [];
236
+ return rows.slice(Math.min(ia, ib), Math.max(ia, ib) + 1);
237
+ }
238
+ function openRange(file, a, b, onClose) {
239
+ var sec = a.tr;
240
+ while (sec && sec.className !== "file") sec = sec.parentNode;
241
+ if (!sec) return;
242
+ var slice = rowsBetween(sec, file, a.tr, b.tr);
243
+ if (!slice.length) return;
244
+ var nums = slice.map(function (r) { return Number(r.getAttribute("data-line")); });
245
+ var start = Math.min.apply(null, nums), end = Math.max.apply(null, nums);
246
+ var code = slice.map(function (r) { return r.getAttribute("data-code") || ""; }).join(String.fromCharCode(10));
247
+ var deleted = slice.every(function (r) { return r.className.indexOf("del") >= 0; });
248
+ removeThreads(sec, file, start, end);
249
+ closeBoxes(sec);
250
+ var box = commentRow(file, start, code, deleted, start === end ? undefined : end, onClose);
251
+ var last = slice[slice.length - 1];
252
+ last.parentNode.insertBefore(box, last.nextSibling);
253
+ var ta = box.querySelector("textarea");
254
+ if (ta) ta.focus();
255
+ }
256
+ /* Drag end (one listener for the whole page): the drop row decides single
257
+ * vs range. Clicking the row that already owns a box closes it (toggle). */
258
+ document.addEventListener("mouseup", function (ev) {
259
+ if (!selAnchor) return;
260
+ var startTr = selAnchor.tr;
261
+ selAnchor = null;
262
+ var sec = startTr;
263
+ while (sec && sec.className !== "file") sec = sec.parentNode;
264
+ if (!sec) return;
265
+ var file = startTr.getAttribute("data-file");
266
+ var endTr = null;
267
+ var t = ev.target;
268
+ if (t && t.closest) {
269
+ var cand = t.closest("tr[data-file]");
270
+ if (cand && cand.getAttribute("data-file") === file) endTr = cand;
271
+ }
272
+ if (!endTr) {
273
+ var painted = sec.querySelectorAll("tr.sel");
274
+ endTr = painted.length ? painted[painted.length - 1] : startTr;
275
+ }
276
+ if (endTr === startTr) {
277
+ var line = Number(startTr.getAttribute("data-line"));
278
+ if (startTr.nextSibling && startTr.nextSibling.className === "cbox") {
279
+ startTr.nextSibling.remove();
280
+ clearSel(sec);
281
+ return;
282
+ }
283
+ removeThreads(sec, file, line, line);
284
+ closeBoxes(sec);
285
+ var box = commentRow(file, line, startTr.getAttribute("data-code") || "", startTr.className.indexOf("del") >= 0, undefined, function () { clearSel(sec); });
286
+ startTr.parentNode.insertBefore(box, startTr.nextSibling);
287
+ var ta = box.querySelector("textarea");
288
+ if (ta) ta.focus();
289
+ return;
290
+ }
291
+ openRange(file, { tr: startTr }, { tr: endTr }, function () { clearSel(sec); });
292
+ });
293
+ function commentRow(file, line, code, deleted, endLine, onClose) {
294
+ var tr = document.createElement("tr");
295
+ tr.className = "cbox";
296
+ var td = document.createElement("td");
297
+ td.colSpan = 3;
298
+ var spec = endLine && endLine !== line ? line + "-" + endLine : String(line);
299
+ var ta = document.createElement("textarea");
300
+ ta.placeholder = "comentar " + file + ":" + spec + "… (shift+enter envia)";
301
+ ta.setAttribute("aria-label", "Comentário em " + file + ":" + spec);
302
+ var prev = loadDraft(file, line, endLine);
303
+ if (prev && prev.body) ta.value = prev.body;
304
+ var row = document.createElement("div");
305
+ row.className = "row";
306
+ var btnSend = document.createElement("button");
307
+ btnSend.type = "button";
308
+ btnSend.textContent = "Enviar";
309
+ btnSend.title = "salva e recolhe — exporte tudo em Copiar todos no topo";
310
+ var btnClear = document.createElement("button");
311
+ btnClear.type = "button";
312
+ btnClear.className = "ghost";
313
+ btnClear.textContent = "Limpar";
314
+ var ok = document.createElement("span");
315
+ ok.className = "ok";
316
+ btnSend.addEventListener("click", function () {
317
+ if (!ta.value.trim()) { toast("escreva o comentário antes de enviar"); return; }
318
+ var saved = { file: file, line: line, endLine: endLine, code: code, body: ta.value, deleted: deleted };
319
+ saveDraft(file, line, { code: code, body: ta.value, deleted: deleted }, endLine);
320
+ refreshCounts();
321
+ tr.remove();
322
+ if (onClose) onClose();
323
+ renderThread(saved);
324
+ toast("salvo ✓ — Copiar todos no topo exporta tudo");
325
+ });
326
+ ta.addEventListener("keydown", function (ev) {
327
+ if (ev.key === "Enter" && ev.shiftKey) {
328
+ ev.preventDefault();
329
+ btnSend.click();
330
+ }
331
+ });
332
+ btnClear.addEventListener("click", function () {
333
+ ta.value = "";
334
+ saveDraft(file, line, null, endLine);
335
+ refreshCounts();
336
+ tr.remove();
337
+ if (onClose) onClose();
338
+ });
339
+ ta.addEventListener("input", function () {
340
+ saveDraft(file, line, ta.value.trim() ? { code: code, body: ta.value, deleted: deleted } : null, endLine);
341
+ refreshCounts();
342
+ });
343
+ row.appendChild(btnSend);
344
+ row.appendChild(btnClear);
345
+ row.appendChild(ok);
346
+ td.appendChild(ta);
347
+ td.appendChild(row);
348
+ tr.appendChild(td);
349
+ return tr;
350
+ }
351
+ /* Saved drafts render as GitHub-like thread boxes under their lines. */
352
+ function removeThreads(sec, file, start, end) {
353
+ Array.prototype.forEach.call(sec.querySelectorAll("tr.thread"), function (r) {
354
+ if (r.getAttribute("data-file") !== file) return;
355
+ var s = Number(r.getAttribute("data-start")), e = Number(r.getAttribute("data-end"));
356
+ if (Math.min(e, end) >= Math.max(s, start)) r.remove();
357
+ });
358
+ }
359
+ function specOf(d) {
360
+ var end = d.endLine || d.line;
361
+ return end !== d.line ? d.line + "-" + end : String(d.line);
362
+ }
363
+ function renderThread(d) {
364
+ var end = d.endLine || d.line;
365
+ var lo = Math.min(d.line, end), hi = Math.max(d.line, end);
366
+ var rows = Array.prototype.filter.call(document.querySelectorAll("tr[data-file]"), function (r) {
367
+ if (r.getAttribute("data-file") !== d.file) return false;
368
+ var ln = Number(r.getAttribute("data-line"));
369
+ return ln >= lo && ln <= hi;
370
+ });
371
+ if (!rows.length) return;
372
+ var last = rows[rows.length - 1];
373
+ var tr = document.createElement("tr");
374
+ tr.className = "thread";
375
+ tr.setAttribute("data-file", d.file);
376
+ tr.setAttribute("data-start", String(lo));
377
+ tr.setAttribute("data-end", String(hi));
378
+ var td = document.createElement("td");
379
+ td.colSpan = 3;
380
+ var box = document.createElement("div");
381
+ box.className = "threadbox";
382
+ var head = document.createElement("div");
383
+ head.className = "thead";
384
+ head.textContent = lo === hi ? "Comentário na linha " + lo : "Comentário nas linhas " + lo + "-" + hi;
385
+ var body = document.createElement("div");
386
+ body.className = "tbody";
387
+ body.textContent = d.body;
388
+ var trow = document.createElement("div");
389
+ trow.className = "trow";
390
+ var bCopy = document.createElement("button");
391
+ bCopy.type = "button";
392
+ bCopy.textContent = "Copiar";
393
+ bCopy.addEventListener("click", function () {
394
+ copyText(formatComment({ file: d.file, line: d.line, endLine: d.endLine, code: d.code, body: d.body, deleted: d.deleted }), "comentário copiado");
395
+ });
396
+ var bEdit = document.createElement("button");
397
+ bEdit.type = "button";
398
+ bEdit.textContent = "Editar";
399
+ bEdit.addEventListener("click", function () {
400
+ tr.remove();
401
+ var ed = commentRow(d.file, d.line, d.code, d.deleted, d.endLine, null);
402
+ last.parentNode.insertBefore(ed, last.nextSibling);
403
+ var ta = ed.querySelector("textarea");
404
+ if (ta) ta.focus();
405
+ });
406
+ var bDel = document.createElement("button");
407
+ bDel.type = "button";
408
+ bDel.textContent = "Excluir";
409
+ bDel.addEventListener("click", function () {
410
+ saveDraft(d.file, d.line, null, d.endLine);
411
+ refreshCounts();
412
+ tr.remove();
413
+ toast("comentário excluído");
414
+ });
415
+ trow.appendChild(bCopy);
416
+ trow.appendChild(bEdit);
417
+ trow.appendChild(bDel);
418
+ box.appendChild(head);
419
+ box.appendChild(body);
420
+ box.appendChild(trow);
421
+ td.appendChild(box);
422
+ tr.appendChild(td);
423
+ last.parentNode.insertBefore(tr, last.nextSibling);
424
+ }
425
+ function render(data) {
426
+ main.innerHTML = "";
427
+ fileList.innerHTML = "";
428
+ var files = data.files || [];
429
+ meta.innerHTML = "ref <b>" + esc(data.ref || "HEAD") + "</b> · <b>" + files.length + "</b> arquivos" +
430
+ (data.truncated ? " · <b>truncado</b>" : "") +
431
+ (data.base ? " · " + esc(String(data.base).slice(0, 8)) : "");
432
+ if (!files.length) {
433
+ main.innerHTML = '<div id="empty">Nada para revisar — working tree limpo contra ' + esc(data.ref || "HEAD") + ".</div>";
434
+ refreshCounts();
435
+ return;
436
+ }
437
+ files.forEach(function (f, idx) {
438
+ var li = document.createElement("li");
439
+ var a = document.createElement("a");
440
+ a.href = "#f" + idx;
441
+ a.setAttribute("data-file", f.path);
442
+ var st = document.createElement("span");
443
+ st.className = "st " + f.status[0].toUpperCase();
444
+ st.textContent = f.status[0].toUpperCase();
445
+ var nm = document.createElement("span");
446
+ nm.textContent = f.path;
447
+ a.appendChild(st);
448
+ a.appendChild(nm);
449
+ li.appendChild(a);
450
+ fileList.appendChild(li);
451
+
452
+ var sec = document.createElement("section");
453
+ sec.className = "file";
454
+ sec.id = "f" + idx;
455
+ var h = document.createElement("h3");
456
+ var title = f.status === "renamed" ? f.oldPath + " → " + f.path : f.path;
457
+ h.innerHTML = '<span class="st ' + f.status[0].toUpperCase() + '">' + f.status[0].toUpperCase() + "</span><span>" + esc(title) + "</span>" +
458
+ (f.oldPath && f.oldPath !== f.path && f.status !== "renamed" ? '<span class="old">' + esc(f.oldPath) + "</span>" : "");
459
+ sec.appendChild(h);
460
+ if (f.binary) {
461
+ var p = document.createElement("p");
462
+ p.style.cssText = "padding:4px 14px 12px;color:#98989f;font-size:12.5px";
463
+ p.textContent = "arquivo binário — sem diff textual.";
464
+ sec.appendChild(p);
465
+ } else if (f.tooLarge) {
466
+ var q = document.createElement("p");
467
+ q.style.cssText = "padding:4px 14px 12px;color:#98989f;font-size:12.5px";
468
+ q.textContent = "arquivo grande demais para exibir — veja no git.";
469
+ sec.appendChild(q);
470
+ }
471
+ (f.hunks || []).forEach(function (hu) {
472
+ var hh = document.createElement("div");
473
+ hh.className = "hunkhead";
474
+ hh.textContent = hu.header;
475
+ sec.appendChild(hh);
476
+ var table = document.createElement("table");
477
+ table.className = "diff";
478
+ (hu.lines || []).forEach(function (ln) {
479
+ var tr = document.createElement("tr");
480
+ if (ln.type === "add") tr.className = "add";
481
+ else if (ln.type === "del") tr.className = "del";
482
+ var anchorLine = ln.newNo != null ? ln.newNo : ln.oldNo;
483
+ tr.setAttribute("data-file", f.path);
484
+ 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";
494
+ var tdC = document.createElement("td");
495
+ tdC.className = "code";
496
+ tdC.textContent = (ln.type === "add" ? "+" : ln.type === "del" ? "-" : " ") + ln.text;
497
+ function dragStart(ev) {
498
+ if (ev.button !== undefined && ev.button !== 0) return;
499
+ ev.preventDefault();
500
+ closeBoxes(sec);
501
+ clearSel(sec);
502
+ selAnchor = { tr: tr };
503
+ tr.classList.add("sel");
504
+ }
505
+ function dragOver(ev) {
506
+ if (!selAnchor || selAnchor.tr.getAttribute("data-file") !== f.path) return;
507
+ if (ev.buttons !== undefined && !(ev.buttons & 1)) return;
508
+ clearSel(sec);
509
+ var slice = rowsBetween(sec, f.path, selAnchor.tr, tr);
510
+ slice.forEach(function (r) { r.classList.add("sel"); });
511
+ }
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);
518
+ tr.appendChild(tdC);
519
+ table.appendChild(tr);
520
+ });
521
+ sec.appendChild(table);
522
+ });
523
+ main.appendChild(sec);
524
+ });
525
+ refreshCounts();
526
+ allDrafts().forEach(function (d) { renderThread(d); });
527
+ }
528
+ function load() {
529
+ var params = new URLSearchParams(location.search);
530
+ var ref = params.get("ref") || "HEAD";
531
+ fetch("api/review?ref=" + encodeURIComponent(ref))
532
+ .then(function (res) {
533
+ if (!res.ok) throw new Error("review indisponível (HTTP " + res.status + ")");
534
+ return res.json();
535
+ })
536
+ .then(render)
537
+ .catch(function (err) {
538
+ meta.textContent = "erro";
539
+ main.innerHTML = '<div id="error">' + esc(err.message) + "</div>";
540
+ });
541
+ }
542
+ document.getElementById("copyAll").addEventListener("click", function () {
543
+ var drafts = allDrafts();
544
+ if (!drafts.length) { toast("nenhum comentário ainda — clique num número de linha"); return; }
545
+ copyText(drafts.map(formatComment).join("\\n\\n---\\n\\n"), drafts.length + " comentários copiados");
546
+ });
547
+ document.getElementById("filter").addEventListener("input", function (ev) {
548
+ var q = ev.target.value.toLowerCase();
549
+ Array.prototype.forEach.call(fileList.children, function (li) {
550
+ var f = li.firstChild.getAttribute("data-file").toLowerCase();
551
+ li.style.display = !q || f.indexOf(q) >= 0 ? "" : "none";
552
+ });
553
+ Array.prototype.forEach.call(main.children, function (sec) {
554
+ if (sec.id && sec.id[0] === "f") {
555
+ var idx = Number(sec.id.slice(1));
556
+ var link = fileList.children[idx];
557
+ sec.style.display = link && link.style.display === "none" ? "none" : "";
558
+ }
559
+ });
560
+ });
561
+ load();
562
+ </script>
563
+ </body>
564
+ </html>`;
565
+ //# sourceMappingURL=review-page.js.map
@@ -0,0 +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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codedeck",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
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": {