@liy/mote-agentic-component-catalog
v0.2.12
Published
Shared component catalog, JSON renderer, and Mote query integration for agent-authored interfaces.
Maintainers
Readme
@liy/mote-agentic-component-catalog
Shared component catalog for agent-authored interfaces. The package
includes component schemas and authoring guides, the React registry and JSON
renderer, chart interactions, styles, and optional Mote registered-query helpers.
Supply a catalog spec and prepared datasets to ComponentRenderer; rendering
does not require a query provider or an app bootstrap.
Generated apps can additionally use the query helpers to fetch registered
queries pinned to an app bundle. Build those apps as static SPAs with Vite
base: "./". Mote timelines use the same renderer with agent-supplied datasets;
the MCP host owns submission and the timeline owns persistence and revisions.
The ./catalog entry exports catalog schemas and parseInlineWidgetContent
without importing CSS or React rendering. Mote's MCP host uses it to validate
widget submissions. Every component has one public contract and responsive
behavior in generated apps and timeline widgets. ComponentRenderer receives
spec, prepared datasets, and locale; data acquisition stays with the caller.
Local state and event bindings use the same json-render runtime in both hosts.
ComponentRenderer contains one ComponentProvider for datasets, locale, and
chart handles, plus json-render's state/action provider. The timeline mounts it
directly; envelope validation happens when reading timeline records. Query hooks
remain outside this rendering stack.
Generated apps import ./styles.css, which is complete: the Mote design-system
tokens, base styles and font faces followed by the catalog's component rules.
./components.css holds only the component rules, for hosts that already load
the design-system stylesheet, such as the Mote timeline.
Agent discovery
component-catalog list
component-catalog describe Table
component-catalog describe ButtonThe package ships this standalone executable and one generated catalog.json.
Commands return JSON with the package version and manifest path. describe
includes props, semantic requirements, a validated example, available local
actions and paths to detailed authoring and widget submission
guides. There are no host or device flags. --help documents usage; unknown
components and invalid arguments exit nonzero.
The sandbox runtime bundles the executable and assets unchanged and exposes the
command on agents' PATH. Outside the sandbox, run npm exec -- component-catalog
from a project with this package installed. Discovery makes no network calls.
Match the app dependency to the reported catalog release.
src/components.ts owns schemas, descriptions, events, guides, and examples.
src/component-rules.ts pairs semantic checks with descriptions. The build
validates examples through the same contract used by ComponentRenderer and
widget submission, then generates the manifest. Do not edit generated JSON.
Widget title, summary, identity, and transport limits remain submission metadata;
they do not change component props or interactions.
Data tables
Use the catalog's Table to browse prepared records. Tables share the renderer's
datasets and locale with charts; rows stay outside the authored spec.
const tableSpec: Spec = {
root: "records",
elements: {
records: {
type: "Table", visible: true, children: [],
props: {
dataset: "usage", title: "Product use", pageSize: 10, showPageSize: true,
columns: [
{ field: "product", mobile: "primary" },
{ field: "kg", mobile: "primary" },
{ field: "month", mobile: "secondary" },
],
},
},
},
};
// Reuse the prepared usage dataset from the chart example below.
<ComponentRenderer spec={tableSpec} datasets={{ usage }} locale="en" />;Omit columns to display all declared fields. Columns inherit the dataset's
labels, types, units, and category labelField; authored labels may override
metadata. Set sortable: false when ordering a field is not meaningful.
Local pagination defaults to 10 rows and accepts authored sizes from 1 to 100.
Set showPageSize: true to expose a lightweight labeled number input; it is
hidden by default. Operators can enter a positive integer, apply with Enter or
blur, and cancel with Escape. Sorting covers the supplied dataset and
returns to its first page. Numeric values keep numeric ordering, nulls appear
as missing, and ISO date values sort chronologically while displaying as supplied.
Narrow containers keep primary fields visible and move secondary fields into expandable row details. Without explicit primary fields, the first two columns are primary. Mobile operators can sort by any sortable column. All controls support keyboard input, 44px touch targets on mobile, English/Chinese copy, and Mote themes. Compact headers, alternating row backgrounds without divider lines, and quiet chevron pagination keep the emphasis on the data.
For React composition, wrap Table in ComponentProvider with the same
datasets and locale. ComponentProvider also supports standalone charts.
Table accepts the same props as the catalog's Table.
Table pagination covers only the rows supplied by the host. It does not fetch
additional query pages or recover truncated results. Retain GenAppQueryStatus
and refine a query if its result is truncated. Server fetching stays outside the
JSON catalog; the design system's React DataTable separately supports
controlled serverPagination for hosts that own a paginated endpoint.
Editable charts
Supply prepared datasets once, outside the render spec. Use stable field keys
for mappings and labels for people. Each chart owns a stable id, a dataset
reference, recommended defaults, and the controls its author chooses to expose.
Field mapping borrows from Vega-Lite; this is a small Mote contract, with no
Vega-Lite grammar compatibility requirement. ECharts is a bundled, private renderer.
import { useRef } from "react";
import {
ComponentRenderer, type ChartDataset, type ChartsHandle, type Spec,
} from "@liy/mote-agentic-component-catalog";
import "@liy/mote-agentic-component-catalog/styles.css";
const usage: ChartDataset = {
grain: "One month and product", timezone: "Asia/Shanghai",
fields: {
month: { type: "date", label: "Month", grain: "month" },
product: { type: "category", label: "Product" },
kg: { type: "number", label: "Product mass", unit: "kg",
aggregates: ["sum"], additive: true },
},
rows: [
{ month: "2026-01-01", product: "A", kg: 4 },
{ month: "2026-01-01", product: "B", kg: 6 },
{ month: "2026-02-01", product: "A", kg: null },
],
};
const spec: Spec = {
root: "usage",
elements: {
usage: {
type: "Chart", visible: true, children: [],
props: {
id: "pesticide-use", dataset: "usage", title: "Product use over time",
defaults: { x: "month", y: "kg", series: "product", aggregate: "sum" },
controls: [
{ key: "type", options: ["line", "bar", "area", "pie", "donut"] },
{ key: "x" }, { key: "y" }, { key: "interval" },
{ key: "stack" }, { key: "horizontal" }, { key: "sort" },
],
},
},
},
};
export function App() {
const charts = useRef<ChartsHandle>(null);
return <ComponentRenderer ref={charts} spec={spec}
datasets={{ usage }} locale="en" />;
}For React composition, ComponentProvider and Chart expose the same
behavior. Use the same props and provider datasets; chart IDs must be unique
within a provider. data-chart-id, field keys, and control keys identify the
same elements in source, the rendered UI, and the host handle.
Token-efficient edits
After mounting, a host can call the renderer ref:
charts.current.list(); // ["pesticide-use"]
const chart = charts.current.get("pesticide-use");
chart?.inspect(); // effective spec, field metadata, row grain, and rows COUNT
chart?.update({ type: "bar", stack: "sum" }); // { ok: true }
chart?.update({ y: "product" }); // { ok: false, issue: "number" }; view retained
chart?.undo();
chart?.reset();Inspect only the selected chart when context is needed. Follow-up edits contain
only changed top-level settings; axis objects replace as a unit. Null clears
series, aggregate, interval, or a pinned axis bound; hidden: [] shows all
series. Legend keys are JSON.stringify(categoryValue), preserving primitive
types; labels are never identities. Routine controls, legend selections,
tooltips, undo, and reset execute locally with no model or query calls.
An agent editing application source changes the target chart's defaults or
controls, without rewriting its rows or component code. On source/default
refresh, changed authored keys win and unrelated valid user choices survive.
Data refresh retains compatible settings and clears stale undo history.
The imperative ref is an explicit host integration surface, not a global or an
MCP endpoint. A host must connect its agent transport to that handle if live
agent edits are desired. This runtime does not install a cross-frame messaging
bridge. Settings and up to 30 undo entries last for the mounted session; a host
can persist inspect().spec and provide it on a later mount.
Data and control rules
- Fields use
number,date, orcategory.labelFieldassociates stable category IDs with another field's display names.keyoptionally declares a row identity, needed for scatter points with identical coordinates. - Supply at most 2,000 prepared rows. Normalize SQL decimal strings explicitly, preserve nulls, and keep measures with different units separate. Unknown categories remain visible; rows missing a numeric/time position are counted as unplottable. Null measures appear as missing in the data table.
aggregateis explicit and must appear in the measure'saggregateslist. Available operations aresum,mean,min, andmax. A mean is over the supplied non-null rows, not a weighted average of source observations; do not permit it on pre-averaged data. A sum with some unknown inputs shows the known subtotal and reports unknown input rows in its tooltip.intervalcombines date values into day/week/month/quarter/year buckets, using the declared timezone for timestamps. Weeks start Monday. The date field'sgrainprevents invented finer detail; weekly totals cannot be redistributed into calendar months or years. ISO timestamps require offsets.- Agents can set
xAxis/yAxispresentation:min,max,step(numeric tick spacing),ticks(a density hint), andscale. These settings stay in the chart spec and have no control sections. Date positions preserve actual elapsed time. Changing tick density does not aggregate data. - Stacked bar/area and pie/donut require explicitly additive, disjoint data.
Pie and percentage stacks require nonnegative values. Legend filtering hides
marks while retaining the full percentage denominator; hidden pie slices
reserve their space.
sortby value orders categorical groups by their combined plotted value. Time and numeric axes retain natural order. - The standard control layout exposes Style, X axis mapping, Y axis mapping, X axis grouping (calendar interval), Compare series (100% stack, Stack, Side by side), Orientation, and Order. Inapplicable controls are hidden. Each declared control can override its label and restrict its options. Metadata supplies field choices; invalid combinations are disabled. Style switches retain compatible settings; pie/donut moves the current grouping field into the category mapping. Undo restores the prior view.
- Charts support hover, tap to pin/dismiss, arrow-key inspection, and a data table. Tooltips follow the mouse and anchor to selected marks, flipping at screen edges. Tap empty space or press Escape to dismiss a pinned value. Legend filtering uses the shared MultiSelect with colored chips and a searchable checklist. Controls wrap and stretch to fill each row; the legend picker spans the available width. Dropdowns, including Vertical/Horizontal bar orientation, use the shared Select. English and Chinese copy, theme tokens, responsive controls, and whitespace grouping are shared with Mote.
Business transformations (SQL planning, joins, allocation, conversion, weighted
rates, and server aggregation) stay upstream. These bounded visual rollups are
not a general transformation pipeline. The old parallel labels/series
chart API is removed; migrate authored charts to named fields and a dataset
reference when adopting this runtime version.
Registered queries
Keep mote.queries.json at the repository root, outside dist:
{
"queries": [{
"key": "farms",
"sql": "SELECT farm_code, farm_name FROM analytics.farms ORDER BY farm_code",
"parameters": [],
"maxRows": 200
}]
}The host validates SQL, parameters and actual database output columns when it
registers the build. Browser code references keys only; never import the SQL
manifest into the frontend. An app without queries still declares
{ "queries": [] }.
import {
GenAppQueryProvider, GenAppQueryStatus, useGenAppQuery,
} from "@liy/mote-agentic-component-catalog";
import "@liy/mote-agentic-component-catalog/styles.css";
function FarmPanel() {
const query = useGenAppQuery("farms");
return <>
<GenAppQueryStatus query={query} />
{query.status === "ready" ? <p>{query.result.rowCount} rows returned</p> : null}
</>;
}
export function App() {
return <GenAppQueryProvider><FarmPanel /></GenAppQueryProvider>;
}Keep the shared status beside your catalog view. It distinguishes loading,
denial, empty results, truncation and dataset Farm coverage. Session expiry
offers Reopen in Mote; unavailable public versions offer the current app.
Query results are { columns, rows, effectiveScope, truncated, ... }; column
names/types come from the registered contract. Adapt ready rows into a prepared
dataset and pass it to the renderer separately from the app's spec.
The serving Worker supplies mote-app-bootstrap with the pinned bundle and
explicit viewing mode. The client posts parameters to /query/<key> and
limits itself to four active requests and 32 waiting requests. It does not
cache results. Abort signals cancel work and queued requests. GenAppQueryClient
is available for non-hook consumers; the provider accepts an explicit
bootstrap for controlled local fixtures.
Use orchestrator workspace check <ticket-id> before integration. Integration
registers an immutable bundle and advances preview; publication separately
requires administrator selection and review of public datasets and Farms.
