@realiizlabs/admin 0.13.2 → 0.14.1
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/dist/{Sidebar-CNuphww6.d.cts → Sidebar-Dz_qJOSx.d.cts} +4 -2
- package/dist/{Sidebar-CNuphww6.d.ts → Sidebar-Dz_qJOSx.d.ts} +4 -2
- package/dist/events/index.cjs +1 -1
- package/dist/events/index.cjs.map +1 -1
- package/dist/events/index.js +1 -1
- package/dist/events/index.js.map +1 -1
- package/dist/forms-ui/index.cjs.map +1 -1
- package/dist/forms-ui/index.d.cts +1 -1
- package/dist/forms-ui/index.d.ts +1 -1
- package/dist/forms-ui/index.js.map +1 -1
- package/dist/shell/index.cjs +62 -40
- package/dist/shell/index.cjs.map +1 -1
- package/dist/shell/index.d.cts +11 -7
- package/dist/shell/index.d.ts +11 -7
- package/dist/shell/index.js +62 -40
- package/dist/shell/index.js.map +1 -1
- package/dist/studio-ui/index.cjs +1493 -102
- package/dist/studio-ui/index.cjs.map +1 -1
- package/dist/studio-ui/index.d.cts +70 -8
- package/dist/studio-ui/index.d.ts +70 -8
- package/dist/studio-ui/index.js +1489 -104
- package/dist/studio-ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/studio-ui/index.cjs
CHANGED
|
@@ -160,6 +160,32 @@ function Sidebar({ items, pinned = [], activeHref, basePath }) {
|
|
|
160
160
|
)
|
|
161
161
|
] });
|
|
162
162
|
}
|
|
163
|
+
function buttonClass(variant = "secondary", size = "md", extra) {
|
|
164
|
+
return ["rz-btn", `rz-btn--${variant}`, `rz-btn--${size}`, extra].filter(Boolean).join(" ");
|
|
165
|
+
}
|
|
166
|
+
function Button({ variant = "secondary", size = "md", className, type = "button", ...rest }) {
|
|
167
|
+
return /* @__PURE__ */ jsxRuntime.jsx("button", { type, className: buttonClass(variant, size, className), ...rest });
|
|
168
|
+
}
|
|
169
|
+
function LinkButton({ variant = "secondary", size = "md", className, external, forward, children, ...rest }) {
|
|
170
|
+
const extra = external ? { target: "_blank", rel: "noreferrer", ...rest } : rest;
|
|
171
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("a", { className: buttonClass(variant, size, className), ...extra, children: [
|
|
172
|
+
children,
|
|
173
|
+
external && /* @__PURE__ */ jsxRuntime.jsx(OutboundArrow, {}),
|
|
174
|
+
forward && !external && /* @__PURE__ */ jsxRuntime.jsx(ForwardArrow, {})
|
|
175
|
+
] });
|
|
176
|
+
}
|
|
177
|
+
function ForwardArrow() {
|
|
178
|
+
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: [
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }),
|
|
180
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m13 6 6 6-6 6" })
|
|
181
|
+
] });
|
|
182
|
+
}
|
|
183
|
+
function OutboundArrow() {
|
|
184
|
+
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: [
|
|
185
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 17 17 7" }),
|
|
186
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 7h10v10" })
|
|
187
|
+
] });
|
|
188
|
+
}
|
|
163
189
|
var THEME_KEY = "rz-admin-theme";
|
|
164
190
|
function useTheme(defaultTheme = "light") {
|
|
165
191
|
const [theme, setTheme] = react.useState(defaultTheme);
|
|
@@ -211,12 +237,16 @@ var HelpIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...ico, children:
|
|
|
211
237
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 4h6a4 4 0 0 1 4 4v12a3 3 0 0 0-3-3H2z" }),
|
|
212
238
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 4h-6a4 4 0 0 0-4 4v12a3 3 0 0 1 3-3h7z" })
|
|
213
239
|
] });
|
|
240
|
+
var GlobeIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...ico, children: [
|
|
241
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
242
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" })
|
|
243
|
+
] });
|
|
214
244
|
var OutIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...ico, children: [
|
|
215
245
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
|
|
216
246
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 17l5-5-5-5" }),
|
|
217
247
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12H9" })
|
|
218
248
|
] });
|
|
219
|
-
function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
|
|
249
|
+
function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, helpHref, siteUrl }) {
|
|
220
250
|
const [open, setOpen] = react.useState(false);
|
|
221
251
|
const wrap = react.useRef(null);
|
|
222
252
|
react.useEffect(() => {
|
|
@@ -251,14 +281,19 @@ function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, hel
|
|
|
251
281
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Theme" }),
|
|
252
282
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-menu__end", children: /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, { theme, onChange: onTheme }) })
|
|
253
283
|
] }),
|
|
254
|
-
(accountHref || helpHref) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__sep" }),
|
|
284
|
+
(siteUrl || accountHref || helpHref) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__sep" }),
|
|
285
|
+
siteUrl && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: siteUrl, target: "_blank", rel: "noreferrer", role: "menuitem", className: "rz-menu__item", children: [
|
|
286
|
+
/* @__PURE__ */ jsxRuntime.jsx(GlobeIcon, {}),
|
|
287
|
+
"View website",
|
|
288
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-menu__end", children: /* @__PURE__ */ jsxRuntime.jsx(OutboundArrow, {}) })
|
|
289
|
+
] }),
|
|
255
290
|
accountHref && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: accountHref, role: "menuitem", className: "rz-menu__item", children: [
|
|
256
291
|
/* @__PURE__ */ jsxRuntime.jsx(UserIcon, {}),
|
|
257
292
|
"Account"
|
|
258
293
|
] }),
|
|
259
294
|
helpHref && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: helpHref, role: "menuitem", className: "rz-menu__item", children: [
|
|
260
295
|
/* @__PURE__ */ jsxRuntime.jsx(HelpIcon, {}),
|
|
261
|
-
"Help"
|
|
296
|
+
"Help center"
|
|
262
297
|
] }),
|
|
263
298
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-menu__sep" }),
|
|
264
299
|
/* @__PURE__ */ jsxRuntime.jsx("form", { method: "post", action: signOutPath, className: "rz-menu__signout", children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "submit", role: "menuitem", className: "rz-menu__item", children: [
|
|
@@ -268,19 +303,28 @@ function AccountMenu({ user, role, theme, onTheme, signOutPath, accountHref, hel
|
|
|
268
303
|
] })
|
|
269
304
|
] });
|
|
270
305
|
}
|
|
271
|
-
function TopBar({ siteName, productName, logoUrl, homeHref, user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
|
|
306
|
+
function TopBar({ siteName, productName, logoUrl, homeHref, siteUrl, user, role, theme, onTheme, signOutPath, accountHref, helpHref }) {
|
|
272
307
|
return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "rz-topbar", children: [
|
|
273
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
308
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
309
|
+
"a",
|
|
310
|
+
{
|
|
311
|
+
href: siteUrl || homeHref,
|
|
312
|
+
className: "rz-topbar__brand",
|
|
313
|
+
...siteUrl ? { target: "_blank", rel: "noreferrer", title: "Open the live website in a new tab" } : {},
|
|
314
|
+
children: [
|
|
315
|
+
logoUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoUrl, alt: "", className: "rz-topbar__logo" }),
|
|
316
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: siteName }),
|
|
317
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-topbar__product", children: productName }),
|
|
318
|
+
siteUrl && /* @__PURE__ */ jsxRuntime.jsx(OutboundArrow, {})
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
),
|
|
278
322
|
user && role && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-topbar__right", children: [
|
|
279
323
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rz-topbar__greeting", "data-testid": "rz-greeting", children: [
|
|
280
324
|
"Welcome back, ",
|
|
281
325
|
firstNameOf(user)
|
|
282
326
|
] }),
|
|
283
|
-
/* @__PURE__ */ jsxRuntime.jsx(AccountMenu, { user, role, theme, onTheme, signOutPath, accountHref, helpHref })
|
|
327
|
+
/* @__PURE__ */ jsxRuntime.jsx(AccountMenu, { user, role, theme, onTheme, signOutPath, accountHref, helpHref, siteUrl })
|
|
284
328
|
] })
|
|
285
329
|
] });
|
|
286
330
|
}
|
|
@@ -340,6 +384,8 @@ var SHELL_CSS = `
|
|
|
340
384
|
.rz-topbar__brand { display: flex; align-items: center; gap: .6rem; font-weight: 600; font-size: .9375rem; text-decoration: none; }
|
|
341
385
|
.rz-topbar__logo { height: 22px; width: auto; display: block; }
|
|
342
386
|
.rz-topbar__product { font-weight: 400; color: var(--text-secondary); }
|
|
387
|
+
.rz-topbar__brand .rz-arrow { color: var(--text-muted); opacity: 0; transition: opacity .15s, transform .15s; }
|
|
388
|
+
.rz-topbar__brand:hover .rz-arrow { opacity: 1; }
|
|
343
389
|
.rz-topbar__right { margin-left: auto; display: flex; align-items: center; gap: 1rem; }
|
|
344
390
|
.rz-topbar__greeting { color: var(--text-secondary); font-size: .8rem; }
|
|
345
391
|
.rz-body { display: flex; flex: 1; min-height: 0; }
|
|
@@ -514,7 +560,7 @@ var SHELL_CSS = `
|
|
|
514
560
|
.rz-admin .realiiz-prose h2 { font-size: 1.35rem; } .rz-admin .realiiz-prose h3 { font-size: 1.1rem; }
|
|
515
561
|
.rz-admin .realiiz-prose p, .rz-admin .realiiz-prose ul, .rz-admin .realiiz-prose ol, .rz-admin .realiiz-prose blockquote { margin: 0 0 1em; }
|
|
516
562
|
.rz-admin .realiiz-prose a { color: var(--signal); }
|
|
517
|
-
/* The site's prose class picks a slate body
|
|
563
|
+
/* The site's prose class picks a slate body color for a light page; on the dark surface that reads as disabled. */
|
|
518
564
|
.rz-admin[data-theme="dark"] .realiiz-prose, .rz-admin[data-theme="dark"] .realiiz-prose :is(p, li, td, th, dd, dt, figcaption, strong, b, em, i, h1, h2, h3, h4, h5, h6) { color: var(--text-primary); }
|
|
519
565
|
.rz-admin[data-theme="dark"] .realiiz-prose :is(code, kbd) { color: var(--text-primary); background: var(--surface-3); }
|
|
520
566
|
.rz-admin[data-theme="dark"] .realiiz-prose a { color: var(--signal-dim); }
|
|
@@ -551,6 +597,7 @@ function AdminShell({
|
|
|
551
597
|
signOutPath,
|
|
552
598
|
accountHref,
|
|
553
599
|
helpHref,
|
|
600
|
+
siteUrl,
|
|
554
601
|
branding,
|
|
555
602
|
defaultTheme = "light",
|
|
556
603
|
children
|
|
@@ -567,6 +614,7 @@ function AdminShell({
|
|
|
567
614
|
siteName: shownName,
|
|
568
615
|
productName,
|
|
569
616
|
logoUrl: shownLogo,
|
|
617
|
+
siteUrl,
|
|
570
618
|
homeHref: basePath,
|
|
571
619
|
user,
|
|
572
620
|
role,
|
|
@@ -583,32 +631,6 @@ function AdminShell({
|
|
|
583
631
|
] })
|
|
584
632
|
] });
|
|
585
633
|
}
|
|
586
|
-
function buttonClass(variant = "secondary", size = "md", extra) {
|
|
587
|
-
return ["rz-btn", `rz-btn--${variant}`, `rz-btn--${size}`, extra].filter(Boolean).join(" ");
|
|
588
|
-
}
|
|
589
|
-
function Button({ variant = "secondary", size = "md", className, type = "button", ...rest }) {
|
|
590
|
-
return /* @__PURE__ */ jsxRuntime.jsx("button", { type, className: buttonClass(variant, size, className), ...rest });
|
|
591
|
-
}
|
|
592
|
-
function LinkButton({ variant = "secondary", size = "md", className, external, forward, children, ...rest }) {
|
|
593
|
-
const extra = external ? { target: "_blank", rel: "noreferrer", ...rest } : rest;
|
|
594
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("a", { className: buttonClass(variant, size, className), ...extra, children: [
|
|
595
|
-
children,
|
|
596
|
-
external && /* @__PURE__ */ jsxRuntime.jsx(OutboundArrow, {}),
|
|
597
|
-
forward && !external && /* @__PURE__ */ jsxRuntime.jsx(ForwardArrow, {})
|
|
598
|
-
] });
|
|
599
|
-
}
|
|
600
|
-
function ForwardArrow() {
|
|
601
|
-
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: [
|
|
602
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }),
|
|
603
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m13 6 6 6-6 6" })
|
|
604
|
-
] });
|
|
605
|
-
}
|
|
606
|
-
function OutboundArrow() {
|
|
607
|
-
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: [
|
|
608
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 17 17 7" }),
|
|
609
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 7h10v10" })
|
|
610
|
-
] });
|
|
611
|
-
}
|
|
612
634
|
function Card({ title, meta, note, children, actions, className }) {
|
|
613
635
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["rz-card", className].filter(Boolean).join(" "), children: [
|
|
614
636
|
title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-card__title", children: title }),
|
|
@@ -689,7 +711,7 @@ function Tabs({ options, value, onChange, label = "Sections" }) {
|
|
|
689
711
|
// src/studio-ui/css.ts
|
|
690
712
|
var STUDIO_CSS = `
|
|
691
713
|
/*
|
|
692
|
-
* /admin \u2014 overrides only. Layout,
|
|
714
|
+
* /admin \u2014 overrides only. Layout, color and components come from
|
|
693
715
|
* @realiizlabs/admin/shell (SHELL_CSS, injected by AdminShell). The Realiiz
|
|
694
716
|
* palette is the package default, so this file just maps the shell's font
|
|
695
717
|
* hooks onto the site's loaded fonts. To rebrand, redeclare tokens here:
|
|
@@ -812,7 +834,7 @@ var STUDIO_CSS = `
|
|
|
812
834
|
.rz-admin .acct-dd__check { width: 1rem; flex-shrink: 0; color: var(--signal); font-weight: 700; }
|
|
813
835
|
.rz-admin .acct-dd__text { display: flex; flex-direction: column; line-height: 1.3; }
|
|
814
836
|
.rz-admin .acct-dd__hint { font-size: .74rem; color: var(--text-muted); }
|
|
815
|
-
/* Team table: everything on one
|
|
837
|
+
/* Team table: everything on one center line, and the role menu may overflow the table (the shell
|
|
816
838
|
clips .rz-table for rounded corners, so round the header cells by hand instead). */
|
|
817
839
|
.rz-admin .acct-team { overflow: visible; }
|
|
818
840
|
.rz-admin .acct-team thead th:first-child { border-top-left-radius: 8px; }
|
|
@@ -829,12 +851,55 @@ var STUDIO_CSS = `
|
|
|
829
851
|
.rz-admin .rz-term__word:hover, .rz-admin .rz-term__word:focus-visible { text-decoration-color: var(--signal); outline: none; }
|
|
830
852
|
.rz-admin .rz-term__tip { position: absolute; left: 0; top: calc(100% + 8px); z-index: 70; width: 272px; max-width: 80vw; padding: 12px 14px; border-radius: 8px; background: var(--text-primary); color: var(--surface-0); font-size: .8125rem; line-height: 1.4; font-weight: 400; box-shadow: 0 8px 24px rgba(14,23,38,.18); opacity: 0; pointer-events: none; transition: opacity .15s; }
|
|
831
853
|
.rz-admin .rz-term__tip--open { opacity: 1; }
|
|
854
|
+
|
|
855
|
+
/* ---- Help center ---------------------------------------------------- */
|
|
856
|
+
.rz-admin .rz-help__search { position: relative; margin: 0 0 1.5rem; max-width: 44rem; }
|
|
857
|
+
.rz-admin .rz-help__search svg { position: absolute; left: .9rem; top: 50%; transform: translateY(-50%); color: var(--text-muted); pointer-events: none; }
|
|
858
|
+
.rz-admin .rz-help__search input { width: 100%; padding-left: 2.6rem; font-size: 1rem; }
|
|
859
|
+
.rz-admin .rz-help__body { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 2rem; align-items: start; }
|
|
860
|
+
@media (max-width: 760px) { .rz-admin .rz-help__body { grid-template-columns: 1fr; gap: 1rem; } .rz-admin .rz-help__nav { display: flex; flex-wrap: wrap; gap: .25rem; } }
|
|
861
|
+
.rz-admin .rz-help__nav { position: sticky; top: calc(var(--rz-topbar) + 1rem); display: flex; flex-direction: column; gap: .1rem; }
|
|
862
|
+
.rz-admin .rz-help__navitem { padding: .45rem .75rem; border-radius: 6px; color: var(--text-secondary); text-decoration: none; font-size: .875rem; }
|
|
863
|
+
.rz-admin .rz-help__navitem:hover { background: var(--surface-2); color: var(--text-primary); }
|
|
864
|
+
.rz-admin .rz-help__navitem--active { background: var(--surface-2); color: var(--signal); font-weight: 500; }
|
|
865
|
+
.rz-admin .rz-help__main { max-width: 44rem; min-width: 0; }
|
|
866
|
+
.rz-admin .rz-help__h { font-family: var(--rz-font-display); font-size: 1.5rem; font-weight: 600; letter-spacing: -.01em; margin: 0 0 .35rem; }
|
|
867
|
+
.rz-admin .rz-help__blurb { color: var(--text-secondary); margin: 0 0 1.25rem; }
|
|
868
|
+
.rz-admin .rz-help__count { color: var(--text-muted); font-size: .85rem; margin: 0 0 .75rem; }
|
|
869
|
+
.rz-admin .rz-help__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
|
|
870
|
+
.rz-admin .rz-help__card { display: flex; flex-direction: column; gap: .2rem; padding: .9rem 1rem; background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--rz-radius); text-decoration: none; color: inherit; transition: border-color .15s; }
|
|
871
|
+
.rz-admin .rz-help__card:hover { border-color: var(--signal); }
|
|
872
|
+
.rz-admin .rz-help__meta { font-family: var(--rz-font-mono); font-size: .65rem; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); }
|
|
873
|
+
.rz-admin .rz-help__title { font-weight: 600; color: var(--text-primary); }
|
|
874
|
+
.rz-admin .rz-help__summary { color: var(--text-secondary); font-size: .9rem; line-height: 1.5; }
|
|
875
|
+
.rz-admin .rz-help__crumb { display: inline-flex; align-items: center; gap: .35rem; font-family: var(--rz-font-mono); font-size: .75rem; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); text-decoration: none; margin: 0 0 1rem; }
|
|
876
|
+
.rz-admin .rz-help__crumb:hover { color: var(--signal); }
|
|
877
|
+
.rz-admin .rz-help__prose { line-height: 1.65; color: var(--text-primary); }
|
|
878
|
+
.rz-admin .rz-help__prose > * + * { margin-top: .85rem; }
|
|
879
|
+
.rz-admin .rz-help__prose h2 { font-family: var(--rz-font-display); font-size: 1.1rem; font-weight: 600; margin-top: 1.75rem; }
|
|
880
|
+
.rz-admin .rz-help__prose h3 { font-size: .95rem; font-weight: 600; margin-top: 1.25rem; }
|
|
881
|
+
.rz-admin .rz-help__prose p, .rz-admin .rz-help__prose li { color: var(--text-secondary); }
|
|
882
|
+
.rz-admin .rz-help__prose strong { color: var(--text-primary); font-weight: 600; }
|
|
883
|
+
.rz-admin .rz-help__prose ul, .rz-admin .rz-help__prose ol { padding-left: 1.35rem; }
|
|
884
|
+
.rz-admin .rz-help__prose li + li { margin-top: .3rem; }
|
|
885
|
+
.rz-admin .rz-help__prose code { font-family: var(--rz-font-mono); font-size: .85em; background: var(--surface-2); border: 1px solid var(--border); border-radius: 4px; padding: .05em .35em; }
|
|
886
|
+
.rz-admin .rz-help__prose pre { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--rz-radius); padding: .8rem 1rem; overflow-x: auto; }
|
|
887
|
+
.rz-admin .rz-help__prose pre code { background: transparent; border: 0; padding: 0; font-size: .82rem; line-height: 1.55; }
|
|
888
|
+
.rz-admin .rz-help__prose blockquote { margin: 0; padding: .2rem 0 .2rem 1rem; border-left: 3px solid var(--signal); color: var(--text-secondary); }
|
|
889
|
+
.rz-admin .rz-help__prose a { color: var(--signal); text-decoration: none; }
|
|
890
|
+
.rz-admin .rz-help__prose a:hover { color: var(--text-primary); }
|
|
891
|
+
.rz-admin .rz-help__prose .rz-table td { white-space: normal; color: var(--text-secondary); }
|
|
892
|
+
.rz-admin .rz-help__pager { display: flex; justify-content: space-between; gap: 1rem; margin-top: 2.5rem; padding-top: 1rem; border-top: 1px solid var(--separator); font-size: .875rem; }
|
|
893
|
+
.rz-admin .rz-help__pager a { display: inline-flex; align-items: center; gap: .35rem; color: var(--text-secondary); text-decoration: none; }
|
|
894
|
+
.rz-admin .rz-help__pager a:hover { color: var(--signal); }
|
|
895
|
+
.rz-admin .rz-arrow--back { transform: none; }
|
|
896
|
+
@media (prefers-reduced-motion: no-preference) { .rz-admin a:hover .rz-arrow--back { transform: translateX(-4px); } }
|
|
832
897
|
`;
|
|
833
|
-
function StudioShellFrame({ siteName, logoUrl, user, role, nav, pinnedNav, branding, children }) {
|
|
898
|
+
function StudioShellFrame({ siteName, logoUrl, siteUrl, user, role, nav, pinnedNav, branding, children }) {
|
|
834
899
|
const pathname = navigation.usePathname() ?? "/admin";
|
|
835
900
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
836
901
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: STUDIO_CSS }),
|
|
837
|
-
/* @__PURE__ */ jsxRuntime.jsx(AdminShell, { siteName, logoUrl: logoUrl ?? void 0, user, role, nav, pinnedNav, branding, accountHref: null, activeHref: pathname, children })
|
|
902
|
+
/* @__PURE__ */ jsxRuntime.jsx(AdminShell, { siteName, logoUrl: logoUrl ?? void 0, siteUrl, user, role, nav, pinnedNav, branding, accountHref: null, activeHref: pathname, children })
|
|
838
903
|
] });
|
|
839
904
|
}
|
|
840
905
|
|
|
@@ -3395,26 +3460,6 @@ function StudioPullRequest({ pr, status, item }) {
|
|
|
3395
3460
|
/* @__PURE__ */ jsxRuntime.jsx(Card, { className: "rz-card--narrow", children: /* @__PURE__ */ jsxRuntime.jsx(PublishPanel, { number: pr.number, htmlUrl: pr.html_url, merged: pr.merged, closed: pr.state === "closed" && !pr.merged, branch: pr.branch, removal: pr.removal, mergedSha: pr.mergeCommitSha, status, item }) })
|
|
3396
3461
|
] });
|
|
3397
3462
|
}
|
|
3398
|
-
function StudioHelp() {
|
|
3399
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3400
|
-
/* @__PURE__ */ jsxRuntime.jsx(PageHeader, { eyebrow: "Help", title: "Help centre", subtitle: "Everything about running your website from Studio, in plain English." }),
|
|
3401
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "rz-card--narrow", children: [
|
|
3402
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { marginTop: 0 }, children: [
|
|
3403
|
-
"The full guide is on its way. In the meantime the ",
|
|
3404
|
-
/* @__PURE__ */ jsxRuntime.jsx("b", { children: "Ask" }),
|
|
3405
|
-
" bubble in the bottom-right corner knows how Studio works \u2014 ask it anything, from \u201Cwhat does Publish do?\u201D to \u201Chow do I add a picture?\u201D."
|
|
3406
|
-
] }),
|
|
3407
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { marginBottom: 4 }, children: "Coming here soon:" }),
|
|
3408
|
-
/* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "acct-help-list", children: [
|
|
3409
|
-
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Writing and editing a blog post \u2014 every field explained" }),
|
|
3410
|
-
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Pictures: choosing, sizing and where they go" }),
|
|
3411
|
-
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Save, checks, preview and Publish \u2014 the whole journey" }),
|
|
3412
|
-
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Fixing a change that \u201Cneeds fixing\u201D, discarding, removing a post" }),
|
|
3413
|
-
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your account, your team and putting your own name on Studio" })
|
|
3414
|
-
] })
|
|
3415
|
-
] })
|
|
3416
|
-
] });
|
|
3417
|
-
}
|
|
3418
3463
|
function StudioSignIn({ url, anonKey, redirectTo, siteName, error }) {
|
|
3419
3464
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-card", style: { maxWidth: 440, margin: "40px auto" }, children: [
|
|
3420
3465
|
error === "link" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-alert admin__alert", role: "alert", children: "That sign-in link didn't work \u2014 it may have expired or been used already. Request a new one below." }),
|
|
@@ -3834,9 +3879,1142 @@ function StudioAccount({ me, manages, team, branding, myUserId, myRole }) {
|
|
|
3834
3879
|
] });
|
|
3835
3880
|
}
|
|
3836
3881
|
|
|
3882
|
+
// src/studio-ui/help/sections.ts
|
|
3883
|
+
var HELP_SECTIONS = [
|
|
3884
|
+
{ id: "start", title: "Quick start", blurb: "Ten minutes from signing in to your first published change." },
|
|
3885
|
+
{ id: "publishing", title: "Publishing a change", blurb: "Save, checks, preview, publish \u2014 and how to undo." },
|
|
3886
|
+
{ id: "blog", title: "Blog posts", blurb: "Every field on a post, and what it does for readers and Google." },
|
|
3887
|
+
{ id: "pictures", title: "Pictures", blurb: "Where to get one, what size, and how to drop it in." },
|
|
3888
|
+
{ id: "markdown", title: "Writing in Markdown", blurb: "Headings, lists, links, tables \u2014 the formatting the editor understands." },
|
|
3889
|
+
{ id: "events", title: "Events", blurb: "Dates, times, time zones, kinds, and what to do when plans change." },
|
|
3890
|
+
{ id: "account", title: "Account & team", blurb: "Your profile, your password, who else can sign in, your branding." },
|
|
3891
|
+
{ id: "trouble", title: "Troubleshooting", blurb: "When something doesn't look right \u2014 what it means and what to do." }
|
|
3892
|
+
];
|
|
3893
|
+
|
|
3894
|
+
// src/studio-ui/help/quick-start.ts
|
|
3895
|
+
var QUICK_START = [
|
|
3896
|
+
{
|
|
3897
|
+
id: "what-is-studio",
|
|
3898
|
+
section: "start",
|
|
3899
|
+
title: "What Studio is (and isn't)",
|
|
3900
|
+
summary: "A simple front end over the files that make up your website. Nothing goes live by accident.",
|
|
3901
|
+
keywords: ["overview", "github", "repository", "how it works"],
|
|
3902
|
+
body: `
|
|
3903
|
+
## What it is
|
|
3904
|
+
|
|
3905
|
+
Studio is the publishing dashboard for {siteName}. It lets you write, edit and publish {types} without touching code or learning GitHub.
|
|
3906
|
+
|
|
3907
|
+
Your website is stored as plain files in a private, backed-up folder on GitHub called a **repository**. Every version of every page is kept forever. Studio reads that folder and writes to it on your behalf.
|
|
3908
|
+
|
|
3909
|
+
## Why it works this way
|
|
3910
|
+
|
|
3911
|
+
Because every edit goes through the same three steps \u2014 **Save**, **Checks**, **Publish** \u2014 a broken page can't reach your visitors. The website is rebuilt with your change in a private test copy first. If something is wrong, you're told, and the live site keeps showing the last good version.
|
|
3912
|
+
|
|
3913
|
+
## What it isn't
|
|
3914
|
+
|
|
3915
|
+
- It isn't a page builder. The design of the site is fixed by {supportName}; Studio fills it with content.
|
|
3916
|
+
- It isn't instant. Publishing takes about two minutes end to end, on purpose.
|
|
3917
|
+
- It isn't the only way in. The same files can be edited on GitHub directly, so nothing is ever locked inside Studio.
|
|
3918
|
+
`
|
|
3919
|
+
},
|
|
3920
|
+
{
|
|
3921
|
+
id: "signing-in",
|
|
3922
|
+
section: "start",
|
|
3923
|
+
title: "Signing in",
|
|
3924
|
+
summary: "Email link by default; a password if you set one. No account to create.",
|
|
3925
|
+
keywords: ["login", "magic link", "password", "email link"],
|
|
3926
|
+
body: `
|
|
3927
|
+
## How it works
|
|
3928
|
+
|
|
3929
|
+
Go to **/admin** on your website. Enter your email and press **Send me a sign-in link**. Open the email and click the link \u2014 you're in. The link is single-use and expires after a short while.
|
|
3930
|
+
|
|
3931
|
+
There's no account to create: {supportName} invited your email address, which is what allows you in.
|
|
3932
|
+
|
|
3933
|
+
## Prefer a password?
|
|
3934
|
+
|
|
3935
|
+
Under the email form, click **Have a password? Sign in with it instead**. A password is optional \u2014 you set one under **Settings \u2192 Change Password** after signing in with an email link. Email links keep working either way.
|
|
3936
|
+
|
|
3937
|
+
## Forgot your password?
|
|
3938
|
+
|
|
3939
|
+
There's nothing to reset. Sign in with an email link, then set a new password under **Settings \u2192 Change Password**.
|
|
3940
|
+
|
|
3941
|
+
## Signing out
|
|
3942
|
+
|
|
3943
|
+
Open the avatar menu (top right) and choose **Sign out**.
|
|
3944
|
+
`
|
|
3945
|
+
},
|
|
3946
|
+
{
|
|
3947
|
+
id: "finding-your-way",
|
|
3948
|
+
section: "start",
|
|
3949
|
+
title: "Finding your way around",
|
|
3950
|
+
summary: "The sidebar, the dashboard, the lists, and the avatar menu.",
|
|
3951
|
+
keywords: ["sidebar", "dashboard", "navigation", "menu", "settings", "view website", "live site", "visit site"],
|
|
3952
|
+
body: `
|
|
3953
|
+
## The sidebar
|
|
3954
|
+
|
|
3955
|
+
One item per kind of content this site publishes \u2014 on {siteName} that's {types}. Each opens a list of everything of that kind with its status. **Dashboard** at the top shows one card per kind with a count and how many changes are waiting. **Settings** at the bottom opens your account.
|
|
3956
|
+
|
|
3957
|
+
On a narrow screen the sidebar collapses to icons; press the arrow at the bottom to expand it.
|
|
3958
|
+
|
|
3959
|
+
## A list page
|
|
3960
|
+
|
|
3961
|
+
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.
|
|
3962
|
+
|
|
3963
|
+
## Getting to the live website
|
|
3964
|
+
|
|
3965
|
+
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.
|
|
3966
|
+
|
|
3967
|
+
## The avatar menu
|
|
3968
|
+
|
|
3969
|
+
Top right, your photo or initials. From here: a light/dark toggle, **View website**, **Account**, **Help center** (this page), and **Sign out**.
|
|
3970
|
+
|
|
3971
|
+
## The Ask bubble
|
|
3972
|
+
|
|
3973
|
+
If your site has one (bottom right), it answers questions about Studio from the same articles you're reading now. Ask it things like "how do I add a picture to a post?".
|
|
3974
|
+
`
|
|
3975
|
+
},
|
|
3976
|
+
{
|
|
3977
|
+
id: "your-first-change",
|
|
3978
|
+
section: "start",
|
|
3979
|
+
title: "Your first change, start to finish",
|
|
3980
|
+
summary: "Edit something small, save it, watch the checks, preview it, publish it.",
|
|
3981
|
+
keywords: ["tutorial", "walkthrough", "first post", "publish"],
|
|
3982
|
+
body: `
|
|
3983
|
+
Pick something small \u2014 a typo in an existing post is perfect.
|
|
3984
|
+
|
|
3985
|
+
1. In the sidebar, open the list, then click the item's title.
|
|
3986
|
+
2. Make the edit. The form checks required fields as you go; a red message means something needs attention before you can save.
|
|
3987
|
+
3. Press **Save changes** (or **Save and continue** for a new item). Studio writes the file and opens a **change**. Nothing is live yet.
|
|
3988
|
+
4. You land on the change page. A tracker shows **Saved \u2192 Checks \u2192 Published \u2192 Live**. Checks take a minute or two; the page refreshes itself.
|
|
3989
|
+
5. When checks pass, **Preview the change** opens a private copy of the website with your edit in it. Look at it on your phone too.
|
|
3990
|
+
6. Press **Publish**. The **Live** step spins until the website has really rebuilt (about two minutes), then **View live** unlocks.
|
|
3991
|
+
|
|
3992
|
+
That's the whole loop. Every edit you ever make is these six steps.
|
|
3993
|
+
|
|
3994
|
+
## If checks fail
|
|
3995
|
+
|
|
3996
|
+
The status becomes **Needs fixing** and the message names the problem. Open the item, fix it, save again \u2014 the same change re-runs its checks. Nothing was published.
|
|
3997
|
+
`
|
|
3998
|
+
},
|
|
3999
|
+
{
|
|
4000
|
+
id: "statuses",
|
|
4001
|
+
section: "start",
|
|
4002
|
+
title: "What the statuses mean",
|
|
4003
|
+
summary: "Live, Checking\u2026, Ready to publish, Needs fixing, New \u2014 and what to do for each.",
|
|
4004
|
+
keywords: ["status", "pill", "checking", "ready", "needs fixing", "live", "new"],
|
|
4005
|
+
body: `
|
|
4006
|
+
| Status | What it means | What to do |
|
|
4007
|
+
| --- | --- | --- |
|
|
4008
|
+
| **Live** | What's on the website now. No change waiting. | Nothing \u2014 or click the title to start an edit. |
|
|
4009
|
+
| **Checking\u2026** | You saved; the test build is running. | Wait a minute or two. The page refreshes itself. |
|
|
4010
|
+
| **Ready to publish** | Checks passed. | Open the change and press **Publish** (preview first if you like). |
|
|
4011
|
+
| **Needs fixing** | The test build failed. Nothing was published. | Open the item, fix what the message names, save again. |
|
|
4012
|
+
| **New \xB7 \u2026** | A brand-new item that exists only in a change. | It isn't on the website until you publish it. |
|
|
4013
|
+
| **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. |
|
|
4014
|
+
|
|
4015
|
+
One item has at most one change waiting at a time. Saving again updates that same change rather than starting a second one.
|
|
4016
|
+
`
|
|
4017
|
+
}
|
|
4018
|
+
];
|
|
4019
|
+
|
|
4020
|
+
// src/studio-ui/help/publishing.ts
|
|
4021
|
+
var PUBLISHING = [
|
|
4022
|
+
{
|
|
4023
|
+
id: "save-checks-publish-live",
|
|
4024
|
+
section: "publishing",
|
|
4025
|
+
title: "Save, Checks, Publish, Live \u2014 the journey",
|
|
4026
|
+
summary: "What happens at each step, how long it takes, and why there are four of them.",
|
|
4027
|
+
keywords: ["pull request", "change", "PR", "build", "deploy", "how long"],
|
|
4028
|
+
body: `
|
|
4029
|
+
## Save
|
|
4030
|
+
|
|
4031
|
+
You press **Save**. Studio writes your item as a file and opens a **change** \u2014 GitHub calls this a pull request. Think of it as a draft handed in for checking. The live website hasn't changed.
|
|
4032
|
+
|
|
4033
|
+
## Checks
|
|
4034
|
+
|
|
4035
|
+
The moment a change exists, the website builds a private test copy with your change in it. This catches the things that would break a page: a required field left empty, a picture that isn't there, a date that doesn't exist. It usually takes one to two minutes. The change page shows a tracker and refreshes itself \u2014 you can leave and come back.
|
|
4036
|
+
|
|
4037
|
+
## Publish
|
|
4038
|
+
|
|
4039
|
+
When the checks pass, the status becomes **Ready to publish** and the **Publish** button unlocks. **Preview the change** appears too: a private copy of the real website with your edit in it, so you see exactly how it will look. Pressing **Publish** sends the change to the live site.
|
|
4040
|
+
|
|
4041
|
+
## Live
|
|
4042
|
+
|
|
4043
|
+
The website rebuilds itself with your change \u2014 about two minutes. The **Live** step keeps spinning until that's really done, then **View live** unlocks. If you look at the website before Live turns green you'll still see the old version. That's normal.
|
|
4044
|
+
|
|
4045
|
+
## Why four steps
|
|
4046
|
+
|
|
4047
|
+
Because it means a broken page can never reach a visitor. Everything you publish has already been built once, successfully, with your change in it.
|
|
4048
|
+
`
|
|
4049
|
+
},
|
|
4050
|
+
{
|
|
4051
|
+
id: "preview-a-change",
|
|
4052
|
+
section: "publishing",
|
|
4053
|
+
title: "Previewing before you publish",
|
|
4054
|
+
summary: "See your edit on the real site design, privately, before anyone else can.",
|
|
4055
|
+
keywords: ["preview", "test copy", "private link"],
|
|
4056
|
+
body: `
|
|
4057
|
+
## What it is
|
|
4058
|
+
|
|
4059
|
+
After checks pass, the change page shows **Preview the change**. It opens a private copy of the website \u2014 same design, same everything \u2014 with your edit in it.
|
|
4060
|
+
|
|
4061
|
+
## Why you'd use it
|
|
4062
|
+
|
|
4063
|
+
Pictures, long titles and tables can look different on the real page than in the editor. Check on your phone as well; most visitors are on one.
|
|
4064
|
+
|
|
4065
|
+
## Where it goes
|
|
4066
|
+
|
|
4067
|
+
The preview link is private to the change. It isn't on the live website and won't be found by Google. It disappears once the change is published or discarded.
|
|
4068
|
+
|
|
4069
|
+
## Notes
|
|
4070
|
+
|
|
4071
|
+
- Editing again after previewing? Save, wait for the new checks, preview again.
|
|
4072
|
+
- New pictures show in the preview exactly as they'll go live.
|
|
4073
|
+
`
|
|
4074
|
+
},
|
|
4075
|
+
{
|
|
4076
|
+
id: "publish-a-change",
|
|
4077
|
+
section: "publishing",
|
|
4078
|
+
title: "Publishing",
|
|
4079
|
+
summary: "Press Publish once checks pass; wait for Live to turn green.",
|
|
4080
|
+
keywords: ["publish", "go live", "merge"],
|
|
4081
|
+
body: `
|
|
4082
|
+
1. Open the change \u2014 from the item's **Status** link, the notice at the top of the item, or the dashboard.
|
|
4083
|
+
2. The tracker should show **Checks** green and the status **Ready to publish**.
|
|
4084
|
+
3. Press **Publish**.
|
|
4085
|
+
4. Wait for **Live** to turn green (about two minutes). **View live** unlocks the moment the website has rebuilt.
|
|
4086
|
+
|
|
4087
|
+
## Who can publish
|
|
4088
|
+
|
|
4089
|
+
Editors and owners. Every publish is recorded with your name, so it's always clear who changed what.
|
|
4090
|
+
|
|
4091
|
+
## If Publish is disabled
|
|
4092
|
+
|
|
4093
|
+
Checks haven't passed yet (wait), or failed (fix and save again). The button never unlocks on a failing change.
|
|
4094
|
+
`
|
|
4095
|
+
},
|
|
4096
|
+
{
|
|
4097
|
+
id: "needs-fixing",
|
|
4098
|
+
section: "publishing",
|
|
4099
|
+
title: "When a change 'needs fixing'",
|
|
4100
|
+
summary: "The test build failed. Nothing was published. Fix the named problem and save again.",
|
|
4101
|
+
keywords: ["failed", "error", "red", "checks failed", "broken"],
|
|
4102
|
+
body: `
|
|
4103
|
+
## What happened
|
|
4104
|
+
|
|
4105
|
+
The website was rebuilt with your change and something broke. The live site is untouched \u2014 it still shows the last good version.
|
|
4106
|
+
|
|
4107
|
+
## What to do
|
|
4108
|
+
|
|
4109
|
+
1. Open the item. The message at the top names the problem \u2014 most often a required field, a picture path, or a date/time that isn't valid.
|
|
4110
|
+
2. Fix it and press **Save**. The **same change** re-runs its checks; you don't get a second one.
|
|
4111
|
+
3. When the status flips to **Ready to publish**, publish as usual.
|
|
4112
|
+
|
|
4113
|
+
## If the message doesn't make sense
|
|
4114
|
+
|
|
4115
|
+
Press **Discard this change** to throw the edit away and start over, or send the message to {supportName}. Copy the whole line \u2014 the exact words matter.
|
|
4116
|
+
`
|
|
4117
|
+
},
|
|
4118
|
+
{
|
|
4119
|
+
id: "discard-a-change",
|
|
4120
|
+
section: "publishing",
|
|
4121
|
+
title: "Discarding a change",
|
|
4122
|
+
summary: "Throw away unpublished edits. The live site is never touched.",
|
|
4123
|
+
keywords: ["undo", "cancel", "throw away", "revert", "discard"],
|
|
4124
|
+
body: `
|
|
4125
|
+
## What it does
|
|
4126
|
+
|
|
4127
|
+
Deletes the waiting change and everything in it \u2014 the edited text and any new pictures. The item goes back to **Live** exactly as it was.
|
|
4128
|
+
|
|
4129
|
+
## How
|
|
4130
|
+
|
|
4131
|
+
On the change page, scroll down to **Discard this change**. Confirm. Done.
|
|
4132
|
+
|
|
4133
|
+
## When you'd use it
|
|
4134
|
+
|
|
4135
|
+
- You changed your mind.
|
|
4136
|
+
- Checks failed and starting fresh is easier than fixing.
|
|
4137
|
+
- You saved the wrong item by accident.
|
|
4138
|
+
|
|
4139
|
+
Discarding is only for unpublished work. Something that's already live is edited by saving a new change on top of it.
|
|
4140
|
+
`
|
|
4141
|
+
},
|
|
4142
|
+
{
|
|
4143
|
+
id: "remove-an-item",
|
|
4144
|
+
section: "publishing",
|
|
4145
|
+
title: "Removing an item from the website",
|
|
4146
|
+
summary: "Take a post or event down \u2014 through the same checked, publishable change.",
|
|
4147
|
+
keywords: ["delete", "unpublish", "take down", "remove"],
|
|
4148
|
+
body: `
|
|
4149
|
+
## What it is
|
|
4150
|
+
|
|
4151
|
+
Removing takes an item off the website. Like every edit, it travels as a change: **Removing\u2026**, then **Removal ready**, then you **Publish** the removal.
|
|
4152
|
+
|
|
4153
|
+
## How
|
|
4154
|
+
|
|
4155
|
+
1. Open the item.
|
|
4156
|
+
2. At the bottom, press **Remove this post from the website** (for other kinds the link names them \u2014 "Remove this event\u2026") and confirm with **Yes, remove it**.
|
|
4157
|
+
3. Publish the removal from the change page when it's ready. **Cancel this removal** on that page keeps the item.
|
|
4158
|
+
|
|
4159
|
+
## Good to know
|
|
4160
|
+
|
|
4161
|
+
- Until you publish, the item is still live. Discarding the change keeps it.
|
|
4162
|
+
- Every version is kept in the repository, so {supportName} can bring a removed item back if you ever need it.
|
|
4163
|
+
- Pictures that belonged only to that item go with it.
|
|
4164
|
+
`
|
|
4165
|
+
},
|
|
4166
|
+
{
|
|
4167
|
+
id: "two-people-editing",
|
|
4168
|
+
section: "publishing",
|
|
4169
|
+
title: "Two people editing the same item",
|
|
4170
|
+
summary: "The second to publish is told the file changed first. Nothing is silently overwritten.",
|
|
4171
|
+
keywords: ["conflict", "overwrite", "team", "collision"],
|
|
4172
|
+
body: `
|
|
4173
|
+
## What happens
|
|
4174
|
+
|
|
4175
|
+
If someone published a change to an item after you opened it, your publish is refused with a note that the file changed first. Your edit isn't lost \u2014 it's still in your change.
|
|
4176
|
+
|
|
4177
|
+
## What to do
|
|
4178
|
+
|
|
4179
|
+
Open the item fresh (it now shows the newest version), make your edit again, save, and publish.
|
|
4180
|
+
|
|
4181
|
+
## Avoiding it
|
|
4182
|
+
|
|
4183
|
+
One item, one change at a time is the rule Studio enforces. If several people write, agree who owns which items, or finish one change before starting the next.
|
|
4184
|
+
`
|
|
4185
|
+
}
|
|
4186
|
+
];
|
|
4187
|
+
|
|
4188
|
+
// src/studio-ui/help/blog.ts
|
|
4189
|
+
var BLOG = [
|
|
4190
|
+
{
|
|
4191
|
+
id: "new-post",
|
|
4192
|
+
section: "blog",
|
|
4193
|
+
title: "Writing a new blog post",
|
|
4194
|
+
summary: "Title, web address, excerpt, date, tags, a header picture, the article \u2014 then Save and continue.",
|
|
4195
|
+
keywords: ["new post", "article", "write", "create post"],
|
|
4196
|
+
body: `
|
|
4197
|
+
1. Sidebar \u2192 **Blog posts** \u2192 **+ New post**.
|
|
4198
|
+
2. Type the **Title**. The **Web address** fills itself from it \u2014 fine to leave.
|
|
4199
|
+
3. Write the **Excerpt** (the teaser on the blog page), check the **Publish date**, add exactly three **Tags**.
|
|
4200
|
+
4. Drop in a **Header image** and describe it in **Header image description**. A post needs a header picture before it can go live.
|
|
4201
|
+
5. Write the article in **Post content** \u2014 see *Writing in Markdown*.
|
|
4202
|
+
6. Open **Search & social** and fill **Meta description** at least.
|
|
4203
|
+
7. Press **Save and continue**, wait for the checks, preview, **Publish**.
|
|
4204
|
+
|
|
4205
|
+
## Where it goes
|
|
4206
|
+
|
|
4207
|
+
The post appears on the blog index, in the RSS feed and in the sitemap automatically, at yoursite.com/blog/your-web-address.
|
|
4208
|
+
|
|
4209
|
+
## Tips
|
|
4210
|
+
|
|
4211
|
+
- Write the post first, polish the search fields last.
|
|
4212
|
+
- Sort the picture before you start \u2014 it's the step most likely to hold you up.
|
|
4213
|
+
- Saving early is fine: you can keep editing and saving onto the same change until you publish.
|
|
4214
|
+
`
|
|
4215
|
+
},
|
|
4216
|
+
{
|
|
4217
|
+
id: "post-fields",
|
|
4218
|
+
section: "blog",
|
|
4219
|
+
title: "Every field on a blog post",
|
|
4220
|
+
summary: "What each field does, where it shows up, and what a good one looks like.",
|
|
4221
|
+
keywords: ["fields", "title", "slug", "excerpt", "tags", "author", "featured", "publish date", "last updated"],
|
|
4222
|
+
body: `
|
|
4223
|
+
## Title (H1)
|
|
4224
|
+
|
|
4225
|
+
The headline at the top of the post and the text of links to it. Say what the reader gets. Length isn't limited, but 50\u201370 characters reads best in lists.
|
|
4226
|
+
|
|
4227
|
+
## Web address (slug)
|
|
4228
|
+
|
|
4229
|
+
The last part of the post's link: my-first-post \u2192 yoursite.com/blog/my-first-post. Lowercase words joined by hyphens, no spaces. It fills itself from the title; shorten it if the title is long. Changing it after publishing changes the link \u2014 old links break \u2014 so settle it before the first publish.
|
|
4230
|
+
|
|
4231
|
+
## Excerpt
|
|
4232
|
+
|
|
4233
|
+
The one- or two-sentence teaser shown on the blog index under the title. Not the first paragraph again; a reason to click.
|
|
4234
|
+
|
|
4235
|
+
## Publish date / Last updated
|
|
4236
|
+
|
|
4237
|
+
Shown on the post and used to order the list. **Last updated** is optional \u2014 set it when you revise a published post and readers see "Updated \u2026".
|
|
4238
|
+
|
|
4239
|
+
## Author
|
|
4240
|
+
|
|
4241
|
+
How you're credited. Fills from your profile's "Name on blog posts" (Settings \u2192 My Profile).
|
|
4242
|
+
|
|
4243
|
+
## Tags
|
|
4244
|
+
|
|
4245
|
+
Exactly three, lowercase, hyphens between words (small-business, ai-tools). Type one and press Enter or a comma. Tags group related posts and drive the "related posts" links.
|
|
4246
|
+
|
|
4247
|
+
## Header image / Header image description
|
|
4248
|
+
|
|
4249
|
+
The picture at the top of the post and on cards. The description is the alt text \u2014 say what's in the picture for readers who can't see it. See *Pictures*.
|
|
4250
|
+
|
|
4251
|
+
## Featured post
|
|
4252
|
+
|
|
4253
|
+
Pins the post to the top of the blog index. One or two at a time is plenty.
|
|
4254
|
+
|
|
4255
|
+
## Post content
|
|
4256
|
+
|
|
4257
|
+
The article itself, in Markdown. See *Writing in Markdown*.
|
|
4258
|
+
`
|
|
4259
|
+
},
|
|
4260
|
+
{
|
|
4261
|
+
id: "search-and-social",
|
|
4262
|
+
section: "blog",
|
|
4263
|
+
title: "The Search & social fields",
|
|
4264
|
+
summary: "Search title, meta description, focus keyword, social image, canonical \u2014 what Google and social cards see.",
|
|
4265
|
+
keywords: ["seo", "meta description", "search title", "focus keyword", "canonical", "og image", "social share", "google"],
|
|
4266
|
+
body: `
|
|
4267
|
+
These sit in their own group under the main fields. Readers never see them on the page \u2014 search engines and social networks do.
|
|
4268
|
+
|
|
4269
|
+
## Search title
|
|
4270
|
+
|
|
4271
|
+
What Google shows as the blue link and what the browser tab says. Aim for 50\u201360 characters including your site name. Blank means the Title is used. The gauge under the field turns green inside the target range.
|
|
4272
|
+
|
|
4273
|
+
## Meta description
|
|
4274
|
+
|
|
4275
|
+
The gray snippet under the link in Google results. 120\u2013155 characters: what the post covers and why to read it. Google may write its own, but a good one usually wins.
|
|
4276
|
+
|
|
4277
|
+
## Focus keyword
|
|
4278
|
+
|
|
4279
|
+
The single phrase this post should rank for ("small business ai audit"). Never shown; used for reporting. One per post.
|
|
4280
|
+
|
|
4281
|
+
## Social share image
|
|
4282
|
+
|
|
4283
|
+
The picture shown when the link is shared on LinkedIn, Facebook or in messages. 1200 \xD7 630 works everywhere. Blank reuses the header image \u2014 usually fine.
|
|
4284
|
+
|
|
4285
|
+
## Canonical URL
|
|
4286
|
+
|
|
4287
|
+
Advanced. Only if the same article was published somewhere else first (LinkedIn, Medium): paste that original link so Google credits it and doesn't treat yours as a copy. Otherwise leave blank.
|
|
4288
|
+
`
|
|
4289
|
+
},
|
|
4290
|
+
{
|
|
4291
|
+
id: "edit-a-post",
|
|
4292
|
+
section: "blog",
|
|
4293
|
+
title: "Editing a published post",
|
|
4294
|
+
summary: "Open it, change it, save \u2014 the live version stays up until you publish the change.",
|
|
4295
|
+
keywords: ["edit", "update", "revise", "change post"],
|
|
4296
|
+
body: `
|
|
4297
|
+
1. Sidebar \u2192 **Blog posts** \u2192 click the title.
|
|
4298
|
+
2. Existing posts open in **Preview** so you can read first; click **Edit Markdown** to change the text.
|
|
4299
|
+
3. Make your edits. Consider setting **Last updated** to today.
|
|
4300
|
+
4. Press **Save changes**, wait for checks, preview, **Publish**.
|
|
4301
|
+
|
|
4302
|
+
## Good to know
|
|
4303
|
+
|
|
4304
|
+
- If a change is already waiting for this post, a notice at the top says so and links to it. Saving updates that change.
|
|
4305
|
+
- Renaming the **Web address** breaks old links. If you must, ask {supportName} to add a redirect.
|
|
4306
|
+
- Nothing you do here changes the live post until you press Publish.
|
|
4307
|
+
`
|
|
4308
|
+
},
|
|
4309
|
+
{
|
|
4310
|
+
id: "post-tips",
|
|
4311
|
+
section: "blog",
|
|
4312
|
+
title: "Writing posts that work",
|
|
4313
|
+
summary: "Structure, length, headings, links \u2014 what makes a post readable and findable.",
|
|
4314
|
+
keywords: ["tips", "writing", "structure", "length", "headings"],
|
|
4315
|
+
body: `
|
|
4316
|
+
- **One idea per post.** If you have two, write two posts and link them.
|
|
4317
|
+
- **Say the answer early.** The first paragraph should tell a hurried reader what they'll learn.
|
|
4318
|
+
- **Use section headings** every 200\u2013300 words. Readers scan; Google reads headings too.
|
|
4319
|
+
- **Short paragraphs.** Two to four sentences. Phones make long paragraphs a wall.
|
|
4320
|
+
- **Link to your own pages** where it helps ("we explain this on our services page") and to sources you quote.
|
|
4321
|
+
- **800\u20131,500 words** suits most topics. Shorter is fine if it's complete; longer is fine if it earns it.
|
|
4322
|
+
- **Pictures with alt text**, one near the top and one wherever a picture explains faster than words.
|
|
4323
|
+
- **Finish with a next step**: a related post, a contact page, a question to think about.
|
|
4324
|
+
`
|
|
4325
|
+
}
|
|
4326
|
+
];
|
|
4327
|
+
|
|
4328
|
+
// src/studio-ui/help/pictures.ts
|
|
4329
|
+
var PICTURES = [
|
|
4330
|
+
{
|
|
4331
|
+
id: "where-pictures-come-from",
|
|
4332
|
+
section: "pictures",
|
|
4333
|
+
title: "Getting a picture to use",
|
|
4334
|
+
summary: "Your phone, a stock library, Canva, or an AI image tool \u2014 make it outside Studio, then drop it in.",
|
|
4335
|
+
keywords: ["stock photo", "canva", "ai image", "unsplash", "photo", "camera", "create image"],
|
|
4336
|
+
body: `
|
|
4337
|
+
Studio doesn't make pictures; it takes the ones you give it. Four good sources:
|
|
4338
|
+
|
|
4339
|
+
## Your phone
|
|
4340
|
+
|
|
4341
|
+
Real photos of your work, your team, your space beat stock every time. Landscape (wider than tall) suits headers. Wipe the lens.
|
|
4342
|
+
|
|
4343
|
+
## A stock library
|
|
4344
|
+
|
|
4345
|
+
Unsplash, Pexels and Pixabay are free for commercial use. Search a feeling, not an object ("focused workshop" beats "laptop"). Download the large size.
|
|
4346
|
+
|
|
4347
|
+
## Canva (or similar)
|
|
4348
|
+
|
|
4349
|
+
For header images with text on them and for social share images. Start from a 1200 \xD7 630 template for social, 1600 \xD7 900 for headers. Export as JPG or PNG.
|
|
4350
|
+
|
|
4351
|
+
## An AI image tool
|
|
4352
|
+
|
|
4353
|
+
ChatGPT, Midjourney and the rest can make a fitting illustration in a minute. Describe the scene, the mood and "no text" (AI text is usually garbled). Download the largest version offered.
|
|
4354
|
+
|
|
4355
|
+
## Then
|
|
4356
|
+
|
|
4357
|
+
Save it to your computer and drop it onto the picture field in Studio \u2014 see *Adding a picture to a post or event*. Studio handles the resizing.
|
|
4358
|
+
|
|
4359
|
+
## Rights
|
|
4360
|
+
|
|
4361
|
+
Only use pictures you took, bought, generated, or that are licensed for commercial use. When in doubt, ask {supportName}.
|
|
4362
|
+
`
|
|
4363
|
+
},
|
|
4364
|
+
{
|
|
4365
|
+
id: "add-a-picture",
|
|
4366
|
+
section: "pictures",
|
|
4367
|
+
title: "Adding a picture to a post or event",
|
|
4368
|
+
summary: "Drop, choose or paste. It's shrunk in your browser and saved with the item when you press Save.",
|
|
4369
|
+
keywords: ["upload", "header image", "hero", "drop zone", "drag", "paste image"],
|
|
4370
|
+
body: `
|
|
4371
|
+
## The picture fields
|
|
4372
|
+
|
|
4373
|
+
**Header image** (the big one at the top) and **Social share image** (for link previews) are drop zones. Drag a file onto one, click it to choose a file, or click it and paste (Cmd/Ctrl+V) a copied image.
|
|
4374
|
+
|
|
4375
|
+
## Pictures inside the text
|
|
4376
|
+
|
|
4377
|
+
In **Post content**, put the cursor where the picture goes and press the toolbar's **Image** button, then choose the file. A line like \`\` is inserted \u2014 edit the description in the square brackets to say what's in the picture.
|
|
4378
|
+
|
|
4379
|
+
## What happens next
|
|
4380
|
+
|
|
4381
|
+
- Nothing is uploaded when you pick. The picture travels with the item when you press **Save**, into the same change, and goes live when you **Publish**. Discarding the change discards its pictures too.
|
|
4382
|
+
- Your browser shrinks the picture first (about 1600 pixels wide, WebP format, usually 100\u2013400 KB), so pages stay fast. Accepted: JPG, PNG or WebP up to 10 MB.
|
|
4383
|
+
- Pictures are named after the item's **Web address**, so fill that in first. The header image becomes /blog/your-post.webp; pictures in the text go under /blog/your-post/.
|
|
4384
|
+
|
|
4385
|
+
## Removing or replacing
|
|
4386
|
+
|
|
4387
|
+
Drop a new picture on the field to replace it, or use the field's **Remove** to clear it. The change goes live with the next Publish.
|
|
4388
|
+
`
|
|
4389
|
+
},
|
|
4390
|
+
{
|
|
4391
|
+
id: "picture-sizes",
|
|
4392
|
+
section: "pictures",
|
|
4393
|
+
title: "Sizes and shapes that work",
|
|
4394
|
+
summary: "Landscape for headers, 1200 \xD7 630 for social, square for logos and avatars.",
|
|
4395
|
+
keywords: ["size", "dimensions", "aspect ratio", "resolution", "1200x630", "square"],
|
|
4396
|
+
body: `
|
|
4397
|
+
| Use | Best shape | Minimum |
|
|
4398
|
+
| --- | --- | --- |
|
|
4399
|
+
| Header image | Landscape, about 16:9 | 1600 pixels wide |
|
|
4400
|
+
| Social share image | 1.91:1 (1200 \xD7 630) | 1200 \xD7 630 |
|
|
4401
|
+
| Picture in the text | Any; landscape reads best | 1200 pixels wide |
|
|
4402
|
+
| Profile photo | Square, face centered | 400 \xD7 400 |
|
|
4403
|
+
| Business logo | Square or wide, on a plain background | 400 pixels on the short side |
|
|
4404
|
+
| Favicon (browser-tab icon) | Square, simple shape | 256 \xD7 256 |
|
|
4405
|
+
|
|
4406
|
+
## Why these
|
|
4407
|
+
|
|
4408
|
+
Bigger than needed is fine \u2014 Studio shrinks. Smaller than needed looks soft on large screens and can't be fixed later. Text on a header image gets cropped on phones; keep it near the center.
|
|
4409
|
+
|
|
4410
|
+
## File size
|
|
4411
|
+
|
|
4412
|
+
Don't worry about it. Studio converts everything to WebP at web size. The 10 MB limit is per original file.
|
|
4413
|
+
`
|
|
4414
|
+
},
|
|
4415
|
+
{
|
|
4416
|
+
id: "alt-text",
|
|
4417
|
+
section: "pictures",
|
|
4418
|
+
title: "Picture descriptions (alt text)",
|
|
4419
|
+
summary: "One sentence saying what's in the picture \u2014 for screen readers and for Google.",
|
|
4420
|
+
keywords: ["alt text", "accessibility", "description", "screen reader"],
|
|
4421
|
+
body: `
|
|
4422
|
+
## What it is
|
|
4423
|
+
|
|
4424
|
+
Every picture has a short text description that's read aloud to visitors who use a screen reader and shown if the picture can't load. Google reads it too.
|
|
4425
|
+
|
|
4426
|
+
## Where it goes
|
|
4427
|
+
|
|
4428
|
+
The **Header image description** field, and the words in square brackets for pictures in the text: \`\`.
|
|
4429
|
+
|
|
4430
|
+
## How to write one
|
|
4431
|
+
|
|
4432
|
+
Say what's in the picture as if describing it over the phone. "Two people reviewing a report at a kitchen table" \u2014 not "image" or "photo123" and not a list of keywords. Decorative pictures can have a short description; pictures that carry information need a full one.
|
|
4433
|
+
`
|
|
4434
|
+
}
|
|
4435
|
+
];
|
|
4436
|
+
|
|
4437
|
+
// src/studio-ui/help/markdown.ts
|
|
4438
|
+
var MARKDOWN = [
|
|
4439
|
+
{
|
|
4440
|
+
id: "the-editor",
|
|
4441
|
+
section: "markdown",
|
|
4442
|
+
title: "The content editor",
|
|
4443
|
+
summary: "Edit Markdown or Preview, one toolbar row, Expand for a full window.",
|
|
4444
|
+
keywords: ["editor", "toolbar", "preview", "expand", "fullscreen"],
|
|
4445
|
+
body: `
|
|
4446
|
+
## Two tabs
|
|
4447
|
+
|
|
4448
|
+
**Edit Markdown** is where you type. **Preview** shows exactly how the website will render it \u2014 same fonts, same spacing. An existing item opens in Preview so you can read first; a blank one opens in Edit.
|
|
4449
|
+
|
|
4450
|
+
## The toolbar
|
|
4451
|
+
|
|
4452
|
+
Buttons work on your selection and toggle on and off. Left to right: **Bold**, **Italic** \xB7 **Section heading**, **Sub-heading**, **Bulleted list**, **Numbered list**, **Quote** \xB7 **Link**, **Inline code** \xB7 Insert: **Image**, **Separator (\xB7)**, **Divider line**. When the cursor is inside a table, row and column buttons appear.
|
|
4453
|
+
|
|
4454
|
+
Hover a button for its name and shortcut. **Bold** is Cmd/Ctrl+B, **Italic** is Cmd/Ctrl+I.
|
|
4455
|
+
|
|
4456
|
+
## Expand
|
|
4457
|
+
|
|
4458
|
+
The arrows button at the right of the toolbar fills the window with the editor. Press **Esc** or the button again to come back.
|
|
4459
|
+
|
|
4460
|
+
## Why Markdown
|
|
4461
|
+
|
|
4462
|
+
It's plain text with a few marks \u2014 no hidden formatting to fight, it never breaks, and it reads fine even raw. You'll learn all of it in ten minutes; the toolbar does the typing for you anyway.
|
|
4463
|
+
`
|
|
4464
|
+
},
|
|
4465
|
+
{
|
|
4466
|
+
id: "markdown-basics",
|
|
4467
|
+
section: "markdown",
|
|
4468
|
+
title: "Markdown in ten minutes",
|
|
4469
|
+
summary: "Paragraphs, headings, bold and italic, lists, links, quotes.",
|
|
4470
|
+
keywords: ["markdown", "syntax", "formatting", "bold", "italic", "heading", "list", "link", "quote"],
|
|
4471
|
+
body: `
|
|
4472
|
+
## Paragraphs
|
|
4473
|
+
|
|
4474
|
+
Just type. Leave a blank line between paragraphs. A single line break inside a paragraph is ignored, so don't press Enter to wrap lines.
|
|
4475
|
+
|
|
4476
|
+
## Headings
|
|
4477
|
+
|
|
4478
|
+
\`\`\`
|
|
4479
|
+
## A section heading
|
|
4480
|
+
### A smaller sub-heading
|
|
4481
|
+
\`\`\`
|
|
4482
|
+
|
|
4483
|
+
Use \`##\` for the sections of your post and \`###\` under them. The title of the post is already the top heading, so never use a single \`#\`.
|
|
4484
|
+
|
|
4485
|
+
## Bold and italic
|
|
4486
|
+
|
|
4487
|
+
\`\`\`
|
|
4488
|
+
**bold** _italic_ **_both_**
|
|
4489
|
+
\`\`\`
|
|
4490
|
+
|
|
4491
|
+
## Lists
|
|
4492
|
+
|
|
4493
|
+
\`\`\`
|
|
4494
|
+
- first point
|
|
4495
|
+
- second point
|
|
4496
|
+
- a point under it (indent two spaces)
|
|
4497
|
+
|
|
4498
|
+
1. first step
|
|
4499
|
+
2. second step
|
|
4500
|
+
\`\`\`
|
|
4501
|
+
|
|
4502
|
+
Leave a blank line before a list.
|
|
4503
|
+
|
|
4504
|
+
## Links
|
|
4505
|
+
|
|
4506
|
+
\`\`\`
|
|
4507
|
+
[the words people click](https://example.com)
|
|
4508
|
+
[our services page](/services)
|
|
4509
|
+
\`\`\`
|
|
4510
|
+
|
|
4511
|
+
Select words and press the **Link** button; it wraps them and puts the cursor where the address goes.
|
|
4512
|
+
|
|
4513
|
+
## Quotes
|
|
4514
|
+
|
|
4515
|
+
\`\`\`
|
|
4516
|
+
> Something someone said, or a callout worth setting apart.
|
|
4517
|
+
\`\`\`
|
|
4518
|
+
|
|
4519
|
+
## Small touches
|
|
4520
|
+
|
|
4521
|
+
\`\`\`
|
|
4522
|
+
Some \`code\` or a product name in a fixed-width font
|
|
4523
|
+
--- (a divider line across the page)
|
|
4524
|
+
Date \xB7 Place (the \xB7 separator, from the Insert menu)
|
|
4525
|
+
\`\`\`
|
|
4526
|
+
`
|
|
4527
|
+
},
|
|
4528
|
+
{
|
|
4529
|
+
id: "markdown-tables",
|
|
4530
|
+
section: "markdown",
|
|
4531
|
+
title: "Tables",
|
|
4532
|
+
summary: "Pipes and dashes; the toolbar adds rows and columns when you're inside one.",
|
|
4533
|
+
keywords: ["table", "grid", "columns", "rows"],
|
|
4534
|
+
body: `
|
|
4535
|
+
## The shape
|
|
4536
|
+
|
|
4537
|
+
\`\`\`
|
|
4538
|
+
| Plan | Price | Best for |
|
|
4539
|
+
| --- | --- | --- |
|
|
4540
|
+
| Starter | $20/mo | One person |
|
|
4541
|
+
| Team | $60/mo | Up to five |
|
|
4542
|
+
\`\`\`
|
|
4543
|
+
|
|
4544
|
+
The first line is the header row; the line of dashes is required; every row has the same number of \`|\` separators. Spacing inside the cells doesn't matter \u2014 Preview lines everything up.
|
|
4545
|
+
|
|
4546
|
+
## Editing
|
|
4547
|
+
|
|
4548
|
+
Put the cursor anywhere in a table and the toolbar shows **+ Row**, **\u2212 Row**, **+ Col**, **\u2212 Col**. They act on the row or column the cursor is in.
|
|
4549
|
+
|
|
4550
|
+
## Good to know
|
|
4551
|
+
|
|
4552
|
+
- Keep cells short. Long text wraps and the table gets tall on phones.
|
|
4553
|
+
- Bold and links work inside cells; lists and headings don't.
|
|
4554
|
+
- Three columns is comfortable on a phone; five is the practical limit.
|
|
4555
|
+
`
|
|
4556
|
+
},
|
|
4557
|
+
{
|
|
4558
|
+
id: "markdown-images",
|
|
4559
|
+
section: "markdown",
|
|
4560
|
+
title: "Pictures inside the text",
|
|
4561
|
+
summary: "The Image button inserts one; the square brackets hold the description.",
|
|
4562
|
+
keywords: ["inline image", "image in post", "figure"],
|
|
4563
|
+
body: `
|
|
4564
|
+
## Insert
|
|
4565
|
+
|
|
4566
|
+
Put the cursor on its own blank line where the picture should appear and press the **Image** button in the toolbar. Choose the file. Studio inserts:
|
|
4567
|
+
|
|
4568
|
+
\`\`\`
|
|
4569
|
+

|
|
4570
|
+
\`\`\`
|
|
4571
|
+
|
|
4572
|
+
Replace the words in the square brackets with a real description (see *Picture descriptions*). Leave the address alone \u2014 it's where the picture will live once published.
|
|
4573
|
+
|
|
4574
|
+
## Placement
|
|
4575
|
+
|
|
4576
|
+
A picture on its own line spans the text column. Put one near the top of a long post and then wherever a picture explains faster than words. Don't put text on the same line as the picture.
|
|
4577
|
+
|
|
4578
|
+
## Preview shows it
|
|
4579
|
+
|
|
4580
|
+
Even before you save, **Preview** shows the new picture in place. After checks, **Preview the change** shows it on the real page.
|
|
4581
|
+
`
|
|
4582
|
+
},
|
|
4583
|
+
{
|
|
4584
|
+
id: "markdown-gotchas",
|
|
4585
|
+
section: "markdown",
|
|
4586
|
+
title: "Things that trip people up",
|
|
4587
|
+
summary: "Blank lines, pasted formatting, straight quotes, and where headings start.",
|
|
4588
|
+
keywords: ["problem", "not rendering", "paste from word", "weird formatting"],
|
|
4589
|
+
body: `
|
|
4590
|
+
- **My list runs into the paragraph above.** Leave a blank line before the list.
|
|
4591
|
+
- **My heading shows a # sign.** Put a space after the hashes: \`## Heading\`, not \`##Heading\`.
|
|
4592
|
+
- **Pasting from Word or Google Docs** brings hidden formatting. Paste, then check Preview; if it looks odd, paste again as plain text (Cmd/Ctrl+Shift+V) and add headings with the toolbar.
|
|
4593
|
+
- **A link doesn't work.** The address must start with https:// (or / for a page on your own site). No spaces inside the parentheses.
|
|
4594
|
+
- **Two line breaks in a row but no gap.** That's a blank line \u2014 it starts a new paragraph. To force a gap without text, a divider line \`---\` is the honest choice.
|
|
4595
|
+
- **Emoji and special characters** are fine. Just type them.
|
|
4596
|
+
`
|
|
4597
|
+
}
|
|
4598
|
+
];
|
|
4599
|
+
|
|
4600
|
+
// src/studio-ui/help/events.ts
|
|
4601
|
+
var EVENTS = [
|
|
4602
|
+
{
|
|
4603
|
+
id: "new-event",
|
|
4604
|
+
section: "events",
|
|
4605
|
+
title: "Adding an event",
|
|
4606
|
+
summary: "Title, what kind, date and times, where, a link \u2014 save, check, publish. Upcoming first on the site.",
|
|
4607
|
+
keywords: ["new event", "add event", "calendar", "appearance", "talk", "show", "workshop"],
|
|
4608
|
+
body: `
|
|
4609
|
+
Events are anything with a date: a workshop, a show, a talk, a podcast appearance, an open house. If {siteName} has an events section it appears in the sidebar under its own name.
|
|
4610
|
+
|
|
4611
|
+
1. Sidebar \u2192 the events list \u2192 **+ New**.
|
|
4612
|
+
2. **Title**, then check the **Web address** it suggests.
|
|
4613
|
+
3. **One-line summary** \u2014 what it is and who it's for. Shown in the list.
|
|
4614
|
+
4. **What kind of event**. Pick from the list, or choose **Other** and type a **Custom kind** ("Co-Keynote", "Wedding", "Pop-up").
|
|
4615
|
+
5. **Date**, then **Start** and **End** times if it isn't all-day. Set **Time zone** only if the event isn't in the site's home zone.
|
|
4616
|
+
6. **Where** \u2014 an address, a room, or "Online".
|
|
4617
|
+
7. **Link for details** \u2014 the event's own page, if it has one (tickets, the venue, a livestream).
|
|
4618
|
+
8. A **Picture** and its description if you have one.
|
|
4619
|
+
9. Write the long description below, press **Save and continue**, wait for checks, preview, **Publish**.
|
|
4620
|
+
|
|
4621
|
+
## Where it goes
|
|
4622
|
+
|
|
4623
|
+
The site's events page lists upcoming events first, then past ones. Each event has its own page at yoursite.com/\u2026/your-web-address with the details and your description. Search engines get an Event listing automatically.
|
|
4624
|
+
`
|
|
4625
|
+
},
|
|
4626
|
+
{
|
|
4627
|
+
id: "event-fields",
|
|
4628
|
+
section: "events",
|
|
4629
|
+
title: "Every field on an event",
|
|
4630
|
+
summary: "Kind and custom kind, date, times, time zone, where, link, pictures.",
|
|
4631
|
+
keywords: ["fields", "kind", "custom kind", "date", "start time", "end time", "time zone", "location"],
|
|
4632
|
+
body: `
|
|
4633
|
+
## Title / Web address / One-line summary
|
|
4634
|
+
|
|
4635
|
+
Same as a blog post: the name, the link, the teaser. Keep the summary to one or two sentences \u2014 it's shown in the list and to search engines.
|
|
4636
|
+
|
|
4637
|
+
## What kind of event / Custom kind
|
|
4638
|
+
|
|
4639
|
+
The kind is the small label on the event (Workshop, Talk, Podcast\u2026). The list is set for your site. If yours isn't on it, choose **Other** and type your own in **Custom kind** \u2014 it's shown exactly as you type it.
|
|
4640
|
+
|
|
4641
|
+
## Date
|
|
4642
|
+
|
|
4643
|
+
The day, picked from the calendar. Must be a real date.
|
|
4644
|
+
|
|
4645
|
+
## Start / End
|
|
4646
|
+
|
|
4647
|
+
24-hour times, e.g. 14:00 to 15:00. Leave both empty for an all-day event. End must be after Start.
|
|
4648
|
+
|
|
4649
|
+
## Time zone
|
|
4650
|
+
|
|
4651
|
+
Leave empty and the site's home zone is used. Set it when the event happens somewhere else \u2014 a talk in Denver for a New York site. Visitors see the time in the event's zone with its name, so nobody shows up an hour off.
|
|
4652
|
+
|
|
4653
|
+
## Where
|
|
4654
|
+
|
|
4655
|
+
Free text: an address, "Studio B", or "Online". Anything with the word "online" is marked as an online event for search engines.
|
|
4656
|
+
|
|
4657
|
+
## Link for details
|
|
4658
|
+
|
|
4659
|
+
The event's own web page. Shown as **Event website** on the event page. Not the same as an RSVP link (see *Tickets, venue and appearance fields*).
|
|
4660
|
+
|
|
4661
|
+
## Picture / Picture description / Social share image
|
|
4662
|
+
|
|
4663
|
+
As for blog posts. See *Pictures*.
|
|
4664
|
+
|
|
4665
|
+
## Canceled, Cancellation note, Moved to, Previous date
|
|
4666
|
+
|
|
4667
|
+
For when plans change. See *When an event is canceled or moves*.
|
|
4668
|
+
`
|
|
4669
|
+
},
|
|
4670
|
+
{
|
|
4671
|
+
id: "event-changes",
|
|
4672
|
+
section: "events",
|
|
4673
|
+
title: "When an event is canceled or moves",
|
|
4674
|
+
summary: "Keep it listed and say so \u2014 don't delete it. Two fields each for canceled and moved.",
|
|
4675
|
+
keywords: ["canceled", "cancelled", "postponed", "rescheduled", "moved", "new date"],
|
|
4676
|
+
body: `
|
|
4677
|
+
People who saw the original listing will come back to it. A vanished event tells them nothing; a marked one tells them everything.
|
|
4678
|
+
|
|
4679
|
+
## Canceled
|
|
4680
|
+
|
|
4681
|
+
Tick **Canceled** and, optionally, write a **Cancellation note** ("Postponed \u2014 new date coming soon"). The event stays in the list with a Canceled label and a line through the title; its page says the same. Any RSVP button is hidden.
|
|
4682
|
+
|
|
4683
|
+
## Moved to a new date
|
|
4684
|
+
|
|
4685
|
+
1. Create the event again with the new date \u2014 a new item with its own web address.
|
|
4686
|
+
2. On the **old** event, set **Moved to** to the new event's web address, and **Previous date** if you want the new one to say when it was originally planned.
|
|
4687
|
+
|
|
4688
|
+
The old listing shows "Rescheduled" with a link to the new date. The new one is a normal upcoming event.
|
|
4689
|
+
|
|
4690
|
+
## Why not just edit the date?
|
|
4691
|
+
|
|
4692
|
+
You can, for small corrections. But once people have shared the link or put it in a calendar, a visible "moved" trail avoids no-shows on the wrong day.
|
|
4693
|
+
|
|
4694
|
+
## After it's over
|
|
4695
|
+
|
|
4696
|
+
Nothing to do. Past events move to the **Past** section automatically the day after they end.
|
|
4697
|
+
`
|
|
4698
|
+
},
|
|
4699
|
+
{
|
|
4700
|
+
id: "event-extras",
|
|
4701
|
+
section: "events",
|
|
4702
|
+
title: "Tickets, venue and appearance fields",
|
|
4703
|
+
summary: "Extra groups a site may have switched on: RSVP and capacity, a named venue, host and recording links.",
|
|
4704
|
+
keywords: ["rsvp", "tickets", "capacity", "private event", "sign-ups closed", "venue", "host", "recording", "slides"],
|
|
4705
|
+
body: `
|
|
4706
|
+
Some sites need more than the core fields. {supportName} switches these groups on per site; if you don't see them, your site doesn't use them.
|
|
4707
|
+
|
|
4708
|
+
## Tickets & RSVP
|
|
4709
|
+
|
|
4710
|
+
- **RSVP or ticket link** \u2014 where people sign up or buy. Shown as a prominent **RSVP** button until the event starts.
|
|
4711
|
+
- **Capacity** \u2014 the number of places, for your reference and for search engines.
|
|
4712
|
+
- **Sign-ups closed** \u2014 keeps the event listed but replaces the RSVP button with "Sign-ups are closed."
|
|
4713
|
+
- **Private event** \u2014 listed with a Private label, no RSVP button, "Invited guests only." Use it for events you want on the calendar but not open to the public.
|
|
4714
|
+
|
|
4715
|
+
## Venue
|
|
4716
|
+
|
|
4717
|
+
- **Venue** \u2014 the named place ("The Grand Hall"). **Where** then holds extra directions or the room.
|
|
4718
|
+
|
|
4719
|
+
## Appearance details
|
|
4720
|
+
|
|
4721
|
+
For talks, podcasts and interviews you take part in rather than host:
|
|
4722
|
+
|
|
4723
|
+
- **Hosted or published by** \u2014 the conference, the podcast, the organization.
|
|
4724
|
+
- **Recording link** \u2014 added afterward; past events then show **Watch or listen**.
|
|
4725
|
+
- **Slides link** \u2014 if you share them.
|
|
4726
|
+
`
|
|
4727
|
+
}
|
|
4728
|
+
];
|
|
4729
|
+
|
|
4730
|
+
// src/studio-ui/help/account.ts
|
|
4731
|
+
var ACCOUNT = [
|
|
4732
|
+
{
|
|
4733
|
+
id: "my-profile",
|
|
4734
|
+
section: "account",
|
|
4735
|
+
title: "My Profile: your name, your photo, your byline",
|
|
4736
|
+
summary: "Full name for the greeting, a separate name for blog posts, and a profile photo.",
|
|
4737
|
+
keywords: ["profile", "name", "photo", "avatar", "byline", "author name", "email"],
|
|
4738
|
+
body: `
|
|
4739
|
+
**Settings** (sidebar, or avatar menu \u2192 **Account**) \u2192 **My Profile**.
|
|
4740
|
+
|
|
4741
|
+
## Full name
|
|
4742
|
+
|
|
4743
|
+
How you're greeted in Studio and how teammates see you in the Team tab.
|
|
4744
|
+
|
|
4745
|
+
## Name on blog posts
|
|
4746
|
+
|
|
4747
|
+
How you're credited as the author of a post \u2014 "Jane Doe" or "The {siteName} team". Leave blank to use your full name. New posts pick this up in their **Author** field.
|
|
4748
|
+
|
|
4749
|
+
## Your photo
|
|
4750
|
+
|
|
4751
|
+
Under **Avatar**, **Choose a picture** (or drop one on it). Square works best; it's shrunk automatically. **Replace** or **Remove** any time. It shows in the top bar and the avatar menu \u2014 never on the public website.
|
|
4752
|
+
|
|
4753
|
+
## Email
|
|
4754
|
+
|
|
4755
|
+
Your email is your login and can't be changed here. Ask {supportName} if it needs to change.
|
|
4756
|
+
|
|
4757
|
+
Press **Save** after editing the name fields; pictures save the moment you choose them.
|
|
4758
|
+
`
|
|
4759
|
+
},
|
|
4760
|
+
{
|
|
4761
|
+
id: "change-password",
|
|
4762
|
+
section: "account",
|
|
4763
|
+
title: "Change Password",
|
|
4764
|
+
summary: "Optional. Email links always work; a password is a second way in.",
|
|
4765
|
+
keywords: ["password", "reset password", "forgot"],
|
|
4766
|
+
body: `
|
|
4767
|
+
**Settings \u2192 Change Password**. Enter a **New password** twice. At least 10 characters \u2014 a short sentence you'll remember is ideal.
|
|
4768
|
+
|
|
4769
|
+
## Using it
|
|
4770
|
+
|
|
4771
|
+
On the sign-in page, click **Have a password? Sign in with it instead** under the email form.
|
|
4772
|
+
|
|
4773
|
+
## Forgot it?
|
|
4774
|
+
|
|
4775
|
+
There's nothing to reset. Sign in with an email link as usual, then set a new password here.
|
|
4776
|
+
|
|
4777
|
+
## Do I need one?
|
|
4778
|
+
|
|
4779
|
+
No. Email links are the default and are safer for most people. A password helps if you sign in many times a day or from a device where checking email is awkward.
|
|
4780
|
+
`
|
|
4781
|
+
},
|
|
4782
|
+
{
|
|
4783
|
+
id: "team",
|
|
4784
|
+
section: "account",
|
|
4785
|
+
title: "Team: who can sign in",
|
|
4786
|
+
summary: "Invite by email, choose owner or editor, resend or revoke, remove. Owners only.",
|
|
4787
|
+
keywords: ["invite", "team", "editor", "owner", "roles", "remove user", "resend invite", "revoke"],
|
|
4788
|
+
body: `
|
|
4789
|
+
**Settings \u2192 Team** \u2014 visible to owners.
|
|
4790
|
+
|
|
4791
|
+
## Inviting someone
|
|
4792
|
+
|
|
4793
|
+
Press **Invite someone**, enter their email, choose a **Role**, send. They get an email with a sign-in link \u2014 no password to set up. Until they click it, they're listed as pending; **Resend** sends the email again, **Revoke** withdraws it.
|
|
4794
|
+
|
|
4795
|
+
## Roles
|
|
4796
|
+
|
|
4797
|
+
- **Editor** \u2014 writes, edits, publishes and removes content. Can't manage the team or the business details.
|
|
4798
|
+
- **Owner** \u2014 everything an editor can do, plus Team and Business. A site always keeps at least one owner.
|
|
4799
|
+
|
|
4800
|
+
{supportName} can reach every site without being listed in its team.
|
|
4801
|
+
|
|
4802
|
+
## Removing someone
|
|
4803
|
+
|
|
4804
|
+
**Remove from this site** next to their name, then **Yes, remove**. They lose access to {siteName} only; anything they published stays. If they also work on another site, their login survives there.
|
|
4805
|
+
|
|
4806
|
+
## If the Team tab says inviting isn't set up
|
|
4807
|
+
|
|
4808
|
+
Invitations need one extra piece of configuration on the server. Ask {supportName} to switch it on; everything else in Studio works without it.
|
|
4809
|
+
`
|
|
4810
|
+
},
|
|
4811
|
+
{
|
|
4812
|
+
id: "business-branding",
|
|
4813
|
+
section: "account",
|
|
4814
|
+
title: "Business: your name and logo on Studio",
|
|
4815
|
+
summary: "Business name, contact details, logo and favicon. Changes Studio's header \u2014 never the public website.",
|
|
4816
|
+
keywords: ["branding", "logo", "favicon", "business name", "white label"],
|
|
4817
|
+
body: `
|
|
4818
|
+
**Settings \u2192 Business** \u2014 visible to owners.
|
|
4819
|
+
|
|
4820
|
+
## What it does
|
|
4821
|
+
|
|
4822
|
+
Puts your own name and logo at the top of Studio for everyone who signs in to {siteName}. It does **not** change the public website; that's designed separately.
|
|
4823
|
+
|
|
4824
|
+
## Fields
|
|
4825
|
+
|
|
4826
|
+
- **Business name** \u2014 appears next to "Studio" at the top of the page.
|
|
4827
|
+
- **Business email / phone** \u2014 optional, for your team's reference.
|
|
4828
|
+
- **Logo** \u2014 shown in the top bar. Square or wide, on a plain background.
|
|
4829
|
+
- **Favicon** \u2014 the small browser-tab icon. Square, simple.
|
|
4830
|
+
|
|
4831
|
+
Pictures save the moment you choose them (**Choose a picture**, **Replace**, **Remove**); press **Save** for the text fields.
|
|
4832
|
+
|
|
4833
|
+
## Where the pictures live
|
|
4834
|
+
|
|
4835
|
+
In Studio's own secure storage \u2014 not in your website's files, so they don't go through Save/Publish.
|
|
4836
|
+
`
|
|
4837
|
+
}
|
|
4838
|
+
];
|
|
4839
|
+
|
|
4840
|
+
// src/studio-ui/help/trouble.ts
|
|
4841
|
+
var TROUBLE = [
|
|
4842
|
+
{
|
|
4843
|
+
id: "dont-see-my-change",
|
|
4844
|
+
section: "trouble",
|
|
4845
|
+
title: "I published, but the website still shows the old version",
|
|
4846
|
+
summary: "The site is still rebuilding, or your browser cached the page. Give it two minutes, then hard-refresh.",
|
|
4847
|
+
keywords: ["not showing", "old version", "cache", "refresh", "still old"],
|
|
4848
|
+
body: `
|
|
4849
|
+
## Most likely
|
|
4850
|
+
|
|
4851
|
+
The **Live** step on the change page hasn't turned green yet. Publishing sends the change; the website then rebuilds itself, which takes about two minutes. Stay on the change page \u2014 it tells you the moment it's live and unlocks **View live**.
|
|
4852
|
+
|
|
4853
|
+
## If Live is green and you still see the old page
|
|
4854
|
+
|
|
4855
|
+
Your browser is showing a saved copy. Hard-refresh: **Cmd+Shift+R** on Mac, **Ctrl+Shift+R** on Windows. On a phone, open the page in a private/incognito tab.
|
|
4856
|
+
|
|
4857
|
+
## If it's still old after that
|
|
4858
|
+
|
|
4859
|
+
Check the item's status in the list. **Live** with no change waiting means it's published. If a change is still listed as waiting, open it and look at the tracker.
|
|
4860
|
+
`
|
|
4861
|
+
},
|
|
4862
|
+
{
|
|
4863
|
+
id: "checks-failed",
|
|
4864
|
+
section: "trouble",
|
|
4865
|
+
title: "Checks failed / Needs fixing",
|
|
4866
|
+
summary: "The test build broke. Read the message, fix that one thing, save again.",
|
|
4867
|
+
keywords: ["failed", "needs fixing", "build error", "red"],
|
|
4868
|
+
body: `
|
|
4869
|
+
## What it means
|
|
4870
|
+
|
|
4871
|
+
The website was rebuilt with your change and something broke. Nothing was published; the live site is unchanged.
|
|
4872
|
+
|
|
4873
|
+
## Common causes and fixes
|
|
4874
|
+
|
|
4875
|
+
- **A required field is empty** \u2014 the form usually catches this, but a field can be emptied by a paste. Fill it in.
|
|
4876
|
+
- **A picture path points nowhere** \u2014 you edited the address inside \`\` by hand. Remove the picture line and insert it again with the toolbar.
|
|
4877
|
+
- **A date or time that doesn't exist** \u2014 2026-02-30, or an end time before the start.
|
|
4878
|
+
- **Tags** \u2014 a post needs exactly three, lowercase with hyphens.
|
|
4879
|
+
|
|
4880
|
+
## Then
|
|
4881
|
+
|
|
4882
|
+
Press **Save**. The same change re-runs its checks. If it fails again with a message you can't act on, copy the whole message and send it to {supportName}.
|
|
4883
|
+
`
|
|
4884
|
+
},
|
|
4885
|
+
{
|
|
4886
|
+
id: "sign-in-link",
|
|
4887
|
+
section: "trouble",
|
|
4888
|
+
title: "The sign-in link didn't work",
|
|
4889
|
+
summary: "Links are single-use and expire. Request a new one and open it on the same device.",
|
|
4890
|
+
keywords: ["link expired", "invalid link", "can't sign in", "no email"],
|
|
4891
|
+
body: `
|
|
4892
|
+
## Expired or already used
|
|
4893
|
+
|
|
4894
|
+
Each link works once and only for a short while. Go back to **/admin**, request a new one, and click the newest email.
|
|
4895
|
+
|
|
4896
|
+
## Open it on the same device
|
|
4897
|
+
|
|
4898
|
+
The link must be opened in the same browser that requested it. Requesting on your laptop and tapping the link on your phone won't sign the laptop in \u2014 request it on the device you want to use.
|
|
4899
|
+
|
|
4900
|
+
## No email arrived
|
|
4901
|
+
|
|
4902
|
+
Check spam and promotions folders. Make sure you typed the address {supportName} invited \u2014 a different address, even your own, isn't allowed in. Still nothing after a few minutes? Ask {supportName} to resend the invitation.
|
|
4903
|
+
|
|
4904
|
+
## "You don't have access"
|
|
4905
|
+
|
|
4906
|
+
Your email is signed in but isn't on this site's team. An owner can add you under **Settings \u2192 Team**.
|
|
4907
|
+
`
|
|
4908
|
+
},
|
|
4909
|
+
{
|
|
4910
|
+
id: "picture-rejected",
|
|
4911
|
+
section: "trouble",
|
|
4912
|
+
title: "My picture was refused",
|
|
4913
|
+
summary: "Too large, the wrong type, or the item's web address wasn't set yet.",
|
|
4914
|
+
keywords: ["image error", "picture too large", "file type", "upload failed"],
|
|
4915
|
+
body: `
|
|
4916
|
+
- **"That picture is too large"** \u2014 the original file is over 10 MB. Export a smaller version (most tools have a "medium" or "web" size) and try again.
|
|
4917
|
+
- **Wrong type** \u2014 only JPG, PNG and WebP. HEIC photos straight from an iPhone need converting: share the photo to yourself as JPG, or change the iPhone's camera format to "Most compatible".
|
|
4918
|
+
- **"That image path isn't allowed"** \u2014 the picture's address was edited by hand to somewhere outside the item's folder. Remove and re-insert it with the toolbar.
|
|
4919
|
+
- **The picture is named oddly** \u2014 pictures are named after the item's **Web address**. Set that first, then add pictures.
|
|
4920
|
+
- **"That picture couldn't be prepared"** \u2014 the file is damaged or not really an image. Open it on your computer to check, or export it again.
|
|
4921
|
+
`
|
|
4922
|
+
},
|
|
4923
|
+
{
|
|
4924
|
+
id: "two-changes",
|
|
4925
|
+
section: "trouble",
|
|
4926
|
+
title: "I can't start a change \u2014 one is already waiting",
|
|
4927
|
+
summary: "One change per item. Open the waiting one and finish it, or discard it.",
|
|
4928
|
+
keywords: ["already a change", "locked", "in progress", "can't edit"],
|
|
4929
|
+
body: `
|
|
4930
|
+
Each item can have one change in progress. When you open an item that has one, a notice at the top says so and links to it.
|
|
4931
|
+
|
|
4932
|
+
- To keep going with it: edit and **Save** \u2014 your edits go onto the same change.
|
|
4933
|
+
- To publish it: open the change and press **Publish**.
|
|
4934
|
+
- To throw it away: open the change and press **Discard this change**.
|
|
4935
|
+
|
|
4936
|
+
If a change was started by a teammate, the notice names them. Talk before discarding.
|
|
4937
|
+
`
|
|
4938
|
+
},
|
|
4939
|
+
{
|
|
4940
|
+
id: "something-else",
|
|
4941
|
+
section: "trouble",
|
|
4942
|
+
title: "Something else is wrong",
|
|
4943
|
+
summary: "Where to look, what to send, and how the Ask bubble can help.",
|
|
4944
|
+
keywords: ["support", "contact", "help", "bug"],
|
|
4945
|
+
body: `
|
|
4946
|
+
1. **Ask the bubble** (bottom right, if your site has one). It knows everything in this Help center and can answer specific questions.
|
|
4947
|
+
2. **Search this Help center** with the box at the top \u2014 try the exact words from the message you saw.
|
|
4948
|
+
3. **Write to {supportName}** with: the page you were on, what you pressed, the exact message (copy it, don't retype), and the time it happened. A screenshot helps.
|
|
4949
|
+
|
|
4950
|
+
Nothing you do in Studio can damage the live website \u2014 every edit is checked before it goes live, and every version is kept. Worst case, {supportName} rolls back.
|
|
4951
|
+
`
|
|
4952
|
+
}
|
|
4953
|
+
];
|
|
4954
|
+
|
|
4955
|
+
// src/studio-ui/help/index.ts
|
|
4956
|
+
var HELP_ARTICLES = [...QUICK_START, ...PUBLISHING, ...BLOG, ...PICTURES, ...MARKDOWN, ...EVENTS, ...ACCOUNT, ...TROUBLE];
|
|
4957
|
+
var GENERIC_VARS = { siteName: "your website", supportName: "your web team", types: "blog posts and events" };
|
|
4958
|
+
function joinTypes(labels) {
|
|
4959
|
+
if (labels.length === 0) return GENERIC_VARS.types;
|
|
4960
|
+
if (labels.length === 1) return labels[0];
|
|
4961
|
+
return `${labels.slice(0, -1).join(", ")} and ${labels[labels.length - 1]}`;
|
|
4962
|
+
}
|
|
4963
|
+
function fillVars(text, vars) {
|
|
4964
|
+
return text.replace(/\{(siteName|supportName|types)\}/g, (_, k) => vars[k]);
|
|
4965
|
+
}
|
|
4966
|
+
function articlesIn(section) {
|
|
4967
|
+
return HELP_ARTICLES.filter((a) => a.section === section);
|
|
4968
|
+
}
|
|
4969
|
+
function sectionOf(article) {
|
|
4970
|
+
return HELP_SECTIONS.find((s) => s.id === article.section) ?? HELP_SECTIONS[0];
|
|
4971
|
+
}
|
|
4972
|
+
var strip = (md) => md.replace(/[#*_`>|\\-]+/g, " ").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/\s+/g, " ").trim();
|
|
4973
|
+
function searchHelp(query, articles = HELP_ARTICLES) {
|
|
4974
|
+
const words = query.toLowerCase().split(/\s+/).filter((w) => w.length > 1);
|
|
4975
|
+
if (words.length === 0) return [];
|
|
4976
|
+
const hits = [];
|
|
4977
|
+
for (const a of articles) {
|
|
4978
|
+
const title = a.title.toLowerCase();
|
|
4979
|
+
const summary = a.summary.toLowerCase();
|
|
4980
|
+
const keys = (a.keywords ?? []).join(" ").toLowerCase();
|
|
4981
|
+
const body = strip(a.body).toLowerCase();
|
|
4982
|
+
let score = 0;
|
|
4983
|
+
let ok = true;
|
|
4984
|
+
for (const w of words) {
|
|
4985
|
+
const inTitle = title.includes(w), inSum = summary.includes(w), inKey = keys.includes(w), inBody = body.includes(w);
|
|
4986
|
+
if (!inTitle && !inSum && !inKey && !inBody) {
|
|
4987
|
+
ok = false;
|
|
4988
|
+
break;
|
|
4989
|
+
}
|
|
4990
|
+
score += (inTitle ? 10 : 0) + (inSum ? 5 : 0) + (inKey ? 5 : 0) + (inBody ? 1 : 0);
|
|
4991
|
+
}
|
|
4992
|
+
if (!ok) continue;
|
|
4993
|
+
const plain = strip(a.body);
|
|
4994
|
+
const at = plain.toLowerCase().indexOf(words[0]);
|
|
4995
|
+
const snippet = at < 0 ? a.summary : `${at > 40 ? "\u2026" : ""}${plain.slice(Math.max(0, at - 40), at + 120).trim()}\u2026`;
|
|
4996
|
+
hits.push({ article: a, score, snippet });
|
|
4997
|
+
}
|
|
4998
|
+
return hits.sort((x, y) => y.score - x.score || x.article.title.localeCompare(y.article.title));
|
|
4999
|
+
}
|
|
5000
|
+
function helpAsText(vars = GENERIC_VARS) {
|
|
5001
|
+
const parts = [];
|
|
5002
|
+
for (const s of HELP_SECTIONS) {
|
|
5003
|
+
parts.push(`
|
|
5004
|
+
=== ${s.title.toUpperCase()} \u2014 ${s.blurb}`);
|
|
5005
|
+
for (const a of articlesIn(s.id)) {
|
|
5006
|
+
parts.push(`
|
|
5007
|
+
## ${a.title}
|
|
5008
|
+
${a.summary}
|
|
5009
|
+
${fillVars(a.body.trim(), vars)}`);
|
|
5010
|
+
}
|
|
5011
|
+
}
|
|
5012
|
+
return parts.join("\n");
|
|
5013
|
+
}
|
|
5014
|
+
|
|
3837
5015
|
// src/studio-ui/facts.ts
|
|
3838
|
-
var
|
|
3839
|
-
HOW
|
|
5016
|
+
var OVERVIEW = `
|
|
5017
|
+
HOW STUDIO WORKS (the publishing dashboard at /admin)
|
|
3840
5018
|
|
|
3841
5019
|
The big picture: your website is stored as files in a private "repository" on GitHub (think: a version-controlled folder in the cloud that keeps every change ever made). Studio is a friendly front end over that folder. You never need to open GitHub \u2014 every action you take in Studio is one of these steps:
|
|
3842
5020
|
|
|
@@ -3844,47 +5022,254 @@ The big picture: your website is stored as files in a private "repository" on Gi
|
|
|
3844
5022
|
2. CHECKS. The moment a change exists, the site builds a private test copy with your change in it, to make sure nothing breaks (a broken link tag, a missing required field, an image path that doesn't exist). This usually takes a minute or two. The change page shows a tracker: Saved \u2192 Checks \u2192 Published \u2192 Live, and refreshes itself.
|
|
3845
5023
|
3. PUBLISH. When the checks pass the status becomes "Ready to publish" and the Publish button unlocks. A "Preview the change" button also appears: it opens a private copy of the website with your edit in it, so you can see exactly how the post will look before anyone else does. Clicking Publish sends your change to the live site.
|
|
3846
5024
|
4. LIVE. The website then rebuilds itself with your change \u2014 usually about two minutes. The Live step keeps spinning until the rebuild is really done, and "View the post" unlocks at that moment. If you look at the website before the Live step turns green you will still see the old version; that's normal, not a failure.
|
|
5025
|
+
`;
|
|
5026
|
+
var STUDIO_FACTS = `${OVERVIEW}
|
|
3847
5027
|
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
STATUSES you'll see in each list and on each item:
|
|
3851
|
-
- Live: what's on the website right now, no change waiting.
|
|
3852
|
-
- Checking\u2026: you saved; the test build is running. Wait a minute or two.
|
|
3853
|
-
- Ready to publish: checks passed; open the change and click Publish.
|
|
3854
|
-
- Needs fixing: the test build failed. Nothing was published. Open the post, fix the problem the message names, save again \u2014 the same change re-runs its checks.
|
|
3855
|
-
- New \xB7 \u2026: a brand-new post that exists only in a change; it isn't on the live site until you publish.
|
|
3856
|
-
|
|
3857
|
-
RULES OF THUMB:
|
|
3858
|
-
- One open change per post. Saving again while a change is waiting updates that same change (it doesn't create a second one). The sticky bar at the bottom of the editor tells you which change you're updating.
|
|
3859
|
-
- You can leave and come back: the posts table's Status column links to the change, and the post page shows a notice with a Publish button when a change is waiting.
|
|
3860
|
-
- Changed your mind? On the change page, "Discard this change" (with a confirm step) throws the unpublished edits away. The live site is untouched and the post goes back to Live.
|
|
3861
|
-
- "View on GitHub" opens the raw change for the curious; you never need it.
|
|
3862
|
-
- Only signed-in editors and owners can save or publish. Sign-in is by email link (no password).
|
|
3863
|
-
- If two people edit the same post, the second to publish is told the file changed first and asked to redo their edit on the latest version \u2014 nothing is silently overwritten.
|
|
3864
|
-
|
|
3865
|
-
FIELDS on a blog post:
|
|
3866
|
-
- Title (H1): the headline at the top of the post. Can be long.
|
|
3867
|
-
- Web address (slug): the last part of the post's URL, e.g. my-first-post \u2192 realiiz.com/blog/my-first-post. Lowercase words joined by hyphens.
|
|
3868
|
-
- Excerpt: the short teaser shown on the blog listing page.
|
|
3869
|
-
- Publish date / Last updated: shown on the post and used for ordering; Last updated is optional, for revisions.
|
|
3870
|
-
- Author, Tags (exactly 3, lowercase-hyphenated), Header image (choose or drop a picture \u2014 see PICTURES below), Header image description (alt text for accessibility), Featured (pins the post).
|
|
3871
|
-
- Search & social group: Search title (what Google and the browser tab show \u2014 aim for 50\u201360 characters including the site name; blank means same as Title), Meta description (the snippet under the title in Google \u2014 aim for 120\u2013155 characters), Focus keyword (the phrase the post targets; used for SEO reporting, never shown), Social share image (the picture for social cards; blank reuses the hero image), Canonical (advanced; leave blank unless the article was published elsewhere first).
|
|
3872
|
-
- Post content: Markdown. The toolbar buttons toggle formatting on and off; Preview shows exactly how the blog will render it; the tooltip button wraps a selected term in a definition that appears on hover; Expand fills the window (Esc to come back).
|
|
3873
|
-
- Length gauges under the search fields: the green band is the target range; the fill turns green when you're inside it and red when you're over.
|
|
3874
|
-
|
|
3875
|
-
PICTURES:
|
|
3876
|
-
- Choose a picture (or drag one in, or paste) for the header image, the social share image, or anywhere in the post content via the toolbar's picture button. JPG, PNG or WebP up to 10 MB; Studio shrinks it in your browser to a web-friendly WebP (about 1600 pixels wide, usually 100\u2013400 KB) before sending.
|
|
3877
|
-
- Nothing is uploaded when you pick \u2014 the pictures are saved together with the post when you press Save, into the same change, and go live with it when you Publish. Discarding a change discards its pictures too.
|
|
3878
|
-
- Pictures are named after the post's web address (slug), so fill that in first: the header image becomes /blog/<slug>.webp and pictures in the text go under /blog/<slug>/.
|
|
3879
|
-
- Preview shows new pictures before they're saved. The "Preview the change" link after the checks shows them on the real site design.
|
|
3880
|
-
|
|
3881
|
-
ACCOUNT (avatar menu \u2192 Account, or Settings in the sidebar):
|
|
3882
|
-
- My Profile: your photo, your name (shown in the greeting and to teammates) and \u201CName on blog posts\u201D \u2014 how you're credited as the author; blank means your full name. Your email is your login and can't be changed here \u2014 ask your web team.
|
|
3883
|
-
- Change Password: optional. Email links always work; a password is a second way in. At least 10 characters. On the sign-in page, "Have a password? Sign in with it instead" reveals the password form. Forgot your password? There's nothing to reset \u2014 sign in with an email link and set a new one under Settings \u2192 Change Password.
|
|
3884
|
-
- Team (owners only): who can sign in to this site. "+ Invite someone" sends an email link \u2014 no password needed. Editors write and publish; owners also manage the team and the business details. Pending invites can be resent or revoked; active people can be removed from this site (their login survives for any other site). A site always keeps at least one owner. Support staff aren't listed in a site's team \u2014 they can reach every site without being a member.
|
|
3885
|
-
- Business (owners only): your business name, contact email/phone, logo and favicon. Saving puts your name and logo at the top of Studio for everyone who signs in to your site \u2014 it never changes the public website.
|
|
3886
|
-
- Help (avatar menu): the help centre. For now it points here, to the Ask bubble; a full browsable guide is coming.
|
|
5028
|
+
THE HELP CENTER (every article, as shown at /admin/help):
|
|
5029
|
+
${helpAsText()}
|
|
3887
5030
|
`;
|
|
5031
|
+
var cells = (line) => line.trim().replace(/^\||\|$/g, "").split("|").map((c) => c.trim());
|
|
5032
|
+
function parseBlocks(md) {
|
|
5033
|
+
const lines = md.replace(/\r/g, "").split("\n");
|
|
5034
|
+
const out = [];
|
|
5035
|
+
let i = 0;
|
|
5036
|
+
while (i < lines.length) {
|
|
5037
|
+
const line = lines[i];
|
|
5038
|
+
if (!line.trim()) {
|
|
5039
|
+
i++;
|
|
5040
|
+
continue;
|
|
5041
|
+
}
|
|
5042
|
+
if (line.startsWith("```")) {
|
|
5043
|
+
const buf2 = [];
|
|
5044
|
+
i++;
|
|
5045
|
+
while (i < lines.length && !lines[i].startsWith("```")) buf2.push(lines[i++]);
|
|
5046
|
+
i++;
|
|
5047
|
+
out.push({ t: "code", text: buf2.join("\n") });
|
|
5048
|
+
continue;
|
|
5049
|
+
}
|
|
5050
|
+
const h = /^(##|###)\s+(.*)$/.exec(line);
|
|
5051
|
+
if (h) {
|
|
5052
|
+
out.push({ t: h[1] === "##" ? "h2" : "h3", text: h[2] });
|
|
5053
|
+
i++;
|
|
5054
|
+
continue;
|
|
5055
|
+
}
|
|
5056
|
+
if (line.trim().startsWith("|") && i + 1 < lines.length && /^\s*\|?\s*:?-{3,}/.test(lines[i + 1])) {
|
|
5057
|
+
const head = cells(line);
|
|
5058
|
+
i += 2;
|
|
5059
|
+
const rows = [];
|
|
5060
|
+
while (i < lines.length && lines[i].trim().startsWith("|")) rows.push(cells(lines[i++]));
|
|
5061
|
+
out.push({ t: "table", head, rows });
|
|
5062
|
+
continue;
|
|
5063
|
+
}
|
|
5064
|
+
if (/^\s*[-*]\s+/.test(line)) {
|
|
5065
|
+
const items = [];
|
|
5066
|
+
while (i < lines.length && /^\s*[-*]\s+/.test(lines[i])) items.push(lines[i++].replace(/^\s*[-*]\s+/, ""));
|
|
5067
|
+
out.push({ t: "ul", items });
|
|
5068
|
+
continue;
|
|
5069
|
+
}
|
|
5070
|
+
if (/^\s*\d+[.)]\s+/.test(line)) {
|
|
5071
|
+
const items = [];
|
|
5072
|
+
while (i < lines.length && /^\s*\d+[.)]\s+/.test(lines[i])) items.push(lines[i++].replace(/^\s*\d+[.)]\s+/, ""));
|
|
5073
|
+
out.push({ t: "ol", items });
|
|
5074
|
+
continue;
|
|
5075
|
+
}
|
|
5076
|
+
if (line.startsWith(">")) {
|
|
5077
|
+
const buf2 = [];
|
|
5078
|
+
while (i < lines.length && lines[i].startsWith(">")) buf2.push(lines[i++].replace(/^>\s?/, ""));
|
|
5079
|
+
out.push({ t: "quote", text: buf2.join(" ") });
|
|
5080
|
+
continue;
|
|
5081
|
+
}
|
|
5082
|
+
const buf = [];
|
|
5083
|
+
while (i < lines.length && lines[i].trim() && !/^(##|###)\s|^```|^\s*[-*]\s|^\s*\d+[.)]\s|^>|^\s*\|/.test(lines[i])) buf.push(lines[i++].trim());
|
|
5084
|
+
out.push({ t: "p", text: buf.join(" ") });
|
|
5085
|
+
}
|
|
5086
|
+
return out;
|
|
5087
|
+
}
|
|
5088
|
+
var INLINE2 = /(\*\*[^*]+\*\*|_[^_]+_|`[^`]+`|\[[^\]]+\]\([^)]+\))/g;
|
|
5089
|
+
function inline(text) {
|
|
5090
|
+
const parts = text.split(INLINE2);
|
|
5091
|
+
return parts.map((p, i) => {
|
|
5092
|
+
if (!p) return null;
|
|
5093
|
+
if (p.startsWith("**") && p.endsWith("**")) return /* @__PURE__ */ jsxRuntime.jsx("strong", { children: inline(p.slice(2, -2)) }, i);
|
|
5094
|
+
if (p.startsWith("_") && p.endsWith("_") && p.length > 2) return /* @__PURE__ */ jsxRuntime.jsx("em", { children: inline(p.slice(1, -1)) }, i);
|
|
5095
|
+
if (p.startsWith("`") && p.endsWith("`")) return /* @__PURE__ */ jsxRuntime.jsx("code", { children: p.slice(1, -1) }, i);
|
|
5096
|
+
const link = /^\[([^\]]+)\]\(([^)]+)\)$/.exec(p);
|
|
5097
|
+
if (link) {
|
|
5098
|
+
const ext = /^https?:\/\//.test(link[2]);
|
|
5099
|
+
return /* @__PURE__ */ jsxRuntime.jsx("a", { href: link[2], target: ext ? "_blank" : void 0, rel: ext ? "noreferrer" : void 0, children: inline(link[1]) }, i);
|
|
5100
|
+
}
|
|
5101
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: p }, i);
|
|
5102
|
+
});
|
|
5103
|
+
}
|
|
5104
|
+
function renderHelpMarkdown(md) {
|
|
5105
|
+
return parseBlocks(md).map((b, i) => {
|
|
5106
|
+
switch (b.t) {
|
|
5107
|
+
case "h2":
|
|
5108
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h2", { children: inline(b.text) }, i);
|
|
5109
|
+
case "h3":
|
|
5110
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h3", { children: inline(b.text) }, i);
|
|
5111
|
+
case "p":
|
|
5112
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { children: inline(b.text) }, i);
|
|
5113
|
+
case "quote":
|
|
5114
|
+
return /* @__PURE__ */ jsxRuntime.jsx("blockquote", { children: inline(b.text) }, i);
|
|
5115
|
+
case "code":
|
|
5116
|
+
return /* @__PURE__ */ jsxRuntime.jsx("pre", { children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: b.text }) }, i);
|
|
5117
|
+
case "ul":
|
|
5118
|
+
return /* @__PURE__ */ jsxRuntime.jsx("ul", { children: b.items.map((it, j) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: inline(it) }, j)) }, i);
|
|
5119
|
+
case "ol":
|
|
5120
|
+
return /* @__PURE__ */ jsxRuntime.jsx("ol", { children: b.items.map((it, j) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: inline(it) }, j)) }, i);
|
|
5121
|
+
case "table":
|
|
5122
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-table-wrap", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "rz-table", children: [
|
|
5123
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: b.head.map((c, j) => /* @__PURE__ */ jsxRuntime.jsx("th", { children: inline(c) }, j)) }) }),
|
|
5124
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: b.rows.map((r, j) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: r.map((c, k) => /* @__PURE__ */ jsxRuntime.jsx("td", { children: inline(c) }, k)) }, j)) })
|
|
5125
|
+
] }) }, i);
|
|
5126
|
+
}
|
|
5127
|
+
});
|
|
5128
|
+
}
|
|
5129
|
+
function viewFromHash(hash) {
|
|
5130
|
+
const h = hash.replace(/^#/, "");
|
|
5131
|
+
if (h.startsWith("s-")) {
|
|
5132
|
+
const id = h.slice(2);
|
|
5133
|
+
if (HELP_SECTIONS.some((s) => s.id === id)) return { kind: "section", id };
|
|
5134
|
+
}
|
|
5135
|
+
const a = HELP_ARTICLES.find((x) => x.id === h);
|
|
5136
|
+
return a ? { kind: "article", article: a } : { kind: "section", id: "start" };
|
|
5137
|
+
}
|
|
5138
|
+
function StudioHelp() {
|
|
5139
|
+
const { contentTypes, siteName, supportName } = useStudio();
|
|
5140
|
+
const vars = react.useMemo(() => ({ siteName, supportName, types: joinTypes(Object.values(contentTypes).map((e) => e.label)) }), [contentTypes, siteName, supportName]);
|
|
5141
|
+
const [view, setView] = react.useState({ kind: "section", id: "start" });
|
|
5142
|
+
const [q, setQ] = react.useState("");
|
|
5143
|
+
const top = react.useRef(null);
|
|
5144
|
+
react.useEffect(() => {
|
|
5145
|
+
const sync = () => setView(viewFromHash(window.location.hash));
|
|
5146
|
+
sync();
|
|
5147
|
+
window.addEventListener("hashchange", sync);
|
|
5148
|
+
return () => window.removeEventListener("hashchange", sync);
|
|
5149
|
+
}, []);
|
|
5150
|
+
const go = (hash) => {
|
|
5151
|
+
setQ("");
|
|
5152
|
+
setView(viewFromHash(hash));
|
|
5153
|
+
window.location.hash = hash;
|
|
5154
|
+
top.current?.scrollIntoView?.({ block: "start" });
|
|
5155
|
+
};
|
|
5156
|
+
const hits = q.trim().length > 1 ? searchHelp(q) : null;
|
|
5157
|
+
const activeSection = view.kind === "section" ? view.id : view.article.section;
|
|
5158
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: top, className: "rz-help", children: [
|
|
5159
|
+
/* @__PURE__ */ jsxRuntime.jsx(PageHeader, { eyebrow: "Help", title: "Help center", subtitle: "Everything about running your website from Studio, in plain English. Search, or browse by section." }),
|
|
5160
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-help__search", children: [
|
|
5161
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", "aria-hidden": "true", children: [
|
|
5162
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "7" }),
|
|
5163
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 20l-3.5-3.5" })
|
|
5164
|
+
] }),
|
|
5165
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5166
|
+
"input",
|
|
5167
|
+
{
|
|
5168
|
+
type: "search",
|
|
5169
|
+
className: "realiiz-form__control",
|
|
5170
|
+
placeholder: "Search the help center \u2014 try \u201Cneeds fixing\u201D or \u201Cadd a picture\u201D",
|
|
5171
|
+
"aria-label": "Search the help center",
|
|
5172
|
+
value: q,
|
|
5173
|
+
onChange: (e) => setQ(e.target.value),
|
|
5174
|
+
onKeyDown: (e) => {
|
|
5175
|
+
if (e.key === "Escape") setQ("");
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5178
|
+
)
|
|
5179
|
+
] }),
|
|
5180
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-help__body", children: [
|
|
5181
|
+
/* @__PURE__ */ jsxRuntime.jsx("nav", { className: "rz-help__nav", "aria-label": "Help sections", children: HELP_SECTIONS.map((s) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: `#s-${s.id}`, className: `rz-help__navitem${!hits && activeSection === s.id ? " rz-help__navitem--active" : ""}`, onClick: (e) => {
|
|
5182
|
+
e.preventDefault();
|
|
5183
|
+
go(`s-${s.id}`);
|
|
5184
|
+
}, children: s.title }, s.id)) }),
|
|
5185
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-help__main", children: hits ? /* @__PURE__ */ jsxRuntime.jsx(Results, { hits, q, onOpen: (a) => go(a.id) }) : view.kind === "section" ? /* @__PURE__ */ jsxRuntime.jsx(SectionView, { id: view.id, onOpen: (a) => go(a.id) }) : /* @__PURE__ */ jsxRuntime.jsx(ArticleView, { article: view.article, vars, onOpen: (a) => go(a.id), onSection: (id) => go(`s-${id}`) }) })
|
|
5186
|
+
] })
|
|
5187
|
+
] });
|
|
5188
|
+
}
|
|
5189
|
+
function Results({ hits, q, onOpen }) {
|
|
5190
|
+
if (hits.length === 0) {
|
|
5191
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-empty", children: [
|
|
5192
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h3", { children: [
|
|
5193
|
+
"No articles match \u201C",
|
|
5194
|
+
q,
|
|
5195
|
+
"\u201D"
|
|
5196
|
+
] }),
|
|
5197
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0 }, children: "Try fewer words, or the exact words from the message you saw. The Ask bubble can also answer specific questions." })
|
|
5198
|
+
] });
|
|
5199
|
+
}
|
|
5200
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5201
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "rz-help__count", children: [
|
|
5202
|
+
hits.length,
|
|
5203
|
+
" ",
|
|
5204
|
+
hits.length === 1 ? "article" : "articles",
|
|
5205
|
+
" for \u201C",
|
|
5206
|
+
q,
|
|
5207
|
+
"\u201D"
|
|
5208
|
+
] }),
|
|
5209
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "rz-help__list", children: hits.map(({ article, snippet }) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(ArticleLink, { article, onOpen, meta: sectionOf(article).title, children: snippet }) }, article.id)) })
|
|
5210
|
+
] });
|
|
5211
|
+
}
|
|
5212
|
+
function SectionView({ id, onOpen }) {
|
|
5213
|
+
const s = HELP_SECTIONS.find((x) => x.id === id) ?? HELP_SECTIONS[0];
|
|
5214
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5215
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "rz-help__h", children: s.title }),
|
|
5216
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "rz-help__blurb", children: s.blurb }),
|
|
5217
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "rz-help__list", children: articlesIn(s.id).map((a) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(ArticleLink, { article: a, onOpen, children: a.summary }) }, a.id)) })
|
|
5218
|
+
] });
|
|
5219
|
+
}
|
|
5220
|
+
function ArticleLink({ article, onOpen, meta, children }) {
|
|
5221
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("a", { href: `#${article.id}`, className: "rz-help__card", onClick: (e) => {
|
|
5222
|
+
e.preventDefault();
|
|
5223
|
+
onOpen(article);
|
|
5224
|
+
}, children: [
|
|
5225
|
+
meta && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-help__meta", children: meta }),
|
|
5226
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-help__title", children: article.title }),
|
|
5227
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rz-help__summary", children })
|
|
5228
|
+
] });
|
|
5229
|
+
}
|
|
5230
|
+
function ArticleView({ article, vars, onOpen, onSection }) {
|
|
5231
|
+
const section = sectionOf(article);
|
|
5232
|
+
const siblings = articlesIn(article.section);
|
|
5233
|
+
const i = siblings.findIndex((a) => a.id === article.id);
|
|
5234
|
+
const prev = siblings[i - 1], next = siblings[i + 1];
|
|
5235
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "rz-help__article", children: [
|
|
5236
|
+
/* @__PURE__ */ jsxRuntime.jsxs("a", { href: `#s-${section.id}`, className: "rz-help__crumb", onClick: (e) => {
|
|
5237
|
+
e.preventDefault();
|
|
5238
|
+
onSection(section.id);
|
|
5239
|
+
}, children: [
|
|
5240
|
+
/* @__PURE__ */ jsxRuntime.jsx(BackArrow, {}),
|
|
5241
|
+
" ",
|
|
5242
|
+
section.title
|
|
5243
|
+
] }),
|
|
5244
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "rz-help__h", children: article.title }),
|
|
5245
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "rz-help__blurb", children: fillVars(article.summary, vars) }),
|
|
5246
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rz-help__prose", children: renderHelpMarkdown(fillVars(article.body, vars)) }),
|
|
5247
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rz-help__pager", children: [
|
|
5248
|
+
prev ? /* @__PURE__ */ jsxRuntime.jsxs("a", { href: `#${prev.id}`, onClick: (e) => {
|
|
5249
|
+
e.preventDefault();
|
|
5250
|
+
onOpen(prev);
|
|
5251
|
+
}, children: [
|
|
5252
|
+
/* @__PURE__ */ jsxRuntime.jsx(BackArrow, {}),
|
|
5253
|
+
" ",
|
|
5254
|
+
prev.title
|
|
5255
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
5256
|
+
next ? /* @__PURE__ */ jsxRuntime.jsxs("a", { href: `#${next.id}`, onClick: (e) => {
|
|
5257
|
+
e.preventDefault();
|
|
5258
|
+
onOpen(next);
|
|
5259
|
+
}, children: [
|
|
5260
|
+
next.title,
|
|
5261
|
+
" ",
|
|
5262
|
+
/* @__PURE__ */ jsxRuntime.jsx(ForwardArrow, {})
|
|
5263
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("span", {})
|
|
5264
|
+
] })
|
|
5265
|
+
] });
|
|
5266
|
+
}
|
|
5267
|
+
function BackArrow() {
|
|
5268
|
+
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--back", children: [
|
|
5269
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 12H5" }),
|
|
5270
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m11 18-6-6 6-6" })
|
|
5271
|
+
] });
|
|
5272
|
+
}
|
|
3888
5273
|
|
|
3889
5274
|
// src/studio-ui/types.ts
|
|
3890
5275
|
var ACTION_NAMES = [
|
|
@@ -3910,6 +5295,8 @@ var ACTION_NAMES = [
|
|
|
3910
5295
|
exports.ACTION_NAMES = ACTION_NAMES;
|
|
3911
5296
|
exports.ContentEditor = ContentEditor;
|
|
3912
5297
|
exports.GLOSSARY = GLOSSARY;
|
|
5298
|
+
exports.HELP_ARTICLES = HELP_ARTICLES;
|
|
5299
|
+
exports.HELP_SECTIONS = HELP_SECTIONS;
|
|
3913
5300
|
exports.Jargon = Jargon;
|
|
3914
5301
|
exports.PublishPanel = PublishPanel;
|
|
3915
5302
|
exports.RemoveItem = RemoveItem;
|
|
@@ -3930,7 +5317,11 @@ exports.StudioShellFrame = StudioShellFrame;
|
|
|
3930
5317
|
exports.StudioSignIn = StudioSignIn;
|
|
3931
5318
|
exports.StudioTerm = StudioTerm;
|
|
3932
5319
|
exports.copyFor = copyFor;
|
|
5320
|
+
exports.fillVars = fillVars;
|
|
5321
|
+
exports.helpAsText = helpAsText;
|
|
5322
|
+
exports.joinTypes = joinTypes;
|
|
3933
5323
|
exports.publicDirOf = publicDirOf;
|
|
5324
|
+
exports.searchHelp = searchHelp;
|
|
3934
5325
|
exports.useEntry = useEntry;
|
|
3935
5326
|
exports.useStudio = useStudio;
|
|
3936
5327
|
//# sourceMappingURL=index.cjs.map
|