From bf530cfb49521012c1e5a7b0fec13c4b8911f4ae Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Thu, 15 Oct 2026 20:02:49 -0400 Subject: [PATCH 1/3] fix(theme): restore GitHub Dark Default as the default theme --- .changeset/restore-github-default-theme.md | 5 +++ README.md | 2 +- docs/themes.md | 6 ++-- packages/hunk/src/core/run/config.test.ts | 8 ++--- packages/hunk/src/core/run/config.ts | 3 +- .../src/core/theme/terminalColors.test.ts | 10 ++++++ .../hunk/src/core/theme/terminalColors.ts | 8 ++--- .../hunk/src/ui/AppHost.keybindings.test.tsx | 4 +-- .../hooks/useThemeSelectorController.test.tsx | 2 +- packages/hunk/src/ui/lib/ui-lib.test.ts | 8 ++--- .../src/ui/session/HunkSessionHost.test.tsx | 8 ++--- packages/hunk/src/ui/themes.test.ts | 13 ++++--- packages/hunk/src/ui/themes.ts | 8 +++-- test/pty/chrome.test.ts | 8 ++--- test/pty/log-integration.test.ts | 6 ++-- test/pty/terminal-theme-integration.test.ts | 36 ++++++++++++++++++- .../src/content/docs/docs/configure/themes.md | 2 +- .../content/docs/docs/help/troubleshooting.md | 2 +- .../src/content/docs/docs/reference/config.md | 2 +- 19 files changed, 98 insertions(+), 43 deletions(-) create mode 100644 .changeset/restore-github-default-theme.md create mode 100644 packages/hunk/src/core/theme/terminalColors.test.ts diff --git a/.changeset/restore-github-default-theme.md b/.changeset/restore-github-default-theme.md new file mode 100644 index 000000000..fe566a4a6 --- /dev/null +++ b/.changeset/restore-github-default-theme.md @@ -0,0 +1,5 @@ +--- +"hunkdiff": patch +--- + +Restore `github-dark-default` as the default theme. The `terminal` theme remains available explicitly through config, `--theme terminal`, or the theme picker. diff --git a/README.md b/README.md index 2a7004e88..1b4e7721a 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ and falling back to it if migration fails. Global extension paths are unchanged. For example: ```toml -theme = "terminal" +theme = "github-dark-default" mode = "auto" # auto, split, unified auto_split_min_width = 160 # minimum available diff columns for split in auto mode line_numbers = true diff --git a/docs/themes.md b/docs/themes.md index 31dff94a4..6c4a851b4 100644 --- a/docs/themes.md +++ b/docs/themes.md @@ -1,7 +1,7 @@ # Themes -Hunk follows your terminal's own colors by default. You can instead choose a -built-in theme, let Hunk select one from your terminal background, or create a +Hunk uses `github-dark-default` by default. You can instead follow your terminal's +own colors, choose another built-in theme, select one from your terminal background, or create a custom theme in `~/.config/hunk/config.toml` or `.hunk/config.toml`. ```toml @@ -13,7 +13,7 @@ You can also change themes while reviewing: press `t` or choose ## Terminal theme -`theme = "terminal"` is the default. At startup Hunk asks the terminal for its +Set `theme = "terminal"` to follow your terminal's colors. At startup Hunk asks the terminal for its foreground, background, and 16-color ANSI palette, then draws diffs, chrome, and syntax highlighting from those colors: green for additions and strings, red for removals, blue for functions and accents, magenta for keywords, and so on. diff --git a/packages/hunk/src/core/run/config.test.ts b/packages/hunk/src/core/run/config.test.ts index 2d1f51b79..cc065c9ad 100644 --- a/packages/hunk/src/core/run/config.test.ts +++ b/packages/hunk/src/core/run/config.test.ts @@ -920,7 +920,7 @@ describe("config resolution", () => { expect(configured.input.options.transparentBackground).toBe(true); }); - test("defaults unspecified themes to terminal, including piped pager-style patch input", () => { + test("defaults unspecified themes to GitHub Dark Default, including piped pager-style patch input", () => { const home = createTempDir("hunk-config-home-"); const cwd = createTempDir("hunk-config-cwd-"); @@ -931,7 +931,7 @@ describe("config resolution", () => { expect(resolved.repoConfigPath).toBeUndefined(); expect(resolved.viewPreferencesConfigPath).toBe(join(home, ".config", "hunk", "config.toml")); - expect(resolved.input.options.theme).toBe("terminal"); + expect(resolved.input.options.theme).toBe("github-dark-default"); }); test("command-specific config sections also apply to show mode", () => { @@ -1288,7 +1288,7 @@ describe("config resolution", () => { ]); }); - test("loadAppBootstrap exposes terminal when no theme is configured", async () => { + test("loadAppBootstrap exposes GitHub Dark Default when no theme is configured", async () => { const home = createTempDir("hunk-config-home-"); const repo = createTempDir("hunk-config-repo-"); createRepo(repo); @@ -1309,7 +1309,7 @@ describe("config resolution", () => { ); const bootstrap = await loadAppBootstrap(resolved.input); - expect(bootstrap.initialTheme).toBe("terminal"); + expect(bootstrap.initialTheme).toBe("github-dark-default"); }); }); diff --git a/packages/hunk/src/core/run/config.ts b/packages/hunk/src/core/run/config.ts index da48d95e4..ac23e2259 100644 --- a/packages/hunk/src/core/run/config.ts +++ b/packages/hunk/src/core/run/config.ts @@ -2,7 +2,6 @@ import fs from "node:fs"; import { dirname, join } from "node:path"; import { sanitizeTerminalLine } from "../../lib/terminalText"; import { BUNDLED_SHIKI_THEME_IDS, LEGACY_THEME_ID_ALIASES } from "../theme/catalog"; -import { TERMINAL_THEME_ID } from "../theme/terminalColors"; import { createInvalidThemeIdNotice, createThemeCollisionNotice, @@ -101,7 +100,7 @@ export interface PersistedViewPreferences { export const BUILT_IN_THEME_IDS = BUNDLED_SHIKI_THEME_IDS; // Widen the large literal tuple before formatting it, avoiding TypeScript's deep tuple inference. const BUILT_IN_THEME_IDS_FOR_MESSAGES: readonly string[] = BUILT_IN_THEME_IDS; -const DEFAULT_THEME_ID = TERMINAL_THEME_ID; +const DEFAULT_THEME_ID = "github-dark-default"; const DEFAULT_CUSTOM_THEME_BASE_ID = "github-dark-default"; const DEFAULT_VIEW_PREFERENCES: PersistedViewPreferences = { mode: "auto", diff --git a/packages/hunk/src/core/theme/terminalColors.test.ts b/packages/hunk/src/core/theme/terminalColors.test.ts new file mode 100644 index 000000000..725cf429e --- /dev/null +++ b/packages/hunk/src/core/theme/terminalColors.test.ts @@ -0,0 +1,10 @@ +import { expect, test } from "bun:test"; +import { themeFollowsTerminal } from "./terminalColors"; + +test("only explicit terminal and auto themes need terminal color detection", () => { + expect(themeFollowsTerminal(undefined)).toBe(false); + expect(themeFollowsTerminal("github-dark-default")).toBe(false); + expect(themeFollowsTerminal("github-light-default")).toBe(false); + expect(themeFollowsTerminal("terminal")).toBe(true); + expect(themeFollowsTerminal("auto")).toBe(true); +}); diff --git a/packages/hunk/src/core/theme/terminalColors.ts b/packages/hunk/src/core/theme/terminalColors.ts index 551692f67..c6084acc5 100644 --- a/packages/hunk/src/core/theme/terminalColors.ts +++ b/packages/hunk/src/core/theme/terminalColors.ts @@ -1,12 +1,12 @@ -/** Id of the built-in theme drawn from the terminal's own colors, and Hunk's default theme. */ +/** Id of the opt-in built-in theme drawn from the terminal's own colors. */ export const TERMINAL_THEME_ID = "terminal"; /** - * Return whether a requested theme id depends on what the terminal looks like: the default - * (no id), the terminal-colors theme itself, and `auto` light/dark selection. + * Return whether a requested theme id depends on what the terminal looks like: + * the terminal-colors theme itself and `auto` light/dark selection. */ export function themeFollowsTerminal(themeId: string | undefined) { - return themeId === undefined || themeId === TERMINAL_THEME_ID || themeId === "auto"; + return themeId === TERMINAL_THEME_ID || themeId === "auto"; } /** diff --git a/packages/hunk/src/ui/AppHost.keybindings.test.tsx b/packages/hunk/src/ui/AppHost.keybindings.test.tsx index 3cf8f4de6..cb0fd1bad 100644 --- a/packages/hunk/src/ui/AppHost.keybindings.test.tsx +++ b/packages/hunk/src/ui/AppHost.keybindings.test.tsx @@ -239,13 +239,13 @@ describe("user keybindings", () => { setup.mockInput.pressKey("n", { ctrl: true }); }); await flush(setup); - expect(setup.captureCharFrame()).toContain("› andromeeda"); + expect(setup.captureCharFrame()).toContain("› github-dark-dimmed"); await act(async () => { setup.mockInput.pressKey("p", { ctrl: true }); }); await flush(setup); - expect(setup.captureCharFrame()).toContain("› terminal"); + expect(setup.captureCharFrame()).toContain("› github-dark-default"); }); }); diff --git a/packages/hunk/src/ui/hooks/useThemeSelectorController.test.tsx b/packages/hunk/src/ui/hooks/useThemeSelectorController.test.tsx index 96f47f3bb..386370258 100644 --- a/packages/hunk/src/ui/hooks/useThemeSelectorController.test.tsx +++ b/packages/hunk/src/ui/hooks/useThemeSelectorController.test.tsx @@ -345,7 +345,7 @@ describe("useThemeSelectorController", () => { }), ); expect(harness.controller.themeId).toBe(alpha.id); - expect(harness.controller.baseTheme.id).toBe("terminal"); + expect(harness.controller.baseTheme.id).toBe("github-dark-default"); expect(harness.controller.themeSelectorSelectedIndex).toBeGreaterThanOrEqual(0); expect(harness.controller.themeSelectorSelectedIndex).toBeLessThan( harness.controller.themeSelectorItems.length, diff --git a/packages/hunk/src/ui/lib/ui-lib.test.ts b/packages/hunk/src/ui/lib/ui-lib.test.ts index 7617a62fd..72edc8491 100644 --- a/packages/hunk/src/ui/lib/ui-lib.test.ts +++ b/packages/hunk/src/ui/lib/ui-lib.test.ts @@ -673,7 +673,7 @@ describe("ui helpers", () => { ).toBe(16); }); - test("resolveTheme falls back to the terminal theme while exposing semantic syntax colors", () => { + test("resolveTheme falls back to GitHub themes while exposing semantic syntax colors", () => { const dracula = resolveTheme("dracula", null); const missingLight = resolveTheme("missing", "light"); const missingDark = resolveTheme("missing", "dark"); @@ -694,8 +694,8 @@ describe("ui helpers", () => { const missingCustom = resolveTheme("custom", null); expect(dracula.id).toBe("dracula"); - expect(missingLight).toMatchObject({ id: "terminal", appearance: "light" }); - expect(missingDark).toMatchObject({ id: "terminal", appearance: "dark" }); + expect(missingLight).toMatchObject({ id: "github-light-default", appearance: "light" }); + expect(missingDark).toMatchObject({ id: "github-dark-default", appearance: "dark" }); expect(autoLight.id).toBe("github-light-default"); expect(autoDark.id).toBe("github-dark-default"); expect(custom.id).toBe("custom"); @@ -703,7 +703,7 @@ describe("ui helpers", () => { expect(custom.appearance).toBe("light"); expect(custom.accent).toBe("#7755aa"); expect(custom.syntaxScopeOverrides).toEqual({ "keyword.control": "#123456" }); - expect(missingCustom.id).toBe("terminal"); + expect(missingCustom.id).toBe("github-dark-default"); expect(custom.syntaxColors.default).toBe("#1f2328"); }); }); diff --git a/packages/hunk/src/ui/session/HunkSessionHost.test.tsx b/packages/hunk/src/ui/session/HunkSessionHost.test.tsx index 0de7d44cf..b5e48ea09 100644 --- a/packages/hunk/src/ui/session/HunkSessionHost.test.tsx +++ b/packages/hunk/src/ui/session/HunkSessionHost.test.tsx @@ -309,12 +309,12 @@ test("shares committed themes across history and repeated review surfaces", asyn await act(async () => setup.mockInput.pressEnter()); await settle(setup); - // The default `terminal` theme leads the list, so one step down lands on the first bundled id. - expect(requests[0]?.themeId).toBe("andromeeda"); + // One step down from GitHub Dark Default selects the next bundled GitHub theme. + expect(requests[0]?.themeId).toBe("github-dark-dimmed"); await act(async () => setup.mockInput.typeText("t")); await setup.renderOnce(); - await previewTrailingCustomTheme(setup, "andromeeda"); + await previewTrailingCustomTheme(setup, "github-dark-dimmed"); expect(setup.captureCharFrame()).toContain("Review only"); expect(setup.captureCharFrame()).not.toContain("History only"); await act(async () => setup.mockInput.pressEnter()); @@ -727,7 +727,7 @@ test("blocks reopening until dirty-quit cancellation settles", async () => { expect(setup.captureCharFrame()).toContain("Theme selector"); await act(async () => setup.mockInput.pressArrow("down")); await setup.renderOnce(); - expect(setup.captureCharFrame()).toContain("› andromeeda"); + expect(setup.captureCharFrame()).toContain("› github-dark-dimmed"); await act(async () => setup.mockInput.pressEnter()); await setup.renderOnce(); await act(async () => setup.mockInput.pressEnter()); diff --git a/packages/hunk/src/ui/themes.test.ts b/packages/hunk/src/ui/themes.test.ts index 9cf2b4793..4ed8d8d6c 100644 --- a/packages/hunk/src/ui/themes.test.ts +++ b/packages/hunk/src/ui/themes.test.ts @@ -94,9 +94,12 @@ function themeContrastFailures( describe("themes", () => { afterEach(() => setDetectedTerminalColors(undefined)); - test("defaults to the terminal theme and auto chooses GitHub light/dark", () => { - expect(resolveTheme(undefined, null).id).toBe(TERMINAL_THEME_ID); - expect(resolveTheme("missing", null).id).toBe(TERMINAL_THEME_ID); + test("falls back to GitHub themes and auto chooses GitHub light/dark", () => { + expect(resolveTheme(undefined, null).id).toBe(DEFAULT_DARK_THEME_ID); + expect(resolveTheme("missing", null).id).toBe(DEFAULT_DARK_THEME_ID); + expect(resolveTheme(undefined, "light").id).toBe(DEFAULT_LIGHT_THEME_ID); + expect(resolveTheme("missing", "light").id).toBe(DEFAULT_LIGHT_THEME_ID); + expect(resolveTheme(DEFAULT_DARK_THEME_ID, "light").id).toBe(DEFAULT_DARK_THEME_ID); expect(resolveTheme("auto", "dark").id).toBe(DEFAULT_DARK_THEME_ID); expect(resolveTheme("auto", "light").id).toBe(DEFAULT_LIGHT_THEME_ID); }); @@ -136,7 +139,7 @@ describe("themes", () => { "entity.name.type": "#e0af68", }); // Repeated resolution returns the same object so theme-keyed memoization stays stable. - expect(resolveTheme(undefined, null)).toBe(theme); + expect(resolveTheme(TERMINAL_THEME_ID, "light")).toBe(theme); }); test("falls back to a stand-in ANSI palette when the terminal was never probed", () => { @@ -494,7 +497,7 @@ describe("themes", () => { ); expect(resolveTheme("sunset", null, customThemes).accent).toBe("#654321"); // Unknown ids stay on the built-in fallback instead of picking an unrelated custom theme. - expect(resolveTheme("missing", null, customThemes).id).toBe(TERMINAL_THEME_ID); + expect(resolveTheme("missing", null, customThemes).id).toBe(DEFAULT_DARK_THEME_ID); expect(resolveTheme("auto", "light", customThemes).id).toBe(DEFAULT_LIGHT_THEME_ID); }); diff --git a/packages/hunk/src/ui/themes.ts b/packages/hunk/src/ui/themes.ts index a2a7a9895..1d61a1304 100644 --- a/packages/hunk/src/ui/themes.ts +++ b/packages/hunk/src/ui/themes.ts @@ -512,13 +512,17 @@ export function availableThemes( * * Custom themes are matched before bundled ids so a custom theme that reuses a * deprecated built-in alias still resolves to what the user actually defined. No id, or - * an id nothing defines, resolves to the `terminal` theme. + * an id nothing defines, resolves to the GitHub theme for the current light/dark mode. */ export function resolveTheme( requested: string | undefined, themeMode: ThemeMode | null, customThemes: readonly NamedCustomThemeConfig[] = [], ) { + if (requested === TERMINAL_THEME_ID) { + return buildTerminalTheme(themeMode); + } + if (requested === "auto") { return autoTheme(themeMode); } @@ -533,7 +537,7 @@ export function resolveTheme( return exact; } - return buildTerminalTheme(themeMode); + return autoTheme(themeMode); } /** diff --git a/test/pty/chrome.test.ts b/test/pty/chrome.test.ts index a940acd9a..5c8394027 100644 --- a/test/pty/chrome.test.ts +++ b/test/pty/chrome.test.ts @@ -146,7 +146,7 @@ describe("PTY chrome", () => { await session.waitForText(/line60/, { timeout: 15_000 }); await harness.pressAndWaitForText(session, "t", /Theme selector/, { timeout: 5_000 }); - await harness.pressAndWaitForText(session, "down", /›\s+andromeeda/, { + await harness.pressAndWaitForText(session, "down", /›\s+github-dark-dimmed/, { timeout: 5_000, }); await harness.pressAndWaitForSnapshot( @@ -159,8 +159,8 @@ describe("PTY chrome", () => { const prompt = await harness.pressAndWaitForText(session, "q", /Save view preferences\?/, { timeout: 5_000, }); - expect(prompt).toContain('- theme = "terminal"'); - expect(prompt).toContain('+ theme = "andromeeda"'); + expect(prompt).toContain('- theme = "github-dark-default"'); + expect(prompt).toContain('+ theme = "github-dark-dimmed"'); expect(prompt).toContain("enter/s save"); await session.click(/enter\/s save/); @@ -173,7 +173,7 @@ describe("PTY chrome", () => { await new Promise((resolve) => setTimeout(resolve, 50)); } - expect(readFileSync(configPath, "utf8")).toContain('theme = "andromeeda"'); + expect(readFileSync(configPath, "utf8")).toContain('theme = "github-dark-dimmed"'); } finally { session.close(); rmSync(configHome, { recursive: true, force: true }); diff --git a/test/pty/log-integration.test.ts b/test/pty/log-integration.test.ts index cfd4adc37..3afc7efa7 100644 --- a/test/pty/log-integration.test.ts +++ b/test/pty/log-integration.test.ts @@ -442,14 +442,14 @@ describe("interactive hunk log", () => { const prompt = await harness.pressAndWaitForText(session, "q", /Save view preferences\?/, { timeout: 5_000, }); - expect(prompt).toContain('- theme = "terminal"'); - expect(prompt).toContain('+ theme = "andromeeda"'); + expect(prompt).toContain('- theme = "github-dark-default"'); + expect(prompt).toContain('+ theme = "github-dark-dimmed"'); await session.press("s"); const configPath = join(configHome, "hunk", "config.toml"); const deadline = Date.now() + 5_000; while (Date.now() < deadline && !existsSync(configPath)) await Bun.sleep(50); - expect(readFileSync(configPath, "utf8")).toContain('theme = "andromeeda"'); + expect(readFileSync(configPath, "utf8")).toContain('theme = "github-dark-dimmed"'); } finally { session.close(); } diff --git a/test/pty/terminal-theme-integration.test.ts b/test/pty/terminal-theme-integration.test.ts index c96399bb7..d3facf520 100644 --- a/test/pty/terminal-theme-integration.test.ts +++ b/test/pty/terminal-theme-integration.test.ts @@ -164,7 +164,16 @@ async function expectOnlyPaletteB(session: Session, needles: { keyword: string; async function launchTestDiffSession(terminal: ReturnType) { const fixture = harness.createAgentFilePair(); const session = await harness.launchHunk({ - args: ["diff", "--files", fixture.before, fixture.after, "--mode", "unified"], + args: [ + "diff", + "--files", + fixture.before, + fixture.after, + "--mode", + "unified", + "--theme", + "terminal", + ], cwd: fixture.dir, cols: 100, rows: 24, @@ -179,6 +188,29 @@ async function launchTestDiffSession(terminal: ReturnType { + test("uses GitHub Dark Default without an explicit theme even on a light terminal", async () => { + const fixture = harness.createAgentFilePair(); + const terminal = createTestTerminalResponder({ + ...PALETTE_A, + foreground: "#101010", + background: "#ffffff", + }); + const session = await harness.launchHunk({ + args: ["diff", "--files", fixture.before, fixture.after, "--mode", "unified"], + cwd: fixture.dir, + cols: 100, + rows: 24, + testTerminalResponder: terminal.respond, + }); + + try { + await session.waitForText("export const answer = 42;"); + await harness.pressAndWaitForText(session, "t", /›\s+github-dark-default/); + } finally { + session.close(); + } + }); + test("repaints from a fresh terminal palette after a dark-to-dark color-scheme notification", async () => { const terminal = createTestTerminalResponder(PALETTE_A); const session = await launchTestDiffSession(terminal); @@ -201,6 +233,8 @@ describe("PTY terminal theme", () => { "-", "--mode", "unified", + "--theme", + "terminal", ])}`, cwd: fixture.dir, cols: 100, diff --git a/website/src/content/docs/docs/configure/themes.md b/website/src/content/docs/docs/configure/themes.md index 4b85fb0d9..e1b40b6a6 100644 --- a/website/src/content/docs/docs/configure/themes.md +++ b/website/src/content/docs/docs/configure/themes.md @@ -3,7 +3,7 @@ title: Themes description: Choose a built-in Shiki-backed theme, detect terminal background, or define precise color overrides. --- -By default Hunk uses the `terminal` theme, which follows your terminal's own colors: at startup Hunk reads the terminal's foreground, background, and 16-color ANSI palette, and draws diffs, chrome, and syntax highlighting from them. Hunk keeps following the terminal while it runs: when the terminal reports a color-scheme change (mode 2031, sent by Ghostty, kitty, herdr, and others) or Hunk receives `SIGWINCH` (what tmux-aware theme tools send after rewriting a pane's colors), it probes the palette again and repaints, usually within a tenth of a second and for terminals that take up to a second and a half to answer, including when Hunk reads a piped patch. Terminals that do not answer palette queries get a standard xterm-style palette for their light or dark background. +By default Hunk uses `github-dark-default`. Set `theme = "terminal"` to follow your terminal's own colors: at startup Hunk reads the terminal's foreground, background, and 16-color ANSI palette, and draws diffs, chrome, and syntax highlighting from them. Hunk keeps following the terminal while it runs: when the terminal reports a color-scheme change (mode 2031, sent by Ghostty, kitty, herdr, and others) or Hunk receives `SIGWINCH` (what tmux-aware theme tools send after rewriting a pane's colors), it probes the palette again and repaints, usually within a tenth of a second and for terminals that take up to a second and a half to answer, including when Hunk reads a piped patch. Terminals that do not answer palette queries get a standard xterm-style palette for their light or dark background. Press `t` in Hunk or choose **View → Themes…** to preview and select a different theme. Persist it in TOML: diff --git a/website/src/content/docs/docs/help/troubleshooting.md b/website/src/content/docs/docs/help/troubleshooting.md index 35c3e859b..6de301585 100644 --- a/website/src/content/docs/docs/help/troubleshooting.md +++ b/website/src/content/docs/docs/help/troubleshooting.md @@ -34,7 +34,7 @@ If `hunk pager` or `hunk patch -` fails with this error even though you didn't p ## Theme detection looks wrong -Some terminals do not answer color queries. The default `terminal` theme then falls back to a standard xterm-style palette and `theme = "auto"` falls back to `github-dark-default`; choose a theme explicitly if needed. If you turned on `transparent_background`, turn it off (`--no-transparent-bg`) when terminal compositing makes contrast unpredictable. +Some terminals do not answer color queries. The opt-in `terminal` theme then falls back to a standard xterm-style palette and `theme = "auto"` falls back to `github-dark-default`; choose a theme explicitly if needed. If you turned on `transparent_background`, turn it off (`--no-transparent-bg`) when terminal compositing makes contrast unpredictable. ## Layout or text is hard to read diff --git a/website/src/content/docs/docs/reference/config.md b/website/src/content/docs/docs/reference/config.md index 574407fb5..3f1b7a869 100644 --- a/website/src/content/docs/docs/reference/config.md +++ b/website/src/content/docs/docs/reference/config.md @@ -143,7 +143,7 @@ Select the color theme. `terminal` follows the terminal palette; `auto` chooses - **Type:** string - **Accepted:** `terminal`, `auto`, a built-in theme id, or a custom theme id -- **Built-in default:** `terminal` +- **Built-in default:** `github-dark-default` #### `line_numbers` From 66f4a21871de49455ea0c03a8951fc65e13b7711 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Fri, 16 Oct 2026 14:17:19 -0400 Subject: [PATCH 2/3] test(theme): sync package docs and stabilize PTY assertions --- packages/hunk/README.md | 2 +- test/pty/comment-target.test.ts | 23 ++++++++++++++++++++++- test/pty/open-scroll-integration.test.ts | 5 +++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/hunk/README.md b/packages/hunk/README.md index 2a7004e88..1b4e7721a 100644 --- a/packages/hunk/README.md +++ b/packages/hunk/README.md @@ -177,7 +177,7 @@ and falling back to it if migration fails. Global extension paths are unchanged. For example: ```toml -theme = "terminal" +theme = "github-dark-default" mode = "auto" # auto, split, unified auto_split_min_width = 160 # minimum available diff columns for split in auto mode line_numbers = true diff --git a/test/pty/comment-target.test.ts b/test/pty/comment-target.test.ts index bc301f11a..0f8fdcb08 100644 --- a/test/pty/comment-target.test.ts +++ b/test/pty/comment-target.test.ts @@ -23,7 +23,18 @@ describe("PTY comment target", () => { // and the draft opens a line or two (or a whole gap) away from the clicked line. const fixture = harness.createMultiHunkFilePair(); const session = await harness.launchHunk({ - args: ["diff", "--files", fixture.before, fixture.after, "--mode", "split"], + // Fixed wheel steps make equal event counts a true viewport roundtrip; auto + // acceleration depends on cadence and can leave the viewport one row short. + args: [ + "diff", + "--files", + fixture.before, + fixture.after, + "--mode", + "split", + "--wheel-scroll-lines", + "1", + ], cols: 120, rows: 16, }); @@ -45,6 +56,7 @@ describe("PTY comment target", () => { ); const clickedPaint = rowCellBackgrounds(session, 6).slice(12, 40); expect(clickedPaint).not.toEqual(beforeClick); + const clickedText = await session.text({ immediate: true }); // Fidget: wheel down 3, then up 3 — net zero viewport movement. for (let i = 0; i < 3; i += 1) { @@ -59,6 +71,15 @@ describe("PTY comment target", () => { // The viewport is back where it started with no deliberate cursor move in // between. The highlight must still sit on line2 — and so must the draft. + // A quiet PTY can still precede the deferred viewport/cursor commit. Wait for the + // original viewport and its paint, rather than reading an intermediate scroll frame. + await harness.waitForSnapshot( + session, + (text) => + text === clickedText && + JSON.stringify(rowCellBackgrounds(session, 6).slice(12, 40)) === + JSON.stringify(clickedPaint), + ); expect(rowCellBackgrounds(session, 6).slice(12, 40)).toEqual(clickedPaint); await session.press("c"); diff --git a/test/pty/open-scroll-integration.test.ts b/test/pty/open-scroll-integration.test.ts index 801a3b71b..2e6cbcaca 100644 --- a/test/pty/open-scroll-integration.test.ts +++ b/test/pty/open-scroll-integration.test.ts @@ -114,6 +114,11 @@ test("watched source shrink clamps deep scrolling to the surviving end instead o await session.waitForText("replacement"); expect(firstOpenScrollTestLine(await session.text())).toBe(survivingLine); await session.press("w"); + // Watch publication and wrap layout can briefly clear the mounted rows before repainting. + await harness.waitForSnapshot( + session, + (text) => firstOpenScrollTestLine(text) === survivingLine, + ); expect(firstOpenScrollTestLine(await session.text())).toBe(survivingLine); await session.press("g"); await session.waitForText("LINE_001"); From 6013ad54db3098ed98a244fa869b16e7430b9b0d Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Fri, 16 Oct 2026 14:26:03 -0400 Subject: [PATCH 3/3] test(windows): await compiled highlighter fixture cleanup --- packages/hunk/src/lib/shikiWasm.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/hunk/src/lib/shikiWasm.test.ts b/packages/hunk/src/lib/shikiWasm.test.ts index 4f0640dcd..cd47e1e33 100644 --- a/packages/hunk/src/lib/shikiWasm.test.ts +++ b/packages/hunk/src/lib/shikiWasm.test.ts @@ -4,6 +4,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; import { REPO_ROOT } from "../../../../scripts/build/package-paths"; +import { removeTestDirectory } from "../../../../test/helpers/filesystem"; // Install the main-thread counter before Pierre evaluates its static imports. const highlightDecodeTestPreload = ` @@ -94,7 +95,7 @@ function runHighlightTestProcess(args: string[], cwd = REPO_ROOT) { return JSON.parse(result.stdout.toString()); } -test("Pierre avoids base64 decoding in source, workers, bundles, and binaries with identical tokens", () => { +test("Pierre avoids base64 decoding in source, workers, bundles, and binaries with identical tokens", async () => { const temporary = mkdtempSync(join(tmpdir(), "hunk-shiki-test-")); // Keep the compile entry under the repository so its package imports resolve normally. const sourceDir = mkdtempSync(join(REPO_ROOT, ".shiki-test-")); @@ -177,7 +178,7 @@ test("Pierre avoids base64 decoding in source, workers, bundles, and binaries wi expect(compiled.assets).toContain(466_610); } finally { rmSync(sourceDir, { recursive: true, force: true }); - // Windows can briefly retain the compiled executable's file lock after the child exits. - rmSync(temporary, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); + // Yield between retries so Windows can release the compiled child's executable handles. + await removeTestDirectory(temporary); } }, 60_000);