Skriuw

local-first

FeaturesLocal-firstMarkdownImportDocsDownload
Open the app
Skriuw

A local-first workspace for writing, journaling, and connected knowledge.

Product

  • Open the app
  • Download
  • Features
  • Changelog

Engineering

  • All documentation
  • Architecture
  • Performance contract
  • Decision records
  • Benchmarks

Project

  • Source on GitHub
  • Contributing
  • Security
  • License

skriuw · Frisian · “to write” · © 2026 Remco Stoeten · MIT

Source

06changelog

Changelog

Releases, improvements, and the changes behind them, straight from GitHub.

v2-v0.49.02026-10-03

Skriuw v2-v0.49.0

Highlights

Due dates and recurring tasks (#499, #507). Type due: followed by a date and a space on a checklist line to give it a due date: due:2026-10-12, due:today, due:fri, due:in_3_days. Type every:week (or every:daily, every:weekday, every:2_weeks, every:month) to make it repeat. Checking off a repeating item keeps it done and adds the next one right below it, and one undo reverts both.

  • Dates and rules show as chips after the line. Enter or a click turns a chip back into editable text, Delete clears it.
  • The Tasks view now groups by Overdue, Today, Upcoming, No date and Completed earlier, names each task's source note, and has a date button per row (D on a focused row).
  • Markdown uses the Obsidian Tasks signifiers, so vaults stay portable: - [ ] Water plants 🔁 every week 📅 2026-10-01. Details in ADR-0054.

Math (#503). Inline and block math rendered with KaTeX. Use /math or /inline, mod+alt+e for inline and mod+alt+shift+e for a block, or type $$ on its own line and press Enter. Markdown uses $...$ and $$ fences, like Obsidian, GitHub and Pandoc. KaTeX loads only when a note contains math, and $ still opens the people menu. Formulas are searchable.

Focus mode and typewriter scrolling (#504). mod+shift+f hides the rail, sidebar, inspector, toolbar and tabs and leaves just the note. Exit with the same keys or Escape. Optional dimming of everything but the current paragraph is under Settings, Editor, Focus. mod+shift+y keeps the caret centred while you type, in both the rendered and the raw Markdown editor.

Unlinked mentions (#500). The Relationship Explorer lists other notes that mention the current note's title as plain text. Open one to land on the mention, press L to turn it into a note link, or use Link all. Undo restores every note in one step.

Daily word goal (#501). Set a journal word goal from the ring in the day header or under Settings, Editor, Journal. The header shows progress, days that met the goal are marked in the calendar, and Stats gains Goal days and Goal streak. Changing the goal never rewrites past days.

Account

  • Signed-in accounts show a Facehash avatar, stable per account on every device (#505). Settings, Account & sync, Avatar switches back to initials.
  • Signing in keeps you on the page you were on (#478).
  • Signing out leaves the account's workspace in both the browser and the desktop app (#480). On desktop the app restarts into a workspace no account owns; the account's notes stay on disk and return on the next sign-in.

Fixes

  • Locking a note or folder now closes it immediately. Before, it stayed readable until the idle timer or "Lock notes now" (#508).
  • Desktop: a saved window size larger than the screen is fitted back to the monitor on launch. An oversized window could push the editor off screen and leave the main section looking empty (#511).
  • Desktop: the sync push thread no longer panics with "Could not automatically determine the process-level CryptoProvider". Live sync updates no longer fall back to polling because of it (#511).
  • A task's checkbox and its text stay on one line (#494).
  • A task whose title ends in formatted text, such as bold, keeps its task link after reopening the note (#499).

Site

  • The header shows the signed-in user's menu (#479) and floats on scroll (#482).
  • The download button picks the visitor's operating system (#483).
  • New hero preview (#477) and better changelog contrast (#481).

Under the hood

  • The workspace app is restructured into layered modules with enforced public APIs (#510, ADR-0055). Command ids, shortcuts and hints are unchanged. With an empty query, the palette's Actions group is now ordered by module.
  • The search index rebuilds once after updating, so math can be searched.
  • The sync service's test config typechecks in its own package (#506).

Full changelog: https://github.com/remcostoeten/skriuw/compare/v2-v0.48.0...v2-v0.49.0

v2-v0.48.02026-09-30

Skriuw v2-v0.48.0

Highlights

Open Markdown files from your file manager (#502). Skriuw is now registered for .md, .markdown and .mdx. Double-clicking a file, dropping it on the app, or opening it from another program imports it as a new root note and opens it.

  • Opening the same file again goes to the note it already created.
  • The metadata panel shows the file's original path, and the sidebar row shows its extension after the title.
  • Images referenced next to the file are picked up with it.
  • .mdx is converted to Markdown: import and export lines and JSX comments are dropped, component tags are removed and their contents kept.

Whole-note AI edits (#502). "Custom instruction on note" in the AI menu runs a free-form prompt over the entire note. The result is reviewed as a line diff before anything is written. The AI run card now stays on screen while you scroll, and the AI menu keeps itself inside the visible editor.

Remote images on import (#502). Imports that reference https images can download them once into the workspace. A new "Remote images in imports" setting (ask, download, block) controls it, with a prompt the first time. Details in ADR-0051.

Shortcuts and palette

  • Quit can be switched off or rebound to a chord that includes Ctrl or Cmd. Quit wins any clash, and the losing binding disappears from the palette, help and tooltips.
  • The command palette has a row density setting under Appearance.
  • Arrow keys wrap around at the ends of lists.

Journal

  • Right-click the sidebar calendar to open the day under the pointer, today's entry, the current month or the journal view, or to collapse the calendar.

Import and images

  • SVG files are accepted as images when they contain no scripts, event handlers or external references.
  • A paragraph that holds only images, such as a README badge row, stays on one row.

Browser build and sign-in

  • Signed-out visitors see a dismissible "Saved in this browser only" strip with Get desktop and Sign in. The "This browser may delete your workspace" toast is gone.
  • The sign-in reminder now appears after a stretch of navigation or after five minutes, whichever comes first, and waits for a pause in typing.
  • A sign-in that used to reload the app mid-way, before sync connected, no longer does.

Removed

  • The first-run onboarding screen. New workspaces open straight into the editor.

Site

  • Every page on skriuw.com uses the framed homepage design (#490).
v2-v0.47.02026-09-27

Skriuw v2-v0.47.0

Highlights

Share a note as a public link (#496). Pick "Share note as a public link…" in the command palette, or "Share as link…" (S) in a note's context menu, to publish it as a read-only page at skriuw.com/s/<id>/.

  • Each link is either a frozen snapshot you refresh with "Update shared copy", or follows your edits a few seconds after you stop typing.
  • "Stop sharing" removes the page on its next load. Publishing the same note again keeps its link.
  • Sharing needs a Skriuw account. It does not turn on sync.
  • Shared text is stored on Skriuw's server without end-to-end encryption, even when your sync is encrypted. The dialog says so before the first upload.
  • Images and drawings are left out, and locked notes can't be shared.
  • Details in ADR-0052.

Package channels moved

Self-hosted channels now live in the skriuw organization (#489):

  • Scoop: scoop bucket add skriuw https://github.com/skriuw/scoop-bucket
  • apt and dnf: https://skriuw.github.io/packages/. If you added the old remcostoeten.github.io/skriuw repository, switch to the new URL. The install commands are on skriuw.com/download.
  • Homebrew, AUR, winget and Snap are unchanged.

Other changes

  • The reduce-motion setting, which no longer had any effect, is removed. Workspaces that still store it open unchanged (#492).
  • skriuw.com is now a Next.js site with a changelog page, a nested feature outline in the docs, and condensed AI docs (#475, #476, #488).
  • Contributor tooling: scripts moved to tools/scripts/, a component storybook, and EAS builds of the Rust core for the mobile app (#475, #488, #493).
v2-v0.46.12026-09-25

Skriuw v2-v0.46.1

Changes since v2-v0.46.0

  • chore(v2): release v2-v0.46.1
  • fix(sync): keep sync always on while signed in (#474)
  • fix(lock): relock when the tab is hidden and after a frozen idle timer (#471)
  • fix(media): write PWA media without createWritable on older iOS Safari (#473)
  • chore(v2): one Vitest runner with a root tests mirror (#470)
  • fix(sync): accept current archive versions in cloud checkpoints (#469)
  • fix(media): make PWA media usable on iPhone (#468)
  • fix(sync): surface the service's rejection code instead of request_rejected (#467)
  • ci(v2): automate the release cut, notes and a native boot gate (#466)
  • ci(v2): build the browser WASM bindings before the release frontend build
  • fix(mobile): Handle browser freezing and back/forward cache in workspace tab lock (#464)
  • ci(v2): shard the gate into parallel jobs, release build on push only (#465)
  • feat(icons): one shared Fluent icon system with the approved animations (#438)
  • chore(v2): gate TypeScript with oxlint and oxfmt (#440)
  • chore(v2): reorganize the repository into apps, packages, and services (#439)
  • feat(mobile): bind note lock and credentials to the device keystore (#437)
  • fix(mobile): meet 44 pt targets and trap TalkBack in side sheets (#436)
  • feat(mobile): mount the editor in the notes column (#435)
  • chore(mobile): link the EAS project and pin the Metro web bundler (#434)
  • fix(web): tighten heading scale and use the serif face for section titles (#433)
  • feat(web): replace the static site with a Next.js marketing site (#428)
  • feat(theme): share one theme registry across desktop, web, and mobile (#429)
  • chore(mobile): build development profiles with expo-dev-client (#432)
  • feat(editor): Vim cursor style, blink, and action feedback (#430)
  • fix(app): sidebar focus, palette position, and two-line toasts (#431)
  • fix(app): pin React into one vendor chunk (#427)
  • feat(mobile): expose tasks and search surfaces in the app (#426)
  • Integration 01: run the tests the mobile gate was silently skipping (#425)
  • feat: route mobile workspace through the native core (#424)
  • feat(mobile): FTS bridge between rust and expo (#413)
  • Mobile 16: release readiness — performance evidence, accessibility pass, store metadata, docs (#415)
  • Mobile 15: Locked notes with biometric unlock (#414)
  • Mobile 12: journal, quick capture and the share target (#412)
  • Mobile 14: sync, sign-in and per-account workspaces (#409)
  • Mobile 13: tasks view with explicit promotion (#410)
  • Mobile 09: persistent editor host (DOM component) (#411)
  • Mobile 07: Native shell (router, toolbar, tab bar, sidebar sheet, themes) (#408)
  • Mobile 10: CI and builds (EAS profiles, Android e2e, macOS xcframework job) (#405)
  • Mobile 04: theme token generator (themes.css → React Native tokens) (#402)
  • Mobile 08: bridge adapter over the skriuw-core native module (#406)
  • fix(editor-standalone): import moved modules from renderer-core (#407)
  • Mobile 05: Editor bundle and host protocol (browser-verified) (#403)
  • Mobile 03: extract shared/renderer-core from app/src (#401)
  • Mobile 06: Expo native module skriuw-core (Android verified, iOS unverified) (#404)
  • Mobile 02: UniFFI facade crate over the shared core (#400)
  • Mobile 01: bun workspaces, the Expo scaffold and check-mobile.sh (#399)
  • chore(manifests): brew + scoop 0.46.0
  • fix(e2e): size the native window and document the keyring trap
v2-v0.46.02026-09-19

Skriuw v2-v0.46.0

Highlights

One local workspace per cloud account (#378, ADR-0046). Signing into a second account on the same install no longer dead-ends on "this workspace is linked to another cloud workspace". The first account to sign in keeps the notes you wrote before you had an account; another account opens a workspace of its own and the app restarts (desktop) or reloads (browser) onto it. Switching back is lossless. Account settings gain a "Notes on this device" row that says which account the open notes belong to, and a failed browser session resume now reports its real reason instead of a generic "session ended".

Skriuw on a phone behaves like an app (#380, #381, ADR-0047). The platform back gesture closes the sheet, dialog, or palette on top and otherwise leaves the app; tab bar taps never stack history. Settings fills the screen, every dialog stays above the keyboard and pulls down to close from its grabber or header, and toasts swipe away. When the browser can install Skriuw, a one-line strip above the tab bar says so — Install or Not now, remembered for that browser.

Journal quick capture and mood trend (#379). Installed browser builds get "New journal entry" and "New note" home-screen shortcuts and appear in the system share sheet: shared text or a link becomes a new note with the page title as its heading. The sidebar Stats tab shows the last 30 days of moods as a bar strip with a one-line trend ("Mostly good, lifting lately"). Touch targets across the toolbar, Search, toasts, and dialogs grow to 44px.

Settings sidebar regrouped (#377) into sections so the growing list of surfaces stays scannable.

Also

  • README describes sync as end-to-end encrypted (ADR-0043 shipped in 0.45.0).
  • The keyboard-workflow and account-switch browser e2e suites run from scripts/check-wasm.sh.

Upgrading

No migrations, no cloud Worker changes. Existing installs keep their workspace; it becomes the signed-in account's on the next sign-in.

v2-v0.45.02026-09-18

Skriuw v2-v0.45.0

Everything since v0.44.0. This release is about notes that stay private, an editor you can drive without leaving the home row, and a browser build you can install on a phone.

Private by construction

  • End-to-end encrypted sync (ADR-0043, #363). Note titles, bodies, tags, people, properties, tasks, media, and even the operation type are sealed on the device before push and opened after pull. The service orders and accounts for opaque bytes. One per-workspace key is derived from a recovery code that is shown once and never stored; a second device joins by typing the code alone.
  • Locked notes (ADR-0044, #373). Lock a note behind a workspace PIN or passphrase with a one-time recovery code. This is real encryption at rest: no snapshot, archive, search row, task, reference, or history capture carries a locked body, and locked notes replicate as ciphertext. Losing both the secret and the recovery code makes locked bodies unreadable for good, and the setup dialog says so. Migration 0027_note_lock.sql.

Modal Vim editing

One vimMode setting (mod+alt+i, or Toggle Vim mode in the palette) makes both editors modal (ADR-0042, #367).

  • The rendered editor gets a Vim layer built for the block document: normal, insert, visual, and visual-line modes, counts, operators with motions and text objects, f/t and / search, registers including the system clipboard, dot repeat, macros, and :w, :q, :N, :noh, :s.
  • The raw Markdown view moves to CodeMirror and runs its Vim extension with the same : commands. j/k move by wrapped screen rows.
  • Jump to line counts what you see (#372). "Line N of M", :N, and NG count wrapped rows in the rendered editor, so the same number lands on the same content in both editors. Very large, virtualized documents still count Markdown lines.

Diagrams

  • Rendered Mermaid fences (ADR-0045, #374). Sequence, state, class, and ER fences, plus flowcharts the editable block does not claim, render a themed read-only preview inside the code block, with a Preview/Source toggle (mod+alt+p), a full-viewport expand with pinch and pan, and a visible error line that keeps the last good render. /sequence, /state, /class, and /er insert a template. The fence stays the only stored form, and the renderer loads as a lazy chunk.
  • AI Diagram action (#375). Select text and have AI draw a flowchart, sequence, state, class, or ER diagram from it; Insert diagram places the fence below the selection. A reply that does not render is sent back once for repair. Every AI result now draws its Mermaid fences in the run card, and the card itself is restyled: state dot in the header, a filled primary action with key caps, quiet secondary actions.

Search

  • Full-text content search (ADR-0041, #362, closes #341). The SQLite FTS5 index now projects prose instead of raw Markdown, ranks title hits above body hits, highlights matches in the palette, detects when it has drifted from the workspace, and can rebuild itself in one transaction. Verified in the browser build too.

Phone and tablet

  • Installable browser build (ADR-0040, #366). /app/ is now an installable, offline-capable app. A new build is offered as a reload, never swapped under a running session, and persistent storage is requested once the workspace opens.
  • Compact touch shell (ADR-0041, #366, #368). Below 900px the app is one content column over a bottom tab bar; the tree and inspector become edge drawers that follow your finger 1:1 with rubber-band overscroll, settle on a flick, and close on a scrim tap or a note pick.

Journal

  • Start from a template (#376). An empty entry offers the built-in and personal templates; the pick is remembered, so the next empty day is a single press. Browsing days never creates an entry.
  • On this day (#376). Under the editor: entries from a week ago, a month ago, and the same date in earlier years, with mood and an excerpt.
  • Phone day view (#376). Previous/Next day buttons, a horizontal swipe across the heading to step a day, and the five moods on one row.
  • Faster navigation (#370). Step by week (shift+[/]), month (alt+[/]), and year (alt+shift+[/]), plus a Go to date dialog (d) that accepts input like next week thursday, 312, or dec 2025 and previews the resolved date before confirming. All rebindable.

History

  • Side-by-side diff (#371, closes #272). A Unified / Side by side toggle in the history view; the choice persists per workspace.
  • Revision scrubber (#368). Drag, pinch, scroll, or use the keys to move through a note's revisions.

Shell and polish

  • Tab overflow (#368). Tabs keep a readable width and the rest move behind an "N more" menu; pinned and active tabs always stay visible.
  • Splash and single instance (#368). A frameless splash shows until first paint, and launching Skriuw twice focuses the running window instead of starting a second process against the same database.
  • Cover gradients (#361). A note can carry a theme gradient instead of an image, without storing a blob. The media library gains per-asset reveal.
  • A keyboard and accessibility pass across menus, pickers, and panels (#368). Midnight's focus ring is visible again.

Fixes

  • The shortcut recorder never committed a new binding and stayed on "Press keys…" (#371, closes #262).
  • Brand icons resolve correctly, the command palette is roomier, and Empty trash confirms inline (#360).

Full Changelog: https://github.com/remcostoeten/skriuw/compare/v2-v0.44.0...v2-v0.45.0

v2-v0.44.02026-09-09

Skriuw v2-v0.44.0

Everything since v0.43.0. This release is about AI you can see and steer, notes you can reuse, and a browser build that can recover from a broken database instead of dead-ending.

AI runs are visible work

AI is no longer something that happens behind a modal (ADR-0040, #358).

  • A standing entry point. An Ask AI launcher sits in the editor whenever the AI opt-in gate is open. With nothing selected it opens on the caret, so whole-note actions (summarise, outline, suggest a title, extract tasks, suggest tags) no longer require selecting text they ignore.
  • One anchored, non-modal menu. The AI menu is a popover over the range it is about and swaps in place with the bubble and link menus. Actions that need nothing from you start on the click that chose them. The three that need an instruction (change tone, translate, custom) get a second pane in the same popover, with a live preview of the exact payload.
  • Runs report themselves. Every run shows three labelled stages (sending, waiting, generating), a skeleton where the text will land, elapsed time, and a stall note after six seconds naming which half is quiet. A failure marks the stage it failed in and names its one recovery move. Stop works in every in-flight phase and no longer waits on an acknowledgement a dead transport never sends.
  • One card for every outcome. The word diff for a replacement, the plan checklist for tasks and tags, the proposed line for a title. Only a replacement strikes its range through.
  • Fixed: the result card could dismiss itself the tick it opened, and a torn-down effect could leave a run holding a disposed stream.

Voice dictation

Speech-to-text behind the AI opt-in gate (ADR-0039, #339).

  • Record from the editor with a live level meter; the transcript is inserted as one undoable transaction.
  • Optionally reformat the transcript through the completion pipeline with two new built-in prompts, Clean transcript and Structure transcript (prompt library v3). A failed format falls back to the raw text.
  • Groq (Whisper) and Gemini transcribe behind the existing credential and consent gate. Audio bytes are staged over IPC and the provider call runs off the main thread.
  • Audio-only microphone access now works on Linux (WebKitGTK permission request answered).

Reuse your own notes

Personal templates and saved searches, both device-local and bounded (ADR-0038, #353).

  • Save note as template registers an ordinary note as a template source. Copies keep its properties, default to its folder, expand {{date}}, and get fresh task and block ids. The source stays editable; removing it from the picker never deletes it.
  • Saved searches live in the sidebar and re-evaluate live against current notes. Title text and tag: / person operators keep their meaning across restart, archive round-trips, and preference reset.

Editor

  • Compact selection toolbar (#359). Twenty-one controls become ten: highlight, alignment, and block type each collapse behind one trigger that shows the value in force. Submenus stay inside the window, flip above the toolbar when needed, and are fully keyboard-operable (ArrowDown opens, Escape/Tab return). Highlights can finally be removed with None instead of re-pressing the same colour. The Ask AI entry gets a liquid-metal treatment that respects prefers-reduced-motion and falls back to a plain button.
  • Block gutter is now a setting (#271, #356). Settings → Editor → Blocks toggles the drag handle live, without an editor remount. With it off, every gutter action stays reachable via Alt+Arrow, the slash menu, and the block context menu.
  • Raw Markdown view: wrapped lines get aligned line numbers and correct j/k movement.
  • Task lists: better checkbox accessibility, keyboard navigation, and focus retention.
  • The first # / @ keystroke on large workspaces is ~18 ms faster (collator warm-up moved out of the hot path).

Media

  • Named media with bulk delete (#289, #356). The settings media library gets an inline name and description per item, and multi-select delete for unused entries. The cover and media picker shows and searches the stored name. Names are keyed by content hash so they survive an image being detached and re-attached.
  • Migration 0024_media_metadata.sql. Forward-safe; blanking both fields deletes the record rather than storing an empty one.

Browser runtime recovers from a broken database

  • When the database cannot open (corrupt file, failed migration, open failure, worker crash) the startup screen now offers Reset workspace… behind a confirmation that names what is destroyed, instead of only Retry (#355). The reset cannot be started twice, and a storage code survives being wrapped in an error. Database-too-new and quota-exceeded stay out because both recover without erasing anything.

Reliability

  • Closing the window during an in-flight close attempt can no longer close a replacement session.
  • Filtered full-text search applies candidate ids before the limit, so filtered results are no longer truncated by the wrong set.
  • A stale editor save can no longer undo an accepted task toggle after restart (30-round regression test).
  • Sync refresh retries are bounded, support manual retry, and dispose safely (spec).

Under the hood

  • The AI stack now consumes the extracted ai-core / ai-providers SDK (git tag ai-v0.3.0), with transcription moved into it. Skriuw keeps the catalogue, consent vocabulary, prompt library, run history, Ollama lifecycle, and every command surface. The remote AI crate no longer depends on reqwest or base64 (#357).
  • The browser e2e and performance harnesses are hermetic again: a stubbed Tauri bridge and an offline cloud seam mean neither touches the network. A new headless-Chrome e2e proves the workspace-reset path against a real corrupted OPFS pool file.

Known gaps

  • Voice dictation, named media, and the block gutter setting were verified in the browser build and unit tests, not on a real device against a live provider.
  • Stored media alt text is not yet rendered by the editor's image node, and the lightbox does not show the name or description.

Full Changelog: https://github.com/remcostoeten/skriuw/compare/v2-v0.43.0...v2-v0.44.0

Full Changelog: https://github.com/remcostoeten/skriuw/compare/v2-v0.44.0...v2-v0.44.0

v2-v0.43.02026-09-05

Skriuw v0.43.0

Everything since v0.41.0, the last published release. v0.42.0 was tagged but never published, so its changes ship here too.

Sync converges by itself

Connected devices now reach the same workspace without anyone being asked to choose, and nothing anyone typed is discarded (ADR-0037).

  • No conflict dialogs. The conflict review surface, the conflict sync status, and the resolve-conflict command are gone. A note edited on two devices while both were offline is now a note, not a prompt.
  • Deterministic merge. A remote document write is decided against the greatest server sequence this device has already incorporated for that note, plus whether an unsequenced local write is still pending — never against the device-local revision counter or a base sequence the two devices can disagree about. Two devices reading the same log always make the same decision.
  • Losing bodies are kept. Note history gained provenance (local / remote / superseded); a body that lost a merge is written into that note's history as "Version from another device (superseded)" rather than dropped.
  • Export stopped failing closed. Portable export no longer refuses to run because of sync state elsewhere in the workspace.
  • Migration 0023_automatic_convergence.sql rebuilds history_outbox and sync_received_operations, drops the conflict tables, and adds dangling-reference tracking. Forward-safe; existing conflict rows migrate to superseded records carrying their reason.

Measured propagation floor with the network removed: p50 0.82 ms, p95 0.94 ms in release builds (benchmark).

Account and save reliability

  • A cloud session that dies is now propagated from the sync coordinator to every account surface, so the app stops offering a dead token.
  • Sign-in connects sync in the background; when that fails, the failure is now visible instead of leaving a workspace signed in and quietly not syncing.
  • Saves retry against the winning revision before surfacing an error, and a failed save offers an explicit retry rather than losing the attempt.
  • A commit gate serializes remote reconciliation against local commits without ever making an optimistic renderer update wait.
  • Sync flushes on visibility and connectivity changes instead of waiting out its poll interval.
  • Stale sync epochs are purged on connect, fixing outbox sequence collisions after a workspace was re-provisioned.
  • The cloud worker reads request bodies under a hard byte bound; an oversized or understated body is cut off instead of buffered.

Editing

  • Whole-note ink annotation layer (#336): pen, highlighter, line, rectangle, ellipse, eraser, and select/move over the entire note surface, versioned on the document root.
  • Inline comment threads now have hover previews, Tab-to-reply, and a fully keyboard-operable popover.
  • Version history diffs pair edited lines by similarity and can expand hidden context, so a reworded paragraph reads as an edit instead of a delete plus an insert (#350).
  • Revision stats on the history view; AI model lists are fetched from the provider instead of hardcoded.
  • Sidebar pinned chips (#352).
  • Checklist task promotion now happens in the same transaction as the save that triggers it.
  • Fixed: stacked code blocks had no gap between them.

Browser runtime

  • Multiple tabs of the same workspace are now allowed.
  • The last open note is restored across a browser reload.

Elsewhere

  • Canonical Skriuw landing page with social preview metadata (#337).
  • Animated icons resolve through one keyed module.

Addresses #343–#349.

v2-v0.41.02026-08-23

Skriuw v0.41.0

Highlights

  • Syntax highlighting in raw Markdown mode: block markers, headings, emphasis, inline and fenced code, links, [[wiki references]], #tags, $people, task checkboxes, front matter, and Skriuw marker comments each get their own colour, with a per-line gutter and active-line band that track the caret. Very large notes (over 200k characters) fall back to the plain textarea so typing stays fast.
  • Inline comment threads anchored to selected text, with replies, editing, resolve/reopen, keyboard navigation, and an inspector for open and resolved discussions. Comments persist and sync as first-class workspace data.
  • Optional in-app browser for links. Editor settings choose the default, while the link menu always offers both the Skriuw browser and the system browser.

Fixes

  • Focus the first control when a dialog opens: on Chromium 144+ and WebView2 the command palette opened with its query field unfocused because the scrollable dialog body won focus.
  • Restore production web authentication by allowing the Better Auth Sentinel request, visitor, and proof-of-work headers through the Cloudflare Worker CORS boundary.
  • Add the Skriuw favicon to the /app deployment and verify it in the production smoke test.
  • Make deployment verification follow nested Vite chunks so the browser worker and SQLite WASM artifact are checked reliably.
  • Keep dialogs and shortcut hosts interactive when the workspace is inert.

Verification

  • Full Rust, desktop bridge, renderer, portable WASM, and cloud test gates pass (./scripts/check.sh, 12/12).
  • Native desktop end-to-end run against the release commit.

Full changelog: https://github.com/remcostoeten/skriuw/compare/v2-v0.39.0...v2-v0.41.0

v2-v0.39.02026-08-18

Skriuw v2-v0.39.0

Page 1 of 3Older releases →