Changelog
Every release of Skrive, newest first.
- v1.7.0
- v1.3.0
1.3.0
View release on GitHub →A fresh coat of icons.
Skrive's iconography got a modern redraw. The formatting tools, folders, and document marks are all new — heavier, cleaner, and more characterful, without losing the Skrive feel. Everything adapts to your theme automatically, with tones hand-tuned for both light and dark.
What's new
- Formatting toolbar — bold, italic, code, lists, quote, table, link, divider, heading, and the rest are now solid, confident glyphs. They still light up with your accent color when active, exactly as before.
- Folders — redrawn open and closed folders that cross-fade gently as you expand and collapse them in the sidebar.
- Documents — a refreshed Markdown page icon, tuned to stay crisp on both the light and dark sidebars.
Notes
- Purely visual — no changes to behavior, files, or shortcuts.
- macOS build is signed; Windows build is unsigned (as usual).
Full changelog: https://github.com/brueshi/Skrive/compare/v1.2.1...v1.3.0
- v1.2.1
Skrive 1.2.1
View release on GitHub →Minor fixes and housekeeping.
- Share feedback directly from Settings → About, plus a gentle one-time in-app prompt
- Persistent notifications (updates, feedback) can now be dismissed with a corner control or a swipe
- Internal cleanup and added test coverage
- v1.2.0
Skrive 1.2.0
View release on GitHub →A visual refresh. This release reworks the app chrome end to end: a new light theme, a cleaner dark, a quieter sidebar, and a settings page that finally matches the editor.
A new Light theme
The light theme is now a clean white editor on a soft grey shell, brighter and more neutral than before. It replaces the previous warm light palette as the default.
A refined Dark theme
Dark has been retuned to a cooler, more even neutral, and its panels now carry a defined edge so the editor reads as its own surface instead of blending into the background. Error and destructive text is legible on dark now, too.
A quieter sidebar
The file sidebar is no longer a floating card. It sits flush as a calm rail alongside the editor, which now sits closer to your files. Folder names and the tree guides read more clearly across every theme.
A redesigned Settings page
Settings now mirrors the editor: a flush navigation rail beside a rounded content card. Switching sections fades and rises into place, the surface toggle slides between options, and the redundant Done button is gone (Back already takes you home).
Took care of some minor bugs as well.
- v1.1.0
Skrive 1.1.0
View release on GitHub →Skrive 1.1.0 is a foundation release. It is the largest re-architecture of the app's internals since the move to React, and it deliberately changes nothing about how Skrive looks or feels — every editor behavior is at parity with 1.0.3. The visible change is a single security hardening; the rest is groundwork.
If you are upgrading from 1.0.3: nothing in your workflow changes, your projects and settings are untouched, and there is nothing you need to do.
What changed for you
- Symlink-aware path containment. Skrive now resolves every file path against the real project root: a symbolic link inside a project that points outside it can no longer be followed out of the project folder, for reads, writes, or image serving. The impact is low for a local, single-user editor where you chose the folder yourself — but it is the correct boundary, and it is now enforced and covered by tests.
- Everything else behaves exactly as before. Backlinks, project search, dead-link and orphan detection, rename-with-references, autosave, external-change detection, image paste, and history all work identically to 1.0.3. This release changes no editor behavior by design.
Under the hood
The work behind this release re-draws the line between the editor and the system layer so the editor is no longer tied to any one host.
- One typed contract. Every message between the editor and the system now flows through a single versioned envelope with a closed set of error codes and a tested transport boundary, replacing a scatter of ad-hoc channels. This is the groundwork that lets the same editor run in other environments, including a future web embed.
- Text analysis moved into the editor. The link graph, project search, and frontmatter schema inference now run in a dedicated background worker off one batched project snapshot, rather than in the system process. The system layer no longer parses Markdown at all — it is reduced to filesystem, project, history, and OS primitives.
- Clipboard through the OS. Rich copy from the preview now goes through the operating-system clipboard instead of a browser API, so it keeps working across more environments.
- Housekeeping. Removed the dead Tauri and Svelte code carried over from the original prototype stack, and added a cross-implementation parity corpus that pins the system contract command-by-command.
Verification
- 326 editor tests, 68 system-layer tests (including a new symlink-containment suite covering five distinct escape attempts), 65 shared-contract tests, and a 26-case parity corpus that replays green.
- Validated on a signed, notarized packaged build before tagging.
- No changes to the on-disk file format. Upgrading is safe and reversible.
- v1.0.3
Skrive 1.0.3
View release on GitHub →Skrive 1.0.3 is a correctness and performance release focused on the projection editor — the layer that keeps the Rich surface and the Markdown on disk telling the same story.
This release came out of a deep fidelity and performance audit of the projection architecture, conducted and implemented end to end with Claude (Fable 5), Anthropic's frontier model — the audit found the bugs, multi-agent sessions fixed them in parallel, and a new corpus-wide test gate now stands guard over the dirty path.
Round-trip fidelity fixes
The projection editor's promise is that editing one block never rewrites the rest of your file. The untouched-block path was already byte-identical; this release fixes a set of bugs that fired when a block was edited:
- Inline images are no longer silently deleted from the file when the paragraph around them is edited. Images now appear in the Rich surface as real inline elements and serialize faithfully (alt text, URL, and title preserved).
- Pressing Enter in the middle of a paragraph no longer risks losing the paragraph break on save. Split blocks now correctly forget the verbatim source they inherited, on every split path (Enter, list splits, input rules), with undo still restoring the original bytes exactly.
- The canonical serializer now escapes properly. Backslash escapes survive edits, text that happens to start with Markdown syntax ("> ", "# ", "1. ") can no longer change block structure on save, and inline code containing backticks round-trips correctly.
- Code fences keep their character and length: a ~~~ fence whose body contains ``` lines no longer corrupts when edited, and fence info strings (language plus meta) are preserved.
- Reference-style links and images, inline HTML, and footnote references now freeze their containing block rather than degrading to plain text when touched.
- Link titles survive edits. Hard breaks can no longer be smuggled into headings, where they produce invalid Markdown.
- Loose lists containing tight items keep their rhythm when edited.
A new acceptance gate backs all of this: every block of every document in the test corpus is dirtied, serialized, and re-parsed, asserting the result is structurally identical to the original.
Editor performance
- Moving the cursor within a line no longer re-walks the visible syntax tree to rebuild decorations — rebuilds happen only when the cursor changes lines, the document changes, or the viewport moves. This removes the dominant per-keystroke cost in the Text surface on large documents.
- The preview outline rail no longer re-measures every heading on every keystroke. It now invalidates on heading structure, caches heading elements, and coalesces resize bursts — an image-heavy document measures a handful of times while loading instead of dozens.
- Typing-to-preview latency dropped by ~150ms: the preview's debounce sat downstream of the editor's store sync and coalesced nothing, so it was removed. Preview, word count, and outline now read the same snapshot.
- Word count is memoized instead of re-scanning the document on every state emission.
Verification
297 tests pass, including the new dirty-corpus fidelity gate, a split-block editing matrix driven through the production plugin stack, and unit coverage for the new invalidation logic.
- v1.0.2
Skrive 1.0.2
View release on GitHub →Skrive 1.0.2 adds two focused improvements on top of 1.0.1.
Git history toggle
Settings → General → Version history now has a Use Git for version history switch (on by default). Turn it off and Skrive stops reading your project's Git repository for the History panel and uses its own checkpoint history instead — no
git logorgit showruns when you open a document or switch tabs. This removes the Git activity that came from simply opening or browsing documents in a repo-backed project. Flip it back on and Git history returns immediately, no reopen needed. The setting persists across restarts and applies to every project.macOS dock icon follows system dark mode
The dock tile now matches your system appearance — the dark Skrive mark under dark mode, the light mark otherwise — and swaps the moment you toggle System Settings → Appearance. macOS only.
Builds
- macOS: Apple Silicon
.dmg, signed and notarized. - Windows: NSIS
.exeinstaller, unsigned (SmartScreen warns on first run — More info → Run anyway).
- macOS: Apple Silicon
- v1.0.1
1.0.1
View release on GitHub →Small QOL changes to the Rich Editor as well as some UI fixes Install macOS (Apple Silicon): signed and notarized .dmg — installs with no warnings. Intel Macs run under Rosetta 2. Windows: .exe installer. It is unsigned for now, so SmartScreen will warn on first run — choose More info -> Run anyway.
- v1.0.0
Skrive 1.0.0 — Overcast
View release on GitHub →Skrive 1.0 is two reimaginings landing at once: the editor has been rebuilt from the ground up, and the entire interface has a new visual language we call Overcast. Under both is a single conviction — Skrive should be a document you write in, not a code editor with a preview bolted on.
The editor, reimagined
For years the Markdown editor has been a cliche: a split pane, monospace on the left, a preview on the right, syntax permanently in your face. Skrive 1.0 replaces that with a projection editor — one Markdown file, two ways to work in it:
- Rich — a no-syntax writing surface for everyone. You write the document, and bold, headings, lists, quotes, dividers, tables, and links appear as the real thing — never as raw
**or#. Structure is created through a toolbar, a selection bubble, and a slash menu, so you build a document without ever typing a fence. - Text — an honest source surface for people who know Markdown and want it plain, with a choice of how present the syntax is: Raw, Recessed, or Concealed.
Both surfaces edit the same file. Switch between them with Cmd-Shift-E and your content carries over untouched. New documents open in Rich by default; the Text surface is one keystroke away.
The commitment that makes this honest: your file on disk is always canonical, plain Markdown. Everything richer is a projection of that text, never a container that owns it — so there is no proprietary format, no lossy export, and the bytes you would write by hand are the bytes Skrive saves. (And no AI anywhere — Skrive is a tool for your words, not a generator of them.)
Overcast — a calmer, more crafted interface
The whole app was re-skinned and restructured around a warm near-white page on a cooler dove-grey desk, a slate-indigo accent, rounder corners, and custom iconography:
- Settings is now a full page, with grouped cards, instead of a cramped modal.
- Side panels dock as cards beside a narrowing editor rather than floating over it.
- A reworked topbar — window controls and the sidebar toggle grouped on the left, lifted tabs, a surface toggle, and a quiet save indicator.
- Project-wide search now shows a live context preview of the highlighted match beside the results.
- Backlinks is bidirectional — both what links to this document and what it links out to, grouped by file with folder tags.
- New editor controls that actually do something: line measure (column width), smart typography (curly quotes, em dashes, ellipses), format on save, and a configurable autosave delay.
- The command palette, search, every dialog, and the right-click menus were all brought into the Overcast language.
Under the hood
For the curious, the foundation that makes the two surfaces trustworthy:
- Byte-faithful round-trip. A source-mapped parser tags every block with its verbatim source; the serializer splices untouched blocks back byte-for-byte and only re-writes what you actually changed. Open and edit a real document and the parts you did not touch are identical to the byte.
- Durable saves. Writes are atomic (temp file, fsync, rename — a crash cannot corrupt a document), the app detects changes made on disk outside Skrive and asks before overwriting, autosaves on a debounce, and flushes any pending edit before the app quits.
- Smooth at scale. Project linting runs in a background worker, so typing stays fluid no matter how large the project gets.
- One parser, not three. Preview, the projection, and linting all run off a single Markdown authority.
What's next
1.0 is the foundation, not the finish line. Already in motion:
- Marginalia — margin notes anchored to ranges of your text, with bilateral sidenotes and connectors.
- Typed blocks — callouts, profiles, and richer blocks you author inline but that save as honest, plain-text fences.
- PDF export with margins — beautifully typeset export with bilateral margins, connectors, and footnotes.
Install
- macOS (Apple Silicon): signed and notarized
.dmg— installs with no warnings. Intel Macs run under Rosetta 2. - Windows:
.exeinstaller. It is unsigned for now, so SmartScreen will warn on first run — choose More info -> Run anyway.
- Rich — a no-syntax writing surface for everyone. You write the document, and bold, headings, lists, quotes, dividers, tables, and links appear as the real thing — never as raw
- v0.3.0View release on GitHub →
v0.3.0 — Clipboard paste-in & copy-out, with images
Bidirectional clipboard handling for the editor:
- Paste in — rich HTML from web pages, Google Docs, or Word is converted to clean, canonical Markdown in Skrive's house style. Plain text pastes verbatim.
- Copy / cut out — writes both the raw Markdown (
text/plain) and the rendered HTML (text/html), so a selection pastes cleanly into code editors and as formatting into Gmail/Docs/Word. - Preview copy button — copies the whole document cleanly from the rendered view (no background bleed).
- Image paste — a screenshot or copied image is saved into a sibling
assets/folder and rendered inline in the editor and preview, via a custom protocol confined to the project root.
Prerelease build for testing. macOS is signed + notarized; Windows is unsigned (SmartScreen: "More info → Run anyway").
- v0.2.4
Skrive v0.2.4 — Document outline rail
View release on GitHub →The outline rail: a fast way to see a document's shape and move around inside it, right where you read it.
What's new
Outline rail
In Preview (⌘3), any document with two or more headings gets a slim rail of section ticks down the right edge:
- It tracks where you are. The tick for the section you're currently reading stays highlighted as you scroll.
- Click a tick to jump to that heading.
- Hover or focus the rail to expand a labeled outline — every heading, indented by level, the current one marked. Click a row to jump.
- Drag the rail to scrub the whole document, like a scrollbar that knows your structure.
- Tick width tapers by heading depth, so the shape of the rail echoes the shape of the document.
Toggle it in Settings → General → Outline rail (on by default). The rail lives on the rendered preview — a reading-and-navigating aid — so the writing canvas stays clean.
In-document links
Headings now carry anchor ids, so
[jump to a section](#heading-name)links resolve inside the preview. Ids use the same GitHub-style slug rule a published copy would (lowercase, spaces to hyphens, punctuation dropped), and repeats disambiguate the way GitHub does (-1,-2). A bare#link scrolls back to the top.Under the hood
- Keyboard-complete: Tab to the rail, then ↑/↓/Home/End to move, Enter to jump, Esc to close. The rail is a single focus stop (a listbox with
aria-activedescendant), and motion respectsprefers-reduced-motion. - The rail reads heading positions from the rendered output, so it stays accurate through line wrapping, images, and reflow.
- Slugging and active-section logic are unit-tested — 23 new tests.
Notes
- The rail is preview-only: it navigates the rendered document, not the editor, and it's deliberately an ambient affordance rather than another docked panel. It's hidden in raw and split layouts, and on documents with fewer than two headings.
Install
- macOS arm64 —
Skrive-0.2.4-arm64.dmg, drag Skrive to Applications. Signed and notarized. Already on v0.2.3? The in-app updater will offer this one automatically. - Windows —
Skrive-0.2.4-Setup.exe. SmartScreen warns (unsigned); click "More info" → "Run anyway."
- v0.2.3
Skrive v0.2.3 — Fix macOS auto-updater
View release on GitHub →🩹 Hotfix for the macOS auto-updater. v0.2.0 → v0.2.2 shipped only a
.dmgartifact for macOS, which broke in-place updates withZIP file not provided. v0.2.3 also ships a.zip— the format Squirrel.Mac (the underlying macOS update engine) actually consumes.What you need to do
- macOS, currently on v0.2.0 / 0.2.1 / 0.2.2: the auto-updater can't get you to v0.2.3 — it's blocked on the missing zip in the older releases' manifest. Download the v0.2.3 DMG below and drag-to-Applications once. From v0.2.3 onward, the in-app auto-updater works again.
- macOS, fresh install: just grab the DMG as usual.
- Windows: unaffected; updates worked.
What changed in code
One line of
electron-builder.yml:mac: target: - target: dmg - target: zip # ← newThe DMG is for install (drag-to-Applications); the ZIP is what the autoUpdater downloads and unpacks in place. v0.2.2 and earlier listed only the DMG in
latest-mac.yml, so Squirrel.Mac rejected every update attempt.No other changes
This is a release-engineering fix only — no UI or feature changes from v0.2.2. If you're already on v0.2.2 (light theme, inset design, etc.) the app looks identical; the only difference is the updater will actually work next time.
- v0.2.2
Skrive v0.2.2 — Light theme + Electron stops forcing dark
View release on GitHub →Light mode + a proper theme system. After v0.2.1 locked the inset design, this release adds back the light theme that had been deferred and stops Electron from forcing dark mode on light-mode machines.
What's new
Light mode
- New Theme picker in Settings → General → Appearance: System (matches the OS automatically), Light, or Dark.
- New installs default to Light. A warm paper-like palette:
#fefcf7page,#1f1d1aink, soft warm rules. Existing dark-mode users are migrated to Dark on upgrade so nothing changes for you without consent. - Theme switching is live — flip in Settings and the whole workspace fades to the new palette (no relaunch needed).
Electron stops forcing dark
- The BrowserWindow's pre-paint background no longer hardcodes
#1a1a1a. It now followsnativeTheme.shouldUseDarkColorsso a light-mode OS gets a light flash on launch and a dark-mode OS gets a dark one. No more "the app is dark even though my system is light."
Under the hood
- All palette tokens (
--skrive-bg,--skrive-fg,--skrive-muted,--skrive-rule,--skrive-link,--skrive-selection,--skrive-focus-ring) now use CSSlight-dark(). The browser resolves them against the activecolor-scheme, whichdata-themeon<html>pins (or follows the OS when absent). light-dark()is also wired through the three shell-tone variants. Their values stay distinct in dark mode; in light mode they all resolve to the same fixed light palette.
Known limitation
The Shell tone toggle is dark-mode only for v0.2.2 — picking different tones in light mode has no visible effect. Light-mode shell-tone variants are a follow-up release if the single fixed palette doesn't cover real use.
Upgrade behavior
- First-time installs: start in Light.
- Existing v0.2.1 (or earlier) users: migrated to Dark to preserve your current experience. Switch to Light or System anytime in Settings → Appearance.
- v0.2.1
Skrive v0.2.1 — Inset Design Stabilization
View release on GitHub →A visual overhaul. The editor now floats as a rounded card inside a soft shell frame, with the sidebar as its own floating card alongside. After A/B-testing several layout forks during v0.2.0, this release locks the winning combination and commits to it.
What you'll notice
A new page-on-desk look
- Full 4-side inset — the editor card has shell margin on all sides, with rounded corners and a subtle shadow.
- Sidebar is now a floating card too, separated from the editor by a deliberate gap rather than a 1px divider line.
- Shell tone is configurable — Light, Same, or Dark. Default is "light shell, darker page" (paper-on-inked-desk). Switch from Settings → General → Appearance.
Topbar streamlined
- Just three controls on the right: a panels popover (Frontmatter / Backlinks / History as checkboxes) and the Raw / Split / Preview mode toggle.
- The sidebar toggle button got a Notion-style refresh — larger icon, clear breathing room from the traffic lights, aligned with the tab strip.
- The project name dropdown (Open project / Close project) moved from the topbar to the sidebar's section header. The "FILES" label is replaced with your current project name + caret.
Settings is now a modal
- Press ⌘, and Settings opens as a centered Dialog over the editor instead of replacing the workspace. The editor stays visible behind it — useful when you're previewing appearance changes against an actual document.
New behaviors
- Drag-to-collapse the sidebar — pull the resize handle below ~100px and the sidebar snaps fully closed. ⌘[ reopens it at its previous width.
- Side panel push behavior (Settings → Appearance) — when on, opening a panel narrows the editor card to make room. Switch to "Float" if you'd rather have it overlay.
Under the hood
- Settings rewritten as a Radix Dialog modal to eliminate the layout-shift issues the workspace-view approach kept producing.
- Workspace + sidebar wrapped in
contain: paintfor paint isolation — CodeMirror scroll no longer re-rasterizes the card's rounded mask + box-shadow on every line virtualization frame. - Animation curve token `--skrive-easing-out` added; entrance animations use the snappier ease-out curve. Baseline durations tightened (quick 120→110ms, standard 160→150ms, slow 180→160ms).
- Card shadow lightened in the "Same tone" palette — the old 0 8px 24px blur was costly on inner scroll.
Removed
The following experimental layouts and components no longer exist: Classic topbar (full FM/BL/HI cluster), Collapsed-bottom rail, Top+bottom inset, Embedded sidebar, Tabs-on-card, DocumentToolRail / CollapsedBottomRail / SettingsView. If you had any of these selected in v0.2.0, you'll be migrated to the locked behavior on next launch.
Note: v0.2.1 ships dark-mode only. v0.2.2 immediately follows with light mode and theme switching — see that release for details.
- v0.2.0
Skrive v0.2.0 — Same Skrive, new foundation
View release on GitHub →v0.2.0 — Same Skrive, new foundation
This release is a rebuild, not a redesign. Skrive's writing surface, project model, rename-with-references, and structural diff are all at v0.1.6 parity. What changed is everything underneath.
Skrive moves off Tauri + SvelteKit onto Electron + React + napi-rs. The reason is maintainability: React + Electron is one of the most heavily traveled paths in desktop software, with a single TypeScript codebase across the renderer and the shell, and a vastly larger ecosystem of accessible primitives (Radix), command runners (Cmdk), and animation libraries (Framer Motion) to pull from when design and feature work need to scale. Tauri + Svelte was lean and fast, but every meaningful feature was a custom-build job; the foundation needed to be one we can iterate on instead of fight.
The honest tradeoff is package size. Tauri ships a ~10MB binary by reusing the system webview; Electron bundles its own Chromium and lands in the 150-200MB range. There isn't really a best-of-both-worlds option for desktop apps right now — system webview comes with platform fragmentation, bundled Chromium comes with size. We picked the side that lets a solo project move faster without trading off correctness.
What's new
- Keyboard shortcut cheat sheet. ⌘/ opens a modal listing every binding the app responds to, grouped by area. First answer to "wait, what does ⌘⇧B do?"
- Accessibility baseline. Keyboard navigation reaches every primary action. ARIA labels on custom controls. Modal focus-trap via Radix.
prefers-reduced-motionrespected on every transition. Not a feature flag — just how the app is built now. - Signed + notarized macOS installer. Skrive opens cleanly on a fresh Mac without the Gatekeeper warning, on first launch and forever after.
- Auto-update wired in. "Update on launch" toggle in Settings → Updates, plus a manual "Check for updates" button. Background download, restart-to-apply.
- Sidebar moves to ⌘[. v0.1.6 used ⌘B, the VS Code convention. ⌘[ feels closer to "open the rail" than "make this bold," which matters when you're not coding.
Under the hood
- Stack: Electron 33 + React 19 + CodeMirror 6 + Radix UI + Cmdk + Framer Motion. Replaces Tauri 2 + SvelteKit + CodeMirror 6.
- Native diff core preserved. The Rust crate that powers version history is unchanged at the algorithm level — exposed through napi-rs as
@skrive/diffinstead of through Tauri commands. Same fixtures, same parity tests. - Typed IPC. Renderer and shell communicate through a type-checked contract instead of Tauri's runtime-typed
invoke(). Adding a handler is a type + an implementation, not a refactoring spelunk. - Performance budgets verified. Cold-open <1.5s on a 100-file project. File switch <50ms. Lint refresh <500ms on a 200-file project. Measured by an instrumented fixture in the repo.
- Central shortcut registry. Every keyboard binding lives in one TypeScript module — the same source the cheat sheet renders from, the same source the dispatcher reads. Adding a binding is one edit.
- Token-coherent chrome. Radius, z-index, and motion timing live as CSS custom properties at
:root. The next polish pass touches one place instead of fifty. - Voice consistency. Error toasts say "Couldn't" instead of "Failed to." Empty-state messages teach instead of just stating absence.
Coming next
A lot of great things are planned — typed blocks, exports, faster diff, cloud sync — and the new foundation is what lets those land in months instead of quarters.
Install
Pre-alpha build.
Existing v0.1.6 users: uninstall v0.1.6 before installing v0.2.0. The two versions use different installer formats and updaters, and we haven't validated sharing app data between them. After uninstalling, install v0.2.0 from the assets below; v0.2.0+ will auto-update from here on.
- macOS arm64 — download
Skrive-0.2.0-arm64.dmg, drag Skrive to Applications. Signed and notarized. - Windows — download
Skrive-0.2.0-Setup.exe. SmartScreen will warn (unsigned); click "More info" → "Run anyway." Code signing is on the roadmap.
Skrive is solo work. If something in v0.2.0 doesn't feel like Skrive, open an issue — that's the most useful thing you can do.
- v0.1.6
Skrive v0.1.6
View release on GitHub →v0.1.6 — Top-bar redesign
The chrome at the top of the workspace was reading as a separator strip — a white macOS title bar above a cream Skrive header above the editor, with rules between each layer. This release pulls the cream all the way up to the traffic lights, redoes the open-document tabs as cards that flow into the editor below, and lightens the right-side panel toggles.
What's new
- Cream extends to the very top. The window now uses macOS overlay title bar mode, so the warm cream surface continues up under the traffic lights instead of stopping at a white strip. No more color seam at the top of the window.
- Tabs are document cards. Each open tab gets the Markdown doc icon, browser-style sizing (tabs hug their content width, cap at 14rem for long filenames, and shrink uniformly to 36px icon-only when many are open), and an active state that lifts to the editor cream with rounded top corners — the active tab's bottom edge meets the editor edge, so the open document visually flows out of its tab.
- Right-side panel toggles, lightened. The
FM/BL/Aa/HItext pills in the top-right are replaced with four small icon-only buttons (frontmatter, backlinks, personal dictionary, history). Counts moved into each panel's own header where they're more useful, so the chrome stops carrying content metadata. - Four new panel icons.
IconFrontmatter,IconBacklinks,IconDictionary, andIconHistoryjoin the existingIconDocMarkdown/IconFolderfamily — same hand-drawn voice, same 16/24 sizing pattern.
Under the hood
- New
--skrive-chromeCSS variable (light:#efece5, dark:#1a1a1a) gives the chrome strip a subtle tint so the active tab — which matches editor cream — reads as lifted out of the strip and connected to the writing surface below. Tab strip usesalign-items: stretchplus containerpadding-topso the active tab extends from above the text down to the chrome's bottom edge; tabs themselves useflex: 0 1 autofor the hug-content sizing. - macOS overlay title bar requires the chrome to clear the traffic-light cluster — 78px left-padding applied via a
navigator.platformcheck at mount, so Windows / Linux builds don't get phantom whitespace. Header carriesdata-tauri-drag-regionso window dragging still works in the empty zones of the chrome. - Panel click-outside escape hatch refactored: each side panel's "ignore clicks on my own toggle" check now matches a
data-panel-toggle="X"attribute instead of the (deleted) per-pill class names. ~190 lines of dead pill CSS removed fromHeader.sveltein the same pass.
Coming next
- Sort order options for the sidebar tree with per-project persistence
- Inline metadata in the sidebar (relative time, optional word count, tag chips)
- Pinned section above the sidebar tree
- Drag-to-reorganize with link rewriting
- Smooth expand/collapse animation in the sidebar
- v0.1.5
Skrive v0.1.5
View release on GitHub →v0.1.5 — Sidebar tree redesign
The sidebar is the workspace's most-touched surface. This release rethinks it from a writer's perspective: titles instead of filenames, custom-drawn iconography, and a tree-spine treatment that respects each subtree's structure.
What's new
- Title-as-primary rendering. File rows now show the document's frontmatter
title:as the main label, with the filename rendered as a muted secondary line below. Docs without atitle:field still show the filename, no regression. - Real recursive tree. The previous flat-grouped view (each subfolder rendered as a separate top-level group) is gone. Folders nest naturally, with click-to-collapse on every folder header. Expand/collapse state is in-memory for now; per-project persistence lands in a later pass.
- Custom folder + doc icons. Two new hand-drawn icons in Skrive's visual voice:
IconFolder(a single component whose pocket-line morphs between open and closed states with a subtle CSSdanimation) andIconDocMarkdown(a doc silhouette with an inline Markdown wordmark). Replaces the generic chevron + filename rendering. - Indent guides with L-elbows. Each row gets a connector to its parent — a half-height vertical with a horizontal stub bending into the row's icon. Ancestor spines stack across the tree to make depth legible at a glance.
- Subtree-respecting spine rule. A spine line at column
don row R draws only if R's ancestor at depthd+1is not a last child. Each subtree's spine is contained to its own siblings — when a folder is the last in its parent, its column-line stops at its elbow rather than extending through descendants. Cleaner termination, no lines pointing to nothing.
Under the hood
- Indent step bumped from
0.75remto1.25remso L-elbows have geometric room to read as connectors rather than tick marks. - File rows use a flex-row layout (icon column + flex-column label stack) so titles ellipsize independently from filenames, and the active-state accent uses an inset
box-shadowinstead of a transparent border — no more 2px alignment jog between folder and file rows. - Spine geometry is computed in the snippet and inline-styled per row, not via static CSS. Each row knows its own ancestor-lastness chain and builds its own multi-layer
background-imagefrom that.
Coming next
- Sort order options (modified, created, name) with per-project persistence
- Inline metadata (relative time, optional word count, tag chips)
- Pinned section above the tree
- Drag-to-reorganize with link rewriting
- Smooth expand/collapse animation
- Backlinks panel revamp with shared visual language
Install
Pre-alpha build. See install instructions for each platform.
- Title-as-primary rendering. File rows now show the document's frontmatter
- v0.1.1
Skrive v0.1.1
View release on GitHub →The first post-alpha release. Four dogfood-driven additions to make daily writing flow smoother, plus the supporting plumbing each one needed.
Drag-and-drop image import
Drop an image onto the editor and Skrive copies it into a project-relative
attachments/folder, inserts a Markdown image at your cursor, and renders it inline. Filenames are preserved; collisions get a-1/-2suffix.- A subtle dashed outline appears on drag-enter so the drop target is unambiguous.
- Source paths are written source-file-relative (e.g.
../attachments/foo.pngfrom a file inchapters/), so the markdown renders correctly in Skrive, GitHub, and any other reader. - Filenames with spaces (screenshot defaults like
Screenshot 2026-04-29 at 6.25.48 PM.png) are URL-encoded automatically. - When a drag source doesn't include a real file path — common for browser, Slack, and Photos.app drags that carry URLs instead — Skrive surfaces a quiet notice instead of failing silently.
Tab indents and outdents list items
Tab and Shift-Tab inside a
-,*,+,1., or1)list now nest and unnest the item — what writers expect from Word, Notion, Bear. The gesture is gated on list context so Tab on a prose line falls through to default (which matters: a 4-space prose indent silently becomes a CommonMark code block).Multi-line selections nest as a group when every line is a list line.
Rename-as-path
In the rename modal (F2 on a tab or Rename… in the sidebar), typing a path with
/separators now moves the file into that subdirectory. Intermediate folders are created if missing.docs/foo.md→ moves the file into adocs/subfolder of its current location.- A "Moves to: …" hint appears under the input so typos surface before commit.
- Inbound link rewriting reuses the same machinery as in-place renames; references update across the project automatically.
Inline image preview
now renders as the actual image both in the editor (inline thumbnail) and in the preview pane (full size). Image URLs are resolved against the source file's directory, normalized for.and.., URL-decoded, and converted into asset-protocol URLs the webview can fetch.A console warning surfaces the resolved URL on load failure, so when an image doesn't render the path it tried is one click away.
Upgrade notes: existing image markdown that you typed by hand with literal spaces in URLs won't render until the spaces are encoded as
%20. Re-dropping the image is the fastest fix. - v0.1.0
Skrive v0.1.0
View release on GitHub →Skrive v0.1.0 — alpha
This is the first alpha release of Skrive — a Markdown IDE for people who write seriously and ship to the web.
The product is intentionally narrow. Skrive is not a notes app, not a knowledge graph, not an AI editor. It is a fast, opinionated workspace for real Markdown writing — projects of files on disk, structured frontmatter, a surface that renders inline, a diff that reads like prose, and a rename that updates every reference for you.
If you've been waiting for a tool that takes Markdown writing seriously without trying to become Notion, this is it.
What's in the alpha
The writing surface
- Inline preview decorations.
**bold**collapses to bold when your cursor leaves the line and reverts when it returns. Images render in place. Links display as styled text without the URL clutter. - Three layout modes — raw, split, preview — toggleable per file with
⌘1/⌘2/⌘3. Layout state is remembered per file across sessions. - A real editor surface. CodeMirror 6 underneath, an opinionated default theme on top (Iowan Old Style, 17px, 1.7 line-height), and now a curated typography picker if those defaults aren't yours.
Project model
- Projects are folders of
.mdfiles, not vault databases. Open them, write in them, version them with Git like any other code project. - Structured frontmatter.
⌘⇧Fopens a typed-field panel; the project's schema is inferred at open time so autocomplete and known-value suggestions work immediately. - Personal dictionary layered on top of the OS spellchecker — words you've taught Skrive don't get flagged across any project.
The Unique features
- Rename with references. Right-click any file → Rename, and every link to it across every other file in the project updates atomically. The single feature that separates Skrive from every other Markdown tool.
- Backlinks.
⌘⇧Bshows everything in the project that links to the current file, with byte-precise jump targets. - Structural diff.
⌘⇧Hopens version history (Git or checkpoint-based, whichever your project has). Diff between any two versions, or any version against current.
New in v0.1.0
- Settings page — opens with
⌘,as a workspace tab (VS Code-style). Sections: General, Editor, Personal Dictionary, Updates - Editor typography — five curated presets (Editorial / Classic / Screen / Sans / Mono) plus a Custom font field that uses any typeface you have installed system-wide. Stepped size (14–22) and line-height (1.5 / 1.7 / 2.0) controls.
- Command palette —
⌘⇧Popens a real command runner with fuzzy search, separate from the file switcher (⌘P, unchanged). Every binding in the app discoverable from one keystroke. - Reveal preferences in Finder — Settings → About → Reveal in Finder. Useful for sharing your config when reporting issues.
- Auto-update on launch toggle — Settings → Updates. Default on; opt out if you want full control over update timing.
Installation
- macOS — download the
.dmg, drag Skrive to Applications. Signed and notarized. - Windows — download the
.msiinstaller. Currently unsigned (SmartScreen will warn on first run; click More info → Run anyway). - Linux —
.deband.AppImageboth attached.
Auto-update is enabled by default; subsequent releases install in place.
Alpha caveats
This is alpha. It works — but rough edges exist and your dogfood feedback is the input shape that v0.2 will be built around. Expect:
- Visual quirks under specific font/size combinations (typography is brand new in this release).
- Per-platform polish gaps. The macOS experience is the most mature; Windows and Linux are functional but less hardened.
- Features you might miss. Drag-and-drop image import, hover previews, sidebar folder hierarchy — all on the post-alpha track.
Found something broken? Settings → About → Reveal in Finder, share your
app.json, and open an issue. The faster I see real-world setups, the faster v0.2 happens.
Philosophy
Skrive is local-first — your files live on your disk, in your folders, in plain
.md. No server, no sync (yet), no AI. No phone-home. The writing is yours.It's source-available under PolyForm Noncommercial 1.0.0 — free for personal use; commercial rights reserved for a future paid tier.
- Inline preview decorations.
- v0.0.9
Skrive v0.0.9
View release on GitHub →v0.0.9 — Structural diff: algorithm picked, backend shipped
This release lands the research and backend work for Phase 3.3b (structural diff). The Svelte-side renderer upgrade was attempted and reverted — the diff UI users see is still the Phase 3.3a line-diff, which works cleanly. The algorithm work and Rust backend are in place so the next renderer attempt doesn't start from zero.
What's in
Algorithm decision (T1 resolved). Block-hash matching with Hungarian assignment. Chosen over Zhang-Shasha tree-edit-distance and block-Myers
Full transcripts and reasoning live in docs/3.3-algorithm-memo.md.
Production compute_diff in src-tauri/src/diff.rs. New Block, BlockKind, DiffOp (Kept/Added/Deleted/Moved/Reworded), and WordOp types. Fence-aware block splitter (code blocks with blank lines stay intact). Word-level intra-block diff for reworded paragraphs via similar::TextDiff::from_words. Exposed as a Tauri command. Covered by 10 new tests; full Rust suite at 147 passing.
TypeScript mirror. src/lib/diff/structural-diff.ts carries the frontend-side type definitions and a thin computeDiff invoke wrapper, ready for whenever the renderer gets picked back up.
What's deferred
The renderer. The Svelte preview-mode upgrade (moved banners, reworded inline word-level diff, sage/slate/brass palette) landed and was reverted after a reactivity bug that couldn't be reproduced or diagnosed remotely. The 3.3a line-diff renderer stays as the diff UI. When the structural renderer is picked back up, the approach is: build it as a standalone demo page with hardcoded DiffOp[] fixtures first, then wire into DiffView as a second render path alongside the line-diff, not a replacement. No date.
Upgrade notes
- No user-visible changes. The diff view looks identical to v0.0.8.
- No migration; no data format changes.
Under the hood
- New: src-tauri/src/diff.rs (structural diff), src/lib/diff/structural-diff.ts
- Updated: docs/3.3-algorithm-memo.md (decision + prototype transcripts), src-tauri/src/diff_memo.rs (P2 + P3 prototypes)
- Tests: 10 new Rust, 147 total passing; frontend typecheck clean
- v0.0.8
Skrive v0.0.8
View release on GitHub →v0.0.8 — Version history
Skrive gets a full version-history surface this release. Open any Markdown file, hit ⌘⇧H, and see every version behind it — whether from git commits or Skrive's own checkpoint store for plain folders.
See what you've changed
- History panel (⌘⇧H) — newest-first list of every version of the active file. Single-click a row to diff it against the current file; shift-click a second row to pair-compare two historical versions.
- Two history sources, auto-selected per project:
- Git projects — commits that touched the file, with subject, short sha, and relative time.
- Plain folders — Skrive-managed checkpoints: auto-snapshots every five minutes of active editing (content-hash dedup'd), plus a manual "Pin version…" command for milestones.
- Side-by-side diff view with two modes:
- Raw — source vs source, gutter markers for added / removed / kept lines, placeholder rows keeping panes aligned.
- Preview — rendered Markdown on both sides with sage-tinted added paragraphs and struck-through deletions. Reads as revised prose, not code review.
Navigate a diff like a long document
- n / j — next change; p / k — previous; Home / End — first / last. N of M counter in the chrome so you always know where you are.
- Sync scroll in two modes:
- Matched (default) keeps paired blocks aligned across panes even when they render at different heights.
- Linear is a simple scroll-height ratio — useful when the diff is small or matching isn't helpful.
- Esc exits diff mode and returns the tab to whatever you had open before.
Configure per-project
.skrive.toml parsing lands this release. First live consumers:
- [checkpoints].auto_cap — cap on auto-snapshot count per file. Default 50 (~4 hours of active editing at the 5-minute cadence).
- [checkpoints].manual_cap — cap on pinned versions. Default 0 = unbounded; set to a positive integer to keep a rolling window.
Future sections ([lint], [dictionary], [export.*]) parse against the committed schema so you can populate them today; their consumers ship in later phases. Edit and reopen the project to apply changes — live reload via the watcher is tracked as a follow-up.
Full schema reference: docs/skrive-toml-reference.md.
Under the hood
- Search highlights now land correctly on astral-plane input (emoji, some math symbols) — column and matchLength are reported in UTF-16 code units, matching the backlinks / outgoing-link convention already used elsewhere.
- Structural-lint phase renumbered 3.2 → 3.4 in the roadmap to match execution order (3.1 → 3.3 → lint). Phase 3.2 is an open slot for a future deliverable.
- 134 Rust unit tests passing; frontend svelte-check clean.
Tracked for later
- Structural diff — moves and reworded paragraphs rendered as first-class operations rather than as line noise. Phase 3.3b, gated on dogfooding v0.0.8 to confirm whether the line-level view reads well enough.
- Live reload of .skrive.toml — watcher extension so saves apply immediately without reopening.
Full changelog: v0.0.7...v0.0.8
- v0.0.7
Skrive v0.0.7
View release on GitHub →What's new
Bracket-family shortcuts — three ergonomic keybinds that share one pair of keys.
- ⌘[ toggles the sidebar (was ⌘B). Moving off ⌘B keeps the universal "bold" slot free for when editor formatting shortcuts land.
- ⌘⇧[ / ⌘⇧] cycle through your open tabs. Same convention as Safari, Chrome, and Terminal — already muscle memory if you're on macOS.
Drag-to-resize sidebar — grab the right edge of the file list and drag; double-click the handle snaps back to the default width. Width clamps between 180 and 500 pixels and is remembered per project, so each place you write keeps its own layout.
Fix
Sidebar width was being written to project state on save but never read back on restore — every session opened at 260px regardless of what you'd set it to. Now it sticks.
Install
Download the .dmg (macOS, universal) or .msi (Windows) below. Existing installs will see the update offer inside the app via the built-in updater.
- v0.0.6
Skrive v0.0.6
View release on GitHub →Skrive's first "project intelligence" release. The link graph the Rust core has been building since Phase 1 finally has a UI and the commands behind it.
New
- Rename-with-references. Rename any Markdown file in your project and every inbound link updates automatically. Invoke with
F2on the active tab (orfn+F2on a Mac that hasn't flipped the function-key setting) or right-click → Rename… in the sidebar. A confirmation modal previews exactly what will change —N references across M files, each row showing path, line, and a snippet — before anything is written. Inline links[text](url), wiki links[[Name]], and reference-style[label]: targetdefinitions are all rewritten; relative paths recompute correctly from each source file's directory. - Backlinks panel. Press
⌘⇧Bon any open file to see every other file that links to it. Click a row to jump there. The header shows a liveBL · Ncount. Panel re-queries on tab switch and on external file changes. .skrive.tomlschema documented.docs/skrive-toml-reference.mddefines the full schema —[project],[lint],[lint.required_frontmatter],[dictionary],[export.*]— that Phase 3.2's lint engine and Phase 5's exporters will consume. A missing file is valid; parse errors show a toast and fall back to defaults without blocking project open. No parser yet — that's Phase 3.2.
Fixed
- Preview-pane link clicks no longer throw a 404. Rendered markdown links route internally through the tab store for
.mdtargets and open externals in your default browser via the Tauri opener plugin. - Custom error page. If any navigation does slip past the in-app handlers, you land on a Skrive-styled page with a Return to editor button instead of SvelteKit's raw 404.
- Sidebar hides common noise directories.
node_modules,target,dist,build,__pycache__, andvenvno longer clutter the file tree when you point Skrive at a code repo that happens to contain Markdown..gitignore-aware walking is a follow-up.
Under the hood
- Link graph edges now carry byte ranges, line, column, and link kind — the foundation the rename rewriter uses to edit source bodies back-to-front without shifting offsets.
- Graph updates incrementally on write, create, delete, directory delete, and external-edit reload.
- New Tauri commands:
get_backlinks,get_outgoing_links,get_dead_links(data-only; the lint UI lands in Phase 3.2),preview_rename,rename_with_references. - 77 Rust tests, 0 Svelte-check warnings.
Known limitations
- Rename doesn't refresh open tabs that have unsaved edits. If a rewritten file is dirty when you rename something that references it, we keep your buffer as-is and warn you by name — save or discard on your own terms.
- Rename target paths with spaces emit bare (no angle brackets); fine for the common case, may need bracketing if this comes up.
- Wiki link rename is filename-stem based and case-insensitive. A case-collision with another file's stem would resolve ambiguously — flag if dogfood hits this.
F2on macOS requiresfn+F2unless you've turned on "Use F1, F2, etc. keys as standard function keys" in System Settings. The sidebar's right-click Rename… menu is the Mac-idiomatic fallback.
What's next
Phase 3.3 — structural diff — is the next bet. Phase 3.2's lint engine will surface the dead-link data this release already emits.
- Rename-with-references. Rename any Markdown file in your project and every inbound link updates automatically. Invoke with
- v0.0.3
Skrive v0.0.3
View release on GitHub →Pre-alpha build. See install instructions for each platform.
Small bug squash
- v0.0.2