summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--vhosts/blog/nuldoc-src/atom/generate.ts4
-rw-r--r--vhosts/blog/nuldoc-src/commands/build.ts18
-rw-r--r--vhosts/blog/nuldoc-src/pages/about.ts156
-rw-r--r--vhosts/blog/nuldoc-src/pages/about.tsx106
-rw-r--r--vhosts/blog/nuldoc-src/pages/home.ts97
-rw-r--r--vhosts/blog/nuldoc-src/pages/home.tsx65
-rw-r--r--vhosts/blog/nuldoc-src/pages/not_found.ts47
-rw-r--r--vhosts/blog/nuldoc-src/pages/not_found.tsx39
-rw-r--r--vhosts/blog/nuldoc-src/pages/post.ts145
-rw-r--r--vhosts/blog/nuldoc-src/pages/post.tsx123
-rw-r--r--vhosts/blog/nuldoc-src/pages/post_list.ts61
-rw-r--r--vhosts/blog/nuldoc-src/pages/post_list.tsx53
-rw-r--r--vhosts/blog/nuldoc-src/pages/slide.ts137
-rw-r--r--vhosts/blog/nuldoc-src/pages/slide.tsx110
-rw-r--r--vhosts/blog/nuldoc-src/pages/slide_list.ts62
-rw-r--r--vhosts/blog/nuldoc-src/pages/slide_list.tsx54
-rw-r--r--vhosts/blog/nuldoc-src/pages/tag.ts65
-rw-r--r--vhosts/blog/nuldoc-src/pages/tag.tsx64
-rw-r--r--vhosts/blog/nuldoc-src/pages/tag_list.ts85
-rw-r--r--vhosts/blog/nuldoc-src/pages/tag_list.tsx69
-rw-r--r--vhosts/blog/nuldoc-src/pages/tagged_page.ts4
21 files changed, 696 insertions, 868 deletions
diff --git a/vhosts/blog/nuldoc-src/atom/generate.ts b/vhosts/blog/nuldoc-src/atom/generate.ts
index ee659211..d98c4c18 100644
--- a/vhosts/blog/nuldoc-src/atom/generate.ts
+++ b/vhosts/blog/nuldoc-src/atom/generate.ts
@@ -2,8 +2,8 @@ import { Config } from "../config.ts";
import { el } from "../dom.ts";
import { Page } from "../page.ts";
import { Entry, Feed } from "./types.ts";
-import { PostPage } from "../pages/post.ts";
-import { SlidePage } from "../pages/slide.ts";
+import { PostPage } from "../pages/post.tsx";
+import { SlidePage } from "../pages/slide.tsx";
import { dateToRfc3339String } from "../revision.ts";
const BASE_NAME = "atom.xml";
diff --git a/vhosts/blog/nuldoc-src/commands/build.ts b/vhosts/blog/nuldoc-src/commands/build.ts
index 1ad08825..a542b4f8 100644
--- a/vhosts/blog/nuldoc-src/commands/build.ts
+++ b/vhosts/blog/nuldoc-src/commands/build.ts
@@ -7,20 +7,20 @@ import { parseNulDocFile } from "../ndoc/parse.ts";
import { Page } from "../page.ts";
import { render } from "../render.ts";
import { dateToString } from "../revision.ts";
-import { generateAboutPage } from "../pages/about.ts";
-import { generateHomePage } from "../pages/home.ts";
-import { generateNotFoundPage } from "../pages/not_found.ts";
+import { generateAboutPage } from "../pages/about.tsx";
+import { generateHomePage } from "../pages/home.tsx";
+import { generateNotFoundPage } from "../pages/not_found.tsx";
import {
generatePostPage,
getPostPublishedDate,
PostPage,
-} from "../pages/post.ts";
-import { generatePostListPage } from "../pages/post_list.ts";
-import { generateSlidePage, SlidePage } from "../pages/slide.ts";
-import { generateSlideListPage } from "../pages/slide_list.ts";
-import { generateTagPage, TagPage } from "../pages/tag.ts";
+} from "../pages/post.tsx";
+import { generatePostListPage } from "../pages/post_list.tsx";
+import { generateSlidePage, SlidePage } from "../pages/slide.tsx";
+import { generateSlideListPage } from "../pages/slide_list.tsx";
+import { generateTagPage, TagPage } from "../pages/tag.tsx";
import { TaggedPage } from "../pages/tagged_page.ts";
-import { generateTagListPage } from "../pages/tag_list.ts";
+import { generateTagListPage } from "../pages/tag_list.tsx";
import { parseSlideFile } from "../slide/parse.ts";
export async function runBuildCommand(config: Config) {
diff --git a/vhosts/blog/nuldoc-src/pages/about.ts b/vhosts/blog/nuldoc-src/pages/about.ts
deleted file mode 100644
index b5411eb7..00000000
--- a/vhosts/blog/nuldoc-src/pages/about.ts
+++ /dev/null
@@ -1,156 +0,0 @@
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { staticScriptElement } from "../components/utils.ts";
-import { Config } from "../config.ts";
-import { el } from "../dom.ts";
-import { Page } from "../page.ts";
-import { dateToString } from "../revision.ts";
-import { getPostPublishedDate } from "./post.ts";
-import { SlidePage } from "./slide.ts";
-
-export type AboutPage = Page;
-
-export async function generateAboutPage(
- slides: SlidePage[],
- config: Config,
-): Promise<AboutPage> {
- const body = el(
- "body",
- { className: "single" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el(
- "article",
- { className: "post-single" },
- el(
- "header",
- { className: "post-header" },
- el("h1", { className: "post-title" }, "nsfisis"),
- el(
- "div",
- { className: "my-icon" },
- await staticScriptElement("/p5.min.js", {}, config),
- await staticScriptElement("/my-icon.js", {}, config),
- el("div", { id: "p5jsMyIcon" }),
- el(
- "noscript",
- {},
- el("img", { src: "/favicon.svg" }),
- ),
- ),
- ),
- el(
- "div",
- { className: "post-content" },
- el(
- "section",
- {},
- el("h2", {}, "読み方"),
- el(
- "p",
- {},
- "読み方は決めていません。音にする必要があるときは本名である「いまむら」をお使いください。",
- ),
- ),
- el(
- "section",
- {},
- el("h2", {}, "アカウント"),
- el(
- "ul",
- {},
- el(
- "li",
- {},
- el(
- "a",
- { href: "https://twitter.com/nsfisis" },
- "Twitter (現 𝕏): @nsfisis",
- ),
- ),
- el(
- "li",
- {},
- el(
- "a",
- { href: "https://github.com/nsfisis" },
- "GitHub: @nsfisis",
- ),
- ),
- ),
- ),
- el(
- "section",
- {},
- el("h2", {}, "仕事"),
- el(
- "ul",
- {},
- el(
- "li",
- {},
- "2021-01~現在: ",
- el(
- "a",
- { href: "https://www.dgcircus.com/" },
- "デジタルサーカス株式会社",
- ),
- ),
- ),
- ),
- el(
- "section",
- {},
- el("h2", {}, "登壇"),
- el(
- "ul",
- {},
- ...Array.from(slides).sort((a, b) => {
- const ta = dateToString(getPostPublishedDate(a));
- const tb = dateToString(getPostPublishedDate(b));
- if (ta > tb) return -1;
- if (ta < tb) return 1;
- return 0;
- }).map((slide) =>
- el(
- "li",
- {},
- el(
- "a",
- { href: slide.href },
- `${
- dateToString(getPostPublishedDate(slide))
- }: ${slide.event} (${slide.talkType})`,
- ),
- )
- ),
- ),
- ),
- ),
- ),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: config.blog.siteCopyrightYear,
- metaDescription: "このサイトの著者について",
- metaKeywords: [],
- metaTitle: `About|${config.blog.siteName}`,
- requiresSyntaxHighlight: false,
- },
- body,
- config,
- );
-
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: "/about/index.html",
- href: "/about/",
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/about.tsx b/vhosts/blog/nuldoc-src/pages/about.tsx
new file mode 100644
index 00000000..ea74fa32
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/about.tsx
@@ -0,0 +1,106 @@
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import StaticScript from "../components/StaticScript.tsx";
+import { Config } from "../config.ts";
+import { el } from "../dom.ts";
+import { renderToDOM } from "../jsx/render.ts";
+import { Page } from "../page.ts";
+import { dateToString } from "../revision.ts";
+import { getPostPublishedDate } from "./post.tsx";
+import { SlidePage } from "./slide.tsx";
+
+export type AboutPage = Page;
+
+export async function generateAboutPage(
+ slides: SlidePage[],
+ config: Config,
+): Promise<AboutPage> {
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={config.blog.siteCopyrightYear}
+ metaDescription="このサイトの著者について"
+ metaTitle={`About|${config.blog.siteName}`}
+ config={config}
+ >
+ <body className="single">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <article className="post-single">
+ <header className="post-header">
+ <h1 className="post-title">nsfisis</h1>
+ <div className="my-icon">
+ <StaticScript fileName="/p5.min.js" config={config} />
+ <StaticScript fileName="/my-icon.js" config={config} />
+ <div id="p5jsMyIcon" />
+ <noscript>
+ <img src="/favicon.svg" />
+ </noscript>
+ </div>
+ </header>
+ <div className="post-content">
+ <section>
+ <h2>読み方</h2>
+ <p>
+ 読み方は決めていません。音にする必要があるときは本名である「いまむら」をお使いください。
+ </p>
+ </section>
+ <section>
+ <h2>アカウント</h2>
+ <ul>
+ <li>
+ <a href="https://twitter.com/nsfisis">
+ Twitter (現 𝕏): @nsfisis
+ </a>
+ </li>
+ <li>
+ <a href="https://github.com/nsfisis">GitHub: @nsfisis</a>
+ </li>
+ </ul>
+ </section>
+ <section>
+ <h2>仕事</h2>
+ <ul>
+ <li>
+ {"2021-01~現在: "}
+ <a href="https://www.dgcircus.com/">
+ デジタルサーカス株式会社
+ </a>
+ </li>
+ </ul>
+ </section>
+ <section>
+ <h2>登壇</h2>
+ <ul>
+ {Array.from(slides).sort((a, b) => {
+ const ta = dateToString(getPostPublishedDate(a));
+ const tb = dateToString(getPostPublishedDate(b));
+ if (ta > tb) return -1;
+ if (ta < tb) return 1;
+ return 0;
+ }).map((slide) => (
+ <li>
+ <a href={slide.href}>
+ {`${
+ dateToString(getPostPublishedDate(slide))
+ }: ${slide.event} (${slide.talkType})`}
+ </a>
+ </li>
+ ))}
+ </ul>
+ </section>
+ </div>
+ </article>
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: "/about/index.html",
+ href: "/about/",
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/home.ts b/vhosts/blog/nuldoc-src/pages/home.ts
deleted file mode 100644
index 86a767c0..00000000
--- a/vhosts/blog/nuldoc-src/pages/home.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { Config } from "../config.ts";
-import { el } from "../dom.ts";
-import { Page } from "../page.ts";
-
-export type HomePage = Page;
-
-export async function generateHomePage(config: Config): Promise<HomePage> {
- const body = el(
- "body",
- { className: "single" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el(
- "article",
- { className: "post-single" },
- el(
- "article",
- { className: "post-entry" },
- el(
- "a",
- { href: "/about/" },
- el(
- "header",
- { className: "entry-header" },
- el("h2", {}, "About"),
- ),
- ),
- ),
- el(
- "article",
- { className: "post-entry" },
- el(
- "a",
- { href: "/posts/" },
- el(
- "header",
- { className: "entry-header" },
- el("h2", {}, "Posts"),
- ),
- ),
- ),
- el(
- "article",
- { className: "post-entry" },
- el(
- "a",
- { href: "/slides/" },
- el(
- "header",
- { className: "entry-header" },
- el("h2", {}, "Slides"),
- ),
- ),
- ),
- el(
- "article",
- { className: "post-entry" },
- el(
- "a",
- { href: "/tags/" },
- el(
- "header",
- { className: "entry-header" },
- el("h2", {}, "Tags"),
- ),
- ),
- ),
- ),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: config.blog.siteCopyrightYear,
- metaDescription: "nsfisis のブログサイト",
- metaKeywords: [],
- metaTitle: config.blog.siteName,
- metaAtomFeedHref: `https://${config.blog.fqdn}/atom.xml`,
- requiresSyntaxHighlight: false,
- },
- body,
- config,
- );
-
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: "/index.html",
- href: "/",
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/home.tsx b/vhosts/blog/nuldoc-src/pages/home.tsx
new file mode 100644
index 00000000..3e79928a
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/home.tsx
@@ -0,0 +1,65 @@
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import { renderToDOM } from "../jsx/render.ts";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import { Config } from "../config.ts";
+import { el } from "../dom.ts";
+import { Page } from "../page.ts";
+
+export type HomePage = Page;
+
+export async function generateHomePage(config: Config): Promise<HomePage> {
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={config.blog.siteCopyrightYear}
+ metaDescription="nsfisis のブログサイト"
+ metaTitle={config.blog.siteName}
+ metaAtomFeedHref={`https://${config.blog.fqdn}/atom.xml`}
+ config={config}
+ >
+ <body className="single">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <article className="post-single">
+ <article className="post-entry">
+ <a href="/about/">
+ <header className="entry-header">
+ <h2>About</h2>
+ </header>
+ </a>
+ </article>
+ <article className="post-entry">
+ <a href="/posts/">
+ <header className="entry-header">
+ <h2>Posts</h2>
+ </header>
+ </a>
+ </article>
+ <article className="post-entry">
+ <a href="/slides/">
+ <header className="entry-header">
+ <h2>Slides</h2>
+ </header>
+ </a>
+ </article>
+ <article className="post-entry">
+ <a href="/tags/">
+ <header className="entry-header">
+ <h2>Tags</h2>
+ </header>
+ </a>
+ </article>
+ </article>
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: "/index.html",
+ href: "/",
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/not_found.ts b/vhosts/blog/nuldoc-src/pages/not_found.ts
deleted file mode 100644
index bb70d8ac..00000000
--- a/vhosts/blog/nuldoc-src/pages/not_found.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { Config } from "../config.ts";
-import { el } from "../dom.ts";
-import { Page } from "../page.ts";
-
-export type NotFoundPage = Page;
-
-export async function generateNotFoundPage(
- config: Config,
-): Promise<NotFoundPage> {
- const body = el(
- "body",
- { className: "single" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el(
- "article",
- {},
- el("div", { className: "not-found" }, "404"),
- ),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: config.blog.siteCopyrightYear,
- metaDescription: "リクエストされたページが見つかりません",
- metaKeywords: [],
- metaTitle: `Page Not Found|${config.blog.siteName}`,
- requiresSyntaxHighlight: false,
- },
- body,
- config,
- );
-
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: "/404.html",
- href: "/404.html",
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/not_found.tsx b/vhosts/blog/nuldoc-src/pages/not_found.tsx
new file mode 100644
index 00000000..f0b5f964
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/not_found.tsx
@@ -0,0 +1,39 @@
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import { renderToDOM } from "../jsx/render.ts";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import { Config } from "../config.ts";
+import { el } from "../dom.ts";
+import { Page } from "../page.ts";
+
+export type NotFoundPage = Page;
+
+export async function generateNotFoundPage(
+ config: Config,
+): Promise<NotFoundPage> {
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={config.blog.siteCopyrightYear}
+ metaDescription="リクエストされたページが見つかりません"
+ metaTitle={`Page Not Found|${config.blog.siteName}`}
+ config={config}
+ >
+ <body className="single">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <article>
+ <div className="not-found">404</div>
+ </article>
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: "/404.html",
+ href: "/404.html",
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/post.ts b/vhosts/blog/nuldoc-src/pages/post.ts
deleted file mode 100644
index c0c3bb2e..00000000
--- a/vhosts/blog/nuldoc-src/pages/post.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-import { join } from "std/path/mod.ts";
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { Config, getTagLabel } from "../config.ts";
-import { el, Element } from "../dom.ts";
-import { Document } from "../ndoc/document.ts";
-import { Page } from "../page.ts";
-import { Date, dateToString, Revision } from "../revision.ts";
-
-export interface PostPage extends Page {
- title: string;
- description: string;
- tags: string[];
- revisions: Revision[];
- published: Date;
- updated: Date;
- uuid: string;
-}
-
-export function getPostPublishedDate(page: { revisions: Revision[] }): Date {
- for (const rev of page.revisions) {
- if (!rev.isInternal) {
- return rev.date;
- }
- }
- return page.revisions[0].date;
-}
-
-export function getPostUpdatedDate(page: { revisions: Revision[] }): Date {
- return page.revisions[page.revisions.length - 1].date;
-}
-
-export function postHasAnyUpdates(page: { revisions: Revision[] }): boolean {
- return 2 <= page.revisions.filter((rev) => !rev.isInternal).length;
-}
-
-export async function generatePostPage(
- doc: Document,
- config: Config,
-): Promise<PostPage> {
- const body = el(
- "body",
- { className: "single" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el(
- "article",
- { className: "post-single" },
- el(
- "header",
- { className: "post-header" },
- el("h1", { className: "post-title" }, doc.title),
- ...(doc.tags.length === 0 ? [] : [
- el(
- "ul",
- { className: "post-tags" },
- ...doc.tags.map((slug) =>
- el(
- "li",
- { className: "tag" },
- el(
- "a",
- { href: `/tags/${slug}/` },
- getTagLabel(config, slug),
- ),
- )
- ),
- ),
- ]),
- ),
- el(
- "div",
- { className: "post-content" },
- el(
- "section",
- {},
- el("h2", { id: "changelog" }, "更新履歴"),
- el(
- "ol",
- {},
- ...doc.revisions.map((rev) =>
- el(
- "li",
- { className: "revision" },
- el(
- "time",
- { datetime: dateToString(rev.date) },
- dateToString(rev.date),
- ),
- `: ${rev.remark}`,
- )
- ),
- ),
- ),
- // TODO: refactor
- ...(doc.root.children[0] as Element).children,
- // TODO: footnotes
- // <div id="footnotes">
- // <% for footnote in footnotes %>
- // <div class="footnote" id="_footnotedef_<%= footnote.index %>">
- // <a href="#_footnoteref_<%= footnote.index %>"><%= footnote.index %></a>. <%= footnote.text %>
- // </div>
- // <% end %>
- // </div>
- ),
- ),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: getPostPublishedDate(doc).year,
- metaDescription: doc.description,
- metaKeywords: doc.tags.map((slug) => getTagLabel(config, slug)),
- metaTitle: `${doc.title}|${config.blog.siteName}`,
- requiresSyntaxHighlight: true,
- },
- body,
- config,
- );
-
- const cwd = Deno.cwd();
- const contentDir = join(cwd, config.locations.contentDir);
- const destFilePath = join(
- doc.sourceFilePath.replace(contentDir, "").replace(".ndoc", ""),
- "index.html",
- );
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: destFilePath,
- href: destFilePath.replace("index.html", ""),
- title: doc.title,
- description: doc.description,
- tags: doc.tags,
- revisions: doc.revisions,
- published: getPostPublishedDate(doc),
- updated: getPostUpdatedDate(doc),
- uuid: doc.uuid,
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/post.tsx b/vhosts/blog/nuldoc-src/pages/post.tsx
new file mode 100644
index 00000000..529d6738
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/post.tsx
@@ -0,0 +1,123 @@
+import { join } from "std/path/mod.ts";
+import { renderToDOM } from "../jsx/render.ts";
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import { Config, getTagLabel } from "../config.ts";
+import { el, Element } from "../dom.ts";
+import { Document } from "../ndoc/document.ts";
+import { Page } from "../page.ts";
+import { Date, dateToString, Revision } from "../revision.ts";
+
+export interface PostPage extends Page {
+ title: string;
+ description: string;
+ tags: string[];
+ revisions: Revision[];
+ published: Date;
+ updated: Date;
+ uuid: string;
+}
+
+export function getPostPublishedDate(page: { revisions: Revision[] }): Date {
+ for (const rev of page.revisions) {
+ if (!rev.isInternal) {
+ return rev.date;
+ }
+ }
+ return page.revisions[0].date;
+}
+
+export function getPostUpdatedDate(page: { revisions: Revision[] }): Date {
+ return page.revisions[page.revisions.length - 1].date;
+}
+
+export function postHasAnyUpdates(page: { revisions: Revision[] }): boolean {
+ return 2 <= page.revisions.filter((rev) => !rev.isInternal).length;
+}
+
+export async function generatePostPage(
+ doc: Document,
+ config: Config,
+): Promise<PostPage> {
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={getPostPublishedDate(doc).year}
+ metaDescription={doc.description}
+ metaKeywords={doc.tags.map((slug) => getTagLabel(config, slug))}
+ metaTitle={`${doc.title}|${config.blog.siteName}`}
+ requiresSyntaxHighlight
+ config={config}
+ >
+ <body className="single">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <article className="post-single">
+ <header className="post-header">
+ <h1 className="post-title">{doc.title}</h1>
+ {doc.tags.length !== 0 && (
+ <ul className="post-tags">
+ {doc.tags.map((slug) => (
+ <li className="tag">
+ <a href={`/tags/${slug}/`}>{getTagLabel(config, slug)}</a>
+ </li>
+ ))}
+ </ul>
+ )}
+ </header>
+ <div className="post-content">
+ <section>
+ <h2 id="changelog">更新履歴</h2>
+ <ol>
+ {doc.revisions.map((rev) => (
+ <li className="revision">
+ <time datetime={dateToString(rev.date)}>
+ {dateToString(rev.date)}
+ </time>
+ {`: ${rev.remark}`}
+ </li>
+ ))}
+ </ol>
+ </section>
+ {
+ // TODO: refactor
+ (doc.root.children[0] as Element).children
+ }
+ {
+ // TODO: footnotes
+ // <div id="footnotes">
+ // <% for footnote in footnotes %>
+ // <div class="footnote" id="_footnotedef_<%= footnote.index %>">
+ // <a href="#_footnoteref_<%= footnote.index %>"><%= footnote.index %></a>. <%= footnote.text %>
+ // </div>
+ // <% end %>
+ // </div>
+ }
+ </div>
+ </article>
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ const cwd = Deno.cwd();
+ const contentDir = join(cwd, config.locations.contentDir);
+ const destFilePath = join(
+ doc.sourceFilePath.replace(contentDir, "").replace(".ndoc", ""),
+ "index.html",
+ );
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: destFilePath,
+ href: destFilePath.replace("index.html", ""),
+ title: doc.title,
+ description: doc.description,
+ tags: doc.tags,
+ revisions: doc.revisions,
+ published: getPostPublishedDate(doc),
+ updated: getPostUpdatedDate(doc),
+ uuid: doc.uuid,
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/post_list.ts b/vhosts/blog/nuldoc-src/pages/post_list.ts
deleted file mode 100644
index 40df8ed9..00000000
--- a/vhosts/blog/nuldoc-src/pages/post_list.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { postPageEntry } from "../components/post_page_entry.ts";
-import { Config } from "../config.ts";
-import { el } from "../dom.ts";
-import { Page } from "../page.ts";
-import { dateToString } from "../revision.ts";
-import { getPostPublishedDate, PostPage } from "./post.ts";
-
-export type PostListPage = Page;
-
-export async function generatePostListPage(
- posts: PostPage[],
- config: Config,
-): Promise<PostListPage> {
- const pageTitle = "投稿一覧";
-
- const body = el(
- "body",
- { className: "list" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el(
- "header",
- { className: "page-header" },
- el("h1", {}, pageTitle),
- ),
- ...Array.from(posts).sort((a, b) => {
- const ta = dateToString(getPostPublishedDate(a));
- const tb = dateToString(getPostPublishedDate(b));
- if (ta > tb) return -1;
- if (ta < tb) return 1;
- return 0;
- }).map((post) => postPageEntry(post)),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: config.blog.siteCopyrightYear,
- metaDescription: "投稿した記事の一覧",
- metaKeywords: [],
- metaTitle: `${pageTitle}|${config.blog.siteName}`,
- metaAtomFeedHref: `https://${config.blog.fqdn}/posts/atom.xml`,
- requiresSyntaxHighlight: false,
- },
- body,
- config,
- );
-
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: "/posts/index.html",
- href: "/posts/",
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/post_list.tsx b/vhosts/blog/nuldoc-src/pages/post_list.tsx
new file mode 100644
index 00000000..2d32636f
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/post_list.tsx
@@ -0,0 +1,53 @@
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import { renderToDOM } from "../jsx/render.ts";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import PostPageEntry from "../components/PostPageEntry.tsx";
+import { Config } from "../config.ts";
+import { el } from "../dom.ts";
+import { Page } from "../page.ts";
+import { dateToString } from "../revision.ts";
+import { getPostPublishedDate, PostPage } from "./post.tsx";
+
+export type PostListPage = Page;
+
+export async function generatePostListPage(
+ posts: PostPage[],
+ config: Config,
+): Promise<PostListPage> {
+ const pageTitle = "投稿一覧";
+
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={config.blog.siteCopyrightYear}
+ metaDescription="投稿した記事の一覧"
+ metaTitle={`${pageTitle}|${config.blog.siteName}`}
+ metaAtomFeedHref={`https://${config.blog.fqdn}/posts/atom.xml`}
+ config={config}
+ >
+ <body className="list">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <header className="page-header">
+ <h1>{pageTitle}</h1>
+ </header>
+ {Array.from(posts).sort((a, b) => {
+ const ta = dateToString(getPostPublishedDate(a));
+ const tb = dateToString(getPostPublishedDate(b));
+ if (ta > tb) return -1;
+ if (ta < tb) return 1;
+ return 0;
+ }).map((post) => <PostPageEntry post={post} />)}
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: "/posts/index.html",
+ href: "/posts/",
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/slide.ts b/vhosts/blog/nuldoc-src/pages/slide.ts
deleted file mode 100644
index 15a18bb1..00000000
--- a/vhosts/blog/nuldoc-src/pages/slide.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import { join } from "std/path/mod.ts";
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { staticScriptElement } from "../components/utils.ts";
-import { Config, getTagLabel } from "../config.ts";
-import { el } from "../dom.ts";
-import { Page } from "../page.ts";
-import { Date, dateToString, Revision } from "../revision.ts";
-import { Slide } from "../slide/slide.ts";
-import { getPostPublishedDate, getPostUpdatedDate } from "./post.ts";
-
-export interface SlidePage extends Page {
- title: string;
- description: string;
- event: string;
- talkType: string;
- slideLink: string;
- tags: string[];
- revisions: Revision[];
- published: Date;
- updated: Date;
- uuid: string;
-}
-
-export async function generateSlidePage(
- slide: Slide,
- config: Config,
-): Promise<SlidePage> {
- const body = el(
- "body",
- { className: "single" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el(
- "article",
- { className: "post-single" },
- el(
- "header",
- { className: "post-header" },
- el("h1", { className: "post-title" }, slide.title),
- ...(slide.tags.length === 0 ? [] : [
- el(
- "ul",
- { className: "post-tags" },
- ...slide.tags.map((slug) =>
- el(
- "li",
- { className: "tag" },
- el(
- "a",
- { href: `/tags/${slug}/` },
- getTagLabel(config, slug),
- ),
- )
- ),
- ),
- ]),
- ),
- el(
- "div",
- { className: "post-content" },
- el(
- "section",
- {},
- el("h2", { id: "changelog" }, "更新履歴"),
- el(
- "ol",
- {},
- ...slide.revisions.map((rev) =>
- el(
- "li",
- { className: "revision" },
- el(
- "time",
- { datetime: dateToString(rev.date) },
- dateToString(rev.date),
- ),
- `: ${rev.remark}`,
- )
- ),
- ),
- ),
- el(
- "canvas",
- { id: "slide", "data-slide-link": slide.slideLink },
- ),
- el(
- "div",
- {},
- el("button", { id: "prev" }, "Prev"),
- el("button", { id: "next" }, "Next"),
- ),
- await staticScriptElement("/slide.js", { type: "module" }, config),
- ),
- ),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: getPostPublishedDate(slide).year,
- metaDescription: slide.title,
- metaKeywords: slide.tags.map((slug) => getTagLabel(config, slug)),
- metaTitle: `${slide.event} (${slide.talkType})|${config.blog.siteName}`,
- requiresSyntaxHighlight: true,
- },
- body,
- config,
- );
-
- const cwd = Deno.cwd();
- const contentDir = join(cwd, config.locations.contentDir);
- const destFilePath = join(
- slide.sourceFilePath.replace(contentDir, "").replace(".toml", ""),
- "index.html",
- );
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: destFilePath,
- href: destFilePath.replace("index.html", ""),
- title: slide.title,
- description: `登壇: ${slide.event} (${slide.talkType})`,
- event: slide.event,
- talkType: slide.talkType,
- slideLink: slide.slideLink,
- tags: slide.tags,
- revisions: slide.revisions,
- published: getPostPublishedDate(slide),
- updated: getPostUpdatedDate(slide),
- uuid: slide.uuid,
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/slide.tsx b/vhosts/blog/nuldoc-src/pages/slide.tsx
new file mode 100644
index 00000000..4165163a
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/slide.tsx
@@ -0,0 +1,110 @@
+import { join } from "std/path/mod.ts";
+import { renderToDOM } from "../jsx/render.ts";
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import StaticScript from "../components/StaticScript.tsx";
+import { Config, getTagLabel } from "../config.ts";
+import { el } from "../dom.ts";
+import { Page } from "../page.ts";
+import { Date, dateToString, Revision } from "../revision.ts";
+import { Slide } from "../slide/slide.ts";
+import { getPostPublishedDate, getPostUpdatedDate } from "./post.tsx";
+
+export interface SlidePage extends Page {
+ title: string;
+ description: string;
+ event: string;
+ talkType: string;
+ slideLink: string;
+ tags: string[];
+ revisions: Revision[];
+ published: Date;
+ updated: Date;
+ uuid: string;
+}
+
+export async function generateSlidePage(
+ slide: Slide,
+ config: Config,
+): Promise<SlidePage> {
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={getPostPublishedDate(slide).year}
+ metaDescription={slide.title}
+ metaKeywords={slide.tags.map((slug) => getTagLabel(config, slug))}
+ metaTitle={`${slide.event} (${slide.talkType})|${config.blog.siteName}`}
+ requiresSyntaxHighlight
+ config={config}
+ >
+ <body className="single">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <article className="post-single">
+ <header className="post-header">
+ <h1 className="post-title">{slide.title}</h1>
+ {slide.tags.length !== 0 && (
+ <ul className="post-tags">
+ {slide.tags.map((slug) => (
+ <li className="tag">
+ <a href={`/tags/${slug}/`}>{getTagLabel(config, slug)}</a>
+ </li>
+ ))}
+ </ul>
+ )}
+ </header>
+ <div className="post-content">
+ <section>
+ <h2 id="changelog">更新履歴</h2>
+ <ol>
+ {slide.revisions.map((rev) => (
+ <li className="revision">
+ <time datetime={dateToString(rev.date)}>
+ {dateToString(rev.date)}
+ </time>
+ {`: ${rev.remark}`}
+ </li>
+ ))}
+ </ol>
+ </section>
+ <canvas id="slide" data-slide-link={slide.slideLink} />
+ <div>
+ <button id="prev">Prev</button>
+ <button id="next">Next</button>
+ </div>
+ <StaticScript
+ fileName="/slide.js"
+ type="module"
+ config={config}
+ />
+ </div>
+ </article>
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ const cwd = Deno.cwd();
+ const contentDir = join(cwd, config.locations.contentDir);
+ const destFilePath = join(
+ slide.sourceFilePath.replace(contentDir, "").replace(".toml", ""),
+ "index.html",
+ );
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: destFilePath,
+ href: destFilePath.replace("index.html", ""),
+ title: slide.title,
+ description: `登壇: ${slide.event} (${slide.talkType})`,
+ event: slide.event,
+ talkType: slide.talkType,
+ slideLink: slide.slideLink,
+ tags: slide.tags,
+ revisions: slide.revisions,
+ published: getPostPublishedDate(slide),
+ updated: getPostUpdatedDate(slide),
+ uuid: slide.uuid,
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/slide_list.ts b/vhosts/blog/nuldoc-src/pages/slide_list.ts
deleted file mode 100644
index 53b2d2b1..00000000
--- a/vhosts/blog/nuldoc-src/pages/slide_list.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { slidePageEntry } from "../components/slide_page_entry.ts";
-import { Config } from "../config.ts";
-import { el } from "../dom.ts";
-import { Page } from "../page.ts";
-import { dateToString } from "../revision.ts";
-import { getPostPublishedDate } from "./post.ts";
-import { SlidePage } from "./slide.ts";
-
-export type SlideListPage = Page;
-
-export async function generateSlideListPage(
- slides: SlidePage[],
- config: Config,
-): Promise<SlideListPage> {
- const pageTitle = "スライド一覧";
-
- const body = el(
- "body",
- { className: "list" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el(
- "header",
- { className: "page-header" },
- el("h1", {}, pageTitle),
- ),
- ...Array.from(slides).sort((a, b) => {
- const ta = dateToString(getPostPublishedDate(a));
- const tb = dateToString(getPostPublishedDate(b));
- if (ta > tb) return -1;
- if (ta < tb) return 1;
- return 0;
- }).map((slide) => slidePageEntry(slide)),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: config.blog.siteCopyrightYear,
- metaDescription: "登壇したイベントで使用したスライドの一覧",
- metaKeywords: [],
- metaTitle: `${pageTitle}|${config.blog.siteName}`,
- metaAtomFeedHref: `https://${config.blog.fqdn}/slides/atom.xml`,
- requiresSyntaxHighlight: false,
- },
- body,
- config,
- );
-
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: "/slides/index.html",
- href: "/slides/",
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/slide_list.tsx b/vhosts/blog/nuldoc-src/pages/slide_list.tsx
new file mode 100644
index 00000000..31eeca68
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/slide_list.tsx
@@ -0,0 +1,54 @@
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import { renderToDOM } from "../jsx/render.ts";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import SlidePageEntry from "../components/SlidePageEntry.tsx";
+import { Config } from "../config.ts";
+import { el } from "../dom.ts";
+import { Page } from "../page.ts";
+import { dateToString } from "../revision.ts";
+import { getPostPublishedDate } from "./post.tsx";
+import { SlidePage } from "./slide.tsx";
+
+export type SlideListPage = Page;
+
+export async function generateSlideListPage(
+ slides: SlidePage[],
+ config: Config,
+): Promise<SlideListPage> {
+ const pageTitle = "スライド一覧";
+
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={config.blog.siteCopyrightYear}
+ metaDescription="登壇したイベントで使用したスライドの一覧"
+ metaTitle={`${pageTitle}|${config.blog.siteName}`}
+ metaAtomFeedHref={`https://${config.blog.fqdn}/slides/atom.xml`}
+ config={config}
+ >
+ <body className="list">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <header className="page-header">
+ <h1>{pageTitle}</h1>
+ </header>
+ {Array.from(slides).sort((a, b) => {
+ const ta = dateToString(getPostPublishedDate(a));
+ const tb = dateToString(getPostPublishedDate(b));
+ if (ta > tb) return -1;
+ if (ta < tb) return 1;
+ return 0;
+ }).map((slide) => <SlidePageEntry slide={slide} />)}
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: "/slides/index.html",
+ href: "/slides/",
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/tag.ts b/vhosts/blog/nuldoc-src/pages/tag.ts
deleted file mode 100644
index 32711f7e..00000000
--- a/vhosts/blog/nuldoc-src/pages/tag.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { postPageEntry } from "../components/post_page_entry.ts";
-import { slidePageEntry } from "../components/slide_page_entry.ts";
-import { Config, getTagLabel } from "../config.ts";
-import { el } from "../dom.ts";
-import { Page } from "../page.ts";
-import { getPostPublishedDate } from "./post.ts";
-import { TaggedPage } from "./tagged_page.ts";
-
-export interface TagPage extends Page {
- tagSlug: string;
- tagLabel: string;
- numOfPosts: number;
- numOfSlides: number;
-}
-
-export async function generateTagPage(
- tagSlug: string,
- pages: TaggedPage[],
- config: Config,
-): Promise<TagPage> {
- const tagLabel = getTagLabel(config, tagSlug);
- const pageTitle = `タグ「${tagLabel}」一覧`;
-
- const body = el(
- "body",
- { className: "list" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el("header", { className: "page-header" }, el("h1", {}, pageTitle)),
- ...pages.map((page) =>
- "event" in page ? slidePageEntry(page) : postPageEntry(page)
- ),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: getPostPublishedDate(pages[pages.length - 1]).year,
- metaDescription: `タグ「${tagLabel}」のついた記事またはスライドの一覧`,
- metaKeywords: [tagLabel],
- metaTitle: `${pageTitle}|${config.blog.siteName}`,
- metaAtomFeedHref: `https://${config.blog.fqdn}/tags/${tagSlug}/atom.xml`,
- requiresSyntaxHighlight: false,
- },
- body,
- config,
- );
-
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: `/tags/${tagSlug}/index.html`,
- href: `/tags/${tagSlug}/`,
- tagSlug: tagSlug,
- tagLabel: tagLabel,
- numOfPosts: pages.filter((p) => !("event" in p)).length,
- numOfSlides: pages.filter((p) => "event" in p).length,
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/tag.tsx b/vhosts/blog/nuldoc-src/pages/tag.tsx
new file mode 100644
index 00000000..8dfc9a39
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/tag.tsx
@@ -0,0 +1,64 @@
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import { renderToDOM } from "../jsx/render.ts";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import PostPageEntry from "../components/PostPageEntry.tsx";
+import SlidePageEntry from "../components/SlidePageEntry.tsx";
+import { Config, getTagLabel } from "../config.ts";
+import { el } from "../dom.ts";
+import { Page } from "../page.ts";
+import { getPostPublishedDate } from "./post.tsx";
+import { TaggedPage } from "./tagged_page.ts";
+
+export interface TagPage extends Page {
+ tagSlug: string;
+ tagLabel: string;
+ numOfPosts: number;
+ numOfSlides: number;
+}
+
+export async function generateTagPage(
+ tagSlug: string,
+ pages: TaggedPage[],
+ config: Config,
+): Promise<TagPage> {
+ const tagLabel = getTagLabel(config, tagSlug);
+ const pageTitle = `タグ「${tagLabel}」一覧`;
+
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={getPostPublishedDate(pages[pages.length - 1]).year}
+ metaDescription={`タグ「${tagLabel}」のついた記事またはスライドの一覧`}
+ metaKeywords={[tagLabel]}
+ metaTitle={`${pageTitle}|${config.blog.siteName}`}
+ metaAtomFeedHref={`https://${config.blog.fqdn}/tags/${tagSlug}/atom.xml`}
+ config={config}
+ >
+ <body className="list">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <header className="page-header">
+ <h1>{pageTitle}</h1>
+ </header>
+ {pages.map((page) =>
+ "event" in page
+ ? <SlidePageEntry slide={page} />
+ : <PostPageEntry post={page} />
+ )}
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: `/tags/${tagSlug}/index.html`,
+ href: `/tags/${tagSlug}/`,
+ tagSlug: tagSlug,
+ tagLabel: tagLabel,
+ numOfPosts: pages.filter((p) => !("event" in p)).length,
+ numOfSlides: pages.filter((p) => "event" in p).length,
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/tag_list.ts b/vhosts/blog/nuldoc-src/pages/tag_list.ts
deleted file mode 100644
index 57f6a293..00000000
--- a/vhosts/blog/nuldoc-src/pages/tag_list.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { globalFooter } from "../components/global_footer.ts";
-import { globalHeader } from "../components/global_header.ts";
-import { pageLayout } from "../components/page_layout.ts";
-import { Config } from "../config.ts";
-import { el } from "../dom.ts";
-import { Page } from "../page.ts";
-import { TagPage } from "./tag.ts";
-
-export type TagListPage = Page;
-
-export async function generateTagListPage(
- tags: TagPage[],
- config: Config,
-): Promise<TagListPage> {
- const pageTitle = "タグ一覧";
-
- const body = el(
- "body",
- { className: "list" },
- globalHeader(config),
- el(
- "main",
- { className: "main" },
- el(
- "header",
- { className: "page-header" },
- el("h1", {}, pageTitle),
- ),
- ...Array.from(tags).sort((a, b) => {
- const ta = a.tagSlug;
- const tb = b.tagSlug;
- if (ta < tb) return -1;
- if (ta > tb) return 1;
- return 0;
- }).map((tag) =>
- el(
- "article",
- { className: "post-entry" },
- el(
- "a",
- { href: tag.href },
- el(
- "header",
- { className: "entry-header" },
- el("h2", {}, tag.tagLabel),
- ),
- el(
- "footer",
- { className: "entry-footer" },
- (() => {
- const posts = tag.numOfPosts === 0
- ? ""
- : `${tag.numOfPosts}件の記事`;
- const slides = tag.numOfSlides === 0
- ? ""
- : `${tag.numOfSlides}件のスライド`;
- return `${posts}${posts && slides ? "、" : ""}${slides}`;
- })(),
- ),
- ),
- )
- ),
- ),
- globalFooter(config),
- );
-
- const html = await pageLayout(
- {
- metaCopyrightYear: config.blog.siteCopyrightYear,
- metaDescription: "タグの一覧",
- metaKeywords: [],
- metaTitle: `${pageTitle}|${config.blog.siteName}`,
- requiresSyntaxHighlight: false,
- },
- body,
- config,
- );
-
- return {
- root: el("__root__", {}, html),
- renderer: "html",
- destFilePath: "/tags/index.html",
- href: "/tags/",
- };
-}
diff --git a/vhosts/blog/nuldoc-src/pages/tag_list.tsx b/vhosts/blog/nuldoc-src/pages/tag_list.tsx
new file mode 100644
index 00000000..5ea6f25f
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/pages/tag_list.tsx
@@ -0,0 +1,69 @@
+import GlobalFooter from "../components/GlobalFooter.tsx";
+import { renderToDOM } from "../jsx/render.ts";
+import GlobalHeader from "../components/GlobalHeader.tsx";
+import PageLayout from "../components/PageLayout.tsx";
+import { Config } from "../config.ts";
+import { el } from "../dom.ts";
+import { Page } from "../page.ts";
+import { TagPage } from "./tag.tsx";
+
+export type TagListPage = Page;
+
+export async function generateTagListPage(
+ tags: TagPage[],
+ config: Config,
+): Promise<TagListPage> {
+ const pageTitle = "タグ一覧";
+
+ const html = await renderToDOM(
+ <PageLayout
+ metaCopyrightYear={config.blog.siteCopyrightYear}
+ metaDescription="タグの一覧"
+ metaTitle={`${pageTitle}|${config.blog.siteName}`}
+ config={config}
+ >
+ <body className="list">
+ <GlobalHeader config={config} />
+ <main className="main">
+ <header className="page-header">
+ <h1>{pageTitle}</h1>
+ </header>
+ {Array.from(tags).sort((a, b) => {
+ const ta = a.tagSlug;
+ const tb = b.tagSlug;
+ if (ta < tb) return -1;
+ if (ta > tb) return 1;
+ return 0;
+ }).map((tag) => (
+ <article className="post-entry">
+ <a href={tag.href}>
+ <header className="entry-header">
+ <h2>{tag.tagLabel}</h2>
+ </header>
+ <footer className="entry-footer">
+ {(() => {
+ const posts = tag.numOfPosts === 0
+ ? ""
+ : `${tag.numOfPosts}件の記事`;
+ const slides = tag.numOfSlides === 0
+ ? ""
+ : `${tag.numOfSlides}件のスライド`;
+ return `${posts}${posts && slides ? "、" : ""}${slides}`;
+ })()}
+ </footer>
+ </a>
+ </article>
+ ))}
+ </main>
+ <GlobalFooter config={config} />
+ </body>
+ </PageLayout>,
+ );
+
+ return {
+ root: el("__root__", {}, html),
+ renderer: "html",
+ destFilePath: "/tags/index.html",
+ href: "/tags/",
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/pages/tagged_page.ts b/vhosts/blog/nuldoc-src/pages/tagged_page.ts
index 23de8cb4..026ca91f 100644
--- a/vhosts/blog/nuldoc-src/pages/tagged_page.ts
+++ b/vhosts/blog/nuldoc-src/pages/tagged_page.ts
@@ -1,4 +1,4 @@
-import { PostPage } from "./post.ts";
-import { SlidePage } from "./slide.ts";
+import { PostPage } from "./post.tsx";
+import { SlidePage } from "./slide.tsx";
export type TaggedPage = PostPage | SlidePage;