chore: add changelog helper and unreleased notes
This commit is contained in:
@@ -5,6 +5,26 @@ All notable changes to Phokus are documented here. The format is based on
|
|||||||
aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||||
(0.x: anything may change between minor versions).
|
(0.x: anything may change between minor versions).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Failed AI-tagging jobs can now be located from the background worker prompt,
|
||||||
|
including a gallery filter for images with failed tags and an expanded list
|
||||||
|
of failed filenames/errors.
|
||||||
|
- A new theme system adds Phokus, Subtle Light, and Conventional Dark chrome
|
||||||
|
options across the app.
|
||||||
|
- First-run onboarding now includes an inline theme picker so new users can
|
||||||
|
choose their preferred app chrome before continuing the tour.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Polished the new theme surfaces before release, including readable
|
||||||
|
subtle-light secondary buttons, failed-worker action buttons, and onboarding
|
||||||
|
controls.
|
||||||
|
- Onboarding preview media keeps the dark gallery/media surface regardless of
|
||||||
|
the active chrome theme.
|
||||||
|
|
||||||
## [0.1.0] — 2026-06-14
|
## [0.1.0] — 2026-06-14
|
||||||
|
|
||||||
First public release. Windows desktop, distributed as an unsigned NSIS
|
First public release. Windows desktop, distributed as an unsigned NSIS
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"dev:web": "cd website && pnpm dev",
|
"dev:web": "cd website && pnpm dev",
|
||||||
"build:app:cpu": "tauri build -- --no-default-features",
|
"build:app:cpu": "tauri build -- --no-default-features",
|
||||||
"build:app:cuda": "tauri build --config src-tauri/tauri.cuda.conf.json",
|
"build:app:cuda": "tauri build --config src-tauri/tauri.cuda.conf.json",
|
||||||
|
"changelog:add": "node tools/changelog-add.mjs",
|
||||||
"dev:vite": "vite",
|
"dev:vite": "vite",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import { readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { resolve } from "node:path";
|
||||||
|
|
||||||
|
const SECTION_BY_TYPE = {
|
||||||
|
added: "Added",
|
||||||
|
changed: "Changed",
|
||||||
|
deprecated: "Deprecated",
|
||||||
|
removed: "Removed",
|
||||||
|
fixed: "Fixed",
|
||||||
|
security: "Security",
|
||||||
|
};
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
console.error([
|
||||||
|
"Usage:",
|
||||||
|
" pnpm changelog:add -- --type fixed --message \"Fix thing\"",
|
||||||
|
"",
|
||||||
|
`Types: ${Object.keys(SECTION_BY_TYPE).join(", ")}`,
|
||||||
|
].join("\n"));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function argValue(name) {
|
||||||
|
const index = process.argv.indexOf(`--${name}`);
|
||||||
|
if (index === -1) return null;
|
||||||
|
return process.argv[index + 1] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = argValue("type")?.toLowerCase();
|
||||||
|
const message = argValue("message")?.trim();
|
||||||
|
|
||||||
|
if (!type || !message || !SECTION_BY_TYPE[type]) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
const section = SECTION_BY_TYPE[type];
|
||||||
|
const changelogPath = resolve("CHANGELOG.md");
|
||||||
|
let changelog = readFileSync(changelogPath, "utf8");
|
||||||
|
|
||||||
|
if (!/^## \[Unreleased\]/m.test(changelog)) {
|
||||||
|
changelog = changelog.replace(
|
||||||
|
/(\r?\n)(## \[[^\r\n]+\])/,
|
||||||
|
`$1## [Unreleased]$1$1$2`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unreleasedMatch = changelog.match(/^## \[Unreleased\][\s\S]*?(?=^## \[|\z)/m);
|
||||||
|
if (!unreleasedMatch) {
|
||||||
|
throw new Error("Could not find or create an Unreleased section.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const unreleased = unreleasedMatch[0];
|
||||||
|
const lineEnding = changelog.includes("\r\n") ? "\r\n" : "\n";
|
||||||
|
const bullet = `- ${message}`;
|
||||||
|
|
||||||
|
let nextUnreleased;
|
||||||
|
const sectionRegex = new RegExp(`(^### ${section}\\s*)([\\s\\S]*?)(?=^### |\\z)`, "m");
|
||||||
|
const sectionMatch = unreleased.match(sectionRegex);
|
||||||
|
|
||||||
|
if (sectionMatch) {
|
||||||
|
const body = sectionMatch[2].trimEnd();
|
||||||
|
const replacement = `${sectionMatch[1]}${body ? `${body}${lineEnding}` : ""}${bullet}${lineEnding}${lineEnding}`;
|
||||||
|
nextUnreleased = unreleased.replace(sectionRegex, replacement);
|
||||||
|
} else {
|
||||||
|
const insert = `${lineEnding}### ${section}${lineEnding}${lineEnding}${bullet}${lineEnding}`;
|
||||||
|
nextUnreleased = unreleased.trimEnd() + insert + lineEnding;
|
||||||
|
}
|
||||||
|
|
||||||
|
changelog = changelog.replace(unreleased, nextUnreleased);
|
||||||
|
writeFileSync(changelogPath, changelog);
|
||||||
Reference in New Issue
Block a user