[0.12.3] - 2026-08-17
Fixed
-
A file opened from the file manager now gets a window of its own instead of landing as a tab in whatever window you were working in.
Before the single-instance handoff, such a click started its own process and therefore its own window. The handoff was meant to stop duplicating the process — a second JVM, a second set of language servers — not to change what the click does; but it took over the window in front of you, and with the “Editora Expert Mode” entry restyled that window’s chrome as well. The requested focus mode now applies to the new window, where it is unambiguous, rather than being imposed on one already in use.
Unless the file is already open, in which case the window holding it is brought forward instead. Two independent buffers over one file loses edits — save one and the other is silently stale behind an external-change prompt — and re-opening a file you already have open is an ordinary thing to do.
These windows are deliberately left out of the saved layout, so a file opened from the file manager does not come back as an empty window on the next launch.
[0.12.2] - 2026-08-17
Fixed
-
A file opened from the file manager now brings the running Editora to the front, instead of leaving it behind the file manager with a GNOME notification you have to click.
This was the cost of the single-instance handoff added in 0.12.1, and the cause is the compositor rather than Editora. When a file manager launches a new process it hands that process an activation token, so the window it maps counts as user-initiated and gets focused. Forwarding breaks the chain: the token goes to the forwarder, which delivers the file and exits without ever mapping a window, while the process that owns the window is an older one you have not touched recently — so its raise is an unsolicited focus request from a background application, which GNOME refuses, flagging the window as demanding attention instead.
The window is now briefly pinned above the others to bring it forward — which the compositor honours, because that is a window state rather than a focus request — and released straight after, so nothing stays on top. The raise also happens before the file is opened rather than after, so it is immediate instead of waiting on the file to load, and still happens if opening fails.
[0.12.1] - 2026-08-17
Added
-
Opening a file from the file manager now reuses the running Editora instead of starting a second one. On Linux and Windows a file manager delivers the path as a command-line argument, which by definition means a new process — so clicking a file while Editora was already open paid a full cold start and left a second editor resident (measured: two JVMs at 670 MB and 1707 MB, for what looked like one application). macOS never had this problem, because Finder delivers an Apple Event that reaches the running app; this is the cross-platform equivalent, and it hands the launch to the very same code path that event does.
A second launch now delivers its files over a loopback socket and exits — measured at ~735 ms (JVM startup, which a new process cannot avoid) against ~1.5 s and a permanently resident second editor. The files open in the focused window, and a focus mode the launcher asked for is applied, so the “Editora Expert Mode” entry still means Expert Mode.
Deliberately narrow: only a launch that is purely “open these files” is handed over.
--project,--new-file,--config-dirand--devshape how a process starts and have no honest reading inside a running window, so those still get their own editor — as doeseditorawith no files at all.--new-instanceforces a separate editor in any case. An instance is scoped to its config directory, so a--devlaunch can never hand off to your real editor.The endpoint is loopback-only,
0600, and carries a random token; it is removed on exit, and an endpoint left behind by a crash is detected and reclaimed on the next launch. If anything about the handoff fails, the launch simply starts its own editor — the behaviour before this existed.
Changed
-
Startup is ~225 ms faster to the first frame that shows your file (measured on a packaged Linux build opening a file with
--expert: median 1702 ms → 1477 ms, ~13%). Four pieces of work were running on the JavaFX thread before the editor had painted, none of which the first frame needs:-
The minimap was the largest single piece of app code on the path to first paint (~135–230 ms). Its render iterates every paragraph and then calls
snapshot(), which forces a synchronous full-scene layout, and finishes by forcing aVirtualFlowlayout on top of that — and at startup the triggers arrive in a burst (theme colors, tab size, content settling), so the whole sequence ran three times over before the text appeared. Renders now coalesce to one per pulse, and the first is held until the editor has painted. The minimap is a secondary navigation aid; nothing about it needs to precede the text you are waiting for. Typing is unaffected — the 200 ms edit debounce already paced those renders. -
The debug adapter locations for Python and JavaScript were resolved on the JavaFX thread at startup, walking the VS Code / mason / plugin directories (~30–50 ms) for values not read until a debug session actually launches — and they were resolved again, off-thread, moments later, by the existing availability probes. Only the off-thread resolution remains. The Java bundle is still located synchronously: it has to reach the LSP layer before any jdtls session starts.
-
The rest of the session restore now yields a couple of frames before continuing. The file you asked for is already front-loaded, but the remaining tabs were filled in the very pulses that lay out and render it, so with a session of any size they pushed back the frame you were waiting for.
-
Fold recompute no longer forces a layout pass to discover it has nothing to do. Locating the viewport (
firstVisibleParToAllParIndex) forces aVirtualFlowlayout, and the recompute did it on every run — including the ones whose fold structure was identical, where the loop it feeds does nothing at all. That is the common case twice over: at startup the recompute triggered by setting a buffer’s language runs against a still-empty document (~10 ms of pure forced layout), and while typing this runs on every 250 ms settle, where most edits leave the fold structure untouched. It also stops perturbingestimatedScrollY, which that query does as a side effect, on runs that change nothing.
If a startup regression is ever suspected, measure it rather than reason about it:
scripts/measure-startup.shreports the phase breakdown, andEDITORA_PERF_SAMPLE=1now adds a per-phase sample of what is holding the JavaFX thread. Both of the costs above read as “rendering is slow” from the call graph, and neither was. -
[0.12.0] - 2026-08-16
Added
-
Tool window docking. A tool window can now be maximized over its split, re-docked to another stripe by dragging its button, shared with a second window on the same side, and floated into its own window. Maximize and float have a header button each and a palette command; a side holds at most two windows, split vertically on the left and right and horizontally at the bottom, where two consoles side by side are the only way both stay readable. Floating windows remember their bounds, and reuse them only when they still overlap a screen — a window saved on a since-detached monitor is re-centred rather than opened somewhere unreachable.
Maximize is expressed as divider positions rather than by reparenting, so focus, stripe state and remembered sizes all survive it. The two ways that could have shipped broken: a
SplitPanehonours its items’ minimum widths, so without zeroing them for the duration a “maximize” stops part-way and reads as a bug; and closing or persisting while maximized would record a 0-or-1 divider as that window’s size, reopening it next session covering the editor, permanently. -
A pom.xml summary preview. A
pom.xmlopens with its own rendering instead of the generic XML tree: coordinates and parent, then modules, properties, dependencies, managed dependencies, plugins, managed plugins, and each profile’s own set — with the artifact name and its version in aligned columns. The XML tree is one toggle away (pom.toggleView, or the preview’s right-click menu), and Settings → Editor → File previews turns the whole thing off.The point is the two indirections that otherwise leave you scrolling the file to answer “which version is this?”: a
${property}version is shown resolved, with the reference kept beside it, and a blank version is filled from the file’s own<dependencyManagement>and tagged managed. What the file cannot answer, it says — a version inherited from a parent pom reads inherited rather than a number Editora would be inventing. No parent pom is read, so this is a reading of the file, not an effective pom. -
Maven: update the versions in an existing project.
maven.updateVersionschecks the nearestpom.xml’s dependencies and plugins against Maven Central and shows what would change before writing anything — a row per artifact, current → latest, behind a dialog you can decline. The update is applied through the open buffer as a single edit, so one Ctrl-Z takes the whole thing back.Maven Central’s
<release>marker is deliberately not trusted (it means “newest non-snapshot published”, which for maven-surefire-plugin was a milestone for years), and a version is never walked backwards, which an unguarded set-to-latest would do to a pom pinned on purpose. -
A Maven submenu on the right-click menu — on a
pom.xmlin the editor, and on a folder or apom.xmlrow in the Project tree. One builder serves every surface, so they cannot drift into offering different actions, and it offers nothing at all when Maven is off or there is no pom. -
New Maven Project: advanced options. A collapsed section carrying the project
<url>, the Java release (a combo of the JDK majors actually installed, still editable), and an update-to-latest-versions checkbox. None of the three can be an archetype property —archetype:generateignores them and quickstart bakes its own into the pom template — so they are applied as a post-generation, format- preserving pom edit. Empty means “keep what the archetype wrote”. -
An LSP submenu in the editor’s right-click menu. A fully-featured server contributes up to eight actions, which pushed cut/copy/paste and the spelling suggestions far enough down the menu to hunt for; and a bare “Go to Definition” between “Paste” and a spelling suggestion named nothing about where it came from. Which actions a server contributes is unchanged — the submenu is only their container.
Changed
-
File Templates is no longer labelled beta in Settings.
-
The in-app jdtls install is pinned to a milestone build rather than the rolling
-latesttarball. Two users installing a week apart were getting different servers, and an untested overnight build shipped straight to them — which matters more for jdtls than for most servers, because Editora’s handling of it is measured against specific builds. A test fails if the pin drifts between the two files that carry it.
Fixed
-
Recent files that no longer exist are no longer offered. A recent list outlives the files in it, and an entry that cannot open is worse than no entry. Remote (SFTP) entries are deliberately never checked — that is a network round trip per entry, on the UI thread, every time the menu is built, and it answers “gone” for a host that is merely asleep. Entries are filtered rather than pruned, so a file on an unplugged drive comes back by itself.
-
Find in Files showed two different icons for one feature — the toolbar button and the tool window disagreed, the window carrying the plain magnifier that belongs to the in-file find bar.
-
The tool stripe’s drop-zone highlight stuck after a drag. Reordering a stripe button by dropping it on a neighbour left the dashed border painted for the rest of the session.
-
Creating a Maven project beside an existing one failed outright (“Unable to add module to the current project as it is not of packaging type ‘pom’”).
archetype:generateregisters the new project as a<module>of whatever project it finds and refuses when that one is an ordinary jar project. Generation now happens in a scratch directory and the finished project is moved into place; an aggregator (packaging=pom) is still left attached, where the module is what you want. -
“Failed to open” for a file that had just opened. Launching Editora with a position —
Editora notes.txt:42, which is what a terminal’s clickable-link setting sends — opened the file at the line and reported a failure. macOS delivers a launcher argument through theopenFilesApple Event as well as on argv, and the two were parsed differently: argv understood the:line[:column]suffix, the event took the whole string as a filename and looked for a file that had a colon and a number in its name. Both paths now go through the same parse, and OS-delivered files honour a line number as command-line ones always have. Existence is asked first, because a colon is a legal character in a macOS filename: a file really callednotes:1opens as itself rather than as line 1 ofnotes. -
Every debug launch logged a parse failure for a reply that was correct. jdtls answers the “where is java?” request with a bare path, which is not JSON; it was parsed as JSON anyway, logged a stack trace, and then fell through to the same value. Nothing was broken except the log — which is the problem, since a routine failure firing on every launch trains you to ignore the one that matters.
Internal
-
Routine dependency bumps, taken as one batch: JavaFX 26.0.1 → 26.0.2, Jackson 2.19.0 → 2.22.1, Gson 2.11.0 → 2.14.0, Lucene 10.2.2 → 10.5.0, java-diff-utils 4.12 → 4.17, joni 2.2.6 → 2.2.7, jcodings 1.0.58 → 1.0.64, JUnit 5.11.4 → 5.14.4. All patch or minor.
Verified with a packaged build as well as the suite, because a dependency change here can break the installer while every test passes: java-diff-utils is an automatic module carrying a moditect descriptor, and JavaFX is linked into the runtime image. The app image builds and its AOT training run — which launches the real GUI — completes, which is what says the jimage is whole.
Deliberately not taken: PDFBox, POI and MINA SSHD, whose jlink descriptors are hand-written and want a build and a launch of their own; commonmark, where a 0.x minor can move the API; and the early-access, alpha, milestone and major offers (JavaFX 28-ea, slf4j 2.1.0-alpha1, SSHD 3.0.0-M5, JUnit 6, LSP4J 1.0.0, JSVG 2.1.0).
-
The toolbar’s run-configuration selector rebuilds its rows in one assignment rather than replacing them and then appending its trailing “Edit Configurations…” row. A ComboBox whose items shrink while its popup is being clicked makes JavaFX throw out of its own event handling — reproduced against JavaFX 26 — and the two steps left the list one row short in between. Not presented as the fix for the crash that prompted the look: nothing in that report names a control, and this was simply the one place in the app with the shape.
-
RunConfigToolbarFxTestno longer races the program it launches. It asserted the Stop button was enabled straight after startingjava -version, and “is it running” is a live reading of the OS — so if the UI thread was descheduled for longer than that program takes, the child was already gone. It now runs a sleeper it stops itself, which also covers what the Stop button is for. -
An uncaught exception on the UI thread now records what had keyboard focus beside it. A crash inside a JavaFX control’s own event handling produces a stack with no application frames at all, so it names the control’s class and nothing about which of the app’s many lists or trees it was — which is the difference between a report that can be acted on and one that cannot.
-
The build’s AOT cache is now trained under the same garbage collector the packaged app runs. The switch to G1 in 0.11.0 updated the launcher but not the trainer, which kept training under SerialGC. No user-visible effect: a GC mismatch turns out not to invalidate the cache, the archived heap data follows the runtime collector rather than the training one, and 8 interleaved startup runs of each build found no difference beyond the noise. A test now fails if the trainer’s options and the launcher’s drift apart again, since the next flag to diverge may not be as harmless.
-
scripts/measure-startup.shruns on macOS. It usedtimeoutanddate +%s%3N, both GNU-only and neither present on a stock macOS, so the startup harnessCLAUDE.mdpresents as the permanent way to measure cold start could not run on the primary development machine at all. Both are now capability- detected. Three further defects surfaced while fixing it: agrepthat matched nothing killed the script underset -ebefore its own diagnostic could print, so a failed measurement exited 1 explaining nothing; the log that diagnostic pointed at was deleted by the exit trap before anyone could read it; and the usage text was a hardcoded line range that had already drifted. Measured impact of the missing timestamp: the fallback origin overstated time-to-first-paint by roughly 30% on macOS, all of it in the pre-mainphase.