From c94c0ad5ea19b3151095d389bbebfc3412fc3378 Mon Sep 17 00:00:00 2001 From: PathGao Date: Fri, 31 Jul 2026 15:36:22 +0800 Subject: [PATCH] feat: persist preview width preference --- scripts/previewWidth.test.ts | 35 +++++++++++++++++++++++++++++++ src/lib/stores/settings.svelte.ts | 12 +++++++++++ src/lib/utils/previewWidth.ts | 10 +++++++++ 3 files changed, 57 insertions(+) create mode 100644 scripts/previewWidth.test.ts create mode 100644 src/lib/utils/previewWidth.ts diff --git a/scripts/previewWidth.test.ts b/scripts/previewWidth.test.ts new file mode 100644 index 0000000..0f035c4 --- /dev/null +++ b/scripts/previewWidth.test.ts @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import test from 'node:test'; + +import { + DEFAULT_PREVIEW_MAX_WIDTH, + MAX_PREVIEW_MAX_WIDTH, + MIN_PREVIEW_MAX_WIDTH, + normalizePreviewMaxWidth, +} from '../src/lib/utils/previewWidth.js'; + +const settingsSource = readFileSync(new URL('../src/lib/stores/settings.svelte.ts', import.meta.url), 'utf8'); + +test('preview width defaults and clamps persisted numeric values', () => { + assert.equal(DEFAULT_PREVIEW_MAX_WIDTH, 880); + assert.equal(normalizePreviewMaxWidth(null), DEFAULT_PREVIEW_MAX_WIDTH); + assert.equal(normalizePreviewMaxWidth('not-a-number'), DEFAULT_PREVIEW_MAX_WIDTH); + assert.equal(normalizePreviewMaxWidth('639'), MIN_PREVIEW_MAX_WIDTH); + assert.equal(normalizePreviewMaxWidth('1601'), MAX_PREVIEW_MAX_WIDTH); + assert.equal(normalizePreviewMaxWidth('921.8'), 922); +}); + +test('preview width accepts only finite numeric values', () => { + assert.equal(normalizePreviewMaxWidth(Number.NaN), DEFAULT_PREVIEW_MAX_WIDTH); + assert.equal(normalizePreviewMaxWidth(Number.POSITIVE_INFINITY), DEFAULT_PREVIEW_MAX_WIDTH); + assert.equal(normalizePreviewMaxWidth(1200), 1200); +}); + +test('settings load, persist, and reset the preview width through one normalizer', () => { + assert.match(settingsSource, /previewMaxWidth = \$state\(DEFAULT_PREVIEW_MAX_WIDTH\)/); + assert.match(settingsSource, /localStorage\.getItem\('preview\.maxWidth'\)/); + assert.match(settingsSource, /normalizePreviewMaxWidth\(savedPreviewMaxWidth\)/); + assert.match(settingsSource, /localStorage\.setItem\('preview\.maxWidth', String\(this\.previewMaxWidth\)\)/); + assert.match(settingsSource, /resetPreviewMaxWidth\(\)[\s\S]*this\.previewMaxWidth = DEFAULT_PREVIEW_MAX_WIDTH/); +}); diff --git a/src/lib/stores/settings.svelte.ts b/src/lib/stores/settings.svelte.ts index 30ca2f2..1196ee3 100644 --- a/src/lib/stores/settings.svelte.ts +++ b/src/lib/stores/settings.svelte.ts @@ -18,6 +18,10 @@ import { normalizeTitlebarToolbarPlacement, type TitlebarToolbarPlacement, } from '../utils/titlebarToolbar.js'; +import { + DEFAULT_PREVIEW_MAX_WIDTH, + normalizePreviewMaxWidth, +} from '../utils/previewWidth.js'; export type OSType = 'macos' | 'windows' | 'linux' | 'unknown'; export type LanguageCode = @@ -169,6 +173,7 @@ export class SettingsStore { newFileDefaultMode = $state(true); showRecentFiles = $state(true); editorMaxWidth = $state(80); + previewMaxWidth = $state(DEFAULT_PREVIEW_MAX_WIDTH); pinnedToc = $state(false); tocSide = $state<'left' | 'right'>('left'); tocWidth = $state(240); @@ -218,6 +223,7 @@ export class SettingsStore { const savedNewFileDefaultMode = localStorage.getItem('editor.newFileDefaultMode'); const savedShowRecentFiles = localStorage.getItem('editor.showRecentFiles'); const savedEditorMaxWidth = localStorage.getItem('editor.maxWidth'); + const savedPreviewMaxWidth = localStorage.getItem('preview.maxWidth'); const savedPinnedToc = localStorage.getItem('editor.pinnedToc'); const savedTocSide = localStorage.getItem('editor.tocSide'); const savedTocWidth = localStorage.getItem('editor.tocWidth'); @@ -287,6 +293,7 @@ export class SettingsStore { if (savedNewFileDefaultMode !== null) this.newFileDefaultMode = savedNewFileDefaultMode === 'true'; if (savedShowRecentFiles !== null) this.showRecentFiles = savedShowRecentFiles === 'true'; if (savedEditorMaxWidth !== null) this.editorMaxWidth = parseFontSize(savedEditorMaxWidth, 80, 20, 500); + this.previewMaxWidth = normalizePreviewMaxWidth(savedPreviewMaxWidth); if (savedPinnedToc !== null) this.pinnedToc = savedPinnedToc === 'true'; if (savedTocSide !== null) this.tocSide = savedTocSide as 'left' | 'right'; if (savedTocWidth !== null) this.tocWidth = parseFontSize(savedTocWidth, 240, 180, 420); @@ -361,6 +368,7 @@ export class SettingsStore { localStorage.setItem('editor.newFileDefaultMode', String(this.newFileDefaultMode)); localStorage.setItem('editor.showRecentFiles', String(this.showRecentFiles)); localStorage.setItem('editor.maxWidth', String(this.editorMaxWidth)); + localStorage.setItem('preview.maxWidth', String(this.previewMaxWidth)); localStorage.setItem('editor.pinnedToc', String(this.pinnedToc)); localStorage.setItem('editor.tocSide', this.tocSide); localStorage.setItem('editor.tocWidth', String(this.tocWidth)); @@ -583,6 +591,10 @@ export class SettingsStore { this.editorMaxWidth = 80; } + resetPreviewMaxWidth() { + this.previewMaxWidth = DEFAULT_PREVIEW_MAX_WIDTH; + } + async initOSType() { try { const osType = await invoke('get_os_type'); diff --git a/src/lib/utils/previewWidth.ts b/src/lib/utils/previewWidth.ts new file mode 100644 index 0000000..060157a --- /dev/null +++ b/src/lib/utils/previewWidth.ts @@ -0,0 +1,10 @@ +export const MIN_PREVIEW_MAX_WIDTH = 640; +export const MAX_PREVIEW_MAX_WIDTH = 1600; +export const DEFAULT_PREVIEW_MAX_WIDTH = 880; + +export function normalizePreviewMaxWidth(value: unknown): number { + if (value === null || value === '') return DEFAULT_PREVIEW_MAX_WIDTH; + const parsed = typeof value === 'number' ? value : Number(value); + if (!Number.isFinite(parsed)) return DEFAULT_PREVIEW_MAX_WIDTH; + return Math.min(MAX_PREVIEW_MAX_WIDTH, Math.max(MIN_PREVIEW_MAX_WIDTH, Math.round(parsed))); +}