@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.
- package/DESIGN.md +58 -7
- package/README.md +10 -6
- package/dist/components/ai/Confirmation.js +58 -22
- package/dist/components/ai/Confirmation.js.map +1 -1
- package/dist/components/ai/Queue.d.ts +23 -0
- package/dist/components/ai/Queue.js +52 -1
- package/dist/components/ai/Queue.js.map +1 -1
- package/dist/components/ai/ToolCall.js +69 -30
- package/dist/components/ai/ToolCall.js.map +1 -1
- package/dist/components/auth/AccountControl.d.ts +23 -0
- package/dist/components/auth/AccountControl.js +47 -0
- package/dist/components/auth/AccountControl.js.map +1 -0
- package/dist/components/auth/AuthDialog.d.ts +39 -0
- package/dist/components/auth/AuthDialog.js +327 -0
- package/dist/components/auth/AuthDialog.js.map +1 -0
- package/dist/components/auth/SignInPage.d.ts +48 -0
- package/dist/components/auth/SignInPage.js +217 -0
- package/dist/components/auth/SignInPage.js.map +1 -0
- package/dist/components/chat/CodeBlock.js +3 -2
- package/dist/components/chat/CodeBlock.js.map +1 -1
- package/dist/components/chat/ConversationThread.d.ts +67 -0
- package/dist/components/chat/ConversationThread.js +129 -0
- package/dist/components/chat/ConversationThread.js.map +1 -0
- package/dist/components/code/Artifact.js +44 -9
- package/dist/components/code/Artifact.js.map +1 -1
- package/dist/components/code/JSXPreview.js +19 -12
- package/dist/components/code/JSXPreview.js.map +1 -1
- package/dist/components/code/Snippet.js +2 -2
- package/dist/components/code/Snippet.js.map +1 -1
- package/dist/components/code/Terminal.js +24 -10
- package/dist/components/code/Terminal.js.map +1 -1
- package/dist/components/code/WebPreview.js +44 -10
- package/dist/components/code/WebPreview.js.map +1 -1
- package/dist/components/display/Skeleton.js +17 -4
- package/dist/components/display/Skeleton.js.map +1 -1
- package/dist/components/display/StatusPill.d.ts +12 -0
- package/dist/components/display/StatusPill.js +17 -0
- package/dist/components/display/StatusPill.js.map +1 -0
- package/dist/components/inputs/Form.d.ts +164 -0
- package/dist/components/inputs/Form.js +484 -0
- package/dist/components/inputs/Form.js.map +1 -0
- package/dist/components/inputs/Input.d.ts +2 -0
- package/dist/components/inputs/Input.js +14 -10
- package/dist/components/inputs/Input.js.map +1 -1
- package/dist/components/inputs/SecretField.d.ts +21 -0
- package/dist/components/inputs/SecretField.js +70 -0
- package/dist/components/inputs/SecretField.js.map +1 -0
- package/dist/components/layout/AppShell.d.ts +30 -0
- package/dist/components/layout/AppShell.js +117 -0
- package/dist/components/layout/AppShell.js.map +1 -0
- package/dist/components/layout/DesignerShell.d.ts +39 -0
- package/dist/components/layout/DesignerShell.js +146 -0
- package/dist/components/layout/DesignerShell.js.map +1 -0
- package/dist/components/layout/DocsLayout.d.ts +24 -0
- package/dist/components/layout/DocsLayout.js +113 -0
- package/dist/components/layout/DocsLayout.js.map +1 -0
- package/dist/components/layout/SettingsPage.d.ts +31 -0
- package/dist/components/layout/SettingsPage.js +92 -0
- package/dist/components/layout/SettingsPage.js.map +1 -0
- package/dist/components/review/MarkupLayer.d.ts +20 -0
- package/dist/components/review/MarkupLayer.js +237 -0
- package/dist/components/review/MarkupLayer.js.map +1 -0
- package/dist/components/settings/HelpSteps.d.ts +20 -0
- package/dist/components/settings/HelpSteps.js +40 -0
- package/dist/components/settings/HelpSteps.js.map +1 -0
- package/dist/components/settings/IntegrationSettings.d.ts +15 -0
- package/dist/components/settings/IntegrationSettings.js +630 -0
- package/dist/components/settings/IntegrationSettings.js.map +1 -0
- package/dist/components/settings/TestRow.d.ts +21 -0
- package/dist/components/settings/TestRow.js +66 -0
- package/dist/components/settings/TestRow.js.map +1 -0
- package/dist/components/utilities/BrandMark.d.ts +17 -0
- package/dist/components/utilities/BrandMark.js +43 -0
- package/dist/components/utilities/BrandMark.js.map +1 -0
- package/dist/components/utilities/Icon.d.ts +28 -0
- package/dist/components/utilities/Icon.js +196 -0
- package/dist/components/utilities/Icon.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +67 -64
- 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;
|