import { z } from "zod";
import type { BlockMdxConfig } from "../types.js";
/**
* Pure (React-free) part of the PLAN-SPECIFIC `json-explorer` block: its data
* schema and MDX round-trip config. Shared by the server MDX adapter
* (`plan-mdx.ts` via `plan-block-registry.ts`) and the client spec
* (`planBlocks.tsx`). Keeping this React-free means importing it into a server
* module never pulls React into the Nitro/SSR bundle.
*
* The block renders a browser-devtools / Postman-style collapsible JSON tree:
* object/array nodes show a chevron + a one-line summary ("{…} 3 keys" /
* "[…] 5 items") and expand/collapse; leaf values are type-colored (string =
* green, number = blue, boolean = violet, null = muted). The raw JSON TEXT is
* the source of truth (`json`), so authoring round-trips losslessly even when
* the JSON is invalid — the reader parses defensively and falls back to the raw
* text on a parse error rather than throwing.
*
* The schema MUST stay data-compatible with the inline `json-explorer` member of
* `planBlockSchema` (`plan-content.ts`), and the MDX `tag` (`Json`) +
* attribute shape MUST match the `` encoding so
* stored `.mdx` round-trips. `json` lives in the `json` ATTRIBUTE (not MDX
* children) — the shared `prop()` encoder round-trips multiline strings cleanly,
* and keeping it an attribute avoids the payload being reflowed as prose.
*/
export const JSON_EXPLORER_DEFAULT_COLLAPSED_DEPTH = 2;
export const JSON_EXPLORER_MAX_COLLAPSED_DEPTH = 20;
export interface JsonExplorerData {
/** Optional heading shown above the tree. */
title?: string;
/** Raw JSON text — the source of truth, parsed defensively at render time. */
json: string;
/**
* Depth beyond which nodes start collapsed (default 2). Nodes at a depth `<`
* this value render expanded; deeper nodes render collapsed until clicked.
*/
collapsedDepth?: number;
}
export const jsonExplorerSchema = z.object({
title: z.string().trim().max(200).optional(),
json: z.string().max(200_000),
collapsedDepth: z
.number()
.int()
.min(0)
.max(JSON_EXPLORER_MAX_COLLAPSED_DEPTH)
.optional(),
}) as unknown as z.ZodType;
/**
* MDX config: `title`, `json`, and `collapsedDepth` are flat attributes — the
* `` self-closing form. Insertion order
* of `toAttrs` is the on-disk attribute order (`title` → `json` →
* `collapsedDepth`). `fromAttrs` mirrors a forgiving parse (`json ?? ""`,
* `title`/`collapsedDepth` undefined when absent) so a plan missing an attribute
* still parses.
*/
export const jsonExplorerMdx: BlockMdxConfig = {
tag: "Json",
toAttrs: (data) => ({
title: data.title,
json: data.json,
collapsedDepth: data.collapsedDepth,
}),
fromAttrs: (attrs) => ({
json: attrs.string("json") ?? "",
title: attrs.string("title"),
collapsedDepth: attrs.number("collapsedDepth"),
}),
};