@aicupa/plugin-todo-dependency 1.0.1 → 1.0.2

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 (2) hide show
  1. package/package.json +1 -1
  2. package/view/index.html +53 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicupa/plugin-todo-dependency",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Set and visualize dependencies between todo items",
5
5
  "description_zh": "设置并可视化待办事项之间的依赖关系",
6
6
  "main": "./service",
package/view/index.html CHANGED
@@ -67,8 +67,13 @@
67
67
  .node-badge { font-size: 10px; padding: 1px 6px; border-radius: 10px; flex-shrink: 0; }
68
68
  .badge-pending { background: #fff7e6; color: #d46b08; }
69
69
  .badge-done { background: #f6ffed; color: #389e0d; }
70
+ .badge-blocked { background: #fff1f0; color: #cf1322; }
70
71
  .dark .badge-pending { background: #332b00; color: #ffc53d; }
71
72
  .dark .badge-done { background: #0a2e0a; color: #73d13d; }
73
+ .dark .badge-blocked { background: #2a1215; color: #ff4d4f; }
74
+
75
+ .node.blocked { border-style: dashed; opacity: 0.7; }
76
+ .dark .node.blocked { opacity: 0.7; }
72
77
 
73
78
  .node-content {
74
79
  font-size: 12px; line-height: 1.5; word-break: break-word;
@@ -133,14 +138,14 @@
133
138
  title: '依赖图谱', refresh: '刷新',
134
139
  emptyTitle: '暂无依赖关系',
135
140
  emptyHint: '右键 Todo 节点,选择「设置依赖」<br>即可建立待办之间的依赖关系',
136
- done: '已完成', pending: '待完成', notFound: '未找到该 Todo',
141
+ done: '已完成', pending: '待完成', blocked: '阻塞中', notFound: '未找到该 Todo',
137
142
  cycleWarning: '检测到循环依赖,部分节点的层级可能不准确',
138
143
  },
139
144
  en: {
140
145
  title: 'Dependency Graph', refresh: 'Refresh',
141
146
  emptyTitle: 'No dependencies yet',
142
147
  emptyHint: 'Right-click a Todo and select "Set Dependencies"<br>to create dependency relationships',
143
- done: 'Done', pending: 'Pending', notFound: 'Todo not found',
148
+ done: 'Done', pending: 'Pending', blocked: 'Blocked', notFound: 'Todo not found',
144
149
  cycleWarning: 'Cycle detected — some nodes may be positioned incorrectly',
145
150
  },
146
151
  }
@@ -300,8 +305,28 @@
300
305
  container.style.width = layout.width + 'px'
301
306
  container.style.height = layout.height + 'px'
302
307
 
308
+ // Build dependency map: nodeId -> [depIds]
309
+ const depsOf = {}
310
+ for (const [from, to] of edges) {
311
+ if (!depsOf[to]) depsOf[to] = []
312
+ depsOf[to].push(from)
313
+ }
314
+
315
+ function isBlocked(id) {
316
+ const deps = depsOf[id]
317
+ if (!deps?.length) return false
318
+ const todo = todoData[id]
319
+ if (todo?.done) return false
320
+ return deps.some(depId => {
321
+ const dep = todoData[depId]
322
+ return !dep || !dep.done
323
+ })
324
+ }
325
+
303
326
  const edgeColor = isDark ? '#4a4a6a' : '#c8c8d0'
327
+ const blockedEdgeColor = isDark ? '#5c2020' : '#ffccc7'
304
328
  const arrowColor = isDark ? '#6a6a8a' : '#aaa'
329
+ const blockedArrowColor = isDark ? '#ff4d4f' : '#ff7875'
305
330
  const svg = svgEl('svg', { id: 'edgeSvg', width: layout.width, height: layout.height })
306
331
  const defs = svgEl('defs', {})
307
332
  const marker = svgEl('marker', {
@@ -309,7 +334,12 @@
309
334
  refX: 9, refY: 3.5, orient: 'auto', markerUnits: 'strokeWidth',
310
335
  })
311
336
  marker.appendChild(svgEl('polygon', { points: '0 0.5,9 3.5,0 6.5', fill: arrowColor }))
312
- defs.appendChild(marker); svg.appendChild(defs)
337
+ const blockedMarker = svgEl('marker', {
338
+ id: 'arrow-blocked', markerWidth: 10, markerHeight: 7,
339
+ refX: 9, refY: 3.5, orient: 'auto', markerUnits: 'strokeWidth',
340
+ })
341
+ blockedMarker.appendChild(svgEl('polygon', { points: '0 0.5,9 3.5,0 6.5', fill: blockedArrowColor }))
342
+ defs.appendChild(marker); defs.appendChild(blockedMarker); svg.appendChild(defs)
313
343
 
314
344
  for (const [from, to] of edges) {
315
345
  const p1 = positions[from], p2 = positions[to]
@@ -317,9 +347,16 @@
317
347
  const x1 = p1.x + NODE_W / 2, y1 = p1.y + NODE_H
318
348
  const x2 = p2.x + NODE_W / 2, y2 = p2.y
319
349
  const cp = Math.max(Math.abs(y2 - y1) * 0.35, 20)
350
+ const fromDone = todoData[from]?.done
351
+ const toBlocked = isBlocked(to)
352
+ const isBlockingEdge = toBlocked && !fromDone
320
353
  svg.appendChild(svgEl('path', {
321
354
  d: `M${x1},${y1} C${x1},${y1 + cp} ${x2},${y2 - cp} ${x2},${y2}`,
322
- fill: 'none', stroke: edgeColor, 'stroke-width': 2, 'marker-end': 'url(#arrow)',
355
+ fill: 'none',
356
+ stroke: isBlockingEdge ? blockedEdgeColor : edgeColor,
357
+ 'stroke-width': 2,
358
+ 'stroke-dasharray': isBlockingEdge ? '6,3' : 'none',
359
+ 'marker-end': isBlockingEdge ? 'url(#arrow-blocked)' : 'url(#arrow)',
323
360
  }))
324
361
  }
325
362
  container.appendChild(svg)
@@ -327,10 +364,12 @@
327
364
  for (const id of nodeIds) {
328
365
  const pos = positions[id]; if (!pos) continue
329
366
  const todo = todoData[id]
367
+ const blocked = isBlocked(id)
330
368
  const el = document.createElement('div'); el.className = 'node'
331
369
  if (todo) {
332
370
  el.classList.add('level-' + (todo.level || 'default'))
333
371
  if (todo.done) el.classList.add('done')
372
+ else if (blocked) el.classList.add('blocked')
334
373
  } else el.classList.add('not-found')
335
374
  el.style.cssText = `left:${pos.x}px;top:${pos.y}px;width:${NODE_W}px;`
336
375
 
@@ -339,8 +378,16 @@
339
378
  badge.textContent = '#' + id; header.appendChild(badge)
340
379
  if (todo) {
341
380
  const sb = document.createElement('span')
342
- sb.className = 'node-badge ' + (todo.done ? 'badge-done' : 'badge-pending')
343
- sb.textContent = todo.done ? t.done : t.pending
381
+ if (todo.done) {
382
+ sb.className = 'node-badge badge-done'
383
+ sb.textContent = t.done
384
+ } else if (blocked) {
385
+ sb.className = 'node-badge badge-blocked'
386
+ sb.textContent = t.blocked
387
+ } else {
388
+ sb.className = 'node-badge badge-pending'
389
+ sb.textContent = t.pending
390
+ }
344
391
  header.appendChild(sb)
345
392
  }
346
393
  el.appendChild(header)