From c3b4173e62b0b2afd7a2a6b07de0c8d682a0925d Mon Sep 17 00:00:00 2001 From: PathGao Date: Fri, 31 Jul 2026 15:46:23 +0800 Subject: [PATCH] feat(preview): add width adjustment shortcuts --- scripts/previewWidth.test.ts | 16 ++++++++++++++++ src/lib/MarkdownViewer.svelte | 16 +++++++++++++++- src/lib/utils/previewWidth.ts | 4 ++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/scripts/previewWidth.test.ts b/scripts/previewWidth.test.ts index 4d56c56..368fda0 100644 --- a/scripts/previewWidth.test.ts +++ b/scripts/previewWidth.test.ts @@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs'; import test from 'node:test'; import { + adjustPreviewMaxWidth, DEFAULT_PREVIEW_MAX_WIDTH, getPreviewContentWidth, MAX_PREVIEW_MAX_WIDTH, @@ -34,6 +35,12 @@ test('full mode has no configured content-width cap', () => { assert.equal(getPreviewContentWidth(1200, true), null); }); +test('preview width shortcut adjustments retain the configured bounds', () => { + assert.equal(adjustPreviewMaxWidth(DEFAULT_PREVIEW_MAX_WIDTH, 40), 920); + assert.equal(adjustPreviewMaxWidth(MIN_PREVIEW_MAX_WIDTH, -40), MIN_PREVIEW_MAX_WIDTH); + assert.equal(adjustPreviewMaxWidth(MAX_PREVIEW_MAX_WIDTH, 40), MAX_PREVIEW_MAX_WIDTH); +}); + 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'\)/); @@ -56,3 +63,12 @@ test('Settings exposes a bounded preview-width input and reset action', () => { assert.match(settingsComponentSource, /bind:value=\{settings\.previewMaxWidth\}/); assert.match(settingsComponentSource, /settings\.resetPreviewMaxWidth\(\)/); }); + +test('preview width keyboard shortcuts avoid editable controls and app modals', () => { + assert.match(viewerSource, /function canUsePreviewWidthShortcut\(target: EventTarget \| null, isSplit: boolean\)/); + assert.match(viewerSource, /showSettings \|\| modalState\.show \|\| promptModal\.show \|\| showHome/); + assert.match(viewerSource, /closest\('input, textarea, select, \[contenteditable="true"\], \[role="textbox"\]'\)/); + assert.match(viewerSource, /cmdOrCtrl && e\.altKey && !e\.shiftKey && \(code === 'BracketLeft' \|\| code === 'BracketRight'\)/); + assert.match(viewerSource, /isFullWidth = false/); + assert.match(viewerSource, /settings\.previewMaxWidth = adjustPreviewMaxWidth\(settings\.previewMaxWidth, code === 'BracketLeft' \? -40 : 40\)/); +}); diff --git a/src/lib/MarkdownViewer.svelte b/src/lib/MarkdownViewer.svelte index d7bd21c..8b76d3c 100644 --- a/src/lib/MarkdownViewer.svelte +++ b/src/lib/MarkdownViewer.svelte @@ -51,7 +51,7 @@ import { import HomePage from './components/HomePage.svelte'; import { tabManager } from './stores/tabs.svelte.js'; import { snapshotTab } from './utils/tabTransfer.js'; -import { getPreviewContentWidth } from './utils/previewWidth.js'; +import { adjustPreviewMaxWidth, getPreviewContentWidth } from './utils/previewWidth.js'; import { settings } from './stores/settings.svelte.js'; import { t } from './utils/i18n.js'; import { createWindowSession } from './sessions/windowSession.svelte.js'; @@ -2266,6 +2266,12 @@ import { createDocumentSession, type LoadMarkdownOptions } from './sessions/docu } } + function canUsePreviewWidthShortcut(target: EventTarget | null, isSplit: boolean) { + if (showSettings || modalState.show || promptModal.show || showHome || (isEditing && !isSplit)) return false; + const element = target instanceof Element ? target : null; + return !element?.closest('input, textarea, select, [contenteditable="true"], [role="textbox"]'); + } + function handleKeyDown(e: KeyboardEvent) { if (mode !== 'app') return; @@ -2281,6 +2287,14 @@ import { createDocumentSession, type LoadMarkdownOptions } from './sessions/docu const isSplit = tabManager.activeTab?.isSplit; + if (cmdOrCtrl && e.altKey && !e.shiftKey && (code === 'BracketLeft' || code === 'BracketRight')) { + if (!canUsePreviewWidthShortcut(e.target, !!isSplit)) return; + e.preventDefault(); + isFullWidth = false; + settings.previewMaxWidth = adjustPreviewMaxWidth(settings.previewMaxWidth, code === 'BracketLeft' ? -40 : 40); + return; + } + if (!cmdOrCtrl && !e.shiftKey && !e.altKey && code === 'F5') { e.preventDefault(); reloadFromDisk(); diff --git a/src/lib/utils/previewWidth.ts b/src/lib/utils/previewWidth.ts index 9184cbd..1a43c66 100644 --- a/src/lib/utils/previewWidth.ts +++ b/src/lib/utils/previewWidth.ts @@ -12,3 +12,7 @@ export function normalizePreviewMaxWidth(value: unknown): number { export function getPreviewContentWidth(value: unknown, isFullWidth: boolean): number | null { return isFullWidth ? null : normalizePreviewMaxWidth(value); } + +export function adjustPreviewMaxWidth(value: unknown, delta: number): number { + return normalizePreviewMaxWidth(normalizePreviewMaxWidth(value) + delta); +}