@guard-rail/core 0.10.0 → 0.11.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.
@@ -7,16 +7,13 @@ import { useGRail } from "./Provider";
7
7
  import { TeamDrawer } from "./Team";
8
8
  import { AuthDrawer } from "./AuthDrawer";
9
9
  import { CloseIcon, LogOutIcon, PeopleIcon } from "./Icons";
10
- import { S, THEME_CSS, v } from "./styles";
10
+ import "./guardrail.css";
11
11
  import { GR_COOKIES, GR_EDIT_MODE_OFF_TTL_SECONDS } from "../core/defs";
12
12
  const initialState = { email: "", password: "" };
13
13
  function GRailAdmin(props) {
14
14
  const { isEditMode } = useGRail();
15
15
  if (!isEditMode) return null;
16
- return /* @__PURE__ */ jsxs(Fragment, { children: [
17
- /* @__PURE__ */ jsx("style", { href: "guardrail-theme", precedence: "default", children: THEME_CSS }),
18
- /* @__PURE__ */ jsx(AdminBody, { ...props })
19
- ] });
16
+ return /* @__PURE__ */ jsx(AdminBody, { ...props });
20
17
  }
21
18
  function AdminBody({ toolbarExtra }) {
22
19
  const { isAuth, isEditMode, needsPassword, authError, role, auth } = useGRail();
@@ -60,17 +57,17 @@ function AdminBody({ toolbarExtra }) {
60
57
  if (!isAuth) {
61
58
  if (!auth.password) {
62
59
  return /* @__PURE__ */ jsxs(Fragment, { children: [
63
- /* @__PURE__ */ jsx("div", { "data-guardrail": true, style: S.banner, children: /* @__PURE__ */ jsxs("div", { style: S.bannerForm, children: [
64
- /* @__PURE__ */ jsxs("div", { style: S.brandStack, children: [
65
- /* @__PURE__ */ jsx("span", { style: S.brand, children: "Guardrail" }),
66
- !auth.signInUrl && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: v("muted") }, children: "No sign-in method is configured for this site." })
60
+ /* @__PURE__ */ jsx("div", { "data-guardrail": true, className: "gr-banner", children: /* @__PURE__ */ jsxs("div", { className: "gr-banner-form", children: [
61
+ /* @__PURE__ */ jsxs("div", { className: "gr-brand-stack", children: [
62
+ /* @__PURE__ */ jsx("span", { className: "gr-brand", children: "Guardrail" }),
63
+ !auth.signInUrl && /* @__PURE__ */ jsx("span", { className: "gr-fs-12 gr-text-muted", children: "No sign-in method is configured for this site." })
67
64
  ] }),
68
- auth.signInUrl && /* @__PURE__ */ jsx("a", { href: auth.signInUrl, style: { ...S.primaryBtn, textDecoration: "none" }, children: "Sign in" }),
65
+ auth.signInUrl && /* @__PURE__ */ jsx("a", { href: auth.signInUrl, className: "gr-primary-btn gr-no-underline", children: "Sign in" }),
69
66
  /* @__PURE__ */ jsx(
70
67
  "button",
71
68
  {
72
69
  type: "button",
73
- style: { ...S.iconBtn, color: v("muted") },
70
+ className: "gr-icon-btn gr-text-muted",
74
71
  onClick: exitEditMode,
75
72
  title: "Not editing? Dismiss",
76
73
  "aria-label": "Dismiss",
@@ -82,10 +79,10 @@ function AdminBody({ toolbarExtra }) {
82
79
  ] });
83
80
  }
84
81
  return /* @__PURE__ */ jsxs(Fragment, { children: [
85
- /* @__PURE__ */ jsx("div", { "data-guardrail": true, style: S.banner, children: /* @__PURE__ */ jsxs("form", { action: formAction, style: S.bannerForm, children: [
86
- /* @__PURE__ */ jsxs("div", { style: S.brandStack, children: [
87
- /* @__PURE__ */ jsx("span", { style: S.brand, children: "Guardrail" }),
88
- formState.error && /* @__PURE__ */ jsx("span", { style: S.error, children: formState.error })
82
+ /* @__PURE__ */ jsx("div", { "data-guardrail": true, className: "gr-banner", children: /* @__PURE__ */ jsxs("form", { action: formAction, className: "gr-banner-form", children: [
83
+ /* @__PURE__ */ jsxs("div", { className: "gr-brand-stack", children: [
84
+ /* @__PURE__ */ jsx("span", { className: "gr-brand", children: "Guardrail" }),
85
+ formState.error && /* @__PURE__ */ jsx("span", { className: "gr-error", children: formState.error })
89
86
  ] }),
90
87
  /* @__PURE__ */ jsx(
91
88
  "input",
@@ -96,7 +93,7 @@ function AdminBody({ toolbarExtra }) {
96
93
  placeholder: "Email",
97
94
  defaultValue: formState.email,
98
95
  required: true,
99
- style: S.input
96
+ className: "gr-input"
100
97
  }
101
98
  ),
102
99
  /* @__PURE__ */ jsx(
@@ -107,25 +104,16 @@ function AdminBody({ toolbarExtra }) {
107
104
  autoComplete: "current-password",
108
105
  placeholder: "Password",
109
106
  required: true,
110
- style: S.input
107
+ className: "gr-input"
111
108
  }
112
109
  ),
113
- /* @__PURE__ */ jsx("button", { type: "submit", disabled: isPending, style: S.primaryBtn, children: isPending ? "Authenticating\u2026" : "Sign In" }),
110
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isPending, className: "gr-primary-btn", children: isPending ? "Authenticating\u2026" : "Sign In" }),
114
111
  /* @__PURE__ */ jsx(
115
112
  "button",
116
113
  {
117
114
  type: "button",
118
115
  onClick: () => setForgotOpen(true),
119
- style: {
120
- background: "none",
121
- border: "none",
122
- cursor: "pointer",
123
- color: v("muted"),
124
- fontSize: 12,
125
- textDecoration: "underline",
126
- padding: "0 4px",
127
- whiteSpace: "nowrap"
128
- },
116
+ className: "gr-text-btn",
129
117
  children: "Forgot password?"
130
118
  }
131
119
  ),
@@ -133,7 +121,7 @@ function AdminBody({ toolbarExtra }) {
133
121
  "button",
134
122
  {
135
123
  type: "button",
136
- style: { ...S.iconBtn, color: v("muted") },
124
+ className: "gr-icon-btn gr-text-muted",
137
125
  onClick: exitEditMode,
138
126
  title: "Not editing? Dismiss",
139
127
  "aria-label": "Dismiss",
@@ -147,13 +135,13 @@ function AdminBody({ toolbarExtra }) {
147
135
  if (needsPassword) return /* @__PURE__ */ jsx(AuthDrawer, { mode: "set-password" });
148
136
  if (!role) {
149
137
  return /* @__PURE__ */ jsxs(Fragment, { children: [
150
- /* @__PURE__ */ jsxs("div", { "data-guardrail": true, style: S.toolbar, children: [
151
- /* @__PURE__ */ jsx("span", { style: S.toolbarLabel, children: "No access to this site" }),
152
- /* @__PURE__ */ jsx("span", { style: S.divider }),
138
+ /* @__PURE__ */ jsxs("div", { "data-guardrail": true, className: "gr-toolbar", children: [
139
+ /* @__PURE__ */ jsx("span", { className: "gr-toolbar-label", children: "No access to this site" }),
140
+ /* @__PURE__ */ jsx("span", { className: "gr-divider" }),
153
141
  /* @__PURE__ */ jsx(
154
142
  "button",
155
143
  {
156
- style: { ...S.iconBtn, color: v("muted") },
144
+ className: "gr-icon-btn gr-text-muted",
157
145
  onClick: signOut,
158
146
  title: "Sign out",
159
147
  "aria-label": "Sign out",
@@ -167,14 +155,14 @@ function AdminBody({ toolbarExtra }) {
167
155
  return /* @__PURE__ */ jsxs(Fragment, { children: [
168
156
  linkErrorDrawer,
169
157
  teamOpen && /* @__PURE__ */ jsx(TeamDrawer, { onClose: () => setTeamOpen(false) }),
170
- /* @__PURE__ */ jsxs("div", { "data-guardrail": true, style: S.toolbar, children: [
171
- /* @__PURE__ */ jsx("span", { style: S.dot }),
172
- /* @__PURE__ */ jsx("span", { style: S.toolbarLabel, children: "Edit Mode" }),
173
- /* @__PURE__ */ jsx("span", { style: S.divider }),
158
+ /* @__PURE__ */ jsxs("div", { "data-guardrail": true, className: "gr-toolbar", children: [
159
+ /* @__PURE__ */ jsx("span", { className: "gr-dot" }),
160
+ /* @__PURE__ */ jsx("span", { className: "gr-toolbar-label", children: "Edit Mode" }),
161
+ /* @__PURE__ */ jsx("span", { className: "gr-divider" }),
174
162
  /* @__PURE__ */ jsx(
175
163
  "button",
176
164
  {
177
- style: { ...S.iconBtn, color: v("muted"), width: 24, height: 24 },
165
+ className: "gr-icon-btn gr-text-muted gr-size-24",
178
166
  onClick: () => setTeamOpen(true),
179
167
  title: "Team",
180
168
  "aria-label": "Team",
@@ -182,14 +170,14 @@ function AdminBody({ toolbarExtra }) {
182
170
  }
183
171
  ),
184
172
  toolbarExtra && /* @__PURE__ */ jsxs(Fragment, { children: [
185
- /* @__PURE__ */ jsx("span", { style: S.divider }),
173
+ /* @__PURE__ */ jsx("span", { className: "gr-divider" }),
186
174
  toolbarExtra
187
175
  ] }),
188
- /* @__PURE__ */ jsx("span", { style: S.divider }),
176
+ /* @__PURE__ */ jsx("span", { className: "gr-divider" }),
189
177
  /* @__PURE__ */ jsx(
190
178
  "button",
191
179
  {
192
- style: { ...S.iconBtn, color: v("muted") },
180
+ className: "gr-icon-btn gr-text-muted",
193
181
  onClick: exitEditMode,
194
182
  title: "Exit edit mode",
195
183
  "aria-label": "Exit edit mode",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/Admin.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useActionState, useEffect, useState } from \"react\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport { loginAction, logoutAction } from \"../server/actions\";\nimport { useGRail } from \"./Provider\";\nimport { TeamDrawer } from \"./Team\";\nimport { AuthDrawer } from \"./AuthDrawer\";\nimport { CloseIcon, LogOutIcon, PeopleIcon } from \"./Icons\";\nimport { S, THEME_CSS, v } from \"./styles\";\nimport { GR_COOKIES, GR_EDIT_MODE_OFF_TTL_SECONDS } from \"../core/defs\";\nimport type { ActionState } from \"../core/types\";\n\nconst initialState: ActionState = { email: \"\", password: \"\" };\n\n/**\n * Owns the theme stylesheet so every piece of chrome — the sign-in bar, the\n * toolbar, and the field popups portaled to <body> — picks up the same\n * light/dark variables from one place.\n */\nexport function GRailAdmin(props: { toolbarExtra?: React.ReactNode }) {\n const { isEditMode } = useGRail();\n if (!isEditMode) return null;\n return (\n <>\n {/*\n precedence + href make React hoist this into <head> and dedupe it.\n Required, not cosmetic: this whole component renders as a sibling of\n <html> (withGuardrail can't reach inside the app's own layout), and\n React 19 rejects a bare <style> anywhere outside the main document.\n */}\n <style href=\"guardrail-theme\" precedence=\"default\">\n {THEME_CSS}\n </style>\n <AdminBody {...props} />\n </>\n );\n}\n\nfunction AdminBody({ toolbarExtra }: { toolbarExtra?: React.ReactNode }) {\n const { isAuth, isEditMode, needsPassword, authError, role, auth } = useGRail();\n const [teamOpen, setTeamOpen] = useState(false);\n const [forgotOpen, setForgotOpen] = useState(false);\n const [errorDismissed, setErrorDismissed] = useState(false);\n const [formState, formAction, isPending] = useActionState(\n loginAction,\n initialState,\n );\n const router = useRouter();\n const pathname = usePathname();\n\n useEffect(() => {\n if (formState.success) router.refresh();\n }, [formState.success, router]);\n\n // The message for an emailed link that didn't work. The cookie behind it is\n // read on the server, so dismissing clears it there too (and refreshes), but\n // hides it right away without waiting for that.\n const dismissAuthError = () => {\n document.cookie = `${GR_COOKIES.AUTH_ERROR}=; path=/; max-age=0; SameSite=Lax`;\n setErrorDismissed(true);\n router.refresh();\n };\n const linkErrorDrawer =\n authError && !errorDismissed ? (\n <AuthDrawer\n mode=\"forgot\"\n title=\"That link didn't work\"\n message={\n authError === \"expired\"\n ? \"It has expired or was already used. Links work once. Enter your email to get a new one.\"\n : \"We couldn't complete it. Enter your email to get a new link.\"\n }\n onClose={dismissAuthError}\n />\n ) : null;\n\n // Ends the editing session, not the sign-in: the overlay goes away and the\n // person's session stays, so ?edit=true brings it straight back with no\n // credentials. The sign-in form only returns once the session itself is\n // gone (expired, or ended elsewhere). Explicit \"false\" rather than deleting\n // the cookie: with a live session, deleting it would leave edit mode on —\n // see resolveEditMode. router.replace drops ?edit=true from the URL (a bare\n // refresh would leave it there), and refresh re-renders without chrome.\n //\n // Also what the sign-in bar's dismiss button does, for someone who was never\n // authenticated and doesn't want to be here at all.\n const exitEditMode = () => {\n document.cookie = `${GR_COOKIES.EDIT_MODE}=false; path=/; max-age=${GR_EDIT_MODE_OFF_TTL_SECONDS}; SameSite=Lax`;\n router.replace(pathname);\n router.refresh();\n };\n\n // A real sign-out, kept only for the \"no access\" bar below: someone on the\n // wrong account has no other way to switch to the right one. logoutAction()\n // clears the edit-mode cookie itself — a client-side navigation right after\n // it used to race Next's own post-action route refresh and lose.\n const signOut = async () => {\n await logoutAction();\n router.replace(pathname);\n router.refresh();\n };\n\n // No intent signalled — render nothing at all.\n if (!isEditMode) return null;\n\n if (!isAuth) {\n // The adapter cannot take a password here, so hand sign-in off rather than\n // showing a form that could never succeed.\n if (!auth.password) {\n return (\n <>\n <div data-guardrail style={S.banner}>\n <div style={S.bannerForm}>\n <div style={S.brandStack}>\n <span style={S.brand}>Guardrail</span>\n {!auth.signInUrl && (\n <span style={{ fontSize: 12, color: v(\"muted\") }}>\n No sign-in method is configured for this site.\n </span>\n )}\n </div>\n {auth.signInUrl && (\n <a href={auth.signInUrl} style={{ ...S.primaryBtn, textDecoration: \"none\" }}>\n Sign in\n </a>\n )}\n <button\n type=\"button\"\n style={{ ...S.iconBtn, color: v(\"muted\") }}\n onClick={exitEditMode}\n title=\"Not editing? Dismiss\"\n aria-label=\"Dismiss\"\n >\n <CloseIcon />\n </button>\n </div>\n </div>\n {linkErrorDrawer}\n </>\n );\n }\n\n return (\n <>\n <div data-guardrail style={S.banner}>\n <form action={formAction} style={S.bannerForm}>\n <div style={S.brandStack}>\n <span style={S.brand}>Guardrail</span>\n {formState.error && <span style={S.error}>{formState.error}</span>}\n </div>\n <input\n name=\"email\"\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"Email\"\n defaultValue={formState.email}\n required\n style={S.input}\n />\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n placeholder=\"Password\"\n required\n style={S.input}\n />\n <button type=\"submit\" disabled={isPending} style={S.primaryBtn}>\n {isPending ? \"Authenticating…\" : \"Sign In\"}\n </button>\n <button\n type=\"button\"\n onClick={() => setForgotOpen(true)}\n style={{\n background: \"none\",\n border: \"none\",\n cursor: \"pointer\",\n color: v(\"muted\"),\n fontSize: 12,\n textDecoration: \"underline\",\n padding: \"0 4px\",\n whiteSpace: \"nowrap\",\n }}\n >\n Forgot password?\n </button>\n <button\n type=\"button\"\n style={{ ...S.iconBtn, color: v(\"muted\") }}\n onClick={exitEditMode}\n title=\"Not editing? Dismiss\"\n aria-label=\"Dismiss\"\n >\n <CloseIcon />\n </button>\n </form>\n </div>\n {linkErrorDrawer ??\n (forgotOpen && <AuthDrawer mode=\"forgot\" onClose={() => setForgotOpen(false)} />)}\n </>\n );\n }\n\n // An invite or password-reset link just completed: they're signed in, but must\n // choose a password before anything else. Ahead of the role check below —\n // getCachedViewer() already claims a pending invite as a side effect of\n // resolving them, so the role is normally set by now; this is purely about the\n // password. Not skippable: a password is the only way back in once this\n // session ends, so the overlay and toolbar wait until one exists.\n if (needsPassword) return <AuthDrawer mode=\"set-password\" />;\n\n // Signed in, but holds no role on this site.\n if (!role) {\n return (\n <>\n <div data-guardrail style={S.toolbar}>\n <span style={S.toolbarLabel}>No access to this site</span>\n <span style={S.divider} />\n <button\n style={{ ...S.iconBtn, color: v(\"muted\") }}\n onClick={signOut}\n title=\"Sign out\"\n aria-label=\"Sign out\"\n >\n <LogOutIcon />\n </button>\n </div>\n {linkErrorDrawer}\n </>\n );\n }\n\n return (\n <>\n {linkErrorDrawer}\n {teamOpen && <TeamDrawer onClose={() => setTeamOpen(false)} />}\n <div data-guardrail style={S.toolbar}>\n <span style={S.dot} />\n <span style={S.toolbarLabel}>Edit Mode</span>\n <span style={S.divider} />\n <button\n style={{ ...S.iconBtn, color: v(\"muted\"), width: 24, height: 24 }}\n onClick={() => setTeamOpen(true)}\n title=\"Team\"\n aria-label=\"Team\"\n >\n <PeopleIcon />\n </button>\n\n {toolbarExtra && (\n <>\n <span style={S.divider} />\n {toolbarExtra}\n </>\n )}\n\n <span style={S.divider} />\n <button\n style={{ ...S.iconBtn, color: v(\"muted\") }}\n onClick={exitEditMode}\n title=\"Exit edit mode\"\n aria-label=\"Exit edit mode\"\n >\n <LogOutIcon />\n </button>\n </div>\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/react/Admin.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useActionState, useEffect, useState } from \"react\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport { loginAction, logoutAction } from \"../server/actions\";\nimport { useGRail } from \"./Provider\";\nimport { TeamDrawer } from \"./Team\";\nimport { AuthDrawer } from \"./AuthDrawer\";\nimport { CloseIcon, LogOutIcon, PeopleIcon } from \"./Icons\";\nimport \"./guardrail.css\";\nimport { GR_COOKIES, GR_EDIT_MODE_OFF_TTL_SECONDS } from \"../core/defs\";\nimport type { ActionState } from \"../core/types\";\n\nconst initialState: ActionState = { email: \"\", password: \"\" };\n\n/**\n * Imports the chrome's stylesheet (guardrail.css), which also defines the\n * light/dark theme variables, so every piece of chrome — the sign-in bar, the\n * toolbar, and the field popups portaled to <body> — picks them up from one\n * place. A stylesheet rather than inline styles so the chrome works under a\n * strict Content-Security-Policy.\n */\nexport function GRailAdmin(props: { toolbarExtra?: React.ReactNode }) {\n const { isEditMode } = useGRail();\n if (!isEditMode) return null;\n return <AdminBody {...props} />;\n}\n\nfunction AdminBody({ toolbarExtra }: { toolbarExtra?: React.ReactNode }) {\n const { isAuth, isEditMode, needsPassword, authError, role, auth } = useGRail();\n const [teamOpen, setTeamOpen] = useState(false);\n const [forgotOpen, setForgotOpen] = useState(false);\n const [errorDismissed, setErrorDismissed] = useState(false);\n const [formState, formAction, isPending] = useActionState(\n loginAction,\n initialState,\n );\n const router = useRouter();\n const pathname = usePathname();\n\n useEffect(() => {\n if (formState.success) router.refresh();\n }, [formState.success, router]);\n\n // The message for an emailed link that didn't work. The cookie behind it is\n // read on the server, so dismissing clears it there too (and refreshes), but\n // hides it right away without waiting for that.\n const dismissAuthError = () => {\n document.cookie = `${GR_COOKIES.AUTH_ERROR}=; path=/; max-age=0; SameSite=Lax`;\n setErrorDismissed(true);\n router.refresh();\n };\n const linkErrorDrawer =\n authError && !errorDismissed ? (\n <AuthDrawer\n mode=\"forgot\"\n title=\"That link didn't work\"\n message={\n authError === \"expired\"\n ? \"It has expired or was already used. Links work once. Enter your email to get a new one.\"\n : \"We couldn't complete it. Enter your email to get a new link.\"\n }\n onClose={dismissAuthError}\n />\n ) : null;\n\n // Ends the editing session, not the sign-in: the overlay goes away and the\n // person's session stays, so ?edit=true brings it straight back with no\n // credentials. The sign-in form only returns once the session itself is\n // gone (expired, or ended elsewhere). Explicit \"false\" rather than deleting\n // the cookie: with a live session, deleting it would leave edit mode on —\n // see resolveEditMode. router.replace drops ?edit=true from the URL (a bare\n // refresh would leave it there), and refresh re-renders without chrome.\n //\n // Also what the sign-in bar's dismiss button does, for someone who was never\n // authenticated and doesn't want to be here at all.\n const exitEditMode = () => {\n document.cookie = `${GR_COOKIES.EDIT_MODE}=false; path=/; max-age=${GR_EDIT_MODE_OFF_TTL_SECONDS}; SameSite=Lax`;\n router.replace(pathname);\n router.refresh();\n };\n\n // A real sign-out, kept only for the \"no access\" bar below: someone on the\n // wrong account has no other way to switch to the right one. logoutAction()\n // clears the edit-mode cookie itself — a client-side navigation right after\n // it used to race Next's own post-action route refresh and lose.\n const signOut = async () => {\n await logoutAction();\n router.replace(pathname);\n router.refresh();\n };\n\n // No intent signalled — render nothing at all.\n if (!isEditMode) return null;\n\n if (!isAuth) {\n // The adapter cannot take a password here, so hand sign-in off rather than\n // showing a form that could never succeed.\n if (!auth.password) {\n return (\n <>\n <div data-guardrail className=\"gr-banner\">\n <div className=\"gr-banner-form\">\n <div className=\"gr-brand-stack\">\n <span className=\"gr-brand\">Guardrail</span>\n {!auth.signInUrl && (\n <span className=\"gr-fs-12 gr-text-muted\">\n No sign-in method is configured for this site.\n </span>\n )}\n </div>\n {auth.signInUrl && (\n <a href={auth.signInUrl} className=\"gr-primary-btn gr-no-underline\">\n Sign in\n </a>\n )}\n <button\n type=\"button\"\n className=\"gr-icon-btn gr-text-muted\"\n onClick={exitEditMode}\n title=\"Not editing? Dismiss\"\n aria-label=\"Dismiss\"\n >\n <CloseIcon />\n </button>\n </div>\n </div>\n {linkErrorDrawer}\n </>\n );\n }\n\n return (\n <>\n <div data-guardrail className=\"gr-banner\">\n <form action={formAction} className=\"gr-banner-form\">\n <div className=\"gr-brand-stack\">\n <span className=\"gr-brand\">Guardrail</span>\n {formState.error && <span className=\"gr-error\">{formState.error}</span>}\n </div>\n <input\n name=\"email\"\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"Email\"\n defaultValue={formState.email}\n required\n className=\"gr-input\"\n />\n <input\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n placeholder=\"Password\"\n required\n className=\"gr-input\"\n />\n <button type=\"submit\" disabled={isPending} className=\"gr-primary-btn\">\n {isPending ? \"Authenticating…\" : \"Sign In\"}\n </button>\n <button\n type=\"button\"\n onClick={() => setForgotOpen(true)}\n className=\"gr-text-btn\"\n >\n Forgot password?\n </button>\n <button\n type=\"button\"\n className=\"gr-icon-btn gr-text-muted\"\n onClick={exitEditMode}\n title=\"Not editing? Dismiss\"\n aria-label=\"Dismiss\"\n >\n <CloseIcon />\n </button>\n </form>\n </div>\n {linkErrorDrawer ??\n (forgotOpen && <AuthDrawer mode=\"forgot\" onClose={() => setForgotOpen(false)} />)}\n </>\n );\n }\n\n // An invite or password-reset link just completed: they're signed in, but must\n // choose a password before anything else. Ahead of the role check below —\n // getCachedViewer() already claims a pending invite as a side effect of\n // resolving them, so the role is normally set by now; this is purely about the\n // password. Not skippable: a password is the only way back in once this\n // session ends, so the overlay and toolbar wait until one exists.\n if (needsPassword) return <AuthDrawer mode=\"set-password\" />;\n\n // Signed in, but holds no role on this site.\n if (!role) {\n return (\n <>\n <div data-guardrail className=\"gr-toolbar\">\n <span className=\"gr-toolbar-label\">No access to this site</span>\n <span className=\"gr-divider\" />\n <button\n className=\"gr-icon-btn gr-text-muted\"\n onClick={signOut}\n title=\"Sign out\"\n aria-label=\"Sign out\"\n >\n <LogOutIcon />\n </button>\n </div>\n {linkErrorDrawer}\n </>\n );\n }\n\n return (\n <>\n {linkErrorDrawer}\n {teamOpen && <TeamDrawer onClose={() => setTeamOpen(false)} />}\n <div data-guardrail className=\"gr-toolbar\">\n <span className=\"gr-dot\" />\n <span className=\"gr-toolbar-label\">Edit Mode</span>\n <span className=\"gr-divider\" />\n <button\n className=\"gr-icon-btn gr-text-muted gr-size-24\"\n onClick={() => setTeamOpen(true)}\n title=\"Team\"\n aria-label=\"Team\"\n >\n <PeopleIcon />\n </button>\n\n {toolbarExtra && (\n <>\n <span className=\"gr-divider\" />\n {toolbarExtra}\n </>\n )}\n\n <span className=\"gr-divider\" />\n <button\n className=\"gr-icon-btn gr-text-muted\"\n onClick={exitEditMode}\n title=\"Exit edit mode\"\n aria-label=\"Exit edit mode\"\n >\n <LogOutIcon />\n </button>\n </div>\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -4,8 +4,6 @@ import React, { useActionState, useEffect, useState, useTransition } from "react
4
4
  import { useRouter } from "next/navigation";
5
5
  import { forgotPasswordAction, logoutAction, setPasswordAction } from "../server/actions";
6
6
  import { CloseIcon } from "./Icons";
7
- import { S, v } from "./styles";
8
- import { ANIMATION_CSS } from "./Team";
9
7
  const initialPasswordState = {};
10
8
  function AuthDrawer({
11
9
  mode,
@@ -14,13 +12,12 @@ function AuthDrawer({
14
12
  onClose
15
13
  }) {
16
14
  return /* @__PURE__ */ jsxs(Fragment, { children: [
17
- /* @__PURE__ */ jsx("style", { href: "guardrail-team-animation", precedence: "default", children: ANIMATION_CSS }),
18
15
  /* @__PURE__ */ jsx(
19
16
  "div",
20
17
  {
21
18
  "data-guardrail": true,
22
19
  "data-guardrail-scrim": true,
23
- style: S.scrim,
20
+ className: "gr-scrim",
24
21
  onClick: mode === "forgot" ? onClose : void 0
25
22
  }
26
23
  ),
@@ -29,16 +26,16 @@ function AuthDrawer({
29
26
  {
30
27
  "data-guardrail": true,
31
28
  "data-guardrail-team": true,
32
- style: S.drawer,
29
+ className: "gr-drawer",
33
30
  role: "dialog",
34
31
  "aria-label": mode === "set-password" ? "Set your password" : "Forgot password",
35
32
  children: [
36
- /* @__PURE__ */ jsxs("header", { style: S.drawerHead, children: [
37
- /* @__PURE__ */ jsx("h2", { style: S.drawerTitle, children: title ?? (mode === "set-password" ? "Set your password" : "Forgot your password?") }),
38
- mode === "forgot" && onClose && /* @__PURE__ */ jsx("button", { style: { ...S.iconBtn, color: v("muted") }, onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
33
+ /* @__PURE__ */ jsxs("header", { className: "gr-drawer-head", children: [
34
+ /* @__PURE__ */ jsx("h2", { className: "gr-drawer-title", children: title ?? (mode === "set-password" ? "Set your password" : "Forgot your password?") }),
35
+ mode === "forgot" && onClose && /* @__PURE__ */ jsx("button", { className: "gr-icon-btn gr-text-muted", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
39
36
  ] }),
40
- /* @__PURE__ */ jsxs("div", { style: S.drawerBody, children: [
41
- message && /* @__PURE__ */ jsx("p", { style: { fontSize: 13, lineHeight: 1.5, margin: "0 0 16px", color: v("fg") }, children: message }),
37
+ /* @__PURE__ */ jsxs("div", { className: "gr-drawer-body", children: [
38
+ message && /* @__PURE__ */ jsx("p", { className: "gr-fs-13 gr-lh-15 gr-m-0 gr-mb-16 gr-text-fg", children: message }),
42
39
  mode === "set-password" ? /* @__PURE__ */ jsx(SetPasswordForm, {}) : /* @__PURE__ */ jsx(ForgotPasswordForm, {})
43
40
  ] })
44
41
  ]
@@ -54,14 +51,14 @@ function SetPasswordForm() {
54
51
  if (state.success) router.refresh();
55
52
  }, [state.success, router]);
56
53
  return /* @__PURE__ */ jsxs(Fragment, { children: [
57
- /* @__PURE__ */ jsx("p", { style: { fontSize: 13, lineHeight: 1.5, margin: "0 0 16px", color: v("muted") }, children: "Choose a password. It's how you'll sign in from now on." }),
54
+ /* @__PURE__ */ jsx("p", { className: "gr-fs-13 gr-lh-15 gr-m-0 gr-mb-16 gr-text-muted", children: "Choose a password. It's how you'll sign in from now on." }),
58
55
  /* @__PURE__ */ jsxs("form", { action: formAction, children: [
59
- /* @__PURE__ */ jsx("label", { style: S.label, htmlFor: "gr-new-password", children: "New password" }),
56
+ /* @__PURE__ */ jsx("label", { className: "gr-label", htmlFor: "gr-new-password", children: "New password" }),
60
57
  /* @__PURE__ */ jsx(
61
58
  "input",
62
59
  {
63
60
  id: "gr-new-password",
64
- style: S.field,
61
+ className: "gr-field",
65
62
  name: "password",
66
63
  type: "password",
67
64
  autoComplete: "new-password",
@@ -70,12 +67,12 @@ function SetPasswordForm() {
70
67
  autoFocus: true
71
68
  }
72
69
  ),
73
- /* @__PURE__ */ jsx("label", { style: S.label, htmlFor: "gr-confirm-password", children: "Confirm password" }),
70
+ /* @__PURE__ */ jsx("label", { className: "gr-label", htmlFor: "gr-confirm-password", children: "Confirm password" }),
74
71
  /* @__PURE__ */ jsx(
75
72
  "input",
76
73
  {
77
74
  id: "gr-confirm-password",
78
- style: S.field,
75
+ className: "gr-field",
79
76
  name: "confirm",
80
77
  type: "password",
81
78
  autoComplete: "new-password",
@@ -83,25 +80,16 @@ function SetPasswordForm() {
83
80
  required: true
84
81
  }
85
82
  ),
86
- /* @__PURE__ */ jsx("p", { style: { fontSize: 11, color: v("subtle"), margin: "0 0 12px" }, children: "At least 8 characters." }),
87
- state.error && /* @__PURE__ */ jsx("div", { style: { ...S.error, marginBottom: 12 }, children: state.error }),
88
- /* @__PURE__ */ jsx("button", { style: { ...S.primaryBtn, width: "100%" }, type: "submit", disabled: isPending || signingOut, children: isPending ? "Saving\u2026" : "Set password" })
83
+ /* @__PURE__ */ jsx("p", { className: "gr-fs-11 gr-text-subtle gr-m-0 gr-mb-12", children: "At least 8 characters." }),
84
+ state.error && /* @__PURE__ */ jsx("div", { className: "gr-error gr-mb-12", children: state.error }),
85
+ /* @__PURE__ */ jsx("button", { className: "gr-primary-btn gr-w-full", type: "submit", disabled: isPending || signingOut, children: isPending ? "Saving\u2026" : "Set password" })
89
86
  ] }),
90
87
  /* @__PURE__ */ jsx(
91
88
  "button",
92
89
  {
93
90
  type: "button",
94
91
  disabled: isPending || signingOut,
95
- style: {
96
- background: "none",
97
- border: "none",
98
- cursor: "pointer",
99
- color: v("muted"),
100
- fontSize: 12,
101
- textDecoration: "underline",
102
- padding: 0,
103
- marginTop: 16
104
- },
92
+ className: "gr-text-btn gr-p-0 gr-mt-16",
105
93
  onClick: () => startSignOut(async () => {
106
94
  await logoutAction();
107
95
  router.refresh();
@@ -116,7 +104,7 @@ function ForgotPasswordForm() {
116
104
  const [status, setStatus] = useState("idle");
117
105
  const [error, setError] = useState(null);
118
106
  if (status === "sent") {
119
- return /* @__PURE__ */ jsxs("p", { style: { fontSize: 13, lineHeight: 1.5, margin: 0, color: v("fg") }, children: [
107
+ return /* @__PURE__ */ jsxs("p", { className: "gr-fs-13 gr-lh-15 gr-m-0 gr-text-fg", children: [
120
108
  "If ",
121
109
  /* @__PURE__ */ jsx("strong", { children: email.trim() }),
122
110
  " has access to this site, a link to choose a new password is on its way. It works once, so open it on this device."
@@ -137,13 +125,13 @@ function ForgotPasswordForm() {
137
125
  }
138
126
  },
139
127
  children: [
140
- /* @__PURE__ */ jsx("p", { style: { fontSize: 13, lineHeight: 1.5, margin: "0 0 16px", color: v("muted") }, children: "Enter your email and we'll send a link to choose a new password." }),
141
- /* @__PURE__ */ jsx("label", { style: S.label, htmlFor: "gr-forgot-email", children: "Email" }),
128
+ /* @__PURE__ */ jsx("p", { className: "gr-fs-13 gr-lh-15 gr-m-0 gr-mb-16 gr-text-muted", children: "Enter your email and we'll send a link to choose a new password." }),
129
+ /* @__PURE__ */ jsx("label", { className: "gr-label", htmlFor: "gr-forgot-email", children: "Email" }),
142
130
  /* @__PURE__ */ jsx(
143
131
  "input",
144
132
  {
145
133
  id: "gr-forgot-email",
146
- style: S.field,
134
+ className: "gr-field",
147
135
  type: "email",
148
136
  autoComplete: "email",
149
137
  placeholder: "name@company.com",
@@ -153,8 +141,8 @@ function ForgotPasswordForm() {
153
141
  autoFocus: true
154
142
  }
155
143
  ),
156
- error && /* @__PURE__ */ jsx("div", { style: { ...S.error, marginBottom: 12 }, children: error }),
157
- /* @__PURE__ */ jsx("button", { style: { ...S.primaryBtn, width: "100%" }, type: "submit", disabled: status === "sending", children: status === "sending" ? "Sending\u2026" : "Send reset link" })
144
+ error && /* @__PURE__ */ jsx("div", { className: "gr-error gr-mb-12", children: error }),
145
+ /* @__PURE__ */ jsx("button", { className: "gr-primary-btn gr-w-full", type: "submit", disabled: status === "sending", children: status === "sending" ? "Sending\u2026" : "Send reset link" })
158
146
  ]
159
147
  }
160
148
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/AuthDrawer.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useActionState, useEffect, useState, useTransition } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { forgotPasswordAction, logoutAction, setPasswordAction } from \"../server/actions\";\nimport { CloseIcon } from \"./Icons\";\nimport { S, v } from \"./styles\";\nimport { ANIMATION_CSS } from \"./Team\";\nimport type { SetPasswordState } from \"../core/types\";\n\nconst initialPasswordState: SetPasswordState = {};\n\n/**\n * The two password steps, in the same drawer the Team panel uses.\n *\n * Guardrail asks people for one credential, a password. Emailed links exist only\n * to let someone choose or replace it, so this is where they land:\n *\n * - \"set-password\" — after an invite or reset link. Cannot be dismissed: until a\n * password exists there is nothing to sign in with next time. The one way out\n * is to sign out.\n * - \"forgot\" — asks for an address and emails a reset link. Opened from the\n * sign-in bar, and from the \"that link didn't work\" message with `message`.\n */\nexport function AuthDrawer({\n mode,\n title,\n message,\n onClose,\n}: {\n mode: \"set-password\" | \"forgot\";\n title?: string;\n /** Plain-language context shown above the form, e.g. why a link failed. */\n message?: string;\n onClose?: () => void;\n}) {\n return (\n <>\n {/* Same sheet the Team drawer ships; React dedupes it by href. */}\n <style href=\"guardrail-team-animation\" precedence=\"default\">\n {ANIMATION_CSS}\n </style>\n <div\n data-guardrail\n data-guardrail-scrim\n style={S.scrim}\n // A set-password step is not something to click away from.\n onClick={mode === \"forgot\" ? onClose : undefined}\n />\n <aside\n data-guardrail\n data-guardrail-team\n style={S.drawer}\n role=\"dialog\"\n aria-label={mode === \"set-password\" ? \"Set your password\" : \"Forgot password\"}\n >\n <header style={S.drawerHead}>\n <h2 style={S.drawerTitle}>\n {title ?? (mode === \"set-password\" ? \"Set your password\" : \"Forgot your password?\")}\n </h2>\n {mode === \"forgot\" && onClose && (\n <button style={{ ...S.iconBtn, color: v(\"muted\") }} onClick={onClose} aria-label=\"Close\">\n <CloseIcon />\n </button>\n )}\n </header>\n <div style={S.drawerBody}>\n {message && (\n <p style={{ fontSize: 13, lineHeight: 1.5, margin: \"0 0 16px\", color: v(\"fg\") }}>{message}</p>\n )}\n {mode === \"set-password\" ? <SetPasswordForm /> : <ForgotPasswordForm />}\n </div>\n </aside>\n </>\n );\n}\n\nfunction SetPasswordForm() {\n const router = useRouter();\n const [state, formAction, isPending] = useActionState(setPasswordAction, initialPasswordState);\n const [signingOut, startSignOut] = useTransition();\n\n useEffect(() => {\n if (state.success) router.refresh();\n }, [state.success, router]);\n\n return (\n <>\n <p style={{ fontSize: 13, lineHeight: 1.5, margin: \"0 0 16px\", color: v(\"muted\") }}>\n Choose a password. It&apos;s how you&apos;ll sign in from now on.\n </p>\n <form action={formAction}>\n <label style={S.label} htmlFor=\"gr-new-password\">\n New password\n </label>\n <input\n id=\"gr-new-password\"\n style={S.field}\n name=\"password\"\n type=\"password\"\n autoComplete=\"new-password\"\n minLength={8}\n required\n autoFocus\n />\n <label style={S.label} htmlFor=\"gr-confirm-password\">\n Confirm password\n </label>\n <input\n id=\"gr-confirm-password\"\n style={S.field}\n name=\"confirm\"\n type=\"password\"\n autoComplete=\"new-password\"\n minLength={8}\n required\n />\n <p style={{ fontSize: 11, color: v(\"subtle\"), margin: \"0 0 12px\" }}>At least 8 characters.</p>\n {state.error && <div style={{ ...S.error, marginBottom: 12 }}>{state.error}</div>}\n <button style={{ ...S.primaryBtn, width: \"100%\" }} type=\"submit\" disabled={isPending || signingOut}>\n {isPending ? \"Saving…\" : \"Set password\"}\n </button>\n </form>\n <button\n type=\"button\"\n disabled={isPending || signingOut}\n style={{\n background: \"none\",\n border: \"none\",\n cursor: \"pointer\",\n color: v(\"muted\"),\n fontSize: 12,\n textDecoration: \"underline\",\n padding: 0,\n marginTop: 16,\n }}\n onClick={() =>\n startSignOut(async () => {\n await logoutAction();\n router.refresh();\n })\n }\n >\n Cancel and sign out\n </button>\n </>\n );\n}\n\nfunction ForgotPasswordForm() {\n const [email, setEmail] = useState(\"\");\n const [status, setStatus] = useState<\"idle\" | \"sending\" | \"sent\">(\"idle\");\n const [error, setError] = useState<string | null>(null);\n\n if (status === \"sent\") {\n return (\n <p style={{ fontSize: 13, lineHeight: 1.5, margin: 0, color: v(\"fg\") }}>\n If <strong>{email.trim()}</strong> has access to this site, a link to choose a new password is on its\n way. It works once, so open it on this device.\n </p>\n );\n }\n\n return (\n <form\n onSubmit={async (e) => {\n e.preventDefault();\n setStatus(\"sending\");\n setError(null);\n const res = await forgotPasswordAction(email);\n if (res.success) setStatus(\"sent\");\n else {\n setError(res.error ?? \"Something went wrong.\");\n setStatus(\"idle\");\n }\n }}\n >\n <p style={{ fontSize: 13, lineHeight: 1.5, margin: \"0 0 16px\", color: v(\"muted\") }}>\n Enter your email and we&apos;ll send a link to choose a new password.\n </p>\n <label style={S.label} htmlFor=\"gr-forgot-email\">\n Email\n </label>\n <input\n id=\"gr-forgot-email\"\n style={S.field}\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"name@company.com\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n autoFocus\n />\n {error && <div style={{ ...S.error, marginBottom: 12 }}>{error}</div>}\n <button style={{ ...S.primaryBtn, width: \"100%\" }} type=\"submit\" disabled={status === \"sending\"}>\n {status === \"sending\" ? \"Sending…\" : \"Send reset link\"}\n </button>\n </form>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/react/AuthDrawer.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useActionState, useEffect, useState, useTransition } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { forgotPasswordAction, logoutAction, setPasswordAction } from \"../server/actions\";\nimport { CloseIcon } from \"./Icons\";\nimport type { SetPasswordState } from \"../core/types\";\n\nconst initialPasswordState: SetPasswordState = {};\n\n/**\n * The two password steps, in the same drawer the Team panel uses.\n *\n * Guardrail asks people for one credential, a password. Emailed links exist only\n * to let someone choose or replace it, so this is where they land:\n *\n * - \"set-password\" — after an invite or reset link. Cannot be dismissed: until a\n * password exists there is nothing to sign in with next time. The one way out\n * is to sign out.\n * - \"forgot\" — asks for an address and emails a reset link. Opened from the\n * sign-in bar, and from the \"that link didn't work\" message with `message`.\n */\nexport function AuthDrawer({\n mode,\n title,\n message,\n onClose,\n}: {\n mode: \"set-password\" | \"forgot\";\n title?: string;\n /** Plain-language context shown above the form, e.g. why a link failed. */\n message?: string;\n onClose?: () => void;\n}) {\n return (\n <>\n <div\n data-guardrail\n data-guardrail-scrim\n className=\"gr-scrim\"\n // A set-password step is not something to click away from.\n onClick={mode === \"forgot\" ? onClose : undefined}\n />\n <aside\n data-guardrail\n data-guardrail-team\n className=\"gr-drawer\"\n role=\"dialog\"\n aria-label={mode === \"set-password\" ? \"Set your password\" : \"Forgot password\"}\n >\n <header className=\"gr-drawer-head\">\n <h2 className=\"gr-drawer-title\">\n {title ?? (mode === \"set-password\" ? \"Set your password\" : \"Forgot your password?\")}\n </h2>\n {mode === \"forgot\" && onClose && (\n <button className=\"gr-icon-btn gr-text-muted\" onClick={onClose} aria-label=\"Close\">\n <CloseIcon />\n </button>\n )}\n </header>\n <div className=\"gr-drawer-body\">\n {message && (\n <p className=\"gr-fs-13 gr-lh-15 gr-m-0 gr-mb-16 gr-text-fg\">{message}</p>\n )}\n {mode === \"set-password\" ? <SetPasswordForm /> : <ForgotPasswordForm />}\n </div>\n </aside>\n </>\n );\n}\n\nfunction SetPasswordForm() {\n const router = useRouter();\n const [state, formAction, isPending] = useActionState(setPasswordAction, initialPasswordState);\n const [signingOut, startSignOut] = useTransition();\n\n useEffect(() => {\n if (state.success) router.refresh();\n }, [state.success, router]);\n\n return (\n <>\n <p className=\"gr-fs-13 gr-lh-15 gr-m-0 gr-mb-16 gr-text-muted\">\n Choose a password. It&apos;s how you&apos;ll sign in from now on.\n </p>\n <form action={formAction}>\n <label className=\"gr-label\" htmlFor=\"gr-new-password\">\n New password\n </label>\n <input\n id=\"gr-new-password\"\n className=\"gr-field\"\n name=\"password\"\n type=\"password\"\n autoComplete=\"new-password\"\n minLength={8}\n required\n autoFocus\n />\n <label className=\"gr-label\" htmlFor=\"gr-confirm-password\">\n Confirm password\n </label>\n <input\n id=\"gr-confirm-password\"\n className=\"gr-field\"\n name=\"confirm\"\n type=\"password\"\n autoComplete=\"new-password\"\n minLength={8}\n required\n />\n <p className=\"gr-fs-11 gr-text-subtle gr-m-0 gr-mb-12\">At least 8 characters.</p>\n {state.error && <div className=\"gr-error gr-mb-12\">{state.error}</div>}\n <button className=\"gr-primary-btn gr-w-full\" type=\"submit\" disabled={isPending || signingOut}>\n {isPending ? \"Saving…\" : \"Set password\"}\n </button>\n </form>\n <button\n type=\"button\"\n disabled={isPending || signingOut}\n className=\"gr-text-btn gr-p-0 gr-mt-16\"\n onClick={() =>\n startSignOut(async () => {\n await logoutAction();\n router.refresh();\n })\n }\n >\n Cancel and sign out\n </button>\n </>\n );\n}\n\nfunction ForgotPasswordForm() {\n const [email, setEmail] = useState(\"\");\n const [status, setStatus] = useState<\"idle\" | \"sending\" | \"sent\">(\"idle\");\n const [error, setError] = useState<string | null>(null);\n\n if (status === \"sent\") {\n return (\n <p className=\"gr-fs-13 gr-lh-15 gr-m-0 gr-text-fg\">\n If <strong>{email.trim()}</strong> has access to this site, a link to choose a new password is on its\n way. It works once, so open it on this device.\n </p>\n );\n }\n\n return (\n <form\n onSubmit={async (e) => {\n e.preventDefault();\n setStatus(\"sending\");\n setError(null);\n const res = await forgotPasswordAction(email);\n if (res.success) setStatus(\"sent\");\n else {\n setError(res.error ?? \"Something went wrong.\");\n setStatus(\"idle\");\n }\n }}\n >\n <p className=\"gr-fs-13 gr-lh-15 gr-m-0 gr-mb-16 gr-text-muted\">\n Enter your email and we&apos;ll send a link to choose a new password.\n </p>\n <label className=\"gr-label\" htmlFor=\"gr-forgot-email\">\n Email\n </label>\n <input\n id=\"gr-forgot-email\"\n className=\"gr-field\"\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"name@company.com\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n autoFocus\n />\n {error && <div className=\"gr-error gr-mb-12\">{error}</div>}\n <button className=\"gr-primary-btn gr-w-full\" type=\"submit\" disabled={status === \"sending\"}>\n {status === \"sending\" ? \"Sending…\" : \"Send reset link\"}\n </button>\n </form>\n );\n}\n"],"mappings":"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","names":[]}
@@ -9,7 +9,6 @@ import { useGRail } from "../Provider";
9
9
  import { useAnchorRect } from "../useAnchorRect";
10
10
  import { CheckIcon, CloseIcon, OpenIcon, PencilIcon, SpinnerIcon } from "../Icons";
11
11
  import { openHref } from "../openHref";
12
- import { S } from "../styles";
13
12
  import { MediaPicker } from "../MediaPicker";
14
13
  function GRailClient({
15
14
  as,
@@ -84,17 +83,8 @@ function GRailClient({
84
83
  ...htmlProps,
85
84
  ref: elementRef,
86
85
  override: renderedValue,
87
- style: {
88
- ...htmlProps.style,
89
- // Images are left alone — no outline, position, or z-index — and
90
- // get a separate ImageMarker instead. Restyling the <img> itself
91
- // can't work for a background image with content layered over it.
92
- ...isImage ? { cursor: "pointer" } : {
93
- outline: editing ? "2px solid #2563eb" : "1px dashed #60a5fa",
94
- outlineOffset: 2,
95
- cursor: editing ? "text" : "pointer"
96
- }
97
- },
86
+ "data-gr-field": isImage ? "image" : "text",
87
+ ...editing && !isImage ? { "data-gr-editing": "" } : {},
98
88
  onClick: (e) => {
99
89
  if (editing) return;
100
90
  e.preventDefault();
@@ -168,13 +158,13 @@ function ImageMarker({
168
158
  "div",
169
159
  {
170
160
  "data-guardrail": true,
161
+ className: "gr-marker-outline",
162
+ ...editing ? { "data-gr-editing": "" } : {},
171
163
  style: {
172
- ...S.markerOutline,
173
164
  top: rect.top - 2,
174
165
  left: rect.left - 2,
175
166
  width: rect.width + 4,
176
- height: height + 4,
177
- border: editing ? "2px solid #2563eb" : "1px dashed #60a5fa"
167
+ height: height + 4
178
168
  }
179
169
  }
180
170
  ),
@@ -183,7 +173,8 @@ function ImageMarker({
183
173
  {
184
174
  type: "button",
185
175
  "data-guardrail": true,
186
- style: { ...S.markerHandle, top: handleTop, left: handleLeft },
176
+ className: "gr-marker-handle",
177
+ style: { top: handleTop, left: handleLeft },
187
178
  "aria-label": "Edit image",
188
179
  title: "Edit image",
189
180
  onClick: (e) => {
@@ -226,7 +217,7 @@ function Chrome({
226
217
  /* @__PURE__ */ jsx(
227
218
  "button",
228
219
  {
229
- style: S.iconBtnPrimary,
220
+ className: "gr-icon-btn-primary",
230
221
  onClick: onSave,
231
222
  disabled: saving,
232
223
  title: "Save (Enter)",
@@ -237,7 +228,7 @@ function Chrome({
237
228
  /* @__PURE__ */ jsx(
238
229
  "button",
239
230
  {
240
- style: S.iconBtn,
231
+ className: "gr-icon-btn",
241
232
  onClick: onCancel,
242
233
  disabled: saving,
243
234
  title: "Cancel (Esc)",
@@ -247,46 +238,47 @@ function Chrome({
247
238
  )
248
239
  ] });
249
240
  if (isInline) {
250
- return /* @__PURE__ */ jsxs("div", { "data-guardrail": true, style: { ...S.anchoredBar, ...position }, children: [
241
+ return /* @__PURE__ */ jsxs("div", { "data-guardrail": true, className: "gr-anchored-bar", style: position, children: [
251
242
  actions,
252
- error && /* @__PURE__ */ jsx("span", { style: { ...S.error, margin: "0 6px" }, children: error })
243
+ error && /* @__PURE__ */ jsx("span", { className: "gr-error gr-mx-6", children: error })
253
244
  ] });
254
245
  }
255
246
  return /* @__PURE__ */ jsxs(
256
247
  "div",
257
248
  {
258
249
  "data-guardrail": true,
259
- style: { ...S.anchoredPanel, ...position },
250
+ className: "gr-anchored-panel",
251
+ style: position,
260
252
  onClick: (e) => e.stopPropagation(),
261
253
  children: [
262
254
  row.value.type === "image" && /* @__PURE__ */ jsxs(Fragment, { children: [
263
255
  row.value.src && // Plain img: previews must not depend on remotePatterns.
264
- /* @__PURE__ */ jsx("img", { src: row.value.src, alt: "", style: S.previewThumb }),
256
+ /* @__PURE__ */ jsx("img", { src: row.value.src, alt: "", className: "gr-preview-thumb" }),
265
257
  storage && /* @__PURE__ */ jsx(
266
258
  "button",
267
259
  {
268
260
  type: "button",
269
- style: S.secondaryBtn,
261
+ className: "gr-secondary-btn",
270
262
  onClick: () => setPickerOpen(true),
271
263
  children: row.value.src ? "Replace image" : "Choose image"
272
264
  }
273
265
  ),
274
- /* @__PURE__ */ jsx("label", { style: S.label, children: "Alt text" }),
266
+ /* @__PURE__ */ jsx("label", { className: "gr-label", children: "Alt text" }),
275
267
  /* @__PURE__ */ jsx(
276
268
  "input",
277
269
  {
278
- style: S.field,
270
+ className: "gr-field",
279
271
  value: row.value.alt || "",
280
272
  placeholder: "Describe the image for screen readers",
281
273
  onChange: (e) => onChange("alt", e.target.value)
282
274
  }
283
275
  ),
284
276
  !storage && /* @__PURE__ */ jsxs(Fragment, { children: [
285
- /* @__PURE__ */ jsx("label", { style: S.label, children: "Image URL" }),
277
+ /* @__PURE__ */ jsx("label", { className: "gr-label", children: "Image URL" }),
286
278
  /* @__PURE__ */ jsx(
287
279
  "input",
288
280
  {
289
- style: S.field,
281
+ className: "gr-field",
290
282
  value: row.value.src || "",
291
283
  onChange: (e) => onPatch({ src: e.target.value, width: void 0, height: void 0 })
292
284
  }
@@ -302,48 +294,36 @@ function Chrome({
302
294
  )
303
295
  ] }),
304
296
  row.value.type === "link" && /* @__PURE__ */ jsxs(Fragment, { children: [
305
- /* @__PURE__ */ jsx("label", { style: S.label, children: "Link text" }),
297
+ /* @__PURE__ */ jsx("label", { className: "gr-label", children: "Link text" }),
306
298
  /* @__PURE__ */ jsx(
307
299
  "input",
308
300
  {
309
- style: S.field,
301
+ className: "gr-field",
310
302
  value: row.value.text || "",
311
303
  onChange: (e) => onChange("text", e.target.value)
312
304
  }
313
305
  ),
314
- /* @__PURE__ */ jsx("label", { style: S.label, children: "URL" }),
306
+ /* @__PURE__ */ jsx("label", { className: "gr-label", children: "URL" }),
315
307
  /* @__PURE__ */ jsx(
316
308
  "input",
317
309
  {
318
- style: S.field,
310
+ className: "gr-field",
319
311
  value: row.value.href || "",
320
312
  onChange: (e) => onChange("href", e.target.value)
321
313
  }
322
314
  )
323
315
  ] }),
324
- error && /* @__PURE__ */ jsx("div", { style: { ...S.error, marginBottom: 8 }, children: error }),
316
+ error && /* @__PURE__ */ jsx("div", { className: "gr-error gr-mb-8", children: error }),
325
317
  /* @__PURE__ */ jsxs(
326
318
  "div",
327
319
  {
328
- style: {
329
- display: "flex",
330
- gap: 4,
331
- alignItems: "center",
332
- justifyContent: row.value.type === "link" ? "space-between" : "flex-end"
333
- },
320
+ className: row.value.type === "link" ? "gr-flex gr-gap-4 gr-items-center gr-justify-between" : "gr-flex gr-gap-4 gr-items-center gr-justify-end",
334
321
  children: [
335
322
  row.value.type === "link" && /* @__PURE__ */ jsxs(
336
323
  "button",
337
324
  {
338
325
  type: "button",
339
- style: {
340
- ...S.secondaryBtn,
341
- width: "auto",
342
- marginBottom: 0,
343
- display: "inline-flex",
344
- alignItems: "center",
345
- gap: 6
346
- },
326
+ className: "gr-secondary-btn gr-w-auto gr-mb-0 gr-inline-flex gr-items-center gr-gap-6",
347
327
  disabled: !row.value.href?.trim(),
348
328
  onClick: () => openHref(row.value.href ?? "", (path) => router.push(path)),
349
329
  title: "Open this link",
@@ -354,7 +334,7 @@ function Chrome({
354
334
  ]
355
335
  }
356
336
  ),
357
- /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 4 }, children: actions })
337
+ /* @__PURE__ */ jsx("div", { className: "gr-flex gr-gap-4", children: actions })
358
338
  ]
359
339
  }
360
340
  )