@nextclaw/ui 0.11.21 → 0.11.23
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 +24 -0
- package/dist/assets/{ChannelsList-ByHWHkQS.js → ChannelsList-DVDu1xvz.js} +6 -6
- package/dist/assets/ChatPage-Z9tRzm_n.js +43 -0
- package/dist/assets/DocBrowser-B9OaZjmg.js +1 -0
- package/dist/assets/{DocBrowser-3y_NHZ71.js → DocBrowser-BmtBLFU0.js} +1 -1
- package/dist/assets/{DocBrowserContext-CVJuwCcw.js → DocBrowserContext-YIKkPb76.js} +1 -1
- package/dist/assets/{LogoBadge-D8fyilO-.js → LogoBadge-F7ZWdxLT.js} +1 -1
- package/dist/assets/{MarketplacePage-CmhsZXr1.js → MarketplacePage-Buo9HrOz.js} +2 -2
- package/dist/assets/MarketplacePage-D6rVQEQR.js +1 -0
- package/dist/assets/{McpMarketplacePage-C7PkCYbp.js → McpMarketplacePage-JnkYwK7p.js} +2 -2
- package/dist/assets/ModelConfig-BYRhgp0c.js +1 -0
- package/dist/assets/ProvidersList-DmLyyHvX.js +1 -0
- package/dist/assets/RemoteAccessPage-CDSSvH7Z.js +1 -0
- package/dist/assets/RuntimeConfig-v7a7Fe3x.js +1 -0
- package/dist/assets/{SearchConfig-Dm7r2yfp.js → SearchConfig-D5f1EkLE.js} +1 -1
- package/dist/assets/{SecretsConfig-BBP_mbQh.js → SecretsConfig-D61IKcYt.js} +2 -2
- package/dist/assets/{SessionsConfig-6wNJloZN.js → SessionsConfig-BRIxVTEv.js} +2 -2
- package/dist/assets/{book-open-B26jGBjY.js → book-open-CXoF5nQC.js} +1 -1
- package/dist/assets/chat-session-display-D0WpnuRZ.js +1 -0
- package/dist/assets/{chunk-JZWAC4HX-B-4B29RN.js → chunk-JZWAC4HX-CvRWvTy5.js} +1 -1
- package/dist/assets/{config-BaC29Qf-.js → config-DJswxxE8.js} +1 -1
- package/dist/assets/{createLucideIcon-DiFAvXmK.js → createLucideIcon-CjGHOWb6.js} +1 -1
- package/dist/assets/{dist-pCfWPG1A.js → dist-Cl2QB-2y.js} +1 -1
- package/dist/assets/{dist-kW_O3kyZ.js → dist-nqTTbVdA.js} +1 -1
- package/dist/assets/{external-link-D5-p-Gmm.js → external-link-tIO7zING.js} +1 -1
- package/dist/assets/{hash-BlwrSV0q.js → hash-JWUyl1pT.js} +1 -1
- package/dist/assets/i18n-CDHMXlRZ.js +1 -0
- package/dist/assets/{index-DvKS3L9j.js → index-BuwbBgmT.js} +3 -3
- package/dist/assets/index-bZ8cqQIS.css +1 -0
- package/dist/assets/{label-RyXfZqkP.js → label-BIpeNu4r.js} +1 -1
- package/dist/assets/loader-circle-Cs8XVFTw.js +1 -0
- package/dist/assets/{logos-Bpl8QTgI.js → logos-DThdM9lk.js} +1 -1
- package/dist/assets/{page-layout--S0YBU0W.js → page-layout-D3Xo605Z.js} +1 -1
- package/dist/assets/plus-PHf8q-Ct.js +1 -0
- package/dist/assets/{popover-BEjfbEwy.js → popover-BJRUGA_H.js} +1 -1
- package/dist/assets/provider-models-bz5y28rq.js +1 -0
- package/dist/assets/{react-BuSP2-8B.js → react-7ZHqQtEV.js} +1 -1
- package/dist/assets/refresh-ccw-CC6-_QuL.js +1 -0
- package/dist/assets/{save-DPPPpD_c.js → save-DJM5RRWW.js} +1 -1
- package/dist/assets/search-C91yH_6y.js +1 -0
- package/dist/assets/{security-config-6t78Ph-I.js → security-config-DbUyWcQz.js} +1 -1
- package/dist/assets/{select-CT50pzod.js → select-DSkTc61S.js} +1 -1
- package/dist/assets/skeleton-Dzg-HOiN.js +1 -0
- package/dist/assets/{status-dot-BbBqRHfh.js → status-dot-LNBlDu3q.js} +1 -1
- package/dist/assets/{switch-D3l6AcCk.js → switch-Bo-Y46HZ.js} +1 -1
- package/dist/assets/tabs-custom-DXv507_2.js +1 -0
- package/dist/assets/{trash-2-B2_AGVE3.js → trash-2-DFZmW6Gg.js} +1 -1
- package/dist/assets/useConfirmDialog-COwYXDKm.js +1 -0
- package/dist/assets/{useMutation-BzCrO8j-.js → useMutation-DrZrOgVL.js} +1 -1
- package/dist/assets/x-D7Q1yqSF.js +1 -0
- package/dist/index.html +18 -18
- package/package.json +6 -6
- package/src/api/ncp-session.test.ts +37 -0
- package/src/api/ncp-session.ts +29 -1
- package/src/api/server-path.ts +23 -0
- package/src/api/types.ts +45 -0
- package/src/components/chat/ChatConversationPanel.test.tsx +53 -9
- package/src/components/chat/ChatConversationPanel.tsx +122 -79
- package/src/components/chat/ChatSidebar.test.tsx +2 -2
- package/src/components/chat/ChatSidebar.tsx +2 -2
- package/src/components/chat/adapters/chat-input-bar.adapter.test.ts +1 -0
- package/src/components/chat/adapters/chat-input-bar.adapter.ts +7 -2
- package/src/components/chat/adapters/chat-message-part.adapter.ts +26 -14
- package/src/components/chat/adapters/chat-message.adapter.test.ts +159 -13
- package/src/components/chat/adapters/chat-message.session-request-tool-card.ts +191 -0
- package/src/components/chat/adapters/{chat-message.file-operation-card.ts → file-operation/card.ts} +74 -181
- package/src/components/chat/adapters/{chat-message.file-operation-diff.ts → file-operation/diff.ts} +178 -188
- package/src/components/chat/adapters/file-operation/line-builder.ts +249 -0
- package/src/components/chat/adapters/file-operation/record-readers.ts +233 -0
- package/src/components/chat/chat-child-session-panel.tsx +100 -0
- package/src/components/chat/chat-composer-state.ts +3 -3
- package/src/components/chat/chat-page-runtime.test.ts +1 -0
- package/src/components/chat/chat-session-display.test.ts +22 -0
- package/src/components/chat/chat-session-display.ts +6 -1
- package/src/components/chat/containers/chat-input-bar.container.tsx +21 -24
- package/src/components/chat/containers/chat-message-list.container.tsx +4 -0
- package/src/components/chat/hooks/use-chat-session-label.ts +19 -0
- package/src/components/chat/hooks/use-chat-session-project.test.tsx +117 -0
- package/src/components/chat/hooks/use-chat-session-project.ts +40 -0
- package/src/components/chat/{chat-session-label.service.ts → hooks/use-chat-session-update.ts} +11 -7
- package/src/components/chat/managers/chat-session-list.manager.ts +5 -1
- package/src/components/chat/ncp/NcpChatPage.tsx +219 -116
- package/src/components/chat/ncp/ncp-chat-page-data.test.ts +33 -0
- package/src/components/chat/ncp/ncp-chat-page-data.ts +21 -15
- package/src/components/chat/ncp/ncp-chat-thread.manager.ts +49 -0
- package/src/components/chat/ncp/ncp-session-adapter.test.ts +24 -0
- package/src/components/chat/ncp/ncp-session-adapter.ts +47 -0
- package/src/components/chat/ncp/use-ncp-session-list-view.ts +10 -1
- package/src/components/chat/presenter/chat-presenter-context.tsx +4 -1
- package/src/components/chat/session-header/chat-session-header-actions.test.tsx +63 -0
- package/src/components/chat/session-header/chat-session-header-actions.tsx +95 -0
- package/src/components/chat/session-header/chat-session-header-menu-item.tsx +35 -0
- package/src/components/chat/session-header/chat-session-project-badge.test.tsx +66 -0
- package/src/components/chat/session-header/chat-session-project-badge.tsx +102 -0
- package/src/components/chat/session-header/chat-session-project-dialog.tsx +34 -0
- package/src/components/chat/stores/chat-input.store.ts +6 -3
- package/src/components/chat/stores/chat-thread.store.ts +17 -3
- package/src/components/chat/useHydratedNcpAgent.test.tsx +30 -23
- package/src/components/path-picker/server-path-picker-dialog.test.tsx +92 -0
- package/src/components/path-picker/server-path-picker-dialog.tsx +282 -0
- package/src/hooks/server-path/use-server-path-browse.ts +19 -0
- package/src/hooks/useConfig.ts +26 -1
- package/src/lib/i18n/i18n-language-owner.ts +94 -0
- package/src/lib/i18n/i18n.path-picker.ts +12 -0
- package/src/lib/i18n.chat.ts +23 -0
- package/src/lib/i18n.ts +21 -84
- package/src/lib/session-project/session-project.utils.ts +30 -0
- package/dist/assets/ChatPage-FdT3pDnw.js +0 -42
- package/dist/assets/DocBrowser-CMdPdbZj.js +0 -1
- package/dist/assets/MarketplacePage-9oKmxN2n.js +0 -1
- package/dist/assets/ModelConfig-DmCY6jWM.js +0 -1
- package/dist/assets/ProvidersList-ClT-34aX.js +0 -1
- package/dist/assets/RemoteAccessPage-B6hUZl1O.js +0 -1
- package/dist/assets/RuntimeConfig-C5aqliGk.js +0 -1
- package/dist/assets/chat-session-display-Bjmn4aIZ.js +0 -1
- package/dist/assets/i18n-CSytxMFI.js +0 -1
- package/dist/assets/index-CUy6doWo.css +0 -1
- package/dist/assets/loader-circle-B2J777gj.js +0 -1
- package/dist/assets/plus-CM9XJ0Tf.js +0 -1
- package/dist/assets/provider-models-C8JQUd1E.js +0 -1
- package/dist/assets/search-Ctaw34Kp.js +0 -1
- package/dist/assets/skeleton-Bycyb0zU.js +0 -1
- package/dist/assets/tabs-custom-TZQ5WPWP.js +0 -1
- package/dist/assets/useConfirmDialog-BDpdjfIO.js +0 -1
- package/dist/assets/x-CHOBE-63.js +0 -1
- package/src/components/chat/adapters/chat-message.subagent-tool-card.ts +0 -154
- /package/dist/assets/{config-hints-fGnUjDe9.js → config-hints-WtpHP_DW.js} +0 -0
- /package/dist/assets/{config-layout-B-7erZRN.js → config-layout-LQ10ozRC.js} +0 -0
- /package/dist/assets/{marketplace-localization-CXeGRf6E.js → marketplace-localization-CxSTG9wr.js} +0 -0
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { useRef } from "react";
|
|
2
|
+
import { ArrowLeft } from "lucide-react";
|
|
2
3
|
import { useStickyBottomScroll } from "@nextclaw/agent-chat-ui";
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
4
|
import {
|
|
5
5
|
ChatInputBarContainer,
|
|
6
6
|
ChatMessageListContainer,
|
|
7
7
|
} from "@/components/chat/nextclaw";
|
|
8
|
+
import { ChatChildSessionPanel } from "@/components/chat/chat-child-session-panel";
|
|
8
9
|
import { ChatWelcome } from "@/components/chat/ChatWelcome";
|
|
9
10
|
import { usePresenter } from "@/components/chat/presenter/chat-presenter-context";
|
|
11
|
+
import { ChatSessionHeaderActions } from "@/components/chat/session-header/chat-session-header-actions";
|
|
12
|
+
import { ChatSessionProjectBadge } from "@/components/chat/session-header/chat-session-project-badge";
|
|
10
13
|
import { useChatThreadStore } from "@/components/chat/stores/chat-thread.store";
|
|
11
14
|
import { t } from "@/lib/i18n";
|
|
12
15
|
import { cn } from "@/lib/utils";
|
|
13
|
-
import { Trash2 } from "lucide-react";
|
|
14
16
|
|
|
15
17
|
function ChatConversationSkeleton() {
|
|
16
18
|
return (
|
|
@@ -45,16 +47,20 @@ export function ChatConversationPanel() {
|
|
|
45
47
|
const snapshot = useChatThreadStore((state) => state.snapshot);
|
|
46
48
|
const fallbackThreadRef = useRef<HTMLDivElement | null>(null);
|
|
47
49
|
const threadRef = snapshot.threadRef ?? fallbackThreadRef;
|
|
50
|
+
const detailSessionKey =
|
|
51
|
+
snapshot.childSessionDetailParentSessionKey === snapshot.sessionKey
|
|
52
|
+
? snapshot.childSessionDetailSessionKey
|
|
53
|
+
: null;
|
|
48
54
|
const shouldShowSessionHeader = Boolean(
|
|
49
|
-
snapshot.
|
|
55
|
+
snapshot.sessionKey || snapshot.sessionTypeLabel,
|
|
50
56
|
);
|
|
51
57
|
const sessionHeaderTitle =
|
|
52
58
|
snapshot.sessionDisplayName ||
|
|
53
|
-
snapshot.
|
|
59
|
+
(snapshot.canDeleteSession && snapshot.sessionKey ? snapshot.sessionKey : null) ||
|
|
54
60
|
t("chatSidebarNewTask");
|
|
55
61
|
|
|
56
62
|
const showWelcome =
|
|
57
|
-
!snapshot.
|
|
63
|
+
!snapshot.canDeleteSession &&
|
|
58
64
|
snapshot.messages.length === 0 &&
|
|
59
65
|
!snapshot.isSending;
|
|
60
66
|
const hasConfiguredModel = snapshot.modelOptions.length > 0;
|
|
@@ -68,7 +74,7 @@ export function ChatConversationPanel() {
|
|
|
68
74
|
|
|
69
75
|
const { onScroll: handleScroll } = useStickyBottomScroll({
|
|
70
76
|
scrollRef: threadRef,
|
|
71
|
-
resetKey: snapshot.
|
|
77
|
+
resetKey: snapshot.sessionKey,
|
|
72
78
|
isLoading: snapshot.isHistoryLoading,
|
|
73
79
|
hasContent: snapshot.messages.length > 0,
|
|
74
80
|
contentVersion: snapshot.messages[snapshot.messages.length - 1] ?? null,
|
|
@@ -79,91 +85,128 @@ export function ChatConversationPanel() {
|
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
return (
|
|
82
|
-
<section className="flex-1 min-h-0 flex
|
|
83
|
-
<div
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
</
|
|
99
|
-
|
|
100
|
-
</div>
|
|
101
|
-
{snapshot.selectedSessionKey ? (
|
|
102
|
-
<Button
|
|
103
|
-
variant="ghost"
|
|
104
|
-
size="icon"
|
|
105
|
-
className="rounded-lg shrink-0 text-gray-400 hover:text-destructive"
|
|
106
|
-
onClick={presenter.chatThreadManager.deleteSession}
|
|
107
|
-
disabled={!snapshot.canDeleteSession || snapshot.isDeletePending}
|
|
108
|
-
>
|
|
109
|
-
<Trash2 className="h-4 w-4" />
|
|
110
|
-
</Button>
|
|
88
|
+
<section className="flex-1 min-h-0 flex overflow-hidden bg-gradient-to-b from-gray-50/60 to-white">
|
|
89
|
+
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
|
90
|
+
{snapshot.parentSessionKey ? (
|
|
91
|
+
<div className="border-b border-gray-200/60 bg-white/75 px-5 py-2 backdrop-blur-sm">
|
|
92
|
+
<button
|
|
93
|
+
type="button"
|
|
94
|
+
onClick={presenter.chatThreadManager.goToParentSession}
|
|
95
|
+
className="inline-flex items-center gap-2 text-xs font-medium text-gray-600 transition-colors hover:text-gray-900"
|
|
96
|
+
>
|
|
97
|
+
<ArrowLeft className="h-3.5 w-3.5" />
|
|
98
|
+
<span>
|
|
99
|
+
Back to parent
|
|
100
|
+
{snapshot.parentSessionLabel?.trim()
|
|
101
|
+
? ` · ${snapshot.parentSessionLabel.trim()}`
|
|
102
|
+
: ""}
|
|
103
|
+
</span>
|
|
104
|
+
</button>
|
|
105
|
+
</div>
|
|
111
106
|
) : null}
|
|
112
|
-
</div>
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
108
|
+
<div
|
|
109
|
+
className={cn(
|
|
110
|
+
"px-5 border-b border-gray-200/60 bg-white/80 backdrop-blur-sm flex items-center justify-between shrink-0 overflow-hidden transition-all duration-200",
|
|
111
|
+
shouldShowSessionHeader
|
|
112
|
+
? "py-3 opacity-100"
|
|
113
|
+
: "h-0 py-0 opacity-0 border-b-0",
|
|
114
|
+
)}
|
|
115
|
+
>
|
|
116
|
+
<div className="min-w-0 flex-1 flex items-center gap-2">
|
|
117
|
+
<span className="text-sm font-medium text-gray-700 truncate">
|
|
118
|
+
{sessionHeaderTitle}
|
|
119
|
+
</span>
|
|
120
|
+
{snapshot.sessionTypeLabel ? (
|
|
121
|
+
<span className="shrink-0 rounded-full border border-gray-200 bg-gray-100 px-2 py-0.5 text-[11px] font-medium text-gray-600">
|
|
122
|
+
{snapshot.sessionTypeLabel}
|
|
123
|
+
</span>
|
|
124
|
+
) : null}
|
|
125
|
+
{snapshot.sessionProjectName ? (
|
|
126
|
+
<ChatSessionProjectBadge
|
|
127
|
+
sessionKey={snapshot.sessionKey ?? "draft"}
|
|
128
|
+
projectName={snapshot.sessionProjectName}
|
|
129
|
+
projectRoot={snapshot.sessionProjectRoot}
|
|
130
|
+
persistToServer={snapshot.canDeleteSession}
|
|
131
|
+
/>
|
|
132
|
+
) : null}
|
|
133
|
+
</div>
|
|
134
|
+
{snapshot.sessionKey ? (
|
|
135
|
+
<ChatSessionHeaderActions
|
|
136
|
+
sessionKey={snapshot.sessionKey}
|
|
137
|
+
canDeleteSession={snapshot.canDeleteSession}
|
|
138
|
+
isDeletePending={snapshot.isDeletePending}
|
|
139
|
+
projectRoot={snapshot.sessionProjectRoot}
|
|
140
|
+
onDeleteSession={presenter.chatThreadManager.deleteSession}
|
|
141
|
+
/>
|
|
142
|
+
) : null}
|
|
126
143
|
</div>
|
|
127
|
-
)}
|
|
128
144
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 shrink-0">
|
|
145
|
+
{shouldShowProviderHint && (
|
|
146
|
+
<div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 flex items-center justify-between gap-3 shrink-0">
|
|
132
147
|
<span className="text-xs text-amber-800">
|
|
133
|
-
{
|
|
148
|
+
{t("chatModelNoOptions")}
|
|
134
149
|
</span>
|
|
150
|
+
<button
|
|
151
|
+
type="button"
|
|
152
|
+
onClick={presenter.chatThreadManager.goToProviders}
|
|
153
|
+
className="text-xs font-semibold text-amber-900 underline-offset-2 hover:underline"
|
|
154
|
+
>
|
|
155
|
+
{t("chatGoConfigureProvider")}
|
|
156
|
+
</button>
|
|
135
157
|
</div>
|
|
136
158
|
)}
|
|
137
159
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
<ChatMessageListContainer
|
|
156
|
-
key={snapshot.selectedSessionKey ?? "draft"}
|
|
157
|
-
messages={snapshot.messages}
|
|
158
|
-
isSending={
|
|
159
|
-
snapshot.isSending && snapshot.isAwaitingAssistantOutput
|
|
160
|
-
}
|
|
160
|
+
{snapshot.sessionTypeUnavailable &&
|
|
161
|
+
snapshot.sessionTypeUnavailableMessage?.trim() && (
|
|
162
|
+
<div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 shrink-0">
|
|
163
|
+
<span className="text-xs text-amber-800">
|
|
164
|
+
{snapshot.sessionTypeUnavailableMessage}
|
|
165
|
+
</span>
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
<div
|
|
170
|
+
ref={threadRef}
|
|
171
|
+
onScroll={handleScroll}
|
|
172
|
+
className="flex-1 min-h-0 overflow-y-auto custom-scrollbar"
|
|
173
|
+
>
|
|
174
|
+
{showWelcome ? (
|
|
175
|
+
<ChatWelcome
|
|
176
|
+
onCreateSession={presenter.chatThreadManager.createSession}
|
|
161
177
|
/>
|
|
162
|
-
|
|
163
|
-
|
|
178
|
+
) : hideEmptyHint ? (
|
|
179
|
+
<div className="h-full" />
|
|
180
|
+
) : snapshot.messages.length === 0 ? (
|
|
181
|
+
<div className="px-5 py-5 text-sm text-gray-500">
|
|
182
|
+
{t("chatNoMessages")}
|
|
183
|
+
</div>
|
|
184
|
+
) : (
|
|
185
|
+
<div className="mx-auto w-full max-w-[min(1120px,100%)] px-6 py-5">
|
|
186
|
+
<ChatMessageListContainer
|
|
187
|
+
key={snapshot.sessionKey ?? "draft"}
|
|
188
|
+
messages={snapshot.messages}
|
|
189
|
+
isSending={
|
|
190
|
+
snapshot.isSending && snapshot.isAwaitingAssistantOutput
|
|
191
|
+
}
|
|
192
|
+
onToolAction={presenter.chatThreadManager.openSessionFromToolAction}
|
|
193
|
+
/>
|
|
194
|
+
</div>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<ChatInputBarContainer />
|
|
164
199
|
</div>
|
|
165
200
|
|
|
166
|
-
|
|
201
|
+
{detailSessionKey ? (
|
|
202
|
+
<ChatChildSessionPanel
|
|
203
|
+
sessionKey={detailSessionKey}
|
|
204
|
+
title={snapshot.childSessionDetailLabel}
|
|
205
|
+
onClose={presenter.chatThreadManager.closeChildSessionDetail}
|
|
206
|
+
onBackToParent={presenter.chatThreadManager.goToParentSession}
|
|
207
|
+
onToolAction={presenter.chatThreadManager.openSessionFromToolAction}
|
|
208
|
+
/>
|
|
209
|
+
) : null}
|
|
167
210
|
</section>
|
|
168
211
|
);
|
|
169
212
|
}
|
|
@@ -36,8 +36,8 @@ vi.mock('@/components/doc-browser', () => ({
|
|
|
36
36
|
})
|
|
37
37
|
}));
|
|
38
38
|
|
|
39
|
-
vi.mock('@/components/chat/chat-session-label
|
|
40
|
-
|
|
39
|
+
vi.mock('@/components/chat/hooks/use-chat-session-label', () => ({
|
|
40
|
+
useChatSessionLabel: () => async (params: {
|
|
41
41
|
sessionKey: string;
|
|
42
42
|
label: string | null;
|
|
43
43
|
}) => {
|
|
@@ -8,7 +8,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
|
|
|
8
8
|
import { SelectItem } from '@/components/ui/select';
|
|
9
9
|
import { ChatSidebarSessionItem } from '@/components/chat/chat-sidebar-session-item';
|
|
10
10
|
import { resolveSessionContextView } from '@/lib/session-context.utils';
|
|
11
|
-
import {
|
|
11
|
+
import { useChatSessionLabel } from '@/components/chat/hooks/use-chat-session-label';
|
|
12
12
|
import { useNcpSessionListView, type NcpSessionListItemView } from '@/components/chat/ncp/use-ncp-session-list-view';
|
|
13
13
|
import { usePresenter } from '@/components/chat/presenter/chat-presenter-context';
|
|
14
14
|
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
@@ -108,7 +108,7 @@ export function ChatSidebar() {
|
|
|
108
108
|
const { isLoading, items } = useNcpSessionListView();
|
|
109
109
|
const { language, setLanguage } = useI18n();
|
|
110
110
|
const { theme, setTheme } = useTheme();
|
|
111
|
-
const updateSessionLabel =
|
|
111
|
+
const updateSessionLabel = useChatSessionLabel();
|
|
112
112
|
const currentThemeLabel = t(THEME_OPTIONS.find((o) => o.value === theme)?.labelKey ?? 'themeWarm');
|
|
113
113
|
const currentLanguageLabel = LANGUAGE_OPTIONS.find((o) => o.value === language)?.label ?? language;
|
|
114
114
|
|
|
@@ -12,6 +12,7 @@ export type ChatThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' |
|
|
|
12
12
|
export type ChatSkillRecord = {
|
|
13
13
|
key: string;
|
|
14
14
|
label: string;
|
|
15
|
+
scopeLabel?: string;
|
|
15
16
|
description?: string;
|
|
16
17
|
descriptionZh?: string;
|
|
17
18
|
badgeLabel?: string;
|
|
@@ -43,6 +44,7 @@ const SLASH_ITEM_MATCH_SCORE = {
|
|
|
43
44
|
export type ChatInputBarAdapterTexts = {
|
|
44
45
|
slashSkillSubtitle: string;
|
|
45
46
|
slashSkillSpecLabel: string;
|
|
47
|
+
slashSkillScopeLabel: string;
|
|
46
48
|
noSkillDescription: string;
|
|
47
49
|
recentSkillsLabel: string;
|
|
48
50
|
allSkillsLabel: string;
|
|
@@ -171,7 +173,7 @@ function prioritizeSkillRecords(skillRecords: ChatSkillRecord[], recentSkillValu
|
|
|
171
173
|
export function buildChatSlashItems(
|
|
172
174
|
skillRecords: ChatSkillRecord[],
|
|
173
175
|
normalizedSlashQuery: string,
|
|
174
|
-
texts: Pick<ChatInputBarAdapterTexts, 'slashSkillSubtitle' | 'slashSkillSpecLabel' | 'noSkillDescription'>,
|
|
176
|
+
texts: Pick<ChatInputBarAdapterTexts, 'slashSkillSubtitle' | 'slashSkillSpecLabel' | 'slashSkillScopeLabel' | 'noSkillDescription'>,
|
|
175
177
|
recentSkillValues: string[] = []
|
|
176
178
|
): ChatSlashItem[] {
|
|
177
179
|
const skillSortCollator = new Intl.Collator(undefined, { sensitivity: 'base', numeric: true });
|
|
@@ -211,7 +213,10 @@ export function buildChatSlashItems(
|
|
|
211
213
|
title: record.label || record.key,
|
|
212
214
|
subtitle: texts.slashSkillSubtitle,
|
|
213
215
|
description: (record.descriptionZh ?? record.description ?? '').trim() || texts.noSkillDescription,
|
|
214
|
-
detailLines: [
|
|
216
|
+
detailLines: [
|
|
217
|
+
`${texts.slashSkillSpecLabel}: ${record.key}`,
|
|
218
|
+
...(record.scopeLabel ? [`${texts.slashSkillScopeLabel}: ${record.scopeLabel}`] : [])
|
|
219
|
+
],
|
|
215
220
|
value: record.key
|
|
216
221
|
}));
|
|
217
222
|
}
|
|
@@ -3,15 +3,13 @@ import {
|
|
|
3
3
|
summarizeToolArgs,
|
|
4
4
|
type ToolCard,
|
|
5
5
|
} from "@/lib/chat-message";
|
|
6
|
-
import {
|
|
7
|
-
type ChatInlineTokenSource,
|
|
8
|
-
} from "@/components/chat/chat-inline-token.utils";
|
|
6
|
+
import { type ChatInlineTokenSource } from "@/components/chat/chat-inline-token.utils";
|
|
9
7
|
import {
|
|
10
8
|
buildRenderableText,
|
|
11
9
|
buildTextPart,
|
|
12
10
|
} from "@/components/chat/adapters/chat-message-inline-content.adapter";
|
|
13
|
-
import { buildFileOperationCardData } from "@/components/chat/adapters/
|
|
14
|
-
import {
|
|
11
|
+
import { buildFileOperationCardData } from "@/components/chat/adapters/file-operation/card";
|
|
12
|
+
import { buildSessionRequestToolCard } from "@/components/chat/adapters/chat-message.session-request-tool-card";
|
|
15
13
|
import type {
|
|
16
14
|
ChatMessagePartViewModel,
|
|
17
15
|
ChatToolPartViewModel,
|
|
@@ -79,6 +77,7 @@ export type ChatMessagePartSource =
|
|
|
79
77
|
type ToolCardViewSource = ToolCard & {
|
|
80
78
|
statusTone: ChatToolPartViewModel["statusTone"];
|
|
81
79
|
statusLabel: string;
|
|
80
|
+
action?: ChatToolPartViewModel["action"];
|
|
82
81
|
fileOperation?: ChatToolPartViewModel["fileOperation"];
|
|
83
82
|
outputData?: unknown;
|
|
84
83
|
};
|
|
@@ -116,7 +115,9 @@ function readOptionalNumber(value: unknown): number | null {
|
|
|
116
115
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
function isTerminalResultRecord(
|
|
118
|
+
function isTerminalResultRecord(
|
|
119
|
+
value: unknown,
|
|
120
|
+
): value is Record<string, unknown> {
|
|
120
121
|
if (!isRecord(value)) {
|
|
121
122
|
return false;
|
|
122
123
|
}
|
|
@@ -181,9 +182,10 @@ function buildToolCard(
|
|
|
181
182
|
toolName: toolCard.name,
|
|
182
183
|
summary: toolCard.detail,
|
|
183
184
|
inputLabel: texts.toolInputLabel,
|
|
184
|
-
input:
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
input:
|
|
186
|
+
"input" in toolCard && typeof toolCard.input === "string"
|
|
187
|
+
? toolCard.input
|
|
188
|
+
: undefined,
|
|
187
189
|
output: toolCard.text,
|
|
188
190
|
outputData: toolCard.outputData,
|
|
189
191
|
hasResult: Boolean(toolCard.hasResult),
|
|
@@ -193,6 +195,9 @@ function buildToolCard(
|
|
|
193
195
|
toolCard.kind === "call" ? texts.toolCallLabel : texts.toolResultLabel,
|
|
194
196
|
outputLabel: texts.toolOutputLabel,
|
|
195
197
|
emptyLabel: texts.toolNoOutputLabel,
|
|
198
|
+
...("action" in toolCard && toolCard.action
|
|
199
|
+
? { action: toolCard.action }
|
|
200
|
+
: {}),
|
|
196
201
|
...("fileOperation" in toolCard && toolCard.fileOperation
|
|
197
202
|
? { fileOperation: toolCard.fileOperation }
|
|
198
203
|
: {}),
|
|
@@ -269,7 +274,10 @@ function parseStructuredValue(value: unknown): unknown {
|
|
|
269
274
|
}
|
|
270
275
|
}
|
|
271
276
|
|
|
272
|
-
function buildToolInvocationInput(
|
|
277
|
+
function buildToolInvocationInput(
|
|
278
|
+
args?: unknown,
|
|
279
|
+
parsedArgs?: unknown,
|
|
280
|
+
): string | undefined {
|
|
273
281
|
const source = parsedArgs ?? parseStructuredValue(args);
|
|
274
282
|
const text = stringifyUnknown(source).trim();
|
|
275
283
|
return text || undefined;
|
|
@@ -326,14 +334,18 @@ function buildToolInvocationPart(
|
|
|
326
334
|
return assetFileView;
|
|
327
335
|
}
|
|
328
336
|
|
|
329
|
-
const
|
|
337
|
+
const sessionRequestToolCard = buildSessionRequestToolCard({
|
|
330
338
|
invocation,
|
|
331
|
-
texts
|
|
339
|
+
texts: {
|
|
340
|
+
toolStatusRunningLabel: texts.toolStatusRunningLabel,
|
|
341
|
+
toolStatusCompletedLabel: texts.toolStatusCompletedLabel,
|
|
342
|
+
toolStatusFailedLabel: texts.toolStatusFailedLabel,
|
|
343
|
+
},
|
|
332
344
|
});
|
|
333
|
-
if (
|
|
345
|
+
if (sessionRequestToolCard) {
|
|
334
346
|
return {
|
|
335
347
|
type: "tool-card",
|
|
336
|
-
card: buildToolCard(
|
|
348
|
+
card: buildToolCard(sessionRequestToolCard, texts),
|
|
337
349
|
};
|
|
338
350
|
}
|
|
339
351
|
|