@aicupa/plugin-todo-dependency 1.0.7 → 1.0.9
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 +75 -3
package/package.json
CHANGED
package/view/index.html
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
border: 2px solid #d9d9d9; border-radius: 10px;
|
|
52
52
|
padding: 10px 12px; cursor: default;
|
|
53
53
|
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
|
54
|
-
transition: box-shadow 0.2s, transform 0.15s;
|
|
54
|
+
transition: box-shadow 0.2s, transform 0.15s, opacity 0.25s;
|
|
55
55
|
display: flex; align-items: flex-start; gap: 8px;
|
|
56
56
|
}
|
|
57
57
|
.node:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.1); transform: translateY(-1px); }
|
|
@@ -125,6 +125,8 @@
|
|
|
125
125
|
.level-danger { border-color: #ff4d4f; }
|
|
126
126
|
|
|
127
127
|
#edgeSvg { position: absolute; top: 0; left: 0; pointer-events: none; }
|
|
128
|
+
#edgeSvg path { transition: stroke-width 0.2s, opacity 0.2s; }
|
|
129
|
+
@keyframes edge-flow { from { stroke-dashoffset: 18; } to { stroke-dashoffset: 0; } }
|
|
128
130
|
|
|
129
131
|
#graphInner { transition: opacity 0.35s ease; }
|
|
130
132
|
#graphInner.fade-out { opacity: 0; }
|
|
@@ -444,7 +446,11 @@
|
|
|
444
446
|
svg.appendChild(defs)
|
|
445
447
|
|
|
446
448
|
const ARROW_H = 6
|
|
449
|
+
const edgeEls = []
|
|
450
|
+
const downMap = {}, upMap = {}
|
|
447
451
|
for (const [from, to] of edges) {
|
|
452
|
+
;(downMap[from] || (downMap[from] = [])).push(to)
|
|
453
|
+
;(upMap[to] || (upMap[to] = [])).push(from)
|
|
448
454
|
const p1 = positions[from], p2 = positions[to]
|
|
449
455
|
if (!p1 || !p2) continue
|
|
450
456
|
const x1 = p1.x + NODE_W / 2, y1 = p1.y + NODE_H
|
|
@@ -454,17 +460,56 @@
|
|
|
454
460
|
const fromDone = todoData[from]?.done
|
|
455
461
|
const toBlocked = isBlocked(to)
|
|
456
462
|
const isBlockingEdge = toBlocked && !fromDone
|
|
457
|
-
|
|
463
|
+
const pathEl = svgEl('path', {
|
|
458
464
|
d: `M${x1},${y1} C${x1},${y1 + cp} ${x2},${y2 - cp} ${x2},${y2}`,
|
|
459
465
|
fill: 'none',
|
|
460
466
|
stroke: isBlockingEdge ? blockedEdgeColor : edgeColor,
|
|
461
467
|
'stroke-width': 1.5,
|
|
462
468
|
'stroke-dasharray': isBlockingEdge ? '6,3' : 'none',
|
|
463
469
|
'marker-end': isBlockingEdge ? 'url(#arrow-blocked)' : 'url(#arrow)',
|
|
464
|
-
})
|
|
470
|
+
})
|
|
471
|
+
svg.appendChild(pathEl)
|
|
472
|
+
edgeEls.push({ el: pathEl, from, to })
|
|
465
473
|
}
|
|
466
474
|
inner.appendChild(svg)
|
|
467
475
|
|
|
476
|
+
// Apply flow animation from each focus node to its downstream end
|
|
477
|
+
const flowNodes = new Set()
|
|
478
|
+
for (const nid of nodeIds) {
|
|
479
|
+
if (todoData[nid]?.focus && !todoData[nid]?.done && !isBlocked(nid)) {
|
|
480
|
+
flowNodes.add(nid)
|
|
481
|
+
const q = [nid]
|
|
482
|
+
while (q.length) {
|
|
483
|
+
const cur = q.shift()
|
|
484
|
+
for (const next of (downMap[cur] || [])) {
|
|
485
|
+
if (!flowNodes.has(next)) { flowNodes.add(next); q.push(next) }
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
edgeEls.forEach(e => {
|
|
491
|
+
if (flowNodes.has(e.from) && flowNodes.has(e.to)) {
|
|
492
|
+
e.el.style.strokeDasharray = '12 6'
|
|
493
|
+
e.el.style.animation = 'edge-flow 0.6s linear infinite'
|
|
494
|
+
}
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
function collectChain(id) {
|
|
498
|
+
const visited = new Set([id])
|
|
499
|
+
const queue = [id]
|
|
500
|
+
while (queue.length) {
|
|
501
|
+
const cur = queue.shift()
|
|
502
|
+
for (const next of (downMap[cur] || [])) { if (!visited.has(next)) { visited.add(next); queue.push(next) } }
|
|
503
|
+
}
|
|
504
|
+
const queue2 = [id]
|
|
505
|
+
while (queue2.length) {
|
|
506
|
+
const cur = queue2.shift()
|
|
507
|
+
for (const prev of (upMap[cur] || [])) { if (!visited.has(prev)) { visited.add(prev); queue2.push(prev) } }
|
|
508
|
+
}
|
|
509
|
+
return visited
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const nodeElList = []
|
|
468
513
|
for (const id of nodeIds) {
|
|
469
514
|
const pos = positions[id]; if (!pos) continue
|
|
470
515
|
const todo = todoData[id]
|
|
@@ -476,6 +521,33 @@
|
|
|
476
521
|
else if (blocked) el.classList.add('blocked')
|
|
477
522
|
} else el.classList.add('not-found')
|
|
478
523
|
el.style.cssText = `left:${pos.x}px;top:${pos.y}px;width:${NODE_W}px;`
|
|
524
|
+
el.dataset.id = id
|
|
525
|
+
nodeElList.push({ id, el })
|
|
526
|
+
el.addEventListener('mouseenter', () => {
|
|
527
|
+
const chain = collectChain(id)
|
|
528
|
+
const svgRoot = document.getElementById('edgeSvg')
|
|
529
|
+
if (svgRoot) svgRoot.style.zIndex = '10'
|
|
530
|
+
edgeEls.forEach(e => {
|
|
531
|
+
if (chain.has(e.from) && chain.has(e.to)) {
|
|
532
|
+
e.el.style.strokeWidth = '2.5'
|
|
533
|
+
e.el.style.opacity = '1'
|
|
534
|
+
} else {
|
|
535
|
+
e.el.style.opacity = '0.15'
|
|
536
|
+
}
|
|
537
|
+
})
|
|
538
|
+
nodeElList.forEach(n => {
|
|
539
|
+
if (!chain.has(n.id)) n.el.style.opacity = '0.5'
|
|
540
|
+
})
|
|
541
|
+
})
|
|
542
|
+
el.addEventListener('mouseleave', () => {
|
|
543
|
+
const svgRoot = document.getElementById('edgeSvg')
|
|
544
|
+
if (svgRoot) svgRoot.style.zIndex = ''
|
|
545
|
+
edgeEls.forEach(e => {
|
|
546
|
+
e.el.style.strokeWidth = ''
|
|
547
|
+
e.el.style.opacity = ''
|
|
548
|
+
})
|
|
549
|
+
nodeElList.forEach(n => { n.el.style.opacity = '' })
|
|
550
|
+
})
|
|
479
551
|
|
|
480
552
|
if (todo) {
|
|
481
553
|
const check = document.createElement('div')
|