mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-07 19:58:24 +00:00
0f78505e99
Every note gets an Edit form built from its note type's schema: image fields show a thumbnail with a pick from the vault or an upload, link fields offer note names, and lists of rows can be added to, removed and reordered. A new Site pages view lists each page of the site with its text and images and the notes that fill it. Image and link fields now carry a JSON Schema format so a form knows to offer a picker. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
/**
|
|
* Field builders shared by every note type.
|
|
*
|
|
* Each field carries `title` and `description` metadata. The JSON Schema
|
|
* export keeps them, so the MCP server, the admin page and any later editor
|
|
* can render a form or explain a field without a second definition. Image
|
|
* and link fields also carry a `format` (`vault-image`, `vault-link`,
|
|
* `vault-links`) so a form knows to offer a picker.
|
|
*/
|
|
import { z } from "zod";
|
|
import { normalizeWikilinkInput, wikilinkTarget } from "./names.ts";
|
|
|
|
type Meta = { title: string; description?: string };
|
|
|
|
/** A calendar date written `YYYY-MM-DD`, as Obsidian writes dates. */
|
|
export const isoDate = (meta: Meta) =>
|
|
z
|
|
.preprocess(
|
|
(v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),
|
|
z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Use a date like 2026-10-11"),
|
|
)
|
|
.meta(meta);
|
|
|
|
/** A month written `YYYY-MM`, for resume dates. */
|
|
export const isoMonth = (meta: Meta) =>
|
|
z.string().regex(/^\d{4}-\d{2}$/, "Use a month like 2019-03").meta(meta);
|
|
|
|
/** A link to another note, written `[[Note name]]`. */
|
|
export const wikilink = (meta: Meta) =>
|
|
z
|
|
.preprocess(
|
|
normalizeWikilinkInput,
|
|
z.string().refine((v) => wikilinkTarget(v) !== null, "Write a link as [[Note name]]"),
|
|
)
|
|
.meta({ format: "vault-link", ...meta });
|
|
|
|
/** A list of links to other notes. */
|
|
export const wikilinks = (meta: Meta) =>
|
|
z
|
|
.preprocess(
|
|
(v) => (Array.isArray(v) ? v.map(normalizeWikilinkInput) : v),
|
|
z.array(z.string().refine((v) => wikilinkTarget(v) !== null, "Write each link as [[Note name]]")),
|
|
)
|
|
.meta({ format: "vault-links", ...meta });
|
|
|
|
/** An image: a vault attachment as `[[file.jpg]]`, or a full https URL. */
|
|
export const image = (meta: Meta) =>
|
|
z
|
|
.preprocess(
|
|
normalizeWikilinkInput,
|
|
z
|
|
.string()
|
|
.refine(
|
|
(v) => wikilinkTarget(v) !== null || /^https:\/\//.test(v),
|
|
"Use a vault image as [[file.jpg]] or a full https URL",
|
|
),
|
|
)
|
|
.meta({ format: "vault-image", ...meta });
|
|
|
|
/** One short line of text. */
|
|
export const line = (meta: Meta) => z.string().trim().min(1).meta(meta);
|
|
|
|
/** A list of short tags or names. */
|
|
export const strings = (meta: Meta) => z.array(z.string().trim().min(1)).meta(meta);
|
|
|
|
/**
|
|
* Fields every note shares. `type` is added per note type.
|
|
*
|
|
* `publish` defaults to false: a note goes live only when it says so.
|
|
*/
|
|
export const baseFields = {
|
|
title: line({ title: "Title" }),
|
|
slug: z
|
|
.string()
|
|
.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Lowercase words joined by hyphens")
|
|
.meta({ title: "Slug", description: "The last part of the page address. Made from the title when left out." })
|
|
.optional(),
|
|
publish: z
|
|
.boolean()
|
|
.default(false)
|
|
.meta({ title: "Publish", description: "Only notes set to true appear on the site." }),
|
|
favorite: z.boolean().meta({ title: "Favorite", description: "Starred on the site." }).optional(),
|
|
tags: strings({ title: "Tags" }).optional(),
|
|
links: wikilinks({
|
|
title: "Links",
|
|
description: "Other notes this one is about. The site shows the link on both notes.",
|
|
}).optional(),
|
|
cover: image({ title: "Cover image" }).optional(),
|
|
created: isoDate({ title: "Created" }).optional(),
|
|
updated: isoDate({ title: "Updated" }).optional(),
|
|
updatedBy: z
|
|
.enum(["greg", "agent", "admin", "sync"])
|
|
.meta({ title: "Last edited by", description: "Set by the content service; you never need to write it." })
|
|
.optional(),
|
|
agentEdits: z
|
|
.boolean()
|
|
.meta({ title: "Agents may edit", description: "Let agents change this note without your review." })
|
|
.optional(),
|
|
};
|