@nextclaw/ui 0.11.21 → 0.11.23
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 +24 -0
- package/dist/assets/{ChannelsList-ByHWHkQS.js → ChannelsList-DVDu1xvz.js} +6 -6
- package/dist/assets/ChatPage-Z9tRzm_n.js +43 -0
- package/dist/assets/DocBrowser-B9OaZjmg.js +1 -0
- package/dist/assets/{DocBrowser-3y_NHZ71.js → DocBrowser-BmtBLFU0.js} +1 -1
- package/dist/assets/{DocBrowserContext-CVJuwCcw.js → DocBrowserContext-YIKkPb76.js} +1 -1
- package/dist/assets/{LogoBadge-D8fyilO-.js → LogoBadge-F7ZWdxLT.js} +1 -1
- package/dist/assets/{MarketplacePage-CmhsZXr1.js → MarketplacePage-Buo9HrOz.js} +2 -2
- package/dist/assets/MarketplacePage-D6rVQEQR.js +1 -0
- package/dist/assets/{McpMarketplacePage-C7PkCYbp.js → McpMarketplacePage-JnkYwK7p.js} +2 -2
- package/dist/assets/ModelConfig-BYRhgp0c.js +1 -0
- package/dist/assets/ProvidersList-DmLyyHvX.js +1 -0
- package/dist/assets/RemoteAccessPage-CDSSvH7Z.js +1 -0
- package/dist/assets/RuntimeConfig-v7a7Fe3x.js +1 -0
- package/dist/assets/{SearchConfig-Dm7r2yfp.js → SearchConfig-D5f1EkLE.js} +1 -1
- package/dist/assets/{SecretsConfig-BBP_mbQh.js → SecretsConfig-D61IKcYt.js} +2 -2
- package/dist/assets/{SessionsConfig-6wNJloZN.js → SessionsConfig-BRIxVTEv.js} +2 -2
- package/dist/assets/{book-open-B26jGBjY.js → book-open-CXoF5nQC.js} +1 -1
- package/dist/assets/chat-session-display-D0WpnuRZ.js +1 -0
- package/dist/assets/{chunk-JZWAC4HX-B-4B29RN.js → chunk-JZWAC4HX-CvRWvTy5.js} +1 -1
- package/dist/assets/{config-BaC29Qf-.js → config-DJswxxE8.js} +1 -1
- package/dist/assets/{createLucideIcon-DiFAvXmK.js → createLucideIcon-CjGHOWb6.js} +1 -1
- package/dist/assets/{dist-pCfWPG1A.js → dist-Cl2QB-2y.js} +1 -1
- package/dist/assets/{dist-kW_O3kyZ.js → dist-nqTTbVdA.js} +1 -1
- package/dist/assets/{external-link-D5-p-Gmm.js → external-link-tIO7zING.js} +1 -1
- package/dist/assets/{hash-BlwrSV0q.js → hash-JWUyl1pT.js} +1 -1
- package/dist/assets/i18n-CDHMXlRZ.js +1 -0
- package/dist/assets/{index-DvKS3L9j.js → index-BuwbBgmT.js} +3 -3
- package/dist/assets/index-bZ8cqQIS.css +1 -0
- package/dist/assets/{label-RyXfZqkP.js → label-BIpeNu4r.js} +1 -1
- package/dist/assets/loader-circle-Cs8XVFTw.js +1 -0
- package/dist/assets/{logos-Bpl8QTgI.js → logos-DThdM9lk.js} +1 -1
- package/dist/assets/{page-layout--S0YBU0W.js → page-layout-D3Xo605Z.js} +1 -1
- package/dist/assets/plus-PHf8q-Ct.js +1 -0
- package/dist/assets/{popover-BEjfbEwy.js → popover-BJRUGA_H.js} +1 -1
- package/dist/assets/provider-models-bz5y28rq.js +1 -0
- package/dist/assets/{react-BuSP2-8B.js → react-7ZHqQtEV.js} +1 -1
- package/dist/assets/refresh-ccw-CC6-_QuL.js +1 -0
- package/dist/assets/{save-DPPPpD_c.js → save-DJM5RRWW.js} +1 -1
- package/dist/assets/search-C91yH_6y.js +1 -0
- package/dist/assets/{security-config-6t78Ph-I.js → security-config-DbUyWcQz.js} +1 -1
- package/dist/assets/{select-CT50pzod.js → select-DSkTc61S.js} +1 -1
- package/dist/assets/skeleton-Dzg-HOiN.js +1 -0
- package/dist/assets/{status-dot-BbBqRHfh.js → status-dot-LNBlDu3q.js} +1 -1
- package/dist/assets/{switch-D3l6AcCk.js → switch-Bo-Y46HZ.js} +1 -1
- package/dist/assets/tabs-custom-DXv507_2.js +1 -0
- package/dist/assets/{trash-2-B2_AGVE3.js → trash-2-DFZmW6Gg.js} +1 -1
- package/dist/assets/useConfirmDialog-COwYXDKm.js +1 -0
- package/dist/assets/{useMutation-BzCrO8j-.js → useMutation-DrZrOgVL.js} +1 -1
- package/dist/assets/x-D7Q1yqSF.js +1 -0
- package/dist/index.html +18 -18
- package/package.json +6 -6
- package/src/api/ncp-session.test.ts +37 -0
- package/src/api/ncp-session.ts +29 -1
- package/src/api/server-path.ts +23 -0
- package/src/api/types.ts +45 -0
- package/src/components/chat/ChatConversationPanel.test.tsx +53 -9
- package/src/components/chat/ChatConversationPanel.tsx +122 -79
- package/src/components/chat/ChatSidebar.test.tsx +2 -2
- package/src/components/chat/ChatSidebar.tsx +2 -2
- package/src/components/chat/adapters/chat-input-bar.adapter.test.ts +1 -0
- package/src/components/chat/adapters/chat-input-bar.adapter.ts +7 -2
- package/src/components/chat/adapters/chat-message-part.adapter.ts +26 -14
- package/src/components/chat/adapters/chat-message.adapter.test.ts +159 -13
- package/src/components/chat/adapters/chat-message.session-request-tool-card.ts +191 -0
- package/src/components/chat/adapters/{chat-message.file-operation-card.ts → file-operation/card.ts} +74 -181
- package/src/components/chat/adapters/{chat-message.file-operation-diff.ts → file-operation/diff.ts} +178 -188
- package/src/components/chat/adapters/file-operation/line-builder.ts +249 -0
- package/src/components/chat/adapters/file-operation/record-readers.ts +233 -0
- package/src/components/chat/chat-child-session-panel.tsx +100 -0
- package/src/components/chat/chat-composer-state.ts +3 -3
- package/src/components/chat/chat-page-runtime.test.ts +1 -0
- package/src/components/chat/chat-session-display.test.ts +22 -0
- package/src/components/chat/chat-session-display.ts +6 -1
- package/src/components/chat/containers/chat-input-bar.container.tsx +21 -24
- package/src/components/chat/containers/chat-message-list.container.tsx +4 -0
- package/src/components/chat/hooks/use-chat-session-label.ts +19 -0
- package/src/components/chat/hooks/use-chat-session-project.test.tsx +117 -0
- package/src/components/chat/hooks/use-chat-session-project.ts +40 -0
- package/src/components/chat/{chat-session-label.service.ts → hooks/use-chat-session-update.ts} +11 -7
- package/src/components/chat/managers/chat-session-list.manager.ts +5 -1
- package/src/components/chat/ncp/NcpChatPage.tsx +219 -116
- package/src/components/chat/ncp/ncp-chat-page-data.test.ts +33 -0
- package/src/components/chat/ncp/ncp-chat-page-data.ts +21 -15
- package/src/components/chat/ncp/ncp-chat-thread.manager.ts +49 -0
- package/src/components/chat/ncp/ncp-session-adapter.test.ts +24 -0
- package/src/components/chat/ncp/ncp-session-adapter.ts +47 -0
- package/src/components/chat/ncp/use-ncp-session-list-view.ts +10 -1
- package/src/components/chat/presenter/chat-presenter-context.tsx +4 -1
- package/src/components/chat/session-header/chat-session-header-actions.test.tsx +63 -0
- package/src/components/chat/session-header/chat-session-header-actions.tsx +95 -0
- package/src/components/chat/session-header/chat-session-header-menu-item.tsx +35 -0
- package/src/components/chat/session-header/chat-session-project-badge.test.tsx +66 -0
- package/src/components/chat/session-header/chat-session-project-badge.tsx +102 -0
- package/src/components/chat/session-header/chat-session-project-dialog.tsx +34 -0
- package/src/components/chat/stores/chat-input.store.ts +6 -3
- package/src/components/chat/stores/chat-thread.store.ts +17 -3
- package/src/components/chat/useHydratedNcpAgent.test.tsx +30 -23
- package/src/components/path-picker/server-path-picker-dialog.test.tsx +92 -0
- package/src/components/path-picker/server-path-picker-dialog.tsx +282 -0
- package/src/hooks/server-path/use-server-path-browse.ts +19 -0
- package/src/hooks/useConfig.ts +26 -1
- package/src/lib/i18n/i18n-language-owner.ts +94 -0
- package/src/lib/i18n/i18n.path-picker.ts +12 -0
- package/src/lib/i18n.chat.ts +23 -0
- package/src/lib/i18n.ts +21 -84
- package/src/lib/session-project/session-project.utils.ts +30 -0
- package/dist/assets/ChatPage-FdT3pDnw.js +0 -42
- package/dist/assets/DocBrowser-CMdPdbZj.js +0 -1
- package/dist/assets/MarketplacePage-9oKmxN2n.js +0 -1
- package/dist/assets/ModelConfig-DmCY6jWM.js +0 -1
- package/dist/assets/ProvidersList-ClT-34aX.js +0 -1
- package/dist/assets/RemoteAccessPage-B6hUZl1O.js +0 -1
- package/dist/assets/RuntimeConfig-C5aqliGk.js +0 -1
- package/dist/assets/chat-session-display-Bjmn4aIZ.js +0 -1
- package/dist/assets/i18n-CSytxMFI.js +0 -1
- package/dist/assets/index-CUy6doWo.css +0 -1
- package/dist/assets/loader-circle-B2J777gj.js +0 -1
- package/dist/assets/plus-CM9XJ0Tf.js +0 -1
- package/dist/assets/provider-models-C8JQUd1E.js +0 -1
- package/dist/assets/search-Ctaw34Kp.js +0 -1
- package/dist/assets/skeleton-Bycyb0zU.js +0 -1
- package/dist/assets/tabs-custom-TZQ5WPWP.js +0 -1
- package/dist/assets/useConfirmDialog-BDpdjfIO.js +0 -1
- package/dist/assets/x-CHOBE-63.js +0 -1
- package/src/components/chat/adapters/chat-message.subagent-tool-card.ts +0 -154
- /package/dist/assets/{config-hints-fGnUjDe9.js → config-hints-WtpHP_DW.js} +0 -0
- /package/dist/assets/{config-layout-B-7erZRN.js → config-layout-LQ10ozRC.js} +0 -0
- /package/dist/assets/{marketplace-localization-CXeGRf6E.js → marketplace-localization-CxSTG9wr.js} +0 -0
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
} from '@/components/chat/chat-recent-skills.manager';
|
|
28
28
|
import { useI18n } from '@/components/providers/I18nProvider';
|
|
29
29
|
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
30
|
+
import type { SessionSkillEntryView } from '@/api/types';
|
|
30
31
|
import { t } from '@/lib/i18n';
|
|
31
32
|
import { toast } from 'sonner';
|
|
32
33
|
|
|
@@ -42,19 +43,17 @@ function buildThinkingLabels(): Record<ChatThinkingLevel, string> {
|
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
function toSkillRecords(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
descriptionZh?: string;
|
|
50
|
-
origin?: string;
|
|
51
|
-
}>, officialBadgeLabel: string): ChatSkillRecord[] {
|
|
46
|
+
function toSkillRecords(
|
|
47
|
+
snapshotRecords: SessionSkillEntryView[],
|
|
48
|
+
scopeLabels: Record<SessionSkillEntryView['scope'], string>
|
|
49
|
+
): ChatSkillRecord[] {
|
|
52
50
|
return snapshotRecords.map((record) => ({
|
|
53
|
-
key: record.
|
|
54
|
-
label: record.
|
|
51
|
+
key: record.ref,
|
|
52
|
+
label: record.name,
|
|
53
|
+
scopeLabel: scopeLabels[record.scope],
|
|
55
54
|
description: record.description,
|
|
56
55
|
descriptionZh: record.descriptionZh,
|
|
57
|
-
badgeLabel: record.
|
|
56
|
+
badgeLabel: scopeLabels[record.scope]
|
|
58
57
|
}));
|
|
59
58
|
}
|
|
60
59
|
|
|
@@ -88,20 +87,18 @@ export function ChatInputBarContainer() {
|
|
|
88
87
|
const inputBarRef = useRef<ChatInputBarHandle | null>(null);
|
|
89
88
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
|
90
89
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
const skillScopeLabels = useMemo<Record<'project' | 'workspace', string>>(() => {
|
|
91
|
+
return {
|
|
92
|
+
project: t('chatSkillScopeProject'),
|
|
93
|
+
workspace: t('chatSkillScopeWorkspace'),
|
|
94
|
+
};
|
|
96
95
|
}, [language]);
|
|
97
96
|
const slashTexts = useMemo(
|
|
98
97
|
() => {
|
|
99
|
-
// Keep memo reactive to locale switches even though `t` is imported as a stable function.
|
|
100
|
-
const locale = language;
|
|
101
|
-
void locale;
|
|
102
98
|
return {
|
|
103
99
|
slashSkillSubtitle: t('chatSlashTypeSkill'),
|
|
104
100
|
slashSkillSpecLabel: t('chatSlashSkillSpec'),
|
|
101
|
+
slashSkillScopeLabel: t('chatSlashSkillScope'),
|
|
105
102
|
noSkillDescription: t('chatSkillsPickerNoDescription')
|
|
106
103
|
};
|
|
107
104
|
},
|
|
@@ -109,8 +106,8 @@ export function ChatInputBarContainer() {
|
|
|
109
106
|
);
|
|
110
107
|
|
|
111
108
|
const skillRecords = useMemo(
|
|
112
|
-
() => toSkillRecords(snapshot.skillRecords,
|
|
113
|
-
[snapshot.skillRecords,
|
|
109
|
+
() => toSkillRecords(snapshot.skillRecords, skillScopeLabels),
|
|
110
|
+
[snapshot.skillRecords, skillScopeLabels]
|
|
114
111
|
);
|
|
115
112
|
const modelRecords = useMemo(() => toModelRecords(snapshot.modelOptions), [snapshot.modelOptions]);
|
|
116
113
|
const recentModelValues = chatRecentModelsManager.resolveVisible({
|
|
@@ -137,10 +134,10 @@ export function ChatInputBarContainer() {
|
|
|
137
134
|
: hasModelOptions
|
|
138
135
|
? t('chatInputPlaceholder')
|
|
139
136
|
: t('chatModelNoOptions');
|
|
140
|
-
const recentModelsLabel =
|
|
141
|
-
const allModelsLabel =
|
|
142
|
-
const recentSkillsLabel =
|
|
143
|
-
const allSkillsLabel =
|
|
137
|
+
const recentModelsLabel = t('chatPickerRecentModels');
|
|
138
|
+
const allModelsLabel = t('chatPickerAllModels');
|
|
139
|
+
const recentSkillsLabel = t('chatPickerRecent');
|
|
140
|
+
const allSkillsLabel = t('chatPickerAllSkills');
|
|
144
141
|
|
|
145
142
|
const slashItems = useMemo(
|
|
146
143
|
() => buildChatSlashItems(skillRecords, slashQuery ?? '', slashTexts, recentSkillValues),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import type { NcpMessage } from "@nextclaw/ncp";
|
|
3
3
|
import {
|
|
4
|
+
type ChatToolActionViewModel,
|
|
4
5
|
type ChatMessageViewModel,
|
|
5
6
|
ChatMessageList,
|
|
6
7
|
} from "@nextclaw/agent-chat-ui";
|
|
@@ -18,6 +19,7 @@ type ChatMessageListContainerProps = {
|
|
|
18
19
|
messages: readonly NcpMessage[];
|
|
19
20
|
isSending: boolean;
|
|
20
21
|
className?: string;
|
|
22
|
+
onToolAction?: (action: ChatToolActionViewModel) => void;
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
const messageViewModelCache = new WeakMap<
|
|
@@ -69,6 +71,7 @@ export function ChatMessageListContainer({
|
|
|
69
71
|
messages: rawMessages,
|
|
70
72
|
isSending,
|
|
71
73
|
className,
|
|
74
|
+
onToolAction,
|
|
72
75
|
}: ChatMessageListContainerProps) {
|
|
73
76
|
const { language } = useI18n();
|
|
74
77
|
const texts = useMemo<ChatMessageAdapterTexts>(
|
|
@@ -125,6 +128,7 @@ export function ChatMessageListContainer({
|
|
|
125
128
|
hasAssistantDraft={hasAssistantDraft}
|
|
126
129
|
className={className}
|
|
127
130
|
texts={messageTexts}
|
|
131
|
+
onToolAction={onToolAction}
|
|
128
132
|
/>
|
|
129
133
|
);
|
|
130
134
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { t } from '@/lib/i18n';
|
|
2
|
+
import { useChatSessionUpdate } from '@/components/chat/hooks/use-chat-session-update';
|
|
3
|
+
|
|
4
|
+
type UpdateChatSessionLabelParams = {
|
|
5
|
+
sessionKey: string;
|
|
6
|
+
label: string | null;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function useChatSessionLabel() {
|
|
10
|
+
const updateSession = useChatSessionUpdate();
|
|
11
|
+
|
|
12
|
+
return async (params: UpdateChatSessionLabelParams): Promise<void> => {
|
|
13
|
+
await updateSession({
|
|
14
|
+
sessionKey: params.sessionKey,
|
|
15
|
+
patch: { label: params.label },
|
|
16
|
+
successMessage: t('configSavedApplied'),
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { toast } from 'sonner';
|
|
4
|
+
import { useChatSessionProject } from '@/components/chat/hooks/use-chat-session-project';
|
|
5
|
+
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
6
|
+
|
|
7
|
+
const mocks = vi.hoisted(() => ({
|
|
8
|
+
updateSession: vi.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock('sonner', () => ({
|
|
12
|
+
toast: {
|
|
13
|
+
success: vi.fn(),
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock('@/components/chat/hooks/use-chat-session-update', () => ({
|
|
18
|
+
useChatSessionUpdate: () => mocks.updateSession,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
describe('useChatSessionProject', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
useChatInputStore.setState((state) => ({
|
|
24
|
+
snapshot: {
|
|
25
|
+
...state.snapshot,
|
|
26
|
+
pendingProjectRoot: null,
|
|
27
|
+
pendingProjectRootSessionKey: null,
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
vi.clearAllMocks();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('stores the draft project root locally when the session does not exist yet', async () => {
|
|
37
|
+
const { result } = renderHook(() => useChatSessionProject());
|
|
38
|
+
|
|
39
|
+
await act(async () => {
|
|
40
|
+
await result.current({
|
|
41
|
+
sessionKey: 'draft-session-1',
|
|
42
|
+
projectRoot: '/tmp/project-alpha',
|
|
43
|
+
persistToServer: false,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(mocks.updateSession).not.toHaveBeenCalled();
|
|
48
|
+
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
49
|
+
pendingProjectRoot: '/tmp/project-alpha',
|
|
50
|
+
pendingProjectRootSessionKey: 'draft-session-1',
|
|
51
|
+
});
|
|
52
|
+
expect(toast.success).toHaveBeenCalledTimes(1);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('keeps an explicit draft override when clearing the project root locally', async () => {
|
|
56
|
+
const { result } = renderHook(() => useChatSessionProject());
|
|
57
|
+
|
|
58
|
+
await act(async () => {
|
|
59
|
+
await result.current({
|
|
60
|
+
sessionKey: 'draft-session-1',
|
|
61
|
+
projectRoot: null,
|
|
62
|
+
persistToServer: false,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(mocks.updateSession).not.toHaveBeenCalled();
|
|
67
|
+
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
68
|
+
pendingProjectRoot: null,
|
|
69
|
+
pendingProjectRootSessionKey: 'draft-session-1',
|
|
70
|
+
});
|
|
71
|
+
expect(toast.success).toHaveBeenCalledTimes(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('persists to the server and mirrors the updated project override locally for an existing session', async () => {
|
|
75
|
+
const { result } = renderHook(() => useChatSessionProject());
|
|
76
|
+
|
|
77
|
+
await act(async () => {
|
|
78
|
+
await result.current({
|
|
79
|
+
sessionKey: 'session-1',
|
|
80
|
+
projectRoot: '/tmp/project-beta',
|
|
81
|
+
persistToServer: true,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(mocks.updateSession).toHaveBeenCalledWith({
|
|
86
|
+
sessionKey: 'session-1',
|
|
87
|
+
patch: { projectRoot: '/tmp/project-beta' },
|
|
88
|
+
successMessage: 'Project directory updated',
|
|
89
|
+
});
|
|
90
|
+
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
91
|
+
pendingProjectRoot: '/tmp/project-beta',
|
|
92
|
+
pendingProjectRootSessionKey: 'session-1',
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('persists clearing to the server and keeps the cleared override until session state catches up', async () => {
|
|
97
|
+
const { result } = renderHook(() => useChatSessionProject());
|
|
98
|
+
|
|
99
|
+
await act(async () => {
|
|
100
|
+
await result.current({
|
|
101
|
+
sessionKey: 'session-1',
|
|
102
|
+
projectRoot: null,
|
|
103
|
+
persistToServer: true,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(mocks.updateSession).toHaveBeenCalledWith({
|
|
108
|
+
sessionKey: 'session-1',
|
|
109
|
+
patch: { projectRoot: null },
|
|
110
|
+
successMessage: 'Project directory cleared',
|
|
111
|
+
});
|
|
112
|
+
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
113
|
+
pendingProjectRoot: null,
|
|
114
|
+
pendingProjectRootSessionKey: 'session-1',
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { toast } from 'sonner';
|
|
2
|
+
import { t } from '@/lib/i18n';
|
|
3
|
+
import { useChatSessionUpdate } from '@/components/chat/hooks/use-chat-session-update';
|
|
4
|
+
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
5
|
+
|
|
6
|
+
type UpdateChatSessionProjectParams = {
|
|
7
|
+
sessionKey: string;
|
|
8
|
+
projectRoot: string | null;
|
|
9
|
+
persistToServer: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function useChatSessionProject() {
|
|
13
|
+
const updateSession = useChatSessionUpdate();
|
|
14
|
+
|
|
15
|
+
return async (params: UpdateChatSessionProjectParams): Promise<void> => {
|
|
16
|
+
const successMessage = params.projectRoot
|
|
17
|
+
? t('chatSessionProjectUpdated')
|
|
18
|
+
: t('chatSessionProjectCleared');
|
|
19
|
+
|
|
20
|
+
if (!params.persistToServer) {
|
|
21
|
+
useChatInputStore.getState().setSnapshot({
|
|
22
|
+
pendingProjectRoot: params.projectRoot,
|
|
23
|
+
pendingProjectRootSessionKey: params.sessionKey
|
|
24
|
+
});
|
|
25
|
+
toast.success(successMessage);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
await updateSession({
|
|
30
|
+
sessionKey: params.sessionKey,
|
|
31
|
+
patch: { projectRoot: params.projectRoot },
|
|
32
|
+
successMessage,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
useChatInputStore.getState().setSnapshot({
|
|
36
|
+
pendingProjectRoot: params.projectRoot,
|
|
37
|
+
pendingProjectRootSessionKey: params.sessionKey,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
}
|
package/src/components/chat/{chat-session-label.service.ts → hooks/use-chat-session-update.ts}
RENAMED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
import { useQueryClient } from '@tanstack/react-query';
|
|
2
2
|
import { toast } from 'sonner';
|
|
3
|
+
import type { SessionPatchUpdate } from '@/api/types';
|
|
3
4
|
import { upsertNcpSessionSummaryInQueryClient } from '@/api/ncp-session-query-cache';
|
|
4
5
|
import { updateNcpSession } from '@/api/ncp-session';
|
|
5
6
|
import { t } from '@/lib/i18n';
|
|
6
7
|
|
|
7
|
-
type
|
|
8
|
+
type UpdateChatSessionParams = {
|
|
8
9
|
sessionKey: string;
|
|
9
|
-
|
|
10
|
+
patch: SessionPatchUpdate;
|
|
11
|
+
successMessage?: string;
|
|
10
12
|
};
|
|
11
13
|
|
|
12
|
-
export function
|
|
14
|
+
export function useChatSessionUpdate() {
|
|
13
15
|
const queryClient = useQueryClient();
|
|
14
16
|
|
|
15
|
-
return async (params:
|
|
17
|
+
return async (params: UpdateChatSessionParams): Promise<void> => {
|
|
16
18
|
try {
|
|
17
|
-
const updated = await updateNcpSession(params.sessionKey,
|
|
19
|
+
const updated = await updateNcpSession(params.sessionKey, params.patch);
|
|
18
20
|
upsertNcpSessionSummaryInQueryClient(queryClient, updated);
|
|
19
|
-
toast.success(t('configSavedApplied'));
|
|
21
|
+
toast.success(params.successMessage ?? t('configSavedApplied'));
|
|
20
22
|
} catch (error) {
|
|
21
|
-
toast.error(
|
|
23
|
+
toast.error(
|
|
24
|
+
t('configSaveFailed') + ': ' + (error instanceof Error ? error.message : String(error)),
|
|
25
|
+
);
|
|
22
26
|
throw error;
|
|
23
27
|
}
|
|
24
28
|
};
|
|
@@ -44,7 +44,11 @@ export class ChatSessionListManager {
|
|
|
44
44
|
? sessionType.trim()
|
|
45
45
|
: defaultSessionType;
|
|
46
46
|
this.streamActionsManager.resetStreamState();
|
|
47
|
-
useChatInputStore.getState().setSnapshot({
|
|
47
|
+
useChatInputStore.getState().setSnapshot({
|
|
48
|
+
pendingSessionType: nextSessionType,
|
|
49
|
+
pendingProjectRoot: null,
|
|
50
|
+
pendingProjectRootSessionKey: null
|
|
51
|
+
});
|
|
48
52
|
this.uiManager.goToChatRoot();
|
|
49
53
|
};
|
|
50
54
|
|