@nextclaw/ui 0.12.2 → 0.12.3
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 +6 -0
- package/dist/assets/{ChannelsList-uKmkpD25.js → ChannelsList-DZWam3Ob.js} +1 -1
- package/dist/assets/{ChatPage-CslhBPfT.js → ChatPage-YBL7iJ1X.js} +27 -27
- package/dist/assets/{MarketplacePage-DE0QjYVv.js → MarketplacePage-2tWWgwAb.js} +1 -1
- package/dist/assets/MarketplacePage-BorWJftJ.js +1 -0
- package/dist/assets/{McpMarketplacePage-CeLvv1xy.js → McpMarketplacePage-N-fB4HID.js} +1 -1
- package/dist/assets/{ModelConfig-D1JtGtQv.js → ModelConfig-DvsBTUiE.js} +1 -1
- package/dist/assets/{ProviderScopedModelInput-SAJH6nkC.js → ProviderScopedModelInput-D9woCARc.js} +1 -1
- package/dist/assets/{ProvidersList-1rKi3aQT.js → ProvidersList-D-qPGgC4.js} +1 -1
- package/dist/assets/{RemoteAccessPage-bIAKxDky.js → RemoteAccessPage-COnjm8_x.js} +1 -1
- package/dist/assets/{RuntimeConfig-BTk9319O.js → RuntimeConfig-BHpqcaHm.js} +1 -1
- package/dist/assets/{SearchConfig-EjeszXbv.js → SearchConfig-DIT6M65Q.js} +1 -1
- package/dist/assets/{SecretsConfig-cnAXvREZ.js → SecretsConfig-Cefg1LFJ.js} +1 -1
- package/dist/assets/{SessionsConfig-BIXiDaK2.js → SessionsConfig-BZnmVTIu.js} +1 -1
- package/dist/assets/{index-8XNPYwJu.js → index-DHmCjcxq.js} +2 -2
- package/dist/assets/{security-config-CGazBahs.js → security-config-DEgOD4VX.js} +1 -1
- package/dist/assets/{useConfirmDialog-D6HxybcM.js → useConfirmDialog-CuQqiPx7.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +3 -3
- package/src/components/agents/AgentsPage.test.tsx +37 -1
- package/src/components/agents/AgentsPage.tsx +10 -3
- package/src/components/chat/ChatConversationPanel.test.tsx +69 -3
- package/src/components/chat/ChatConversationPanel.tsx +24 -3
- package/src/components/chat/managers/chat-session-list.manager.test.ts +34 -3
- package/src/components/chat/managers/chat-session-list.manager.ts +17 -4
- package/src/components/chat/ncp/ncp-chat.presenter.ts +5 -1
- package/src/components/chat/stores/chat-session-list.store.ts +6 -1
- package/src/components/chat/useChatSessionTypeState.ts +9 -1
- package/dist/assets/MarketplacePage-BZQW70ti.js +0 -1
|
@@ -11,7 +11,9 @@ import { AgentAvatar } from "@/components/common/AgentAvatar";
|
|
|
11
11
|
import { usePresenter } from "@/components/chat/presenter/chat-presenter-context";
|
|
12
12
|
import { ChatSessionHeaderActions } from "@/components/chat/session-header/chat-session-header-actions";
|
|
13
13
|
import { ChatSessionProjectBadge } from "@/components/chat/session-header/chat-session-project-badge";
|
|
14
|
+
import { useChatInputStore } from "@/components/chat/stores/chat-input.store";
|
|
14
15
|
import { useChatThreadStore } from "@/components/chat/stores/chat-thread.store";
|
|
16
|
+
import { resolveAgentRuntimeSessionType } from "@/components/chat/useChatSessionTypeState";
|
|
15
17
|
import { t } from "@/lib/i18n";
|
|
16
18
|
import { cn } from "@/lib/utils";
|
|
17
19
|
|
|
@@ -45,6 +47,9 @@ function ChatConversationSkeleton() {
|
|
|
45
47
|
|
|
46
48
|
export function ChatConversationPanel() {
|
|
47
49
|
const presenter = usePresenter();
|
|
50
|
+
const defaultSessionType = useChatInputStore(
|
|
51
|
+
(state) => state.snapshot.defaultSessionType,
|
|
52
|
+
);
|
|
48
53
|
const snapshot = useChatThreadStore((state) => state.snapshot);
|
|
49
54
|
const fallbackThreadRef = useRef<HTMLDivElement | null>(null);
|
|
50
55
|
const threadRef = snapshot.threadRef ?? fallbackThreadRef;
|
|
@@ -80,6 +85,22 @@ export function ChatConversationPanel() {
|
|
|
80
85
|
snapshot.messages.length === 0 &&
|
|
81
86
|
!snapshot.isSending &&
|
|
82
87
|
!snapshot.isAwaitingAssistantOutput;
|
|
88
|
+
const availableAgents = snapshot.availableAgents ?? [];
|
|
89
|
+
const resolveDraftAgent = (agentId: string) =>
|
|
90
|
+
availableAgents.find((agent) => agent.id === agentId) ?? null;
|
|
91
|
+
const createDraftSessionForAgent = () => {
|
|
92
|
+
const sessionType = resolveAgentRuntimeSessionType(
|
|
93
|
+
resolveDraftAgent(snapshot.agentId ?? "main"),
|
|
94
|
+
defaultSessionType,
|
|
95
|
+
);
|
|
96
|
+
presenter.chatSessionListManager.createSession(sessionType);
|
|
97
|
+
};
|
|
98
|
+
const selectDraftAgent = (agentId: string) => {
|
|
99
|
+
presenter.chatSessionListManager.setSelectedAgentId(agentId);
|
|
100
|
+
presenter.chatInputManager.setPendingSessionType(
|
|
101
|
+
resolveAgentRuntimeSessionType(resolveDraftAgent(agentId), defaultSessionType),
|
|
102
|
+
);
|
|
103
|
+
};
|
|
83
104
|
|
|
84
105
|
const { onScroll: handleScroll } = useStickyBottomScroll({
|
|
85
106
|
scrollRef: threadRef,
|
|
@@ -192,10 +213,10 @@ export function ChatConversationPanel() {
|
|
|
192
213
|
>
|
|
193
214
|
{showWelcome ? (
|
|
194
215
|
<ChatWelcome
|
|
195
|
-
onCreateSession={
|
|
196
|
-
agents={
|
|
216
|
+
onCreateSession={createDraftSessionForAgent}
|
|
217
|
+
agents={availableAgents}
|
|
197
218
|
selectedAgentId={snapshot.agentId ?? "main"}
|
|
198
|
-
onSelectAgent={
|
|
219
|
+
onSelectAgent={selectDraftAgent}
|
|
199
220
|
/>
|
|
200
221
|
) : hideEmptyHint ? (
|
|
201
222
|
<div className="h-full" />
|
|
@@ -9,13 +9,16 @@ describe('ChatSessionListManager', () => {
|
|
|
9
9
|
snapshot: {
|
|
10
10
|
...useChatInputStore.getState().snapshot,
|
|
11
11
|
defaultSessionType: 'native',
|
|
12
|
-
pendingSessionType: 'native'
|
|
12
|
+
pendingSessionType: 'native',
|
|
13
|
+
pendingProjectRoot: null,
|
|
14
|
+
pendingProjectRootSessionKey: null
|
|
13
15
|
}
|
|
14
16
|
});
|
|
15
17
|
useChatSessionListStore.setState({
|
|
16
18
|
snapshot: {
|
|
17
19
|
...useChatSessionListStore.getState().snapshot,
|
|
18
|
-
selectedSessionKey: 'session-1'
|
|
20
|
+
selectedSessionKey: 'session-1',
|
|
21
|
+
listMode: 'time-first'
|
|
19
22
|
}
|
|
20
23
|
});
|
|
21
24
|
});
|
|
@@ -28,13 +31,30 @@ describe('ChatSessionListManager', () => {
|
|
|
28
31
|
resetStreamState: vi.fn()
|
|
29
32
|
} as unknown as ConstructorParameters<typeof ChatSessionListManager>[1];
|
|
30
33
|
|
|
31
|
-
const manager = new ChatSessionListManager(uiManager, streamActionsManager);
|
|
34
|
+
const manager = new ChatSessionListManager(uiManager, streamActionsManager, () => 'draft-session-1');
|
|
32
35
|
manager.createSession('codex');
|
|
33
36
|
|
|
34
37
|
expect(streamActionsManager.resetStreamState).toHaveBeenCalledTimes(1);
|
|
35
38
|
expect(uiManager.goToChatRoot).toHaveBeenCalledTimes(1);
|
|
36
39
|
expect(useChatSessionListStore.getState().snapshot.selectedSessionKey).toBe('session-1');
|
|
37
40
|
expect(useChatInputStore.getState().snapshot.pendingSessionType).toBe('codex');
|
|
41
|
+
expect(useChatInputStore.getState().snapshot.pendingProjectRoot).toBeNull();
|
|
42
|
+
expect(useChatInputStore.getState().snapshot.pendingProjectRootSessionKey).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('hydrates the draft project root when creating a session inside a project group', () => {
|
|
46
|
+
const uiManager = {
|
|
47
|
+
goToChatRoot: vi.fn()
|
|
48
|
+
} as unknown as ConstructorParameters<typeof ChatSessionListManager>[0];
|
|
49
|
+
const streamActionsManager = {
|
|
50
|
+
resetStreamState: vi.fn()
|
|
51
|
+
} as unknown as ConstructorParameters<typeof ChatSessionListManager>[1];
|
|
52
|
+
|
|
53
|
+
const manager = new ChatSessionListManager(uiManager, streamActionsManager, () => 'draft-session-9');
|
|
54
|
+
manager.createSession('native', '/tmp/project-alpha');
|
|
55
|
+
|
|
56
|
+
expect(useChatInputStore.getState().snapshot.pendingProjectRoot).toBe('/tmp/project-alpha');
|
|
57
|
+
expect(useChatInputStore.getState().snapshot.pendingProjectRootSessionKey).toBe('draft-session-9');
|
|
38
58
|
});
|
|
39
59
|
|
|
40
60
|
it('delegates existing-session selection to routing without eagerly mutating the selected session state', () => {
|
|
@@ -51,4 +71,15 @@ describe('ChatSessionListManager', () => {
|
|
|
51
71
|
expect(uiManager.goToSession).toHaveBeenCalledWith('session-2');
|
|
52
72
|
expect(useChatSessionListStore.getState().snapshot.selectedSessionKey).toBe('session-1');
|
|
53
73
|
});
|
|
74
|
+
|
|
75
|
+
it('updates the sidebar list mode without touching other session list state', () => {
|
|
76
|
+
const uiManager = {} as ConstructorParameters<typeof ChatSessionListManager>[0];
|
|
77
|
+
const streamActionsManager = {} as ConstructorParameters<typeof ChatSessionListManager>[1];
|
|
78
|
+
|
|
79
|
+
const manager = new ChatSessionListManager(uiManager, streamActionsManager);
|
|
80
|
+
manager.setListMode('project-first');
|
|
81
|
+
|
|
82
|
+
expect(useChatSessionListStore.getState().snapshot.listMode).toBe('project-first');
|
|
83
|
+
expect(useChatSessionListStore.getState().snapshot.selectedSessionKey).toBe('session-1');
|
|
84
|
+
});
|
|
54
85
|
});
|
|
@@ -3,11 +3,13 @@ import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
|
3
3
|
import type { ChatUiManager } from '@/components/chat/managers/chat-ui.manager';
|
|
4
4
|
import type { SetStateAction } from 'react';
|
|
5
5
|
import type { ChatStreamActionsManager } from '@/components/chat/managers/chat-stream-actions.manager';
|
|
6
|
+
import { normalizeSessionProjectRootValue } from '@/lib/session-project/session-project.utils';
|
|
6
7
|
|
|
7
8
|
export class ChatSessionListManager {
|
|
8
9
|
constructor(
|
|
9
10
|
private uiManager: ChatUiManager,
|
|
10
|
-
private streamActionsManager: ChatStreamActionsManager
|
|
11
|
+
private streamActionsManager: ChatStreamActionsManager,
|
|
12
|
+
private getDraftSessionId: () => string = () => ''
|
|
11
13
|
) {}
|
|
12
14
|
|
|
13
15
|
private resolveUpdateValue = <T>(prev: T, next: SetStateAction<T>): T => {
|
|
@@ -35,7 +37,16 @@ export class ChatSessionListManager {
|
|
|
35
37
|
useChatSessionListStore.getState().setSnapshot({ selectedSessionKey: value });
|
|
36
38
|
};
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
setListMode = (next: SetStateAction<'time-first' | 'project-first'>) => {
|
|
41
|
+
const prev = useChatSessionListStore.getState().snapshot.listMode;
|
|
42
|
+
const value = this.resolveUpdateValue(prev, next);
|
|
43
|
+
if (value === prev) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
useChatSessionListStore.getState().setSnapshot({ listMode: value });
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
createSession = (sessionType?: string, projectRoot?: string | null) => {
|
|
39
50
|
const { snapshot } = useChatInputStore.getState();
|
|
40
51
|
const { defaultSessionType: configuredDefaultSessionType } = snapshot;
|
|
41
52
|
const defaultSessionType = configuredDefaultSessionType || 'native';
|
|
@@ -43,11 +54,13 @@ export class ChatSessionListManager {
|
|
|
43
54
|
typeof sessionType === 'string' && sessionType.trim().length > 0
|
|
44
55
|
? sessionType.trim()
|
|
45
56
|
: defaultSessionType;
|
|
57
|
+
const normalizedProjectRoot = normalizeSessionProjectRootValue(projectRoot);
|
|
58
|
+
const draftSessionId = normalizedProjectRoot ? this.getDraftSessionId() : null;
|
|
46
59
|
this.streamActionsManager.resetStreamState();
|
|
47
60
|
useChatInputStore.getState().setSnapshot({
|
|
48
61
|
pendingSessionType: nextSessionType,
|
|
49
|
-
pendingProjectRoot:
|
|
50
|
-
pendingProjectRootSessionKey:
|
|
62
|
+
pendingProjectRoot: normalizedProjectRoot,
|
|
63
|
+
pendingProjectRootSessionKey: draftSessionId
|
|
51
64
|
});
|
|
52
65
|
this.uiManager.goToChatRoot();
|
|
53
66
|
};
|
|
@@ -7,7 +7,11 @@ import { NcpChatThreadManager } from '@/components/chat/ncp/ncp-chat-thread.mana
|
|
|
7
7
|
export class NcpChatPresenter {
|
|
8
8
|
chatUiManager = new ChatUiManager();
|
|
9
9
|
chatStreamActionsManager = new ChatStreamActionsManager();
|
|
10
|
-
chatSessionListManager = new ChatSessionListManager(
|
|
10
|
+
chatSessionListManager = new ChatSessionListManager(
|
|
11
|
+
this.chatUiManager,
|
|
12
|
+
this.chatStreamActionsManager,
|
|
13
|
+
() => this.getDraftSessionId()
|
|
14
|
+
);
|
|
11
15
|
chatInputManager = new NcpChatInputManager(
|
|
12
16
|
this.chatUiManager,
|
|
13
17
|
this.chatStreamActionsManager,
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
export type ChatSessionListMode = 'time-first' | 'project-first';
|
|
4
|
+
|
|
2
5
|
export type ChatSessionListSnapshot = {
|
|
3
6
|
selectedSessionKey: string | null;
|
|
4
7
|
selectedAgentId: string;
|
|
5
8
|
query: string;
|
|
9
|
+
listMode: ChatSessionListMode;
|
|
6
10
|
};
|
|
7
11
|
|
|
8
12
|
type ChatSessionListStore = {
|
|
@@ -13,7 +17,8 @@ type ChatSessionListStore = {
|
|
|
13
17
|
const initialSnapshot: ChatSessionListSnapshot = {
|
|
14
18
|
selectedSessionKey: null,
|
|
15
19
|
selectedAgentId: 'main',
|
|
16
|
-
query: ''
|
|
20
|
+
query: '',
|
|
21
|
+
listMode: 'time-first'
|
|
17
22
|
};
|
|
18
23
|
|
|
19
24
|
export const useChatSessionListStore = create<ChatSessionListStore>((set) => ({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import type { Dispatch, SetStateAction } from 'react';
|
|
3
|
-
import type { ChatSessionTypeOptionView, SessionEntryView } from '@/api/types';
|
|
3
|
+
import type { AgentProfileView, ChatSessionTypeOptionView, SessionEntryView } from '@/api/types';
|
|
4
4
|
import { t } from '@/lib/i18n';
|
|
5
5
|
|
|
6
6
|
export const DEFAULT_SESSION_TYPE = 'native';
|
|
@@ -39,6 +39,14 @@ export function normalizeSessionType(value: unknown): string {
|
|
|
39
39
|
return normalized || DEFAULT_SESSION_TYPE;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export function resolveAgentRuntimeSessionType(
|
|
43
|
+
agent: Pick<AgentProfileView, 'runtime' | 'engine'> | null | undefined,
|
|
44
|
+
fallbackSessionType: string = DEFAULT_SESSION_TYPE
|
|
45
|
+
): string {
|
|
46
|
+
const runtime = agent?.runtime?.trim() || agent?.engine?.trim() || fallbackSessionType;
|
|
47
|
+
return normalizeSessionType(runtime);
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
export function resolveSessionTypeLabel(sessionType: string, fallbackLabel?: string): string {
|
|
43
51
|
if (sessionType === 'native') {
|
|
44
52
|
return t('chatSessionTypeNative');
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./MarketplacePage-DE0QjYVv.js";export{e as MarketplacePage};
|