Architecture
browser-json-tree is a small PureScript library with a thin
JavaScript interop layer. The whole thing is four source modules under
src/, one foreign-function file, and one self-contained stylesheet.
Module layout
flowchart TD
PSApp["PureScript / Halogen app"]
JSApp["Vanilla JS app"]
subgraph src["src/JsonTree"]
Render["JsonTree<br/>render / renderWith / Config / LinkSpec"]
Cardano["JsonTree.Cardano<br/>resolve + shape detectors"]
Behaviour["JsonTree.Behaviour<br/>install :: Effect Unit (foreign import)"]
BehaviourJS["Behaviour.js<br/>click / dblclick / copy handlers"]
JSAdaptor["JsonTree.JS<br/>mount / mountFromString / install"]
end
Bundle["dist/browser-json-tree.js<br/>spago bundle of JsonTree.JS"]
CSS["dist/json-tree.css<br/>--jt-* themable styles"]
PSApp --> Render
PSApp --> Behaviour
JSApp --> Bundle
Bundle --> JSAdaptor
JSAdaptor --> Render
JSAdaptor --> Behaviour
Render --> Cardano
Behaviour --> BehaviourJS
Render -.->|"emits .v-* classes"| CSS
Behaviour -.->|"toggles open / .v-copy--ok"| CSS
| Module | File | Responsibility |
|---|---|---|
JsonTree |
src/JsonTree.purs |
The renderer. render / renderWith, the Config and LinkSpec types, and defaultConfig. Walks a Json and emits Halogen.HTML. |
JsonTree.Cardano |
src/JsonTree/Cardano.purs |
The default string-leaf resolver and its shape detectors (isTxidHex, isBech32Addr, isPolicyHex, parseTxin, shortHex, shortAddr). |
JsonTree.Behaviour |
src/JsonTree/Behaviour.purs + Behaviour.js |
A single install :: Effect Unit foreign import that attaches the document-level click / dblclick / copy listeners. |
JsonTree.JS |
src/JsonTree/JS.purs |
Vanilla-JS adaptor. mount / mountFromString / install, bundled to dist/browser-json-tree.js for non-PureScript consumers. |
JsonTree.Cardano mirrors the LinkSpec record type locally rather
than importing it from JsonTree, which keeps the dependency edge
one-directional (JsonTree → JsonTree.Cardano) and avoids a cyclic
module reference.
Render pipeline
render is renderWith defaultConfig, and renderWith is
renderValue, which dispatches on the six Json constructors via
Data.Argonaut.Core.caseJson:
flowchart LR
Json["Json value"] --> Case["renderValue<br/>(caseJson)"]
Case -->|"null / bool / number"| Prim["primitive span<br/>.v-null / .v-bool / .v-num"]
Case -->|"string"| Str["cfg.resolveString s"]
Str -->|"Just LinkSpec"| Link["truncated <a> link"]
Str -->|"Nothing"| Plain[".v-str text leaf"]
Case -->|"array"| Arr["<ol class=v-array><br/>per-item index bracket"]
Case -->|"object"| Obj["<div class=v-object><br/>per-entry <details>"]
Arr -->|"each item"| Case
Obj -->|"each entry"| Case
- Compound nodes (non-empty arrays and objects) render inside a
native
<details>; the key (.v-key-toggle) or array index marker (.v-sep-toggle) is the<summary>. Leaf nodes render inline. hideEmptydrops object entries whose value isnull,[], or{}before rendering (default on).initiallyOpensets the HTMLopenattribute on every compound node (default off — readers drive the unfold).- String leaves pass through
cfg.resolveString.Just LinkSpecbecomes a truncated<a target="_blank" rel="noopener">with the full value on thetitletooltip;Nothingbecomes a plain.v-strspan.
Interaction model
Native <details> already gives single-click toggle. Behaviour.js
adds what it does not:
- Single click on a
.v-key-toggle/.v-sep-togglesummary toggles that one level; on close it cascadesopen = falseto all descendant<details>so the next reopen starts one level deep. - Double click forces the whole subtree open (
cascade(_, true)) and clears the text selection — the gesture commits to "show me everything" and never collapses. - Copy fires in the capture phase for any
.v-copyelement with adata-copyattribute: it writes the value tonavigator.clipboardand briefly flashes.v-copy--ok. Capture phase stops the click from bubbling to a surrounding<summary>.
The summary handlers filter on the two toggle classes, so other native
<details> on the host page keep their stock behaviour.
Build outputs
Everything is driven from flake.nix (nix/project.nix) and mirrored
by just recipes:
| Output | Built by | What it is |
|---|---|---|
library |
spago build --offline |
Compile-check of the library; distributed as source. |
jsBundle |
spago bundle --module JsonTree.JS |
The standalone ES module dist/browser-json-tree.js (~180 KB, Halogen runtime included). |
example |
spago bundle --module Main (in examples/) |
The Halogen demo app, also an end-to-end smoke that the public surface stays reachable. |
docsSite |
mkdocs build --strict |
This documentation site, with the JS bundle, the demo, and json-tree.css copied into docs/assets/. |
nix flake check runs all four derivations plus a purs-tidy lint
gate. On a v* tag, .github/workflows/release.yml publishes a source
tarball alongside the standalone browser-json-tree-<version>.js and
json-tree-<version>.css assets.