@nextclaw/ui 0.12.25 → 0.12.26

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 (42) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/assets/{channels-list-page-FJDuPwU6.js → channels-list-page-HgLgrEg4.js} +1 -1
  3. package/dist/assets/chat-page-DAKMFDrS.js +1 -0
  4. package/dist/assets/{desktop-kk7qvZ-v.js → desktop-DVUbOWbR.js} +1 -1
  5. package/dist/assets/{index-D-AAMKCt.js → index-Cuwst6cc.js} +34 -37
  6. package/dist/assets/index-dlcqieQ0.css +1 -0
  7. package/dist/assets/{marketplace-page-BrCLRIc4.js → marketplace-page-BeFbwxR-.js} +2 -2
  8. package/dist/assets/marketplace-page-CR4xq-TM.js +1 -0
  9. package/dist/assets/mcp-marketplace-page-DlRrSCj3.js +1 -0
  10. package/dist/assets/{mcp-marketplace-page-DIq_SpMe.js → mcp-marketplace-page-DwnaLNTx.js} +1 -1
  11. package/dist/assets/{model-config-Bc6VVnxy.js → model-config-L2l6YAlQ.js} +1 -1
  12. package/dist/assets/{providers-list-DN0tvISH.js → providers-list-DYAEunOp.js} +1 -1
  13. package/dist/assets/{runtime-config-page-CRWOwBbl.js → runtime-config-page-BdeU8PEK.js} +1 -1
  14. package/dist/assets/{search-config-C4c1yZSP.js → search-config-CQUhd5RU.js} +1 -1
  15. package/dist/assets/{secrets-config-zAF30YfO.js → secrets-config-D-NWlW9q.js} +1 -1
  16. package/dist/assets/{use-infinite-scroll-loader-Cvz8ZteY.js → use-infinite-scroll-loader-CFVdPpNv.js} +1 -1
  17. package/dist/index.html +3 -3
  18. package/package.json +9 -9
  19. package/src/features/agents/components/agents-page.test.tsx +1 -1
  20. package/src/features/agents/components/agents-page.tsx +1 -1
  21. package/src/features/chat/components/chat-session-workspace-panel.tsx +31 -45
  22. package/src/features/chat/components/chat-sidebar-session-item.tsx +7 -9
  23. package/src/features/chat/components/conversation/chat-conversation-header.test.tsx +5 -2
  24. package/src/features/chat/components/conversation/chat-conversation-header.tsx +2 -2
  25. package/src/features/chat/components/conversation/chat-conversation-panel.test.tsx +106 -78
  26. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +172 -167
  27. package/src/features/chat/components/conversation/chat-input-bar.container.tsx +11 -1
  28. package/src/features/chat/components/conversation/session-header/chat-session-header-actions.tsx +2 -2
  29. package/src/features/chat/components/providers/chat-presenter.provider.tsx +2 -0
  30. package/src/features/chat/hooks/use-ncp-agent-runtime.test.tsx +147 -88
  31. package/src/features/chat/managers/ncp-chat-input.manager.test.ts +20 -0
  32. package/src/features/chat/managers/ncp-chat-input.manager.ts +18 -0
  33. package/src/features/chat/managers/ncp-chat-presenter.manager.ts +1 -0
  34. package/src/features/chat/pages/ncp-chat-page.tsx +4 -1
  35. package/src/features/chat/stores/chat-input.store.ts +3 -1
  36. package/src/features/chat/utils/ncp-chat-input-availability.utils.test.ts +1 -0
  37. package/src/platforms/desktop/components/desktop-app-shell.test.tsx +1 -0
  38. package/src/platforms/desktop/components/desktop-app-shell.tsx +1 -1
  39. package/dist/assets/chat-page-D1fMNBrT.js +0 -1
  40. package/dist/assets/index-DnBeV2Xm.css +0 -1
  41. package/dist/assets/marketplace-page-odDpPYEs.js +0 -1
  42. package/dist/assets/mcp-marketplace-page-CfbOBgKK.js +0 -1
@@ -48,33 +48,40 @@ vi.mock("@nextclaw/agent-chat-ui", async (importOriginal) => {
48
48
  };
49
49
  });
50
50
 
51
- vi.mock("@/features/chat/components/conversation/chat-input-bar.container", () => ({
52
- ChatInputBarContainer: () => <div data-testid="chat-input-bar" />,
53
- }));
51
+ vi.mock(
52
+ "@/features/chat/components/conversation/chat-input-bar.container",
53
+ () => ({
54
+ ChatInputBarContainer: () => <div data-testid="chat-input-bar" />,
55
+ }),
56
+ );
54
57
 
55
- vi.mock("@/features/chat/components/conversation/chat-message-list.container", () => ({
56
- ChatMessageListContainer: ({
57
- isSending,
58
- messages,
59
- }: {
60
- isSending: boolean;
61
- messages: readonly unknown[];
62
- }) => (
63
- <div
64
- data-testid="chat-message-list"
65
- data-message-count={String(messages.length)}
66
- data-sending={String(isSending)}
67
- />
68
- ),
69
- }));
58
+ vi.mock(
59
+ "@/features/chat/components/conversation/chat-message-list.container",
60
+ () => ({
61
+ ChatMessageListContainer: ({
62
+ isSending,
63
+ messages,
64
+ }: {
65
+ isSending: boolean;
66
+ messages: readonly unknown[];
67
+ }) => (
68
+ <div
69
+ data-testid="chat-message-list"
70
+ data-message-count={String(messages.length)}
71
+ data-sending={String(isSending)}
72
+ />
73
+ ),
74
+ }),
75
+ );
70
76
 
71
- vi.mock("@/features/chat/components/chat-session-workspace-file-preview", () => ({
72
- ChatSessionWorkspaceFilePreview: ({
73
- file,
74
- }: {
75
- file: { path: string };
76
- }) => <div data-testid="workspace-file-preview">{file.path}</div>,
77
- }));
77
+ vi.mock(
78
+ "@/features/chat/components/chat-session-workspace-file-preview",
79
+ () => ({
80
+ ChatSessionWorkspaceFilePreview: ({ file }: { file: { path: string } }) => (
81
+ <div data-testid="workspace-file-preview">{file.path}</div>
82
+ ),
83
+ }),
84
+ );
78
85
 
79
86
  vi.mock("@/features/chat/components/chat-welcome", () => ({
80
87
  ChatWelcome: ({
@@ -123,10 +130,9 @@ vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
123
130
  readAt: string | null | undefined,
124
131
  ) =>
125
132
  sessionKey
126
- ? useChatSessionListStore.getState().markSessionRead(
127
- sessionKey,
128
- readAt,
129
- )
133
+ ? useChatSessionListStore
134
+ .getState()
135
+ .markSessionRead(sessionKey, readAt)
130
136
  : undefined,
131
137
  },
132
138
  chatInputManager: {
@@ -136,42 +142,44 @@ vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
136
142
  }));
137
143
 
138
144
  vi.mock("@/shared/hooks/use-config", () => ({
139
- useCronJobs: () => ({ data: { jobs: mocks.cronJobs, total: mocks.cronJobs.length } }),
145
+ useCronJobs: () => ({
146
+ data: { jobs: mocks.cronJobs, total: mocks.cronJobs.length },
147
+ }),
140
148
  useDeleteCronJob: () => ({
141
149
  mutate: mocks.deleteCronJob,
142
150
  isPending: false,
143
151
  }),
144
152
  }));
145
153
 
146
- vi.mock("@/features/chat/components/conversation/session-header/chat-session-header-actions", () => ({
147
- ChatSessionHeaderActions: () => <button aria-label="More actions" />,
148
- }));
149
-
150
- vi.mock("@/features/chat/components/conversation/session-header/chat-session-project-badge", () => ({
151
- ChatSessionProjectBadge: ({ projectName }: { projectName: string }) => (
152
- <button>{projectName}</button>
153
- ),
154
- }));
155
-
156
154
  vi.mock(
157
- "@/features/chat/hooks/use-ncp-child-session-tabs-view",
155
+ "@/features/chat/components/conversation/session-header/chat-session-header-actions",
158
156
  () => ({
159
- useNcpChildSessionTabsView: () => mocks.resolvedChildTabs,
157
+ ChatSessionHeaderActions: () => <button aria-label="More actions" />,
160
158
  }),
161
159
  );
162
160
 
163
161
  vi.mock(
164
- "@/features/chat/hooks/use-ncp-session-conversation",
162
+ "@/features/chat/components/conversation/session-header/chat-session-project-badge",
165
163
  () => ({
166
- useNcpSessionConversation: () => ({
167
- visibleMessages: [],
168
- isHydrating: false,
169
- hydrateError: null,
170
- isRunning: false,
171
- }),
164
+ ChatSessionProjectBadge: ({ projectName }: { projectName: string }) => (
165
+ <button>{projectName}</button>
166
+ ),
172
167
  }),
173
168
  );
174
169
 
170
+ vi.mock("@/features/chat/hooks/use-ncp-child-session-tabs-view", () => ({
171
+ useNcpChildSessionTabsView: () => mocks.resolvedChildTabs,
172
+ }));
173
+
174
+ vi.mock("@/features/chat/hooks/use-ncp-session-conversation", () => ({
175
+ useNcpSessionConversation: () => ({
176
+ visibleMessages: [],
177
+ isHydrating: false,
178
+ hydrateError: null,
179
+ isRunning: false,
180
+ }),
181
+ }));
182
+
175
183
  vi.mock("@/shared/components/common/agent-avatar", () => ({
176
184
  AgentAvatar: ({ agentId }: { agentId: string }) => (
177
185
  <div data-testid="agent-avatar">{agentId}</div>
@@ -278,7 +286,9 @@ describe("ChatConversationPanel", () => {
278
286
 
279
287
  render(<ChatConversationPanel layoutMode="mobile" />);
280
288
 
281
- await user.click(screen.getByRole("button", { name: "create draft session" }));
289
+ await user.click(
290
+ screen.getByRole("button", { name: "create draft session" }),
291
+ );
282
292
 
283
293
  expect(mocks.createSession).toHaveBeenCalledWith("native");
284
294
  expect(mocks.goToChatRoot).toHaveBeenCalledTimes(1);
@@ -365,13 +375,47 @@ describe("ChatConversationPanel", () => {
365
375
  expect(
366
376
  screen.queryByRole("status", { name: "Loading session history..." }),
367
377
  ).toBeNull();
368
- expect(screen.queryByText("No messages yet. Send one to start.")).toBeNull();
378
+ expect(
379
+ screen.queryByText("No messages yet. Send one to start."),
380
+ ).toBeNull();
369
381
  });
370
382
 
371
383
  it("keeps the message list mounted while waiting for the first assistant token", () => {
372
384
  useChatThreadStore.setState({
373
385
  snapshot: {
374
386
  ...useChatThreadStore.getState().snapshot,
387
+ sessionKey: "session-1",
388
+ messages: [
389
+ {
390
+ id: "user-1",
391
+ sessionId: "session-1",
392
+ role: "user",
393
+ status: "final",
394
+ parts: [{ type: "text", text: "hello" }],
395
+ timestamp: "2026-05-19T00:00:00.000Z",
396
+ } as never,
397
+ ],
398
+ isSending: true,
399
+ isAwaitingAssistantOutput: true,
400
+ },
401
+ });
402
+
403
+ render(<ChatConversationPanel />);
404
+
405
+ expect(screen.getByTestId("chat-message-list").dataset).toMatchObject({
406
+ messageCount: "1",
407
+ sending: "true",
408
+ });
409
+ expect(
410
+ screen.queryByText("No messages yet. Send one to start."),
411
+ ).toBeNull();
412
+ });
413
+
414
+ it("does not show assistant waiting copy before the first message is visible", () => {
415
+ useChatThreadStore.setState({
416
+ snapshot: {
417
+ ...useChatThreadStore.getState().snapshot,
418
+ sessionKey: "session-1",
375
419
  messages: [],
376
420
  isSending: true,
377
421
  isAwaitingAssistantOutput: true,
@@ -380,8 +424,10 @@ describe("ChatConversationPanel", () => {
380
424
 
381
425
  render(<ChatConversationPanel />);
382
426
 
383
- expect(screen.getByTestId("chat-message-list").dataset).toMatchObject({ messageCount: "0", sending: "true" });
384
- expect(screen.queryByText("No messages yet. Send one to start.")).toBeNull();
427
+ expect(screen.queryByTestId("chat-message-list")).toBeNull();
428
+ expect(
429
+ screen.queryByText("No messages yet. Send one to start."),
430
+ ).toBeNull();
385
431
  });
386
432
 
387
433
  it("does not reopen the welcome panel after a root draft send fails", () => {
@@ -399,7 +445,9 @@ describe("ChatConversationPanel", () => {
399
445
  render(<ChatConversationPanel />);
400
446
 
401
447
  expect(screen.queryByTestId("chat-welcome")).toBeNull();
402
- expect(screen.queryByText("No messages yet. Send one to start.")).toBeNull();
448
+ expect(
449
+ screen.queryByText("No messages yet. Send one to start."),
450
+ ).toBeNull();
403
451
  });
404
452
 
405
453
  it("does not render runtime lifecycle copy in the conversation alert strip", () => {
@@ -491,6 +539,7 @@ describe("ChatSessionWorkspacePanel", () => {
491
539
 
492
540
  render(
493
541
  <ChatSessionWorkspacePanel
542
+ sessionKey="parent-session-1"
494
543
  childSessionTabs={[
495
544
  {
496
545
  sessionKey: "child-session-1",
@@ -503,12 +552,6 @@ describe("ChatSessionWorkspacePanel", () => {
503
552
  workspaceFileTabs={[]}
504
553
  activeWorkspaceFileKey={null}
505
554
  sessionProjectRoot="/Users/demo/project-alpha"
506
- onSelectSession={vi.fn()}
507
- onSelectFile={vi.fn()}
508
- onCloseFile={vi.fn()}
509
- onClose={vi.fn()}
510
- onBackToParent={vi.fn()}
511
- onFileOpen={vi.fn()}
512
555
  />,
513
556
  );
514
557
 
@@ -559,6 +602,7 @@ describe("ChatSessionWorkspacePanel", () => {
559
602
 
560
603
  render(
561
604
  <ChatSessionWorkspacePanel
605
+ sessionKey="parent-session-1"
562
606
  childSessionTabs={[
563
607
  {
564
608
  sessionKey: "child-session-1",
@@ -577,12 +621,6 @@ describe("ChatSessionWorkspacePanel", () => {
577
621
  workspaceFileTabs={[]}
578
622
  activeWorkspaceFileKey={null}
579
623
  sessionProjectRoot="/Users/demo/project-alpha"
580
- onSelectSession={vi.fn()}
581
- onSelectFile={vi.fn()}
582
- onCloseFile={vi.fn()}
583
- onClose={vi.fn()}
584
- onBackToParent={vi.fn()}
585
- onFileOpen={vi.fn()}
586
624
  />,
587
625
  );
588
626
 
@@ -592,6 +630,7 @@ describe("ChatSessionWorkspacePanel", () => {
592
630
  it("shows opened files as top tabs and renders the file preview pane", () => {
593
631
  render(
594
632
  <ChatSessionWorkspacePanel
633
+ sessionKey="parent-session-1"
595
634
  childSessionTabs={[]}
596
635
  activeChildSessionKey={null}
597
636
  workspaceFileTabs={[
@@ -605,12 +644,6 @@ describe("ChatSessionWorkspacePanel", () => {
605
644
  ]}
606
645
  activeWorkspaceFileKey="parent-session-1::preview::README.md"
607
646
  sessionProjectRoot="/Users/demo/project-alpha"
608
- onSelectSession={vi.fn()}
609
- onSelectFile={vi.fn()}
610
- onCloseFile={vi.fn()}
611
- onClose={vi.fn()}
612
- onBackToParent={vi.fn()}
613
- onFileOpen={vi.fn()}
614
647
  />,
615
648
  );
616
649
 
@@ -649,6 +682,7 @@ describe("ChatSessionWorkspacePanel", () => {
649
682
 
650
683
  render(
651
684
  <ChatSessionWorkspacePanel
685
+ sessionKey="parent-session-1"
652
686
  childSessionTabs={[]}
653
687
  activeChildSessionKey={null}
654
688
  workspaceFileTabs={[]}
@@ -656,12 +690,6 @@ describe("ChatSessionWorkspacePanel", () => {
656
690
  activePanelKind="cron"
657
691
  sessionCronJobs={[job]}
658
692
  sessionProjectRoot="/Users/demo/project-alpha"
659
- onSelectSession={vi.fn()}
660
- onSelectFile={vi.fn()}
661
- onCloseFile={vi.fn()}
662
- onClose={vi.fn()}
663
- onBackToParent={vi.fn()}
664
- onFileOpen={vi.fn()}
665
693
  />,
666
694
  );
667
695