Releases: johannes-kaindl/json-editor
1.9.0
Eval-free JSON Schema validation + a clean community-review report. Replaces the Ajv validator with the eval-free @cfworker/json-schema, and drives the type-aware ESLint findings from the community.obsidian.md review to zero. The plugin bundle is ~52% smaller.
Changed
- JSON Schema validation now uses
@cfworker/json-schemainstead of Ajv. It is an eval-free, tree-walking validator (nonew Function/ noeval), so the automated review's "dynamic code execution" disclosure no longer applies. The bundle drops from ~176 KB to ~85 KB. Supported drafts are a superset of before (draft-04/07/2019-09/2020-12 vs. draft-07-only); the public validation contract (inline row errors + the error-count banner) is unchanged. formatkeywords are now enforced. The previous Ajv build ran withoutajv-formats, soformat(email,uri,date-time, …) was a no-op annotation. The new validator enforces formats in draft-07 mode, so a value that violates aformatconstraint in an (opt-in) companion schema now reports an error. A structurally malformed companion schema (e.g.{"type": 123}) is still rejected via draft-07 meta-validation; a merely cosmetic, non-URI$idno longer disables validation for the whole file.
Fixed
- The community-review ESLint warnings are now zero. The review's type-aware linter resolved
obsidianto the Vitest mock (via atsconfig.jsonpathsalias), so every Obsidian-typed expression was flagged asno-unsafe-*and one assertion asno-unnecessary-type-assertion. The mock alias is gone fromtsconfig.json(real Obsidian types now resolve), the Vitest mock moved totests/__mocks__/, and the redundant assertions were removed. None of this changes runtime behavior. A committednpm run lint:portalguard now mirrors the review so the warnings cannot silently come back.
Internal
- Added
tsconfig.test.json(editor typing of tests against the mock),eslint.portal.config.mjs+npm run lint:portal, andsrc/core/draft07-meta-schema.ts. The Vitest suite (601 tests) pins the new validator's error granularity and format behavior.
1.8.2
Lint follow-up. Fixes a regression in 1.8.1's review cleanup.
Fixed
- 1.8.1 silenced the
prefer-active-docwarnings by using theactiveDocumentglobal, but the community-review's type-aware linter seesactiveDocumentasany(it doesn't load Obsidian's ambient global declarations), which cascaded into manyno-unsafe-*warnings across the UI components. DOM access now goes through a typedactiveDoc(): Documenthelper, so call sites stay type-safe (prefer-active-docremains satisfied). Functionally identical; our owntscagainst the real Obsidian types was clean throughout.
1.8.1
Review cleanup. Addresses the recommendations and warnings from the Obsidian Community automated review (which had passed with no errors). No user-facing behavior changes — pop-out windows aside.
Fixed
- Pop-out window correctness (audit §2.11). All DOM creation now uses Obsidian's
activeDocument(or, in the purecore/render, aDocumentinjected by the adapter) instead of the globaldocument, and element type checks use Obsidian's cross-window-safeNode.instanceOf. Rendering a JSON view in a detached window now works correctly. (Resolves ~70prefer-active-doc+ 3instanceofreview warnings.)
Changed
- Replaced the
builtin-modulesbuild dependency with Node's built-inmodule.builtinModules(one fewer dependency). - Removed
!importantfrom the stylesheet (uses selector specificity instead); made SettingsonChangehandlers non-async (no-misused-promises); dropped redundant type assertions.
Added
- Build-provenance attestations for the release assets (
actions/attest-build-provenance), so users can cryptographically verify the assets were built from this repo.
Documentation
SECURITY.mddiscloses Ajv'snew Functionschema-compilation (opt-in only; no plugin code useseval/new Function) and the clipboard copy behavior.
1.8.0
Mobile interaction model. Tree-mode editing is now fully usable on touch devices, with no dead or invisible affordances. No desktop behavior changes beyond two additive improvements (keyboard reorder, focus-revealed copy button).
Added
- Cmd/Ctrl+E toggles Tree/Source while a JSON view is focused. It's handled by the view-local keymap scope (not a global command hotkey), so it never shadows the core "Toggle reading view" binding in Markdown — and the Toggle tree/source view command remains for custom rebinding. (Same scope mechanism that already powers Cmd/Ctrl+F/Z/Shift+Z inside JSON views.)
- Long-press action menu (mobile). On Obsidian mobile, long-pressing a tree row opens a consolidated menu with Copy value · Copy path · Rename key · Change type · Move up/down · Delete — replacing the hover-only inline buttons and drag-and-drop, which don't work on touch (audit §4.2, §4.3, §6.10).
Alt+ArrowUp/Alt+ArrowDownreorder the focused row within its parent — a keyboard path for reordering on every platform (previously only mouse drag-and-drop; audit §4.2).- Undo/Redo toolbar buttons on mobile, with a live disabled state, since there is no hardware
Mod+Zon touch (audit §4.5).
Changed
- On mobile, the hover-revealed inline row actions, copy button and drag handle are no longer rendered (they were invisible-but-tappable — a stray tap could fire an unconfirmed delete) and rows are not marked
draggable(which collided with touch scroll/long-press); all actions come from the long-press menu instead (audit §4.3, §4.4, §4.8). - ≥44px touch targets on mobile across the UI (audit §4.4): the collapse toggle, toolbar icon buttons (undo/redo), search-clear, mode pills, and the value-editing controls (inline text/number/key-rename field, the boolean checkbox, and the "+ Add key/item" button). The undo/redo buttons use Obsidian's native
clickable-iconclass for consistent sizing/theming.
Fixed
- The copy button is now revealed on
:focus-within, not just:hover(keyboard / touch-laptop access; audit §4.3.1). - The type-change menu now also closes on
pointerdownoutside, not onlymousedown, so it can't get stuck open on touch. hidden-toggled elements stayed visible. The large-file banner, schema banner, search bar, match-count and search-clear (×) set adisplayvalue on their class, which overrode the UA[hidden] { display: none }(equal specificity) — soel.hidden = truedidn't hide them (the "Load tree anyway" button showed on every file, including small ones; the × showed with an empty query). Added explicit[hidden]overrides (the fix already present for the tooltip).- The large-file banner's "Load tree anyway" button rendered in the wrong (UA-default) font because it set no
font-family, and read as unstyled/unclear; it now uses the interface font + native Obsidian button styling (comfortable label padding + button shadow), carries an explanatory tooltip, and the banner wraps gracefully on narrow screens. The+ Addaffordance gained matching padding.
Toolbar polish (audit §6.1)
- Removed the redundant Tree/Source view-header action — the labeled toolbar toggle and the
toggle-tree-sourcecommand remain the toggle paths. - The breadcrumb's current segment is now emphasized by font weight instead of a filled accent chip, so it reads as a path location rather than a button.
Accessibility & polish (pre-publish pass)
- Breadcrumb segments are now real
<button>s — Tab-focusable and Enter/Space-operable with a visible focus ring (WCAG 2.1.1); previously they were<span>s with click-only handlers. - The copy button and tree row-action buttons now use the interface font (parity with the rest of the chrome), and the copy button gained an
aria-label.
Notes
isDesktopOnlystaysfalse(the plugin uses no Node/Electron APIs). Real pointer-events touch-drag and the broader mobile/perf items (virtualization, source-mode debounce) remain deferred to a later release.
1.7.0
Rename & submission-prep release. No functional changes to the editor — this renames the plugin for the Community Plugin Directory and completes the documentation/attribution pass.
Changed
- Plugin ID renamed
obsidian-json-editor→json-editor. The Obsidian guidelines disallow anobsidian--prefixed plugin id; the manifestidand the on-disk plugin folder change accordingly. The internal view type is unchanged, so existing layouts are unaffected. After updating, move/rename your plugin folder to.obsidian/plugins/json-editor/. - Manifest/package description tightened to "View and edit JSON files with a Tree/Source toggle. Renders JSON code blocks in Markdown notes."
Added
THIRD-PARTY-NOTICES.md— full license texts for the bundled dependencies (ajv + transitive fast-uri (BSD-3-Clause) / fast-deep-equal / json-schema-traverse, plus @codemirror/lang-json and @lezer/json, all MIT). Attribution is also carried in themain.jsbanner.
Documentation
- README aligned to the actual 1.6.0 feature set (status, unified undo, tree-mode structural editing, schema validation, drag-drop, type-switching, large-file guard, all six settings, a Known conflicts section, shipped-vs-2.x roadmap), the dependency-license note corrected, and a documented tree-edit limitation (numeric-string object keys may reorder on save — audit 1.4).
- SECURITY.md threat model updated — prototype-pollution fixed; ReDoS opt-in + heuristic guard + honest residual surface.
- AGENTS.md submission path rewritten to the
community.obsidian.mdportal flow.
1.6.0
Guideline & UX release. Aligns with the Obsidian plugin guidelines and ships the high-value editing/UX gaps from the pre-submission audit.
Heads-up:
minAppVersionis now 1.5.7 (the new view-scoped keymap needs it).
Added
- Tree/Source toggle command (
Toggle tree/source view) plus a view-header icon — the mode toggle is now keyboard- and command-bindable (bind your own hotkey; no default is set to avoid clashing with the core Toggle reading view). - Search in Source mode —
Cmd/Ctrl+F(focus-search) now opens CodeMirror's find panel when you're in source mode instead of yanking you to tree; tree mode still focuses the tree search bar. - Large-file guard — files above a render budget (~1 MB or ~15k nodes) open in Source mode with a Load tree anyway banner, so a multi-MB file no longer freezes the UI on open.
Changed
- No more default hotkeys on commands.
Focus search,Undo edit,Redo edit(renamed, sentence-case) carry no default hotkey, per the guidelines; a view-local keymap still handlesMod+F/Mod+Z/Mod+Shift+Zwhile the JSON view is focused — and now correctly falls through to native input undo while you're typing in an inline editor. minAppVersionraised to 1.5.7 for the view-scoped keymap and view-header action APIs.- Various UI strings corrected to sentence case.
Fixed
- Source-mode undo/redo no longer destroys and rebuilds the CodeMirror editor on every step — it dispatches a minimal change, preserving cursor, scroll, and focus.
- Silent data loss / prototype reassignment when an object had a
__proto__(or other prototype-name) key and you deleted/renamed/reordered another key. Such keys are now preserved (and addable). - Pop-out windows: tooltips and the type-switch menu now resolve their document/window correctly instead of binding to the main window.
- Lifecycle leaks: the Source editor and the type menu are now torn down on view unload.
- Clipboard copy no longer throws (and now reports failure) on platforms without
navigator.clipboard; companion-schema paths are normalized; window-bound timers; no hard-coded inline styles.
Internal
- Added the official
eslint-plugin-obsidianmdas a CI guideline gate (npm run lint:obsidian); Biome stays the formatter. Test count 478 → 537.
1.5.0
Stability & data-integrity release. Fixes the data-loss and crash blockers found in a pre-submission audit. No new user-facing features — every change here protects your files.
Fixed
- Cross-file undo data loss (critical). The undo history was not reset when you switched files in the same pane, so
Cmd/Ctrl+Zin file B could restore — and save — file A's content over it. The same path silently reverted external/sync changes. History (and the schema, search query, and forced source mode) is now reset per file. - Large trees were clipped. A
.json-content { max-height: 5000px }cap hid everything past roughly 200–250 expanded rows with no scrollbar. Removed. - Plugin death on
.jsonconflict. If another plugin already handled.json,registerExtensionsthrew and the whole plugin failed to load (losing code-block rendering, settings, and commands). The claim is now guarded; on conflict you get a notice and code-block rendering keeps working. - Lost place after every edit. Re-rendering the tree reset manual expand/collapse, scroll position, and keyboard focus. These are now preserved across edits; after a delete, focus moves to the next sibling.
- Big-integer corruption. Editing a number to a value beyond 2^53 silently truncated it; the tree editor now rejects such input (use source mode for big integers).
Changed
- Schema validation is now opt-in (
validateAgainstSchemadefaults tofalse). A companion*.schema.jsonwas previously auto-loaded and compiled on every file open; a malicious schema in a shared/synced vault could freeze Obsidian via a catastrophic-backtracking regex (ReDoS). Enable it explicitly if you trust your schema files.compileSchemanow also rejects oversized schemas and obvious nested-quantifier patterns, and a stale companion-schema load can no longer apply to the wrong file.
Added
- Lossy-number warning. When a file contains integers JSON can't represent exactly (e.g. 64-bit IDs > 2^53), a banner appears and the tree opens read-only so a tree edit can't silently rewrite them; source mode stays editable.
Internal
- All
innerHTML = ""DOM clears replaced withreplaceChildren()(Community-Hub submission-gate requirement); a regression test enforces it across the source tree. - Test count 402 → 478 (+76), all green; build and Biome lint clean.
1.4.0
Changed
- Relicensed from GPL-3.0-or-later to AGPL-3.0-or-later. Documentation is now licensed separately under CC BY-SA 4.0 (see
LICENSE-DOCS). Part of adopting the shared workspace conventions. - Adopted Biome for linting/formatting; added
lintandtypechecknpm scripts. - Convention alignment: shields.io badge row,
AGENTS.mdnow committed with the standard section skeleton,.claude/gitignored.
1.3.0
JSON Schema validation in real time. Drop a companion schema file next to any .json you edit, and the plugin highlights validation errors as you type. Closes the 1.x roadmap.
Added
- JSON Schema validation via Ajv (v8). Default behavior: when you open
data.json, the plugin looks fordata.schema.jsonin the same folder; if found, every change in tree or source mode is validated against the schema. - Schema banner above the editor body shows the current error count (
"3 schema errors — hover the red rows for details"). Hidden when the document is valid; switches to a yellow "schema not loaded" variant if the schema file itself is malformed. - Inline row markers — every tree row corresponding to a validation error gets a red outline + a hover-tooltip with the human-readable message.
- Two new settings:
validateAgainstSchema(defaulttrue) — master switch.companionSchemaSuffix(default.schema.json) — change to e.g..json-schemaif your conventions differ.
- New runtime dependency:
ajv@8.
Internal
- New pure module
src/core/schema.ts—compileSchema(text)returns a discriminated union ({ok, schema}|{ok: false, error}); the compiled schema has avalidate(value): PathError[]that converts Ajv'sinstancePathJSON-Pointer to ourJsonPathsegment array (including~0/~1decoding). - New
src/obsidian/SchemaBanner.tsUI component withsetErrors(n)+setSchemaParseError(msg)+hide(). TreeView.setValidationErrors(map)adds.json-row-errorclass +titleattribute on offending rows; persists across re-renders.JsonFileViewgainssetSchema(text)(public for tests/manual) and an asynctryLoadCompanionSchema()that reads the sibling viaapp.vault.cachedReadon file open. Best-effort: silent failure if the vault is unavailable.- 33 new tests (12 schema core, 6 SchemaBanner, 5 TreeView row-marker, 6 JsonFileView integration, +4 settings extension). Total: 373 → 402.
Notes
- Bundle size:
main.jsgrows from ~37 KB to ~163 KB. The vast majority is Ajv; that is the cost of "real" JSON Schema. If you prefer the older lightweight bundle and don't need validation, turn the setting off — the validator code is still bundled but never runs. $schemaURL fetching is out of scope. The plugin treats$schemainside your data as metadata; it does not fetch remote schemas. Companion-file pattern is the canonical wire-up.
1.2.0
Unified cross-mode undo/redo. Tree mode and source mode now share a single 100-deep text-based history. Cmd/Ctrl+Z and Cmd/Ctrl+Shift+Z work in both modes; switching between tree and source no longer wipes the undo stack.
Changed
- Unified history: every change — tree-mode mutation or source-mode text edit — pushes the pre-state TEXT onto a single shared stack. Undo/redo restore that text and refresh the active mode.
- Mode-switch no longer clears the undo history.
- Source mode no longer keeps a separate CodeMirror-local undo stack; the unified history captures every doc change. Trade-off:
Cmd+Zin source mode now undoes peronChangeevent (roughly per keystroke) rather than via CodeMirror's character-grouping heuristic. In practice this is similar granularity.
Internal
Historyis now generic (History<T>).JsonFileViewusesHistory<string>instead of the previous tree-onlyHistory<HistoryState>. The oldHistoryStateinterface is gone; the new API takes and returns the rawT.- Plugin command IDs renamed:
undo-tree-edit→undo-edit,redo-tree-edit→redo-edit. Hotkey bindings are unchanged. - 4 new integration tests in
JsonFileView.undo.test.tscovering source-mode undo, source→tree cross-mode undo, tree→source cross-mode undo, and the canUndo / canRedo mode-independence guarantee. - Test count: 369 → 373 (existing tree-history tests reused as-is via generic typing).