@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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/Team.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useCallback, useEffect, useState, useTransition } from \"react\";\nimport {\n inviteMember,\n listTeam,\n removeMember,\n sendPasswordResetAction,\n updateMemberRole,\n} from \"../server/actions\";\nimport { useGRail } from \"./Provider\";\nimport { CloseIcon, KeyIcon } from \"./Icons\";\nimport { S, v } from \"./styles\";\nimport type { GRMember, GRRole } from \"../core/types\";\n\nconst ROLES: GRRole[] = [\"admin\", \"editor\"];\n\n/**\n * Keyframes cannot be expressed as inline styles, so the drawer ships the few\n * it needs. Scoped to [data-guardrail-team] to stay clear of app styles, and\n * disabled under prefers-reduced-motion.\n */\nexport const ANIMATION_CSS = `\n@keyframes gr-fade-in { from { opacity: 0 } to { opacity: 1 } }\n@keyframes gr-slide-in {\n from { transform: translateX(100%) }\n to { transform: translateX(0) }\n}\n[data-guardrail-scrim] { animation: gr-fade-in 160ms ease-out both }\n[data-guardrail-team] { animation: gr-slide-in 220ms cubic-bezier(0.32, 0.72, 0, 1) both }\n[data-guardrail-team] [data-gr-row] { animation: gr-fade-in 240ms ease-out both }\n@keyframes gr-fade-out { from { opacity: 1 } to { opacity: 0 } }\n@keyframes gr-slide-out {\n from { transform: translateX(0) }\n to { transform: translateX(100%) }\n}\n[data-guardrail-scrim][data-closing] { animation: gr-fade-out 180ms ease-in both }\n[data-guardrail-team][data-closing] { animation: gr-slide-out 180ms cubic-bezier(0.4, 0, 1, 1) both }\n@media (prefers-reduced-motion: reduce) {\n [data-guardrail-scrim], [data-guardrail-team], [data-guardrail-team] [data-gr-row] {\n animation: none\n }\n}\n`;\n\nexport function TeamDrawer({ onClose }: { onClose: () => void }) {\n const { role, email: currentEmail } = useGRail();\n const isAdmin = role === \"admin\";\n\n const [members, setMembers] = useState<GRMember[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const [inviteEmail, setInviteEmail] = useState(\"\");\n const [inviteRole, setInviteRole] = useState<GRRole>(\"editor\");\n const [pending, startTransition] = useTransition();\n\n const refresh = () =>\n listTeam().then((rows) => {\n setMembers(rows);\n setLoading(false);\n });\n\n useEffect(() => {\n void refresh();\n }, []);\n\n // Server actions re-check the role; this only decides what to render.\n // Always refresh: a failure can still have changed the roster (an invite\n // that saved but whose email failed, for instance).\n const run = (fn: () => Promise<{ success: boolean; error?: string; notice?: string }>) =>\n startTransition(async () => {\n setError(null);\n setNotice(null);\n const res = await fn();\n if (!res.success) setError(res.error ?? \"Something went wrong.\");\n else if (res.notice) setNotice(res.notice);\n await refresh();\n });\n\n /**\n * Unmounting on close skips the exit animation entirely. Stay mounted, play\n * it, and let the parent unmount once it finishes.\n */\n const [closing, setClosing] = useState(false);\n const requestClose = useCallback(() => {\n const reduced =\n typeof window !== \"undefined\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n if (reduced) return onClose(); // No animation, so no animationend to wait for.\n setClosing(true);\n }, [onClose]);\n\n useEffect(() => {\n if (!closing) return;\n // Safety net if animationend never arrives (tab hidden, styles stripped).\n const t = window.setTimeout(onClose, 400);\n return () => window.clearTimeout(t);\n }, [closing, onClose]);\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && requestClose();\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [requestClose]);\n\n return (\n <>\n {/* Hoisted to <head> — see the note on the theme <style> in Admin.tsx. */}\n <style href=\"guardrail-team-animation\" precedence=\"default\">\n {ANIMATION_CSS}\n </style>\n <div\n data-guardrail\n data-guardrail-scrim\n {...(closing ? { \"data-closing\": \"\" } : {})}\n style={S.scrim}\n onClick={requestClose}\n />\n <aside\n data-guardrail\n data-guardrail-team\n {...(closing ? { \"data-closing\": \"\" } : {})}\n onAnimationEnd={(e) => {\n // Row fade-ins bubble up here too; only the drawer's own exit counts.\n if (closing && e.target === e.currentTarget) onClose();\n }}\n style={S.drawer}\n role=\"dialog\"\n aria-label=\"Team\"\n >\n <header style={S.drawerHead}>\n <h2 style={S.drawerTitle}>Team</h2>\n <button style={{ ...S.iconBtn, color: v(\"muted\") }} onClick={requestClose} aria-label=\"Close\">\n <CloseIcon />\n </button>\n </header>\n\n <div style={S.drawerBody}>\n {error && <div style={{ ...S.error, marginBottom: 12 }}>{error}</div>}\n {notice && (\n <div style={{ fontSize: 12, color: v(\"muted\"), marginBottom: 12 }} role=\"status\">\n {notice}\n </div>\n )}\n\n {loading ? (\n <p style={{ fontSize: 13, color: v(\"muted\") }}>Loading…</p>\n ) : (\n members.map((m) => (\n <div key={m.email} data-gr-row style={S.memberRow}>\n <span style={S.memberEmail} title={m.email}>\n {m.email}\n {m.email === currentEmail && \" (you)\"}\n </span>\n\n {m.status === \"pending\" && <span style={S.badge}>Invited</span>}\n\n {isAdmin ? (\n <select\n style={S.select}\n value={m.role}\n disabled={pending}\n onChange={(e) =>\n run(() =>\n updateMemberRole(m.email, e.target.value as GRRole),\n )\n }\n >\n {ROLES.map((r) => (\n <option key={r} value={r}>\n {r}\n </option>\n ))}\n </select>\n ) : (\n <span style={{ fontSize: 12, color: v(\"muted\") }}>{m.role}</span>\n )}\n\n {isAdmin && (\n <button\n style={{ ...S.iconBtn, color: v(\"muted\"), width: 22, height: 22 }}\n disabled={pending}\n onClick={() => run(() => sendPasswordResetAction(m.email))}\n aria-label={`Send password reset to ${m.email}`}\n title=\"Send password reset\"\n >\n <KeyIcon />\n </button>\n )}\n\n {isAdmin && (\n <button\n style={{ ...S.iconBtn, color: v(\"muted\"), width: 22, height: 22 }}\n disabled={pending}\n onClick={() => run(() => removeMember(m.email))}\n aria-label={`Remove ${m.email}`}\n title=\"Remove\"\n >\n <CloseIcon />\n </button>\n )}\n </div>\n ))\n )}\n\n {isAdmin && (\n <>\n <p style={S.sectionLabel}>Invite someone</p>\n <form\n onSubmit={(e) => {\n e.preventDefault();\n run(async () => {\n const res = await inviteMember(inviteEmail, inviteRole);\n if (res.success) setInviteEmail(\"\");\n return res;\n });\n }}\n >\n <input\n style={S.field}\n type=\"email\"\n placeholder=\"name@company.com\"\n value={inviteEmail}\n onChange={(e) => setInviteEmail(e.target.value)}\n required\n />\n <div style={{ display: \"flex\", gap: 8 }}>\n <select\n style={{ ...S.select, flex: 1 }}\n value={inviteRole}\n onChange={(e) => setInviteRole(e.target.value as GRRole)}\n >\n {ROLES.map((r) => (\n <option key={r} value={r}>\n {r}\n </option>\n ))}\n </select>\n <button style={S.primaryBtn} type=\"submit\" disabled={pending}>\n {pending ? \"Working…\" : \"Invite\"}\n </button>\n </div>\n </form>\n <p style={{ fontSize: 11, color: v(\"subtle\"), marginTop: 10 }}>\n New people get an email to set a password. Invites and resets go\n out through your <code>onInvite</code> / <code>onPasswordReset</code>{\" \"}\n handlers, or your auth provider&apos;s own emails if you haven&apos;t\n set them.\n </p>\n </>\n )}\n </div>\n </aside>\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/react/Team.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useCallback, useEffect, useState, useTransition } from \"react\";\nimport {\n inviteMember,\n listTeam,\n removeMember,\n sendPasswordResetAction,\n updateMemberRole,\n} from \"../server/actions\";\nimport { useGRail } from \"./Provider\";\nimport { CloseIcon, KeyIcon } from \"./Icons\";\nimport type { GRMember, GRRole } from \"../core/types\";\n\nconst ROLES: GRRole[] = [\"admin\", \"editor\"];\n\nexport function TeamDrawer({ onClose }: { onClose: () => void }) {\n const { role, email: currentEmail } = useGRail();\n const isAdmin = role === \"admin\";\n\n const [members, setMembers] = useState<GRMember[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const [inviteEmail, setInviteEmail] = useState(\"\");\n const [inviteRole, setInviteRole] = useState<GRRole>(\"editor\");\n const [pending, startTransition] = useTransition();\n\n const refresh = () =>\n listTeam().then((rows) => {\n setMembers(rows);\n setLoading(false);\n });\n\n useEffect(() => {\n void refresh();\n }, []);\n\n // Server actions re-check the role; this only decides what to render.\n // Always refresh: a failure can still have changed the roster (an invite\n // that saved but whose email failed, for instance).\n const run = (fn: () => Promise<{ success: boolean; error?: string; notice?: string }>) =>\n startTransition(async () => {\n setError(null);\n setNotice(null);\n const res = await fn();\n if (!res.success) setError(res.error ?? \"Something went wrong.\");\n else if (res.notice) setNotice(res.notice);\n await refresh();\n });\n\n /**\n * Unmounting on close skips the exit animation entirely. Stay mounted, play\n * it, and let the parent unmount once it finishes.\n */\n const [closing, setClosing] = useState(false);\n const requestClose = useCallback(() => {\n const reduced =\n typeof window !== \"undefined\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n if (reduced) return onClose(); // No animation, so no animationend to wait for.\n setClosing(true);\n }, [onClose]);\n\n useEffect(() => {\n if (!closing) return;\n // Safety net if animationend never arrives (tab hidden, styles stripped).\n const t = window.setTimeout(onClose, 400);\n return () => window.clearTimeout(t);\n }, [closing, onClose]);\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && requestClose();\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [requestClose]);\n\n return (\n <>\n <div\n data-guardrail\n data-guardrail-scrim\n {...(closing ? { \"data-closing\": \"\" } : {})}\n className=\"gr-scrim\"\n onClick={requestClose}\n />\n <aside\n data-guardrail\n data-guardrail-team\n {...(closing ? { \"data-closing\": \"\" } : {})}\n onAnimationEnd={(e) => {\n // Row fade-ins bubble up here too; only the drawer's own exit counts.\n if (closing && e.target === e.currentTarget) onClose();\n }}\n className=\"gr-drawer\"\n role=\"dialog\"\n aria-label=\"Team\"\n >\n <header className=\"gr-drawer-head\">\n <h2 className=\"gr-drawer-title\">Team</h2>\n <button className=\"gr-icon-btn gr-text-muted\" onClick={requestClose} aria-label=\"Close\">\n <CloseIcon />\n </button>\n </header>\n\n <div className=\"gr-drawer-body\">\n {error && <div className=\"gr-error gr-mb-12\">{error}</div>}\n {notice && (\n <div className=\"gr-fs-12 gr-text-muted gr-mb-12\" role=\"status\">\n {notice}\n </div>\n )}\n\n {loading ? (\n <p className=\"gr-fs-13 gr-text-muted\">Loading…</p>\n ) : (\n members.map((m) => (\n <div key={m.email} data-gr-row className=\"gr-member-row\">\n <span className=\"gr-member-email\" title={m.email}>\n {m.email}\n {m.email === currentEmail && \" (you)\"}\n </span>\n\n {m.status === \"pending\" && <span className=\"gr-badge\">Invited</span>}\n\n {isAdmin ? (\n <select\n className=\"gr-select\"\n value={m.role}\n disabled={pending}\n onChange={(e) =>\n run(() =>\n updateMemberRole(m.email, e.target.value as GRRole),\n )\n }\n >\n {ROLES.map((r) => (\n <option key={r} value={r}>\n {r}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"gr-fs-12 gr-text-muted\">{m.role}</span>\n )}\n\n {isAdmin && (\n <button\n className=\"gr-icon-btn gr-text-muted gr-size-22\"\n disabled={pending}\n onClick={() => run(() => sendPasswordResetAction(m.email))}\n aria-label={`Send password reset to ${m.email}`}\n title=\"Send password reset\"\n >\n <KeyIcon />\n </button>\n )}\n\n {isAdmin && (\n <button\n className=\"gr-icon-btn gr-text-muted gr-size-22\"\n disabled={pending}\n onClick={() => run(() => removeMember(m.email))}\n aria-label={`Remove ${m.email}`}\n title=\"Remove\"\n >\n <CloseIcon />\n </button>\n )}\n </div>\n ))\n )}\n\n {isAdmin && (\n <>\n <p className=\"gr-section-label\">Invite someone</p>\n <form\n onSubmit={(e) => {\n e.preventDefault();\n run(async () => {\n const res = await inviteMember(inviteEmail, inviteRole);\n if (res.success) setInviteEmail(\"\");\n return res;\n });\n }}\n >\n <input\n className=\"gr-field\"\n type=\"email\"\n placeholder=\"name@company.com\"\n value={inviteEmail}\n onChange={(e) => setInviteEmail(e.target.value)}\n required\n />\n <div className=\"gr-flex gr-gap-8\">\n <select\n className=\"gr-select gr-flex-1\"\n value={inviteRole}\n onChange={(e) => setInviteRole(e.target.value as GRRole)}\n >\n {ROLES.map((r) => (\n <option key={r} value={r}>\n {r}\n </option>\n ))}\n </select>\n <button className=\"gr-primary-btn\" type=\"submit\" disabled={pending}>\n {pending ? \"Working…\" : \"Invite\"}\n </button>\n </div>\n </form>\n <p className=\"gr-fs-11 gr-text-subtle gr-mt-10\">\n New people get an email to set a password. Invites and resets go\n out through your <code>onInvite</code> / <code>onPasswordReset</code>{\" \"}\n handlers, or your auth provider&apos;s own emails if you haven&apos;t\n set them.\n </p>\n </>\n )}\n </div>\n </aside>\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,563 @@
1
+ /*
2
+ * Guardrail's editing chrome — the sign-in bar, toolbar, editors, drawers and
3
+ * the outlines drawn around editable fields.
4
+ *
5
+ * Imported by the package itself (see GRailAdmin), so a site doesn't link it by
6
+ * hand, and shipped as a real stylesheet rather than inline styles so it works
7
+ * under a strict Content-Security-Policy (style-src 'self', no 'unsafe-inline').
8
+ *
9
+ * Theming: every color is a --gr-* variable. Override any of them in your own
10
+ * CSS to restyle the chrome, e.g.
11
+ *
12
+ * :root { --gr-accent: #7c3aed; --gr-field-outline: #7c3aed; }
13
+ *
14
+ * Three layers: change a base color (--gr-bg, --gr-fg, --gr-border …) to retheme
15
+ * everything, or set the --gr-overlay-* variables to restyle only the floating
16
+ * surfaces (sign-in bar, toolbar, popups, drawers, picker) — they default to the
17
+ * base colors, so you only set what you want to differ.
18
+ *
19
+ * The defaults below are declared in :where(:root), which has no specificity, so
20
+ * a plain :root rule of yours wins whatever order the stylesheets load in — no
21
+ * !important, no selector tricks.
22
+ *
23
+ * Every class is prefixed gr-, so nothing here can collide with a site's own.
24
+ */
25
+
26
+ /* ---- theme ---------------------------------------------------------------- */
27
+
28
+ :where(:root) { --gr-bg: #ffffff; --gr-bg-glass: rgba(255,255,255,0.97); --gr-fg: #111827; --gr-fg2: #4b5563; --gr-muted: #6b7280; --gr-subtle: #9ca3af; --gr-border: #e5e7eb; --gr-border-strong: #d1d5db; --gr-border-soft: #f3f4f6; --gr-input-bg: #ffffff; --gr-soft: #f3f4f6; --gr-accent: #2563eb; --gr-accent-soft: #eff6ff; --gr-accent-text: #1d4ed8; --gr-primary-bg: #111827; --gr-primary-fg: #ffffff; --gr-danger: #dc2626; --gr-label: #1e3a8a; --gr-shadow: rgba(0,0,0,0.25); --gr-field-outline: #60a5fa; --gr-field-outline-active: #2563eb; --gr-on-accent: #ffffff; --gr-scrim: rgba(0,0,0,0.4); --gr-scrim-modal: rgba(0,0,0,0.5); --gr-badge-bg: #fef3c7; --gr-badge-fg: #92400e; --gr-overlay-bg: var(--gr-bg); --gr-overlay-bg-glass: var(--gr-bg-glass); --gr-overlay-fg: var(--gr-fg); --gr-overlay-border: var(--gr-border); }
29
+ [data-guardrail] { color-scheme: light; }
30
+ @media (prefers-color-scheme: dark) {
31
+ :where(:root) { --gr-bg: #0f1b2d; --gr-bg-glass: rgba(15,27,45,0.97); --gr-fg: #e6edf7; --gr-fg2: #b6c4d8; --gr-muted: #9fb0c7; --gr-subtle: #7d8ea6; --gr-border: rgba(148,163,184,0.22); --gr-border-strong: rgba(148,163,184,0.35); --gr-border-soft: rgba(148,163,184,0.14); --gr-input-bg: #14243b; --gr-soft: #1a2d49; --gr-accent: #3b82f6; --gr-accent-soft: rgba(59,130,246,0.16); --gr-accent-text: #93c5fd; --gr-primary-bg: #3b82f6; --gr-primary-fg: #ffffff; --gr-danger: #f87171; --gr-label: #dbeafe; --gr-shadow: rgba(0,0,0,0.5); }
32
+ [data-guardrail] { color-scheme: dark; }
33
+ }
34
+
35
+ /* ---- components ------------------------------------------------------------ */
36
+
37
+ .gr-banner {
38
+ position: fixed;
39
+ bottom: 0;
40
+ left: 0;
41
+ width: 100%;
42
+ background: var(--gr-overlay-bg);
43
+ color: var(--gr-overlay-fg);
44
+ padding: 12px 16px;
45
+ z-index: 10000;
46
+ border-top: 1px solid var(--gr-overlay-border);
47
+ font-family: system-ui, -apple-system, sans-serif;
48
+ }
49
+ .gr-banner-form {
50
+ display: flex;
51
+ gap: 12px;
52
+ justify-content: center;
53
+ align-items: center;
54
+ flex-wrap: wrap;
55
+ }
56
+ .gr-brand {
57
+ font-weight: 900;
58
+ letter-spacing: 0.15em;
59
+ font-size: 12px;
60
+ text-transform: uppercase;
61
+ }
62
+ .gr-brand-stack {
63
+ display: flex;
64
+ flex-direction: column;
65
+ align-items: flex-start;
66
+ gap: 2px;
67
+ }
68
+ .gr-error {
69
+ color: var(--gr-danger);
70
+ font-size: 12px;
71
+ font-weight: 700;
72
+ }
73
+ .gr-input {
74
+ border: 1px solid var(--gr-border-strong);
75
+ border-radius: 4px;
76
+ padding: 6px 8px;
77
+ font-size: 13px;
78
+ color: var(--gr-fg);
79
+ background: var(--gr-input-bg);
80
+ outline: none;
81
+ }
82
+ .gr-primary-btn {
83
+ background: var(--gr-primary-bg);
84
+ color: var(--gr-primary-fg);
85
+ padding: 7px 16px;
86
+ border-radius: 4px;
87
+ font-size: 12px;
88
+ font-weight: 700;
89
+ border: none;
90
+ cursor: pointer;
91
+ }
92
+ .gr-toolbar {
93
+ position: fixed;
94
+ bottom: 24px;
95
+ left: 50%;
96
+ transform: translateX(-50%);
97
+ z-index: 9999;
98
+ background: var(--gr-overlay-bg-glass);
99
+ backdrop-filter: blur(8px);
100
+ color: var(--gr-overlay-fg);
101
+ padding: 12px 20px;
102
+ border-radius: 9999px;
103
+ box-shadow: 0 20px 40px var(--gr-shadow);
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 20px;
107
+ border: 1px solid var(--gr-overlay-border);
108
+ font-family: system-ui, -apple-system, sans-serif;
109
+ }
110
+ .gr-dot {
111
+ display: inline-block;
112
+ width: 8px;
113
+ height: 8px;
114
+ border-radius: 50%;
115
+ background: var(--gr-accent);
116
+ }
117
+ .gr-toolbar-label {
118
+ font-size: 10px;
119
+ font-weight: 900;
120
+ text-transform: uppercase;
121
+ letter-spacing: 0.2em;
122
+ color: var(--gr-label);
123
+ }
124
+ .gr-divider {
125
+ width: 1px;
126
+ height: 16px;
127
+ background: var(--gr-border-strong);
128
+ }
129
+ .gr-link-btn {
130
+ font-size: 10px;
131
+ font-weight: 700;
132
+ text-transform: uppercase;
133
+ letter-spacing: 0.15em;
134
+ color: var(--gr-muted);
135
+ background: none;
136
+ border: none;
137
+ cursor: pointer;
138
+ }
139
+ .gr-popover {
140
+ position: fixed;
141
+ bottom: 90px;
142
+ left: 50%;
143
+ transform: translateX(-50%);
144
+ z-index: 10001;
145
+ background: var(--gr-overlay-bg);
146
+ color: var(--gr-overlay-fg);
147
+ padding: 16px;
148
+ border-radius: 8px;
149
+ box-shadow: 0 20px 40px var(--gr-shadow);
150
+ border: 1px solid var(--gr-overlay-border);
151
+ min-width: 300px;
152
+ cursor: default;
153
+ font-family: system-ui, -apple-system, sans-serif;
154
+ }
155
+ .gr-label {
156
+ display: block;
157
+ font-size: 10px;
158
+ font-weight: 700;
159
+ color: var(--gr-subtle);
160
+ margin-bottom: 4px;
161
+ text-transform: uppercase;
162
+ }
163
+ .gr-field {
164
+ border: 1px solid var(--gr-border-strong);
165
+ border-radius: 4px;
166
+ padding: 6px 8px;
167
+ font-size: 13px;
168
+ color: var(--gr-fg);
169
+ background: var(--gr-input-bg);
170
+ width: 100%;
171
+ margin-bottom: 8px;
172
+ box-sizing: border-box;
173
+ }
174
+ .gr-row {
175
+ display: flex;
176
+ gap: 8px;
177
+ margin-top: 8px;
178
+ padding-top: 8px;
179
+ border-top: 1px solid var(--gr-border-soft);
180
+ }
181
+ .gr-cancel-btn {
182
+ background: var(--gr-soft);
183
+ color: var(--gr-fg2);
184
+ padding: 7px 14px;
185
+ border-radius: 4px;
186
+ font-size: 12px;
187
+ border: none;
188
+ cursor: pointer;
189
+ }
190
+ .gr-anchored-bar {
191
+ position: fixed;
192
+ z-index: 10001;
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 4px;
196
+ background: var(--gr-overlay-bg-glass);
197
+ color: var(--gr-overlay-fg);
198
+ backdrop-filter: blur(8px);
199
+ padding: 4px;
200
+ border-radius: 8px;
201
+ box-shadow: 0 8px 24px var(--gr-shadow);
202
+ border: 1px solid var(--gr-overlay-border);
203
+ font-family: system-ui, -apple-system, sans-serif;
204
+ }
205
+ .gr-anchored-panel {
206
+ position: fixed;
207
+ z-index: 10001;
208
+ max-height: calc(100vh - 24px);
209
+ overflow-y: auto;
210
+ background: var(--gr-overlay-bg);
211
+ color: var(--gr-overlay-fg);
212
+ padding: 12px;
213
+ border-radius: 8px;
214
+ box-shadow: 0 12px 32px var(--gr-shadow);
215
+ border: 1px solid var(--gr-overlay-border);
216
+ min-width: 260px;
217
+ max-width: min(340px, calc(100vw - 16px));
218
+ cursor: default;
219
+ font-family: system-ui, -apple-system, sans-serif;
220
+ }
221
+ .gr-icon-btn {
222
+ display: inline-flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ width: 26px;
226
+ height: 26px;
227
+ border-radius: 6px;
228
+ border: none;
229
+ background: transparent;
230
+ color: var(--gr-fg);
231
+ cursor: pointer;
232
+ padding: 0;
233
+ }
234
+ .gr-icon-btn-primary {
235
+ display: inline-flex;
236
+ align-items: center;
237
+ justify-content: center;
238
+ width: 26px;
239
+ height: 26px;
240
+ border-radius: 6px;
241
+ border: none;
242
+ background: var(--gr-accent);
243
+ color: var(--gr-on-accent);
244
+ cursor: pointer;
245
+ padding: 0;
246
+ }
247
+ .gr-scrim {
248
+ position: fixed;
249
+ inset: 0;
250
+ background: var(--gr-scrim);
251
+ z-index: 10002;
252
+ }
253
+ .gr-drawer {
254
+ position: fixed;
255
+ top: 0;
256
+ right: 0;
257
+ height: 100%;
258
+ width: min(420px, 100vw);
259
+ background: var(--gr-overlay-bg);
260
+ color: var(--gr-overlay-fg);
261
+ z-index: 10003;
262
+ box-shadow: -12px 0 32px var(--gr-shadow);
263
+ display: flex;
264
+ flex-direction: column;
265
+ font-family: system-ui, -apple-system, sans-serif;
266
+ }
267
+ .gr-drawer-head {
268
+ display: flex;
269
+ align-items: center;
270
+ justify-content: space-between;
271
+ padding: 16px 20px;
272
+ border-bottom: 1px solid var(--gr-overlay-border);
273
+ }
274
+ .gr-drawer-title {
275
+ font-size: 14px;
276
+ font-weight: 800;
277
+ margin: 0;
278
+ }
279
+ .gr-drawer-body {
280
+ padding: 20px;
281
+ overflow-y: auto;
282
+ flex: 1;
283
+ }
284
+ .gr-member-row {
285
+ display: flex;
286
+ align-items: center;
287
+ gap: 8px;
288
+ padding: 10px 0;
289
+ border-bottom: 1px solid var(--gr-border-soft);
290
+ }
291
+ .gr-member-email {
292
+ flex: 1;
293
+ font-size: 13px;
294
+ overflow: hidden;
295
+ text-overflow: ellipsis;
296
+ white-space: nowrap;
297
+ }
298
+ .gr-select {
299
+ border: 1px solid var(--gr-border-strong);
300
+ border-radius: 4px;
301
+ padding: 4px 6px;
302
+ font-size: 12px;
303
+ background: var(--gr-input-bg);
304
+ color: var(--gr-fg);
305
+ }
306
+ .gr-badge {
307
+ font-size: 9px;
308
+ font-weight: 800;
309
+ text-transform: uppercase;
310
+ letter-spacing: 0.1em;
311
+ padding: 2px 6px;
312
+ border-radius: 999px;
313
+ background: var(--gr-badge-bg);
314
+ color: var(--gr-badge-fg);
315
+ }
316
+ .gr-section-label {
317
+ font-size: 10px;
318
+ font-weight: 800;
319
+ text-transform: uppercase;
320
+ letter-spacing: 0.15em;
321
+ color: var(--gr-subtle);
322
+ margin: 20px 0 8px;
323
+ }
324
+ .gr-picker-scrim {
325
+ position: fixed;
326
+ inset: 0;
327
+ background: var(--gr-scrim-modal);
328
+ z-index: 10010;
329
+ }
330
+ .gr-picker {
331
+ position: fixed;
332
+ top: 50%;
333
+ left: 50%;
334
+ transform: translate(-50%, -50%);
335
+ width: min(720px, calc(100vw - 32px));
336
+ max-height: min(640px, calc(100vh - 32px));
337
+ background: var(--gr-overlay-bg);
338
+ color: var(--gr-overlay-fg);
339
+ border-radius: 12px;
340
+ z-index: 10011;
341
+ box-shadow: 0 24px 64px var(--gr-shadow);
342
+ display: flex;
343
+ flex-direction: column;
344
+ overflow: hidden;
345
+ font-family: system-ui, -apple-system, sans-serif;
346
+ }
347
+ .gr-picker-grid {
348
+ display: grid;
349
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
350
+ gap: 10px;
351
+ }
352
+ .gr-picker-tile {
353
+ position: relative;
354
+ aspect-ratio: 1;
355
+ border-radius: 8px;
356
+ overflow: hidden;
357
+ border: 2px solid transparent;
358
+ background: var(--gr-soft);
359
+ cursor: pointer;
360
+ padding: 0;
361
+ }
362
+ .gr-picker-tile-active {
363
+ border-color: var(--gr-accent);
364
+ }
365
+ .gr-picker-thumb {
366
+ width: 100%;
367
+ height: 100%;
368
+ object-fit: cover;
369
+ display: block;
370
+ }
371
+ .gr-dropzone {
372
+ border: 1.5px dashed var(--gr-border-strong);
373
+ border-radius: 8px;
374
+ padding: 16px;
375
+ text-align: center;
376
+ font-size: 13px;
377
+ color: var(--gr-muted);
378
+ margin-bottom: 14px;
379
+ cursor: pointer;
380
+ }
381
+ .gr-dropzone-active {
382
+ border-color: var(--gr-accent);
383
+ background: var(--gr-accent-soft);
384
+ color: var(--gr-accent-text);
385
+ }
386
+ .gr-preview-thumb {
387
+ width: 100%;
388
+ max-width: 300px;
389
+ height: auto;
390
+ max-height: 200px;
391
+ object-fit: contain;
392
+ background: var(--gr-soft);
393
+ border-radius: 6px;
394
+ display: block;
395
+ margin-bottom: 8px;
396
+ }
397
+ .gr-secondary-btn {
398
+ background: var(--gr-soft);
399
+ color: var(--gr-fg);
400
+ padding: 7px 12px;
401
+ border-radius: 4px;
402
+ font-size: 12px;
403
+ font-weight: 600;
404
+ border: none;
405
+ cursor: pointer;
406
+ width: 100%;
407
+ margin-bottom: 10px;
408
+ }
409
+ .gr-marker-outline {
410
+ position: fixed;
411
+ z-index: 9990;
412
+ pointer-events: none;
413
+ box-sizing: border-box;
414
+ border-radius: 2px;
415
+ }
416
+ .gr-marker-handle {
417
+ position: fixed;
418
+ z-index: 9991;
419
+ display: inline-flex;
420
+ align-items: center;
421
+ gap: 5px;
422
+ padding: 4px 9px 4px 7px;
423
+ border-radius: 6px;
424
+ border: none;
425
+ background: var(--gr-accent);
426
+ color: var(--gr-on-accent);
427
+ font-size: 11px;
428
+ font-weight: 700;
429
+ font-family: system-ui, -apple-system, sans-serif;
430
+ cursor: pointer;
431
+ box-shadow: 0 4px 12px var(--gr-shadow);
432
+ }
433
+ .gr-repeater-item {
434
+ position: relative;
435
+ outline: 1px dashed var(--gr-field-outline);
436
+ outline-offset: 2px;
437
+ }
438
+ .gr-repeater-toolbar {
439
+ position: absolute;
440
+ top: 4px;
441
+ right: 4px;
442
+ z-index: 20;
443
+ display: flex;
444
+ gap: 2px;
445
+ background: var(--gr-overlay-bg-glass);
446
+ color: var(--gr-overlay-fg);
447
+ backdrop-filter: blur(6px);
448
+ padding: 3px;
449
+ border-radius: 6px;
450
+ box-shadow: 0 4px 12px var(--gr-shadow);
451
+ border: 1px solid var(--gr-overlay-border);
452
+ }
453
+ .gr-repeater-add-row {
454
+ display: flex;
455
+ gap: 8px;
456
+ align-items: center;
457
+ margin-top: 8px;
458
+ font-family: system-ui, -apple-system, sans-serif;
459
+ }
460
+ .gr-repeater-add-btn {
461
+ display: inline-flex;
462
+ align-items: center;
463
+ gap: 6px;
464
+ padding: 8px 14px;
465
+ border-radius: 6px;
466
+ border: 1.5px dashed var(--gr-accent);
467
+ background: transparent;
468
+ color: var(--gr-accent);
469
+ font-size: 12px;
470
+ font-weight: 700;
471
+ cursor: pointer;
472
+ }
473
+ .gr-repeater-slug-input {
474
+ border: 1px solid var(--gr-border-strong);
475
+ border-radius: 4px;
476
+ padding: 7px 9px;
477
+ font-size: 13px;
478
+ color: var(--gr-fg);
479
+ background: var(--gr-input-bg);
480
+ min-width: 180px;
481
+ }
482
+
483
+ /* ---- modifiers and utilities ----------------------------------------------
484
+ * Single-class rules that follow the components above, so they win over a
485
+ * component's own value when both are on one element. */
486
+
487
+ .gr-text-fg { color: var(--gr-fg); }
488
+ .gr-text-muted { color: var(--gr-muted); }
489
+ .gr-text-subtle { color: var(--gr-subtle); }
490
+ .gr-text-danger { color: var(--gr-danger); }
491
+ .gr-fs-11 { font-size: 11px; }
492
+ .gr-fs-12 { font-size: 12px; }
493
+ .gr-fs-13 { font-size: 13px; }
494
+ .gr-lh-15 { line-height: 1.5; }
495
+ .gr-m-0 { margin: 0; }
496
+ .gr-mx-6 { margin-left: 6px; margin-right: 6px; }
497
+ .gr-mt-8 { margin-top: 8px; }
498
+ .gr-mt-10 { margin-top: 10px; }
499
+ .gr-mt-16 { margin-top: 16px; }
500
+ .gr-mb-0 { margin-bottom: 0; }
501
+ .gr-mb-8 { margin-bottom: 8px; }
502
+ .gr-mb-12 { margin-bottom: 12px; }
503
+ .gr-mb-16 { margin-bottom: 16px; }
504
+ .gr-p-0 { padding: 0; }
505
+ .gr-w-full { width: 100%; }
506
+ .gr-w-auto { width: auto; }
507
+ .gr-size-22 { width: 22px; height: 22px; }
508
+ .gr-size-24 { width: 24px; height: 24px; }
509
+ .gr-flex { display: flex; }
510
+ .gr-inline-flex { display: inline-flex; }
511
+ .gr-flex-1 { flex: 1; }
512
+ .gr-items-center { align-items: center; }
513
+ .gr-justify-between { justify-content: space-between; }
514
+ .gr-justify-end { justify-content: flex-end; }
515
+ .gr-gap-4 { gap: 4px; }
516
+ .gr-gap-6 { gap: 6px; }
517
+ .gr-gap-8 { gap: 8px; }
518
+ .gr-no-underline { text-decoration: none; }
519
+
520
+ /* A text-only button, e.g. "Forgot password?". */
521
+ .gr-text-btn {
522
+ background: none;
523
+ border: none;
524
+ cursor: pointer;
525
+ color: var(--gr-muted);
526
+ font-size: 12px;
527
+ text-decoration: underline;
528
+ padding: 0 4px;
529
+ white-space: nowrap;
530
+ }
531
+
532
+ /* ---- editable fields ------------------------------------------------------
533
+ * Drawn on the developer's own elements by <GRail>, which marks them with a
534
+ * data attribute instead of a style attribute. Images get a separate marker. */
535
+
536
+ [data-gr-field] { cursor: pointer; }
537
+ [data-gr-field="text"] { outline: 1px dashed var(--gr-field-outline); outline-offset: 2px; }
538
+ [data-gr-field="text"][data-gr-editing] { outline: 2px solid var(--gr-field-outline-active); cursor: text; }
539
+
540
+ .gr-marker-outline { border: 1px dashed var(--gr-field-outline); }
541
+ .gr-marker-outline[data-gr-editing] { border: 2px solid var(--gr-field-outline-active); }
542
+
543
+ /* ---- drawer animation ------------------------------------------------------ */
544
+ @keyframes gr-fade-in { from { opacity: 0 } to { opacity: 1 } }
545
+ @keyframes gr-slide-in {
546
+ from { transform: translateX(100%) }
547
+ to { transform: translateX(0) }
548
+ }
549
+ [data-guardrail-scrim] { animation: gr-fade-in 160ms ease-out both }
550
+ [data-guardrail-team] { animation: gr-slide-in 220ms cubic-bezier(0.32, 0.72, 0, 1) both }
551
+ [data-guardrail-team] [data-gr-row] { animation: gr-fade-in 240ms ease-out both }
552
+ @keyframes gr-fade-out { from { opacity: 1 } to { opacity: 0 } }
553
+ @keyframes gr-slide-out {
554
+ from { transform: translateX(0) }
555
+ to { transform: translateX(100%) }
556
+ }
557
+ [data-guardrail-scrim][data-closing] { animation: gr-fade-out 180ms ease-in both }
558
+ [data-guardrail-team][data-closing] { animation: gr-slide-out 180ms cubic-bezier(0.4, 0, 1, 1) both }
559
+ @media (prefers-reduced-motion: reduce) {
560
+ [data-guardrail-scrim], [data-guardrail-team], [data-guardrail-team] [data-gr-row] {
561
+ animation: none
562
+ }
563
+ }
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@guard-rail/core",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Lightweight inline content editing for developer-owned Next.js sites.",
5
5
  "license": "ISC",
6
6
  "type": "module",
7
- "sideEffects": false,
7
+ "sideEffects": [
8
+ "**/*.css"
9
+ ],
8
10
  "types": "./dist/index.d.ts",
9
11
  "exports": {
10
12
  ".": {
@@ -20,6 +22,7 @@
20
22
  "types": "./dist/index.client.d.ts",
21
23
  "default": "./dist/index.client.js"
22
24
  },
25
+ "./styles.css": "./dist/react/guardrail.css",
23
26
  "./adapters/supabase": {
24
27
  "types": "./dist/adapters/supabase/index.d.ts",
25
28
  "default": "./dist/adapters/supabase/index.js"
@@ -38,13 +41,6 @@
38
41
  "publishConfig": {
39
42
  "access": "public"
40
43
  },
41
- "scripts": {
42
- "build": "tsup",
43
- "dev": "tsup --watch",
44
- "typecheck": "tsc --noEmit",
45
- "prepare": "pnpm run build",
46
- "prepublishOnly": "pnpm run build"
47
- },
48
44
  "peerDependencies": {
49
45
  "@supabase/ssr": ">=0.8.0 <1",
50
46
  "@supabase/supabase-js": "^2.0.0",
@@ -69,5 +65,10 @@
69
65
  "react": "^19.2.4",
70
66
  "tsup": "^8.5.1",
71
67
  "typescript": "^5.9.3"
68
+ },
69
+ "scripts": {
70
+ "build": "tsup",
71
+ "dev": "tsup --watch",
72
+ "typecheck": "tsc --noEmit"
72
73
  }
73
- }
74
+ }