@nextclaw/ui 0.15.3 → 0.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/assets/aiden0z-pptx-renderer.es-Bd99vut2.js +94 -0
- package/dist/assets/api-BOXOrpcJ.js +13 -0
- package/dist/assets/app-presenter-provider-CkHLbvZQ.js +106 -0
- package/dist/assets/app-presenter-provider-D3BuuOyZ.css +1 -0
- package/dist/assets/appearance-settings-page-fLIJbx1C.js +1 -0
- package/dist/assets/{book-open-C3mJGwiy.js → book-open-DWUjS6aW.js} +1 -1
- package/dist/assets/channels-list-page-CvCTJraa.js +8 -0
- package/dist/assets/chat-page-B42UUIFR.js +1 -0
- package/dist/assets/chunk-DseTPa7n.js +1 -0
- package/dist/assets/config-split-page-DBgt9S8b.js +1 -0
- package/dist/assets/confirm-dialog-GSXIvoRU.js +5 -0
- package/dist/assets/desktop-update-config-7bmNeWIC.js +1 -0
- package/dist/assets/dist-DC7f4TJH.js +41 -0
- package/dist/assets/doc-browser-CgdoLUT92.js +1 -0
- package/dist/assets/doc-browser-D0monHjG.js +1 -0
- package/dist/assets/doc-browser-context-CGwEOhBy.js +1 -0
- package/dist/assets/docx-preview-eFH9DRsS.js +20 -0
- package/dist/assets/download-DiP9zx-J.js +1 -0
- package/dist/assets/{external-link-CU0kzzIP.js → external-link-C6ZeSPef.js} +1 -1
- package/dist/assets/eye-suvKmtwn.js +1 -0
- package/dist/assets/folder-CMELbQGG.js +1 -0
- package/dist/assets/{form-actions-BvCp_0lS.js → form-actions-CUAzpNe_.js} +1 -1
- package/dist/assets/index-BFeNDQ4R.css +1 -0
- package/dist/assets/index-D_6U-M94.js +2 -0
- package/dist/assets/jszip.min-Das2IAnb.js +2 -0
- package/dist/assets/loader-circle-tYHStefV.js +1 -0
- package/dist/assets/logo-badge-Cu5UGH_4.js +1 -0
- package/dist/assets/mcp-marketplace-page-BUzHDwW6.js +9 -0
- package/dist/assets/mcp-marketplace-page-ohUAdfqJ.js +1 -0
- package/dist/assets/model-config-page-CWP6pyBm.js +1 -0
- package/dist/assets/plus-tywizkeY.js +1 -0
- package/dist/assets/popover-DImguRok.js +1 -0
- package/dist/assets/provider-models-C_yOh6DE.js +1 -0
- package/dist/assets/provider-scoped-model-input-CE7p4SbJ.js +1 -0
- package/dist/assets/providers-config-page-BKPo8SjH.js +1 -0
- package/dist/assets/react-5sjAT_6d.js +8 -0
- package/dist/assets/{refresh-cw-CILd1vAP.js → refresh-cw-CHLBzWWj.js} +1 -1
- package/dist/assets/remote-DXJryUxB.js +1 -0
- package/dist/assets/rotate-cw-BefO5brU.js +1 -0
- package/dist/assets/runtime-config-page-1Axv4QGu.js +1 -0
- package/dist/assets/{save-ByJqcPTP.js → save-BABzyrjB.js} +1 -1
- package/dist/assets/{search-DVIV6pfD.js → search-BTqxG3ZG.js} +1 -1
- package/dist/assets/search-config-page-BkKKmxmS.js +1 -0
- package/dist/assets/secrets-config-page-CWJMyu-k.js +3 -0
- package/dist/assets/security-config-DeQUIXfi.js +1 -0
- package/dist/assets/select-TYZDBNmq.js +1 -0
- package/dist/assets/setting-row-DR23Z_5I.js +1 -0
- package/dist/assets/{settings-2-CH5shGg_.js → settings-2-D8WWcBnw.js} +1 -1
- package/dist/assets/{skeleton-DeX1FKw7.js → skeleton-CloHeg0S.js} +1 -1
- package/dist/assets/tabs-CX-ckWoo.js +1 -0
- package/dist/assets/tag-chip-mCc23Rhb.js +1 -0
- package/dist/assets/trash-2-SAW1Z4Po.js +1 -0
- package/dist/assets/use-config-BucdaK23.js +1 -0
- package/dist/assets/use-confirm-dialog-C95F6sT1.js +1 -0
- package/dist/assets/use-viewport-layout-C6m3Nte7.js +1 -0
- package/dist/assets/x-DtCPHwCu.js +1 -0
- package/dist/assets/xlsx-B6zVt1RK.js +64 -0
- package/dist/index.html +34 -28
- package/package.json +11 -6
- package/src/features/chat/components/__tests__/chat-popover-content.test.tsx +41 -0
- package/src/features/chat/components/chat-popover-content.tsx +26 -0
- package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +13 -0
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +21 -11
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header.test.tsx +2 -2
- package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +28 -1
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +5 -4
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +9 -18
- package/src/features/chat/components/conversation/chat-conversation-track.tsx +35 -0
- package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +1 -0
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +7 -7
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +3 -14
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +20 -1
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +69 -54
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +16 -1
- package/src/features/chat/features/message/utils/__tests__/chat-message-file-operation.utils.test.ts +30 -0
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +37 -0
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +4 -3
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +3 -8
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +65 -20
- package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +26 -28
- package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +3 -3
- package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +3 -3
- package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +3 -3
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +3 -3
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +403 -106
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +113 -2
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +1 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +197 -44
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +9 -3
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +7 -5
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +57 -177
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +239 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +56 -6
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +29 -7
- package/src/features/chat/features/workspace/components/file-content-preview/use-workspace-file-buffer.ts +42 -0
- package/src/features/chat/features/workspace/components/file-content-preview/workspace-document-preview-state.tsx +18 -0
- package/src/features/chat/features/workspace/components/file-content-preview/workspace-docx-preview.css +89 -0
- package/src/features/chat/features/workspace/components/file-content-preview/workspace-docx-preview.tsx +87 -0
- package/src/features/chat/features/workspace/components/file-content-preview/workspace-presentation-preview.tsx +82 -0
- package/src/features/chat/features/workspace/components/file-content-preview/workspace-spreadsheet-preview.tsx +180 -0
- package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +191 -0
- package/src/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state.ts +1 -4
- package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +53 -10
- package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +68 -2
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +17 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +63 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-file-viewer.utils.ts +49 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils.ts +13 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +120 -38
- package/src/features/chat/index.ts +1 -0
- package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +76 -0
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +44 -60
- package/src/features/chat/managers/chat-thread.manager.ts +123 -126
- package/src/features/chat/pages/ncp-chat-page.tsx +1 -2
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +78 -1
- package/src/features/chat/stores/chat-message-layout.store.test.ts +37 -0
- package/src/features/chat/stores/chat-message-layout.store.ts +41 -0
- package/src/features/chat/stores/chat-thread.store.ts +70 -13
- package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +25 -8
- package/src/features/settings/pages/appearance-settings-page.tsx +92 -13
- package/src/index.css +36 -2
- package/src/shared/components/__tests__/file-type-icon.test.tsx +61 -0
- package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +19 -0
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +16 -0
- package/src/shared/components/doc-browser/doc-browser-context.tsx +1 -0
- package/src/shared/components/doc-browser/doc-browser.tsx +11 -3
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +5 -0
- package/src/shared/components/doc-browser/stores/doc-browser.store.ts +6 -1
- package/src/shared/components/doc-browser/types/doc-browser.types.ts +2 -0
- package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +11 -0
- package/src/shared/components/file-type-icon.tsx +272 -0
- package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx +22 -0
- package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +17 -2
- package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +38 -0
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +82 -2
- package/src/shared/lib/api/__tests__/server-path.utils.test.ts +28 -0
- package/src/shared/lib/api/utils/server-path.utils.ts +19 -20
- package/src/shared/lib/i18n/locales/en-US/chat.json +17 -1
- package/src/shared/lib/i18n/locales/en-US/core.json +6 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +17 -1
- package/src/shared/lib/i18n/locales/zh-CN/core.json +6 -0
- package/dist/assets/api-iNBpppzj.js +0 -13
- package/dist/assets/appearance-settings-page-DMffQFR3.js +0 -1
- package/dist/assets/channels-list-page-CCtp0BRS.js +0 -8
- package/dist/assets/chat-page-H5a0dWvO.js +0 -1
- package/dist/assets/config-split-page-CrOJgB-Y.js +0 -1
- package/dist/assets/confirm-dialog-Chv50USy.js +0 -5
- package/dist/assets/desktop-update-config-KzTuXRf_.js +0 -1
- package/dist/assets/dist-BunzmC4B.js +0 -1
- package/dist/assets/dist-ChmGwvtK.js +0 -1
- package/dist/assets/doc-browser-DNRL4Zvd.js +0 -1
- package/dist/assets/doc-browser-Dg0pmkOg.js +0 -1
- package/dist/assets/doc-browser-context-BHY-0FSD.js +0 -1
- package/dist/assets/es2015-DQZ5JWwK.js +0 -41
- package/dist/assets/i18n-provider-Cu8XyZb_.js +0 -1
- package/dist/assets/index-CzfBdvK1.css +0 -1
- package/dist/assets/index-DGqHne1Q.js +0 -104
- package/dist/assets/loader-circle-CDSFuzmk.js +0 -1
- package/dist/assets/mcp-marketplace-page-BeEQK0DL.js +0 -9
- package/dist/assets/mcp-marketplace-page-D6idOTLp.js +0 -1
- package/dist/assets/model-config-page-bNNo-XyG.js +0 -1
- package/dist/assets/plus-D4DsdLN-.js +0 -1
- package/dist/assets/provider-scoped-model-input-IlgQCsoT.js +0 -1
- package/dist/assets/providers-config-page-wLo6CAJw.js +0 -1
- package/dist/assets/react-BPIsQIlO.js +0 -8
- package/dist/assets/remote-uKEYp7AR.js +0 -1
- package/dist/assets/rotate-cw-wPFPg1kH.js +0 -1
- package/dist/assets/runtime-config-page-DDn1sP4G.js +0 -1
- package/dist/assets/search-config-page-B-Cf4iiB.js +0 -1
- package/dist/assets/secrets-config-page-DPKsY8Iw.js +0 -3
- package/dist/assets/security-config-e9SSBlKr.js +0 -1
- package/dist/assets/select-BdgczSNw.js +0 -1
- package/dist/assets/setting-row-BkTVUTKx.js +0 -1
- package/dist/assets/side-dock-BsBvcJ9i.js +0 -3
- package/dist/assets/switch-Cv8Ekb3x.js +0 -1
- package/dist/assets/tag-chip-RB9uKcoC.js +0 -1
- package/dist/assets/tooltip-oQlEN-l6.js +0 -1
- package/dist/assets/use-config-l3W7y8Qz.js +0 -1
- package/dist/assets/use-confirm-dialog-BAxaVOJI.js +0 -1
- package/dist/assets/x-BBMffEN0.js +0 -1
- /package/dist/assets/{config-hints-C_iP1zl_.js → config-hints-fDrYfl0l.js} +0 -0
- /package/dist/assets/{host-capabilities-9bB_998Z.js → host-capabilities-zZvY9U63.js} +0 -0
- /package/dist/assets/{middleware-BILWbJE4.js → middleware-BLMHv9YF.js} +0 -0
package/dist/index.html
CHANGED
|
@@ -186,34 +186,40 @@
|
|
|
186
186
|
})();
|
|
187
187
|
</script>
|
|
188
188
|
<title>NextClaw</title>
|
|
189
|
-
<script type="module" crossorigin src="/assets/index-
|
|
190
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
191
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
192
|
-
<link rel="modulepreload" crossorigin href="/assets/dist-
|
|
193
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
194
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
195
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
196
|
-
<link rel="modulepreload" crossorigin href="/assets/x-
|
|
197
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
198
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
199
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
200
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
201
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
202
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
203
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
204
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
205
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
206
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
207
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
208
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
209
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
210
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
211
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
212
|
-
<link rel="modulepreload" crossorigin href="/assets/loader-circle-
|
|
213
|
-
<link rel="modulepreload" crossorigin href="/assets/skeleton-
|
|
214
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
215
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
216
|
-
<link rel="
|
|
189
|
+
<script type="module" crossorigin src="/assets/index-D_6U-M94.js"></script>
|
|
190
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-DseTPa7n.js">
|
|
191
|
+
<link rel="modulepreload" crossorigin href="/assets/react-5sjAT_6d.js">
|
|
192
|
+
<link rel="modulepreload" crossorigin href="/assets/dist-DC7f4TJH.js">
|
|
193
|
+
<link rel="modulepreload" crossorigin href="/assets/select-TYZDBNmq.js">
|
|
194
|
+
<link rel="modulepreload" crossorigin href="/assets/popover-DImguRok.js">
|
|
195
|
+
<link rel="modulepreload" crossorigin href="/assets/api-BOXOrpcJ.js">
|
|
196
|
+
<link rel="modulepreload" crossorigin href="/assets/x-DtCPHwCu.js">
|
|
197
|
+
<link rel="modulepreload" crossorigin href="/assets/confirm-dialog-GSXIvoRU.js">
|
|
198
|
+
<link rel="modulepreload" crossorigin href="/assets/search-BTqxG3ZG.js">
|
|
199
|
+
<link rel="modulepreload" crossorigin href="/assets/external-link-C6ZeSPef.js">
|
|
200
|
+
<link rel="modulepreload" crossorigin href="/assets/plus-tywizkeY.js">
|
|
201
|
+
<link rel="modulepreload" crossorigin href="/assets/refresh-cw-CHLBzWWj.js">
|
|
202
|
+
<link rel="modulepreload" crossorigin href="/assets/middleware-BLMHv9YF.js">
|
|
203
|
+
<link rel="modulepreload" crossorigin href="/assets/doc-browser-context-CGwEOhBy.js">
|
|
204
|
+
<link rel="modulepreload" crossorigin href="/assets/doc-browser-D0monHjG.js">
|
|
205
|
+
<link rel="modulepreload" crossorigin href="/assets/book-open-DWUjS6aW.js">
|
|
206
|
+
<link rel="modulepreload" crossorigin href="/assets/use-confirm-dialog-C95F6sT1.js">
|
|
207
|
+
<link rel="modulepreload" crossorigin href="/assets/download-DiP9zx-J.js">
|
|
208
|
+
<link rel="modulepreload" crossorigin href="/assets/eye-suvKmtwn.js">
|
|
209
|
+
<link rel="modulepreload" crossorigin href="/assets/folder-CMELbQGG.js">
|
|
210
|
+
<link rel="modulepreload" crossorigin href="/assets/tabs-CX-ckWoo.js">
|
|
211
|
+
<link rel="modulepreload" crossorigin href="/assets/use-viewport-layout-C6m3Nte7.js">
|
|
212
|
+
<link rel="modulepreload" crossorigin href="/assets/loader-circle-tYHStefV.js">
|
|
213
|
+
<link rel="modulepreload" crossorigin href="/assets/skeleton-CloHeg0S.js">
|
|
214
|
+
<link rel="modulepreload" crossorigin href="/assets/trash-2-SAW1Z4Po.js">
|
|
215
|
+
<link rel="modulepreload" crossorigin href="/assets/logo-badge-Cu5UGH_4.js">
|
|
216
|
+
<link rel="modulepreload" crossorigin href="/assets/host-capabilities-zZvY9U63.js">
|
|
217
|
+
<link rel="modulepreload" crossorigin href="/assets/provider-models-C_yOh6DE.js">
|
|
218
|
+
<link rel="modulepreload" crossorigin href="/assets/app-presenter-provider-CkHLbvZQ.js">
|
|
219
|
+
<link rel="modulepreload" crossorigin href="/assets/tag-chip-mCc23Rhb.js">
|
|
220
|
+
<link rel="modulepreload" crossorigin href="/assets/mcp-marketplace-page-BUzHDwW6.js">
|
|
221
|
+
<link rel="stylesheet" crossorigin href="/assets/app-presenter-provider-D3BuuOyZ.css">
|
|
222
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BFeNDQ4R.css">
|
|
217
223
|
</head>
|
|
218
224
|
|
|
219
225
|
<body>
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ui",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"dependencies": {
|
|
8
|
+
"@aiden0z/pptx-renderer": "1.2.4",
|
|
9
|
+
"@iconify/icons-vscode-icons": "^1.2.29",
|
|
10
|
+
"@iconify/react": "^6.0.2",
|
|
8
11
|
"@radix-ui/react-dialog": "^1.1.2",
|
|
9
12
|
"@radix-ui/react-label": "^2.1.0",
|
|
10
13
|
"@radix-ui/react-popover": "^1.1.15",
|
|
@@ -16,6 +19,7 @@
|
|
|
16
19
|
"@tanstack/react-query": "^5.59.20",
|
|
17
20
|
"class-variance-authority": "^0.7.1",
|
|
18
21
|
"clsx": "^2.1.1",
|
|
22
|
+
"docx-preview": "0.4.0",
|
|
19
23
|
"lucide-react": "^0.462.0",
|
|
20
24
|
"qrcode": "^1.5.4",
|
|
21
25
|
"react": "^18.3.1",
|
|
@@ -26,16 +30,17 @@
|
|
|
26
30
|
"rxjs": "^7.8.2",
|
|
27
31
|
"sonner": "^1.7.1",
|
|
28
32
|
"tailwind-merge": "^2.5.4",
|
|
33
|
+
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
29
34
|
"zod": "^3.23.8",
|
|
30
35
|
"zustand": "^5.0.2",
|
|
31
|
-
"@nextclaw/agent-chat-ui": "0.6.
|
|
32
|
-
"@nextclaw/client-sdk": "0.5.
|
|
33
|
-
"@nextclaw/ncp-http-agent-client": "0.4.3",
|
|
36
|
+
"@nextclaw/agent-chat-ui": "0.6.4",
|
|
37
|
+
"@nextclaw/client-sdk": "0.5.4",
|
|
34
38
|
"@nextclaw/ncp": "0.7.3",
|
|
35
|
-
"@nextclaw/ncp-
|
|
39
|
+
"@nextclaw/ncp-react": "0.5.4",
|
|
40
|
+
"@nextclaw/ncp-toolkit": "0.6.4",
|
|
36
41
|
"@nextclaw/shared": "0.4.3",
|
|
37
42
|
"@nextclaw/agent-chat": "0.3.2",
|
|
38
|
-
"@nextclaw/ncp-
|
|
43
|
+
"@nextclaw/ncp-http-agent-client": "0.4.3"
|
|
39
44
|
},
|
|
40
45
|
"devDependencies": {
|
|
41
46
|
"@testing-library/react": "^16.3.0",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { ChatPopoverContent } from "@/features/chat/components/chat-popover-content";
|
|
5
|
+
import { Popover, PopoverTrigger } from "@/shared/components/ui/popover";
|
|
6
|
+
|
|
7
|
+
function ChatPopoverHarness() {
|
|
8
|
+
const [open, setOpen] = useState(true);
|
|
9
|
+
return (
|
|
10
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
11
|
+
<PopoverTrigger>Sessions</PopoverTrigger>
|
|
12
|
+
<ChatPopoverContent>Session search</ChatPopoverContent>
|
|
13
|
+
</Popover>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function appendComposerFocusTarget(): HTMLElement {
|
|
18
|
+
const shell = document.createElement("div");
|
|
19
|
+
shell.className = "nextclaw-chat-input-bar-shell";
|
|
20
|
+
const composer = document.createElement("div");
|
|
21
|
+
composer.setAttribute("contenteditable", "true");
|
|
22
|
+
composer.setAttribute("role", "textbox");
|
|
23
|
+
shell.appendChild(composer);
|
|
24
|
+
document.body.appendChild(shell);
|
|
25
|
+
return composer;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("ChatPopoverContent", () => {
|
|
29
|
+
it("keeps chat popovers open when the composer receives programmatic focus", () => {
|
|
30
|
+
const composer = appendComposerFocusTarget();
|
|
31
|
+
try {
|
|
32
|
+
render(<ChatPopoverHarness />);
|
|
33
|
+
|
|
34
|
+
fireEvent.focusIn(composer);
|
|
35
|
+
|
|
36
|
+
expect(screen.getByText("Session search")).toBeTruthy();
|
|
37
|
+
} finally {
|
|
38
|
+
composer.closest(".nextclaw-chat-input-bar-shell")?.remove();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react";
|
|
2
|
+
import { PopoverContent } from "@/shared/components/ui/popover";
|
|
3
|
+
|
|
4
|
+
function isChatComposerFocusTarget(target: EventTarget | null): boolean {
|
|
5
|
+
return target instanceof HTMLElement && Boolean(
|
|
6
|
+
target.closest('.nextclaw-chat-input-bar-shell [role="textbox"][contenteditable="true"]'),
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ChatPopoverContent = forwardRef<
|
|
11
|
+
ElementRef<typeof PopoverContent>,
|
|
12
|
+
ComponentPropsWithoutRef<typeof PopoverContent>
|
|
13
|
+
>(function ChatPopoverContent({ onFocusOutside, ...props }, ref) {
|
|
14
|
+
return (
|
|
15
|
+
<PopoverContent
|
|
16
|
+
{...props}
|
|
17
|
+
ref={ref}
|
|
18
|
+
onFocusOutside={(event) => {
|
|
19
|
+
onFocusOutside?.(event);
|
|
20
|
+
if (!event.defaultPrevented && isChatComposerFocusTarget(event.detail.originalEvent.target)) {
|
|
21
|
+
event.preventDefault();
|
|
22
|
+
}
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
});
|
package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event";
|
|
|
3
3
|
import type { NcpMessage } from "@nextclaw/ncp";
|
|
4
4
|
import { beforeEach, expect, it, vi } from "vitest";
|
|
5
5
|
import { ChatConversationContent } from "@/features/chat/components/conversation/chat-conversation-content";
|
|
6
|
+
import { useChatMessageLayoutStore } from "@/features/chat/stores/chat-message-layout.store";
|
|
6
7
|
|
|
7
8
|
const captures = vi.hoisted(() => ({
|
|
8
9
|
isAtBottom: true,
|
|
@@ -52,11 +53,23 @@ function renderContent(options: { bottomSlot?: React.ReactNode } = {}) {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
beforeEach(() => {
|
|
56
|
+
useChatMessageLayoutStore.getState().setLayout("card");
|
|
55
57
|
captures.isAtBottom = true;
|
|
56
58
|
captures.onScroll.mockReset();
|
|
57
59
|
captures.scrollToBottom.mockReset();
|
|
58
60
|
});
|
|
59
61
|
|
|
62
|
+
it("uses the centered reading track in flat message layout", () => {
|
|
63
|
+
useChatMessageLayoutStore.getState().setLayout("flat");
|
|
64
|
+
|
|
65
|
+
renderContent();
|
|
66
|
+
|
|
67
|
+
const track = screen
|
|
68
|
+
.getByTestId("chat-message-list")
|
|
69
|
+
.closest('[data-chat-conversation-track="flat"]');
|
|
70
|
+
expect(track?.className).toContain("max-w-[min(52rem,100%)]");
|
|
71
|
+
});
|
|
72
|
+
|
|
60
73
|
it("hides the scroll-to-bottom action while the conversation is already at the bottom", () => {
|
|
61
74
|
renderContent();
|
|
62
75
|
|
|
@@ -13,8 +13,7 @@ import type { NcpSessionSummaryView, SessionEntryView } from "@/shared/lib/api";
|
|
|
13
13
|
|
|
14
14
|
const mocks = vi.hoisted(() => ({
|
|
15
15
|
deleteSession: vi.fn(),
|
|
16
|
-
|
|
17
|
-
openSessionCronPanel: vi.fn(),
|
|
16
|
+
toggleWorkspacePanel: vi.fn(),
|
|
18
17
|
selectSession: vi.fn(),
|
|
19
18
|
sessionItems: [] as NcpSessionListItemView[],
|
|
20
19
|
isSessionListLoading: false,
|
|
@@ -24,8 +23,7 @@ vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
|
|
|
24
23
|
usePresenter: () => ({
|
|
25
24
|
chatThreadManager: {
|
|
26
25
|
deleteSession: mocks.deleteSession,
|
|
27
|
-
|
|
28
|
-
openSessionCronPanel: mocks.openSessionCronPanel,
|
|
26
|
+
toggleWorkspacePanel: mocks.toggleWorkspacePanel,
|
|
29
27
|
},
|
|
30
28
|
chatSessionListManager: {
|
|
31
29
|
selectSession: mocks.selectSession,
|
|
@@ -120,8 +118,7 @@ function renderHeaderSection() {
|
|
|
120
118
|
describe("ChatConversationHeaderSection", () => {
|
|
121
119
|
beforeEach(() => {
|
|
122
120
|
mocks.deleteSession.mockReset();
|
|
123
|
-
mocks.
|
|
124
|
-
mocks.openSessionCronPanel.mockReset();
|
|
121
|
+
mocks.toggleWorkspacePanel.mockReset();
|
|
125
122
|
mocks.selectSession.mockReset();
|
|
126
123
|
mocks.isSessionListLoading = false;
|
|
127
124
|
viewportLayoutManager.resetForTests();
|
|
@@ -195,16 +192,29 @@ describe("ChatConversationHeaderSection", () => {
|
|
|
195
192
|
];
|
|
196
193
|
});
|
|
197
194
|
|
|
198
|
-
it("
|
|
195
|
+
it("opens the current session workspace overview from the header", async () => {
|
|
199
196
|
const user = userEvent.setup();
|
|
200
197
|
|
|
201
198
|
renderHeaderSection();
|
|
202
|
-
await user.click(screen.getByRole("button", { name: "
|
|
199
|
+
await user.click(screen.getByRole("button", { name: "Open session workspace" }));
|
|
203
200
|
|
|
204
|
-
expect(mocks.
|
|
205
|
-
|
|
206
|
-
|
|
201
|
+
expect(mocks.toggleWorkspacePanel).toHaveBeenCalledWith("parent-session-1");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("shows the close workspace action while the current session panel is open", () => {
|
|
205
|
+
useChatThreadStore.setState({
|
|
206
|
+
snapshot: {
|
|
207
|
+
...useChatThreadStore.getState().snapshot,
|
|
208
|
+
workspacePanelParentKey: "parent-session-1",
|
|
209
|
+
activeWorkspacePanelKind: "overview",
|
|
210
|
+
},
|
|
207
211
|
});
|
|
212
|
+
|
|
213
|
+
renderHeaderSection();
|
|
214
|
+
|
|
215
|
+
expect(
|
|
216
|
+
screen.getByRole("button", { name: "Close session workspace" }),
|
|
217
|
+
).toBeTruthy();
|
|
208
218
|
});
|
|
209
219
|
|
|
210
220
|
it("uses the collapsed desktop title as a session switcher", async () => {
|
package/src/features/chat/components/conversation/__tests__/chat-conversation-header.test.tsx
CHANGED
|
@@ -51,8 +51,8 @@ describe("ChatConversationHeader", () => {
|
|
|
51
51
|
sessionKey="session-1"
|
|
52
52
|
canDeleteSession
|
|
53
53
|
isDeletePending={false}
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
isWorkspaceOpen={false}
|
|
55
|
+
onToggleWorkspace={vi.fn()}
|
|
56
56
|
onDeleteSession={vi.fn()}
|
|
57
57
|
/>
|
|
58
58
|
),
|
|
@@ -14,16 +14,23 @@ vi.mock("@/features/chat/features/workspace/components/chat-session-workspace-pa
|
|
|
14
14
|
ChatSessionWorkspacePanel: ({
|
|
15
15
|
sessionProjectRoot,
|
|
16
16
|
sessionWorkingDir,
|
|
17
|
+
workspacePanelWidth,
|
|
17
18
|
}: {
|
|
18
19
|
sessionProjectRoot: string | null;
|
|
19
20
|
sessionWorkingDir: string | null;
|
|
21
|
+
workspacePanelWidth: number;
|
|
20
22
|
}) => {
|
|
21
|
-
mocks.panelProps({
|
|
23
|
+
mocks.panelProps({
|
|
24
|
+
sessionProjectRoot,
|
|
25
|
+
sessionWorkingDir,
|
|
26
|
+
workspacePanelWidth,
|
|
27
|
+
});
|
|
22
28
|
return (
|
|
23
29
|
<div
|
|
24
30
|
data-testid="workspace-panel"
|
|
25
31
|
data-project-root={sessionProjectRoot ?? ""}
|
|
26
32
|
data-working-dir={sessionWorkingDir ?? ""}
|
|
33
|
+
data-width={workspacePanelWidth}
|
|
27
34
|
/>
|
|
28
35
|
);
|
|
29
36
|
},
|
|
@@ -85,6 +92,7 @@ describe("ChatConversationWorkspaceSection", () => {
|
|
|
85
92
|
workspaceNavigationHistoryIndex: 0,
|
|
86
93
|
sessionProjectRoot: null,
|
|
87
94
|
sessionWorkingDir: null,
|
|
95
|
+
workspacePanelWidth: 620,
|
|
88
96
|
},
|
|
89
97
|
});
|
|
90
98
|
useChatQueryStore.setState({
|
|
@@ -122,11 +130,30 @@ describe("ChatConversationWorkspaceSection", () => {
|
|
|
122
130
|
expect(panel.getAttribute("data-working-dir")).toBe(
|
|
123
131
|
"/Users/peiwang/Projects/nextbot",
|
|
124
132
|
);
|
|
133
|
+
expect(panel.getAttribute("data-width")).toBe("620");
|
|
125
134
|
expect(mocks.panelProps).toHaveBeenCalledWith(
|
|
126
135
|
expect.objectContaining({
|
|
127
136
|
sessionProjectRoot: "/Users/peiwang/Projects/nextbot",
|
|
128
137
|
sessionWorkingDir: "/Users/peiwang/Projects/nextbot",
|
|
138
|
+
workspacePanelWidth: 620,
|
|
129
139
|
}),
|
|
130
140
|
);
|
|
131
141
|
});
|
|
142
|
+
|
|
143
|
+
it("renders an explicitly opened overview without requiring existing workspace resources", () => {
|
|
144
|
+
useChatThreadStore.getState().setSnapshot({
|
|
145
|
+
activeWorkspacePanelKind: "overview",
|
|
146
|
+
workspaceFileTabs: [],
|
|
147
|
+
activeWorkspaceFileKey: null,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
render(
|
|
151
|
+
<ChatConversationWorkspaceSection
|
|
152
|
+
layoutMode="desktop"
|
|
153
|
+
sessionKey="session-1"
|
|
154
|
+
/>,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
expect(screen.getByTestId("workspace-panel")).toBeTruthy();
|
|
158
|
+
});
|
|
132
159
|
});
|
|
@@ -3,6 +3,7 @@ import { useStickyBottomScroll } from "@nextclaw/agent-chat-ui";
|
|
|
3
3
|
import type { NcpMessage } from "@nextclaw/ncp";
|
|
4
4
|
import { ArrowDown } from "lucide-react";
|
|
5
5
|
import { ChatMessageListContainer } from "@/features/chat/features/message/components/chat-message-list.container";
|
|
6
|
+
import { ChatConversationTrack } from "@/features/chat/components/conversation/chat-conversation-track";
|
|
6
7
|
import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
|
|
7
8
|
import { t } from "@/shared/lib/i18n";
|
|
8
9
|
|
|
@@ -60,17 +61,17 @@ export function ChatConversationContent({
|
|
|
60
61
|
) : (
|
|
61
62
|
<>
|
|
62
63
|
{hideEmptyHint || !hasMessages ? null : (
|
|
63
|
-
<
|
|
64
|
+
<ChatConversationTrack className="py-4 sm:py-5">
|
|
64
65
|
<ChatMessageListContainer
|
|
65
66
|
messages={messages}
|
|
66
67
|
isSending={hasMessages && isSending && isAwaitingAssistantOutput}
|
|
67
68
|
/>
|
|
68
|
-
</
|
|
69
|
+
</ChatConversationTrack>
|
|
69
70
|
)}
|
|
70
71
|
{bottomSlot ? (
|
|
71
|
-
<
|
|
72
|
+
<ChatConversationTrack className="pb-4 sm:pb-5">
|
|
72
73
|
{bottomSlot}
|
|
73
|
-
</
|
|
74
|
+
</ChatConversationTrack>
|
|
74
75
|
) : null}
|
|
75
76
|
</>
|
|
76
77
|
)}
|
|
@@ -5,7 +5,6 @@ import { ChatSessionProjectBadge } from "@/features/chat/features/session/compon
|
|
|
5
5
|
import { ChatSessionTitleSwitcher } from "@/features/chat/features/session/components/session-header/chat-session-title-switcher";
|
|
6
6
|
import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
|
|
7
7
|
import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
|
|
8
|
-
import { useChatConversationWorkspaceState } from "@/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state";
|
|
9
8
|
import { useNcpChatSelectedSession } from "@/features/chat/features/ncp/hooks/use-ncp-chat-derived-state";
|
|
10
9
|
import { sessionDisplayName } from "@/features/chat/features/session/utils/chat-session-display.utils";
|
|
11
10
|
import {
|
|
@@ -59,8 +58,6 @@ export function ChatConversationHeaderSection({
|
|
|
59
58
|
const sessionProjectName =
|
|
60
59
|
selectedSession?.projectName ?? getSessionProjectName(sessionProjectRoot);
|
|
61
60
|
const canDeleteSession = Boolean(selectedSession);
|
|
62
|
-
const { childSessionTabs, sessionCronJobs } =
|
|
63
|
-
useChatConversationWorkspaceState(snapshot, sessionKey);
|
|
64
61
|
const shouldShowSessionHeader = Boolean(sessionKey || sessionTypeLabel);
|
|
65
62
|
const sessionHeaderTitle =
|
|
66
63
|
(selectedSession ? sessionDisplayName(selectedSession) : undefined) ||
|
|
@@ -72,20 +69,16 @@ export function ChatConversationHeaderSection({
|
|
|
72
69
|
selectedSession?.agentId?.trim() ?? selectedAgentId?.trim() ?? "";
|
|
73
70
|
const shouldShowHeaderAgentAvatar =
|
|
74
71
|
normalizedAgentId.length > 0 && normalizedAgentId.toLowerCase() !== "main";
|
|
75
|
-
const
|
|
72
|
+
const isWorkspaceOpen = Boolean(
|
|
73
|
+
sessionKey &&
|
|
74
|
+
snapshot.workspacePanelParentKey === sessionKey &&
|
|
75
|
+
snapshot.activeWorkspacePanelKind,
|
|
76
|
+
);
|
|
77
|
+
const toggleWorkspace = () => {
|
|
76
78
|
if (!sessionKey) {
|
|
77
79
|
return;
|
|
78
80
|
}
|
|
79
|
-
presenter.chatThreadManager.
|
|
80
|
-
parentSessionKey: sessionKey,
|
|
81
|
-
activeChildSessionKey: childSessionTabs[0]?.sessionKey ?? null,
|
|
82
|
-
});
|
|
83
|
-
};
|
|
84
|
-
const openSessionCronJobs = () => {
|
|
85
|
-
if (!sessionKey || sessionCronJobs.length === 0) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
presenter.chatThreadManager.openSessionCronPanel(sessionKey);
|
|
81
|
+
presenter.chatThreadManager.toggleWorkspacePanel(sessionKey);
|
|
89
82
|
};
|
|
90
83
|
|
|
91
84
|
return (
|
|
@@ -148,10 +141,8 @@ export function ChatConversationHeaderSection({
|
|
|
148
141
|
isDeletePending={snapshot.isDeletePending}
|
|
149
142
|
projectRoot={sessionProjectRoot}
|
|
150
143
|
metadata={selectedSession?.metadata ?? null}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
onOpenChildSessions={openChildSessions}
|
|
154
|
-
onOpenSessionCronJobs={openSessionCronJobs}
|
|
144
|
+
isWorkspaceOpen={isWorkspaceOpen}
|
|
145
|
+
onToggleWorkspace={toggleWorkspace}
|
|
155
146
|
onDeleteSession={presenter.chatThreadManager.deleteSession}
|
|
156
147
|
/>
|
|
157
148
|
) : null
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { useChatMessageLayoutStore } from "@/features/chat/stores/chat-message-layout.store";
|
|
3
|
+
import { cn } from "@/shared/lib/utils";
|
|
4
|
+
|
|
5
|
+
type ChatConversationTrackProps = {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
width?: "messages" | "composer";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function ChatConversationTrack({
|
|
12
|
+
children,
|
|
13
|
+
className,
|
|
14
|
+
width = "messages",
|
|
15
|
+
}: ChatConversationTrackProps) {
|
|
16
|
+
const layout = useChatMessageLayoutStore((state) => state.layout);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
data-chat-conversation-track={layout}
|
|
21
|
+
data-chat-conversation-track-width={width}
|
|
22
|
+
className={cn(
|
|
23
|
+
"mx-auto w-full px-4 sm:px-6",
|
|
24
|
+
layout === "flat"
|
|
25
|
+
? width === "composer"
|
|
26
|
+
? "max-w-[min(54rem,100%)]"
|
|
27
|
+
: "max-w-[min(52rem,100%)]"
|
|
28
|
+
: "max-w-[min(1120px,100%)]",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -42,6 +42,7 @@ export function ChatConversationWorkspaceSection({
|
|
|
42
42
|
sessionWorkingDir={
|
|
43
43
|
selectedSession?.workingDir ?? selectedSession?.projectRoot ?? null
|
|
44
44
|
}
|
|
45
|
+
workspacePanelWidth={snapshot.workspacePanelWidth}
|
|
45
46
|
displayMode={layoutMode === "mobile" ? "overlay" : "docked"}
|
|
46
47
|
/>
|
|
47
48
|
);
|
|
@@ -3,9 +3,9 @@ import { IconActionButton } from "@/shared/components/ui/actions/icon-action-but
|
|
|
3
3
|
import { Input } from "@/shared/components/ui/input";
|
|
4
4
|
import {
|
|
5
5
|
Popover,
|
|
6
|
-
PopoverContent,
|
|
7
6
|
PopoverTrigger,
|
|
8
7
|
} from "@/shared/components/ui/popover";
|
|
8
|
+
import { ChatPopoverContent } from "@/features/chat/components/chat-popover-content";
|
|
9
9
|
import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
|
|
10
10
|
import { ChatSessionTypeMenu } from "@/features/chat/features/session-type/components/chat-session-type-menu";
|
|
11
11
|
import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
@@ -175,7 +175,7 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
175
175
|
)}
|
|
176
176
|
/>
|
|
177
177
|
</PopoverTrigger>
|
|
178
|
-
<
|
|
178
|
+
<ChatPopoverContent
|
|
179
179
|
align="start"
|
|
180
180
|
side="right"
|
|
181
181
|
className="w-56 rounded-2xl border border-border bg-popover p-1.5 text-popover-foreground shadow-[0_24px_60px_-28px_rgba(15,23,42,0.38)]"
|
|
@@ -188,7 +188,7 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
188
188
|
onCreateMenuOpenChange(false);
|
|
189
189
|
}}
|
|
190
190
|
/>
|
|
191
|
-
</
|
|
191
|
+
</ChatPopoverContent>
|
|
192
192
|
</Popover>
|
|
193
193
|
) : null}
|
|
194
194
|
</div>
|
|
@@ -238,7 +238,7 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
238
238
|
)}
|
|
239
239
|
/>
|
|
240
240
|
</PopoverTrigger>
|
|
241
|
-
<
|
|
241
|
+
<ChatPopoverContent
|
|
242
242
|
align="end"
|
|
243
243
|
className="w-56 rounded-2xl border border-border bg-popover p-1.5 text-popover-foreground shadow-[0_24px_60px_-28px_rgba(15,23,42,0.38)]"
|
|
244
244
|
>
|
|
@@ -250,7 +250,7 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
250
250
|
onCreateMenuOpenChange(false);
|
|
251
251
|
}}
|
|
252
252
|
/>
|
|
253
|
-
</
|
|
253
|
+
</ChatPopoverContent>
|
|
254
254
|
</Popover>
|
|
255
255
|
) : null}
|
|
256
256
|
</div>
|
|
@@ -315,7 +315,7 @@ export function ChatSidebarMobileToolbar(props: ChatSidebarToolbarProps) {
|
|
|
315
315
|
className="h-9 w-9 shrink-0 rounded-full bg-muted text-foreground shadow-none hover:bg-accent hover:text-accent-foreground"
|
|
316
316
|
/>
|
|
317
317
|
</PopoverTrigger>
|
|
318
|
-
<
|
|
318
|
+
<ChatPopoverContent
|
|
319
319
|
align="end"
|
|
320
320
|
className="w-60 rounded-3xl border border-border bg-popover p-2 text-popover-foreground shadow-[0_24px_70px_-30px_rgba(15,23,42,0.45)]"
|
|
321
321
|
>
|
|
@@ -329,7 +329,7 @@ export function ChatSidebarMobileToolbar(props: ChatSidebarToolbarProps) {
|
|
|
329
329
|
onCreateMenuOpenChange(false);
|
|
330
330
|
}}
|
|
331
331
|
/>
|
|
332
|
-
</
|
|
332
|
+
</ChatPopoverContent>
|
|
333
333
|
</Popover>
|
|
334
334
|
) : (
|
|
335
335
|
<IconActionButton
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { NavLink } from "react-router-dom";
|
|
2
2
|
import {
|
|
3
3
|
Popover,
|
|
4
|
-
PopoverContent,
|
|
5
4
|
PopoverTrigger,
|
|
6
5
|
} from "@/shared/components/ui/popover";
|
|
6
|
+
import { ChatPopoverContent } from "@/features/chat/components/chat-popover-content";
|
|
7
7
|
import {
|
|
8
8
|
Select,
|
|
9
9
|
SelectContent,
|
|
@@ -52,12 +52,6 @@ type ChatSidebarUtilityMenuProps = {
|
|
|
52
52
|
collapsed?: boolean;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
function isChatComposerFocusTarget(target: EventTarget | null): boolean {
|
|
56
|
-
return target instanceof HTMLElement && Boolean(
|
|
57
|
-
target.closest('.nextclaw-chat-input-bar-shell [role="textbox"][contenteditable="true"]'),
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
55
|
export function ChatSidebarUtilityMenu({
|
|
62
56
|
isOpen,
|
|
63
57
|
onOpenChange,
|
|
@@ -118,15 +112,10 @@ export function ChatSidebarUtilityMenu({
|
|
|
118
112
|
</button>
|
|
119
113
|
</PopoverTrigger>
|
|
120
114
|
)}
|
|
121
|
-
<
|
|
115
|
+
<ChatPopoverContent
|
|
122
116
|
side={collapsed ? "right" : "top"}
|
|
123
117
|
align={collapsed ? "end" : "start"}
|
|
124
118
|
className="w-64 p-2"
|
|
125
|
-
onFocusOutside={(event) => {
|
|
126
|
-
if (isChatComposerFocusTarget(event.detail.originalEvent.target)) {
|
|
127
|
-
event.preventDefault();
|
|
128
|
-
}
|
|
129
|
-
}}
|
|
130
119
|
>
|
|
131
120
|
<div className="space-y-1">
|
|
132
121
|
<NavLink
|
|
@@ -177,7 +166,7 @@ export function ChatSidebarUtilityMenu({
|
|
|
177
166
|
onSelect={onSelectLanguage}
|
|
178
167
|
onCloseMenu={() => onOpenChange(false)}
|
|
179
168
|
/>
|
|
180
|
-
</
|
|
169
|
+
</ChatPopoverContent>
|
|
181
170
|
</Popover>
|
|
182
171
|
);
|
|
183
172
|
}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type MutableRefObject,
|
|
7
7
|
} from 'react';
|
|
8
8
|
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
9
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
9
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
10
10
|
import {
|
|
11
11
|
createChatComposerTextNode,
|
|
12
12
|
type ChatComposerNode,
|
|
@@ -26,9 +26,14 @@ import type {
|
|
|
26
26
|
SessionConversationInputSnapshot,
|
|
27
27
|
} from '@/features/chat/features/conversation/hooks/use-session-conversation-input-state';
|
|
28
28
|
import { useSessionConversationInputState } from '@/features/chat/features/conversation/hooks/use-session-conversation-input-state';
|
|
29
|
+
import { useChatMessageLayoutStore } from '@/features/chat/stores/chat-message-layout.store';
|
|
29
30
|
|
|
30
31
|
const uploadNcpAssetsMock = vi.hoisted(() => vi.fn());
|
|
31
32
|
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
useChatMessageLayoutStore.getState().setLayout('card');
|
|
35
|
+
});
|
|
36
|
+
|
|
32
37
|
vi.mock('@/app/hooks/use-viewport-layout', () => ({
|
|
33
38
|
useViewportLayout: () => ({ isDesktop: true, isMobile: false }),
|
|
34
39
|
}));
|
|
@@ -190,6 +195,20 @@ function StreamingSessionConversationInputHarness({
|
|
|
190
195
|
}
|
|
191
196
|
|
|
192
197
|
describe('SessionConversationInput streaming stability', () => {
|
|
198
|
+
it('aligns the default input with the flat message reading track', () => {
|
|
199
|
+
useChatMessageLayoutStore.getState().setLayout('flat');
|
|
200
|
+
const controlRef: MutableRefObject<StreamingInputControl | null> = { current: null };
|
|
201
|
+
|
|
202
|
+
render(<StreamingSessionConversationInputHarness controlRef={controlRef} />);
|
|
203
|
+
|
|
204
|
+
const track = document.querySelector(
|
|
205
|
+
'[data-chat-conversation-track="flat"][data-chat-conversation-track-width="composer"]',
|
|
206
|
+
);
|
|
207
|
+
expect(track?.className).toContain('max-w-[min(54rem,100%)]');
|
|
208
|
+
expect(track?.querySelector('.nextclaw-chat-input-bar-shell')).toBeTruthy();
|
|
209
|
+
expect(track?.firstElementChild?.className).toContain('px-0');
|
|
210
|
+
});
|
|
211
|
+
|
|
193
212
|
it('keeps IME composition stable while simulated streamed output rerenders the owner', async () => {
|
|
194
213
|
const controlRef: MutableRefObject<StreamingInputControl | null> = { current: null };
|
|
195
214
|
render(<StreamingSessionConversationInputHarness controlRef={controlRef} />);
|