@qnote/q-ai-note 1.0.5 → 1.0.6
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/README.md +44 -0
- package/dist/cli.js +68 -18
- package/dist/cli.js.map +1 -1
- package/dist/server/index.d.ts +7 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +62 -4
- package/dist/server/index.js.map +1 -1
- package/dist/web/app.js +176 -40
- package/dist/web/index.html +4 -1
- package/dist/web/styles.css +67 -7
- package/dist/web/vueRenderers.js +64 -52
- package/package.json +3 -2
package/dist/web/app.js
CHANGED
|
@@ -9,6 +9,7 @@ const state = {
|
|
|
9
9
|
operations: [],
|
|
10
10
|
chats: [],
|
|
11
11
|
settings: {},
|
|
12
|
+
readonly: false,
|
|
12
13
|
pendingAction: null,
|
|
13
14
|
nodeEntities: [],
|
|
14
15
|
nodeEntityStats: null,
|
|
@@ -16,7 +17,8 @@ const state = {
|
|
|
16
17
|
nodeEntityFilter: 'all',
|
|
17
18
|
editingNodeEntityId: null,
|
|
18
19
|
nodeEntityFormExpanded: false,
|
|
19
|
-
|
|
20
|
+
sandboxChatVisible: false,
|
|
21
|
+
sandboxChatVisibleBeforeFullscreen: false,
|
|
20
22
|
sandboxFullscreenMode: false,
|
|
21
23
|
workTreeViewMode: 'full',
|
|
22
24
|
workItemElementPreviewMode: 'none',
|
|
@@ -38,6 +40,7 @@ let sandboxActionHandler = null;
|
|
|
38
40
|
let sandboxEscLocked = false;
|
|
39
41
|
let resizeRenderTimer = null;
|
|
40
42
|
const WORK_ITEM_SHOW_ASSIGNEE_STORAGE_KEY = 'q-ai-note.work-item.show-assignee';
|
|
43
|
+
const WORK_TREE_VIEW_MODE_STORAGE_KEY = 'q-ai-note.work-tree.view-mode';
|
|
41
44
|
|
|
42
45
|
function loadWorkItemAssigneePreference() {
|
|
43
46
|
try {
|
|
@@ -60,14 +63,78 @@ function persistWorkItemAssigneePreference() {
|
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
function
|
|
66
|
+
function loadWorkTreeViewModePreference() {
|
|
67
|
+
try {
|
|
68
|
+
const raw = String(window.localStorage.getItem(WORK_TREE_VIEW_MODE_STORAGE_KEY) || '').trim();
|
|
69
|
+
if (raw === 'full' || raw === 'report' || raw === 'dense') {
|
|
70
|
+
state.workTreeViewMode = raw;
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
// Ignore storage failures in restricted environments.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function persistWorkTreeViewModePreference() {
|
|
78
|
+
try {
|
|
79
|
+
window.localStorage.setItem(WORK_TREE_VIEW_MODE_STORAGE_KEY, state.workTreeViewMode || 'full');
|
|
80
|
+
} catch {
|
|
81
|
+
// Ignore storage failures in restricted environments.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function applySandboxChatVisibility() {
|
|
64
86
|
const layout = document.getElementById('sandbox-layout');
|
|
65
|
-
const toggleBtn = document.getElementById('toggle-sandbox-
|
|
87
|
+
const toggleBtn = document.getElementById('toggle-sandbox-chat-btn');
|
|
66
88
|
if (!layout || !toggleBtn) return;
|
|
67
|
-
const
|
|
68
|
-
layout.classList.toggle('
|
|
69
|
-
toggleBtn.
|
|
70
|
-
toggleBtn.
|
|
89
|
+
const shouldShow = !state.readonly && Boolean(state.sandboxChatVisible);
|
|
90
|
+
layout.classList.toggle('show-chat', shouldShow);
|
|
91
|
+
toggleBtn.classList.toggle('hidden', state.readonly);
|
|
92
|
+
toggleBtn.innerHTML = shouldShow
|
|
93
|
+
? '<span class="icon" aria-hidden="true">🤖</span><span>隐藏 AI 助手</span>'
|
|
94
|
+
: '<span class="icon" aria-hidden="true">🤖</span><span>显示 AI 助手</span>';
|
|
95
|
+
toggleBtn.setAttribute('aria-pressed', shouldShow ? 'true' : 'false');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function loadRuntimeMode() {
|
|
99
|
+
try {
|
|
100
|
+
const runtime = await apiRequest(`${API_BASE}/runtime`);
|
|
101
|
+
state.readonly = Boolean(runtime?.readonly);
|
|
102
|
+
} catch {
|
|
103
|
+
state.readonly = false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function setHiddenById(id, hidden = true) {
|
|
108
|
+
const el = document.getElementById(id);
|
|
109
|
+
if (!el) return;
|
|
110
|
+
el.classList.toggle('hidden', hidden);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function applyReadonlyMode() {
|
|
114
|
+
document.body.classList.toggle('readonly-mode', state.readonly);
|
|
115
|
+
const settingsNav = document.querySelector('[data-nav="settings"]');
|
|
116
|
+
if (settingsNav instanceof HTMLElement) {
|
|
117
|
+
settingsNav.classList.toggle('hidden', state.readonly);
|
|
118
|
+
}
|
|
119
|
+
setHiddenById('page-settings', state.readonly);
|
|
120
|
+
setHiddenById('add-sandbox-btn', state.readonly);
|
|
121
|
+
setHiddenById('add-item-btn', state.readonly);
|
|
122
|
+
setHiddenById('toggle-node-entity-form-btn', state.readonly);
|
|
123
|
+
setHiddenById('drawer-diary-save-btn', state.readonly);
|
|
124
|
+
setHiddenById('save-diary-btn', state.readonly);
|
|
125
|
+
const diaryForm = document.querySelector('#page-diaries .diary-form');
|
|
126
|
+
if (diaryForm instanceof HTMLElement) {
|
|
127
|
+
diaryForm.classList.toggle('hidden', state.readonly);
|
|
128
|
+
}
|
|
129
|
+
const drawerDiaryForm = document.querySelector('#node-entity-drawer .drawer-diary-quick-form');
|
|
130
|
+
if (drawerDiaryForm instanceof HTMLElement) {
|
|
131
|
+
drawerDiaryForm.classList.toggle('hidden', state.readonly);
|
|
132
|
+
}
|
|
133
|
+
if (state.readonly) {
|
|
134
|
+
state.sandboxChatVisible = false;
|
|
135
|
+
closeQuickChatPopover();
|
|
136
|
+
}
|
|
137
|
+
applySandboxChatVisibility();
|
|
71
138
|
}
|
|
72
139
|
|
|
73
140
|
function getSandboxLayoutElement() {
|
|
@@ -100,7 +167,14 @@ function applySandboxFullscreenState() {
|
|
|
100
167
|
const layout = getSandboxLayoutElement();
|
|
101
168
|
const fullscreenRoot = getSandboxFullscreenElement();
|
|
102
169
|
const toggleBtn = document.getElementById('toggle-sandbox-fullscreen-btn');
|
|
170
|
+
const wasFullscreen = Boolean(state.sandboxFullscreenMode);
|
|
103
171
|
const isFullscreen = Boolean(fullscreenRoot && document.fullscreenElement === fullscreenRoot);
|
|
172
|
+
if (!wasFullscreen && isFullscreen) {
|
|
173
|
+
state.sandboxChatVisibleBeforeFullscreen = Boolean(state.sandboxChatVisible);
|
|
174
|
+
state.sandboxChatVisible = false;
|
|
175
|
+
} else if (wasFullscreen && !isFullscreen) {
|
|
176
|
+
state.sandboxChatVisible = Boolean(state.sandboxChatVisibleBeforeFullscreen);
|
|
177
|
+
}
|
|
104
178
|
state.sandboxFullscreenMode = isFullscreen;
|
|
105
179
|
if (layout) {
|
|
106
180
|
layout.classList.toggle('is-fullscreen', isFullscreen);
|
|
@@ -112,9 +186,22 @@ function applySandboxFullscreenState() {
|
|
|
112
186
|
toggleBtn.textContent = isFullscreen ? '退出全屏' : '全屏';
|
|
113
187
|
toggleBtn.setAttribute('aria-pressed', isFullscreen ? 'true' : 'false');
|
|
114
188
|
}
|
|
189
|
+
applySandboxChatVisibility();
|
|
190
|
+
applySandboxLayoutHeight();
|
|
115
191
|
void syncSandboxEscLock(isFullscreen);
|
|
116
192
|
}
|
|
117
193
|
|
|
194
|
+
function applySandboxLayoutHeight() {
|
|
195
|
+
const layout = getSandboxLayoutElement();
|
|
196
|
+
if (!(layout instanceof HTMLElement)) return;
|
|
197
|
+
const rect = layout.getBoundingClientRect();
|
|
198
|
+
const viewportHeight = Math.floor(window.visualViewport?.height || window.innerHeight || 0);
|
|
199
|
+
if (!Number.isFinite(viewportHeight) || viewportHeight <= 0) return;
|
|
200
|
+
const bottomGap = state.sandboxFullscreenMode ? 8 : 10;
|
|
201
|
+
const nextHeight = Math.max(320, viewportHeight - Math.floor(rect.top) - bottomGap);
|
|
202
|
+
layout.style.height = `${nextHeight}px`;
|
|
203
|
+
}
|
|
204
|
+
|
|
118
205
|
async function toggleSandboxFullscreen() {
|
|
119
206
|
const fullscreenRoot = getSandboxFullscreenElement();
|
|
120
207
|
if (!fullscreenRoot) return;
|
|
@@ -230,7 +317,7 @@ function renderWorkTree() {
|
|
|
230
317
|
}
|
|
231
318
|
|
|
232
319
|
if (allItems.length === 0) {
|
|
233
|
-
tree.innerHTML =
|
|
320
|
+
tree.innerHTML = `<div class="empty-state"><p>${state.readonly ? '暂无任务' : '点击上方"添加"按钮创建第一个任务'}</p></div>`;
|
|
234
321
|
return;
|
|
235
322
|
}
|
|
236
323
|
|
|
@@ -246,7 +333,7 @@ function renderWorkTree() {
|
|
|
246
333
|
}
|
|
247
334
|
renderWorkTree();
|
|
248
335
|
},
|
|
249
|
-
onAddChild: (parentId) => {
|
|
336
|
+
onAddChild: state.readonly ? undefined : (parentId) => {
|
|
250
337
|
document.getElementById('item-dialog-title').textContent = '添加子任务';
|
|
251
338
|
document.getElementById('item-dialog').dataset.editId = '';
|
|
252
339
|
document.getElementById('new-item-name').value = '';
|
|
@@ -257,14 +344,14 @@ function renderWorkTree() {
|
|
|
257
344
|
document.getElementById('new-item-parent').value = parentId;
|
|
258
345
|
document.getElementById('item-dialog').showModal();
|
|
259
346
|
},
|
|
260
|
-
onAddDiary: (nodeId) => {
|
|
347
|
+
onAddDiary: state.readonly ? undefined : (nodeId) => {
|
|
261
348
|
showNodeEntityDrawer(nodeId, 'diary');
|
|
262
349
|
const textarea = document.getElementById('drawer-diary-content');
|
|
263
350
|
if (textarea instanceof HTMLTextAreaElement) {
|
|
264
351
|
textarea.focus();
|
|
265
352
|
}
|
|
266
353
|
},
|
|
267
|
-
onEdit: (id) => {
|
|
354
|
+
onEdit: state.readonly ? undefined : (id) => {
|
|
268
355
|
editWorkItem(id);
|
|
269
356
|
},
|
|
270
357
|
onSelect: (id) => {
|
|
@@ -273,7 +360,7 @@ function renderWorkTree() {
|
|
|
273
360
|
onSelectEntity: (nodeId, entityType) => {
|
|
274
361
|
showNodeEntityDrawer(nodeId, entityType || 'all');
|
|
275
362
|
},
|
|
276
|
-
onMoveNode: async (dragNodeId, newParentId) => {
|
|
363
|
+
onMoveNode: state.readonly ? undefined : async (dragNodeId, newParentId) => {
|
|
277
364
|
if (!state.currentSandbox) return;
|
|
278
365
|
if (!dragNodeId || dragNodeId === newParentId) return;
|
|
279
366
|
const byId = new Map((state.currentSandbox.items || []).map((item) => [item.id, item]));
|
|
@@ -301,7 +388,7 @@ function renderWorkTree() {
|
|
|
301
388
|
});
|
|
302
389
|
await loadSandbox(state.currentSandbox.id);
|
|
303
390
|
},
|
|
304
|
-
onReorderSiblings: async (dragNodeId, targetNodeId, position) => {
|
|
391
|
+
onReorderSiblings: state.readonly ? undefined : async (dragNodeId, targetNodeId, position) => {
|
|
305
392
|
if (!state.currentSandbox) return;
|
|
306
393
|
if (!dragNodeId || !targetNodeId || dragNodeId === targetNodeId) return;
|
|
307
394
|
const byId = new Map((state.currentSandbox.items || []).map((item) => [item.id, item]));
|
|
@@ -334,7 +421,7 @@ function renderWorkTree() {
|
|
|
334
421
|
});
|
|
335
422
|
await loadSandbox(state.currentSandbox.id);
|
|
336
423
|
},
|
|
337
|
-
onReorderLanes: async (dragRootId, targetRootId) => {
|
|
424
|
+
onReorderLanes: state.readonly ? undefined : async (dragRootId, targetRootId) => {
|
|
338
425
|
if (!state.currentSandbox) return;
|
|
339
426
|
if (!dragRootId || !targetRootId || dragRootId === targetRootId) return;
|
|
340
427
|
const roots = (state.currentSandbox.items || []).filter((item) => !item.parent_id);
|
|
@@ -359,19 +446,21 @@ function renderWorkTree() {
|
|
|
359
446
|
});
|
|
360
447
|
await loadSandbox(state.currentSandbox.id);
|
|
361
448
|
},
|
|
362
|
-
onDelete: async (id) => {
|
|
449
|
+
onDelete: state.readonly ? undefined : async (id) => {
|
|
363
450
|
if (confirm('确定删除此任务?')) {
|
|
364
451
|
await apiRequest(`${API_BASE}/items/${id}`, { method: 'DELETE' });
|
|
365
452
|
await loadSandbox(state.currentSandbox.id);
|
|
366
453
|
}
|
|
367
454
|
},
|
|
368
|
-
onQuickChat: (id, el) => {
|
|
455
|
+
onQuickChat: state.readonly ? undefined : (id, el) => {
|
|
369
456
|
openQuickChatPopover(id, el);
|
|
370
457
|
},
|
|
371
458
|
renderMode: state.workTreeViewMode === 'dense' ? 'dense' : 'card',
|
|
372
459
|
showAssignee: state.workItemShowAssignee,
|
|
373
460
|
elementPreviewMode: state.workItemElementPreviewMode,
|
|
374
461
|
entityRowsByNodeId,
|
|
462
|
+
readonly: state.readonly,
|
|
463
|
+
selectedId: state.selectedNodeId || '',
|
|
375
464
|
});
|
|
376
465
|
|
|
377
466
|
populateParentSelect(allItems);
|
|
@@ -674,6 +763,7 @@ function closeNodeEntityDrawer() {
|
|
|
674
763
|
state.nodeEntityFilter = 'all';
|
|
675
764
|
state.editingNodeEntityId = null;
|
|
676
765
|
state.nodeEntityFormExpanded = false;
|
|
766
|
+
renderWorkTree();
|
|
677
767
|
}
|
|
678
768
|
|
|
679
769
|
function setNodeEntityFormExpanded(expanded) {
|
|
@@ -755,13 +845,15 @@ function renderNodeEntityList(nodeId) {
|
|
|
755
845
|
<span class="entity-type-pill">diary</span>
|
|
756
846
|
<strong>${safeText('日志记录')}</strong>
|
|
757
847
|
</div>
|
|
758
|
-
|
|
759
|
-
<
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
848
|
+
${state.readonly ? '' : `
|
|
849
|
+
<div class="entity-card-actions">
|
|
850
|
+
<button class="btn btn-secondary btn-sm" data-diary-edit-id="${safeText(row.id)}">编辑</button>
|
|
851
|
+
${row.processed ? '' : `
|
|
852
|
+
<button class="btn btn-secondary btn-sm" data-diary-confirm-id="${safeText(row.id)}">采纳</button>
|
|
853
|
+
<button class="btn btn-secondary btn-sm" data-diary-ignore-id="${safeText(row.id)}">忽略</button>
|
|
854
|
+
`}
|
|
855
|
+
</div>
|
|
856
|
+
`}
|
|
765
857
|
</div>
|
|
766
858
|
<div class="entity-meta">
|
|
767
859
|
${safeText(new Date(row.created_at).toLocaleString())}
|
|
@@ -778,10 +870,12 @@ function renderNodeEntityList(nodeId) {
|
|
|
778
870
|
<span class="entity-type-pill">${safeText(row.entity_type)}</span>
|
|
779
871
|
<strong>${safeText(row.title || '-')}</strong>
|
|
780
872
|
</div>
|
|
781
|
-
|
|
782
|
-
<
|
|
783
|
-
|
|
784
|
-
|
|
873
|
+
${state.readonly ? '' : `
|
|
874
|
+
<div class="entity-card-actions">
|
|
875
|
+
<button class="btn btn-secondary btn-sm" data-entity-edit-id="${safeText(row.id)}">编辑</button>
|
|
876
|
+
<button class="btn btn-secondary btn-sm" data-entity-delete-id="${safeText(row.id)}">删除</button>
|
|
877
|
+
</div>
|
|
878
|
+
`}
|
|
785
879
|
</div>
|
|
786
880
|
<div class="entity-meta">
|
|
787
881
|
${safeText(new Date(row.created_at).toLocaleString())}
|
|
@@ -920,6 +1014,7 @@ function showNodeEntityDrawer(nodeId, preferredFilter = 'all') {
|
|
|
920
1014
|
state.nodeEntityFilter = filter;
|
|
921
1015
|
title.textContent = node.name || nodeId;
|
|
922
1016
|
renderNodeEntitySummary(nodeId);
|
|
1017
|
+
renderWorkTree();
|
|
923
1018
|
renderQuickDiaryTargetLabel();
|
|
924
1019
|
renderNodeEntityFilterTabs();
|
|
925
1020
|
renderNodeEntityList(nodeId);
|
|
@@ -1031,6 +1126,7 @@ function composeQuickChatContent(nodeId, userQuestion) {
|
|
|
1031
1126
|
}
|
|
1032
1127
|
|
|
1033
1128
|
async function sendSandboxChatMessage(content, options = {}) {
|
|
1129
|
+
if (state.readonly) return;
|
|
1034
1130
|
if (!content || !state.currentSandbox) return;
|
|
1035
1131
|
const messages = document.getElementById('sandbox-chat-messages');
|
|
1036
1132
|
const btn = document.getElementById('sandbox-send-btn');
|
|
@@ -1221,12 +1317,13 @@ function renderSandboxes() {
|
|
|
1221
1317
|
onOpen: (id) => {
|
|
1222
1318
|
window.location.hash = `/sandbox/${id}`;
|
|
1223
1319
|
},
|
|
1224
|
-
onDelete: async (id) => {
|
|
1320
|
+
onDelete: state.readonly ? undefined : async (id) => {
|
|
1225
1321
|
if (confirm('确定删除此沙盘?')) {
|
|
1226
1322
|
await apiRequest(`${API_BASE}/sandboxes/${id}`, { method: 'DELETE' });
|
|
1227
1323
|
await loadSandboxes();
|
|
1228
1324
|
}
|
|
1229
|
-
}
|
|
1325
|
+
},
|
|
1326
|
+
readonly: state.readonly,
|
|
1230
1327
|
});
|
|
1231
1328
|
}
|
|
1232
1329
|
|
|
@@ -1247,12 +1344,19 @@ async function loadSandbox(id) {
|
|
|
1247
1344
|
applyWorkTreeViewMode(state.workTreeViewMode || 'full');
|
|
1248
1345
|
applyWorkItemAssigneeToggle();
|
|
1249
1346
|
applyWorkItemElementPreviewMode();
|
|
1250
|
-
|
|
1347
|
+
applySandboxChatVisibility();
|
|
1251
1348
|
applySandboxFullscreenState();
|
|
1252
1349
|
renderQuickDiaryTargetLabel();
|
|
1253
1350
|
renderSandboxOverview();
|
|
1254
1351
|
renderWorkTree();
|
|
1255
|
-
|
|
1352
|
+
applySandboxLayoutHeight();
|
|
1353
|
+
if (state.readonly) {
|
|
1354
|
+
state.chats = [];
|
|
1355
|
+
const messages = document.getElementById('sandbox-chat-messages');
|
|
1356
|
+
if (messages) messages.innerHTML = '';
|
|
1357
|
+
} else {
|
|
1358
|
+
loadSandboxChats(id);
|
|
1359
|
+
}
|
|
1256
1360
|
}
|
|
1257
1361
|
|
|
1258
1362
|
function renderSandboxOverview() {
|
|
@@ -1277,6 +1381,7 @@ function renderSandboxOverview() {
|
|
|
1277
1381
|
}
|
|
1278
1382
|
|
|
1279
1383
|
async function loadSandboxChats(sandboxId) {
|
|
1384
|
+
if (state.readonly) return;
|
|
1280
1385
|
const messages = document.getElementById('sandbox-chat-messages');
|
|
1281
1386
|
if (!messages) return;
|
|
1282
1387
|
|
|
@@ -1436,25 +1541,26 @@ function renderDiaries() {
|
|
|
1436
1541
|
openDiaryWorkItemInSandbox(sandboxId, workItemId);
|
|
1437
1542
|
},
|
|
1438
1543
|
renderContent: (content) => renderMarkdownSnippet(content),
|
|
1439
|
-
onConfirm: async (id) => {
|
|
1544
|
+
onConfirm: state.readonly ? undefined : async (id) => {
|
|
1440
1545
|
await apiRequest(`${API_BASE}/diaries/${id}/process`, {
|
|
1441
1546
|
method: 'PUT',
|
|
1442
1547
|
body: JSON.stringify({ action: 'confirm' }),
|
|
1443
1548
|
});
|
|
1444
1549
|
await loadDiaries();
|
|
1445
1550
|
},
|
|
1446
|
-
onIgnore: async (id) => {
|
|
1551
|
+
onIgnore: state.readonly ? undefined : async (id) => {
|
|
1447
1552
|
await apiRequest(`${API_BASE}/diaries/${id}/process`, {
|
|
1448
1553
|
method: 'PUT',
|
|
1449
1554
|
body: JSON.stringify({ action: 'ignore' }),
|
|
1450
1555
|
});
|
|
1451
1556
|
await loadDiaries();
|
|
1452
1557
|
},
|
|
1453
|
-
onEdit: async (id) => {
|
|
1558
|
+
onEdit: state.readonly ? undefined : async (id) => {
|
|
1454
1559
|
const diary = state.diaries.find((row) => row.id === id);
|
|
1455
1560
|
if (!diary) return;
|
|
1456
1561
|
openDiaryEditDialog(diary);
|
|
1457
1562
|
},
|
|
1563
|
+
readonly: state.readonly,
|
|
1458
1564
|
});
|
|
1459
1565
|
}
|
|
1460
1566
|
|
|
@@ -1812,10 +1918,12 @@ function editWorkItem(id) {
|
|
|
1812
1918
|
document.getElementById('item-dialog').showModal();
|
|
1813
1919
|
}
|
|
1814
1920
|
|
|
1815
|
-
function initApp() {
|
|
1816
|
-
|
|
1921
|
+
async function initApp() {
|
|
1922
|
+
await loadRuntimeMode();
|
|
1817
1923
|
loadWorkItemAssigneePreference();
|
|
1924
|
+
loadWorkTreeViewModePreference();
|
|
1818
1925
|
renderQuickDiaryTargetLabel();
|
|
1926
|
+
applyReadonlyMode();
|
|
1819
1927
|
|
|
1820
1928
|
document.querySelectorAll('.nav-list a').forEach(link => {
|
|
1821
1929
|
link.addEventListener('click', (e) => {
|
|
@@ -1825,6 +1933,7 @@ function initApp() {
|
|
|
1825
1933
|
});
|
|
1826
1934
|
|
|
1827
1935
|
document.getElementById('add-sandbox-btn')?.addEventListener('click', () => {
|
|
1936
|
+
if (state.readonly) return;
|
|
1828
1937
|
document.getElementById('sandbox-dialog').showModal();
|
|
1829
1938
|
});
|
|
1830
1939
|
|
|
@@ -1833,6 +1942,7 @@ function initApp() {
|
|
|
1833
1942
|
});
|
|
1834
1943
|
|
|
1835
1944
|
const createSandbox = async (e) => {
|
|
1945
|
+
if (state.readonly) return;
|
|
1836
1946
|
e?.preventDefault?.();
|
|
1837
1947
|
const name = document.getElementById('new-sandbox-name').value;
|
|
1838
1948
|
const description = document.getElementById('new-sandbox-desc').value;
|
|
@@ -1856,6 +1966,7 @@ function initApp() {
|
|
|
1856
1966
|
document.getElementById('confirm-sandbox-btn')?.addEventListener('click', createSandbox);
|
|
1857
1967
|
|
|
1858
1968
|
document.getElementById('add-item-btn')?.addEventListener('click', () => {
|
|
1969
|
+
if (state.readonly) return;
|
|
1859
1970
|
document.getElementById('item-dialog-title').textContent = '添加任务';
|
|
1860
1971
|
document.getElementById('item-dialog').dataset.editId = '';
|
|
1861
1972
|
document.getElementById('new-item-name').value = '';
|
|
@@ -1867,9 +1978,11 @@ function initApp() {
|
|
|
1867
1978
|
document.getElementById('item-dialog').showModal();
|
|
1868
1979
|
});
|
|
1869
1980
|
|
|
1870
|
-
document.getElementById('toggle-sandbox-
|
|
1871
|
-
state.
|
|
1872
|
-
|
|
1981
|
+
document.getElementById('toggle-sandbox-chat-btn')?.addEventListener('click', () => {
|
|
1982
|
+
if (state.readonly) return;
|
|
1983
|
+
state.sandboxChatVisible = !state.sandboxChatVisible;
|
|
1984
|
+
applySandboxChatVisibility();
|
|
1985
|
+
applySandboxLayoutHeight();
|
|
1873
1986
|
});
|
|
1874
1987
|
|
|
1875
1988
|
document.getElementById('toggle-sandbox-fullscreen-btn')?.addEventListener('click', async () => {
|
|
@@ -1890,6 +2003,7 @@ function initApp() {
|
|
|
1890
2003
|
});
|
|
1891
2004
|
|
|
1892
2005
|
document.getElementById('toggle-node-entity-form-btn')?.addEventListener('click', () => {
|
|
2006
|
+
if (state.readonly) return;
|
|
1893
2007
|
setNodeEntityFormExpanded(!state.nodeEntityFormExpanded);
|
|
1894
2008
|
if (state.nodeEntityFormExpanded) {
|
|
1895
2009
|
document.getElementById('entity-title-input')?.focus();
|
|
@@ -1918,6 +2032,7 @@ function initApp() {
|
|
|
1918
2032
|
});
|
|
1919
2033
|
|
|
1920
2034
|
document.getElementById('create-node-entity-btn')?.addEventListener('click', async () => {
|
|
2035
|
+
if (state.readonly) return;
|
|
1921
2036
|
if (!state.currentSandbox || !state.selectedNodeId) return;
|
|
1922
2037
|
const btn = document.getElementById('create-node-entity-btn');
|
|
1923
2038
|
const title = document.getElementById('entity-title-input').value.trim();
|
|
@@ -2016,6 +2131,7 @@ function initApp() {
|
|
|
2016
2131
|
});
|
|
2017
2132
|
|
|
2018
2133
|
document.getElementById('confirm-item-btn')?.addEventListener('click', async () => {
|
|
2134
|
+
if (state.readonly) return;
|
|
2019
2135
|
const dialog = document.getElementById('item-dialog');
|
|
2020
2136
|
const editId = dialog.dataset.editId || null;
|
|
2021
2137
|
const isNewItem = !editId;
|
|
@@ -2062,6 +2178,7 @@ function initApp() {
|
|
|
2062
2178
|
});
|
|
2063
2179
|
|
|
2064
2180
|
document.getElementById('rollback-btn')?.addEventListener('click', async () => {
|
|
2181
|
+
if (state.readonly) return;
|
|
2065
2182
|
if (!state.currentSandbox?.items?.length) return;
|
|
2066
2183
|
const lastItem = state.currentSandbox.items[state.currentSandbox.items.length - 1];
|
|
2067
2184
|
await apiRequest(`${API_BASE}/items/${lastItem.id}/rollback`, { method: 'POST' });
|
|
@@ -2069,6 +2186,7 @@ function initApp() {
|
|
|
2069
2186
|
});
|
|
2070
2187
|
|
|
2071
2188
|
document.getElementById('sandbox-send-btn')?.addEventListener('click', async () => {
|
|
2189
|
+
if (state.readonly) return;
|
|
2072
2190
|
const input = document.getElementById('sandbox-chat-input');
|
|
2073
2191
|
const content = input.value.trim();
|
|
2074
2192
|
if (!content || !state.currentSandbox) return;
|
|
@@ -2186,12 +2304,14 @@ function initApp() {
|
|
|
2186
2304
|
sandboxActionHandler = handleAIAction;
|
|
2187
2305
|
|
|
2188
2306
|
document.getElementById('sandbox-chat-input')?.addEventListener('keypress', (e) => {
|
|
2307
|
+
if (state.readonly) return;
|
|
2189
2308
|
if (e.key === 'Enter') {
|
|
2190
2309
|
document.getElementById('sandbox-send-btn').click();
|
|
2191
2310
|
}
|
|
2192
2311
|
});
|
|
2193
2312
|
|
|
2194
2313
|
document.getElementById('save-diary-btn')?.addEventListener('click', async () => {
|
|
2314
|
+
if (state.readonly) return;
|
|
2195
2315
|
const content = document.getElementById('diary-content').value.trim();
|
|
2196
2316
|
if (!content) return;
|
|
2197
2317
|
await saveDiaryEntry({ content, sandboxId: null, workItemId: null });
|
|
@@ -2200,6 +2320,7 @@ function initApp() {
|
|
|
2200
2320
|
});
|
|
2201
2321
|
|
|
2202
2322
|
document.getElementById('drawer-diary-save-btn')?.addEventListener('click', async () => {
|
|
2323
|
+
if (state.readonly) return;
|
|
2203
2324
|
if (!state.currentSandbox || !state.selectedNodeId) return;
|
|
2204
2325
|
const textarea = document.getElementById('drawer-diary-content');
|
|
2205
2326
|
if (!(textarea instanceof HTMLTextAreaElement)) return;
|
|
@@ -2222,9 +2343,11 @@ function initApp() {
|
|
|
2222
2343
|
closeDiaryEditDialog();
|
|
2223
2344
|
});
|
|
2224
2345
|
document.getElementById('confirm-edit-diary-btn')?.addEventListener('click', async () => {
|
|
2346
|
+
if (state.readonly) return;
|
|
2225
2347
|
await submitDiaryEditDialog();
|
|
2226
2348
|
});
|
|
2227
2349
|
document.getElementById('diary-edit-content')?.addEventListener('keydown', async (event) => {
|
|
2350
|
+
if (state.readonly) return;
|
|
2228
2351
|
const isSubmit = event.key === 'Enter' && (event.metaKey || event.ctrlKey);
|
|
2229
2352
|
if (!isSubmit) return;
|
|
2230
2353
|
event.preventDefault();
|
|
@@ -2241,6 +2364,7 @@ function initApp() {
|
|
|
2241
2364
|
});
|
|
2242
2365
|
|
|
2243
2366
|
document.getElementById('save-settings-btn')?.addEventListener('click', async () => {
|
|
2367
|
+
if (state.readonly) return;
|
|
2244
2368
|
const api_url = document.getElementById('setting-api-url').value;
|
|
2245
2369
|
const api_key = document.getElementById('setting-api-key').value;
|
|
2246
2370
|
const model = document.getElementById('setting-model').value;
|
|
@@ -2270,6 +2394,7 @@ function initApp() {
|
|
|
2270
2394
|
});
|
|
2271
2395
|
|
|
2272
2396
|
document.getElementById('add-setting-header-btn')?.addEventListener('click', () => {
|
|
2397
|
+
if (state.readonly) return;
|
|
2273
2398
|
const list = document.getElementById('setting-headers-list');
|
|
2274
2399
|
if (!(list instanceof HTMLElement)) return;
|
|
2275
2400
|
list.appendChild(createSettingHeaderRow('', ''));
|
|
@@ -2288,6 +2413,7 @@ function initApp() {
|
|
|
2288
2413
|
document.getElementById('work-tree-view-mode')?.addEventListener('change', (e) => {
|
|
2289
2414
|
const nextMode = e.target.value || 'full';
|
|
2290
2415
|
applyWorkTreeViewMode(nextMode);
|
|
2416
|
+
persistWorkTreeViewModePreference();
|
|
2291
2417
|
renderWorkTree();
|
|
2292
2418
|
});
|
|
2293
2419
|
|
|
@@ -2411,12 +2537,13 @@ function initApp() {
|
|
|
2411
2537
|
clearTimeout(resizeRenderTimer);
|
|
2412
2538
|
}
|
|
2413
2539
|
resizeRenderTimer = setTimeout(() => {
|
|
2540
|
+
applySandboxLayoutHeight();
|
|
2414
2541
|
renderWorkTree();
|
|
2415
2542
|
}, 120);
|
|
2416
2543
|
});
|
|
2417
2544
|
|
|
2418
2545
|
window.addEventListener('hashchange', handleRoute);
|
|
2419
|
-
handleRoute();
|
|
2546
|
+
await handleRoute();
|
|
2420
2547
|
|
|
2421
2548
|
async function handleRoute() {
|
|
2422
2549
|
const hash = window.location.hash.slice(1) || '/';
|
|
@@ -2452,6 +2579,15 @@ function initApp() {
|
|
|
2452
2579
|
await loadSandboxes();
|
|
2453
2580
|
await loadChanges();
|
|
2454
2581
|
} else if (pathHash === '/settings') {
|
|
2582
|
+
if (state.readonly) {
|
|
2583
|
+
if (window.location.hash !== '#/sandboxes') {
|
|
2584
|
+
window.location.hash = '/sandboxes';
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
showPage('sandboxes');
|
|
2588
|
+
await loadSandboxes();
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2455
2591
|
showPage('settings');
|
|
2456
2592
|
await loadSettings();
|
|
2457
2593
|
}
|
package/dist/web/index.html
CHANGED
|
@@ -39,7 +39,10 @@
|
|
|
39
39
|
<div class="action-strip">
|
|
40
40
|
<button class="btn btn-secondary btn-sm" id="generate-insight-btn">智能总结</button>
|
|
41
41
|
<button class="btn btn-secondary btn-sm" id="generate-report-btn">生成汇报</button>
|
|
42
|
-
<button class="btn btn-secondary btn-sm" id="toggle-sandbox-
|
|
42
|
+
<button class="btn btn-secondary btn-sm ai-toggle-btn" id="toggle-sandbox-chat-btn" type="button">
|
|
43
|
+
<span class="icon" aria-hidden="true">🤖</span>
|
|
44
|
+
<span>显示 AI 助手</span>
|
|
45
|
+
</button>
|
|
43
46
|
<button class="btn btn-secondary btn-sm" id="toggle-sandbox-fullscreen-btn" type="button">全屏</button>
|
|
44
47
|
</div>
|
|
45
48
|
<div class="insight-box hidden" id="sandbox-insight-output" hidden>
|