with one click
ophanim
// Primer for agents working inside the ophanim repo — what ophanim is, how its code is laid out, where config lives, and how to add new configurable options without breaking the schema-driven UI.
// Primer for agents working inside the ophanim repo — what ophanim is, how its code is laid out, where config lives, and how to add new configurable options without breaking the schema-driven UI.
| name | ophanim |
| description | Primer for agents working inside the ophanim repo — what ophanim is, how its code is laid out, where config lives, and how to add new configurable options without breaking the schema-driven UI. |
A terminal that can summon a browser. Tile terminals and web pages in the
same window; flip a pane to a browser with browse <url>, flip it back
with Alt+Esc. macOS-first. tmux-backed so shells survive quit +
relaunch. Session restore brings back the full layout, shells, URLs and
back/forward history.
main.js — Electron main. World → Workspace → Split-tree → Pane
model, layout math, IPC, keybinding dispatch, config loading, session
restore glue.renderer.js — The termView. One xterm.js Terminal instance per
terminal pane, custom OSC handler for the browse / config shell
helpers, custom wheel handler for alt-buffer apps.browser-chrome.html — URL bar / nav chrome that stacks on top of
each browser pane.config-ui.html — In-app settings pane. Schema-driven — walks the
DEFAULT_* objects and renders form rows with live preview + per-field
reset.tmux-backend.js — tmux as PTY supervisor. Generates
ophanim-tmux.conf from config, manages session lifecycle.session-store.js — JSON snapshot of window bounds, split tree,
pane kinds, browser history, focus, zoom. Round-trips on quit.Shell spawn path: ophanim → spawnPty → tmux attach client → tmux
session → user's shell. Shells live in the tmux daemon (label
-L ophanim) so they outlast the Electron process. An external terminal
can tmux -L ophanim attach -t <session> and see the same shell.
Browser panes: each is a WebContentsView (page) plus a second
WebContentsView (chrome bar) stacked on top of the termView.
pane-host divs in the termView have a 1px CSS border that peeks through
the 1px inset around the WebContentsViews — that's the focus ring.
At runtime the user has two files in ~/.config/ophanim/:
defaults.json — auto-rewritten on every launch from the source
schema. Contains every setting with its default value and an inline
_reference block describing what each one does. Read this file
first when the user asks "how do I change X?" or "what keys does
ophanim use?" — it's the source of truth for what's configurable
right now.config.json — user overrides. Only fields that differ from default
need to be present; everything else falls back. Edits hot-reload (no
relaunch needed).The in-app config command opens a schema-driven UI for the same file.
In source, all defaults are defined in main.js:
DEFAULT_BINDINGS — keyboard shortcuts (action → chord strings).DEFAULT_TERMINAL — font, theme, shell, tmux path, scroll mode.DEFAULT_WINDOW — window size.DEFAULT_PANES — pane border colors / width.DEFAULT_WORKSPACES — workspace strip colors / wrap.DEFAULT_BROWSER — browser chrome + border colors.Live values in module-level globals: bindings, termConfig,
windowConfig, paneConfig, workspaceConfig, browserConfig. Read
those at the point of use; never cache copies.
applyParsedConfig (in main.js) is the one place that reassigns the
globals + pushes changes to running state (keybindings, themes, tmux
conf, window sizes). Hot-reload works automatically if your feature
reads the live global.
Every feature has hardcoded knobs — colors, keys, sizes, timing, thresholds. After finishing a change, audit them and tell the user what you could expose. Don't wait to be asked.
Good candidates: keys, colors, fonts, sizes, timing constants, behavioral toggles, paths to external tools, multipliers and factors, any default where reasonable people disagree.
Not candidates: protocol constants (OSC numbers, IPC channel names), values required for correctness (e.g. 1-pixel pane inset — breaks focus border if changed), values with one obviously-right answer.
How to add a configurable:
DEFAULT_* (nest under a sub-object when
grouping — e.g. terminal.scroll.linesPerPress).applyParsedConfig pushes the change.
(The tmux conf, keybinding table, browser themes, and window sizes
already do this.)validateConfig.Report format:
Feature X has these hardcoded now. Candidates for config:
section.fieldA(default...) — what it controlssection.fieldB(default...) — what it controlsWant me to wire any?
Tight, scannable, one line per field. Let the user pick.
DEFAULT_* buckets over new top-level sections.main.js additions narrow — no abstraction without a second user.npm run dist after changes; copy
dist/mac-arm64/Ophanim.app → /Applications/Ophanim.app to test.