Changes
35 changed files (+1507/-1768)
-
-
@@ -188,35 +188,6 @@ "https://deno.land/x/brotli@0.1.7/mod.ts": "08b913e51488b6e7fa181f2814b9ad087fdb5520041db0368f8156bfa45fd73e","https://deno.land/x/brotli@0.1.7/wasm.js": "77771b89e89ec7ff6e3e0939a7fb4f9b166abec3504cec0532ad5c127d6f35d2", "https://deno.land/x/lz4@v0.1.2/mod.ts": "4decfc1a3569d03fd1813bd39128b71c8f082850fe98ecfdde20025772916582", "https://deno.land/x/lz4@v0.1.2/wasm.js": "b9c65605327ba273f0c76a6dc596ec534d4cda0f0225d7a94ebc606782319e46", "https://deno.land/x/nano_jsx@v0.1.0/component.ts": "6bd3dab67754e91308df863e11e2b7945bbe0bf575b4bc529e8a9e02087eb732", "https://deno.land/x/nano_jsx@v0.1.0/components/helmet.ts": "59e598dc79c7b18620de175c61eb26f4aeb6905a6654cd63359208a26471541e", "https://deno.land/x/nano_jsx@v0.1.0/components/img.ts": "82a9f830394a7d6cd9b789b48fe1e70bc64eb151c4b410a3da74347c06acdae0", "https://deno.land/x/nano_jsx@v0.1.0/components/index.ts": "8855ee8302a9a19f38202d4a7ff5b17e22942600da02356bdfbc80c99c5c55f5", "https://deno.land/x/nano_jsx@v0.1.0/components/link.ts": "0f0b1c57dc8c466203105062335e0d9ded89a34e75d08b1bbeece198d42021cb", "https://deno.land/x/nano_jsx@v0.1.0/components/router.ts": "186e77064a96c31cb66ff1935c93d91904347647fcb13b56b5875ea64905397d", "https://deno.land/x/nano_jsx@v0.1.0/components/suspense.ts": "0711b7cdcd42b0b085972eca796d3ba7c436a736e1d2ce17db7e0cd1d3b928f5", "https://deno.land/x/nano_jsx@v0.1.0/components/visible.ts": "f19487f7faff0bad38a5549f1efac1df351e83ee4a77ca5886412b439f4b6b53", "https://deno.land/x/nano_jsx@v0.1.0/context.ts": "a65018beedf3e1863b96f9d99cf2d471c231a6ae0c77877a77754a398105f9fc", "https://deno.land/x/nano_jsx@v0.1.0/core.ts": "9346346ba7ddc52057131953a333c3ffa12e2a93aaceb25dabbab4bf8f1a171c", "https://deno.land/x/nano_jsx@v0.1.0/customElementsMode.ts": "61b9688ae89bfcc6109b72eaab4d82a69a8482d89770f3e2c1359b79bf57bb91", "https://deno.land/x/nano_jsx@v0.1.0/fragment.ts": "9cfb0dcdcb02d5cc53f027b4b55609e4f9d6d9322b665292e572856f4b688569", "https://deno.land/x/nano_jsx@v0.1.0/helpers.ts": "d904aa646534f1c0cf7c05a6c8e02231f906d962acb725c964aedbcf50216d85", "https://deno.land/x/nano_jsx@v0.1.0/htm.ts": "d8d9cd4fb4ad4645bff3698c7eb96e7798022c5519d04e709ebde236edde156b", "https://deno.land/x/nano_jsx@v0.1.0/htm/build.ts": "9d6b6eb407c0db2d1213a3f9c82efc66a2bec34c89976132ff13e3e095dcaaaf", "https://deno.land/x/nano_jsx@v0.1.0/htm/constants.ts": "3f296f87f03bd0ba8d7673884a1f6c4c927a2774648eaa4fd3ca9e3c46c3f7b8", "https://deno.land/x/nano_jsx@v0.1.0/htm/index.ts": "29912b9f7a760884fce79caca82649b9772cfa3c3272e0426a4ce377b67d70d5", "https://deno.land/x/nano_jsx@v0.1.0/index.ts": "0f39c911a39c61cb39f9bd8a2481500126dcb1b2a826d5b4523dd47a08b6938d", "https://deno.land/x/nano_jsx@v0.1.0/jsx-runtime/index.ts": "eb3d0884774eb3bf360d731bd7787aef2701f1be7e91aff8f88da59153d6aaa5", "https://deno.land/x/nano_jsx@v0.1.0/jsx.ts": "7d5b4d4a249fba1a42f2016f54f71ca049d96ff490e098db4a19b2a9b197b952", "https://deno.land/x/nano_jsx@v0.1.0/lazy.ts": "f8b8a2b915daff1fb70704ffad5171c805f1e28778b5cd0f8c827780762776c4", "https://deno.land/x/nano_jsx@v0.1.0/mod.ts": "3dda2bc0dbee9abd01c03acf43ab55a7854952afe0450245fc2ad12eb9bd9598", "https://deno.land/x/nano_jsx@v0.1.0/regexDom.ts": "de82fd3110c5a367e3d4ba9b774cfef3baf995deff455edc7363551d79089c90", "https://deno.land/x/nano_jsx@v0.1.0/ssr.ts": "1ab6c17f41f612fe409d29d81ce1f17692ff02e5abea6ef4080cabecafc246dd", "https://deno.land/x/nano_jsx@v0.1.0/state.ts": "cfc6ca6eb5e65f4cd60717a0a96ce604053bacf6e0b8835afbe22327e9979a1b", "https://deno.land/x/nano_jsx@v0.1.0/store.ts": "bf075833a656fc87b2a24c486bb3f77e13f19d6d9efcc42e31ffb6f24ec8eb93", "https://deno.land/x/nano_jsx@v0.1.0/types.ts": "fb338d7fa4422a92c442d14e073a2deb01d42b017fcd36013a1f6554e17c5449", "https://deno.land/x/nano_jsx@v0.1.0/version.ts": "d7f3813a3b1c490f16e77047483b0dc52cc0cec6cb1b73d6926cf9ad96129a59", "https://deno.land/x/nano_jsx@v0.1.0/withStyles.ts": "8aae4c8e79319fe991d49dd8ccc927c221786dfe58de640dba9d9885ce0dd4b7", "https://deno.land/x/zstd_wasm@0.0.21/deno/zstd.deno.js": "189187a43a4bdc8d695ff2f6525c8d3461cbd715333123e502a6a3bd84c272b4", "https://deno.land/x/zstd_wasm@0.0.21/deno/zstd.encoded.wasm.ts": "87e04064657bf5082b47a709931f8eb3f0d2fe537a8c650cfb5c997b7b460d07", "https://deno.land/x/zstd_wasm@0.0.21/deno/zstd.ts": "ba3889ee2b11f73ae7c6192da2851ec5f037b5ae7e3afa4c9129f33e856616ec",
-
@@ -225,6 +196,7 @@ "https://esm.sh/v135/@shelf/fast-uslug@1.0.0": "46107bc12821cd3fd8605a1a837fc9288a254d8e4e406126059b5a6031695ad0","https://esm.sh/v135/@shelf/fast-uslug@1.0.0/denonext/fast-uslug.mjs": "d140f64633d7ede6e407d8b9ea3236b6c614da96c5fce03297f353e735106cf0", "https://esm.sh/v135/@ungap/structured-clone@1.2.0/denonext/structured-clone.mjs": "e683ab48ef7a3afd3bce9d1589d14177ddbdbf76fa1483524dddbeb6b142469f", "https://esm.sh/v135/ccount@2.0.1/denonext/ccount.mjs": "7b32092651a866fcc992c028982ce5e911356da7653baa3febb1a8ccb93e30f8", "https://esm.sh/v135/character-entities-html4@2.1.0/denonext/character-entities-html4.mjs": "0b4e64d1b0152acbeec6d854eadce6ceb2de05b0f459ad47485afa206f745f10", "https://esm.sh/v135/character-entities-legacy@3.0.0/denonext/character-entities-legacy.mjs": "5da76ada1554e4956dc6b702ba92b56a3faf158b24bf45279c522e85f5d9cd21", "https://esm.sh/v135/character-entities@2.0.2/denonext/character-entities.mjs": "9e8657f056310ac3ca8058eaf96cef695ee13a4bf6c302674796a882464f305c", "https://esm.sh/v135/character-reference-invalid@2.0.1/denonext/character-reference-invalid.mjs": "41034e591247fb2bc6a12dd190e776b84cfe1da74e984fef3efbff2a97814d53",
-
@@ -234,7 +206,6 @@ "https://esm.sh/v135/devlop@1.1.0/denonext/devlop.mjs": "05fffa5a5168daec45963b784734dbc468758e130a340af874adfe0d457e394a","https://esm.sh/v135/entities@4.5.0/denonext/lib/decode.js": "7fea6d8bd725edbbf7ea05031d2ea1bbbc1166dc11e3345d541198dd2dc16f1e", "https://esm.sh/v135/entities@4.5.0/denonext/lib/escape.js": "7ebdc622bf3618bab25db40da4a49e2b9d03f044745f125f0bc3359f2d060def", "https://esm.sh/v135/escape-string-regexp@5.0.0/denonext/escape-string-regexp.mjs": "6080dd39c43a11f999a41172a27b8c58572d747d8276c039d93d4be8b21747a5", "https://esm.sh/v135/estree-util-is-identifier-name@3.0.0/denonext/estree-util-is-identifier-name.mjs": "2d1080530be602e98e40807bbb760a08a017b315be59fe869b57afca5d667dca", "https://esm.sh/v135/hast-util-from-parse5@8.0.1/denonext/hast-util-from-parse5.mjs": "1c3f8a5b4fab57bb4edfd4dd5646c6857c1fb0bd9aab82b10720035e10a26505", "https://esm.sh/v135/hast-util-is-element@3.0.0": "d5b1ded368a5fbbcb79687bebcf33d94a37020bd82116c1bec69a012f17e5cf2", "https://esm.sh/v135/hast-util-is-element@3.0.0/denonext/hast-util-is-element.mjs": "d6abf8aaf1da9775a8731295649387cf59ceb0b50eae25953bee6ea9af8043af",
-
@@ -242,16 +213,17 @@ "https://esm.sh/v135/hast-util-parse-selector@3.1.1/denonext/hast-util-parse-selector.mjs": "df576c8298a048d9aad89506f5884db91bc249bb6e221b9fd3b228eb40980a62","https://esm.sh/v135/hast-util-parse-selector@4.0.0/denonext/hast-util-parse-selector.mjs": "bd794c65760a29b2ba20eef4de4f94d4b480a45ae01d2047957143a37e4adbb5", "https://esm.sh/v135/hast-util-raw@9.0.2": "0ec383b32ef4ecbcbfa35f1107befae9038743edcadba6eb8c2c485f7e609101", "https://esm.sh/v135/hast-util-raw@9.0.2/denonext/hast-util-raw.mjs": "47b434e3ac191454283f7077edb75508d53ef5b52e5a025df0121941867f0624", "https://esm.sh/v135/hast-util-to-jsx-runtime@2.3.0": "d70b72b8ff9932e3953de3fbcf7690ca6174a30c9a48bd236b4aa743229c2cfb", "https://esm.sh/v135/hast-util-to-jsx-runtime@2.3.0/denonext/hast-util-to-jsx-runtime.mjs": "c94c36c11225b1d6f36dc7e3f13597a223058510eceb11fa97b844a3bd176fc4", "https://esm.sh/v135/hast-util-to-html@9.0.1": "c15bb9378682abf08886257d1f99cbc58792fbe7b631cf08f273d88bfe98abf2", "https://esm.sh/v135/hast-util-to-html@9.0.1/denonext/hast-util-to-html.mjs": "acaefbe9b52e06225112183ec03bfe31aa4390a5615c08fca5d96c6c4be651ba", "https://esm.sh/v135/hast-util-to-parse5@8.0.0/denonext/hast-util-to-parse5.mjs": "b3ce8667b168b69115ada0ca9fe4ec2cd750fcee5a87c594fe1e50b516fa167a", "https://esm.sh/v135/hast-util-to-string@3.0.0": "efe413bc8cc4a67ac6b84b46e2fc2b0a79e4d7e4a31707cc8e60fab683b71080", "https://esm.sh/v135/hast-util-to-string@3.0.0/denonext/hast-util-to-string.mjs": "4f7c061733fa8e99e3055e443c09a8d4e7f6e95ccd7a06b61d38682989bf6ba8", "https://esm.sh/v135/hast-util-whitespace@3.0.0/denonext/hast-util-whitespace.mjs": "b2988a87d03b42636bca6ebee778326993a23953ba6638973be17ce03100a357", "https://esm.sh/v135/hastscript@7.2.0/denonext/hastscript.mjs": "21c0292f187622f364e3b3b91db0cdea352b3e5cb61b6557583a2c74b9d8d76e", "https://esm.sh/v135/hastscript@8.0.0/denonext/hastscript.mjs": "55a8079d45899d20a70c6968c3a46b15d247d223b02b1804b79f49bae5dc9a06", "https://esm.sh/v135/hastscript@9.0.0": "2593f19b64c9652d947dadf1c6a7bc59ed5574020c7fb2a182b35774c0b6f1d7", "https://esm.sh/v135/hastscript@9.0.0/denonext/hastscript.mjs": "30c8562cced3e4f8d4f1afb374220bb3faf1e12adf1fdb2ea2dd668b8cb58c2d", "https://esm.sh/v135/html-void-elements@3.0.0/denonext/html-void-elements.mjs": "331b966249908a14e21bcbd584cc9f88d8df7d78a4bed94f002c9fd297d17459", "https://esm.sh/v135/inline-style-parser@0.2.2/denonext/inline-style-parser.mjs": "6b516634a2a716b57fa335bdaf9910c568a01330bfb7686ca534228aff59149e", "https://esm.sh/v135/is-alphabetical@2.0.1/denonext/is-alphabetical.mjs": "d0feb3b8b248c1c89544825a12db253946e94532c3ba2df47c5ce9ed06a10242", "https://esm.sh/v135/is-alphanumerical@2.0.1/denonext/is-alphanumerical.mjs": "978a2d980f37c9b42b82e6dc915455d43a9ba5f7d11c6f314d5ef5694ed9427e", "https://esm.sh/v135/is-decimal@2.0.1/denonext/is-decimal.mjs": "d58375f587816fc947898747f8e6986158dec29151c185e7a510641b17668e46",
-
@@ -316,6 +288,8 @@ "https://esm.sh/v135/micromark@4.0.0/denonext/micromark.mjs": "dc73ed793c5bbc49ad7201a326fc724d08bc94372a291c184167b22c4edc320d","https://esm.sh/v135/parse-entities@4.0.1/denonext/parse-entities.mjs": "d850db8f1291c1f42a880afb290f6c0774871651092c3fed6f9dedfee8f971c4", "https://esm.sh/v135/parse5@7.1.2/denonext/parse5.mjs": "35bb04ec36a1c25c8cd8137296d64d16fd523a8ad1c2b63c41ba867fcd455c36", "https://esm.sh/v135/property-information@6.4.0/denonext/property-information.mjs": "8a61ad4cc589e3a30c752bd2eea7703cf286139c919f55f4fd289b90a7ac65a9", "https://esm.sh/v135/property-information@6.4.1/denonext/property-information.mjs": "71cc8e0ca4326dc44fc10219cdd3c7c3363434147f99f20b875745eda465a865", "https://esm.sh/v135/property-information@6.5.0/denonext/property-information.mjs": "462b8eb379aa32acbc3c144ad6e9442bc96f01c766e10d6467a121c80d62ab71", "https://esm.sh/v135/refractor@4.8.1/denonext/lang/abap.js": "d172cd4efeb8a73131e075ac5b1a598da105375b75e6d11c7eccd79b667e1758", "https://esm.sh/v135/refractor@4.8.1/denonext/lang/abnf.js": "1a600d9650e59f20dffbc0a01b177596f11e5bea31b7494912e1e0a195b7a2a3", "https://esm.sh/v135/refractor@4.8.1/denonext/lang/actionscript.js": "1038a44a594c56dc8dabdc96894f54dd3bd44726632a257aabd2a443ee332744",
-
@@ -616,7 +590,8 @@ "https://esm.sh/v135/refractor@4.8.1/denonext/lang/zig.js": "834d13aafc434045b3497f691d2bd72ce5eb217ba869f4f1c1378e48373a0ff1","https://esm.sh/v135/refractor@4.8.1/denonext/lib/all.js": "047b43ef41e7d61def9ad5ab1013b7741826d32192a8d2d14496639d592a50ba", "https://esm.sh/v135/refractor@4.8.1/denonext/lib/core.js": "388b70847fd55fbd1063166d45d2a327f7ad3a113c79d30c1d52017a7266b69d", "https://esm.sh/v135/space-separated-tokens@2.0.2/denonext/space-separated-tokens.mjs": "f30773a9959cacfe7511c250e5d125a9f88ee00d3aef6e87b4d17fe49806b276", "https://esm.sh/v135/style-to-object@1.0.5/denonext/style-to-object.mjs": "fa46c0fbe36f636aec3f7a3f5af231c37b9265a444c343cd3067ec2d214c18d0", "https://esm.sh/v135/stringify-entities@4.0.3/denonext/stringify-entities.mjs": "379aaecccdf6a05e9725a5eff4de4fa61193f0a793ddacaa1033f071bd0e2842", "https://esm.sh/v135/stringify-entities@4.0.4/denonext/stringify-entities.mjs": "dc80c6c09409f443a2f6eb08b9712ce2887c1d0a475c17b5634b39af739c098d", "https://esm.sh/v135/trim-lines@3.0.1/denonext/trim-lines.mjs": "f01a20253341eb2554f307ab05bc9cd93d6f33bcbb24fde2fc9fcd857564283e", "https://esm.sh/v135/unist-util-is@6.0.0/denonext/unist-util-is.mjs": "d92da46b3a1084450f150cc06c02f3bf85b93ab4c4b43a960d72a4f5678e95cc", "https://esm.sh/v135/unist-util-position@5.0.0/denonext/unist-util-position.mjs": "aaef05774a54f2b84400e98325219e2ba6cf966e318173bcd895647c56bb8871",
-
@@ -628,7 +603,6 @@ "https://esm.sh/v135/unist-util-visit-parents@6.0.1/denonext/unist-util-visit-parents.mjs": "5f7ececae47bea6d87b7e323153a7415ad8f299dc42e61aefda6d28eaf264c64","https://esm.sh/v135/unist-util-visit@5.0.0": "39c6a28445ca31c6ad1e97c663c9d6f86d8820a2b5893c77e2d363cb630c8dc5", "https://esm.sh/v135/unist-util-visit@5.0.0/denonext/unist-util-visit.mjs": "6c1b5b3d517cc6dbc88406b2dbab1735d503a797e994dbd4a89f3764098318f7", "https://esm.sh/v135/vfile-location@5.0.2/denonext/vfile-location.mjs": "9a7912bc394ed9af26d91b97db831926b0e8ce965ae20f000f25ab17c2658d03", "https://esm.sh/v135/vfile-message@4.0.2/denonext/vfile-message.mjs": "efc85b18bedda337fb1c20cdc452fac3addac32ee55948cebf2845396ae641ac", "https://esm.sh/v135/web-namespaces@2.0.1/denonext/web-namespaces.mjs": "bc6ee363bbe40abddca68678e849584981176d9bde3728b4c5d47a5241dac7bf", "https://esm.sh/v135/zwitch@2.0.4/denonext/zwitch.mjs": "c0e8c246a1f38b425335ea78cc366a7801d3ef89701229a35f85a51310e6e49f" }
-
-
-
@@ -1,5 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 export * from "https://deno.land/x/nano_jsx@v0.1.0/jsx-runtime/index.ts";
-
-
-
@@ -2,4 +2,4 @@ // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>// // SPDX-License-Identifier: Apache-2.0 export * from "https://deno.land/x/nano_jsx@v0.1.0/mod.ts"; export * from "https://esm.sh/v135/hast-util-to-html@9.0.1";
-
-
-
@@ -2,4 +2,4 @@ // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>// // SPDX-License-Identifier: Apache-2.0 export * from "https://esm.sh/v135/hast-util-to-jsx-runtime@2.3.0"; export * from "https://esm.sh/v135/hastscript@9.0.0";
-
-
-
@@ -17,7 +17,7 @@ } from "../tree_builder/default_tree_builder.ts";import { ObsidianMarkdownParser } from "../content_parser/obsidian_markdown.ts"; import { JSONCanvasParser } from "../content_parser/json_canvas.ts"; import { oneof } from "../content_parser/oneof.ts"; import { DefaultThemeBuilder } from "../page_builder/default_theme/builder.tsx"; import { DefaultThemeBuilder } from "../page_builder/default_theme/mod.ts"; export async function build() { const outDir = new URL("./.dist", import.meta.url);
-
-
-
@@ -2,10 +2,8 @@ // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>// // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h, renderSSR } from "../../deps/deno.land/x/nano_jsx/mod.ts"; import type * as Mdast from "../../deps/esm.sh/mdast/types.ts"; import { toHtml } from "../../deps/esm.sh/hast-util-to-html/mod.ts"; import { logger } from "../../logger.ts";
-
@@ -26,14 +24,29 @@ DocumentTree,} from "../../types.ts"; import * as css from "./css.ts"; import { globalStyles } from "./global_styles.ts"; import type { Assets, BuildContext } from "./context.ts"; import * as Html from "./components/html.tsx"; import * as HastRenderer from "./components/atoms/hast_renderer.tsx"; import { tocMut } from "./hast/hast_util_toc_mut.ts"; import { mapTocItem, tocMut } from "./hast/hast_util_toc_mut.ts"; import { PathResolverProvider } from "./contexts/path_resolver.tsx"; import { fromMdast, fromMdastStyles, style as styleMarkdownContent, } from "./from_mdast/mod.ts"; import { indexRedirect } from "./pages/index_redirect.tsx"; import { markdownEmbed, markdownPage, markdownPageStyles, } from "./pages/markdown.tsx"; import { jsonCanvasEmbed, jsonCanvasPage, jsonCanvasPageStyles, } from "./pages/json_canvas.tsx"; const DOCTYPE = "<!DOCTYPE html>"; export type { BuildContext } from "./context.ts"; function isAssetToken(token: unknown): token is AssetToken { return typeof token === "string" && token.startsWith("mxa_");
-
@@ -58,16 +71,18 @@function toRelativePath( path: readonly string[], from: readonly string[], ): string { return Array.from({ length: from.length }, () => "../").join("") + path.join("/"); ): readonly string[] { return [ ...Array.from({ length: from.length }, () => ".."), ...path, ]; } export interface Assets { globalCss: readonly string[]; faviconSvg?: readonly string[]; faviconPng?: readonly string[]; siteLogo?: readonly string[]; function toRelativePathString( path: readonly string[], from: readonly string[], ): string { return toRelativePath(path, from).join("/"); } interface InnerBuildParameters {
-
@@ -139,7 +154,12 @@ ) {const start = performance.now(); const styles = css.serialize( Html.styles, css.join( globalStyles, fromMdastStyles, markdownPageStyles, jsonCanvasPageStyles, ), ); const assets: Assets = {
-
@@ -178,16 +198,7 @@ );} const defaultPage = [...documentTree.defaultDocument.path, ""].join("/"); const redirectHtml = [ "<!DOCTYPE html>", "<html><head>", `<meta charset="utf-8">`, `<meta http-equiv="refresh" content="0; URL='${defaultPage}'">`, "</head><body>", // For cases when a user or UA disallows automatic redirection. `<a href="${defaultPage}">TOP</a>`, "</body></html>", ].join(""); const redirectHtml = toHtml(indexRedirect({ redirectTo: defaultPage })); await fileSystemWriter.write( ["index.html"], new TextEncoder().encode(redirectHtml),
-
@@ -217,6 +228,19 @@ ): Promise<void> {const { fileSystemWriter } = buildParameters; if ("file" in item) { const context: BuildContext = { document: item, documentTree: tree, language: item.metadata.language || parentLanguage, assets, websiteTitle: this.#siteName, copyright: this.#copyright, resolvePath(to) { // This page builder transforms path to "Foo/Bar.md" to "Foo/Bar/(index.html)" return toRelativePath(to, [...item.path, ""]); }, }; if (isObsidianMarkdown(item) || isJSONCanvas(item)) { const enc = new TextEncoder();
-
@@ -244,7 +268,7 @@ file.path,await file.read(), )); return toRelativePath(file.path, item.path); return toRelativePathString(file.path, item.path); }, );
-
@@ -254,7 +278,7 @@ async (token) => {const document = tree.exchangeToken(token); return { path: toRelativePath( path: toRelativePathString( [...document.path, ""], item.path, ),
-
@@ -264,11 +288,7 @@ );return { ...node, text: ( <HastRenderer.View node={HastRenderer.mdastToHast(node.text)} /> ), text: fromMdast(node.text), }; } case "file": {
-
@@ -281,7 +301,7 @@ );return { ...node, file: toRelativePath(file.path, item.path), file: toRelativePathString(file.path, item.path), }; }
-
@@ -290,7 +310,7 @@ const doc = tree.exchangeToken(node.file);return { ...node, file: toRelativePath( file: toRelativePathString( [...doc.path, "embed.html"], item.path, ),
-
@@ -306,24 +326,10 @@ }}, ); const document = item as Document<JSONCanvasDocument<Mdast.Nodes>>; const html = DOCTYPE + renderSSR( () => ( // Adds 1 to depth due to `<name>/index.html` conversion. <PathResolverProvider depth={pathPrefix.length + 1}> <Html.JSONCanvasView title={this.#siteName} tree={tree} copyright={this.#copyright} content={content} document={item} language={item.metadata.language || parentLanguage} assets={assets} /> </PathResolverProvider> ), ); const html = toHtml(jsonCanvasPage({ content, context, })); writeTasks.push( fileSystemWriter.write([
-
@@ -332,18 +338,10 @@ "index.html",], enc.encode(html)), ); const embed = DOCTYPE + renderSSR( () => ( <PathResolverProvider depth={pathPrefix.length + 1}> <Html.JSONCanvasEmbed document={document} language={item.metadata.language || parentLanguage} content={content} assets={assets} /> </PathResolverProvider> ), ); const embed = toHtml(jsonCanvasEmbed({ context, content, })); writeTasks.push( fileSystemWriter.write([
-
@@ -366,7 +364,7 @@ file.path,await file.read(), )); return toRelativePath(file.path, item.path); return toRelativePathString(file.path, item.path); }, );
-
@@ -376,42 +374,19 @@ async (token) => {const document = tree.exchangeToken(token); return { path: toRelativePath([...document.path, ""], item.path), path: toRelativePathString([...document.path, ""], item.path), }; }, ); const document = item as Document<ObsidianMarkdownDocument>; const hast = HastRenderer.mdastToHast(item.content.content); const renderedNode = <HastRenderer.View node={hast} />; const hast = fromMdast(item.content.content); const toc = tocMut(hast); const html = DOCTYPE + renderSSR( () => ( // Adds 1 to depth due to `<name>/index.html` conversion. <PathResolverProvider depth={pathPrefix.length + 1}> <Html.ObsidianMarkdownView title={this.#siteName} document={item} language={item.metadata.language || parentLanguage} assets={assets} content={renderedNode} tree={tree} copyright={this.#copyright} toc={tocMut(hast).map((node) => { return mapTocItem( node, (item) => ( <HastRenderer.View node={{ type: "root", children: item }} wrapAndStyle={false} /> ), ); })} /> </PathResolverProvider> ), ); const html = toHtml(markdownPage({ context, content: styleMarkdownContent(hast), tocItems: toc, })); writeTasks.push( fileSystemWriter.write([
-
@@ -420,18 +395,10 @@ "index.html",], enc.encode(html)), ); const embed = DOCTYPE + renderSSR( () => ( <PathResolverProvider depth={pathPrefix.length + 1}> <Html.ObsidianMarkdownEmbed document={document} language={item.metadata.language || parentLanguage} content={hast} assets={assets} /> </PathResolverProvider> ), ); const embed = toHtml(markdownEmbed({ context, content: styleMarkdownContent(hast), })); writeTasks.push( fileSystemWriter.write([
-
-
-
@@ -1,337 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ /** @jsxFrag Fragment */ import type * as Hast from "../../../../deps/esm.sh/hast/types.ts"; import type * as Mdast from "../../../../deps/esm.sh/mdast/types.ts"; import { h } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import * as jsxRuntime from "../../../../deps/deno.land/x/nano_jsx/jsx-runtime/index.ts"; import { toJsxRuntime } from "../../../../deps/esm.sh/hast-util-to-jsx-runtime/mod.ts"; import * as HastToJSXRuntime from "../../../../deps/esm.sh/hast-util-to-jsx-runtime/mod.ts"; import { type State, toHast, } from "../../../../deps/esm.sh/mdast-util-to-hast/mod.ts"; import { ofmHtml, ofmToHastHandlers, } from "../../../../content_parser/obsidian_markdown.ts"; import { parseOfmCalloutNode } from "../../../../content_parser/obsidian_markdown/mdast_util_ofm_callout.ts"; import { syntaxHighlightingHandlers } from "../../mdast/syntax_highlighting_handlers.ts"; import { css, join as joinCss } from "../../css.ts"; import * as callout from "../from-hast/callout.tsx"; import * as taskList from "../from-hast/task_list.tsx"; const enum C { Wrapper = "a--hr", } const ownStyles = css` :where(.${C.Wrapper}) a { color: var(--color-fg-sub); font-weight: 500; text-decoration: underline; transition: color 0.15s ease; } :where(.${C.Wrapper}) a:hover { color: var(--color-primary); } :where(.${C.Wrapper}) p { margin: 0; margin-top: calc(var(--baseline) * 1rem); } :where(.${C.Wrapper}) pre { margin: 0; margin-top: calc(var(--baseline) * 1rem) !important; padding: calc(var(--baseline) * 1rem) 1em !important; line-height: calc(var(--baseline) * 1rem); max-width: 100%; font-size: 1rem; background-color: var(--color-fg); color: var(--color-bg); border-radius: calc(1rem / 4); overflow-x: auto; } :where(.${C.Wrapper}) pre > code { all: unset; } :where(.${C.Wrapper}) code { margin: 0 0.2em; padding: calc(1rem / 4); background-color: var(--color-bg-accent); color: var(--color-fg-sub); border-radius: calc(1rem / 4); font-family: "Ubuntu Mono", monospace; } :where(.${C.Wrapper}) pre > code .token.comment { font-style: italic; } :where(.${C.Wrapper}) a, :where(.${C.Wrapper}) time, :where(.${C.Wrapper}) span, :where(.${C.Wrapper}) code, :where(.${C.Wrapper}) sup, :where(.${C.Wrapper}) small, :where(.${C.Wrapper}) s, :where(.${C.Wrapper}) b, :where(.${C.Wrapper}) i { line-height: 1; } :where(.${C.Wrapper}) button { font-family: inherit; } :where(.${C.Wrapper}) s, :where(.${C.Wrapper}) del { color: var(--color-fg-sub); text-decoration: line-through; } :where(.${C.Wrapper}) b { font-weight: bold; } :where(.${C.Wrapper}) i { font-style: italic; } :where(.${C.Wrapper}) ul { margin: 0; margin-top: calc(var(--baseline) * 1rem); padding-left: 1.5em; } :where(.${C.Wrapper}) ul ul { margin-top: 0; } :where(.${C.Wrapper}) h1, :where(.${C.Wrapper}) h2, :where(.${C.Wrapper}) h3 { font-weight: 700; color: var(--color-fg-sub); } :where(.${C.Wrapper}) h1 { margin: 0; margin-top: calc(var(--baseline) * 2rem); line-height: calc(var(--baseline) * 2rem); } :where(.${C.Wrapper}) h2 { margin: 0; margin-top: calc(var(--baseline) * 2rem); } :where(.${C.Wrapper}) h3, :where(.${C.Wrapper}) h4, :where(.${C.Wrapper}) h5, :where(.${C.Wrapper}) h6 { margin: 0; margin-top: calc(var(--baseline) * 1rem); font-weight: 600; } :where(.${C.Wrapper}) table { border-spacing: 0; margin: 0; margin-top: calc(var(--baseline) * 0.5rem); width: 100%; } :where(.${C.Wrapper}) thead { background-color: var(--color-bg-accent); } :where(.${C.Wrapper}) th { font-weight: 500; padding: calc(var(--baseline) * 0.25rem) 1em; } :where(.${C.Wrapper}) td { padding: calc(var(--baseline) * 0.5rem) 1em; } :where(.${C.Wrapper}) tbody td { position: relative; } :where(.${C.Wrapper}) tbody td::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; border-bottom: 1px solid var(--color-fg-light); } :where(.${C.Wrapper}) hr { margin: 0; margin-top: calc(var(--baseline) * 1rem); } :where(.${C.Wrapper}) img { max-width: 100%; } :where(.${C.Wrapper}) img:not(:first-child) { margin-top: calc(var(--baseline) * 1rem); } `; export const styles = joinCss(ownStyles, callout.styles, taskList.styles); function nanoifyProps(props: HastToJSXRuntime.Props): HastToJSXRuntime.Props { const ret: HastToJSXRuntime.Props = {}; for (const key in props) { switch (props[key]) { // nanojsx cannot handle falsy attribute correctly case false: case null: break; // ideal `true` for boolean attribute is empty string, but nanojsx emits `"true"`. case true: ret[key] = ""; break; default: ret[key] = props[key]; break; } } return ret; } export function render(hast: Hast.Nodes, wrapAndStyle: boolean = true) { return toJsxRuntime(hast, { components: wrapAndStyle ? { "macana-ofm-callout": callout.MacanaOfmCallout, "macana-ofm-callout-title": callout.MacanaOfmCalloutTitle, "macana-ofm-callout-body": callout.MacanaOfmCalloutBody, "macana-gfm-task-list": taskList.MacanaGfmTaskList, "macana-gfm-task-list-item": taskList.MacanaGfmTaskListItem, } : {}, Fragment: jsxRuntime.Fragment, jsx(type, props, key) { return jsxRuntime.jsx(type, nanoifyProps(props), key || ""); }, jsxs(type, props, key) { return jsxRuntime.jsxs(type, nanoifyProps(props), key || ""); }, }); } export function mdastToHast(input: Mdast.Nodes): Hast.Nodes { return ofmHtml(toHast(input, { handlers: { ...ofmToHastHandlers({ callout: { generateIcon(type) { return { type: "element", tagName: "macana-ofm-callout-icon", properties: { type, }, children: [], }; }, }, }), // @ts-expect-error: unist-related libraries heavily relies on ambient module declarations, // which Deno does not support. APIs also don't accept type parameters. ofmCallout(state: State, node: OfmCallout): Hast.Nodes { const { title, body, type } = parseOfmCalloutNode(state, node); return { type: "element", tagName: "macana-ofm-callout", properties: { type: type, foldable: node.isFoldable, defaultExpanded: node.defaultExpanded, }, children: [ { type: "element", tagName: "macana-ofm-callout-title", properties: {}, children: title, }, { type: "element", tagName: "macana-ofm-callout-body", properties: {}, children: body, }, ], }; }, list(state: State, node: Mdast.List): Hast.ElementContent { return { type: "element", tagName: "macana-gfm-task-list", properties: { ordered: node.ordered, }, children: state.all(node), }; }, listItem(state: State, node: Mdast.ListItem): Hast.ElementContent { const children = state.all(node).map((child) => child.type === "element" && child.tagName === "p" ? child.children : [child] ).flat(); return { type: "element", tagName: "macana-gfm-task-list-item", properties: { checked: !!node.checked, is_task: typeof node.checked === "boolean", }, children, }; }, ...syntaxHighlightingHandlers(), }, allowDangerousHtml: true, })); } export interface ViewProps { node: Hast.Nodes; wrapAndStyle?: boolean; } export function View({ node, wrapAndStyle = true }: ViewProps) { if (!wrapAndStyle) { return render(node); } return ( <div className={C.Wrapper}> {render(node)} </div> ); }
-
-
page_builder/default_theme/components/atoms/json_canvas_renderer.tsx > page_builder/default_theme/json_canvas/mod.tsx
-
@@ -2,16 +2,13 @@ // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>// // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ /** @jsxFrag Fragment */ /** @jsx s */ import { extname } from "../../../../deps/deno.land/std/path/mod.ts"; import { Fragment, h } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import { logger } from "../../../../logger.ts"; import { css } from "../../css.ts"; import { extname } from "../../../deps/deno.land/std/path/mod.ts"; import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import { h, s } from "../../../deps/esm.sh/hastscript/mod.ts"; import { logger } from "../../../logger.ts"; import type { CanvasColor, FileNode,
-
@@ -21,7 +18,9 @@ LinkNode,Node, NodeSide, TextNode, } from "../../../../content_parser/json_canvas/types.ts"; } from "../../../content_parser/json_canvas/types.ts"; import { css } from "../css.ts"; import { getBoundingBox,
-
@@ -32,14 +31,14 @@ move,type Point2D, type Vec2D, vecMul, } from "./json_canvas_renderer/layout.ts"; } from "./layout.ts"; const enum C { Wrapper = "jcr--wr", Embed = "jcr--em", Wrapper = "jc--wr", Embed = "jc--em", } export const styles = css` export const jsonCanvasStyles = css` .${C.Wrapper} { overflow: auto; max-width: 100%;
-
@@ -96,11 +95,11 @@ return key + ":" + (typeof value === "number" ? value + "px" : value);}).filter((s): s is string => !!s).join(";"); } interface TextNodeRendererProps { node: TextNode<unknown>; interface TextNodeProps { node: TextNode<Hast.Nodes>; } function TextNodeRenderer({ node }: TextNodeRendererProps) { function textNode({ node }: TextNodeProps) { const containerStyle: StyleConstructor = { width: node.width, height: node.height,
-
@@ -122,7 +121,7 @@ ><div xmlns="http://www.w3.org/1999/xhtml" style={constructStyle(containerStyle)} className={C.Embed} class={C.Embed} > {node.text} </div>
-
@@ -130,11 +129,11 @@ </foreignObject>); } interface LinkNodeRendererProps { interface LinkNodeProps { node: LinkNode; } function LinkNodeRenderer({ node }: LinkNodeRendererProps) { function linkNode({ node }: LinkNodeProps) { const iframeStyles: StyleConstructor = { width: node.width, height: node.height,
-
@@ -162,11 +161,11 @@ </foreignObject>); } interface FileNodeRendererProps { interface FileNodeProps { node: FileNode; } function FileNodeRenderer({ node }: FileNodeRendererProps) { function fileNode({ node }: FileNodeProps) { const ext = extname(node.file); switch (ext) {
-
@@ -279,7 +278,7 @@ /*** Renders text with background and padding. * SVG does not have this functionality, so we need to roll our own... */ function BoxText( function boxText( { label, x = 0,
-
@@ -371,13 +370,13 @@ </foreignObject>); } interface GroupNodeRendererProps { interface GroupNodeProps { node: GroupNode; } function GroupNodeRenderer({ node }: GroupNodeRendererProps) { function groupNode({ node }: GroupNodeProps) { if (!node.label) { return <></>; return null; } const color = node.color
-
@@ -385,36 +384,34 @@ ? canvasColorToCssColor(node.color): "var(--canvas-color-fallback)"; // TODO: Make fg color configurable return ( <BoxText x={node.x} y={node.y - 6} background={color} color="var(--color-bg)" padding={4} radius={3} fontSize={20} label={node.label} hAlign="left" vAlign="bottom" /> ); return boxText({ x: node.x, y: node.y - 6, background: color, color: "var(--color-bg)", padding: 4, radius: 3, fontSize: 20, label: node.label, hAlign: "left", vAlign: "bottom", }); } interface NodeRendererProps { node: Node<unknown>; node: Node<Hast.Nodes>; } function NodeRenderer({ node }: NodeRendererProps) { function nodeRenderer({ node }: NodeRendererProps) { switch (node.type) { case "text": return <TextNodeRenderer node={node} />; return textNode({ node }); case "link": return <LinkNodeRenderer node={node} />; return linkNode({ node }); case "file": return <FileNodeRenderer node={node} />; return fileNode({ node }); case "group": return <GroupNodeRenderer node={node} />; return groupNode({ node }); } }
-
@@ -429,7 +426,7 @@ fill?: string;stroke?: string; } function EdgeArrow({ target, pointTo, size = 15, ...rest }: EdgeArrowProps) { function edgeArrow({ target, pointTo, size = 15, ...rest }: EdgeArrowProps) { const start = `M${target[0]},${target[1]}`; const width = size * 1.2;
-
@@ -464,17 +461,16 @@ />); } export interface JSONCanvasRendererProps { // nano-jsx does not ship working typings, thus "unknown" (no JSX.Element). data: JSONCanvas<unknown>; export interface JSONCanvasProps { data: JSONCanvas<Hast.Nodes>; radius?: number; arrowSize?: number; } export function JSONCanvasRenderer( { data, radius = 6, arrowSize = 20 }: JSONCanvasRendererProps, export function jsonCanvas( { data, radius = 6, arrowSize = 20 }: JSONCanvasProps, ) { const boundingBox = getBoundingBox(data);
-
@@ -537,7 +533,7 @@ stroke-width="var(--canvas-node-stroke-width)"rx={radius} ry={radius} /> <NodeRenderer node={node} /> {nodeRenderer({ node })} </g> ); })}
-
@@ -639,34 +635,40 @@ stroke={color}stroke-width="var(--canvas-edge-stroke-width)" fill="none" /> {edge.fromEnd === "arrow" && ( <EdgeArrow target={fromPoint} pointTo={fromSide} fill={color} size={arrowSize} /> )} {edge.toEnd !== "none" && ( <EdgeArrow target={toPoint} pointTo={toSide} fill={color} size={arrowSize} /> )} {edge.label && ( <BoxText label={edge.label} x={(p1[0] + p2[0]) * 0.5} y={(p1[1] + p2[1]) * 0.5} background="var(--color-bg)" color="var(--color-fg)" fontSize={18} padding={4} radius={2} /> )} {edge.fromEnd === "arrow" ? ( edgeArrow({ target: fromPoint, pointTo: fromSide, fill: color, size: arrowSize, }) ) : null} {edge.toEnd !== "none" ? ( edgeArrow({ target: toPoint, pointTo: toSide, fill: color, size: arrowSize, }) ) : null} {edge.label ? ( boxText({ label: edge.label, x: (p1[0] + p2[0]) * 0.5, y: (p1[1] + p2[1]) * 0.5, background: "var(--color-bg)", color: "var(--color-fg)", fontSize: 18, padding: 4, radius: 2, }) ) : null} </g> ); })}
-
@@ -674,12 +676,6 @@ </svg>); } export type ViewProps = JSONCanvasRendererProps; export function View({ data }: ViewProps) { return ( <div className={C.Wrapper}> <JSONCanvasRenderer data={data} /> </div> ); export function wrappedJsonCanvas(props: JSONCanvasProps) { return h("div", { class: C.Wrapper }, jsonCanvas(props)); }
-
-
page_builder/default_theme/components/atoms/json_canvas_renderer/layout.ts > page_builder/default_theme/json_canvas/layout.ts
-
@@ -6,7 +6,7 @@ import type {JSONCanvas, Node, NodeSide, } from "../../../../../content_parser/json_canvas/types.ts"; } from "../../../content_parser/json_canvas/types.ts"; export interface Rect { x: number;
-
-
-
@@ -1,38 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import { css } from "../../css.ts"; const enum C { Title = "a--title", } export const styles = css` .${C.Title} { font-weight: 700; font-size: 2rem; margin: 0; line-height: calc(var(--baseline) * 2rem); color: var(--color-fg-sub); } `; export interface ViewProps { className?: string; children: JSX.ElementChildrenAttribute["children"]; } export function View({ className, children }: ViewProps) { return ( <h1 className={[className, C.Title].filter((s) => !!s).join(" ")}> {children} </h1> ); }
-
-
-
@@ -1,368 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { createContext, h, useContext, } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import { type CalloutType } from "../../../../content_parser/obsidian_markdown.ts"; import { css } from "../../css.ts"; import * as LucideIcons from "../lucide_icons.tsx"; const enum C { Root = "fh-co--root", Title = "fh-co--title", TitleText = "fh-co--tt", Body = "fh-co--b", Icon = "fh-co--i", Bg = "fg-co--g", Chevron = "fg-co--c", } export const styles = css` .${C.Root} { --_macana-callout-overlay: hsl(0deg 0% 0% / 0.03); --_macana-callout-color: var(--callout-color-info, var(--obsidian-color-fallback)); position: relative; margin: 0; margin-top: calc(var(--baseline) * 1rem); padding: 0; line-height: calc(var(--baseline) * 1rem); max-width: 100%; font-size: 1rem; border: 1px solid var(--_macana-callout-color); border-radius: 4px; } .${C.Root}[data-type="todo"] { --_macana-callout-color: var(--callout-color-todo); } .${C.Root}[data-type="tip"] { --_macana-callout-color: var(--callout-color-tip); } .${C.Root}[data-type="success"] { --_macana-callout-color: var(--callout-color-success); } .${C.Root}[data-type="question"] { --_macana-callout-color: var(--callout-color-question); } .${C.Root}[data-type="warning"] { --_macana-callout-color: var(--callout-color-warning); } .${C.Root}[data-type="failure"] { --_macana-callout-color: var(--callout-color-failure); } .${C.Root}[data-type="danger"] { --_macana-callout-color: var(--callout-color-danger); } .${C.Root}[data-type="bug"] { --_macana-callout-color: var(--callout-color-bug); } .${C.Root}[data-type="example"] { --_macana-callout-color: var(--callout-color-example); } .${C.Root}[data-type="quote"] { --_macana-callout-color: var(--callout-color-quote); } .${C.Bg} { position: absolute; inset: 0; background-color: var(--_macana-callout-color); pointer-events: none; opacity: 0.02; } @media (prefers-color-scheme: dark) { .${C.Root} { --_macana-callout-overlay: hsl(0deg 0% 100% / 0.1); } .${C.Bg} { opacity: 0.05; } } .${C.Title} { font-size: 1.1rem; display: flex; justify-content: flex-start; align-items: center; gap: 0.25em; padding: calc(var(--baseline) * 0.5rem) 8px; border-bottom: 1px solid var(--_macana-callout-overlay); margin-top: 0; font-weight: 700; } summary.${C.Title} { cursor: pointer; } summary.${C.Title}:hover { background-color: var(--_macana-callout-overlay); } details:not([open]) > summary.${C.Title} { border-bottom-color: transparent; } .${C.Icon} { color: var(--_macana-callout-color); } .${C.TitleText} { line-height: calc(var(--baseline) * 1rem); } .${C.Chevron} { transition: transform 0.15s ease-out; } details:not([open]) .${C.Chevron} { transform: rotate(-90deg); } .${C.Body} { font-size: 1rem; padding: calc(var(--baseline) * 0.5rem) 12px; } .${C.Body} > :first-child { margin-block-start: 0; } `; interface CalloutContextValue { foldable: boolean; defaultExpanded: boolean; type: CalloutType; titleId: string; } const CalloutContext = createContext( { foldable: false, defaultExpanded: false, type: "note", titleId: "", } satisfies CalloutContextValue, ); export interface ViewProps { type?: CalloutType; foldable?: "" | false; defaultExpanded?: "" | false; children: JSX.ElementChildrenAttribute["children"]; } let counter = 0; export function MacanaOfmCallout( { type = "note", foldable = false, defaultExpanded = false, children }: ViewProps, ) { const titleId = "__macana_callout__" + (counter++).toString(16); const isFoldable = typeof foldable === "string"; const isDefaultExpanded = typeof defaultExpanded === "string"; const contextValue: CalloutContextValue = { foldable: isFoldable, defaultExpanded: isDefaultExpanded, type, titleId, }; if (!isFoldable) { return ( <aside className={C.Root} aria-labelledby={titleId} data-type={type} > <div className={C.Bg} /> <CalloutContext.Provider value={contextValue}> {children} </CalloutContext.Provider> </aside> ); } return ( <aside className={C.Root} aria-labelledby={titleId} data-type={type} > <div className={C.Bg} /> <details open={isDefaultExpanded ? "" : undefined}> <CalloutContext.Provider value={contextValue}> {children} </CalloutContext.Provider> </details> </aside> ); } interface IconProps { className?: string; type: CalloutType; } function Icon({ className, type }: IconProps) { switch (type) { case "abstract": return ( <LucideIcons.ClipboardList className={className} role="img" aria-label="Clipboard icon" /> ); case "info": return ( <LucideIcons.Info className={className} role="img" aria-label="Info icon" /> ); case "todo": return ( <LucideIcons.CircleCheck className={className} role="img" aria-label="Check icon" /> ); case "tip": return ( <LucideIcons.Flame className={className} role="img" aria-label="Flame icon" /> ); case "success": return ( <LucideIcons.Check className={className} role="img" aria-label="Check icon" /> ); case "question": return ( <LucideIcons.CircleHelp className={className} role="img" aria-label="Question icon" /> ); case "warning": return ( <LucideIcons.TriangleAlert className={className} role="img" aria-label="Warning icon" /> ); case "failure": return ( <LucideIcons.X className={className} role="img" aria-label="Cross icon" /> ); case "danger": return ( <LucideIcons.Zap className={className} role="img" aria-label="Lightning icon" /> ); case "bug": return ( <LucideIcons.Bug className={className} role="img" aria-label="Bug icon" /> ); case "example": return ( <LucideIcons.List className={className} role="img" aria-label="List icon" /> ); case "quote": return ( <LucideIcons.Quote className={className} role="img" aria-label="Quote icon" /> ); case "note": default: return ( <LucideIcons.Pencil className={className} role="img" aria-label="Pencil icon" /> ); } } export interface MacanaOfmCalloutTitleProps { children: JSX.ElementChildrenAttribute["children"]; } export function MacanaOfmCalloutTitle( { children }: MacanaOfmCalloutTitleProps, ) { const { titleId, type, foldable }: CalloutContextValue = useContext( CalloutContext, ); if (foldable) { return ( <summary className={C.Title} id={titleId}> <Icon className={C.Icon} type={type} /> <span className={C.TitleText}>{children}</span> <LucideIcons.ChevronDown className={C.Chevron} aria-hidden="true" /> </summary> ); } return ( <p className={C.Title} id={titleId}> <Icon className={C.Icon} type={type} /> <span className={C.TitleText}>{children}</span> </p> ); } export interface MacanaOfmCalloutBodyProps { children: JSX.ElementChildrenAttribute["children"]; } export function MacanaOfmCalloutBody({ children }: MacanaOfmCalloutBodyProps) { return <div className={C.Body}>{children}</div>; }
-
-
-
@@ -1,108 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import { css } from "../../css.ts"; import * as LucideIcons from "../lucide_icons.tsx"; const enum C { TaskItem = "fh-tl--i", Checkbox = "fh-tl--c", Check = "fh-tl--h", Text = "fh-tl--t", } export const styles = css` .${C.TaskItem} { display: flex; align-items: start; list-style: none; } .${C.Checkbox} { display: flex; align-items: stretch; justify-content: stretch; width: calc(1em - 2px); height: calc(1em - 2px); aspect-ratio: 1 / 1; border: 1px solid currentColor; margin: 0; margin-block-start: calc((var(--baseline) * 1rem - 1em) * 0.5 + 1px); margin-inline-start: calc(-1em - 2px); margin-inline-end: 4px; border-radius: 2px; } .${C.Check} { min-width: 0px; min-height: 0px; height: 100%; } .${C.Text} { line-height: calc(var(--baseline) * 1rem); } `; export interface MacanaGfmTaskListProps { ordered?: "" | false; children: unknown; } export function MacanaGfmTaskList( { ordered, children }: MacanaGfmTaskListProps, ) { const Tag = ordered ? "ol" : "ul"; return ( <Tag> {children} </Tag> ); } let checkCounter = 0; export interface MacanaGfmTaskListItemProps { is_task: "" | false; checked?: "" | false; children: unknown; } export function MacanaGfmTaskListItem( { children, checked, is_task }: MacanaGfmTaskListItemProps, ) { if (typeof is_task !== "string") { return <li>{children}</li>; } const isChecked = typeof checked === "string"; const labelId = `__macana_tcheck_lbl__${checkCounter++}`; return ( <li className={C.TaskItem}> <span className={C.Checkbox} role="checkbox" aria-disabled="true" tabindex="0" aria-checked={isChecked.toString()} aria-labelledby={labelId} > {isChecked && ( <LucideIcons.Check className={C.Check} aria-hidden="true" /> )} </span> <span id={labelId} className={C.Text}>{children}</span> </li> ); }
-
-
page_builder/default_theme/components/global_styles.ts > page_builder/default_theme/global_styles.ts
-
@@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>// // SPDX-License-Identifier: Apache-2.0 import { css } from "../css.ts"; import { css } from "./css.ts"; export const globalStyles = css` /* Variables */
-
-
-
@@ -1,280 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ /** @jsxFrag Fragment */ import { Fragment, h } from "../../../deps/deno.land/x/nano_jsx/mod.ts"; import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import type { Document, DocumentMetadata, DocumentTree, } from "../../../types.ts"; import type { JSONCanvas } from "../../../content_parser/json_canvas/types.ts"; import { usePathResolver } from "../contexts/path_resolver.tsx"; import * as css from "../css.ts"; import { globalStyles } from "./global_styles.ts"; import { TocItem } from "../hast/hast_util_toc_mut.ts"; import type { Assets } from "../builder.tsx"; import * as LucideIcons from "./lucide_icons.tsx"; import * as Title from "./atoms/title.tsx"; import * as HastRenderer from "./atoms/hast_renderer.tsx"; import * as JSONCanvasRenderer from "./atoms/json_canvas_renderer.tsx"; import * as DocumentTreeUI from "./organisms/document_tree.tsx"; import * as Footer from "./organisms/footer.tsx"; import * as Toc from "./organisms/toc.tsx"; import * as SiteLayout from "./templates/site_layout.tsx"; export const styles = css.join( globalStyles, Title.styles, LucideIcons.styles, DocumentTreeUI.styles, Footer.styles, SiteLayout.styles, Toc.styles, JSONCanvasRenderer.styles, HastRenderer.styles, ); interface DatetimeTextProps { datetime: Date; } function DatetimeText({ datetime }: DatetimeTextProps) { const z = datetime.toISOString(); // Showing Z time for noscript env, because the timezone of the machine that build // this document and the timezone of viewers can be different. return ( <> <noscript> <time datetime={z}>{z}</time> </noscript> {/* Initially hidden in order to avoid duplication on noscript env */} <time style="display:none;" datetime={z} data-macana-datetime={z} /> </> ); } interface MetadataDatesProps { metadata: DocumentMetadata; } function MetadataDates({ metadata }: MetadataDatesProps) { return (metadata.updatedAt || metadata.createdAt) && ( <> {metadata.createdAt && ( <div> <small> Created at <DatetimeText datetime={metadata.createdAt} /> </small> </div> )} {metadata.updatedAt && ( <div> <small> Updated at <DatetimeText datetime={metadata.updatedAt} /> </small> </div> )} </> ); } interface ViewProps { title: string; document: Document; language: string; assets: Assets; children: JSX.ElementChildrenAttribute["children"]; } function View( { assets, language, document, children, title }: ViewProps, ) { const path = usePathResolver(); return ( <html lang={language}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>{document.metadata.title} - {title}</title> <link rel="stylesheet" href={path.resolve(assets.globalCss)} /> {assets.faviconSvg && ( <link rel="icon" type="image/svg+xml" href={path.resolve(assets.faviconSvg)} /> )} {assets.faviconPng && ( <link rel="icon" type="image/png" href={path.resolve(assets.faviconPng)} /> )} </head> <body> {children} <script> {`document.querySelectorAll("[data-macana-datetime]").forEach(el => { const datetime = new Date(el.dataset.macanaDatetime); el.textContent = datetime.toLocaleString(); el.style.display = ""; });`} </script> </body> </html> ); } export interface ObsidianMarkdownViewProps extends Omit<ViewProps, "children"> { tree: DocumentTree; copyright: string; // nano-jsx does not ship any usable type definition content: unknown; // nano-jsx does not ship any usable type definition toc: readonly TocItem<unknown>[]; } export function ObsidianMarkdownView( { copyright, tree, content, toc, ...props }: ObsidianMarkdownViewProps, ) { const { document, assets } = props; return ( <View {...props}> <SiteLayout.View aside={toc.length > 0 && <Toc.View toc={toc} />} nav={ <DocumentTreeUI.View tree={tree} currentPath={document.path} /> } footer={<Footer.View copyright={copyright} />} logoImage={assets.siteLogo} defaultDocument={tree.defaultDocument} > <Title.View>{document.metadata.title}</Title.View> <MetadataDates metadata={document.metadata} /> {content} </SiteLayout.View> </View> ); } export interface JSONCanvasViewProps extends Omit<ViewProps, "children"> { tree: DocumentTree; copyright: string; // nano-jsx does not ship usable type definition content: JSONCanvas<unknown>; } export function JSONCanvasView( { tree, copyright, content, ...props }: JSONCanvasViewProps, ) { const { document, assets } = props; return ( <View {...props}> <SiteLayout.View nav={ <DocumentTreeUI.View tree={tree} currentPath={document.path} /> } footer={<Footer.View copyright={copyright} />} logoImage={assets.siteLogo} defaultDocument={tree.defaultDocument} > <Title.View>{document.metadata.title}</Title.View> <MetadataDates metadata={document.metadata} /> <JSONCanvasRenderer.View data={content} /> </SiteLayout.View> </View> ); } interface BaseEmbedProps { document: Document; language: string; assets: Assets; children: JSX.ElementChildrenAttribute["children"]; } function BaseEmbed({ document, language, assets, children }: BaseEmbedProps) { const path = usePathResolver(); return ( <html lang={language}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="robot" content="noindex" /> <title>{document.metadata.title}</title> <link rel="stylesheet" href={path.resolve(assets.globalCss)} /> </head> <body> {children} </body> </html> ); } export interface ObsidianMarkdownEmbedProps extends Omit<BaseEmbedProps, "children"> { content: Hast.Nodes; } export function ObsidianMarkdownEmbed( { content, ...rest }: ObsidianMarkdownEmbedProps, ) { return ( <BaseEmbed {...rest}> <HastRenderer.View node={content} /> </BaseEmbed> ); } export interface JSONCanvasEmbedProps extends Omit<BaseEmbedProps, "children"> { // nano-jsx does not ship usable TypeScript definition. content: JSONCanvas<unknown>; } export function JSONCanvasEmbed({ content, ...rest }: JSONCanvasEmbedProps) { return ( <BaseEmbed {...rest}> <JSONCanvasRenderer.View data={content} /> </BaseEmbed> ); }
-
-
page_builder/default_theme/components/lucide_icons.tsx > page_builder/default_theme/icons/lucide.tsx
-
@@ -5,9 +5,9 @@ //// This file includes source code of [Lucide](https://lucide.dev/). // See NOTICE file at the project root for its own license. /** @jsx h */ /** @jsx s */ import { h } from "../../../deps/deno.land/x/nano_jsx/mod.ts"; import { s } from "../../../deps/esm.sh/hastscript/mod.ts"; import { css } from "../css.ts";
-
@@ -21,7 +21,7 @@ const enum C {Icon = "li--i", } export const styles = css` export const lucideIconStyles = css` .${C.Icon} { height: 1em; width: auto;
-
@@ -33,9 +33,12 @@ export interface LucideIconProps {id?: string; className?: string; role?: string; [key: `aria-${string}`]: unknown; [key: `data-${string}`]: unknown; } export function ClipboardList({ className, ...rest }: LucideIconProps) { export function clipboardList({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -58,7 +61,7 @@ </svg>); } export function Pencil({ className, ...rest }: LucideIconProps) { export function pencil({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -77,7 +80,7 @@ </svg>); } export function Info({ className, ...rest }: LucideIconProps) { export function info({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -97,7 +100,7 @@ </svg>); } export function CircleCheck({ className, ...rest }: LucideIconProps) { export function circleCheck({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -116,7 +119,7 @@ </svg>); } export function Flame({ className, ...rest }: LucideIconProps) { export function flame({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -134,7 +137,7 @@ </svg>); } export function Check({ className, ...rest }: LucideIconProps) { export function check({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -152,7 +155,7 @@ </svg>); } export function CircleHelp({ className, ...rest }: LucideIconProps) { export function circleHelp({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -172,7 +175,7 @@ </svg>); } export function TriangleAlert({ className, ...rest }: LucideIconProps) { export function triangleAlert({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -192,7 +195,7 @@ </svg>); } export function X({ className, ...rest }: LucideIconProps) { export function x({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -211,7 +214,7 @@ </svg>); } export function Zap({ className, ...rest }: LucideIconProps) { export function zap({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -229,7 +232,7 @@ </svg>); } export function Bug({ className, ...rest }: LucideIconProps) { export function bug({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -257,7 +260,7 @@ </svg>); } export function List({ className, ...rest }: LucideIconProps) { export function list({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -280,7 +283,7 @@ </svg>); } export function Quote({ className, ...rest }: LucideIconProps) { export function quote({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
@@ -299,7 +302,7 @@ </svg>); } export function ChevronDown({ className, ...rest }: LucideIconProps) { export function chevronDown({ className, ...rest }: LucideIconProps) { return ( <svg xmlns="http://www.w3.org/2000/svg"
-
-
-
@@ -1,140 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import type { Document, DocumentDirectory, DocumentTree, } from "../../../../types.ts"; import { css } from "../../css.ts"; import { usePathResolver } from "../../contexts/path_resolver.tsx"; import { ChevronDown } from "../lucide_icons.tsx"; const enum C { Root = "o-dt--root", List = "o-dt--list", DirectoryHeader = "o-dt--dirh", Directory = "o-dt--dir", Chevron = "o-dt--ch", Link = "o-dt--ln", } export const styles = css` .${C.Root} { padding: calc(var(--baseline) * 0.25rem) 0.75em; font-size: 0.85rem; } .${C.Root}, .${C.List} { margin: 0; list-style: none; } .${C.List} { padding: 0; padding-inline-start: calc(1em + 4px); border-inline-start: 2px solid var(--color-subtle-overlay); } .${C.DirectoryHeader} { display: flex; justify-content: flex-start; align-items: center; gap: 4px; cursor: pointer; } .${C.DirectoryHeader}::marker, .${C.DirectoryHeader}::-webkit-details-marker { display: none; } .${C.Link} { color: var(--color-fg-sub); text-decoration: none; } .${C.Link}:hover { text-decoration: underline; } .${C.Directory} { display: flex; } .${C.Chevron} { color: var(--color-fg-light); transition: transform 0.1s ease; } .${C.Directory}:not([open]) > .${C.DirectoryHeader} > .${C.Chevron} { transform: rotate(-90deg); } `; export interface ViewProps { tree: DocumentTree; currentPath: readonly string[]; } export function View({ tree, currentPath }: ViewProps) { return ( <ul className={C.Root} lang={tree.defaultLanguage}> {tree.nodes.map((entry) => ( <Node value={entry} currentPath={currentPath} /> ))} </ul> ); } interface NodeProps { value: Document | DocumentDirectory; currentPath: readonly string[]; } function Node({ value, currentPath }: NodeProps) { const pathResolver = usePathResolver(); if ("file" in value) { const path = pathResolver.resolve([ ...value.path.map((segment) => encodeURIComponent(segment)), // For trailing slash "", ]); return ( <li lang={value.metadata.language ?? undefined}> <a className={C.Link} href={path}>{value.metadata.title}</a> </li> ); } const defaultOpened = currentPath[0] === value.directory.name; return ( <li lang={value.metadata.language ?? undefined}> <details className={C.Directory} open={defaultOpened ? "" : undefined}> <summary className={C.DirectoryHeader}> <ChevronDown className={C.Chevron} /> <span>{value.metadata.title}</span> </summary> <ul className={C.List}> {value.entries.map((entry) => ( <Node value={entry} currentPath={currentPath.slice(1)} /> ))} </ul> </details> </li> ); }
-
-
-
@@ -1,55 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import { css } from "../../css.ts"; const enum C { Root = "o-fo--root", Copyright = "o-fo--cpy", Links = "o-fo--links", } export const styles = css` .${C.Root} { display: flex; justify-content: space-between; align-items: center; gap: 2em; } .${C.Copyright} { font-size: 0.8em; } .${C.Links} { font-size: 0.9em; display: flex; gap: 0.25em 0.5em; justify-content: start; align-items: start; } `; export interface ViewProps { copyright: JSX.ElementChildrenAttribute["children"]; children?: JSX.ElementChildrenAttribute["children"]; } export function View({ copyright, children }: ViewProps) { return ( <div className={C.Root}> <small className={C.Copyright}>{copyright}</small> {children && ( <div className={C.Links}> {children} </div> )} </div> ); }
-
-
-
@@ -1,71 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import { type TocItem } from "../../hast/hast_util_toc_mut.ts"; import { css } from "../../css.ts"; const enum C { Root = "o-toc--root", List = "o-toc--l", Item = "o-toc--i", Link = "o-toc--k", } export const styles = css` .${C.Root} { font-size: 0.8rem; } .${C.List} { display: flex; flex-direction: column; padding-left: 0.75em; border-left: 2px solid var(--color-subtle-overlay); } .${C.Item} { display: flex; flex-direction: column; } .${C.Link} { text-decoration: none; color: var(--color-fg-sub); } .${C.Link}:hover { text-decoration: underline; } `; export interface ViewProps { // nano-jsx does not ship any useful typings toc: readonly TocItem<unknown>[]; } export function View({ toc }: ViewProps) { return ( <div className={C.Root}> <Items toc={toc} /> </div> ); } export function Items({ toc }: ViewProps) { return ( <ul className={C.List}> {toc.map((item) => ( <li className={C.Item}> <a className={C.Link} href={`#${item.id}`}>{item.text}</a> {item.children.length > 0 && <Items toc={item.children} />} </li> ))} </ul> ); }
-
-
page_builder/default_theme/components/templates/site_layout.tsx > page_builder/default_theme/widgets/layout.tsx
-
@@ -4,30 +4,28 @@ // SPDX-License-Identifier: Apache-2.0/** @jsx h */ import { h } from "../../../../deps/deno.land/x/nano_jsx/mod.ts"; import { h, type Result } from "../../../deps/esm.sh/hastscript/mod.ts"; import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import { type Document } from "../../../../types.ts"; import { usePathResolver } from "../../contexts/path_resolver.tsx"; import { css } from "../../css.ts"; import type { BuildContext } from "../context.ts"; import { css } from "../css.ts"; const enum C { Layout = "t-sl--root", HeaderBg = "t-sl--headbg", Header = "t-sl--head", Logo = "t-sl--lg", LogoLink = "t-sl--ll", Nav = "t-sl--nav", NavInner = "t-sl--nav-i", FooterBg = "t-sl--fbg", Footer = "t-sl--foot", Main = "t-sl--main", Aside = "t-sl--aside", AsideInner = "t-sl--aside-i", Layout = "w-l--root", HeaderBg = "w-l--headbg", Header = "w-l--head", Logo = "w-l--lg", LogoLink = "w-l--ll", Nav = "w-l--nav", NavInner = "w-l--nav-i", FooterBg = "w-l--fbg", Footer = "w-l--foot", Main = "w-l--main", Aside = "w-l--aside", AsideInner = "w-l--aside-i", } export const styles = css` export const layoutStyles = css` .${C.Layout} { --_ends-shadow-color: hsl(0deg 0% 0% / 0.03);
-
@@ -157,59 +155,61 @@ }} `; export interface ViewProps { children: JSX.ElementChildrenAttribute["children"]; export interface LayoutProps { /** * Site navigation content. */ nav: Result; nav: JSX.ElementChildrenAttribute["children"]; aside?: Result; aside?: JSX.ElementChildrenAttribute["children"]; footer: JSX.ElementChildrenAttribute["children"]; logoImage?: readonly string[]; logoSize?: number; /** * Content shown inside `<main>`. */ main: Result; defaultDocument: Document; footer: Result; } export function View( { children, nav, aside, footer, logoImage, logoSize = 32, defaultDocument }: ViewProps, ) { const path = usePathResolver(); export function layout({ nav, aside, main, footer, }: LayoutProps, ctx: BuildContext) { const { assets, resolvePath, documentTree: { defaultDocument } } = ctx; return ( <div className={C.Layout}> <div className={C.HeaderBg} /> {logoImage && ( <header className={C.Header}> <div class={C.Layout}> <div class={C.HeaderBg} /> {assets.siteLogo && ( <header class={C.Header}> <a className={C.LogoLink} href={path.resolve([...defaultDocument.path, ""])} class={C.LogoLink} href={resolvePath([...defaultDocument.path, ""]).join("/")} title={defaultDocument.metadata.title} lang={defaultDocument.metadata.language} > <img className={C.Logo} src={path.resolve(logoImage)} width={logoSize} height={logoSize} class={C.Logo} src={resolvePath(assets.siteLogo).join("/")} width={32} height={32} /> </a> </header> )} <nav className={C.Nav}> <div className={C.NavInner}>{nav}</div> <nav class={C.Nav}> <div class={C.NavInner}>{nav}</div> </nav> <main className={C.Main}>{children}</main> <main class={C.Main}>{main}</main> {aside && ( <aside className={C.Aside}> <div className={C.AsideInner}>{aside}</div> <aside class={C.Aside}> <div class={C.AsideInner}>{aside}</div> </aside> )} <div className={C.FooterBg} /> <footer className={C.Footer}>{footer}</footer> <div class={C.FooterBg} /> <footer class={C.Footer}>{footer}</footer> </div> ); }
-
-
-
@@ -0,0 +1,42 @@import type { Document, DocumentTree } from "../../types.ts"; export interface Assets { globalCss: readonly string[]; faviconSvg?: readonly string[]; faviconPng?: readonly string[]; siteLogo?: readonly string[]; } export interface BuildContext { /** * The document currently building. */ document: Document; documentTree: DocumentTree; /** * Current document's language. * * You should use this property instead of `document.metadata.language`. * The document metadata only contains metadata defined by the document itself. * If the document does not declare language and inherits from ancestor, * the value of `document.metadata.language` would be `undefined`. */ language: string; /** * Site-wide asset paths. */ assets: Readonly<Assets>; websiteTitle: string; copyright: string; /** * Resolves the given path as an absolute path from the document root to * relative path from the current document. */ resolvePath(to: readonly string[]): readonly string[]; }
-
-
-
@@ -1,55 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { createContext, h, useContext, } from "../../../deps/deno.land/x/nano_jsx/mod.ts"; interface PathResolver { resolve(path: readonly string[]): string; } const PathResolverContext = createContext({ resolve(path: readonly string[]) { return path.join("/"); }, }); export function usePathResolver(): PathResolver { return useContext(PathResolverContext); } export interface PathResolverProviderProps { /** * How deep the path from the root? * 0 for top-level documents. * For example, the depth for "foo/bar/baz.html" is 2. * * @default 0 */ depth?: number; children: JSX.ElementChildrenAttribute["children"]; } export function PathResolverProvider( { depth = 0, children }: PathResolverProviderProps, ) { const resolver: PathResolver = { resolve(path) { return Array.from({ length: depth }, () => "../").join("") + path.join("/"); }, }; return ( <PathResolverContext.Provider value={resolver}> {children} </PathResolverContext.Provider> ); }
-
-
-
@@ -0,0 +1,284 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import { type Handlers, type State, } from "../../../deps/esm.sh/mdast-util-to-hast/mod.ts"; import { type CalloutType, type OfmCallout, parseOfmCalloutNode, } from "../../../content_parser/obsidian_markdown/mdast_util_ofm_callout.ts"; import { css, join } from "../css.ts"; import * as icons from "../icons/lucide.tsx"; const enum C { Root = "fm-co--root", Title = "fm-co--title", TitleText = "fm-co--tt", Body = "fm-co--b", Icon = "fm-co--i", Bg = "fm-co--g", Chevron = "fm-co--c", } export const calloutStyles = join( icons.lucideIconStyles, css` .${C.Root} { --_macana-callout-overlay: hsl(0deg 0% 0% / 0.03); --_macana-callout-color: var(--callout-color-info, var(--obsidian-color-fallback)); position: relative; margin: 0; margin-top: calc(var(--baseline) * 1rem); padding: 0; line-height: calc(var(--baseline) * 1rem); max-width: 100%; font-size: 1rem; border: 1px solid var(--_macana-callout-color); border-radius: 4px; } .${C.Root}[data-type="todo"] { --_macana-callout-color: var(--callout-color-todo); } .${C.Root}[data-type="tip"] { --_macana-callout-color: var(--callout-color-tip); } .${C.Root}[data-type="success"] { --_macana-callout-color: var(--callout-color-success); } .${C.Root}[data-type="question"] { --_macana-callout-color: var(--callout-color-question); } .${C.Root}[data-type="warning"] { --_macana-callout-color: var(--callout-color-warning); } .${C.Root}[data-type="failure"] { --_macana-callout-color: var(--callout-color-failure); } .${C.Root}[data-type="danger"] { --_macana-callout-color: var(--callout-color-danger); } .${C.Root}[data-type="bug"] { --_macana-callout-color: var(--callout-color-bug); } .${C.Root}[data-type="example"] { --_macana-callout-color: var(--callout-color-example); } .${C.Root}[data-type="quote"] { --_macana-callout-color: var(--callout-color-quote); } .${C.Bg} { position: absolute; inset: 0; background-color: var(--_macana-callout-color); pointer-events: none; opacity: 0.02; } @media (prefers-color-scheme: dark) { .${C.Root} { --_macana-callout-overlay: hsl(0deg 0% 100% / 0.1); } .${C.Bg} { opacity: 0.05; } } .${C.Title} { font-size: 1.1rem; display: flex; justify-content: flex-start; align-items: center; gap: 0.25em; padding: calc(var(--baseline) * 0.5rem) 8px; border-bottom: 1px solid var(--_macana-callout-overlay); margin-top: 0; font-weight: 700; } summary.${C.Title} { cursor: pointer; } summary.${C.Title}:hover { background-color: var(--_macana-callout-overlay); } details:not([open]) > summary.${C.Title} { border-bottom-color: transparent; } .${C.Icon} { color: var(--_macana-callout-color); } .${C.TitleText} { line-height: calc(var(--baseline) * 1rem); } .${C.Chevron} { transition: transform 0.15s ease-out; } details:not([open]) .${C.Chevron} { transform: rotate(-90deg); } .${C.Body} { font-size: 1rem; padding: calc(var(--baseline) * 0.5rem) 12px; } .${C.Body} > :first-child { margin-block-start: 0; } `, ); let counter = 0; export function calloutHandlers(): Handlers { return { // @ts-expect-error: unist-related libraries heavily relies on ambient module declarations, // which Deno does not support. APIs also don't accept type parameters. ofmCallout(state: State, node: OfmCallout): Hast.Nodes { const { title, body, type } = parseOfmCalloutNode(state, node); const titleId = "__macana-callout__" + (counter++).toString(16); const containerProps = { class: C.Root, "aria-labelledby": titleId, "data-type": type, }; if (!node.isFoldable) { return ( <aside {...containerProps} > <div class={C.Bg} /> <p class={C.Title} id={titleId}> {icon(type, C.Icon)} <span class={C.TitleText}>{title}</span> </p> <div class={C.Body}>{body}</div> </aside> ); } return ( <aside {...containerProps}> <div class={C.Bg} /> <details open={node.defaultExpanded ? "" : undefined}> <summary class={C.Title} id={titleId}> {icon(type, C.Icon)} <span class={C.TitleText}>{title}</span> {icons.chevronDown({ className: C.Chevron, "aria-hidden": "true", })} </summary> <div class={C.Body}>{body}</div> </details> </aside> ); }, }; } function icon(type: CalloutType, className?: string) { switch (type) { case "abstract": return icons.clipboardList({ className, role: "img", "aria-label": "Clipboard icon", }); case "info": return icons.info({ className, role: "img", "aria-label": "Info icon", }); case "todo": return icons.circleCheck({ className, role: "img", "aria-label": "Check icon", }); case "tip": return icons.flame({ className, role: "img", "aria-label": "Flame icon", }); case "success": return icons.check({ className, role: "img", "aria-label": "Check icon", }); case "question": return icons.circleHelp({ className, role: "img", "aria-label": "Question icon", }); case "warning": return icons.triangleAlert({ className, role: "img", "aria-label": "Warning icon", }); case "failure": return icons.x({ className, role: "img", "aria-label": "Cross icon", }); case "danger": return icons.zap({ className, role: "img", "aria-label": "Lightning icon", }); case "bug": return icons.bug({ className, role: "img", "aria-label": "Bug icon", }); case "example": return icons.list({ className, role: "img", "aria-label": "List icon", }); case "quote": return icons.quote({ className, role: "img", "aria-label": "Quote icon", }); case "note": default: return icons.pencil({ className, role: "img", "aria-label": "Pencil icon", }); } }
-
-
-
@@ -0,0 +1,95 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import type * as Mdast from "../../../deps/esm.sh/mdast/types.ts"; import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import { type Handlers } from "../../../deps/esm.sh/mdast-util-to-hast/mod.ts"; import { css, join } from "../css.ts"; import * as icons from "../icons/lucide.tsx"; const enum C { TaskItem = "fm-l--i", Checkbox = "fm-l--c", Check = "fm-l--h", Text = "fm-l--t", } export const listStyles = join( icons.lucideIconStyles, css` .${C.TaskItem} { display: flex; align-items: start; list-style: none; } .${C.Checkbox} { display: flex; align-items: stretch; justify-content: stretch; width: calc(1em - 2px); height: calc(1em - 2px); aspect-ratio: 1 / 1; border: 1px solid currentColor; margin: 0; margin-block-start: calc((var(--baseline) * 1rem - 1em) * 0.5 + 1px); margin-inline-start: calc(-1em - 2px); margin-inline-end: 4px; border-radius: 2px; } .${C.Check} { min-width: 0px; min-height: 0px; height: 100%; } .${C.Text} { line-height: calc(var(--baseline) * 1rem); } `, ); let counter = 0; export function listHandlers(): Handlers { return { list(state, node: Mdast.List) { return h(node.ordered ? "ol" : "ul", {}, state.all(node)); }, listItem(state, node: Mdast.ListItem) { const children = state.all(node).map((child) => child.type === "element" && child.tagName === "p" ? child.children : [child] ).flat(); if (typeof node.checked !== "boolean") { return h("li", {}, children); } const labelId = "__macana_tcheck_lbl__" + (counter++).toString(16); return h("li", { class: C.TaskItem }, [ <span class={C.Checkbox} role="checkbox" aria-disabled="true" tabindex="0" aria-checked={node.checked.toString()} aria-labelledby={labelId} > {node.checked ? icons.check({ className: C.Check, "aria-hidden": "true" }) : null} </span>, <span id={labelId} className={C.Text}>{children}</span>, ]); }, }; }
-
-
-
@@ -0,0 +1,205 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import type * as Mdast from "../../../deps/esm.sh/mdast/types.ts"; import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import { toHast } from "../../../deps/esm.sh/mdast-util-to-hast/mod.ts"; import { ofmHtml, ofmToHastHandlers, } from "../../../content_parser/obsidian_markdown.ts"; import { syntaxHighlightingHandlers } from "../mdast/syntax_highlighting_handlers.ts"; import { css, join as joinCss } from "../css.ts"; import { calloutHandlers, calloutStyles } from "./callout.tsx"; import { listHandlers, listStyles } from "./list.tsx"; const enum C { Wrapper = "fm--m", } const ownStyles = css` :where(.${C.Wrapper}) a { color: var(--color-fg-sub); font-weight: 500; text-decoration: underline; transition: color 0.15s ease; } :where(.${C.Wrapper}) a:hover { color: var(--color-primary); } :where(.${C.Wrapper}) p { margin: 0; margin-top: calc(var(--baseline) * 1rem); } :where(.${C.Wrapper}) pre { margin: 0; margin-top: calc(var(--baseline) * 1rem) !important; padding: calc(var(--baseline) * 1rem) 1em !important; line-height: calc(var(--baseline) * 1rem); max-width: 100%; font-size: 1rem; background-color: var(--color-fg); color: var(--color-bg); border-radius: calc(1rem / 4); overflow-x: auto; } :where(.${C.Wrapper}) pre > code { all: unset; } :where(.${C.Wrapper}) code { margin: 0 0.2em; padding: calc(1rem / 4); background-color: var(--color-bg-accent); color: var(--color-fg-sub); border-radius: calc(1rem / 4); font-family: "Ubuntu Mono", monospace; } :where(.${C.Wrapper}) pre > code .token.comment { font-style: italic; } :where(.${C.Wrapper}) a, :where(.${C.Wrapper}) time, :where(.${C.Wrapper}) span, :where(.${C.Wrapper}) code, :where(.${C.Wrapper}) sup, :where(.${C.Wrapper}) small, :where(.${C.Wrapper}) s, :where(.${C.Wrapper}) b, :where(.${C.Wrapper}) i { line-height: 1; } :where(.${C.Wrapper}) button { font-family: inherit; } :where(.${C.Wrapper}) s, :where(.${C.Wrapper}) del { color: var(--color-fg-sub); text-decoration: line-through; } :where(.${C.Wrapper}) b { font-weight: bold; } :where(.${C.Wrapper}) i { font-style: italic; } :where(.${C.Wrapper}) ul { margin: 0; margin-top: calc(var(--baseline) * 1rem); padding-left: 1.5em; } :where(.${C.Wrapper}) ul ul { margin-top: 0; } :where(.${C.Wrapper}) h1, :where(.${C.Wrapper}) h2, :where(.${C.Wrapper}) h3 { font-weight: 700; color: var(--color-fg-sub); } :where(.${C.Wrapper}) h1 { margin: 0; margin-top: calc(var(--baseline) * 2rem); line-height: calc(var(--baseline) * 2rem); } :where(.${C.Wrapper}) h2 { margin: 0; margin-top: calc(var(--baseline) * 2rem); } :where(.${C.Wrapper}) h3, :where(.${C.Wrapper}) h4, :where(.${C.Wrapper}) h5, :where(.${C.Wrapper}) h6 { margin: 0; margin-top: calc(var(--baseline) * 1rem); font-weight: 600; } :where(.${C.Wrapper}) table { border-spacing: 0; margin: 0; margin-top: calc(var(--baseline) * 0.5rem); width: 100%; } :where(.${C.Wrapper}) thead { background-color: var(--color-bg-accent); } :where(.${C.Wrapper}) th { font-weight: 500; padding: calc(var(--baseline) * 0.25rem) 1em; } :where(.${C.Wrapper}) td { padding: calc(var(--baseline) * 0.5rem) 1em; } :where(.${C.Wrapper}) tbody td { position: relative; } :where(.${C.Wrapper}) tbody td::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; border-bottom: 1px solid var(--color-fg-light); } :where(.${C.Wrapper}) hr { margin: 0; margin-top: calc(var(--baseline) * 1rem); } :where(.${C.Wrapper}) img { max-width: 100%; } :where(.${C.Wrapper}) img:not(:first-child) { margin-top: calc(var(--baseline) * 1rem); } `; export const fromMdastStyles = joinCss(ownStyles, calloutStyles, listStyles); export function fromMdast(mdast: Mdast.Nodes): Hast.Nodes { return ofmHtml(toHast(mdast, { handlers: { ...ofmToHastHandlers(), ...calloutHandlers(), ...listHandlers(), ...syntaxHighlightingHandlers(), }, allowDangerousHtml: true, })); } export function style(node: Hast.Nodes): Hast.Nodes { return h("div", { class: C.Wrapper }, [node]); }
-
-
-
@@ -8,6 +8,7 @@ import type * as Mdast from "../../../deps/esm.sh/mdast/types.ts";import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import { defaultHandlers, type Handlers, type State, } from "../../../deps/esm.sh/mdast-util-to-hast/mod.ts";
-
@@ -53,9 +54,9 @@ export function syntaxHighlightingHandlers({className = "macana--highlight", nodeTypeAttribute = "data-hl-node", langNameAttribute = "data-hl-lang", }: SyntaxHighlightingOptions = {}) { }: SyntaxHighlightingOptions = {}): Handlers { return { code(state: State, node: Mdast.Code): Hast.Nodes { code(state: State, node: Mdast.Code) { if (!node.lang || !refractor.registered(node.lang)) { return defaultHandlers.code(state, node); }
-
@@ -89,8 +90,7 @@ {type: "element", tagName: "code", properties: {}, // @ts-expect-error: refractor relys on @types/hast@2.x, which is not compatible the latest v3 children: code.children, children: code.children as Hast.ElementContent[], }, ], };
-
-
-
@@ -0,0 +1,27 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; export interface IndexRedirectProps { redirectTo: string; } export function indexRedirect({ redirectTo }: IndexRedirectProps) { return h(null, [ { type: "doctype" }, <html> <head> <meta charset="utf-8" /> <meta http-equiv="refresh" content={`0; URL='${redirectTo}'`} /> </head> <body> {/* For cases when a user or UA disallows automatic redirection. */} <a href={redirectTo}>TOP</a> </body> </html>, ]); }
-
-
-
@@ -0,0 +1,69 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import type { JSONCanvas } from "../../../content_parser/json_canvas/types.ts"; import type { BuildContext } from "../context.ts"; import * as css from "../css.ts"; import { layout, layoutStyles } from "../widgets/layout.tsx"; import { documentTree, documentTreeStyles } from "../widgets/document_tree.tsx"; import { footer, footerStyles } from "../widgets/footer.tsx"; import { title, titleStyles } from "../widgets/title.tsx"; import { pageMetadata, pageMetadataScript } from "../widgets/page_metadata.tsx"; import { jsonCanvas, jsonCanvasStyles, wrappedJsonCanvas, } from "../json_canvas/mod.tsx"; import { embedTemplate, template } from "./template.tsx"; export const jsonCanvasPageStyles = css.join( layoutStyles, documentTreeStyles, footerStyles, titleStyles, jsonCanvasStyles, ); export interface JsonCanvasPageProps { context: Readonly<BuildContext>; content: JSONCanvas<Hast.Nodes>; } export function jsonCanvasPage({ content, context }: JsonCanvasPageProps) { return h(null, [ { type: "doctype" }, template({ context, scripts: [pageMetadataScript], body: layout({ nav: documentTree({ context }), footer: footer({ copyright: context.copyright }), main: h(null, [ title({ children: context.document.metadata.title }), pageMetadata({ metadata: context.document.metadata }), wrappedJsonCanvas({ data: content }), ]), }, context), }), ]); } export function jsonCanvasEmbed({ context, content }: JsonCanvasPageProps) { return h(null, [ { type: "doctype" }, embedTemplate({ context, body: jsonCanvas({ data: content }), }), ]); }
-
-
-
@@ -0,0 +1,75 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import type { BuildContext } from "../context.ts"; import * as css from "../css.ts"; import type { TocItem } from "../hast/hast_util_toc_mut.ts"; import { layout, layoutStyles } from "../widgets/layout.tsx"; import { toc, tocStyles } from "../widgets/toc.tsx"; import { documentTree, documentTreeStyles } from "../widgets/document_tree.tsx"; import { footer, footerStyles } from "../widgets/footer.tsx"; import { title, titleStyles } from "../widgets/title.tsx"; import { pageMetadata, pageMetadataScript } from "../widgets/page_metadata.tsx"; import { embedTemplate, template } from "./template.tsx"; export const markdownPageStyles = css.join( layoutStyles, tocStyles, documentTreeStyles, footerStyles, titleStyles, ); export interface MarkdownPageProps { context: Readonly<BuildContext>; content: Hast.Nodes; tocItems: readonly TocItem[]; } export function markdownPage( { context, content, tocItems }: MarkdownPageProps, ) { return h(null, [ { type: "doctype" }, template({ context, scripts: [pageMetadataScript], body: layout({ aside: tocItems.length > 0 ? toc({ toc: tocItems }) : undefined, nav: documentTree({ context }), footer: footer({ copyright: context.copyright }), main: h(null, [ title({ children: context.document.metadata.title }), pageMetadata({ metadata: context.document.metadata }), content, ]), }, context), }), ]); } export interface MarkdownEmbedProps { context: Readonly<BuildContext>; content: Hast.Nodes; } export function markdownEmbed({ context, content }: MarkdownEmbedProps) { return h(null, [ { type: "doctype" }, embedTemplate({ context, body: content, }), ]); }
-
-
-
@@ -0,0 +1,80 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { type Child, h } from "../../../deps/esm.sh/hastscript/mod.ts"; import type { BuildContext } from "../context.ts"; export interface TemplateProps { body: Child; context: Readonly<BuildContext>; scripts?: readonly string[]; } export function template({ body, context, scripts = [] }: TemplateProps) { const { language, document, websiteTitle, assets, resolvePath } = context; return ( <html lang={language}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>{document.metadata.title} - {websiteTitle}</title> <link rel="stylesheet" href={resolvePath(assets.globalCss).join("/")} /> {assets.faviconSvg && ( <link rel="icon" type="image/svg+xml" href={resolvePath(assets.faviconSvg).join("/")} /> )} {assets.faviconPng && ( <link rel="icon" type="image/png" href={resolvePath(assets.faviconPng).join("/")} /> )} </head> {h( "body", {}, body, ...scripts.map((script) => <script>{script}</script>), )} </html> ); } export function embedTemplate({ body, context, scripts = [] }: TemplateProps) { const { language, document, assets, resolvePath } = context; return ( <html lang={language}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="robot" content="noindex" /> <title>{document.metadata.title}</title> <link rel="stylesheet" href={resolvePath(assets.globalCss).join("/")} /> </head> {h( "body", {}, body, ...scripts.map((script) => <script>{script}</script>), )} </html> ); }
-
-
-
@@ -0,0 +1,31 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; export const datetimeScript = ` document.querySelectorAll("[data-macana-datetime]").forEach(el => { const datetime = new Date(el.dataset.macanaDatetime); el.textContent = datetime.toLocaleString(); el.style.display = ""; }); `.trim(); export interface DatetimeProps { datetime: Date; } export function datetime({ datetime }: DatetimeProps) { const z = datetime.toISOString(); return h(null, [ <noscript> <time datetime={z}>{z}</time> </noscript>, // Initially hidden in order to avoid duplication on noscript env <time style="display:none;" datetime={z} data-macana-datetime={z} />, ]); }
-
-
-
@@ -0,0 +1,142 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import type { Document, DocumentDirectory } from "../../../types.ts"; import { css, join } from "../css.ts"; import type { BuildContext } from "../context.ts"; import * as icons from "../icons/lucide.tsx"; const enum C { Root = "w-dt--root", List = "w-dt--list", DirectoryHeader = "w-dt--dirh", Directory = "w-dt--dir", Chevron = "w-dt--ch", Link = "w-dt--ln", } export const documentTreeStyles = join( icons.lucideIconStyles, css` .${C.Root} { padding: calc(var(--baseline) * 0.25rem) 0.75em; font-size: 0.85rem; } .${C.Root}, .${C.List} { margin: 0; list-style: none; } .${C.List} { padding: 0; padding-inline-start: calc(1em + 4px); border-inline-start: 2px solid var(--color-subtle-overlay); } .${C.DirectoryHeader} { display: flex; justify-content: flex-start; align-items: center; gap: 4px; cursor: pointer; } .${C.DirectoryHeader}::marker, .${C.DirectoryHeader}::-webkit-details-marker { display: none; } .${C.Link} { color: var(--color-fg-sub); text-decoration: none; } .${C.Link}:hover { text-decoration: underline; } .${C.Directory} { display: flex; } .${C.Chevron} { color: var(--color-fg-light); transition: transform 0.1s ease; } .${C.Directory}:not([open]) > .${C.DirectoryHeader} > .${C.Chevron} { transform: rotate(-90deg); } `, ); export interface DocumentTreeProps { context: Readonly<BuildContext>; } export function documentTree({ context }: DocumentTreeProps) { return ( <ul className={C.Root} lang={context.documentTree.defaultLanguage}> {context.documentTree.nodes.map((entry) => ( node({ value: entry, currentPath: context.document.path, context, }) ))} </ul> ); } interface NodeProps { value: Document | DocumentDirectory; currentPath: readonly string[]; context: Readonly<BuildContext>; } function node({ currentPath, value, context }: NodeProps) { if ("file" in value) { const path = context.resolvePath([ ...value.path.map((segment) => encodeURIComponent(segment)), // For trailing slash "", ]); return ( <li lang={value.metadata.language ?? undefined}> <a className={C.Link} href={path.join("/")}>{value.metadata.title}</a> </li> ); } const defaultOpened = currentPath[0] === value.directory.name; return ( <li lang={value.metadata.language ?? undefined}> <details className={C.Directory} open={defaultOpened ? "" : undefined}> <summary className={C.DirectoryHeader}> {icons.chevronDown({ className: C.Chevron })} <span>{value.metadata.title}</span> </summary> <ul className={C.List}> {value.entries.map((entry) => ( node({ value: entry, currentPath: currentPath.slice(1), context, }) ))} </ul> </details> </li> ); }
-
-
-
@@ -0,0 +1,55 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { Child, h } from "../../../deps/esm.sh/hastscript/mod.ts"; import { css } from "../css.ts"; const enum C { Root = "w-fo--root", Copyright = "w-fo--cpy", Links = "w-fo--links", } export const footerStyles = css` .${C.Root} { display: flex; justify-content: space-between; align-items: center; gap: 2em; } .${C.Copyright} { font-size: 0.8em; } .${C.Links} { font-size: 0.9em; display: flex; gap: 0.25em 0.5em; justify-content: start; align-items: start; } `; export interface FooterProps { copyright: Child; children?: Child; } export function footer({ copyright, children }: FooterProps) { return ( <div class={C.Root}> <small class={C.Copyright}>{copyright}</small> {children && ( <div class={C.Links}> {children} </div> )} </div> ); }
-
-
-
@@ -0,0 +1,44 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import type { DocumentMetadata } from "../../../types.ts"; import { datetime, datetimeScript } from "./datetime.tsx"; export const pageMetadataScript = datetimeScript; export interface PageMetadataProps { metadata: DocumentMetadata; } export function pageMetadata({ metadata }: PageMetadataProps) { if (!metadata.createdAt && !metadata.updatedAt) { return null; } return h(null, [ metadata.createdAt ? ( <div> <small> Created at {datetime({ datetime: metadata.createdAt })} </small> </div> ) : null, metadata.updatedAt ? ( <div> <small> Updated at {datetime({ datetime: metadata.updatedAt })} </small> </div> ) : null, ]); }
-
-
-
@@ -0,0 +1,38 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { type Child, h } from "../../../deps/esm.sh/hastscript/mod.ts"; import { css } from "../css.ts"; const enum C { Title = "w--title", } export const titleStyles = css` .${C.Title} { font-weight: 700; font-size: 2rem; margin: 0; line-height: calc(var(--baseline) * 2rem); color: var(--color-fg-sub); } `; export interface TitleProps { className?: string; children: Child; } export function title({ className, children }: TitleProps) { return ( <h1 class={[className, C.Title].filter((s) => !!s).join(" ")}> {children} </h1> ); }
-
-
-
@@ -0,0 +1,69 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import type { TocItem } from "../hast/hast_util_toc_mut.ts"; import { css } from "../css.ts"; const enum C { Root = "w-toc--root", List = "w-toc--l", Item = "w-toc--i", Link = "w-toc--k", } export const tocStyles = css` .${C.Root} { font-size: 0.8rem; } .${C.List} { display: flex; flex-direction: column; padding-left: 0.75em; border-left: 2px solid var(--color-subtle-overlay); } .${C.Item} { display: flex; flex-direction: column; } .${C.Link} { text-decoration: none; color: var(--color-fg-sub); } .${C.Link}:hover { text-decoration: underline; } `; export interface TocProps { toc: readonly TocItem[]; } export function toc({ toc }: TocProps) { return ( <div class={C.Root}> {items({ toc })} </div> ); } export function items({ toc }: TocProps) { return ( <ul className={C.List}> {toc.map((item) => ( <li class={C.Item}> <a class={C.Link} href={`#${item.id}`}>{item.text}</a> {item.children.length > 0 ? items({ toc: item.children }) : null} </li> ))} </ul> ); }
-