@nextclaw/ui 0.12.8 → 0.12.10

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 (227) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/dist/assets/ChannelsList-M9FTK1Ak.js +8 -0
  3. package/dist/assets/DocBrowser-CH7-GxlL.js +1 -0
  4. package/dist/assets/{DocBrowser-BMxf9CIK.js → DocBrowser-DMfr0Oow.js} +1 -1
  5. package/dist/assets/{DocBrowserContext-Ce28gRXt.js → DocBrowserContext-BXydqby-.js} +1 -1
  6. package/dist/assets/{LogoBadge-o92MOA2L.js → LogoBadge-hO7tY7hE.js} +1 -1
  7. package/dist/assets/ModelConfig-CNIgLf0e.js +1 -0
  8. package/dist/assets/{ProviderScopedModelInput-CmTIzgI7.js → ProviderScopedModelInput-B3HWP4oz.js} +1 -1
  9. package/dist/assets/ProvidersList-CHjMnRhX.js +1 -0
  10. package/dist/assets/RuntimeConfig-psp8nMSG.js +1 -0
  11. package/dist/assets/SearchConfig-CSoKip1f.js +1 -0
  12. package/dist/assets/{SecretsConfig-Ba1RPJaG.js → SecretsConfig-MEt6MjuD.js} +2 -2
  13. package/dist/assets/SessionsConfig-DifCiXwR.js +2 -0
  14. package/dist/assets/{app-query-client-DniXoIN5.js → app-query-client-9jNewezV.js} +1 -1
  15. package/dist/assets/{book-open-DocgeQtR.js → book-open-DzdUViDm.js} +1 -1
  16. package/dist/assets/chat-page-CLp0UV0Y.js +58 -0
  17. package/dist/assets/chat-session-display-DsYHx0RZ.js +1 -0
  18. package/dist/assets/{chunk-JZWAC4HX-BvKvh1R8.js → chunk-JZWAC4HX-C5dEc8hV.js} +1 -1
  19. package/dist/assets/{client-CVqPF5ie.js → client-C-8fH7-c.js} +1 -1
  20. package/dist/assets/{config-Bop2oB18.js → config-CBScxsdV.js} +1 -1
  21. package/dist/assets/config-split-page-BUout_Ak.js +1 -0
  22. package/dist/assets/{createLucideIcon-DVv8taGY.js → createLucideIcon-dy5ie7Ox.js} +1 -1
  23. package/dist/assets/desktop-update-config-2BS6BMkW.js +1 -0
  24. package/dist/assets/{dist-DmAlInRu.js → dist-BruyLa92.js} +1 -1
  25. package/dist/assets/{dist-Da5Gm_pO.js → dist-Cy7_j6hA.js} +1 -1
  26. package/dist/assets/download-BD0ETkB-.js +1 -0
  27. package/dist/assets/{external-link-DFjw3x1B.js → external-link-kZSAO8nT.js} +1 -1
  28. package/dist/assets/{hash-DJtaCejM.js → hash-BHJC2Ovu.js} +1 -1
  29. package/dist/assets/i18n-CpTZLchQ.js +1 -0
  30. package/dist/assets/index-mW8W2FUu.css +1 -0
  31. package/dist/assets/index-zDZfXoI4.js +6 -0
  32. package/dist/assets/{infiniteQueryBehavior-DHSEQ3OH.js → infiniteQueryBehavior-CyER9hv0.js} +1 -1
  33. package/dist/assets/loader-circle-Bc2gCU33.js +1 -0
  34. package/dist/assets/{logos-DEFUIR12.js → logos-B7gRObP8.js} +1 -1
  35. package/dist/assets/marketplace-page-3qVMnF3d.js +1 -0
  36. package/dist/assets/marketplace-page-BhFIeQzI.js +49 -0
  37. package/dist/assets/mcp-marketplace-page-DYfteJ1D.js +40 -0
  38. package/dist/assets/{page-layout-Da3i3r6G.js → page-layout-0UcO9H9Z.js} +1 -1
  39. package/dist/assets/play-CKDjSQFL.js +1 -0
  40. package/dist/assets/plus-CG0QrVY_.js +1 -0
  41. package/dist/assets/{refresh-ccw-D6HkNtfz.js → refresh-ccw-COVhNHtN.js} +1 -1
  42. package/dist/assets/{refresh-cw-DRcvRrnc.js → refresh-cw-Bcv40SXy.js} +1 -1
  43. package/dist/assets/remote-access-page-CWHG-sug.js +1 -0
  44. package/dist/assets/{rotate-cw-BmDKfXtH.js → rotate-cw-oHMKJMC8.js} +1 -1
  45. package/dist/assets/{save-DHGmi2e9.js → save-EqJPOF0G.js} +1 -1
  46. package/dist/assets/search-BCAlB8nz.js +1 -0
  47. package/dist/assets/security-config-Slh0Mayz.js +1 -0
  48. package/dist/assets/select-CVz0t7MF.js +41 -0
  49. package/dist/assets/setting-row-CbVHAuQt.js +1 -0
  50. package/dist/assets/skeleton-D5rdKvzy.js +1 -0
  51. package/dist/assets/{status-dot-DurKKSwA.js → status-dot-DpPtVzQT.js} +1 -1
  52. package/dist/assets/{switch-0rmPBRKI.js → switch-CM29eCAR.js} +1 -1
  53. package/dist/assets/{tabs-custom-5JLVL6v8.js → tabs-custom-YcZUWn3o.js} +1 -1
  54. package/dist/assets/tag-chip-DMXdnLcj.js +1 -0
  55. package/dist/assets/{trash-2-C6caKPoz.js → trash-2-mJT6oWa2.js} +1 -1
  56. package/dist/assets/{use-infinite-scroll-loader-dwnaa_qi.js → use-infinite-scroll-loader-DJ1L81Dz.js} +1 -1
  57. package/dist/assets/{useConfirmDialog-mMeWD_yo.js → useConfirmDialog-BsVuqu1x.js} +1 -1
  58. package/dist/assets/{useMutation-BmxxvCNf.js → useMutation-CNcz2fgt.js} +1 -1
  59. package/dist/assets/x-Czwxm82I.js +1 -0
  60. package/dist/index.html +95 -21
  61. package/dist/manifest.webmanifest +30 -0
  62. package/dist/offline.html +102 -0
  63. package/dist/pwa-192.png +0 -0
  64. package/dist/pwa-512.png +0 -0
  65. package/dist/runtime-icons/claude.ico +0 -0
  66. package/dist/runtime-icons/codex-openai.svg +6 -0
  67. package/dist/runtime-icons/hermes-agent.png +0 -0
  68. package/dist/sw.js +80 -0
  69. package/index.html +73 -1
  70. package/package.json +5 -5
  71. package/public/manifest.webmanifest +30 -0
  72. package/public/offline.html +102 -0
  73. package/public/pwa-192.png +0 -0
  74. package/public/pwa-512.png +0 -0
  75. package/public/runtime-icons/claude.ico +0 -0
  76. package/public/runtime-icons/codex-openai.svg +6 -0
  77. package/public/runtime-icons/hermes-agent.png +0 -0
  78. package/public/sw.js +80 -0
  79. package/src/account/components/account-panel.tsx +217 -97
  80. package/src/account/managers/account.manager.ts +3 -2
  81. package/src/api/chat-session-type.types.ts +7 -0
  82. package/src/api/runtime-control.types.ts +8 -0
  83. package/src/api/server-path.ts +27 -4
  84. package/src/api/types.ts +25 -10
  85. package/src/app.tsx +227 -54
  86. package/src/components/agents/agent-dialogs.tsx +499 -0
  87. package/src/components/agents/agents-page.test.tsx +238 -0
  88. package/src/components/agents/agents-page.tsx +435 -0
  89. package/src/components/chat/ChatSidebar.test.tsx +43 -1
  90. package/src/components/chat/ChatSidebar.tsx +35 -35
  91. package/src/components/chat/adapters/chat-message.summary-truncation.test.ts +66 -0
  92. package/src/components/chat/adapters/file-operation/card.ts +9 -0
  93. package/src/components/chat/adapters/file-operation/diff.ts +14 -0
  94. package/src/components/chat/{ChatConversationPanel.test.tsx → chat-conversation-panel.test.tsx} +127 -206
  95. package/src/components/chat/chat-conversation-panel.tsx +482 -0
  96. package/src/components/chat/chat-page-shell.tsx +19 -13
  97. package/src/components/chat/chat-session-type-option-item.test.tsx +46 -0
  98. package/src/components/chat/chat-session-type-option-item.tsx +68 -0
  99. package/src/components/chat/chat-session-workspace-file-preview.test.tsx +178 -0
  100. package/src/components/chat/chat-session-workspace-file-preview.tsx +278 -0
  101. package/src/components/chat/chat-session-workspace-panel-nav.tsx +203 -0
  102. package/src/components/chat/chat-session-workspace-panel.tsx +318 -0
  103. package/src/components/chat/chat-sidebar-project-groups.tsx +11 -36
  104. package/src/components/chat/chat-sidebar-session-item.tsx +32 -2
  105. package/src/components/chat/containers/chat-message-list.container.test.tsx +49 -0
  106. package/src/components/chat/containers/chat-message-list.container.tsx +4 -0
  107. package/src/components/chat/managers/chat-session-list.manager.test.ts +12 -0
  108. package/src/components/chat/managers/chat-session-list.manager.ts +7 -0
  109. package/src/components/chat/ncp/__tests__/ncp-session-adapter.cancelled-tool.test.ts +77 -0
  110. package/src/components/chat/ncp/ncp-chat-page.tsx +9 -7
  111. package/src/components/chat/ncp/ncp-chat-thread.manager.ts +179 -41
  112. package/src/components/chat/ncp/ncp-session-adapter.test.ts +36 -1
  113. package/src/components/chat/ncp/ncp-session-adapter.ts +20 -0
  114. package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +62 -13
  115. package/src/components/chat/ncp/tests/ncp-chat-thread.manager.test.ts +189 -0
  116. package/src/components/chat/presenter/chat-presenter-context.tsx +13 -2
  117. package/src/components/chat/session-header/chat-session-header-actions.test.tsx +26 -0
  118. package/src/components/chat/session-header/chat-session-header-actions.tsx +19 -1
  119. package/src/components/chat/stores/chat-input.store.ts +2 -1
  120. package/src/components/chat/stores/chat-thread.store.ts +27 -1
  121. package/src/components/chat/useChatSessionTypeState.ts +10 -1
  122. package/src/components/chat/workspace/chat-session-workspace-file-breadcrumbs.tsx +86 -0
  123. package/src/components/common/BrandHeader.tsx +3 -1
  124. package/src/components/common/session-context-icon.tsx +15 -2
  125. package/src/components/common/{TagInput.tsx → tag-input.tsx} +25 -17
  126. package/src/components/config/ChannelForm.test.tsx +89 -3
  127. package/src/components/config/ChannelForm.tsx +157 -188
  128. package/src/components/config/ChannelsList.test.tsx +163 -119
  129. package/src/components/config/ChannelsList.tsx +90 -101
  130. package/src/components/config/ProviderForm.tsx +108 -146
  131. package/src/components/config/ProvidersList.tsx +100 -123
  132. package/src/components/config/RuntimeConfig.tsx +141 -2
  133. package/src/components/config/SearchConfig.tsx +423 -393
  134. package/src/components/config/channel-form-fields-section.tsx +70 -37
  135. package/src/components/config/config-split-page.tsx +109 -0
  136. package/src/components/config/provider-enabled-field.tsx +17 -10
  137. package/src/components/config/runtime-control-card.test.tsx +56 -0
  138. package/src/components/config/runtime-control-card.tsx +25 -0
  139. package/src/components/config/runtime-presence-card.tsx +93 -79
  140. package/src/components/layout/AppLayout.tsx +25 -37
  141. package/src/components/layout/app-layout.test.tsx +46 -14
  142. package/src/components/layout/runtime-status-entry.test.tsx +157 -0
  143. package/src/components/layout/runtime-status-entry.tsx +143 -0
  144. package/src/components/marketplace/marketplace-detail-doc.ts +93 -0
  145. package/src/components/marketplace/marketplace-list-card.tsx +288 -0
  146. package/src/components/marketplace/marketplace-page-data.ts +129 -0
  147. package/src/components/marketplace/marketplace-page.test.tsx +339 -0
  148. package/src/components/marketplace/marketplace-page.tsx +596 -0
  149. package/src/components/marketplace/mcp/mcp-marketplace-card.tsx +128 -0
  150. package/src/components/marketplace/mcp/mcp-marketplace-dialogs.tsx +191 -0
  151. package/src/components/marketplace/mcp/mcp-marketplace-doc.ts +152 -0
  152. package/src/components/marketplace/mcp/mcp-marketplace-page.test.tsx +223 -0
  153. package/src/components/marketplace/mcp/mcp-marketplace-page.tsx +414 -0
  154. package/src/components/providers/ThemeProvider.tsx +5 -0
  155. package/src/components/remote/remote-access-page.test.tsx +105 -0
  156. package/src/components/remote/remote-access-page.tsx +248 -0
  157. package/src/components/ui/notice-card.tsx +129 -0
  158. package/src/components/ui/setting-row.tsx +51 -0
  159. package/src/components/ui/tag-chip.tsx +39 -0
  160. package/src/components/ui/textarea.tsx +19 -0
  161. package/src/hooks/server-path/use-server-path-read.ts +20 -0
  162. package/src/hooks/useConfig.ts +2 -1
  163. package/src/index.css +24 -0
  164. package/src/lib/app-resource-uri.test.ts +20 -0
  165. package/src/lib/app-resource-uri.ts +29 -0
  166. package/src/lib/chat-message.ts +14 -3
  167. package/src/lib/i18n.chat.ts +12 -1
  168. package/src/lib/i18n.pwa.ts +62 -0
  169. package/src/lib/i18n.remote.ts +1 -1
  170. package/src/lib/i18n.runtime-control.ts +31 -0
  171. package/src/lib/i18n.ts +7 -10
  172. package/src/lib/session-context.utils.test.ts +71 -0
  173. package/src/lib/session-context.utils.ts +28 -3
  174. package/src/lib/session-project/workspace-file-breadcrumb.test.ts +83 -0
  175. package/src/lib/session-project/workspace-file-breadcrumb.ts +188 -0
  176. package/src/pwa/components/pwa-install-entry.test.tsx +110 -0
  177. package/src/pwa/components/pwa-install-entry.tsx +205 -0
  178. package/src/pwa/managers/pwa-install.manager.test.ts +160 -0
  179. package/src/pwa/managers/pwa-install.manager.ts +232 -0
  180. package/src/pwa/managers/pwa-runtime.manager.ts +196 -0
  181. package/src/pwa/managers/pwa-shell-theme.manager.test.ts +30 -0
  182. package/src/pwa/managers/pwa-shell-theme.manager.ts +46 -0
  183. package/src/pwa/pwa-install-banner.storage.ts +55 -0
  184. package/src/pwa/pwa.types.ts +22 -0
  185. package/src/pwa/register-pwa.ts +14 -0
  186. package/src/pwa/stores/pwa.store.ts +17 -0
  187. package/src/vite-env.d.ts +9 -0
  188. package/dist/assets/ChannelsList-KIQIxluX.js +0 -8
  189. package/dist/assets/DocBrowser-CyDgAtO9.js +0 -1
  190. package/dist/assets/MarketplacePage-BySqkYDh.js +0 -49
  191. package/dist/assets/MarketplacePage-C0olZaek.js +0 -1
  192. package/dist/assets/McpMarketplacePage-DqKaiXO9.js +0 -40
  193. package/dist/assets/ModelConfig-IrmzoslW.js +0 -1
  194. package/dist/assets/ProvidersList-8_Kalfwl.js +0 -1
  195. package/dist/assets/RemoteAccessPage-CyQlSjPf.js +0 -1
  196. package/dist/assets/RuntimeConfig-Bk0uYBhf.js +0 -1
  197. package/dist/assets/SearchConfig-DNBR-UbE.js +0 -1
  198. package/dist/assets/SessionsConfig-Doqp5ghH.js +0 -2
  199. package/dist/assets/chat-page-Bph8M5zo.js +0 -58
  200. package/dist/assets/chat-session-display-CoN3Wmn-.js +0 -1
  201. package/dist/assets/config-layout-DmlGaay2.js +0 -1
  202. package/dist/assets/desktop-update-config-1KBrqLBC.js +0 -1
  203. package/dist/assets/i18n-CwHZ-9vt.js +0 -1
  204. package/dist/assets/index-DafCdM4F.css +0 -1
  205. package/dist/assets/index-DdksE6U3.js +0 -6
  206. package/dist/assets/loader-circle-PsSP0H9n.js +0 -1
  207. package/dist/assets/play-DBQbBxTA.js +0 -1
  208. package/dist/assets/plus-DUOVbsyQ.js +0 -1
  209. package/dist/assets/popover-C_mWOFzI.js +0 -1
  210. package/dist/assets/search-MChQRYR1.js +0 -1
  211. package/dist/assets/security-config-CbXfPZzr.js +0 -1
  212. package/dist/assets/select-Caud8QvU.js +0 -41
  213. package/dist/assets/skeleton-B-4vRq_Z.js +0 -1
  214. package/dist/assets/x-DuMhMATD.js +0 -1
  215. package/src/components/agents/AgentDialogs.tsx +0 -400
  216. package/src/components/agents/AgentsPage.test.tsx +0 -217
  217. package/src/components/agents/AgentsPage.tsx +0 -352
  218. package/src/components/chat/ChatConversationPanel.tsx +0 -256
  219. package/src/components/chat/chat-child-session-panel.tsx +0 -270
  220. package/src/components/config/config-layout.ts +0 -10
  221. package/src/components/marketplace/MarketplacePage.test.tsx +0 -322
  222. package/src/components/marketplace/MarketplacePage.tsx +0 -827
  223. package/src/components/marketplace/mcp/McpMarketplacePage.test.tsx +0 -208
  224. package/src/components/marketplace/mcp/McpMarketplacePage.tsx +0 -580
  225. package/src/components/remote/RemoteAccessPage.test.tsx +0 -103
  226. package/src/components/remote/RemoteAccessPage.tsx +0 -144
  227. /package/dist/assets/{config-hints-BZoDjXye.js → config-hints-BhTmc9P1.js} +0 -0
@@ -11,6 +11,7 @@ const mocks = vi.hoisted(() => ({
11
11
  setQuery: vi.fn(),
12
12
  setListMode: vi.fn(),
13
13
  selectSession: vi.fn(),
14
+ openChildSessionPanel: vi.fn(),
14
15
  docOpen: vi.fn(),
15
16
  updateNcpSession: vi.fn(),
16
17
  agents: [] as Array<{ id: string; displayName?: string; avatarUrl?: string | null }>,
@@ -36,7 +37,10 @@ vi.mock('@/components/chat/presenter/chat-presenter-context', () => ({
36
37
  sessionKey: string | null | undefined,
37
38
  readAt: string | null | undefined,
38
39
  ) => (sessionKey ? useChatSessionListStore.getState().markSessionRead(sessionKey, readAt) : undefined),
39
- }
40
+ },
41
+ chatThreadManager: {
42
+ openChildSessionPanel: mocks.openChildSessionPanel,
43
+ },
40
44
  })
41
45
  }));
42
46
 
@@ -113,6 +117,7 @@ function resetSidebarTestState() {
113
117
  mocks.setQuery.mockReset();
114
118
  mocks.setListMode.mockReset();
115
119
  mocks.selectSession.mockReset();
120
+ mocks.openChildSessionPanel.mockReset();
116
121
  mocks.docOpen.mockReset();
117
122
  mocks.updateNcpSession.mockReset();
118
123
  mocks.updateNcpSession.mockResolvedValue({});
@@ -630,4 +635,41 @@ describe('ChatSidebar session item interactions', () => {
630
635
 
631
636
  expect(screen.queryByLabelText('Session has unread updates')).toBeNull();
632
637
  });
638
+
639
+ it('opens the child-session browser from a parent session row', () => {
640
+ mocks.sessionItems = [
641
+ createSessionItem({
642
+ key: 'session:parent-1',
643
+ createdAt: '2026-03-19T09:00:00.000Z',
644
+ updatedAt: '2026-03-19T09:05:00.000Z',
645
+ label: 'Parent Task',
646
+ sessionType: 'native',
647
+ sessionTypeMutable: false,
648
+ messageCount: 1
649
+ }),
650
+ createSessionItem({
651
+ key: 'session:child-1',
652
+ createdAt: '2026-03-19T09:06:00.000Z',
653
+ updatedAt: '2026-03-19T09:07:00.000Z',
654
+ label: 'Child Task',
655
+ sessionType: 'native',
656
+ sessionTypeMutable: false,
657
+ messageCount: 1,
658
+ parentSessionId: 'session:parent-1'
659
+ })
660
+ ];
661
+
662
+ render(
663
+ <MemoryRouter>
664
+ <ChatSidebar />
665
+ </MemoryRouter>
666
+ );
667
+
668
+ fireEvent.click(screen.getByLabelText('View child sessions'));
669
+
670
+ expect(mocks.openChildSessionPanel).toHaveBeenCalledWith({
671
+ parentSessionKey: 'session:parent-1',
672
+ activeChildSessionKey: 'session:child-1',
673
+ });
674
+ });
633
675
  });
@@ -7,6 +7,7 @@ import { Input } from '@/components/ui/input';
7
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
+ import { ChatSessionTypeOptionItem } from '@/components/chat/chat-session-type-option-item';
10
11
  import { ChatSidebarListModeSwitch } from '@/components/chat/chat-sidebar-list-mode-switch';
11
12
  import {
12
13
  ChatSidebarProjectGroups,
@@ -130,16 +131,6 @@ function sessionTitle(session: SessionEntryView): string {
130
131
  return chunks[chunks.length - 1] || session.key;
131
132
  }
132
133
 
133
- function resolveSessionTypeStatusText(option: {
134
- ready?: boolean;
135
- reasonMessage?: string | null;
136
- }): string {
137
- if (option.ready === false) {
138
- return option.reasonMessage?.trim() || t('statusSetup');
139
- }
140
- return t('statusReady');
141
- }
142
-
143
134
  const navItems = [
144
135
  { target: '/cron', label: () => t('chatSidebarScheduledTasks'), icon: AlarmClock },
145
136
  { target: '/skills', label: () => t('chatSidebarSkills'), icon: BrainCircuit },
@@ -201,6 +192,22 @@ export function ChatSidebar() {
201
192
  [agentsQuery.data?.agents]
202
193
  );
203
194
  const sortedItems = useMemo(() => sortSessionItemsByUpdatedAtDesc(items), [items]);
195
+ const childSessionsByParentKey = useMemo(() => {
196
+ const grouped = new Map<string, NcpSessionListItemView[]>();
197
+ for (const item of items) {
198
+ const parentSessionKey = item.session.parentSessionId?.trim();
199
+ if (!parentSessionKey) {
200
+ continue;
201
+ }
202
+ const bucket = grouped.get(parentSessionKey) ?? [];
203
+ bucket.push(item);
204
+ grouped.set(parentSessionKey, bucket);
205
+ }
206
+ for (const bucket of grouped.values()) {
207
+ bucket.sort((left, right) => getSessionUpdatedAtTimestamp(right) - getSessionUpdatedAtTimestamp(left));
208
+ }
209
+ return grouped;
210
+ }, [items]);
204
211
  const groups = useMemo(() => groupSessionsByDate(sortedItems), [sortedItems]);
205
212
  const projectGroups = useMemo(() => groupSessionsByProject(sortedItems), [sortedItems]);
206
213
  const defaultSessionType = inputSnapshot.defaultSessionType || 'native';
@@ -263,6 +270,7 @@ export function ChatSidebar() {
263
270
  const context = resolveSessionContextView(session, inputSnapshot.sessionTypeOptions);
264
271
  const isEditing = editingSessionKey === session.key;
265
272
  const isSaving = savingSessionKey === session.key;
273
+ const childSessions = childSessionsByParentKey.get(session.key) ?? [];
266
274
  return (
267
275
  <ChatSidebarSessionItem
268
276
  key={session.key}
@@ -275,10 +283,17 @@ export function ChatSidebar() {
275
283
  agentId={session.agentId ?? null}
276
284
  agentLabel={session.agentId ? (agentsById.get(session.agentId)?.displayName ?? session.agentId) : null}
277
285
  agentAvatarUrl={session.agentId ? (agentsById.get(session.agentId)?.avatarUrl ?? null) : null}
286
+ childSessionCount={childSessions.length}
278
287
  isEditing={isEditing}
279
288
  draftLabel={draftLabel}
280
289
  isSaving={isSaving}
281
290
  onSelect={() => presenter.chatSessionListManager.selectSession(session.key)}
291
+ onOpenChildSessions={() =>
292
+ presenter.chatThreadManager.openChildSessionPanel({
293
+ parentSessionKey: session.key,
294
+ activeChildSessionKey: childSessions[0]?.session.key ?? null,
295
+ })
296
+ }
282
297
  onStartEditing={() => startEditingSessionLabel(session)}
283
298
  onDraftLabelChange={setDraftLabel}
284
299
  onSave={() => saveSessionLabel(session)}
@@ -323,38 +338,23 @@ export function ChatSidebar() {
323
338
  <ChevronDown className="h-4 w-4" />
324
339
  </Button>
325
340
  </PopoverTrigger>
326
- <PopoverContent align="end" className="w-64 p-2">
327
- <div className="px-2 py-1 text-[11px] font-medium uppercase tracking-wider text-gray-400">
341
+ <PopoverContent
342
+ align="end"
343
+ className="w-56 rounded-2xl border border-gray-200/80 bg-white p-1.5 shadow-[0_24px_60px_-28px_rgba(15,23,42,0.38)]"
344
+ >
345
+ <div className="px-3 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-[0.18em] text-gray-400">
328
346
  {t('chatSessionTypeLabel')}
329
347
  </div>
330
- <div className="mt-1 space-y-1">
348
+ <div className="space-y-1">
331
349
  {nonDefaultSessionTypeOptions.map((option) => (
332
- <button
350
+ <ChatSessionTypeOptionItem
333
351
  key={option.value}
334
- type="button"
335
- onClick={() => {
352
+ option={option}
353
+ onSelect={() => {
336
354
  presenter.chatSessionListManager.createSession(option.value);
337
355
  setIsCreateMenuOpen(false);
338
356
  }}
339
- className="w-full rounded-xl px-3 py-2 text-left transition-colors hover:bg-gray-100"
340
- >
341
- <div className="flex items-center justify-between gap-3">
342
- <div className="text-[13px] font-medium text-gray-900">{option.label}</div>
343
- <span
344
- className={cn(
345
- 'shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold',
346
- option.ready === false
347
- ? 'bg-amber-100 text-amber-800'
348
- : 'bg-emerald-100 text-emerald-700'
349
- )}
350
- >
351
- {option.ready === false ? t('statusSetup') : t('statusReady')}
352
- </span>
353
- </div>
354
- <div className="mt-0.5 text-[11px] text-gray-500">
355
- {resolveSessionTypeStatusText(option)}
356
- </div>
357
- </button>
357
+ />
358
358
  ))}
359
359
  </div>
360
360
  </PopoverContent>
@@ -0,0 +1,66 @@
1
+ import { ToolInvocationStatus, type UiMessage } from "@nextclaw/agent-chat";
2
+ import { adaptChatMessages } from "@/components/chat/adapters/chat-message.adapter";
3
+ import type { ChatMessageSource } from "@/components/chat/adapters/chat-message.adapter";
4
+ import type { ChatMessagePartViewModel } from "@nextclaw/agent-chat-ui";
5
+
6
+ const defaultTexts = {
7
+ roleLabels: {
8
+ user: "You",
9
+ assistant: "Assistant",
10
+ tool: "Tool",
11
+ system: "System",
12
+ fallback: "Message",
13
+ },
14
+ reasoningLabel: "Reasoning",
15
+ toolCallLabel: "Tool Call",
16
+ toolResultLabel: "Tool Result",
17
+ toolInputLabel: "Input",
18
+ toolNoOutputLabel: "No output",
19
+ toolOutputLabel: "Output",
20
+ toolStatusPreparingLabel: "Preparing",
21
+ toolStatusRunningLabel: "Running",
22
+ toolStatusCompletedLabel: "Completed",
23
+ toolStatusFailedLabel: "Failed",
24
+ toolStatusCancelledLabel: "Cancelled",
25
+ imageAttachmentLabel: "Image attachment",
26
+ fileAttachmentLabel: "File attachment",
27
+ unknownPartLabel: "Unknown Part",
28
+ };
29
+
30
+ function adapt(uiMessages: UiMessage[]) {
31
+ return adaptChatMessages({
32
+ uiMessages: uiMessages as unknown as ChatMessageSource[],
33
+ formatTimestamp: (value) => `formatted:${value}`,
34
+ texts: defaultTexts,
35
+ });
36
+ }
37
+
38
+ it("truncates long structured tool summaries into a single-line ellipsis detail", () => {
39
+ const adapted = adapt([
40
+ {
41
+ id: "assistant-long-tool-summary",
42
+ role: "assistant",
43
+ parts: [
44
+ {
45
+ type: "tool-invocation",
46
+ toolInvocation: {
47
+ status: ToolInvocationStatus.PARTIAL_CALL,
48
+ toolCallId: "call-long-tool-summary",
49
+ toolName: "terminal",
50
+ args: JSON.stringify({
51
+ command:
52
+ "ls -la /Users/peiwang/.nextclaw/workspace/skills/bird/snapshots/archive/releases/2026-04-17/builds/current/output 2>/dev/null | sed -n '1,160p' && echo 'done'",
53
+ }),
54
+ },
55
+ },
56
+ ],
57
+ },
58
+ ]);
59
+
60
+ const summary = (
61
+ adapted[0]?.parts[0] as Extract<ChatMessagePartViewModel, { type: "tool-card" }> | undefined
62
+ )?.card.summary;
63
+
64
+ expect(summary?.startsWith("command: ls -la /Users/peiwang/.nextclaw/workspace/skills/bird/")).toBe(true);
65
+ expect(summary?.endsWith("…")).toBe(true);
66
+ });
@@ -54,7 +54,16 @@ function finalizeParsedBlocks(
54
54
  display: block.display,
55
55
  ...(block.caption ? { caption: block.caption } : {}),
56
56
  lines: block.lines,
57
+ ...(block.fullLines ? { fullLines: block.fullLines } : {}),
57
58
  ...(block.rawText ? { rawText: block.rawText } : {}),
59
+ ...(block.beforeText ? { beforeText: block.beforeText } : {}),
60
+ ...(block.afterText ? { afterText: block.afterText } : {}),
61
+ ...(typeof block.oldStartLine === "number"
62
+ ? { oldStartLine: block.oldStartLine }
63
+ : {}),
64
+ ...(typeof block.newStartLine === "number"
65
+ ? { newStartLine: block.newStartLine }
66
+ : {}),
58
67
  ...(block.truncated ? { truncated: true } : {}),
59
68
  }))
60
69
  .filter((block) => block.lines.length > 0 || Boolean(block.rawText));
@@ -13,7 +13,12 @@ export type ParsedBlock = {
13
13
  display: "preview" | "diff";
14
14
  caption?: string;
15
15
  lines: ChatFileOperationLineViewModel[];
16
+ fullLines?: ChatFileOperationLineViewModel[];
16
17
  rawText?: string;
18
+ beforeText?: string;
19
+ afterText?: string;
20
+ oldStartLine?: number;
21
+ newStartLine?: number;
17
22
  truncated?: boolean;
18
23
  };
19
24
 
@@ -114,6 +119,9 @@ export function buildRawPreviewBlock(params: {
114
119
  lines,
115
120
  }),
116
121
  lines,
122
+ rawText: previewText,
123
+ oldStartLine,
124
+ newStartLine,
117
125
  };
118
126
  }
119
127
 
@@ -144,6 +152,11 @@ export function buildFullReplaceBlock(params: {
144
152
  lines,
145
153
  }),
146
154
  lines: limited.lines,
155
+ ...(limited.truncated ? { fullLines: lines } : {}),
156
+ ...(params.beforeText != null ? { beforeText: params.beforeText } : {}),
157
+ ...(params.afterText != null ? { afterText: params.afterText } : {}),
158
+ ...(typeof oldStartLine === "number" ? { oldStartLine } : {}),
159
+ ...(typeof newStartLine === "number" ? { newStartLine } : {}),
147
160
  truncated: limited.truncated,
148
161
  };
149
162
  }
@@ -162,6 +175,7 @@ function buildParsedPatchBlock(params: {
162
175
  lines: params.lines,
163
176
  }),
164
177
  lines: limited.lines,
178
+ ...(limited.truncated ? { fullLines: params.lines } : {}),
165
179
  truncated: limited.truncated,
166
180
  };
167
181
  }