Skip to content

kk4cnm/nanoj

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nanoj

The JSON editor that can’t save broken JSON.

A terminal app for viewing and editing JSON — think nano, but it understands JSON structure so you can't accidentally break the formatting.

nanoj is for anyone who hand-edits JSON and is tired of hunting down a missing comma or an unbalanced brace. You navigate and edit a structured view of your data; nanoj writes the file back out as clean, valid JSON every time. Bad formatting isn't caught — it's impossible, because you never edit the raw text directly.

nanoj editing a large JSON manifest in the terminal

Status: a capable interactive editor. Navigate and edit a collapsible tree or a spreadsheet-style table view; change types, add/cut/copy/paste nodes, search, sort, and save — all backed by a tested valid-by-construction JSON engine. See docs/DESIGN.md.

Why it exists

JSON is everywhere in modern LLM/agent tooling — MCP servers, tool definitions, config. Hand-editing it is error-prone, and a single formatting slip wastes time. nanoj makes well-formed JSON the only possible output.

Try the current build

Requires Go 1.21+.

go build -o nanoj .
./nanoj path/to/file.json
./nanoj --lenient path/to/config.jsonc      # tolerate comments + trailing commas
./nanoj --schema schema.json config.json    # validate against a JSON Schema
./nanoj --diff old.json new.json             # show a structural diff overlay
./nanoj --view data.json                     # read-only: browse without editing
./nanoj --from yaml config.yaml              # convert YAML (or toml) to a JSON working copy

Reading JSONC (comments and trailing commas)

By default nanoj reads strict JSON. Pass --lenient to also accept // and /* */ comments and trailing commas — handy for opening config-style files. Because the document model has nowhere to store comments, they are dropped, and saving writes strict JSON; nanoj shows a in the title and a reminder when you save so this is never a silent surprise. (Trailing commas are simply normalized away — that's not lossy.) Think of it as "import sloppy JSON and clean it up" rather than "edit JSONC in place."

Keys (nano-style; ^X means Ctrl-X):

Key Action
/, Ctrl-P/Ctrl-N move
/ expand / collapse (or descend / jump to parent)
Enter edit a value, toggle a bool, or expand/collapse a container
t change a value's type
a add a key (object) or element (array)
^K / M-6 / ^U cut / copy / paste a node (recoverable via paste or undo)
^W search keys and values, or filter by predicate (reveals matches in collapsed branches)
^T toggle the table view for an array of objects
M-U / M-E undo / redo (also ^Z / ^Y)
^O write the file
^X exit (prompts if there are unsaved changes)

Search filters

^W does a plain key-or-value substring search by default. For large documents you can also search by predicate using a small grammar:

Term Matches
key:name nodes whose key contains name
type:number nodes of that JSON type (null/bool/number/string/array/object, plus aliases like boolean, int, obj)
value:123 scalar values containing 123
key=value a field whose key equals key and value equals value (exact, case-insensitive)

Terms are combined with AND, so key:port type:number jumps to numeric port fields. A query with no predicate term is treated as a single substring exactly as before (including queries that contain spaces). In the table view, a cell's column name acts as its key, so key: and key=value work there too.

Table view

When a document is an array of objects, nanoj opens it as a spreadsheet-style grid (rows = elements, columns = the union of keys). You can also press ^T on any such array in the tree view. Arrow keys move between cells and Enter edits a cell: a string/number keeps its type, a bool toggles, and a blank or null cell is filled with a value whose type is inferred from what you type (42 → number, true → bool, empty → null, otherwise text). Nested values are edited in the tree view. ^T or Esc returns to the tree.

Wide tables scroll horizontally as you move: the row-number column stays fixed, and / markers in the status bar show when more columns lie off-screen. ^A/^E jump to the first/last column, Home/End to the first/last row, and PgUp/PgDn page through rows. ^W searches cell values and jumps to the next match (scrolling it into view). ^K/M-6/^U cut/copy/paste whole rows — handy for duplicating a row (copy then paste). s sorts the rows by the selected column (press again to reverse); the sort is type-aware and reorders the underlying array, so it persists on save and is undoable.

Schema awareness

Point nanoj at a JSON Schema and it validates the document and overlays what it learns — without ever touching your data:

nanoj --schema config.schema.json config.json
  • Values that fail validation are marked with a red in a left gutter.
  • Required fields are flagged, and an object that is missing a required key reports it in the status line.
  • The selected field's expected type and description show in the status line, so the schema doubles as inline documentation.
  • When a field is constrained to an enum, pressing Enter offers the allowed values as a numbered pick-list instead of free-text entry.
  • In the table view, annotated cells show their marker inline and the row gutter carries row-level problems (like a missing required key).

It's a read-only overlay: the schema is never written back, and validation re-runs as you edit so the markers stay live. $ref (including $defs) is resolved, and the combinators are surfaced per-field where that's unambiguous: allOf branches all apply, so their properties, required keys, types, and descriptions are merged; anyOf/oneOf branches contribute the alternative types (e.g. string or integer) and their enum values merged into one pick-list. A schema that fails to compile is reported and skipped so it never blocks editing.

Diff overlay

Compare a document against a baseline and see a structured, path-aware diff right in the tree or table:

nanoj --diff baseline.json working.json   # opens working.json, compared to baseline.json
  • Added nodes are marked +, changed values ~; the title bar summarizes the counts ([diff +A~C-R]).
  • Objects are compared by key and arrays positionally (no move/LCS detection — a reordered array reads as a run of changes).
  • Removed nodes don't exist in the working tree, so they can't be marked inline; they're surfaced as the -R count in the summary.

Like the schema overlay, it's read-only and recomputes as you edit.

Read-only mode

nanoj --view data.json

Browse and search a document with no chance of changing it: every editing key and save is disabled and the title shows [read-only]. Handy for inspecting a file (optionally alongside --schema or --diff) when you only want to look. Note this is a safety/intent mode — it doesn't meaningfully change memory use, since undo snapshots are only ever created by edits.

Opening YAML and TOML (conversion)

nanoj --from yaml config.yaml    # or: --from toml config.toml

This is an explicit, one-way conversion, not round-trip editing. nanoj decodes the file into the same JSON tree (key order preserved, big integers intact, YAML anchors expanded) and opens it as a JSON working copy — the buffer points at config.json and ^O writes JSON there. The YAML/TOML source file is never written back, and the title shows a [from yaml] badge so the mode is never a surprise.

What doesn't survive, by design: comments, anchors/aliases (they're expanded), and custom tags. Datetimes become RFC 3339 strings. Values JSON genuinely cannot represent (inf, nan) are a load-time error rather than a silent mangling. The valid-by-construction promise is a JSON promise — keeping export out of the editing path is what keeps it honest.

Configuration

nanoj reads an optional JSON config file (so you can edit it in nanoj itself). Write a starter file to the default location with:

nanoj --write-config

The file is looked up at --config <path>, then $NANOJ_CONFIG, then your per-OS config dir (e.g. ~/.config/nanoj/config.json on Linux, ~/Library/Application Support/nanoj/config.json on macOS).

Options:

Field Values Meaning
defaultView auto (default), tree, table which view to open in (auto = table for arrays of objects)
indent a string, e.g. " " or "\t" indentation used when saving
theme default, colorblind, high-contrast, mono base color/attribute palette
styles per-element overrides fine-tune individual elements

Accessibility

Theming is built around accessibility, not just looks:

  • colorblind — an Okabe–Ito-based palette chosen to stay distinguishable across common forms of color blindness, and it reinforces type differences with bold/italic so meaning never depends on hue alone.
  • high-contrast — bright, bold colors for low-vision use.
  • mono — no color at all; element types are told apart purely by bold, italic, underline, and faint.
  • The standard NO_COLOR environment variable (and NANOJ_NO_COLOR) is honored — when set, nanoj drops all color and falls back to attribute-only styling.

Each element (key, string, number, bool, null, structure, header, selection) can be individually overridden with fg, bg, bold, italic, underline, faint, and reverse:

{
  "theme": "colorblind",
  "styles": {
    "string": { "fg": "#3cb371", "italic": true },
    "key":    { "fg": "12", "bold": true }
  }
}

Design

nanoj is written in Go for single static binaries across macOS, Windows, Linux, and the BSDs, with a low barrier for contributors. The full reasoning — the "valid by construction" principle, why the tree is the source of truth, and why we lean on the standard-library JSON parser — is in docs/DESIGN.md.

License

MIT

About

A nano-style terminal editor for JSON — edit a structured tree, always save valid JSON.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages