@aicupa/plugin-todo-dependency 1.0.5 → 1.0.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicupa/plugin-todo-dependency",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Set and visualize dependencies between todo items",
5
5
  "description_zh": "设置并可视化待办事项之间的依赖关系",
6
6
  "main": "./service",
package/service.js CHANGED
@@ -11,6 +11,7 @@ module.exports = (api) => {
11
11
  id: node.todo.id,
12
12
  content: node.todo.content,
13
13
  done: node.todo.done,
14
+ focus: node.todo.focus || false,
14
15
  level: node.todo.level,
15
16
  depIds: node.todo.depIds || [],
16
17
  })
@@ -83,6 +84,32 @@ module.exports = (api) => {
83
84
  }
84
85
  },
85
86
 
87
+ async toggleFocus({ todoId, focus, filePath }) {
88
+ try {
89
+ const content = await api.readFile(filePath)
90
+ const data = JSON.parse(content)
91
+ const todotree = data.todotree
92
+
93
+ function findAndUpdate(nodes) {
94
+ for (const node of nodes) {
95
+ if (node.todo && node.todo.id === todoId) {
96
+ node.todo.focus = focus
97
+ return true
98
+ }
99
+ if (node.children?.length && findAndUpdate(node.children)) return true
100
+ }
101
+ return false
102
+ }
103
+
104
+ findAndUpdate(todotree.tree)
105
+ await api.store('todotree', todotree, filePath)
106
+ await api.reload(filePath)
107
+ return { ok: true }
108
+ } catch (e) {
109
+ return { ok: false, error: e.message }
110
+ }
111
+ },
112
+
86
113
  async toggleDone({ todoId, done, filePath }) {
87
114
  try {
88
115
  const content = await api.readFile(filePath)
@@ -94,9 +121,10 @@ module.exports = (api) => {
94
121
  if (node.todo && node.todo.id === todoId) {
95
122
  node.todo.done = done
96
123
  if (done) {
97
- node.todo.doneAt = Date.now()
124
+ node.todo.end = Date.now()
125
+ node.todo.focus = false
98
126
  } else {
99
- delete node.todo.doneAt
127
+ delete node.todo.end
100
128
  }
101
129
  return true
102
130
  }
package/view/index.html CHANGED
@@ -52,7 +52,7 @@
52
52
  padding: 10px 12px; cursor: default;
53
53
  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
54
54
  transition: box-shadow 0.2s, transform 0.15s;
55
- display: flex; align-items: flex-start; gap: 10px;
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); }
58
58
  .dark .node { background: #2d2d2d; border-color: #3c3c3c; box-shadow: 0 1px 4px rgba(0,0,0,0.3); }
@@ -79,7 +79,22 @@
79
79
  .dark .node-check.checked { border-color: #73d13d; background: #73d13d; }
80
80
  .dark .node-check.checked::after { border-color: #2d2d2d; }
81
81
 
82
- .node-body { flex: 1; min-width: 0; }
82
+ .node-body { flex: 1; min-width: 0; position: relative; }
83
+
84
+ .node-focus {
85
+ position: absolute; top: -2px; right: -4px;
86
+ width: 16px; height: 16px; cursor: pointer;
87
+ border: none; background: none; padding: 0;
88
+ color: #bbb; transition: color 0.15s;
89
+ display: flex; align-items: center; justify-content: center;
90
+ opacity: 0; transition: opacity 0.15s, color 0.15s;
91
+ }
92
+ .node:hover .node-focus { opacity: 1; }
93
+ .node-focus.active { opacity: 1; color: #1890ff; }
94
+ .node-focus:hover { color: #1890ff; }
95
+ .dark .node-focus { color: #555; }
96
+ .dark .node-focus:hover { color: #40a9ff; }
97
+ .dark .node-focus.active { color: #40a9ff; }
83
98
 
84
99
  .node-content {
85
100
  font-size: 12px; line-height: 1.5; word-break: break-word;
@@ -91,12 +106,14 @@
91
106
  display: inline-block; font-size: 10px; padding: 1px 6px;
92
107
  border-radius: 10px; margin-top: 5px;
93
108
  }
94
- .badge-pending { background: #fff7e6; color: #d46b08; }
95
- .badge-done { background: #f6ffed; color: #389e0d; }
96
- .badge-blocked { background: #fff1f0; color: #cf1322; }
97
- .dark .badge-pending { background: #3a3000; color: #ffc53d; }
98
- .dark .badge-done { background: #1e3a1e; color: #73d13d; }
99
- .dark .badge-blocked { background: #3a1a1a; color: #ff4d4f; }
109
+ .badge-pending { background: #fff7e6; color: #d46b08; }
110
+ .badge-done { background: #f6ffed; color: #389e0d; }
111
+ .badge-blocked { background: #fff1f0; color: #cf1322; }
112
+ .badge-focus { background: #e6f7ff; color: #096dd9; }
113
+ .dark .badge-pending { background: #3a3000; color: #ffc53d; }
114
+ .dark .badge-done { background: #1e3a1e; color: #73d13d; }
115
+ .dark .badge-blocked { background: #3a1a1a; color: #ff4d4f; }
116
+ .dark .badge-focus { background: #111d2c; color: #40a9ff; }
100
117
 
101
118
  .node.blocked { border-style: dashed; opacity: 0.7; }
102
119
  .dark .node.blocked { opacity: 0.7; }
@@ -160,7 +177,7 @@
160
177
  emptyHint: '右键 Todo 节点,选择「设置依赖」<br>即可建立待办之间的依赖关系',
161
178
  allDoneTitle: '所有依赖任务已完成 🎉',
162
179
  allDoneHint: '当前依赖链路中的任务均已完成<br>如有新的依赖关系,请重新创建',
163
- done: '已完成', pending: '待完成', blocked: '阻塞中', notFound: '未找到该 Todo',
180
+ done: '已完成', pending: '待完成', blocked: '阻塞中', focus: '进行中', unfocus: '取消进行', notFound: '未找到该 Todo',
164
181
  cycleWarning: '检测到循环依赖,部分节点的层级可能不准确',
165
182
  },
166
183
  en: {
@@ -169,7 +186,7 @@
169
186
  emptyHint: 'Right-click a Todo and select "Set Dependencies"<br>to create dependency relationships',
170
187
  allDoneTitle: 'All dependency tasks completed 🎉',
171
188
  allDoneHint: 'All tasks in dependency chains are done<br>Create new dependencies if needed',
172
- done: 'Done', pending: 'Pending', blocked: 'Blocked', notFound: 'Todo not found',
189
+ done: 'Done', pending: 'Pending', blocked: 'Blocked', focus: 'In Progress', unfocus: 'Cancel Focus', notFound: 'Todo not found',
173
190
  cycleWarning: 'Cycle detected — some nodes may be positioned incorrectly',
174
191
  },
175
192
  }
@@ -374,6 +391,13 @@
374
391
  autoScan()
375
392
  }
376
393
 
394
+ async function toggleTodoFocus(todoId, focus) {
395
+ try {
396
+ await callPlugin('toggleFocus', { todoId, focus, filePath: currentFilePath })
397
+ } catch {}
398
+ autoScan()
399
+ }
400
+
377
401
  function renderGraph(nodeIds, edges, positions, todoData, layout) {
378
402
  const container = document.getElementById('graphContainer')
379
403
  container.innerHTML = ''
@@ -464,6 +488,18 @@
464
488
  }
465
489
 
466
490
  const body = document.createElement('div'); body.className = 'node-body'
491
+
492
+ if (todo && !todo.done && !blocked) {
493
+ const focusBtn = document.createElement('button')
494
+ focusBtn.className = 'node-focus' + (todo.focus ? ' active' : '')
495
+ focusBtn.title = todo.focus ? t.unfocus : t.focus
496
+ focusBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2" fill="currentColor"/></svg>'
497
+ focusBtn.addEventListener('click', e => {
498
+ e.stopPropagation()
499
+ toggleTodoFocus(id, !todo.focus)
500
+ })
501
+ body.appendChild(focusBtn)
502
+ }
467
503
  const content = document.createElement('div'); content.className = 'node-content'
468
504
  if (todo) {
469
505
  content.textContent = todo.content
@@ -481,6 +517,9 @@
481
517
  } else if (blocked) {
482
518
  sb.className = 'node-badge badge-blocked'
483
519
  sb.textContent = t.blocked
520
+ } else if (todo.focus) {
521
+ sb.className = 'node-badge badge-focus'
522
+ sb.textContent = t.focus
484
523
  } else {
485
524
  sb.className = 'node-badge badge-pending'
486
525
  sb.textContent = t.pending