Deepen JSON formatter: tree view, copy path, per-field expansion, auto-fixes, lossless numbers - #7
Merged
Merged
Conversation
A Text / Tree switch in format mode. The tree shows the same value as the text output (sorted keys, expanded embedded JSON), flattened by a pure core/tree.ts that only walks expanded containers, and virtualised with @tanstack/react-virtual so only viewport rows are in the DOM. Rows are keyed by RFC 6901 pointer, which A-2 (copy-path) will build on.
expandEmbedded rebuilt objects with `out[key] = v`, which for a "__proto__" key invokes the prototype setter instead of defining a property, so the key vanished from the output. Build with Object.fromEntries, which defines own properties.
…ssless numbers - A-2: the tree header copies the selected node as an RFC 6901 pointer, dot path or jq path. core/paths.ts adds toJqPath plus parsers for all three, and tests prove every flavour resolves back to the same node. toDotPath no longer prints a top-level "$" key as the root marker. - A-3: embedded JSON chips toggle per field. A field nested in another payload is disabled until its parent is expanded. - A-4: pure fixes returning TextEdit[] (BOM, )]}' prefix, JSONP, comments, single quotes, bare keys, trailing commas), run as a pipeline so edits never overlap. "Review in Diff" seeds Diff's saved state via lib/persist/handoff.ts instead of embedding MergeView. - A-5: when the number gate trips, re-parse with native JSON source access and JSON.rawJSON so format/minify keep literals exactly, including inside expanded embedded JSON. Falls back to the warning where unsupported. Tree rows no longer carry a path array; pathOf rebuilds it from parent links for the selected row only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Covers every "deepen JSON" task in TODO (A-1 to A-5), plus an existing bug found along the way. There are three commits: the tree view, the bug fix, and the other four tasks.
A-1 · Tree view
A Text / Tree switch in Format mode. The tree shows the same value as the text output (sorted keys, expanded embedded JSON), because both read from one shared memo.
@tanstack/react-virtual, so only rows on screen are in the DOM.role="tree"andaria-activedescendant.md(touch targets).A-2 · Copy path
Selecting a row shows its path in a header strip, with three copy buttons: Pointer (
/items/0/id), Path (items[0].id) and jq (.items[0].id). They sit in the header rather than on the row, so they work on touch and aren't hover-only.core/paths.tsaddstoJqPathand parsers for all three formats. The tests check that every row of a test document resolves back to the same node in each format, plus fast-check round-trip properties.toDotPath(['$'])printed$, the same as the root, so a copied path pointed at the wrong node.pathOfrebuilds the path from parent links, and only for the selected row.A-3 · Expand embedded JSON one field at a time
Each embedded-JSON chip is now a toggle (
aria-pressed). Expand all / Show original and the toolbar toggle still switch all of them.expandPointers. It's additive (saved state is merged over the defaults), so nostateVersionbump.A-4 · Auto-fixes
core/fixes.tshas one pure function per fix, each returningTextEdit[]:)]}'prefixThey run as a pipeline, so each fix sees the previous one's output. That keeps edits from overlapping and makes the counts exact.
When the input doesn't parse, a panel lists what it can fix and whether the result will be valid JSON. It offers:
lib/persist/handoff.ts, then opens/diff. Embedding CodeMirror's MergeView would have pulled ~92 kB into the JSON chunk.This replaces the old "Strip them" bar.
Properties: valid JSON is never changed, and a sloppy JS-style version of any JSON value repairs to the same value.
A-5 · Lossless numbers
When the existing big-number check fires, the document is re-parsed with native
JSON.parsesource access andJSON.rawJSON. Format, minify and sort then keep the literals exactly:12345678901234567890stays exactly as written. This also covers numbers inside expanded embedded JSON.lossless-json: no dependency to vet, and itsLosslessNumberobjects would need the same special-casing in every walker anyway.Fixed:
__proto__keys dropped by embedded expansionAn existing bug.
expandEmbeddedrebuilt objects without[key] = …. For a"__proto__"key, that sets the object's prototype instead of adding a key, so the key vanished. Objects are now built withObject.fromEntries. There's a regression test, checked failing first.Size
JsonToolchunk goes from 9.2 to 21.5 kB gzip: about 5–6 kB is the virtualizer, the rest is the new code.react/incompatible-libraryrule, which is about the React Compiler, and this build doesn't use it.Checks
tsc --noEmit,lint(only the existingMergePanewarning),test(445 passing),build.items[0]["id-x"]shown, all three buttons present.1.0, and a big number inside an embedded payload are all kept./diffwith both sides filled; Apply gives valid JSON.