@agentaily/design-system 0.2.0 → 0.4.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.
Files changed (79) hide show
  1. package/DESIGN.md +56 -7
  2. package/README.md +5 -5
  3. package/dist/components/ai/Confirmation.js +58 -22
  4. package/dist/components/ai/Confirmation.js.map +1 -1
  5. package/dist/components/ai/Queue.d.ts +23 -0
  6. package/dist/components/ai/Queue.js +52 -1
  7. package/dist/components/ai/Queue.js.map +1 -1
  8. package/dist/components/ai/ToolCall.js +69 -30
  9. package/dist/components/ai/ToolCall.js.map +1 -1
  10. package/dist/components/auth/AccountControl.d.ts +23 -0
  11. package/dist/components/auth/AccountControl.js +47 -0
  12. package/dist/components/auth/AccountControl.js.map +1 -0
  13. package/dist/components/auth/AuthDialog.d.ts +39 -0
  14. package/dist/components/auth/AuthDialog.js +327 -0
  15. package/dist/components/auth/AuthDialog.js.map +1 -0
  16. package/dist/components/auth/SignInPage.d.ts +69 -0
  17. package/dist/components/auth/SignInPage.js +239 -0
  18. package/dist/components/auth/SignInPage.js.map +1 -0
  19. package/dist/components/chat/CodeBlock.js +3 -2
  20. package/dist/components/chat/CodeBlock.js.map +1 -1
  21. package/dist/components/chat/ConversationThread.d.ts +67 -0
  22. package/dist/components/chat/ConversationThread.js +129 -0
  23. package/dist/components/chat/ConversationThread.js.map +1 -0
  24. package/dist/components/code/Artifact.js +44 -9
  25. package/dist/components/code/Artifact.js.map +1 -1
  26. package/dist/components/code/JSXPreview.js +19 -12
  27. package/dist/components/code/JSXPreview.js.map +1 -1
  28. package/dist/components/code/Snippet.js +2 -2
  29. package/dist/components/code/Snippet.js.map +1 -1
  30. package/dist/components/code/Terminal.js +24 -10
  31. package/dist/components/code/Terminal.js.map +1 -1
  32. package/dist/components/code/WebPreview.js +44 -10
  33. package/dist/components/code/WebPreview.js.map +1 -1
  34. package/dist/components/display/Skeleton.js +17 -4
  35. package/dist/components/display/Skeleton.js.map +1 -1
  36. package/dist/components/display/StatusPill.d.ts +12 -0
  37. package/dist/components/display/StatusPill.js +17 -0
  38. package/dist/components/display/StatusPill.js.map +1 -0
  39. package/dist/components/inputs/SecretField.d.ts +21 -0
  40. package/dist/components/inputs/SecretField.js +70 -0
  41. package/dist/components/inputs/SecretField.js.map +1 -0
  42. package/dist/components/layout/AppShell.d.ts +30 -0
  43. package/dist/components/layout/AppShell.js +117 -0
  44. package/dist/components/layout/AppShell.js.map +1 -0
  45. package/dist/components/layout/DesignerShell.d.ts +39 -0
  46. package/dist/components/layout/DesignerShell.js +146 -0
  47. package/dist/components/layout/DesignerShell.js.map +1 -0
  48. package/dist/components/layout/DocsLayout.d.ts +24 -0
  49. package/dist/components/layout/DocsLayout.js +113 -0
  50. package/dist/components/layout/DocsLayout.js.map +1 -0
  51. package/dist/components/layout/SettingsPage.d.ts +31 -0
  52. package/dist/components/layout/SettingsPage.js +92 -0
  53. package/dist/components/layout/SettingsPage.js.map +1 -0
  54. package/dist/components/review/MarkupLayer.d.ts +20 -0
  55. package/dist/components/review/MarkupLayer.js +237 -0
  56. package/dist/components/review/MarkupLayer.js.map +1 -0
  57. package/dist/components/settings/HelpSteps.d.ts +20 -0
  58. package/dist/components/settings/HelpSteps.js +40 -0
  59. package/dist/components/settings/HelpSteps.js.map +1 -0
  60. package/dist/components/settings/IntegrationSettings.d.ts +15 -0
  61. package/dist/components/settings/IntegrationSettings.js +630 -0
  62. package/dist/components/settings/IntegrationSettings.js.map +1 -0
  63. package/dist/components/settings/TestRow.d.ts +21 -0
  64. package/dist/components/settings/TestRow.js +66 -0
  65. package/dist/components/settings/TestRow.js.map +1 -0
  66. package/dist/components/utilities/BrandMark.d.ts +17 -0
  67. package/dist/components/utilities/BrandMark.js +43 -0
  68. package/dist/components/utilities/BrandMark.js.map +1 -0
  69. package/dist/components/utilities/Icon.d.ts +28 -0
  70. package/dist/components/utilities/Icon.js +196 -0
  71. package/dist/components/utilities/Icon.js.map +1 -0
  72. package/dist/components/utilities/RotatingTagline.d.ts +30 -0
  73. package/dist/components/utilities/RotatingTagline.js +90 -0
  74. package/dist/components/utilities/RotatingTagline.js.map +1 -0
  75. package/dist/index.d.ts +23 -0
  76. package/dist/index.js +34 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/styles.css +67 -64
  79. package/package.json +2 -2
@@ -0,0 +1,47 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import "react";
3
+ import { Avatar } from "../display/Avatar.js";
4
+ import { Button } from "../buttons/Button.js";
5
+ import { DropdownMenu } from "../overlay/DropdownMenu.js";
6
+ import { Icon } from "../utilities/Icon.js";
7
+ const AX_ACCOUNT_CSS = `
8
+ .am-acct { appearance: none; border: none; background: none; padding: 0; cursor: pointer;
9
+ display: inline-flex; border-radius: var(--radius-2); transition: box-shadow var(--dur-1) var(--ease-out); }
10
+ .am-acct:hover { box-shadow: 0 0 0 2px var(--border-strong); }
11
+ .am-acct:active { transform: translateY(1px); }
12
+ `;
13
+ if (typeof document !== "undefined" && !document.getElementById("ax-account-css")) {
14
+ const s = document.createElement("style");
15
+ s.id = "ax-account-css";
16
+ s.textContent = AX_ACCOUNT_CSS;
17
+ document.head.appendChild(s);
18
+ }
19
+ function AccountControl({ user, onLogin, onLogout, items = [], signInLabel = "登录" }) {
20
+ if (!user) {
21
+ return /* @__PURE__ */ jsx(Button, { variant: "ghost", icon: /* @__PURE__ */ jsx(Icon, { name: "user", size: 14 }), onClick: onLogin, children: signInLabel });
22
+ }
23
+ return /* @__PURE__ */ jsx(
24
+ DropdownMenu,
25
+ {
26
+ align: "end",
27
+ trigger: /* @__PURE__ */ jsx("button", { className: "am-acct", "aria-label": "账户菜单", children: /* @__PURE__ */ jsx(Avatar, { name: user.name || user.email, size: "sm" }) }),
28
+ items: [
29
+ { type: "label", label: "已登录账户" },
30
+ { label: user.email, icon: /* @__PURE__ */ jsx(Icon, { name: "mail", size: 15 }), onSelect: () => {
31
+ } },
32
+ ...items.length ? [{ type: "separator" }, ...items] : [],
33
+ { type: "separator" },
34
+ {
35
+ label: "退出登录",
36
+ icon: /* @__PURE__ */ jsx(Icon, { name: "logout", size: 15 }),
37
+ danger: true,
38
+ onSelect: onLogout
39
+ }
40
+ ]
41
+ }
42
+ );
43
+ }
44
+ export {
45
+ AccountControl
46
+ };
47
+ //# sourceMappingURL=AccountControl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountControl.js","sources":["../../../src/components/auth/AccountControl.jsx"],"sourcesContent":["import React from \"react\";\nimport { Avatar } from \"../display/Avatar.jsx\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { DropdownMenu } from \"../overlay/DropdownMenu.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\n\n// AccountControl — top-bar account affordance. Signed out: a \"登录\" button.\n// Signed in: an avatar that opens a dropdown (email, custom items, sign out).\nconst AX_ACCOUNT_CSS = `\n.am-acct { appearance: none; border: none; background: none; padding: 0; cursor: pointer;\n display: inline-flex; border-radius: var(--radius-2); transition: box-shadow var(--dur-1) var(--ease-out); }\n.am-acct:hover { box-shadow: 0 0 0 2px var(--border-strong); }\n.am-acct:active { transform: translateY(1px); }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-account-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-account-css\";\n s.textContent = AX_ACCOUNT_CSS;\n document.head.appendChild(s);\n}\n\nexport function AccountControl({ user, onLogin, onLogout, items = [], signInLabel = \"登录\" }) {\n if (!user) {\n return (\n <Button variant=\"ghost\" icon={<Icon name=\"user\" size={14} />} onClick={onLogin}>\n {signInLabel}\n </Button>\n );\n }\n return (\n <DropdownMenu\n align=\"end\"\n trigger={\n <button className=\"am-acct\" aria-label=\"账户菜单\">\n <Avatar name={user.name || user.email} size=\"sm\" />\n </button>\n }\n items={[\n { type: \"label\", label: \"已登录账户\" },\n { label: user.email, icon: <Icon name=\"mail\" size={15} />, onSelect: () => {} },\n ...(items.length ? [{ type: \"separator\" }, ...items] : []),\n { type: \"separator\" },\n {\n label: \"退出登录\",\n icon: <Icon name=\"logout\" size={15} />,\n danger: true,\n onSelect: onLogout,\n },\n ]}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;AAQA,MAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOvB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,gBAAgB,GAAG;AACjF,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEO,SAAS,eAAe,EAAE,MAAM,SAAS,UAAU,QAAQ,CAAA,GAAI,cAAc,QAAQ;AAC1F,MAAI,CAAC,MAAM;AACT,WACE,oBAAC,QAAA,EAAO,SAAQ,SAAQ,MAAM,oBAAC,MAAA,EAAK,MAAK,QAAO,MAAM,IAAI,GAAI,SAAS,SACpE,UAAA,aACH;AAAA,EAEJ;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN,SACE,oBAAC,UAAA,EAAO,WAAU,WAAU,cAAW,QACrC,UAAA,oBAAC,QAAA,EAAO,MAAM,KAAK,QAAQ,KAAK,OAAO,MAAK,MAAK,GACnD;AAAA,MAEF,OAAO;AAAA,QACL,EAAE,MAAM,SAAS,OAAO,QAAA;AAAA,QACxB,EAAE,OAAO,KAAK,OAAO,MAAM,oBAAC,MAAA,EAAK,MAAK,QAAO,MAAM,GAAA,CAAI,GAAI,UAAU,MAAM;AAAA,QAAC,EAAA;AAAA,QAC5E,GAAI,MAAM,SAAS,CAAC,EAAE,MAAM,eAAe,GAAG,KAAK,IAAI,CAAA;AAAA,QACvD,EAAE,MAAM,YAAA;AAAA,QACR;AAAA,UACE,OAAO;AAAA,UACP,MAAM,oBAAC,MAAA,EAAK,MAAK,UAAS,MAAM,IAAI;AAAA,UACpC,QAAQ;AAAA,UACR,UAAU;AAAA,QAAA;AAAA,MACZ;AAAA,IACF;AAAA,EAAA;AAGN;"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Sign in / register modal with a localStorage-persisted session. Tabs switch
3
+ * mode; client-side validation; a brief faux-submit then a success panel that
4
+ * fires `onSuccess(email)`. The session hook is exposed as `AuthDialog.useAuth`.
5
+ */
6
+ export interface AuthUser {
7
+ email: string;
8
+ name: string;
9
+ since: number;
10
+ }
11
+ export interface AuthSession {
12
+ user: AuthUser | null;
13
+ signIn: (email: string) => void;
14
+ signOut: () => void;
15
+ }
16
+ export interface AuthCopy {
17
+ title?: string;
18
+ sub?: string;
19
+ cta?: string;
20
+ busy?: string;
21
+ okh?: string;
22
+ }
23
+ export interface AuthDialogProps {
24
+ open: boolean;
25
+ /** Optional gated-reason line shown above the form (e.g. "登录以继续导出"). */
26
+ reason?: string;
27
+ /** @default "login" */
28
+ initialMode?: "login" | "register";
29
+ /** Override per-mode copy (title / sub / cta / busy / okh). */
30
+ copy?: { login?: AuthCopy; register?: AuthCopy };
31
+ onClose?: () => void;
32
+ /** Fires with the email after a successful submit. */
33
+ onSuccess?: (email: string) => void;
34
+ }
35
+ export declare function AuthDialog(props: AuthDialogProps): JSX.Element | null;
36
+ export declare namespace AuthDialog {
37
+ /** localStorage-persisted session: { user, signIn, signOut }. */
38
+ function useAuth(storageKey?: string): AuthSession;
39
+ }
@@ -0,0 +1,327 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { useState, useRef } from "react";
3
+ import { BrandMark } from "../utilities/BrandMark.js";
4
+ import { Button } from "../buttons/Button.js";
5
+ import { Checkbox } from "../inputs/Checkbox.js";
6
+ import { Icon } from "../utilities/Icon.js";
7
+ import { Tabs } from "../display/Tabs.js";
8
+ const AX_AUTH_CSS = `
9
+ .am-modal { width: min(412px, calc(100vw - 40px)); position: relative; }
10
+ .am-x { position: absolute; top: 14px; right: 14px; z-index: 1; appearance: none; background: none; border: none;
11
+ cursor: pointer; color: var(--text-faint); font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.02em;
12
+ padding: 3px 5px; border-radius: var(--radius-1); transition: color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out); }
13
+ .am-x:hover { color: var(--text-body); background: var(--surface-raised); }
14
+ .am-body { padding: 30px 30px 26px; }
15
+ .am-brand { display: flex; align-items: center; gap: 9px; margin-bottom: 18px; }
16
+ .am-reason { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; padding: 9px 11px;
17
+ background: var(--surface-card); border: 1px solid var(--border-default); border-radius: var(--radius-2);
18
+ font-size: var(--text-xs); color: var(--text-muted); line-height: var(--leading-snug); }
19
+ .am-reason svg { flex: none; color: var(--text-faint); }
20
+ .am-switch { margin-bottom: 20px; }
21
+ .am-head { margin-bottom: 20px; }
22
+ .am-title { font-family: var(--font-display); font-size: var(--text-xl); font-weight: 500;
23
+ letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); color: var(--text-body); margin: 0 0 7px; }
24
+ .am-sub { font-size: var(--text-sm); color: var(--text-muted); line-height: var(--leading-snug); margin: 0; }
25
+ .am-form { display: flex; flex-direction: column; }
26
+ .am-fields { display: flex; flex-direction: column; gap: 16px; }
27
+ .am-pwwrap { position: relative; display: block; }
28
+ .am-pwtoggle { position: absolute; top: 0; right: 0; height: 36px; width: 38px; display: flex;
29
+ align-items: center; justify-content: center; background: none; border: none; cursor: pointer;
30
+ color: var(--text-faint); border-radius: 0 var(--radius-2) var(--radius-2) 0; transition: color var(--dur-1) var(--ease-out); }
31
+ .am-pwtoggle:hover { color: var(--text-body); }
32
+ .am-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 16px; }
33
+ .am-link { appearance: none; border: none; background: none; padding: 0; cursor: pointer;
34
+ font-family: var(--font-body); font-size: var(--text-sm); color: var(--text-muted);
35
+ text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--border-strong);
36
+ transition: color var(--dur-1) var(--ease-out), text-decoration-color var(--dur-1) var(--ease-out); }
37
+ .am-link:hover { color: var(--text-body); text-decoration-color: var(--text-faint); }
38
+ .am-link--mute { color: var(--text-muted); }
39
+ .am-note { margin: 12px 0 0; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); }
40
+ .am-note strong { color: var(--text-muted); font-weight: 500; }
41
+ .am-submit { margin-top: 22px; }
42
+ .am-terms { margin: 14px 0 0; text-align: center; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); }
43
+ .am-done { padding: 46px 30px 42px; display: flex; flex-direction: column; align-items: center; text-align: center; }
44
+ .am-donemark { width: 48px; height: 48px; border-radius: var(--radius-full); background: var(--ok-dim); color: var(--ok);
45
+ display: flex; align-items: center; justify-content: center; margin-bottom: 16px; }
46
+ .am-doneh { font-family: var(--font-display); font-size: var(--text-xl); font-weight: 500;
47
+ letter-spacing: var(--tracking-tight); color: var(--text-body); margin: 0 0 9px; }
48
+ .am-donep { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted); letter-spacing: 0.02em;
49
+ border: 1px solid var(--border-default); border-radius: var(--radius-2); padding: 7px 12px; margin: 0; }
50
+ .am-in { animation: am-in var(--dur-3) var(--ease-out) both; }
51
+ @keyframes am-in { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
52
+ @media (prefers-reduced-motion: reduce) { .am-in { animation: none; } }
53
+ `;
54
+ if (typeof document !== "undefined" && !document.getElementById("ax-auth-css")) {
55
+ const s = document.createElement("style");
56
+ s.id = "ax-auth-css";
57
+ s.textContent = AX_AUTH_CSS;
58
+ document.head.appendChild(s);
59
+ }
60
+ const AUTH_KEY = "agentaily.auth.v1";
61
+ function useAuth(storageKey = AUTH_KEY) {
62
+ const [user, setUser] = useState(() => {
63
+ try {
64
+ return JSON.parse(localStorage.getItem(storageKey) || "null");
65
+ } catch (e) {
66
+ return null;
67
+ }
68
+ });
69
+ const signIn = (email) => {
70
+ const u = { email, name: email.split("@")[0], since: Date.now() };
71
+ setUser(u);
72
+ try {
73
+ localStorage.setItem(storageKey, JSON.stringify(u));
74
+ } catch (e) {
75
+ }
76
+ };
77
+ const signOut = () => {
78
+ setUser(null);
79
+ try {
80
+ localStorage.removeItem(storageKey);
81
+ } catch (e) {
82
+ }
83
+ };
84
+ return { user, signIn, signOut };
85
+ }
86
+ function AuthPwField({ label, value, onChange, error, placeholder, autoComplete, name }) {
87
+ const [show, setShow] = useState(false);
88
+ return /* @__PURE__ */ jsxs("label", { className: "ax-field", children: [
89
+ /* @__PURE__ */ jsx("span", { className: "ax-field__label", children: label }),
90
+ /* @__PURE__ */ jsxs("span", { className: "am-pwwrap", children: [
91
+ /* @__PURE__ */ jsx(
92
+ "input",
93
+ {
94
+ className: "ax-input" + (error ? " ax-input--error" : ""),
95
+ type: show ? "text" : "password",
96
+ value,
97
+ name,
98
+ autoComplete,
99
+ placeholder,
100
+ onChange: (e) => onChange(e.target.value),
101
+ style: { paddingRight: 42 }
102
+ }
103
+ ),
104
+ /* @__PURE__ */ jsx(
105
+ "button",
106
+ {
107
+ type: "button",
108
+ className: "am-pwtoggle",
109
+ tabIndex: -1,
110
+ "aria-label": show ? "隐藏密码" : "显示密码",
111
+ onMouseDown: (e) => e.preventDefault(),
112
+ onClick: () => setShow((s) => !s),
113
+ children: /* @__PURE__ */ jsx(Icon, { name: show ? "eyeOff" : "eye", size: 16 })
114
+ }
115
+ )
116
+ ] }),
117
+ error ? /* @__PURE__ */ jsx("span", { className: "ax-field__hint ax-field__hint--error", children: error }) : null
118
+ ] });
119
+ }
120
+ const DEFAULT_COPY = {
121
+ login: { title: "欢迎回来", sub: "登录以继续。", cta: "登录", busy: "登录中…", okh: "登录成功" },
122
+ register: {
123
+ title: "创建账户",
124
+ sub: "注册一个 Agentaily 账户开始创作。",
125
+ cta: "创建账户",
126
+ busy: "创建中…",
127
+ okh: "账户已创建"
128
+ }
129
+ };
130
+ function AuthDialog({ open, reason, initialMode = "login", copy, onClose, onSuccess }) {
131
+ const COPY = {
132
+ login: { ...DEFAULT_COPY.login, ...copy && copy.login },
133
+ register: { ...DEFAULT_COPY.register, ...copy && copy.register }
134
+ };
135
+ const [mode, setMode] = useState(initialMode);
136
+ const [vals, setVals] = useState({ email: "", password: "", confirm: "" });
137
+ const [errs, setErrs] = useState({});
138
+ const [remember, setRemember] = useState(true);
139
+ const [forgot, setForgot] = useState(false);
140
+ const [submitting, setSubmitting] = useState(false);
141
+ const [ok, setOk] = useState(false);
142
+ const busy = useRef(false);
143
+ const lastOpen = useRef(false);
144
+ if (open && !lastOpen.current) {
145
+ lastOpen.current = true;
146
+ setMode(initialMode);
147
+ setVals({ email: "", password: "", confirm: "" });
148
+ setErrs({});
149
+ setForgot(false);
150
+ setSubmitting(false);
151
+ setOk(false);
152
+ busy.current = false;
153
+ }
154
+ if (!open && lastOpen.current) lastOpen.current = false;
155
+ if (!open) return null;
156
+ const c = COPY[mode];
157
+ const setField = (k, v) => {
158
+ setVals((s) => ({ ...s, [k]: v }));
159
+ if (errs[k])
160
+ setErrs((e) => {
161
+ const n = { ...e };
162
+ delete n[k];
163
+ return n;
164
+ });
165
+ };
166
+ const switchMode = (m) => {
167
+ if (m === mode || submitting || ok) return;
168
+ setMode(m);
169
+ setErrs({});
170
+ setForgot(false);
171
+ };
172
+ const validate = () => {
173
+ const e = {};
174
+ const email = vals.email.trim();
175
+ if (!email) e.email = "请输入邮箱";
176
+ else if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) e.email = "邮箱格式不正确";
177
+ if (!vals.password) e.password = "请输入密码";
178
+ else if (vals.password.length < 8) e.password = "密码至少 8 位";
179
+ if (mode === "register") {
180
+ if (!vals.confirm) e.confirm = "请再次输入密码";
181
+ else if (vals.confirm !== vals.password) e.confirm = "两次输入的密码不一致";
182
+ }
183
+ return e;
184
+ };
185
+ const submit = (ev) => {
186
+ if (ev) ev.preventDefault();
187
+ if (busy.current || ok) return;
188
+ const e = validate();
189
+ setErrs(e);
190
+ if (Object.keys(e).length) return;
191
+ const email = vals.email.trim();
192
+ busy.current = true;
193
+ setSubmitting(true);
194
+ setTimeout(() => {
195
+ setSubmitting(false);
196
+ setOk(true);
197
+ setTimeout(() => onSuccess && onSuccess(email), 720);
198
+ }, 780);
199
+ };
200
+ return /* @__PURE__ */ jsx("div", { className: "ax-dialog-overlay", onClick: onClose, children: /* @__PURE__ */ jsxs(
201
+ "div",
202
+ {
203
+ className: "ax-dialog am-modal",
204
+ role: "dialog",
205
+ "aria-modal": "true",
206
+ onClick: (e) => e.stopPropagation(),
207
+ children: [
208
+ /* @__PURE__ */ jsx("button", { className: "am-x", onClick: onClose, "aria-label": "关闭", children: "ESC ✕" }),
209
+ ok ? /* @__PURE__ */ jsxs("div", { className: "am-body am-done", children: [
210
+ /* @__PURE__ */ jsx("span", { className: "am-donemark", children: /* @__PURE__ */ jsx(Icon, { name: "check", size: 22, strokeWidth: 2.2 }) }),
211
+ /* @__PURE__ */ jsx("h2", { className: "am-doneh", children: c.okh }),
212
+ /* @__PURE__ */ jsx("p", { className: "am-donep", children: vals.email.trim() })
213
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "am-body", children: [
214
+ /* @__PURE__ */ jsx("div", { className: "am-brand", children: /* @__PURE__ */ jsx(BrandMark, { size: 20, wordmark: true, blink: false }) }),
215
+ reason ? /* @__PURE__ */ jsxs("div", { className: "am-reason", children: [
216
+ /* @__PURE__ */ jsx(Icon, { name: "lock", size: 13 }),
217
+ /* @__PURE__ */ jsx("span", { children: reason })
218
+ ] }) : null,
219
+ /* @__PURE__ */ jsx("div", { className: "am-switch", children: /* @__PURE__ */ jsx(
220
+ Tabs,
221
+ {
222
+ items: [
223
+ { id: "login", label: "登录" },
224
+ { id: "register", label: "注册" }
225
+ ],
226
+ active: mode,
227
+ onChange: switchMode
228
+ }
229
+ ) }),
230
+ /* @__PURE__ */ jsxs("div", { className: "am-head", children: [
231
+ /* @__PURE__ */ jsx("h2", { className: "am-title", children: c.title }),
232
+ /* @__PURE__ */ jsx("p", { className: "am-sub", children: c.sub })
233
+ ] }),
234
+ /* @__PURE__ */ jsxs("form", { className: "am-form", onSubmit: submit, noValidate: true, children: [
235
+ /* @__PURE__ */ jsxs("div", { className: "am-fields", children: [
236
+ /* @__PURE__ */ jsxs("label", { className: "ax-field", children: [
237
+ /* @__PURE__ */ jsx("span", { className: "ax-field__label", children: "邮箱" }),
238
+ /* @__PURE__ */ jsx(
239
+ "input",
240
+ {
241
+ className: "ax-input" + (errs.email ? " ax-input--error" : ""),
242
+ type: "email",
243
+ name: "email",
244
+ autoComplete: "email",
245
+ placeholder: "you@domain.com",
246
+ value: vals.email,
247
+ onChange: (e) => setField("email", e.target.value)
248
+ }
249
+ ),
250
+ errs.email ? /* @__PURE__ */ jsx("span", { className: "ax-field__hint ax-field__hint--error", children: errs.email }) : null
251
+ ] }),
252
+ /* @__PURE__ */ jsx(
253
+ AuthPwField,
254
+ {
255
+ label: "密码",
256
+ name: "password",
257
+ autoComplete: mode === "login" ? "current-password" : "new-password",
258
+ placeholder: mode === "login" ? "输入密码" : "至少 8 位",
259
+ value: vals.password,
260
+ error: errs.password,
261
+ onChange: (v) => setField("password", v)
262
+ }
263
+ ),
264
+ mode === "register" ? /* @__PURE__ */ jsx("div", { className: "am-in", children: /* @__PURE__ */ jsx(
265
+ AuthPwField,
266
+ {
267
+ label: "确认密码",
268
+ name: "confirm",
269
+ autoComplete: "new-password",
270
+ placeholder: "再次输入密码",
271
+ value: vals.confirm,
272
+ error: errs.confirm,
273
+ onChange: (v) => setField("confirm", v)
274
+ }
275
+ ) }) : null
276
+ ] }),
277
+ mode === "login" ? /* @__PURE__ */ jsxs("div", { className: "am-row", children: [
278
+ /* @__PURE__ */ jsx(
279
+ Checkbox,
280
+ {
281
+ label: "记住我",
282
+ checked: remember,
283
+ onChange: (e) => setRemember(e.target.checked)
284
+ }
285
+ ),
286
+ /* @__PURE__ */ jsx("button", { type: "button", className: "am-link", onClick: () => setForgot(true), children: "忘记密码?" })
287
+ ] }) : null,
288
+ mode === "login" && forgot ? /* @__PURE__ */ jsxs("p", { className: "am-note am-in", children: [
289
+ "重置链接将发送到你的邮箱",
290
+ vals.email.trim() ? /* @__PURE__ */ jsxs(Fragment, { children: [
291
+ " ",
292
+ "· ",
293
+ /* @__PURE__ */ jsx("strong", { children: vals.email.trim() })
294
+ ] }) : /* @__PURE__ */ jsx(Fragment, { children: ",请先填写邮箱" }),
295
+ "。"
296
+ ] }) : null,
297
+ /* @__PURE__ */ jsx("div", { className: "am-submit", children: /* @__PURE__ */ jsx(
298
+ Button,
299
+ {
300
+ variant: "primary",
301
+ size: "lg",
302
+ full: true,
303
+ type: "submit",
304
+ disabled: submitting,
305
+ onClick: submit,
306
+ children: submitting ? c.busy : c.cta
307
+ }
308
+ ) }),
309
+ mode === "register" ? /* @__PURE__ */ jsxs("p", { className: "am-terms", children: [
310
+ "注册即代表你同意 ",
311
+ /* @__PURE__ */ jsx("span", { className: "am-link am-link--mute", children: "服务条款" }),
312
+ " 与",
313
+ " ",
314
+ /* @__PURE__ */ jsx("span", { className: "am-link am-link--mute", children: "隐私政策" }),
315
+ "。"
316
+ ] }) : null
317
+ ] })
318
+ ] })
319
+ ]
320
+ }
321
+ ) });
322
+ }
323
+ AuthDialog.useAuth = useAuth;
324
+ export {
325
+ AuthDialog
326
+ };
327
+ //# sourceMappingURL=AuthDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthDialog.js","sources":["../../../src/components/auth/AuthDialog.jsx"],"sourcesContent":["import React, { useState, useRef } from \"react\";\nimport { BrandMark } from \"../utilities/BrandMark.jsx\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { Checkbox } from \"../inputs/Checkbox.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\nimport { Tabs } from \"../display/Tabs.jsx\";\n\n// AuthDialog — sign in / register modal with localStorage-persisted session.\n// The session hook is exposed as a static: AuthDialog.useAuth(). Copy text is\n// overridable so each product can say what it gates (\"编辑与导出你的脚本\" etc).\n// Built on the bundle's .ax-dialog + .ax-field; uses Tabs / Button / Checkbox.\nconst AX_AUTH_CSS = `\n.am-modal { width: min(412px, calc(100vw - 40px)); position: relative; }\n.am-x { position: absolute; top: 14px; right: 14px; z-index: 1; appearance: none; background: none; border: none;\n cursor: pointer; color: var(--text-faint); font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.02em;\n padding: 3px 5px; border-radius: var(--radius-1); transition: color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out); }\n.am-x:hover { color: var(--text-body); background: var(--surface-raised); }\n.am-body { padding: 30px 30px 26px; }\n.am-brand { display: flex; align-items: center; gap: 9px; margin-bottom: 18px; }\n.am-reason { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; padding: 9px 11px;\n background: var(--surface-card); border: 1px solid var(--border-default); border-radius: var(--radius-2);\n font-size: var(--text-xs); color: var(--text-muted); line-height: var(--leading-snug); }\n.am-reason svg { flex: none; color: var(--text-faint); }\n.am-switch { margin-bottom: 20px; }\n.am-head { margin-bottom: 20px; }\n.am-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 0 7px; }\n.am-sub { font-size: var(--text-sm); color: var(--text-muted); line-height: var(--leading-snug); margin: 0; }\n.am-form { display: flex; flex-direction: column; }\n.am-fields { display: flex; flex-direction: column; gap: 16px; }\n.am-pwwrap { position: relative; display: block; }\n.am-pwtoggle { position: absolute; top: 0; right: 0; height: 36px; width: 38px; display: flex;\n align-items: center; justify-content: center; background: none; border: none; cursor: pointer;\n color: var(--text-faint); border-radius: 0 var(--radius-2) var(--radius-2) 0; transition: color var(--dur-1) var(--ease-out); }\n.am-pwtoggle:hover { color: var(--text-body); }\n.am-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 16px; }\n.am-link { appearance: none; border: none; background: none; padding: 0; cursor: pointer;\n font-family: var(--font-body); font-size: var(--text-sm); color: var(--text-muted);\n text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--border-strong);\n transition: color var(--dur-1) var(--ease-out), text-decoration-color var(--dur-1) var(--ease-out); }\n.am-link:hover { color: var(--text-body); text-decoration-color: var(--text-faint); }\n.am-link--mute { color: var(--text-muted); }\n.am-note { margin: 12px 0 0; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); }\n.am-note strong { color: var(--text-muted); font-weight: 500; }\n.am-submit { margin-top: 22px; }\n.am-terms { margin: 14px 0 0; text-align: center; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); }\n.am-done { padding: 46px 30px 42px; display: flex; flex-direction: column; align-items: center; text-align: center; }\n.am-donemark { width: 48px; height: 48px; border-radius: var(--radius-full); background: var(--ok-dim); color: var(--ok);\n display: flex; align-items: center; justify-content: center; margin-bottom: 16px; }\n.am-doneh { font-family: var(--font-display); font-size: var(--text-xl); font-weight: 500;\n letter-spacing: var(--tracking-tight); color: var(--text-body); margin: 0 0 9px; }\n.am-donep { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted); letter-spacing: 0.02em;\n border: 1px solid var(--border-default); border-radius: var(--radius-2); padding: 7px 12px; margin: 0; }\n.am-in { animation: am-in var(--dur-3) var(--ease-out) both; }\n@keyframes am-in { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }\n@media (prefers-reduced-motion: reduce) { .am-in { animation: none; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-auth-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-auth-css\";\n s.textContent = AX_AUTH_CSS;\n document.head.appendChild(s);\n}\n\nconst AUTH_KEY = \"agentaily.auth.v1\";\n\n// localStorage-persisted session hook (exposed as AuthDialog.useAuth)\nfunction useAuth(storageKey = AUTH_KEY) {\n const [user, setUser] = useState(() => {\n try {\n return JSON.parse(localStorage.getItem(storageKey) || \"null\");\n } catch (e) {\n return null;\n }\n });\n const signIn = (email) => {\n const u = { email, name: email.split(\"@\")[0], since: Date.now() };\n setUser(u);\n try {\n localStorage.setItem(storageKey, JSON.stringify(u));\n } catch (e) {}\n };\n const signOut = () => {\n setUser(null);\n try {\n localStorage.removeItem(storageKey);\n } catch (e) {}\n };\n return { user, signIn, signOut };\n}\n\nfunction AuthPwField({ label, value, onChange, error, placeholder, autoComplete, name }) {\n const [show, setShow] = useState(false);\n return (\n <label className=\"ax-field\">\n <span className=\"ax-field__label\">{label}</span>\n <span className=\"am-pwwrap\">\n <input\n className={\"ax-input\" + (error ? \" ax-input--error\" : \"\")}\n type={show ? \"text\" : \"password\"}\n value={value}\n name={name}\n autoComplete={autoComplete}\n placeholder={placeholder}\n onChange={(e) => onChange(e.target.value)}\n style={{ paddingRight: 42 }}\n />\n <button\n type=\"button\"\n className=\"am-pwtoggle\"\n tabIndex={-1}\n aria-label={show ? \"隐藏密码\" : \"显示密码\"}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => setShow((s) => !s)}\n >\n <Icon name={show ? \"eyeOff\" : \"eye\"} size={16} />\n </button>\n </span>\n {error ? <span className=\"ax-field__hint ax-field__hint--error\">{error}</span> : null}\n </label>\n );\n}\n\nconst DEFAULT_COPY = {\n login: { title: \"欢迎回来\", sub: \"登录以继续。\", cta: \"登录\", busy: \"登录中…\", okh: \"登录成功\" },\n register: {\n title: \"创建账户\",\n sub: \"注册一个 Agentaily 账户开始创作。\",\n cta: \"创建账户\",\n busy: \"创建中…\",\n okh: \"账户已创建\",\n },\n};\n\nexport function AuthDialog({ open, reason, initialMode = \"login\", copy, onClose, onSuccess }) {\n const COPY = {\n login: { ...DEFAULT_COPY.login, ...(copy && copy.login) },\n register: { ...DEFAULT_COPY.register, ...(copy && copy.register) },\n };\n\n const [mode, setMode] = useState(initialMode);\n const [vals, setVals] = useState({ email: \"\", password: \"\", confirm: \"\" });\n const [errs, setErrs] = useState({});\n const [remember, setRemember] = useState(true);\n const [forgot, setForgot] = useState(false);\n const [submitting, setSubmitting] = useState(false);\n const [ok, setOk] = useState(false);\n const busy = useRef(false);\n const lastOpen = useRef(false);\n\n if (open && !lastOpen.current) {\n lastOpen.current = true;\n setMode(initialMode);\n setVals({ email: \"\", password: \"\", confirm: \"\" });\n setErrs({});\n setForgot(false);\n setSubmitting(false);\n setOk(false);\n busy.current = false;\n }\n if (!open && lastOpen.current) lastOpen.current = false;\n if (!open) return null;\n\n const c = COPY[mode];\n const setField = (k, v) => {\n setVals((s) => ({ ...s, [k]: v }));\n if (errs[k])\n setErrs((e) => {\n const n = { ...e };\n delete n[k];\n return n;\n });\n };\n const switchMode = (m) => {\n if (m === mode || submitting || ok) return;\n setMode(m);\n setErrs({});\n setForgot(false);\n };\n const validate = () => {\n const e = {};\n const email = vals.email.trim();\n if (!email) e.email = \"请输入邮箱\";\n else if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(email)) e.email = \"邮箱格式不正确\";\n if (!vals.password) e.password = \"请输入密码\";\n else if (vals.password.length < 8) e.password = \"密码至少 8 位\";\n if (mode === \"register\") {\n if (!vals.confirm) e.confirm = \"请再次输入密码\";\n else if (vals.confirm !== vals.password) e.confirm = \"两次输入的密码不一致\";\n }\n return e;\n };\n const submit = (ev) => {\n if (ev) ev.preventDefault();\n if (busy.current || ok) return;\n const e = validate();\n setErrs(e);\n if (Object.keys(e).length) return;\n const email = vals.email.trim();\n busy.current = true;\n setSubmitting(true);\n setTimeout(() => {\n setSubmitting(false);\n setOk(true);\n setTimeout(() => onSuccess && onSuccess(email), 720);\n }, 780);\n };\n\n return (\n <div className=\"ax-dialog-overlay\" onClick={onClose}>\n <div\n className=\"ax-dialog am-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n onClick={(e) => e.stopPropagation()}\n >\n <button className=\"am-x\" onClick={onClose} aria-label=\"关闭\">\n ESC ✕\n </button>\n\n {ok ? (\n <div className=\"am-body am-done\">\n <span className=\"am-donemark\">\n <Icon name=\"check\" size={22} strokeWidth={2.2} />\n </span>\n <h2 className=\"am-doneh\">{c.okh}</h2>\n <p className=\"am-donep\">{vals.email.trim()}</p>\n </div>\n ) : (\n <div className=\"am-body\">\n <div className=\"am-brand\">\n <BrandMark size={20} wordmark blink={false} />\n </div>\n\n {reason ? (\n <div className=\"am-reason\">\n <Icon name=\"lock\" size={13} />\n <span>{reason}</span>\n </div>\n ) : null}\n\n <div className=\"am-switch\">\n <Tabs\n items={[\n { id: \"login\", label: \"登录\" },\n { id: \"register\", label: \"注册\" },\n ]}\n active={mode}\n onChange={switchMode}\n />\n </div>\n\n <div className=\"am-head\">\n <h2 className=\"am-title\">{c.title}</h2>\n <p className=\"am-sub\">{c.sub}</p>\n </div>\n\n <form className=\"am-form\" onSubmit={submit} noValidate>\n <div className=\"am-fields\">\n <label className=\"ax-field\">\n <span className=\"ax-field__label\">邮箱</span>\n <input\n className={\"ax-input\" + (errs.email ? \" ax-input--error\" : \"\")}\n type=\"email\"\n name=\"email\"\n autoComplete=\"email\"\n placeholder=\"you@domain.com\"\n value={vals.email}\n onChange={(e) => setField(\"email\", e.target.value)}\n />\n {errs.email ? (\n <span className=\"ax-field__hint ax-field__hint--error\">{errs.email}</span>\n ) : null}\n </label>\n\n <AuthPwField\n label=\"密码\"\n name=\"password\"\n autoComplete={mode === \"login\" ? \"current-password\" : \"new-password\"}\n placeholder={mode === \"login\" ? \"输入密码\" : \"至少 8 位\"}\n value={vals.password}\n error={errs.password}\n onChange={(v) => setField(\"password\", v)}\n />\n\n {mode === \"register\" ? (\n <div className=\"am-in\">\n <AuthPwField\n label=\"确认密码\"\n name=\"confirm\"\n autoComplete=\"new-password\"\n placeholder=\"再次输入密码\"\n value={vals.confirm}\n error={errs.confirm}\n onChange={(v) => setField(\"confirm\", v)}\n />\n </div>\n ) : null}\n </div>\n\n {mode === \"login\" ? (\n <div className=\"am-row\">\n <Checkbox\n label=\"记住我\"\n checked={remember}\n onChange={(e) => setRemember(e.target.checked)}\n />\n <button type=\"button\" className=\"am-link\" onClick={() => setForgot(true)}>\n 忘记密码?\n </button>\n </div>\n ) : null}\n {mode === \"login\" && forgot ? (\n <p className=\"am-note am-in\">\n 重置链接将发送到你的邮箱\n {vals.email.trim() ? (\n <>\n {\" \"}\n · <strong>{vals.email.trim()}</strong>\n </>\n ) : (\n <>,请先填写邮箱</>\n )}\n 。\n </p>\n ) : null}\n\n <div className=\"am-submit\">\n <Button\n variant=\"primary\"\n size=\"lg\"\n full\n type=\"submit\"\n disabled={submitting}\n onClick={submit}\n >\n {submitting ? c.busy : c.cta}\n </Button>\n </div>\n\n {mode === \"register\" ? (\n <p className=\"am-terms\">\n 注册即代表你同意 <span className=\"am-link am-link--mute\">服务条款</span> 与{\" \"}\n <span className=\"am-link am-link--mute\">隐私政策</span>。\n </p>\n ) : null}\n </form>\n </div>\n )}\n </div>\n </div>\n );\n}\n\n// session hook, exposed off the capitalized component (mirrors Form.useForm)\nAuthDialog.useAuth = useAuth;\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Full-page auth: a split brand panel (dot-grid + quote) beside a centered card.
3
+ * One component, two modes (signin / signup) — the brand panel, SSO, and chrome
4
+ * are shared; only fields, copy, and footer differ. The built-in footer link
5
+ * flips mode and fires onModeChange (wire it to your router to swap the URL).
6
+ * Light built-in validation (email format, password length, confirm match).
7
+ * For a modal instead, use AuthDialog. Brand panel hides < 860px.
8
+ */
9
+ export interface SignInPageCopy {
10
+ title?: string;
11
+ subtitle?: string;
12
+ submit?: string;
13
+ /** Footer lead-in, e.g. "No account?" */
14
+ switchText?: string;
15
+ /** Footer link label that flips mode, e.g. "Create one" */
16
+ switchCta?: string;
17
+ }
18
+ /** Shared, non-mode-specific strings. All optional — merged over the English defaults. */
19
+ export interface SignInPageSharedCopy {
20
+ /** Field labels + chrome: { email, password, confirm, forgot, or, sso }. */
21
+ labels?: { email?: string; password?: string; confirm?: string; forgot?: string; or?: string; sso?: string };
22
+ /** Input placeholders: { email, password, passwordNew, confirm }. */
23
+ placeholders?: { email?: string; password?: string; passwordNew?: string; confirm?: string };
24
+ /** Validation messages: { emailRequired, emailInvalid, passwordRequired, passwordShort, confirmRequired, confirmMismatch }. */
25
+ errors?: {
26
+ emailRequired?: string; emailInvalid?: string;
27
+ passwordRequired?: string; passwordShort?: string;
28
+ confirmRequired?: string; confirmMismatch?: string;
29
+ };
30
+ /** Default signup terms line (string). Overridden by the `terms` prop if passed. */
31
+ terms?: React.ReactNode;
32
+ }
33
+ export interface SignInPageProps {
34
+ /** Controlled mode. Omit for uncontrolled (uses defaultMode). */
35
+ mode?: "signin" | "signup";
36
+ /** Initial mode when uncontrolled. @default "signin" */
37
+ defaultMode?: "signin" | "signup";
38
+ /** Fires when the footer link flips mode — wire to your router (/signin ↔ /signup). */
39
+ onModeChange?: (mode: "signin" | "signup") => void;
40
+ /** Brand-panel lockup. @default <BrandMark wordmark /> (blinking cursor) */
41
+ brand?: React.ReactNode;
42
+ /**
43
+ * All user-facing strings, deep-merged over the English defaults. Per-mode
44
+ * copy under `signin`/`signup`; shared labels, placeholders, errors, and the
45
+ * terms line at the top level. Pass a full zh-CN object to localize.
46
+ */
47
+ copy?: { signin?: SignInPageCopy; signup?: SignInPageCopy } & SignInPageSharedCopy;
48
+ /** Props forwarded to the brand-panel RotatingTagline (prefix, phrases, flowDuration…). @default { breakAfterPrefix: true } */
49
+ tagline?: Record<string, unknown>;
50
+ email?: string;
51
+ password?: string;
52
+ onEmailChange?: (value: string) => void;
53
+ onPasswordChange?: (value: string) => void;
54
+ /** Submit handler (after validation passes); receives { mode, email, password }. */
55
+ onSubmit?: (values: { mode: "signin" | "signup"; email: string; password: string }) => void;
56
+ /** Show + handle the "Forgot?" link (signin mode only). */
57
+ onForgot?: () => void;
58
+ /** SSO button label. Overrides copy.labels.sso. @default "Continue with SSO" */
59
+ ssoLabel?: string;
60
+ /** Show + handle the SSO button (hidden when omitted). */
61
+ onSSO?: () => void;
62
+ /** Terms line shown in signup mode. Pass null to hide; omit for the default. */
63
+ terms?: React.ReactNode;
64
+ /** Override the built-in mode-flip footer. Pass null to hide; omit for the default. */
65
+ footer?: React.ReactNode;
66
+ /** Show the left brand panel. @default true */
67
+ showBrandPanel?: boolean;
68
+ }
69
+ export declare function SignInPage(props: SignInPageProps): JSX.Element;