@nextclaw/ui 0.15.24 → 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.
Files changed (94) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/assets/{api-BxPNDp30.js → api-FK7tikI1.js} +3 -3
  3. package/dist/assets/appearance-settings-page-BYbvl2wj.js +1 -0
  4. package/dist/assets/{channels-list-page-DRaNWAxC.js → channels-list-page-CocdcIko.js} +2 -2
  5. package/dist/assets/chat-page-C5E-LLVd.js +1 -0
  6. package/dist/assets/{config-split-page-BVWVDbvG.js → config-split-page-CA_a0AJ5.js} +1 -1
  7. package/dist/assets/{confirm-dialog-mA2iVzxh.js → confirm-dialog-CxZfenjK.js} +1 -1
  8. package/dist/assets/{desktop-update-config-DyQb1LXr.js → desktop-update-config-BEKC8Ly9.js} +1 -1
  9. package/dist/assets/{dist-B4Pf5X3Q.js → dist-BH7W5yCi.js} +1 -1
  10. package/dist/assets/{dist-DHEWkGWX.js → dist-CaYdVVeO.js} +1 -1
  11. package/dist/assets/doc-browser-Cl2ldE3f.js +1 -0
  12. package/dist/assets/doc-browser-DXCSd63E.js +1 -0
  13. package/dist/assets/{doc-browser-context-CuGI6PQc.js → doc-browser-context-FDPs3gJf.js} +1 -1
  14. package/dist/assets/{form-actions-BOfIyG3O.js → form-actions-0c0aw_bl.js} +1 -1
  15. package/dist/assets/i18n-provider-DkITJNz8.js +1 -0
  16. package/dist/assets/index-CQ2LcI2p.css +1 -0
  17. package/dist/assets/index-CrdfvBVg.js +107 -0
  18. package/dist/assets/loader-circle-DHtUxCIA.js +1 -0
  19. package/dist/assets/{mcp-marketplace-page-ofDqbOIF.js → mcp-marketplace-page-Bc_jI5iH.js} +2 -2
  20. package/dist/assets/mcp-marketplace-page-CzCieD9r.js +1 -0
  21. package/dist/assets/{model-config-page-D73Tu3jW.js → model-config-page-C4jw-pcj.js} +1 -1
  22. package/dist/assets/{navigation-link-CAFOcXNg.js → navigation-link-CVy0_kVl.js} +1 -1
  23. package/dist/assets/plus-CG1RwZQq.js +1 -0
  24. package/dist/assets/{provider-scoped-model-input-BaJJJYMX.js → provider-scoped-model-input-CmH4BFvc.js} +1 -1
  25. package/dist/assets/{providers-config-page-BL6IRVBm.js → providers-config-page-CJQjwzaw.js} +1 -1
  26. package/dist/assets/{react-BC_orpAj.js → react-DjFBVA0n.js} +1 -1
  27. package/dist/assets/{refresh-cw-CWDp_JnN.js → refresh-cw-j2bl79Ea.js} +1 -1
  28. package/dist/assets/remote-BIlpD3yL.js +1 -0
  29. package/dist/assets/rotate-cw-bKG8zH8M.js +1 -0
  30. package/dist/assets/runtime-config-page-B_x2Glz5.js +1 -0
  31. package/dist/assets/{save-Cnr71JON.js → save-BKNrtK81.js} +1 -1
  32. package/dist/assets/{search-CCX11X2m.js → search-CkOm65W_.js} +1 -1
  33. package/dist/assets/{search-config-page-BB6d5gT7.js → search-config-page-BaIijtmO.js} +1 -1
  34. package/dist/assets/{secrets-config-page-DYsDeT9K.js → secrets-config-page-CJjtdFAl.js} +2 -2
  35. package/dist/assets/security-config-DB8apj09.js +1 -0
  36. package/dist/assets/{select-DvwBu99K.js → select-Ea7ffKCE.js} +1 -1
  37. package/dist/assets/{settings-2-Dmv6zMne.js → settings-2-CtyN3m_V.js} +1 -1
  38. package/dist/assets/skeleton-Bt6zxcAB.js +1 -0
  39. package/dist/assets/{tag-chip-gMPvx7n_.js → tag-chip-C9l4xsKp.js} +1 -1
  40. package/dist/assets/x-CowtCu9Z.js +1 -0
  41. package/dist/index.html +20 -20
  42. package/package.json +8 -8
  43. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +30 -1
  44. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +1 -1
  45. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +40 -3
  46. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.selection.test.tsx +66 -0
  47. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +13 -1
  48. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +573 -90
  49. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +389 -288
  50. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +54 -35
  51. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +79 -148
  52. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +149 -94
  53. package/src/features/chat/features/workspace/components/project-files/__tests__/workspace-directory-entry-menu.test.tsx +50 -0
  54. package/src/features/chat/features/workspace/components/project-files/workspace-directory-entry-menu.tsx +209 -0
  55. package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +411 -0
  56. package/src/features/chat/features/workspace/components/project-files/workspace-new-folder-tree-item.tsx +112 -0
  57. package/src/features/chat/features/workspace/components/project-files/workspace-project-files-toolbar.tsx +64 -0
  58. package/src/features/chat/features/workspace/hooks/use-workspace-explorer-layout.ts +88 -0
  59. package/src/features/chat/features/workspace/hooks/use-workspace-file-actions.ts +335 -0
  60. package/src/features/chat/features/workspace/utils/chat-workspace-file-path-mutation.utils.ts +58 -0
  61. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +23 -22
  62. package/src/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils.ts +13 -4
  63. package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +86 -60
  64. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +103 -99
  65. package/src/features/chat/managers/chat-composer-intent.manager.ts +37 -33
  66. package/src/features/chat/managers/chat-thread.manager.ts +132 -128
  67. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +20 -40
  68. package/src/features/chat/stores/chat-thread.store.ts +47 -74
  69. package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx +35 -38
  70. package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +25 -19
  71. package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +60 -34
  72. package/src/shared/components/ui/context-menu/context-menu.tsx +95 -68
  73. package/src/shared/lib/api/managers/client.manager.ts +31 -17
  74. package/src/shared/lib/api/server-path/server-path.types.ts +66 -0
  75. package/src/shared/lib/api/types.ts +145 -116
  76. package/src/shared/lib/api/utils/server-path.utils.ts +35 -11
  77. package/src/shared/lib/i18n/locales/en-US/chat.json +42 -0
  78. package/src/shared/lib/i18n/locales/zh-CN/chat.json +42 -0
  79. package/dist/assets/appearance-settings-page-DpVJjvkB.js +0 -1
  80. package/dist/assets/chat-page-BuFDmG5_.js +0 -1
  81. package/dist/assets/doc-browser-CtO_HtRy.js +0 -1
  82. package/dist/assets/doc-browser-eMUagyBV.js +0 -1
  83. package/dist/assets/index-BjBdWHV1.js +0 -107
  84. package/dist/assets/index-DmpmjAGQ.css +0 -1
  85. package/dist/assets/loader-circle-Df8wF2Pr.js +0 -1
  86. package/dist/assets/mcp-marketplace-page-DI49IKxp.js +0 -1
  87. package/dist/assets/plus-DVU5RLCS.js +0 -1
  88. package/dist/assets/remote-T24O58lH.js +0 -1
  89. package/dist/assets/rotate-cw-BqhwSV5p.js +0 -1
  90. package/dist/assets/runtime-config-page-CXUx017t.js +0 -1
  91. package/dist/assets/security-config-CVPgqL7G.js +0 -1
  92. package/dist/assets/skeleton-C5tWata3.js +0 -1
  93. package/dist/assets/use-confirm-dialog-D_3-QpZU.js +0 -1
  94. package/dist/assets/x-BEWSqeux.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 () => ({ deleted: true, sessionId: 'parent-session-1' })),
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({ storage: createPersistStorage() as never });
38
- useChatThreadStore.persist.setOptions({ storage: createPersistStorage() as never });
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
- { path: 'auto.html', previewViewer: 'auto' as const, expectedViewer: 'source' },
321
+ {
322
+ path: 'auto.html',
323
+ previewViewer: 'auto' as const,
324
+ expectedViewer: 'source',
325
+ },
341
326
  { path: 'README.md', previewViewer: undefined, expectedViewer: 'rendered' },
342
- { path: 'report.docx', previewViewer: 'auto' as const, expectedViewer: 'auto' },
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
- const manager = new ChatThreadManager(
345
- createUiManager(),
346
- {} as ConstructorParameters<typeof ChatThreadManager>[1],
347
- );
348
-
349
- manager.openFilePreview({
350
- path,
351
- label: path,
352
- viewMode: 'preview',
353
- previewViewer,
354
- });
355
-
356
- expect(useChatThreadStore.getState().snapshot.workspaceFileTabs[0]).toMatchObject({
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,8 +1,9 @@
1
1
  import {
2
2
  CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
3
+ CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
3
4
  CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
4
5
  CHAT_WORKSPACE_FILE_TOKEN_KIND,
5
- } from "@nextclaw/shared";
6
+ } from '@nextclaw/shared';
6
7
 
7
8
  type ChatComposerReferenceIntentBase = {
8
9
  id: number;
@@ -15,6 +16,10 @@ export type ChatComposerFileReferenceIntent = ChatComposerReferenceIntentBase &
15
16
  kind: typeof CHAT_WORKSPACE_FILE_TOKEN_KIND;
16
17
  };
17
18
 
19
+ export type ChatComposerDirectoryReferenceIntent = ChatComposerReferenceIntentBase & {
20
+ kind: typeof CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND;
21
+ };
22
+
18
23
  export type ChatComposerExcerptReferenceIntent = ChatComposerReferenceIntentBase & {
19
24
  kind: typeof CHAT_WORKSPACE_EXCERPT_TOKEN_KIND;
20
25
  path: string;
@@ -26,19 +31,21 @@ export type ChatComposerExcerptReferenceIntent = ChatComposerReferenceIntentBase
26
31
  export type ChatComposerConversationExcerptIntent = ChatComposerReferenceIntentBase & {
27
32
  kind: typeof CHAT_CONVERSATION_EXCERPT_TOKEN_KIND;
28
33
  messageId: string;
29
- role: "assistant" | "user";
34
+ role: 'assistant' | 'user';
30
35
  excerpt: string;
31
36
  };
32
37
 
33
38
  export type ChatComposerReferenceIntent =
34
39
  | ChatComposerFileReferenceIntent
40
+ | ChatComposerDirectoryReferenceIntent
35
41
  | ChatComposerExcerptReferenceIntent
36
42
  | ChatComposerConversationExcerptIntent;
37
43
 
38
44
  type ChatComposerReferenceRequest =
39
- | Omit<ChatComposerFileReferenceIntent, "id">
40
- | Omit<ChatComposerExcerptReferenceIntent, "id">
41
- | Omit<ChatComposerConversationExcerptIntent, "id">;
45
+ | Omit<ChatComposerFileReferenceIntent, 'id'>
46
+ | Omit<ChatComposerDirectoryReferenceIntent, 'id'>
47
+ | Omit<ChatComposerExcerptReferenceIntent, 'id'>
48
+ | Omit<ChatComposerConversationExcerptIntent, 'id'>;
42
49
 
43
50
  function createExcerptTokenKey(prefix: string, identity: string): string {
44
51
  let hash = 2166136261;
@@ -64,9 +71,11 @@ export class ChatComposerIntentManager {
64
71
  private publish = (intent: ChatComposerReferenceRequest) => {
65
72
  const id = this.nextId + 1;
66
73
  const targetSessionKey = intent.targetSessionKey?.trim() || null;
67
- const nextIntent: ChatComposerReferenceIntent = intent.kind === CHAT_WORKSPACE_FILE_TOKEN_KIND
68
- ? { ...intent, id, targetSessionKey }
69
- : { ...intent, id, targetSessionKey };
74
+ const nextIntent: ChatComposerReferenceIntent = {
75
+ ...intent,
76
+ id,
77
+ targetSessionKey,
78
+ };
70
79
  this.nextId = nextIntent.id;
71
80
  this.pendingIntent = nextIntent;
72
81
  this.subscriptions.forEach((subscription) => {
@@ -76,11 +85,7 @@ export class ChatComposerIntentManager {
76
85
  });
77
86
  };
78
87
 
79
- requestFileReference = (params: {
80
- targetSessionKey: string | null;
81
- tokenKey: string;
82
- label: string;
83
- }) => {
88
+ requestFileReference = (params: { targetSessionKey: string | null; tokenKey: string; label: string }) => {
84
89
  const tokenKey = params.tokenKey.trim();
85
90
  const label = params.label.trim();
86
91
  if (!tokenKey || !label) {
@@ -94,6 +99,20 @@ export class ChatComposerIntentManager {
94
99
  });
95
100
  };
96
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
+
97
116
  requestExcerptReference = (params: {
98
117
  targetSessionKey: string | null;
99
118
  path: string;
@@ -102,14 +121,7 @@ export class ChatComposerIntentManager {
102
121
  startLine: number | null;
103
122
  endLine: number | null;
104
123
  }) => {
105
- const {
106
- endLine,
107
- excerpt: rawExcerpt,
108
- label: rawLabel,
109
- path: rawPath,
110
- startLine,
111
- targetSessionKey,
112
- } = params;
124
+ const { endLine, excerpt: rawExcerpt, label: rawLabel, path: rawPath, startLine, targetSessionKey } = params;
113
125
  const path = rawPath.trim();
114
126
  const label = rawLabel.trim();
115
127
  const excerpt = rawExcerpt.trim();
@@ -119,10 +131,7 @@ export class ChatComposerIntentManager {
119
131
  this.publish({
120
132
  kind: CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
121
133
  targetSessionKey,
122
- tokenKey: createExcerptTokenKey(
123
- path,
124
- `${path}:${startLine ?? "x"}:${endLine ?? "x"}:${excerpt}`,
125
- ),
134
+ tokenKey: createExcerptTokenKey(path, `${path}:${startLine ?? 'x'}:${endLine ?? 'x'}:${excerpt}`),
126
135
  path,
127
136
  label,
128
137
  excerpt,
@@ -134,7 +143,7 @@ export class ChatComposerIntentManager {
134
143
  requestConversationExcerptReference = (params: {
135
144
  targetSessionKey: string | null;
136
145
  messageId: string;
137
- role: "assistant" | "user";
146
+ role: 'assistant' | 'user';
138
147
  label: string;
139
148
  excerpt: string;
140
149
  }) => {
@@ -160,9 +169,7 @@ export class ChatComposerIntentManager {
160
169
  });
161
170
  };
162
171
 
163
- consumePending = (
164
- targetSessionKey: string | null,
165
- ): ChatComposerReferenceIntent | null => {
172
+ consumePending = (targetSessionKey: string | null): ChatComposerReferenceIntent | null => {
166
173
  if (this.pendingIntent?.targetSessionKey !== targetSessionKey) {
167
174
  return null;
168
175
  }
@@ -177,10 +184,7 @@ export class ChatComposerIntentManager {
177
184
  }
178
185
  };
179
186
 
180
- subscribe = (
181
- targetSessionKey: string | null,
182
- listener: ChatComposerIntentListener,
183
- ) => {
187
+ subscribe = (targetSessionKey: string | null, listener: ChatComposerIntentListener) => {
184
188
  const subscription = { targetSessionKey, listener };
185
189
  this.subscriptions.add(subscription);
186
190
  return () => {