@nextclaw/ui 0.12.7 → 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 +85 -0
- package/dist/assets/ChannelsList-Ita2Zm1_.js +8 -0
- package/dist/assets/{DocBrowser-Cse_F8Nn.js → DocBrowser-6ReNjvzF.js} +1 -1
- package/dist/assets/DocBrowser-BNwbPHf4.js +1 -0
- package/dist/assets/{DocBrowserContext-Bai1WU2H.js → DocBrowserContext-B6SpA7Qs.js} +1 -1
- package/dist/assets/{LogoBadge-BdxMPc9v.js → LogoBadge-ByNLYg65.js} +1 -1
- package/dist/assets/MarketplacePage-CjX2MWww.js +1 -0
- package/dist/assets/{MarketplacePage-BbpAkllU.js → MarketplacePage-D0sDlYX4.js} +1 -1
- package/dist/assets/McpMarketplacePage-BGKJm1sJ.js +40 -0
- package/dist/assets/{ModelConfig-3GLqQ5GY.js → ModelConfig-BzZenCH-.js} +1 -1
- package/dist/assets/{ProviderScopedModelInput-BYNouw-i.js → ProviderScopedModelInput-Da7khnBA.js} +1 -1
- package/dist/assets/{ProvidersList-BR1gJ4Dm.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-DTeJvp8m.js → SearchConfig-BGkzXQP-.js} +1 -1
- package/dist/assets/{SecretsConfig-CCYO6NcV.js → SecretsConfig-D281Rotl.js} +2 -2
- package/dist/assets/{SessionsConfig-Du39vDgt.js → SessionsConfig-ChHQ7M5c.js} +2 -2
- package/dist/assets/{app-query-client-Dr5d-K8d.js → app-query-client-VnFElj4E.js} +1 -1
- package/dist/assets/{book-open-Da4OEPqB.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-CoFVxHXV.js → chunk-JZWAC4HX-DK5HPmIK.js} +1 -1
- package/dist/assets/{client-CSk58DcF.js → client-_i4MU2bB.js} +1 -1
- package/dist/assets/{config-D8KzikVB.js → config-DtIQwrHF.js} +1 -1
- package/dist/assets/{createLucideIcon-83gaZMtv.js → createLucideIcon-BSeTgkZW.js} +1 -1
- package/dist/assets/desktop-update-config-Dpcf4BKG.js +1 -0
- package/dist/assets/{dist-toEYs-MZ.js → dist-6TrrnPCR.js} +1 -1
- package/dist/assets/{dist-aTmhMDVh.js → dist-ccBFUi-o.js} +1 -1
- package/dist/assets/download-BhDxnyvU.js +1 -0
- package/dist/assets/{external-link-QQ0TC6X4.js → external-link-BgErLCNT.js} +1 -1
- package/dist/assets/{hash-DaFBEkmi.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-BmHX_ayZ.js → infiniteQueryBehavior-ZDS92Qpp.js} +1 -1
- package/dist/assets/loader-circle-ACM1s51e.js +1 -0
- package/dist/assets/{logos-Dzlz30M3.js → logos-x89HbrZ4.js} +1 -1
- package/dist/assets/{page-layout-D2eRufRQ.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-BSXxm5bj.js → popover-Bg1VoTZ6.js} +1 -1
- package/dist/assets/{refresh-ccw-B3zMtN-_.js → refresh-ccw-DT98i__E.js} +1 -1
- package/dist/assets/{refresh-cw-DlZkIHnJ.js → refresh-cw-C47QSEwg.js} +1 -1
- package/dist/assets/rotate-cw-JtFzpNn6.js +1 -0
- package/dist/assets/{save-Us9fg4Sj.js → save-3S6-H3Xw.js} +1 -1
- package/dist/assets/search-3kFR_zh9.js +1 -0
- package/dist/assets/{security-config-BGWYwxNr.js → security-config-BWaiARNk.js} +1 -1
- package/dist/assets/{select-DLYqySQK.js → select-DJ2MUjBB.js} +1 -1
- package/dist/assets/skeleton-ByQepn0M.js +1 -0
- package/dist/assets/{status-dot-DGayudyB.js → status-dot-vbanNPFU.js} +1 -1
- package/dist/assets/{switch-Dz2ScsKx.js → switch-BsLtHOH-.js} +1 -1
- package/dist/assets/{tabs-custom-CdKyjiGk.js → tabs-custom-D3HYMt6k.js} +1 -1
- package/dist/assets/{trash-2-Db-mZOZs.js → trash-2-G48scll7.js} +1 -1
- package/dist/assets/{use-infinite-scroll-loader-DBJX5hj0.js → use-infinite-scroll-loader-DkNhD-42.js} +1 -1
- package/dist/assets/{useConfirmDialog-DL0a-oGC.js → useConfirmDialog-BkvTN-vd.js} +1 -1
- package/dist/assets/{useMutation-BdZm-9PL.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/runtime-control.ts +34 -0
- package/src/api/runtime-control.types.ts +58 -0
- package/src/api/server-path.ts +27 -4
- package/src/api/types.ts +30 -10
- package/src/{App.test.tsx → app.test.tsx} +1 -1
- package/src/{App.tsx → app.tsx} +10 -1
- package/src/components/chat/ChatSidebar.test.tsx +79 -8
- package/src/components/chat/ChatSidebar.tsx +43 -26
- 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} +118 -155
- package/src/components/chat/chat-conversation-panel.tsx +412 -0
- package/src/components/chat/chat-page-runtime.test.ts +1 -1
- package/src/components/chat/chat-page-shell.tsx +1 -1
- package/src/components/chat/{ChatPage.tsx → chat-page.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 +94 -31
- package/src/components/chat/managers/chat-session-list.manager.ts +86 -14
- package/src/components/chat/managers/chat-ui.manager.ts +2 -0
- package/src/components/chat/ncp/README.md +1 -1
- package/src/components/chat/ncp/ncp-chat-input.manager.ts +7 -1
- package/src/components/chat/ncp/ncp-chat-page-data.test.ts +1 -1
- package/src/components/chat/ncp/{NcpChatPage.tsx → 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 +40 -2
- package/src/components/chat/ncp/ncp-session-adapter.ts +29 -0
- package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +54 -11
- package/src/components/chat/ncp/session-conversation/use-ncp-child-session-tabs-view.ts +4 -0
- package/src/components/chat/ncp/tests/ncp-chat-input.manager.test.ts +99 -0
- 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-session-list.store.ts +25 -54
- package/src/components/chat/stores/chat-thread.store.ts +24 -0
- package/src/components/common/ProviderScopedModelInput.tsx +12 -2
- package/src/components/config/ModelConfig.test.tsx +108 -2
- package/src/components/config/RuntimeConfig.tsx +154 -7
- package/src/components/config/desktop-update-config.test.tsx +85 -0
- package/src/components/config/desktop-update-config.tsx +44 -3
- package/src/components/config/runtime-control-card.test.tsx +255 -0
- package/src/components/config/runtime-control-card.tsx +301 -0
- package/src/components/config/runtime-presence-card.test.tsx +154 -0
- package/src/components/config/runtime-presence-card.tsx +163 -0
- package/src/components/layout/AppLayout.tsx +1 -1
- package/src/components/providers/ThemeProvider.tsx +5 -0
- package/src/desktop/desktop-update.types.ts +25 -0
- package/src/desktop/managers/desktop-presence.manager.ts +91 -0
- package/src/desktop/managers/desktop-update.manager.ts +37 -1
- package/src/desktop/stores/desktop-presence.store.ts +18 -0
- package/src/desktop/stores/desktop-update.store.ts +7 -1
- package/src/hooks/server-path/use-server-path-read.ts +20 -0
- package/src/hooks/use-runtime-control.ts +24 -0
- package/src/lib/chat-message.ts +14 -3
- package/src/lib/desktop-update-labels.utils.ts +28 -2
- package/src/lib/i18n.chat.ts +12 -1
- package/src/lib/i18n.pwa.ts +62 -0
- package/src/lib/i18n.runtime-control.ts +120 -0
- package/src/lib/i18n.ts +4 -6
- package/src/main.tsx +1 -1
- 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/runtime-control/runtime-control.manager.ts +118 -0
- package/src/vite-env.d.ts +9 -0
- package/dist/assets/ChannelsList-D8p4OlM6.js +0 -8
- package/dist/assets/ChatPage-A45t1Rmf.js +0 -58
- package/dist/assets/DocBrowser-B2MpsnU9.js +0 -1
- package/dist/assets/MarketplacePage-BNZ3Jx5d.js +0 -1
- package/dist/assets/McpMarketplacePage-CxPFOgxv.js +0 -40
- package/dist/assets/RemoteAccessPage-DyYVWsyK.js +0 -1
- package/dist/assets/RuntimeConfig-ChdfK4Y_.js +0 -1
- package/dist/assets/chat-session-display-CAlPrnlV.js +0 -1
- package/dist/assets/desktop-update-config-CfoVwf-w.js +0 -1
- package/dist/assets/i18n-C3jb83S6.js +0 -1
- package/dist/assets/index-CE4N7ItL.css +0 -1
- package/dist/assets/index-riX7Sg0_.js +0 -6
- package/dist/assets/loader-circle-BjMg63eu.js +0 -1
- package/dist/assets/plus-CIXME2pD.js +0 -1
- package/dist/assets/search-B_Qr0f6C.js +0 -1
- package/dist/assets/skeleton-CYQJazv6.js +0 -1
- package/dist/assets/x-B8Tho_xC.js +0 -1
- package/src/components/chat/ChatConversationPanel.tsx +0 -256
- package/src/components/chat/chat-child-session-panel.tsx +0 -262
- /package/dist/assets/{config-hints-GSUMvmSo.js → config-hints-BhTmc9P1.js} +0 -0
- /package/dist/assets/{config-layout-CgBMG7OL.js → config-layout-CHs0mAaR.js} +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { api } from './client';
|
|
2
|
+
import type { RuntimeControlActionResult, RuntimeControlView } from './runtime-control.types';
|
|
3
|
+
|
|
4
|
+
export async function fetchRuntimeControl(): Promise<RuntimeControlView> {
|
|
5
|
+
const response = await api.get<RuntimeControlView>('/api/runtime/control');
|
|
6
|
+
if (!response.ok) {
|
|
7
|
+
throw new Error(response.error.message);
|
|
8
|
+
}
|
|
9
|
+
return response.data;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function startRuntimeService(): Promise<RuntimeControlActionResult> {
|
|
13
|
+
const response = await api.post<RuntimeControlActionResult>('/api/runtime/control/start-service', {});
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error(response.error.message);
|
|
16
|
+
}
|
|
17
|
+
return response.data;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function restartRuntimeService(): Promise<RuntimeControlActionResult> {
|
|
21
|
+
const response = await api.post<RuntimeControlActionResult>('/api/runtime/control/restart-service', {});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(response.error.message);
|
|
24
|
+
}
|
|
25
|
+
return response.data;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function stopRuntimeService(): Promise<RuntimeControlActionResult> {
|
|
29
|
+
const response = await api.post<RuntimeControlActionResult>('/api/runtime/control/stop-service', {});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(response.error.message);
|
|
32
|
+
}
|
|
33
|
+
return response.data;
|
|
34
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export type RuntimeControlEnvironment =
|
|
2
|
+
| 'desktop-embedded'
|
|
3
|
+
| 'managed-local-service'
|
|
4
|
+
| 'self-hosted-web'
|
|
5
|
+
| 'shared-web';
|
|
6
|
+
|
|
7
|
+
export type RuntimeLifecycleState =
|
|
8
|
+
| 'healthy'
|
|
9
|
+
| 'starting-service'
|
|
10
|
+
| 'restarting-service'
|
|
11
|
+
| 'stopping-service'
|
|
12
|
+
| 'restarting-app'
|
|
13
|
+
| 'recovering'
|
|
14
|
+
| 'unavailable'
|
|
15
|
+
| 'failed';
|
|
16
|
+
|
|
17
|
+
export type RuntimeActionImpact = 'none' | 'brief-ui-disconnect' | 'full-app-relaunch';
|
|
18
|
+
|
|
19
|
+
export type RuntimeActionCapability = {
|
|
20
|
+
available: boolean;
|
|
21
|
+
requiresConfirmation: boolean;
|
|
22
|
+
impact: RuntimeActionImpact;
|
|
23
|
+
reasonIfUnavailable?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type RuntimeServiceState =
|
|
27
|
+
| 'running'
|
|
28
|
+
| 'stopped'
|
|
29
|
+
| 'starting'
|
|
30
|
+
| 'stopping'
|
|
31
|
+
| 'restarting'
|
|
32
|
+
| 'unknown';
|
|
33
|
+
|
|
34
|
+
export type RuntimeControlView = {
|
|
35
|
+
environment: RuntimeControlEnvironment;
|
|
36
|
+
lifecycle: RuntimeLifecycleState;
|
|
37
|
+
serviceState: RuntimeServiceState;
|
|
38
|
+
canStartService: RuntimeActionCapability;
|
|
39
|
+
canRestartService: RuntimeActionCapability;
|
|
40
|
+
canStopService: RuntimeActionCapability;
|
|
41
|
+
canRestartApp: RuntimeActionCapability;
|
|
42
|
+
ownerLabel?: string;
|
|
43
|
+
managementHint?: string;
|
|
44
|
+
message?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type RuntimeControlAction =
|
|
48
|
+
| 'start-service'
|
|
49
|
+
| 'restart-service'
|
|
50
|
+
| 'stop-service'
|
|
51
|
+
| 'restart-app';
|
|
52
|
+
|
|
53
|
+
export type RuntimeControlActionResult = {
|
|
54
|
+
accepted: boolean;
|
|
55
|
+
action: RuntimeControlAction;
|
|
56
|
+
lifecycle: RuntimeLifecycleState;
|
|
57
|
+
message: string;
|
|
58
|
+
};
|
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
|
@@ -174,6 +174,16 @@ export type {
|
|
|
174
174
|
RemoteSettingsUpdateRequest,
|
|
175
175
|
RemoteSettingsView
|
|
176
176
|
} from './remote.types';
|
|
177
|
+
export type {
|
|
178
|
+
RuntimeActionCapability,
|
|
179
|
+
RuntimeActionImpact,
|
|
180
|
+
RuntimeControlAction,
|
|
181
|
+
RuntimeControlEnvironment,
|
|
182
|
+
RuntimeControlView,
|
|
183
|
+
RuntimeLifecycleState,
|
|
184
|
+
RuntimeServiceState,
|
|
185
|
+
RuntimeControlActionResult
|
|
186
|
+
} from './runtime-control.types';
|
|
177
187
|
|
|
178
188
|
export type AgentProfileView = {
|
|
179
189
|
id: string;
|
|
@@ -236,10 +246,19 @@ export type SessionConfigView = {
|
|
|
236
246
|
dmScope?: "main" | "per-peer" | "per-channel-peer" | "per-account-channel-peer";
|
|
237
247
|
};
|
|
238
248
|
|
|
249
|
+
export type RuntimeEntryView = {
|
|
250
|
+
enabled?: boolean;
|
|
251
|
+
label?: string;
|
|
252
|
+
type: string;
|
|
253
|
+
config?: Record<string, unknown>;
|
|
254
|
+
};
|
|
255
|
+
|
|
239
256
|
export type SessionEntryView = {
|
|
240
257
|
key: string;
|
|
241
258
|
createdAt: string;
|
|
242
259
|
updatedAt: string;
|
|
260
|
+
lastMessageAt?: string;
|
|
261
|
+
readAt?: string;
|
|
243
262
|
agentId?: string;
|
|
244
263
|
label?: string;
|
|
245
264
|
channel?: string;
|
|
@@ -331,20 +350,13 @@ export type SessionPatchUpdate = {
|
|
|
331
350
|
preferredThinking?: ThinkingLevel | null;
|
|
332
351
|
sessionType?: string | null;
|
|
333
352
|
projectRoot?: string | null;
|
|
353
|
+
uiReadAt?: string | null;
|
|
334
354
|
clearHistory?: boolean;
|
|
335
355
|
};
|
|
336
356
|
|
|
337
|
-
export type ServerPathEntryView = {
|
|
338
|
-
name: string;
|
|
339
|
-
path: string;
|
|
340
|
-
kind: "directory" | "file";
|
|
341
|
-
hidden: boolean;
|
|
342
|
-
};
|
|
357
|
+
export type ServerPathEntryView = { name: string; path: string; kind: "directory" | "file"; hidden: boolean };
|
|
343
358
|
|
|
344
|
-
export type ServerPathBreadcrumbView = {
|
|
345
|
-
label: string;
|
|
346
|
-
path: string;
|
|
347
|
-
};
|
|
359
|
+
export type ServerPathBreadcrumbView = { label: string; path: string };
|
|
348
360
|
|
|
349
361
|
export type ServerPathBrowseView = {
|
|
350
362
|
currentPath: string;
|
|
@@ -354,6 +366,8 @@ export type ServerPathBrowseView = {
|
|
|
354
366
|
entries: ServerPathEntryView[];
|
|
355
367
|
};
|
|
356
368
|
|
|
369
|
+
export type ServerPathReadView = { requestedPath: string; resolvedPath: string; kind: "text" | "markdown" | "binary"; sizeBytes: number; truncated: boolean; text?: string; languageHint?: string | null };
|
|
370
|
+
|
|
357
371
|
export type {
|
|
358
372
|
ChatSessionTypeCtaView,
|
|
359
373
|
ChatSessionTypeOptionView,
|
|
@@ -418,6 +432,9 @@ export type RuntimeConfigUpdate = {
|
|
|
418
432
|
engine?: string;
|
|
419
433
|
engineConfig?: Record<string, unknown>;
|
|
420
434
|
};
|
|
435
|
+
runtimes?: {
|
|
436
|
+
entries?: Record<string, RuntimeEntryView> | null;
|
|
437
|
+
};
|
|
421
438
|
list?: AgentProfileView[];
|
|
422
439
|
};
|
|
423
440
|
bindings?: AgentBindingView[];
|
|
@@ -487,6 +504,9 @@ export type ConfigView = {
|
|
|
487
504
|
contextTokens?: number;
|
|
488
505
|
maxToolIterations?: number;
|
|
489
506
|
};
|
|
507
|
+
runtimes?: {
|
|
508
|
+
entries?: Record<string, RuntimeEntryView>;
|
|
509
|
+
};
|
|
490
510
|
list?: AgentProfileView[];
|
|
491
511
|
context?: {
|
|
492
512
|
bootstrap?: {
|
|
@@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
3
3
|
import { MemoryRouter } from 'react-router-dom';
|
|
4
4
|
import { I18nProvider } from '@/components/providers/I18nProvider';
|
|
5
5
|
import { ThemeProvider } from '@/components/providers/ThemeProvider';
|
|
6
|
-
import AppContent from '@/
|
|
6
|
+
import AppContent from '@/app';
|
|
7
7
|
|
|
8
8
|
const mocks = vi.hoisted(() => ({
|
|
9
9
|
refetch: vi.fn(),
|
package/src/{App.tsx → app.tsx}
RENAMED
|
@@ -7,11 +7,14 @@ 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
|
-
const ChatPage = lazy(async () => ({ default: (await import('@/components/chat/
|
|
17
|
+
const ChatPage = lazy(async () => ({ default: (await import('@/components/chat/chat-page')).ChatPage }));
|
|
15
18
|
const SearchConfigPage = lazy(async () => ({ default: (await import('@/components/config/SearchConfig')).SearchConfig }));
|
|
16
19
|
const ProvidersListPage = lazy(async () => ({ default: (await import('@/components/config/ProvidersList')).ProvidersList }));
|
|
17
20
|
const ChannelsListPage = lazy(async () => ({ default: (await import('@/components/config/ChannelsList')).ChannelsList }));
|
|
@@ -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 }>,
|
|
@@ -32,12 +33,14 @@ vi.mock('@/components/chat/presenter/chat-presenter-context', () => ({
|
|
|
32
33
|
setQuery: mocks.setQuery,
|
|
33
34
|
setListMode: mocks.setListMode,
|
|
34
35
|
selectSession: mocks.selectSession,
|
|
35
|
-
markSessionRead: (
|
|
36
|
-
sessionKey
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
markSessionRead: (
|
|
37
|
+
sessionKey: string | null | undefined,
|
|
38
|
+
readAt: string | null | undefined,
|
|
39
|
+
) => (sessionKey ? useChatSessionListStore.getState().markSessionRead(sessionKey, readAt) : undefined),
|
|
40
|
+
},
|
|
41
|
+
chatThreadManager: {
|
|
42
|
+
openChildSessionPanel: mocks.openChildSessionPanel,
|
|
43
|
+
},
|
|
41
44
|
})
|
|
42
45
|
}));
|
|
43
46
|
|
|
@@ -114,6 +117,7 @@ function resetSidebarTestState() {
|
|
|
114
117
|
mocks.setQuery.mockReset();
|
|
115
118
|
mocks.setListMode.mockReset();
|
|
116
119
|
mocks.selectSession.mockReset();
|
|
120
|
+
mocks.openChildSessionPanel.mockReset();
|
|
117
121
|
mocks.docOpen.mockReset();
|
|
118
122
|
mocks.updateNcpSession.mockReset();
|
|
119
123
|
mocks.updateNcpSession.mockResolvedValue({});
|
|
@@ -132,8 +136,7 @@ function resetSidebarTestState() {
|
|
|
132
136
|
}
|
|
133
137
|
});
|
|
134
138
|
useChatSessionListStore.setState({
|
|
135
|
-
|
|
136
|
-
hasHydratedReadWatermarks: false,
|
|
139
|
+
optimisticReadAtBySessionKey: {},
|
|
137
140
|
snapshot: {
|
|
138
141
|
...useChatSessionListStore.getState().snapshot,
|
|
139
142
|
query: '',
|
|
@@ -514,6 +517,8 @@ describe('ChatSidebar session item interactions', () => {
|
|
|
514
517
|
key: 'session:ncp-1',
|
|
515
518
|
createdAt: '2026-03-19T09:00:00.000Z',
|
|
516
519
|
updatedAt: '2026-03-19T09:05:00.000Z',
|
|
520
|
+
lastMessageAt: '2026-03-19T09:05:00.000Z',
|
|
521
|
+
readAt: '2026-03-19T09:05:00.000Z',
|
|
517
522
|
label: 'Current Task',
|
|
518
523
|
sessionType: 'native',
|
|
519
524
|
sessionTypeMutable: false,
|
|
@@ -523,6 +528,8 @@ describe('ChatSidebar session item interactions', () => {
|
|
|
523
528
|
key: 'session:ncp-2',
|
|
524
529
|
createdAt: '2026-03-19T10:00:00.000Z',
|
|
525
530
|
updatedAt: '2026-03-19T10:05:00.000Z',
|
|
531
|
+
lastMessageAt: '2026-03-19T10:05:00.000Z',
|
|
532
|
+
readAt: '2026-03-19T10:05:00.000Z',
|
|
526
533
|
label: 'Background Task',
|
|
527
534
|
sessionType: 'native',
|
|
528
535
|
sessionTypeMutable: false,
|
|
@@ -550,6 +557,8 @@ describe('ChatSidebar session item interactions', () => {
|
|
|
550
557
|
key: 'session:ncp-2',
|
|
551
558
|
createdAt: '2026-03-19T10:00:00.000Z',
|
|
552
559
|
updatedAt: '2026-03-19T10:06:00.000Z',
|
|
560
|
+
lastMessageAt: '2026-03-19T10:06:00.000Z',
|
|
561
|
+
readAt: '2026-03-19T10:05:00.000Z',
|
|
553
562
|
label: 'Background Task',
|
|
554
563
|
sessionType: 'native',
|
|
555
564
|
sessionTypeMutable: false,
|
|
@@ -571,6 +580,8 @@ describe('ChatSidebar session item interactions', () => {
|
|
|
571
580
|
key: 'session:ncp-2',
|
|
572
581
|
createdAt: '2026-03-19T10:00:00.000Z',
|
|
573
582
|
updatedAt: '2026-03-19T10:06:00.000Z',
|
|
583
|
+
lastMessageAt: '2026-03-19T10:06:00.000Z',
|
|
584
|
+
readAt: '2026-03-19T10:05:00.000Z',
|
|
574
585
|
label: 'Background Task',
|
|
575
586
|
sessionType: 'native',
|
|
576
587
|
sessionTypeMutable: false,
|
|
@@ -601,4 +612,64 @@ describe('ChatSidebar session item interactions', () => {
|
|
|
601
612
|
|
|
602
613
|
expect(screen.queryByLabelText('Session has unread updates')).toBeNull();
|
|
603
614
|
});
|
|
615
|
+
|
|
616
|
+
it('does not show an unread dot for sessions without a persisted ui read baseline', () => {
|
|
617
|
+
mocks.sessionItems = [
|
|
618
|
+
createSessionItem({
|
|
619
|
+
key: 'session:ncp-legacy',
|
|
620
|
+
createdAt: '2026-03-19T09:00:00.000Z',
|
|
621
|
+
updatedAt: '2026-03-19T09:05:00.000Z',
|
|
622
|
+
lastMessageAt: '2026-03-19T09:05:00.000Z',
|
|
623
|
+
label: 'Legacy Session',
|
|
624
|
+
sessionType: 'native',
|
|
625
|
+
sessionTypeMutable: false,
|
|
626
|
+
messageCount: 1
|
|
627
|
+
})
|
|
628
|
+
];
|
|
629
|
+
|
|
630
|
+
render(
|
|
631
|
+
<MemoryRouter>
|
|
632
|
+
<ChatSidebar />
|
|
633
|
+
</MemoryRouter>
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
expect(screen.queryByLabelText('Session has unread updates')).toBeNull();
|
|
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
|
+
});
|
|
604
675
|
});
|
|
@@ -149,28 +149,13 @@ const navItems = [
|
|
|
149
149
|
function useChatSessionUnreadState(
|
|
150
150
|
items: readonly NcpSessionListItemView[],
|
|
151
151
|
selectedSessionKey: string | null,
|
|
152
|
-
markSessionRead: (
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
markSessionRead: (
|
|
153
|
+
sessionKey: string | null | undefined,
|
|
154
|
+
readAt: string | null | undefined,
|
|
155
|
+
currentReadAt?: string | null,
|
|
155
156
|
) => void,
|
|
156
157
|
): Record<string, string> {
|
|
157
|
-
const
|
|
158
|
-
const hasHydratedReadWatermarks = useChatSessionListStore((state) => state.hasHydratedReadWatermarks);
|
|
159
|
-
|
|
160
|
-
useEffect(() => {
|
|
161
|
-
const syncHydratedReadWatermarks = () => {
|
|
162
|
-
if (hasHydratedReadWatermarks || items.length === 0) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
hydrateReadWatermarks(
|
|
166
|
-
items.map(({ session }) => ({
|
|
167
|
-
sessionKey: session.key,
|
|
168
|
-
updatedAt: session.updatedAt
|
|
169
|
-
}))
|
|
170
|
-
);
|
|
171
|
-
};
|
|
172
|
-
syncHydratedReadWatermarks();
|
|
173
|
-
}, [hasHydratedReadWatermarks, hydrateReadWatermarks, items]);
|
|
158
|
+
const optimisticReadAtBySessionKey = useChatSessionListStore((state) => state.optimisticReadAtBySessionKey);
|
|
174
159
|
|
|
175
160
|
useEffect(() => {
|
|
176
161
|
const syncSelectedSessionReadState = () => {
|
|
@@ -182,12 +167,16 @@ function useChatSessionUnreadState(
|
|
|
182
167
|
return;
|
|
183
168
|
}
|
|
184
169
|
const { session: selectedSession } = selectedItem;
|
|
185
|
-
markSessionRead(
|
|
170
|
+
markSessionRead(
|
|
171
|
+
selectedSession.key,
|
|
172
|
+
selectedSession.lastMessageAt,
|
|
173
|
+
selectedSession.readAt,
|
|
174
|
+
);
|
|
186
175
|
};
|
|
187
176
|
syncSelectedSessionReadState();
|
|
188
177
|
}, [items, markSessionRead, selectedSessionKey]);
|
|
189
178
|
|
|
190
|
-
return
|
|
179
|
+
return optimisticReadAtBySessionKey;
|
|
191
180
|
}
|
|
192
181
|
|
|
193
182
|
export function ChatSidebar() {
|
|
@@ -212,6 +201,22 @@ export function ChatSidebar() {
|
|
|
212
201
|
[agentsQuery.data?.agents]
|
|
213
202
|
);
|
|
214
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]);
|
|
215
220
|
const groups = useMemo(() => groupSessionsByDate(sortedItems), [sortedItems]);
|
|
216
221
|
const projectGroups = useMemo(() => groupSessionsByProject(sortedItems), [sortedItems]);
|
|
217
222
|
const defaultSessionType = inputSnapshot.defaultSessionType || 'native';
|
|
@@ -220,11 +225,10 @@ export function ChatSidebar() {
|
|
|
220
225
|
[defaultSessionType, inputSnapshot.sessionTypeOptions]
|
|
221
226
|
);
|
|
222
227
|
const isProjectFirstView = listSnapshot.listMode === 'project-first';
|
|
223
|
-
const
|
|
228
|
+
const optimisticReadAtBySessionKey = useChatSessionUnreadState(
|
|
224
229
|
items,
|
|
225
230
|
listSnapshot.selectedSessionKey,
|
|
226
231
|
presenter.chatSessionListManager.markSessionRead,
|
|
227
|
-
presenter.chatSessionListManager.hydrateReadWatermarks,
|
|
228
232
|
);
|
|
229
233
|
const handleLanguageSwitch = (nextLang: I18nLanguage) => {
|
|
230
234
|
if (language === nextLang) return;
|
|
@@ -261,15 +265,21 @@ export function ChatSidebar() {
|
|
|
261
265
|
};
|
|
262
266
|
const renderSessionItem = ({ session, runStatus }: NcpSessionListItemView) => {
|
|
263
267
|
const active = listSnapshot.selectedSessionKey === session.key;
|
|
268
|
+
const optimisticReadAt = optimisticReadAtBySessionKey[session.key];
|
|
269
|
+
const effectiveReadAt =
|
|
270
|
+
optimisticReadAt && session.readAt
|
|
271
|
+
? (optimisticReadAt.localeCompare(session.readAt) > 0 ? optimisticReadAt : session.readAt)
|
|
272
|
+
: optimisticReadAt ?? session.readAt;
|
|
264
273
|
const showUnreadDot = shouldShowUnreadSessionIndicator({
|
|
265
274
|
active,
|
|
266
|
-
|
|
267
|
-
|
|
275
|
+
lastMessageAt: session.lastMessageAt,
|
|
276
|
+
readAt: effectiveReadAt,
|
|
268
277
|
runStatus,
|
|
269
278
|
});
|
|
270
279
|
const context = resolveSessionContextView(session, inputSnapshot.sessionTypeOptions);
|
|
271
280
|
const isEditing = editingSessionKey === session.key;
|
|
272
281
|
const isSaving = savingSessionKey === session.key;
|
|
282
|
+
const childSessions = childSessionsByParentKey.get(session.key) ?? [];
|
|
273
283
|
return (
|
|
274
284
|
<ChatSidebarSessionItem
|
|
275
285
|
key={session.key}
|
|
@@ -282,10 +292,17 @@ export function ChatSidebar() {
|
|
|
282
292
|
agentId={session.agentId ?? null}
|
|
283
293
|
agentLabel={session.agentId ? (agentsById.get(session.agentId)?.displayName ?? session.agentId) : null}
|
|
284
294
|
agentAvatarUrl={session.agentId ? (agentsById.get(session.agentId)?.avatarUrl ?? null) : null}
|
|
295
|
+
childSessionCount={childSessions.length}
|
|
285
296
|
isEditing={isEditing}
|
|
286
297
|
draftLabel={draftLabel}
|
|
287
298
|
isSaving={isSaving}
|
|
288
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
|
+
}
|
|
289
306
|
onStartEditing={() => startEditingSessionLabel(session)}
|
|
290
307
|
onDraftLabelChange={setDraftLabel}
|
|
291
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
|
}
|