@agentaily/design-system 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +3 -3
- package/README.md +2 -2
- package/dist/components/auth/SignInPage.d.ts +42 -4
- package/dist/components/auth/SignInPage.js +84 -40
- package/dist/components/auth/SignInPage.js.map +1 -1
- package/dist/components/settings/IntegrationSettings.d.ts +73 -4
- package/dist/components/settings/IntegrationSettings.js +199 -141
- package/dist/components/settings/IntegrationSettings.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 一次 |
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* are shared; only fields, copy, and footer differ. The built-in footer link
|
|
5
5
|
* flips mode and fires onModeChange (wire it to your router to swap the URL).
|
|
6
6
|
* Light built-in validation (email format, password length, confirm match).
|
|
7
|
-
*
|
|
7
|
+
* Pass `error` to surface a backend error above the submit button and
|
|
8
|
+
* `submitting` to drive the async busy state. For a modal instead, use AuthDialog.
|
|
9
|
+
* Brand panel hides < 860px.
|
|
8
10
|
*/
|
|
9
11
|
export interface SignInPageCopy {
|
|
10
12
|
title?: string;
|
|
@@ -15,6 +17,21 @@ export interface SignInPageCopy {
|
|
|
15
17
|
/** Footer link label that flips mode, e.g. "Create one" */
|
|
16
18
|
switchCta?: string;
|
|
17
19
|
}
|
|
20
|
+
/** Shared, non-mode-specific strings. All optional — merged over the English defaults. */
|
|
21
|
+
export interface SignInPageSharedCopy {
|
|
22
|
+
/** Field labels + chrome: { email, password, confirm, forgot, or, sso }. */
|
|
23
|
+
labels?: { email?: string; password?: string; confirm?: string; forgot?: string; or?: string; sso?: string };
|
|
24
|
+
/** Input placeholders: { email, password, passwordNew, confirm }. */
|
|
25
|
+
placeholders?: { email?: string; password?: string; passwordNew?: string; confirm?: string };
|
|
26
|
+
/** Validation messages: { emailRequired, emailInvalid, passwordRequired, passwordShort, confirmRequired, confirmMismatch }. */
|
|
27
|
+
errors?: {
|
|
28
|
+
emailRequired?: string; emailInvalid?: string;
|
|
29
|
+
passwordRequired?: string; passwordShort?: string;
|
|
30
|
+
confirmRequired?: string; confirmMismatch?: string;
|
|
31
|
+
};
|
|
32
|
+
/** Default signup terms line (string). Overridden by the `terms` prop if passed. */
|
|
33
|
+
terms?: React.ReactNode;
|
|
34
|
+
}
|
|
18
35
|
export interface SignInPageProps {
|
|
19
36
|
/** Controlled mode. Omit for uncontrolled (uses defaultMode). */
|
|
20
37
|
mode?: "signin" | "signup";
|
|
@@ -24,8 +41,14 @@ export interface SignInPageProps {
|
|
|
24
41
|
onModeChange?: (mode: "signin" | "signup") => void;
|
|
25
42
|
/** Brand-panel lockup. @default <BrandMark wordmark /> (blinking cursor) */
|
|
26
43
|
brand?: React.ReactNode;
|
|
27
|
-
/**
|
|
28
|
-
|
|
44
|
+
/**
|
|
45
|
+
* All user-facing strings, deep-merged over the English defaults. Per-mode
|
|
46
|
+
* copy under `signin`/`signup`; shared labels, placeholders, errors, and the
|
|
47
|
+
* terms line at the top level. Pass a full zh-CN object to localize.
|
|
48
|
+
*/
|
|
49
|
+
copy?: { signin?: SignInPageCopy; signup?: SignInPageCopy } & SignInPageSharedCopy;
|
|
50
|
+
/** Props forwarded to the brand-panel RotatingTagline (prefix, phrases, flowDuration…). @default { breakAfterPrefix: true } */
|
|
51
|
+
tagline?: Record<string, unknown>;
|
|
29
52
|
email?: string;
|
|
30
53
|
password?: string;
|
|
31
54
|
onEmailChange?: (value: string) => void;
|
|
@@ -34,7 +57,7 @@ export interface SignInPageProps {
|
|
|
34
57
|
onSubmit?: (values: { mode: "signin" | "signup"; email: string; password: string }) => void;
|
|
35
58
|
/** Show + handle the "Forgot?" link (signin mode only). */
|
|
36
59
|
onForgot?: () => void;
|
|
37
|
-
/** @default "Continue with SSO" */
|
|
60
|
+
/** SSO button label. Overrides copy.labels.sso. @default "Continue with SSO" */
|
|
38
61
|
ssoLabel?: string;
|
|
39
62
|
/** Show + handle the SSO button (hidden when omitted). */
|
|
40
63
|
onSSO?: () => void;
|
|
@@ -44,5 +67,20 @@ export interface SignInPageProps {
|
|
|
44
67
|
footer?: React.ReactNode;
|
|
45
68
|
/** Show the left brand panel. @default true */
|
|
46
69
|
showBrandPanel?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Server-side error to show in a danger banner directly above the submit
|
|
72
|
+
* button (e.g. 409 email taken, 401 bad credentials, 400 weak password).
|
|
73
|
+
* Omit/falsy = nothing shown. The component owns its CLIENT validation errors
|
|
74
|
+
* and clears them on input/mode change; this backend error is caller-owned —
|
|
75
|
+
* clear it yourself (typically inside onSubmit before the request, and on
|
|
76
|
+
* onModeChange / onEmailChange / onPasswordChange).
|
|
77
|
+
*/
|
|
78
|
+
error?: React.ReactNode;
|
|
79
|
+
/**
|
|
80
|
+
* Submit-in-flight flag for an async onSubmit. When true the submit button is
|
|
81
|
+
* disabled and shows a spinner, and re-submits are blocked — prevents double
|
|
82
|
+
* submission. @default false
|
|
83
|
+
*/
|
|
84
|
+
submitting?: boolean;
|
|
47
85
|
}
|
|
48
86
|
export declare function SignInPage(props: SignInPageProps): JSX.Element;
|
|
@@ -1,20 +1,24 @@
|
|
|
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";
|
|
8
|
+
import { Spinner } from "../feedback/Spinner.js";
|
|
7
9
|
const AX_SIGNIN_CSS = `
|
|
8
10
|
.ax-signin { display: grid; grid-template-columns: 1fr 1fr; height: 100%; background: var(--surface-page); }
|
|
9
11
|
.ax-signin__brand { position: relative; border-right: 1px solid var(--border-default); background: var(--surface-panel);
|
|
10
12
|
background-image: var(--dot-grid); background-size: 24px 24px; padding: 40px; display: flex; flex-direction: column; }
|
|
11
13
|
.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-
|
|
14
|
+
.ax-signin__mid { margin-top: auto; margin-bottom: auto; }
|
|
15
|
+
.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
16
|
.ax-signin__by { margin: 16px 0 0; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; color: var(--text-faint); }
|
|
15
17
|
.ax-signin__form { display: flex; align-items: center; justify-content: center; padding: 40px; }
|
|
16
18
|
.ax-signin__card { width: 100%; max-width: 360px; }
|
|
17
19
|
.ax-signin__mbrand { display: none; }
|
|
20
|
+
.ax-signin__mtag { display: none; }
|
|
21
|
+
.ax-signin__mtag .ax-signin__quote { font-size: var(--text-3xl); }
|
|
18
22
|
.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
23
|
.ax-signin__sub { margin: 8px 0 28px; font-size: var(--text-sm); color: var(--text-muted); }
|
|
20
24
|
.ax-signin__fields { display: flex; flex-direction: column; gap: 16px; }
|
|
@@ -22,6 +26,9 @@ const AX_SIGNIN_CSS = `
|
|
|
22
26
|
.ax-signin__link { font-size: 12px; color: var(--text-muted); text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit; }
|
|
23
27
|
.ax-signin__link:hover { color: var(--text-body); }
|
|
24
28
|
.ax-signin__submit { margin-top: 22px; }
|
|
29
|
+
.ax-signin__error { display: flex; align-items: flex-start; gap: 9px; margin-top: 22px; padding: 10px 12px; border: 1px solid var(--danger); border-radius: var(--radius-2); background: var(--danger-dim); color: var(--danger); font-size: var(--text-sm); line-height: var(--leading-snug); }
|
|
30
|
+
.ax-signin__error svg { flex: none; margin-top: 1px; }
|
|
31
|
+
.ax-signin__submit .ax-spinner { border-top-color: var(--accent-fg); }
|
|
25
32
|
.ax-signin__or { display: flex; align-items: center; gap: 12px; margin: 22px 0; }
|
|
26
33
|
.ax-signin__or::before, .ax-signin__or::after { content: ""; flex: 1; height: 1px; background: var(--border-default); }
|
|
27
34
|
.ax-signin__or span { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--text-faint); }
|
|
@@ -34,7 +41,7 @@ const AX_SIGNIN_CSS = `
|
|
|
34
41
|
.ax-signin__footlink { color: var(--text-body); background: none; border: none; cursor: pointer; font-family: inherit; font-size: var(--text-sm);
|
|
35
42
|
border-bottom: 1px solid var(--border-strong); padding: 0 0 1px; transition: border-color var(--dur-1) var(--ease-out); }
|
|
36
43
|
.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:
|
|
44
|
+
@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
45
|
`;
|
|
39
46
|
if (typeof document !== "undefined" && !document.getElementById("ax-signin-css")) {
|
|
40
47
|
const s = document.createElement("style");
|
|
@@ -56,7 +63,30 @@ const DEFAULT_COPY = {
|
|
|
56
63
|
submit: "Create account",
|
|
57
64
|
switchText: "Already have an account?",
|
|
58
65
|
switchCta: "Sign in"
|
|
59
|
-
}
|
|
66
|
+
},
|
|
67
|
+
labels: {
|
|
68
|
+
email: "EMAIL",
|
|
69
|
+
password: "PASSWORD",
|
|
70
|
+
confirm: "CONFIRM PASSWORD",
|
|
71
|
+
forgot: "Forgot?",
|
|
72
|
+
or: "OR",
|
|
73
|
+
sso: "Continue with SSO"
|
|
74
|
+
},
|
|
75
|
+
placeholders: {
|
|
76
|
+
email: "you@example.com",
|
|
77
|
+
password: "••••••••",
|
|
78
|
+
passwordNew: "At least 8 characters",
|
|
79
|
+
confirm: "Re-enter your password"
|
|
80
|
+
},
|
|
81
|
+
errors: {
|
|
82
|
+
emailRequired: "Enter your email",
|
|
83
|
+
emailInvalid: "That email doesn’t look right",
|
|
84
|
+
passwordRequired: "Enter your password",
|
|
85
|
+
passwordShort: "Password must be at least 8 characters",
|
|
86
|
+
confirmRequired: "Re-enter your password",
|
|
87
|
+
confirmMismatch: "Passwords don’t match"
|
|
88
|
+
},
|
|
89
|
+
terms: "By creating an account you agree to our Terms and Privacy Policy."
|
|
60
90
|
};
|
|
61
91
|
function SignInPage({
|
|
62
92
|
mode,
|
|
@@ -64,22 +94,30 @@ function SignInPage({
|
|
|
64
94
|
onModeChange,
|
|
65
95
|
brand,
|
|
66
96
|
copy,
|
|
97
|
+
tagline,
|
|
67
98
|
email,
|
|
68
99
|
password,
|
|
69
100
|
onEmailChange,
|
|
70
101
|
onPasswordChange,
|
|
71
102
|
onSubmit,
|
|
72
103
|
onForgot,
|
|
73
|
-
ssoLabel
|
|
104
|
+
ssoLabel,
|
|
74
105
|
onSSO,
|
|
75
106
|
terms,
|
|
76
107
|
footer,
|
|
77
|
-
showBrandPanel = true
|
|
108
|
+
showBrandPanel = true,
|
|
109
|
+
error,
|
|
110
|
+
submitting = false
|
|
78
111
|
}) {
|
|
79
112
|
const [modeI, setModeI] = useState(defaultMode);
|
|
80
113
|
const m = mode !== void 0 ? mode : modeI;
|
|
81
114
|
const isSignup = m === "signup";
|
|
82
115
|
const c = { ...DEFAULT_COPY[m], ...copy && copy[m] };
|
|
116
|
+
const labels = { ...DEFAULT_COPY.labels, ...copy && copy.labels };
|
|
117
|
+
const ph = { ...DEFAULT_COPY.placeholders, ...copy && copy.placeholders };
|
|
118
|
+
const err = { ...DEFAULT_COPY.errors, ...copy && copy.errors };
|
|
119
|
+
const ssoText = ssoLabel !== void 0 ? ssoLabel : labels.sso;
|
|
120
|
+
const taglineProps = { breakAfterPrefix: true, ...tagline };
|
|
83
121
|
const [emailI, setEmailI] = useState("");
|
|
84
122
|
const [pwI, setPwI] = useState("");
|
|
85
123
|
const [confirm, setConfirm] = useState("");
|
|
@@ -110,55 +148,46 @@ function SignInPage({
|
|
|
110
148
|
const validate = () => {
|
|
111
149
|
const e = {};
|
|
112
150
|
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 =
|
|
151
|
+
if (!em) e.email = err.emailRequired;
|
|
152
|
+
else if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(em)) e.email = err.emailInvalid;
|
|
153
|
+
if (!pwV) e.password = err.passwordRequired;
|
|
154
|
+
else if (isSignup && pwV.length < 8) e.password = err.passwordShort;
|
|
117
155
|
if (isSignup) {
|
|
118
|
-
if (!confirm) e.confirm =
|
|
119
|
-
else if (confirm !== pwV) e.confirm =
|
|
156
|
+
if (!confirm) e.confirm = err.confirmRequired;
|
|
157
|
+
else if (confirm !== pwV) e.confirm = err.confirmMismatch;
|
|
120
158
|
}
|
|
121
159
|
return e;
|
|
122
160
|
};
|
|
123
161
|
const submit = (ev) => {
|
|
124
162
|
ev.preventDefault();
|
|
163
|
+
if (submitting) return;
|
|
125
164
|
const e = validate();
|
|
126
165
|
setErrs(e);
|
|
127
166
|
if (Object.keys(e).some((k) => e[k])) return;
|
|
128
167
|
onSubmit && onSubmit({ mode: m, email: (emailV || "").trim(), password: pwV });
|
|
129
168
|
};
|
|
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
|
-
] });
|
|
169
|
+
const termsNode = terms !== void 0 ? terms : c.terms !== void 0 ? c.terms : DEFAULT_COPY.terms;
|
|
138
170
|
return /* @__PURE__ */ jsxs("div", { className: "ax-signin", children: [
|
|
139
171
|
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 }) }),
|
|
172
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__brandtop", children: brand || /* @__PURE__ */ jsx(BrandMark, { size: 24, wordmark: true, blink: false }) }),
|
|
141
173
|
/* @__PURE__ */ jsxs("div", { className: "ax-signin__mid", children: [
|
|
142
|
-
/* @__PURE__ */
|
|
143
|
-
"聊天,",
|
|
144
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
145
|
-
"构建万物"
|
|
146
|
-
] }),
|
|
174
|
+
/* @__PURE__ */ jsx("p", { className: "ax-signin__quote", children: /* @__PURE__ */ jsx(RotatingTagline, { ...taglineProps }) }),
|
|
147
175
|
/* @__PURE__ */ jsx("p", { className: "ax-signin__by", children: "— AGENTAILY" })
|
|
148
176
|
] })
|
|
149
177
|
] }) : null,
|
|
150
178
|
/* @__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 }) }),
|
|
179
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__mbrand", children: brand || /* @__PURE__ */ jsx(BrandMark, { size: 22, wordmark: true, blink: false }) }),
|
|
180
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__mtag", children: /* @__PURE__ */ jsx("p", { className: "ax-signin__quote", children: /* @__PURE__ */ jsx(RotatingTagline, { ...taglineProps }) }) }),
|
|
152
181
|
/* @__PURE__ */ jsx("h1", { className: "ax-signin__h", children: c.title }),
|
|
153
182
|
/* @__PURE__ */ jsx("p", { className: "ax-signin__sub", children: c.subtitle }),
|
|
154
183
|
/* @__PURE__ */ jsxs("div", { className: "ax-signin__fields", children: [
|
|
155
184
|
/* @__PURE__ */ jsx(
|
|
156
185
|
Input,
|
|
157
186
|
{
|
|
158
|
-
label:
|
|
187
|
+
label: labels.email,
|
|
159
188
|
type: "email",
|
|
160
189
|
autoComplete: "email",
|
|
161
|
-
placeholder:
|
|
190
|
+
placeholder: ph.email,
|
|
162
191
|
value: emailV,
|
|
163
192
|
error: errs.email,
|
|
164
193
|
onChange: (e) => setEmail(e.target.value)
|
|
@@ -166,15 +195,15 @@ function SignInPage({
|
|
|
166
195
|
),
|
|
167
196
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
168
197
|
/* @__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:
|
|
198
|
+
/* @__PURE__ */ jsx("span", { className: "ax-label", children: labels.password }),
|
|
199
|
+
!isSignup && onForgot ? /* @__PURE__ */ jsx("button", { type: "button", className: "ax-signin__link", onClick: onForgot, children: labels.forgot }) : null
|
|
171
200
|
] }),
|
|
172
201
|
/* @__PURE__ */ jsx(
|
|
173
202
|
Input,
|
|
174
203
|
{
|
|
175
204
|
type: "password",
|
|
176
205
|
autoComplete: isSignup ? "new-password" : "current-password",
|
|
177
|
-
placeholder: isSignup ?
|
|
206
|
+
placeholder: isSignup ? ph.passwordNew : ph.password,
|
|
178
207
|
value: pwV,
|
|
179
208
|
error: errs.password,
|
|
180
209
|
onChange: (e) => setPw(e.target.value)
|
|
@@ -184,23 +213,38 @@ function SignInPage({
|
|
|
184
213
|
isSignup ? /* @__PURE__ */ jsx(
|
|
185
214
|
Input,
|
|
186
215
|
{
|
|
187
|
-
label:
|
|
216
|
+
label: labels.confirm,
|
|
188
217
|
type: "password",
|
|
189
218
|
autoComplete: "new-password",
|
|
190
|
-
placeholder:
|
|
219
|
+
placeholder: ph.confirm,
|
|
191
220
|
value: confirm,
|
|
192
221
|
error: errs.confirm,
|
|
193
222
|
onChange: (e) => setConf(e.target.value)
|
|
194
223
|
}
|
|
195
224
|
) : null
|
|
196
225
|
] }),
|
|
197
|
-
/* @__PURE__ */
|
|
226
|
+
error ? /* @__PURE__ */ jsxs("div", { className: "ax-signin__error", role: "alert", children: [
|
|
227
|
+
/* @__PURE__ */ jsx(Icon, { name: "warn", size: 15 }),
|
|
228
|
+
/* @__PURE__ */ jsx("span", { children: error })
|
|
229
|
+
] }) : null,
|
|
230
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__submit", children: /* @__PURE__ */ jsx(
|
|
231
|
+
Button,
|
|
232
|
+
{
|
|
233
|
+
variant: "primary",
|
|
234
|
+
full: true,
|
|
235
|
+
type: "submit",
|
|
236
|
+
disabled: submitting,
|
|
237
|
+
"aria-busy": submitting || void 0,
|
|
238
|
+
icon: submitting ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : /* @__PURE__ */ jsx(Icon, { name: "arrow", size: 15 }),
|
|
239
|
+
children: c.submit
|
|
240
|
+
}
|
|
241
|
+
) }),
|
|
198
242
|
isSignup ? /* @__PURE__ */ jsx("p", { className: "ax-signin__terms", children: termsNode }) : null,
|
|
199
243
|
onSSO ? /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
200
|
-
/* @__PURE__ */ jsx("div", { className: "ax-signin__or", children: /* @__PURE__ */ jsx("span", { children:
|
|
244
|
+
/* @__PURE__ */ jsx("div", { className: "ax-signin__or", children: /* @__PURE__ */ jsx("span", { children: labels.or }) }),
|
|
201
245
|
/* @__PURE__ */ jsxs("button", { type: "button", className: "ax-signin__sso", onClick: onSSO, children: [
|
|
202
246
|
/* @__PURE__ */ jsx(Icon, { name: "shield", size: 15 }),
|
|
203
|
-
|
|
247
|
+
ssoText
|
|
204
248
|
] })
|
|
205
249
|
] }) : null,
|
|
206
250
|
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\";\nimport { Spinner } from \"../feedback/Spinner.jsx\";\n\n// SignInPage — full-page auth: a split brand panel (dot-grid + quote) beside a\n// centered card. One component, two modes (signin / signup) — the brand panel,\n// SSO, and card chrome are shared; only the fields, copy, and footer differ.\n// The footer link flips mode (and fires onModeChange so a router can swap the\n// URL). The full-page sibling of AuthDialog (the modal); same login/register idea.\nconst AX_SIGNIN_CSS = `\n.ax-signin { display: grid; grid-template-columns: 1fr 1fr; height: 100%; background: var(--surface-page); }\n.ax-signin__brand { position: relative; border-right: 1px solid var(--border-default); background: var(--surface-panel);\n background-image: var(--dot-grid); background-size: 24px 24px; padding: 40px; display: flex; flex-direction: column; }\n.ax-signin__brandtop { display: flex; align-items: center; gap: 10px; }\n.ax-signin__mid { margin-top: auto; margin-bottom: auto; }\n.ax-signin__quote { font-family: var(--font-display); white-space: nowrap; font-size: clamp(40px, 4.4vw, var(--text-hero)); font-weight: var(--weight-medium); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--text-body); margin: 0; }\n.ax-signin__by { margin: 16px 0 0; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; color: var(--text-faint); }\n.ax-signin__form { display: flex; align-items: center; justify-content: center; padding: 40px; }\n.ax-signin__card { width: 100%; max-width: 360px; }\n.ax-signin__mbrand { display: none; }\n.ax-signin__mtag { display: none; }\n.ax-signin__mtag .ax-signin__quote { font-size: var(--text-3xl); }\n.ax-signin__h { margin: 0; font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); color: var(--text-body); }\n.ax-signin__sub { margin: 8px 0 28px; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__fields { display: flex; flex-direction: column; gap: 16px; }\n.ax-signin__row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }\n.ax-signin__link { font-size: 12px; color: var(--text-muted); text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit; }\n.ax-signin__link:hover { color: var(--text-body); }\n.ax-signin__submit { margin-top: 22px; }\n.ax-signin__error { display: flex; align-items: flex-start; gap: 9px; margin-top: 22px; padding: 10px 12px; border: 1px solid var(--danger); border-radius: var(--radius-2); background: var(--danger-dim); color: var(--danger); font-size: var(--text-sm); line-height: var(--leading-snug); }\n.ax-signin__error svg { flex: none; margin-top: 1px; }\n.ax-signin__submit .ax-spinner { border-top-color: var(--accent-fg); }\n.ax-signin__or { display: flex; align-items: center; gap: 12px; margin: 22px 0; }\n.ax-signin__or::before, .ax-signin__or::after { content: \"\"; flex: 1; height: 1px; background: var(--border-default); }\n.ax-signin__or span { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--text-faint); }\n.ax-signin__sso { display: flex; align-items: center; justify-content: center; gap: 9px; width: 100%; height: 40px; border: 1px solid var(--border-default);\n border-radius: var(--radius-2); background: var(--surface-card); color: var(--text-body); font-family: inherit; font-size: var(--text-sm); cursor: pointer; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__sso:hover { border-color: var(--border-strong); }\n.ax-signin__terms { margin: 16px 0 0; font-size: var(--text-xs); color: var(--text-faint); line-height: var(--leading-snug); text-align: center; }\n.ax-signin__terms a, .ax-signin__terms span.lk { color: var(--text-muted); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }\n.ax-signin__foot { margin-top: 26px; text-align: center; font-size: var(--text-sm); color: var(--text-muted); }\n.ax-signin__footlink { color: var(--text-body); background: none; border: none; cursor: pointer; font-family: inherit; font-size: var(--text-sm);\n border-bottom: 1px solid var(--border-strong); padding: 0 0 1px; transition: border-color var(--dur-1) var(--ease-out); }\n.ax-signin__footlink:hover { border-color: var(--text-faint); }\n@media (max-width: 860px) { .ax-signin { grid-template-columns: 1fr; } .ax-signin__brand { display: none; } .ax-signin__form { padding: 56px 40px; } .ax-signin__mbrand { display: flex; margin-bottom: 28px; } .ax-signin__mtag { display: block; margin-bottom: 36px; } .ax-signin__h { font-family: var(--font-mono); font-size: var(--text-xs); font-weight: var(--weight-medium); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-body); margin: 0 0 8px; } .ax-signin__sub { margin: 0 0 32px; color: var(--text-muted); } .ax-signin__fields { gap: 22px; } .ax-signin__submit { margin-top: 32px; } .ax-signin__or { margin: 32px 0; } .ax-signin__foot { margin-top: 28px; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-signin-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-signin-css\";\n s.textContent = AX_SIGNIN_CSS;\n document.head.appendChild(s);\n}\n\n// Every user-facing string lives here so nothing is hardcoded in the markup.\n// Defaults are a self-consistent English baseline; pass a `copy` prop (any\n// subset, deep-merged per group) to localize — e.g. a full zh-CN object.\nconst DEFAULT_COPY = {\n signin: {\n title: \"Sign in\",\n subtitle: \"Welcome back. Use your work email to continue.\",\n submit: \"Continue\",\n switchText: \"No account?\",\n switchCta: \"Create one\",\n },\n signup: {\n title: \"Create account\",\n subtitle: \"Start with your work email — it takes a minute.\",\n submit: \"Create account\",\n switchText: \"Already have an account?\",\n switchCta: \"Sign in\",\n },\n labels: {\n email: \"EMAIL\",\n password: \"PASSWORD\",\n confirm: \"CONFIRM PASSWORD\",\n forgot: \"Forgot?\",\n or: \"OR\",\n sso: \"Continue with SSO\",\n },\n placeholders: {\n email: \"you@example.com\",\n password: \"••••••••\",\n passwordNew: \"At least 8 characters\",\n confirm: \"Re-enter your password\",\n },\n errors: {\n emailRequired: \"Enter your email\",\n emailInvalid: \"That email doesn’t look right\",\n passwordRequired: \"Enter your password\",\n passwordShort: \"Password must be at least 8 characters\",\n confirmRequired: \"Re-enter your password\",\n confirmMismatch: \"Passwords don’t match\",\n },\n terms: \"By creating an account you agree to our Terms and Privacy Policy.\",\n};\n\nexport function SignInPage({\n mode,\n defaultMode = \"signin\",\n onModeChange,\n brand,\n copy,\n tagline,\n email,\n password,\n onEmailChange,\n onPasswordChange,\n onSubmit,\n onForgot,\n ssoLabel,\n onSSO,\n terms,\n footer,\n showBrandPanel = true,\n error,\n submitting = false,\n}) {\n const [modeI, setModeI] = useState(defaultMode);\n const m = mode !== undefined ? mode : modeI;\n const isSignup = m === \"signup\";\n const c = { ...DEFAULT_COPY[m], ...(copy && copy[m]) };\n const labels = { ...DEFAULT_COPY.labels, ...(copy && copy.labels) };\n const ph = { ...DEFAULT_COPY.placeholders, ...(copy && copy.placeholders) };\n const err = { ...DEFAULT_COPY.errors, ...(copy && copy.errors) };\n const ssoText = ssoLabel !== undefined ? ssoLabel : labels.sso;\n const taglineProps = { breakAfterPrefix: true, ...tagline };\n\n const [emailI, setEmailI] = useState(\"\");\n const [pwI, setPwI] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [errs, setErrs] = useState({});\n const emailV = email !== undefined ? email : emailI;\n const pwV = password !== undefined ? password : pwI;\n const setEmail = (v) => {\n if (onEmailChange) onEmailChange(v);\n else setEmailI(v);\n if (errs.email) setErrs((e) => ({ ...e, email: undefined }));\n };\n const setPw = (v) => {\n if (onPasswordChange) onPasswordChange(v);\n else setPwI(v);\n if (errs.password) setErrs((e) => ({ ...e, password: undefined }));\n };\n const setConf = (v) => {\n setConfirm(v);\n if (errs.confirm) setErrs((e) => ({ ...e, confirm: undefined }));\n };\n\n const switchMode = () => {\n const next = isSignup ? \"signin\" : \"signup\";\n setErrs({});\n setConfirm(\"\");\n if (onModeChange) onModeChange(next);\n if (mode === undefined) setModeI(next);\n };\n\n const validate = () => {\n const e = {};\n const em = (emailV || \"\").trim();\n if (!em) e.email = err.emailRequired;\n else if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(em)) e.email = err.emailInvalid;\n if (!pwV) e.password = err.passwordRequired;\n else if (isSignup && pwV.length < 8) e.password = err.passwordShort;\n if (isSignup) {\n if (!confirm) e.confirm = err.confirmRequired;\n else if (confirm !== pwV) e.confirm = err.confirmMismatch;\n }\n return e;\n };\n\n const submit = (ev) => {\n ev.preventDefault();\n if (submitting) return;\n const e = validate();\n setErrs(e);\n if (Object.keys(e).some((k) => e[k])) return;\n onSubmit && onSubmit({ mode: m, email: (emailV || \"\").trim(), password: pwV });\n };\n\n const termsNode =\n terms !== undefined ? terms : c.terms !== undefined ? c.terms : DEFAULT_COPY.terms;\n\n return (\n <div className=\"ax-signin\">\n {showBrandPanel ? (\n <aside className=\"ax-signin__brand\">\n <div className=\"ax-signin__brandtop\">\n {brand || <BrandMark size={24} wordmark blink={false} />}\n </div>\n <div className=\"ax-signin__mid\">\n <p className=\"ax-signin__quote\">\n <RotatingTagline {...taglineProps} />\n </p>\n <p className=\"ax-signin__by\">— AGENTAILY</p>\n </div>\n </aside>\n ) : null}\n\n <main className=\"ax-signin__form\">\n <form className=\"ax-signin__card\" onSubmit={submit} noValidate>\n <div className=\"ax-signin__mbrand\">\n {brand || <BrandMark size={22} wordmark blink={false} />}\n </div>\n <div className=\"ax-signin__mtag\">\n <p className=\"ax-signin__quote\">\n <RotatingTagline {...taglineProps} />\n </p>\n </div>\n <h1 className=\"ax-signin__h\">{c.title}</h1>\n <p className=\"ax-signin__sub\">{c.subtitle}</p>\n\n <div className=\"ax-signin__fields\">\n <Input\n label={labels.email}\n type=\"email\"\n autoComplete=\"email\"\n placeholder={ph.email}\n value={emailV}\n error={errs.email}\n onChange={(e) => setEmail(e.target.value)}\n />\n <div>\n <div className=\"ax-signin__row\">\n <span className=\"ax-label\">{labels.password}</span>\n {!isSignup && onForgot ? (\n <button type=\"button\" className=\"ax-signin__link\" onClick={onForgot}>\n {labels.forgot}\n </button>\n ) : null}\n </div>\n <Input\n type=\"password\"\n autoComplete={isSignup ? \"new-password\" : \"current-password\"}\n placeholder={isSignup ? ph.passwordNew : ph.password}\n value={pwV}\n error={errs.password}\n onChange={(e) => setPw(e.target.value)}\n />\n </div>\n {isSignup ? (\n <Input\n label={labels.confirm}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={ph.confirm}\n value={confirm}\n error={errs.confirm}\n onChange={(e) => setConf(e.target.value)}\n />\n ) : null}\n </div>\n\n {error ? (\n <div className=\"ax-signin__error\" role=\"alert\">\n <Icon name=\"warn\" size={15} />\n <span>{error}</span>\n </div>\n ) : null}\n\n <div className=\"ax-signin__submit\">\n <Button\n variant=\"primary\"\n full\n type=\"submit\"\n disabled={submitting}\n aria-busy={submitting || undefined}\n icon={submitting ? <Spinner size=\"sm\" /> : <Icon name=\"arrow\" size={15} />}\n >\n {c.submit}\n </Button>\n </div>\n\n {isSignup ? <p className=\"ax-signin__terms\">{termsNode}</p> : null}\n\n {onSSO ? (\n <React.Fragment>\n <div className=\"ax-signin__or\">\n <span>{labels.or}</span>\n </div>\n <button type=\"button\" className=\"ax-signin__sso\" onClick={onSSO}>\n <Icon name=\"shield\" size={15} />\n {ssoText}\n </button>\n </React.Fragment>\n ) : null}\n\n {footer !== undefined ? (\n footer ? (\n <p className=\"ax-signin__foot\">{footer}</p>\n ) : null\n ) : (\n <p className=\"ax-signin__foot\">\n {c.switchText}{\" \"}\n <button type=\"button\" className=\"ax-signin__footlink\" onClick={switchMode}>\n {c.switchCta}\n </button>\n </p>\n )}\n </form>\n </main>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,84 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fullscreen integration modal: connect a DeepSeek key (conversation engine) and
|
|
3
|
-
* a Feishu Bitable (data sink).
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* a Feishu Bitable (data sink). Reports a 0/2 readiness state and gates Save until
|
|
4
|
+
* both connect. Composes SecretField, StatusPill, TestRow, HelpSteps. The monthly
|
|
5
|
+
* usage-cap block is opt-in.
|
|
6
|
+
*
|
|
7
|
+
* Two storage modes:
|
|
8
|
+
* - **Uncontrolled (default):** self-persists the whole config to localStorage —
|
|
9
|
+
* the legacy behavior, unchanged when none of the seam props below are passed.
|
|
10
|
+
* - **Controlled / backend-wired:** pass `value` + `onChange` to hold the config
|
|
11
|
+
* yourself (no localStorage writes), `onSave`/`onTest` to reach a real backend,
|
|
12
|
+
* `readiness` to reflect server-known connection state, and `masked` so stored
|
|
13
|
+
* secrets echo masked and are never re-submitted.
|
|
6
14
|
*/
|
|
15
|
+
export interface IntegrationConfig {
|
|
16
|
+
/** DeepSeek API key. Empty when masked + untouched (caller keeps the stored one). */
|
|
17
|
+
dsKey: string;
|
|
18
|
+
/** Conversation model id, e.g. "deepseek-chat" | "deepseek-reasoner". */
|
|
19
|
+
dsModel: string;
|
|
20
|
+
/** Monthly usage-cap toggle. */
|
|
21
|
+
capOn: boolean;
|
|
22
|
+
/** Monthly cap amount (digits-only string). */
|
|
23
|
+
cap: string;
|
|
24
|
+
/** DeepSeek connection status. */
|
|
25
|
+
dsStatus: "idle" | "testing" | "ok" | "error";
|
|
26
|
+
/** DeepSeek test result line. */
|
|
27
|
+
dsResult: string;
|
|
28
|
+
/** Feishu app id (public). */
|
|
29
|
+
appId: string;
|
|
30
|
+
/** Feishu app secret. Empty when masked + untouched. */
|
|
31
|
+
secret: string;
|
|
32
|
+
/** Feishu Bitable share URL (app_token + table auto-parsed). */
|
|
33
|
+
link: string;
|
|
34
|
+
/** Feishu connection status. */
|
|
35
|
+
fsStatus: "idle" | "testing" | "ok" | "error";
|
|
36
|
+
/** Feishu test result line. */
|
|
37
|
+
fsResult: string;
|
|
38
|
+
/** Whether the last config was saved. */
|
|
39
|
+
saved: boolean;
|
|
40
|
+
}
|
|
7
41
|
export interface IntegrationSettingsProps {
|
|
8
42
|
/** Close handler (Esc + the × button + overlay). */
|
|
9
43
|
onClose?: () => void;
|
|
10
44
|
/** Show the DeepSeek monthly usage-cap warning + toggle. @default true */
|
|
11
45
|
showUsageCap?: boolean;
|
|
12
|
-
/** localStorage key for the persisted config. @default "agentaily.integrations.v1" */
|
|
46
|
+
/** localStorage key for the persisted config (uncontrolled mode only). @default "agentaily.integrations.v1" */
|
|
13
47
|
storageKey?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Controlled config. When provided, this is the source of truth — the modal
|
|
50
|
+
* stops reading/writing localStorage and every edit flows out via `onChange`.
|
|
51
|
+
* Omit for the self-persisting default.
|
|
52
|
+
*/
|
|
53
|
+
value?: IntegrationConfig;
|
|
54
|
+
/** Fires with the next full config on every edit/test (controlled mode). */
|
|
55
|
+
onChange?: (next: IntegrationConfig) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Persist handler. Receives the current config (masked secrets stay empty —
|
|
58
|
+
* treat empty as "keep the stored one"). Return a Promise: Save is disabled and
|
|
59
|
+
* spins while it's pending, marks saved on resolve, and stays dirty on reject.
|
|
60
|
+
* When omitted, Save just flips the in-component saved state.
|
|
61
|
+
*/
|
|
62
|
+
onSave?: (value: IntegrationConfig) => void | Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Connection probe. `which` is "deepseek" | "feishu"; return
|
|
65
|
+
* `Promise<{ ok, message }>` to drive that card's StatusPill + TestRow. When
|
|
66
|
+
* omitted, a built-in mock probe runs (validates key shape / link parsing).
|
|
67
|
+
*/
|
|
68
|
+
onTest?: (which: "deepseek" | "feishu") => Promise<{ ok: boolean; message?: string }>;
|
|
69
|
+
/**
|
|
70
|
+
* External readiness override. When set, these flags (not the in-app test
|
|
71
|
+
* status) decide the 0/2 rail, Save gating, and the green pills — e.g. show
|
|
72
|
+
* "connected" for credentials already verified server-side. Live testing/error
|
|
73
|
+
* transitions still surface on the pill.
|
|
74
|
+
*/
|
|
75
|
+
readiness?: { deepseek?: boolean; feishu?: boolean };
|
|
76
|
+
/**
|
|
77
|
+
* Masked-echo flags for already-stored secrets. When `deepseek`/`feishu` is
|
|
78
|
+
* true and the field is untouched, it renders a masked placeholder, keeps its
|
|
79
|
+
* value empty, and still counts as "present" for testing — so the mask is never
|
|
80
|
+
* sent back as a new value. Typing overrides the mask for that field.
|
|
81
|
+
*/
|
|
82
|
+
masked?: { deepseek?: boolean; feishu?: boolean };
|
|
14
83
|
}
|
|
15
84
|
export declare function IntegrationSettings(props: IntegrationSettingsProps): JSX.Element;
|