@nextclaw/ui 0.15.23 → 0.15.25
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/CHANGELOG.md +38 -0
- package/dist/assets/{api-Glw2OGXK.js → api-FK7tikI1.js} +4 -4
- package/dist/assets/appearance-settings-page-BYbvl2wj.js +1 -0
- package/dist/assets/{channels-list-page-C7E4Sxwd.js → channels-list-page-CocdcIko.js} +2 -2
- package/dist/assets/{chat-page-BT6dNI-4.js → chat-page-C5E-LLVd.js} +1 -1
- package/dist/assets/config-split-page-CA_a0AJ5.js +1 -0
- package/dist/assets/confirm-dialog-CxZfenjK.js +5 -0
- package/dist/assets/desktop-update-config-BEKC8Ly9.js +1 -0
- package/dist/assets/{dist-dv_SUR54.js → dist-BH7W5yCi.js} +1 -1
- package/dist/assets/{dist-CtLyEJeO.js → dist-CaYdVVeO.js} +1 -1
- package/dist/assets/doc-browser-Cl2ldE3f.js +1 -0
- package/dist/assets/doc-browser-DXCSd63E.js +1 -0
- package/dist/assets/{doc-browser-context-BIBjkwfV.js → doc-browser-context-FDPs3gJf.js} +1 -1
- package/dist/assets/form-actions-0c0aw_bl.js +1 -0
- package/dist/assets/i18n-provider-DkITJNz8.js +1 -0
- package/dist/assets/index-CQ2LcI2p.css +1 -0
- package/dist/assets/index-CrdfvBVg.js +107 -0
- package/dist/assets/loader-circle-DHtUxCIA.js +1 -0
- package/dist/assets/mcp-marketplace-page-Bc_jI5iH.js +9 -0
- package/dist/assets/mcp-marketplace-page-CzCieD9r.js +1 -0
- package/dist/assets/model-config-page-C4jw-pcj.js +1 -0
- package/dist/assets/{navigation-link-CB0OQbnY.js → navigation-link-CVy0_kVl.js} +2 -2
- package/dist/assets/plus-CG1RwZQq.js +1 -0
- package/dist/assets/provider-scoped-model-input-CmH4BFvc.js +1 -0
- package/dist/assets/providers-config-page-CJQjwzaw.js +1 -0
- package/dist/assets/react-DjFBVA0n.js +8 -0
- package/dist/assets/{refresh-cw-Ba0LZou4.js → refresh-cw-j2bl79Ea.js} +1 -1
- package/dist/assets/remote-BIlpD3yL.js +1 -0
- package/dist/assets/rotate-cw-bKG8zH8M.js +1 -0
- package/dist/assets/runtime-config-page-B_x2Glz5.js +1 -0
- package/dist/assets/{save-DTOAvqzt.js → save-BKNrtK81.js} +1 -1
- package/dist/assets/{search-D4X5DNMe.js → search-CkOm65W_.js} +1 -1
- package/dist/assets/{search-config-page-Cr6254K5.js → search-config-page-BaIijtmO.js} +1 -1
- package/dist/assets/{secrets-config-page-W2Hx332d.js → secrets-config-page-CJjtdFAl.js} +2 -2
- package/dist/assets/security-config-DB8apj09.js +1 -0
- package/dist/assets/{select-CDv0g2Pj.js → select-Ea7ffKCE.js} +2 -2
- package/dist/assets/{settings-2-CoxzNABp.js → settings-2-CtyN3m_V.js} +1 -1
- package/dist/assets/skeleton-Bt6zxcAB.js +1 -0
- package/dist/assets/{tag-chip-CjkZEoS2.js → tag-chip-C9l4xsKp.js} +1 -1
- package/dist/assets/x-CowtCu9Z.js +1 -0
- package/dist/index.html +20 -20
- package/package.json +8 -8
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +30 -1
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +1 -1
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-provider-model-catalog.test.tsx +54 -2
- package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +10 -1
- package/src/features/chat/features/conversation/hooks/use-session-provider-model-catalog.ts +76 -1
- package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +6 -1
- package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +29 -0
- package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +12 -3
- package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +5 -0
- package/src/features/chat/features/input/utils/chat-conversation-excerpt-token.utils.ts +59 -0
- package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +42 -16
- package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +50 -14
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +40 -0
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +5 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +40 -3
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.selection.test.tsx +66 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +13 -1
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +573 -90
- package/src/features/chat/features/workspace/components/__tests__/workspace-text-selection-menu.test.tsx +11 -4
- package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +389 -288
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +54 -35
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +79 -148
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +149 -94
- package/src/features/chat/features/workspace/components/project-files/__tests__/workspace-directory-entry-menu.test.tsx +50 -0
- package/src/features/chat/features/workspace/components/project-files/workspace-directory-entry-menu.tsx +209 -0
- package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +411 -0
- package/src/features/chat/features/workspace/components/project-files/workspace-new-folder-tree-item.tsx +112 -0
- package/src/features/chat/features/workspace/components/project-files/workspace-project-files-toolbar.tsx +64 -0
- package/src/features/chat/features/workspace/components/workspace-text-selection-menu.tsx +17 -195
- package/src/features/chat/features/workspace/hooks/use-workspace-explorer-layout.ts +88 -0
- package/src/features/chat/features/workspace/hooks/use-workspace-file-actions.ts +335 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-file-path-mutation.utils.ts +58 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +23 -22
- package/src/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils.ts +13 -4
- package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +94 -45
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +103 -99
- package/src/features/chat/managers/chat-composer-intent.manager.ts +83 -34
- package/src/features/chat/managers/chat-thread.manager.ts +132 -128
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +20 -40
- package/src/features/chat/stores/chat-thread.store.ts +47 -74
- package/src/features/chat/types/chat-input-bar.types.ts +3 -0
- package/src/features/side-dock/components/__tests__/side-dock.test.tsx +1 -0
- package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +0 -9
- package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx +35 -38
- package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +25 -19
- package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +60 -34
- package/src/shared/components/ui/context-menu/context-menu.tsx +95 -68
- package/src/shared/lib/api/managers/client.manager.ts +31 -17
- package/src/shared/lib/api/server-path/server-path.types.ts +66 -0
- package/src/shared/lib/api/types.ts +145 -116
- package/src/shared/lib/api/utils/server-path.utils.ts +35 -11
- package/src/shared/lib/i18n/locales/en-US/chat.json +49 -1
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +49 -1
- package/dist/assets/appearance-settings-page-Bl_W_9m7.js +0 -1
- package/dist/assets/config-split-page-D5BgtKrm.js +0 -1
- package/dist/assets/confirm-dialog-Bf907Azu.js +0 -5
- package/dist/assets/desktop-update-config-BwuyBPJb.js +0 -1
- package/dist/assets/doc-browser-C23NDGh5.js +0 -1
- package/dist/assets/doc-browser-DlOOhBxI.js +0 -1
- package/dist/assets/form-actions-DAzi68Cn.js +0 -1
- package/dist/assets/index-BLNg2yg0.css +0 -1
- package/dist/assets/index-D3EKjEyK.js +0 -107
- package/dist/assets/loader-circle-BSg5u0r4.js +0 -1
- package/dist/assets/mcp-marketplace-page-ByhvQQj5.js +0 -9
- package/dist/assets/mcp-marketplace-page-CytXwnUs.js +0 -1
- package/dist/assets/model-config-page-bgEx3MjQ.js +0 -1
- package/dist/assets/plus-CNjWdLhZ.js +0 -1
- package/dist/assets/provider-scoped-model-input-8F6JMd2n.js +0 -1
- package/dist/assets/providers-config-page-CxpTjR81.js +0 -1
- package/dist/assets/react-Bk2ydNTe.js +0 -8
- package/dist/assets/remote-Dv38PuMV.js +0 -1
- package/dist/assets/rotate-cw-IkYVk9JX.js +0 -1
- package/dist/assets/runtime-config-page-C0ByyUVr.js +0 -1
- package/dist/assets/security-config-1E8OF4kR.js +0 -1
- package/dist/assets/skeleton-DW018fJv.js +0 -1
- package/dist/assets/use-confirm-dialog-CavtY1vJ.js +0 -1
- package/dist/assets/x-PTXYTEwz.js +0 -1
|
@@ -6,7 +6,10 @@ import { useChatThreadStore } from '@/features/chat/stores/chat-thread.store';
|
|
|
6
6
|
import type * as SharedApi from '@/shared/lib/api';
|
|
7
7
|
|
|
8
8
|
const { deleteNcpSessionMock, deleteSummaryMock } = vi.hoisted(() => ({
|
|
9
|
-
deleteNcpSessionMock: vi.fn(async () => ({
|
|
9
|
+
deleteNcpSessionMock: vi.fn(async () => ({
|
|
10
|
+
deleted: true,
|
|
11
|
+
sessionId: 'parent-session-1',
|
|
12
|
+
})),
|
|
10
13
|
deleteSummaryMock: vi.fn(),
|
|
11
14
|
}));
|
|
12
15
|
const persistStorage = new Map<string, unknown>();
|
|
@@ -34,8 +37,12 @@ vi.mock('@/shared/lib/api', async (importOriginal) => {
|
|
|
34
37
|
|
|
35
38
|
beforeEach(() => {
|
|
36
39
|
persistStorage.clear();
|
|
37
|
-
useChatSessionListStore.persist.setOptions({
|
|
38
|
-
|
|
40
|
+
useChatSessionListStore.persist.setOptions({
|
|
41
|
+
storage: createPersistStorage() as never,
|
|
42
|
+
});
|
|
43
|
+
useChatThreadStore.persist.setOptions({
|
|
44
|
+
storage: createPersistStorage() as never,
|
|
45
|
+
});
|
|
39
46
|
useChatSessionListStore.setState({
|
|
40
47
|
optimisticReadAtBySessionKey: {},
|
|
41
48
|
snapshot: {
|
|
@@ -67,9 +74,7 @@ beforeEach(() => {
|
|
|
67
74
|
});
|
|
68
75
|
});
|
|
69
76
|
|
|
70
|
-
function createUiManager(
|
|
71
|
-
overrides: Record<string, unknown> = {},
|
|
72
|
-
): ConstructorParameters<typeof ChatThreadManager>[0] {
|
|
77
|
+
function createUiManager(overrides: Record<string, unknown> = {}): ConstructorParameters<typeof ChatThreadManager>[0] {
|
|
73
78
|
return {
|
|
74
79
|
goToSession: vi.fn(),
|
|
75
80
|
goToChatRoot: vi.fn(),
|
|
@@ -90,10 +95,7 @@ describe('ChatThreadManager', () => {
|
|
|
90
95
|
confirm: vi.fn(),
|
|
91
96
|
} as unknown as ConstructorParameters<typeof ChatThreadManager>[0];
|
|
92
97
|
|
|
93
|
-
const manager = new ChatThreadManager(
|
|
94
|
-
uiManager,
|
|
95
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
96
|
-
);
|
|
98
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
97
99
|
|
|
98
100
|
manager.openChildSessionPanel({
|
|
99
101
|
parentSessionKey: 'parent-session-1',
|
|
@@ -119,10 +121,7 @@ describe('ChatThreadManager', () => {
|
|
|
119
121
|
confirm: vi.fn(),
|
|
120
122
|
} as unknown as ConstructorParameters<typeof ChatThreadManager>[0];
|
|
121
123
|
|
|
122
|
-
const manager = new ChatThreadManager(
|
|
123
|
-
uiManager,
|
|
124
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
125
|
-
);
|
|
124
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
126
125
|
|
|
127
126
|
manager.openChildSessionPanel({
|
|
128
127
|
parentSessionKey: 'parent-session-1',
|
|
@@ -140,10 +139,7 @@ describe('ChatThreadManager', () => {
|
|
|
140
139
|
confirm: vi.fn(),
|
|
141
140
|
} as unknown as ConstructorParameters<typeof ChatThreadManager>[0];
|
|
142
141
|
|
|
143
|
-
const manager = new ChatThreadManager(
|
|
144
|
-
uiManager,
|
|
145
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
146
|
-
);
|
|
142
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
147
143
|
|
|
148
144
|
manager.openSessionCronPanel('parent-session-1');
|
|
149
145
|
|
|
@@ -194,10 +190,7 @@ describe('ChatThreadManager', () => {
|
|
|
194
190
|
const uiManager = createUiManager({
|
|
195
191
|
goToSession: vi.fn(),
|
|
196
192
|
});
|
|
197
|
-
const manager = new ChatThreadManager(
|
|
198
|
-
uiManager,
|
|
199
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
200
|
-
);
|
|
193
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
201
194
|
|
|
202
195
|
manager.openSideChatDraft('parent-session-1');
|
|
203
196
|
|
|
@@ -223,10 +216,7 @@ describe('ChatThreadManager', () => {
|
|
|
223
216
|
});
|
|
224
217
|
|
|
225
218
|
it('materializes an active side chat draft into a real child-session tab', () => {
|
|
226
|
-
const manager = new ChatThreadManager(
|
|
227
|
-
createUiManager(),
|
|
228
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
229
|
-
);
|
|
219
|
+
const manager = new ChatThreadManager(createUiManager(), {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
230
220
|
|
|
231
221
|
manager.openSideChatDraft('parent-session-1');
|
|
232
222
|
const draftKey = useChatThreadStore.getState().snapshot.activeSideChatDraft?.draftKey;
|
|
@@ -256,9 +246,7 @@ describe('ChatThreadManager', () => {
|
|
|
256
246
|
sessionKey: 'child-session-1',
|
|
257
247
|
},
|
|
258
248
|
],
|
|
259
|
-
workspaceNavigationHistory: [
|
|
260
|
-
{ kind: 'child-session', key: 'child-session-side' },
|
|
261
|
-
],
|
|
249
|
+
workspaceNavigationHistory: [{ kind: 'child-session', key: 'child-session-side' }],
|
|
262
250
|
workspaceNavigationHistoryIndex: 0,
|
|
263
251
|
});
|
|
264
252
|
});
|
|
@@ -277,10 +265,7 @@ describe('ChatThreadManager', () => {
|
|
|
277
265
|
confirm: vi.fn(),
|
|
278
266
|
} as unknown as ConstructorParameters<typeof ChatThreadManager>[0];
|
|
279
267
|
|
|
280
|
-
const manager = new ChatThreadManager(
|
|
281
|
-
uiManager,
|
|
282
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
283
|
-
);
|
|
268
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
284
269
|
|
|
285
270
|
manager.openSessionCronPanel('parent-session-1');
|
|
286
271
|
|
|
@@ -295,10 +280,7 @@ describe('ChatThreadManager', () => {
|
|
|
295
280
|
confirm: vi.fn(),
|
|
296
281
|
} as unknown as ConstructorParameters<typeof ChatThreadManager>[0];
|
|
297
282
|
|
|
298
|
-
const manager = new ChatThreadManager(
|
|
299
|
-
uiManager,
|
|
300
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
301
|
-
);
|
|
283
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
302
284
|
|
|
303
285
|
manager.openFilePreview({
|
|
304
286
|
path: 'chart.html',
|
|
@@ -331,39 +313,40 @@ describe('ChatThreadManager', () => {
|
|
|
331
313
|
]),
|
|
332
314
|
);
|
|
333
315
|
});
|
|
334
|
-
|
|
335
316
|
});
|
|
336
317
|
|
|
337
318
|
describe('ChatThreadManager workspace file tabs', () => {
|
|
338
319
|
it.each([
|
|
339
320
|
{ path: 'demo.html', previewViewer: undefined, expectedViewer: 'source' },
|
|
340
|
-
{
|
|
321
|
+
{
|
|
322
|
+
path: 'auto.html',
|
|
323
|
+
previewViewer: 'auto' as const,
|
|
324
|
+
expectedViewer: 'source',
|
|
325
|
+
},
|
|
341
326
|
{ path: 'README.md', previewViewer: undefined, expectedViewer: 'rendered' },
|
|
342
|
-
{
|
|
327
|
+
{
|
|
328
|
+
path: 'report.docx',
|
|
329
|
+
previewViewer: 'auto' as const,
|
|
330
|
+
expectedViewer: 'auto',
|
|
331
|
+
},
|
|
343
332
|
])('normalizes the viewer for $path', ({ path, previewViewer, expectedViewer }) => {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
path,
|
|
358
|
-
previewViewer: expectedViewer,
|
|
359
|
-
});
|
|
333
|
+
const manager = new ChatThreadManager(createUiManager(), {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
334
|
+
|
|
335
|
+
manager.openFilePreview({
|
|
336
|
+
path,
|
|
337
|
+
label: path,
|
|
338
|
+
viewMode: 'preview',
|
|
339
|
+
previewViewer,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
expect(useChatThreadStore.getState().snapshot.workspaceFileTabs[0]).toMatchObject({
|
|
343
|
+
path,
|
|
344
|
+
previewViewer: expectedViewer,
|
|
345
|
+
});
|
|
360
346
|
});
|
|
361
347
|
|
|
362
348
|
it('opens source and rendered previews for the same file as adjacent workspace tabs', () => {
|
|
363
|
-
const manager = new ChatThreadManager(
|
|
364
|
-
createUiManager(),
|
|
365
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
366
|
-
);
|
|
349
|
+
const manager = new ChatThreadManager(createUiManager(), {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
367
350
|
|
|
368
351
|
manager.openFilePreview({
|
|
369
352
|
path: 'demo.html',
|
|
@@ -403,6 +386,56 @@ describe('ChatThreadManager workspace file tabs', () => {
|
|
|
403
386
|
]),
|
|
404
387
|
});
|
|
405
388
|
});
|
|
389
|
+
|
|
390
|
+
it('remaps open file tabs and navigation history after a file or folder rename', () => {
|
|
391
|
+
const manager = new ChatThreadManager(createUiManager(), {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
392
|
+
manager.openFilePreview({
|
|
393
|
+
path: '/workspace/docs/guide.md',
|
|
394
|
+
label: 'guide.md',
|
|
395
|
+
viewMode: 'preview',
|
|
396
|
+
});
|
|
397
|
+
const previousKey = useChatThreadStore.getState().snapshot.activeWorkspaceFileKey!;
|
|
398
|
+
|
|
399
|
+
manager.renameWorkspacePath({
|
|
400
|
+
previousPath: '/workspace/docs',
|
|
401
|
+
path: '/workspace/designs',
|
|
402
|
+
label: 'designs',
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const { snapshot } = useChatThreadStore.getState();
|
|
406
|
+
expect(snapshot.workspaceFileTabs[0]).toMatchObject({
|
|
407
|
+
path: '/workspace/designs/guide.md',
|
|
408
|
+
label: 'guide.md',
|
|
409
|
+
});
|
|
410
|
+
expect(snapshot.activeWorkspaceFileKey).toBe('parent-session-1::preview:rendered::/workspace/designs/guide.md');
|
|
411
|
+
expect(snapshot.workspaceNavigationHistory).toContainEqual({
|
|
412
|
+
kind: 'file',
|
|
413
|
+
key: snapshot.activeWorkspaceFileKey,
|
|
414
|
+
});
|
|
415
|
+
expect(snapshot.activeWorkspaceFileKey).not.toBe(previousKey);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('closes every open tab below a deleted path and restores the prior file', () => {
|
|
419
|
+
const manager = new ChatThreadManager(createUiManager(), {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
420
|
+
manager.openFilePreview({
|
|
421
|
+
path: '/workspace/README.md',
|
|
422
|
+
label: 'README.md',
|
|
423
|
+
viewMode: 'preview',
|
|
424
|
+
});
|
|
425
|
+
manager.openFilePreview({
|
|
426
|
+
path: '/workspace/docs/guide.md',
|
|
427
|
+
label: 'guide.md',
|
|
428
|
+
viewMode: 'preview',
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
manager.removeWorkspacePath('/workspace/docs');
|
|
432
|
+
|
|
433
|
+
expect(useChatThreadStore.getState().snapshot).toMatchObject({
|
|
434
|
+
activeWorkspacePanelKind: 'file',
|
|
435
|
+
activeWorkspaceFileKey: 'parent-session-1::preview:rendered::/workspace/README.md',
|
|
436
|
+
workspaceFileTabs: [expect.objectContaining({ path: '/workspace/README.md' })],
|
|
437
|
+
});
|
|
438
|
+
});
|
|
406
439
|
});
|
|
407
440
|
|
|
408
441
|
describe('ChatThreadManager workspace navigation', () => {
|
|
@@ -413,10 +446,7 @@ describe('ChatThreadManager workspace navigation', () => {
|
|
|
413
446
|
goToProviders: vi.fn(),
|
|
414
447
|
confirm: vi.fn(),
|
|
415
448
|
} as unknown as ConstructorParameters<typeof ChatThreadManager>[0];
|
|
416
|
-
const manager = new ChatThreadManager(
|
|
417
|
-
uiManager,
|
|
418
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
419
|
-
);
|
|
449
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
420
450
|
|
|
421
451
|
manager.openChildSessionPanel({
|
|
422
452
|
parentSessionKey: 'parent-session-1',
|
|
@@ -517,9 +547,7 @@ describe('ChatThreadManager workspace navigation', () => {
|
|
|
517
547
|
activeWorkspacePanelKind: 'child-session',
|
|
518
548
|
activeChildSessionKey: 'child-session-1',
|
|
519
549
|
activeWorkspaceFileKey: null,
|
|
520
|
-
workspaceNavigationHistory: [
|
|
521
|
-
{ kind: 'child-session', key: 'child-session-1' },
|
|
522
|
-
],
|
|
550
|
+
workspaceNavigationHistory: [{ kind: 'child-session', key: 'child-session-1' }],
|
|
523
551
|
workspaceNavigationHistoryIndex: 0,
|
|
524
552
|
});
|
|
525
553
|
});
|
|
@@ -535,10 +563,7 @@ describe('ChatThreadManager tool actions', () => {
|
|
|
535
563
|
const uiManager = createUiManager({
|
|
536
564
|
goToSession: vi.fn(),
|
|
537
565
|
});
|
|
538
|
-
const manager = new ChatThreadManager(
|
|
539
|
-
uiManager,
|
|
540
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
541
|
-
);
|
|
566
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
542
567
|
|
|
543
568
|
await manager.handleToolAction({
|
|
544
569
|
kind: 'open-session',
|
|
@@ -562,9 +587,7 @@ describe('ChatThreadManager tool actions', () => {
|
|
|
562
587
|
agentId: 'verifier-agent',
|
|
563
588
|
},
|
|
564
589
|
],
|
|
565
|
-
workspaceNavigationHistory: [
|
|
566
|
-
{ kind: 'child-session', key: 'child-session-9' },
|
|
567
|
-
],
|
|
590
|
+
workspaceNavigationHistory: [{ kind: 'child-session', key: 'child-session-9' }],
|
|
568
591
|
});
|
|
569
592
|
expect(uiManager.goToSession).not.toHaveBeenCalled();
|
|
570
593
|
});
|
|
@@ -575,10 +598,7 @@ describe('ChatThreadManager showContent', () => {
|
|
|
575
598
|
const uiManager = createUiManager({
|
|
576
599
|
goToSession: vi.fn(),
|
|
577
600
|
});
|
|
578
|
-
const manager = new ChatThreadManager(
|
|
579
|
-
uiManager,
|
|
580
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
581
|
-
);
|
|
601
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
582
602
|
|
|
583
603
|
await manager.handleToolAction({
|
|
584
604
|
kind: 'show-content',
|
|
@@ -652,10 +672,7 @@ describe('ChatThreadManager showContent', () => {
|
|
|
652
672
|
|
|
653
673
|
it('shows URL and panel app content through DocBrowser', async () => {
|
|
654
674
|
const uiManager = createUiManager();
|
|
655
|
-
const manager = new ChatThreadManager(
|
|
656
|
-
uiManager,
|
|
657
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
658
|
-
);
|
|
675
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
659
676
|
|
|
660
677
|
await manager.handleToolAction({
|
|
661
678
|
kind: 'show-content',
|
|
@@ -706,10 +723,7 @@ describe('ChatThreadManager showContent', () => {
|
|
|
706
723
|
|
|
707
724
|
it('handles ui.show-content events once through the same owner path', async () => {
|
|
708
725
|
const uiManager = createUiManager();
|
|
709
|
-
const manager = new ChatThreadManager(
|
|
710
|
-
uiManager,
|
|
711
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
712
|
-
);
|
|
726
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
713
727
|
|
|
714
728
|
await manager.handleUiShowContentEvent({
|
|
715
729
|
id: 'tool:call-show-content-1:show-content',
|
|
@@ -752,10 +766,7 @@ describe('ChatThreadManager showContent', () => {
|
|
|
752
766
|
|
|
753
767
|
it('delegates panel app content to the UI manager owner', async () => {
|
|
754
768
|
const uiManager = createUiManager();
|
|
755
|
-
const manager = new ChatThreadManager(
|
|
756
|
-
uiManager,
|
|
757
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
758
|
-
);
|
|
769
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
759
770
|
|
|
760
771
|
await manager.handleToolAction({
|
|
761
772
|
kind: 'show-content',
|
|
@@ -781,16 +792,12 @@ describe('ChatThreadManager showContent', () => {
|
|
|
781
792
|
title: 'Reader',
|
|
782
793
|
});
|
|
783
794
|
});
|
|
784
|
-
|
|
785
795
|
});
|
|
786
796
|
|
|
787
797
|
describe('ChatThreadManager inline showContent', () => {
|
|
788
798
|
it('does not auto-open inline panel app content outside the message card', async () => {
|
|
789
799
|
const uiManager = createUiManager();
|
|
790
|
-
const manager = new ChatThreadManager(
|
|
791
|
-
uiManager,
|
|
792
|
-
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
793
|
-
);
|
|
800
|
+
const manager = new ChatThreadManager(uiManager, {} as ConstructorParameters<typeof ChatThreadManager>[1]);
|
|
794
801
|
|
|
795
802
|
await manager.handleUiShowContentEvent({
|
|
796
803
|
id: 'tool:call-show-content-inline:show-content',
|
|
@@ -845,10 +852,7 @@ describe('ChatThreadManager deletion', () => {
|
|
|
845
852
|
confirm: vi.fn(async () => true),
|
|
846
853
|
} as unknown as ConstructorParameters<typeof ChatThreadManager>[0];
|
|
847
854
|
const sessionListManager = {} as ConstructorParameters<typeof ChatThreadManager>[1];
|
|
848
|
-
const manager = new ChatThreadManager(
|
|
849
|
-
uiManager,
|
|
850
|
-
sessionListManager,
|
|
851
|
-
);
|
|
855
|
+
const manager = new ChatThreadManager(uiManager, sessionListManager);
|
|
852
856
|
|
|
853
857
|
await manager.deleteSession();
|
|
854
858
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
|
|
3
|
+
CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
|
|
2
4
|
CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
|
|
3
5
|
CHAT_WORKSPACE_FILE_TOKEN_KIND,
|
|
4
|
-
} from
|
|
6
|
+
} from '@nextclaw/shared';
|
|
5
7
|
|
|
6
8
|
type ChatComposerReferenceIntentBase = {
|
|
7
9
|
id: number;
|
|
@@ -14,6 +16,10 @@ export type ChatComposerFileReferenceIntent = ChatComposerReferenceIntentBase &
|
|
|
14
16
|
kind: typeof CHAT_WORKSPACE_FILE_TOKEN_KIND;
|
|
15
17
|
};
|
|
16
18
|
|
|
19
|
+
export type ChatComposerDirectoryReferenceIntent = ChatComposerReferenceIntentBase & {
|
|
20
|
+
kind: typeof CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND;
|
|
21
|
+
};
|
|
22
|
+
|
|
17
23
|
export type ChatComposerExcerptReferenceIntent = ChatComposerReferenceIntentBase & {
|
|
18
24
|
kind: typeof CHAT_WORKSPACE_EXCERPT_TOKEN_KIND;
|
|
19
25
|
path: string;
|
|
@@ -22,13 +28,33 @@ export type ChatComposerExcerptReferenceIntent = ChatComposerReferenceIntentBase
|
|
|
22
28
|
endLine: number | null;
|
|
23
29
|
};
|
|
24
30
|
|
|
31
|
+
export type ChatComposerConversationExcerptIntent = ChatComposerReferenceIntentBase & {
|
|
32
|
+
kind: typeof CHAT_CONVERSATION_EXCERPT_TOKEN_KIND;
|
|
33
|
+
messageId: string;
|
|
34
|
+
role: 'assistant' | 'user';
|
|
35
|
+
excerpt: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
25
38
|
export type ChatComposerReferenceIntent =
|
|
26
39
|
| ChatComposerFileReferenceIntent
|
|
27
|
-
|
|
|
40
|
+
| ChatComposerDirectoryReferenceIntent
|
|
41
|
+
| ChatComposerExcerptReferenceIntent
|
|
42
|
+
| ChatComposerConversationExcerptIntent;
|
|
28
43
|
|
|
29
44
|
type ChatComposerReferenceRequest =
|
|
30
|
-
| Omit<ChatComposerFileReferenceIntent,
|
|
31
|
-
| Omit<
|
|
45
|
+
| Omit<ChatComposerFileReferenceIntent, 'id'>
|
|
46
|
+
| Omit<ChatComposerDirectoryReferenceIntent, 'id'>
|
|
47
|
+
| Omit<ChatComposerExcerptReferenceIntent, 'id'>
|
|
48
|
+
| Omit<ChatComposerConversationExcerptIntent, 'id'>;
|
|
49
|
+
|
|
50
|
+
function createExcerptTokenKey(prefix: string, identity: string): string {
|
|
51
|
+
let hash = 2166136261;
|
|
52
|
+
for (let index = 0; index < identity.length; index += 1) {
|
|
53
|
+
hash ^= identity.charCodeAt(index);
|
|
54
|
+
hash = Math.imul(hash, 16777619);
|
|
55
|
+
}
|
|
56
|
+
return `${prefix}#excerpt-${(hash >>> 0).toString(36)}`;
|
|
57
|
+
}
|
|
32
58
|
|
|
33
59
|
type ChatComposerIntentListener = (intent: ChatComposerReferenceIntent) => void;
|
|
34
60
|
|
|
@@ -45,9 +71,11 @@ export class ChatComposerIntentManager {
|
|
|
45
71
|
private publish = (intent: ChatComposerReferenceRequest) => {
|
|
46
72
|
const id = this.nextId + 1;
|
|
47
73
|
const targetSessionKey = intent.targetSessionKey?.trim() || null;
|
|
48
|
-
const nextIntent: ChatComposerReferenceIntent =
|
|
49
|
-
|
|
50
|
-
|
|
74
|
+
const nextIntent: ChatComposerReferenceIntent = {
|
|
75
|
+
...intent,
|
|
76
|
+
id,
|
|
77
|
+
targetSessionKey,
|
|
78
|
+
};
|
|
51
79
|
this.nextId = nextIntent.id;
|
|
52
80
|
this.pendingIntent = nextIntent;
|
|
53
81
|
this.subscriptions.forEach((subscription) => {
|
|
@@ -57,11 +85,7 @@ export class ChatComposerIntentManager {
|
|
|
57
85
|
});
|
|
58
86
|
};
|
|
59
87
|
|
|
60
|
-
requestFileReference = (params: {
|
|
61
|
-
targetSessionKey: string | null;
|
|
62
|
-
tokenKey: string;
|
|
63
|
-
label: string;
|
|
64
|
-
}) => {
|
|
88
|
+
requestFileReference = (params: { targetSessionKey: string | null; tokenKey: string; label: string }) => {
|
|
65
89
|
const tokenKey = params.tokenKey.trim();
|
|
66
90
|
const label = params.label.trim();
|
|
67
91
|
if (!tokenKey || !label) {
|
|
@@ -75,6 +99,20 @@ export class ChatComposerIntentManager {
|
|
|
75
99
|
});
|
|
76
100
|
};
|
|
77
101
|
|
|
102
|
+
requestDirectoryReference = (params: { targetSessionKey: string | null; tokenKey: string; label: string }) => {
|
|
103
|
+
const tokenKey = params.tokenKey.trim();
|
|
104
|
+
const label = params.label.trim();
|
|
105
|
+
if (!tokenKey || !label) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.publish({
|
|
109
|
+
kind: CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
|
|
110
|
+
targetSessionKey: params.targetSessionKey?.trim() || null,
|
|
111
|
+
tokenKey,
|
|
112
|
+
label,
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
|
|
78
116
|
requestExcerptReference = (params: {
|
|
79
117
|
targetSessionKey: string | null;
|
|
80
118
|
path: string;
|
|
@@ -83,30 +121,17 @@ export class ChatComposerIntentManager {
|
|
|
83
121
|
startLine: number | null;
|
|
84
122
|
endLine: number | null;
|
|
85
123
|
}) => {
|
|
86
|
-
const {
|
|
87
|
-
endLine,
|
|
88
|
-
excerpt: rawExcerpt,
|
|
89
|
-
label: rawLabel,
|
|
90
|
-
path: rawPath,
|
|
91
|
-
startLine,
|
|
92
|
-
targetSessionKey,
|
|
93
|
-
} = params;
|
|
124
|
+
const { endLine, excerpt: rawExcerpt, label: rawLabel, path: rawPath, startLine, targetSessionKey } = params;
|
|
94
125
|
const path = rawPath.trim();
|
|
95
126
|
const label = rawLabel.trim();
|
|
96
127
|
const excerpt = rawExcerpt.trim();
|
|
97
128
|
if (!path || !label || !excerpt) {
|
|
98
129
|
return;
|
|
99
130
|
}
|
|
100
|
-
const identity = `${path}:${startLine ?? "x"}:${endLine ?? "x"}:${excerpt}`;
|
|
101
|
-
let hash = 2166136261;
|
|
102
|
-
for (let index = 0; index < identity.length; index += 1) {
|
|
103
|
-
hash ^= identity.charCodeAt(index);
|
|
104
|
-
hash = Math.imul(hash, 16777619);
|
|
105
|
-
}
|
|
106
131
|
this.publish({
|
|
107
132
|
kind: CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
|
|
108
133
|
targetSessionKey,
|
|
109
|
-
tokenKey: `${path}
|
|
134
|
+
tokenKey: createExcerptTokenKey(path, `${path}:${startLine ?? 'x'}:${endLine ?? 'x'}:${excerpt}`),
|
|
110
135
|
path,
|
|
111
136
|
label,
|
|
112
137
|
excerpt,
|
|
@@ -115,9 +140,36 @@ export class ChatComposerIntentManager {
|
|
|
115
140
|
});
|
|
116
141
|
};
|
|
117
142
|
|
|
118
|
-
|
|
119
|
-
targetSessionKey: string | null
|
|
120
|
-
|
|
143
|
+
requestConversationExcerptReference = (params: {
|
|
144
|
+
targetSessionKey: string | null;
|
|
145
|
+
messageId: string;
|
|
146
|
+
role: 'assistant' | 'user';
|
|
147
|
+
label: string;
|
|
148
|
+
excerpt: string;
|
|
149
|
+
}) => {
|
|
150
|
+
const {
|
|
151
|
+
targetSessionKey,
|
|
152
|
+
messageId: requestedMessageId,
|
|
153
|
+
role,
|
|
154
|
+
label: requestedLabel,
|
|
155
|
+
excerpt: requestedExcerpt,
|
|
156
|
+
} = params;
|
|
157
|
+
const messageId = requestedMessageId.trim();
|
|
158
|
+
const label = requestedLabel.trim();
|
|
159
|
+
const excerpt = requestedExcerpt.trim();
|
|
160
|
+
if (!messageId || !label || !excerpt) return;
|
|
161
|
+
this.publish({
|
|
162
|
+
kind: CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
|
|
163
|
+
targetSessionKey,
|
|
164
|
+
tokenKey: createExcerptTokenKey(messageId, `${messageId}:${excerpt}`),
|
|
165
|
+
messageId,
|
|
166
|
+
role,
|
|
167
|
+
label,
|
|
168
|
+
excerpt,
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
consumePending = (targetSessionKey: string | null): ChatComposerReferenceIntent | null => {
|
|
121
173
|
if (this.pendingIntent?.targetSessionKey !== targetSessionKey) {
|
|
122
174
|
return null;
|
|
123
175
|
}
|
|
@@ -132,10 +184,7 @@ export class ChatComposerIntentManager {
|
|
|
132
184
|
}
|
|
133
185
|
};
|
|
134
186
|
|
|
135
|
-
subscribe = (
|
|
136
|
-
targetSessionKey: string | null,
|
|
137
|
-
listener: ChatComposerIntentListener,
|
|
138
|
-
) => {
|
|
187
|
+
subscribe = (targetSessionKey: string | null, listener: ChatComposerIntentListener) => {
|
|
139
188
|
const subscription = { targetSessionKey, listener };
|
|
140
189
|
this.subscriptions.add(subscription);
|
|
141
190
|
return () => {
|