@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.
- package/package.json +1 -1
- package/view/index.html +53 -6
package/package.json
CHANGED
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
|
-
|
|
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',
|
|
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
|
-
|
|
343
|
-
|
|
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)
|