@sevenfold/setto-client 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21481,28 +21481,28 @@ function parseManifest(input) {
21481
21481
  const rawPages = Array.isArray(input.pages) ? input.pages : null;
21482
21482
  if (!rawPages) issues.push({ at: "pages", message: "must be a list" });
21483
21483
  (rawPages ?? []).forEach((raw, i) => {
21484
- const at = `pages[${i}]`;
21484
+ const at2 = `pages[${i}]`;
21485
21485
  if (!isRecord$1(raw)) {
21486
- issues.push({ at, message: "not an object" });
21486
+ issues.push({ at: at2, message: "not an object" });
21487
21487
  return;
21488
21488
  }
21489
21489
  const id = String(raw.id ?? "");
21490
21490
  const path2 = String(raw.path ?? "");
21491
21491
  const titleKey = String(raw.titleKey ?? "");
21492
- if (!ID.test(id)) issues.push({ at: `${at}.id`, message: `invalid id "${id}"` });
21493
- else if (pageIds.has(id)) issues.push({ at: `${at}.id`, message: `duplicate page "${id}"` });
21492
+ if (!ID.test(id)) issues.push({ at: `${at2}.id`, message: `invalid id "${id}"` });
21493
+ else if (pageIds.has(id)) issues.push({ at: `${at2}.id`, message: `duplicate page "${id}"` });
21494
21494
  pageIds.add(id);
21495
- if (!PATH.test(path2)) issues.push({ at: `${at}.path`, message: `invalid path "${path2}"` });
21495
+ if (!PATH.test(path2)) issues.push({ at: `${at2}.path`, message: `invalid path "${path2}"` });
21496
21496
  else if (path2 === "/setto" || path2.startsWith("/setto/")) {
21497
- issues.push({ at: `${at}.path`, message: "/setto belongs to the editor" });
21498
- } else if (paths.has(path2)) issues.push({ at: `${at}.path`, message: `duplicate path "${path2}"` });
21497
+ issues.push({ at: `${at2}.path`, message: "/setto belongs to the editor" });
21498
+ } else if (paths.has(path2)) issues.push({ at: `${at2}.path`, message: `duplicate path "${path2}"` });
21499
21499
  paths.add(path2);
21500
- if (!titleKey) issues.push({ at: `${at}.titleKey`, message: "required" });
21500
+ if (!titleKey) issues.push({ at: `${at2}.titleKey`, message: "required" });
21501
21501
  const sections = [];
21502
21502
  const rawSections = Array.isArray(raw.sections) ? raw.sections : null;
21503
- if (!rawSections) issues.push({ at: `${at}.sections`, message: "must be a list" });
21503
+ if (!rawSections) issues.push({ at: `${at2}.sections`, message: "must be a list" });
21504
21504
  (rawSections ?? []).forEach((s, j) => {
21505
- const sat = `${at}.sections[${j}]`;
21505
+ const sat = `${at2}.sections[${j}]`;
21506
21506
  if (!isRecord$1(s)) {
21507
21507
  issues.push({ at: sat, message: "not an object" });
21508
21508
  return;
@@ -21544,16 +21544,16 @@ function parseManifest(input) {
21544
21544
  if (issues.length > 0) return { ok: false, issues };
21545
21545
  return { ok: true, manifest: { version: PAGES_MANIFEST_VERSION, pages, menu, footer: footer2 } };
21546
21546
  }
21547
- function parseMenu(raw, at, issues) {
21547
+ function parseMenu(raw, at2, issues) {
21548
21548
  if (raw === void 0) return [];
21549
21549
  if (!Array.isArray(raw)) {
21550
- issues.push({ at, message: "must be a list" });
21550
+ issues.push({ at: at2, message: "must be a list" });
21551
21551
  return [];
21552
21552
  }
21553
21553
  const ids = /* @__PURE__ */ new Set();
21554
21554
  const items = [];
21555
21555
  raw.forEach((item, i) => {
21556
- const iat = `${at}[${i}]`;
21556
+ const iat = `${at2}[${i}]`;
21557
21557
  if (!isRecord$1(item)) {
21558
21558
  issues.push({ at: iat, message: "not an object" });
21559
21559
  return;
@@ -21613,6 +21613,15 @@ function findSection(manifest, sectionId) {
21613
21613
  function visibleMenu(manifest, list2) {
21614
21614
  return manifest[list2].filter((item) => !item.hidden && !leadsNowhere(manifest, item.href));
21615
21615
  }
21616
+ function ownerMenu(manifest, list2, blogEmpty) {
21617
+ const blog = blogPage(manifest);
21618
+ const waiting = blogEmpty && blog && !blog.page.hidden && !blog.section.hidden ? normalisePath(blog.page.path) : null;
21619
+ return manifest[list2].flatMap((item) => {
21620
+ if (!item.hidden && !leadsNowhere(manifest, item.href)) return [item];
21621
+ if (waiting && item.hidden && normalisePath(item.href) === waiting) return [{ ...item, dimmed: true }];
21622
+ return [];
21623
+ });
21624
+ }
21616
21625
  function leadsNowhere(manifest, href) {
21617
21626
  if (!href.startsWith("/") && !href.startsWith("#")) return false;
21618
21627
  const [pathPart, fragment] = href.split("#");
@@ -21656,8 +21665,8 @@ function placeSection(manifest, sectionId, index2) {
21656
21665
  const found = findSection(next, sectionId);
21657
21666
  if (!found) throw new ManifestError("section_not_found", `no section "${sectionId}"`);
21658
21667
  const [moved] = found.page.sections.splice(found.index, 1);
21659
- const at = Math.max(0, Math.min(index2, found.page.sections.length));
21660
- found.page.sections.splice(at, 0, moved);
21668
+ const at2 = Math.max(0, Math.min(index2, found.page.sections.length));
21669
+ found.page.sections.splice(at2, 0, moved);
21661
21670
  return next;
21662
21671
  }
21663
21672
  function setSectionHidden(manifest, sectionId, hidden) {
@@ -21675,8 +21684,8 @@ function addSection(manifest, pageId, section, index2) {
21675
21684
  }
21676
21685
  const next = clone(manifest);
21677
21686
  const page = mustPage(next, pageId);
21678
- const at = index2 === void 0 ? page.sections.length : Math.max(0, Math.min(index2, page.sections.length));
21679
- page.sections.splice(at, 0, { ...section });
21687
+ const at2 = index2 === void 0 ? page.sections.length : Math.max(0, Math.min(index2, page.sections.length));
21688
+ page.sections.splice(at2, 0, { ...section });
21680
21689
  return next;
21681
21690
  }
21682
21691
  function addPage(manifest, page) {
@@ -34621,19 +34630,19 @@ class EditMap {
34621
34630
  this.index.clear();
34622
34631
  }
34623
34632
  }
34624
- function addImplementation(editMap, at, remove2, add) {
34633
+ function addImplementation(editMap, at2, remove2, add) {
34625
34634
  if (remove2 === 0 && add.length === 0) {
34626
34635
  return;
34627
34636
  }
34628
- const existing = editMap.index.get(at);
34637
+ const existing = editMap.index.get(at2);
34629
34638
  if (existing) {
34630
34639
  existing[1] += remove2;
34631
34640
  existing[2].push(...add);
34632
34641
  return;
34633
34642
  }
34634
- const change = [at, remove2, add];
34643
+ const change = [at2, remove2, add];
34635
34644
  editMap.map.push(change);
34636
- editMap.index.set(at, change);
34645
+ editMap.index.set(at2, change);
34637
34646
  }
34638
34647
  function gfmTableAlign(events, index2) {
34639
34648
  let inDelimiterRow = false;
@@ -35213,11 +35222,11 @@ function createTypingSender(send, now = Date.now) {
35213
35222
  stop();
35214
35223
  return;
35215
35224
  }
35216
- const at = now();
35217
- if (typing && at - lastSentAt < TYPING_THROTTLE_MS) return;
35225
+ const at2 = now();
35226
+ if (typing && at2 - lastSentAt < TYPING_THROTTLE_MS) return;
35218
35227
  if (send(true)) {
35219
35228
  typing = true;
35220
- lastSentAt = at;
35229
+ lastSentAt = at2;
35221
35230
  }
35222
35231
  }
35223
35232
  return { update, stop, reset };
@@ -38172,7 +38181,49 @@ function usePageManifest() {
38172
38181
  }
38173
38182
  function useSettoMenu(list2 = "menu") {
38174
38183
  const manifest = usePageManifest();
38175
- return manifest ? visibleMenu(manifest, list2) : null;
38184
+ const { canEdit, config } = useSetto();
38185
+ const blogEmpty = (config.blog?.posts.length ?? 0) === 0;
38186
+ const items = manifest ? canEdit ? ownerMenu(manifest, list2, blogEmpty) : visibleMenu(manifest, list2) : null;
38187
+ const dimmed = items?.filter((item) => item.dimmed).map((item) => item.href) ?? [];
38188
+ useDimmedLinks(dimmed, config.languages ?? []);
38189
+ return items;
38190
+ }
38191
+ const DIM_STYLE_ID = "setto-dimmed-links";
38192
+ const dimmedBy = /* @__PURE__ */ new Map();
38193
+ function drawDimmedLinks() {
38194
+ if (typeof document === "undefined") return;
38195
+ const selectors = [...new Set([...dimmedBy.values()].flat())];
38196
+ let style = document.getElementById(DIM_STYLE_ID);
38197
+ if (!selectors.length) {
38198
+ style?.remove();
38199
+ return;
38200
+ }
38201
+ if (!style) {
38202
+ style = document.createElement("style");
38203
+ style.id = DIM_STYLE_ID;
38204
+ document.head.appendChild(style);
38205
+ }
38206
+ style.textContent = `${selectors.join(",")}{opacity:.45;transition:opacity .15s}${selectors.map((s) => `${s}:hover`).join(",")}{opacity:.8}`;
38207
+ }
38208
+ function useDimmedLinks(hrefs, languages) {
38209
+ const key = hrefs.join("|");
38210
+ const langs = languages.join(",");
38211
+ useEffect(() => {
38212
+ if (!key) return;
38213
+ const mine = {};
38214
+ dimmedBy.set(
38215
+ mine,
38216
+ key.split("|").flatMap((href) => [
38217
+ `a[href="${href}"]`,
38218
+ ...langs.split(",").slice(1).filter(Boolean).map((language) => `a[href="/${language}${href}"]`)
38219
+ ])
38220
+ );
38221
+ drawDimmedLinks();
38222
+ return () => {
38223
+ dimmedBy.delete(mine);
38224
+ drawDimmedLinks();
38225
+ };
38226
+ }, [key, langs]);
38176
38227
  }
38177
38228
  function SettoPage({ pageId, path: path2, sections = {}, builtins, fallback = null }) {
38178
38229
  const manifest = usePageManifest();
@@ -38427,13 +38478,21 @@ function blogStrings(language) {
38427
38478
  function BlogBody({ nodes, language }) {
38428
38479
  return /* @__PURE__ */ jsx(Fragment, { children: nodes.map((node2, i) => renderFlow(node2, i, language)) });
38429
38480
  }
38481
+ function at(node2) {
38482
+ const range = node2.at;
38483
+ return Array.isArray(range) && range.length === 2 && range.every((n) => Number.isInteger(n)) ? { "data-setto-at": `${range[0]}:${range[1]}` } : {};
38484
+ }
38485
+ function atImage(node2, alt) {
38486
+ const where = at(node2);
38487
+ return where["data-setto-at"] ? { ...where, "data-setto-kind": "image", "data-setto-alt": alt } : {};
38488
+ }
38430
38489
  function renderFlow(node2, key, language) {
38431
38490
  switch (node2.type) {
38432
38491
  case "paragraph":
38433
- return /* @__PURE__ */ jsx("p", { children: renderPhrasing(node2.children) }, key);
38492
+ return /* @__PURE__ */ jsx("p", { ...at(node2), children: renderPhrasing(node2.children) }, key);
38434
38493
  case "heading": {
38435
38494
  const Tag = node2.depth === 2 ? "h2" : node2.depth === 3 ? "h3" : "h4";
38436
- return /* @__PURE__ */ jsx(Tag, { children: renderPhrasing(node2.children) }, key);
38495
+ return /* @__PURE__ */ jsx(Tag, { ...at(node2), children: renderPhrasing(node2.children) }, key);
38437
38496
  }
38438
38497
  case "list": {
38439
38498
  const items = node2.children.map((item, i) => /* @__PURE__ */ jsx("li", { children: item.children.map((child, j) => renderListChild(child, j, item.spread === true, language)) }, i));
@@ -38454,7 +38513,8 @@ function renderFlow(node2, key, language) {
38454
38513
  }
38455
38514
  }
38456
38515
  function renderListChild(child, key, spread, language) {
38457
- if (child.type === "paragraph" && !spread) return /* @__PURE__ */ jsx("span", { children: renderPhrasing(child.children) }, key);
38516
+ if (child.type === "paragraph" && !spread)
38517
+ return /* @__PURE__ */ jsx("span", { ...at(child), children: renderPhrasing(child.children) }, key);
38458
38518
  return renderFlow(child, key, language);
38459
38519
  }
38460
38520
  function renderPhrasing(children) {
@@ -38503,7 +38563,7 @@ function BlogComponent({ element: element2, language }) {
38503
38563
  const props = attrs(element2);
38504
38564
  switch (element2.name) {
38505
38565
  case "Image":
38506
- return props.src && props.alt !== void 0 ? /* @__PURE__ */ jsxs("figure", { className: "setto-blog__figure", children: [
38566
+ return props.src && props.alt !== void 0 ? /* @__PURE__ */ jsxs("figure", { className: "setto-blog__figure", ...atImage(element2, props.alt), children: [
38507
38567
  /* @__PURE__ */ jsx(BlogImg, { src: props.src, alt: props.alt, sizes: "(min-width: 760px) 720px, 100vw" }),
38508
38568
  props.caption ? /* @__PURE__ */ jsx("figcaption", { children: props.caption }) : null
38509
38569
  ] }) : null;
@@ -38514,7 +38574,7 @@ function BlogComponent({ element: element2, language }) {
38514
38574
  return /* @__PURE__ */ jsxs("figure", { className: "setto-blog__gallery", children: [
38515
38575
  /* @__PURE__ */ jsx("div", { className: "setto-blog__gallery-grid", children: images.map((image2, i) => {
38516
38576
  const a = attrs(image2);
38517
- return a.src ? /* @__PURE__ */ jsxs("figure", { className: "setto-blog__gallery-item", children: [
38577
+ return a.src ? /* @__PURE__ */ jsxs("figure", { className: "setto-blog__gallery-item", ...atImage(image2, a.alt ?? ""), children: [
38518
38578
  /* @__PURE__ */ jsx(BlogImg, { src: a.src, alt: a.alt ?? "", sizes: "(min-width: 760px) 360px, 50vw" }),
38519
38579
  a.caption ? /* @__PURE__ */ jsx("figcaption", { children: a.caption }) : null
38520
38580
  ] }, i) : null;
@@ -38629,16 +38689,19 @@ function useClearFixedHeader(ref, enabled) {
38629
38689
  return clear;
38630
38690
  }
38631
38691
  function fixedAncestor(node2) {
38632
- for (let at = node2; at && at !== document.body; at = at.parentElement) {
38633
- if (getComputedStyle(at).position === "fixed") return at;
38692
+ for (let at2 = node2; at2 && at2 !== document.body; at2 = at2.parentElement) {
38693
+ if (getComputedStyle(at2).position === "fixed") return at2;
38634
38694
  }
38635
38695
  return null;
38636
38696
  }
38637
38697
  function BlogIndexView({ title, first, blogPath, ctx }) {
38638
38698
  const posts = ctx.config.blog ? postsForLanguage(ctx.config.blog.posts, ctx.language, ctx.fallbackLanguage) : [];
38639
38699
  const Heading = first ? "h1" : "h2";
38700
+ const { canEdit } = useSetto();
38701
+ const Owner = useSettoChromeSlot("blog-owner");
38640
38702
  return /* @__PURE__ */ jsxs(Fragment, { children: [
38641
38703
  /* @__PURE__ */ jsx(Heading, { className: "setto-blog__title", children: title }),
38704
+ canEdit && Owner ? /* @__PURE__ */ jsx("div", { className: "setto-blog__owner", "data-setto-ui": "", children: /* @__PURE__ */ jsx(Owner, { blogPath, language: ctx.language, posts: posts.length, Draft: BlogDraft }) }) : null,
38642
38705
  posts.length === 0 ? /* @__PURE__ */ jsx("p", { className: "setto-blog__empty", children: ctx.strings.empty }) : /* @__PURE__ */ jsx("div", { className: "setto-blog__list", children: posts.map((post2) => /* @__PURE__ */ jsx(PostCard, { post: post2, blogPath, ctx, headingLevel: first ? "h2" : "h3" }, post2.slug)) })
38643
38706
  ] });
38644
38707
  }
@@ -38698,18 +38761,35 @@ function BlogPostView({ slug, blogPath, ctx }) {
38698
38761
  ] });
38699
38762
  }
38700
38763
  const fm = post2?.frontmatter ?? summary;
38764
+ return /* @__PURE__ */ jsx(PostArticle, { frontmatter: fm, ctx, before: back, after: /* @__PURE__ */ jsx(ShareBar, { title: fm.title, strings: ctx.strings }), children: state === "ready" && post2 ? /* @__PURE__ */ jsx(BlogBody, { nodes: post2.body.children, language: ctx.language }) : /* @__PURE__ */ jsx("p", { className: "setto-blog__loading", "aria-live": "polite", children: ctx.strings.loading }) });
38765
+ }
38766
+ function PostArticle({
38767
+ frontmatter: fm,
38768
+ ctx,
38769
+ before,
38770
+ after,
38771
+ draft = false,
38772
+ children
38773
+ }) {
38774
+ const field = (name2) => draft ? { "data-setto-field": name2 } : {};
38775
+ const Title = draft ? "h2" : "h1";
38701
38776
  return /* @__PURE__ */ jsxs("article", { className: "setto-blog__post", lang: fm.locale, children: [
38702
- back,
38777
+ before,
38703
38778
  /* @__PURE__ */ jsxs("header", { className: "setto-blog__post-header", children: [
38704
38779
  /* @__PURE__ */ jsx("time", { className: "setto-blog__date", dateTime: fm.date, children: formatPostDate(fm.date, ctx.language) }),
38705
- /* @__PURE__ */ jsx("h1", { className: "setto-blog__post-title", children: fm.title }),
38706
- /* @__PURE__ */ jsx("p", { className: "setto-blog__lead", children: fm.description })
38780
+ /* @__PURE__ */ jsx(Title, { className: "setto-blog__post-title", ...field("title"), children: fm.title }),
38781
+ /* @__PURE__ */ jsx("p", { className: "setto-blog__lead", ...field("description"), children: fm.description })
38707
38782
  ] }),
38708
- fm.cover ? /* @__PURE__ */ jsx("figure", { className: "setto-blog__cover", children: /* @__PURE__ */ jsx(BlogImg, { src: fm.cover, alt: "", sizes: "(min-width: 1040px) 960px, 100vw" }) }) : null,
38709
- state === "ready" && post2 ? /* @__PURE__ */ jsx("div", { className: "setto-blog__prose", children: /* @__PURE__ */ jsx(BlogBody, { nodes: post2.body.children, language: ctx.language }) }) : /* @__PURE__ */ jsx("p", { className: "setto-blog__loading", "aria-live": "polite", children: ctx.strings.loading }),
38710
- /* @__PURE__ */ jsx(ShareBar, { title: fm.title, strings: ctx.strings })
38783
+ fm.cover ? /* @__PURE__ */ jsx("figure", { className: "setto-blog__cover", ...field("cover"), children: /* @__PURE__ */ jsx(BlogImg, { src: fm.cover, alt: "", sizes: "(min-width: 1040px) 960px, 100vw" }) }) : null,
38784
+ /* @__PURE__ */ jsx("div", { className: "setto-blog__prose", children }),
38785
+ after
38711
38786
  ] });
38712
38787
  }
38788
+ function BlogDraft({ draft }) {
38789
+ useBlogStyles();
38790
+ const ctx = useBlogContext();
38791
+ return /* @__PURE__ */ jsx(PostArticle, { frontmatter: draft.frontmatter, ctx, draft: true, children: /* @__PURE__ */ jsx(BlogBody, { nodes: draft.body.children, language: ctx.language }) });
38792
+ }
38713
38793
  function usePostTitle(title, siteName) {
38714
38794
  useEffect(() => {
38715
38795
  if (!title || typeof document === "undefined") return;
@@ -40815,6 +40895,7 @@ export {
40815
40895
  BLOCK_TYPES,
40816
40896
  BLOG_CSS,
40817
40897
  BUILTIN_SECTION_TYPES,
40898
+ BlogDraft,
40818
40899
  CHECKOUT_INTENT,
40819
40900
  DEFAULT_LANGUAGE,
40820
40901
  EMPTY_I18N_STORE_SNAPSHOT,
@@ -40888,6 +40969,7 @@ export {
40888
40969
  normalisePath,
40889
40970
  optimizedSrcSet,
40890
40971
  optimizedUrl,
40972
+ ownerMenu,
40891
40973
  pageById,
40892
40974
  pageForPath,
40893
40975
  pageTitleKey,