From f75b155fed8693ef80580d4ebd9e400f5f403826 Mon Sep 17 00:00:00 2001 From: PathGao Date: Fri, 7 Aug 2026 00:47:24 +0800 Subject: [PATCH 1/2] chore: delete the code nothing reaches, and narrow the exports nothing imports Four functions in MarkdownViewer.svelte have no caller. Three of them are stale copies of render steps that moved elsewhere and kept evolving there: processTaskItems and processBlockIds moved into markdown.ts in 09fa88f, and the ==highlight== rewrite moved into the Rust renderer, where it later grew code-span protection (#228, #371). Each migration re-pointed the call site and left the old body behind, so the copies have been sitting there since b46a283 looking like reusable helpers. getSplitTransition is plain dead. Also removed: 24 exports nothing outside their own file imports, the escapeHtmlText alias that only forwards to escapeHtml, an addFrontMatterList- Item wrapper with no production caller, the toPlainRecord guard that isFrontMatterMapping already makes unreachable, and FrontMatterField's editableValue, which computes the same string as displayValue in every branch with nothing to keep the two from drifting apart. No behaviour change. The front matter tag test now calls the plural helper. Co-Authored-By: Claude Opus 5 --- scripts/frontMatter.test.ts | 5 +- src/lib/MarkdownViewer.svelte | 137 +-------------------- src/lib/sessions/documentSession.svelte.ts | 2 +- src/lib/utils/editorToolbar.ts | 2 +- src/lib/utils/export.ts | 6 +- src/lib/utils/exportHtml.ts | 6 +- src/lib/utils/frontMatter.ts | 24 +--- src/lib/utils/openExportedFile.ts | 6 +- src/lib/utils/pasteContext.ts | 2 +- src/lib/utils/previewAnchor.ts | 10 +- src/lib/utils/scrollSync.ts | 2 +- src/lib/utils/shortcuts.ts | 12 +- src/lib/utils/tabFileActions.ts | 2 +- src/lib/utils/tabHistory.ts | 6 +- src/lib/utils/titlebarToolbar.ts | 6 +- 15 files changed, 33 insertions(+), 195 deletions(-) diff --git a/scripts/frontMatter.test.ts b/scripts/frontMatter.test.ts index 4e5cbc5f..0d5a4665 100644 --- a/scripts/frontMatter.test.ts +++ b/scripts/frontMatter.test.ts @@ -2,7 +2,6 @@ import assert from 'node:assert/strict'; import { test } from 'node:test'; import { - addFrontMatterListItem, addFrontMatterListItems, getMarkdownBodyWithoutFrontMatter, getFrontMatterListItems, @@ -104,8 +103,8 @@ test('getFrontMatterListItems returns string tag values for YAML lists', () => { test('front matter tag helpers add, edit, and remove tags predictably', () => { const original = ['logger', 'synlog']; - assert.deepEqual(addFrontMatterListItem(original, ' appconfig '), ['logger', 'synlog', 'appconfig']); - assert.deepEqual(addFrontMatterListItem(original, 'logger'), ['logger', 'synlog']); + assert.deepEqual(addFrontMatterListItems(original, [' appconfig ']), ['logger', 'synlog', 'appconfig']); + assert.deepEqual(addFrontMatterListItems(original, ['logger']), ['logger', 'synlog']); assert.deepEqual(addFrontMatterListItems(original, [' ', 'synlog', 'codesite, onoff']), ['logger', 'synlog', 'codesite', 'onoff']); assert.deepEqual(updateFrontMatterListItem(original, 1, ' syslog '), ['logger', 'syslog']); assert.deepEqual(updateFrontMatterListItem(original, 1, 'logger'), ['logger', 'synlog']); diff --git a/src/lib/MarkdownViewer.svelte b/src/lib/MarkdownViewer.svelte index e5038a51..3291915a 100644 --- a/src/lib/MarkdownViewer.svelte +++ b/src/lib/MarkdownViewer.svelte @@ -697,119 +697,6 @@ import { createDocumentSession, type LoadMarkdownOptions } from './sessions/docu } }); - function processHighlights(root: Element) { - const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { - acceptNode(node) { - let curr = node.parentElement; - while (curr && curr !== root) { - if (['CODE', 'PRE', 'SCRIPT', 'STYLE'].includes(curr.tagName)) return NodeFilter.FILTER_REJECT; - curr = curr.parentElement; - } - return NodeFilter.FILTER_ACCEPT; - }, - }); - - const toReplace: { node: Text; replaced: string }[] = []; - let node: Node | null; - while ((node = walker.nextNode())) { - const text = (node as Text).nodeValue || ''; - if (text.includes('==')) { - const replaced = text.replace(/==([^=\n]+)==/g, '$1'); - if (replaced !== text) toReplace.push({ node: node as Text, replaced }); - } - } - for (const { node, replaced } of toReplace) { - const span = root.ownerDocument!.createElement('span'); - span.innerHTML = replaced; - node.parentNode?.replaceChild(span, node); - } - } - - function processBlockIds(root: Element, doc: Document) { - // handle pre-emitted block-id spans from rust parser - for (const el of Array.from(root.querySelectorAll('.block-id, [data-block-id]'))) { - const rawId = el.getAttribute('data-block-id') || (el as HTMLElement).textContent?.replace(/^\^/, '').trim() || ''; - if (!rawId) continue; - const anchor = doc.createElement('a'); - anchor.id = rawId; - anchor.className = 'block-id-anchor'; - anchor.setAttribute('data-label', rawId); - anchor.setAttribute('aria-hidden', 'true'); - el.replaceWith(anchor); - } - - // scan text nodes for trailing ^id pattern (text ^blockid at end of block) - const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { - acceptNode(node) { - const parent = node.parentElement; - if (!parent) return NodeFilter.FILTER_REJECT; - if (['CODE', 'PRE', 'SCRIPT', 'STYLE', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(parent.tagName)) return NodeFilter.FILTER_REJECT; - return NodeFilter.FILTER_ACCEPT; - }, - }); - - const blockIdPattern = / \^([a-zA-Z0-9_-]+)\s*$/; - const nodes: { node: Text; id: string }[] = []; - let textNode: Node | null; - while ((textNode = walker.nextNode())) { - const text = (textNode as Text).nodeValue || ''; - const match = text.match(blockIdPattern); - if (match) nodes.push({ node: textNode as Text, id: match[1] }); - } - - for (const { node, id } of nodes) { - const text = node.nodeValue || ''; - const cleanText = text.replace(blockIdPattern, ''); - const anchor = doc.createElement('a'); - anchor.id = id; - anchor.className = 'block-id-anchor'; - anchor.setAttribute('data-label', id); - anchor.setAttribute('aria-hidden', 'true'); - const parent = node.parentNode; - if (parent) { - const textBefore = doc.createTextNode(cleanText); - parent.replaceChild(anchor, node); - parent.insertBefore(textBefore, anchor); - } - } - } - - function processTaskItems(root: Element) { - for (const input of Array.from(root.querySelectorAll('li input[type="checkbox"]'))) { - input.setAttribute('data-task-checkbox', ''); - input.removeAttribute('disabled'); - (input as HTMLInputElement).style.cursor = 'pointer'; - - const li = input.closest('li'); - if (!li) continue; - - // wrap bare text/inline nodes after checkbox in a span for CSS targeting - const nodes = Array.from(li.childNodes); - const inputIdx = nodes.indexOf(input); - const afterInput = nodes.slice(inputIdx + 1); - - // we loop until we hit a block child (like a nested UL) - const inlineNodes = []; - for (const n of afterInput) { - if (n.nodeType === 1 && ['P', 'DIV', 'UL', 'OL'].includes((n as Element).tagName)) break; - inlineNodes.push(n); - } - - if (inlineNodes.length > 0) { - const wrapper = root.ownerDocument!.createElement('span'); - wrapper.className = 'task-text'; - for (const n of inlineNodes) wrapper.appendChild(n); - - // insert the newly wrapped span after the checkbox - li.insertBefore(wrapper, afterInput[inlineNodes.length] || null); - } - - if ((input as HTMLInputElement).checked) { - li.classList.add('task-done'); - } - } - } - // The preview and the export run the same filter in opposite orders, on // purpose. The export sanitizes the renderer output first and processes // afterwards, because the bytes it writes are read by another program and @@ -2790,28 +2677,6 @@ import { createDocumentSession, type LoadMarkdownOptions } from './sessions/docu window.addEventListener('pointercancel', onUp); } - function getSplitTransition(node: Element, { isEditing, side }: { isEditing: boolean; side: 'left' | 'right' }) { - let shouldAnimate = false; - let x = 0; - - if (side === 'left') { - if (!isEditing) { - shouldAnimate = true; - x = -50; - } - } else { - if (isEditing) { - shouldAnimate = true; - x = 50; - } - } - - if (shouldAnimate) { - return fly(node, { x, duration: 250 }); - } - return { duration: 0 }; - } - onMount(() => { loadRecentFiles(); isDisposed = false; @@ -3452,7 +3317,7 @@ import { createDocumentSession, type LoadMarkdownOptions } from './sessions/docu handleFrontMatterEdit(field, (e.currentTarget as HTMLInputElement).value)} /> {/if} {:else} diff --git a/src/lib/sessions/documentSession.svelte.ts b/src/lib/sessions/documentSession.svelte.ts index 35c4ddd1..e7e588c7 100644 --- a/src/lib/sessions/documentSession.svelte.ts +++ b/src/lib/sessions/documentSession.svelte.ts @@ -32,7 +32,7 @@ export type LoadMarkdownOptions = { * `conflict` means the owning tab has unsaved edits, so the choice belongs * to the user rather than to a background reload. */ -export type ExternalChangeOutcome = +type ExternalChangeOutcome = | { action: 'ignore' } | { action: 'reload'; tabId: string; path: string } | { action: 'conflict'; tabId: string; path: string }; diff --git a/src/lib/utils/editorToolbar.ts b/src/lib/utils/editorToolbar.ts index 4d44e622..d98ac3eb 100644 --- a/src/lib/utils/editorToolbar.ts +++ b/src/lib/utils/editorToolbar.ts @@ -10,7 +10,7 @@ export type EditorToolbarTool = { group: EditorToolbarGroup; }; -export type EditorToolbarMove = { +type EditorToolbarMove = { fromIndex: number; toIndex: number; }; diff --git a/src/lib/utils/export.ts b/src/lib/utils/export.ts index beea4471..283fded8 100644 --- a/src/lib/utils/export.ts +++ b/src/lib/utils/export.ts @@ -3,7 +3,7 @@ import { invoke } from '@tauri-apps/api/core'; import { getMarkdownBodyWithoutFrontMatter, parseFrontMatter } from './frontMatter.js'; import { processMarkdownHtml } from './markdown.js'; import { - escapeHtmlText, + escapeHtml, renderStaticFrontMatterPanel, resolveExportImagePath, rewriteMarkdownHrefForExport, @@ -53,7 +53,7 @@ interface ExportContext { libraries: RichContentLibraries | null; } -export type ExportHtmlResult = { +type ExportHtmlResult = { path: string; embeddedImages: number; missingImages: number; @@ -176,7 +176,7 @@ export function buildExportDocument(input: ExportDocumentInput): string { -${escapeHtmlText(input.title || 'Export')} +${escapeHtml(input.title || 'Export')}