What's New
Everything recently shipped, straight from the changelog. For curated announcements and deep-dives, see News.
[0.9.5] - 2026-07-15
Fixed
-
Joining two lines no longer silently loses a breakpoint or bookmark on the lower line. Pressing Backspace at the start of a line (or Delete at the end of the line above) merges the two lines — the code survives, but a breakpoint or bookmark on the merged-up line was being dropped, so you’d set a breakpoint, tidy up the line above, and then debug without it. The marker now follows its code to the joined line.
-
French and Italian error/status messages no longer show a literal
{0}instead of the filename or error. In those languages an apostrophe (l',d',dell') sits right before the value placeholder, and an unescaped apostrophe is a MessageFormat escape that swallowed it — so “save failed: /path” came out as “save failed: {0}”, the actual path gone. 60 messages across French and Italian are fixed (save/open failures, theme changes, plugin/agent/build/export errors), and a test now guards every locale so it can’t come back.
Security
-
Previewing an untrusted Markdown file can no longer probe your internal network or leak credentials. An image reference in the source (
) was fetched with no restriction the moment the preview rendered, so a malicious document could reach cloud-metadata endpoints, internal hosts, or a Windows file share (an SMB credential leak) with no consent. The preview now refuses to fetch loopback, link-local, private, and UNC targets, re-checking each redirect hop; public images and local files still load. -
A malicious file template can no longer write outside the target folder. The single-file “New from Template” path didn’t contain its file-name pattern, so a
../…or absolute name could create a file anywhere writable. It now applies the same containment the multi-file path already used. (Templates aren’t loaded from the opened workspace, so this is defense-in-depth.) -
Format Document no longer corrupts the file if you edit while it’s working. A language server computes formatting against the file as it was when asked; if you typed during the round-trip, those edits landed at the wrong offsets, silently mangling lines. Editora now detects that the file changed and skips the stale result, telling you to run it again.
-
Semantic highlighting no longer briefly mis-colors code while you type. A slow server’s colors, computed against an older version of the file, could paint onto shifted text (a variable colored as a function, the whole overlay off by a line) until the next response caught up. Stale responses are now dropped.
-
A pathological regex in Find no longer freezes the editor. Typing a valid-but-catastrophic pattern (the classic ReDoS case) with regex search on could send the find bar backtracking for many seconds — on the UI thread, so the whole editor locked up with no way out. Regex search is now time-bounded and abandons a runaway match instead.
-
A hostile or fat-fingered
.editorconfigno longer throws when you open a file. An enormous numeric glob range like[{1..99999999999999999999}]crashed the read of any file it governed. It now degrades to matching any number. -
The systemd unit preview no longer throws on an out-of-range time value. A directive like
RestartSec=99999999999999999999(or one that overflows when converted to seconds) now shows the raw value instead of failing to render. -
Closing an image, hex, or PDF tab now actually releases it. Those tabs were only cleaned up when closed by clicking the ✕ — closing them with Ctrl-W, “Close All”/“Close Others”, or by closing the window leaked a live thread and (for a PDF) kept the file open for as long as Editora was running. Open and close a few dozen PDFs and it added up.
-
Closing a window now shuts down everything it started. The diff, external-tool and HTTP-client workers were left running, and an SFTP connection stayed open on the remote host with its SSH client pinned in memory for the rest of the session.
-
Stopping (or quitting during) a run or a build now kills the whole process tree.
npm run dev,mvnand./gradlewall launch a child process; only the wrapper was being killed, so a dev server kept holding its port and a build JVM kept running. Those processes are now tracked, so they’re also cleaned up if Editora exits unexpectedly. -
Auto-save can no longer undo a manual save. Hitting Ctrl/Cmd-S just as a queued auto-save fired let the auto-save land afterwards and put the older text back on disk — while the editor showed the file as saved. Writes to a file are now serialized, and a stale auto-save is dropped.
-
Auto-save no longer overwrites a file that changed on disk. A dirty buffer in a background tab was never checked for outside modification (that check only looks at the tab you’re on), so a timer could silently overwrite a file that a
git checkoutor a generator had just rewritten. Auto-save now skips such a file and leaves the usual “changed on disk” prompt to handle it. -
Saving a file is now crash-safe. Files were written by truncating them first, so an interruption (a crash, a full disk) could leave the document truncated or half-written. The new content is written to a temporary file and moved into place — and the file keeps its permissions (an executable script stays executable) and stays a symlink if it was one.
-
A character your file’s encoding can’t represent is no longer silently turned into
?. With an.editorconfigsettingcharset = latin1, an em dash, a curly quote,€, or an emoji was written to disk as a question mark — and the editor kept showing the real character until you reopened the file. Editora now saves as UTF-8 instead and tells you why. -
Trimming trailing whitespace no longer flips a whole file to Windows line endings. A mostly-LF file containing a single stray CRLF was rewritten entirely as CRLF the first time it was saved with
trim_trailing_whitespaceon. The file’s dominant line ending is now what’s preserved. -
Save-As now applies the destination’s
.editorconfig, instead of writing with the previous location’s charset, line endings, and trim rules (and continuing to do so on every later save). -
A crash or a full disk while saving can no longer wipe out your bookmarks, notes, breakpoints, or the project list. Those files were written by truncating them first and then streaming the new contents in, so an interruption left a half-written file — which Editora then treated as empty on the next launch and promptly overwrote. Every config store is now written to a temporary file and moved into place, so the real file is only ever replaced by a complete one. A config file that still can’t be read is kept as
<name>.corrupt.bakinstead of being silently replaced. -
Running an older Editora twice no longer destroys a newer config. A settings/session file from a newer build is moved aside so the older build can’t clobber it — but if a backup with that name already existed, the move was skipped and the newer file was overwritten anyway, leaving only a stale backup. Backups are now numbered.
-
Deleting a project no longer risks it coming back. A save queued moments earlier could re-create the deleted project’s session file, and because a project’s identity is derived from its folder, re-adding that folder later picked the old session right back up.
-
Pressing Ctrl/Cmd-S on a very large file no longer destroys it. A file of 50 MB or more is deliberately loaded only in part (the first 50 MB — or, for a log, the last 50 MB) and opened read-only. But read-only only blocks typing: Save needs neither an edit nor unsaved changes, and it wrote the buffer straight back over the file — truncating it on disk to whichever slice happened to be loaded, irreversibly. Every write path now refuses a partially-loaded file and says why.
-
Quitting no longer discards the unsaved work and the session of every window except the one you quit from. Quitting from inside the app never ran the other windows’ close handling, so their dirty buffers were dropped with no save prompt and their sessions (open tabs, carets, window bounds) reverted to whatever they were at launch. Quit now prompts and persists each window in turn, and any window’s prompt can cancel the quit. Each window’s services and subprocesses are shut down properly too.
-
--zenand--expertare now genuinely session-only, like--simple. Launching once with either flag wrote the mode into the saved session, so every later launch came up in it — with no hint as to why. The flags now apply the mode for the current run only and leave the saved session untouched (including the docked tool windows the mode closes). Turning the mode on from inside the app still sticks, as before. -
Dragging a folder together with something inside it no longer pops an error dialog. Selecting a folder and a file within it and dropping both on a target moved the folder first (contents and all), then failed trying to move the file from a path that no longer existed. Nested entries are now dropped from the move — the folder carries them along, as it always did.
-
A build-tool toggle that vanished from the build file no longer silently poisons every run. Ticking a Maven profile (or Cargo’s
--release) and then deleting it from thepom.xml— or pointing the window at a different project — left the id active with no checkbox to untick, so-P<profile>kept getting appended to every build, invisibly. Stale toggles are now dropped when the build file is re-parsed. -
The build-tool tasks tree no longer loses your selection and scroll position every time a file is saved, the tab changes, or the window regains focus (each of those re-parses the build file, which was rebuilding the whole tree from scratch).
-
The build-tool tasks tree’s Stop button no longer looks live before anything has run.
-
In the Commit tool window, a file’s status letter and its color can no longer disagree — a file staged as modified but deleted in the working tree showed “M” in the deleted (grey) color. Both now come from the same classification.
-
Closing a project window can no longer throw on the filesystem-watcher thread (a race between
dispose()and the watch loop).
Changed
- The Project tree builds a row’s right-click menu on demand rather than on every cell render, so scrolling a large tree (or any Git-status refresh) no longer allocates a ~20-node menu per visible row.
- The Project tool window now shows a single-letter Git status on each changed file (M / A / D / R / U), matching the Commit tool window. The colors it already had stay (modified = blue, added = green, deleted = gray, renamed = violet, untracked = olive), and the letter is prefixed like the Commit window’s rows. The Commit window’s file rows are now colored with the same palette too, so the two read identically (an unsaved-in-editor file still shows its amber ”•” instead of a Git letter).
Added
- The Project tool window now works like a mini file manager. Multi-select files/folders with Ctrl/Cmd- and Shift-click, then drag them onto a folder to move them (drop onto any folder or the project root; a name conflict in the target is skipped rather than overwritten, and a folder can’t be moved into its own subtree). Open editor tabs follow a moved file — or the files under a moved folder — to their new path. Delete now acts on the whole multi-file selection at once (one confirm), alongside the existing per-file Rename/Delete.
[0.9.4] - 2026-07-13
Added
-
Expert mode — a lighter focus mode than Zen that strips only the window chrome and keeps the whole editor view. A new per-window distraction-free overlay that hides the surrounding chrome like Zen — toolbar, tab bar, breadcrumb, tool stripes, and the whitespace guides — but keeps the full editor view: line numbers, the status bar, the minimap, the column ruler, and the current-line highlight. So you get a focused coding surface that still shows where you are. Toggle it from the palette (View: Toggle Expert Mode),
C-c C-e, Settings → Interface → Modes, the--expertlaunch flag, or the floating “E” button (top-right) to exit — mirroring Zen’s “Z”. Expert and Zen are mutually exclusive; like Zen it’s per-window state and never mutates your saved preferences, so leaving it restores them exactly. -
File Templates can write into a chosen folder. The template creation wizard gained an optional Folder field (with a Browse button): leave it blank to create an unsaved buffer (as before), or pick a folder to write the file(s) into. Invoking “New From Template…” from a project folder’s right-click menu pre-fills that folder.
-
Project tool window: create folders and act on the root. Folder right-click menus gained a New Folder… item, and the project root now has its own right-click menu (New Folder / New From Template / Reveal / Open Terminal / Local History / Git stage·revert) — with Rename omitted on the root so it can’t move the whole project folder.
Changed
-
Build tools (Maven / npm / Cargo / Go / Gradle) now have IntelliJ-style tasks tool windows instead of main-toolbar icons. Each detected build tool gets its own tool-window stripe (shown when the tool’s marker file is found) whose panel is a browsable tree of the tool’s goals / scripts / targets with a mini toolbar (Run / Reload / Stop / Run custom…); double-click or Enter runs a task, and the streaming output goes to a separate per-tool console window (auto-opens on a run). The per-tool icons were removed from the main toolbar; the searchable actions popup is still available from the command palette (
<tool>.showActions/ Maven: Show Actions, …). Everything else — marker detection, wrapper/executable selection, profiles and--release-style toggles, Gradle’s on-demand “Load all tasks…” — is unchanged.tool.<tool>now opens the tasks window; the output console istool.<tool>Output. -
The editor install banner for a missing language server now says “X language server isn’t installed” instead of “X language support isn’t installed” — clearer that it offers the optional LSP (code intelligence), not the language’s render/run tool, which may already work (e.g. the Typst preview renders via the typst CLI even when the tinymist language server isn’t installed).
Fixed
-
The Cargo, Go, and Gradle build-tool tool-window icons showed blank. Their SVG logos (vendored from Simple Icons) used SVGO’s compact packed elliptical-arc flags (e.g.
a1 1 0 000-.5), which JavaFX’sSVGPathparser can’t read — it fails with “invalid boolean flag” and renders an empty shape. Replaced the three with clean Material Design glyphs (a box for Cargo, a running figure for Go, stacked layers for Gradle) that parse and render correctly. A newIconsFxTestguards that every build-tool glyph produces a non-empty shape, so a future un-parseable path is caught. (Maven and npm were unaffected — their paths have no packed arc flags.) -
Installing the Typst language server (tinymist) from the banner/Settings now actually activates it. The extracted
tinymistbinary’s path was never written toSettings.typstLspCommand, so detection kept failing on the PATH default and the install banner reappeared right after the install “succeeded”. Added the missing persist case (InstallCoordinator.applyServerCommand) + a regression test covering every binary-archive server (clangd/kotlin/lua/xml/terraform/typst).
[0.9.3] - 2026-07-10
Added
-
GitHub Actions workflow preview. A workflow YAML (detected by content — a top-level
on:+jobs:— so it works regardless of path and keeps YAML highlighting) gets the same 3-mode view, rendering a plain-English digest instead of the generic YAML tree: the triggers (“push to main, develop”, “pull request to main”,workflow_dispatch→ “manual dispatch”, and aschedule:cron decoded via the cron engine → “on a schedule (…Monday through Friday)”), then each job with its runner,needs/if, and the ordered step list (anactions/checkout@v4step shown as “checkout”, arun:as its command). Pure, unit-testedghactions/core (GithubActionssniff +Workflowparser, reusing the existing YAML + cron support). On by default (Settings.githubActionsPreview); Settings → Editor → GitHub Actions, paletteview.toggleGithubActionsPreview(turning it off falls back to the YAML tree). No new dependency. -
In-app typst-CLI installer. Settings → Languages & Tools → Typst gains an Install… button (and the
install.typstClicommand) that downloads thetypstrenderer binary for your OS/arch from typst’s GitHub releases, extracts it (adds.tar.xzsupport — typst ships xz tarballs), and pointsSettings.typstPathat it, so the document preview lights up without a restart. Distinct from the tinymist LSP-server installer. The button shows Installed whentypstis already detected. -
Typst editing conveniences. For
.typfiles: snippets (a bundledtypst.json— figure, table, grid, equation, import, outline, bibliography, …); Insert Table (a grid size-picker that drops a#table(…)skeleton with the caret in the first cell — on the format bar, right-click menu, andtypst.insertTable); Insert Table of Contents (#outline(),typst.outline); Insert Image (typst.insertImage/ right-click — pick or paste/drop/drag an image, copied into the doc’sassets/and inserted as#image("assets/…")— the Markdown image-paste paths now handle Typst too); and Export the preview to PNG / SVG from the preview right-click menu (nativetypst -f png/-f svg, alongside the existing PDF), plustypst.exportPng/typst.exportSvgcommands. Typst-specific bits live in the pure, unit-testedcom.editora.typst.TypstMarkup. i18n ×6. -
Typst language server (tinymist).
.typfiles now get full LSP code intelligence via tinymist — completion, hover docs, go-to-definition, find-references, a precise document-symbol outline, diagnostics (squiggles + the Problems window), and Format Document — the 22nd server in Editora’s LSP layer (oneServerDef, root markerstypst.toml). It complements the existing Typst rendered preview. Off until the global LSP feature is on; auto-detected onPATH(tinymist lsp), with a per-server enable + command field and a one-click Install… button (downloads the tinymist binary from GitHub releases) under Settings → LSP.Settings.typstLspEnabled/typstLspCommand(schema 72→73). -
Three more config-file previews — systemd units, SSH config, and Dockerfiles — each gets the same 3-mode Editor/Split/Preview view that decodes the file into plain English:
- systemd (
.service/.timer/.socket/…): each directive glossed (“ExecStart→ runs: …”, “Restart=on-failure”, “WantedBy=multi-user.target→ enabled for: …”), and a.timer’sOnCalendar=is decoded into English + the next trigger times (e.g.Mon..Fri *-*-* 02:30:00→ “At 02:30, Monday through Friday” + upcoming runs), including thedaily/weekly/hourly/… shorthands andOn*Sec=monotonic timers via time-span decoding (“15min” → “15 minutes after boot”). - SSH config (
~/.ssh/config,ssh_config): one connection summary perHostblock — “Connects to example.com on port 2222 as deploy, key ~/.ssh/id_ed25519, via jump host bastion” — plus per-option glosses (agent forwarding, keepalive, proxy jump, …). - Dockerfile: a per-build-stage digest — base image, exposed ports, workdir, user, env count, entrypoint/command, health check, and a build-step count — so you can see what an image does at a glance.
All three are pure, unit-tested (
systemd/sshconfig/dockerfilepackages); on by default (Settings.systemdPreview/sshConfigPreview/dockerfilePreview); Settings → Editor + paletteview.toggleSystemdPreview/toggleSshConfigPreview/toggleDockerfilePreview. No new dependency. - systemd (
-
fstab mount preview. An
/etc/fstabfile (already syntax-highlighted) gains the same 3-mode Editor/Split/Preview view. The preview decodes each mount line into plain English — the device spec (UUID=…→ “the filesystem with UUID …”,LABEL=,//nas/share→ SMB/CIFS,host:/export→ NFS), the mount point + filesystem, the comma-separated options (noatime→ “access times not updated”,nofail→ “boot continues even if the device is missing”,uid=1000, …), and the fsck/dump columns (“fsck-checked first (root) · not backed up”). A malformed line (too few columns, non-numeric dump/pass) turns red. Pure, unit-testedfstab/core (Fstabparser +FstabDescribe); no new dependency. On by default (Settings.fstabPreview); Settings → Editor → fstab, paletteview.toggleFstabPreview. -
Typst document preview. Standalone
.typfiles get the same 3-mode preview (Editor / Split / Preview) as Markdown, rendered off-thread via the externaltypstCLI as a multi-page stack — one image per page. Editing updates in place with no flicker (the last good render stays visible while the new one runs; a compile error keeps it visible under a small banner). The document is compiled with--rootset to the file’s folder, so relative#image/#importreferences resolve. Export to PDF (native single file) / PNG / SVG (typst.export), and print paginates the pages. Syntax highlighting via a bundledsource.typstgrammar;//+/* */comments and brace folding. On by default — self-gating on detection, inert untiltypstis found (install via your package manager, e.g.brew install typstorcargo install typst-cli). Toggle (view.toggleTypstSupport) + command path under Settings → Languages & Tools → Typst.- Editing ergonomics on par with Markdown: pressing Enter in a Typst list continues the marker
(
-/+/N.) on the next line and ends the list on an empty item (Backspace clears an empty marker); selecting text pops the same floating format bar (Bold*, Emphasis_, Raw`, Link, Bullet- a heading dropdown), with matching right-click Format items and palette commands (
typst.bold,typst.emph,typst.raw,typst.link,typst.bulletList,typst.headingPromote/headingDemote). Headings use=/==, links insert#link("url")[…]— the purecom.editora.typst.TypstMarkupcore (unit-tested) supplies the Typst-specific pieces; the generic inline-wrap and bullet toggle are shared with Markdown.
- a heading dropdown), with matching right-click Format items and palette commands (
- Multi-file projects resolve. The preview compiles with
--rootset to the nearesttypst.tomlancestor (or the active project root when the file is inside it), so a document deep in a project can#import/#imagefiles above its own folder; relative refs still resolve because the throwaway input is written in the file’s own directory. The preview also caps at 40 stacked pages (a ”… more pages” note; export/print use every page), and remote/SFTP.typfiles fall back to an isolated root so a self-contained one still renders.
- Editing ergonomics on par with Markdown: pressing Enter in a Typst list continues the marker
(
-
Crontab schedule preview. A
crontab/*.cron/cron.d/*file (already syntax-highlighted) gains the same 3-mode Editor/Split/Preview view as Markdown. The preview decodes each job’s terse* * * * *schedule into plain English (30 2 * * 1-5→ “At 02:30, Monday through Friday”), lists the next few fire times computed from now, handles the@reboot/@daily/… macros, and red-flags a malformed line with the exact field error (e.g. minute 99 out of range). Because every field is parsed to describe it, the whole file’s validity is visible at a glance. Pure, unit-testedcron/core (CronExpressionwith the Vixie day-of-month/day-of-week OR-rule +Crontabfile parser); no new dependency. On by default (Settings.crontabPreview); Settings → Editor → Crontab, paletteview.toggleCrontabPreview. -
Cargo, Go, and Gradle build-tool support — completing the build-tool set (Maven + npm shipped earlier). Each detected tool gets its own toolbar icon, actions popup, and streaming console:
- Cargo (
Cargo.toml) — the standard subcommands plus[[bin]]/[[example]]targets and a--releasetoggle. - Go (
go.mod/go.work) — the standardgosubcommands over the whole module. - Gradle (
build.gradle[.kts]) — the common tasks plus an on-demand Load all tasks… that enumeratesgradle tasks; prefers the project’s./gradlewwrapper. Each parses its marker file with existing dependencies (no new libraries); toggle under Settings → Languages & Tools → Build Tools.
- Cargo (
-
npm build-tool support, plus a generalized build-tool framework. The Maven integration is now one tool-agnostic
com.editora.buildframework, so every build tool gets the same first-class treatment: its own toolbar icon (shown only when its marker file is detected), an IntelliJ-style actions popup, and a streaming per-tool console. npm is the first new tool — apackage.jsontoolbar icon whose popup lists everyscriptsentry (run portably as<pm> run <name>) plus common tasks (install, andcifor npm), using the package manager detected from thepackageManagerfield or the lockfile (npm/yarn/pnpm/bun). Discovery parsespackage.jsondirectly (no new dependency). Maven is unchanged in behavior. Toggle each tool under Settings → Languages & Tools → Build Tools (both on by default, inert until their marker file is found). Cargo, Go, and Gradle will follow. -
Every preview can now export to PDF from its right-click menu. Previously only Markdown, CSV, and the Mermaid diagram had a working preview → PDF; the newer previews either had no context menu or silently exported garbage (their content was run through the Markdown renderer). Now:
- SVG rasterizes to a PDF page; DOT/PlantUML render via their CLI to PDF (both were misrouted to Markdown before);
- JSON/YAML/TOML and XML tree previews gained a right-click Export to PDF that snapshots the whole tree (rendered in bounded row-chunks so a large document can’t build a giant image) and lays it across pages;
- Markwhen timelines gained Export to PDF (a snapshot of the timeline/calendar), no longer disabled.
Every preview also gained a matching Print (right-click) — the snapshot previews (SVG, Markwhen, the trees) and DOT/PlantUML paginate their rendered image across pages, mirroring the PDF path (previously only Markdown/CSV/Mermaid could print; the rest silently printed the source through the Markdown renderer). And a snapshot export/print is now forced to a light theme (Primer Light) regardless of the app theme, so a dark-theme user still gets an ink-friendly page. New
pdf/ImagePdfWriter(scale-to-width + slice-tall-across-pages, shared withprint/PrintService.prepareImages) +PdfExportService.exportImages, withEditorBuffer.snapshotPreviewChunks(lightUa)doing the off-screen render/snapshot. No new dependency (PDFBox was already bundled). Note: a tree/timeline/SVG/diagram PDF or print is a light raster snapshot; Markdown/CSV stay native-vector. -
Portable Linux install tarball (
Editora-<version>-linux-<arch>.tar.gz, x64 + arm64). A new release artifact for systems without.deb/.rpm: it bundles the same self-contained, AOT-trained app image the installers are built from (a jlink’d Java runtime + the nativebin/Editoralauncher — no system Java needed) plus a POSIXinstall.shthat installs to/opt/editorawhen run as root or~/.local/editoraas a regular user, wiring up aneditoracommand onPATHand an application-menu entry (StartupWMClass=com.editora.Appso the running window inherits the icon). Supports--system/--user/--prefix DIR/--uninstall; the image also runs in place (./Editora/bin/Editora) without installing. Built by the newscripts/build-tarball.shfrom the existingtarget/aot-image/Editora— the extract-and-install counterpart to the.AppImage, reusing the jpackageAPP_IMAGEoutput (jlink under the hood), so nopom.xmlchange and no second build. -
XML tree preview.
.xmlfiles (xsd/xsl/fxml/pom/wsdl/rss/…) join the structured-data preview with a collapsible DOM tree in the 3-mode view — each element shows its tag + inline attributes, children in document order, and text-only elements inlined astag = "value". A faithful DOM model (elements/attributes/text/comments), not a lossy XML→JSON shoehorn. Parsed off-thread with the JDK’s own DOM parser (no new dependency), hardened against XXE exactly like the Maven pom parser (DOCTYPE disallowed, external entities/DTD off). New purestructured/XmlNode+structured/XmlParser(unit-tested, incl. an XXE-blocked case) +editor/XmlTree; reuses the existing structured-preview hosting and the sameSettings.structuredPreviewtoggle (relabelled JSON/YAML/TOML → JSON/YAML/TOML/ XML)..svgstill renders as an image (excluded from the tree);.xhtmlstill uses the browser preview. Deferred: specialized dialect renderers (RSS/Atom feed view, like OpenAPI for JSON), attribute/text search. -
PDF viewer.
.pdffiles now open in a read-only page viewer (rasterized via Apache PDFBox — already a dependency, for PDF export) instead of the hex viewer. A top bar navigates pages (◀ Page N of M ▶) and zooms (out / in / fit-to-window / actual-size;Ctrl+wheel too). Pages are rasterized one at a time, off the FX thread on a single daemon thread (PDFBox’sPDDocumentisn’t thread-safe, so all document access is serialized there), so only the current page’s image is held — bounding the Prism texture pool. Newui/PdfViewerPane implements TabContent(mirrorsImageViewerPane); routed inopenPathbefore the hex fallback and round-tripped through session restore +tabPathlike the image/hex viewers. Works for local and remote (SFTP) PDFs; refused above 128 MB. No new dependency. Deferred: continuous page scroll, a page-jump field, text selection/search, password- protected PDFs. -
SVG image preview.
.svgfiles stay editable XML (with XML highlighting + LSP) but now gain a rendered-image preview in the same 3-mode view (Editor / Editor + Preview / Preview) as Markdown — edit the source, see the image re-render live. Rasterized off-thread via JSVG (already bundled for Markdown badge images, so no new dependency) and cached by source hash; the preview zoom resizes the image. Neweditor/SvgImages(async render cache, mirroringMermaidImages/DiagramImagesbut in-process — no external tool);EditorBuffer.isSvg()/hasSvgPreview()+ ascheduleRenderPreview()branch. On by default,Settings.svgPreview(schema 64→65) — Settings → Editor → SVG + paletteview.toggleSvgPreview. Deferred: export-as-PNG from the preview, a checkerboard backdrop for transparent SVGs. -
Structured-data preview for JSON / YAML / TOML, with OpenAPI/Swagger docs.
.json,.yaml/.yml, and.tomlfiles now get the same 3-mode preview (Editor / Editor + Preview / Preview) as Markdown: a collapsible, type-colored data tree rendered off-thread. A JSON/YAML document detected as an OpenAPI 3 / Swagger 2 spec instead renders as browsable API docs (title, servers, endpoints with colored method badges + params + responses, schemas), with a tree ⇄ API-docs toggle (structured.toggleView). New pure packagecom.editora.structured(StructuredNode/StructuredParser/OpenApiModel/OpenApiParser— all Jackson stays here, unit-tested)editor/StructuredTree+editor/OpenApiDoc(self-scrolling nodes hosted the CSV-grid way, so theTreeViewkeeps virtualization). Render-branch model (no coordinator, like Markwhen); a 50k-node cap guards pathological files. On by default,Settings.structuredPreview(schema 63→64) — Settings → Editor → Structured data + paletteview.toggleStructuredPreview. Adds a dependency: jackson-dataformat-yaml (+ snakeyaml 2.4; both real JPMS modules, no moditect). Deferred: GeoJSON map, jump-from-tree-to-source-line, per-file view persistence, expand/collapse-all, editing.
-
Diagram-as-code preview for Graphviz DOT and PlantUML. Standalone
.dot/.gvand.puml/.plantuml/.pu/.iumlfiles now get the same IntelliJ-style 3-mode preview (Editor / Editor + Preview / Preview) as Markdown and Mermaid: the whole buffer renders to an image via an external CLI (dot/plantuml), off the FX thread, cached by a hash of the source so an unchanged re-render is a cheap re-fit; the preview zoom resizes the image and a diagram exports to SVG / PNG / PDF (diagram.export). Both tools rasterize to PNG natively (no headless browser, unlike Mermaid’s mmdc). New generic, Mermaid-independent seam: pure packagecom.editora.diagram(DiagramKind/DiagramRenderer/DiagramService, the CLI facade + per-kind argv builders, unit-tested)editor/DiagramImages(the async render cache, mirroringMermaidImages) +ui/DiagramCoordinator(theCoordinatorHostfeature-coordinator pattern). Authored compactgrammars/dot.tmLanguage.json+grammars/plantuml.tmLanguage.jsonfor highlighting, plusLanguageRegistry/GrammarRegistry/Commenter/FoldRegionsentries. On by default — self-gating on detection, so it stays inert untildot/plantumlare found (install via your package manager, e.g.brew install graphviz plantuml); toggle + tool paths under Settings → Languages & Tools → Diagrams.Settings.diagramSupport/dotPath/plantumlPath(schema 62→63, additive). Commandsdiagram.export,view.toggleDiagramSupport,diagram.setDotCommand,diagram.setPlantumlCommand. Mermaid’s shipped path is untouched (the two share only theeditor/DiagramImagesrender-cache convention). Deferred: PlantUML@start/@endblock folding, a themed (dark) render, live linting, and the browser-raster diagram tools (D2, WaveDrom).
Changed
- CSV/TSV grid preview is now an in-editor Editor/Split/Preview view, mirroring the Markdown preview.
The spreadsheet grid for
.csv/.tsvfiles moved out of the bottom tool window into the same IntelliJ-style floating Editor / Split / Preview toggle that Markdown uses (top-right of the editor): Split shows the source and the live grid side by side, Preview shows the grid alone. The view mode is remembered per file. The grid keeps everything it had — the spreadsheet rendering, the “First row is a header” toggle, the row/column type profiler, sort/filter, ragged-row diagnostics, in-cell editing (writes back to the source line), click-a-cell → jump to the field, and the PDF / Excel (.xlsx) / ODF (.ods) / print exports. The oldcsvGridbottom tool-window stripe and itstool.csvGridcommand were retired; the Settings → Editor → CSV toggle and theview.toggleCsvGridpalette command now gate the in-editor preview.
Fixed
- Structured / XML tree preview no longer recolors its text when you select a row. The JSON/YAML/TOML
and XML preview rows are
TextFlows of coloredTextruns, whose fill AtlantaFX flips to the selection foreground on a selected cell — so clicking a line made the syntax-colored tokens visibly flash to the selection color and back (a “redraw”). Added selected-state CSS rules pinning each token’s-fx-fillto its normal color (the same fix.completion-listalready uses for itsTextFlowlabels), so selection only changes the row background now. CSS-only. - Spell-check and Personal-Notes overlays no longer pin an idle full-viewport GPU texture. Both
SpellCheckOverlayandNoteHighlightOverlaykept theirCanvassized to the whole viewport whenever the feature was on (the default), even on a buffer with zero visible misspellings/notes — retaining a viewport-sized RTTexture that other overlays (Todo/Search/LSP/…) already release. They now shrink the canvas to 1×1 when there’s nothing visible to paint and grow it back on demand (with no per-scroll size flapping), matching the established overlay convention — trimming idle Prism-texture footprint on every spell-checked/annotated buffer. Purely a resource fix; no visible behavior change. - Structured / XML tree preview no longer blanks silently on a render error. The off-thread
PREVIEW_POOL.submit(...)tasks that parse a JSON/YAML/TOML/XML buffer now catchThrowable(not just the parser’sException), so anErrorsuch as aNoClassDefFoundError/LinkageErrorwhile loading a Jackson format factory on the pool thread is surfaced as a visible error label in the preview + aWARNINGin the Debug Log, instead of being swallowed into the task’sFutureand leaving a blank pane. Aligns these two branches with the project’s “catchThrowableinexec.submittasks” convention.
[0.9.2] - 2026-07-08
Added
- Maven support — a toolbar icon + actions popup that reads the active project’s pom.xml, IntelliJ-style.
A new Maven toolbar button (hidden until a pom.xml is actually detected for the active file/project;
refreshed on tab switch, window focus-regain, and save) opens a searchable popup listing the standard
lifecycle phases (clean, validate, compile, test, package, verify, install, site, deploy), the pom’s
declared
<profiles>(checkable — composing with a run via-P<id>, without closing the popup — and marking anyactiveByDefaultone), and each declared<build>/<plugins>entry’s explicitly-bound<executions>goals as<prefix>:<goal>rows (e.g.spotless:check,jacoco:report) using Maven’s own plugin-prefix naming convention; profile-scoped plugins (<profiles>/<profile>/<build>/<plugins>) are nested under their own profile once checked. Discovery is pom.xml-only — parsed directly with the JDK’s own hardened DOM parser (java.xml, XXE disallowed outright; no third-party XML dependency, no shelling out tomvn help:effective-pom) — so it’s instant and offline; a plugin with no<executions>is omitted from the goal list (the “Run custom goal(s)…” freeform prompt covers those). Runs prefer the project’s own./mvnw/mvnw.cmdwrapper when present, falling back tomvnresolved on PATH (or a Settings override), streaming output to a dedicated Maven console tool window (default-hidden, auto-opens on a run). New pure packagecom.editora.maven(PomModel/PomParser/MavenPluginPrefix/MavenLifecycle/MavenExecutable/MavenArgs) +MavenService(mirrorsRunService’s streaming shape)ui/MavenPanel/ui/MavenActionsPopup/ui/MavenCoordinator. Gated bySettings.mavenSupport(default on — inert until a pom.xml is found) +Settings.mavenCommandoverride (schema 61→62), with a Settings → Languages & Tools → Maven page; off in Simple UI mode and for remote (SFTP) files. Palette commandsview.toggleMavenSupport,maven.showActions,maven.runCustom,maven.stop,maven.rerunLast,maven.refresh,tool.maven. Also generalizesOverlayHostwith ashowBelow(...)variant (mirroring the existingpositionAbove) so a popup can drop below a top-of-window anchor like a toolbar button, rather than only above a status-bar-style anchor.
- A dedicated “AI” group in Settings, with a master Enable/Disable AI switch (off by default).
The AI Agent and AI Actions pages — previously scattered under separate groups — now live together
under a new AI sidebar group, alongside a new landing page holding a single master checkbox
(
Settings.aiEnabled, schema 60→61 additive). Turning it off disables every AI feature regardless of the AI Agent / AI Actions pages’ own settings below it: the AI Agent chat window, commit-message generation, explain/rewrite selection, and inline ghost-text completion —AgentCoordinator.isEnabled()andAiCoordinator.isEnabled()both now gate on it. It does not affect the MCP server (a separate feature). Also a palette command,view.toggleAiEnabled(“Toggle AI”). Defaults to off, so a fresh install has no AI usage until explicitly turned on. - Clickable links in the Markdown preview. A rendered link now shows a hand cursor and opens in the
system default browser on click (the same
Consumer<String>already used by Ctrl/Cmd-click-in-source and the Welcome page’s links), rather than being inert.MarkdownRenderer.renderDocumentgained an overload taking an optional click handler; the print/PDF writers and the hover/completion-doc popups keep using the existing no-handler path, so only the interactive live preview is affected. - Loading indicator while an AI explanation streams into the Markdown preview.
ai.explainSelectionopens a newexplanation.mdbuffer in Preview mode and streams the response in, but the preview’s re-render is debounced ~250ms after the text stops changing — a fast, continuous stream can go the whole generation without a quiet gap, leaving the preview blank with no feedback until it finally finishes.EditorBuffergained a centered spinner + message overlay (setPreviewLoading), shown from just before the request starts until it ends (success or error).
Changed
- The gutter Run play glyph is 20% bigger (scale 1.014 → 1.217), making the clickable green run target easier to see and click.
Fixed
-
Windows installs failed to launch: “Failed to find JVM in …\runtime directory”. The AOT-cache build step (
scripts/aot_build.java) strips the bundled runtime’sbin/after training to reclaim footprint — safe on macOS/Linux, where the JVM shared library lives inruntime/lib/server/andbin/holds only launcher executables. But the Windows JDK layout puts the JVM itself insidebin/(runtime\bin\server\jvm.dllplus the bootstrapjli.dll/java.dll/jvm.cfg), whichEditora.exeloads from there — so deletingbin/removed the JVM, and every Windows install since the AOT cache landed failed to start. Thebin/strip is now guarded to non-Windows only; the Windowsbin/is kept intact (its launcher exes are a negligible footprint). -
AI Agent header buttons (History / New Session / Stop) took up too much width. They’re now icon-only (matching the Debug panel’s icon-only toolbar buttons), with the translated label moved to a tooltip instead of being lost — hover to see what each does.
-
CSV grid preview could stay open on a non-CSV file after a restart. If the CSV/TSV grid tool window was left open in the previous session, it was force-reopened at startup (session restore) before the first real buffer loaded — at that point every buffer-gated tool window is provisionally marked “unavailable”, so the reopened window’s tracked availability and its actual open state diverged. The very next gating pass (on the restored tab) saw the tracked flag as already matching and skipped closing it, leaving the grid visibly open over a file that wasn’t CSV until some other state change happened to flip it.
ToolWindowManager.setAvailablenow always reconciles the actual open/closed + stripe-button state instead of short-circuiting when its tracked flag already matches, so it self-heals regardless of how it got out of sync (mirrors the same gating used by the Commit/Markdown-Lint/TODO windows).
[0.9.1] - 2026-07-06
First tagged release. (v0.9.0 was cut and pushed first, but a JReleaser misconfiguration published it
as an immutable GitHub Release before the asset uploads failed; GitHub permanently reserves a tag once
it has backed an immutable release, even after both the release and the tag are deleted, so v0.9.0
itself can never be reused on this repository — see the fixes below. This release ships as 0.9.1
instead.)
Fixed
-
macOS release build failure on a pre-1.0 version. jpackage rejects an
--app-versionwhose first number is zero or negative, so a0.x.ypom.xmlversion (like this release’s0.9.1) failed the macOS legs of the release build with “The first number in an app-version cannot be zero or negative” — on both the app-image build and the DMG wrap step, since jpackage enforces the rule on every invocation. The macOS build now passes jpackage a bumped placeholder (a leading0.to1., e.g.1.9.1) purely to satisfy that CLI check, then immediately rewrites the built app’sInfo.plist(CFBundleVersion/CFBundleShortVersionString) back to the true version before wrapping the DMG — so the placeholder never reaches the delivered app: Finder “Get Info”,mdls, System Settings, and the app’s own--version/About dialog all correctly show0.9.1. Linux and Windows are untouched (jpackage accepts a leading-zero version there). -
Release job failed to check out branch
main. JReleaser defaults to a branch namedmainwhen none is configured; this repo’s default branch ismaster, so the final “Release” job failed with “Could not checkout branch main” even after every platform’s installer build succeeded.jreleaser.yml’srelease.githubnow setsbranch: masterexplicitly. -
Release job failed with “Cannot upload assets to an immutable release.” GitHub’s Immutable Releases feature (GA October 2025) rejects any asset upload once a release is published, but JReleaser’s default flow publishes the release before uploading each installer — so every asset upload 422’d and the whole release failed after all five platform builds had already succeeded.
jreleaser.yml’srelease.githubnow setsimmutableRelease: true, which makes JReleaser create the release as a draft, upload every asset while it’s still a mutable draft, then publish — the flow GitHub’s own docs prescribe. This is mutually exclusive with the previousoverwrite: true(removed), so re-running on the same tag now needs the prior release deleted first (gh release delete v<version>) rather than overwritten in place. -
AI connection check no longer hangs on a slow endpoint. The Settings → AI Actions health check is now a non-streaming request whose 30 s timeout bounds the entire exchange — so a local server that sends response headers immediately but is slow to produce the first token (LM Studio warming up a model) can no longer leave the status stuck on “Checking connection…”. It resolves to green Connected or a red Not working: … (with the HTTP error / “timed out”). Real streaming AI requests also gained a 120 s time-to-first-response cap (unchanged for a healthy generation).
Added
-
Open a
.patch/.difffile in the structured diff viewer. A right-click “Open in Diff Viewer” item (shown only for a.patch/.difffile, plus the palette-onlydiff.openPatchFilecommand) parses the active buffer’s unified diff and opens its first file-section as a read-only side-by-side diff — with word-level highlighting and prev/next-change navigation — instead of just plain syntax-highlighted diff text. Works on the buffer’s own (possibly unsaved) text; a multi-file patch shows a status note and just the first file. -
Configurable TODO per-part colors. The highlight color for a structured TODO comment’s
[tag]and each(priority)level (critical/high/medium/low) is now user-configurable — a TODO Part Colors section in Settings → Editor → TODO with five color pickers, plus a palette commandtodo.setPartColor(pick a part →#RRGGBB). Applies live to every buffer. -
AceJump line-mode (avy-goto-line). A new
nav.aceJumpLinecommand (M-g L) labels every visible line at once — type a label to jump the caret to that line’s first non-whitespace character. Complements the existing character-mode AceJump (M-g j). -
MCP:
list_tabsandtodo_scantools. The MCP bridge gains two read tools —list_tabs(every open tab including non-editor tabs — Welcome/image/hex/diff — with each tab’s type + which is active) andtodo_scan(scans the project + open buffers for the configured TODO/FIXME highlight patterns, returning file/line/col/keyword/tag/priority/text) — bringing the tool count to fourteen. -
External Tools: editor right-click submenu. The editor context menu now shows an External Tools submenu listing every enabled tool, so you can run one on the active file without the palette or a keybinding (hidden when no tool is enabled or in Simple UI mode).
-
Find-in-Files popup: include/exclude globs. The keyboard-first Find in Files popup now has the same comma-separated include / exclude glob fields as the tool window (e.g.
*.java, src/**/**/test/**), narrowing the search live as you type. -
Remote-file (SFTP) status indicator. When the active file lives on a remote host, the status bar now shows a ⇅ SFTP badge (with the
host:/pathin its tooltip; click it to manage connections), so it’s clear at a glance that you’re editing over SFTP. (The tab already showed a cloud icon + path tooltip.) -
Macro recording indicator. While recording a keyboard macro (
F3), the status bar now shows a red ● REC badge so it’s obvious you’re recording; click it to stop (orF4). -
Log viewer: jump to next/previous error. Two palette commands — Log: Next Error / Log: Previous Error — move the caret to the next/previous line at WARNING level or higher in a
.logfile (wrapping around), so you can hop between problems without scrolling. -
AI connection status (green/red). The Settings → AI Actions page now shows a live health-check indicator under the enable checkbox: green Connected when the configured provider/endpoint/key (and model) actually accept a request, red Not working: … with the error otherwise (a wrong endpoint path, a refused connection, a bad key, an unknown model). It checks when the page opens and re-checks — debounced — after you edit the provider, endpoint, key, or model, so a misconfigured local server (LM Studio/Ollama) or a typo’d URL is obvious immediately instead of failing silently at first use. Also a palette command AI: Test Connection that reports to the status bar. The probe is a tiny one-token request and never disturbs a real generation.
-
View: Open as Text — a palette command that opens the current file as plain text, bypassing the image/hex viewer. The counterpart to Open as Hex: use it when a text file is mis-detected as binary, or to read a binary’s raw text.
-
Local LLMs for the AI features (LM Studio, Ollama, vLLM). The AI actions and inline completion can now talk to a local OpenAI-compatible server instead of the Anthropic API — pick Local (OpenAI-compatible) under Settings → AI Actions → Provider. The default endpoint is LM Studio’s local server (
http://127.0.0.1:1234/v1/chat/completions) and no API key is required; the endpoint is configurable for Ollama/vLLM/llama.cpp. Leave the model fields blank to use whatever model the server has loaded, or name one explicitly. The OpenAI chat-completions streaming dialect (SSEdata:chunks →[DONE],choices[].delta.content,finish_reason) is handled alongside the Anthropic Messages format; commit-message generation, explain/rewrite selection, and ghost-text completion all work against either. Palette: AI: Set Provider / AI: Set Endpoint. Schema 56→57, additive. -
AI inline completion (ghost text). After a short typing pause with the caret at the end of a line, a muted single-line AI suggestion appears at the caret — Tab accepts it, Esc or typing dismisses it (the same ghost-text presentation prose autocomplete already uses). Requests are strictly bounded: one per ~600 ms idle pause, a windowed prefix/suffix prompt, a one-line stop sequence, and a small output cap — and each keystroke supersedes the in-flight request, so stale suggestions never appear. Runs on its own fast model (default
claude-haiku-4-5, configurable) independent of the AI-actions model, and never competes with the local snippet/dictionary popup. Off by default (Settings → AI Actions → “Enable AI inline completion”; palette “Toggle AI Inline Completion”); needs the AI Actions feature + an API key; inert in large files and read-only buffers. -
AI actions — one-shot Anthropic-API features, no agent required. Three streamed, palette-driven actions that call the Anthropic Messages API directly (a hand-rolled
java.net.httpclient — no SDK, no new dependency): AI: Generate Commit Message turns the staged diff into a commit message and drops it into the Commit window for review; AI: Explain Selection streams an explanation of the selected code into a new Markdown buffer; AI: Rewrite Selection… rewrites the selection per your instruction as a single undoable edit (aborted safely if the buffer changed while generating), and AI: Cancel stops an in-flight generation. Off by default (Settings → AI Actions, Beta) with a configurable model (defaultclaude-opus-4-8) and an API key fromANTHROPIC_API_KEYor a Settings override (stored in plain text — the environment variable is preferred). Separate from (and complementary to) the AI Agent chat window. -
Auto Close Tags. Typing the
>that completes an HTML/XML open tag now inserts the matching closing tag after the caret (<body+>→<body>|</body>), with the caret left between the tags. Closers, self-closing/>, doctypes/comments, HTML void elements (<br>,<img>, …) and a>typed inside an attribute string are all left alone. Works in macro replay too. On by default for.html/.xml-family files (Settings → Editor, palette “Toggle Auto Close Tags”). -
Embedded AI agent (ACP) — chat with Claude Code inside Editora. A new AI Agent tool window drives any Agent Client Protocol agent over stdio — the default command is
claude-code-acp(Claude Code’s ACP adapter;npm i -g @zed-industries/claude-code-acp), and Gemini CLI or any other ACP agent works by changing the command. The transcript streams the agent’s replies and tool activity; Enter sends, Shift+Enter inserts a newline; Stop cancels a turn and New Session starts fresh. The integration is editor-aware: the agent’s file reads serve open buffers’ unsaved text, and its edits to open files apply as undoable buffer edits (oneC-zreverts; you review and save) — writes to files you don’t have open go to disk and refresh the Project tree. Agent permission requests pop a dialog with the agent’s own options. Off by default (Settings → AI Agent, Beta) with a configurable command; the agent process is tracked like the LSP servers (killed on window close / app exit, reaped after a crash). Commands: AI Agent window toggle, Agent: New Session, Agent: Stop, Toggle AI Agent, Agent: Set Command. -
MCP server: six new tools — an AI agent can now edit, save, and navigate, not just read. The embedded Model Context Protocol server grows from six to twelve tools:
edit_bufferapplies an undoable text edit to an open buffer (exact-match str-replace — the old text must occur exactly once unlessreplace_all— or a whole-buffer rewrite; oneC-zreverts it),save_bufferwrites an open buffer to disk (untitled buffers are refused rather than popping a Save-As dialog),open_fileopens a file and optionally jumps to a line/column,get_selectionreports the active buffer’s caret and selection,document_symbolsreturns a file’s LSP symbol outline, andgit_statusreports the branch, ahead/behind counts, and changed files. The security-notice dialog now discloses that a connected agent can edit and save files, not just read them. Everything stays loopback-only + bearer-token-gated and off by default (Settings → MCP Server). -
Auto Rename Tag (the VS Code behavior). Editing an HTML/XML tag name now renames the paired open/close tag live as you type — nested and same-name sibling tags pair correctly, real-world HTML’s unclosed optional-close tags (
<li>,<p>, …) don’t break the pairing, comments, CDATA, doctypes, quoted attribute values, self-closing tags, HTML void elements (<br>,<img>, …) and<script>/<style>content are all skipped, and an old-name guard makes sure typing a brand-new tag never renames an unrelated one. On by default for.html/.xml-family files (Settings → Editor, palette “Toggle Auto Rename Tag”); inert in read-only and very large files. -
String manipulation commands (the JetBrains String Manipulation plugin family). Case-style conversions on the selection — or the identifier at the caret — to camelCase / PascalCase / snake_case / SCREAMING_SNAKE_CASE / kebab-case / dot.case, plus Cycle Case Style (repeated invocations step the same token through the styles) and Swap Case; and whole-line transforms on the selected lines (or the whole file): sort ascending/descending (numeric-aware —
file2beforefile10— and case-insensitive), sort by length, reverse, shuffle, remove duplicate lines, remove empty lines, and trim trailing whitespace. Every operation is its own palette command and key-bindable; Edit: String Manipulation… (C-c x) opens one filterable picker over all of them. Each transform applies as a single undoable edit and re-selects its result. -
Hex viewer for binary files. Opening a binary file (an executable, archive,
.class,.pdf, …) now shows a read-onlyoffset | hex | ASCIIdump instead of dumping its bytes as garbage text — completing the image-viewer story. Binaries are detected by content (a NUL byte / high control-character density, with a BOM guard so UTF-16 text isn’t misrouted), large files show their first slice with a truncation note, and the View: Open as Hex command force-opens any file’s bytes as hex. Hex tabs restore across sessions. -
More default TODO keywords + structured comments (Comment Manager). The built-in TODO highlight keywords now include HACK, NOTE, XXX and DONE alongside TODO/FIXME (existing custom patterns are kept; the defaults grow on upgrade). Editora now recognizes the IntelliJ-style
KEYWORD [tag] (priority) descriptioncomment format — e.g.// TODO [auth] (high) fix token refresh— and colors each part in the editor: the keyword in its pattern color, the[tag]in blue (underlined), and the(priority)by level (critical→red, high→orange, medium→amber, low→cyan). The TODO tool window now shows each match with those parts colored and a “Group by” selector — File, Priority, Tag, or Keyword — so you can, for example, see every critical item across the project together. Right-click a match to edit it directly in your source: Mark Done (rewrites the keyword toDONE) / Reopen, set the priority, or edit the description — each applied as one undoable edit to the file. -
Image viewer. Opening a raster image (
.png,.jpg/.jpeg,.gif,.bmp) now renders the picture in a read-only tab instead of dumping its binary bytes into a text buffer. A small top bar zooms out / in / fit-to-window / actual size (and Ctrl+mouse-wheel zooms); large images scroll, small ones center. SVG is deliberately left as editable text. Image tabs are restored across sessions like any other tab. -
Filter/search box focused on open. Opening the Project, Structure, Bookmarks, or Personal Notes tool window now puts keyboard focus on its filter/search box so you can type to filter immediately; Down moves into the results and Enter opens the selected (or first) one.
-
Project filter skips .gitignore. The Project tool window’s filter now skips
.gitignored files and folders (target/,node_modules/,*.log, …) by default — governed by the same “respect .gitignore” search setting. The full project tree still shows every file. -
Active file first in Problems & TODO. In the Problems and TODO tool windows, the file you’re currently editing now sorts to the top of the list, so its diagnostics / TODOs are the first thing you see (matching the IDE convention). Re-orders automatically as you switch tabs.
-
macOS “Open With” support. The packaged
Editora.appnow registers as a handler for common text and source files, so Editora appears in Finder’s right-click Open With menu (and the “Always Open With” picker). Opening a file that way launches or focuses Editora and opens the file — even though macOS delivers it as an Apple Event rather than a command-line argument. It’s offered as an alternate handler, so it doesn’t take over your existing default editor. (MoveEditora.appto/Applicationsso macOS registers it.) -
More plain-text developer formats. Syntax highlighting for unified diffs (
.diff/.patch— added/removed lines tint green/red, hunk headers stand out), Makefiles (Makefile/GNUmakefile/.mk), justfiles, Protocol Buffers (.proto), GraphQL (.graphql/.gql), and.gitattributes(including a repo’s.git/info/attributes). Java.propertiesfiles get a dedicated grammar (:and=separators, Unicode escapes, backslash line-continuations) instead of borrowing the INI one. Proto and GraphQL also fold on braces and auto-indent like C-family code, and comment toggling (M-;) works in all of them. -
Compare / Annotate in the Project tree. The Project tool window’s file Git submenu now also offers Compare with Branch… (diff the file against its version on any local or remote branch), Compare with Revision… (diff against a commit from the file’s history), and Annotate (open the file and show inline Git blame) — alongside the existing Compare with HEAD.
-
Git actions in the Project tree. Right-clicking a file in the Project tool window now offers a Git submenu with Stage, Unstage, Revert… (discard changes — untracked files are removed, tracked files reset to HEAD, with a confirmation), and Add to .gitignore, alongside Compare with HEAD and Show File History. Folders offer Stage and Revert… of the whole subtree. Actions enable or disable based on the file’s real Git status, and the whole submenu is hidden when the folder isn’t a Git repository.
-
Git status colors in the Project tree. Files in the Project tool window are now colored by their Git status — added (green), modified (blue), deleted (gray), renamed (violet), and untracked (olive) — and a folder that contains changes is tinted, just like IntelliJ. Updates as you edit, stage, commit, or switch branches; rides the Git integration (nothing to turn on).
-
References tool window. Find References now shows its results in a dedicated, browsable References tool window — grouped by file, with a line + preview per reference — instead of a one-shot picker. Enter or double-click jumps to a reference; if there’s only a single reference, it jumps straight there.
-
Go to Symbol in Workspace. A new command opens a live search box that finds any symbol (class, function, method, …) across the whole project by name and jumps to it — the LSP
workspace/symbolfeature, like VS Code’sCtrl-T/ IntelliJ’sCtrl-N. Available from the command palette in a code file with a running language server. -
Ctrl/Cmd-click to go to definition. In a code file with a running language server, holding Ctrl (or ⌘ on macOS) and clicking a symbol now jumps to its definition — the same gesture as VS Code / IntelliJ. It reuses the existing Go to Definition, so it also works across files and records a jump you can step back from (Back /
nav.back). -
Markdown table: export to CSV / Excel / ODF file. The editor’s right-click Table submenu (and the command palette) now export the Markdown table under the cursor to a
.csv, Excel.xlsx, or OpenDocument.odsfile — alongside the existing “Copy as CSV” (clipboard). -
CSV grid: columns fit their content. The CSV Grid now sizes each column to the width of its widest cell (or header), so you rarely need to drag a column edge to read the data. Very long values are capped so one giant field can’t push the rest of the table off-screen, and you can still resize any column by hand.
-
CSV: align / shrink columns. Two new commands reformat a
.csv/.tsvfile so it’s easy to read as a table: CSV: Align Columns pads each field with spaces so the delimiters line up in the editor, and CSV: Shrink Columns removes that padding again (fully reversible). Both preserve quoted fields, blank lines, and the trailing newline, and are undoable. (Files with a quoted field that spans multiple lines are left alone, since aligning them would split a record across lines.) -
Rainbow CSV columns.
.csv/.tsvfiles are now colored per column in the editor — each column gets a distinct color that cycles every eight, so field boundaries are easy to follow (like VS Code’s Rainbow CSV). On by default; toggle in Settings → Editor → CSV or the Toggle Rainbow CSV Columns command. -
CSV grid: sort, filter, and inconsistent-row highlighting. The CSV Grid now has a filter box that live-filters rows as you type, a right-click Sort Ascending / Descending / Clear Sort on any column (numbers sort numerically), and rows whose field count doesn’t match the header are tinted red with an “N inconsistent” count in the summary line — so ragged CSVs are easy to spot. Sorting and filtering only change the view; editing a cell and jumping to the editor still land on the right line.
-
CSV grid: export menu. Right-click the CSV Grid to Export to PDF, Print, Export to Excel (.xlsx), or Export to an ODF spreadsheet (.ods) (also available as palette commands). Numbers are written as real spreadsheet numbers where safe — leading-zero codes (like ZIP codes) and thousands-separated values stay text so they aren’t corrupted — and the header row is bold.
-
CSV grid: editable headers + spreadsheet polish. Column headers in the CSV Grid are now editable — double-click a header, type a new name, and it’s written back to the file’s first row. The grid also got a more spreadsheet-like look: a ”#” row-number gutter, zebra striping, bold headers, and right-aligned numeric columns.
-
CSV / TSV syntax highlighting.
.csvand.tsvfiles now open as a proper “csv” language instead of plain text: quoted fields are colored as strings (with the""escape), bare numeric fields as numbers, and the field separators (comma / semicolon / tab / pipe) get a muted tint so column boundaries stand out. First step of broader CSV support (a column readout, a table/grid preview, and grid editing are planned next). -
CSV / TSV column readout. For a
.csv/.tsvfile the status bar shows a “Field N of M” segment — which column the caret is in and how many the row has (RFC-4180 quote-aware, delimiter auto-detected). Clicking the segment, or the new CSV: Copy as Markdown Table command, copies the whole file to the clipboard as a GitHub-flavored Markdown table. -
CSV / TSV grid preview. A new CSV Grid tool window shows the active
.csv/.tsvfile as a read-only spreadsheet: a first-row-as-header toggle, a summary line (row/column counts + how many columns are numeric vs. text), and double-click / Enter on a cell jumps the editor caret to that field. Refreshes as you edit; on by default for CSV/TSV files (Settings → Editor → CSV, or the Toggle CSV Grid Preview command). -
Editable CSV grid. The CSV Grid is now editable: double-click a cell, type a new value, and it writes back to the file (undoable, with proper RFC-4180 quoting). Editing is available when the file is writable and has no quoted multi-line fields; otherwise the grid stays read-only. Right-click Reveal in editor jumps to a cell’s location either way.
Changed
- Local File History is now entirely in its tool window — no more separate window. Opening a revision’s diff used to spawn a standalone “Local History” window alongside the File History tool window. The tool window is now a split: the revision list on the left, and the live side-by-side diff of the selected revision vs the current file on the right — with the Ignore-whitespace / Highlight-words toggles, the “N differences” count, the per-hunk apply chevrons (selective restore), and the whole-file Revert, all in one place. Selecting a revision shows its diff inline; nothing pops out into a second window.
Fixed
-
Blank editor after switching tabs. Clicking a tab could occasionally show a blank editor until you clicked it a second time to give it focus. The editor area now repaints on the layout pulse right after the tab is shown, so its content appears on the first click (without moving the scroll position or stealing focus from a Find-in-Files preview).
-
Markdown print preview: tables no longer get pushed to their own page. When printing (or print- previewing) a Markdown file, a table could be bumped onto the next page even when it easily fit under the preceding heading, leaving the first page mostly blank. The page-layout measurement was collapsing the table’s columns and over-estimating its height; it now measures at the real page width, so the printed output matches the on-screen preview.
Added
-
Find in Files popup. A keyboard-first overlay for multi-file content search (command “Find in Files (Popup)”, palette-discoverable) — type a query, see results grouped by file, and press Enter to jump straight to a match (Enter on a file header opens that file); Esc closes. It has case / regex / whole-word toggles (no replace) and an editable search-folder field that defaults to the project root in a project window, or the current file’s folder otherwise, and can be pointed at any folder. Reuses the same fast (ripgrep-backed) search engine as the Find in Files tool window and shares its search history, and shows a ripgrep badge when that backend is active.
-
Bundled “Shell Script” and “Zsh Script” file templates. New File From Template now includes shell starters: a Bash one (
#!/usr/bin/env bash,set -euo pipefail, amain/main "$@"scaffold →${baseName:script}.sh) and a Zsh one (#!/usr/bin/env zsh,emulate -L zsh+setopt errexit nounset pipefail, the samemainscaffold →${baseName:script}.zsh); both prompt for the file name. Available from the template picker, the toolbar, and the project-tree “New From Template…” menu. -
Doc comments in the Structure tool window. Each item in the Structure outline now shows its leading documentation comment as a hover tooltip when one is present — Javadoc/JSDoc/PHPDoc and plain block comments (
/** … */,/* … */), and runs of//,#, or--line comments above the declaration. Annotation/decorator lines (@Override,#[derive(...)], …) between the comment and the declaration are skipped, and long comments are trimmed. Works for both LSP-served and heuristic outlines.
Fixed
-
New File From Template no longer doubles the extension. When a template’s file-name pattern ends in an extension (e.g.
${baseName}.md) and you typed a name that already included it (notes.md), the new file came out asnotes.md.md. The engine now collapses a doubled trailing extension — but only when the two extensions are identical, sotypes.d.ts,foo.tar.gz, andapp.min.jsare left alone. -
Release builds package from a clean tree, fixing a rare “dead keyboard” in the installer. The native-installer build fed jlink from an incrementally-compiled
target/, where a synthetic$SwitchMapclass (emitted for aswitchover an enum, e.g. the key dispatcher’s) could go missing if a background compiler or an interrupted build lefttarget/classesinconsistent — javac won’t regenerate it while the source looks up-to-date. The packaged app then threw aClassNotFoundExceptionon the first keypress. The release workflow now runscleanbefore-Pdist, guaranteeing every class is regenerated. (Only the native installers could be affected, and only when built from a dirty tree;mvn javafx:runand the fat jar were never affected.) -
Markdown “Insert Table” size picker no longer fills the editor. The grid picker was stretched to cover the whole code area; it now hugs the grid (with padding) and is centered.
-
Structure tool window: methods no longer show a doubled
(). When a language server (e.g. jdtls) already includes the parameter list in a method’s name (setX(Foo)), the outline no longer appended a redundant empty()— so it readssetX(Foo)instead ofsetX(Foo)().
Added
-
User-defined themes from the config folder. Drop a theme CSS in your config directory and it shows up in the theme pickers, named from the file (
my-cool-theme.css→ “My Cool Theme”). Two folders:<configDir>/themes/*.cssfor a full AtlantaFX theme (a self-contained stylesheet defining-color-*) — it becomes both the app theme and its editor colors via the adaptive syntax palette (base colors parsed from the CSS); and<configDir>/editor-themes/*.cssfor a hand-authored editor override (.editor-area+.text.<class>rules, like the bundled ones) that appears in the Editor Theme picker only. Themes load at startup; the palette command “Theme: Reload User Themes” re-scans without a restart. -
Per-hunk selective restore in the Local History window. The window’s diff is now editable on the current-file side — the IntelliJ-style apply chevrons are back, so you can copy an individual fragment from a past revision into the file (single chevron per changed row, double at each hunk start), routed through the undoable buffer with Undo/Save. The diff re-computes after each apply so the remaining chevrons update; the whole-file Revert button still does a full restore.
-
IntelliJ-style Local History window. Opening a file’s local history (double-click a revision in the File History tool window, or “Compare with Current”) now opens a dedicated window with the revision list on the left and a live side-by-side diff of the selected revision vs. the current file on the right — selecting a revision re-diffs instantly. A toolbar shows the revision timestamp, an “N differences” count, Ignore whitespace and Highlight words toggles (both re-diff), and a Revert button (undoable whole-file restore); a Search by content box filters the revision list. The diff pane keeps its side-by-side/unified toggle and prev/next-change navigation. Backed by a new
DiffEngine.DiffOptions(ignore-whitespace normalizes lines one-to-one so the original text still renders; word-level toggles the intra-line emphasis). -
19 new color themes. The theme picker gains the MIT-licensed AtlantaFX community themes from the DLSC collection — Blue, Navy, Army, and the four seasonal sets (Spring, Summer, Fall, Winter) in light/dark pairs, plus Autumn, Browny, News (dark) and Yacht (light) — bringing the total to 26 app themes. Each ships a matching syntax-highlighting theme: a single adaptive editor palette draws every token color from the active theme’s own AtlantaFX
-color-*variables, so the editor surface and syntax colors always coordinate with the chrome. Pick one from Settings → Appearance or theappearance.setTheme/appearance.setEditorThemepalette commands. (The GitHub colorblind/tritanopia variants are intentionally excluded — an adaptive red/green token palette would defeat their accessibility purpose, and GitHub’s default duplicates the existing Primer Light.)
Performance
- Dirty-state tracking no longer allocates the whole document on every keystroke. The editor’s modified-flag listener was bound to RichTextFX’s
textProperty(), which forces the full documentStringto be materialized on each change — an O(n) allocation per character on a very large single buffer (e.g. minified JS/CSS or a one-line JSON/log that slips past the line-count heavy-file tier). It now runs offplainTextChangeswith a cheapgetLength()gate, so the full text is only built in the rare near-clean state, never while typing. Behavior is unchanged (reverting an edit still clears the marker).
Internal
- Broadened the headless-FX harness over the Run + External-Tool console panels. New
RunPanelFxTestandExternalToolPanelFxTestexercise the console lifecycle (started → appendOutput stdout/stderr → finished/failed, Stop-button enablement) and the one-shot tool console (command header + stdout + stderr + status line, clear). - Broadened the headless-FX harness over the Notes + TODO panels. New
NotesPanelFxTestandTodoPanelFxTestfeed an in-memory note bucket / aTodoService.Outcomeinto the panels and assert the rendered file→note / file→match grouping, empty-input handling, the note-body filter, and the TODO summary label. - Broadened the headless-FX harness over the Structure + Bookmarks panels. New
StructurePanelFxTestandBookmarksPanelFxTestfeed an LSPSymbolNodetree / an in-memory bookmark bucket into the panels and assert the rendered hierarchy — Structure’s class→methods outline (and that symbols for a non-attached buffer are ignored); Bookmarks’ file→bookmark grouping, empty-file skipping, and the name filter. - Broadened the headless-FX harness over the Git + Problems tool-window panels. New
GitPanelFxTestandProblemsPanelFxTestfeed pure-record data (GitStatus/LspDiagnosticmaps) into the panels and assert the rendered grouping — Git’s Staged/Changes/Untracked tree, branch label, and commit-button enablement (incl. clean / not-a-repo states); Problems’ language→file→diagnostic tree plus empty-input handling — using no-opActionsstubs (no live git / LSP). - Broadened the headless-FX harness over
MainControllercommand dispatch. A newMainControllerCommandsFxTestdrives service-free commands through the realCommandRegistryagainst a fully-wired window (text zoom, the read-only toggle, line-numbers/minimap/whitespace view toggles, select-all), covering theuicommand-execution surface that the palette + keymap share. - Broadened the headless-FX harness over
EditorBuffer. A newEditorBufferFxTestexercises Markdown view-mode switching (incl. the minimap-hidden-in-preview rule), read-only / view mode, programmatic typing through the macro-replay path (with auto-close), per-language run-glyph detection (compact Java / Python / gated shell), and display-name/dirty/EOL bookkeeping — raisingeditor-package coverage with no new production code beyond two helper-visibility tweaks. - Test coverage: added unit tests + ratcheted the JaCoCo floors. New tests for
configmodels/stores (Breakpoint,BreakpointStore,RecentFiles, plusFileIdentitynull/empty branches) raisedconfigline coverage to ~88%, and newInstallServicetests coverfindBinary/makeExecutable/Result. The per-packagejacoco-checkfloors were ratcheted to sit a few points below the measured coverage (migration·diff 0.85→0.90, config split out at 0.86, template·editorconfig 0.80→0.82, completion·http·pdf 0.68→0.70).
Changed
- Indent detection now defaults to spaces when a file has no indentation, matching VSCode/IntelliJ (“spaces unless the file is detected to use tabs”). A file whose first indented line uses a tab still gets tabs, and spaces still get spaces; only the no-evidence case (empty or flat file) flipped from tab to spaces. This affects the default
detectindent style; an explicitspace/tabsetting or.editorconfigstill wins. - Settings → Snippets: the language picker is now a plain dropdown (no longer an editable/typing combo box) — it lists the curated languages plus any that already have a user snippet file, and you select from it rather than typing.
- The Structure and Bookmarks tool-window filter fields now have a clear (✕) button, matching the Personal Notes and Project panels. It appears only while the filter has text and clears it on click.
- Settings → Keymaps → Customize shortcuts now reveals the Record/Reset buttons only for the selected row. Every command row used to show both buttons, cluttering the long list; a row’s actions now appear only when you click to select it (the selected row is highlighted), so the list reads as a clean command + chord table.
- Save As from the command palette (or a keybinding) now prompts for the path in-scene instead of opening the native file chooser, keeping the keyboard flow mouse-free. The field is pre-filled with the current file’s path (or the project folder + suggested name for an untitled buffer); a bare name resolves against that folder and
~expands to home. The toolbar Save-As button still opens the native file chooser (it’s a mouse action), and overwriting a different existing file asks for confirmation.
Fixed
-
File templates now prompt for a variable used in the file-name pattern. A single-file template whose file name was e.g.
${baseName:Main}.javanever asked forbaseNameat creation time, so it silently used the default and always producedMain.java— even though the same variable worked in the body. The file-identity built-ins (baseName/fileName/extension) can’t be derived for a brand-new file, so when they appear in the file-name pattern they’re now prompted in the wizard (the answer flows into both the file name and the body); used only in the body, they’re still auto-derived as before. -
Markdown-preview code blocks stay readable when the preview theme is toggled independently. Code-block syntax colors followed the editor theme, so forcing the preview to the opposite brightness (the sun/moon toggle) left dark tokens on a light code background (or vice versa). They now follow the preview’s own light/dark — a fixed GitHub Primer palette matching the preview’s prose — while still tracking the editor theme when the preview follows the app.
-
The 80-column ruler no longer jumps to the left edge when word wrap is on. The ruler derived its per-column width from the caret x at the start vs. the end of the longest visible line; with wrap on, that line spans multiple visual rows, so the two points sat on different rows and the width collapsed — planting the ruler near column 13 instead of 80. It now measures the advance on a single visual row (falling back to an adjacent-column advance when the line wraps), so the ruler stays at the correct column in both modes.
-
Settings labels no longer truncate. A long settings-row label (e.g. “Max size / project (MB)” under Workspace → Local History) was clipped to ”…”; the label column now keeps its alignment floor but grows to fit longer labels (and translations). The Settings window also has a minimum size now, so wide rows (e.g. the large-file threshold row + its unit) can’t be clipped by shrinking it.
-
Flaky FX test fixed. The headless-FX harness could intermittently error with
NoSuchFileException: settings.toml.tmp— it now flushes pending async config writes before deleting the temp config dir (FxWindowFixture). -
Clearer Mermaid render error when headless Chrome is missing. A failed
mmdcrender whose error is the Puppeteer “Could not find Chrome” stack now leads with a one-line fix (thenpx puppeteer browsers install chrome-headless-shellcommand). -
Multi-line
$$…$$block math now exports as an image to PDF/DOCX/ODT. A display-math block with the$$delimiters on their own lines was exported as raw text (the export’ssoleDisplayMathbailed on the soft line breaks); single-line$$…$$and inline$…$were unaffected. -
Markdown preview: multi-line
$$…$$block math now renders. A display-math block with the$$delimiters on their own lines was shown as raw text (the paragraph scanner bailed on the soft line breaks); single-line$$…$$and inline$…$were unaffected. -
PDF export now works in packaged builds. PDFBox + pdfbox-io + fontbox all log via commons-logging, which jlink was silently pruning (jdeps dropped the
requiresunder--ignore-missing-deps), so every packaged PDF export failed withNoClassDefFoundError: org/apache/commons/logging/LogFactory. Fixed with hand-written module descriptors; also made the PDF/print/office export services catchThrowableso a failure reports instead of hanging the status. -
Minimap now shows in Markdown SPLIT view. It had been hidden whenever the preview was on (gated to
EDITORmode only); it now hides only in fullPREVIEW(no editor surface), and stays visible in SPLIT where the editor is on screen. -
Markdown → PDF export no longer fails on SVG images. A badge/image served as SVG (e.g. shields.io) made the whole export throw (
IllegalArgumentException: Image type UNKNOWN); SVG images are now rasterized to PNG (via the same JSVG path the on-screen preview uses) and any undecodable image degrades to alt text instead of aborting. -
Tool-window consoles really match the editor font now. The
.run-output/.debug-consoleCSS carried a-fx-font-sizethat overrode the programmatic font on every CSS pass, so the External Tools / Run / Debug consoles kept rendering at 12px regardless. Removed the font from those CSS rules so the editor-matchedsetFontsticks (guarded by a newConsoleFontFxTest). -
Tool-window consoles now actually pick up the editor font. The previous attempt set
-fx-font-sizeviasetStyle, which a JavaFXTextAreaignores; the consoles (External Tools / Run / Debug) now set the control’sfontproperty directly so they match the editor’s family + size. -
Command palette: clicking a result now runs it. A card-wide
MOUSE_CLICKEDconsume filter was swallowing the result cells’ click-to-run handler (only Enter worked). -
Command palette: removed the stray separator line above the command description.
-
Project / Current-Folder filter now finds top-level files in a large root. The filter-box search walked the tree depth-first, so under a big root (e.g. a home directory with huge
.cache/.m2/node_modulessubtrees) it exhausted its visit budget deep inside one subtree before ever reaching a shallow file like.profile— which then never appeared in the results. The search is now breadth-first, visiting every shallower entry before descending, so top-level matches are never starved by a deep sibling subtree. -
Hardened several spots against NullPointerExceptions from corrupted config or non-conforming servers (found via a full-codebase null-flow audit).
Macronow defaults a missingnameto"", so a hand-edited/corruptedmacros.jsonentry without a name can’t NPE macro lookups (find/save/delete).SnippetManagerskips a snippet file whose content is the literalnullinstead of throwing.LspManagergo-to-definition / find-references guard aLocation/LocationLinkrange that a non-conforming language server leaves null.DapClient.evaluatetolerates a null adapter response likeevaluateFullalready did. (The wider audit found theui/editor/MainControllerpackages already guard nullable returns consistently.) -
Hardened the Bookmarks drag-reorder against a theoretical NPE.
handleDropdereferenced a dragged mark row’sgetParent()directly; it’s always non-null today (a mark is always under a file group), but a defensive guard now returns early if a detached row is ever dropped, so a future tree-shape change can’t turn it into a crash. -
Markdown preview no longer renders HTML comments.
<!-- … -->blocks (and inline comments) were shown as visible text/code in the preview (and PDF/print export); they’re now hidden, matching GitHub and every other Markdown renderer. -
Markdown preview hides the editor minimap. In Split/Preview mode the minimap was wedged between the editor and the preview; it’s now hidden while the preview is shown (the preview is the overview) and restored when you return to the plain editor view.
-
The “install language support?” banner no longer shows when the language server is actually installed and running. It was reading the server-availability flag before startup detection finished (and never re-checking), so it could falsely claim e.g. Java support was missing while jdtls was serving the file. The banner now only appears once a server is confirmed absent (not merely “not probed yet”), never when a live session is already serving the file, and re-evaluates as soon as detection settles. Also fixed the banner text dropping its apostrophe (“isnt”) — the message runs through
MessageFormat, where'must be doubled.
Added
-
Drag a raw image out of a web browser into a Markdown file. Alongside dropping image files and pasting a clipboard image, you can now drag an image straight from a web page onto a Markdown editor — Editora downloads it (off the UI thread) into the sibling
assets/folder and inserts. It resolves the source from the drag’s URL, an<img src>in the dragged HTML, or adata:URI; a raw dropped image is encoded to PNG; if the download fails it falls back to inserting the remote URL. (Requires the Markdown file to be saved, soassets/has a home.) -
Syntax highlighting in Markdown-preview code blocks. A fenced code block with a language (
```java,```python,```js, …) is now tokenized with the same TextMate engine + theme the editor uses, so the preview shows colored code instead of flat monospace. Unknown/absent languages and indented blocks stay plain; very large blocks skip highlighting. -
Save as Administrator (Linux & macOS). Editing a file you lack write permission for (e.g. one under
/etc) now offers to save it via the OS’s own authentication prompt — Editora never sees the password. On Linuxpkexec/polkit handles it; on macOS AppleScript’sdo shell script … with administrator privilegesshows the native Touch ID / password prompt. The View-mode banner on a read-only-on-disk file shows Edit as Administrator, andCtrl-S(or theFile: Save as Administratorpalette command) then writes through the elevated helper, which rewrites the file in place so its owner and permissions are preserved. Off by default (Settings → Editor → Saving; paletteToggle Save as Administrator); Windows isn’t supported; each elevated save prompts for authentication. -
Shebang language detection. A file with no telling extension (e.g.
shellscript,myscript) whose first line is an interpreter shebang now gets the right language — syntax highlighting, folding, run gutter, and LSP all light up. Recognizes direct paths (#!/bin/bash),envlookups (#!/usr/bin/env python3), andenv -Swith args, mapping the interpreter (version-stripped:python3.12→python) to shell (bash/zsh/sh/dash/ksh/…), python, javascript (node), typescript (deno/bun/ts-node/tsx), ruby, php, lua, groovy, or powershell. A#!/usr/bin/env -S java --source NNline is treated as a Java compact source file (JEP 512) — highlighting plus a Run ▶ that launchesjava --source NN <file>. Only applies when the extension didn’t already decide the language (never overrides a real type), and the status-bar language picker still overrides manually. -
Back / forward navigation (jump list). A browser-style history of caret jumps — going to a definition, a search hit, a file-history location, a diagnostic, or any go-to-line records where you were, and
Go: Back/Go: Forward(palette commands, user-bindable) walk the trail. A new jump after going back forks the history (drops the forward stack), consecutive same-file+line jumps are deduped, and the trail is capped. -
Word wrap. Toggle soft wrapping of long lines (
View: Toggle Word Wrap, Settings → Editor → “Word wrap”; default off, per buffer/window). The 80-column ruler stays visible when wrap is on. -
Plugin API Javadoc build.
./mvnw -Papidocs javadoc:javadocgenerates thecom.editora.pluginAPI docs and doclint-validates their references/HTML (a broken@linkin the public API fails the build). Also added the missing class header onApp(100% type-level Javadoc). -
Inline math (
$…$) exports as an image to PDF, DOCX and ODT. Previously only block$$…$$math embedded; inline math now renders to a PNG inline in all three exporters (falls back to literal text when math support is off). -
Markdown table of contents.
markdown.tocinserts a TOC (nested heading links, GitHub-style anchors) wrapped in<!-- toc -->markers, or regenerates it in place on re-run — palette command + the right-click Markdown menu. (Moves the formermarkdown-tocplugin into the editor core.) -
Markdown table ↔ CSV. Convert selected CSV (or the clipboard) into a GFM table (
markdown.tableFromCsv; delimiter auto-detected, RFC-4180 quoting) and copy the table at the caret to the clipboard as CSV (markdown.tableToCsv) — palette commands + the right-click Table menu. -
Smart Backspace clears an empty Markdown list/quote marker. Pressing Backspace at the end of an empty
-/1./>/- [ ]item now deletes the whole marker (→ a blank line), instead of removing one space at a time. -
Export the Markdown preview to MS Word (
.docx) and OpenDocument Text (.odt). From the preview right-click menu or thepreview.exportDocx/preview.exportOdtcommands — headings, text styling, lists, quotes, code, GFM tables, and local images..docxuses Apache POI;.odtis generated natively. Mermaid diagrams,$$…$$math, and images (incl. SVG badges) embed as pictures; the packaged installer build is supported (device-tested). -
Markdown table tools. Insert a table from a Typora/Word-style grid size-picker (format-bar button +
markdown.insertTable), add/delete the caret’s row or column, and set a column’s alignment (left/center/right) — via the editor right-click Table submenu and palette commands. Tab-to-next-cell and add-row-on-Enter already worked; tables stay auto-aligned after every edit. The command-palette Insert-Table is a keyboard-onlyRxCsize prompt (e.g.4x4), while the format-bar button and right-click menu use the mouse grid picker. -
Markdown format bar now uses icons (the same glyphs as the editor right-click menu) instead of letter labels, and gained a checkbox / task-list button that toggles a GFM
- [ ]item (commandmarkdown.taskList,C-c C-s k). -
Generic
rcconfig files now highlight as INI. An unrecognized runtime-config file whose name ends inrc(extension-less or a single-dot dotfile — e.g..vimrc,.inputrc,.screenrc,.curlrc,/etc/inputrc) is parsed as INI. Specific rc formats still win (.bashrc=shell,.npmrc=ini,.babelrc=json,.condarc=yaml). -
Jump to next/previous TODO —
todo.next/todo.previous(M-g ]/M-g [) move the caret between TODO/FIXME (highlight-pattern) matches in the active file, wrapping around. -
Clickable inline note marker — click a personal note’s amber start triangle to edit it (restores the click-to-edit the old gutter marker had).
-
notes.toggleResolved— resolve/reopen the personal note at the caret from the keyboard (was panel-only). -
Keybindings for
notes.editNote(C-c e),notes.search(M-g s), andundoHistory.jump(M-g v) — previously palette-only. -
Undo History popup. Besides the Undo History tool window, the new
undoHistory.jumppalette command opens the active buffer’s edit checkpoints as a filterable popup (caret-line preview + capture time); pick one to jump back to that state (undoable). Same checkpoints, a faster keyboard-driven path. -
Clear (✕) button on the Project / Current-Folder filter. A small clear button appears at the right of the filter box once you’ve typed something; clicking it empties the filter (restoring the full tree) and returns focus to the field. It’s hidden whenever the filter is empty.
-
“Beta” pills in the Settings sidebar. Still-beta features (Language Servers, Debugger, Web — HTTP client + HTML preview, Templates, Git, Remote, MCP) now show a small Beta pill beside their name in the Settings category list, so their maturity is clear at a glance.
-
One-click install completed for the binary-only language servers — C/C++ (
clangd), XML (lemminx), Kotlin (kotlin-language-server), Terraform (terraform-ls), and Lua (lua-language-server). Editora downloads the right per-OS/architecture release archive (from GitHub releases, or releases.hashicorp.com for terraform-ls), extracts it into~/.editora/plugins/lsp/<server>/, and points that server’s command at the extracted binary — so every supported language server now has an in-app installer (Install… button / banner / Install: Language Server… picker). With this, all 21 language servers across the npm, toolchain, and binary tiers are covered. -
One-click install extended to toolchain-based language servers: Go (
gopls), SQL (sqls), Ruby (ruby-lsp), C# (csharp-ls), Rust (rust-analyzer), and PHP (phpactor) join the Install… buttons / banner / Install: Language Server… picker. Each installs via that language’s own package manager —go install,gem install,dotnet tool install,rustup component add,composer global require— so they only run when the relevant toolchain (Go / Ruby / .NET / rustup / Composer) is already on your PATH; otherwise Editora tells you which toolchain to install first. -
One-click install extended to more language servers (npm batch): JSON, HTML, CSS (one package,
vscode-langservers-extracted), Bash/Shell, YAML, Dockerfile, and TOML now have Install… buttons on Settings → Language Servers, an in-editor install banner (when LSP is on but the server is missing), and a new Install: Language Server… palette command that lists every installable server. All install globally vianpm(needs Node). -
In-editor “install language support?” banner. When you open a Java/Python/JavaScript file (with the Language Servers feature on) whose language server isn’t installed — or a Mermaid file with the Mermaid feature on but no
mmdc— a slim banner appears above the editor offering Install (one click, with a progress spinner) or Not now (dismissed for that file this session). It only appears when the relevant feature is enabled, and you can turn the nudge off globally via the new Offer to install missing language support checkbox (Settings → Language Servers) or theview.toggleInstallPromptspalette command. -
Install buttons on the Settings → Language Servers and Mermaid pages. Each installable language server row (Java/Python/JavaScript) and the Mermaid page now has an Install… button that runs the same one-click install as the palette commands (downloading that language’s server + debugger, or the Mermaid CLI). The button shows Installed and disables once the tool is detected, updating live right after an install finishes.
-
One-click install of language support (LSP + debugger) for Java, Python, and JavaScript/Node, plus the Mermaid diagram CLI — cross-platform (macOS/Linux/Windows). New palette commands Install: Java / Python / JavaScript-Node Language Support and Install: Mermaid Diagram Tools download and set up each ecosystem’s language server and debug adapter for you, instead of hand-running scripts. Node-based tools (Pyright, typescript-language-server, mermaid-cli) install globally via
npm; debugpy installs viapipinto the config folder; java-debug downloads from Open VSX; the Java language server (Eclipse JDT-LS) and the Node debugger (js-debug) download into~/.editora/plugins/. After installing, the tools are auto-detected and activate without a restart. Editora never installs the underlying runtimes — if Node/Python/taris missing it tells you which one to install first. (An in-editor “install support?” banner follows.) -
“Compare with Commit…” in the editor tab’s right-click menu. Sits next to “Compare with HEAD” and opens the same commit picker as the
diff.vsCommitcommand (diffs the tab’s file against a commit you choose). Like the other Git items in the menu, it’s shown only when the file is inside a repo with Git enabled. -
--single-window[=project]command-line flag. Opens exactly one window at startup instead of restoring every window that was open at last quit: bare--single-windowopens the no-project window;--single-window=MyProjectopens that project’s window (falling back to the no-project window if no project matches that name). It’s session-only — it does not change your saved multi-window layout, so a normal launch afterwards restores all your windows as before (and quitting a one-window run won’t shrink the saved set). CLI file targets /--zen/--new-file/--simpleapply to the single window. -
Syntax highlighting for many extension-less dotfiles / named config files (matched by name via
ConfigFileType, so they also get the matching language server when enabled):- Shell —
.bashrc,.bash_profile,.bash_aliases,.zshrc,.zshenv,.zprofile,.zsh_aliases,.profile,.aliases,.kshrc, the.<rc>.localoverrides, and X-session scripts (.xprofile/.xinitrc/.xsession). (.bash_aliasesis standard — Debian/Ubuntu sources it;.zsh_aliasesisn’t auto-sourced by zsh but is a common user-chosen name.) - Ruby —
Gemfile,Rakefile,Guardfile,Capfile,Thorfile,Berksfile,Brewfile,Podfile,Vagrantfile,Fastfile,Appfile,Dangerfile(lockfiles likeGemfile.lockare intentionally left alone). - Groovy —
Jenkinsfile(andJenkinsfile.<env>). - INI —
.npmrc,pylintrc/.pylintrc,.flake8,.coveragerc,.pep8,.gitlint,.hgrc; git-config —.gitmodules. - YAML —
.clang-format,.clang-tidy,.condarc,.gemrc,.yamllint. - JSON —
.babelrc,.eslintrc,.prettierrc,.stylelintrc,.swcrc,.bowerrc(the bare forms; the explicit.json/.yaml/.jsvariants were already handled by extension).
- Shell —
-
The window title bar now shows the active file’s name + path. Format:
<file path> — <project> — Editora(e.g.~/src/app/Main.java — MyProject — Editora), with a•prefix for unsaved changes; an untitled buffer shows its name, and with no file open it’s just the project/app name. The path is home-collapsed (~) and updates on tab switch, save, Save-As, and rename. -
Linux
.deb: aneditoracommand onPATH, plus a proper menu entry + app icon. The Debian package now (1) creates a/usr/bin/editorasymlink to the installed launcher (jpackage otherwise installs it only at/opt/editora/bin/Editora, offPATH), so you can start Editora from a terminal with arguments — e.g.editora some/file.java:42; and (2) registers the bundled.desktopinto/usr/share/applications(injectingStartupWMClassso the running window matches it), so the application menu and dock show the Editora icon instead of the generic Java icon. Both are removed on uninstall. Implemented via custom DEB maintainer scripts (packaging/linux/postinst/postrm) passed throughjpackage --resource-dirin the installer-wrap step — these replace jpackage’s generated scripts, so they reproduce jpackage’s menu/icon registration in addition to the new symlink. The build (scripts/aot_build.java) also overwrites the app image’slib/Editora.pngwith our branding PNG before wrapping — jpackage’s app-image phase left its default Java icon there andjpackage --app-imageignores--iconat wrap time, so the menu/dock were showing the generic icon..rpmis unaffected (run/opt/editora/bin/Editora).
Fixed
- A “Compare with HEAD” diff no longer blanks its HEAD pane on refresh. In a window with no project open, opening a file’s vs-HEAD diff and then triggering a refresh (clicking an “apply change” chevron, or regaining window focus) could empty the left (HEAD) pane and render the whole working side as all-added. The diff’s HEAD side was re-fetched against the live repo root, which the Git status machine drops to “none” when the active tab is a diff tab with no backing buffer (a No-Project window has no project root to fall back to) — so the re-fetch ran against a null root and returned nothing. The diff now captures the repo root when it’s opened and reuses it on every refresh. Affected all Git-backed diffs (vs HEAD, vs commit, Git-panel staged/unstaged rows, a commit’s file, and the Local File History revision diff). Covered by a new FX regression test.
Changed
-
Perf: the Markdown minimap now coalesces scroll-driven repaints to one per pulse (was a synchronous repaint per scroll event); the completion popup’s prefix check reads a bounded window before the caret instead of materializing the whole document on every caret move.
-
Installing Mermaid support now also installs the headless Chrome it needs.
install.mermaidSupport(Settings → Mermaid → Install…) runsnpx puppeteer browsers install chrome-headless-shellfrom mmdc’s own dir afternpm i -g @mermaid-js/mermaid-cli, so diagrams render on a fresh Linux box without the “Could not find Chrome” Puppeteer error. -
Snippet choice dropdown (
${1|a,b|}) now uses the same keys as autocomplete:C-n/C-pto move,Tab/Enterto accept,C-gto cancel (Down/Up/Enter/Esc still work). -
Command palette ranks results by relevance. Matches are ordered exact > whole-word > word-start > substring > scattered-subsequence (tie-broken by shorter title), so e.g. typing
undonow listsEdit: Undofirst instead of a scattered match likeUnsplit Editor. -
Clearer unsaved marker in the window title. A modified file now shows a prominent leading
●in the title bar (was a small•), so the modified state is visible in Zen mode where the tab strip is hidden. (Native title bars are plain text, so the tab’s colour/italics can’t be reproduced there.) -
The tool-window consoles (External Tools, Run, Debug) now use the editor’s code-area font (family + effective size, tracking the zoom) instead of a fixed JetBrains Mono 12.
-
The Personal Notes inline indicator is 25% larger (the amber corner triangle at a note’s start), making it easier to spot; its click target grew to match.
-
Personal Notes tool window: bold file headers + a clear-filter button. File-name rows are now bold (matching Find-in-Files), and the filter has a trailing
✕button shown only when there’s text (like the Project panel). -
Personal Note editor: Markdown hint + delete confirmation. The note edit popup now shows a hint that the body supports Markdown formatting, and its Delete button asks for confirmation first (the body already rendered as Markdown in the hover tooltip).
-
The External Tools tool-window stripe is off by default. It’s registered default-hidden (like Undo History and Remote Sites); reach it via the
tool.externalToolscommand, theexternalTool.runpicker, or Settings → Tool Windows. The feature is unaffected. -
Refactor: pure text-editing operation cores moved to
com.editora.editops. The 10 toolkit-free editing/indent/navigation cores (Indenter,AutoClose,BraceMatcher,Commenter,Transposer,LineOps,EmacsEdits,SexpNav,Filler,LineIndent) and their tests moved out ofcom.editora.editor. Internal-only — no behavior, dependency, ormodule-infochange. -
Refactor: pure Markdown logic moved to a
com.editora.markdownpackage. The 11 toolkit-free Markdown cores (editing, lint, outline, image-paste) and their tests moved out ofcom.editora.editorinto their own package; the FX renderer and editor overlays stay. Internal-only — no behavior, dependency, ormodule-infochange. -
The “New File From Template” picker is bigger. Widened (and given a taller minimum) so the longer template descriptions are no longer clipped (the default width showed a horizontal scrollbar).
-
Personal Notes show an inline marker at the note’s start instead of a gutter icon. A small amber corner-triangle now marks where a note begins (the selected character/word/range), reinforcing that notes are anchored to the selection — not the line. The gutter note slot is gone; edit the note at the caret with the new
notes.editNotecommand (the inline highlight + hover tooltip + Notes tool window are unchanged). -
The Undo History tool-window stripe button is now off by default. With the new Undo History popup (
undoHistory.jump) as the primary entry point, the side-stripe icon is hidden until you enable it (Settings → Tool Windows, or thetool.undoHistorycommand). Existing sessions keep whatever visibility you had. -
Internal: moved the last Git blame UI bits into
GitCoordinator. The blame annotation engine already lived inGitCoordinator; the three remaining UI methods — thegit.toggleBlametoggle, the caret-lineblameShowCommit, and the gutter-annotation click (onGutterBlameClick) — moved there too, so the whole inline-blame feature now resides with the engine. The two surfaces those touch outside the coordinator route through newWindowOpscallbacks:syncBlameCheck()(re-sync the Settings checkbox) andopenCommitFileDiff()(open the line’s commit diff — routed through the window to reachDiffCoordinatorwithout a circular dependency).MainControllershed ~34 lines (8,716 → ~8,682); its blame command + gutter-click wiring delegate togit::toggleBlame/git::blameShowCommit/git::onGutterBlameClick. No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: moved the Git clone flow into
GitCoordinator. Thegit.cloneUI — the URL + destinationOverlayInputform (with the<home>/<repo>auto-fill and the Browse picker), thegit clonecall, and opening a file from the fresh clone (openClonedEntry) — moved intoGitCoordinatoralongside the engine it already used (GitService.clone/gitError), ascloneRepo()(renamed offcloneto avoid clashing withObject.clone()). The pure/unit-testedrepoNameFromUrlhelper moved with it (GitCoordinator.repoNameFromUrl).MainController’s three clone callbacks — thegit.clonecommand, the empty-GitPanelbutton, and theBranchPopupNo-VCS action — now delegate togit::cloneRepo.MainControllershed ~131 lines (8,847 → ~8,716). Verified byGitStateFxTest(a new case asserts the clone form renders as an in-scene overlay through the coordinator wiring) + the moved unit tests. No behavior change — part of the ongoingMainControllerdecomposition (the remaining Git-Log / blame / branch-popup wiring is a follow-up). -
Internal: extracted
RemoteCoordinatorfromMainController. The Remote file access (SFTP) feature — the SSH connect form, the saved-connection picker, mounting a remote folder as the Project tree root, single-file open from ansftp://URI, and disconnect — moved into its ownCoordinatorHost-style coordinator. It owns theRemoteFileSystemsengine (the SSH client +Vfsresolver), theRemoteConnectionsPanel, and the mount state;MainControllerkeeps theremoteToolWindow(built with the coordinator’s panel), queries the coordinator for the current-folder view, and wires theremote.*/tool.remotecommands + the Welcome-page quick-connect callback to it.MainControllershed ~172 lines (9,019 → ~8,847). Verified by a new FX test that drives the connect flow on a real window and asserts the SFTP form renders as an in-scene overlay (no network). No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: extracted
PluginCoordinatorfromMainController. The Plugin support feature — discovery/apply/install/uninstall plus the per-windowPluginContextadapter that lets a plugin contribute commands, keybindings, tool windows, status-bar segments, and editor menu items — moved into its ownCoordinatorHost-style coordinator. It owns the per-windowPluginRegistry/PluginInstaller, the started-plugin instances, the plugin-contributed tool windows + editor menu items, the “Browse Plugins” picker, and the two inner adapters (PluginContextImpl/ActiveEditorImpl). The sharedPluginManager(owned byWindowManager) is passed in;MainControllerkeeps thepluginManagerfield + thindisposePlugins()/pluginsEnabled()delegates and wiresapplyPlugins/ the tool-window gating / the editor-menu contributor / theplugins.*+view.togglePluginscommands to the coordinator.MainControllershed ~574 lines (9,578 → ~9,004). Device-tested live (MCP bridge): the example plugin’s Java-registered command, its tool-window toggle, and its declarative external command all loaded + ran through the movedPluginContextImpl.startpath. No behavior change — part of the ongoingMainControllerdecomposition (the largest single extraction in the campaign). -
Internal: extracted
HistoryCoordinatorfromMainController. The Local File History feature (per-file save/label/delete snapshots stored outside the file) moved into its ownCoordinatorHost-style coordinator: it owns theHistoryService(off-thread snapshot/blob engine) + theFileHistoryPanel, and the whole flow — record (save / autosave / external / pre-delete), the per-file + folder-history views, label / recent-changes pickers, and revision diff/restore (reusing the sharedDiffCoordinator).MainControllerkeeps the File HistoryToolWindow(built with the coordinator’s panel) and wires the record hooks + thehistory.*/tool.fileHistorycommands + the project-tree “Show Local History” item to it.MainControllershed ~300 lines (9,881 → ~9,578). Device-tested live (MCP bridge): saving a file recorded a revision intohistory/index.json, and the history commands ran clean. No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: moved the Git operations into
GitCoordinator. The user-facing Git mutation operations that were still inMainController—gitOp/gitCommit/discardChanges, branch checkout/create, fetch/pull/push (gitSync), the stash commands, the active-file stage/unstage/discard, the commit-focus action, and thegitErrordialog — moved intoGitCoordinator(which already owned theGitService+ repo state), so the whole Git engine + its mutations now live in one cohesive coordinator.MainControllerkeeps the panels/tool-windows/branch-popup + command registrations (delegating togit.*).MainControllershed ~294 lines (10,175 → ~9,881). No behavior change. (The commit/branch/log/blame/clone UI wiring is a follow-up.) -
Internal: extracted
DiffCoordinatorfromMainController. The diff + merge-conflict viewer (theDiffService, the diff-tab open/refresh machinery, the “apply change” hunk flow through an undoable buffer with Undo/Save, the compare entry points — vs HEAD / vs commit / compare-with-file / Git-panel rows / a commit’s file — patch export, and merge-conflict resolution) moved into its ownCoordinatorHost-style coordinator. Git-backed diffs reach the repo via the sharedGitCoordinator;openDiff+ theDiffSideinterface stay package-visible so the Local File History flow reuses them.MainControllershed ~317 lines (10,492 → ~10,175). No behavior change — continuing theMainControllerdecomposition. -
Internal: extracted
DebugCoordinatorfromMainController. The whole DAP debugging integration (theDebugPanel, breakpoint persistence + gutter gating, the DAP event sink + panel actions, the inline-values / hover / execution-line editor surfaces, the start/attach/step/run-to-cursor/jump flows, and thedebug.toggleAdapter/debug.setAdapterPathpickers) moved into its ownCoordinatorHost-style coordinator. TheDapManagerstays aMainControllerfield (it’s built on the LSPlspManager, andSettingsWindow+ window-dispose reach it) and is passed to the coordinator, mirroring the LSP split; Java debugging layers on the jdtls session, so the coordinator also takes thelspManager+LspCoordinator.MainControllershed ~565 lines (11,057 → ~10,492). This completes theMainControllerdecomposition campaign (13,017 → ~10,492). No behavior change. -
Internal: extracted
LspCoordinatorfromMainController. The whole LSP integration moved into a newCoordinatorHost-style coordinator across three slices: the navigation/format flows (go-to-definition, find-references, hover, document formatting + the hover popup); the diagnostics routing (the per-open-fileproblemsmap, theProblemsPanel, thepublishDiagnosticscallback); and the configure/detect/gating + per-buffer lifecycle (applySupport/server detection/the per-server enable+command switches/syncBufferopen-activate-close/the status-barLSP:segment/the Structure outline/semantic tokens/the per-buffer hook wiring/thelsp.toggleServer+lsp.setServerCommandpickers). TheLspManagerstays owned byMainController(Debug layers on the jdtls session, the MCP bridge reads its diagnostics) and is passed to the coordinator; its callbacks route through thin method-ref delegates.MainControllershed ~685 lines (11,742 → ~11,057). No behavior change — part of the ongoing decomposition. -
Internal: extracted
BookmarkCoordinatorfromMainController. The Bookmarks feature (the panel + jump picker, per-buffer persist/restore, the gutter-click flow, and thebookmarks.*commands) moved into its ownCoordinatorHost-style coordinator. No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: extracted
NotesCoordinatorfromMainController. The Personal Notes feature (the panel + cross-file pickers, per-buffer persist/restore with anchor re-keying, the note editor dialog, and thenotes.*flow) moved into its ownCoordinatorHost-style coordinator. No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: documented soft code-size limits in
CLAUDE.md. Added review heuristics for class/method size (target ≤1000 lines/class, ≤50/method; refactor-smell beyond ~2000/~100) and codified theCoordinatorHostdecomposition as the way to keepMainController(and future features) in check. Docs only — no tooling/behavior change. -
Internal: extracted
RunCoordinatorfromMainController. The run-a-file feature (the run service + console panel, the run/rerun/stop flow, and the launcher-command building) moved into its ownCoordinatorHost-style coordinator. No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: extracted
SearchCoordinatorfromMainController. The Find-in-Files feature (the multi-file search service + results panel, scope tracking, replace-in-files, and the ripgrep/walker backend selection) moved into its ownCoordinatorHost-style coordinator. No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: extracted
TodoCoordinatorfromMainController. The TODO / highlight-pattern feature (service + tool-window panel + the in-editor highlight matcher + the project/open-files scan +todo.*commands) moved into its ownCoordinatorHost-style coordinator. No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: extracted
MacroCoordinatorfromMainController. The keyboard-macro feature (per-window service + record/replay capture hooks +macro.*commands) moved into its ownCoordinatorHost-style coordinator. No behavior change — part of the ongoingMainControllerdecomposition. -
Internal: extracted
ExternalToolCoordinatorfromMainController. The External Tools feature (service + console panel + run/output logic +externalTool.*commands) moved into its ownCoordinatorHost-style coordinator, trimming theMainControllergod-class. No behavior change — first step of an ongoingMainControllerdecomposition. -
The Remote Sites tool window is off by default and opens on the right. Its stripe button is now hidden until you enable it (Settings → Tool Windows, or the
tool.remotecommand /M-g r), and when shown it docks on the right. The remote (SFTP) functionality is unchanged — only the tool window’s default visibility and side moved. Existing layouts (if you’d already shown or moved it) are preserved.
Added
-
Open the session log from Settings → Advanced. Added a “Session log” link next to the settings-file path that opens
editora-session.log(the captured logging + uncaught exceptions for the current session) in the editor — handy for grabbing diagnostics for a bug report. -
Open the dictionary files from Settings → Spell Check. Two links (grouped near the top of the page): “Open the technical.txt dictionary” opens the built-in technical dictionary read-only in a new tab so you can browse what it covers, and “Open dictionary.txt…” opens your personal dictionary file in the editor (created empty if you haven’t added a word yet).
-
Large source files stay responsive (intermediate large-file tier). A very long single file (e.g. a 13k-line source) below the 5 MB hard cutoff used to get the full treatment — syntax highlighting plus the minimap (which redraws every line) plus a language server churning over a huge AST — which made it slow to load and scroll. There’s now an intermediate tier: above a configurable line count (Settings → Editor → Performance → “Disable minimap & LSP above”, default 10000 lines,
0= never), the minimap and language server auto-disable while syntax highlighting and full editing stay on. A status note reports it on open, and you can flip it per file from the palette (Toggle Large-File Mode). Paletteeditor.setLargeFileThresholdchanges the threshold;view.toggleLargeFileModeoverrides per buffer. -
The TODO tool window shows the folder it’s scanning. Like Find in Files, the TODO panel now has a “Scanning in:” line at the top showing the scope — the project root when a project is open, otherwise open files only (full path on hover).
-
The TODO scan skips
.gitignored files and folders. Scanning a project for TODO/FIXME markers now excludes the paths your.gitignoreexcludes (target/,node_modules/, build output,*.log, …), so the TODO panel stops filling with generated-code noise. Governed by the same Settings → Search → “Exclude .gitignore files and folders from search” toggle as Find in Files. -
Find in Files shows the folder it’s searching. The Find-in-Files panel now has a “Searching in:” line at the top showing the exact scope — the project root when a project is open, otherwise the current file’s folder (with the full path on hover). Non-project searches now actually scan that folder on disk (previously they only matched open buffers), and the scope updates as you switch tabs.
-
Find in Files skips
.gitignored files and folders. Searching a project now excludes the paths your.gitignoreexcludes —target/,node_modules/,build/,dist/,*.log, etc. — so build output and dependencies stop cluttering results. New setting Settings → Search → “Exclude .gitignore files and folders from search” (default on; paletteview.toggleSearchGitignore). The ripgrep backend already honored.gitignore; the built-in walker now does too (via a new pragmatic.gitignoreparser), and turning the setting off searches everything (rg--no-ignore). -
Mexican Spanish spell-check dictionary (
es_MX). Added a Spanish (Mexico) dictionary alongside the existing Spanish (Spain) one — both from the RLA / LibreOffice project, redistributed under MPL-1.1. Choose it per file via Spell Check: Set Language… or as the default in Settings → Spell Check; the existingesentry is now labelled “Spanish (Spain)”. -
Technical-terms dictionary for spell check. A bundled list of common programming / web / data / devops words that standard prose dictionaries flag as misspellings (
config,async,boolean,middleware,kubernetes,stdin,json, …) is now accepted by the spell checker, so code-adjacent prose stops getting peppered with red squiggles. Toggle it in Settings → Spell Check → “Enable technical dictionary” (default on) or via the palette commandToggle Technical Dictionary(view.toggleTechnicalDictionary). It layers on top of the language dictionary and your personal dictionary, each independently switchable. -
Command-palette parity for actions that were UI-only. Every remaining click-only action now also has a registered, keybindable palette command:
- Git Log commit operations (Checkout / New Branch / Revert / Cherry-Pick / Reset… / Copy Hash) →
git.log.*, acting on the selected commit. - Debugger data inspection →
debug.evaluate(open console + focus the eval field) /debug.addWatch/debug.setValue. - Diff viewer toolbar →
diff.toggleView(unified ↔ side-by-side) /diff.applyAll/diff.nextChange/diff.previousChange, acting on the active diff tab. - Find/Replace bar →
find.next/find.previous/find.replaceCurrent/find.replaceAll(open the bar if it’s closed). - Git working-file ops →
git.unstageFile/git.discardFile(active file; discard confirms first), mirroring the existinggit.stageFile. - Console Clear →
run.clear/externalTool.clearOutput.
- Git Log commit operations (Checkout / New Branch / Revert / Cherry-Pick / Reset… / Copy Hash) →
-
Macros management in Settings. A new Settings → Macros page lists your saved keyboard macros and lets you rename them, edit/reorder/remove their steps (add command or text steps), delete them, and assign a keybinding inline — all in one place.
-
Snippet & template body editors got syntax highlighting + Emacs keys. The Snippets and File Templates pages in Settings now edit the body in a real code editor (RichTextFX
CodeArea) with live syntax highlighting for the chosen language and basic Emacs caret movement (C-a/C-e/C-f/C-b/C-n/C-p/M-f/M-b/C-d/C-k), instead of a plain text box. Also fixed the form-field labels that could collapse to “…” on the Snippets/Templates/External-Tools pages. -
Reorganized the Settings window. The flat 22-item sidebar is now grouped into five sections (General · Editor · Languages & Tools · Version Control · System) with non-selectable headers and indented items. The overloaded Editor page was split (Code Completion and TODO are their own pages), the Application grab-bag became Interface (window chrome + Simple/Zen modes) and Workspace (projects, notes, local history), HTTP Client + HTML Preview merged into Web, and the author field moved to Templates. Search now also hides empty group headers. Every setting is preserved — only the organization and presentation changed.
-
Saved remote sites UI. Your saved SFTP connections (in
connections.json) now have three visible surfaces beyond the palette picker: a Remote Sites tool window (M-g r) listing every saved site with New / Connect / Remove, a Settings → Remote page to add/edit/remove them (label, host, port, user, auth, key path), and a Remote Sites quick-connect list on the Welcome page. Picking a site opens the connection form pre-filled (your password/passphrase is still prompted and is never stored). CommandsRemote Sites(tool.remote) andManage Remote Sites…(remote.settings). -
Personal dictionary editor. Settings → Spell Check now has a Personal Dictionary section listing your accepted spell-check words (from
dictionary.txt) with add/remove and an Enable personal dictionary checkbox (on by default; off re-flags words you’ve added) — so you can prune, add, or temporarily ignore words without editing the file by hand. Opens via the paletteSpell: Manage Personal Dictionary…(spell.manageDictionary) and the toggleview.togglePersonalDictionary; changes apply globally across all languages. -
File template management GUI. A new Settings → Templates page lists your file templates — the bundled (shipped) ones too, tagged read-only — alongside your own. Editing a bundled template writes a user override to
<configDir>/templates/<id>.json(the shipped one is never modified); add/remove your own with an id / name / description / language / file-name / body form. Opens via the paletteTemplates: Manage File Templates…(template.manage); multi-file templates are shown read-only (edit viaEdit User Templates). The raw-JSON command still works. Both the Snippets and Templates pages also got a taller list and an explicit Save button (edits still auto-save on Enter/focus-loss). -
Undo History tool window (
M-g u) — an in-session timeline of document checkpoints captured as you edit (one per typing burst); double-click or Enter to jump back to any recent state (a single undoable restore). Finer-grained than save-based Local History, session-only, and disabled for very large files. -
Word/line-level undo. Undo no longer collapses an entire typing burst into one step — one
C-znow undoes a word or line. The undo manager starts a new group at word/whitespace/newline boundaries and after a short typing pause (idle break), matching VS Code/IntelliJ. (First of a planned undo arc; an undo tree and a history panel are next.) -
Snippet management GUI. A new Settings → Snippets page lists the snippets for a language — the bundled (shipped) ones too, tagged read-only — alongside your own. Editing a bundled snippet writes a user override to
<configDir>/snippets/<lang>.json(the shipped one is never modified); add/remove your own snippets with a name / trigger / description / body form, applied live. Opens via the paletteSnippets: Manage Snippets…(snippets.manage); the raw-JSONEdit User Snippetscommand still works. -
Sample corpus for manual feature testing. A new top-level
samples/folder holds small, curated text samples organized by feature (syntax per language, folding, indent, markdown, mermaid, todo, spell, search, editorconfig, http, log, diff, encodings) — open the relevant file to smoke-test a feature.samples/README.mdis the manifest;SamplesCorpusTestkeeps it in sync with the files on disk. Large perf inputs are generated on demand byjava scripts/GenSamples.java(git-ignored, never committed), and a scoped.gitattributespreserves the encoding/EOL samples verbatim. -
Syntax highlighting for common project config files.
.editorconfig(INI),.gitignore(and other.*ignorefiles, via a newignoregrammar),mvnw/gradlew(shell), and Eclipse.classpath/.project(XML) are now recognized by name and highlighted — wired through the sharedConfigFileTyperesolver so the grammar and language registries stay in sync. -
Local History, closer to IntelliJ. Three additions to the File History tool window: selective restore — the snapshot-vs-current diff is now editable on the current side, so the apply-chevrons copy individual fragments from a revision back into the file (undoable) instead of only a whole-file revert; named snapshots — a “Put Label” command (
history.putLabel) records a labeled point in time (shown bold in the list, even when the content is unchanged); and search + recent changes — a filter field over the revision list (by label/reason/time) plus ahistory.recentChangespicker of the most recent revisions across the whole project. (history/index.jsonschema 1→2, additive.) -
Local History: folder view + recover a deleted file. Right-click a folder in the Project tool window → Show Local History lists every file under it that has recorded revisions, with deleted files badged; a revision’s “Restore This Version” writes it back to disk (recreating the file). Deleting a file from the project tree now snapshots it into history first, so an accidental delete is recoverable. (Recovery covers files deleted in Editora or that already had edit history — not files deleted by an external tool that were never opened.)
Fixed
- Tool-window sizes are now remembered when you quit with them open. Resizing a tool window (e.g. the Project panel) and quitting with it still open used to reset its divider to the default on next launch — the position was only captured when a tool window was hidden, never on quit. The session now captures the live divider positions on quit, so the layout comes back as you left it. (Window size/position already persisted.)
- Minimap no longer shows a second, stale viewport box on large files. The minimap’s content render calls
canvas.snapshot(), which forces a synchronous layout pass that could settle the editor’s viewport and re-enter the minimap’s redraw mid-render — painting a duplicate viewport highlight that lingered. The minimap now guards against re-entrant paints, so exactly one viewport box is drawn. - Tidier, better-fitting Settings → Templates page. It showed “Templates” three times (page title + two section headers) and its body editor was tall enough to overflow the window. The section headers are now “Author” and “Manage templates”, and the template/snippet body editors use a more modest height (they still grow to fill a taller window).
- Settings page titles no longer shave the bold heading. The big bold heading on each Settings page (e.g. “Markdown”) looked like it was missing slivers of its letters — a JavaFX
Labelclips its text to the glyphs’ logical (advance-based) bounds, which cuts the outer strokes of bold faces (pipeline- and font-independent). The heading is now aTextnode withTextBoundsType.VISUAL, which renders the full glyph ink without clipping. - EditorConfig BOM charset no longer truncates files on open. When a project’s
.editorconfigdeclaredcharset = utf-8-bom(orutf-16le/utf-16be) but a file on disk had no actual byte-order mark, the first 2–3 content bytes were silently dropped on open — and saving re-added a BOM, corrupting the file.EditorConfigCharset.decodenow strips the BOM only when the bytes really start with it. - Typing a closing bracket over a selection no longer silently deletes it. With text selected, typing
)/]/}where the next character was that same bracket “typed over” it and discarded the selection without inserting anything. It now replaces the selection like normal typing. (AutoClose.decidegates skip-over on having no selection.) - Regex “Replace All” now respects the whole-word toggle. With both Regex and Whole word enabled, Replace All replaced every unbounded occurrence instead of the
\b-bounded matches it had highlighted and counted. It now uses the sameSearchMatcher.compileRegexas the search/highlight path. - A hung
javaccan no longer freeze debugging. The loose-Java compile-and-debug fallback now runs the compile throughProcessRunnerwith a 60-second timeout (and concurrent pipe draining), so a stuck compiler can’t pin the single-threaded debug-adapter executor and block every later debug start.
Changed
-
Headless FX tests now use JavaFX 26’s built-in headless platform. The
@Tag("fx")harness boots over the Headless Glass platform that ships insidejavafx.graphics(-Dglass.platform=Headless) instead of the self-built Monocle backend. The vendoredopenjfx-monoclejar (and the rebuild-on-every-JavaFX-bump ritual it required) are gone — nothing extra to download or maintain, and the backend can no longer go stale on a JavaFX bump. Test-scope only; no runtime, packaging, ormodule-infoimpact. The harness only uses TestFX to boot the toolkit (never the robot), so the prototype platform’s input/rendering limits don’t apply. -
Command docs now open the version-matched page. The command palette’s “open docs” (C-h) now links to
…/docs/v-<appVersion>/commands/<command-id>, matching the website’s new versioned docs layout, so each build opens the docs for the version that’s actually running. -
De-duplicated the plugin examples. The full plugin catalog lives canonically in the adriandeleon/editora-plugins repo (source + signed registry index), so the editor repo no longer keeps copies under
examples/— those had already drifted from the registry. Onlyexamples/example-plugin/remains as the in-tree SDK reference;docs/plugins.md, the README, andCLAUDE.mdnow point the catalog at the registry repo.
Added
-
Plugin API:
needsBufferopt-out for tool windows. Plugin tool windows are gated on an open editor buffer by default (they act on the active editor), but a newregisterToolWindow(…, icon, needsBuffer)overload lets a self-contained tool window (scratchpad, calculator, color picker, …) passneedsBuffer = falseso its stripe button stays available on every tab, including Welcome. The existing overloads are unchanged (they default toneedsBuffer = true). -
Markdown lint, substantially expanded. The Markdown linter now covers more of the markdownlint rule set — MD001 (heading-level increment), MD010 (hard tabs), MD022 (headings surrounded by blank lines), MD023 (heading indent), MD026 (heading trailing punctuation), MD031 (fenced code surrounded by blank lines), MD034 (bare URLs), and MD041 (first line should be a top-level heading) — joining the existing MD009/012/018/019/025/040/047/052. New capabilities:
- Per-rule configuration. Turn individual rules off in Settings → Markdown (a per-rule
checklist) or via the
markdownLint.toggleRulepalette picker; persisted insettings.toml(markdownLintDisabledRules, schema 39→40). - Inline disable comments.
<!-- markdownlint-disable [MDxxx] -->/enable/disable-line/disable-next-lineare honored, matching markdownlint. .markdownlint.jsondiscovery. The nearest config file (walking up from the file) is merged into the disabled-rule set ({"MD013": false}/{"default": false}forms).- Auto-fix.
markdownLint.fix(palette) corrects the safely-mechanical issues in the active file (trailing whitespace, tabs→spaces, blank-line runs, heading-marker spacing, heading indent, heading trailing punctuation, final newline) as one undoable edit. - Overview stripe + minimap ticks. Lint warnings now appear as an IntelliJ-style stripe over the scrollbar (click to jump, hover for the rule + message) and as ticks on the minimap, beside the TODO and LSP-diagnostic stripes.
- Dedicated Settings page. The Markdown options (format bar, math preview, lint enable + per-rule checklist) moved out of Editor into their own Settings → Markdown page.
- Per-rule configuration. Turn individual rules off in Settings → Markdown (a per-rule
checklist) or via the
-
Export to HTML opens the result in a tab. After Preview: Export to HTML writes the file, the generated
.htmlnow opens in a new editor tab (re-selecting it if already open). -
Project tool window can show hidden files. A new Settings → Tool Windows → “Show hidden files in the project tree” checkbox (and the
view.toggleProjectHiddenpalette command) reveals dot-files/folders in the project tree and its filter search. Off by default. Persisted insettings.toml(schema 38→39).
Fixed
-
“Add to Dictionary” now persists to
dictionary.txt. The added word was applied for the current session but never written to disk, so it reappeared as misspelled after a restart. The buffer added the word to the shared dictionary set before calling the persist callback, andaddUserWordonly writes the file when the word is newly added to that (same) set — so the write was always skipped. The word is now persisted on the first add. -
Structure tool window no longer jumps the editor back while navigating. A language server that re-announces readiness repeatedly (jdtls re-sends
language/status“ServiceReady” on workspace events) re-pushed the same document symbols, rebuilding the outline tree, resetting the selection to the first row, and snapping the editor back to the top of the file as you clicked around. Re-pushing an unchanged outline is now a no-op, and a genuine rebuild preserves the symbol you had selected. -
Java LSP (jdtls) now initializes reliably; completion works. jdtls was launched without a
-dataworkspace, so every project shared one default Eclipse workspace and deadlocked on its.lock(contending with a leaked previous run or another editor) — the server never finishedinitialize, leaving the status-bar loading bar spinning forever and autocomplete dead. Each project root now gets its own jdtls workspace under<config>/jdtls-workspaces/<hash>(its index persists across sessions, so later opens are faster). A user-supplied-datain the configured command is respected. A language server’s stderr is also now captured into the Debug Log (it was discarded), so when a server fails to start the reason — missing JDK, a locked workspace, a bad command — is finally visible. -
Opening a large source file no longer freezes the UI. Computing fold regions on open recreated a gutter graphic for every fold header in the file (thousands, for a big file like a 12k-line Java source) on the JavaFX thread, blocking it for several seconds. The fold recompute now only refreshes the chevrons of currently-visible rows — offscreen rows get the correct fold state when the gutter factory builds them on scroll — cutting the per-open cost from seconds to a few milliseconds.
-
Breadcrumb dropdown uses the same file icons as the Project tool window. The folder dropdown on a breadcrumb crumb now shows per-type file glyphs (and the boxed folder icon) via
FileIcons, instead of a single generic file icon — matching the Project tree. -
The Git Log tool window refreshes when opened. Opening it — via the stripe icon (which toggles the window directly) or a command — now reloads the commit history, so it no longer shows stale commits from a previous open. (Opening via the stripe button previously bypassed the load entirely.)
-
Tool windows that act on the active file now hide on non-buffer tabs. The Git Commit and Git Log stripe buttons — and plugin tool windows (e.g. GnuPG, the Regex tester) — only appear when there’s an open editor buffer to act on; on the Welcome tab (or any non-buffer tab) they’re hidden, matching how the Structure/File-Info/TODO windows already behave. Commit/Log additionally stay gated on being inside a repo.
-
Find/Replace bar buttons disable when there’s no search term. Prev, Next, Replace, Replace-All, and the case/regex/whole-word toggles are now disabled while the Find field is empty — nothing they do is meaningful without a query (an empty replacement still works as “delete matches”).
-
Buffer-only tool windows are hidden when there’s no buffer to act on. The Structure, File Information, TODO, Markdown Lint, and External Tools stripe buttons no longer show on the Welcome page (or other non-buffer tabs) — they now gate their availability on an actionable active editor buffer (Markdown Lint additionally needs a Markdown buffer; External Tools needs a buffer and non-Simple mode), mirroring how Run / Debug / HTTP / Commit already gate their buttons.
-
Settings → Editor → “Enable log viewer” checkbox now reflects the actual setting. It was omitted from the Settings window’s
load()(the value was only synced insyncViewChecks(), whichload()doesn’t call), so the box always rendered unchecked even though the log viewer was on. The feature itself was unaffected — only the checkbox display.
Changed
- More features on out of the box (new installs only). The default
settings.tomlnow enables: Breadcrumbs, Personal Notes, Git integration (self-gating — inert untilgitis on PATH, so effectively “on when Git is installed”), HTML Live Preview, the HTTP Client, Render LaTeX math in Markdown, and Mermaid diagrams (self-gating — inert until themmdcCLI is found). The server-log viewer was already on. The built-in TODO/FIXME highlight patterns are now case-sensitive (the uppercase/lowercase check is on), so onlyTODO/FIXMEmatch, nottodo/fixme. These are defaults for a fresh config — existingsettings.tomlfiles keep their saved values; flip any of these in Settings or via the palette toggles.
Added
-
HTTP tool window auto-shows for
.httpfiles. Opening (or switching to) a.httpbuffer now automatically opens the HTTP Client tool window, which defaults to the right side. Switching to another tab auto-hides it (restoring any right-side window it displaced) and returning re-opens it; a manual close is remembered per file — once you close it for a.httpbuffer it stays closed until you reopen it (running a request or toggling it back on re-enables auto-show). -
Find in Files improvements. The Find-in-Files tool window (
C-S-f,M-6) gains: include/exclude file globs (comma-separated,.gitignore-style — e.g. include*.java, src/**, exclude**/test/**), applied by ripgrep (-g/-g !) and by the Java-walker fallback + open-buffer overlay alike; query history (the query field is now an editable dropdown of recent searches, persisted insearch-history.json); regex capture groups in replace ($1/${name}in regex mode viaMatcher.replaceAll, with a bad-reference guard; literal mode still inserts$verbatim); a ripgrep badge shown in the panel when that backend is the active one; bold file-name rows in the results tree; single-click / keyboard selection opens the match (a focus-keeping preview, like the Structure tool window — double-click / Enter still dives into the editor); and the window now defaults to the right side (existing sessions keep their saved position).
Changed
-
Refactor: extract the
git pushargv decision into a pure, testedGitService.pushArgs(developer-facing). The first-push-of-an-upstream-less-branch logic (--set-upstream origin <branch>so the first push “just works”, else a plainpush) — including the guard that a blank/detached branch never emits--set-upstream origin <empty>— moved out ofMainController.gitPushinto a pure static helper with a focusedGitPushArgsTest(6 cases). No behavior change; the decision is now unit-covered instead of buried in the UI layer. -
Refactor: centralize the “no repo” guard on the
GitCoordinator(developer-facing). Every repo-only Git operation opened with the same four-line guard —if (repoRoot == null) { echo "not a repo" / "git not installed"; return; }— duplicated 11×. It’s now one engine method,git.reportIfNoRepo(), and each call site is a one-liner (if (git.reportIfNoRepo()) return;). The status string lives in exactly one place. Pure consolidation, no behavior change; full suite green. -
Refactor: move inline Git blame into the
GitCoordinator(developer-facing). The blame annotation engine — eligibility (isBlameEnabled), the off-thread per-file fetch (applyBlame/refreshBlame), and the blame→BlameInfomapping (author/date label, full-commit tooltip, age-heatmap tint) — moves out ofMainControllerintoGitCoordinator, whereapplyStatenow drives it directly. That removes theWindowOps.refreshBlameHost hop added when the engine was extracted: the coordinator owns blame end to end and needs nothing new from the window (only the sharedCoordinatorHost). The click→commit-diff actions (blameShowCommit/onGutterBlameClick) stay inMainControllersince they open a diff tab. No behavior change; full suite green. -
Refactor: extract the stateful Git core into a
GitCoordinator(developer-facing). Under the net added in the previous step, the engine of the Git integration moves out ofMainControllerintoui/GitCoordinator: theGitServiceCLI facade, the repo state (root / branch / upstream), and the state machine that keeps the status bar, the Commit / Log tool windows, and the active buffer’s gutter change bars in sync (isEnabled/isAvailable/ifEnabled/applySupport/refresh/applyState/afterMutation/contextPath). It reaches the window through the sharedCoordinatorHostplus a small git-specificWindowOps(status-bar/tool-window/blame/diff surfaces). The user-facing Git operations (commit, branch, log, blame, stash, clone, diff) stay inMainControllerand reach the engine viagit.service()/git.repoRoot(). No behavior change;GitStateFxTestre-pointed at the new home with the same assertions, full suite green. -
Refactor: a characterization-test net + pure helpers for the Git integration (developer-facing). First step of the Git extraction (the largest, least-tested subsystem): before moving any stateful code, this adds a safety net —
GitStateFxTestdrives theapplyGitState/applyGitSupportcore directly with a syntheticRepoStateand pins the repo-root/branch/upstream state machine, and a new pure, unit-testedgit/GitChangeBars(the diff→CSS-class mapping + the background-buffer re-diff predicate) dedups the gutter change-bar logic at its two call sites. No behavior change; sets up the subsequentGitCoordinatorextraction under the net. -
Refactor: extract the HTTP Client into an
HttpClientCoordinator(developer-facing). The fourth (and most window-entangled) feature coordinator peeled offMainController: the request runner, the response tool-window panel, response-tab opening, clipboard curl import/export, and.envscanning move toui/HttpClientCoordinator. It takes the sharedCoordinatorHostplus a smallWindowOpsextension (open a tab / open-toggle the tool window / re-gate the run affordance / persist the selected environment);MainControllerkeeps only theToolWindowregistration + availability plumbing. Thanks to the shared host, this dropped ~210 lines fromMainController(vs ~60 for the earlier ones). Behavior is identical; covered by a newHttpClientCoordinatorFxTestagainst fake host + ops. -
Refactor: a shared
CoordinatorHostfor the feature coordinators (developer-facing). The three feature coordinators (log viewer / HTML preview / Mermaid) had a bespoke ~60-line anonymousHostadapter each inMainController. They now share oneui/CoordinatorHostinterface and a singleMainController.Servicesadapter, dropping the per-feature boilerplate — and future coordinators reuse it for free (zero adapter code). Test fakes extend a no-opCoordinatorHostStuband override only what they exercise. No behavior change; full suite green. -
Refactor: extract Mermaid into a
MermaidCoordinator(developer-facing). The third feature coordinator peeled offMainController: themmdc/maidservice, the detected-tool availability, and the apply/gating/export logic now live inui/MermaidCoordinator, behind a narrowHostinterface. The few mermaid reads other features need are exposed as coordinator methods (mmdcCommandOrNull()for PDF/print,effectiveAutocomplete()for the completion config,exportDiagram()for PDF export). Behavior is identical; covered by a newMermaidCoordinatorFxTestagainst a fakeHost. Continues de-goddingMainControllerone feature at a time. -
Refactor: extract HTML Live Preview into an
HtmlPreviewCoordinator(developer-facing). The second feature coordinator peeled offMainController(afterLogViewerCoordinator): the loopback HTTP server, the detected-browser list, and the browser picker now live inui/HtmlPreviewCoordinator, reaching the window only through a narrowHostinterface (an anonymous adapter inMainController+ one-line delegations at the call sites). Behavior is identical; covered by a newHtmlPreviewCoordinatorFxTestagainst a fakeHost. Continues de-goddingMainControllerone feature at a time. -
Refactor: extract the log viewer into a
LogViewerCoordinator(developer-facing). The first feature coordinator split out of the ~13k-lineMainController: the server-log-viewer state (tail service + per-buffer follow/offset/control maps) and all its logic now live inui/LogViewerCoordinator, which reaches back into the window only through a narrowHostinterface.MainControllersupplies an anonymousHostadapter and keeps one-line delegations at the call sites (open/save-as/rename, tab close, settings apply, huge-file load, command registration), dropping ~240 lines. Behavior is identical; the payoff is a smaller controller and a unit-testable seam — the newLogViewerCoordinatorFxTestexercises the coordinator against a hand-written fakeHost. This establishes the template for de-goddingMainControllerone feature at a time. -
Upgraded to JavaFX 26 + enabled the macOS Metal rendering pipeline. On Apple silicon (e.g. M-series), the deprecated es2/OpenGL backend JavaFX 25 used could render parts of the UI as blank/garbled rectangles in the packaged app (the file breadcrumb and tool-window stripe icons). JavaFX 26 adds the Metal pipeline, now selected first on macOS (
-Dprism.order=mtl,es2,sw; es2/sw fallback). Windows keeps Direct3D, Linux keeps es2 — the pipeline order is set per-OS. The JDK stays 25 (JavaFX 26 runs on JDK 24+). -
Performance: config writes happen off the JavaFX thread (developer-facing). The frequent, coalesced in-session save (
requestSave— fired by settings toggles, view changes, tab/fold changes) now serializes a consistent snapshot on the FX thread and writes it on a dedicatedconfig-writerdaemon thread, instead of blocking the UI onsettings.toml+workspace-state.jsondisk I/O. A newconfig/ConfigWritercoalesces writes per file (latest wins) and keeps them ordered through one thread, with an atomic temp-file- move so a crash never leaves a half-written config. Durable saves (quit, one-off actions, export) and a
JVM-shutdown flush block until written, and all settings/session writes funnel through the one writer
queue, so an async write can never land after and clobber a durable one. No behavior change; covered by a
new unit test (
ConfigWriterTest).
- move so a crash never leaves a half-written config. Durable saves (quit, one-off actions, export) and a
JVM-shutdown flush block until written, and all settings/session writes funnel through the one writer
queue, so an async write can never land after and clobber a durable one. No behavior change; covered by a
new unit test (
-
Performance: lazy-attach feature overlays (developer-facing). Each editor buffer no longer eagerly builds the five overlays that are inert for most files — find-match highlight, log-level, Mermaid-lint, LSP-diagnostic, and AceJump. They’re constructed (Canvas + scroll/edit subscriptions) only on first activation and inserted in their original z-order, so a buffer with LSP off, no diagram, not a log, and never searched/ace-jumped allocates none of them. Cuts per-buffer memory and per-pulse listener fan-out when many tabs are open; no behavior change. Covered by a new headless-FX test (
LazyOverlayFxTest).
Added
-
Server log viewer. Opening a
.logfile now gets log-aware treatment: level highlighting (ERROR/ WARN/INFO/DEBUG/TRACE coloured inline and as a left-edge bar that works even on huge files where syntax highlighting is off), Follow tail (tail -f— a floating toggle streams new lines as the file grows on disk and auto-scrolls to the bottom), open-the-tail for very large logs (a multi-GB log opens read-only at its end instead of its start), and live level + regex filtering (a floating control narrows the view to e.g.WARN+or lines matching a pattern as you type — the query is a case-insensitive regex that falls back to a literal substring when it isn’t valid one; a stack trace inherits its record’s level so it stays visible). Detection covers Logback/Log4j,java.util.logging, syslog, nginx, structuredlevel=…/JSON logs, zerolog, and Apache/Nginx access logs (5xx→error, 4xx→warn). Log files open in View mode (read-only with an Enable-Editing banner) by default, since they’re for reading — follow still streams while read-only. On by default for.logfiles (Settings → Editor → Logs, orView: Toggle Log Viewer); the rest is per-buffer via the floating control or the palette (Log: Toggle Follow,Log: Filter by Level,Log: Filter by Pattern,Log: Clear Filter,Log: View as Log). No new dependency. -
Ripgrep backend for Find in Files. When ripgrep (
rg) is installed, project-wide Find in Files uses it for a much faster,.gitignore-aware search; otherwise the built-in walker is used unchanged. It’s automatic (on whenrgis detected) and falls back silently when rg is absent, the regex is one rg can’t compile, or the project is remote (SFTP). Unsaved edits in open buffers still win (their in-memory text is searched). Configurable in Settings → Search (toggle + command path + a found/not-found status) and via the palette (Toggle Ripgrep Search,Set Ripgrep Command…). No new dependency —rgis an external tool resolved on PATH. -
Headless-JavaFX test harness (developer-facing).
ui/controller behavior is now covered by real, headless FX tests — TestFX over a self-built JavaFX 25 Monocle backend (vendored inm2-repo, test scope only), so they run on CI with no display/xvfb. Tests lock down Zen mode hiding/restoring chrome, Simple-mode toolbar stripping, chrome-visibility prefs, the tab/buffer lifecycle, the floating toolbar-restore/Zen-exit buttons, tab pinning + Close-Others/Close-All, and command-palette feature gating. FX tests are taggedfx(run the pure suite alone with-DexcludedGroups=fx). No change to the app, thedist/fatjarbuilds, ormodule-info— Monocle is never shipped. -
Window/tool-window decision logic extracted + unit-tested (developer-facing). The multi-window restore-set / primary-window / orphan-session-GC logic (where a quit-vs-close drain bug once lived) moved into the pure
ui/WindowKeys, and the tool-window stripe/button visibility rules intoui/ToolWindowVisibility;WindowManager/ToolWindowManagernow delegate to them. No behavior change. -
External Tools (IntelliJ-style). Define your own CLI commands in Settings → External Tools and run them on the current file/buffer. Command and arguments support
$Name$macros ($FilePath$,$FileDir$,$FileName$,$FileNameWithoutExtension$,$SelectedText$,$LineNumber$,$ColumnNumber$,$ProjectFileDir$), and a tool can pipe the selection or whole buffer to the command’s stdin. Each tool chooses what happens with its output: show it in a read-only console tool window, replace the selection, replace the whole buffer (undoable), or insert at the caret — so the same feature covers both “run a command and see the output” and text transforms (formatters/filters likejq,sort,sed). Every defined tool becomes its own palette command (and is bindable to a key via Settings → Keymaps); there’s also External Tools: Run… (a picker) and Rerun Last. Tools run off the UI thread with a 2-minute timeout. Available by default (the tool list is empty until you add one) and disabled in Simple UI mode. -
Markdown support improvements. A broad set of upgrades to the Markdown experience:
- More CommonMark extensions in the preview — YAML front matter (rendered as a metadata block),
footnotes, heading anchors, and
++inserted++text, on top of the existing GFM tables / task lists / strikethrough / autolinks. - Heading outline — the Structure tool window now shows a Markdown document’s
#…######heading tree (ATX + Setext), so long documents are navigable; click a heading to jump. - Markdown linting — high-confidence rules (trailing whitespace, multiple blank lines, ATX hash spacing, multiple top-level H1, fenced block without a language, missing final newline, broken reference links) shown as inline squiggles with hover messages and listed in a Markdown Lint tool window. On by default; toggle with View: Toggle Markdown Lint.
- Image paste & drag-drop — paste an image from the clipboard, or drop image files onto a saved
Markdown buffer, and the image is written into a sibling
assets/folder with anlink inserted. - Smart link paste — paste a URL over a selection to wrap it as
[selection](url). - Table editing — Tab / Shift-Tab move between pipe-table cells and Enter on the last row adds a row, reflowing the table as you go.
- LaTeX math — render inline
$…$and display$$…$$math in the preview (and block formulas in PDF export) via JLaTeXMath, with GitHub-style delimiter rules so prose dollar amounts are left alone. Off by default (Settings → Editor → Render LaTeX math, or View: Toggle Math Rendering). - Export to HTML — export the rendered preview to a standalone, self-contained
.htmlfile (embedded stylesheet, heading anchors, math as images) via Preview: Export to HTML.
- More CommonMark extensions in the preview — YAML front matter (rendered as a metadata block),
footnotes, heading anchors, and
-
More Emacs editing & movement commands. Filled the remaining gaps versus a standard Emacs
global-map: backward-kill-word (M-DEL); case commands upcase/downcase/capitalize-word (M-u/M-l/M-c) and upcase/downcase-region (C-x C-u/C-x C-l); whitespace and line surgery — join-line / delete-indentation (M-^), delete-horizontal-space (M-\), just-one-space (M-SPC), delete-blank-lines (C-x C-o), open-line (C-o), kill-whole-line (C-S-DEL), and zap-to-char (M-z); structural navigation — forward/backward-sexp (C-M-f/C-M-b), mark-sexp (C-M-SPC), kill-sexp (C-M-k), beginning/end-of-defun (C-M-a/C-M-e, heuristic in a language-agnostic editor), and mark-paragraph (M-h); plus mark-whole-buffer (C-x h) and move-to-window-line-top-bottom (M-r). All are palette-discoverable and rebindable like any command. Kill-ring behavior (yank-pop, consecutive-kill accumulation) remains deferred — these “kill” commands delete, and cut/copy/paste still use the system clipboard. -
Keyboard macros (Emacs-style). Record a sequence of editor actions and replay it. Recording captures the faithful interleaved stream of invoked commands and literally typed text, and replay reproduces the exact sequence — replayed typing runs through the same auto-close / auto-indent assists as live typing. Start with F3, stop with F4, replay the last macro with C-x e; the palette adds Replay Last N Times, Name and Save Last (saved macros persist across sessions in
macros.json), Run Saved, and Delete Saved. A saved macro becomes its own palette command, so it can be bound to a keyboard shortcut via Settings → Keymaps like any other command. The recording hooks are inert (a single boolean check) when not recording, so there’s no idle cost. Known limitation: a recorded command that opens a modal picker/prompt pauses replay for input (it isn’t scripted). -
New Window command. A palette command Window: New Window (
C-x 5 2) opens an additional editor window without having to load a project — previously a second window only appeared when a project was opened. Each new window has its own session (independent open files, layout, and bounds — windows no longer share or clobber one state file) and is restored on the next launch like a project window. Works whether or not the Projects feature is enabled. -
TODO / highlight patterns (IntelliJ-style). Configurable regex patterns (TODO and FIXME by default, each with its own color) are highlighted everywhere they match in the editor, and listed in a new TODO tool window (
M-g o) grouped by file — scanning the open project’s tree when a project is open, else the open files; double-click a result to jump to it. Matches also show as overview stripes over the scrollbar and on the minimap edge (each in its pattern’s color; click to jump, hover for the line). On by default. Add/edit/remove patterns (name, regex, a color picker, case-sensitivity, enabled) in Settings → Editor → TODO Highlighting; commandstool.todo,todo.refresh,todo.addPattern, andview.toggleTodoHighlight. Highlighting runs off the UI thread and is debounced; the project scan is lazy (only when the tool window is open or refreshed). -
LSP-powered Structure tool window. For files served by a language server, the Structure outline is now built from the server’s
textDocument/documentSymbolresponse — a precise hierarchy of classes, methods, fields, enums, etc. with proper kinds and per-kind icons, and methods showing their signature (parameters and return type, in a muted colour) — instead of the fold/TextMate heuristic (which remains the fallback for non-LSP languages, when LSP is off, or for remote files). The outline refreshes live as you type and once the server finishes starting, and clicking an entry still navigates to it. New toolbar controls: a sort selector (Position / Name / Kind) and a kind filter to hide categories. -
Project tool window auto-refreshes on filesystem changes. A watcher on the project tree (the root + currently-expanded folders) refreshes the view when files are created, deleted, renamed, or modified on disk — by another app, a build, git, or Editora itself — preserving expanded folders and the selection. Local projects only; on macOS (where the JDK uses a polling watcher) external changes may take a few seconds to appear. Deleting a file from the tree’s right-click menu now also removes its row immediately.
-
EditorConfig status-bar indicator. When an
.editorconfiggoverns the current file, the status bar shows an EditorConfig segment; clicking it (or the EditorConfig: Open Active File palette command) opens the nearest governing.editorconfig. Hidden when no config applies and in Simple UI mode. -
Global indent-style preference. Settings → Editor → “Indent style” (and the Editor: Set Indent Style… palette command) lets you force Spaces or Tabs for Tab/Enter indentation, or keep Detect from file (the default — per-file auto-detection from the file’s existing indentation). A file’s
.editorconfigindent_stylestill wins over this when EditorConfig is enabled; the global preference is the fallback, and per-file detection is the fallback below that. Applies live to every open buffer. -
EditorConfig (
.editorconfig) support. Opening a file now resolves the nearest.editorconfigchain (walking up toroot = true, nearest-directory-wins) and applies its style to the buffer:indent_style/indent_size/tab_widthdrive Tab/Enter indentation (tabs vs. N spaces) and the minimap/indent math, overriding Editora’s auto-detection.end_of_line(lf/crlf/cr) sets the line ending used on save; a manual EOL change still wins for the session.charset(utf-8, utf-8-bom, latin1, utf-16le, utf-16be) is honored on read and save — files are decoded with the right charset (BOM-detected first, else the.editorconfigvalue) and written back in the same charset, BOM preserved. The status bar shows the active charset.max_line_lengthsets the column-ruler position (offhides it).trim_trailing_whitespaceandinsert_final_newlineare applied on save (disk bytes only — the on-screen document, caret, and undo history are untouched).- Glob sections support
***?[seq][!seq]{a,b}{n1..n2}; unknown keys/sections are ignored. - On by default; toggle via Settings → Editor → “Enable EditorConfig” or the
view.toggleEditorConfigpalette command. Local files only.
-
Tab re-indents the current line to the language server’s convention. In an LSP language whose server supports range formatting (e.g. Java via jdtls), pressing Tab snaps the current line’s indentation to what the formatter would produce — only the leading whitespace changes, the rest of the line is left alone. It still accepts an open completion / advances a snippet tab-stop first, indents a multi-line selection, and on a blank line or when the server can’t format it falls back to the normal smart-Tab indent. Uses
textDocument/rangeFormatting(resolved off-thread). -
IntelliJ-style code completion popup. The autocomplete list now looks and behaves much closer to IntelliJ IDEA:
- Per-kind icons — each row shows a glyph for its kind (method, field, variable, class, interface,
enum, keyword, module, snippet, …), mapped from the language server’s
CompletionItemKind. - Matched-character bolding — the characters you’ve typed are bolded within each candidate (contiguous prefix/substring, or camelCase/subsequence).
- Relevance ordering — LSP candidates are ordered by the server’s relevance (preselect, then
sortText), and the server’s preselected item starts highlighted, instead of raw server order. - Documentation side-popup — a panel beside the list shows the selected symbol’s documentation (and signature), resolved lazily from the server and rendered as Markdown. It auto-shows (toggle the default in Settings → Editor) and Ctrl+Q shows/hides it for the current item. It positions to the right of the list and flips left at the screen edge.
- Deprecated APIs are struck through; PageUp/PageDown page the list.
- The type/detail column is enriched from the server’s
labelDetails. Negligible cost on the typing hot path — the icon/label/ordering work is cheap and per-render, and the documentation is resolved off the FX thread and debounced per selection.
- Per-kind icons — each row shows a glyph for its kind (method, field, variable, class, interface,
enum, keyword, module, snippet, …), mapped from the language server’s
Performance
-
Smoother scrolling in files with many lines. The line-number gutter set each row’s number via a per-row reactive binding that subscribed/unsubscribed as cells recycled during a scroll — measurable churn on the hot path. The number is now set directly from the live line count, and re-pads only when the count crosses a power of 10 (rare). A layout-forced scroll sweep over a ~375-line Markdown file was ~12–21% faster in the gutter (measured A/B in one process). Line numbers, folding, and all gutter markers are unchanged.
-
Cheaper spell-check overlay redraw while scrolling prose. The red-squiggle overlay re-runs each scroll pulse over the visible words. It now checks the (cached) spelling verdict before the per-word syntax-style lookup, so that lookup runs only for the few misspelled words instead of every visible word, and it hoists the per-word offset query out of the loop. Redrawing a ~375-line Markdown file was ~16–22% faster (measured A/B in one process); squiggle placement is unchanged.
Changed
-
Git blame is now an IntelliJ-style “Annotate” gutter column instead of a single inline annotation. Turning on blame (
M-g a, or Settings → Git) now shows a per-line column on the left of the editor — author and commit date on every line, with an age-heatmap background (recent commits tinted warmer, older ones faded), a hover tooltip with the full commit (author / date / relative time / message / hash), and click to open that line’s commit. This replaces the previous GitLens-style annotation that only appeared after the current caret line. Same toggle and setting; only the active file is annotated, and the blame still runs once per file off the UI thread (no per-keystroke cost). -
Tool windows and status segments now follow the active file’s capabilities. The Problems stripe button shows only when the active file is served by a language server; the Debug stripe button only for a debuggable file (Java/Python/JS) or while a debug session is live; and the Git Log stripe button plus the status-bar branch segment only when the active file is inside a git repo. Each is hidden (not greyed) when it doesn’t apply — so a Welcome/Markdown/plain-text tab no longer shows Problems, Debug, the branch segment, or a git context inherited from the working directory. The
LSP:status segment already cleared on non-LSP files. -
Find and Find in Files pre-fill from the selected text. Select a word or phrase on a single line, then open the in-editor Find/Replace bar (the toolbar icon,
C-s/C-r, or Replace) or Find in Files (its toolbar icon orC-S-f) and the selection becomes the search term — the field is also selected so you can type over it. A multi-line selection is left alone (it isn’t a sensible find term) and whatever query was already there is kept. Matches the VS Code / browser convention. -
Markdown split view now keeps the editor and preview scrolled together. In SPLIT mode (editor + rendered preview side by side), scrolling one pane scrolls the other by the same fraction of its content, so the two stay roughly aligned even though their heights differ. The pane the mouse is over drives the other, which keeps the sync one-directional at any moment so it can’t oscillate; entering SPLIT aligns the preview to the editor’s current position. Negligible cost — it rides existing scroll-position listeners with no new per-keystroke or per-pulse work.
-
The Project tool window shows the current file’s folder when no project is open. In the global (“No Project”) window it no longer just shows an empty “No project open” placeholder — it roots the tree at the active file’s parent directory and titles the tool window “Current Folder”, so it doubles as a file explorer that follows you as you switch tabs. With a project open it’s unchanged (the tree stays the project root, titled “Project”); a mounted remote folder is also left as-is. An unsaved/Welcome tab (no folder) falls back to the placeholder.
-
File-type icons now line up in the Project tree (and other file lists). Each glyph’s node bounds equal its ink, which varies per file type, so the icon column had a ragged width and file names jumped left/right (and the taller document glyphs sat high). Every file/folder glyph is now centered in a fixed-size box, so names start at the same x and icons are vertically centered. Applies wherever file icons appear — the tree, tabs, the Open-Files / Recent pickers, and the Switcher.
-
Distinct icons for the tab right-click close menu. The six close commands (Close, Close Other Tabs, Close All Tabs, Close Unmodified Tabs, Close Tabs to the Left/Right) all shared the plain ✕. They now each carry a variant of the ✕ so they’re distinguishable at a glance: one small square (other), three squares (all), a check (unmodified/saved), and a left/right triangle (direction). “Close” keeps the plain ✕.
-
The Find in Files toolbar icon now toggles the panel — one click opens it (focusing the query field), another click closes it (matching
C-s’s in-file find and theM-6tool-window toggle).C-S-ftoggles too, since it shares the command.
Fixed
-
Zen mode is now per-window and restores chrome reliably. Zen mode used to hide the toolbar, status bar, tab bar, breadcrumb, and editor view options by flipping the shared, app-wide view preferences and snapshotting/restoring them — so entering Zen in one window changed every window (and the saved settings), and the restore could desync. Zen is now a per-window effective overlay (like Simple UI mode): it lives in the window’s own session state and is layered on top of the saved prefs without mutating them, so one window can be in Zen while another isn’t, leaving Zen always restores exactly what was showing, and your saved view preferences are never touched.
-
Git menu items are hidden/disabled when there’s no VCS. The tab right-click menu no longer shows Compare with HEAD and Git: Show File History when the file isn’t under Git (feature off, or not inside a repo) — only “Compare With…” (which isn’t a Git action) remains. The Project tool window’s Git submenu is greyed out in the same case. Both now key off “Git available” (enabled and inside a repo) rather than just the feature toggle.
-
HTTP Client: you can now right-click the response body to copy it. The response body is a read-only RichTextFX editor, which (unlike the headers’ text area) has no built-in context menu, so right-clicking the JSON/XML/etc. output did nothing. It now has a Copy / Select All menu — Copy puts the selection, or the whole body when nothing is selected, on the clipboard.
-
Loading a large file (5–50 MB) with a language server enabled no longer freezes the editor. Large-file mode already turns off syntax highlighting and the minimap, but LSP stayed on — so opening, say, a big
.json/.xml/.logwith its server enabled sent the whole document to the server and then mapped and rendered the flood of diagnostics it returned (the Problems tree, minimap, and scrollbar stripes) on the UI thread, locking up the editor. LSP is now disabled in large-file mode too (like highlighting, the minimap, Git change bars, and inline blame), so the file just opens as fast, plain text. Autocomplete is likewise disabled in large-file mode — it allocated the whole document on every keystroke to find the word prefix (the same per-cursor-move cost brace matching already avoids), which made typing in a large file stutter. -
Diff viewer: next/previous-change no longer makes the scroll jump erratically. The two side-by-side panes were kept in sync by copying the absolute pixel scroll position from one to the other, in both directions. That value is a RichTextFX estimate refined as lines are measured, so each pane settled to a slightly different value and pushed the other back — a feedback loop, worst when navigation jumped into an unmeasured region. Now only the focused (actively scrolled) pane drives the other, so interactive sync is strictly one-directional and cannot oscillate. Next/previous-change navigation bypasses the sync entirely: it pins both panes to the same top row explicitly (the rows are 1:1 aligned, so this can’t desync) and puts the selection caret at the block’s top so caret-follow agrees with the scroll.
-
Spell check no longer flags commands, URLs, paths, and identifiers. Tokens like
./mvnw,javafx:run,https://github.com/…,path/to/file.txt,snake_case, anda@b.comwere getting red squiggles — in Markdown prose, in fenced```code blocks, and in comments/strings of Java/Python/Shell and other code files. The checker now recognizes a flagged word that’s part of a URL, path, e-mail, or dotted/underscored/colon-joined token and leaves it alone (ordinary words, including hyphenated ones likewell-known, are still checked). In Markdown it additionally skips whole fenced code blocks and link targets. The check runs only for the few already-flagged words, so it doesn’t affect scroll performance. -
Language servers and debug adapters no longer pile up as orphaned processes. Previously the external servers Editora spawns (e.g.
jdtls) were only killed when a window was closed through the app — so force-quitting, a crash, an OS quit, or akillleft them running, where they accumulate and hold their workspace locks. Now a centralProcessRegistryreaps them three ways: a JVM shutdown hook force-kills every spawned server tree on exit (covers normal quit and SIGTERM/kill); the per-server teardown escalates (SIGTERM the whole tree, then force-kill anything that ignores it after a grace period); and a small on-disk ledger lets a fresh launch reap any server leaked by a previous run that died hard (SIGKILL / power loss), matched by PID + start time + executable so it can never kill an unrelated process. -
Silenced a spurious LSP “Internal error” log. Language servers that support pull diagnostics (the HTML/CSS/JSON servers) send a
workspace/diagnostic/refreshrequest; the client didn’t implement it, so lsp4j logged a SEVEREUnsupportedOperationExceptioneach time (visible in the debug log / dev console). The client now acknowledges the request. Diagnostics were never actually affected — they already refresh on each edit, on save, and when a server reports ready. -
Find/Replace highlights now stay in sync when you edit the buffer. With the find bar open, editing the text left the match highlights painted at their old offsets (no longer aligned with the searched text). The bar now re-runs the search on each (debounced) edit and re-highlights at the new positions — without moving the caret or selection, so it doesn’t interfere with typing.
-
Right-click Cut/Copy/Paste now act on a column (box) selection. The editor context menu’s Cut/Copy/Paste called the native single-caret operations, so on an Alt+drag column/box selection they only touched the primary row — even though the
edit.cut/copy/pastecommands already worked (they route through the multi-caret-aware path). The menu items now use that same path (falling back to the native op when there’s a single caret), Cut/Copy are enabled on a box selection even when the primary selection is empty, and the mutating Cut/Paste are gated to editable buffers. -
Every window that was open at quit now reopens. With multiple project windows open, a Cmd-Q / OS quit fired each window’s close handler in turn, and the eager “mark closed” drained the saved open-window set down to just the last window — so only one reopened on the next launch. Closing a window is now reconciled on a short debounce that can’t elapse during a quit’s close-handler burst: a genuine single close persists the reduced set (that window is forgotten), but a quit exits the JVM before the timer fires, leaving the full pre-quit set on disk so every window reopens. (
app.quitalready worked — it fires no close handlers.)
Added
-
Open a command’s online docs from the palette (C-h). In the command palette, press C-h to open the highlighted command’s documentation page (
https://editora-project.dev/docs/commands/<id>) in your system default browser. The palette footer now listsC-h docsalongside the other shortcuts. -
Format Document command (LSP). A new command-palette command LSP: Format Document reformats the whole active file via its language server (
textDocument/formatting) — available when a server is running for the file’s language and advertises formatting. Edits apply through the undoable buffer (so a single Undo reverts), and it reports when no formatter is available or the file is already formatted. Also available as Format Document in the editor right-click menu, shown only when the server advertises formatting. -
Right-click a tool-window icon → Hide. Right-clicking a button in the tool-window stripe now offers Hide, which removes that icon from the stripe (persisted). Bring it back from Settings → Tool Windows.
-
Reveal in File Manager / Open Terminal Here from the breadcrumb. Clicking a folder or file crumb in the navigation bar now lists Reveal in File Manager and Open Terminal Here for that crumb (above the folder-contents dropdown) — Reveal selects the file or opens the folder; Open Terminal opens at the folder (the file’s parent for a file crumb). Shown for local files only. This also fixes the crumb dropdown not opening at all: the AtlantaFX breadcrumb skin owns each crumb button’s action, so the click is now caught via the control’s crumb-action event instead of an
onActionthe skin overrode. -
“Comment / Uncomment” in the editor right-click menu. For files whose language has comment syntax, the code-area context menu now offers Comment / Uncomment (the same action as the
M-;keybinding and the command palette) — it line-comments the caret’s line or block/line-comments the selection, and toggles back off when already commented. Hidden for plaintext (no comment syntax) and while read-only. -
MCP server (Model Context Protocol). Editora can run a small Model Context Protocol server so an LLM agent (Claude Code, etc.) can observe live editor state and drive the command registry. It’s a loopback-only HTTP/JSON-RPC server with bearer-token auth that writes its endpoint to
<configDir>/mcp-endpoint.jsonfor discovery, and exposes six tools:list_open_files,read_buffer,get_diagnostics,find_in_files,list_commands, andexecute_command. A status-bar MCP indicator shows when it’s running (click to copy the connection command) and is hidden in Simple UI mode. Off by default and gated by a security-notice dialog — enable it under Settings → MCP Server (or the Toggle MCP Server command,view.toggleMcp;mcp.copyEndpointcopies the endpoint). No new dependency (the JDK’s built-inHttpServer+ Jackson). -
Reveal in File Manager / Open Terminal Here (built-in). Revealing a file in Finder/Explorer/your file manager and opening a terminal at a folder are now core features (previously an example plugin), reachable from the command palette (
file.revealInFileManager/file.openTerminal), the tab right-click menu, and the Project tool window menu (both files and folders). Local saved files only; per-OS argv (Finder/Explorer select,xdg-openon Linux) launched off the UI thread. (The breadcrumb crumb menu — see below — surfaces the same two actions.) -
Show Local History + a Git submenu in the Project tree menu. The Project tool window’s per-file right-click menu gained Show Local History and a Git submenu (Show File History, Compare with HEAD, Stage File), mirroring the editor tab menu but acting on the clicked tree path. The Git items grey out when Git is off, and Local History when it’s off.
-
HTTP Client — request chaining, multipart, dynamic vars, and a richer viewer. The
.http/.restclient moved much closer to IntelliJ’s: reference an earlier named request’s response in a later one ({{name.response.body.$.path}}/.headers.H/.status, threaded across a run-all), multipart/ form-data bodies (inline +< ./fileparts), external request bodies (< ./fileraw,<@ ./filesubstituted), response-to-file redirects (>> file/>>! file), per-request directives (# @no-redirect/@no-cookie-jar/@no-log/@timeout N), a per-run cookie jar, and the full dynamic-variable family ({{$random.*}},{{$datetime "fmt"}},{{$processEnv.X}},{{$dotenv.X}}). The response viewer is now a content-type-highlighted read-only editor with split status/headers/body, an in-session history picker, Copy as cURL, Import cURL, and Open in editor tab. No new dependency. -
HTTP Client — Digest auth, URL auto-encoding, date math, and
$sharedenv. A second IntelliJ-parity pass: theAuthorization: Digest user passshorthand now performs the 401-challenge round-trip (RFC 2617); request URLs are percent-encoded automatically after substitution (disable with# @no-auto-encoding);{{$datetime}}/{{$localDatetime}}/{{$timestamp}}take signed offsets (y/M/w/d/h/m/s/ms); and a$sharedsection inhttp-client.env.jsonmerges its variables under every environment (and is hidden from the picker). New directive# @connection-timeout N. -
Per-file-type icons. File icons now reflect the file’s type instead of a single generic document glyph. Editing
Main.javashows the Java logo,app.pythe Python logo,style.cssthe CSS logo, an image its picture glyph, a.zipan archive glyph, and so on — covering every language/format Editora supports (Java, Python, JS/TS, Go, PHP, Ruby, C/C++, C#, Kotlin, HTML, CSS, Markdown, YAML, JSON, XML, shell, Docker, Terraform, Mermaid, SQL, INI/TOML, images, PDFs, archives, CSV, …), with the generic document glyph as a fallback for unknown types. The icons appear everywhere a file is listed — editor tabs, the Project tool window tree, the Jump to Open File and Jump to Recent File pickers, the Switcher (Ctrl-Tab), and the file/folder finders. They are monochrome single-path glyphs in the existing toolbar-icon style, so they track the light/dark theme automatically (no per-theme overrides). Brand logos are from Simple Icons (CC0); category glyphs from Material Design Icons (Apache-2.0). -
Local file history. Editora now silently snapshots local files over time — on save, auto-save, and before an external-change reload — so you can recover earlier versions independently of any VCS. Browse them in a new File History tool window (
M-g lor the palette File History: Show): each revision shows its date/time, reason, and size, with the latest tagged Current. Double-click a revision for a read-only diff against the current file, or restore one (an undoable whole-file replace). Snapshots are deduped by content, stored gzip-compressed and content-addressed under<configDir>/history/, and pruned by configurable limits (max revisions/file, max age, max size per project — Settings → Application → Local History). On by default; local-only (no SFTP) and off in Simple UI mode. -
Emacs fill commands. Re-wrap paragraphs to a fill column: Fill Paragraph (
M-q) reflows the paragraph at the caret, Fill Region reflows every paragraph in the selection, and Set Fill Column (C-x f) sets the target width (default 70, also in Settings → Editor). Filling preserves the paragraph’s indentation and an adaptive fill prefix — line comments (//,#,;, …), Markdown blockquotes (>), and Javadoc (*) — so code comments and quoted text wrap correctly; a word longer than the column is never broken. (Auto Fill mode and justification are deferred.)
Fixed
- LSP completion now triggers on each server’s own trigger characters (e.g.
<for HTML,:/ space for CSS), not just on.or a 2-character word prefix. This makes HTML/CSS/JSON/YAML autocomplete behave like VS Code — typing<in an HTML file now pops up tag completions. The trigger set is read from the server’s advertised capabilities, so every language server gets its correct triggers. - LSP pull diagnostics (
textDocument/diagnostic, LSP 3.17) are now supported. Servers that deliver diagnostics on request rather than by pushingpublishDiagnostics— vscode-html/css/json and others — now surface their warnings/errors as squiggles and in the Problems panel. Push-only servers (jdtls, Pyright, tsserver) are unaffected.
Added
-
HTML Live Preview. A floating browser-globe button now appears at the top-right of any
.html/.htm/.xhtmleditor (mirroring the Markdown preview toggle). Click it to open the current file in a detected browser — Safari, Chrome, Firefox, Edge, or the system default. The file is served over a tiny embedded web server bound to loopback only, so its sibling CSS, JS, and images load, and a small injected script reloads the page live as you type (serving the buffer’s in-memory text, so unsaved edits show instantly). Off by default — enable it under Settings → HTML Preview (or the Toggle HTML Live Preview command). Palette commands: Open in Browser / Open in Browser… (pick a browser). No new dependency (uses the JDK’s built-inHttpServer). -
Command descriptions in the palette. Every command now has a one-line description, shown as a detail line between the results list and the navigation hint in the command palette (
M-x) — it updates live as you arrow through the list, so you can tell what an unfamiliar command does before running it. All ~231 descriptions are fully localized in every bundled language (en/it/es/fr/pt/de); a build test guarantees every command has one. The website’s Commands page reads the same descriptions, so the app and the site never drift. -
Configurable shortcuts (keybinding editor). Settings → Keymaps now lists every command with its current shortcut and a filter box: Record a new chord (multi-key sequences like
C-x C-sare captured live), Reset one command to the keymap default, or Reset all shortcuts. Rebinding replaces the keymap’s default (with a conflict warning before stealing another command’s chord) and applies live, no restart, across all windows; overrides persist insettings.tomland layer on top of whichever keymap theme is active. -
Select All, Duplicate Line, Move Line Up/Down commands (
edit.selectAll/edit.duplicateLine/edit.moveLineUp/edit.moveLineDown), bound in the CUA/Sublime/VSCode/IntelliJ keymaps (Ctrl/Cmd-A, Alt/Option-Up/Down, etc.) and available in the command palette + the new keybinding editor. -
Keybinding themes (CUA, Sublime Text, VSCode, IntelliJ IDEA). Beyond the default Emacs keymap, Editora now bundles four familiar non-modal keymaps. Pick one in Settings → Keymaps (or the Keymap: Select… command) — switching is live, no restart, across all open windows. Each keymap ships a Ctrl-based (Windows/Linux) and a Cmd-based (macOS) variant, auto-selected per platform. User and plugin keybinding overrides are re-applied on top of whichever keymap is active. (Modal Vim is deferred — it needs a mode state machine the current resolver doesn’t model.)
-
Signed plugin registry (authenticity). The registry
index.jsonis verified against a bundled Ed25519 public key via a detached signature (index.json.sig) before Editora trusts it, and “Require signed plugins” (Settings → Plugins, default on) blocks installs from a registry that doesn’t verify — closing the registry-tampering / silent-malicious-update gap that SHA-256 alone can’t. A non-default registry must be signed with its own key or have the setting turned off. JDK-only (no dependency); sign withscripts/PluginSigningTool.java. Signing proves who published — not a sandbox. -
Plugin security hardening (consent + integrity). Enabling a plugin now shows a capability disclosure first — whether it runs executable code, the exact external commands it declares, and any keybindings it remaps — at every arming point (Browse install, install-from-file, and the per-plugin enable checkbox). The registry index and plugin downloads are read with a hard size cap (a hostile registry can’t OOM the app), registry-entry fields are length-capped, and a non-default registry URL is flagged with its host. (Consent/integrity, not a sandbox — plugins remain full-trust.)
-
Plugin support — extend Editora without forking it. A plugin is a folder under
<configDir>/plugins/<id>/with aplugin.jsonmanifest plus, optionally, a Java jar and asset dirs. Two flavors, usable together: a Java SPI (implementcom.editora.plugin.Plugin; aPluginContextlets it register palette commands, keybindings, dockable tool windows, editor right-click items, and status-bar segments, and edit the active buffer) and a declarative manifest (keymap bindings, palette commands that run an external program, andsnippets//templates/dirs merged into the registries). Plugins are loaded once at startup by a childURLClassLoader(parent = the app loader), so the same jar works in dev and inside the packaged jlink installers — no module path,ServiceLoader, orModuleLayerneeded. Off by default and full-trust (no sandbox): a master Enable plugins gate plus a per-plugin enable list live in Settings → Plugins (with a security note, the plugins-folder path, a Reload button, and load-error reporting); enabling/disabling takes effect on the next launch. Simple UI mode disables plugins along with the other local-process features. Palette:view.togglePlugins. Seedocs/plugins.mdandexamples/example-plugin/(a complete example with a build script). -
Plugin registry & install — install plugins without hand-copying folders. Settings → Plugins → Browse plugins… (palette
plugins.browse) fetches a curated GitHub-hostedindex.jsonover HTTPS and lists installable plugins; picking one downloads its.zip, verifies the SHA-256, and unpacks it into the plugins dir (with a zip-slip guard + size caps). Install from file… (plugins.installFromDisk) installs a local.zip, and each Settings row has a Remove button. The registry URL is configurable (a baked-in default you can override); entries show Installed / Update available / Install / Requires Editora ≥ x. The distribution format is a.zipwhose top level is the plugin folder contents;examples/example-plugin/build.shnow emits one (+ its SHA-256) andexamples/editora-plugins-registry/is a ready-to-host sample index. All network/zip/disk work is off the FX thread; no new dependency (java.net.http). Seedocs/plugins.md→ Publishing & installing. -
More example plugins +
ActiveEditor.setText— the pluginActiveEditorfacade gainedsetText(whole-buffer, undoable replace) for whole-file transforms. New real example plugins underexamples/: lorem-ipsum (text generator), text-tools (case convert / sort / unique / reverse / trim, on the selection or whole document), insert-tools (UUID / timestamp inserters), scratchpad (a persistent tool window backed by the plugin’s data dir), and format-runner (run an external formatter — prettier/black/gofmt/rustfmt/clang-format — over stdin→stdout). Plus encode-tools (Base64/URL/HTML/ ROT13/hex), hash-tools (MD5/SHA-1/SHA-256), markdown-toc (insert a TOC from headings), and regex-tester (a live regex tester tool window); Text Tools also gained Squeeze Blank Lines (v1.1.0). All are published in theadriandeleon/editora-pluginsregistry, which carries each plugin’s source. -
Git history, blame & stash (IntelliJ/VSCode parity) — a Git Log tool window (
M-g h, or Show File History on a tab) lists commits; select one to see its changed files, double-click a file for a read-only diff against its parent, and right-click a commit to Copy Hash / Checkout / Reset (soft·mixed·hard) / Revert / Cherry-Pick / New Branch. Inline blame (M-g a, GitLens-style) shows a faint “author, time ago • summary” after the current line, following the caret; toggle it from Settings → Git or the palette. Stash push (with an optional message) / pop / apply / drop via the palette or the status-bar branch dropdown. All require Git enabled (and are off in Simple UI mode); blame is off by default. -
Simple UI mode — a one-toggle minimal layout that strips the editor to the essentials: it hides the extra toolbar groups (new-from-template, recent, find-in-files, split, project selector), the tool-window stripe, the file breadcrumb, the entire gutter (line numbers, fold chevrons, and bookmark/note/run/breakpoint/git markers — any collapsed regions are unfolded first so nothing is stranded), the minimap, and most status-bar segments (git, the LSP server indicator, language, tab size, line endings, encoding), while keeping the tabs, core toolbar icons (including Open), and the echo / read-only / zoom / Ln-Col / file-size status. It also disables the heavier features — language servers (LSP), debugging, the HTTP client, Git, and multiple cursors / column selection — for a quiet, plain editor (the saved enable settings are untouched and restored on exit). Toggle it from the toolbar icon, the command palette (View: Toggle Simple UI Mode), or Settings → Application; it persists. The
--simplecommand-line flag starts a session in Simple mode without changing the saved preference. Your own line-number/minimap preferences are preserved and return when you toggle it off. -
Remote file access (SFTP) — connect to a server over SSH/SFTP and edit its files as if they were local. Remote: Connect to SFTP… opens a connection form (host/port/user, and auth via your default
~/.sshkeys, a chosen key file, or a password); on connect the remote folder is mounted in the Project tool window so you can browse it, and opening a file edits + saves it directly over SFTP. Saved connections are remembered (metadata only — never a password) and reachable via Remote: Saved Connections…; Remote: Open File… opens a singlesftp://URI and Remote: Disconnect returns to your local project. Features that need a local process (language servers, debugging, Git, Run, the HTTP client) are automatically disabled for remote files. Built on Apache MINA SSHD; no external tools needed. -
HTTP Client (
.http/.restfiles) — open a.httpfile with syntax highlighting and a green ▶ on every request; click it to run that request and see the response (status, headers, pretty-printed JSON body, timing/size) in an “HTTP Client” tool window (M-0). Uses Editora’s built-in HTTP client — no external tools needed (off by default; enable in Settings → HTTP Client). Supports in-file@variable/{{var}}substitution (incl.{{$uuid}}/{{$timestamp}}), environment files (http-client.env.json+.private.env.json, with an environment picker), running the whole file, and saving the response. Commands:HTTP: Run Request at Caret,HTTP: Run All Requests in File,HTTP: Select Environment. -
File templates — “New File From Template” creates a file (or set of files) from a reusable template with
${variable}substitution, a${cursor}placement, and a variable-entry wizard:- A template picker (palette
Template: New File From Template…, C-c C-n, and a toolbar button) lists bundled + user templates; a wizard prompts for any variables the template references (e.g.${className:Main}), pre-filled with defaults. Built-ins (${author},${projectName},${date},${baseName}, …) are filled automatically. - Single-file templates open as an untitled buffer with the caret at
${cursor}; multi-file templates (afiles[]array) and the project tree’s folder right-click New From Template… write the files to disk and open the primary one. A traversal guard rejects paths that escape the target folder; existing files are never overwritten. - User templates live in
<configDir>/templates/*.json(Template: Edit User Templates…,Template: Reload Templates); a new Author name setting (Settings → Application → Templates, defaulting to the OS user) feeds${author}. Always-on, like snippets.
- A template picker (palette
-
Diff viewer — compare files visually in a dedicated tab:
- Side-by-side (default) with synchronized scrolling, original line numbers, syntax highlighting, per-line added/removed/changed backgrounds, and intra-line word emphasis on changed lines; a toolbar toggle switches to a unified (+/−) view.
- Prev/next change navigation and Export patch… (writes a
.patch). - Entry points: Compare with HEAD for the current file (
C-x v =, also the tab and Git-panel menus), Compare With… any other file, Compare with Commit… (pick from the file’s history), and Show Diff on a Git-panel row (staged → index↔HEAD, unstaged → working↔index). - Merge-conflict resolution — open a file with Git conflict markers in a merge view and accept
Ours / Theirs / Both per conflict, then save the resolved file (
Merge: Resolve Conflicts). - Commands
diff.vsHead,diff.compareWith,diff.vsCommit,merge.resolve(palette-discoverable).
-
Better Compact Source File (JDK 25) support — running and debugging single-file programs is now much more complete:
- Console input — the Run window has an input field: type a line and press Enter to send it to
the program’s stdin (so
IO.readln(...)-style interactive programs work instead of hanging). - Program arguments — Run File with Arguments… prompts for arguments (quote-aware), remembers them per file, and both Run and Debug pass them to the program. Rerun Last Run repeats the previous run.
- Clickable stack traces — double-click a Java/Python/Node stack-trace line in the Run or Debug console to jump straight to that file and line.
- JDK version check — running a
.javafile with an older JDK on PATH now says “Compact source files need JDK 25 or newer — found Java N” instead of a cryptic launcher error. - Quieter diagnostics — implicit-class complaints from a Java language server that doesn’t know JDK 25 yet are filtered out for compact files (real errors still show).
- Snippets —
cmain(compactvoid main()skeleton),ioprintln, andioreadln.
- Console input — the Run window has an input field: type a line and press Enter to send it to
the program’s stdin (so
-
IntelliJ-style debugger UI — the Debug tool window and the editor now match IntelliJ IDEA’s debugger as closely as practical:
- New look — an icon-only grouped toolbar (resume / pause / stop / restart | step over / into /
out / run to cursor, with full titles in tooltips), a thread selector dropdown over the call
stack, rich frame cells (
method:line, File), and type-colored variable values (strings green, numbers blue, booleans amber, null muted) with a muted type suffix. - Pause (
C-c C-d p) suspends a running program; Run to Cursor (C-c C-d u) resumes and stops at the caret line via a temporary breakpoint that is cleaned up automatically. - Watches — a “Watches” node at the top of the variables tree with a ”+ Add watch…” row; add/edit/remove via the context menu or double-click. Watches re-evaluate on every stop and frame selection, expand when the result is structured, and persist across restarts (per project).
- Set Value — change a variable’s value mid-session from the context menu, F2, or double-click; the tree updates in place and watches re-evaluate.
- Jump to Line (
C-c C-d j) — move the execution pointer to the caret line without executing the code in between (like the IntelliJ “Jump to Line” plugin). Works with Python (debugpy); the Java and JS adapters don’t support it yet and say so. - Inline values — while suspended, grey italic
name: valueannotations appear at the end of each visible line that mentions a variable of the current frame, and vanish on resume. - Hover values — hovering a variable in the editor while suspended shows its value in a tooltip.
- New look — an icon-only grouped toolbar (resume / pause / stop / restart | step over / into /
out / run to cursor, with full titles in tooltips), a thread selector dropdown over the call
stack, rich frame cells (
-
Multiple cursors & column/box selection — VS Code–style multi-caret editing, powered by a personal RichTextFX fork. Alt+drag makes a vertical column/box selection; Cmd/Ctrl+click adds a caret at another spot; Cmd/Ctrl+D selects the next occurrence of the current selection; Esc collapses back to a single caret. While multiple carets exist, typing, Enter/Tab, Backspace/Delete, paste (one line per caret), and arrow/Home/End movement apply to every caret as a single undoable step. On by default; toggle under Settings → Editor → “Enable multiple cursors & column selection” (or the
view.toggleMultiCaretcommand). The palette also lists Add Caret at Next Occurrence / Above / Below and Collapse to Single Caret (unbound by default — bind them in your keymap if you like). With a single caret everything behaves exactly as before. (Note: Emacs movement chords likeC-f/C-nact on the primary caret only — use the arrow keys to move all carets together.) -
Java debugging (DAP) — a full debugger for Java, complementing the existing Java LSP support, off by default (Settings → Debugging → “Enable Java debugging”). It layers on the running jdtls language server plus the Microsoft java-debug plugin jar (not bundled — auto-detected from a VS Code Java extension / nvim mason install, or set its path in Settings) and speaks the Debug Adapter Protocol over a socket. Features:
- Breakpoints in a clickable leftmost gutter strip (red dot), persisted per project in
breakpoints.jsonand re-anchored to their line’s content across edits; conditional breakpoints and logpoints via Edit Breakpoint (C-c C-d e). - Launch a standalone Java file (incl. JEP 512 compact source) or a project main class (a picker
appears when several are found), or attach to a running JVM (
host:port). - Stepping (continue / over / into / out), a current-execution-line highlight that follows
execution, and a Debug tool window (
M-g d) with the call stack, a lazily-expanding variables tree, an output console with an evaluate REPL, and exception breakpoints. - Commands
debug.start(C-c C-d d),debug.continue/stepOver/stepInto/stepOut(C-c C-d c/n/i/o),debug.stop(C-c C-d k),debug.attach(C-c C-d a),debug.toggleBreakpoint(C-c C-b),debug.restart,debug.toggleExceptionBreakpoints, and a status-bar “Debug” segment. Requires the Java LSP server enabled + jdtls and the plugin detected; everything no-ops cleanly otherwise.
- Breakpoints in a clickable leftmost gutter strip (red dot), persisted per project in
-
Python & JavaScript debugging (DAP) — the debugger now also debugs Python (via debugpy over stdio) and JavaScript / Node (via vscode-js-debug over a socket), reusing the same breakpoints, Debug tool window, stepping, variables, console, and execution-line highlight. Each is enabled per-language under Settings → Debugging (default on, only effective when the master debugging toggle is on). Like the Java adapter, the runtimes are not bundled — install them with the new
scripts/install-debugpy.shandscripts/install-js-debug.sh(auto-detected from~/.editora/plugins/dap/{python,javascript}), or point Settings at an existing install. The breakpoint gutter now appears only for debuggable files (Java / Python / JavaScript). JavaScript is Node-only for now (no browser); TypeScript and remote attach for Python/JS are not yet supported. -
Debug Log viewer — a new in-app window that shows the application log (java.util.logging output + uncaught exceptions), so you can see errors/warnings in a packaged build (DMG/MSI/DEB) where stderr isn’t visible. Open it via the Debug Log command in the palette or Settings → Advanced → Show Debug Log…; it has Refresh / Copy / Clear / Export. The same log is also mirrored to
editora-session.login your config folder, so it survives a crash and can be attached to a bug report. -
Icons on every right-click menu item — all context menus (editor surface + Markdown preview, tab strip, Project / Git / Bookmarks / Notes / Message-log panels) now show a leading Material-style glyph next to each item, matching the toolbar and tool-window icons. Editor-package menus use a new
MenuIconsglyph factory (so theeditorpackage stays independent ofui); colors track the theme via the existing.toolbar-iconstyle, so there’s no new CSS. -
Markdown preview right-click menu — the preview pane now has a context menu with Select All, Copy (both copy the preview’s rendered plain text — markup stripped, via commonmark
TextContentRenderer), Export to PDF, and Print. Copy puts the visible text on the clipboard. -
Markdown selection format bar — selecting text in a Markdown buffer shows a floating, IntelliJ-style bar above the selection with Bold / Italic / Strikethrough / Inline code / Link / Bulleted list buttons and a Normal / H1–H6 paragraph-style dropdown. Each action toggles the wrapping (a second press removes it). It never steals typing focus, repositions on scroll, and hides on Escape / empty selection. Toggle it in Settings → Editor → Markdown (“Show format bar on text selection”, default on) or the
markdown.toggleFormatBarcommand. -
Markdown smart editing — the same actions are available as commands,
C-c-prefixed keybindings (C-c C-s b/i/s/cbold/italic/strike/code,C-c C-a llink,C-c C-h p/C-c C-h dheading promote/demote,C-c C-oopen link,C-c C-s treformat table), and a right-click Format group. Plus: list/blockquote continuation on Enter (-,*,1.(auto-incrementing),- [ ],>— Enter on an empty item ends the list), link helpers (wrap a selection as a link using a clipboard URL when present; Ctrl/Cmd-click ormarkdown.openLinkopens the link under the caret), and GFM table reflow (markdown.reflowTablepads/aligns a table’s pipes honoring:--/:-:/--:).
Changed
-
Keyboard popups are now in-scene overlays — the command palette, the “Jump to…” pickers (recent files, structure, open files, tool windows, bookmarks, notes, snippets, projects, LSP references, spell/app/editor-theme), the file/folder finder, the IntelliJ-style Switcher, the Git branch dropdown, and the status message-log popup no longer use separate
Popupwindows. They render as a card inside the main window over a dim, click-to-dismiss backdrop (a sharedOverlayHost). This fixes a Windows bug where aPopupdidn’t reliably take OS keyboard focus — opening the palette (or a picker) could leave the keyboard dead until restart. Anchored popups (branch dropdown, message log) still “drop” from their status-bar segment; clicking the segment again closes them cleanly. -
Input dialogs are now in-scene too — the text-input dialogs (Personal Note editor, file/bookmark rename, bookmark note, Git clone, new branch, go to line) are no longer separate native windows; they show as a form card in the main window over the same backdrop (a new shared
OverlayInputhelper).Esc/C-gcancel;Enteraccepts (Ctrl/Cmd+Enterfor the multi-line note editor). This continues the Windows keyboard-focus fix to every text prompt. (Simple yes/no confirmations and choice pickers — overwrite, delete, set language/tab-size/line-endings — stay native.) -
Status bar polish — the Git segment now toggles its branch dropdown (click to open, click again to close). The status message-log popup is 20% wider and gains a Copy button (copies the selected messages, or all of them when none are selected); its per-row right-click menu was removed in favor of the more discoverable button (⌘/Ctrl+C still works too).
-
Command palette polish — removed the separator line above the footer legend, and the legend now lists
C-galongside esc as a way to close the palette. -
C-a(beginning of line) is now “smart home” — the first press moves to the beginning of the line’s text (the first non-whitespace character); a second press toggles to the true line start (column 0). Shift-extends selection as before. Applies in the editor for every file type.
Fixed
- LSP (and DAP) failed to initialize in the packaged native build — in the modular
jlinkruntime,lsp4jis a real module that didn’t open its packages togson, so its JSON-RPCinitializethrewInaccessibleObjectExceptionthe moment a language server (or debugger) started. Thelsp4jmodules are now built as open modules. Only affected the installers / app image (notmvn javafx:run).
Added
-
LSP support for C/C++, C#, HTML, CSS, Kotlin, Lua, Dockerfile, SQL, Terraform, and TOML — ten more language servers (twenty-one total): clangd (one server for both C and C++), csharp-ls (C#), vscode-html-language-server, vscode-css-language-server, kotlin-language-server, lua-language-server, docker-langserver, sqls (SQL), terraform-ls, and taplo (TOML). Each has its own enable toggle + command field in Settings → LSP (auto-detected on PATH, never bundled, off by default with the rest of LSP). Lua, Dockerfile, Terraform, and TOML had no syntax support, so each gained a bundled TextMate grammar (from the shiki tm-grammars collection) — they now highlight, fold, and comment-toggle.
Dockerfile(andContainerfile) is recognized by filename, not just extension. TOML moved off the borrowed INI grammar onto its own. Settings schema 11→12→13 (additive-identity migrations). -
Lua auto-indent — Lua now has a dedicated indent style (
do/then/function(…)/repeat/{open a block;end/else/elseif/untilde-indent), so pressing Enter inside Lua blocks indents like the other keyword-block languages instead of just inheriting the previous line. -
LSP support for YAML, Go, Rust, PHP, and Ruby — five more language servers (eleven total): YAML via
yaml-language-server, Go viagopls, Rust viarust-analyzer, PHP via phpactor, and Ruby via ruby-lsp. Each has its own enable toggle + command field in Settings → LSP (auto-detected on PATH, never bundled, off by default with the rest of LSP). YAML/Go/ Rust/Ruby already had syntax highlighting + folding; PHP gained a bundled TextMate grammar (php.tmLanguage.json, from VS Code, MIT) so.phpfiles now get highlighting + folding + comment/indent support alongside the LSP. Settings schema 10→11 (additive-identity migration). -
LSP support for XML, JSON, and Bash/Shell — three more language servers alongside Java/TypeScript/ Python: XML via lemminx (
lemminx; schema-aware completion + validation for.xml/.xsd/.xsl/.svg/.fxml/…), JSON viavscode-json-language-server(schema completion + validation), and Bash/Shell via bash-language-server (bash-language-server start; diagnostics come fromshellcheckwhen it’s on your PATH). Each has its own enable toggle + command field in Settings → LSP (auto-detected on PATH; never bundled). Off by default with the rest of LSP. These ride the existing server-centric foundation — XML/JSON/shell already had syntax highlighting + folding, so no new grammars. The Settings → LSP page is now data-driven over a server-descriptor list. -
Gutter Run ▶ for shell scripts — a green play glyph in the gutter of
.sh/.bash/.zshfiles runs the script withbashand streams output into the Run tool window (reusing the Java/Python Run plumbing). Shown only when the Bash LSP server is enabled (Settings → LSP), gating it separately from the Java/Python Run glyphs. -
LSP support for Python (Pyright) — diagnostics, completion, go-to-definition/references, and hover for
.py/.pyw/.pyi. Configure the server command in Settings → LSP with its own enable toggle (auto-detected on PATH;npm i -g pyright). Off by default with the rest of LSP. (Python already had syntax highlighting, so this rides the existing server-centric LSP foundation.) -
Gutter Run ▶ for Python scripts — a green play glyph in the gutter (on the
if __name__ == "__main__":line, else the first line) runs the file withpython3and streams output into the Run tool window, reusing the Java compact-source Run plumbing. -
Python completion fixes — the popup now appears for fuzzy-matching servers (Pyright returns subsequence matches; the client falls back to those when no literal-prefix match exists). Also wired up auto-import support (declare
completionItem.resolveSupport/workspace.configuration, pushpython.analysis.autoImportCompletions=true) so accepting a cross-module symbol inserts itsimport— auto-import is wired but still being verified. -
LSP support for TypeScript & JavaScript (
typescript-language-server) alongside Java — diagnostics, go-to-definition/references, hover, and completion for.ts/.tsx/.js/.jsx/.mjs/.cjs/.mts/.cts. One TypeScript server serves all of them; the LSP registry is now server-centric (one session per server + project root). Also bundles TypeScript/TSX TextMate syntax highlighting (so JS/TS are no longer plain text) and supports auto-imports — accepting a completion that needs an import inserts theimportline viacompletionItem/resolve. Configure the server command in Settings → LSP (auto-detected on PATH;npm i -g typescript-language-server typescript). Off by default with the rest of LSP.
Fixed
-
Settings → Mermaid: the
mmdc/maiddetection status is now colored green (found) / red (missing), matching the LSP and Git status labels, instead of plain text. -
Windows: keyboard froze and the command palette wouldn’t open. Two distinct bugs (found via on-device key-event logging):
- Keyboard freeze on Alt. A bare
Altor an unboundAlt+<key>was interpreted by Windows as menu/mnemonic activation, putting the window into “menu mode” — which froze all keyboard input app-wide (mouse still worked, restart required) and broke the manyM-chords. Editora now consumes the bare Alt and any unbound plain-Alt key on Windows/Linux so menu mode never engages. BoundM-chords work as before; AltGr (Ctrl+Alt) is left alone so international layouts keep composing characters; macOS is unchanged. - Command palette (
M-x) never appeared. It was ajavafx.stage.Popup, which on Windows doesn’t reliably take OS keyboard focus — focus got orphaned and the keyboard went dead. It’s now an in-scene overlay (centered card + dim backdrop) that takes focus correctly on every platform.
(Note: if a specific chord like
Alt+Xstill does nothing on Windows, check for a global hotkey manager — e.g. XKeymaps — grabbing it before Editora; that’s external to the editor.) - Keyboard freeze on Alt. A bare
-
Diagnostic hover tooltip flickered while the mouse moved over a mark — the tooltip was re-shown (and re-positioned to the cursor) on every mouse-move event, so hovering a squiggle or a scrollbar diagnostic mark looked janky. It’s now shown once and left in place until you move to a different mark/message. Applies to the editor squiggle hover, the Mermaid-lint hover, and the scrollbar diagnostic stripe.
-
LSP error squiggles / Problems never appeared when the file path involved a symlink — a language server reports diagnostics under the file’s real (canonical) path (e.g.
/private/tmp/…for a/tmp/…symlink on macOS, or any symlinked project directory), but the open tab was matched by a non-symlink-resolved path, so every diagnostic was silently dropped. The match now uses the canonical (symlink-resolved) path on both sides. -
jdtls (and other wrapper-script servers) leaked orphaned processes and could hang on restart — the Homebrew
jdtlslauncher is a wrapper (jdtls→ python → java); Editora only killed the wrapper on shutdown, orphaning the real server JVM, which kept running and held its Eclipse workspace lock so the next session for that folder couldn’t start (stuck on “Starting…”, no completion/diagnostics). Editora now tears down the whole process tree. (Orphans left by an earlier version may need a one-time manual kill, e.g.pkill -f org.eclipse.jdt.ls.) -
A chatty language server could deadlock during startup — its stderr stream was never drained, so once the ~64 KB OS pipe buffer filled the server blocked. The server’s stderr is now discarded.
-
Language servers installed via a Node version manager (nvm/fnm/asdf) showed “not found” in the packaged app — a Finder-launched
.appinherits a strippedPATHthat omits the version manager’s bin dir (e.g. nvm’s~/.nvm/versions/node/<ver>/bin), so npm-global servers liketypescript-language-server,pyright-langserver,vscode-json-language-server,bash-language-server, andyaml-language-servercouldn’t be located even though they were installed.ProcessRunnernow resolves the user’s login-shellPATHonce (running$SHELL -l -i -cwith a timeout) and folds it into the PATH it gives every subprocess, recovering all version-manager and profile-set dirs at a stroke. This also helps Git (a Homebrewgit) and Mermaid (mmdc/maid). -
LSP loading bar in the status bar never stopped spinning — it was only cleared by incoming diagnostics or jdtls’s vendor-specific status message, so a file with no diagnostics (or any non-jdtls server) left it running indefinitely. It now clears as soon as the universal LSP
initializehandshake completes. -
Intermittent black screen / render glitches in the packaged macOS app after opening many files — JavaFX’s GPU texture pool defaults to a 512 MB ceiling; with enough open files (each holding editor + overlay + minimap textures, plus cached preview/diagram images) it filled up and the render thread threw
NullPointerExceptions (nullRTTexture/ mask texture), showing as a black window. Reduced retained GPU memory so it no longer scales with the number of open files — background tabs now drop their minimap snapshot (regenerated when shown), and the Markdown image + Mermaid diagram caches are LRU-bounded — and raised the Prism texture caps (prism.maxvram,prism.maxTextureSize) in the packaged app and dev run as a safety net. (Only reproduced in the jlink/jpackage build, not undermvn javafx:run.) -
Editor froze (render thread crashed) when opening a Markdown file straight into Preview/Split mode — the editor’s
Canvasoverlays (minimap, whitespace, spell-check, note-highlight, Mermaid lint) were sized directly from the editor pane, which is momentarily zero-width while a Markdown buffer opens into Preview/Split. A buffered draw on a zero/NaN-sized canvas made JavaFX allocate a null GPU texture and the render thread NPE’d inNGCanvas— stalling the whole pipeline so the UI looked hung. Canvas dimensions are now clamped to a finite, in-range size (new unit-testedCanvasGuards) and overlays skip painting into a collapsed surface. -
External tools not found in the installed app — a GUI-launched app (a macOS
.appfrom Finder, a Linux.desktop) inherits a strippedPATHthat omits Homebrew / npm / Node locations, sommdc,npx/maid(and a Homebrew-installedgit) showed up as “not found” even when installed. Editora now augments the subprocessPATHwith the usual install dirs (e.g./opt/homebrew/bin,/usr/local/bin) and resolves a bare command name to its absolute path before launching. Setting an absolute path in Settings still works as an override. -
App freeze with several Markdown previews open (macOS) — rendering SVG badges in the Markdown preview pulled in AWT/Java2D, whose native macOS pipeline contended with JavaFX for the AppKit run loop, an intermittent deadlock that grew more likely the more previews were open. AWT/Java2D now runs headless (software rasterization only), eliminating the conflict; SVG badges still render.
-
The Markdown preview table columns no longer collapse to one character wide (short headers like “Phase”/“Depends on” stacking letter-by-letter); columns now size proportionally to their content.
-
Thread leak on tab close — each open file’s two background worker threads (Markdown preview + syntax highlighter) are now shut down when its tab closes, instead of lingering for the rest of the session.
-
The message log opened from the status bar now toggles: clicking the status message again closes the popup.
-
The Welcome page now shows a scrollbar (vertical or horizontal) when its content doesn’t fit the window, and its tab can be dragged to reorder like any other tab.
Added
-
LSP support (Phase 1: Java) — language intelligence via the Language Server Protocol, backed by Eclipse JDT LS. Live diagnostics (severity-colored squiggles + hover tooltips + a Problems tool window,
M-8), go-to-definition (M-.), find references (M-?, results in a picker), hover documentation (C-c h, rendered Markdown), and LSP-backed completion merged into the existing completion popup. The server is auto-detected (jdtlson your PATH; a Settings command override is available) and never bundled — install it (e.g.brew install jdtls). Off by default: enable it in Settings → LSP (or theview.toggleLspcommand). The workspace root is the active Editora project, else the nearest build file (pom.xml/build.gradle/…), else the file’s folder. All server I/O runs off the UI thread; document sync is debounced. (Built on LSP4J; more languages, formatting, and refactoring are planned.) -
Toolbar refinements — the top icon bar’s edit buttons are now state-aware: Save is disabled unless the file has unsaved changes; Undo/Redo only when there’s something to undo/redo; Cut/Copy only with a selection (Cut also needs an editable buffer); Paste only when the buffer is editable and the clipboard holds text (all disabled on the Welcome tab). A new Find in Files icon sits beside the in-file Find icon. The toolbar can be hidden (Settings → Application “Show toolbar”, or the
view.toggleToolbarpalette command); when hidden, a small floating Tool button (top-right, like the Zen “Z”) brings it back. The decorative Switcher keybinding hint was removed from the bar. -
Search — a big upgrade across three areas. In the editor, the find bar (
C-s) now searches incrementally as you type, highlights every match (the current one accented), shows a “{n} of {total}” count, and adds a whole-word toggle (regex + case already existed). Find in Files (C-S-f, or the Search Results tool window onM-6) searches the project and open files off-thread and lists matches grouped by file — Enter/double-click jumps to one — with replace across files (open buffers edited undoably, closed files rewritten on disk, after a confirmation). AceJump (M-g j, avy-style): type a character, then a 1–2 char label drawn over any on-screen occurrence, to jump the caret there. -
Printing — two new commands show a print preview first, then send to the printer.
File: Print…(editor.print) previews/prints the active buffer’s source code — syntax-highlighted with an optional line-number gutter, paginated by whole lines, in a clean light theme.File: Print Preview…(preview.print) handles the rendered Markdown preview (block-aware pagination — a paragraph, table, or image is never split across a page boundary) or a standalone Mermaid.mmddiagram scaled to the page. A modal preview window lets you page through the exact output (scaled to fit, with page navigation); Print… then opens the native dialog (printer, copies, paper) to send, Close cancels. Reuses the Include line numbers / Syntax highlighting settings (the Settings → Editor section is now Export & Print); preparation runs off the UI thread. -
Export to PDF — two new commands.
File: Export to PDF(editor.exportPdf) writes the active buffer’s source as a real, searchable PDF: embedded JetBrains Mono, syntax highlighting, and a right-aligned line-number gutter (both toggleable), in a clean light theme regardless of the app/editor theme.File: Export Preview to PDF(preview.exportPdf) exports a previewable buffer: a Markdown document becomes native vector text (headings, lists, tables, block quotes, code blocks, links and images — including embedded Mermaid diagrams and SVG badges as images), and a standalone Mermaid.mmdfile exports via mmdc’s native vector PDF. New Settings → Editor → PDF Export: Include line numbers, Syntax highlighting, and a Page size selector (Letter / A4). PDF generation runs entirely off the UI thread. Powered by Apache PDFBox. -
Mermaid diagram support (Settings → Mermaid, off by default) — renders Mermaid diagrams in the preview: standalone
.mmdfiles (syntax-highlighted, with the Editor/Split/Preview toggle) and```mermaidfenced blocks inside Markdown. Uses the external mmdc (mermaid-cli) to render and to export a diagram to SVG/PNG/PDF (Mermaid: Export Diagramcommand), and maid (probelabs/maid) to validate — a failed diagram shows the error with its line/column in the preview. Configure themmdc/maidcommands in Settings (blank =mmdconPATHandnpx -y @probelabs/maid). Rendered diagrams are cached by content so editing stays responsive..mmdfiles also get live maid linting (red squiggly underlines + hover tooltip, debounced while typing) and autocomplete of Mermaid keywords + diagram snippets — both with their own toggles, automatically disabled when Mermaid is off or the tools aren’t detected. -
Export configuration — a new “Export Configuration…” button (Settings → Advanced) and the
Configuration: Export to Zipcommand zip your active config folder (~/.editora,~/.editora-dev, or a--config-diroverride) into a timestamped.zipin your home directory, for quick backups. -
Show/hide the tool stripe — a new “Show tool stripe” setting (Settings → Tool Windows) and the
View: Toggle Tool Stripecommand (command palette) hide the side icon bars. This is UI only — tool windows still open via their keyboard shortcuts (e.g.M-1) and the command palette. Hiding the stripe takes precedence over each tool window’s individual visibility toggle. -
Build commit in dev mode — when running with
--dev, the About dialog and the Welcome page now show the short git commit the app is running from, so it’s easy to tell which build you’re testing. Not shown in normal (non-dev) runs. -
Independent Markdown preview theme — the rendered preview can now be light or dark independently of the app/editor theme, so you can read docs in light while coding in a dark theme (or vice versa). Toggle it with the floating sun/moon button next to the preview’s −/+ zoom controls, or the Markdown: Toggle Preview Light/Dark command. The choice is remembered; until you first toggle it, the preview follows the app theme as before.
Changed
-
Unsaved-file marker everywhere — the dot + amber-italic style the tabs use for a file with unsaved changes now also appears in the Switcher, the Open Files picker, and the Project tool window tree, so every list of open files reads consistently. The Project tool window also refreshes when files/folders change outside Editora (on window focus), keeping its expanded folders and selection.
-
Markdown preview readability — the rendered preview now lays its content out as a centered, width-capped column (GitHub-style) with more generous margins and vertical spacing (line height, space between blocks and list items, heading separation), instead of stretching edge-to-edge across a wide window.
Added
-
SVG images in the Markdown preview — the preview now renders SVG images, which JavaFX can’t decode on its own. This makes shields.io / GitHub status badges (served as SVG) show up in the preview as they do on GitHub. Images load off the UI thread and are cached. (Uses the lightweight JSVG library.)
-
Keyboard scrolling in the Markdown preview — Space / PageDown (page down), Backspace / PageUp (page up), and the Emacs
C-v/M-vnow scroll the rendered preview. -
Status-bar message log — click the status message on the left of the status bar to see every message from the current session in a scrollable popup, newest first, each with an
HH:mm:sstime indicator. Messages are selectable (multi-select) and can be copied to the clipboard (Cmd/Ctrl+C or a right-click Copy). The log is in-memory only (not persisted), capped at the most recent 200 messages, with a Clear action. Also available as the Message Log command (view.messageLog). -
Welcome page — instead of opening an empty “Untitled” buffer, Editora now opens a VSCode-style Welcome page in its own tab at startup when there’s no session to restore. It offers Start actions — New File, Open File, then Open Folder as a Project and Clone Git Repository (each shown only when that feature is enabled), and the Command Palette last — each with its configured keybinding shown alongside, plus a Recent files list and a footer with the app version, a link to the project home page, and the license. Being a real tab, it activates, switches, and closes like any other tab; the Welcome Page command (
view.welcome) opens it (or re-selects it if already open) on demand even with files open. The About dialog now also shows the home-page link, copyright, and license. New CLI flag--new-file[=name]bypasses it by opening a fresh buffer instead:--new-file=notes.mdopens an unsaved buffer titlednotes.md(highlighted by its extension; first save prompts for a location), and bare--new-fileopens a blank untitled buffer. -
Personal Notes — private annotations attached to a file without modifying the file, for read-only / generated / shared code where you want knowledge stored separately. Three scopes (word / line / range), an optional body, tags, and a status (active / resolved / orphaned). Notes follow their content as you edit (live offset tracking) and re-anchor on reopen by the captured selection + surrounding context; a note whose anchor can no longer be found is marked orphaned (kept and recoverable, never silently lost). File identity is content-hash + path, so a note re-attaches even when the file is renamed/moved outside the app. Indicators: a gutter glyph, a soft in-editor highlight behind the anchored span, and a hover tooltip (the body is rendered as Markdown in the editor’s font) — the gutter/highlight is toggleable via Settings → Editor → Show note indicators. A Personal Notes tool window (
M-5) groups notes per file with a filter, edit/resolve/delete, and “delete all in file”; commands cover add (C-c n), next/previous, cross-file Jump to Note (M-g n), Search Notes (a picker that matches the full note body, tags, and file path), delete (the note on the caret line — also available from the panel and as a Delete button in the note editor), and export to JSON (resolve/reopen lives in the tool window’s context menu). Note bodies are edited in a multi-line text box (Enter inserts a newline; Ctrl/Cmd+Enter saves). Stored per project in a single versionednotes.json(reuses the bookmark per-project bucket model + the config migration framework). Bookmarks are unaffected (separate gutter slot, no layout shift). The whole feature is off by default — enable it via Settings → Application → Enable Personal Notes (when off, the tool window, commands, and editor menu items are hidden). -
Config schema versioning + migrations — every structured config file (
settings.toml,workspace-state.json,projects/<id>.json,projects.json,bookmarks.json,recent-files.json) now carries a per-file integerschemaVersion(baseline 1), and reads go through a small migration framework (config/migration/) so future releases can evolve a file’s format with one registeredv→v+1step. Existing unversioned files load unchanged and get stamped on the next save (no data loss);recent-files.json(previously a bare array) is migrated to a versioned object. If a file is newer than the running build (e.g. after a downgrade), it’s backed up to<name>.v<n>.bakand defaults are loaded rather than overwriting newer data. -
Autocomplete — appears as you type (debounced) and on demand (
C-M-i/M-/, command “Edit: Trigger Autocomplete”). Code files show a caret-anchored popup of snippet completions (accepting one expands the snippet with its tab stops); Enter/Tab accept, ↑/↓ navigate, Esc dismisses. Prose files (plain text / Markdown) instead show inline “ghost text” — a single greyed continuation drawn after the caret, completed from the bundled spell dictionary plus your personal dictionary; press Tab to accept, Esc (or just keep typing / move the caret) to dismiss. In the code popup, ↑/↓ and C-n/C-p move the selection. Settings → Editor has a master “Enable autocomplete” toggle plus per-source checkboxes — Words (prose) and Snippets (code) — all on by default (more sources can be added later), and there are command-palette toggles for each (“View: Toggle Autocomplete”, ”… Words (Prose)”, ”… Snippets”). The dictionary word list is parsed off-thread and cached; prefix lookup is a binary-search range, so typing/scrolling stay unaffected. -
Multi-language interface (i18n) — Editora’s UI can now run in English, Italian, Spanish, French, Portuguese, or German. Command-palette titles, toolbar tooltips, tool-window titles, and the full Settings window are translated; pick a language under Settings → Appearance → Language (default Automatic follows the system language, falling back to English). A language change applies on the next restart. Strings live in a
messages[_<lang>].propertiescatalog loaded by the newcom.editora.i18n.Messageshelper; a key-parity unit test keeps every translation complete. Essentially the entire interface is translated — command palette, toolbar tooltips, tool-window titles and panel contents (Project, Structure, Bookmarks, File Information, Commit/Git), the full Settings window, status-bar segments, echo-area status messages, every dialog (titles, bodies and buttons), context menus, and popups (branch dropdown, command palette, file finder, switcher). The chosen language also drivesLocale.setDefault, so JavaFX’s own OK/Cancel buttons match.
Changed
-
Settings window redesigned — a scalable left category sidebar (Appearance, Editor, Tool Windows, Spell Check, Application, Advanced, plus “coming soon” placeholders for Keymaps/Plugins/Git/AI) replaces the single stacked list, with a search box that filters settings + jumps to matches, a live preview on Appearance (sample code that recolors/re-fonts as you change theme/font), and a Reset to Defaults on the Advanced page. Changes still apply live. Tool-window placement moved to its own page; the new Tab size control lives on the Editor page. About moved out of Settings (it’s on the toolbar + the “About Editora” command); the settings-file link now lives on the Advanced page.
-
Markdown preview now renders closer to GitHub — task lists (
- [x]/- [ ]) show real checkboxes instead of bullets, inlinecodegets a rounded gray pill (instead of blue text),#/##headings get an underline rule, and links are no longer permanently underlined. Standalone images now render as block images (with the alt text / title as a tooltip), in addition to inline images; relative image paths resolve against the file’s folder, andhttp(s)/file/data:URLs are supported.
Fixed
- In-app file rename now carries bookmarks and personal notes to the new path — renaming an open file from the tab/right-click menu already moved its folds and recent-files entry; it now also re-keys that file’s bookmarks and notes, so they no longer get stranded under the old path.
Added
-
Emacs transpose commands — transpose characters (
C-t), words (M-t), and lines (C-x C-t), also in the command palette (“Edit: Transpose …”). At end of line,C-tswaps the two preceding characters (typo fix);C-x C-tswaps the current line with the one above. -
“Enable Git” setting (Settings → Git), off by default — Git integration is now opt-in. When off, the status-bar VCS segment is disabled, the Commit tool window is hidden, gutter change markers are cleared, and all Git commands/keybindings are inactive (and hidden from the palette). Turn it on in Settings → Git to use branch/status, change bars, commit, and sync. The Git page detects the
gitcommand: it shows the installed version when found, or “git command not found” and disables the checkbox when git isn’t onPATH. -
Save / Save As… in the tab right-click menu — save the right-clicked tab directly (Save is greyed out for an unchanged, already-saved file).
-
Git support (native CLI) — Editora now talks to your installed
git. The status bar shows the current branch with ahead/behind counts (⎇ main ↑2 ↓1, click for the branch dropdown — an IntelliJ-style searchable list of actions + Local/Remote branches, each local branch showing its upstream and incoming/outgoing (↓/↑) commit counts); the editor gutter draws change bars relative toHEAD(green added / blue modified / red deleted); and a Commit tool window (M-4) groups Staged / Changes / Untracked files with stage, unstage, discard, Stage All, a Push button, and a commit message box (Ctrl/Cmd+Enter to commit). Clone a remote repo with “Git: Clone Repository…” (or the button in the empty Commit tool window) — one dialog asks for the repo URL and the destination directory (with a Browse button; the directory auto-fills to<home>/<repo>from the URL), clones, and opens a file from it (its README, if any) so Git lights up; cloning is independent of projects (you don’t need project support to clone or use Git). Palette commands: “Git: Clone Repository…”, “Git: Commit…” (C-x g), “Git: Stage Current File”, “Git: Switch Branch…”, “Git: New Branch…”, “Git: Fetch”, “Git: Pull”, “Git: Push”, and “Git: Refresh Status”. Pushing a brand-new branch automatically sets its upstream (--set-upstream origin <branch>), and Git command failures are shown in a readable dialog rather than the one-line status bar. The Commit tool window is shown only when the active file is under Git; otherwise it’s hidden. The status-bar VCS segment is always present: outside a repo it reads No VCS, and clicking it offers Clone Git repository…. All Git calls run off the UI thread; everything degrades silently when a file isn’t in a repository orgitisn’t installed. (No new dependencies — Editora shells out to your real git, so credential helpers, SSH, and signing all work.) -
Theme commands in the palette — “Theme: Set App Theme…” and “Theme: Set Editor Theme…” open a fuzzy picker. The app-theme picker switches the chrome theme and the editor theme to match; the editor-theme picker changes only the editor colors (and pins it so it won’t follow the app theme).
-
Spell checking — misspelled words get a red wavy underline; right-click for suggestions (click one to replace), Add to Dictionary, or Ignore. In source files only comments and string literals are checked (identifiers aren’t flagged); plaintext and Markdown are checked in full. Toggle it with “View: Toggle Spell Check” (or the Settings window, where you can also choose the default dictionary language); pick the dictionary per file with “Spell Check: Set Language…” (ships English (en_US, en_GB), Spanish (es), and French (fr); your added words live in
dictionary.txt). Powered by Apache Lucene’s pure-Java Hunspell engine. -
Detect external file changes — when the file in the active tab is modified by another program, Editora notices (on window focus or when you switch to that tab) and offers to reload it, or keep your version (also when you have unsaved edits). The prompt only appears for the tab you’re looking at. Deleted files are left untouched. Editora’s own saves never trigger the prompt.
-
Smart backspace — pressing Backspace while the caret is in a line’s leading indentation deletes the whole indent in one press instead of one space at a time. On a blank, auto-indented line a single Backspace jumps back to the end of the previous line (undoing the Enter); on an indented line that has content, it clears the indent. Outside leading whitespace, Backspace behaves normally.
Changed
- Bookmarks now live in their own
bookmarks.json(in the config directory) instead of inside each session’sworkspace-state.json, so they’re all in one easy-to-find file. They remain scoped per project — switching projects shows only that project’s bookmarks (the global session has its own), and deleting a project deletes its bookmarks. Existing bookmarks are migrated automatically on first run (moved into the right per-project bucket; the old session files are cleaned up). This also fixes the cross-file “Jump to Bookmark” picker (M-g b) showing an empty list when a project was active. - Reorder bookmarks in the Bookmarks tool window — move a bookmark within its file, or a whole file
group, with Alt+Up/Down, the right-click Move Up/Down menu, or drag-and-drop (with the
same drag visuals as the editor tab strip: a translucent ghost, the dragged row dimmed, and an accent
insertion line on the drop target). The order you set is exactly the order the
M-g bjump picker uses, and it persists (and survives edits to the file).
Fixed
- The About dialog now shows the live config/settings path, so
--dev(~/.editora-dev/) and--config-dirare reflected (it previously always showed the default~/.editora/settings.toml).
Added
-
Zen-mode exit button — a small floating “Z” button appears at the top-right of the window while in Zen mode (same look as the Markdown preview controls); click it (tooltip: “Exit Zen mode”) to leave Zen. It’s hidden whenever Zen is off, and on Markdown files it sits just below the preview controls so the two don’t overlap.
-
Navigation key hints in the pickers — the Command Palette, the “Jump to…” pickers (recent files, bookmarks, structure, tool windows, snippets), and the file finder now show a footer legend of their relevant keys (move / select / cancel, plus Tab-complete in the file finder), matching the Switcher.
-
Focused tool window is highlighted — the panel that currently holds keyboard focus (Project, Structure, Bookmarks, File Information) gets an accent-tinted header, so it’s clear where you are when moving between panels and the editor.
-
--devcommand-line flag — runs Editora against a separate~/.editora-dev/config directory so a development instance can run alongside your everyday editor without sharing settings or session state. Config-dir precedence is now--config-dir>EDITORA_CONFIG_DIR>--dev>~/.editora/. A light-red “dev mode” badge appears in the toolbar (just left of the About icon) so the dev instance is visually distinct. -
Comment / uncomment (
M-;or the palette: “Edit: Toggle Comment”). A single line toggles a line comment; a multi-line selection toggles a block/region comment — using whichever the language has (e.g.//and/* */for Java/C-likes,#for Python/shell/YAML,<!-- -->for XML/HTML/Markdown,/* */for CSS,--for SQL). Falls back gracefully (block-only languages always wrap; line-only languages comment each line), preserves indentation, and is a no-op for languages without comments. -
Auto-close brackets and quotes — typing
(,[,{,",', or`inserts the matching closer and keeps the caret between; typing the closer (or a quote) when it’s already next to the caret types over it; typing an opener/quote with a selection wraps the selection; and Backspace inside an empty pair deletes both halves. Quotes are not auto-paired next to a word character (so apostrophes indon'tare left alone). -
Highlight matching brackets — when the caret is next to a
(),[], or{}, both it and its match are highlighted. -
Auto / smart indentation for all 21 languages. Enter keeps the current line’s indentation; a block-opener adds a level (braces
{ ( [;:in Python/YAML;do/then/inin shell;def/class/do/… in Ruby; an open tag in XML/HTML); pressing Enter between a matching pair ({}/()/[]or<a>|</a>) opens an indented stanza with the closer dropped below; and typing a closer (a)]}bracket alone on the line, or a keyword likeend/fi/done) re-aligns the line to its opener. The indent unit (tab vs spaces) is inferred from the file. -
Ctrl + mouse wheel zooms the Markdown preview while in Preview mode (scroll up/down to zoom in/out, driving the preview’s
−/+); the editor text zoom is left untouched there. In Editor and Split modes Ctrl+wheel still zooms the editor text. -
Snippets (VS Code / TextMate-style) — expand templates with interactive tab stops. Type a prefix and press Tab to expand (Tab still indents when nothing matches), or pick from the “Snippet: Insert…” fuzzy list (
C-c i). After expanding, Tab / Shift-Tab cycle the fields, placeholders are pre-selected to overtype, mirrored fields update live, and$0is the final caret. Bodies use the standard syntax —$1,${1:default}, mirrors,${1|a,b|}choices (a dropdown appears on the field), variables ($TM_FILENAME,$TM_DIRECTORY,$CLIPBOARD,$CURRENT_YEAR, the selection, …) and\$escapes. Snippets ship for all 21 highlighted languages — most from the MIT-licensed friendly-snippets collection (attributed inNOTICE), the rest written for Editora. Add your own in~/.editora/snippets/<language>.json(orglobal.json) — “Snippet: Edit User Snippets…” opens the file and “Snippet: Reload Snippets” picks up changes. User snippets override bundled ones. -
Read-only / View mode — toggle a buffer read-only with
C-x C-q(or the palette: “View: Toggle Read-Only”), so it can’t be edited by accident. Typing and the editor’s own edit commands are blocked (with a status-bar hint), while highlighting, minimap, folding, scrolling, and copy keep working. A file opens read-only automatically when it isn’t writable on disk, a status-bar toggle flips it either way (“Read-Only” ⇄ “Editable”) and the tab title is muted, and the per-file state is remembered across restarts. A Word-style “View Mode” banner docks above the editor with the navigation hint and an Enable Editing button (shown only when the file is writable). While read-only, Space pages down and Backspace pages up (pager-style, likeless/man). -
More command-line options —
--version/-Vand--help/-h(print and exit, no window); positionalFILE,FILE:LINE, andFILE:LINE:COLUMNto open a file (and jump);--project[=]<dir>to open a folder as a project (only when Projects are enabled, else ignored); and--zento start in Zen mode. File/project args are additive on top of the restored session. Cross-platform. -
Custom config folder — point Editora’s config directory somewhere other than the default
~/.editora/with the--config-dir <path>command-line argument (or--config-dir=<path>) or theEDITORA_CONFIG_DIRenvironment variable. Precedence:--config-dir>EDITORA_CONFIG_DIR>~/.editora/. Works on macOS, Linux, and Windows. -
Text zoom — quickly scale the editor text up/down independent of the configured font size (the set size is 100%). Use the status-bar
− 100% +control,C-=/C--(andCtrl+Plus) /C-0to reset, Ctrl + mouse-wheel, or the palette (“View: Zoom In / Out / Reset Text”). The zoom level persists across restarts and is separate from the font-size setting (it’s not shown in Settings). -
Markdown preview — an IntelliJ-style 3-mode view for Markdown files via a small floating control at the top-right of the editor: Editor, Editor + Preview (side-by-side), and Preview. The preview is rendered natively (no WebView) from CommonMark + GitHub-flavored Markdown (tables, task lists, strikethrough, autolinks), updates live as you type (debounced, off-thread), follows the active theme, and remembers its mode per file. The preview has −/+ buttons (top-left) to zoom the text in/out. Also available as palette commands (“Markdown: Editor / Editor and Preview / Preview”, and “Markdown: Zoom In / Out / Reset Preview”).
-
Bookmarks — mark lines and jump back to them, across files. Toggle a bookmark on the caret line with
C-c m, or click the gutter to add one (clicking an existing bookmark asks before removing it); a light-orange bookmark glyph appears in the line-number gutter. Each bookmark can carry an optional note (“Bookmarks: Edit Note…”). The Bookmarks tool window (M-2) lists every bookmark across all files (open or closed), grouped by file, with a filter box; Enter opens the file and jumps to the line, and right-click edits the note or deletes.C-c ]/C-c [cycle through the current file’s bookmarks, andM-g b(“Bookmarks: Jump…”) is a fuzzy cross-file picker. Bookmarks persist with the session (per project) and track their line as you edit above them. -
The Project tool window’s file tree now colors its icons to match the active theme: folders use the theme accent and files a muted tone. The colors are driven by two looked-up CSS variables (
-project-folder-color/-project-file-color) that default to the GUI theme (AtlantaFX) and are overridden per editor theme, so the tree tracks the chosen code theme. Files open with unsaved changes are marked in the tree like dirty tabs — a ”• ” prefix and amber italic — updating live as you edit and save. -
Reorder the tool-window stripe icons: drag an icon up/down within its side, or use the new ▲/▼ buttons next to each row in Settings → “Tool window placement”. The order persists in the session state (
workspace-state.json). -
Projects (VSCode single-folder-workspace style), off by default — enable via Settings → “Enable projects” (when off, the Project tool window, the toolbar open-folder icon + project switcher, and the project commands are all hidden). Each project is a root folder plus its own saved session (open files with carets/pins, active file, folds, tool-window layout). Open one via the toolbar’s open-folder icon (native dialog) or “Project: Open Folder…” (
C-x C-p, a keyboard folder picker likeC-x C-f); “Project: Switch…” (C-x p) saves the current session and restores another’s; “Project: Close” returns to the global session (with confirmation); “Project: Delete…” (also a trash button in the Project tool window) removes a project from your list — its folder and files on disk are kept, only the project entry and its saved session are removed. The active project is shown in the window title and a project switcher combobox in both the toolbar and the Project tool window (each with a “No Project” entry that returns to the global session without closing any project). The Project tool window shows the project’s files as a lazy tree with Emacs-style keyboard navigation (C-n/C-p, C-f/C-b, Enter to open), a filter box that runs a bounded project-wide filename search (like the Structure filter), and a right-click menu to rename or delete files (syncing open tabs). Opening a recent file that belongs to another project switches to that project first (restoring its session/tree) before opening the file. Settings stay global. -
Keyboard file finder (Emacs
find-filestyle) onC-x C-f: a path popup with a live directory listing that prefix-autocompletes as you type.Tabcompletes the common prefix, Enter descends into a folder or opens a file (a non-existent path opens a new buffer, written on save),C-n/C-pmove,Esccancels — no mouse needed. Also thefile.findpalette command. The toolbar Open icon (andfile.open) still uses the native OS dialog. -
Fold/unfold the single region at the caret:
view.fold(C-c C-f) collapses the innermost region around the caret,view.unfold(C-c C-u) expands it, andview.toggleFold(C-c C-t) toggles it — complementing the existing Fold All (C-c f) / Unfold All (C-c u). -
Keyboard “Jump to…” pickers — command-palette-style fuzzy popups: recent files (
C-x C-r), the active file’s structure/symbols (M-g i), open files/tabs (C-x b), and tool windows (M-g t). Type to filter,C-n/C-por ↑/↓ to navigate, Enter to act (open the file / jump to the symbol / switch tabs / open the tool window),Escto cancel. All are also in the command palette (“Recent Files: Jump”, “Structure: Jump”, “Open Files: Jump”, “Tool Windows: Jump”). -
More Emacs movement keys:
M-m(back to indentation),C-l(recenter the caret line in the viewport),M-{/M-}(backward/forward paragraph), andM-a/M-e(backward/forward sentence) — all registered commands, so they also appear in the palette. Note: on macOS the Option dead keys (Option+e/i/u/n/`) are intercepted by the OS for accent composition and never reach the app, soM-e(forward sentence) can’t be triggered by keyboard there — use the palette command “Go: Forward Sentence” instead. -
Auto save (VS Code-style), off by default. Choose a mode in Settings: “After delay” saves a file once it’s been idle for a configurable number of seconds (default 1), or “On focus change” saves when you switch editor tabs or the window loses focus. Only dirty, file-backed, writable buffers are saved (untitled/read-only ones are skipped); writes happen off the UI thread. Cycle the mode with the
file.toggleAutoSavepalette command orC-c a. -
File breadcrumb bar (IntelliJ-style) along the bottom, above the status bar: shows the active file’s path as clickable segments. Click a segment to drop down that folder’s contents (sub-folders first, then files); pick a folder to drill in (it becomes the trailing crumb and the dropdown reopens) or a file to open it. Long paths scroll, anchored to the file. Off by default; toggle via the Settings checkbox, the
view.toggleBreadcrumbpalette command, orC-c p. -
Runnable fat jar:
mvn -Pfatjar packagebuildstarget/Editora-<version>.jar, launchable withjava -jar(bundles JavaFX classes + natives for the build host’s platform via a non-ApplicationLaunchermain class). The release pipeline builds one per platform and attaches them to the GitHub release alongside the native installers. -
Zen mode (distraction-free): one toggle hides the editor view options (80-column ruler, current-line highlight, line numbers, minimap, hidden characters), the toolbar, status bar, and tab bar, and all open tool windows — leaving just the editor. While in Zen you can still switch individual items back on (e.g. line numbers or the status bar); toggling Zen off restores your previous configuration exactly. The mode persists across restarts. Via the Settings checkbox, the
view.toggleZenpalette command, orC-c z. -
Toggle the toolbar, the status bar, and the tab bar — via Settings checkboxes (“Show toolbar”, “Show status bar”, “Show tab bar”), the
view.toggleToolbar(C-c t) /view.toggleStatusBar(C-c s) /view.toggleTabBar(C-c b) palette commands, and persisted in settings. -
Application icon: the Editora logo now appears as the window/dock/taskbar icon, in the About dialog, and as the native installer icon (macOS
.icns, Windows.ico, Linux.png), all generated frombranding/editora-icon.svg. -
Emacs-style text selection:
C-SPCsets the mark, then any caret-movement chord (C-f/C-b/C-n/C-p/M-f/M-b/C-a/C-e/M-</M->/C-v/M-v) extends the selection from it.C-x C-xexchanges point and mark;C-g(and a mouse click, cut/copy/paste) clears the mark. The region works withC-w/M-w/C-y. -
Bundled monospace fonts (no install needed): JetBrains Mono (the new default), Cascadia Code, Fira Code, IBM Plex Mono, and Source Code Pro. They’re loaded at startup and listed first in the Settings font picker. (Existing configs keep their saved font; the new default applies to fresh installs.)
-
Editor color themes (syntax tokens + editor surface) chosen in Settings under “Editor theme”: Primer Light/Dark, Nord Light/Dark, Cupertino Light/Dark, Dracula (these match the AtlantaFX themes), plus JetBrains-style Islands Light and Islands Dark. Selecting an AtlantaFX app theme switches to the matching editor theme automatically, until you pick an editor theme yourself.
-
Editor tabs can be reordered by dragging them with the mouse. Pinned tabs stay grouped at the front (a drag is clamped to the dragged tab’s group).
-
Very large files (50 MB or larger) now open read-only with a capped load — at most the first 50 MB is read (so a multi-GB log can’t exhaust memory) and editing/undo are disabled; the status bar notes the truncation.
-
Undo history is now bounded (300 entries per view) instead of unlimited, and is disabled entirely in large/huge-file mode, capping undo memory.
-
Large-file mode: opening a file 5 MB or larger skips syntax highlighting and the minimap (regardless of view settings) to stay responsive, and the status bar announces this when it happens.
-
The main window now remembers its size, position, and maximized state across launches (stored in
workspace-state.json). A saved position that no longer lands on a connected screen falls back to the default centered window. -
Release pipeline: pushing a
vX.Y.Ztag builds native installers for Linux (x64/arm64), macOS (x64/arm64), and Windows (x64) on per-platform GitHub runners and publishes a GitHub Release via JReleaser (jreleaser.yml+release.yml). Installers are currently unsigned. -
Session restore: on launch, reopens the files that were open at last exit (in tab order), reselects the active tab, and restores each file’s caret position (scrolled into view). Missing files are skipped; an empty session opens a fresh buffer.
-
Editor split view: toolbar icons (and
view.splitVertical/view.splitHorizontal/view.unsplitcommands,C-x 3/C-x 2/C-x 1) toggle a second synced view of the current file — side by side or stacked. Edits/highlighting stay in sync; scroll, caret, and minimap are per-pane. The icons reflect the active split state. -
“Show hidden characters” view option: renders markers for spaces (·), tabs (→), and line ends (¶) on a transparent overlay without altering the document. Toggle via
view.toggleWhitespace(C-c w) or Settings; off by default. -
The About dialog now shows the build date/time (baked in at build time).
-
Editor tab right-click context menu: Close, Close Other Tabs, Close All Tabs, Close Unmodified Tabs, Close Tabs to the Left/Right, Copy Path, Pin/Unpin Tab, and Rename File…. Pinned tabs are marked with a pin icon, kept grouped at the front, and skipped by the bulk-close actions. Each action is also a palette command (
buffer.closeOthers,buffer.closeAll,buffer.copyPath,buffer.togglePin,buffer.rename, …). Tabs now show a full-path tooltip on hover. -
Status bar segments: live cursor position/selection, language, indentation (tab size), line endings, file size, and encoding, shown right of the message area. Segments are clickable and dispatch commands —
nav.goToLine(M-g g),buffer.setLanguage(override the syntax grammar),buffer.setTabSize, andbuffer.convertLineEndings(LF/CRLF). Tab size now affects the minimap. -
Syntax highlighting via bundled TextMate grammars (using tm4e) for 21 languages: Java, XML, shell, PowerShell, DOS batch, Python, Groovy, Kotlin, Ruby, C, C++, Rust, Go, C#, Markdown, JSON, CSS, HTML, YAML, INI, and SQL. Stateful tokenization carries grammar state across lines so multi-line constructs (block comments, heredocs, fenced code) highlight correctly.
-
Code folding now covers more languages (C, C++, Rust, Go, Kotlin, Groovy, C#, CSS) in addition to Java, JSON, XML/HTML, and Markdown.
-
NOTICEfile attributing the bundled TextMate grammars and third-party libraries. -
Structure tool window: a collapsible tree of the active file’s foldable regions with a search filter and Emacs-style keyboard navigation. Selecting an entry navigates the editor and anchors the target line at the top of the viewport. Bound to
M-7. -
C-x o(Window: Other) to cycle keyboard focus between the editor and open tool windows. -
Code/text folding with
view.foldAll(C-c f) andview.unfoldAll(C-c u); fold state persists across sessions. -
Editor view options for line numbers and a minimap.
-
Theme switching and a recent-files list.
-
IntelliJ-style left/right/bottom tool windows, a buffer Switcher, and a File Information panel.
-
Toolbar icons, a settings window, and an expanded Emacs keymap.
-
Command palette (
M-x) with fuzzy matching, driven by the command registry. -
GitHub Actions CI workflow with a status badge, plus a Maven wrapper.
-
README, MIT license, and
CLAUDE.mdproject guide.
Changed
- Opening a tool window now moves keyboard focus into it and selects its first item (the first
folder/file in Project, the first bookmark, the first symbol in Structure), so you can navigate it
with the keyboard immediately. Restoring tool windows on startup/session-switch does not steal focus.
While a tool window is focused, global commands still work —
M-x, the tool-window toggles (M-1/M-2/…), theM-g …jumps, andC-x …prefixes; the panel only intercepts the editor navigation chords it reuses (C-n/C-p,C-f/C-b, …). - The Switcher (
C-x C-b) now lists only the open files — the Tool Windows column was removed. It’s a single column in tab order, with a bold header and a fixed width (sized to the longest file path on open) so it no longer resizes while you navigate. Tool windows are still reachable via their stripe icons and the “Tool Windows: Jump” picker (M-g t). - Silenced the benign tm4e/Oniguruma grammar warnings that flooded the console
(
'…]' without escape,No grammar source for scope …) by raising theorg.eclipse.tm4elog level toSEVEREat startup. They were harmless bundled-grammar quirks; real errors still surface. - The Switcher (
C-x C-b) now lists all open files (labeled “Open Files”, most-recently-used first) instead of only previously-activated ones. It is also larger with bigger column headers; the selection bar now tracks the active column (vivid in the focused column, dimmed in the other), and the highlighted file’s full path is shown at the bottom of the popup, above a legend of the navigation keys. A “Switcher: C-x C-b” hint on the toolbar makes the keybinding discoverable. - Syntax highlighting is now incremental: an edit re-tokenizes only from the changed line to the end of the document (reusing stored per-line grammar states for the unchanged prefix) instead of the whole file, lowering highlight latency on larger files. Still runs off the UI thread with stale-result discarding.
- App chrome (toolbar/tool-window icons, status bar, tool windows, command palette, switcher, find bar, File Information) now uses AtlantaFX theme variables instead of hardcoded light colors, so it adapts to dark themes (icons and text stay legible).
- Clearing the recent-files list now asks for confirmation; each entry in the recent-files menu has an inline ✕ icon to remove just that file (no confirmation).
- Go to Line (
M-g g) now accepts an optional column asline:column(e.g.342:35); a bare number still goes to the line’s start. The dialog notes the column is optional and the column is clamped to the target line’s length. If the target line is inside a collapsed fold, the region is unfolded so the line is shown. - File Information tool window: values are now selectable/copyable (read-only text fields), with tighter padding and without the boxed section borders. Long values (e.g. the full path) no longer overflow the panel, which previously truncated the key labels to ”…” and showed scrollbars.
- The Settings font-family picker now lists only monospaced fonts, with a note explaining the filter (a previously saved non-monospaced font stays selectable).
- Quitting now asks for confirmation first (the Quit button,
C-x C-c, and the window close button), in addition to the existing per-buffer unsaved-changes prompts. - User preferences are now stored as TOML in
~/.editora-v2/settings.toml(wassettings.json). Session state (fold regions, tool-window layout) moved to a separateworkspace-state.json; recent files stay inrecent-files.json. No migration — a pre-existingsettings.jsonis ignored. - Closing a pinned tab now asks for confirmation first (the X, the Close menu item, and the close command); bulk-close actions still skip pinned tabs.
- The Structure tool window now labels entries with the symbol name and kind (functions, types/classes, namespaces, Markdown headings, XML tags) derived from the TextMate grammar, and hides trivial blocks (if/for/while/…), instead of showing the raw brace line. Works across all bundled languages; clicking a node jumps to the symbol’s line.
- Folded regions now shade their header line (in addition to the gutter chevron) so collapsed code is easier to spot.
- Opening a file that is already open now switches to its existing tab instead of opening a duplicate.
- The recent-files picker is now a toolbar menu button (with a per-entry remove action) instead of a combo box.
- The key dispatcher now yields only single-key chords to windows that own
their keys (e.g. the Structure tool window); multi-key Emacs chords such as
C-x ostay global so window switching andC-x/C-ccommands work from anywhere.
Fixed
- Bookmarks no longer drift to the wrong line when a file is edited outside the editor. Bookmarks followed their content only for edits made inside Editora, so changing a file in another program left its markers on stale lines. Each bookmark now re-anchors to its saved line text when the file is opened (re-found at the nearest matching line), and the corrected position is saved back so the session self-heals.
- Adding a bookmark no longer shifts that line’s text indentation. The gutter now reserves a fixed-width bookmark column on every line, so the marker glyph no longer widens only the bookmarked row (which had pushed its text rightward).
- A bookmark’s gutter marker now follows its line when an edit moves it. Inserting or deleting lines above a bookmark already moved the bookmark (and its Bookmarks-panel entry), but the gutter glyph wasn’t repainted onto the new line; it is now.
- Switching projects (or “No Project”), or closing/deleting a project, while in Zen mode no longer leaves the UI permanently mangled. Zen stores its “everything off” view state in the global settings while keeping the restore snapshot in the per-session state; swapping sessions orphaned that snapshot. The editor now exits Zen (restoring the real view settings) before a session switch and lands the incoming session in normal view.
- The
M-</M->(document start/end) andM-%(find & replace) keybindings now work. They were stored in Emacs notation (M-<) but the dispatcher emits shifted symbols asM-S-,etc., so the entries never matched; the keymap now uses the dispatcher’s token form. - No more light-gray flash on startup with a dark theme: the scene is pre-filled with the theme’s background color, so the first frame paints in the theme color instead of JavaFX’s default background before the CSS is applied.
- Much faster startup when restoring a session: all tab headers are created first (so they appear at once), then each file’s content and folds are loaded one per pulse — the active file first — keeping the UI responsive. Previously a session with a heavily-folded file could freeze the window for several seconds on launch.
- Folding no longer smears the hidden lines’ numbers onto the fold-header row in the gutter: a collapsed paragraph’s gutter cell is laid out at zero height, but its line-number label did not clip to it; the gutter is now clipped to its bounds.
- “Show hidden characters” no longer piles stray space/tab/EOL markers onto the fold-header row when a region is collapsed: folded (hidden) paragraphs are now skipped by the whitespace overlay (and the 80-column ruler’s measurement).
- Running a command via a chord that opens a dialog no longer types a stray
character afterwards (e.g.
M-g gthen OK inserted ag): the dispatcher now also swallows the character event paired with a key press it handled, which a modal dialog (showAndWait) would otherwise deliver to the editor after closing. M-(Meta) chords no longer insert a stray character on macOS. Option is the Meta key, andOption+<key>also emits a special character (e.g.M-f→ “ƒ”,M-x→ ”≈”) whose KEY_TYPED was inserted into the editor (or palette) after the command ran. Characters typed with Alt held are now swallowed on macOS — globally by the key dispatcher and in the command palette popup. (Scoped to macOS so AltGr-composed characters keep working on other platforms.)- Command palette entries can now be run with the mouse — clicking a command runs it (previously only Enter on the keyboard-selected row worked).
- Next/previous line (
C-n/C-p) now move the caret like Emacs: they were bound to RichTextFX’s scroll-basednextLine/prevLine(which move relative to the viewport and no-op when the caret is off-screen). They now move by paragraph and preserve a goal column, so passing through short lines keeps the original column. - Syntax highlighting no longer intermittently fails to load when multiple files of the same language are open. They share one TextMate grammar instance, and tm4e’s tokenizer is not thread-safe, so concurrent highlighting on each buffer’s background thread could throw and silently drop a file’s highlighting; tokenization is now serialized per grammar.
- The minimap no longer stretches short files across its full height (scattering sparse blocks): line height is capped, so short documents fill from the top and long documents still compress to fit.
- Pinned tabs are now remembered across sessions: a tab’s pinned state is saved on exit and restored on the next launch (alongside the open files and carets).
- The 80-column ruler now lands exactly on column 80. It was positioned from a font-probe character width and ignored the line-number gutter, so it drifted off the text grid; the column is now derived from the editor’s live layout (caret positions, glyph-independent). It is hidden when column 80 is outside the visible text width (window too narrow, or scrolled past it).
- The 80-column ruler now tracks horizontal scroll instead of staying pinned to a fixed x-offset when a horizontal scrollbar is present.
- The Structure tool window no longer shows “No structure” for documents whose regions were computed before the panel attached.
- The editor context menu now dismisses on left-click.