@riebeckite/honox 0.0.8 → 0.0.10

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/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  // src/config_loader.ts
2
2
  import fs2 from "node:fs/promises";
3
- import os from "node:os";
4
3
  import path2 from "node:path";
5
4
  import { build as buildWithEsbuild } from "esbuild";
6
5
 
@@ -112,7 +111,7 @@ function escapeRegExp(value) {
112
111
  async function loadRiebeckiteConfig(options) {
113
112
  const configFile = options.configFile ?? "riebeckite.config.ts";
114
113
  const temporaryDirectory = await fs2.mkdtemp(
115
- path2.join(os.tmpdir(), "riebeckite-config-")
114
+ path2.join(options.configRoot, ".riebeckite-config-")
116
115
  );
117
116
  const outputFile = path2.join(temporaryDirectory, "config.mjs");
118
117
  try {
@@ -133,7 +132,10 @@ async function loadRiebeckiteConfig(options) {
133
132
  banner: {
134
133
  js: 'import { createRequire as __riebeckiteCreateRequire } from "node:module"; const require = __riebeckiteCreateRequire(import.meta.url);'
135
134
  },
136
- plugins: options.workspaceRoot ? [workspacePackageResolver(options.workspaceRoot)] : []
135
+ plugins: [
136
+ externalizeBareModuleImports(),
137
+ ...options.workspaceRoot ? [workspacePackageResolver(options.workspaceRoot)] : []
138
+ ]
137
139
  });
138
140
  const module = await import(`${pathToFileUrl(outputFile)}?t=${Date.now()}`);
139
141
  return module.default;
@@ -151,6 +153,17 @@ function resolveHonoxConfig(config, appRoot) {
151
153
  }
152
154
  };
153
155
  }
156
+ function externalizeBareModuleImports() {
157
+ return {
158
+ name: "externalize-bare-module-imports",
159
+ setup(buildApi) {
160
+ buildApi.onResolve({ filter: /^[^./]|^\.[^./]|^\.\.$/ }, (args) => {
161
+ if (path2.isAbsolute(args.path)) return null;
162
+ return { path: args.path, external: true };
163
+ });
164
+ }
165
+ };
166
+ }
154
167
  function pathToFileUrl(filePath) {
155
168
  return `file:///${filePath.replace(/\\/g, "/").replace(/^([A-Za-z]):/, "$1:")}`;
156
169
  }
@@ -1337,139 +1350,833 @@ function riebeckiteSsrExternals(externals) {
1337
1350
  };
1338
1351
  }
1339
1352
 
1340
- // src/scaffold/localized-content.ts
1341
- var HELPERS = {
1342
- en: "https://github.com/Rerurate514/riebeckite/blob/main/docs/en/README.md",
1343
- ja: "https://github.com/Rerurate514/riebeckite/blob/main/docs/ja/README.md"
1353
+ // src/scaffold/app-templates.ts
1354
+ function server() {
1355
+ return `import { mountRiebeckiteEndpoints } from "@riebeckite/honox/server";
1356
+ import { createApp } from "honox/server";
1357
+ import { config } from "./config";
1358
+ import { content } from "./content";
1359
+
1360
+ const app = createApp({
1361
+ init: (app) => {
1362
+ mountRiebeckiteEndpoints(app, { config, content });
1363
+ },
1364
+ });
1365
+
1366
+ export default app;
1367
+ export { config, content };
1368
+ `;
1369
+ }
1370
+ function client() {
1371
+ return `import { initRiebeckiteClient } from "virtual:riebeckite/client";
1372
+ import { createClient } from "honox/client";
1373
+
1374
+ createClient();
1375
+
1376
+ if (document.readyState === "loading") {
1377
+ document.addEventListener("DOMContentLoaded", initPage, { once: true });
1378
+ } else {
1379
+ initPage();
1380
+ }
1381
+
1382
+ function initPage() {
1383
+ initRiebeckiteClient();
1384
+ }
1385
+ `;
1386
+ }
1387
+ function appConfig() {
1388
+ return `import path from "node:path";
1389
+ import { fileURLToPath } from "node:url";
1390
+ import { resolveConfigModule } from "@riebeckite/core";
1391
+ import * as rawConfigModule from "../riebeckite.config";
1392
+
1393
+ const appRoot =
1394
+ process.env.RIEBECKITE_APP_ROOT ??
1395
+ fileURLToPath(new URL("../", import.meta.url));
1396
+ const resolvedConfig = resolveConfigModule(rawConfigModule);
1397
+
1398
+ export const config = {
1399
+ ...resolvedConfig,
1400
+ buildDirectory: path.join(appRoot, ".riebeckite"),
1401
+ content: {
1402
+ ...resolvedConfig.content,
1403
+ directory: path.resolve(appRoot, resolvedConfig.content.directory),
1404
+ },
1344
1405
  };
1345
- var PLUGIN_INDEX_URL = "https://github.com/Rerurate514/riebeckite/tree/main/packages/plugins";
1346
- var THEMES_INDEX_URL = "https://github.com/Rerurate514/riebeckite/tree/main/packages/themes";
1347
- function pluginReadmeUrl(slug) {
1348
- return `https://github.com/Rerurate514/riebeckite/blob/main/packages/plugins/${slug}/README.md`;
1406
+ `;
1349
1407
  }
1350
- function themeReadmeUrl(slug) {
1351
- return `https://github.com/Rerurate514/riebeckite/blob/main/packages/themes/${slug}/README.md`;
1408
+ function appContent() {
1409
+ return `import { ContentManager } from "@riebeckite/core";
1410
+ import { config } from "./config";
1411
+ import { CONTENT_DIR } from "./constants/paths";
1412
+
1413
+ export const content = new ContentManager(
1414
+ CONTENT_DIR,
1415
+ config.content.exclude,
1416
+ {
1417
+ config,
1418
+ plugins: config.plugins,
1419
+ },
1420
+ );
1421
+ `;
1352
1422
  }
1353
- function localizedContentFiles(variables, preset) {
1354
- const defaultLang = defaultLanguageForLocale(variables.locale);
1355
- const files = [];
1356
- const localized = preset.languages.length > 1;
1357
- const pushLocalized = (basePath, build2) => {
1358
- if (!localized) {
1359
- files.push({
1360
- path: `content/${basePath}.md`,
1361
- content: build2(defaultLang)
1362
- });
1363
- return;
1364
- }
1365
- for (const language of SCAFFOLD_LANGUAGES) {
1366
- const suffix = language === defaultLang ? "" : `.${language}`;
1367
- files.push({
1368
- path: `content/${basePath}${suffix}.md`,
1369
- content: build2(language)
1370
- });
1371
- }
1372
- };
1373
- const pushEnglishOnly = (basePath, build2) => {
1374
- const suffix = defaultLang === "en" ? "" : ".en";
1375
- files.push({
1376
- path: `content/${basePath}${suffix}.md`,
1377
- content: build2()
1378
- });
1379
- };
1380
- for (const page of preset.contentPages) {
1381
- if (page === "index") {
1382
- pushLocalized("index", (language) => indexContent(variables, language));
1383
- } else if (page === "framework/plugins") {
1384
- pushLocalized(
1385
- "framework/plugins",
1386
- (language) => pluginsContent(language)
1387
- );
1388
- } else if (page === "framework/themes") {
1389
- pushLocalized("framework/themes", (language) => themesContent(language));
1390
- } else if (page === "guide") {
1391
- pushEnglishOnly("guide", guideContent);
1392
- } else if (page === "examples") {
1393
- pushLocalized(
1394
- "examples",
1395
- (language) => examplesContent(preset, language)
1396
- );
1397
- } else if (page === "reference/plugins") {
1398
- pushEnglishOnly(
1399
- "reference/plugins",
1400
- () => referencePluginsContent(preset, variables)
1401
- );
1402
- } else if (page === "reference/themes") {
1403
- pushEnglishOnly("reference/themes", referenceThemesContent);
1404
- }
1423
+ function paths() {
1424
+ return `import path from "node:path";
1425
+ import { fileURLToPath } from "node:url";
1426
+ import { config } from "../config";
1427
+
1428
+ const appRoot =
1429
+ process.env.RIEBECKITE_APP_ROOT ??
1430
+ fileURLToPath(new URL("../../", import.meta.url));
1431
+
1432
+ export const CONTENT_DIR = path.resolve(appRoot, config.content.directory);
1433
+ export const ASSETS_ROOT = "public/";
1434
+ `;
1435
+ }
1436
+ function globalDeclarations() {
1437
+ return `import type { PluginHeadTag } from "@riebeckite/core";
1438
+
1439
+ declare module "virtual:riebeckite/client" {
1440
+ export function initRiebeckiteClient(): void;
1441
+ }
1442
+
1443
+ declare module "hono" {
1444
+ interface Env {
1445
+ Variables: {
1446
+ headTags?: readonly PluginHeadTag[];
1447
+ htmlLanguage?: string;
1448
+ };
1449
+ Bindings: Record<string, never>;
1405
1450
  }
1406
- return files;
1407
1451
  }
1408
- function read(text, language) {
1409
- return text[language];
1452
+ `;
1410
1453
  }
1411
- function frontmatter() {
1412
- return ["---", "publish: true", "---", ""].join("\n");
1454
+ function style(preset) {
1455
+ const imports = [];
1456
+ if (preset.plugins.length > 0) {
1457
+ imports.push('@import "./.riebeckite/plugin-styles.css";');
1458
+ }
1459
+ if (preset.theme) {
1460
+ imports.push('@import "./.riebeckite/theme-styles.css";');
1461
+ }
1462
+ const blocks = [
1463
+ "",
1464
+ "body {",
1465
+ " margin: 0;",
1466
+ " font-family: system-ui, sans-serif;",
1467
+ "}",
1468
+ ""
1469
+ ];
1470
+ if (preset.appFiles.includes("header")) {
1471
+ blocks.push(
1472
+ ".site-header {",
1473
+ " display: flex;",
1474
+ " align-items: center;",
1475
+ " justify-content: space-between;",
1476
+ " gap: 1rem;",
1477
+ " padding: 1rem;",
1478
+ " border-bottom: 1px solid #d1d5db;",
1479
+ "}",
1480
+ "",
1481
+ ".site-header__home {",
1482
+ " color: #111827;",
1483
+ " font-weight: 700;",
1484
+ " text-decoration: none;",
1485
+ "}",
1486
+ ""
1487
+ );
1488
+ }
1489
+ if (preset.appFiles.includes("article")) {
1490
+ blocks.push(
1491
+ ".site-article {",
1492
+ " display: block;",
1493
+ " max-width: 48rem;",
1494
+ " padding: 2rem 1rem;",
1495
+ " margin: 0 auto;",
1496
+ "}",
1497
+ ""
1498
+ );
1499
+ }
1500
+ if (!preset.theme) {
1501
+ blocks.push(
1502
+ ".riebeckite-empty {",
1503
+ " max-width: 48rem;",
1504
+ " padding: 4rem 1rem;",
1505
+ " margin: 0 auto;",
1506
+ "}",
1507
+ ""
1508
+ );
1509
+ }
1510
+ return [...imports, ...blocks].join("\n");
1413
1511
  }
1414
- function heading(level, text) {
1415
- return `${"#".repeat(level)} ${text}`;
1512
+ function siteHeader() {
1513
+ return `import { ColorModeToggle } from "@riebeckite/plugin-color-mode";
1514
+ import { config } from "../config";
1515
+
1516
+ export function SiteHeader() {
1517
+ return (
1518
+ <header class="site-header">
1519
+ <a href="/" class="site-header__home">
1520
+ {config.site.title}
1521
+ </a>
1522
+ <ColorModeToggle />
1523
+ </header>
1524
+ );
1416
1525
  }
1417
- function codeBlock(language, code) {
1418
- return `\`\`\`${language}
1419
- ${code}
1420
- \`\`\`
1421
1526
  `;
1422
1527
  }
1423
- var INDEX = {
1424
- lead: {
1425
- en: "Welcome to your Riebeckite site.",
1426
- ja: "あなたの Riebeckite サイトへようこそ。",
1427
- "zh-CN": "欢迎来到你的 Riebeckite 站点。",
1428
- es: "Bienvenido a tu sitio Riebeckite.",
1429
- de: "Willkommen auf deiner Riebeckite-Seite.",
1430
- fr: "Bienvenue sur votre site Riebeckite.",
1431
- ko: "Riebeckite 사이트에 오신 것을 환영합니다."
1432
- },
1433
- translatedNote: {
1434
- en: "Every page of this starter is available in seven languages. Switch with the selector below the page title.",
1435
- ja: "このスターターのすべてのページは 7 言語で利用できます。ページタイトル下のセレクターで切り替えられます。",
1436
- "zh-CN": "本模板的每个页面均提供七种语言版本,可用页面标题下方的选择器切换。",
1437
- es: "Cada página de este starter está disponible en siete idiomas. Cámbialos con el selector bajo el título.",
1438
- de: "Jede Seite dieses Starters ist in sieben Sprachen verfügbar. Wechsel mit dem Auswahlfeld unter dem Seitentitel.",
1439
- fr: "Chaque page de ce starter est disponible en sept langues. Changez avec le sélecteur sous le titre.",
1440
- ko: "이 스타터의 모든 페이지는 7개 언어로 제공됩니다. 페이지 제목 아래 선택기로 전환하세요."
1528
+ function article() {
1529
+ return `import type { PostContent } from "@riebeckite/core";
1530
+ import { Article, ArticleContent, ArticleLayout } from "@riebeckite/honox/ui";
1531
+
1532
+ export function SiteArticle({
1533
+ post,
1534
+ afterMeta,
1535
+ }: {
1536
+ post: PostContent;
1537
+ afterMeta?: string;
1538
+ }) {
1539
+ return (
1540
+ <Article class="site-article">
1541
+ <ArticleLayout>
1542
+ <ArticleContent>
1543
+ {afterMeta ? (
1544
+ <div
1545
+ class="site-article__after-meta"
1546
+ dangerouslySetInnerHTML={{ __html: afterMeta }}
1547
+ />
1548
+ ) : null}
1549
+ <div dangerouslySetInnerHTML={{ __html: post.html ?? "" }} />
1550
+ </ArticleContent>
1551
+ </ArticleLayout>
1552
+ </Article>
1553
+ );
1554
+ }
1555
+ `;
1556
+ }
1557
+ function renderer(preset) {
1558
+ if (!preset.theme) return bareRenderer();
1559
+ const hasHeader = preset.appFiles.includes("header");
1560
+ const hasColorMode = preset.plugins.some(
1561
+ (plugin) => plugin.package === "@riebeckite/plugin-color-mode"
1562
+ );
1563
+ return `import type { PluginHeadTag } from "@riebeckite/core";
1564
+ import { jsxRenderer } from "hono/jsx-renderer";
1565
+ import { Link, Script } from "honox/server";
1566
+ ${hasColorMode ? `import { ColorModeScript } from "@riebeckite/plugin-color-mode";
1567
+ ` : ""}${hasHeader ? `import { SiteHeader } from "../components/site-header";
1568
+ ` : ""}import { config } from "../config";
1569
+
1570
+ function themeAttributes() {
1571
+ const { theme } = config;
1572
+
1573
+ return {
1574
+ ...theme.attributes,
1575
+ "data-theme": theme.colorMode === "system" ? undefined : theme.colorMode,
1576
+ "data-theme-name": theme.name,
1577
+ "data-typography": theme.typography,
1578
+ "data-article-layout": theme.articleLayout,
1579
+ };
1580
+ }
1581
+
1582
+ export default jsxRenderer(({ children }, c) => {
1583
+ const headTags: readonly PluginHeadTag[] = c.get("headTags") ?? [];
1584
+
1585
+ return (
1586
+ <html
1587
+ lang={c.get("htmlLanguage") ?? config.site.locale}
1588
+ {...themeAttributes()}
1589
+ >
1590
+ <head>
1591
+ <meta charset="utf-8" />
1592
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1593
+ <title>{config.site.title}</title>
1594
+ ${hasColorMode ? ` <ColorModeScript />
1595
+ ` : ""} <Link href="/app/style.css" rel="stylesheet" />
1596
+ {headTags.map(renderHeadTag)}
1597
+ <Script src="/app/client.ts" async />
1598
+ </head>
1599
+ <body class="riebeckite-page">
1600
+ ${hasHeader ? ` <SiteHeader />
1601
+ ` : ""} {children}
1602
+ </body>
1603
+ </html>
1604
+ );
1605
+ });
1606
+
1607
+ function renderHeadTag(tag: PluginHeadTag, index: number) {
1608
+ const key = \`\${tag.tag}-\${index}\`;
1609
+ if (tag.tag === "meta") return <meta {...tag.attrs} key={key} />;
1610
+ if (tag.tag === "link") return <link {...tag.attrs} key={key} />;
1611
+ return (
1612
+ <script
1613
+ {...tag.attrs}
1614
+ key={key}
1615
+ dangerouslySetInnerHTML={
1616
+ tag.children ? { __html: tag.children } : undefined
1617
+ }
1618
+ />
1619
+ );
1620
+ }
1621
+ `;
1622
+ }
1623
+ function bareRenderer() {
1624
+ return `import { jsxRenderer } from "hono/jsx-renderer";
1625
+ import { Link, Script } from "honox/server";
1626
+ import { config } from "../config";
1627
+
1628
+ export default jsxRenderer(({ children }, c) => (
1629
+ <html lang={c.get("htmlLanguage") ?? config.site.locale}>
1630
+ <head>
1631
+ <meta charset="utf-8" />
1632
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1633
+ <title>{config.site.title}</title>
1634
+ <Link href="/app/style.css" rel="stylesheet" />
1635
+ <Script src="/app/client.ts" async />
1636
+ </head>
1637
+ <body class="riebeckite-page">{children}</body>
1638
+ </html>
1639
+ ));
1640
+ `;
1641
+ }
1642
+ function indexRoute(preset) {
1643
+ if (!preset.appFiles.includes("article")) return staticIndexRoute();
1644
+ return `import { isPublished } from "@riebeckite/core";
1645
+ import { createRoute } from "honox/factory";
1646
+ import { SiteArticle } from "../components/article";
1647
+ import { config } from "../config";
1648
+ import { content } from "../content";
1649
+
1650
+ export default createRoute(async (c) => {
1651
+ const manifest = await content.getManifest();
1652
+ const indexEntry = manifest.bySlug.get("index");
1653
+ if (indexEntry && indexEntry.permalink !== "/") {
1654
+ return c.redirect(indexEntry.permalink, 308);
1655
+ }
1656
+
1657
+ const post = await content.getProcessedContent("index");
1658
+ if (!isPublished(config, post.frontmatter)) {
1659
+ return c.notFound();
1660
+ }
1661
+
1662
+ if (indexEntry) {
1663
+ c.set("htmlLanguage", indexEntry.publicLocation.metadata?.["l10n.lang"]);
1664
+ c.set("headTags", indexEntry.headTags ?? []);
1665
+ }
1666
+
1667
+ return c.render(
1668
+ <SiteArticle
1669
+ post={post}
1670
+ afterMeta={indexEntry?.bodySlots?.["article.after-meta"]}
1671
+ />,
1672
+ );
1673
+ });
1674
+ `;
1675
+ }
1676
+ function staticIndexRoute() {
1677
+ return `import { createRoute } from "honox/factory";
1678
+ import { config } from "../config";
1679
+
1680
+ export default createRoute((c) =>
1681
+ c.render(
1682
+ <main class="riebeckite-empty">
1683
+ <h1>{config.site.title}</h1>
1684
+ <p>This is a blank Riebeckite site. Add Markdown files under content/ to get started.</p>
1685
+ </main>,
1686
+ ),
1687
+ );
1688
+ `;
1689
+ }
1690
+ function slugRoute() {
1691
+ return `import { isPublished } from "@riebeckite/core";
1692
+ import {
1693
+ contentRouteSsgParams,
1694
+ resolveContentRoute,
1695
+ } from "@riebeckite/honox/server";
1696
+ import { createRoute } from "honox/factory";
1697
+ import { SiteArticle } from "../components/article";
1698
+ import { config } from "../config";
1699
+ import { content } from "../content";
1700
+
1701
+ export default createRoute(
1702
+ contentRouteSsgParams("/:slug{.+}", async () => {
1703
+ const manifest = await content.getManifest();
1704
+ return manifest.entries
1705
+ .filter((entry) => isPublished(config, entry.frontmatter))
1706
+ .filter((entry) => entry.permalink !== "/")
1707
+ .map((entry) => ({ slug: entry.permalink.replace(/^\\/+/, "") }));
1708
+ }),
1709
+ async (c) => {
1710
+ const requestedSlug = c.req.param("slug");
1711
+ if (!requestedSlug) return c.notFound();
1712
+ if (/\\.[a-zA-Z0-9]+$/.test(requestedSlug)) return c.notFound();
1713
+
1714
+ const manifest = await content.getManifest();
1715
+ const route = resolveContentRoute(manifest, c.req.path);
1716
+ if (!route) return c.notFound();
1717
+ if (route.kind === "redirect")
1718
+ return c.redirect(route.location, route.status);
1719
+
1720
+ const post = await content.getProcessedContent(route.entry.slug);
1721
+ if (!isPublished(config, post.frontmatter)) {
1722
+ return c.notFound();
1723
+ }
1724
+
1725
+ c.set("htmlLanguage", route.entry.publicLocation.metadata?.["l10n.lang"]);
1726
+ c.set("headTags", route.entry.headTags ?? []);
1727
+
1728
+ return c.render(
1729
+ <SiteArticle
1730
+ post={post}
1731
+ afterMeta={route.entry.bodySlots?.["article.after-meta"]}
1732
+ />,
1733
+ );
1441
1734
  },
1442
- whatHeading: {
1443
- en: "What is Riebeckite?",
1444
- ja: "Riebeckite とは",
1445
- "zh-CN": "什么是 Riebeckite?",
1446
- es: "¿Qué es Riebeckite?",
1447
- de: "Was ist Riebeckite?",
1448
- fr: "Qu'est-ce que Riebeckite ?",
1449
- ko: "Riebeckite란?"
1735
+ );
1736
+ `;
1737
+ }
1738
+
1739
+ // src/scaffold/localized-content.ts
1740
+ var HELPERS = {
1741
+ en: "https://github.com/Rerurate514/riebeckite/blob/main/docs/en/README.md",
1742
+ ja: "https://github.com/Rerurate514/riebeckite/blob/main/docs/ja/README.md"
1743
+ };
1744
+ var PLUGIN_INDEX_URL = "https://github.com/Rerurate514/riebeckite/tree/main/packages/plugins";
1745
+ var THEMES_INDEX_URL = "https://github.com/Rerurate514/riebeckite/tree/main/packages/themes";
1746
+ var GALLERY_PACKAGE = "@riebeckite/plugin-gallery";
1747
+ var PLUGIN_MARKDOWN_GUIDES = {
1748
+ "@riebeckite/plugin-color-mode": {
1749
+ summary: "Adds the light/dark/system color-mode control to the app shell. Markdown does not need special syntax; every page inherits the toggle.",
1750
+ markdown: "Write any page normally. Use the color-mode control in the header to see the same Markdown under light and dark tokens."
1450
1751
  },
1451
- whatBody: {
1452
- en: "Riebeckite is an extensible, content-first framework that builds fast static sites from plain Markdown — the same notes you keep in Obsidian. The ecosystem includes 50+ plugins and six themes, and this site demos both.",
1453
- ja: "Riebeckite は、プレーンな Markdown(Obsidian で管理しているのと同じノート)から高速な静的サイトを生成する、拡張性のあるコンテンツファーストのフレームワークです。エコシステムには 50 以上のプラグインと 6 つのテーマがあり、このサイトはその両方をデモしています。",
1454
- "zh-CN": "Riebeckite 是一个可扩展、内容优先的框架,可从纯 Markdown(即你在 Obsidian 中保存的笔记)构建快速的静态站点。生态包含 50+ 插件与六个主题,本站点同时演示两者。",
1455
- es: "Riebeckite es un framework extensible y orientado al contenido que construye sitios estáticos rápidos desde Markdown simple — las mismas notas que guardas en Obsidian. El ecosistema incluye más de 50 plugins y seis temas; este sitio demuestra ambos.",
1456
- de: "Riebeckite ist ein erweiterbares, inhaltsorientiertes Framework, das schnelle statische Seiten aus einfachem Markdown baut — denselben Notizen, die du in Obsidian führst. Das Ökosystem umfasst 50+ Plugins und sechs Themes; diese Seite demonstriert beides.",
1457
- fr: "Riebeckite est un framework extensible et centré sur le contenu qui construit des sites statiques rapides à partir de Markdown simple — les mêmes notes que vous gardez dans Obsidian. L'écosystème inclut plus de 50 plugins et six thèmes ; ce site en démontre les deux.",
1458
- ko: "Riebeckite는 일반 Markdown(Obsidian에서 관리하는 노트)에서 빠른 정적 사이트를 만드는 확장 가능한 콘텐츠 우선 프레임워크입니다. 생태계에는 50개 이상의 플러그인과 6개 테마가 있으며, 이 사이트가 그 두 가지를 보여줍니다."
1752
+ "@riebeckite/plugin-l10n": {
1753
+ summary: "Routes translated Markdown files by language suffix and keeps the default-language file unsuffixed.",
1754
+ markdown: fence(
1755
+ "text",
1756
+ [
1757
+ "content/about.md # default language",
1758
+ "content/about.ja.md # Japanese version served under /ja/about/",
1759
+ "content/about.fr.md # French version served under /fr/about/"
1760
+ ].join("\n")
1761
+ )
1459
1762
  },
1460
- exploreHeading: {
1461
- en: "Explore",
1462
- ja: "さらに深く",
1463
- "zh-CN": "继续探索",
1464
- es: "Explorar",
1465
- de: "Weiter erkunden",
1466
- fr: "Explorer",
1467
- ko: "더 살펴보기"
1763
+ "@riebeckite/plugin-seo": {
1764
+ summary: "Turns Markdown frontmatter and site config into metadata, feeds, sitemap, and robots output.",
1765
+ markdown: [
1766
+ "---",
1767
+ "title: Search-friendly title",
1768
+ "description: Page-specific summary used for meta tags and cards.",
1769
+ "image: /ogp/page.png",
1770
+ "date: 2026-09-30",
1771
+ "publish: true",
1772
+ "---",
1773
+ "",
1774
+ "# Search-friendly title"
1775
+ ].join("\n")
1468
1776
  },
1469
- explorePlugins: {
1470
- en: "Plugins — representative packages grouped by capability",
1471
- ja: "プラグイン — 機能別の代表的なパッケージ",
1472
- "zh-CN": "插件 — 按能力分组的代表性包",
1777
+ "@riebeckite/plugin-toc": {
1778
+ summary: "Builds a table of contents from Markdown headings.",
1779
+ markdown: [
1780
+ "# Page",
1781
+ "",
1782
+ "## First section",
1783
+ "",
1784
+ "### Nested section",
1785
+ "",
1786
+ "## Second section"
1787
+ ].join("\n")
1788
+ },
1789
+ "@riebeckite/plugin-properties": {
1790
+ summary: 'Renders selected frontmatter properties near the article when configured with `render: "slot"`.',
1791
+ markdown: [
1792
+ "---",
1793
+ "created: 2026-09-30",
1794
+ "modified: 2026-09-30",
1795
+ "tags: [riebeckite, demo]",
1796
+ "status: active",
1797
+ "---"
1798
+ ].join("\n")
1799
+ },
1800
+ "@riebeckite/plugin-alias": {
1801
+ summary: "Creates redirects from old Markdown paths declared in frontmatter.",
1802
+ markdown: [
1803
+ "---",
1804
+ "aliases:",
1805
+ " - /old-page/",
1806
+ " - /notes/previous-name/",
1807
+ "---"
1808
+ ].join("\n")
1809
+ },
1810
+ "@riebeckite/plugin-search": {
1811
+ summary: "Indexes published Markdown pages for client-side search. No special syntax is required; searchable text comes from each page body and metadata.",
1812
+ markdown: "# Indexed page\n\nThis body text becomes searchable after the site is built."
1813
+ },
1814
+ "@riebeckite/plugin-backlinks": {
1815
+ summary: "Shows pages that link to the current page. Use normal Markdown links or Obsidian wikilinks.",
1816
+ markdown: "Mention another page with [[guide]] or [the guide](/guide/). The target page can show this page as a backlink."
1817
+ },
1818
+ "@riebeckite/plugin-related-posts": {
1819
+ summary: "Finds related pages from tags and backlink relationships.",
1820
+ markdown: [
1821
+ "---",
1822
+ "tags: [typescript, static-site]",
1823
+ "---",
1824
+ "",
1825
+ "Link to [[another-note]] to strengthen the relationship."
1826
+ ].join("\n")
1827
+ },
1828
+ "@riebeckite/plugin-share": {
1829
+ summary: "Adds share links to rendered pages. Markdown does not need special syntax; page title and URL are used automatically.",
1830
+ markdown: [
1831
+ "---",
1832
+ "title: Shareable article",
1833
+ "---",
1834
+ "",
1835
+ "# Shareable article"
1836
+ ].join("\n")
1837
+ },
1838
+ "@riebeckite/plugin-changelog": {
1839
+ summary: "Surfaces recent or per-note update information from Markdown metadata.",
1840
+ markdown: [
1841
+ "---",
1842
+ "title: Updated article",
1843
+ "modified: 2026-09-30",
1844
+ "---"
1845
+ ].join("\n")
1846
+ },
1847
+ "@riebeckite/plugin-webmention": {
1848
+ summary: "Displays webmentions for the page URL when data is available. Markdown controls the canonical page metadata.",
1849
+ markdown: [
1850
+ "---",
1851
+ "title: Mentionable page",
1852
+ "canonical: https://example.com/mentionable-page/",
1853
+ "---"
1854
+ ].join("\n")
1855
+ },
1856
+ "@riebeckite/plugin-recent-posts": {
1857
+ summary: "Builds recent-post lists from dated Markdown entries.",
1858
+ markdown: [
1859
+ "---",
1860
+ "title: New post",
1861
+ "date: 2026-09-30",
1862
+ "publish: true",
1863
+ "---"
1864
+ ].join("\n")
1865
+ },
1866
+ "@riebeckite/plugin-attachment": {
1867
+ summary: "Enhances links to downloadable assets, including size display when enabled.",
1868
+ markdown: "Download the handout: [slides.pdf](/attachments/slides.pdf)"
1869
+ },
1870
+ "@riebeckite/plugin-pdf": {
1871
+ summary: "Embeds linked PDFs with a viewer/fallback UI.",
1872
+ markdown: "![Project brief](./attachments/project-brief.pdf)"
1873
+ },
1874
+ "@riebeckite/plugin-media": {
1875
+ summary: "Enhances Markdown audio/video embeds with lazy loading and captions.",
1876
+ markdown: "![Demo video](./media/demo.mp4)\n\n![Episode audio](./media/episode.mp3)"
1877
+ },
1878
+ "@riebeckite/plugin-responsive-image": {
1879
+ summary: "Rewrites Markdown image embeds into responsive images with configured widths and formats.",
1880
+ markdown: "![A responsive landscape](./images/landscape.jpg)"
1881
+ },
1882
+ "@riebeckite/plugin-lightbox": {
1883
+ summary: "Makes image links open in a lightbox when the configured trigger class is present.",
1884
+ markdown: "[![Open in lightbox](./images/photo.jpg)](./images/photo.jpg){.rr-lightbox-trigger}"
1885
+ },
1886
+ "@riebeckite/plugin-highlight": {
1887
+ summary: "Highlights marked inline text.",
1888
+ markdown: "This sentence contains ==highlighted text== inside normal Markdown."
1889
+ },
1890
+ "@riebeckite/plugin-code-annotations": {
1891
+ summary: "Adds callouts/annotations to code fences.",
1892
+ markdown: [
1893
+ "```ts",
1894
+ "const answer = 42 // [!code focus]",
1895
+ "console.log(answer)",
1896
+ "```"
1897
+ ].join("\n")
1898
+ },
1899
+ "@riebeckite/plugin-shortcodes": {
1900
+ summary: "Expands built-in shortcode syntax inside Markdown.",
1901
+ markdown: '{{< youtube dQw4w9WgXcQ >}}\n\n{{< figure src="/images/demo.png" caption="Demo image" >}}'
1902
+ },
1903
+ "@riebeckite/plugin-series": {
1904
+ summary: "Groups Markdown pages into a reading series using frontmatter.",
1905
+ markdown: [
1906
+ "---",
1907
+ "series: riebeckite-guide",
1908
+ "series_title: Riebeckite Guide",
1909
+ "series_order: 2",
1910
+ "---"
1911
+ ].join("\n")
1912
+ },
1913
+ "@riebeckite/plugin-taxonomy": {
1914
+ summary: "Builds tag/folder taxonomy pages from Markdown metadata and content location.",
1915
+ markdown: ["---", "tags: [design, notes]", "---", "", "# Tagged note"].join(
1916
+ "\n"
1917
+ )
1918
+ },
1919
+ "@riebeckite/plugin-autocardlink": {
1920
+ summary: "Turns suitable standalone links into rich card links.",
1921
+ markdown: "https://example.com/articles/riebeckite-introduction"
1922
+ },
1923
+ "@riebeckite/plugin-plantuml": {
1924
+ summary: "A `plantuml` fence is rendered to SVG through the configured PlantUML server.",
1925
+ markdown: fence(
1926
+ "plantuml",
1927
+ ["@startuml", "Alice -> Bob: Hello", "Bob --> Alice: Hi", "@enduml"].join(
1928
+ "\n"
1929
+ )
1930
+ )
1931
+ },
1932
+ "@riebeckite/plugin-discord-embed": {
1933
+ summary: "Renders Discord-style rich embed metadata from frontmatter or embed blocks.",
1934
+ markdown: [
1935
+ "---",
1936
+ "title: Discord preview",
1937
+ "description: A page with rich metadata for card rendering.",
1938
+ "image: /images/card.png",
1939
+ "---"
1940
+ ].join("\n")
1941
+ },
1942
+ "@riebeckite/plugin-excalidraw": {
1943
+ summary: "Embeds Excalidraw drawings referenced from Markdown.",
1944
+ markdown: "![[Architecture.excalidraw]]\n\n![Sketch](./drawings/sketch.excalidraw)"
1945
+ },
1946
+ "@riebeckite/plugin-excalibrain": {
1947
+ summary: "Builds an Excalibrain-style local graph from links, tags, headings, and inferred relationships.",
1948
+ markdown: "# Concept\n\nLinks to [[Parent idea]], [[Sibling idea]], and [[Related idea]] become graph relationships."
1949
+ },
1950
+ "@riebeckite/plugin-canvas": {
1951
+ summary: "Embeds Obsidian Canvas JSON as a rendered canvas/fallback.",
1952
+ markdown: '```canvas\n{ "nodes": [], "edges": [] }\n```'
1953
+ },
1954
+ "@riebeckite/plugin-flashcards": {
1955
+ summary: "Turns Q/A style Markdown into flashcards.",
1956
+ markdown: [
1957
+ "# Flashcards",
1958
+ "",
1959
+ "Q: What is Riebeckite?",
1960
+ "A: A content-first static site framework.",
1961
+ "",
1962
+ "---",
1963
+ "",
1964
+ "What does SSG mean?::Static Site Generation"
1965
+ ].join("\n")
1966
+ },
1967
+ "@riebeckite/plugin-local-graph": {
1968
+ summary: "Shows nearby notes based on Markdown links and wikilinks.",
1969
+ markdown: "# Local graph source\n\nConnect this page to [[guide]], [[examples]], and [[reference/plugins]]."
1970
+ },
1971
+ "@riebeckite/plugin-hover-preview": {
1972
+ summary: "Shows previews when hovering internal links generated from Markdown.",
1973
+ markdown: "Hover this internal link: [Guide](/guide/) or this wikilink: [[guide]]."
1974
+ },
1975
+ "@riebeckite/plugin-garden-explorer": {
1976
+ summary: "Adds garden navigation/explorer UI from the content tree. Markdown files and folders become the source data.",
1977
+ markdown: fence(
1978
+ "text",
1979
+ [
1980
+ "content/",
1981
+ " notes/",
1982
+ " ideas.md",
1983
+ " projects.md",
1984
+ " reference/",
1985
+ " plugins.md"
1986
+ ].join("\n")
1987
+ )
1988
+ },
1989
+ "@riebeckite/plugin-ux": {
1990
+ summary: "Adds reading UX such as progress, back-to-top, scroll spy, and copy buttons. Markdown headings and code blocks provide the anchors.",
1991
+ markdown: [
1992
+ "## Long section",
1993
+ "",
1994
+ "```ts",
1995
+ "console.log('copy me')",
1996
+ "```"
1997
+ ].join("\n")
1998
+ },
1999
+ "@riebeckite/plugin-daily-notes": {
2000
+ summary: "Extracts daily-note summaries/snippets from dated Markdown files.",
2001
+ markdown: [
2002
+ "---",
2003
+ "daily-summary: Shipped the plugin reference page.",
2004
+ "---",
2005
+ "",
2006
+ "```daily-snippet",
2007
+ "Fixed the ultra preset examples.",
2008
+ "```"
2009
+ ].join("\n")
2010
+ },
2011
+ "@riebeckite/plugin-rename": {
2012
+ summary: "Creates redirects when Markdown entries declare previous paths or when rename data is available.",
2013
+ markdown: [
2014
+ "---",
2015
+ "previousPaths:",
2016
+ " - /old-slug/",
2017
+ " - /notes/old-title/",
2018
+ "---"
2019
+ ].join("\n")
2020
+ },
2021
+ "@riebeckite/plugin-text-fragment": {
2022
+ summary: "Adds text-fragment friendly copy/open behavior for selected Markdown text.",
2023
+ markdown: "Select this sentence in the rendered page and copy a text-fragment link to it."
2024
+ },
2025
+ "@riebeckite/plugin-quality": {
2026
+ summary: "Reports accessibility/content quality diagnostics from rendered Markdown output.",
2027
+ markdown: "![Missing alt text example](./images/needs-alt.png)\n\nUse headings in order: #, then ##, then ###."
2028
+ },
2029
+ "@riebeckite/plugin-deploy": {
2030
+ summary: "Adds deployment integration for the generated site. Markdown does not need syntax; published pages become deployable output.",
2031
+ markdown: [
2032
+ "---",
2033
+ "publish: true",
2034
+ "---",
2035
+ "",
2036
+ "# This page is included in the deployed site"
2037
+ ].join("\n")
2038
+ },
2039
+ "@riebeckite/plugin-diagnostics": {
2040
+ summary: "Reports diagnostics such as missing required frontmatter, orphan pages, and unused assets.",
2041
+ markdown: [
2042
+ "---",
2043
+ "title: Required title",
2044
+ "publish: true",
2045
+ "---",
2046
+ "",
2047
+ "Link orphan pages from another note to clear orphan diagnostics."
2048
+ ].join("\n")
2049
+ }
2050
+ };
2051
+ function pluginReadmeUrl(slug) {
2052
+ return `https://github.com/Rerurate514/riebeckite/blob/main/packages/plugins/${slug}/README.md`;
2053
+ }
2054
+ function themeReadmeUrl(slug) {
2055
+ return `https://github.com/Rerurate514/riebeckite/blob/main/packages/themes/${slug}/README.md`;
2056
+ }
2057
+ function localizedContentFiles(variables, preset) {
2058
+ const defaultLang = defaultLanguageForLocale(variables.locale);
2059
+ const files = [];
2060
+ const localized = preset.languages.length > 1;
2061
+ const pushLocalized = (basePath, build2) => {
2062
+ if (!localized) {
2063
+ files.push({
2064
+ path: `content/${basePath}.md`,
2065
+ content: build2(defaultLang)
2066
+ });
2067
+ return;
2068
+ }
2069
+ for (const language of SCAFFOLD_LANGUAGES) {
2070
+ const suffix = language === defaultLang ? "" : `.${language}`;
2071
+ files.push({
2072
+ path: `content/${basePath}${suffix}.md`,
2073
+ content: build2(language)
2074
+ });
2075
+ }
2076
+ };
2077
+ const pushEnglishOnly = (basePath, build2) => {
2078
+ const suffix = defaultLang === "en" ? "" : ".en";
2079
+ files.push({
2080
+ path: `content/${basePath}${suffix}.md`,
2081
+ content: build2()
2082
+ });
2083
+ };
2084
+ for (const page of preset.contentPages) {
2085
+ if (page === "index") {
2086
+ pushLocalized("index", (language) => indexContent(variables, language));
2087
+ } else if (page === "framework/plugins") {
2088
+ pushLocalized(
2089
+ "framework/plugins",
2090
+ (language) => pluginsContent(language)
2091
+ );
2092
+ } else if (page === "framework/themes") {
2093
+ pushLocalized(
2094
+ "framework/themes",
2095
+ (language) => themesContent(language, preset)
2096
+ );
2097
+ } else if (page === "guide") {
2098
+ pushEnglishOnly("guide", guideContent);
2099
+ } else if (page === "examples") {
2100
+ pushLocalized(
2101
+ "examples",
2102
+ (language) => examplesContent(preset, language)
2103
+ );
2104
+ } else if (page === "reference/plugins") {
2105
+ pushEnglishOnly(
2106
+ "reference/plugins",
2107
+ () => referencePluginsContent(preset, variables)
2108
+ );
2109
+ } else if (page === "reference/themes") {
2110
+ pushEnglishOnly("reference/themes", () => referenceThemesContent(preset));
2111
+ }
2112
+ }
2113
+ return files;
2114
+ }
2115
+ function read(text, language) {
2116
+ return text[language];
2117
+ }
2118
+ function frontmatter() {
2119
+ return ["---", "publish: true", "---", ""].join("\n");
2120
+ }
2121
+ function heading(level, text) {
2122
+ return `${"#".repeat(level)} ${text}`;
2123
+ }
2124
+ function codeBlock(language, code) {
2125
+ return `\`\`\`${language}
2126
+ ${code}
2127
+ \`\`\`
2128
+ `;
2129
+ }
2130
+ var INDEX = {
2131
+ lead: {
2132
+ en: "Welcome to your Riebeckite site.",
2133
+ ja: "あなたの Riebeckite サイトへようこそ。",
2134
+ "zh-CN": "欢迎来到你的 Riebeckite 站点。",
2135
+ es: "Bienvenido a tu sitio Riebeckite.",
2136
+ de: "Willkommen auf deiner Riebeckite-Seite.",
2137
+ fr: "Bienvenue sur votre site Riebeckite.",
2138
+ ko: "Riebeckite 사이트에 오신 것을 환영합니다."
2139
+ },
2140
+ translatedNote: {
2141
+ en: "Every page of this starter is available in seven languages. Switch with the selector below the page title.",
2142
+ ja: "このスターターのすべてのページは 7 言語で利用できます。ページタイトル下のセレクターで切り替えられます。",
2143
+ "zh-CN": "本模板的每个页面均提供七种语言版本,可用页面标题下方的选择器切换。",
2144
+ es: "Cada página de este starter está disponible en siete idiomas. Cámbialos con el selector bajo el título.",
2145
+ de: "Jede Seite dieses Starters ist in sieben Sprachen verfügbar. Wechsel mit dem Auswahlfeld unter dem Seitentitel.",
2146
+ fr: "Chaque page de ce starter est disponible en sept langues. Changez avec le sélecteur sous le titre.",
2147
+ ko: "이 스타터의 모든 페이지는 7개 언어로 제공됩니다. 페이지 제목 아래 선택기로 전환하세요."
2148
+ },
2149
+ whatHeading: {
2150
+ en: "What is Riebeckite?",
2151
+ ja: "Riebeckite とは",
2152
+ "zh-CN": "什么是 Riebeckite?",
2153
+ es: "¿Qué es Riebeckite?",
2154
+ de: "Was ist Riebeckite?",
2155
+ fr: "Qu'est-ce que Riebeckite ?",
2156
+ ko: "Riebeckite란?"
2157
+ },
2158
+ whatBody: {
2159
+ en: "Riebeckite is an extensible, content-first framework that builds fast static sites from plain Markdown — the same notes you keep in Obsidian. The ecosystem includes 50+ plugins and six themes, and this site demos both.",
2160
+ ja: "Riebeckite は、プレーンな Markdown(Obsidian で管理しているのと同じノート)から高速な静的サイトを生成する、拡張性のあるコンテンツファーストのフレームワークです。エコシステムには 50 以上のプラグインと 6 つのテーマがあり、このサイトはその両方をデモしています。",
2161
+ "zh-CN": "Riebeckite 是一个可扩展、内容优先的框架,可从纯 Markdown(即你在 Obsidian 中保存的笔记)构建快速的静态站点。生态包含 50+ 插件与六个主题,本站点同时演示两者。",
2162
+ es: "Riebeckite es un framework extensible y orientado al contenido que construye sitios estáticos rápidos desde Markdown simple — las mismas notas que guardas en Obsidian. El ecosistema incluye más de 50 plugins y seis temas; este sitio demuestra ambos.",
2163
+ de: "Riebeckite ist ein erweiterbares, inhaltsorientiertes Framework, das schnelle statische Seiten aus einfachem Markdown baut — denselben Notizen, die du in Obsidian führst. Das Ökosystem umfasst 50+ Plugins und sechs Themes; diese Seite demonstriert beides.",
2164
+ fr: "Riebeckite est un framework extensible et centré sur le contenu qui construit des sites statiques rapides à partir de Markdown simple — les mêmes notes que vous gardez dans Obsidian. L'écosystème inclut plus de 50 plugins et six thèmes ; ce site en démontre les deux.",
2165
+ ko: "Riebeckite는 일반 Markdown(Obsidian에서 관리하는 노트)에서 빠른 정적 사이트를 만드는 확장 가능한 콘텐츠 우선 프레임워크입니다. 생태계에는 50개 이상의 플러그인과 6개 테마가 있으며, 이 사이트가 그 두 가지를 보여줍니다."
2166
+ },
2167
+ exploreHeading: {
2168
+ en: "Explore",
2169
+ ja: "さらに深く",
2170
+ "zh-CN": "继续探索",
2171
+ es: "Explorar",
2172
+ de: "Weiter erkunden",
2173
+ fr: "Explorer",
2174
+ ko: "더 살펴보기"
2175
+ },
2176
+ explorePlugins: {
2177
+ en: "Plugins — representative packages grouped by capability",
2178
+ ja: "プラグイン — 機能別の代表的なパッケージ",
2179
+ "zh-CN": "插件 — 按能力分组的代表性包",
1473
2180
  es: "Plugins — paquetes representativos por capacidad",
1474
2181
  de: "Plugins — repräsentative Pakete nach Fähigkeit",
1475
2182
  fr: "Plugins — paquets représentatifs par capacité",
@@ -1549,6 +2256,15 @@ var THEMES_COPY = {
1549
2256
  fr: "Un thème change tout l'aspect d'un site — couleurs, typographie, mise en page — sans toucher au contenu ni aux routes. Riebeckite fournit six thèmes ; installez un paquet et changez une ligne.",
1550
2257
  ko: "테마를 바꾸면 색상·타이포그래피·레이아웃 등 사이트 전체 분위기가 달라집니다. 콘텐츠나 라우트는 건드릴 필요가 없습니다. Riebeckite는 6개 테마를 제공하며 패키지 설치 후 한 줄만 바꾸면 전환됩니다."
1551
2258
  },
2259
+ galleryHeading: {
2260
+ en: "Theme gallery",
2261
+ ja: "テーマギャラリー",
2262
+ "zh-CN": "主题画廊",
2263
+ es: "Galería de temas",
2264
+ de: "Theme-Galerie",
2265
+ fr: "Galerie des thèmes",
2266
+ ko: "테마 갤러리"
2267
+ },
1552
2268
  switchHeading: {
1553
2269
  en: "Switching themes",
1554
2270
  ja: "テーマの切り替え",
@@ -1702,11 +2418,37 @@ var THEMES = [
1702
2418
  }
1703
2419
  }
1704
2420
  ];
1705
- function themesContent(language) {
2421
+ var THEME_LABELS = {
2422
+ default: "Default",
2423
+ minimal: "Minimal",
2424
+ sakura: "Sakura",
2425
+ gruvbox: "Gruvbox",
2426
+ tokyonight: "Tokyo Night",
2427
+ rerurate: "Rerurate"
2428
+ };
2429
+ function themeGalleryFence(description) {
2430
+ const items = ["columns: 3", "items:"];
2431
+ for (const theme of THEMES) {
2432
+ const label = THEME_LABELS[theme.slug] ?? theme.slug;
2433
+ items.push(` - title: ${JSON.stringify(label)}`);
2434
+ items.push(` description: ${JSON.stringify(description(theme))}`);
2435
+ items.push(` meta: ${JSON.stringify(theme.factory)}`);
2436
+ items.push(` href: ${JSON.stringify(themeReadmeUrl(theme.slug))}`);
2437
+ }
2438
+ return fence("gallery", items.join("\n"));
2439
+ }
2440
+ function themesContent(language, preset) {
1706
2441
  const copy = THEMES_COPY;
1707
2442
  const lines = [frontmatter()];
1708
2443
  lines.push(heading(1, read(copy.title, language)), "");
1709
2444
  lines.push(read(copy.intro, language), "");
2445
+ if (hasPlugin(preset, GALLERY_PACKAGE)) {
2446
+ lines.push(heading(2, read(copy.galleryHeading, language)), "");
2447
+ lines.push(
2448
+ themeGalleryFence((theme) => read(theme.desc, language)),
2449
+ ""
2450
+ );
2451
+ }
1710
2452
  lines.push(heading(2, read(copy.switchHeading, language)), "");
1711
2453
  lines.push(read(copy.switchCurrent, language), "");
1712
2454
  lines.push(read(copy.switchStep1, language), "");
@@ -2326,9 +3068,9 @@ function guideContent() {
2326
3068
  codeBlock(
2327
3069
  "sh",
2328
3070
  [
2329
- "npm install",
2330
- "npm exec riebeckite dev",
2331
- "npm exec riebeckite build"
3071
+ "pnpm install",
3072
+ "pnpm exec riebeckite dev",
3073
+ "pnpm exec riebeckite build"
2332
3074
  ].join("\n")
2333
3075
  ),
2334
3076
  "",
@@ -2397,11 +3139,47 @@ function referencePluginsContent(preset, variables) {
2397
3139
  );
2398
3140
  }
2399
3141
  lines.push("");
3142
+ lines.push(heading(2, "Markdown behavior"), "");
3143
+ lines.push(
3144
+ "Every registered plugin is listed below with the Markdown that triggers it,",
3145
+ "or with the Markdown/frontmatter/content shape it reads when the plugin is",
3146
+ "site-wide rather than block-based. Paste the examples into files under",
3147
+ "`content/` and run `pnpm exec riebeckite dev` to inspect the rendered result.",
3148
+ ""
3149
+ );
3150
+ for (const plugin of preset.plugins) {
3151
+ const demoKeys = README_DEMO_ORDER.filter(
3152
+ (key) => demoPackage(key) === plugin.package
3153
+ );
3154
+ const guide = PLUGIN_MARKDOWN_GUIDES[plugin.package];
3155
+ const slug = plugin.package.replace(/^@riebeckite\/plugin-/, "");
3156
+ lines.push(heading(3, plugin.package), "");
3157
+ lines.push(
3158
+ `Factory: \`${plugin.factory}\` · [README](${pluginReadmeUrl(slug)})`,
3159
+ ""
3160
+ );
3161
+ if (guide) {
3162
+ lines.push(guide.summary, "");
3163
+ lines.push(guide.markdown, "");
3164
+ }
3165
+ for (const key of demoKeys) {
3166
+ const demo = README_DEMOS[key];
3167
+ lines.push(heading(4, demo.title.en), "");
3168
+ lines.push(demo.intro.en, "");
3169
+ lines.push(demo.markdown, "");
3170
+ }
3171
+ if (!guide && demoKeys.length === 0) {
3172
+ lines.push(
3173
+ "This plugin has no Markdown-specific demo in the scaffold yet. See its package README for the exact behavior and options.",
3174
+ ""
3175
+ );
3176
+ }
3177
+ }
2400
3178
  lines.push(`[Riebeckite plugins on GitHub](${PLUGIN_INDEX_URL})`, "");
2401
3179
  lines.push("");
2402
3180
  return lines.join("\n");
2403
3181
  }
2404
- function referenceThemesContent() {
3182
+ function referenceThemesContent(preset) {
2405
3183
  const lines = [frontmatter()];
2406
3184
  lines.push(heading(1, "Theme reference"), "");
2407
3185
  lines.push(
@@ -2409,6 +3187,13 @@ function referenceThemesContent() {
2409
3187
  "`theme` factory in `riebeckite.config.ts`.",
2410
3188
  ""
2411
3189
  );
3190
+ if (hasPlugin(preset, GALLERY_PACKAGE)) {
3191
+ lines.push(heading(2, "Theme gallery"), "");
3192
+ lines.push(
3193
+ themeGalleryFence((theme) => theme.desc.en),
3194
+ ""
3195
+ );
3196
+ }
2412
3197
  lines.push("| Theme | Factory | Description |");
2413
3198
  lines.push("| --- | --- | --- |");
2414
3199
  for (const theme of THEMES) {
@@ -2424,7 +3209,7 @@ function referenceThemesContent() {
2424
3209
  }
2425
3210
 
2426
3211
  // src/scaffold/version.ts
2427
- var RIEBECKITE_VERSION = "^0.0.8";
3212
+ var RIEBECKITE_VERSION = "^0.0.10";
2428
3213
 
2429
3214
  // src/scaffold/templates.ts
2430
3215
  function siteTemplateFiles(preset, variables) {
@@ -2738,7 +3523,7 @@ var README_WORDS = {
2738
3523
  demoPages: "Try the demos",
2739
3524
  demoPagesBody: "The generated site ships content pages that exercise these features:",
2740
3525
  copyDemos: "Copy-paste demos",
2741
- copyDemosBody: "Paste any of these snippets into a Markdown file under `content/` and run `npm exec riebeckite dev`. Each one renders through a plugin this preset registers."
3526
+ copyDemosBody: "Paste any of these snippets into a Markdown file under `content/` and run `pnpm exec riebeckite dev`. Each one renders through a plugin this preset registers."
2742
3527
  },
2743
3528
  ja: {
2744
3529
  included: "含まれているもの",
@@ -2762,7 +3547,7 @@ var README_WORDS = {
2762
3547
  demoPages: "デモを試す",
2763
3548
  demoPagesBody: "生成されたサイトには、各機能を体験できるコンテンツページが含まれています:",
2764
3549
  copyDemos: "コピーして使えるデモ",
2765
- copyDemosBody: "以下のスニペットを `content/` 配下の Markdown ファイルに貼り付けて `npm exec riebeckite dev` を実行してください。それぞれ、このプリセットが登録しているプラグインでレンダリングされます。"
3550
+ copyDemosBody: "以下のスニペットを `content/` 配下の Markdown ファイルに貼り付けて `pnpm exec riebeckite dev` を実行してください。それぞれ、このプリセットが登録しているプラグインでレンダリングされます。"
2766
3551
  }
2767
3552
  };
2768
3553
  function readme(preset, variables) {
@@ -2795,10 +3580,10 @@ function readmeEn(preset, variables) {
2795
3580
  lines.push(`## ${words.commands}`, "");
2796
3581
  lines.push(
2797
3582
  "```sh",
2798
- "npm install",
2799
- "npm exec riebeckite check",
2800
- "npm exec riebeckite dev",
2801
- "npm exec riebeckite build",
3583
+ "pnpm install",
3584
+ "pnpm exec riebeckite check",
3585
+ "pnpm exec riebeckite dev",
3586
+ "pnpm exec riebeckite build",
2802
3587
  "```",
2803
3588
  ""
2804
3589
  );
@@ -2843,10 +3628,10 @@ function readmeJa(preset, variables) {
2843
3628
  lines.push(`## ${words.commands}`, "");
2844
3629
  lines.push(
2845
3630
  "```sh",
2846
- "npm install",
2847
- "npm exec riebeckite check",
2848
- "npm exec riebeckite dev",
2849
- "npm exec riebeckite build",
3631
+ "pnpm install",
3632
+ "pnpm exec riebeckite check",
3633
+ "pnpm exec riebeckite dev",
3634
+ "pnpm exec riebeckite build",
2850
3635
  "```",
2851
3636
  ""
2852
3637
  );
@@ -3188,505 +3973,121 @@ var README_DEMOS = {
3188
3973
  "- [ ] Draft the release notes",
3189
3974
  "- [ ] Link to [[index]]",
3190
3975
  "",
3191
- "## Done",
3192
- "",
3193
- "- [x] Publish the fixture"
3194
- ].join("\n")
3195
- )
3196
- }
3197
- };
3198
- var README_PAGE_LINKS = {
3199
- "framework/plugins": { en: "Plugin tour", ja: "プラグインツアー" },
3200
- "framework/themes": { en: "Theme tour", ja: "テーマツアー" },
3201
- guide: { en: "Getting-started guide", ja: "はじめにガイド" },
3202
- examples: { en: "Examples", ja: "サンプル集" },
3203
- "reference/plugins": { en: "Plugin reference", ja: "プラグインリファレンス" },
3204
- "reference/themes": { en: "Theme reference", ja: "テーマリファレンス" }
3205
- };
3206
- var ENGLISH_ONLY_PAGES = /* @__PURE__ */ new Set([
3207
- "guide",
3208
- "reference/plugins",
3209
- "reference/themes"
3210
- ]);
3211
- function fence(language, body) {
3212
- const tick = "```";
3213
- return `${tick}${language}
3214
- ${body}
3215
- ${tick}`;
3216
- }
3217
- function hasPlugin(preset, packageName) {
3218
- return preset.plugins.some((plugin) => plugin.package === packageName);
3219
- }
3220
- function demoPackage(key) {
3221
- return key.split("/", 2).join("/");
3222
- }
3223
- function readmePluginOptions(variables, preset, plugin) {
3224
- return renderOptions(plugin.options, optionContext(preset, variables));
3225
- }
3226
- function configurationReferenceLines(preset, variables, words) {
3227
- const lines = [
3228
- `## ${words.configReference}`,
3229
- "",
3230
- words.configReferenceBody,
3231
- ""
3232
- ];
3233
- if (preset.theme) {
3234
- const themeCall = preset.theme.options ? `${preset.theme.factory}(${preset.theme.options})` : `${preset.theme.factory}()`;
3235
- lines.push(`- **${words.theme}**: \`${themeCall}\``, "");
3236
- }
3237
- lines.push(
3238
- `| Package | ${words.factory} | ${words.options} |`,
3239
- "| --- | --- | --- |"
3240
- );
3241
- for (const plugin of preset.plugins) {
3242
- const options = readmePluginOptions(variables, preset, plugin);
3243
- const cell = options === null ? words.emDash : `\`${options}\``;
3244
- lines.push(`| \`${plugin.package}\` | \`${plugin.factory}\` | ${cell} |`);
3245
- }
3246
- lines.push("");
3247
- return lines;
3248
- }
3249
- function demoPagesLines(preset, variables, words, language) {
3250
- const lines = [
3251
- `## ${words.demoPages}`,
3252
- "",
3253
- words.demoPagesBody,
3254
- ""
3255
- ];
3256
- const defaultLang = defaultLanguageForLocale(variables.locale);
3257
- const pages = preset.contentPages.filter((page) => page !== "index");
3258
- for (const page of pages) {
3259
- const label = README_PAGE_LINKS[page]?.[language] ?? `/${page}/`;
3260
- const suffix = ENGLISH_ONLY_PAGES.has(page) && defaultLang !== "en" ? ".en" : "";
3261
- const source = `content/${page}${suffix}.md`;
3262
- const url = `/${page}/`;
3263
- lines.push(`- [**${label}**](${source}) — live at [\`${url}\`](${url})`);
3264
- }
3265
- lines.push("");
3266
- return lines;
3267
- }
3268
- var README_DEMO_ORDER = [
3269
- "@riebeckite/plugin-obsidian-markdown/callout",
3270
- "@riebeckite/plugin-obsidian-markdown/wikilinks",
3271
- "@riebeckite/plugin-code-enhance",
3272
- "@riebeckite/plugin-code-tabs",
3273
- "@riebeckite/plugin-mermaid",
3274
- "@riebeckite/plugin-d2",
3275
- "@riebeckite/plugin-graphviz",
3276
- "@riebeckite/plugin-chartjs",
3277
- "@riebeckite/plugin-vega-lite",
3278
- "@riebeckite/plugin-wavedrom",
3279
- "@riebeckite/plugin-markmap",
3280
- "@riebeckite/plugin-map",
3281
- "@riebeckite/plugin-marp",
3282
- "@riebeckite/plugin-qr-code",
3283
- "@riebeckite/plugin-rich-embed",
3284
- "@riebeckite/plugin-gallery",
3285
- "@riebeckite/plugin-dataview",
3286
- "@riebeckite/plugin-query",
3287
- "@riebeckite/plugin-bases",
3288
- "@riebeckite/plugin-kanban"
3289
- ];
3290
- function copyPasteDemoLines(preset, words, language) {
3291
- const lines = [
3292
- `## ${words.copyDemos}`,
3293
- "",
3294
- words.copyDemosBody,
3295
- ""
3296
- ];
3297
- for (const key of README_DEMO_ORDER) {
3298
- const packageName = demoPackage(key);
3299
- if (!hasPlugin(preset, packageName)) continue;
3300
- const demo = README_DEMOS[key];
3301
- lines.push(`### ${demo.title[language]}`, "");
3302
- lines.push(demo.intro[language], "");
3303
- lines.push(demo.markdown, "");
3304
- }
3305
- return lines;
3306
- }
3307
- function server() {
3308
- return `import { mountRiebeckiteEndpoints } from "@riebeckite/honox/server";
3309
- import { createApp } from "honox/server";
3310
- import { config } from "./config";
3311
- import { content } from "./content";
3312
-
3313
- const app = createApp({
3314
- init: (app) => {
3315
- mountRiebeckiteEndpoints(app, { config, content });
3316
- },
3317
- });
3318
-
3319
- export default app;
3320
- export { config, content };
3321
- `;
3322
- }
3323
- function client() {
3324
- return `import { initRiebeckiteClient } from "virtual:riebeckite/client";
3325
- import { createClient } from "honox/client";
3326
-
3327
- createClient();
3328
-
3329
- if (document.readyState === "loading") {
3330
- document.addEventListener("DOMContentLoaded", initPage, { once: true });
3331
- } else {
3332
- initPage();
3333
- }
3334
-
3335
- function initPage() {
3336
- initRiebeckiteClient();
3337
- }
3338
- `;
3339
- }
3340
- function appConfig() {
3341
- return `import path from "node:path";
3342
- import { fileURLToPath } from "node:url";
3343
- import { resolveConfigModule } from "@riebeckite/core";
3344
- import * as rawConfigModule from "../riebeckite.config";
3345
-
3346
- const appRoot =
3347
- process.env.RIEBECKITE_APP_ROOT ??
3348
- fileURLToPath(new URL("../", import.meta.url));
3349
- const resolvedConfig = resolveConfigModule(rawConfigModule);
3350
-
3351
- export const config = {
3352
- ...resolvedConfig,
3353
- buildDirectory: path.join(appRoot, ".riebeckite"),
3354
- content: {
3355
- ...resolvedConfig.content,
3356
- directory: path.resolve(appRoot, resolvedConfig.content.directory),
3357
- },
3358
- };
3359
- `;
3360
- }
3361
- function appContent() {
3362
- return `import { ContentManager } from "@riebeckite/core";
3363
- import { config } from "./config";
3364
- import { CONTENT_DIR } from "./constants/paths";
3365
-
3366
- export const content = new ContentManager(
3367
- CONTENT_DIR,
3368
- config.content.exclude,
3369
- {
3370
- config,
3371
- plugins: config.plugins,
3372
- },
3373
- );
3374
- `;
3375
- }
3376
- function paths() {
3377
- return `import path from "node:path";
3378
- import { fileURLToPath } from "node:url";
3379
- import { config } from "../config";
3380
-
3381
- const appRoot =
3382
- process.env.RIEBECKITE_APP_ROOT ??
3383
- fileURLToPath(new URL("../../", import.meta.url));
3384
-
3385
- export const CONTENT_DIR = path.resolve(appRoot, config.content.directory);
3386
- export const ASSETS_ROOT = "public/";
3387
- `;
3388
- }
3389
- function globalDeclarations() {
3390
- return `import type { PluginHeadTag } from "@riebeckite/core";
3391
-
3392
- declare module "virtual:riebeckite/client" {
3393
- export function initRiebeckiteClient(): void;
3394
- }
3395
-
3396
- declare module "hono" {
3397
- interface Env {
3398
- Variables: {
3399
- headTags?: readonly PluginHeadTag[];
3400
- htmlLanguage?: string;
3401
- };
3402
- Bindings: Record<string, never>;
3403
- }
3404
- }
3405
- `;
3406
- }
3407
- function style(preset) {
3408
- const imports = [];
3409
- if (preset.plugins.length > 0) {
3410
- imports.push('@import "./.riebeckite/plugin-styles.css";');
3411
- }
3412
- if (preset.theme) {
3413
- imports.push('@import "./.riebeckite/theme-styles.css";');
3414
- }
3415
- const blocks = [
3416
- "",
3417
- "body {",
3418
- " margin: 0;",
3419
- " font-family: system-ui, sans-serif;",
3420
- "}",
3421
- ""
3422
- ];
3423
- if (preset.appFiles.includes("header")) {
3424
- blocks.push(
3425
- ".site-header {",
3426
- " display: flex;",
3427
- " align-items: center;",
3428
- " justify-content: space-between;",
3429
- " gap: 1rem;",
3430
- " padding: 1rem;",
3431
- " border-bottom: 1px solid #d1d5db;",
3432
- "}",
3433
- "",
3434
- ".site-header__home {",
3435
- " color: #111827;",
3436
- " font-weight: 700;",
3437
- " text-decoration: none;",
3438
- "}",
3439
- ""
3440
- );
3441
- }
3442
- if (preset.appFiles.includes("article")) {
3443
- blocks.push(
3444
- ".site-article {",
3445
- " display: block;",
3446
- " max-width: 48rem;",
3447
- " padding: 2rem 1rem;",
3448
- " margin: 0 auto;",
3449
- "}",
3450
- ""
3451
- );
3452
- }
3453
- if (!preset.theme) {
3454
- blocks.push(
3455
- ".riebeckite-empty {",
3456
- " max-width: 48rem;",
3457
- " padding: 4rem 1rem;",
3458
- " margin: 0 auto;",
3459
- "}",
3460
- ""
3461
- );
3462
- }
3463
- return [...imports, ...blocks].join("\n");
3464
- }
3465
- function siteHeader() {
3466
- return `import { ColorModeToggle } from "@riebeckite/plugin-color-mode";
3467
- import { config } from "../config";
3468
-
3469
- export function SiteHeader() {
3470
- return (
3471
- <header class="site-header">
3472
- <a href="/" class="site-header__home">
3473
- {config.site.title}
3474
- </a>
3475
- <ColorModeToggle />
3476
- </header>
3477
- );
3478
- }
3479
- `;
3480
- }
3481
- function article() {
3482
- return `import type { PostContent } from "@riebeckite/core";
3483
- import { Article, ArticleContent, ArticleLayout } from "@riebeckite/honox/ui";
3484
-
3485
- export function SiteArticle({
3486
- post,
3487
- afterMeta,
3488
- }: {
3489
- post: PostContent;
3490
- afterMeta?: string;
3491
- }) {
3492
- return (
3493
- <Article class="site-article">
3494
- <ArticleLayout>
3495
- <ArticleContent>
3496
- {afterMeta ? (
3497
- <div
3498
- class="site-article__after-meta"
3499
- dangerouslySetInnerHTML={{ __html: afterMeta }}
3500
- />
3501
- ) : null}
3502
- <div dangerouslySetInnerHTML={{ __html: post.html ?? "" }} />
3503
- </ArticleContent>
3504
- </ArticleLayout>
3505
- </Article>
3506
- );
3507
- }
3508
- `;
3509
- }
3510
- function renderer(preset) {
3511
- if (!preset.theme) return bareRenderer();
3512
- const hasHeader = preset.appFiles.includes("header");
3513
- const hasColorMode = preset.plugins.some(
3514
- (plugin) => plugin.package === "@riebeckite/plugin-color-mode"
3515
- );
3516
- return `import type { PluginHeadTag } from "@riebeckite/core";
3517
- import { jsxRenderer } from "hono/jsx-renderer";
3518
- import { Link, Script } from "honox/server";
3519
- ${hasColorMode ? `import { ColorModeScript } from "@riebeckite/plugin-color-mode";
3520
- ` : ""}${hasHeader ? `import { SiteHeader } from "../components/site-header";
3521
- ` : ""}import { config } from "../config";
3522
-
3523
- function themeAttributes() {
3524
- const { theme } = config;
3525
-
3526
- return {
3527
- ...theme.attributes,
3528
- "data-theme": theme.colorMode === "system" ? undefined : theme.colorMode,
3529
- "data-theme-name": theme.name,
3530
- "data-typography": theme.typography,
3531
- "data-article-layout": theme.articleLayout,
3532
- };
3976
+ "## Done",
3977
+ "",
3978
+ "- [x] Publish the fixture"
3979
+ ].join("\n")
3980
+ )
3981
+ }
3982
+ };
3983
+ var README_PAGE_LINKS = {
3984
+ "framework/plugins": { en: "Plugin tour", ja: "プラグインツアー" },
3985
+ "framework/themes": { en: "Theme tour", ja: "テーマツアー" },
3986
+ guide: { en: "Getting-started guide", ja: "はじめにガイド" },
3987
+ examples: { en: "Examples", ja: "サンプル集" },
3988
+ "reference/plugins": { en: "Plugin reference", ja: "プラグインリファレンス" },
3989
+ "reference/themes": { en: "Theme reference", ja: "テーマリファレンス" }
3990
+ };
3991
+ var ENGLISH_ONLY_PAGES = /* @__PURE__ */ new Set([
3992
+ "guide",
3993
+ "reference/plugins",
3994
+ "reference/themes"
3995
+ ]);
3996
+ function fence(language, body) {
3997
+ const tick = "```";
3998
+ return `${tick}${language}
3999
+ ${body}
4000
+ ${tick}`;
3533
4001
  }
3534
-
3535
- export default jsxRenderer(({ children }, c) => {
3536
- const headTags: readonly PluginHeadTag[] = c.get("headTags") ?? [];
3537
-
3538
- return (
3539
- <html
3540
- lang={c.get("htmlLanguage") ?? config.site.locale}
3541
- {...themeAttributes()}
3542
- >
3543
- <head>
3544
- <meta charset="utf-8" />
3545
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3546
- <title>{config.site.title}</title>
3547
- ${hasColorMode ? ` <ColorModeScript />
3548
- ` : ""} <Link href="/app/style.css" rel="stylesheet" />
3549
- {headTags.map(renderHeadTag)}
3550
- <Script src="/app/client.ts" async />
3551
- </head>
3552
- <body class="riebeckite-page">
3553
- ${hasHeader ? ` <SiteHeader />
3554
- ` : ""} {children}
3555
- </body>
3556
- </html>
3557
- );
3558
- });
3559
-
3560
- function renderHeadTag(tag: PluginHeadTag, index: number) {
3561
- const key = \`\${tag.tag}-\${index}\`;
3562
- if (tag.tag === "meta") return <meta {...tag.attrs} key={key} />;
3563
- if (tag.tag === "link") return <link {...tag.attrs} key={key} />;
3564
- return (
3565
- <script
3566
- {...tag.attrs}
3567
- key={key}
3568
- dangerouslySetInnerHTML={
3569
- tag.children ? { __html: tag.children } : undefined
3570
- }
3571
- />
3572
- );
4002
+ function hasPlugin(preset, packageName) {
4003
+ return preset.plugins.some((plugin) => plugin.package === packageName);
3573
4004
  }
3574
- `;
4005
+ function demoPackage(key) {
4006
+ return key.split("/", 2).join("/");
3575
4007
  }
3576
- function bareRenderer() {
3577
- return `import { jsxRenderer } from "hono/jsx-renderer";
3578
- import { Link, Script } from "honox/server";
3579
- import { config } from "../config";
3580
-
3581
- export default jsxRenderer(({ children }, c) => (
3582
- <html lang={c.get("htmlLanguage") ?? config.site.locale}>
3583
- <head>
3584
- <meta charset="utf-8" />
3585
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3586
- <title>{config.site.title}</title>
3587
- <Link href="/app/style.css" rel="stylesheet" />
3588
- <Script src="/app/client.ts" async />
3589
- </head>
3590
- <body class="riebeckite-page">{children}</body>
3591
- </html>
3592
- ));
3593
- `;
4008
+ function readmePluginOptions(variables, preset, plugin) {
4009
+ return renderOptions(plugin.options, optionContext(preset, variables));
3594
4010
  }
3595
- function indexRoute(preset) {
3596
- if (!preset.appFiles.includes("article")) return staticIndexRoute();
3597
- return `import { isPublished } from "@riebeckite/core";
3598
- import { createRoute } from "honox/factory";
3599
- import { SiteArticle } from "../components/article";
3600
- import { config } from "../config";
3601
- import { content } from "../content";
3602
-
3603
- export default createRoute(async (c) => {
3604
- const manifest = await content.getManifest();
3605
- const indexEntry = manifest.bySlug.get("index");
3606
- if (indexEntry && indexEntry.permalink !== "/") {
3607
- return c.redirect(indexEntry.permalink, 308);
3608
- }
3609
-
3610
- const post = await content.getProcessedContent("index");
3611
- if (!isPublished(config, post.frontmatter)) {
3612
- return c.notFound();
3613
- }
3614
-
3615
- if (indexEntry) {
3616
- c.set("htmlLanguage", indexEntry.publicLocation.metadata?.["l10n.lang"]);
3617
- c.set("headTags", indexEntry.headTags ?? []);
4011
+ function configurationReferenceLines(preset, variables, words) {
4012
+ const lines = [
4013
+ `## ${words.configReference}`,
4014
+ "",
4015
+ words.configReferenceBody,
4016
+ ""
4017
+ ];
4018
+ if (preset.theme) {
4019
+ const themeCall = preset.theme.options ? `${preset.theme.factory}(${preset.theme.options})` : `${preset.theme.factory}()`;
4020
+ lines.push(`- **${words.theme}**: \`${themeCall}\``, "");
3618
4021
  }
3619
-
3620
- return c.render(
3621
- <SiteArticle
3622
- post={post}
3623
- afterMeta={indexEntry?.bodySlots?.["article.after-meta"]}
3624
- />,
4022
+ lines.push(
4023
+ `| Package | ${words.factory} | ${words.options} |`,
4024
+ "| --- | --- | --- |"
3625
4025
  );
3626
- });
3627
- `;
4026
+ for (const plugin of preset.plugins) {
4027
+ const options = readmePluginOptions(variables, preset, plugin);
4028
+ const cell = options === null ? words.emDash : `\`${options}\``;
4029
+ lines.push(`| \`${plugin.package}\` | \`${plugin.factory}\` | ${cell} |`);
4030
+ }
4031
+ lines.push("");
4032
+ return lines;
3628
4033
  }
3629
- function staticIndexRoute() {
3630
- return `import { createRoute } from "honox/factory";
3631
- import { config } from "../config";
3632
-
3633
- export default createRoute((c) =>
3634
- c.render(
3635
- <main class="riebeckite-empty">
3636
- <h1>{config.site.title}</h1>
3637
- <p>This is a blank Riebeckite site. Add Markdown files under content/ to get started.</p>
3638
- </main>,
3639
- ),
3640
- );
3641
- `;
4034
+ function demoPagesLines(preset, variables, words, language) {
4035
+ const lines = [
4036
+ `## ${words.demoPages}`,
4037
+ "",
4038
+ words.demoPagesBody,
4039
+ ""
4040
+ ];
4041
+ const defaultLang = defaultLanguageForLocale(variables.locale);
4042
+ const pages = preset.contentPages.filter((page) => page !== "index");
4043
+ for (const page of pages) {
4044
+ const label = README_PAGE_LINKS[page]?.[language] ?? `/${page}/`;
4045
+ const suffix = ENGLISH_ONLY_PAGES.has(page) && defaultLang !== "en" ? ".en" : "";
4046
+ const source = `content/${page}${suffix}.md`;
4047
+ const url = `/${page}/`;
4048
+ lines.push(`- [**${label}**](${source}) — live at [\`${url}\`](${url})`);
4049
+ }
4050
+ lines.push("");
4051
+ return lines;
3642
4052
  }
3643
- function slugRoute() {
3644
- return `import { isPublished } from "@riebeckite/core";
3645
- import {
3646
- contentRouteSsgParams,
3647
- resolveContentRoute,
3648
- } from "@riebeckite/honox/server";
3649
- import { createRoute } from "honox/factory";
3650
- import { SiteArticle } from "../components/article";
3651
- import { config } from "../config";
3652
- import { content } from "../content";
3653
-
3654
- export default createRoute(
3655
- contentRouteSsgParams("/:slug{.+}", async () => {
3656
- const manifest = await content.getManifest();
3657
- return manifest.entries
3658
- .filter((entry) => isPublished(config, entry.frontmatter))
3659
- .filter((entry) => entry.permalink !== "/")
3660
- .map((entry) => ({ slug: entry.permalink.replace(/^\\/+/, "") }));
3661
- }),
3662
- async (c) => {
3663
- const requestedSlug = c.req.param("slug");
3664
- if (!requestedSlug) return c.notFound();
3665
- if (/\\.[a-zA-Z0-9]+$/.test(requestedSlug)) return c.notFound();
3666
-
3667
- const manifest = await content.getManifest();
3668
- const route = resolveContentRoute(manifest, c.req.path);
3669
- if (!route) return c.notFound();
3670
- if (route.kind === "redirect")
3671
- return c.redirect(route.location, route.status);
3672
-
3673
- const post = await content.getProcessedContent(route.entry.slug);
3674
- if (!isPublished(config, post.frontmatter)) {
3675
- return c.notFound();
3676
- }
3677
-
3678
- c.set("htmlLanguage", route.entry.publicLocation.metadata?.["l10n.lang"]);
3679
- c.set("headTags", route.entry.headTags ?? []);
3680
-
3681
- return c.render(
3682
- <SiteArticle
3683
- post={post}
3684
- afterMeta={route.entry.bodySlots?.["article.after-meta"]}
3685
- />,
3686
- );
3687
- },
3688
- );
3689
- `;
4053
+ var README_DEMO_ORDER = [
4054
+ "@riebeckite/plugin-obsidian-markdown/callout",
4055
+ "@riebeckite/plugin-obsidian-markdown/wikilinks",
4056
+ "@riebeckite/plugin-code-enhance",
4057
+ "@riebeckite/plugin-code-tabs",
4058
+ "@riebeckite/plugin-mermaid",
4059
+ "@riebeckite/plugin-d2",
4060
+ "@riebeckite/plugin-graphviz",
4061
+ "@riebeckite/plugin-chartjs",
4062
+ "@riebeckite/plugin-vega-lite",
4063
+ "@riebeckite/plugin-wavedrom",
4064
+ "@riebeckite/plugin-markmap",
4065
+ "@riebeckite/plugin-map",
4066
+ "@riebeckite/plugin-marp",
4067
+ "@riebeckite/plugin-qr-code",
4068
+ "@riebeckite/plugin-rich-embed",
4069
+ "@riebeckite/plugin-gallery",
4070
+ "@riebeckite/plugin-dataview",
4071
+ "@riebeckite/plugin-query",
4072
+ "@riebeckite/plugin-bases",
4073
+ "@riebeckite/plugin-kanban"
4074
+ ];
4075
+ function copyPasteDemoLines(preset, words, language) {
4076
+ const lines = [
4077
+ `## ${words.copyDemos}`,
4078
+ "",
4079
+ words.copyDemosBody,
4080
+ ""
4081
+ ];
4082
+ for (const key of README_DEMO_ORDER) {
4083
+ const packageName = demoPackage(key);
4084
+ if (!hasPlugin(preset, packageName)) continue;
4085
+ const demo = README_DEMOS[key];
4086
+ lines.push(`### ${demo.title[language]}`, "");
4087
+ lines.push(demo.intro[language], "");
4088
+ lines.push(demo.markdown, "");
4089
+ }
4090
+ return lines;
3690
4091
  }
3691
4092
 
3692
4093
  // src/scaffold/index.ts
@@ -3760,10 +4161,10 @@ function formatScaffoldNextSteps(relativeDirectory) {
3760
4161
  if (relativeDirectory !== ".") {
3761
4162
  lines.push(` cd ${relativeDirectory}`);
3762
4163
  }
3763
- lines.push(" npm install");
3764
- lines.push(" npm exec riebeckite check");
3765
- lines.push(" npm exec riebeckite dev");
3766
- lines.push(" npm exec riebeckite build");
4164
+ lines.push(" pnpm install");
4165
+ lines.push(" pnpm exec riebeckite check");
4166
+ lines.push(" pnpm exec riebeckite dev");
4167
+ lines.push(" pnpm exec riebeckite build");
3767
4168
  return lines.join("\n");
3768
4169
  }
3769
4170
  export {