From 2d23885bd3ac2402e661aab501baa2b5592760d9 Mon Sep 17 00:00:00 2001 From: webadderall <131426131+webadderall@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:36:01 +1000 Subject: [PATCH] style(ui): use SF Pro globally and add release helper --- RELEASING.md | 17 ++- package.json | 1 + scripts/create-release.mjs | 120 ++++++++++++++++++++ src/components/launch/UpdateToastWindow.tsx | 2 +- src/components/video-editor/types.ts | 12 +- src/index.css | 13 +-- 6 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 scripts/create-release.mjs diff --git a/RELEASING.md b/RELEASING.md index 7af64644..4ec8e22a 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -73,7 +73,20 @@ Optional repository variables: 1. Bump `package.json` to the version you want to ship. 2. Commit and push that version. 3. Create a Git tag in the form `vX.Y.Z`. -4. In GitHub, create and publish a release for that tag. +4. Create and publish a GitHub release for that tag. Prefer the helper so custom notes are prepended while GitHub still generates the contributor section: + +```bash +npm run release:create -- --tag v1.2.3 --title "v1.2.3" --notes-file ./release-notes.md +``` + +For prereleases: + +```bash +npm run release:create -- --tag v1.2.0-beta.2 --title "v1.2.0 beta-2" --prerelease --notes-file ./release-notes.md +``` + +This uses `gh release create --generate-notes`, which keeps GitHub's generated change summary and contributor list instead of replacing it with a fully manual release body. + 5. The `Publish Release` workflow builds, signs, notarizes, uploads, and publishes update metadata. That is the normal path if you want “click new release and let CI do the rest.” @@ -87,4 +100,4 @@ If you need to rerun publishing for an existing tag, use the manual dispatch for - macOS auto-updates require the `zip` target in addition to `dmg`, because `latest-mac.yml` is generated from the zipped build. - macOS arm64 and x64 builds both publish updater zips, and the release workflow merges them into one `latest-mac.yml` so `electron-updater` can choose the correct architecture automatically. - The release workflow publishes versioned artifact names so the generated update metadata matches the uploaded files. -- `build.yml` is intentionally forced to `--publish never` so ad hoc CI builds do not accidentally upload to a draft release. \ No newline at end of file +- `build.yml` is intentionally forced to `--publish never` so ad hoc CI builds do not accidentally upload to a draft release. diff --git a/package.json b/package.json index 12cea5e0..e58ea7a2 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "build:linux": "npm run build:platform-native-helpers && tsc && vite build --config vite.config.ts && electron-builder --linux", "i18n:check": "node scripts/i18n-check.mjs", "benchmark:export-queues": "node scripts/benchmark-export-queues.mjs", + "release:create": "node scripts/create-release.mjs", "test": "vitest --run", "test:watch": "vitest" }, diff --git a/scripts/create-release.mjs b/scripts/create-release.mjs new file mode 100644 index 00000000..3a586bde --- /dev/null +++ b/scripts/create-release.mjs @@ -0,0 +1,120 @@ +import { execFileSync } from "node:child_process"; +import fs from "node:fs"; + +function printUsage() { + console.error(`Usage: + node scripts/create-release.mjs --tag [--title ] [--notes <text> | --notes-file <path>] [--prerelease] [--draft] + +Examples: + node scripts/create-release.mjs --tag v1.2.0-beta.2 --title "v1.2.0 beta-2" --prerelease --notes-file ./release-notes.md + node scripts/create-release.mjs --tag v1.2.0 --notes "Stable release summary" +`); +} + +function parseArgs(argv) { + const parsed = { + tag: "", + title: "", + notes: "", + notesFile: "", + prerelease: false, + draft: false, + }; + + for (let i = 0; i < argv.length; i += 1) { + const arg = argv[i]; + switch (arg) { + case "--tag": + parsed.tag = argv[++i] ?? ""; + break; + case "--title": + parsed.title = argv[++i] ?? ""; + break; + case "--notes": + parsed.notes = argv[++i] ?? ""; + break; + case "--notes-file": + parsed.notesFile = argv[++i] ?? ""; + break; + case "--prerelease": + parsed.prerelease = true; + break; + case "--draft": + parsed.draft = true; + break; + case "--help": + case "-h": + printUsage(); + process.exit(0); + break; + default: + throw new Error(`Unknown argument: ${arg}`); + } + } + + if (!parsed.tag) { + throw new Error("Missing required --tag argument"); + } + + if (parsed.notes && parsed.notesFile) { + throw new Error("Use either --notes or --notes-file, not both"); + } + + return parsed; +} + +function loadNotes({ notes, notesFile }) { + if (notesFile) { + return fs.readFileSync(notesFile, "utf8"); + } + + return notes; +} + +function resolveGhBinary() { + const candidates = [process.env.GH_BIN, "gh", "/opt/homebrew/bin/gh", "/usr/local/bin/gh"].filter( + Boolean, + ); + + for (const candidate of candidates) { + try { + execFileSync(candidate, ["--version"], { stdio: "ignore" }); + return candidate; + } catch { + continue; + } + } + + throw new Error( + "Could not find the GitHub CLI. Install `gh`, add it to PATH, or set GH_BIN to its full path.", + ); +} + +try { + const options = parseArgs(process.argv.slice(2)); + const notes = loadNotes(options).trim(); + const ghBinary = resolveGhBinary(); + const commandArgs = ["release", "create", options.tag, "--verify-tag", "--generate-notes"]; + + if (options.title) { + commandArgs.push("--title", options.title); + } + + if (notes) { + commandArgs.push("--notes", notes); + } + + if (options.prerelease) { + commandArgs.push("--prerelease"); + } + + if (options.draft) { + commandArgs.push("--draft"); + } + + execFileSync(ghBinary, commandArgs, { stdio: "inherit" }); +} catch (error) { + console.error(error instanceof Error ? error.message : String(error)); + printUsage(); + process.exit(1); +} diff --git a/src/components/launch/UpdateToastWindow.tsx b/src/components/launch/UpdateToastWindow.tsx index 2f2f89d4..e872a25c 100644 --- a/src/components/launch/UpdateToastWindow.tsx +++ b/src/components/launch/UpdateToastWindow.tsx @@ -167,7 +167,7 @@ export function UpdateToastWindow() { border: "1px solid rgba(37, 99, 235, 0.24)", boxShadow: "0 20px 48px rgba(2, 6, 23, 0.5), inset 0 1px 0 rgba(148, 163, 184, 0.08)", color: "#ffffff", - fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif', + fontFamily: "var(--app-font-sans)", } as const; const iconBoxStyle = { width: 42, diff --git a/src/components/video-editor/types.ts b/src/components/video-editor/types.ts index 69cde9ad..ec29d812 100644 --- a/src/components/video-editor/types.ts +++ b/src/components/video-editor/types.ts @@ -266,19 +266,11 @@ export interface AnnotationTextStyle { } function getDefaultAnnotationFontFamily() { - if (typeof navigator !== "undefined" && /mac/i.test(navigator.platform)) { - return '"SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, sans-serif'; - } - - return "Inter, system-ui, sans-serif"; + return '"SF Pro Display", "SF Pro Text", Helvetica, sans-serif'; } export function getDefaultCaptionFontFamily() { - if (typeof navigator !== "undefined" && /mac/i.test(navigator.platform)) { - return '"SF Pro Text", "SF Pro Display", -apple-system, BlinkMacSystemFont, sans-serif'; - } - - return '"Helvetica Neue", Helvetica, Arial, sans-serif'; + return '"SF Pro Text", "SF Pro Display", Helvetica, sans-serif'; } export interface AnnotationRegion { diff --git a/src/index.css b/src/index.css index 2ce5d50e..765a6239 100644 --- a/src/index.css +++ b/src/index.css @@ -23,6 +23,7 @@ } :root { + --app-font-sans: "SF Pro Display", "SF Pro Text", Helvetica, sans-serif; --brand-accent: #2563eb; --brand-accent-rgb: 37, 99, 235; --background: 0 0% 100%; @@ -124,7 +125,7 @@ -webkit-user-select: none; user-select: none; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: var(--app-font-sans); } button, @@ -134,13 +135,9 @@ font: inherit; } - [data-platform="macos"] body { - font-family: "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, sans-serif; - } - - [data-platform="macos"] body .font-mono, - [data-platform="macos"] body kbd { - font-family: "SF Pro Text", "SF Pro Display", -apple-system, BlinkMacSystemFont, sans-serif; + body .font-mono, + body kbd { + font-family: var(--app-font-sans); } }