@nextclaw/ui 0.14.1 → 0.14.2
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 +29 -0
- package/dist/assets/{api-DPcvCLfa.js → api-a14tR_z1.js} +7 -7
- package/dist/assets/channels-list-page-CFGkm4DT.js +8 -0
- package/dist/assets/chat-page-CEdavJXc.js +105 -0
- package/dist/assets/{config-split-page-BCJhqdK9.js → config-split-page-B5b9B0uE.js} +1 -1
- package/dist/assets/{confirm-dialog-CWh5FfK2.js → confirm-dialog-ClH_Ec7n.js} +1 -1
- package/dist/assets/{desktop-update-config-DsRhBizi.js → desktop-update-config-DPNDnVpk.js} +1 -1
- package/dist/assets/{dist-DPrgcEC0.js → dist-BtCS2Ucq.js} +1 -1
- package/dist/assets/{dist-DsE4OsSS.js → dist-CLGGLm8b.js} +1 -1
- package/dist/assets/doc-browser-5nMWYKBD.js +1 -0
- package/dist/assets/doc-browser-BJRLhztv.js +1 -0
- package/dist/assets/doc-browser-context-D4JwMMxK.js +1 -0
- package/dist/assets/{ellipsis-B81FStLm.js → ellipsis-CtJXjIol.js} +1 -1
- package/dist/assets/{external-link-C_dmG-WJ.js → external-link-CdJiG13p.js} +1 -1
- package/dist/assets/index-BDN1zzMQ.js +105 -0
- package/dist/assets/index-g3uSZXoc.css +1 -0
- package/dist/assets/mcp-marketplace-page-BU0kIjhR.js +40 -0
- package/dist/assets/mcp-marketplace-page-sMNFSq_T.js +1 -0
- package/dist/assets/middleware-CkFT3R4v.js +1 -0
- package/dist/assets/model-config-page-D6hYXROd.js +1 -0
- package/dist/assets/plus-DPjb0piO.js +1 -0
- package/dist/assets/provider-scoped-model-input-DAX2HgmR.js +1 -0
- package/dist/assets/providers-config-page-BzAeaACA.js +1 -0
- package/dist/assets/{react-sZz5HSCm.js → react-C6cZgP44.js} +1 -1
- package/dist/assets/remote-B0G80uPr.js +1 -0
- package/dist/assets/rotate-cw-DnEB1Rju.js +1 -0
- package/dist/assets/runtime-config-page-C5xKaV3i.js +1 -0
- package/dist/assets/{save-MzjoiHri.js → save-DKA0x4zk.js} +1 -1
- package/dist/assets/{search-B29dE4GN.js → search-BNlcoj5c.js} +1 -1
- package/dist/assets/{search-config-page-Dl5sZzob.js → search-config-page-Cq9uJ-N8.js} +1 -1
- package/dist/assets/{secrets-config-page-C_l-IFCK.js → secrets-config-page-CxSbZLBo.js} +2 -2
- package/dist/assets/{select-BKmh05hd.js → select-CvTlDZTQ.js} +1 -1
- package/dist/assets/{tag-chip-BS_7Ueom.js → tag-chip-8ffF2y2C.js} +1 -1
- package/dist/assets/use-config-DmT0o1gv.js +1 -0
- package/dist/assets/x-BiSSpgqB.js +1 -0
- package/dist/index.html +15 -14
- package/package.json +9 -9
- package/src/app/components/app-presenter-provider.tsx +11 -2
- package/src/app/components/layout/__tests__/app-layout.test.tsx +11 -2
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +110 -79
- package/src/app/components/layout/app-layout.tsx +18 -4
- package/src/app/components/layout/sidebar-items.tsx +194 -71
- package/src/app/components/layout/sidebar-rail.styles.ts +15 -0
- package/src/app/components/layout/sidebar.tsx +340 -83
- package/src/app/managers/viewport-layout.manager.ts +10 -0
- package/src/app/stores/viewport-layout.store.ts +39 -3
- package/src/features/agents/components/__tests__/agents-page.test.tsx +62 -39
- package/src/features/agents/components/agents-page.tsx +10 -6
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +145 -0
- package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +69 -762
- package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +132 -0
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +22 -15
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +14 -15
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +18 -18
- package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +10 -4
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +52 -11
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +16 -14
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +247 -0
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +80 -7
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +82 -25
- package/src/features/chat/components/layout/chat-sidebar.tsx +59 -92
- package/src/features/chat/components/providers/chat-presenter.provider.tsx +0 -6
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +289 -0
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +427 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +204 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-attachments.ts +66 -0
- package/src/features/chat/features/{input/hooks/use-chat-input-bar-query-state.ts → conversation/hooks/use-session-conversation-input-query.ts} +32 -17
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +281 -0
- package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +158 -0
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +56 -0
- package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +117 -0
- package/src/features/chat/features/input/input-surface-plugins/__tests__/panel-app-reference-plugin.test.ts +77 -0
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +10 -0
- package/src/features/chat/features/input/input-surface-plugins/input-surface-search.utils.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +161 -0
- package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +22 -1
- package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +26 -13
- package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +7 -28
- package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +6 -0
- package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +26 -51
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +2 -0
- package/src/features/chat/features/input/utils/ncp-chat-input-availability.utils.ts +5 -6
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +47 -12
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +93 -12
- package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +31 -12
- package/src/features/chat/features/message/utils/chat-message-markdown-part.utils.ts +38 -0
- package/src/features/chat/features/message/utils/chat-message-part.utils.ts +1 -1
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +2 -2
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +2 -2
- package/src/features/chat/features/session/hooks/__tests__/use-chat-session-project.test.tsx +3 -30
- package/src/features/chat/features/session/hooks/use-chat-session-project.ts +0 -5
- package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +8 -0
- package/src/features/chat/features/session-type/components/__tests__/chat-session-type-menu.test.tsx +2 -2
- package/src/features/chat/features/session-type/components/__tests__/chat-session-type-option-item.test.tsx +2 -0
- package/src/features/chat/features/session-type/components/chat-session-type-menu.tsx +2 -2
- package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +2 -2
- package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +49 -31
- package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +2 -2
- package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +21 -10
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +2 -2
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +2 -2
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +54 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +44 -76
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +5 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +7 -1
- package/src/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state.ts +40 -31
- package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +59 -1
- package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +109 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +44 -4
- package/src/features/chat/index.ts +0 -1
- package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +51 -99
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +135 -21
- package/src/features/chat/managers/chat-session-list.manager.ts +12 -43
- package/src/features/chat/managers/chat-thread.manager.ts +108 -34
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +48 -1
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +72 -0
- package/src/features/chat/pages/ncp-chat-page.tsx +0 -122
- package/src/features/chat/presenters/chat.presenter.ts +2 -19
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +31 -0
- package/src/features/chat/stores/chat-thread.store.ts +42 -26
- package/src/features/panel-apps/hooks/use-panel-apps.ts +2 -1
- package/src/platforms/desktop/components/__tests__/desktop-app-shell.test.tsx +50 -10
- package/src/platforms/desktop/components/desktop-app-shell.tsx +34 -10
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +41 -16
- package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +24 -1
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +8 -4
- package/src/shared/lib/i18n/locales/en-US/chat.json +20 -2
- package/src/shared/lib/i18n/locales/en-US/core.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +20 -2
- package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -0
- package/src/shared/lib/ui-document-title/__tests__/ui-document-title.test.ts +49 -0
- package/src/shared/lib/ui-document-title/index.ts +50 -21
- package/dist/assets/channels-list-page-D6jtziu4.js +0 -8
- package/dist/assets/chat-page-BBnNJoKG.js +0 -105
- package/dist/assets/doc-browser-C8xOF_9F.js +0 -1
- package/dist/assets/doc-browser-DgQedeAn.js +0 -1
- package/dist/assets/doc-browser-context-B46tQGmO.js +0 -1
- package/dist/assets/index-7o1WdUbU.js +0 -103
- package/dist/assets/index-DQWmEWzk.css +0 -1
- package/dist/assets/mcp-marketplace-page-CWZN7ohK.js +0 -1
- package/dist/assets/mcp-marketplace-page-Dzull_4-.js +0 -40
- package/dist/assets/model-config-page-Cv0O3BIj.js +0 -1
- package/dist/assets/plus-Bnt56oqG.js +0 -1
- package/dist/assets/provider-scoped-model-input-Duuh7i3h.js +0 -1
- package/dist/assets/providers-config-page-CPDz0Lxc.js +0 -1
- package/dist/assets/remote-B0LMs8u7.js +0 -1
- package/dist/assets/rotate-cw-BfyG_KEF.js +0 -1
- package/dist/assets/runtime-config-page-q0fvv8ME.js +0 -1
- package/dist/assets/use-config-DZkBWIdL.js +0 -1
- package/dist/assets/x-CW2KO-Hz.js +0 -1
- package/src/features/chat/components/conversation/chat-conversation-alerts.tsx +0 -37
- package/src/features/chat/features/input/components/__tests__/chat-attachment-upload-limit.test.ts +0 -38
- package/src/features/chat/features/input/components/chat-input-bar.container.tsx +0 -496
- package/src/features/chat/features/message/utils/chat-message-inline-content.utils.ts +0 -95
- package/src/features/chat/managers/__tests__/chat-input.manager.test.ts +0 -448
- package/src/features/chat/managers/__tests__/chat-run-snapshot.manager.test.ts +0 -129
- package/src/features/chat/managers/__tests__/chat-run.manager.test.ts +0 -236
- package/src/features/chat/managers/__tests__/chat-session-preference-sync.manager.test.ts +0 -219
- package/src/features/chat/managers/chat-input.manager.ts +0 -430
- package/src/features/chat/managers/chat-run.manager.ts +0 -110
- package/src/features/chat/managers/chat-session-preference-sync.manager.ts +0 -142
- package/src/features/chat/stores/chat-input.store.ts +0 -104
- /package/dist/assets/{config-hints-CTqBnCDp.js → config-hints-BkYBc7z0.js} +0 -0
package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useStickyBottomScroll } from "@nextclaw/agent-chat-ui";
|
|
3
|
-
import { FolderGit2, Loader2 } from "lucide-react";
|
|
1
|
+
import { MessageSquarePlus } from "lucide-react";
|
|
4
2
|
import type { CronJobView } from "@/shared/lib/api";
|
|
5
|
-
import { ChatMessageListContainer } from "@/features/chat/features/message/components/chat-message-list.container";
|
|
6
3
|
import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
|
|
4
|
+
import { SessionConversationArea } from "@/features/chat/features/conversation/components/session-conversation-area";
|
|
7
5
|
import { ChatSessionWorkspaceFilePreview } from "@/features/chat/features/workspace/components/chat-session-workspace-file-preview";
|
|
8
6
|
import { SessionCronJobContent } from "@/features/chat/features/workspace/components/session-cron-job-content";
|
|
9
7
|
import type { ResolvedChildSessionTab } from "@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view";
|
|
10
|
-
import { useNcpSessionConversation } from "@/features/chat/features/ncp/hooks/use-ncp-session-conversation";
|
|
11
8
|
import type { WorkspaceSelection } from "@/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils";
|
|
12
|
-
import { AgentIdentityAvatar } from "@/shared/components/common/agent-identity";
|
|
13
9
|
import { t } from "@/shared/lib/i18n";
|
|
14
10
|
|
|
15
11
|
type ChatSessionWorkspacePanelContentProps = {
|
|
@@ -19,53 +15,9 @@ type ChatSessionWorkspacePanelContentProps = {
|
|
|
19
15
|
sessionWorkingDir: string | null;
|
|
20
16
|
};
|
|
21
17
|
|
|
22
|
-
function ChildSessionContent({ sessionKey }: { sessionKey: string }) {
|
|
23
|
-
const agent = useNcpSessionConversation(sessionKey);
|
|
24
|
-
const messages = agent.visibleMessages;
|
|
25
|
-
const scrollRef = useRef<HTMLDivElement>(null);
|
|
26
|
-
const { onScroll } = useStickyBottomScroll({
|
|
27
|
-
scrollRef,
|
|
28
|
-
resetKey: sessionKey,
|
|
29
|
-
isLoading: agent.isHydrating,
|
|
30
|
-
hasContent: messages.length > 0,
|
|
31
|
-
contentVersion: messages[messages.length - 1] ?? null,
|
|
32
|
-
stickyThresholdPx: 20,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div
|
|
37
|
-
ref={scrollRef}
|
|
38
|
-
onScroll={onScroll}
|
|
39
|
-
className="h-full overflow-y-auto custom-scrollbar"
|
|
40
|
-
>
|
|
41
|
-
{agent.isHydrating ? (
|
|
42
|
-
<div className="flex h-full items-center justify-center text-sm text-gray-500">
|
|
43
|
-
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
44
|
-
{t("chatChildSessionLoading")}
|
|
45
|
-
</div>
|
|
46
|
-
) : agent.hydrateError ? (
|
|
47
|
-
<div className="px-4 py-5 text-sm text-rose-600">
|
|
48
|
-
{agent.hydrateError.message}
|
|
49
|
-
</div>
|
|
50
|
-
) : messages.length === 0 && !agent.isRunning ? (
|
|
51
|
-
<div className="px-4 py-5 text-sm text-gray-500">
|
|
52
|
-
{t("chatChildSessionEmpty")}
|
|
53
|
-
</div>
|
|
54
|
-
) : (
|
|
55
|
-
<div className="px-4 py-5">
|
|
56
|
-
<ChatMessageListContainer
|
|
57
|
-
messages={messages}
|
|
58
|
-
isSending={agent.isRunning}
|
|
59
|
-
/>
|
|
60
|
-
</div>
|
|
61
|
-
)}
|
|
62
|
-
</div>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
18
|
function ChildSessionMetaChip({ value }: { value: string }) {
|
|
67
19
|
return (
|
|
68
|
-
<span className="inline-flex max-w-full items-center rounded border border-gray-200 bg-gray-50 px-
|
|
20
|
+
<span className="inline-flex max-w-full shrink-0 items-center rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 text-[10px] font-medium text-gray-600">
|
|
69
21
|
<span className="truncate">{value}</span>
|
|
70
22
|
</span>
|
|
71
23
|
);
|
|
@@ -83,41 +35,32 @@ function ChildSessionMetaStrip({ tab }: { tab: ResolvedChildSessionTab }) {
|
|
|
83
35
|
}
|
|
84
36
|
|
|
85
37
|
return (
|
|
86
|
-
<div className="
|
|
87
|
-
{metaItems.
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
<ChildSessionMetaChip key={item} value={item} />
|
|
91
|
-
))}
|
|
92
|
-
</div>
|
|
93
|
-
) : null}
|
|
38
|
+
<div className="flex min-h-9 min-w-0 items-center gap-1.5 border-b border-gray-200/70 px-3 py-1.5 text-[11px] text-gray-500">
|
|
39
|
+
{metaItems.map((item) => (
|
|
40
|
+
<ChildSessionMetaChip key={item} value={item} />
|
|
41
|
+
))}
|
|
94
42
|
{tab.projectRoot ? (
|
|
95
|
-
<
|
|
43
|
+
<span
|
|
96
44
|
title={tab.projectRoot}
|
|
97
|
-
className="truncate rounded border border-gray-200 bg-gray-50 px-
|
|
45
|
+
className="min-w-0 flex-1 truncate rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 font-mono text-[10px] text-gray-500"
|
|
98
46
|
>
|
|
99
47
|
{tab.projectRoot}
|
|
100
|
-
</
|
|
48
|
+
</span>
|
|
101
49
|
) : null}
|
|
102
50
|
</div>
|
|
103
51
|
);
|
|
104
52
|
}
|
|
105
53
|
|
|
106
|
-
function
|
|
54
|
+
function WorkspaceSideChatDraftHeader() {
|
|
107
55
|
return (
|
|
108
56
|
<div className="border-b border-gray-200/70 px-4 py-3">
|
|
109
57
|
<div className="flex min-w-0 items-center gap-2 text-sm font-semibold text-gray-900">
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
agentId={tab.agentId}
|
|
113
|
-
className="h-4 w-4 shrink-0"
|
|
114
|
-
/>
|
|
115
|
-
) : (
|
|
116
|
-
<FolderGit2 className="h-4 w-4 shrink-0 text-gray-400" />
|
|
117
|
-
)}
|
|
118
|
-
<span className="truncate">{tab.title}</span>
|
|
58
|
+
<MessageSquarePlus className="h-4 w-4 shrink-0 text-primary" />
|
|
59
|
+
<span className="truncate">{t("chatWorkspaceSideChatDraftTitle")}</span>
|
|
119
60
|
</div>
|
|
120
|
-
<
|
|
61
|
+
<p className="mt-1 text-xs leading-5 text-gray-500">
|
|
62
|
+
{t("chatWorkspaceSideChatDraftSubtitle")}
|
|
63
|
+
</p>
|
|
121
64
|
</div>
|
|
122
65
|
);
|
|
123
66
|
}
|
|
@@ -130,12 +73,37 @@ export function ChatSessionWorkspacePanelContent({
|
|
|
130
73
|
}: ChatSessionWorkspacePanelContentProps) {
|
|
131
74
|
const presenter = usePresenter();
|
|
132
75
|
|
|
76
|
+
if (activeSelection.kind === "side-chat-draft") {
|
|
77
|
+
return (
|
|
78
|
+
<>
|
|
79
|
+
<WorkspaceSideChatDraftHeader />
|
|
80
|
+
<div className="flex min-h-0 flex-1 flex-col">
|
|
81
|
+
<SessionConversationArea
|
|
82
|
+
materializationContext={{
|
|
83
|
+
kind: "child",
|
|
84
|
+
parentSessionKey: activeSelection.draft.parentSessionKey,
|
|
85
|
+
inheritContext: true,
|
|
86
|
+
}}
|
|
87
|
+
sessionKey={null}
|
|
88
|
+
showWelcomeForDraft={false}
|
|
89
|
+
onSessionMaterialized={(sessionKey) =>
|
|
90
|
+
presenter.chatThreadManager.materializeSideChatDraft({
|
|
91
|
+
draftKey: activeSelection.draft.draftKey,
|
|
92
|
+
sessionKey,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
</>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
133
101
|
if (activeSelection.kind === "child-session") {
|
|
134
102
|
return (
|
|
135
103
|
<>
|
|
136
|
-
<
|
|
137
|
-
<div className="flex
|
|
138
|
-
<
|
|
104
|
+
<ChildSessionMetaStrip tab={activeSelection.tab} />
|
|
105
|
+
<div className="flex min-h-0 flex-1 flex-col">
|
|
106
|
+
<SessionConversationArea sessionKey={activeSelection.tab.sessionKey} />
|
|
139
107
|
</div>
|
|
140
108
|
</>
|
|
141
109
|
);
|
package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AlarmClock, ArrowLeft, ArrowRight, FileCode2, MessageSquareText, X } from "lucide-react";
|
|
1
|
+
import { AlarmClock, ArrowLeft, ArrowRight, FileCode2, MessageSquarePlus, MessageSquareText, X } from "lucide-react";
|
|
2
2
|
import type { WorkspaceTabViewModel } from "@/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils";
|
|
3
3
|
import { AgentIdentityAvatar } from "@/shared/components/common/agent-identity";
|
|
4
4
|
import {
|
|
@@ -17,6 +17,10 @@ function WorkspaceTabIcon({ agentId, kind }: Pick<WorkspaceTabViewModel, "agentI
|
|
|
17
17
|
return <FileCode2 className="h-3.5 w-3.5 shrink-0 text-gray-400" />;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
if (kind === "side-chat-draft") {
|
|
21
|
+
return <MessageSquarePlus className="h-3.5 w-3.5 shrink-0 text-primary" />;
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
if (agentId) {
|
|
21
25
|
return (
|
|
22
26
|
<AgentIdentityAvatar agentId={agentId} className="h-3.5 w-3.5 shrink-0" />
|
|
@@ -4,6 +4,7 @@ import { useNcpChildSessionTabsView } from "@/features/chat/features/ncp/hooks/u
|
|
|
4
4
|
import { useChatSessionListStore } from "@/features/chat/stores/chat-session-list.store";
|
|
5
5
|
import type {
|
|
6
6
|
ChatChildSessionTab,
|
|
7
|
+
ChatWorkspaceSideChatDraft,
|
|
7
8
|
ChatWorkspaceNavigationEntry,
|
|
8
9
|
ChatWorkspaceFileTab,
|
|
9
10
|
} from "@/features/chat/stores/chat-thread.store";
|
|
@@ -26,11 +27,12 @@ type ChatSessionWorkspacePanelProps = {
|
|
|
26
27
|
sessionKey: string | null;
|
|
27
28
|
childSessionTabs: readonly ChatChildSessionTab[];
|
|
28
29
|
activeChildSessionKey: string | null;
|
|
30
|
+
activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
|
|
29
31
|
workspaceFileTabs: readonly ChatWorkspaceFileTab[];
|
|
30
32
|
activeWorkspaceFileKey: string | null;
|
|
31
33
|
workspaceNavigationHistory?: readonly ChatWorkspaceNavigationEntry[];
|
|
32
34
|
workspaceNavigationHistoryIndex?: number;
|
|
33
|
-
activePanelKind?: "child-session" | "file" | "cron" | null;
|
|
35
|
+
activePanelKind?: "child-session" | "side-chat-draft" | "file" | "cron" | null;
|
|
34
36
|
sessionCronJobs?: readonly CronJobView[];
|
|
35
37
|
sessionProjectRoot: string | null;
|
|
36
38
|
sessionWorkingDir: string | null;
|
|
@@ -41,6 +43,7 @@ export function ChatSessionWorkspacePanel({
|
|
|
41
43
|
sessionKey,
|
|
42
44
|
childSessionTabs,
|
|
43
45
|
activeChildSessionKey,
|
|
46
|
+
activeSideChatDraft,
|
|
44
47
|
workspaceFileTabs,
|
|
45
48
|
activeWorkspaceFileKey,
|
|
46
49
|
workspaceNavigationHistory = [],
|
|
@@ -58,6 +61,7 @@ export function ChatSessionWorkspacePanel({
|
|
|
58
61
|
);
|
|
59
62
|
const activeSelection = resolveWorkspaceSelection({
|
|
60
63
|
activeChildSessionKey,
|
|
64
|
+
activeSideChatDraft,
|
|
61
65
|
activeWorkspaceFileKey,
|
|
62
66
|
activePanelKind,
|
|
63
67
|
childSessionTabs: resolvedChildTabs,
|
|
@@ -77,6 +81,7 @@ export function ChatSessionWorkspacePanel({
|
|
|
77
81
|
() =>
|
|
78
82
|
buildWorkspaceTabsViewModel({
|
|
79
83
|
resolvedChildTabs,
|
|
84
|
+
activeSideChatDraft,
|
|
80
85
|
workspaceFileTabs,
|
|
81
86
|
sessionCronJobCount: sessionCronJobs.length,
|
|
82
87
|
activeSelection,
|
|
@@ -91,6 +96,7 @@ export function ChatSessionWorkspacePanel({
|
|
|
91
96
|
}),
|
|
92
97
|
[
|
|
93
98
|
activeSelection,
|
|
99
|
+
activeSideChatDraft,
|
|
94
100
|
optimisticReadAtBySessionKey,
|
|
95
101
|
presenter.chatThreadManager,
|
|
96
102
|
resolvedChildTabs,
|
|
@@ -1,63 +1,72 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import { useCronJobs } from "@/features/cron";
|
|
3
3
|
import type { useChatThreadStore } from "@/features/chat/stores/chat-thread.store";
|
|
4
|
+
import { adaptNcpSessionSummaries } from "@/features/chat/features/session/utils/ncp-session-adapter.utils";
|
|
4
5
|
import { isCronJobForSession } from "@/shared/lib/cron";
|
|
6
|
+
import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
|
|
5
7
|
|
|
6
8
|
type ChatThreadSnapshot = ReturnType<
|
|
7
9
|
typeof useChatThreadStore.getState
|
|
8
10
|
>["snapshot"];
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
snapshot: ChatThreadSnapshot,
|
|
12
|
-
childSessionTabs: ChatThreadSnapshot["childSessionTabs"],
|
|
13
|
-
workspaceFileTabs: ChatThreadSnapshot["workspaceFileTabs"],
|
|
14
|
-
sessionCronJobCount: number,
|
|
15
|
-
) {
|
|
16
|
-
if (snapshot.workspacePanelParentKey !== snapshot.sessionKey) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
return (
|
|
20
|
-
childSessionTabs.length > 0 ||
|
|
21
|
-
workspaceFileTabs.length > 0 ||
|
|
22
|
-
sessionCronJobCount > 0
|
|
23
|
-
);
|
|
24
|
-
}
|
|
12
|
+
const EMPTY_SESSION_SUMMARIES: never[] = [];
|
|
25
13
|
|
|
26
14
|
export function useChatConversationWorkspaceState(
|
|
27
15
|
snapshot: ChatThreadSnapshot,
|
|
16
|
+
sessionKey: string | null,
|
|
28
17
|
) {
|
|
29
|
-
const
|
|
30
|
-
() =>
|
|
31
|
-
snapshot.
|
|
32
|
-
(tab) => tab.parentSessionKey === snapshot.sessionKey,
|
|
33
|
-
),
|
|
34
|
-
[snapshot.childSessionTabs, snapshot.sessionKey],
|
|
18
|
+
const sessionSummaries = useChatQueryStore(
|
|
19
|
+
(state) =>
|
|
20
|
+
state.snapshot.sessionsQuery?.data?.sessions ?? EMPTY_SESSION_SUMMARIES,
|
|
35
21
|
);
|
|
22
|
+
const childSessionTabs = useMemo(() => {
|
|
23
|
+
const tabs = snapshot.childSessionTabs.filter(
|
|
24
|
+
(tab) => tab.parentSessionKey === sessionKey,
|
|
25
|
+
);
|
|
26
|
+
const tabKeys = new Set(tabs.map((tab) => tab.sessionKey));
|
|
27
|
+
return [
|
|
28
|
+
...tabs,
|
|
29
|
+
...adaptNcpSessionSummaries(sessionSummaries)
|
|
30
|
+
.filter(
|
|
31
|
+
(session) =>
|
|
32
|
+
session.parentSessionId === sessionKey && !tabKeys.has(session.key),
|
|
33
|
+
)
|
|
34
|
+
.map((session) => ({
|
|
35
|
+
sessionKey: session.key,
|
|
36
|
+
parentSessionKey: sessionKey,
|
|
37
|
+
})),
|
|
38
|
+
];
|
|
39
|
+
}, [sessionKey, sessionSummaries, snapshot.childSessionTabs]);
|
|
36
40
|
const workspaceFileTabs = useMemo(
|
|
37
41
|
() =>
|
|
38
42
|
snapshot.workspaceFileTabs.filter(
|
|
39
|
-
(tab) => tab.parentSessionKey ===
|
|
43
|
+
(tab) => tab.parentSessionKey === sessionKey,
|
|
40
44
|
),
|
|
41
|
-
[
|
|
45
|
+
[sessionKey, snapshot.workspaceFileTabs],
|
|
42
46
|
);
|
|
47
|
+
const activeSideChatDraft =
|
|
48
|
+
snapshot.activeSideChatDraft?.parentSessionKey === sessionKey
|
|
49
|
+
? snapshot.activeSideChatDraft
|
|
50
|
+
: null;
|
|
43
51
|
const cronQuery = useCronJobs({ all: true });
|
|
44
52
|
const sessionCronJobs = useMemo(
|
|
45
53
|
() =>
|
|
46
54
|
(cronQuery.data?.jobs ?? []).filter((job) =>
|
|
47
|
-
isCronJobForSession(job,
|
|
55
|
+
isCronJobForSession(job, sessionKey),
|
|
48
56
|
),
|
|
49
|
-
[cronQuery.data?.jobs,
|
|
57
|
+
[cronQuery.data?.jobs, sessionKey],
|
|
50
58
|
);
|
|
51
59
|
|
|
52
60
|
return {
|
|
53
61
|
childSessionTabs,
|
|
62
|
+
activeSideChatDraft,
|
|
54
63
|
workspaceFileTabs,
|
|
55
64
|
sessionCronJobs,
|
|
56
|
-
showWorkspacePanel:
|
|
57
|
-
snapshot
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
showWorkspacePanel:
|
|
66
|
+
snapshot.workspacePanelParentKey === sessionKey &&
|
|
67
|
+
(activeSideChatDraft !== null ||
|
|
68
|
+
childSessionTabs.length > 0 ||
|
|
69
|
+
workspaceFileTabs.length > 0 ||
|
|
70
|
+
sessionCronJobs.length > 0),
|
|
62
71
|
};
|
|
63
72
|
}
|
|
@@ -3,7 +3,7 @@ import type { ResolvedChildSessionTab } from "@/features/chat/features/ncp/hooks
|
|
|
3
3
|
import {
|
|
4
4
|
buildWorkspaceTabsViewModel,
|
|
5
5
|
resolveWorkspaceSelection,
|
|
6
|
-
} from "
|
|
6
|
+
} from "@/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils";
|
|
7
7
|
|
|
8
8
|
function createChildTab(
|
|
9
9
|
overrides: Partial<ResolvedChildSessionTab> = {},
|
|
@@ -39,6 +39,7 @@ describe("resolveWorkspaceSelection", () => {
|
|
|
39
39
|
resolveWorkspaceSelection({
|
|
40
40
|
activePanelKind: "file",
|
|
41
41
|
activeChildSessionKey: childTab.sessionKey,
|
|
42
|
+
activeSideChatDraft: null,
|
|
42
43
|
activeWorkspaceFileKey: fileTab.key,
|
|
43
44
|
childSessionTabs: [childTab],
|
|
44
45
|
workspaceFileTabs: [fileTab],
|
|
@@ -57,6 +58,7 @@ describe("resolveWorkspaceSelection", () => {
|
|
|
57
58
|
resolveWorkspaceSelection({
|
|
58
59
|
activePanelKind: null,
|
|
59
60
|
activeChildSessionKey: null,
|
|
61
|
+
activeSideChatDraft: null,
|
|
60
62
|
activeWorkspaceFileKey: null,
|
|
61
63
|
childSessionTabs: [childTab],
|
|
62
64
|
workspaceFileTabs: [],
|
|
@@ -67,6 +69,28 @@ describe("resolveWorkspaceSelection", () => {
|
|
|
67
69
|
tab: childTab,
|
|
68
70
|
});
|
|
69
71
|
});
|
|
72
|
+
|
|
73
|
+
it("honors the explicit side chat draft selection", () => {
|
|
74
|
+
const draft = {
|
|
75
|
+
draftKey: "draft-1",
|
|
76
|
+
parentSessionKey: "parent-1",
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
expect(
|
|
80
|
+
resolveWorkspaceSelection({
|
|
81
|
+
activePanelKind: "side-chat-draft",
|
|
82
|
+
activeChildSessionKey: null,
|
|
83
|
+
activeSideChatDraft: draft,
|
|
84
|
+
activeWorkspaceFileKey: null,
|
|
85
|
+
childSessionTabs: [createChildTab()],
|
|
86
|
+
workspaceFileTabs: [],
|
|
87
|
+
sessionCronJobCount: 0,
|
|
88
|
+
}),
|
|
89
|
+
).toMatchObject({
|
|
90
|
+
kind: "side-chat-draft",
|
|
91
|
+
draft,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
70
94
|
});
|
|
71
95
|
|
|
72
96
|
describe("buildWorkspaceTabsViewModel", () => {
|
|
@@ -79,6 +103,7 @@ describe("buildWorkspaceTabsViewModel", () => {
|
|
|
79
103
|
|
|
80
104
|
const tabs = buildWorkspaceTabsViewModel({
|
|
81
105
|
resolvedChildTabs: [childTab],
|
|
106
|
+
activeSideChatDraft: null,
|
|
82
107
|
workspaceFileTabs: [],
|
|
83
108
|
sessionCronJobCount: 0,
|
|
84
109
|
activeSelection: null,
|
|
@@ -100,4 +125,37 @@ describe("buildWorkspaceTabsViewModel", () => {
|
|
|
100
125
|
tabs[0]?.onSelect();
|
|
101
126
|
expect(onSelectSession).toHaveBeenCalledWith("child-1");
|
|
102
127
|
});
|
|
128
|
+
|
|
129
|
+
it("places the side chat draft tab before child session tabs", () => {
|
|
130
|
+
const draft = {
|
|
131
|
+
draftKey: "draft-1",
|
|
132
|
+
parentSessionKey: "parent-1",
|
|
133
|
+
};
|
|
134
|
+
const childTab = createChildTab();
|
|
135
|
+
|
|
136
|
+
const tabs = buildWorkspaceTabsViewModel({
|
|
137
|
+
resolvedChildTabs: [childTab],
|
|
138
|
+
activeSideChatDraft: draft,
|
|
139
|
+
workspaceFileTabs: [],
|
|
140
|
+
sessionCronJobCount: 0,
|
|
141
|
+
activeSelection: {
|
|
142
|
+
kind: "side-chat-draft",
|
|
143
|
+
draft,
|
|
144
|
+
},
|
|
145
|
+
optimisticReadAtBySessionKey: {},
|
|
146
|
+
onSelectSession: vi.fn(),
|
|
147
|
+
onSelectFile: vi.fn(),
|
|
148
|
+
onCloseFile: vi.fn(),
|
|
149
|
+
onSelectCronJobs: vi.fn(),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
expect(tabs.map((tab) => tab.key)).toEqual([
|
|
153
|
+
"side-chat-draft:draft-1",
|
|
154
|
+
"child:child-1",
|
|
155
|
+
]);
|
|
156
|
+
expect(tabs[0]).toMatchObject({
|
|
157
|
+
kind: "side-chat-draft",
|
|
158
|
+
active: true,
|
|
159
|
+
});
|
|
160
|
+
});
|
|
103
161
|
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChatChildSessionTab,
|
|
3
|
+
ChatThreadSnapshot,
|
|
4
|
+
ChatWorkspaceNavigationEntry,
|
|
5
|
+
ChatWorkspaceSideChatDraft,
|
|
6
|
+
} from "@/features/chat/stores/chat-thread.store";
|
|
7
|
+
import {
|
|
8
|
+
filterNavigationHistoryEntries,
|
|
9
|
+
pushNavigationHistoryEntry,
|
|
10
|
+
} from "@/shared/lib/navigation-history";
|
|
11
|
+
|
|
12
|
+
export function areWorkspaceNavigationEntriesEqual(
|
|
13
|
+
current: ChatWorkspaceNavigationEntry,
|
|
14
|
+
next: ChatWorkspaceNavigationEntry,
|
|
15
|
+
): boolean {
|
|
16
|
+
if (current.kind !== next.kind) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (current.kind === 'cron') {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return next.kind !== 'cron' && current.key === next.key;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createSideChatDraft(parentSessionKey: string): ChatWorkspaceSideChatDraft {
|
|
26
|
+
const randomPart =
|
|
27
|
+
typeof globalThis.crypto?.randomUUID === 'function'
|
|
28
|
+
? globalThis.crypto.randomUUID()
|
|
29
|
+
: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
30
|
+
return {
|
|
31
|
+
draftKey: `side-chat-draft:${parentSessionKey}:${randomPart}`,
|
|
32
|
+
parentSessionKey,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function upsertChildSessionTab(
|
|
37
|
+
childSessionTabs: readonly ChatChildSessionTab[],
|
|
38
|
+
nextTab: ChatChildSessionTab,
|
|
39
|
+
): ChatChildSessionTab[] {
|
|
40
|
+
const existingIndex = childSessionTabs.findIndex((tab) => tab.sessionKey === nextTab.sessionKey);
|
|
41
|
+
if (existingIndex === -1) {
|
|
42
|
+
return [nextTab, ...childSessionTabs];
|
|
43
|
+
}
|
|
44
|
+
const existingTab = childSessionTabs[existingIndex];
|
|
45
|
+
const nextTabs = [...childSessionTabs];
|
|
46
|
+
nextTabs[existingIndex] = {
|
|
47
|
+
...existingTab,
|
|
48
|
+
parentSessionKey: existingTab.parentSessionKey ?? nextTab.parentSessionKey,
|
|
49
|
+
label: existingTab.label?.trim() ? existingTab.label : nextTab.label,
|
|
50
|
+
agentId: existingTab.agentId?.trim() ? existingTab.agentId : nextTab.agentId,
|
|
51
|
+
};
|
|
52
|
+
return nextTabs;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function materializeSideChatDraftSnapshot(params: {
|
|
56
|
+
snapshot: ChatThreadSnapshot;
|
|
57
|
+
draftKey: string;
|
|
58
|
+
sessionKey: string;
|
|
59
|
+
label?: string | null;
|
|
60
|
+
agentId?: string | null;
|
|
61
|
+
}): { patch: Partial<ChatThreadSnapshot>; parentSessionKey: string } | null {
|
|
62
|
+
const {
|
|
63
|
+
agentId: rawAgentId,
|
|
64
|
+
draftKey: rawDraftKey,
|
|
65
|
+
label: rawLabel,
|
|
66
|
+
sessionKey: rawSessionKey,
|
|
67
|
+
snapshot,
|
|
68
|
+
} = params;
|
|
69
|
+
const sessionKey = rawSessionKey.trim();
|
|
70
|
+
const draftKey = rawDraftKey.trim();
|
|
71
|
+
const { activeSideChatDraft } = snapshot;
|
|
72
|
+
if (!sessionKey || !draftKey || activeSideChatDraft?.draftKey !== draftKey) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const { parentSessionKey } = activeSideChatDraft;
|
|
76
|
+
const filteredHistory = filterNavigationHistoryEntries(
|
|
77
|
+
{
|
|
78
|
+
entries: snapshot.workspaceNavigationHistory,
|
|
79
|
+
index: snapshot.workspaceNavigationHistoryIndex,
|
|
80
|
+
},
|
|
81
|
+
(entry) => entry.kind !== 'side-chat-draft' || entry.key !== draftKey,
|
|
82
|
+
);
|
|
83
|
+
const history = pushNavigationHistoryEntry(
|
|
84
|
+
filteredHistory,
|
|
85
|
+
{
|
|
86
|
+
kind: 'child-session',
|
|
87
|
+
key: sessionKey,
|
|
88
|
+
},
|
|
89
|
+
areWorkspaceNavigationEntriesEqual,
|
|
90
|
+
);
|
|
91
|
+
return {
|
|
92
|
+
parentSessionKey,
|
|
93
|
+
patch: {
|
|
94
|
+
workspacePanelParentKey: parentSessionKey,
|
|
95
|
+
activeWorkspacePanelKind: 'child-session',
|
|
96
|
+
activeChildSessionKey: sessionKey,
|
|
97
|
+
activeSideChatDraft: null,
|
|
98
|
+
activeWorkspaceFileKey: null,
|
|
99
|
+
childSessionTabs: upsertChildSessionTab(snapshot.childSessionTabs, {
|
|
100
|
+
sessionKey,
|
|
101
|
+
parentSessionKey,
|
|
102
|
+
label: rawLabel?.trim() || null,
|
|
103
|
+
agentId: rawAgentId?.trim() || null,
|
|
104
|
+
}),
|
|
105
|
+
workspaceNavigationHistory: [...history.entries],
|
|
106
|
+
workspaceNavigationHistoryIndex: history.index,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ResolvedChildSessionTab } from "@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view";
|
|
2
2
|
import { shouldShowUnreadSessionIndicator } from "@/features/chat/stores/chat-session-list.store";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ChatWorkspaceFileTab,
|
|
5
|
+
ChatWorkspaceSideChatDraft,
|
|
6
|
+
} from "@/features/chat/stores/chat-thread.store";
|
|
4
7
|
import { t } from "@/shared/lib/i18n";
|
|
5
8
|
|
|
6
9
|
export type WorkspaceSelection =
|
|
@@ -8,6 +11,10 @@ export type WorkspaceSelection =
|
|
|
8
11
|
kind: "child-session";
|
|
9
12
|
tab: ResolvedChildSessionTab;
|
|
10
13
|
}
|
|
14
|
+
| {
|
|
15
|
+
kind: "side-chat-draft";
|
|
16
|
+
draft: ChatWorkspaceSideChatDraft;
|
|
17
|
+
}
|
|
11
18
|
| {
|
|
12
19
|
kind: "file";
|
|
13
20
|
file: ChatWorkspaceFileTab;
|
|
@@ -18,7 +25,7 @@ export type WorkspaceSelection =
|
|
|
18
25
|
|
|
19
26
|
export type WorkspaceTabViewModel = {
|
|
20
27
|
key: string;
|
|
21
|
-
kind: "child-session" | "file" | "cron";
|
|
28
|
+
kind: "child-session" | "side-chat-draft" | "file" | "cron";
|
|
22
29
|
title: string;
|
|
23
30
|
tooltip: string;
|
|
24
31
|
active: boolean;
|
|
@@ -35,8 +42,9 @@ export function readWorkspaceFileTitle(file: ChatWorkspaceFileTab): string {
|
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
export function resolveWorkspaceSelection(params: {
|
|
38
|
-
activePanelKind?: "child-session" | "file" | "cron" | null;
|
|
45
|
+
activePanelKind?: "child-session" | "side-chat-draft" | "file" | "cron" | null;
|
|
39
46
|
activeChildSessionKey: string | null;
|
|
47
|
+
activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
|
|
40
48
|
activeWorkspaceFileKey: string | null;
|
|
41
49
|
childSessionTabs: ResolvedChildSessionTab[];
|
|
42
50
|
workspaceFileTabs: readonly ChatWorkspaceFileTab[];
|
|
@@ -45,6 +53,7 @@ export function resolveWorkspaceSelection(params: {
|
|
|
45
53
|
const {
|
|
46
54
|
activePanelKind,
|
|
47
55
|
activeChildSessionKey,
|
|
56
|
+
activeSideChatDraft,
|
|
48
57
|
activeWorkspaceFileKey,
|
|
49
58
|
childSessionTabs,
|
|
50
59
|
workspaceFileTabs,
|
|
@@ -55,6 +64,13 @@ export function resolveWorkspaceSelection(params: {
|
|
|
55
64
|
return { kind: "cron" };
|
|
56
65
|
}
|
|
57
66
|
|
|
67
|
+
if (activePanelKind === "side-chat-draft" && activeSideChatDraft) {
|
|
68
|
+
return {
|
|
69
|
+
kind: "side-chat-draft",
|
|
70
|
+
draft: activeSideChatDraft,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
if (activePanelKind !== "child-session" && activeWorkspaceFileKey) {
|
|
59
75
|
const activeFile = workspaceFileTabs.find(
|
|
60
76
|
(file) => file.key === activeWorkspaceFileKey,
|
|
@@ -86,6 +102,13 @@ export function resolveWorkspaceSelection(params: {
|
|
|
86
102
|
};
|
|
87
103
|
}
|
|
88
104
|
|
|
105
|
+
if (activeSideChatDraft) {
|
|
106
|
+
return {
|
|
107
|
+
kind: "side-chat-draft",
|
|
108
|
+
draft: activeSideChatDraft,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
89
112
|
if (workspaceFileTabs[0]) {
|
|
90
113
|
return {
|
|
91
114
|
kind: "file",
|
|
@@ -102,6 +125,7 @@ export function resolveWorkspaceSelection(params: {
|
|
|
102
125
|
|
|
103
126
|
export function buildWorkspaceTabsViewModel(params: {
|
|
104
127
|
resolvedChildTabs: ResolvedChildSessionTab[];
|
|
128
|
+
activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
|
|
105
129
|
workspaceFileTabs: readonly ChatWorkspaceFileTab[];
|
|
106
130
|
sessionCronJobCount: number;
|
|
107
131
|
activeSelection: WorkspaceSelection | null;
|
|
@@ -112,6 +136,7 @@ export function buildWorkspaceTabsViewModel(params: {
|
|
|
112
136
|
onSelectCronJobs: () => void;
|
|
113
137
|
}): WorkspaceTabViewModel[] {
|
|
114
138
|
const {
|
|
139
|
+
activeSideChatDraft,
|
|
115
140
|
resolvedChildTabs,
|
|
116
141
|
workspaceFileTabs,
|
|
117
142
|
sessionCronJobCount,
|
|
@@ -123,6 +148,21 @@ export function buildWorkspaceTabsViewModel(params: {
|
|
|
123
148
|
onSelectCronJobs,
|
|
124
149
|
} = params;
|
|
125
150
|
|
|
151
|
+
const sideChatDraftTabs = activeSideChatDraft
|
|
152
|
+
? [
|
|
153
|
+
{
|
|
154
|
+
key: `side-chat-draft:${activeSideChatDraft.draftKey}`,
|
|
155
|
+
kind: "side-chat-draft" as const,
|
|
156
|
+
title: t("chatWorkspaceSideChatDraftTitle"),
|
|
157
|
+
tooltip: t("chatWorkspaceSideChatDraftSubtitle"),
|
|
158
|
+
active:
|
|
159
|
+
activeSelection?.kind === "side-chat-draft" &&
|
|
160
|
+
activeSelection.draft.draftKey === activeSideChatDraft.draftKey,
|
|
161
|
+
onSelect: () => undefined,
|
|
162
|
+
},
|
|
163
|
+
]
|
|
164
|
+
: [];
|
|
165
|
+
|
|
126
166
|
const childTabs = resolvedChildTabs.map((tab) => {
|
|
127
167
|
const optimisticReadAt = optimisticReadAtBySessionKey[tab.sessionKey];
|
|
128
168
|
const effectiveReadAt =
|
|
@@ -179,5 +219,5 @@ export function buildWorkspaceTabsViewModel(params: {
|
|
|
179
219
|
]
|
|
180
220
|
: [];
|
|
181
221
|
|
|
182
|
-
return [...childTabs, ...fileTabs, ...cronTab];
|
|
222
|
+
return [...sideChatDraftTabs, ...childTabs, ...fileTabs, ...cronTab];
|
|
183
223
|
}
|
|
@@ -4,7 +4,6 @@ export { ChatPresenterProvider } from "./components/providers/chat-presenter.pro
|
|
|
4
4
|
export { usePresenter } from "./components/providers/chat-presenter.provider";
|
|
5
5
|
export { ChatPresenter } from "./presenters/chat.presenter";
|
|
6
6
|
export { ChatDraftIntentManager } from "./managers/chat-draft-intent.manager";
|
|
7
|
-
export { useChatInputStore } from "./stores/chat-input.store";
|
|
8
7
|
export { useChatSessionListStore } from "./stores/chat-session-list.store";
|
|
9
8
|
export { useChatThreadStore } from "./stores/chat-thread.store";
|
|
10
9
|
export { useNcpChatSessionTypes } from "./features/session-type/hooks/use-ncp-chat-session-types";
|