From 78a4ca39b37d2178e57e64801dbdba89f42955a4 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Sat, 6 Jun 2026 10:45:20 -0400 Subject: [PATCH 1/2] fix(ui): preserve CJK scroll alignment --- CHANGELOG.md | 1 + src/ui/lib/text.ts | 5 +++++ src/ui/lib/ui-lib.test.ts | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04e5bde8..bbc2bb67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable user-visible changes to Hunk are documented in this file. ### Fixed +- Preserved split diff alignment when horizontal scrolling starts inside a wide CJK or emoji character. - Made diff syntax highlighting follow the active theme: keyword, function, number, and variable token colors now resolve to the configured theme's palette (including custom themes) in both light and dark, instead of passing through Pierre's built-in syntax colors. - Expanded the diff window during rapid scrolling bursts so large reviews keep real rows mounted instead of falling back to blank placeholder regions. diff --git a/src/ui/lib/text.ts b/src/ui/lib/text.ts index a46b4504..c929f9c7 100644 --- a/src/ui/lib/text.ts +++ b/src/ui/lib/text.ts @@ -53,6 +53,11 @@ export function sliceTextByWidth(text: string, offset: number, width: number) { continue; } if (clusterStart < startOffset) { + const hiddenCellWidth = Math.min(clusterEnd, startOffset + maxWidth) - startOffset; + if (hiddenCellWidth > 0) { + visibleText += " ".repeat(hiddenCellWidth); + usedWidth += hiddenCellWidth; + } continue; } if (usedWidth + clusterWidth > maxWidth) { diff --git a/src/ui/lib/ui-lib.test.ts b/src/ui/lib/ui-lib.test.ts index ed872994..5698d5de 100644 --- a/src/ui/lib/ui-lib.test.ts +++ b/src/ui/lib/ui-lib.test.ts @@ -314,7 +314,7 @@ describe("ui helpers", () => { test("text helpers measure and slice wide characters by terminal cells", () => { expect(measureTextWidth("日本語")).toBe(6); expect(sliceTextByWidth("a日本b", 1, 4)).toEqual({ text: "日本", width: 4 }); - expect(sliceTextByWidth("a日本b", 2, 4)).toEqual({ text: "本b", width: 3 }); + expect(sliceTextByWidth("a日本b", 2, 4)).toEqual({ text: " 本b", width: 4 }); expect(fitText("日本語", 5)).toBe("日本."); expect(measureTextWidth(padText("日本", 6))).toBe(6); }); From d8221b3a3811fa01a308199030bb481312340edf Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Sat, 6 Jun 2026 17:12:11 -0400 Subject: [PATCH 2/2] test(ui): cover CJK scroll slice edges --- src/ui/lib/ui-lib.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ui/lib/ui-lib.test.ts b/src/ui/lib/ui-lib.test.ts index 5698d5de..5bea1f3b 100644 --- a/src/ui/lib/ui-lib.test.ts +++ b/src/ui/lib/ui-lib.test.ts @@ -315,6 +315,8 @@ describe("ui helpers", () => { expect(measureTextWidth("日本語")).toBe(6); expect(sliceTextByWidth("a日本b", 1, 4)).toEqual({ text: "日本", width: 4 }); expect(sliceTextByWidth("a日本b", 2, 4)).toEqual({ text: " 本b", width: 4 }); + expect(sliceTextByWidth("日本b", 3, 3)).toEqual({ text: " b", width: 2 }); + expect(sliceTextByWidth("日", 1, 1)).toEqual({ text: " ", width: 1 }); expect(fitText("日本語", 5)).toBe("日本."); expect(measureTextWidth(padText("日本", 6))).toBe(6); });