cli-jaw 2.0.2 → 2.0.4
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.ja.md +6 -2
- package/README.ko.md +6 -2
- package/README.md +6 -2
- package/dist/src/agent/alert-escalation.js +12 -1
- package/dist/src/agent/alert-escalation.js.map +1 -1
- package/dist/src/agent/error-classifier.js +14 -8
- package/dist/src/agent/error-classifier.js.map +1 -1
- package/dist/src/agent/lifecycle-handler.js +81 -4
- package/dist/src/agent/lifecycle-handler.js.map +1 -1
- package/dist/src/agent/session-persistence.js +2 -0
- package/dist/src/agent/session-persistence.js.map +1 -1
- package/dist/src/agent/spawn.js +70 -9
- package/dist/src/agent/spawn.js.map +1 -1
- package/dist/src/browser/connection.js +69 -15
- package/dist/src/browser/connection.js.map +1 -1
- package/dist/src/browser/runtime-diagnostics.js +39 -0
- package/dist/src/browser/runtime-diagnostics.js.map +1 -1
- package/dist/src/cli/compact.js +5 -1
- package/dist/src/cli/compact.js.map +1 -1
- package/dist/src/core/compact.js +5 -1
- package/dist/src/core/compact.js.map +1 -1
- package/dist/src/manager/lifecycle.js +1 -1
- package/dist/src/manager/lifecycle.js.map +1 -1
- package/dist/src/manager/notes/routes.js +7 -0
- package/dist/src/manager/notes/routes.js.map +1 -1
- package/dist/src/manager/notes/search.js +282 -0
- package/dist/src/manager/notes/search.js.map +1 -0
- package/dist/src/manager/preview-origin-proxy.js +12 -2
- package/dist/src/manager/preview-origin-proxy.js.map +1 -1
- package/dist/src/memory/bootstrap.js +8 -1
- package/dist/src/memory/bootstrap.js.map +1 -1
- package/dist/src/memory/indexing.js +221 -134
- package/dist/src/memory/indexing.js.map +1 -1
- package/dist/src/memory/keyword-expand.js +26 -4
- package/dist/src/memory/keyword-expand.js.map +1 -1
- package/dist/src/memory/reflect.js +119 -8
- package/dist/src/memory/reflect.js.map +1 -1
- package/dist/src/memory/synonyms.js +60 -0
- package/dist/src/memory/synonyms.js.map +1 -0
- package/dist/src/orchestrator/gateway.js +1 -1
- package/dist/src/orchestrator/gateway.js.map +1 -1
- package/dist/src/orchestrator/pipeline.js +15 -18
- package/dist/src/orchestrator/pipeline.js.map +1 -1
- package/dist/src/orchestrator/state-machine.js +12 -2
- package/dist/src/orchestrator/state-machine.js.map +1 -1
- package/package.json +1 -1
- package/public/dist/assets/{MilkdownWysiwygEditor-Cm3uXfWf.js → MilkdownWysiwygEditor-DIebNZF7.js} +1 -1
- package/public/dist/assets/{app-Be58Cs3Y.js → app-DJ8ys0j5.js} +4 -4
- package/public/dist/assets/{employees-CxdghzoD.js → employees-RJ_wRL09.js} +1 -1
- package/public/dist/assets/insert-image-markdown-kk053MvN.js +22 -0
- package/public/dist/assets/manager-DAe38I94.js +25 -0
- package/public/dist/assets/{manager-DEiyrWDP.css → manager-fQR46YFa.css} +1 -1
- package/public/dist/assets/{memory-CsMNkYtv.js → memory-dJGp6QBv.js} +1 -1
- package/public/dist/assets/memory-w3yQettQ.js +1 -0
- package/public/dist/assets/{render-DGQX46ei.js → render-KVGsbWj1.js} +1 -1
- package/public/dist/assets/{settings-BH213Yv3.js → settings-C7QWaUHB.js} +1 -1
- package/public/dist/assets/settings-DmUCo6lz.js +1 -0
- package/public/dist/assets/{skills-CQtCtHPA.js → skills-CHkTgM7L.js} +1 -1
- package/public/dist/assets/skills-SxG_nfwn.js +1 -0
- package/public/dist/assets/{slash-commands-Dzk1xHWS.js → slash-commands-2ThyUGvX.js} +1 -1
- package/public/dist/assets/slash-commands-BxJkKdhB.js +1 -0
- package/public/dist/assets/{trace-drawer-SRKcfm2S.js → trace-drawer-Dis80M6X.js} +1 -1
- package/public/dist/assets/ui-LhD1VfQs.js +1 -0
- package/public/dist/assets/ui-kS1ZJfez.js +143 -0
- package/public/dist/assets/{ws-CTHQFzM1.js → ws-DVE3eWRj.js} +2 -2
- package/public/dist/index.html +1 -1
- package/public/dist/manager/index.html +2 -2
- package/public/js/features/chat.ts +6 -1
- package/public/js/features/process-block.ts +34 -6
- package/public/js/features/process-step-match.ts +2 -1
- package/public/js/ui.ts +100 -13
- package/public/js/virtual-scroll-bootstrap.ts +8 -1
- package/public/js/virtual-scroll.ts +83 -13
- package/public/js/ws.ts +3 -3
- package/public/locales/en.json +2 -1
- package/public/locales/ja.json +2 -1
- package/public/locales/ko.json +2 -1
- package/public/locales/zh.json +2 -1
- package/public/manager/src/App.tsx +10 -3
- package/public/manager/src/api.ts +17 -0
- package/public/manager/src/main.tsx +1 -0
- package/public/manager/src/notes/NotesFileTree.tsx +14 -22
- package/public/manager/src/notes/NotesSearchSidebar.tsx +118 -0
- package/public/manager/src/notes/NotesSidebar.tsx +65 -23
- package/public/manager/src/notes/NotesWorkspace.tsx +13 -0
- package/public/manager/src/notes/notes-api.ts +1 -0
- package/public/manager/src/notes/notes-search.css +90 -0
- package/public/manager/src/notes/notes-types.ts +2 -0
- package/public/manager/src/preview.ts +20 -1
- package/public/manager/src/types.ts +8 -0
- package/scripts/install-wsl.sh +48 -14
- package/public/dist/assets/insert-image-markdown-DIEa-zjk.js +0 -22
- package/public/dist/assets/manager-UEXd1_9T.js +0 -25
- package/public/dist/assets/memory-DXad_DPO.js +0 -1
- package/public/dist/assets/settings-DXT87G2U.js +0 -1
- package/public/dist/assets/skills-5o_1v0nz.js +0 -1
- package/public/dist/assets/slash-commands-D4-hrrmh.js +0 -1
- package/public/dist/assets/ui-CdRKN2S6.js +0 -141
- package/public/dist/assets/ui-n43jmg_f.js +0 -1
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { escapeHtml } from '../render.js';
|
|
2
2
|
import { ICONS } from '../icons.js';
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
interface Window {
|
|
6
|
+
__jawProcessBlockLayoutMutation?: (anchor: Element | null, mutate: () => void) => void;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
3
9
|
export interface ProcessStep {
|
|
4
10
|
id: string;
|
|
5
11
|
type: 'tool' | 'thinking' | 'search' | 'subagent';
|
|
6
12
|
icon: string;
|
|
7
13
|
rawIcon?: string | undefined;
|
|
8
14
|
label: string;
|
|
15
|
+
isEmployee?: boolean | undefined;
|
|
9
16
|
detail?: string;
|
|
10
17
|
detailPreview?: string | undefined;
|
|
11
18
|
detailLength?: number | undefined;
|
|
@@ -36,6 +43,7 @@ export interface StoredProcessStepMeta {
|
|
|
36
43
|
icon: string;
|
|
37
44
|
rawIcon?: string | undefined;
|
|
38
45
|
label: string;
|
|
46
|
+
isEmployee?: boolean | undefined;
|
|
39
47
|
stepRef?: string | undefined;
|
|
40
48
|
traceRunId?: string | undefined; traceSeq?: number | undefined; detailAvailable?: boolean | undefined; detailBytes?: number | undefined; rawRetentionStatus?: string | undefined;
|
|
41
49
|
status: ProcessStep['status'];
|
|
@@ -117,6 +125,7 @@ export function compactProcessStepForStorage(step: ProcessStep): ProcessStep {
|
|
|
117
125
|
icon: step.icon,
|
|
118
126
|
rawIcon: step.rawIcon,
|
|
119
127
|
label: step.label,
|
|
128
|
+
isEmployee: step.isEmployee,
|
|
120
129
|
stepRef: step.stepRef,
|
|
121
130
|
traceRunId: step.traceRunId, traceSeq: step.traceSeq, detailAvailable: step.detailAvailable,
|
|
122
131
|
detailBytes: step.detailBytes, rawRetentionStatus: step.rawRetentionStatus,
|
|
@@ -164,6 +173,7 @@ function updateStoredStepMeta(step: ProcessStep): void {
|
|
|
164
173
|
icon: compact.icon,
|
|
165
174
|
rawIcon: compact.rawIcon,
|
|
166
175
|
label: compact.label,
|
|
176
|
+
isEmployee: compact.isEmployee,
|
|
167
177
|
stepRef: compact.stepRef,
|
|
168
178
|
traceRunId: compact.traceRunId, traceSeq: compact.traceSeq, detailAvailable: compact.detailAvailable,
|
|
169
179
|
detailBytes: compact.detailBytes, rawRetentionStatus: compact.rawRetentionStatus,
|
|
@@ -204,6 +214,9 @@ function renderStep(step: ProcessStep): string {
|
|
|
204
214
|
const badgeClass = `process-step-badge ${step.type}`;
|
|
205
215
|
const badgeText = step.type.toUpperCase();
|
|
206
216
|
const label = escapeHtml(step.label || step.icon || '');
|
|
217
|
+
const employeeMarker = step.isEmployee
|
|
218
|
+
? '<span class="process-step-origin" aria-label="Employee tool">(E)</span>'
|
|
219
|
+
: '';
|
|
207
220
|
const icon = renderTrustedIcon(step.icon);
|
|
208
221
|
const detail = step.detailPreview || step.detail || '';
|
|
209
222
|
const detailId = `process-detail-${step.id}`;
|
|
@@ -217,6 +230,7 @@ function renderStep(step: ProcessStep): string {
|
|
|
217
230
|
data-step-id="${step.id}"
|
|
218
231
|
data-type="${escapeHtml(step.type)}"
|
|
219
232
|
data-status="${escapeHtml(step.status)}"
|
|
233
|
+
data-is-employee="${step.isEmployee ? 'true' : ''}"
|
|
220
234
|
data-step-ref="${escapeHtml(step.stepRef || '')}"
|
|
221
235
|
data-trace-run-id="${escapeHtml(step.traceRunId || '')}"
|
|
222
236
|
data-trace-seq="${String(step.traceSeq || '')}"
|
|
@@ -226,6 +240,7 @@ function renderStep(step: ProcessStep): string {
|
|
|
226
240
|
<span class="process-step-icon" aria-hidden="true">${icon}</span>
|
|
227
241
|
<span class="${badgeClass}">${badgeText}</span>
|
|
228
242
|
<span class="process-step-main">
|
|
243
|
+
${employeeMarker}
|
|
229
244
|
<span class="process-step-label">${label}</span>
|
|
230
245
|
${snippetHtml}
|
|
231
246
|
</span>
|
|
@@ -300,6 +315,15 @@ function toggleStepDetails(toggle: HTMLElement): void {
|
|
|
300
315
|
if (chevron) chevron.innerHTML = expanding ? ICONS.chevronDown : ICONS.chevronRight;
|
|
301
316
|
}
|
|
302
317
|
|
|
318
|
+
function withProcessBlockLayoutMutation(anchor: Element | null, mutate: () => void): void {
|
|
319
|
+
const hook = window.__jawProcessBlockLayoutMutation;
|
|
320
|
+
if (typeof hook === 'function') {
|
|
321
|
+
hook(anchor, mutate);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
mutate();
|
|
325
|
+
}
|
|
326
|
+
|
|
303
327
|
export function bindProcessBlockInteractions(root: HTMLElement): void {
|
|
304
328
|
if (root.dataset['processBlockBound'] === '1') return;
|
|
305
329
|
root.addEventListener('click', (event) => {
|
|
@@ -318,7 +342,9 @@ export function bindProcessBlockInteractions(root: HTMLElement): void {
|
|
|
318
342
|
|
|
319
343
|
const stepToggle = target.closest('.process-step-toggle') as HTMLElement | null;
|
|
320
344
|
if (stepToggle) {
|
|
321
|
-
|
|
345
|
+
withProcessBlockLayoutMutation(stepToggle.closest('.process-step, .process-block'), () => {
|
|
346
|
+
toggleStepDetails(stepToggle);
|
|
347
|
+
});
|
|
322
348
|
return;
|
|
323
349
|
}
|
|
324
350
|
|
|
@@ -326,11 +352,13 @@ export function bindProcessBlockInteractions(root: HTMLElement): void {
|
|
|
326
352
|
if (summary) {
|
|
327
353
|
const block = summary.closest('.process-block');
|
|
328
354
|
if (!block) return;
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
355
|
+
withProcessBlockLayoutMutation(block, () => {
|
|
356
|
+
const expanding = block.classList.contains('collapsed');
|
|
357
|
+
block.classList.toggle('collapsed', !expanding);
|
|
358
|
+
summary.setAttribute('aria-expanded', expanding ? 'true' : 'false');
|
|
359
|
+
const chevron = summary.querySelector('.process-chevron');
|
|
360
|
+
if (chevron) chevron.innerHTML = expanding ? ICONS.chevronDown : ICONS.chevronRight;
|
|
361
|
+
});
|
|
334
362
|
}
|
|
335
363
|
});
|
|
336
364
|
root.dataset['processBlockBound'] = '1';
|
|
@@ -4,7 +4,8 @@ export function findLegacyRunningMatch(steps: ProcessStep[], step: ProcessStep):
|
|
|
4
4
|
const matches = steps.filter(s => s.status === 'running'
|
|
5
5
|
&& !s.stepRef
|
|
6
6
|
&& s.label === step.label
|
|
7
|
-
&& s.type === step.type
|
|
7
|
+
&& s.type === step.type
|
|
8
|
+
&& Boolean(s.isEmployee) === Boolean(step.isEmployee));
|
|
8
9
|
return matches.length === 1 ? matches[0]! : null;
|
|
9
10
|
}
|
|
10
11
|
|
package/public/js/ui.ts
CHANGED
|
@@ -42,6 +42,12 @@ interface MessageItem { role: string; content: string; tool_log?: string | null;
|
|
|
42
42
|
interface QueuedOverlayItem { id: string; prompt: string; source?: string; ts?: number; }
|
|
43
43
|
interface ActiveRunSnapshot { running?: boolean; cli?: string; text?: string; toolLog?: ToolLogEntry[]; startedAt?: number; }
|
|
44
44
|
|
|
45
|
+
declare global {
|
|
46
|
+
interface Window {
|
|
47
|
+
__jawProcessBlockLayoutMutation?: (anchor: Element | null, mutate: () => void) => void;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
45
51
|
function processStepType(toolType?: string): ProcessStep['type'] {
|
|
46
52
|
return toolType === 'thinking' || toolType === 'search' || toolType === 'subagent'
|
|
47
53
|
? toolType
|
|
@@ -82,6 +88,7 @@ function toProcessSteps(tools: ToolLogEntry[], runStartedAt?: number): ProcessSt
|
|
|
82
88
|
icon: tool.icon ? emojiToIcon(tool.icon) : ICONS.tool,
|
|
83
89
|
rawIcon: tool.rawIcon || tool.icon || '',
|
|
84
90
|
label: fallbackToolLabel(tool),
|
|
91
|
+
isEmployee: tool.isEmployee === true,
|
|
85
92
|
type: processStepType(tool.toolType),
|
|
86
93
|
detail: tool.detail || '',
|
|
87
94
|
stepRef: tool.stepRef || '',
|
|
@@ -170,6 +177,7 @@ function processStepFromDom(row: HTMLElement): ProcessStep | null {
|
|
|
170
177
|
icon: storedMeta?.icon || icon,
|
|
171
178
|
rawIcon: storedMeta?.rawIcon,
|
|
172
179
|
label: storedMeta?.label || label,
|
|
180
|
+
isEmployee: storedMeta?.isEmployee === true || row.dataset['isEmployee'] === 'true',
|
|
173
181
|
detail,
|
|
174
182
|
detailPreview: storedMeta?.preview,
|
|
175
183
|
detailLength: storedMeta?.detailLength,
|
|
@@ -205,6 +213,7 @@ function processStepToToolLog(step: ProcessStep, finalize = false): ToolLogEntry
|
|
|
205
213
|
icon: step.rawIcon || step.icon || ICONS.tool,
|
|
206
214
|
rawIcon: step.rawIcon || step.icon || '',
|
|
207
215
|
label: step.label || 'tool',
|
|
216
|
+
isEmployee: step.isEmployee === true,
|
|
208
217
|
detail,
|
|
209
218
|
toolType: step.type,
|
|
210
219
|
stepRef: step.stepRef || '',
|
|
@@ -225,6 +234,7 @@ function processStepFromMeta(stepId: string, finalize = false): ToolLogEntry | n
|
|
|
225
234
|
icon: meta.rawIcon || meta.icon || ICONS.tool,
|
|
226
235
|
rawIcon: meta.rawIcon || meta.icon || '',
|
|
227
236
|
label: meta.label || 'tool',
|
|
237
|
+
isEmployee: meta.isEmployee === true,
|
|
228
238
|
detail: getStoredProcessStepDetail(stepId) || meta.preview || '',
|
|
229
239
|
toolType: meta.type,
|
|
230
240
|
stepRef: meta.stepRef || '',
|
|
@@ -473,6 +483,7 @@ export function showProcessStep(step: ProcessStep): void {
|
|
|
473
483
|
.find(s => s.status === 'running'
|
|
474
484
|
&& s.label === step.label
|
|
475
485
|
&& s.type === step.type
|
|
486
|
+
&& Boolean(s.isEmployee) === Boolean(step.isEmployee)
|
|
476
487
|
&& !s.detail);
|
|
477
488
|
if (ghost) {
|
|
478
489
|
replaceStep(state.currentProcessBlock, ghost.id, step);
|
|
@@ -762,19 +773,74 @@ export function addMessage(role: string, text: string, cli?: string | null): HTM
|
|
|
762
773
|
|
|
763
774
|
let scrollRAF: number | null = null;
|
|
764
775
|
let userNearBottom = true;
|
|
776
|
+
type ScrollIntent = 'unknown' | 'following' | 'pinnedAway';
|
|
777
|
+
let scrollIntent: ScrollIntent = 'unknown';
|
|
765
778
|
let scrollTrackingBound = false;
|
|
766
779
|
const SCROLL_BOTTOM_THRESHOLD = 80; // px
|
|
767
780
|
const RESTORE_INDICATOR_SETTLE_MS = 1100;
|
|
768
781
|
let chatRestoreIndicatorHideTimer: number | null = null;
|
|
782
|
+
const chatRestorePassTimers = new Set<number>();
|
|
783
|
+
const chatRestorePassRafs = new Set<number>();
|
|
784
|
+
|
|
785
|
+
function canFollowAfterRestore(): boolean {
|
|
786
|
+
return scrollIntent !== 'pinnedAway';
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function markFollowingBottom(): void {
|
|
790
|
+
userNearBottom = true;
|
|
791
|
+
scrollIntent = 'following';
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function updateScrollIntentFromDistance(dist: number): void {
|
|
795
|
+
userNearBottom = dist < SCROLL_BOTTOM_THRESHOLD;
|
|
796
|
+
scrollIntent = userNearBottom ? 'following' : 'pinnedAway';
|
|
797
|
+
if (scrollIntent === 'pinnedAway') cancelPendingChatRestorePasses();
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function cancelPendingChatRestorePasses(): void {
|
|
801
|
+
for (const timer of chatRestorePassTimers) window.clearTimeout(timer);
|
|
802
|
+
chatRestorePassTimers.clear();
|
|
803
|
+
for (const raf of chatRestorePassRafs) cancelAnimationFrame(raf);
|
|
804
|
+
chatRestorePassRafs.clear();
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function requestChatRestoreFrame(callback: () => void): void {
|
|
808
|
+
const raf = requestAnimationFrame(() => {
|
|
809
|
+
chatRestorePassRafs.delete(raf);
|
|
810
|
+
callback();
|
|
811
|
+
});
|
|
812
|
+
chatRestorePassRafs.add(raf);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function trackChatRestoreTimer(timer: number): void {
|
|
816
|
+
chatRestorePassTimers.add(timer);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function scheduleChatRestoreTimer(callback: () => void, delayMs: number): void {
|
|
820
|
+
const timer = window.setTimeout(() => {
|
|
821
|
+
chatRestorePassTimers.delete(timer);
|
|
822
|
+
callback();
|
|
823
|
+
}, delayMs);
|
|
824
|
+
trackChatRestoreTimer(timer);
|
|
825
|
+
}
|
|
769
826
|
|
|
770
827
|
function ensureScrollTracking(): void {
|
|
828
|
+
getVirtualScroll().setRestoreFollowPredicate(canFollowAfterRestore);
|
|
829
|
+
window.__jawProcessBlockLayoutMutation = (anchor, mutate) => {
|
|
830
|
+
const vs = getVirtualScroll();
|
|
831
|
+
if (vs.active) {
|
|
832
|
+
vs.preserveScrollDuringMutation(anchor, mutate);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
mutate();
|
|
836
|
+
};
|
|
771
837
|
if (scrollTrackingBound) return;
|
|
772
838
|
const c = document.getElementById('chatMessages');
|
|
773
839
|
if (!c) return;
|
|
774
840
|
scrollTrackingBound = true;
|
|
775
841
|
c.addEventListener('scroll', () => {
|
|
776
842
|
const dist = c.scrollHeight - c.scrollTop - c.clientHeight;
|
|
777
|
-
|
|
843
|
+
updateScrollIntentFromDistance(dist);
|
|
778
844
|
}, { passive: true });
|
|
779
845
|
}
|
|
780
846
|
|
|
@@ -791,7 +857,7 @@ export function isChatNearBottom(): boolean {
|
|
|
791
857
|
export function reconcileChatBottomAfterLayout(shouldFollow = isChatNearBottom()): void {
|
|
792
858
|
ensureScrollTracking();
|
|
793
859
|
if (!shouldFollow) return;
|
|
794
|
-
|
|
860
|
+
markFollowingBottom();
|
|
795
861
|
const vs = getVirtualScroll();
|
|
796
862
|
if (vs.active) {
|
|
797
863
|
vs.reconcileBottomAfterLayout('reconnect', true);
|
|
@@ -847,22 +913,36 @@ export function reconcileChatBottomAfterRestore(reason: string): void {
|
|
|
847
913
|
showChatRestoreIndicator(reason);
|
|
848
914
|
hideChatRestoreIndicatorAfterSettle();
|
|
849
915
|
ensureScrollTracking();
|
|
850
|
-
userNearBottom = true;
|
|
851
916
|
const vs = getVirtualScroll();
|
|
852
917
|
if (vs.active) {
|
|
853
|
-
vs.
|
|
918
|
+
vs.reconcileAfterRestore(reason as RestoreReason, canFollowAfterRestore);
|
|
854
919
|
return;
|
|
855
920
|
}
|
|
856
|
-
|
|
921
|
+
if (!canFollowAfterRestore()) return;
|
|
922
|
+
const scrollIfFollowing = () => {
|
|
923
|
+
if (!canFollowAfterRestore()) {
|
|
924
|
+
cancelPendingChatRestorePasses();
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
857
927
|
const c = document.getElementById('chatMessages');
|
|
858
|
-
if (c)
|
|
928
|
+
if (c) {
|
|
929
|
+
c.scrollTop = c.scrollHeight;
|
|
930
|
+
markFollowingBottom();
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
const runRestorePass = () => {
|
|
934
|
+
if (!canFollowAfterRestore()) {
|
|
935
|
+
cancelPendingChatRestorePasses();
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
requestChatRestoreFrame(scrollIfFollowing);
|
|
859
939
|
};
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
void document.fonts?.ready.then(
|
|
940
|
+
runRestorePass();
|
|
941
|
+
requestChatRestoreFrame(runRestorePass);
|
|
942
|
+
requestChatRestoreFrame(() => requestChatRestoreFrame(runRestorePass));
|
|
943
|
+
scheduleChatRestoreTimer(runRestorePass, 250);
|
|
944
|
+
scheduleChatRestoreTimer(runRestorePass, 1000);
|
|
945
|
+
void document.fonts?.ready.then(runRestorePass);
|
|
866
946
|
}
|
|
867
947
|
|
|
868
948
|
/** Scroll chat to bottom.
|
|
@@ -872,7 +952,7 @@ export function scrollToBottom(force = false): void {
|
|
|
872
952
|
if (!force && !userNearBottom) return;
|
|
873
953
|
// After force scroll, mark as near-bottom so subsequent
|
|
874
954
|
// streaming chunks keep auto-scrolling until user scrolls up
|
|
875
|
-
if (force)
|
|
955
|
+
if (force) markFollowingBottom();
|
|
876
956
|
|
|
877
957
|
const vs = getVirtualScroll();
|
|
878
958
|
if (vs.active) {
|
|
@@ -993,6 +1073,13 @@ function makeBootstrapDeps(
|
|
|
993
1073
|
setItems: (items, opts) => vs.setItems(items, opts),
|
|
994
1074
|
activateIfNeeded: (toBottom) => vs.activateIfNeeded(toBottom),
|
|
995
1075
|
scrollToBottom: () => vs.scrollToBottom(),
|
|
1076
|
+
shouldFollowBottom: canFollowAfterRestore,
|
|
1077
|
+
onBeforeVirtualHistoryBootstrap: () => {
|
|
1078
|
+
ensureScrollTracking();
|
|
1079
|
+
},
|
|
1080
|
+
onAfterVirtualHistoryBottomed: () => {
|
|
1081
|
+
markFollowingBottom();
|
|
1082
|
+
},
|
|
996
1083
|
};
|
|
997
1084
|
}
|
|
998
1085
|
|
|
@@ -9,6 +9,9 @@ export interface VirtualHistoryBootstrapDeps {
|
|
|
9
9
|
setItems: (items: VirtualItem[], options?: { autoActivate?: boolean; toBottom?: boolean }) => void;
|
|
10
10
|
activateIfNeeded: (toBottom: boolean) => void;
|
|
11
11
|
scrollToBottom: () => void;
|
|
12
|
+
shouldFollowBottom?: () => boolean;
|
|
13
|
+
onBeforeVirtualHistoryBootstrap?: () => void;
|
|
14
|
+
onAfterVirtualHistoryBottomed?: () => void;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
/**
|
|
@@ -22,8 +25,12 @@ export function bootstrapVirtualHistory(
|
|
|
22
25
|
items: VirtualItem[],
|
|
23
26
|
deps: VirtualHistoryBootstrapDeps,
|
|
24
27
|
): void {
|
|
28
|
+
deps.onBeforeVirtualHistoryBootstrap?.();
|
|
25
29
|
deps.registerCallbacks();
|
|
26
30
|
deps.setItems(items, { autoActivate: false });
|
|
27
|
-
deps.
|
|
31
|
+
const shouldFollowBottom = deps.shouldFollowBottom?.() ?? true;
|
|
32
|
+
deps.activateIfNeeded(shouldFollowBottom);
|
|
33
|
+
if (!shouldFollowBottom) return;
|
|
28
34
|
deps.scrollToBottom();
|
|
35
|
+
deps.onAfterVirtualHistoryBottomed?.();
|
|
29
36
|
}
|
|
@@ -34,7 +34,12 @@ export interface VirtualItem {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export type LazyRenderCallback = (targets: HTMLElement[]) => void;
|
|
37
|
+
export type RestoreFollowPredicate = () => boolean;
|
|
37
38
|
type MeasurableVirtualElement = Pick<HTMLElement, 'getBoundingClientRect'>;
|
|
39
|
+
interface ScrollAnchor {
|
|
40
|
+
el: HTMLElement;
|
|
41
|
+
top: number;
|
|
42
|
+
}
|
|
38
43
|
|
|
39
44
|
function readMeasuredHeight(el: MeasurableVirtualElement): number {
|
|
40
45
|
const height = Math.ceil(el.getBoundingClientRect().height);
|
|
@@ -73,6 +78,7 @@ export class VirtualScroll {
|
|
|
73
78
|
private mounted = new Map<number, HTMLElement>();
|
|
74
79
|
private itemGap = 0;
|
|
75
80
|
private restorePassTimers = new Set<number>();
|
|
81
|
+
private shouldFollowAfterRestore: RestoreFollowPredicate = () => true;
|
|
76
82
|
|
|
77
83
|
onLazyRender: LazyRenderCallback | null = null;
|
|
78
84
|
onPostRender: ((viewport: HTMLElement) => void) | null = null;
|
|
@@ -193,6 +199,10 @@ export class VirtualScroll {
|
|
|
193
199
|
return dist < threshold;
|
|
194
200
|
}
|
|
195
201
|
|
|
202
|
+
setRestoreFollowPredicate(predicate: RestoreFollowPredicate | null): void {
|
|
203
|
+
this.shouldFollowAfterRestore = predicate ?? (() => true);
|
|
204
|
+
}
|
|
205
|
+
|
|
196
206
|
reconcileBottomAfterLayout(reason: RestoreReason, shouldFollow = this.isNearBottom()): void {
|
|
197
207
|
if (!shouldFollow) return;
|
|
198
208
|
void reason;
|
|
@@ -204,37 +214,98 @@ export class VirtualScroll {
|
|
|
204
214
|
});
|
|
205
215
|
}
|
|
206
216
|
|
|
217
|
+
reconcileAfterRestore(reason: RestoreReason, shouldFollow: RestoreFollowPredicate = this.shouldFollowAfterRestore): void {
|
|
218
|
+
if (!shouldFollow()) {
|
|
219
|
+
this.invalidateLayout();
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
this.scheduleRestoreReconcile(reason, shouldFollow);
|
|
223
|
+
}
|
|
224
|
+
|
|
207
225
|
forceBottomAfterRestore(reason: RestoreReason): void {
|
|
208
226
|
this.scheduleRestoreReconcile(reason);
|
|
209
227
|
}
|
|
210
228
|
|
|
211
|
-
|
|
212
|
-
this.
|
|
213
|
-
|
|
229
|
+
cancelRestoreReconcile(_reason?: RestoreReason): void {
|
|
230
|
+
this.clearRestoreTimers();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
preserveScrollDuringMutation<T>(anchorEl: Element | null, mutate: () => T): T {
|
|
234
|
+
const wasNearBottom = this.isNearBottom();
|
|
235
|
+
const anchor = this.captureScrollAnchor(anchorEl);
|
|
236
|
+
const result = mutate();
|
|
237
|
+
this.invalidateLayout();
|
|
238
|
+
if (wasNearBottom) {
|
|
239
|
+
this.reconcileBottomAfterLayout('manual', true);
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
this.restoreScrollAnchor(anchor);
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private scheduleRestoreReconcile(reason: RestoreReason, shouldFollow?: RestoreFollowPredicate): void {
|
|
247
|
+
this.runRestoreReconcilePass(reason, shouldFollow);
|
|
248
|
+
requestAnimationFrame(() => this.runRestoreReconcilePass(reason, shouldFollow));
|
|
214
249
|
requestAnimationFrame(() => {
|
|
215
|
-
requestAnimationFrame(() => this.runRestoreReconcilePass(reason));
|
|
250
|
+
requestAnimationFrame(() => this.runRestoreReconcilePass(reason, shouldFollow));
|
|
216
251
|
});
|
|
217
|
-
this.scheduleRestoreTimer(reason, 250);
|
|
218
|
-
this.scheduleRestoreTimer(reason, 1000);
|
|
219
|
-
void document.fonts?.ready.then(() => this.runRestoreReconcilePass(reason));
|
|
252
|
+
this.scheduleRestoreTimer(reason, 250, shouldFollow);
|
|
253
|
+
this.scheduleRestoreTimer(reason, 1000, shouldFollow);
|
|
254
|
+
void document.fonts?.ready.then(() => this.runRestoreReconcilePass(reason, shouldFollow));
|
|
220
255
|
}
|
|
221
256
|
|
|
222
|
-
private scheduleRestoreTimer(reason: RestoreReason, delayMs: number): void {
|
|
257
|
+
private scheduleRestoreTimer(reason: RestoreReason, delayMs: number, shouldFollow?: RestoreFollowPredicate): void {
|
|
223
258
|
const timer = window.setTimeout(() => {
|
|
224
259
|
this.restorePassTimers.delete(timer);
|
|
225
|
-
this.runRestoreReconcilePass(reason);
|
|
260
|
+
this.runRestoreReconcilePass(reason, shouldFollow);
|
|
226
261
|
}, delayMs);
|
|
227
262
|
this.restorePassTimers.add(timer);
|
|
228
263
|
}
|
|
229
264
|
|
|
230
|
-
private runRestoreReconcilePass(reason: RestoreReason): void {
|
|
265
|
+
private runRestoreReconcilePass(reason: RestoreReason, shouldFollow?: RestoreFollowPredicate): void {
|
|
231
266
|
if (!this.virtualizer) return;
|
|
232
267
|
void reason;
|
|
268
|
+
if (shouldFollow && !shouldFollow()) {
|
|
269
|
+
this.cancelRestoreReconcile(reason);
|
|
270
|
+
this.invalidateLayout();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
233
273
|
this.invalidateLayout();
|
|
234
274
|
remeasureMountedVirtualItems(this.items, this.mounted, this.virtualizer);
|
|
235
275
|
this.scrollToBottom();
|
|
236
276
|
}
|
|
237
277
|
|
|
278
|
+
private captureScrollAnchor(preferred: Element | null): ScrollAnchor | null {
|
|
279
|
+
const preferredEl = preferred instanceof HTMLElement ? preferred : null;
|
|
280
|
+
const chosen = preferredEl && this.isVisibleInContainer(preferredEl)
|
|
281
|
+
? preferredEl
|
|
282
|
+
: this.firstVisibleMountedItem();
|
|
283
|
+
return chosen ? { el: chosen, top: chosen.getBoundingClientRect().top } : null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private restoreScrollAnchor(anchor: ScrollAnchor | null): void {
|
|
287
|
+
if (!anchor || !anchor.el.isConnected) return;
|
|
288
|
+
const afterTop = anchor.el.getBoundingClientRect().top;
|
|
289
|
+
const delta = afterTop - anchor.top;
|
|
290
|
+
if (Number.isFinite(delta) && delta !== 0) {
|
|
291
|
+
this.container.scrollTop += delta;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private firstVisibleMountedItem(): HTMLElement | null {
|
|
296
|
+
const mounted = Array.from(this.mounted.entries()).sort(([a], [b]) => a - b);
|
|
297
|
+
for (const [, el] of mounted) {
|
|
298
|
+
if (this.isVisibleInContainer(el)) return el;
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private isVisibleInContainer(el: HTMLElement): boolean {
|
|
304
|
+
const rect = el.getBoundingClientRect();
|
|
305
|
+
const containerRect = this.container.getBoundingClientRect();
|
|
306
|
+
return rect.bottom > containerRect.top && rect.top < containerRect.bottom;
|
|
307
|
+
}
|
|
308
|
+
|
|
238
309
|
private clearRestoreTimers(): void {
|
|
239
310
|
for (const timer of this.restorePassTimers) {
|
|
240
311
|
window.clearTimeout(timer);
|
|
@@ -337,10 +408,9 @@ export class VirtualScroll {
|
|
|
337
408
|
|
|
338
409
|
// ── Browser restore reconciliation ──
|
|
339
410
|
// Resume/discard paths can restore stale virtualizer measurements.
|
|
340
|
-
// Product policy: browser restore/reconnect forces the newest message.
|
|
341
411
|
const restoreBottomAfterLayout = (reason: RestoreReason) => {
|
|
342
412
|
if (!this.virtualizer) return;
|
|
343
|
-
this.
|
|
413
|
+
this.reconcileAfterRestore(reason, this.shouldFollowAfterRestore);
|
|
344
414
|
};
|
|
345
415
|
const onPageShow = (e: PageTransitionEvent) => {
|
|
346
416
|
if (!e.persisted) return;
|
|
@@ -394,7 +464,7 @@ export class VirtualScroll {
|
|
|
394
464
|
const wasDiscarded = 'wasDiscarded' in document
|
|
395
465
|
&& Boolean((document as Document & { wasDiscarded?: boolean }).wasDiscarded);
|
|
396
466
|
if (wasDiscarded) {
|
|
397
|
-
|
|
467
|
+
restoreBottomAfterLayout('discard');
|
|
398
468
|
}
|
|
399
469
|
}
|
|
400
470
|
|
package/public/js/ws.ts
CHANGED
|
@@ -66,7 +66,7 @@ interface WsMessage {
|
|
|
66
66
|
detailBytes?: number;
|
|
67
67
|
rawRetentionStatus?: string;
|
|
68
68
|
text?: string;
|
|
69
|
-
toolLog?: { icon: string; label: string; detail?: string; toolType?: string; stepRef?: string; traceRunId?: string; traceSeq?: number; detailAvailable?: boolean; detailBytes?: number; rawRetentionStatus?: string }[];
|
|
69
|
+
toolLog?: { icon: string; label: string; detail?: string; toolType?: string; stepRef?: string; isEmployee?: boolean; traceRunId?: string; traceSeq?: number; detailAvailable?: boolean; detailBytes?: number; rawRetentionStatus?: string }[];
|
|
70
70
|
from?: string;
|
|
71
71
|
to?: string;
|
|
72
72
|
source?: string;
|
|
@@ -377,7 +377,6 @@ export function connect(): void {
|
|
|
377
377
|
addSystemMsg(t('ws.roundRetry', { round: msg.round || 0 }));
|
|
378
378
|
}
|
|
379
379
|
} else if (msg.type === 'agent_tool') {
|
|
380
|
-
const empPrefix = msg.isEmployee ? '(E) ' : '';
|
|
381
380
|
const stepType = msg.toolType === 'thinking' ? 'thinking'
|
|
382
381
|
: msg.toolType === 'search' ? 'search'
|
|
383
382
|
: msg.toolType === 'subagent' ? 'subagent' : 'tool';
|
|
@@ -386,7 +385,8 @@ export function connect(): void {
|
|
|
386
385
|
type: stepType,
|
|
387
386
|
icon: msg.icon || ICONS.tool,
|
|
388
387
|
rawIcon: msg.rawIcon || msg.icon || '',
|
|
389
|
-
label:
|
|
388
|
+
label: msg.label || '',
|
|
389
|
+
isEmployee: msg.isEmployee === true,
|
|
390
390
|
detail: msg.detail || '',
|
|
391
391
|
stepRef: msg.stepRef || '',
|
|
392
392
|
traceRunId: msg.traceRunId || '',
|
package/public/locales/en.json
CHANGED
|
@@ -141,7 +141,8 @@
|
|
|
141
141
|
"chat.file.sentWithMsg": "\n\nUser message: {text}",
|
|
142
142
|
"chat.file.uploadFail": "File upload failed: {msg}",
|
|
143
143
|
"chat.requestFail": "Request failed ({status})",
|
|
144
|
-
"chat.continue": "Continuing
|
|
144
|
+
"chat.continue": "Continuing the current task.",
|
|
145
|
+
"chat.noPendingContinue": "No pending work to continue.",
|
|
145
146
|
"chat.voice.label": "🎤 [Voice message]",
|
|
146
147
|
"skill.loadFail": "Failed to load skills",
|
|
147
148
|
"skill.count": "{active} active / {total} total",
|
package/public/locales/ja.json
CHANGED
|
@@ -141,7 +141,8 @@
|
|
|
141
141
|
"chat.file.sentWithMsg": "\n\nユーザーメッセージ: {text}",
|
|
142
142
|
"chat.file.uploadFail": "ファイルのアップロードに失敗しました: {msg}",
|
|
143
143
|
"chat.requestFail": "リクエストに失敗しました({status})",
|
|
144
|
-
"chat.continue": "
|
|
144
|
+
"chat.continue": "現在の作業を続行しています。",
|
|
145
|
+
"chat.noPendingContinue": "続行できる保留中の作業はありません。",
|
|
145
146
|
"chat.voice.label": "🎤 [音声メッセージ]",
|
|
146
147
|
"skill.loadFail": "スキルを読み込めませんでした",
|
|
147
148
|
"skill.count": "有効 {active} 件 / 全 {total} 件",
|
package/public/locales/ko.json
CHANGED
|
@@ -141,7 +141,8 @@
|
|
|
141
141
|
"chat.file.sentWithMsg": "\n\n사용자 메시지: {text}",
|
|
142
142
|
"chat.file.uploadFail": "파일 업로드 실패: {msg}",
|
|
143
143
|
"chat.requestFail": "요청 실패 ({status})",
|
|
144
|
-
"chat.continue": "
|
|
144
|
+
"chat.continue": "현재 작업을 이어서 진행합니다.",
|
|
145
|
+
"chat.noPendingContinue": "이어갈 작업이 없습니다.",
|
|
145
146
|
"chat.voice.label": "🎤 [음성 메시지]",
|
|
146
147
|
"skill.loadFail": "스킬 로드 실패",
|
|
147
148
|
"skill.count": "활성 {active}개 / 전체 {total}개",
|
package/public/locales/zh.json
CHANGED
|
@@ -141,7 +141,8 @@
|
|
|
141
141
|
"chat.file.sentWithMsg": "\n\n用户消息:{text}",
|
|
142
142
|
"chat.file.uploadFail": "文件上传失败:{msg}",
|
|
143
143
|
"chat.requestFail": "请求失败({status})",
|
|
144
|
-
"chat.continue": "
|
|
144
|
+
"chat.continue": "正在继续当前任务。",
|
|
145
|
+
"chat.noPendingContinue": "没有可继续的待处理工作。",
|
|
145
146
|
"chat.voice.label": "🎤 [语音消息]",
|
|
146
147
|
"skill.loadFail": "无法加载技能",
|
|
147
148
|
"skill.count": "{active} 个启用 / 共 {total} 个",
|
|
@@ -24,7 +24,7 @@ import { DashboardSettingsSidebar, type DashboardSettingsSection } from './dashb
|
|
|
24
24
|
import { DashboardSettingsWorkspace } from './dashboard-settings/DashboardSettingsWorkspace';
|
|
25
25
|
import { summarizeActivityTitleSupport } from './dashboard-settings/activity-title-support';
|
|
26
26
|
import { dashboardSettingsUiFromView } from './dashboard-settings/dashboard-settings-ui';
|
|
27
|
-
import { NotesSidebar } from './notes/NotesSidebar';
|
|
27
|
+
import { NotesSidebar, type NotesSidebarMode } from './notes/NotesSidebar';
|
|
28
28
|
import { NotesWorkspace } from './notes/NotesWorkspace';
|
|
29
29
|
import { useNotesModel } from './notes/useNotesModel';
|
|
30
30
|
import { publishInvalidation } from './sync/invalidation-bus';
|
|
@@ -84,6 +84,8 @@ export function App() {
|
|
|
84
84
|
const [activeProfileIds, setActiveProfileIds] = useState<string[]>([]);
|
|
85
85
|
const [settingsDirty, setSettingsDirty] = useState(false);
|
|
86
86
|
const [notesDirtyPath, setNotesDirtyPath] = useState<string | null>(null);
|
|
87
|
+
const [notesSidebarMode, setNotesSidebarMode] = useState<NotesSidebarMode>('files');
|
|
88
|
+
const [notesSearchFocusToken, setNotesSearchFocusToken] = useState(0);
|
|
87
89
|
const [dashboardSettingsSection, setDashboardSettingsSection] = useState<DashboardSettingsSection>('display');
|
|
88
90
|
const [boardView, setBoardView] = useState<BoardView>({ kind: 'overall' });
|
|
89
91
|
const [scheduleGroup, setScheduleGroup] = useState<ScheduleGroup>('today');
|
|
@@ -341,6 +343,11 @@ export function App() {
|
|
|
341
343
|
view.setNotesSelectedPath(path); void saveUi({ notesSelectedPath: path });
|
|
342
344
|
}
|
|
343
345
|
|
|
346
|
+
function openNotesSidebarSearch(): void {
|
|
347
|
+
setNotesSidebarMode('search');
|
|
348
|
+
setNotesSearchFocusToken(token => token + 1);
|
|
349
|
+
}
|
|
350
|
+
|
|
344
351
|
function handleNotesViewModeChange(mode: DashboardNotesViewMode): void {
|
|
345
352
|
view.setNotesViewMode(mode); void saveUi({ notesViewMode: mode });
|
|
346
353
|
}
|
|
@@ -517,7 +524,7 @@ export function App() {
|
|
|
517
524
|
{view.sidebarMode === 'settings' ? (
|
|
518
525
|
<DashboardSettingsSidebar activeSection={dashboardSettingsSection} locale={view.locale} onSectionChange={setDashboardSettingsSection} />
|
|
519
526
|
) : view.sidebarMode === 'notes' ? (
|
|
520
|
-
<NotesSidebar tree={notesModel.tree} loading={notesModel.loading} error={notesModel.error} notesRoot={notesModel.notesRoot} selectedPath={view.notesSelectedPath} dirtyPath={notesDirtyPath} treeWidth={view.notesTreeWidth} onSelectedPathChange={handleNotesSelectedPathChange} onRefreshTree={notesModel.refresh} />
|
|
527
|
+
<NotesSidebar tree={notesModel.tree} loading={notesModel.loading} error={notesModel.error} notesRoot={notesModel.notesRoot} selectedPath={view.notesSelectedPath} dirtyPath={notesDirtyPath} treeWidth={view.notesTreeWidth} mode={notesSidebarMode} searchFocusToken={notesSearchFocusToken} onModeChange={setNotesSidebarMode} onOpenSearch={openNotesSidebarSearch} onSelectedPathChange={handleNotesSelectedPathChange} onRefreshTree={notesModel.refresh} />
|
|
521
528
|
) : view.sidebarMode === 'board' ? (
|
|
522
529
|
<DashboardBoardSidebar view={boardView} onViewChange={setBoardView} instances={instances} titlesByPort={messageActivity.titlesByPort} busyPorts={messageActivity.busyPorts} />
|
|
523
530
|
) : SCHEDULE_WORKSPACE_ENABLED && view.sidebarMode === 'schedule' ? (
|
|
@@ -552,7 +559,7 @@ export function App() {
|
|
|
552
559
|
)} logs={detailContent('logs')} settings={detailContent('settings')} />
|
|
553
560
|
</WorkspaceSurface>
|
|
554
561
|
<WorkspaceSurface active={view.sidebarMode === 'notes'}>
|
|
555
|
-
<NotesWorkspace active={view.sidebarMode === 'notes'} selectedPath={view.notesSelectedPath} vaultIndex={notesModel.index} viewMode={view.notesViewMode} authoringMode={view.notesAuthoringMode} wordWrap={view.notesWordWrap} treeWidth={view.notesTreeWidth} onSelectedPathChange={handleNotesSelectedPathChange} onDirtyPathChange={setNotesDirtyPath} onViewModeChange={handleNotesViewModeChange} onAuthoringModeChange={handleNotesAuthoringModeChange} onWordWrapChange={handleNotesWordWrapChange} onTreeWidthChange={handleNotesTreeWidthChange} />
|
|
562
|
+
<NotesWorkspace active={view.sidebarMode === 'notes'} selectedPath={view.notesSelectedPath} vaultIndex={notesModel.index} viewMode={view.notesViewMode} authoringMode={view.notesAuthoringMode} wordWrap={view.notesWordWrap} treeWidth={view.notesTreeWidth} onOpenSidebarSearch={openNotesSidebarSearch} onSelectedPathChange={handleNotesSelectedPathChange} onDirtyPathChange={setNotesDirtyPath} onViewModeChange={handleNotesViewModeChange} onAuthoringModeChange={handleNotesAuthoringModeChange} onWordWrapChange={handleNotesWordWrapChange} onTreeWidthChange={handleNotesTreeWidthChange} />
|
|
556
563
|
</WorkspaceSurface>
|
|
557
564
|
<WorkspaceSurface active={view.sidebarMode === 'settings'}>
|
|
558
565
|
<DashboardSettingsWorkspace activeSection={dashboardSettingsSection} ui={dashboardSettingsUi} titleSupport={titleSupport} onUiPatch={handleDashboardSettingsPatch} />
|