@nextclaw/ui 0.12.8 → 0.12.9
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 +35 -0
- package/dist/assets/ChannelsList-Ita2Zm1_.js +8 -0
- package/dist/assets/{DocBrowser-BMxf9CIK.js → DocBrowser-6ReNjvzF.js} +1 -1
- package/dist/assets/DocBrowser-BNwbPHf4.js +1 -0
- package/dist/assets/{DocBrowserContext-Ce28gRXt.js → DocBrowserContext-B6SpA7Qs.js} +1 -1
- package/dist/assets/{LogoBadge-o92MOA2L.js → LogoBadge-ByNLYg65.js} +1 -1
- package/dist/assets/MarketplacePage-CjX2MWww.js +1 -0
- package/dist/assets/{MarketplacePage-BySqkYDh.js → MarketplacePage-D0sDlYX4.js} +1 -1
- package/dist/assets/McpMarketplacePage-BGKJm1sJ.js +40 -0
- package/dist/assets/{ModelConfig-IrmzoslW.js → ModelConfig-BzZenCH-.js} +1 -1
- package/dist/assets/{ProviderScopedModelInput-CmTIzgI7.js → ProviderScopedModelInput-Da7khnBA.js} +1 -1
- package/dist/assets/{ProvidersList-8_Kalfwl.js → ProvidersList-BbVzRxjY.js} +1 -1
- package/dist/assets/RemoteAccessPage-BaDH_X1Q.js +1 -0
- package/dist/assets/RuntimeConfig-F_XKGgLm.js +1 -0
- package/dist/assets/{SearchConfig-DNBR-UbE.js → SearchConfig-BGkzXQP-.js} +1 -1
- package/dist/assets/{SecretsConfig-Ba1RPJaG.js → SecretsConfig-D281Rotl.js} +2 -2
- package/dist/assets/{SessionsConfig-Doqp5ghH.js → SessionsConfig-ChHQ7M5c.js} +2 -2
- package/dist/assets/{app-query-client-DniXoIN5.js → app-query-client-VnFElj4E.js} +1 -1
- package/dist/assets/{book-open-DocgeQtR.js → book-open-BdcxxoQu.js} +1 -1
- package/dist/assets/chat-page-Doe0yTtB.js +58 -0
- package/dist/assets/chat-session-display-cw78aiI_.js +1 -0
- package/dist/assets/{chunk-JZWAC4HX-BvKvh1R8.js → chunk-JZWAC4HX-DK5HPmIK.js} +1 -1
- package/dist/assets/{client-CVqPF5ie.js → client-_i4MU2bB.js} +1 -1
- package/dist/assets/{config-Bop2oB18.js → config-DtIQwrHF.js} +1 -1
- package/dist/assets/{createLucideIcon-DVv8taGY.js → createLucideIcon-BSeTgkZW.js} +1 -1
- package/dist/assets/desktop-update-config-Dpcf4BKG.js +1 -0
- package/dist/assets/{dist-Da5Gm_pO.js → dist-6TrrnPCR.js} +1 -1
- package/dist/assets/{dist-DmAlInRu.js → dist-ccBFUi-o.js} +1 -1
- package/dist/assets/download-BhDxnyvU.js +1 -0
- package/dist/assets/{external-link-DFjw3x1B.js → external-link-BgErLCNT.js} +1 -1
- package/dist/assets/{hash-DJtaCejM.js → hash-Bl7dr_UG.js} +1 -1
- package/dist/assets/i18n-eDHeDY0n.js +1 -0
- package/dist/assets/index-CF9xve0E.js +6 -0
- package/dist/assets/index-FgA52VBt.css +1 -0
- package/dist/assets/{infiniteQueryBehavior-DHSEQ3OH.js → infiniteQueryBehavior-ZDS92Qpp.js} +1 -1
- package/dist/assets/loader-circle-ACM1s51e.js +1 -0
- package/dist/assets/{logos-DEFUIR12.js → logos-x89HbrZ4.js} +1 -1
- package/dist/assets/{page-layout-Da3i3r6G.js → page-layout-vZnghcFy.js} +1 -1
- package/dist/assets/play-CFUwCA2E.js +1 -0
- package/dist/assets/plus-rYsv72JG.js +1 -0
- package/dist/assets/{popover-C_mWOFzI.js → popover-Bg1VoTZ6.js} +1 -1
- package/dist/assets/{refresh-ccw-D6HkNtfz.js → refresh-ccw-DT98i__E.js} +1 -1
- package/dist/assets/{refresh-cw-DRcvRrnc.js → refresh-cw-C47QSEwg.js} +1 -1
- package/dist/assets/{rotate-cw-BmDKfXtH.js → rotate-cw-JtFzpNn6.js} +1 -1
- package/dist/assets/{save-DHGmi2e9.js → save-3S6-H3Xw.js} +1 -1
- package/dist/assets/search-3kFR_zh9.js +1 -0
- package/dist/assets/{security-config-CbXfPZzr.js → security-config-BWaiARNk.js} +1 -1
- package/dist/assets/{select-Caud8QvU.js → select-DJ2MUjBB.js} +1 -1
- package/dist/assets/skeleton-ByQepn0M.js +1 -0
- package/dist/assets/{status-dot-DurKKSwA.js → status-dot-vbanNPFU.js} +1 -1
- package/dist/assets/{switch-0rmPBRKI.js → switch-BsLtHOH-.js} +1 -1
- package/dist/assets/{tabs-custom-5JLVL6v8.js → tabs-custom-D3HYMt6k.js} +1 -1
- package/dist/assets/{trash-2-C6caKPoz.js → trash-2-G48scll7.js} +1 -1
- package/dist/assets/{use-infinite-scroll-loader-dwnaa_qi.js → use-infinite-scroll-loader-DkNhD-42.js} +1 -1
- package/dist/assets/{useConfirmDialog-mMeWD_yo.js → useConfirmDialog-BkvTN-vd.js} +1 -1
- package/dist/assets/{useMutation-BmxxvCNf.js → useMutation-CBWjE2uj.js} +1 -1
- package/dist/assets/x-ByDbItbq.js +1 -0
- package/dist/index.html +95 -21
- package/dist/manifest.webmanifest +30 -0
- package/dist/offline.html +102 -0
- package/dist/pwa-192.png +0 -0
- package/dist/pwa-512.png +0 -0
- package/dist/sw.js +80 -0
- package/index.html +73 -1
- package/package.json +6 -6
- package/public/manifest.webmanifest +30 -0
- package/public/offline.html +102 -0
- package/public/pwa-192.png +0 -0
- package/public/pwa-512.png +0 -0
- package/public/sw.js +80 -0
- package/src/api/server-path.ts +27 -4
- package/src/api/types.ts +17 -10
- package/src/app.tsx +9 -0
- package/src/components/chat/ChatSidebar.test.tsx +43 -1
- package/src/components/chat/ChatSidebar.tsx +24 -0
- package/src/components/chat/adapters/chat-message.summary-truncation.test.ts +66 -0
- package/src/components/chat/adapters/file-operation/card.ts +9 -0
- package/src/components/chat/adapters/file-operation/diff.ts +14 -0
- package/src/components/chat/{ChatConversationPanel.test.tsx → chat-conversation-panel.test.tsx} +107 -206
- package/src/components/chat/chat-conversation-panel.tsx +412 -0
- package/src/components/chat/chat-page-shell.tsx +1 -1
- package/src/components/chat/chat-session-workspace-file-preview.test.tsx +91 -0
- package/src/components/chat/chat-session-workspace-file-preview.tsx +307 -0
- package/src/components/chat/chat-session-workspace-panel-nav.tsx +197 -0
- package/src/components/chat/chat-session-workspace-panel.tsx +318 -0
- package/src/components/chat/chat-sidebar-session-item.tsx +32 -2
- package/src/components/chat/containers/chat-message-list.container.test.tsx +49 -0
- package/src/components/chat/containers/chat-message-list.container.tsx +4 -0
- package/src/components/chat/managers/chat-session-list.manager.test.ts +12 -0
- package/src/components/chat/managers/chat-session-list.manager.ts +7 -0
- package/src/components/chat/ncp/ncp-chat-page.tsx +7 -7
- package/src/components/chat/ncp/ncp-chat-thread.manager.ts +179 -41
- package/src/components/chat/ncp/ncp-session-adapter.test.ts +35 -1
- package/src/components/chat/ncp/ncp-session-adapter.ts +17 -0
- package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +54 -11
- package/src/components/chat/ncp/tests/ncp-chat-thread.manager.test.ts +189 -0
- package/src/components/chat/presenter/chat-presenter-context.tsx +13 -2
- package/src/components/chat/session-header/chat-session-header-actions.test.tsx +26 -0
- package/src/components/chat/session-header/chat-session-header-actions.tsx +19 -1
- package/src/components/chat/stores/chat-thread.store.ts +24 -0
- package/src/components/config/RuntimeConfig.tsx +141 -2
- package/src/components/layout/AppLayout.tsx +1 -1
- package/src/components/providers/ThemeProvider.tsx +5 -0
- package/src/hooks/server-path/use-server-path-read.ts +20 -0
- package/src/lib/chat-message.ts +14 -3
- package/src/lib/i18n.chat.ts +12 -1
- package/src/lib/i18n.pwa.ts +62 -0
- package/src/lib/i18n.ts +2 -2
- package/src/pwa/components/pwa-install-entry.test.tsx +110 -0
- package/src/pwa/components/pwa-install-entry.tsx +205 -0
- package/src/pwa/managers/pwa-install.manager.test.ts +160 -0
- package/src/pwa/managers/pwa-install.manager.ts +232 -0
- package/src/pwa/managers/pwa-runtime.manager.ts +196 -0
- package/src/pwa/managers/pwa-shell-theme.manager.test.ts +30 -0
- package/src/pwa/managers/pwa-shell-theme.manager.ts +46 -0
- package/src/pwa/pwa-install-banner.storage.ts +55 -0
- package/src/pwa/pwa.types.ts +22 -0
- package/src/pwa/register-pwa.ts +14 -0
- package/src/pwa/stores/pwa.store.ts +17 -0
- package/src/vite-env.d.ts +9 -0
- package/dist/assets/ChannelsList-KIQIxluX.js +0 -8
- package/dist/assets/DocBrowser-CyDgAtO9.js +0 -1
- package/dist/assets/MarketplacePage-C0olZaek.js +0 -1
- package/dist/assets/McpMarketplacePage-DqKaiXO9.js +0 -40
- package/dist/assets/RemoteAccessPage-CyQlSjPf.js +0 -1
- package/dist/assets/RuntimeConfig-Bk0uYBhf.js +0 -1
- package/dist/assets/chat-page-Bph8M5zo.js +0 -58
- package/dist/assets/chat-session-display-CoN3Wmn-.js +0 -1
- package/dist/assets/desktop-update-config-1KBrqLBC.js +0 -1
- package/dist/assets/i18n-CwHZ-9vt.js +0 -1
- package/dist/assets/index-DafCdM4F.css +0 -1
- package/dist/assets/index-DdksE6U3.js +0 -6
- package/dist/assets/loader-circle-PsSP0H9n.js +0 -1
- package/dist/assets/play-DBQbBxTA.js +0 -1
- package/dist/assets/plus-DUOVbsyQ.js +0 -1
- package/dist/assets/search-MChQRYR1.js +0 -1
- package/dist/assets/skeleton-B-4vRq_Z.js +0 -1
- package/dist/assets/x-DuMhMATD.js +0 -1
- package/src/components/chat/ChatConversationPanel.tsx +0 -256
- package/src/components/chat/chat-child-session-panel.tsx +0 -270
- /package/dist/assets/{config-hints-BZoDjXye.js → config-hints-BhTmc9P1.js} +0 -0
- /package/dist/assets/{config-layout-DmlGaay2.js → config-layout-CHs0mAaR.js} +0 -0
package/src/api/server-path.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { api } from './client';
|
|
2
|
-
import type { ServerPathBrowseView } from './types';
|
|
2
|
+
import type { ServerPathBrowseView, ServerPathReadView } from './types';
|
|
3
3
|
|
|
4
4
|
export async function fetchServerPathBrowse(params?: {
|
|
5
5
|
path?: string | null;
|
|
6
6
|
includeFiles?: boolean;
|
|
7
7
|
}): Promise<ServerPathBrowseView> {
|
|
8
|
+
const path = typeof params?.path === 'string' ? params.path.trim() : '';
|
|
9
|
+
const includeFiles = Boolean(params?.includeFiles);
|
|
8
10
|
const query = new URLSearchParams();
|
|
9
|
-
if (
|
|
10
|
-
query.set('path',
|
|
11
|
+
if (path) {
|
|
12
|
+
query.set('path', path);
|
|
11
13
|
}
|
|
12
|
-
if (
|
|
14
|
+
if (includeFiles) {
|
|
13
15
|
query.set('includeFiles', '1');
|
|
14
16
|
}
|
|
15
17
|
const suffix = query.toString();
|
|
@@ -21,3 +23,24 @@ export async function fetchServerPathBrowse(params?: {
|
|
|
21
23
|
}
|
|
22
24
|
return response.data;
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
export async function fetchServerPathRead(params: {
|
|
28
|
+
path: string;
|
|
29
|
+
basePath?: string | null;
|
|
30
|
+
}): Promise<ServerPathReadView> {
|
|
31
|
+
const { path } = params;
|
|
32
|
+
const basePath =
|
|
33
|
+
typeof params.basePath === 'string' ? params.basePath.trim() : '';
|
|
34
|
+
const query = new URLSearchParams();
|
|
35
|
+
query.set('path', path.trim());
|
|
36
|
+
if (basePath) {
|
|
37
|
+
query.set('basePath', basePath);
|
|
38
|
+
}
|
|
39
|
+
const response = await api.get<ServerPathReadView>(
|
|
40
|
+
`/api/server-paths/read?${query.toString()}`
|
|
41
|
+
);
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(response.error.message);
|
|
44
|
+
}
|
|
45
|
+
return response.data;
|
|
46
|
+
}
|
package/src/api/types.ts
CHANGED
|
@@ -246,6 +246,13 @@ export type SessionConfigView = {
|
|
|
246
246
|
dmScope?: "main" | "per-peer" | "per-channel-peer" | "per-account-channel-peer";
|
|
247
247
|
};
|
|
248
248
|
|
|
249
|
+
export type RuntimeEntryView = {
|
|
250
|
+
enabled?: boolean;
|
|
251
|
+
label?: string;
|
|
252
|
+
type: string;
|
|
253
|
+
config?: Record<string, unknown>;
|
|
254
|
+
};
|
|
255
|
+
|
|
249
256
|
export type SessionEntryView = {
|
|
250
257
|
key: string;
|
|
251
258
|
createdAt: string;
|
|
@@ -347,17 +354,9 @@ export type SessionPatchUpdate = {
|
|
|
347
354
|
clearHistory?: boolean;
|
|
348
355
|
};
|
|
349
356
|
|
|
350
|
-
export type ServerPathEntryView = {
|
|
351
|
-
name: string;
|
|
352
|
-
path: string;
|
|
353
|
-
kind: "directory" | "file";
|
|
354
|
-
hidden: boolean;
|
|
355
|
-
};
|
|
357
|
+
export type ServerPathEntryView = { name: string; path: string; kind: "directory" | "file"; hidden: boolean };
|
|
356
358
|
|
|
357
|
-
export type ServerPathBreadcrumbView = {
|
|
358
|
-
label: string;
|
|
359
|
-
path: string;
|
|
360
|
-
};
|
|
359
|
+
export type ServerPathBreadcrumbView = { label: string; path: string };
|
|
361
360
|
|
|
362
361
|
export type ServerPathBrowseView = {
|
|
363
362
|
currentPath: string;
|
|
@@ -367,6 +366,8 @@ export type ServerPathBrowseView = {
|
|
|
367
366
|
entries: ServerPathEntryView[];
|
|
368
367
|
};
|
|
369
368
|
|
|
369
|
+
export type ServerPathReadView = { requestedPath: string; resolvedPath: string; kind: "text" | "markdown" | "binary"; sizeBytes: number; truncated: boolean; text?: string; languageHint?: string | null };
|
|
370
|
+
|
|
370
371
|
export type {
|
|
371
372
|
ChatSessionTypeCtaView,
|
|
372
373
|
ChatSessionTypeOptionView,
|
|
@@ -431,6 +432,9 @@ export type RuntimeConfigUpdate = {
|
|
|
431
432
|
engine?: string;
|
|
432
433
|
engineConfig?: Record<string, unknown>;
|
|
433
434
|
};
|
|
435
|
+
runtimes?: {
|
|
436
|
+
entries?: Record<string, RuntimeEntryView> | null;
|
|
437
|
+
};
|
|
434
438
|
list?: AgentProfileView[];
|
|
435
439
|
};
|
|
436
440
|
bindings?: AgentBindingView[];
|
|
@@ -500,6 +504,9 @@ export type ConfigView = {
|
|
|
500
504
|
contextTokens?: number;
|
|
501
505
|
maxToolIterations?: number;
|
|
502
506
|
};
|
|
507
|
+
runtimes?: {
|
|
508
|
+
entries?: Record<string, RuntimeEntryView>;
|
|
509
|
+
};
|
|
503
510
|
list?: AgentProfileView[];
|
|
504
511
|
context?: {
|
|
505
512
|
bootstrap?: {
|
package/src/app.tsx
CHANGED
|
@@ -7,8 +7,11 @@ import { AppLayout } from '@/components/layout/AppLayout';
|
|
|
7
7
|
import { isTransientAuthStatusBootstrapError, useAuthStatus } from '@/hooks/use-auth';
|
|
8
8
|
import { useRealtimeQueryBridge } from '@/hooks/use-realtime-query-bridge';
|
|
9
9
|
import { AppPresenterProvider } from '@/presenter/app-presenter-context';
|
|
10
|
+
import { PwaInstallBanner, PwaUpdateBanner } from '@/pwa/components/pwa-install-entry';
|
|
11
|
+
import { startNextClawPwa } from '@/pwa/register-pwa';
|
|
10
12
|
import { Toaster } from 'sonner';
|
|
11
13
|
import { Routes, Route, Navigate } from 'react-router-dom';
|
|
14
|
+
import { useEffect } from 'react';
|
|
12
15
|
|
|
13
16
|
const ModelConfigPage = lazy(async () => ({ default: (await import('@/components/config/ModelConfig')).ModelConfig }));
|
|
14
17
|
const ChatPage = lazy(async () => ({ default: (await import('@/components/chat/chat-page')).ChatPage }));
|
|
@@ -89,9 +92,15 @@ function AuthGate() {
|
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
export default function AppContent() {
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
startNextClawPwa();
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
92
99
|
return (
|
|
93
100
|
<QueryClientProvider client={appQueryClient}>
|
|
94
101
|
<AuthGate />
|
|
102
|
+
<PwaInstallBanner />
|
|
103
|
+
<PwaUpdateBanner />
|
|
95
104
|
<Toaster position="top-right" richColors />
|
|
96
105
|
</QueryClientProvider>
|
|
97
106
|
);
|
|
@@ -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
|
});
|
|
@@ -201,6 +201,22 @@ export function ChatSidebar() {
|
|
|
201
201
|
[agentsQuery.data?.agents]
|
|
202
202
|
);
|
|
203
203
|
const sortedItems = useMemo(() => sortSessionItemsByUpdatedAtDesc(items), [items]);
|
|
204
|
+
const childSessionsByParentKey = useMemo(() => {
|
|
205
|
+
const grouped = new Map<string, NcpSessionListItemView[]>();
|
|
206
|
+
for (const item of items) {
|
|
207
|
+
const parentSessionKey = item.session.parentSessionId?.trim();
|
|
208
|
+
if (!parentSessionKey) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const bucket = grouped.get(parentSessionKey) ?? [];
|
|
212
|
+
bucket.push(item);
|
|
213
|
+
grouped.set(parentSessionKey, bucket);
|
|
214
|
+
}
|
|
215
|
+
for (const bucket of grouped.values()) {
|
|
216
|
+
bucket.sort((left, right) => getSessionUpdatedAtTimestamp(right) - getSessionUpdatedAtTimestamp(left));
|
|
217
|
+
}
|
|
218
|
+
return grouped;
|
|
219
|
+
}, [items]);
|
|
204
220
|
const groups = useMemo(() => groupSessionsByDate(sortedItems), [sortedItems]);
|
|
205
221
|
const projectGroups = useMemo(() => groupSessionsByProject(sortedItems), [sortedItems]);
|
|
206
222
|
const defaultSessionType = inputSnapshot.defaultSessionType || 'native';
|
|
@@ -263,6 +279,7 @@ export function ChatSidebar() {
|
|
|
263
279
|
const context = resolveSessionContextView(session, inputSnapshot.sessionTypeOptions);
|
|
264
280
|
const isEditing = editingSessionKey === session.key;
|
|
265
281
|
const isSaving = savingSessionKey === session.key;
|
|
282
|
+
const childSessions = childSessionsByParentKey.get(session.key) ?? [];
|
|
266
283
|
return (
|
|
267
284
|
<ChatSidebarSessionItem
|
|
268
285
|
key={session.key}
|
|
@@ -275,10 +292,17 @@ export function ChatSidebar() {
|
|
|
275
292
|
agentId={session.agentId ?? null}
|
|
276
293
|
agentLabel={session.agentId ? (agentsById.get(session.agentId)?.displayName ?? session.agentId) : null}
|
|
277
294
|
agentAvatarUrl={session.agentId ? (agentsById.get(session.agentId)?.avatarUrl ?? null) : null}
|
|
295
|
+
childSessionCount={childSessions.length}
|
|
278
296
|
isEditing={isEditing}
|
|
279
297
|
draftLabel={draftLabel}
|
|
280
298
|
isSaving={isSaving}
|
|
281
299
|
onSelect={() => presenter.chatSessionListManager.selectSession(session.key)}
|
|
300
|
+
onOpenChildSessions={() =>
|
|
301
|
+
presenter.chatThreadManager.openChildSessionPanel({
|
|
302
|
+
parentSessionKey: session.key,
|
|
303
|
+
activeChildSessionKey: childSessions[0]?.session.key ?? null,
|
|
304
|
+
})
|
|
305
|
+
}
|
|
282
306
|
onStartEditing={() => startEditingSessionLabel(session)}
|
|
283
307
|
onDraftLabelChange={setDraftLabel}
|
|
284
308
|
onSave={() => saveSessionLabel(session)}
|
|
@@ -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
|
}
|