aifastdb-devplan 1.6.1 → 1.6.3

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 (97) hide show
  1. package/dist/dev-plan-document-store.d.ts +13 -1
  2. package/dist/dev-plan-document-store.d.ts.map +1 -1
  3. package/dist/dev-plan-document-store.js +119 -0
  4. package/dist/dev-plan-document-store.js.map +1 -1
  5. package/dist/dev-plan-factory.d.ts.map +1 -1
  6. package/dist/dev-plan-factory.js +3 -1
  7. package/dist/dev-plan-factory.js.map +1 -1
  8. package/dist/dev-plan-graph-store.d.ts +341 -9
  9. package/dist/dev-plan-graph-store.d.ts.map +1 -1
  10. package/dist/dev-plan-graph-store.js +2414 -210
  11. package/dist/dev-plan-graph-store.js.map +1 -1
  12. package/dist/dev-plan-interface.d.ts +119 -1
  13. package/dist/dev-plan-interface.d.ts.map +1 -1
  14. package/dist/dev-plan-migrate.d.ts +1 -0
  15. package/dist/dev-plan-migrate.d.ts.map +1 -1
  16. package/dist/dev-plan-migrate.js +28 -2
  17. package/dist/dev-plan-migrate.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/mcp-server/index.js +652 -0
  22. package/dist/mcp-server/index.js.map +1 -1
  23. package/dist/shard-config.d.ts +64 -0
  24. package/dist/shard-config.d.ts.map +1 -0
  25. package/dist/shard-config.js +109 -0
  26. package/dist/shard-config.js.map +1 -0
  27. package/dist/types.d.ts +305 -2
  28. package/dist/types.d.ts.map +1 -1
  29. package/dist/types.js.map +1 -1
  30. package/dist/visualize/graph-canvas/api-compat.d.ts.map +1 -1
  31. package/dist/visualize/graph-canvas/api-compat.js +22 -12
  32. package/dist/visualize/graph-canvas/api-compat.js.map +1 -1
  33. package/dist/visualize/graph-canvas/core.d.ts.map +1 -1
  34. package/dist/visualize/graph-canvas/core.js +296 -4
  35. package/dist/visualize/graph-canvas/core.js.map +1 -1
  36. package/dist/visualize/graph-canvas/interaction.d.ts.map +1 -1
  37. package/dist/visualize/graph-canvas/interaction.js +11 -0
  38. package/dist/visualize/graph-canvas/interaction.js.map +1 -1
  39. package/dist/visualize/graph-canvas/layout-worker.d.ts.map +1 -1
  40. package/dist/visualize/graph-canvas/layout-worker.js +45 -9
  41. package/dist/visualize/graph-canvas/layout-worker.js.map +1 -1
  42. package/dist/visualize/graph-canvas/renderer.d.ts.map +1 -1
  43. package/dist/visualize/graph-canvas/renderer.js +164 -33
  44. package/dist/visualize/graph-canvas/renderer.js.map +1 -1
  45. package/dist/visualize/graph-canvas/styles.d.ts.map +1 -1
  46. package/dist/visualize/graph-canvas/styles.js +146 -121
  47. package/dist/visualize/graph-canvas/styles.js.map +1 -1
  48. package/dist/visualize/graph-canvas/viewport.d.ts.map +1 -1
  49. package/dist/visualize/graph-canvas/viewport.js +10 -0
  50. package/dist/visualize/graph-canvas/viewport.js.map +1 -1
  51. package/dist/visualize/server.js +371 -32
  52. package/dist/visualize/server.js.map +1 -1
  53. package/dist/visualize/template-core.d.ts +9 -0
  54. package/dist/visualize/template-core.d.ts.map +1 -0
  55. package/dist/visualize/template-core.js +721 -0
  56. package/dist/visualize/template-core.js.map +1 -0
  57. package/dist/visualize/template-data-loading.d.ts +7 -0
  58. package/dist/visualize/template-data-loading.d.ts.map +1 -0
  59. package/dist/visualize/template-data-loading.js +677 -0
  60. package/dist/visualize/template-data-loading.js.map +1 -0
  61. package/dist/visualize/template-detail-panel.d.ts +14 -0
  62. package/dist/visualize/template-detail-panel.d.ts.map +1 -0
  63. package/dist/visualize/template-detail-panel.js +624 -0
  64. package/dist/visualize/template-detail-panel.js.map +1 -0
  65. package/dist/visualize/template-graph-3d.d.ts +7 -0
  66. package/dist/visualize/template-graph-3d.d.ts.map +1 -0
  67. package/dist/visualize/template-graph-3d.js +1114 -0
  68. package/dist/visualize/template-graph-3d.js.map +1 -0
  69. package/dist/visualize/template-graph-vis.d.ts +8 -0
  70. package/dist/visualize/template-graph-vis.d.ts.map +1 -0
  71. package/dist/visualize/template-graph-vis.js +1215 -0
  72. package/dist/visualize/template-graph-vis.js.map +1 -0
  73. package/dist/visualize/template-html.d.ts +9 -0
  74. package/dist/visualize/template-html.d.ts.map +1 -0
  75. package/dist/visualize/template-html.js +635 -0
  76. package/dist/visualize/template-html.js.map +1 -0
  77. package/dist/visualize/template-md-viewer.d.ts +11 -0
  78. package/dist/visualize/template-md-viewer.d.ts.map +1 -0
  79. package/dist/visualize/template-md-viewer.js +806 -0
  80. package/dist/visualize/template-md-viewer.js.map +1 -0
  81. package/dist/visualize/template-pages.d.ts +7 -0
  82. package/dist/visualize/template-pages.d.ts.map +1 -0
  83. package/dist/visualize/template-pages.js +1892 -0
  84. package/dist/visualize/template-pages.js.map +1 -0
  85. package/dist/visualize/template-stats-modal.d.ts +7 -0
  86. package/dist/visualize/template-stats-modal.d.ts.map +1 -0
  87. package/dist/visualize/template-stats-modal.js +466 -0
  88. package/dist/visualize/template-stats-modal.js.map +1 -0
  89. package/dist/visualize/template-styles.d.ts +9 -0
  90. package/dist/visualize/template-styles.d.ts.map +1 -0
  91. package/dist/visualize/template-styles.js +623 -0
  92. package/dist/visualize/template-styles.js.map +1 -0
  93. package/dist/visualize/template.d.ts +15 -3
  94. package/dist/visualize/template.d.ts.map +1 -1
  95. package/dist/visualize/template.js +44 -3475
  96. package/dist/visualize/template.js.map +1 -1
  97. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-data-loading.js","sourceRoot":"","sources":["../../src/visualize/template-data-loading.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAEH,oDA2pBC;AA3pBD,SAAgB,oBAAoB;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAypBR,CAAC;AACF,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * DevPlan 图可视化 — 共享详情面板模块
3
+ *
4
+ * 包含: 节点详情面板 (showPanel)、面板缩放、面板导航历史、
5
+ * 工具函数 (row/statusBadge/escHtml/fmtTime)、文档内容加载、
6
+ * Markdown 渲染、Phase 展开。
7
+ *
8
+ * 【共享设计】此面板同时服务于:
9
+ * - vis-network 点击节点
10
+ * - 3D Force Graph 点击节点
11
+ * - 统计弹层列表点击
12
+ */
13
+ export declare function getDetailPanelScript(): string;
14
+ //# sourceMappingURL=template-detail-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-detail-panel.d.ts","sourceRoot":"","sources":["../../src/visualize/template-detail-panel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,wBAAgB,oBAAoB,IAAI,MAAM,CA+lB7C"}
@@ -0,0 +1,624 @@
1
+ "use strict";
2
+ /**
3
+ * DevPlan 图可视化 — 共享详情面板模块
4
+ *
5
+ * 包含: 节点详情面板 (showPanel)、面板缩放、面板导航历史、
6
+ * 工具函数 (row/statusBadge/escHtml/fmtTime)、文档内容加载、
7
+ * Markdown 渲染、Phase 展开。
8
+ *
9
+ * 【共享设计】此面板同时服务于:
10
+ * - vis-network 点击节点
11
+ * - 3D Force Graph 点击节点
12
+ * - 统计弹层列表点击
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getDetailPanelScript = getDetailPanelScript;
16
+ function getDetailPanelScript() {
17
+ return `
18
+ // ========== Detail Panel ==========
19
+
20
+ /** 面板导航历史栈:存储节点 ID,用于"返回"功能 */
21
+ var panelHistory = [];
22
+ var currentPanelNodeId = null;
23
+
24
+ /** 从关联链接跳转到新面板(将当前节点压入历史栈)— 引擎无关 */
25
+ function navigateToPanel(nodeId) {
26
+ if (currentPanelNodeId) {
27
+ panelHistory.push(currentPanelNodeId);
28
+ }
29
+ // 选中节点(兼容所有引擎: vis-network / 3D wrapper / GraphCanvas)
30
+ if (network && typeof network.selectNodes === 'function') {
31
+ network.selectNodes([nodeId]);
32
+ }
33
+ highlightConnectedEdges(nodeId);
34
+ showPanel(nodeId);
35
+ }
36
+
37
+ /** 返回上一个面板 — 引擎无关 */
38
+ function panelGoBack() {
39
+ if (panelHistory.length === 0) return;
40
+ var prevNodeId = panelHistory.pop();
41
+ if (network && typeof network.selectNodes === 'function') {
42
+ network.selectNodes([prevNodeId]);
43
+ }
44
+ highlightConnectedEdges(prevNodeId);
45
+ showPanel(prevNodeId);
46
+ }
47
+
48
+ /** 更新返回按钮的可见性 */
49
+ function updateBackButton() {
50
+ var btn = document.getElementById('panelBack');
51
+ if (!btn) return;
52
+ if (panelHistory.length > 0) {
53
+ btn.classList.add('visible');
54
+ } else {
55
+ btn.classList.remove('visible');
56
+ }
57
+ }
58
+
59
+ /** 根据主任务节点 ID,从 allNodes/allEdges 中查找其所有子任务节点 */
60
+ function getSubTasksForMainTask(mainTaskNodeId) {
61
+ var subTaskIds = [];
62
+ for (var i = 0; i < allEdges.length; i++) {
63
+ var e = allEdges[i];
64
+ if (e.from === mainTaskNodeId && e.label === 'has_sub_task') {
65
+ subTaskIds.push(e.to);
66
+ }
67
+ }
68
+ var subTasks = [];
69
+ var idSet = {};
70
+ for (var i = 0; i < subTaskIds.length; i++) idSet[subTaskIds[i]] = true;
71
+ for (var i = 0; i < allNodes.length; i++) {
72
+ if (idSet[allNodes[i].id]) {
73
+ subTasks.push(allNodes[i]);
74
+ }
75
+ }
76
+ return subTasks;
77
+ }
78
+
79
+ function getRelatedDocsForTask(taskNodeId) {
80
+ var docIds = [];
81
+ for (var i = 0; i < allEdges.length; i++) {
82
+ var e = allEdges[i];
83
+ if (e.from === taskNodeId && e.label === 'task_has_doc') {
84
+ docIds.push(e.to);
85
+ }
86
+ }
87
+ var docs = [];
88
+ var idSet = {};
89
+ for (var i = 0; i < docIds.length; i++) idSet[docIds[i]] = true;
90
+ for (var i = 0; i < allNodes.length; i++) {
91
+ if (idSet[allNodes[i].id]) docs.push(allNodes[i]);
92
+ }
93
+ return docs;
94
+ }
95
+
96
+ function getRelatedTasksForDoc(docNodeId) {
97
+ var taskIds = [];
98
+ for (var i = 0; i < allEdges.length; i++) {
99
+ var e = allEdges[i];
100
+ if (e.to === docNodeId && e.label === 'task_has_doc') {
101
+ taskIds.push(e.from);
102
+ }
103
+ }
104
+ var tasks = [];
105
+ var idSet = {};
106
+ for (var i = 0; i < taskIds.length; i++) idSet[taskIds[i]] = true;
107
+ for (var i = 0; i < allNodes.length; i++) {
108
+ if (idSet[allNodes[i].id]) tasks.push(allNodes[i]);
109
+ }
110
+ return tasks;
111
+ }
112
+
113
+ /** 跨引擎通用: 根据 nodeId 获取节点数据 */
114
+ function getNodeById(nodeId) {
115
+ // 1. 优先从 nodesDataSet (vis-network DataSet / SimpleDataSet) 获取
116
+ if (nodesDataSet) {
117
+ var dsNode = nodesDataSet.get(nodeId);
118
+ if (dsNode) return dsNode;
119
+ }
120
+ // 2. fallback: 从全局 allNodes 搜索并适配格式
121
+ for (var i = 0; i < allNodes.length; i++) {
122
+ if (allNodes[i].id === nodeId) {
123
+ var n = allNodes[i];
124
+ return {
125
+ id: n.id,
126
+ label: n.label || n._origLabel || '',
127
+ _type: n._type || n.type || '',
128
+ _props: n._props || n.properties || {}
129
+ };
130
+ }
131
+ }
132
+ return null;
133
+ }
134
+
135
+ function showPanel(nodeId) {
136
+ var node = getNodeById(nodeId);
137
+ if (!node) return;
138
+ var panel = document.getElementById('panel');
139
+ var header = document.getElementById('panelHeader');
140
+ var title = document.getElementById('panelTitle');
141
+ var body = document.getElementById('panelBody');
142
+
143
+ header.className = 'panel-header ' + (node._type || '');
144
+ var typeNames = { project: '项目', module: '模块', 'main-task': '主任务', 'sub-task': '子任务', document: '文档', memory: '记忆' };
145
+ title.textContent = (typeNames[node._type] || '节点') + ' 详情';
146
+
147
+ var p = node._props;
148
+ var html = '<div class="panel-row"><span class="panel-label">名称</span><span class="panel-value">' + escHtml(node.label) + '</span></div>';
149
+
150
+ if (node._type === 'main-task') {
151
+ html += row('任务ID', p.taskId);
152
+ html += row('优先级', '<span class="status-badge priority-' + (p.priority || 'P2') + '">' + (p.priority || 'P2') + '</span>');
153
+ html += row('状态', statusBadge(p.status));
154
+ if (p.completedAt) { html += row('完成时间', '<span style="color:#6ee7b7;">' + fmtTime(p.completedAt) + '</span>'); }
155
+ if (p.totalSubtasks !== undefined) {
156
+ var pct = p.totalSubtasks > 0 ? Math.round((p.completedSubtasks || 0) / p.totalSubtasks * 100) : 0;
157
+ html += row('子任务', (p.completedSubtasks || 0) + '/' + p.totalSubtasks);
158
+ html += '<div class="panel-progress"><div class="panel-progress-bar"><div class="panel-progress-fill" style="width:' + pct + '%"></div></div></div>';
159
+ }
160
+
161
+ // 查找并显示子任务列表
162
+ var subTasks = getSubTasksForMainTask(nodeId);
163
+ if (subTasks.length > 0) {
164
+ var completedCount = 0;
165
+ for (var si = 0; si < subTasks.length; si++) {
166
+ if ((subTasks[si].properties || {}).status === 'completed') completedCount++;
167
+ }
168
+ html += '<div class="subtask-section">';
169
+ html += '<div class="subtask-section-title"><span>子任务列表</span><span style="color:#6b7280;">' + completedCount + '/' + subTasks.length + '</span></div>';
170
+ html += '<ul class="subtask-list">';
171
+ // 排序:进行中 > 待开始 > 已完成 > 已取消
172
+ var statusOrder = { in_progress: 0, pending: 1, completed: 2, cancelled: 3 };
173
+ subTasks.sort(function(a, b) {
174
+ var sa = (a.properties || {}).status || 'pending';
175
+ var sb = (b.properties || {}).status || 'pending';
176
+ return (statusOrder[sa] || 1) - (statusOrder[sb] || 1);
177
+ });
178
+ for (var si = 0; si < subTasks.length; si++) {
179
+ var st = subTasks[si];
180
+ var stProps = st.properties || {};
181
+ var stStatus = stProps.status || 'pending';
182
+ var stIcon = stStatus === 'completed' ? '✓' : stStatus === 'in_progress' ? '▶' : stStatus === 'cancelled' ? '✗' : '○';
183
+ var stTime = stProps.completedAt ? fmtTime(stProps.completedAt) : '';
184
+ html += '<li class="subtask-item">';
185
+ html += '<span class="subtask-icon ' + stStatus + '">' + stIcon + '</span>';
186
+ html += '<span class="subtask-name ' + stStatus + '" title="' + escHtml(st.label) + '">' + escHtml(st.label) + '</span>';
187
+ if (stTime) { html += '<span class="subtask-time">' + stTime + '</span>'; }
188
+ html += '<span class="subtask-id">' + escHtml(stProps.taskId || '') + '</span>';
189
+ html += '</li>';
190
+ }
191
+ html += '</ul>';
192
+ html += '</div>';
193
+ }
194
+
195
+ // 查找并显示关联文档
196
+ var relDocs = getRelatedDocsForTask(nodeId);
197
+ if (relDocs.length > 0) {
198
+ html += '<div class="subtask-section">';
199
+ html += '<div class="subtask-section-title"><span style="color:#f59e0b;">关联文档</span><span style="color:#6b7280;">' + relDocs.length + '</span></div>';
200
+ html += '<ul class="subtask-list">';
201
+ for (var di = 0; di < relDocs.length; di++) {
202
+ var doc = relDocs[di];
203
+ var docProps = doc.properties || {};
204
+ var docLabel = docProps.section || '';
205
+ if (docProps.subSection) docLabel += ' / ' + docProps.subSection;
206
+ html += '<li class="subtask-item" style="cursor:pointer;" onclick="navigateToPanel(\\x27' + doc.id + '\\x27)">';
207
+ html += '<span class="subtask-icon" style="color:#f59e0b;">&#x1F4C4;</span>';
208
+ html += '<span class="subtask-name" title="' + escHtml(doc.label) + '">' + escHtml(doc.label) + '</span>';
209
+ html += '<span class="subtask-id">' + escHtml(docLabel) + '</span>';
210
+ html += '</li>';
211
+ }
212
+ html += '</ul>';
213
+ html += '</div>';
214
+ }
215
+ } else if (node._type === 'sub-task') {
216
+ html += row('任务ID', p.taskId);
217
+ html += row('父任务', p.parentTaskId);
218
+ html += row('状态', statusBadge(p.status));
219
+ if (p.completedAt) { html += row('完成时间', '<span style="color:#6ee7b7;">' + fmtTime(p.completedAt) + '</span>'); }
220
+ } else if (node._type === 'module') {
221
+ html += row('模块ID', p.moduleId);
222
+ html += row('状态', statusBadge(p.status || 'active'));
223
+ html += row('主任务数', p.mainTaskCount);
224
+ } else if (node._type === 'document') {
225
+ html += row('类型', p.section);
226
+ if (p.subSection) html += row('子类型', p.subSection);
227
+ html += row('版本', p.version);
228
+
229
+ // 查找并显示关联任务
230
+ var relTasks = getRelatedTasksForDoc(nodeId);
231
+ if (relTasks.length > 0) {
232
+ html += '<div class="subtask-section">';
233
+ html += '<div class="subtask-section-title"><span style="color:#6366f1;">关联任务</span><span style="color:#6b7280;">' + relTasks.length + '</span></div>';
234
+ html += '<ul class="subtask-list">';
235
+ for (var ti = 0; ti < relTasks.length; ti++) {
236
+ var task = relTasks[ti];
237
+ var tProps = task.properties || {};
238
+ var tStatus = tProps.status || 'pending';
239
+ var tIcon = tStatus === 'completed' ? '✓' : tStatus === 'in_progress' ? '▶' : '○';
240
+ html += '<li class="subtask-item" style="cursor:pointer;" onclick="navigateToPanel(\\x27' + task.id + '\\x27)">';
241
+ html += '<span class="subtask-icon ' + tStatus + '">' + tIcon + '</span>';
242
+ html += '<span class="subtask-name" title="' + escHtml(task.label) + '">' + escHtml(task.label) + '</span>';
243
+ html += '<span class="subtask-id">' + escHtml(tProps.taskId || '') + '</span>';
244
+ html += '</li>';
245
+ }
246
+ html += '</ul>';
247
+ html += '</div>';
248
+ }
249
+
250
+ // 文档内容区域 — 先显示加载中,稍后异步填充
251
+ html += '<div class="doc-section">';
252
+ html += '<div class="doc-section-title"><span>文档内容</span><button class="doc-toggle" id="docToggleBtn" onclick="toggleDocContent()">收起</button></div>';
253
+ html += '<div id="docContentArea"><div class="doc-loading">加载中...</div></div>';
254
+ html += '</div>';
255
+ } else if (node._type === 'memory') {
256
+ html += row('类型', '<span style="color:#e879f9;">' + escHtml(p.memoryType || '') + '</span>');
257
+ html += row('重要性', '<span style="color:#fbbf24;">' + (p.importance != null ? p.importance : 0.5) + '</span>');
258
+ html += row('访问次数', p.hitCount || 0);
259
+ if (p.tags && p.tags.length > 0) {
260
+ html += row('标签', p.tags.map(function(t) { return '<span style="background:#334155;padding:1px 6px;border-radius:4px;font-size:11px;margin-right:4px;">' + escHtml(t) + '</span>'; }).join(''));
261
+ }
262
+ if (p.relatedTaskId) { html += row('关联任务', p.relatedTaskId); }
263
+ if (p.sourceId) { html += row('来源ID', p.sourceId); }
264
+ html += '<div class="doc-section" style="margin-top:8px;">';
265
+ html += '<div class="doc-section-title"><span>记忆内容</span></div>';
266
+ html += '<div style="padding:8px;background:#0f172a;border-radius:8px;font-size:12px;line-height:1.6;color:#cbd5e1;white-space:pre-wrap;">' + escHtml(p.content || '') + '</div>';
267
+ html += '</div>';
268
+ } else if (node._type === 'project') {
269
+ html += row('类型', '项目根节点');
270
+ }
271
+
272
+ body.innerHTML = html;
273
+ panel.classList.add('show');
274
+ currentPanelNodeId = nodeId;
275
+ updateBackButton();
276
+
277
+ // 如果是文档节点,异步加载内容
278
+ if (node._type === 'document') {
279
+ loadDocContent(p.section, p.subSection);
280
+ }
281
+ }
282
+
283
+ function closePanel() {
284
+ document.getElementById('panel').classList.remove('show');
285
+ panelHistory = [];
286
+ currentPanelNodeId = null;
287
+ updateBackButton();
288
+ resetAllEdgeColors();
289
+ }
290
+
291
+
292
+ // ========== Panel Resize ==========
293
+ var panelDefaultWidth = 340;
294
+ var panelExpandedWidth = 680;
295
+ var panelIsExpanded = false;
296
+ var panelResizing = false;
297
+
298
+ // 双击标题栏切换宽度
299
+ (function() {
300
+ var header = document.getElementById('panelHeader');
301
+ if (!header) return;
302
+ header.addEventListener('dblclick', function(e) {
303
+ // 不要在关闭按钮上触发
304
+ if (e.target.closest && e.target.closest('.panel-close')) return;
305
+ var panel = document.getElementById('panel');
306
+ if (!panel) return;
307
+ panelIsExpanded = !panelIsExpanded;
308
+ var targetWidth = panelIsExpanded ? panelExpandedWidth : panelDefaultWidth;
309
+ panel.style.transition = 'width 0.25s ease';
310
+ panel.style.width = targetWidth + 'px';
311
+ setTimeout(function() { panel.style.transition = 'none'; }, 260);
312
+ });
313
+ })();
314
+
315
+ // 拖拽左边线调整宽度
316
+ (function() {
317
+ var handle = document.getElementById('panelResizeHandle');
318
+ var panel = document.getElementById('panel');
319
+ if (!handle || !panel) return;
320
+
321
+ var startX = 0;
322
+ var startWidth = 0;
323
+
324
+ handle.addEventListener('mousedown', function(e) {
325
+ e.preventDefault();
326
+ e.stopPropagation();
327
+ panelResizing = true;
328
+ startX = e.clientX;
329
+ startWidth = panel.offsetWidth;
330
+ handle.classList.add('active');
331
+ document.body.style.cursor = 'col-resize';
332
+ document.body.style.userSelect = 'none';
333
+
334
+ function onMouseMove(ev) {
335
+ if (!panelResizing) return;
336
+ // 面板在右侧,向左拖 = 增大宽度
337
+ var dx = startX - ev.clientX;
338
+ var newWidth = Math.max(280, Math.min(startWidth + dx, window.innerWidth - 40));
339
+ panel.style.width = newWidth + 'px';
340
+ panelIsExpanded = newWidth > (panelDefaultWidth + 50);
341
+ }
342
+
343
+ function onMouseUp() {
344
+ panelResizing = false;
345
+ handle.classList.remove('active');
346
+ document.body.style.cursor = '';
347
+ document.body.style.userSelect = '';
348
+ document.removeEventListener('mousemove', onMouseMove);
349
+ document.removeEventListener('mouseup', onMouseUp);
350
+ }
351
+
352
+ document.addEventListener('mousemove', onMouseMove);
353
+ document.addEventListener('mouseup', onMouseUp);
354
+ });
355
+ })();
356
+
357
+ function row(label, value) { return '<div class="panel-row"><span class="panel-label">' + label + '</span><span class="panel-value">' + (value || '-') + '</span></div>'; }
358
+ function statusBadge(s) { return '<span class="status-badge status-' + (s || 'pending') + '">' + statusText(s) + '</span>'; }
359
+ function statusText(s) { var m = { completed: '已完成', in_progress: '进行中', pending: '待开始', cancelled: '已取消', active: '活跃', planning: '规划中', deprecated: '已废弃' }; return m[s] || s || '未知'; }
360
+ function escHtml(s) { var d = document.createElement('div'); d.textContent = s || ''; return d.innerHTML; }
361
+
362
+ // 格式化时间戳(毫秒)为可读日期时间,当年省略年份
363
+ function fmtTime(ts) {
364
+ if (!ts) return '';
365
+ var d = new Date(ts);
366
+ var m = String(d.getMonth() + 1).padStart(2, '0');
367
+ var day = String(d.getDate()).padStart(2, '0');
368
+ var h = String(d.getHours()).padStart(2, '0');
369
+ var min = String(d.getMinutes()).padStart(2, '0');
370
+ var time = m + '-' + day + ' ' + h + ':' + min;
371
+ if (d.getFullYear() !== new Date().getFullYear()) {
372
+ time = d.getFullYear() + '-' + time;
373
+ }
374
+ return time;
375
+ }
376
+
377
+ /** 文档列表用的短日期格式:MM-DD 或 YYYY-MM-DD */
378
+ function fmtDateShort(ts) {
379
+ if (!ts) return '';
380
+ var d = new Date(ts);
381
+ var m = String(d.getMonth() + 1).padStart(2, '0');
382
+ var day = String(d.getDate()).padStart(2, '0');
383
+ if (d.getFullYear() !== new Date().getFullYear()) {
384
+ return d.getFullYear() + '-' + m + '-' + day;
385
+ }
386
+ return m + '-' + day;
387
+ }
388
+
389
+
390
+ // ========== Phase Expand (Stats page) ==========
391
+ function togglePhaseExpand(el) {
392
+ var wrap = el.closest('.phase-item-wrap');
393
+ if (wrap) wrap.classList.toggle('expanded');
394
+ }
395
+
396
+
397
+ // ========== Document Content ==========
398
+ var docContentVisible = true;
399
+
400
+ function toggleDocContent() {
401
+ var area = document.getElementById('docContentArea');
402
+ var btn = document.getElementById('docToggleBtn');
403
+ if (!area) return;
404
+ docContentVisible = !docContentVisible;
405
+ area.style.display = docContentVisible ? 'block' : 'none';
406
+ if (btn) btn.textContent = docContentVisible ? '收起' : '展开';
407
+ }
408
+
409
+ function loadDocContent(section, subSection) {
410
+ var area = document.getElementById('docContentArea');
411
+ if (!area) return;
412
+ var url = '/api/doc?section=' + encodeURIComponent(section || '');
413
+ if (subSection) url += '&subSection=' + encodeURIComponent(subSection);
414
+
415
+ fetch(url).then(function(r) {
416
+ if (!r.ok) throw new Error('HTTP ' + r.status);
417
+ return r.json();
418
+ }).then(function(doc) {
419
+ if (!doc || !doc.content) {
420
+ area.innerHTML = '<div class="doc-error">文档内容为空</div>';
421
+ return;
422
+ }
423
+ area.innerHTML = '<div class="doc-content mdv-body">' + renderMarkdown(doc.content) + '</div>';
424
+ // 后处理:代码高亮、复制按钮、表格包裹等
425
+ var mdvEl = area.querySelector('.mdv-body');
426
+ if (mdvEl && typeof mdEnhanceContent === 'function') mdEnhanceContent(mdvEl);
427
+ docContentVisible = true;
428
+ var btn = document.getElementById('docToggleBtn');
429
+ if (btn) btn.textContent = '收起';
430
+ }).catch(function(err) {
431
+ area.innerHTML = '<div class="doc-error">加载失败: ' + escHtml(err.message) + '</div>';
432
+ });
433
+ }
434
+
435
+ /** 预处理:修复无表头的孤立管道行块 → 合法 Markdown 表格 */
436
+ function fixOrphanPipeRows(md) {
437
+ var lines = md.split('\\n');
438
+ var result = [];
439
+ var pipeBlock = [];
440
+
441
+ function flushBlock() {
442
+ if (pipeBlock.length < 1) return;
443
+ // 检查此块前方是否紧跟分隔行(说明已有表头)
444
+ var prevIdx = result.length - 1;
445
+ var hasSep = false;
446
+ // 块内是否已有分隔行
447
+ for (var k = 0; k < pipeBlock.length; k++) {
448
+ if (/^\\|[\\s\\-:|]+\\|\\s*$/.test(pipeBlock[k])) { hasSep = true; break; }
449
+ }
450
+ if (!hasSep) {
451
+ // 计算列数并生成分隔行
452
+ var cols = pipeBlock[0].split('|').filter(function(c, i, a) { return i > 0 && i < a.length - 1; }).length;
453
+ var sep = '|' + Array(cols).fill(' --- ').join('|') + '|';
454
+ // 第一行当表头,插入分隔行
455
+ result.push(pipeBlock[0]);
456
+ result.push(sep);
457
+ for (var k = 1; k < pipeBlock.length; k++) result.push(pipeBlock[k]);
458
+ } else {
459
+ for (var k = 0; k < pipeBlock.length; k++) result.push(pipeBlock[k]);
460
+ }
461
+ pipeBlock = [];
462
+ }
463
+
464
+ for (var i = 0; i < lines.length; i++) {
465
+ var line = lines[i];
466
+ if (/^\\|.+\\|\\s*$/.test(line)) {
467
+ pipeBlock.push(line);
468
+ } else {
469
+ if (pipeBlock.length > 0) flushBlock();
470
+ result.push(line);
471
+ }
472
+ }
473
+ if (pipeBlock.length > 0) flushBlock();
474
+ return result.join('\\n');
475
+ }
476
+
477
+ /** Markdown 渲染 — 优先使用 marked.js(CDN),降级到简易解析器 */
478
+ function renderMarkdown(md) {
479
+ if (!md) return '';
480
+
481
+ // 预处理:修复字面 \\n 文本为真实换行符(部分文档存储时换行被转义)
482
+ if (md.indexOf('\\\\n') !== -1) {
483
+ md = md.replace(/\\\\n/g, '\\n');
484
+ }
485
+
486
+ // 预处理:修复孤立管道行
487
+ md = fixOrphanPipeRows(md);
488
+
489
+ // 优先使用 marked.js(由 MD Viewer CDN 加载)
490
+ if (typeof marked !== 'undefined') {
491
+ try { return marked.parse(md); } catch(e) { console.warn('marked.parse fallback:', e); }
492
+ }
493
+
494
+ // 先处理代码块(防止内部被其他规则干扰)
495
+ var codeBlocks = [];
496
+ md = md.replace(/\`\`\`(\\w*)?\\n([\\s\\S]*?)\`\`\`/g, function(m, lang, code) {
497
+ var idx = codeBlocks.length;
498
+ codeBlocks.push('<pre><code>' + escHtml(code.replace(/\\n$/, '')) + '</code></pre>');
499
+ return '%%CODEBLOCK_' + idx + '%%';
500
+ });
501
+
502
+ // 按行处理
503
+ var lines = md.split('\\n');
504
+ var html = '';
505
+ var inTable = false;
506
+ var inList = false;
507
+ var listType = '';
508
+
509
+ for (var i = 0; i < lines.length; i++) {
510
+ var line = lines[i];
511
+
512
+ // 代码块占位符
513
+ var cbMatch = line.match(/^%%CODEBLOCK_(\\d+)%%$/);
514
+ if (cbMatch) {
515
+ if (inList) { html += '</' + listType + '>'; inList = false; }
516
+ if (inTable) { html += '</table>'; inTable = false; }
517
+ html += codeBlocks[parseInt(cbMatch[1])];
518
+ continue;
519
+ }
520
+
521
+ // 表格行
522
+ if (line.match(/^\\|(.+)\\|\\s*$/)) {
523
+ if (inList) { html += '</' + listType + '>'; inList = false; }
524
+ // 跳过分隔行
525
+ if (line.match(/^\\|[\\s\\-:|]+\\|\\s*$/)) continue;
526
+ var cells = line.split('|').filter(function(c, idx, arr) { return idx > 0 && idx < arr.length - 1; });
527
+ if (!inTable) {
528
+ html += '<table>';
529
+ html += '<tr>' + cells.map(function(c) { return '<th>' + inlineFormat(c.trim()) + '</th>'; }).join('') + '</tr>';
530
+ inTable = true;
531
+ } else {
532
+ html += '<tr>' + cells.map(function(c) { return '<td>' + inlineFormat(c.trim()) + '</td>'; }).join('') + '</tr>';
533
+ }
534
+ continue;
535
+ } else if (inTable) {
536
+ html += '</table>';
537
+ inTable = false;
538
+ }
539
+
540
+ // 空行
541
+ if (line.trim() === '') {
542
+ if (inList) { html += '</' + listType + '>'; inList = false; }
543
+ continue;
544
+ }
545
+
546
+ // 标题
547
+ var hMatch = line.match(/^(#{1,4})\\s+(.+)$/);
548
+ if (hMatch) {
549
+ if (inList) { html += '</' + listType + '>'; inList = false; }
550
+ var level = hMatch[1].length;
551
+ html += '<h' + level + '>' + inlineFormat(hMatch[2]) + '</h' + level + '>';
552
+ continue;
553
+ }
554
+
555
+ // 分隔线
556
+ if (line.match(/^(\\*{3,}|-{3,}|_{3,})\\s*$/)) {
557
+ if (inList) { html += '</' + listType + '>'; inList = false; }
558
+ html += '<hr>';
559
+ continue;
560
+ }
561
+
562
+ // 引用
563
+ if (line.match(/^>\\s?/)) {
564
+ if (inList) { html += '</' + listType + '>'; inList = false; }
565
+ html += '<blockquote>' + inlineFormat(line.replace(/^>\\s?/, '')) + '</blockquote>';
566
+ continue;
567
+ }
568
+
569
+ // 无序列表
570
+ var ulMatch = line.match(/^\\s*[-*+]\\s+(.+)$/);
571
+ if (ulMatch) {
572
+ if (!inList || listType !== 'ul') {
573
+ if (inList) html += '</' + listType + '>';
574
+ html += '<ul>';
575
+ inList = true;
576
+ listType = 'ul';
577
+ }
578
+ html += '<li>' + inlineFormat(ulMatch[1]) + '</li>';
579
+ continue;
580
+ }
581
+
582
+ // 有序列表
583
+ var olMatch = line.match(/^\\s*\\d+\\.\\s+(.+)$/);
584
+ if (olMatch) {
585
+ if (!inList || listType !== 'ol') {
586
+ if (inList) html += '</' + listType + '>';
587
+ html += '<ol>';
588
+ inList = true;
589
+ listType = 'ol';
590
+ }
591
+ html += '<li>' + inlineFormat(olMatch[1]) + '</li>';
592
+ continue;
593
+ }
594
+
595
+ // 普通段落
596
+ if (inList) { html += '</' + listType + '>'; inList = false; }
597
+ html += '<p>' + inlineFormat(line) + '</p>';
598
+ }
599
+
600
+ if (inList) html += '</' + listType + '>';
601
+ if (inTable) html += '</table>';
602
+
603
+ return html;
604
+ }
605
+
606
+ /** 行内格式化:粗体、斜体、行内代码、链接 */
607
+ function inlineFormat(text) {
608
+ if (!text) return '';
609
+ // 行内代码
610
+ text = text.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
611
+ // 粗体
612
+ text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');
613
+ text = text.replace(/__(.+?)__/g, '<strong>$1</strong>');
614
+ // 斜体
615
+ text = text.replace(/\\*(.+?)\\*/g, '<em>$1</em>');
616
+ text = text.replace(/_(.+?)_/g, '<em>$1</em>');
617
+ // 链接
618
+ text = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2" target="_blank">$1</a>');
619
+ return text;
620
+ }
621
+
622
+ `;
623
+ }
624
+ //# sourceMappingURL=template-detail-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-detail-panel.js","sourceRoot":"","sources":["../../src/visualize/template-detail-panel.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAEH,oDA+lBC;AA/lBD,SAAgB,oBAAoB;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6lBR,CAAC;AACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * DevPlan 图可视化 — 3D Force Graph 渲染模块
3
+ *
4
+ * 包含: Three.js WebGL 3D 图渲染、力导向布局、节点交互。
5
+ */
6
+ export declare function getGraph3DScript(): string;
7
+ //# sourceMappingURL=template-graph-3d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-graph-3d.d.ts","sourceRoot":"","sources":["../../src/visualize/template-graph-3d.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAgB,gBAAgB,IAAI,MAAM,CAglCzC"}