@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.
Files changed (119) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/assets/{api-Glw2OGXK.js → api-FK7tikI1.js} +4 -4
  3. package/dist/assets/appearance-settings-page-BYbvl2wj.js +1 -0
  4. package/dist/assets/{channels-list-page-C7E4Sxwd.js → channels-list-page-CocdcIko.js} +2 -2
  5. package/dist/assets/{chat-page-BT6dNI-4.js → chat-page-C5E-LLVd.js} +1 -1
  6. package/dist/assets/config-split-page-CA_a0AJ5.js +1 -0
  7. package/dist/assets/confirm-dialog-CxZfenjK.js +5 -0
  8. package/dist/assets/desktop-update-config-BEKC8Ly9.js +1 -0
  9. package/dist/assets/{dist-dv_SUR54.js → dist-BH7W5yCi.js} +1 -1
  10. package/dist/assets/{dist-CtLyEJeO.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-BIBjkwfV.js → doc-browser-context-FDPs3gJf.js} +1 -1
  14. package/dist/assets/form-actions-0c0aw_bl.js +1 -0
  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-Bc_jI5iH.js +9 -0
  20. package/dist/assets/mcp-marketplace-page-CzCieD9r.js +1 -0
  21. package/dist/assets/model-config-page-C4jw-pcj.js +1 -0
  22. package/dist/assets/{navigation-link-CB0OQbnY.js → navigation-link-CVy0_kVl.js} +2 -2
  23. package/dist/assets/plus-CG1RwZQq.js +1 -0
  24. package/dist/assets/provider-scoped-model-input-CmH4BFvc.js +1 -0
  25. package/dist/assets/providers-config-page-CJQjwzaw.js +1 -0
  26. package/dist/assets/react-DjFBVA0n.js +8 -0
  27. package/dist/assets/{refresh-cw-Ba0LZou4.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-DTOAvqzt.js → save-BKNrtK81.js} +1 -1
  32. package/dist/assets/{search-D4X5DNMe.js → search-CkOm65W_.js} +1 -1
  33. package/dist/assets/{search-config-page-Cr6254K5.js → search-config-page-BaIijtmO.js} +1 -1
  34. package/dist/assets/{secrets-config-page-W2Hx332d.js → secrets-config-page-CJjtdFAl.js} +2 -2
  35. package/dist/assets/security-config-DB8apj09.js +1 -0
  36. package/dist/assets/{select-CDv0g2Pj.js → select-Ea7ffKCE.js} +2 -2
  37. package/dist/assets/{settings-2-CoxzNABp.js → settings-2-CtyN3m_V.js} +1 -1
  38. package/dist/assets/skeleton-Bt6zxcAB.js +1 -0
  39. package/dist/assets/{tag-chip-CjkZEoS2.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/conversation/hooks/__tests__/use-session-provider-model-catalog.test.tsx +54 -2
  46. package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +10 -1
  47. package/src/features/chat/features/conversation/hooks/use-session-provider-model-catalog.ts +76 -1
  48. package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +6 -1
  49. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +29 -0
  50. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +12 -3
  51. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +5 -0
  52. package/src/features/chat/features/input/utils/chat-conversation-excerpt-token.utils.ts +59 -0
  53. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +42 -16
  54. package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +50 -14
  55. package/src/features/chat/features/message/components/chat-message-list.container.tsx +40 -0
  56. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +5 -0
  57. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +40 -3
  58. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.selection.test.tsx +66 -0
  59. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +13 -1
  60. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +573 -90
  61. package/src/features/chat/features/workspace/components/__tests__/workspace-text-selection-menu.test.tsx +11 -4
  62. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +389 -288
  63. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +54 -35
  64. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +79 -148
  65. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +149 -94
  66. package/src/features/chat/features/workspace/components/project-files/__tests__/workspace-directory-entry-menu.test.tsx +50 -0
  67. package/src/features/chat/features/workspace/components/project-files/workspace-directory-entry-menu.tsx +209 -0
  68. package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +411 -0
  69. package/src/features/chat/features/workspace/components/project-files/workspace-new-folder-tree-item.tsx +112 -0
  70. package/src/features/chat/features/workspace/components/project-files/workspace-project-files-toolbar.tsx +64 -0
  71. package/src/features/chat/features/workspace/components/workspace-text-selection-menu.tsx +17 -195
  72. package/src/features/chat/features/workspace/hooks/use-workspace-explorer-layout.ts +88 -0
  73. package/src/features/chat/features/workspace/hooks/use-workspace-file-actions.ts +335 -0
  74. package/src/features/chat/features/workspace/utils/chat-workspace-file-path-mutation.utils.ts +58 -0
  75. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +23 -22
  76. package/src/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils.ts +13 -4
  77. package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +94 -45
  78. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +103 -99
  79. package/src/features/chat/managers/chat-composer-intent.manager.ts +83 -34
  80. package/src/features/chat/managers/chat-thread.manager.ts +132 -128
  81. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +20 -40
  82. package/src/features/chat/stores/chat-thread.store.ts +47 -74
  83. package/src/features/chat/types/chat-input-bar.types.ts +3 -0
  84. package/src/features/side-dock/components/__tests__/side-dock.test.tsx +1 -0
  85. package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +0 -9
  86. package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx +35 -38
  87. package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +25 -19
  88. package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +60 -34
  89. package/src/shared/components/ui/context-menu/context-menu.tsx +95 -68
  90. package/src/shared/lib/api/managers/client.manager.ts +31 -17
  91. package/src/shared/lib/api/server-path/server-path.types.ts +66 -0
  92. package/src/shared/lib/api/types.ts +145 -116
  93. package/src/shared/lib/api/utils/server-path.utils.ts +35 -11
  94. package/src/shared/lib/i18n/locales/en-US/chat.json +49 -1
  95. package/src/shared/lib/i18n/locales/zh-CN/chat.json +49 -1
  96. package/dist/assets/appearance-settings-page-Bl_W_9m7.js +0 -1
  97. package/dist/assets/config-split-page-D5BgtKrm.js +0 -1
  98. package/dist/assets/confirm-dialog-Bf907Azu.js +0 -5
  99. package/dist/assets/desktop-update-config-BwuyBPJb.js +0 -1
  100. package/dist/assets/doc-browser-C23NDGh5.js +0 -1
  101. package/dist/assets/doc-browser-DlOOhBxI.js +0 -1
  102. package/dist/assets/form-actions-DAzi68Cn.js +0 -1
  103. package/dist/assets/index-BLNg2yg0.css +0 -1
  104. package/dist/assets/index-D3EKjEyK.js +0 -107
  105. package/dist/assets/loader-circle-BSg5u0r4.js +0 -1
  106. package/dist/assets/mcp-marketplace-page-ByhvQQj5.js +0 -9
  107. package/dist/assets/mcp-marketplace-page-CytXwnUs.js +0 -1
  108. package/dist/assets/model-config-page-bgEx3MjQ.js +0 -1
  109. package/dist/assets/plus-CNjWdLhZ.js +0 -1
  110. package/dist/assets/provider-scoped-model-input-8F6JMd2n.js +0 -1
  111. package/dist/assets/providers-config-page-CxpTjR81.js +0 -1
  112. package/dist/assets/react-Bk2ydNTe.js +0 -8
  113. package/dist/assets/remote-Dv38PuMV.js +0 -1
  114. package/dist/assets/rotate-cw-IkYVk9JX.js +0 -1
  115. package/dist/assets/runtime-config-page-C0ByyUVr.js +0 -1
  116. package/dist/assets/security-config-1E8OF4kR.js +0 -1
  117. package/dist/assets/skeleton-DW018fJv.js +0 -1
  118. package/dist/assets/use-confirm-dialog-CavtY1vJ.js +0 -1
  119. package/dist/assets/x-PTXYTEwz.js +0 -1
@@ -1,22 +1,17 @@
1
1
  import { create } from 'zustand';
2
2
  import { createJSONStorage, persist } from 'zustand/middleware';
3
3
  import type { NcpMessage } from '@nextclaw/ncp';
4
- import type {
5
- ChatContentParams,
6
- ChatFileOperationLineViewModel,
7
- ChatFilePreviewViewer,
8
- } from '@nextclaw/agent-chat-ui';
9
- import type {
10
- SessionContextWindowView,
11
- SessionTypeIconView
12
- } from '@/shared/lib/api';
4
+ import type { ChatContentParams, ChatFileOperationLineViewModel, ChatFilePreviewViewer } from '@nextclaw/agent-chat-ui';
5
+ import type { SessionContextWindowView, SessionTypeIconView } from '@/shared/lib/api';
13
6
  import {
14
7
  normalizePersistedWorkspaceFileTab,
15
8
  retainWorkspaceFileTabs,
16
9
  toPersistedWorkspaceFileTab,
17
10
  } from '@/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils';
18
11
  import {
12
+ CHAT_WORKSPACE_EXPLORER_DEFAULT_WIDTH,
19
13
  CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH,
14
+ normalizeChatWorkspaceExplorerWidth,
20
15
  normalizeChatWorkspacePanelWidth,
21
16
  } from '@/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils';
22
17
  import { createWorkspaceNavigationEntryFromSnapshot } from '@/features/chat/features/workspace/utils/chat-thread-workspace-session.utils';
@@ -100,6 +95,7 @@ export type ChatThreadSnapshot = {
100
95
  closedWorkspaceTabEntries: ChatWorkspaceNavigationEntry[];
101
96
  workspaceNavigationHistory: ChatWorkspaceNavigationEntry[];
102
97
  workspaceNavigationHistoryIndex: number;
98
+ workspaceExplorerWidth: number;
103
99
  workspacePanelWidth: number;
104
100
  contextWindow?: SessionContextWindowView | null;
105
101
  };
@@ -123,6 +119,7 @@ type PersistedChatThreadStore = {
123
119
  closedWorkspaceTabEntries?: unknown;
124
120
  workspaceNavigationHistory?: unknown;
125
121
  workspaceNavigationHistoryIndex?: unknown;
122
+ workspaceExplorerWidth?: unknown;
126
123
  workspacePanelWidth?: unknown;
127
124
  };
128
125
  };
@@ -137,6 +134,7 @@ type PersistedChatWorkspaceSnapshot = Pick<
137
134
  | 'closedWorkspaceTabEntries'
138
135
  | 'workspaceNavigationHistory'
139
136
  | 'workspaceNavigationHistoryIndex'
137
+ | 'workspaceExplorerWidth'
140
138
  | 'workspacePanelWidth'
141
139
  >;
142
140
 
@@ -169,8 +167,9 @@ const initialSnapshot: ChatThreadSnapshot = {
169
167
  closedWorkspaceTabEntries: [],
170
168
  workspaceNavigationHistory: [],
171
169
  workspaceNavigationHistoryIndex: 0,
170
+ workspaceExplorerWidth: CHAT_WORKSPACE_EXPLORER_DEFAULT_WIDTH,
172
171
  workspacePanelWidth: CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH,
173
- contextWindow: null
172
+ contextWindow: null,
174
173
  };
175
174
 
176
175
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -178,20 +177,14 @@ function isRecord(value: unknown): value is Record<string, unknown> {
178
177
  }
179
178
 
180
179
  function normalizeOptionalString(value: unknown): string | null {
181
- return typeof value === 'string' && value.trim().length > 0
182
- ? value.trim()
183
- : null;
180
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
184
181
  }
185
182
 
186
183
  function normalizeHistoryIndex(value: unknown, maxIndex: number): number {
187
- return typeof value === 'number' && Number.isInteger(value)
188
- ? Math.min(Math.max(0, value), maxIndex)
189
- : maxIndex;
184
+ return typeof value === 'number' && Number.isInteger(value) ? Math.min(Math.max(0, value), maxIndex) : maxIndex;
190
185
  }
191
186
 
192
- function isWorkspacePanelKind(
193
- value: unknown,
194
- ): value is PersistedWorkspacePanelKind {
187
+ function isWorkspacePanelKind(value: unknown): value is PersistedWorkspacePanelKind {
195
188
  return (
196
189
  value === 'overview' ||
197
190
  value === 'child-sessions' ||
@@ -202,9 +195,7 @@ function isWorkspacePanelKind(
202
195
  );
203
196
  }
204
197
 
205
- function normalizePersistedWorkspaceNavigationEntry(
206
- value: unknown,
207
- ): ChatWorkspaceNavigationEntry | null {
198
+ function normalizePersistedWorkspaceNavigationEntry(value: unknown): ChatWorkspaceNavigationEntry | null {
208
199
  if (!isRecord(value)) {
209
200
  return null;
210
201
  }
@@ -226,9 +217,7 @@ function normalizePersistedWorkspaceNavigationEntry(
226
217
  };
227
218
  }
228
219
 
229
- function normalizePersistedWorkspaceSnapshot(
230
- value: unknown,
231
- ): PersistedChatWorkspaceSnapshot | null {
220
+ function normalizePersistedWorkspaceSnapshot(value: unknown): PersistedChatWorkspaceSnapshot | null {
232
221
  if (!isRecord(value)) {
233
222
  return null;
234
223
  }
@@ -236,35 +225,29 @@ function normalizePersistedWorkspaceSnapshot(
236
225
  const activeWorkspaceFileKey = normalizeOptionalString(value.activeWorkspaceFileKey);
237
226
  const normalizedWorkspaceFileTabs = Array.isArray(value.workspaceFileTabs)
238
227
  ? value.workspaceFileTabs
239
- .map(normalizePersistedWorkspaceFileTab)
240
- .filter((tab): tab is ChatWorkspaceFileTab => tab !== null)
228
+ .map(normalizePersistedWorkspaceFileTab)
229
+ .filter((tab): tab is ChatWorkspaceFileTab => tab !== null)
241
230
  : [];
242
231
  const workspaceFileTabs = retainWorkspaceFileTabs(
243
232
  normalizedWorkspaceFileTabs,
244
233
  activeWorkspaceFileKey,
245
234
  CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS,
246
235
  );
247
- const sessionWorkspaceFileTabs = workspaceFileTabs.filter(
248
- (tab) => tab.parentSessionKey === workspacePanelParentKey,
249
- );
236
+ const sessionWorkspaceFileTabs = workspaceFileTabs.filter((tab) => tab.parentSessionKey === workspacePanelParentKey);
250
237
  const resolvedActiveWorkspaceFileKey =
251
238
  activeWorkspaceFileKey && sessionWorkspaceFileTabs.some((tab) => tab.key === activeWorkspaceFileKey)
252
239
  ? activeWorkspaceFileKey
253
- : sessionWorkspaceFileTabs[0]?.key ?? null;
240
+ : (sessionWorkspaceFileTabs[0]?.key ?? null);
254
241
  const activeWorkspacePanelKind = isWorkspacePanelKind(value.activeWorkspacePanelKind)
255
242
  ? value.activeWorkspacePanelKind
256
243
  : null;
257
244
  const resolvedActiveWorkspacePanelKind =
258
- activeWorkspacePanelKind === 'file' && !resolvedActiveWorkspaceFileKey
259
- ? null
260
- : activeWorkspacePanelKind;
245
+ activeWorkspacePanelKind === 'file' && !resolvedActiveWorkspaceFileKey ? null : activeWorkspacePanelKind;
261
246
  const activeChildSessionKey = normalizeOptionalString(value.activeChildSessionKey);
262
247
  const closedWorkspaceTabEntries = Array.isArray(value.closedWorkspaceTabEntries)
263
248
  ? value.closedWorkspaceTabEntries
264
- .map(normalizePersistedWorkspaceNavigationEntry)
265
- .filter((entry): entry is ChatWorkspaceNavigationEntry =>
266
- entry !== null && entry.kind !== 'overview',
267
- )
249
+ .map(normalizePersistedWorkspaceNavigationEntry)
250
+ .filter((entry): entry is ChatWorkspaceNavigationEntry => entry !== null && entry.kind !== 'overview')
268
251
  : [];
269
252
  const fallbackHistoryEntry = createWorkspaceNavigationEntryFromSnapshot({
270
253
  activeWorkspacePanelKind: resolvedActiveWorkspacePanelKind,
@@ -274,11 +257,9 @@ function normalizePersistedWorkspaceSnapshot(
274
257
  });
275
258
  const normalizedNavigationHistory = Array.isArray(value.workspaceNavigationHistory)
276
259
  ? value.workspaceNavigationHistory
277
- .map(normalizePersistedWorkspaceNavigationEntry)
278
- .filter((entry): entry is ChatWorkspaceNavigationEntry => entry !== null)
279
- .filter((entry) =>
280
- entry.kind !== 'file' || workspaceFileTabs.some((tab) => tab.key === entry.key),
281
- )
260
+ .map(normalizePersistedWorkspaceNavigationEntry)
261
+ .filter((entry): entry is ChatWorkspaceNavigationEntry => entry !== null)
262
+ .filter((entry) => entry.kind !== 'file' || workspaceFileTabs.some((tab) => tab.key === entry.key))
282
263
  : [];
283
264
  const workspaceNavigationHistory =
284
265
  normalizedNavigationHistory.length > 0
@@ -288,10 +269,7 @@ function normalizePersistedWorkspaceSnapshot(
288
269
  : [];
289
270
  const workspaceNavigationHistoryIndex =
290
271
  workspaceNavigationHistory.length > 0
291
- ? normalizeHistoryIndex(
292
- value.workspaceNavigationHistoryIndex,
293
- workspaceNavigationHistory.length - 1,
294
- )
272
+ ? normalizeHistoryIndex(value.workspaceNavigationHistoryIndex, workspaceNavigationHistory.length - 1)
295
273
  : 0;
296
274
 
297
275
  return {
@@ -303,9 +281,8 @@ function normalizePersistedWorkspaceSnapshot(
303
281
  closedWorkspaceTabEntries,
304
282
  workspaceNavigationHistory,
305
283
  workspaceNavigationHistoryIndex,
306
- workspacePanelWidth: normalizeChatWorkspacePanelWidth(
307
- value.workspacePanelWidth,
308
- ),
284
+ workspaceExplorerWidth: normalizeChatWorkspaceExplorerWidth(value.workspaceExplorerWidth),
285
+ workspacePanelWidth: normalizeChatWorkspacePanelWidth(value.workspacePanelWidth),
309
286
  };
310
287
  }
311
288
 
@@ -317,23 +294,21 @@ export const useChatThreadStore = create<ChatThreadStore>()(
317
294
  set((state) => ({
318
295
  snapshot: {
319
296
  ...state.snapshot,
320
- ...patch
321
- }
322
- }))
297
+ ...patch,
298
+ },
299
+ })),
323
300
  }),
324
301
  {
325
302
  name: CHAT_THREAD_WORKSPACE_STORAGE_KEY,
326
303
  version: CHAT_THREAD_WORKSPACE_STORAGE_VERSION,
327
304
  storage: createJSONStorage(() => window.localStorage),
328
305
  partialize: (state): PersistedChatThreadStore => {
329
- const workspaceNavigationHistory = state.snapshot.workspaceNavigationHistory
330
- .filter((entry) => entry.kind !== 'side-chat-draft');
306
+ const workspaceNavigationHistory = state.snapshot.workspaceNavigationHistory.filter(
307
+ (entry) => entry.kind !== 'side-chat-draft',
308
+ );
331
309
  const workspaceNavigationHistoryIndex =
332
310
  workspaceNavigationHistory.length > 0
333
- ? Math.min(
334
- state.snapshot.workspaceNavigationHistoryIndex,
335
- workspaceNavigationHistory.length - 1,
336
- )
311
+ ? Math.min(state.snapshot.workspaceNavigationHistoryIndex, workspaceNavigationHistory.length - 1)
337
312
  : 0;
338
313
  return {
339
314
  snapshot: {
@@ -347,23 +322,21 @@ export const useChatThreadStore = create<ChatThreadStore>()(
347
322
  state.snapshot.workspaceFileTabs,
348
323
  state.snapshot.activeWorkspaceFileKey,
349
324
  CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS,
350
- )
351
- .map(toPersistedWorkspaceFileTab),
325
+ ).map(toPersistedWorkspaceFileTab),
352
326
  activeWorkspaceFileKey: state.snapshot.activeWorkspaceFileKey,
353
- closedWorkspaceTabEntries: state.snapshot.closedWorkspaceTabEntries
354
- .filter((entry) => entry.kind !== 'overview' && entry.kind !== 'side-chat-draft'),
327
+ closedWorkspaceTabEntries: state.snapshot.closedWorkspaceTabEntries.filter(
328
+ (entry) => entry.kind !== 'overview' && entry.kind !== 'side-chat-draft',
329
+ ),
355
330
  workspaceNavigationHistory,
356
331
  workspaceNavigationHistoryIndex,
332
+ workspaceExplorerWidth: state.snapshot.workspaceExplorerWidth,
357
333
  workspacePanelWidth: state.snapshot.workspacePanelWidth,
358
- }
334
+ },
359
335
  };
360
336
  },
361
337
  merge: (persistedState, currentState) => {
362
- const persistedSnapshot = isRecord(persistedState)
363
- ? persistedState.snapshot
364
- : null;
365
- const workspaceSnapshot =
366
- normalizePersistedWorkspaceSnapshot(persistedSnapshot);
338
+ const persistedSnapshot = isRecord(persistedState) ? persistedState.snapshot : null;
339
+ const workspaceSnapshot = normalizePersistedWorkspaceSnapshot(persistedSnapshot);
367
340
  if (!workspaceSnapshot) {
368
341
  return currentState;
369
342
  }
@@ -371,10 +344,10 @@ export const useChatThreadStore = create<ChatThreadStore>()(
371
344
  ...currentState,
372
345
  snapshot: {
373
346
  ...currentState.snapshot,
374
- ...workspaceSnapshot
375
- }
347
+ ...workspaceSnapshot,
348
+ },
376
349
  };
377
- }
378
- }
379
- )
350
+ },
351
+ },
352
+ ),
380
353
  );
@@ -49,7 +49,10 @@ export type ChatInputBarAdapterTexts = {
49
49
  discoveredModelsViewLabel: string;
50
50
  discoveredModelsGroupLabel: string;
51
51
  discoveredModelAddLabel: string;
52
+ discoveredModelAddedLabel: string;
52
53
  discoveredModelsDismissLabel: string;
54
+ discoveredModelsDoneLabel: string;
55
+ discoveredModelsCloseLabel: string;
53
56
  thinkingLabels: Record<ChatThinkingLevel, string>;
54
57
  noModelOptionsLabel: string;
55
58
  configureProviderLabel: string;
@@ -36,6 +36,7 @@ describe('SideDock', () => {
36
36
  builtInItems.forEach((item) => {
37
37
  expect(document.querySelector(`[data-side-dock-item-id="${item.id}"]`)).toBeTruthy();
38
38
  });
39
+ expect(document.querySelector('[data-side-dock-item-id="service-apps"]')).toBeNull();
39
40
 
40
41
  const appsButton = document.querySelector('[data-side-dock-item-id="apps"]');
41
42
  expect(appsButton).toBeTruthy();
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  RIGHT_PANEL_APPS_URL,
3
3
  RIGHT_PANEL_HOME_URL,
4
- RIGHT_PANEL_SERVICE_APPS_URL,
5
4
  } from '@/features/right-panel-resources';
6
5
  import type { SideDockItem } from '@/features/side-dock/types/side-dock.types';
7
6
  import { t } from '@/shared/lib/i18n';
@@ -20,14 +19,6 @@ export function getSideDockBuiltInItems(): SideDockItem[] {
20
19
  removable: false,
21
20
  target: { type: 'right-panel-resource', uri: RIGHT_PANEL_APPS_URL },
22
21
  },
23
- {
24
- builtIn: true,
25
- icon: { type: 'builtin', name: 'service-apps' },
26
- id: 'service-apps',
27
- label: t('serviceAppsTitle'),
28
- removable: false,
29
- target: { type: 'right-panel-resource', uri: RIGHT_PANEL_SERVICE_APPS_URL },
30
- },
31
22
  {
32
23
  builtIn: true,
33
24
  icon: { type: 'builtin', name: 'docs' },
@@ -1,6 +1,6 @@
1
- import { fireEvent, render, screen } from "@testing-library/react";
2
- import { beforeEach, describe, expect, it, vi } from "vitest";
3
- import { ResizableRightPanel } from "@/shared/components/resizable-right-panel/resizable-right-panel";
1
+ import { fireEvent, render, screen } from '@testing-library/react';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { ResizableRightPanel } from '@/shared/components/resizable-right-panel/resizable-right-panel';
4
4
 
5
5
  function firePointerEvent(
6
6
  target: Window | Document | Node | Element,
@@ -15,12 +15,12 @@ function firePointerEvent(
15
15
  fireEvent(target, event);
16
16
  }
17
17
 
18
- describe("ResizableRightPanel", () => {
18
+ describe('ResizableRightPanel', () => {
19
19
  beforeEach(() => {
20
20
  HTMLElement.prototype.setPointerCapture = vi.fn();
21
21
  });
22
22
 
23
- it("resizes horizontally from the left handle and clamps to max width", () => {
23
+ it('resizes horizontally from the left handle and clamps to max width', () => {
24
24
  const onWidthCommit = vi.fn();
25
25
  render(
26
26
  <ResizableRightPanel
@@ -34,48 +34,51 @@ describe("ResizableRightPanel", () => {
34
34
  </ResizableRightPanel>,
35
35
  );
36
36
 
37
- firePointerEvent(
38
- screen.getByTestId("resizable-right-panel-handle"),
39
- "pointerdown",
40
- { clientX: 800, pointerId: 1 },
41
- );
42
- firePointerEvent(window, "pointermove", { clientX: 600, pointerId: 1 });
37
+ firePointerEvent(screen.getByTestId('resizable-right-panel-handle'), 'pointerdown', { clientX: 800, pointerId: 1 });
38
+ firePointerEvent(window, 'pointermove', { clientX: 600, pointerId: 1 });
43
39
 
44
- expect(screen.getByTestId("right-panel").style.width).toBe("500px");
45
- expect(
46
- screen.getByTestId("resizable-right-panel-resize-shield"),
47
- ).toBeTruthy();
40
+ expect(screen.getByTestId('right-panel').style.width).toBe('500px');
41
+ expect(screen.getByTestId('resizable-right-panel-resize-shield')).toBeTruthy();
48
42
 
49
- firePointerEvent(window, "pointerup", { clientX: 600, pointerId: 1 });
43
+ firePointerEvent(window, 'pointerup', { clientX: 600, pointerId: 1 });
50
44
 
51
45
  expect(onWidthCommit).toHaveBeenCalledWith(500);
52
46
 
53
- expect(
54
- screen.queryByTestId("resizable-right-panel-resize-shield"),
55
- ).toBeNull();
47
+ expect(screen.queryByTestId('resizable-right-panel-resize-shield')).toBeNull();
56
48
  });
57
49
 
58
- it("does not render resize controls in overlay mode", () => {
50
+ it('does not render resize controls in overlay mode', () => {
59
51
  render(
60
52
  <ResizableRightPanel data-testid="right-panel" overlay>
61
53
  Content
62
54
  </ResizableRightPanel>,
63
55
  );
64
56
 
65
- expect(
66
- screen.queryByTestId("resizable-right-panel-handle"),
67
- ).toBeNull();
68
- expect(screen.getByTestId("right-panel").className).toContain("fixed");
57
+ expect(screen.queryByTestId('resizable-right-panel-handle')).toBeNull();
58
+ expect(screen.getByTestId('right-panel').className).toContain('fixed');
59
+ });
60
+
61
+ it('keeps the outer resize boundary above nested panel overlays', () => {
62
+ render(
63
+ <ResizableRightPanel data-testid="right-panel">
64
+ <div className="absolute inset-0 z-20" data-testid="nested-overlay" />
65
+ </ResizableRightPanel>,
66
+ );
67
+
68
+ const panel = screen.getByTestId('right-panel');
69
+ const handle = screen.getByTestId('resizable-right-panel-handle');
70
+ expect(handle.className).toContain('z-30');
71
+ expect(panel.compareDocumentPosition(handle) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
69
72
  });
70
73
 
71
- it("applies a persisted controlled width after store rehydration", () => {
74
+ it('applies a persisted controlled width after store rehydration', () => {
72
75
  const view = render(
73
76
  <ResizableRightPanel data-testid="right-panel" width={480}>
74
77
  Content
75
78
  </ResizableRightPanel>,
76
79
  );
77
80
 
78
- expect(screen.getByTestId("right-panel").style.width).toBe("480px");
81
+ expect(screen.getByTestId('right-panel').style.width).toBe('480px');
79
82
 
80
83
  view.rerender(
81
84
  <ResizableRightPanel data-testid="right-panel" width={620}>
@@ -83,24 +86,18 @@ describe("ResizableRightPanel", () => {
83
86
  </ResizableRightPanel>,
84
87
  );
85
88
 
86
- expect(screen.getByTestId("right-panel").style.width).toBe("620px");
89
+ expect(screen.getByTestId('right-panel').style.width).toBe('620px');
87
90
  });
88
91
 
89
- it("can scope overlay positioning to its container", () => {
92
+ it('can scope overlay positioning to its container', () => {
90
93
  render(
91
- <ResizableRightPanel
92
- data-testid="right-panel"
93
- overlay
94
- overlayScope="container"
95
- >
94
+ <ResizableRightPanel data-testid="right-panel" overlay overlayScope="container">
96
95
  Content
97
96
  </ResizableRightPanel>,
98
97
  );
99
98
 
100
- expect(
101
- screen.queryByTestId("resizable-right-panel-handle"),
102
- ).toBeNull();
103
- expect(screen.getByTestId("right-panel").className).toContain("absolute");
104
- expect(screen.getByTestId("right-panel").className).not.toContain("fixed");
99
+ expect(screen.queryByTestId('resizable-right-panel-handle')).toBeNull();
100
+ expect(screen.getByTestId('right-panel').className).toContain('absolute');
101
+ expect(screen.getByTestId('right-panel').className).not.toContain('fixed');
105
102
  });
106
103
  });
@@ -1,13 +1,13 @@
1
- import { useRef, useState, type ComponentPropsWithoutRef } from "react";
2
- import { cn } from "@/shared/lib/utils";
1
+ import { useRef, useState, type ComponentPropsWithoutRef } from 'react';
2
+ import { cn } from '@/shared/lib/utils';
3
3
 
4
- type ResizableRightPanelProps = ComponentPropsWithoutRef<"aside"> & {
4
+ type ResizableRightPanelProps = ComponentPropsWithoutRef<'aside'> & {
5
5
  defaultWidth?: number;
6
6
  minWidth?: number;
7
7
  maxWidth?: number;
8
8
  onWidthCommit?: (width: number) => void;
9
9
  overlay?: boolean;
10
- overlayScope?: "viewport" | "container";
10
+ overlayScope?: 'viewport' | 'container';
11
11
  width?: number;
12
12
  };
13
13
 
@@ -20,7 +20,7 @@ export function ResizableRightPanel({
20
20
  maxWidth = 860,
21
21
  onWidthCommit,
22
22
  overlay = false,
23
- overlayScope = "viewport",
23
+ overlayScope = 'viewport',
24
24
  width: controlledWidth,
25
25
  ...props
26
26
  }: ResizableRightPanelProps) {
@@ -31,10 +31,10 @@ export function ResizableRightPanel({
31
31
  const [uncontrolledWidth, setUncontrolledWidth] = useState(defaultWidth);
32
32
  const width = dragWidth ?? controlledWidth ?? uncontrolledWidth;
33
33
  const overlayClassName = overlay
34
- ? overlayScope === "container"
35
- ? "absolute inset-0 z-30"
36
- : "fixed inset-0 z-40"
37
- : "border-l border-border";
34
+ ? overlayScope === 'container'
35
+ ? 'absolute inset-0 z-30'
36
+ : 'fixed inset-0 z-40'
37
+ : 'border-l border-border';
38
38
 
39
39
  const onResizeStart = (event: React.PointerEvent<HTMLDivElement>) => {
40
40
  if (overlay) return;
@@ -60,30 +60,36 @@ export function ResizableRightPanel({
60
60
  setDragWidth(null);
61
61
  setIsResizing(false);
62
62
  resizeRef.current = null;
63
- window.removeEventListener("pointermove", onMove);
64
- window.removeEventListener("pointerup", onUp);
65
- window.removeEventListener("pointercancel", onUp);
63
+ window.removeEventListener('pointermove', onMove);
64
+ window.removeEventListener('pointerup', onUp);
65
+ window.removeEventListener('pointercancel', onUp);
66
66
  };
67
- window.addEventListener("pointermove", onMove);
68
- window.addEventListener("pointerup", onUp);
69
- window.addEventListener("pointercancel", onUp);
67
+ window.addEventListener('pointermove', onMove);
68
+ window.addEventListener('pointerup', onUp);
69
+ window.addEventListener('pointercancel', onUp);
70
70
  };
71
71
 
72
72
  return (
73
73
  <aside
74
74
  {...props}
75
75
  className={cn(
76
- "relative flex h-full min-h-0 shrink-0 overflow-hidden bg-card text-card-foreground",
76
+ 'relative flex h-full min-h-0 shrink-0 overflow-hidden bg-card text-card-foreground',
77
77
  overlayClassName,
78
78
  className,
79
79
  )}
80
80
  style={overlay ? style : { ...style, width }}
81
81
  >
82
+ <div className="flex h-full min-h-0 w-full flex-col overflow-hidden">{children}</div>
82
83
  {!overlay ? (
83
- <div className="absolute left-0 top-0 z-20 h-full w-3 cursor-ew-resize transition-colors hover:bg-primary/10" data-testid="resizable-right-panel-handle" onPointerDown={onResizeStart} />
84
+ <div
85
+ className="absolute left-0 top-0 z-30 h-full w-3 cursor-ew-resize transition-colors hover:bg-primary/10"
86
+ data-testid="resizable-right-panel-handle"
87
+ onPointerDown={onResizeStart}
88
+ />
89
+ ) : null}
90
+ {isResizing ? (
91
+ <div className="absolute inset-0 z-10 cursor-ew-resize" data-testid="resizable-right-panel-resize-shield" />
84
92
  ) : null}
85
- <div className="flex h-full min-h-0 w-full flex-col overflow-hidden">{children}</div>
86
- {isResizing ? <div className="absolute inset-0 z-10 cursor-ew-resize" data-testid="resizable-right-panel-resize-shield" /> : null}
87
93
  </aside>
88
94
  );
89
95
  }