@nextclaw/ui 0.14.4-beta.0 → 0.15.0
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 +119 -0
- package/dist/assets/api-DLtsiBKm.js +13 -0
- package/dist/assets/appearance-settings-page-CtkeYXux.js +1 -0
- package/dist/assets/book-open-BQwWBDKb.js +1 -0
- package/dist/assets/channels-list-page-Cv8o9Hxg.js +8 -0
- package/dist/assets/chat-page-BtA7H1Ea.js +1 -0
- package/dist/assets/{config-split-page-CRhlZ_pH.js → config-split-page-EidRBpP2.js} +1 -1
- package/dist/assets/confirm-dialog-iIJFFp2T.js +5 -0
- package/dist/assets/desktop-update-config-C6n_TB1r.js +1 -0
- package/dist/assets/{dist-CikkhOlq.js → dist-CBxWOazz.js} +1 -1
- package/dist/assets/{dist-PVA_xL1D.js → dist-D2eZC3OR.js} +1 -1
- package/dist/assets/doc-browser-B76XOXmW.js +1 -0
- package/dist/assets/doc-browser-DBnj9UMB.js +1 -0
- package/dist/assets/{doc-browser-context-MTw4EvKd.js → doc-browser-context-Sv-044Cl.js} +1 -1
- package/dist/assets/{ellipsis-y6vDtq4M.js → ellipsis-2uzhypw5.js} +1 -1
- package/dist/assets/es2015-DhQo_oS6.js +41 -0
- package/dist/assets/{external-link-DVJIQf_G.js → external-link-CM5dnUI6.js} +1 -1
- package/dist/assets/host-capabilities-9bB_998Z.js +1 -0
- package/dist/assets/i18n-provider-di1waTGX.js +1 -0
- package/dist/assets/index-CBRP-7-a.css +1 -0
- package/dist/assets/index-Z1fYpbNd.js +104 -0
- package/dist/assets/loader-circle-BjmN5DeJ.js +1 -0
- package/dist/assets/mcp-marketplace-page-C_MVQUbT.js +9 -0
- package/dist/assets/mcp-marketplace-page-CoZIiD6G.js +1 -0
- package/dist/assets/model-config-page-Cdr9P6TQ.js +1 -0
- package/dist/assets/plus-3ztRZD_l.js +1 -0
- package/dist/assets/provider-scoped-model-input-g1WkGiJ8.js +1 -0
- package/dist/assets/providers-config-page-CWlO7J4A.js +1 -0
- package/dist/assets/{react-VNqT5Zp7.js → react-DjpzoZXB.js} +1 -1
- package/dist/assets/refresh-cw-DaTv3zxE.js +1 -0
- package/dist/assets/remote-CKWZfVVB.js +1 -0
- package/dist/assets/rotate-cw-CHcaRUDz.js +1 -0
- package/dist/assets/runtime-config-page-CfTqh1nv.js +1 -0
- package/dist/assets/{save-BOJ477H-.js → save-BZof1uie.js} +1 -1
- package/dist/assets/{search-BEti5beA.js → search-MxAarS_g.js} +1 -1
- package/dist/assets/search-config-page-Dm8daYny.js +1 -0
- package/dist/assets/{secrets-config-page--IzrIOid.js → secrets-config-page-D8AbcR05.js} +2 -2
- package/dist/assets/security-config-Ds0LsJmo.js +1 -0
- package/dist/assets/select-D7GcNP0z.js +1 -0
- package/dist/assets/setting-row-CuozlnJd.js +1 -0
- package/dist/assets/side-dock-BcLau5S_.js +3 -0
- package/dist/assets/skeleton-shfmXuYp.js +1 -0
- package/dist/assets/switch-C1BWMU5c.js +1 -0
- package/dist/assets/tag-chip-DCUrQUCP.js +1 -0
- package/dist/assets/tooltip-DfAHHA76.js +1 -0
- package/dist/assets/use-config-DAdbT5QJ.js +1 -0
- package/dist/assets/use-confirm-dialog-eWcJ6V-s.js +1 -0
- package/dist/assets/x-DqL1mYLC.js +1 -0
- package/dist/index.html +53 -17
- package/index.html +25 -2
- package/package.json +9 -9
- package/src/app/components/layout/__tests__/app-layout.test.tsx +27 -0
- package/src/app/components/layout/__tests__/settings-entry-page.test.tsx +9 -1
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +6 -3
- package/src/app/components/layout/app-layout.tsx +10 -3
- package/src/app/components/layout/sidebar-items.tsx +8 -8
- package/src/app/components/layout/sidebar-rail.styles.ts +4 -2
- package/src/app/components/layout/sidebar.tsx +21 -28
- package/src/app/configs/app-navigation.config.ts +11 -5
- package/src/app/index.tsx +8 -0
- package/src/app/managers/__tests__/viewport-layout.manager.test.ts +59 -0
- package/src/app/managers/viewport-layout.manager.ts +39 -0
- package/src/app/presenters/app.presenter.ts +25 -2
- package/src/app/styles/design-system.css +91 -4
- package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +86 -0
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +47 -17
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +7 -0
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +9 -5
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +5 -5
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +51 -2
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +170 -1
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +23 -1
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +24 -7
- package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +96 -0
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +181 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +351 -89
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +29 -4
- package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +28 -3
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -8
- package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +50 -14
- package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +77 -6
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +21 -12
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +166 -0
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +19 -11
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +38 -64
- package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +59 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +56 -1
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +0 -2
- package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +80 -0
- package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +12 -3
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +60 -0
- package/src/features/chat/features/message/utils/chat-message.utils.ts +3 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +39 -0
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +2 -2
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +3 -1
- package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +1 -1
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +270 -115
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +121 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +75 -20
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +70 -5
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +83 -0
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +79 -1
- package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +2 -0
- package/src/features/chat/managers/chat-thread.manager.ts +9 -3
- package/src/features/chat/managers/chat-ui.manager.ts +5 -1
- package/src/features/chat/presenters/chat.presenter.ts +2 -1
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +4 -0
- package/src/features/chat/stores/chat-thread.store.ts +6 -64
- package/src/features/chat/types/chat-message.types.ts +4 -0
- package/src/features/marketplace/components/__tests__/marketplace-page-detail.test.tsx +33 -26
- package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +1 -0
- package/src/features/marketplace/components/curated-shelves/__tests__/marketplace-curated-scene-route.test.tsx +1 -0
- package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.tsx +115 -0
- package/src/features/marketplace/components/marketplace-detail-doc.tsx +441 -0
- package/src/features/marketplace/components/mcp/__tests__/mcp-marketplace-page.test.tsx +3 -3
- package/src/features/marketplace/components/mcp/mcp-marketplace-doc.ts +0 -60
- package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +64 -29
- package/src/features/marketplace/hooks/use-marketplace-item-detail.ts +42 -31
- package/src/features/marketplace/index.ts +3 -0
- package/src/features/marketplace/stores/marketplace-detail-doc.store.ts +38 -0
- package/src/features/panel-apps/components/__tests__/panel-apps-list.test.tsx +32 -2
- package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +11 -0
- package/src/features/pwa/managers/pwa-shell-theme.manager.ts +8 -3
- package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +25 -0
- package/src/features/settings/pages/appearance-settings-page.tsx +40 -0
- package/src/features/side-dock/components/__tests__/side-dock.test.tsx +44 -2
- package/src/features/side-dock/components/side-dock.tsx +100 -13
- package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +10 -0
- package/src/features/side-dock/managers/__tests__/side-dock.manager.test.ts +32 -2
- package/src/features/side-dock/managers/side-dock.manager.ts +17 -7
- package/src/features/side-dock/stores/side-dock.store.ts +9 -0
- package/src/features/side-dock/types/side-dock.types.ts +10 -3
- package/src/features/side-dock/utils/side-dock-item.utils.ts +15 -2
- package/src/index.css +501 -27
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +1 -1
- package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +30 -1
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +206 -38
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +34 -11
- package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -1
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +23 -5
- package/src/shared/components/doc-browser/doc-browser.tsx +128 -75
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +9 -1
- package/src/shared/components/doc-browser/utils/doc-browser-floating-panel.utils.ts +83 -0
- package/src/shared/components/ui/__tests__/dialog.test.tsx +27 -0
- package/src/shared/components/ui/actions/icon-action-button.tsx +14 -2
- package/src/shared/components/ui/dialog.tsx +2 -2
- package/src/shared/components/ui/popover.tsx +1 -1
- package/src/shared/components/ui/select.tsx +1 -1
- package/src/shared/components/ui/switch.tsx +4 -3
- package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +54 -0
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +4 -5
- package/src/shared/components/ui/tooltip.tsx +1 -1
- package/src/shared/lib/api/utils/server-path.utils.ts +27 -1
- package/src/shared/lib/i18n/locales/en-US/chat.json +15 -6
- package/src/shared/lib/i18n/locales/en-US/core.json +2 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -0
- package/src/shared/lib/i18n/locales/en-US/marketplace.json +5 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +15 -6
- package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -0
- package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +5 -0
- package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +14 -0
- package/src/shared/lib/session-project/index.ts +1 -1
- package/src/shared/lib/session-project/{workspace-file-breadcrumb.ts → workspace-file-breadcrumb.utils.ts} +62 -8
- package/src/shared/lib/theme/index.ts +51 -13
- package/dist/assets/api-BP_8IaxB.js +0 -13
- package/dist/assets/channels-list-page-DEh5Jjqx.js +0 -8
- package/dist/assets/chat-page-BRB0N5WW.js +0 -105
- package/dist/assets/confirm-dialog-DsivFJ4r.js +0 -5
- package/dist/assets/desktop-update-config-B0w9p2ep.js +0 -1
- package/dist/assets/doc-browser-BoNJ7-tE.js +0 -1
- package/dist/assets/doc-browser-CdJ_d4ik.js +0 -1
- package/dist/assets/index-BiE-K8-q.css +0 -1
- package/dist/assets/index-D9BAE_L_.js +0 -105
- package/dist/assets/mcp-marketplace-page-B1SZNY5m.js +0 -40
- package/dist/assets/mcp-marketplace-page-Cq_Tw42T.js +0 -1
- package/dist/assets/model-config-page-DotaTUHb.js +0 -1
- package/dist/assets/plus-BbZqquJe.js +0 -1
- package/dist/assets/provider-scoped-model-input-DsFRuWJH.js +0 -1
- package/dist/assets/providers-config-page-DW4tQSLN.js +0 -1
- package/dist/assets/remote-B8TiUfwW.js +0 -1
- package/dist/assets/rotate-cw-DjHDTkCv.js +0 -1
- package/dist/assets/runtime-config-page-S6ErRHR8.js +0 -1
- package/dist/assets/search-config-page-DGj5am03.js +0 -1
- package/dist/assets/select-hoj8dK4K.js +0 -41
- package/dist/assets/tag-chip-T8-h-die.js +0 -1
- package/dist/assets/use-config-CjRV2hwL.js +0 -1
- package/dist/assets/x-D9PXs_xP.js +0 -1
- package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.ts +0 -40
- package/src/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer.ts +0 -201
- package/src/features/marketplace/components/marketplace-detail-doc.ts +0 -142
- /package/dist/assets/{config-hints-BkYBc7z0.js → config-hints-fGnUjDe9.js} +0 -0
- /package/dist/assets/{middleware-CkFT3R4v.js → middleware-BILWbJE4.js} +0 -0
package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { Fragment } from "react";
|
|
1
|
+
import { Fragment, useState } from "react";
|
|
2
|
+
import type { ChatFileOpenActionViewModel } from "@nextclaw/agent-chat-ui";
|
|
2
3
|
import { ChevronRight, FileCode2, FolderTree } from "lucide-react";
|
|
3
|
-
import
|
|
4
|
+
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/components/ui/popover";
|
|
5
|
+
import { WorkspaceBreadcrumbBrowser } from "./chat-session-workspace-file-breadcrumb-browser";
|
|
6
|
+
import type {
|
|
7
|
+
WorkspaceFileBreadcrumbSegmentViewModel,
|
|
8
|
+
WorkspaceFileBreadcrumbViewModel,
|
|
9
|
+
} from "@/shared/lib/session-project";
|
|
4
10
|
import { t } from "@/shared/lib/i18n";
|
|
5
11
|
import { cn } from "@/shared/lib/utils";
|
|
6
12
|
|
|
@@ -19,7 +25,69 @@ function WorkspaceBreadcrumbMetaChip({ tone = "neutral", value }: { tone?: "neut
|
|
|
19
25
|
);
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
function WorkspaceBreadcrumbSegmentButton({
|
|
29
|
+
onFileOpen,
|
|
30
|
+
segment,
|
|
31
|
+
}: {
|
|
32
|
+
onFileOpen: (action: ChatFileOpenActionViewModel) => void;
|
|
33
|
+
segment: WorkspaceFileBreadcrumbSegmentViewModel;
|
|
34
|
+
}) {
|
|
35
|
+
const [open, setOpen] = useState(false);
|
|
36
|
+
const [browsePath, setBrowsePath] = useState<string | null>(segment.browsePath);
|
|
37
|
+
const handleOpenChange = (nextOpen: boolean) => {
|
|
38
|
+
if (nextOpen) {
|
|
39
|
+
setBrowsePath(segment.browsePath);
|
|
40
|
+
}
|
|
41
|
+
setOpen(nextOpen);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Popover open={open} onOpenChange={handleOpenChange}>
|
|
46
|
+
<PopoverTrigger asChild>
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
aria-current={segment.isCurrent ? "page" : undefined}
|
|
50
|
+
className={cn(
|
|
51
|
+
"inline-flex h-5 items-center gap-1 rounded-sm px-1 text-[11px] leading-none transition-colors hover:bg-gray-200/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40",
|
|
52
|
+
segment.kind === "workspace"
|
|
53
|
+
? "bg-primary/8 text-primary hover:bg-primary/12"
|
|
54
|
+
: segment.isCurrent
|
|
55
|
+
? "bg-gray-200/70 text-gray-900"
|
|
56
|
+
: "text-gray-500",
|
|
57
|
+
)}
|
|
58
|
+
disabled={!segment.browsePath}
|
|
59
|
+
>
|
|
60
|
+
{segment.kind === "workspace" ? (
|
|
61
|
+
<FolderTree className="h-3 w-3 shrink-0" />
|
|
62
|
+
) : segment.isCurrent ? (
|
|
63
|
+
<FileCode2 className="h-3 w-3 shrink-0" />
|
|
64
|
+
) : null}
|
|
65
|
+
<span>{segment.label}</span>
|
|
66
|
+
</button>
|
|
67
|
+
</PopoverTrigger>
|
|
68
|
+
<PopoverContent
|
|
69
|
+
data-testid="workspace-breadcrumb-popover"
|
|
70
|
+
className="w-[22rem] rounded-md p-0"
|
|
71
|
+
align="start"
|
|
72
|
+
>
|
|
73
|
+
<WorkspaceBreadcrumbBrowser
|
|
74
|
+
browsePath={browsePath}
|
|
75
|
+
onBrowsePathChange={setBrowsePath}
|
|
76
|
+
onClose={() => setOpen(false)}
|
|
77
|
+
onFileOpen={onFileOpen}
|
|
78
|
+
/>
|
|
79
|
+
</PopoverContent>
|
|
80
|
+
</Popover>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function ChatSessionWorkspaceFileBreadcrumbs({
|
|
85
|
+
breadcrumb,
|
|
86
|
+
onFileOpen,
|
|
87
|
+
}: {
|
|
88
|
+
breadcrumb: WorkspaceFileBreadcrumbViewModel;
|
|
89
|
+
onFileOpen: (action: ChatFileOpenActionViewModel) => void;
|
|
90
|
+
}) {
|
|
23
91
|
return (
|
|
24
92
|
<div
|
|
25
93
|
data-testid="workspace-file-breadcrumbs"
|
|
@@ -33,23 +101,10 @@ export function ChatSessionWorkspaceFileBreadcrumbs({ breadcrumb }: { breadcrumb
|
|
|
33
101
|
<div className="flex min-w-0 flex-1 items-center gap-1 pr-1">
|
|
34
102
|
{breadcrumb.segments.map((segment, index) => (
|
|
35
103
|
<Fragment key={segment.key}>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
? "bg-primary/8 text-primary"
|
|
41
|
-
: segment.isCurrent
|
|
42
|
-
? "bg-gray-200/70 text-gray-900"
|
|
43
|
-
: "text-gray-500",
|
|
44
|
-
)}
|
|
45
|
-
>
|
|
46
|
-
{segment.kind === "workspace" ? (
|
|
47
|
-
<FolderTree className="h-3 w-3 shrink-0" />
|
|
48
|
-
) : segment.isCurrent ? (
|
|
49
|
-
<FileCode2 className="h-3 w-3 shrink-0" />
|
|
50
|
-
) : null}
|
|
51
|
-
<span>{segment.label}</span>
|
|
52
|
-
</span>
|
|
104
|
+
<WorkspaceBreadcrumbSegmentButton
|
|
105
|
+
segment={segment}
|
|
106
|
+
onFileOpen={onFileOpen}
|
|
107
|
+
/>
|
|
53
108
|
{index < breadcrumb.segments.length - 1 ? (
|
|
54
109
|
<ChevronRight className="h-3 w-3 shrink-0 text-gray-300" />
|
|
55
110
|
) : null}
|
package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
ChatFileOpenActionViewModel,
|
|
4
|
+
ChatFileOperationBlockViewModel,
|
|
5
|
+
ChatFilePreviewViewer,
|
|
6
|
+
} from "@nextclaw/agent-chat-ui";
|
|
3
7
|
import { ChatMessageMarkdown, FileOperationCodeSurface } from "@nextclaw/agent-chat-ui";
|
|
4
8
|
import type { ChatWorkspaceFileTab } from "@/features/chat/stores/chat-thread.store";
|
|
5
9
|
import { ChatSessionWorkspaceFileBreadcrumbs } from "./chat-session-workspace-file-breadcrumbs";
|
|
6
10
|
import { useServerPathRead } from "@/shared/hooks/use-server-path-read";
|
|
11
|
+
import { buildServerPathContentUrl } from "@/shared/lib/api";
|
|
7
12
|
import { buildLineDiff, buildPreviewLines } from "@/features/chat/features/message/utils/file-operation/line-builder.utils";
|
|
8
13
|
import { t } from "@/shared/lib/i18n";
|
|
9
14
|
import { buildWorkspaceFileBreadcrumb } from "@/shared/lib/session-project";
|
|
@@ -19,12 +24,26 @@ function inferPreviewKind(params: {
|
|
|
19
24
|
return /\.mdx?$/i.test(params.path) ? "markdown" : "text";
|
|
20
25
|
}
|
|
21
26
|
|
|
27
|
+
function isHtmlFilePath(path: string): boolean {
|
|
28
|
+
return /\.html?$/i.test(path);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolveFilePreviewViewer(params: {
|
|
32
|
+
path: string;
|
|
33
|
+
viewer?: ChatFilePreviewViewer | null;
|
|
34
|
+
}): "source" | "rendered" {
|
|
35
|
+
return params.viewer === "rendered" && isHtmlFilePath(params.path)
|
|
36
|
+
? "rendered"
|
|
37
|
+
: "source";
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
function buildPreviewBlock(params: {
|
|
23
41
|
path: string;
|
|
24
42
|
text: string;
|
|
43
|
+
languageHint?: string | null;
|
|
25
44
|
line?: number | null;
|
|
26
45
|
}): ChatFileOperationBlockViewModel {
|
|
27
|
-
const { line, path, text } = params;
|
|
46
|
+
const { languageHint, line, path, text } = params;
|
|
28
47
|
const startLine = line ?? 1;
|
|
29
48
|
return {
|
|
30
49
|
key: `preview:${path}`,
|
|
@@ -37,6 +56,7 @@ function buildPreviewBlock(params: {
|
|
|
37
56
|
newStartLine: startLine,
|
|
38
57
|
}),
|
|
39
58
|
rawText: text,
|
|
59
|
+
languageHint: languageHint ?? null,
|
|
40
60
|
oldStartLine: startLine,
|
|
41
61
|
newStartLine: startLine,
|
|
42
62
|
};
|
|
@@ -130,18 +150,38 @@ function WorkspaceCodeSurface({
|
|
|
130
150
|
return <div className="h-full overflow-auto custom-scrollbar bg-white"><FileOperationCodeSurface block={block} layout="workspace" /></div>;
|
|
131
151
|
}
|
|
132
152
|
|
|
153
|
+
function WorkspaceHtmlRenderedPreview({
|
|
154
|
+
src,
|
|
155
|
+
}: {
|
|
156
|
+
src: string;
|
|
157
|
+
}) {
|
|
158
|
+
return (
|
|
159
|
+
<iframe
|
|
160
|
+
allowFullScreen
|
|
161
|
+
className="h-full w-full border-0 bg-white"
|
|
162
|
+
data-testid="workspace-html-preview"
|
|
163
|
+
src={src}
|
|
164
|
+
title={t("chatWorkspaceHtmlPreviewTitle")}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
133
169
|
function WorkspacePreviewBody({
|
|
134
170
|
onFileOpen,
|
|
135
171
|
previewBlock,
|
|
136
172
|
previewKind,
|
|
137
173
|
previewQuery,
|
|
138
174
|
previewText,
|
|
175
|
+
previewUrl,
|
|
176
|
+
previewViewer,
|
|
139
177
|
}: {
|
|
140
178
|
onFileOpen: (action: ChatFileOpenActionViewModel) => void;
|
|
141
179
|
previewBlock: ChatFileOperationBlockViewModel | null;
|
|
142
180
|
previewKind: "text" | "markdown" | "binary";
|
|
143
181
|
previewQuery: ReturnType<typeof useServerPathRead>;
|
|
144
182
|
previewText: string | null;
|
|
183
|
+
previewUrl: string | null;
|
|
184
|
+
previewViewer: "source" | "rendered";
|
|
145
185
|
}) {
|
|
146
186
|
if (previewQuery.isLoading && !previewBlock) {
|
|
147
187
|
return <WorkspaceFilePreviewStatus text={t("chatWorkspaceLoadingFile")} />;
|
|
@@ -166,6 +206,10 @@ function WorkspacePreviewBody({
|
|
|
166
206
|
);
|
|
167
207
|
}
|
|
168
208
|
|
|
209
|
+
if (previewViewer === "rendered" && previewUrl) {
|
|
210
|
+
return <WorkspaceHtmlRenderedPreview src={previewUrl} />;
|
|
211
|
+
}
|
|
212
|
+
|
|
169
213
|
if (previewKind === "markdown" && previewText) {
|
|
170
214
|
return (
|
|
171
215
|
<div className="h-full overflow-auto custom-scrollbar px-5 py-4">
|
|
@@ -218,6 +262,15 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
218
262
|
path: previewQuery.data?.resolvedPath ?? file.path,
|
|
219
263
|
serverKind: previewQuery.data?.kind,
|
|
220
264
|
});
|
|
265
|
+
const resolvedPath = previewQuery.data?.resolvedPath ?? file.path;
|
|
266
|
+
const previewViewer = resolveFilePreviewViewer({
|
|
267
|
+
path: resolvedPath,
|
|
268
|
+
viewer: file.previewViewer,
|
|
269
|
+
});
|
|
270
|
+
const previewUrl =
|
|
271
|
+
previewViewer === "rendered" && previewQuery.data?.resolvedPath
|
|
272
|
+
? buildServerPathContentUrl(previewQuery.data.resolvedPath)
|
|
273
|
+
: null;
|
|
221
274
|
const previewBlock = useMemo(() => {
|
|
222
275
|
if (!isPreviewMode || !previewText) {
|
|
223
276
|
return null;
|
|
@@ -225,10 +278,17 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
225
278
|
return buildPreviewBlock({
|
|
226
279
|
path: previewQuery.data?.resolvedPath ?? file.path,
|
|
227
280
|
text: previewText,
|
|
281
|
+
languageHint: previewQuery.data?.languageHint ?? null,
|
|
228
282
|
line: file.line,
|
|
229
283
|
});
|
|
230
|
-
}, [
|
|
231
|
-
|
|
284
|
+
}, [
|
|
285
|
+
file.line,
|
|
286
|
+
file.path,
|
|
287
|
+
isPreviewMode,
|
|
288
|
+
previewQuery.data?.languageHint,
|
|
289
|
+
previewQuery.data?.resolvedPath,
|
|
290
|
+
previewText,
|
|
291
|
+
]);
|
|
232
292
|
const isTruncated = Boolean(previewQuery.data?.truncated);
|
|
233
293
|
const breadcrumbBasePath = sessionProjectRoot ?? sessionWorkingDir;
|
|
234
294
|
const breadcrumb = useMemo(
|
|
@@ -245,7 +305,10 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
245
305
|
|
|
246
306
|
return (
|
|
247
307
|
<div className="flex h-full min-h-0 flex-col bg-white">
|
|
248
|
-
<ChatSessionWorkspaceFileBreadcrumbs
|
|
308
|
+
<ChatSessionWorkspaceFileBreadcrumbs
|
|
309
|
+
breadcrumb={breadcrumb}
|
|
310
|
+
onFileOpen={onFileOpen}
|
|
311
|
+
/>
|
|
249
312
|
|
|
250
313
|
<div className="flex-1 min-h-0 overflow-hidden">
|
|
251
314
|
{file.viewMode === "diff" ? (
|
|
@@ -257,6 +320,8 @@ export function ChatSessionWorkspaceFilePreview({
|
|
|
257
320
|
previewKind={previewKind}
|
|
258
321
|
previewQuery={previewQuery}
|
|
259
322
|
previewText={previewText}
|
|
323
|
+
previewUrl={previewUrl}
|
|
324
|
+
previewViewer={previewViewer}
|
|
260
325
|
/>
|
|
261
326
|
)}
|
|
262
327
|
</div>
|
package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ChatFilePreviewViewer } from '@nextclaw/agent-chat-ui';
|
|
2
|
+
import type { ChatWorkspaceFileTab } from '@/features/chat/stores/chat-thread.store';
|
|
3
|
+
|
|
4
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
5
|
+
return !!value && typeof value === 'object';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function normalizeOptionalString(value: unknown): string | null {
|
|
9
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
10
|
+
? value.trim()
|
|
11
|
+
: null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeOptionalNumber(value: unknown): number | null {
|
|
15
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
16
|
+
? value
|
|
17
|
+
: null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizeOptionalText(value: unknown): string | null {
|
|
21
|
+
return typeof value === 'string' ? value : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeFilePreviewViewer(value: unknown): ChatFilePreviewViewer | null {
|
|
25
|
+
return value === 'auto' || value === 'source' || value === 'rendered'
|
|
26
|
+
? value
|
|
27
|
+
: null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function normalizePersistedWorkspaceFileTab(
|
|
31
|
+
value: unknown,
|
|
32
|
+
): ChatWorkspaceFileTab | null {
|
|
33
|
+
if (
|
|
34
|
+
!isRecord(value)
|
|
35
|
+
|| typeof value.key !== 'string'
|
|
36
|
+
|| typeof value.path !== 'string'
|
|
37
|
+
|| (value.viewMode !== 'preview' && value.viewMode !== 'diff')
|
|
38
|
+
) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const key = value.key.trim();
|
|
42
|
+
const path = value.path.trim();
|
|
43
|
+
if (!key || !path) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
key,
|
|
48
|
+
parentSessionKey: normalizeOptionalString(value.parentSessionKey),
|
|
49
|
+
path,
|
|
50
|
+
label: normalizeOptionalString(value.label),
|
|
51
|
+
viewMode: value.viewMode,
|
|
52
|
+
previewViewer: normalizeFilePreviewViewer(value.previewViewer),
|
|
53
|
+
line: normalizeOptionalNumber(value.line),
|
|
54
|
+
column: normalizeOptionalNumber(value.column),
|
|
55
|
+
rawText: normalizeOptionalText(value.rawText),
|
|
56
|
+
beforeText: normalizeOptionalText(value.beforeText),
|
|
57
|
+
afterText: normalizeOptionalText(value.afterText),
|
|
58
|
+
patchText: normalizeOptionalText(value.patchText),
|
|
59
|
+
oldStartLine: normalizeOptionalNumber(value.oldStartLine),
|
|
60
|
+
newStartLine: normalizeOptionalNumber(value.newStartLine),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function toPersistedWorkspaceFileTab(
|
|
65
|
+
tab: ChatWorkspaceFileTab,
|
|
66
|
+
): ChatWorkspaceFileTab {
|
|
67
|
+
return {
|
|
68
|
+
key: tab.key,
|
|
69
|
+
parentSessionKey: tab.parentSessionKey,
|
|
70
|
+
path: tab.path,
|
|
71
|
+
label: tab.label,
|
|
72
|
+
viewMode: tab.viewMode,
|
|
73
|
+
previewViewer: tab.previewViewer,
|
|
74
|
+
line: tab.line,
|
|
75
|
+
column: tab.column,
|
|
76
|
+
rawText: tab.rawText,
|
|
77
|
+
beforeText: tab.beforeText,
|
|
78
|
+
afterText: tab.afterText,
|
|
79
|
+
patchText: tab.patchText,
|
|
80
|
+
oldStartLine: tab.oldStartLine,
|
|
81
|
+
newStartLine: tab.newStartLine,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -155,6 +155,40 @@ describe('ChatThreadManager', () => {
|
|
|
155
155
|
expect(uiManager.goToSession).not.toHaveBeenCalled();
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
it('notifies the app layout coordinator after opening the workspace panel', () => {
|
|
159
|
+
const onWorkspacePanelOpened = vi.fn();
|
|
160
|
+
const manager = new ChatThreadManager(
|
|
161
|
+
createUiManager(),
|
|
162
|
+
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
163
|
+
onWorkspacePanelOpened,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
manager.openFilePreview({
|
|
167
|
+
path: 'README.md',
|
|
168
|
+
label: 'README.md',
|
|
169
|
+
viewMode: 'preview',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(onWorkspacePanelOpened).toHaveBeenCalledTimes(1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('does not notify the app layout coordinator when no workspace panel opens', () => {
|
|
176
|
+
const onWorkspacePanelOpened = vi.fn();
|
|
177
|
+
const manager = new ChatThreadManager(
|
|
178
|
+
createUiManager(),
|
|
179
|
+
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
180
|
+
onWorkspacePanelOpened,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
manager.openFilePreview({
|
|
184
|
+
path: ' ',
|
|
185
|
+
label: 'README.md',
|
|
186
|
+
viewMode: 'preview',
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
expect(onWorkspacePanelOpened).not.toHaveBeenCalled();
|
|
190
|
+
});
|
|
191
|
+
|
|
158
192
|
it('opens a side chat draft beside the selected parent session', () => {
|
|
159
193
|
const uiManager = createUiManager({
|
|
160
194
|
goToSession: vi.fn(),
|
|
@@ -293,6 +327,47 @@ describe('ChatThreadManager', () => {
|
|
|
293
327
|
]),
|
|
294
328
|
);
|
|
295
329
|
});
|
|
330
|
+
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe('ChatThreadManager workspace file tabs', () => {
|
|
334
|
+
it('keeps source and rendered previews for the same file as separate workspace tabs', () => {
|
|
335
|
+
const manager = new ChatThreadManager(
|
|
336
|
+
createUiManager(),
|
|
337
|
+
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
manager.openFilePreview({
|
|
341
|
+
path: 'demo.html',
|
|
342
|
+
label: 'demo.html',
|
|
343
|
+
viewMode: 'preview',
|
|
344
|
+
previewViewer: 'source',
|
|
345
|
+
});
|
|
346
|
+
manager.openFilePreview({
|
|
347
|
+
path: 'demo.html',
|
|
348
|
+
label: 'demo.html',
|
|
349
|
+
viewMode: 'preview',
|
|
350
|
+
previewViewer: 'rendered',
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
expect(useChatThreadStore.getState().snapshot).toMatchObject({
|
|
354
|
+
activeWorkspaceFileKey: 'parent-session-1::preview:rendered::demo.html',
|
|
355
|
+
workspaceFileTabs: [
|
|
356
|
+
{
|
|
357
|
+
key: 'parent-session-1::preview:rendered::demo.html',
|
|
358
|
+
path: 'demo.html',
|
|
359
|
+
viewMode: 'preview',
|
|
360
|
+
previewViewer: 'rendered',
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
key: 'parent-session-1::preview::demo.html',
|
|
364
|
+
path: 'demo.html',
|
|
365
|
+
viewMode: 'preview',
|
|
366
|
+
previewViewer: 'source',
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
});
|
|
370
|
+
});
|
|
296
371
|
});
|
|
297
372
|
|
|
298
373
|
describe('ChatThreadManager workspace navigation', () => {
|
|
@@ -513,6 +588,7 @@ describe('ChatThreadManager showContent', () => {
|
|
|
513
588
|
payload: {
|
|
514
589
|
path: 'docs/example.md',
|
|
515
590
|
line: 5,
|
|
591
|
+
viewer: 'rendered',
|
|
516
592
|
},
|
|
517
593
|
},
|
|
518
594
|
title: 'Example',
|
|
@@ -522,12 +598,14 @@ describe('ChatThreadManager showContent', () => {
|
|
|
522
598
|
|
|
523
599
|
expect(useChatThreadStore.getState().snapshot).toMatchObject({
|
|
524
600
|
activeWorkspacePanelKind: 'file',
|
|
525
|
-
activeWorkspaceFileKey: 'parent-session-1::preview::docs/example.md',
|
|
601
|
+
activeWorkspaceFileKey: 'parent-session-1::preview:rendered::docs/example.md',
|
|
526
602
|
workspaceFileTabs: [
|
|
527
603
|
expect.objectContaining({
|
|
604
|
+
key: 'parent-session-1::preview:rendered::docs/example.md',
|
|
528
605
|
path: 'docs/example.md',
|
|
529
606
|
label: 'Example',
|
|
530
607
|
line: 5,
|
|
608
|
+
previewViewer: 'rendered',
|
|
531
609
|
viewMode: 'preview',
|
|
532
610
|
}),
|
|
533
611
|
],
|
|
@@ -44,6 +44,7 @@ export class ChatThreadManager {
|
|
|
44
44
|
constructor(
|
|
45
45
|
private uiManager: ChatUiManager,
|
|
46
46
|
private sessionListManager: ChatSessionListManager,
|
|
47
|
+
private readonly onWorkspacePanelOpened?: () => void,
|
|
47
48
|
) {}
|
|
48
49
|
|
|
49
50
|
private hasSnapshotChanges = (patch: Partial<ChatThreadSnapshot>): boolean => {
|
|
@@ -110,14 +111,14 @@ export class ChatThreadManager {
|
|
|
110
111
|
return null;
|
|
111
112
|
}
|
|
112
113
|
const normalizedParentSessionKey = parentSessionKey?.trim() || null;
|
|
113
|
-
const
|
|
114
|
-
`${normalizedParentSessionKey ?? 'draft'}::${action.viewMode}::${normalizedPath}`;
|
|
114
|
+
const tabViewIdentity = action.viewMode === 'preview' && action.previewViewer === 'rendered' ? 'preview:rendered' : action.viewMode;
|
|
115
115
|
return {
|
|
116
|
-
key
|
|
116
|
+
key: `${normalizedParentSessionKey ?? 'draft'}::${tabViewIdentity}::${normalizedPath}`,
|
|
117
117
|
parentSessionKey: normalizedParentSessionKey,
|
|
118
118
|
path: normalizedPath,
|
|
119
119
|
label: action.label?.trim() || null,
|
|
120
120
|
viewMode: action.viewMode,
|
|
121
|
+
previewViewer: action.previewViewer ?? null,
|
|
121
122
|
line: action.line ?? null,
|
|
122
123
|
column: action.column ?? null,
|
|
123
124
|
rawText: action.rawText ?? null,
|
|
@@ -265,6 +266,7 @@ export class ChatThreadManager {
|
|
|
265
266
|
useChatThreadStore.getState().setSnapshot(patch);
|
|
266
267
|
}
|
|
267
268
|
this.ensureWorkspaceParentRoute(parentSessionKey);
|
|
269
|
+
this.onWorkspacePanelOpened?.();
|
|
268
270
|
};
|
|
269
271
|
|
|
270
272
|
openSideChatDraft = (parentSessionKey?: string | null) => {
|
|
@@ -285,6 +287,7 @@ export class ChatThreadManager {
|
|
|
285
287
|
key: activeSideChatDraft.draftKey,
|
|
286
288
|
});
|
|
287
289
|
this.ensureWorkspaceParentRoute(resolvedParentSessionKey);
|
|
290
|
+
this.onWorkspacePanelOpened?.();
|
|
288
291
|
};
|
|
289
292
|
|
|
290
293
|
materializeSideChatDraft = (params: {
|
|
@@ -322,6 +325,7 @@ export class ChatThreadManager {
|
|
|
322
325
|
key: nextTab.key,
|
|
323
326
|
});
|
|
324
327
|
this.ensureWorkspaceParentRoute(parentSessionKey);
|
|
328
|
+
this.onWorkspacePanelOpened?.();
|
|
325
329
|
};
|
|
326
330
|
|
|
327
331
|
private openSessionFromToolAction = (action: ChatToolActionViewModel) => {
|
|
@@ -384,6 +388,7 @@ export class ChatThreadManager {
|
|
|
384
388
|
path: request.target.payload.path,
|
|
385
389
|
label: request.title,
|
|
386
390
|
viewMode: 'preview',
|
|
391
|
+
previewViewer: request.target.payload.viewer,
|
|
387
392
|
line: request.target.payload.line,
|
|
388
393
|
column: request.target.payload.column,
|
|
389
394
|
});
|
|
@@ -504,6 +509,7 @@ export class ChatThreadManager {
|
|
|
504
509
|
kind: 'cron',
|
|
505
510
|
});
|
|
506
511
|
this.ensureWorkspaceParentRoute(parentSessionKey);
|
|
512
|
+
this.onWorkspacePanelOpened?.();
|
|
507
513
|
};
|
|
508
514
|
|
|
509
515
|
goBackWorkspacePanel = () => {
|
|
@@ -89,7 +89,11 @@ export class ChatUiManager {
|
|
|
89
89
|
showContent = async (params: { target: ChatUiDisplayContentTarget; title?: string }) => {
|
|
90
90
|
const { target, title } = params;
|
|
91
91
|
if (target.type === 'url') {
|
|
92
|
-
this.docBrowserManager.open(target.payload.url, {
|
|
92
|
+
this.docBrowserManager.open(target.payload.url, {
|
|
93
|
+
dedupeKey: `browser:${target.payload.url}`,
|
|
94
|
+
kind: 'content',
|
|
95
|
+
title,
|
|
96
|
+
});
|
|
93
97
|
return;
|
|
94
98
|
}
|
|
95
99
|
const { appId } = target.payload;
|
|
@@ -16,7 +16,8 @@ export class ChatPresenter {
|
|
|
16
16
|
this.chatQueryManager = new ChatQueryManager();
|
|
17
17
|
this.chatThreadManager = new ChatThreadManager(
|
|
18
18
|
this.chatUiManager,
|
|
19
|
-
this.chatSessionListManager
|
|
19
|
+
this.chatSessionListManager,
|
|
20
|
+
appPresenter.notifyRightPanelOpened,
|
|
20
21
|
);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -54,6 +54,7 @@ describe('chat thread workspace panel persistence', () => {
|
|
|
54
54
|
path: 'README.md',
|
|
55
55
|
label: 'README.md',
|
|
56
56
|
viewMode: 'preview',
|
|
57
|
+
previewViewer: 'rendered',
|
|
57
58
|
rawText: '# Hello',
|
|
58
59
|
fullLines: [],
|
|
59
60
|
},
|
|
@@ -83,6 +84,7 @@ describe('chat thread workspace panel persistence', () => {
|
|
|
83
84
|
expect(persisted.state.snapshot.sessionKey).toBeUndefined();
|
|
84
85
|
expect(persisted.state.snapshot.isSending).toBeUndefined();
|
|
85
86
|
expect(persisted.state.snapshot.workspaceFileTabs[0].rawText).toBe('# Hello');
|
|
87
|
+
expect(persisted.state.snapshot.workspaceFileTabs[0].previewViewer).toBe('rendered');
|
|
86
88
|
expect(persisted.state.snapshot.workspaceFileTabs[0].fullLines).toBeUndefined();
|
|
87
89
|
});
|
|
88
90
|
|
|
@@ -138,6 +140,7 @@ describe('chat thread workspace panel persistence', () => {
|
|
|
138
140
|
path: 'README.md',
|
|
139
141
|
label: 'README.md',
|
|
140
142
|
viewMode: 'preview',
|
|
143
|
+
previewViewer: 'rendered',
|
|
141
144
|
rawText: '# Hello',
|
|
142
145
|
},
|
|
143
146
|
{
|
|
@@ -171,6 +174,7 @@ describe('chat thread workspace panel persistence', () => {
|
|
|
171
174
|
path: 'README.md',
|
|
172
175
|
label: 'README.md',
|
|
173
176
|
viewMode: 'preview',
|
|
177
|
+
previewViewer: 'rendered',
|
|
174
178
|
rawText: '# Hello',
|
|
175
179
|
},
|
|
176
180
|
],
|