@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.
Files changed (87) hide show
  1. package/CHANGELOG.md +16 -2
  2. package/dist/assets/{ChannelsList-BqsOYnXz.js → ChannelsList-CKl1Zg8f.js} +3 -3
  3. package/dist/assets/ChatPage-BJgO27mk.js +37 -0
  4. package/dist/assets/{DocBrowser-BmL0QXBZ.js → DocBrowser-DYRBs4-z.js} +1 -1
  5. package/dist/assets/{LogoBadge-C1HiPZPf.js → LogoBadge-33Qlv3Hg.js} +1 -1
  6. package/dist/assets/MarketplacePage-B8BZVtjV.js +49 -0
  7. package/dist/assets/{McpMarketplacePage-CLHFnNBd.js → McpMarketplacePage-BRuE5fJJ.js} +2 -2
  8. package/dist/assets/{ModelConfig-LQSR58tc.js → ModelConfig-BiFblwO-.js} +1 -1
  9. package/dist/assets/ProvidersList-9goRgHE4.js +1 -0
  10. package/dist/assets/RemoteAccessPage-5vCxZPS6.js +1 -0
  11. package/dist/assets/RuntimeConfig-BmDFHBdW.js +1 -0
  12. package/dist/assets/{SearchConfig-Chzo_JGs.js → SearchConfig-CJx5CKwG.js} +1 -1
  13. package/dist/assets/{SecretsConfig-CEIbjZYA.js → SecretsConfig-B91efXoK.js} +2 -2
  14. package/dist/assets/SessionsConfig-CbFPVmx3.js +2 -0
  15. package/dist/assets/index-BtAuUyww.css +1 -0
  16. package/dist/assets/index-COJomMe9.js +8 -0
  17. package/dist/assets/{label-GACO2RzW.js → label-BnSDpjhL.js} +1 -1
  18. package/dist/assets/ncp-session-adapter-w8ZHprab.js +1 -0
  19. package/dist/assets/{page-layout-DjXaK3A3.js → page-layout-B1RIu5-r.js} +1 -1
  20. package/dist/assets/popover-ChzbCIfO.js +1 -0
  21. package/dist/assets/security-config-eYa6Ovfa.js +1 -0
  22. package/dist/assets/skeleton-D4Eyop0R.js +1 -0
  23. package/dist/assets/{status-dot-IWEBezqb.js → status-dot-CrCw5tkJ.js} +1 -1
  24. package/dist/assets/{switch-DCHAJSrA.js → switch-C3vVTpfU.js} +1 -1
  25. package/dist/assets/tabs-custom-Ilrgt6n1.js +1 -0
  26. package/dist/assets/useConfirmDialog-BeaFLDO8.js +1 -0
  27. package/dist/assets/{vendor-CNhxtHCf.js → vendor-waGu-koL.js} +101 -86
  28. package/dist/index.html +3 -3
  29. package/package.json +5 -5
  30. package/src/App.test.tsx +42 -10
  31. package/src/App.tsx +5 -40
  32. package/src/api/api-base.test.ts +37 -0
  33. package/src/api/api-base.ts +0 -4
  34. package/src/api/config.ts +2 -270
  35. package/src/api/types.ts +0 -117
  36. package/src/components/chat/ChatPage.tsx +1 -11
  37. package/src/components/chat/ChatSidebar.test.tsx +1 -50
  38. package/src/components/chat/ChatSidebar.tsx +0 -5
  39. package/src/components/chat/README.md +2 -0
  40. package/src/components/chat/adapters/chat-message.adapter.test.ts +71 -4
  41. package/src/components/chat/adapters/chat-message.adapter.ts +195 -78
  42. package/src/components/chat/chat-attachment-upload-limit.test.ts +41 -0
  43. package/src/components/chat/chat-session-display.ts +9 -0
  44. package/src/components/chat/chat-session-label.service.ts +3 -12
  45. package/src/components/chat/chat-session-preference-sync.test.ts +10 -13
  46. package/src/components/chat/chat-stream/types.ts +4 -57
  47. package/src/components/chat/containers/chat-message-list.container.tsx +7 -0
  48. package/src/components/chat/ncp/NcpChatPage.tsx +3 -3
  49. package/src/components/chat/useHydratedNcpAgent.test.tsx +77 -0
  50. package/src/components/config/README.md +2 -0
  51. package/src/components/config/SessionsConfig.tsx +152 -132
  52. package/src/hooks/use-auth.test.ts +3 -3
  53. package/src/hooks/use-auth.ts +16 -4
  54. package/src/hooks/use-realtime-query-bridge.ts +0 -24
  55. package/src/hooks/useConfig.ts +10 -137
  56. package/src/lib/i18n.chat.ts +7 -0
  57. package/src/lib/session-run-status.ts +1 -63
  58. package/src/vite-env.d.ts +1 -0
  59. package/vite.config.ts +4 -4
  60. package/dist/assets/ChatPage-CJBYKR-Y.js +0 -38
  61. package/dist/assets/MarketplacePage-BIRP0NRS.js +0 -49
  62. package/dist/assets/ProvidersList-CwI-mxah.js +0 -1
  63. package/dist/assets/RemoteAccessPage-Cw5BqZb6.js +0 -1
  64. package/dist/assets/RuntimeConfig-DbowSRAb.js +0 -1
  65. package/dist/assets/SessionsConfig-BR8GfGWL.js +0 -2
  66. package/dist/assets/chat-message-CPG7zxRR.js +0 -3
  67. package/dist/assets/index-j6A_-1b6.js +0 -8
  68. package/dist/assets/index-kaPUhd-8.css +0 -1
  69. package/dist/assets/popover-DTaFiTmU.js +0 -1
  70. package/dist/assets/security-config-Dk-yoKvK.js +0 -1
  71. package/dist/assets/skeleton-Dm2xOBSA.js +0 -1
  72. package/dist/assets/tabs-custom-DKSbDSB9.js +0 -1
  73. package/dist/assets/useConfirmDialog-ByJ8A8n7.js +0 -1
  74. package/src/api/config.stream.test.ts +0 -115
  75. package/src/components/chat/chat-chain.test.ts +0 -22
  76. package/src/components/chat/chat-chain.ts +0 -23
  77. package/src/components/chat/chat-page-data.ts +0 -171
  78. package/src/components/chat/chat-page-runtime.ts +0 -190
  79. package/src/components/chat/chat-stream/nextbot-parsers.ts +0 -52
  80. package/src/components/chat/chat-stream/nextbot-runtime-agent.ts +0 -413
  81. package/src/components/chat/chat-stream/stream-event-adapter.ts +0 -98
  82. package/src/components/chat/chat-stream/transport.ts +0 -253
  83. package/src/components/chat/legacy/LegacyChatPage.tsx +0 -223
  84. package/src/components/chat/managers/chat-input.manager.ts +0 -228
  85. package/src/components/chat/managers/chat-thread.manager.ts +0 -87
  86. package/src/components/chat/presenter/chat.presenter.ts +0 -32
  87. 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
- const location = useLocation();
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
  });
@@ -0,0 +1,2 @@
1
+ ## 目录预算豁免
2
+ - 原因:聊天入口层需要并列承载页面壳、会话侧栏、会话偏好同步、展示组件与配套测试,且已通过 `ncp/` 等子目录承接更细分的实现,短期内仍需保留这组扁平入口文件。
@@ -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: "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2F2026%2F03%2F27%2Fasset_1",
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
  });