create-riebeckite 0.0.21 → 0.0.23

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/README.md CHANGED
@@ -30,7 +30,7 @@ The prompts ask, in order:
30
30
  | --- | --- | --- |
31
31
  | Project name | the folder to create (empty input uses the default name) | any name, e.g. `my-site` |
32
32
  | Preset | `starter`, `minimal`, `showcase`, `empty` | `starter` for most sites |
33
- | Project files | `.editorconfig`, `.gitattributes`, `biome.json`, `.npmrc`, `.vscode/settings.json` | the recommended set is pre-selected |
33
+ | Project files | `.gitignore`, `.editorconfig`, `.gitattributes`, `biome.json`, `.npmrc`, `.vscode/settings.json` | the recommended set is pre-selected |
34
34
  | Content source | `This project`, `Separate GitHub repository` | `This project` to start |
35
35
  | Deployment | `GitHub Actions + Cloudflare Workers`, `Not now` | `Not now` for local development |
36
36
 
@@ -46,7 +46,7 @@ Deployment can be added later, described in
46
46
  | --- | --- |
47
47
  | `[directory]` | Directory to scaffold into (default: the current directory) |
48
48
  | `--preset <name>` | Starter composition, e.g. `showcase` (default: `starter`) |
49
- | `--utilities <names>` | Comma-separated project files to generate: `editorconfig`, `gitattributes`, `biome`, `npmrc`, `vscode`, or `none` (default: `editorconfig,gitattributes,biome`) |
49
+ | `--utilities <names>` | Comma-separated project files to generate: `gitignore`, `editorconfig`, `gitattributes`, `biome`, `npmrc`, `vscode`, or `none` (default: `gitignore,editorconfig,gitattributes,biome`) |
50
50
  | `--force` | Scaffold even when the target directory is not empty |
51
51
  | `--list-presets` | Print the available presets and their descriptions, then exit |
52
52
  | `--github-actions` | Generate the Cloudflare deployment workflow |
package/README_ja.md CHANGED
@@ -31,7 +31,7 @@ npm exec riebeckite build
31
31
  | --- | --- | --- |
32
32
  | Project name | 作るフォルダ名(空なら既定の名前) | 任意。例: `my-site` |
33
33
  | Preset | `starter`、`minimal`、`showcase`、`empty` | 大半のサイトは `starter` |
34
- | Project files | `.editorconfig`、`.gitattributes`、`biome.json`、`.npmrc`、`.vscode/settings.json` | 推奨セットをあらかじめ選択 |
34
+ | Project files | `.gitignore`、`.editorconfig`、`.gitattributes`、`biome.json`、`.npmrc`、`.vscode/settings.json` | 推奨セットをあらかじめ選択 |
35
35
  | Content source | `This project`、`Separate GitHub repository` | 最初は `This project` |
36
36
  | デプロイ設定 | `GitHub Actions + Cloudflare Workers`、`Not now` | 手元で試すなら `Not now` |
37
37
 
@@ -43,7 +43,7 @@ npm exec riebeckite build
43
43
  | --- | --- |
44
44
  | `[directory]` | 生成先ディレクトリ(既定はカレントディレクトリ) |
45
45
  | `--preset <name>` | 使用するスターター構成(既定は `starter`) |
46
- | `--utilities <names>` | 生成するプロジェクトファイルをカンマ区切りで指定する(`editorconfig`、`gitattributes`、`biome`、`npmrc`、`vscode`、または `none`。既定は `editorconfig,gitattributes,biome`) |
46
+ | `--utilities <names>` | 生成するプロジェクトファイルをカンマ区切りで指定する(`gitignore`、`editorconfig`、`gitattributes`、`biome`、`npmrc`、`vscode`、または `none`。既定は `gitignore,editorconfig,gitattributes,biome`) |
47
47
  | `--force` | 空でないディレクトリにも展開する |
48
48
  | `--list-presets` | 利用可能な preset と説明を一覧表示して終了する |
49
49
  | `--github-actions` | Cloudflare へのデプロイ workflow を生成する |
package/dist/src/index.js CHANGED
@@ -297,7 +297,6 @@ var seo = np("@riebeckite/plugin-seo", "seo", {
297
297
  });
298
298
  var toc = np("@riebeckite/plugin-toc", "tocPlugin");
299
299
  var properties = np("@riebeckite/plugin-properties", "properties", {
300
- render: { depth: 1, value: '"slot"' },
301
300
  include: { depth: 2, value: '["created", "modified", "tags", "status"]' },
302
301
  order: { depth: 2, value: '["created", "modified", "tags", "status"]' }
303
302
  });
@@ -314,11 +313,7 @@ var codeEnhance = np("@riebeckite/plugin-code-enhance", "codeEnhance", {
314
313
  var search = np("@riebeckite/plugin-search", "searchPlugin");
315
314
  var backlinks = np("@riebeckite/plugin-backlinks", "backlinksPlugin");
316
315
  var breadcrumbs = np("@riebeckite/plugin-breadcrumbs", "breadcrumbsPlugin");
317
- var navigationStarter = np(
318
- "@riebeckite/plugin-navigation",
319
- "navigation",
320
- `{ items: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }, { label: "Notes", href: "/notes/planning", children: [{ label: "Planning", href: "/notes/planning" }, { label: "Writing", href: "/notes/writing" }] }], secondary: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }] }`
321
- );
316
+ var navigationStarter = np("@riebeckite/plugin-navigation", "navigation");
322
317
  var navigationShowcase = np(
323
318
  "@riebeckite/plugin-navigation",
324
319
  "navigation",
@@ -334,7 +329,6 @@ var relatedPosts = np("@riebeckite/plugin-related-posts", "relatedPosts", {
334
329
  className: { depth: 3, value: '"rb-related-posts"' }
335
330
  });
336
331
  var share = np("@riebeckite/plugin-share", "share", {
337
- placement: { depth: 2, value: '"bottom"' },
338
332
  services: {
339
333
  depth: 3,
340
334
  value: '["x", "bluesky", "mastodon", "facebook", "linkedin", "hatena", "copy"]'
@@ -399,8 +393,7 @@ var codeTabs = np(
399
393
  );
400
394
  var codeAnnotations = np(
401
395
  "@riebeckite/plugin-code-annotations",
402
- "codeAnnotations",
403
- `{ className: "rb-code" }`
396
+ "codeAnnotations"
404
397
  );
405
398
  var shortcodes = np(
406
399
  "@riebeckite/plugin-shortcodes",
@@ -652,12 +645,11 @@ var minimal = {
652
645
  var starter = {
653
646
  name: "starter",
654
647
  description: "Recommended default: a practical Markdown garden with search, navigation, and reading essentials.",
655
- languages: [...SCAFFOLD_LANGUAGES],
648
+ languages: ["en"],
656
649
  theme: defaultTheme,
657
650
  plugins: [
658
651
  obsidianMarkdown,
659
652
  colorMode,
660
- l10n,
661
653
  seo,
662
654
  toc,
663
655
  properties,
@@ -671,8 +663,9 @@ var starter = {
671
663
  recentPosts,
672
664
  responsiveImage,
673
665
  lightbox,
674
- series,
675
- taxonomy
666
+ taxonomy,
667
+ folderPages,
668
+ hoverPreview
676
669
  ],
677
670
  contentPages: ["index", "guide", "examples"],
678
671
  readme: "standard"
@@ -776,6 +769,7 @@ function resolveScaffoldPreset(value) {
776
769
 
777
770
  // src/scaffold/utilities.ts
778
771
  var SCAFFOLD_UTILITY_NAMES = [
772
+ "gitignore",
779
773
  "editorconfig",
780
774
  "gitattributes",
781
775
  "biome",
@@ -783,6 +777,11 @@ var SCAFFOLD_UTILITY_NAMES = [
783
777
  "vscode"
784
778
  ];
785
779
  var SCAFFOLD_UTILITIES = [
780
+ {
781
+ name: "gitignore",
782
+ label: ".gitignore",
783
+ description: "Git exclusions for dependencies, build output, and local files."
784
+ },
786
785
  {
787
786
  name: "editorconfig",
788
787
  label: ".editorconfig",
@@ -810,6 +809,7 @@ var SCAFFOLD_UTILITIES = [
810
809
  }
811
810
  ];
812
811
  var SCAFFOLD_DEFAULT_UTILITIES = [
812
+ "gitignore",
813
813
  "editorconfig",
814
814
  "gitattributes",
815
815
  "biome"
@@ -849,9 +849,10 @@ function utilityTemplateFiles(utilities) {
849
849
  const files = [];
850
850
  for (const name of utilities) {
851
851
  for (const file of copyTemplateTree(`utilities/${name}`)) {
852
- if (seen.has(file.path)) continue;
853
- seen.add(file.path);
854
- files.push(file);
852
+ const path5 = name === "gitignore" ? ".gitignore" : file.path;
853
+ if (seen.has(path5)) continue;
854
+ seen.add(path5);
855
+ files.push({ ...file, path: path5 });
855
856
  }
856
857
  }
857
858
  return files;
@@ -1339,10 +1340,11 @@ var PLUGIN_MARKDOWN_GUIDES = {
1339
1340
  markdown: "This sentence contains ==highlighted text== inside normal Markdown."
1340
1341
  },
1341
1342
  "@riebeckite/plugin-code-annotations": {
1342
- summary: "Adds callouts/annotations to code fences.",
1343
+ summary: "Renders code diffs with syntax highlighting for the target language.",
1343
1344
  markdown: [
1344
- "```ts",
1345
- "const answer = 42 // [!code focus]",
1345
+ "```diff ts",
1346
+ "+ const answer = 42",
1347
+ "- const answer = 0",
1346
1348
  "console.log(answer)",
1347
1349
  "```"
1348
1350
  ].join("\n")
@@ -2589,7 +2591,7 @@ function referenceThemesContent(preset) {
2589
2591
  }
2590
2592
 
2591
2593
  // src/scaffold/version.ts
2592
- var RIEBECKITE_VERSION = "^0.0.21";
2594
+ var RIEBECKITE_VERSION = "^0.0.23";
2593
2595
 
2594
2596
  // src/scaffold/templates.ts
2595
2597
  function siteTemplateFiles(preset, variables, options = {}) {
@@ -2614,17 +2616,43 @@ function siteTemplateFiles(preset, variables, options = {}) {
2614
2616
  var CONTENT_LANGUAGE_PATTERN = /^(.*)\.([a-z]{2}(?:-[A-Z]{2})?)\.md$/;
2615
2617
  function presetContentFiles(preset, variables) {
2616
2618
  const defaultLanguage = defaultLanguageForLocale(variables.locale);
2617
- return copyTemplateTree(`presets/${preset.name}/content`).map((file) => {
2619
+ const files = copyTemplateTree(`presets/${preset.name}/content`);
2620
+ if (preset.languages.length <= 1) {
2621
+ const selected = /* @__PURE__ */ new Map();
2622
+ for (const file of files) {
2623
+ const match = CONTENT_LANGUAGE_PATTERN.exec(file.path);
2624
+ if (!match || !SCAFFOLD_LANGUAGES.includes(match[2])) {
2625
+ selected.set(file.path, { file, score: 0 });
2626
+ continue;
2627
+ }
2628
+ const [, base, language] = match;
2629
+ const score = language === defaultLanguage ? 2 : language === "en" ? 1 : 0;
2630
+ const current = selected.get(base);
2631
+ if (!current || score > current.score) {
2632
+ selected.set(base, { file, score });
2633
+ }
2634
+ }
2635
+ return [...selected.entries()].map(([relativePath, { file }]) => ({
2636
+ path: `content/${relativePath.endsWith(".md") ? relativePath : `${relativePath}.md`}`,
2637
+ content: replaceTitle(file.content, variables.title)
2638
+ }));
2639
+ }
2640
+ return files.map((file) => {
2618
2641
  const match = CONTENT_LANGUAGE_PATTERN.exec(file.path);
2619
2642
  let relativePath = file.path;
2620
2643
  if (match && SCAFFOLD_LANGUAGES.includes(match[2])) {
2621
2644
  const [, base, language] = match;
2622
2645
  relativePath = language === defaultLanguage ? `${base}.md` : `${base}.${language}.md`;
2623
2646
  }
2624
- const content = typeof file.content === "string" ? file.content.split("{{title}}").join(variables.title) : file.content;
2625
- return { path: `content/${relativePath}`, content };
2647
+ return {
2648
+ path: `content/${relativePath}`,
2649
+ content: replaceTitle(file.content, variables.title)
2650
+ };
2626
2651
  });
2627
2652
  }
2653
+ function replaceTitle(content, title) {
2654
+ return typeof content === "string" ? content.split("{{title}}").join(title) : content;
2655
+ }
2628
2656
  function packageJson(preset, variables, options) {
2629
2657
  const dependencies = {
2630
2658
  "@riebeckite/core": RIEBECKITE_VERSION,
@@ -2654,6 +2682,7 @@ function packageJson(preset, variables, options) {
2654
2682
  dependencies,
2655
2683
  devDependencies: {
2656
2684
  "@hono/vite-build": "^1.11.1",
2685
+ "@hono/node-server": "^1.19.17",
2657
2686
  "@riebeckite/cli": RIEBECKITE_VERSION,
2658
2687
  "@types/node": "^24.5.2",
2659
2688
  typescript: "^5.0.0",
@@ -2819,7 +2848,9 @@ function readmeEn(preset, variables) {
2819
2848
  ];
2820
2849
  if (preset.readme !== "short") {
2821
2850
  lines.push(`## ${words.included}`, "");
2822
- lines.push(`- **${words.languages}**: ${languagesLabel(preset, words)}`);
2851
+ lines.push(
2852
+ `- **${words.languages}**: ${languagesLabel(preset, variables, words)}`
2853
+ );
2823
2854
  lines.push(
2824
2855
  `- **${words.theme}**: ${preset.theme ? `\`${preset.theme.package}\`` : words.none}`
2825
2856
  );
@@ -2880,7 +2911,9 @@ function readmeJa(preset, variables) {
2880
2911
  ];
2881
2912
  if (preset.readme !== "short") {
2882
2913
  lines.push(`## ${words.included}`, "");
2883
- lines.push(`- **${words.languages}**: ${languagesLabel(preset, words)}`);
2914
+ lines.push(
2915
+ `- **${words.languages}**: ${languagesLabel(preset, variables, words)}`
2916
+ );
2884
2917
  lines.push(
2885
2918
  `- **${words.theme}**: ${preset.theme ? `\`${preset.theme.package}\`` : words.none}`
2886
2919
  );
@@ -2959,10 +2992,12 @@ function deploymentSecretLines(language) {
2959
2992
  ""
2960
2993
  ];
2961
2994
  }
2962
- function languagesLabel(preset, words) {
2995
+ function languagesLabel(preset, variables, words) {
2963
2996
  if (preset.languages.length === 0) return words.none;
2964
- if (preset.languages.length === 1)
2965
- return `${preset.languages[0]} (${words.single})`;
2997
+ if (preset.languages.length === 1) {
2998
+ const language = preset.name === "starter" ? defaultLanguageForLocale(variables.locale) : preset.languages[0];
2999
+ return `${language} (${words.single})`;
3000
+ }
2966
3001
  return preset.languages.join(", ");
2967
3002
  }
2968
3003
  function pagesLabel(preset) {
@@ -3473,8 +3508,17 @@ async function scaffoldRiebeckiteSite(options) {
3473
3508
  await fs2.mkdir(path3.dirname(filePath), { recursive: true });
3474
3509
  await fs2.writeFile(filePath, file.content);
3475
3510
  }
3511
+ await ensureContentDirectory(targetDirectory, files);
3476
3512
  return { targetDirectory, files: files.map((file) => file.path) };
3477
3513
  }
3514
+ async function ensureContentDirectory(targetDirectory, files) {
3515
+ if (files.some(
3516
+ (file) => file.path === "content" || file.path.startsWith("content/")
3517
+ )) {
3518
+ return;
3519
+ }
3520
+ await fs2.mkdir(path3.join(targetDirectory, "content"), { recursive: true });
3521
+ }
3478
3522
  function externalContentSource(deployment) {
3479
3523
  if (deployment.type !== "github-actions") return void 0;
3480
3524
  return deployment.content.type === "external" ? deployment.content : void 0;
@@ -297,7 +297,6 @@ var seo = np("@riebeckite/plugin-seo", "seo", {
297
297
  });
298
298
  var toc = np("@riebeckite/plugin-toc", "tocPlugin");
299
299
  var properties = np("@riebeckite/plugin-properties", "properties", {
300
- render: { depth: 1, value: '"slot"' },
301
300
  include: { depth: 2, value: '["created", "modified", "tags", "status"]' },
302
301
  order: { depth: 2, value: '["created", "modified", "tags", "status"]' }
303
302
  });
@@ -314,11 +313,7 @@ var codeEnhance = np("@riebeckite/plugin-code-enhance", "codeEnhance", {
314
313
  var search = np("@riebeckite/plugin-search", "searchPlugin");
315
314
  var backlinks = np("@riebeckite/plugin-backlinks", "backlinksPlugin");
316
315
  var breadcrumbs = np("@riebeckite/plugin-breadcrumbs", "breadcrumbsPlugin");
317
- var navigationStarter = np(
318
- "@riebeckite/plugin-navigation",
319
- "navigation",
320
- `{ items: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }, { label: "Notes", href: "/notes/planning", children: [{ label: "Planning", href: "/notes/planning" }, { label: "Writing", href: "/notes/writing" }] }], secondary: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }] }`
321
- );
316
+ var navigationStarter = np("@riebeckite/plugin-navigation", "navigation");
322
317
  var navigationShowcase = np(
323
318
  "@riebeckite/plugin-navigation",
324
319
  "navigation",
@@ -334,7 +329,6 @@ var relatedPosts = np("@riebeckite/plugin-related-posts", "relatedPosts", {
334
329
  className: { depth: 3, value: '"rb-related-posts"' }
335
330
  });
336
331
  var share = np("@riebeckite/plugin-share", "share", {
337
- placement: { depth: 2, value: '"bottom"' },
338
332
  services: {
339
333
  depth: 3,
340
334
  value: '["x", "bluesky", "mastodon", "facebook", "linkedin", "hatena", "copy"]'
@@ -399,8 +393,7 @@ var codeTabs = np(
399
393
  );
400
394
  var codeAnnotations = np(
401
395
  "@riebeckite/plugin-code-annotations",
402
- "codeAnnotations",
403
- `{ className: "rb-code" }`
396
+ "codeAnnotations"
404
397
  );
405
398
  var shortcodes = np(
406
399
  "@riebeckite/plugin-shortcodes",
@@ -652,12 +645,11 @@ var minimal = {
652
645
  var starter = {
653
646
  name: "starter",
654
647
  description: "Recommended default: a practical Markdown garden with search, navigation, and reading essentials.",
655
- languages: [...SCAFFOLD_LANGUAGES],
648
+ languages: ["en"],
656
649
  theme: defaultTheme,
657
650
  plugins: [
658
651
  obsidianMarkdown,
659
652
  colorMode,
660
- l10n,
661
653
  seo,
662
654
  toc,
663
655
  properties,
@@ -671,8 +663,9 @@ var starter = {
671
663
  recentPosts,
672
664
  responsiveImage,
673
665
  lightbox,
674
- series,
675
- taxonomy
666
+ taxonomy,
667
+ folderPages,
668
+ hoverPreview
676
669
  ],
677
670
  contentPages: ["index", "guide", "examples"],
678
671
  readme: "standard"
@@ -926,10 +919,11 @@ var PLUGIN_MARKDOWN_GUIDES = {
926
919
  markdown: "This sentence contains ==highlighted text== inside normal Markdown."
927
920
  },
928
921
  "@riebeckite/plugin-code-annotations": {
929
- summary: "Adds callouts/annotations to code fences.",
922
+ summary: "Renders code diffs with syntax highlighting for the target language.",
930
923
  markdown: [
931
- "```ts",
932
- "const answer = 42 // [!code focus]",
924
+ "```diff ts",
925
+ "+ const answer = 42",
926
+ "- const answer = 0",
933
927
  "console.log(answer)",
934
928
  "```"
935
929
  ].join("\n")
@@ -2177,6 +2171,7 @@ function referenceThemesContent(preset) {
2177
2171
 
2178
2172
  // src/scaffold/utilities.ts
2179
2173
  var SCAFFOLD_UTILITY_NAMES = [
2174
+ "gitignore",
2180
2175
  "editorconfig",
2181
2176
  "gitattributes",
2182
2177
  "biome",
@@ -2184,6 +2179,11 @@ var SCAFFOLD_UTILITY_NAMES = [
2184
2179
  "vscode"
2185
2180
  ];
2186
2181
  var SCAFFOLD_UTILITIES = [
2182
+ {
2183
+ name: "gitignore",
2184
+ label: ".gitignore",
2185
+ description: "Git exclusions for dependencies, build output, and local files."
2186
+ },
2187
2187
  {
2188
2188
  name: "editorconfig",
2189
2189
  label: ".editorconfig",
@@ -2211,6 +2211,7 @@ var SCAFFOLD_UTILITIES = [
2211
2211
  }
2212
2212
  ];
2213
2213
  var SCAFFOLD_DEFAULT_UTILITIES = [
2214
+ "gitignore",
2214
2215
  "editorconfig",
2215
2216
  "gitattributes",
2216
2217
  "biome"
@@ -2250,16 +2251,17 @@ function utilityTemplateFiles(utilities) {
2250
2251
  const files = [];
2251
2252
  for (const name of utilities) {
2252
2253
  for (const file of copyTemplateTree(`utilities/${name}`)) {
2253
- if (seen.has(file.path)) continue;
2254
- seen.add(file.path);
2255
- files.push(file);
2254
+ const path4 = name === "gitignore" ? ".gitignore" : file.path;
2255
+ if (seen.has(path4)) continue;
2256
+ seen.add(path4);
2257
+ files.push({ ...file, path: path4 });
2256
2258
  }
2257
2259
  }
2258
2260
  return files;
2259
2261
  }
2260
2262
 
2261
2263
  // src/scaffold/version.ts
2262
- var RIEBECKITE_VERSION = "^0.0.21";
2264
+ var RIEBECKITE_VERSION = "^0.0.23";
2263
2265
 
2264
2266
  // src/scaffold/templates.ts
2265
2267
  function siteTemplateFiles(preset, variables, options = {}) {
@@ -2284,17 +2286,43 @@ function siteTemplateFiles(preset, variables, options = {}) {
2284
2286
  var CONTENT_LANGUAGE_PATTERN = /^(.*)\.([a-z]{2}(?:-[A-Z]{2})?)\.md$/;
2285
2287
  function presetContentFiles(preset, variables) {
2286
2288
  const defaultLanguage = defaultLanguageForLocale(variables.locale);
2287
- return copyTemplateTree(`presets/${preset.name}/content`).map((file) => {
2289
+ const files = copyTemplateTree(`presets/${preset.name}/content`);
2290
+ if (preset.languages.length <= 1) {
2291
+ const selected = /* @__PURE__ */ new Map();
2292
+ for (const file of files) {
2293
+ const match = CONTENT_LANGUAGE_PATTERN.exec(file.path);
2294
+ if (!match || !SCAFFOLD_LANGUAGES.includes(match[2])) {
2295
+ selected.set(file.path, { file, score: 0 });
2296
+ continue;
2297
+ }
2298
+ const [, base, language] = match;
2299
+ const score = language === defaultLanguage ? 2 : language === "en" ? 1 : 0;
2300
+ const current = selected.get(base);
2301
+ if (!current || score > current.score) {
2302
+ selected.set(base, { file, score });
2303
+ }
2304
+ }
2305
+ return [...selected.entries()].map(([relativePath, { file }]) => ({
2306
+ path: `content/${relativePath.endsWith(".md") ? relativePath : `${relativePath}.md`}`,
2307
+ content: replaceTitle(file.content, variables.title)
2308
+ }));
2309
+ }
2310
+ return files.map((file) => {
2288
2311
  const match = CONTENT_LANGUAGE_PATTERN.exec(file.path);
2289
2312
  let relativePath = file.path;
2290
2313
  if (match && SCAFFOLD_LANGUAGES.includes(match[2])) {
2291
2314
  const [, base, language] = match;
2292
2315
  relativePath = language === defaultLanguage ? `${base}.md` : `${base}.${language}.md`;
2293
2316
  }
2294
- const content = typeof file.content === "string" ? file.content.split("{{title}}").join(variables.title) : file.content;
2295
- return { path: `content/${relativePath}`, content };
2317
+ return {
2318
+ path: `content/${relativePath}`,
2319
+ content: replaceTitle(file.content, variables.title)
2320
+ };
2296
2321
  });
2297
2322
  }
2323
+ function replaceTitle(content, title) {
2324
+ return typeof content === "string" ? content.split("{{title}}").join(title) : content;
2325
+ }
2298
2326
  function packageJson(preset, variables, options) {
2299
2327
  const dependencies = {
2300
2328
  "@riebeckite/core": RIEBECKITE_VERSION,
@@ -2324,6 +2352,7 @@ function packageJson(preset, variables, options) {
2324
2352
  dependencies,
2325
2353
  devDependencies: {
2326
2354
  "@hono/vite-build": "^1.11.1",
2355
+ "@hono/node-server": "^1.19.17",
2327
2356
  "@riebeckite/cli": RIEBECKITE_VERSION,
2328
2357
  "@types/node": "^24.5.2",
2329
2358
  typescript: "^5.0.0",
@@ -2489,7 +2518,9 @@ function readmeEn(preset, variables) {
2489
2518
  ];
2490
2519
  if (preset.readme !== "short") {
2491
2520
  lines.push(`## ${words.included}`, "");
2492
- lines.push(`- **${words.languages}**: ${languagesLabel(preset, words)}`);
2521
+ lines.push(
2522
+ `- **${words.languages}**: ${languagesLabel(preset, variables, words)}`
2523
+ );
2493
2524
  lines.push(
2494
2525
  `- **${words.theme}**: ${preset.theme ? `\`${preset.theme.package}\`` : words.none}`
2495
2526
  );
@@ -2550,7 +2581,9 @@ function readmeJa(preset, variables) {
2550
2581
  ];
2551
2582
  if (preset.readme !== "short") {
2552
2583
  lines.push(`## ${words.included}`, "");
2553
- lines.push(`- **${words.languages}**: ${languagesLabel(preset, words)}`);
2584
+ lines.push(
2585
+ `- **${words.languages}**: ${languagesLabel(preset, variables, words)}`
2586
+ );
2554
2587
  lines.push(
2555
2588
  `- **${words.theme}**: ${preset.theme ? `\`${preset.theme.package}\`` : words.none}`
2556
2589
  );
@@ -2629,10 +2662,12 @@ function deploymentSecretLines(language) {
2629
2662
  ""
2630
2663
  ];
2631
2664
  }
2632
- function languagesLabel(preset, words) {
2665
+ function languagesLabel(preset, variables, words) {
2633
2666
  if (preset.languages.length === 0) return words.none;
2634
- if (preset.languages.length === 1)
2635
- return `${preset.languages[0]} (${words.single})`;
2667
+ if (preset.languages.length === 1) {
2668
+ const language = preset.name === "starter" ? defaultLanguageForLocale(variables.locale) : preset.languages[0];
2669
+ return `${language} (${words.single})`;
2670
+ }
2636
2671
  return preset.languages.join(", ");
2637
2672
  }
2638
2673
  function pagesLabel(preset) {
@@ -3143,8 +3178,17 @@ async function scaffoldRiebeckiteSite(options) {
3143
3178
  await fs2.mkdir(path3.dirname(filePath), { recursive: true });
3144
3179
  await fs2.writeFile(filePath, file.content);
3145
3180
  }
3181
+ await ensureContentDirectory(targetDirectory, files);
3146
3182
  return { targetDirectory, files: files.map((file) => file.path) };
3147
3183
  }
3184
+ async function ensureContentDirectory(targetDirectory, files) {
3185
+ if (files.some(
3186
+ (file) => file.path === "content" || file.path.startsWith("content/")
3187
+ )) {
3188
+ return;
3189
+ }
3190
+ await fs2.mkdir(path3.join(targetDirectory, "content"), { recursive: true });
3191
+ }
3148
3192
  function externalContentSource(deployment) {
3149
3193
  if (deployment.type !== "github-actions") return void 0;
3150
3194
  return deployment.content.type === "external" ? deployment.content : void 0;
@@ -1,5 +1,5 @@
1
1
  import type { SiteTemplateFile } from "./templates.js";
2
- export declare const SCAFFOLD_UTILITY_NAMES: readonly ["editorconfig", "gitattributes", "biome", "npmrc", "vscode"];
2
+ export declare const SCAFFOLD_UTILITY_NAMES: readonly ["gitignore", "editorconfig", "gitattributes", "biome", "npmrc", "vscode"];
3
3
  export type ScaffoldUtilityName = (typeof SCAFFOLD_UTILITY_NAMES)[number];
4
4
  export type ScaffoldUtilitySummary = {
5
5
  readonly name: ScaffoldUtilityName;
@@ -1 +1 @@
1
- export declare const RIEBECKITE_VERSION = "^0.0.21";
1
+ export declare const RIEBECKITE_VERSION = "^0.0.23";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-riebeckite",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "esbuild": "^0.28.2",
42
- "@riebeckite/test": "0.0.21"
42
+ "@riebeckite/test": "0.0.23"
43
43
  },
44
44
  "repository": {
45
45
  "type": "git",
@@ -1,48 +1,12 @@
1
- import type { ContentBodySlots, PostContent } from "@riebeckite/core";
2
- import {
3
- Article,
4
- ArticleBody,
5
- ArticleContent,
6
- ArticleLayout,
7
- ContentSlot,
8
- } from "@riebeckite/honox/ui";
1
+ import type { PostContent } from "@riebeckite/core";
2
+ import { Article, ArticleBody, ArticleContent } from "@riebeckite/honox/ui";
9
3
 
10
- export function SiteArticle({
11
- post,
12
- bodySlots,
13
- asideContent,
14
- }: {
15
- post: PostContent;
16
- bodySlots?: ContentBodySlots;
17
- asideContent?: unknown;
18
- }) {
4
+ export function SiteArticle({ post }: { post: PostContent }) {
19
5
  return (
20
6
  <Article class="site-article">
21
- <ArticleLayout>
22
- {asideContent}
23
-
24
- <ContentSlot
25
- slots={bodySlots}
26
- name="article.aside"
27
- class="site-article__aside"
28
- />
29
-
30
- <ArticleContent>
31
- <ContentSlot slots={bodySlots} name="article.header" />
32
- <ContentSlot slots={bodySlots} name="article.metadata" />
33
- <ContentSlot slots={bodySlots} name="article.before-content" />
34
-
35
- <ArticleBody html={post.html ?? ""} />
36
-
37
- <ContentSlot slots={bodySlots} name="article.after-content" />
38
- </ArticleContent>
39
-
40
- <ContentSlot
41
- slots={bodySlots}
42
- name="article.footer"
43
- class="site-article__footer"
44
- />
45
- </ArticleLayout>
7
+ <ArticleContent>
8
+ <ArticleBody html={post.html ?? ""} />
9
+ </ArticleContent>
46
10
  </Article>
47
11
  );
48
12
  }
@@ -20,10 +20,7 @@ export default createRoute(
20
20
  }
21
21
 
22
22
  return c.render(
23
- <SiteArticle
24
- post={resolved.post}
25
- bodySlots={resolved.entry.bodySlots}
26
- />,
23
+ <SiteArticle post={resolved.post} />,
27
24
  );
28
25
  },
29
26
  );
@@ -14,6 +14,6 @@ export default createRoute(async (c) => {
14
14
  }
15
15
 
16
16
  return c.render(
17
- <SiteArticle post={home.post} bodySlots={home.entry.bodySlots} />,
17
+ <SiteArticle post={home.post} />,
18
18
  );
19
19
  });
@@ -29,37 +29,11 @@ body {
29
29
  margin: 0 auto;
30
30
  }
31
31
 
32
- .rb-article-body,
33
- .site-article__footer {
32
+ .rb-article-body {
34
33
  min-width: 0;
35
34
  padding-inline: var(--rb-space-2, 1rem);
36
35
  }
37
36
 
38
- .site-article__aside {
39
- display: none;
40
- }
41
-
42
- @media (min-width: 88rem) {
43
- :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__footer {
44
- grid-column: 1;
45
- }
46
-
47
- :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__aside {
48
- display: block;
49
- grid-column: 1;
50
- grid-row: 1;
51
- align-self: start;
52
- width: var(--rb-layout-sidebar, 14rem);
53
- margin-inline-start: calc(
54
- -1 * var(--rb-layout-sidebar, 14rem) -
55
- var(--rb-layout-gap, 2rem)
56
- );
57
- padding-inline-end: 1rem;
58
- border-inline-end: 1px solid
59
- color-mix(in oklab, currentColor 18%, transparent);
60
- }
61
- }
62
-
63
37
  .not-found {
64
38
  width: min(100% - 2rem, var(--rb-layout-article-max, 48rem));
65
39
  margin-block: auto;
@@ -1,57 +1,32 @@
1
- import type { ContentBodySlots, PostContent } from "@riebeckite/core";
2
- import {
3
- Article,
4
- ArticleBody,
5
- ArticleContent,
6
- ArticleFooter,
7
- ArticleLayout,
8
- ContentSlot,
9
- hasSlot,
10
- } from "@riebeckite/honox/ui";
1
+ import type { PostContent } from "@riebeckite/core";
2
+ import { Article, ArticleBody, ArticleContent, ArticleFooter } from "@riebeckite/honox/ui";
11
3
 
12
- export function SiteArticle({
13
- post,
14
- bodySlots,
15
- asideContent,
16
- afterContent,
17
- footerContent,
18
- }: {
4
+ type SiteArticleProps = {
19
5
  post: PostContent;
20
- bodySlots?: ContentBodySlots;
21
- asideContent?: unknown;
6
+ header?: unknown;
7
+ metadata?: unknown;
8
+ aside?: unknown;
22
9
  afterContent?: unknown;
23
- footerContent?: unknown;
24
- }) {
10
+ footer?: unknown;
11
+ };
12
+
13
+ export function SiteArticle(props: SiteArticleProps) {
25
14
  return (
26
15
  <Article class="site-article">
27
- <ArticleLayout>
28
- {asideContent}
29
-
30
- <ContentSlot
31
- slots={bodySlots}
32
- name="article.aside"
33
- class="site-article__aside"
34
- />
35
-
16
+ <div class="site-article__layout">
36
17
  <ArticleContent>
37
- <ContentSlot slots={bodySlots} name="article.header" />
38
- <ContentSlot slots={bodySlots} name="article.metadata" />
39
- <ContentSlot slots={bodySlots} name="article.before-content" />
40
-
41
- <ArticleBody html={post.html ?? ""} />
42
-
43
- {afterContent}
44
-
45
- <ContentSlot slots={bodySlots} name="article.after-content" />
18
+ {props.header}
19
+ {props.metadata}
20
+ <ArticleBody html={props.post.html ?? ""} />
21
+ {props.afterContent}
22
+ {props.footer ? (
23
+ <ArticleFooter class="site-article__footer">
24
+ {props.footer}
25
+ </ArticleFooter>
26
+ ) : null}
46
27
  </ArticleContent>
47
-
48
- {hasSlot(bodySlots, "article.footer") || footerContent ? (
49
- <ArticleFooter class="site-article__footer">
50
- <ContentSlot slots={bodySlots} name="article.footer" />
51
- {footerContent}
52
- </ArticleFooter>
53
- ) : null}
54
- </ArticleLayout>
28
+ {props.aside ? <aside class="site-article__aside">{props.aside}</aside> : null}
29
+ </div>
55
30
  </Article>
56
31
  );
57
32
  }
@@ -4,9 +4,16 @@ import {
4
4
  riebeckiteSsgParams,
5
5
  } from "@riebeckite/honox/server";
6
6
  import { PageBody } from "@riebeckite/honox/ui";
7
+ import { createPublicContentView } from "@riebeckite/core";
8
+ import { Backlinks, getBacklinks } from "@riebeckite/plugin-backlinks";
9
+ import { Breadcrumbs, getBreadcrumbs } from "@riebeckite/plugin-breadcrumbs";
10
+ import { Properties, getProperties } from "@riebeckite/plugin-properties";
11
+ import { getRelatedPosts, RelatedPosts, resolveRelatedPostsOptions } from "@riebeckite/plugin-related-posts";
12
+ import { getShareLinks, ShareControls } from "@riebeckite/plugin-share";
7
13
  import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";
8
14
  import { createRoute } from "honox/factory";
9
15
  import { SiteArticle } from "../components/article";
16
+ import { config } from "virtual:riebeckite/config";
10
17
  import { content } from "virtual:riebeckite/content";
11
18
 
12
19
  export default createRoute(
@@ -21,12 +28,19 @@ export default createRoute(
21
28
  }
22
29
 
23
30
  const tableOfContents = extractTableOfContents(resolved.post.html ?? "");
31
+ const manifest = await content.getManifest();
32
+ const view = createPublicContentView(manifest);
33
+ const entry = view.findRoutableBySlug(resolved.entry.slug);
34
+ if (!entry) return c.notFound();
35
+ const relatedOptions = resolveRelatedPostsOptions({});
24
36
 
25
37
  return c.render(
26
38
  <SiteArticle
27
39
  post={resolved.post}
28
- bodySlots={resolved.entry.bodySlots}
29
- asideContent={<TableOfContents className="rr-table-of-contents--desktop" items={tableOfContents} />}
40
+ header={<Breadcrumbs items={getBreadcrumbs({ view, entry, homeLabel: config.site.title })} />}
41
+ metadata={<Properties data={getProperties({ view, frontmatter: resolved.post.frontmatter, options: { include: ["created", "modified", "tags", "status"], order: ["created", "modified", "tags", "status"] } })} />}
42
+ aside={<TableOfContents className="rr-table-of-contents--desktop" items={tableOfContents} />}
43
+ footer={<><ShareControls {...getShareLinks({ entry, site: config })} /><Backlinks backlinks={getBacklinks({ view, slug: entry.slug })} /><RelatedPosts entries={getRelatedPosts({ view, entry, options: relatedOptions })} options={relatedOptions} /></>}
30
44
  />,
31
45
  );
32
46
  },
@@ -1,7 +1,13 @@
1
1
  import { resolveRiebeckiteHomeRequest } from "@riebeckite/honox/server";
2
2
  import { PageBody } from "@riebeckite/honox/ui";
3
+ import { createPublicContentView } from "@riebeckite/core";
4
+ import { Backlinks, getBacklinks } from "@riebeckite/plugin-backlinks";
5
+ import { Breadcrumbs, getBreadcrumbs } from "@riebeckite/plugin-breadcrumbs";
6
+ import { Properties, getProperties } from "@riebeckite/plugin-properties";
7
+ import { getRelatedPosts, RelatedPosts, resolveRelatedPostsOptions } from "@riebeckite/plugin-related-posts";
3
8
  import { DailyNotes, getDailyNotes } from "@riebeckite/plugin-daily-notes";
4
9
  import { getRecentPosts, RecentPosts } from "@riebeckite/plugin-recent-posts";
10
+ import { getShareLinks, ShareControls } from "@riebeckite/plugin-share";
5
11
  import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";
6
12
  import { createRoute } from "honox/factory";
7
13
  import { SiteArticle } from "../components/article";
@@ -21,13 +27,19 @@ export default createRoute(async (c) => {
21
27
  const recentPosts = getRecentPosts({ manifest });
22
28
  const dailyNotes = getDailyNotes({ manifest, config });
23
29
  const tableOfContents = extractTableOfContents(home.post.html ?? "");
30
+ const view = createPublicContentView(manifest);
31
+ const entry = view.findRoutableBySlug(home.entry.slug);
32
+ if (!entry) return c.notFound();
33
+ const relatedOptions = resolveRelatedPostsOptions({});
24
34
 
25
35
  return c.render(
26
36
  <SiteArticle
27
37
  post={home.post}
28
- bodySlots={home.entry.bodySlots}
29
- asideContent={<TableOfContents className="rr-table-of-contents--desktop" items={tableOfContents} />}
38
+ header={<Breadcrumbs items={getBreadcrumbs({ view, entry, homeLabel: config.site.title })} />}
39
+ metadata={<Properties data={getProperties({ view, frontmatter: home.post.frontmatter, options: { include: ["created", "modified", "tags", "status"], order: ["created", "modified", "tags", "status"] } })} />}
40
+ aside={<TableOfContents className="rr-table-of-contents--desktop" items={tableOfContents} />}
30
41
  afterContent={<><RecentPosts posts={recentPosts} /><DailyNotes notes={dailyNotes} /></>}
42
+ footer={<><ShareControls {...getShareLinks({ entry, site: config })} /><Backlinks backlinks={getBacklinks({ view, slug: entry.slug })} /><RelatedPosts entries={getRelatedPosts({ view, entry, options: relatedOptions })} options={relatedOptions} /></>}
31
43
  />,
32
44
  );
33
45
  });
@@ -14,6 +14,7 @@ body {
14
14
  }
15
15
 
16
16
  .riebeckite-page {
17
+ --site-toc-sticky-top: var(--rb-space-3, 1.5rem);
17
18
  display: flex;
18
19
  min-height: 100vh;
19
20
  width: 100%;
@@ -99,7 +100,14 @@ body {
99
100
  border-top: 1px solid var(--rb-color-border);
100
101
  }
101
102
 
102
- @media (max-width: 48rem) {
103
+ @media (min-width: 30.001rem) and (max-width: 48rem) {
104
+ .rb-site-header,
105
+ .rb-nav > .rb-nav__list {
106
+ flex-wrap: wrap;
107
+ }
108
+ }
109
+
110
+ @media (max-width: 30rem) {
103
111
  .riebeckite-page {
104
112
  padding-block: var(--rb-space-3, 1.5rem);
105
113
  }
@@ -158,9 +166,12 @@ body {
158
166
  }
159
167
 
160
168
  .site-article {
161
- display: block;
162
- width: min(100% - 2rem, var(--rb-layout-article-max, 48rem));
169
+ width: min(100% - 2rem, var(--rb-layout-page-max, 80rem));
163
170
  padding-block: 2rem;
171
+ }
172
+
173
+ .site-article__layout {
174
+ width: min(100%, var(--rb-layout-article-max, 48rem));
164
175
  margin: 0 auto;
165
176
  }
166
177
 
@@ -174,24 +185,29 @@ body {
174
185
  display: none;
175
186
  }
176
187
 
177
- @media (min-width: 88rem) {
178
- :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__footer {
179
- grid-column: 1;
188
+ @media (min-width: 64rem) {
189
+ .site-article__layout {
190
+ display: grid;
191
+ width: 100%;
192
+ grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem)) minmax(0, var(--rb-layout-sidebar, 14rem));
193
+ gap: var(--rb-layout-gap, 2rem);
194
+ align-items: start;
180
195
  }
181
196
 
182
- :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__aside {
197
+ .site-article__aside {
183
198
  display: block;
184
- grid-column: 1;
185
- grid-row: 1;
186
- align-self: start;
187
- width: var(--rb-layout-sidebar, 14rem);
188
- margin-inline-start: calc(
189
- -1 * var(--rb-layout-sidebar, 14rem) -
190
- var(--rb-layout-gap, 2rem)
191
- );
192
- padding-inline-end: 1rem;
193
- border-inline-end: 1px solid
194
- color-mix(in oklab, currentColor 18%, transparent);
199
+ min-width: 0;
200
+ align-self: stretch;
201
+ }
202
+
203
+ .site-article__aside > .rr-table-of-contents--desktop {
204
+ position: sticky;
205
+ top: var(--site-toc-sticky-top);
206
+ max-height: calc(100dvh - var(--site-toc-sticky-top) - var(--rb-space-4, 2rem));
207
+ overflow-y: auto;
208
+ overscroll-behavior: contain;
209
+ border-inline-start: 1px solid color-mix(in oklab, currentColor 18%, transparent);
210
+ padding-inline-start: 1rem;
195
211
  }
196
212
  }
197
213
 
@@ -1,57 +1,32 @@
1
- import type { ContentBodySlots, PostContent } from "@riebeckite/core";
2
- import {
3
- Article,
4
- ArticleBody,
5
- ArticleContent,
6
- ArticleFooter,
7
- ArticleLayout,
8
- ContentSlot,
9
- hasSlot,
10
- } from "@riebeckite/honox/ui";
1
+ import type { PostContent } from "@riebeckite/core";
2
+ import { Article, ArticleBody, ArticleContent, ArticleFooter } from "@riebeckite/honox/ui";
11
3
 
12
- export function SiteArticle({
13
- post,
14
- bodySlots,
15
- asideContent,
16
- afterContent,
17
- footerContent,
18
- }: {
4
+ type SiteArticleProps = {
19
5
  post: PostContent;
20
- bodySlots?: ContentBodySlots;
21
- asideContent?: unknown;
6
+ header?: unknown;
7
+ metadata?: unknown;
8
+ aside?: unknown;
22
9
  afterContent?: unknown;
23
- footerContent?: unknown;
24
- }) {
10
+ footer?: unknown;
11
+ };
12
+
13
+ export function SiteArticle(props: SiteArticleProps) {
25
14
  return (
26
15
  <Article class="site-article">
27
- <ArticleLayout>
28
- {asideContent}
29
-
30
- <ContentSlot
31
- slots={bodySlots}
32
- name="article.aside"
33
- class="site-article__aside"
34
- />
35
-
16
+ <div class="site-article__layout">
36
17
  <ArticleContent>
37
- <ContentSlot slots={bodySlots} name="article.header" />
38
- <ContentSlot slots={bodySlots} name="article.metadata" />
39
- <ContentSlot slots={bodySlots} name="article.before-content" />
40
-
41
- <ArticleBody html={post.html ?? ""} />
42
-
43
- {afterContent}
44
-
45
- <ContentSlot slots={bodySlots} name="article.after-content" />
18
+ {props.header}
19
+ {props.metadata}
20
+ <ArticleBody html={props.post.html ?? ""} />
21
+ {props.afterContent}
22
+ {props.footer ? (
23
+ <ArticleFooter class="site-article__footer">
24
+ {props.footer}
25
+ </ArticleFooter>
26
+ ) : null}
46
27
  </ArticleContent>
47
-
48
- {hasSlot(bodySlots, "article.footer") || footerContent ? (
49
- <ArticleFooter class="site-article__footer">
50
- <ContentSlot slots={bodySlots} name="article.footer" />
51
- {footerContent}
52
- </ArticleFooter>
53
- ) : null}
54
- </ArticleLayout>
28
+ {props.aside ? <aside class="site-article__aside">{props.aside}</aside> : null}
29
+ </div>
55
30
  </Article>
56
31
  );
57
32
  }
@@ -4,9 +4,15 @@ import {
4
4
  riebeckiteSsgParams,
5
5
  } from "@riebeckite/honox/server";
6
6
  import { PageBody } from "@riebeckite/honox/ui";
7
+ import { createPublicContentView } from "@riebeckite/core";
8
+ import { Backlinks, getBacklinks } from "@riebeckite/plugin-backlinks";
9
+ import { Breadcrumbs, getBreadcrumbs } from "@riebeckite/plugin-breadcrumbs";
10
+ import { Properties, getProperties } from "@riebeckite/plugin-properties";
11
+ import { getRelatedPosts, RelatedPosts, resolveRelatedPostsOptions } from "@riebeckite/plugin-related-posts";
7
12
  import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";
8
13
  import { createRoute } from "honox/factory";
9
14
  import { SiteArticle } from "../components/article";
15
+ import { config } from "virtual:riebeckite/config";
10
16
  import { content } from "virtual:riebeckite/content";
11
17
 
12
18
  export default createRoute(
@@ -21,12 +27,19 @@ export default createRoute(
21
27
  }
22
28
 
23
29
  const tableOfContents = extractTableOfContents(resolved.post.html ?? "");
30
+ const manifest = await content.getManifest();
31
+ const view = createPublicContentView(manifest);
32
+ const entry = view.findRoutableBySlug(resolved.entry.slug);
33
+ if (!entry) return c.notFound();
34
+ const relatedOptions = resolveRelatedPostsOptions({});
24
35
 
25
36
  return c.render(
26
37
  <SiteArticle
27
38
  post={resolved.post}
28
- bodySlots={resolved.entry.bodySlots}
29
- asideContent={<TableOfContents className="rr-table-of-contents--desktop" items={tableOfContents} />}
39
+ header={<Breadcrumbs items={getBreadcrumbs({ view, entry, homeLabel: config.site.title })} />}
40
+ metadata={<Properties data={getProperties({ view, frontmatter: resolved.post.frontmatter, options: { include: ["created", "modified", "tags", "status"], order: ["created", "modified", "tags", "status"] } })} />}
41
+ aside={<TableOfContents className="rr-table-of-contents--desktop" items={tableOfContents} />}
42
+ footer={<><Backlinks backlinks={getBacklinks({ view, slug: entry.slug })} /><RelatedPosts entries={getRelatedPosts({ view, entry, options: relatedOptions })} options={relatedOptions} /></>}
30
43
  />,
31
44
  );
32
45
  },
@@ -1,9 +1,15 @@
1
1
  import { resolveRiebeckiteHomeRequest } from "@riebeckite/honox/server";
2
2
  import { PageBody } from "@riebeckite/honox/ui";
3
+ import { createPublicContentView } from "@riebeckite/core";
4
+ import { Backlinks, getBacklinks } from "@riebeckite/plugin-backlinks";
5
+ import { Breadcrumbs, getBreadcrumbs } from "@riebeckite/plugin-breadcrumbs";
6
+ import { Properties, getProperties } from "@riebeckite/plugin-properties";
7
+ import { getRelatedPosts, RelatedPosts, resolveRelatedPostsOptions } from "@riebeckite/plugin-related-posts";
3
8
  import { getRecentPosts, RecentPosts } from "@riebeckite/plugin-recent-posts";
4
9
  import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";
5
10
  import { createRoute } from "honox/factory";
6
11
  import { SiteArticle } from "../components/article";
12
+ import { config } from "virtual:riebeckite/config";
7
13
  import { content } from "virtual:riebeckite/content";
8
14
 
9
15
  export default createRoute(async (c) => {
@@ -15,15 +21,22 @@ export default createRoute(async (c) => {
15
21
  return c.render(<PageBody html={home.page.body} />);
16
22
  }
17
23
 
18
- const recentPosts = getRecentPosts({ manifest: await content.getManifest() });
24
+ const manifest = await content.getManifest();
25
+ const recentPosts = getRecentPosts({ manifest });
19
26
  const tableOfContents = extractTableOfContents(home.post.html ?? "");
27
+ const view = createPublicContentView(manifest);
28
+ const entry = view.findRoutableBySlug(home.entry.slug);
29
+ if (!entry) return c.notFound();
30
+ const relatedOptions = resolveRelatedPostsOptions({});
20
31
 
21
32
  return c.render(
22
33
  <SiteArticle
23
34
  post={home.post}
24
- bodySlots={home.entry.bodySlots}
25
- asideContent={<TableOfContents className="rr-table-of-contents--desktop" items={tableOfContents} />}
26
- afterContent={<><RecentPosts posts={recentPosts} /></>}
35
+ header={<Breadcrumbs items={getBreadcrumbs({ view, entry, homeLabel: config.site.title })} />}
36
+ metadata={<Properties data={getProperties({ view, frontmatter: home.post.frontmatter, options: { include: ["created", "modified", "tags", "status"], order: ["created", "modified", "tags", "status"] } })} />}
37
+ aside={<TableOfContents className="rr-table-of-contents--desktop" items={tableOfContents} />}
38
+ afterContent={<RecentPosts posts={recentPosts} />}
39
+ footer={<><Backlinks backlinks={getBacklinks({ view, slug: entry.slug })} /><RelatedPosts entries={getRelatedPosts({ view, entry, options: relatedOptions })} options={relatedOptions} /></>}
27
40
  />,
28
41
  );
29
42
  });
@@ -14,6 +14,7 @@ body {
14
14
  }
15
15
 
16
16
  .riebeckite-page {
17
+ --site-toc-sticky-top: var(--rb-space-3, 1.5rem);
17
18
  display: flex;
18
19
  min-height: 100vh;
19
20
  width: 100%;
@@ -99,7 +100,14 @@ body {
99
100
  border-top: 1px solid var(--rb-color-border);
100
101
  }
101
102
 
102
- @media (max-width: 48rem) {
103
+ @media (min-width: 30.001rem) and (max-width: 48rem) {
104
+ .rb-site-header,
105
+ .rb-nav > .rb-nav__list {
106
+ flex-wrap: wrap;
107
+ }
108
+ }
109
+
110
+ @media (max-width: 30rem) {
103
111
  .riebeckite-page {
104
112
  padding-block: var(--rb-space-3, 1.5rem);
105
113
  }
@@ -158,9 +166,12 @@ body {
158
166
  }
159
167
 
160
168
  .site-article {
161
- display: block;
162
- width: min(100% - 2rem, var(--rb-layout-article-max, 48rem));
169
+ width: min(100% - 2rem, var(--rb-layout-page-max, 80rem));
163
170
  padding-block: 2rem;
171
+ }
172
+
173
+ .site-article__layout {
174
+ width: min(100%, var(--rb-layout-article-max, 48rem));
164
175
  margin: 0 auto;
165
176
  }
166
177
 
@@ -174,24 +185,29 @@ body {
174
185
  display: none;
175
186
  }
176
187
 
177
- @media (min-width: 88rem) {
178
- :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__footer {
179
- grid-column: 1;
188
+ @media (min-width: 64rem) {
189
+ .site-article__layout {
190
+ display: grid;
191
+ width: 100%;
192
+ grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem)) minmax(0, var(--rb-layout-sidebar, 14rem));
193
+ gap: var(--rb-layout-gap, 2rem);
194
+ align-items: start;
180
195
  }
181
196
 
182
- :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__aside {
197
+ .site-article__aside {
183
198
  display: block;
184
- grid-column: 1;
185
- grid-row: 1;
186
- align-self: start;
187
- width: var(--rb-layout-sidebar, 14rem);
188
- margin-inline-start: calc(
189
- -1 * var(--rb-layout-sidebar, 14rem) -
190
- var(--rb-layout-gap, 2rem)
191
- );
192
- padding-inline-end: 1rem;
193
- border-inline-end: 1px solid
194
- color-mix(in oklab, currentColor 18%, transparent);
199
+ min-width: 0;
200
+ align-self: stretch;
201
+ }
202
+
203
+ .site-article__aside > .rr-table-of-contents--desktop {
204
+ position: sticky;
205
+ top: var(--site-toc-sticky-top);
206
+ max-height: calc(100dvh - var(--site-toc-sticky-top) - var(--rb-space-4, 2rem));
207
+ overflow-y: auto;
208
+ overscroll-behavior: contain;
209
+ border-inline-start: 1px solid color-mix(in oklab, currentColor 18%, transparent);
210
+ padding-inline-start: 1rem;
195
211
  }
196
212
  }
197
213
 
@@ -5,12 +5,9 @@ date: 2026-09-30
5
5
  tags: [riebeckite, project]
6
6
  status: active
7
7
  aliases: [/start-here/]
8
- series: publish-a-site
9
- series_title: Publish a site
10
- series_order: 1
11
8
  publish: true
12
9
  ---
13
10
 
14
11
  # Planning a Markdown site
15
12
 
16
- Start with [[writing]] to see backlinks, related posts, tags, and the series navigation working together.
13
+ Start with [[writing]] to see backlinks, related posts, and tags working together.
@@ -4,12 +4,9 @@ description: A second connected note for search and related-content examples.
4
4
  date: 2026-10-01
5
5
  tags: [riebeckite, project]
6
6
  status: active
7
- series: publish-a-site
8
- series_title: Publish a site
9
- series_order: 2
10
7
  publish: true
11
8
  ---
12
9
 
13
10
  # Writing the first note
14
11
 
15
- This note links back to [[planning]]. Search for **Markdown site** or browse the generated tags.
12
+ This note links back to [[planning]]. Search for **Markdown site** or browse the generated tags.
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ dist/
3
+ .riebeckite/
4
+ app/.riebeckite/
5
+ .wrangler/
6
+ .dev.vars