@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.
Files changed (29) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/assets/{ChannelsList-uKmkpD25.js → ChannelsList-DZWam3Ob.js} +1 -1
  3. package/dist/assets/{ChatPage-CslhBPfT.js → ChatPage-YBL7iJ1X.js} +27 -27
  4. package/dist/assets/{MarketplacePage-DE0QjYVv.js → MarketplacePage-2tWWgwAb.js} +1 -1
  5. package/dist/assets/MarketplacePage-BorWJftJ.js +1 -0
  6. package/dist/assets/{McpMarketplacePage-CeLvv1xy.js → McpMarketplacePage-N-fB4HID.js} +1 -1
  7. package/dist/assets/{ModelConfig-D1JtGtQv.js → ModelConfig-DvsBTUiE.js} +1 -1
  8. package/dist/assets/{ProviderScopedModelInput-SAJH6nkC.js → ProviderScopedModelInput-D9woCARc.js} +1 -1
  9. package/dist/assets/{ProvidersList-1rKi3aQT.js → ProvidersList-D-qPGgC4.js} +1 -1
  10. package/dist/assets/{RemoteAccessPage-bIAKxDky.js → RemoteAccessPage-COnjm8_x.js} +1 -1
  11. package/dist/assets/{RuntimeConfig-BTk9319O.js → RuntimeConfig-BHpqcaHm.js} +1 -1
  12. package/dist/assets/{SearchConfig-EjeszXbv.js → SearchConfig-DIT6M65Q.js} +1 -1
  13. package/dist/assets/{SecretsConfig-cnAXvREZ.js → SecretsConfig-Cefg1LFJ.js} +1 -1
  14. package/dist/assets/{SessionsConfig-BIXiDaK2.js → SessionsConfig-BZnmVTIu.js} +1 -1
  15. package/dist/assets/{index-8XNPYwJu.js → index-DHmCjcxq.js} +2 -2
  16. package/dist/assets/{security-config-CGazBahs.js → security-config-DEgOD4VX.js} +1 -1
  17. package/dist/assets/{useConfirmDialog-D6HxybcM.js → useConfirmDialog-CuQqiPx7.js} +1 -1
  18. package/dist/index.html +1 -1
  19. package/package.json +3 -3
  20. package/src/components/agents/AgentsPage.test.tsx +37 -1
  21. package/src/components/agents/AgentsPage.tsx +10 -3
  22. package/src/components/chat/ChatConversationPanel.test.tsx +69 -3
  23. package/src/components/chat/ChatConversationPanel.tsx +24 -3
  24. package/src/components/chat/managers/chat-session-list.manager.test.ts +34 -3
  25. package/src/components/chat/managers/chat-session-list.manager.ts +17 -4
  26. package/src/components/chat/ncp/ncp-chat.presenter.ts +5 -1
  27. package/src/components/chat/stores/chat-session-list.store.ts +6 -1
  28. package/src/components/chat/useChatSessionTypeState.ts +9 -1
  29. 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={presenter.chatThreadManager.createSession}
196
- agents={snapshot.availableAgents ?? []}
216
+ onCreateSession={createDraftSessionForAgent}
217
+ agents={availableAgents}
197
218
  selectedAgentId={snapshot.agentId ?? "main"}
198
- onSelectAgent={presenter.chatSessionListManager.setSelectedAgentId}
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
- createSession = (sessionType?: string) => {
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: null,
50
- pendingProjectRootSessionKey: null
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(this.chatUiManager, this.chatStreamActionsManager);
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};