@cp949/inspecta-react
v1.0.2
Published
Interactive React component for displaying and editing JSON/JS objects.
Readme
@cp949/inspecta-react
@cp949/inspecta-react is a React component for exploring and editing JSON and general JavaScript Document Values.
It supports scalar roots, Map, Set, BigInt, circular and shared References, and consumer-defined value adapters.
Installation
pnpm add @cp949/inspecta-reactReact and react-dom ^18.0.0 || ^19.0.0 are peer dependencies. Import the stylesheet explicitly:
import Inspecta from "@cp949/inspecta-react";
import "@cp949/inspecta-react/index.css";
export function App() {
return <Inspecta value={{ hello: "world", count: 1n }} />;
}Controlled editing
Inspecta never mutates the original value. Editing creates an immutable replacement, which the consumer must publish as the next controlled value:
const [value, setValue] = useState({ user: { name: "Kim" } });
<Inspecta value={value} onChange={(event) => setValue(event.nextRoot)} />;The handler may return undefined to accept synchronously, or return:
(event, { signal }) => ChangeDecision | PromiseLike<ChangeDecision>;An accepted decision does not update the viewer until the consumer supplies the new value. A structured rejection keeps the draft and reports an error at the origin path. Cancellation provides local lifecycle shutdown and an AbortSignal, but does not roll back remote side effects. There is no timeout, automatic retry, queue, or optimistic update.
Value adapters
Use forClass for class instances and when for values identified by a consumer type guard.
type Point = readonly [number, number];
const pointAdapter = valueAdapter("point")
.when(
(value): value is Point =>
Array.isArray(value) &&
value.length === 2 &&
value.every((item) => typeof item === "number"),
)
.scalar((point) => ({
kind: "point",
typeLabel: "Point",
summary: "(" + point[0] + ", " + point[1] + ")",
tone: "number",
}))
.build();
<Inspecta value={[37.5665, 126.978]} adapters={[pointAdapter]} />;Adapter rules:
- Consumer adapters are evaluated in registration order before built-ins.
- Matching, presentation, children, renderer, copy, codec, and replacement phases are synchronous and deterministic.
- A thrown or Promise-like result fails closed for that occurrence; it does not fall through to another adapter.
- Collection child tokens identify Document Path segments; labels are display text.
- Descendant editing requires immutable
replacesupport from every ancestor child.
Copy and redaction
strict-jsonsucceeds only when the selected subtree preserves JSON meaning. It does not automatically convertBigInt,Map,Set, symbols, special numbers, binary values, or References.readableproduces a deterministic string with type tags and Reference target paths.
An adapter can provide a strictJson projection and call hideRawSelection() to prevent raw values from being exposed through selection.
Selection and diagnostics
<Inspecta
value={documentValue}
onSelect={(event) => {
console.log(event.path, event.adapterId, event.presentation);
if (event.raw.available) console.log(event.raw.value);
}}
onAdapterDiagnostic={(diagnostic) => {
console.error(diagnostic.adapterId, diagnostic.phase, diagnostic.code);
}}
/>Diagnostics are deduplicated by snapshot, path, adapter, phase, and code. A throwing diagnostic callback does not take down the viewer.
Search and accessibility
useJsonSearch is a headless synchronous search hook. The consumer owns the query input and controls navigation with next and previous. Search uses an eager snapshot independent of virtualization.
The tree uses role="tree" and role="treeitem" with roving tabIndex. Arrow keys navigate visible nodes, Enter activates or follows references, Space selects, and F2 enters the action toolbar. Focus returns to the nearest surviving ancestor when the active node disappears.
Virtualization
Virtualization is opt-in:
<Inspecta value={value} virtualization={{ height: 480 }} />height must be a finite positive number. The viewer owns the fixed viewport and internal scroll. There are no public overscan, measurement, or work-budget options.
Public entry points
@cp949/inspecta-react— component, hooks, adapters, and public types@cp949/inspecta-react/index.css— package styles@cp949/inspecta-react/adapter-test— adapter contract test helper
License
MIT
