@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
package/src/components/chat/{ChatConversationPanel.test.tsx → chat-conversation-panel.test.tsx}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { render, screen } from "@testing-library/react";
|
|
2
2
|
import userEvent from "@testing-library/user-event";
|
|
3
3
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { ChatConversationPanel } from "@/components/chat/chat-conversation-panel";
|
|
5
|
+
import { ChatSessionWorkspacePanel } from "@/components/chat/chat-session-workspace-panel";
|
|
6
6
|
import type { ResolvedChildSessionTab } from "@/components/chat/ncp/session-conversation/use-ncp-child-session-tabs-view";
|
|
7
7
|
import { useChatInputStore } from "@/components/chat/stores/chat-input.store";
|
|
8
8
|
import { useChatSessionListStore } from "@/components/chat/stores/chat-session-list.store";
|
|
@@ -24,6 +24,8 @@ const mocks = vi.hoisted(() => ({
|
|
|
24
24
|
title: "北京天气",
|
|
25
25
|
agentId: "weather",
|
|
26
26
|
updatedAt: "2026-04-10T09:00:00.000Z",
|
|
27
|
+
lastMessageAt: "2026-04-10T09:00:00.000Z",
|
|
28
|
+
readAt: "2026-04-10T09:00:00.000Z",
|
|
27
29
|
sessionTypeLabel: "Codex",
|
|
28
30
|
preferredModel: "openai/gpt-5.3-codex",
|
|
29
31
|
projectName: "project-alpha",
|
|
@@ -49,6 +51,14 @@ vi.mock("@/components/chat/containers/chat-message-list.container", () => ({
|
|
|
49
51
|
ChatMessageListContainer: () => <div data-testid="child-chat-message-list" />,
|
|
50
52
|
}));
|
|
51
53
|
|
|
54
|
+
vi.mock("@/components/chat/chat-session-workspace-file-preview", () => ({
|
|
55
|
+
ChatSessionWorkspaceFilePreview: ({
|
|
56
|
+
file,
|
|
57
|
+
}: {
|
|
58
|
+
file: { path: string };
|
|
59
|
+
}) => <div data-testid="workspace-file-preview">{file.path}</div>,
|
|
60
|
+
}));
|
|
61
|
+
|
|
52
62
|
vi.mock("@/components/chat/ChatWelcome", () => ({
|
|
53
63
|
ChatWelcome: ({
|
|
54
64
|
onCreateSession,
|
|
@@ -73,9 +83,13 @@ vi.mock("@/components/chat/presenter/chat-presenter-context", () => ({
|
|
|
73
83
|
chatThreadManager: {
|
|
74
84
|
deleteSession: mocks.deleteSession,
|
|
75
85
|
goToProviders: mocks.goToProviders,
|
|
86
|
+
openChildSessionPanel: vi.fn(),
|
|
87
|
+
openFilePreview: vi.fn(),
|
|
76
88
|
openSessionFromToolAction: vi.fn(),
|
|
77
89
|
selectChildSessionDetail: vi.fn(),
|
|
78
|
-
|
|
90
|
+
selectWorkspaceFile: vi.fn(),
|
|
91
|
+
closeWorkspaceFile: vi.fn(),
|
|
92
|
+
closeWorkspacePanel: vi.fn(),
|
|
79
93
|
goToParentSession: vi.fn(),
|
|
80
94
|
},
|
|
81
95
|
chatSessionListManager: {
|
|
@@ -84,17 +98,14 @@ vi.mock("@/components/chat/presenter/chat-presenter-context", () => ({
|
|
|
84
98
|
setSelectedAgentId: mocks.setSelectedAgentId,
|
|
85
99
|
markSessionRead: (
|
|
86
100
|
sessionKey: string | null | undefined,
|
|
87
|
-
|
|
101
|
+
readAt: string | null | undefined,
|
|
88
102
|
) =>
|
|
89
103
|
sessionKey
|
|
90
104
|
? useChatSessionListStore.getState().markSessionRead(
|
|
91
105
|
sessionKey,
|
|
92
|
-
|
|
106
|
+
readAt,
|
|
93
107
|
)
|
|
94
108
|
: undefined,
|
|
95
|
-
hydrateReadWatermarks: (
|
|
96
|
-
entries: readonly { sessionKey: string; updatedAt: string | null | undefined }[],
|
|
97
|
-
) => useChatSessionListStore.getState().hydrateReadWatermarks(entries),
|
|
98
109
|
},
|
|
99
110
|
chatInputManager: {
|
|
100
111
|
setPendingSessionType: mocks.setPendingSessionType,
|
|
@@ -183,17 +194,19 @@ describe("ChatConversationPanel", () => {
|
|
|
183
194
|
isAwaitingAssistantOutput: false,
|
|
184
195
|
parentSessionKey: null,
|
|
185
196
|
parentSessionLabel: null,
|
|
197
|
+
workspacePanelParentKey: null,
|
|
186
198
|
availableAgents: [
|
|
187
199
|
{ id: "main", displayName: "Main", runtime: "native" },
|
|
188
200
|
{ id: "engineer", displayName: "Engineer", runtime: "codex" },
|
|
189
201
|
],
|
|
190
202
|
childSessionTabs: [],
|
|
191
203
|
activeChildSessionKey: null,
|
|
204
|
+
workspaceFileTabs: [],
|
|
205
|
+
activeWorkspaceFileKey: null,
|
|
192
206
|
},
|
|
193
207
|
});
|
|
194
208
|
useChatSessionListStore.setState({
|
|
195
|
-
|
|
196
|
-
hasHydratedReadWatermarks: false,
|
|
209
|
+
optimisticReadAtBySessionKey: {},
|
|
197
210
|
snapshot: {
|
|
198
211
|
...useChatSessionListStore.getState().snapshot,
|
|
199
212
|
},
|
|
@@ -256,6 +269,50 @@ describe("ChatConversationPanel", () => {
|
|
|
256
269
|
expect(screen.queryByText("Engineer")).toBeNull();
|
|
257
270
|
});
|
|
258
271
|
|
|
272
|
+
it("keeps the message area clean while a session history is hydrating", () => {
|
|
273
|
+
useChatThreadStore.setState({
|
|
274
|
+
snapshot: {
|
|
275
|
+
...useChatThreadStore.getState().snapshot,
|
|
276
|
+
sessionKey: "session-1",
|
|
277
|
+
canDeleteSession: true,
|
|
278
|
+
isHistoryLoading: true,
|
|
279
|
+
messages: [],
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
render(<ChatConversationPanel />);
|
|
284
|
+
|
|
285
|
+
expect(
|
|
286
|
+
screen.queryByRole("status", { name: "Loading session history..." }),
|
|
287
|
+
).toBeNull();
|
|
288
|
+
expect(screen.queryByText("No messages yet. Send one to start.")).toBeNull();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("does not auto-open the child-session panel until the panel is explicitly opened", () => {
|
|
292
|
+
useChatThreadStore.setState({
|
|
293
|
+
snapshot: {
|
|
294
|
+
...useChatThreadStore.getState().snapshot,
|
|
295
|
+
sessionKey: "parent-session-1",
|
|
296
|
+
sessionDisplayName: "Parent Session",
|
|
297
|
+
canDeleteSession: true,
|
|
298
|
+
childSessionTabs: [
|
|
299
|
+
{
|
|
300
|
+
sessionKey: "child-session-1",
|
|
301
|
+
parentSessionKey: "parent-session-1",
|
|
302
|
+
label: "北京天气",
|
|
303
|
+
agentId: "weather",
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
activeChildSessionKey: "child-session-1",
|
|
307
|
+
workspacePanelParentKey: null,
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
render(<ChatConversationPanel />);
|
|
312
|
+
|
|
313
|
+
expect(screen.queryByLabelText("Close child session panel")).toBeNull();
|
|
314
|
+
});
|
|
315
|
+
|
|
259
316
|
it("creates a draft session with the selected draft agent runtime", async () => {
|
|
260
317
|
const user = userEvent.setup();
|
|
261
318
|
|
|
@@ -290,8 +347,8 @@ describe("ChatConversationPanel", () => {
|
|
|
290
347
|
});
|
|
291
348
|
});
|
|
292
349
|
|
|
293
|
-
describe("
|
|
294
|
-
it("
|
|
350
|
+
describe("ChatSessionWorkspacePanel", () => {
|
|
351
|
+
it("renders child session tabs and active child metadata in the workspace sidebar", () => {
|
|
295
352
|
mocks.resolvedChildTabs = [
|
|
296
353
|
{
|
|
297
354
|
sessionKey: "child-session-1",
|
|
@@ -299,15 +356,18 @@ describe("ChatChildSessionPanel", () => {
|
|
|
299
356
|
title: "北京天气",
|
|
300
357
|
agentId: "weather",
|
|
301
358
|
updatedAt: "2026-04-10T09:00:00.000Z",
|
|
359
|
+
lastMessageAt: "2026-04-10T09:00:00.000Z",
|
|
360
|
+
readAt: "2026-04-10T09:00:00.000Z",
|
|
302
361
|
sessionTypeLabel: "Codex",
|
|
303
362
|
preferredModel: "openai/gpt-5.3-codex",
|
|
304
363
|
projectName: "project-alpha",
|
|
305
364
|
projectRoot: "/Users/demo/project-alpha",
|
|
306
365
|
},
|
|
307
366
|
];
|
|
367
|
+
|
|
308
368
|
render(
|
|
309
|
-
<
|
|
310
|
-
|
|
369
|
+
<ChatSessionWorkspacePanel
|
|
370
|
+
childSessionTabs={[
|
|
311
371
|
{
|
|
312
372
|
sessionKey: "child-session-1",
|
|
313
373
|
parentSessionKey: "parent-session-1",
|
|
@@ -315,20 +375,26 @@ describe("ChatChildSessionPanel", () => {
|
|
|
315
375
|
agentId: "weather",
|
|
316
376
|
},
|
|
317
377
|
]}
|
|
318
|
-
|
|
378
|
+
activeChildSessionKey="child-session-1"
|
|
379
|
+
workspaceFileTabs={[]}
|
|
380
|
+
activeWorkspaceFileKey={null}
|
|
381
|
+
sessionProjectRoot="/Users/demo/project-alpha"
|
|
319
382
|
onSelectSession={vi.fn()}
|
|
383
|
+
onSelectFile={vi.fn()}
|
|
384
|
+
onCloseFile={vi.fn()}
|
|
320
385
|
onClose={vi.fn()}
|
|
321
386
|
onBackToParent={vi.fn()}
|
|
387
|
+
onFileOpen={vi.fn()}
|
|
322
388
|
/>,
|
|
323
389
|
);
|
|
324
390
|
|
|
325
|
-
expect(screen.
|
|
391
|
+
expect(screen.queryByText("Child sessions")).toBeNull();
|
|
392
|
+
expect(screen.getAllByText("北京天气")).toHaveLength(2);
|
|
326
393
|
expect(screen.getByText("Codex")).toBeTruthy();
|
|
327
394
|
expect(screen.getByText("openai/gpt-5.3-codex")).toBeTruthy();
|
|
328
395
|
expect(screen.getByText("project-alpha")).toBeTruthy();
|
|
329
396
|
expect(screen.getByText("/Users/demo/project-alpha")).toBeTruthy();
|
|
330
|
-
expect(screen.
|
|
331
|
-
expect(screen.queryByText("child-session-1")).toBeNull();
|
|
397
|
+
expect(screen.getByText("No child session messages yet.")).toBeTruthy();
|
|
332
398
|
expect(mocks.stickyBottomScroll).toHaveBeenCalledWith(
|
|
333
399
|
expect.objectContaining({
|
|
334
400
|
resetKey: "child-session-1",
|
|
@@ -337,7 +403,7 @@ describe("ChatChildSessionPanel", () => {
|
|
|
337
403
|
);
|
|
338
404
|
});
|
|
339
405
|
|
|
340
|
-
it("
|
|
406
|
+
it("shows unread state for inactive child session tabs", () => {
|
|
341
407
|
mocks.resolvedChildTabs = [
|
|
342
408
|
{
|
|
343
409
|
sessionKey: "child-session-1",
|
|
@@ -345,6 +411,8 @@ describe("ChatChildSessionPanel", () => {
|
|
|
345
411
|
title: "北京天气",
|
|
346
412
|
agentId: "weather",
|
|
347
413
|
updatedAt: "2026-04-10T09:00:00.000Z",
|
|
414
|
+
lastMessageAt: "2026-04-10T09:00:00.000Z",
|
|
415
|
+
readAt: "2026-04-10T09:00:00.000Z",
|
|
348
416
|
sessionTypeLabel: "Codex",
|
|
349
417
|
preferredModel: "openai/gpt-5.3-codex",
|
|
350
418
|
projectName: "project-alpha",
|
|
@@ -356,6 +424,8 @@ describe("ChatChildSessionPanel", () => {
|
|
|
356
424
|
title: "上海天气",
|
|
357
425
|
agentId: "weather",
|
|
358
426
|
updatedAt: "2026-04-10T09:05:00.000Z",
|
|
427
|
+
lastMessageAt: "2026-04-10T09:06:00.000Z",
|
|
428
|
+
readAt: "2026-04-10T09:05:00.000Z",
|
|
359
429
|
sessionTypeLabel: "Claude Code",
|
|
360
430
|
preferredModel: "anthropic/claude-sonnet-4",
|
|
361
431
|
projectName: "project-beta",
|
|
@@ -364,8 +434,8 @@ describe("ChatChildSessionPanel", () => {
|
|
|
364
434
|
];
|
|
365
435
|
|
|
366
436
|
render(
|
|
367
|
-
<
|
|
368
|
-
|
|
437
|
+
<ChatSessionWorkspacePanel
|
|
438
|
+
childSessionTabs={[
|
|
369
439
|
{
|
|
370
440
|
sessionKey: "child-session-1",
|
|
371
441
|
parentSessionKey: "parent-session-1",
|
|
@@ -379,158 +449,51 @@ describe("ChatChildSessionPanel", () => {
|
|
|
379
449
|
agentId: "weather",
|
|
380
450
|
},
|
|
381
451
|
]}
|
|
382
|
-
|
|
452
|
+
activeChildSessionKey="child-session-1"
|
|
453
|
+
workspaceFileTabs={[]}
|
|
454
|
+
activeWorkspaceFileKey={null}
|
|
455
|
+
sessionProjectRoot="/Users/demo/project-alpha"
|
|
383
456
|
onSelectSession={vi.fn()}
|
|
457
|
+
onSelectFile={vi.fn()}
|
|
458
|
+
onCloseFile={vi.fn()}
|
|
384
459
|
onClose={vi.fn()}
|
|
385
460
|
onBackToParent={vi.fn()}
|
|
461
|
+
onFileOpen={vi.fn()}
|
|
386
462
|
/>,
|
|
387
463
|
);
|
|
388
464
|
|
|
389
|
-
expect(screen.
|
|
390
|
-
expect(screen.getByText("上海天气")).toBeTruthy();
|
|
391
|
-
expect(screen.getByText("Codex")).toBeTruthy();
|
|
392
|
-
expect(screen.getByText("openai/gpt-5.3-codex")).toBeTruthy();
|
|
393
|
-
expect(screen.getByText("project-alpha")).toBeTruthy();
|
|
394
|
-
expect(screen.getByText("/Users/demo/project-alpha")).toBeTruthy();
|
|
395
|
-
const tabButtons = screen
|
|
396
|
-
.getAllByRole("button")
|
|
397
|
-
.filter((element) => element.getAttribute("aria-pressed") !== null);
|
|
398
|
-
expect(tabButtons).toHaveLength(2);
|
|
399
|
-
expect(tabButtons[0]?.getAttribute("aria-pressed")).toBe("true");
|
|
400
|
-
expect(tabButtons[1]?.getAttribute("aria-pressed")).toBe("false");
|
|
465
|
+
expect(screen.getByLabelText("Session has unread updates")).toBeTruthy();
|
|
401
466
|
});
|
|
402
467
|
|
|
403
|
-
it("shows
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
agentId: "weather",
|
|
410
|
-
updatedAt: "2026-04-10T09:00:00.000Z",
|
|
411
|
-
sessionTypeLabel: "Codex",
|
|
412
|
-
preferredModel: "openai/gpt-5.3-codex",
|
|
413
|
-
projectName: "project-alpha",
|
|
414
|
-
projectRoot: "/Users/demo/project-alpha",
|
|
415
|
-
},
|
|
416
|
-
{
|
|
417
|
-
sessionKey: "child-session-2",
|
|
418
|
-
parentSessionKey: "parent-session-1",
|
|
419
|
-
title: "上海天气",
|
|
420
|
-
agentId: "weather",
|
|
421
|
-
updatedAt: "2026-04-10T09:05:00.000Z",
|
|
422
|
-
runStatus: "running",
|
|
423
|
-
sessionTypeLabel: "Claude Code",
|
|
424
|
-
preferredModel: "anthropic/claude-sonnet-4",
|
|
425
|
-
projectName: "project-beta",
|
|
426
|
-
projectRoot: "/Users/demo/project-beta",
|
|
427
|
-
},
|
|
428
|
-
];
|
|
429
|
-
|
|
430
|
-
const { rerender } = render(
|
|
431
|
-
<ChatChildSessionPanel
|
|
432
|
-
tabs={[
|
|
433
|
-
{
|
|
434
|
-
sessionKey: "child-session-1",
|
|
435
|
-
parentSessionKey: "parent-session-1",
|
|
436
|
-
label: "北京天气",
|
|
437
|
-
agentId: "weather",
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
sessionKey: "child-session-2",
|
|
441
|
-
parentSessionKey: "parent-session-1",
|
|
442
|
-
label: "上海天气",
|
|
443
|
-
agentId: "weather",
|
|
444
|
-
},
|
|
445
|
-
]}
|
|
446
|
-
activeSessionKey="child-session-1"
|
|
447
|
-
onSelectSession={vi.fn()}
|
|
448
|
-
onClose={vi.fn()}
|
|
449
|
-
onBackToParent={vi.fn()}
|
|
450
|
-
/>,
|
|
451
|
-
);
|
|
452
|
-
|
|
453
|
-
expect(
|
|
454
|
-
screen.queryByLabelText("Session has unread updates"),
|
|
455
|
-
).toBeNull();
|
|
456
|
-
|
|
457
|
-
mocks.resolvedChildTabs = [
|
|
458
|
-
{
|
|
459
|
-
sessionKey: "child-session-1",
|
|
460
|
-
parentSessionKey: "parent-session-1",
|
|
461
|
-
title: "北京天气",
|
|
462
|
-
agentId: "weather",
|
|
463
|
-
updatedAt: "2026-04-10T09:00:00.000Z",
|
|
464
|
-
sessionTypeLabel: "Codex",
|
|
465
|
-
preferredModel: "openai/gpt-5.3-codex",
|
|
466
|
-
projectName: "project-alpha",
|
|
467
|
-
projectRoot: "/Users/demo/project-alpha",
|
|
468
|
-
},
|
|
469
|
-
{
|
|
470
|
-
sessionKey: "child-session-2",
|
|
471
|
-
parentSessionKey: "parent-session-1",
|
|
472
|
-
title: "上海天气",
|
|
473
|
-
agentId: "weather",
|
|
474
|
-
updatedAt: "2026-04-10T09:05:00.000Z",
|
|
475
|
-
sessionTypeLabel: "Claude Code",
|
|
476
|
-
preferredModel: "anthropic/claude-sonnet-4",
|
|
477
|
-
projectName: "project-beta",
|
|
478
|
-
projectRoot: "/Users/demo/project-beta",
|
|
479
|
-
},
|
|
480
|
-
];
|
|
481
|
-
|
|
482
|
-
rerender(
|
|
483
|
-
<ChatChildSessionPanel
|
|
484
|
-
tabs={[
|
|
485
|
-
{
|
|
486
|
-
sessionKey: "child-session-1",
|
|
487
|
-
parentSessionKey: "parent-session-1",
|
|
488
|
-
label: "北京天气",
|
|
489
|
-
agentId: "weather",
|
|
490
|
-
},
|
|
468
|
+
it("shows opened files as top tabs and renders the file preview pane", () => {
|
|
469
|
+
render(
|
|
470
|
+
<ChatSessionWorkspacePanel
|
|
471
|
+
childSessionTabs={[]}
|
|
472
|
+
activeChildSessionKey={null}
|
|
473
|
+
workspaceFileTabs={[
|
|
491
474
|
{
|
|
492
|
-
|
|
475
|
+
key: "parent-session-1::preview::README.md",
|
|
493
476
|
parentSessionKey: "parent-session-1",
|
|
494
|
-
|
|
495
|
-
|
|
477
|
+
path: "README.md",
|
|
478
|
+
label: "README.md",
|
|
479
|
+
viewMode: "preview",
|
|
496
480
|
},
|
|
497
481
|
]}
|
|
498
|
-
|
|
482
|
+
activeWorkspaceFileKey="parent-session-1::preview::README.md"
|
|
483
|
+
sessionProjectRoot="/Users/demo/project-alpha"
|
|
499
484
|
onSelectSession={vi.fn()}
|
|
485
|
+
onSelectFile={vi.fn()}
|
|
486
|
+
onCloseFile={vi.fn()}
|
|
500
487
|
onClose={vi.fn()}
|
|
501
488
|
onBackToParent={vi.fn()}
|
|
489
|
+
onFileOpen={vi.fn()}
|
|
502
490
|
/>,
|
|
503
491
|
);
|
|
504
492
|
|
|
505
|
-
expect(
|
|
506
|
-
|
|
507
|
-
).
|
|
508
|
-
|
|
509
|
-
rerender(
|
|
510
|
-
<ChatChildSessionPanel
|
|
511
|
-
tabs={[
|
|
512
|
-
{
|
|
513
|
-
sessionKey: "child-session-1",
|
|
514
|
-
parentSessionKey: "parent-session-1",
|
|
515
|
-
label: "北京天气",
|
|
516
|
-
agentId: "weather",
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
sessionKey: "child-session-2",
|
|
520
|
-
parentSessionKey: "parent-session-1",
|
|
521
|
-
label: "上海天气",
|
|
522
|
-
agentId: "weather",
|
|
523
|
-
},
|
|
524
|
-
]}
|
|
525
|
-
activeSessionKey="child-session-2"
|
|
526
|
-
onSelectSession={vi.fn()}
|
|
527
|
-
onClose={vi.fn()}
|
|
528
|
-
onBackToParent={vi.fn()}
|
|
529
|
-
/>,
|
|
493
|
+
expect(screen.queryByText("Open files")).toBeNull();
|
|
494
|
+
expect(screen.getAllByText("README.md").length).toBeGreaterThan(0);
|
|
495
|
+
expect(screen.getByTestId("workspace-file-preview").textContent).toBe(
|
|
496
|
+
"README.md",
|
|
530
497
|
);
|
|
531
|
-
|
|
532
|
-
expect(
|
|
533
|
-
screen.queryByLabelText("Session has unread updates"),
|
|
534
|
-
).toBeNull();
|
|
535
498
|
});
|
|
536
499
|
});
|