@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.
Files changed (129) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/assets/{ChannelsList-ByHWHkQS.js → ChannelsList-DVDu1xvz.js} +6 -6
  3. package/dist/assets/ChatPage-Z9tRzm_n.js +43 -0
  4. package/dist/assets/DocBrowser-B9OaZjmg.js +1 -0
  5. package/dist/assets/{DocBrowser-3y_NHZ71.js → DocBrowser-BmtBLFU0.js} +1 -1
  6. package/dist/assets/{DocBrowserContext-CVJuwCcw.js → DocBrowserContext-YIKkPb76.js} +1 -1
  7. package/dist/assets/{LogoBadge-D8fyilO-.js → LogoBadge-F7ZWdxLT.js} +1 -1
  8. package/dist/assets/{MarketplacePage-CmhsZXr1.js → MarketplacePage-Buo9HrOz.js} +2 -2
  9. package/dist/assets/MarketplacePage-D6rVQEQR.js +1 -0
  10. package/dist/assets/{McpMarketplacePage-C7PkCYbp.js → McpMarketplacePage-JnkYwK7p.js} +2 -2
  11. package/dist/assets/ModelConfig-BYRhgp0c.js +1 -0
  12. package/dist/assets/ProvidersList-DmLyyHvX.js +1 -0
  13. package/dist/assets/RemoteAccessPage-CDSSvH7Z.js +1 -0
  14. package/dist/assets/RuntimeConfig-v7a7Fe3x.js +1 -0
  15. package/dist/assets/{SearchConfig-Dm7r2yfp.js → SearchConfig-D5f1EkLE.js} +1 -1
  16. package/dist/assets/{SecretsConfig-BBP_mbQh.js → SecretsConfig-D61IKcYt.js} +2 -2
  17. package/dist/assets/{SessionsConfig-6wNJloZN.js → SessionsConfig-BRIxVTEv.js} +2 -2
  18. package/dist/assets/{book-open-B26jGBjY.js → book-open-CXoF5nQC.js} +1 -1
  19. package/dist/assets/chat-session-display-D0WpnuRZ.js +1 -0
  20. package/dist/assets/{chunk-JZWAC4HX-B-4B29RN.js → chunk-JZWAC4HX-CvRWvTy5.js} +1 -1
  21. package/dist/assets/{config-BaC29Qf-.js → config-DJswxxE8.js} +1 -1
  22. package/dist/assets/{createLucideIcon-DiFAvXmK.js → createLucideIcon-CjGHOWb6.js} +1 -1
  23. package/dist/assets/{dist-pCfWPG1A.js → dist-Cl2QB-2y.js} +1 -1
  24. package/dist/assets/{dist-kW_O3kyZ.js → dist-nqTTbVdA.js} +1 -1
  25. package/dist/assets/{external-link-D5-p-Gmm.js → external-link-tIO7zING.js} +1 -1
  26. package/dist/assets/{hash-BlwrSV0q.js → hash-JWUyl1pT.js} +1 -1
  27. package/dist/assets/i18n-CDHMXlRZ.js +1 -0
  28. package/dist/assets/{index-DvKS3L9j.js → index-BuwbBgmT.js} +3 -3
  29. package/dist/assets/index-bZ8cqQIS.css +1 -0
  30. package/dist/assets/{label-RyXfZqkP.js → label-BIpeNu4r.js} +1 -1
  31. package/dist/assets/loader-circle-Cs8XVFTw.js +1 -0
  32. package/dist/assets/{logos-Bpl8QTgI.js → logos-DThdM9lk.js} +1 -1
  33. package/dist/assets/{page-layout--S0YBU0W.js → page-layout-D3Xo605Z.js} +1 -1
  34. package/dist/assets/plus-PHf8q-Ct.js +1 -0
  35. package/dist/assets/{popover-BEjfbEwy.js → popover-BJRUGA_H.js} +1 -1
  36. package/dist/assets/provider-models-bz5y28rq.js +1 -0
  37. package/dist/assets/{react-BuSP2-8B.js → react-7ZHqQtEV.js} +1 -1
  38. package/dist/assets/refresh-ccw-CC6-_QuL.js +1 -0
  39. package/dist/assets/{save-DPPPpD_c.js → save-DJM5RRWW.js} +1 -1
  40. package/dist/assets/search-C91yH_6y.js +1 -0
  41. package/dist/assets/{security-config-6t78Ph-I.js → security-config-DbUyWcQz.js} +1 -1
  42. package/dist/assets/{select-CT50pzod.js → select-DSkTc61S.js} +1 -1
  43. package/dist/assets/skeleton-Dzg-HOiN.js +1 -0
  44. package/dist/assets/{status-dot-BbBqRHfh.js → status-dot-LNBlDu3q.js} +1 -1
  45. package/dist/assets/{switch-D3l6AcCk.js → switch-Bo-Y46HZ.js} +1 -1
  46. package/dist/assets/tabs-custom-DXv507_2.js +1 -0
  47. package/dist/assets/{trash-2-B2_AGVE3.js → trash-2-DFZmW6Gg.js} +1 -1
  48. package/dist/assets/useConfirmDialog-COwYXDKm.js +1 -0
  49. package/dist/assets/{useMutation-BzCrO8j-.js → useMutation-DrZrOgVL.js} +1 -1
  50. package/dist/assets/x-D7Q1yqSF.js +1 -0
  51. package/dist/index.html +18 -18
  52. package/package.json +6 -6
  53. package/src/api/ncp-session.test.ts +37 -0
  54. package/src/api/ncp-session.ts +29 -1
  55. package/src/api/server-path.ts +23 -0
  56. package/src/api/types.ts +45 -0
  57. package/src/components/chat/ChatConversationPanel.test.tsx +53 -9
  58. package/src/components/chat/ChatConversationPanel.tsx +122 -79
  59. package/src/components/chat/ChatSidebar.test.tsx +2 -2
  60. package/src/components/chat/ChatSidebar.tsx +2 -2
  61. package/src/components/chat/adapters/chat-input-bar.adapter.test.ts +1 -0
  62. package/src/components/chat/adapters/chat-input-bar.adapter.ts +7 -2
  63. package/src/components/chat/adapters/chat-message-part.adapter.ts +26 -14
  64. package/src/components/chat/adapters/chat-message.adapter.test.ts +159 -13
  65. package/src/components/chat/adapters/chat-message.session-request-tool-card.ts +191 -0
  66. package/src/components/chat/adapters/{chat-message.file-operation-card.ts → file-operation/card.ts} +74 -181
  67. package/src/components/chat/adapters/{chat-message.file-operation-diff.ts → file-operation/diff.ts} +178 -188
  68. package/src/components/chat/adapters/file-operation/line-builder.ts +249 -0
  69. package/src/components/chat/adapters/file-operation/record-readers.ts +233 -0
  70. package/src/components/chat/chat-child-session-panel.tsx +100 -0
  71. package/src/components/chat/chat-composer-state.ts +3 -3
  72. package/src/components/chat/chat-page-runtime.test.ts +1 -0
  73. package/src/components/chat/chat-session-display.test.ts +22 -0
  74. package/src/components/chat/chat-session-display.ts +6 -1
  75. package/src/components/chat/containers/chat-input-bar.container.tsx +21 -24
  76. package/src/components/chat/containers/chat-message-list.container.tsx +4 -0
  77. package/src/components/chat/hooks/use-chat-session-label.ts +19 -0
  78. package/src/components/chat/hooks/use-chat-session-project.test.tsx +117 -0
  79. package/src/components/chat/hooks/use-chat-session-project.ts +40 -0
  80. package/src/components/chat/{chat-session-label.service.ts → hooks/use-chat-session-update.ts} +11 -7
  81. package/src/components/chat/managers/chat-session-list.manager.ts +5 -1
  82. package/src/components/chat/ncp/NcpChatPage.tsx +219 -116
  83. package/src/components/chat/ncp/ncp-chat-page-data.test.ts +33 -0
  84. package/src/components/chat/ncp/ncp-chat-page-data.ts +21 -15
  85. package/src/components/chat/ncp/ncp-chat-thread.manager.ts +49 -0
  86. package/src/components/chat/ncp/ncp-session-adapter.test.ts +24 -0
  87. package/src/components/chat/ncp/ncp-session-adapter.ts +47 -0
  88. package/src/components/chat/ncp/use-ncp-session-list-view.ts +10 -1
  89. package/src/components/chat/presenter/chat-presenter-context.tsx +4 -1
  90. package/src/components/chat/session-header/chat-session-header-actions.test.tsx +63 -0
  91. package/src/components/chat/session-header/chat-session-header-actions.tsx +95 -0
  92. package/src/components/chat/session-header/chat-session-header-menu-item.tsx +35 -0
  93. package/src/components/chat/session-header/chat-session-project-badge.test.tsx +66 -0
  94. package/src/components/chat/session-header/chat-session-project-badge.tsx +102 -0
  95. package/src/components/chat/session-header/chat-session-project-dialog.tsx +34 -0
  96. package/src/components/chat/stores/chat-input.store.ts +6 -3
  97. package/src/components/chat/stores/chat-thread.store.ts +17 -3
  98. package/src/components/chat/useHydratedNcpAgent.test.tsx +30 -23
  99. package/src/components/path-picker/server-path-picker-dialog.test.tsx +92 -0
  100. package/src/components/path-picker/server-path-picker-dialog.tsx +282 -0
  101. package/src/hooks/server-path/use-server-path-browse.ts +19 -0
  102. package/src/hooks/useConfig.ts +26 -1
  103. package/src/lib/i18n/i18n-language-owner.ts +94 -0
  104. package/src/lib/i18n/i18n.path-picker.ts +12 -0
  105. package/src/lib/i18n.chat.ts +23 -0
  106. package/src/lib/i18n.ts +21 -84
  107. package/src/lib/session-project/session-project.utils.ts +30 -0
  108. package/dist/assets/ChatPage-FdT3pDnw.js +0 -42
  109. package/dist/assets/DocBrowser-CMdPdbZj.js +0 -1
  110. package/dist/assets/MarketplacePage-9oKmxN2n.js +0 -1
  111. package/dist/assets/ModelConfig-DmCY6jWM.js +0 -1
  112. package/dist/assets/ProvidersList-ClT-34aX.js +0 -1
  113. package/dist/assets/RemoteAccessPage-B6hUZl1O.js +0 -1
  114. package/dist/assets/RuntimeConfig-C5aqliGk.js +0 -1
  115. package/dist/assets/chat-session-display-Bjmn4aIZ.js +0 -1
  116. package/dist/assets/i18n-CSytxMFI.js +0 -1
  117. package/dist/assets/index-CUy6doWo.css +0 -1
  118. package/dist/assets/loader-circle-B2J777gj.js +0 -1
  119. package/dist/assets/plus-CM9XJ0Tf.js +0 -1
  120. package/dist/assets/provider-models-C8JQUd1E.js +0 -1
  121. package/dist/assets/search-Ctaw34Kp.js +0 -1
  122. package/dist/assets/skeleton-Bycyb0zU.js +0 -1
  123. package/dist/assets/tabs-custom-TZQ5WPWP.js +0 -1
  124. package/dist/assets/useConfirmDialog-BDpdjfIO.js +0 -1
  125. package/dist/assets/x-CHOBE-63.js +0 -1
  126. package/src/components/chat/adapters/chat-message.subagent-tool-card.ts +0 -154
  127. /package/dist/assets/{config-hints-fGnUjDe9.js → config-hints-WtpHP_DW.js} +0 -0
  128. /package/dist/assets/{config-layout-B-7erZRN.js → config-layout-LQ10ozRC.js} +0 -0
  129. /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(snapshotRecords: Array<{
46
- spec: string;
47
- label?: string;
48
- description?: string;
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.spec,
54
- label: record.label || record.spec,
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.origin === 'builtin' ? officialBadgeLabel : undefined
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 officialSkillBadgeLabel = useMemo(() => {
92
- // Keep memo reactive to locale switches even though `t` is imported as a stable function.
93
- const locale = language;
94
- void locale;
95
- return t('chatSkillsPickerOfficial');
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, officialSkillBadgeLabel),
113
- [snapshot.skillRecords, officialSkillBadgeLabel]
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 = language === 'zh' ? '最近选择' : 'Recent';
141
- const allModelsLabel = language === 'zh' ? '全部模型' : 'All models';
142
- const recentSkillsLabel = language === 'zh' ? '最近使用' : 'Recent';
143
- const allSkillsLabel = language === 'zh' ? '全部技能' : 'All skills';
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
+ }
@@ -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 UpdateChatSessionLabelParams = {
8
+ type UpdateChatSessionParams = {
8
9
  sessionKey: string;
9
- label: string | null;
10
+ patch: SessionPatchUpdate;
11
+ successMessage?: string;
10
12
  };
11
13
 
12
- export function useChatSessionLabelService() {
14
+ export function useChatSessionUpdate() {
13
15
  const queryClient = useQueryClient();
14
16
 
15
- return async (params: UpdateChatSessionLabelParams): Promise<void> => {
17
+ return async (params: UpdateChatSessionParams): Promise<void> => {
16
18
  try {
17
- const updated = await updateNcpSession(params.sessionKey, { label: params.label });
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(t('configSaveFailed') + ': ' + (error instanceof Error ? error.message : String(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({ pendingSessionType: nextSessionType });
47
+ useChatInputStore.getState().setSnapshot({
48
+ pendingSessionType: nextSessionType,
49
+ pendingProjectRoot: null,
50
+ pendingProjectRootSessionKey: null
51
+ });
48
52
  this.uiManager.goToChatRoot();
49
53
  };
50
54