create-riebeckite 0.0.16 → 0.0.18

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.
Files changed (76) hide show
  1. package/README.md +7 -7
  2. package/README_ja.md +2 -2
  3. package/dist/src/arguments.d.ts +3 -5
  4. package/dist/src/index.js +3161 -37
  5. package/dist/src/interactive.d.ts +8 -9
  6. package/dist/src/scaffold/content-pages.d.ts +16 -0
  7. package/dist/src/scaffold/deployment.d.ts +7 -0
  8. package/dist/src/scaffold/index.d.ts +7 -0
  9. package/dist/src/scaffold/index.js +3134 -0
  10. package/dist/src/scaffold/next-steps.d.ts +7 -0
  11. package/dist/src/scaffold/options.d.ts +43 -0
  12. package/dist/src/scaffold/presets.d.ts +102 -0
  13. package/dist/src/scaffold/template-loader.d.ts +4 -0
  14. package/dist/src/scaffold/templates.d.ts +53 -0
  15. package/dist/src/scaffold/version.d.ts +1 -0
  16. package/dist/src/scaffold/wrangler-defaults.d.ts +46 -0
  17. package/package.json +16 -5
  18. package/templates/scaffold/base/app/client.ts +14 -0
  19. package/templates/scaffold/base/app/config.ts +18 -0
  20. package/templates/scaffold/base/app/constants/paths.ts +10 -0
  21. package/templates/scaffold/base/app/content.ts +12 -0
  22. package/templates/scaffold/base/app/global.d.ts +15 -0
  23. package/templates/scaffold/base/app/server.ts +13 -0
  24. package/templates/scaffold/base/public/favicon.ico +0 -0
  25. package/templates/scaffold/base/public/riebeckite-logo.png +0 -0
  26. package/templates/scaffold/base/tsconfig.json +30 -0
  27. package/templates/scaffold/base/vite.config.ts +34 -0
  28. package/templates/scaffold/biome.json +6 -0
  29. package/templates/scaffold/deployment/github-actions/content-checkout.yml +6 -0
  30. package/templates/scaffold/deployment/github-actions/deploy.yml +54 -0
  31. package/templates/scaffold/deployment/github-actions/notify-site.yml +31 -0
  32. package/templates/scaffold/presets/empty/app/routes/_renderer.tsx +21 -0
  33. package/templates/scaffold/presets/empty/app/routes/index.tsx +11 -0
  34. package/templates/scaffold/presets/empty/app/style.css +17 -0
  35. package/templates/scaffold/presets/minimal/app/components/article.tsx +66 -0
  36. package/templates/scaffold/presets/minimal/app/routes/[slug{.+}].tsx +48 -0
  37. package/templates/scaffold/presets/minimal/app/routes/_renderer.tsx +59 -0
  38. package/templates/scaffold/presets/minimal/app/routes/index.tsx +29 -0
  39. package/templates/scaffold/presets/minimal/app/style.css +84 -0
  40. package/templates/scaffold/presets/minimal/content/index.en.md +12 -0
  41. package/templates/scaffold/presets/showcase/app/components/article.tsx +66 -0
  42. package/templates/scaffold/presets/showcase/app/components/site-header.tsx +113 -0
  43. package/templates/scaffold/presets/showcase/app/routes/[slug{.+}].tsx +52 -0
  44. package/templates/scaffold/presets/showcase/app/routes/_renderer.tsx +69 -0
  45. package/templates/scaffold/presets/showcase/app/routes/index.tsx +39 -0
  46. package/templates/scaffold/presets/showcase/app/style.css +227 -0
  47. package/templates/scaffold/presets/showcase/content/attachments/project-brief.pdf +5 -0
  48. package/templates/scaffold/presets/showcase/content/demo/planning.md +16 -0
  49. package/templates/scaffold/presets/showcase/content/demo/writing.md +15 -0
  50. package/templates/scaffold/presets/showcase/content/drawings/Architecture.excalidraw +1 -0
  51. package/templates/scaffold/presets/showcase/content/drawings/site.canvas +1 -0
  52. package/templates/scaffold/presets/showcase/content/guide.en.md +46 -0
  53. package/templates/scaffold/presets/showcase/content/images/demo.svg +1 -0
  54. package/templates/scaffold/presets/showcase/content/index.de.md +17 -0
  55. package/templates/scaffold/presets/showcase/content/index.en.md +17 -0
  56. package/templates/scaffold/presets/showcase/content/index.es.md +17 -0
  57. package/templates/scaffold/presets/showcase/content/index.fr.md +17 -0
  58. package/templates/scaffold/presets/showcase/content/index.ja.md +17 -0
  59. package/templates/scaffold/presets/showcase/content/index.ko.md +17 -0
  60. package/templates/scaffold/presets/showcase/content/index.zh-CN.md +17 -0
  61. package/templates/scaffold/presets/starter/app/components/article.tsx +66 -0
  62. package/templates/scaffold/presets/starter/app/components/site-header.tsx +113 -0
  63. package/templates/scaffold/presets/starter/app/routes/[slug{.+}].tsx +52 -0
  64. package/templates/scaffold/presets/starter/app/routes/_renderer.tsx +69 -0
  65. package/templates/scaffold/presets/starter/app/routes/index.tsx +37 -0
  66. package/templates/scaffold/presets/starter/app/style.css +227 -0
  67. package/templates/scaffold/presets/starter/content/guide.en.md +60 -0
  68. package/templates/scaffold/presets/starter/content/index.de.md +19 -0
  69. package/templates/scaffold/presets/starter/content/index.en.md +19 -0
  70. package/templates/scaffold/presets/starter/content/index.es.md +19 -0
  71. package/templates/scaffold/presets/starter/content/index.fr.md +19 -0
  72. package/templates/scaffold/presets/starter/content/index.ja.md +19 -0
  73. package/templates/scaffold/presets/starter/content/index.ko.md +19 -0
  74. package/templates/scaffold/presets/starter/content/index.zh-CN.md +19 -0
  75. package/templates/scaffold/presets/starter/content/notes/planning.md +16 -0
  76. package/templates/scaffold/presets/starter/content/notes/writing.md +15 -0
@@ -0,0 +1,66 @@
1
+ import type { PostContent } from "@riebeckite/core";
2
+ import { Article, ArticleContent, ArticleLayout } from "@riebeckite/honox/ui";
3
+
4
+ export function SiteArticle({
5
+ post,
6
+ bodySlots,
7
+ asideContent,
8
+ afterContent,
9
+ footerContent,
10
+ }: {
11
+ post: PostContent;
12
+ bodySlots?: Readonly<Record<string, string>>;
13
+ asideContent?: unknown;
14
+ afterContent?: unknown;
15
+ footerContent?: unknown;
16
+ }) {
17
+ return (
18
+ <Article class="site-article">
19
+ <ArticleLayout>
20
+ {asideContent}
21
+ {bodySlots?.["article.aside"] ? (
22
+ <div
23
+ class="site-article__aside"
24
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.aside"] }}
25
+ />
26
+ ) : null}
27
+ <ArticleContent>
28
+ {bodySlots?.["article.header"] ? (
29
+ <div
30
+ class="site-article__header"
31
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.header"] }}
32
+ />
33
+ ) : null}
34
+ {bodySlots?.["article.metadata"] ? (
35
+ <div
36
+ class="site-article__metadata"
37
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.metadata"] }}
38
+ />
39
+ ) : null}
40
+ {bodySlots?.["article.before-content"] ? (
41
+ <div
42
+ class="site-article__before-content"
43
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.before-content"] }}
44
+ />
45
+ ) : null}
46
+ <div dangerouslySetInnerHTML={{ __html: post.html ?? "" }} />
47
+ {afterContent}
48
+ {bodySlots?.["article.after-content"] ? (
49
+ <div
50
+ class="site-article__after-content"
51
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.after-content"] }}
52
+ />
53
+ ) : null}
54
+ </ArticleContent>
55
+ {bodySlots?.["article.footer"] || footerContent ? (
56
+ <div class="site-article__footer">
57
+ {bodySlots?.["article.footer"] ? (
58
+ <div dangerouslySetInnerHTML={{ __html: bodySlots["article.footer"] }} />
59
+ ) : null}
60
+ {footerContent}
61
+ </div>
62
+ ) : null}
63
+ </ArticleLayout>
64
+ </Article>
65
+ );
66
+ }
@@ -0,0 +1,48 @@
1
+ import { getEntryLanguage } from "@riebeckite/core";
2
+ import {
3
+ contentRouteSsgParams,
4
+ pluginPageSsgParams,
5
+ resolveRiebeckiteRoute,
6
+ } from "@riebeckite/honox/server";
7
+ import { createRoute } from "honox/factory";
8
+ import { SiteArticle } from "../components/article";
9
+ import { content } from "../content";
10
+
11
+ export default createRoute(
12
+ contentRouteSsgParams("/:slug{.+}", async () => {
13
+ const manifest = await content.getManifest();
14
+ const contentPaths = manifest.publicEntries
15
+ .filter((entry) => entry.permalink !== "/")
16
+ .map((entry) => ({ slug: entry.permalink.replace(/^\/+/, "") }));
17
+ return [...contentPaths, ...(await pluginPageSsgParams(content))];
18
+ }),
19
+ async (c) => {
20
+ const requestedSlug = c.req.param("slug");
21
+ if (!requestedSlug) return c.notFound();
22
+ if (/\.[a-zA-Z0-9]+$/.test(requestedSlug)) return c.notFound();
23
+
24
+ const route = await resolveRiebeckiteRoute(content, c.req.path);
25
+ if (!route) return c.notFound();
26
+ if (route.kind === "redirect")
27
+ return c.redirect(route.location, route.status);
28
+ if (route.kind === "page") {
29
+ c.set("headTags", route.page.headTags ?? []);
30
+ c.set("htmlLanguage", route.page.language);
31
+ return c.render(
32
+ <div dangerouslySetInnerHTML={{ __html: route.page.body }} />,
33
+ );
34
+ }
35
+
36
+ const post = await content.getProcessedContent(route.entry.slug);
37
+
38
+ c.set("htmlLanguage", getEntryLanguage(route.entry));
39
+ c.set("headTags", route.entry.headTags ?? []);
40
+
41
+ return c.render(
42
+ <SiteArticle
43
+ post={post}
44
+ bodySlots={route.entry.bodySlots}
45
+ />,
46
+ );
47
+ },
48
+ );
@@ -0,0 +1,59 @@
1
+ import type { PluginHeadTag } from "@riebeckite/core";
2
+ import { jsxRenderer } from "hono/jsx-renderer";
3
+ import { Link, Script } from "honox/server";
4
+ import { config } from "../config";
5
+
6
+ function themeAttributes() {
7
+ const { theme } = config;
8
+
9
+ return {
10
+ ...theme.attributes,
11
+ "data-theme": theme.colorMode === "system" ? undefined : theme.colorMode,
12
+ "data-theme-name": theme.name,
13
+ "data-typography": theme.typography,
14
+ "data-article-layout": theme.articleLayout,
15
+ };
16
+ }
17
+
18
+ // Riebeckite plugin client entries need the HonoX client bundle even without
19
+ // an island component.
20
+ export const __importing_islands = true;
21
+
22
+ export default jsxRenderer(async ({ children }, c) => {
23
+ const headTags: readonly PluginHeadTag[] = c.get("headTags") ?? [];
24
+
25
+ return (
26
+ <html
27
+ lang={c.get("htmlLanguage") ?? config.site.locale}
28
+ {...themeAttributes()}
29
+ >
30
+ <head>
31
+ <meta charset="utf-8" />
32
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
33
+ <title>{config.site.title}</title>
34
+ <link rel="icon" href="/favicon.ico" />
35
+ <Link href="/app/style.css" rel="stylesheet" />
36
+ {headTags.map(renderHeadTag)}
37
+ <Script src="/app/client.ts" async />
38
+ </head>
39
+ <body class="riebeckite-page rb-site">
40
+ {children}
41
+ </body>
42
+ </html>
43
+ );
44
+ });
45
+
46
+ function renderHeadTag(tag: PluginHeadTag, index: number) {
47
+ const key = `${tag.tag}-${index}`;
48
+ if (tag.tag === "meta") return <meta {...tag.attrs} key={key} />;
49
+ if (tag.tag === "link") return <link {...tag.attrs} key={key} />;
50
+ return (
51
+ <script
52
+ {...tag.attrs}
53
+ key={key}
54
+ dangerouslySetInnerHTML={
55
+ tag.children ? { __html: tag.children } : undefined
56
+ }
57
+ />
58
+ );
59
+ }
@@ -0,0 +1,29 @@
1
+ import { getEntryLanguage } from "@riebeckite/core";
2
+ import { createRoute } from "honox/factory";
3
+ import { SiteArticle } from "../components/article";
4
+ import { content } from "../content";
5
+
6
+ export default createRoute(async (c) => {
7
+ const manifest = await content.getManifest();
8
+ const indexEntry = manifest.bySlug.get("index");
9
+ if (indexEntry && indexEntry.permalink !== "/") {
10
+ return c.redirect(indexEntry.permalink, 308);
11
+ }
12
+
13
+ const post = await content.getProcessedContent("index");
14
+ if (indexEntry?.publishing?.routable === false) {
15
+ return c.notFound();
16
+ }
17
+
18
+ if (indexEntry) {
19
+ c.set("htmlLanguage", getEntryLanguage(indexEntry));
20
+ c.set("headTags", indexEntry.headTags ?? []);
21
+ }
22
+
23
+ return c.render(
24
+ <SiteArticle
25
+ post={post}
26
+ bodySlots={indexEntry?.bodySlots}
27
+ />,
28
+ );
29
+ });
@@ -0,0 +1,84 @@
1
+ @import "./.riebeckite/plugin-styles.css";
2
+ @import "./.riebeckite/theme-styles.css";
3
+
4
+ *,
5
+ *::before,
6
+ *::after {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ body {
11
+ margin: 0;
12
+ font-family: system-ui, sans-serif;
13
+ }
14
+
15
+ .riebeckite-page {
16
+ display: flex;
17
+ min-height: 100vh;
18
+ width: 100%;
19
+ flex-direction: column;
20
+ align-items: center;
21
+ padding-block: var(--rb-space-8, 4rem);
22
+ }
23
+
24
+ .site-article {
25
+ display: block;
26
+ width: min(100% - 2rem, var(--rb-layout-article-max, 48rem));
27
+ padding-block: 2rem;
28
+ margin: 0 auto;
29
+ }
30
+
31
+ .rb-article-layout {
32
+ display: grid;
33
+ grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem));
34
+ justify-content: center;
35
+ gap: var(--rb-layout-gap, 2rem);
36
+ }
37
+
38
+ .rb-article-body {
39
+ min-width: 0;
40
+ padding-inline: var(--rb-space-2, 1rem);
41
+ }
42
+
43
+ .site-article__aside {
44
+ display: none;
45
+ }
46
+
47
+ .site-article__footer {
48
+ min-width: 0;
49
+ }
50
+
51
+ @media (min-width: 88rem) {
52
+ :is(:root, .rb-theme-root)[data-article-layout="article"] .rb-article-layout {
53
+ grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem));
54
+ position: relative;
55
+ }
56
+
57
+ :is(:root, .rb-theme-root)[data-article-layout="article"] .rb-article-body {
58
+ grid-column: 1;
59
+ grid-row: 1;
60
+ }
61
+
62
+ :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__footer {
63
+ grid-column: 1;
64
+ }
65
+
66
+ :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__aside {
67
+ display: block;
68
+ grid-column: 1;
69
+ grid-row: 1;
70
+ position: sticky;
71
+ top: 4rem;
72
+ align-self: start;
73
+ width: var(--rb-layout-sidebar, 14rem);
74
+ max-height: calc(100dvh - 8rem);
75
+ margin-inline-start: calc(
76
+ -1 * var(--rb-layout-sidebar, 14rem) -
77
+ var(--rb-layout-gap, 2rem)
78
+ );
79
+ overflow: auto;
80
+ padding-inline-end: 1rem;
81
+ border-inline-end: 1px solid
82
+ color-mix(in oklab, currentColor 18%, transparent);
83
+ }
84
+ }
@@ -0,0 +1,12 @@
1
+ ---
2
+ publish: true
3
+ ---
4
+
5
+ # {{title}}
6
+ Welcome to your Riebeckite site.
7
+ Every page of this starter is available in seven languages. Switch with the selector below the page title.
8
+ ## What is Riebeckite?
9
+ 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.
10
+ ## Edit this site
11
+ Content lives in `content/` as plain Markdown. Add a file, give it `publish: true` in the frontmatter, and it appears in the built site.
12
+ Localized pages use the `<base>.<lang>.md` convention next to the default file — for example `about.ja.md`. The l10n plugin serves them under `/lang/` paths and links them automatically.
@@ -0,0 +1,66 @@
1
+ import type { PostContent } from "@riebeckite/core";
2
+ import { Article, ArticleContent, ArticleLayout } from "@riebeckite/honox/ui";
3
+
4
+ export function SiteArticle({
5
+ post,
6
+ bodySlots,
7
+ asideContent,
8
+ afterContent,
9
+ footerContent,
10
+ }: {
11
+ post: PostContent;
12
+ bodySlots?: Readonly<Record<string, string>>;
13
+ asideContent?: unknown;
14
+ afterContent?: unknown;
15
+ footerContent?: unknown;
16
+ }) {
17
+ return (
18
+ <Article class="site-article">
19
+ <ArticleLayout>
20
+ {asideContent}
21
+ {bodySlots?.["article.aside"] ? (
22
+ <div
23
+ class="site-article__aside"
24
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.aside"] }}
25
+ />
26
+ ) : null}
27
+ <ArticleContent>
28
+ {bodySlots?.["article.header"] ? (
29
+ <div
30
+ class="site-article__header"
31
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.header"] }}
32
+ />
33
+ ) : null}
34
+ {bodySlots?.["article.metadata"] ? (
35
+ <div
36
+ class="site-article__metadata"
37
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.metadata"] }}
38
+ />
39
+ ) : null}
40
+ {bodySlots?.["article.before-content"] ? (
41
+ <div
42
+ class="site-article__before-content"
43
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.before-content"] }}
44
+ />
45
+ ) : null}
46
+ <div dangerouslySetInnerHTML={{ __html: post.html ?? "" }} />
47
+ {afterContent}
48
+ {bodySlots?.["article.after-content"] ? (
49
+ <div
50
+ class="site-article__after-content"
51
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.after-content"] }}
52
+ />
53
+ ) : null}
54
+ </ArticleContent>
55
+ {bodySlots?.["article.footer"] || footerContent ? (
56
+ <div class="site-article__footer">
57
+ {bodySlots?.["article.footer"] ? (
58
+ <div dangerouslySetInnerHTML={{ __html: bodySlots["article.footer"] }} />
59
+ ) : null}
60
+ {footerContent}
61
+ </div>
62
+ ) : null}
63
+ </ArticleLayout>
64
+ </Article>
65
+ );
66
+ }
@@ -0,0 +1,113 @@
1
+ import type { NavigationItem } from "@riebeckite/plugin-navigation";
2
+ import { ColorModeToggle } from "@riebeckite/plugin-color-mode";
3
+ import { config } from "../config";
4
+
5
+ export function SiteHeader({
6
+ path,
7
+ items,
8
+ }: {
9
+ path: string;
10
+ items: readonly NavigationItem[];
11
+ }) {
12
+ const navigation = items.filter((item) => item.href !== "/");
13
+
14
+ return (
15
+ <header class="site-header rb-site-header">
16
+ <a href="/" class="site-header__home rb-site-header__home">
17
+ <img
18
+ src="/riebeckite-logo.png"
19
+ alt=""
20
+ class="site-header__logo"
21
+ width="28"
22
+ height="28"
23
+ />
24
+ {config.site.title}
25
+ </a>
26
+ {navigation.length > 0 ? (
27
+ <>
28
+ <SiteNavigation items={navigation} path={path} />
29
+ <details class="site-navigation__mobile rb-nav__mobile">
30
+ <summary class="rb-nav__toggle">Menu</summary>
31
+ <SiteNavigation items={navigation} path={path} />
32
+ </details>
33
+ </>
34
+ ) : null}
35
+ <ColorModeToggle />
36
+ </header>
37
+ );
38
+ }
39
+
40
+ export function SiteFooter({
41
+ path,
42
+ items,
43
+ }: {
44
+ path: string;
45
+ items: readonly NavigationItem[];
46
+ }) {
47
+ if (items.length === 0) return null;
48
+
49
+ return (
50
+ <footer class="site-footer rb-site-footer">
51
+ <SiteNavigation items={items} path={path} />
52
+ </footer>
53
+ );
54
+ }
55
+
56
+ function SiteNavigation({
57
+ items,
58
+ path,
59
+ }: {
60
+ items: readonly NavigationItem[];
61
+ path: string;
62
+ }) {
63
+ return (
64
+ <nav class="site-navigation rb-nav" aria-label="Site navigation">
65
+ <NavigationItems items={items} path={path} />
66
+ </nav>
67
+ );
68
+ }
69
+
70
+ function NavigationItems({
71
+ items,
72
+ path,
73
+ isChildList = false,
74
+ }: {
75
+ items: readonly NavigationItem[];
76
+ path: string;
77
+ isChildList?: boolean;
78
+ }) {
79
+ return (
80
+ <ul class={isChildList ? "site-navigation__list rb-nav__list rb-nav__children" : "site-navigation__list rb-nav__list"}>
81
+ {items.map((item) => {
82
+ const active = item.href ? isActive(item.href, path) : false;
83
+ return (
84
+ <li class="site-navigation__item rb-nav__item">
85
+ {item.href ? (
86
+ <a
87
+ href={item.href}
88
+ class={active ? "site-navigation__link rb-nav__link rb-nav__link--active is-active" : "site-navigation__link rb-nav__link"}
89
+ aria-current={active ? "page" : undefined}
90
+ target={item.external ? "_blank" : undefined}
91
+ rel={item.external ? "noreferrer" : undefined}
92
+ >
93
+ {item.label}
94
+ </a>
95
+ ) : (
96
+ <span class="site-navigation__label rb-nav__label">{item.label}</span>
97
+ )}
98
+ {item.children && item.children.length > 0 ? (
99
+ <NavigationItems items={item.children} path={path} isChildList />
100
+ ) : null}
101
+ </li>
102
+ );
103
+ })}
104
+ </ul>
105
+ );
106
+ }
107
+
108
+ function isActive(href: string, path: string): boolean {
109
+ if (!href.startsWith("/")) return false;
110
+ const target = href.replace(/\/+$/, "") || "/";
111
+ const current = path.replace(/^\/[a-z]{2}(?:-[A-Z]{2})?(?=\/|$)/, "").replace(/\/+$/, "") || "/";
112
+ return target === "/" ? current === target : current === target || current.startsWith(`${target}/`);
113
+ }
@@ -0,0 +1,52 @@
1
+ import { getEntryLanguage } from "@riebeckite/core";
2
+ import {
3
+ contentRouteSsgParams,
4
+ pluginPageSsgParams,
5
+ resolveRiebeckiteRoute,
6
+ } from "@riebeckite/honox/server";
7
+ import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";
8
+ import { createRoute } from "honox/factory";
9
+ import { SiteArticle } from "../components/article";
10
+ import { content } from "../content";
11
+
12
+ export default createRoute(
13
+ contentRouteSsgParams("/:slug{.+}", async () => {
14
+ const manifest = await content.getManifest();
15
+ const contentPaths = manifest.publicEntries
16
+ .filter((entry) => entry.permalink !== "/")
17
+ .map((entry) => ({ slug: entry.permalink.replace(/^\/+/, "") }));
18
+ return [...contentPaths, ...(await pluginPageSsgParams(content))];
19
+ }),
20
+ async (c) => {
21
+ const requestedSlug = c.req.param("slug");
22
+ if (!requestedSlug) return c.notFound();
23
+ if (/\.[a-zA-Z0-9]+$/.test(requestedSlug)) return c.notFound();
24
+
25
+ const route = await resolveRiebeckiteRoute(content, c.req.path);
26
+ if (!route) return c.notFound();
27
+ if (route.kind === "redirect")
28
+ return c.redirect(route.location, route.status);
29
+ if (route.kind === "page") {
30
+ c.set("headTags", route.page.headTags ?? []);
31
+ c.set("htmlLanguage", route.page.language);
32
+ return c.render(
33
+ <div dangerouslySetInnerHTML={{ __html: route.page.body }} />,
34
+ );
35
+ }
36
+
37
+ const post = await content.getProcessedContent(route.entry.slug);
38
+
39
+ const tableOfContents = extractTableOfContents(post.html ?? "");
40
+
41
+ c.set("htmlLanguage", getEntryLanguage(route.entry));
42
+ c.set("headTags", route.entry.headTags ?? []);
43
+
44
+ return c.render(
45
+ <SiteArticle
46
+ post={post}
47
+ bodySlots={route.entry.bodySlots}
48
+ asideContent={<TableOfContents className="table-of-contents--desktop" items={tableOfContents} />}
49
+ />,
50
+ );
51
+ },
52
+ );
@@ -0,0 +1,69 @@
1
+ import type { PluginHeadTag } from "@riebeckite/core";
2
+ import { jsxRenderer } from "hono/jsx-renderer";
3
+ import { Link, Script } from "honox/server";
4
+ import { ColorModeScript } from "@riebeckite/plugin-color-mode";
5
+ import { SearchBar } from "@riebeckite/plugin-search";
6
+ import { resolveSiteNavigation } from "@riebeckite/plugin-navigation";
7
+ import { SiteFooter, SiteHeader } from "../components/site-header";
8
+ import { content } from "../content";
9
+ import { config } from "../config";
10
+
11
+ function themeAttributes() {
12
+ const { theme } = config;
13
+
14
+ return {
15
+ ...theme.attributes,
16
+ "data-theme": theme.colorMode === "system" ? undefined : theme.colorMode,
17
+ "data-theme-name": theme.name,
18
+ "data-typography": theme.typography,
19
+ "data-article-layout": theme.articleLayout,
20
+ };
21
+ }
22
+
23
+ // Riebeckite plugin client entries need the HonoX client bundle even without
24
+ // an island component.
25
+ export const __importing_islands = true;
26
+
27
+ export default jsxRenderer(async ({ children }, c) => {
28
+ const headTags: readonly PluginHeadTag[] = c.get("headTags") ?? [];
29
+ const navigation = resolveSiteNavigation(config, await content.getManifest(), c.get("htmlLanguage")) ?? { primary: [], secondary: [] };
30
+
31
+ return (
32
+ <html
33
+ lang={c.get("htmlLanguage") ?? config.site.locale}
34
+ {...themeAttributes()}
35
+ >
36
+ <head>
37
+ <meta charset="utf-8" />
38
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
39
+ <title>{config.site.title}</title>
40
+ <link rel="icon" href="/favicon.ico" />
41
+ <ColorModeScript />
42
+ <Link href="/app/style.css" rel="stylesheet" />
43
+ {headTags.map(renderHeadTag)}
44
+ <Script src="/app/client.ts" async />
45
+ </head>
46
+ <body class="riebeckite-page rb-site">
47
+ <SiteHeader path={c.req.path} items={navigation.primary} />
48
+ <SearchBar />
49
+ {children}
50
+ <SiteFooter path={c.req.path} items={navigation.secondary} />
51
+ </body>
52
+ </html>
53
+ );
54
+ });
55
+
56
+ function renderHeadTag(tag: PluginHeadTag, index: number) {
57
+ const key = `${tag.tag}-${index}`;
58
+ if (tag.tag === "meta") return <meta {...tag.attrs} key={key} />;
59
+ if (tag.tag === "link") return <link {...tag.attrs} key={key} />;
60
+ return (
61
+ <script
62
+ {...tag.attrs}
63
+ key={key}
64
+ dangerouslySetInnerHTML={
65
+ tag.children ? { __html: tag.children } : undefined
66
+ }
67
+ />
68
+ );
69
+ }
@@ -0,0 +1,39 @@
1
+ import { getEntryLanguage } from "@riebeckite/core";
2
+ import { createRoute } from "honox/factory";
3
+ import { DailyNotes, getDailyNotes } from "@riebeckite/plugin-daily-notes";
4
+ import { RecentPosts, getRecentPosts } from "@riebeckite/plugin-recent-posts";
5
+ import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";
6
+ import { SiteArticle } from "../components/article";
7
+ import { config } from "../config";
8
+ import { content } from "../content";
9
+
10
+ export default createRoute(async (c) => {
11
+ const manifest = await content.getManifest();
12
+ const indexEntry = manifest.bySlug.get("index");
13
+ if (indexEntry && indexEntry.permalink !== "/") {
14
+ return c.redirect(indexEntry.permalink, 308);
15
+ }
16
+
17
+ const post = await content.getProcessedContent("index");
18
+ if (indexEntry?.publishing?.routable === false) {
19
+ return c.notFound();
20
+ }
21
+
22
+ const recentPosts = await getRecentPosts({ posts: manifest.discoverableEntries, config, getProcessedContent: (slug) => content.getProcessedContent(slug), resolveTitle: (slug, title) => typeof title === "string" && title.trim() ? title : slug.split("/").at(-1) ?? slug });
23
+ const dailyNotes = getDailyNotes({ manifest, config });
24
+ const tableOfContents = extractTableOfContents(post.html ?? "");
25
+
26
+ if (indexEntry) {
27
+ c.set("htmlLanguage", getEntryLanguage(indexEntry));
28
+ c.set("headTags", indexEntry.headTags ?? []);
29
+ }
30
+
31
+ return c.render(
32
+ <SiteArticle
33
+ post={post}
34
+ bodySlots={indexEntry?.bodySlots}
35
+ asideContent={<TableOfContents className="table-of-contents--desktop" items={tableOfContents} />}
36
+ afterContent={<><RecentPosts posts={recentPosts} /><DailyNotes notes={dailyNotes} /></>}
37
+ />,
38
+ );
39
+ });