@nextclaw/ui 0.15.1 → 0.15.3
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 +51 -0
- package/dist/assets/{api-b1R9VdeH.js → api-iNBpppzj.js} +3 -3
- package/dist/assets/{appearance-settings-page-D2O9pch9.js → appearance-settings-page-DMffQFR3.js} +1 -1
- package/dist/assets/{book-open-B77lQ2Tg.js → book-open-C3mJGwiy.js} +1 -1
- package/dist/assets/channels-list-page-CCtp0BRS.js +8 -0
- package/dist/assets/chat-page-H5a0dWvO.js +1 -0
- package/dist/assets/config-split-page-CrOJgB-Y.js +1 -0
- package/dist/assets/{confirm-dialog-C-NXSioX.js → confirm-dialog-Chv50USy.js} +2 -2
- package/dist/assets/{desktop-update-config-usHpXugP.js → desktop-update-config-KzTuXRf_.js} +1 -1
- package/dist/assets/{dist-jdGmbb6w.js → dist-BunzmC4B.js} +1 -1
- package/dist/assets/{dist-YtoqU-AT.js → dist-ChmGwvtK.js} +1 -1
- package/dist/assets/doc-browser-DNRL4Zvd.js +1 -0
- package/dist/assets/doc-browser-Dg0pmkOg.js +1 -0
- package/dist/assets/doc-browser-context-BHY-0FSD.js +1 -0
- package/dist/assets/{es2015-bNpL2LKy.js → es2015-DQZ5JWwK.js} +1 -1
- package/dist/assets/{external-link-BM6fk8q-.js → external-link-CU0kzzIP.js} +1 -1
- package/dist/assets/form-actions-BvCp_0lS.js +1 -0
- package/dist/assets/{i18n-provider-CntWolRA.js → i18n-provider-Cu8XyZb_.js} +1 -1
- package/dist/assets/index-CzfBdvK1.css +1 -0
- package/dist/assets/index-DGqHne1Q.js +104 -0
- package/dist/assets/loader-circle-CDSFuzmk.js +1 -0
- package/dist/assets/mcp-marketplace-page-BeEQK0DL.js +9 -0
- package/dist/assets/mcp-marketplace-page-D6idOTLp.js +1 -0
- package/dist/assets/model-config-page-bNNo-XyG.js +1 -0
- package/dist/assets/plus-D4DsdLN-.js +1 -0
- package/dist/assets/{provider-scoped-model-input-Cm_a4GHf.js → provider-scoped-model-input-IlgQCsoT.js} +1 -1
- package/dist/assets/providers-config-page-wLo6CAJw.js +1 -0
- package/dist/assets/{react-BN8uzPnp.js → react-BPIsQIlO.js} +1 -1
- package/dist/assets/{refresh-cw-BpVcFPmJ.js → refresh-cw-CILd1vAP.js} +1 -1
- package/dist/assets/remote-uKEYp7AR.js +1 -0
- package/dist/assets/rotate-cw-wPFPg1kH.js +1 -0
- package/dist/assets/runtime-config-page-DDn1sP4G.js +1 -0
- package/dist/assets/{save-BpFW5fpk.js → save-ByJqcPTP.js} +1 -1
- package/dist/assets/{search-D0uO7enf.js → search-DVIV6pfD.js} +1 -1
- package/dist/assets/search-config-page-B-Cf4iiB.js +1 -0
- package/dist/assets/secrets-config-page-DPKsY8Iw.js +3 -0
- package/dist/assets/security-config-e9SSBlKr.js +1 -0
- package/dist/assets/{select-Bh-CrA4p.js → select-BdgczSNw.js} +1 -1
- package/dist/assets/{setting-row-BJfCHRcb.js → setting-row-BkTVUTKx.js} +1 -1
- package/dist/assets/{settings-2-Blzi1vr6.js → settings-2-CH5shGg_.js} +1 -1
- package/dist/assets/{side-dock-ZdaSrQVB.js → side-dock-BsBvcJ9i.js} +2 -2
- package/dist/assets/{skeleton-BLlEzebZ.js → skeleton-DeX1FKw7.js} +1 -1
- package/dist/assets/switch-Cv8Ekb3x.js +1 -0
- package/dist/assets/{tag-chip-CzGihiQx.js → tag-chip-RB9uKcoC.js} +1 -1
- package/dist/assets/{tooltip-CdcrBjrC.js → tooltip-oQlEN-l6.js} +1 -1
- package/dist/assets/{use-config-CVPgsBmU.js → use-config-l3W7y8Qz.js} +1 -1
- package/dist/assets/{use-confirm-dialog-CSeBk9SF.js → use-confirm-dialog-BAxaVOJI.js} +1 -1
- package/dist/assets/x-BBMffEN0.js +1 -0
- package/dist/index.html +38 -30
- package/index.html +12 -4
- package/package.json +9 -9
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +29 -1
- package/src/app/components/layout/sidebar-items.tsx +46 -7
- package/src/app/components/layout/sidebar-rail.styles.ts +2 -2
- package/src/app/components/layout/sidebar.tsx +84 -62
- package/src/app/configs/app-navigation.config.ts +49 -0
- package/src/app/styles/design-system.css +99 -4
- package/src/features/channels/components/config/channel-form.tsx +14 -11
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +40 -0
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +3 -1
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +1 -1
- package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +48 -0
- package/src/features/chat/components/layout/__tests__/chat-sidebar-utility-menu.test.tsx +55 -0
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +0 -3
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +3 -4
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +30 -31
- package/src/features/chat/components/layout/chat-sidebar.tsx +21 -7
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +2 -1
- package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +1 -1
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +22 -1
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +62 -1
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +10 -5
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +47 -1
- package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +11 -1
- package/src/features/chat/features/message/utils/__tests__/chat-message-texts.utils.test.ts +23 -0
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +0 -11
- package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +5 -0
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +138 -0
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +116 -0
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +108 -46
- package/src/features/chat/features/session/components/chat-sidebar-session-entry.tsx +6 -0
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +48 -24
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +0 -3
- package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +0 -4
- package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +2 -2
- package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +25 -3
- package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +9 -4
- package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +0 -5
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +226 -56
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +1 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +144 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +99 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +2 -2
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +4 -2
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +232 -21
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +3 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +44 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +45 -3
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +4 -0
- package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +33 -2
- package/src/features/chat/managers/chat-session-list.manager.ts +32 -0
- package/src/features/chat/managers/chat-thread.manager.ts +2 -0
- package/src/features/chat/stores/chat-session-list.store.ts +28 -5
- package/src/features/chat/stores/chat-thread.store.ts +3 -0
- package/src/features/cron/components/cron-config.tsx +63 -80
- package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.tsx +44 -6
- package/src/features/marketplace/components/marketplace-detail-doc.tsx +8 -4
- package/src/features/marketplace/components/marketplace-page-parts.tsx +1 -1
- package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +5 -12
- package/src/features/marketplace/hooks/use-marketplace-detail-doc-entry.ts +84 -0
- package/src/features/panel-apps/components/panel-app-list-item.tsx +6 -6
- package/src/features/panel-apps/components/panel-apps-list.tsx +2 -2
- package/src/features/panel-apps/index.ts +1 -0
- package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +2 -10
- package/src/features/panel-apps/utils/panel-app-iframe.utils.ts +6 -0
- package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +11 -3
- package/src/features/pwa/managers/pwa-shell-theme.manager.ts +3 -1
- package/src/features/right-panel-resources/components/right-panel-resource-home-page.tsx +1 -1
- package/src/features/service-apps/components/service-apps-panel.tsx +145 -35
- package/src/features/settings/components/config/provider-auth-section.tsx +1 -1
- package/src/features/settings/components/config/provider-form.tsx +11 -8
- package/src/features/settings/components/config/provider-models-section.tsx +7 -7
- package/src/features/settings/components/config/provider-pill-selector.tsx +1 -1
- package/src/features/settings/components/config/secrets-config-form.tsx +5 -4
- package/src/features/settings/pages/model-config-page.tsx +26 -25
- package/src/features/settings/pages/providers-config-page.tsx +10 -13
- package/src/features/settings/pages/search-config-page.tsx +8 -5
- package/src/features/side-dock/components/side-dock.tsx +3 -3
- package/src/features/system-status/components/config/runtime-config-editor.tsx +5 -4
- package/src/index.css +257 -13
- package/src/shared/components/actions/action-link.tsx +1 -1
- package/src/shared/components/config-split-page.tsx +6 -6
- package/src/shared/components/doc-browser/__tests__/doc-browser-url.utils.test.ts +73 -0
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +48 -0
- package/src/shared/components/doc-browser/doc-browser-context.tsx +4 -0
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +6 -2
- package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +2 -1
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +10 -10
- package/src/shared/components/doc-browser/doc-browser.tsx +6 -4
- package/src/shared/components/doc-browser/utils/doc-browser-url.utils.ts +62 -4
- package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx +18 -0
- package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +28 -3
- package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +36 -0
- package/src/shared/components/ui/actions/form-actions.tsx +25 -0
- package/src/shared/components/ui/actions/icon-action-button.tsx +25 -2
- package/src/shared/components/ui/button.tsx +1 -1
- package/src/shared/components/ui/card.tsx +12 -4
- package/src/shared/components/ui/dialog.tsx +1 -1
- package/src/shared/components/ui/input.tsx +1 -1
- package/src/shared/components/ui/select.tsx +1 -1
- package/src/shared/components/ui/switch.tsx +1 -1
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +2 -1
- package/src/shared/components/ui/tabs.tsx +1 -1
- package/src/shared/components/ui/textarea.tsx +1 -1
- package/src/shared/hooks/use-server-path-browse.ts +26 -4
- package/src/shared/hooks/use-server-path-read.ts +11 -1
- package/src/shared/lib/api/utils/server-path.utils.ts +1 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +99 -0
- package/src/shared/lib/i18n/locales/en-US/core.json +3 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +5 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +99 -0
- package/src/shared/lib/i18n/locales/zh-CN/core.json +3 -0
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +5 -0
- package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +9 -5
- package/src/shared/lib/theme/index.test.ts +21 -0
- package/src/shared/lib/theme/index.ts +6 -4
- package/src/vite-env.d.ts +2 -0
- package/dist/assets/channels-list-page-DcsIQc6A.js +0 -8
- package/dist/assets/chat-page-CZ7WxPir.js +0 -1
- package/dist/assets/config-split-page-BWQ6_Raq.js +0 -1
- package/dist/assets/doc-browser-B_Tf9n70.js +0 -1
- package/dist/assets/doc-browser-BdZPFOVa.js +0 -1
- package/dist/assets/doc-browser-context-DwokemUn.js +0 -1
- package/dist/assets/index-BIcEw90u.js +0 -104
- package/dist/assets/index-BiNSTKTU.css +0 -1
- package/dist/assets/loader-circle-MBgNSVIH.js +0 -1
- package/dist/assets/mcp-marketplace-page-CLmUstLN.js +0 -9
- package/dist/assets/mcp-marketplace-page-rZkmWPMy.js +0 -1
- package/dist/assets/model-config-page-Di0ySJD2.js +0 -1
- package/dist/assets/plus-vDqW5Kxp.js +0 -1
- package/dist/assets/providers-config-page-BAf2W0MV.js +0 -1
- package/dist/assets/remote-CLQ0IBM8.js +0 -1
- package/dist/assets/rotate-cw-CvNBv7_w.js +0 -1
- package/dist/assets/runtime-config-page-CflvvoDc.js +0 -1
- package/dist/assets/search-config-page-D0klFUXd.js +0 -1
- package/dist/assets/secrets-config-page-dVZ631Wp.js +0 -3
- package/dist/assets/security-config-C5gnlkuj.js +0 -1
- package/dist/assets/switch-CFLJFNSd.js +0 -1
- package/dist/assets/x-BF2KQ--V.js +0 -1
- /package/dist/assets/{config-hints-fGnUjDe9.js → config-hints-C_iP1zl_.js} +0 -0
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
SIDEBAR_RAIL_CONTROL_CLASS,
|
|
17
17
|
SIDEBAR_RAIL_ICON_CLASS,
|
|
18
18
|
SIDEBAR_RAIL_ITEM_GAP_CLASS,
|
|
19
|
-
SIDEBAR_RAIL_PRIMARY_SURFACE_CLASS,
|
|
20
19
|
SIDEBAR_RAIL_SURFACE_CLASS,
|
|
21
20
|
} from "@/app/components/layout/sidebar-rail.styles";
|
|
22
21
|
|
|
@@ -150,7 +149,7 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
150
149
|
label={t("chatSidebarNewTask")}
|
|
151
150
|
className={cn(
|
|
152
151
|
SIDEBAR_RAIL_CONTROL_CLASS,
|
|
153
|
-
|
|
152
|
+
SIDEBAR_RAIL_SURFACE_CLASS,
|
|
154
153
|
)}
|
|
155
154
|
onClick={() => {
|
|
156
155
|
onCreateMenuOpenChange(false);
|
|
@@ -259,7 +258,7 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
259
258
|
|
|
260
259
|
<div className="px-4 pb-3">
|
|
261
260
|
<div className="relative">
|
|
262
|
-
<Search className="absolute left-3 top-2.5 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
261
|
+
<Search className="pointer-events-none absolute left-3 top-2.5 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
263
262
|
<Input
|
|
264
263
|
value={query}
|
|
265
264
|
onChange={(event) => onQueryChange(event.target.value)}
|
|
@@ -294,7 +293,7 @@ export function ChatSidebarMobileToolbar(props: ChatSidebarToolbarProps) {
|
|
|
294
293
|
<div className="px-4 pb-2 pt-1">
|
|
295
294
|
<div className="flex items-center gap-2">
|
|
296
295
|
<div className="relative min-w-0 flex-1">
|
|
297
|
-
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground/70" />
|
|
296
|
+
<Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground/70" />
|
|
298
297
|
<Input
|
|
299
298
|
value={query}
|
|
300
299
|
onChange={(event) => onQueryChange(event.target.value)}
|
|
@@ -34,10 +34,7 @@ import {
|
|
|
34
34
|
type LucideIcon,
|
|
35
35
|
} from "lucide-react";
|
|
36
36
|
|
|
37
|
-
type ChatSidebarUtilityOption<Value extends string> = {
|
|
38
|
-
value: Value;
|
|
39
|
-
label: string;
|
|
40
|
-
};
|
|
37
|
+
type ChatSidebarUtilityOption<Value extends string> = { value: Value; label: string };
|
|
41
38
|
|
|
42
39
|
type ChatSidebarUtilityMenuProps = {
|
|
43
40
|
isOpen: boolean;
|
|
@@ -55,6 +52,12 @@ type ChatSidebarUtilityMenuProps = {
|
|
|
55
52
|
collapsed?: boolean;
|
|
56
53
|
};
|
|
57
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
|
+
|
|
58
61
|
export function ChatSidebarUtilityMenu({
|
|
59
62
|
isOpen,
|
|
60
63
|
onOpenChange,
|
|
@@ -70,16 +73,6 @@ export function ChatSidebarUtilityMenu({
|
|
|
70
73
|
onOpenApps,
|
|
71
74
|
collapsed = false,
|
|
72
75
|
}: ChatSidebarUtilityMenuProps) {
|
|
73
|
-
const handleOpenDocs = () => {
|
|
74
|
-
onOpenDocs();
|
|
75
|
-
onOpenChange(false);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const handleOpenApps = () => {
|
|
79
|
-
onOpenApps();
|
|
80
|
-
onOpenChange(false);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
76
|
return (
|
|
84
77
|
<Popover open={isOpen} onOpenChange={onOpenChange}>
|
|
85
78
|
{collapsed ? (
|
|
@@ -115,7 +108,7 @@ export function ChatSidebarUtilityMenu({
|
|
|
115
108
|
<button
|
|
116
109
|
type="button"
|
|
117
110
|
aria-label={t("settingsMenu")}
|
|
118
|
-
className="flex w-full items-center gap-2.5 rounded-xl px-3 py-2 text-[13px] font-medium text-muted-foreground transition-colors duration-base hover:bg-gray-200/60 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-
|
|
111
|
+
className="flex w-full items-center gap-2.5 rounded-xl px-3 py-2 text-[13px] font-medium text-muted-foreground transition-colors duration-base hover:bg-gray-200/60 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
|
|
119
112
|
>
|
|
120
113
|
<Settings className="h-4 w-4 shrink-0 text-muted-foreground/70" />
|
|
121
114
|
<span className="min-w-0 flex-1 text-left">{t("settings")}</span>
|
|
@@ -129,6 +122,11 @@ export function ChatSidebarUtilityMenu({
|
|
|
129
122
|
side={collapsed ? "right" : "top"}
|
|
130
123
|
align={collapsed ? "end" : "start"}
|
|
131
124
|
className="w-64 p-2"
|
|
125
|
+
onFocusOutside={(event) => {
|
|
126
|
+
if (isChatComposerFocusTarget(event.detail.originalEvent.target)) {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
}
|
|
129
|
+
}}
|
|
132
130
|
>
|
|
133
131
|
<div className="space-y-1">
|
|
134
132
|
<NavLink
|
|
@@ -139,22 +137,23 @@ export function ChatSidebarUtilityMenu({
|
|
|
139
137
|
<Settings className="h-4 w-4 text-muted-foreground/70" />
|
|
140
138
|
<span className="flex-1 text-left">{t("settings")}</span>
|
|
141
139
|
</NavLink>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
140
|
+
{[
|
|
141
|
+
{ key: "docs", icon: BookOpen, label: t("docBrowserHelp"), action: onOpenDocs },
|
|
142
|
+
{ key: "apps", icon: Boxes, label: t("appsTitle"), action: onOpenApps },
|
|
143
|
+
].map(({ key, icon: Icon, label, action }) => (
|
|
144
|
+
<button
|
|
145
|
+
key={key}
|
|
146
|
+
type="button"
|
|
147
|
+
onClick={() => {
|
|
148
|
+
action();
|
|
149
|
+
onOpenChange(false);
|
|
150
|
+
}}
|
|
151
|
+
className="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-gray-200/60 hover:text-gray-900"
|
|
152
|
+
>
|
|
153
|
+
<Icon className="h-4 w-4 text-muted-foreground/70" />
|
|
154
|
+
<span className="flex-1 text-left">{label}</span>
|
|
155
|
+
</button>
|
|
156
|
+
))}
|
|
158
157
|
</div>
|
|
159
158
|
|
|
160
159
|
<div className="my-2 h-px bg-border" />
|
|
@@ -105,7 +105,7 @@ export function ChatSidebar({
|
|
|
105
105
|
const { language, setLanguage } = useI18n();
|
|
106
106
|
const { theme, setTheme } = useTheme();
|
|
107
107
|
const currentThemeLabel = t(
|
|
108
|
-
THEME_OPTIONS.find((o) => o.value === theme)?.labelKey ?? "
|
|
108
|
+
THEME_OPTIONS.find((o) => o.value === theme)?.labelKey ?? "themeWork",
|
|
109
109
|
);
|
|
110
110
|
const currentLanguageLabel =
|
|
111
111
|
LANGUAGE_OPTIONS.find((o) => o.value === language)?.label ?? language;
|
|
@@ -132,6 +132,14 @@ export function ChatSidebar({
|
|
|
132
132
|
),
|
|
133
133
|
[agentsQuery.data?.agents],
|
|
134
134
|
);
|
|
135
|
+
const pinnedSessionKeys = useMemo(
|
|
136
|
+
() => new Set(listSnapshot.pinnedSessionKeys),
|
|
137
|
+
[listSnapshot.pinnedSessionKeys],
|
|
138
|
+
);
|
|
139
|
+
const pinnedProjectRoots = useMemo(
|
|
140
|
+
() => new Set(listSnapshot.pinnedProjectRoots),
|
|
141
|
+
[listSnapshot.pinnedProjectRoots],
|
|
142
|
+
);
|
|
135
143
|
const sortedItems = useMemo(
|
|
136
144
|
() => sortSessionItemsByActivityAtDesc(items),
|
|
137
145
|
[items],
|
|
@@ -140,10 +148,13 @@ export function ChatSidebar({
|
|
|
140
148
|
() => groupChildSessionsByParentKey(items),
|
|
141
149
|
[items],
|
|
142
150
|
);
|
|
143
|
-
const groups = useMemo(
|
|
151
|
+
const groups = useMemo(
|
|
152
|
+
() => groupSessionsByDate(sortedItems, pinnedSessionKeys),
|
|
153
|
+
[pinnedSessionKeys, sortedItems],
|
|
154
|
+
);
|
|
144
155
|
const projectGroups = useMemo(
|
|
145
|
-
() => groupSessionsByProject(sortedItems),
|
|
146
|
-
[sortedItems],
|
|
156
|
+
() => groupSessionsByProject(sortedItems, pinnedSessionKeys, pinnedProjectRoots),
|
|
157
|
+
[pinnedProjectRoots, pinnedSessionKeys, sortedItems],
|
|
147
158
|
);
|
|
148
159
|
const sessionTypeOptions = useMemo(
|
|
149
160
|
() => buildSessionTypeOptions(sessionTypesData?.options ?? []),
|
|
@@ -200,6 +211,7 @@ export function ChatSidebar({
|
|
|
200
211
|
draftLabel={draftLabel}
|
|
201
212
|
savingSessionKey={savingSessionKey}
|
|
202
213
|
sessionTypeOptions={sessionTypeOptions}
|
|
214
|
+
isPinned={pinnedSessionKeys.has(item.session.key)}
|
|
203
215
|
sessionTitle={getSessionTitle}
|
|
204
216
|
onSelectSession={presenter.chatSessionListManager.selectSession}
|
|
205
217
|
onOpenChildSessions={(parentSessionKey, activeChildSessionKey) =>
|
|
@@ -212,6 +224,9 @@ export function ChatSidebar({
|
|
|
212
224
|
onDraftLabelChange={setDraftLabel}
|
|
213
225
|
onSaveSessionLabel={saveSessionLabel}
|
|
214
226
|
onCancelEditingSessionLabel={cancelEditingSessionLabel}
|
|
227
|
+
onTogglePinned={() =>
|
|
228
|
+
presenter.chatSessionListManager.toggleSessionPinned(item.session.key)
|
|
229
|
+
}
|
|
215
230
|
/>
|
|
216
231
|
);
|
|
217
232
|
const createSessionAndOpenIfNeeded = (
|
|
@@ -233,9 +248,9 @@ export function ChatSidebar({
|
|
|
233
248
|
: shouldCollapse
|
|
234
249
|
? cn(
|
|
235
250
|
SIDEBAR_RAIL_WIDTH_CLASS,
|
|
236
|
-
"shrink-0
|
|
251
|
+
"shrink-0",
|
|
237
252
|
)
|
|
238
|
-
: "w-[280px] shrink-0
|
|
253
|
+
: "w-[280px] shrink-0",
|
|
239
254
|
)}
|
|
240
255
|
data-sidebar-collapsed={shouldCollapse ? "true" : "false"}
|
|
241
256
|
>
|
|
@@ -295,7 +310,6 @@ export function ChatSidebar({
|
|
|
295
310
|
isCollapsed={shouldCollapse}
|
|
296
311
|
isLoading={isLoading}
|
|
297
312
|
isProjectFirstView={isProjectFirstView}
|
|
298
|
-
onCreateSession={createSessionAndOpenIfNeeded}
|
|
299
313
|
onSelectMode={presenter.chatSessionListManager.setListMode}
|
|
300
314
|
projectGroups={projectGroups}
|
|
301
315
|
renderSessionItem={renderSessionItem}
|
|
@@ -44,7 +44,7 @@ function QueuedInputIconButton({
|
|
|
44
44
|
aria-label={label}
|
|
45
45
|
disabled={disabled}
|
|
46
46
|
onClick={onClick}
|
|
47
|
-
className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-
|
|
47
|
+
className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:cursor-not-allowed disabled:text-muted-foreground/50"
|
|
48
48
|
>
|
|
49
49
|
{children}
|
|
50
50
|
</button>
|
package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx
CHANGED
|
@@ -32,8 +32,13 @@ vi.mock('@/app/presenters/app.presenter', () => ({
|
|
|
32
32
|
}));
|
|
33
33
|
|
|
34
34
|
vi.mock('@/features/panel-apps', () => ({
|
|
35
|
+
PANEL_APP_IFRAME_SANDBOX: 'allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-downloads allow-pointer-lock allow-presentation',
|
|
35
36
|
findPanelAppEntryByDisplayId: (entries: Array<{ appId: string; id: string; title: string }>, value: string) =>
|
|
36
37
|
entries.find((entry) => [entry.appId, entry.id, entry.title].includes(value)) ?? null,
|
|
38
|
+
focusPanelAppIframe: (iframe: HTMLIFrameElement | null) => {
|
|
39
|
+
iframe?.focus({ preventScroll: true });
|
|
40
|
+
iframe?.contentWindow?.focus();
|
|
41
|
+
},
|
|
37
42
|
usePanelApps: () => mocks.panelApps,
|
|
38
43
|
}));
|
|
39
44
|
|
|
@@ -68,7 +73,6 @@ it('renders inline panel apps as bounded card-mode iframes with an expand action
|
|
|
68
73
|
expect(iframe.getAttribute('src')).toBe(
|
|
69
74
|
'/api/panel-apps/weather-card/content?nextclawDisplayMode=card&nextclawPlacement=inline',
|
|
70
75
|
);
|
|
71
|
-
expect(iframe.getAttribute('scrolling')).toBe('auto');
|
|
72
76
|
|
|
73
77
|
fireEvent.click(screen.getByLabelText('chatPanelCardExpand'));
|
|
74
78
|
|
|
@@ -80,6 +84,23 @@ it('renders inline panel apps as bounded card-mode iframes with an expand action
|
|
|
80
84
|
}));
|
|
81
85
|
});
|
|
82
86
|
|
|
87
|
+
it('focuses the inline panel app iframe when the pointer enters it', () => {
|
|
88
|
+
render(<ChatInlinePanelAppCard panelApp={{
|
|
89
|
+
appId: 'weather-card',
|
|
90
|
+
title: 'Weather',
|
|
91
|
+
}} />);
|
|
92
|
+
|
|
93
|
+
const iframe = screen.getByTitle('Weather') as HTMLIFrameElement;
|
|
94
|
+
const iframeFocus = vi.spyOn(iframe, 'focus');
|
|
95
|
+
const contentWindowFocus = vi.spyOn(iframe.contentWindow!, 'focus').mockImplementation(() => undefined);
|
|
96
|
+
|
|
97
|
+
fireEvent.pointerOver(iframe);
|
|
98
|
+
|
|
99
|
+
expect(iframe.getAttribute('tabindex')).toBe('0');
|
|
100
|
+
expect(iframeFocus).toHaveBeenCalledWith({ preventScroll: true });
|
|
101
|
+
expect(contentWindowFocus).toHaveBeenCalled();
|
|
102
|
+
});
|
|
103
|
+
|
|
83
104
|
it('can render inline panel apps without a side-panel expand action', () => {
|
|
84
105
|
render(
|
|
85
106
|
<ChatInlinePanelAppCard
|
package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx
CHANGED
|
@@ -9,6 +9,8 @@ const captures = vi.hoisted(() => ({
|
|
|
9
9
|
messages: unknown[];
|
|
10
10
|
onToolAction?: (action: unknown) => void;
|
|
11
11
|
onFileOpen?: (action: unknown) => void;
|
|
12
|
+
onAttachmentOpen?: (file: unknown) => void;
|
|
13
|
+
onInlineTokenClick?: (token: unknown) => void;
|
|
12
14
|
renderInlineDisplay?: (display: unknown) => unknown;
|
|
13
15
|
renderPanelAppCard?: (panelApp: unknown) => unknown;
|
|
14
16
|
texts?: Record<string, unknown>;
|
|
@@ -16,6 +18,7 @@ const captures = vi.hoisted(() => ({
|
|
|
16
18
|
language: "en",
|
|
17
19
|
openFilePreview: vi.fn(),
|
|
18
20
|
handleToolAction: vi.fn(),
|
|
21
|
+
showContent: vi.fn(),
|
|
19
22
|
}));
|
|
20
23
|
|
|
21
24
|
vi.mock("@nextclaw/agent-chat-ui", async (importOriginal) => {
|
|
@@ -26,6 +29,7 @@ vi.mock("@nextclaw/agent-chat-ui", async (importOriginal) => {
|
|
|
26
29
|
messages: unknown[];
|
|
27
30
|
onToolAction?: (action: unknown) => void;
|
|
28
31
|
onFileOpen?: (action: unknown) => void;
|
|
32
|
+
onAttachmentOpen?: (file: unknown) => void;
|
|
29
33
|
renderInlineDisplay?: (display: unknown) => unknown;
|
|
30
34
|
renderPanelAppCard?: (panelApp: unknown) => unknown;
|
|
31
35
|
texts?: Record<string, unknown>;
|
|
@@ -42,6 +46,9 @@ vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
|
|
|
42
46
|
openFilePreview: captures.openFilePreview,
|
|
43
47
|
handleToolAction: captures.handleToolAction,
|
|
44
48
|
},
|
|
49
|
+
chatUiManager: {
|
|
50
|
+
showContent: captures.showContent,
|
|
51
|
+
},
|
|
45
52
|
}),
|
|
46
53
|
}));
|
|
47
54
|
|
|
@@ -59,6 +66,7 @@ beforeEach(() => {
|
|
|
59
66
|
captures.language = "en";
|
|
60
67
|
captures.openFilePreview.mockReset();
|
|
61
68
|
captures.handleToolAction.mockReset();
|
|
69
|
+
captures.showContent.mockReset();
|
|
62
70
|
});
|
|
63
71
|
|
|
64
72
|
it("reuses adapted message references when the source message object is unchanged", () => {
|
|
@@ -300,6 +308,41 @@ it("wires markdown file link actions to the workspace file preview manager", ()
|
|
|
300
308
|
expect(captures.openFilePreview).toHaveBeenCalledWith(action);
|
|
301
309
|
});
|
|
302
310
|
|
|
311
|
+
it("opens message attachments in the workspace file preview", () => {
|
|
312
|
+
const message = {
|
|
313
|
+
id: "assistant-attachment",
|
|
314
|
+
sessionId: "session-1",
|
|
315
|
+
role: "assistant",
|
|
316
|
+
status: "final",
|
|
317
|
+
timestamp: "2026-03-31T10:05:00.000Z",
|
|
318
|
+
parts: [
|
|
319
|
+
{
|
|
320
|
+
type: "file",
|
|
321
|
+
name: "spec.pdf",
|
|
322
|
+
mimeType: "application/pdf",
|
|
323
|
+
contentBase64: "cGRm",
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
} satisfies NcpMessage;
|
|
327
|
+
|
|
328
|
+
render(<ChatMessageListContainer messages={[message]} isSending={false} />);
|
|
329
|
+
captures.renders[captures.renders.length - 1]?.onAttachmentOpen?.({
|
|
330
|
+
label: "spec.pdf",
|
|
331
|
+
mimeType: "application/pdf",
|
|
332
|
+
dataUrl: "data:application/pdf;base64,cGRm",
|
|
333
|
+
isImage: false,
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(captures.openFilePreview).toHaveBeenCalledWith({
|
|
337
|
+
path: "spec.pdf",
|
|
338
|
+
label: "spec.pdf",
|
|
339
|
+
viewMode: "preview",
|
|
340
|
+
contentUrl: "data:application/pdf;base64,cGRm",
|
|
341
|
+
mimeType: "application/pdf",
|
|
342
|
+
});
|
|
343
|
+
expect(captures.showContent).not.toHaveBeenCalled();
|
|
344
|
+
});
|
|
345
|
+
|
|
303
346
|
it("renders context inheritance as a divider without repeating inherited messages", () => {
|
|
304
347
|
const inheritedMessage = {
|
|
305
348
|
id: "child-session:inherited:1",
|
|
@@ -387,7 +430,7 @@ it("keeps Hermes tool invocation parts as tool cards instead of flattening them
|
|
|
387
430
|
});
|
|
388
431
|
});
|
|
389
432
|
|
|
390
|
-
it("passes localized
|
|
433
|
+
it("passes localized message presentation texts to the shared chat UI", () => {
|
|
391
434
|
captures.language = "zh";
|
|
392
435
|
|
|
393
436
|
render(<ChatMessageListContainer messages={[]} isSending={false} />);
|
|
@@ -400,6 +443,24 @@ it("passes localized attachment card texts to the shared chat UI", () => {
|
|
|
400
443
|
pdf: "chatAttachmentCategoryPdf",
|
|
401
444
|
generic: "chatAttachmentCategoryGeneric",
|
|
402
445
|
},
|
|
446
|
+
reasoningCharacterCountTemplates: {
|
|
447
|
+
inProgress: "chatReasoningInProgressCharacterCount",
|
|
448
|
+
completed: "chatReasoningCompletedCharacterCount",
|
|
449
|
+
},
|
|
450
|
+
toolStatusLabels: {
|
|
451
|
+
terminal: {
|
|
452
|
+
running: "chatToolTerminalRunning",
|
|
453
|
+
success: "chatToolTerminalSuccess",
|
|
454
|
+
error: "chatToolTerminalError",
|
|
455
|
+
cancelled: "chatToolTerminalCancelled",
|
|
456
|
+
},
|
|
457
|
+
fileEdit: {
|
|
458
|
+
running: "chatToolFileEditRunning",
|
|
459
|
+
success: "chatToolFileEditSuccess",
|
|
460
|
+
error: "chatToolFileEditError",
|
|
461
|
+
cancelled: "chatToolFileEditCancelled",
|
|
462
|
+
},
|
|
463
|
+
},
|
|
403
464
|
});
|
|
404
465
|
});
|
|
405
466
|
|
|
@@ -2,13 +2,17 @@ import { useEffect, useId, useMemo, useRef } from 'react';
|
|
|
2
2
|
import type { ChatPanelAppCardViewModel } from '@nextclaw/agent-chat-ui';
|
|
3
3
|
import { AppWindow, Maximize2 } from 'lucide-react';
|
|
4
4
|
import { getPresenter } from '@/app/presenters/app.presenter';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
PANEL_APP_IFRAME_SANDBOX,
|
|
7
|
+
findPanelAppEntryByDisplayId,
|
|
8
|
+
focusPanelAppIframe,
|
|
9
|
+
usePanelApps,
|
|
10
|
+
} from '@/features/panel-apps';
|
|
6
11
|
import { createPanelAppRightPanelResourceTarget } from '@/features/right-panel-resources';
|
|
7
12
|
import {
|
|
8
13
|
createInlinePanelAppCardUrl,
|
|
9
14
|
createFallbackPanelAppContentPath,
|
|
10
15
|
createInlinePanelAppTab,
|
|
11
|
-
PANEL_APP_INLINE_IFRAME_SANDBOX,
|
|
12
16
|
} from '@/features/chat/features/message/utils/chat-inline-panel-app-card.utils';
|
|
13
17
|
import { useDocBrowser } from '@/shared/components/doc-browser';
|
|
14
18
|
import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
|
|
@@ -98,7 +102,7 @@ export function ChatInlinePanelAppCard({
|
|
|
98
102
|
</div>
|
|
99
103
|
{showExpandAction ? (
|
|
100
104
|
<IconActionButton
|
|
101
|
-
|
|
105
|
+
size="md"
|
|
102
106
|
icon={<Maximize2 className="h-3.5 w-3.5" />}
|
|
103
107
|
label={t('chatPanelCardExpand')}
|
|
104
108
|
onClick={openExpanded}
|
|
@@ -113,8 +117,9 @@ export function ChatInlinePanelAppCard({
|
|
|
113
117
|
key={iframeInstanceId}
|
|
114
118
|
src={cardUrl}
|
|
115
119
|
title={title}
|
|
116
|
-
sandbox={
|
|
117
|
-
|
|
120
|
+
sandbox={PANEL_APP_IFRAME_SANDBOX}
|
|
121
|
+
tabIndex={0}
|
|
122
|
+
onPointerOver={() => focusPanelAppIframe(iframeRef.current)}
|
|
118
123
|
className="h-full w-full border-0 bg-card"
|
|
119
124
|
/>
|
|
120
125
|
</div>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
2
|
import type { NcpMessage } from "@nextclaw/ncp";
|
|
3
3
|
import {
|
|
4
4
|
type ChatInlineDisplayViewModel,
|
|
5
|
+
type ChatInlineTokenViewModel,
|
|
5
6
|
type ChatMessageViewModel,
|
|
6
7
|
ChatMessageList,
|
|
7
8
|
} from "@nextclaw/agent-chat-ui";
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
} from "@/features/chat/features/session/utils/ncp-session-context-metadata.utils";
|
|
24
25
|
import { AgentIdentityAvatar } from "@/shared/components/common/agent-identity";
|
|
25
26
|
import { ChatInlinePanelAppCard } from "@/features/chat/features/message/components/chat-inline-panel-app-card";
|
|
27
|
+
import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
|
|
26
28
|
import { useI18n } from "@/app/components/i18n-provider";
|
|
27
29
|
import { formatDateTime, t } from "@/shared/lib/i18n";
|
|
28
30
|
|
|
@@ -342,6 +344,48 @@ export function ChatMessageListContainer({
|
|
|
342
344
|
() => buildTimelineItems({ rawMessages, messages }),
|
|
343
345
|
[messages, rawMessages],
|
|
344
346
|
);
|
|
347
|
+
const sessionSkillsQuery = useChatQueryStore((state) => state.snapshot.sessionSkillsQuery);
|
|
348
|
+
const handleInlineTokenClick = useCallback(
|
|
349
|
+
(token: ChatInlineTokenViewModel) => {
|
|
350
|
+
if (token.kind !== "skill") {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const skillKey = token.key.trim();
|
|
354
|
+
if (!skillKey) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const records = sessionSkillsQuery?.data?.records ?? [];
|
|
358
|
+
const matched = records.find((record) => record.ref === skillKey || record.name === skillKey);
|
|
359
|
+
const skillPath = matched?.path?.trim();
|
|
360
|
+
if (!skillPath) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
presenter.chatThreadManager.openFilePreview({
|
|
364
|
+
path: skillPath,
|
|
365
|
+
label: token.label || matched?.name || skillKey,
|
|
366
|
+
viewMode: "preview",
|
|
367
|
+
previewViewer: "rendered",
|
|
368
|
+
});
|
|
369
|
+
},
|
|
370
|
+
[presenter.chatThreadManager, sessionSkillsQuery],
|
|
371
|
+
);
|
|
372
|
+
const handleAttachmentOpen = useCallback(
|
|
373
|
+
(file: { label: string; mimeType: string; dataUrl?: string; sizeBytes?: number; isImage: boolean }) => {
|
|
374
|
+
const contentUrl = file.dataUrl?.trim();
|
|
375
|
+
if (!contentUrl) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const label = file.label.trim() || "attachment";
|
|
379
|
+
presenter.chatThreadManager.openFilePreview({
|
|
380
|
+
path: label,
|
|
381
|
+
label,
|
|
382
|
+
viewMode: "preview",
|
|
383
|
+
contentUrl,
|
|
384
|
+
mimeType: file.mimeType,
|
|
385
|
+
});
|
|
386
|
+
},
|
|
387
|
+
[presenter.chatThreadManager],
|
|
388
|
+
);
|
|
345
389
|
return (
|
|
346
390
|
<div className={className}>
|
|
347
391
|
{timelineItems.map((item, index) =>
|
|
@@ -358,6 +402,8 @@ export function ChatMessageListContainer({
|
|
|
358
402
|
texts={messageTexts}
|
|
359
403
|
onToolAction={presenter.chatThreadManager.handleToolAction}
|
|
360
404
|
onFileOpen={presenter.chatThreadManager.openFilePreview}
|
|
405
|
+
onAttachmentOpen={handleAttachmentOpen}
|
|
406
|
+
onInlineTokenClick={handleInlineTokenClick}
|
|
361
407
|
renderInlineDisplay={renderChatInlineDisplay}
|
|
362
408
|
renderToolAgent={(agentId) => (
|
|
363
409
|
<AgentIdentityAvatar
|
|
@@ -18,7 +18,7 @@ const baseAssistantMessage = {
|
|
|
18
18
|
toolCallId: "tool-1",
|
|
19
19
|
toolName: "exec_command",
|
|
20
20
|
state: "result",
|
|
21
|
-
args:
|
|
21
|
+
args: '{"cmd":"git status"}',
|
|
22
22
|
result: "clean",
|
|
23
23
|
},
|
|
24
24
|
{
|
|
@@ -56,4 +56,14 @@ describe("buildChatMessageProcessSummary", () => {
|
|
|
56
56
|
label: "Processed 3m 51s",
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
|
+
|
|
60
|
+
it("stays free of tool-activity semantics", () => {
|
|
61
|
+
const summary = buildChatMessageProcessSummary({
|
|
62
|
+
message: baseAssistantMessage,
|
|
63
|
+
processedLabel: "Processed",
|
|
64
|
+
});
|
|
65
|
+
expect(summary?.label).toBe("Processed");
|
|
66
|
+
expect(summary?.label.toLowerCase()).not.toContain("bash");
|
|
67
|
+
expect(summary?.label.toLowerCase()).not.toContain("read");
|
|
68
|
+
});
|
|
59
69
|
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expect, it, vi } from "vitest";
|
|
2
|
+
import { buildChatMessageTexts } from "@/features/chat/features/message/utils/chat-message-texts.utils";
|
|
3
|
+
|
|
4
|
+
vi.mock("@/shared/lib/i18n", () => ({
|
|
5
|
+
t: (key: string) => key,
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
it("provides localized status keys for built-in tool categories", () => {
|
|
9
|
+
expect(buildChatMessageTexts("zh").toolStatusLabels?.builtIn).toMatchObject({
|
|
10
|
+
directory: {
|
|
11
|
+
running: "chatToolDirectoryRunning",
|
|
12
|
+
success: "chatToolDirectorySuccess",
|
|
13
|
+
error: "chatToolDirectoryError",
|
|
14
|
+
cancelled: "chatToolDirectoryCancelled",
|
|
15
|
+
},
|
|
16
|
+
memory: {
|
|
17
|
+
running: "chatToolMemoryRunning",
|
|
18
|
+
success: "chatToolMemorySuccess",
|
|
19
|
+
error: "chatToolMemoryError",
|
|
20
|
+
cancelled: "chatToolMemoryCancelled",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -4,17 +4,6 @@ import {
|
|
|
4
4
|
} from '@/features/right-panel-resources';
|
|
5
5
|
import type { DocBrowserTab } from '@/shared/components/doc-browser/doc-browser-context';
|
|
6
6
|
|
|
7
|
-
export const PANEL_APP_INLINE_IFRAME_SANDBOX = [
|
|
8
|
-
'allow-scripts',
|
|
9
|
-
'allow-forms',
|
|
10
|
-
'allow-modals',
|
|
11
|
-
'allow-popups',
|
|
12
|
-
'allow-popups-to-escape-sandbox',
|
|
13
|
-
'allow-downloads',
|
|
14
|
-
'allow-pointer-lock',
|
|
15
|
-
'allow-presentation',
|
|
16
|
-
].join(' ');
|
|
17
|
-
|
|
18
7
|
const PANEL_APP_INLINE_CARD_SEARCH_PARAMS = {
|
|
19
8
|
nextclawDisplayMode: 'card',
|
|
20
9
|
nextclawPlacement: 'inline',
|
|
@@ -68,6 +68,11 @@ function formatLifecycleDuration(message: NcpMessage): string | null {
|
|
|
68
68
|
return `${seconds}s`;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Outer process collapse only.
|
|
73
|
+
* Tool activity grouping is a separate layer over consecutive tool-cards
|
|
74
|
+
* and must not be mixed into this summary.
|
|
75
|
+
*/
|
|
71
76
|
export function buildChatMessageProcessSummary({
|
|
72
77
|
message,
|
|
73
78
|
processedLabel,
|