From 8c971e431aaf79ec3582a62fac8402896c30f1d9 Mon Sep 17 00:00:00 2001 From: h Date: Sat, 29 Aug 2026 19:39:19 +0200 Subject: [PATCH] fix(css,preview): scope utilities under the view root, preview with real boards and foreign css --- .gitignore | 1 + README.md | 16 +++++ package.json | 6 +- preview/entry.ts | 64 ++++++++++++++++--- preview/index.html | 2 + preview/serve.mjs | 64 ++++++++++++++++++- preview/shot.mjs | 137 ++++++++++++++++++++++++++++++++++++++++ scripts/css.mjs | 48 ++++++++++++++ scripts/scope-css.mjs | 71 +++++++++++++++++++++ tests/scope-css.test.ts | 63 ++++++++++++++++++ 10 files changed, 458 insertions(+), 14 deletions(-) create mode 100644 preview/shot.mjs create mode 100644 scripts/css.mjs create mode 100644 scripts/scope-css.mjs create mode 100644 tests/scope-css.test.ts diff --git a/.gitignore b/.gitignore index c66f83c..f80d756 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ node_modules/ # vault by `make install`. main.js styles.css +.tailwind.css *.zip # Obsidian writes plugin settings here when the repo is symlinked into a diff --git a/README.md b/README.md index df76efb..30c2b1b 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,19 @@ make zip # distributable ``` After `make install` turn on plugin in settings. + +`styles.css` is Tailwind output run through `scripts/scope-css.mjs`: every +selector is prefixed with `:is(.bcal-root, .bcal-root *)`. Other plugins built +with Tailwind put their utilities into the same `utilities` cascade layer, and +without the extra specificity whichever plugin loaded last would win (their +`.hidden` over our `@container` variants, for one). + +Preview knobs, all optional: + +```bash +VAULT=/path/to/vault npm run preview # the vault's theme +BOARDS=/path/to/vault/Boards npm run preview # real boards, not fixtures +EXTRA_CSS=/path/to/other-plugin/styles.css … # another plugin's css, loaded after ours +open 'http://localhost:4173/?groupBy=due&railCollapsed=1#edit-date' # prefs + scene +node preview/shot.mjs out.png 'http://localhost:4173/#date' # screenshot + console +``` diff --git a/package.json b/package.json index 9f832c5..46e0703 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,10 @@ "private": true, "type": "module", "scripts": { - "css": "tailwindcss -i src/tailwind.css -o styles.css", - "css:watch": "tailwindcss -i src/tailwind.css -o styles.css --watch", + "css": "node scripts/css.mjs", + "css:watch": "node scripts/css.mjs --watch", "dev": "npm run css && node esbuild.config.mjs", - "build": "svelte-check --tsconfig ./tsconfig.json --threshold error && npm run css -- --minify && node esbuild.config.mjs production", + "build": "svelte-check --tsconfig ./tsconfig.json --threshold error && node scripts/css.mjs --minify && node esbuild.config.mjs production", "test": "vitest run && node tests/smoke.mjs", "preview": "node preview/serve.mjs" }, diff --git a/preview/entry.ts b/preview/entry.ts index bc08ecc..cdc966e 100644 --- a/preview/entry.ts +++ b/preview/entry.ts @@ -2,17 +2,46 @@ import { Component, TFile } from "obsidian"; import { mount } from "svelte"; import App from "../src/ui/app.svelte"; import { DEFAULT_SETTINGS } from "../src/settings"; +import type { BeaverCalendarSettings } from "../src/settings"; import { BoardStore } from "../src/vault/store.svelte"; -const BOARDS = ["personal", "work", "reading", "home"]; -const FOLDER = "Boards"; +const FIXTURES = ["personal", "work", "reading", "home"]; + +/** + * Settings come off the query string, so a screenshot can start from the + * same preferences a real vault has: ?groupBy=due&includeArchive=1&railCollapsed=1 + */ +function prefsFromQuery(): Partial { + const params = new URLSearchParams(location.search); + const out: Record = {}; + const flag = (value: string): boolean => value === "1" || value === "true"; + for (const [key, value] of params) { + if (!(key in DEFAULT_SETTINGS)) continue; + const current = DEFAULT_SETTINGS[key as keyof BeaverCalendarSettings]; + out[key] = typeof current === "boolean" ? flag(value) : value; + } + return out as Partial; +} async function main(): Promise { + // Real boards when the server was given a folder, the fixtures otherwise. + const index = (await (await fetch("/boards.json")).json()) as { + folder: string; + names: string[]; + } | null; + const folder = index?.folder ?? "Boards"; + const sources = index + ? index.names.map((name) => ({ path: `${folder}/${name}`, url: `/boards/${name}` })) + : FIXTURES.map((name) => ({ + path: `${folder}/${name}.md`, + url: `/tests/fixtures/${name}.md`, + })); + const files = new Map(); await Promise.all( - BOARDS.map(async (name) => { - const response = await fetch(`/tests/fixtures/${name}.md`); - files.set(`${FOLDER}/${name}.md`, await response.text()); + sources.map(async ({ path, url }) => { + const response = await fetch(url); + files.set(path, await response.text()); }), ); const handles = new Map( @@ -37,7 +66,7 @@ async function main(): Promise { plugins: { plugins: {} }, }; - const settings = { ...DEFAULT_SETTINGS, boardsFolder: FOLDER }; + const settings = { ...DEFAULT_SETTINGS, boardsFolder: folder, ...prefsFromQuery() }; const store = new BoardStore(app as never, settings, () => {}); await store.reloadAll(); @@ -58,6 +87,14 @@ async function main(): Promise { await wait(50); store.keyboard = true; const first = store.spheres[0]; + const tab = async (times: number): Promise => { + for (let i = 0; i < times; i += 1) { + document.activeElement?.dispatchEvent( + new KeyboardEvent("keydown", { key: "Tab", code: "Tab", bubbles: true }), + ); + await wait(20); + } + }; if (scene.startsWith("rail")) { store.toggleRail(); store.railExpanded = new Set([first.path, store.spheres[1].path]); @@ -85,10 +122,17 @@ async function main(): Promise { if (scene === "date") { store.write(null, "[data-composer='toolbar']"); await wait(50); - for (let i = 0; i < 3; i += 1) { - document.activeElement?.dispatchEvent(new KeyboardEvent("keydown", { key: "Tab", bubbles: true })); - await wait(20); - } + await tab(3); + } + // The editor over an existing task, tabbed through to its date field. + if (scene === "edit-date") { + const task = store.visible[0]; + store.zone = "task"; + store.cursorTask = task.id; + await wait(20); + store.edit(task.id); + await wait(50); + await tab(3); } } diff --git a/preview/index.html b/preview/index.html index d2cba35..b8dc2cb 100644 --- a/preview/index.html +++ b/preview/index.html @@ -6,6 +6,8 @@ + +