From 02437358cfcac639e786600e982fb1921353c9a7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 16:52:56 +0000 Subject: [PATCH 1/2] Add a Before launch placeholder check to admin Lists pages still showing built-in wording, notes holding placeholder text, unpublished posts and live notes missing the field their page is built around, each linked to its editor. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx --- docs/admin.md | 1 + services/admin/public/admin.css | 6 +++ services/admin/src/client.ts | 9 ++++ services/admin/src/launch.ts | 87 +++++++++++++++++++++++++++++++ services/admin/src/layout.ts | 1 + services/admin/src/server.ts | 3 ++ services/admin/test/admin.test.ts | 8 +++ 7 files changed, 115 insertions(+) create mode 100644 services/admin/src/launch.ts diff --git a/docs/admin.md b/docs/admin.md index 91cc26f..9d1f1c2 100644 --- a/docs/admin.md +++ b/docs/admin.md @@ -17,6 +17,7 @@ the vault so Obsidian shows them too. Its changes are recorded with the author | --- | --- | | Overview | Live and total notes, what waits for review, folder problems, the service's last scan and last error, a Scan now button, each sync job's last run and last success, and Library suggestions: series moved to Reading in PersonalReader with no Library item, each with Add to the Library (title, medium, AniList id, state now, published) and Dismiss. | | Site pages | Each page of the site with its Page text note (lede, photos, portrait) as thumbnails, **Edit page** or **Add page text**, and links to the notes that fill the page (crafts, games, decks, product lines, settings). | +| Before launch | What is still a placeholder, worked out from the notes so it shrinks as they are fixed: pages showing their built-in wording (no Page text note, a draft one, no lede, no About portrait), notes holding a comment, Coming soon, TODO, TBD or lorem, posts not published, and live notes missing the field their page is built around (a craft's blurb, a make's finish date, a deck's take). Each line links to Edit or Add page text. | | Review | Held changes with the rules' reasons and a line diff against the live version. Accept puts it live (and into the vault, for API changes). Reject keeps the live version; for an edit made in Obsidian, "Reject and put the live version back" also rewrites the file. Earlier decisions are listed by status. | | Notes, Drafts | Browse and search every note, or only the unpublished ones. Each row has Edit; filtered to one type, the list also offers to add a note of that type. | | A note | A preview of the body, its fields, what links to it, Edit, Publish or Unpublish, and its history. Any earlier version can be compared with the live one and rolled back. | diff --git a/services/admin/public/admin.css b/services/admin/public/admin.css index 722f287..9fefea9 100644 --- a/services/admin/public/admin.css +++ b/services/admin/public/admin.css @@ -138,3 +138,9 @@ form.inline button { min-height: 30px; padding: 2px 12px; font-size: 13px; } .page-images img { width: 120px; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 8px; border: 1px solid var(--line); display: block; margin-bottom: 4px; } .feeds { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; } .pill-link { border: 1px solid var(--line); border-radius: 999px; padding: 2px 10px; font-size: 0.85rem; } + +/* Before launch */ +.launch ul { list-style: none; padding: 0; margin: 0; } +.launch li { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; border-top: 1px solid var(--line); } +.launch li:first-child { border-top: 0; } +.count-pill { font-size: 0.8rem; border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px; margin-left: 6px; vertical-align: middle; } diff --git a/services/admin/src/client.ts b/services/admin/src/client.ts index 7eaba83..bfcf33f 100644 --- a/services/admin/src/client.ts +++ b/services/admin/src/client.ts @@ -155,6 +155,15 @@ export class ContentApi { notes = (p: { type?: string; q?: string; limit?: number; full?: boolean } = {}) => this.get("/api/notes", { type: p.type, q: p.q, limit: p.limit, full: p.full ? "true" : undefined }); fullNotes = (type: string) => this.get("/api/notes", { type, full: "true", limit: 1000 }); + /** Every note, whole, a thousand at a time. */ + async allNotes(): Promise { + const all: FullNote[] = []; + for (let offset = 0; ; offset += 1000) { + const page = await this.get("/api/notes", { full: "true", limit: 1000, offset }); + all.push(...page); + if (page.length < 1000) return all; + } + } note = (path: string) => this.get("/api/note", { path }); noteByName = (name: string) => this.get("/api/note", { name }); history = (path: string) => this.get("/api/note/history", { path }); diff --git a/services/admin/src/launch.ts b/services/admin/src/launch.ts new file mode 100644 index 0000000..96a52e4 --- /dev/null +++ b/services/admin/src/launch.ts @@ -0,0 +1,87 @@ +/** + * Before launch: what on the live site is still a placeholder, worked out + * from the notes themselves so it stays true as they change. Pages using + * their built-in wording, notes that still hold placeholder text, posts not + * published yet, and the thin spots the page designs expect to be filled. + */ +import type { FullNote } from "./client.ts"; +import { editHref, newHref } from "./editor.ts"; +import { html, type Html } from "./html.ts"; +import { sitePages } from "./sitepages.ts"; + +export interface Finding { + what: string; + /** The note to open, or a page to add. */ + href: string; + action: string; +} + +export interface Section { + title: string; + hint: string; + findings: Finding[]; +} + +/** Text that marks something as not written yet. */ +const PLACEHOLDER = /