These notes preserve runtime details that are too specific for the README but matter when changing launch, config, editor, shell, or popup behavior.
Root appearance.mode is the managed dark/light authority. Ratconfig uses it
for its own palette. When rio/config.toml is writable, Nova owns only its
top-level force-theme field and projects the root mode there. Rio starts
without a theme override and its native config watcher applies later Ratconfig
saves. A custom complete Rio config must supply its own adaptive pair to
participate; every field other than force-theme remains native and
user-owned.
Yazelix passes only the root dark/light mode to each new managed Zellij
session. Zellij resolves the matching theme_dark or theme_light member. A
managed session ignores ambient terminal appearance reports so the explicit
root mode remains authoritative. A root appearance save from inside that
session calls set-dark-theme or set-light-theme against that exact session.
Zellij's host-theme event switches the top bar between its child-owned dark and
light palettes. A bar loaded by a later tab receives the session's current mode
even when no new host-theme transition occurs. The coordinated save rolls back
if a live component projection fails.
When Rio's config is read-only or cannot be projected, Nova starts Rio with the explicit mode and captures that mode for the session. Ratconfig, Zellij, the bar, and new Yazi opens stay on that side even if the root setting changes. The saved mode applies everywhere together in the next session.
Each managed Yazi launch reads the active session mode before materializing its
config. Yazelix selects the matching native flavor.dark or flavor.light,
using Yazi Bistro's Bluloco Light default when the light side is absent. An
absent dark side uses Yazi's native preset; Ratconfig exposes that state as the
first dark choice, default, without writing it as a flavor name. When a flavor
is selected, both runtime flavor keys receive it so ambient detection cannot
choose the opposite side. The source theme.toml is never rewritten, and an
existing Yazi process keeps its current theme until it is reopened.
yzx enter and yzx launch pass the managed config and
--new-session-with-layout to packaged Zellij before forwarding caller
arguments. Zellij owns session creation, attachment, switching, process
lifetime, and native failure messages. Plain launch creates an independent
session. --session NAME requests a fresh named session, while attach NAME
requires a live target. Attach and switching preserve the target state without
reapplying the managed layout.
Rio closes with its child, so under yzx launch the welcome helper runs Zellij
as a child instead of replacing itself. When Zellij exits unsuccessfully, for
example after rejecting a session name or finding an existing session, its
message stays on screen with the exit status until Enter is pressed. Normal
quit, detach, and cancelling the only startup picker exit successfully and close
the window immediately. yzx enter keeps the direct handoff, because the host
terminal already keeps Zellij's output.
The managed Zellij config sets default_layout "layout" and points
layout_dir at the directory containing the active layout.kdl and
layout.swap.kdl. Runtime appearance, shell, or widget projection rewrites
that directory to the materialized state path. Zellij lists the configured
default first, so native type-to-create selects the Yazelix layout before it
creates the named session.
Use full names for supported attach behavior. Native unique-prefix matching, rename, and structural resurrection remain Zellij surfaces outside the Nova v1 continuity contract. Yazelix adds no registry or second session owner.
zellij/config.kdl is a guarded sidecar for scalar preferences such as paired
dark/light themes, pane frames, mouse mode, scrollback size, copy behavior,
styled underlines, startup tips, and ui.pane_frames.rounded_corners.
An explicit top-level scroll_mode_sync true restores Zellij's automatic
Scroll mode entry in new sessions. Explicit pane_frame_style and
stacked_pane_list values also replace the corresponding packaged defaults;
omitting them inherits Nova's values.
Ratconfig uses the same 41 identities from the pinned Zellij assets for both
theme fields, with a flake check keeping the inventory aligned. The inherited
pair is theme_dark "ansi" and theme_light "gruvbox-light"; resetting a
field removes only its sparse override, and removing the final assignment
removes the sidecar. Custom names found on either member
join the shared pool shown by both pickers.
The former static theme field is not part of the managed model. An existing
assignment remains untouched in the user sidecar and is reported as ignored,
while runtime materialization omits it so the complete pair has one clear
authority.
When yzx config runs inside a managed session (ZELLIJ_SESSION_NAME or
YAZELIX_ZELLIJ_SESSION_NAME, plus YAZELIX_STATE_DIR), saving a Zellij tab
field also patches $YAZELIX_STATE_DIR/zellij/config.kdl so the running Zellij
watcher can pick up scalars without rewriting integration patches. Editing the
active theme-pair member applies live; editing the other member takes effect on
the next appearance change or managed session launch. Ratconfig labels pane
frames, rounded corners, copy-on-select, and clipboard target as now when
that active runtime file is addressable. Mouse mode, scrollback size, styled
underlines, and startup tips say next session; outside an active managed
session, every Zellij field is next-session. Quoted custom theme names without
KDL escapes remain accepted; richer string syntax stays preserved but
native-file-only.
Ratconfig scopes sidecar diagnostics instead of marking every Zellij field invalid. Safe untyped top-level leaves stay unchanged across unrelated saves and resets without a synthetic field or informational diagnostic for each leaf. The exact native-file action opens the sidecar for those settings. Invalid known fields affect only their own row and remain repairable. Advanced diagnostics report ignored legacy settings, malformed or structured native nodes, structural KDL comments or continuations, extra metadata on managed blocks, and integration-owned nodes. Unsafe source state blocks writes, and each guarded diagnostic names its Yazelix owner.
The runtime rejects uncommented top-level ownership nodes in that sidecar:
keybinds
default_shell
default_layout
layout_dir
layout
plugins
load_plugins
support_kitty_keyboard_protocol
env
session_name
attach_to_session
Use zellij/plugins.kdl for extra plugin declarations. It accepts only
plugins and load_plugins blocks:
plugins {
my_plugin location="file:/home/me/.config/zellij/plugins/my_plugin.wasm"
}
load_plugins {
my_plugin
}Plugin ids owned by Yazelix (nova_hints, yzpp,
yazelix_pane_orchestrator, radar, and radar_controller) cannot be
redeclared. Plugin keybindings are not
managed by this sidecar.
sidebar.command = "radar" selects the packaged Radar plugin. Any other valid
value creates a terminal pane with that executable and sidebar.args. The
config helper validates the pair and renders one KDL pane body; the launcher
substitutes it into the base and swap layouts. Both providers therefore use the
same Zellij pane named sidebar, including its open and collapsed sizes.
The packaged Radar alias is a lightweight per-tab view. One suppressed
radar_controller instance owns session state, event subscriptions, rendering,
and command keybindings. Views send bounded dimensions and mouse input over
plugin messages; the controller replies directly to each view's plugin id.
Both roles request the same permission union because Zellij stores one grant per
Wasm path. A custom sidebar omits the controller as well as Radar's bindings and
grant.
Nova captures the provider when it creates a session. It passes custom
arguments as argv without a shell. The pane orchestrator remains
provider-neutral and addresses the pane by name. With a custom provider, Nova
omits Radar command bindings, controller load and permission seeding, sets
YZX_RADAR_ENABLED=false for child launchers, and skips Radar's Codex doctor
path.
Opening or revealing a managed popup shows Zellij's tab-wide floating layer. Toggling the focused popup off or explicitly closing it hides that layer after closing or suppressing the managed pane. This returns directly to the tiled workspace instead of exposing a session manager, terminal, or other floating pane underneath. Suppressed keep-alive popups and unrelated floating panes continue running; their explicit keybindings can show them again.
The popup plugin observes the tiled pane named sidebar. While that pane is
wider than its collapsed divider, managed popups use a shared 33-cell left
margin so the framed 32-column sidebar rail remains visible. A pane update
reflows any visible managed popup without restarting it. When the rail is
collapsed, the configured side margin applies symmetrically again.
Native usable viewport changes also reflow visible managed popups. Hiding the
bottom hints extends the popup by one row; restoring hints keeps that row clear.
Configured margins, the running process and keyboard focus survive the resize.
For a sidebar toggle, the pane orchestrator asks Nova Zellij to select the exact named tiled swap layout underneath the visible floating layer. Zellij applies it in one render, so the popup is never hidden, restarted, or refocused.
Alt [ and Alt ] ask the pane orchestrator to apply the next content
arrangement directly. The command palette's layout entry asks for the same
next variant, then applies it through Zellij's CLI. Nova has
one stacked startup layout and four content/sidebar families: stacked or split
content, each with the sidebar open or collapsed. Every family has matching
visible-hint and hidden-hint swap variants. The split places one existing work
pane beside a stack of the others. It requires at least two visible tiled work
panes, starts no new shell, and leaves the current tab folder unchanged.
The bottom hint row has one session-wide visibility owner in the pane orchestrator.
Alt Shift B and the command palette's Toggle bottom hints entry send the
same toggle_bottom_hints command. Native pane suppression reclaims the row;
paired named layouts restore its bottom position. Sidebar and content-layout
changes preserve the choice. Hint toggles preserve work-pane order, including
manual rearrangements and background tabs. The native tiled matcher retains
relative pane order when explicit UI slots change; new panes follow existing panes.
New tabs inherit the hint choice. Fresh sessions start visible unless
bottom_hints.start_hidden = true; changing that setting affects future sessions.
Attaching preserves the current choice. One connected client handles broadcast toggles; each client restores
its active tab’s layout and focus. Closing a tab removes its hint pane while
retaining native preservation of unrelated background panes.
Popup reconciliation uses native pane geometry to retain work columns and settle
the hint layout without a repeated redraw loop.
keybindings.bottom_hints accepts a replacement chord or false.
BOTTOM-HINTS-START-001 is the Nova/provider-to-orchestrator startup contract.
Nova names only initial and new-tab hint panes bottom_hints_start_hidden when
the preference is enabled. The oldest hint pane seeds visibility once in the
orchestrator's native background controller pane. Its title records
yazelix_pane_orchestrator:hints:v1:hidden or :visible; it survives tab closure
and is shared by attached clients. Marked hint panes become bottom_hints after
reconciliation. Later tabs inherit the stored choice even when the original tab
closes immediately. Visible swap layouts keep the ordinary name. Native pane
state remains the sole runtime visibility owner.
Retiring hints lose their hint role before being shown and closed; one native
client elected across all tabs handles retirement and toggles.
The nova_hints alias passes each managed shortcut chord to nova-zjhints as
pipe_hint_<id> metadata because native mode events omit plugin-message
identities. The managed keybinding patch owns both binding and hint remapping
or omission. The plugin shows only matching active pure-pipe keys, using native
key parsing and existing whole-hint fitting. Compact modifier headers retain
separate backgrounds; core workspace and Nova actions receive fitting priority.
In Normal mode, C-A hjkl move combines the four orchestrator pane moves:
j/k within a column and h/l across split columns. Existing pipe annotations
and explicit-label merging provide the shared hint; its fixed key display is
scoped to the merged Normal hint, keeping the native Move-mode entry and other
input modes distinct. Tab reordering uses Ctrl Alt Shift H/L, which relies on
the Kitty keyboard protocol to keep Shift distinct from the pane moves.
Radar owns agent activity. The top bar does not track execution state; its tabs
use Zellij's native names, selection, bells, and layout indicators. The
orchestrator's v2 session response retains an empty extensions.ai_pane_activity
list for existing consumers, but no activity tracker or broadcast feeds it.
Codex activity status and prompt labels reach Radar through enabled, trusted
Radar hooks. Nova does not query Codex for activity or provide an equivalent
hookless fallback. Without those hooks, ordinary pane and command information
remains available, but Codex activity reporting is unavailable.
Radar's hook and sidebar broadcasts use the bundled yzx-zellij through the
managed session PATH's private zellij alias. This prevents another installed
Zellij version from selecting a different session namespace. The alias reports
the upstream Zellij version for Radar's compatibility check; yzx-zellij retains
the distribution identity.
Managed Codex launches with Radar selected use --no-daemon when the installed
Codex advertises it. Codex's shared server snapshots its own environment for
hooks, which can omit the invoking pane's Zellij identifiers or refer to another pane.
Running in the pane keeps reporting associated with that pane. Existing hook
trust and enablement still apply; older Codex versions keep their launch arguments.
When starting Codex directly in a shell, use codex --no-daemon if that version
otherwise uses a shared server.
Agent popup identity uses the stable /bin/yzx-agent command marker. A runtime
update can change the launcher's store path without losing the running popup,
including when the agent supplies its own terminal title.
The packaged agent launcher gives the pane its initial agent popup terminal title,
then replaces itself with [agent].command. The default auto chooses a
provider once per state directory. On first launch it checks PATH in this
order:
codex resume
grok
opencode
pi
claude --resume
It stores the selected provider at:
${YAZELIX_STATE_DIR}/agent/provider
Later launches use that stored provider. If the stored provider is unknown or
missing from PATH, the popup prints a diagnostic and tells the user to remove
the provider file so Yazelix can choose again.
With Radar selected, the agent launcher runs zj-radar setup codex --check
before each managed Codex launch. It consumes Radar's pinned report rather than
parsing Codex files: warning lines make partial or disabled hooks unhealthy even
when Radar exits zero. A healthy report records enabled in
agent/radar-codex-setup-offered. An unhealthy report with no known disposition
produces the Enable Codex activity in Radar? [Y/n] prompt only when stdin and
stderr are terminals. Declining records declined; accepting records enabled
only if setup succeeds and a second read-only check proves health. The old 1
marker encoded both answers and is treated as unknown. Closing the prompt or
entering an unrecognized answer leaves that state unknown.
If a previously enabled integration becomes missing, partial, or disabled,
Nova sends one transient zjstatus::notify bar notice per interactive launch
with zj-radar setup codex as the Codex-only repair path. It never repairs
automatically or reopens the consent prompt. A declined state remains quiet
until an explicit successful zj-radar setup codex is recognized by a later
healthy check.
Non-interactive launches neither prompt nor toast. Child-check, setup, and toast
failures never block Codex. yzx doctor replays Radar's read-only Codex
diagnosis and keeps missing integration warning-only. It omits the trust
reminder until hooks exist and reports startup failures once through the
shared diagnostic. Its default
report groups useful health checks and colors statuses on a TTY.
yzx doctor --verbose prints Radar's raw report and individual Classic residue
entries. yzx status remains
the owner of paths and settings. Trust remains owned by Codex's /hooks UI.
Both diagnostic commands validate configuration without writing files, initializing
Rio or granting plugin permissions, including their verbose/JSON forms.
Missing runtime files remain absent until an existing launch/run command prepares
them. Text status marks absent generated paths as not initialized; doctor gives
the launch action. Invalid settings or sidecars fail without changing user state.
yzx radar-setup and its Set up Radar for Codex, Claude Code, OpenCode
entry in Alt Shift M invoke the packaged
zj-radar setup codex claude opencode with inherited terminal input/output and
without --yes. Radar owns sequential detection, existing-state handling,
consent and installation. Its prompts default to no ([y/N]); noninteractive
setup skips writes. Codex and OpenCode detection also considers existing config,
including CODEX_HOME and XDG_CONFIG_HOME. Setup does not consult Nova's offer
marker, so a remembered decline never prevents explicit setup. Child output and
exit status are preserved, and unsupported Nova arguments fail before setup.
Start a fresh agent session afterward and review Codex trust in /hooks.
The automatic Codex offer never runs setup for other agents. Claude Code and
OpenCode setup stays explicit; Grok and Pi have no Radar adapter.
Any other agent.command value is executed directly by the same launcher for
new sessions, so custom commands receive the same initial title. Put argv-style
arguments in agent.args, not in agent.command.
Every new managed tab starts with the configured sidebar provider and one
focused tiled yazi_picker. A successful ordinary open or startup-search
Ctrl O owns the tab's initial retarget, creates the managed editor, and only
then closes that exact picker by id. Alt Enter is unavailable in this startup
picker; there is no separate workspace-only step for an editor handoff to
overwrite. There is no hidden starter shell or prestarted editor whose cwd can
become stale.
Exiting before a successful handoff closes the picker's stable tab. A surviving editor tab remains focused; cancelling the only picker ends the session.
Alt Shift Y asks the pane orchestrator to toggle the packaged yazi popup
with the active tab's canonical workspace root as its explicit request cwd.
The popup uses toggle_close_behavior "hide" and
preserve_on_cwd_change true, so ordinary toggles only hide or show the same
live Yazi process. Yazi remains the sole owner of its navigation state even if
its process cwd or the canonical tab root changes. yzx reveal sends one
configured replace request to the popup owner with the absolute target as a
launch argument. Yazi reveals that target during normal startup, so reveal does
not wait for or address a partially started process. This deliberately resets
the popup's navigation state while leaving the canonical workspace unchanged.
The managed PATH resolves yzx to the exact package that launched the
session before consulting the inherited host path, so mixed installed channels
cannot change the behavior of Helix's reveal command.
Managed Yazi owns Alt r locally and passes no hovered path. The popup role
sends one explicit hide to the popup owner, preserving the live Yazi process
and navigation state and returning to the underlying pane. It does not open the
hovered item or change editor cwd or the canonical workspace; Enter remains
the explicit file-open action. Helix owns its
reveal binding locally as well. Zellij does not intercept or reroute the chord,
so hiding the popup cannot replay it as a Helix reveal.
The popup runs the yzx-yazi launcher and layered config with the private
workspace-popup role. Popup navigation and ordinary opens retain their local
and canonical-workspace semantics.
When shell.program = "nu", Yazelix does not read normal Nushell config. It
generates runtime Nu files that source packaged Yazelix config first and then
optional user files:
~/.config/yazelix/nu/env.nu
~/.config/yazelix/nu/config.nu
If host mise is available on the inherited PATH, managed Nu inserts
mise activate nu output after packaged config.nu and before user
nu/config.nu. Missing or failing mise is skipped.
Managed Nu enables the pinned native clipboard commands. Pipe any Nu value into
clip copy (for example, pwd | clip copy) and read it with clip paste.
The managed clc and clp aliases provide shorter copy and paste forms.
Nushell owns platform clipboard access and errors; Nova does not translate
remote or headless sessions to a client clipboard.
shell.atuin = true sources Nova's build-generated, locked Atuin init files
after the selected shell's user startup layer. yzx-nu appends the Nushell
form after user nu/config.nu; yzx-shell composes the others through Bash's
explicit rc file, a temporary Zsh ZDOTDIR layer, or Fish's post-config
command. An existing shell-specific Atuin search function skips the managed
source, leaving user initialization as the sole hook owner. A managed init
failure reaches stderr without preventing shell startup. Bindings, imports,
and the opt-out are described in
Configuration.
Managed Nu always sets STARSHIP_CONFIG to a runtime-effective file. That file
starts with Nova's sparse [character].format = ":: " marker and recursively
merges optional native overrides from ~/.config/yazelix/starship.toml.
Top-level format stays unset by default, so Starship retains its native $all
layout, including directory, Git, environment, and tool modules. Normal
~/.config/starship.toml does not affect the managed Nu prompt.
yzx-hx builds the effective Helix config on each launch from the packaged
default plus optional managed user files under:
~/.config/yazelix/helix/
helix/config.toml is deep-merged over the packaged TOML default. The generated
effective file is ${YAZELIX_STATE_DIR}/helix/config.toml. Yazelix reserves
Alt r for reveal, so the generated config enforces:
[keys.normal]
A-r = ':sh yzx reveal "%{buffer_name}"'forest.enabled = false omits Nova's generated :forest-open binding and skips
Forest configuration and startup. Managed Helix and user Steel initialization
still load. When Forest is enabled, keybindings.sidebar_focus adds, remaps, or
disables its generated binding and matching focus toggle. A user-owned binding
at the previous key remains when the managed binding is remapped or disabled.
Managed Helix prepends the exact packaged Forest, notify, and glyph modules to
STEEL_SEARCH_PATHS, opens Forest on the forest.side choice (right by
default), and loads user Steel initialization last. When managed Helix starts on
one directory, Forest stays visible but unfocused so Helix's native directory
picker owns input.
helix/languages.toml is loaded by the managed Helix config dir when present.
When helix/init.scm exists, a private HELIX_STEEL_CONFIG overlay loads it
after Nova's managed startup. The overlay uses the packaged helix.scm module
unless the user also provides helix/helix.scm; that explicit module replaces
the packaged one. The packaged module provides :yzx-new-shell, which opens a
new Yazelix terminal pane at the current file directory or workspace.
Packaged Steel also starts the fork's bounded authenticated loopback transport. Yazelix owns token generation, atomic private registry publication and lookup under validated session path components, same-session/tab instance selection that prefers the caller's own Helix pane, and the TCP client. The fork passes authenticated actions to Steel on the editor thread without owning Zellij, Yazi, or registry policy. Closed bridges, stale-token authentication failures, and invalid or mismatched response frames leave later registry candidates eligible.
The pane orchestrator owns one canonical workspace root per tab. The first managed open resolves the containing Git worktree, or the opened directory or file parent outside Git, and publishes it as explicit state. Later ordinary Yazi opens preserve that root while passing the requested absolute target to Helix. This includes ignored files, nested repositories, and non-Git descendants. A managed open also resets a drifted Helix cwd to the canonical root. After success, only the originating managed Yazi follows the primary target's directory; the canonical workspace, shell panes, and hidden agent stay unchanged.
The search and tab-folder keys are listed under
Editor and file opens. Search Enter
emits native cd and leaves the canonical root unchanged. Startup search
Ctrl O sends the selected directory through the existing retarget/editor
transaction and closes the picker only after success. Popup Alt Enter sets
the exact canonical root without moving Yazi or opening an editor.
The orchestrator updates the tab label; existing shells, Yazi, and
editor processes keep their own cwd. A later ordinary startup open provides the
same first-editor handoff. Explicit popup workspace
choices inside Git keep the chosen subdirectory; ordinary implicit file-open
bootstrap may still use the containing worktree. A failed editor handoff
restores the prior root and its bootstrap or explicit provenance. Git and agent
popup requests carry the canonical root explicitly. A hidden agent is reused
across pane focus and local navigation changes, and is replaced only after the
canonical root changes.
Alt m and pane mode's n, d and r ask the orchestrator for a new shell
pane at the canonical root, with native stacked, split or directional placement
and focus. d and r reuse Zellij's own down and right actions with the
packaged shell, which Nova passes to the orchestrator as default_shell. Pane
mode's s stays native and stacks in the focused pane's directory, because
Zellij's plugin API drops a stacked pane's command. Until a workspace is chosen,
the root is the tab's bootstrap directory: the launch directory or the new tab's
picker directory. Zellij reads plugin paths under /host, /data, /cache and
/tmp as plugin mounts, so Alt m and n open a root inside those directories
at the session's launch directory instead.
Configuration lists the managed Yazi
files and their merge semantics. Only plugin.prepend_fetchers uses
replace-plus-managed-Git semantics: user fetchers are kept while the two managed
Git fetchers are restored exactly once. The presence of yazi/starship.toml
alone activates materialization; the materializer requires readable TOML and
links it into the effective config as yazelix_starship.toml, the same path the
workspace popup uses. Plugin and flavor directories also activate
materialization independently of init.lua. The materializer copies
theme.toml only when runtime appearance projection requires it.
Automation can materialize that config without starting Yazi, Zellij, Rio, an editor, or the full Yazelix runtime:
effective_config="$(
yzx yazi-config materialize \
--user-config-dir "$PWD/yazi" \
--state-dir "$PWD/state"
)"
YAZI_CONFIG_HOME="$effective_config" yazi --debug--user-config-dir names the exact directory that contains yazi.toml,
init.lua, plugins/, and the other native Yazi entries. --state-dir names
the isolated state root. Callers must pass both flags; the command does not use
YAZELIX_CONFIG_HOME or YAZELIX_STATE_DIR as defaults. The selected Yazelix
package supplies the packaged config, including for no-yazi variants and
Home Manager installations. The active session appearance and the packaged
Yazi Bistro light default are supplied internally; the public command takes no
appearance flags. The Home Manager module adds no materializer option.
Successful calls print one absolute effective config directory and a newline
to stdout, with no stderr output. In dark mode, a user directory with no
managed entries returns the package's read-only config directory and leaves the
state path absent. Light mode materializes runtime theme.toml even without
user entries so it can select the packaged light default. Other inputs replace
only <state-dir>/yazi through a staging directory. Bad command usage exits
64. Materialization failures exit 1; validation failures leave an existing
effective directory intact.
The materializer validates paths, TOML syntax and merge structure, required
integration fields, plugin and flavor shapes and collisions, appearance
projection, source/state overlap, and output construction. Yazi remains
responsible for semantic config checks, Lua and plugin behavior, and external
commands. Run yazi --debug with the printed YAZI_CONFIG_HOME when automation
needs those checks.