@nextclaw/ui 0.11.0 → 0.11.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 +16 -2
- package/dist/assets/{ChannelsList-BqsOYnXz.js → ChannelsList-CKl1Zg8f.js} +3 -3
- package/dist/assets/ChatPage-BJgO27mk.js +37 -0
- package/dist/assets/{DocBrowser-BmL0QXBZ.js → DocBrowser-DYRBs4-z.js} +1 -1
- package/dist/assets/{LogoBadge-C1HiPZPf.js → LogoBadge-33Qlv3Hg.js} +1 -1
- package/dist/assets/MarketplacePage-B8BZVtjV.js +49 -0
- package/dist/assets/{McpMarketplacePage-CLHFnNBd.js → McpMarketplacePage-BRuE5fJJ.js} +2 -2
- package/dist/assets/{ModelConfig-LQSR58tc.js → ModelConfig-BiFblwO-.js} +1 -1
- package/dist/assets/ProvidersList-9goRgHE4.js +1 -0
- package/dist/assets/RemoteAccessPage-5vCxZPS6.js +1 -0
- package/dist/assets/RuntimeConfig-BmDFHBdW.js +1 -0
- package/dist/assets/{SearchConfig-Chzo_JGs.js → SearchConfig-CJx5CKwG.js} +1 -1
- package/dist/assets/{SecretsConfig-CEIbjZYA.js → SecretsConfig-B91efXoK.js} +2 -2
- package/dist/assets/SessionsConfig-CbFPVmx3.js +2 -0
- package/dist/assets/index-BtAuUyww.css +1 -0
- package/dist/assets/index-COJomMe9.js +8 -0
- package/dist/assets/{label-GACO2RzW.js → label-BnSDpjhL.js} +1 -1
- package/dist/assets/ncp-session-adapter-w8ZHprab.js +1 -0
- package/dist/assets/{page-layout-DjXaK3A3.js → page-layout-B1RIu5-r.js} +1 -1
- package/dist/assets/popover-ChzbCIfO.js +1 -0
- package/dist/assets/security-config-eYa6Ovfa.js +1 -0
- package/dist/assets/skeleton-D4Eyop0R.js +1 -0
- package/dist/assets/{status-dot-IWEBezqb.js → status-dot-CrCw5tkJ.js} +1 -1
- package/dist/assets/{switch-DCHAJSrA.js → switch-C3vVTpfU.js} +1 -1
- package/dist/assets/tabs-custom-Ilrgt6n1.js +1 -0
- package/dist/assets/useConfirmDialog-BeaFLDO8.js +1 -0
- package/dist/assets/{vendor-CNhxtHCf.js → vendor-waGu-koL.js} +101 -86
- package/dist/index.html +3 -3
- package/package.json +5 -5
- package/src/App.test.tsx +42 -10
- package/src/App.tsx +5 -40
- package/src/api/api-base.test.ts +37 -0
- package/src/api/api-base.ts +0 -4
- package/src/api/config.ts +2 -270
- package/src/api/types.ts +0 -117
- package/src/components/chat/ChatPage.tsx +1 -11
- package/src/components/chat/ChatSidebar.test.tsx +1 -50
- package/src/components/chat/ChatSidebar.tsx +0 -5
- package/src/components/chat/README.md +2 -0
- package/src/components/chat/adapters/chat-message.adapter.test.ts +71 -4
- package/src/components/chat/adapters/chat-message.adapter.ts +195 -78
- package/src/components/chat/chat-attachment-upload-limit.test.ts +41 -0
- package/src/components/chat/chat-session-display.ts +9 -0
- package/src/components/chat/chat-session-label.service.ts +3 -12
- package/src/components/chat/chat-session-preference-sync.test.ts +10 -13
- package/src/components/chat/chat-stream/types.ts +4 -57
- package/src/components/chat/containers/chat-message-list.container.tsx +7 -0
- package/src/components/chat/ncp/NcpChatPage.tsx +3 -3
- package/src/components/chat/useHydratedNcpAgent.test.tsx +77 -0
- package/src/components/config/README.md +2 -0
- package/src/components/config/SessionsConfig.tsx +152 -132
- package/src/hooks/use-auth.test.ts +3 -3
- package/src/hooks/use-auth.ts +16 -4
- package/src/hooks/use-realtime-query-bridge.ts +0 -24
- package/src/hooks/useConfig.ts +10 -137
- package/src/lib/i18n.chat.ts +7 -0
- package/src/lib/session-run-status.ts +1 -63
- package/src/vite-env.d.ts +1 -0
- package/vite.config.ts +4 -4
- package/dist/assets/ChatPage-CJBYKR-Y.js +0 -38
- package/dist/assets/MarketplacePage-BIRP0NRS.js +0 -49
- package/dist/assets/ProvidersList-CwI-mxah.js +0 -1
- package/dist/assets/RemoteAccessPage-Cw5BqZb6.js +0 -1
- package/dist/assets/RuntimeConfig-DbowSRAb.js +0 -1
- package/dist/assets/SessionsConfig-BR8GfGWL.js +0 -2
- package/dist/assets/chat-message-CPG7zxRR.js +0 -3
- package/dist/assets/index-j6A_-1b6.js +0 -8
- package/dist/assets/index-kaPUhd-8.css +0 -1
- package/dist/assets/popover-DTaFiTmU.js +0 -1
- package/dist/assets/security-config-Dk-yoKvK.js +0 -1
- package/dist/assets/skeleton-Dm2xOBSA.js +0 -1
- package/dist/assets/tabs-custom-DKSbDSB9.js +0 -1
- package/dist/assets/useConfirmDialog-ByJ8A8n7.js +0 -1
- package/src/api/config.stream.test.ts +0 -115
- package/src/components/chat/chat-chain.test.ts +0 -22
- package/src/components/chat/chat-chain.ts +0 -23
- package/src/components/chat/chat-page-data.ts +0 -171
- package/src/components/chat/chat-page-runtime.ts +0 -190
- package/src/components/chat/chat-stream/nextbot-parsers.ts +0 -52
- package/src/components/chat/chat-stream/nextbot-runtime-agent.ts +0 -413
- package/src/components/chat/chat-stream/stream-event-adapter.ts +0 -98
- package/src/components/chat/chat-stream/transport.ts +0 -253
- package/src/components/chat/legacy/LegacyChatPage.tsx +0 -223
- package/src/components/chat/managers/chat-input.manager.ts +0 -228
- package/src/components/chat/managers/chat-thread.manager.ts +0 -87
- package/src/components/chat/presenter/chat.presenter.ts +0 -32
- package/src/components/chat/useChatRuntimeController.ts +0 -134
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
import { useLocation } from 'react-router-dom';
|
|
2
|
-
import { resolveChatChain } from '@/components/chat/chat-chain';
|
|
3
1
|
import type { ChatPageProps } from '@/components/chat/chat-page-shell';
|
|
4
|
-
import { LegacyChatPage } from '@/components/chat/legacy/LegacyChatPage';
|
|
5
2
|
import { NcpChatPage } from '@/components/chat/ncp/NcpChatPage';
|
|
6
3
|
|
|
7
4
|
export function ChatPage({ view }: ChatPageProps) {
|
|
8
|
-
|
|
9
|
-
const chatChain = resolveChatChain(location.search);
|
|
10
|
-
|
|
11
|
-
if (chatChain === 'ncp') {
|
|
12
|
-
return <NcpChatPage view={view} />;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return <LegacyChatPage view={view} />;
|
|
5
|
+
return <NcpChatPage view={view} />;
|
|
16
6
|
}
|
|
@@ -11,7 +11,6 @@ const mocks = vi.hoisted(() => ({
|
|
|
11
11
|
setQuery: vi.fn(),
|
|
12
12
|
selectSession: vi.fn(),
|
|
13
13
|
docOpen: vi.fn(),
|
|
14
|
-
updateSession: vi.fn(),
|
|
15
14
|
updateNcpSession: vi.fn()
|
|
16
15
|
}));
|
|
17
16
|
|
|
@@ -33,15 +32,9 @@ vi.mock('@/components/doc-browser', () => ({
|
|
|
33
32
|
|
|
34
33
|
vi.mock('@/components/chat/chat-session-label.service', () => ({
|
|
35
34
|
useChatSessionLabelService: () => async (params: {
|
|
36
|
-
chatChain: 'legacy' | 'ncp';
|
|
37
35
|
sessionKey: string;
|
|
38
36
|
label: string | null;
|
|
39
|
-
}) => {
|
|
40
|
-
if (params.chatChain === 'ncp') {
|
|
41
|
-
return mocks.updateNcpSession(params.sessionKey, { label: params.label });
|
|
42
|
-
}
|
|
43
|
-
return mocks.updateSession(params.sessionKey, { label: params.label });
|
|
44
|
-
}
|
|
37
|
+
}) => mocks.updateNcpSession(params.sessionKey, { label: params.label })
|
|
45
38
|
}));
|
|
46
39
|
|
|
47
40
|
vi.mock('@/components/common/BrandHeader', () => ({
|
|
@@ -77,9 +70,7 @@ describe('ChatSidebar', () => {
|
|
|
77
70
|
mocks.setQuery.mockReset();
|
|
78
71
|
mocks.selectSession.mockReset();
|
|
79
72
|
mocks.docOpen.mockReset();
|
|
80
|
-
mocks.updateSession.mockReset();
|
|
81
73
|
mocks.updateNcpSession.mockReset();
|
|
82
|
-
mocks.updateSession.mockResolvedValue({});
|
|
83
74
|
mocks.updateNcpSession.mockResolvedValue({});
|
|
84
75
|
|
|
85
76
|
useChatInputStore.setState({
|
|
@@ -285,48 +276,9 @@ describe('ChatSidebar', () => {
|
|
|
285
276
|
label: 'Renamed Label'
|
|
286
277
|
});
|
|
287
278
|
});
|
|
288
|
-
expect(mocks.updateSession).not.toHaveBeenCalled();
|
|
289
279
|
expect(screen.getByText('Renamed Label')).not.toBeNull();
|
|
290
280
|
});
|
|
291
281
|
|
|
292
|
-
it('routes inline session label edits to the legacy session api when chatChain=legacy', async () => {
|
|
293
|
-
useChatSessionListStore.setState({
|
|
294
|
-
snapshot: {
|
|
295
|
-
...useChatSessionListStore.getState().snapshot,
|
|
296
|
-
sessions: [
|
|
297
|
-
{
|
|
298
|
-
key: 'session:legacy-1',
|
|
299
|
-
createdAt: '2026-03-19T09:00:00.000Z',
|
|
300
|
-
updatedAt: '2026-03-19T09:05:00.000Z',
|
|
301
|
-
label: 'Legacy Label',
|
|
302
|
-
sessionType: 'native',
|
|
303
|
-
sessionTypeMutable: false,
|
|
304
|
-
messageCount: 1
|
|
305
|
-
}
|
|
306
|
-
]
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
render(
|
|
311
|
-
<MemoryRouter initialEntries={['/chat?chatChain=legacy']}>
|
|
312
|
-
<ChatSidebar />
|
|
313
|
-
</MemoryRouter>
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
fireEvent.click(screen.getByLabelText('Edit'));
|
|
317
|
-
fireEvent.change(screen.getByPlaceholderText('Session label (optional)'), {
|
|
318
|
-
target: { value: 'Legacy Renamed' }
|
|
319
|
-
});
|
|
320
|
-
fireEvent.click(screen.getByLabelText('Save'));
|
|
321
|
-
|
|
322
|
-
await waitFor(() => {
|
|
323
|
-
expect(mocks.updateSession).toHaveBeenCalledWith('session:legacy-1', {
|
|
324
|
-
label: 'Legacy Renamed'
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
expect(mocks.updateNcpSession).not.toHaveBeenCalled();
|
|
328
|
-
});
|
|
329
|
-
|
|
330
282
|
it('cancels inline session label editing without saving', () => {
|
|
331
283
|
useChatSessionListStore.setState({
|
|
332
284
|
snapshot: {
|
|
@@ -357,7 +309,6 @@ describe('ChatSidebar', () => {
|
|
|
357
309
|
});
|
|
358
310
|
fireEvent.click(screen.getByLabelText('Cancel'));
|
|
359
311
|
|
|
360
|
-
expect(mocks.updateSession).not.toHaveBeenCalled();
|
|
361
312
|
expect(mocks.updateNcpSession).not.toHaveBeenCalled();
|
|
362
313
|
expect(screen.queryByDisplayValue('Should Not Persist')).toBeNull();
|
|
363
314
|
expect(screen.getByText('Cancelable Label')).not.toBeNull();
|
|
@@ -12,7 +12,6 @@ import { usePresenter } from '@/components/chat/presenter/chat-presenter-context
|
|
|
12
12
|
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
13
13
|
import { useChatRunStatusStore } from '@/components/chat/stores/chat-run-status.store';
|
|
14
14
|
import { useChatSessionListStore } from '@/components/chat/stores/chat-session-list.store';
|
|
15
|
-
import { resolveChatChain } from '@/components/chat/chat-chain';
|
|
16
15
|
import { cn } from '@/lib/utils';
|
|
17
16
|
import { LANGUAGE_OPTIONS, t, type I18nLanguage } from '@/lib/i18n';
|
|
18
17
|
import { THEME_OPTIONS, type UiTheme } from '@/lib/theme';
|
|
@@ -21,7 +20,6 @@ import { useTheme } from '@/components/providers/ThemeProvider';
|
|
|
21
20
|
import { useDocBrowser } from '@/components/doc-browser';
|
|
22
21
|
import { SidebarActionItem, SidebarNavLinkItem, SidebarSelectItem } from '@/components/layout/sidebar-items';
|
|
23
22
|
import { useUiStore } from '@/stores/ui.store';
|
|
24
|
-
import { useLocation } from 'react-router-dom';
|
|
25
23
|
import {
|
|
26
24
|
AlarmClock,
|
|
27
25
|
BookOpen,
|
|
@@ -117,7 +115,6 @@ const navItems = [
|
|
|
117
115
|
export function ChatSidebar() {
|
|
118
116
|
const presenter = usePresenter();
|
|
119
117
|
const docBrowser = useDocBrowser();
|
|
120
|
-
const location = useLocation();
|
|
121
118
|
const [isCreateMenuOpen, setIsCreateMenuOpen] = useState(false);
|
|
122
119
|
const [editingSessionKey, setEditingSessionKey] = useState<string | null>(null);
|
|
123
120
|
const [draftLabel, setDraftLabel] = useState('');
|
|
@@ -129,7 +126,6 @@ export function ChatSidebar() {
|
|
|
129
126
|
const { language, setLanguage } = useI18n();
|
|
130
127
|
const { theme, setTheme } = useTheme();
|
|
131
128
|
const updateSessionLabel = useChatSessionLabelService();
|
|
132
|
-
const chatChain = resolveChatChain(location.search);
|
|
133
129
|
const currentThemeLabel = t(THEME_OPTIONS.find((o) => o.value === theme)?.labelKey ?? 'themeWarm');
|
|
134
130
|
const currentLanguageLabel = LANGUAGE_OPTIONS.find((o) => o.value === language)?.label ?? language;
|
|
135
131
|
|
|
@@ -182,7 +178,6 @@ export function ChatSidebar() {
|
|
|
182
178
|
setSavingSessionKey(session.key);
|
|
183
179
|
try {
|
|
184
180
|
await updateSessionLabel({
|
|
185
|
-
chatChain,
|
|
186
181
|
sessionKey: session.key,
|
|
187
182
|
label: normalizedLabel || null
|
|
188
183
|
});
|
|
@@ -17,8 +17,15 @@ const defaultTexts = {
|
|
|
17
17
|
reasoningLabel: "Reasoning",
|
|
18
18
|
toolCallLabel: "Tool Call",
|
|
19
19
|
toolResultLabel: "Tool Result",
|
|
20
|
+
toolInputLabel: "Input Summary",
|
|
21
|
+
toolCallIdLabel: "Call ID",
|
|
20
22
|
toolNoOutputLabel: "No output",
|
|
21
23
|
toolOutputLabel: "View Output",
|
|
24
|
+
toolStatusPreparingLabel: "Preparing",
|
|
25
|
+
toolStatusRunningLabel: "Running",
|
|
26
|
+
toolStatusCompletedLabel: "Completed",
|
|
27
|
+
toolStatusFailedLabel: "Failed",
|
|
28
|
+
toolStatusCancelledLabel: "Cancelled",
|
|
22
29
|
imageAttachmentLabel: "Image attachment",
|
|
23
30
|
fileAttachmentLabel: "File attachment",
|
|
24
31
|
unknownPartLabel: "Unknown Part",
|
|
@@ -66,9 +73,7 @@ it("maps markdown, reasoning, and tool parts into UI view models", () => {
|
|
|
66
73
|
|
|
67
74
|
expect(adapted).toHaveLength(1);
|
|
68
75
|
expect(adapted[0]?.roleLabel).toBe("Assistant");
|
|
69
|
-
expect(adapted[0]?.timestampLabel).toBe(
|
|
70
|
-
"formatted:2026-03-17T10:00:00.000Z",
|
|
71
|
-
);
|
|
76
|
+
expect(adapted[0]?.timestampLabel).toBe("formatted:2026-03-17T10:00:00.000Z");
|
|
72
77
|
expect(adapted[0]?.parts.map((part) => part.type)).toEqual([
|
|
73
78
|
"markdown",
|
|
74
79
|
"reasoning",
|
|
@@ -82,12 +87,67 @@ it("maps markdown, reasoning, and tool parts into UI view models", () => {
|
|
|
82
87
|
expect(adapted[0]?.parts[2]).toMatchObject({
|
|
83
88
|
type: "tool-card",
|
|
84
89
|
card: {
|
|
90
|
+
callId: "call-1",
|
|
91
|
+
callIdLabel: "Call ID",
|
|
92
|
+
inputLabel: "Input Summary",
|
|
93
|
+
statusLabel: "Completed",
|
|
94
|
+
statusTone: "success",
|
|
85
95
|
titleLabel: "Tool Result",
|
|
86
96
|
outputLabel: "View Output",
|
|
87
97
|
},
|
|
88
98
|
});
|
|
89
99
|
});
|
|
90
100
|
|
|
101
|
+
it("maps tool lifecycle statuses into visible card state feedback", () => {
|
|
102
|
+
const adapted = adapt([
|
|
103
|
+
{
|
|
104
|
+
id: "assistant-tool-statuses",
|
|
105
|
+
role: "assistant",
|
|
106
|
+
parts: [
|
|
107
|
+
{
|
|
108
|
+
type: "tool-invocation",
|
|
109
|
+
toolInvocation: {
|
|
110
|
+
status: ToolInvocationStatus.PARTIAL_CALL,
|
|
111
|
+
toolCallId: "call-prep",
|
|
112
|
+
toolName: "web_search",
|
|
113
|
+
args: '{"q":"latest"}',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: "tool-invocation",
|
|
118
|
+
toolInvocation: {
|
|
119
|
+
status: ToolInvocationStatus.ERROR,
|
|
120
|
+
toolCallId: "call-error",
|
|
121
|
+
toolName: "exec_command",
|
|
122
|
+
args: '{"cmd":"exit 1"}',
|
|
123
|
+
error: "Command failed",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
] as unknown as ChatMessageSource[]);
|
|
129
|
+
|
|
130
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
131
|
+
type: "tool-card",
|
|
132
|
+
card: {
|
|
133
|
+
statusTone: "running",
|
|
134
|
+
statusLabel: "Preparing",
|
|
135
|
+
titleLabel: "Tool Call",
|
|
136
|
+
callId: "call-prep",
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
expect(adapted[0]?.parts[1]).toMatchObject({
|
|
140
|
+
type: "tool-card",
|
|
141
|
+
card: {
|
|
142
|
+
statusTone: "error",
|
|
143
|
+
statusLabel: "Failed",
|
|
144
|
+
titleLabel: "Tool Result",
|
|
145
|
+
output: "Command failed",
|
|
146
|
+
callId: "call-error",
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
91
151
|
it("maps non-standard roles back to the generic message role", () => {
|
|
92
152
|
const adapted = adapt([
|
|
93
153
|
{
|
|
@@ -149,6 +209,7 @@ it("maps file parts into previewable attachment view models", () => {
|
|
|
149
209
|
type: "file",
|
|
150
210
|
mimeType: "image/png",
|
|
151
211
|
data: "ZmFrZS1pbWFnZQ==",
|
|
212
|
+
sizeBytes: 4096,
|
|
152
213
|
},
|
|
153
214
|
],
|
|
154
215
|
},
|
|
@@ -160,6 +221,7 @@ it("maps file parts into previewable attachment view models", () => {
|
|
|
160
221
|
label: "Image attachment",
|
|
161
222
|
mimeType: "image/png",
|
|
162
223
|
dataUrl: "data:image/png;base64,ZmFrZS1pbWFnZQ==",
|
|
224
|
+
sizeBytes: 4096,
|
|
163
225
|
isImage: true,
|
|
164
226
|
},
|
|
165
227
|
});
|
|
@@ -176,6 +238,7 @@ it("keeps named non-image files as downloadable attachments", () => {
|
|
|
176
238
|
name: "spec.pdf",
|
|
177
239
|
mimeType: "application/pdf",
|
|
178
240
|
data: "cGRm",
|
|
241
|
+
sizeBytes: 2048,
|
|
179
242
|
},
|
|
180
243
|
],
|
|
181
244
|
},
|
|
@@ -187,6 +250,7 @@ it("keeps named non-image files as downloadable attachments", () => {
|
|
|
187
250
|
label: "spec.pdf",
|
|
188
251
|
mimeType: "application/pdf",
|
|
189
252
|
dataUrl: "data:application/pdf;base64,cGRm",
|
|
253
|
+
sizeBytes: 2048,
|
|
190
254
|
isImage: false,
|
|
191
255
|
},
|
|
192
256
|
});
|
|
@@ -212,6 +276,7 @@ it("renders asset tool results as previewable files", () => {
|
|
|
212
276
|
name: "output.png",
|
|
213
277
|
mimeType: "image/png",
|
|
214
278
|
url: "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2F2026%2F03%2F27%2Fasset_1",
|
|
279
|
+
sizeBytes: 5120,
|
|
215
280
|
},
|
|
216
281
|
},
|
|
217
282
|
},
|
|
@@ -225,7 +290,9 @@ it("renders asset tool results as previewable files", () => {
|
|
|
225
290
|
file: {
|
|
226
291
|
label: "output.png",
|
|
227
292
|
mimeType: "image/png",
|
|
228
|
-
dataUrl:
|
|
293
|
+
dataUrl:
|
|
294
|
+
"/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2F2026%2F03%2F27%2Fasset_1",
|
|
295
|
+
sizeBytes: 5120,
|
|
229
296
|
isImage: true,
|
|
230
297
|
},
|
|
231
298
|
});
|