@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/session/hooks/__tests__/use-chat-session-project.test.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { act, renderHook } from '@testing-library/react';
|
|
2
|
-
import { afterEach,
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
3
|
import { toast } from 'sonner';
|
|
4
|
-
import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
|
|
5
4
|
import { useChatSessionProject } from '@/features/chat/features/session/hooks/use-chat-session-project';
|
|
6
5
|
|
|
7
6
|
const mocks = vi.hoisted(() => ({
|
|
@@ -19,21 +18,11 @@ vi.mock('@/features/chat/features/session/hooks/use-chat-session-update', () =>
|
|
|
19
18
|
}));
|
|
20
19
|
|
|
21
20
|
describe('useChatSessionProject', () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
useChatInputStore.setState((state) => ({
|
|
24
|
-
snapshot: {
|
|
25
|
-
...state.snapshot,
|
|
26
|
-
pendingProjectRoot: null,
|
|
27
|
-
pendingProjectRootSessionKey: null
|
|
28
|
-
}
|
|
29
|
-
}));
|
|
30
|
-
});
|
|
31
|
-
|
|
32
21
|
afterEach(() => {
|
|
33
22
|
vi.clearAllMocks();
|
|
34
23
|
});
|
|
35
24
|
|
|
36
|
-
it('
|
|
25
|
+
it('does not persist draft project root through the session update hook', async () => {
|
|
37
26
|
const { result } = renderHook(() => useChatSessionProject());
|
|
38
27
|
|
|
39
28
|
await act(async () => {
|
|
@@ -45,14 +34,10 @@ describe('useChatSessionProject', () => {
|
|
|
45
34
|
});
|
|
46
35
|
|
|
47
36
|
expect(mocks.updateSession).not.toHaveBeenCalled();
|
|
48
|
-
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
49
|
-
pendingProjectRoot: '/tmp/project-alpha',
|
|
50
|
-
pendingProjectRootSessionKey: 'draft-session-1'
|
|
51
|
-
});
|
|
52
37
|
expect(toast.success).toHaveBeenCalledTimes(1);
|
|
53
38
|
});
|
|
54
39
|
|
|
55
|
-
it('
|
|
40
|
+
it('does not persist draft project clearing through the session update hook', async () => {
|
|
56
41
|
const { result } = renderHook(() => useChatSessionProject());
|
|
57
42
|
|
|
58
43
|
await act(async () => {
|
|
@@ -64,10 +49,6 @@ describe('useChatSessionProject', () => {
|
|
|
64
49
|
});
|
|
65
50
|
|
|
66
51
|
expect(mocks.updateSession).not.toHaveBeenCalled();
|
|
67
|
-
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
68
|
-
pendingProjectRoot: null,
|
|
69
|
-
pendingProjectRootSessionKey: 'draft-session-1'
|
|
70
|
-
});
|
|
71
52
|
expect(toast.success).toHaveBeenCalledTimes(1);
|
|
72
53
|
});
|
|
73
54
|
|
|
@@ -87,10 +68,6 @@ describe('useChatSessionProject', () => {
|
|
|
87
68
|
patch: { projectRoot: '/tmp/project-beta' },
|
|
88
69
|
successMessage: 'Project directory updated'
|
|
89
70
|
});
|
|
90
|
-
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
91
|
-
pendingProjectRoot: null,
|
|
92
|
-
pendingProjectRootSessionKey: null
|
|
93
|
-
});
|
|
94
71
|
});
|
|
95
72
|
|
|
96
73
|
it('persists clearing to the server without keeping a session-scoped local override', async () => {
|
|
@@ -109,9 +86,5 @@ describe('useChatSessionProject', () => {
|
|
|
109
86
|
patch: { projectRoot: null },
|
|
110
87
|
successMessage: 'Project directory cleared'
|
|
111
88
|
});
|
|
112
|
-
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
113
|
-
pendingProjectRoot: null,
|
|
114
|
-
pendingProjectRootSessionKey: null
|
|
115
|
-
});
|
|
116
89
|
});
|
|
117
90
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { toast } from 'sonner';
|
|
2
2
|
import { t } from '@/shared/lib/i18n';
|
|
3
|
-
import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
|
|
4
3
|
import { useChatSessionUpdate } from '@/features/chat/features/session/hooks/use-chat-session-update';
|
|
5
4
|
|
|
6
5
|
type UpdateChatSessionProjectParams = {
|
|
@@ -17,10 +16,6 @@ export function useChatSessionProject() {
|
|
|
17
16
|
const successMessage = projectRoot ? t('chatSessionProjectUpdated') : t('chatSessionProjectCleared');
|
|
18
17
|
|
|
19
18
|
if (!persistToServer) {
|
|
20
|
-
useChatInputStore.getState().setSnapshot({
|
|
21
|
-
pendingProjectRoot: projectRoot,
|
|
22
|
-
pendingProjectRootSessionKey: sessionKey
|
|
23
|
-
});
|
|
24
19
|
toast.success(successMessage);
|
|
25
20
|
return;
|
|
26
21
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CHAT_SESSION_MATERIALIZATION_METADATA_KEY,
|
|
3
|
+
type AgentRunSessionMaterializationMetadata,
|
|
4
|
+
} from "@nextclaw/shared";
|
|
1
5
|
import { buildInlineSkillTokensFromComposer, CHAT_UI_INLINE_TOKENS_METADATA_KEY } from "@/features/chat/features/input/utils/chat-inline-token.utils";
|
|
2
6
|
import { normalizeRequestedSkills } from "@/features/chat/features/runtime/utils/chat-runtime.utils";
|
|
3
7
|
import { normalizeSessionProjectRootValue } from "@/shared/lib/session-project";
|
|
@@ -17,6 +21,7 @@ export function buildChatRunMetadata(payload: {
|
|
|
17
21
|
projectRoot?: string | null;
|
|
18
22
|
requestedSkills?: string[];
|
|
19
23
|
composerNodes?: Parameters<typeof buildInlineSkillTokensFromComposer>[0];
|
|
24
|
+
sessionMaterialization?: AgentRunSessionMaterializationMetadata | null;
|
|
20
25
|
}): Record<string, unknown> {
|
|
21
26
|
const projectRoot = normalizeSessionProjectRootValue(payload.projectRoot);
|
|
22
27
|
const metadata: Record<string, unknown> = {
|
|
@@ -36,6 +41,9 @@ export function buildChatRunMetadata(payload: {
|
|
|
36
41
|
if (inlineSkillTokens.length > 0) {
|
|
37
42
|
metadata[CHAT_UI_INLINE_TOKENS_METADATA_KEY] = inlineSkillTokens;
|
|
38
43
|
}
|
|
44
|
+
if (payload.sessionMaterialization) {
|
|
45
|
+
metadata[CHAT_SESSION_MATERIALIZATION_METADATA_KEY] = payload.sessionMaterialization;
|
|
46
|
+
}
|
|
39
47
|
return metadata;
|
|
40
48
|
}
|
|
41
49
|
|
package/src/features/chat/features/session-type/components/__tests__/chat-session-type-menu.test.tsx
CHANGED
|
@@ -4,8 +4,8 @@ import { ChatSessionTypeMenu } from "@/features/chat/features/session-type/compo
|
|
|
4
4
|
import { createPopoverAvailableHeightLimit } from "@/shared/components/ui/popover";
|
|
5
5
|
|
|
6
6
|
const options = [
|
|
7
|
-
{ value: "native", label: "Native", ready: true },
|
|
8
|
-
{ value: "codex", label: "Codex", ready: true },
|
|
7
|
+
{ value: "native", label: "Native", icon: null, ready: true },
|
|
8
|
+
{ value: "codex", label: "Codex", icon: null, ready: true },
|
|
9
9
|
];
|
|
10
10
|
|
|
11
11
|
describe("ChatSessionTypeMenu", () => {
|
|
@@ -50,6 +50,7 @@ describe("ChatSessionTypeOptionItem", () => {
|
|
|
50
50
|
option={{
|
|
51
51
|
value: "native",
|
|
52
52
|
label: "Native",
|
|
53
|
+
icon: null,
|
|
53
54
|
ready: true,
|
|
54
55
|
}}
|
|
55
56
|
onSelect={vi.fn()}
|
|
@@ -70,6 +71,7 @@ describe("ChatSessionTypeOptionItem", () => {
|
|
|
70
71
|
option={{
|
|
71
72
|
value: "claude",
|
|
72
73
|
label: "Claude",
|
|
74
|
+
icon: null,
|
|
73
75
|
ready: false,
|
|
74
76
|
reasonMessage: "Configure a provider API key first.",
|
|
75
77
|
}}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ChatSessionTypeOptionItem } from "@/features/chat/features/session-type/components/chat-session-type-option-item";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
3
3
|
import { createPopoverAvailableHeightLimit } from "@/shared/components/ui/popover";
|
|
4
4
|
import { t } from "@/shared/lib/i18n";
|
|
5
5
|
import { cn } from "@/shared/lib/utils";
|
|
6
6
|
|
|
7
|
-
type SessionTypeOption =
|
|
7
|
+
type SessionTypeOption = ChatSessionTypeOption;
|
|
8
8
|
const CHAT_SESSION_TYPE_MENU_MAX_HEIGHT =
|
|
9
9
|
createPopoverAvailableHeightLimit("18rem");
|
|
10
10
|
const CHAT_SESSION_TYPE_MENU_STYLE = {
|
package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { ChatInputSnapshot } from "@/features/chat/stores/chat-input.store";
|
|
2
1
|
import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
|
|
2
|
+
import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
3
3
|
import { StatusDot } from "@/shared/components/status/status-dot";
|
|
4
4
|
import { t } from "@/shared/lib/i18n";
|
|
5
5
|
import { cn } from "@/shared/lib/utils";
|
|
6
6
|
import { Bot, Check } from "lucide-react";
|
|
7
7
|
|
|
8
|
-
type SessionTypeOption =
|
|
8
|
+
type SessionTypeOption = ChatSessionTypeOption;
|
|
9
9
|
|
|
10
10
|
export function ChatSessionTypeOptionItem(props: {
|
|
11
11
|
option: SessionTypeOption;
|
package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import type { SetStateAction } from "react";
|
|
2
3
|
import { render, screen } from "@testing-library/react";
|
|
3
4
|
import userEvent from "@testing-library/user-event";
|
|
4
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
6
|
import { ChatConversationWelcome } from "@/features/chat/features/welcome/components/chat-conversation-welcome";
|
|
6
|
-
import { useChatInputStore } from "@/features/chat/stores/chat-input.store";
|
|
7
7
|
import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
|
|
8
8
|
import { useChatSessionListStore } from "@/features/chat/stores/chat-session-list.store";
|
|
9
9
|
import { useChatThreadStore } from "@/features/chat/stores/chat-thread.store";
|
|
@@ -11,14 +11,24 @@ import type { ChatQuerySnapshot } from "@/features/chat/stores/ncp-chat-query.st
|
|
|
11
11
|
|
|
12
12
|
const mocks = vi.hoisted(() => ({
|
|
13
13
|
setSelectedAgentId: vi.fn(),
|
|
14
|
-
applyPromptSuggestion: vi.fn(),
|
|
15
|
-
setPendingProjectRoot: vi.fn(),
|
|
16
|
-
setPendingSessionType: vi.fn(),
|
|
17
14
|
agents: [
|
|
18
15
|
{ id: "main", displayName: "Main", runtime: "native" },
|
|
19
16
|
{ id: "engineer", displayName: "Engineer", runtime: "codex" },
|
|
20
17
|
],
|
|
21
18
|
}));
|
|
19
|
+
const persistStorage = new Map<string, unknown>();
|
|
20
|
+
|
|
21
|
+
function createPersistStorage() {
|
|
22
|
+
return {
|
|
23
|
+
getItem: (name: string) => persistStorage.get(name) ?? null,
|
|
24
|
+
setItem: (name: string, value: unknown) => {
|
|
25
|
+
persistStorage.set(name, value);
|
|
26
|
+
},
|
|
27
|
+
removeItem: (name: string) => {
|
|
28
|
+
persistStorage.delete(name);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
22
32
|
|
|
23
33
|
vi.mock("@/features/chat/features/welcome/components/chat-welcome", () => ({
|
|
24
34
|
ChatWelcome: ({
|
|
@@ -60,11 +70,6 @@ vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
|
|
|
60
70
|
chatSessionListManager: {
|
|
61
71
|
setSelectedAgentId: mocks.setSelectedAgentId,
|
|
62
72
|
},
|
|
63
|
-
chatInputManager: {
|
|
64
|
-
applyPromptSuggestion: mocks.applyPromptSuggestion,
|
|
65
|
-
setPendingProjectRoot: mocks.setPendingProjectRoot,
|
|
66
|
-
setPendingSessionType: mocks.setPendingSessionType,
|
|
67
|
-
},
|
|
68
73
|
}),
|
|
69
74
|
}));
|
|
70
75
|
|
|
@@ -88,19 +93,10 @@ function createFetchedQuery<TData>(data: TData) {
|
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
function resetWelcomeTestState() {
|
|
96
|
+
persistStorage.clear();
|
|
97
|
+
useChatSessionListStore.persist.setOptions({ storage: createPersistStorage() as never });
|
|
98
|
+
useChatThreadStore.persist.setOptions({ storage: createPersistStorage() as never });
|
|
91
99
|
mocks.setSelectedAgentId.mockReset();
|
|
92
|
-
mocks.applyPromptSuggestion.mockReset();
|
|
93
|
-
mocks.setPendingProjectRoot.mockReset();
|
|
94
|
-
mocks.setPendingSessionType.mockReset();
|
|
95
|
-
useChatInputStore.setState({
|
|
96
|
-
snapshot: {
|
|
97
|
-
...useChatInputStore.getState().snapshot,
|
|
98
|
-
defaultSessionType: "native",
|
|
99
|
-
pendingSessionType: "native",
|
|
100
|
-
selectedSessionType: undefined,
|
|
101
|
-
pendingProjectRoot: null,
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
100
|
useChatSessionListStore.setState({
|
|
105
101
|
snapshot: {
|
|
106
102
|
...useChatSessionListStore.getState().snapshot,
|
|
@@ -138,56 +134,78 @@ function resetWelcomeTestState() {
|
|
|
138
134
|
});
|
|
139
135
|
}
|
|
140
136
|
|
|
137
|
+
function renderWelcome(overrides: Partial<{
|
|
138
|
+
onSelectPrompt: (prompt: string) => void;
|
|
139
|
+
onSelectProjectRoot: (projectRoot: string | null) => void;
|
|
140
|
+
onSelectSessionType: (sessionType: SetStateAction<string>) => void;
|
|
141
|
+
}> = {}) {
|
|
142
|
+
return render(
|
|
143
|
+
<ChatConversationWelcome
|
|
144
|
+
inputSlot={<div data-testid="input-slot" />}
|
|
145
|
+
pendingProjectRoot={null}
|
|
146
|
+
pendingSessionType="native"
|
|
147
|
+
selectedSessionTypeValue={null}
|
|
148
|
+
onSelectPrompt={overrides.onSelectPrompt ?? vi.fn()}
|
|
149
|
+
onSelectProjectRoot={overrides.onSelectProjectRoot ?? vi.fn()}
|
|
150
|
+
onSelectSessionType={overrides.onSelectSessionType ?? vi.fn()}
|
|
151
|
+
/>,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
141
155
|
describe("ChatConversationWelcome", () => {
|
|
142
156
|
beforeEach(resetWelcomeTestState);
|
|
143
157
|
|
|
144
|
-
it("
|
|
158
|
+
it("emits the selected welcome project through the local input callback", async () => {
|
|
145
159
|
const user = userEvent.setup();
|
|
160
|
+
const onSelectProjectRoot = vi.fn();
|
|
146
161
|
|
|
147
|
-
|
|
162
|
+
renderWelcome({ onSelectProjectRoot });
|
|
148
163
|
|
|
149
164
|
await user.click(
|
|
150
165
|
screen.getByRole("button", { name: "select draft project" }),
|
|
151
166
|
);
|
|
152
167
|
|
|
153
|
-
expect(
|
|
168
|
+
expect(onSelectProjectRoot).toHaveBeenCalledWith("/tmp/project-alpha");
|
|
154
169
|
});
|
|
155
170
|
|
|
156
|
-
it("
|
|
171
|
+
it("emits a selected prompt suggestion through the local input callback", async () => {
|
|
157
172
|
const user = userEvent.setup();
|
|
173
|
+
const onSelectPrompt = vi.fn();
|
|
158
174
|
|
|
159
|
-
|
|
175
|
+
renderWelcome({ onSelectPrompt });
|
|
160
176
|
|
|
161
177
|
await user.click(
|
|
162
178
|
screen.getByRole("button", { name: "pick prompt" }),
|
|
163
179
|
);
|
|
164
180
|
|
|
165
|
-
expect(
|
|
181
|
+
expect(onSelectPrompt).toHaveBeenCalledWith("example prompt");
|
|
166
182
|
});
|
|
167
183
|
|
|
168
184
|
|
|
169
185
|
it("syncs the pending session type when switching the draft agent", async () => {
|
|
170
186
|
const user = userEvent.setup();
|
|
187
|
+
const onSelectSessionType = vi.fn();
|
|
171
188
|
|
|
172
|
-
|
|
189
|
+
renderWelcome({ onSelectSessionType });
|
|
173
190
|
|
|
174
191
|
await user.click(
|
|
175
192
|
screen.getByRole("button", { name: "switch draft agent" }),
|
|
176
193
|
);
|
|
177
194
|
|
|
178
195
|
expect(mocks.setSelectedAgentId).toHaveBeenCalledWith("engineer");
|
|
179
|
-
expect(
|
|
196
|
+
expect(onSelectSessionType).toHaveBeenCalledWith("codex");
|
|
180
197
|
});
|
|
181
198
|
|
|
182
199
|
it("stores an explicit welcome session type selection", async () => {
|
|
183
200
|
const user = userEvent.setup();
|
|
201
|
+
const onSelectSessionType = vi.fn();
|
|
184
202
|
|
|
185
|
-
|
|
203
|
+
renderWelcome({ onSelectSessionType });
|
|
186
204
|
|
|
187
205
|
await user.click(
|
|
188
206
|
screen.getByRole("button", { name: "switch session type" }),
|
|
189
207
|
);
|
|
190
208
|
|
|
191
|
-
expect(
|
|
209
|
+
expect(onSelectSessionType).toHaveBeenCalledWith("codex");
|
|
192
210
|
});
|
|
193
211
|
});
|
|
@@ -10,8 +10,8 @@ vi.mock(
|
|
|
10
10
|
);
|
|
11
11
|
|
|
12
12
|
const sessionTypeOptions = [
|
|
13
|
-
{ value: 'native', label: 'Native', ready: true },
|
|
14
|
-
{ value: 'codex', label: 'Codex', ready: true },
|
|
13
|
+
{ value: 'native', label: 'Native', icon: null, ready: true },
|
|
14
|
+
{ value: 'codex', label: 'Codex', icon: null, ready: true },
|
|
15
15
|
];
|
|
16
16
|
|
|
17
17
|
function renderWelcome(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import type { SetStateAction } from "react";
|
|
2
3
|
import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
|
|
3
4
|
import {
|
|
4
5
|
buildSessionTypeOptions,
|
|
@@ -11,7 +12,6 @@ import {
|
|
|
11
12
|
resolveChatWelcomeSelectedSessionType,
|
|
12
13
|
} from "@/features/chat/features/welcome/utils/chat-welcome-selection.utils";
|
|
13
14
|
import { buildChatWelcomeProjectOptions } from "@/features/chat/features/welcome/utils/chat-welcome-project-options.utils";
|
|
14
|
-
import { useChatInputStore } from "@/features/chat/stores/chat-input.store";
|
|
15
15
|
import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
|
|
16
16
|
import { useChatSessionListStore } from "@/features/chat/stores/chat-session-list.store";
|
|
17
17
|
import { useChatThreadStore } from "@/features/chat/stores/chat-thread.store";
|
|
@@ -23,13 +23,24 @@ const EMPTY_NCP_SESSION_SUMMARIES: NcpSessionSummaryView[] = [];
|
|
|
23
23
|
|
|
24
24
|
type ChatConversationWelcomeProps = {
|
|
25
25
|
inputSlot: ReactNode;
|
|
26
|
+
pendingProjectRoot: string | null;
|
|
27
|
+
pendingSessionType: string;
|
|
28
|
+
selectedSessionTypeValue: string | null;
|
|
29
|
+
onSelectProjectRoot: (projectRoot: string | null) => void;
|
|
30
|
+
onSelectPrompt: (prompt: string) => void;
|
|
31
|
+
onSelectSessionType: (sessionType: SetStateAction<string>) => void;
|
|
26
32
|
};
|
|
27
33
|
|
|
28
34
|
export function ChatConversationWelcome({
|
|
29
35
|
inputSlot,
|
|
36
|
+
pendingProjectRoot,
|
|
37
|
+
pendingSessionType,
|
|
38
|
+
selectedSessionTypeValue,
|
|
39
|
+
onSelectProjectRoot,
|
|
40
|
+
onSelectPrompt,
|
|
41
|
+
onSelectSessionType,
|
|
30
42
|
}: ChatConversationWelcomeProps) {
|
|
31
43
|
const presenter = usePresenter();
|
|
32
|
-
const inputSnapshot = useChatInputStore((state) => state.snapshot);
|
|
33
44
|
const selectedAgentId = useChatSessionListStore(
|
|
34
45
|
(state) => state.snapshot.selectedAgentId,
|
|
35
46
|
);
|
|
@@ -53,18 +64,18 @@ export function ChatConversationWelcome({
|
|
|
53
64
|
config?.agents.defaults.workspace,
|
|
54
65
|
);
|
|
55
66
|
const selectedProjectRoot = normalizeSessionProjectRootValue(
|
|
56
|
-
|
|
67
|
+
pendingProjectRoot,
|
|
57
68
|
);
|
|
58
69
|
const sessionTypeOptions = buildSessionTypeOptions(
|
|
59
70
|
sessionTypesData?.options ?? [],
|
|
60
71
|
);
|
|
61
72
|
const defaultSessionType = normalizeSessionType(
|
|
62
|
-
sessionTypesData?.defaultType ??
|
|
73
|
+
sessionTypesData?.defaultType ?? DEFAULT_SESSION_TYPE,
|
|
63
74
|
);
|
|
64
75
|
const selectedSessionType = resolveChatWelcomeSelectedSessionType({
|
|
65
76
|
defaultSessionType,
|
|
66
|
-
pendingSessionType
|
|
67
|
-
selectedSessionType:
|
|
77
|
+
pendingSessionType,
|
|
78
|
+
selectedSessionType: selectedSessionTypeValue,
|
|
68
79
|
sessionTypeOptions,
|
|
69
80
|
});
|
|
70
81
|
const projectOptions = buildChatWelcomeProjectOptions({
|
|
@@ -73,7 +84,7 @@ export function ChatConversationWelcome({
|
|
|
73
84
|
});
|
|
74
85
|
const selectDraftAgent = (agentId: string) => {
|
|
75
86
|
presenter.chatSessionListManager.setSelectedAgentId(agentId);
|
|
76
|
-
|
|
87
|
+
onSelectSessionType(
|
|
77
88
|
resolveChatWelcomeSelectedSessionType({
|
|
78
89
|
agents: availableAgents,
|
|
79
90
|
agentId,
|
|
@@ -96,9 +107,9 @@ export function ChatConversationWelcome({
|
|
|
96
107
|
selectedSessionType={selectedSessionType}
|
|
97
108
|
sessionTypeOptions={sessionTypeOptions}
|
|
98
109
|
onSelectAgent={selectDraftAgent}
|
|
99
|
-
onSelectPrompt={
|
|
100
|
-
onSelectProjectRoot={
|
|
101
|
-
onSelectSessionType={
|
|
110
|
+
onSelectPrompt={onSelectPrompt}
|
|
111
|
+
onSelectProjectRoot={onSelectProjectRoot}
|
|
112
|
+
onSelectSessionType={onSelectSessionType}
|
|
102
113
|
/>
|
|
103
114
|
);
|
|
104
115
|
}
|
|
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
|
|
2
2
|
import { Bot, ChevronDown } from 'lucide-react';
|
|
3
3
|
import { SessionContextIconNode } from '@/features/chat/features/session/components/session-context-icon';
|
|
4
4
|
import { ChatSessionTypeOptionItem } from '@/features/chat/features/session-type/components/chat-session-type-option-item';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ChatSessionTypeOption } from '@/features/chat/features/session-type/utils/chat-session-type.utils';
|
|
6
6
|
import {
|
|
7
7
|
Popover,
|
|
8
8
|
PopoverContent,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from '@/shared/components/ui/popover';
|
|
12
12
|
import { t } from '@/shared/lib/i18n';
|
|
13
13
|
|
|
14
|
-
type SessionTypeOption =
|
|
14
|
+
type SessionTypeOption = ChatSessionTypeOption;
|
|
15
15
|
|
|
16
16
|
type ChatWelcomeSessionTypePickerProps = {
|
|
17
17
|
options: readonly SessionTypeOption[];
|
|
@@ -7,11 +7,11 @@ import { ChatWelcomeCapabilityGrid } from '@/features/chat/features/welcome/comp
|
|
|
7
7
|
import { ChatWelcomeProjectPicker } from '@/features/chat/features/welcome/components/chat-welcome-project-picker';
|
|
8
8
|
import { ChatWelcomeSessionTypePicker } from '@/features/chat/features/welcome/components/chat-welcome-session-type-picker';
|
|
9
9
|
import type { ChatWelcomeProjectOption } from '@/features/chat/features/welcome/utils/chat-welcome-project-options.utils';
|
|
10
|
-
import type {
|
|
10
|
+
import type { ChatSessionTypeOption } from '@/features/chat/features/session-type/utils/chat-session-type.utils';
|
|
11
11
|
import type { AgentProfileView } from '@/shared/lib/api';
|
|
12
12
|
import { t } from '@/shared/lib/i18n';
|
|
13
13
|
|
|
14
|
-
type SessionTypeOption =
|
|
14
|
+
type SessionTypeOption = ChatSessionTypeOption;
|
|
15
15
|
|
|
16
16
|
type ChatWelcomeProps = {
|
|
17
17
|
agents: AgentProfileView[];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { expect, it, vi } from "vitest";
|
|
3
|
+
import { ChatSessionWorkspacePanelContent } from "@/features/chat/features/workspace/components/chat-session-workspace-panel-content";
|
|
4
|
+
import type { ResolvedChildSessionTab } from "@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view";
|
|
5
|
+
|
|
6
|
+
vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
|
|
7
|
+
usePresenter: () => ({
|
|
8
|
+
chatThreadManager: {
|
|
9
|
+
materializeSideChatDraft: vi.fn(),
|
|
10
|
+
openFilePreview: vi.fn(),
|
|
11
|
+
},
|
|
12
|
+
}),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock("@/features/chat/features/conversation/components/session-conversation-area", () => ({
|
|
16
|
+
SessionConversationArea: () => <div data-testid="session-conversation-area" />,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
function createChildTab(): ResolvedChildSessionTab {
|
|
20
|
+
return {
|
|
21
|
+
sessionKey: "child-1",
|
|
22
|
+
parentSessionKey: "parent-1",
|
|
23
|
+
title: "Child title",
|
|
24
|
+
agentId: "agent-1",
|
|
25
|
+
updatedAt: null,
|
|
26
|
+
lastMessageAt: null,
|
|
27
|
+
readAt: null,
|
|
28
|
+
runStatus: undefined,
|
|
29
|
+
sessionTypeLabel: "原生",
|
|
30
|
+
preferredModel: "minimax/MiniMax-M3",
|
|
31
|
+
projectName: "nextbot",
|
|
32
|
+
projectRoot: "/Users/peiwang/Projects/nextbot",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
it("shows compact child session metadata without repeating the tab title", () => {
|
|
37
|
+
render(
|
|
38
|
+
<ChatSessionWorkspacePanelContent
|
|
39
|
+
activeSelection={{
|
|
40
|
+
kind: "child-session",
|
|
41
|
+
tab: createChildTab(),
|
|
42
|
+
}}
|
|
43
|
+
sessionCronJobs={[]}
|
|
44
|
+
sessionProjectRoot={null}
|
|
45
|
+
sessionWorkingDir={null}
|
|
46
|
+
/>,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(screen.queryByText("Child title")).toBeNull();
|
|
50
|
+
expect(screen.getByText("原生")).toBeTruthy();
|
|
51
|
+
expect(screen.getByText("minimax/MiniMax-M3")).toBeTruthy();
|
|
52
|
+
expect(screen.getByText("nextbot")).toBeTruthy();
|
|
53
|
+
expect(screen.getByTitle("/Users/peiwang/Projects/nextbot")).toBeTruthy();
|
|
54
|
+
});
|