██
░░
██ ██████ ██████ ███████
░██ ██░░░░ ██░░░░██░░██░░░██
░██░░█████ ░██ ░██ ░██ ░██
██░██ ░░░░░██░██ ░██ ░██ ░██
░░███ ██████ ░░██████ ███ ░██
░░░ ░░░░░░ ░░░░░░ ░░░ ░░
🭅█▌ 🭅🭡 🭊🭂██🭍🬾 🭅🭡 🭅🭡 🭅🭡 🭊🭂███🭡 🭊🭂██🭍🬾 🭅████🭡 🭊🭂██🭍🬾 🭅███🭍🬾
🭋██▌🭋█🭛🭋█🭛 🭋█🭛🭋█🭛 🭊█🭛🭋█🭛🭋█🭛 🭋█🭛 🭋█🭛 🭋█🭛 🭋█🭛 🭋█🭛🭋█🭛 🭋█🭛
🭅🭡▐▌🭅🭡 🭅████🭡 ▐█🭇🭄🭝🭚 🭅🭡 🭅🭡🭋██🭡 🭅████🭡 🭅🭡 🭅🭡 🭅🭡 🭅████🭚
🭋█🭛▐██🭛🭋█🭛 🭋█🭛 ▐█🭁🭠🭗 🭋█🭛🭋█🭛 🭋█🭛🭋█🭛 🭋█🭛 🭋█🭛 🭋█🭛 🭋█🭛🭋█🭛 ▐█
🭅🭡 ▐█🭡 🭅🭡 🭅🭡 ▐🭝🭚 🭅🭡 🭤🭓███🭡 🭅🭡 🭅🭡 🭅🭡 🭤🭓██🭞🭚 🭅🭡 🭅🭡
A fast, mouse‑friendly terminal JSON explorer/editor built with Textual and Rich. It renders a collapsed key tree where leaf values are hidden as (...). Use arrows or the mouse to navigate; Enter toggles branches or opens an operations menu for leaves: Display, Base64 decode, or Edit (in your $EDITOR).
✅ Works across multiple Textual releases (shimmed for
Log/TextLog/RichLog, conservative CSS, and portable Tree APIs).
-
Collapsed JSON key tree with
(...)for leaf values (keeps secrets and long blobs out of list view). -
Keyboard & mouse navigation in the terminal.
-
Enter on a branch toggles expand/collapse; Enter on a leaf opens ops menu.
-
Leaf operations
- Display: pretty‑prints the full value.
- Base64 decode: previews UTF‑8 text or a hex dump of bytes; optionally replace the leaf value with the decoded content.
- Edit: opens the value in
$EDITOR; upon save, updates in‑memory JSON.
-
Version‑tolerant UI: handles differences between Textual versions (tree args, log widget name, CSS properties).
-
No accidental data exposure in tree labels — only keys +
(...)are shown.
Note: Some Textual themes show chevrons/triangles for expand/collapse instead of explicit
+/−. Behavior is the same.
pip install textual richPython 3.11–3.13 recommended.
# From a file
python json_navigator.py --in path/to/data.json
# From stdin
cat data.json | python json_navigator.py
# Optional: set a custom title for the root
python json_navigator.py --in path/to/data.json --title "My JSON"-
$EDITOR or $VISUAL controls which editor opens for Edit.
- Fallbacks:
nano(POSIX) ornotepad(Windows).
- Fallbacks:
| Action | Keys/Mouse |
|---|---|
| Move selection | ↑ ↓ (and mouse) |
| Expand/Collapse branch | Enter (or mouse double‑click) |
| Open ops menu on leaf | Enter |
| Display selected leaf | d |
| Base64 decode leaf | o (open ops) → choose Base64 decode |
| Edit selected leaf | e |
| Quit | q |
Pretty‑prints the selected value using rich.pretty. Containers (dict/list) and primitives render clearly.
- Attempts
base64.b64decode(..., validate=True). - If result is valid UTF‑8, shows decoded text.
- Otherwise, shows a hex dump preview of bytes.
- You can Replace the leaf with the decoded content (UTF‑8 text, or a Latin‑1 best‑effort string for bytes).
- Strings: you edit the raw string; result is stored as a string.
- Non‑strings: initial buffer contains pretty‑printed JSON. On save, the app tries
json.loads(...); if parsing fails, it stores the raw edited text as a string.
All edits are in working memory only. See Persisting Changes below.
Currently the tool edits in‑memory only. Planned enhancements:
--out FILEto write the modified JSON on exit.:wstyle shortcut to save.- Confirm‑overwrite prompts.
This project aims to run on a wide range of Textual versions:
- Log widget: supports
Log,TextLog, orRichLogvia a shim. - Tree API: avoids
show_root/expandctor args; sets properties inon_mount()instead. - CSS: uses
text-style: bold;and avoids unsupported features likegap:or adjacent sibling selectors.
If you see an error, try: python -c "import textual; print(textual.__version__)" and open an issue.
-
ImportError: cannot import name 'TextLog'- Fixed by the shim; no action required. If you still see it, upgrade Textual or confirm the file is current.
-
CSS parsing errors (
bold,gap, orButton + Button)- The stylesheet uses conservative rules. If your local copy differs, switch to
text-style: bold;, avoidgap:, and prefer simple classes (e.g.,.ml2).
- The stylesheet uses conservative rules. If your local copy differs, switch to
-
Tree.__init__() got an unexpected keyword argument 'show_root'- Use the included version which sets
tree.show_root = Trueinsideon_mount()when available.
- Use the included version which sets
-
Editor doesn’t open
- Set
$EDITOR(e.g.,export EDITOR=vim) or rely on fallback (nano/notepad).
- Set
- Save to file (
--out,:w). - Search: fuzzy key search and JSONPath queries (
jsonpath-ng). - Copy value/path to clipboard (optional
pyperclip). - Add/Delete/Rename keys and list items.
- Type coercions and validators.
- Diff viewer (before/after; pretty unified diff for edited leaves).
- Huge JSON quality‑of‑life: streaming views, node count limits, and on‑demand loading for massive arrays.
- prompt_toolkit – lower‑level TUI primitives, flexible keymaps.
- urwid, npyscreen – mature non‑async TUI stacks.
- orjson/ujson – faster JSON parse/serialize.
- jsonpath‑ng – structured querying (future search feature).
- python‑editor or
click.edit– robust editor launching helpers.
Textual alone is sufficient for the core UX; the above can enhance performance or features.
- Style: Python, two‑space indents.
- Single‑file entry point:
json_navigator.py. - Keep UI portability in mind: prefer runtime feature checks over hard dependencies on the newest Textual API.
python json_navigator.py --in examples/sample.jsonpip install ruff mypy
ruff check .
mypy .MIT © You. Replace with your preferred license if needed.
Consider adding a GIF or screenshot in
docs/and referencing it here once you have one.