fix(find): reveal collapsed matches, and keep them out of the PDF - #377
Merged
Merged
Conversation
**Find counted matches nobody could reach.** The tree walker skipped only script, style, noscript and its own marks, so text inside a collapsed heading section or a collapsed callout stayed in the DOM and stayed countable — but both containers clip to zero height, so pressing Enter onto one of those matches scrolled nowhere and looked like the search was broken. Matching a match now reveals its folds. That is what the surrounding software does: Chrome reveals `hidden=until-found` content and closed `<details>` when find lands inside them, VS Code unfolds a region when navigating to a match, and Obsidian tracks "counted but never revealed" as a bug. Simply skipping folded text would have been the other consistent answer, but it makes folding silently remove text from search with no signal, which nothing does. The reveal goes through the viewer's own fold toggle rather than stripping the class. `collapsedHeaders` is the source of truth and is re-applied on every render, so a class removed here would come back on the next one, and the table of contents would disagree in the meantime. Dispatching the toggle's own click keeps the DOM and that Set in step. **Find highlights were printed into exported PDFs.** Nothing cleared them before printing and `print-color-adjust: exact` made sure they came through. They cannot go in the print block's `display: none` list — the mark wraps document text, so hiding it would delete the matched words from the page — so its background, shadow and colour are neutralised instead and the text survives. Highlights authored with `==…==` are untouched. **Images and diagrams taller than a page were clipped rather than scaled.** `break-inside: avoid` was set with no height cap, and a replaced element cannot fragment, so anything taller than the printable area simply lost its bottom. They now cap at 9in with `object-fit: contain`, which fits both Letter (9.5in printable) and A4 (10.19in). Mermaid containers also need `overflow: visible` in print, because their on-screen `overflow-x: auto` computes `overflow-y` to `auto` and would re-crop what the cap just fixed. The size cap is purely additive to #359, whose mermaid work is a light-theme re-render carrying deliberately zero mermaid print CSS. No existing declaration in the print block was changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Find counted matches nobody could reach
The tree walker skipped only
SCRIPT,STYLE,NOSCRIPTand its own marks. Text inside a collapsed heading section (.foldable-content-wrapper.is-collapsed,height: 0) or a collapsed callout (.markdown-alert-content.is-collapsed,grid-template-rows: 0fr) stays in the DOM, so it was counted — but both clip to zero height, so pressing Enter onto one of those matches scrolled nowhere. The count said 7, three of them were unreachable, and nothing said why.Landing on a match now reveals its folds.
Why reveal rather than skip
Both options restore the invariant "a counted match is reachable", so the reference frame decided it:
display:noneandhiddencontent is not findable, buthidden=until-foundand closed<details>are findable and auto-revealed. Thebeforematchevent exists precisely so a page can sync its own state before the browser reveals.Markpad's behaviour matched none of them. Skipping folded text would be internally consistent, but it makes folding silently remove text from search with no signal — a usability cliff nothing else has.
Why it goes through the fold toggle
collapsedHeadersin the viewer is the source of truth:processMarkdownHtmlre-appliesis-collapsedfrom it on every render, and the same Set feeds the table of contents. Stripping the class from the find bar would create a second source of truth that the next render silently reverts, with the ToC disagreeing in the meantime. Dispatching the toggle's own click routes through the existing handler so the DOM and that Set stay in step — andMarkdownViewer.svelteneeded no change.Find highlights were printed into exported PDFs
Nothing cleared them before printing, and
.markdown-bodysetsprint-color-adjust: exact, so the orange blocks came through.They cannot simply join the print block's
display: nonelist — thatmarkwraps document text, so hiding it would delete the matched words from the printed page. Its background, shadow, colour, radius and padding are neutralised instead, and the text survives. Highlights authored with==…==are untouched.!importantis required because the find bar's:global()rule has equal specificity and the order between a component stylesheet andstyles.cssis not guaranteed; a test locks the find bar's own rules!important-free so the two cannot start fighting.Images and diagrams taller than a page were clipped
break-inside: avoidwas set with no height cap. A replaced element cannot fragment, so anything taller than the printable area simply lost its bottom rather than scaling or moving to the next page.Capped at
9inwithobject-fit: contain—@page { margin: 0 }plus the body's0.75inpadding leaves 9.5in printable on Letter and 10.19in on A4, so 9in fits both. Absolute units deliberately, notvh: page-box resolution differs across the three platform webviews. Mermaid containers also needoverflow: visiblein print, because their on-screenoverflow-x: autocomputesoverflow-ytoautoand would re-crop what the cap just fixed.Not conflicting with #359
Checked before editing: #359's mermaid work is a light-theme re-render in
utils/mermaidPrint.ts, carrying deliberately zero mermaid print CSS — so a size cap is purely additive. Only new rules were appended; no existing declaration changed, and the paper layout, table word-breaking and alert/trbreak rules are byte-identical. The new tests additionally re-assert #359'sdoesNotMatch(/\.mermaid-diagram svg[^{]*\{[^}]*fill:/)and add astroke:counterpart, so a future size rule cannot smuggle paint rules back in.Validation
npm run check— 0 errors, 0 warningsnpm test— 223/223 (18 new); fix(print): keep PDF exports readable in all themes #359's regression net inissue261EditorPdf.test.tsstill greenBaseline counter-proof: 12 of 16 failed on unmodified source; the 4 that passed are deliberate guards (the find bar is already print-hidden, find-match is not in the
display:nonelist, #359's rules intact). After tightening the print-block extraction to brace-match the real@media printblock rather than slicing to EOF, the two print files were re-run on still-unmodified source: 9 of 12 failed. 18/18 after the fixes. Every property lookup uses a(?<![\w-])lookbehind sobox-shadowcannot be satisfied by-webkit-box-shadow.Adjacent gap, deliberately left
A document containing raw
<details>(comrak runsunsafe_ = true) renders a real disclosure whose closed children aredisplay: nonebut still in the DOM — the same "counted, unreachable" symptom, and Chrome auto-opens<details>on find. Closing it is roughly three lines, but it brings a<summary>-match edge case and was outside this scope, so it is flagged rather than folded in.