A desktop editor for structured data files. It opens JSON, JSON Lines, YAML, and multi-document YAML as an editable tree with typed cells, schema validation, undo/redo, search, themes, and file-safe saves.
It started from Qt's Editable Tree Model example, but the current app is a practical editor for configuration files, data fixtures, schema-backed documents, and structured text that is awkward to maintain in a plain text editor.
git clone <this repo>
cd Editable-Tree-Model-Example
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtPython 3.12+ is expected. Dependencies are pinned in requirements.txt and include PySide6, PyYAML, simplejson,
gmpy2, python-dateutil, tzdata, pytest, and jsonschema[format].
python main.py
python main.py data.json
python main.py data.yamlYou can also drag one or more local .json, .jsonl, .ndjson,
.yaml, or .yml files onto the main window; each opens in its own
tab.
- Open a file with File ▸ Open or by passing a path to
python main.py. - Edit the Value column. The editor matches the value type (spin box, datetime editor, line edit, multiline dialog, hex dialog, color picker, etc.).
- Edit the Type column to intentionally reinterpret a value. The app preserves data when it can and uses explicit placeholders when a conversion would otherwise be ambiguous.
- Press
Ctrl+Sto save. Plain Save keeps the detected file format; Save As lets you choose a different supported format.
- Inspecting and editing nested configuration without losing the shape of objects and arrays.
- Working with JSON/YAML data that has richer semantics than plain text: exact rationals, percents, dates, timezones, base64 payloads, colors, currency/unit numbers, and secret-like fields.
- Schema-guided editing: attach or auto-discover JSON Schemas, see validation issues in a dock, and jump to offending rows.
- Large or sensitive fields: open multiline, binary, and secret values in dedicated editors with warning limits and masked display.
- Bulk tree edits: move, duplicate, paste, delete, and drag/drop multiple rows with undo support.
- Fixture maintenance: round-trip JSON/YAML fixtures while keeping exact numbers and typed display/editor behaviour.
- JSON (
.json) - JSON Lines / NDJSON (
.jsonl,.ndjson) - YAML (
.yaml,.yml) - YAML multi-document streams
- Atomic writes via
os.replace - Exact rational round-trip through
gmpy2.mpqhelpers
- Three-column tree: Name | Type | Value.
- Editable objects, arrays, primitive values, and the synthetic root.
- Multi-selection copy/cut/paste/delete/duplicate.
- Native drag-and-drop: move by default, copy with
Ctrl. - Drop cycle guard: a row cannot be moved into its own descendant.
- Context menu adapts to the selection — disabled actions are hidden rather than greyed; Expand / Collapse Recursively scopes to the selected subtree (or the whole document when the root is selected).
- Undo/redo and a History dialog powered by Qt's
QUndoStack.
- Multiple documents open in tabs; the title bar shows a
*for unsaved changes and the tab tooltip shows the full file path. - Reload from Disk (
Ctrl+R) re-reads the current file; if the tab has unsaved edits you can Discard them, Overwrite the file with the in-memory copy, or Cancel. - Close Tab (
Ctrl+W) and Reopen Closed Tab (Ctrl+Shift+T) — last 10 closed tabs are remembered. Empty untitled tabs close without a prompt; untitled tabs that contain data prompt to save first. - New From Clipboard (
Ctrl+Space) opens a fresh tab from a JSON or YAML payload on the system clipboard.
- Copy as YAML text — File-menu toggle that switches the copy text format between JSON (default) and YAML. The internal MIME payload still round-trips through other tabs without loss.
- Paste accepts JSON or YAML text from other apps; bare scalars are ignored.
The app infers and displays a JsonType for each value. Common kinds:
- Numbers: integer, exact rational float, percent, currency, units.
- Text: ASCII/UTF-8 line, multiline text, empty/whitespace previews.
- Secrets: single-line and multiline values are masked in the tree.
- Dates: date, time, naive datetime, timezone datetime, UTC
Zdatetime. - Binary: bytes, zlib, gzip, stored as base64-compatible text.
- Colors:
rgb/rgbahex values with swatch previews. - Structure: object, array, null, boolean.
Specialized editors include arbitrary-precision spin boxes, segmented datetime editing, multiline and hex dialogs, masked secret editors, and a color dialog.
- JSON Schema validation with
jsonschema[format]. - Schema discovery from inline local
$schema, sibling schema files, and persisted per-file manual bindings. - Manual schema attach from local JSON/YAML schema files or
http(s)URLs. - Local schema files hot-reload and revalidate all bound tabs.
- YAML multi-doc validation reports issues per document.
- Validation dock shows issues, navigates to rows, and can open the schema rule for an issue.
Ctrl+Frecursive name/value filter; matching descendants keep their ancestors visible.- Per-file view state: column widths, expanded rows, current row, and zoom level.
- Persistent window geometry, recent files, recent schemas, and dock layout.
- Built-in light/dark themes, type icons, follow-system mode, and optional user-theme hot reload.
- Confirm-before-open thresholds for very large string, multiline, binary, and attach-file operations.
- Context-menu Attach from… / Save as… for base64-like binary cells.
- Secret field masking is for shoulder-surfing/screen sharing only; secrets are still saved as plain strings on disk.
- Open the document.
- Show View ▸ Validation Panel.
- Use Schema ▸ Attach schema… in the dock or top-level Schemas menu.
- Click an issue to jump to the row; use the issue context menu to open the matching schema rule.
- Edit the value cell.
- If the field exceeds the configured limit, confirm that you want to open the modal editor.
- Save in the dialog; the edit is committed through undo/redo.
- Select a
bytes,zlib, orgzipvalue. - Right-click the row.
- Use Attach from… to encode a file into the cell, or Save as… to decode the cell to disk.
Secret fields are detected by word-prefixes in field names (for
example password, api_key, authToken, private_key). To edit the
prefix list, use File ▸ Secret word prefixes….
- Press
Ctrl+Fand type part of a key or value. - Right-click a match and choose Go To — the filter clears and the editor jumps to that row.
If another tool changes the file on disk, press Ctrl+R. If you have
unsaved edits in the tab, choose Discard to take the disk version,
Overwrite to save your edits over disk, or Cancel.
| Shortcut | Action |
|---|---|
Ctrl+N / Ctrl+O |
New / Open |
Ctrl+Space |
New tab from clipboard (JSON or YAML) |
Ctrl+R |
Reload current tab from disk |
Ctrl+S / Ctrl+Shift+S |
Save / Save As |
Ctrl+W / Ctrl+Shift+T |
Close current tab / Reopen last closed tab |
Ctrl+F |
Focus filter |
F2 or Enter |
Edit current cell |
Ctrl+I / Ctrl+Shift+I |
Insert sibling before / after |
Del |
Remove selection |
Ctrl+D |
Duplicate selection |
Ctrl+C / Ctrl+X / Ctrl+V |
Copy / Cut / Paste |
Ctrl+Shift+V |
Multi-paste: insert clipboard entries after selected targets |
Ctrl+Alt+V |
Multi-paste: replace selected target values |
Alt+↑ / Alt+↓ |
Move selection up / down |
Ctrl+Alt+↑ / Ctrl+Alt+↓ |
Promote selection out of its parent |
Ctrl+Alt+S |
Sort keys under selected object |
Ctrl++ / Ctrl+- / Ctrl+0 |
Zoom in / out / reset |
QT_QPA_PLATFORM=offscreen pytestThe suite currently collects 1023 tests. A small set of
color-scheme tests is known to be platform-sensitive under Qt's
offscreen QPA plugin because offscreen does not round-trip
QStyleHints.setColorScheme like real desktop platforms do.
make lintThe lint target runs autoflake, isort, and black with the repository
configuration.
ai-memory/repo-map.md— dense module-by-module map for agents and contributors.ai-memory/pros-n-cons.md— current strengths, caveats, and gaps.ai-memory/todo-n-fixme.md— active open work only.ai-memory/history.md— archived resolved phase/feature history.plans/— feature plans and definitions of done for larger changes.
Activate the repo-local git hooks (run once after cloning):
make dev-setupThis installs .githooks/pre-commit, which rejects new getattr /
hasattr calls outside the small allowlist documented in
plans/10-allowlist-and-precommit-hook.md (jsontream/__init__.py,
validation/error_adapter.py, app/runtime_compat.py). Tests may use
reflection but must justify each call with an inline # allow: <reason>
comment. CI runs the same check via make check-no-reflection.