Skip to content

Editable rendered views, Open with, presentation mode - #60

Merged
mindaugaskasp merged 3 commits into
mainfrom
feat/editable-rendered-views
Aug 19, 2026
Merged

Editable rendered views, Open with, presentation mode#60
mindaugaskasp merged 3 commits into
mainfrom
feat/editable-rendered-views

Conversation

@mindaugaskasp

Copy link
Copy Markdown
Owner

What

Editable rendered views. The rendered Markdown and Jira views in the snippet editor are contenteditable WYSIWYG rather than one-way previews. Both parsers already emitted the same block tree, so the read-back is one shared reader (domToBlocks) and only the markers differ (markdownSerialize, jiraSerialize).

While typing, the DOM is the source of truth — the tree is re-parsed only when content changes from outside, or the caret jumps to offset 0 on every keystroke. Paste reads text/plain only: a native contenteditable paste inserts the clipboard's text/html, which is attacker-authored markup landing in the DOM behind Vue's back (rule 8). Task boxes tick where they are drawn.

Round trips normalise spelling, never structure (* item- item, bq.{quote}). Each loss is asserted individually so a silent widening fails the build.

Open with. DiffBro appears in Finder's and Explorer's Open-with lists for 12 text/data extensions, role: Viewer / rank: Alternate so it never becomes the default handler. First file opens a tab and takes the left pane, the next joins it on the right, the one after starts a new tab — derived from tab state, never counted, so an action between two opens cannot desync the cycle.

Presentation mode (F5). The comparison alone, full screen, Esc restores — including leaving a window that was already full screen alone.

Two launch-time bugs this exposed

  • The app refused to start. Two files selected at once parsed the second path as a verb; index.js reported "Unknown command" and exit(1) before any window existed.
  • It opened its own source. Unpackaged, argv[1] is the entry script, which was read as a document.

Both now have regression tests.

Smaller fixes

  • Capture is Mermaid-only (a picture of text is a worse copy of Copy)
  • Tab names take word characters only — */ was accepted and rendered
  • Collapsed rail no longer glues the key buttons to the window edge
  • The waiting screen can clear a wrong first file

Housekeeping

  • Decoding moved out of Electron-importing files.js into a pure, tested decodeText.js on TextDecoder, dropping iconv-lite. Verified byte-identical against iconv for the same detected label.
  • The 20-theme list is imported rather than written out three times
  • DEFAULT_SETTINGS, walkReset, currentLocale, isDiagramTheme deleted as dead
  • tabs.js beat its size ratchet; its exemption is deleted, not raised

Verification

  • npm run checkexit 0; coverage 95.30 / 88.38 / 95.83 / 96.36
  • Full e2e suite green (the two tag-shelf-resize failures were mine and are fixed — sidebar padding is the shelf grip's clearance, held at 10px)
  • make theme-sweep — 1580 measurements across all 20 themes, all passing. Worst new reading: sepia 3.93 against a 3.0 floor
  • Size ratchets fired four times and were resolved by extraction, never by raising a cap

Not verified

macOS open-file and the Windows default-handler behaviour need a real packaged install — LaunchServices can't be driven from a test.

🤖 Generated with Claude Code

mindaugaskasp and others added 3 commits August 19, 2026 15:24
Snippets: the rendered Markdown AND Jira views are now contenteditable
WYSIWYG rather than one-way previews. Both parsers already emitted the same
block tree, so the read-back is one shared reader (domToBlocks) and only the
markers differ (markdownSerialize, jiraSerialize). While typing the DOM is the
source of truth; the tree is re-parsed only on an external change, or the caret
jumps to offset 0 on every keystroke. Paste reads text/plain ONLY — a native
paste inserts the clipboard's text/html, which is markup landing in the DOM
behind Vue's back (rule 8). Task boxes tick where they are drawn.

Open with: DiffBro appears in Finder's and Explorer's Open-with lists for 12
text and data extensions, role Viewer so it never becomes the default handler.
First file opens a tab and takes the left pane, the next joins it on the right,
the one after starts a new tab — derived from tab state, never counted.

Fixes two launch-time bugs this exposed:
- Two files selected at once parsed the second path as a verb, so index.js
  reported "Unknown command" and exit(1) BEFORE any window existed.
- Unpackaged, argv[1] is the entry SCRIPT, which was opened as a document.

Presentation mode (F5): the comparison alone, full screen, Esc restores —
including leaving a window that was already full screen alone.

Also: capture is Mermaid-only; tab names take word characters only; the
collapsed rail no longer glues the key buttons to the window edge; the waiting
screen can clear a wrong first file.

Encoding: decoding moved out of the Electron-importing files.js into a pure,
tested decodeText.js on TextDecoder, dropping the iconv-lite dependency.
Verified byte-identical against iconv for the same detected label.

Housekeeping: the 20-theme list is imported rather than written out three
times; DEFAULT_SETTINGS, walkReset, currentLocale and isDiagramTheme deleted as
dead; tabs.js beat its size ratchet and its exemption is gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Retargeting this test to the seeded Mermaid example made the assertion
marginal: that diagram nearly fills the pane, so "cropped to the snippet" and
"run to the bottom of the column" stopped being distinguishable. CI measured
581 against a 579.75 ceiling; macOS passed only by luck of the pane height.

A two-node diagram leaves the headroom the assertion needs. The ceiling is
unchanged — loosening it would have removed what the test is for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`movable: false` meant the OS refused the move whatever the CSS said, so the
flag and the drag region had to change together.

`.drag-band` sits beside `.band` in ui.css and is worn by the search strip, the
compose head and the footer. Its blanket `no-drag` on interactive descendants is
the load-bearing half: without it every control inside a drag band renders
normally and ignores the pointer, which reads as a dead UI rather than a
draggable one. The test pins a minimum uninterrupted grab width so a future
control cannot quietly eat the handle.

Translucency was measured and REFUSED. Compositing --bg-panel over black, white
and mid-grey desktops and re-running the depth pairs: at 0.90 alpha — the
mildest level where the effect reads at all — 9 of 20 themes drop below a floor
(dark, solar, neon, nord, dim, linen, bloom, ember on border/panel; sepia on
dim/panel). The soft-keyline palettes collapse; contrast and beacon survive
precisely because their keylines are hard.

The stronger reason is that both guards would go BLIND rather than red:
check-theme-depth and theme-sweep resolve --bg-panel as an opaque token, so a
translucent card would keep scoring its opaque number while the reader looked at
something else. Losing enforcement is worse than losing the effect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mindaugaskasp
mindaugaskasp merged commit 58b0259 into main Aug 19, 2026
6 checks passed
@mindaugaskasp
mindaugaskasp deleted the feat/editable-rendered-views branch August 19, 2026 17:55
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