Skip to content

Releases: shteynu/ngx-json-render

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 12:24

Two new entry points: build an MCP App in Angular from npm, and load a catalog on a server without Angular.

ngx-json-render/mcp is the Angular side of an MCP App, a tool result that Claude, ChatGPT or VS Code renders as an inline view. injectJsonRenderApp() connects to the host and keeps the spec the model passed to the tool in a signal. Until now it lived only in this repository's example.

readonly mcp = injectJsonRenderApp({ name: 'my-app', version: '1.0.0' });
// <json-render [spec]="mcp.spec()" [loading]="mcp.loading()" [registry]="registry" />

The spec renders while the model is still writing the tool call. mcp.sendMessage(text, data) posts a user message to the chat, so a button can continue the conversation. mcp.callServerTool(name, args) replaces the spec with another tool's result. See Inside an MCP App host.

ngx-json-render/schema holds the spec grammar with nothing but @json-render/core behind it. A catalog.ts that imports schema from it can be loaded by a plain Node server, tsx or an edge function, with no import '@angular/compiler' workaround.

// catalog.ts
import { schema } from 'ngx-json-render/schema';

What changes for your app

  • Nothing breaks. ngx-json-render still exports the same schema object, so existing catalogs keep working.
  • @modelcontextprotocol/ext-apps and @modelcontextprotocol/sdk are new optional peers. Install them only if you import ngx-json-render/mcp; ng add does not add them.

Upgrading

npm i ngx-json-render@0.8.0 ngx-json-render-material@0.3.10

Install both: the catalog's peer range moves to ^0.8.0, and npm resolves a mismatched pair by quietly picking an older renderer.

Full Changelog: v0.7.8...v0.8.0

v0.7.8

Choose a tag to compare

@github-actions github-actions released this 08 Oct 09:51

A spec that arrives again, or a form full of bound fields, now costs only what actually changed.

Two cases still made the renderer redo work across the whole tree. A spec handed over as all-new objects (a whole-spec part, a host that fetches the spec again, buildSpecFromParts replaying a message) made every element resolve its props, and every container re-render. And every element with a $bindState or $bindItem prop resolved and re-rendered on every state write, whatever path the write touched. On a tree of 1,001 elements, an identical spec now resolves nothing instead of 1,000 elements, and one keystroke in a 500-field form re-renders one field instead of 500.

What changes for your app

  • Spec elements compare by content. An element whose content did not change keeps its resolved props and its template does not re-run, however the spec object was built. Streamed patches already shared unchanged elements and behave as before.
  • spec.state no longer overwrites the user's edits when the same spec arrives again. In uncontrolled mode, only leaves whose content changed are written to the store. Before, arrays compared by reference, so re-sending an unchanged spec wrote every array back and discarded edits made inside it, such as text typed into a field bound with $bindItem. A spec whose state really changed still writes the new value.
  • Two-way bound elements follow only their own paths. They resolve when a path they read is written, like any other element. A write that touches the bound path (the path itself, a container above it, or a path inside it) still always reaches the component, so the README's input pattern keeps putting the DOM right when the value returns to what it was.

Upgrading

npm i ngx-json-render@0.7.8 — no API changes, and ngx-json-render-material@^0.3.8 works with it unchanged. If an app relied on re-sending the same spec to reset its state, write that state explicitly instead, for example with a setState action.

Full Changelog: v0.7.7...v0.7.8

material-v0.3.9

Choose a tag to compare

@github-actions github-actions released this 08 Oct 10:18

IconButton shows its label as a tooltip. An icon-only button already used label as its accessible name, but sighted mouse users couldn't see it, so a generated toolbar of icons was guesswork. The label now also appears as a Material tooltip on hover and focus. The accessible name is unchanged, and screen readers still read the label once: the CDK adds no aria-describedby when the tooltip repeats the aria-label. The catalog's description of IconButton now mentions the tooltip.

Thanks to @vns-agm for this one (#4), the catalog's first outside contribution. A disabled IconButton doesn't show the tooltip yet. That is #5, open as a good first issue.

Upgrading

npm i ngx-json-render-material@^0.3.9

No code changes are needed. It works with ngx-json-render 0.7.x.

Full Changelog: material-v0.3.8...material-v0.3.9

material-v0.3.10

Choose a tag to compare

@github-actions github-actions released this 08 Oct 12:30

The catalog now loads on a server without Angular, and a disabled IconButton shows its tooltip.

  • ngx-json-render-material/catalog: the catalog without the components. A Node server can describe the vocabulary to a model with materialCatalog.prompt() and check what comes back with materialCatalog.validate(spec), without loading Angular. The MCP App example's server now imports it this way.
  • A disabled IconButton keeps its tooltip. It now renders with Material's disabledInteractive, so it stays hoverable and focusable, shows the disabled styling and gets aria-disabled="true". A click on it emits nothing.

Thanks to @knownshah for the IconButton fix (#6), the catalog's second outside contribution. Two more good first issues are open: #7 does the same for the plain Button, and #8 is a small check in the MCP App server.

import { materialCatalog } from 'ngx-json-render-material/catalog';

Upgrading

npm i ngx-json-render@0.8.0 ngx-json-render-material@0.3.10

Requires ngx-json-render@^0.8.0, so install the pair together. No code changes are needed. Full notes: v0.8.0.

Full Changelog: material-v0.3.9...material-v0.3.10

material-v0.3.8

Choose a tag to compare

@github-actions github-actions released this 07 Oct 13:32

Two layout fixes found by running the catalog inside ChatGPT. Both changed what a generated UI looked like without anything in the spec being wrong.

  • Text keeps line breaks. A \n in content now starts a new line (white-space: pre-line); before, the browser folded it into a space, so a poem, an address or a short list arrived as one run-on line. Runs of spaces still collapse as before. The catalog's description of Text now tells the model that \n starts a new line.
  • Card without actions has no empty footer. The actions row is rendered only when the element's actions slot has children. Material gives that row a minimum height, so every card without buttons ended in a blank strip about 50 px tall.

Upgrading

npm i ngx-json-render-material@^0.3.8

No code changes are needed. Two things to check: Text content that contains \n now renders on several lines, and a Card with no actions slot no longer has a mat-card-actions element, which matters only if your styles or tests look for it. It works with ngx-json-render 0.7.x.

Full Changelog: material-v0.3.7...material-v0.3.8

v0.7.7

Choose a tag to compare

@github-actions github-actions released this 06 Oct 12:59

Type a component's props from its catalog entry. ngx-json-render now re-exports core's InferComponentProps, InferCatalogComponents and InferActionParams, so the Zod schema in the catalog is the one place a component's props are declared.

import type { InferComponentProps } from 'ngx-json-render';
import type { catalog } from './catalog';

export class Card {
  readonly ctx = injectRenderContext<InferComponentProps<typeof catalog, 'Card'>>();
}

A field with a Zod .default() comes out required in the inferred type, but the renderer applies no defaults: the component still gets undefined when the spec leaves the field out. Wrap the type in Partial<…> or handle the missing value.

Upgrading

npm i ngx-json-render@^0.7.7

Types only, no runtime change. ngx-json-render-material@0.3.7 works with it.

Full Changelog: v0.7.6...v0.7.7

v0.7.6

Choose a tag to compare

@github-actions github-actions released this 06 Oct 12:38

State keys that contain / or ~ now stay one key. JSON Pointer writes such a key as ~1 or ~0, and two places in the renderer did not: a key like "a/b" was split into a nested { "a": { "b": … } }, and validation could not find a field whose path used the escape.

What changes for your app

  • Seeding keeps the key you wrote. State from the state input or spec.state is written into the store key by key. A key containing / or ~ used to land at the wrong place, so { "links": { "a/b": "x" } } became { "links": { "a": { "b": "x" } } }, and { "$state": "/links/a~1b" } read nothing. It now stays "a/b".
  • createStoreSetState writes to the same key. It had the same split when it diffed a whole-state update into path writes.
  • Validation finds escaped paths. A field bound to /links/a~1b is now checked against the value at "a/b", the same way the state store reads it. Before, it was always checked against undefined, so required failed on a filled field.

State whose keys contain neither character behaves exactly as before.

Upgrading

npm i ngx-json-render@^0.7.6

No code changes are needed, and ngx-json-render-material@0.3.6 works with it as it is.

Full Changelog: v0.7.5...v0.7.6

v0.7.5

Choose a tag to compare

@github-actions github-actions released this 06 Oct 11:12

Two fixes where an action or a watcher silently never happened. Neither changes the API; both change what your app sees in a case that used to fail quietly.

What changes for your app

  • A second confirm no longer strands the first. When an action with a confirm was dispatched while another confirmation was still open (from a watch, or a programmatic injectActions().execute()), it replaced the open one, and the first execute() never settled: no onSuccess, no onError, no settled action observer. Now only one confirmation is open at a time, and a new one cancels the waiting one, so its execute() rejects with the usual cancellation (isActionCancelled(error) is true). A late answer to the replaced dialog no longer closes its successor.
  • watch fires for writes made on an external store. In controlled mode (the store input), watch only heard about writes made through the renderer. A write from your own state management re-rendered the UI but triggered no watcher. On every store notification the renderer now compares the new snapshot with the last one, by reference, and reports each changed path, containers included (/user as well as /user/name). Writes made through the renderer are still reported once, not twice.

Upgrading

npm i ngx-json-render@^0.7.5

No code changes are needed, and ngx-json-render-material@0.3.6 works with it as it is. If you use an external store, check that it replaces objects on write rather than editing them in place: a store that mutates its snapshot still re-renders, but its changes cannot be seen by the comparison and trigger no watch.

Full Changelog: v0.7.4...v0.7.5

material-v0.3.7

Choose a tag to compare

@github-actions github-actions released this 06 Oct 13:03

Component props are typed from the catalog's Zod schemas. The 25 Material components used hand-written prop interfaces that had to be kept in step with the schemas by hand; they now read their types from the schemas, so the two can no longer drift.

The new MaterialProps<'Card'> export is that type for one component: its schema's props with every field optional, because the renderer hands props over as the spec wrote them and checks them only when validate is on. Use it to type a replacement component:

import { type MaterialProps, materialComponents } from 'ngx-json-render-material';

export class BrandedCard {
  readonly ctx = injectRenderContext<MaterialProps<'Card'>>();
}

const registry = defineRegistry(materialCatalog, {
  components: { ...materialComponents, Card: BrandedCard },
  actions: {},
});

Upgrading

npm i ngx-json-render-material@^0.3.7

No code changes are needed; the components render exactly as before. It works with ngx-json-render 0.7.x.

Full Changelog: material-v0.3.6...material-v0.3.7

v0.7.4

Choose a tag to compare

@github-actions github-actions released this 30 Sep 23:23
ae81754

Full Changelog: v0.7.3...v0.7.4