@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.
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { createContext, useContext, useState, useId, useTransition, useEffect, Suspense, useMemo, useRef, useCallback, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
2
+ import { createContext, useContext, useState, useEffect, useId, useTransition, Suspense, useMemo, useRef, useCallback, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { usePathname, useRouter, useSearchParams } from 'next/navigation';
5
5
  import { createBrowserClient } from '@supabase/ssr';
@@ -154,6 +154,32 @@ function Sidebar({ items, pinned = [], activeHref, basePath }) {
154
154
  )
155
155
  ] });
156
156
  }
157
+ function buttonClass(variant = "secondary", size = "md", extra) {
158
+ return ["rz-btn", `rz-btn--${variant}`, `rz-btn--${size}`, extra].filter(Boolean).join(" ");
159
+ }
160
+ function Button({ variant = "secondary", size = "md", className, type = "button", ...rest }) {
161
+ return /* @__PURE__ */ jsx("button", { type, className: buttonClass(variant, size, className), ...rest });
162
+ }
163
+ function LinkButton({ variant = "secondary", size = "md", className, external, forward, children, ...rest }) {
164
+ const extra = external ? { target: "_blank", rel: "noreferrer", ...rest } : rest;
165
+ return /* @__PURE__ */ jsxs("a", { className: buttonClass(variant, size, className), ...extra, children: [
166
+ children,
167
+ external && /* @__PURE__ */ jsx(OutboundArrow, {}),
168
+ forward && !external && /* @__PURE__ */ jsx(ForwardArrow, {})
169
+ ] });
170
+ }
171
+ function ForwardArrow() {
172
+ return /* @__PURE__ */ 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: [
173
+ /* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
174
+ /* @__PURE__ */ jsx("path", { d: "m13 6 6 6-6 6" })
175
+ ] });
176
+ }
177
+ function OutboundArrow() {
178
+ return /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "rz-arrow", children: [
179
+ /* @__PURE__ */ jsx("path", { d: "M7 17 17 7" }),
180
+ /* @__PURE__ */ jsx("path", { d: "M7 7h10v10" })
181
+ ] });
182
+ }
157
183
  var THEME_KEY = "rz-admin-theme";
158
184
  function useTheme(defaultTheme = "light") {
159
185
  const [theme, setTheme] = useState(defaultTheme);
@@ -205,12 +231,16 @@ var HelpIcon = () => /* @__PURE__ */ jsxs("svg", { ...ico, children: [
205
231
  /* @__PURE__ */ jsx("path", { d: "M2 4h6a4 4 0 0 1 4 4v12a3 3 0 0 0-3-3H2z" }),
206
232
  /* @__PURE__ */ jsx("path", { d: "M22 4h-6a4 4 0 0 0-4 4v12a3 3 0 0 1 3-3h7z" })
207
233
  ] });
234
+ var GlobeIcon = () => /* @__PURE__ */ jsxs("svg", { ...ico, children: [
235
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
236
+ /* @__PURE__ */ jsx("path", { d: "M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" })
237
+ ] });
208
238
  var OutIcon = () => /* @__PURE__ */ jsxs("svg", { ...ico, children: [
209
239
  /* @__PURE__ */ jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
210
240
  /* @__PURE__ */ jsx("path", { d: "M16 17l5-5-5-5" }),
211
241
  /* @__PURE__ */ jsx("path", { d: "M21 12H9" })
212
242
  ] });
213
- function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
243
+ function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref, siteUrl }) {
214
244
  const [open, setOpen] = useState(false);
215
245
  const wrap = useRef(null);
216
246
  useEffect(() => {
@@ -245,7 +275,12 @@ function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, hel
245
275
  /* @__PURE__ */ jsx("span", { children: "Theme" }),
246
276
  /* @__PURE__ */ jsx("span", { className: "rz-menu__end", children: /* @__PURE__ */ jsx(ThemeToggle, { theme, onChange: onTheme }) })
247
277
  ] }),
248
- (accountHref || helpHref) && /* @__PURE__ */ jsx("div", { className: "rz-menu__sep" }),
278
+ (siteUrl || accountHref || helpHref) && /* @__PURE__ */ jsx("div", { className: "rz-menu__sep" }),
279
+ siteUrl && /* @__PURE__ */ jsxs("a", { href: siteUrl, target: "_blank", rel: "noreferrer", role: "menuitem", className: "rz-menu__item", children: [
280
+ /* @__PURE__ */ jsx(GlobeIcon, {}),
281
+ "View website",
282
+ /* @__PURE__ */ jsx("span", { className: "rz-menu__end", children: /* @__PURE__ */ jsx(OutboundArrow, {}) })
283
+ ] }),
249
284
  accountHref && /* @__PURE__ */ jsxs("a", { href: accountHref, role: "menuitem", className: "rz-menu__item", children: [
250
285
  /* @__PURE__ */ jsx(UserIcon, {}),
251
286
  "Account"
@@ -262,19 +297,28 @@ function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, hel
262
297
  ] })
263
298
  ] });
264
299
  }
265
- function TopBar({ siteName, productName, logoUrl, homeHref, user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
300
+ function TopBar({ siteName, productName, logoUrl, homeHref, siteUrl, user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
266
301
  return /* @__PURE__ */ jsxs("header", { className: "rz-topbar", children: [
267
- /* @__PURE__ */ jsxs("a", { href: homeHref, className: "rz-topbar__brand", children: [
268
- logoUrl && /* @__PURE__ */ jsx("img", { src: logoUrl, alt: "", className: "rz-topbar__logo" }),
269
- /* @__PURE__ */ jsx("span", { children: siteName }),
270
- /* @__PURE__ */ jsx("span", { className: "rz-topbar__product", children: productName })
271
- ] }),
302
+ /* @__PURE__ */ jsxs(
303
+ "a",
304
+ {
305
+ href: siteUrl || homeHref,
306
+ className: "rz-topbar__brand",
307
+ ...siteUrl ? { target: "_blank", rel: "noreferrer", title: "Open the live website in a new tab" } : {},
308
+ children: [
309
+ logoUrl && /* @__PURE__ */ jsx("img", { src: logoUrl, alt: "", className: "rz-topbar__logo" }),
310
+ /* @__PURE__ */ jsx("span", { children: siteName }),
311
+ /* @__PURE__ */ jsx("span", { className: "rz-topbar__product", children: productName }),
312
+ siteUrl && /* @__PURE__ */ jsx(OutboundArrow, {})
313
+ ]
314
+ }
315
+ ),
272
316
  user && role && /* @__PURE__ */ jsxs("div", { className: "rz-topbar__right", children: [
273
317
  /* @__PURE__ */ jsxs("span", { className: "rz-topbar__greeting", "data-testid": "rz-greeting", children: [
274
318
  "Welcome back, ",
275
319
  firstNameOf(user)
276
320
  ] }),
277
- /* @__PURE__ */ jsx(AccountMenu, { user, role, theme, onTheme, signOutPath, accountHref, helpHref })
321
+ /* @__PURE__ */ jsx(AccountMenu, { user, role, theme, onTheme, signOutPath, accountHref, helpHref, siteUrl })
278
322
  ] })
279
323
  ] });
280
324
  }
@@ -334,6 +378,8 @@ var SHELL_CSS = `
334
378
  .rz-topbar__brand { display: flex; align-items: center; gap: .6rem; font-weight: 600; font-size: .9375rem; text-decoration: none; }
335
379
  .rz-topbar__logo { height: 22px; width: auto; display: block; }
336
380
  .rz-topbar__product { font-weight: 400; color: var(--text-secondary); }
381
+ .rz-topbar__brand .rz-arrow { color: var(--text-muted); opacity: 0; transition: opacity .15s, transform .15s; }
382
+ .rz-topbar__brand:hover .rz-arrow { opacity: 1; }
337
383
  .rz-topbar__right { margin-left: auto; display: flex; align-items: center; gap: 1rem; }
338
384
  .rz-topbar__greeting { color: var(--text-secondary); font-size: .8rem; }
339
385
  .rz-body { display: flex; flex: 1; min-height: 0; }
@@ -545,6 +591,7 @@ function AdminShell({
545
591
  signOutPath,
546
592
  accountHref,
547
593
  helpHref,
594
+ siteUrl,
548
595
  branding,
549
596
  defaultTheme = "light",
550
597
  children
@@ -561,6 +608,7 @@ function AdminShell({
561
608
  siteName: shownName,
562
609
  productName,
563
610
  logoUrl: shownLogo,
611
+ siteUrl,
564
612
  homeHref: basePath,
565
613
  user,
566
614
  role,
@@ -577,32 +625,6 @@ function AdminShell({
577
625
  ] })
578
626
  ] });
579
627
  }
580
- function buttonClass(variant = "secondary", size = "md", extra) {
581
- return ["rz-btn", `rz-btn--${variant}`, `rz-btn--${size}`, extra].filter(Boolean).join(" ");
582
- }
583
- function Button({ variant = "secondary", size = "md", className, type = "button", ...rest }) {
584
- return /* @__PURE__ */ jsx("button", { type, className: buttonClass(variant, size, className), ...rest });
585
- }
586
- function LinkButton({ variant = "secondary", size = "md", className, external, forward, children, ...rest }) {
587
- const extra = external ? { target: "_blank", rel: "noreferrer", ...rest } : rest;
588
- return /* @__PURE__ */ jsxs("a", { className: buttonClass(variant, size, className), ...extra, children: [
589
- children,
590
- external && /* @__PURE__ */ jsx(OutboundArrow, {}),
591
- forward && !external && /* @__PURE__ */ jsx(ForwardArrow, {})
592
- ] });
593
- }
594
- function ForwardArrow() {
595
- return /* @__PURE__ */ 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: [
596
- /* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
597
- /* @__PURE__ */ jsx("path", { d: "m13 6 6 6-6 6" })
598
- ] });
599
- }
600
- function OutboundArrow() {
601
- return /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "rz-arrow", children: [
602
- /* @__PURE__ */ jsx("path", { d: "M7 17 17 7" }),
603
- /* @__PURE__ */ jsx("path", { d: "M7 7h10v10" })
604
- ] });
605
- }
606
628
  function Card({ title, meta, note, children, actions, className }) {
607
629
  return /* @__PURE__ */ jsxs("div", { className: ["rz-card", className].filter(Boolean).join(" "), children: [
608
630
  title && /* @__PURE__ */ jsx("div", { className: "rz-card__title", children: title }),
@@ -866,12 +888,149 @@ var STUDIO_CSS = `
866
888
  .rz-admin .rz-help__pager a:hover { color: var(--signal); }
867
889
  .rz-admin .rz-arrow--back { transform: none; }
868
890
  @media (prefers-reduced-motion: no-preference) { .rz-admin a:hover .rz-arrow--back { transform: translateX(-4px); } }
891
+
892
+ /* ---- Studio update strip / Settings \u2192 Studio ---------------------------- */
893
+ .rz-admin .rz-update { border: 1px solid var(--border); border-left: 3px solid var(--signal); border-radius: var(--rz-radius); background: var(--surface-1); padding: .7rem 1rem; }
894
+ .rz-admin .rz-update--strip { margin: 0 0 1.25rem; }
895
+ .rz-admin .rz-update__row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; }
896
+ .rz-admin .rz-update__title { font-weight: 600; color: var(--text-primary); }
897
+ .rz-admin .rz-update__link { display: inline-flex; align-items: center; gap: .3rem; background: none; border: 0; padding: 0; font: inherit; font-size: .875rem; color: var(--signal); cursor: pointer; text-decoration: none; }
898
+ .rz-admin .rz-update__link:hover { color: var(--text-primary); }
899
+ .rz-admin .rz-update__later { margin-left: auto; background: none; border: 0; padding: 0; font: inherit; font-size: .8rem; color: var(--text-muted); cursor: pointer; }
900
+ .rz-admin .rz-update__later:hover { color: var(--text-primary); }
901
+ .rz-admin .rz-update__notes { margin: .6rem 0 0; font-size: .875rem; line-height: 1.55; color: var(--text-secondary); white-space: pre-line; }
902
+ .rz-admin .rz-update__problem { margin: .6rem 0 0; font-size: .875rem; color: var(--status-down); }
903
+ .rz-admin .rz-update__done { display: block; margin-top: .5rem; font-size: .875rem; color: var(--text-secondary); }
869
904
  `;
870
- function StudioShellFrame({ siteName, logoUrl, user, role, nav, pinnedNav, branding, children }) {
905
+ var POLL_MS = 8e3;
906
+ var LOCAL_FALLBACK_MS = 15e4;
907
+ var GIVE_UP_MS = 15 * 6e4;
908
+ function useDeployed(active, mergedSha) {
909
+ const [state, setState] = useState("waiting");
910
+ useEffect(() => {
911
+ if (!active) return;
912
+ let stop = false;
913
+ let baseline;
914
+ const started = Date.now();
915
+ const read2 = async () => {
916
+ try {
917
+ const r = await fetch("/api/version", { cache: "no-store" });
918
+ const j = await r.json();
919
+ return j.sha;
920
+ } catch {
921
+ return null;
922
+ }
923
+ };
924
+ const tick = async () => {
925
+ if (stop) return;
926
+ const sha = await read2();
927
+ if (stop) return;
928
+ if (baseline === void 0) baseline = sha;
929
+ const elapsed = Date.now() - started;
930
+ const matched = sha !== null && mergedSha !== null && sha === mergedSha;
931
+ const moved = sha !== null && baseline !== null && sha !== baseline;
932
+ const localDone = sha === null && elapsed >= LOCAL_FALLBACK_MS;
933
+ if (matched || moved || localDone) {
934
+ setState("live");
935
+ return;
936
+ }
937
+ if (elapsed >= GIVE_UP_MS) {
938
+ setState("slow");
939
+ return;
940
+ }
941
+ setTimeout(tick, POLL_MS);
942
+ };
943
+ void tick();
944
+ return () => {
945
+ stop = true;
946
+ };
947
+ }, [active, mergedSha]);
948
+ return state;
949
+ }
950
+ var HIDE_KEY = "rz-update-later";
951
+ function canSeeUpdates(role) {
952
+ return role === "owner" || role === "staff";
953
+ }
954
+ function UpdateStrip({ update, role }) {
955
+ const [hidden, setHidden] = useState(true);
956
+ useEffect(() => {
957
+ try {
958
+ setHidden(sessionStorage.getItem(HIDE_KEY) === update?.version);
959
+ } catch {
960
+ setHidden(false);
961
+ }
962
+ }, [update?.version]);
963
+ if (!update || update.state !== "ready" || !canSeeUpdates(role) || hidden) return null;
964
+ const later = () => {
965
+ try {
966
+ sessionStorage.setItem(HIDE_KEY, update.version);
967
+ } catch {
968
+ }
969
+ setHidden(true);
970
+ };
971
+ return /* @__PURE__ */ jsx(UpdateBody, { update, onLater: later, compact: true });
972
+ }
973
+ function UpdateBody({ update, onLater, compact = false }) {
974
+ const { actions } = useStudio();
975
+ const [showNotes, setShowNotes] = useState(!compact);
976
+ const [busy, setBusy] = useState(false);
977
+ const [outcome, setOutcome] = useState(null);
978
+ const merged = outcome?.state === "merged";
979
+ const deploy = useDeployed(merged, merged ? outcome.sha : null);
980
+ const run = async () => {
981
+ setBusy(true);
982
+ try {
983
+ setOutcome(await actions.updateStudio(update.number));
984
+ } finally {
985
+ setBusy(false);
986
+ }
987
+ };
988
+ if (merged) {
989
+ const live = deploy === "live";
990
+ const steps = [
991
+ { label: "Updated", state: "done", note: `Studio ${update.version}` },
992
+ { label: "Live", state: live ? "done" : "active", note: live ? "On the site now" : deploy === "slow" ? "Taking longer than usual" : "Rebuilding, about two minutes" }
993
+ ];
994
+ return /* @__PURE__ */ jsxs("div", { className: `rz-update${compact ? " rz-update--strip" : ""}`, role: "status", children: [
995
+ /* @__PURE__ */ jsx(Steps, { steps }),
996
+ live && /* @__PURE__ */ jsxs("span", { className: "rz-update__done", children: [
997
+ "Studio updated to ",
998
+ update.version,
999
+ ". Reload to see it."
1000
+ ] })
1001
+ ] });
1002
+ }
1003
+ const problem = outcome && outcome.state !== "pending" ? outcome.state === "stale" ? "The site changed since the checks ran. Try again in a minute." : outcome.state === "red" ? `The checks failed${outcome.failingCheck ? ` (${outcome.failingCheck})` : ""}. Your web team has been able to see this.` : outcome.state === "error" ? outcome.message : null : null;
1004
+ return /* @__PURE__ */ jsxs("div", { className: `rz-update${compact ? " rz-update--strip" : ""}`, role: "status", children: [
1005
+ /* @__PURE__ */ jsxs("div", { className: "rz-update__row", children: [
1006
+ /* @__PURE__ */ jsxs("span", { className: "rz-update__title", children: [
1007
+ "Studio ",
1008
+ update.version,
1009
+ " is available"
1010
+ ] }),
1011
+ /* @__PURE__ */ jsx("button", { type: "button", className: "rz-update__link", onClick: () => setShowNotes((v) => !v), "aria-expanded": showNotes, children: "What\u2019s new" }),
1012
+ update.previewUrl && /* @__PURE__ */ jsxs("a", { href: update.previewUrl, target: "_blank", rel: "noreferrer", className: "rz-update__link", children: [
1013
+ "Preview ",
1014
+ /* @__PURE__ */ jsx(OutboundArrow, {})
1015
+ ] }),
1016
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: run, disabled: busy, children: busy ? "Updating\u2026" : "Update" }),
1017
+ onLater && /* @__PURE__ */ jsx("button", { type: "button", className: "rz-update__later", onClick: onLater, children: "Later" })
1018
+ ] }),
1019
+ showNotes && /* @__PURE__ */ jsx("p", { className: "rz-update__notes", children: update.notes ?? `Version ${update.version} of Studio, replacing ${update.currentVersion}. Nothing changes on your website until you press Update.` }),
1020
+ problem && /* @__PURE__ */ jsx("p", { className: "rz-update__problem", children: problem })
1021
+ ] });
1022
+ }
1023
+ function StudioShellFrame({ siteName, logoUrl, siteUrl = "/", user, role, nav, pinnedNav, branding, update = null, children }) {
871
1024
  const pathname = usePathname() ?? "/admin";
1025
+ useEffect(() => {
1026
+ if (user) document.cookie = "rz-studio=1; Path=/; Max-Age=2592000; SameSite=Lax";
1027
+ }, [user]);
872
1028
  return /* @__PURE__ */ jsxs(Fragment, { children: [
873
1029
  /* @__PURE__ */ jsx("style", { children: STUDIO_CSS }),
874
- /* @__PURE__ */ jsx(AdminShell, { siteName, logoUrl: logoUrl ?? void 0, user, role, nav, pinnedNav, branding, accountHref: null, activeHref: pathname, children })
1030
+ /* @__PURE__ */ jsxs(AdminShell, { siteName, logoUrl: logoUrl ?? void 0, siteUrl, user, role, nav, pinnedNav, branding, accountHref: null, activeHref: pathname, children: [
1031
+ /* @__PURE__ */ jsx(UpdateStrip, { update, role }),
1032
+ children
1033
+ ] })
875
1034
  ] });
876
1035
  }
877
1036
 
@@ -2883,51 +3042,6 @@ function RemoveItem({ typeId, slug }) {
2883
3042
  error && /* @__PURE__ */ jsx("div", { className: "rz-alert admin__alert", children: error })
2884
3043
  ] });
2885
3044
  }
2886
- var POLL_MS = 8e3;
2887
- var LOCAL_FALLBACK_MS = 15e4;
2888
- var GIVE_UP_MS = 15 * 6e4;
2889
- function useDeployed(active, mergedSha) {
2890
- const [state, setState] = useState("waiting");
2891
- useEffect(() => {
2892
- if (!active) return;
2893
- let stop = false;
2894
- let baseline;
2895
- const started = Date.now();
2896
- const read2 = async () => {
2897
- try {
2898
- const r = await fetch("/api/version", { cache: "no-store" });
2899
- const j = await r.json();
2900
- return j.sha;
2901
- } catch {
2902
- return null;
2903
- }
2904
- };
2905
- const tick = async () => {
2906
- if (stop) return;
2907
- const sha = await read2();
2908
- if (stop) return;
2909
- if (baseline === void 0) baseline = sha;
2910
- const elapsed = Date.now() - started;
2911
- const matched = sha !== null && mergedSha !== null && sha === mergedSha;
2912
- const moved = sha !== null && baseline !== null && sha !== baseline;
2913
- const localDone = sha === null && elapsed >= LOCAL_FALLBACK_MS;
2914
- if (matched || moved || localDone) {
2915
- setState("live");
2916
- return;
2917
- }
2918
- if (elapsed >= GIVE_UP_MS) {
2919
- setState("slow");
2920
- return;
2921
- }
2922
- setTimeout(tick, POLL_MS);
2923
- };
2924
- void tick();
2925
- return () => {
2926
- stop = true;
2927
- };
2928
- }, [active, mergedSha]);
2929
- return state;
2930
- }
2931
3045
  var REFRESH_MS = 1e4;
2932
3046
  function PublishPanel({ number, htmlUrl, merged, closed = false, branch, removal = false, mergedSha = null, status, item }) {
2933
3047
  const { mergeContentPr, discardChange } = useStudio().actions;
@@ -3826,12 +3940,46 @@ function BusinessTab({ initial }) {
3826
3940
  ] })
3827
3941
  ] });
3828
3942
  }
3829
- var LABELS = { profile: "My Profile", password: "Change Password", team: "Team", business: "Business" };
3830
- function AccountTabs({ me, manages, team, branding, myUserId, myRole }) {
3943
+ function StudioTab({ update, version, role }) {
3944
+ const { supportName } = useStudio();
3945
+ return /* @__PURE__ */ jsxs("div", { className: "acct-stack", children: [
3946
+ /* @__PURE__ */ jsxs(Card, { className: "acct-card", children: [
3947
+ /* @__PURE__ */ jsxs("h2", { className: "acct-title", children: [
3948
+ "Studio ",
3949
+ version
3950
+ ] }),
3951
+ !update && /* @__PURE__ */ jsx("p", { className: "acct-blurb", children: "You\u2019re up to date. When a new version comes out, it appears here and as a strip at the top of every Studio page." }),
3952
+ update?.state === "checking" && /* @__PURE__ */ jsxs("p", { className: "acct-blurb", children: [
3953
+ "Studio ",
3954
+ update.version,
3955
+ " is on its way \u2014 the checks are still running. Come back in a couple of minutes."
3956
+ ] }),
3957
+ update?.state === "failed" && /* @__PURE__ */ jsxs("p", { className: "acct-blurb", children: [
3958
+ "Studio ",
3959
+ update.version,
3960
+ " is available, but its checks failed, so it can\u2019t be installed yet.",
3961
+ role === "staff" && update.failingCheck ? ` Failing check: ${update.failingCheck}.` : ` ${supportName} has been able to see this and will sort it out.`
3962
+ ] }),
3963
+ update?.state === "ready" && /* @__PURE__ */ jsx(UpdateBody, { update })
3964
+ ] }),
3965
+ /* @__PURE__ */ jsxs(Card, { className: "acct-card", children: [
3966
+ /* @__PURE__ */ jsx("h2", { className: "acct-title", children: "How updates work" }),
3967
+ /* @__PURE__ */ jsxs("p", { className: "acct-blurb", children: [
3968
+ "Studio is a small package inside your website. When ",
3969
+ supportName,
3970
+ " releases a new version, your site prepares the update as a checked change \u2014 the same way a blog post is checked before it goes live \u2014 and builds a private preview. Nothing changes until you press ",
3971
+ /* @__PURE__ */ jsx("b", { children: "Update" }),
3972
+ ". If the update would break anything, the button never unlocks."
3973
+ ] })
3974
+ ] })
3975
+ ] });
3976
+ }
3977
+ var LABELS = { profile: "My Profile", password: "Change Password", team: "Team", business: "Business", studio: "Studio" };
3978
+ function AccountTabs({ me, manages, team, branding, myUserId, myRole, update = null, version = "" }) {
3831
3979
  const router = useRouter();
3832
3980
  const pathname = usePathname();
3833
3981
  const params = useSearchParams();
3834
- const ids = manages ? ["profile", "password", "team", "business"] : ["profile", "password"];
3982
+ const ids = manages ? ["profile", "password", "team", "business", "studio"] : ["profile", "password"];
3835
3983
  const requested = params.get("tab");
3836
3984
  const active = requested && ids.includes(requested) ? requested : "profile";
3837
3985
  const setActive = (v) => router.replace(`${pathname}?tab=${v}`, { scroll: false });
@@ -3840,14 +3988,15 @@ function AccountTabs({ me, manages, team, branding, myUserId, myRole }) {
3840
3988
  active === "profile" && /* @__PURE__ */ jsx(ProfileTab, { email: me.email, name: me.name, displayName: me.displayName, avatarUrl: me.avatarUrl }),
3841
3989
  active === "password" && /* @__PURE__ */ jsx(PasswordTab, {}),
3842
3990
  active === "team" && manages && (team?.ok ? /* @__PURE__ */ jsx(TeamTab, { members: team.members, canInvite: team.canInvite, myUserId, myRole }) : /* @__PURE__ */ jsx("div", { className: "rz-alert admin__alert", children: team?.error ?? "Couldn\u2019t load the team." })),
3843
- active === "business" && manages && /* @__PURE__ */ jsx(BusinessTab, { initial: branding })
3991
+ active === "business" && manages && /* @__PURE__ */ jsx(BusinessTab, { initial: branding }),
3992
+ active === "studio" && manages && /* @__PURE__ */ jsx(StudioTab, { update, version, role: myRole })
3844
3993
  ] });
3845
3994
  }
3846
- function StudioAccount({ me, manages, team, branding, myUserId, myRole }) {
3995
+ function StudioAccount({ me, manages, team, branding, myUserId, myRole, update = null, version = "" }) {
3847
3996
  return /* @__PURE__ */ jsxs("div", { className: "acct", children: [
3848
3997
  /* @__PURE__ */ jsx("h1", { className: "acct-h1", children: "Account" }),
3849
3998
  /* @__PURE__ */ jsx("p", { className: "acct-email", children: me.email }),
3850
- /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(AccountTabs, { me, manages, team, branding, myUserId, myRole }) })
3999
+ /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(AccountTabs, { me, manages, team, branding, myUserId, myRole, update, version }) })
3851
4000
  ] });
3852
4001
  }
3853
4002
 
@@ -3920,7 +4069,7 @@ Open the avatar menu (top right) and choose **Sign out**.
3920
4069
  section: "start",
3921
4070
  title: "Finding your way around",
3922
4071
  summary: "The sidebar, the dashboard, the lists, and the avatar menu.",
3923
- keywords: ["sidebar", "dashboard", "navigation", "menu", "settings"],
4072
+ keywords: ["sidebar", "dashboard", "navigation", "menu", "settings", "view website", "live site", "visit site"],
3924
4073
  body: `
3925
4074
  ## The sidebar
3926
4075
 
@@ -3932,9 +4081,13 @@ On a narrow screen the sidebar collapses to icons; press the arrow at the bottom
3932
4081
 
3933
4082
  Every row is one item: its title, date, status and a **View live** link. **+ New** at the top starts a new one. Click a title to edit it. The **Status** column links straight to a waiting change.
3934
4083
 
4084
+ ## Getting to the live website
4085
+
4086
+ Click the site name at the top left \u2014 it opens the live website in a new tab (hover and an arrow appears). The avatar menu has the same thing as **View website**. Each item's list row also has **View live** for that one page.
4087
+
3935
4088
  ## The avatar menu
3936
4089
 
3937
- Top right, your photo or initials. From here: **Account**, **Help center** (this page), a light/dark toggle, and **Sign out**.
4090
+ Top right, your photo or initials. From here: a light/dark toggle, **View website**, **Account**, **Help center** (this page), and **Sign out**.
3938
4091
 
3939
4092
  ## The Ask bubble
3940
4093
 
@@ -3981,6 +4134,28 @@ The status becomes **Needs fixing** and the message names the problem. Open the
3981
4134
  | **Removing\u2026** / **Removal ready** | You asked to take an item down; same steps as any change. | Publish to make the removal live, or discard to keep the item. |
3982
4135
 
3983
4136
  One item has at most one change waiting at a time. Saving again updates that same change rather than starting a second one.
4137
+ `
4138
+ },
4139
+ {
4140
+ id: "studio-pill",
4141
+ section: "start",
4142
+ title: "Getting back to Studio from the website",
4143
+ summary: "A small Studio pill appears bottom-left on the website while you're signed in \u2014 Edit this page, or Dashboard.",
4144
+ keywords: ["pill", "edit this page", "admin bar", "back to studio", "website"],
4145
+ body: `
4146
+ ## What it is
4147
+
4148
+ While you're signed in to Studio, your public website shows a small dark pill in the bottom-left corner: **Studio \xB7 Edit this page \xB7 Dashboard**. Visitors never see it \u2014 it only appears in a browser where you've signed in.
4149
+
4150
+ ## Why it's useful
4151
+
4152
+ Spot a typo while reading your own site? **Edit this page** opens that exact post or event in Studio. **Dashboard** takes you to the Studio home.
4153
+
4154
+ ## Good to know
4155
+
4156
+ - **Edit this page** only shows on pages Studio manages (a blog post, an event). On the home page or About, you get **Dashboard** only.
4157
+ - The \xD7 hides the pill for that browser tab; it comes back next time you open the site.
4158
+ - It disappears when you sign out of Studio. If you see it but Studio asks you to sign in again, your session simply expired \u2014 sign in and carry on.
3984
4159
  `
3985
4160
  }
3986
4161
  ];
@@ -4801,6 +4976,38 @@ Pictures save the moment you choose them (**Choose a picture**, **Replace**, **R
4801
4976
  ## Where the pictures live
4802
4977
 
4803
4978
  In Studio's own secure storage \u2014 not in your website's files, so they don't go through Save/Publish.
4979
+ `
4980
+ },
4981
+ {
4982
+ id: "updating-studio",
4983
+ section: "account",
4984
+ title: "Updating Studio",
4985
+ summary: "When a new version is out, owners see a strip with what's new, a preview and an Update button. Nothing installs until you press it.",
4986
+ keywords: ["update", "upgrade", "new version", "release", "what's new", "studio version"],
4987
+ body: `
4988
+ ## What it is
4989
+
4990
+ Studio is a small package inside your website, and it improves over time. When {supportName} releases a new version, your website prepares the update automatically as a **checked change** \u2014 exactly like a blog post is checked before it goes live \u2014 and builds a private preview of your site with the new Studio in it.
4991
+
4992
+ ## Where you see it
4993
+
4994
+ - A quiet strip at the top of every Studio page: **Studio 0.15 is available \xB7 What's new \xB7 Preview \xB7 Update \xB7 Later**. Only owners and {supportName} see it; editors don't.
4995
+ - **Settings \u2192 Studio**: the version you're running, the pending update with its notes, and the same Update button.
4996
+
4997
+ The strip appears only once the update's checks have passed. If they fail, nothing is offered \u2014 {supportName} sees the failure and fixes it.
4998
+
4999
+ ## How to update
5000
+
5001
+ 1. Read **What's new** (a few lines, plain English).
5002
+ 2. Optional: press **Preview** to open your website running the new Studio.
5003
+ 3. Press **Update**. The tracker shows *Updated \u2192 Live*; about two minutes later the website has rebuilt.
5004
+ 4. Reload Studio to use the new version.
5005
+
5006
+ ## Good to know
5007
+
5008
+ - **Later** hides the strip for this browser tab; it comes back next time.
5009
+ - Nothing on your public website changes because of a Studio update \u2014 only the dashboard does.
5010
+ - You can't skip a version or go back from here; if something looks wrong after an update, tell {supportName}.
4804
5011
  `
4805
5012
  }
4806
5013
  ];
@@ -5239,8 +5446,12 @@ function BackArrow() {
5239
5446
  ] });
5240
5447
  }
5241
5448
 
5449
+ // src/version.ts
5450
+ var ADMIN_VERSION = "0.15.0" ;
5451
+
5242
5452
  // src/studio-ui/types.ts
5243
5453
  var ACTION_NAMES = [
5454
+ "updateStudio",
5244
5455
  "publishContent",
5245
5456
  "removeContent",
5246
5457
  "mergeContentPr",
@@ -5260,6 +5471,6 @@ var ACTION_NAMES = [
5260
5471
  "remove"
5261
5472
  ];
5262
5473
 
5263
- export { ACTION_NAMES, ContentEditor, GLOSSARY, HELP_ARTICLES, HELP_SECTIONS, Jargon, PublishPanel, RemoveItem, STUDIO_CSS, STUDIO_FACTS, StudioAcceptInvite, StudioAccount, StudioDashboard, StudioEdit, StudioHelp, StudioList, StudioNew, StudioNoAccess, StudioNotConfigured, StudioProvider, StudioPullRequest, StudioShellFrame, StudioSignIn, StudioTerm, copyFor, fillVars, helpAsText, joinTypes, publicDirOf, searchHelp, useEntry, useStudio };
5474
+ export { ACTION_NAMES, ADMIN_VERSION, ContentEditor, GLOSSARY, HELP_ARTICLES, HELP_SECTIONS, Jargon, PublishPanel, RemoveItem, STUDIO_CSS, STUDIO_FACTS, StudioAcceptInvite, StudioAccount, StudioDashboard, StudioEdit, StudioHelp, StudioList, StudioNew, StudioNoAccess, StudioNotConfigured, StudioProvider, StudioPullRequest, StudioShellFrame, StudioSignIn, StudioTerm, UpdateBody, UpdateStrip, canSeeUpdates, copyFor, fillVars, helpAsText, joinTypes, publicDirOf, searchHelp, useEntry, useStudio };
5264
5475
  //# sourceMappingURL=index.js.map
5265
5476
  //# sourceMappingURL=index.js.map