@agentaily/design-system 0.4.0 → 0.5.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.
@@ -4,7 +4,9 @@
4
4
  * are shared; only fields, copy, and footer differ. The built-in footer link
5
5
  * flips mode and fires onModeChange (wire it to your router to swap the URL).
6
6
  * Light built-in validation (email format, password length, confirm match).
7
- * For a modal instead, use AuthDialog. Brand panel hides < 860px.
7
+ * Pass `error` to surface a backend error above the submit button and
8
+ * `submitting` to drive the async busy state. For a modal instead, use AuthDialog.
9
+ * Brand panel hides < 860px.
8
10
  */
9
11
  export interface SignInPageCopy {
10
12
  title?: string;
@@ -65,5 +67,20 @@ export interface SignInPageProps {
65
67
  footer?: React.ReactNode;
66
68
  /** Show the left brand panel. @default true */
67
69
  showBrandPanel?: boolean;
70
+ /**
71
+ * Server-side error to show in a danger banner directly above the submit
72
+ * button (e.g. 409 email taken, 401 bad credentials, 400 weak password).
73
+ * Omit/falsy = nothing shown. The component owns its CLIENT validation errors
74
+ * and clears them on input/mode change; this backend error is caller-owned —
75
+ * clear it yourself (typically inside onSubmit before the request, and on
76
+ * onModeChange / onEmailChange / onPasswordChange).
77
+ */
78
+ error?: React.ReactNode;
79
+ /**
80
+ * Submit-in-flight flag for an async onSubmit. When true the submit button is
81
+ * disabled and shows a spinner, and re-submits are blocked — prevents double
82
+ * submission. @default false
83
+ */
84
+ submitting?: boolean;
68
85
  }
69
86
  export declare function SignInPage(props: SignInPageProps): JSX.Element;
@@ -5,6 +5,7 @@ import { Button } from "../buttons/Button.js";
5
5
  import { Icon } from "../utilities/Icon.js";
6
6
  import { BrandMark } from "../utilities/BrandMark.js";
7
7
  import { RotatingTagline } from "../utilities/RotatingTagline.js";
8
+ import { Spinner } from "../feedback/Spinner.js";
8
9
  const AX_SIGNIN_CSS = `
9
10
  .ax-signin { display: grid; grid-template-columns: 1fr 1fr; height: 100%; background: var(--surface-page); }
10
11
  .ax-signin__brand { position: relative; border-right: 1px solid var(--border-default); background: var(--surface-panel);
@@ -25,6 +26,9 @@ const AX_SIGNIN_CSS = `
25
26
  .ax-signin__link { font-size: 12px; color: var(--text-muted); text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit; }
26
27
  .ax-signin__link:hover { color: var(--text-body); }
27
28
  .ax-signin__submit { margin-top: 22px; }
29
+ .ax-signin__error { display: flex; align-items: flex-start; gap: 9px; margin-top: 22px; padding: 10px 12px; border: 1px solid var(--danger); border-radius: var(--radius-2); background: var(--danger-dim); color: var(--danger); font-size: var(--text-sm); line-height: var(--leading-snug); }
30
+ .ax-signin__error svg { flex: none; margin-top: 1px; }
31
+ .ax-signin__submit .ax-spinner { border-top-color: var(--accent-fg); }
28
32
  .ax-signin__or { display: flex; align-items: center; gap: 12px; margin: 22px 0; }
29
33
  .ax-signin__or::before, .ax-signin__or::after { content: ""; flex: 1; height: 1px; background: var(--border-default); }
30
34
  .ax-signin__or span { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--text-faint); }
@@ -101,7 +105,9 @@ function SignInPage({
101
105
  onSSO,
102
106
  terms,
103
107
  footer,
104
- showBrandPanel = true
108
+ showBrandPanel = true,
109
+ error,
110
+ submitting = false
105
111
  }) {
106
112
  const [modeI, setModeI] = useState(defaultMode);
107
113
  const m = mode !== void 0 ? mode : modeI;
@@ -154,6 +160,7 @@ function SignInPage({
154
160
  };
155
161
  const submit = (ev) => {
156
162
  ev.preventDefault();
163
+ if (submitting) return;
157
164
  const e = validate();
158
165
  setErrs(e);
159
166
  if (Object.keys(e).some((k) => e[k])) return;
@@ -216,7 +223,22 @@ function SignInPage({
216
223
  }
217
224
  ) : null
218
225
  ] }),
219
- /* @__PURE__ */ jsx("div", { className: "ax-signin__submit", children: /* @__PURE__ */ jsx(Button, { variant: "primary", full: true, type: "submit", icon: /* @__PURE__ */ jsx(Icon, { name: "arrow", size: 15 }), children: c.submit }) }),
226
+ error ? /* @__PURE__ */ jsxs("div", { className: "ax-signin__error", role: "alert", children: [
227
+ /* @__PURE__ */ jsx(Icon, { name: "warn", size: 15 }),
228
+ /* @__PURE__ */ jsx("span", { children: error })
229
+ ] }) : null,
230
+ /* @__PURE__ */ jsx("div", { className: "ax-signin__submit", children: /* @__PURE__ */ jsx(
231
+ Button,
232
+ {
233
+ variant: "primary",
234
+ full: true,
235
+ type: "submit",
236
+ disabled: submitting,
237
+ "aria-busy": submitting || void 0,
238
+ icon: submitting ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : /* @__PURE__ */ jsx(Icon, { name: "arrow", size: 15 }),
239
+ children: c.submit
240
+ }
241
+ ) }),
220
242
  isSignup ? /* @__PURE__ */ jsx("p", { className: "ax-signin__terms", children: termsNode }) : null,
221
243
  onSSO ? /* @__PURE__ */ jsxs(React.Fragment, { children: [
222
244
  /* @__PURE__ */ jsx("div", { className: "ax-signin__or", children: /* @__PURE__ */ jsx("span", { children: labels.or }) }),
@@ -1 +1 @@
1
- {"version":3,"file":"SignInPage.js","sources":["../../../src/components/auth/SignInPage.jsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Input } from \"../inputs/Input.jsx\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\nimport { BrandMark } from \"../utilities/BrandMark.jsx\";\nimport { RotatingTagline } from \"../utilities/RotatingTagline.jsx\";\n\n// SignInPage — full-page auth: a split brand panel (dot-grid + quote) beside a\n// centered card. One component, two modes (signin / signup) — the brand panel,\n// SSO, and card chrome are shared; only the fields, copy, and footer differ.\n// The footer link flips mode (and fires onModeChange so a router can swap the\n// URL). The full-page sibling of AuthDialog (the modal); same login/register idea.\nconst AX_SIGNIN_CSS = `\n.ax-signin { display: grid; grid-template-columns: 1fr 1fr; height: 100%; background: var(--surface-page); }\n.ax-signin__brand { position: relative; border-right: 1px solid var(--border-default); background: var(--surface-panel);\n background-image: var(--dot-grid); background-size: 24px 24px; padding: 40px; display: flex; flex-direction: column; }\n.ax-signin__brandtop { display: flex; align-items: center; gap: 10px; }\n.ax-signin__mid { margin-top: auto; margin-bottom: auto; }\n.ax-signin__quote { font-family: var(--font-display); white-space: nowrap; font-size: clamp(40px, 4.4vw, var(--text-hero)); font-weight: var(--weight-medium); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--text-body); margin: 0; }\n.ax-signin__by { margin: 16px 0 0; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; color: var(--text-faint); }\n.ax-signin__form { display: flex; align-items: center; justify-content: center; padding: 40px; }\n.ax-signin__card { width: 100%; max-width: 360px; }\n.ax-signin__mbrand { display: none; }\n.ax-signin__mtag { display: none; }\n.ax-signin__mtag .ax-signin__quote { font-size: var(--text-3xl); }\n.ax-signin__h { margin: 0; font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); color: var(--text-body); }\n.ax-signin__sub { margin: 8px 0 28px; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__fields { display: flex; flex-direction: column; gap: 16px; }\n.ax-signin__row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }\n.ax-signin__link { font-size: 12px; color: var(--text-muted); text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit; }\n.ax-signin__link:hover { color: var(--text-body); }\n.ax-signin__submit { margin-top: 22px; }\n.ax-signin__or { display: flex; align-items: center; gap: 12px; margin: 22px 0; }\n.ax-signin__or::before, .ax-signin__or::after { content: \"\"; flex: 1; height: 1px; background: var(--border-default); }\n.ax-signin__or span { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--text-faint); }\n.ax-signin__sso { display: flex; align-items: center; justify-content: center; gap: 9px; width: 100%; height: 40px; border: 1px solid var(--border-default);\n border-radius: var(--radius-2); background: var(--surface-card); color: var(--text-body); font-family: inherit; font-size: var(--text-sm); cursor: pointer; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__sso:hover { border-color: var(--border-strong); }\n.ax-signin__terms { margin: 16px 0 0; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); text-align: center; }\n.ax-signin__terms a, .ax-signin__terms span.lk { color: var(--text-muted); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }\n.ax-signin__foot { margin-top: 26px; text-align: center; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__footlink { color: var(--text-body); background: none; border: none; cursor: pointer; font-family: inherit; font-size: var(--text-sm);\n border-bottom: 1px solid var(--border-strong); padding: 0 0 1px; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__footlink:hover { border-color: var(--text-faint); }\n@media (max-width: 860px) { .ax-signin { grid-template-columns: 1fr; } .ax-signin__brand { display: none; } .ax-signin__form { padding: 56px 40px; } .ax-signin__mbrand { display: flex; margin-bottom: 28px; } .ax-signin__mtag { display: block; margin-bottom: 36px; } .ax-signin__h { font-family: var(--font-mono); font-size: var(--text-xs); font-weight: var(--weight-medium); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-body); margin: 0 0 8px; } .ax-signin__sub { margin: 0 0 32px; color: var(--text-muted); } .ax-signin__fields { gap: 22px; } .ax-signin__submit { margin-top: 32px; } .ax-signin__or { margin: 32px 0; } .ax-signin__foot { margin-top: 28px; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-signin-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-signin-css\";\n s.textContent = AX_SIGNIN_CSS;\n document.head.appendChild(s);\n}\n\n// Every user-facing string lives here so nothing is hardcoded in the markup.\n// Defaults are a self-consistent English baseline; pass a `copy` prop (any\n// subset, deep-merged per group) to localize — e.g. a full zh-CN object.\nconst DEFAULT_COPY = {\n signin: {\n title: \"Sign in\",\n subtitle: \"Welcome back. Use your work email to continue.\",\n submit: \"Continue\",\n switchText: \"No account?\",\n switchCta: \"Create one\",\n },\n signup: {\n title: \"Create account\",\n subtitle: \"Start with your work email — it takes a minute.\",\n submit: \"Create account\",\n switchText: \"Already have an account?\",\n switchCta: \"Sign in\",\n },\n labels: {\n email: \"EMAIL\",\n password: \"PASSWORD\",\n confirm: \"CONFIRM PASSWORD\",\n forgot: \"Forgot?\",\n or: \"OR\",\n sso: \"Continue with SSO\",\n },\n placeholders: {\n email: \"you@example.com\",\n password: \"••••••••\",\n passwordNew: \"At least 8 characters\",\n confirm: \"Re-enter your password\",\n },\n errors: {\n emailRequired: \"Enter your email\",\n emailInvalid: \"That email doesn’t look right\",\n passwordRequired: \"Enter your password\",\n passwordShort: \"Password must be at least 8 characters\",\n confirmRequired: \"Re-enter your password\",\n confirmMismatch: \"Passwords don’t match\",\n },\n terms: \"By creating an account you agree to our Terms and Privacy Policy.\",\n};\n\nexport function SignInPage({\n mode,\n defaultMode = \"signin\",\n onModeChange,\n brand,\n copy,\n tagline,\n email,\n password,\n onEmailChange,\n onPasswordChange,\n onSubmit,\n onForgot,\n ssoLabel,\n onSSO,\n terms,\n footer,\n showBrandPanel = true,\n}) {\n const [modeI, setModeI] = useState(defaultMode);\n const m = mode !== undefined ? mode : modeI;\n const isSignup = m === \"signup\";\n const c = { ...DEFAULT_COPY[m], ...(copy && copy[m]) };\n const labels = { ...DEFAULT_COPY.labels, ...(copy && copy.labels) };\n const ph = { ...DEFAULT_COPY.placeholders, ...(copy && copy.placeholders) };\n const err = { ...DEFAULT_COPY.errors, ...(copy && copy.errors) };\n const ssoText = ssoLabel !== undefined ? ssoLabel : labels.sso;\n const taglineProps = { breakAfterPrefix: true, ...tagline };\n\n const [emailI, setEmailI] = useState(\"\");\n const [pwI, setPwI] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [errs, setErrs] = useState({});\n const emailV = email !== undefined ? email : emailI;\n const pwV = password !== undefined ? password : pwI;\n const setEmail = (v) => {\n if (onEmailChange) onEmailChange(v);\n else setEmailI(v);\n if (errs.email) setErrs((e) => ({ ...e, email: undefined }));\n };\n const setPw = (v) => {\n if (onPasswordChange) onPasswordChange(v);\n else setPwI(v);\n if (errs.password) setErrs((e) => ({ ...e, password: undefined }));\n };\n const setConf = (v) => {\n setConfirm(v);\n if (errs.confirm) setErrs((e) => ({ ...e, confirm: undefined }));\n };\n\n const switchMode = () => {\n const next = isSignup ? \"signin\" : \"signup\";\n setErrs({});\n setConfirm(\"\");\n if (onModeChange) onModeChange(next);\n if (mode === undefined) setModeI(next);\n };\n\n const validate = () => {\n const e = {};\n const em = (emailV || \"\").trim();\n if (!em) e.email = err.emailRequired;\n else if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(em)) e.email = err.emailInvalid;\n if (!pwV) e.password = err.passwordRequired;\n else if (isSignup && pwV.length < 8) e.password = err.passwordShort;\n if (isSignup) {\n if (!confirm) e.confirm = err.confirmRequired;\n else if (confirm !== pwV) e.confirm = err.confirmMismatch;\n }\n return e;\n };\n\n const submit = (ev) => {\n ev.preventDefault();\n const e = validate();\n setErrs(e);\n if (Object.keys(e).some((k) => e[k])) return;\n onSubmit && onSubmit({ mode: m, email: (emailV || \"\").trim(), password: pwV });\n };\n\n const termsNode =\n terms !== undefined ? terms : c.terms !== undefined ? c.terms : DEFAULT_COPY.terms;\n\n return (\n <div className=\"ax-signin\">\n {showBrandPanel ? (\n <aside className=\"ax-signin__brand\">\n <div className=\"ax-signin__brandtop\">\n {brand || <BrandMark size={24} wordmark blink={false} />}\n </div>\n <div className=\"ax-signin__mid\">\n <p className=\"ax-signin__quote\">\n <RotatingTagline {...taglineProps} />\n </p>\n <p className=\"ax-signin__by\">— AGENTAILY</p>\n </div>\n </aside>\n ) : null}\n\n <main className=\"ax-signin__form\">\n <form className=\"ax-signin__card\" onSubmit={submit} noValidate>\n <div className=\"ax-signin__mbrand\">\n {brand || <BrandMark size={22} wordmark blink={false} />}\n </div>\n <div className=\"ax-signin__mtag\">\n <p className=\"ax-signin__quote\">\n <RotatingTagline {...taglineProps} />\n </p>\n </div>\n <h1 className=\"ax-signin__h\">{c.title}</h1>\n <p className=\"ax-signin__sub\">{c.subtitle}</p>\n\n <div className=\"ax-signin__fields\">\n <Input\n label={labels.email}\n type=\"email\"\n autoComplete=\"email\"\n placeholder={ph.email}\n value={emailV}\n error={errs.email}\n onChange={(e) => setEmail(e.target.value)}\n />\n <div>\n <div className=\"ax-signin__row\">\n <span className=\"ax-label\">{labels.password}</span>\n {!isSignup && onForgot ? (\n <button type=\"button\" className=\"ax-signin__link\" onClick={onForgot}>\n {labels.forgot}\n </button>\n ) : null}\n </div>\n <Input\n type=\"password\"\n autoComplete={isSignup ? \"new-password\" : \"current-password\"}\n placeholder={isSignup ? ph.passwordNew : ph.password}\n value={pwV}\n error={errs.password}\n onChange={(e) => setPw(e.target.value)}\n />\n </div>\n {isSignup ? (\n <Input\n label={labels.confirm}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={ph.confirm}\n value={confirm}\n error={errs.confirm}\n onChange={(e) => setConf(e.target.value)}\n />\n ) : null}\n </div>\n\n <div className=\"ax-signin__submit\">\n <Button variant=\"primary\" full type=\"submit\" icon={<Icon name=\"arrow\" size={15} />}>\n {c.submit}\n </Button>\n </div>\n\n {isSignup ? <p className=\"ax-signin__terms\">{termsNode}</p> : null}\n\n {onSSO ? (\n <React.Fragment>\n <div className=\"ax-signin__or\">\n <span>{labels.or}</span>\n </div>\n <button type=\"button\" className=\"ax-signin__sso\" onClick={onSSO}>\n <Icon name=\"shield\" size={15} />\n {ssoText}\n </button>\n </React.Fragment>\n ) : null}\n\n {footer !== undefined ? (\n footer ? (\n <p className=\"ax-signin__foot\">{footer}</p>\n ) : null\n ) : (\n <p className=\"ax-signin__foot\">\n {c.switchText}{\" \"}\n <button type=\"button\" className=\"ax-signin__footlink\" onClick={switchMode}>\n {c.switchCta}\n </button>\n </p>\n )}\n </form>\n </main>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SignInPage.js","sources":["../../../src/components/auth/SignInPage.jsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Input } from \"../inputs/Input.jsx\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\nimport { BrandMark } from \"../utilities/BrandMark.jsx\";\nimport { RotatingTagline } from \"../utilities/RotatingTagline.jsx\";\nimport { Spinner } from \"../feedback/Spinner.jsx\";\n\n// SignInPage — full-page auth: a split brand panel (dot-grid + quote) beside a\n// centered card. One component, two modes (signin / signup) — the brand panel,\n// SSO, and card chrome are shared; only the fields, copy, and footer differ.\n// The footer link flips mode (and fires onModeChange so a router can swap the\n// URL). The full-page sibling of AuthDialog (the modal); same login/register idea.\nconst AX_SIGNIN_CSS = `\n.ax-signin { display: grid; grid-template-columns: 1fr 1fr; height: 100%; background: var(--surface-page); }\n.ax-signin__brand { position: relative; border-right: 1px solid var(--border-default); background: var(--surface-panel);\n background-image: var(--dot-grid); background-size: 24px 24px; padding: 40px; display: flex; flex-direction: column; }\n.ax-signin__brandtop { display: flex; align-items: center; gap: 10px; }\n.ax-signin__mid { margin-top: auto; margin-bottom: auto; }\n.ax-signin__quote { font-family: var(--font-display); white-space: nowrap; font-size: clamp(40px, 4.4vw, var(--text-hero)); font-weight: var(--weight-medium); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--text-body); margin: 0; }\n.ax-signin__by { margin: 16px 0 0; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; color: var(--text-faint); }\n.ax-signin__form { display: flex; align-items: center; justify-content: center; padding: 40px; }\n.ax-signin__card { width: 100%; max-width: 360px; }\n.ax-signin__mbrand { display: none; }\n.ax-signin__mtag { display: none; }\n.ax-signin__mtag .ax-signin__quote { font-size: var(--text-3xl); }\n.ax-signin__h { margin: 0; font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); color: var(--text-body); }\n.ax-signin__sub { margin: 8px 0 28px; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__fields { display: flex; flex-direction: column; gap: 16px; }\n.ax-signin__row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }\n.ax-signin__link { font-size: 12px; color: var(--text-muted); text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit; }\n.ax-signin__link:hover { color: var(--text-body); }\n.ax-signin__submit { margin-top: 22px; }\n.ax-signin__error { display: flex; align-items: flex-start; gap: 9px; margin-top: 22px; padding: 10px 12px; border: 1px solid var(--danger); border-radius: var(--radius-2); background: var(--danger-dim); color: var(--danger); font-size: var(--text-sm); line-height: var(--leading-snug); }\n.ax-signin__error svg { flex: none; margin-top: 1px; }\n.ax-signin__submit .ax-spinner { border-top-color: var(--accent-fg); }\n.ax-signin__or { display: flex; align-items: center; gap: 12px; margin: 22px 0; }\n.ax-signin__or::before, .ax-signin__or::after { content: \"\"; flex: 1; height: 1px; background: var(--border-default); }\n.ax-signin__or span { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--text-faint); }\n.ax-signin__sso { display: flex; align-items: center; justify-content: center; gap: 9px; width: 100%; height: 40px; border: 1px solid var(--border-default);\n border-radius: var(--radius-2); background: var(--surface-card); color: var(--text-body); font-family: inherit; font-size: var(--text-sm); cursor: pointer; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__sso:hover { border-color: var(--border-strong); }\n.ax-signin__terms { margin: 16px 0 0; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); text-align: center; }\n.ax-signin__terms a, .ax-signin__terms span.lk { color: var(--text-muted); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }\n.ax-signin__foot { margin-top: 26px; text-align: center; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__footlink { color: var(--text-body); background: none; border: none; cursor: pointer; font-family: inherit; font-size: var(--text-sm);\n border-bottom: 1px solid var(--border-strong); padding: 0 0 1px; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__footlink:hover { border-color: var(--text-faint); }\n@media (max-width: 860px) { .ax-signin { grid-template-columns: 1fr; } .ax-signin__brand { display: none; } .ax-signin__form { padding: 56px 40px; } .ax-signin__mbrand { display: flex; margin-bottom: 28px; } .ax-signin__mtag { display: block; margin-bottom: 36px; } .ax-signin__h { font-family: var(--font-mono); font-size: var(--text-xs); font-weight: var(--weight-medium); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-body); margin: 0 0 8px; } .ax-signin__sub { margin: 0 0 32px; color: var(--text-muted); } .ax-signin__fields { gap: 22px; } .ax-signin__submit { margin-top: 32px; } .ax-signin__or { margin: 32px 0; } .ax-signin__foot { margin-top: 28px; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-signin-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-signin-css\";\n s.textContent = AX_SIGNIN_CSS;\n document.head.appendChild(s);\n}\n\n// Every user-facing string lives here so nothing is hardcoded in the markup.\n// Defaults are a self-consistent English baseline; pass a `copy` prop (any\n// subset, deep-merged per group) to localize — e.g. a full zh-CN object.\nconst DEFAULT_COPY = {\n signin: {\n title: \"Sign in\",\n subtitle: \"Welcome back. Use your work email to continue.\",\n submit: \"Continue\",\n switchText: \"No account?\",\n switchCta: \"Create one\",\n },\n signup: {\n title: \"Create account\",\n subtitle: \"Start with your work email — it takes a minute.\",\n submit: \"Create account\",\n switchText: \"Already have an account?\",\n switchCta: \"Sign in\",\n },\n labels: {\n email: \"EMAIL\",\n password: \"PASSWORD\",\n confirm: \"CONFIRM PASSWORD\",\n forgot: \"Forgot?\",\n or: \"OR\",\n sso: \"Continue with SSO\",\n },\n placeholders: {\n email: \"you@example.com\",\n password: \"••••••••\",\n passwordNew: \"At least 8 characters\",\n confirm: \"Re-enter your password\",\n },\n errors: {\n emailRequired: \"Enter your email\",\n emailInvalid: \"That email doesn’t look right\",\n passwordRequired: \"Enter your password\",\n passwordShort: \"Password must be at least 8 characters\",\n confirmRequired: \"Re-enter your password\",\n confirmMismatch: \"Passwords don’t match\",\n },\n terms: \"By creating an account you agree to our Terms and Privacy Policy.\",\n};\n\nexport function SignInPage({\n mode,\n defaultMode = \"signin\",\n onModeChange,\n brand,\n copy,\n tagline,\n email,\n password,\n onEmailChange,\n onPasswordChange,\n onSubmit,\n onForgot,\n ssoLabel,\n onSSO,\n terms,\n footer,\n showBrandPanel = true,\n error,\n submitting = false,\n}) {\n const [modeI, setModeI] = useState(defaultMode);\n const m = mode !== undefined ? mode : modeI;\n const isSignup = m === \"signup\";\n const c = { ...DEFAULT_COPY[m], ...(copy && copy[m]) };\n const labels = { ...DEFAULT_COPY.labels, ...(copy && copy.labels) };\n const ph = { ...DEFAULT_COPY.placeholders, ...(copy && copy.placeholders) };\n const err = { ...DEFAULT_COPY.errors, ...(copy && copy.errors) };\n const ssoText = ssoLabel !== undefined ? ssoLabel : labels.sso;\n const taglineProps = { breakAfterPrefix: true, ...tagline };\n\n const [emailI, setEmailI] = useState(\"\");\n const [pwI, setPwI] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [errs, setErrs] = useState({});\n const emailV = email !== undefined ? email : emailI;\n const pwV = password !== undefined ? password : pwI;\n const setEmail = (v) => {\n if (onEmailChange) onEmailChange(v);\n else setEmailI(v);\n if (errs.email) setErrs((e) => ({ ...e, email: undefined }));\n };\n const setPw = (v) => {\n if (onPasswordChange) onPasswordChange(v);\n else setPwI(v);\n if (errs.password) setErrs((e) => ({ ...e, password: undefined }));\n };\n const setConf = (v) => {\n setConfirm(v);\n if (errs.confirm) setErrs((e) => ({ ...e, confirm: undefined }));\n };\n\n const switchMode = () => {\n const next = isSignup ? \"signin\" : \"signup\";\n setErrs({});\n setConfirm(\"\");\n if (onModeChange) onModeChange(next);\n if (mode === undefined) setModeI(next);\n };\n\n const validate = () => {\n const e = {};\n const em = (emailV || \"\").trim();\n if (!em) e.email = err.emailRequired;\n else if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(em)) e.email = err.emailInvalid;\n if (!pwV) e.password = err.passwordRequired;\n else if (isSignup && pwV.length < 8) e.password = err.passwordShort;\n if (isSignup) {\n if (!confirm) e.confirm = err.confirmRequired;\n else if (confirm !== pwV) e.confirm = err.confirmMismatch;\n }\n return e;\n };\n\n const submit = (ev) => {\n ev.preventDefault();\n if (submitting) return;\n const e = validate();\n setErrs(e);\n if (Object.keys(e).some((k) => e[k])) return;\n onSubmit && onSubmit({ mode: m, email: (emailV || \"\").trim(), password: pwV });\n };\n\n const termsNode =\n terms !== undefined ? terms : c.terms !== undefined ? c.terms : DEFAULT_COPY.terms;\n\n return (\n <div className=\"ax-signin\">\n {showBrandPanel ? (\n <aside className=\"ax-signin__brand\">\n <div className=\"ax-signin__brandtop\">\n {brand || <BrandMark size={24} wordmark blink={false} />}\n </div>\n <div className=\"ax-signin__mid\">\n <p className=\"ax-signin__quote\">\n <RotatingTagline {...taglineProps} />\n </p>\n <p className=\"ax-signin__by\">— AGENTAILY</p>\n </div>\n </aside>\n ) : null}\n\n <main className=\"ax-signin__form\">\n <form className=\"ax-signin__card\" onSubmit={submit} noValidate>\n <div className=\"ax-signin__mbrand\">\n {brand || <BrandMark size={22} wordmark blink={false} />}\n </div>\n <div className=\"ax-signin__mtag\">\n <p className=\"ax-signin__quote\">\n <RotatingTagline {...taglineProps} />\n </p>\n </div>\n <h1 className=\"ax-signin__h\">{c.title}</h1>\n <p className=\"ax-signin__sub\">{c.subtitle}</p>\n\n <div className=\"ax-signin__fields\">\n <Input\n label={labels.email}\n type=\"email\"\n autoComplete=\"email\"\n placeholder={ph.email}\n value={emailV}\n error={errs.email}\n onChange={(e) => setEmail(e.target.value)}\n />\n <div>\n <div className=\"ax-signin__row\">\n <span className=\"ax-label\">{labels.password}</span>\n {!isSignup && onForgot ? (\n <button type=\"button\" className=\"ax-signin__link\" onClick={onForgot}>\n {labels.forgot}\n </button>\n ) : null}\n </div>\n <Input\n type=\"password\"\n autoComplete={isSignup ? \"new-password\" : \"current-password\"}\n placeholder={isSignup ? ph.passwordNew : ph.password}\n value={pwV}\n error={errs.password}\n onChange={(e) => setPw(e.target.value)}\n />\n </div>\n {isSignup ? (\n <Input\n label={labels.confirm}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={ph.confirm}\n value={confirm}\n error={errs.confirm}\n onChange={(e) => setConf(e.target.value)}\n />\n ) : null}\n </div>\n\n {error ? (\n <div className=\"ax-signin__error\" role=\"alert\">\n <Icon name=\"warn\" size={15} />\n <span>{error}</span>\n </div>\n ) : null}\n\n <div className=\"ax-signin__submit\">\n <Button\n variant=\"primary\"\n full\n type=\"submit\"\n disabled={submitting}\n aria-busy={submitting || undefined}\n icon={submitting ? <Spinner size=\"sm\" /> : <Icon name=\"arrow\" size={15} />}\n >\n {c.submit}\n </Button>\n </div>\n\n {isSignup ? <p className=\"ax-signin__terms\">{termsNode}</p> : null}\n\n {onSSO ? (\n <React.Fragment>\n <div className=\"ax-signin__or\">\n <span>{labels.or}</span>\n </div>\n <button type=\"button\" className=\"ax-signin__sso\" onClick={onSSO}>\n <Icon name=\"shield\" size={15} />\n {ssoText}\n </button>\n </React.Fragment>\n ) : null}\n\n {footer !== undefined ? (\n footer ? (\n <p className=\"ax-signin__foot\">{footer}</p>\n ) : null\n ) : (\n <p className=\"ax-signin__foot\">\n {c.switchText}{\" \"}\n <button type=\"button\" className=\"ax-signin__footlink\" onClick={switchMode}>\n {c.switchCta}\n </button>\n </p>\n )}\n </form>\n </main>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1,15 +1,84 @@
1
1
  /**
2
2
  * Fullscreen integration modal: connect a DeepSeek key (conversation engine) and
3
- * a Feishu Bitable (data sink). Self-persists to localStorage, reports a 0/2
4
- * readiness state, and gates Save until both connect. Composes SecretField,
5
- * StatusPill, TestRow, HelpSteps. The monthly usage-cap block is opt-in.
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.
6
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
+ }
7
41
  export interface IntegrationSettingsProps {
8
42
  /** Close handler (Esc + the × button + overlay). */
9
43
  onClose?: () => void;
10
44
  /** Show the DeepSeek monthly usage-cap warning + toggle. @default true */
11
45
  showUsageCap?: boolean;
12
- /** localStorage key for the persisted config. @default "agentaily.integrations.v1" */
46
+ /** localStorage key for the persisted config (uncontrolled mode only). @default "agentaily.integrations.v1" */
13
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 };
14
83
  }
15
84
  export declare function IntegrationSettings(props: IntegrationSettingsProps): JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import React, { useRef, useState, useEffect } from "react";
2
+ import React, { useState, useRef, useEffect } from "react";
3
3
  import { Alert } from "../feedback/Alert.js";
4
4
  import { Badge } from "../display/Badge.js";
5
5
  import { BrandMark } from "../utilities/BrandMark.js";
@@ -9,6 +9,7 @@ import { Icon } from "../utilities/Icon.js";
9
9
  import { IconButton } from "../buttons/IconButton.js";
10
10
  import { SecretField } from "../inputs/SecretField.js";
11
11
  import { Select } from "../inputs/Select.js";
12
+ import { Spinner } from "../feedback/Spinner.js";
12
13
  import { StatusPill } from "../display/StatusPill.js";
13
14
  import { Switch } from "../inputs/Switch.js";
14
15
  import { TestRow } from "./TestRow.js";
@@ -129,7 +130,49 @@ const S_FIELD_MAP = [
129
130
  { from: null, to: "提交时间", tag: "自动" },
130
131
  { from: null, to: "来源链接", tag: "自动" }
131
132
  ];
132
- function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_KEY }) {
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;
133
176
  const load = () => {
134
177
  try {
135
178
  return JSON.parse(localStorage.getItem(storageKey)) || {};
@@ -137,20 +180,33 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
137
180
  return {};
138
181
  }
139
182
  };
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 || "");
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
+ };
152
206
  const [dirty, setDirty] = useState(false);
153
- const [saved, setSaved] = useState(!!boot.saved);
207
+ const [saving, setSaving] = useState(false);
208
+ const [dsKeyEdited, setDsKeyEdited] = useState(false);
209
+ const [secretEdited, setSecretEdited] = useState(false);
154
210
  useEffect(() => {
155
211
  const onKey = (e) => {
156
212
  if (e.key === "Escape") onClose && onClose();
@@ -158,124 +214,130 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
158
214
  window.addEventListener("keydown", onKey);
159
215
  return () => window.removeEventListener("keydown", onKey);
160
216
  }, [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
217
  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
- };
218
+ if (controlled) return;
181
219
  try {
182
- localStorage.setItem(storageKey, JSON.stringify(snap));
220
+ localStorage.setItem(storageKey, JSON.stringify(localCfg));
183
221
  } catch (e) {
184
222
  }
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 = () => {
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 });
201
235
  setDirty(true);
202
- setSaved(false);
203
236
  };
204
- const editDs = (setter) => (v) => {
205
- setter(v);
206
- if (dsStatus !== "idle") {
207
- setDsStatus("idle");
208
- setDsResult("");
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 = "";
209
242
  }
210
- touch();
243
+ update(patch);
244
+ setDirty(true);
211
245
  };
212
- const editFs = (setter) => (v) => {
213
- setter(v);
214
- if (fsStatus !== "idle") {
215
- setFsStatus("idle");
216
- setFsResult("");
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 = "";
217
251
  }
218
- touch();
252
+ update(patch);
253
+ setDirty(true);
219
254
  };
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}`);
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
+ );
228
283
  } else {
229
- setDsStatus("error");
230
- setDsResult(k ? "密钥无效或额度不足,请核对后重试" : "请先填写 API Key");
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);
231
293
  }
232
- touch();
233
294
  };
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();
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
+ }
242
310
  return;
243
311
  }
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();
312
+ builtinTest(which);
259
313
  };
260
- const onSave = () => {
261
- setSaved(true);
314
+ const afterSaved = () => {
262
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
+ }
263
334
  };
264
335
  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);
336
+ setDsKeyEdited(false);
337
+ setSecretEdited(false);
278
338
  setDirty(false);
339
+ if (controlled) return;
340
+ setLocalCfg(s_normalize(load()));
279
341
  };
280
342
  return /* @__PURE__ */ jsx("div", { className: "s-overlay", role: "dialog", "aria-modal": "true", "aria-label": "集成设置", children: /* @__PURE__ */ jsxs("div", { className: "s-modal", children: [
281
343
  /* @__PURE__ */ jsxs("header", { className: "s-modal__bar", children: [
@@ -318,13 +380,13 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
318
380
  /* @__PURE__ */ jsxs(
319
381
  "section",
320
382
  {
321
- className: "s-card" + (dsStatus === "ok" ? " is-ok" : dsStatus === "error" ? " is-error" : ""),
383
+ className: "s-card" + (dsPill === "ok" ? " is-ok" : dsPill === "error" ? " is-error" : ""),
322
384
  children: [
323
385
  /* @__PURE__ */ jsxs("div", { className: "s-card__head", children: [
324
386
  /* @__PURE__ */ jsxs("div", { className: "s-card__toprow", children: [
325
387
  /* @__PURE__ */ jsx("div", { className: "s-card__icon", children: /* @__PURE__ */ jsx(Icon, { name: "key", size: 16 }) }),
326
388
  /* @__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 }) })
389
+ /* @__PURE__ */ jsx("span", { className: "s-card__status", children: /* @__PURE__ */ jsx(StatusPill, { status: dsPill }) })
328
390
  ] }),
329
391
  /* @__PURE__ */ jsx("h2", { className: "s-card__title", children: "DeepSeek" }),
330
392
  /* @__PURE__ */ jsx("p", { className: "s-card__desc", children: "驱动对话式交互。用户发送的每一条消息,都通过这把密钥调用 DeepSeek 补全。" })
@@ -335,9 +397,10 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
335
397
  {
336
398
  label: "API KEY",
337
399
  value: dsKey,
338
- onChange: editDs(setDsKey),
339
- placeholder: "sk-xxxxxxxxxxxxxxxxxxxxxxxx",
340
- error: dsStatus === "error" && !dsKey.trim() ? "此项必填" : void 0
400
+ onChange: editDsKey,
401
+ placeholder: maskedDs ? "已保存 ········ · 留空则保持不变" : "sk-xxxxxxxxxxxxxxxxxxxxxxxx",
402
+ hint: maskedDs ? "已存密钥 · 留空表示不修改,输入新值即覆盖" : void 0,
403
+ error: !maskedDs && dsStatus === "error" && !dsKey.trim() ? "此项必填" : void 0
341
404
  }
342
405
  ),
343
406
  /* @__PURE__ */ jsxs("div", { className: "s-lock", children: [
@@ -354,7 +417,7 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
354
417
  Select,
355
418
  {
356
419
  value: dsModel,
357
- onChange: (e) => editDs(setDsModel)(e.target.value),
420
+ onChange: (e) => editDs("dsModel")(e.target.value),
358
421
  options: [
359
422
  { value: "deepseek-chat", label: "deepseek-chat · 通用 · 快" },
360
423
  { value: "deepseek-reasoner", label: "deepseek-reasoner · 深度推理" }
@@ -378,10 +441,7 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
378
441
  {
379
442
  label: "启用每月用量上限",
380
443
  checked: capOn,
381
- onChange: (e) => {
382
- setCapOn(e.target.checked);
383
- touch();
384
- }
444
+ onChange: (e) => setCfg({ capOn: e.target.checked })
385
445
  }
386
446
  ),
387
447
  /* @__PURE__ */ jsxs("div", { className: "s-cap__field", style: { display: capOn ? "flex" : "none" }, children: [
@@ -392,10 +452,7 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
392
452
  type: "text",
393
453
  inputMode: "numeric",
394
454
  value: cap,
395
- onChange: (e) => {
396
- setCap(e.target.value.replace(/[^0-9]/g, ""));
397
- touch();
398
- },
455
+ onChange: (e) => setCfg({ cap: e.target.value.replace(/[^0-9]/g, "") }),
399
456
  "aria-label": "每月上限(元)"
400
457
  }
401
458
  ),
@@ -433,8 +490,8 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
433
490
  {
434
491
  status: dsStatus,
435
492
  result: dsResult,
436
- onTest: testDeepSeek,
437
- disabled: !dsKey.trim(),
493
+ onTest: () => runTest("deepseek"),
494
+ disabled: !dsKey.trim() && !maskedDs,
438
495
  idleHint: "填写密钥后测试连通性"
439
496
  }
440
497
  )
@@ -444,13 +501,13 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
444
501
  /* @__PURE__ */ jsxs(
445
502
  "section",
446
503
  {
447
- className: "s-card" + (fsStatus === "ok" ? " is-ok" : fsStatus === "error" ? " is-error" : ""),
504
+ className: "s-card" + (fsPill === "ok" ? " is-ok" : fsPill === "error" ? " is-error" : ""),
448
505
  children: [
449
506
  /* @__PURE__ */ jsxs("div", { className: "s-card__head", children: [
450
507
  /* @__PURE__ */ jsxs("div", { className: "s-card__toprow", children: [
451
508
  /* @__PURE__ */ jsx("div", { className: "s-card__icon", children: /* @__PURE__ */ jsx(Icon, { name: "table", size: 16 }) }),
452
509
  /* @__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 }) })
510
+ /* @__PURE__ */ jsx("span", { className: "s-card__status", children: /* @__PURE__ */ jsx(StatusPill, { status: fsPill }) })
454
511
  ] }),
455
512
  /* @__PURE__ */ jsx("h2", { className: "s-card__title", children: "飞书多维表格" }),
456
513
  /* @__PURE__ */ jsx("p", { className: "s-card__desc", children: "每次提交后,数据自动写入指定多维表格的一行。需要一个飞书自建应用的凭证,以及目标表格的链接。" })
@@ -467,7 +524,7 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
467
524
  value: appId,
468
525
  spellCheck: "false",
469
526
  placeholder: "cli_xxxxxxxxxxxx",
470
- onChange: (e) => editFs(setAppId)(e.target.value)
527
+ onChange: (e) => editFs("appId")(e.target.value)
471
528
  }
472
529
  ),
473
530
  /* @__PURE__ */ jsx("p", { className: "s-field__hint", children: "应用标识,可公开。" })
@@ -477,9 +534,9 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
477
534
  {
478
535
  label: "App Secret",
479
536
  value: secret,
480
- onChange: editFs(setSecret),
481
- placeholder: "••••••••••••••••",
482
- hint: "应用密钥,加密存储。"
537
+ onChange: editSecret,
538
+ placeholder: maskedFs ? "已保存 ········ · 留空则保持不变" : "••••••••••••••••",
539
+ hint: maskedFs ? "已存密钥 · 留空表示不修改" : "应用密钥,加密存储。"
483
540
  }
484
541
  )
485
542
  ] }),
@@ -493,7 +550,7 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
493
550
  value: link,
494
551
  spellCheck: "false",
495
552
  placeholder: "https://your-team.feishu.cn/base/bascn…?table=tbl…",
496
- onChange: (e) => editFs(setLink)(e.target.value)
553
+ onChange: (e) => editFs("link")(e.target.value)
497
554
  }
498
555
  ),
499
556
  /* @__PURE__ */ jsx("p", { className: "s-field__hint", children: "在多维表格右上角「分享」中复制链接粘贴即可,App Token 与数据表会自动识别。" })
@@ -586,8 +643,8 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
586
643
  {
587
644
  status: fsStatus,
588
645
  result: fsResult,
589
- onTest: testFeishu,
590
- disabled: !appId.trim() || !secret.trim() || !parsed,
646
+ onTest: () => runTest("feishu"),
647
+ disabled: !appId.trim() || !secret.trim() && !maskedFs || !parsed,
591
648
  idleHint: "填写凭证与链接后测试写入权限"
592
649
  }
593
650
  )
@@ -614,9 +671,10 @@ function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_K
614
671
  {
615
672
  variant: "primary",
616
673
  size: "md",
617
- icon: /* @__PURE__ */ jsx(Icon, { name: "save", size: 14 }),
618
- disabled: !allReady || !dirty,
619
- onClick: onSave,
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,
620
678
  children: "保存配置"
621
679
  }
622
680
  )
@@ -1 +1 @@
1
- {"version":3,"file":"IntegrationSettings.js","sources":["../../../src/components/settings/IntegrationSettings.jsx"],"sourcesContent":["import React, { useState, useEffect, useRef } from \"react\";\nimport { Alert } from \"../feedback/Alert.jsx\";\nimport { Badge } from \"../display/Badge.jsx\";\nimport { BrandMark } from \"../utilities/BrandMark.jsx\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { HelpSteps } from \"./HelpSteps.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\nimport { IconButton } from \"../buttons/IconButton.jsx\";\nimport { SecretField } from \"../inputs/SecretField.jsx\";\nimport { Select } from \"../inputs/Select.jsx\";\nimport { StatusPill } from \"../display/StatusPill.jsx\";\nimport { Switch } from \"../inputs/Switch.jsx\";\nimport { TestRow } from \"./TestRow.jsx\";\n\n// IntegrationSettings — fullscreen modal for connecting the two services an\n// Agentaily designer needs to run: a DeepSeek key (drives the conversation) and\n// a Feishu Bitable (receives the data). Self-persisting to localStorage; reports\n// readiness; gated save. The one cross-app difference (a monthly usage cap) is\n// behind `showUsageCap`. Composes SecretField / StatusPill / TestRow / HelpSteps.\nconst AX_INTEGRATION_CSS = `\n.s-overlay { position: fixed; inset: 0; z-index: 100; display: flex; animation: s-fade var(--dur-2) var(--ease-out) both; }\n.s-modal { position: relative; flex: 1; display: flex; flex-direction: column; min-height: 0;\n background: var(--surface-page); animation: s-rise var(--dur-3) var(--ease-out) both; }\n@keyframes s-fade { from { opacity: 0; } to { opacity: 1; } }\n@keyframes s-rise { from { opacity: 0; transform: translateY(10px) scale(0.994); } to { opacity: 1; transform: none; } }\n@media (prefers-reduced-motion: reduce) { .s-overlay, .s-modal { animation: none; } }\n\n.s-modal__bar { flex: none; display: flex; align-items: center; gap: 16px; height: 52px;\n padding: 0 12px 0 18px; border-bottom: 1px solid var(--border-default); background: var(--surface-panel); }\n.s-modal__brand { display: flex; align-items: center; gap: 8px; }\n.s-modal__word { font-family: var(--font-mono); font-size: 15px; font-weight: 500; letter-spacing: -0.02em; color: var(--text-body); }\n.s-modal__div { color: var(--text-faint); }\n.s-modal__crumb { font-family: var(--font-mono); font-size: 13px; color: var(--text-body); }\n.s-modal__baractions { margin-left: auto; display: flex; align-items: center; gap: 12px; flex: none; }\n\n.s-modal__body { flex: 1; overflow-y: auto; min-height: 0; }\n.s-wrap { max-width: 768px; margin: 0 auto; padding: 40px 24px 48px; }\n\n.s-hero { margin-bottom: 28px; }\n.s-hero__kicker { color: var(--text-faint); margin-bottom: 12px; }\n.s-hero__h { font-family: var(--font-display); font-size: var(--text-2xl); font-weight: 500;\n letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); color: var(--text-body); margin: 0 0 10px; }\n.s-hero__p { font-size: var(--text-md); color: var(--text-muted); line-height: var(--leading-body); margin: 0; max-width: 62ch; }\n\n.s-ready { display: flex; align-items: center; gap: 14px; padding: 13px 16px; margin-bottom: 26px;\n border: 1px solid var(--border-default); border-radius: var(--radius-3); background: var(--surface-panel); }\n.s-ready__steps { display: flex; align-items: center; gap: 7px; flex: none; }\n.s-ready__dot { width: 9px; height: 9px; border-radius: var(--radius-1); border: 1px solid var(--border-strong);\n background: transparent; transition: background var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out); }\n.s-ready__dot.is-on { background: var(--ok); border-color: var(--ok); }\n.s-ready__txt { flex: 1; min-width: 0; font-size: var(--text-sm); color: var(--text-muted); }\n.s-ready__txt strong { color: var(--text-body); font-weight: 500; }\n.s-ready__count { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint); letter-spacing: 0.04em; flex: none; }\n\n.s-cards { display: flex; flex-direction: column; gap: 18px; }\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\n.s-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n@media (max-width: 560px) { .s-row2 { grid-template-columns: 1fr; } }\n\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.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\n.s-cap { display: flex; align-items: center; gap: 12px; padding-top: 4px; }\n.s-cap__field { display: flex; align-items: center; gap: 8px; }\n.s-cap__field .ax-input { width: 132px; }\n.s-cap.is-off { opacity: 0.5; }\n\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\n.s-sub { display: flex; align-items: center; gap: 8px; }\n.s-sub__line { flex: 1; height: 1px; background: var(--border-default); }\n\n.s-save { flex: none; border-top: 1px solid var(--border-default); background: var(--surface-panel); }\n.s-save__inner { max-width: 768px; margin: 0 auto; padding: 12px 24px; display: flex; align-items: center; gap: 16px; }\n.s-save__status { flex: 1; min-width: 0; display: flex; align-items: center; gap: 9px; font-size: var(--text-sm); color: var(--text-muted); }\n.s-save__status svg { flex: none; }\n.s-save__actions { display: flex; align-items: center; gap: 8px; flex: none; }\n@media (max-width: 520px) { .s-wrap { padding: 24px 16px 36px; } .s-modal__baractions { gap: 6px; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-integration-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-integration-css\";\n s.textContent = AX_INTEGRATION_CSS;\n document.head.appendChild(s);\n}\n\nconst S_LS_KEY = \"agentaily.integrations.v1\";\nconst s_sleep = (ms) => new Promise((r) => setTimeout(r, ms));\n\nfunction s_parseFeishu(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 S_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\nfunction s_load() {\n try {\n return JSON.parse(localStorage.getItem(S_LS_KEY)) || {};\n } catch (e) {\n return {};\n }\n}\n\nexport function IntegrationSettings({ onClose, showUsageCap = true, storageKey = S_LS_KEY }) {\n const load = () => {\n try {\n return JSON.parse(localStorage.getItem(storageKey)) || {};\n } catch (e) {\n return {};\n }\n };\n const boot = useRef(load()).current;\n\n const [dsKey, setDsKey] = useState(boot.dsKey || \"\");\n const [dsModel, setDsModel] = useState(boot.dsModel || \"deepseek-chat\");\n const [capOn, setCapOn] = useState(!!boot.capOn);\n const [cap, setCap] = useState(boot.cap || \"200\");\n const [dsStatus, setDsStatus] = useState(boot.dsStatus === \"ok\" ? \"ok\" : \"idle\");\n const [dsResult, setDsResult] = useState(boot.dsResult || \"\");\n\n const [appId, setAppId] = useState(boot.appId || \"\");\n const [secret, setSecret] = useState(boot.secret || \"\");\n const [link, setLink] = useState(boot.link || \"\");\n const [fsStatus, setFsStatus] = useState(boot.fsStatus === \"ok\" ? \"ok\" : \"idle\");\n const [fsResult, setFsResult] = useState(boot.fsResult || \"\");\n\n const [dirty, setDirty] = useState(false);\n const [saved, setSaved] = useState(!!boot.saved);\n\n useEffect(() => {\n const onKey = (e) => {\n if (e.key === \"Escape\") onClose && onClose();\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [onClose]);\n\n const parsed = s_parseFeishu(link);\n const dsConnected = dsStatus === \"ok\";\n const fsConnected = fsStatus === \"ok\";\n const readyCount = (dsConnected ? 1 : 0) + (fsConnected ? 1 : 0);\n const allReady = dsConnected && fsConnected;\n\n useEffect(() => {\n const snap = {\n dsKey,\n dsModel,\n capOn,\n cap,\n dsStatus,\n dsResult,\n appId,\n secret,\n link,\n fsStatus,\n fsResult,\n saved,\n };\n try {\n localStorage.setItem(storageKey, JSON.stringify(snap));\n } catch (e) {}\n }, [\n dsKey,\n dsModel,\n capOn,\n cap,\n dsStatus,\n dsResult,\n appId,\n secret,\n link,\n fsStatus,\n fsResult,\n saved,\n storageKey,\n ]);\n\n const touch = () => {\n setDirty(true);\n setSaved(false);\n };\n const editDs = (setter) => (v) => {\n setter(v);\n if (dsStatus !== \"idle\") {\n setDsStatus(\"idle\");\n setDsResult(\"\");\n }\n touch();\n };\n const editFs = (setter) => (v) => {\n setter(v);\n if (fsStatus !== \"idle\") {\n setFsStatus(\"idle\");\n setFsResult(\"\");\n }\n touch();\n };\n\n const testDeepSeek = async () => {\n setDsStatus(\"testing\");\n setDsResult(\"\");\n await s_sleep(1300);\n const k = dsKey.trim();\n if (k.startsWith(\"sk-\") && k.length >= 20) {\n setDsStatus(\"ok\");\n setDsResult(`连接正常 · 延迟 0.4s · ${dsModel}`);\n } else {\n setDsStatus(\"error\");\n setDsResult(k ? \"密钥无效或额度不足,请核对后重试\" : \"请先填写 API Key\");\n }\n touch();\n };\n\n const testFeishu = async () => {\n setFsStatus(\"testing\");\n setFsResult(\"\");\n await s_sleep(1500);\n if (!appId.trim() || !secret.trim()) {\n setFsStatus(\"error\");\n setFsResult(\"缺少 App ID 或 App Secret\");\n touch();\n return;\n }\n if (!parsed) {\n setFsStatus(\"error\");\n setFsResult(\"无法识别多维表格链接,请粘贴完整 URL\");\n touch();\n return;\n }\n if (!parsed.table) {\n setFsStatus(\"error\");\n setFsResult(\"链接缺少数据表 (table) 参数\");\n touch();\n return;\n }\n setFsStatus(\"ok\");\n setFsResult(\"已连接 ·「报名登记表」· 检测到 6 个字段\");\n touch();\n };\n\n const onSave = () => {\n setSaved(true);\n setDirty(false);\n };\n const onDiscard = () => {\n const b = load();\n setDsKey(b.dsKey || \"\");\n setDsModel(b.dsModel || \"deepseek-chat\");\n setCapOn(!!b.capOn);\n setCap(b.cap || \"200\");\n setDsStatus(b.dsStatus === \"ok\" ? \"ok\" : \"idle\");\n setDsResult(b.dsResult || \"\");\n setAppId(b.appId || \"\");\n setSecret(b.secret || \"\");\n setLink(b.link || \"\");\n setFsStatus(b.fsStatus === \"ok\" ? \"ok\" : \"idle\");\n setFsResult(b.fsResult || \"\");\n setSaved(!!b.saved);\n setDirty(false);\n };\n\n return (\n <div className=\"s-overlay\" role=\"dialog\" aria-modal=\"true\" aria-label=\"集成设置\">\n <div className=\"s-modal\">\n <header className=\"s-modal__bar\">\n <div className=\"s-modal__brand\">\n <BrandMark size={16} />\n <span className=\"s-modal__word\">agentaily</span>\n <span className=\"s-modal__div\">/</span>\n <span className=\"s-modal__crumb\">集成设置</span>\n </div>\n <div className=\"s-modal__baractions\">\n <Badge variant={allReady ? \"ok\" : \"neutral\"} dot={allReady}>\n {allReady ? \"就绪\" : \"未就绪\"}\n </Badge>\n <IconButton label=\"关闭\" onClick={() => onClose && onClose()}>\n <Icon name=\"x\" size={16} />\n </IconButton>\n </div>\n </header>\n\n <main className=\"s-modal__body\">\n <div className=\"s-wrap\">\n <div className=\"s-hero\">\n <div className=\"ax-label s-hero__kicker\">集成 · INTEGRATIONS</div>\n <h1 className=\"s-hero__h\">连接你的服务</h1>\n <p className=\"s-hero__p\">\n 运行需要两条连接:一把驱动对话的 DeepSeek\n 密钥,和一张接收数据的飞书多维表格。两项都连通后即可发布。\n </p>\n </div>\n\n <div className=\"s-ready\">\n <div className=\"s-ready__steps\">\n <span className={\"s-ready__dot\" + (dsConnected ? \" is-on\" : \"\")}></span>\n <span className={\"s-ready__dot\" + (fsConnected ? \" is-on\" : \"\")}></span>\n </div>\n <div className=\"s-ready__txt\">\n {allReady ? (\n <span>\n <strong>两项连接均已就绪。</strong>保存后即可开始运行。\n </span>\n ) : (\n <span>\n 完成下方 <strong>2 项</strong> 连接后才能发布运行。\n </span>\n )}\n </div>\n <span className=\"s-ready__count\">{readyCount} / 2</span>\n </div>\n\n <div className=\"s-cards\">\n {/* ── Card 1 · DeepSeek ── */}\n <section\n className={\n \"s-card\" +\n (dsStatus === \"ok\" ? \" is-ok\" : dsStatus === \"error\" ? \" is-error\" : \"\")\n }\n >\n <div className=\"s-card__head\">\n <div className=\"s-card__toprow\">\n <div className=\"s-card__icon\">\n <Icon name=\"key\" size={16} />\n </div>\n <span className=\"ax-label s-card__eyebrow\">对话引擎 · LLM</span>\n <span className=\"s-card__status\">\n <StatusPill status={dsStatus} />\n </span>\n </div>\n <h2 className=\"s-card__title\">DeepSeek</h2>\n <p className=\"s-card__desc\">\n 驱动对话式交互。用户发送的每一条消息,都通过这把密钥调用 DeepSeek 补全。\n </p>\n </div>\n\n <div className=\"s-card__body\">\n <SecretField\n label=\"API KEY\"\n value={dsKey}\n onChange={editDs(setDsKey)}\n placeholder=\"sk-xxxxxxxxxxxxxxxxxxxxxxxx\"\n error={dsStatus === \"error\" && !dsKey.trim() ? \"此项必填\" : undefined}\n />\n\n <div className=\"s-lock\">\n <Icon name=\"lock\" size={14} />\n <span>\n <strong>密钥加密存储</strong>,仅在服务端发起调用。用户\n <strong>永远看不到、也拿不到这把密钥。</strong>\n </span>\n </div>\n\n <div className=\"s-row2\">\n <div>\n <label className=\"s-field__label ax-label\">对话模型</label>\n <Select\n value={dsModel}\n onChange={(e) => editDs(setDsModel)(e.target.value)}\n options={[\n { value: \"deepseek-chat\", label: \"deepseek-chat · 通用 · 快\" },\n { value: \"deepseek-reasoner\", label: \"deepseek-reasoner · 深度推理\" },\n ]}\n />\n </div>\n </div>\n\n {showUsageCap ? (\n <React.Fragment>\n <Alert\n variant=\"warn\"\n icon={<Icon name=\"zap\" size={15} />}\n title=\"用量由你承担\"\n >\n 每一轮对话消耗的都是你自己 DeepSeek\n 账户的额度。用得越多,调用越多——建议设置每月上限以防意外超支。\n </Alert>\n <div className={\"s-cap\" + (capOn ? \"\" : \" is-off\")}>\n <Switch\n label=\"启用每月用量上限\"\n checked={capOn}\n onChange={(e) => {\n setCapOn(e.target.checked);\n touch();\n }}\n />\n <div className=\"s-cap__field\" style={{ display: capOn ? \"flex\" : \"none\" }}>\n <input\n className=\"ax-input ax-input--mono\"\n type=\"text\"\n inputMode=\"numeric\"\n value={cap}\n onChange={(e) => {\n setCap(e.target.value.replace(/[^0-9]/g, \"\"));\n touch();\n }}\n aria-label=\"每月上限(元)\"\n />\n <span className=\"s-field__hint\" style={{ margin: 0 }}>\n 元 / 月,达到后暂停对话\n </span>\n </div>\n </div>\n </React.Fragment>\n ) : null}\n\n <HelpSteps\n title=\"如何获取 DeepSeek API Key?\"\n steps={[\n <>\n 登录 <code>platform.deepseek.com</code>,进入「API Keys」页面。\n </>,\n <>点击「创建 API Key」,命名后立即复制——密钥只在创建时完整显示一次。</>,\n <>在「充值」中确认账户余额充足,对话才能持续调用。</>,\n <>\n 把以 <code>sk-</code> 开头的密钥粘贴到上方输入框。\n </>,\n ]}\n link={{\n href: \"https://platform.deepseek.com\",\n label: \"打开 DeepSeek 开放平台\",\n }}\n />\n </div>\n\n <TestRow\n status={dsStatus}\n result={dsResult}\n onTest={testDeepSeek}\n disabled={!dsKey.trim()}\n idleHint=\"填写密钥后测试连通性\"\n />\n </section>\n\n {/* ── Card 2 · Feishu Bitable ── */}\n <section\n className={\n \"s-card\" +\n (fsStatus === \"ok\" ? \" is-ok\" : fsStatus === \"error\" ? \" is-error\" : \"\")\n }\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={fsStatus} />\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\"\n type=\"text\"\n value={appId}\n spellCheck=\"false\"\n placeholder=\"cli_xxxxxxxxxxxx\"\n onChange={(e) => editFs(setAppId)(e.target.value)}\n />\n <p className=\"s-field__hint\">应用标识,可公开。</p>\n </div>\n <SecretField\n label=\"App Secret\"\n value={secret}\n onChange={editFs(setSecret)}\n placeholder=\"••••••••••••••••\"\n hint=\"应用密钥,加密存储。\"\n />\n </div>\n\n <div>\n <label className=\"s-field__label ax-label\">多维表格链接</label>\n <input\n className=\"ax-input\"\n type=\"text\"\n value={link}\n spellCheck=\"false\"\n placeholder=\"https://your-team.feishu.cn/base/bascn…?table=tbl…\"\n onChange={(e) => editFs(setLink)(e.target.value)}\n />\n <p className=\"s-field__hint\">\n 在多维表格右上角「分享」中复制链接粘贴即可,App Token 与数据表会自动识别。\n </p>\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 {fsConnected ? (\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 {S_FIELD_MAP.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 <HelpSteps\n title=\"如何获取飞书应用凭证?\"\n steps={[\n <>\n 打开飞书开放平台 <code>open.feishu.cn</code>,创建一个「企业自建应用」。\n </>,\n <>\n 在「凭证与基础信息」中复制 <code>App ID</code> 与 <code>App Secret</code>。\n </>,\n <>\n 到「权限管理」开通多维表格读写权限 <code>bitable:app</code>,并发布版本。\n </>,\n <>\n 在目标多维表格里,把这个应用添加为<strong>可编辑协作者</strong>。\n </>,\n <>复制该多维表格的分享链接,粘贴到上方「多维表格链接」。</>,\n ]}\n link={{ href: \"https://open.feishu.cn\", label: \"打开飞书开放平台\" }}\n />\n </div>\n\n <TestRow\n status={fsStatus}\n result={fsResult}\n onTest={testFeishu}\n disabled={!appId.trim() || !secret.trim() || !parsed}\n idleHint=\"填写凭证与链接后测试写入权限\"\n />\n </section>\n </div>\n </div>\n </main>\n\n <footer className=\"s-save\">\n <div className=\"s-save__inner\">\n <div className=\"s-save__status\">\n {!allReady ? (\n <>\n <Icon name=\"info\" size={15} style={{ color: \"var(--text-faint)\" }} />\n <span>完成两项连接后即可保存并启用。</span>\n </>\n ) : saved ? (\n <>\n <Icon name=\"check\" size={15} style={{ color: \"var(--ok)\" }} />\n <span>已保存 · 已启用对话与数据写入。</span>\n </>\n ) : (\n <>\n <Icon name=\"zap\" size={15} style={{ color: \"var(--text-muted)\" }} />\n <span>配置已就绪,保存后立即生效。</span>\n </>\n )}\n </div>\n <div className=\"s-save__actions\">\n <Button variant=\"ghost\" size=\"md\" disabled={!dirty} onClick={onDiscard}>\n 放弃更改\n </Button>\n <Button\n variant=\"primary\"\n size=\"md\"\n icon={<Icon name=\"save\" size={14} />}\n disabled={!allReady || !dirty}\n onClick={onSave}\n >\n 保存配置\n </Button>\n </div>\n </div>\n </footer>\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"IntegrationSettings.js","sources":["../../../src/components/settings/IntegrationSettings.jsx"],"sourcesContent":["import React, { useState, useEffect, useRef } from \"react\";\nimport { Alert } from \"../feedback/Alert.jsx\";\nimport { Badge } from \"../display/Badge.jsx\";\nimport { BrandMark } from \"../utilities/BrandMark.jsx\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { HelpSteps } from \"./HelpSteps.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\nimport { IconButton } from \"../buttons/IconButton.jsx\";\nimport { SecretField } from \"../inputs/SecretField.jsx\";\nimport { Select } from \"../inputs/Select.jsx\";\nimport { Spinner } from \"../feedback/Spinner.jsx\";\nimport { StatusPill } from \"../display/StatusPill.jsx\";\nimport { Switch } from \"../inputs/Switch.jsx\";\nimport { TestRow } from \"./TestRow.jsx\";\n\n// IntegrationSettings — fullscreen modal for connecting the two services an\n// Agentaily designer needs to run: a DeepSeek key (drives the conversation) and\n// a Feishu Bitable (receives the data). Self-persisting to localStorage; reports\n// readiness; gated save. The one cross-app difference (a monthly usage cap) is\n// behind `showUsageCap`. Composes SecretField / StatusPill / TestRow / HelpSteps.\nconst AX_INTEGRATION_CSS = `\n.s-overlay { position: fixed; inset: 0; z-index: 100; display: flex; animation: s-fade var(--dur-2) var(--ease-out) both; }\n.s-modal { position: relative; flex: 1; display: flex; flex-direction: column; min-height: 0;\n background: var(--surface-page); animation: s-rise var(--dur-3) var(--ease-out) both; }\n@keyframes s-fade { from { opacity: 0; } to { opacity: 1; } }\n@keyframes s-rise { from { opacity: 0; transform: translateY(10px) scale(0.994); } to { opacity: 1; transform: none; } }\n@media (prefers-reduced-motion: reduce) { .s-overlay, .s-modal { animation: none; } }\n\n.s-modal__bar { flex: none; display: flex; align-items: center; gap: 16px; height: 52px;\n padding: 0 12px 0 18px; border-bottom: 1px solid var(--border-default); background: var(--surface-panel); }\n.s-modal__brand { display: flex; align-items: center; gap: 8px; }\n.s-modal__word { font-family: var(--font-mono); font-size: 15px; font-weight: 500; letter-spacing: -0.02em; color: var(--text-body); }\n.s-modal__div { color: var(--text-faint); }\n.s-modal__crumb { font-family: var(--font-mono); font-size: 13px; color: var(--text-body); }\n.s-modal__baractions { margin-left: auto; display: flex; align-items: center; gap: 12px; flex: none; }\n\n.s-modal__body { flex: 1; overflow-y: auto; min-height: 0; }\n.s-wrap { max-width: 768px; margin: 0 auto; padding: 40px 24px 48px; }\n\n.s-hero { margin-bottom: 28px; }\n.s-hero__kicker { color: var(--text-faint); margin-bottom: 12px; }\n.s-hero__h { font-family: var(--font-display); font-size: var(--text-2xl); font-weight: 500;\n letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); color: var(--text-body); margin: 0 0 10px; }\n.s-hero__p { font-size: var(--text-md); color: var(--text-muted); line-height: var(--leading-body); margin: 0; max-width: 62ch; }\n\n.s-ready { display: flex; align-items: center; gap: 14px; padding: 13px 16px; margin-bottom: 26px;\n border: 1px solid var(--border-default); border-radius: var(--radius-3); background: var(--surface-panel); }\n.s-ready__steps { display: flex; align-items: center; gap: 7px; flex: none; }\n.s-ready__dot { width: 9px; height: 9px; border-radius: var(--radius-1); border: 1px solid var(--border-strong);\n background: transparent; transition: background var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out); }\n.s-ready__dot.is-on { background: var(--ok); border-color: var(--ok); }\n.s-ready__txt { flex: 1; min-width: 0; font-size: var(--text-sm); color: var(--text-muted); }\n.s-ready__txt strong { color: var(--text-body); font-weight: 500; }\n.s-ready__count { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint); letter-spacing: 0.04em; flex: none; }\n\n.s-cards { display: flex; flex-direction: column; gap: 18px; }\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\n.s-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n@media (max-width: 560px) { .s-row2 { grid-template-columns: 1fr; } }\n\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.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\n.s-cap { display: flex; align-items: center; gap: 12px; padding-top: 4px; }\n.s-cap__field { display: flex; align-items: center; gap: 8px; }\n.s-cap__field .ax-input { width: 132px; }\n.s-cap.is-off { opacity: 0.5; }\n\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\n.s-sub { display: flex; align-items: center; gap: 8px; }\n.s-sub__line { flex: 1; height: 1px; background: var(--border-default); }\n\n.s-save { flex: none; border-top: 1px solid var(--border-default); background: var(--surface-panel); }\n.s-save__inner { max-width: 768px; margin: 0 auto; padding: 12px 24px; display: flex; align-items: center; gap: 16px; }\n.s-save__status { flex: 1; min-width: 0; display: flex; align-items: center; gap: 9px; font-size: var(--text-sm); color: var(--text-muted); }\n.s-save__status svg { flex: none; }\n.s-save__actions { display: flex; align-items: center; gap: 8px; flex: none; }\n@media (max-width: 520px) { .s-wrap { padding: 24px 16px 36px; } .s-modal__baractions { gap: 6px; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-integration-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-integration-css\";\n s.textContent = AX_INTEGRATION_CSS;\n document.head.appendChild(s);\n}\n\nconst S_LS_KEY = \"agentaily.integrations.v1\";\nconst s_sleep = (ms) => new Promise((r) => setTimeout(r, ms));\n\nfunction s_parseFeishu(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 S_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 S_DEFAULTS = {\n dsKey: \"\",\n dsModel: \"deepseek-chat\",\n capOn: false,\n cap: \"200\",\n dsStatus: \"idle\",\n dsResult: \"\",\n appId: \"\",\n secret: \"\",\n link: \"\",\n fsStatus: \"idle\",\n fsResult: \"\",\n saved: false,\n};\n\n// Normalize a raw stored/external blob into a full config. Non-ok statuses\n// collapse to idle on (re)load — a stored \"error\"/\"testing\" is never trusted.\nfunction s_normalize(raw) {\n const r = raw || {};\n return {\n dsKey: r.dsKey || \"\",\n dsModel: r.dsModel || \"deepseek-chat\",\n capOn: !!r.capOn,\n cap: r.cap || \"200\",\n dsStatus: r.dsStatus === \"ok\" ? \"ok\" : \"idle\",\n dsResult: r.dsResult || \"\",\n appId: r.appId || \"\",\n secret: r.secret || \"\",\n link: r.link || \"\",\n fsStatus: r.fsStatus === \"ok\" ? \"ok\" : \"idle\",\n fsResult: r.fsResult || \"\",\n saved: !!r.saved,\n };\n}\n\nexport function IntegrationSettings({\n onClose,\n showUsageCap = true,\n storageKey = S_LS_KEY,\n value,\n onChange,\n onSave,\n onTest,\n readiness,\n masked,\n}) {\n // ── State source ──────────────────────────────────────────────────────────\n // Uncontrolled (default): own the config + self-persist to localStorage — the\n // exact legacy behavior. Controlled: `value` is the source of truth and every\n // mutation flows out through `onChange`; we never touch localStorage. The rest\n // of the component reads `cfg` and writes via `update()`, so both modes share\n // one code path.\n const controlled = value !== undefined;\n const load = () => {\n try {\n return JSON.parse(localStorage.getItem(storageKey)) || {};\n } catch (e) {\n return {};\n }\n };\n const [localCfg, setLocalCfg] = useState(() => (controlled ? S_DEFAULTS : s_normalize(load())));\n const cfg = controlled ? { ...S_DEFAULTS, ...value } : localCfg;\n const {\n dsKey,\n dsModel,\n capOn,\n cap,\n dsStatus,\n dsResult,\n appId,\n secret,\n link,\n fsStatus,\n fsResult,\n saved,\n } = cfg;\n // Always-fresh handle so async flows (test/save) read the latest config even\n // after the controlling parent has re-rendered mid-await.\n const cfgRef = useRef(cfg);\n cfgRef.current = cfg;\n\n const update = (partial) => {\n if (controlled) {\n if (onChange) onChange({ ...cfgRef.current, ...partial });\n } else setLocalCfg((c) => ({ ...c, ...partial }));\n };\n\n const [dirty, setDirty] = useState(false);\n const [saving, setSaving] = useState(false);\n const [dsKeyEdited, setDsKeyEdited] = useState(false);\n const [secretEdited, setSecretEdited] = useState(false);\n\n useEffect(() => {\n const onKey = (e) => {\n if (e.key === \"Escape\") onClose && onClose();\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [onClose]);\n\n // Self-persist ONLY when uncontrolled. Controlled callers own storage.\n useEffect(() => {\n if (controlled) return;\n try {\n localStorage.setItem(storageKey, JSON.stringify(localCfg));\n } catch (e) {}\n }, [controlled, localCfg, storageKey]);\n\n const parsed = s_parseFeishu(link);\n // Readiness: external override (`readiness.deepseek` / `.feishu`) wins over the\n // internal test status — lets a backend report \"already connected\" without a\n // fresh in-app test. The pill still shows live testing/error transitions.\n const dsConnected = readiness ? !!readiness.deepseek : dsStatus === \"ok\";\n const fsConnected = readiness ? !!readiness.feishu : fsStatus === \"ok\";\n const dsPill =\n dsStatus === \"testing\" || dsStatus === \"error\" ? dsStatus : dsConnected ? \"ok\" : \"idle\";\n const fsPill =\n fsStatus === \"testing\" || fsStatus === \"error\" ? fsStatus : fsConnected ? \"ok\" : \"idle\";\n const readyCount = (dsConnected ? 1 : 0) + (fsConnected ? 1 : 0);\n const allReady = dsConnected && fsConnected;\n\n // Masked echo: a stored secret exists server-side but is never sent back. While\n // the field is untouched we show a masked placeholder, keep the value empty, and\n // count it as \"present\" for test/validation — so the mask is never re-submitted.\n const maskedDs = !!(masked && masked.deepseek) && !dsKeyEdited;\n const maskedFs = !!(masked && masked.feishu) && !secretEdited;\n\n const setCfg = (partial) => {\n update({ ...partial, saved: false });\n setDirty(true);\n };\n const editDs = (key) => (v) => {\n const patch = { [key]: v, saved: false };\n if (cfgRef.current.dsStatus !== \"idle\") {\n patch.dsStatus = \"idle\";\n patch.dsResult = \"\";\n }\n update(patch);\n setDirty(true);\n };\n const editFs = (key) => (v) => {\n const patch = { [key]: v, saved: false };\n if (cfgRef.current.fsStatus !== \"idle\") {\n patch.fsStatus = \"idle\";\n patch.fsResult = \"\";\n }\n update(patch);\n setDirty(true);\n };\n const editDsKey = (v) => {\n if (!dsKeyEdited) setDsKeyEdited(true);\n editDs(\"dsKey\")(v);\n };\n const editSecret = (v) => {\n if (!secretEdited) setSecretEdited(true);\n editFs(\"secret\")(v);\n };\n\n const setTest = (which, status, result, finalize) => {\n const patch =\n which === \"deepseek\"\n ? { dsStatus: status, dsResult: result }\n : { fsStatus: status, fsResult: result };\n if (finalize) patch.saved = false;\n update(patch);\n if (finalize) setDirty(true);\n };\n\n // Built-in mock probe — used only when no `onTest` is wired.\n const builtinTest = async (which) => {\n if (which === \"deepseek\") {\n setTest(\"deepseek\", \"testing\", \"\", false);\n await s_sleep(1300);\n const k = cfgRef.current.dsKey.trim();\n if (k.startsWith(\"sk-\") && k.length >= 20)\n setTest(\"deepseek\", \"ok\", `连接正常 · 延迟 0.4s · ${cfgRef.current.dsModel}`, true);\n else\n setTest(\n \"deepseek\",\n \"error\",\n k ? \"密钥无效或额度不足,请核对后重试\" : \"请先填写 API Key\",\n true,\n );\n } else {\n setTest(\"feishu\", \"testing\", \"\", false);\n await s_sleep(1500);\n const c = cfgRef.current;\n const p = s_parseFeishu(c.link);\n if (!c.appId.trim() || !c.secret.trim())\n return setTest(\"feishu\", \"error\", \"缺少 App ID 或 App Secret\", true);\n if (!p) return setTest(\"feishu\", \"error\", \"无法识别多维表格链接,请粘贴完整 URL\", true);\n if (!p.table) return setTest(\"feishu\", \"error\", \"链接缺少数据表 (table) 参数\", true);\n setTest(\"feishu\", \"ok\", \"已连接 ·「报名登记表」· 检测到 6 个字段\", true);\n }\n };\n\n // `onTest(which)` → Promise<{ ok, message }> drives StatusPill + TestRow.\n const runTest = async (which) => {\n if (onTest) {\n setTest(which, \"testing\", \"\", false);\n try {\n const res = await onTest(which);\n const ok = !!(res && res.ok);\n setTest(\n which,\n ok ? \"ok\" : \"error\",\n (res && res.message) || (ok ? \"连接正常\" : \"连接失败\"),\n true,\n );\n } catch (e) {\n setTest(which, \"error\", (e && e.message) || \"测试失败\", true);\n }\n return;\n }\n builtinTest(which);\n };\n\n const afterSaved = () => {\n setDirty(false);\n setDsKeyEdited(false);\n setSecretEdited(false);\n };\n // `onSave(value)` → Promise. Save disables + spins while pending; resolve marks\n // saved (and re-masks secrets), reject leaves the form dirty for a retry.\n const doSave = async () => {\n if (onSave) {\n setSaving(true);\n try {\n await onSave(cfgRef.current);\n update({ saved: true });\n afterSaved();\n } catch (e) {\n /* keep dirty so the user can retry */\n } finally {\n setSaving(false);\n }\n } else {\n update({ saved: true });\n afterSaved();\n }\n };\n const onDiscard = () => {\n setDsKeyEdited(false);\n setSecretEdited(false);\n setDirty(false);\n if (controlled) return; // controlled callers own revert\n setLocalCfg(s_normalize(load()));\n };\n\n return (\n <div className=\"s-overlay\" role=\"dialog\" aria-modal=\"true\" aria-label=\"集成设置\">\n <div className=\"s-modal\">\n <header className=\"s-modal__bar\">\n <div className=\"s-modal__brand\">\n <BrandMark size={16} />\n <span className=\"s-modal__word\">agentaily</span>\n <span className=\"s-modal__div\">/</span>\n <span className=\"s-modal__crumb\">集成设置</span>\n </div>\n <div className=\"s-modal__baractions\">\n <Badge variant={allReady ? \"ok\" : \"neutral\"} dot={allReady}>\n {allReady ? \"就绪\" : \"未就绪\"}\n </Badge>\n <IconButton label=\"关闭\" onClick={() => onClose && onClose()}>\n <Icon name=\"x\" size={16} />\n </IconButton>\n </div>\n </header>\n\n <main className=\"s-modal__body\">\n <div className=\"s-wrap\">\n <div className=\"s-hero\">\n <div className=\"ax-label s-hero__kicker\">集成 · INTEGRATIONS</div>\n <h1 className=\"s-hero__h\">连接你的服务</h1>\n <p className=\"s-hero__p\">\n 运行需要两条连接:一把驱动对话的 DeepSeek\n 密钥,和一张接收数据的飞书多维表格。两项都连通后即可发布。\n </p>\n </div>\n\n <div className=\"s-ready\">\n <div className=\"s-ready__steps\">\n <span className={\"s-ready__dot\" + (dsConnected ? \" is-on\" : \"\")}></span>\n <span className={\"s-ready__dot\" + (fsConnected ? \" is-on\" : \"\")}></span>\n </div>\n <div className=\"s-ready__txt\">\n {allReady ? (\n <span>\n <strong>两项连接均已就绪。</strong>保存后即可开始运行。\n </span>\n ) : (\n <span>\n 完成下方 <strong>2 项</strong> 连接后才能发布运行。\n </span>\n )}\n </div>\n <span className=\"s-ready__count\">{readyCount} / 2</span>\n </div>\n\n <div className=\"s-cards\">\n {/* ── Card 1 · DeepSeek ── */}\n <section\n className={\n \"s-card\" + (dsPill === \"ok\" ? \" is-ok\" : dsPill === \"error\" ? \" is-error\" : \"\")\n }\n >\n <div className=\"s-card__head\">\n <div className=\"s-card__toprow\">\n <div className=\"s-card__icon\">\n <Icon name=\"key\" size={16} />\n </div>\n <span className=\"ax-label s-card__eyebrow\">对话引擎 · LLM</span>\n <span className=\"s-card__status\">\n <StatusPill status={dsPill} />\n </span>\n </div>\n <h2 className=\"s-card__title\">DeepSeek</h2>\n <p className=\"s-card__desc\">\n 驱动对话式交互。用户发送的每一条消息,都通过这把密钥调用 DeepSeek 补全。\n </p>\n </div>\n\n <div className=\"s-card__body\">\n <SecretField\n label=\"API KEY\"\n value={dsKey}\n onChange={editDsKey}\n placeholder={\n maskedDs\n ? \"已保存 ········ · 留空则保持不变\"\n : \"sk-xxxxxxxxxxxxxxxxxxxxxxxx\"\n }\n hint={maskedDs ? \"已存密钥 · 留空表示不修改,输入新值即覆盖\" : undefined}\n error={\n !maskedDs && dsStatus === \"error\" && !dsKey.trim() ? \"此项必填\" : undefined\n }\n />\n\n <div className=\"s-lock\">\n <Icon name=\"lock\" size={14} />\n <span>\n <strong>密钥加密存储</strong>,仅在服务端发起调用。用户\n <strong>永远看不到、也拿不到这把密钥。</strong>\n </span>\n </div>\n\n <div className=\"s-row2\">\n <div>\n <label className=\"s-field__label ax-label\">对话模型</label>\n <Select\n value={dsModel}\n onChange={(e) => editDs(\"dsModel\")(e.target.value)}\n options={[\n { value: \"deepseek-chat\", label: \"deepseek-chat · 通用 · 快\" },\n { value: \"deepseek-reasoner\", label: \"deepseek-reasoner · 深度推理\" },\n ]}\n />\n </div>\n </div>\n\n {showUsageCap ? (\n <React.Fragment>\n <Alert\n variant=\"warn\"\n icon={<Icon name=\"zap\" size={15} />}\n title=\"用量由你承担\"\n >\n 每一轮对话消耗的都是你自己 DeepSeek\n 账户的额度。用得越多,调用越多——建议设置每月上限以防意外超支。\n </Alert>\n <div className={\"s-cap\" + (capOn ? \"\" : \" is-off\")}>\n <Switch\n label=\"启用每月用量上限\"\n checked={capOn}\n onChange={(e) => setCfg({ capOn: e.target.checked })}\n />\n <div className=\"s-cap__field\" style={{ display: capOn ? \"flex\" : \"none\" }}>\n <input\n className=\"ax-input ax-input--mono\"\n type=\"text\"\n inputMode=\"numeric\"\n value={cap}\n onChange={(e) => setCfg({ cap: e.target.value.replace(/[^0-9]/g, \"\") })}\n aria-label=\"每月上限(元)\"\n />\n <span className=\"s-field__hint\" style={{ margin: 0 }}>\n 元 / 月,达到后暂停对话\n </span>\n </div>\n </div>\n </React.Fragment>\n ) : null}\n\n <HelpSteps\n title=\"如何获取 DeepSeek API Key?\"\n steps={[\n <>\n 登录 <code>platform.deepseek.com</code>,进入「API Keys」页面。\n </>,\n <>点击「创建 API Key」,命名后立即复制——密钥只在创建时完整显示一次。</>,\n <>在「充值」中确认账户余额充足,对话才能持续调用。</>,\n <>\n 把以 <code>sk-</code> 开头的密钥粘贴到上方输入框。\n </>,\n ]}\n link={{\n href: \"https://platform.deepseek.com\",\n label: \"打开 DeepSeek 开放平台\",\n }}\n />\n </div>\n\n <TestRow\n status={dsStatus}\n result={dsResult}\n onTest={() => runTest(\"deepseek\")}\n disabled={!dsKey.trim() && !maskedDs}\n idleHint=\"填写密钥后测试连通性\"\n />\n </section>\n\n {/* ── Card 2 · Feishu Bitable ── */}\n <section\n className={\n \"s-card\" + (fsPill === \"ok\" ? \" is-ok\" : fsPill === \"error\" ? \" is-error\" : \"\")\n }\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={fsPill} />\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\"\n type=\"text\"\n value={appId}\n spellCheck=\"false\"\n placeholder=\"cli_xxxxxxxxxxxx\"\n onChange={(e) => editFs(\"appId\")(e.target.value)}\n />\n <p className=\"s-field__hint\">应用标识,可公开。</p>\n </div>\n <SecretField\n label=\"App Secret\"\n value={secret}\n onChange={editSecret}\n placeholder={\n maskedFs ? \"已保存 ········ · 留空则保持不变\" : \"••••••••••••••••\"\n }\n hint={maskedFs ? \"已存密钥 · 留空表示不修改\" : \"应用密钥,加密存储。\"}\n />\n </div>\n\n <div>\n <label className=\"s-field__label ax-label\">多维表格链接</label>\n <input\n className=\"ax-input\"\n type=\"text\"\n value={link}\n spellCheck=\"false\"\n placeholder=\"https://your-team.feishu.cn/base/bascn…?table=tbl…\"\n onChange={(e) => editFs(\"link\")(e.target.value)}\n />\n <p className=\"s-field__hint\">\n 在多维表格右上角「分享」中复制链接粘贴即可,App Token 与数据表会自动识别。\n </p>\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 {fsConnected ? (\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 {S_FIELD_MAP.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 <HelpSteps\n title=\"如何获取飞书应用凭证?\"\n steps={[\n <>\n 打开飞书开放平台 <code>open.feishu.cn</code>,创建一个「企业自建应用」。\n </>,\n <>\n 在「凭证与基础信息」中复制 <code>App ID</code> 与 <code>App Secret</code>。\n </>,\n <>\n 到「权限管理」开通多维表格读写权限 <code>bitable:app</code>,并发布版本。\n </>,\n <>\n 在目标多维表格里,把这个应用添加为<strong>可编辑协作者</strong>。\n </>,\n <>复制该多维表格的分享链接,粘贴到上方「多维表格链接」。</>,\n ]}\n link={{ href: \"https://open.feishu.cn\", label: \"打开飞书开放平台\" }}\n />\n </div>\n\n <TestRow\n status={fsStatus}\n result={fsResult}\n onTest={() => runTest(\"feishu\")}\n disabled={!appId.trim() || (!secret.trim() && !maskedFs) || !parsed}\n idleHint=\"填写凭证与链接后测试写入权限\"\n />\n </section>\n </div>\n </div>\n </main>\n\n <footer className=\"s-save\">\n <div className=\"s-save__inner\">\n <div className=\"s-save__status\">\n {!allReady ? (\n <>\n <Icon name=\"info\" size={15} style={{ color: \"var(--text-faint)\" }} />\n <span>完成两项连接后即可保存并启用。</span>\n </>\n ) : saved ? (\n <>\n <Icon name=\"check\" size={15} style={{ color: \"var(--ok)\" }} />\n <span>已保存 · 已启用对话与数据写入。</span>\n </>\n ) : (\n <>\n <Icon name=\"zap\" size={15} style={{ color: \"var(--text-muted)\" }} />\n <span>配置已就绪,保存后立即生效。</span>\n </>\n )}\n </div>\n <div className=\"s-save__actions\">\n <Button variant=\"ghost\" size=\"md\" disabled={!dirty} onClick={onDiscard}>\n 放弃更改\n </Button>\n <Button\n variant=\"primary\"\n size=\"md\"\n icon={saving ? <Spinner size=\"sm\" /> : <Icon name=\"save\" size={14} />}\n disabled={!allReady || !dirty || saving}\n aria-busy={saving || undefined}\n onClick={doSave}\n >\n 保存配置\n </Button>\n </div>\n </div>\n </footer>\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentaily/design-system",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Agentaily design system — light-first monochrome React component library (113 components) + Storybook.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",