canvas_update_widget_spec expects.
What a widget spec is
A widget spec defines the layout and visualization components for a canvas. In MCP, widget specs are:- created implicitly when a canvas is created
- edited deterministically via
canvas_update_widget_spec - validated against the canvas entity type and field IDs before they are saved
- authored without top-level widget source/entity bindings, which Capsulate injects before persistence
Canonical shape
Capsulate accepts either:- a flat spec with
componentsandrootId - a nested tree that can be flattened automatically
data.sort:
props.colorField for any useful selected scalar data point, including dynamic categories without
allowedValues. Explicit semantic allowedValueTones take priority; other values receive stable colors
from the shared 12-color categorical chart palette, and missing values remain neutral. Omitting
colorField keeps all events neutral.
Kanban props.groupByField follows the same permissive scalar-field rule. allowedValues provide
persistent ordered columns when available; otherwise distinct record values create dynamic columns.
Explicit semantic tones take priority and stable colors from the same 12-color categorical chart
palette provide the fallback. Dynamic boards show the 12 most populated values and consolidate the
rest into a non-writable Other (n) column. Synthetic Other labels change deterministically when a
real category would otherwise have the same display label.
Pie and donut charts apply allowedValueTones from the calculation’s grouping data point when an exact
category match exists. Unmatched categories use the shared categorical palette, and a synthetic
metadata-marked Other remainder remains neutral. A real data value named Other stays a normal
colored category; when both exist, the generated remainder is displayed as Other categories. This
behavior is derived; do not author segment colors or another widget property for it. Missing grouping
values are a separate neutral, metadata-marked No value category. If that label is already real,
Capsulate uses Missing values with a deterministic suffix. Neither missing-value nor remainder
buckets are drillable.
Single-series bar charts use the same derived semantic-first coloring for categorical groups. Unmatched
values receive stable categorical colors and a metadata-marked synthetic remainder remains neutral.
Multi-series bar charts continue to use series colors. Do not author a separate bar-category color
property.
All semantic chart marks resolve through Capsulate’s shared light/dark semantic palette on desktop and
mobile. Authors continue to provide semantic tones only; they do not provide platform colors or hex
values.
When semantic tones are unavailable, badge renderers use one shared exact-token fallback vocabulary
across desktop and mobile. They do not infer a status from a substring of an unknown value.
Entity views and detail cards
Declare the main browsing view with a root-levelentityViews.primary entry that references one
entity-sourced visual-table and one entity-sourced card-grid. Both representations are available
through the user-visible Table/Cards toggle. Do not add display, viewGroup, or viewRole to the
referenced components; legacy surface-pair metadata is not inferred.
Flyouts and drilldowns need a separate, explicit choice of reusable one-record card. Declare it at
the widget-spec root with entityPresentations:
card-grid for the same entity
collection and must contain a non-empty cardTemplate. The primary mobile card can also be the
detail card, but that relationship is explicit rather than inferred from component order.
Capsulate infers the detail-card mapping only when there is one unambiguous candidate.
How Capsulate normalizes widget specs
Before saving, Capsulate:- infers
rootIdif the spec is already flat but missing it - converts nested specs into a flat
components+rootIdstructure - preserves explicit
entityViewsmappings; legacy surface-pair metadata is not upgraded - preserves explicit
entityPresentationsmappings and infers them only when unambiguous - rewrites
entityViews,entityPresentations, and structural component references when generated component IDs are canonicalized - normalizes the spec into the canonical saved shape
- validates bindings against the canvas field and entity context
return_nested_spec, the response also returns the normalized nested shape.
Inferred dashboard composition
Capsulate applies conservative composition guardrails when a dashboard has only a generic, root-level layout:- a metric-only
SimpleGridwithoutcolsreceives responsive column defaults - two or more leading
metric-cardwidgets in a rootStackare grouped into a responsive summary grid - one root-level detail view plus exactly two supporting charts can use the standard responsive 8/4 detail-and-analysis composition
Grid and
SimpleGrid layouts, nested layouts, surface-specific views, workflow layouts, spacing values,
typography, and widget height props are preserved.
New and refined dashboards store presentation.version: 2 at the root. The renderer only applies
these composition defaults to that version, so older saved dashboards keep their existing layout.
Set presentation.autoCompose: false to disable inferred composition while retaining all other
renderer behavior:
Renderer-owned chart presentation
Canvas chart widgets share one renderer-owned frame for card borders, padding, title treatment, definition help, approximation labels, and empty/error states. Widget titles remain 13px. On desktop, row heights are derived at render time to align neighboring widgets. Grid columns are grouped by their desktop spans, so a wrapped second row is sized independently from the first. For a detail widget beside a stack, the stack’s combined height matches the neighboring widget and its children divide the available space.props.layoutHeight is renderer-owned state and is not a
valid authored property. Generated and refined saved specs must omit it. The desktop renderer
normalizes each supported row as one automatic composition instead of exposing a separate custom
sizing mode. Mobile previews do not apply desktop row equalization.
Rows containing flow widgets such as kanban boards, or unsupported nested compositions, keep their
natural sizing. A flow widget in one wrapped row does not disable automatic sizing for other rows
in the same grid. Grids that change their column spans again above the desktop breakpoint remain
fluid rather than receiving heights derived from an uncertain row grouping.
Renderer defaults use the widget’s interaction needs rather than one universal height: charts use
300px total cards, tables and card grids use 400px, recent items use 280px, timelines use 320px,
checklists use 360px, task lists use 440px, and calendars use 360–480px by available desktop width. Kanban boards retain
their flow layout. Unsafe chart overrides are clamped to the minimum complete card height so the
plot cannot overflow its frame.
The renderer also supplies chart presentation defaults when a spec does not explicitly override
them:
- a stable series palette
- legends for multi-series charts, without redundant single-series legends
- compact axis labels with long-label truncation
- horizontal grid guidance for axis charts
- useful fixed-height no-data and currency-error explanations
black, are preserved. The stable palette is only assigned when
a series omits its color.
Calculated bar-chart, line-chart, donut-chart, pie-chart, and heatmap-chart widgets can
open the existing matching-items drilldown when their calculation fields support a deterministic
record filter. Exact categories use equality; numeric buckets use inclusive lower and exclusive
upper bounds; temporal line buckets use timezone-aware exclusive-end ranges; and datetime heatmaps
match their derived weekday/hour parts in the chart timezone. Heatmap axes can open the broader
one-dimensional subset. Period-normalized numeric buckets remain noninteractive because their
displayed ranges cannot be mapped safely to an unnormalized record-field predicate.
Empty and zero-value heatmap cells are not interactive; populated cells expose keyboard controls
and descriptive accessibility labels.
Across the desktop and mobile renderers, interactive widget rows, cards, chart drilldowns, calendar
items, and workflow controls expose platform-appropriate accessibility labels and states. Widget
surfaces and calendar cells use theme tokens instead of assuming a light background. Tables, card
grids, boards, charts, calendars, checklists, and task lists distinguish loading, empty, and failed
states so a request failure is not presented as an empty result. Mobile chart and table content
scrolls within the available screen width and continues to use natural mobile heights rather than
desktop row equalization.
Widget generation receives both a small row sample and a deterministic profile of all available
preview rows. The profile includes field cardinality and missingness, numeric ranges/totals, date
coverage, and common bounded values so the generated spec can avoid weak or misleading charts.
How field and entity context is resolved
Validation uses the canvas field context from:canvas.extraction_patterns.data_pointsif present- otherwise top-level
canvas.data_points
Authoring rule: use exact field IDs
When binding widgets to fields, use the exact field IDs from the canvas pattern orrecords_schema.
Do not invent new field names and do not switch between display names and IDs.
Good:
Text and Title content belongs in top-level children. Dynamic text binds through the
component’s top-level data field:
props.children.
Common component families
Layout and structure
StackGroupBoxSimpleGridGridGrid.ColContainerCardCenterDividerSpace
Display and text
TextTitleBadgeListList.ItemTableButton
Visualization widgets
metric-cardvisual-tableline-chartbar-chartdonut-chartpie-chartheatmap-chartcalendartimelinekanban-boardchecklisttask-listrecent-items-listcard-grid
timeline is a semantic entity widget. Use dateField for the start time and optionally provide
endDateField, originField, destinationField, locationField, categoryField, statusField, and
timezoneField. groupByDate adds renderer-owned day headings, while showTemporalStatus derives
Upcoming, In progress, or Completed from the configured date range. These fields must reference
canvas data points; do not compose timeline rows from primitive Timeline.Item nodes.
When categoryField is booking_type, desktop/web timeline markers and mobile row titles show
travel emojis: ✈️ flight, 🏨 hotel, 🚗 car rental, 🚆 train, 🚢 cruise, 🗺️ tour, 🎟️ activity,
and 📍 other. Category labels remain visible. Matching ignores case and accepts spaces or hyphens
in place of underscores; unknown values and unrelated category fields retain neutral presentation.
Existing saved travel timelines receive this presentation without a spec migration.
Card subcomponents
card-headercard-kvcard-kv-rowcard-status-chipcard-status-steppercard-countdowncard-metadata-gridcard-visualcard-section-dividercard-progresscard-actionscard-journeycard-rowcard-textcard-avatar
card-metadata-grid when key/value rows and countdowns share one semantic reading order. Its
ordered children must be card-kv or card-countdown nodes. Visible children compact into the next
available cell in a one- or two-column grid; the renderer does not reorder nodes by component type.
Field-backed card-kv, card-kv-row, card-countdown, and card-progress nodes bind data points
without authoring separate labels; the renderer derives their labels from the bound field names.
card-countdown reports dates without inferring completion state. It always shows the absolute date
and adds relative context for past dates, today, tomorrow, and the next three days. Cards present
this compactly as Jun 29 (21d ago), while the accessible label retains the complete context, such
as Renewal Date: Jun 29 (21 days ago). It never infers overdue status; an explicit sourced status
can be displayed separately. Past dates also remain visually neutral because the date alone does
not establish whether an obligation is still incomplete.
Status progress components must use canonical stored values. When card-progress.status
uses statusOrder, or card-status-stepper.value uses stages, each ordered value
must match the bound data point’s allowedValues. If the order is omitted or empty and
the bound field has allowedValues, validation can inherit that canonical order. Runtime matching is
case-insensitive but preserves punctuation and spacing, so A-B, A B, and AB remain distinct.
Data binding rules
Many widgets require adata binding. The validator enforces this.
Widgets that require data binding include:
metric-cardvisual-tableline-chartbar-chartdonut-chartpie-chartheatmap-chartcalendartimelinekanban-boardchecklisttask-listrecent-items-listcard-grid
Allowed data sources by widget
metric-card:calculatedvisual-table:entitycard-grid:entitykanban-board:entitycalendar:entitytimeline:entitychecklist:entitytask-list:entityrecent-items-list:entity- charts (
line-chart,bar-chart,donut-chart,pie-chart,heatmap-chart):calculated
Kanban grouping fields
Use any useful scalar data point forgroupByField. Prefer status, state, or stage when the board
represents workflow progression, but dynamic categories such as assignee, category, or type are valid.
The Canvas-owned canvas.kanban_status field remains available for a generic workflow board. Never
author props.columns for a Kanban board.
- Extracted fields with
allowedValuesuse those values as persistent writable columns in their saved order, without a runtime column cap. - Extracted fields without
allowedValuesuse the 12 most populated distinct non-empty record values as writable columns. Additional values are consolidated into Other (n), wherenis the number of hidden values. A dynamic column disappears when no matching records remain anywhere in the filtered Canvas scope. - Empty extracted values appear in a writable No value column only while occupied.
- Values outside an extracted field’s
allowedValuesare consolidated into one non-droppable Other column; cards can move out to a configured value. - The dynamic overflow Other (n) column is also non-droppable. Cards can move out to any visible
dynamic column, but Capsulate never writes the synthetic
Otherlabel into the grouped field. canvas.kanban_statussuppliesnot_started(Not started),in_progress(In progress), anddone(Done). Missing values resolve tonot_started; unexpected stored values appear in non-droppable Other.
Calculation guidance
For calculated widgets:- use single-value calculations for KPI-style widgets
- use grouped calculations for charts
- use time-based grouping for trend charts
Important validation rules
metric-card
metric-card cannot rely on a static number alone. It must have a real binding.
Invalid:
- keep metric values neutral by default
- use status badges or chart colors for semantic emphasis instead of coloring every KPI number
visual-table
visual-table requires:
props.titleprops.columnsprops.columnswith at least one field-backed column
field. Its visible name is derived from that data point by default. Add
header only when the column needs an intentional shorter or domain-specific name; omitting it is
the canonical form. type controls presentation and may be inferred from field metadata.
Supported column types include:
textlogobadgecurrencydatedatetimeemailurlbooleanpercentagenumber
- keep tables focused on the most important columns
- prefer a few high-signal columns over wide tables with every field
line-chart
line-chart must provide a grouped calculation; Capsulate adds the calculated source.
For grouped time-series calculations:
groupByshould be a date/datetime field when appropriate for time-series output
bar-chart, donut-chart, pie-chart, heatmap-chart
These widgets also expect calculated data. Use them for:
- category comparisons
- distribution breakdowns
- aggregate rollups
- two-dimensional summaries for heatmaps
card-header
card-header requires props.title.
card-status-chip
card-status-chip requires props.value.
Allowed variant values are:
lightfilledoutline-soliddot
Validation feedback
When possible, the save path returns validation feedback that helps you correct the spec. This can include:errorswarningsfixessummarynormalized_spec
Failure behavior
If validation fails,canvas_update_widget_spec returns:
errorswarningsfixessummarynormalized_spec
Minimal valid example
MCP guidance
Recommended widget-spec workflow:fetch(canvas:...)to inspect the current canvas and patternrecords_schemato confirm available field IDs- prepare a complete
widget_spec canvas_update_widget_spec- use the returned
normalized_spec,warnings, andfixesto refine if needed
- bind widgets to real fields or calculations
- omit widget source and entity bindings; Capsulate applies the Canvas entity consistently
- prefer compact layouts with explicit IDs and a single clear root
- start with a few strong widgets instead of trying to visualize every field at once

