@nextclaw/ui 0.9.13 → 0.9.14

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 (73) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +17 -16
  3. package/public/logos/weixin.svg +5 -0
  4. package/src/components/chat/ncp/ncp-app-client-fetch.test.ts +9 -0
  5. package/src/components/chat/ncp/ncp-app-client-fetch.ts +10 -0
  6. package/src/components/chat/ncp/ncp-session-adapter.ts +0 -1
  7. package/src/components/config/ChannelForm.tsx +1 -122
  8. package/src/components/config/ChannelsList.test.tsx +127 -0
  9. package/src/components/config/ChannelsList.tsx +2 -1
  10. package/src/components/config/channel-form-fields.ts +131 -0
  11. package/src/lib/i18n.channels.ts +52 -0
  12. package/src/lib/i18n.ts +2 -43
  13. package/src/lib/logos.ts +1 -0
  14. package/src/transport/local.transport.ts +4 -1
  15. package/src/transport/remote.transport.ts +5 -1
  16. package/src/transport/sse-stream.test.ts +68 -0
  17. package/src/transport/sse-stream.ts +48 -7
  18. package/src/transport/transport.types.ts +2 -0
  19. package/LICENSE +0 -21
  20. package/dist/assets/ChannelsList-bROKR37R.js +0 -1
  21. package/dist/assets/ChatPage-B9dHVmrV.js +0 -41
  22. package/dist/assets/DocBrowser-S-1-qnZQ.js +0 -1
  23. package/dist/assets/LogoBadge-t1JzzCtI.js +0 -1
  24. package/dist/assets/MarketplacePage-CzIHYJpM.js +0 -49
  25. package/dist/assets/McpMarketplacePage-BTJdjNQ1.js +0 -40
  26. package/dist/assets/ModelConfig-BD4o3Kna.js +0 -1
  27. package/dist/assets/ProvidersList-BOQArFRk.js +0 -1
  28. package/dist/assets/RemoteAccessPage-CYNQ53xu.js +0 -1
  29. package/dist/assets/RuntimeConfig-B0B73pye.js +0 -1
  30. package/dist/assets/SearchConfig-CKy2QkAP.js +0 -1
  31. package/dist/assets/SecretsConfig-BpZLUu88.js +0 -3
  32. package/dist/assets/SessionsConfig-CoFI6Fa2.js +0 -2
  33. package/dist/assets/chat-message-D3jZIASl.js +0 -3
  34. package/dist/assets/config-hints-CApS3K_7.js +0 -1
  35. package/dist/assets/config-layout-BHnOoweL.js +0 -1
  36. package/dist/assets/index-CmGwUgcl.js +0 -8
  37. package/dist/assets/index-SGSkQCPi.css +0 -1
  38. package/dist/assets/label-BOvIOmQx.js +0 -1
  39. package/dist/assets/marketplace-localization-Dk31LJJJ.js +0 -1
  40. package/dist/assets/page-layout-PG3cwSpz.js +0 -1
  41. package/dist/assets/popover-BB-kINz7.js +0 -1
  42. package/dist/assets/provider-models-BOeNnjk9.js +0 -1
  43. package/dist/assets/security-config-Bb6l-viE.js +0 -1
  44. package/dist/assets/skeleton-CLSc5FYO.js +0 -1
  45. package/dist/assets/status-dot-Behu7kDZ.js +0 -1
  46. package/dist/assets/switch-CvNG9775.js +0 -1
  47. package/dist/assets/tabs-custom-CUdBQO_7.js +0 -1
  48. package/dist/assets/useConfirmDialog-CLLe2uIJ.js +0 -1
  49. package/dist/assets/vendor-TJ2hy_Lv.js +0 -441
  50. package/dist/index.html +0 -18
  51. package/dist/logo.svg +0 -5
  52. package/dist/logos/aihubmix.png +0 -0
  53. package/dist/logos/anthropic.svg +0 -1
  54. package/dist/logos/dashscope.png +0 -0
  55. package/dist/logos/deepseek.png +0 -0
  56. package/dist/logos/dingtalk.svg +0 -1
  57. package/dist/logos/discord.svg +0 -1
  58. package/dist/logos/email.svg +0 -1
  59. package/dist/logos/feishu.svg +0 -12
  60. package/dist/logos/gemini.svg +0 -1
  61. package/dist/logos/groq.svg +0 -1
  62. package/dist/logos/minimax.svg +0 -1
  63. package/dist/logos/mochat.svg +0 -6
  64. package/dist/logos/moonshot.png +0 -0
  65. package/dist/logos/openai.svg +0 -1
  66. package/dist/logos/openrouter.svg +0 -1
  67. package/dist/logos/qq.svg +0 -1
  68. package/dist/logos/slack.svg +0 -1
  69. package/dist/logos/telegram.svg +0 -1
  70. package/dist/logos/vllm.svg +0 -1
  71. package/dist/logos/wecom.svg +0 -11
  72. package/dist/logos/whatsapp.svg +0 -1
  73. package/dist/logos/zhipu.svg +0 -15
package/src/lib/logos.ts CHANGED
@@ -9,6 +9,7 @@ const CHANNEL_LOGOS: LogoMap = {
9
9
  feishu: "feishu.svg",
10
10
  dingtalk: "dingtalk.svg",
11
11
  wecom: "wecom.svg",
12
+ weixin: "weixin.svg",
12
13
  mochat: "mochat.svg",
13
14
  email: "email.svg"
14
15
  };
@@ -151,7 +151,10 @@ export class LocalAppTransport implements AppTransport {
151
151
  throw new Error(text.trim() || `HTTP ${response.status}`);
152
152
  }
153
153
  try {
154
- return await readSseStreamResult<TFinal>(response, input.onEvent);
154
+ return await readSseStreamResult<TFinal>(response, input.onEvent, {
155
+ terminalEventNames: input.terminalEventNames,
156
+ terminalEventPayloadTypes: input.terminalEventPayloadTypes
157
+ });
155
158
  } finally {
156
159
  input.signal?.removeEventListener('abort', abort);
157
160
  }
@@ -7,6 +7,8 @@ type RemoteTarget = {
7
7
  method: string;
8
8
  path: string;
9
9
  body?: unknown;
10
+ terminalEventNames?: readonly string[];
11
+ terminalEventPayloadTypes?: Partial<Record<string, readonly string[]>>;
10
12
  };
11
13
 
12
14
  type RemoteBrowserFrame =
@@ -167,7 +169,9 @@ export class RemoteSessionMultiplexTransport implements AppTransport {
167
169
  target: {
168
170
  method: input.method,
169
171
  path: input.path,
170
- ...(input.body !== undefined ? { body: input.body } : {})
172
+ ...(input.body !== undefined ? { body: input.body } : {}),
173
+ ...(input.terminalEventNames ? { terminalEventNames: input.terminalEventNames } : {}),
174
+ ...(input.terminalEventPayloadTypes ? { terminalEventPayloadTypes: input.terminalEventPayloadTypes } : {})
171
175
  }
172
176
  });
173
177
  })
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { readSseStreamResult } from './sse-stream';
3
+
4
+ function createSseResponse(frames: string[]): Response {
5
+ const payload = new TextEncoder().encode(frames.join(''));
6
+ const stream = new ReadableStream<Uint8Array>({
7
+ start(controller) {
8
+ controller.enqueue(payload);
9
+ controller.close();
10
+ }
11
+ });
12
+ return new Response(stream, {
13
+ status: 200,
14
+ headers: {
15
+ 'content-type': 'text/event-stream; charset=utf-8'
16
+ }
17
+ });
18
+ }
19
+
20
+ function encodeFrame(event: string, payload: unknown): string {
21
+ return `event: ${event}\ndata: ${JSON.stringify(payload)}\n\n`;
22
+ }
23
+
24
+ describe('readSseStreamResult', () => {
25
+ it('accepts terminal-event streams without final frame when configured', async () => {
26
+ const events: Array<{ name: string; payload?: unknown }> = [];
27
+ const response = createSseResponse([
28
+ encodeFrame('ncp-event', { type: 'message.text-delta', payload: { delta: 'hello' } }),
29
+ encodeFrame('run.finished', { sessionId: 's1' })
30
+ ]);
31
+
32
+ const result = await readSseStreamResult(response, (event) => {
33
+ events.push(event);
34
+ }, {
35
+ terminalEventNames: ['run.finished']
36
+ });
37
+
38
+ expect(result).toBeUndefined();
39
+ expect(events.map((event) => event.name)).toEqual(['ncp-event', 'run.finished']);
40
+ });
41
+
42
+ it('accepts terminal payload-type streams without final frame when configured', async () => {
43
+ const events: Array<{ name: string; payload?: unknown }> = [];
44
+ const response = createSseResponse([
45
+ encodeFrame('ncp-event', { type: 'message.text-delta', payload: { delta: 'hello' } }),
46
+ encodeFrame('ncp-event', { type: 'run.finished', payload: { sessionId: 's1' } })
47
+ ]);
48
+
49
+ const result = await readSseStreamResult(response, (event) => {
50
+ events.push(event);
51
+ }, {
52
+ terminalEventPayloadTypes: {
53
+ 'ncp-event': ['run.finished']
54
+ }
55
+ });
56
+
57
+ expect(result).toBeUndefined();
58
+ expect(events.map((event) => event.name)).toEqual(['ncp-event', 'ncp-event']);
59
+ });
60
+
61
+ it('still rejects streams that end without final or configured terminal event', async () => {
62
+ const response = createSseResponse([
63
+ encodeFrame('ncp-event', { type: 'message.text-delta', payload: { delta: 'hello' } })
64
+ ]);
65
+
66
+ await expect(readSseStreamResult(response, () => undefined)).rejects.toThrow('stream ended without final event');
67
+ });
68
+ });
@@ -1,6 +1,12 @@
1
1
  import type { StreamEvent } from './transport.types';
2
2
 
3
3
  type SseErrorPayload = { message?: string } | string | undefined;
4
+ type FinalResultSink = (value: unknown) => void;
5
+ type TerminalEventSink = (frame: StreamEvent) => void;
6
+ type TerminalDetectionOptions = {
7
+ terminalEventNames?: readonly string[];
8
+ terminalEventPayloadTypes?: Partial<Record<string, readonly string[]>>;
9
+ };
4
10
 
5
11
  function parseSseFrame(frame: string): StreamEvent | null {
6
12
  const lines = frame.split('\n');
@@ -42,10 +48,29 @@ function readSseErrorMessage(payload: SseErrorPayload, fallback: string): string
42
48
  : payload?.message ?? fallback;
43
49
  }
44
50
 
51
+ function matchesTerminalFrame(frame: StreamEvent, options: TerminalDetectionOptions): boolean {
52
+ if ((options.terminalEventNames ?? []).includes(frame.name)) {
53
+ return true;
54
+ }
55
+ const payloadTypes = options.terminalEventPayloadTypes?.[frame.name];
56
+ if (!payloadTypes || payloadTypes.length === 0) {
57
+ return false;
58
+ }
59
+ const payloadType =
60
+ typeof frame.payload === 'object' &&
61
+ frame.payload &&
62
+ 'type' in frame.payload &&
63
+ typeof (frame.payload as { type?: unknown }).type === 'string'
64
+ ? (frame.payload as { type: string }).type
65
+ : null;
66
+ return payloadType !== null && payloadTypes.includes(payloadType);
67
+ }
68
+
45
69
  function processSseFrame(
46
70
  rawFrame: string,
47
71
  onEvent: (event: StreamEvent) => void,
48
- setFinalResult: (value: unknown) => void
72
+ setFinalResult: FinalResultSink,
73
+ setTerminalEvent: TerminalEventSink
49
74
  ): void {
50
75
  const frame = parseSseFrame(rawFrame);
51
76
  if (!frame) {
@@ -58,17 +83,19 @@ function processSseFrame(
58
83
  if (frame.name === 'error') {
59
84
  throw new Error(readSseErrorMessage(frame.payload as SseErrorPayload, 'chat stream failed'));
60
85
  }
86
+ setTerminalEvent(frame);
61
87
  onEvent(frame);
62
88
  }
63
89
 
64
90
  function flushBufferedFrames(
65
91
  bufferState: { value: string },
66
92
  onEvent: (event: StreamEvent) => void,
67
- setFinalResult: (value: unknown) => void
93
+ setFinalResult: FinalResultSink,
94
+ setTerminalEvent: TerminalEventSink
68
95
  ): void {
69
96
  let boundary = bufferState.value.indexOf('\n\n');
70
97
  while (boundary !== -1) {
71
- processSseFrame(bufferState.value.slice(0, boundary), onEvent, setFinalResult);
98
+ processSseFrame(bufferState.value.slice(0, boundary), onEvent, setFinalResult, setTerminalEvent);
72
99
  bufferState.value = bufferState.value.slice(boundary + 2);
73
100
  boundary = bufferState.value.indexOf('\n\n');
74
101
  }
@@ -76,7 +103,8 @@ function flushBufferedFrames(
76
103
 
77
104
  export async function readSseStreamResult<TFinal>(
78
105
  response: Response,
79
- onEvent: (event: StreamEvent) => void
106
+ onEvent: (event: StreamEvent) => void,
107
+ options: TerminalDetectionOptions = {}
80
108
  ): Promise<TFinal> {
81
109
  const reader = response.body?.getReader();
82
110
  if (!reader) {
@@ -86,7 +114,7 @@ export async function readSseStreamResult<TFinal>(
86
114
  const decoder = new TextDecoder();
87
115
  const bufferState = { value: '' };
88
116
  let finalResult: unknown = undefined;
89
-
117
+ let sawTerminalEvent = false;
90
118
  try {
91
119
  while (true) {
92
120
  const { value, done } = await reader.read();
@@ -96,18 +124,31 @@ export async function readSseStreamResult<TFinal>(
96
124
  bufferState.value += decoder.decode(value, { stream: true });
97
125
  flushBufferedFrames(bufferState, onEvent, (nextValue) => {
98
126
  finalResult = nextValue;
127
+ }, (event) => {
128
+ if (matchesTerminalFrame(event, options)) {
129
+ sawTerminalEvent = true;
130
+ }
99
131
  });
100
132
  }
101
133
  if (bufferState.value.trim()) {
102
- processSseFrame(bufferState.value, onEvent, (nextValue) => {
134
+ processSseFrame(bufferState.value, (event) => {
135
+ if (matchesTerminalFrame(event, options)) {
136
+ sawTerminalEvent = true;
137
+ }
138
+ onEvent(event);
139
+ }, (nextValue) => {
103
140
  finalResult = nextValue;
141
+ }, (event) => {
142
+ if (matchesTerminalFrame(event, options)) {
143
+ sawTerminalEvent = true;
144
+ }
104
145
  });
105
146
  }
106
147
  } finally {
107
148
  reader.releaseLock();
108
149
  }
109
150
 
110
- if (finalResult === undefined) {
151
+ if (finalResult === undefined && !sawTerminalEvent) {
111
152
  throw new Error('stream ended without final event');
112
153
  }
113
154
  return finalResult as TFinal;
@@ -18,6 +18,8 @@ export type StreamInput = {
18
18
  path: string;
19
19
  body?: unknown;
20
20
  signal?: AbortSignal;
21
+ terminalEventNames?: readonly string[];
22
+ terminalEventPayloadTypes?: Partial<Record<string, readonly string[]>>;
21
23
  onEvent: (event: StreamEvent) => void;
22
24
  };
23
25
 
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 NextClaw contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1 +0,0 @@
1
- import{r as v,j as a,X as Z,a3 as ee,e as T,K as ae,aq as te,b1 as se,b2 as ne,b3 as le,a0 as re,G as oe,ai as ce,H as ie}from"./vendor-TJ2hy_Lv.js";import{t as e,c as I,Z as me,u as q,a as $,b as H,$ as pe,a0 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-CmGwUgcl.js";import{L as he}from"./label-BOvIOmQx.js";import{S as fe}from"./switch-CvNG9775.js";import{S as K}from"./status-dot-Behu7kDZ.js";import{L as J}from"./LogoBadge-t1JzzCtI.js";import{h as _}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-CUdBQO_7.js";import{P as Ce,a as Ne}from"./page-layout-PG3cwSpz.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"}],G=[{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 R(){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:G},{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:G},{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,U;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?R()[t]??[]:[],c=r==null?void 0:r.uiHints,p=t?`channels.${t}`:null,S=((O=r==null?void 0:r.actions)==null?void 0:O.filter(s=>s.scope===p))??[],C=t&&(((U=_(`channels.${t}`,c))==null?void 0:U.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?R()[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})},Q=s=>{if(!s||!t)return;const n=s.channels;if(!A(n))return;const l=n[t];A(l)&&g(y=>V(y,l))},W=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)}});Q(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(J,{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(K,{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?_(`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(X=>({...X,[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:()=>W(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 Ke(){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=_(`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(J,{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(K,{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{Ke as ChannelsList};