ZenNotes 2.38.0: paragraphs reflow, boards show what is in flight, tabs hand over focus
Hard-wrapped prose reflows with one key (
gqip, or Ctrl+Q / Alt+Q), the Status board gains an In progress column for started tasks, CJK input methods stop swallowing Vim normal mode, pictures survive the trip into edit mode, email HTML and Word exports, PDFs embed on the self-hosted web client, mermaid stops re-rendering on every keystroke, Backspace removes an empty Ctrl+K link scaffold whole, clicking a note tab lands you in the note, and the Publish icon only shows once a Cloud account is signed in.
✨ New
-
Reflow a hard-wrapped paragraph. (#676, reported by @rrpolanco) Text pasted from a terminal, an email, or a model reply arrives wrapped at 80 or so columns. 2.35.0 taught the preview to treat those newlines as soft breaks (#656), but the editor shows the file as written, so every source line spilled a word or two onto a second row. The editor cannot pretend the newlines are not there, so the fix is a command that removes them: Reflow Paragraph joins the hard-wrapped lines of the paragraph under the cursor into one line, which the editor then wraps to the pane exactly like the preview. With a selection it reflows every paragraph the selection touches. Paragraph boundaries come from the Markdown syntax tree, so headings, list and quote markers, tables, fenced and indented code,
$$math, and explicit line breaks (two trailing spaces, a backslash,<br>) are never touched, and the change is a normal edit, one undo away. It isAlt+Qon Windows and Linux (Emacs's fill-paragraph chord),Ctrl+Qon macOS, remappable aseditor.reflowParagraph, in the command palette, and in Vim mode it is thegqoperator:gqipreflows the paragraph,gqjtwo lines,Vgqa selection,gwkeeps the cursor on the same text. Note for Vim users: codemirror-vim shippedgqas a hard wrap attextwidth80, which produced precisely the shape this release fixes;gqnow means reflow. How to test locally: paste a few lines wrapped at 80 columns into a note, put the cursor in them, pressgqip(Vim) or Ctrl+Q / Alt+Q. Before: each source line breaks a word short of the pane edge. After: one paragraph that wraps with the pane;uor Cmd+Z brings the lines back. -
The Status board has an In progress column. (#677, requested by @Kreisleriana) Marking a task in progress changed its icon and left the card where it was. A board is about flow, so started tasks (
[/]) now sit in their own In progress column between Upcoming and Waiting, and a glance shows how much work is in flight. Dropping a card there (or sending it withShift+L) writes- [/]to the note; dropping it back on Today or Upcoming reopens it as- [ ]with that date;@waitingstill wins, so a blocked task reads as blocked and returns to In progress when the wait clears. Nowhere else changes:[/]stays an open task in the Tasks list, on the calendar, in the rollups, and on the home view. A Status board whose columns were reordered before 2.38 gets the new column slotted after Upcoming instead of at the end. How to test locally: Tasks → Kanban → Group by: Status with a- [/]task in any note. Before: the card sits in Today with a half-filled box. After: it sits in In progress; drag a Today card into that column and the note line becomes- [/]; drag it back and it becomes- [ ] … due:<today>.
🐛 Fixes
-
Backspace removes an empty Ctrl+K link scaffold whole. (#678, reported by @uNyanda) Ctrl+K with nothing selected inserts
[]()with the caret inside the parentheses. Backspace there was answered by the auto-pair rule, which deleted the()and stranded a[]. #468 already made Backspace inside an empty**|**remove the whole snippet; that rule now knows the link scaffold in both holes ([](|)and[|](), plus the![]()image forms from the slash commands), and it runs ahead of the auto-pair keymap so it wins the race. A link with text or a URL is untouched. How to test locally: in a note press Ctrl+K (Cmd+K on macOS) with no selection, then Backspace. Before:[]. After: nothing left. -
Clicking a note tab lands you in the note. (#679, reported by @uNyanda) With Note Tabs on, a mouse click on a tab parked DOM focus on the tab button: the next keystrokes went nowhere, a keyboard press drew a white focus ring around the tab title, and the editor only took input after a second click inside it. Leader hints and
Ctrl+w jalready landed in the editor; a tab click now does the same (focusEditorNormalMode, like a palette pick), a panel tab such as Tasks simply releases the button, and Alt+j (Focus pane down) from the tab strip drops into the note the wayCtrl+w jdid. How to test locally: Settings → Editor → Writing → Note Tabs on, open two notes, click the inactive tab with the mouse and type. Before: nothing reaches the note and the tab grows a white border. After: the text lands in the note; with Vim on you are in normal mode, sooopens a line. -
The Publish link icon only shows with a signed-in Cloud account. The chain icon next to the note title (and the "Publish note…" entry in the note's context menu) was always there, and without a ZenNotes Cloud account it opened a dialog that could only fail. Both now wait for a signed-in account, which the status bar's Connect gets you; a signed-in account with no linked vault still sees them, since publishing talks to the account, not to a synced vault. How to test locally: with the status bar reading "ZenNotes Cloud · Connect", open a note. Before: a link icon after the title. After: no icon and no Publish entry in the sidebar's note menu; sign in and both return.
-
Korean, Chinese, and Japanese input methods no longer swallow Vim normal mode. (#84 reported by @garmede, #464 reported by @tonghejiao) With a CJK input method active, a normal-mode key never reached Vim as a key: the IME started a composition, and what landed in the note was the composed syllable (
jtyped ㅓ), which codemirror-vim then tried to undo with an Escape. Motions became stray characters,iinserted anibefore entering insert mode, and text got corrupted. An IME only engages on an editable surface, so outside insert mode the editor now takes no text input at all: the content stays focused (it gets a tabindex), keys reach every keymap and Vim,x/dd/pstill edit, and insert or replace mode flip it back so composition works exactly as before. It is on by default (Settings → Vim → Keep the input method out of normal mode,[vim] block_ime_in_normal_modein config.toml) and skipped on touch devices, where hiding the keyboard would strand you. How to test locally: switch the OS input method to Korean or Chinese, open a note in Vim normal mode, pressj,k,i. Before: syllables appear in the note. After: the cursor moves,ienters insert mode, and typing composes normally. -
A Zettlr-style image with a title renders in edit mode. (#199, reported by @ryanthara) Zettlr writes
on every image, and edit mode read the title into the path, so the picture became an attachment chip while the preview drew it fine. Standalone image and PDF lines now drop a quoted or parenthesised title, and clicking such a link opens the path, not the path plus title. Paths written relative to the note (../../99_Bilder/x.png) resolve as before. How to test locally: puton its own line. Before: a chip reading "chart". After: the picture. -
Copy Note as HTML (for email) carries the pictures. (#628, reported by @vlcinsky) Every image degraded to
[alt text]and a![[picture.png]]embed, which is what the app writes when you paste one, arrived as literal text. The note's local images now travel as inlinedata:images (mail clients turn those into attached inline pictures), wikilink embeds are rewritten like any other image with their|320size honoured, remote images stay by URL, and an image the vault cannot serve, or one over roughly 8 MB, keeps the alt-text fallback. How to test locally: a note with![[chart.png|320]], Command palette → Copy Note as HTML (for email), paste into a mail draft. Before: the literal![[chart.png|320]]. After: the picture, 320 px wide. -
Export Note as Word embeds
![[wikilink]]pictures. (#629, reported by @vlcinsky) The Word export embedded standard markdown images but let![[picture.png]]through as text. Wikilink image embeds are now rewritten before the document is built, the|600and|600x400size hints size the picture inside the page column, and a bare file name is looked up under the vault's asset folders the way the editor does. Note embeds still export as text. How to test locally: a note with![[chart.png|320]], Command palette → Export Note as Word, open the file. Before:![[chart.png|320]]in the text. After: the picture at 320 px. -
Self-hosted web client shows PDF embeds. (#121, reported by @TomSchimana) The server sent
frame-ancestors 'none'on every response, including the PDF itself, which forbade even the app's own same-origin iframe, so each PDF embed rendered as a blocked frame. The policy now allows same-origin framing ('self') and still blocks other sites. How to test locally: self-hosted server, a note with![[deck.pdf]], open it in the browser. Before: an empty or blocked frame. After: the PDF. -
Typing next to a mermaid diagram in split view no longer lags. (#184, reported by @DaveDavenport) Every preview rebuild handed over fresh DOM, so each keystroke sent every diagram back through mermaid's parse and layout. The preview now takes its SVGs from the cache the editor's live widget already keeps (keyed by source and theme), so a rebuild is an
innerHTMLassignment and the two surfaces share one render per diagram. How to test locally: a note with a mermaid fence, split view, type in a paragraph below it. Before: visible stutter on each key, the diagram flickers. After: smooth, the diagram stays put.
🧰 For contributors
lib/cm-reflow.tsholdsreflowChanges(state, from, to, wholeParagraphs)and thereflowParagraphcommand; paragraphs areParagraphnodes of the Lezer Markdown tree, so anything the parser treats as a block is safe by construction.lib/cm-vim-reflow.tsdefines thezenReflowoperator and mapsgq/gwover codemirror-vim'shardWrap; every editor host registers it next toregisterHeadingMotion. Keymap ideditor.reflowParagraph(Alt+Q, macOSCtrl+Q); palette ideditor.reflow-paragraph.TaskMutationgainedset-in-progress, applied throughsetTaskInProgressAtIndex(inline) andstatus: in-progress(file tasks).TasksKanbanexportsstatusColumns,dropMutationsFor,completeStatusOrder, andIN_PROGRESS_COLUMN_ID; Today and Upcoming drops now also clear[/], and the Waiting drop deliberately does not.formatMarkerBackspaceTransactionchecks the empty link scaffold before the symmetric markers, and the #468 keymap moved ahead ofautoPairExtensioninsideappMarkdownSnippetExtension(same-precedence keymaps run in array order).- The tab button's
onClickawaitsfocusTabInPaneand then callsfocusEditorNormalMode({ attempts: 10, delayMs: 24 })for note tabs orblur()for virtual ones;focusPaneOrEdgePanelshort-circuitsfocusedPanel === 'tabs'+jtofocusPanel('editor'). isCloudAccountConnectedPhase(phase)inlib/cloud-auto-sync.tsis the one answer to "is a Cloud account signed in":PublishedNoteButtonrenders nothing (and skips the published-notes lookup) outside it, and the sidebar's note context menu builds its Publish entries only inside it.hidden(no cloud capability, status unknown) counts as signed out, so the web client is covered too.lib/cm-vim-ime-guard.ts:vimImeGuard(enabled)swapsEditorView.editablethrough a compartment onvim-mode-changeand on every update (Vim can be toggled at runtime), addingtabindex=0while non-editable so the content stays focusable; installed inEditorPane,FloatingNoteApp,QuickCaptureAppbehind thevimBlockImeInNormalModepref (portable,[vim] block_ime_in_normal_mode).isTouchPrimaryDevice()skips it on coarse pointers.@shared/embed-sizenow ownsparseEmbedSizeHint/splitEmbedLabel(re-exported fromexcalidraw-preview.ts) plusrewriteWikilinkImageEmbeds, used by both the email HTML renderer andnoteMarkdownToIRin the Word export; the docxImageResolvertakes the size hint, and the main-process resolver searchesassets/,attachements/,_assets/by basename.renderNoteEmailHtml(body, title, { images })acceptssrc → data URI;collectEmailImageRefslists what the caller has to read (readVaultAssetBase64).cm-live-preview.tsexportsparseStandaloneImageLine; the standalone image and PDF regexes accept a markdown title, andinternal-links.unwrapMdUrlstrips one.Preview.renderMermaidBlocksgoes throughrenderMermaidSvg/peekMermaidSvgwith the diagram theme key; the directmermaid.rendercall and the per-renderinitializeare gone.apps/serverCSP:frame-ancestors 'self', withcsp_test.goguarding it.- Both docs surfaces updated:
help.ts(new Reflow card, Kanban Status sentence, two shortcut rows) and the website blade +docs-sections.json(Kanban rules table row, editor card, shortcut rows, feature-index entry).
Local-first and keyboard-first, as always.