@nextclaw/ui 0.11.9 → 0.11.11

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 (56) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/assets/{ChannelsList-XGMfinnc.js → ChannelsList-c6t0mn89.js} +3 -3
  3. package/dist/assets/ChatPage-2A8MjFld.js +37 -0
  4. package/dist/assets/{DocBrowser-DTRCNsSM.js → DocBrowser-DNArT9C7.js} +1 -1
  5. package/dist/assets/{LogoBadge-CPMOwWdA.js → LogoBadge-Bg6qKIGM.js} +1 -1
  6. package/dist/assets/{MarketplacePage-De2qZ9C0.js → MarketplacePage-Bdg8GqQ6.js} +1 -1
  7. package/dist/assets/{McpMarketplacePage-cjVKSQ2f.js → McpMarketplacePage-CU6gr58O.js} +2 -2
  8. package/dist/assets/{ModelConfig-CMn3-VZk.js → ModelConfig-CHlpmjUg.js} +1 -1
  9. package/dist/assets/{ProvidersList-CArDOswN.js → ProvidersList-CwTZF2yz.js} +1 -1
  10. package/dist/assets/RemoteAccessPage-bBI52qCV.js +1 -0
  11. package/dist/assets/RuntimeConfig-BvifZdub.js +1 -0
  12. package/dist/assets/{SearchConfig-a38m8Ynx.js → SearchConfig-COmMqF50.js} +1 -1
  13. package/dist/assets/{SecretsConfig-B6mf4JY9.js → SecretsConfig-u9OrM8fR.js} +2 -2
  14. package/dist/assets/{SessionsConfig-B_WQ1lVd.js → SessionsConfig-Cu8ou527.js} +2 -2
  15. package/dist/assets/chat-session-display-cb3oTJlV.js +1 -0
  16. package/dist/assets/{index-D-wEIgPn.js → index-BRfdTpro.js} +4 -4
  17. package/dist/assets/{label-usOOP7mv.js → label-DkFojDz9.js} +1 -1
  18. package/dist/assets/{page-layout-CuIf20mx.js → page-layout-DJ1cEM0C.js} +1 -1
  19. package/dist/assets/{popover-CTtTCP5d.js → popover-OLgPYzWf.js} +1 -1
  20. package/dist/assets/security-config-C1kqOE-O.js +1 -0
  21. package/dist/assets/{skeleton-BNUaFYE7.js → skeleton-CZp_aCj4.js} +1 -1
  22. package/dist/assets/{status-dot-BeHTBy9k.js → status-dot-Cf8rkmc5.js} +1 -1
  23. package/dist/assets/{switch-CtNnWZpa.js → switch-CF529ZId.js} +1 -1
  24. package/dist/assets/{tabs-custom-Dz_4tV62.js → tabs-custom-BweiG3H2.js} +1 -1
  25. package/dist/assets/{useConfirmDialog-C_n_JIEq.js → useConfirmDialog-_kYcW1mi.js} +1 -1
  26. package/dist/index.html +1 -1
  27. package/package.json +5 -5
  28. package/src/api/ncp-session-query-cache.test.ts +89 -0
  29. package/src/api/ncp-session-query-cache.ts +85 -0
  30. package/src/api/types.ts +2 -0
  31. package/src/components/chat/ChatConversationPanel.test.tsx +1 -1
  32. package/src/components/chat/ChatConversationPanel.tsx +6 -6
  33. package/src/components/chat/ChatSidebar.test.tsx +87 -92
  34. package/src/components/chat/ChatSidebar.tsx +21 -36
  35. package/src/components/chat/chat-session-label.service.ts +3 -3
  36. package/src/components/chat/containers/chat-message-list.container.test.tsx +53 -8
  37. package/src/components/chat/containers/chat-message-list.container.tsx +15 -14
  38. package/src/components/chat/managers/chat-session-list.manager.ts +0 -18
  39. package/src/components/chat/ncp/NcpChatPage.tsx +4 -52
  40. package/src/components/chat/ncp/ncp-chat-thread.manager.ts +4 -18
  41. package/src/components/chat/ncp/ncp-chat.presenter.ts +0 -2
  42. package/src/components/chat/ncp/ncp-session-adapter.test.ts +0 -23
  43. package/src/components/chat/ncp/ncp-session-adapter.ts +0 -19
  44. package/src/components/chat/ncp/use-ncp-session-list-view.ts +42 -0
  45. package/src/components/chat/presenter/chat-presenter-context.tsx +0 -3
  46. package/src/components/chat/stores/chat-session-list.store.ts +1 -7
  47. package/src/components/chat/stores/chat-thread.store.ts +3 -3
  48. package/src/hooks/use-realtime-query-bridge.ts +14 -19
  49. package/src/hooks/useConfig.ts +10 -11
  50. package/dist/assets/ChatPage-DYTcCRPp.js +0 -37
  51. package/dist/assets/RemoteAccessPage-C0I4tHey.js +0 -1
  52. package/dist/assets/RuntimeConfig-B4o6uJq9.js +0 -1
  53. package/dist/assets/ncp-session-adapter-DSacECph.js +0 -1
  54. package/dist/assets/security-config-Bxrrv8Ac.js +0 -1
  55. package/src/components/chat/managers/chat-run-status.manager.ts +0 -32
  56. package/src/components/chat/stores/chat-run-status.store.ts +0 -30
@@ -0,0 +1,42 @@
1
+ import { useMemo } from 'react';
2
+ import type { SessionEntryView } from '@/api/types';
3
+ import { adaptNcpSessionSummaries } from '@/components/chat/ncp/ncp-session-adapter';
4
+ import { useChatSessionListStore } from '@/components/chat/stores/chat-session-list.store';
5
+ import { useNcpSessions } from '@/hooks/useConfig';
6
+ import type { SessionRunStatus } from '@/lib/session-run-status';
7
+
8
+ export type NcpSessionListItemView = {
9
+ session: SessionEntryView;
10
+ runStatus?: SessionRunStatus;
11
+ };
12
+
13
+ function filterSessionsByQuery(sessions: readonly SessionEntryView[], query: string): SessionEntryView[] {
14
+ const normalizedQuery = query.trim().toLowerCase();
15
+ if (!normalizedQuery) {
16
+ return [...sessions];
17
+ }
18
+
19
+ return sessions.filter((session) => session.key.toLowerCase().includes(normalizedQuery));
20
+ }
21
+
22
+ export function useNcpSessionListView(params: { limit?: number } = {}) {
23
+ const query = useChatSessionListStore((state) => state.snapshot.query);
24
+ const sessionsQuery = useNcpSessions({ limit: params.limit ?? 200 });
25
+
26
+ const items = useMemo<NcpSessionListItemView[]>(() => {
27
+ const summaries = sessionsQuery.data?.sessions ?? [];
28
+ const sessions = adaptNcpSessionSummaries(summaries);
29
+ const filteredSessions = filterSessionsByQuery(sessions, query);
30
+ const summaryBySessionId = new Map(summaries.map((summary) => [summary.sessionId, summary]));
31
+
32
+ return filteredSessions.map((session) => ({
33
+ session,
34
+ runStatus: summaryBySessionId.get(session.key)?.status === 'running' ? 'running' : undefined
35
+ }));
36
+ }, [query, sessionsQuery.data?.sessions]);
37
+
38
+ return {
39
+ isLoading: sessionsQuery.isLoading,
40
+ items
41
+ };
42
+ }
@@ -3,7 +3,6 @@ import type { NcpDraftAttachment } from '@nextclaw/ncp-react';
3
3
  import { createContext, useContext } from 'react';
4
4
  import type { ReactNode } from 'react';
5
5
  import type { SetStateAction } from 'react';
6
- import type { ChatRunStatusManager } from '@/components/chat/managers/chat-run-status.manager';
7
6
  import type { ChatSessionListManager } from '@/components/chat/managers/chat-session-list.manager';
8
7
  import type { ChatStreamActionsManager } from '@/components/chat/managers/chat-stream-actions.manager';
9
8
  import type { ChatUiManager } from '@/components/chat/managers/chat-ui.manager';
@@ -33,7 +32,6 @@ export type ChatInputManagerLike = {
33
32
  };
34
33
 
35
34
  export type ChatThreadManagerLike = {
36
- bindActions: (patch: { refetchSessions?: () => Promise<unknown> }) => void;
37
35
  syncSnapshot: (patch: Partial<ChatThreadSnapshot>) => void;
38
36
  deleteSession: () => void;
39
37
  createSession: () => void;
@@ -45,7 +43,6 @@ export type ChatPresenterLike = {
45
43
  chatStreamActionsManager: ChatStreamActionsManager;
46
44
  chatInputManager: ChatInputManagerLike;
47
45
  chatSessionListManager: ChatSessionListManager;
48
- chatRunStatusManager: ChatRunStatusManager;
49
46
  chatThreadManager: ChatThreadManagerLike;
50
47
  };
51
48
 
@@ -1,12 +1,8 @@
1
1
  import { create } from 'zustand';
2
- import type { SessionEntryView } from '@/api/types';
3
-
4
2
  export type ChatSessionListSnapshot = {
5
- sessions: SessionEntryView[];
6
3
  selectedSessionKey: string | null;
7
4
  selectedAgentId: string;
8
5
  query: string;
9
- isLoading: boolean;
10
6
  };
11
7
 
12
8
  type ChatSessionListStore = {
@@ -15,11 +11,9 @@ type ChatSessionListStore = {
15
11
  };
16
12
 
17
13
  const initialSnapshot: ChatSessionListSnapshot = {
18
- sessions: [],
19
14
  selectedSessionKey: null,
20
15
  selectedAgentId: 'main',
21
- query: '',
22
- isLoading: false
16
+ query: ''
23
17
  };
24
18
 
25
19
  export const useChatSessionListStore = create<ChatSessionListStore>((set) => ({
@@ -1,6 +1,6 @@
1
1
  import { create } from 'zustand';
2
2
  import type { MutableRefObject } from 'react';
3
- import type { UiMessage } from '@nextclaw/agent-chat';
3
+ import type { NcpMessage } from '@nextclaw/ncp';
4
4
  import type { ChatModelOption } from '@/components/chat/chat-input.types';
5
5
 
6
6
  export type ChatThreadSnapshot = {
@@ -15,7 +15,7 @@ export type ChatThreadSnapshot = {
15
15
  isDeletePending: boolean;
16
16
  threadRef: MutableRefObject<HTMLDivElement | null> | null;
17
17
  isHistoryLoading: boolean;
18
- uiMessages: UiMessage[];
18
+ messages: readonly NcpMessage[];
19
19
  isSending: boolean;
20
20
  isAwaitingAssistantOutput: boolean;
21
21
  };
@@ -37,7 +37,7 @@ const initialSnapshot: ChatThreadSnapshot = {
37
37
  isDeletePending: false,
38
38
  threadRef: null,
39
39
  isHistoryLoading: false,
40
- uiMessages: [],
40
+ messages: [],
41
41
  isSending: false,
42
42
  isAwaitingAssistantOutput: false
43
43
  };
@@ -1,4 +1,5 @@
1
- import { useEffect } from 'react';
1
+ import { useEffect, useRef } from 'react';
2
+ import { applyNcpSessionRealtimeEvent } from '@/api/ncp-session-query-cache';
2
3
  import { appClient } from '@/transport';
3
4
  import { useUiStore } from '@/stores/ui.store';
4
5
  import type { QueryClient } from '@tanstack/react-query';
@@ -30,39 +31,30 @@ function invalidateMarketplaceQueries(queryClient: QueryClient | undefined, conf
30
31
  }
31
32
  }
32
33
 
33
- function invalidateSessionQueries(queryClient: QueryClient | undefined, sessionKey?: string): void {
34
- if (!queryClient) {
35
- return;
36
- }
37
- queryClient.invalidateQueries({ queryKey: ['ncp-sessions'] });
38
- if (sessionKey && sessionKey.trim().length > 0) {
39
- queryClient.invalidateQueries({ queryKey: ['ncp-session-messages', sessionKey.trim()] });
40
- return;
41
- }
42
- queryClient.invalidateQueries({ queryKey: ['ncp-session-messages'] });
43
- }
44
-
45
34
  function handleConfigUpdatedEvent(queryClient: QueryClient | undefined, path: string): void {
46
35
  if (queryClient && shouldInvalidateConfigQuery(path)) {
47
36
  queryClient.invalidateQueries({ queryKey: ['config'] });
48
37
  }
49
- if (path.startsWith('session')) {
50
- invalidateSessionQueries(queryClient);
51
- }
52
38
  invalidateMarketplaceQueries(queryClient, path);
53
39
  }
54
40
 
55
41
  function handleRealtimeEvent(
56
42
  queryClient: QueryClient | undefined,
57
43
  setConnectionStatus: SetConnectionStatus,
44
+ shouldResyncSessionsRef: { current: boolean },
58
45
  event: Parameters<Parameters<typeof appClient.subscribe>[0]>[0]
59
46
  ): void {
60
47
  if (event.type === 'connection.open') {
61
48
  setConnectionStatus('connected');
49
+ if (shouldResyncSessionsRef.current) {
50
+ shouldResyncSessionsRef.current = false;
51
+ queryClient?.invalidateQueries({ queryKey: ['ncp-sessions'] });
52
+ }
62
53
  return;
63
54
  }
64
55
  if (event.type === 'connection.close' || event.type === 'connection.error') {
65
56
  setConnectionStatus('disconnected');
57
+ shouldResyncSessionsRef.current = true;
66
58
  return;
67
59
  }
68
60
  if (event.type === 'config.updated') {
@@ -70,8 +62,8 @@ function handleRealtimeEvent(
70
62
  handleConfigUpdatedEvent(queryClient, configPath);
71
63
  return;
72
64
  }
73
- if (event.type === 'session.updated') {
74
- invalidateSessionQueries(queryClient, event.payload.sessionKey);
65
+ if (event.type === 'session.summary.upsert' || event.type === 'session.summary.delete') {
66
+ applyNcpSessionRealtimeEvent(queryClient, event);
75
67
  return;
76
68
  }
77
69
  if (event.type === 'error') {
@@ -81,10 +73,13 @@ function handleRealtimeEvent(
81
73
 
82
74
  export function useRealtimeQueryBridge(queryClient?: QueryClient) {
83
75
  const { setConnectionStatus } = useUiStore();
76
+ const shouldResyncSessionsRef = useRef(false);
84
77
 
85
78
  useEffect(() => {
86
79
  setConnectionStatus('connecting');
87
80
 
88
- return appClient.subscribe((event) => handleRealtimeEvent(queryClient, setConnectionStatus, event));
81
+ return appClient.subscribe((event) =>
82
+ handleRealtimeEvent(queryClient, setConnectionStatus, shouldResyncSessionsRef, event)
83
+ );
89
84
  }, [queryClient, setConnectionStatus]);
90
85
  }
@@ -1,4 +1,8 @@
1
1
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
2
+ import {
3
+ deleteNcpSessionSummaryInQueryClient,
4
+ upsertNcpSessionSummaryInQueryClient
5
+ } from '@/api/ncp-session-query-cache';
2
6
  import {
3
7
  fetchAppMeta,
4
8
  fetchConfig,
@@ -230,11 +234,7 @@ export function useNcpSessions(params?: { limit?: number }) {
230
234
  queryKey: ['ncp-sessions', params?.limit ?? null],
231
235
  queryFn: () => fetchNcpSessions(params),
232
236
  staleTime: 5_000,
233
- retry: false,
234
- refetchInterval: (query) => {
235
- const hasRunningSession = Boolean(query.state.data?.sessions.some((session) => session.status === 'running'));
236
- return hasRunningSession ? 800 : false;
237
- }
237
+ retry: false
238
238
  });
239
239
  }
240
240
 
@@ -253,9 +253,9 @@ export function useDeleteNcpSession() {
253
253
 
254
254
  return useMutation({
255
255
  mutationFn: ({ sessionId }: { sessionId: string }) => deleteNcpSession(sessionId),
256
- onSuccess: () => {
257
- queryClient.invalidateQueries({ queryKey: ['ncp-sessions'] });
258
- queryClient.invalidateQueries({ queryKey: ['ncp-session-messages'] });
256
+ onSuccess: (_data, variables) => {
257
+ deleteNcpSessionSummaryInQueryClient(queryClient, variables.sessionId);
258
+ queryClient.removeQueries({ queryKey: ['ncp-session-messages', variables.sessionId] });
259
259
  toast.success(t('configSavedApplied'));
260
260
  },
261
261
  onError: (error: Error) => {
@@ -270,9 +270,8 @@ export function useUpdateNcpSession() {
270
270
  return useMutation({
271
271
  mutationFn: ({ sessionId, data }: { sessionId: string; data: Parameters<typeof updateNcpSession>[1] }) =>
272
272
  updateNcpSession(sessionId, data),
273
- onSuccess: (_data, variables) => {
274
- queryClient.invalidateQueries({ queryKey: ['ncp-sessions'] });
275
- queryClient.invalidateQueries({ queryKey: ['ncp-session-messages', variables.sessionId] });
273
+ onSuccess: (data) => {
274
+ upsertNcpSessionSummaryInQueryClient(queryClient, data);
276
275
  toast.success(t('configSavedApplied'));
277
276
  },
278
277
  onError: (error: Error) => {