@qnote/q-ai-note 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/web/app.js +58 -15
- package/dist/web/chatView.js +5 -1
- package/dist/web/styles.css +29 -0
- package/dist/web/vueRenderers.js +99 -1
- package/package.json +1 -1
package/dist/web/app.js
CHANGED
|
@@ -182,8 +182,6 @@ function applyReadonlyMode() {
|
|
|
182
182
|
if (systemSettingsNav instanceof HTMLElement) {
|
|
183
183
|
systemSettingsNav.classList.toggle('hidden', !state.canAccessSystemSettings);
|
|
184
184
|
}
|
|
185
|
-
setHiddenById('page-settings', !state.pageAccess.settings);
|
|
186
|
-
setHiddenById('page-system-settings', !state.canAccessSystemSettings);
|
|
187
185
|
setHiddenById('add-sandbox-btn', state.readonly || !state.fullAccess);
|
|
188
186
|
setHiddenById('add-item-btn', state.readonly || !state.currentSandboxWritable);
|
|
189
187
|
setHiddenById('toggle-node-entity-form-btn', state.readonly || !state.currentSandboxWritable);
|
|
@@ -489,16 +487,50 @@ function renderWorkTree() {
|
|
|
489
487
|
});
|
|
490
488
|
await loadSandbox(state.currentSandbox.id);
|
|
491
489
|
},
|
|
492
|
-
onReorderLanes: treeReadonly ? undefined : async (dragRootId, targetRootId) => {
|
|
490
|
+
onReorderLanes: treeReadonly ? undefined : async (dragRootId, targetRootId, position = 'before') => {
|
|
493
491
|
if (!state.currentSandbox) return;
|
|
494
|
-
if (!dragRootId
|
|
492
|
+
if (!dragRootId) return;
|
|
493
|
+
if (targetRootId && dragRootId === targetRootId) return;
|
|
495
494
|
const roots = (state.currentSandbox.items || []).filter((item) => !item.parent_id);
|
|
496
495
|
const ordered = [...roots].sort((a, b) => compareSiblingOrder(a, b, 'lane_order_key'));
|
|
496
|
+
const missingLaneKeyRoots = ordered.filter((item) => !getNodeOrderKey(item, 'lane_order_key'));
|
|
497
|
+
if (missingLaneKeyRoots.length > 0) {
|
|
498
|
+
let leftKey = '';
|
|
499
|
+
for (const root of ordered) {
|
|
500
|
+
const currentKey = getNodeOrderKey(root, 'lane_order_key');
|
|
501
|
+
if (currentKey) {
|
|
502
|
+
leftKey = currentKey;
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const generatedKey = rankBetween(leftKey, '');
|
|
506
|
+
await apiRequest(`${API_BASE}/items/${root.id}`, {
|
|
507
|
+
method: 'PUT',
|
|
508
|
+
body: JSON.stringify({
|
|
509
|
+
extra_data: {
|
|
510
|
+
...(root.extra_data || {}),
|
|
511
|
+
lane_order_key: generatedKey,
|
|
512
|
+
},
|
|
513
|
+
}),
|
|
514
|
+
});
|
|
515
|
+
root.extra_data = {
|
|
516
|
+
...(root.extra_data || {}),
|
|
517
|
+
lane_order_key: generatedKey,
|
|
518
|
+
};
|
|
519
|
+
leftKey = generatedKey;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
497
522
|
const fromIdx = ordered.findIndex((item) => item.id === dragRootId);
|
|
498
|
-
|
|
499
|
-
if (fromIdx < 0 || toIdx < 0) return;
|
|
523
|
+
if (fromIdx < 0) return;
|
|
500
524
|
const [moved] = ordered.splice(fromIdx, 1);
|
|
501
|
-
ordered.
|
|
525
|
+
let insertIndex = ordered.length;
|
|
526
|
+
if (targetRootId) {
|
|
527
|
+
const targetIdx = ordered.findIndex((item) => item.id === targetRootId);
|
|
528
|
+
if (targetIdx < 0) return;
|
|
529
|
+
insertIndex = position === 'after' ? targetIdx + 1 : targetIdx;
|
|
530
|
+
} else if (position !== 'end') {
|
|
531
|
+
insertIndex = ordered.length;
|
|
532
|
+
}
|
|
533
|
+
ordered.splice(insertIndex, 0, moved);
|
|
502
534
|
const movedIndex = ordered.findIndex((item) => item.id === dragRootId);
|
|
503
535
|
const left = ordered[movedIndex - 1] || null;
|
|
504
536
|
const right = ordered[movedIndex + 1] || null;
|
|
@@ -625,7 +657,7 @@ function getNodeOrderKey(item, keyName = 'order_key') {
|
|
|
625
657
|
function compareSiblingOrder(a, b, keyName = 'order_key') {
|
|
626
658
|
const keyA = getNodeOrderKey(a, keyName);
|
|
627
659
|
const keyB = getNodeOrderKey(b, keyName);
|
|
628
|
-
if (keyA && keyB && keyA !== keyB) return keyA
|
|
660
|
+
if (keyA && keyB && keyA !== keyB) return keyA < keyB ? -1 : 1;
|
|
629
661
|
if (keyA && !keyB) return -1;
|
|
630
662
|
if (!keyA && keyB) return 1;
|
|
631
663
|
const numericA = Number(a?.extra_data?.lane_order);
|
|
@@ -635,7 +667,10 @@ function compareSiblingOrder(a, b, keyName = 'order_key') {
|
|
|
635
667
|
if (keyName === 'lane_order_key' && validA && validB && numericA !== numericB) return numericA - numericB;
|
|
636
668
|
if (keyName === 'lane_order_key' && validA && !validB) return -1;
|
|
637
669
|
if (keyName === 'lane_order_key' && !validA && validB) return 1;
|
|
638
|
-
|
|
670
|
+
const createdA = String(a?.created_at || '');
|
|
671
|
+
const createdB = String(b?.created_at || '');
|
|
672
|
+
if (createdA === createdB) return 0;
|
|
673
|
+
return createdA < createdB ? -1 : 1;
|
|
639
674
|
}
|
|
640
675
|
|
|
641
676
|
function populateParentSelect(items, preferredParentId = null) {
|
|
@@ -1470,7 +1505,14 @@ async function loadSandboxChats(sandboxId) {
|
|
|
1470
1505
|
const chats = await apiRequest(`${API_BASE}/chats/sandbox/${sandboxId}`);
|
|
1471
1506
|
state.chats = chats;
|
|
1472
1507
|
|
|
1473
|
-
mountHtmlList(
|
|
1508
|
+
mountHtmlList(
|
|
1509
|
+
'sandbox-chat-messages',
|
|
1510
|
+
chats.map((chat) => renderChatEntry(chat, {
|
|
1511
|
+
safeText,
|
|
1512
|
+
renderAIActionMessage,
|
|
1513
|
+
renderContent: (content) => renderMarkdownSnippet(content),
|
|
1514
|
+
})),
|
|
1515
|
+
);
|
|
1474
1516
|
|
|
1475
1517
|
messages.scrollTop = messages.scrollHeight;
|
|
1476
1518
|
}
|
|
@@ -1502,7 +1544,7 @@ function renderAIActionMessage(action) {
|
|
|
1502
1544
|
};
|
|
1503
1545
|
|
|
1504
1546
|
if (actionType === 'response' || actionType === 'clarify') {
|
|
1505
|
-
return `<div class="chat-message assistant">${
|
|
1547
|
+
return `<div class="chat-message assistant">${renderMarkdownSnippet(action.response || action.observation || '')}</div>`;
|
|
1506
1548
|
}
|
|
1507
1549
|
else if (actionType === 'confirm' && action.confirm_items) {
|
|
1508
1550
|
// Skip confirm, go directly to done
|
|
@@ -1529,7 +1571,7 @@ function renderAIActionMessage(action) {
|
|
|
1529
1571
|
</div>`;
|
|
1530
1572
|
}
|
|
1531
1573
|
|
|
1532
|
-
return `<div class="chat-message assistant">${
|
|
1574
|
+
return `<div class="chat-message assistant">${renderMarkdownSnippet(action.response || '')}</div>`;
|
|
1533
1575
|
}
|
|
1534
1576
|
|
|
1535
1577
|
window.undoOperation = async function(operationId, btn) {
|
|
@@ -2227,7 +2269,8 @@ function showPage(pageId) {
|
|
|
2227
2269
|
}
|
|
2228
2270
|
|
|
2229
2271
|
document.querySelectorAll('.nav-list a').forEach(a => a.classList.remove('active'));
|
|
2230
|
-
const
|
|
2272
|
+
const navPageId = pageId === 'sandbox-detail' ? 'sandboxes' : pageId;
|
|
2273
|
+
const nav = document.querySelector(`[data-nav="${navPageId}"]`);
|
|
2231
2274
|
if (nav) nav.classList.add('active');
|
|
2232
2275
|
}
|
|
2233
2276
|
|
|
@@ -2532,7 +2575,7 @@ async function initApp() {
|
|
|
2532
2575
|
const actionType = action.action;
|
|
2533
2576
|
|
|
2534
2577
|
if (actionType === 'response' || actionType === 'clarify') {
|
|
2535
|
-
messages.insertAdjacentHTML('beforeend', `<div class="chat-message assistant">${
|
|
2578
|
+
messages.insertAdjacentHTML('beforeend', `<div class="chat-message assistant">${renderMarkdownSnippet(action.response || action.observation || '')}</div>`);
|
|
2536
2579
|
messages.scrollTop = messages.scrollHeight;
|
|
2537
2580
|
|
|
2538
2581
|
if (actionType === 'clarify') {
|
|
@@ -2624,7 +2667,7 @@ async function initApp() {
|
|
|
2624
2667
|
state.pendingAction = null;
|
|
2625
2668
|
}
|
|
2626
2669
|
else if (actionType === 'stop') {
|
|
2627
|
-
messages.insertAdjacentHTML('beforeend', `<div class="chat-message assistant">${
|
|
2670
|
+
messages.insertAdjacentHTML('beforeend', `<div class="chat-message assistant">${renderMarkdownSnippet(action.observation || '已取消操作')}</div>`);
|
|
2628
2671
|
messages.scrollTop = messages.scrollHeight;
|
|
2629
2672
|
state.pendingAction = null;
|
|
2630
2673
|
}
|
package/dist/web/chatView.js
CHANGED
|
@@ -14,7 +14,7 @@ export function tryParseAssistantAction(content) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function renderChatEntry(chat, helpers) {
|
|
17
|
-
const { safeText, renderAIActionMessage } = helpers;
|
|
17
|
+
const { safeText, renderAIActionMessage, renderContent } = helpers;
|
|
18
18
|
|
|
19
19
|
if (!('role' in chat)) {
|
|
20
20
|
return `<div class="chat-message diary">[日记] ${safeText(chat.content)}</div>`;
|
|
@@ -25,6 +25,10 @@ export function renderChatEntry(chat, helpers) {
|
|
|
25
25
|
if (parsedAction) {
|
|
26
26
|
return renderAIActionMessage(parsedAction);
|
|
27
27
|
}
|
|
28
|
+
const contentHtml = typeof renderContent === 'function'
|
|
29
|
+
? renderContent(chat.content)
|
|
30
|
+
: safeText(chat.content);
|
|
31
|
+
return `<div class="chat-message assistant">${contentHtml}</div>`;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
return `<div class="chat-message ${chat.role}">${safeText(chat.content)}</div>`;
|
package/dist/web/styles.css
CHANGED
|
@@ -134,6 +134,11 @@ body {
|
|
|
134
134
|
animation: fadeIn 0.2s ease;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
#page-sandbox-detail {
|
|
138
|
+
max-width: none;
|
|
139
|
+
margin: 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
137
142
|
.page.hidden {
|
|
138
143
|
display: none;
|
|
139
144
|
}
|
|
@@ -642,6 +647,30 @@ h2 {
|
|
|
642
647
|
flex-wrap: nowrap;
|
|
643
648
|
}
|
|
644
649
|
|
|
650
|
+
.dense-lane-drop-slot {
|
|
651
|
+
width: 14px;
|
|
652
|
+
min-width: 14px;
|
|
653
|
+
align-self: stretch;
|
|
654
|
+
border-radius: 6px;
|
|
655
|
+
min-height: 180px;
|
|
656
|
+
transition: background-color 0.15s ease, box-shadow 0.15s ease;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.dense-lane-drop-slot.end {
|
|
660
|
+
width: 24px;
|
|
661
|
+
min-width: 24px;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.dense-lane-drop-slot.lane-slot-drag-over {
|
|
665
|
+
background: rgba(59, 130, 246, 0.28);
|
|
666
|
+
box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.45);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.dense-lane-board.is-lane-dragging-active .dense-lane-drop-slot {
|
|
670
|
+
background: rgba(148, 163, 184, 0.14);
|
|
671
|
+
box-shadow: inset 0 0 0 1px rgba(148, 163, 184, 0.22);
|
|
672
|
+
}
|
|
673
|
+
|
|
645
674
|
.dense-lane {
|
|
646
675
|
min-width: var(--lane-width, 220px);
|
|
647
676
|
width: var(--lane-width, 220px);
|
package/dist/web/vueRenderers.js
CHANGED
|
@@ -411,6 +411,7 @@ function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId,
|
|
|
411
411
|
const laneWidth = getDenseLaneWidthPx(root, byParent, showAssignee);
|
|
412
412
|
const laneNameMax = Math.max(120, laneWidth - 86);
|
|
413
413
|
return `
|
|
414
|
+
<div class="dense-lane-drop-slot" data-lane-slot-root-id="${esc(root.id)}" data-lane-slot-position="before" title="拖拽到此处:插入到当前泳道之前"></div>
|
|
414
415
|
<section class="dense-lane" data-root-id="${esc(root.id)}" style="--lane-width:${laneWidth}px;--lane-name-max:${laneNameMax}px;">
|
|
415
416
|
${readonly ? '' : `<div class="dense-lane-drag-handle" data-lane-drag-id="${esc(root.id)}" draggable="true" title="拖拽调整泳道顺序">⋮⋮</div>`}
|
|
416
417
|
<div class="dense-lane-body">
|
|
@@ -419,6 +420,7 @@ function renderDenseTree(roots, byParent, expandedIdSet, entitySummaryByNodeId,
|
|
|
419
420
|
</section>
|
|
420
421
|
`;
|
|
421
422
|
}).join('')}
|
|
423
|
+
<div class="dense-lane-drop-slot end" data-lane-slot-end="1" title="拖拽到此处:移动到最后"></div>
|
|
422
424
|
</div>
|
|
423
425
|
`;
|
|
424
426
|
}
|
|
@@ -608,12 +610,16 @@ export function mountWorkTree(targetId, options) {
|
|
|
608
610
|
});
|
|
609
611
|
|
|
610
612
|
const laneEls = Array.from(container.querySelectorAll('.dense-lane[data-root-id]'));
|
|
613
|
+
const laneSlotEls = Array.from(container.querySelectorAll('.dense-lane-drop-slot[data-lane-slot-root-id]'));
|
|
614
|
+
const laneEndSlotEl = container.querySelector('.dense-lane-drop-slot[data-lane-slot-end="1"]');
|
|
615
|
+
const laneDropPositionByRootId = new Map();
|
|
611
616
|
const laneHandleEls = Array.from(container.querySelectorAll('.dense-lane-drag-handle[data-lane-drag-id]'));
|
|
612
617
|
laneHandleEls.forEach((handle) => {
|
|
613
618
|
const rootId = handle.getAttribute('data-lane-drag-id') || '';
|
|
614
619
|
handle.addEventListener('dragstart', (event) => {
|
|
615
620
|
draggingLaneId = rootId;
|
|
616
621
|
draggingNodeId = '';
|
|
622
|
+
board?.classList.add('is-lane-dragging-active');
|
|
617
623
|
const lane = handle.closest('.dense-lane');
|
|
618
624
|
lane?.classList.add('is-lane-dragging');
|
|
619
625
|
event.dataTransfer?.setData('text/plain', rootId);
|
|
@@ -622,6 +628,14 @@ export function mountWorkTree(targetId, options) {
|
|
|
622
628
|
handle.addEventListener('dragend', () => {
|
|
623
629
|
const lane = handle.closest('.dense-lane');
|
|
624
630
|
lane?.classList.remove('is-lane-dragging');
|
|
631
|
+
board?.classList.remove('is-lane-dragging-active');
|
|
632
|
+
laneEls.forEach((laneEl) => {
|
|
633
|
+
laneEl.classList.remove('lane-drag-over-target');
|
|
634
|
+
laneEl.removeAttribute('data-lane-drop-position');
|
|
635
|
+
});
|
|
636
|
+
laneSlotEls.forEach((slotEl) => slotEl.classList.remove('lane-slot-drag-over'));
|
|
637
|
+
laneEndSlotEl?.classList.remove('lane-slot-drag-over');
|
|
638
|
+
laneDropPositionByRootId.clear();
|
|
625
639
|
draggingLaneId = '';
|
|
626
640
|
});
|
|
627
641
|
});
|
|
@@ -630,17 +644,101 @@ export function mountWorkTree(targetId, options) {
|
|
|
630
644
|
el.addEventListener('dragover', (event) => {
|
|
631
645
|
if (!draggingLaneId || draggingLaneId === rootId) return;
|
|
632
646
|
event.preventDefault();
|
|
647
|
+
const rect = el.getBoundingClientRect();
|
|
648
|
+
const offsetX = event.clientX - rect.left;
|
|
649
|
+
const ratio = rect.width > 0 ? offsetX / rect.width : 0.5;
|
|
650
|
+
const laneDropPosition = ratio >= 0.5 ? 'after' : 'before';
|
|
651
|
+
laneDropPositionByRootId.set(rootId, laneDropPosition);
|
|
652
|
+
el.setAttribute('data-lane-drop-position', laneDropPosition);
|
|
633
653
|
el.classList.add('lane-drag-over-target');
|
|
634
654
|
});
|
|
635
655
|
el.addEventListener('dragleave', () => {
|
|
636
656
|
el.classList.remove('lane-drag-over-target');
|
|
657
|
+
el.removeAttribute('data-lane-drop-position');
|
|
658
|
+
laneDropPositionByRootId.delete(rootId);
|
|
637
659
|
});
|
|
638
660
|
el.addEventListener('drop', (event) => {
|
|
639
661
|
event.preventDefault();
|
|
640
662
|
el.classList.remove('lane-drag-over-target');
|
|
663
|
+
let laneDropPosition = laneDropPositionByRootId.get(rootId);
|
|
664
|
+
if (!laneDropPosition) {
|
|
665
|
+
const rect = el.getBoundingClientRect();
|
|
666
|
+
const offsetX = event.clientX - rect.left;
|
|
667
|
+
const ratio = rect.width > 0 ? offsetX / rect.width : 0.5;
|
|
668
|
+
laneDropPosition = ratio >= 0.5 ? 'after' : 'before';
|
|
669
|
+
}
|
|
670
|
+
el.removeAttribute('data-lane-drop-position');
|
|
671
|
+
laneDropPositionByRootId.delete(rootId);
|
|
641
672
|
if (!draggingLaneId || draggingLaneId === rootId) return;
|
|
642
|
-
|
|
673
|
+
if (laneDropPosition === 'before') {
|
|
674
|
+
event.stopPropagation();
|
|
675
|
+
onReorderLanes?.(draggingLaneId, rootId, 'before');
|
|
676
|
+
board?.classList.remove('is-lane-dragging-active');
|
|
677
|
+
draggingLaneId = '';
|
|
678
|
+
} else {
|
|
679
|
+
const orderedRootIds = laneEls
|
|
680
|
+
.map((laneEl) => laneEl.getAttribute('data-root-id') || '')
|
|
681
|
+
.filter((id) => id && id !== draggingLaneId);
|
|
682
|
+
const isLastTarget = orderedRootIds.length > 0 && orderedRootIds[orderedRootIds.length - 1] === rootId;
|
|
683
|
+
if (!isLastTarget) {
|
|
684
|
+
event.stopPropagation();
|
|
685
|
+
onReorderLanes?.(draggingLaneId, rootId, 'after');
|
|
686
|
+
board?.classList.remove('is-lane-dragging-active');
|
|
687
|
+
draggingLaneId = '';
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
laneSlotEls.forEach((slotEl) => {
|
|
693
|
+
const targetRootId = slotEl.getAttribute('data-lane-slot-root-id') || '';
|
|
694
|
+
slotEl.addEventListener('dragover', (event) => {
|
|
695
|
+
if (!draggingLaneId || draggingLaneId === targetRootId) return;
|
|
696
|
+
event.preventDefault();
|
|
697
|
+
slotEl.classList.add('lane-slot-drag-over');
|
|
698
|
+
});
|
|
699
|
+
slotEl.addEventListener('dragleave', () => {
|
|
700
|
+
slotEl.classList.remove('lane-slot-drag-over');
|
|
701
|
+
});
|
|
702
|
+
slotEl.addEventListener('drop', (event) => {
|
|
703
|
+
event.preventDefault();
|
|
704
|
+
event.stopPropagation();
|
|
705
|
+
slotEl.classList.remove('lane-slot-drag-over');
|
|
706
|
+
if (!draggingLaneId || draggingLaneId === targetRootId) return;
|
|
707
|
+
onReorderLanes?.(draggingLaneId, targetRootId, 'before');
|
|
708
|
+
board?.classList.remove('is-lane-dragging-active');
|
|
643
709
|
draggingLaneId = '';
|
|
644
710
|
});
|
|
645
711
|
});
|
|
712
|
+
laneEndSlotEl?.addEventListener('dragover', (event) => {
|
|
713
|
+
if (!draggingLaneId) return;
|
|
714
|
+
event.preventDefault();
|
|
715
|
+
laneEndSlotEl.classList.add('lane-slot-drag-over');
|
|
716
|
+
});
|
|
717
|
+
laneEndSlotEl?.addEventListener('dragleave', () => {
|
|
718
|
+
laneEndSlotEl.classList.remove('lane-slot-drag-over');
|
|
719
|
+
});
|
|
720
|
+
laneEndSlotEl?.addEventListener('drop', (event) => {
|
|
721
|
+
if (!draggingLaneId) return;
|
|
722
|
+
event.preventDefault();
|
|
723
|
+
event.stopPropagation();
|
|
724
|
+
laneEndSlotEl.classList.remove('lane-slot-drag-over');
|
|
725
|
+
onReorderLanes?.(draggingLaneId, '', 'end');
|
|
726
|
+
board?.classList.remove('is-lane-dragging-active');
|
|
727
|
+
draggingLaneId = '';
|
|
728
|
+
});
|
|
729
|
+
board?.addEventListener('dragover', (event) => {
|
|
730
|
+
if (!draggingLaneId) return;
|
|
731
|
+
event.preventDefault();
|
|
732
|
+
});
|
|
733
|
+
board?.addEventListener('drop', (event) => {
|
|
734
|
+
if (!draggingLaneId) return;
|
|
735
|
+
event.preventDefault();
|
|
736
|
+
laneEls.forEach((laneEl) => {
|
|
737
|
+
laneEl.classList.remove('lane-drag-over-target');
|
|
738
|
+
laneEl.removeAttribute('data-lane-drop-position');
|
|
739
|
+
});
|
|
740
|
+
laneDropPositionByRootId.clear();
|
|
741
|
+
onReorderLanes?.(draggingLaneId, '', 'end');
|
|
742
|
+
draggingLaneId = '';
|
|
743
|
+
});
|
|
646
744
|
}
|