@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/styles.css
CHANGED
|
@@ -138,6 +138,10 @@ body {
|
|
|
138
138
|
display: none;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
.hidden {
|
|
142
|
+
display: none !important;
|
|
143
|
+
}
|
|
144
|
+
|
|
141
145
|
@keyframes fadeIn {
|
|
142
146
|
from { opacity: 0; transform: translateY(8px); }
|
|
143
147
|
to { opacity: 1; transform: translateY(0); }
|
|
@@ -453,6 +457,11 @@ h2 {
|
|
|
453
457
|
background: linear-gradient(135deg, #e8f0fe 0%, #f0f7ff 100%);
|
|
454
458
|
}
|
|
455
459
|
|
|
460
|
+
.tree-parent-header.is-selected {
|
|
461
|
+
background: linear-gradient(135deg, #dbeafe 0%, #eef6ff 100%);
|
|
462
|
+
box-shadow: inset 0 0 0 1px #8bb8ff;
|
|
463
|
+
}
|
|
464
|
+
|
|
456
465
|
.tree-parent-header .node-status {
|
|
457
466
|
width: 10px;
|
|
458
467
|
height: 10px;
|
|
@@ -772,6 +781,12 @@ h2 {
|
|
|
772
781
|
background: #f7faff;
|
|
773
782
|
}
|
|
774
783
|
|
|
784
|
+
.lane-tree-node-row.is-selected {
|
|
785
|
+
border-color: #80aef5;
|
|
786
|
+
background: #edf5ff;
|
|
787
|
+
box-shadow: inset 0 0 0 1px #80aef5;
|
|
788
|
+
}
|
|
789
|
+
|
|
775
790
|
.lane-tree-node-row:focus-visible {
|
|
776
791
|
outline: 2px solid #c2dbff;
|
|
777
792
|
outline-offset: 1px;
|
|
@@ -936,6 +951,12 @@ h2 {
|
|
|
936
951
|
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
|
|
937
952
|
}
|
|
938
953
|
|
|
954
|
+
.tree-leaf-node.is-selected {
|
|
955
|
+
border-color: #7aa9f8;
|
|
956
|
+
background: linear-gradient(180deg, #f5f9ff 0%, #ffffff 100%);
|
|
957
|
+
box-shadow: 0 0 0 2px rgba(122, 169, 248, 0.28), 0 6px 14px rgba(58, 108, 196, 0.12);
|
|
958
|
+
}
|
|
959
|
+
|
|
939
960
|
.tree-leaf-node .node-status {
|
|
940
961
|
display: inline-flex;
|
|
941
962
|
width: 6px;
|
|
@@ -1753,16 +1774,24 @@ dialog::backdrop {
|
|
|
1753
1774
|
/* Sandbox Layout - Left/Right */
|
|
1754
1775
|
.sandbox-layout {
|
|
1755
1776
|
display: grid;
|
|
1756
|
-
grid-template-columns: minmax(0,
|
|
1777
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1757
1778
|
gap: 16px;
|
|
1758
|
-
height:
|
|
1779
|
+
min-height: 320px;
|
|
1759
1780
|
padding-right: 8px;
|
|
1760
1781
|
box-sizing: border-box;
|
|
1761
1782
|
position: relative;
|
|
1762
1783
|
}
|
|
1763
1784
|
|
|
1764
|
-
.sandbox-layout.
|
|
1765
|
-
grid-template-columns: minmax(0,
|
|
1785
|
+
.sandbox-layout.show-chat {
|
|
1786
|
+
grid-template-columns: minmax(0, 72fr) minmax(280px, 28fr);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
.sandbox-layout .sandbox-chat-section {
|
|
1790
|
+
display: none;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
.sandbox-layout.show-chat .sandbox-chat-section {
|
|
1794
|
+
display: flex;
|
|
1766
1795
|
}
|
|
1767
1796
|
|
|
1768
1797
|
.sandbox-layout.is-fullscreen {
|
|
@@ -1774,12 +1803,24 @@ dialog::backdrop {
|
|
|
1774
1803
|
align-items: stretch;
|
|
1775
1804
|
}
|
|
1776
1805
|
|
|
1806
|
+
.sandbox-layout.is-fullscreen.show-chat {
|
|
1807
|
+
grid-template-columns: minmax(0, 72fr) minmax(280px, 28fr);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1777
1810
|
.sandbox-layout.is-fullscreen .sandbox-tree-section,
|
|
1778
1811
|
.sandbox-layout.is-fullscreen .sandbox-chat-section {
|
|
1779
1812
|
min-height: 0;
|
|
1780
1813
|
}
|
|
1781
1814
|
|
|
1782
|
-
.sandbox-layout.is-fullscreen .sandbox-chat-section {
|
|
1815
|
+
.sandbox-layout.is-fullscreen:not(.show-chat) .sandbox-chat-section {
|
|
1816
|
+
display: none;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
#page-sandbox-detail.is-sandbox-fullscreen #sandbox-overview {
|
|
1820
|
+
display: none;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
#page-sandbox-detail.is-sandbox-fullscreen .back-link {
|
|
1783
1824
|
display: none;
|
|
1784
1825
|
}
|
|
1785
1826
|
|
|
@@ -2121,11 +2162,15 @@ dialog::backdrop {
|
|
|
2121
2162
|
|
|
2122
2163
|
@media (max-width: 1366px) {
|
|
2123
2164
|
.sandbox-layout {
|
|
2165
|
+
grid-template-columns: minmax(0, 1fr);
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
.sandbox-layout.show-chat {
|
|
2124
2169
|
grid-template-columns: minmax(0, 70fr) minmax(260px, 30fr);
|
|
2125
2170
|
}
|
|
2126
2171
|
|
|
2127
|
-
.sandbox-layout.
|
|
2128
|
-
grid-template-columns: minmax(0,
|
|
2172
|
+
.sandbox-layout.is-fullscreen.show-chat {
|
|
2173
|
+
grid-template-columns: minmax(0, 70fr) minmax(260px, 30fr);
|
|
2129
2174
|
}
|
|
2130
2175
|
|
|
2131
2176
|
.node-entity-drawer {
|
|
@@ -2138,6 +2183,21 @@ dialog::backdrop {
|
|
|
2138
2183
|
}
|
|
2139
2184
|
}
|
|
2140
2185
|
|
|
2186
|
+
.ai-toggle-btn {
|
|
2187
|
+
background: linear-gradient(135deg, #2563eb, #1d4ed8);
|
|
2188
|
+
color: #fff;
|
|
2189
|
+
border-color: transparent;
|
|
2190
|
+
box-shadow: 0 6px 14px rgba(37, 99, 235, 0.24);
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
.ai-toggle-btn:hover {
|
|
2194
|
+
background: linear-gradient(135deg, #1d4ed8, #1e40af);
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
.ai-toggle-btn .icon {
|
|
2198
|
+
margin-right: 4px;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2141
2201
|
.sandbox-chat-section .chat-container {
|
|
2142
2202
|
flex: 1;
|
|
2143
2203
|
height: auto;
|
package/dist/web/vueRenderers.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
248
|
-
<
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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,7 +313,7 @@ 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);
|
|
@@ -314,11 +321,12 @@ function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeI
|
|
|
314
321
|
const useStackSummary = depth >= 3;
|
|
315
322
|
const previewHtml = renderEntityPreviewBoxes(node.id, entityRowsByNodeId, elementPreviewMode);
|
|
316
323
|
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>`
|
|
324
|
+
? `<div class="lane-tree-children">${children.map((child) => renderDenseLaneNode(child, byParent, expandedIdSet, entitySummaryByNodeId, depth + 1, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId)).join('')}</div>`
|
|
318
325
|
: '';
|
|
326
|
+
const isSelected = String(selectedId || '') === String(node.id || '');
|
|
319
327
|
return `
|
|
320
328
|
<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">
|
|
329
|
+
<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
330
|
${hasChildren ? `
|
|
323
331
|
<button
|
|
324
332
|
class="node-expand-btn dense-expand-btn ${isExpanded ? 'expanded' : ''}"
|
|
@@ -345,12 +353,14 @@ function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeI
|
|
|
345
353
|
</div>
|
|
346
354
|
` : ''}
|
|
347
355
|
</div>
|
|
348
|
-
|
|
349
|
-
<
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
356
|
+
${readonly ? '' : `
|
|
357
|
+
<div class="node-actions">
|
|
358
|
+
<button class="node-action-btn chat" data-action="quick-chat" data-id="${esc(node.id)}" title="快捷提问">💬</button>
|
|
359
|
+
<button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
|
|
360
|
+
<button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
|
|
361
|
+
<button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
|
|
362
|
+
</div>
|
|
363
|
+
`}
|
|
354
364
|
</div>
|
|
355
365
|
${previewHtml ? `<div class="lane-node-preview-wrapper">${previewHtml}</div>` : ''}
|
|
356
366
|
${childrenHtml}
|
|
@@ -392,7 +402,7 @@ function getDenseLaneWidthPx(root, byParent, showAssignee = false) {
|
|
|
392
402
|
return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, preferred));
|
|
393
403
|
}
|
|
394
404
|
|
|
395
|
-
function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none') {
|
|
405
|
+
function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none', readonly = false, selectedId = '') {
|
|
396
406
|
return `
|
|
397
407
|
<div class="dense-tree dense-horizontal dense-lane-board">
|
|
398
408
|
${roots.map((root) => {
|
|
@@ -400,9 +410,9 @@ function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId,
|
|
|
400
410
|
const laneNameMax = Math.max(120, laneWidth - 86);
|
|
401
411
|
return `
|
|
402
412
|
<section class="dense-lane" data-root-id="${esc(root.id)}" style="--lane-width:${laneWidth}px;--lane-name-max:${laneNameMax}px;">
|
|
403
|
-
|
|
413
|
+
${readonly ? '' : `<div class="dense-lane-drag-handle" data-lane-drag-id="${esc(root.id)}" draggable="true" title="拖拽调整泳道顺序">⋮⋮</div>`}
|
|
404
414
|
<div class="dense-lane-body">
|
|
405
|
-
${renderDenseLaneNode(root, byParent, expandedIdSet, entitySummaryByNodeId, 0, showAssignee, entityRowsByNodeId, elementPreviewMode)}
|
|
415
|
+
${renderDenseLaneNode(root, byParent, expandedIdSet, entitySummaryByNodeId, 0, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId)}
|
|
406
416
|
</div>
|
|
407
417
|
</section>
|
|
408
418
|
`;
|
|
@@ -433,6 +443,8 @@ export function mountWorkTree(targetId, options) {
|
|
|
433
443
|
elementPreviewMode = 'none',
|
|
434
444
|
renderMode = 'card',
|
|
435
445
|
showAssignee = false,
|
|
446
|
+
readonly = false,
|
|
447
|
+
selectedId = '',
|
|
436
448
|
} = options || {};
|
|
437
449
|
|
|
438
450
|
const byParent = new Map();
|
|
@@ -476,13 +488,13 @@ export function mountWorkTree(targetId, options) {
|
|
|
476
488
|
return;
|
|
477
489
|
}
|
|
478
490
|
if (renderMode === 'dense') {
|
|
479
|
-
container.innerHTML = renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode);
|
|
491
|
+
container.innerHTML = renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId);
|
|
480
492
|
} else {
|
|
481
493
|
const htmlParts = roots.map((root) => {
|
|
482
494
|
const hasChildren = ((byParent.get(root.id) || []).length > 0);
|
|
483
495
|
return `
|
|
484
496
|
<div class="root-grid-item ${hasChildren ? 'branch' : 'leaf'}" data-root-item-type="${hasChildren ? 'branch' : 'leaf'}">
|
|
485
|
-
${renderTreeNode(root, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode)}
|
|
497
|
+
${renderTreeNode(root, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee, entityRowsByNodeId, elementPreviewMode, readonly, selectedId)}
|
|
486
498
|
</div>
|
|
487
499
|
`;
|
|
488
500
|
});
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qnote/q-ai-note",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
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
|
|
9
|
+
"q-ai-note-server": "dist/cli.js",
|
|
10
|
+
"q-ai-note-my-server": "dist/cli.js"
|
|
10
11
|
},
|
|
11
12
|
"files": [
|
|
12
13
|
"dist/**/*",
|