@agentaily/design-system 0.1.1 → 0.3.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 (82) hide show
  1. package/DESIGN.md +58 -7
  2. package/README.md +10 -6
  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 +48 -0
  17. package/dist/components/auth/SignInPage.js +217 -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/Form.d.ts +164 -0
  40. package/dist/components/inputs/Form.js +484 -0
  41. package/dist/components/inputs/Form.js.map +1 -0
  42. package/dist/components/inputs/Input.d.ts +2 -0
  43. package/dist/components/inputs/Input.js +14 -10
  44. package/dist/components/inputs/Input.js.map +1 -1
  45. package/dist/components/inputs/SecretField.d.ts +21 -0
  46. package/dist/components/inputs/SecretField.js +70 -0
  47. package/dist/components/inputs/SecretField.js.map +1 -0
  48. package/dist/components/layout/AppShell.d.ts +30 -0
  49. package/dist/components/layout/AppShell.js +117 -0
  50. package/dist/components/layout/AppShell.js.map +1 -0
  51. package/dist/components/layout/DesignerShell.d.ts +39 -0
  52. package/dist/components/layout/DesignerShell.js +146 -0
  53. package/dist/components/layout/DesignerShell.js.map +1 -0
  54. package/dist/components/layout/DocsLayout.d.ts +24 -0
  55. package/dist/components/layout/DocsLayout.js +113 -0
  56. package/dist/components/layout/DocsLayout.js.map +1 -0
  57. package/dist/components/layout/SettingsPage.d.ts +31 -0
  58. package/dist/components/layout/SettingsPage.js +92 -0
  59. package/dist/components/layout/SettingsPage.js.map +1 -0
  60. package/dist/components/review/MarkupLayer.d.ts +20 -0
  61. package/dist/components/review/MarkupLayer.js +237 -0
  62. package/dist/components/review/MarkupLayer.js.map +1 -0
  63. package/dist/components/settings/HelpSteps.d.ts +20 -0
  64. package/dist/components/settings/HelpSteps.js +40 -0
  65. package/dist/components/settings/HelpSteps.js.map +1 -0
  66. package/dist/components/settings/IntegrationSettings.d.ts +15 -0
  67. package/dist/components/settings/IntegrationSettings.js +630 -0
  68. package/dist/components/settings/IntegrationSettings.js.map +1 -0
  69. package/dist/components/settings/TestRow.d.ts +21 -0
  70. package/dist/components/settings/TestRow.js +66 -0
  71. package/dist/components/settings/TestRow.js.map +1 -0
  72. package/dist/components/utilities/BrandMark.d.ts +17 -0
  73. package/dist/components/utilities/BrandMark.js +43 -0
  74. package/dist/components/utilities/BrandMark.js.map +1 -0
  75. package/dist/components/utilities/Icon.d.ts +28 -0
  76. package/dist/components/utilities/Icon.js +196 -0
  77. package/dist/components/utilities/Icon.js.map +1 -0
  78. package/dist/index.d.ts +23 -0
  79. package/dist/index.js +35 -0
  80. package/dist/index.js.map +1 -1
  81. package/dist/styles.css +67 -64
  82. 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,48 @@
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
+ export interface SignInPageProps {
19
+ /** Controlled mode. Omit for uncontrolled (uses defaultMode). */
20
+ mode?: "signin" | "signup";
21
+ /** Initial mode when uncontrolled. @default "signin" */
22
+ defaultMode?: "signin" | "signup";
23
+ /** Fires when the footer link flips mode — wire to your router (/signin ↔ /signup). */
24
+ onModeChange?: (mode: "signin" | "signup") => void;
25
+ /** Brand-panel lockup. @default <BrandMark wordmark /> (blinking cursor) */
26
+ brand?: React.ReactNode;
27
+ /** Per-mode copy overrides, merged over defaults: { signin?, signup? }. */
28
+ copy?: { signin?: SignInPageCopy; signup?: SignInPageCopy };
29
+ email?: string;
30
+ password?: string;
31
+ onEmailChange?: (value: string) => void;
32
+ onPasswordChange?: (value: string) => void;
33
+ /** Submit handler (after validation passes); receives { mode, email, password }. */
34
+ onSubmit?: (values: { mode: "signin" | "signup"; email: string; password: string }) => void;
35
+ /** Show + handle the "Forgot?" link (signin mode only). */
36
+ onForgot?: () => void;
37
+ /** @default "Continue with SSO" */
38
+ ssoLabel?: string;
39
+ /** Show + handle the SSO button (hidden when omitted). */
40
+ onSSO?: () => void;
41
+ /** Terms line shown in signup mode. Pass null to hide; omit for the default. */
42
+ terms?: React.ReactNode;
43
+ /** Override the built-in mode-flip footer. Pass null to hide; omit for the default. */
44
+ footer?: React.ReactNode;
45
+ /** Show the left brand panel. @default true */
46
+ showBrandPanel?: boolean;
47
+ }
48
+ export declare function SignInPage(props: SignInPageProps): JSX.Element;