@agentaily/design-system 0.3.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.
- package/DESIGN.md +3 -3
- package/README.md +2 -2
- package/dist/components/auth/SignInPage.d.ts +24 -3
- package/dist/components/auth/SignInPage.js +60 -38
- package/dist/components/auth/SignInPage.js.map +1 -1
- package/dist/components/utilities/RotatingTagline.d.ts +30 -0
- package/dist/components/utilities/RotatingTagline.js +90 -0
- package/dist/components/utilities/RotatingTagline.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/DESIGN.md
CHANGED
|
@@ -98,7 +98,7 @@ Examples — ✓ "Rate limited. Retry in 18s." ✗ "Oops! Something went wrong
|
|
|
98
98
|
| `components/code/` | **Terminal, FileTree, Snippet, StackTrace, TestResults, Artifact, WebPreview, Agent, Commit, EnvironmentVariables, PackageInfo, Sandbox, SchemaDisplay, JSXPreview** |
|
|
99
99
|
| `components/voice/` | **AudioPlayer, MicSelector, VoiceSelector, SpeechInput, Transcription, Persona** |
|
|
100
100
|
| `components/workflow/` | **Flow (Canvas), Canvas, Node, Edge, Connection, Controls, Panel, Toolbar** |
|
|
101
|
-
| `components/utilities/` | **Image, OpenInChat, Icon (unified Lucide set), BrandMark** |
|
|
101
|
+
| `components/utilities/` | **Image, OpenInChat, Icon (unified Lucide set), BrandMark, RotatingTagline** |
|
|
102
102
|
| `components/settings/` | **TestRow, HelpSteps, IntegrationSettings** — connection-card primitives + the DeepSeek/Feishu integration modal |
|
|
103
103
|
| `components/auth/` | **AuthDialog (+ AuthDialog.useAuth), AccountControl, SignInPage** — sign-in/register modal + persisted session + top-bar account menu + full-page sign-in |
|
|
104
104
|
| `components/review/` | **MarkupLayer** — point-at-an-element review overlay (`data-mk-label`) |
|
|
@@ -108,7 +108,7 @@ Examples — ✓ "Rate limited. Retry in 18s." ✗ "Oops! Something went wrong
|
|
|
108
108
|
| `ui_kits/docs/` | Documentation site (interactive) |
|
|
109
109
|
| `SKILL.md` | Agent-skill entry point |
|
|
110
110
|
|
|
111
|
-
Every component ships `<Name>.jsx` + `<Name>.d.ts` (props) + `<Name>.prompt.md` (usage) — **
|
|
111
|
+
Every component ships `<Name>.jsx` + `<Name>.d.ts` (props) + `<Name>.prompt.md` (usage) — **146 component exports** across the primitive categories (buttons, inputs, display, feedback, overlay, layout, chat, ai, code, voice, workflow, utilities) plus product-domain layers (**settings, auth, review**). Full-page frames — `AppShell` / `DesignerShell` / `DocsLayout` / `SettingsPage` (layout), `ConversationThread` (chat), `SignInPage` (auth) — are **live components, not copy-templates**: change one, every consuming project benefits on re-sync. Consume via the compiled bundle: `window.AxiomDesignSystem_7fc962`. Read each component's `.prompt.md` for copy-paste usage.
|
|
112
112
|
|
|
113
113
|
**Forms are layered, not monolithic.** Presentational controls (Input/Select/Field…) own layout and never depend on a form engine. `Form` + `FormActions` add pure structure. `Form.useForm` is an **optional**, zero-dependency orchestration hook (values/errors/touched/validate/submit) exposed off the capitalized `Form` export — drop it for react-hook-form or TanStack and the controls still work. Errors surface only after blur or submit; spread `form.field(name)` onto any value control, or `form.field(name, {type:"checkbox"})` for boolean ones.
|
|
114
114
|
|
|
@@ -121,7 +121,7 @@ The system sits on **two orthogonal axes**. Don't conflate them: a *domain* is n
|
|
|
121
121
|
| Layer | What | Depends on | Examples |
|
|
122
122
|
|---|---|---|---|
|
|
123
123
|
| **L0 Tokens** | colour / type / space / radius / shadow / motion variables | — | `tokens/*.css` |
|
|
124
|
-
| **L1 Primitives** (atoms) | no business meaning, usable anywhere | tokens | `Button`, `Input`, `Badge`, `Icon`, `BrandMark` |
|
|
124
|
+
| **L1 Primitives** (atoms) | no business meaning, usable anywhere | tokens | `Button`, `Input`, `Badge`, `Icon`, `BrandMark`, `RotatingTagline` |
|
|
125
125
|
| **L2 Composites** (molecules) | a few primitives combined; still cross-product | L1 | `Composer`, `SecretField`, `StatusPill` |
|
|
126
126
|
| **L3 Patterns** (organisms) | self-contained interaction + state, configurable | L1–L2 | `AuthDialog`, `IntegrationSettings`, `MarkupLayer`, `ConversationThread` |
|
|
127
127
|
| **L4 Page shells / frames** | full-page layout with slots | L1–L3 | `AppShell`, `DesignerShell`, `DocsLayout`, `SettingsPage`, `SignInPage` |
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# agentaily design system
|
|
2
2
|
|
|
3
|
-
Agentaily(AI chatbot)设计系统:
|
|
3
|
+
Agentaily(AI chatbot)设计系统:113 个 React 组件 + Storybook,单色亮色优先(暗色可切)。品牌一句话:**极客风格,简约,大气,科技感**。
|
|
4
4
|
|
|
5
5
|
📖 **在线 Storybook:** https://agentaily.github.io/design-system/
|
|
6
6
|
|
|
@@ -28,7 +28,7 @@ npm run build:lib # 产出 dist/:每组件一个 .js + index.d.ts + styles.css
|
|
|
28
28
|
|
|
29
29
|
| 路径 | 内容 |
|
|
30
30
|
| ----------------------------------------------- | ----------------------------------------- |
|
|
31
|
-
| `dist/index.js` | ESM 入口,re-export 全部
|
|
31
|
+
| `dist/index.js` | ESM 入口,re-export 全部 113 个组件符号 |
|
|
32
32
|
| `dist/components/**/*.js` | 每个组件独立模块(含运行时 CSS 注入) |
|
|
33
33
|
| `dist/index.d.ts` + `dist/components/**/*.d.ts` | TypeScript 类型契约 |
|
|
34
34
|
| `dist/styles.css` | 内联好的 tokens + 字体,消费方 import 一次 |
|
|
@@ -15,6 +15,21 @@ export interface SignInPageCopy {
|
|
|
15
15
|
/** Footer link label that flips mode, e.g. "Create one" */
|
|
16
16
|
switchCta?: string;
|
|
17
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
|
+
}
|
|
18
33
|
export interface SignInPageProps {
|
|
19
34
|
/** Controlled mode. Omit for uncontrolled (uses defaultMode). */
|
|
20
35
|
mode?: "signin" | "signup";
|
|
@@ -24,8 +39,14 @@ export interface SignInPageProps {
|
|
|
24
39
|
onModeChange?: (mode: "signin" | "signup") => void;
|
|
25
40
|
/** Brand-panel lockup. @default <BrandMark wordmark /> (blinking cursor) */
|
|
26
41
|
brand?: React.ReactNode;
|
|
27
|
-
/**
|
|
28
|
-
|
|
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>;
|
|
29
50
|
email?: string;
|
|
30
51
|
password?: string;
|
|
31
52
|
onEmailChange?: (value: string) => void;
|
|
@@ -34,7 +55,7 @@ export interface SignInPageProps {
|
|
|
34
55
|
onSubmit?: (values: { mode: "signin" | "signup"; email: string; password: string }) => void;
|
|
35
56
|
/** Show + handle the "Forgot?" link (signin mode only). */
|
|
36
57
|
onForgot?: () => void;
|
|
37
|
-
/** @default "Continue with SSO" */
|
|
58
|
+
/** SSO button label. Overrides copy.labels.sso. @default "Continue with SSO" */
|
|
38
59
|
ssoLabel?: string;
|
|
39
60
|
/** Show + handle the SSO button (hidden when omitted). */
|
|
40
61
|
onSSO?: () => void;
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import { jsxs, jsx
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useState } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { Input } from "../inputs/Input.js";
|
|
4
4
|
import { Button } from "../buttons/Button.js";
|
|
5
5
|
import { Icon } from "../utilities/Icon.js";
|
|
6
|
-
import {
|
|
6
|
+
import { BrandMark } from "../utilities/BrandMark.js";
|
|
7
|
+
import { RotatingTagline } from "../utilities/RotatingTagline.js";
|
|
7
8
|
const AX_SIGNIN_CSS = `
|
|
8
9
|
.ax-signin { display: grid; grid-template-columns: 1fr 1fr; height: 100%; background: var(--surface-page); }
|
|
9
10
|
.ax-signin__brand { position: relative; border-right: 1px solid var(--border-default); background: var(--surface-panel);
|
|
10
11
|
background-image: var(--dot-grid); background-size: 24px 24px; padding: 40px; display: flex; flex-direction: column; }
|
|
11
12
|
.ax-signin__brandtop { display: flex; align-items: center; gap: 10px; }
|
|
12
|
-
.ax-signin__mid { margin-top: auto; margin-bottom: auto;
|
|
13
|
-
.ax-signin__quote { font-family: var(--font-display); font-size: var(--text-
|
|
13
|
+
.ax-signin__mid { margin-top: auto; margin-bottom: auto; }
|
|
14
|
+
.ax-signin__quote { font-family: var(--font-display); white-space: nowrap; font-size: clamp(40px, 4.4vw, var(--text-hero)); font-weight: var(--weight-medium); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--text-body); margin: 0; }
|
|
14
15
|
.ax-signin__by { margin: 16px 0 0; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; color: var(--text-faint); }
|
|
15
16
|
.ax-signin__form { display: flex; align-items: center; justify-content: center; padding: 40px; }
|
|
16
17
|
.ax-signin__card { width: 100%; max-width: 360px; }
|
|
17
18
|
.ax-signin__mbrand { display: none; }
|
|
19
|
+
.ax-signin__mtag { display: none; }
|
|
20
|
+
.ax-signin__mtag .ax-signin__quote { font-size: var(--text-3xl); }
|
|
18
21
|
.ax-signin__h { margin: 0; font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); color: var(--text-body); }
|
|
19
22
|
.ax-signin__sub { margin: 8px 0 28px; font-size: var(--text-sm); color: var(--text-muted); }
|
|
20
23
|
.ax-signin__fields { display: flex; flex-direction: column; gap: 16px; }
|
|
@@ -34,7 +37,7 @@ const AX_SIGNIN_CSS = `
|
|
|
34
37
|
.ax-signin__footlink { color: var(--text-body); background: none; border: none; cursor: pointer; font-family: inherit; font-size: var(--text-sm);
|
|
35
38
|
border-bottom: 1px solid var(--border-strong); padding: 0 0 1px; transition: border-color var(--dur-1) var(--ease-out); }
|
|
36
39
|
.ax-signin__footlink:hover { border-color: var(--text-faint); }
|
|
37
|
-
@media (max-width: 860px) { .ax-signin { grid-template-columns: 1fr; } .ax-signin__brand { display: none; } .ax-signin__mbrand { display: flex; margin-bottom:
|
|
40
|
+
@media (max-width: 860px) { .ax-signin { grid-template-columns: 1fr; } .ax-signin__brand { display: none; } .ax-signin__form { padding: 56px 40px; } .ax-signin__mbrand { display: flex; margin-bottom: 28px; } .ax-signin__mtag { display: block; margin-bottom: 36px; } .ax-signin__h { font-family: var(--font-mono); font-size: var(--text-xs); font-weight: var(--weight-medium); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-body); margin: 0 0 8px; } .ax-signin__sub { margin: 0 0 32px; color: var(--text-muted); } .ax-signin__fields { gap: 22px; } .ax-signin__submit { margin-top: 32px; } .ax-signin__or { margin: 32px 0; } .ax-signin__foot { margin-top: 28px; } }
|
|
38
41
|
`;
|
|
39
42
|
if (typeof document !== "undefined" && !document.getElementById("ax-signin-css")) {
|
|
40
43
|
const s = document.createElement("style");
|
|
@@ -56,7 +59,30 @@ const DEFAULT_COPY = {
|
|
|
56
59
|
submit: "Create account",
|
|
57
60
|
switchText: "Already have an account?",
|
|
58
61
|
switchCta: "Sign in"
|
|
59
|
-
}
|
|
62
|
+
},
|
|
63
|
+
labels: {
|
|
64
|
+
email: "EMAIL",
|
|
65
|
+
password: "PASSWORD",
|
|
66
|
+
confirm: "CONFIRM PASSWORD",
|
|
67
|
+
forgot: "Forgot?",
|
|
68
|
+
or: "OR",
|
|
69
|
+
sso: "Continue with SSO"
|
|
70
|
+
},
|
|
71
|
+
placeholders: {
|
|
72
|
+
email: "you@example.com",
|
|
73
|
+
password: "••••••••",
|
|
74
|
+
passwordNew: "At least 8 characters",
|
|
75
|
+
confirm: "Re-enter your password"
|
|
76
|
+
},
|
|
77
|
+
errors: {
|
|
78
|
+
emailRequired: "Enter your email",
|
|
79
|
+
emailInvalid: "That email doesn’t look right",
|
|
80
|
+
passwordRequired: "Enter your password",
|
|
81
|
+
passwordShort: "Password must be at least 8 characters",
|
|
82
|
+
confirmRequired: "Re-enter your password",
|
|
83
|
+
confirmMismatch: "Passwords don’t match"
|
|
84
|
+
},
|
|
85
|
+
terms: "By creating an account you agree to our Terms and Privacy Policy."
|
|
60
86
|
};
|
|
61
87
|
function SignInPage({
|
|
62
88
|
mode,
|
|
@@ -64,13 +90,14 @@ function SignInPage({
|
|
|
64
90
|
onModeChange,
|
|
65
91
|
brand,
|
|
66
92
|
copy,
|
|
93
|
+
tagline,
|
|
67
94
|
email,
|
|
68
95
|
password,
|
|
69
96
|
onEmailChange,
|
|
70
97
|
onPasswordChange,
|
|
71
98
|
onSubmit,
|
|
72
99
|
onForgot,
|
|
73
|
-
ssoLabel
|
|
100
|
+
ssoLabel,
|
|
74
101
|
onSSO,
|
|
75
102
|
terms,
|
|
76
103
|
footer,
|
|
@@ -80,6 +107,11 @@ function SignInPage({
|
|
|
80
107
|
const m = mode !== void 0 ? mode : modeI;
|
|
81
108
|
const isSignup = m === "signup";
|
|
82
109
|
const c = { ...DEFAULT_COPY[m], ...copy && copy[m] };
|
|
110
|
+
const labels = { ...DEFAULT_COPY.labels, ...copy && copy.labels };
|
|
111
|
+
const ph = { ...DEFAULT_COPY.placeholders, ...copy && copy.placeholders };
|
|
112
|
+
const err = { ...DEFAULT_COPY.errors, ...copy && copy.errors };
|
|
113
|
+
const ssoText = ssoLabel !== void 0 ? ssoLabel : labels.sso;
|
|
114
|
+
const taglineProps = { breakAfterPrefix: true, ...tagline };
|
|
83
115
|
const [emailI, setEmailI] = useState("");
|
|
84
116
|
const [pwI, setPwI] = useState("");
|
|
85
117
|
const [confirm, setConfirm] = useState("");
|
|
@@ -110,13 +142,13 @@ function SignInPage({
|
|
|
110
142
|
const validate = () => {
|
|
111
143
|
const e = {};
|
|
112
144
|
const em = (emailV || "").trim();
|
|
113
|
-
if (!em) e.email =
|
|
114
|
-
else if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(em)) e.email =
|
|
115
|
-
if (!pwV) e.password =
|
|
116
|
-
else if (isSignup && pwV.length < 8) e.password =
|
|
145
|
+
if (!em) e.email = err.emailRequired;
|
|
146
|
+
else if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(em)) e.email = err.emailInvalid;
|
|
147
|
+
if (!pwV) e.password = err.passwordRequired;
|
|
148
|
+
else if (isSignup && pwV.length < 8) e.password = err.passwordShort;
|
|
117
149
|
if (isSignup) {
|
|
118
|
-
if (!confirm) e.confirm =
|
|
119
|
-
else if (confirm !== pwV) e.confirm =
|
|
150
|
+
if (!confirm) e.confirm = err.confirmRequired;
|
|
151
|
+
else if (confirm !== pwV) e.confirm = err.confirmMismatch;
|
|
120
152
|
}
|
|
121
153
|
return e;
|
|
122
154
|
};
|
|
@@ -127,38 +159,28 @@ function SignInPage({
|
|
|
127
159
|
if (Object.keys(e).some((k) => e[k])) return;
|
|
128
160
|
onSubmit && onSubmit({ mode: m, email: (emailV || "").trim(), password: pwV });
|
|
129
161
|
};
|
|
130
|
-
const termsNode = terms !== void 0 ? terms :
|
|
131
|
-
"注册即代表你同意 ",
|
|
132
|
-
/* @__PURE__ */ jsx("span", { className: "lk", children: "服务条款" }),
|
|
133
|
-
" 与",
|
|
134
|
-
" ",
|
|
135
|
-
/* @__PURE__ */ jsx("span", { className: "lk", children: "隐私政策" }),
|
|
136
|
-
"。"
|
|
137
|
-
] });
|
|
162
|
+
const termsNode = terms !== void 0 ? terms : c.terms !== void 0 ? c.terms : DEFAULT_COPY.terms;
|
|
138
163
|
return /* @__PURE__ */ jsxs("div", { className: "ax-signin", children: [
|
|
139
164
|
showBrandPanel ? /* @__PURE__ */ jsxs("aside", { className: "ax-signin__brand", children: [
|
|
140
|
-
/* @__PURE__ */ jsx("div", { className: "ax-signin__brandtop", children: brand || /* @__PURE__ */ jsx(BrandMark, { size: 24, wordmark: true }) }),
|
|
165
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__brandtop", children: brand || /* @__PURE__ */ jsx(BrandMark, { size: 24, wordmark: true, blink: false }) }),
|
|
141
166
|
/* @__PURE__ */ jsxs("div", { className: "ax-signin__mid", children: [
|
|
142
|
-
/* @__PURE__ */
|
|
143
|
-
"聊天,",
|
|
144
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
145
|
-
"构建万物"
|
|
146
|
-
] }),
|
|
167
|
+
/* @__PURE__ */ jsx("p", { className: "ax-signin__quote", children: /* @__PURE__ */ jsx(RotatingTagline, { ...taglineProps }) }),
|
|
147
168
|
/* @__PURE__ */ jsx("p", { className: "ax-signin__by", children: "— AGENTAILY" })
|
|
148
169
|
] })
|
|
149
170
|
] }) : null,
|
|
150
171
|
/* @__PURE__ */ jsx("main", { className: "ax-signin__form", children: /* @__PURE__ */ jsxs("form", { className: "ax-signin__card", onSubmit: submit, noValidate: true, children: [
|
|
151
|
-
/* @__PURE__ */ jsx("div", { className: "ax-signin__mbrand", children: brand || /* @__PURE__ */ jsx(BrandMark, { size: 22, wordmark: true }) }),
|
|
172
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__mbrand", children: brand || /* @__PURE__ */ jsx(BrandMark, { size: 22, wordmark: true, blink: false }) }),
|
|
173
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__mtag", children: /* @__PURE__ */ jsx("p", { className: "ax-signin__quote", children: /* @__PURE__ */ jsx(RotatingTagline, { ...taglineProps }) }) }),
|
|
152
174
|
/* @__PURE__ */ jsx("h1", { className: "ax-signin__h", children: c.title }),
|
|
153
175
|
/* @__PURE__ */ jsx("p", { className: "ax-signin__sub", children: c.subtitle }),
|
|
154
176
|
/* @__PURE__ */ jsxs("div", { className: "ax-signin__fields", children: [
|
|
155
177
|
/* @__PURE__ */ jsx(
|
|
156
178
|
Input,
|
|
157
179
|
{
|
|
158
|
-
label:
|
|
180
|
+
label: labels.email,
|
|
159
181
|
type: "email",
|
|
160
182
|
autoComplete: "email",
|
|
161
|
-
placeholder:
|
|
183
|
+
placeholder: ph.email,
|
|
162
184
|
value: emailV,
|
|
163
185
|
error: errs.email,
|
|
164
186
|
onChange: (e) => setEmail(e.target.value)
|
|
@@ -166,15 +188,15 @@ function SignInPage({
|
|
|
166
188
|
),
|
|
167
189
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
168
190
|
/* @__PURE__ */ jsxs("div", { className: "ax-signin__row", children: [
|
|
169
|
-
/* @__PURE__ */ jsx("span", { className: "ax-label", children:
|
|
170
|
-
!isSignup && onForgot ? /* @__PURE__ */ jsx("button", { type: "button", className: "ax-signin__link", onClick: onForgot, children:
|
|
191
|
+
/* @__PURE__ */ jsx("span", { className: "ax-label", children: labels.password }),
|
|
192
|
+
!isSignup && onForgot ? /* @__PURE__ */ jsx("button", { type: "button", className: "ax-signin__link", onClick: onForgot, children: labels.forgot }) : null
|
|
171
193
|
] }),
|
|
172
194
|
/* @__PURE__ */ jsx(
|
|
173
195
|
Input,
|
|
174
196
|
{
|
|
175
197
|
type: "password",
|
|
176
198
|
autoComplete: isSignup ? "new-password" : "current-password",
|
|
177
|
-
placeholder: isSignup ?
|
|
199
|
+
placeholder: isSignup ? ph.passwordNew : ph.password,
|
|
178
200
|
value: pwV,
|
|
179
201
|
error: errs.password,
|
|
180
202
|
onChange: (e) => setPw(e.target.value)
|
|
@@ -184,10 +206,10 @@ function SignInPage({
|
|
|
184
206
|
isSignup ? /* @__PURE__ */ jsx(
|
|
185
207
|
Input,
|
|
186
208
|
{
|
|
187
|
-
label:
|
|
209
|
+
label: labels.confirm,
|
|
188
210
|
type: "password",
|
|
189
211
|
autoComplete: "new-password",
|
|
190
|
-
placeholder:
|
|
212
|
+
placeholder: ph.confirm,
|
|
191
213
|
value: confirm,
|
|
192
214
|
error: errs.confirm,
|
|
193
215
|
onChange: (e) => setConf(e.target.value)
|
|
@@ -197,10 +219,10 @@ function SignInPage({
|
|
|
197
219
|
/* @__PURE__ */ jsx("div", { className: "ax-signin__submit", children: /* @__PURE__ */ jsx(Button, { variant: "primary", full: true, type: "submit", icon: /* @__PURE__ */ jsx(Icon, { name: "arrow", size: 15 }), children: c.submit }) }),
|
|
198
220
|
isSignup ? /* @__PURE__ */ jsx("p", { className: "ax-signin__terms", children: termsNode }) : null,
|
|
199
221
|
onSSO ? /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
200
|
-
/* @__PURE__ */ jsx("div", { className: "ax-signin__or", children: /* @__PURE__ */ jsx("span", { children:
|
|
222
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__or", children: /* @__PURE__ */ jsx("span", { children: labels.or }) }),
|
|
201
223
|
/* @__PURE__ */ jsxs("button", { type: "button", className: "ax-signin__sso", onClick: onSSO, children: [
|
|
202
224
|
/* @__PURE__ */ jsx(Icon, { name: "shield", size: 15 }),
|
|
203
|
-
|
|
225
|
+
ssoText
|
|
204
226
|
] })
|
|
205
227
|
] }) : null,
|
|
206
228
|
footer !== void 0 ? footer ? /* @__PURE__ */ jsx("p", { className: "ax-signin__foot", children: footer }) : null : /* @__PURE__ */ jsxs("p", { className: "ax-signin__foot", children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SignInPage.js","sources":["../../../src/components/auth/SignInPage.jsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { BrandMark } from \"../utilities/BrandMark.jsx\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\nimport { Input } from \"../inputs/Input.jsx\";\n\n// SignInPage — full-page auth: a split brand panel (dot-grid + quote) beside a\n// centered card. One component, two modes (signin / signup) — the brand panel,\n// SSO, and card chrome are shared; only the fields, copy, and footer differ.\n// The footer link flips mode (and fires onModeChange so a router can swap the\n// URL). The full-page sibling of AuthDialog (the modal); same login/register idea.\nconst AX_SIGNIN_CSS = `\n.ax-signin { display: grid; grid-template-columns: 1fr 1fr; height: 100%; background: var(--surface-page); }\n.ax-signin__brand { position: relative; border-right: 1px solid var(--border-default); background: var(--surface-panel);\n background-image: var(--dot-grid); background-size: 24px 24px; padding: 40px; display: flex; flex-direction: column; }\n.ax-signin__brandtop { display: flex; align-items: center; gap: 10px; }\n.ax-signin__mid { margin-top: auto; margin-bottom: auto; max-width: 22ch; }\n.ax-signin__quote { font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-medium); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--text-body); margin: 0; }\n.ax-signin__by { margin: 16px 0 0; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; color: var(--text-faint); }\n.ax-signin__form { display: flex; align-items: center; justify-content: center; padding: 40px; }\n.ax-signin__card { width: 100%; max-width: 360px; }\n.ax-signin__mbrand { display: none; }\n.ax-signin__h { margin: 0; font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); color: var(--text-body); }\n.ax-signin__sub { margin: 8px 0 28px; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__fields { display: flex; flex-direction: column; gap: 16px; }\n.ax-signin__row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }\n.ax-signin__link { font-size: 12px; color: var(--text-muted); text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit; }\n.ax-signin__link:hover { color: var(--text-body); }\n.ax-signin__submit { margin-top: 22px; }\n.ax-signin__or { display: flex; align-items: center; gap: 12px; margin: 22px 0; }\n.ax-signin__or::before, .ax-signin__or::after { content: \"\"; flex: 1; height: 1px; background: var(--border-default); }\n.ax-signin__or span { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--text-faint); }\n.ax-signin__sso { display: flex; align-items: center; justify-content: center; gap: 9px; width: 100%; height: 40px; border: 1px solid var(--border-default);\n border-radius: var(--radius-2); background: var(--surface-card); color: var(--text-body); font-family: inherit; font-size: var(--text-sm); cursor: pointer; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__sso:hover { border-color: var(--border-strong); }\n.ax-signin__terms { margin: 16px 0 0; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); text-align: center; }\n.ax-signin__terms a, .ax-signin__terms span.lk { color: var(--text-muted); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }\n.ax-signin__foot { margin-top: 26px; text-align: center; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__footlink { color: var(--text-body); background: none; border: none; cursor: pointer; font-family: inherit; font-size: var(--text-sm);\n border-bottom: 1px solid var(--border-strong); padding: 0 0 1px; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__footlink:hover { border-color: var(--text-faint); }\n@media (max-width: 860px) { .ax-signin { grid-template-columns: 1fr; } .ax-signin__brand { display: none; } .ax-signin__mbrand { display: flex; margin-bottom: 26px; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-signin-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-signin-css\";\n s.textContent = AX_SIGNIN_CSS;\n document.head.appendChild(s);\n}\n\nconst DEFAULT_COPY = {\n signin: {\n title: \"Sign in\",\n subtitle: \"Welcome back. Use your work email to continue.\",\n submit: \"Continue\",\n switchText: \"No account?\",\n switchCta: \"Create one\",\n },\n signup: {\n title: \"Create account\",\n subtitle: \"Start with your work email — it takes a minute.\",\n submit: \"Create account\",\n switchText: \"Already have an account?\",\n switchCta: \"Sign in\",\n },\n};\n\nexport function SignInPage({\n mode,\n defaultMode = \"signin\",\n onModeChange,\n brand,\n copy,\n email,\n password,\n onEmailChange,\n onPasswordChange,\n onSubmit,\n onForgot,\n ssoLabel = \"Continue with SSO\",\n onSSO,\n terms,\n footer,\n showBrandPanel = true,\n}) {\n const [modeI, setModeI] = useState(defaultMode);\n const m = mode !== undefined ? mode : modeI;\n const isSignup = m === \"signup\";\n const c = { ...DEFAULT_COPY[m], ...(copy && copy[m]) };\n\n const [emailI, setEmailI] = useState(\"\");\n const [pwI, setPwI] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [errs, setErrs] = useState({});\n const emailV = email !== undefined ? email : emailI;\n const pwV = password !== undefined ? password : pwI;\n const setEmail = (v) => {\n if (onEmailChange) onEmailChange(v);\n else setEmailI(v);\n if (errs.email) setErrs((e) => ({ ...e, email: undefined }));\n };\n const setPw = (v) => {\n if (onPasswordChange) onPasswordChange(v);\n else setPwI(v);\n if (errs.password) setErrs((e) => ({ ...e, password: undefined }));\n };\n const setConf = (v) => {\n setConfirm(v);\n if (errs.confirm) setErrs((e) => ({ ...e, confirm: undefined }));\n };\n\n const switchMode = () => {\n const next = isSignup ? \"signin\" : \"signup\";\n setErrs({});\n setConfirm(\"\");\n if (onModeChange) onModeChange(next);\n if (mode === undefined) setModeI(next);\n };\n\n const validate = () => {\n const e = {};\n const em = (emailV || \"\").trim();\n if (!em) e.email = \"请输入邮箱\";\n else if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(em)) e.email = \"邮箱格式不正确\";\n if (!pwV) e.password = \"请输入密码\";\n else if (isSignup && pwV.length < 8) e.password = \"密码至少 8 位\";\n if (isSignup) {\n if (!confirm) e.confirm = \"请再次输入密码\";\n else if (confirm !== pwV) e.confirm = \"两次输入的密码不一致\";\n }\n return e;\n };\n\n const submit = (ev) => {\n ev.preventDefault();\n const e = validate();\n setErrs(e);\n if (Object.keys(e).some((k) => e[k])) return;\n onSubmit && onSubmit({ mode: m, email: (emailV || \"\").trim(), password: pwV });\n };\n\n const termsNode =\n terms !== undefined ? (\n terms\n ) : (\n <>\n 注册即代表你同意 <span className=\"lk\">服务条款</span> 与{\" \"}\n <span className=\"lk\">隐私政策</span>。\n </>\n );\n\n return (\n <div className=\"ax-signin\">\n {showBrandPanel ? (\n <aside className=\"ax-signin__brand\">\n <div className=\"ax-signin__brandtop\">{brand || <BrandMark size={24} wordmark />}</div>\n <div className=\"ax-signin__mid\">\n <p className=\"ax-signin__quote\">\n 聊天,\n <br />\n 构建万物\n </p>\n <p className=\"ax-signin__by\">— AGENTAILY</p>\n </div>\n </aside>\n ) : null}\n\n <main className=\"ax-signin__form\">\n <form className=\"ax-signin__card\" onSubmit={submit} noValidate>\n <div className=\"ax-signin__mbrand\">{brand || <BrandMark size={22} wordmark />}</div>\n <h1 className=\"ax-signin__h\">{c.title}</h1>\n <p className=\"ax-signin__sub\">{c.subtitle}</p>\n\n <div className=\"ax-signin__fields\">\n <Input\n label=\"EMAIL\"\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"you@example.com\"\n value={emailV}\n error={errs.email}\n onChange={(e) => setEmail(e.target.value)}\n />\n <div>\n <div className=\"ax-signin__row\">\n <span className=\"ax-label\">PASSWORD</span>\n {!isSignup && onForgot ? (\n <button type=\"button\" className=\"ax-signin__link\" onClick={onForgot}>\n Forgot?\n </button>\n ) : null}\n </div>\n <Input\n type=\"password\"\n autoComplete={isSignup ? \"new-password\" : \"current-password\"}\n placeholder={isSignup ? \"至少 8 位\" : \"••••••••\"}\n value={pwV}\n error={errs.password}\n onChange={(e) => setPw(e.target.value)}\n />\n </div>\n {isSignup ? (\n <Input\n label=\"CONFIRM PASSWORD\"\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder=\"再次输入密码\"\n value={confirm}\n error={errs.confirm}\n onChange={(e) => setConf(e.target.value)}\n />\n ) : null}\n </div>\n\n <div className=\"ax-signin__submit\">\n <Button variant=\"primary\" full type=\"submit\" icon={<Icon name=\"arrow\" size={15} />}>\n {c.submit}\n </Button>\n </div>\n\n {isSignup ? <p className=\"ax-signin__terms\">{termsNode}</p> : null}\n\n {onSSO ? (\n <React.Fragment>\n <div className=\"ax-signin__or\">\n <span>OR</span>\n </div>\n <button type=\"button\" className=\"ax-signin__sso\" onClick={onSSO}>\n <Icon name=\"shield\" size={15} />\n {ssoLabel}\n </button>\n </React.Fragment>\n ) : null}\n\n {footer !== undefined ? (\n footer ? (\n <p className=\"ax-signin__foot\">{footer}</p>\n ) : null\n ) : (\n <p className=\"ax-signin__foot\">\n {c.switchText}{\" \"}\n <button type=\"button\" className=\"ax-signin__footlink\" onClick={switchMode}>\n {c.switchCta}\n </button>\n </p>\n )}\n </form>\n </main>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SignInPage.js","sources":["../../../src/components/auth/SignInPage.jsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Input } from \"../inputs/Input.jsx\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { Icon } from \"../utilities/Icon.jsx\";\nimport { BrandMark } from \"../utilities/BrandMark.jsx\";\nimport { RotatingTagline } from \"../utilities/RotatingTagline.jsx\";\n\n// SignInPage — full-page auth: a split brand panel (dot-grid + quote) beside a\n// centered card. One component, two modes (signin / signup) — the brand panel,\n// SSO, and card chrome are shared; only the fields, copy, and footer differ.\n// The footer link flips mode (and fires onModeChange so a router can swap the\n// URL). The full-page sibling of AuthDialog (the modal); same login/register idea.\nconst AX_SIGNIN_CSS = `\n.ax-signin { display: grid; grid-template-columns: 1fr 1fr; height: 100%; background: var(--surface-page); }\n.ax-signin__brand { position: relative; border-right: 1px solid var(--border-default); background: var(--surface-panel);\n background-image: var(--dot-grid); background-size: 24px 24px; padding: 40px; display: flex; flex-direction: column; }\n.ax-signin__brandtop { display: flex; align-items: center; gap: 10px; }\n.ax-signin__mid { margin-top: auto; margin-bottom: auto; }\n.ax-signin__quote { font-family: var(--font-display); white-space: nowrap; font-size: clamp(40px, 4.4vw, var(--text-hero)); font-weight: var(--weight-medium); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--text-body); margin: 0; }\n.ax-signin__by { margin: 16px 0 0; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; color: var(--text-faint); }\n.ax-signin__form { display: flex; align-items: center; justify-content: center; padding: 40px; }\n.ax-signin__card { width: 100%; max-width: 360px; }\n.ax-signin__mbrand { display: none; }\n.ax-signin__mtag { display: none; }\n.ax-signin__mtag .ax-signin__quote { font-size: var(--text-3xl); }\n.ax-signin__h { margin: 0; font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); color: var(--text-body); }\n.ax-signin__sub { margin: 8px 0 28px; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__fields { display: flex; flex-direction: column; gap: 16px; }\n.ax-signin__row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }\n.ax-signin__link { font-size: 12px; color: var(--text-muted); text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit; }\n.ax-signin__link:hover { color: var(--text-body); }\n.ax-signin__submit { margin-top: 22px; }\n.ax-signin__or { display: flex; align-items: center; gap: 12px; margin: 22px 0; }\n.ax-signin__or::before, .ax-signin__or::after { content: \"\"; flex: 1; height: 1px; background: var(--border-default); }\n.ax-signin__or span { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--text-faint); }\n.ax-signin__sso { display: flex; align-items: center; justify-content: center; gap: 9px; width: 100%; height: 40px; border: 1px solid var(--border-default);\n border-radius: var(--radius-2); background: var(--surface-card); color: var(--text-body); font-family: inherit; font-size: var(--text-sm); cursor: pointer; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__sso:hover { border-color: var(--border-strong); }\n.ax-signin__terms { margin: 16px 0 0; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); text-align: center; }\n.ax-signin__terms a, .ax-signin__terms span.lk { color: var(--text-muted); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }\n.ax-signin__foot { margin-top: 26px; text-align: center; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__footlink { color: var(--text-body); background: none; border: none; cursor: pointer; font-family: inherit; font-size: var(--text-sm);\n border-bottom: 1px solid var(--border-strong); padding: 0 0 1px; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__footlink:hover { border-color: var(--text-faint); }\n@media (max-width: 860px) { .ax-signin { grid-template-columns: 1fr; } .ax-signin__brand { display: none; } .ax-signin__form { padding: 56px 40px; } .ax-signin__mbrand { display: flex; margin-bottom: 28px; } .ax-signin__mtag { display: block; margin-bottom: 36px; } .ax-signin__h { font-family: var(--font-mono); font-size: var(--text-xs); font-weight: var(--weight-medium); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-body); margin: 0 0 8px; } .ax-signin__sub { margin: 0 0 32px; color: var(--text-muted); } .ax-signin__fields { gap: 22px; } .ax-signin__submit { margin-top: 32px; } .ax-signin__or { margin: 32px 0; } .ax-signin__foot { margin-top: 28px; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-signin-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-signin-css\";\n s.textContent = AX_SIGNIN_CSS;\n document.head.appendChild(s);\n}\n\n// Every user-facing string lives here so nothing is hardcoded in the markup.\n// Defaults are a self-consistent English baseline; pass a `copy` prop (any\n// subset, deep-merged per group) to localize — e.g. a full zh-CN object.\nconst DEFAULT_COPY = {\n signin: {\n title: \"Sign in\",\n subtitle: \"Welcome back. Use your work email to continue.\",\n submit: \"Continue\",\n switchText: \"No account?\",\n switchCta: \"Create one\",\n },\n signup: {\n title: \"Create account\",\n subtitle: \"Start with your work email — it takes a minute.\",\n submit: \"Create account\",\n switchText: \"Already have an account?\",\n switchCta: \"Sign in\",\n },\n labels: {\n email: \"EMAIL\",\n password: \"PASSWORD\",\n confirm: \"CONFIRM PASSWORD\",\n forgot: \"Forgot?\",\n or: \"OR\",\n sso: \"Continue with SSO\",\n },\n placeholders: {\n email: \"you@example.com\",\n password: \"••••••••\",\n passwordNew: \"At least 8 characters\",\n confirm: \"Re-enter your password\",\n },\n errors: {\n emailRequired: \"Enter your email\",\n emailInvalid: \"That email doesn’t look right\",\n passwordRequired: \"Enter your password\",\n passwordShort: \"Password must be at least 8 characters\",\n confirmRequired: \"Re-enter your password\",\n confirmMismatch: \"Passwords don’t match\",\n },\n terms: \"By creating an account you agree to our Terms and Privacy Policy.\",\n};\n\nexport function SignInPage({\n mode,\n defaultMode = \"signin\",\n onModeChange,\n brand,\n copy,\n tagline,\n email,\n password,\n onEmailChange,\n onPasswordChange,\n onSubmit,\n onForgot,\n ssoLabel,\n onSSO,\n terms,\n footer,\n showBrandPanel = true,\n}) {\n const [modeI, setModeI] = useState(defaultMode);\n const m = mode !== undefined ? mode : modeI;\n const isSignup = m === \"signup\";\n const c = { ...DEFAULT_COPY[m], ...(copy && copy[m]) };\n const labels = { ...DEFAULT_COPY.labels, ...(copy && copy.labels) };\n const ph = { ...DEFAULT_COPY.placeholders, ...(copy && copy.placeholders) };\n const err = { ...DEFAULT_COPY.errors, ...(copy && copy.errors) };\n const ssoText = ssoLabel !== undefined ? ssoLabel : labels.sso;\n const taglineProps = { breakAfterPrefix: true, ...tagline };\n\n const [emailI, setEmailI] = useState(\"\");\n const [pwI, setPwI] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [errs, setErrs] = useState({});\n const emailV = email !== undefined ? email : emailI;\n const pwV = password !== undefined ? password : pwI;\n const setEmail = (v) => {\n if (onEmailChange) onEmailChange(v);\n else setEmailI(v);\n if (errs.email) setErrs((e) => ({ ...e, email: undefined }));\n };\n const setPw = (v) => {\n if (onPasswordChange) onPasswordChange(v);\n else setPwI(v);\n if (errs.password) setErrs((e) => ({ ...e, password: undefined }));\n };\n const setConf = (v) => {\n setConfirm(v);\n if (errs.confirm) setErrs((e) => ({ ...e, confirm: undefined }));\n };\n\n const switchMode = () => {\n const next = isSignup ? \"signin\" : \"signup\";\n setErrs({});\n setConfirm(\"\");\n if (onModeChange) onModeChange(next);\n if (mode === undefined) setModeI(next);\n };\n\n const validate = () => {\n const e = {};\n const em = (emailV || \"\").trim();\n if (!em) e.email = err.emailRequired;\n else if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(em)) e.email = err.emailInvalid;\n if (!pwV) e.password = err.passwordRequired;\n else if (isSignup && pwV.length < 8) e.password = err.passwordShort;\n if (isSignup) {\n if (!confirm) e.confirm = err.confirmRequired;\n else if (confirm !== pwV) e.confirm = err.confirmMismatch;\n }\n return e;\n };\n\n const submit = (ev) => {\n ev.preventDefault();\n const e = validate();\n setErrs(e);\n if (Object.keys(e).some((k) => e[k])) return;\n onSubmit && onSubmit({ mode: m, email: (emailV || \"\").trim(), password: pwV });\n };\n\n const termsNode =\n terms !== undefined ? terms : c.terms !== undefined ? c.terms : DEFAULT_COPY.terms;\n\n return (\n <div className=\"ax-signin\">\n {showBrandPanel ? (\n <aside className=\"ax-signin__brand\">\n <div className=\"ax-signin__brandtop\">\n {brand || <BrandMark size={24} wordmark blink={false} />}\n </div>\n <div className=\"ax-signin__mid\">\n <p className=\"ax-signin__quote\">\n <RotatingTagline {...taglineProps} />\n </p>\n <p className=\"ax-signin__by\">— AGENTAILY</p>\n </div>\n </aside>\n ) : null}\n\n <main className=\"ax-signin__form\">\n <form className=\"ax-signin__card\" onSubmit={submit} noValidate>\n <div className=\"ax-signin__mbrand\">\n {brand || <BrandMark size={22} wordmark blink={false} />}\n </div>\n <div className=\"ax-signin__mtag\">\n <p className=\"ax-signin__quote\">\n <RotatingTagline {...taglineProps} />\n </p>\n </div>\n <h1 className=\"ax-signin__h\">{c.title}</h1>\n <p className=\"ax-signin__sub\">{c.subtitle}</p>\n\n <div className=\"ax-signin__fields\">\n <Input\n label={labels.email}\n type=\"email\"\n autoComplete=\"email\"\n placeholder={ph.email}\n value={emailV}\n error={errs.email}\n onChange={(e) => setEmail(e.target.value)}\n />\n <div>\n <div className=\"ax-signin__row\">\n <span className=\"ax-label\">{labels.password}</span>\n {!isSignup && onForgot ? (\n <button type=\"button\" className=\"ax-signin__link\" onClick={onForgot}>\n {labels.forgot}\n </button>\n ) : null}\n </div>\n <Input\n type=\"password\"\n autoComplete={isSignup ? \"new-password\" : \"current-password\"}\n placeholder={isSignup ? ph.passwordNew : ph.password}\n value={pwV}\n error={errs.password}\n onChange={(e) => setPw(e.target.value)}\n />\n </div>\n {isSignup ? (\n <Input\n label={labels.confirm}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={ph.confirm}\n value={confirm}\n error={errs.confirm}\n onChange={(e) => setConf(e.target.value)}\n />\n ) : null}\n </div>\n\n <div className=\"ax-signin__submit\">\n <Button variant=\"primary\" full type=\"submit\" icon={<Icon name=\"arrow\" size={15} />}>\n {c.submit}\n </Button>\n </div>\n\n {isSignup ? <p className=\"ax-signin__terms\">{termsNode}</p> : null}\n\n {onSSO ? (\n <React.Fragment>\n <div className=\"ax-signin__or\">\n <span>{labels.or}</span>\n </div>\n <button type=\"button\" className=\"ax-signin__sso\" onClick={onSSO}>\n <Icon name=\"shield\" size={15} />\n {ssoText}\n </button>\n </React.Fragment>\n ) : null}\n\n {footer !== undefined ? (\n footer ? (\n <p className=\"ax-signin__foot\">{footer}</p>\n ) : null\n ) : (\n <p className=\"ax-signin__foot\">\n {c.switchText}{\" \"}\n <button type=\"button\" className=\"ax-signin__footlink\" onClick={switchMode}>\n {c.switchCta}\n </button>\n </p>\n )}\n </form>\n </main>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RotatingTagline — the brand's signature animated headline. A fixed prefix
|
|
3
|
+
* (e.g. "聊天,") followed by a phrase that types in, holds, deletes, and
|
|
4
|
+
* advances. The rotating phrase wears the flowing geek-rainbow gradient; a
|
|
5
|
+
* block cursor trails it and blinks only at rest. Font size/weight inherit
|
|
6
|
+
* from the parent — size it by styling the container. Respects
|
|
7
|
+
* prefers-reduced-motion (whole-phrase swap, no gradient flow).
|
|
8
|
+
*/
|
|
9
|
+
export interface RotatingTaglineProps {
|
|
10
|
+
/** Static text shown before the rotating phrase. @default "聊天," */
|
|
11
|
+
prefix?: string;
|
|
12
|
+
/** Phrases cycled through, in order. @default ["构建万物","生成万物","设计万物","学习万物"] */
|
|
13
|
+
phrases?: string[];
|
|
14
|
+
/** Paint the rotating phrase with the flowing rainbow gradient. @default true */
|
|
15
|
+
gradient?: boolean;
|
|
16
|
+
/** Show the trailing block cursor. @default true */
|
|
17
|
+
cursor?: boolean;
|
|
18
|
+
/** Put a <br/> between prefix and phrase (two-line tagline). @default false */
|
|
19
|
+
breakAfterPrefix?: boolean;
|
|
20
|
+
/** Per-character type-in delay, ms. @default 140 */
|
|
21
|
+
typeSpeed?: number;
|
|
22
|
+
/** Per-character delete delay, ms. @default 70 */
|
|
23
|
+
deleteSpeed?: number;
|
|
24
|
+
/** Hold time on the full phrase before deleting, ms. @default 1700 */
|
|
25
|
+
hold?: number;
|
|
26
|
+
/** Gradient flow loop duration (CSS time). @default "1.2s" */
|
|
27
|
+
flowDuration?: string;
|
|
28
|
+
className?: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function RotatingTagline(props: RotatingTaglineProps): JSX.Element;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
const AX_RTAG_CSS = `
|
|
4
|
+
.ax-rtag { display: inline; color: var(--text-body); }
|
|
5
|
+
.ax-rtag__prefix { color: var(--text-body); }
|
|
6
|
+
.ax-rtag__verb { color: var(--text-body); }
|
|
7
|
+
.ax-rtag__grad {
|
|
8
|
+
background: linear-gradient(95deg, #F2806B 0%, #E9B24F 18%, #6FB66A 36%, #4DB6AE 54%, #5B8DEF 72%, #9B7BEA 86%, #F2806B 100%);
|
|
9
|
+
background-size: 220% 100%;
|
|
10
|
+
-webkit-background-clip: text; background-clip: text;
|
|
11
|
+
-webkit-text-fill-color: transparent; color: transparent;
|
|
12
|
+
animation: ax-rtag-flow var(--ax-rtag-flow, 1.2s) linear infinite;
|
|
13
|
+
}
|
|
14
|
+
.ax-rtag__cursor {
|
|
15
|
+
display: inline-block; width: 0.5em; height: 0.84em; background: var(--text-body);
|
|
16
|
+
vertical-align: -0.02em; margin-left: 0.04em;
|
|
17
|
+
}
|
|
18
|
+
.ax-rtag__cursor.is-blink { animation: ax-rtag-blink 1.1s steps(1) infinite; }
|
|
19
|
+
@keyframes ax-rtag-flow { to { background-position: 220% 0; } }
|
|
20
|
+
@keyframes ax-rtag-blink { 50% { opacity: 0; } }
|
|
21
|
+
@media (prefers-reduced-motion: reduce) { .ax-rtag__grad { animation: none; } }
|
|
22
|
+
`;
|
|
23
|
+
if (typeof document !== "undefined" && !document.getElementById("ax-rtag-css")) {
|
|
24
|
+
const s = document.createElement("style");
|
|
25
|
+
s.id = "ax-rtag-css";
|
|
26
|
+
s.textContent = AX_RTAG_CSS;
|
|
27
|
+
document.head.appendChild(s);
|
|
28
|
+
}
|
|
29
|
+
function RotatingTagline({
|
|
30
|
+
prefix = "聊天,",
|
|
31
|
+
phrases = ["构建万物", "生成万物", "设计万物", "学习万物"],
|
|
32
|
+
gradient = true,
|
|
33
|
+
cursor = true,
|
|
34
|
+
breakAfterPrefix = false,
|
|
35
|
+
typeSpeed = 140,
|
|
36
|
+
deleteSpeed = 70,
|
|
37
|
+
hold = 1700,
|
|
38
|
+
flowDuration = "1.2s",
|
|
39
|
+
className = "",
|
|
40
|
+
...rest
|
|
41
|
+
}) {
|
|
42
|
+
const list = phrases && phrases.length ? phrases : [""];
|
|
43
|
+
const reduced = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
44
|
+
const [wi, setWi] = useState(0);
|
|
45
|
+
const [txt, setTxt] = useState(list[0]);
|
|
46
|
+
const [deleting, setDeleting] = useState(false);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (reduced) {
|
|
49
|
+
const t2 = setTimeout(() => setWi((w) => (w + 1) % list.length), hold + 700);
|
|
50
|
+
return () => clearTimeout(t2);
|
|
51
|
+
}
|
|
52
|
+
const word = list[wi % list.length];
|
|
53
|
+
let t;
|
|
54
|
+
if (!deleting && txt === word) {
|
|
55
|
+
t = setTimeout(() => setDeleting(true), hold);
|
|
56
|
+
} else if (deleting && txt === "") {
|
|
57
|
+
setDeleting(false);
|
|
58
|
+
setWi((w) => (w + 1) % list.length);
|
|
59
|
+
} else {
|
|
60
|
+
const next = deleting ? word.slice(0, txt.length - 1) : word.slice(0, txt.length + 1);
|
|
61
|
+
t = setTimeout(() => setTxt(next), deleting ? deleteSpeed : typeSpeed);
|
|
62
|
+
}
|
|
63
|
+
return () => clearTimeout(t);
|
|
64
|
+
}, [txt, deleting, wi, reduced, hold, typeSpeed, deleteSpeed]);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (reduced) setTxt(list[wi % list.length]);
|
|
67
|
+
}, [wi, reduced]);
|
|
68
|
+
const atRest = reduced || !deleting && txt === list[wi % list.length];
|
|
69
|
+
const verbCls = ["ax-rtag__verb", gradient ? "ax-rtag__grad" : ""].filter(Boolean).join(" ");
|
|
70
|
+
return /* @__PURE__ */ jsxs(
|
|
71
|
+
"span",
|
|
72
|
+
{
|
|
73
|
+
className: ["ax-rtag", className].filter(Boolean).join(" "),
|
|
74
|
+
style: { "--ax-rtag-flow": flowDuration },
|
|
75
|
+
...rest,
|
|
76
|
+
children: [
|
|
77
|
+
prefix ? /* @__PURE__ */ jsxs("span", { className: "ax-rtag__prefix", children: [
|
|
78
|
+
prefix,
|
|
79
|
+
breakAfterPrefix ? /* @__PURE__ */ jsx("br", {}) : null
|
|
80
|
+
] }) : null,
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: verbCls, children: txt }),
|
|
82
|
+
cursor ? /* @__PURE__ */ jsx("i", { className: "ax-rtag__cursor" + (atRest ? " is-blink" : ""), "aria-hidden": "true" }) : null
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
RotatingTagline
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=RotatingTagline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RotatingTagline.js","sources":["../../../src/components/utilities/RotatingTagline.jsx"],"sourcesContent":["import React, { useState, useEffect } from \"react\";\n\n// RotatingTagline — the brand's signature animated headline: a fixed prefix\n// followed by a phrase that types in, holds, deletes, and advances to the next.\n// The rotating phrase wears the flowing geek-rainbow gradient; a block cursor\n// trails the text and blinks only while at rest. Used in the auth brand panel\n// and the marketing hero. Respects prefers-reduced-motion (whole-phrase swap,\n// no gradient flow). Font size / weight are inherited from the parent — size it\n// by styling the container, not this component.\nconst AX_RTAG_CSS = `\n.ax-rtag { display: inline; color: var(--text-body); }\n.ax-rtag__prefix { color: var(--text-body); }\n.ax-rtag__verb { color: var(--text-body); }\n.ax-rtag__grad {\n background: linear-gradient(95deg, #F2806B 0%, #E9B24F 18%, #6FB66A 36%, #4DB6AE 54%, #5B8DEF 72%, #9B7BEA 86%, #F2806B 100%);\n background-size: 220% 100%;\n -webkit-background-clip: text; background-clip: text;\n -webkit-text-fill-color: transparent; color: transparent;\n animation: ax-rtag-flow var(--ax-rtag-flow, 1.2s) linear infinite;\n}\n.ax-rtag__cursor {\n display: inline-block; width: 0.5em; height: 0.84em; background: var(--text-body);\n vertical-align: -0.02em; margin-left: 0.04em;\n}\n.ax-rtag__cursor.is-blink { animation: ax-rtag-blink 1.1s steps(1) infinite; }\n@keyframes ax-rtag-flow { to { background-position: 220% 0; } }\n@keyframes ax-rtag-blink { 50% { opacity: 0; } }\n@media (prefers-reduced-motion: reduce) { .ax-rtag__grad { animation: none; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-rtag-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-rtag-css\";\n s.textContent = AX_RTAG_CSS;\n document.head.appendChild(s);\n}\n\nexport function RotatingTagline({\n prefix = \"聊天,\",\n phrases = [\"构建万物\", \"生成万物\", \"设计万物\", \"学习万物\"],\n gradient = true,\n cursor = true,\n breakAfterPrefix = false,\n typeSpeed = 140,\n deleteSpeed = 70,\n hold = 1700,\n flowDuration = \"1.2s\",\n className = \"\",\n ...rest\n}) {\n const list = phrases && phrases.length ? phrases : [\"\"];\n const reduced =\n typeof window !== \"undefined\" &&\n window.matchMedia &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n const [wi, setWi] = useState(0);\n const [txt, setTxt] = useState(list[0]);\n const [deleting, setDeleting] = useState(false);\n\n useEffect(() => {\n if (reduced) {\n const t = setTimeout(() => setWi((w) => (w + 1) % list.length), hold + 700);\n return () => clearTimeout(t);\n }\n const word = list[wi % list.length];\n let t;\n if (!deleting && txt === word) {\n t = setTimeout(() => setDeleting(true), hold);\n } else if (deleting && txt === \"\") {\n setDeleting(false);\n setWi((w) => (w + 1) % list.length);\n } else {\n const next = deleting ? word.slice(0, txt.length - 1) : word.slice(0, txt.length + 1);\n t = setTimeout(() => setTxt(next), deleting ? deleteSpeed : typeSpeed);\n }\n return () => clearTimeout(t);\n }, [txt, deleting, wi, reduced, hold, typeSpeed, deleteSpeed]);\n\n useEffect(() => {\n if (reduced) setTxt(list[wi % list.length]);\n }, [wi, reduced]);\n\n const atRest = reduced || (!deleting && txt === list[wi % list.length]);\n const verbCls = [\"ax-rtag__verb\", gradient ? \"ax-rtag__grad\" : \"\"].filter(Boolean).join(\" \");\n\n return (\n <span\n className={[\"ax-rtag\", className].filter(Boolean).join(\" \")}\n style={{ \"--ax-rtag-flow\": flowDuration }}\n {...rest}\n >\n {prefix ? (\n <span className=\"ax-rtag__prefix\">\n {prefix}\n {breakAfterPrefix ? <br /> : null}\n </span>\n ) : null}\n <span className={verbCls}>{txt}</span>\n {cursor ? (\n <i className={\"ax-rtag__cursor\" + (atRest ? \" is-blink\" : \"\")} aria-hidden=\"true\"></i>\n ) : null}\n </span>\n );\n}\n"],"names":["t"],"mappings":";;AASA,MAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBpB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,aAAa,GAAG;AAC9E,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEO,SAAS,gBAAgB;AAAA,EAC9B,SAAS;AAAA,EACT,UAAU,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,EACzC,WAAW;AAAA,EACX,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,OAAO;AAAA,EACP,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,GAAG;AACL,GAAG;AACD,QAAM,OAAO,WAAW,QAAQ,SAAS,UAAU,CAAC,EAAE;AACtD,QAAM,UACJ,OAAO,WAAW,eAClB,OAAO,cACP,OAAO,WAAW,kCAAkC,EAAE;AACxD,QAAM,CAAC,IAAI,KAAK,IAAI,SAAS,CAAC;AAC9B,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,KAAK,CAAC,CAAC;AACtC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAE9C,YAAU,MAAM;AACd,QAAI,SAAS;AACX,YAAMA,KAAI,WAAW,MAAM,MAAM,CAAC,OAAO,IAAI,KAAK,KAAK,MAAM,GAAG,OAAO,GAAG;AAC1E,aAAO,MAAM,aAAaA,EAAC;AAAA,IAC7B;AACA,UAAM,OAAO,KAAK,KAAK,KAAK,MAAM;AAClC,QAAI;AACJ,QAAI,CAAC,YAAY,QAAQ,MAAM;AAC7B,UAAI,WAAW,MAAM,YAAY,IAAI,GAAG,IAAI;AAAA,IAC9C,WAAW,YAAY,QAAQ,IAAI;AACjC,kBAAY,KAAK;AACjB,YAAM,CAAC,OAAO,IAAI,KAAK,KAAK,MAAM;AAAA,IACpC,OAAO;AACL,YAAM,OAAO,WAAW,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC;AACpF,UAAI,WAAW,MAAM,OAAO,IAAI,GAAG,WAAW,cAAc,SAAS;AAAA,IACvE;AACA,WAAO,MAAM,aAAa,CAAC;AAAA,EAC7B,GAAG,CAAC,KAAK,UAAU,IAAI,SAAS,MAAM,WAAW,WAAW,CAAC;AAE7D,YAAU,MAAM;AACd,QAAI,QAAS,QAAO,KAAK,KAAK,KAAK,MAAM,CAAC;AAAA,EAC5C,GAAG,CAAC,IAAI,OAAO,CAAC;AAEhB,QAAM,SAAS,WAAY,CAAC,YAAY,QAAQ,KAAK,KAAK,KAAK,MAAM;AACrE,QAAM,UAAU,CAAC,iBAAiB,WAAW,kBAAkB,EAAE,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAE3F,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,CAAC,WAAW,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MAC1D,OAAO,EAAE,kBAAkB,aAAA;AAAA,MAC1B,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,SACC,qBAAC,QAAA,EAAK,WAAU,mBACb,UAAA;AAAA,UAAA;AAAA,UACA,mBAAmB,oBAAC,MAAA,CAAA,CAAG,IAAK;AAAA,QAAA,EAAA,CAC/B,IACE;AAAA,QACJ,oBAAC,QAAA,EAAK,WAAW,SAAU,UAAA,KAAI;AAAA,QAC9B,SACC,oBAAC,KAAA,EAAE,WAAW,qBAAqB,SAAS,cAAc,KAAK,eAAY,OAAA,CAAO,IAChF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGV;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -130,6 +130,7 @@ export * from "./components/utilities/BrandMark";
|
|
|
130
130
|
export * from "./components/utilities/Icon";
|
|
131
131
|
export * from "./components/utilities/Image";
|
|
132
132
|
export * from "./components/utilities/OpenInChat";
|
|
133
|
+
export * from "./components/utilities/RotatingTagline";
|
|
133
134
|
|
|
134
135
|
// voice
|
|
135
136
|
export * from "./components/voice/AudioPlayer";
|
package/dist/index.js
CHANGED
|
@@ -103,6 +103,7 @@ import { BrandMark } from "./components/utilities/BrandMark.js";
|
|
|
103
103
|
import { Icon } from "./components/utilities/Icon.js";
|
|
104
104
|
import { Image } from "./components/utilities/Image.js";
|
|
105
105
|
import { OpenInChat } from "./components/utilities/OpenInChat.js";
|
|
106
|
+
import { RotatingTagline } from "./components/utilities/RotatingTagline.js";
|
|
106
107
|
import { AudioPlayer } from "./components/voice/AudioPlayer.js";
|
|
107
108
|
import { MicSelector } from "./components/voice/MicSelector.js";
|
|
108
109
|
import { Persona } from "./components/voice/Persona.js";
|
|
@@ -199,6 +200,7 @@ export {
|
|
|
199
200
|
RadioGroup,
|
|
200
201
|
Reasoning,
|
|
201
202
|
Resizable,
|
|
203
|
+
RotatingTagline,
|
|
202
204
|
Sandbox,
|
|
203
205
|
SchemaDisplay,
|
|
204
206
|
ScrollArea,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentaily/design-system",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Agentaily design system — light-first monochrome React component library (
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Agentaily design system — light-first monochrome React component library (113 components) + Storybook.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"author": "Agentaily",
|