@nextclaw/ui 0.14.4 → 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 +54 -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
|
@@ -8,17 +8,20 @@ import {
|
|
|
8
8
|
createInlinePanelAppCardUrl,
|
|
9
9
|
createFallbackPanelAppContentPath,
|
|
10
10
|
createInlinePanelAppTab,
|
|
11
|
-
PANEL_APP_INLINE_CARD_MAX_HEIGHT_PX,
|
|
12
11
|
PANEL_APP_INLINE_IFRAME_SANDBOX,
|
|
13
12
|
} from '@/features/chat/features/message/utils/chat-inline-panel-app-card.utils';
|
|
14
13
|
import { useDocBrowser } from '@/shared/components/doc-browser';
|
|
15
14
|
import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
|
|
16
15
|
import { t } from '@/shared/lib/i18n';
|
|
17
16
|
|
|
17
|
+
type ChatInlinePanelAppDescriptor = Pick<ChatPanelAppCardViewModel, 'appId' | 'title'>;
|
|
18
|
+
|
|
18
19
|
export function ChatInlinePanelAppCard({
|
|
19
20
|
panelApp,
|
|
21
|
+
showExpandAction = true,
|
|
20
22
|
}: {
|
|
21
|
-
panelApp:
|
|
23
|
+
panelApp: ChatInlinePanelAppDescriptor;
|
|
24
|
+
showExpandAction?: boolean;
|
|
22
25
|
}) {
|
|
23
26
|
const docBrowser = useDocBrowser();
|
|
24
27
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
@@ -77,25 +80,30 @@ export function ChatInlinePanelAppCard({
|
|
|
77
80
|
if (!url) {
|
|
78
81
|
return (
|
|
79
82
|
<div
|
|
80
|
-
className="h-[320px] min-h-[220px] w-full animate-pulse rounded-lg border border-border bg-muted/45"
|
|
81
|
-
|
|
83
|
+
className="h-[320px] min-h-[220px] w-full max-w-[48rem] animate-pulse rounded-lg border border-border bg-muted/45"
|
|
84
|
+
data-chat-message-wide-content="true"
|
|
82
85
|
/>
|
|
83
86
|
);
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
return (
|
|
87
|
-
<div
|
|
90
|
+
<div
|
|
91
|
+
className="w-full max-w-[48rem] overflow-hidden rounded-lg border border-border bg-card shadow-sm"
|
|
92
|
+
data-chat-message-wide-content="true"
|
|
93
|
+
>
|
|
88
94
|
<div className="flex h-9 items-center justify-between gap-2 border-b border-border bg-muted/45 px-2.5">
|
|
89
95
|
<div className="flex min-w-0 items-center gap-2 text-xs font-medium text-foreground">
|
|
90
96
|
<AppWindow className="h-3.5 w-3.5 shrink-0 text-primary" />
|
|
91
97
|
<span className="truncate">{title}</span>
|
|
92
98
|
</div>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
{showExpandAction ? (
|
|
100
|
+
<IconActionButton
|
|
101
|
+
className="h-7 w-7 rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
102
|
+
icon={<Maximize2 className="h-3.5 w-3.5" />}
|
|
103
|
+
label={t('chatPanelCardExpand')}
|
|
104
|
+
onClick={openExpanded}
|
|
105
|
+
/>
|
|
106
|
+
) : null}
|
|
99
107
|
</div>
|
|
100
108
|
<div
|
|
101
109
|
className="relative h-[420px] min-h-[220px] max-h-[min(60vh,420px)] overflow-hidden bg-card"
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import type { NcpMessage } from "@nextclaw/ncp";
|
|
3
3
|
import {
|
|
4
|
+
type ChatInlineDisplayViewModel,
|
|
4
5
|
type ChatMessageViewModel,
|
|
5
6
|
ChatMessageList,
|
|
6
7
|
} from "@nextclaw/agent-chat-ui";
|
|
7
8
|
import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
|
|
8
9
|
import {
|
|
9
10
|
adaptChatMessage,
|
|
10
|
-
type ChatMessageAdapterTexts,
|
|
11
11
|
type ChatMessageSource,
|
|
12
12
|
} from "@/features/chat/features/message/utils/chat-message.utils";
|
|
13
|
+
import { buildChatMessageProcessSummary } from "@/features/chat/features/message/utils/chat-message-process-summary.utils";
|
|
14
|
+
import {
|
|
15
|
+
buildChatMessageAdapterTexts,
|
|
16
|
+
buildChatMessageTexts,
|
|
17
|
+
} from "@/features/chat/features/message/utils/chat-message-texts.utils";
|
|
13
18
|
import { readInlineTokensFromMetadata } from "@/features/chat/features/input/utils/chat-inline-token.utils";
|
|
14
19
|
import { adaptNcpMessageToUiMessage } from "@/features/chat/features/session/utils/ncp-session-adapter.utils";
|
|
15
20
|
import {
|
|
@@ -31,9 +36,27 @@ const INHERITED_FROM_SESSION_METADATA_KEY = "inherited_from_session_id";
|
|
|
31
36
|
|
|
32
37
|
const messageViewModelCache = new WeakMap<
|
|
33
38
|
NcpMessage,
|
|
34
|
-
{
|
|
39
|
+
{
|
|
40
|
+
language: string;
|
|
41
|
+
processSummaryLabel: string | null;
|
|
42
|
+
viewModel: ChatMessageViewModel;
|
|
43
|
+
}
|
|
35
44
|
>();
|
|
36
45
|
|
|
46
|
+
function renderChatInlineDisplay(display: ChatInlineDisplayViewModel) {
|
|
47
|
+
if (display.target.type !== "panel_app") {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return (
|
|
51
|
+
<ChatInlinePanelAppCard
|
|
52
|
+
panelApp={{
|
|
53
|
+
appId: display.target.payload.appId,
|
|
54
|
+
title: display.title,
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
37
60
|
type ContextInheritanceTimelineView = {
|
|
38
61
|
sourceSessionId: string;
|
|
39
62
|
inheritedMessageCount: number;
|
|
@@ -60,61 +83,6 @@ type ChatTimelineItem =
|
|
|
60
83
|
inheritance: ContextInheritanceTimelineView;
|
|
61
84
|
};
|
|
62
85
|
|
|
63
|
-
function buildChatMessageAdapterTexts(
|
|
64
|
-
language: string,
|
|
65
|
-
): ChatMessageAdapterTexts {
|
|
66
|
-
void language;
|
|
67
|
-
return {
|
|
68
|
-
roleLabels: {
|
|
69
|
-
user: t("chatRoleUser"),
|
|
70
|
-
assistant: t("chatRoleAssistant"),
|
|
71
|
-
tool: t("chatRoleTool"),
|
|
72
|
-
system: t("chatRoleSystem"),
|
|
73
|
-
fallback: t("chatRoleMessage"),
|
|
74
|
-
},
|
|
75
|
-
reasoningLabel: t("chatReasoning"),
|
|
76
|
-
toolCallLabel: t("chatToolCall"),
|
|
77
|
-
toolResultLabel: t("chatToolResult"),
|
|
78
|
-
toolInputLabel: t("chatToolInput"),
|
|
79
|
-
toolNoOutputLabel: t("chatToolNoOutput"),
|
|
80
|
-
toolOutputLabel: t("chatToolOutput"),
|
|
81
|
-
toolStatusPreparingLabel: t("chatToolStatusPreparing"),
|
|
82
|
-
toolStatusRunningLabel: t("chatToolStatusRunning"),
|
|
83
|
-
toolStatusCompletedLabel: t("chatToolStatusCompleted"),
|
|
84
|
-
toolStatusFailedLabel: t("chatToolStatusFailed"),
|
|
85
|
-
toolStatusCancelledLabel: t("chatToolStatusCancelled"),
|
|
86
|
-
showContentActionLabel: t("chatShowContentAction"),
|
|
87
|
-
imageAttachmentLabel: t("chatImageAttachment"),
|
|
88
|
-
fileAttachmentLabel: t("chatFileAttachment"),
|
|
89
|
-
unknownPartLabel: t("chatUnknownPart"),
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function buildChatMessageTexts(language: string) {
|
|
94
|
-
void language;
|
|
95
|
-
return {
|
|
96
|
-
copyCodeLabel: t("chatCodeCopy"),
|
|
97
|
-
copiedCodeLabel: t("chatCodeCopied"),
|
|
98
|
-
copyMessageLabel: t("chatMessageCopy"),
|
|
99
|
-
copiedMessageLabel: t("chatMessageCopied"),
|
|
100
|
-
typingLabel: t("chatTyping"),
|
|
101
|
-
attachmentOpenLabel: t("chatAttachmentOpen"),
|
|
102
|
-
attachmentAttachedLabel: t("chatAttachmentAttached"),
|
|
103
|
-
attachmentCategoryLabels: {
|
|
104
|
-
archive: t("chatAttachmentCategoryArchive"),
|
|
105
|
-
audio: t("chatAttachmentCategoryAudio"),
|
|
106
|
-
code: t("chatAttachmentCategoryCode"),
|
|
107
|
-
data: t("chatAttachmentCategoryData"),
|
|
108
|
-
document: t("chatAttachmentCategoryDocument"),
|
|
109
|
-
generic: t("chatAttachmentCategoryGeneric"),
|
|
110
|
-
image: t("chatAttachmentCategoryImage"),
|
|
111
|
-
pdf: t("chatAttachmentCategoryPdf"),
|
|
112
|
-
sheet: t("chatAttachmentCategorySheet"),
|
|
113
|
-
video: t("chatAttachmentCategoryVideo"),
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
86
|
function ChatContextCompactionDivider({
|
|
119
87
|
checkpoint,
|
|
120
88
|
}: {
|
|
@@ -316,18 +284,22 @@ export function ChatMessageListContainer({
|
|
|
316
284
|
}: ChatMessageListContainerProps) {
|
|
317
285
|
const presenter = usePresenter();
|
|
318
286
|
const { language } = useI18n();
|
|
319
|
-
const texts = useMemo
|
|
287
|
+
const texts = useMemo(
|
|
320
288
|
() => buildChatMessageAdapterTexts(language),
|
|
321
289
|
[language],
|
|
322
290
|
);
|
|
323
291
|
|
|
324
292
|
const messages = useMemo(() => {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
293
|
+
const visibleRawMessages = rawMessages.filter(isVisibleChatMessage);
|
|
294
|
+
const processedLabel = t("chatProcessSummaryProcessed");
|
|
295
|
+
return visibleRawMessages.flatMap((message) => {
|
|
296
|
+
const processSummary = buildChatMessageProcessSummary({
|
|
297
|
+
message,
|
|
298
|
+
processedLabel,
|
|
299
|
+
});
|
|
300
|
+
const processSummaryLabel = processSummary?.label ?? null;
|
|
329
301
|
const cached = messageViewModelCache.get(message);
|
|
330
|
-
if (cached && cached.language === language) {
|
|
302
|
+
if (cached && cached.language === language && cached.processSummaryLabel === processSummaryLabel) {
|
|
331
303
|
return [cached.viewModel];
|
|
332
304
|
}
|
|
333
305
|
|
|
@@ -339,6 +311,7 @@ export function ChatMessageListContainer({
|
|
|
339
311
|
timestamp: uiMessage.meta?.timestamp,
|
|
340
312
|
status: uiMessage.meta?.status,
|
|
341
313
|
inlineTokens: readInlineTokensFromMetadata(message.metadata),
|
|
314
|
+
processSummary,
|
|
342
315
|
},
|
|
343
316
|
parts: uiMessage.parts as unknown as ChatMessageSource["parts"],
|
|
344
317
|
};
|
|
@@ -347,7 +320,7 @@ export function ChatMessageListContainer({
|
|
|
347
320
|
texts,
|
|
348
321
|
});
|
|
349
322
|
|
|
350
|
-
messageViewModelCache.set(message, { language, viewModel });
|
|
323
|
+
messageViewModelCache.set(message, { language, processSummaryLabel, viewModel });
|
|
351
324
|
return [viewModel];
|
|
352
325
|
});
|
|
353
326
|
}, [language, rawMessages, texts]);
|
|
@@ -385,6 +358,7 @@ export function ChatMessageListContainer({
|
|
|
385
358
|
texts={messageTexts}
|
|
386
359
|
onToolAction={presenter.chatThreadManager.handleToolAction}
|
|
387
360
|
onFileOpen={presenter.chatThreadManager.openFilePreview}
|
|
361
|
+
renderInlineDisplay={renderChatInlineDisplay}
|
|
388
362
|
renderToolAgent={(agentId) => (
|
|
389
363
|
<AgentIdentityAvatar
|
|
390
364
|
agentId={agentId}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { NcpMessage } from "@nextclaw/ncp";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { buildChatMessageProcessSummary } from "@/features/chat/features/message/utils/chat-message-process-summary.utils";
|
|
4
|
+
|
|
5
|
+
const baseAssistantMessage = {
|
|
6
|
+
id: "assistant-1",
|
|
7
|
+
sessionId: "session-1",
|
|
8
|
+
role: "assistant",
|
|
9
|
+
status: "final",
|
|
10
|
+
timestamp: "2026-03-31T10:03:51.000Z",
|
|
11
|
+
parts: [
|
|
12
|
+
{
|
|
13
|
+
type: "reasoning",
|
|
14
|
+
text: "Inspecting current state.",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
type: "tool-invocation",
|
|
18
|
+
toolCallId: "tool-1",
|
|
19
|
+
toolName: "exec_command",
|
|
20
|
+
state: "result",
|
|
21
|
+
args: "{\"cmd\":\"git status\"}",
|
|
22
|
+
result: "clean",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: "Done.",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
} satisfies NcpMessage;
|
|
30
|
+
|
|
31
|
+
describe("buildChatMessageProcessSummary", () => {
|
|
32
|
+
it("does not invent duration when lifecycle timing is absent", () => {
|
|
33
|
+
expect(
|
|
34
|
+
buildChatMessageProcessSummary({
|
|
35
|
+
message: baseAssistantMessage,
|
|
36
|
+
processedLabel: "Processed",
|
|
37
|
+
}),
|
|
38
|
+
).toEqual({
|
|
39
|
+
label: "Processed",
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("derives duration from lifecycle startedAt and endedAt", () => {
|
|
44
|
+
expect(
|
|
45
|
+
buildChatMessageProcessSummary({
|
|
46
|
+
message: {
|
|
47
|
+
...baseAssistantMessage,
|
|
48
|
+
lifecycle: {
|
|
49
|
+
startedAt: "2026-03-31T10:00:00.000Z",
|
|
50
|
+
endedAt: "2026-03-31T10:03:51.000Z",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
processedLabel: "Processed",
|
|
54
|
+
}),
|
|
55
|
+
).toEqual({
|
|
56
|
+
label: "Processed 3m 51s",
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -29,6 +29,59 @@ const TEXTS: ChatMessageAdapterTexts = {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
describe("buildShowContentToolCard", () => {
|
|
32
|
+
it("builds a show-content action from a normalized show_file result", () => {
|
|
33
|
+
const card = buildShowContentToolCard({
|
|
34
|
+
invocation: {
|
|
35
|
+
status: "result",
|
|
36
|
+
toolCallId: "call-show-file",
|
|
37
|
+
toolName: "show_file",
|
|
38
|
+
result: {
|
|
39
|
+
ok: true,
|
|
40
|
+
action: "showContent",
|
|
41
|
+
request: {
|
|
42
|
+
target: {
|
|
43
|
+
type: "file",
|
|
44
|
+
payload: {
|
|
45
|
+
path: "docs/example.md",
|
|
46
|
+
line: 2,
|
|
47
|
+
viewer: "rendered",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
title: "Example",
|
|
51
|
+
purpose: "read",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
actionLabel: "Show content",
|
|
56
|
+
statusLabel: "Completed",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(card).toMatchObject({
|
|
60
|
+
kind: "result",
|
|
61
|
+
name: "show_file",
|
|
62
|
+
detail: "Example",
|
|
63
|
+
statusTone: "success",
|
|
64
|
+
statusLabel: "Completed",
|
|
65
|
+
action: {
|
|
66
|
+
kind: "show-content",
|
|
67
|
+
label: "Show content",
|
|
68
|
+
request: {
|
|
69
|
+
target: {
|
|
70
|
+
type: "file",
|
|
71
|
+
payload: {
|
|
72
|
+
path: "docs/example.md",
|
|
73
|
+
line: 2,
|
|
74
|
+
viewer: "rendered",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
title: "Example",
|
|
78
|
+
purpose: "read",
|
|
79
|
+
placement: "side_panel",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
32
85
|
it("builds a show-content action from a normalized show_content result", () => {
|
|
33
86
|
const card = buildShowContentToolCard({
|
|
34
87
|
invocation: {
|
|
@@ -44,6 +97,7 @@ describe("buildShowContentToolCard", () => {
|
|
|
44
97
|
payload: {
|
|
45
98
|
path: "docs/example.md",
|
|
46
99
|
line: 2,
|
|
100
|
+
viewer: "rendered",
|
|
47
101
|
},
|
|
48
102
|
},
|
|
49
103
|
title: "Example",
|
|
@@ -70,6 +124,7 @@ describe("buildShowContentToolCard", () => {
|
|
|
70
124
|
payload: {
|
|
71
125
|
path: "docs/example.md",
|
|
72
126
|
line: 2,
|
|
127
|
+
viewer: "rendered",
|
|
73
128
|
},
|
|
74
129
|
},
|
|
75
130
|
title: "Example",
|
|
@@ -85,7 +140,7 @@ describe("buildShowContentToolCard", () => {
|
|
|
85
140
|
invocation: {
|
|
86
141
|
status: "result",
|
|
87
142
|
toolCallId: "call-show-content",
|
|
88
|
-
toolName: "
|
|
143
|
+
toolName: "show_panel_app",
|
|
89
144
|
result: {
|
|
90
145
|
ok: true,
|
|
91
146
|
action: "showContent",
|
|
@@ -15,8 +15,6 @@ export const PANEL_APP_INLINE_IFRAME_SANDBOX = [
|
|
|
15
15
|
'allow-presentation',
|
|
16
16
|
].join(' ');
|
|
17
17
|
|
|
18
|
-
export const PANEL_APP_INLINE_CARD_MAX_HEIGHT_PX = 420;
|
|
19
|
-
|
|
20
18
|
const PANEL_APP_INLINE_CARD_SEARCH_PARAMS = {
|
|
21
19
|
nextclawDisplayMode: 'card',
|
|
22
20
|
nextclawPlacement: 'inline',
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { NcpMessage } from "@nextclaw/ncp";
|
|
2
|
+
import type { ChatMessageProcessSummarySource } from "@/features/chat/types/chat-message.types";
|
|
3
|
+
|
|
4
|
+
type BuildChatMessageProcessSummaryParams = {
|
|
5
|
+
message: NcpMessage;
|
|
6
|
+
processedLabel: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function isAssistantProcessPart(part: NcpMessage["parts"][number]): boolean {
|
|
10
|
+
return part.type === "reasoning" || part.type === "tool-invocation";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isAssistantFinalContentPart(
|
|
14
|
+
part: NcpMessage["parts"][number],
|
|
15
|
+
): boolean {
|
|
16
|
+
if (part.type === "text" || part.type === "rich-text") {
|
|
17
|
+
return part.text.trim().length > 0;
|
|
18
|
+
}
|
|
19
|
+
return part.type === "file" || part.type === "source" || part.type === "card";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function hasCollapsibleAssistantProcess(message: NcpMessage): boolean {
|
|
23
|
+
if (
|
|
24
|
+
message.role !== "assistant" ||
|
|
25
|
+
message.status === "pending" ||
|
|
26
|
+
message.status === "streaming"
|
|
27
|
+
) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
let lastProcessPartIndex = -1;
|
|
31
|
+
for (let index = message.parts.length - 1; index >= 0; index -= 1) {
|
|
32
|
+
const part = message.parts[index];
|
|
33
|
+
if (part && isAssistantProcessPart(part)) {
|
|
34
|
+
lastProcessPartIndex = index;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return (
|
|
39
|
+
lastProcessPartIndex >= 0 &&
|
|
40
|
+
lastProcessPartIndex < message.parts.length - 1 &&
|
|
41
|
+
message.parts
|
|
42
|
+
.slice(lastProcessPartIndex + 1)
|
|
43
|
+
.some(isAssistantFinalContentPart)
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function formatLifecycleDuration(message: NcpMessage): string | null {
|
|
48
|
+
const startedAt = message.lifecycle?.startedAt;
|
|
49
|
+
const endedAt = message.lifecycle?.endedAt;
|
|
50
|
+
if (!startedAt || !endedAt) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const started = Date.parse(startedAt);
|
|
54
|
+
const ended = Date.parse(endedAt);
|
|
55
|
+
if (!Number.isFinite(started) || !Number.isFinite(ended) || ended < started) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const totalSeconds = Math.round((ended - started) / 1000);
|
|
59
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
60
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
61
|
+
const seconds = totalSeconds % 60;
|
|
62
|
+
if (hours > 0) {
|
|
63
|
+
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
64
|
+
}
|
|
65
|
+
if (minutes > 0) {
|
|
66
|
+
return `${minutes}m ${seconds}s`;
|
|
67
|
+
}
|
|
68
|
+
return `${seconds}s`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function buildChatMessageProcessSummary({
|
|
72
|
+
message,
|
|
73
|
+
processedLabel,
|
|
74
|
+
}: BuildChatMessageProcessSummaryParams): ChatMessageProcessSummarySource | undefined {
|
|
75
|
+
if (!hasCollapsibleAssistantProcess(message)) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
const duration = formatLifecycleDuration(message);
|
|
79
|
+
return { label: duration ? `${processedLabel} ${duration}` : processedLabel };
|
|
80
|
+
}
|
package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
ToolCardViewSource,
|
|
9
9
|
} from "./chat-message-tool-card.utils";
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const SHOW_CONTENT_TOOL_NAMES = ["show_content", "show_file", "show_url", "show_panel_app"];
|
|
12
12
|
|
|
13
13
|
type ShowContentTargetType = ChatUiShowContentRequest["target"]["type"];
|
|
14
14
|
|
|
@@ -48,6 +48,14 @@ function readPlacement(value: unknown): ChatUiShowContentRequest["placement"] {
|
|
|
48
48
|
: undefined;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function readFileViewer(value: unknown): NonNullable<
|
|
52
|
+
Extract<ChatUiShowContentRequest["target"], { type: "file" }>["payload"]["viewer"]
|
|
53
|
+
> | undefined {
|
|
54
|
+
return value === "auto" || value === "source" || value === "rendered"
|
|
55
|
+
? value
|
|
56
|
+
: undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
function readShowContentRequest(value: unknown): ChatUiShowContentRequest | null {
|
|
52
60
|
if (!isRecord(value) || !isRecord(value.target)) {
|
|
53
61
|
return null;
|
|
@@ -77,6 +85,7 @@ function readShowContentRequest(value: unknown): ChatUiShowContentRequest | null
|
|
|
77
85
|
path,
|
|
78
86
|
line: readOptionalPositiveInteger(payload.line),
|
|
79
87
|
column: readOptionalPositiveInteger(payload.column),
|
|
88
|
+
viewer: readFileViewer(payload.viewer),
|
|
80
89
|
},
|
|
81
90
|
},
|
|
82
91
|
title,
|
|
@@ -150,7 +159,7 @@ export function buildShowContentToolCard(params: {
|
|
|
150
159
|
invocation,
|
|
151
160
|
statusLabel,
|
|
152
161
|
} = params;
|
|
153
|
-
if (invocation.toolName
|
|
162
|
+
if (!SHOW_CONTENT_TOOL_NAMES.includes(invocation.toolName)) {
|
|
154
163
|
return null;
|
|
155
164
|
}
|
|
156
165
|
const request = readShowContentResult(invocation.result);
|
|
@@ -175,7 +184,7 @@ export function buildShowContentToolCard(params: {
|
|
|
175
184
|
: undefined;
|
|
176
185
|
return {
|
|
177
186
|
kind: "result",
|
|
178
|
-
name:
|
|
187
|
+
name: invocation.toolName,
|
|
179
188
|
detail: summarizeShowContentRequest(request),
|
|
180
189
|
text: undefined,
|
|
181
190
|
outputData: invocation.result,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ChatMessageTexts } from "@nextclaw/agent-chat-ui";
|
|
2
|
+
import type { ChatMessageAdapterTexts } from "@/features/chat/features/message/utils/chat-message.utils";
|
|
3
|
+
import { t } from "@/shared/lib/i18n";
|
|
4
|
+
|
|
5
|
+
export function buildChatMessageAdapterTexts(
|
|
6
|
+
language: string,
|
|
7
|
+
): ChatMessageAdapterTexts {
|
|
8
|
+
void language;
|
|
9
|
+
return {
|
|
10
|
+
roleLabels: {
|
|
11
|
+
user: t("chatRoleUser"),
|
|
12
|
+
assistant: t("chatRoleAssistant"),
|
|
13
|
+
tool: t("chatRoleTool"),
|
|
14
|
+
system: t("chatRoleSystem"),
|
|
15
|
+
fallback: t("chatRoleMessage"),
|
|
16
|
+
},
|
|
17
|
+
reasoningLabel: t("chatReasoning"),
|
|
18
|
+
toolCallLabel: t("chatToolCall"),
|
|
19
|
+
toolResultLabel: t("chatToolResult"),
|
|
20
|
+
toolInputLabel: t("chatToolInput"),
|
|
21
|
+
toolNoOutputLabel: t("chatToolNoOutput"),
|
|
22
|
+
toolOutputLabel: t("chatToolOutput"),
|
|
23
|
+
toolStatusPreparingLabel: t("chatToolStatusPreparing"),
|
|
24
|
+
toolStatusRunningLabel: t("chatToolStatusRunning"),
|
|
25
|
+
toolStatusCompletedLabel: t("chatToolStatusCompleted"),
|
|
26
|
+
toolStatusFailedLabel: t("chatToolStatusFailed"),
|
|
27
|
+
toolStatusCancelledLabel: t("chatToolStatusCancelled"),
|
|
28
|
+
showContentActionLabel: t("chatShowContentAction"),
|
|
29
|
+
imageAttachmentLabel: t("chatImageAttachment"),
|
|
30
|
+
fileAttachmentLabel: t("chatFileAttachment"),
|
|
31
|
+
unknownPartLabel: t("chatUnknownPart"),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function buildChatMessageTexts(
|
|
36
|
+
language: string,
|
|
37
|
+
): ChatMessageTexts {
|
|
38
|
+
void language;
|
|
39
|
+
return {
|
|
40
|
+
copyCodeLabel: t("chatCodeCopy"),
|
|
41
|
+
copiedCodeLabel: t("chatCodeCopied"),
|
|
42
|
+
copyMessageLabel: t("chatMessageCopy"),
|
|
43
|
+
copiedMessageLabel: t("chatMessageCopied"),
|
|
44
|
+
typingLabel: t("chatTyping"),
|
|
45
|
+
attachmentOpenLabel: t("chatAttachmentOpen"),
|
|
46
|
+
attachmentAttachedLabel: t("chatAttachmentAttached"),
|
|
47
|
+
attachmentCategoryLabels: {
|
|
48
|
+
archive: t("chatAttachmentCategoryArchive"),
|
|
49
|
+
audio: t("chatAttachmentCategoryAudio"),
|
|
50
|
+
code: t("chatAttachmentCategoryCode"),
|
|
51
|
+
data: t("chatAttachmentCategoryData"),
|
|
52
|
+
document: t("chatAttachmentCategoryDocument"),
|
|
53
|
+
generic: t("chatAttachmentCategoryGeneric"),
|
|
54
|
+
image: t("chatAttachmentCategoryImage"),
|
|
55
|
+
pdf: t("chatAttachmentCategoryPdf"),
|
|
56
|
+
sheet: t("chatAttachmentCategorySheet"),
|
|
57
|
+
video: t("chatAttachmentCategoryVideo"),
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
import type {
|
|
8
8
|
ChatMessageAdapterTexts,
|
|
9
9
|
ChatMessagePartSource,
|
|
10
|
+
ChatMessageProcessSummarySource,
|
|
10
11
|
} from "@/features/chat/types/chat-message.types";
|
|
11
12
|
|
|
12
13
|
export type {
|
|
@@ -21,6 +22,7 @@ export type ChatMessageSource = {
|
|
|
21
22
|
timestamp?: string;
|
|
22
23
|
status?: string;
|
|
23
24
|
inlineTokens?: ChatInlineTokenSource[];
|
|
25
|
+
processSummary?: ChatMessageProcessSummarySource;
|
|
24
26
|
};
|
|
25
27
|
parts: ChatMessagePartSource[];
|
|
26
28
|
};
|
|
@@ -79,6 +81,7 @@ export function adaptChatMessage(
|
|
|
79
81
|
roleLabel: resolveRoleLabel(message.role, params.texts.roleLabels),
|
|
80
82
|
timestampLabel: params.formatTimestamp(resolveMessageTimestamp(message)),
|
|
81
83
|
status: message.meta?.status,
|
|
84
|
+
processSummary: message.meta?.processSummary,
|
|
82
85
|
parts: message.parts
|
|
83
86
|
.map((part) =>
|
|
84
87
|
adaptChatMessagePart({
|
|
@@ -220,6 +220,45 @@ describe("useNcpAgentRuntime", () => {
|
|
|
220
220
|
expect(client.abort).toHaveBeenCalledWith({ sessionId: "session-running" });
|
|
221
221
|
});
|
|
222
222
|
|
|
223
|
+
it("clears the local running state when the live stream publishes message.abort", async () => {
|
|
224
|
+
const client = new DeferredSendClient();
|
|
225
|
+
const manager = new DefaultNcpAgentConversationStateManager();
|
|
226
|
+
const { result } = renderHook(() =>
|
|
227
|
+
useNcpAgentRuntime({ sessionId: "session-running", client, manager: manager as never }),
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
await act(async () => {
|
|
231
|
+
await client.emit({
|
|
232
|
+
type: NcpEventType.RunStarted,
|
|
233
|
+
payload: {
|
|
234
|
+
sessionId: "session-running",
|
|
235
|
+
messageId: "assistant-1",
|
|
236
|
+
runId: "run-1",
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
await waitFor(() => {
|
|
242
|
+
expect(result.current.isRunning).toBe(true);
|
|
243
|
+
expect(result.current.activeRunId).toBe("run-1");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
await act(async () => {
|
|
247
|
+
await client.emit({
|
|
248
|
+
type: NcpEventType.MessageAbort,
|
|
249
|
+
payload: {
|
|
250
|
+
sessionId: "session-running",
|
|
251
|
+
messageId: "assistant-1",
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
await waitFor(() => {
|
|
257
|
+
expect(result.current.isRunning).toBe(false);
|
|
258
|
+
expect(result.current.activeRunId).toBeNull();
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
223
262
|
it("uses the hydrated live stream as the only event source while sending to an existing session", async () => {
|
|
224
263
|
const client = new ExistingSessionLiveClient();
|
|
225
264
|
const manager = new DefaultNcpAgentConversationStateManager();
|
|
@@ -70,7 +70,7 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
|
|
|
70
70
|
icon={<Plus className="h-3.5 w-3.5" />}
|
|
71
71
|
label={actionLabel}
|
|
72
72
|
tooltip={false}
|
|
73
|
-
className="h-7 w-7 shrink-0 rounded-lg text-muted-foreground hover:bg-
|
|
73
|
+
className="h-7 w-7 shrink-0 rounded-lg text-muted-foreground hover:bg-gray-200/60 hover:text-gray-900"
|
|
74
74
|
/>
|
|
75
75
|
</PopoverTrigger>
|
|
76
76
|
<PopoverContent
|
|
@@ -91,7 +91,7 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
|
|
|
91
91
|
<IconActionButton
|
|
92
92
|
icon={<Plus className="h-3.5 w-3.5" />}
|
|
93
93
|
label={actionLabel}
|
|
94
|
-
className="h-7 w-7 shrink-0 rounded-lg text-muted-foreground hover:bg-
|
|
94
|
+
className="h-7 w-7 shrink-0 rounded-lg text-muted-foreground hover:bg-gray-200/60 hover:text-gray-900"
|
|
95
95
|
onClick={() => onCreateSession(preferredSessionType, group.projectRoot)}
|
|
96
96
|
/>
|
|
97
97
|
)}
|
|
@@ -230,7 +230,7 @@ export function ChatSidebarSessionItem({
|
|
|
230
230
|
className={cn(
|
|
231
231
|
'w-full rounded-xl px-3 py-2.5 text-left transition-all text-[13px]',
|
|
232
232
|
active
|
|
233
|
-
? 'bg-gray-200 text-gray-900 font-semibold shadow-sm'
|
|
233
|
+
? 'bg-gray-200/80 text-gray-900 font-semibold shadow-sm'
|
|
234
234
|
: 'text-gray-700 hover:bg-gray-200/60 hover:text-gray-900'
|
|
235
235
|
)}
|
|
236
236
|
>
|