Changes
9 changed files (+211/-18)
-
-
@@ -46,6 +46,23 @@return value; } function getFrontMatterDate( frontmatter: Record<string, unknown>, key: string, ): Date | undefined { const str = getFrontMatterValue(frontmatter, key); if (typeof str !== "string") { return str; } const timestamp = Date.parse(str); if (isNaN(timestamp)) { return undefined; } return new Date(timestamp); } export type ObsidianMarkdownDocument = DocumentContent< "obsidian_markdown", Mdast.Nodes
-
@@ -66,6 +83,10 @@ ** ## `lang` / `language` * * Use property value as a document language. * * ## `createdAt` / `updatedAt` * * Use property values as corresponding metadata dates. * * @default false */
-
@@ -128,6 +149,8 @@ const name = getFrontMatterValue(frontmatter.attrs, "name");const title = getFrontMatterValue(frontmatter.attrs, "title"); const lang = getFrontMatterValue(frontmatter.attrs, "lang") || getFrontMatterValue(frontmatter.attrs, "language"); const createdAt = getFrontMatterDate(frontmatter.attrs, "createdAt"); const updatedAt = getFrontMatterDate(frontmatter.attrs, "updatedAt"); return { documentMetadata: {
-
@@ -135,6 +158,8 @@ ...documentMetadata,name: name || documentMetadata.name, title: title || documentMetadata.title, language: lang || documentMetadata.language, createdAt: createdAt || documentMetadata.createdAt, updatedAt: updatedAt || documentMetadata.updatedAt, }, documentContent: { kind: "obsidian_markdown",
-
-
-
@@ -13,6 +13,7 @@ fileExtensions,ignoreDotfiles, langDir, removeExtFromMetadata, useFileSystemTimestamps, } from "../tree_builder/default_tree_builder.ts"; import { ObsidianMarkdownParser } from "../content_parser/obsidian_markdown.ts"; import { JSONCanvasParser } from "../content_parser/json_canvas.ts";
-
@@ -44,6 +45,7 @@ ignore: [ignoreDotfiles],strategies: [ fileExtensions([".md", ".canvas"]), removeExtFromMetadata(), useFileSystemTimestamps(), langDir({ en: "English", ja: "日本語",
-
-
-
@@ -1,3 +1,8 @@--- createdAt: 2024-04-07T18:00:00+09:00 updatedAt: 2024-04-19T20:00:00+09:00 --- ## Document A parsed note or canvas in a Vault.
-
@@ -6,7 +11,14 @@## Document metadata Properties describing a *document* or a *document directory* required for generating a website. *Document metadata* consists of a *document name*, *document title*, whether the document is default document, and language of the document or the document directory. *Document metadata* contains those properties: - *Document name* - *Document title* - Whether the document is the default document of Vault - Language (optional) - Creation date-time (optional) - Update date-time (optional) ## Document name
-
-
-
@@ -54,7 +54,7 @@ - [x] Colors- [ ] Label - [x] Document sorting - [ ] Search functionality - [ ] Creation / Update date - [x] Creation / Update date - [ ] Tags - [x] Config for site logo - [x] Tool's logo
-
-
-
@@ -71,6 +71,14 @@ parent,read: () => { return Deno.readFile(this.#resolve(path)); }, stat: async () => { const stat = await Deno.stat(this.#resolve(path)); return { contentUpdatedAt: stat.mtime ?? undefined, createdAt: stat.birthtime ?? undefined, }; }, }; }
-
@@ -90,6 +98,14 @@ }} return converted; }, stat: async () => { const stat = await Deno.stat(this.#resolve(path)); return { contentUpdatedAt: stat.mtime ?? undefined, createdAt: stat.birthtime ?? undefined, }; }, };
-
@@ -137,6 +153,14 @@ parent,read: async () => { return Deno.readFile(resolvedPath); }, stat: async () => { const stat = await Deno.stat(this.#resolve(path)); return { contentUpdatedAt: stat.mtime ?? undefined, createdAt: stat.birthtime ?? undefined, }; }, }; }, openDirectory: (path) => {
-
@@ -172,6 +196,14 @@ }return converted; }, stat: async () => { const stat = await Deno.stat(this.#resolve(path)); return { contentUpdatedAt: stat.mtime ?? undefined, createdAt: stat.birthtime ?? undefined, }; }, }; return dir;
-
@@ -203,6 +235,14 @@ }} return converted; }, stat: async () => { const stat = await Deno.stat(this.#resolve(path)); return { contentUpdatedAt: stat.mtime ?? undefined, createdAt: stat.birthtime ?? undefined, }; }, };
-
-
-
@@ -9,14 +9,23 @@ FileReader,RootDirectoryReader, } from "../types.ts"; interface InternalMetadata { updatedAt?: Date; createdAt?: Date; } interface FileBuilder { path: string | readonly string[]; content: string | Uint8Array; metadata?: InternalMetadata; } const SEP = "/"; type InternalTree = Map<string, Uint8Array | InternalTree>; type InternalTree = Map< string, (readonly [Uint8Array, InternalMetadata]) | InternalTree >; /** * In-memory readonly filesystem.
-
@@ -39,13 +48,14 @@ const content = typeof file.content === "string"? encoder.encode(file.content) : file.content; this.#createRecur(path, content); this.#createRecur(path, content, file.metadata ?? {}); } } #createRecur( path: readonly string[], content: Uint8Array, metadata: InternalMetadata, parent: InternalTree = this.#tree, ): void { switch (path.length) {
-
@@ -61,14 +71,14 @@ `Trying to create a file named "${name}", but directory with same name already exists.`,); } parent.set(name, content); parent.set(name, [content, metadata]); return; } default: { const [name, ...rest] = path; let dir = parent.get(name); if (dir && dir instanceof Uint8Array) { if (dir && dir instanceof Array) { throw new Error( `Trying to create a directory named "${name}", but file with same name already exists.`, );
-
@@ -79,7 +89,7 @@ dir = new Map();parent.set(name, dir); } this.#createRecur(rest, content, dir); this.#createRecur(rest, content, metadata, dir); return; } }
-
@@ -102,6 +112,7 @@ path,parent, read: () => Promise.resolve(this.#mapToReaders(contentOrSubTree, dir)), stat: () => ({}), }; readers.push(dir); continue;
-
@@ -112,7 +123,13 @@ type: "file",name, path, parent, read: () => Promise.resolve(contentOrSubTree), read: () => Promise.resolve(contentOrSubTree[0]), stat: () => { return { createdAt: contentOrSubTree[1].createdAt, contentUpdatedAt: contentOrSubTree[1].updatedAt, }; }, }); }
-
-
-
@@ -3,18 +3,19 @@ //// SPDX-License-Identifier: Apache-2.0 /** @jsx h */ /** @jsxFrag Fragment */ import { h } from "../../../deps/deno.land/x/nano_jsx/mod.ts"; import { Fragment, jsx, jsxs, } from "../../../deps/deno.land/x/nano_jsx/jsx-runtime/index.ts"; import { Fragment, 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 { toHast } from "../../../deps/esm.sh/mdast-util-to-hast/mod.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 { Document, DocumentTree } from "../../../types.ts"; import type { Document, DocumentMetadata, DocumentTree, } from "../../../types.ts"; import { type CalloutType, type ObsidianMarkdownDocument,
-
@@ -106,12 +107,12 @@ return <LucideIcons.Pencil role="img" aria-label="Pencil icon" />;} }, }, Fragment, Fragment: jsxRuntime.Fragment, jsx(type, props, key) { return jsx(type, nanoifyProps(props), key || ""); return jsxRuntime.jsx(type, nanoifyProps(props), key || ""); }, jsxs(type, props, key) { return jsxs(type, nanoifyProps(props), key || ""); return jsxRuntime.jsxs(type, nanoifyProps(props), key || ""); }, }); }
-
@@ -126,6 +127,51 @@ Toc.styles,JSONCanvasRenderer.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 ObsidianMarkdownBodyProps extends ViewProps { content: ObsidianMarkdownDocument; }
-
@@ -173,6 +219,7 @@ logoImage={assets.siteLogo}defaultDocument={tree.defaultDocument} > <h1>{document.metadata.title}</h1> <MetadataDates metadata={document.metadata} /> {contentNodes} </SiteLayout.View> );
-
@@ -198,6 +245,7 @@ logoImage={assets.siteLogo}defaultDocument={tree.defaultDocument} > <h1>{document.metadata.title}</h1> <MetadataDates metadata={document.metadata} /> <JSONCanvasRenderer.View data={content.content} /> </SiteLayout.View> );
-
@@ -266,6 +314,13 @@ assets={assets}{...props} /> )} <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> );
-
-
-
@@ -158,6 +158,29 @@ return { metadata };}; } /** * Use creation/update timestamps as metadata timestamps. * This strategy does not overwrite if those are already set. */ export function useFileSystemTimestamps(): TreeBuildStrategy { return async (node, metadata) => { // If other strategy already sets them, skip the process for performance. if (metadata.createdAt && metadata.updatedAt) { return { metadata }; } const stats = await node.stat(); return { metadata: { ...metadata, createdAt: metadata.createdAt ?? stats.createdAt, updatedAt: metadata.updatedAt ?? stats.contentUpdatedAt, }, }; }; } function isAssetToken(token: unknown): token is AssetToken { return typeof token === "string" && token.startsWith("mxa_"); }
-
-
-
@@ -2,6 +2,11 @@ // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>// // SPDX-License-Identifier: Apache-2.0 export interface FileSystemStats { contentUpdatedAt?: Date; createdAt?: Date; } export interface FileReader { readonly type: "file"; readonly name: string;
-
@@ -9,6 +14,8 @@ readonly path: readonly string[];readonly parent: DirectoryReader | RootDirectoryReader; read(): Promise<Uint8Array>; stat(): FileSystemStats | Promise<FileSystemStats>; } export interface DirectoryReader {
-
@@ -18,6 +25,8 @@ readonly path: readonly string[];readonly parent: DirectoryReader | RootDirectoryReader; read(): Promise<ReadonlyArray<FileReader | DirectoryReader>>; stat(): FileSystemStats | Promise<FileSystemStats>; } export type DocumentToken = `mxt_${string}`;
-
@@ -71,6 +80,16 @@ * The behavior of when multiple documents have this property set to true is undefined.* This property does not take an effect for document tree. */ readonly isDefaultDocument?: boolean; /** * Datetime when the document or the document directory created at. */ readonly createdAt?: Date; /** * Datetime when the document or the document directory last updated at. */ readonly updatedAt?: Date; } export interface DocumentContent<
-