@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
|
@@ -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
|
-
|
|
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
|
-
|
|
240
|
-
|
|
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
|
-
|
|
265
|
-
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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' },
|
package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { fireEvent, render, screen } from
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from
|
|
3
|
-
import { ResizableRightPanel } from
|
|
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(
|
|
18
|
+
describe('ResizableRightPanel', () => {
|
|
19
19
|
beforeEach(() => {
|
|
20
20
|
HTMLElement.prototype.setPointerCapture = vi.fn();
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
it(
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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(
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
89
|
+
expect(screen.getByTestId('right-panel').style.width).toBe('620px');
|
|
87
90
|
});
|
|
88
91
|
|
|
89
|
-
it(
|
|
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
|
-
|
|
102
|
-
).
|
|
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
|
|
2
|
-
import { cn } from
|
|
1
|
+
import { useRef, useState, type ComponentPropsWithoutRef } from 'react';
|
|
2
|
+
import { cn } from '@/shared/lib/utils';
|
|
3
3
|
|
|
4
|
-
type ResizableRightPanelProps = ComponentPropsWithoutRef<
|
|
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?:
|
|
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 =
|
|
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 ===
|
|
35
|
-
?
|
|
36
|
-
:
|
|
37
|
-
:
|
|
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(
|
|
64
|
-
window.removeEventListener(
|
|
65
|
-
window.removeEventListener(
|
|
63
|
+
window.removeEventListener('pointermove', onMove);
|
|
64
|
+
window.removeEventListener('pointerup', onUp);
|
|
65
|
+
window.removeEventListener('pointercancel', onUp);
|
|
66
66
|
};
|
|
67
|
-
window.addEventListener(
|
|
68
|
-
window.addEventListener(
|
|
69
|
-
window.addEventListener(
|
|
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
|
-
|
|
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
|
|
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
|
}
|