Skip to content

feat: add a rendered preview mode toggled with Ctrl+/ - #45

Open
cristim wants to merge 2 commits into
omacom:masterfrom
cristim:feat/preview-mode
Open

feat: add a rendered preview mode toggled with Ctrl+/#45
cristim wants to merge 2 commits into
omacom:masterfrom
cristim:feat/preview-mode

Conversation

@cristim

@cristim cristim commented Aug 31, 2026

Copy link
Copy Markdown

Read this first: this reverses a deliberate decision

Preview mode was in the first commit and you removed it yourself in ad65d2f ("Replace preview with footer file controls", 23 Jul), along with regression tests asserting it stays gone:

QVERIFY(!window->findChild<QObject *>(QStringLiteral("renderedPreview")));
QVERIFY(!window->findChild<QObject *>(QStringLiteral("modeToggle")));

Those assertions pass on this branch, but only because I named things previewView and previewButton. That is a naming accident, not a design agreement. If the intent of that commit was "no preview in this app", this PR contradicts it and you should close it — no hard feelings, and I would rather say that here than have it discovered in review.

I am opening it because #3 is still open with people asking for the feature, and because the commit message gives no rationale, so I could not tell whether the removal was "not like this" or "not ever". If it was the former, this is a concrete proposal. If the latter, say the word and I will close it and add the guard for the new names myself.

What it does

Ctrl+/ (Typora's Source Code Mode key) or the eye button in the footer, beside Save and Open, swaps the source buffer for a rendered view. The original used Ctrl+E; I went with Ctrl+/ for Typora parity, but Ctrl+E is a one-line change if you prefer the old binding.

Source (default) Preview
source preview

How

The preview is a separate read-only TextEdit. It never calls attachDocument, so MarkdownHighlighter stays bound to the source editor alone and the editing path is untouched.

Rendering goes through Backend::renderPreview, which runs setMarkdown and then restores block spacing. Qt's Markdown reader packs every block flush against the next, so the blank lines that separate paragraphs in the source vanish from the render. The pass gives headings a leading gap, keeps list items tight, separates blockquotes (via QTextFormat::BlockQuoteLevel), and puts a blank line's worth beneath ordinary paragraphs — sized from the editor font so it tracks the desktop text scale.

Multiple consecutive blank lines collapse to one gap. That is deliberate: CommonMark treats them as equivalent, and the Ctrl+P path already renders them that way.

Verification

  • bin/test — 12 passed, 0 failed, including the two assertions above.
  • Built and exercised by hand: toggle both ways, edit in source and re-enter preview, links open externally, footer icon takes the theme accent while active.

Known gaps

  • Built and tested on macOS against Homebrew Qt 6.11.1, not on Omarchy. The D-Bus portal path is untouched, but I cannot claim a Linux run.
  • The original had separate scroll positions per mode and forced previewMode = false on Find/Replace. I did not restore either; both are worth adding if this direction is acceptable at all.

Restores a rendered view of the document alongside the in-place
highlighter, reachable with Ctrl+/ (Typora's Source Code Mode key) or
the eye button in the footer beside Save and Open.

The preview is a separate read-only TextEdit. It never calls
attachDocument, so the syntax highlighter stays bound to the source
editor alone and the editing path is untouched.

Rendering goes through Backend::renderPreview, which runs setMarkdown
and then restores block spacing. Qt's Markdown reader packs every block
flush against the next, so the blank lines that separate paragraphs in
the source disappear from the render; the pass gives headings a leading
gap, keeps list items tight, and puts a blank line's worth beneath
ordinary paragraphs, sized from the editor font so it tracks the
desktop text scale.
Three cases, driving the real code paths rather than test-only hooks:

- togglesPreviewFromFooterButtonAndBack asserts the default is source, that
  the footer button swaps which surface is visible, and that it swaps back.
- restoresBlockSpacingLostByTheMarkdownReader renders through
  attachDocument/renderPreview and checks headings lead their section, quotes
  get a leading gap, paragraphs get a trailing one, and list items stay
  tighter than paragraphs. Verified it fails when the spacing pass is removed.
- collapsesRunsOfBlankLinesToOneGap pins the CommonMark behaviour that a run
  of blank lines is one separator, so the render cannot start growing with the
  newline count.
@cristim

cristim commented Aug 31, 2026

Copy link
Copy Markdown
Author

Added test coverage (03ca13b) — bin/test now runs 15 passed, 0 failed, including the two assertions from ad65d2f.

Three cases, driving the real code paths rather than test-only hooks:

  • togglesPreviewFromFooterButtonAndBack — source is the default, the footer button swaps which surface is visible, and it swaps back.
  • restoresBlockSpacingLostByTheMarkdownReader — renders through attachDocument / renderPreview and checks headings lead their section, quotes get a leading gap, paragraphs get a trailing one, and list items stay tighter than paragraphs.
  • collapsesRunsOfBlankLinesToOneGap — pins the CommonMark behaviour that a run of blank lines is one separator, so the render can't start growing with the newline count.

I checked the middle one is a real regression test rather than one that passes either way: with the spacing pass removed it fails with 'headingTop > 0' returned FALSE (headings must lead their section), and passes with it restored.

In fairness, the blank-line test passes with or without the change — it characterises Qt's behaviour rather than mine, and is there to catch a future attempt to preserve blank-line runs.

The "before" screenshot above was wrong when first posted (I'd captured preview mode twice); it now shows actual source mode with the markers visible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant