@nextclaw/ui 0.7.0 → 0.9.0

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 (80) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/assets/ChannelsList-C7F_As4r.js +1 -0
  3. package/dist/assets/ChatPage-Oo7-OUsx.js +37 -0
  4. package/dist/assets/{DocBrowser-B9ws5JL7.js → DocBrowser-Dsd8Dlq8.js} +1 -1
  5. package/dist/assets/{LogoBadge-DvGAzkZ3.js → LogoBadge-2ChEc_oz.js} +1 -1
  6. package/dist/assets/MarketplacePage-BXck6-X3.js +49 -0
  7. package/dist/assets/{ModelConfig-BL_HsOsm.js → ModelConfig-CgHRSD0b.js} +1 -1
  8. package/dist/assets/ProvidersList-PPfZucvS.js +1 -0
  9. package/dist/assets/RuntimeConfig-ClLEKNTN.js +1 -0
  10. package/dist/assets/{SearchConfig-BhaI0fUf.js → SearchConfig-CuXVCbrf.js} +1 -1
  11. package/dist/assets/{SecretsConfig-CFoimOh9.js → SecretsConfig-udJz6Ake.js} +2 -2
  12. package/dist/assets/SessionsConfig-C1XnFfiC.js +2 -0
  13. package/dist/assets/{session-run-status-TkIuGbVw.js → chat-message-BETwXLD4.js} +3 -3
  14. package/dist/assets/{index-uMsNsQX6.js → index-COJdlL0e.js} +1 -1
  15. package/dist/assets/index-CsvP4CER.js +8 -0
  16. package/dist/assets/index-D-bXl7qL.css +1 -0
  17. package/dist/assets/{label-D8ly4a2P.js → label-BGL-ztxh.js} +1 -1
  18. package/dist/assets/{page-layout-BSYfvwbp.js → page-layout-aw88k7tG.js} +1 -1
  19. package/dist/assets/popover-DyEvzhmV.js +1 -0
  20. package/dist/assets/security-config-BuPAQn82.js +1 -0
  21. package/dist/assets/skeleton-drzO_tdU.js +1 -0
  22. package/dist/assets/{switch-Ce_g9lpN.js → switch-BK8jIzto.js} +1 -1
  23. package/dist/assets/{tabs-custom-Cf5azvT5.js → tabs-custom-Da3cEOji.js} +1 -1
  24. package/dist/assets/{useConfirmDialog-A8Ek8Wu7.js → useConfirmDialog-z0CE92iS.js} +2 -2
  25. package/dist/assets/{vendor-B7ozqnFC.js → vendor-CkJHmX1g.js} +65 -70
  26. package/dist/index.html +3 -3
  27. package/package.json +5 -2
  28. package/src/api/config.ts +9 -0
  29. package/src/api/ncp-session.ts +50 -0
  30. package/src/api/types.ts +20 -0
  31. package/src/components/chat/ChatConversationPanel.test.tsx +65 -0
  32. package/src/components/chat/ChatConversationPanel.tsx +21 -12
  33. package/src/components/chat/ChatPage.tsx +10 -324
  34. package/src/components/chat/ChatSidebar.test.tsx +203 -0
  35. package/src/components/chat/ChatSidebar.tsx +97 -7
  36. package/src/components/chat/adapters/chat-message.adapter.test.ts +132 -81
  37. package/src/components/chat/adapters/chat-message.adapter.ts +27 -9
  38. package/src/components/chat/chat-chain.test.ts +22 -0
  39. package/src/components/chat/chat-chain.ts +23 -0
  40. package/src/components/chat/chat-page-data.ts +30 -1
  41. package/src/components/chat/chat-page-runtime.test.ts +181 -0
  42. package/src/components/chat/chat-page-runtime.ts +101 -15
  43. package/src/components/chat/chat-page-shell.tsx +103 -0
  44. package/src/components/chat/chat-session-preference-sync.test.ts +62 -0
  45. package/src/components/chat/chat-session-preference-sync.ts +75 -0
  46. package/src/components/chat/containers/chat-input-bar.container.tsx +0 -22
  47. package/src/components/chat/containers/chat-message-list.container.tsx +34 -26
  48. package/src/components/chat/legacy/LegacyChatPage.tsx +252 -0
  49. package/src/components/chat/managers/chat-input.manager.ts +5 -0
  50. package/src/components/chat/managers/chat-session-list.manager.test.ts +39 -0
  51. package/src/components/chat/managers/chat-session-list.manager.ts +9 -3
  52. package/src/components/chat/ncp/NcpChatPage.tsx +381 -0
  53. package/src/components/chat/ncp/ncp-chat-input.manager.ts +179 -0
  54. package/src/components/chat/ncp/ncp-chat-page-data.ts +166 -0
  55. package/src/components/chat/ncp/ncp-chat-thread.manager.ts +89 -0
  56. package/src/components/chat/ncp/ncp-chat.presenter.ts +33 -0
  57. package/src/components/chat/ncp/ncp-session-adapter.test.ts +75 -0
  58. package/src/components/chat/ncp/ncp-session-adapter.ts +214 -0
  59. package/src/components/chat/presenter/chat-presenter-context.tsx +43 -4
  60. package/src/components/chat/stores/chat-thread.store.ts +2 -0
  61. package/src/components/chat/useChatSessionTypeState.test.tsx +58 -0
  62. package/src/components/chat/useChatSessionTypeState.ts +25 -8
  63. package/src/hooks/use-ncp-chat-session-types.ts +11 -0
  64. package/src/hooks/useConfig.ts +41 -1
  65. package/src/hooks/useMarketplace.ts +7 -4
  66. package/src/hooks/useWebSocket.ts +23 -2
  67. package/src/lib/i18n.ts +1 -1
  68. package/tailwind.config.js +8 -3
  69. package/tsconfig.json +4 -1
  70. package/dist/assets/ChannelsList-DF2U-LY1.js +0 -1
  71. package/dist/assets/ChatPage-BX39y0U5.js +0 -36
  72. package/dist/assets/MarketplacePage-DG5mHWJ8.js +0 -49
  73. package/dist/assets/ProvidersList-CH5z00YT.js +0 -1
  74. package/dist/assets/RuntimeConfig-BplBgkwo.js +0 -1
  75. package/dist/assets/SessionsConfig-BHTAYn9T.js +0 -2
  76. package/dist/assets/index-BLeJkJ0o.css +0 -1
  77. package/dist/assets/index-DK4TS5ev.js +0 -8
  78. package/dist/assets/index-X5J6Mm--.js +0 -1
  79. package/dist/assets/security-config-DlKEYHNN.js +0 -1
  80. package/dist/assets/skeleton-CWbsNx2h.js +0 -1
@@ -0,0 +1,58 @@
1
+ import { renderHook } from '@testing-library/react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import { resolveSessionTypeLabel, useChatSessionTypeState } from '@/components/chat/useChatSessionTypeState';
4
+
5
+ vi.mock('@/lib/i18n', () => ({
6
+ t: (key: string) => key
7
+ }));
8
+
9
+ describe('useChatSessionTypeState', () => {
10
+ it('formats non-native runtime labels generically when no explicit label is provided', () => {
11
+ expect(resolveSessionTypeLabel('workspace-agent')).toBe('Workspace Agent');
12
+ });
13
+
14
+ it('preserves an explicitly selected draft session type instead of resetting to the default', () => {
15
+ const setPendingSessionType = vi.fn();
16
+
17
+ const { result } = renderHook(() =>
18
+ useChatSessionTypeState({
19
+ selectedSession: null,
20
+ selectedSessionKey: null,
21
+ pendingSessionType: 'codex-sdk',
22
+ setPendingSessionType,
23
+ sessionTypesData: {
24
+ defaultType: 'native',
25
+ options: [
26
+ { value: 'native', label: 'Native' },
27
+ { value: 'codex-sdk', label: 'Codex' }
28
+ ]
29
+ }
30
+ })
31
+ );
32
+
33
+ expect(result.current.selectedSessionType).toBe('codex-sdk');
34
+ expect(setPendingSessionType).not.toHaveBeenCalled();
35
+ });
36
+
37
+ it('hydrates the draft session type from the runtime default when no explicit type exists', () => {
38
+ const setPendingSessionType = vi.fn();
39
+
40
+ renderHook(() =>
41
+ useChatSessionTypeState({
42
+ selectedSession: null,
43
+ selectedSessionKey: null,
44
+ pendingSessionType: '',
45
+ setPendingSessionType,
46
+ sessionTypesData: {
47
+ defaultType: 'codex-sdk',
48
+ options: [
49
+ { value: 'native', label: 'Native' },
50
+ { value: 'codex-sdk', label: 'Codex' }
51
+ ]
52
+ }
53
+ })
54
+ );
55
+
56
+ expect(setPendingSessionType).toHaveBeenCalledWith('codex-sdk');
57
+ });
58
+ });
@@ -1,4 +1,4 @@
1
- import { useEffect, useMemo } from 'react';
1
+ import { useEffect, useMemo, useRef } from 'react';
2
2
  import type { Dispatch, SetStateAction } from 'react';
3
3
  import type { SessionEntryView } from '@/api/types';
4
4
  import { t } from '@/lib/i18n';
@@ -33,13 +33,16 @@ export function resolveSessionTypeLabel(sessionType: string, fallbackLabel?: str
33
33
  if (sessionType === 'native') {
34
34
  return t('chatSessionTypeNative');
35
35
  }
36
- if (sessionType === 'codex-sdk') {
37
- return t('chatSessionTypeCodex');
36
+ const normalizedFallback = fallbackLabel?.trim();
37
+ if (normalizedFallback) {
38
+ return normalizedFallback;
38
39
  }
39
- if (sessionType === 'claude-agent-sdk') {
40
- return t('chatSessionTypeClaude');
41
- }
42
- return fallbackLabel?.trim() || sessionType;
40
+ return sessionType
41
+ .trim()
42
+ .split(/[-_]+/g)
43
+ .filter(Boolean)
44
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
45
+ .join(' ') || sessionType;
43
46
  }
44
47
 
45
48
  function buildSessionTypeOptions(
@@ -113,6 +116,7 @@ export function useChatSessionTypeState(params: UseChatSessionTypeStateParams):
113
116
  () => normalizeSessionType(sessionTypesData?.defaultType ?? DEFAULT_SESSION_TYPE),
114
117
  [sessionTypesData?.defaultType]
115
118
  );
119
+ const lastAutoPendingSessionTypeRef = useRef<string | null>(null);
116
120
  const selectedSessionType = useMemo(
117
121
  () => normalizeSessionType(selectedSession?.sessionType ?? pendingSessionType ?? defaultSessionType),
118
122
  [defaultSessionType, pendingSessionType, selectedSession?.sessionType]
@@ -122,8 +126,21 @@ export function useChatSessionTypeState(params: UseChatSessionTypeStateParams):
122
126
  if (selectedSessionKey) {
123
127
  return;
124
128
  }
129
+ const rawPending = typeof pendingSessionType === 'string' ? pendingSessionType.trim() : '';
130
+ const normalizedPending = normalizeSessionType(pendingSessionType);
131
+ const shouldFollowDefault =
132
+ rawPending.length === 0 ||
133
+ lastAutoPendingSessionTypeRef.current === normalizedPending ||
134
+ (lastAutoPendingSessionTypeRef.current === null && normalizedPending === DEFAULT_SESSION_TYPE);
135
+ if (!shouldFollowDefault) {
136
+ return;
137
+ }
138
+ lastAutoPendingSessionTypeRef.current = defaultSessionType;
139
+ if (normalizedPending === defaultSessionType) {
140
+ return;
141
+ }
125
142
  setPendingSessionType(defaultSessionType);
126
- }, [defaultSessionType, selectedSessionKey, setPendingSessionType]);
143
+ }, [defaultSessionType, pendingSessionType, selectedSessionKey, setPendingSessionType]);
127
144
 
128
145
  const canEditSessionType = !selectedSessionKey || Boolean(selectedSession?.sessionTypeMutable);
129
146
  const availableSessionTypeSet = useMemo(
@@ -0,0 +1,11 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { fetchNcpChatSessionTypes } from '@/api/config';
3
+
4
+ export function useNcpChatSessionTypes() {
5
+ return useQuery({
6
+ queryKey: ['ncp-session-types'],
7
+ queryFn: fetchNcpChatSessionTypes,
8
+ staleTime: 10_000,
9
+ retry: false
10
+ });
11
+ }
@@ -31,6 +31,7 @@ import {
31
31
  setCronJobEnabled,
32
32
  runCronJob
33
33
  } from '@/api/config';
34
+ import { deleteNcpSession, fetchNcpSessionMessages, fetchNcpSessions } from '@/api/ncp-session';
34
35
  import { toast } from 'sonner';
35
36
  import { t } from '@/lib/i18n';
36
37
 
@@ -252,6 +253,29 @@ export function useSessionHistory(key: string | null, limit = 200) {
252
253
  });
253
254
  }
254
255
 
256
+ export function useNcpSessions(params?: { limit?: number }) {
257
+ return useQuery({
258
+ queryKey: ['ncp-sessions', params?.limit ?? null],
259
+ queryFn: () => fetchNcpSessions(params),
260
+ staleTime: 5_000,
261
+ retry: false,
262
+ refetchInterval: (query) => {
263
+ const hasRunningSession = Boolean(query.state.data?.sessions.some((session) => session.status === 'running'));
264
+ return hasRunningSession ? 800 : false;
265
+ }
266
+ });
267
+ }
268
+
269
+ export function useNcpSessionMessages(sessionId: string | null, limit = 200) {
270
+ return useQuery({
271
+ queryKey: ['ncp-session-messages', sessionId, limit],
272
+ queryFn: () => fetchNcpSessionMessages(sessionId as string, limit),
273
+ enabled: Boolean(sessionId),
274
+ staleTime: 5_000,
275
+ retry: false
276
+ });
277
+ }
278
+
255
279
  export function useUpdateSession() {
256
280
  const queryClient = useQueryClient();
257
281
 
@@ -285,6 +309,22 @@ export function useDeleteSession() {
285
309
  });
286
310
  }
287
311
 
312
+ export function useDeleteNcpSession() {
313
+ const queryClient = useQueryClient();
314
+
315
+ return useMutation({
316
+ mutationFn: ({ sessionId }: { sessionId: string }) => deleteNcpSession(sessionId),
317
+ onSuccess: () => {
318
+ queryClient.invalidateQueries({ queryKey: ['ncp-sessions'] });
319
+ queryClient.invalidateQueries({ queryKey: ['ncp-session-messages'] });
320
+ toast.success(t('configSavedApplied'));
321
+ },
322
+ onError: (error: Error) => {
323
+ toast.error(t('configSaveFailed') + ': ' + error.message);
324
+ }
325
+ });
326
+ }
327
+
288
328
  export function useSendChatTurn() {
289
329
  return useMutation({
290
330
  mutationFn: ({ data }: { data: Parameters<typeof sendChatTurn>[0] }) =>
@@ -348,7 +388,7 @@ export function useChatRuns(params?: {
348
388
  if (params?.isLocallyRunning) {
349
389
  return 800;
350
390
  }
351
- const data = query.state.data;
391
+ const { data } = query.state;
352
392
  const hasActiveRuns = Array.isArray(data?.runs) && data.runs.length > 0;
353
393
  return hasActiveRuns ? 800 : false;
354
394
  },
@@ -61,8 +61,10 @@ export function useInstallMarketplaceItem() {
61
61
  mutationFn: (request: MarketplaceInstallRequest) => installMarketplaceItem(request),
62
62
  onSuccess: (result) => {
63
63
  queryClient.invalidateQueries({ queryKey: ['marketplace-installed', result.type] });
64
- queryClient.refetchQueries({ queryKey: ['marketplace-installed', result.type], type: 'active' });
65
- queryClient.refetchQueries({ queryKey: ['marketplace-items'], type: 'active' });
64
+ queryClient.invalidateQueries({ queryKey: ['marketplace-items'] });
65
+ if (result.type === 'plugin') {
66
+ queryClient.invalidateQueries({ queryKey: ['ncp-session-types'] });
67
+ }
66
68
  const fallback = result.type === 'plugin'
67
69
  ? t('marketplaceInstallSuccessPlugin')
68
70
  : t('marketplaceInstallSuccessSkill');
@@ -82,8 +84,9 @@ export function useManageMarketplaceItem() {
82
84
  onSuccess: (result) => {
83
85
  queryClient.invalidateQueries({ queryKey: ['marketplace-installed', result.type] });
84
86
  queryClient.invalidateQueries({ queryKey: ['marketplace-items'] });
85
- queryClient.refetchQueries({ queryKey: ['marketplace-installed', result.type], type: 'active' });
86
- queryClient.refetchQueries({ queryKey: ['marketplace-items'], type: 'active' });
87
+ if (result.type === 'plugin') {
88
+ queryClient.invalidateQueries({ queryKey: ['ncp-session-types'] });
89
+ }
87
90
  const fallback = result.action === 'enable'
88
91
  ? t('marketplaceEnableSuccess')
89
92
  : result.action === 'disable'
@@ -73,11 +73,25 @@ export function useWebSocket(queryClient?: QueryClient) {
73
73
  return;
74
74
  }
75
75
  queryClient.invalidateQueries({ queryKey: ['sessions'] });
76
+ queryClient.invalidateQueries({ queryKey: ['ncp-sessions'] });
76
77
  if (sessionKey && sessionKey.trim().length > 0) {
77
78
  queryClient.invalidateQueries({ queryKey: ['session-history', sessionKey.trim()] });
79
+ queryClient.invalidateQueries({ queryKey: ['ncp-session-messages', sessionKey.trim()] });
78
80
  return;
79
81
  }
80
82
  queryClient.invalidateQueries({ queryKey: ['session-history'] });
83
+ queryClient.invalidateQueries({ queryKey: ['ncp-session-messages'] });
84
+ };
85
+
86
+ const shouldInvalidateConfigQuery = (configPath: string) => {
87
+ const normalized = configPath.trim().toLowerCase();
88
+ if (!normalized) {
89
+ return true;
90
+ }
91
+ if (normalized.startsWith('plugins') || normalized.startsWith('skills')) {
92
+ return false;
93
+ }
94
+ return true;
81
95
  };
82
96
 
83
97
  setConnectionStatus('connecting');
@@ -98,13 +112,20 @@ export function useWebSocket(queryClient?: QueryClient) {
98
112
  });
99
113
 
100
114
  client.on('config.updated', (event) => {
115
+ const payload = event.payload as { path?: unknown } | undefined;
116
+ const configPath = typeof payload?.path === 'string' ? payload.path : '';
101
117
  // Trigger refetch of config
102
- if (queryClient) {
118
+ if (queryClient && shouldInvalidateConfigQuery(configPath)) {
103
119
  queryClient.invalidateQueries({ queryKey: ['config'] });
104
120
  }
105
- if (event.type === 'config.updated' && event.payload.path.startsWith('session')) {
121
+ if (configPath.startsWith('session')) {
106
122
  invalidateSessionQueries();
107
123
  }
124
+ if (configPath.startsWith('plugins')) {
125
+ queryClient?.invalidateQueries({ queryKey: ['ncp-session-types'] });
126
+ queryClient?.invalidateQueries({ queryKey: ['marketplace-installed', 'plugin'] });
127
+ queryClient?.invalidateQueries({ queryKey: ['marketplace-items'] });
128
+ }
108
129
  });
109
130
 
110
131
  client.on('run.updated', (event) => {
package/src/lib/i18n.ts CHANGED
@@ -670,7 +670,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
670
670
  chatToolWorkflowDetails: { zh: '展开查看参数和结果', en: 'Expand to view params and results' },
671
671
  chatToolOutput: { zh: '查看输出', en: 'View Output' },
672
672
  chatToolNoOutput: { zh: '无输出(执行完成)', en: 'No output (completed)' },
673
- chatReasoning: { zh: '查看推理内容', en: 'Show reasoning' },
673
+ chatReasoning: { zh: '推理过程', en: 'Reasoning' },
674
674
  chatUnknownPart: { zh: '未知消息片段', en: 'Unknown message part' },
675
675
  chatCodeCopy: { zh: '复制代码', en: 'Copy' },
676
676
  chatCodeCopied: { zh: '已复制', en: 'Copied' },
@@ -1,7 +1,13 @@
1
+ import tailwindcssAnimate from 'tailwindcss-animate';
2
+
1
3
  /** @type {import('tailwindcss').Config} */
2
4
  export default {
3
5
  darkMode: ['class'],
4
- content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
6
+ content: [
7
+ './index.html',
8
+ './src/**/*.{js,ts,jsx,tsx}',
9
+ '../nextclaw-agent-chat-ui/src/**/*.{js,ts,jsx,tsx}',
10
+ ],
5
11
  theme: {
6
12
  extend: {
7
13
  colors: {
@@ -146,6 +152,5 @@ export default {
146
152
  },
147
153
  },
148
154
  },
149
- // eslint-disable-next-line @typescript-eslint/no-require-imports
150
- plugins: [require('tailwindcss-animate')],
155
+ plugins: [tailwindcssAnimate],
151
156
  };
package/tsconfig.json CHANGED
@@ -13,7 +13,10 @@
13
13
  "paths": {
14
14
  "@/*": ["./src/*"],
15
15
  "@nextclaw/agent-chat": ["../nextclaw-agent-chat/src/index.ts"],
16
- "@nextclaw/agent-chat-ui": ["../nextclaw-agent-chat-ui/src/index.ts"]
16
+ "@nextclaw/agent-chat-ui": ["../nextclaw-agent-chat-ui/src/index.ts"],
17
+ "@nextclaw/ncp": ["../ncp-packages/nextclaw-ncp/src/index.ts"],
18
+ "@nextclaw/ncp-http-agent-client": ["../ncp-packages/nextclaw-ncp-http-agent-client/src/index.ts"],
19
+ "@nextclaw/ncp-react": ["../ncp-packages/nextclaw-ncp-react/src/index.ts"]
17
20
  }
18
21
  },
19
22
  "include": ["src"]
@@ -1 +0,0 @@
1
- import{r as v,j as a,a5 as Z,F as ee,e as T,K as ae,ai as te,aS as se,aT as ne,aU as le,z as re,s as oe,a9 as ce,v as ie}from"./vendor-B7ozqnFC.js";import{t as e,c as I,U as me,u as q,a as $,b as H,W as pe,X as de,I as D,S as be,e as ue,f as xe,g as ye,h as ge,B as E}from"./index-DK4TS5ev.js";import{L as he}from"./label-D8ly4a2P.js";import{S as fe}from"./switch-Ce_g9lpN.js";import{L as K,S as J}from"./LogoBadge-DvGAzkZ3.js";import{h as U}from"./config-hints-CApS3K_7.js";import{c as we,b as ve,a as je,C as ke}from"./config-layout-BHnOoweL.js";import{T as Se}from"./tabs-custom-Cf5azvT5.js";import{P as Ce,a as Ne}from"./page-layout-BSYfvwbp.js";function Pe({value:t,onChange:m,className:i,placeholder:r=""}){const[o,u]=v.useState(""),d=x=>{x.key==="Enter"&&o.trim()?(x.preventDefault(),m([...t,o.trim()]),u("")):x.key==="Backspace"&&!o&&t.length>0&&m(t.slice(0,-1))},g=x=>{m(t.filter((j,h)=>h!==x))};return a.jsxs("div",{className:I("flex flex-wrap gap-2 p-2 border rounded-md min-h-[42px]",i),children:[t.map((x,j)=>a.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1 bg-primary text-primary-foreground rounded text-sm",children:[x,a.jsx("button",{type:"button",onClick:()=>g(j),className:"hover:text-red-300 transition-colors",children:a.jsx(Z,{className:"h-3 w-3"})})]},j)),a.jsx("input",{type:"text",value:o,onChange:x=>u(x.target.value),onKeyDown:d,className:"flex-1 outline-none min-w-[100px] bg-transparent text-sm",placeholder:r||e("enterTag")})]})}function z(t){var r,o;const m=me();return((r=t.tutorialUrls)==null?void 0:r[m])||((o=t.tutorialUrls)==null?void 0:o.default)||t.tutorialUrl}const Ie={telegram:"telegram.svg",slack:"slack.svg",discord:"discord.svg",whatsapp:"whatsapp.svg",qq:"qq.svg",feishu:"feishu.svg",dingtalk:"dingtalk.svg",wecom:"wecom.svg",mochat:"mochat.svg",email:"email.svg"};function Fe(t,m){const i=m.toLowerCase(),r=t[i];return r?`/logos/${r}`:null}function Y(t){return Fe(Ie,t)}const B=[{value:"pairing",label:"pairing"},{value:"allowlist",label:"allowlist"},{value:"open",label:"open"},{value:"disabled",label:"disabled"}],R=[{value:"open",label:"open"},{value:"allowlist",label:"allowlist"},{value:"disabled",label:"disabled"}],Te=[{value:"off",label:"off"},{value:"partial",label:"partial"},{value:"block",label:"block"},{value:"progress",label:"progress"}],De=t=>t.includes("token")||t.includes("secret")||t.includes("password")?a.jsx(ae,{className:"h-3.5 w-3.5 text-gray-500"}):t.includes("url")||t.includes("host")?a.jsx(te,{className:"h-3.5 w-3.5 text-gray-500"}):t.includes("email")||t.includes("mail")?a.jsx(se,{className:"h-3.5 w-3.5 text-gray-500"}):t.includes("id")||t.includes("from")?a.jsx(ne,{className:"h-3.5 w-3.5 text-gray-500"}):t==="enabled"||t==="consentGranted"?a.jsx(le,{className:"h-3.5 w-3.5 text-gray-500"}):a.jsx(re,{className:"h-3.5 w-3.5 text-gray-500"});function G(){return{telegram:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"token",type:"password",label:e("botToken")},{name:"allowFrom",type:"tags",label:e("allowFrom")},{name:"proxy",type:"text",label:e("proxy")},{name:"accountId",type:"text",label:e("accountId")},{name:"dmPolicy",type:"select",label:e("dmPolicy"),options:B},{name:"groupPolicy",type:"select",label:e("groupPolicy"),options:R},{name:"groupAllowFrom",type:"tags",label:e("groupAllowFrom")},{name:"requireMention",type:"boolean",label:e("requireMention")},{name:"mentionPatterns",type:"tags",label:e("mentionPatterns")},{name:"groups",type:"json",label:e("groupRulesJson")}],discord:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"token",type:"password",label:e("botToken")},{name:"allowBots",type:"boolean",label:e("allowBotMessages")},{name:"allowFrom",type:"tags",label:e("allowFrom")},{name:"gatewayUrl",type:"text",label:e("gatewayUrl")},{name:"intents",type:"number",label:e("intents")},{name:"proxy",type:"text",label:e("proxy")},{name:"mediaMaxMb",type:"number",label:e("attachmentMaxSizeMb")},{name:"streaming",type:"select",label:e("streamingMode"),options:Te},{name:"draftChunk",type:"json",label:e("draftChunkingJson")},{name:"textChunkLimit",type:"number",label:e("textChunkLimit")},{name:"accountId",type:"text",label:e("accountId")},{name:"dmPolicy",type:"select",label:e("dmPolicy"),options:B},{name:"groupPolicy",type:"select",label:e("groupPolicy"),options:R},{name:"groupAllowFrom",type:"tags",label:e("groupAllowFrom")},{name:"requireMention",type:"boolean",label:e("requireMention")},{name:"mentionPatterns",type:"tags",label:e("mentionPatterns")},{name:"groups",type:"json",label:e("groupRulesJson")}],whatsapp:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"bridgeUrl",type:"text",label:e("bridgeUrl")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],feishu:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"appId",type:"text",label:e("appId")},{name:"appSecret",type:"password",label:e("appSecret")},{name:"encryptKey",type:"password",label:e("encryptKey")},{name:"verificationToken",type:"password",label:e("verificationToken")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],dingtalk:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"clientId",type:"text",label:e("clientId")},{name:"clientSecret",type:"password",label:e("clientSecret")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],wecom:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"corpId",type:"text",label:e("corpId")},{name:"agentId",type:"text",label:e("agentId")},{name:"secret",type:"password",label:e("secret")},{name:"token",type:"password",label:e("token")},{name:"callbackPort",type:"number",label:e("callbackPort")},{name:"callbackPath",type:"text",label:e("callbackPath")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],slack:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"mode",type:"text",label:e("mode")},{name:"webhookPath",type:"text",label:e("webhookPath")},{name:"allowBots",type:"boolean",label:e("allowBotMessages")},{name:"botToken",type:"password",label:e("botToken")},{name:"appToken",type:"password",label:e("appToken")}],email:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"consentGranted",type:"boolean",label:e("consentGranted")},{name:"imapHost",type:"text",label:e("imapHost")},{name:"imapPort",type:"number",label:e("imapPort")},{name:"imapUsername",type:"text",label:e("imapUsername")},{name:"imapPassword",type:"password",label:e("imapPassword")},{name:"fromAddress",type:"email",label:e("fromAddress")}],mochat:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"baseUrl",type:"text",label:e("baseUrl")},{name:"clawToken",type:"password",label:e("clawToken")},{name:"agentUserId",type:"text",label:e("agentUserId")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],qq:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"appId",type:"text",label:e("appId")},{name:"secret",type:"password",label:e("appSecret")},{name:"markdownSupport",type:"boolean",label:e("markdownSupport")},{name:"allowFrom",type:"tags",label:e("allowFrom")}]}}function A(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function V(t,m){const i={...t};for(const[r,o]of Object.entries(m)){const u=i[r];if(A(u)&&A(o)){i[r]=V(u,o);continue}i[r]=o}return i}function Ae(t,m){const i=t.split("."),r={};let o=r;for(let u=0;u<i.length-1;u+=1){const d=i[u];o[d]={},o=o[d]}return o[i[i.length-1]]=m,r}function Le({channelName:t}){var _,O;const{data:m}=q(),{data:i}=$(),{data:r}=H(),o=pe(),u=de(),[d,g]=v.useState({}),[x,j]=v.useState({}),[h,f]=v.useState(null),k=t?m==null?void 0:m.channels[t]:null,w=t?G()[t]??[]:[],c=r==null?void 0:r.uiHints,p=t?`channels.${t}`:null,S=((_=r==null?void 0:r.actions)==null?void 0:_.filter(s=>s.scope===p))??[],C=t&&(((O=U(`channels.${t}`,c))==null?void 0:O.label)??t),P=i==null?void 0:i.channels.find(s=>s.name===t),F=P?z(P):void 0;v.useEffect(()=>{if(k){g({...k});const s={};(t?G()[t]??[]:[]).filter(l=>l.type==="json").forEach(l=>{const y=k[l.name];s[l.name]=JSON.stringify(y??{},null,2)}),j(s)}else g({}),j({})},[k,t]);const N=(s,n)=>{g(l=>({...l,[s]:n}))},L=s=>{if(s.preventDefault(),!t)return;const n={...d};for(const l of w){if(l.type!=="password")continue;const y=n[l.name];(typeof y!="string"||y.length===0)&&delete n[l.name]}for(const l of w){if(l.type!=="json")continue;const y=x[l.name]??"";try{n[l.name]=y.trim()?JSON.parse(y):{}}catch{T.error(`${e("invalidJson")}: ${l.name}`);return}}o.mutate({channel:t,data:n})},W=s=>{if(!s||!t)return;const n=s.channels;if(!A(n))return;const l=n[t];A(l)&&g(y=>V(y,l))},X=async s=>{if(!(!t||!p)){f(s.id);try{let n={...d};s.saveBeforeRun&&(n={...n,...s.savePatch??{}},g(n),await o.mutateAsync({channel:t,data:n}));const l=await u.mutateAsync({actionId:s.id,data:{scope:p,draftConfig:Ae(p,n)}});W(l.patch),l.ok?T.success(l.message||e("success")):T.error(l.message||e("error"))}catch(n){const l=n instanceof Error?n.message:String(n);T.error(`${e("error")}: ${l}`)}finally{f(null)}}};if(!t||!P||!k)return a.jsx("div",{className:we,children:a.jsxs("div",{children:[a.jsx("h3",{className:"text-base font-semibold text-gray-900",children:e("channelsSelectTitle")}),a.jsx("p",{className:"mt-2 text-sm text-gray-500",children:e("channelsSelectDescription")})]})});const M=!!k.enabled;return a.jsxs("div",{className:ve,children:[a.jsx("div",{className:"border-b border-gray-100 px-6 py-5",children:a.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[a.jsxs("div",{className:"min-w-0",children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx(K,{name:t,src:Y(t),className:I("h-9 w-9 rounded-lg border",M?"border-primary/30 bg-white":"border-gray-200/70 bg-white"),imgClassName:"h-5 w-5 object-contain",fallback:a.jsx("span",{className:"text-sm font-semibold uppercase text-gray-500",children:t[0]})}),a.jsx("h3",{className:"truncate text-lg font-semibold text-gray-900 capitalize",children:C})]}),a.jsx("p",{className:"mt-2 text-sm text-gray-500",children:e("channelsFormDescription")}),F&&a.jsxs("a",{href:F,className:"mt-2 inline-flex items-center gap-1.5 text-xs text-primary transition-colors hover:text-primary-hover",children:[a.jsx(ee,{className:"h-3.5 w-3.5"}),e("channelsGuideTitle")]})]}),a.jsx(J,{status:M?"active":"inactive",label:M?e("statusActive"):e("statusInactive")})]})}),a.jsxs("form",{onSubmit:L,className:"flex min-h-0 flex-1 flex-col",children:[a.jsx("div",{className:"min-h-0 flex-1 space-y-6 overflow-y-auto overscroll-contain px-6 py-5",children:w.map(s=>{const n=t?U(`channels.${t}.${s.name}`,c):void 0,l=(n==null?void 0:n.label)??s.label,y=n==null?void 0:n.placeholder;return a.jsxs("div",{className:"space-y-2.5",children:[a.jsxs(he,{htmlFor:s.name,className:"flex items-center gap-2 text-sm font-medium text-gray-900",children:[De(s.name),l]}),s.type==="boolean"&&a.jsxs("div",{className:"flex items-center justify-between rounded-xl bg-gray-50 p-3",children:[a.jsx("span",{className:"text-sm text-gray-500",children:d[s.name]?e("enabled"):e("disabled")}),a.jsx(fe,{id:s.name,checked:d[s.name]||!1,onCheckedChange:b=>N(s.name,b),className:"data-[state=checked]:bg-emerald-500"})]}),(s.type==="text"||s.type==="email")&&a.jsx(D,{id:s.name,type:s.type,value:d[s.name]||"",onChange:b=>N(s.name,b.target.value),placeholder:y,className:"rounded-xl"}),s.type==="password"&&a.jsx(D,{id:s.name,type:"password",value:d[s.name]||"",onChange:b=>N(s.name,b.target.value),placeholder:y??e("leaveBlankToKeepUnchanged"),className:"rounded-xl"}),s.type==="number"&&a.jsx(D,{id:s.name,type:"number",value:d[s.name]||0,onChange:b=>N(s.name,parseInt(b.target.value,10)||0),placeholder:y,className:"rounded-xl"}),s.type==="tags"&&a.jsx(Pe,{value:d[s.name]||[],onChange:b=>N(s.name,b)}),s.type==="select"&&a.jsxs(be,{value:d[s.name]||"",onValueChange:b=>N(s.name,b),children:[a.jsx(ue,{className:"rounded-xl",children:a.jsx(xe,{})}),a.jsx(ye,{children:(s.options??[]).map(b=>a.jsx(ge,{value:b.value,children:b.label},b.value))})]}),s.type==="json"&&a.jsx("textarea",{id:s.name,value:x[s.name]??"{}",onChange:b=>j(Q=>({...Q,[s.name]:b.target.value})),className:"min-h-[120px] w-full resize-none rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-mono"})]},s.name)})}),a.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3 border-t border-gray-100 px-6 py-4",children:[a.jsx("div",{className:"flex flex-wrap items-center gap-2",children:S.filter(s=>s.trigger==="manual").map(s=>a.jsx(E,{type:"button",onClick:()=>X(s),disabled:o.isPending||!!h,variant:"secondary",children:h===s.id?e("connecting"):s.title},s.id))}),a.jsx(E,{type:"submit",disabled:o.isPending||!!h,children:o.isPending?e("saving"):e("save")})]})]})]})}const Me={telegram:"channelDescTelegram",slack:"channelDescSlack",email:"channelDescEmail",webhook:"channelDescWebhook",discord:"channelDescDiscord",feishu:"channelDescFeishu"};function He(){const{data:t}=q(),{data:m}=$(),{data:i}=H(),[r,o]=v.useState("enabled"),[u,d]=v.useState(),[g,x]=v.useState(""),j=i==null?void 0:i.uiHints,h=m==null?void 0:m.channels,f=t==null?void 0:t.channels,k=[{id:"enabled",label:e("channelsTabEnabled"),count:(h??[]).filter(c=>{var p;return(p=f==null?void 0:f[c.name])==null?void 0:p.enabled}).length},{id:"all",label:e("channelsTabAll"),count:(h??[]).length}],w=v.useMemo(()=>{const c=g.trim().toLowerCase();return(h??[]).filter(p=>{var C;const S=((C=f==null?void 0:f[p.name])==null?void 0:C.enabled)||!1;return r==="enabled"?S:!0}).filter(p=>c?(p.displayName||p.name).toLowerCase().includes(c)||p.name.toLowerCase().includes(c):!0)},[r,f,h,g]);return v.useEffect(()=>{if(w.length===0){d(void 0);return}w.some(p=>p.name===u)||d(w[0].name)},[w,u]),!t||!m?a.jsx("div",{className:"p-8 text-gray-400",children:e("channelsLoading")}):a.jsxs(Ce,{className:"xl:flex xl:h-full xl:min-h-0 xl:flex-col xl:pb-0",children:[a.jsx(Ne,{title:e("channelsPageTitle"),description:e("channelsPageDescription")}),a.jsxs("div",{className:I(ke,"xl:min-h-0 xl:flex-1"),children:[a.jsxs("section",{className:je,children:[a.jsx("div",{className:"border-b border-gray-100 px-4 pt-4",children:a.jsx(Se,{tabs:k,activeTab:r,onChange:o,className:"mb-0"})}),a.jsx("div",{className:"border-b border-gray-100 px-4 py-3",children:a.jsxs("div",{className:"relative",children:[a.jsx(oe,{className:"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"}),a.jsx(D,{value:g,onChange:c=>x(c.target.value),placeholder:e("channelsFilterPlaceholder"),className:"h-10 rounded-xl pl-9"})]})}),a.jsxs("div",{className:"min-h-0 flex-1 space-y-2 overflow-y-auto overscroll-contain p-3",children:[w.map(c=>{const p=t.channels[c.name],S=(p==null?void 0:p.enabled)||!1,C=U(`channels.${c.name}`,j),P=z(c),F=(C==null?void 0:C.help)||e(Me[c.name]||"channelDescriptionDefault"),N=u===c.name;return a.jsx("button",{type:"button",onClick:()=>d(c.name),className:I("w-full rounded-xl border p-2.5 text-left transition-all",N?"border-primary/30 bg-primary-50/40 shadow-sm":"border-gray-200/70 bg-white hover:border-gray-300 hover:bg-gray-50/70"),children:a.jsxs("div",{className:"flex items-start justify-between gap-3",children:[a.jsxs("div",{className:"flex min-w-0 items-center gap-3",children:[a.jsx(K,{name:c.name,src:Y(c.name),className:I("h-10 w-10 rounded-lg border",S?"border-primary/30 bg-white":"border-gray-200/70 bg-white"),imgClassName:"h-5 w-5 object-contain",fallback:a.jsx("span",{className:"text-sm font-semibold uppercase text-gray-500",children:c.name[0]})}),a.jsxs("div",{className:"min-w-0",children:[a.jsx("p",{className:"truncate text-sm font-semibold text-gray-900",children:c.displayName||c.name}),a.jsx("p",{className:"line-clamp-1 text-[11px] text-gray-500",children:F})]})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[P&&a.jsx("a",{href:P,onClick:L=>L.stopPropagation(),className:"inline-flex h-7 w-7 items-center justify-center rounded-md text-gray-300 transition-colors hover:bg-gray-100/70 hover:text-gray-500",title:e("channelsGuideTitle"),children:a.jsx(ce,{className:"h-3.5 w-3.5"})}),a.jsx(J,{status:S?"active":"inactive",label:S?e("statusActive"):e("statusInactive"),className:"min-w-[56px] justify-center"})]})]})},c.name)}),w.length===0&&a.jsxs("div",{className:"flex h-full min-h-[220px] flex-col items-center justify-center rounded-xl border border-dashed border-gray-200 bg-gray-50/70 py-10 text-center",children:[a.jsx("div",{className:"mb-3 flex h-10 w-10 items-center justify-center rounded-lg bg-white",children:a.jsx(ie,{className:"h-5 w-5 text-gray-300"})}),a.jsx("p",{className:"text-sm font-medium text-gray-700",children:e("channelsNoMatch")})]})]})]}),a.jsx(Le,{channelName:u})]})]})}export{He as ChannelsList};