norte v0.3.0-alpha.4
Pre-releasePre-release. Linux x86_64 only.
Downloads
norte_0.3.0-alpha.4_amd64.deb,norte-0.3.0-alpha.4-1.x86_64.rpm,norte_0.3.0-alpha.4_amd64.AppImage— the window (norte-gui) together with the daemon (norte) and the terminal client (ntc), in one package.norte-cli-x86_64-unknown-linux-gnu.tar.xz(norte),norte-tui-x86_64-unknown-linux-gnu.tar.xz(ntc) and their-installer.sh— without the window.
Requirements. Everything here was built on Ubuntu 22.04 and needs glibc 2.34 or newer; the window also needs WebKitGTK 4.1. Each artefact was installed and started in clean containers of Ubuntu 22.04 and 24.04, Debian 12 and 13, and Fedora 41 (ADR 0112). The AppImage expects the graphics and font libraries every desktop provides.
Checksums. SHA256SUMS covers every file; each archive also has its own .sha256. Nothing is signed yet.
Added
-
Your own themes by name. A theme saved as
~/.config/norte/themes/<name>.tomlis offered by the theme picker, the
first-run wizard and the settings screen in both frontends, previews live,
and can be set as[ui] theme = "<name>". The order is fixed: a bundled
preset first — a stalethemes/nord.tomlcannot change whatnord
means — then your themes directory, then the value as a path. A file that
does not parse is left out of the list rather than breaking the picker. -
norte theme importturns a Visual Studio Code colour theme into one
of yours:norte theme import OneDark-Pro.json --usewrites
themes/one-dark-pro.tomland sets[ui] theme. It follows the theme's
includechain, paints it overvscode-darkorvscode-lightso what the
theme leaves undefined is not monochrome, flattens translucent colours over
the editor background, and refuses a name that a bundled preset would
shadow. Comments and trailing commas in the JSON are fine;
tokenColorsis ignored. Seedocs/theming.md. -
Seti file icons. The
file-iconsextension has a fourth style,
seti: the icons Visual Studio Code shows by default, one per language
wherenerdhas one per class, so a Python file and a Go file no longer
look alike. They come from the same Nerd font, so the window's bundled
subset grew from 18 to 73 glyphs (16 KB) and a Nerd-patched terminal font
already has them. Twenty more extensions are recognised in every style
(html,css,vue,svelte,dart,scala,tex,xml…). -
Alt on its own opens the menu bar. In the window always (bridge 68):
press and release Alt with nothing in between, as on any desktop; Alt+F4,
Alt+Tab, an Alt-drag and AltGr do not count, and a dialog in front keeps
the keyboard as it does for F9. In the terminal behind[ui] alt_menu,
off by default: a lone modifier can only be reported under the kitty
keyboard protocol (kitty, foot, WezTerm, Ghostty — not tmux, xterm or
GNOME Terminal), and in that mode the terminal sends keys rather than
text: a letter typed with a dead key (é) or a symbol typed with AltGr
(@, #) arrives as its base key. -
Column widths by dragging in the terminal too. Drag the separator
that opens a column in the header; the width follows the pointer and is
written to[ui.columns]when you let go, the same key the window writes. -
Scrolling the window no longer flickers. Every update rebuilt the
menu, panel and key bars, the tabs, the title, the column header and
every visible row even when only the rows at the edge had changed; what
paints the same now stays the same node. The overscan grew from 8 to 24
rows, so a quick wheel gesture no longer shows blank rows at the edge
while the new ones travel. -
vscode-darkandvscode-lightpresets (spec 2026-09-11),
transcribed from Visual Studio Code's Dark Modern and Light Modern and
from the editor's built-in colour registry — a VSCode theme JSON is not
a complete palette, solist.*andscrollbarSlider.*come from the
registry, not from any file in theincludechain. Each preset's header
records where every role came from and where it diverges: on white,
VSCode's ownerror(3.35:1) andwarning(3.12:1) fall under the
4.5:1 that norte requires of a signal you have to read when something
has gone wrong, so both are darkened and the original values are named. -
The window paints a theme's file colours (ADR 0108, bridge 66).
[files.kind]and[files.ext]— half of what a theme file declares —
had never reached the window: every entry came out the same colour,
which reads as a broken theme rather than a plain one. An entry's
colour is resolved by the host against the filename's raw bytes and
travels in its row, because extensions are an open set and no CSS class
could name them. Four ofStyle's six attributes cross;bgand
reversestay behind so a theme cannot hide where the cursor is. The
scrollbar, row hover, widget surfaces and control focus rings now come
from the theme too, and the pane footer stops painting its text with a
border colour. -
Entry colours follow the desktop's colour scheme (bridge 67). With
theme_light/theme_darkset, flipping the desktop repainted the
chrome from one variant and left the file names coloured by the other. -
Ten chrome roles —
hover,input-background,input-border,
widget-background,widget-shadow,badge,scrollbar-slider,
separator,focus-borderandmuted— for the surfaces a modern
editor separates by elevation rather than by borders. A preset is not
required to define them (Role::COREis what completeness asserts):
the window's stylesheet derives each from a colour the theme already
has, so the eight existing presets gain nothing and change nowhere. -
The window, polished (spec 2026-09-11). Bundled typography —
JetBrains Mono for cells, Inter for chrome, 14 px on 22 px rows — with
[ui] font/mono_font/font_sizestill in charge; column widths by
dragging a header's edge, written to[ui.columns] widthand honoured
by both frontends, with the terminal's discard rule when the name would
fall under ten cells; keycaps on the key bar, small-caps headers, an
accent on the cursor, a mark checkbox on hover; breadcrumbs in the pane
title, a toast for notices, pills for persistent warnings, a two-pixel
gauge of the volume's usage in the footer;[ui] theme_light/
theme_darkfollow the desktop's colour scheme live, and a theme's
[effects] backdrop = "blur"blurs what lies behind a dialog (the
default preset asks for it). Bridge 64 and 65. -
A
thumbnailplugin kind (ADR 0107), in its own WIT package
norte:thumbnail@0.1.0so no installed guest needs a rebuild. A guest
lists mimetypes like a previewer, gets the file's bytes (capped at 8 MiB)
and the longest edge allowed, and answers a PNG/JPEG/WebP raster that
the plugin-host verifies — magic, declared mimetype, dimensions, edge —
before it crosses.plugin.thumbnail(protocol 0.73.0) carries it; the
window's viewer asks for one when it has no picture of its own and paints
it labelled «via ‹plugin›».org.norte.image-thumbis the first guest:
a photo too big for the viewer's cap now gets a picture. -
Plugins parametrised.
file-iconspaints one-cell Nerd Font glyphs
(style = "nerd", bundled in the window as a 3.8 KB subset) and takes
dir-iconandunknown-icon; two new columns plugins,size-bar(a
█░bar per file;scale,width,relative-to) andage(a glyph
per bucket and a short figure;thresholds,glyphs,format);
git-statustakesglyphs(letters or symbols) andignored. -
The chrome of an orthodox manager, derived and configurable (ADR
0106). A function-key bar on the last row of the terminal and a strip at
the bottom of the window, read from the keymap of the screen that owns the
keyboard; the panel bar names its buttons with the access letter
underlined; every listing carries a footer with counts, what is marked and
the free space of its volume; a dialog's key line is painted as clickable
buttons; a status notice expires into the log after eight seconds and
leaves a!nbadge that opens it; the modified column prints local time
with the precision the distance asks for (smart); the palette reads the
human label first and keeps the last five commands on top; the focused
cursor takes the theme's accent and the other pane's stays grey. Six
[ui]keys (key_bar,panel_bar_style,pane_footer,date_format,
notice_seconds,dialog_buttons), each a row on the settings screen and
a section of the newappearancehelp page. -
A first-start wizard. With no
norte.tomlof your own, both frontends
ask three things once — which file manager you have in your fingers, which
theme (previewed live), and whether your terminal shows icons — and write
the answers as configuration. Esc keeps the defaults and never asks again;
ntc --setupasks again;NORTE_NO_WIZARD=1keeps it closed.
Changed
- F9 opens the menu in six presets, as in mc, FAR, Norton Commander and
Total Commander; the theme picker moves toAlt+9. Krusader keeps F9 as
the terminal, as its source attests. - The mtime column is 12 cells (was 10), to fit
09-10 14:02. - A plugin may name a meaning, not a piece of chrome (ADR 0108,
amending ADR 0037).roleon a span or a decoration is validated
againstRole::REQUESTABLE— what a piece of content MEANS — rather
than against everyRole. The window's chrome and its state
(selection,status-bar,mark…) are no longer requestable: a badge
in the cursor's colour would lie about where the cursor is. A
non-requestable name degrades toNone, exactly as an unknown one
already did. No WIT bump; the twelve bundled plugins are unaffected. - The window's bridge is version 67.
Fixed
- A
lua:key in the window says it is not available. Lua runs in the
terminal frontend only (ADR 0110), but alua:binding in your keymap
layer was offered by the window's reference sheet, which-key and palette,
and pressing it did nothing. It is now "not available here", like any
other command the window does not have;ntcruns it as before. - A double click opens a directory in the window. The renderer waited
for the engine's owndblclick, the only door into a directory with the
mouse; it now counts two presses on the same row itself, the way the
terminal does. And the host no longer refuses an activation that names a
pane other than the focused one: it focuses it first, so a double click
on the pane next door works. - A column a plugin contributes is named by its manifest. The
header
every manifest declares was parsed, carried over the wire and read by
nobody, so the listing showed the column's id (acme.git/status). Both
frontends now install the catalogue's label into the shared model, under
the user's[ui.columns] headerand above the id. - The terminal's extension manager answers the mouse. It was the one
overlay where a click did nothing: no row selected, and none of the
buttons the window has. The detail pane now opens with a row of buttons —
enable or disable, approve or revoke, settings, uninstall, and help when
the extension ships a page — each firing exactly the command its key
fires. Clicking a row selects it, clicking the selected row opens its
settings as Enter does, and the wheel moves the cursor. Choosing another
row with a plugin's settings open closes them, so the pane never shows one
extension and the settings of another. The keys were always there and
still work.
Changed
-
The menu bar has ten groups, by what the reader wants to do. File
(what reads a file: view, edit, open, properties, size, copy the path,
quit), Operate (what writes: copy, move, rename, batch and AI rename, new
folder, delete, permissions, pack, unpack, test, split, combine,
checksums), Mark, Go (parent, back, forward, history, hotlist, volumes,
connect, disconnect, refresh, command line, terminal), Panels, Tabs, Find,
View (what the listing shows and the side panes), Tools (extensions,
agents, settings, profiles, the palette) and Help. Every built command is
in exactly one menu — 32 of them were in none: permissions, packing,
checksums, the batch rename, the hotlist, the history, the agents… were
keyboard- and palette-only. In the terminal the bar tightens to one space
between titles when ten do not fit in the width, instead of dropping the
last one. -
An upper-case letter under a modifier is labelled
Shift.alt+C
printedAlt+Cin the menus, the palette, the help and the reference
sheet, and nothing said the case mattered — whilealt+cis another
command. It now printsAlt+Shift+Ceverywhere the chord is painted; the
stored chord is unchanged. Andmark.filesmoves fromalt+Ftoalt+f
in the orthodox, vim and cua presets, because the lower case was free: a
Shift that buys nothing is a Shift the reader should not have to press.
The imported presets keep their transcribed chords. -
The terminal's extension manager has the window's detail pane. Two
columns when the terminal is 64 cells or wider: the list on the left,
compact — name, version, ✓ or «not approved» — and the selected extension
on the right: version · publisher · category, its state as two facts, its
description, its capabilities as chips, how many commands and columns it
brings and whether it ships help, then its settings table when opened with
Enter — inside the pane now, with the cursor and the key's description —
and the commands it contributes. Narrower terminals keep the single list
with the settings in their own box. Same keys as before; the decisions
behind the two managers were already the host's and the terminal's shared
ones, only the screen differed. -
A daemon a frontend started stops with its last client. The window,
ntc --daemonand a one-shotnorte --daemon …start a daemon when none
answers, and it used to outlive them by five minutes: a process nobody
could see and nobody had asked for. They now start it with
--idle-timeout 2, so two seconds after the last client disconnects — with
no task running — it exits; a client that reconnects within that margin
finds the same daemon, and a second client keeps it alive.norte daemon runby hand keeps its five minutes. The argv is built once, in the SDK
(daemon_run_argv), instead of in four places. -
A detached window says so quietly, and explains itself on demand. A
terminal that started while another window held the session greeted the
reader with «another window owns the session; this one runs on its own» in
the message bar — jargon to anyone opening norte for what they thought was
the first time, and gone at the next key. Now nothing is announced: the
persistent indicator in the status bar is the whole signal, shortened to
session not saved(plain text: the badge now drives a mouse hit-test, and
this frontend keeps its badges ASCII for exactly that reason), and it
appears and disappears by itself as ownership
changes (a daemon handover no longer produces a message each way either).
Clicking the indicator opens the help on the panes page, which gains a
section on the session: who keeps it, the three reasons a window may not be
the one, and that no file is at risk. The window shows the same indicator in
its status bar — it showed nothing at all before, so a detached window closed
and lost every panel's place in silence (ADR 0077). -
A terminal modal can have a hierarchy now (ADR 0103), and the copy
dialog is the first to use it. A modal's body was ONE string painted as a flat
paragraph, so the editable field, the paths, the hint and the keys all came
out in the same colour and the same weight: the last thing you found was the
only thing you could touch. Each line now declares its ROLE — data, label or
hint, destination, field, warning, error — and the theme decides how it is
painted. A modal that declares nothing looks exactly as it did, so the 26 of
them migrate one at a time.
InTransferName: the destination stands out and the source dims, the label
sits ABOVE the field (it was below — you read the name and then found out
what it was), and the field is painted as a field, its background running to
the border. The source now shows the DIRECTORY instead of repeating the file
name, which appeared twice in a five-line body. Labels «From»/«To» replace
the arrow:→is legitimate inside a name and is not masked, so
docs → /home/BURNmanufactured a line that reads as two paths — that is
the corpus'sarrow_join_spooffixture, and what the host already did in
DialogView::destination. A test finally ties the declared height to the
body that gets composed: the module's own rustdoc had warned from the start
that the two halves drift apart and the modal gets clipped, and nothing
checked it.
Two more things reviews caught. A rename now names the file it renames:
a rename opens withto_dir = from.parent(), so showing only the directory
made «From» and «To» identical and the name being changed vanished from the
screen the moment you typed — confirming a mutation whose operand is not
visible, which is what ADR 0070 forbids. AndConfirmTransferloses its
arrow too: it marked its destination with→directly above a list of
somebody else's file names, and dropping⟨file⟩made that line cheaper to
forge — slash homoglyphs (U+2215, U+2044, U+FF0F) are legal on ext4, APFS
and NTFS, so→ ∕srv∕publicois a legal file name that renders a complete
destination line. What distinguishes it now is its ROLE, which a name cannot
write. Both spoofs are in the corpus. -
A modal's height is derived from its body, and
modal_height— a table
of 131 lines of hand-written formulas, one per variant — is gone. This
module's own rustdoc had warned from the start that the two halves drift
apart and the modal gets clipped; when a test was finally written for one
variant, it turned out the formulas did not even agree with each other:
some added 2 to the line count, some 3, some 4, andTrustHostKeydeclared
9 fixed rows for "five lines". Deriving it makes the drift impossible —
there are no longer two numbers that can disagree. The one modal that lets
ratatuiwrap its body still declares its height by hand, because counting
wrapped rows needs ratatui's own rule (Paragraph::line_countknows it, but
it is an unstable feature and is not worth turning on for one modal); a test
now checks that its message reaches the screen. -
tail_windowbudgets in CELLS, not chars. Fifty chars of CJK are a
hundred cells, so a Japanese name overflowed its box anyway and the overflow
ate the cursor at the end — you kept typing and the screen stopped changing.
It affected all six free-text fields; the first snapshot of the transfer
modal is what made it visible. -
⟨file⟩stops announcing itself on local paths. It is the default case
— this machine, this disk — so its label distinguished nothing at all, and
it was painted on every path of every listing, header and modal, spending
eight columns exactly where room is scarce. What informs is the scheme that
is NOT the usual one:sftp,s3,zipand friends still say so, and a
fileWITH an authority does too — that one is another machine.
Added
- File icons are a column left of the name, folders included (ADR 0105,
protocol 0.72.0, bridge 62, WITnorte:plugin0.10.0). A decorator's
manifest now says which slot it fills —icon, a fixed-width column left
of the name, orbadge, the git-status place right of it — and a row can
carry one of each from two plugins, where before the first plugin silenced
the second. The column opens for the whole listing the moment one row has
an icon, so names stay aligned, and the terminal's header moves with it.
decoratereceives each entry's kind along with its name, so
file-iconsgives folders the folder icon whatever they are called, links
the link icon, and files what their name says — spreadsheets and slides
included. The package bump means every installed plugin is rebuilt
(just plugins force) and re-approved in the manager; a guest built
against 0.9.0 is listed as such with both versions. - An extension is uninstalled from the manager, on both frontends (ADR
0104, protocol 0.71.0).plugin.uninstalldoes whatnorte plugin uninstalldid on disk — delete the directory, leave the state switched off
and unapproved — and then what the CLI could not: the daemon forgets the
plugin in its in-memory registry, so it stops being listed, and stops
decorating listings, at once instead of at the next restart. Human
connections only, like approving. The manager binds it todialog.remove
(din the bundled presets; the imported four inherit the dialog block) and
always asks first, naming the extension and saying the two things that go:
its files, and its approval — a plugin installed later under the same id
starts unapproved. Cancelling sends nothing. - The window's extension manager has a detail pane and buttons (bridge
61). Two panes: the installed extensions on the left — name, version, a
state pill that says both facts, publisher and category, description,
capabilities as chips — and the selected one on the right, with Approve
or Revoke, Enable or Disable, Help when it ships a page, and
Uninstall, followed by its settings sheet or a hint saying how to open
it. A header counts what is installed and what is on. The buttons carry no
logic: each sends the row and the change, and the host walks the same path
the key does — approving opens the consent dialog that enumerates the
capabilities, enabling an unapproved extension is refused with the same
message (the button is disabled and its tooltip says why), uninstalling
asks.Helpcloses the manager and opens the help at that extension's page,
asF1over the row does in the terminal. - The window writes settings (bridge 60). F11 in the window was a showcase:
it listed the shared registry with each effective value and told you it did
not write. Nowenter(or a double click) does what it does in the terminal:
a boolean, an enumeration, the theme or the keymap preset cycle to the next
value and are written at once; a text or a number opens the window's
one-field prompt, prefilled with the current value, and the shared editor
validates it — a font size outside[8, 32]is refused with the range and
writes nothing. The write goes to the layer the window already writes
(the active profile, else the user's), off the actor, and the configuration
is re-read and applied through the SAME path a profile switch uses: theme,
keymap, columns, favourites and layout change without restarting; what that
path cannot apply (language, fonts, reduced motion, and what is fixed when a
pane is created) keeps its "restart required" badge, and the saved message
says so.SettingsView.read_onlyis gone from the bridge: it was a phase-4
promise and no longer true. Out of scope, and said in the parity
classification: the terminal's search filter over the settings, and the
plugins section, which is informational in both frontends. - The viewer scrolls sideways (
viewer.left,viewer.right, bound to
left/rightin all seven presets and toh/linvim, both taking a
count). The viewer does not wrap, so a minified HTML file, a wide CSV or a
log had its right-hand half nowhere at all: painted clipped, unreachable.
The cut is made once, in the shared model, on the already-rendered line, and
it counts CELLS of terminal — by bytes the text jumps at the first accent,
by characters any line with CJK misaligns against its neighbours. A wide
character straddling the cut goes entirely and leaves its cell blank —
dropping it without a filler slides that row one column against its
neighbours, and the grid is the whole point. A zero-width mark that would
open a row is dropped, as the truncators beside it already do with a tail: a
split ZWJ cluster would otherwise paint a glyph that is not in the file. The
stop is the longest line, measured when decoding, leaving one column always
in view. The hex dump scrolls too, with its own 77-cell width: in a split
pane its ASCII gutter did not fit, so refusing the axis made it unreachable.
The status line and the window's header marks say the column in words, which
in the docked viewer is the only thing that says the view is shifted. - The viewer says there is more, and the wheel moves it. Both scrollbars
in the terminal, drawn over the frame's borders and never when everything
fits; the coupled preview gets only the vertical one, because its bottom
border carries the line that says WHAT is being read. The wheel reached
neither the full-screen viewer (it sits behind the overlay cutoff) nor the
coupled one (its slot is not a listing, so the hit test returned nothing).
Bridge 59 carriestotal_cols/first_coland theviewer_scrollaction. - A hook may write a sidecar (ADR 0101, protocol 0.70.0,
norte:hook@0.2.0). A hook plugin can now returnwrite-sidecar: a file
with one of the exact names its manifest declares in
fs-write = { sidecar = [...] }— the only formfs-writetakes; the old
reserved"scoped"is rejected — written by the core as a plugin actor
in the parent directory of the event, through the policy engine (a rule
actor = "plugin", action = "deny"stops it, and the reader is told once
with the newplugin.noticekindeffect-denied; anaskrule on a
plugin is a deny) and through the journal (created;replacetrashes
the previous file first, so its content has a way back; a directory with
the name is never touched). Rows a plugin writes never come back to any
hook as events. Approval showsfs-write:<name>badges.org.norte.rename-lognow keeps a
.norte-renames.lognext to what it renamed, carrying the previous log
forward. A 0.69 client ignores the new notice kind. - Operation hooks: a plugin can observe what the journal recorded, and
say so (ADR 0100, protocol 0.69.0). A new plugin kind,hook, with
its own WIT packagenorte:hook@0.1.0: the manifest names the journal
events it listens to —after-created,after-removed,after-trashed,
after-renamed,after-mode-changed, a closed vocabulary — and the guest
receives the committed entries in batches and may return one kind of
effect,notify(text). There is nobefore-*and there is no veto: what
decides whether a mutation happens is the policy engine, and a hook sees
the entry after it is durable. The source is the journal's commit path,
so a hook fires for a human's rename, an agent's, a batch and an undo
alike, from the daemon and from an embeddedntc. The dispatcher runs off
the critical path with a bounded queue, tells the guest how many events
it lost, and three consecutive failures switch that plugin's hooks off and
say so (disabling the plugin re-arms them). A hook may not declarenet,
is shown nothing under norte's own state directory, and its events appear
at approval ashook:<event>badges. The sentence reaches both frontends
as the newplugin.noticenotification (humans only,kind∈ {notify,
hooks-disabled}), masked, capped, rate-limited and prefixed with the
plugin id. Declaring a hook no longer rejects the manifest; an unknown
event does. First hook:org.norte.rename-log(plugins/rename-log,
just plugin-rename-log), which says how many files a rename touched. A
0.68 client ignores the notification: the hook ran, its sentence reached
nobody. - Every binary says which build it is.
ntc --version,norte --version
andntc-gui --versionprint the workspace version followed by the tree's
git describe(0.3.0-alpha.3 (v0.3.0-alpha.3-10-g674b0eb9-dirty)); the
TUI shows the same line in the frame of the help screen (F1) and the window
carries it in its title. The revision is fixed at compile time by
norte-frontend's build script, falls back toNORTE_REVISIONfor
packagers and tounknownwithout git. On a development machinejust link
pointsntcattarget/debug, and «0.3.0-alpha.3» was the same string ten
commits after the tag: now the binary tells you.
Fixed
- Switching a decorator off in the manager left its icons and badges on
the rows until the nextcd, on both frontends, and the reader concluded
that switching off does not switch off. Any governance change — approve,
revoke, enable, disable, uninstall — and any plugin setting written now
make every open listing forget what the plugins said and ask again; a
batch already in flight lands with an older generation, is dropped, and
asked again. The window's rows go bare at once and fill back in; the
terminal's event loop drains a flag the manager raises. - The terminal's initial listings carried no icons or badges until the
firstcd. Both panes are built at startup outside the path acd
takes, and only that path asked the decorators; a freshly openedntc
showed bare rows and the reader concluded the plugin did not work. The
event loop now requests decorations for the initial panes through the same
function everycduses. F10andqclose the window.app.quitwas classified as "not
applicable to a window — the window manager closes it", which left the quit
key of all seven presets, and the menu's own "Quit" entry, doing nothing.
It now asks to close through the same path as the close button, with the
same[ui] confirm_quitquestion. With it, no command the orthodox preset
binds is left dimmed in the window's key sheet.- A menu's dropdown hung one title too far to the right. Its left edge
was a guess in cells —12chper title — while the titles are painted with
pixel padding and do not measure the same, so the error added up towards
the right until "Help" opened under the next title. The renderer now
measures the painted title and hangs the list from it; the cell count stays
only as a fallback. - The dialog field lost the keyboard on every key. Each key sends
dialog_input, the host answers with a patch, the dialog box is rebuilt and
the reused input is MOVED into the new box — and moving a node takes it out
of the document for an instant, which drops its focus. Deleting a digit in
"Font size" left the field unfocused and the next key went to the host as a
chord. The renderer now gives the focus back once the new box is mounted. - The window's setting prompt was painted under the settings.
enteron
a text or number row opened the one-field prompt, but#dialogscame
before#settingsin the document and this window has noz-index
anywhere: the veil darkened and no field appeared, while the invisible
dialog kept the keyboard. Dialogs now come after every selector and panel,
before only the help and the fatal notice, and a test pins that order
againstindex.html. - "restart required" was on sixteen of eighteen settings rows. The window
re-reads the whole configuration after a write, and almost everything is
read at the moment it is used — the bars on every snapshot, the editor and
the diff tool when launched, the search mode when searching, confirm-on-quit
when quitting — so it changes at once. The badge now marks only what the
host resolves once at startup (language, fonts, reduced motion) and what a
pane fixes when it is created (hidden files, the..row). F10andqquit from inside a side panel. With the keyboard in the
tree, the places sidebar, the processes panel or the log,app.quitwas
not in the panel's allowlist, so the panel swallowed it and onlyCtrl+C
got out. The consequence was worse than a dead key: the reader pressed
F10, nothing happened, closed the terminal window believing the program
had exited, and thentcstayed alive holding the session lock — every
ntcopened afterwards started detached and saved nothing, for as long as
the ghost lived (a week, in the case that surfaced this). Quitting now goes
through the same chrome funnel as the menu key, honouring
[ui] confirm_quitexactly as it does from a listing.- The window remembers its panels. Open the places sidebar, pick a layout
template, split a pane, close the window, open it again: everything was back
to the configured layout. The window never wrote its layout tree into the
session and never read one from it — a comment justified that by citing
ADR 0058 D5, which is about screen size not rewriting a stored tree, while
D8 of the same ADR asks for exactly the opposite: close one frontend, open
the other, carry on where you were. And the window only wrote the session
at all on shutdown, and only when the close reached the host in time: the
shared write policy was instantiated and never ticked. Now the window keeps
its tree under the same session key as the terminal (default, or the
active profile's name), applies the saved one at startup above--layout
and the configuration — the terminal's order —, writes the session every
second like the terminal when something changed, and writes it AT ONCE after
any change of the tree: a panel toggled, a template chosen, a split, a
resize. A detached window still writes nothing. The age seal of each slot is
now remembered between writes, as in the terminal; stamping every capture
with "now" would have made every tick a write. - The window's viewer header was invisible on a light status bar. The
theme projection carriedstatus-bgand never its foreground, so anything
painted on top had to GUESS the text colour — the viewer header guessed
title-fg, which on a theme whose status bar is light is light on light:
the path, the encoding, the EOL, the lossy mark and the column all
disappeared.Role::StatusBaris a PAIR, and the terminal has always used
it as one.status-fgnow crosses with it, and a test pins the whole key
list — it is an agreement with a stylesheet that shares no types, so
dropping one has to turn red rather than be found by looking at the screen.
Found by painting the window, not by a test. - And the scrollbar track was a solid band across the window, for the same
reason: it usedstatus-bg. A track is chrome — what informs is the thumb —
so it takes the dimmed border, the same pair the terminal uses. - An image in the window's viewer had lost its height, and grew bars that
described a hex dump nobody could see. One branch of the image painter
rebuilt the whole frame where the other only replaced the body, so the new
canvas was thrown away; it replaces the body in place now, like its twin.
No bars over an image: the counts describe the hex view underneath, and the
picture is scaled to fit — there is nothing to scroll. - A double click on a file does something again, in the terminal. On a
filenav.enterdoes not navigate: it resolves the desktop's program and
leaves it armed for whoever owns the terminal to launch. The mouse arm ran
the command and never launched what it armed, so double-clicking a.jpg
did nothing at all and did not say why — whileEnteron the same row
worked.on_mouse's own rustdoc had promised "launch the opener a double
click resolved" since it was written. The fix is that all three mouse arms
now leave through the same function, so none of them can forget the third
thing again. The window was already right: its double click goes through
the host, which opens a local file externally. - The tree follows the panel that navigates (ADR 0102). Opening the tree
and walking around left the panel pointing at the folder you were in when
you opened it. Not a loose wire: both frontends anchored at open and never
again, on purpose, because anchoring EMPTIES the tree and re-anchoring on
everycdwould have closed every open branch. What was missing was the
ability to reveal without emptying —Tree::followexpands the ancestors
and moves the cursor, and a sibling branch you opened stays open. It is
wired into the one funnel each frontend already had for acd, plus the
focus change, and not into each gesture: that list went stale once already,
which is why the funnels exist.
The rule is about the ROOT: what has been read stays valid as long as the
new root is an ancestor of the old one. So going UP a level (Backspace) and
alternating between two sibling panels withTabmove the root up and keep
every branch, where before each of them emptied the whole tree on every
press — which made the tree useless with the two gestures it most needs to
survive. Only another provider anchors and empties. Tabreaches the third listing, and stops only at listings (ADR 0102).
In the terminal it wasfocus ^= 1, a count of two: afteralt+vsplit a
panel the key silently did nothing from the third one, becausePaneSlots
clamps out of range instead of panicking. In the window the opposite —
pane.switchshared an arm withlayout.focus-next, so with the places
bar, the tree and the viewer open it took five keystrokes to get back to the
listing beside you. Now they are two rings:pane.switchis "the other
panel" and cycles the listings,layout.focus-*walks the whole screen.
Tabstill takes you out of a side panel.