@nextclaw/ui 0.9.0 → 0.9.1
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 +8 -0
- package/dist/assets/{ChannelsList-C7F_As4r.js → ChannelsList-DhvjpZcs.js} +1 -1
- package/dist/assets/ChatPage-B8VBaMQm.js +38 -0
- package/dist/assets/{DocBrowser-Dsd8Dlq8.js → DocBrowser-LpzGe8An.js} +1 -1
- package/dist/assets/{LogoBadge-2ChEc_oz.js → LogoBadge-Be4lktJN.js} +1 -1
- package/dist/assets/{MarketplacePage-BXck6-X3.js → MarketplacePage-Cx9AI3_h.js} +1 -1
- package/dist/assets/{ModelConfig-CgHRSD0b.js → ModelConfig-DuImUHIX.js} +1 -1
- package/dist/assets/{ProvidersList-PPfZucvS.js → ProvidersList-Ccleg25k.js} +1 -1
- package/dist/assets/{RuntimeConfig-ClLEKNTN.js → RuntimeConfig-C6iqpJR_.js} +1 -1
- package/dist/assets/{SearchConfig-CuXVCbrf.js → SearchConfig-Dvp1TAXu.js} +1 -1
- package/dist/assets/{SecretsConfig-udJz6Ake.js → SecretsConfig-D5Ymlvt9.js} +1 -1
- package/dist/assets/{SessionsConfig-C1XnFfiC.js → SessionsConfig-CIA_jA1P.js} +1 -1
- package/dist/assets/{chat-message-BETwXLD4.js → chat-message-B60Fh9kI.js} +1 -1
- package/dist/assets/{index-CsvP4CER.js → index-BiPDnzv0.js} +3 -3
- package/dist/assets/index-C8GsgIUn.css +1 -0
- package/dist/assets/{index-COJdlL0e.js → index-CPDASUXh.js} +1 -1
- package/dist/assets/{label-BGL-ztxh.js → label-D4fGx6Wb.js} +1 -1
- package/dist/assets/{page-layout-aw88k7tG.js → page-layout-twy8gmBE.js} +1 -1
- package/dist/assets/{popover-DyEvzhmV.js → popover-DYbYpt1j.js} +1 -1
- package/dist/assets/{security-config-BuPAQn82.js → security-config-BcIZ4rpb.js} +1 -1
- package/dist/assets/skeleton-DypBy7jp.js +1 -0
- package/dist/assets/{switch-BK8jIzto.js → switch-DqA6r5XR.js} +1 -1
- package/dist/assets/{tabs-custom-Da3cEOji.js → tabs-custom-C6enKKs1.js} +1 -1
- package/dist/assets/{useConfirmDialog-z0CE92iS.js → useConfirmDialog-CHBf5Of7.js} +1 -1
- package/dist/assets/{vendor-CkJHmX1g.js → vendor-DKBNiC31.js} +1 -1
- package/dist/index.html +3 -3
- package/package.json +6 -6
- package/src/components/chat/chat-composer-state.ts +53 -0
- package/src/components/chat/chat-stream/types.ts +3 -0
- package/src/components/chat/containers/chat-input-bar.container.tsx +12 -41
- package/src/components/chat/legacy/LegacyChatPage.tsx +1 -0
- package/src/components/chat/managers/chat-input.manager.ts +43 -13
- package/src/components/chat/ncp/NcpChatPage.tsx +11 -3
- package/src/components/chat/ncp/ncp-chat-input.manager.ts +42 -12
- package/src/components/chat/presenter/chat-presenter-context.tsx +2 -0
- package/src/components/chat/stores/chat-input.store.ts +4 -0
- package/dist/assets/ChatPage-Oo7-OUsx.js +0 -37
- package/dist/assets/index-D-bXl7qL.css +0 -1
- package/dist/assets/skeleton-drzO_tdU.js +0 -1
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
2
|
import { ChatInputBar } from '@nextclaw/agent-chat-ui';
|
|
3
3
|
import {
|
|
4
4
|
buildChatSlashItems,
|
|
5
5
|
buildModelStateHint,
|
|
6
6
|
buildModelToolbarSelect,
|
|
7
|
-
buildSelectedSkillItems,
|
|
8
7
|
buildSkillPickerModel,
|
|
9
8
|
buildThinkingToolbarSelect,
|
|
10
|
-
resolveSlashQuery,
|
|
11
9
|
type ChatModelRecord,
|
|
12
10
|
type ChatSkillRecord,
|
|
13
11
|
type ChatThinkingLevel
|
|
14
12
|
} from '@/components/chat/adapters/chat-input-bar.adapter';
|
|
15
|
-
import { useChatInputBarController } from '@/components/chat/chat-input/chat-input-bar.controller';
|
|
16
13
|
import { usePresenter } from '@/components/chat/presenter/chat-presenter-context';
|
|
17
14
|
import { useI18n } from '@/components/providers/I18nProvider';
|
|
18
15
|
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
@@ -72,6 +69,7 @@ export function ChatInputBarContainer() {
|
|
|
72
69
|
const presenter = usePresenter();
|
|
73
70
|
const { language } = useI18n();
|
|
74
71
|
const snapshot = useChatInputStore((state) => state.snapshot);
|
|
72
|
+
const [slashQuery, setSlashQuery] = useState<string | null>(null);
|
|
75
73
|
|
|
76
74
|
const officialSkillBadgeLabel = useMemo(() => {
|
|
77
75
|
// Keep memo reactive to locale switches even though `t` is imported as a stable function.
|
|
@@ -110,31 +108,11 @@ export function ChatInputBarContainer() {
|
|
|
110
108
|
? t('chatInputPlaceholder')
|
|
111
109
|
: t('chatModelNoOptions');
|
|
112
110
|
|
|
113
|
-
const slashQuery = resolveSlashQuery(snapshot.draft);
|
|
114
111
|
const slashItems = useMemo(
|
|
115
112
|
() => buildChatSlashItems(skillRecords, slashQuery ?? '', slashTexts),
|
|
116
113
|
[slashQuery, skillRecords, slashTexts]
|
|
117
114
|
);
|
|
118
115
|
|
|
119
|
-
const controller = useChatInputBarController({
|
|
120
|
-
isSlashMode: slashQuery !== null,
|
|
121
|
-
slashItems,
|
|
122
|
-
isSlashLoading: snapshot.isSkillsLoading,
|
|
123
|
-
onSelectSlashItem: (item) => {
|
|
124
|
-
if (!item.value) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (!snapshot.selectedSkills.includes(item.value)) {
|
|
128
|
-
presenter.chatInputManager.selectSkills([...snapshot.selectedSkills, item.value]);
|
|
129
|
-
}
|
|
130
|
-
presenter.chatInputManager.setDraft('');
|
|
131
|
-
},
|
|
132
|
-
onSend: presenter.chatInputManager.send,
|
|
133
|
-
onStop: presenter.chatInputManager.stop,
|
|
134
|
-
isSending: snapshot.isSending,
|
|
135
|
-
canStopGeneration: snapshot.canStopGeneration
|
|
136
|
-
});
|
|
137
|
-
|
|
138
116
|
const selectedModelOption = modelRecords.find((option) => option.value === snapshot.selectedModel);
|
|
139
117
|
const selectedModelThinkingCapability = selectedModelOption?.thinkingCapability;
|
|
140
118
|
const thinkingSupportedLevels = selectedModelThinkingCapability?.supported ?? [];
|
|
@@ -183,27 +161,23 @@ export function ChatInputBarContainer() {
|
|
|
183
161
|
|
|
184
162
|
return (
|
|
185
163
|
<ChatInputBar
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
164
|
+
composer={{
|
|
165
|
+
nodes: snapshot.composerNodes,
|
|
166
|
+
placeholder: textareaPlaceholder,
|
|
167
|
+
disabled: inputDisabled,
|
|
168
|
+
onNodesChange: presenter.chatInputManager.setComposerNodes,
|
|
169
|
+
onSlashQueryChange: setSlashQuery
|
|
170
|
+
}}
|
|
191
171
|
slashMenu={{
|
|
192
|
-
isOpen: controller.isSlashPanelOpen,
|
|
193
172
|
isLoading: snapshot.isSkillsLoading,
|
|
194
173
|
items: slashItems,
|
|
195
|
-
activeIndex: controller.activeSlashIndex,
|
|
196
|
-
activeItem: controller.activeSlashItem,
|
|
197
174
|
texts: {
|
|
198
175
|
slashLoadingLabel: t('chatSlashLoading'),
|
|
199
176
|
slashSectionLabel: t('chatSlashSectionSkills'),
|
|
200
177
|
slashEmptyLabel: t('chatSlashNoResult'),
|
|
201
178
|
slashHintLabel: t('chatSlashHint'),
|
|
202
179
|
slashSkillHintLabel: t('chatSlashSkillHint')
|
|
203
|
-
}
|
|
204
|
-
onSelectItem: controller.onSelectSlashItem,
|
|
205
|
-
onOpenChange: controller.onSlashPanelOpenChange,
|
|
206
|
-
onSetActiveIndex: controller.onSetActiveSlashIndex
|
|
180
|
+
}
|
|
207
181
|
}}
|
|
208
182
|
hint={buildModelStateHint({
|
|
209
183
|
isModelOptionsLoading,
|
|
@@ -214,17 +188,14 @@ export function ChatInputBarContainer() {
|
|
|
214
188
|
configureProviderLabel: t('chatGoConfigureProvider')
|
|
215
189
|
}
|
|
216
190
|
})}
|
|
217
|
-
selectedItems={{
|
|
218
|
-
items: buildSelectedSkillItems(snapshot.selectedSkills, skillRecords),
|
|
219
|
-
onRemove: (key) => presenter.chatInputManager.selectSkills(snapshot.selectedSkills.filter((skill) => skill !== key))
|
|
220
|
-
}}
|
|
221
191
|
toolbar={{
|
|
222
192
|
selects: toolbarSelects,
|
|
223
193
|
accessories: [
|
|
224
194
|
{
|
|
225
195
|
key: 'attach',
|
|
226
|
-
label: t('
|
|
196
|
+
label: t('chatInputAttach'),
|
|
227
197
|
icon: 'paperclip',
|
|
198
|
+
iconOnly: true,
|
|
228
199
|
disabled: true,
|
|
229
200
|
tooltip: t('chatInputAttachComingSoon')
|
|
230
201
|
}
|
|
@@ -75,6 +75,7 @@ export function LegacyChatPage({ view }: ChatPageProps) {
|
|
|
75
75
|
selectedSessionKeyRef,
|
|
76
76
|
setSelectedSessionKey: presenter.chatSessionListManager.setSelectedSessionKey,
|
|
77
77
|
setDraft: presenter.chatInputManager.setDraft,
|
|
78
|
+
setComposerNodes: presenter.chatInputManager.setComposerNodes,
|
|
78
79
|
refetchSessions: sessionsQuery.refetch,
|
|
79
80
|
refetchHistory: historyQuery.refetch
|
|
80
81
|
},
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
|
|
2
|
+
import {
|
|
3
|
+
createInitialChatComposerNodes,
|
|
4
|
+
createChatComposerNodesFromDraft,
|
|
5
|
+
deriveChatComposerDraft,
|
|
6
|
+
deriveSelectedSkillsFromComposer,
|
|
7
|
+
syncComposerSkills
|
|
8
|
+
} from '@/components/chat/chat-composer-state';
|
|
1
9
|
import { updateSession } from '@/api/config';
|
|
2
10
|
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
3
11
|
import { buildNewSessionKey } from '@/components/chat/chat-session-route';
|
|
@@ -36,6 +44,17 @@ export class ChatInputManager {
|
|
|
36
44
|
return next;
|
|
37
45
|
};
|
|
38
46
|
|
|
47
|
+
private isSameStringArray = (left: string[], right: string[]): boolean =>
|
|
48
|
+
left.length === right.length && left.every((value, index) => value === right[index]);
|
|
49
|
+
|
|
50
|
+
private syncComposerSnapshot = (nodes: ChatComposerNode[]) => {
|
|
51
|
+
useChatInputStore.getState().setSnapshot({
|
|
52
|
+
composerNodes: nodes,
|
|
53
|
+
draft: deriveChatComposerDraft(nodes),
|
|
54
|
+
selectedSkills: deriveSelectedSkillsFromComposer(nodes)
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
39
58
|
syncSnapshot = (patch: Partial<ChatInputSnapshot>) => {
|
|
40
59
|
if (!this.hasSnapshotChanges(patch)) {
|
|
41
60
|
return;
|
|
@@ -46,8 +65,8 @@ export class ChatInputManager {
|
|
|
46
65
|
Object.prototype.hasOwnProperty.call(patch, 'selectedModel') ||
|
|
47
66
|
Object.prototype.hasOwnProperty.call(patch, 'selectedThinkingLevel')
|
|
48
67
|
) {
|
|
49
|
-
const
|
|
50
|
-
this.reconcileThinkingForModel(
|
|
68
|
+
const { selectedModel } = useChatInputStore.getState().snapshot;
|
|
69
|
+
this.reconcileThinkingForModel(selectedModel);
|
|
51
70
|
}
|
|
52
71
|
};
|
|
53
72
|
|
|
@@ -57,7 +76,16 @@ export class ChatInputManager {
|
|
|
57
76
|
if (value === prev) {
|
|
58
77
|
return;
|
|
59
78
|
}
|
|
60
|
-
|
|
79
|
+
this.syncComposerSnapshot(createChatComposerNodesFromDraft(value));
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
setComposerNodes = (next: SetStateAction<ChatComposerNode[]>) => {
|
|
83
|
+
const prev = useChatInputStore.getState().snapshot.composerNodes;
|
|
84
|
+
const value = this.resolveUpdateValue(prev, next);
|
|
85
|
+
if (Object.is(value, prev)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.syncComposerSnapshot(value);
|
|
61
89
|
};
|
|
62
90
|
|
|
63
91
|
setPendingSessionType = (next: SetStateAction<string>) => {
|
|
@@ -76,14 +104,13 @@ export class ChatInputManager {
|
|
|
76
104
|
if (!message) {
|
|
77
105
|
return;
|
|
78
106
|
}
|
|
79
|
-
const requestedSkills = inputSnapshot
|
|
107
|
+
const { selectedSkills: requestedSkills, composerNodes } = inputSnapshot;
|
|
80
108
|
const hasSelectedSession = Boolean(sessionSnapshot.selectedSessionKey);
|
|
81
109
|
const sessionKey = sessionSnapshot.selectedSessionKey ?? buildNewSessionKey(sessionSnapshot.selectedAgentId);
|
|
82
110
|
if (!hasSelectedSession) {
|
|
83
111
|
this.uiManager.goToSession(sessionKey, { replace: true });
|
|
84
112
|
}
|
|
85
|
-
this.
|
|
86
|
-
this.setSelectedSkills([]);
|
|
113
|
+
this.setComposerNodes(createInitialChatComposerNodes());
|
|
87
114
|
await this.streamActionsManager.sendMessage({
|
|
88
115
|
message,
|
|
89
116
|
sessionKey,
|
|
@@ -94,7 +121,8 @@ export class ChatInputManager {
|
|
|
94
121
|
stopSupported: inputSnapshot.stopSupported,
|
|
95
122
|
stopReason: inputSnapshot.stopReason,
|
|
96
123
|
requestedSkills,
|
|
97
|
-
restoreDraftOnError: true
|
|
124
|
+
restoreDraftOnError: true,
|
|
125
|
+
composerNodes
|
|
98
126
|
});
|
|
99
127
|
};
|
|
100
128
|
|
|
@@ -132,12 +160,13 @@ export class ChatInputManager {
|
|
|
132
160
|
};
|
|
133
161
|
|
|
134
162
|
setSelectedSkills = (next: SetStateAction<string[]>) => {
|
|
135
|
-
const
|
|
163
|
+
const snapshot = useChatInputStore.getState().snapshot;
|
|
164
|
+
const { selectedSkills: prev } = snapshot;
|
|
136
165
|
const value = this.resolveUpdateValue(prev, next);
|
|
137
|
-
if (
|
|
166
|
+
if (this.isSameStringArray(value, prev)) {
|
|
138
167
|
return;
|
|
139
168
|
}
|
|
140
|
-
|
|
169
|
+
this.syncComposerSnapshot(syncComposerSkills(snapshot.composerNodes, value, snapshot.skillRecords));
|
|
141
170
|
};
|
|
142
171
|
|
|
143
172
|
selectModel = (value: string) => {
|
|
@@ -174,15 +203,16 @@ export class ChatInputManager {
|
|
|
174
203
|
private reconcileThinkingForModel(model: string): void {
|
|
175
204
|
const snapshot = useChatInputStore.getState().snapshot;
|
|
176
205
|
const modelOption = snapshot.modelOptions.find((option) => option.value === model);
|
|
177
|
-
const
|
|
178
|
-
|
|
206
|
+
const { selectedThinkingLevel } = snapshot;
|
|
207
|
+
const nextThinking = this.resolveThinkingForModel(modelOption, selectedThinkingLevel);
|
|
208
|
+
if (nextThinking !== selectedThinkingLevel) {
|
|
179
209
|
useChatInputStore.getState().setSnapshot({ selectedThinkingLevel: nextThinking });
|
|
180
210
|
}
|
|
181
211
|
}
|
|
182
212
|
|
|
183
213
|
private syncRemoteSessionType = async (normalizedType: string) => {
|
|
184
214
|
const sessionSnapshot = useChatSessionListStore.getState().snapshot;
|
|
185
|
-
const selectedSessionKey = sessionSnapshot
|
|
215
|
+
const { selectedSessionKey } = sessionSnapshot;
|
|
186
216
|
if (!selectedSessionKey) {
|
|
187
217
|
return;
|
|
188
218
|
}
|
|
@@ -212,9 +212,17 @@ export function NcpChatPage({ view }: ChatPageProps) {
|
|
|
212
212
|
await sessionsQuery.refetch();
|
|
213
213
|
} catch (error) {
|
|
214
214
|
if (payload.restoreDraftOnError) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
if (payload.composerNodes && payload.composerNodes.length > 0) {
|
|
216
|
+
presenter.chatInputManager.setComposerNodes((currentNodes) =>
|
|
217
|
+
currentNodes.length === 1 && currentNodes[0]?.type === 'text' && currentNodes[0].text.length === 0
|
|
218
|
+
? payload.composerNodes ?? currentNodes
|
|
219
|
+
: currentNodes
|
|
220
|
+
);
|
|
221
|
+
} else {
|
|
222
|
+
presenter.chatInputManager.setDraft((currentDraft) =>
|
|
223
|
+
currentDraft.trim().length === 0 ? payload.message : currentDraft
|
|
224
|
+
);
|
|
225
|
+
}
|
|
218
226
|
}
|
|
219
227
|
throw error;
|
|
220
228
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
|
|
1
2
|
import type { SetStateAction } from 'react';
|
|
2
3
|
import type { ThinkingLevel } from '@/api/types';
|
|
3
4
|
import { updateNcpSession } from '@/api/ncp-session';
|
|
5
|
+
import {
|
|
6
|
+
createChatComposerNodesFromDraft,
|
|
7
|
+
createInitialChatComposerNodes,
|
|
8
|
+
deriveChatComposerDraft,
|
|
9
|
+
deriveSelectedSkillsFromComposer,
|
|
10
|
+
syncComposerSkills
|
|
11
|
+
} from '@/components/chat/chat-composer-state';
|
|
4
12
|
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
5
13
|
import { useChatSessionListStore } from '@/components/chat/stores/chat-session-list.store';
|
|
6
14
|
import type { ChatInputSnapshot } from '@/components/chat/stores/chat-input.store';
|
|
@@ -36,6 +44,17 @@ export class NcpChatInputManager {
|
|
|
36
44
|
return next;
|
|
37
45
|
};
|
|
38
46
|
|
|
47
|
+
private isSameStringArray = (left: string[], right: string[]): boolean =>
|
|
48
|
+
left.length === right.length && left.every((value, index) => value === right[index]);
|
|
49
|
+
|
|
50
|
+
private syncComposerSnapshot = (nodes: ChatComposerNode[]) => {
|
|
51
|
+
useChatInputStore.getState().setSnapshot({
|
|
52
|
+
composerNodes: nodes,
|
|
53
|
+
draft: deriveChatComposerDraft(nodes),
|
|
54
|
+
selectedSkills: deriveSelectedSkillsFromComposer(nodes)
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
39
58
|
syncSnapshot = (patch: Partial<ChatInputSnapshot>) => {
|
|
40
59
|
if (!this.hasSnapshotChanges(patch)) {
|
|
41
60
|
return;
|
|
@@ -46,8 +65,8 @@ export class NcpChatInputManager {
|
|
|
46
65
|
Object.prototype.hasOwnProperty.call(patch, 'selectedModel') ||
|
|
47
66
|
Object.prototype.hasOwnProperty.call(patch, 'selectedThinkingLevel')
|
|
48
67
|
) {
|
|
49
|
-
const
|
|
50
|
-
this.reconcileThinkingForModel(
|
|
68
|
+
const { selectedModel } = useChatInputStore.getState().snapshot;
|
|
69
|
+
this.reconcileThinkingForModel(selectedModel);
|
|
51
70
|
}
|
|
52
71
|
};
|
|
53
72
|
|
|
@@ -57,7 +76,16 @@ export class NcpChatInputManager {
|
|
|
57
76
|
if (value === prev) {
|
|
58
77
|
return;
|
|
59
78
|
}
|
|
60
|
-
|
|
79
|
+
this.syncComposerSnapshot(createChatComposerNodesFromDraft(value));
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
setComposerNodes = (next: SetStateAction<ChatComposerNode[]>) => {
|
|
83
|
+
const prev = useChatInputStore.getState().snapshot.composerNodes;
|
|
84
|
+
const value = this.resolveUpdateValue(prev, next);
|
|
85
|
+
if (Object.is(value, prev)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.syncComposerSnapshot(value);
|
|
61
89
|
};
|
|
62
90
|
|
|
63
91
|
setPendingSessionType = (next: SetStateAction<string>) => {
|
|
@@ -76,13 +104,12 @@ export class NcpChatInputManager {
|
|
|
76
104
|
if (!message) {
|
|
77
105
|
return;
|
|
78
106
|
}
|
|
79
|
-
const requestedSkills = inputSnapshot
|
|
107
|
+
const { selectedSkills: requestedSkills, composerNodes } = inputSnapshot;
|
|
80
108
|
const sessionKey = sessionSnapshot.selectedSessionKey ?? this.getDraftSessionId();
|
|
81
109
|
if (!sessionSnapshot.selectedSessionKey) {
|
|
82
110
|
this.uiManager.goToSession(sessionKey, { replace: true });
|
|
83
111
|
}
|
|
84
|
-
this.
|
|
85
|
-
this.setSelectedSkills([]);
|
|
112
|
+
this.setComposerNodes(createInitialChatComposerNodes());
|
|
86
113
|
await this.streamActionsManager.sendMessage({
|
|
87
114
|
message,
|
|
88
115
|
sessionKey,
|
|
@@ -92,7 +119,8 @@ export class NcpChatInputManager {
|
|
|
92
119
|
thinkingLevel: inputSnapshot.selectedThinkingLevel ?? undefined,
|
|
93
120
|
stopSupported: true,
|
|
94
121
|
requestedSkills,
|
|
95
|
-
restoreDraftOnError: true
|
|
122
|
+
restoreDraftOnError: true,
|
|
123
|
+
composerNodes
|
|
96
124
|
});
|
|
97
125
|
};
|
|
98
126
|
|
|
@@ -129,12 +157,13 @@ export class NcpChatInputManager {
|
|
|
129
157
|
};
|
|
130
158
|
|
|
131
159
|
setSelectedSkills = (next: SetStateAction<string[]>) => {
|
|
132
|
-
const
|
|
160
|
+
const snapshot = useChatInputStore.getState().snapshot;
|
|
161
|
+
const { selectedSkills: prev } = snapshot;
|
|
133
162
|
const value = this.resolveUpdateValue(prev, next);
|
|
134
|
-
if (
|
|
163
|
+
if (this.isSameStringArray(value, prev)) {
|
|
135
164
|
return;
|
|
136
165
|
}
|
|
137
|
-
|
|
166
|
+
this.syncComposerSnapshot(syncComposerSkills(snapshot.composerNodes, value, snapshot.skillRecords));
|
|
138
167
|
};
|
|
139
168
|
|
|
140
169
|
selectModel = (value: string) => {
|
|
@@ -171,8 +200,9 @@ export class NcpChatInputManager {
|
|
|
171
200
|
private reconcileThinkingForModel(model: string): void {
|
|
172
201
|
const snapshot = useChatInputStore.getState().snapshot;
|
|
173
202
|
const modelOption = snapshot.modelOptions.find((option) => option.value === model);
|
|
174
|
-
const
|
|
175
|
-
|
|
203
|
+
const { selectedThinkingLevel } = snapshot;
|
|
204
|
+
const nextThinking = this.resolveThinkingForModel(modelOption, selectedThinkingLevel);
|
|
205
|
+
if (nextThinking !== selectedThinkingLevel) {
|
|
176
206
|
useChatInputStore.getState().setSnapshot({ selectedThinkingLevel: nextThinking });
|
|
177
207
|
}
|
|
178
208
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
|
|
1
2
|
import { createContext, useContext } from 'react';
|
|
2
3
|
import type { ReactNode } from 'react';
|
|
3
4
|
import type { SetStateAction } from 'react';
|
|
@@ -11,6 +12,7 @@ import type { ThinkingLevel } from '@/api/types';
|
|
|
11
12
|
export type ChatInputManagerLike = {
|
|
12
13
|
syncSnapshot: (patch: Record<string, unknown>) => void;
|
|
13
14
|
setDraft: (next: SetStateAction<string>) => void;
|
|
15
|
+
setComposerNodes: (next: SetStateAction<ChatComposerNode[]>) => void;
|
|
14
16
|
setPendingSessionType: (next: SetStateAction<string>) => void;
|
|
15
17
|
send: () => Promise<void>;
|
|
16
18
|
stop: () => Promise<void>;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
|
+
import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
|
|
2
3
|
import type { MarketplaceInstalledRecord } from '@/api/types';
|
|
3
4
|
import type { ThinkingLevel } from '@/api/types';
|
|
4
5
|
import type { ChatModelOption } from '@/components/chat/chat-input.types';
|
|
6
|
+
import { createInitialChatComposerNodes } from '@/components/chat/chat-composer-state';
|
|
5
7
|
|
|
6
8
|
export type ChatInputSnapshot = {
|
|
7
9
|
isProviderStateResolved: boolean;
|
|
10
|
+
composerNodes: ChatComposerNode[];
|
|
8
11
|
draft: string;
|
|
9
12
|
pendingSessionType: string;
|
|
10
13
|
defaultSessionType: string;
|
|
@@ -33,6 +36,7 @@ type ChatInputStore = {
|
|
|
33
36
|
|
|
34
37
|
const initialSnapshot: ChatInputSnapshot = {
|
|
35
38
|
isProviderStateResolved: false,
|
|
39
|
+
composerNodes: createInitialChatComposerNodes(),
|
|
36
40
|
draft: '',
|
|
37
41
|
pendingSessionType: 'native',
|
|
38
42
|
defaultSessionType: 'native',
|