Skip to content

Configuration

abax is configured through a single JSON settings file, a handful of environment variables, and a set of platform-correct runtime directories that hold its config, data, cache, and logs. Almost nothing needs configuring to get started — the defaults are sensible and every optional feature degrades gracefully when its dependency is absent — but this page documents every knob: where settings live, what each field does, the environment variables, the theme presets, the on-demand OpenDyslexic font, and pandoc detection for equation rendering.

Settings file

Settings are stored as JSON in settings.json inside abax's config directory (see Runtime directories below — typically …/abax/settings.json). The GUI and TUI load it at startup and write it back when you change a setting. If the file is missing or unreadable, abax silently uses the defaults, so deleting it is a safe way to reset.

JSON encoding uses msgspec when the fast-io extra is installed and falls back to the standard library otherwise; the behavior is identical either way. The schema is versioned and migrates lazily on read (for example, an old color_scheme field is renamed to theme automatically and written back).

Settings fields

Field Type Default Meaning
theme string "obsidian" GUI theme preset (see Themes).
vim_mode bool true Vim-style key bindings, on by default.
tui_theme string "obsidian" TUI color theme.
zoom float 1.0 GUI zoom factor.
dyslexic_font bool false Use the OpenDyslexic font across the GUI (see OpenDyslexic font).
calc_model string "" Last-used calculator model key (e.g. 16c, 15c, ti83, alg); restored on launch. Empty = default (HP-16C).
calc_style string "image" Last-used HP faceplate style (image or vector).
calc_degrees bool false Calculator Deg/Rad mode, restored on launch.
last_sheet int 0 Active sheet index, restored on launch.
last_cell string "" Cursor cell (A1), restored on launch.
code_consent bool false Whether you've consented to run untrusted code (console/terminal/scripts/macros). Set back to false to be prompted again.
code_isolation string "isolated" How code execution is isolated: off (in-process, no worker/limits), restricted (the out-of-process, resource-limited worker plus an AST allowlist applied to your code that blocks OS/filesystem/network access — a language-level block, not an OS boundary; sits between off and isolated in the cycle; the optional restricted extra adds RestrictedPython compile-time guards), isolated (out-of-process worker + resource limits), or strict (also OS-confine filesystem + network). Cycle it from the command palette; see Macros & scripting.
faceplate_assets_dir string "" Folder of calculator faceplate artwork (see Faceplate assets).
show_toolbar bool true Show the GUI toolbar.
autosave_enabled bool true Whether the GUI periodically autosaves settings.json.
autosave_interval int 30 Autosave cadence in seconds (when enabled).
recent_files list [] Recently opened file paths.
window_geometry dict {} Saved GUI window position/size.
fm_buttons list [] Your custom file-manager command buttons ({label, command}); see File manager.
auto_install bool true Auto-install optional dependencies (and show the first-run chooser); see Auto-install. Set false to opt out.
deps_prompted bool false Whether the first-run optional-feature chooser has been shown. Set back to false to be asked again.
calc_iterative bool false Resolve circular references by capped fixed-point iteration instead of surfacing #CIRC! (off by default, like Excel).
calc_max_iterations int 100 Maximum iterations for iterative calculation (when calc_iterative is on).
calc_max_change float 0.001 Convergence tolerance for iterative calculation — iteration stops once the largest cell change falls below this.
high_contrast bool false High-contrast accessibility mode.
speak_on_move bool false Speak the active cell aloud on cursor move (GUI + TUI); needs the tts extra.
tui_screen_reader bool false Single-line, reader-friendly TUI rendering for screen readers.
plugins_enabled bool false Whether third-party UDF/format plugins (entry points) may load. Off by default — loading runs third-party code with full privileges.
live_data_enabled bool false Whether the REST/WEBSOCKET live-data formulas may open network connections. Off by default so a workbook opened from disk can never phone home; enable via Tools → Enable live data or TUI :live on. URLs are limited to http/https/ws/wss.
external_refs_enabled bool false Whether closed-workbook external references (=[Book.abax]Sheet1!A1) may read other workbook files. Off by default so an opened file never pulls in others on its own; enable via Tools → Enable external references or TUI :extern on. Paths resolve relative to the open workbook's folder; only .abax/.json load.
windowed_store_capacity int 0 Bounds resident cells per sheet, spilling the rest to a private temp file. Three-way: 0 (default) = Auto — when a file is opened, only sheets with ≥ 100,000 populated cells are windowed (at 50,000 resident cells), so small workbooks are untouched; a positive value windows every sheet at that capacity; -1 = never. A memory ↔ latency trade-off that matters for very large data imports (lots of literal cells — ~48% steady-state saving measured on a 125k-cell sheet); formula-heavy sheets see little benefit (the value cache and key index stay resident). Dependency chains evaluate correctly at any capacity (up to ~10,000 cells deep — the engine's recursion headroom, independent of this setting). Opening a native .abax/.json file applies the policy during the load: cells stream straight into the windowed store (spilling past-capacity cells as they arrive), so a huge file never materializes fully in memory first — peak usage tracks the parsed file plus the resident window instead of the whole workbook (measured on a 150k-cell file: below even a plain un-windowed load, where the old load-then-migrate path briefly exceeded it). Other formats (CSV, Excel, …) still load into the plain store and then migrate, and changing the capacity on an already-open workbook re-homes its cells likewise — those paths briefly cost extra memory (~1.5× while cells move); the savings are the steady state after it. Set it from Preferences → System → Performance (or by hand in settings.json).
chart_backend string "auto" How embedded charts (Insert → Embedded chart (on sheet)…) render in the GUI: auto = matplotlib when it is installed, else the built-in pure-stdlib SVG renderer; svg = always the built-in renderer; matplotlib = matplotlib, falling back to SVG (with a status-bar hint) when it isn't installed. Both backends draw the same data. Set it from Preferences → Appearance → Embedded charts.
schema_version int 7 Settings schema version (managed by abax).

You can edit settings.json by hand while abax is closed, but you rarely need to: the Preferences dialog (Edit → Preferences…, Ctrl+,) is the central place to manage every field, grouped into Appearance (GUI + TUI theme, font, zoom, toolbar, vim keys), Calculator (default model, faceplate style, angle mode, faceplate folder), and System (autosave, code-execution consent + isolation, optional-dependency install). Changing settings from the app is the recommended way — it persists and applies without a manual edit.

Startup script (init.py)

For power users, abax runs an optional Python script at CONFIG_DIR/init.py (e.g. ~/.config/abax/init.py) when the GUI or TUI starts. It's your own trusted config — like a .vimrc or .pythonrc, executed with your privileges — and it receives an abax facade for rebinding keys, adding macro-menu entries, and registering custom formula functions:

# ~/.config/abax/init.py

def to_top(ed):
    ed.row = 0
    ed._reclamp()

# Rebind a key (rebinds override the built-in bindings). Works in every TUI
# mode: "normal", "insert", "command", "rpn", "visual", "browser".
abax.bind_key("normal", "K", to_top, desc="jump to top")
abax.bind_key("insert", "ctrl+w", lambda ed: ed.delete_word(), desc="del word")

# Register a named entry for the macro menu / palette.
abax.register_macro_menu("Uppercase cell", lambda ed: ed.sheet.set_cell(
    ed.row, ed.col, ed.sheet.get_raw(ed.row, ed.col).upper()))

# Register a custom formula function — usable in cells as =DOUBLE(A1).
def double(args):
    return (args[0] or 0) * 2
abax.register_function("DOUBLE", double)
# kind="lazy" receives unevaluated argument nodes (control-flow functions);
# kind="context" receives (arg_nodes, ctx) like ROW/OFFSET. A user function
# may deliberately shadow a built-in of the same name.

Bare keys are matched as the literal keystroke, so case matters ("K""k"). Modifier chords are normalized, so "Ctrl+S", "ctrl+s" and "C-s" all name the same binding. Rebinds fire in whichever mode you registered them for (the six modes above); in insert and command mode only non-printable chords like ctrl+w are intercepted, so ordinary typing is never captured. Run :map in the TUI to list all your rebinds, or :map <mode> for one mode.

The action is called with the editor. A broken init.py never blocks startup — the error is captured and surfaced in the status line, and abax carries on with its defaults. This is not the sandboxed code-execution path (console/macros); it is your own config, trusted by design.

Runtime directories

abax never hardcodes paths. It resolves four OS-appropriate directories at startup and creates them if needed. When the platformdirs package (from the fast-io extra) is installed it uses that; otherwise a built-in fallback mirrors the same logic.

Directory Holds
CONFIG settings.json, the macros/ folder for auto-discovered macros.
DATA Persistent application data — an exchange/ subfolder for the generic JSON interchange format, and crash.log (a stack dump written on a native / Qt crash).
CACHE Downloaded assets — the OpenDyslexic font (fonts/).
LOG Log files.

Typical locations per platform:

Platform CONFIG DATA CACHE LOG
Windows %APPDATA%\abax %LOCALAPPDATA%\abax %LOCALAPPDATA%\abax\Cache %LOCALAPPDATA%\abax\Logs
macOS ~/Library/Application Support/abax same as config ~/Library/Caches/abax ~/Library/Logs/abax
Linux $XDG_CONFIG_HOME/abax (~/.config/abax) $XDG_DATA_HOME/abax (~/.local/share/abax) $XDG_CACHE_HOME/abax (~/.cache/abax) …/abax/logs

To see the exact paths on your machine, run:

abax --deps

It prints the config, data, cache, and log directories at the bottom of the report (see cli.md).

Environment variables

ABAX_QT_BINDING

Forces which Qt binding the GUI uses. abax prefers PySide6 (LGPL) and falls back to PyQt6; no GUI code branches on the binding, so the app runs identically on either. Set this to override the default order:

ABAX_QT_BINDING=PyQt6 abax gui

Only PyQt6 is treated specially: setting it forces the PyQt6 path even when PySide6 is installed. Any other value (or leaving it unset) keeps the default PySide6-then-PyQt6 order. This is mainly useful for testing both bindings. See getting-started.md for installing each one.

ABAX_FACEPLATE_DIR

Points at a faceplate-assets root — a directory that holds per-model subfolders of calculator faceplate artwork used by the GUI's photographic faceplate for the built-in RPN calculator. Each model subfolder must contain a background.png and at least one .kml layout file.

# Point at a local checkout's voyager assets
export ABAX_FACEPLATE_DIR=/path/to/qrpn-voyager/qrpn/assets/voyager
abax gui

See Faceplate assets for the full resolution order. abax bundles no artwork and never copies these files — it only reads them in place.

PANDOC

Points at a pandoc executable for rich equation rendering. See Pandoc below.

ABAX_NO_AUTOINSTALL

Set to any non-empty value to disable optional-dependency installs entirely — the first-run chooser won't appear and abax won't run pip (equivalent to auto_install: false in settings). See Auto-install.

Auto-install

abax's core is pure stdlib and every heavier capability is an optional package with a graceful fallback — abax installs nothing on its own. On first GUI launch it shows a chooser that explains each optional feature group; nothing is selected by default — you pick only what you want. Two presets make choosing a whole set one click: Thin (the lean everyday conveniences) and All (everything abax can use). Your selection is fetched in a background daemon thread (best-effort, non-blocking) and the fact that you were asked is remembered (deps_prompted), so the chooser doesn't reappear on its own. In the TUI/headless there's no dialog: a one-time notice points you at abax deps (install everything) or pip install abax[…] (specific extras).

  • Best-effort and non-blocking. Startup and the UI never wait on pip. If pip is unavailable, you're offline, or a build fails, abax silently keeps using its pure-Python fallbacks.
  • Once per machine. A marker file per package (under the cache directory's autodeps/ folder) means a slow or failing install is not retried on every launch.
  • Opt in, and revocable. Nothing installs unless you choose it. auto_install: false in settings (Preferences → System) or ABAX_NO_AUTOINSTALL=1 disables installs entirely.
  • The Qt GUI binding (PySide6/PyQt6) is not auto-installed — you need it to launch the GUI in the first place, so install it explicitly with pip install abax[gui].

Add more any time: Tools → Install optional features or Preferences → System → Manage optional features… re-open the chooser; abax deps installs everything from the command line, synchronously; and abax --deps reports the install state and how many optional packages are present.

Themes

The GUI ships a set of theme presets in abax/gui/theming.py; the TUI has matching color themes. Set theme (GUI) or tui_theme (TUI) in settings.json, or switch from within the app. An unknown name falls back to the default (obsidian).

Preset Style
obsidian Default dark theme.
light Light theme.
high_contrast High-contrast theme.
nord Nord palette.
dark_one Atom One Dark style.
solarized Solarized.
crt_green Green phosphor CRT.
crt_amber Amber phosphor CRT.

The GUI renders any preset through a token-based stylesheet, and the GUI theming module can also import themes from an Obsidian CSS snippet or a Zed JSON theme. The TUI maps each theme to the nearest 256-color terminal palette.

OpenDyslexic font

abax can use the OpenDyslexic typeface (SIL OFL 1.1), a free, openly licensed font designed to ease reading for people with dyslexia. When enabled it applies across the UI — menus and dialogs, the grid cells, and the Python console / terminal — while the calculator's LCD and the painted faceplates keep their own display fonts. The binaries are not bundled: when you enable the dyslexic font (the dyslexic_font setting, toggled in the GUI), abax downloads the Regular and Bold .otf files from the upstream GitHub repository (pinned to a fixed commit) into its cache directory (CACHE/fonts/) on first use.

The fetch is best-effort and offline-safe — any network or file error is logged and swallowed, so toggling the font on without a connection simply leaves it unavailable rather than raising an error. Once cached, the font is reused with no further network access.

Faceplate assets

The GUI's built-in RPN calculator can render a photographic faceplate (background image + key overlays + a .kml layout per calculator model). abax distributes none of this artwork; it reads asset files you supply. A faceplate is considered usable when its model folder contains a background.png and at least one *.kml layout file.

abax looks for a model's assets in this order and uses the first usable match:

  1. The faceplate_assets_dir setting, if set (the assets-root folder).
  2. The ABAX_FACEPLATE_DIR environment variable (also an assets-root folder).
  3. A local qrpn-voyager/ or qv/ checkout found beside the working directory, its parent, or the abax source tree — assets are expected under qrpn/assets/voyager/<model>/. Contributors who keep that checkout handy get the artwork with no configuration.

Both faceplate_assets_dir and ABAX_FACEPLATE_DIR should point at the assets root (the directory that holds the per-model subfolders), for example a local qrpn-voyager checkout's qrpn/assets/voyager.

Pandoc for equations

abax can render LaTeX math to MathML for its equation feature. It prefers a real pandoc binary and resolves one in this order:

  1. The PANDOC environment variable, if it names an executable on the PATH.
  2. A pandoc executable on the PATH.
  3. A pandoc binary managed by the pypandoc package, if installed.

If none is found, abax can bootstrap one on demand by pip install-ing the pypandoc_binary wheel (which bundles the executable) and then exposing its path through the PANDOC environment variable. The whole process is graceful: with no network or no pip it simply reports pandoc as unavailable and falls back to a built-in subset MathML renderer — it never raises.

To point abax at a specific pandoc:

export PANDOC=/usr/local/bin/pandoc
abax gui

abax --deps reports whether pandoc is available and what the fallback is.

See also