@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
|
@@ -5,10 +5,10 @@ import { ChatSessionTypeOptionItem } from '@/features/chat/features/session-type
|
|
|
5
5
|
import type { ChatSessionTypeOption } from '@/features/chat/features/session-type/utils/chat-session-type.utils';
|
|
6
6
|
import {
|
|
7
7
|
Popover,
|
|
8
|
-
PopoverContent,
|
|
9
8
|
PopoverTrigger,
|
|
10
9
|
createPopoverAvailableHeightLimit,
|
|
11
10
|
} from '@/shared/components/ui/popover';
|
|
11
|
+
import { ChatPopoverContent } from '@/features/chat/components/chat-popover-content';
|
|
12
12
|
import { t } from '@/shared/lib/i18n';
|
|
13
13
|
|
|
14
14
|
type SessionTypeOption = ChatSessionTypeOption;
|
|
@@ -59,7 +59,7 @@ export function ChatWelcomeSessionTypePicker({
|
|
|
59
59
|
<ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground/70" />
|
|
60
60
|
</button>
|
|
61
61
|
</PopoverTrigger>
|
|
62
|
-
<
|
|
62
|
+
<ChatPopoverContent
|
|
63
63
|
align="start"
|
|
64
64
|
className="flex w-[min(16rem,calc(100vw-1rem))] flex-col overflow-hidden rounded-2xl border border-border bg-popover p-1.5 text-popover-foreground shadow-[0_24px_60px_-28px_rgba(15,23,42,0.38)]"
|
|
65
65
|
style={{ maxHeight: SESSION_TYPE_PICKER_MAX_HEIGHT }}
|
|
@@ -76,7 +76,7 @@ export function ChatWelcomeSessionTypePicker({
|
|
|
76
76
|
/>
|
|
77
77
|
))}
|
|
78
78
|
</div>
|
|
79
|
-
</
|
|
79
|
+
</ChatPopoverContent>
|
|
80
80
|
</Popover>
|
|
81
81
|
);
|
|
82
82
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
1
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import type {
|
|
4
4
|
ChatFileOpenActionViewModel,
|
|
5
5
|
ChatFileOperationBlockViewModel,
|
|
@@ -10,6 +10,11 @@ import { t } from "@/shared/lib/i18n";
|
|
|
10
10
|
|
|
11
11
|
const serverPathReadMock = vi.fn();
|
|
12
12
|
const serverPathBrowseMock = vi.fn();
|
|
13
|
+
const renderDocxMock = vi.fn();
|
|
14
|
+
const readSpreadsheetMock = vi.fn();
|
|
15
|
+
const sheetToJsonMock = vi.fn();
|
|
16
|
+
const openPresentationMock = vi.fn();
|
|
17
|
+
const destroyPresentationMock = vi.fn();
|
|
13
18
|
|
|
14
19
|
type RenderWorkspaceFilePreviewOptions = {
|
|
15
20
|
file?: Partial<ChatWorkspaceFileTab>;
|
|
@@ -20,6 +25,7 @@ type RenderWorkspaceFilePreviewOptions = {
|
|
|
20
25
|
};
|
|
21
26
|
|
|
22
27
|
type TextReadDataOverrides = {
|
|
28
|
+
kind?: "text" | "markdown" | "binary";
|
|
23
29
|
languageHint?: string;
|
|
24
30
|
resolvedPath?: string;
|
|
25
31
|
text?: string;
|
|
@@ -53,6 +59,24 @@ vi.mock("@nextclaw/agent-chat-ui", () => ({
|
|
|
53
59
|
),
|
|
54
60
|
}));
|
|
55
61
|
|
|
62
|
+
vi.mock("docx-preview", () => ({
|
|
63
|
+
renderAsync: (...args: unknown[]) => renderDocxMock(...args),
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
vi.mock("xlsx", () => ({
|
|
67
|
+
read: (...args: unknown[]) => readSpreadsheetMock(...args),
|
|
68
|
+
utils: {
|
|
69
|
+
sheet_to_json: (...args: unknown[]) => sheetToJsonMock(...args),
|
|
70
|
+
},
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
vi.mock("@aiden0z/pptx-renderer", () => ({
|
|
74
|
+
PptxViewer: {
|
|
75
|
+
open: (...args: unknown[]) => openPresentationMock(...args),
|
|
76
|
+
},
|
|
77
|
+
RECOMMENDED_ZIP_LIMITS: { maxEntries: 4_000 },
|
|
78
|
+
}));
|
|
79
|
+
|
|
56
80
|
function buildWorkspaceFile(
|
|
57
81
|
overrides: Partial<ChatWorkspaceFileTab>,
|
|
58
82
|
): ChatWorkspaceFileTab {
|
|
@@ -87,7 +111,7 @@ function renderWorkspaceFilePreview({
|
|
|
87
111
|
sessionProjectRoot = "/tmp",
|
|
88
112
|
sessionWorkingDir = "/tmp",
|
|
89
113
|
}: RenderWorkspaceFilePreviewOptions = {}) {
|
|
90
|
-
render(
|
|
114
|
+
const view = render(
|
|
91
115
|
<ChatSessionWorkspaceFilePreview
|
|
92
116
|
file={buildWorkspaceFile(file ?? {})}
|
|
93
117
|
sessionProjectRoot={sessionProjectRoot}
|
|
@@ -96,77 +120,106 @@ function renderWorkspaceFilePreview({
|
|
|
96
120
|
onFileOpen={onFileOpen}
|
|
97
121
|
/>,
|
|
98
122
|
);
|
|
99
|
-
return { onFileOpen };
|
|
123
|
+
return { ...view, onFileOpen };
|
|
100
124
|
}
|
|
101
125
|
|
|
102
126
|
function mockWorkspaceBreadcrumbBrowseTree() {
|
|
103
|
-
serverPathBrowseMock.mockImplementation(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
serverPathBrowseMock.mockImplementation(
|
|
128
|
+
({ path }: { path?: string | null }) => {
|
|
129
|
+
if (path === "/tmp/workspace/src/components") {
|
|
130
|
+
return {
|
|
131
|
+
isLoading: false,
|
|
132
|
+
error: null,
|
|
133
|
+
data: {
|
|
134
|
+
currentPath: "/tmp/workspace/src/components",
|
|
135
|
+
parentPath: "/tmp/workspace/src",
|
|
136
|
+
homePath: "/Users/demo",
|
|
137
|
+
breadcrumbs: [
|
|
138
|
+
{ label: "workspace", path: "/tmp/workspace" },
|
|
139
|
+
{ label: "src", path: "/tmp/workspace/src" },
|
|
140
|
+
{ label: "components", path: "/tmp/workspace/src/components" },
|
|
141
|
+
],
|
|
142
|
+
entries: [
|
|
143
|
+
{
|
|
144
|
+
name: "button.tsx",
|
|
145
|
+
path: "/tmp/workspace/src/components/button.tsx",
|
|
146
|
+
kind: "file",
|
|
147
|
+
hidden: false,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (path === "/tmp/workspace/src") {
|
|
155
|
+
return {
|
|
156
|
+
isLoading: false,
|
|
157
|
+
error: null,
|
|
158
|
+
data: {
|
|
159
|
+
currentPath: "/tmp/workspace/src",
|
|
160
|
+
parentPath: "/tmp/workspace",
|
|
161
|
+
homePath: "/Users/demo",
|
|
162
|
+
breadcrumbs: [
|
|
163
|
+
{ label: "workspace", path: "/tmp/workspace" },
|
|
164
|
+
{ label: "src", path: "/tmp/workspace/src" },
|
|
165
|
+
],
|
|
166
|
+
entries: [
|
|
167
|
+
{
|
|
168
|
+
name: "components",
|
|
169
|
+
path: "/tmp/workspace/src/components",
|
|
170
|
+
kind: "directory",
|
|
171
|
+
hidden: false,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "index.ts",
|
|
175
|
+
path: "/tmp/workspace/src/index.ts",
|
|
176
|
+
kind: "file",
|
|
177
|
+
hidden: false,
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
128
183
|
|
|
129
|
-
if (path === "/tmp/workspace/src") {
|
|
130
184
|
return {
|
|
131
185
|
isLoading: false,
|
|
132
|
-
error:
|
|
133
|
-
data:
|
|
134
|
-
currentPath: "/tmp/workspace/src",
|
|
135
|
-
parentPath: "/tmp/workspace",
|
|
136
|
-
homePath: "/Users/demo",
|
|
137
|
-
breadcrumbs: [
|
|
138
|
-
{ label: "workspace", path: "/tmp/workspace" },
|
|
139
|
-
{ label: "src", path: "/tmp/workspace/src" },
|
|
140
|
-
],
|
|
141
|
-
entries: [
|
|
142
|
-
{
|
|
143
|
-
name: "components",
|
|
144
|
-
path: "/tmp/workspace/src/components",
|
|
145
|
-
kind: "directory",
|
|
146
|
-
hidden: false,
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
name: "index.ts",
|
|
150
|
-
path: "/tmp/workspace/src/index.ts",
|
|
151
|
-
kind: "file",
|
|
152
|
-
hidden: false,
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
},
|
|
186
|
+
error: new Error("server path must point to a directory"),
|
|
187
|
+
data: null,
|
|
156
188
|
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
isLoading: false,
|
|
161
|
-
error: new Error("server path must point to a directory"),
|
|
162
|
-
data: null,
|
|
163
|
-
};
|
|
164
|
-
});
|
|
189
|
+
},
|
|
190
|
+
);
|
|
165
191
|
}
|
|
166
192
|
|
|
167
193
|
beforeEach(() => {
|
|
168
194
|
serverPathReadMock.mockReset();
|
|
169
195
|
serverPathBrowseMock.mockReset();
|
|
196
|
+
renderDocxMock.mockReset();
|
|
197
|
+
renderDocxMock.mockResolvedValue(undefined);
|
|
198
|
+
readSpreadsheetMock.mockReset();
|
|
199
|
+
readSpreadsheetMock.mockReturnValue({
|
|
200
|
+
SheetNames: ["Summary", "Details"],
|
|
201
|
+
Sheets: { Summary: { name: "Summary" }, Details: { name: "Details" } },
|
|
202
|
+
});
|
|
203
|
+
sheetToJsonMock.mockReset();
|
|
204
|
+
sheetToJsonMock.mockImplementation((sheet: { name: string }) =>
|
|
205
|
+
sheet.name === "Summary"
|
|
206
|
+
? [
|
|
207
|
+
["City", "Temperature"],
|
|
208
|
+
["Hangzhou", "32 C"],
|
|
209
|
+
]
|
|
210
|
+
: [["Status"], ["Sunny"]],
|
|
211
|
+
);
|
|
212
|
+
openPresentationMock.mockReset();
|
|
213
|
+
destroyPresentationMock.mockReset();
|
|
214
|
+
openPresentationMock.mockResolvedValue({ destroy: destroyPresentationMock });
|
|
215
|
+
vi.stubGlobal(
|
|
216
|
+
"fetch",
|
|
217
|
+
vi
|
|
218
|
+
.fn()
|
|
219
|
+
.mockResolvedValue(
|
|
220
|
+
new Response(new Uint8Array([1, 2, 3]), { status: 200 }),
|
|
221
|
+
),
|
|
222
|
+
);
|
|
170
223
|
serverPathReadMock.mockReturnValue({
|
|
171
224
|
isLoading: false,
|
|
172
225
|
error: null,
|
|
@@ -179,6 +232,10 @@ beforeEach(() => {
|
|
|
179
232
|
});
|
|
180
233
|
});
|
|
181
234
|
|
|
235
|
+
afterEach(() => {
|
|
236
|
+
vi.unstubAllGlobals();
|
|
237
|
+
});
|
|
238
|
+
|
|
182
239
|
describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
183
240
|
it("renders attachment content URLs as workspace-native media previews", () => {
|
|
184
241
|
renderWorkspaceFilePreview({
|
|
@@ -205,25 +262,240 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
205
262
|
expect(image.getAttribute("alt")).toBe("photo.png");
|
|
206
263
|
});
|
|
207
264
|
|
|
208
|
-
it("
|
|
265
|
+
it("renders local SVG files through the automatic server-content viewer", () => {
|
|
266
|
+
mockTextRead({
|
|
267
|
+
resolvedPath: "/tmp/project/diagram.svg",
|
|
268
|
+
text: '<svg xmlns="http://www.w3.org/2000/svg" />',
|
|
269
|
+
});
|
|
270
|
+
renderWorkspaceFilePreview({
|
|
271
|
+
file: {
|
|
272
|
+
path: "/tmp/project/diagram.svg",
|
|
273
|
+
label: "diagram.svg",
|
|
274
|
+
previewViewer: "auto",
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const image = screen.getByTestId("workspace-content-image");
|
|
279
|
+
expect(image.getAttribute("src")).toContain(
|
|
280
|
+
"/api/server-paths/content?path=%2Ftmp%2Fproject%2Fdiagram.svg",
|
|
281
|
+
);
|
|
282
|
+
expect(screen.queryByTestId("file-code-surface")).toBeNull();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("renders SVG source when source viewer is explicitly requested", () => {
|
|
286
|
+
mockTextRead({
|
|
287
|
+
resolvedPath: "/tmp/project/diagram.svg",
|
|
288
|
+
text: '<svg xmlns="http://www.w3.org/2000/svg" />',
|
|
289
|
+
});
|
|
290
|
+
renderWorkspaceFilePreview({
|
|
291
|
+
file: {
|
|
292
|
+
path: "/tmp/project/diagram.svg",
|
|
293
|
+
label: "diagram.svg",
|
|
294
|
+
previewViewer: "source",
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
expect(screen.queryByTestId("workspace-content-image")).toBeNull();
|
|
299
|
+
expect(screen.getByTestId("file-code-surface")).toBeTruthy();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it.each([
|
|
303
|
+
["image", "sample.png", "workspace-content-image"],
|
|
304
|
+
["audio", "sample.mp3", "workspace-content-audio"],
|
|
305
|
+
["video", "sample.mp4", "workspace-content-video"],
|
|
306
|
+
])(
|
|
307
|
+
"does not report truncated metadata for local %s content",
|
|
308
|
+
(_kind, path, testId) => {
|
|
309
|
+
mockTextRead({
|
|
310
|
+
kind: "binary",
|
|
311
|
+
resolvedPath: `/tmp/${path}`,
|
|
312
|
+
text: undefined,
|
|
313
|
+
truncated: true,
|
|
314
|
+
});
|
|
315
|
+
renderWorkspaceFilePreview({
|
|
316
|
+
file: { path, label: path, previewViewer: "auto" },
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
expect(screen.getByTestId(testId)).toBeTruthy();
|
|
320
|
+
expect(
|
|
321
|
+
screen.queryByText(t("chatWorkspacePreviewTruncated")),
|
|
322
|
+
).toBeNull();
|
|
323
|
+
},
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe("ChatSessionWorkspaceFilePreview Office rendering", () => {
|
|
328
|
+
it("renders DOCX attachments inside the workspace preview", async () => {
|
|
329
|
+
renderDocxMock.mockImplementationOnce(
|
|
330
|
+
async (_data: ArrayBuffer, bodyContainer: HTMLDivElement) => {
|
|
331
|
+
const wrapper = document.createElement("div");
|
|
332
|
+
wrapper.className = "docx-wrapper";
|
|
333
|
+
const page = document.createElement("section");
|
|
334
|
+
page.className = "docx";
|
|
335
|
+
const table = document.createElement("table");
|
|
336
|
+
const colgroup = document.createElement("colgroup");
|
|
337
|
+
for (let index = 0; index < 6; index += 1) {
|
|
338
|
+
colgroup.append(document.createElement("col"));
|
|
339
|
+
}
|
|
340
|
+
table.append(colgroup);
|
|
341
|
+
page.append(table);
|
|
342
|
+
wrapper.append(page);
|
|
343
|
+
bodyContainer.append(wrapper);
|
|
344
|
+
},
|
|
345
|
+
);
|
|
209
346
|
renderWorkspaceFilePreview({
|
|
210
347
|
file: {
|
|
211
348
|
key: "attachment-docx",
|
|
212
349
|
path: "overview.zh-CN.docx",
|
|
213
350
|
label: "overview.zh-CN.docx",
|
|
214
351
|
viewMode: "preview",
|
|
215
|
-
contentUrl:
|
|
352
|
+
contentUrl:
|
|
353
|
+
"/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
|
|
216
354
|
mimeType:
|
|
217
355
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
218
356
|
},
|
|
219
357
|
});
|
|
220
358
|
|
|
221
|
-
expect(screen.getByTestId("workspace-content-
|
|
359
|
+
expect(screen.getByTestId("workspace-content-docx")).toBeTruthy();
|
|
360
|
+
await waitFor(() => expect(renderDocxMock).toHaveBeenCalled());
|
|
361
|
+
await waitFor(() =>
|
|
362
|
+
expect(screen.getByTestId("workspace-content-docx").className).toContain(
|
|
363
|
+
"workspace-docx-preview--reflow",
|
|
364
|
+
),
|
|
365
|
+
);
|
|
366
|
+
expect(
|
|
367
|
+
screen
|
|
368
|
+
.getByTestId("workspace-content-docx")
|
|
369
|
+
.querySelector(".workspace-docx-wide-table > table"),
|
|
370
|
+
).toBeTruthy();
|
|
371
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
372
|
+
"/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
|
|
373
|
+
expect.objectContaining({ signal: expect.any(AbortSignal) }),
|
|
374
|
+
);
|
|
375
|
+
expect(screen.queryByTestId("workspace-content-unsupported")).toBeNull();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("renders a relative local DOCX before binary metadata finishes loading", async () => {
|
|
379
|
+
mockTextRead({
|
|
380
|
+
kind: "binary",
|
|
381
|
+
resolvedPath: "/tmp/report.docx",
|
|
382
|
+
text: undefined,
|
|
383
|
+
});
|
|
384
|
+
renderWorkspaceFilePreview({
|
|
385
|
+
file: {
|
|
386
|
+
path: "report.docx",
|
|
387
|
+
label: "report.docx",
|
|
388
|
+
previewViewer: "auto",
|
|
389
|
+
},
|
|
390
|
+
sessionWorkingDir: "/tmp",
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
expect(screen.getByTestId("workspace-content-docx")).toBeTruthy();
|
|
394
|
+
await waitFor(() => expect(renderDocxMock).toHaveBeenCalled());
|
|
395
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
396
|
+
expect.stringContaining("path=report.docx&basePath=%2Ftmp"),
|
|
397
|
+
expect.objectContaining({ signal: expect.any(AbortSignal) }),
|
|
398
|
+
);
|
|
399
|
+
expect(screen.queryByText(t("chatWorkspacePreviewUnsupported"))).toBeNull();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("preserves DOCX page geometry when the document defines it", async () => {
|
|
403
|
+
renderDocxMock.mockImplementationOnce(
|
|
404
|
+
async (_data: ArrayBuffer, bodyContainer: HTMLDivElement) => {
|
|
405
|
+
const wrapper = document.createElement("div");
|
|
406
|
+
wrapper.className = "docx-wrapper";
|
|
407
|
+
const page = document.createElement("section");
|
|
408
|
+
page.className = "docx";
|
|
409
|
+
page.style.width = "612pt";
|
|
410
|
+
page.style.padding = "72pt";
|
|
411
|
+
wrapper.append(page);
|
|
412
|
+
bodyContainer.append(wrapper);
|
|
413
|
+
},
|
|
414
|
+
);
|
|
415
|
+
renderWorkspaceFilePreview({
|
|
416
|
+
file: {
|
|
417
|
+
key: "attachment-docx-with-page-layout",
|
|
418
|
+
path: "page-layout.docx",
|
|
419
|
+
label: "page-layout.docx",
|
|
420
|
+
viewMode: "preview",
|
|
421
|
+
contentUrl:
|
|
422
|
+
"/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Fpage-layout",
|
|
423
|
+
mimeType:
|
|
424
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
await waitFor(() => expect(renderDocxMock).toHaveBeenCalled());
|
|
222
429
|
expect(
|
|
223
|
-
screen.getByTestId("workspace-content-
|
|
224
|
-
).toContain("
|
|
430
|
+
screen.getByTestId("workspace-content-docx").className,
|
|
431
|
+
).not.toContain("workspace-docx-preview--reflow");
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("renders Excel workbooks with sheet navigation", async () => {
|
|
435
|
+
renderWorkspaceFilePreview({
|
|
436
|
+
file: {
|
|
437
|
+
key: "attachment-xlsx",
|
|
438
|
+
path: "overview.xlsx",
|
|
439
|
+
label: "overview.xlsx",
|
|
440
|
+
viewMode: "preview",
|
|
441
|
+
contentUrl:
|
|
442
|
+
"/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
|
|
443
|
+
mimeType:
|
|
444
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
expect(screen.getByTestId("workspace-content-spreadsheet")).toBeTruthy();
|
|
449
|
+
await waitFor(() => expect(readSpreadsheetMock).toHaveBeenCalled());
|
|
450
|
+
expect(screen.getByText("Summary")).toBeTruthy();
|
|
451
|
+
expect(screen.getByText("Details")).toBeTruthy();
|
|
452
|
+
expect(screen.getByText("Hangzhou")).toBeTruthy();
|
|
453
|
+
fireEvent.click(screen.getByRole("button", { name: "Details" }));
|
|
454
|
+
expect(screen.getByText("Sunny")).toBeTruthy();
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it("renders PowerPoint presentations in the workspace", async () => {
|
|
458
|
+
renderWorkspaceFilePreview({
|
|
459
|
+
file: {
|
|
460
|
+
key: "attachment-pptx",
|
|
461
|
+
path: "overview.pptx",
|
|
462
|
+
label: "overview.pptx",
|
|
463
|
+
viewMode: "preview",
|
|
464
|
+
contentUrl: "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Fslides",
|
|
465
|
+
mimeType:
|
|
466
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
expect(screen.getByTestId("workspace-content-presentation")).toBeTruthy();
|
|
471
|
+
await waitFor(() => expect(openPresentationMock).toHaveBeenCalled());
|
|
472
|
+
expect(openPresentationMock).toHaveBeenCalledWith(
|
|
473
|
+
expect.any(ArrayBuffer),
|
|
474
|
+
expect.any(HTMLElement),
|
|
475
|
+
expect.objectContaining({
|
|
476
|
+
lazySlides: true,
|
|
477
|
+
lazyMedia: true,
|
|
478
|
+
pdfjs: false,
|
|
479
|
+
zipLimits: { maxEntries: 4_000 },
|
|
480
|
+
}),
|
|
481
|
+
);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it("offers download and system open actions for legacy PowerPoint attachments", () => {
|
|
485
|
+
renderWorkspaceFilePreview({
|
|
486
|
+
file: {
|
|
487
|
+
key: "attachment-ppt",
|
|
488
|
+
path: "overview.ppt",
|
|
489
|
+
label: "overview.ppt",
|
|
490
|
+
viewMode: "preview",
|
|
491
|
+
contentUrl:
|
|
492
|
+
"/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
|
|
493
|
+
mimeType: "application/vnd.ms-powerpoint",
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
expect(screen.getByTestId("workspace-content-unsupported")).toBeTruthy();
|
|
225
498
|
expect(screen.getByText(t("chatWorkspacePreviewUnsupported"))).toBeTruthy();
|
|
226
|
-
expect(screen.getByText(t("chatWorkspacePreviewUnsupportedHint"))).toBeTruthy();
|
|
227
499
|
|
|
228
500
|
const download = screen.getByRole("link", {
|
|
229
501
|
name: t("chatWorkspacePreviewDownload"),
|
|
@@ -231,7 +503,7 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
231
503
|
expect(download.getAttribute("href")).toBe(
|
|
232
504
|
"/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
|
|
233
505
|
);
|
|
234
|
-
expect(download.getAttribute("download")).toBe("overview.
|
|
506
|
+
expect(download.getAttribute("download")).toBe("overview.ppt");
|
|
235
507
|
|
|
236
508
|
const openExternally = screen.getByRole("link", {
|
|
237
509
|
name: t("chatWorkspacePreviewOpenExternally"),
|
|
@@ -241,14 +513,16 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
241
513
|
);
|
|
242
514
|
expect(openExternally.getAttribute("target")).toBe("_blank");
|
|
243
515
|
});
|
|
516
|
+
});
|
|
244
517
|
|
|
518
|
+
describe("ChatSessionWorkspaceFilePreview text rendering", () => {
|
|
245
519
|
it("renders preview files inside a full-height workspace code surface", () => {
|
|
246
520
|
mockTextRead();
|
|
247
521
|
renderWorkspaceFilePreview();
|
|
248
522
|
|
|
249
|
-
expect(
|
|
250
|
-
"
|
|
251
|
-
);
|
|
523
|
+
expect(
|
|
524
|
+
screen.getByTestId("file-code-surface").getAttribute("data-layout"),
|
|
525
|
+
).toBe("workspace");
|
|
252
526
|
});
|
|
253
527
|
|
|
254
528
|
it("passes server language hints to the workspace code surface", () => {
|
|
@@ -262,10 +536,34 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
262
536
|
});
|
|
263
537
|
|
|
264
538
|
expect(
|
|
265
|
-
screen
|
|
539
|
+
screen
|
|
540
|
+
.getByTestId("file-code-surface")
|
|
541
|
+
.getAttribute("data-language-hint"),
|
|
266
542
|
).toBe("js");
|
|
267
543
|
});
|
|
268
544
|
|
|
545
|
+
it("renders Markdown by default and keeps source view explicit", () => {
|
|
546
|
+
mockTextRead({
|
|
547
|
+
kind: "markdown",
|
|
548
|
+
resolvedPath: "/tmp/README.md",
|
|
549
|
+
text: "# Hello",
|
|
550
|
+
});
|
|
551
|
+
const { unmount } = renderWorkspaceFilePreview({
|
|
552
|
+
file: { path: "/tmp/README.md" },
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
expect(screen.getByTestId("markdown-preview")).toBeTruthy();
|
|
556
|
+
expect(screen.queryByTestId("file-code-surface")).toBeNull();
|
|
557
|
+
|
|
558
|
+
unmount();
|
|
559
|
+
renderWorkspaceFilePreview({
|
|
560
|
+
file: { path: "/tmp/README.md", previewViewer: "source" },
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
expect(screen.queryByTestId("markdown-preview")).toBeNull();
|
|
564
|
+
expect(screen.getByTestId("file-code-surface")).toBeTruthy();
|
|
565
|
+
});
|
|
566
|
+
|
|
269
567
|
it("keeps HTML files in the source preview when preview viewer is automatic", () => {
|
|
270
568
|
mockTextRead({
|
|
271
569
|
resolvedPath: "/tmp/example.html",
|
|
@@ -301,7 +599,7 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
301
599
|
expect(frame.getAttribute("sandbox")).toBeNull();
|
|
302
600
|
expect(frame.getAttribute("srcdoc")).toBeNull();
|
|
303
601
|
expect(frame.getAttribute("src")).toContain(
|
|
304
|
-
"/api/server-paths/content
|
|
602
|
+
"/api/server-paths/content?path=%2Ftmp%2Fexample.html",
|
|
305
603
|
);
|
|
306
604
|
expect(screen.queryByTestId("file-code-surface")).toBeNull();
|
|
307
605
|
});
|
|
@@ -321,9 +619,9 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
321
619
|
refreshVersion: 3,
|
|
322
620
|
});
|
|
323
621
|
|
|
324
|
-
expect(
|
|
325
|
-
"
|
|
326
|
-
);
|
|
622
|
+
expect(
|
|
623
|
+
screen.getByTestId("workspace-html-preview").getAttribute("src"),
|
|
624
|
+
).toContain("refresh=3");
|
|
327
625
|
});
|
|
328
626
|
|
|
329
627
|
it("keeps HTML files in the source preview when source is requested", () => {
|
|
@@ -374,9 +672,9 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
374
672
|
},
|
|
375
673
|
});
|
|
376
674
|
|
|
377
|
-
expect(
|
|
378
|
-
"
|
|
379
|
-
);
|
|
675
|
+
expect(
|
|
676
|
+
screen.getByTestId("file-code-surface").getAttribute("data-layout"),
|
|
677
|
+
).toBe("workspace");
|
|
380
678
|
});
|
|
381
679
|
|
|
382
680
|
it("does not repeat the preview badge inside the workspace header", () => {
|
|
@@ -389,7 +687,7 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
389
687
|
expect(screen.getByText("example.ts")).toBeTruthy();
|
|
390
688
|
});
|
|
391
689
|
|
|
392
|
-
it("renders directory entries
|
|
690
|
+
it("renders directory entries, expands child directories, and opens files", () => {
|
|
393
691
|
serverPathReadMock.mockReturnValue({
|
|
394
692
|
isLoading: false,
|
|
395
693
|
error: new Error("server path must point to a file"),
|
|
@@ -443,18 +741,17 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
|
|
|
443
741
|
});
|
|
444
742
|
expect(screen.getByTestId("workspace-directory-browser")).toBeTruthy();
|
|
445
743
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
);
|
|
449
|
-
expect(onFileOpen).toHaveBeenCalledWith({
|
|
450
|
-
path: "/tmp/workspace/src/components",
|
|
451
|
-
label: "components",
|
|
452
|
-
viewMode: "preview",
|
|
744
|
+
const componentsTreeItem = screen.getByRole("treeitem", {
|
|
745
|
+
name: "Open directory: components",
|
|
453
746
|
});
|
|
747
|
+
const indexButton = screen.getByRole("button", { name: "index.ts" });
|
|
748
|
+
fireEvent.click(componentsTreeItem.querySelector("button")!);
|
|
749
|
+
expect(
|
|
750
|
+
componentsTreeItem.getAttribute("aria-expanded"),
|
|
751
|
+
).toBe("true");
|
|
752
|
+
expect(onFileOpen).not.toHaveBeenCalled();
|
|
454
753
|
|
|
455
|
-
fireEvent.click(
|
|
456
|
-
screen.getByRole("button", { name: "Open file: index.ts" }),
|
|
457
|
-
);
|
|
754
|
+
fireEvent.click(indexButton);
|
|
458
755
|
expect(onFileOpen).toHaveBeenCalledWith({
|
|
459
756
|
path: "/tmp/workspace/src/index.ts",
|
|
460
757
|
label: "index.ts",
|
|
@@ -477,9 +774,9 @@ describe("ChatSessionWorkspaceFilePreview breadcrumbs", () => {
|
|
|
477
774
|
expect(
|
|
478
775
|
screen.getByTestId("workspace-file-breadcrumb-scroll").className,
|
|
479
776
|
).toContain("py-1.5");
|
|
480
|
-
expect(
|
|
481
|
-
"workspace-
|
|
482
|
-
);
|
|
777
|
+
expect(
|
|
778
|
+
screen.getByTestId("workspace-file-breadcrumbs").className,
|
|
779
|
+
).toContain("workspace-horizontal-scrollbar");
|
|
483
780
|
});
|
|
484
781
|
|
|
485
782
|
it("browses from a breadcrumb segment and opens selected files", () => {
|
|
@@ -521,18 +818,18 @@ describe("ChatSessionWorkspaceFilePreview breadcrumbs", () => {
|
|
|
521
818
|
|
|
522
819
|
fireEvent.click(screen.getByRole("button", { name: "src" }));
|
|
523
820
|
|
|
524
|
-
expect(
|
|
525
|
-
"
|
|
526
|
-
);
|
|
527
|
-
expect(
|
|
528
|
-
"
|
|
529
|
-
);
|
|
530
|
-
expect(
|
|
531
|
-
"
|
|
532
|
-
);
|
|
533
|
-
expect(
|
|
534
|
-
"
|
|
535
|
-
);
|
|
821
|
+
expect(
|
|
822
|
+
screen.getByTestId("workspace-breadcrumb-popover").className,
|
|
823
|
+
).toContain("w-[22rem]");
|
|
824
|
+
expect(
|
|
825
|
+
screen.getByTestId("workspace-breadcrumb-browser").className,
|
|
826
|
+
).toContain("max-h-72");
|
|
827
|
+
expect(
|
|
828
|
+
screen.getAllByRole("button", { name: "workspace" }).at(-1)?.className,
|
|
829
|
+
).toContain("h-5");
|
|
830
|
+
expect(
|
|
831
|
+
screen.getByRole("button", { name: "components" }).className,
|
|
832
|
+
).toContain("h-6");
|
|
536
833
|
});
|
|
537
834
|
|
|
538
835
|
it("keeps line and truncation metadata without the duplicated type badge", () => {
|