Repository navigation
Architecture
AI assistants: stop and read CLAUDE.md (and its pointers —
AGENTS.md,.cursorrules,.github/copilot-instructions.md) before editing anything in this repo. This page is the human-facing summary;CLAUDE.mdis the authoritative, exhaustive version and is kept in sync with every change by convention — if the two ever disagree,CLAUDE.mdwins.
Mongoterm is a full-screen neo-blessed TUI with no component re-render cycle. Each panel is a factory function that builds a blessed widget once, wires its own key/event handlers, and gets appended to the screen a single time by MognoTermLayout. From then on, panels mutate their own widgets directly (box.setContent(...), table.setData(...)) and call appInstance.renderScreen() to flush — there's no virtual DOM, no diffing, no subscriptions.
src/
app.ts entry point — bootstraps config, builds MongoTermApp
core/ screen ownership, global keybindings, event bus, theme
layout/ constructs all panels once, appends to screen
panels/ one file per widget (tree, workspace, query, modal, shell, ...)
services/ EVENTS enum, Mongo driver glue, connection I/O, query history
shared/ state.ts — one mutable object, not a reactive store
config/ theme + cross-platform filesystem paths
src/shared/state.ts holds everything — selected connection/db/collection indices, the live mongoClient, pagination, sort. There's no subscription model; modules just import state and read/write it directly. This is why panels have to explicitly call renderScreen() after mutating state — nothing re-renders automatically.
Panels never call MongoDB directly for anything beyond simple reads through state. Every mutation goes through one path:
- A panel emits an event from
EVENTS(src/services/enum.ts) — e.g.eventBus.emit(EVENTS.RECORD_UPDATE, { updated, query }). -
EventMongoService(src/services/mongodb/mongodb.events.ts) has the matchingeventBus.on(...)handler, which calls intoMongodbRepositoryfor the actual driver call, then emits a follow-up event — usuallyQUERY_SENDto refresh the visible results, orTOAST_SHOWfor user feedback.
Adding a new mutation always means: new EVENTS entry → emit it from the panel → handle it in mongodb.events.ts → implement the driver call in mongodb.repository.ts. Never skip straight to the repository from a panel.
One sharp edge here: those event handlers are async with no surrounding try/catch, and Node kills the whole process on an unhandled rejection. Any repository method reachable before a collection/db is guaranteed selected needs its own guard (see fetchQuery's no-db/no-collection check) — the UI won't always prevent that state for you.
-
Global/screen-level (
src/core/keybindings.ts) — bound once onscreen, dispatched by acondition()check (usually "is this panel focused?"). Used for cross-panel navigation. -
Per-widget (
widget.key([...], handler)inside a panel factory) — only fires while that exact widget has focus.
Both layers can fire on the same keypress — a global handler can move focus away from a widget, and that widget's own .key() binding still fires afterward using the pre-move focus reference. This is real, observed, and intentional (e.g. pressing l on the tree both expands the node and moves focus to the workspace in one keypress) — not a bug to "fix" unless a specific case is reported.
keybindbar.config.ts is the single source of truth for every keybind shown to the user — it drives both the bottom status bar and the ? help popup. Never hand-edit help.panel.ts's content directly.
-
Textbox/Textareanever implemented cursor-aware editing natively —cursorInput.service.tspatches this in. Any new free-text input should use it. - A text input with
keys: truebut noinputOnFocusand no immediate.readInput()call will silently launch$EDITORon the letter "e". - Percentage-based (
"N%-M") layout only works if every panel agrees on the same fixed-height bands (header/footer row counts) — hand-tuned percentages drift on resize. - Removing a modal overlay only removes the elements you explicitly hand to
removeScreenElement— remove every direct child, not just the top-level box, or you get ghost renders.
- Keybindings — full keyboard reference
- Features / Roadmap — what's implemented vs. planned
- Full architecture reference: CLAUDE.md