@realiizlabs/admin 0.14.0 → 0.15.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/LICENSE +21 -0
- package/dist/{Sidebar-Dvrc9_xR.d.cts → Sidebar-Dz_qJOSx.d.cts} +3 -1
- package/dist/{Sidebar-Dvrc9_xR.d.ts → Sidebar-Dz_qJOSx.d.ts} +3 -1
- package/dist/bar/index.cjs +82 -0
- package/dist/bar/index.cjs.map +1 -0
- package/dist/bar/index.d.cts +18 -0
- package/dist/bar/index.d.ts +18 -0
- package/dist/bar/index.js +77 -0
- package/dist/bar/index.js.map +1 -0
- package/dist/shell/index.cjs +60 -38
- package/dist/shell/index.cjs.map +1 -1
- package/dist/shell/index.d.cts +9 -5
- package/dist/shell/index.d.ts +9 -5
- package/dist/shell/index.js +60 -38
- package/dist/shell/index.js.map +1 -1
- package/dist/studio/index.cjs +80 -2
- package/dist/studio/index.cjs.map +1 -1
- package/dist/studio/index.d.cts +32 -1
- package/dist/studio/index.d.ts +32 -1
- package/dist/studio/index.js +78 -3
- package/dist/studio/index.js.map +1 -1
- package/dist/studio-ui/index.cjs +305 -90
- package/dist/studio-ui/index.cjs.map +1 -1
- package/dist/studio-ui/index.d.cts +37 -6
- package/dist/studio-ui/index.d.ts +37 -6
- package/dist/studio-ui/index.js +303 -92
- package/dist/studio-ui/index.js.map +1 -1
- package/dist/version-DMS0azUx.d.cts +3 -0
- package/dist/version-DMS0azUx.d.ts +3 -0
- package/package.json +12 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Realiiz Studio License (source-available)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Realiiz Labs LLC. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This package (@realiizlabs/admin, "Studio") is published so that websites built
|
|
6
|
+
from the Realiiz site-starter-pro template can install and update it without
|
|
7
|
+
credentials. It is not open source.
|
|
8
|
+
|
|
9
|
+
You may install, run and modify this package as part of a website that you or
|
|
10
|
+
your organization built from a licensed copy of site-starter-pro, or that
|
|
11
|
+
Realiiz built for you. You may keep it installed for as long as that website
|
|
12
|
+
exists, including after your relationship with Realiiz ends.
|
|
13
|
+
|
|
14
|
+
You may not redistribute this package, sell it, offer it as part of another
|
|
15
|
+
template, product or service, or remove this notice.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. In no event
|
|
18
|
+
shall Realiiz Labs LLC be liable for any claim, damages or other liability
|
|
19
|
+
arising from the use of the software.
|
|
20
|
+
|
|
21
|
+
Questions: hello@realiiz.com
|
|
@@ -23,7 +23,7 @@ interface ShellUser {
|
|
|
23
23
|
}
|
|
24
24
|
declare function firstNameOf(user: ShellUser): string;
|
|
25
25
|
declare function initialsOf(user: ShellUser): string;
|
|
26
|
-
declare function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref }: {
|
|
26
|
+
declare function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref, siteUrl }: {
|
|
27
27
|
user: ShellUser;
|
|
28
28
|
role: string;
|
|
29
29
|
theme: Theme;
|
|
@@ -33,6 +33,8 @@ declare function AccountMenu({ user, role, theme, onTheme, signOutPath, accountH
|
|
|
33
33
|
accountHref?: string;
|
|
34
34
|
/** The Help center. Omit to hide the item. */
|
|
35
35
|
helpHref?: string;
|
|
36
|
+
/** The live website — a "View website" item that opens in a new tab. Omit to hide it. */
|
|
37
|
+
siteUrl?: string | null;
|
|
36
38
|
}): react.JSX.Element;
|
|
37
39
|
|
|
38
40
|
/**
|
|
@@ -23,7 +23,7 @@ interface ShellUser {
|
|
|
23
23
|
}
|
|
24
24
|
declare function firstNameOf(user: ShellUser): string;
|
|
25
25
|
declare function initialsOf(user: ShellUser): string;
|
|
26
|
-
declare function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref }: {
|
|
26
|
+
declare function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref, siteUrl }: {
|
|
27
27
|
user: ShellUser;
|
|
28
28
|
role: string;
|
|
29
29
|
theme: Theme;
|
|
@@ -33,6 +33,8 @@ declare function AccountMenu({ user, role, theme, onTheme, signOutPath, accountH
|
|
|
33
33
|
accountHref?: string;
|
|
34
34
|
/** The Help center. Omit to hide the item. */
|
|
35
35
|
helpHref?: string;
|
|
36
|
+
/** The live website — a "View website" item that opens in a new tab. Omit to hide it. */
|
|
37
|
+
siteUrl?: string | null;
|
|
36
38
|
}): react.JSX.Element;
|
|
37
39
|
|
|
38
40
|
/**
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
// src/bar/StudioBar.tsx
|
|
8
|
+
var STUDIO_MARKER_COOKIE = "rz-studio";
|
|
9
|
+
var HIDE_KEY = "rz-bar-hidden";
|
|
10
|
+
function hasStudioMarker(cookie = typeof document === "undefined" ? "" : document.cookie) {
|
|
11
|
+
return cookie.split(";").some((c) => c.trim().startsWith(`${STUDIO_MARKER_COOKIE}=1`));
|
|
12
|
+
}
|
|
13
|
+
function editHrefFor(pathname, routes, adminPath = "/admin") {
|
|
14
|
+
for (const [prefix, typeId] of Object.entries(routes)) {
|
|
15
|
+
if (!pathname.startsWith(prefix)) continue;
|
|
16
|
+
const rest = pathname.slice(prefix.length).replace(/\/+$/, "");
|
|
17
|
+
if (!rest || rest.includes("/")) continue;
|
|
18
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(rest)) continue;
|
|
19
|
+
return `${adminPath}/${typeId}/${rest}`;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function StudioBar({ routes = {}, adminPath = "/admin", label = "Studio" }) {
|
|
24
|
+
const [state, setState] = react.useState({ show: false, path: "" });
|
|
25
|
+
react.useEffect(() => {
|
|
26
|
+
const path = window.location.pathname;
|
|
27
|
+
if (path === adminPath || path.startsWith(`${adminPath}/`)) return;
|
|
28
|
+
let hidden = false;
|
|
29
|
+
try {
|
|
30
|
+
hidden = sessionStorage.getItem(HIDE_KEY) === "1";
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
if (!hidden && hasStudioMarker()) setState({ show: true, path });
|
|
34
|
+
}, [adminPath]);
|
|
35
|
+
if (!state.show) return null;
|
|
36
|
+
const edit = editHrefFor(state.path, routes, adminPath);
|
|
37
|
+
const close = () => {
|
|
38
|
+
try {
|
|
39
|
+
sessionStorage.setItem(HIDE_KEY, "1");
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
setState({ show: false, path: "" });
|
|
43
|
+
};
|
|
44
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.wrap, role: "region", "aria-label": "Studio", "data-studio-bar": true, children: [
|
|
45
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: S.brand, children: [
|
|
46
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3l7 3v5c0 5-3.5 8.5-7 10-3.5-1.5-7-5-7-10V6z" }) }),
|
|
47
|
+
label
|
|
48
|
+
] }),
|
|
49
|
+
edit && /* @__PURE__ */ jsxRuntime.jsx("a", { href: edit, style: S.link, children: "Edit this page" }),
|
|
50
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: adminPath, style: S.link, children: "Dashboard" }),
|
|
51
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: close, style: S.close, "aria-label": "Hide the Studio bar for this tab", children: "\xD7" })
|
|
52
|
+
] });
|
|
53
|
+
}
|
|
54
|
+
var S = {
|
|
55
|
+
wrap: {
|
|
56
|
+
position: "fixed",
|
|
57
|
+
left: 16,
|
|
58
|
+
bottom: 16,
|
|
59
|
+
zIndex: 2147483e3,
|
|
60
|
+
display: "flex",
|
|
61
|
+
alignItems: "center",
|
|
62
|
+
gap: 2,
|
|
63
|
+
padding: "4px 6px 4px 10px",
|
|
64
|
+
borderRadius: 999,
|
|
65
|
+
background: "#0b1220",
|
|
66
|
+
color: "#e5e7eb",
|
|
67
|
+
boxShadow: "0 6px 24px rgba(0,0,0,.28)",
|
|
68
|
+
font: "500 13px/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
|
|
69
|
+
letterSpacing: ".01em",
|
|
70
|
+
WebkitFontSmoothing: "antialiased"
|
|
71
|
+
},
|
|
72
|
+
brand: { display: "inline-flex", alignItems: "center", gap: 6, paddingRight: 8, marginRight: 4, borderRight: "1px solid rgba(255,255,255,.18)", color: "#a5b4fc", fontWeight: 600 },
|
|
73
|
+
link: { color: "#e5e7eb", textDecoration: "none", padding: "6px 8px", borderRadius: 999 },
|
|
74
|
+
close: { marginLeft: 2, width: 26, height: 26, border: 0, borderRadius: 999, background: "transparent", color: "#9ca3af", fontSize: 16, lineHeight: 1, cursor: "pointer" }
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
exports.STUDIO_MARKER_COOKIE = STUDIO_MARKER_COOKIE;
|
|
78
|
+
exports.StudioBar = StudioBar;
|
|
79
|
+
exports.editHrefFor = editHrefFor;
|
|
80
|
+
exports.hasStudioMarker = hasStudioMarker;
|
|
81
|
+
//# sourceMappingURL=index.cjs.map
|
|
82
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bar/StudioBar.tsx"],"names":["useState","useEffect","jsxs","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["\"use client\";\n\nimport { useEffect, useState, type CSSProperties } from \"react\";\n\nexport const STUDIO_MARKER_COOKIE = \"rz-studio\";\nconst HIDE_KEY = \"rz-bar-hidden\";\n\nexport interface StudioBarProps {\n /** Public URL prefix → content type id, e.g. { \"/blog/\": \"posts\", \"/events/\": \"events\" }. */\n routes?: Record<string, string>;\n /** Where Studio lives. Default \"/admin\". */\n adminPath?: string;\n /** Shown on the pill. Default \"Studio\". */\n label?: string;\n}\n\n/** True when Studio's marker cookie is present (someone signed in to Studio in this browser). */\nexport function hasStudioMarker(cookie: string = typeof document === \"undefined\" ? \"\" : document.cookie): boolean {\n return cookie.split(\";\").some((c) => c.trim().startsWith(`${STUDIO_MARKER_COOKIE}=1`));\n}\n\n/** The Studio edit page for a public path, or null when the path isn't an item Studio manages. */\nexport function editHrefFor(pathname: string, routes: Record<string, string>, adminPath = \"/admin\"): string | null {\n for (const [prefix, typeId] of Object.entries(routes)) {\n if (!pathname.startsWith(prefix)) continue;\n const rest = pathname.slice(prefix.length).replace(/\\/+$/, \"\");\n if (!rest || rest.includes(\"/\")) continue; // the index page, or something nested — not an item\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(rest)) continue;\n return `${adminPath}/${typeId}/${rest}`;\n }\n return null;\n}\n\nexport function StudioBar({ routes = {}, adminPath = \"/admin\", label = \"Studio\" }: StudioBarProps) {\n const [state, setState] = useState<{ show: boolean; path: string }>({ show: false, path: \"\" });\n\n useEffect(() => {\n const path = window.location.pathname;\n if (path === adminPath || path.startsWith(`${adminPath}/`)) return;\n let hidden = false;\n try { hidden = sessionStorage.getItem(HIDE_KEY) === \"1\"; } catch { /* private mode */ }\n if (!hidden && hasStudioMarker()) setState({ show: true, path });\n }, [adminPath]);\n\n if (!state.show) return null;\n const edit = editHrefFor(state.path, routes, adminPath);\n const close = () => { try { sessionStorage.setItem(HIDE_KEY, \"1\"); } catch { /* private mode */ } setState({ show: false, path: \"\" }); };\n\n return (\n <div style={S.wrap} role=\"region\" aria-label=\"Studio\" data-studio-bar>\n <span style={S.brand}>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.4\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 3l7 3v5c0 5-3.5 8.5-7 10-3.5-1.5-7-5-7-10V6z\" /></svg>\n {label}\n </span>\n {edit && <a href={edit} style={S.link}>Edit this page</a>}\n <a href={adminPath} style={S.link}>Dashboard</a>\n <button type=\"button\" onClick={close} style={S.close} aria-label=\"Hide the Studio bar for this tab\">×</button>\n </div>\n );\n}\n\n/* Self-contained styles: no dependency on the site's CSS, fonts or theme. */\nconst S: Record<string, CSSProperties> = {\n wrap: {\n position: \"fixed\", left: 16, bottom: 16, zIndex: 2147483000,\n display: \"flex\", alignItems: \"center\", gap: 2,\n padding: \"4px 6px 4px 10px\", borderRadius: 999,\n background: \"#0b1220\", color: \"#e5e7eb\", boxShadow: \"0 6px 24px rgba(0,0,0,.28)\",\n font: \"500 13px/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif\", letterSpacing: \".01em\",\n WebkitFontSmoothing: \"antialiased\",\n },\n brand: { display: \"inline-flex\", alignItems: \"center\", gap: 6, paddingRight: 8, marginRight: 4, borderRight: \"1px solid rgba(255,255,255,.18)\", color: \"#a5b4fc\", fontWeight: 600 },\n link: { color: \"#e5e7eb\", textDecoration: \"none\", padding: \"6px 8px\", borderRadius: 999 },\n close: { marginLeft: 2, width: 26, height: 26, border: 0, borderRadius: 999, background: \"transparent\", color: \"#9ca3af\", fontSize: 16, lineHeight: 1, cursor: \"pointer\" },\n};\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
declare const STUDIO_MARKER_COOKIE = "rz-studio";
|
|
4
|
+
interface StudioBarProps {
|
|
5
|
+
/** Public URL prefix → content type id, e.g. { "/blog/": "posts", "/events/": "events" }. */
|
|
6
|
+
routes?: Record<string, string>;
|
|
7
|
+
/** Where Studio lives. Default "/admin". */
|
|
8
|
+
adminPath?: string;
|
|
9
|
+
/** Shown on the pill. Default "Studio". */
|
|
10
|
+
label?: string;
|
|
11
|
+
}
|
|
12
|
+
/** True when Studio's marker cookie is present (someone signed in to Studio in this browser). */
|
|
13
|
+
declare function hasStudioMarker(cookie?: string): boolean;
|
|
14
|
+
/** The Studio edit page for a public path, or null when the path isn't an item Studio manages. */
|
|
15
|
+
declare function editHrefFor(pathname: string, routes: Record<string, string>, adminPath?: string): string | null;
|
|
16
|
+
declare function StudioBar({ routes, adminPath, label }: StudioBarProps): react.JSX.Element | null;
|
|
17
|
+
|
|
18
|
+
export { STUDIO_MARKER_COOKIE, StudioBar, type StudioBarProps, editHrefFor, hasStudioMarker };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
declare const STUDIO_MARKER_COOKIE = "rz-studio";
|
|
4
|
+
interface StudioBarProps {
|
|
5
|
+
/** Public URL prefix → content type id, e.g. { "/blog/": "posts", "/events/": "events" }. */
|
|
6
|
+
routes?: Record<string, string>;
|
|
7
|
+
/** Where Studio lives. Default "/admin". */
|
|
8
|
+
adminPath?: string;
|
|
9
|
+
/** Shown on the pill. Default "Studio". */
|
|
10
|
+
label?: string;
|
|
11
|
+
}
|
|
12
|
+
/** True when Studio's marker cookie is present (someone signed in to Studio in this browser). */
|
|
13
|
+
declare function hasStudioMarker(cookie?: string): boolean;
|
|
14
|
+
/** The Studio edit page for a public path, or null when the path isn't an item Studio manages. */
|
|
15
|
+
declare function editHrefFor(pathname: string, routes: Record<string, string>, adminPath?: string): string | null;
|
|
16
|
+
declare function StudioBar({ routes, adminPath, label }: StudioBarProps): react.JSX.Element | null;
|
|
17
|
+
|
|
18
|
+
export { STUDIO_MARKER_COOKIE, StudioBar, type StudioBarProps, editHrefFor, hasStudioMarker };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// src/bar/StudioBar.tsx
|
|
6
|
+
var STUDIO_MARKER_COOKIE = "rz-studio";
|
|
7
|
+
var HIDE_KEY = "rz-bar-hidden";
|
|
8
|
+
function hasStudioMarker(cookie = typeof document === "undefined" ? "" : document.cookie) {
|
|
9
|
+
return cookie.split(";").some((c) => c.trim().startsWith(`${STUDIO_MARKER_COOKIE}=1`));
|
|
10
|
+
}
|
|
11
|
+
function editHrefFor(pathname, routes, adminPath = "/admin") {
|
|
12
|
+
for (const [prefix, typeId] of Object.entries(routes)) {
|
|
13
|
+
if (!pathname.startsWith(prefix)) continue;
|
|
14
|
+
const rest = pathname.slice(prefix.length).replace(/\/+$/, "");
|
|
15
|
+
if (!rest || rest.includes("/")) continue;
|
|
16
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(rest)) continue;
|
|
17
|
+
return `${adminPath}/${typeId}/${rest}`;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function StudioBar({ routes = {}, adminPath = "/admin", label = "Studio" }) {
|
|
22
|
+
const [state, setState] = useState({ show: false, path: "" });
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const path = window.location.pathname;
|
|
25
|
+
if (path === adminPath || path.startsWith(`${adminPath}/`)) return;
|
|
26
|
+
let hidden = false;
|
|
27
|
+
try {
|
|
28
|
+
hidden = sessionStorage.getItem(HIDE_KEY) === "1";
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
if (!hidden && hasStudioMarker()) setState({ show: true, path });
|
|
32
|
+
}, [adminPath]);
|
|
33
|
+
if (!state.show) return null;
|
|
34
|
+
const edit = editHrefFor(state.path, routes, adminPath);
|
|
35
|
+
const close = () => {
|
|
36
|
+
try {
|
|
37
|
+
sessionStorage.setItem(HIDE_KEY, "1");
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
setState({ show: false, path: "" });
|
|
41
|
+
};
|
|
42
|
+
return /* @__PURE__ */ jsxs("div", { style: S.wrap, role: "region", "aria-label": "Studio", "data-studio-bar": true, children: [
|
|
43
|
+
/* @__PURE__ */ jsxs("span", { style: S.brand, children: [
|
|
44
|
+
/* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M12 3l7 3v5c0 5-3.5 8.5-7 10-3.5-1.5-7-5-7-10V6z" }) }),
|
|
45
|
+
label
|
|
46
|
+
] }),
|
|
47
|
+
edit && /* @__PURE__ */ jsx("a", { href: edit, style: S.link, children: "Edit this page" }),
|
|
48
|
+
/* @__PURE__ */ jsx("a", { href: adminPath, style: S.link, children: "Dashboard" }),
|
|
49
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: close, style: S.close, "aria-label": "Hide the Studio bar for this tab", children: "\xD7" })
|
|
50
|
+
] });
|
|
51
|
+
}
|
|
52
|
+
var S = {
|
|
53
|
+
wrap: {
|
|
54
|
+
position: "fixed",
|
|
55
|
+
left: 16,
|
|
56
|
+
bottom: 16,
|
|
57
|
+
zIndex: 2147483e3,
|
|
58
|
+
display: "flex",
|
|
59
|
+
alignItems: "center",
|
|
60
|
+
gap: 2,
|
|
61
|
+
padding: "4px 6px 4px 10px",
|
|
62
|
+
borderRadius: 999,
|
|
63
|
+
background: "#0b1220",
|
|
64
|
+
color: "#e5e7eb",
|
|
65
|
+
boxShadow: "0 6px 24px rgba(0,0,0,.28)",
|
|
66
|
+
font: "500 13px/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
|
|
67
|
+
letterSpacing: ".01em",
|
|
68
|
+
WebkitFontSmoothing: "antialiased"
|
|
69
|
+
},
|
|
70
|
+
brand: { display: "inline-flex", alignItems: "center", gap: 6, paddingRight: 8, marginRight: 4, borderRight: "1px solid rgba(255,255,255,.18)", color: "#a5b4fc", fontWeight: 600 },
|
|
71
|
+
link: { color: "#e5e7eb", textDecoration: "none", padding: "6px 8px", borderRadius: 999 },
|
|
72
|
+
close: { marginLeft: 2, width: 26, height: 26, border: 0, borderRadius: 999, background: "transparent", color: "#9ca3af", fontSize: 16, lineHeight: 1, cursor: "pointer" }
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export { STUDIO_MARKER_COOKIE, StudioBar, editHrefFor, hasStudioMarker };
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bar/StudioBar.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useState, type CSSProperties } from \"react\";\n\nexport const STUDIO_MARKER_COOKIE = \"rz-studio\";\nconst HIDE_KEY = \"rz-bar-hidden\";\n\nexport interface StudioBarProps {\n /** Public URL prefix → content type id, e.g. { \"/blog/\": \"posts\", \"/events/\": \"events\" }. */\n routes?: Record<string, string>;\n /** Where Studio lives. Default \"/admin\". */\n adminPath?: string;\n /** Shown on the pill. Default \"Studio\". */\n label?: string;\n}\n\n/** True when Studio's marker cookie is present (someone signed in to Studio in this browser). */\nexport function hasStudioMarker(cookie: string = typeof document === \"undefined\" ? \"\" : document.cookie): boolean {\n return cookie.split(\";\").some((c) => c.trim().startsWith(`${STUDIO_MARKER_COOKIE}=1`));\n}\n\n/** The Studio edit page for a public path, or null when the path isn't an item Studio manages. */\nexport function editHrefFor(pathname: string, routes: Record<string, string>, adminPath = \"/admin\"): string | null {\n for (const [prefix, typeId] of Object.entries(routes)) {\n if (!pathname.startsWith(prefix)) continue;\n const rest = pathname.slice(prefix.length).replace(/\\/+$/, \"\");\n if (!rest || rest.includes(\"/\")) continue; // the index page, or something nested — not an item\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(rest)) continue;\n return `${adminPath}/${typeId}/${rest}`;\n }\n return null;\n}\n\nexport function StudioBar({ routes = {}, adminPath = \"/admin\", label = \"Studio\" }: StudioBarProps) {\n const [state, setState] = useState<{ show: boolean; path: string }>({ show: false, path: \"\" });\n\n useEffect(() => {\n const path = window.location.pathname;\n if (path === adminPath || path.startsWith(`${adminPath}/`)) return;\n let hidden = false;\n try { hidden = sessionStorage.getItem(HIDE_KEY) === \"1\"; } catch { /* private mode */ }\n if (!hidden && hasStudioMarker()) setState({ show: true, path });\n }, [adminPath]);\n\n if (!state.show) return null;\n const edit = editHrefFor(state.path, routes, adminPath);\n const close = () => { try { sessionStorage.setItem(HIDE_KEY, \"1\"); } catch { /* private mode */ } setState({ show: false, path: \"\" }); };\n\n return (\n <div style={S.wrap} role=\"region\" aria-label=\"Studio\" data-studio-bar>\n <span style={S.brand}>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.4\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 3l7 3v5c0 5-3.5 8.5-7 10-3.5-1.5-7-5-7-10V6z\" /></svg>\n {label}\n </span>\n {edit && <a href={edit} style={S.link}>Edit this page</a>}\n <a href={adminPath} style={S.link}>Dashboard</a>\n <button type=\"button\" onClick={close} style={S.close} aria-label=\"Hide the Studio bar for this tab\">×</button>\n </div>\n );\n}\n\n/* Self-contained styles: no dependency on the site's CSS, fonts or theme. */\nconst S: Record<string, CSSProperties> = {\n wrap: {\n position: \"fixed\", left: 16, bottom: 16, zIndex: 2147483000,\n display: \"flex\", alignItems: \"center\", gap: 2,\n padding: \"4px 6px 4px 10px\", borderRadius: 999,\n background: \"#0b1220\", color: \"#e5e7eb\", boxShadow: \"0 6px 24px rgba(0,0,0,.28)\",\n font: \"500 13px/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif\", letterSpacing: \".01em\",\n WebkitFontSmoothing: \"antialiased\",\n },\n brand: { display: \"inline-flex\", alignItems: \"center\", gap: 6, paddingRight: 8, marginRight: 4, borderRight: \"1px solid rgba(255,255,255,.18)\", color: \"#a5b4fc\", fontWeight: 600 },\n link: { color: \"#e5e7eb\", textDecoration: \"none\", padding: \"6px 8px\", borderRadius: 999 },\n close: { marginLeft: 2, width: 26, height: 26, border: 0, borderRadius: 999, background: \"transparent\", color: \"#9ca3af\", fontSize: 16, lineHeight: 1, cursor: \"pointer\" },\n};\n"]}
|
package/dist/shell/index.cjs
CHANGED
|
@@ -138,6 +138,35 @@ function Sidebar({ items, pinned = [], activeHref, basePath }) {
|
|
|
138
138
|
)
|
|
139
139
|
] });
|
|
140
140
|
}
|
|
141
|
+
function buttonClass(variant = "secondary", size = "md", extra) {
|
|
142
|
+
return ["rz-btn", `rz-btn--${variant}`, `rz-btn--${size}`, extra].filter(Boolean).join(" ");
|
|
143
|
+
}
|
|
144
|
+
function buttonStyle(variant = "secondary", size = "md") {
|
|
145
|
+
return { className: buttonClass(variant, size) };
|
|
146
|
+
}
|
|
147
|
+
function Button({ variant = "secondary", size = "md", className, type = "button", ...rest }) {
|
|
148
|
+
return /* @__PURE__ */ jsxRuntime.jsx("button", { type, className: buttonClass(variant, size, className), ...rest });
|
|
149
|
+
}
|
|
150
|
+
function LinkButton({ variant = "secondary", size = "md", className, external, forward, children, ...rest }) {
|
|
151
|
+
const extra = external ? { target: "_blank", rel: "noreferrer", ...rest } : rest;
|
|
152
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("a", { className: buttonClass(variant, size, className), ...extra, children: [
|
|
153
|
+
children,
|
|
154
|
+
external && /* @__PURE__ */ jsxRuntime.jsx(OutboundArrow, {}),
|
|
155
|
+
forward && !external && /* @__PURE__ */ jsxRuntime.jsx(ForwardArrow, {})
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
158
|
+
function ForwardArrow() {
|
|
159
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "rz-arrow rz-arrow--fwd", children: [
|
|
160
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }),
|
|
161
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m13 6 6 6-6 6" })
|
|
162
|
+
] });
|
|
163
|
+
}
|
|
164
|
+
function OutboundArrow() {
|
|
165
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "rz-arrow", children: [
|
|
166
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 17 17 7" }),
|
|
167
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 7h10v10" })
|
|
168
|
+
] });
|
|
169
|
+
}
|
|
141
170
|
var THEME_KEY = "rz-admin-theme";
|
|
142
171
|
function useTheme(defaultTheme = "light") {
|
|
143
172
|
const [theme, setTheme] = react.useState(defaultTheme);
|
|
@@ -189,12 +218,16 @@ var HelpIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...ico, children:
|
|
|
189
218
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 4h6a4 4 0 0 1 4 4v12a3 3 0 0 0-3-3H2z" }),
|
|
190
219
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 4h-6a4 4 0 0 0-4 4v12a3 3 0 0 1 3-3h7z" })
|
|
191
220
|
] });
|
|
221
|
+
var GlobeIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...ico, children: [
|
|
222
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
223
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" })
|
|
224
|
+
] });
|
|
192
225
|
var OutIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...ico, children: [
|
|
193
226
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
|
|
194
227
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 17l5-5-5-5" }),
|
|
195
228
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12H9" })
|
|
196
229
|
] });
|
|
197
|
-
function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
|
|
230
|
+
function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref, siteUrl }) {
|
|
198
231
|
const [open, setOpen] = react.useState(false);
|
|
199
232
|
const wrap = react.useRef(null);
|
|
200
233
|
react.useEffect(() => {
|
|
@@ -229,7 +262,12 @@ function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, hel
|
|
|
229
262
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Theme" }),
|
|
230
263
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-menu__end", children: /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, { theme, onChange: onTheme }) })
|
|
231
264
|
] }),
|
|
232
|
-
(accountHref || helpHref) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__sep" }),
|
|
265
|
+
(siteUrl || accountHref || helpHref) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__sep" }),
|
|
266
|
+
siteUrl && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: siteUrl, target: "_blank", rel: "noreferrer", role: "menuitem", className: "rz-menu__item", children: [
|
|
267
|
+
/* @__PURE__ */ jsxRuntime.jsx(GlobeIcon, {}),
|
|
268
|
+
"View website",
|
|
269
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-menu__end", children: /* @__PURE__ */ jsxRuntime.jsx(OutboundArrow, {}) })
|
|
270
|
+
] }),
|
|
233
271
|
accountHref && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: accountHref, role: "menuitem", className: "rz-menu__item", children: [
|
|
234
272
|
/* @__PURE__ */ jsxRuntime.jsx(UserIcon, {}),
|
|
235
273
|
"Account"
|
|
@@ -246,19 +284,28 @@ function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, hel
|
|
|
246
284
|
] })
|
|
247
285
|
] });
|
|
248
286
|
}
|
|
249
|
-
function TopBar({ siteName, productName, logoUrl, homeHref, user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
|
|
287
|
+
function TopBar({ siteName, productName, logoUrl, homeHref, siteUrl, user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
|
|
250
288
|
return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "rz-topbar", children: [
|
|
251
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
289
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
290
|
+
"a",
|
|
291
|
+
{
|
|
292
|
+
href: siteUrl || homeHref,
|
|
293
|
+
className: "rz-topbar__brand",
|
|
294
|
+
...siteUrl ? { target: "_blank", rel: "noreferrer", title: "Open the live website in a new tab" } : {},
|
|
295
|
+
children: [
|
|
296
|
+
logoUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoUrl, alt: "", className: "rz-topbar__logo" }),
|
|
297
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: siteName }),
|
|
298
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-topbar__product", children: productName }),
|
|
299
|
+
siteUrl && /* @__PURE__ */ jsxRuntime.jsx(OutboundArrow, {})
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
),
|
|
256
303
|
user && role && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-topbar__right", children: [
|
|
257
304
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rz-topbar__greeting", "data-testid": "rz-greeting", children: [
|
|
258
305
|
"Welcome back, ",
|
|
259
306
|
firstNameOf(user)
|
|
260
307
|
] }),
|
|
261
|
-
/* @__PURE__ */ jsxRuntime.jsx(AccountMenu, { user, role, theme, onTheme, signOutPath, accountHref, helpHref })
|
|
308
|
+
/* @__PURE__ */ jsxRuntime.jsx(AccountMenu, { user, role, theme, onTheme, signOutPath, accountHref, helpHref, siteUrl })
|
|
262
309
|
] })
|
|
263
310
|
] });
|
|
264
311
|
}
|
|
@@ -318,6 +365,8 @@ var SHELL_CSS = `
|
|
|
318
365
|
.rz-topbar__brand { display: flex; align-items: center; gap: .6rem; font-weight: 600; font-size: .9375rem; text-decoration: none; }
|
|
319
366
|
.rz-topbar__logo { height: 22px; width: auto; display: block; }
|
|
320
367
|
.rz-topbar__product { font-weight: 400; color: var(--text-secondary); }
|
|
368
|
+
.rz-topbar__brand .rz-arrow { color: var(--text-muted); opacity: 0; transition: opacity .15s, transform .15s; }
|
|
369
|
+
.rz-topbar__brand:hover .rz-arrow { opacity: 1; }
|
|
321
370
|
.rz-topbar__right { margin-left: auto; display: flex; align-items: center; gap: 1rem; }
|
|
322
371
|
.rz-topbar__greeting { color: var(--text-secondary); font-size: .8rem; }
|
|
323
372
|
.rz-body { display: flex; flex: 1; min-height: 0; }
|
|
@@ -529,6 +578,7 @@ function AdminShell({
|
|
|
529
578
|
signOutPath,
|
|
530
579
|
accountHref,
|
|
531
580
|
helpHref,
|
|
581
|
+
siteUrl,
|
|
532
582
|
branding,
|
|
533
583
|
defaultTheme = "light",
|
|
534
584
|
children
|
|
@@ -545,6 +595,7 @@ function AdminShell({
|
|
|
545
595
|
siteName: shownName,
|
|
546
596
|
productName,
|
|
547
597
|
logoUrl: shownLogo,
|
|
598
|
+
siteUrl,
|
|
548
599
|
homeHref: basePath,
|
|
549
600
|
user,
|
|
550
601
|
role,
|
|
@@ -561,35 +612,6 @@ function AdminShell({
|
|
|
561
612
|
] })
|
|
562
613
|
] });
|
|
563
614
|
}
|
|
564
|
-
function buttonClass(variant = "secondary", size = "md", extra) {
|
|
565
|
-
return ["rz-btn", `rz-btn--${variant}`, `rz-btn--${size}`, extra].filter(Boolean).join(" ");
|
|
566
|
-
}
|
|
567
|
-
function buttonStyle(variant = "secondary", size = "md") {
|
|
568
|
-
return { className: buttonClass(variant, size) };
|
|
569
|
-
}
|
|
570
|
-
function Button({ variant = "secondary", size = "md", className, type = "button", ...rest }) {
|
|
571
|
-
return /* @__PURE__ */ jsxRuntime.jsx("button", { type, className: buttonClass(variant, size, className), ...rest });
|
|
572
|
-
}
|
|
573
|
-
function LinkButton({ variant = "secondary", size = "md", className, external, forward, children, ...rest }) {
|
|
574
|
-
const extra = external ? { target: "_blank", rel: "noreferrer", ...rest } : rest;
|
|
575
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("a", { className: buttonClass(variant, size, className), ...extra, children: [
|
|
576
|
-
children,
|
|
577
|
-
external && /* @__PURE__ */ jsxRuntime.jsx(OutboundArrow, {}),
|
|
578
|
-
forward && !external && /* @__PURE__ */ jsxRuntime.jsx(ForwardArrow, {})
|
|
579
|
-
] });
|
|
580
|
-
}
|
|
581
|
-
function ForwardArrow() {
|
|
582
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "rz-arrow rz-arrow--fwd", children: [
|
|
583
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }),
|
|
584
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m13 6 6 6-6 6" })
|
|
585
|
-
] });
|
|
586
|
-
}
|
|
587
|
-
function OutboundArrow() {
|
|
588
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "rz-arrow", children: [
|
|
589
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 17 17 7" }),
|
|
590
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 7h10v10" })
|
|
591
|
-
] });
|
|
592
|
-
}
|
|
593
615
|
function Card({ title, meta, note, children, actions, className }) {
|
|
594
616
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["rz-card", className].filter(Boolean).join(" "), children: [
|
|
595
617
|
title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-card__title", children: title }),
|