fix: preserve vault notes and user fonts

This commit is contained in:
Peter Li 2026-07-16 20:44:46 +08:00
parent fcd3d9f1e0
commit 4cec652800
8 changed files with 82 additions and 7 deletions

View file

@ -11,9 +11,11 @@ dark, desktop, and mobile support.
Run `npm install`, `npm run build`, and `npm run check`.
For live preview, set `AERA_TEST_VAULT`, then run `npm run link:vault` and
`npm run dev`. Changes to `manifest.json` require an Obsidian restart, while
compiled CSS changes automatically reload in the linked vault.
For live preview, set `AERA_TEST_VAULT` to a dedicated test vault, then run
`npm run link:vault` and `npm run dev`. The linker refuses to replace unknown
notes named `Theme Playground.md` or `Embedded Note.md`. Changes to
`manifest.json` require an Obsidian restart, while
compiled CSS automatically reloads in the linked vault.
## Release assets

View file

@ -3,3 +3,5 @@
这是一篇用于验证 `[[Embedded Note]]` 已解析内部链接与嵌入预览的中文笔记。
返回 [[Theme Playground]]。
<!-- Aera fixture managed by obsidian-aera-theme. -->

View file

@ -66,3 +66,5 @@ published: 2026-07-16
演练场以脚注引用结束。[^aera]
[^aera]: Aera 是用于检查 Obsidian 主题的演练 fixture。
<!-- Aera fixture managed by obsidian-aera-theme. -->

View file

@ -17,6 +17,8 @@ import { fileURLToPath } from "node:url";
const modulePath = fileURLToPath(import.meta.url);
const defaultRepoPath = dirname(dirname(modulePath));
const fixtureNames = ["Theme Playground.md", "Embedded Note.md"];
const fixtureOwnershipMarker =
"<!-- Aera fixture managed by obsidian-aera-theme. -->";
async function existingPathType(path) {
try {
@ -55,6 +57,17 @@ async function assertSafeFixtureTarget(path) {
throw new Error(`Refusing to replace fixture ${type} at ${path}`);
}
async function assertManagedFixtureTarget(path) {
const existing = await existingPathType(path);
if (!existing) return;
await assertSafeFixtureTarget(path);
const contents = await readFile(path, "utf8");
if (!contents.trimEnd().endsWith(fixtureOwnershipMarker)) {
throw new Error(`Refusing to replace unknown fixture at ${path}`);
}
}
export async function copyFixtureSafely(
sourcePath,
destinationPath,
@ -140,7 +153,7 @@ export async function linkVault(vaultPath, repoPath = defaultRepoPath) {
source: join(repo, "fixtures", name),
}));
for (const { destination } of fixtures) {
await assertSafeFixtureTarget(destination);
await assertManagedFixtureTarget(destination);
}
for (const { destination, source } of fixtures) {
await copyFixtureSafely(source, destination);

View file

@ -1,6 +1,6 @@
body {
--inline-title-color: var(--text-normal);
--inline-title-font: var(--font-text-theme);
--inline-title-font: var(--font-text);
--inline-title-line-height: 1.25;
--inline-title-size: 2em;
--inline-title-weight: 650;

View file

@ -16,7 +16,7 @@ function assertDeclarations(expected) {
test("defines the inline title hierarchy", () => {
assertDeclarations({
"--inline-title-color": "var(--text-normal)",
"--inline-title-font": "var(--font-text-theme)",
"--inline-title-font": "var(--font-text)",
"--inline-title-line-height": "1.25",
"--inline-title-size": "2em",
"--inline-title-weight": "650",

View file

@ -28,6 +28,8 @@ import {
const repoRoot = dirname(fileURLToPath(new URL("../package.json", import.meta.url)));
const cliPath = join(repoRoot, "scripts/link-vault.mjs");
const fixtureNames = ["Theme Playground.md", "Embedded Note.md"];
const fixtureOwnershipMarker =
"<!-- Aera fixture managed by obsidian-aera-theme. -->";
async function createVault(t) {
const vault = await mkdtemp(join(tmpdir(), "aera-vault-"));
@ -71,6 +73,58 @@ test("linkVault links Aera and copies the playground fixtures", async (t) => {
}
});
test("linkVault updates fixtures carrying the Aera ownership marker", async (t) => {
const vault = await createVault(t);
for (const name of fixtureNames) {
await writeFile(
join(vault, name),
`stale fixture\n\n${fixtureOwnershipMarker}\n`,
);
}
await linkVault(vault, repoRoot);
for (const name of fixtureNames) {
assert.equal(
await readFile(join(vault, name), "utf8"),
await readFile(join(repoRoot, "fixtures", name), "utf8"),
);
}
});
for (const unknownFixture of fixtureNames) {
test(`linkVault rejects an unknown ${unknownFixture} before updating any fixture`, async (t) => {
const vault = await createVault(t);
const originalContents = new Map();
for (const name of fixtureNames) {
const contents =
name === unknownFixture
? `personal ${name} content\n`
: `stale owned ${name}\n\n${fixtureOwnershipMarker}\n`;
originalContents.set(name, contents);
await writeFile(join(vault, name), contents);
}
await assert.rejects(
linkVault(vault, repoRoot),
new RegExp(`Refusing to replace unknown fixture.*${unknownFixture}`, "i"),
);
for (const name of fixtureNames) {
assert.equal(
await readFile(join(vault, name), "utf8"),
originalContents.get(name),
);
}
assert.equal(
(await readdir(vault)).some((name) => /aera.*tmp|\.tmp/i.test(name)),
false,
);
});
}
test("the playground fixture covers the supported Markdown elements", async () => {
const playground = await readFile(
join(repoRoot, "fixtures", "Theme Playground.md"),
@ -118,6 +172,7 @@ test("the playground fixture covers the supported Markdown elements", async () =
assert.match(playground, /^!\[\[Embedded Note\]\]$/m);
assert.match(playground, /^演练场以脚注引用结束。\[\^aera\]$/m);
assert.match(playground, /^\[\^aera\]: Aera 是用于检查 Obsidian 主题的演练 fixture。$/m);
assert.equal(playground.endsWith(`${fixtureOwnershipMarker}\n`), true);
const embedded = await readFile(
join(repoRoot, "fixtures", "Embedded Note.md"),
@ -126,6 +181,7 @@ test("the playground fixture covers the supported Markdown elements", async () =
assert.match(embedded, /^# 嵌入内容$/m);
assert.match(embedded, /^这是一篇用于验证 `\[\[Embedded Note\]\]` 已解析内部链接与嵌入预览的中文笔记。$/m);
assert.match(embedded, /^返回 \[\[Theme Playground\]\]。$/m);
assert.equal(embedded.endsWith(`${fixtureOwnershipMarker}\n`), true);
});
for (const fixtureName of fixtureNames) {

View file

@ -72,7 +72,7 @@ body {
body {
--inline-title-color: var(--text-normal);
--inline-title-font: var(--font-text-theme);
--inline-title-font: var(--font-text);
--inline-title-line-height: 1.25;
--inline-title-size: 2em;
--inline-title-weight: 650;