Skip to content

Releases: Razee4315/Paperling

Paperling v1.0.35

22 Jun 19:15

Choose a tag to compare

What's new in v1.0.35

Added

  • The Export and Settings dropdown menus are now fully keyboard-operable: focus
    moves into the menu on open, Arrow/Home/End move between items, and Escape
    closes and returns focus to the button.
  • The file explorer refreshes when the window regains focus and gained a manual
    refresh button, so its list no longer goes stale after files change on disk.

Changed

  • PDF export now saves directly. On Windows, "Export → PDF" asks where to
    save and writes the file straight away, instead of opening the system print
    dialog. The PDF keeps selectable text and working links.
  • The theme now matches your operating system's light/dark setting on first
    launch, and follows it until you pick a theme yourself.
  • Notifications stack instead of replacing one another, and error messages stay
    on screen longer than confirmations.
  • The Light theme has a softer, warmer tone for a bit more character.

Removed

  • The GitHub theme. (Light covers the same clean, bright look.)

Fixed

  • Find & Replace now shows "Invalid pattern" for an unparseable regex instead of
    a misleading "No results".
  • Cancelling the export save dialog no longer shows a false "Exported" message.
  • A persistent autosave failure keeps reminding you (throttled) instead of
    going quiet after the first warning.

Installation

  • Windows: .msi (recommended) or .exe (NSIS installer)
  • Linux: .deb or .AppImage
  • macOS: .dmg

See the CHANGELOG for the full history.

Paperling v1.0.34

22 Jun 17:58

Choose a tag to compare

What's new in v1.0.34

Changed

  • PDF export now saves directly. On Windows, "Export → PDF" asks where to
    save and writes the file straight away, instead of opening the system print
    dialog. The PDF keeps selectable text and working links.

Installation

  • Windows: .msi (recommended) or .exe (NSIS installer)
  • Linux: .deb or .AppImage
  • macOS: .dmg

See the CHANGELOG for the full history.

Paperling v1.0.33

18 Jun 18:04

Choose a tag to compare

What's new in v1.0.33

A new version of Paperling is available.

Installation

  • Windows: .msi (recommended) or .exe (NSIS installer)
  • Linux: .deb or .AppImage
  • macOS: .dmg

See the CHANGELOG for the full history.

Paperling v1.0.32

18 Jun 17:54

Choose a tag to compare

What's new in v1.0.32

A new version of Paperling is available.

Installation

  • Windows: .msi (recommended) or .exe (NSIS installer)
  • Linux: .deb or .AppImage
  • macOS: .dmg

See the CHANGELOG for the full history.

Paperling v1.0.31

18 Jun 17:27

Choose a tag to compare

What's new in v1.0.31

A new version of Paperling is available.

Installation

  • Windows: .msi (recommended) or .exe (NSIS installer)
  • Linux: .deb or .AppImage
  • macOS: .dmg

See the CHANGELOG for the full history.

Paperling v1.0.30

18 Jun 16:21

Choose a tag to compare

What's new in v1.0.30

Changed

  • The "What's new" update popup now shows a concise summary of just the latest
    release's changes, instead of the full changelog history.

Installation

  • Windows: .msi (recommended) or .exe (NSIS installer)
  • Linux: .deb or .AppImage
  • macOS: .dmg

See the CHANGELOG for the full history.

Paperling v1.0.29

18 Jun 16:00

Choose a tag to compare

What's new in v1.0.29

Added

  • Fullscreen mode (F11). Press F11 to toggle distraction-free fullscreen on
    Windows, Linux, and macOS. The title bar stays visible so there's always an
    obvious way back, and a one-time hint reminds you to press F11 to exit. Also
    available from the command palette.
  • Auto-updater. Paperling now checks GitHub Releases on launch and shows a
    popup when a newer version is available, with "Update now" (download with
    progress, install, restart), "Skip this version" (remembered per version),
    and "Later". Update packages are signed; the updater verifies the signature
    before installing.
  • AI on/off switch. A new "Enable AI" toggle in Settings → AI (on by
    default). Turning it off hides every AI surface: the title-bar AI button,
    the AI side panel, the formatting-toolbar sparkle, Alt+J, and the command
    palette entry.
  • Shimmery AI button. The title-bar AI button now carries the familiar
    sparkle icon with a subtle shimmer animation.
  • Visual table editor. A floating toolbar appears when the caret is inside a
    Markdown table, with buttons to insert or delete rows and columns, set
    per-column alignment, and tidy (re-align) the layout. Built on a pure,
    fully tested table model, so editing a table no longer means hand-aligning pipes.

Changed

  • Cleaner "What's new" in the update popup. The updater now shows the
    release's actual changes as a tidy, scrollable list instead of a block of raw
    text. Release notes are sourced automatically from this changelog at build
    time, and links open in your browser rather than inside the app.
  • Relicensed to Apache 2.0. MarkLite moved from the previous custom
    non-commercial terms to the Apache License 2.0: free for both personal and
    commercial use, with an explicit patent grant. See LICENSE and NOTICE.
  • Sharper positioning. The hero, README, and site now lead with what is
    genuinely different (no-setup single-file editing, a free bring-your-own-model
    AI that proposes edits as accept-or-reject diffs, live math and chemistry)
    instead of generic "minimal / lightweight" claims.

Fixed

  • List bullets and numbers now render in the preview. Tailwind v4's base
    reset had stripped the list markers, leaving lists indented but marker-less.
  • Ctrl+S (and Ctrl+O / N / E) work with CapsLock on. An unshifted Ctrl+S
    reports e.key === "S", which previously fell through and did nothing.
  • Heading anchor links give feedback. Clicking the link icon next to a
    heading now copies a section link and shows a checkmark, instead of appearing
    to do nothing when the heading is already at the top of the view.
  • Alt+J opens the AI side panel. With no text selected it now opens the
    docked AI panel (previously it only opened the inline selection bubble); with
    a selection it still opens selection-assist in place.

Fixed — Click after scroll lands on the right line

  • Scrolling and immediately clicking (or double-clicking) used to land
    the caret on a line one row off from where it visibly looked like
    the click was — typing then added text to the "wrong" line, which
    was confusing. The cause: the highlight overlay's scrollTop was
    synced to the textarea via a rAF loop, which catches up on the NEXT
    frame after the scroll. If you clicked inside that 16-ms window, the
    textarea had already placed the caret at the new position but the
    overlay was still painting the old one, so the visible glyph at the
    click coordinate didn't match the textarea's text-position mapping.
  • Replaced the rAF loop with a synchronous onScroll handler on the
    textarea. The overlay's scrollTop is now updated in the same turn
    as the scroll event, so the two layers stay in lockstep and clicks
    always land where the user expects. Also frees the main thread from
    a 60 Hz rAF callback that was firing whether the editor was idle or
    not.

Improved — Smaller per-keystroke recompute

  • updateCursorPosition ran twice per keystroke (selectionchange AND
    keyup both fire). The downstream setStates already bailed via
    Object.is, but the substring + split work itself ran twice. Now
    caches the last reported (start, end) range and short-circuits at
    the top when nothing's moved.

Improved — Cold start and runtime performance

  • Bundle main chunk: 1.08 MB → 282 kB (~74% smaller). Welcome screen no
    longer ships the markdown rendering pipeline, jspdf/html2canvas, the
    command palette, settings, stats dialog, file explorer, outline panel,
    shortcuts cheatsheet, or the unsaved-changes dialog. Each is its own
    chunk, fetched the moment its surface mounts. vite manualChunks
    groups React and the markdown stack into stable vendor chunks the
    WebView2 disk cache can hold across upgrades.
  • First file open: instant. A requestIdleCallback after the welcome
    screen settles starts the markdown chunk download in the background, so
    by the time the user opens a file the bundle is already in cache.
  • Typing path: less work per keystroke. Command-palette heading scan
    no longer runs on every typing pause — only while the palette is
    actually open. App's per-keystroke content.split("\n") for
    lineCount moved into MarkdownPreview where it's actually used (one
    fewer full-document scan per keystroke). StatusBar, TitleBar,
    MarkdownPreview, and CodeEditor wrapped in React.memo with stable
    callbacks so caret-only re-renders bail out of reconciliation.
  • Highlight cache: drop LRU thrash. The CodeEditor's per-line
    highlight cache used to do delete + set on every cached line per
    render to maintain LRU order — ~20 k Map mutations per keystroke on a
    10 k-line file. The pruning that actually mattered ("is this line
    still in the doc?") doesn't depend on order, so we just lookup-only on
    hit and let the rare hard-cap fallback evict FIFO.

Added — Chemistry notation in math

  • KaTeX now loads the mhchem contrib alongside the rest of the math
    bundle, so \ce{...} and \pu{...} render in the preview.
    This unlocks textbook-grade chemistry: balanced equations, ions,
    isotopes, oxidation states, arrows, and Kröger-Vink defect notation
    (e.g. $\ce{2 Fe^x_{Fe} + O^x_{O} -> 2 Fe'_{Fe} + V_{O}^{**} + 1/2 O2 ^}$).
    The math-detection regex now also picks up bare \ce{ / \pu{,
    so chemistry-only documents trigger the lazy load even without
    $ delimiters. New /chem slash command inserts a starter snippet.

Improved — Book-style math typography

  • Display equations are centered with proper vertical breathing room and
    scroll horizontally on narrow viewports instead of overflowing the
    reading column. KaTeX glyphs no longer inherit the global code border
    /background, and equation tags pick up the muted-text colour for a
    printed-textbook feel.

Fixed — Caret drifts off the rendered glyph after scrolling

  • The CodeEditor stacks a transparent <textarea> on top of a styled
    highlight overlay; alignment relies on both layers wrapping at the
    same column. Once the document grew past the viewport the textarea
    sprouted a vertical scrollbar that quietly ate ~10 px of its content
    area, while the overlay kept its full width. With word-wrap on, the
    two layers wrapped at different columns and the caret started landing
    a character or two off the rendered text after every scroll. Both
    layers now reserve a fixed scrollbar gutter (scrollbar-gutter: stable),
    and the overlay's own scrollbar is hidden visually so only the
    textarea's remains user-facing.

Improved — Editor performance on large documents

  • Typing into a 5 k+ line markdown file used to feel "sticky" because
    the highlight overlay mounted every line as a <div> and React's
    reconciler walked the lot on every keystroke. The overlay now
    virtualizes: only the lines visible in the viewport (plus a 40-line
    buffer) render, with fixed-height spacers above and below preserving
    scroll-height parity with the textarea so caret alignment is intact.
    Re-renders only fire when the visible window shifts by more than half
    the buffer, so smooth scrolling no longer thrashes setState.
    Word-wrap mode and small docs (under 400 lines) keep the previous
    full-render path. Per-line wrapper styles are also hoisted to module
    scope and the non-virtualized list is wrapped in React.memo so
    unchanged lines short-circuit prop diffing.

Fixed — Webview accelerator collision

  • AI assist shortcut now also responds to Alt+J. On Windows, WebView2
    treats Ctrl+J as a "browser accelerator" for the built-in Downloads
    UI — the page never sees the keydown, so e.preventDefault() can't
    rescue it. Alt+J skips that path entirely. macOS (WKWebView) and Linux
    (WebKitGTK) keep Ctrl+J working; the cheatsheet detects the platform
    and shows the right one. A capture-phase keydown listener also
    preventDefaults Ctrl+J app-wide, so on platforms where the page does
    see the event the host webview's default action is suppressed
    regardless of which element is focused.

Fixed — Security

  • read_file / save_file refuse documents above 50 MB. Stat happens before
    the read so an oversized file fails fast with a clear "File too large"
    toast instead of pulling hundreds of MB of UTF-8 into the webview while
    the UI freezes.
  • save_image refuses payloads above 25 MB and now enforces an extension
    whitelist (png, jpg, jpeg, gif, webp, bmp, svg,
    case-insensitive). A caller can no longer drop a .exe / .dll / .lnk
    into the user's documents folder under cover of the markdown image-paste
    flow. Tests cover the new rejections plus all whitelisted extensions.
  • AI requests get a 60 s wall-clock timeout (composed with the user's
    existing AbortController) and the response is capped at 200 KB. A
    stuck local llama.cpp or a runaway model can no longer hang the AI
    bubble forever or paste megabytes of text into the editor.
  • Tauri CSP fur...
Read more

Paperling v1.0.28

16 Jun 16:43
f978971

Choose a tag to compare

What's new in v1.0.28

Added

  • Fullscreen mode (F11). Press F11 to toggle distraction-free fullscreen on
    Windows, Linux, and macOS. The title bar stays visible so there's always an
    obvious way back, and a one-time hint reminds you to press F11 to exit. Also
    available from the command palette.
  • Auto-updater. Paperling now checks GitHub Releases on launch and shows a
    popup when a newer version is available, with "Update now" (download with
    progress, install, restart), "Skip this version" (remembered per version),
    and "Later". Update packages are signed; the updater verifies the signature
    before installing.
  • AI on/off switch. A new "Enable AI" toggle in Settings → AI (on by
    default). Turning it off hides every AI surface: the title-bar AI button,
    the AI side panel, the formatting-toolbar sparkle, Alt+J, and the command
    palette entry.
  • Shimmery AI button. The title-bar AI button now carries the familiar
    sparkle icon with a subtle shimmer animation.
  • Visual table editor. A floating toolbar appears when the caret is inside a
    Markdown table, with buttons to insert or delete rows and columns, set
    per-column alignment, and tidy (re-align) the layout. Built on a pure,
    fully tested table model, so editing a table no longer means hand-aligning pipes.

Changed

  • Cleaner "What's new" in the update popup. The updater now shows the
    release's actual changes as a tidy, scrollable list instead of a block of raw
    text. Release notes are sourced automatically from this changelog at build
    time, and links open in your browser rather than inside the app.
  • Relicensed to Apache 2.0. MarkLite moved from the previous custom
    non-commercial terms to the Apache License 2.0: free for both personal and
    commercial use, with an explicit patent grant. See LICENSE and NOTICE.
  • Sharper positioning. The hero, README, and site now lead with what is
    genuinely different (no-setup single-file editing, a free bring-your-own-model
    AI that proposes edits as accept-or-reject diffs, live math and chemistry)
    instead of generic "minimal / lightweight" claims.

Fixed

  • List bullets and numbers now render in the preview. Tailwind v4's base
    reset had stripped the list markers, leaving lists indented but marker-less.
  • Ctrl+S (and Ctrl+O / N / E) work with CapsLock on. An unshifted Ctrl+S
    reports e.key === "S", which previously fell through and did nothing.
  • Heading anchor links give feedback. Clicking the link icon next to a
    heading now copies a section link and shows a checkmark, instead of appearing
    to do nothing when the heading is already at the top of the view.
  • Alt+J opens the AI side panel. With no text selected it now opens the
    docked AI panel (previously it only opened the inline selection bubble); with
    a selection it still opens selection-assist in place.

Fixed — Click after scroll lands on the right line

  • Scrolling and immediately clicking (or double-clicking) used to land
    the caret on a line one row off from where it visibly looked like
    the click was — typing then added text to the "wrong" line, which
    was confusing. The cause: the highlight overlay's scrollTop was
    synced to the textarea via a rAF loop, which catches up on the NEXT
    frame after the scroll. If you clicked inside that 16-ms window, the
    textarea had already placed the caret at the new position but the
    overlay was still painting the old one, so the visible glyph at the
    click coordinate didn't match the textarea's text-position mapping.
  • Replaced the rAF loop with a synchronous onScroll handler on the
    textarea. The overlay's scrollTop is now updated in the same turn
    as the scroll event, so the two layers stay in lockstep and clicks
    always land where the user expects. Also frees the main thread from
    a 60 Hz rAF callback that was firing whether the editor was idle or
    not.

Improved — Smaller per-keystroke recompute

  • updateCursorPosition ran twice per keystroke (selectionchange AND
    keyup both fire). The downstream setStates already bailed via
    Object.is, but the substring + split work itself ran twice. Now
    caches the last reported (start, end) range and short-circuits at
    the top when nothing's moved.

Improved — Cold start and runtime performance

  • Bundle main chunk: 1.08 MB → 282 kB (~74% smaller). Welcome screen no
    longer ships the markdown rendering pipeline, jspdf/html2canvas, the
    command palette, settings, stats dialog, file explorer, outline panel,
    shortcuts cheatsheet, or the unsaved-changes dialog. Each is its own
    chunk, fetched the moment its surface mounts. vite manualChunks
    groups React and the markdown stack into stable vendor chunks the
    WebView2 disk cache can hold across upgrades.
  • First file open: instant. A requestIdleCallback after the welcome
    screen settles starts the markdown chunk download in the background, so
    by the time the user opens a file the bundle is already in cache.
  • Typing path: less work per keystroke. Command-palette heading scan
    no longer runs on every typing pause — only while the palette is
    actually open. App's per-keystroke content.split("\n") for
    lineCount moved into MarkdownPreview where it's actually used (one
    fewer full-document scan per keystroke). StatusBar, TitleBar,
    MarkdownPreview, and CodeEditor wrapped in React.memo with stable
    callbacks so caret-only re-renders bail out of reconciliation.
  • Highlight cache: drop LRU thrash. The CodeEditor's per-line
    highlight cache used to do delete + set on every cached line per
    render to maintain LRU order — ~20 k Map mutations per keystroke on a
    10 k-line file. The pruning that actually mattered ("is this line
    still in the doc?") doesn't depend on order, so we just lookup-only on
    hit and let the rare hard-cap fallback evict FIFO.

Added — Chemistry notation in math

  • KaTeX now loads the mhchem contrib alongside the rest of the math
    bundle, so \ce{...} and \pu{...} render in the preview.
    This unlocks textbook-grade chemistry: balanced equations, ions,
    isotopes, oxidation states, arrows, and Kröger-Vink defect notation
    (e.g. $\ce{2 Fe^x_{Fe} + O^x_{O} -> 2 Fe'_{Fe} + V_{O}^{**} + 1/2 O2 ^}$).
    The math-detection regex now also picks up bare \ce{ / \pu{,
    so chemistry-only documents trigger the lazy load even without
    $ delimiters. New /chem slash command inserts a starter snippet.

Improved — Book-style math typography

  • Display equations are centered with proper vertical breathing room and
    scroll horizontally on narrow viewports instead of overflowing the
    reading column. KaTeX glyphs no longer inherit the global code border
    /background, and equation tags pick up the muted-text colour for a
    printed-textbook feel.

Fixed — Caret drifts off the rendered glyph after scrolling

  • The CodeEditor stacks a transparent <textarea> on top of a styled
    highlight overlay; alignment relies on both layers wrapping at the
    same column. Once the document grew past the viewport the textarea
    sprouted a vertical scrollbar that quietly ate ~10 px of its content
    area, while the overlay kept its full width. With word-wrap on, the
    two layers wrapped at different columns and the caret started landing
    a character or two off the rendered text after every scroll. Both
    layers now reserve a fixed scrollbar gutter (scrollbar-gutter: stable),
    and the overlay's own scrollbar is hidden visually so only the
    textarea's remains user-facing.

Improved — Editor performance on large documents

  • Typing into a 5 k+ line markdown file used to feel "sticky" because
    the highlight overlay mounted every line as a <div> and React's
    reconciler walked the lot on every keystroke. The overlay now
    virtualizes: only the lines visible in the viewport (plus a 40-line
    buffer) render, with fixed-height spacers above and below preserving
    scroll-height parity with the textarea so caret alignment is intact.
    Re-renders only fire when the visible window shifts by more than half
    the buffer, so smooth scrolling no longer thrashes setState.
    Word-wrap mode and small docs (under 400 lines) keep the previous
    full-render path. Per-line wrapper styles are also hoisted to module
    scope and the non-virtualized list is wrapped in React.memo so
    unchanged lines short-circuit prop diffing.

Fixed — Webview accelerator collision

  • AI assist shortcut now also responds to Alt+J. On Windows, WebView2
    treats Ctrl+J as a "browser accelerator" for the built-in Downloads
    UI — the page never sees the keydown, so e.preventDefault() can't
    rescue it. Alt+J skips that path entirely. macOS (WKWebView) and Linux
    (WebKitGTK) keep Ctrl+J working; the cheatsheet detects the platform
    and shows the right one. A capture-phase keydown listener also
    preventDefaults Ctrl+J app-wide, so on platforms where the page does
    see the event the host webview's default action is suppressed
    regardless of which element is focused.

Fixed — Security

  • read_file / save_file refuse documents above 50 MB. Stat happens before
    the read so an oversized file fails fast with a clear "File too large"
    toast instead of pulling hundreds of MB of UTF-8 into the webview while
    the UI freezes.
  • save_image refuses payloads above 25 MB and now enforces an extension
    whitelist (png, jpg, jpeg, gif, webp, bmp, svg,
    case-insensitive). A caller can no longer drop a .exe / .dll / .lnk
    into the user's documents folder under cover of the markdown image-paste
    flow. Tests cover the new rejections plus all whitelisted extensions.
  • AI requests get a 60 s wall-clock timeout (composed with the user's
    existing AbortController) and the response is capped at 200 KB. A
    stuck local llama.cpp or a runaway model can no longer hang the AI
    bubble forever or paste megabytes of text into the editor.
  • Tauri CSP fur...
Read more

Paperling v1.0.27

16 Jun 15:47
8eb98b7

Choose a tag to compare

What's new in v1.0.27

Added

  • Fullscreen mode (F11). Press F11 to toggle distraction-free fullscreen on
    Windows, Linux, and macOS. The title bar stays visible so there's always an
    obvious way back, and a one-time hint reminds you to press F11 to exit. Also
    available from the command palette.
  • Auto-updater. Paperling now checks GitHub Releases on launch and shows a
    popup when a newer version is available, with "Update now" (download with
    progress, install, restart), "Skip this version" (remembered per version),
    and "Later". Update packages are signed; the updater verifies the signature
    before installing.
  • AI on/off switch. A new "Enable AI" toggle in Settings → AI (on by
    default). Turning it off hides every AI surface: the title-bar AI button,
    the AI side panel, the formatting-toolbar sparkle, Alt+J, and the command
    palette entry.
  • Shimmery AI button. The title-bar AI button now carries the familiar
    sparkle icon with a subtle shimmer animation.
  • Visual table editor. A floating toolbar appears when the caret is inside a
    Markdown table, with buttons to insert or delete rows and columns, set
    per-column alignment, and tidy (re-align) the layout. Built on a pure,
    fully tested table model, so editing a table no longer means hand-aligning pipes.

Changed

  • Cleaner "What's new" in the update popup. The updater now shows the
    release's actual changes as a tidy, scrollable list instead of a block of raw
    text. Release notes are sourced automatically from this changelog at build
    time, and links open in your browser rather than inside the app.
  • Relicensed to Apache 2.0. MarkLite moved from the previous custom
    non-commercial terms to the Apache License 2.0: free for both personal and
    commercial use, with an explicit patent grant. See LICENSE and NOTICE.
  • Sharper positioning. The hero, README, and site now lead with what is
    genuinely different (no-setup single-file editing, a free bring-your-own-model
    AI that proposes edits as accept-or-reject diffs, live math and chemistry)
    instead of generic "minimal / lightweight" claims.

Fixed

  • List bullets and numbers now render in the preview. Tailwind v4's base
    reset had stripped the list markers, leaving lists indented but marker-less.
  • Ctrl+S (and Ctrl+O / N / E) work with CapsLock on. An unshifted Ctrl+S
    reports e.key === "S", which previously fell through and did nothing.
  • Heading anchor links give feedback. Clicking the link icon next to a
    heading now copies a section link and shows a checkmark, instead of appearing
    to do nothing when the heading is already at the top of the view.
  • Alt+J opens the AI side panel. With no text selected it now opens the
    docked AI panel (previously it only opened the inline selection bubble); with
    a selection it still opens selection-assist in place.

Fixed — Click after scroll lands on the right line

  • Scrolling and immediately clicking (or double-clicking) used to land
    the caret on a line one row off from where it visibly looked like
    the click was — typing then added text to the "wrong" line, which
    was confusing. The cause: the highlight overlay's scrollTop was
    synced to the textarea via a rAF loop, which catches up on the NEXT
    frame after the scroll. If you clicked inside that 16-ms window, the
    textarea had already placed the caret at the new position but the
    overlay was still painting the old one, so the visible glyph at the
    click coordinate didn't match the textarea's text-position mapping.
  • Replaced the rAF loop with a synchronous onScroll handler on the
    textarea. The overlay's scrollTop is now updated in the same turn
    as the scroll event, so the two layers stay in lockstep and clicks
    always land where the user expects. Also frees the main thread from
    a 60 Hz rAF callback that was firing whether the editor was idle or
    not.

Improved — Smaller per-keystroke recompute

  • updateCursorPosition ran twice per keystroke (selectionchange AND
    keyup both fire). The downstream setStates already bailed via
    Object.is, but the substring + split work itself ran twice. Now
    caches the last reported (start, end) range and short-circuits at
    the top when nothing's moved.

Improved — Cold start and runtime performance

  • Bundle main chunk: 1.08 MB → 282 kB (~74% smaller). Welcome screen no
    longer ships the markdown rendering pipeline, jspdf/html2canvas, the
    command palette, settings, stats dialog, file explorer, outline panel,
    shortcuts cheatsheet, or the unsaved-changes dialog. Each is its own
    chunk, fetched the moment its surface mounts. vite manualChunks
    groups React and the markdown stack into stable vendor chunks the
    WebView2 disk cache can hold across upgrades.
  • First file open: instant. A requestIdleCallback after the welcome
    screen settles starts the markdown chunk download in the background, so
    by the time the user opens a file the bundle is already in cache.
  • Typing path: less work per keystroke. Command-palette heading scan
    no longer runs on every typing pause — only while the palette is
    actually open. App's per-keystroke content.split("\n") for
    lineCount moved into MarkdownPreview where it's actually used (one
    fewer full-document scan per keystroke). StatusBar, TitleBar,
    MarkdownPreview, and CodeEditor wrapped in React.memo with stable
    callbacks so caret-only re-renders bail out of reconciliation.
  • Highlight cache: drop LRU thrash. The CodeEditor's per-line
    highlight cache used to do delete + set on every cached line per
    render to maintain LRU order — ~20 k Map mutations per keystroke on a
    10 k-line file. The pruning that actually mattered ("is this line
    still in the doc?") doesn't depend on order, so we just lookup-only on
    hit and let the rare hard-cap fallback evict FIFO.

Added — Chemistry notation in math

  • KaTeX now loads the mhchem contrib alongside the rest of the math
    bundle, so \ce{...} and \pu{...} render in the preview.
    This unlocks textbook-grade chemistry: balanced equations, ions,
    isotopes, oxidation states, arrows, and Kröger-Vink defect notation
    (e.g. $\ce{2 Fe^x_{Fe} + O^x_{O} -> 2 Fe'_{Fe} + V_{O}^{**} + 1/2 O2 ^}$).
    The math-detection regex now also picks up bare \ce{ / \pu{,
    so chemistry-only documents trigger the lazy load even without
    $ delimiters. New /chem slash command inserts a starter snippet.

Improved — Book-style math typography

  • Display equations are centered with proper vertical breathing room and
    scroll horizontally on narrow viewports instead of overflowing the
    reading column. KaTeX glyphs no longer inherit the global code border
    /background, and equation tags pick up the muted-text colour for a
    printed-textbook feel.

Fixed — Caret drifts off the rendered glyph after scrolling

  • The CodeEditor stacks a transparent <textarea> on top of a styled
    highlight overlay; alignment relies on both layers wrapping at the
    same column. Once the document grew past the viewport the textarea
    sprouted a vertical scrollbar that quietly ate ~10 px of its content
    area, while the overlay kept its full width. With word-wrap on, the
    two layers wrapped at different columns and the caret started landing
    a character or two off the rendered text after every scroll. Both
    layers now reserve a fixed scrollbar gutter (scrollbar-gutter: stable),
    and the overlay's own scrollbar is hidden visually so only the
    textarea's remains user-facing.

Improved — Editor performance on large documents

  • Typing into a 5 k+ line markdown file used to feel "sticky" because
    the highlight overlay mounted every line as a <div> and React's
    reconciler walked the lot on every keystroke. The overlay now
    virtualizes: only the lines visible in the viewport (plus a 40-line
    buffer) render, with fixed-height spacers above and below preserving
    scroll-height parity with the textarea so caret alignment is intact.
    Re-renders only fire when the visible window shifts by more than half
    the buffer, so smooth scrolling no longer thrashes setState.
    Word-wrap mode and small docs (under 400 lines) keep the previous
    full-render path. Per-line wrapper styles are also hoisted to module
    scope and the non-virtualized list is wrapped in React.memo so
    unchanged lines short-circuit prop diffing.

Fixed — Webview accelerator collision

  • AI assist shortcut now also responds to Alt+J. On Windows, WebView2
    treats Ctrl+J as a "browser accelerator" for the built-in Downloads
    UI — the page never sees the keydown, so e.preventDefault() can't
    rescue it. Alt+J skips that path entirely. macOS (WKWebView) and Linux
    (WebKitGTK) keep Ctrl+J working; the cheatsheet detects the platform
    and shows the right one. A capture-phase keydown listener also
    preventDefaults Ctrl+J app-wide, so on platforms where the page does
    see the event the host webview's default action is suppressed
    regardless of which element is focused.

Fixed — Security

  • read_file / save_file refuse documents above 50 MB. Stat happens before
    the read so an oversized file fails fast with a clear "File too large"
    toast instead of pulling hundreds of MB of UTF-8 into the webview while
    the UI freezes.
  • save_image refuses payloads above 25 MB and now enforces an extension
    whitelist (png, jpg, jpeg, gif, webp, bmp, svg,
    case-insensitive). A caller can no longer drop a .exe / .dll / .lnk
    into the user's documents folder under cover of the markdown image-paste
    flow. Tests cover the new rejections plus all whitelisted extensions.
  • AI requests get a 60 s wall-clock timeout (composed with the user's
    existing AbortController) and the response is capped at 200 KB. A
    stuck local llama.cpp or a runaway model can no longer hang the AI
    bubble forever or paste megabytes of text into the editor.
  • Tauri CSP fur...
Read more

Paperling v1.0.26

14 Jun 15:19
7857da1

Choose a tag to compare

Paperling v1.0.26

A minimal, distraction-free markdown editor.

Installation

  • Windows: .msi (recommended) or .exe (NSIS installer)
  • Linux: .deb or .AppImage
  • macOS: .dmg

See the CHANGELOG for details.