@agentaily/design-system 0.5.0 → 0.7.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.
@@ -1,688 +0,0 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import React, { useState, useRef, useEffect } from "react";
3
- import { Alert } from "../feedback/Alert.js";
4
- import { Badge } from "../display/Badge.js";
5
- import { BrandMark } from "../utilities/BrandMark.js";
6
- import { Button } from "../buttons/Button.js";
7
- import { HelpSteps } from "./HelpSteps.js";
8
- import { Icon } from "../utilities/Icon.js";
9
- import { IconButton } from "../buttons/IconButton.js";
10
- import { SecretField } from "../inputs/SecretField.js";
11
- import { Select } from "../inputs/Select.js";
12
- import { Spinner } from "../feedback/Spinner.js";
13
- import { StatusPill } from "../display/StatusPill.js";
14
- import { Switch } from "../inputs/Switch.js";
15
- import { TestRow } from "./TestRow.js";
16
- const AX_INTEGRATION_CSS = `
17
- .s-overlay { position: fixed; inset: 0; z-index: 100; display: flex; animation: s-fade var(--dur-2) var(--ease-out) both; }
18
- .s-modal { position: relative; flex: 1; display: flex; flex-direction: column; min-height: 0;
19
- background: var(--surface-page); animation: s-rise var(--dur-3) var(--ease-out) both; }
20
- @keyframes s-fade { from { opacity: 0; } to { opacity: 1; } }
21
- @keyframes s-rise { from { opacity: 0; transform: translateY(10px) scale(0.994); } to { opacity: 1; transform: none; } }
22
- @media (prefers-reduced-motion: reduce) { .s-overlay, .s-modal { animation: none; } }
23
-
24
- .s-modal__bar { flex: none; display: flex; align-items: center; gap: 16px; height: 52px;
25
- padding: 0 12px 0 18px; border-bottom: 1px solid var(--border-default); background: var(--surface-panel); }
26
- .s-modal__brand { display: flex; align-items: center; gap: 8px; }
27
- .s-modal__word { font-family: var(--font-mono); font-size: 15px; font-weight: 500; letter-spacing: -0.02em; color: var(--text-body); }
28
- .s-modal__div { color: var(--text-faint); }
29
- .s-modal__crumb { font-family: var(--font-mono); font-size: 13px; color: var(--text-body); }
30
- .s-modal__baractions { margin-left: auto; display: flex; align-items: center; gap: 12px; flex: none; }
31
-
32
- .s-modal__body { flex: 1; overflow-y: auto; min-height: 0; }
33
- .s-wrap { max-width: 768px; margin: 0 auto; padding: 40px 24px 48px; }
34
-
35
- .s-hero { margin-bottom: 28px; }
36
- .s-hero__kicker { color: var(--text-faint); margin-bottom: 12px; }
37
- .s-hero__h { font-family: var(--font-display); font-size: var(--text-2xl); font-weight: 500;
38
- letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); color: var(--text-body); margin: 0 0 10px; }
39
- .s-hero__p { font-size: var(--text-md); color: var(--text-muted); line-height: var(--leading-body); margin: 0; max-width: 62ch; }
40
-
41
- .s-ready { display: flex; align-items: center; gap: 14px; padding: 13px 16px; margin-bottom: 26px;
42
- border: 1px solid var(--border-default); border-radius: var(--radius-3); background: var(--surface-panel); }
43
- .s-ready__steps { display: flex; align-items: center; gap: 7px; flex: none; }
44
- .s-ready__dot { width: 9px; height: 9px; border-radius: var(--radius-1); border: 1px solid var(--border-strong);
45
- background: transparent; transition: background var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out); }
46
- .s-ready__dot.is-on { background: var(--ok); border-color: var(--ok); }
47
- .s-ready__txt { flex: 1; min-width: 0; font-size: var(--text-sm); color: var(--text-muted); }
48
- .s-ready__txt strong { color: var(--text-body); font-weight: 500; }
49
- .s-ready__count { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint); letter-spacing: 0.04em; flex: none; }
50
-
51
- .s-cards { display: flex; flex-direction: column; gap: 18px; }
52
- .s-card { position: relative; background: var(--surface-card); border: 1px solid var(--border-default);
53
- border-radius: var(--radius-3); overflow: hidden; transition: border-color var(--dur-2) var(--ease-out); }
54
- .s-card.is-ok { border-color: rgba(62, 207, 142, 0.4); }
55
- .s-card.is-error { border-color: rgba(229, 72, 77, 0.4); }
56
- .s-card__head { padding: 20px 24px 18px; }
57
- .s-card__toprow { display: flex; align-items: center; gap: 11px; margin-bottom: 15px; }
58
- .s-card__icon { flex: none; width: 30px; height: 30px; border-radius: var(--radius-2);
59
- border: 1px solid var(--border-strong); background: var(--surface-raised); color: var(--text-body);
60
- display: flex; align-items: center; justify-content: center; }
61
- .s-card__eyebrow { color: var(--text-faint); }
62
- .s-card__status { margin-left: auto; flex: none; }
63
- .s-card__title { font-family: var(--font-display); font-size: var(--text-xl); font-weight: 500;
64
- letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); color: var(--text-body); margin: 0; }
65
- .s-card__desc { font-size: var(--text-sm); color: var(--text-muted); line-height: var(--leading-snug); margin: 9px 0 0; max-width: 58ch; }
66
- .s-card__body { padding: 4px 24px 22px; display: flex; flex-direction: column; gap: 18px; }
67
-
68
- .s-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
69
- @media (max-width: 560px) { .s-row2 { grid-template-columns: 1fr; } }
70
-
71
- .s-lock { display: flex; align-items: flex-start; gap: 8px; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); }
72
- .s-lock svg { flex: none; margin-top: 1px; color: var(--text-muted); }
73
- .s-lock strong { color: var(--text-muted); font-weight: 500; }
74
-
75
- .s-detect { display: flex; flex-direction: column; gap: 9px; padding: 13px 14px; border: 1px solid var(--border-default);
76
- border-radius: var(--radius-2); background: var(--surface-page); }
77
- .s-detect__row { display: flex; align-items: center; gap: 10px; }
78
- .s-detect__k { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-label); text-transform: uppercase;
79
- color: var(--text-faint); width: 84px; flex: none; }
80
- .s-detect__v { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-body); word-break: break-all; flex: 1; }
81
- .s-detect__ok { color: var(--ok); display: flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px;
82
- letter-spacing: var(--tracking-label); text-transform: uppercase; flex: none; }
83
-
84
- .s-cap { display: flex; align-items: center; gap: 12px; padding-top: 4px; }
85
- .s-cap__field { display: flex; align-items: center; gap: 8px; }
86
- .s-cap__field .ax-input { width: 132px; }
87
- .s-cap.is-off { opacity: 0.5; }
88
-
89
- .s-map { width: 100%; border-collapse: collapse; font-size: var(--text-xs); }
90
- .s-map th { text-align: left; font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-label);
91
- text-transform: uppercase; color: var(--text-faint); font-weight: 500; padding: 0 0 8px; }
92
- .s-map td { padding: 7px 0; border-top: 1px solid var(--border-default); color: var(--text-body); vertical-align: middle; }
93
- .s-map__from { color: var(--text-muted); }
94
- .s-map__arrow { width: 36px; color: var(--text-faint); }
95
- .s-map__col { font-family: var(--font-mono); }
96
- .s-map__tag { font-family: var(--font-mono); font-size: 9px; letter-spacing: var(--tracking-label); text-transform: uppercase;
97
- color: var(--text-faint); border: 1px solid var(--border-default); border-radius: var(--radius-1); padding: 2px 5px; margin-left: 7px; }
98
-
99
- .s-sub { display: flex; align-items: center; gap: 8px; }
100
- .s-sub__line { flex: 1; height: 1px; background: var(--border-default); }
101
-
102
- .s-save { flex: none; border-top: 1px solid var(--border-default); background: var(--surface-panel); }
103
- .s-save__inner { max-width: 768px; margin: 0 auto; padding: 12px 24px; display: flex; align-items: center; gap: 16px; }
104
- .s-save__status { flex: 1; min-width: 0; display: flex; align-items: center; gap: 9px; font-size: var(--text-sm); color: var(--text-muted); }
105
- .s-save__status svg { flex: none; }
106
- .s-save__actions { display: flex; align-items: center; gap: 8px; flex: none; }
107
- @media (max-width: 520px) { .s-wrap { padding: 24px 16px 36px; } .s-modal__baractions { gap: 6px; } }
108
- `;
109
- if (typeof document !== "undefined" && !document.getElementById("ax-integration-css")) {
110
- const s = document.createElement("style");
111
- s.id = "ax-integration-css";
112
- s.textContent = AX_INTEGRATION_CSS;
113
- document.head.appendChild(s);
114
- }
115
- const S_LS_KEY = "agentaily.integrations.v1";
116
- const s_sleep = (ms) => new Promise((r) => setTimeout(r, ms));
117
- function s_parseFeishu(url) {
118
- if (!url) return null;
119
- const tokenM = url.match(/\/base\/([A-Za-z0-9]+)/) || url.match(/[?&]app_token=([A-Za-z0-9]+)/);
120
- const tableM = url.match(/[?&]table=([A-Za-z0-9]+)/);
121
- if (!tokenM) return null;
122
- return { token: tokenM[1], table: tableM ? tableM[1] : "" };
123
- }
124
- const S_FIELD_MAP = [
125
- { from: "姓名", to: "姓名", tag: null },
126
- { from: "手机号", to: "手机号", tag: null },
127
- { from: "邮箱", to: "邮箱", tag: null },
128
- { from: "报名场次", to: "场次", tag: "单选" },
129
- { from: "备注", to: "备注", tag: null },
130
- { from: null, to: "提交时间", tag: "自动" },
131
- { from: null, to: "来源链接", tag: "自动" }
132
- ];
133
- const S_DEFAULTS = {
134
- dsKey: "",
135
- dsModel: "deepseek-chat",
136
- capOn: false,
137
- cap: "200",
138
- dsStatus: "idle",
139
- dsResult: "",
140
- appId: "",
141
- secret: "",
142
- link: "",
143
- fsStatus: "idle",
144
- fsResult: "",
145
- saved: false
146
- };
147
- function s_normalize(raw) {
148
- const r = raw || {};
149
- return {
150
- dsKey: r.dsKey || "",
151
- dsModel: r.dsModel || "deepseek-chat",
152
- capOn: !!r.capOn,
153
- cap: r.cap || "200",
154
- dsStatus: r.dsStatus === "ok" ? "ok" : "idle",
155
- dsResult: r.dsResult || "",
156
- appId: r.appId || "",
157
- secret: r.secret || "",
158
- link: r.link || "",
159
- fsStatus: r.fsStatus === "ok" ? "ok" : "idle",
160
- fsResult: r.fsResult || "",
161
- saved: !!r.saved
162
- };
163
- }
164
- function IntegrationSettings({
165
- onClose,
166
- showUsageCap = true,
167
- storageKey = S_LS_KEY,
168
- value,
169
- onChange,
170
- onSave,
171
- onTest,
172
- readiness,
173
- masked
174
- }) {
175
- const controlled = value !== void 0;
176
- const load = () => {
177
- try {
178
- return JSON.parse(localStorage.getItem(storageKey)) || {};
179
- } catch (e) {
180
- return {};
181
- }
182
- };
183
- const [localCfg, setLocalCfg] = useState(() => controlled ? S_DEFAULTS : s_normalize(load()));
184
- const cfg = controlled ? { ...S_DEFAULTS, ...value } : localCfg;
185
- const {
186
- dsKey,
187
- dsModel,
188
- capOn,
189
- cap,
190
- dsStatus,
191
- dsResult,
192
- appId,
193
- secret,
194
- link,
195
- fsStatus,
196
- fsResult,
197
- saved
198
- } = cfg;
199
- const cfgRef = useRef(cfg);
200
- cfgRef.current = cfg;
201
- const update = (partial) => {
202
- if (controlled) {
203
- if (onChange) onChange({ ...cfgRef.current, ...partial });
204
- } else setLocalCfg((c) => ({ ...c, ...partial }));
205
- };
206
- const [dirty, setDirty] = useState(false);
207
- const [saving, setSaving] = useState(false);
208
- const [dsKeyEdited, setDsKeyEdited] = useState(false);
209
- const [secretEdited, setSecretEdited] = useState(false);
210
- useEffect(() => {
211
- const onKey = (e) => {
212
- if (e.key === "Escape") onClose && onClose();
213
- };
214
- window.addEventListener("keydown", onKey);
215
- return () => window.removeEventListener("keydown", onKey);
216
- }, [onClose]);
217
- useEffect(() => {
218
- if (controlled) return;
219
- try {
220
- localStorage.setItem(storageKey, JSON.stringify(localCfg));
221
- } catch (e) {
222
- }
223
- }, [controlled, localCfg, storageKey]);
224
- const parsed = s_parseFeishu(link);
225
- const dsConnected = readiness ? !!readiness.deepseek : dsStatus === "ok";
226
- const fsConnected = readiness ? !!readiness.feishu : fsStatus === "ok";
227
- const dsPill = dsStatus === "testing" || dsStatus === "error" ? dsStatus : dsConnected ? "ok" : "idle";
228
- const fsPill = fsStatus === "testing" || fsStatus === "error" ? fsStatus : fsConnected ? "ok" : "idle";
229
- const readyCount = (dsConnected ? 1 : 0) + (fsConnected ? 1 : 0);
230
- const allReady = dsConnected && fsConnected;
231
- const maskedDs = !!(masked && masked.deepseek) && !dsKeyEdited;
232
- const maskedFs = !!(masked && masked.feishu) && !secretEdited;
233
- const setCfg = (partial) => {
234
- update({ ...partial, saved: false });
235
- setDirty(true);
236
- };
237
- const editDs = (key) => (v) => {
238
- const patch = { [key]: v, saved: false };
239
- if (cfgRef.current.dsStatus !== "idle") {
240
- patch.dsStatus = "idle";
241
- patch.dsResult = "";
242
- }
243
- update(patch);
244
- setDirty(true);
245
- };
246
- const editFs = (key) => (v) => {
247
- const patch = { [key]: v, saved: false };
248
- if (cfgRef.current.fsStatus !== "idle") {
249
- patch.fsStatus = "idle";
250
- patch.fsResult = "";
251
- }
252
- update(patch);
253
- setDirty(true);
254
- };
255
- const editDsKey = (v) => {
256
- if (!dsKeyEdited) setDsKeyEdited(true);
257
- editDs("dsKey")(v);
258
- };
259
- const editSecret = (v) => {
260
- if (!secretEdited) setSecretEdited(true);
261
- editFs("secret")(v);
262
- };
263
- const setTest = (which, status, result, finalize) => {
264
- const patch = which === "deepseek" ? { dsStatus: status, dsResult: result } : { fsStatus: status, fsResult: result };
265
- if (finalize) patch.saved = false;
266
- update(patch);
267
- if (finalize) setDirty(true);
268
- };
269
- const builtinTest = async (which) => {
270
- if (which === "deepseek") {
271
- setTest("deepseek", "testing", "", false);
272
- await s_sleep(1300);
273
- const k = cfgRef.current.dsKey.trim();
274
- if (k.startsWith("sk-") && k.length >= 20)
275
- setTest("deepseek", "ok", `连接正常 · 延迟 0.4s · ${cfgRef.current.dsModel}`, true);
276
- else
277
- setTest(
278
- "deepseek",
279
- "error",
280
- k ? "密钥无效或额度不足,请核对后重试" : "请先填写 API Key",
281
- true
282
- );
283
- } else {
284
- setTest("feishu", "testing", "", false);
285
- await s_sleep(1500);
286
- const c = cfgRef.current;
287
- const p = s_parseFeishu(c.link);
288
- if (!c.appId.trim() || !c.secret.trim())
289
- return setTest("feishu", "error", "缺少 App ID 或 App Secret", true);
290
- if (!p) return setTest("feishu", "error", "无法识别多维表格链接,请粘贴完整 URL", true);
291
- if (!p.table) return setTest("feishu", "error", "链接缺少数据表 (table) 参数", true);
292
- setTest("feishu", "ok", "已连接 ·「报名登记表」· 检测到 6 个字段", true);
293
- }
294
- };
295
- const runTest = async (which) => {
296
- if (onTest) {
297
- setTest(which, "testing", "", false);
298
- try {
299
- const res = await onTest(which);
300
- const ok = !!(res && res.ok);
301
- setTest(
302
- which,
303
- ok ? "ok" : "error",
304
- res && res.message || (ok ? "连接正常" : "连接失败"),
305
- true
306
- );
307
- } catch (e) {
308
- setTest(which, "error", e && e.message || "测试失败", true);
309
- }
310
- return;
311
- }
312
- builtinTest(which);
313
- };
314
- const afterSaved = () => {
315
- setDirty(false);
316
- setDsKeyEdited(false);
317
- setSecretEdited(false);
318
- };
319
- const doSave = async () => {
320
- if (onSave) {
321
- setSaving(true);
322
- try {
323
- await onSave(cfgRef.current);
324
- update({ saved: true });
325
- afterSaved();
326
- } catch (e) {
327
- } finally {
328
- setSaving(false);
329
- }
330
- } else {
331
- update({ saved: true });
332
- afterSaved();
333
- }
334
- };
335
- const onDiscard = () => {
336
- setDsKeyEdited(false);
337
- setSecretEdited(false);
338
- setDirty(false);
339
- if (controlled) return;
340
- setLocalCfg(s_normalize(load()));
341
- };
342
- return /* @__PURE__ */ jsx("div", { className: "s-overlay", role: "dialog", "aria-modal": "true", "aria-label": "集成设置", children: /* @__PURE__ */ jsxs("div", { className: "s-modal", children: [
343
- /* @__PURE__ */ jsxs("header", { className: "s-modal__bar", children: [
344
- /* @__PURE__ */ jsxs("div", { className: "s-modal__brand", children: [
345
- /* @__PURE__ */ jsx(BrandMark, { size: 16 }),
346
- /* @__PURE__ */ jsx("span", { className: "s-modal__word", children: "agentaily" }),
347
- /* @__PURE__ */ jsx("span", { className: "s-modal__div", children: "/" }),
348
- /* @__PURE__ */ jsx("span", { className: "s-modal__crumb", children: "集成设置" })
349
- ] }),
350
- /* @__PURE__ */ jsxs("div", { className: "s-modal__baractions", children: [
351
- /* @__PURE__ */ jsx(Badge, { variant: allReady ? "ok" : "neutral", dot: allReady, children: allReady ? "就绪" : "未就绪" }),
352
- /* @__PURE__ */ jsx(IconButton, { label: "关闭", onClick: () => onClose && onClose(), children: /* @__PURE__ */ jsx(Icon, { name: "x", size: 16 }) })
353
- ] })
354
- ] }),
355
- /* @__PURE__ */ jsx("main", { className: "s-modal__body", children: /* @__PURE__ */ jsxs("div", { className: "s-wrap", children: [
356
- /* @__PURE__ */ jsxs("div", { className: "s-hero", children: [
357
- /* @__PURE__ */ jsx("div", { className: "ax-label s-hero__kicker", children: "集成 · INTEGRATIONS" }),
358
- /* @__PURE__ */ jsx("h1", { className: "s-hero__h", children: "连接你的服务" }),
359
- /* @__PURE__ */ jsx("p", { className: "s-hero__p", children: "运行需要两条连接:一把驱动对话的 DeepSeek 密钥,和一张接收数据的飞书多维表格。两项都连通后即可发布。" })
360
- ] }),
361
- /* @__PURE__ */ jsxs("div", { className: "s-ready", children: [
362
- /* @__PURE__ */ jsxs("div", { className: "s-ready__steps", children: [
363
- /* @__PURE__ */ jsx("span", { className: "s-ready__dot" + (dsConnected ? " is-on" : "") }),
364
- /* @__PURE__ */ jsx("span", { className: "s-ready__dot" + (fsConnected ? " is-on" : "") })
365
- ] }),
366
- /* @__PURE__ */ jsx("div", { className: "s-ready__txt", children: allReady ? /* @__PURE__ */ jsxs("span", { children: [
367
- /* @__PURE__ */ jsx("strong", { children: "两项连接均已就绪。" }),
368
- "保存后即可开始运行。"
369
- ] }) : /* @__PURE__ */ jsxs("span", { children: [
370
- "完成下方 ",
371
- /* @__PURE__ */ jsx("strong", { children: "2 项" }),
372
- " 连接后才能发布运行。"
373
- ] }) }),
374
- /* @__PURE__ */ jsxs("span", { className: "s-ready__count", children: [
375
- readyCount,
376
- " / 2"
377
- ] })
378
- ] }),
379
- /* @__PURE__ */ jsxs("div", { className: "s-cards", children: [
380
- /* @__PURE__ */ jsxs(
381
- "section",
382
- {
383
- className: "s-card" + (dsPill === "ok" ? " is-ok" : dsPill === "error" ? " is-error" : ""),
384
- children: [
385
- /* @__PURE__ */ jsxs("div", { className: "s-card__head", children: [
386
- /* @__PURE__ */ jsxs("div", { className: "s-card__toprow", children: [
387
- /* @__PURE__ */ jsx("div", { className: "s-card__icon", children: /* @__PURE__ */ jsx(Icon, { name: "key", size: 16 }) }),
388
- /* @__PURE__ */ jsx("span", { className: "ax-label s-card__eyebrow", children: "对话引擎 · LLM" }),
389
- /* @__PURE__ */ jsx("span", { className: "s-card__status", children: /* @__PURE__ */ jsx(StatusPill, { status: dsPill }) })
390
- ] }),
391
- /* @__PURE__ */ jsx("h2", { className: "s-card__title", children: "DeepSeek" }),
392
- /* @__PURE__ */ jsx("p", { className: "s-card__desc", children: "驱动对话式交互。用户发送的每一条消息,都通过这把密钥调用 DeepSeek 补全。" })
393
- ] }),
394
- /* @__PURE__ */ jsxs("div", { className: "s-card__body", children: [
395
- /* @__PURE__ */ jsx(
396
- SecretField,
397
- {
398
- label: "API KEY",
399
- value: dsKey,
400
- onChange: editDsKey,
401
- placeholder: maskedDs ? "已保存 ········ · 留空则保持不变" : "sk-xxxxxxxxxxxxxxxxxxxxxxxx",
402
- hint: maskedDs ? "已存密钥 · 留空表示不修改,输入新值即覆盖" : void 0,
403
- error: !maskedDs && dsStatus === "error" && !dsKey.trim() ? "此项必填" : void 0
404
- }
405
- ),
406
- /* @__PURE__ */ jsxs("div", { className: "s-lock", children: [
407
- /* @__PURE__ */ jsx(Icon, { name: "lock", size: 14 }),
408
- /* @__PURE__ */ jsxs("span", { children: [
409
- /* @__PURE__ */ jsx("strong", { children: "密钥加密存储" }),
410
- ",仅在服务端发起调用。用户",
411
- /* @__PURE__ */ jsx("strong", { children: "永远看不到、也拿不到这把密钥。" })
412
- ] })
413
- ] }),
414
- /* @__PURE__ */ jsx("div", { className: "s-row2", children: /* @__PURE__ */ jsxs("div", { children: [
415
- /* @__PURE__ */ jsx("label", { className: "s-field__label ax-label", children: "对话模型" }),
416
- /* @__PURE__ */ jsx(
417
- Select,
418
- {
419
- value: dsModel,
420
- onChange: (e) => editDs("dsModel")(e.target.value),
421
- options: [
422
- { value: "deepseek-chat", label: "deepseek-chat · 通用 · 快" },
423
- { value: "deepseek-reasoner", label: "deepseek-reasoner · 深度推理" }
424
- ]
425
- }
426
- )
427
- ] }) }),
428
- showUsageCap ? /* @__PURE__ */ jsxs(React.Fragment, { children: [
429
- /* @__PURE__ */ jsx(
430
- Alert,
431
- {
432
- variant: "warn",
433
- icon: /* @__PURE__ */ jsx(Icon, { name: "zap", size: 15 }),
434
- title: "用量由你承担",
435
- children: "每一轮对话消耗的都是你自己 DeepSeek 账户的额度。用得越多,调用越多——建议设置每月上限以防意外超支。"
436
- }
437
- ),
438
- /* @__PURE__ */ jsxs("div", { className: "s-cap" + (capOn ? "" : " is-off"), children: [
439
- /* @__PURE__ */ jsx(
440
- Switch,
441
- {
442
- label: "启用每月用量上限",
443
- checked: capOn,
444
- onChange: (e) => setCfg({ capOn: e.target.checked })
445
- }
446
- ),
447
- /* @__PURE__ */ jsxs("div", { className: "s-cap__field", style: { display: capOn ? "flex" : "none" }, children: [
448
- /* @__PURE__ */ jsx(
449
- "input",
450
- {
451
- className: "ax-input ax-input--mono",
452
- type: "text",
453
- inputMode: "numeric",
454
- value: cap,
455
- onChange: (e) => setCfg({ cap: e.target.value.replace(/[^0-9]/g, "") }),
456
- "aria-label": "每月上限(元)"
457
- }
458
- ),
459
- /* @__PURE__ */ jsx("span", { className: "s-field__hint", style: { margin: 0 }, children: "元 / 月,达到后暂停对话" })
460
- ] })
461
- ] })
462
- ] }) : null,
463
- /* @__PURE__ */ jsx(
464
- HelpSteps,
465
- {
466
- title: "如何获取 DeepSeek API Key?",
467
- steps: [
468
- /* @__PURE__ */ jsxs(Fragment, { children: [
469
- "登录 ",
470
- /* @__PURE__ */ jsx("code", { children: "platform.deepseek.com" }),
471
- ",进入「API Keys」页面。"
472
- ] }),
473
- /* @__PURE__ */ jsx(Fragment, { children: "点击「创建 API Key」,命名后立即复制——密钥只在创建时完整显示一次。" }),
474
- /* @__PURE__ */ jsx(Fragment, { children: "在「充值」中确认账户余额充足,对话才能持续调用。" }),
475
- /* @__PURE__ */ jsxs(Fragment, { children: [
476
- "把以 ",
477
- /* @__PURE__ */ jsx("code", { children: "sk-" }),
478
- " 开头的密钥粘贴到上方输入框。"
479
- ] })
480
- ],
481
- link: {
482
- href: "https://platform.deepseek.com",
483
- label: "打开 DeepSeek 开放平台"
484
- }
485
- }
486
- )
487
- ] }),
488
- /* @__PURE__ */ jsx(
489
- TestRow,
490
- {
491
- status: dsStatus,
492
- result: dsResult,
493
- onTest: () => runTest("deepseek"),
494
- disabled: !dsKey.trim() && !maskedDs,
495
- idleHint: "填写密钥后测试连通性"
496
- }
497
- )
498
- ]
499
- }
500
- ),
501
- /* @__PURE__ */ jsxs(
502
- "section",
503
- {
504
- className: "s-card" + (fsPill === "ok" ? " is-ok" : fsPill === "error" ? " is-error" : ""),
505
- children: [
506
- /* @__PURE__ */ jsxs("div", { className: "s-card__head", children: [
507
- /* @__PURE__ */ jsxs("div", { className: "s-card__toprow", children: [
508
- /* @__PURE__ */ jsx("div", { className: "s-card__icon", children: /* @__PURE__ */ jsx(Icon, { name: "table", size: 16 }) }),
509
- /* @__PURE__ */ jsx("span", { className: "ax-label s-card__eyebrow", children: "数据写入 · FEISHU BITABLE" }),
510
- /* @__PURE__ */ jsx("span", { className: "s-card__status", children: /* @__PURE__ */ jsx(StatusPill, { status: fsPill }) })
511
- ] }),
512
- /* @__PURE__ */ jsx("h2", { className: "s-card__title", children: "飞书多维表格" }),
513
- /* @__PURE__ */ jsx("p", { className: "s-card__desc", children: "每次提交后,数据自动写入指定多维表格的一行。需要一个飞书自建应用的凭证,以及目标表格的链接。" })
514
- ] }),
515
- /* @__PURE__ */ jsxs("div", { className: "s-card__body", children: [
516
- /* @__PURE__ */ jsxs("div", { className: "s-row2", children: [
517
- /* @__PURE__ */ jsxs("div", { children: [
518
- /* @__PURE__ */ jsx("label", { className: "s-field__label ax-label", children: "App ID" }),
519
- /* @__PURE__ */ jsx(
520
- "input",
521
- {
522
- className: "ax-input ax-input--mono",
523
- type: "text",
524
- value: appId,
525
- spellCheck: "false",
526
- placeholder: "cli_xxxxxxxxxxxx",
527
- onChange: (e) => editFs("appId")(e.target.value)
528
- }
529
- ),
530
- /* @__PURE__ */ jsx("p", { className: "s-field__hint", children: "应用标识,可公开。" })
531
- ] }),
532
- /* @__PURE__ */ jsx(
533
- SecretField,
534
- {
535
- label: "App Secret",
536
- value: secret,
537
- onChange: editSecret,
538
- placeholder: maskedFs ? "已保存 ········ · 留空则保持不变" : "••••••••••••••••",
539
- hint: maskedFs ? "已存密钥 · 留空表示不修改" : "应用密钥,加密存储。"
540
- }
541
- )
542
- ] }),
543
- /* @__PURE__ */ jsxs("div", { children: [
544
- /* @__PURE__ */ jsx("label", { className: "s-field__label ax-label", children: "多维表格链接" }),
545
- /* @__PURE__ */ jsx(
546
- "input",
547
- {
548
- className: "ax-input",
549
- type: "text",
550
- value: link,
551
- spellCheck: "false",
552
- placeholder: "https://your-team.feishu.cn/base/bascn…?table=tbl…",
553
- onChange: (e) => editFs("link")(e.target.value)
554
- }
555
- ),
556
- /* @__PURE__ */ jsx("p", { className: "s-field__hint", children: "在多维表格右上角「分享」中复制链接粘贴即可,App Token 与数据表会自动识别。" })
557
- ] }),
558
- parsed ? /* @__PURE__ */ jsxs("div", { className: "s-detect", children: [
559
- /* @__PURE__ */ jsxs("div", { className: "s-detect__row", children: [
560
- /* @__PURE__ */ jsx("span", { className: "s-detect__k", children: "App Token" }),
561
- /* @__PURE__ */ jsx("span", { className: "s-detect__v", children: parsed.token }),
562
- /* @__PURE__ */ jsxs("span", { className: "s-detect__ok", children: [
563
- /* @__PURE__ */ jsx(Icon, { name: "check", size: 12 }),
564
- "已识别"
565
- ] })
566
- ] }),
567
- /* @__PURE__ */ jsxs("div", { className: "s-detect__row", children: [
568
- /* @__PURE__ */ jsx("span", { className: "s-detect__k", children: "数据表" }),
569
- /* @__PURE__ */ jsx("span", { className: "s-detect__v", children: parsed.table || "—" }),
570
- parsed.table ? /* @__PURE__ */ jsxs("span", { className: "s-detect__ok", children: [
571
- /* @__PURE__ */ jsx(Icon, { name: "check", size: 12 }),
572
- "已识别"
573
- ] }) : /* @__PURE__ */ jsxs("span", { className: "s-detect__ok", style: { color: "var(--warn)" }, children: [
574
- /* @__PURE__ */ jsx(Icon, { name: "warn", size: 12 }),
575
- "缺失"
576
- ] })
577
- ] })
578
- ] }) : null,
579
- /* @__PURE__ */ jsxs("div", { className: "s-lock", children: [
580
- /* @__PURE__ */ jsx(Icon, { name: "shield", size: 14 }),
581
- /* @__PURE__ */ jsxs("span", { children: [
582
- /* @__PURE__ */ jsx("strong", { children: "凭证加密存储" }),
583
- ",仅服务端用于写入数据。用户",
584
- /* @__PURE__ */ jsx("strong", { children: "无法接触你的飞书凭证或表格权限。" })
585
- ] })
586
- ] }),
587
- fsConnected ? /* @__PURE__ */ jsxs("div", { children: [
588
- /* @__PURE__ */ jsxs("div", { className: "s-sub", style: { marginBottom: 12 }, children: [
589
- /* @__PURE__ */ jsx("span", { className: "ax-label", children: "字段映射 · 自动" }),
590
- /* @__PURE__ */ jsx("span", { className: "s-sub__line" })
591
- ] }),
592
- /* @__PURE__ */ jsxs("table", { className: "s-map", children: [
593
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
594
- /* @__PURE__ */ jsx("th", { children: "字段" }),
595
- /* @__PURE__ */ jsx("th", { className: "s-map__arrow" }),
596
- /* @__PURE__ */ jsx("th", { children: "表格列" })
597
- ] }) }),
598
- /* @__PURE__ */ jsx("tbody", { children: S_FIELD_MAP.map((m, i) => /* @__PURE__ */ jsxs("tr", { children: [
599
- /* @__PURE__ */ jsx("td", { className: "s-map__from", children: m.from || /* @__PURE__ */ jsx("span", { style: { color: "var(--text-faint)" }, children: "—" }) }),
600
- /* @__PURE__ */ jsx("td", { className: "s-map__arrow", children: /* @__PURE__ */ jsx(Icon, { name: "arrow", size: 14 }) }),
601
- /* @__PURE__ */ jsxs("td", { children: [
602
- /* @__PURE__ */ jsx("span", { className: "s-map__col", children: m.to }),
603
- m.tag ? /* @__PURE__ */ jsx("span", { className: "s-map__tag", children: m.tag }) : null
604
- ] })
605
- ] }, i)) })
606
- ] })
607
- ] }) : null,
608
- /* @__PURE__ */ jsx(
609
- HelpSteps,
610
- {
611
- title: "如何获取飞书应用凭证?",
612
- steps: [
613
- /* @__PURE__ */ jsxs(Fragment, { children: [
614
- "打开飞书开放平台 ",
615
- /* @__PURE__ */ jsx("code", { children: "open.feishu.cn" }),
616
- ",创建一个「企业自建应用」。"
617
- ] }),
618
- /* @__PURE__ */ jsxs(Fragment, { children: [
619
- "在「凭证与基础信息」中复制 ",
620
- /* @__PURE__ */ jsx("code", { children: "App ID" }),
621
- " 与 ",
622
- /* @__PURE__ */ jsx("code", { children: "App Secret" }),
623
- "。"
624
- ] }),
625
- /* @__PURE__ */ jsxs(Fragment, { children: [
626
- "到「权限管理」开通多维表格读写权限 ",
627
- /* @__PURE__ */ jsx("code", { children: "bitable:app" }),
628
- ",并发布版本。"
629
- ] }),
630
- /* @__PURE__ */ jsxs(Fragment, { children: [
631
- "在目标多维表格里,把这个应用添加为",
632
- /* @__PURE__ */ jsx("strong", { children: "可编辑协作者" }),
633
- "。"
634
- ] }),
635
- /* @__PURE__ */ jsx(Fragment, { children: "复制该多维表格的分享链接,粘贴到上方「多维表格链接」。" })
636
- ],
637
- link: { href: "https://open.feishu.cn", label: "打开飞书开放平台" }
638
- }
639
- )
640
- ] }),
641
- /* @__PURE__ */ jsx(
642
- TestRow,
643
- {
644
- status: fsStatus,
645
- result: fsResult,
646
- onTest: () => runTest("feishu"),
647
- disabled: !appId.trim() || !secret.trim() && !maskedFs || !parsed,
648
- idleHint: "填写凭证与链接后测试写入权限"
649
- }
650
- )
651
- ]
652
- }
653
- )
654
- ] })
655
- ] }) }),
656
- /* @__PURE__ */ jsx("footer", { className: "s-save", children: /* @__PURE__ */ jsxs("div", { className: "s-save__inner", children: [
657
- /* @__PURE__ */ jsx("div", { className: "s-save__status", children: !allReady ? /* @__PURE__ */ jsxs(Fragment, { children: [
658
- /* @__PURE__ */ jsx(Icon, { name: "info", size: 15, style: { color: "var(--text-faint)" } }),
659
- /* @__PURE__ */ jsx("span", { children: "完成两项连接后即可保存并启用。" })
660
- ] }) : saved ? /* @__PURE__ */ jsxs(Fragment, { children: [
661
- /* @__PURE__ */ jsx(Icon, { name: "check", size: 15, style: { color: "var(--ok)" } }),
662
- /* @__PURE__ */ jsx("span", { children: "已保存 · 已启用对话与数据写入。" })
663
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
664
- /* @__PURE__ */ jsx(Icon, { name: "zap", size: 15, style: { color: "var(--text-muted)" } }),
665
- /* @__PURE__ */ jsx("span", { children: "配置已就绪,保存后立即生效。" })
666
- ] }) }),
667
- /* @__PURE__ */ jsxs("div", { className: "s-save__actions", children: [
668
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "md", disabled: !dirty, onClick: onDiscard, children: "放弃更改" }),
669
- /* @__PURE__ */ jsx(
670
- Button,
671
- {
672
- variant: "primary",
673
- size: "md",
674
- icon: saving ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : /* @__PURE__ */ jsx(Icon, { name: "save", size: 14 }),
675
- disabled: !allReady || !dirty || saving,
676
- "aria-busy": saving || void 0,
677
- onClick: doSave,
678
- children: "保存配置"
679
- }
680
- )
681
- ] })
682
- ] }) })
683
- ] }) });
684
- }
685
- export {
686
- IntegrationSettings
687
- };
688
- //# sourceMappingURL=IntegrationSettings.js.map