@nextclaw/ui 0.12.24 → 0.12.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -29
- package/dist/assets/api-DGD9_Bg4.js +15 -0
- package/dist/assets/app-manager-provider-oYdeYPSv.js +1 -0
- package/dist/assets/{book-open-DDlN5MvX.js → book-open-BcnAiKde.js} +1 -1
- package/dist/assets/channels-list-page-HgLgrEg4.js +8 -0
- package/dist/assets/chat-page-DAKMFDrS.js +1 -0
- package/dist/assets/config-split-page-CcrEUtwu.js +1 -0
- package/dist/assets/cpu-DPPwMzoC.js +3 -0
- package/dist/assets/{createLucideIcon-BLMK3QUd.js → createLucideIcon-DzY6wN61.js} +1 -1
- package/dist/assets/desktop-DVUbOWbR.js +3 -0
- package/dist/assets/desktop-update-config-CP8dFYXK.js +1 -0
- package/dist/assets/{dialog-C3D7Be0p.js → dialog-BKo0RItd.js} +1 -1
- package/dist/assets/{dist-CPlbUgwU.js → dist-CFiwgaLs.js} +1 -1
- package/dist/assets/doc-browser-CAhfnm0D.js +1 -0
- package/dist/assets/{doc-browser-context-BJuMaI3o.js → doc-browser-context-FukQHvyo.js} +1 -1
- package/dist/assets/doc-browser-p9DDNPWB.js +1 -0
- package/dist/assets/doc-browser-rZIQIjuw.js +1 -0
- package/dist/assets/download-CMM8po31.js +1 -0
- package/dist/assets/{es2015-xqN1slyW.js → es2015-BhznEEyJ.js} +1 -1
- package/dist/assets/{external-link-DwfSfTLB.js → external-link-CpEvG65F.js} +1 -1
- package/dist/assets/i18n-D1144VAA.js +1 -0
- package/dist/assets/index-Cuwst6cc.js +100 -0
- package/dist/assets/index-dlcqieQ0.css +1 -0
- package/dist/assets/{key-round-CJ5gDAAG.js → key-round-DUq47t0P.js} +1 -1
- package/dist/assets/marketplace-page-BeFbwxR-.js +105 -0
- package/dist/assets/marketplace-page-CR4xq-TM.js +1 -0
- package/dist/assets/mcp-marketplace-page-DlRrSCj3.js +1 -0
- package/dist/assets/mcp-marketplace-page-DwnaLNTx.js +40 -0
- package/dist/assets/model-config-L2l6YAlQ.js +1 -0
- package/dist/assets/{notice-card-BFDbKQDA.js → notice-card-Dr6xCwva.js} +1 -1
- package/dist/assets/play-AqrNslHI.js +1 -0
- package/dist/assets/plus-B-YHtTNC.js +1 -0
- package/dist/assets/{popover-B86Dbfhf.js → popover-BDFNiLlg.js} +1 -1
- package/dist/assets/provider-scoped-model-input-BMTp4BEH.js +1 -0
- package/dist/assets/providers-list-DYAEunOp.js +1 -0
- package/dist/assets/refresh-cw-CrbD8EkT.js +1 -0
- package/dist/assets/remote-Dr3jcfWP.js +1 -0
- package/dist/assets/{rotate-cw-BZ2JObNs.js → rotate-cw-BN9yjccP.js} +1 -1
- package/dist/assets/runtime-config-page-BdeU8PEK.js +1 -0
- package/dist/assets/{save-euRxl8pI.js → save-CO_4qf6b.js} +1 -1
- package/dist/assets/{search-CLd7m0M7.js → search-CRtQwr-h.js} +1 -1
- package/dist/assets/search-config-CQUhd5RU.js +1 -0
- package/dist/assets/secrets-config-D-NWlW9q.js +3 -0
- package/dist/assets/{select-CJ0wbo3D.js → select-BUTwE_lC.js} +1 -1
- package/dist/assets/{setting-row-D1Yygqp7.js → setting-row-BavcnXw1.js} +1 -1
- package/dist/assets/settings-MWL2SMyk.js +1 -0
- package/dist/assets/{sparkles-DVfeSVJQ.js → sparkles-BmgOD4nY.js} +1 -1
- package/dist/assets/{status-dot-ChvPCib9.js → status-dot-l3kPFdq_.js} +1 -1
- package/dist/assets/{tabs-custom-Hia_ong0.js → tabs-custom-D48zdZoc.js} +1 -1
- package/dist/assets/{tag-chip-FrkmkT8r.js → tag-chip-Dm2Lqnpu.js} +1 -1
- package/dist/assets/use-config-Cyv5IuSt.js +1 -0
- package/dist/assets/use-infinite-scroll-loader-CFVdPpNv.js +1 -0
- package/dist/assets/x-BeyYA_h6.js +1 -0
- package/dist/index.html +29 -40
- package/package.json +9 -9
- package/src/app/components/layout/sidebar.layout.test.tsx +2 -4
- package/src/app/components/theme-provider.tsx +1 -0
- package/src/app/configs/app-navigation.config.ts +0 -6
- package/src/app/index.tsx +4 -7
- package/src/features/agents/components/agents-page.test.tsx +25 -15
- package/src/features/agents/components/agents-page.tsx +133 -172
- package/src/features/channels/components/config/channel-form.test.tsx +1 -0
- package/src/features/channels/components/config/channel-form.tsx +4 -3
- package/src/features/channels/components/config/weixin-channel-auth-section.test.tsx +38 -1
- package/src/features/channels/components/config/weixin-channel-auth-section.tsx +137 -40
- package/src/features/channels/index.ts +1 -1
- package/src/features/channels/utils/channel-form-fields.utils.test.ts +26 -0
- package/src/features/channels/utils/channel-form-fields.utils.ts +32 -18
- package/src/features/chat/components/chat-session-workspace-panel-nav.tsx +23 -4
- package/src/features/chat/components/chat-session-workspace-panel.tsx +53 -35
- package/src/features/chat/components/chat-sidebar-session-item.tsx +16 -12
- package/src/features/chat/components/conversation/chat-conversation-header.test.tsx +74 -0
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +8 -2
- package/src/features/chat/components/conversation/chat-conversation-panel.test.tsx +262 -114
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +210 -174
- package/src/features/chat/components/conversation/chat-input-bar.container.tsx +11 -1
- package/src/features/chat/components/conversation/session-header/chat-session-header-actions.test.tsx +24 -0
- package/src/features/chat/components/conversation/session-header/chat-session-header-actions.tsx +27 -6
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +174 -0
- package/src/features/chat/components/layout/chat-sidebar.test.tsx +45 -8
- package/src/features/chat/components/layout/chat-sidebar.tsx +29 -46
- package/src/features/chat/components/providers/chat-presenter.provider.tsx +4 -0
- package/src/features/chat/components/workspace/session-cron-job-content.tsx +103 -0
- package/src/features/chat/hooks/use-ncp-agent-runtime.test.tsx +153 -80
- package/src/features/chat/hooks/use-ncp-chat-page-data.test.tsx +70 -0
- package/src/features/chat/hooks/use-ncp-chat-page-data.ts +1 -1
- package/src/features/chat/hooks/use-ncp-child-session-tabs-view.ts +2 -8
- package/src/features/chat/hooks/use-ncp-session-list-view.ts +1 -2
- package/src/features/chat/managers/chat-session-list.manager.test.ts +7 -9
- package/src/features/chat/managers/chat-session-list.manager.ts +5 -10
- package/src/features/chat/managers/ncp-chat-input.manager.test.ts +20 -2
- package/src/features/chat/managers/ncp-chat-input.manager.ts +18 -0
- package/src/features/chat/managers/ncp-chat-presenter.manager.ts +7 -0
- package/src/features/chat/managers/ncp-chat-thread.manager.test.ts +52 -1
- package/src/features/chat/managers/ncp-chat-thread.manager.ts +21 -0
- package/src/features/chat/pages/ncp-chat-page.tsx +9 -5
- package/src/features/chat/stores/chat-input.store.ts +3 -1
- package/src/features/chat/stores/chat-session-list.store.ts +0 -2
- package/src/features/chat/stores/chat-thread.store.ts +4 -0
- package/src/features/chat/utils/chat-session-display.utils.test.ts +83 -1
- package/src/features/chat/utils/chat-session-display.utils.ts +73 -0
- package/src/features/chat/utils/ncp-chat-input-availability.utils.test.ts +1 -0
- package/src/features/chat/utils/ncp-session-adapter.utils.test.ts +22 -0
- package/src/features/chat/utils/ncp-session-adapter.utils.ts +32 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-curated-scene-route.test.tsx +235 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.config.ts +162 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.tsx +355 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-shelf-card.tsx +118 -0
- package/src/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer.ts +201 -0
- package/src/features/marketplace/components/detail-doc/marketplace-detail-doc.test.ts +40 -0
- package/src/features/marketplace/components/marketplace-catalog-grid.tsx +114 -0
- package/src/features/marketplace/components/marketplace-detail-doc.ts +73 -24
- package/src/features/marketplace/components/marketplace-item-icon.tsx +45 -0
- package/src/features/marketplace/components/marketplace-list-card.tsx +177 -93
- package/src/features/marketplace/components/marketplace-page-detail.test.tsx +9 -2
- package/src/features/marketplace/components/marketplace-page-parts.tsx +1 -1
- package/src/features/marketplace/components/marketplace-page.test.tsx +25 -6
- package/src/features/marketplace/components/marketplace-page.tsx +154 -132
- package/src/features/marketplace/hooks/use-marketplace-curated-scene-route.ts +97 -0
- package/src/features/marketplace/hooks/use-marketplace.ts +59 -3
- package/src/features/system-status/components/config/runtime-agent-list-card.tsx +4 -8
- package/src/features/system-status/components/config/runtime-binding-list-card.tsx +5 -7
- package/src/features/system-status/components/config/runtime-config-editor.tsx +1 -19
- package/src/features/system-status/components/config/runtime-entry-list-card.tsx +10 -11
- package/src/features/system-status/components/config/runtime-settings-card.tsx +15 -23
- package/src/features/system-status/components/runtime-control-card.test.tsx +8 -6
- package/src/features/system-status/components/runtime-control-card.tsx +7 -6
- package/src/features/system-status/pages/runtime-config-page.test.tsx +19 -9
- package/src/features/system-status/pages/runtime-config-page.tsx +2 -3
- package/src/features/system-status/utils/runtime-config-agent.utils.ts +4 -4
- package/src/features/system-status/utils/system-status.utils.ts +31 -6
- package/src/index.css +8 -0
- package/src/platforms/desktop/components/desktop-app-shell.test.tsx +68 -0
- package/src/platforms/desktop/components/desktop-app-shell.tsx +46 -18
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +30 -0
- package/src/platforms/desktop/index.ts +6 -0
- package/src/platforms/desktop/types/desktop-update.types.ts +3 -0
- package/src/platforms/desktop/utils/desktop-host.utils.ts +56 -0
- package/src/shared/components/common/brand-header.tsx +36 -16
- package/src/shared/components/config/provider-form-support.ts +2 -22
- package/src/shared/components/cron-config.tsx +12 -58
- package/src/shared/components/doc-browser/doc-browser.tsx +4 -4
- package/src/shared/components/ui/select.tsx +19 -7
- package/src/shared/lib/api/channel-auth.types.ts +1 -0
- package/src/shared/lib/api/ncp-session.types.ts +9 -0
- package/src/shared/lib/api/types.ts +12 -1
- package/src/shared/lib/api/utils/marketplace.utils.ts +7 -1
- package/src/shared/lib/cron/cron-job-view.utils.ts +59 -0
- package/src/shared/lib/cron/index.ts +1 -0
- package/src/shared/lib/i18n/{channel-auth.ts → channel-auth.constants.ts} +31 -0
- package/src/shared/lib/i18n/chat-labels.utils.ts +3 -2
- package/src/shared/lib/i18n/index.ts +20 -59
- package/src/shared/lib/i18n/{runtime-control.ts → runtime-control-labels.utils.ts} +30 -1
- package/src/shared/lib/provider-models/index.test.ts +39 -0
- package/src/shared/lib/provider-models/index.ts +1 -3
- package/src/shared/lib/ui-document-title/index.ts +0 -1
- package/tsconfig.json +1 -0
- package/vite.config.ts +1 -1
- package/vitest.config.ts +1 -1
- package/dist/assets/api-D2xRKmZd.js +0 -15
- package/dist/assets/app-manager-provider-CNaZboG4.js +0 -1
- package/dist/assets/app-navigation.config-Ihhrrt--.js +0 -1
- package/dist/assets/channels-list-page-p26lgxLk.js +0 -8
- package/dist/assets/chat-Dkh2qtuz.js +0 -61
- package/dist/assets/chat-page-DoTmE2wx.js +0 -1
- package/dist/assets/chunk-JZWAC4HX-Kydj4yEz.js +0 -3
- package/dist/assets/config-split-page-DIOCjj2Q.js +0 -1
- package/dist/assets/desktop-update-config-DlpzDfKM.js +0 -1
- package/dist/assets/doc-browser-C8FM5fC0.js +0 -1
- package/dist/assets/doc-browser-RJUOL_GO.js +0 -1
- package/dist/assets/doc-browser-p82AdNO-.js +0 -1
- package/dist/assets/folder-CeJKPx5P.js +0 -1
- package/dist/assets/hash-BqxRTZW5.js +0 -1
- package/dist/assets/i18n-DnTGDIRw.js +0 -1
- package/dist/assets/index-D8MKmXtO.css +0 -1
- package/dist/assets/index-pBvbJ5Mt.js +0 -2
- package/dist/assets/loader-circle-fd-vQKtW.js +0 -1
- package/dist/assets/logo-badge-KAe-7d8c.js +0 -1
- package/dist/assets/logos-C4sYP1Vl.js +0 -1
- package/dist/assets/marketplace-page-Cql0kDi-.js +0 -1
- package/dist/assets/marketplace-page-m4P5g_Ht.js +0 -49
- package/dist/assets/mcp-marketplace-page-9WVKl1m1.js +0 -1
- package/dist/assets/mcp-marketplace-page-ByzBQZcx.js +0 -40
- package/dist/assets/message-square-z_osm9c0.js +0 -1
- package/dist/assets/model-config-Dbr_0APb.js +0 -1
- package/dist/assets/play-Dv6Nr1Ew.js +0 -1
- package/dist/assets/plus-D8eKFY7h.js +0 -1
- package/dist/assets/provider-scoped-model-input-DFm6N2f7.js +0 -1
- package/dist/assets/providers-list-BJcLOjun.js +0 -1
- package/dist/assets/refresh-ccw-ByVwmnN_.js +0 -1
- package/dist/assets/refresh-cw-PcqoYB3K.js +0 -1
- package/dist/assets/remote-BOxo9iwd.js +0 -1
- package/dist/assets/runtime-config-page-CjLhnbSl.js +0 -1
- package/dist/assets/search-config-J4Htco-P.js +0 -1
- package/dist/assets/secrets-config-CUdERjco.js +0 -3
- package/dist/assets/sessions-config-page-DpK991fs.js +0 -2
- package/dist/assets/settings-drbWqzA4.js +0 -1
- package/dist/assets/skeleton-BK1SOSRA.js +0 -1
- package/dist/assets/theme-provider-0hxjiPc_.js +0 -2
- package/dist/assets/tooltip-Cj4yA0gH.js +0 -1
- package/dist/assets/trash-2-CBsHCfqq.js +0 -1
- package/dist/assets/use-config-38Ur-89i.js +0 -1
- package/dist/assets/use-confirm-dialog-DPQThaeU.js +0 -1
- package/dist/assets/use-infinite-scroll-loader-5Gf1xQi7.js +0 -1
- package/dist/assets/use-viewport-layout-D1XzKeip.js +0 -1
- package/dist/assets/x-CM-XDMpk.js +0 -1
- package/src/features/chat/components/config/sessions-config-detail-pane.tsx +0 -244
- package/src/features/chat/pages/sessions-config-page.test.tsx +0 -152
- package/src/features/chat/pages/sessions-config-page.tsx +0 -192
- /package/dist/assets/{config-hints-MogHYQ8G.js → config-hints-BNfpOL4J.js} +0 -0
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
ChatFileOpenActionViewModel,
|
|
5
5
|
ChatToolActionViewModel,
|
|
6
6
|
} from "@nextclaw/agent-chat-ui";
|
|
7
|
+
import type { CronJobView } from "@/shared/lib/api";
|
|
7
8
|
import { useStickyBottomScroll } from "@nextclaw/agent-chat-ui";
|
|
8
9
|
import { ChatMessageListContainer } from "@/features/chat/components/conversation/chat-message-list.container";
|
|
9
10
|
import {
|
|
@@ -28,23 +29,20 @@ import {
|
|
|
28
29
|
import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
|
|
29
30
|
import { ChatSessionWorkspaceFilePreview } from "./chat-session-workspace-file-preview";
|
|
30
31
|
import { AgentIdentityAvatar } from "@/shared/components/common/agent-identity";
|
|
32
|
+
import { SessionCronJobContent } from "@/features/chat/components/workspace/session-cron-job-content";
|
|
31
33
|
import { t } from "@/shared/lib/i18n";
|
|
32
34
|
import { cn } from "@/shared/lib/utils";
|
|
33
35
|
|
|
34
36
|
type ChatSessionWorkspacePanelProps = {
|
|
37
|
+
sessionKey: string | null;
|
|
35
38
|
childSessionTabs: readonly ChatChildSessionTab[];
|
|
36
39
|
activeChildSessionKey: string | null;
|
|
37
40
|
workspaceFileTabs: readonly ChatWorkspaceFileTab[];
|
|
38
41
|
activeWorkspaceFileKey: string | null;
|
|
42
|
+
activePanelKind?: "child-session" | "file" | "cron" | null;
|
|
43
|
+
sessionCronJobs?: readonly CronJobView[];
|
|
39
44
|
sessionProjectRoot: string | null;
|
|
40
45
|
displayMode?: "docked" | "overlay";
|
|
41
|
-
onSelectSession: (sessionKey: string) => void;
|
|
42
|
-
onSelectFile: (fileKey: string) => void;
|
|
43
|
-
onCloseFile: (fileKey: string) => void;
|
|
44
|
-
onClose: () => void;
|
|
45
|
-
onBackToParent: () => void;
|
|
46
|
-
onToolAction?: (action: ChatToolActionViewModel) => void;
|
|
47
|
-
onFileOpen: (action: ChatFileOpenActionViewModel) => void;
|
|
48
46
|
};
|
|
49
47
|
|
|
50
48
|
function ChildSessionContent({
|
|
@@ -141,11 +139,7 @@ function ChildSessionMetaStrip({ tab }: { tab: ResolvedChildSessionTab }) {
|
|
|
141
139
|
);
|
|
142
140
|
}
|
|
143
141
|
|
|
144
|
-
function WorkspaceActiveChildHeader({
|
|
145
|
-
tab,
|
|
146
|
-
}: {
|
|
147
|
-
tab: ResolvedChildSessionTab;
|
|
148
|
-
}) {
|
|
142
|
+
function WorkspaceActiveChildHeader({ tab }: { tab: ResolvedChildSessionTab }) {
|
|
149
143
|
return (
|
|
150
144
|
<div className="border-b border-gray-200/70 px-4 py-3">
|
|
151
145
|
<div className="flex min-w-0 items-center gap-2 text-sm font-semibold text-gray-900">
|
|
@@ -167,20 +161,24 @@ function WorkspaceActiveChildHeader({
|
|
|
167
161
|
function buildWorkspaceTabsViewModel(params: {
|
|
168
162
|
resolvedChildTabs: ResolvedChildSessionTab[];
|
|
169
163
|
workspaceFileTabs: readonly ChatWorkspaceFileTab[];
|
|
164
|
+
sessionCronJobCount: number;
|
|
170
165
|
activeSelection: ReturnType<typeof resolveWorkspaceSelection>;
|
|
171
166
|
optimisticReadAtBySessionKey: Record<string, string>;
|
|
172
167
|
onSelectSession: (sessionKey: string) => void;
|
|
173
168
|
onSelectFile: (fileKey: string) => void;
|
|
174
169
|
onCloseFile: (fileKey: string) => void;
|
|
170
|
+
onSelectCronJobs: () => void;
|
|
175
171
|
}): WorkspaceTabViewModel[] {
|
|
176
172
|
const {
|
|
177
173
|
resolvedChildTabs,
|
|
178
174
|
workspaceFileTabs,
|
|
175
|
+
sessionCronJobCount,
|
|
179
176
|
activeSelection,
|
|
180
177
|
optimisticReadAtBySessionKey,
|
|
181
178
|
onSelectSession,
|
|
182
179
|
onSelectFile,
|
|
183
180
|
onCloseFile,
|
|
181
|
+
onSelectCronJobs,
|
|
184
182
|
} = params;
|
|
185
183
|
|
|
186
184
|
const childTabs = resolvedChildTabs.map((tab) => {
|
|
@@ -190,7 +188,7 @@ function buildWorkspaceTabsViewModel(params: {
|
|
|
190
188
|
? optimisticReadAt.localeCompare(tab.readAt) > 0
|
|
191
189
|
? optimisticReadAt
|
|
192
190
|
: tab.readAt
|
|
193
|
-
: optimisticReadAt ?? tab.readAt;
|
|
191
|
+
: (optimisticReadAt ?? tab.readAt);
|
|
194
192
|
return {
|
|
195
193
|
key: `child:${tab.sessionKey}`,
|
|
196
194
|
kind: "child-session" as const,
|
|
@@ -219,29 +217,38 @@ function buildWorkspaceTabsViewModel(params: {
|
|
|
219
217
|
tooltip: file.path,
|
|
220
218
|
viewMode: file.viewMode,
|
|
221
219
|
active:
|
|
222
|
-
activeSelection?.kind === "file" &&
|
|
223
|
-
activeSelection.file.key === file.key,
|
|
220
|
+
activeSelection?.kind === "file" && activeSelection.file.key === file.key,
|
|
224
221
|
onSelect: () => onSelectFile(file.key),
|
|
225
222
|
onClose: () => onCloseFile(file.key),
|
|
226
223
|
}));
|
|
227
224
|
|
|
228
|
-
|
|
225
|
+
const cronTab =
|
|
226
|
+
sessionCronJobCount > 0
|
|
227
|
+
? [
|
|
228
|
+
{
|
|
229
|
+
key: "cron:session",
|
|
230
|
+
kind: "cron" as const,
|
|
231
|
+
title: t("chatWorkspaceSessionCronJobs"),
|
|
232
|
+
tooltip: t("chatWorkspaceSessionCronJobs"),
|
|
233
|
+
active: activeSelection?.kind === "cron",
|
|
234
|
+
onSelect: onSelectCronJobs,
|
|
235
|
+
},
|
|
236
|
+
]
|
|
237
|
+
: [];
|
|
238
|
+
|
|
239
|
+
return [...childTabs, ...fileTabs, ...cronTab];
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
export function ChatSessionWorkspacePanel({
|
|
243
|
+
sessionKey,
|
|
232
244
|
childSessionTabs,
|
|
233
245
|
activeChildSessionKey,
|
|
234
246
|
workspaceFileTabs,
|
|
235
247
|
activeWorkspaceFileKey,
|
|
248
|
+
activePanelKind,
|
|
249
|
+
sessionCronJobs = [],
|
|
236
250
|
sessionProjectRoot,
|
|
237
251
|
displayMode = "docked",
|
|
238
|
-
onSelectSession,
|
|
239
|
-
onSelectFile,
|
|
240
|
-
onCloseFile,
|
|
241
|
-
onClose,
|
|
242
|
-
onBackToParent,
|
|
243
|
-
onToolAction,
|
|
244
|
-
onFileOpen,
|
|
245
252
|
}: ChatSessionWorkspacePanelProps) {
|
|
246
253
|
const presenter = usePresenter();
|
|
247
254
|
const resolvedChildTabs = useNcpChildSessionTabsView(childSessionTabs);
|
|
@@ -251,8 +258,10 @@ export function ChatSessionWorkspacePanel({
|
|
|
251
258
|
const activeSelection = resolveWorkspaceSelection({
|
|
252
259
|
activeChildSessionKey,
|
|
253
260
|
activeWorkspaceFileKey,
|
|
261
|
+
activePanelKind,
|
|
254
262
|
childSessionTabs: resolvedChildTabs,
|
|
255
263
|
workspaceFileTabs,
|
|
264
|
+
sessionCronJobCount: sessionCronJobs.length,
|
|
256
265
|
});
|
|
257
266
|
const hasParentSession = resolvedChildTabs.some((tab) =>
|
|
258
267
|
Boolean(tab.parentSessionKey),
|
|
@@ -278,20 +287,25 @@ export function ChatSessionWorkspacePanel({
|
|
|
278
287
|
buildWorkspaceTabsViewModel({
|
|
279
288
|
resolvedChildTabs,
|
|
280
289
|
workspaceFileTabs,
|
|
290
|
+
sessionCronJobCount: sessionCronJobs.length,
|
|
281
291
|
activeSelection,
|
|
282
292
|
optimisticReadAtBySessionKey,
|
|
283
|
-
onSelectSession,
|
|
284
|
-
onSelectFile,
|
|
285
|
-
onCloseFile,
|
|
293
|
+
onSelectSession: presenter.chatThreadManager.selectChildSessionDetail,
|
|
294
|
+
onSelectFile: presenter.chatThreadManager.selectWorkspaceFile,
|
|
295
|
+
onCloseFile: presenter.chatThreadManager.closeWorkspaceFile,
|
|
296
|
+
onSelectCronJobs: () => {
|
|
297
|
+
if (sessionKey)
|
|
298
|
+
presenter.chatThreadManager.openSessionCronPanel(sessionKey);
|
|
299
|
+
},
|
|
286
300
|
}),
|
|
287
301
|
[
|
|
288
302
|
activeSelection,
|
|
289
|
-
onCloseFile,
|
|
290
|
-
onSelectFile,
|
|
291
|
-
onSelectSession,
|
|
292
303
|
optimisticReadAtBySessionKey,
|
|
304
|
+
presenter.chatThreadManager,
|
|
293
305
|
resolvedChildTabs,
|
|
306
|
+
sessionKey,
|
|
294
307
|
workspaceFileTabs,
|
|
308
|
+
sessionCronJobs.length,
|
|
295
309
|
],
|
|
296
310
|
);
|
|
297
311
|
|
|
@@ -311,7 +325,7 @@ export function ChatSessionWorkspacePanel({
|
|
|
311
325
|
<div className="flex items-center justify-between gap-3 border-b border-gray-200/70 px-4 py-2.5">
|
|
312
326
|
<button
|
|
313
327
|
type="button"
|
|
314
|
-
onClick={
|
|
328
|
+
onClick={presenter.chatThreadManager.goToParentSession}
|
|
315
329
|
className={cn(
|
|
316
330
|
"inline-flex items-center gap-1 text-xs font-medium text-gray-600 transition-colors hover:text-gray-900",
|
|
317
331
|
!hasParentSession && "pointer-events-none opacity-0",
|
|
@@ -322,7 +336,7 @@ export function ChatSessionWorkspacePanel({
|
|
|
322
336
|
</button>
|
|
323
337
|
<button
|
|
324
338
|
type="button"
|
|
325
|
-
onClick={
|
|
339
|
+
onClick={presenter.chatThreadManager.closeWorkspacePanel}
|
|
326
340
|
className="rounded-full border border-gray-200/80 p-1.5 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900"
|
|
327
341
|
aria-label={t("chatWorkspaceClosePanel")}
|
|
328
342
|
>
|
|
@@ -339,17 +353,21 @@ export function ChatSessionWorkspacePanel({
|
|
|
339
353
|
<div className="flex-1 min-h-0">
|
|
340
354
|
<ChildSessionContent
|
|
341
355
|
sessionKey={activeSelection.tab.sessionKey}
|
|
342
|
-
onToolAction={
|
|
343
|
-
|
|
356
|
+
onToolAction={
|
|
357
|
+
presenter.chatThreadManager.openSessionFromToolAction
|
|
358
|
+
}
|
|
359
|
+
onFileOpen={presenter.chatThreadManager.openFilePreview}
|
|
344
360
|
/>
|
|
345
361
|
</div>
|
|
346
362
|
</>
|
|
347
|
-
) : (
|
|
363
|
+
) : activeSelection.kind === "file" ? (
|
|
348
364
|
<ChatSessionWorkspaceFilePreview
|
|
349
365
|
file={activeSelection.file}
|
|
350
366
|
sessionProjectRoot={sessionProjectRoot}
|
|
351
|
-
onFileOpen={
|
|
367
|
+
onFileOpen={presenter.chatThreadManager.openFilePreview}
|
|
352
368
|
/>
|
|
369
|
+
) : (
|
|
370
|
+
<SessionCronJobContent jobs={sessionCronJobs} />
|
|
353
371
|
)}
|
|
354
372
|
</div>
|
|
355
373
|
</div>
|
|
@@ -5,9 +5,13 @@ import { SessionRunBadge } from '@/features/chat/components/session/session-run-
|
|
|
5
5
|
import { Button } from '@/shared/components/ui/button';
|
|
6
6
|
import { Input } from '@/shared/components/ui/input';
|
|
7
7
|
import { type SessionContextView } from '@/features/chat/utils/session-context.utils';
|
|
8
|
+
import {
|
|
9
|
+
formatSessionListTime,
|
|
10
|
+
sessionActivityPreviewText
|
|
11
|
+
} from '@/features/chat/utils/chat-session-display.utils';
|
|
8
12
|
import type { SessionRunStatus } from '@/features/chat/types/session-run-status.types';
|
|
9
13
|
import { cn } from '@/shared/lib/utils';
|
|
10
|
-
import {
|
|
14
|
+
import { t } from '@/shared/lib/i18n';
|
|
11
15
|
import { Check, GitBranch, Pencil, X } from 'lucide-react';
|
|
12
16
|
|
|
13
17
|
type ChatSidebarSessionItemProps = {
|
|
@@ -116,11 +120,13 @@ function ChatSidebarSessionDisplayView({
|
|
|
116
120
|
onStartEditing
|
|
117
121
|
}: ChatSidebarSessionDisplayViewProps) {
|
|
118
122
|
const trailingControlsClassName = childSessionCount > 0 && onOpenChildSessions ? 'pr-14' : 'pr-6';
|
|
123
|
+
const previewText = sessionActivityPreviewText(session);
|
|
124
|
+
const fallbackPreviewText = `${agentLabel?.trim() ? `${agentLabel} · ` : ''}${session.messageCount}`;
|
|
119
125
|
|
|
120
126
|
return (
|
|
121
127
|
<div className="group/session relative">
|
|
122
128
|
<button type="button" onClick={onSelect} className="w-full text-left">
|
|
123
|
-
<div className={cn('
|
|
129
|
+
<div className={cn('flex min-w-0 items-start', trailingControlsClassName)}>
|
|
124
130
|
<span className="flex min-w-0 items-center gap-1.5">
|
|
125
131
|
{agentId?.trim() && agentId.trim().toLowerCase() !== 'main' ? (
|
|
126
132
|
<AgentAvatar
|
|
@@ -149,15 +155,10 @@ function ChatSidebarSessionDisplayView({
|
|
|
149
155
|
</span>
|
|
150
156
|
) : null}
|
|
151
157
|
</span>
|
|
152
|
-
{runStatus ? (
|
|
153
|
-
<span className="inline-flex shrink-0 items-center justify-end gap-1.5 pt-0.5">
|
|
154
|
-
<SessionRunBadge status={runStatus} />
|
|
155
|
-
</span>
|
|
156
|
-
) : null}
|
|
157
158
|
</div>
|
|
158
159
|
<div className="mt-1 flex items-center gap-2 text-[11px] text-gray-400">
|
|
159
160
|
<span className="min-w-0 truncate">
|
|
160
|
-
{
|
|
161
|
+
{previewText ?? fallbackPreviewText}
|
|
161
162
|
</span>
|
|
162
163
|
{showUnreadDot ? (
|
|
163
164
|
<span
|
|
@@ -165,7 +166,7 @@ function ChatSidebarSessionDisplayView({
|
|
|
165
166
|
className="ml-auto h-2 w-2 shrink-0 rounded-full bg-primary"
|
|
166
167
|
/>
|
|
167
168
|
) : (
|
|
168
|
-
<span className="ml-auto shrink-0">{
|
|
169
|
+
<span className="ml-auto shrink-0">{formatSessionListTime(session.lastMessageAt ?? session.createdAt)}</span>
|
|
169
170
|
)}
|
|
170
171
|
</div>
|
|
171
172
|
</button>
|
|
@@ -189,6 +190,11 @@ function ChatSidebarSessionDisplayView({
|
|
|
189
190
|
<span>{childSessionCount}</span>
|
|
190
191
|
</button>
|
|
191
192
|
) : null}
|
|
193
|
+
{runStatus ? (
|
|
194
|
+
<span className="absolute right-0 top-0 inline-flex h-5 w-5 items-center justify-center transition-opacity group-hover/session:opacity-0 group-focus-within/session:opacity-0">
|
|
195
|
+
<SessionRunBadge status={runStatus} />
|
|
196
|
+
</span>
|
|
197
|
+
) : null}
|
|
192
198
|
<button
|
|
193
199
|
type="button"
|
|
194
200
|
onClick={(event) => {
|
|
@@ -197,9 +203,7 @@ function ChatSidebarSessionDisplayView({
|
|
|
197
203
|
}}
|
|
198
204
|
className={cn(
|
|
199
205
|
'absolute right-0 top-0 inline-flex h-5 w-5 items-center justify-center rounded-md text-gray-400 transition-all hover:bg-white hover:text-gray-900',
|
|
200
|
-
|
|
201
|
-
? 'opacity-100'
|
|
202
|
-
: 'opacity-0 group-hover/session:opacity-100 group-focus-within/session:opacity-100'
|
|
206
|
+
'opacity-0 group-hover/session:opacity-100 group-focus-within/session:opacity-100'
|
|
203
207
|
)}
|
|
204
208
|
aria-label={t('edit')}
|
|
205
209
|
>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { ChatConversationHeader } from "@/features/chat/components/conversation/chat-conversation-header";
|
|
5
|
+
import { useChatThreadStore } from "@/features/chat/stores/chat-thread.store";
|
|
6
|
+
|
|
7
|
+
vi.mock("@/shared/components/common/agent-avatar", () => ({
|
|
8
|
+
AgentAvatar: ({ agentId }: { agentId: string }) => (
|
|
9
|
+
<div data-testid="agent-avatar">{agentId}</div>
|
|
10
|
+
),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
function renderHeader(
|
|
14
|
+
snapshotPatch: Partial<ReturnType<typeof useChatThreadStore.getState>["snapshot"]>,
|
|
15
|
+
) {
|
|
16
|
+
const queryClient = new QueryClient();
|
|
17
|
+
const snapshot = {
|
|
18
|
+
...useChatThreadStore.getState().snapshot,
|
|
19
|
+
isProviderStateResolved: true,
|
|
20
|
+
modelOptions: [],
|
|
21
|
+
sessionTypeLabel: "Codex",
|
|
22
|
+
sessionKey: null,
|
|
23
|
+
agentId: "main",
|
|
24
|
+
canDeleteSession: false,
|
|
25
|
+
messages: [],
|
|
26
|
+
...snapshotPatch,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return render(
|
|
30
|
+
<QueryClientProvider client={queryClient}>
|
|
31
|
+
<ChatConversationHeader
|
|
32
|
+
snapshot={snapshot}
|
|
33
|
+
childSessionCount={0}
|
|
34
|
+
sessionCronJobCount={0}
|
|
35
|
+
layoutMode="desktop"
|
|
36
|
+
normalizedAgentId={snapshot.agentId ?? ""}
|
|
37
|
+
sessionHeaderTitle={snapshot.sessionDisplayName ?? "New Task"}
|
|
38
|
+
shouldShowHeaderAgentAvatar={false}
|
|
39
|
+
shouldShowSessionHeader={Boolean(
|
|
40
|
+
snapshot.sessionKey || snapshot.sessionTypeLabel,
|
|
41
|
+
)}
|
|
42
|
+
onOpenChildSessions={vi.fn()}
|
|
43
|
+
onOpenSessionCronJobs={vi.fn()}
|
|
44
|
+
onDeleteSession={vi.fn()}
|
|
45
|
+
/>
|
|
46
|
+
</QueryClientProvider>,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("ChatConversationHeader", () => {
|
|
51
|
+
it("uses a stable desktop height before and after session materialization", () => {
|
|
52
|
+
renderHeader({});
|
|
53
|
+
|
|
54
|
+
const header = screen.getByText("New Task").closest(".border-b");
|
|
55
|
+
|
|
56
|
+
expect(header?.className).toContain("h-[52px]");
|
|
57
|
+
expect(header?.className).not.toContain("transition-all");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("uses the standard session-header action button density after the session is materialized", () => {
|
|
61
|
+
renderHeader({
|
|
62
|
+
sessionKey: "session-1",
|
|
63
|
+
canDeleteSession: true,
|
|
64
|
+
sessionDisplayName: "First message",
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const moreActions = screen.getByRole("button", { name: "More actions" });
|
|
68
|
+
const header = screen.getByText("First message").closest(".border-b");
|
|
69
|
+
|
|
70
|
+
expect(header?.className).toContain("h-[52px]");
|
|
71
|
+
expect(moreActions.className).toContain("h-7");
|
|
72
|
+
expect(moreActions.className).toContain("w-7");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -44,6 +44,7 @@ export function ChatParentSessionBanner({
|
|
|
44
44
|
export function ChatConversationHeader({
|
|
45
45
|
snapshot,
|
|
46
46
|
childSessionCount,
|
|
47
|
+
sessionCronJobCount,
|
|
47
48
|
layoutMode,
|
|
48
49
|
normalizedAgentId,
|
|
49
50
|
sessionHeaderTitle,
|
|
@@ -51,10 +52,12 @@ export function ChatConversationHeader({
|
|
|
51
52
|
shouldShowSessionHeader,
|
|
52
53
|
onBackToList,
|
|
53
54
|
onOpenChildSessions,
|
|
55
|
+
onOpenSessionCronJobs,
|
|
54
56
|
onDeleteSession,
|
|
55
57
|
}: {
|
|
56
58
|
snapshot: ChatThreadSnapshot;
|
|
57
59
|
childSessionCount: number;
|
|
60
|
+
sessionCronJobCount: number;
|
|
58
61
|
layoutMode: "desktop" | "mobile";
|
|
59
62
|
normalizedAgentId: string;
|
|
60
63
|
sessionHeaderTitle: string;
|
|
@@ -62,6 +65,7 @@ export function ChatConversationHeader({
|
|
|
62
65
|
shouldShowSessionHeader: boolean;
|
|
63
66
|
onBackToList?: () => void;
|
|
64
67
|
onOpenChildSessions: () => void;
|
|
68
|
+
onOpenSessionCronJobs: () => void;
|
|
65
69
|
onDeleteSession: ChatHeaderDeleteHandler;
|
|
66
70
|
}) {
|
|
67
71
|
const isMobileLayout = layoutMode === "mobile";
|
|
@@ -69,10 +73,10 @@ export function ChatConversationHeader({
|
|
|
69
73
|
return (
|
|
70
74
|
<div
|
|
71
75
|
className={cn(
|
|
72
|
-
"border-b border-gray-200/60 bg-white/80 backdrop-blur-sm flex items-center justify-between shrink-0 overflow-hidden transition-
|
|
76
|
+
"border-b border-gray-200/60 bg-white/80 backdrop-blur-sm flex items-center justify-between shrink-0 overflow-hidden transition-colors duration-200",
|
|
73
77
|
isMobileLayout ? "px-3 sm:px-3" : "px-4 sm:px-5",
|
|
74
78
|
shouldShowSessionHeader ? "opacity-100" : "h-0 py-0 opacity-0 border-b-0",
|
|
75
|
-
shouldShowSessionHeader && (isMobileLayout ? "pb-2 pt-2" : "
|
|
79
|
+
shouldShowSessionHeader && (isMobileLayout ? "min-h-12 pb-2 pt-2" : "h-[52px]"),
|
|
76
80
|
)}
|
|
77
81
|
style={
|
|
78
82
|
isMobileLayout && shouldShowSessionHeader
|
|
@@ -137,7 +141,9 @@ export function ChatConversationHeader({
|
|
|
137
141
|
isDeletePending={snapshot.isDeletePending}
|
|
138
142
|
projectRoot={snapshot.sessionProjectRoot}
|
|
139
143
|
childSessionCount={childSessionCount}
|
|
144
|
+
sessionCronJobCount={sessionCronJobCount}
|
|
140
145
|
onOpenChildSessions={onOpenChildSessions}
|
|
146
|
+
onOpenSessionCronJobs={onOpenSessionCronJobs}
|
|
141
147
|
onDeleteSession={onDeleteSession}
|
|
142
148
|
/>
|
|
143
149
|
) : null}
|