@domphy/press 0.19.0 → 0.19.2

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.
@@ -277,10 +277,53 @@ function announcementBar(config) {
277
277
  ...idAttr ? { dataId: idAttr } : {}
278
278
  };
279
279
  }
280
- function header(config) {
280
+ function localeSwitcher(ctx) {
281
+ const { config, route } = ctx;
282
+ if (!config.locales) return null;
283
+ const entries = Object.entries(config.locales);
284
+ if (entries.length <= 1) return null;
285
+ let currentKey = "/";
286
+ let barePath = route;
287
+ for (const [key] of entries) {
288
+ if (key !== "/" && route.startsWith(key.replace(/\/$/, ""))) {
289
+ currentKey = key;
290
+ barePath = route.slice(key.replace(/\/$/, "").length) || "/";
291
+ break;
292
+ }
293
+ }
294
+ const currentLocale = config.locales[currentKey];
295
+ if (!currentLocale) return null;
296
+ const links = entries.map(([key, locale]) => {
297
+ const prefix = key === "/" ? "" : key.replace(/\/$/, "");
298
+ const href = prefix + (barePath === "/" ? "/" : barePath);
299
+ return {
300
+ a: locale.label,
301
+ href,
302
+ class: `dp-locale-option${key === currentKey ? " active" : ""}`,
303
+ ...key === currentKey ? { ariaCurrent: "true" } : {},
304
+ lang: locale.lang
305
+ };
306
+ });
307
+ return {
308
+ div: [
309
+ { span: ["\u{1F310} ", currentLocale.label], class: "dp-locale-current" },
310
+ { div: links, class: "dp-locale-menu" }
311
+ ],
312
+ class: "dp-locale-switcher",
313
+ ariaLabel: "Select language"
314
+ };
315
+ }
316
+ function header(ctx) {
317
+ const { config } = ctx;
281
318
  const searchEnabled = config.themeConfig.search !== false;
282
- const logoEl = config.themeConfig.logo ? { a: [{ img: null, src: config.themeConfig.logo, alt: config.title, class: "dp-logo-img" }], href: config.base, class: "dp-logo" } : { a: config.title, href: config.base, class: "dp-logo" };
319
+ const logo = config.themeConfig.logo;
320
+ const logoInner = logo ? typeof logo === "string" ? [{ img: null, src: logo, alt: config.title, class: "dp-logo-img" }] : [
321
+ { img: null, src: logo.light, alt: config.title, class: "dp-logo-img dp-logo-light" },
322
+ { img: null, src: logo.dark, alt: config.title, class: "dp-logo-img dp-logo-dark" }
323
+ ] : [];
324
+ const logoEl = logo ? { a: logoInner, href: config.base, class: "dp-logo" } : { a: config.title, href: config.base, class: "dp-logo" };
283
325
  const socialEls = (config.themeConfig.socialLinks ?? []).map(socialLinkEl);
326
+ const localeEl = localeSwitcher(ctx);
284
327
  return {
285
328
  header: [
286
329
  logoEl,
@@ -307,6 +350,7 @@ function header(config) {
307
350
  class: "dp-search-slot"
308
351
  }] : [],
309
352
  ...socialEls,
353
+ ...localeEl ? [localeEl] : [],
310
354
  { button: "\u25D0", type: "button", class: "dp-theme-toggle", ariaLabel: "Toggle dark mode", dataThemeToggle: "" },
311
355
  { button: "\u2630", type: "button", class: "dp-menu-toggle", ariaLabel: "Toggle menu", dataMenuToggle: "" }
312
356
  ],
@@ -377,9 +421,10 @@ function tocAside(ctx) {
377
421
  const [minLevel, maxLevel] = ctx.config.themeConfig.outline?.level ?? [2, 3];
378
422
  const entries = ctx.toc.filter((e) => e.level >= minLevel && e.level <= maxLevel);
379
423
  if (entries.length === 0) return null;
424
+ const tocTitle = ctx.config.themeConfig.tocTitle ?? "On this page";
380
425
  return {
381
426
  aside: [
382
- { div: "On this page", class: "dp-aside-title" },
427
+ { div: tocTitle, class: "dp-aside-title" },
383
428
  { nav: entries.map((e) => ({ a: e.text, href: `#${e.slug}`, class: `dp-toc-${e.level}` })), class: "dp-toc" }
384
429
  ],
385
430
  class: "dp-aside"
@@ -427,25 +472,37 @@ function pageBadge(frontmatter) {
427
472
  if (!text2) return null;
428
473
  return { span: text2, class: `dp-badge dp-badge-${type} dp-page-badge` };
429
474
  }
475
+ function resolveSlot(ctx, key, fallback) {
476
+ const override = ctx.config.themeConfig.slots?.[key];
477
+ return override ? override(ctx) : fallback(ctx);
478
+ }
430
479
  function pageShell(ctx) {
480
+ const slots = ctx.config.themeConfig.slots;
481
+ const showSidebar = ctx.frontmatter.sidebar !== false;
431
482
  const main = [];
432
483
  const badge = pageBadge(ctx.frontmatter);
433
484
  if (badge) main.push({ div: [badge], class: "dp-page-badge-row" });
434
485
  main.push({ div: ctx.body, class: "dp-content" });
435
- const pn = prevNext(ctx);
486
+ const pn = resolveSlot(ctx, "prevNext", prevNext);
436
487
  if (pn) main.push(pn);
437
- const footer = docFooter(ctx);
438
- if (footer) main.push(footer);
439
- const shellChildren = [sidebar(ctx), { main, class: "dp-main" }];
440
- const aside = tocAside(ctx);
441
- if (aside) shellChildren.push(aside);
488
+ const docFooterEl = resolveSlot(ctx, "docFooter", docFooter);
489
+ if (docFooterEl) main.push(docFooterEl);
490
+ const sidebarEl = showSidebar ? resolveSlot(ctx, "sidebar", sidebar) : null;
491
+ const shellChildren = [
492
+ ...sidebarEl ? [sidebarEl] : [],
493
+ { main, class: `dp-main${showSidebar ? "" : " dp-main-full"}` }
494
+ ];
495
+ const asideEl = resolveSlot(ctx, "aside", tocAside);
496
+ if (asideEl && showSidebar) shellChildren.push(asideEl);
497
+ const headerEl = resolveSlot(ctx, "header", header);
442
498
  const bar = announcementBar(ctx.config);
499
+ const footerContent = slots?.footer ? slots.footer(ctx) : { footer: ctx.config.themeConfig.footerMessage ?? "", class: "dp-footer" };
443
500
  return {
444
501
  div: [
445
502
  ...bar ? [bar] : [],
446
- header(ctx.config),
503
+ ...headerEl ? [headerEl] : [],
447
504
  { div: shellChildren, class: "dp-shell" },
448
- { footer: ctx.config.themeConfig.footerMessage ?? "", class: "dp-footer" }
505
+ ...footerContent ? [footerContent] : []
449
506
  ]
450
507
  };
451
508
  }
@@ -483,7 +540,7 @@ function homeShell(ctx) {
483
540
  return {
484
541
  div: [
485
542
  ...bar ? [bar] : [],
486
- header(ctx.config),
543
+ header(ctx),
487
544
  { main, class: "dp-main dp-main-home" },
488
545
  { footer: ctx.config.themeConfig.footerMessage ?? "", class: "dp-footer" }
489
546
  ]
@@ -669,6 +726,47 @@ function shapeContainers(tokens) {
669
726
  output.push(token);
670
727
  continue;
671
728
  }
729
+ if (token.type === "container_card-grid_open") {
730
+ token.tag = "div";
731
+ token.attrSet("class", "custom-block card-grid");
732
+ output.push(token);
733
+ continue;
734
+ }
735
+ if (token.type === "container_card-grid_close") {
736
+ token.tag = "div";
737
+ output.push(token);
738
+ continue;
739
+ }
740
+ if (token.type === "container_card_open") {
741
+ token.tag = "div";
742
+ token.attrSet("class", "custom-block card");
743
+ const title = containerTitle(token.info.trim(), "card");
744
+ output.push(token);
745
+ if (title) output.push(...buildTitleTokens(Token, "p", "paragraph_open", "paragraph_close", "card-title", title));
746
+ continue;
747
+ }
748
+ if (token.type === "container_card_close") {
749
+ token.tag = "div";
750
+ output.push(token);
751
+ continue;
752
+ }
753
+ if (token.type === "container_link-card_open") {
754
+ const info = token.info.trim().slice("link-card".length).trim();
755
+ const linkMatch = info.match(/^\[([^\]]+)\]\(([^)]+)\)/);
756
+ const href = linkMatch ? linkMatch[2] : "#";
757
+ const title = linkMatch ? linkMatch[1] : info;
758
+ token.tag = "a";
759
+ token.attrSet("class", "custom-block link-card");
760
+ token.attrSet("href", href);
761
+ output.push(token);
762
+ if (title) output.push(...buildTitleTokens(Token, "p", "paragraph_open", "paragraph_close", "link-card-title", title));
763
+ continue;
764
+ }
765
+ if (token.type === "container_link-card_close") {
766
+ token.tag = "a";
767
+ output.push(token);
768
+ continue;
769
+ }
672
770
  if (token.type === "container_code-group_open") {
673
771
  let depth = 0, closeIndex = -1;
674
772
  for (let j = i; j < tokens.length; j++) {
@@ -726,7 +824,7 @@ function createParser(docsDir, highlight) {
726
824
  md.use(emojiPlugin);
727
825
  const noopRender = () => "";
728
826
  const ADMONITION_NAMES = Object.keys(ADMONITION_TITLES);
729
- for (const name of [...ADMONITION_NAMES, "details", "code-group", "steps"]) {
827
+ for (const name of [...ADMONITION_NAMES, "details", "code-group", "steps", "card", "card-grid", "link-card"]) {
730
828
  md.use(container, name, { render: noopRender });
731
829
  }
732
830
  md.core.ruler.push("press_containers", (state) => {
@@ -915,6 +1013,21 @@ a:hover{text-decoration:underline}
915
1013
  .dp-theme-toggle,.dp-menu-toggle{border:1px solid ${border};background:${bgSoft};color:${text};border-radius:${ts(2)};width:${ts(8.5)};height:${ts(8.5)};cursor:pointer;font-size:16px}
916
1014
  .dp-menu-toggle{display:none}
917
1015
 
1016
+ /* --------------------------------------------------------- locale switcher */
1017
+ .dp-locale-switcher{position:relative;display:flex;align-items:center}
1018
+ .dp-locale-current{color:${textSoft};font-size:13px;font-weight:500;cursor:pointer;user-select:none;padding:${ts(1)} ${ts(2)};border:1px solid ${border};border-radius:${ts(1.5)};background:${bgSoft};white-space:nowrap;display:flex;align-items:center;gap:${ts(1)}}
1019
+ .dp-locale-current::after{content:" \u25BE";font-size:10px;opacity:.6}
1020
+ .dp-locale-menu{display:none;position:absolute;top:calc(100% + ${ts(2)});right:0;background:${bgSoft};border:1px solid ${border};border-radius:${ts(2)};padding:${ts(1.5)};min-width:${ts(32)};z-index:200;flex-direction:column;gap:${ts(0.5)};box-shadow:0 4px 16px rgba(0,0,0,.1)}
1021
+ .dp-locale-switcher:hover .dp-locale-menu,.dp-locale-switcher:focus-within .dp-locale-menu{display:flex}
1022
+ .dp-locale-option{display:block;padding:${ts(1.25)} ${ts(2.5)};border-radius:${ts(1.25)};font-size:13px;color:${textSoft}}
1023
+ .dp-locale-option:hover{background:${bgMute};color:${text};text-decoration:none}
1024
+ .dp-locale-option.active{color:${brand};font-weight:600}
1025
+
1026
+ /* -------------------------------------------------- logo light/dark variant */
1027
+ [data-theme="dark"] .dp-logo-light{display:none}
1028
+ [data-theme="light"] .dp-logo-dark,.dp-logo-dark{display:none}
1029
+ [data-theme="dark"] .dp-logo-dark{display:block}
1030
+
918
1031
  /* ------------------------------------------------------------ social links */
919
1032
  .dp-social-link{display:inline-flex;align-items:center;justify-content:center;width:${ts(8.5)};height:${ts(8.5)};border-radius:${ts(2)};color:${textSoft};background:${bgSoft};border:1px solid ${border};font-size:10px;font-weight:700;flex-shrink:0}
920
1033
  .dp-social-link:hover{color:${text};border-color:${textSoft};text-decoration:none}
@@ -938,6 +1051,7 @@ a:hover{text-decoration:underline}
938
1051
  .dp-sidebar a[aria-current="page"]{color:${brand};font-weight:600;background:${bgSoft}}
939
1052
  .dp-sidebar-link-with-badge{display:flex;align-items:center}
940
1053
  .dp-main{padding:${ts(8)} ${ts(12)} ${ts(20)};min-width:0}
1054
+ .dp-main-full{grid-column:1 / -1;max-width:${contentMax};margin:0 auto}
941
1055
  .dp-content{max-width:${contentMax}}
942
1056
  .dp-aside{position:sticky;top:${headerH};max-height:calc(100vh - ${headerH});overflow-y:auto;padding:${ts(8)} ${ts(6)};font-size:13px}
943
1057
  .dp-aside-title{font-weight:700;margin-bottom:${ts(2)};color:${text}}
@@ -947,6 +1061,15 @@ a:hover{text-decoration:underline}
947
1061
  .dp-toc-3{padding-left:${ts(3)}}
948
1062
  .dp-toc-4{padding-left:${ts(6)}}
949
1063
 
1064
+ /* -------------------------------------------------- card / link-card / card-grid */
1065
+ .custom-block.card{background:${bgSoft};border:1px solid ${border};border-radius:${ts(3)};padding:${ts(5)} ${ts(6)};margin:${ts(3)} 0}
1066
+ .custom-block.card .card-title{font-size:16px;font-weight:600;color:${textStrong};margin:0 0 ${ts(2)}}
1067
+ .custom-block.card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(${ts(56)},1fr));gap:${ts(4)};margin:${ts(4)} 0}
1068
+ a.custom-block.link-card{display:block;background:${bgSoft};border:1px solid ${border};border-radius:${ts(3)};padding:${ts(5)} ${ts(6)};margin:${ts(3)} 0;transition:border-color .15s,background .15s;text-decoration:none;color:${text}}
1069
+ a.custom-block.link-card:hover{border-color:${brand};background:${bgMute};text-decoration:none}
1070
+ a.custom-block.link-card .link-card-title{font-size:16px;font-weight:600;color:${brand};margin:0 0 ${ts(2)}}
1071
+ .custom-block.card-grid .custom-block.card,.custom-block.card-grid a.custom-block.link-card{margin:0}
1072
+
950
1073
  /* ------------------------------------------------------------------ badges */
951
1074
  .dp-badge{display:inline-block;padding:${ts(0.5)} ${ts(1.75)};border-radius:${ts(2.5)};font-size:11px;font-weight:700;line-height:1.4;white-space:nowrap;vertical-align:middle}
952
1075
  .dp-badge-tip{background:color-mix(in srgb,${brand} 12%,${bg});color:${brand}}
@@ -1281,6 +1404,10 @@ async function buildSite(options) {
1281
1404
  for (const page of localePages) {
1282
1405
  const source = readFileSync2(page.filePath, "utf8");
1283
1406
  const doc = await renderDoc(source, { filePath: page.filePath, docsDir: srcDir, repoRoot: srcDir, highlight });
1407
+ if (doc.frontmatter.draft === true) {
1408
+ console.log(` \u21B7 ${page.route} (draft, skipped)`);
1409
+ continue;
1410
+ }
1284
1411
  sanitizeStyles(doc.body);
1285
1412
  const textParts = [];
1286
1413
  flattenText(doc.body, textParts);
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ if (command === "build") {
25
25
  const srcDir = resolve(process.cwd(), flag("--src") ?? config.srcDir ?? ".");
26
26
  const outDir = resolve(process.cwd(), flag("--out") ?? config.outDir ?? "dist");
27
27
  const publicDir = resolve(process.cwd(), "public");
28
- const { buildSite } = await import("./build-6Q2QN246.js");
28
+ const { buildSite } = await import("./build-DKE372GH.js");
29
29
  await buildSite({
30
30
  config: { ...config, srcDir: config.srcDir ?? ".", outDir: config.outDir ?? "dist" },
31
31
  srcDir,
@@ -39,7 +39,7 @@ if (command === "build") {
39
39
  const srcDir = resolve(process.cwd(), flag("--src") ?? config.srcDir ?? ".");
40
40
  const outDir = resolve(process.cwd(), flag("--out") ?? config.outDir ?? ".press-dev");
41
41
  const publicDir = resolve(process.cwd(), "public");
42
- const { buildSite } = await import("./build-6Q2QN246.js");
42
+ const { buildSite } = await import("./build-DKE372GH.js");
43
43
  const { startDevServer } = await import("./serve-LQBYPP6C.js");
44
44
  async function rebuild() {
45
45
  const start = Date.now();
package/dist/index.d.ts CHANGED
@@ -33,11 +33,31 @@ interface EditLink {
33
33
  pattern: string;
34
34
  text?: string;
35
35
  }
36
+ /** Override built-in layout slots. Each function receives the full LayoutContext
37
+ * and must return a DomphyElement (or null to omit the slot entirely). */
38
+ interface LayoutSlots {
39
+ /** Replace the entire header bar. */
40
+ header?: (ctx: unknown) => DomphyElement | null;
41
+ /** Replace the sidebar navigation. */
42
+ sidebar?: (ctx: unknown) => DomphyElement | null;
43
+ /** Replace the TOC aside panel. */
44
+ aside?: (ctx: unknown) => DomphyElement | null;
45
+ /** Replace the prev/next pagination row. */
46
+ prevNext?: (ctx: unknown) => DomphyElement | null;
47
+ /** Replace the doc footer (edit link + last updated). */
48
+ docFooter?: (ctx: unknown) => DomphyElement | null;
49
+ /** Replace the page footer bar. */
50
+ footer?: (ctx: unknown) => DomphyElement | null;
51
+ }
36
52
  interface ThemeConfig {
37
53
  nav: NavItem[];
38
54
  /** Keys are route prefixes (e.g. "/guide/"). Longest match wins. */
39
55
  sidebar: Record<string, SidebarItem[]>;
40
- logo?: string;
56
+ /** Single URL or separate light/dark variants. */
57
+ logo?: string | {
58
+ light: string;
59
+ dark: string;
60
+ };
41
61
  search?: false | {
42
62
  placeholder?: string;
43
63
  limit?: number;
@@ -49,6 +69,8 @@ interface ThemeConfig {
49
69
  outline?: {
50
70
  level: [number, number];
51
71
  };
72
+ /** TOC section heading text. Default "On this page". */
73
+ tocTitle?: string;
52
74
  /** Enable mermaid diagrams. Pass { cdn } to override CDN URL. */
53
75
  mermaid?: boolean | {
54
76
  cdn?: string;
@@ -59,6 +81,8 @@ interface ThemeConfig {
59
81
  text: string;
60
82
  dismissible?: boolean;
61
83
  };
84
+ /** Override individual layout slots with custom components. */
85
+ slots?: LayoutSlots;
62
86
  }
63
87
  interface LocaleConfig {
64
88
  label: string;