github sinelaw/fresh v0.5.2
fresh-editor 0.5.2

2 hours ago

0.5.2

For live updates on Fresh, follow me on X.

Fresh is now licensed GPL-3.0-or-later, up from GPL-2.0-only (#3328).

Most config options below can be changed in the Settings UI - run Open Settings from the command palette (Ctrl+P).

Features

  • Orchestrator mode - a bare fresh, with no file or flags, reopens the workspace you were last in, dock and all, running as a background daemon. On by default, toggle it in Settings (#3306)
  • New CLI commands - workspace list, and agent list / get / explain / wait / start, for scripting Orchestrator workspaces and agents without reading the dock
  • Confirmations are a modal dialog now, not a line on the bottom row - a centred card with each outcome spelled out as its own button, answering to arrows, Tab, Enter, Esc, the mouse and the letter underlined in the label; an outcome that loses work is in the error colour and never opens armed (#3320)
  • Orchestrator: a Repositories registry tracks one main clone per project per machine - the New Workspace / Run Agent dialog resolves paths from it, warns when a machine has none, and confirms before cloning; its fields now fold behind one-line summaries instead of staying open all at once (#3360)
  • Orchestrator: workspaces run on a saved machine, chosen from one consistent list - the dock's ⋯ is a labeled Menu now, and the Machines dialog matches the look of the Projects dialog (#3361)
  • Ctrl+Q in a daemon session asks Detach / Quit / Cancel instead of always stopping the daemon and every terminal and agent it hosts; Detach is the default, Quit and Stop All continue into the normal quit path
  • Tokyo Night and Gruvbox ship as built-in themes, which the homepage already promised (#3107, reported by @sgon00)
  • Alt+Shift+N / Alt+Shift+P cycle the keyboard through the sidebar, so a plugin panel like the Markdown outline is reachable without the palette (#3326)
  • Search folds case by default everywhere - Find/Replace, Live Grep, the Search & Replace panel and Git Grep all match todo against TODO now; each keeps its own toggle, and editor.search seeds the default for a workspace that hasn't set its own (#3349, requested by @Korkman)
  • Settings reorganized - new Keybindings and Syntax & Languages pages, plugin settings nested under Plugins, a more compact category tree, and shorter descriptions (#3357)
  • Interrupted saves are recoverable - if a save stops partway (disk full, I/O error, crash), a full copy is kept and an Interrupted Save dialog offers Restore / Show Diff / Discard on the next start, or any time via Review Interrupted Saves (#3383)
  • Much faster JavaScript/TypeScript highlighting, and a quieter idle daemon - JS/TS moved off a parser that re-read the whole file on every keystroke, large files parse only as far as the view, and idle CPU drops to a few percent (#3418)
  • Live Grep dialog redesigned - an aligned form with underlined shortcut letters and a Provider dropdown listing only the providers that can run; the Ignored toggle moves to Alt+I (#3367)
  • Dialog pickers behave like standard controls - suggestion lists act as combo boxes, and the Browse… file picker floats over the form, closes when focus leaves, and supports double-click, Home/End and type-to-jump (#3367)
  • fresh --cmd init plugin sets up a typed plugin project - TypeScript config, API types and the language server, with a new setup guide; Load Plugin from Buffer on a saved file now loads it from disk, so relative imports work, and reloads it in place (#3422)

Bug Fixes

  • echo 123 | fresh opens the pipe instead of hanging - piping into fresh with no file and no - brought up an editor that took no input at all, spun on a full CPU core, and could not even be quit with Ctrl+Q. Stdin with nothing else to open now means fresh -; with a file to open it is left unread, but the keyboard works either way. Any redirect counts, not just a pipe - fresh < file and fresh < /dev/null hung the same way (#3252)
  • Orchestrator dock polish - the welcome screen and dock could fail to appear in a daemon session, a context menu could close itself too fast, and a workspace just created from the dock could end up not taking keyboard input (#3306, #3275)
  • The workspace dock is there from the first frame - the editor used to come up full width and the dock shoved it aside a moment later, once the plugins had loaded. The column is now laid out before any plugin runs. The dock also remembers whether you left it open and how wide you dragged it across launches; autoOpenDock: false keeps it closed until you open it, and a bare fresh always opens it (#3321)
  • A nested fresh in the editor's own terminal no longer shreds the pane - and a terminal in a daemon session advertises FRESH_SESSION again, so fresh FILE there hands the file to the editor you are already in (#3318)
  • Sidebar panels stay with the window and file they belong to - a Markdown outline mounted in one window showed in every other, and a restored panel no plugin claimed sat reading "Panel unavailable" forever (#3326)
  • The Markdown outline follows the reader while the file tree has the keyboard, and its toggle reveals the panel instead of reporting ON for something off screen (#3326)
  • The explorer says why a reveal could not move the tree - an unnamed buffer, or a file outside the project - and its scrollbar can be grabbed and dragged (#3326)
  • SSH workspaces - creating one didn't always honor your ~/.ssh/config, and hung with a plain error on an unrecognized host key instead of asking to trust it; deleting one could occasionally crash the app (#3301, #3299, #3300)
  • A remote host that connects and then says nothing gives up instead of leaving the workspace on "scanning" forever
  • Attaching to a machine keeps the window you are in - its buffers, splits, layout and terminals - instead of restarting the editor
  • Markdown compose: a heading no longer flickers while you type in it, and backspacing a list item no longer throws the caret two rows down (#3318)
  • The occurrence highlight keeps up with the cursor - it used to wait for your next keystroke to repaint, and in a daemon session every other time-driven repaint was stranded with it (#3318)
  • Code tour: clicking the prose takes the keyboard, and its caret stays visible past the end of a line (#3318)
  • A tab after CJK text no longer eats the character before it - the tab marker was placed by visual column against an index counted in characters, so a double-width glyph put it one character early and the → took that glyph's place: 你好⇥world drew as 你→ world. The file was never touched (#3218, reported by @sgon00)
  • Highlighted lines (diff view, code tour) no longer hide inlay hints at the end of the line (#3314)
  • wrap_column counts text columns - wrap_column: 80 now fits 80 characters per row; the line-number gutter used to eat into it (#3405)
  • With wrap_column set, arrowing down no longer leaves the cursor below the visible lines (#3294, reported by @gibboni)
  • editor.scroll_offset fixed for files under 5000 lines with line wrap off (#3248)
  • Markdown code block borders no longer break while you type inside them (#3247)
  • LSP now finds the right project folder on Windows (#3067, reported by @Bearmancer; fixed by @56steve)
  • Keybinding fixes - multi-byte bindings like German Ctrl+ü now load from config (#3036, by @georglauterbach); a panel's Shift+Tab and Review Diff's fold shortcuts now fire (#3253); a rare case with certain Unicode letters no longer got a phantom Shift (#3302)
  • Emacs chords work in a plugin-mode buffer again - C-x C-c, C-x C-f and C-x C-s in a Markdown buffer ran the second key's own action instead of completing the chord (#3060, by @dbactual)
  • Split resize shortcuts reach the editor from a terminal pane instead of being typed into the shell (#3245, by @kimprap)
  • Vi mode: many Vim-parity fixes - Y, J, G, x/X, visual mode, text objects, dot-repeat, and more, checked against real Vim (#2447)
  • Vi . no longer drops the insert it replays when typed quickly after Esc
  • Fixed stale LSP diagnostics after vi-mode edits, and Save All sending the wrong file's content to language servers (#3258)
  • Vi mode holds in every window - autoStart turns it on at startup again, and dialogs, window switches and newly opened workspaces no longer drop it (#3305, reported by @thedadams; #3395)
  • The homepage's demo player paints again on Chrome/Linux instead of showing a black box (#3106, reported by @sgon00)
  • A language server can no longer delete your files. A server-initiated workspace/applyEdit arrived with no confirmation and was applied as-is, including its "delete this file (recursively)" operation — permanently, bypassing the system trash the file explorer deletes through. Fresh now reports the request and ignores it
  • Uninstalling a package, replacing one on upgrade, and deleting a theme all go to the system trash, instead of being unlinked outright. A mis-click is recoverable now
  • Deleting a folder that contains a symlink no longer empties what the link points at. The recursive delete treated a link to a directory as a directory and descended into it, so deleting a folder holding a link to, say, ~/Documents deleted the contents of ~/Documents — then reported a failure, after the fact. Links are unlinked now, never followed (reachable from the file explorer when cutting and pasting between filesystems)
  • A language server can no longer overwrite a file either. Create and Rename resource operations honoured an overwrite flag from the server, truncating or clobbering an existing file with no confirmation. Both are now reported and ignored
  • The install script refuses to replace a directory that is not a Fresh install. FRESH_INSTALL_DIR is removed wholesale during an install, and the only guards were against "", / and $HOME — so FRESH_INSTALL_DIR=$HOME/.local wiped it without a prompt. An existing directory now has to be empty or hold a Fresh binary, receipt or file manifest
  • Rust highlighting is on a current grammar now - an unspaced < before a string, as in ensure!(limit<=MAX_OUTPUT,"..."), used to be read as the start of a generic argument list; recovering from it ate the string's opening quote, so the string body rendered as code and every line after it was painted as a string literal until the next quote. The Rust grammar bundled inside syntect was years out of date, so Fresh now ships its own copy of the current upstream one - which also brings better type, constant and macro colouring across Rust files (#3325, reported by @asukaminato0721)
  • File Explorer: the scrollbar no longer steals a row's hit targets - hovering the bar could pop the tooltip of the file hidden behind it, and the selection caret covered its own row so that row's status marker answered nothing (#2859, by @asukaminato0721)
  • Search, replace and goto-line history persists across restarts again - it silently stopped saving, and a plain fresh file.rs (which skips workspace restore by default) could even overwrite what was already stored
  • A block (column) selection no longer stays active after you move the cursor without selecting - Alt+Shift+Arrow then a plain arrow left a stale rectangle painted, and the next keystroke fanned out into one cursor per rectangle line
  • Terminal id mix-ups across windows fixed - an exit in a background window's terminal could mark or kill the active window's same-numbered terminal instead; ids are now unique across the whole editor
  • LSP: semantic-token highlighting refreshes when a language server asks for it, instead of staying stale until you next edit the buffer
  • Custom LSP notifications (e.g. clangd's file-status) reach plugins again, instead of being logged and dropped
  • Settings dialog keybindings fire again - a custom binding scoped to the settings context was silently dropped instead of reaching the dialog
  • File Explorer resize divider tracks the pointer accurately, and its grip sits on the correct wall when the explorer is docked on the right
  • Plugin API: killing a background process now actually kills it and reports whether it's still running, instead of leaving it orphaned with its promise never settling
  • Settings: adding an entry to a nested map (an LSP server's env or language_id_overrides) saves into the field it belongs to, instead of being misfiled as a new top-level item on the settings page
  • The status bar underlines a hovered element again - the underline was silently dropped in the move to the current renderer, and every hovered element's colour, plus the separator, stopped following the theme
  • The horizontal scrollbar can reach the end of the widest line - a press at the far end of the track, or a drag of the thumb to it, used to stop short by the gutter's width
  • Clicking a split pane hits the pane you clicked - in a layout without a visible separator, every pane past the first could be off by one column, and a click on the separator or past the last pane landed in the first pane instead
  • Saving keeps the file's identity and touches nothing else - saving foo.txt could delete an unrelated foo.tmp, and saving (including under sudo) changed the file's group and broke hard links and xattrs; very long file names now save too (#3377, #3348 reported by @qs5779, #3409)
  • A file changed on disk is never silently overwritten - Save All, Save and Quit, auto-save and closing a tab with Save skip it and say why, and auto-save on exit waits until you confirm the quit (#3346, reported by @mommysgoodpuppy)
  • "Changed on disk" is reported once, and only for real changes - clock skew on NFS or SMB, or a save's own failed write, no longer counts (#3380, #3403)
  • Quitting - the prompt lists each unsaved buffer and why, cancelling says "Quit cancelled", Save on a tab that needs sudo keeps it open until the save succeeds, restoring a recoverable quit adds no extra [No Name], and counts read "Saved 1 file" (#3400, #3404, #3385, #3401, #3399)
  • Integrated terminal keys reach the program - a terminal-context binding like noop on Ctrl+Q passes the key through (#3270, reported by @brunnerh), Ctrl+J arrives as Ctrl+J rather than Enter (#3169, reported by @liugh-dev), and Shift+Enter and other modified keys reach kitty-protocol TUIs like OpenCode (#3323, reported by @sdecima; #3408)
  • Integrated terminal output - CJK text in scrollback no longer gains extra spaces (#3235, reported by @leonchen2012), a program's last output survives its exit (#3379), and a mouse-tracking program gets each wheel notch once with smooth scroll on
  • Clicks and paging keep the view still - clicking the top or bottom row no longer scrolls, PageUp/PageDown keep the caret's column and screen row, and drag-selecting past the end of the top line no longer scrolls and selects from the wrong place (#3407, #3398, #3329 reported by @asukaminato0721)
  • Clicking past the end of a one-character line puts the caret after it (#3351, reported by @asukaminato0721)
  • The welcome page's cursor sits in the focused text field (#3234, reported by @asukaminato0721)
  • The status bar shows the right line after opening a file that's already open in another window (#3397)
  • TOML: an apostrophe in a comment no longer turns the rest of the file into a string (#3358, reported by @rsramkis)
  • Live Diff shows no false diff for Shift-JIS and other non-UTF-8 files (#3246, reported by @asukaminato0721)
  • Rainbow brackets: the matching pair under the cursor is highlighted on every page (#3084, reported by @Korkman)
  • C++ headers highlight as C++ - a .h in a C++ project, including over SSH, used to highlight and report as C (#3009, reported by @asukaminato0721)
  • Large files with long multi-byte lines no longer panic or save invalid UTF-8 - invalid bytes now show as <XX> (#3285, reported by @CnsMaple)
  • Windows: formatters and shell commands run through cmd /c - they used to fail, or replace the buffer with cmd's banner (#3279, by @CnsMaple)
  • A transient LSP crash no longer pops "Language Server Not Found" or opens log tabs while a restart is pending (#3282, by @CnsMaple)
  • A client whose daemon dies at startup says so and names the daemon's log, instead of spinning at full CPU (#3418)

Internals

  • Minor performance and dependency updates, including a security fix for a TLS library (RUSTSEC-2026-0285)
  • Less allocation churn on the render path - a new memory-profiling harness found two hot spots copying and regrowing tens of MB over a two-minute editing session, and a large batch of explorer decorations no longer re-resolves the project root once per path (#3266, #3103)
  • Scrollbars are one shared implementation, so they behave the same on every surface
  • Plugins can no longer delete, move, or overwrite a path they name. removePath, renamePath and copyPath are gone from the plugin API. removePath checked that its target sat under the temp or config directory, but only the top-level argument — a symlink inside the target walked its recursive delete back out of the fence — and renamePath had no fence at all and fell back to copy-then-delete, so anything removePath refused could be moved somewhere it allowed and deleted from there. What replaced them names a thing rather than a path: scratchCreate/scratchPath/scratchDiscard for staging directories the editor issues and takes back, scratchFromDirectory/installScratch/uninstallPackage for packages by kind and name, and stateSet/stateGet/stateKeys/stateDelete for namespaced storage whose layout the editor owns. Third-party plugins using the old calls will need updating
  • writeFile refuses to overwrite an existing file, as it always claimed to. The docs said it "fails if the file already exists to prevent plugins from accidentally overwriting user data"; the implementation wrote a temp file and renamed it over whatever was there, so a plugin trusting the documentation destroyed the file. Replacing one is now replaceFile, asked for by name
  • editor.setInputMode(name | null) sets an editor-wide input mode for modal-editing plugins like vi, beside setEditorMode, which is now documented as window-scoped. Keys resolve against a focused panel's mode, then the buffer's, the window's editor mode, the input mode, and the base keymap. getInputMode() reads it and input_mode_changed announces changes (#3395)
  • The orchestrator's saved machines moved into the editor-owned state store and are imported from the old directory on first load. The old files are left in place — a plugin can no longer delete a path it names — and are inert

Don't miss a new fresh release

NewReleases is sending notifications on new releases.