style(ui): use SF Pro globally and add release helper

This commit is contained in:
webadderall
2026-04-26 20:36:01 +10:00
parent 9c495beec1
commit 2d23885bd3
6 changed files with 144 additions and 21 deletions
+15 -2
View File
@@ -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.
- `build.yml` is intentionally forced to `--publish never` so ad hoc CI builds do not accidentally upload to a draft release.
+1
View File
@@ -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"
},
+120
View File
@@ -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 <tag> [--title <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);
}
+1 -1
View File
@@ -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,
+2 -10
View File
@@ -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 {
+5 -8
View File
@@ -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);
}
}