@qnote/q-ai-note 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +41 -0
  2. package/dist/cli.js +55 -18
  3. package/dist/cli.js.map +1 -1
  4. package/dist/server/accessControl.d.ts +29 -0
  5. package/dist/server/accessControl.d.ts.map +1 -0
  6. package/dist/server/accessControl.js +161 -0
  7. package/dist/server/accessControl.js.map +1 -0
  8. package/dist/server/api/accessHelpers.d.ts +11 -0
  9. package/dist/server/api/accessHelpers.d.ts.map +1 -0
  10. package/dist/server/api/accessHelpers.js +45 -0
  11. package/dist/server/api/accessHelpers.js.map +1 -0
  12. package/dist/server/api/chat.d.ts.map +1 -1
  13. package/dist/server/api/chat.js +31 -0
  14. package/dist/server/api/chat.js.map +1 -1
  15. package/dist/server/api/diary.d.ts.map +1 -1
  16. package/dist/server/api/diary.js +61 -1
  17. package/dist/server/api/diary.js.map +1 -1
  18. package/dist/server/api/nodeEntities.d.ts.map +1 -1
  19. package/dist/server/api/nodeEntities.js +31 -0
  20. package/dist/server/api/nodeEntities.js.map +1 -1
  21. package/dist/server/api/projectSettings.d.ts +3 -0
  22. package/dist/server/api/projectSettings.d.ts.map +1 -0
  23. package/dist/server/api/projectSettings.js +29 -0
  24. package/dist/server/api/projectSettings.js.map +1 -0
  25. package/dist/server/api/sandbox.d.ts.map +1 -1
  26. package/dist/server/api/sandbox.js +35 -1
  27. package/dist/server/api/sandbox.js.map +1 -1
  28. package/dist/server/api/settings.d.ts.map +1 -1
  29. package/dist/server/api/settings.js +25 -1
  30. package/dist/server/api/settings.js.map +1 -1
  31. package/dist/server/api/workItem.d.ts.map +1 -1
  32. package/dist/server/api/workItem.js +59 -0
  33. package/dist/server/api/workItem.js.map +1 -1
  34. package/dist/server/config.d.ts +3 -2
  35. package/dist/server/config.d.ts.map +1 -1
  36. package/dist/server/config.js +6 -1
  37. package/dist/server/config.js.map +1 -1
  38. package/dist/server/index.d.ts +8 -2
  39. package/dist/server/index.d.ts.map +1 -1
  40. package/dist/server/index.js +102 -4
  41. package/dist/server/index.js.map +1 -1
  42. package/dist/server/projectConfig.d.ts +37 -0
  43. package/dist/server/projectConfig.d.ts.map +1 -0
  44. package/dist/server/projectConfig.js +180 -0
  45. package/dist/server/projectConfig.js.map +1 -0
  46. package/dist/web/app.js +760 -44
  47. package/dist/web/index.html +107 -60
  48. package/dist/web/styles.css +256 -11
  49. package/dist/web/vueRenderers.js +71 -57
  50. package/package.json +2 -2
@@ -14,7 +14,7 @@ function byId(targetId) {
14
14
  export function mountSandboxGrid(targetId, options) {
15
15
  const container = byId(targetId);
16
16
  if (!container) return;
17
- const { sandboxes = [], emptyText = '暂无数据', onOpen, onDelete } = options || {};
17
+ const { sandboxes = [], emptyText = '暂无数据', onOpen, onDelete, readonly = false } = options || {};
18
18
 
19
19
  if (!sandboxes.length) {
20
20
  container.innerHTML = `<div class="empty-state"><p>${esc(emptyText)}</p></div>`;
@@ -28,7 +28,7 @@ export function mountSandboxGrid(targetId, options) {
28
28
  <div class="sandbox-meta">
29
29
  <span>${esc(new Date(sandbox.updated_at).toLocaleDateString())}</span>
30
30
  </div>
31
- <button class="btn btn-secondary btn-sm sandbox-delete" data-delete-id="${esc(sandbox.id)}">删除</button>
31
+ ${readonly ? '' : `<button class="btn btn-secondary btn-sm sandbox-delete" data-delete-id="${esc(sandbox.id)}">删除</button>`}
32
32
  </div>
33
33
  `).join('');
34
34
 
@@ -60,6 +60,7 @@ export function mountDiaryTimeline(targetId, options) {
60
60
  onIgnore,
61
61
  onEdit,
62
62
  onOpenWorkItem,
63
+ readonly = false,
63
64
  renderContent,
64
65
  } = options || {};
65
66
 
@@ -84,13 +85,15 @@ export function mountDiaryTimeline(targetId, options) {
84
85
  >${esc(getWorkItemName?.(diary.sandbox_id, diary.work_item_id) || diary.work_item_id)}</button>
85
86
  ` : ''}
86
87
  </div>
87
- <div class="diary-actions">
88
- <button class="edit" data-edit-id="${esc(diary.id)}">编辑</button>
89
- ${diary.processed ? '' : `
90
- <button class="confirm" data-confirm-id="${esc(diary.id)}">采纳</button>
91
- <button class="ignore" data-ignore-id="${esc(diary.id)}">忽略</button>
92
- `}
93
- </div>
88
+ ${readonly ? '' : `
89
+ <div class="diary-actions">
90
+ <button class="edit" data-edit-id="${esc(diary.id)}">编辑</button>
91
+ ${diary.processed ? '' : `
92
+ <button class="confirm" data-confirm-id="${esc(diary.id)}">采纳</button>
93
+ <button class="ignore" data-ignore-id="${esc(diary.id)}">忽略</button>
94
+ `}
95
+ </div>
96
+ `}
94
97
  </div>
95
98
  <div class="diary-content">${typeof renderContent === 'function' ? renderContent(diary.content) : esc(diary.content)}</div>
96
99
  </div>
@@ -173,28 +176,30 @@ function renderEntityPreviewBoxes(nodeId, entityRowsByNodeId, mode) {
173
176
  `;
174
177
  }
175
178
 
176
- function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none') {
179
+ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none', readonly = false, selectedId = '') {
177
180
  const children = byParent.get(node.id) || [];
178
181
  const hasChildren = children.length > 0;
179
182
  const isExpanded = expandedIdSet.has(node.id);
180
183
  const isShort = String(node.name || '').trim().length <= 10;
181
184
  const nodeSummary = entitySummaryByNodeId?.[node.id] || { issue: 0, knowledge: 0, capability: 0 };
182
185
  const previewHtml = renderEntityPreviewBoxes(node.id, entityRowsByNodeId, elementPreviewMode);
186
+ const isSelected = String(selectedId || '') === String(node.id || '');
183
187
 
184
188
  if (!hasChildren) {
185
189
  return `
186
- <div class="tree-leaf-node" data-id="${esc(node.id)}" data-select-id="${esc(node.id)}" tabindex="0">
187
- <span class="node-status ${esc(node.status)}"></span>
190
+ <div class="tree-leaf-node ${isSelected ? 'is-selected' : ''}" data-id="${esc(node.id)}" data-select-id="${esc(node.id)}" tabindex="0">
188
191
  <span class="node-name ${isShort ? 'short-name' : ''}" data-select-id="${esc(node.id)}">${esc(node.name)}</span>
189
192
  ${renderEntityBadges(nodeSummary)}
190
193
  ${previewHtml}
191
- <div class="node-actions">
192
- <button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(node.id)}" title="快捷提问">💬</button>
193
- <button class="node-action-btn" data-action="add-diary" data-id="${esc(node.id)}" title="记录日记">📝</button>
194
- <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
195
- <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
196
- <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
197
- </div>
194
+ ${readonly ? '' : `
195
+ <div class="node-actions">
196
+ <button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(node.id)}" title="快捷提问">💬</button>
197
+ <button class="node-action-btn" data-action="add-diary" data-id="${esc(node.id)}" title="记录日记">📝</button>
198
+ <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
199
+ <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
200
+ <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
201
+ </div>
202
+ `}
198
203
  </div>
199
204
  `;
200
205
  }
@@ -204,7 +209,7 @@ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, sh
204
209
 
205
210
  return `
206
211
  <div class="tree-parent-card ${isExpanded ? 'expanded' : 'collapsed'}">
207
- <div class="tree-parent-header" data-select-id="${esc(node.id)}" tabindex="0" aria-expanded="${isExpanded ? 'true' : 'false'}">
212
+ <div class="tree-parent-header ${isSelected ? 'is-selected' : ''}" data-select-id="${esc(node.id)}" tabindex="0" aria-expanded="${isExpanded ? 'true' : 'false'}">
208
213
  <button
209
214
  class="node-expand-btn ${isExpanded ? 'expanded' : ''}"
210
215
  data-action="toggle"
@@ -215,22 +220,23 @@ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, sh
215
220
  >
216
221
  <svg viewBox="0 0 24 24" fill="currentColor"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg>
217
222
  </button>
218
- <span class="node-status ${esc(node.status)}"></span>
219
223
  <span class="node-name">${esc(node.name)}</span>
220
224
  ${renderEntityBadges(nodeSummary)}
221
225
  ${showAssignee && node.assignee ? `<span class="node-meta">@${esc(node.assignee)}</span>` : ''}
222
- <div class="node-actions">
223
- <button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(node.id)}" title="快捷提问">💬</button>
224
- <button class="node-action-btn" data-action="add-diary" data-id="${esc(node.id)}" title="记录日记">📝</button>
225
- <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
226
- <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
227
- <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
228
- </div>
226
+ ${readonly ? '' : `
227
+ <div class="node-actions">
228
+ <button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(node.id)}" title="快捷提问">💬</button>
229
+ <button class="node-action-btn" data-action="add-diary" data-id="${esc(node.id)}" title="记录日记">📝</button>
230
+ <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
231
+ <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
232
+ <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
233
+ </div>
234
+ `}
229
235
  </div>
230
236
  ${isExpanded ? `
231
237
  ${previewHtml ? `<div class="tree-node-preview-wrapper">${previewHtml}</div>` : ''}
232
238
  <div class="tree-parent-children">
233
- ${branchChildren.map((child) => renderTreeNode(child, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode)).join('')}
239
+ ${branchChildren.map((child) => renderTreeNode(child, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId)).join('')}
234
240
  ${leafChildren.length ? `
235
241
  <div class="tree-leaf-container">
236
242
  <div class="tree-leaf-grid">
@@ -239,18 +245,19 @@ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, sh
239
245
  const childSummary = entitySummaryByNodeId?.[child.id] || { issue: 0, knowledge: 0, capability: 0 };
240
246
  const childPreviewHtml = renderEntityPreviewBoxes(child.id, entityRowsByNodeId, elementPreviewMode);
241
247
  return `
242
- <div class="tree-leaf-node" data-id="${esc(child.id)}" data-select-id="${esc(child.id)}" tabindex="0">
243
- <span class="node-status ${esc(child.status)}"></span>
248
+ <div class="tree-leaf-node ${String(selectedId || '') === String(child.id || '') ? 'is-selected' : ''}" data-id="${esc(child.id)}" data-select-id="${esc(child.id)}" tabindex="0">
244
249
  <span class="node-name ${shortName ? 'short-name' : ''}" data-select-id="${esc(child.id)}">${esc(child.name)}</span>
245
250
  ${renderEntityBadges(childSummary)}
246
251
  ${childPreviewHtml}
247
- <div class="node-actions">
248
- <button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(child.id)}" title="快捷提问">💬</button>
249
- <button class="node-action-btn" data-action="add-diary" data-id="${esc(child.id)}" title="记录日记">📝</button>
250
- <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(child.id)}" title="添加子任务">+</button>
251
- <button class="node-action-btn" data-action="edit" data-id="${esc(child.id)}" title="编辑">✎</button>
252
- <button class="node-action-btn delete" data-action="delete" data-id="${esc(child.id)}" title="删除">✕</button>
253
- </div>
252
+ ${readonly ? '' : `
253
+ <div class="node-actions">
254
+ <button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(child.id)}" title="快捷提问">💬</button>
255
+ <button class="node-action-btn" data-action="add-diary" data-id="${esc(child.id)}" title="记录日记">📝</button>
256
+ <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(child.id)}" title="添加子任务">+</button>
257
+ <button class="node-action-btn" data-action="edit" data-id="${esc(child.id)}" title="编辑">✎</button>
258
+ <button class="node-action-btn delete" data-action="delete" data-id="${esc(child.id)}" title="删除">✕</button>
259
+ </div>
260
+ `}
254
261
  </div>
255
262
  `;
256
263
  }).join('')}
@@ -306,19 +313,22 @@ function applyAdaptiveRootMixedGridLayout(container) {
306
313
  });
307
314
  }
308
315
 
309
- function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeId, depth = 0, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none') {
316
+ function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeId, depth = 0, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none', readonly = false, selectedId = '') {
310
317
  const children = byParent.get(node.id) || [];
311
318
  const hasChildren = children.length > 0;
312
319
  const isExpanded = expandedIdSet.has(node.id);
313
320
  const nodeSummary = entitySummaryByNodeId?.[node.id] || { issue: 0, knowledge: 0, capability: 0 };
314
321
  const useStackSummary = depth >= 3;
322
+ const summaryBadgeHtml = renderEntityBadges(nodeSummary);
323
+ const summaryAssigneeHtml = showAssignee && node.assignee ? `<span class="node-meta">@${esc(node.assignee)}</span>` : '';
315
324
  const previewHtml = renderEntityPreviewBoxes(node.id, entityRowsByNodeId, elementPreviewMode);
316
325
  const childrenHtml = hasChildren && isExpanded
317
- ? `<div class="lane-tree-children">${children.map((child) => renderDenseLaneNode(child, byParent, expandedIdSet, entitySummaryByNodeId, depth + 1, showAssignee, entityRowsByNodeId, elementPreviewMode)).join('')}</div>`
326
+ ? `<div class="lane-tree-children">${children.map((child) => renderDenseLaneNode(child, byParent, expandedIdSet, entitySummaryByNodeId, depth + 1, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId)).join('')}</div>`
318
327
  : '';
328
+ const isSelected = String(selectedId || '') === String(node.id || '');
319
329
  return `
320
330
  <div class="lane-tree-node" data-depth="${depth}">
321
- <div class="lane-tree-node-row" data-select-id="${esc(node.id)}" data-node-id="${esc(node.id)}" tabindex="0" draggable="true">
331
+ <div class="lane-tree-node-row ${isSelected ? 'is-selected' : ''}" data-select-id="${esc(node.id)}" data-node-id="${esc(node.id)}" tabindex="0" draggable="${readonly ? 'false' : 'true'}">
322
332
  ${hasChildren ? `
323
333
  <button
324
334
  class="node-expand-btn dense-expand-btn ${isExpanded ? 'expanded' : ''}"
@@ -335,22 +345,24 @@ function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeI
335
345
  <div class="lane-tree-node-main ${useStackSummary ? 'stack-summary' : ''}">
336
346
  <div class="lane-tree-title-line">
337
347
  <span class="dense-node-name">${esc(node.name)}</span>
338
- ${!useStackSummary ? renderEntityBadges(nodeSummary) : ''}
339
- ${!useStackSummary && showAssignee && node.assignee ? `<span class="node-meta">@${esc(node.assignee)}</span>` : ''}
348
+ ${!useStackSummary ? summaryBadgeHtml : ''}
349
+ ${!useStackSummary ? summaryAssigneeHtml : ''}
340
350
  </div>
341
- ${useStackSummary ? `
351
+ ${useStackSummary && (summaryBadgeHtml || summaryAssigneeHtml) ? `
342
352
  <div class="lane-tree-summary-line">
343
- ${renderEntityBadges(nodeSummary)}
344
- ${showAssignee && node.assignee ? `<span class="node-meta">@${esc(node.assignee)}</span>` : ''}
353
+ ${summaryBadgeHtml}
354
+ ${summaryAssigneeHtml}
345
355
  </div>
346
356
  ` : ''}
347
357
  </div>
348
- <div class="node-actions">
349
- <button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(node.id)}" title="快捷提问">💬</button>
350
- <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
351
- <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
352
- <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
353
- </div>
358
+ ${readonly ? '' : `
359
+ <div class="node-actions">
360
+ <button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(node.id)}" title="快捷提问">💬</button>
361
+ <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
362
+ <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
363
+ <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
364
+ </div>
365
+ `}
354
366
  </div>
355
367
  ${previewHtml ? `<div class="lane-node-preview-wrapper">${previewHtml}</div>` : ''}
356
368
  ${childrenHtml}
@@ -392,7 +404,7 @@ function getDenseLaneWidthPx(root, byParent, showAssignee = false) {
392
404
  return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, preferred));
393
405
  }
394
406
 
395
- function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none') {
407
+ function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none', readonly = false, selectedId = '') {
396
408
  return `
397
409
  <div class="dense-tree dense-horizontal dense-lane-board">
398
410
  ${roots.map((root) => {
@@ -400,9 +412,9 @@ function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId,
400
412
  const laneNameMax = Math.max(120, laneWidth - 86);
401
413
  return `
402
414
  <section class="dense-lane" data-root-id="${esc(root.id)}" style="--lane-width:${laneWidth}px;--lane-name-max:${laneNameMax}px;">
403
- <div class="dense-lane-drag-handle" data-lane-drag-id="${esc(root.id)}" draggable="true" title="拖拽调整泳道顺序">⋮⋮</div>
415
+ ${readonly ? '' : `<div class="dense-lane-drag-handle" data-lane-drag-id="${esc(root.id)}" draggable="true" title="拖拽调整泳道顺序">⋮⋮</div>`}
404
416
  <div class="dense-lane-body">
405
- ${renderDenseLaneNode(root, byParent, expandedIdSet, entitySummaryByNodeId, 0, showAssignee, entityRowsByNodeId, elementPreviewMode)}
417
+ ${renderDenseLaneNode(root, byParent, expandedIdSet, entitySummaryByNodeId, 0, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId)}
406
418
  </div>
407
419
  </section>
408
420
  `;
@@ -433,6 +445,8 @@ export function mountWorkTree(targetId, options) {
433
445
  elementPreviewMode = 'none',
434
446
  renderMode = 'card',
435
447
  showAssignee = false,
448
+ readonly = false,
449
+ selectedId = '',
436
450
  } = options || {};
437
451
 
438
452
  const byParent = new Map();
@@ -476,13 +490,13 @@ export function mountWorkTree(targetId, options) {
476
490
  return;
477
491
  }
478
492
  if (renderMode === 'dense') {
479
- container.innerHTML = renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode);
493
+ container.innerHTML = renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId);
480
494
  } else {
481
495
  const htmlParts = roots.map((root) => {
482
496
  const hasChildren = ((byParent.get(root.id) || []).length > 0);
483
497
  return `
484
498
  <div class="root-grid-item ${hasChildren ? 'branch' : 'leaf'}" data-root-item-type="${hasChildren ? 'branch' : 'leaf'}">
485
- ${renderTreeNode(root, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode)}
499
+ ${renderTreeNode(root, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId)}
486
500
  </div>
487
501
  `;
488
502
  });
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@qnote/q-ai-note",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "description": "AI-assisted personal work sandbox and diary system",
6
6
  "main": "dist/server/index.js",
7
7
  "bin": {
8
8
  "q-ai-note": "dist/cli.js",
9
- "q-ai-note-server": "dist/cli-server.js"
9
+ "q-ai-note-server": "dist/cli.js"
10
10
  },
11
11
  "files": [
12
12
  "dist/**/*",