@qnote/q-ai-note 1.0.3 → 1.0.5
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 +15 -0
- package/dist/cli-server.d.ts +3 -0
- package/dist/cli-server.d.ts.map +1 -0
- package/dist/cli-server.js +79 -0
- package/dist/cli-server.js.map +1 -0
- package/dist/cli.js +14 -2
- package/dist/cli.js.map +1 -1
- package/dist/server/aiClient.d.ts.map +1 -1
- package/dist/server/aiClient.js +7 -0
- package/dist/server/aiClient.js.map +1 -1
- package/dist/server/api/chat.d.ts.map +1 -1
- package/dist/server/api/chat.js +6 -112
- package/dist/server/api/chat.js.map +1 -1
- package/dist/server/api/diary.d.ts.map +1 -1
- package/dist/server/api/diary.js +34 -0
- package/dist/server/api/diary.js.map +1 -1
- package/dist/server/api/settings.d.ts.map +1 -1
- package/dist/server/api/settings.js +16 -1
- package/dist/server/api/settings.js.map +1 -1
- package/dist/server/config.d.ts +3 -2
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +6 -1
- package/dist/server/config.js.map +1 -1
- package/dist/server/db.d.ts.map +1 -1
- package/dist/server/db.js +8 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +5 -3
- package/dist/server/index.js.map +1 -1
- package/dist/web/app.js +714 -98
- package/dist/web/index.html +77 -22
- package/dist/web/styles.css +386 -6
- package/dist/web/vueRenderers.js +249 -23
- package/package.json +5 -3
package/dist/web/vueRenderers.js
CHANGED
|
@@ -52,7 +52,16 @@ export function mountHtmlList(targetId, htmlItems) {
|
|
|
52
52
|
export function mountDiaryTimeline(targetId, options) {
|
|
53
53
|
const container = byId(targetId);
|
|
54
54
|
if (!container) return;
|
|
55
|
-
const {
|
|
55
|
+
const {
|
|
56
|
+
diaries = [],
|
|
57
|
+
getSandboxName,
|
|
58
|
+
getWorkItemName,
|
|
59
|
+
onConfirm,
|
|
60
|
+
onIgnore,
|
|
61
|
+
onEdit,
|
|
62
|
+
onOpenWorkItem,
|
|
63
|
+
renderContent,
|
|
64
|
+
} = options || {};
|
|
56
65
|
|
|
57
66
|
if (!diaries.length) {
|
|
58
67
|
container.innerHTML = '<div class="empty-state"><p>暂无日记</p></div>';
|
|
@@ -61,17 +70,29 @@ export function mountDiaryTimeline(targetId, options) {
|
|
|
61
70
|
|
|
62
71
|
container.innerHTML = diaries.map((diary) => `
|
|
63
72
|
<div class="diary-item ${diary.processed ? 'processed' : ''}">
|
|
64
|
-
<div class="diary-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
<div class="diary-head-row">
|
|
74
|
+
<div class="diary-meta">
|
|
75
|
+
${esc(new Date(diary.created_at).toLocaleString())}
|
|
76
|
+
${diary.sandbox_id ? ` · ${esc(getSandboxName?.(diary.sandbox_id) || diary.sandbox_id)}` : ''}
|
|
77
|
+
${diary.work_item_id ? `
|
|
78
|
+
· 节点:
|
|
79
|
+
<button
|
|
80
|
+
class="diary-open-node-link"
|
|
81
|
+
type="button"
|
|
82
|
+
data-open-work-item="${esc(diary.work_item_id)}"
|
|
83
|
+
data-open-sandbox="${esc(diary.sandbox_id || '')}"
|
|
84
|
+
>${esc(getWorkItemName?.(diary.sandbox_id, diary.work_item_id) || diary.work_item_id)}</button>
|
|
85
|
+
` : ''}
|
|
86
|
+
</div>
|
|
70
87
|
<div class="diary-actions">
|
|
71
|
-
<button class="
|
|
72
|
-
|
|
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
|
+
`}
|
|
73
93
|
</div>
|
|
74
|
-
|
|
94
|
+
</div>
|
|
95
|
+
<div class="diary-content">${typeof renderContent === 'function' ? renderContent(diary.content) : esc(diary.content)}</div>
|
|
75
96
|
</div>
|
|
76
97
|
`).join('');
|
|
77
98
|
|
|
@@ -81,15 +102,28 @@ export function mountDiaryTimeline(targetId, options) {
|
|
|
81
102
|
container.querySelectorAll('[data-ignore-id]').forEach((el) => {
|
|
82
103
|
el.addEventListener('click', () => onIgnore?.(el.getAttribute('data-ignore-id')));
|
|
83
104
|
});
|
|
105
|
+
container.querySelectorAll('[data-edit-id]').forEach((el) => {
|
|
106
|
+
el.addEventListener('click', () => onEdit?.(el.getAttribute('data-edit-id')));
|
|
107
|
+
});
|
|
108
|
+
container.querySelectorAll('[data-open-work-item]').forEach((el) => {
|
|
109
|
+
el.addEventListener('click', () => {
|
|
110
|
+
const workItemId = el.getAttribute('data-open-work-item');
|
|
111
|
+
const sandboxId = el.getAttribute('data-open-sandbox');
|
|
112
|
+
if (!workItemId || !sandboxId) return;
|
|
113
|
+
onOpenWorkItem?.(sandboxId, workItemId);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
84
116
|
}
|
|
85
117
|
|
|
86
118
|
function renderEntityBadges(summary) {
|
|
87
119
|
const issue = Number(summary?.issue || 0);
|
|
88
120
|
const knowledge = Number(summary?.knowledge || 0);
|
|
89
121
|
const capability = Number(summary?.capability || 0);
|
|
122
|
+
if (issue + knowledge + capability === 0) {
|
|
123
|
+
return '';
|
|
124
|
+
}
|
|
90
125
|
const issueOpen = Number(summary?.issue_open || 0);
|
|
91
126
|
const issueClosed = Number(summary?.issue_closed || 0);
|
|
92
|
-
const isBlindSpot = issue + knowledge + capability === 0;
|
|
93
127
|
const issueStateClass = issue === 0
|
|
94
128
|
? 'issue-none'
|
|
95
129
|
: issueOpen > 0
|
|
@@ -99,20 +133,53 @@ function renderEntityBadges(summary) {
|
|
|
99
133
|
: 'issue-none';
|
|
100
134
|
return `
|
|
101
135
|
<span
|
|
102
|
-
class="node-entity-mini-badges ${
|
|
136
|
+
class="node-entity-mini-badges ${issueStateClass}"
|
|
103
137
|
title="节点摘要:${issue}/${knowledge}/${capability}(Issue/Knowledge/Capability)"
|
|
104
138
|
>
|
|
105
|
-
<span class="node-entity-mini-badge
|
|
139
|
+
<span class="node-entity-mini-badge active">${issue}/${knowledge}/${capability}</span>
|
|
106
140
|
</span>
|
|
107
141
|
`;
|
|
108
142
|
}
|
|
109
143
|
|
|
110
|
-
function
|
|
144
|
+
function pickEntityPreviewRows(nodeId, entityRowsByNodeId, mode) {
|
|
145
|
+
if (!entityRowsByNodeId || mode === 'none') return [];
|
|
146
|
+
const rows = entityRowsByNodeId[nodeId] || [];
|
|
147
|
+
if (mode === 'all') return rows;
|
|
148
|
+
return rows.filter((row) => row.entity_type === mode);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function renderEntityPreviewBoxes(nodeId, entityRowsByNodeId, mode) {
|
|
152
|
+
const rows = pickEntityPreviewRows(nodeId, entityRowsByNodeId, mode);
|
|
153
|
+
if (!rows.length) return '';
|
|
154
|
+
const shownRows = rows.slice(0, 4);
|
|
155
|
+
return `
|
|
156
|
+
<div class="node-entity-preview-strip" data-node-id="${esc(nodeId)}">
|
|
157
|
+
${shownRows.map((row) => `
|
|
158
|
+
<button
|
|
159
|
+
class="entity-preview-box ${esc(row.entity_type)}"
|
|
160
|
+
type="button"
|
|
161
|
+
data-action="entity-preview"
|
|
162
|
+
data-node-id="${esc(nodeId)}"
|
|
163
|
+
data-entity-type="${esc(row.entity_type)}"
|
|
164
|
+
data-entity-id="${esc(row.id)}"
|
|
165
|
+
title="${esc(row.title || row.entity_type)}"
|
|
166
|
+
>
|
|
167
|
+
<span class="entity-preview-type">${esc(row.entity_type)}</span>
|
|
168
|
+
<span class="entity-preview-title">${esc(row.title || '-')}</span>
|
|
169
|
+
</button>
|
|
170
|
+
`).join('')}
|
|
171
|
+
${rows.length > shownRows.length ? `<span class="entity-preview-more">+${rows.length - shownRows.length}</span>` : ''}
|
|
172
|
+
</div>
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none') {
|
|
111
177
|
const children = byParent.get(node.id) || [];
|
|
112
178
|
const hasChildren = children.length > 0;
|
|
113
179
|
const isExpanded = expandedIdSet.has(node.id);
|
|
114
180
|
const isShort = String(node.name || '').trim().length <= 10;
|
|
115
181
|
const nodeSummary = entitySummaryByNodeId?.[node.id] || { issue: 0, knowledge: 0, capability: 0 };
|
|
182
|
+
const previewHtml = renderEntityPreviewBoxes(node.id, entityRowsByNodeId, elementPreviewMode);
|
|
116
183
|
|
|
117
184
|
if (!hasChildren) {
|
|
118
185
|
return `
|
|
@@ -120,8 +187,10 @@ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, sh
|
|
|
120
187
|
<span class="node-status ${esc(node.status)}"></span>
|
|
121
188
|
<span class="node-name ${isShort ? 'short-name' : ''}" data-select-id="${esc(node.id)}">${esc(node.name)}</span>
|
|
122
189
|
${renderEntityBadges(nodeSummary)}
|
|
190
|
+
${previewHtml}
|
|
123
191
|
<div class="node-actions">
|
|
124
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>
|
|
125
194
|
<button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
|
|
126
195
|
<button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
|
|
127
196
|
<button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
|
|
@@ -152,27 +221,32 @@ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, sh
|
|
|
152
221
|
${showAssignee && node.assignee ? `<span class="node-meta">@${esc(node.assignee)}</span>` : ''}
|
|
153
222
|
<div class="node-actions">
|
|
154
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>
|
|
155
225
|
<button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
|
|
156
226
|
<button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
|
|
157
227
|
<button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
|
|
158
228
|
</div>
|
|
159
229
|
</div>
|
|
160
230
|
${isExpanded ? `
|
|
231
|
+
${previewHtml ? `<div class="tree-node-preview-wrapper">${previewHtml}</div>` : ''}
|
|
161
232
|
<div class="tree-parent-children">
|
|
162
|
-
${branchChildren.map((child) => renderTreeNode(child, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee)).join('')}
|
|
233
|
+
${branchChildren.map((child) => renderTreeNode(child, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode)).join('')}
|
|
163
234
|
${leafChildren.length ? `
|
|
164
235
|
<div class="tree-leaf-container">
|
|
165
236
|
<div class="tree-leaf-grid">
|
|
166
237
|
${leafChildren.map((child) => {
|
|
167
238
|
const shortName = String(child.name || '').trim().length <= 10;
|
|
168
239
|
const childSummary = entitySummaryByNodeId?.[child.id] || { issue: 0, knowledge: 0, capability: 0 };
|
|
240
|
+
const childPreviewHtml = renderEntityPreviewBoxes(child.id, entityRowsByNodeId, elementPreviewMode);
|
|
169
241
|
return `
|
|
170
242
|
<div class="tree-leaf-node" data-id="${esc(child.id)}" data-select-id="${esc(child.id)}" tabindex="0">
|
|
171
243
|
<span class="node-status ${esc(child.status)}"></span>
|
|
172
244
|
<span class="node-name ${shortName ? 'short-name' : ''}" data-select-id="${esc(child.id)}">${esc(child.name)}</span>
|
|
173
245
|
${renderEntityBadges(childSummary)}
|
|
246
|
+
${childPreviewHtml}
|
|
174
247
|
<div class="node-actions">
|
|
175
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>
|
|
176
250
|
<button class="node-action-btn add-child" data-action="add-child" data-id="${esc(child.id)}" title="添加子任务">+</button>
|
|
177
251
|
<button class="node-action-btn" data-action="edit" data-id="${esc(child.id)}" title="编辑">✎</button>
|
|
178
252
|
<button class="node-action-btn delete" data-action="delete" data-id="${esc(child.id)}" title="删除">✕</button>
|
|
@@ -232,18 +306,19 @@ function applyAdaptiveRootMixedGridLayout(container) {
|
|
|
232
306
|
});
|
|
233
307
|
}
|
|
234
308
|
|
|
235
|
-
function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeId, depth = 0, showAssignee = false) {
|
|
309
|
+
function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeId, depth = 0, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none') {
|
|
236
310
|
const children = byParent.get(node.id) || [];
|
|
237
311
|
const hasChildren = children.length > 0;
|
|
238
312
|
const isExpanded = expandedIdSet.has(node.id);
|
|
239
313
|
const nodeSummary = entitySummaryByNodeId?.[node.id] || { issue: 0, knowledge: 0, capability: 0 };
|
|
240
314
|
const useStackSummary = depth >= 3;
|
|
315
|
+
const previewHtml = renderEntityPreviewBoxes(node.id, entityRowsByNodeId, elementPreviewMode);
|
|
241
316
|
const childrenHtml = hasChildren && isExpanded
|
|
242
|
-
? `<div class="lane-tree-children">${children.map((child) => renderDenseLaneNode(child, byParent, expandedIdSet, entitySummaryByNodeId, depth + 1, showAssignee)).join('')}</div>`
|
|
317
|
+
? `<div class="lane-tree-children">${children.map((child) => renderDenseLaneNode(child, byParent, expandedIdSet, entitySummaryByNodeId, depth + 1, showAssignee, entityRowsByNodeId, elementPreviewMode)).join('')}</div>`
|
|
243
318
|
: '';
|
|
244
319
|
return `
|
|
245
320
|
<div class="lane-tree-node" data-depth="${depth}">
|
|
246
|
-
<div class="lane-tree-node-row" data-select-id="${esc(node.id)}" data-node-id="${esc(node.id)}" tabindex="0">
|
|
321
|
+
<div class="lane-tree-node-row" data-select-id="${esc(node.id)}" data-node-id="${esc(node.id)}" tabindex="0" draggable="true">
|
|
247
322
|
${hasChildren ? `
|
|
248
323
|
<button
|
|
249
324
|
class="node-expand-btn dense-expand-btn ${isExpanded ? 'expanded' : ''}"
|
|
@@ -277,6 +352,7 @@ function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeI
|
|
|
277
352
|
<button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
|
|
278
353
|
</div>
|
|
279
354
|
</div>
|
|
355
|
+
${previewHtml ? `<div class="lane-node-preview-wrapper">${previewHtml}</div>` : ''}
|
|
280
356
|
${childrenHtml}
|
|
281
357
|
</div>
|
|
282
358
|
`;
|
|
@@ -316,16 +392,17 @@ function getDenseLaneWidthPx(root, byParent, showAssignee = false) {
|
|
|
316
392
|
return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, preferred));
|
|
317
393
|
}
|
|
318
394
|
|
|
319
|
-
function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false) {
|
|
395
|
+
function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none') {
|
|
320
396
|
return `
|
|
321
397
|
<div class="dense-tree dense-horizontal dense-lane-board">
|
|
322
398
|
${roots.map((root) => {
|
|
323
399
|
const laneWidth = getDenseLaneWidthPx(root, byParent, showAssignee);
|
|
324
400
|
const laneNameMax = Math.max(120, laneWidth - 86);
|
|
325
401
|
return `
|
|
326
|
-
<section class="dense-lane" style="--lane-width:${laneWidth}px;--lane-name-max:${laneNameMax}px;">
|
|
402
|
+
<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>
|
|
327
404
|
<div class="dense-lane-body">
|
|
328
|
-
${renderDenseLaneNode(root, byParent, expandedIdSet, entitySummaryByNodeId, 0, showAssignee)}
|
|
405
|
+
${renderDenseLaneNode(root, byParent, expandedIdSet, entitySummaryByNodeId, 0, showAssignee, entityRowsByNodeId, elementPreviewMode)}
|
|
329
406
|
</div>
|
|
330
407
|
</section>
|
|
331
408
|
`;
|
|
@@ -342,11 +419,18 @@ export function mountWorkTree(targetId, options) {
|
|
|
342
419
|
expandedIds = [],
|
|
343
420
|
onToggleExpand,
|
|
344
421
|
onAddChild,
|
|
422
|
+
onAddDiary,
|
|
345
423
|
onEdit,
|
|
346
424
|
onDelete,
|
|
347
425
|
onQuickChat,
|
|
348
426
|
onSelect,
|
|
427
|
+
onSelectEntity,
|
|
428
|
+
onMoveNode,
|
|
429
|
+
onReorderSiblings,
|
|
430
|
+
onReorderLanes,
|
|
349
431
|
entitySummaryByNodeId = {},
|
|
432
|
+
entityRowsByNodeId = {},
|
|
433
|
+
elementPreviewMode = 'none',
|
|
350
434
|
renderMode = 'card',
|
|
351
435
|
showAssignee = false,
|
|
352
436
|
} = options || {};
|
|
@@ -357,6 +441,33 @@ export function mountWorkTree(targetId, options) {
|
|
|
357
441
|
if (!byParent.has(key)) byParent.set(key, []);
|
|
358
442
|
byParent.get(key).push(item);
|
|
359
443
|
}
|
|
444
|
+
const laneOrderOf = (item) => {
|
|
445
|
+
const value = Number(item?.extra_data?.lane_order);
|
|
446
|
+
return Number.isFinite(value) ? value : Number.POSITIVE_INFINITY;
|
|
447
|
+
};
|
|
448
|
+
const keyOf = (item, keyName) => String(item?.extra_data?.[keyName] || '').trim();
|
|
449
|
+
byParent.forEach((rows, parentKey) => {
|
|
450
|
+
rows.sort((a, b) => {
|
|
451
|
+
if (parentKey === '__root__') {
|
|
452
|
+
const laneKeyA = keyOf(a, 'lane_order_key');
|
|
453
|
+
const laneKeyB = keyOf(b, 'lane_order_key');
|
|
454
|
+
if (laneKeyA && laneKeyB && laneKeyA !== laneKeyB) return laneKeyA.localeCompare(laneKeyB);
|
|
455
|
+
if (laneKeyA && !laneKeyB) return -1;
|
|
456
|
+
if (!laneKeyA && laneKeyB) return 1;
|
|
457
|
+
const laneDiff = laneOrderOf(a) - laneOrderOf(b);
|
|
458
|
+
if (laneDiff !== 0) return laneDiff;
|
|
459
|
+
} else {
|
|
460
|
+
const orderKeyA = keyOf(a, 'order_key');
|
|
461
|
+
const orderKeyB = keyOf(b, 'order_key');
|
|
462
|
+
if (orderKeyA && orderKeyB && orderKeyA !== orderKeyB) return orderKeyA.localeCompare(orderKeyB);
|
|
463
|
+
if (orderKeyA && !orderKeyB) return -1;
|
|
464
|
+
if (!orderKeyA && orderKeyB) return 1;
|
|
465
|
+
}
|
|
466
|
+
const timeA = String(a.created_at || '');
|
|
467
|
+
const timeB = String(b.created_at || '');
|
|
468
|
+
return timeA.localeCompare(timeB);
|
|
469
|
+
});
|
|
470
|
+
});
|
|
360
471
|
const roots = byParent.get('__root__') || [];
|
|
361
472
|
const expandedIdSet = new Set(expandedIds);
|
|
362
473
|
|
|
@@ -365,13 +476,13 @@ export function mountWorkTree(targetId, options) {
|
|
|
365
476
|
return;
|
|
366
477
|
}
|
|
367
478
|
if (renderMode === 'dense') {
|
|
368
|
-
container.innerHTML = renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee);
|
|
479
|
+
container.innerHTML = renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode);
|
|
369
480
|
} else {
|
|
370
481
|
const htmlParts = roots.map((root) => {
|
|
371
482
|
const hasChildren = ((byParent.get(root.id) || []).length > 0);
|
|
372
483
|
return `
|
|
373
484
|
<div class="root-grid-item ${hasChildren ? 'branch' : 'leaf'}" data-root-item-type="${hasChildren ? 'branch' : 'leaf'}">
|
|
374
|
-
${renderTreeNode(root, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee)}
|
|
485
|
+
${renderTreeNode(root, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode)}
|
|
375
486
|
</div>
|
|
376
487
|
`;
|
|
377
488
|
});
|
|
@@ -382,10 +493,17 @@ export function mountWorkTree(targetId, options) {
|
|
|
382
493
|
el.addEventListener('click', (e) => {
|
|
383
494
|
e.stopPropagation();
|
|
384
495
|
const action = el.getAttribute('data-action');
|
|
496
|
+
if (action === 'entity-preview') {
|
|
497
|
+
const nodeId = el.getAttribute('data-node-id');
|
|
498
|
+
const entityType = el.getAttribute('data-entity-type') || 'all';
|
|
499
|
+
if (nodeId) onSelectEntity?.(nodeId, entityType);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
385
502
|
const id = el.getAttribute('data-id');
|
|
386
503
|
if (!id) return;
|
|
387
504
|
if (action === 'toggle') onToggleExpand?.(id);
|
|
388
505
|
if (action === 'add-child') onAddChild?.(id);
|
|
506
|
+
if (action === 'add-diary') onAddDiary?.(id);
|
|
389
507
|
if (action === 'edit') onEdit?.(id);
|
|
390
508
|
if (action === 'delete') onDelete?.(id);
|
|
391
509
|
if (action === 'quick-chat') onQuickChat?.(id, el);
|
|
@@ -402,5 +520,113 @@ export function mountWorkTree(targetId, options) {
|
|
|
402
520
|
if (renderMode !== 'dense') {
|
|
403
521
|
applyAdaptiveLeafGridLayout(container);
|
|
404
522
|
applyAdaptiveRootMixedGridLayout(container);
|
|
523
|
+
return;
|
|
405
524
|
}
|
|
525
|
+
|
|
526
|
+
let draggingNodeId = '';
|
|
527
|
+
let draggingLaneId = '';
|
|
528
|
+
const dragDropPositionByNodeId = new Map();
|
|
529
|
+
const rowEls = Array.from(container.querySelectorAll('.lane-tree-node-row[data-node-id]'));
|
|
530
|
+
rowEls.forEach((el) => {
|
|
531
|
+
const nodeId = el.getAttribute('data-node-id') || '';
|
|
532
|
+
el.addEventListener('dragstart', (event) => {
|
|
533
|
+
draggingNodeId = nodeId;
|
|
534
|
+
draggingLaneId = '';
|
|
535
|
+
el.classList.add('is-dragging');
|
|
536
|
+
event.stopPropagation();
|
|
537
|
+
event.dataTransfer?.setData('text/plain', nodeId);
|
|
538
|
+
if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';
|
|
539
|
+
});
|
|
540
|
+
el.addEventListener('dragend', () => {
|
|
541
|
+
draggingNodeId = '';
|
|
542
|
+
el.classList.remove('is-dragging');
|
|
543
|
+
rowEls.forEach((rowEl) => {
|
|
544
|
+
rowEl.classList.remove('drag-over-target');
|
|
545
|
+
rowEl.removeAttribute('data-drop-position');
|
|
546
|
+
});
|
|
547
|
+
dragDropPositionByNodeId.clear();
|
|
548
|
+
});
|
|
549
|
+
el.addEventListener('dragover', (event) => {
|
|
550
|
+
if (!draggingNodeId || draggingNodeId === nodeId) return;
|
|
551
|
+
event.preventDefault();
|
|
552
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';
|
|
553
|
+
const rect = el.getBoundingClientRect();
|
|
554
|
+
const offsetY = event.clientY - rect.top;
|
|
555
|
+
const ratio = rect.height > 0 ? offsetY / rect.height : 0.5;
|
|
556
|
+
let dropPosition = 'child';
|
|
557
|
+
if (ratio < 0.25) dropPosition = 'before';
|
|
558
|
+
else if (ratio > 0.75) dropPosition = 'after';
|
|
559
|
+
dragDropPositionByNodeId.set(nodeId, dropPosition);
|
|
560
|
+
el.setAttribute('data-drop-position', dropPosition);
|
|
561
|
+
el.classList.add('drag-over-target');
|
|
562
|
+
});
|
|
563
|
+
el.addEventListener('dragleave', () => {
|
|
564
|
+
el.classList.remove('drag-over-target');
|
|
565
|
+
el.removeAttribute('data-drop-position');
|
|
566
|
+
dragDropPositionByNodeId.delete(nodeId);
|
|
567
|
+
});
|
|
568
|
+
el.addEventListener('drop', (event) => {
|
|
569
|
+
event.preventDefault();
|
|
570
|
+
el.classList.remove('drag-over-target');
|
|
571
|
+
const dropPosition = dragDropPositionByNodeId.get(nodeId) || 'child';
|
|
572
|
+
el.removeAttribute('data-drop-position');
|
|
573
|
+
dragDropPositionByNodeId.delete(nodeId);
|
|
574
|
+
if (!draggingNodeId || draggingNodeId === nodeId) return;
|
|
575
|
+
if (dropPosition === 'before' || dropPosition === 'after') {
|
|
576
|
+
onReorderSiblings?.(draggingNodeId, nodeId, dropPosition);
|
|
577
|
+
} else {
|
|
578
|
+
onMoveNode?.(draggingNodeId, nodeId);
|
|
579
|
+
}
|
|
580
|
+
draggingNodeId = '';
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
const board = container.querySelector('.dense-lane-board');
|
|
585
|
+
board?.addEventListener('dragover', (event) => {
|
|
586
|
+
if (!draggingNodeId) return;
|
|
587
|
+
event.preventDefault();
|
|
588
|
+
});
|
|
589
|
+
board?.addEventListener('drop', (event) => {
|
|
590
|
+
if (!draggingNodeId) return;
|
|
591
|
+
event.preventDefault();
|
|
592
|
+
onMoveNode?.(draggingNodeId, null);
|
|
593
|
+
draggingNodeId = '';
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
const laneEls = Array.from(container.querySelectorAll('.dense-lane[data-root-id]'));
|
|
597
|
+
const laneHandleEls = Array.from(container.querySelectorAll('.dense-lane-drag-handle[data-lane-drag-id]'));
|
|
598
|
+
laneHandleEls.forEach((handle) => {
|
|
599
|
+
const rootId = handle.getAttribute('data-lane-drag-id') || '';
|
|
600
|
+
handle.addEventListener('dragstart', (event) => {
|
|
601
|
+
draggingLaneId = rootId;
|
|
602
|
+
draggingNodeId = '';
|
|
603
|
+
const lane = handle.closest('.dense-lane');
|
|
604
|
+
lane?.classList.add('is-lane-dragging');
|
|
605
|
+
event.dataTransfer?.setData('text/plain', rootId);
|
|
606
|
+
if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';
|
|
607
|
+
});
|
|
608
|
+
handle.addEventListener('dragend', () => {
|
|
609
|
+
const lane = handle.closest('.dense-lane');
|
|
610
|
+
lane?.classList.remove('is-lane-dragging');
|
|
611
|
+
draggingLaneId = '';
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
laneEls.forEach((el) => {
|
|
615
|
+
const rootId = el.getAttribute('data-root-id') || '';
|
|
616
|
+
el.addEventListener('dragover', (event) => {
|
|
617
|
+
if (!draggingLaneId || draggingLaneId === rootId) return;
|
|
618
|
+
event.preventDefault();
|
|
619
|
+
el.classList.add('lane-drag-over-target');
|
|
620
|
+
});
|
|
621
|
+
el.addEventListener('dragleave', () => {
|
|
622
|
+
el.classList.remove('lane-drag-over-target');
|
|
623
|
+
});
|
|
624
|
+
el.addEventListener('drop', (event) => {
|
|
625
|
+
event.preventDefault();
|
|
626
|
+
el.classList.remove('lane-drag-over-target');
|
|
627
|
+
if (!draggingLaneId || draggingLaneId === rootId) return;
|
|
628
|
+
onReorderLanes?.(draggingLaneId, rootId);
|
|
629
|
+
draggingLaneId = '';
|
|
630
|
+
});
|
|
631
|
+
});
|
|
406
632
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qnote/q-ai-note",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
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
|
-
"q-ai-note": "dist/cli.js"
|
|
8
|
+
"q-ai-note": "dist/cli.js",
|
|
9
|
+
"q-ai-note-server": "dist/cli-server.js"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
12
|
"dist/**/*",
|
|
@@ -16,7 +17,8 @@
|
|
|
16
17
|
"build": "tsc && node scripts/copy-web-assets.mjs",
|
|
17
18
|
"start": "PORT=3000 node dist/server/index.js",
|
|
18
19
|
"test": "vitest run",
|
|
19
|
-
"test:e2e": "playwright test",
|
|
20
|
+
"test:e2e": "env -u CI playwright test --grep-invert @ai --workers=1",
|
|
21
|
+
"test:e2e:full": "playwright test",
|
|
20
22
|
"test:watch": "vitest",
|
|
21
23
|
"paths:check": "tsx scripts/print-runtime-paths.ts",
|
|
22
24
|
"prepack": "npm run build"
|