claude-coding-flow 1.3.2 → 1.4.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.
@@ -16,7 +16,10 @@ const TAB_CONFIG = {
16
16
  let currentTab = "doc-gen";
17
17
  let _ctxTaskId = null;
18
18
  let _ctxModuleId = null;
19
+ let _ctxModuleType = "";
20
+ let _ctxModuleTaskCount = 0;
19
21
  let _ctxTabType = null;
22
+ let _editingFilePath = null;
20
23
 
21
24
  // ── Tab Switching ──
22
25
 
@@ -68,8 +71,9 @@ async function loadTabContent(tab) {
68
71
 
69
72
  function renderModuleShell(mod, innerHtml, color) {
70
73
  const tasks = mod.tasks || [];
74
+ const modType = mod.type || "code-gen";
71
75
  return `<div class="module-section">
72
- <div class="module-header" data-module-id="${mod.id}" onclick="toggleModule('${mod.id}')">
76
+ <div class="module-header" data-module-id="${mod.id}" data-type="${modType}" data-task-count="${tasks.length}" onclick="toggleModule('${mod.id}')">
73
77
  <div class="module-left">
74
78
  <span class="material-icons module-expand" id="expand-${mod.id}">expand_more</span>
75
79
  <span class="module-name">${esc(mod.name)}</span>
@@ -111,7 +115,15 @@ async function renderTaskChain(tasks, tabType) {
111
115
  await Promise.all(relPromises);
112
116
  }
113
117
 
114
- const roots = tasks.filter(t => !t.prev_task_id);
118
+ // Build chain parent: prev_task_id first, then related_task_id (for doc-gen iterations)
119
+ const taskIds = new Set(tasks.map(t => t.id));
120
+ function chainParent(t) {
121
+ if (t.prev_task_id && taskIds.has(t.prev_task_id)) return { id: t.prev_task_id, label: "迭代" };
122
+ if (t.related_task_id && taskIds.has(t.related_task_id)) return { id: t.related_task_id, label: "迭代" };
123
+ return null;
124
+ }
125
+
126
+ const roots = tasks.filter(t => !chainParent(t));
115
127
  const visited = new Set();
116
128
 
117
129
  for (const root of roots) {
@@ -119,7 +131,9 @@ async function renderTaskChain(tasks, tabType) {
119
131
  while (cur) {
120
132
  if (visited.has(cur.id)) break;
121
133
  visited.add(cur.id);
122
- const next = tasks.find(t => t.prev_task_id === cur.id);
134
+ const nextInfo = tasks.map(t => ({ task: t, parent: chainParent(t) })).find(x => x.parent && x.parent.id === cur.id);
135
+ const next = nextInfo ? nextInfo.task : null;
136
+ const linkLabel = nextInfo ? nextInfo.parent.label : "迭代";
123
137
 
124
138
  lines.push(`<div class="chain-node">
125
139
  <span class="chain-dot ${cur.status}"></span>
@@ -130,7 +144,7 @@ async function renderTaskChain(tasks, tabType) {
130
144
  if (next) {
131
145
  lines.push(`<div class="chain-link">
132
146
  <span class="material-icons chain-arrow">arrow_downward</span>
133
- <span class="chain-link-label">迭代</span>
147
+ <span class="chain-link-label">${linkLabel}</span>
134
148
  </div>`);
135
149
  }
136
150
  cur = next;
@@ -149,7 +163,7 @@ async function renderTaskChain(tasks, tabType) {
149
163
  return `<div class="task-chain">${lines.join("")}</div>`;
150
164
  }
151
165
 
152
- function _renderCrossBadge(relations) {
166
+ function _renderCrossBadge(relations, tabType) {
153
167
  if (!relations || relations.length === 0) return "";
154
168
  const badges = relations.map(r => {
155
169
  if (r.type === "based-on") {
@@ -187,8 +201,14 @@ function renderCodeGenCard(task) {
187
201
 
188
202
  const progress = calcProgress(task);
189
203
 
204
+ const bugBadges = (task.bug_fix_details || []).map(b => {
205
+ const statusText = b.status === "resolved" ? "已解决" : "未解决";
206
+ const cls = b.status === "resolved" ? "resolved" : "unresolved";
207
+ return `<span class="cross-badge traces-to" title="Bug: ${esc(b.title)} (${statusText})">Bug: ${esc(b.title)} (${statusText})</span>`;
208
+ }).join("");
209
+
190
210
  return `<div class="task-card" data-id="${task.id}" data-type="code-gen">
191
- <div class="task-header" onclick="toggleDetail('${task.id}')">
211
+ <div class="task-header" onclick="loadTaskDetail('${task.id}')">
192
212
  <div>
193
213
  <div class="task-title">${esc(task.title)}</div>
194
214
  <div class="task-meta">${task.id}</div>
@@ -197,11 +217,11 @@ function renderCodeGenCard(task) {
197
217
  <span class="status-badge ${task.status}">${statusLabel(task.status)}</span>
198
218
  </div>
199
219
  </div>
220
+ ${bugBadges ? `<div style="padding:0 20px 4px">${bugBadges}</div>` : ""}
200
221
  <div class="phases-row">${phases}</div>
201
222
  <div class="progress-bar-wrap"><div class="progress-bar blue ${task.status === 'failed' ? 'failed' : ''}" style="width:${progress}%"></div></div>
202
223
  <div class="task-footer">
203
224
  <span>${task.created_at || ''}</span>
204
- <a class="log-toggle" onclick="event.stopPropagation();loadTaskDetail('${task.id}')">日志详情</a>
205
225
  </div>
206
226
  <div class="task-detail-content hidden" id="detail-${task.id}"></div>
207
227
  </div>`;
@@ -225,11 +245,12 @@ function renderDocGenCard(task) {
225
245
  const sketchFolder = task.sketch_folder || "无";
226
246
  const outputDoc = task.output_doc || "未生成";
227
247
  const imgCount = task._image_count || 0;
248
+ const figmaCount = task._figma_count || 0;
228
249
 
229
250
  const progress = calcProgress(task);
230
251
 
231
252
  return `<div class="task-card" data-id="${task.id}" data-type="doc-gen">
232
- <div class="task-header" onclick="toggleDetail('${task.id}')">
253
+ <div class="task-header" onclick="loadTaskDetail('${task.id}')">
233
254
  <div>
234
255
  <div class="task-title">${esc(task.title)}</div>
235
256
  <div class="task-meta">${task.id}</div>
@@ -240,13 +261,14 @@ function renderDocGenCard(task) {
240
261
  </div>
241
262
  <div class="artifact-tags">
242
263
  <span class="artifact-tag"><span class="material-icons">article</span>${esc(reqDoc)}</span>
243
- <span class="artifact-tag"><span class="material-icons">image</span>${imgCount} 张草图</span>
264
+ ${imgCount > 0 ? `<span class="artifact-tag"><span class="material-icons">image</span>${imgCount} 张图片</span>` : ""}
265
+ ${figmaCount > 0 ? `<span class="artifact-tag figma"><span class="material-icons">design_services</span>${figmaCount} 个 Figma</span>` : ""}
266
+ ${imgCount === 0 && figmaCount === 0 ? `<span class="artifact-tag"><span class="material-icons">image</span>无图片</span>` : ""}
244
267
  <span class="artifact-tag"><span class="material-icons">auto_awesome</span>${esc(outputDoc)}</span>
245
268
  </div>
246
269
  <div class="progress-bar-wrap"><div class="progress-bar amber" style="width:${progress}%"></div></div>
247
270
  <div class="task-footer">
248
271
  <span>${task.created_at || ''}</span>
249
- <a class="log-toggle" onclick="event.stopPropagation();loadTaskDetail('${task.id}')">日志详情</a>
250
272
  </div>
251
273
  <div class="task-detail-content hidden" id="detail-${task.id}"></div>
252
274
  </div>`;
@@ -283,7 +305,7 @@ function renderBugFixCard(task) {
283
305
  }
284
306
 
285
307
  return `<div class="bug-card" data-id="${task.id}" data-type="bug-fix">
286
- <div class="bug-header">
308
+ <div class="bug-header" onclick="loadTaskDetail('${task.id}')" style="cursor:pointer">
287
309
  <span class="material-icons bug-status-icon ${resolved ? 'resolved' : ''}">${resolved ? 'check_circle' : 'error_outline'}</span>
288
310
  <div class="bug-info">
289
311
  <div class="bug-title">${esc(task.title)}</div>
@@ -292,12 +314,11 @@ function renderBugFixCard(task) {
292
314
  </div>
293
315
  <span class="status-badge ${resolved ? 'resolved' : 'unresolved'}">${resolved ? '已解决' : '未解决'}</span>
294
316
  </div>
295
- <div class="phases-row" style="padding-left:44px">${phases}</div>
317
+ <div class="phases-row" style="padding-left:52px">${phases}</div>
296
318
  <div class="progress-bar-wrap"><div class="progress-bar red" style="width:${progress}%"></div></div>
297
319
  <div class="bug-footer">
298
320
  <span>${task.id}</span>
299
321
  <span>${task.created_at || ''}</span>
300
- <a class="log-toggle" onclick="event.stopPropagation();loadTaskDetail('${task.id}')">日志详情</a>
301
322
  </div>
302
323
  <div class="task-detail-content hidden" id="detail-${task.id}"></div>
303
324
  </div>`;
@@ -334,6 +355,12 @@ document.addEventListener("contextmenu", e => {
334
355
  if (modHeader && !e.target.closest(".task-card") && !e.target.closest(".bug-card")) {
335
356
  e.preventDefault();
336
357
  _ctxModuleId = modHeader.dataset.moduleId;
358
+ _ctxModuleType = modHeader.dataset.type || "";
359
+ _ctxModuleTaskCount = parseInt(modHeader.dataset.taskCount || "0");
360
+ // doc-gen 不显示菜单,有任务的模块也不显示
361
+ if (_ctxModuleType === "doc-gen" || _ctxModuleTaskCount > 0) {
362
+ return;
363
+ }
337
364
  showCtx("ctx-module", e.clientX, e.clientY);
338
365
  return;
339
366
  }
@@ -398,6 +425,7 @@ document.querySelectorAll(".ctx-menu").forEach(menu => {
398
425
  else if (action === "view-detail") viewDetail();
399
426
  else if (action === "view-snapshot") viewSnapshot();
400
427
  else if (action === "delete-task") deleteTask();
428
+ else if (action === "delete-module") deleteModule();
401
429
  else if (action === "view-req-doc") viewReqDoc();
402
430
  else if (action === "view-images") viewImages();
403
431
  else if (action === "view-dev-doc") viewDevDoc();
@@ -420,6 +448,17 @@ async function deleteTask() {
420
448
  else showToast("删除失败");
421
449
  }
422
450
 
451
+ async function deleteModule() {
452
+ if (!_ctxModuleId) return;
453
+ if (!confirm(`确定删除模块 ${_ctxModuleId}?`)) return;
454
+ const res = await fetch(`/api/modules/${_ctxModuleId}`, { method: "DELETE" });
455
+ if (res.ok) { showToast("已删除模块"); loadTabContent(currentTab); }
456
+ else {
457
+ const data = await res.json().catch(() => ({}));
458
+ showToast(data.detail || "删除失败");
459
+ }
460
+ }
461
+
423
462
  // ── View Detail Modal ──
424
463
 
425
464
  async function viewDetail() {
@@ -455,7 +494,7 @@ async function viewSnapshot() {
455
494
  const res = await fetch(`/api/tasks/${_ctxTaskId}/snapshots/files/changes.diff`);
456
495
  if (res.ok) {
457
496
  const data = await res.json();
458
- body.innerHTML = renderDiff(data.content || "");
497
+ body.innerHTML = renderDiffByFile(data.content || "");
459
498
  } else {
460
499
  body.innerHTML = '<div class="no-data">加载 diff 失败</div>';
461
500
  }
@@ -480,6 +519,8 @@ async function viewFile(taskId, snapName, filepath) {
480
519
 
481
520
  document.getElementById("modal-file-title").textContent = filepath;
482
521
  document.getElementById("modal-file-body").innerHTML = renderFileContent(filepath, content);
522
+ _editingFilePath = null;
523
+ resetFileEditButtons(false);
483
524
  openModal("modal-file");
484
525
  }
485
526
 
@@ -490,11 +531,13 @@ async function viewReqDoc() {
490
531
  const art = await fetch(`/api/tasks/${_ctxTaskId}/doc-artifacts`).then(r => r.json());
491
532
  if (!art.requirement_doc) { showToast("无需求文档"); return; }
492
533
 
493
- const res = await fetch(`/api/tasks/${_ctxTaskId}/doc-artifacts/requirement/${art.requirement_doc}`);
534
+ const res = await fetch(`/api/tasks/${_ctxTaskId}/doc-artifacts/prds/${art.requirement_doc}`);
494
535
  if (!res.ok) { showToast("加载失败"); return; }
495
536
  const data = await res.json();
496
537
  document.getElementById("modal-file-title").textContent = art.requirement_doc;
497
538
  document.getElementById("modal-file-body").innerHTML = `<div class="log-md-content">${renderDiffMarkers(marked.parse(data.content || ''))}</div>`;
539
+ _editingFilePath = null;
540
+ resetFileEditButtons(false);
498
541
  openModal("modal-file");
499
542
  }
500
543
 
@@ -510,9 +553,59 @@ async function viewDevDoc() {
510
553
  const data = await res.json();
511
554
  document.getElementById("modal-file-title").textContent = art.develop_doc;
512
555
  document.getElementById("modal-file-body").innerHTML = `<div class="log-md-content">${renderDiffMarkers(marked.parse(data.content || ''))}</div>`;
556
+ _editingFilePath = { taskId: _ctxTaskId, type: "develop", path: art.develop_doc, rawContent: data.content || "" };
557
+ resetFileEditButtons(true);
513
558
  openModal("modal-file");
514
559
  }
515
560
 
561
+ function resetFileEditButtons(showEdit) {
562
+ document.getElementById("btn-edit-file").classList.toggle("hidden", !showEdit);
563
+ document.getElementById("btn-save-file").classList.add("hidden");
564
+ document.getElementById("btn-cancel-edit").classList.add("hidden");
565
+ document.getElementById("modal-file-body").classList.remove("hidden");
566
+ document.getElementById("file-editor").classList.add("hidden");
567
+ }
568
+
569
+ function closeModal(id) {
570
+ document.getElementById(id).classList.add("hidden");
571
+ _editingFilePath = null;
572
+ resetFileEditButtons(false);
573
+ }
574
+
575
+ function toggleFileEdit() {
576
+ if (!_editingFilePath) return;
577
+ const editor = document.getElementById("file-editor");
578
+ editor.value = _editingFilePath.rawContent;
579
+ document.getElementById("modal-file-body").classList.add("hidden");
580
+ editor.classList.remove("hidden");
581
+ document.getElementById("btn-edit-file").classList.add("hidden");
582
+ document.getElementById("btn-save-file").classList.remove("hidden");
583
+ document.getElementById("btn-cancel-edit").classList.remove("hidden");
584
+ }
585
+
586
+ function cancelFileEdit() {
587
+ resetFileEditButtons(true);
588
+ }
589
+
590
+ async function saveFileEdit() {
591
+ if (!_editingFilePath) return;
592
+ const content = document.getElementById("file-editor").value;
593
+ const { taskId, type, path } = _editingFilePath;
594
+ const res = await fetch(`/api/tasks/${taskId}/doc-artifacts/${type}/${path}`, {
595
+ method: "PUT",
596
+ headers: { "Content-Type": "application/json" },
597
+ body: JSON.stringify({ content }),
598
+ });
599
+ if (res.ok) {
600
+ _editingFilePath.rawContent = content;
601
+ document.getElementById("modal-file-body").innerHTML = `<div class="log-md-content">${renderDiffMarkers(marked.parse(content))}</div>`;
602
+ resetFileEditButtons(true);
603
+ showToast("已保存");
604
+ } else {
605
+ showToast("保存失败");
606
+ }
607
+ }
608
+
516
609
  // ── Doc-gen: View Sketch Images ──
517
610
 
518
611
  async function viewImages() {
@@ -521,7 +614,7 @@ async function viewImages() {
521
614
  const body = document.getElementById("modal-gallery-body");
522
615
 
523
616
  if (!art.images || art.images.length === 0) {
524
- body.innerHTML = '<div class="no-data">暂无草图图片</div>';
617
+ body.innerHTML = '<div class="no-data">暂无图片</div>';
525
618
  } else {
526
619
  body.innerHTML = `<div class="image-grid">
527
620
  ${art.images.map(img =>
@@ -556,6 +649,49 @@ function renderDiff(content) {
556
649
  return `<div class="diff-viewer">${html}</div>`;
557
650
  }
558
651
 
652
+ function renderDiffByFile(content) {
653
+ const lines = content.split("\n");
654
+ const files = [];
655
+ let current = null;
656
+ for (const line of lines) {
657
+ if (line.startsWith("diff --git ")) {
658
+ const match = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
659
+ const fileName = match ? match[2] : line;
660
+ current = { name: fileName, lines: [] };
661
+ files.push(current);
662
+ }
663
+ if (current) current.lines.push(line);
664
+ }
665
+ if (files.length <= 1) return renderDiff(content);
666
+
667
+ let idx = 0;
668
+ const fileItems = files.map(f => {
669
+ const addCount = f.lines.filter(l => l.startsWith("+") && !l.startsWith("+++")).length;
670
+ const delCount = f.lines.filter(l => l.startsWith("-") && !l.startsWith("---")).length;
671
+ return `<div class="snap-file-item" onclick="toggleDiffFile(this, 'diff-file-${idx}')">
672
+ <span class="material-icons">description</span>
673
+ <span style="flex:1">${esc(f.name)}</span>
674
+ <span style="color:var(--green-600);font-size:11px">+${addCount}</span>
675
+ <span style="color:var(--red-600);font-size:11px">-${delCount}</span>
676
+ <span class="material-icons" style="font-size:16px;color:var(--g400)">expand_more</span>
677
+ </div>
678
+ <div id="diff-file-${idx++}" style="display:none">${renderDiff(f.lines.join("\n"))}</div>`;
679
+ }).join("");
680
+ return fileItems;
681
+ }
682
+
683
+ function toggleDiffFile(item, diffId) {
684
+ const el = document.getElementById(diffId);
685
+ const icon = item.querySelector('.material-icons:last-child');
686
+ if (el.style.display === "none") {
687
+ el.style.display = "block";
688
+ icon.textContent = "expand_less";
689
+ } else {
690
+ el.style.display = "none";
691
+ icon.textContent = "expand_more";
692
+ }
693
+ }
694
+
559
695
  function renderFileContent(filepath, content) {
560
696
  const ext = filepath.split(".").pop().toLowerCase();
561
697
  const langMap = {
@@ -577,19 +713,15 @@ function openModal(id) {
577
713
  document.getElementById(id).classList.remove("hidden");
578
714
  }
579
715
 
580
- function closeModal(id) {
581
- document.getElementById(id).classList.add("hidden");
582
- }
583
-
584
716
  document.querySelectorAll(".modal-overlay").forEach(el => {
585
- el.addEventListener("click", e => {
586
- if (e.target === el) el.classList.add("hidden");
717
+ el.addEventListener("mousedown", e => {
718
+ if (e.target === el) closeModal(el.id);
587
719
  });
588
720
  });
589
721
 
590
722
  document.addEventListener("keydown", e => {
591
723
  if (e.key === "Escape") {
592
- document.querySelectorAll(".modal-overlay:not(.hidden)").forEach(el => el.classList.add("hidden"));
724
+ document.querySelectorAll(".modal-overlay:not(.hidden)").forEach(el => closeModal(el.id));
593
725
  }
594
726
  });
595
727
 
@@ -606,7 +738,7 @@ function esc(text) {
606
738
  }
607
739
 
608
740
  function statusLabel(status) {
609
- return { running: "运行中", completed: "已完成", failed: "失败" }[status] || status;
741
+ return { pending: "进行中", running: "运行中", completed: "已完成", failed: "失败" }[status] || status;
610
742
  }
611
743
 
612
744
  function calcProgress(task) {
@@ -99,7 +99,6 @@
99
99
 
100
100
  <!-- 右键菜单: code-gen 任务 -->
101
101
  <div id="ctx-code-gen" class="ctx-menu hidden">
102
- <div class="ctx-item" data-action="copy-id"><span class="material-icons">content_copy</span>拷贝 ID</div>
103
102
  <div class="ctx-item" data-action="view-detail"><span class="material-icons">info</span>查看详情</div>
104
103
  <div class="ctx-item" data-action="view-snapshot"><span class="material-icons">code</span>查看代码快照</div>
105
104
  <div class="ctx-sep"></div>
@@ -109,23 +108,21 @@
109
108
  <!-- 右键菜单: doc-gen 任务 -->
110
109
  <div id="ctx-doc-gen" class="ctx-menu hidden">
111
110
  <div class="ctx-item" data-action="view-req-doc"><span class="material-icons">article</span>查看需求文档</div>
112
- <div class="ctx-item" data-action="view-images"><span class="material-icons">image</span>查看草图图片</div>
111
+ <div class="ctx-item" data-action="view-images"><span class="material-icons">image</span>查看图片</div>
113
112
  <div class="ctx-item" data-action="view-dev-doc"><span class="material-icons">auto_awesome</span>查看开发文档</div>
114
- <div class="ctx-sep"></div>
115
- <div class="ctx-item" data-action="copy-id"><span class="material-icons">content_copy</span>拷贝 ID</div>
116
113
  </div>
117
114
 
118
115
  <!-- 右键菜单: bug-fix 任务 -->
119
116
  <div id="ctx-bug-fix" class="ctx-menu hidden">
120
117
  <div class="ctx-item" data-action="view-detail"><span class="material-icons">info</span>查看详情</div>
121
- <div class="ctx-item" data-action="copy-id"><span class="material-icons">content_copy</span>拷贝 ID</div>
118
+ <div class="ctx-item" data-action="view-snapshot"><span class="material-icons">code</span>查看代码快照</div>
122
119
  <div class="ctx-sep"></div>
123
120
  <div class="ctx-item danger" data-action="delete-task"><span class="material-icons">delete</span>删除任务</div>
124
121
  </div>
125
122
 
126
123
  <!-- 右键菜单: 模块 -->
127
124
  <div id="ctx-module" class="ctx-menu hidden">
128
- <div class="ctx-item" data-action="copy-module-id"><span class="material-icons">content_copy</span>拷贝模块 ID</div>
125
+ <div class="ctx-item danger" data-action="delete-module"><span class="material-icons">delete</span>删除模块</div>
129
126
  </div>
130
127
 
131
128
  <!-- 通用弹框: 详情 -->
@@ -150,14 +147,20 @@
150
147
  </div>
151
148
  </div>
152
149
 
153
- <!-- 通用弹框: 文件查看 -->
150
+ <!-- 通用弹框: 文件查看/编辑 -->
154
151
  <div id="modal-file" class="modal-overlay hidden">
155
152
  <div class="modal-box wide">
156
153
  <div class="modal-head">
157
154
  <span class="modal-title" id="modal-file-title">文件内容</span>
158
- <span class="material-icons modal-close" onclick="closeModal('modal-file')">close</span>
155
+ <div class="modal-head-right">
156
+ <button class="head-btn" id="btn-edit-file" onclick="toggleFileEdit()"><span class="material-icons" style="font-size:16px">edit</span>编辑</button>
157
+ <button class="head-btn head-btn-save hidden" id="btn-save-file" onclick="saveFileEdit()"><span class="material-icons" style="font-size:16px">save</span>保存</button>
158
+ <button class="head-btn head-btn-cancel hidden" id="btn-cancel-edit" onclick="cancelFileEdit()"><span class="material-icons" style="font-size:16px">close</span>取消</button>
159
+ <span class="material-icons modal-close" onclick="closeModal('modal-file')">close</span>
160
+ </div>
159
161
  </div>
160
162
  <div class="modal-body" id="modal-file-body"></div>
163
+ <textarea class="file-editor hidden" id="file-editor"></textarea>
161
164
  </div>
162
165
  </div>
163
166
 
@@ -165,7 +168,7 @@
165
168
  <div id="modal-gallery" class="modal-overlay hidden">
166
169
  <div class="modal-box wide">
167
170
  <div class="modal-head">
168
- <span class="modal-title">草图图片</span>
171
+ <span class="modal-title">图片</span>
169
172
  <span class="material-icons modal-close" onclick="closeModal('modal-gallery')">close</span>
170
173
  </div>
171
174
  <div class="modal-body" id="modal-gallery-body"></div>
@@ -116,7 +116,7 @@ body {
116
116
  }
117
117
 
118
118
  .nav-item:hover {
119
- background: var(--sidebar-hover);
119
+ background: #1e2230;
120
120
  color: var(--g200);
121
121
  }
122
122
 
@@ -297,7 +297,7 @@ body {
297
297
  display: flex;
298
298
  align-items: center;
299
299
  justify-content: space-between;
300
- padding: 14px 20px;
300
+ padding: 10px 20px;
301
301
  background: var(--surface);
302
302
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
303
303
  cursor: pointer;
@@ -319,7 +319,7 @@ body {
319
319
  .module-expand.open { transform: rotate(180deg); }
320
320
 
321
321
  .module-name {
322
- font-size: 14px; font-weight: 600; color: var(--g900);
322
+ font-size: 15px; font-weight: 600; color: var(--g900);
323
323
  }
324
324
 
325
325
  .module-id {
@@ -333,7 +333,7 @@ body {
333
333
  }
334
334
 
335
335
  .module-count {
336
- font-size: 12px; color: var(--g500);
336
+ font-size: 13px; color: var(--g500);
337
337
  }
338
338
 
339
339
  /* ── Module Drawer ── */
@@ -393,7 +393,7 @@ body {
393
393
  }
394
394
 
395
395
  .chain-title {
396
- font-size: 12px; font-weight: 500; color: var(--g700);
396
+ font-size: 13px; font-weight: 500; color: var(--g700);
397
397
  }
398
398
 
399
399
  .chain-arrow {
@@ -457,10 +457,10 @@ body {
457
457
  transition: background .15s;
458
458
  }
459
459
 
460
- .task-card:hover { background: var(--surface-dim); }
460
+ .task-card:hover { background: #f4f6f9; }
461
461
 
462
462
  .task-header {
463
- padding: 14px 20px;
463
+ padding: 10px 20px;
464
464
  display: flex;
465
465
  align-items: center;
466
466
  justify-content: space-between;
@@ -468,12 +468,12 @@ body {
468
468
  }
469
469
 
470
470
  .task-title {
471
- font-size: 13px; font-weight: 600; color: var(--g900);
471
+ font-size: 14px; font-weight: 600; color: var(--g900);
472
472
  }
473
473
 
474
474
  .task-meta {
475
- font-size: 11px; color: var(--g500);
476
- margin-top: 3px;
475
+ font-size: 12px; color: var(--g500);
476
+ margin-top: 2px;
477
477
  display: flex; align-items: center; gap: 6px;
478
478
  }
479
479
 
@@ -484,15 +484,15 @@ body {
484
484
  /* ── Phase Chips ── */
485
485
 
486
486
  .phases-row {
487
- padding: 8px 20px 12px;
488
- display: flex; flex-wrap: wrap; gap: 6px;
487
+ padding: 6px 20px 10px;
488
+ display: flex; flex-wrap: wrap; gap: 5px;
489
489
  }
490
490
 
491
491
  .phase-chip {
492
492
  display: inline-flex; align-items: center;
493
493
  padding: 3px 10px;
494
494
  border-radius: 14px;
495
- font-size: 11px; font-weight: 500;
495
+ font-size: 12px; font-weight: 500;
496
496
  white-space: nowrap;
497
497
  transition: transform .12s;
498
498
  }
@@ -508,12 +508,13 @@ body {
508
508
  /* ── Status Badge ── */
509
509
 
510
510
  .status-badge {
511
- font-size: 11px;
511
+ font-size: 12px;
512
512
  padding: 3px 12px;
513
513
  border-radius: 12px;
514
514
  font-weight: 600;
515
515
  letter-spacing: .2px;
516
516
  }
517
+ .status-badge.pending { background: var(--g100); color: var(--g600); }
517
518
  .status-badge.running { background: var(--blue-50); color: var(--blue-600); }
518
519
  .status-badge.completed { background: var(--green-100); color: var(--green-700); }
519
520
  .status-badge.failed { background: var(--red-100); color: var(--red-600); }
@@ -575,18 +576,25 @@ body {
575
576
 
576
577
  .artifact-tag .material-icons { font-size: 14px; }
577
578
 
579
+ .artifact-tag.figma {
580
+ background: #f0e6ff;
581
+ color: #7c3aed;
582
+ border-color: #e0d0ff;
583
+ }
584
+
578
585
  /* ── Bug Card ── */
579
586
 
580
587
  .bug-card {
581
588
  border-top: 1px solid var(--g100);
582
- padding: 14px 20px;
589
+ padding: 0;
583
590
  transition: background .15s;
584
591
  }
585
592
 
586
- .bug-card:hover { background: var(--surface-dim); }
593
+ .bug-card:hover { background: #f4f6f9; }
587
594
 
588
595
  .bug-header {
589
596
  display: flex; align-items: flex-start; gap: 12px;
597
+ padding: 10px 20px;
590
598
  }
591
599
 
592
600
  .bug-status-icon {
@@ -598,12 +606,12 @@ body {
598
606
  .bug-info { flex: 1; min-width: 0; }
599
607
 
600
608
  .bug-title {
601
- font-size: 13px; font-weight: 600; color: var(--g900);
609
+ font-size: 14px; font-weight: 600; color: var(--g900);
602
610
  }
603
611
 
604
612
  .bug-desc {
605
- font-size: 12px; color: var(--g600);
606
- margin-top: 4px;
613
+ font-size: 13px; color: var(--g600);
614
+ margin-top: 3px;
607
615
  line-height: 1.5;
608
616
  }
609
617
 
@@ -625,6 +633,7 @@ body {
625
633
 
626
634
  .bug-footer {
627
635
  margin-top: 8px;
636
+ padding: 0 20px 12px;
628
637
  font-size: 11px; color: var(--g500);
629
638
  display: flex; gap: 14px;
630
639
  }
@@ -642,7 +651,7 @@ body {
642
651
  border: 1px solid var(--g200);
643
652
  }
644
653
 
645
- .ctx-menu.hidden { display: none; }
654
+ .ctx-menu.hidden, .hidden, button.hidden { display: none !important; }
646
655
 
647
656
  .ctx-sep {
648
657
  height: 1px;
@@ -714,6 +723,9 @@ body {
714
723
  padding: 16px 20px;
715
724
  border-bottom: 1px solid var(--g100);
716
725
  }
726
+ .modal-head-right {
727
+ display: flex; align-items: center; gap: 8px;
728
+ }
717
729
 
718
730
  .modal-title {
719
731
  font-size: 14px; font-weight: 600; color: var(--g900);
@@ -1032,3 +1044,35 @@ body {
1032
1044
  .panel-header { padding: 16px 20px 12px; }
1033
1045
  .panel-content { padding: 12px 16px 32px; }
1034
1046
  }
1047
+
1048
+ /* ── File Editor ── */
1049
+
1050
+ .head-btn {
1051
+ display: inline-flex; align-items: center; gap: 4px;
1052
+ padding: 4px 12px; border-radius: 6px;
1053
+ background: rgba(255,255,255,0.06);
1054
+ border: 1px solid rgba(255,255,255,0.12);
1055
+ color: #b0b8c8; font-size: 13px; cursor: pointer;
1056
+ transition: all 0.15s;
1057
+ }
1058
+ .head-btn:hover { background: rgba(255,255,255,0.1); color: #e0e4ec; }
1059
+ .head-btn-save { color: #34d399; border-color: rgba(52,211,153,0.3); }
1060
+ .head-btn-save:hover { background: rgba(52,211,153,0.1); }
1061
+ .head-btn-cancel { color: #f87171; border-color: rgba(248,113,113,0.3); }
1062
+ .head-btn-cancel:hover { background: rgba(248,113,113,0.1); }
1063
+ .file-editor {
1064
+ width: 100%;
1065
+ min-height: 400px;
1066
+ padding: 16px;
1067
+ background: #0d1117;
1068
+ color: #c9d1d9;
1069
+ border: 1px solid #30363d;
1070
+ border-radius: 6px;
1071
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
1072
+ font-size: 13px;
1073
+ line-height: 1.6;
1074
+ resize: vertical;
1075
+ outline: none;
1076
+ tab-size: 2;
1077
+ }
1078
+ .file-editor:focus { border-color: #58a6ff; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-coding-flow",
3
- "version": "1.3.2",
3
+ "version": "1.4.1",
4
4
  "description": "Claude Code skills for requirement analysis, code generation and bug fixing",
5
5
  "bin": {
6
6
  "flow": "bin/flow.js"