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