@nextclaw/ui 0.15.0 → 0.15.2
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 +50 -0
- package/dist/assets/{api-DLtsiBKm.js → api-B7Qigvtm.js} +2 -2
- package/dist/assets/{appearance-settings-page-CtkeYXux.js → appearance-settings-page-CuE0Iiqj.js} +1 -1
- package/dist/assets/{book-open-BQwWBDKb.js → book-open-Ba2f7YiG.js} +1 -1
- package/dist/assets/channels-list-page-CTsM6TcP.js +8 -0
- package/dist/assets/chat-page-C_5dcuE3.js +1 -0
- package/dist/assets/config-split-page-Fbb4PtTf.js +1 -0
- package/dist/assets/{confirm-dialog-iIJFFp2T.js → confirm-dialog-D2EG3nqE.js} +1 -1
- package/dist/assets/desktop-update-config-BGR3HUi1.js +1 -0
- package/dist/assets/{dist-D2eZC3OR.js → dist-7JTjycxE.js} +1 -1
- package/dist/assets/{dist-CBxWOazz.js → dist-wXLhk4hl.js} +1 -1
- package/dist/assets/doc-browser-bHyXnxlj.js +1 -0
- package/dist/assets/doc-browser-context-CYAGm5d5.js +1 -0
- package/dist/assets/doc-browser-gTrQ3jCF.js +1 -0
- package/dist/assets/{es2015-DhQo_oS6.js → es2015-j1SRCIX5.js} +1 -1
- package/dist/assets/{external-link-CM5dnUI6.js → external-link-DxuPaDoK.js} +1 -1
- package/dist/assets/form-actions-BztL7P_T.js +1 -0
- package/dist/assets/{i18n-provider-di1waTGX.js → i18n-provider-B4oMxOFx.js} +1 -1
- package/dist/assets/index-CvrvblgX.css +1 -0
- package/dist/assets/index-Obd4JYU2.js +104 -0
- package/dist/assets/loader-circle-CdzgK4eH.js +1 -0
- package/dist/assets/{mcp-marketplace-page-C_MVQUbT.js → mcp-marketplace-page-Bt7CuOoq.js} +2 -2
- package/dist/assets/mcp-marketplace-page-Ck3t78_n.js +1 -0
- package/dist/assets/model-config-page-DeKk9gvE.js +1 -0
- package/dist/assets/plus-Bx7wQbhG.js +1 -0
- package/dist/assets/{provider-scoped-model-input-g1WkGiJ8.js → provider-scoped-model-input-DsH2LF1q.js} +1 -1
- package/dist/assets/providers-config-page-BFnGp68F.js +1 -0
- package/dist/assets/{react-DjpzoZXB.js → react-UirzUuST.js} +1 -1
- package/dist/assets/{refresh-cw-DaTv3zxE.js → refresh-cw-eT8BF-8B.js} +1 -1
- package/dist/assets/remote-CDkHZtA8.js +1 -0
- package/dist/assets/rotate-cw-1JqxHTuw.js +1 -0
- package/dist/assets/runtime-config-page-BelAqiNG.js +1 -0
- package/dist/assets/{save-BZof1uie.js → save-Cb2g0BOi.js} +1 -1
- package/dist/assets/{search-MxAarS_g.js → search-BcHjwdsb.js} +1 -1
- package/dist/assets/search-config-page-zPm6--Ig.js +1 -0
- package/dist/assets/secrets-config-page-Cxyp-KuQ.js +3 -0
- package/dist/assets/security-config-B_gCq9fn.js +1 -0
- package/dist/assets/{select-D7GcNP0z.js → select-CdSEOwVM.js} +1 -1
- package/dist/assets/{setting-row-CuozlnJd.js → setting-row-CTAtGexr.js} +1 -1
- package/dist/assets/settings-2-2DWauOjY.js +1 -0
- package/dist/assets/{side-dock-BcLau5S_.js → side-dock-DPvakJ0a.js} +2 -2
- package/dist/assets/{skeleton-shfmXuYp.js → skeleton-P5oM14E1.js} +1 -1
- package/dist/assets/{switch-C1BWMU5c.js → switch-BU_UEULb.js} +1 -1
- package/dist/assets/{tag-chip-DCUrQUCP.js → tag-chip-D-WsKoT1.js} +1 -1
- package/dist/assets/{tooltip-DfAHHA76.js → tooltip-CjkOaHW7.js} +1 -1
- package/dist/assets/{use-config-DAdbT5QJ.js → use-config--hx5oxQn.js} +1 -1
- package/dist/assets/{use-confirm-dialog-eWcJ6V-s.js → use-confirm-dialog-CD9a_SK1.js} +1 -1
- package/dist/assets/x-DT5x45JU.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 +43 -4
- package/src/app/components/layout/sidebar.tsx +83 -61
- package/src/app/configs/app-navigation.config.ts +49 -0
- package/src/app/styles/design-system.css +82 -0
- package/src/features/agents/components/__tests__/agents-page.test.tsx +135 -2
- package/src/features/agents/components/agent-advanced-config-fields.tsx +70 -0
- package/src/features/agents/components/agent-details-dialog.tsx +398 -0
- package/src/features/agents/components/agent-dialogs.tsx +20 -47
- package/src/features/agents/components/agents-page.tsx +76 -10
- package/src/features/agents/types/agent-form.types.ts +21 -0
- package/src/features/agents/utils/agent-form.utils.ts +39 -0
- package/src/features/channels/components/config/channel-form.tsx +14 -11
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +172 -1
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +11 -1
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +7 -3
- 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 +29 -30
- package/src/features/chat/components/layout/chat-sidebar.tsx +21 -7
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +18 -3
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +2 -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 +1 -1
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +10 -5
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +0 -11
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +23 -1
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-list-view.test.tsx +74 -0
- package/src/features/chat/features/ncp/hooks/use-ncp-session-list-view.ts +3 -2
- 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 +294 -0
- package/src/features/chat/features/session/utils/__tests__/chat-session-display.utils.test.ts +15 -1
- package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +24 -4
- package/src/features/chat/features/session/utils/chat-session-display.utils.ts +3 -0
- package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +25 -3
- package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +1 -0
- package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +9 -4
- 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 +193 -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-breadcrumbs.tsx +3 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +67 -14
- 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/__tests__/chat-workspace-panel-view-model.utils.test.ts +46 -0
- package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +33 -2
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +47 -0
- package/src/features/chat/managers/chat-session-list.manager.ts +32 -0
- package/src/features/chat/managers/chat-thread.manager.ts +8 -2
- package/src/features/chat/stores/chat-session-list.store.ts +28 -5
- package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +5 -12
- package/src/features/panel-apps/components/panel-app-list-item.tsx +6 -6
- 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/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/system-status/components/__tests__/desktop-update-config.test.tsx +97 -5
- package/src/features/system-status/components/config/runtime-config-editor.tsx +5 -4
- package/src/features/system-status/components/desktop-update-config.tsx +91 -2
- package/src/features/system-status/utils/update-release-notes.utils.ts +94 -0
- package/src/index.css +18 -0
- 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 +5 -1
- 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/card.tsx +12 -4
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +2 -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/ncp-session.types.ts +1 -1
- package/src/shared/lib/api/types.ts +12 -3
- package/src/shared/lib/api/utils/server-path.utils.ts +1 -0
- package/src/shared/lib/i18n/locales/en-US/agents.json +41 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +19 -0
- package/src/shared/lib/i18n/locales/en-US/core.json +3 -0
- package/src/shared/lib/i18n/locales/en-US/desktop-update.json +8 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +5 -0
- package/src/shared/lib/i18n/locales/zh-CN/agents.json +41 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +19 -0
- package/src/shared/lib/i18n/locales/zh-CN/core.json +3 -0
- package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +8 -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-Cv8o9Hxg.js +0 -8
- package/dist/assets/chat-page-BtA7H1Ea.js +0 -1
- package/dist/assets/config-split-page-EidRBpP2.js +0 -1
- package/dist/assets/desktop-update-config-C6n_TB1r.js +0 -1
- package/dist/assets/doc-browser-B76XOXmW.js +0 -1
- package/dist/assets/doc-browser-DBnj9UMB.js +0 -1
- package/dist/assets/doc-browser-context-Sv-044Cl.js +0 -1
- package/dist/assets/ellipsis-2uzhypw5.js +0 -1
- package/dist/assets/index-CBRP-7-a.css +0 -1
- package/dist/assets/index-Z1fYpbNd.js +0 -104
- package/dist/assets/loader-circle-BjmN5DeJ.js +0 -1
- package/dist/assets/mcp-marketplace-page-CoZIiD6G.js +0 -1
- package/dist/assets/model-config-page-Cdr9P6TQ.js +0 -1
- package/dist/assets/plus-3ztRZD_l.js +0 -1
- package/dist/assets/providers-config-page-CWlO7J4A.js +0 -1
- package/dist/assets/remote-CKWZfVVB.js +0 -1
- package/dist/assets/rotate-cw-CHcaRUDz.js +0 -1
- package/dist/assets/runtime-config-page-CfTqh1nv.js +0 -1
- package/dist/assets/search-config-page-Dm8daYny.js +0 -1
- package/dist/assets/secrets-config-page-D8AbcR05.js +0 -3
- package/dist/assets/security-config-Ds0LsJmo.js +0 -1
- package/dist/assets/x-DqL1mYLC.js +0 -1
- /package/dist/assets/{config-hints-fGnUjDe9.js → config-hints-C_iP1zl_.js} +0 -0
|
@@ -7,7 +7,7 @@ import { type SessionContextView } from '@/features/chat/features/session/utils/
|
|
|
7
7
|
import type { SessionRunStatus } from '@/features/chat/types/session-run-status.types';
|
|
8
8
|
import { cn } from '@/shared/lib/utils';
|
|
9
9
|
import { t } from '@/shared/lib/i18n';
|
|
10
|
-
import { Check, GitBranch, Pencil, X } from 'lucide-react';
|
|
10
|
+
import { Check, GitBranch, Pencil, Pin, X } from 'lucide-react';
|
|
11
11
|
|
|
12
12
|
type ChatSidebarSessionItemProps = {
|
|
13
13
|
sessionKey: string;
|
|
@@ -15,6 +15,7 @@ type ChatSidebarSessionItemProps = {
|
|
|
15
15
|
showUnreadDot: boolean;
|
|
16
16
|
runStatus?: SessionRunStatus;
|
|
17
17
|
context: SessionContextView;
|
|
18
|
+
isPinned: boolean;
|
|
18
19
|
title: string;
|
|
19
20
|
previewText: string;
|
|
20
21
|
trailingText: string;
|
|
@@ -31,6 +32,7 @@ type ChatSidebarSessionItemProps = {
|
|
|
31
32
|
onDraftLabelChange: (value: string) => void;
|
|
32
33
|
onSave: () => void | Promise<void>;
|
|
33
34
|
onCancel: () => void;
|
|
35
|
+
onTogglePinned: () => void;
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
type ChatSidebarSessionEditingViewProps = Pick<
|
|
@@ -72,7 +74,6 @@ function ChatSidebarSessionEditingView({
|
|
|
72
74
|
icon={<Check className="h-3.5 w-3.5" />}
|
|
73
75
|
label={t('save')}
|
|
74
76
|
tooltip={false}
|
|
75
|
-
className="h-7 w-7 rounded-lg text-gray-500 hover:bg-white hover:text-gray-900"
|
|
76
77
|
onClick={() => void onSave()}
|
|
77
78
|
disabled={isSaving}
|
|
78
79
|
/>
|
|
@@ -80,7 +81,6 @@ function ChatSidebarSessionEditingView({
|
|
|
80
81
|
icon={<X className="h-3.5 w-3.5" />}
|
|
81
82
|
label={t('cancel')}
|
|
82
83
|
tooltip={false}
|
|
83
|
-
className="h-7 w-7 rounded-lg text-gray-500 hover:bg-white hover:text-gray-900"
|
|
84
84
|
onClick={onCancel}
|
|
85
85
|
disabled={isSaving}
|
|
86
86
|
/>
|
|
@@ -100,6 +100,7 @@ function ChatSidebarSessionDisplayView({
|
|
|
100
100
|
showUnreadDot,
|
|
101
101
|
runStatus,
|
|
102
102
|
context,
|
|
103
|
+
isPinned,
|
|
103
104
|
title,
|
|
104
105
|
previewText,
|
|
105
106
|
trailingText,
|
|
@@ -109,14 +110,15 @@ function ChatSidebarSessionDisplayView({
|
|
|
109
110
|
childSessionCount = 0,
|
|
110
111
|
onSelect,
|
|
111
112
|
onOpenChildSessions,
|
|
112
|
-
onStartEditing
|
|
113
|
+
onStartEditing,
|
|
114
|
+
onTogglePinned
|
|
113
115
|
}: ChatSidebarSessionDisplayViewProps) {
|
|
114
|
-
const trailingControlsClassName = childSessionCount > 0 && onOpenChildSessions ? 'pr-
|
|
116
|
+
const trailingControlsClassName = childSessionCount > 0 && onOpenChildSessions ? 'pr-20' : 'pr-12';
|
|
115
117
|
|
|
116
118
|
return (
|
|
117
119
|
<div className="group/session relative">
|
|
118
120
|
<button type="button" onClick={onSelect} className="w-full text-left">
|
|
119
|
-
<div className={cn('flex min-w-0 items-
|
|
121
|
+
<div className={cn('flex min-h-6 min-w-0 items-center', trailingControlsClassName)}>
|
|
120
122
|
<span className="flex min-w-0 items-center gap-1.5">
|
|
121
123
|
{agentId?.trim() && agentId.trim().toLowerCase() !== 'main' ? (
|
|
122
124
|
<AgentAvatar
|
|
@@ -126,7 +128,7 @@ function ChatSidebarSessionDisplayView({
|
|
|
126
128
|
className="h-5 w-5 shrink-0"
|
|
127
129
|
/>
|
|
128
130
|
) : null}
|
|
129
|
-
<span className="truncate font-medium">{title}</span>
|
|
131
|
+
<span className="truncate leading-6 font-medium">{title}</span>
|
|
130
132
|
{context.label ? (
|
|
131
133
|
<span
|
|
132
134
|
className={cn(
|
|
@@ -140,7 +142,7 @@ function ChatSidebarSessionDisplayView({
|
|
|
140
142
|
</span>
|
|
141
143
|
) : null}
|
|
142
144
|
{context.icon ? (
|
|
143
|
-
<span className="inline-flex h-
|
|
145
|
+
<span className="inline-flex h-5 w-5 shrink-0 items-center justify-center">
|
|
144
146
|
<SessionContextIconNode icon={context.icon} className={active ? 'text-gray-700' : 'text-gray-500'} />
|
|
145
147
|
</span>
|
|
146
148
|
) : null}
|
|
@@ -168,7 +170,7 @@ function ChatSidebarSessionDisplayView({
|
|
|
168
170
|
onOpenChildSessions();
|
|
169
171
|
}}
|
|
170
172
|
className={cn(
|
|
171
|
-
'absolute right-
|
|
173
|
+
'absolute right-12 top-0 inline-flex h-6 items-center gap-1 rounded-md px-1.5 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-black/10 hover:text-foreground',
|
|
172
174
|
active
|
|
173
175
|
? 'opacity-100'
|
|
174
176
|
: 'opacity-0 group-hover/session:opacity-100 group-focus-within/session:opacity-100'
|
|
@@ -181,23 +183,41 @@ function ChatSidebarSessionDisplayView({
|
|
|
181
183
|
</button>
|
|
182
184
|
) : null}
|
|
183
185
|
{runStatus ? (
|
|
184
|
-
<span className="absolute right-0 top-0 inline-flex h-
|
|
186
|
+
<span className="absolute right-0 top-0 inline-flex h-6 w-6 items-center justify-center transition-opacity group-hover/session:opacity-0 group-focus-within/session:opacity-0">
|
|
185
187
|
<SessionRunBadge status={runStatus} />
|
|
186
188
|
</span>
|
|
187
189
|
) : null}
|
|
188
|
-
<
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
190
|
+
<div className="absolute right-0 top-0 flex h-6 items-center gap-0.5 opacity-0 transition-opacity group-hover/session:opacity-100 group-focus-within/session:opacity-100">
|
|
191
|
+
<IconActionButton
|
|
192
|
+
size="sm"
|
|
193
|
+
tone="strong"
|
|
194
|
+
icon={
|
|
195
|
+
<Pin
|
|
196
|
+
className={cn(
|
|
197
|
+
'h-3.5 w-3.5',
|
|
198
|
+
isPinned && 'fill-current text-foreground',
|
|
199
|
+
)}
|
|
200
|
+
/>
|
|
201
|
+
}
|
|
202
|
+
label={t(isPinned ? 'chatSidebarUnpinSession' : 'chatSidebarPinSession')}
|
|
203
|
+
tooltipSide="right"
|
|
204
|
+
onClick={(event) => {
|
|
205
|
+
event.stopPropagation();
|
|
206
|
+
onTogglePinned();
|
|
207
|
+
}}
|
|
208
|
+
/>
|
|
209
|
+
<IconActionButton
|
|
210
|
+
size="sm"
|
|
211
|
+
tone="strong"
|
|
212
|
+
icon={<Pencil className="h-3.5 w-3.5" />}
|
|
213
|
+
label={t('edit')}
|
|
214
|
+
tooltipSide="right"
|
|
215
|
+
onClick={(event) => {
|
|
216
|
+
event.stopPropagation();
|
|
217
|
+
onStartEditing();
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
201
221
|
</div>
|
|
202
222
|
);
|
|
203
223
|
}
|
|
@@ -208,6 +228,7 @@ export function ChatSidebarSessionItem({
|
|
|
208
228
|
showUnreadDot,
|
|
209
229
|
runStatus,
|
|
210
230
|
context,
|
|
231
|
+
isPinned,
|
|
211
232
|
title,
|
|
212
233
|
previewText,
|
|
213
234
|
trailingText,
|
|
@@ -223,7 +244,8 @@ export function ChatSidebarSessionItem({
|
|
|
223
244
|
onStartEditing,
|
|
224
245
|
onDraftLabelChange,
|
|
225
246
|
onSave,
|
|
226
|
-
onCancel
|
|
247
|
+
onCancel,
|
|
248
|
+
onTogglePinned
|
|
227
249
|
}: ChatSidebarSessionItemProps) {
|
|
228
250
|
return (
|
|
229
251
|
<div
|
|
@@ -249,6 +271,7 @@ export function ChatSidebarSessionItem({
|
|
|
249
271
|
showUnreadDot={showUnreadDot}
|
|
250
272
|
runStatus={runStatus}
|
|
251
273
|
context={context}
|
|
274
|
+
isPinned={isPinned}
|
|
252
275
|
title={title}
|
|
253
276
|
previewText={previewText}
|
|
254
277
|
trailingText={trailingText}
|
|
@@ -259,6 +282,7 @@ export function ChatSidebarSessionItem({
|
|
|
259
282
|
childSessionCount={childSessionCount}
|
|
260
283
|
onOpenChildSessions={onOpenChildSessions}
|
|
261
284
|
onStartEditing={onStartEditing}
|
|
285
|
+
onTogglePinned={onTogglePinned}
|
|
262
286
|
/>
|
|
263
287
|
)}
|
|
264
288
|
</div>
|
|
@@ -19,7 +19,6 @@ type ChatSidebarSessionListProps = {
|
|
|
19
19
|
defaultSessionType: string;
|
|
20
20
|
sessionTypeOptions: SessionTypeOption[];
|
|
21
21
|
renderSessionItem: (item: NcpSessionListItemView) => ReactNode;
|
|
22
|
-
onCreateSession: (sessionType: string, projectRoot?: string | null) => void;
|
|
23
22
|
};
|
|
24
23
|
|
|
25
24
|
function ChatSidebarEmptyState({ label }: { label: string }) {
|
|
@@ -36,7 +35,6 @@ export function ChatSidebarSessionList({
|
|
|
36
35
|
groups,
|
|
37
36
|
isLoading,
|
|
38
37
|
isProjectFirstView,
|
|
39
|
-
onCreateSession,
|
|
40
38
|
projectGroups,
|
|
41
39
|
renderSessionItem,
|
|
42
40
|
sessionTypeOptions,
|
|
@@ -54,7 +52,6 @@ export function ChatSidebarSessionList({
|
|
|
54
52
|
defaultSessionType={defaultSessionType}
|
|
55
53
|
sessionTypeOptions={sessionTypeOptions}
|
|
56
54
|
renderSessionItem={renderSessionItem}
|
|
57
|
-
onCreateSession={onCreateSession}
|
|
58
55
|
/>
|
|
59
56
|
);
|
|
60
57
|
}
|
package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx
CHANGED
|
@@ -9,7 +9,6 @@ import { ChatSessionProjectDialog } from './chat-session-project-dialog';
|
|
|
9
9
|
import { t } from '@/shared/lib/i18n';
|
|
10
10
|
|
|
11
11
|
const SESSION_HEADER_ACTION_GROUP_CLASS = 'flex shrink-0 items-center gap-1.5';
|
|
12
|
-
const SESSION_HEADER_ACTION_BUTTON_CLASS = 'h-7 w-7 shrink-0 rounded-lg text-muted-foreground/70 hover:bg-accent hover:text-accent-foreground';
|
|
13
12
|
|
|
14
13
|
type ChatSessionHeaderActionsProps = {
|
|
15
14
|
sessionKey: string;
|
|
@@ -65,7 +64,6 @@ export function ChatSessionHeaderActions({
|
|
|
65
64
|
<IconActionButton
|
|
66
65
|
icon={<GitBranch className="h-4 w-4" />}
|
|
67
66
|
label={t('chatSessionOpenChildSessions')}
|
|
68
|
-
className={SESSION_HEADER_ACTION_BUTTON_CLASS}
|
|
69
67
|
onClick={onOpenChildSessions}
|
|
70
68
|
disabled={isBusy}
|
|
71
69
|
/>
|
|
@@ -74,7 +72,6 @@ export function ChatSessionHeaderActions({
|
|
|
74
72
|
<IconActionButton
|
|
75
73
|
icon={<AlarmClock className="h-4 w-4" />}
|
|
76
74
|
label={t('chatSessionOpenCronJobs')}
|
|
77
|
-
className={SESSION_HEADER_ACTION_BUTTON_CLASS}
|
|
78
75
|
onClick={onOpenSessionCronJobs}
|
|
79
76
|
disabled={isBusy}
|
|
80
77
|
/>
|
|
@@ -85,7 +82,6 @@ export function ChatSessionHeaderActions({
|
|
|
85
82
|
icon={<MoreVertical className="h-4 w-4" />}
|
|
86
83
|
label={t('chatSessionMoreActions')}
|
|
87
84
|
tooltip={false}
|
|
88
|
-
className={SESSION_HEADER_ACTION_BUTTON_CLASS}
|
|
89
85
|
disabled={isBusy}
|
|
90
86
|
/>
|
|
91
87
|
</PopoverTrigger>
|
package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Check,
|
|
4
|
+
ChevronDown,
|
|
5
|
+
Loader2,
|
|
6
|
+
MessageSquareText,
|
|
7
|
+
Search,
|
|
8
|
+
} from "lucide-react";
|
|
9
|
+
import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
|
|
10
|
+
import {
|
|
11
|
+
useNcpSessionListView,
|
|
12
|
+
type NcpSessionListItemView,
|
|
13
|
+
} from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
|
|
14
|
+
import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
|
|
15
|
+
import { SessionRunBadge } from "@/features/chat/features/session/components/session-run-badge";
|
|
16
|
+
import {
|
|
17
|
+
formatSessionListTime,
|
|
18
|
+
sessionActivityPreviewText,
|
|
19
|
+
sessionDisplayName,
|
|
20
|
+
} from "@/features/chat/features/session/utils/chat-session-display.utils";
|
|
21
|
+
import { sortSessionItemsByActivityAtDesc } from "@/features/chat/features/session/utils/chat-sidebar-session-groups.utils";
|
|
22
|
+
import { resolveSessionContextView } from "@/features/chat/features/session/utils/session-context.utils";
|
|
23
|
+
import {
|
|
24
|
+
buildSessionTypeOptions,
|
|
25
|
+
type ChatSessionTypeOption,
|
|
26
|
+
} from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
27
|
+
import {
|
|
28
|
+
shouldShowUnreadSessionIndicator,
|
|
29
|
+
useChatSessionListStore,
|
|
30
|
+
} from "@/features/chat/stores/chat-session-list.store";
|
|
31
|
+
import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
|
|
32
|
+
import { useViewportLayoutStore } from "@/app/stores/viewport-layout.store";
|
|
33
|
+
import {
|
|
34
|
+
Popover,
|
|
35
|
+
PopoverContent,
|
|
36
|
+
PopoverTrigger,
|
|
37
|
+
} from "@/shared/components/ui/popover";
|
|
38
|
+
import { Input } from "@/shared/components/ui/input";
|
|
39
|
+
import { t } from "@/shared/lib/i18n";
|
|
40
|
+
import { cn } from "@/shared/lib/utils";
|
|
41
|
+
|
|
42
|
+
const SWITCHER_TRIGGER_CLASS =
|
|
43
|
+
"group inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-md px-1.5 py-1 text-sm font-medium text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40";
|
|
44
|
+
|
|
45
|
+
function ChatSessionTitle({ title }: { title: string }) {
|
|
46
|
+
return (
|
|
47
|
+
<span className="text-sm font-medium text-foreground truncate">
|
|
48
|
+
{title}
|
|
49
|
+
</span>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function ChatSessionTitleSwitcherEmptyState({ label }: { label: string }) {
|
|
54
|
+
return (
|
|
55
|
+
<div className="flex items-center gap-2 px-3 py-4 text-xs text-muted-foreground">
|
|
56
|
+
<MessageSquareText className="h-4 w-4 shrink-0 text-muted-foreground/55" />
|
|
57
|
+
<span>{label}</span>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function ChatSessionTitleSwitcherLoadingState() {
|
|
63
|
+
return (
|
|
64
|
+
<div className="flex items-center gap-2 px-3 py-4 text-xs text-muted-foreground">
|
|
65
|
+
<Loader2 className="h-4 w-4 shrink-0 animate-spin" />
|
|
66
|
+
<span>{t("sessionsLoading")}</span>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function ChatSessionSwitchItem({
|
|
72
|
+
item,
|
|
73
|
+
selectedSessionKey,
|
|
74
|
+
optimisticReadAtBySessionKey,
|
|
75
|
+
sessionTypeOptions,
|
|
76
|
+
onSelect,
|
|
77
|
+
}: {
|
|
78
|
+
item: NcpSessionListItemView;
|
|
79
|
+
selectedSessionKey: string | null;
|
|
80
|
+
optimisticReadAtBySessionKey: Record<string, string>;
|
|
81
|
+
sessionTypeOptions: ChatSessionTypeOption[];
|
|
82
|
+
onSelect: (sessionKey: string) => void;
|
|
83
|
+
}) {
|
|
84
|
+
const { session, runStatus } = item;
|
|
85
|
+
const active = selectedSessionKey === session.key;
|
|
86
|
+
const optimisticReadAt = optimisticReadAtBySessionKey[session.key];
|
|
87
|
+
const effectiveReadAt =
|
|
88
|
+
optimisticReadAt && session.readAt
|
|
89
|
+
? optimisticReadAt.localeCompare(session.readAt) > 0
|
|
90
|
+
? optimisticReadAt
|
|
91
|
+
: session.readAt
|
|
92
|
+
: (optimisticReadAt ?? session.readAt);
|
|
93
|
+
const shouldShowUnread = shouldShowUnreadSessionIndicator({
|
|
94
|
+
active,
|
|
95
|
+
lastMessageAt: session.lastMessageAt,
|
|
96
|
+
readAt: effectiveReadAt,
|
|
97
|
+
runStatus,
|
|
98
|
+
});
|
|
99
|
+
const previewText =
|
|
100
|
+
sessionActivityPreviewText(session) ??
|
|
101
|
+
session.projectName ??
|
|
102
|
+
session.projectRoot ??
|
|
103
|
+
`${session.messageCount}`;
|
|
104
|
+
const trailingText = formatSessionListTime(
|
|
105
|
+
session.lastMessageAt ?? session.createdAt,
|
|
106
|
+
);
|
|
107
|
+
const sessionContext = resolveSessionContextView(session, sessionTypeOptions);
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
aria-current={active ? "page" : undefined}
|
|
113
|
+
className={cn(
|
|
114
|
+
"flex w-full min-w-0 items-start gap-2 rounded-lg px-2.5 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40",
|
|
115
|
+
active
|
|
116
|
+
? "bg-accent text-accent-foreground"
|
|
117
|
+
: "text-popover-foreground hover:bg-accent/70",
|
|
118
|
+
)}
|
|
119
|
+
onClick={() => onSelect(session.key)}
|
|
120
|
+
>
|
|
121
|
+
<span className="mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center">
|
|
122
|
+
{active ? (
|
|
123
|
+
<Check className="h-3.5 w-3.5" />
|
|
124
|
+
) : shouldShowUnread ? (
|
|
125
|
+
<span
|
|
126
|
+
aria-label={t("chatSessionUnread")}
|
|
127
|
+
className="h-2 w-2 rounded-full bg-primary"
|
|
128
|
+
/>
|
|
129
|
+
) : null}
|
|
130
|
+
</span>
|
|
131
|
+
<span className="min-w-0 flex-1">
|
|
132
|
+
<span className="flex min-w-0 items-center gap-1.5">
|
|
133
|
+
<span className="min-w-0 truncate text-[13px] font-medium">
|
|
134
|
+
{sessionDisplayName(session)}
|
|
135
|
+
</span>
|
|
136
|
+
{sessionContext.label ? (
|
|
137
|
+
<span className="shrink-0 rounded-full border border-border/70 px-1.5 py-0.5 text-[10px] font-semibold leading-none text-muted-foreground">
|
|
138
|
+
{sessionContext.label}
|
|
139
|
+
</span>
|
|
140
|
+
) : null}
|
|
141
|
+
{sessionContext.icon ? (
|
|
142
|
+
<span className="inline-flex h-[1.125rem] w-[1.125rem] shrink-0 items-center justify-center text-muted-foreground">
|
|
143
|
+
<SessionContextIconNode icon={sessionContext.icon} />
|
|
144
|
+
</span>
|
|
145
|
+
) : null}
|
|
146
|
+
</span>
|
|
147
|
+
<span className="mt-0.5 flex min-w-0 items-center gap-2 text-[11px] text-muted-foreground">
|
|
148
|
+
<span className="min-w-0 truncate">{previewText}</span>
|
|
149
|
+
<span className="shrink-0">{trailingText}</span>
|
|
150
|
+
</span>
|
|
151
|
+
</span>
|
|
152
|
+
{runStatus ? (
|
|
153
|
+
<span className="mt-0.5 shrink-0">
|
|
154
|
+
<SessionRunBadge status={runStatus} />
|
|
155
|
+
</span>
|
|
156
|
+
) : null}
|
|
157
|
+
</button>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function ChatSessionTitleSwitcherPopover({
|
|
162
|
+
selectedSessionKey,
|
|
163
|
+
title,
|
|
164
|
+
}: {
|
|
165
|
+
selectedSessionKey: string | null;
|
|
166
|
+
title: string;
|
|
167
|
+
}) {
|
|
168
|
+
const presenter = usePresenter();
|
|
169
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
170
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
171
|
+
const optimisticReadAtBySessionKey = useChatSessionListStore(
|
|
172
|
+
(state) => state.optimisticReadAtBySessionKey,
|
|
173
|
+
);
|
|
174
|
+
const sessionTypesData = useChatQueryStore(
|
|
175
|
+
(state) => state.snapshot.sessionTypesQuery?.data ?? null,
|
|
176
|
+
);
|
|
177
|
+
const { isLoading, items } = useNcpSessionListView({ query: searchQuery });
|
|
178
|
+
const sortedItems = useMemo(
|
|
179
|
+
() => sortSessionItemsByActivityAtDesc(items),
|
|
180
|
+
[items],
|
|
181
|
+
);
|
|
182
|
+
const sessionTypeOptions = useMemo(
|
|
183
|
+
() => buildSessionTypeOptions(sessionTypesData?.options ?? []),
|
|
184
|
+
[sessionTypesData?.options],
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const selectSession = (sessionKey: string) => {
|
|
188
|
+
setIsOpen(false);
|
|
189
|
+
setSearchQuery("");
|
|
190
|
+
if (sessionKey === selectedSessionKey) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
presenter.chatSessionListManager.selectSession(sessionKey);
|
|
194
|
+
};
|
|
195
|
+
const updateOpen = (open: boolean) => {
|
|
196
|
+
setIsOpen(open);
|
|
197
|
+
if (!open) {
|
|
198
|
+
setSearchQuery("");
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<Popover open={isOpen} onOpenChange={updateOpen}>
|
|
204
|
+
<PopoverTrigger asChild>
|
|
205
|
+
<button
|
|
206
|
+
type="button"
|
|
207
|
+
aria-label={`${t("chatSessionSwitch")}: ${title}`}
|
|
208
|
+
aria-expanded={isOpen}
|
|
209
|
+
className={SWITCHER_TRIGGER_CLASS}
|
|
210
|
+
>
|
|
211
|
+
<span className="min-w-0 truncate">{title}</span>
|
|
212
|
+
<ChevronDown
|
|
213
|
+
className={cn(
|
|
214
|
+
"h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform group-hover:text-accent-foreground",
|
|
215
|
+
isOpen ? "rotate-180" : null,
|
|
216
|
+
)}
|
|
217
|
+
/>
|
|
218
|
+
</button>
|
|
219
|
+
</PopoverTrigger>
|
|
220
|
+
<PopoverContent
|
|
221
|
+
align="start"
|
|
222
|
+
className="w-[22rem] max-w-[calc(100vw-2rem)] p-0"
|
|
223
|
+
>
|
|
224
|
+
<div className="space-y-2 border-b border-border px-3 py-2">
|
|
225
|
+
<div className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground/75">
|
|
226
|
+
{t("chatSessionSwitcherTitle")}
|
|
227
|
+
</div>
|
|
228
|
+
<div className="relative">
|
|
229
|
+
<Search className="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/65" />
|
|
230
|
+
<Input
|
|
231
|
+
value={searchQuery}
|
|
232
|
+
onChange={(event) => setSearchQuery(event.target.value)}
|
|
233
|
+
placeholder={t("chatSearchSessionPlaceholder")}
|
|
234
|
+
aria-label={t("chatSearchSessionPlaceholder")}
|
|
235
|
+
className="h-8 rounded-lg bg-background pl-8 pr-2 text-xs"
|
|
236
|
+
autoFocus
|
|
237
|
+
/>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
<div className="max-h-80 overflow-y-auto p-1.5">
|
|
241
|
+
{isLoading ? (
|
|
242
|
+
<ChatSessionTitleSwitcherLoadingState />
|
|
243
|
+
) : sortedItems.length > 0 ? (
|
|
244
|
+
<div className="space-y-1">
|
|
245
|
+
{sortedItems.map((item) => (
|
|
246
|
+
<ChatSessionSwitchItem
|
|
247
|
+
key={item.session.key}
|
|
248
|
+
item={item}
|
|
249
|
+
selectedSessionKey={selectedSessionKey}
|
|
250
|
+
optimisticReadAtBySessionKey={optimisticReadAtBySessionKey}
|
|
251
|
+
sessionTypeOptions={sessionTypeOptions}
|
|
252
|
+
onSelect={selectSession}
|
|
253
|
+
/>
|
|
254
|
+
))}
|
|
255
|
+
</div>
|
|
256
|
+
) : (
|
|
257
|
+
<ChatSessionTitleSwitcherEmptyState
|
|
258
|
+
label={
|
|
259
|
+
searchQuery.trim()
|
|
260
|
+
? t("chatSessionSwitcherNoResults")
|
|
261
|
+
: t("sessionsEmpty")
|
|
262
|
+
}
|
|
263
|
+
/>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
</PopoverContent>
|
|
267
|
+
</Popover>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function ChatSessionTitleSwitcher({
|
|
272
|
+
layoutMode,
|
|
273
|
+
selectedSessionKey,
|
|
274
|
+
title,
|
|
275
|
+
}: {
|
|
276
|
+
layoutMode: "desktop" | "mobile";
|
|
277
|
+
selectedSessionKey: string | null;
|
|
278
|
+
title: string;
|
|
279
|
+
}) {
|
|
280
|
+
const isSidebarCollapsed = useViewportLayoutStore(
|
|
281
|
+
(state) => state.isSidebarCollapsed,
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
if (layoutMode !== "desktop" || !isSidebarCollapsed) {
|
|
285
|
+
return <ChatSessionTitle title={title} />;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
<ChatSessionTitleSwitcherPopover
|
|
290
|
+
selectedSessionKey={selectedSessionKey}
|
|
291
|
+
title={title}
|
|
292
|
+
/>
|
|
293
|
+
);
|
|
294
|
+
}
|
package/src/features/chat/features/session/utils/__tests__/chat-session-display.utils.test.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
sessionActivityPreviewText,
|
|
6
6
|
sessionDisplayName,
|
|
7
7
|
sessionMatchesQuery
|
|
8
|
-
} from '
|
|
8
|
+
} from '@/features/chat/features/session/utils/chat-session-display.utils';
|
|
9
9
|
|
|
10
10
|
function createSession(overrides: Partial<SessionEntryView> = {}): SessionEntryView {
|
|
11
11
|
return {
|
|
@@ -88,6 +88,20 @@ describe('chat-session-display', () => {
|
|
|
88
88
|
).toBe('最终回复内容');
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
it('does not show cancelled activity as ordinary session preview text', () => {
|
|
92
|
+
expect(
|
|
93
|
+
sessionActivityPreviewText(
|
|
94
|
+
createSession({
|
|
95
|
+
activityPreview: {
|
|
96
|
+
state: 'cancelled',
|
|
97
|
+
statusText: 'Run interrupted: User stopped the current run.',
|
|
98
|
+
timestamp: '2026-05-16T01:00:00.000Z'
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
|
|
91
105
|
it('formats today activity as time like WeChat session lists', () => {
|
|
92
106
|
expect(
|
|
93
107
|
formatSessionListTime(
|
package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts
CHANGED
|
@@ -105,8 +105,8 @@ describe('adaptNcpSessionSummary', () => {
|
|
|
105
105
|
metadata: {
|
|
106
106
|
last_activity_preview: {
|
|
107
107
|
state: 'completed',
|
|
108
|
-
replyText: '
|
|
109
|
-
statusText: '
|
|
108
|
+
replyText: 'Plan is ready',
|
|
109
|
+
statusText: 'Tool call completed',
|
|
110
110
|
timestamp: '2026-05-16T01:00:00.000Z',
|
|
111
111
|
},
|
|
112
112
|
},
|
|
@@ -115,8 +115,28 @@ describe('adaptNcpSessionSummary', () => {
|
|
|
115
115
|
|
|
116
116
|
expect(adapted.activityPreview).toEqual({
|
|
117
117
|
state: 'completed',
|
|
118
|
-
replyText: '
|
|
119
|
-
statusText: '
|
|
118
|
+
replyText: 'Plan is ready',
|
|
119
|
+
statusText: 'Tool call completed',
|
|
120
|
+
timestamp: '2026-05-16T01:00:00.000Z',
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('maps cancelled session activity preview metadata into the session entry', () => {
|
|
125
|
+
const adapted = adaptNcpSessionSummary(
|
|
126
|
+
createSummary({
|
|
127
|
+
metadata: {
|
|
128
|
+
last_activity_preview: {
|
|
129
|
+
state: 'cancelled',
|
|
130
|
+
statusText: 'Run interrupted: User stopped the current run.',
|
|
131
|
+
timestamp: '2026-05-16T01:00:00.000Z',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(adapted.activityPreview).toEqual({
|
|
138
|
+
state: 'cancelled',
|
|
139
|
+
statusText: 'Run interrupted: User stopped the current run.',
|
|
120
140
|
timestamp: '2026-05-16T01:00:00.000Z',
|
|
121
141
|
});
|
|
122
142
|
});
|
|
@@ -15,6 +15,9 @@ export function sessionActivityPreviewText(session: SessionEntryView): string |
|
|
|
15
15
|
if (!preview) {
|
|
16
16
|
return null;
|
|
17
17
|
}
|
|
18
|
+
if (preview.state === 'cancelled') {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
18
21
|
if (preview.state === 'failed' || preview.state === 'running') {
|
|
19
22
|
return preview.statusText ?? preview.replyText ?? null;
|
|
20
23
|
}
|