fix(reader): split large txt into chapters (章 as boundaries, 卷 as optgroup labels, pseudo-sections when unmarked) rendered one at a time so the browser stops freezing; decode via strict-UTF-8-then-GB18030 with UTF-16 BOM sniffing to kill mojibake; progress locator {ch,scrollFraction} with old global-fraction fallback

This commit is contained in:
2026-09-07 22:21:29 +08:00
parent 991de0ef26
commit f47f2a6199
6 changed files with 318 additions and 31 deletions
+23
View File
@@ -0,0 +1,23 @@
import { expect, it } from "vitest";
import { decodeText } from "../src/lib/encoding";
const u8 = (...rows: number[][]) => new Uint8Array(rows.flat());
const ab = (v: Uint8Array) => v.slice().buffer;
it("GBK 编码的中文回退 GB18030 正确解码", () => {
expect(decodeText(ab(u8([0xd6, 0xd0], [0xce, 0xc4])))).toBe("中文");
});
it("合法 UTF-8 不被 GBK 路径污染;UTF-8 BOM 被剥掉", () => {
expect(decodeText(ab(u8([0xe4, 0xb8, 0xad], [0xe6, 0x96, 0x87])))).toBe("中文");
expect(decodeText(ab(u8([0xef, 0xbb, 0xbf], [0xe4, 0xb8, 0xad])))).toBe("中");
});
it("UTF-16 靠 BOM 识别", () => {
expect(decodeText(ab(u8([0xff, 0xfe], [0x2d, 0x4e])))).toBe("中");
expect(decodeText(ab(u8([0xfe, 0xff], [0x4e, 0x2d])))).toBe("中");
});
it("纯 ASCII 原样通过", () => {
expect(decodeText(ab(u8([...new TextEncoder().encode("hello\nworld")])))).toBe("hello\nworld");
});