[{"name":"JsonRender","comment":" A native, fail-closed Elm renderer for [json-render](https://github.com/vercel-labs/json-render)\nmanifests, pinned to `@json-render/core` v0.19.0.\n\nThis top module is the convenience entry point: validate a manifest fail-closed, and get\nan error stub for the failure path. The renderer itself (a small TEA component:\n`Model` / `Msg` / `Effect` / `update` / `view`) lives in\n[`JsonRender.Render`](JsonRender-Render); the typed spec model in\n[`JsonRender.Spec`](JsonRender-Spec); the expression dialect in\n[`JsonRender.Expr`](JsonRender-Expr).\n\nTypical host wiring:\n\n    case JsonRender.decodeValue manifestJson of\n        Ok spec ->\n            -- mount the renderer, feeding host options + host-owned state\n            Html.map RendererMsg (JsonRender.Render.view options spec hostState rendererModel)\n\n        Err message ->\n            JsonRender.errorStub message\n\nDecoding is the security gate: an off-catalog component type, a malformed prop, a\ndangling child key, or a missing root all produce `Err`, never a partial tree.\n\n\n# Validate a manifest\n\n@docs decodeValue, decodeString\n\n\n# Failure path\n\n@docs errorStub\n\n","unions":[],"aliases":[],"values":[{"name":"decodeString","comment":" Validate a raw JSON string into a [`Spec`](JsonRender-Spec#Spec), fail-closed.\n","type":"String.String -> Result.Result String.String JsonRender.Spec.Spec"},{"name":"decodeValue","comment":" Validate an already-parsed JSON `Value` into a [`Spec`](JsonRender-Spec#Spec),\nfail-closed. `Err` carries a human-readable diagnostic.\n","type":"Json.Decode.Value -> Result.Result String.String JsonRender.Spec.Spec"},{"name":"errorStub","comment":" The fail-closed failure view: a self-contained error stub the host renders instead of\na manifest that did not validate. Never a partial tree.\n\nIt prints the decoder's diagnostic verbatim, which suits a developer host and does not suit an\nend user. A host with a real audience should classify the failure with\n[`Spec.errorKind`](JsonRender-Spec#errorKind) and write its own copy instead. This stays the right\ndefault for a host that has none.\n\n","type":"String.String -> Html.Html msg"}],"binops":[]},{"name":"JsonRender.Expr","comment":" The json-render expression / binding dialect, scoped to this package's\nsupported set and pinned to `@json-render/core` v0.19.0 (see [`SUPPORT.md`](https://github.com/Juli4nG/elm-json-render/blob/main/SUPPORT.md)).\n\nEvery dynamic value in a manifest is a plain JSON object carrying a single\n`$`-prefixed discriminant key. This module decodes the supported forms into a typed\n[`Expr`](#Expr) and resolves them against a host-owned state `Value` plus an optional\n`repeat` scope, using RFC 6901 JSON Pointers throughout.\n\n**Fail-closed:** an object carrying an unsupported `$`-directive (e.g. `$computed`)\n**fails the decode**. json-render's own runtime is fail-open here; we are not. The\nsupported set is exactly the v1 subset (which now includes `$cond`).\n\n\n# Expressions\n\n@docs Expr\n@docs Condition\n@docs SingleCondition\n@docs CondSource\n@docs Comparison\n@docs Comparand\n@docs decoder\n\n\n# Resolution scope\n\n@docs Context\n@docs rootContext\n@docs childContext\n\n\n# JSON Pointers\n\n@docs getByPath\n\n\n# Resolving\n\n@docs resolve\n@docs resolveDisplay\n@docs resolveBool\n@docs writeBackPath\n@docs resolveParams\n@docs validatedParams\n\n","unions":[{"name":"Comparand","comment":" The right-hand side of a comparison: either a literal JSON value or a\n`{ \"$state\": \"/ptr\" }` reference resolved against global state at eval time (core's\n`resolveComparisonValue`).\n","args":[],"cases":[["CLiteral",["Json.Decode.Value"]],["CStateRef",["String.String"]]]},{"name":"Comparison","comment":" The comparison operator. `CmpTruthy` is the no-operator case (JS `Boolean(value)`).\n`eq` / `neq` compare against any value by JSON scalar equality; `gt` / `gte` / `lt` /\n`lte` compare numbers only: a non-numeric literal operand is **rejected at decode**, and a\nnon-numeric source value yields `False` at eval time (per core).\n\nA **missing** path (an absent `$state` / `$item` / `$index` source or `{$state}` operand) is\nJS `undefined`, kept distinct from JSON `null`: it is falsy, `eq`-equal only to another\nmissing path (`undefined === undefined`; `undefined !== null`), and never orders.\n\n","args":[],"cases":[["CmpTruthy",[]],["CmpEq",["JsonRender.Expr.Comparand"]],["CmpNeq",["JsonRender.Expr.Comparand"]],["CmpGt",["JsonRender.Expr.Comparand"]],["CmpGte",["JsonRender.Expr.Comparand"]],["CmpLt",["JsonRender.Expr.Comparand"]],["CmpLte",["JsonRender.Expr.Comparand"]]]},{"name":"CondSource","comment":" The left-hand side of a single condition: `$state` pointer, `$item` field, or the\nrepeat `$index`.\n","args":[],"cases":[["SrcState",["String.String"]],["SrcItem",["String.String"]],["SrcIndex",[]]]},{"name":"Condition","comment":" A json-render **condition** — the predicate of a `$cond`, and the same grammar as\ncore's `visible` / `hidden` (pinned to `@json-render/core` v0.19.0). Evaluated to a\n`Bool` by [`resolve`](#resolve) when picking a `$cond` branch:\n\n  - `CBool b`: a literal `true` / `false`.\n  - `CSingle c`: one `$state` / `$item` / `$index` comparison.\n  - `CEvery cs`: a bare JSON **array** of single conditions, an implicit AND (all true).\n  - `CAnd cs` / `COr cs`: explicit `{ \"$and\": [ … ] }` / `{ \"$or\": [ … ] }`, recursive.\n\nMalformed conditions (unknown keys, mixed sources, a non-`true` `not`, a non-numeric\noperand for `gt`/`gte`/`lt`/`lte`) **fail the decode**, mirroring the rest of this module.\n\n","args":[],"cases":[["CBool",["Basics.Bool"]],["CSingle",["JsonRender.Expr.SingleCondition"]],["CEvery",["List.List JsonRender.Expr.SingleCondition"]],["CAnd",["List.List JsonRender.Expr.Condition"]],["COr",["List.List JsonRender.Expr.Condition"]]]},{"name":"Expr","comment":" A decoded json-render expression. The discriminant maps 1:1 to the pinned\n`$`-forms:\n\n  - `ELiteral v`: any value with no `$`-directive (scalar, array, or plain object).\n  - `EState ptr`: `{ \"$state\": \"/ptr\" }`, read global state.\n  - `EItem field`: `{ \"$item\": \"field\" }`, read the current repeat item (field `\"\"` = whole item).\n  - `EIndex`: `{ \"$index\": true }`, the current repeat index.\n  - `EBindState ptr`: `{ \"$bindState\": \"/ptr\" }`, two-way bind to global state.\n  - `EBindItem field`: `{ \"$bindItem\": \"field\" }`, two-way bind to a repeat-item field.\n  - `ETemplate tmpl`: `{ \"$template\": \"…${/ptr}…${bare}…\" }`, string interpolation.\n  - `ECond cond t e`: `{ \"$cond\": <condition>, \"$then\": <expr>, \"$else\": <expr> }`,\n    picks `t` when `cond` holds, else `e`. Both branches are themselves expressions\n    (recursive). A two-way `$bindState` / `$bindItem` inside the selected branch keeps its\n    write-back: [`writeBackPath`](#writeBackPath) targets the branch chosen at render time.\n    See [`Condition`](#Condition).\n\n","args":[],"cases":[["ELiteral",["Json.Decode.Value"]],["EState",["String.String"]],["EItem",["String.String"]],["EIndex",[]],["EBindState",["String.String"]],["EBindItem",["String.String"]],["ETemplate",["String.String"]],["ECond",["JsonRender.Expr.Condition","JsonRender.Expr.Expr","JsonRender.Expr.Expr"]]]}],"aliases":[{"name":"Context","comment":" The resolution scope handed to every expression.\n\n  - `state`: the host-owned global state `Value` (the single source of truth).\n  - `item`: the current `repeat` item, if inside a repeat scope.\n  - `index`: the current `repeat` index, if inside a repeat scope.\n  - `basePath`: the repeat item's absolute JSON Pointer (`statePath ++ \"/\" ++ index`),\n    used to compute `$bindItem` / top-level `$item` write-back paths.\n  - `allowedOrigins`: the host-provided iframe origin allowlist. The `Iframe` element only\n    renders when a resolved `src`'s origin is an exact member of this list (fail-closed).\n\n","args":[],"type":"{ state : Json.Decode.Value, item : Maybe.Maybe Json.Decode.Value, index : Maybe.Maybe Basics.Int, basePath : Maybe.Maybe String.String, allowedOrigins : List.List String.String }"},{"name":"SingleCondition","comment":" One comparison against a single source value. `negate` is core's `\"not\": true`\nmodifier, which inverts whatever the operator (or the bare truthiness test) yields.\n","args":[],"type":"{ source : JsonRender.Expr.CondSource, comparison : JsonRender.Expr.Comparison, negate : Basics.Bool }"}],"values":[{"name":"childContext","comment":" Derive a per-item scope for a `repeat` element. `statePath` is the pointer to the\nstate array; `index` and `item` identify the current row. `basePath` becomes\n`statePath ++ \"/\" ++ index` per the pinned reference.\n","type":"String.String -> Basics.Int -> Json.Decode.Value -> JsonRender.Expr.Context -> JsonRender.Expr.Context"},{"name":"decoder","comment":" Decode a prop value into an [`Expr`](#Expr).\n\nA non-object (string / number / bool / array / null) decodes to `ELiteral`. An object\nwith no `$`-prefixed key decodes to `ELiteral`. An object with exactly one supported\n`$`-directive decodes to that form. An object with an **unsupported** `$`-directive, or\nmore than one `$`-directive, **fails**. This is the fail-closed boundary.\n\n","type":"Json.Decode.Decoder JsonRender.Expr.Expr"},{"name":"getByPath","comment":" Resolve an RFC 6901 JSON Pointer against a `Value`. Returns `Nothing` if any token\nis missing. The empty pointer `\"\"` returns the whole value. Tokens are unescaped\n(`~1` → `/`, `~0` → `~`). A leading `/` is honored; a bare token (no leading `/`, as\nproduced by `$item` field lookups) is tolerated as a single step.\n","type":"String.String -> Json.Decode.Value -> Maybe.Maybe Json.Decode.Value"},{"name":"resolve","comment":" Resolve an expression to a JSON `Value`. A missing pointer / item field yields\n`null`. Two-way `$bindState` / `$bindItem` read exactly like their read-only siblings;\ntheir write-back path is exposed separately via [`writeBackPath`](#writeBackPath).\n","type":"JsonRender.Expr.Context -> JsonRender.Expr.Expr -> Json.Decode.Value"},{"name":"resolveBool","comment":" Resolve an expression to a boolean (for `Checkbox` `checked`). Only a JSON `true`\nis `True`; everything else (including missing) is `False`.\n","type":"JsonRender.Expr.Context -> JsonRender.Expr.Expr -> Basics.Bool"},{"name":"resolveDisplay","comment":" Resolve an expression to its display string (for `Text` / `Badge`). Scalars render\nas themselves; `null` / missing renders as `\"\"`; objects / arrays render as `\"\"`.\n","type":"JsonRender.Expr.Context -> JsonRender.Expr.Expr -> String.String"},{"name":"resolveParams","comment":" Resolve an action's `params` object per the pinned `resolveActionParam` semantics:\n\n  - A **top-level** `{ \"$item\": \"field\" }` resolves to the absolute state **path**\n    (`basePath ++ \"/\" ++ field`) so the host can target the row.\n  - A **top-level** `{ \"$index\": true }` resolves to the index **number**.\n  - Everything else (including `$item` **nested in an array**) resolves to the **value**\n    by deep recursion.\n\nSo `{ \"ids\": [ { \"$item\": \"id\" } ] }` yields `{ \"ids\": [\"<id>\"] }`: the literal id, never\na path.\n\n","type":"JsonRender.Expr.Context -> Json.Decode.Value -> Json.Decode.Value"},{"name":"rootContext","comment":" The top-level scope: global state only, no repeat item. `allowedOrigins` is the\nhost-provided iframe origin allowlist, threaded to every expression scope.\n","type":"List.List String.String -> Json.Decode.Value -> JsonRender.Expr.Context"},{"name":"validatedParams","comment":" A `Decoder` for an action's `params` that **validates fail-closed at decode time**:\nit returns the params `Value` unchanged, but fails if any nested object carrying a\n`$`-directive is not a supported, well-formed [`Expr`](#Expr) (e.g. an unsupported\n`$computed`, or a malformed `{ \"$item\": 123 }`). This closes the gap where params are stored\nraw and only resolved at dispatch: a bad directive is rejected with the rest of the\nmanifest, never emitted to the host.\n","type":"Json.Decode.Decoder Json.Decode.Value"},{"name":"writeBackPath","comment":" The absolute JSON Pointer a two-way binding writes back to, or `Nothing` for\nread-only expressions. `$bindState` writes its own pointer; `$bindItem` writes\n`basePath ++ \"/\" ++ field`. A `$cond` passes through to the branch it selects at render\ntime (see [`Expr`](#Expr)), so a `$bindState` / `$bindItem` inside the chosen branch keeps\nits write-back handler.\n","type":"JsonRender.Expr.Context -> JsonRender.Expr.Expr -> Maybe.Maybe String.String"}],"binops":[]},{"name":"JsonRender.Render","comment":" Render a validated [`Spec`](JsonRender-Spec#Spec) to `Html`, driven by host-owned\nstate, with actions flowing back out as [`Effect`](#Effect) values.\n\nThis is a small TEA component. The host owns the json-render `state` and passes it to\n[`view`](#view) on every frame; the renderer reads it but never mutates it behind the\nhost's back. The only local state the renderer owns is the pending confirm dialog. User\nactions (button presses, checkbox toggles) surface to the host as `Effect`s the host\napplies, so the host stays the single source of truth.\n\nBecause the output is Elm `Html` (no `innerHTML`, no script escape hatch), the rendered\ntree is **XSS-safe by construction**.\n\n\n# State\n\n@docs Model, init\n\n\n# Messages\n\n@docs Msg, update\n\n\n# Effects (out to the host)\n\n@docs Effect\n\n\n# View\n\n@docs Options, CountPillDefaults, defaultOptions, view\n\n\n# Styling hooks\n\n@docs badgeTone\n\n","unions":[{"name":"Effect","comment":" What the renderer asks the host to do. The renderer never performs the side effect\nitself.\n\n  - `EmitAction { verb, params }`: a wired `on:press` fired (and any `confirm` was\n    accepted). `params` are already expression-resolved. The host re-checks the verb\n    against its allowlist and performs the side effect.\n  - `EmitStateChange { path, value }`: a two-way input (`$bindState` / `$bindItem`\n    checkbox) was toggled. The host treats this as the source of truth, writes it at the\n    given absolute JSON Pointer, and re-projects state.\n\n","args":[],"cases":[["EmitAction",["{ verb : String.String, params : Json.Decode.Value }"]],["EmitStateChange",["{ path : String.String, value : Json.Decode.Value }"]]]},{"name":"Model","comment":" Renderer-local UI state. Holds only the pending confirm dialog (if any); the\nhost owns everything else.\n","args":[],"cases":[]},{"name":"Msg","comment":" Renderer messages. Opaque to the host: the host maps `Html Msg` into its own message\ntype and feeds `Msg`s back through [`update`](#update), acting on the returned\n[`Effect`](#Effect).\n","args":[],"cases":[]}],"aliases":[{"name":"CountPillDefaults","comment":" The words a `CountPills` element uses when its manifest does not name its own.\n\nThe renderer counts rows and groups them; it has no opinion about what a row is. A host does:\nit knows what one row is called, which field groups them, and which group belongs at the front. A\nmanifest published before `CountPills` could carry those keys cannot say so itself, so the host\nthat mounts the renderer supplies them and its already-published manifests keep reading correctly.\n\n  - `groupBy` — the record field to group rows on.\n  - `groupOrder` — group values in display order. Anything outside the list sorts after the\n    listed ones by descending count then name, which is what an empty list does to everything.\n    Matching is case-insensitive, and a value listed more than once takes its FIRST position.\n  - `itemNoun` / `itemNounPlural` — what one row and many rows are called.\n\n","args":[],"type":"{ groupBy : String.String, groupOrder : List.List String.String, itemNoun : String.String, itemNounPlural : String.String }"},{"name":"Options","comment":" What the host tells the renderer about itself.\n\n  - `allowedIframeOrigins` — the iframe origin allowlist: an `Iframe` element renders only when\n    its resolved `src` is an https URL whose origin is an exact member of this list. An empty\n    list disables all iframes (fail-closed).\n  - `hostName` — what the iframe provenance bar calls you, as in \"not verified by `<hostName>`\".\n    The bar is trust chrome, so the name has to be the one a reader recognizes; a generic\n    \"the host application\" is a weaker claim than naming the app they are looking at. A manifest\n    cannot reach this, which is the point.\n  - `countPills` — the vocabulary a `CountPills` element falls back to.\n\n","args":[],"type":"{ allowedIframeOrigins : List.List String.String, hostName : String.String, countPills : JsonRender.Render.CountPillDefaults }"}],"values":[{"name":"badgeTone","comment":" Maps common run-state strings to a tone class; unrecognized values fall back to neutral.\nKeyed on the leading whitespace-delimited token so a value carrying a trailing detail suffix\n(e.g. `\"running · 0:15\"`, a counting-up elapsed) still maps to its state tone.\n\nThe arms are that closed run-state vocabulary and nothing else. A publisher whose own vocabulary\nhas a word this table does not know gets the neutral fall-through, and supplies a `variant` if it\nwants a specific tone — which is the mechanism that exists for exactly this, and is why no\nextension's private verb needs an arm of its own here.\n\n","type":"String.String -> String.String"},{"name":"defaultOptions","comment":" Options for a host that has no vocabulary of its own: no iframes, an unnamed host, no group\norder (so counts fall back to descending), and rows that are simply \"items\".\n","type":"JsonRender.Render.Options"},{"name":"init","comment":" The initial (empty) renderer state: no dialog open.\n","type":"JsonRender.Render.Model"},{"name":"update","comment":" Advance renderer-local state in response to a `Msg`, optionally yielding an `Effect`\nfor the host to apply.\n\n`confirm` is honored here: a press carrying a confirm opens the dialog and emits nothing;\nthe `EmitAction` only fires once the user accepts.\n\n","type":"JsonRender.Render.Msg -> JsonRender.Render.Model -> ( JsonRender.Render.Model, Maybe.Maybe JsonRender.Render.Effect )"},{"name":"view","comment":" Render the spec against the current host-owned `state`. The returned `Html Msg`\nincludes the confirm dialog overlay when one is pending.\n","type":"JsonRender.Render.Options -> JsonRender.Spec.Spec -> Json.Decode.Value -> JsonRender.Render.Model -> Html.Html JsonRender.Render.Msg"}],"binops":[]},{"name":"JsonRender.Spec","comment":" The json-render flat `Spec` model and its fail-closed decoder, scoped to this\npackage's component set and pinned to `@json-render/core` v0.19.0.\n\nA `Spec` is the canonical flat form: `{ root, elements, state }` where `elements` is a\nmap keyed by id and children are referenced by string key (never nested inline).\n\n**Fail-closed.** The decoder rejects, rather than silently dropping:\n\n  - an **unknown / off-catalog** component `type` (json-render's renderer is fail-open\n    here; we are not);\n  - props that don't match the strict per-component shape;\n  - a **dangling** child key, a **missing** root, or a `repeat` element with no children\n    (the structural floor `validateSpec` enforces).\n\nA rejected manifest never produces a partial tree: the host shows an error stub.\n\n\n# Spec\n\n@docs Spec\n@docs decoder\n\n\n# Why a decode failed\n\n@docs ErrorKind\n@docs errorKind\n\n\n# Elements\n\n@docs UIElement\n@docs ComponentType\n@docs componentName\n\n\n# Props\n\n@docs Props\n@docs Direction\n@docs CardProps\n@docs StackProps\n@docs TextProps\n@docs BadgeProps\n@docs ButtonProps\n@docs CheckboxProps\n@docs CountPillsProps\n@docs IframeProps\n@docs TableProps\n@docs Column\n@docs AlertProps\n@docs DisclosureProps\n@docs Tone\n\n\n# Actions & iteration\n\n@docs ActionBinding\n@docs Confirm\n@docs Repeat\n\n","unions":[{"name":"ComponentType","comment":" The allowlisted component types. Anything else fails the decode.\n","args":[],"cases":[["Card",[]],["Stack",[]],["Text",[]],["Badge",[]],["Button",[]],["Checkbox",[]],["CountPills",[]],["Iframe",[]],["Table",[]],["Alert",[]],["Disclosure",[]]]},{"name":"Direction","comment":" `Stack` layout direction.\n","args":[],"cases":[["Row",[]],["Col",[]]]},{"name":"ErrorKind","comment":" Why a manifest failed the fail-closed decode, as far as the decoder's own diagnostics can\ntell. A host shows its audience one sentence, not a decoder dump, and this is what decides which\nsentence:\n\n  - `UnknownCatalogSurface` — the manifest named a component type, an icon, or any other key this\n    catalog does not have. \"Any other key\" is the whole `rejectUnknownKeys` surface, not just\n    props: an unsupported element key (`visible`, `watch`), action-binding key (`onSuccess`),\n    confirm key, `repeat` key, or `Table` column key all land here. The publisher is describing an\n    interface some newer renderer would understand, so the honest reading is version skew, not a\n    broken manifest.\n  - `Malformed` — everything else: a missing required field, a wrong-shaped prop, a body that is\n    not even JSON. Nothing here suggests a newer renderer would fare better.\n\nThis lives beside the `Decode.fail` arms it classifies, and reads the very strings those arms are\nbuilt from, so the two cannot drift apart: rewording a diagnostic moves the marker with it.\n\n","args":[],"cases":[["UnknownCatalogSurface",[]],["Malformed",[]]]},{"name":"Props","comment":" Strictly-decoded props, one variant per component type. The variant always agrees\nwith the element's [`ComponentType`](#ComponentType) (both come from a single read of\nthe `type` field).\n","args":[],"cases":[["CardP",["JsonRender.Spec.CardProps"]],["StackP",["JsonRender.Spec.StackProps"]],["TextP",["JsonRender.Spec.TextProps"]],["BadgeP",["JsonRender.Spec.BadgeProps"]],["ButtonP",["JsonRender.Spec.ButtonProps"]],["CheckboxP",["JsonRender.Spec.CheckboxProps"]],["CountPillsP",["JsonRender.Spec.CountPillsProps"]],["IframeP",["JsonRender.Spec.IframeProps"]],["TableP",["JsonRender.Spec.TableProps"]],["AlertP",["JsonRender.Spec.AlertProps"]],["DisclosureP",["JsonRender.Spec.DisclosureProps"]]]},{"name":"Tone","comment":" `Alert` severity tone, driving the `jr-alert--<tone>` modifier class. Any other\nstring fails the decode (fail-closed).\n","args":[],"cases":[["Info",[]],["Warning",[]],["Danger",[]]]}],"aliases":[{"name":"ActionBinding","comment":" An event binding: a named verb, its (unresolved) params, and an optional confirm\ndialog. The key is `action`/`params` per the pinned format, never a URL.\n","args":[],"type":"{ action : String.String, params : Json.Decode.Value, confirm : Maybe.Maybe JsonRender.Spec.Confirm }"},{"name":"AlertProps","comment":" `Alert` props: a `tone`, an optional `title` expression, and the required `message`\nexpression (which may itself be a `$template` or other binding).\n","args":[],"type":"{ tone : JsonRender.Spec.Tone, title : Maybe.Maybe JsonRender.Expr.Expr, message : JsonRender.Expr.Expr }"},{"name":"BadgeProps","comment":" `Badge` props: the `value` expression (e.g. a status string mapped to a tone) and an\noptional `variant` expression. When `variant` is present its resolved string becomes the\nbadge's `data-state` attribute (so a manifest can key styling on a stable token while showing\ndifferent display text); when absent, `data-state` falls back to the resolved `value` text.\n","args":[],"type":"{ value : JsonRender.Expr.Expr, variant : Maybe.Maybe JsonRender.Expr.Expr }"},{"name":"ButtonProps","comment":" `Button` props: the `label` expression, an optional `icon` name, and an optional `disabled`\nexpression. When `disabled` resolves truthy the button renders inert (the native `disabled`\nattribute, a `jr-button--disabled` class, and no press handler at all); absent means enabled, the\nhistorical behavior.\n\n`icon` is a **name from a closed set** (`\"trash\"`, `\"close\"`, `\"external\"`, `\"refresh\"`), never\nmarkup and never an expression: the renderer draws the glyph itself, so a manifest can ask for one\nof a handful of shapes and cannot inject an image. Any other name is refused at decode time, like\nevery other off-catalog value. When the resolved `label` is non-empty it stays visible next to the\nglyph; an empty label with an `icon` renders the button icon-only, with the renderer supplying the\naccessible name.\n\n","args":[],"type":"{ label : JsonRender.Expr.Expr, icon : Maybe.Maybe String.String, disabled : Maybe.Maybe JsonRender.Expr.Expr }"},{"name":"CardProps","comment":" `Card` props. `title` is optional and may be any expression.\n","args":[],"type":"{ title : Maybe.Maybe JsonRender.Expr.Expr }"},{"name":"CheckboxProps","comment":" `Checkbox` props: an optional `label` and an optional `checked` binding (typically a\ntwo-way `$bindState` / `$bindItem`).\n","args":[],"type":"{ label : Maybe.Maybe JsonRender.Expr.Expr, checked : Maybe.Maybe JsonRender.Expr.Expr }"},{"name":"Column","comment":" One `Table` column: the row-object `key` to read and the header `label` to show.\n","args":[],"type":"{ key : String.String, label : String.String }"},{"name":"Confirm","comment":" A confirm dialog shown by the renderer before an action emits. `title` and `message`\nmay be expressions (e.g. a `$template` interpolating a row field).\n","args":[],"type":"{ title : JsonRender.Expr.Expr, message : JsonRender.Expr.Expr, confirmLabel : Maybe.Maybe String.String, cancelLabel : Maybe.Maybe String.String, variant : String.String }"},{"name":"CountPillsProps","comment":" `CountPills` props: the `bind` expression pointing at an array of records, and the vocabulary\nfor counting them.\n\nEverything but `bind` is optional. `groupBy`, `groupOrder`, `itemNoun` and `itemNounPlural` fall\nback to the host's [`Render.CountPillDefaults`](JsonRender-Render#CountPillDefaults) when absent;\n`emptyLabel` is the exception and has no host default. That is deliberate: the words a publisher\ncounts in — what a row is called, which field groups them, which group leads — are the\npublisher's, not the renderer's, and a manifest written before these keys existed must keep\nrendering in them.\n\nOptional does not mean lenient. A key that is PRESENT but malformed (a null `groupBy`, a\n`groupOrder` carrying a non-string member, a `$computed` in `emptyLabel`) rejects the whole\nmanifest rather than decoding as absent.\n\n  - `groupBy` — the record field to group on.\n  - `groupOrder` — the group values, in the order they should be shown. Groups outside the list\n    sort after the listed ones, by descending count and then name, which is also what an empty\n    order does to everything. Matching is case-insensitive, and a value listed more than once\n    takes its FIRST position.\n  - `itemNoun` / `itemNounPlural` — what one row and many rows are called, for the total.\n  - `emptyLabel` — what a manifest says definitively about an empty table. Alone among these\n    keys it has NO host default: absent, the renderer synthesizes \"No <plural> yet\" from whichever\n    plural noun is in play. That fallback is right for a table that has nothing bound yet and\n    wrong for a completed, genuinely empty one; only the publisher knows which it is, so it\n    supplies the string. Resolving to an empty string means no empty-state node at all.\n\n","args":[],"type":"{ bind : JsonRender.Expr.Expr, groupBy : Maybe.Maybe String.String, groupOrder : Maybe.Maybe (List.List String.String), itemNoun : Maybe.Maybe String.String, itemNounPlural : Maybe.Maybe String.String, emptyLabel : Maybe.Maybe JsonRender.Expr.Expr }"},{"name":"DisclosureProps","comment":" `Disclosure` props: a `label` expression shown in the always-visible summary, and an\noptional `open` literal bool (default `False`) that sets the initial expanded state. Children\nrender inside the disclosure body.\n","args":[],"type":"{ label : JsonRender.Expr.Expr, open : Basics.Bool }"},{"name":"IframeProps","comment":" `Iframe` props: the `src` URL expression and a `title` expression. Both are required.\nThe renderer only emits an `<iframe>` when the resolved `src` is an https URL whose origin\nis in the host-provided allowlist; otherwise it shows a benign placeholder (fail-closed).\n","args":[],"type":"{ src : JsonRender.Expr.Expr, title : JsonRender.Expr.Expr }"},{"name":"Repeat","comment":" Element-level iteration: `statePath` points at a state array, `key` is the optional\nstable-list item field.\n","args":[],"type":"{ statePath : String.String, key : Maybe.Maybe String.String }"},{"name":"Spec","comment":" A flat json-render spec: a root element key, a keyed map of elements, and the\nhost-owned initial state.\n","args":[],"type":"{ root : String.String, elements : Dict.Dict String.String JsonRender.Spec.UIElement, state : Json.Decode.Value }"},{"name":"StackProps","comment":" `Stack` props: a layout `direction` and a numeric `gap`.\n","args":[],"type":"{ direction : JsonRender.Spec.Direction, gap : Basics.Int }"},{"name":"TableProps","comment":" `Table` props: the `columns` to show (each a `key`/`label` pair) and a `bind`\nexpression resolving to an array of row objects. Each cell reads `row[column.key]`; a\nmissing key renders an empty cell.\n","args":[],"type":"{ columns : List.List JsonRender.Spec.Column, bind : JsonRender.Expr.Expr }"},{"name":"TextProps","comment":" `Text` props: the displayed `value` expression.\n","args":[],"type":"{ value : JsonRender.Expr.Expr }"},{"name":"UIElement","comment":" One element: its (validated) component type, its strictly-decoded props, its child\nkeys, its event bindings, and an optional `repeat`. Mirrors json-render's `UIElement`\nshape (`type/props/children/on/repeat`), minus the `visible`/`watch` siblings this\nrenderer does not support.\n","args":[],"type":"{ componentType : JsonRender.Spec.ComponentType, props : JsonRender.Spec.Props, children : List.List String.String, on : Dict.Dict String.String (List.List JsonRender.Spec.ActionBinding), repeat : Maybe.Maybe JsonRender.Spec.Repeat }"}],"values":[{"name":"componentName","comment":" The component type's wire name, for diagnostics.\n","type":"JsonRender.Spec.ComponentType -> String.String"},{"name":"decoder","comment":" Decode a flat json-render `Spec`, fail-closed. See the module doc for what is\nrejected. On any structural or off-catalog error the decoder fails with a diagnostic\nmessage; the host renders an error stub rather than a partial tree.\n","type":"Json.Decode.Decoder JsonRender.Spec.Spec"},{"name":"errorKind","comment":" Classify a decode failure message (the `Err` from [`JsonRender.decodeString`](JsonRender#decodeString),\nwhich is `Decode.errorToString` output and so embeds the failing arm's own text).\n\nSubstring matching is the only tool available — Elm decode errors are strings by the time they\nreach a host. `Decode.errorToString` also prints offending JSON, so a manifest containing one of\nthese markers verbatim as data would be read as skew. That mis-picks which reassuring sentence the\nreader sees and nothing else: both kinds refuse to render, which is the part that matters.\n\n","type":"String.String -> JsonRender.Spec.ErrorKind"}],"binops":[]}]