@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.
- package/DESIGN.md +5 -5
- package/README.md +2 -2
- package/dist/components/auth/SignInPage.d.ts +1 -1
- package/dist/components/chat/Markdown.d.ts +30 -0
- package/dist/components/chat/Markdown.js +502 -0
- package/dist/components/chat/Markdown.js.map +1 -0
- package/dist/components/chat/Message.d.ts +6 -0
- package/dist/components/chat/Message.js +11 -1
- package/dist/components/chat/Message.js.map +1 -1
- package/dist/components/layout/DesignerShell.d.ts +1 -1
- package/dist/components/layout/DesignerShell.js.map +1 -1
- package/dist/components/settings/DeepSeekCard.d.ts +65 -0
- package/dist/components/settings/DeepSeekCard.js +197 -0
- package/dist/components/settings/DeepSeekCard.js.map +1 -0
- package/dist/components/settings/FeishuCard.d.ts +65 -0
- package/dist/components/settings/FeishuCard.js +271 -0
- package/dist/components/settings/FeishuCard.js.map +1 -0
- package/dist/components/utilities/BrandMark.d.ts +1 -1
- package/dist/components/utilities/BrandMark.js +1 -1
- package/dist/components/utilities/BrandMark.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/components/settings/IntegrationSettings.d.ts +0 -84
- package/dist/components/settings/IntegrationSettings.js +0 -688
- package/dist/components/settings/IntegrationSettings.js.map +0 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { HelpSteps } from "./HelpSteps.js";
|
|
4
|
+
import { Icon } from "../utilities/Icon.js";
|
|
5
|
+
import { SecretField } from "../inputs/SecretField.js";
|
|
6
|
+
import { StatusPill } from "../display/StatusPill.js";
|
|
7
|
+
import { TestRow } from "./TestRow.js";
|
|
8
|
+
const AX_FEISHUCARD_CSS = `
|
|
9
|
+
.s-detect { display: flex; flex-direction: column; gap: 9px; padding: 13px 14px; border: 1px solid var(--border-default);
|
|
10
|
+
border-radius: var(--radius-2); background: var(--surface-page); }
|
|
11
|
+
.s-detect__row { display: flex; align-items: center; gap: 10px; }
|
|
12
|
+
.s-detect__k { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-label); text-transform: uppercase;
|
|
13
|
+
color: var(--text-faint); width: 84px; flex: none; }
|
|
14
|
+
.s-detect__v { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-body); word-break: break-all; flex: 1; }
|
|
15
|
+
.s-detect__ok { color: var(--ok); display: flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px;
|
|
16
|
+
letter-spacing: var(--tracking-label); text-transform: uppercase; flex: none; }
|
|
17
|
+
.s-map { width: 100%; border-collapse: collapse; font-size: var(--text-xs); }
|
|
18
|
+
.s-map th { text-align: left; font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-label);
|
|
19
|
+
text-transform: uppercase; color: var(--text-faint); font-weight: 500; padding: 0 0 8px; }
|
|
20
|
+
.s-map td { padding: 7px 0; border-top: 1px solid var(--border-default); color: var(--text-body); vertical-align: middle; }
|
|
21
|
+
.s-map__from { color: var(--text-muted); }
|
|
22
|
+
.s-map__arrow { width: 36px; color: var(--text-faint); }
|
|
23
|
+
.s-map__col { font-family: var(--font-mono); }
|
|
24
|
+
.s-map__tag { font-family: var(--font-mono); font-size: 9px; letter-spacing: var(--tracking-label); text-transform: uppercase;
|
|
25
|
+
color: var(--text-faint); border: 1px solid var(--border-default); border-radius: var(--radius-1); padding: 2px 5px; margin-left: 7px; }
|
|
26
|
+
.s-sub { display: flex; align-items: center; gap: 8px; }
|
|
27
|
+
.s-sub__line { flex: 1; height: 1px; background: var(--border-default); }
|
|
28
|
+
`;
|
|
29
|
+
const AX_CONNCARD_CSS_FS = `
|
|
30
|
+
.s-card { position: relative; background: var(--surface-card); border: 1px solid var(--border-default);
|
|
31
|
+
border-radius: var(--radius-3); overflow: hidden; transition: border-color var(--dur-2) var(--ease-out); }
|
|
32
|
+
.s-card.is-ok { border-color: rgba(62, 207, 142, 0.4); }
|
|
33
|
+
.s-card.is-error { border-color: rgba(229, 72, 77, 0.4); }
|
|
34
|
+
.s-card__head { padding: 20px 24px 18px; }
|
|
35
|
+
.s-card__toprow { display: flex; align-items: center; gap: 11px; margin-bottom: 15px; }
|
|
36
|
+
.s-card__icon { flex: none; width: 30px; height: 30px; border-radius: var(--radius-2);
|
|
37
|
+
border: 1px solid var(--border-strong); background: var(--surface-raised); color: var(--text-body);
|
|
38
|
+
display: flex; align-items: center; justify-content: center; }
|
|
39
|
+
.s-card__eyebrow { color: var(--text-faint); }
|
|
40
|
+
.s-card__status { margin-left: auto; flex: none; }
|
|
41
|
+
.s-card__title { font-family: var(--font-display); font-size: var(--text-xl); font-weight: 500;
|
|
42
|
+
letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); color: var(--text-body); margin: 0; }
|
|
43
|
+
.s-card__desc { font-size: var(--text-sm); color: var(--text-muted); line-height: var(--leading-snug); margin: 9px 0 0; max-width: 58ch; }
|
|
44
|
+
.s-card__body { padding: 4px 24px 22px; display: flex; flex-direction: column; gap: 18px; }
|
|
45
|
+
.s-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
46
|
+
@media (max-width: 560px) { .s-row2 { grid-template-columns: 1fr; } }
|
|
47
|
+
.s-lock { display: flex; align-items: flex-start; gap: 8px; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); }
|
|
48
|
+
.s-lock svg { flex: none; margin-top: 1px; color: var(--text-muted); }
|
|
49
|
+
.s-lock strong { color: var(--text-muted); font-weight: 500; }
|
|
50
|
+
`;
|
|
51
|
+
function fs_injectCss(id, css) {
|
|
52
|
+
if (typeof document === "undefined" || document.getElementById(id)) return;
|
|
53
|
+
const s = document.createElement("style");
|
|
54
|
+
s.id = id;
|
|
55
|
+
s.textContent = css;
|
|
56
|
+
document.head.appendChild(s);
|
|
57
|
+
}
|
|
58
|
+
fs_injectCss("ax-conncard-css", AX_CONNCARD_CSS_FS);
|
|
59
|
+
fs_injectCss("ax-feishucard-css", AX_FEISHUCARD_CSS);
|
|
60
|
+
function parseFeishuLink(url) {
|
|
61
|
+
if (!url) return null;
|
|
62
|
+
const tokenM = url.match(/\/base\/([A-Za-z0-9]+)/) || url.match(/[?&]app_token=([A-Za-z0-9]+)/);
|
|
63
|
+
const tableM = url.match(/[?&]table=([A-Za-z0-9]+)/);
|
|
64
|
+
if (!tokenM) return null;
|
|
65
|
+
return { token: tokenM[1], table: tableM ? tableM[1] : "" };
|
|
66
|
+
}
|
|
67
|
+
const FS_DEFAULT_FIELD_MAP = [
|
|
68
|
+
{ from: "姓名", to: "姓名", tag: null },
|
|
69
|
+
{ from: "手机号", to: "手机号", tag: null },
|
|
70
|
+
{ from: "邮箱", to: "邮箱", tag: null },
|
|
71
|
+
{ from: "报名场次", to: "场次", tag: "单选" },
|
|
72
|
+
{ from: "备注", to: "备注", tag: null },
|
|
73
|
+
{ from: null, to: "提交时间", tag: "自动" },
|
|
74
|
+
{ from: null, to: "来源链接", tag: "自动" }
|
|
75
|
+
];
|
|
76
|
+
const FS_DEFAULT_HELP = {
|
|
77
|
+
title: "如何获取飞书应用凭证?",
|
|
78
|
+
steps: [
|
|
79
|
+
/* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
80
|
+
"打开飞书开放平台 ",
|
|
81
|
+
/* @__PURE__ */ jsx("code", { children: "open.feishu.cn" }),
|
|
82
|
+
",创建一个「企业自建应用」。"
|
|
83
|
+
] }),
|
|
84
|
+
/* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
85
|
+
"在「凭证与基础信息」中复制 ",
|
|
86
|
+
/* @__PURE__ */ jsx("code", { children: "App ID" }),
|
|
87
|
+
" 与 ",
|
|
88
|
+
/* @__PURE__ */ jsx("code", { children: "App Secret" }),
|
|
89
|
+
"。"
|
|
90
|
+
] }),
|
|
91
|
+
/* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
92
|
+
"到「权限管理」开通多维表格读写权限 ",
|
|
93
|
+
/* @__PURE__ */ jsx("code", { children: "bitable:app" }),
|
|
94
|
+
",并发布版本。"
|
|
95
|
+
] }),
|
|
96
|
+
/* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
97
|
+
"在目标多维表格里,把这个应用添加为",
|
|
98
|
+
/* @__PURE__ */ jsx("strong", { children: "可编辑协作者" }),
|
|
99
|
+
"。"
|
|
100
|
+
] }),
|
|
101
|
+
/* @__PURE__ */ jsx(React.Fragment, { children: "复制该多维表格的分享链接,粘贴到上方「多维表格链接」。" })
|
|
102
|
+
],
|
|
103
|
+
link: { href: "https://open.feishu.cn", label: "打开飞书开放平台" }
|
|
104
|
+
};
|
|
105
|
+
function FeishuCard({
|
|
106
|
+
appId = "",
|
|
107
|
+
onAppIdChange,
|
|
108
|
+
secret = "",
|
|
109
|
+
onSecretChange,
|
|
110
|
+
link = "",
|
|
111
|
+
onLinkChange,
|
|
112
|
+
status = "idle",
|
|
113
|
+
result,
|
|
114
|
+
onTest,
|
|
115
|
+
masked = false,
|
|
116
|
+
appIdError,
|
|
117
|
+
secretError,
|
|
118
|
+
linkError,
|
|
119
|
+
fieldMap = FS_DEFAULT_FIELD_MAP,
|
|
120
|
+
showMapping,
|
|
121
|
+
help,
|
|
122
|
+
canTest,
|
|
123
|
+
idleHint = "填写凭证与链接后测试写入权限"
|
|
124
|
+
}) {
|
|
125
|
+
const parsed = parseFeishuLink(link);
|
|
126
|
+
const maskedNow = masked && !(secret || "").trim();
|
|
127
|
+
const testDisabled = canTest !== void 0 ? !canTest : !(appId || "").trim() || !(secret || "").trim() && !masked || !parsed;
|
|
128
|
+
const showMap = showMapping !== void 0 ? showMapping : status === "ok";
|
|
129
|
+
const help_ = help || FS_DEFAULT_HELP;
|
|
130
|
+
return /* @__PURE__ */ jsxs(
|
|
131
|
+
"section",
|
|
132
|
+
{
|
|
133
|
+
className: "s-card" + (status === "ok" ? " is-ok" : status === "error" ? " is-error" : ""),
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ jsxs("div", { className: "s-card__head", children: [
|
|
136
|
+
/* @__PURE__ */ jsxs("div", { className: "s-card__toprow", children: [
|
|
137
|
+
/* @__PURE__ */ jsx("div", { className: "s-card__icon", children: /* @__PURE__ */ jsx(Icon, { name: "table", size: 16 }) }),
|
|
138
|
+
/* @__PURE__ */ jsx("span", { className: "ax-label s-card__eyebrow", children: "数据写入 · FEISHU BITABLE" }),
|
|
139
|
+
/* @__PURE__ */ jsx("span", { className: "s-card__status", children: /* @__PURE__ */ jsx(StatusPill, { status }) })
|
|
140
|
+
] }),
|
|
141
|
+
/* @__PURE__ */ jsx("h2", { className: "s-card__title", children: "飞书多维表格" }),
|
|
142
|
+
/* @__PURE__ */ jsx("p", { className: "s-card__desc", children: "每次提交后,数据自动写入指定多维表格的一行。需要一个飞书自建应用的凭证,以及目标表格的链接。" })
|
|
143
|
+
] }),
|
|
144
|
+
/* @__PURE__ */ jsxs("div", { className: "s-card__body", children: [
|
|
145
|
+
/* @__PURE__ */ jsxs("div", { className: "s-row2", children: [
|
|
146
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
147
|
+
/* @__PURE__ */ jsx("label", { className: "s-field__label ax-label", children: "App ID" }),
|
|
148
|
+
/* @__PURE__ */ jsx(
|
|
149
|
+
"input",
|
|
150
|
+
{
|
|
151
|
+
className: "ax-input ax-input--mono" + (appIdError ? " ax-input--error" : ""),
|
|
152
|
+
type: "text",
|
|
153
|
+
value: appId,
|
|
154
|
+
spellCheck: "false",
|
|
155
|
+
placeholder: "cli_xxxxxxxxxxxx",
|
|
156
|
+
onChange: (e) => onAppIdChange && onAppIdChange(e.target.value)
|
|
157
|
+
}
|
|
158
|
+
),
|
|
159
|
+
appIdError ? /* @__PURE__ */ jsx("p", { className: "s-field__hint s-field__hint--err", children: appIdError }) : /* @__PURE__ */ jsx("p", { className: "s-field__hint", children: "应用标识,可公开。" })
|
|
160
|
+
] }),
|
|
161
|
+
/* @__PURE__ */ jsx(
|
|
162
|
+
SecretField,
|
|
163
|
+
{
|
|
164
|
+
label: "App Secret",
|
|
165
|
+
value: secret,
|
|
166
|
+
onChange: onSecretChange,
|
|
167
|
+
placeholder: maskedNow ? "已保存 ········ · 留空则保持不变" : "••••••••••••••••",
|
|
168
|
+
hint: maskedNow ? "已存密钥 · 留空表示不修改" : "应用密钥,加密存储。",
|
|
169
|
+
error: secretError
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
] }),
|
|
173
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
174
|
+
/* @__PURE__ */ jsx("label", { className: "s-field__label ax-label", children: "多维表格链接" }),
|
|
175
|
+
/* @__PURE__ */ jsx(
|
|
176
|
+
"input",
|
|
177
|
+
{
|
|
178
|
+
className: "ax-input" + (linkError ? " ax-input--error" : ""),
|
|
179
|
+
type: "text",
|
|
180
|
+
value: link,
|
|
181
|
+
spellCheck: "false",
|
|
182
|
+
placeholder: "https://your-team.feishu.cn/base/bascn…?table=tbl…",
|
|
183
|
+
onChange: (e) => onLinkChange && onLinkChange(e.target.value)
|
|
184
|
+
}
|
|
185
|
+
),
|
|
186
|
+
linkError ? /* @__PURE__ */ jsx("p", { className: "s-field__hint s-field__hint--err", children: linkError }) : /* @__PURE__ */ jsx("p", { className: "s-field__hint", children: "在多维表格右上角「分享」中复制链接粘贴即可,App Token 与数据表会自动识别。" })
|
|
187
|
+
] }),
|
|
188
|
+
parsed ? /* @__PURE__ */ jsxs("div", { className: "s-detect", children: [
|
|
189
|
+
/* @__PURE__ */ jsxs("div", { className: "s-detect__row", children: [
|
|
190
|
+
/* @__PURE__ */ jsx("span", { className: "s-detect__k", children: "App Token" }),
|
|
191
|
+
/* @__PURE__ */ jsx("span", { className: "s-detect__v", children: parsed.token }),
|
|
192
|
+
/* @__PURE__ */ jsxs("span", { className: "s-detect__ok", children: [
|
|
193
|
+
/* @__PURE__ */ jsx(Icon, { name: "check", size: 12 }),
|
|
194
|
+
"已识别"
|
|
195
|
+
] })
|
|
196
|
+
] }),
|
|
197
|
+
/* @__PURE__ */ jsxs("div", { className: "s-detect__row", children: [
|
|
198
|
+
/* @__PURE__ */ jsx("span", { className: "s-detect__k", children: "数据表" }),
|
|
199
|
+
/* @__PURE__ */ jsx("span", { className: "s-detect__v", children: parsed.table || "—" }),
|
|
200
|
+
parsed.table ? /* @__PURE__ */ jsxs("span", { className: "s-detect__ok", children: [
|
|
201
|
+
/* @__PURE__ */ jsx(Icon, { name: "check", size: 12 }),
|
|
202
|
+
"已识别"
|
|
203
|
+
] }) : /* @__PURE__ */ jsxs("span", { className: "s-detect__ok", style: { color: "var(--warn)" }, children: [
|
|
204
|
+
/* @__PURE__ */ jsx(Icon, { name: "warn", size: 12 }),
|
|
205
|
+
"缺失"
|
|
206
|
+
] })
|
|
207
|
+
] })
|
|
208
|
+
] }) : null,
|
|
209
|
+
/* @__PURE__ */ jsxs("div", { className: "s-lock", children: [
|
|
210
|
+
/* @__PURE__ */ jsx(Icon, { name: "shield", size: 14 }),
|
|
211
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
212
|
+
/* @__PURE__ */ jsx("strong", { children: "凭证加密存储" }),
|
|
213
|
+
",仅服务端用于写入数据。用户",
|
|
214
|
+
/* @__PURE__ */ jsx("strong", { children: "无法接触你的飞书凭证或表格权限。" })
|
|
215
|
+
] })
|
|
216
|
+
] }),
|
|
217
|
+
showMap ? /* @__PURE__ */ jsxs("div", { children: [
|
|
218
|
+
/* @__PURE__ */ jsxs("div", { className: "s-sub", style: { marginBottom: 12 }, children: [
|
|
219
|
+
/* @__PURE__ */ jsx("span", { className: "ax-label", children: "字段映射 · 自动" }),
|
|
220
|
+
/* @__PURE__ */ jsx("span", { className: "s-sub__line" })
|
|
221
|
+
] }),
|
|
222
|
+
/* @__PURE__ */ jsxs("table", { className: "s-map", children: [
|
|
223
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
224
|
+
/* @__PURE__ */ jsx("th", { children: "字段" }),
|
|
225
|
+
/* @__PURE__ */ jsx("th", { className: "s-map__arrow" }),
|
|
226
|
+
/* @__PURE__ */ jsx("th", { children: "表格列" })
|
|
227
|
+
] }) }),
|
|
228
|
+
/* @__PURE__ */ jsx("tbody", { children: fieldMap.map((m, i) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
229
|
+
/* @__PURE__ */ jsx("td", { className: "s-map__from", children: m.from || /* @__PURE__ */ jsx("span", { style: { color: "var(--text-faint)" }, children: "—" }) }),
|
|
230
|
+
/* @__PURE__ */ jsx("td", { className: "s-map__arrow", children: /* @__PURE__ */ jsx(Icon, { name: "arrow", size: 14 }) }),
|
|
231
|
+
/* @__PURE__ */ jsxs("td", { children: [
|
|
232
|
+
/* @__PURE__ */ jsx("span", { className: "s-map__col", children: m.to }),
|
|
233
|
+
m.tag ? /* @__PURE__ */ jsx("span", { className: "s-map__tag", children: m.tag }) : null
|
|
234
|
+
] })
|
|
235
|
+
] }, i)) })
|
|
236
|
+
] })
|
|
237
|
+
] }) : null,
|
|
238
|
+
/* @__PURE__ */ jsx(FeishuHelpSlot, { help: help_ })
|
|
239
|
+
] }),
|
|
240
|
+
/* @__PURE__ */ jsx(
|
|
241
|
+
TestRow,
|
|
242
|
+
{
|
|
243
|
+
status,
|
|
244
|
+
result,
|
|
245
|
+
onTest,
|
|
246
|
+
disabled: testDisabled,
|
|
247
|
+
idleHint
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
function FeishuHelpSlot({ help }) {
|
|
255
|
+
if (!help) return null;
|
|
256
|
+
if (React.isValidElement(help)) return help;
|
|
257
|
+
return /* @__PURE__ */ jsx(
|
|
258
|
+
HelpSteps,
|
|
259
|
+
{
|
|
260
|
+
title: help.title,
|
|
261
|
+
steps: help.steps,
|
|
262
|
+
link: help.link,
|
|
263
|
+
defaultOpen: help.defaultOpen
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
export {
|
|
268
|
+
FeishuCard,
|
|
269
|
+
parseFeishuLink
|
|
270
|
+
};
|
|
271
|
+
//# sourceMappingURL=FeishuCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FeishuCard.js","sources":["../../../src/components/settings/FeishuCard.jsx"],"sourcesContent":["import React from \"react\";\nimport { HelpSteps } from \"./HelpSteps.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\nimport { SecretField } from \"../inputs/SecretField.jsx\";\nimport { StatusPill } from \"../display/StatusPill.jsx\";\nimport { TestRow } from \"./TestRow.jsx\";\n\n// FeishuCard — a PURE-DISPLAY connection card for a Feishu (Lark) Bitable data\n// sink. Props in (appId / secret / link, status, result, masked, errors,\n// fieldMap, help…), events out (onAppIdChange / onSecretChange / onLinkChange /\n// onTest). Zero state, zero localStorage, zero save bar, zero gating — the\n// caller owns all of that. The only thing it derives locally is the App\n// Token / table read-out parsed from the share link (pure display). Composes\n// SecretField / StatusPill / TestRow / HelpSteps.\nconst AX_FEISHUCARD_CSS = `\n.s-detect { display: flex; flex-direction: column; gap: 9px; padding: 13px 14px; border: 1px solid var(--border-default);\n border-radius: var(--radius-2); background: var(--surface-page); }\n.s-detect__row { display: flex; align-items: center; gap: 10px; }\n.s-detect__k { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-label); text-transform: uppercase;\n color: var(--text-faint); width: 84px; flex: none; }\n.s-detect__v { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-body); word-break: break-all; flex: 1; }\n.s-detect__ok { color: var(--ok); display: flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px;\n letter-spacing: var(--tracking-label); text-transform: uppercase; flex: none; }\n.s-map { width: 100%; border-collapse: collapse; font-size: var(--text-xs); }\n.s-map th { text-align: left; font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-label);\n text-transform: uppercase; color: var(--text-faint); font-weight: 500; padding: 0 0 8px; }\n.s-map td { padding: 7px 0; border-top: 1px solid var(--border-default); color: var(--text-body); vertical-align: middle; }\n.s-map__from { color: var(--text-muted); }\n.s-map__arrow { width: 36px; color: var(--text-faint); }\n.s-map__col { font-family: var(--font-mono); }\n.s-map__tag { font-family: var(--font-mono); font-size: 9px; letter-spacing: var(--tracking-label); text-transform: uppercase;\n color: var(--text-faint); border: 1px solid var(--border-default); border-radius: var(--radius-1); padding: 2px 5px; margin-left: 7px; }\n.s-sub { display: flex; align-items: center; gap: 8px; }\n.s-sub__line { flex: 1; height: 1px; background: var(--border-default); }\n`;\n\n// Shared connection-card shell, mirrored from DeepSeekCard so this card renders\n// correctly when mounted on its own. Guarded by a single id — first writer wins.\nconst AX_CONNCARD_CSS_FS = `\n.s-card { position: relative; background: var(--surface-card); border: 1px solid var(--border-default);\n border-radius: var(--radius-3); overflow: hidden; transition: border-color var(--dur-2) var(--ease-out); }\n.s-card.is-ok { border-color: rgba(62, 207, 142, 0.4); }\n.s-card.is-error { border-color: rgba(229, 72, 77, 0.4); }\n.s-card__head { padding: 20px 24px 18px; }\n.s-card__toprow { display: flex; align-items: center; gap: 11px; margin-bottom: 15px; }\n.s-card__icon { flex: none; width: 30px; height: 30px; border-radius: var(--radius-2);\n border: 1px solid var(--border-strong); background: var(--surface-raised); color: var(--text-body);\n display: flex; align-items: center; justify-content: center; }\n.s-card__eyebrow { color: var(--text-faint); }\n.s-card__status { margin-left: auto; flex: none; }\n.s-card__title { font-family: var(--font-display); font-size: var(--text-xl); font-weight: 500;\n letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); color: var(--text-body); margin: 0; }\n.s-card__desc { font-size: var(--text-sm); color: var(--text-muted); line-height: var(--leading-snug); margin: 9px 0 0; max-width: 58ch; }\n.s-card__body { padding: 4px 24px 22px; display: flex; flex-direction: column; gap: 18px; }\n.s-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n@media (max-width: 560px) { .s-row2 { grid-template-columns: 1fr; } }\n.s-lock { display: flex; align-items: flex-start; gap: 8px; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); }\n.s-lock svg { flex: none; margin-top: 1px; color: var(--text-muted); }\n.s-lock strong { color: var(--text-muted); font-weight: 500; }\n`;\n\nfunction fs_injectCss(id, css) {\n if (typeof document === \"undefined\" || document.getElementById(id)) return;\n const s = document.createElement(\"style\");\n s.id = id;\n s.textContent = css;\n document.head.appendChild(s);\n}\nfs_injectCss(\"ax-conncard-css\", AX_CONNCARD_CSS_FS);\nfs_injectCss(\"ax-feishucard-css\", AX_FEISHUCARD_CSS);\n\n// Parse the App Token + data-table id out of a Bitable share URL — pure display.\nexport function parseFeishuLink(url) {\n if (!url) return null;\n const tokenM = url.match(/\\/base\\/([A-Za-z0-9]+)/) || url.match(/[?&]app_token=([A-Za-z0-9]+)/);\n const tableM = url.match(/[?&]table=([A-Za-z0-9]+)/);\n if (!tokenM) return null;\n return { token: tokenM[1], table: tableM ? tableM[1] : \"\" };\n}\n\nconst FS_DEFAULT_FIELD_MAP = [\n { from: \"姓名\", to: \"姓名\", tag: null },\n { from: \"手机号\", to: \"手机号\", tag: null },\n { from: \"邮箱\", to: \"邮箱\", tag: null },\n { from: \"报名场次\", to: \"场次\", tag: \"单选\" },\n { from: \"备注\", to: \"备注\", tag: null },\n { from: null, to: \"提交时间\", tag: \"自动\" },\n { from: null, to: \"来源链接\", tag: \"自动\" },\n];\n\nconst FS_DEFAULT_HELP = {\n title: \"如何获取飞书应用凭证?\",\n steps: [\n <React.Fragment>\n 打开飞书开放平台 <code>open.feishu.cn</code>,创建一个「企业自建应用」。\n </React.Fragment>,\n <React.Fragment>\n 在「凭证与基础信息」中复制 <code>App ID</code> 与 <code>App Secret</code>。\n </React.Fragment>,\n <React.Fragment>\n 到「权限管理」开通多维表格读写权限 <code>bitable:app</code>,并发布版本。\n </React.Fragment>,\n <React.Fragment>\n 在目标多维表格里,把这个应用添加为<strong>可编辑协作者</strong>。\n </React.Fragment>,\n <React.Fragment>复制该多维表格的分享链接,粘贴到上方「多维表格链接」。</React.Fragment>,\n ],\n link: { href: \"https://open.feishu.cn\", label: \"打开飞书开放平台\" },\n};\n\nexport function FeishuCard({\n appId = \"\",\n onAppIdChange,\n secret = \"\",\n onSecretChange,\n link = \"\",\n onLinkChange,\n status = \"idle\",\n result,\n onTest,\n masked = false,\n appIdError,\n secretError,\n linkError,\n fieldMap = FS_DEFAULT_FIELD_MAP,\n showMapping,\n help,\n canTest,\n idleHint = \"填写凭证与链接后测试写入权限\",\n}) {\n const parsed = parseFeishuLink(link);\n // Masked echo is derived from props (no state): stored secret + empty field →\n // masked placeholder, still counts as \"present\" for the Test button.\n const maskedNow = masked && !(secret || \"\").trim();\n const testDisabled =\n canTest !== undefined\n ? !canTest\n : !(appId || \"\").trim() || (!(secret || \"\").trim() && !masked) || !parsed;\n const showMap = showMapping !== undefined ? showMapping : status === \"ok\";\n const help_ = help || FS_DEFAULT_HELP;\n\n return (\n <section\n className={\"s-card\" + (status === \"ok\" ? \" is-ok\" : status === \"error\" ? \" is-error\" : \"\")}\n >\n <div className=\"s-card__head\">\n <div className=\"s-card__toprow\">\n <div className=\"s-card__icon\">\n <Icon name=\"table\" size={16} />\n </div>\n <span className=\"ax-label s-card__eyebrow\">数据写入 · FEISHU BITABLE</span>\n <span className=\"s-card__status\">\n <StatusPill status={status} />\n </span>\n </div>\n <h2 className=\"s-card__title\">飞书多维表格</h2>\n <p className=\"s-card__desc\">\n 每次提交后,数据自动写入指定多维表格的一行。需要一个飞书自建应用的凭证,以及目标表格的链接。\n </p>\n </div>\n\n <div className=\"s-card__body\">\n <div className=\"s-row2\">\n <div>\n <label className=\"s-field__label ax-label\">App ID</label>\n <input\n className={\"ax-input ax-input--mono\" + (appIdError ? \" ax-input--error\" : \"\")}\n type=\"text\"\n value={appId}\n spellCheck=\"false\"\n placeholder=\"cli_xxxxxxxxxxxx\"\n onChange={(e) => onAppIdChange && onAppIdChange(e.target.value)}\n />\n {appIdError ? (\n <p className=\"s-field__hint s-field__hint--err\">{appIdError}</p>\n ) : (\n <p className=\"s-field__hint\">应用标识,可公开。</p>\n )}\n </div>\n <SecretField\n label=\"App Secret\"\n value={secret}\n onChange={onSecretChange}\n placeholder={maskedNow ? \"已保存 ········ · 留空则保持不变\" : \"••••••••••••••••\"}\n hint={maskedNow ? \"已存密钥 · 留空表示不修改\" : \"应用密钥,加密存储。\"}\n error={secretError}\n />\n </div>\n\n <div>\n <label className=\"s-field__label ax-label\">多维表格链接</label>\n <input\n className={\"ax-input\" + (linkError ? \" ax-input--error\" : \"\")}\n type=\"text\"\n value={link}\n spellCheck=\"false\"\n placeholder=\"https://your-team.feishu.cn/base/bascn…?table=tbl…\"\n onChange={(e) => onLinkChange && onLinkChange(e.target.value)}\n />\n {linkError ? (\n <p className=\"s-field__hint s-field__hint--err\">{linkError}</p>\n ) : (\n <p className=\"s-field__hint\">\n 在多维表格右上角「分享」中复制链接粘贴即可,App Token 与数据表会自动识别。\n </p>\n )}\n </div>\n\n {parsed ? (\n <div className=\"s-detect\">\n <div className=\"s-detect__row\">\n <span className=\"s-detect__k\">App Token</span>\n <span className=\"s-detect__v\">{parsed.token}</span>\n <span className=\"s-detect__ok\">\n <Icon name=\"check\" size={12} />\n 已识别\n </span>\n </div>\n <div className=\"s-detect__row\">\n <span className=\"s-detect__k\">数据表</span>\n <span className=\"s-detect__v\">{parsed.table || \"—\"}</span>\n {parsed.table ? (\n <span className=\"s-detect__ok\">\n <Icon name=\"check\" size={12} />\n 已识别\n </span>\n ) : (\n <span className=\"s-detect__ok\" style={{ color: \"var(--warn)\" }}>\n <Icon name=\"warn\" size={12} />\n 缺失\n </span>\n )}\n </div>\n </div>\n ) : null}\n\n <div className=\"s-lock\">\n <Icon name=\"shield\" size={14} />\n <span>\n <strong>凭证加密存储</strong>,仅服务端用于写入数据。用户\n <strong>无法接触你的飞书凭证或表格权限。</strong>\n </span>\n </div>\n\n {showMap ? (\n <div>\n <div className=\"s-sub\" style={{ marginBottom: 12 }}>\n <span className=\"ax-label\">字段映射 · 自动</span>\n <span className=\"s-sub__line\"></span>\n </div>\n <table className=\"s-map\">\n <thead>\n <tr>\n <th>字段</th>\n <th className=\"s-map__arrow\"></th>\n <th>表格列</th>\n </tr>\n </thead>\n <tbody>\n {fieldMap.map((m, i) => (\n <tr key={i}>\n <td className=\"s-map__from\">\n {m.from || <span style={{ color: \"var(--text-faint)\" }}>—</span>}\n </td>\n <td className=\"s-map__arrow\">\n <Icon name=\"arrow\" size={14} />\n </td>\n <td>\n <span className=\"s-map__col\">{m.to}</span>\n {m.tag ? <span className=\"s-map__tag\">{m.tag}</span> : null}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : null}\n\n <FeishuHelpSlot help={help_} />\n </div>\n\n <TestRow\n status={status}\n result={result}\n onTest={onTest}\n disabled={testDisabled}\n idleHint={idleHint}\n />\n </section>\n );\n}\n\nfunction FeishuHelpSlot({ help }) {\n if (!help) return null;\n if (React.isValidElement(help)) return help;\n return (\n <HelpSteps\n title={help.title}\n steps={help.steps}\n link={help.link}\n defaultOpen={help.defaultOpen}\n />\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ export interface BrandMarkProps {
|
|
|
8
8
|
size?: number;
|
|
9
9
|
/** Render the "agentaily" wordmark after the mark. @default false */
|
|
10
10
|
wordmark?: boolean;
|
|
11
|
-
/** Show the
|
|
11
|
+
/** Show the block cursor after the wordmark. Off by default in lockups; opt in for the liveness motif. @default false */
|
|
12
12
|
cursor?: boolean;
|
|
13
13
|
/** Animate the cursor (blink). Set false for a solid, static cursor. @default true */
|
|
14
14
|
blink?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrandMark.js","sources":["../../../src/components/utilities/BrandMark.jsx"],"sourcesContent":["import React from \"react\";\n\n// BrandMark — the agentaily mark (cursor block inside corner ticks) and optional\n// typed wordmark. Monochrome, drawn in currentColor so it inverts with the theme.\n// Used in auth modals, settings headers, top bars. Never recolor with a hue.\nconst AX_BRANDMARK_CSS = `\n.ax-brandmark { display: inline-flex; align-items: center; gap: 9px; color: var(--text-body); }\n.ax-brandmark__mark { display: inline-flex; flex: none; }\n.ax-brandmark__word {\n font-family: var(--font-mono); font-weight: var(--weight-medium);\n letter-spacing: -0.02em; color: var(--text-body);\n display: inline-flex; align-items: baseline; line-height: 1;\n}\n.ax-brandmark__word .ax-cursor { margin-left: 1px; width: 0.42em; height: 0.92em; }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-brandmark-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-brandmark-css\";\n s.textContent = AX_BRANDMARK_CSS;\n document.head.appendChild(s);\n}\n\nexport function BrandMark({\n size = 20,\n wordmark = false,\n cursor =
|
|
1
|
+
{"version":3,"file":"BrandMark.js","sources":["../../../src/components/utilities/BrandMark.jsx"],"sourcesContent":["import React from \"react\";\n\n// BrandMark — the agentaily mark (cursor block inside corner ticks) and optional\n// typed wordmark. Monochrome, drawn in currentColor so it inverts with the theme.\n// Used in auth modals, settings headers, top bars. Never recolor with a hue.\nconst AX_BRANDMARK_CSS = `\n.ax-brandmark { display: inline-flex; align-items: center; gap: 9px; color: var(--text-body); }\n.ax-brandmark__mark { display: inline-flex; flex: none; }\n.ax-brandmark__word {\n font-family: var(--font-mono); font-weight: var(--weight-medium);\n letter-spacing: -0.02em; color: var(--text-body);\n display: inline-flex; align-items: baseline; line-height: 1;\n}\n.ax-brandmark__word .ax-cursor { margin-left: 1px; width: 0.42em; height: 0.92em; }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-brandmark-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-brandmark-css\";\n s.textContent = AX_BRANDMARK_CSS;\n document.head.appendChild(s);\n}\n\nexport function BrandMark({\n size = 20,\n wordmark = false,\n cursor = false,\n blink = true,\n className = \"\",\n ...rest\n}) {\n const stroke = Math.max(2, Math.round((size / 32) * 2.4 * 10) / 10);\n return (\n <span className={[\"ax-brandmark\", className].filter(Boolean).join(\" \")} {...rest}>\n <span className=\"ax-brandmark__mark\" aria-hidden=\"true\">\n <svg width={size} height={size} viewBox=\"0 0 32 32\" fill=\"none\">\n <path d=\"M2 10 V2 H10\" stroke=\"currentColor\" strokeWidth={stroke} />\n <path d=\"M22 30 H30 V22\" stroke=\"currentColor\" strokeWidth={stroke} />\n <rect x=\"12\" y=\"9\" width=\"8\" height=\"14\" fill=\"currentColor\" />\n </svg>\n </span>\n {wordmark ? (\n <span className=\"ax-brandmark__word\" style={{ fontSize: Math.round(size * 0.75) }}>\n agentaily\n {cursor ? (\n <i className=\"ax-cursor\" style={blink ? undefined : { animation: \"none\" }}></i>\n ) : null}\n </span>\n ) : null}\n </span>\n );\n}\n"],"names":[],"mappings":";;AAKA,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWzB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,kBAAkB,GAAG;AACnF,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEO,SAAS,UAAU;AAAA,EACxB,OAAO;AAAA,EACP,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,GAAG;AACL,GAAG;AACD,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,MAAO,OAAO,KAAM,MAAM,EAAE,IAAI,EAAE;AAClE,SACE,qBAAC,QAAA,EAAK,WAAW,CAAC,gBAAgB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAI,GAAG,MAC1E,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,WAAU,sBAAqB,eAAY,QAC/C,UAAA,qBAAC,OAAA,EAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QACvD,UAAA;AAAA,MAAA,oBAAC,UAAK,GAAE,gBAAe,QAAO,gBAAe,aAAa,QAAQ;AAAA,0BACjE,QAAA,EAAK,GAAE,kBAAiB,QAAO,gBAAe,aAAa,QAAQ;AAAA,MACpE,oBAAC,QAAA,EAAK,GAAE,MAAK,GAAE,KAAI,OAAM,KAAI,QAAO,MAAK,MAAK,eAAA,CAAe;AAAA,IAAA,EAAA,CAC/D,EAAA,CACF;AAAA,IACC,WACC,qBAAC,QAAA,EAAK,WAAU,sBAAqB,OAAO,EAAE,UAAU,KAAK,MAAM,OAAO,IAAI,KAAK,UAAA;AAAA,MAAA;AAAA,MAEhF,SACC,oBAAC,KAAA,EAAE,WAAU,aAAY,OAAO,QAAQ,SAAY,EAAE,WAAW,OAAA,EAAO,CAAG,IACzE;AAAA,IAAA,EAAA,CACN,IACE;AAAA,EAAA,GACN;AAEJ;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export * from "./components/buttons/IconButton";
|
|
|
30
30
|
export * from "./components/chat/CodeBlock";
|
|
31
31
|
export * from "./components/chat/Composer";
|
|
32
32
|
export * from "./components/chat/ConversationThread";
|
|
33
|
+
export * from "./components/chat/Markdown";
|
|
33
34
|
export * from "./components/chat/Message";
|
|
34
35
|
|
|
35
36
|
// code
|
|
@@ -121,8 +122,9 @@ export * from "./components/overlay/Sheet";
|
|
|
121
122
|
export * from "./components/review/MarkupLayer";
|
|
122
123
|
|
|
123
124
|
// settings
|
|
125
|
+
export * from "./components/settings/DeepSeekCard";
|
|
126
|
+
export * from "./components/settings/FeishuCard";
|
|
124
127
|
export * from "./components/settings/HelpSteps";
|
|
125
|
-
export * from "./components/settings/IntegrationSettings";
|
|
126
128
|
export * from "./components/settings/TestRow";
|
|
127
129
|
|
|
128
130
|
// utilities
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { IconButton } from "./components/buttons/IconButton.js";
|
|
|
21
21
|
import { CodeBlock } from "./components/chat/CodeBlock.js";
|
|
22
22
|
import { Composer } from "./components/chat/Composer.js";
|
|
23
23
|
import { ConversationThread } from "./components/chat/ConversationThread.js";
|
|
24
|
+
import { Markdown } from "./components/chat/Markdown.js";
|
|
24
25
|
import { Message } from "./components/chat/Message.js";
|
|
25
26
|
import { Agent } from "./components/code/Agent.js";
|
|
26
27
|
import { Artifact } from "./components/code/Artifact.js";
|
|
@@ -96,8 +97,9 @@ import { NavigationMenu } from "./components/overlay/NavigationMenu.js";
|
|
|
96
97
|
import { Popover } from "./components/overlay/Popover.js";
|
|
97
98
|
import { Sheet } from "./components/overlay/Sheet.js";
|
|
98
99
|
import { MarkupLayer } from "./components/review/MarkupLayer.js";
|
|
100
|
+
import { DeepSeekCard } from "./components/settings/DeepSeekCard.js";
|
|
101
|
+
import { FeishuCard, parseFeishuLink } from "./components/settings/FeishuCard.js";
|
|
99
102
|
import { HelpSteps } from "./components/settings/HelpSteps.js";
|
|
100
|
-
import { IntegrationSettings } from "./components/settings/IntegrationSettings.js";
|
|
101
103
|
import { TestRow } from "./components/settings/TestRow.js";
|
|
102
104
|
import { BrandMark } from "./components/utilities/BrandMark.js";
|
|
103
105
|
import { Icon } from "./components/utilities/Icon.js";
|
|
@@ -153,6 +155,7 @@ export {
|
|
|
153
155
|
ConversationThread,
|
|
154
156
|
DataTable,
|
|
155
157
|
DatePicker,
|
|
158
|
+
DeepSeekCard,
|
|
156
159
|
DesignerShell,
|
|
157
160
|
Dialog,
|
|
158
161
|
DocsLayout,
|
|
@@ -160,6 +163,7 @@ export {
|
|
|
160
163
|
Edge,
|
|
161
164
|
Empty,
|
|
162
165
|
EnvironmentVariables,
|
|
166
|
+
FeishuCard,
|
|
163
167
|
Field,
|
|
164
168
|
FieldGroup,
|
|
165
169
|
FileTree,
|
|
@@ -174,12 +178,12 @@ export {
|
|
|
174
178
|
Input,
|
|
175
179
|
InputGroup,
|
|
176
180
|
InputOTP,
|
|
177
|
-
IntegrationSettings,
|
|
178
181
|
Item,
|
|
179
182
|
JSXPreview,
|
|
180
183
|
Kbd,
|
|
181
184
|
Label,
|
|
182
185
|
LineChart,
|
|
186
|
+
Markdown,
|
|
183
187
|
MarkupLayer,
|
|
184
188
|
Menubar,
|
|
185
189
|
Message,
|
|
@@ -240,6 +244,7 @@ export {
|
|
|
240
244
|
Transcription,
|
|
241
245
|
Typography,
|
|
242
246
|
VoiceSelector,
|
|
243
|
-
WebPreview
|
|
247
|
+
WebPreview,
|
|
248
|
+
parseFeishuLink
|
|
244
249
|
};
|
|
245
250
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentaily/design-system",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Agentaily design system — light-first monochrome React component library (
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Agentaily design system — light-first monochrome React component library (115 components) + Storybook.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"author": "Agentaily",
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fullscreen integration modal: connect a DeepSeek key (conversation engine) and
|
|
3
|
-
* a Feishu Bitable (data sink). Reports a 0/2 readiness state and gates Save until
|
|
4
|
-
* both connect. Composes SecretField, StatusPill, TestRow, HelpSteps. The monthly
|
|
5
|
-
* usage-cap block is opt-in.
|
|
6
|
-
*
|
|
7
|
-
* Two storage modes:
|
|
8
|
-
* - **Uncontrolled (default):** self-persists the whole config to localStorage —
|
|
9
|
-
* the legacy behavior, unchanged when none of the seam props below are passed.
|
|
10
|
-
* - **Controlled / backend-wired:** pass `value` + `onChange` to hold the config
|
|
11
|
-
* yourself (no localStorage writes), `onSave`/`onTest` to reach a real backend,
|
|
12
|
-
* `readiness` to reflect server-known connection state, and `masked` so stored
|
|
13
|
-
* secrets echo masked and are never re-submitted.
|
|
14
|
-
*/
|
|
15
|
-
export interface IntegrationConfig {
|
|
16
|
-
/** DeepSeek API key. Empty when masked + untouched (caller keeps the stored one). */
|
|
17
|
-
dsKey: string;
|
|
18
|
-
/** Conversation model id, e.g. "deepseek-chat" | "deepseek-reasoner". */
|
|
19
|
-
dsModel: string;
|
|
20
|
-
/** Monthly usage-cap toggle. */
|
|
21
|
-
capOn: boolean;
|
|
22
|
-
/** Monthly cap amount (digits-only string). */
|
|
23
|
-
cap: string;
|
|
24
|
-
/** DeepSeek connection status. */
|
|
25
|
-
dsStatus: "idle" | "testing" | "ok" | "error";
|
|
26
|
-
/** DeepSeek test result line. */
|
|
27
|
-
dsResult: string;
|
|
28
|
-
/** Feishu app id (public). */
|
|
29
|
-
appId: string;
|
|
30
|
-
/** Feishu app secret. Empty when masked + untouched. */
|
|
31
|
-
secret: string;
|
|
32
|
-
/** Feishu Bitable share URL (app_token + table auto-parsed). */
|
|
33
|
-
link: string;
|
|
34
|
-
/** Feishu connection status. */
|
|
35
|
-
fsStatus: "idle" | "testing" | "ok" | "error";
|
|
36
|
-
/** Feishu test result line. */
|
|
37
|
-
fsResult: string;
|
|
38
|
-
/** Whether the last config was saved. */
|
|
39
|
-
saved: boolean;
|
|
40
|
-
}
|
|
41
|
-
export interface IntegrationSettingsProps {
|
|
42
|
-
/** Close handler (Esc + the × button + overlay). */
|
|
43
|
-
onClose?: () => void;
|
|
44
|
-
/** Show the DeepSeek monthly usage-cap warning + toggle. @default true */
|
|
45
|
-
showUsageCap?: boolean;
|
|
46
|
-
/** localStorage key for the persisted config (uncontrolled mode only). @default "agentaily.integrations.v1" */
|
|
47
|
-
storageKey?: string;
|
|
48
|
-
/**
|
|
49
|
-
* Controlled config. When provided, this is the source of truth — the modal
|
|
50
|
-
* stops reading/writing localStorage and every edit flows out via `onChange`.
|
|
51
|
-
* Omit for the self-persisting default.
|
|
52
|
-
*/
|
|
53
|
-
value?: IntegrationConfig;
|
|
54
|
-
/** Fires with the next full config on every edit/test (controlled mode). */
|
|
55
|
-
onChange?: (next: IntegrationConfig) => void;
|
|
56
|
-
/**
|
|
57
|
-
* Persist handler. Receives the current config (masked secrets stay empty —
|
|
58
|
-
* treat empty as "keep the stored one"). Return a Promise: Save is disabled and
|
|
59
|
-
* spins while it's pending, marks saved on resolve, and stays dirty on reject.
|
|
60
|
-
* When omitted, Save just flips the in-component saved state.
|
|
61
|
-
*/
|
|
62
|
-
onSave?: (value: IntegrationConfig) => void | Promise<void>;
|
|
63
|
-
/**
|
|
64
|
-
* Connection probe. `which` is "deepseek" | "feishu"; return
|
|
65
|
-
* `Promise<{ ok, message }>` to drive that card's StatusPill + TestRow. When
|
|
66
|
-
* omitted, a built-in mock probe runs (validates key shape / link parsing).
|
|
67
|
-
*/
|
|
68
|
-
onTest?: (which: "deepseek" | "feishu") => Promise<{ ok: boolean; message?: string }>;
|
|
69
|
-
/**
|
|
70
|
-
* External readiness override. When set, these flags (not the in-app test
|
|
71
|
-
* status) decide the 0/2 rail, Save gating, and the green pills — e.g. show
|
|
72
|
-
* "connected" for credentials already verified server-side. Live testing/error
|
|
73
|
-
* transitions still surface on the pill.
|
|
74
|
-
*/
|
|
75
|
-
readiness?: { deepseek?: boolean; feishu?: boolean };
|
|
76
|
-
/**
|
|
77
|
-
* Masked-echo flags for already-stored secrets. When `deepseek`/`feishu` is
|
|
78
|
-
* true and the field is untouched, it renders a masked placeholder, keeps its
|
|
79
|
-
* value empty, and still counts as "present" for testing — so the mask is never
|
|
80
|
-
* sent back as a new value. Typing overrides the mask for that field.
|
|
81
|
-
*/
|
|
82
|
-
masked?: { deepseek?: boolean; feishu?: boolean };
|
|
83
|
-
}
|
|
84
|
-
export declare function IntegrationSettings(props: IntegrationSettingsProps): JSX.Element;
|