aifastdb-devplan 1.6.1 → 1.6.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/dist/dev-plan-document-store.d.ts +13 -1
- package/dist/dev-plan-document-store.d.ts.map +1 -1
- package/dist/dev-plan-document-store.js +119 -0
- package/dist/dev-plan-document-store.js.map +1 -1
- package/dist/dev-plan-factory.d.ts.map +1 -1
- package/dist/dev-plan-factory.js +2 -1
- package/dist/dev-plan-factory.js.map +1 -1
- package/dist/dev-plan-graph-store.d.ts +64 -1
- package/dist/dev-plan-graph-store.d.ts.map +1 -1
- package/dist/dev-plan-graph-store.js +364 -2
- package/dist/dev-plan-graph-store.js.map +1 -1
- package/dist/dev-plan-interface.d.ts +24 -1
- package/dist/dev-plan-interface.d.ts.map +1 -1
- package/dist/dev-plan-migrate.d.ts +1 -0
- package/dist/dev-plan-migrate.d.ts.map +1 -1
- package/dist/dev-plan-migrate.js +28 -2
- package/dist/dev-plan-migrate.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/index.js +119 -0
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/types.d.ts +88 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/visualize/graph-canvas/api-compat.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/api-compat.js +22 -12
- package/dist/visualize/graph-canvas/api-compat.js.map +1 -1
- package/dist/visualize/graph-canvas/core.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/core.js +296 -4
- package/dist/visualize/graph-canvas/core.js.map +1 -1
- package/dist/visualize/graph-canvas/interaction.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/interaction.js +11 -0
- package/dist/visualize/graph-canvas/interaction.js.map +1 -1
- package/dist/visualize/graph-canvas/layout-worker.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/layout-worker.js +45 -9
- package/dist/visualize/graph-canvas/layout-worker.js.map +1 -1
- package/dist/visualize/graph-canvas/renderer.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/renderer.js +164 -33
- package/dist/visualize/graph-canvas/renderer.js.map +1 -1
- package/dist/visualize/graph-canvas/styles.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/styles.js +136 -121
- package/dist/visualize/graph-canvas/styles.js.map +1 -1
- package/dist/visualize/graph-canvas/viewport.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/viewport.js +10 -0
- package/dist/visualize/graph-canvas/viewport.js.map +1 -1
- package/dist/visualize/server.js +149 -32
- package/dist/visualize/server.js.map +1 -1
- package/dist/visualize/template-core.d.ts +9 -0
- package/dist/visualize/template-core.d.ts.map +1 -0
- package/dist/visualize/template-core.js +714 -0
- package/dist/visualize/template-core.js.map +1 -0
- package/dist/visualize/template-data-loading.d.ts +7 -0
- package/dist/visualize/template-data-loading.d.ts.map +1 -0
- package/dist/visualize/template-data-loading.js +677 -0
- package/dist/visualize/template-data-loading.js.map +1 -0
- package/dist/visualize/template-detail-panel.d.ts +14 -0
- package/dist/visualize/template-detail-panel.d.ts.map +1 -0
- package/dist/visualize/template-detail-panel.js +553 -0
- package/dist/visualize/template-detail-panel.js.map +1 -0
- package/dist/visualize/template-graph-3d.d.ts +7 -0
- package/dist/visualize/template-graph-3d.d.ts.map +1 -0
- package/dist/visualize/template-graph-3d.js +1112 -0
- package/dist/visualize/template-graph-3d.js.map +1 -0
- package/dist/visualize/template-graph-vis.d.ts +8 -0
- package/dist/visualize/template-graph-vis.d.ts.map +1 -0
- package/dist/visualize/template-graph-vis.js +1204 -0
- package/dist/visualize/template-graph-vis.js.map +1 -0
- package/dist/visualize/template-html.d.ts +9 -0
- package/dist/visualize/template-html.d.ts.map +1 -0
- package/dist/visualize/template-html.js +484 -0
- package/dist/visualize/template-html.js.map +1 -0
- package/dist/visualize/template-pages.d.ts +7 -0
- package/dist/visualize/template-pages.d.ts.map +1 -0
- package/dist/visualize/template-pages.js +806 -0
- package/dist/visualize/template-pages.js.map +1 -0
- package/dist/visualize/template-stats-modal.d.ts +7 -0
- package/dist/visualize/template-stats-modal.d.ts.map +1 -0
- package/dist/visualize/template-stats-modal.js +406 -0
- package/dist/visualize/template-stats-modal.js.map +1 -0
- package/dist/visualize/template-styles.d.ts +9 -0
- package/dist/visualize/template-styles.d.ts.map +1 -0
- package/dist/visualize/template-styles.js +487 -0
- package/dist/visualize/template-styles.js.map +1 -0
- package/dist/visualize/template.d.ts +14 -3
- package/dist/visualize/template.d.ts.map +1 -1
- package/dist/visualize/template.js +38 -3475
- package/dist/visualize/template.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DevPlan 图可视化 — 核心脚本模块
|
|
4
|
+
*
|
|
5
|
+
* 从 template.ts 拆分出的核心 JavaScript 代码。
|
|
6
|
+
* 包含: 侧边栏导航、设置页、通用图谱显示设置、3D Force Graph 自定义设置、
|
|
7
|
+
* Debug 日志、渲染引擎选择与加载、SimpleDataSet shim、全局状态变量。
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.getCoreScript = getCoreScript;
|
|
11
|
+
function getCoreScript() {
|
|
12
|
+
return `
|
|
13
|
+
// ========== Sidebar ==========
|
|
14
|
+
function toggleSidebar() {
|
|
15
|
+
var sidebar = document.getElementById('sidebar');
|
|
16
|
+
if (!sidebar) return;
|
|
17
|
+
sidebar.classList.toggle('expanded');
|
|
18
|
+
var isExpanded = sidebar.classList.contains('expanded');
|
|
19
|
+
// 记住偏好
|
|
20
|
+
try { localStorage.setItem('devplan_sidebar_expanded', isExpanded ? '1' : '0'); } catch(e) {}
|
|
21
|
+
// 同步更新左侧弹层位置
|
|
22
|
+
updateStatsModalPosition();
|
|
23
|
+
// 通知 vis-network 重新适配尺寸
|
|
24
|
+
setTimeout(function() { if (network) network.redraw(); }, 300);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 根据侧边栏状态更新左侧弹层位置 */
|
|
28
|
+
function updateStatsModalPosition() {
|
|
29
|
+
var modal = document.querySelector('.stats-modal');
|
|
30
|
+
var sidebar = document.getElementById('sidebar');
|
|
31
|
+
if (modal && sidebar) {
|
|
32
|
+
modal.style.left = (sidebar.classList.contains('expanded') ? 200 : 48) + 'px';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var currentPage = 'graph';
|
|
37
|
+
var pageMap = { graph: 'pageGraph', stats: 'pageStats', docs: 'pageDocs', settings: 'pageSettings' };
|
|
38
|
+
|
|
39
|
+
function navTo(page) {
|
|
40
|
+
// 仅支持已实现的页面
|
|
41
|
+
if (!pageMap[page]) return;
|
|
42
|
+
if (page === currentPage) return;
|
|
43
|
+
|
|
44
|
+
// 切换页面视图
|
|
45
|
+
var oldView = document.getElementById(pageMap[currentPage]);
|
|
46
|
+
var newView = document.getElementById(pageMap[page]);
|
|
47
|
+
if (oldView) oldView.classList.remove('active');
|
|
48
|
+
if (newView) newView.classList.add('active');
|
|
49
|
+
|
|
50
|
+
// 切换导航高亮
|
|
51
|
+
var items = document.querySelectorAll('.nav-item[data-page]');
|
|
52
|
+
for (var i = 0; i < items.length; i++) {
|
|
53
|
+
items[i].classList.remove('active');
|
|
54
|
+
if (items[i].getAttribute('data-page') === page) items[i].classList.add('active');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
currentPage = page;
|
|
58
|
+
|
|
59
|
+
// 离开图谱页面时关闭左侧弹层
|
|
60
|
+
if (page !== 'graph') closeStatsModal();
|
|
61
|
+
|
|
62
|
+
// 按需加载页面数据
|
|
63
|
+
if (page === 'stats') loadStatsPage();
|
|
64
|
+
if (page === 'docs') loadDocsPage();
|
|
65
|
+
if (page === 'graph' && network) {
|
|
66
|
+
setTimeout(function() { network.redraw(); network.fit(); }, 100);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 恢复 sidebar 偏好
|
|
71
|
+
(function() {
|
|
72
|
+
try {
|
|
73
|
+
var saved = localStorage.getItem('devplan_sidebar_expanded');
|
|
74
|
+
if (saved === '1') {
|
|
75
|
+
var sidebar = document.getElementById('sidebar');
|
|
76
|
+
if (sidebar) { sidebar.classList.add('expanded'); }
|
|
77
|
+
// 同步弹层初始位置
|
|
78
|
+
updateStatsModalPosition();
|
|
79
|
+
}
|
|
80
|
+
} catch(e) {}
|
|
81
|
+
})();
|
|
82
|
+
|
|
83
|
+
// ========== Settings Page ==========
|
|
84
|
+
function selectRenderer(value) {
|
|
85
|
+
// Skip if already the current engine
|
|
86
|
+
if (value === RENDERER_ENGINE) return;
|
|
87
|
+
|
|
88
|
+
var cards = document.querySelectorAll('#rendererOptions .settings-radio-card');
|
|
89
|
+
for (var i = 0; i < cards.length; i++) {
|
|
90
|
+
var card = cards[i];
|
|
91
|
+
var radio = card.querySelector('input[type="radio"]');
|
|
92
|
+
if (card.getAttribute('data-value') === value) {
|
|
93
|
+
card.classList.add('selected');
|
|
94
|
+
if (radio) radio.checked = true;
|
|
95
|
+
} else {
|
|
96
|
+
card.classList.remove('selected');
|
|
97
|
+
if (radio) radio.checked = false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Persist to localStorage
|
|
101
|
+
try { localStorage.setItem('devplan_renderer_engine', value); } catch(e) {}
|
|
102
|
+
// Show/hide 3D settings section
|
|
103
|
+
var sec = document.getElementById('settings3dSection');
|
|
104
|
+
if (sec) sec.style.display = (value === '3d') ? 'block' : 'none';
|
|
105
|
+
// Show toast then auto-reload
|
|
106
|
+
var engineLabel = value === '3d' ? '3D Force Graph' : 'vis-network';
|
|
107
|
+
showSettingsToast('✅ 引擎已切换为 ' + engineLabel + ',正在重新加载...');
|
|
108
|
+
// Auto-reload after a short delay so user can see the toast
|
|
109
|
+
setTimeout(function() { location.reload(); }, 1200);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function showSettingsToast(message) {
|
|
113
|
+
var toast = document.getElementById('settingsSavedToast');
|
|
114
|
+
if (!toast) return;
|
|
115
|
+
if (message) toast.textContent = message;
|
|
116
|
+
toast.classList.add('show');
|
|
117
|
+
clearTimeout(toast._timer);
|
|
118
|
+
toast._timer = setTimeout(function() { toast.classList.remove('show'); }, 3000);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Initialize settings page state on load
|
|
122
|
+
(function() {
|
|
123
|
+
try {
|
|
124
|
+
var saved = localStorage.getItem('devplan_renderer_engine');
|
|
125
|
+
// 兼容旧值 graphcanvas → 3d
|
|
126
|
+
if (saved === 'graphcanvas') saved = '3d';
|
|
127
|
+
if (saved === '3d' || saved === 'vis') {
|
|
128
|
+
// Sync radio cards to saved value
|
|
129
|
+
var cards = document.querySelectorAll('#rendererOptions .settings-radio-card');
|
|
130
|
+
for (var i = 0; i < cards.length; i++) {
|
|
131
|
+
var card = cards[i];
|
|
132
|
+
var radio = card.querySelector('input[type="radio"]');
|
|
133
|
+
if (card.getAttribute('data-value') === saved) {
|
|
134
|
+
card.classList.add('selected');
|
|
135
|
+
if (radio) radio.checked = true;
|
|
136
|
+
} else {
|
|
137
|
+
card.classList.remove('selected');
|
|
138
|
+
if (radio) radio.checked = false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch(e) {}
|
|
143
|
+
})();
|
|
144
|
+
|
|
145
|
+
// ========== 通用图谱显示设置 ==========
|
|
146
|
+
var GRAPH_SETTINGS_KEY = 'devplan_graph_settings';
|
|
147
|
+
var GRAPH_SETTINGS_DEFAULTS = {
|
|
148
|
+
showProjectEdges: false // 默认隐藏主节点连线
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
function getGraphSettings() {
|
|
152
|
+
var settings = {};
|
|
153
|
+
for (var k in GRAPH_SETTINGS_DEFAULTS) settings[k] = GRAPH_SETTINGS_DEFAULTS[k];
|
|
154
|
+
try {
|
|
155
|
+
var saved = localStorage.getItem(GRAPH_SETTINGS_KEY);
|
|
156
|
+
if (saved) {
|
|
157
|
+
var parsed = JSON.parse(saved);
|
|
158
|
+
for (var k in parsed) {
|
|
159
|
+
if (GRAPH_SETTINGS_DEFAULTS.hasOwnProperty(k)) settings[k] = parsed[k];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} catch(e) {}
|
|
163
|
+
return settings;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function saveGraphSettings(settings) {
|
|
167
|
+
try { localStorage.setItem(GRAPH_SETTINGS_KEY, JSON.stringify(settings)); } catch(e) {}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function updateGraphSetting(key, value) {
|
|
171
|
+
var settings = getGraphSettings();
|
|
172
|
+
if (typeof GRAPH_SETTINGS_DEFAULTS[key] === 'boolean') value = !!value;
|
|
173
|
+
settings[key] = value;
|
|
174
|
+
saveGraphSettings(settings);
|
|
175
|
+
showSettingsToast('✅ 显示设置已保存,刷新图谱页面生效');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Initialize general graph settings UI
|
|
179
|
+
(function() {
|
|
180
|
+
var s = getGraphSettings();
|
|
181
|
+
var el = document.getElementById('settingShowProjectEdges');
|
|
182
|
+
if (el) el.checked = !!s.showProjectEdges;
|
|
183
|
+
})();
|
|
184
|
+
|
|
185
|
+
// ========== 统一节点颜色配置 (适用于所有渲染引擎) ==========
|
|
186
|
+
function darkenHex(hex, amount) {
|
|
187
|
+
try {
|
|
188
|
+
var r = parseInt(hex.slice(1,3), 16);
|
|
189
|
+
var g = parseInt(hex.slice(3,5), 16);
|
|
190
|
+
var b = parseInt(hex.slice(5,7), 16);
|
|
191
|
+
r = Math.max(0, Math.round(r * (1 - amount)));
|
|
192
|
+
g = Math.max(0, Math.round(g * (1 - amount)));
|
|
193
|
+
b = Math.max(0, Math.round(b * (1 - amount)));
|
|
194
|
+
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
|
195
|
+
} catch(e) { return hex; }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
var NODE_COLORS_KEY = 'devplan_node_colors';
|
|
199
|
+
var NODE_COLORS_VERSION_KEY = 'devplan_node_colors_v';
|
|
200
|
+
var NODE_COLORS_VERSION = 2; // 递增此值可强制重置用户缓存到新默认值
|
|
201
|
+
var NODE_COLORS_DEFAULTS = {
|
|
202
|
+
colorProject: '#f59e0b',
|
|
203
|
+
colorModule: '#ff6600',
|
|
204
|
+
colorMainTask: '#22c55e',
|
|
205
|
+
colorSubTask: '#047857',
|
|
206
|
+
colorDocument: '#3b82f6'
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
function getNodeColors() {
|
|
210
|
+
var colors = {};
|
|
211
|
+
for (var k in NODE_COLORS_DEFAULTS) colors[k] = NODE_COLORS_DEFAULTS[k];
|
|
212
|
+
try {
|
|
213
|
+
// 版本检查: 默认值变更时强制重置缓存
|
|
214
|
+
var savedVer = parseInt(localStorage.getItem(NODE_COLORS_VERSION_KEY) || '0', 10);
|
|
215
|
+
if (savedVer < NODE_COLORS_VERSION) {
|
|
216
|
+
// 默认值已更新, 清除旧缓存
|
|
217
|
+
localStorage.removeItem(NODE_COLORS_KEY);
|
|
218
|
+
localStorage.setItem(NODE_COLORS_VERSION_KEY, String(NODE_COLORS_VERSION));
|
|
219
|
+
return colors;
|
|
220
|
+
}
|
|
221
|
+
var saved = localStorage.getItem(NODE_COLORS_KEY);
|
|
222
|
+
if (saved) {
|
|
223
|
+
var parsed = JSON.parse(saved);
|
|
224
|
+
for (var k in parsed) {
|
|
225
|
+
if (NODE_COLORS_DEFAULTS.hasOwnProperty(k)) colors[k] = parsed[k];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} catch(e) {}
|
|
229
|
+
return colors;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function saveNodeColors(colors) {
|
|
233
|
+
try { localStorage.setItem(NODE_COLORS_KEY, JSON.stringify(colors)); } catch(e) {}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function updateNodeColor(nodeType, colorValue) {
|
|
237
|
+
var keyMap = { 'project': 'colorProject', 'module': 'colorModule', 'main-task': 'colorMainTask', 'sub-task': 'colorSubTask', 'document': 'colorDocument' };
|
|
238
|
+
var key = keyMap[nodeType];
|
|
239
|
+
if (!key) return;
|
|
240
|
+
var colors = getNodeColors();
|
|
241
|
+
colors[key] = colorValue;
|
|
242
|
+
saveNodeColors(colors);
|
|
243
|
+
// Update hex display
|
|
244
|
+
var hexMap = { 'project': 'ncColorProjectHex', 'module': 'ncColorModuleHex', 'main-task': 'ncColorMainTaskHex', 'sub-task': 'ncColorSubTaskHex', 'document': 'ncColorDocumentHex' };
|
|
245
|
+
var hexEl = document.getElementById(hexMap[nodeType]);
|
|
246
|
+
if (hexEl) hexEl.textContent = colorValue;
|
|
247
|
+
// Update dot color
|
|
248
|
+
var dotMap = { 'project': 'ncColorProject', 'module': 'ncColorModule', 'main-task': 'ncColorMainTask', 'sub-task': 'ncColorSubTask', 'document': 'ncColorDocument' };
|
|
249
|
+
var input = document.getElementById(dotMap[nodeType]);
|
|
250
|
+
if (input) {
|
|
251
|
+
var dot = input.parentElement.querySelector('.s3d-dot');
|
|
252
|
+
if (dot) dot.style.background = colorValue;
|
|
253
|
+
}
|
|
254
|
+
showSettingsToast('✅ 节点颜色已保存 (适用于所有渲染引擎),刷新图谱页面生效');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** 获取统一节点样式配置 — 所有渲染引擎共享 */
|
|
258
|
+
function getUnifiedNodeStyle() {
|
|
259
|
+
var nc = getNodeColors();
|
|
260
|
+
return {
|
|
261
|
+
project: { bg: nc.colorProject, border: darkenHex(nc.colorProject, 0.15), font: '#fff' },
|
|
262
|
+
module: { bg: nc.colorModule, border: darkenHex(nc.colorModule, 0.2), font: '#fff3e0' },
|
|
263
|
+
document: { bg: nc.colorDocument, border: darkenHex(nc.colorDocument, 0.15), font: '#dbeafe' },
|
|
264
|
+
// 主任务: 亮绿色系 (pending=亮绿, completed=略深)
|
|
265
|
+
mainTask: {
|
|
266
|
+
'pending': { bg: nc.colorMainTask, border: darkenHex(nc.colorMainTask, 0.15), font: '#052e16' },
|
|
267
|
+
'completed': { bg: darkenHex(nc.colorMainTask, 0.20), border: darkenHex(nc.colorMainTask, 0.35), font: '#d1fae5' },
|
|
268
|
+
'in_progress': { bg: '#7c3aed', border: '#6d28d9', font: '#ddd6fe' },
|
|
269
|
+
'cancelled': { bg: '#92400e', border: '#78350f', font: '#fde68a' }
|
|
270
|
+
},
|
|
271
|
+
// 子任务: 深绿色系 (pending=深绿, completed=更深)
|
|
272
|
+
subTask: {
|
|
273
|
+
'pending': { bg: nc.colorSubTask, border: darkenHex(nc.colorSubTask, 0.15), font: '#d1fae5' },
|
|
274
|
+
'completed': { bg: darkenHex(nc.colorSubTask, 0.20), border: darkenHex(nc.colorSubTask, 0.35), font: '#a7f3d0' },
|
|
275
|
+
'in_progress': { bg: '#7c3aed', border: '#6d28d9', font: '#ddd6fe' },
|
|
276
|
+
'cancelled': { bg: '#92400e', border: '#78350f', font: '#fde68a' }
|
|
277
|
+
},
|
|
278
|
+
// 通用状态颜色 (用于状态饼图等)
|
|
279
|
+
statusGeneric: {
|
|
280
|
+
completed: { bg: '#059669', border: '#047857', font: '#d1fae5' },
|
|
281
|
+
in_progress: { bg: '#7c3aed', border: '#6d28d9', font: '#ddd6fe' },
|
|
282
|
+
pending: { bg: '#4b5563', border: '#374151', font: '#d1d5db' },
|
|
283
|
+
cancelled: { bg: '#92400e', border: '#78350f', font: '#fde68a' }
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function initNodeColorsUI() {
|
|
289
|
+
var nc = getNodeColors();
|
|
290
|
+
var colorMap = {
|
|
291
|
+
'ncColorProject': 'colorProject',
|
|
292
|
+
'ncColorModule': 'colorModule',
|
|
293
|
+
'ncColorMainTask': 'colorMainTask',
|
|
294
|
+
'ncColorSubTask': 'colorSubTask',
|
|
295
|
+
'ncColorDocument': 'colorDocument'
|
|
296
|
+
};
|
|
297
|
+
for (var id in colorMap) {
|
|
298
|
+
var el = document.getElementById(id);
|
|
299
|
+
var hexEl = document.getElementById(id + 'Hex');
|
|
300
|
+
var v = nc[colorMap[id]];
|
|
301
|
+
if (el) el.value = v;
|
|
302
|
+
if (hexEl) hexEl.textContent = v;
|
|
303
|
+
if (el) {
|
|
304
|
+
var dot = el.parentElement.querySelector('.s3d-dot');
|
|
305
|
+
if (dot) dot.style.background = v;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function resetNodeColors() {
|
|
311
|
+
try { localStorage.removeItem(NODE_COLORS_KEY); } catch(e) {}
|
|
312
|
+
initNodeColorsUI();
|
|
313
|
+
showSettingsToast('↩ 已恢复默认节点颜色,刷新图谱页面生效');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
initNodeColorsUI();
|
|
317
|
+
|
|
318
|
+
// ========== 3D Force Graph 自定义设置 ==========
|
|
319
|
+
var S3D_DEFAULTS = {
|
|
320
|
+
gravity: 0.05,
|
|
321
|
+
repulsion: -30,
|
|
322
|
+
linkDistance: 40,
|
|
323
|
+
velocityDecay: 0.30,
|
|
324
|
+
alphaDecay: 0.020,
|
|
325
|
+
// 类型分层 (力导向模式): 不同类型节点保持不同轨道距离
|
|
326
|
+
typeSeparation: true, // 启用类型间空间分层
|
|
327
|
+
typeSepStrength: 0.8, // 分层力强度 (0=关闭, 1=强)
|
|
328
|
+
typeSepSpacing: 80, // 层间间距 (模块@80, 文档@160, 主任务@240, 子任务@320)
|
|
329
|
+
// 布局模式: 'force' (力导向) | 'orbital' (行星轨道)
|
|
330
|
+
layoutMode: 'force',
|
|
331
|
+
orbitSpacing: 80, // 轨道间距 (行星轨道模式)
|
|
332
|
+
orbitStrength: 0.8, // 轨道吸引力强度
|
|
333
|
+
orbitFlatten: 0.6, // Z 轴压平力度 (0=不压平/球壳, 1=完全压平/圆盘)
|
|
334
|
+
showOrbits: true, // 显示轨道环线
|
|
335
|
+
sizeProject: 50,
|
|
336
|
+
sizeModule: 25,
|
|
337
|
+
sizeMainTask: 15,
|
|
338
|
+
sizeSubTask: 8,
|
|
339
|
+
sizeDocument: 10,
|
|
340
|
+
particles: true,
|
|
341
|
+
arrows: false,
|
|
342
|
+
nodeOpacity: 0.90,
|
|
343
|
+
linkOpacity: 0.25,
|
|
344
|
+
bgColor: '#0a0e1a'
|
|
345
|
+
};
|
|
346
|
+
var S3D_KEY = 'devplan_3d_settings';
|
|
347
|
+
|
|
348
|
+
function get3DSettings() {
|
|
349
|
+
var settings = {};
|
|
350
|
+
for (var k in S3D_DEFAULTS) settings[k] = S3D_DEFAULTS[k];
|
|
351
|
+
try {
|
|
352
|
+
var saved = localStorage.getItem(S3D_KEY);
|
|
353
|
+
if (saved) {
|
|
354
|
+
var parsed = JSON.parse(saved);
|
|
355
|
+
for (var k in parsed) {
|
|
356
|
+
if (S3D_DEFAULTS.hasOwnProperty(k)) settings[k] = parsed[k];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} catch(e) {}
|
|
360
|
+
return settings;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function save3DSettings(settings) {
|
|
364
|
+
try { localStorage.setItem(S3D_KEY, JSON.stringify(settings)); } catch(e) {}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function update3DSetting(key, value) {
|
|
368
|
+
var settings = get3DSettings();
|
|
369
|
+
// Parse numeric values
|
|
370
|
+
if (typeof S3D_DEFAULTS[key] === 'number') {
|
|
371
|
+
value = parseFloat(value);
|
|
372
|
+
} else if (typeof S3D_DEFAULTS[key] === 'boolean') {
|
|
373
|
+
value = !!value;
|
|
374
|
+
}
|
|
375
|
+
settings[key] = value;
|
|
376
|
+
save3DSettings(settings);
|
|
377
|
+
// Update the display value
|
|
378
|
+
var valEl = document.getElementById('s3d' + key.charAt(0).toUpperCase() + key.slice(1) + 'Val');
|
|
379
|
+
if (valEl) {
|
|
380
|
+
if (typeof value === 'number') valEl.textContent = value.toFixed ? (Number.isInteger(value) ? value : value.toFixed(key === 'alphaDecay' ? 3 : 2)) : value;
|
|
381
|
+
else valEl.textContent = value;
|
|
382
|
+
}
|
|
383
|
+
showSettingsToast('✅ 3D 参数已保存,刷新图谱页面生效');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 兼容旧调用: 重定向到统一颜色管理
|
|
387
|
+
function update3DColor(nodeType, colorValue) {
|
|
388
|
+
updateNodeColor(nodeType, colorValue);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function updateLayoutMode(mode) {
|
|
392
|
+
var settings = get3DSettings();
|
|
393
|
+
settings.layoutMode = mode;
|
|
394
|
+
save3DSettings(settings);
|
|
395
|
+
var orbitalSettings = document.getElementById('s3dOrbitalSettings');
|
|
396
|
+
if (orbitalSettings) orbitalSettings.style.display = mode === 'orbital' ? 'block' : 'none';
|
|
397
|
+
showSettingsToast('✅ 布局模式已切换为 ' + (mode === 'orbital' ? '🪐 行星轨道' : '⚡ 力导向') + ',刷新图谱页面生效');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function reset3DSettings() {
|
|
401
|
+
try { localStorage.removeItem(S3D_KEY); } catch(e) {}
|
|
402
|
+
init3DSettingsUI();
|
|
403
|
+
showSettingsToast('↩ 已恢复 3D 默认设置,刷新图谱页面生效');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function toggle3DPanel(panelId) {
|
|
407
|
+
var panel = document.getElementById(panelId);
|
|
408
|
+
if (panel) panel.classList.toggle('collapsed');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function init3DSettingsUI() {
|
|
412
|
+
var s = get3DSettings();
|
|
413
|
+
// Show/hide 3D section based on engine
|
|
414
|
+
var sec = document.getElementById('settings3dSection');
|
|
415
|
+
if (sec) {
|
|
416
|
+
var engine = 'vis';
|
|
417
|
+
try { engine = localStorage.getItem('devplan_renderer_engine') || 'vis'; } catch(e) {}
|
|
418
|
+
if (engine === 'graphcanvas') engine = '3d';
|
|
419
|
+
sec.style.display = (engine === '3d') ? 'block' : 'none';
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Physics
|
|
423
|
+
var sliderMap = {
|
|
424
|
+
's3dGravity': { key: 'gravity', fmt: 2 },
|
|
425
|
+
's3dRepulsion': { key: 'repulsion', fmt: 0 },
|
|
426
|
+
's3dLinkDist': { key: 'linkDistance', fmt: 0 },
|
|
427
|
+
's3dVelocityDecay': { key: 'velocityDecay', fmt: 2 },
|
|
428
|
+
's3dAlphaDecay': { key: 'alphaDecay', fmt: 3 },
|
|
429
|
+
's3dSizeProject': { key: 'sizeProject', fmt: 0 },
|
|
430
|
+
's3dSizeModule': { key: 'sizeModule', fmt: 0 },
|
|
431
|
+
's3dSizeMainTask': { key: 'sizeMainTask', fmt: 0 },
|
|
432
|
+
's3dSizeSubTask': { key: 'sizeSubTask', fmt: 0 },
|
|
433
|
+
's3dSizeDocument': { key: 'sizeDocument', fmt: 0 },
|
|
434
|
+
's3dNodeOpacity': { key: 'nodeOpacity', fmt: 2 },
|
|
435
|
+
's3dLinkOpacity': { key: 'linkOpacity', fmt: 2 },
|
|
436
|
+
's3dTypeSepStrength': { key: 'typeSepStrength', fmt: 2 },
|
|
437
|
+
's3dTypeSepSpacing': { key: 'typeSepSpacing', fmt: 0 },
|
|
438
|
+
's3dOrbitSpacing': { key: 'orbitSpacing', fmt: 0 },
|
|
439
|
+
's3dOrbitStrength': { key: 'orbitStrength', fmt: 2 },
|
|
440
|
+
's3dOrbitFlatten': { key: 'orbitFlatten', fmt: 2 }
|
|
441
|
+
};
|
|
442
|
+
for (var id in sliderMap) {
|
|
443
|
+
var el = document.getElementById(id);
|
|
444
|
+
var valEl = document.getElementById(id + 'Val');
|
|
445
|
+
var cfg = sliderMap[id];
|
|
446
|
+
var v = s[cfg.key];
|
|
447
|
+
if (el) el.value = v;
|
|
448
|
+
if (valEl) valEl.textContent = cfg.fmt > 0 ? parseFloat(v).toFixed(cfg.fmt) : Math.round(v);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Background color (3D only)
|
|
452
|
+
var bgEl = document.getElementById('s3dBgColor');
|
|
453
|
+
var bgHexEl = document.getElementById('s3dBgColorHex');
|
|
454
|
+
if (bgEl) bgEl.value = s.bgColor || '#0a0e1a';
|
|
455
|
+
if (bgHexEl) bgHexEl.textContent = s.bgColor || '#0a0e1a';
|
|
456
|
+
|
|
457
|
+
// Toggles
|
|
458
|
+
var toggleMap = { 's3dParticles': 'particles', 's3dArrows': 'arrows', 's3dShowOrbits': 'showOrbits', 's3dTypeSeparation': 'typeSeparation' };
|
|
459
|
+
for (var id in toggleMap) {
|
|
460
|
+
var el = document.getElementById(id);
|
|
461
|
+
if (el) el.checked = !!s[toggleMap[id]];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Layout mode radio
|
|
465
|
+
var layoutRadios = document.querySelectorAll('input[name="s3dLayoutMode"]');
|
|
466
|
+
for (var i = 0; i < layoutRadios.length; i++) {
|
|
467
|
+
layoutRadios[i].checked = (layoutRadios[i].value === s.layoutMode);
|
|
468
|
+
}
|
|
469
|
+
// Show/hide orbital-specific settings
|
|
470
|
+
var orbitalSettings = document.getElementById('s3dOrbitalSettings');
|
|
471
|
+
if (orbitalSettings) orbitalSettings.style.display = s.layoutMode === 'orbital' ? 'block' : 'none';
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Initialize 3D settings UI on page load
|
|
475
|
+
init3DSettingsUI();
|
|
476
|
+
|
|
477
|
+
// ========== Debug ==========
|
|
478
|
+
var dbg = document.getElementById('debug');
|
|
479
|
+
function log(msg, ok) {
|
|
480
|
+
console.log('[DevPlan]', msg);
|
|
481
|
+
dbg.innerHTML = (ok ? '<span class="ok">✓</span> ' : '<span class="err">✗</span> ') + msg;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ========== 渲染引擎选择: vis-network (默认) vs 3D Force Graph ==========
|
|
485
|
+
// 优先级: URL 参数 > localStorage (项目设置页) > 默认值 (vis)
|
|
486
|
+
var RENDERER_ENGINE = 'vis'; // 'vis' (默认) | '3d' (3D 球体可视化)
|
|
487
|
+
(function() {
|
|
488
|
+
// 1. 先从 localStorage 读取用户在项目设置页的选择
|
|
489
|
+
try {
|
|
490
|
+
var saved = localStorage.getItem('devplan_renderer_engine');
|
|
491
|
+
if (saved === '3d' || saved === 'vis') RENDERER_ENGINE = saved;
|
|
492
|
+
// 兼容旧值 graphcanvas → 自动迁移为 3d
|
|
493
|
+
if (saved === 'graphcanvas') { RENDERER_ENGINE = '3d'; try { localStorage.setItem('devplan_renderer_engine', '3d'); } catch(e2) {} }
|
|
494
|
+
} catch(e) {}
|
|
495
|
+
// 2. URL 参数优先级最高(覆盖 localStorage)
|
|
496
|
+
var params = new URLSearchParams(window.location.search);
|
|
497
|
+
var r = params.get('renderer');
|
|
498
|
+
if (r === '3d' || r === '3d-force' || r === 'graphcanvas' || r === 'gc') RENDERER_ENGINE = '3d';
|
|
499
|
+
else if (r === 'vis') RENDERER_ENGINE = 'vis';
|
|
500
|
+
})();
|
|
501
|
+
var USE_3D = false; // set after 3d-force-graph engine loads
|
|
502
|
+
|
|
503
|
+
// Update engine badge label
|
|
504
|
+
(function() {
|
|
505
|
+
var label = document.getElementById('engineNameLabel');
|
|
506
|
+
if (label) label.textContent = RENDERER_ENGINE === '3d' ? '3D Force Graph' : 'vis-network';
|
|
507
|
+
})();
|
|
508
|
+
|
|
509
|
+
// ========== SimpleDataSet — vis.DataSet shim for non-vis-network modes ==========
|
|
510
|
+
function SimpleDataSet(items) {
|
|
511
|
+
this._data = {};
|
|
512
|
+
this._ids = [];
|
|
513
|
+
if (items) {
|
|
514
|
+
for (var i = 0; i < items.length; i++) {
|
|
515
|
+
var item = items[i];
|
|
516
|
+
this._data[item.id] = item;
|
|
517
|
+
this._ids.push(item.id);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
SimpleDataSet.prototype.get = function(id) {
|
|
522
|
+
if (id === undefined || id === null) {
|
|
523
|
+
// Return all items as array
|
|
524
|
+
var result = [];
|
|
525
|
+
for (var i = 0; i < this._ids.length; i++) result.push(this._data[this._ids[i]]);
|
|
526
|
+
return result;
|
|
527
|
+
}
|
|
528
|
+
return this._data[id] || null;
|
|
529
|
+
};
|
|
530
|
+
SimpleDataSet.prototype.getIds = function() {
|
|
531
|
+
return this._ids.slice();
|
|
532
|
+
};
|
|
533
|
+
SimpleDataSet.prototype.forEach = function(callback) {
|
|
534
|
+
for (var i = 0; i < this._ids.length; i++) {
|
|
535
|
+
callback(this._data[this._ids[i]], this._ids[i]);
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
SimpleDataSet.prototype.update = function(itemOrArray) {
|
|
539
|
+
var items = Array.isArray(itemOrArray) ? itemOrArray : [itemOrArray];
|
|
540
|
+
for (var i = 0; i < items.length; i++) {
|
|
541
|
+
var item = items[i];
|
|
542
|
+
if (this._data[item.id]) {
|
|
543
|
+
for (var key in item) {
|
|
544
|
+
if (item.hasOwnProperty(key)) this._data[item.id][key] = item[key];
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
SimpleDataSet.prototype.add = function(itemOrArray) {
|
|
550
|
+
var items = Array.isArray(itemOrArray) ? itemOrArray : [itemOrArray];
|
|
551
|
+
for (var i = 0; i < items.length; i++) {
|
|
552
|
+
var item = items[i];
|
|
553
|
+
this._data[item.id] = item;
|
|
554
|
+
if (this._ids.indexOf(item.id) === -1) this._ids.push(item.id);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
SimpleDataSet.prototype.remove = function(idOrArray) {
|
|
558
|
+
var ids = Array.isArray(idOrArray) ? idOrArray : [idOrArray];
|
|
559
|
+
for (var i = 0; i < ids.length; i++) {
|
|
560
|
+
var id = typeof ids[i] === 'object' ? ids[i].id : ids[i];
|
|
561
|
+
delete this._data[id];
|
|
562
|
+
var idx = this._ids.indexOf(id);
|
|
563
|
+
if (idx >= 0) this._ids.splice(idx, 1);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
// ========== 动态加载渲染引擎 ==========
|
|
567
|
+
function loadRenderEngine() {
|
|
568
|
+
if (RENDERER_ENGINE === '3d') {
|
|
569
|
+
log('正在加载 3D Force Graph 引擎 (Three.js + d3-force-3d)...', true);
|
|
570
|
+
load3DForceGraph(0);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// 默认: 使用 vis-network 渲染器(成熟稳定、形状丰富)
|
|
575
|
+
log('使用 vis-network 渲染器 (默认)', true);
|
|
576
|
+
loadVisNetwork(0);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// ── Three.js CDN URLs (必须在 3d-force-graph 之前加载) ──
|
|
580
|
+
var THREE_JS_URLS = [
|
|
581
|
+
'https://unpkg.com/three@0.160.0/build/three.min.js',
|
|
582
|
+
'https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js',
|
|
583
|
+
'https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.min.js'
|
|
584
|
+
];
|
|
585
|
+
|
|
586
|
+
// ── 3D Force Graph CDN URLs ──
|
|
587
|
+
var THREE_D_URLS = [
|
|
588
|
+
'https://unpkg.com/3d-force-graph@1/dist/3d-force-graph.min.js',
|
|
589
|
+
'https://cdn.jsdelivr.net/npm/3d-force-graph@1/dist/3d-force-graph.min.js'
|
|
590
|
+
];
|
|
591
|
+
|
|
592
|
+
function load3DForceGraph(index) {
|
|
593
|
+
// Step 1: 先加载 Three.js (3d-force-graph 依赖 window.THREE)
|
|
594
|
+
log('Step 1/2: 加载 Three.js...', true);
|
|
595
|
+
loadThreeJS(0);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function loadThreeJS(index) {
|
|
599
|
+
if (index >= THREE_JS_URLS.length) {
|
|
600
|
+
log('Three.js CDN 均加载失败, 回退到 vis-network', false);
|
|
601
|
+
loadVisNetwork(0);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
var url = THREE_JS_URLS[index];
|
|
605
|
+
log('尝试加载 Three.js CDN #' + (index+1) + ': ' + url.split('/')[2], true);
|
|
606
|
+
var s = document.createElement('script');
|
|
607
|
+
s.src = url;
|
|
608
|
+
s.onload = function() {
|
|
609
|
+
if (typeof THREE !== 'undefined') {
|
|
610
|
+
log('Three.js 加载成功 ✓ (r' + (THREE.REVISION || '?') + ')', true);
|
|
611
|
+
// Step 2: 加载 3d-force-graph
|
|
612
|
+
log('Step 2/2: 加载 3D Force Graph...', true);
|
|
613
|
+
loadForceGraph3D(0);
|
|
614
|
+
} else {
|
|
615
|
+
log('Three.js CDN #' + (index+1) + ' 加载但 THREE 不存在, 尝试下一个', false);
|
|
616
|
+
loadThreeJS(index + 1);
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
s.onerror = function() {
|
|
620
|
+
log('Three.js CDN #' + (index+1) + ' 加载失败, 尝试下一个', false);
|
|
621
|
+
loadThreeJS(index + 1);
|
|
622
|
+
};
|
|
623
|
+
document.head.appendChild(s);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function loadForceGraph3D(index) {
|
|
627
|
+
if (index >= THREE_D_URLS.length) {
|
|
628
|
+
log('3D Force Graph CDN 均加载失败, 回退到 vis-network', false);
|
|
629
|
+
loadVisNetwork(0);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
var url = THREE_D_URLS[index];
|
|
633
|
+
log('尝试加载 3D Force Graph CDN #' + (index+1) + ': ' + url.split('/')[2], true);
|
|
634
|
+
var s = document.createElement('script');
|
|
635
|
+
s.src = url;
|
|
636
|
+
s.onload = function() {
|
|
637
|
+
if (typeof ForceGraph3D !== 'undefined') {
|
|
638
|
+
log('3D Force Graph 引擎加载成功 ✓ (Three.js WebGL)', true);
|
|
639
|
+
USE_3D = true;
|
|
640
|
+
startApp();
|
|
641
|
+
} else {
|
|
642
|
+
log('3D CDN #' + (index+1) + ' 加载但 ForceGraph3D 不存在, 尝试下一个', false);
|
|
643
|
+
loadForceGraph3D(index + 1);
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
s.onerror = function() {
|
|
647
|
+
log('3D CDN #' + (index+1) + ' 加载失败, 尝试下一个', false);
|
|
648
|
+
loadForceGraph3D(index + 1);
|
|
649
|
+
};
|
|
650
|
+
document.head.appendChild(s);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// ── vis-network CDN URLs ──
|
|
654
|
+
var VIS_URLS = [
|
|
655
|
+
'https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js',
|
|
656
|
+
'https://cdn.jsdelivr.net/npm/vis-network@9.1.9/standalone/umd/vis-network.min.js',
|
|
657
|
+
'https://cdnjs.cloudflare.com/ajax/libs/vis-network/9.1.9/standalone/umd/vis-network.min.js'
|
|
658
|
+
];
|
|
659
|
+
|
|
660
|
+
function loadVisNetwork(index) {
|
|
661
|
+
if (index >= VIS_URLS.length) {
|
|
662
|
+
log('所有 CDN 均加载失败,请检查网络连接', false);
|
|
663
|
+
document.getElementById('loading').innerHTML = '<div style="text-align:center"><div style="font-size:48px;margin-bottom:16px;">⚠️</div><p style="color:#f87171;">渲染引擎加载失败</p><p style="color:#9ca3af;margin-top:8px;font-size:13px;">所有 CDN 均不可用</p><button class="refresh-btn" onclick="location.reload()" style="margin-top:12px;">刷新页面</button></div>';
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
var url = VIS_URLS[index];
|
|
667
|
+
log('尝试加载 vis-network CDN #' + (index+1) + ': ' + url.split('/')[2], true);
|
|
668
|
+
var s = document.createElement('script');
|
|
669
|
+
s.src = url;
|
|
670
|
+
s.onload = function() {
|
|
671
|
+
if (typeof vis !== 'undefined' && vis.Network && vis.DataSet) {
|
|
672
|
+
log('vis-network 加载成功 (CDN #' + (index+1) + ')', true);
|
|
673
|
+
USE_3D = false;
|
|
674
|
+
startApp();
|
|
675
|
+
} else {
|
|
676
|
+
log('CDN #' + (index+1) + ' 加载但 vis 对象不完整, 尝试下一个', false);
|
|
677
|
+
loadVisNetwork(index + 1);
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
s.onerror = function() {
|
|
681
|
+
log('CDN #' + (index+1) + ' 加载失败, 尝试下一个', false);
|
|
682
|
+
loadVisNetwork(index + 1);
|
|
683
|
+
};
|
|
684
|
+
document.head.appendChild(s);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// ========== State ==========
|
|
688
|
+
var network = null;
|
|
689
|
+
var allNodes = [];
|
|
690
|
+
var allEdges = [];
|
|
691
|
+
var nodesDataSet = null;
|
|
692
|
+
var edgesDataSet = null;
|
|
693
|
+
var hiddenTypes = {};
|
|
694
|
+
var ctrlPressed = false;
|
|
695
|
+
var INCLUDE_NODE_DEGREE = true;
|
|
696
|
+
var ENABLE_BACKEND_DEGREE_FALLBACK = true;
|
|
697
|
+
|
|
698
|
+
// ── Phase-10: Tiered loading state ──
|
|
699
|
+
// L0: project, module | L1: main-task | L2: sub-task | L3: document
|
|
700
|
+
var TIER_L0L1_TYPES = ['devplan-project', 'devplan-module', 'devplan-main-task'];
|
|
701
|
+
var TIER_L2_TYPES = ['devplan-sub-task'];
|
|
702
|
+
var TIER_L3_TYPES = ['devplan-document'];
|
|
703
|
+
var tieredLoadState = {
|
|
704
|
+
l0l1Loaded: false, // L0+L1 core nodes loaded
|
|
705
|
+
l2Loaded: false, // L2 sub-tasks loaded
|
|
706
|
+
l3Loaded: false, // L3 documents loaded
|
|
707
|
+
expandedPhases: {}, // phase-X -> true: which main-tasks have been expanded
|
|
708
|
+
totalNodes: 0, // total node count from server
|
|
709
|
+
};
|
|
710
|
+
// Phase-10 T10.3: Track if network instance should be reused
|
|
711
|
+
var networkReusable = false;
|
|
712
|
+
`;
|
|
713
|
+
}
|
|
714
|
+
//# sourceMappingURL=template-core.js.map
|