Grid Views
Grid Views
A Grid View declares a custom surface in model source. Its indentation describes what the person sees, changes, and does; ordinary Grid expressions keep their existing meaning and run in the native runtime.
input qty IS integer = 2 VALIDATE 1..10 PRESENT Slider
price IS currency = 19.99
total IS currency = qty * price
STATE placed = FALSE
SURFACE Order AS "Order"
qty AS "How many?"
[price, total]
"Place order" -> placed = TRUE
"Order placed." IF placed
ENDSURFACE is contextual. A complete declaration header requires a newline;
surface = 4 and SURFACE Order = 5 retain their existing binding meanings.
Use spaces for indentation and align END with its declaration header.
A value may have an AS label and an IF or UNLESS guard. The last guard on
the line applies to the whole view. Parenthesize an expression-level guard.
Brackets arrange outline items across commas and down semicolons; write
show [1, 2] when the array itself is the value to display.
A declaration's trailing PRESENT Slider chooses its usual element. An explicit
kind on the view line takes precedence; ownership and show/edit still decide
whether the placement can write. A local or imported element alias may also be
used: PRESENT QuantityField. Native elements must declare a value-bearing
subject; composites must accept exactly one parameter. A composite's subject
controls must match show or edit; an incompatible direction is a diagnostic.
Explicit element names inside the composite avoid recursively selecting its own
presentation. See declared presentation.
Use named ARRANGE regions for merged cells:
SURFACE Dashboard
ARRANGE [header, header; navigation, content]
header: "Overview"
navigation: "Sections"
content: "Details"
ENDRepeated names must fill one rectangle. Every region has exactly one labelled content block; the compiler reports missing, unused, duplicate, or nonrectangular regions. Positions and spans become static layout metadata, without model reads.
In Code, select ARRANGE in the outline to edit Named regions. Add or remove
rows and columns, then choose the region for each cell. Rename a region without
changing its content. Add region creates a name and placeholder content;
assign it at least one cell. Remove [name] and content stages removal and
lists the affected regions before Apply layout changes source. Retained
content keeps its bindings, guards, comments and embedded payloads. Nested and
flat labelled blocks are supported, and the whole edit uses one editor Undo step.
Every region must remain one filled rectangle, with a distinct name and at least
one cell; the compiler explains invalid arrangements without applying them.
To start a layout, choose Named region layout in Add element or page.
Select a region label such as content: to add elements inside it or move
existing elements there. Content written on the same line, such as
content: NumberInput quantity, appears as a separate selectable child with its
own properties. The region's visibility condition stays on the region.
Simple text, binding and option edits keep the inline spelling. Structural
changes and native event/slot edits expand the affected region onto indented
lines automatically, preserving conditions, comments and multiline payloads.
The expansion and requested change share one editor Undo step. A container
inside a region has its own Inside selected element destination, distinct
from Inside selected region. Moves across declaration, page or repeater scopes
show a source review before Apply. Matrix comments stay
in place when names or cell assignments change. Resizing retains every matrix
comment in its original order above the new cells, inside the matrix brackets;
Properties explains this before applying. Comment text and line endings are
preserved even when their former rows or columns are removed. The visual matrix
supports up to 64 rows, 64 columns and 256 regions; larger matrices remain in
Code. See the
region layout example.
Pages use PAGE Name(parameters) AS "Title" AT "path/:parameter".
Reusable ELEMENT Name(parameters) … END declarations hold view outlines or
contracts that map phrases to properties, lenses, slots, and events. Contracts
are libraries; the outline scanner does not contain a widget vocabulary.
Phrases match longest first and may continue on directly indented lines. A
blank line detaches a continuation. Operator-led phrases start their own line.
Boolean split lenses name each callback and its fixed value:
open means open, set by onOpen(TRUE), onClose(FALSE) : boolean. A close-only
lens names just onClose(FALSE). These callbacks use the same write guards and
Form staging as other lenses.
local declares private browser state. SCRIPT <js>…</js> and inline
js"…" contain real JavaScript; STYLE <css>…</css> contains real CSS. These
payloads are verbatim. JavaScript receives model values through explicit
USING … AS bindings; it is never a second evaluator for Grid expressions.
Code checks named helpers against known standard/native property contracts and
native asks signatures at each use. For example, a helper returning text for
Stack gap receives a type error at that helper's Grid reference. Multiple uses
of the same helper keep their own expectations. Ordinary property helpers
receive a context object; a bare named asks helper receives positional
arguments. The JavaScript module remains editable at its declaration. These
checks also supply contextual parameter types inside unannotated local helper
declarations, using the existing TypeScript worker. Multiple callers contribute
a union of input types, and author annotations take precedence. Completion and
diagnostics still point to authored source; the generated types are not edits.
Imported library helpers remain outside these named-export checks.
An imported element can name a tier-2 adapter with adapter exportName inside
its ELEMENT … FROM "react:…" contract. Define that export in the surface's
or source library's SCRIPT <jsx>…</jsx>. JSX stays verbatim until the selected
browser preparation step; the model compiler never executes it. For example,
inside an existing locked element contract:
subject means value, set by onChange : number
caption means label : string
on reset tells onReset()
parts label, value
adapter renderControlThe corresponding module can wrap the locked export:
import { Control } from "react:your-controls@1";
import { useRef } from "react";
export function renderControl({ subject: [value, setValue], caption, tell, parts, className }) {
const root = useRef(null);
return <label ref={root} className={className} {...parts.label}>
{caption}
<Control value={value} onChange={setValue} {...parts.value} />
<button onClick={() => tell("on reset")}>Reset</button>
</label>;
}Here your-controls represents your installed, locked package, and Control
must be one of its admitted exports. Adapter props use contract phrase names;
a lens is [value, setValue], the default body is children, and named slots
retain their phrase names. Multiword phrases use bracket access. tell accepts
the full declared event phrase and its positional arguments. parts.label is
{ "data-part": "label" }: spread it onto the actual styleable DOM element, or
onto a component that forwards DOM attributes. An adapter chooses that mapping;
a foreign component's internal DOM is not guessed. parts, tell, className
and the default children slot cannot collide with other adapter phrase names.
Hooks and refs use the host's React instance. Static imports resolve only to the contract's locked package references (or an unambiguous package name), React, React DOM, and the React JSX runtime. Unlisted imports refuse loading. Adapters retain ordinary lens conversion, validation, Form staging and event write guards. They require the existing trusted workspace or package sandbox, are React-only, and are prepared as self-hosted JavaScript modules in published packages; the package does not evaluate JSX source or load a JSX compiler at runtime.
See the SCRIPT contract exercise.
Use SCRIPT <jsx>…</jsx> when a helper produces React elements or
component-valued properties. It is real JavaScript with JSX, with the same
explicit Grid value bridge and trust requirements. React is available, and
import React, { useMemo } from "react" uses the renderer's existing React
instance. Component imports use the View's exact locked library references.
Code checks JSX and maps errors and completion edits to the original block;
headless View CHECKs use the same React declarations. Ordinary <js> scripts
do not gain React globals. Preview prepares selected JSX when loading the module;
packaged Views and Vue/Svelte/Solid archives prepare it during export and load
ordinary module files. Library scripts retain their separate namespaces.
See the JSX helper example,
which supplies a custom day button through an admitted component property.
Contract-selected adapters also support lens pairs, tell and slot wiring as
described above. The adapter example
wraps a pinned chooser, focuses it through a private ref, and retains ordinary
Grid selection and event handling. Code completes control.subject and
parts.help, and reports invalid contract fields and tell phrases at their
original JSX bytes. Locked component imports also offer the native props retained
by their contracts. For example, changing the chooser's options={options} to
options={1} reports a type error at that JSX attribute. Named/default imports
and unambiguous package aliases use the exact admitted export list; unknown
exports and unadmitted imports still report errors.
Re-importing a package also projects bounded component prop declarations into
its library lock. These retain required and unmapped props, unions and callback
signatures for Code, without loading package code. Older locks use the contract
as a fallback. Recursive serializable shapes are retained as bounded type
graphs (a record repeated across props is one definition); a React-node value
is unknown, while the record that holds it stays structural; shapes past 16
levels, 2,048 distinct types for a contract (4,096 for a component) or the
byte limits become unknown. These are bounded declarations, not a copy of
the package type graph. Each View
owns its import declarations, so two Views can use different package versions
without sharing types. Headless CHECK uses the same projected declarations and
source maps, without loading package code.
Unknown event phrases and extra captured arguments are refused; a declared but
unused event does nothing. Lens setters retain the original conversion, revision,
permission and Form rules even if an event shares the native callback name.
A phrasebook can replace the adapter while retaining the locked component.
An imported contract with parts needs an explicit component adapter before it
is placed. Spread parts.label, parts.value, and the other declared attributes
on the corresponding elements; the compiler diagnoses a missing adapter with
the repair spelling. Declaring names cannot identify a third-party DOM node.
Missing exports and render/event failures use source-bound reporting; imported
selections link to their caller's USE while retaining the definition origin.
Adapter authoring metadata is bounded to 32 KiB, 64 events and 64 parts per use.
Native event contracts can retain their callback signature after tells, for
example on pick tells onPick(chosen) : (chosen: { label: string }) => void.
Known literal inputs and locals also check tuple lengths and positions, optional
trailing tuple entries, literal choices and discriminated record alternatives.
Lens converter glue receives separate model-side and foreign-side expectations:
goes in maps the model type to the declared element value type, and comes out
maps back. Known scalar and record types are retained; raw collection wire shapes
remain unknown. Grid lambda converters diagnose known return mismatches during
admission. The raw model value is not checked against the foreign phrase type
before its declared read converter runs.
Imported SCRIPT helpers are checked in isolated library documents with their
contract parameter and result types. Their diagnostics point to the caller's
USE statement; imported bytes cannot be edited through that anchor. Code and
headless CHECK share helper contextual typing and source-map handling.
These checks use admitted literal data; they do not evaluate formulas. Dynamic values retain the ordinary conservative type checks.
The importer now preserves the types of admitted event values, including dates,
records and optional fields; older pinned contracts need an explicit re-import
to gain these annotations. Inline JavaScript in the handler receives the declared
values and Code checks member access and offers completion. Untyped event values
remain unknown in Code. Event values shadow same-named locals; an explicit
USING alias overrides that capture. A selected event member retains its type
through USING, and the event scope ends with its handler. Named helpers used
inside an arrow receive captured fields on their normal context object.
Trailing raw DOM events remain excluded. Rest, overloaded and nonserializable
callbacks need adapters. Grid admission also checks finite imported record,
array, union and callback shapes against inferred property/lens values. In event
handlers it checks nested declared fields, nullable payloads and array accesses.
Unknown aliases, index signatures and complex foreign types stay open. This
bounded contract reader does not execute foreign code or validate runtime payloads.
Grid autocomplete uses those declared records too: payload.customer. offers
customer fields, and payload.items[1]. offers the array item's fields. Optional
access retains ?; choosing a name with spaces inserts quoted brackets. A union
offers fields common to its non-null alternatives, and shadowed parameters keep
their own scope. Unknown aliases and arbitrary function results do not acquire
invented fields. These hints have a separate bounded authoring budget; a large
contract remains usable even when Code withholds some suggestions.
See the typed day-click exercise.
Built-in Kanban, Calendar and Map handlers also carry event types into Code.
Destination columns, calendar start/end values and map selection identifiers are
strings; calendar patches map column names to strings. Known model row fields
supply completion and scalar hints for card, event, feature and row.
Numeric fields allow numbers or exact numeric text, so narrow them before using
representation-specific methods. Nested, dynamic and resident-table fields stay
unknown; previous Kanban column values are unknown because the source column
can hold any value. Field hints are bounded to 256 names and 16 KiB; omitted
names retain unknown access. Grid handlers also check direct field reads against
complete known row shapes: card.titel reports the declared fields, and
column.value explains that the destination column is a string. This covers
model/local updates, guards, notifications and page arguments. LET/LAMBDA
parameters retain their own scope. Unknown or truncated rows, nested data and
opaque imported callback types stay open. These checks do not validate runtime
payloads.
In Grid handlers, type card. (or the event's row, event or feature name)
to choose a known field. Suggestions work inside balanced calls and retain
lexical scope: a local parameter named card does not borrow the event's fields.
Choosing a field containing spaces or punctuation inserts bracket syntax, such
as card["order total"]; optional reads retain their ?. A reusable element
used with different row schemas offers only fields common to those uses.
Unknown event records do not offer unrelated model names. Large or incomplete
expressions that cannot establish scope keep source editing without field
suggestions. Nested built-in row inference and complex foreign callback types
remain separate work; finite imported event records use their declared shapes.
The ticket-selection exercise
uses a private browser message and never writes model inputs. Calendar's end
column setting can be written on its own indented line; a closing END,
END SURFACE or END ELEMENT still needs the declaration's indentation.
The formatter retains foreign payload bytes, comments, and expression spelling.
Imported operator-led phrases use their admitted contract vocabulary through
format_source_with_resolver. The compiler formatter CLI can resolve local
.grid libraries with --mode fmt --library-root <directory>; imports must
remain inside that directory. Without a resolver it preserves unknown imported
phrases rather than guessing their boundaries. Native package contracts use the
same locked resolver supplied to the compiler.
An arrow can pin its rule identity with a literal key:
"Place order" key :place_order -> placed = TRUEChanging the label or moving the arrow through presentation wrappers keeps its touch input and outcome identity within that page or composite instance. Keys must be unique in that scope and be nonempty strings/symbols, finite numbers, or booleans. A model expression cannot be a key. Arrows without keys retain their existing path-and-label identity.
Undo and redo
Undo is opt-in per arrow, because the model is shared: restoring a value can
overwrite someone else's later change. Mark an arrow undoable and the open
view offers it on an Undo/Redo bar and on Ctrl/Cmd+Z, Shift+Ctrl/Cmd+Z and
Ctrl+Y. Text fields keep their own undo.
STATE decision = "pending"
STATE approvals = 0
SURFACE Review
"Approve" ->
decision = "approved"
approvals += 1
undoable
ENDThe runtime reports what the click actually committed, and undo reverses that:
- A value written only with
+=or-=moves back by its own amount, so other people's changes to it survive. - Any other value (
=,?=,*=,/=) is restored. If someone has changed it since, the Undo button says whose change it would overwrite. - Table mutations (
table T += …,^=,*=byid,-=byid) restore the rows they touched. Awhereform offers no undo for that click.
Effects such as HTTP calls cannot be taken back once they run. Either hold them so an undo can cancel them first, or say how to reverse them:
input amount = 20
STATE sent = FALSE
SURFACE Payments
"Send reminder" ->
sent = TRUE
HTTP_JSON("https://example.test/remind", amount)
undoable within 10s
"Charge" ->
HTTP_JSON("https://example.test/charge", amount)
undo -> HTTP_JSON("https://example.test/refund", amount)
ENDundoable within <duration> (up to 10min) commits the values at once and holds
the effects: an undo inside the window cancels them, and after it they run.
undo -> … declares the reversal; it runs on undo with the values of the
original click, unless held effects were cancelled instead. Redo runs the arrow
again for either form. History belongs to the open view: it holds 50 steps,
a reload clears it, and other viewers never see it.
CHECK "description" blocks retain isolated interaction checks for tooling and
are not rendered. Syntax errors recover at outline lines so later siblings
remain available to the editor.
Code completion offers standard and imported element names, the phrases their contracts declare, and closed property choices. The compiler supplies exact replacement locations, including unfinished view lines; comments and embedded JavaScript/CSS remain outside these Grid suggestions.
Create and convert in Code
Choose New Grid View in the surface explorer or the model
Code toolbar. Name the View, then choose Add to source draft. Whole model
opens before insertion. The starter includes a page, an editable greeting, and
an isolated interaction check. Existing model source is preserved; the compiler
selects the insertion point, including inside an explicit MODEL block.
The rich Code editor records the addition as one Undo step and keeps Whole model
open with the new View's inspector. Try creating Preview, use Code Undo to
remove the addition, and Redo to restore it. Save in Code when ready to apply the
new declaration; creation pauses source autosync until that explicit Save.
The plain editor fallback can still stage a View, with its existing limited
editor commands.
For an existing App, choose Convert to Grid View in its Code surface toolbar. Apply or discard pending surface-file changes first. Review the candidate source, compiler feedback, translation notes, and document differences. The conversion keeps the original App and opens a separately named View in Whole model. In the rich Code editor, the reviewed source is one Undo step, preserving earlier history. The View's inspector appears when the compiler recognizes its declaration. If Code is still loading, Cancel conversion stops the pending insertion. A candidate with gaps can still be opened for correction. Source autosync is paused until an explicit Save. Changes to the App, model source, partition or installed dependency environment before insertion require a fresh conversion; an editor refusal never retries the insertion automatically.
With the canonical examples/canonical/grid-view-component-migration.json App,
review the component mappings, open the draft, then use Code Undo and Redo before
saving. The original App source and the generated component declarations stay
separate. The plain editor retains direct staging and its limited commands.
The source outline displays the authored hierarchy beside the preview. Selecting an item reveals its source and available Properties. Edit literal text, labels, page titles and paths, button captions, and unavailable reasons. For supported declarations, the inspector also groups bindings, visibility and availability conditions, row sources, actions, and named-region arrangements. While a text or expression property update is pending, Cancel update keeps the entered field draft and prevents the pending result from applying. You can retry immediately. Cancel does not reverse an update already applied; use Code Undo for that. Changed field contracts or withdrawn host editing support retire pending updates automatically. Component options, reusable arguments, event handlers, slots and availability requirements offer Cancel option update with the same retained-draft behavior. If an imported contract changes its fields or limits, obsolete drafts and pending requests are retired even when its contract name is unchanged. Record defaults, field additions, renames and removals offer Cancel record update. The pending source edit is canceled while the entered default, field name and new-field draft remain available for retry. Equivalent analysis refreshes keep these drafts; a changed record declaration contract clears obsolete edits.
Update source applies the selected property as one editor undo step; normal Code saving applies the declaration to the model. Selecting an element in the accepted preview opens its matching outline item and available fields. Selecting a generated record-form field also selects its matching default in Record fields, including nested paths. This works in Inspect copy as well. If a nested field comes from a computed or referenced default, selecting it opens the containing default expression supplied by the compiler. Properties identifies the preview field and explains that editing the expression changes the whole nested default. The inspector requires one matching compiler-owned record and expression; computed root schemas or ambiguous owners retain their existing source guidance. The nested defaults example shows an address selected from two named records. These controls edit source defaults, not the field's current runtime value.
Explicit standard elements and native components declared with ELEMENT ... FROM
also expose an options panel, as do embedded views such as Calendar, Map and
Kanban. Existing properties
appear first; Add property lists the remaining contract-supported properties.
Numbers use bounded numeric fields, closed choices use dropdowns, and boolean
options use Yes/No. Choose Expression or binding for a dynamic value;
input/read binding slots use that mode directly. Text values are quoted by the
compiler, so quotes and line breaks do not need manual escaping.
Automatic standard controls show the chosen type in the panel heading. Bare
placements such as qty AS "How many?" offer shared options such as help, size,
spacing and width while keeping automatic type selection. For a direct binding,
Use Slider (or the current component type) writes an explicit component use
and exposes its full options. This is a deliberate choice to pin the component
type, with one undo step; later model type changes no longer choose a different
component for that placement. The binding, label, guard, comments and model-owned
validation stay intact. show and edit placements offer this explicit-type
action where supported. In examples/canonical/grid-view-order.grid, select
qty AS "How many?", choose Use Slider, then edit its step or other options.
Custom PRESENT controls use the same panel. Shared options remain automatic;
Use [component] pins a caller-visible local or imported component and opens
its argument or native property controls. The compiler retains the effective
label, binding, guards, body and required model access. This is a source change,
so the preview refreshes and component-local draft state may restart. A component
private to its library offers shared options with an explanation; the inspector
does not expose a private name as a caller invocation.
Automatic tables and maps also expose shared options. Use Table preserves
columns, filters, paging and matrix headers, then opens the table settings.
Columns whose names could be mistaken for settings, such as empty or rows,
gain an explicit show prefix in the same Undo step. Use Map opens the
embedded map configuration, including basemap and zoom, while retaining its data
binding, label and visibility condition. Direct bindings offer these actions;
literal and computed placements retain their existing admission semantics.
Generated record forms expose shared options while keeping their generated
fields and Save action. For an edit Profile placement, Edit form options
uses the equivalent automatic Profile spelling before opening those controls.
Record fields lists the source defaults of an input or local object literal,
including nested fields. Select a field to open its exact source or change its
default expression. The compiler checks the edit, preserves the other fields
and validation, and applies one Code Undo step. Plain aliases lead to their input;
component-local defaults belong to the shared definition, while each instance
keeps its own state. These source edits do not write the current live record.
Local defaults must be literal values. Sensitive defaults stay in Code, and
computed or indirect schemas link back to the owning declaration.
Use Add field to choose a parent record, name and default. An object default,
such as {city: ""}, creates a nested record. Empty records can receive their
first field here. Select a field or nested record to rename or remove it; removing
a nested record removes its children together. These operations update the owning
declaration and generated form in one Code Undo step, retaining surrounding
comments and the other field values. Parenthesized default expressions retain
their source spelling. Names are stored record keys: references elsewhere keep
their old spelling and may need changes in Code. This is a source-schema edit,
not a migration of already stored record values.
Whole-record VALIDATE and individual model-input field rules can coexist:
input Order = {amount: 2, city: "London"} VALIDATE <> BLANK
VALIDATE FIELD Order.amount > 0 MESSAGE "Enter a positive amount"
VALIDATE FIELD Order.city <> "" MESSAGE "Enter a city"The generated Form saves the record as one input. Native validation checks the whole record and its field rules against that proposed write; the fields do not become independent inputs. Field failures appear at their exact paths, with a summary for errors that have no matching control. Whole-record failures stay at Form level. Save checks the contract again even if no Check values action was run, and a refused save retains the draft for correction.
When model field rules are present, the schema controls refuse rename and removal and offer links to their declarations in Code. This is conservative: the links include all model field rules because target aliases and operand references have not been proved for schema refactoring. Additions and default edits remain available. Review a coordinated schema/rule edit in Code rather than leaving a rule against an old stored key. Private-local field contracts and table-row contracts remain separate future work. See the field-rule reference for exact-key syntax, ownership limits and the remaining development verification. The record-field example combines an alias target, a live bound and a whole-record rule; its source is also used by the native proposed-batch regression test. The inventory is bounded to 256 leaf defaults and 256 schema members per record, 32 path levels, 64 KiB per expression, 16,384 field/container/rule-link associations and 512 KiB of field and path text across an analysis. New names accept up to 1 KiB without quotes, backslashes or control characters. Large records or unsupported source layouts explain the Code editing boundary. The automatic controls example demonstrates a record form, a table with named columns and a map.
When converting an existing App to a View in Code, the review includes a Page mapping table. It shows each original page name, its name in Code, route, starting-page status and old/new position. Choose Show source to select that page in the candidate without changing your draft. The starting page appears first because View page order selects its starting page. Invalid names and names that differ only by case receive distinct aliases; valid names are reserved before aliases are chosen. Buttons use the same names as the generated pages. Renaming and reordering stay visible in the document comparison, and duplicate routes remain an explicit migration gap.
The page-migration App document
shows a quote page with a non-Grid name, an existing Page_1, and two order pages
whose IDs differ only by case. The last page is home. Conversion produces
Page_3, Page_2, Page_1, then Orders; the page table retains their original
identities and positions. The buttons reach the new order, quote and home pages,
including an uppercase route with surrounding slashes. The original App remains
available while you review and save the candidate.
The Component mapping table reviews each App library component's original
name and library ID, its Code declaration name, and whether source was generated.
Names with spaces, View keywords, standard-control names and conflicts with other
components or the receiving model get distinct Component_N names. Existing valid
names are reserved first. Show source selects the complete declaration;
unsupported templates remain listed with their translation notes.
The component-migration App document
includes a “Résumé card,” a Card, an existing Component_1, and two names differing
only by case. Convert it as Desk in a model declaring input Price = 3 to also
see model and View-name conflicts. Review the mappings, open a declaration, then
stage the candidate in Code. Library IDs, versions and descriptions survive in
source annotations; exporting later uses the current Code declaration name.
The original App and remaining document differences stay available for review.
The selected App migration operation retains portable component-library templates
as ELEMENT declarations. The compiler also exposes a portable component bridge
for standard nodes with concrete App binding parameter types, named content slots
and default content. Exported templates pass the normal App component package validator and can
be instantiated with new bindings and body content. Local state, captured model
bindings, runtime glue and unsupported properties require
an explicit mapping; the bridge refuses them. Original template node identities remain a reported migration difference.
In Code, choose Export component, select a top-level ELEMENT and set each
parameter to Any value, Number, Text, Boolean, or Range or table. Prepare
component checks the complete current draft using the model’s installed
imports. Review the parameters and named content slots, then Download .gridui.
Import that file through the App Builder’s component library and bind its
parameters to the receiving model. A draft containing only reusable elements
also supports this flow; it does not need a SURFACE or a source save first.
Changing the draft, model, partition, imports, or parameter choices discards the
prepared file. Compiler refusals appear in the dialog and leave source intact.
The reusable amount editor
is a complete example: export Editor with amount set to Number.
To bring an App package directly into Code, choose Import component. Open
its .gridui file, select one component, and choose Name in Code. Review
component source checks it against this model’s current draft and installed
imports. Review the generated ELEMENT declaration, then choose Add to source
draft. Code opens Whole model, pauses automatic source deployment, and adds the
declaration before END MODEL (or at the end of an unwrapped model). Use it from
a View’s Add element controls. Import is one Undo step; Save applies it.
The package must be version 1, at most 1 MiB, and contain 1–256 components. Names use letters, digits or underscores, start with a letter or underscore, and have at most 80 characters. Choose a different name if a component, function, model binding or namespace already uses it. Changing source, model, partition, imports or the chosen name invalidates the review. Unsupported templates fail without inserting partial source.
Import preserves the component’s library ID, tier, version, description,
parameter labels/types/defaults and slot labels in a # @grid-component-v1
JSON comment directly beneath its ELEMENT header. Keep that comment with the
definition. It is bounded to 64 KiB; individual text fields accept up to 16 KiB.
Export component starts with those parameter types and shows the retained
version, description, labels and defaults in its review. Changing a type still
runs the compiler’s normal component checks. Renaming a parameter or slot requires
updating its metadata before export; malformed or duplicate annotations refuse
export instead of silently dropping details. Code marks these metadata problems
as warnings at the source annotation.
This metadata is for App reuse. It does not change Grid formulas, argument arity or default values: Grid callers still supply each argument. App library import assigns its own local ID; the downloaded definition retains the source-owned ID. Template node IDs are regenerated. The estimate card demonstrates version 3, an App default binding and a labeled body slot. Export it, import it into another model’s Code and prepare it again to review those details.
For an element with local state or model actions, choose Grid source component
(.gridcomponent) as the package type in Export component. Review its source
and parameter types before downloading it. The
stateful counter example
has two instances with separate drafts and an Apply action that writes the
receiving model's explicit amount argument.
Open a .gridcomponent file through Import component, review its source, and
add it to the draft. Place it in a View and Save to compile its locals and rules
in that model. App Builder also accepts source packages through Import View
component…, with binding review and separate source/App saves. Portable
.gridui templates remain available through the component library. Export and
import do not execute the component or copy model values, compiled rules, live
state, or trust approvals.
A version 1 source package contains one top-level ELEMENT, its name and
concrete parameter types, within 1 MiB. Export includes the local element and
named action helpers it uses, including helpers used by other helpers, action
callbacks passed to local elements, and custom PRESENT controls.
They appear as nested declarations inside the exported component, preserving
existing nested overrides and keeping receiving-model helpers separate. The
import review lists their names; review their source before saving. Pass model
reads and write targets as parameters. Captured bindings, model-relative
references and calls whose standalone dependencies cannot be established remain
refused. A named action is invoked from an action body; it does not become a
function for ordinary formulas. Shared Grid functions and imported dependencies
still belong in a Grid library.
The component helpers example
splits a counter across DraftField, ApplyControls and CounterFields.
Export Counter as a Grid source component with amount set to Number. Import
it as QuoteCounter or create a complete View with amount bound to the
receiving Estimate input. The review includes all three helpers. Two instances
keep independent drafts, and Apply writes only the chosen instance's value.
The named action example
exports ActionCounter with bumpDraft, writeAmount and applyAmount nested
inside it. DraftControls receives the Apply action as a callback. Export with
amount set to Number, import with an explicit receiving-model binding, and
review the named action list before saving. Bumping one draft leaves the other
unchanged; Apply sends only the chosen draft to the bound input.
Helper source and captured bindings are checked again in the receiving model. Import also checks the receiving context: if a bare caption would turn into a binding there, quote it before exporting. Embedded payload bytes stay intact; normal View trust and library requirements still apply. Changing package type or host support retires the prepared review.
To create a complete View during import, select Create a View instance, enter
its View name, and supply each parameter's model binding. For the counter,
choose CounterView and bind amount to Estimate. Review the generated element
and SURFACE together before Add to source draft. Both are one Undo step;
Save compiles the new View. Repeat with a different View name to create another
instance with separate local state and action rules. Bindings must be declared
model names or cell/range references; formulas and literal values are not binding
arguments. The compiler refuses missing/extra parameters and occupied names.
After saving, choose Insert View… in App Builder, select the saved View and
review its bindings before insertion. You can also choose Import View
component… directly in Builder: open the source package, name its View, choose
bindings and review source, then Save and insert. Source and App placement
are saved separately. Failed placement retains an App draft for recovery; Undo
removes the placement and leaves the reusable View in Code.
Place the same saved View twice to give each placement independent local edits.
Labels, help, validation messages, page headings and tab panels address their own
rendered instance. Controls retain those associations through value updates and
keyed row reordering. Both placements still share the model bindings you chose;
create separately bound Views when different model inputs are required. To try
it, save the source component example,
insert its Estimates View twice, and use the Draft fields and Bump actions in
each placement. Code selection and source navigation continue to identify the
shared declaration.
A composite declares BODY "heading" where named content belongs. Its indented
children are defaults. At the use site, SLOT "heading" supplies replacement
children; an empty SLOT "heading" suppresses the defaults. Bare BODY receives
unnamed caller children (or an explicit SLOT "body"). Defaults resolve in the
component’s scope, while supplied content resolves in the caller’s scope. Names
are case-insensitive in Grid, with the original declaration spelling retained in
App exports. Fill each slot once; put conditional content inside the slot rather
than guarding the slot header. Names accept up to 256 UTF-8 bytes without control
characters, with at most 64 BODY placements per component.
The named-slot card
demonstrates an overridden heading, unnamed body content and a default footer.
Export ReviewCard with amount set to Number. In another model, use Code’s
Import component, name it EstimateCard, review and add it, then insert
EstimateCard in a View and bind amount to an input. You can also try Undo
immediately after import to remove the declaration. App imports preserve the slot
names and default children; inserted content keeps its own bindings. Select a
composite in Code to add, update or remove its declared slot fills through
Add property, event or slot. Removing a fill restores its defaults. Bare BODY
also offers the Content editor. Completion supplies the exact SLOT headers.
Portable App export retains each slot’s Stack container settings. For example,
BODY "heading" padding 12 gap 4 class :heading keeps that layout when callers
replace the content. Use an explicit quoted name, including "body", before
container phrases. View runtime behavior still needs an explicit mapping.
An explicit composite with labels or layout settings uses a closed argument
list, for example OrderSummary("Order total", total) AS "Summary" help "Details".
Following shared property lines belong to that component; a blank line starts
caller body content. Arguments named width, size or other phrase words stay
arguments inside the parentheses. Bare argument expressions retain their
existing meaning. The reusable arguments example
includes both this spelling and a custom automatic quantity editor.
Direct model input bindings also show Input validation, owned by the named
or cell-address input declaration. Choose Open input declaration to open Whole model while
keeping the View inspector beside it. Enter the clause after VALIDATE, such as
BETWEEN 1 AND 10 MESSAGE "Choose 1–10", then add, update or remove validation.
The change shares Code's undo history and applies everywhere the input is used.
For the order example, select qty AS "How many?", open its input declaration,
and change the validation clause to BETWEEN 1 AND 20. The declaration keeps
its existing default, type and presentation settings. Automatic controls may
choose a different presentation when their input contract changes.
These controls follow direct named inputs, including qualified names and supported native main bindings, and direct A1/R1C1 cell inputs. Plain aliases also lead back to the input; the panel names the alias and the declaration it owns. Calculations, typed or lazy bindings, cycles, and aliases with their own validation do not authorize this shortcut. Cell-address controls keep the input's validation, format and preferred presentation.
Select a table, picker or one of its columns to open Table columns. The
selector lists display columns, editable fields and row actions in source order.
Add a column, change its value or heading, set a literal FORMAT, alignment,
width, remove it, or move it between neighboring editable columns or row actions.
Display columns also offer Text/Badge/Metric presentation; editable fields keep
their input control. Each change uses one checked Code transaction
and selects the resulting column. Attached comments move with their column;
filters, paging and table empty messages keep their source. Complex column
settings retain an Open column in Code action.
Add row action creates a button caption and a Grid action, such as
SelectedRequest = row.id. An optional unique action key keeps its identity
through caption changes and moves. Select an existing unkeyed action to add its
key from the same field, then choose Update row action. The compiler checks
uniqueness before applying the change in one Code Undo step. Existing explicit
keys remain in the source header; changing them deliberately stays in Code.
Use semicolons between inline steps; open nested steps from the outline to edit
their source.
The selected row action also exposes Action requirements, with paired
conditions and explanations. Updating a caption or inline body preserves nested
steps, visibility, requirements and refusal handlers. Moving or removing an
action carries its whole block, including an attached sibling ELSE. The
compiler checks row-field references and model writes before changing source.
Nonliteral captions retain their source editing explanation and navigation.
With no explicit columns, the table shows all source fields. Adding the first
column or row action starts an explicit list; removing the last returns to all
fields. Use
row.amount for an explicit row read or a computed value such as
row.amount * 2. Editable cells instead takes a plain field name and requires
an admitted keyed array/table source. It retains the heading, literal format,
alignment and width; for example edit amount FORMAT "$#,##0.00" AS "Amount".
The compiler refuses computed or unwritable row targets. Formatted fields show
native display text while resting and unrounded values in display units while
editing. An untouched blur makes no write. Enter saves and Escape cancels; failed
saves keep the draft with a retry. A concurrent value change keeps the draft and
requires Reapply my value before overwriting it. Sorting retains the draft by
row key. Leave the Format field empty to inherit the model's FORMAT <tag>
policies. The native formatter selects the most specific policy from each field's
actual value tag, falling back through its tag ancestors. Reactive policy changes
refresh the display without reinterpreting an active draft; an explicit column
pattern takes priority. A blank policy inherits its ancestor. Pending, invalid or
failed policies explain the problem instead of displaying an earlier policy as
current. Exact numeric strings keep their text under inherited numeric policies.
The inherited row formats example
shows a reactive number policy, currency and percent fields, editable cells and
an explicit override.
Declared field tags also survive plain JSON replacements and browser-local rows. The compiler retains tags shared by every literal row. Conditional records and row sets keep only tags shared by both outcomes; IFERROR/IFNA fallbacks must agree too. Whole-row FILTER, SORT, SORTBY, TAKE, DROP and UNIQUE operations keep these hints. A FILTER fallback must have the same hints. Column transformations, disagreeing branches and unknown schemas do not acquire an invented common tag. A live refined tag takes precedence over the declaration hint. The same metadata selects generated record controls, including nested local records. Record fields inherit model formats; numeric fields keep their editing units in a staged Form draft. Dates, date-times, text and checkboxes show a formatted readout beside the original editable value. Date controls keep civil dates, and zoned date-times retain their supplied offset. An ISO-looking text field remains text. Save still commits the record once; formatting does not rewrite its values. The parent record's own format does not leak into individual fields. These scalar controls remain editable while formatting loads or retries; print uses the selected formatted text. A policy with no match retains the normal display. Exact numeric text and structured editors retain their existing presentation. Currency, percent and unit literals in local defaults use the native literal lowerer; decimal and bigint defaults preserve their exact text. The field formats example combines a reactive currency policy, a nested record Form and private local rows. The computed field formats example uses a conditional invoice default and a selected row list. Generated controls keep currency, percentage and date presentation. The Form stages raw field values and saves one checked input; changing a column format in Code overrides its inherited policy, and removing it restores inheritance. Declared date/datetime row values use the native formatter's temporal carrier, including plain JSON replacements. Ordinary date-looking text stays text; live refined tags and resident table schemas take precedence over declaration hints.
Resident model tables and table views use the current handle's column type and unit. Currency columns select their concrete currency policy, percent columns select percentage policies, physical units use the native tag hierarchy, and date/datetime columns retain date formatting. An explicit column format still wins. Schema changes refresh the display; active drafts keep their editing units. Blank numeric columns retain numeric entry and the keyed native write. Only requested pages cross the table binding boundary; format selection reads no model data. The native table example uses an existing Ledger table and explains the required schema.
Per-row local hints for mixed/computed schemas still need integration.
Plain eager aliases, including chains to named or cell-address inputs/defaults, retain keyed editing at the owning declaration. The runtime reads and rewrites that owner in one commit; the browser sends only the selected key and new value. Native table aliases keep their keyed update. Calculated, typed, lazy or cyclic aliases and aliases with their own validation assertion do not gain this write access. A local that shadows a model input does not become a model row lens.
A model record can own the editable list: Table Order.shipping.lines updates
that stored list inside the Order input/default. Plain aliases to Order work
too. The generated rule reads the current order and replaces only the selected
row field, preserving parent fields, neighboring rows and native field values.
Scalar and record/list editors, sorting and acknowledgements use the same
controls. The shipping lines example
shows both kinds of edit.
Code's column Properties edits these declarations, and Row source validation opens the containing input's whole-value rule. It keeps navigation to the displayed row expression separate from navigation to the input declaration. Repeated readouts over the same stored path inherit that contract context. It is not a contract on each row or field. Dynamic field keys, dotted INDEX fallback keys, calculated collection aliases remain outside this write contract.
A table can also edit a list inside an enclosing model row:
SURFACE Desk
FOR order IN Orders key order.id
Table order.lines
edit amount
ENDEach enclosing repeater needs an explicit stored-field key, such as order.id
or batch.code. Nested rows and reusable elements retain those keys; the final
table rows need id. The initial records must establish common fields across
all parents. Up to 32 stored-field/key steps are admitted. The
nested orders example
includes two enclosing lists and scalar/record editors in a shared element.
Edits read the current owning input and retain neighboring rows and parent fields.
Missing or duplicate keys reject the whole edit with an explanation. Drafts stay
with their keyed parents through reordering; changed fields require explicit
reapplication. Nested edits use the updated native host's guarded View action.
Code's shared table definition offers the same column controls and containing
input validation; those changes share Code Undo.
A declared local array can also use edit amount. The compiler keeps this edit
private to the open View, including Table draft.lines inside a local record and
lists owned by repeated elements. The initial records establish the editable
fields and stable id key. Numeric and text keys remain distinct. Edits find the
current row by its key, preserve other fields and records, and check the whole
local declaration's validation before saving. Removed rows, missing/duplicate
keys and conflicting values keep the draft with an explanation. A changed field
requires Reapply my value; it cannot silently overwrite a newer value.
Private table columns also support record and list fields. Choose Edit record
or Edit list, change the JSON value, then Save value (or Command/Ctrl+Enter).
Cancel or Escape discards the draft; leaving the field does not save it.
Use null to clear a field. Initial array/record hints retain the appropriate
editor for blank fields. Save replaces only that field at its current row key;
newer neighboring fields and other rows survive. A conflict retains your draft,
shows the current value and requires Reapply my value. Failed saves retain
the draft for another attempt. An unchanged save writes nothing.
The editor accepts plain JSON up to 64 KiB, 4,096 values and 32 nested levels.
Keep integers beyond 9,007,199,254,740,991 as text. Nested lists retain their shape,
including lists of pairs; records with kind or value fields remain records.
This edits literal data, not formulas. Whole-local validation still applies;
individual field contracts remain incomplete.
The catalog draft example
shows record/list edits, blank repair and reset. Column Properties uses the same
checked edit source declaration and Code Undo as scalar columns.
Model-backed input/default record lists support the same editor for stored
record/list columns, including through plain aliases. Existing native field and
list-position types are retained: currencies keep their units, dates and exact
numbers keep their text, and matrices keep their axes. Retained value types
shows up to 32 tagged paths. Native type changes also cause a conflict, even
when the displayed JSON is unchanged. Reapplying uses the types from when the
draft was opened. Use Code to change an existing value's type. New fields and
list positions become literal Grid values; null clears a value.
A save sends the typed row key and field value through the existing generated model action and waits for its outcome. It does not replace the displayed row. The model catalog example shows a currency inside a record and a list of pairs. The editor bounds include the native representation as well as the visible JSON. Native record carriers are required for rows; opaque JSON rows, resident table blobs and unsupported native field kinds retain an explanation and source editing. Full native-host journey validation remains pending.
Sorting, paging, native formats, print and the column Properties editor use the
same controls as model tables. Reset restores the local's default, and closing
the View discards its private changes. The
private estimates example
combines a nested local picker with independent repeated estimate lists.
Nested tables can follow keyed enclosing rows back to the same private local.
For example, FOR order IN orders key order.id can contain a
Table order.lines with edit amount. Further keyed levels and stored record
paths work too, including rows passed to reusable elements. Each enclosing list
needs an explicit stored-field key. The initial records must establish the
editable column across all nonempty instances. The
private orders example
edits line items through order and batch keys; reordering either level preserves
ownership. A removed or duplicate ancestor refuses the edit. Literal dotted
field names that conflict with a nested path must be renamed before editing. Whole-local
validation and reset still belong to the original local declaration.
Computed JavaScript collections, bare literals, native slot values and nested model collections still have no admitted private local write owner. Column Properties edits a caller-owned nested table; shared element definitions remain authored in Code.
Tables, pickers, columns, row actions and controls inside a repeater also expose
Row source validation. This is the contract on the whole surrounding collection,
not a separate rule for each row or field. Open row source in Code selects the
authored collection expression; Open input declaration or Open local
declaration selects the declaration that owns its contract. Plain aliases lead
to their admitted owner, and a View local takes precedence over a same-named model
input. Nested tables and repeaters use their nearest row source. A private
collection such as draft.orders, or order.lines inside a repeat, also links
to its containing local declaration. Properties explains that the rule validates
the entire local, including its other fields and lists. This works for read-only
collections too; it does not require an inline row-edit lens. Repeated body
locals retain their exact declaration even when an outer local has the same name.
Try the nested validation example:
select Amount, open its row expression or the draft declaration, then
change the whole-draft rule through Code Undo.
A picker retains separate owners for its selected value and its rows. A repeated
control bound to another input also keeps that input's own contract. Choose
Validation owner to switch between them. Updating or removing the row-source
clause changes only the owning declaration, through one Code Undo step. In
examples/canonical/grid-view-columns.grid, select edit amount or the repeated
Metric to find Requests; select the picker itself to also inspect
SelectedRequest. The picker's RequestRows alias keeps edits and validation
at Requests; the source navigation button selects the alias expression. The
example's <> BLANK requires a supplied value; it does not require a nonempty array.
Computed collections and native table expressions without a direct editable
input/local owner offer source navigation and an explanation. The inspector does
not guess an upstream input or treat a native table handle's declaration contract
as validation of stored rows. If the authoring inventory limit is reached, source
navigation remains available and additional row-source edits are withheld.
Computed JavaScript collections and foreign or ambiguous origins do not borrow
a containing declaration's contract. Admitted stored column names remain row
fields in Code even when the schema is known only at runtime.
Individual table-row contracts remain outstanding; this row-source editor changes
the whole input or local declaration's VALIDATE clause. Model-input record
members may separately use VALIDATE FIELD.
That syntax does not grant validation authority over array elements, resident
table rows or private-local fields.
Local collections can also supply tables and repeated controls. For example,
FOR request IN requests key request.id reads a stored ID from a View-local
array without creating a model calculation. Nested collections such as
FOR line IN request.lines key line.id read the enclosing row. Dotted local
collections, literal arrays and empty arrays use the same renderer and paging;
empty lists display their ELSE sentence. Stable keys retain a row's Form drafts
when the collection is reordered.
A sibling ELSE block immediately after a repeated button handles a refused
action for that row. It retains the row's values and outcome.message; it does
not run the successful follow-up actions. ELSE "No requests." remains the
list's empty-state sentence. The same sibling handler form works for named
native-control events, and editing or removing an event in Properties includes
its whole refusal block while leaving adjacent events and body content intact.
Try the request refusal example:
choosing Northwind leaves the selection unchanged and reports its review
requirement; choosing Contoso succeeds. All state stays in the open View.
Try the private shortlist example.
Choose a customer, clear the list, and restore its default. In Properties, change
Row identity to another stored field through a checked source edit and Undo.
These actions affect the open View's locals. They do not write a model array.
Locals declared in a repeated component or directly inside a FOR body belong
to that element path and row key. Reordering, paging away or temporarily removing
a row keeps its draft; restoring the same key restores that state. A different
key starts from the declared default. Closing and reopening the View resets all
locals. Inputs, actions, reset and validation use the same row-owned value;
View-wide locals remain shared within the open View.
Try the private row drafts example.
Increase Ada's quantity, hide the requests, then restore them. Only Ada retains
the changed quantity. Select RequestDraft request.customer in Properties to
edit the shared validation declaration with Code Undo. A local declared directly
inside the repeater likewise edits its own declaration, even if an outer local
has the same name. Private-copy setup lists View-wide values; edit individual row
drafts in the preview and their shared defaults or validation in Properties.
Stateful repetitions require a key, including ancestors of nested stateful
repetitions. Keys may be text, finite numbers or booleans; numeric 1 and text
"1" are distinct. Missing or duplicate keys show an error at the repeater,
without combining drafts. Native handles rely on the host's stable row identity;
array keys are checked across the complete collection, including other pages.
Browser rows support stored fields and direct field keys. Grid calculations,
computed keys and FOR … IF over browser-local data are refused with
GRID_VIEW_NOT_STAGEABLE. Use an explicit JavaScript row source to filter or
compute its fields first, or use a model collection for Grid expressions. Inline
JavaScript and named SCRIPT exports can provide an array or record immediately;
USING retains explicit model reads. Use row.amount when a SCRIPT source has
no inferred schema and amount also names a model value. A Promise or invalid scalar result reports
an error at the row source. Ordinary model row calculations keep the native
path. This does not add local-array inline editing.
Width accepts a literal such as 140 or "12rem". Literal column FORMAT
patterns use Grid's native TEXT formatter in previews and published Views,
including decimal places, grouping, percentages, negative sections, scientific
notation and dates. Sorting, selection and inline edits keep the original values.
An unavailable host or failed batch shows a concrete explanation and unformatted
values; Retry formatting retries the display operation. Stale responses cannot
replace a newer page. Print/PDF waits for the displayed formats and asks you to
resolve failures before capture. Patterns support up to 256 UTF-8 bytes and
scalar text up to 16 KiB; larger values need a shorter display expression.
See the formatted statement example.
Text, Metric, Stat and Badge values also honor declared FORMAT patterns and
host-resolved tag or cell format policies through the native formatter. An
explicit format choice on the control wins; otherwise the current host metadata
wins over the declaration hint. View-local patterns work in private copies too.
For example, Balance = -25 FORMAT "0.00;(0.00)" displays (25.00) in an
inferred Metric. Dates retain their native scalar kind, and declared unit labels
remain visible.
Scalar displays share bounded requests, show a fallback with recovery guidance when formatting is unavailable, and participate in print/PDF and HTML readiness. Changing a value, pattern, host or preview cancels its old display result. Scalar controls with no format pattern create no formatting request. Full native-host acceptance and row-field policy propagation remain separate work.
Numeric inputs also use the native pattern and the same precedence. At rest, they show the formatted value; focusing exposes the unrounded number in display units. Leaving without a change never writes a rounded value. Native section selection supplies decimal precision, percent/thousands scaling and locale separators, including quoted literals that look like formatting instructions. Sliders project bounds and steps into those units and write model units. An active draft retains its editing units if the pattern or host changes.
Plain decimal or scientific input is accepted in the active format's units.
Numeric controls and editable table cells also accept the native pattern's
prefixes and suffixes across sections, including $12.50, ($12.50), localized
percentages and scaled values such as 1.25K. Fraction patterns accept 1 1/2,
3/2, negative fractions and their unit labels. A fixed display denominator does
not restrict entry: entering 3/8 under a sixteenths pattern stores 0.375.
Focusing still exposes the unrounded decimal, and untouched blur never writes.
Try the workshop quote:
enter 2 3/16 as the cut length and paste ($15.75) into a positive charge.
The private line table uses the same entry behavior. In Properties, change the
Length column's format to # ?/?, then Undo the source change.
Invalid grouping, zero denominators, unsafe fraction integers and ambiguous signs or scales retain the draft with an explanation. For example, a pattern that uses the same decoration for both signs cannot recover the sign from pasted text; enter a signed decimal instead. Form Save stays disabled until an invalid field is repaired. New host or format responses cannot reinterpret an active draft. Plain decimals always use that draft's current display units.
Exact decimal/bigint strings retain their separate text path. Date/time formats, literal-only sections and text embedded between numeric digits do not gain a numeric inverse; their existing controls remain in effect. Older hosts retain their current-section decimal entry. If the host cannot provide a valid editing plan, the control explains the failure and offers basic editing plus Retry formatting. Pending plans block new numeric edits; retries and late results cannot replace an active draft.
The editor supports up to 256 columns within the shared analysis budget; larger lists remain in Code. Source changes, selection changes or unavailable host tools cancel pending edits. Inline region tables expand in the same Undo transaction when needed.
Open examples/canonical/grid-view-columns.grid, select its Requests picker,
change the Customer heading to Account, move the display columns, and undo.
The edit amount column retains its keyed model write and the Choose action
continues to select row.id.
A local declared directly inside a View shows Local state validation when
you select its declaration or a directly bound control. Open local declaration
locates its source. Each open View keeps its own state; editing this declaration
does not change a same-named model input. Local validation accepts literal values,
not model references. Defaults, sensitive, comments, FORMAT and PRESENT
remain intact, and edits share Code's undo history.
Select a reusable element to inspect the input or View-local contracts passed as
plain arguments, along with locals in its definition and the helper components
it uses. This includes nested definitions, transitive helpers, native slots and
caller content consumed through BODY. Unused definitions and unconsumed caller
content do not contribute local contracts. Choose a
Validation owner when more than one declaration is involved. Component-local
edits change the shared definition for every instance; each instance still keeps
its own state. The panel opens that exact declaration before editing it outside
the current source section. Imported component locals remain in their library's
source, while caller-owned input arguments can still be edited here.
Nested definition paths distinguish same-named helpers in the owner selector.
Only compiler-admitted instances contribute owners; repeated instances of one
definition share one entry. If the bounded inventory cannot include all nested
owners, the inspector explains the limit and directs you to Code.
Open examples/canonical/grid-view-validation.grid to compare the shared
Orders!B2 input, the private trialQuantity local and two QuantityPreview
instances. Select the alias display to find the shared input. Select either
QuantityPreview, choose draftQuantity (QuantityPreview local), change its
validation to BETWEEN 1 AND 20, and undo the source change. Both instances use
the definition's contract, while their drafts and the View's trial remain separate.
Choose trialStep (QuantityPreview › DraftControls local) to edit the nested
helper's contract or open its exact declaration.
Row lenses, complex indirect bindings and computed-cell assertions still require Code. A row edit updates its array/table field; a validation on the whole input is not a field-level rule. Ambiguous owners and unresolved or unsupported contract references cannot be applied through the panel.
For native components, the panel reads the admitted contract, including imported
aliases such as Kit.Chooser and local phrasebook extensions. Fixed presets have
an Add property or Remove property action without a value field. A preset
such as spacing(size) presents its named arguments in order; enter 4 to apply
spacing (4). Two-way properties request an input or local binding, and conditional
properties explain the setting they require. Contract choices, argument counts,
variant conditions and writable bindings are checked by the same compiler as Code.
The inspector edits the caller's use site; the locked library stays intact.
Embedded view options follow the renderer contract. Column fields accept names
such as Order title and quote them safely. Closed choices, such as Calendar's
view and Map's basemap, use dropdowns. Fixed configuration values do not offer
a dynamic expression switch; binding fields still accept model expressions.
Array settings use constant Grid expressions—for example [45, -73] for Map's
center. The compiler checks their types and refuses changing data in a fixed
configuration field. Other settings stay in source.
Native components and embedded views also expose declared events in Add
property or event. Select an event to edit its handler source; the field lists
the values supplied by that event. For example, Kanban's on select handler can
use card in on select -> selected = card.id. Keep the event name and edit
its actions, key, guard or nested refusal actions, then choose Add event or
Update event. Remove event removes that handler and its refusal actions.
The compiler checks the event's scope and preserves neighboring settings, slots
and events. Event changes share Code's undo history. This is a handler source
field; it does not invoke the event.
Below component options, open a named event's requirements section to add,
update or remove its individual conditions and explanations. The same controls
appear when selecting the handler in the outline. Each event lists its own
supplied values and keeps separate drafts. Open handler in Code reveals the
exact declaration. Removing a requirement retains its nested action steps and
refusal handlers; it does not remove the event. These controls cover native
components and embedded views such as Kanban. Component header Availability
remains separate. Try the ticket selection example:
select the Kanban, open on select requirements, and edit the selection check
or the card.id check. Each change shares Code's Undo history.
Declared native slots appear in Add property, event or slot. Select a slot
to edit its content source, including its header and indented child elements.
The field lists any values supplied by a scoped slot: for row(entry) holds renderRow, the row content can use Text entry.label. Choose Add slot,
Update slot or Remove slot to apply one checked, undoable edit. The slot
name stays fixed, and neighboring settings, events and other slots are preserved.
An explicit body block uses these controls too. Components declaring
body holds children also offer Content for child elements written without
a slot header. Use Add content, Update content or Remove content;
the compiler checks names in the caller's scope. If content is interleaved with
properties or other slots, the field shows those content blocks in order.
Updating gathers them at the first content location and preserves intervening
settings, events and named slots. Attached comments and multiline payloads travel
with their content. The operation is one undo step. A named body or alias prevents
adding unnamed content over it, and existing unnamed content prevents adding a
conflicting named body. If both forms are already present, resolve them in Code.
Direct native arrows such as Kit.Button label "Save" key :save -> save()
also expose their header options. Additions go before key and ->; property
updates preserve the action, guard, key and refusal handlers. On activation
edits the inline actions and lists the event and supplied values selected by
the component contract (onPress, with onClick as its fallback).
A separate Action requirements section adds, updates and removes body-level
requirements. Component Availability stays in the options panel. For example,
with Ready, HasChanges and save() declared in the caller's model:
# Keep the control visible with an explanation while preparation is incomplete.
Kit.Button label "Save" needs Ready ELSE "Prepare first." KEY :save -> save()
needs HasChanges ELSE "There are no changes to save."The header condition controls availability; the indented requirement is checked before the action runs. Each is edited in its own section. Action requirements can use values supplied by the selected activation event; those event values are not available to component header options. The panel lists the event values. Removing a requirement retains its nested action steps, other checks and refusal handlers. Other nested action steps remain in Code. Named handlers offer their own requirement controls in an ordinary component block; their complete action source remains editable through the handler field.
Selecting a caller-owned use of a reusable Grid element exposes an arguments and options
panel with the parameter names from its definition. Choose Text, Number,
Yes/No or Expression or binding, then Update argument. Text is quoted
safely; bindings and named actions are checked in the caller's scope. Arguments
are required, so the panel updates their values rather than adding or removing
positions. Commas, call parentheses, guards, comments and caller content
are preserved. Imported aliases use the same controls without changing the
library. If an imported parameter changes identity, refresh analysis before editing.
The canonical examples/canonical/grid-view-arguments.grid example uses
OrderSummary("Order total", total): select that instance to edit heading as
Text or amount as an expression or binding.
The Reusable component section identifies the shared definition behind the
selected instance. Open component definition selects its declaration in Code;
when it lives outside the current View, Whole model opens with the View inspector
retained. A native component labels this section Component contract. Shared
body or contract changes affect its instances; argument and option controls edit
only the selected placement. In the canonical
examples/canonical/grid-view-component-helpers.grid example, select either
Counter(Estimate) and open the same ELEMENT Counter(amount) definition.
For an imported definition, the section names the owning library, including a private helper selected by PRESENT. Open import in Code selects the model's USE statement; Review component dependency opens the available dependency tools. Edit the shared definition in its owning library. These links require current analysis and preserve the caller's source when opening an import.
The same panel offers shared presentation options, including help, width, spacing
and Availability. They apply to this placement without changing the element's
definition. Argument names remain separate even when a parameter is named width
or size. The canonical example includes width and availability controls.
Adding the first shared option encloses bare arguments in a call when needed. Caller content that resembles a property gains a blank separator to keep its meaning. Removing the last header option may move a remaining property from a continuation onto the header. Arguments, guards, comments and content remain intact; these source adjustments and the requested edit share one Undo step.
Add property, Update property and Remove property each produce one
checked, undoable source edit. The compiler preserves neighboring phrases,
comments, guards and child content, and refuses unknown choices, invalid literal
ranges or unresolved expressions. Removing a property restores its declaration's
normal default behavior. Native components expose their positional subject as
Main binding or Main value, including add, update and remove controls.
Two-way bindings require a writable input or local value; value contracts use
their declared type. A named alias supplying the same property prevents adding
a conflicting subject. Conflicting aliases, duplicate phrases or nested
declarations stay in their existing source editors. Contract always settings
are fixed by the component.
Availability edits a supported control's needs condition and explanation
together. Enter a Grid expression in Available when and plain text in
Unavailable explanation, then add or update the pair with one Undo step.
The control remains visible when the condition is false and shows the explanation.
This includes standard, automatic, embedded, admitted native and reusable Grid
controls, with reusable arguments and shared options in the same panel.
Ordinary arrows and direct native arrows offer Action requirements. Each requirement has its own pair of fields, including requirements nested around other actions. Updates preserve neighboring requirements, action keys and refusal handlers. Use Add another requirement to append a condition and explanation after the existing checks. Remove requirement removes that check while keeping its nested actions and other requirements. Nested actions move up one indentation level; comments, multiline values and refusal handlers remain intact. Each change uses one Undo step. Cancel closes an unfinished addition. Adding a requirement to an inline region action expands its content and edits it in the same Undo step. See the availability example. Availability expresses readiness; it does not grant authorization to run an action.
To share availability across controls, select an element or container and choose Wrap in availability. Enter Available when and choose When unavailable: Show an explanation keeps the block visible with disabled controls and an explanation; Go to a page or View hides it and redirects when the condition fails. Enter the destination's declared name and any arguments. The compiler checks that destination. Create availability group wraps the selected block, its comments and attached refusal actions. Add or move more controls into the resulting group through the outline.
Select an existing needs block to change its condition, explanation or redirect
with Update group. Remove wrapper, keep contents removes the availability
or redirect and lifts its contents into the surrounding block. Arrange →
Remove group and contents deletes the complete block instead. A separate
visibility condition is retained when removing only the availability wrapper. Comments, multiline payloads,
keys and refusal handlers remain intact. Inline regions expand automatically in
the same Undo step. The canonical availability example groups its shipping and
payment controls; select that group to try updating or removing its wrapper.
Wrapping or lifting a supported block that contains local, reusable or named-action declarations opens Review this group change. It shows the affected declarations and the exact before/after source. Apply reviewed group change applies that checked candidate in one Undo step; Cancel group change keeps the current source. A lifted declaration can become visible in the surrounding block, and a wrapped declaration can lose that visibility. The compiler refuses duplicate names, undefined references and invalid destinations. This does not move or migrate stored values. Condition-only updates keep the declaration scope and apply directly. Draft, selection or capability changes discard a pending review; Apply rechecks the current editor. Keep shared declarations at View level when only the placed controls should share availability. Reusable definition bodies, page-containing blocks and other unsupported source scopes stay in Code. Table row actions use their own requirement controls; a generic availability wrapper is not a table column. A control's presentation options do not replace its input contract; use Input validation for supported owners. A bare binding that shares a contract phrase's name can be parenthesized in Code to make the boundary explicit before editing it in the panel.
For complete source blocks, Arrange offers Move up, Move down, Duplicate, and Remove where supported. These actions include the element's contents and use one editor undo step. Consecutive leading comments at the same indentation travel with their element; a blank line separates unattached comments. Internal and trailing inline comments and original line endings are preserved. Use Move to to place an existing element inside another section, form or page, or after another element. The destination list excludes the selected subtree. Structural indentation changes; multiline literals and embedded JavaScript/CSS keep their exact contents. The compiler checks the resulting hierarchy and bindings before returning the undoable source change.
Moves across repeater, page or declaration scopes open Review this move. Review the current and destination scopes, declarations carried with the block, and the exact before/after source. Apply reviewed move applies that checked candidate in one Undo step; Cancel move leaves the draft unchanged. Names resolve at the destination: a valid same-spelling name may denote a different row or declaration there. Repetition, page action identities and component-local state may change with placement. Unknown references and invalid source still prevent the move. Changing the draft, selection, destination inventory or host support discards the review; Apply also checks the current model, partition, editor, version and edit permission. Moves within unchanged scopes keep the direct operation. Older hosts keep scope-changing moves in Code.
Try the request desk move example: move the repeated caption into Summary's Card, then Undo. A row-dependent field cannot follow it unless its row binding is valid at the destination. Pages use their own page controls; moving a block containing page definitions is refused. End of View appends after the last page in a paged View.
Pages can be reordered and removed with both nested content and flat placements
following their headers. Shared View declarations stay in the source, including
locals, reusable elements, named actions, scripts, styles, policies, themes,
checks and next steps written between pages. Their source and relative order are
preserved. Page order determines the starting page; removing the last named page
leaves the View's shared content. A page still referenced by a link or check cannot
be removed until those references are changed in Code.
Select a page and change Page name in Properties, then choose Rename page.
If needed, Open Whole model to rename page keeps that page selected while
opening all source. The compiler updates local and qualified go destinations,
NEXT links, route guards, and CHECK references to the page name. Existing titles,
URLs and route parameters stay the same; inherited titles and paths become
explicit AS and AT phrases. All replacements share one Code Undo step.
A conflicting page name/title or a name that would redirect an existing link is
refused. Shared definitions with unqualified destinations need a View qualifier
first, such as go Orders!Review(selectedOrder); a foreign component whose
navigation cannot be preserved needs a change in its defining library. Script
text and external links are not rewritten. Save applies the renamed source.
Select a page and open Duplicate page to copy it with a new name and path. The suggested values are unused; overlapping routes, including parameterized routes, are refused. Both nested content and flat placements following the page header are copied after the original, along with attached comments and exact multiline payloads. The copy becomes the selected outline item and uses one Code undo step. Model bindings and navigation destinations remain unchanged; the copy gets its own page and action identities. Route parameters keep their bindings and order. Explicitly authored titles are preserved; otherwise the title derives from the new name.
In examples/canonical/grid-view-pages.grid, select PAGE Details(...) and
choose Duplicate page. The suggested DetailsCopy at orders/:id/copy
shares selectedOrder and submitted with the original, and its Review button
still goes to the Review page. To create independent model data, change those
bindings explicitly after copying. The example also declares reviewNote between
pages: it remains a single shared local. Its flat Text placement belongs to
Details and is copied with that page. Shared declarations, including their
attached comments and exact embedded payloads, are never duplicated or deleted
by page controls.
To try a rename in the same example, select PAGE Review(...), open Whole model,
and rename it to Approval. Its Review button now goes to Approval(selectedOrder);
the displayed title remains Review order and its URL remains review/:id.
Use Undo to restore the page name and every changed reference together.
Inline array members, branch alternatives, reusable definitions and embedded modules also remain in Code. Named layout regions have their own coordinated editor described above.
Use Find in View outline to locate existing elements by their name, kind or parent path, including inside collapsed sections. Selecting a result expands its ancestors and opens source and Properties. Results remain readable while analysis is stale, but source navigation waits for current analysis. The list shows up to 50 matches; narrow your search to find a particular element.
Use Add element or page to choose a placement: At View root, After selected element, or Inside selected element for supported containers. The compiler's standard control catalog supplies available elements and binding fields. Add labels, sections, displayed values, editable inputs, action buttons and pages. Input controls use an existing editable model input or local value. For a page, choose a name, title and optional path; the suggested name is unused, and conflicting names or paths are refused. New pages are appended using At View root, so existing placements keep their page. For other elements, the root choice appends to the source; in a paged View this follows the last page. Select a page and Inside selected element to choose its contents. Add element or Add page inserts a checked source draft with editor Undo. Use Find element to search names and descriptions, including imported components. Results respect the selected placement. Clearing search keeps the selected component and its entered fields; choosing another component starts with that component's defaults. Pressing Enter in search does not insert anything. If refreshed component contracts or available placements change, the panel clears obsolete fields and cancels pending insertion. Equivalent analysis refreshes preserve the fields you are editing. Local and imported components also appear, including definitions that have not yet been used. The choices follow the selected View's imports, aliases and local phrasebooks. Library-private helpers and another View's definitions stay out of that View's palette. Reusable elements list their required arguments in order; enter Grid expressions or bindings, and put text in double quotes. Native components expose their declared main value, writable binding or preset arguments. Remaining native properties, events and slots can be added through Properties after insertion.
The compiler checks the new use in its chosen scope, including argument counts, writable targets and native library locks. A missing library, unresolved name or invalid component body leaves the draft unchanged and shows the reason. Adding an element does not install its library. Edits stay in the calling View, preserving library definitions, existing comments and editor Undo. The form supports up to 256 fields totaling 64 KiB; larger argument lists remain in Code. Creating new model inputs also remains in Code. The panel appears only for an editable current draft. See the reusable element example.
Preview at Panel width, Phone, Tablet, Desktop, or a custom size from 240 to 2560 pixels. Fit to panel scales the display without changing the chosen viewport. CSS media queries use that viewport, and resizing preserves in-progress form drafts. This is a layout preview; trusted JavaScript and native libraries still share the workbench environment, so it is not a device emulator or an isolated script sandbox.
Inspect is the default: clicking an element reveals source without invoking its control. Choose Interact with live model to use controls that can change the connected model. Escape returns to inspection. Editing source also revokes live interaction until the draft is applied and live interaction is selected again. The Page controls navigate within the preview without changing the workbench URL. Page parameters require experimentation or live interaction because restoring them can write model inputs; each parameter must be a valid single route segment. Path placeholders map to page parameters in declaration order. Open page asks before leaving unsaved Form fields, then waits for the View's page inputs to be accepted. A refused input shows its message beside the picker; the current page and Form draft stay in place. While opening, the page controls and preview canvas wait for that attempt. Choosing Inspect, resetting, reloading, changing source or closing the preview cancels a pending page check. Returning to live interaction does not revive it. Cancellation does not undo a parameter write that the host already accepted.
Inspect pauses a live Form without discarding its fields. Returning to live interaction resumes that draft. Starting another copy, leaving a copy, or opening a CHECK setup asks before replacing unsaved Form fields; canceling keeps the same session. Resizing does not replace a draft. Reset copy and Reload preview remain explicit discard actions and need no second confirmation. Try a staged field in the validation example while switching modes; use the pages example to try parameter changes from the picker.
Choose Experiment in a copy to try an analyzed source draft against a private native model copy. You can change inputs, use local state and forms, navigate pages, and run model actions supported by that copy. Reset copy discards its changes and starts again from the current model inputs and source draft. Leaving experimentation or changing source discards the copy; switching to Live does not transfer experimental inputs, form drafts, or page parameters.
Choose Inspect copy to pause interactions while keeping the private copy and its Form drafts. Select a rendered element to open its draft source and Properties, even before the View's first Save. Turn off Inspect copy or choose Experiment in a copy to resume the same session. Preparation tools are hidden while inspecting. Writes already accepted by the host can still finish; queued new writes are refused. Editing source still retires the copy. Copy selections do not open live-model value inspection.
New Views can use this private preview before their first Save, including Views staged from an App conversion. Until the View is saved, Code explains that no saved preview exists and keeps Live disabled. Saving returns the preview to Inspect; choose Live explicitly to interact with the saved model. Opening a saved View in Code loads its compiled preview without first visiting another layer. If that request fails, the preview reports the error; use Reload preview to retry.
Open Set up inputs and local state to prepare values outside the preview viewport. Choose Model inputs or View local state, find an input, and stage its current value. Stage up to 256 values and apply them together. Values are literal numbers, text, Yes/No, Blank or structured JSON; formulas remain in source. Model inputs keep their validation and normal rules. Locals use the View's own constraints. Apply the two scopes separately. Restore default removes a model input override or restores a local's compiled initial value. If an input changes after staging, reload the staged values before trying again; that discards the staged edits. Each value is limited to 64 KiB and a batch to 3 MiB.
Save or load a preparation keeps a selected set of staged values for another experiment. Stage model inputs or View locals, give the preparation a name, then choose Save staged preparation. This downloads a JSON file without applying the values. Default-restoration choices and value types are retained. Save the two scopes separately. To capture a value you already changed in the preview, stage its current value first.
After resetting or reopening the copy, choose Open preparation file. The file must match this View and source identity. Loading opens its staged values for review and uses fresh copy revisions when applying them. Only listed values are affected; omitted values remain as they are. You can edit the preparation before applying it. Model inputs keep their normal validation and rules; locals keep their View constraints. Files contain up to 256 values, 64 KiB per value and 3 MiB overall. They contain selected values and source identity, not model source, page, rule clock, external resources, runtime or library snapshots.
Start from a CHECK setup reopens an existing CHECK's preparation in a fresh
private preview. Choose a CHECK, then Open CHECK setup in a copy. Its initial
given, reset and on steps run in order, stopping before the first interaction or
expectation. References read the results of preceding setup steps. Model givens
use the same validated native fixture path as a CHECK run, without firing normal
input-change rules. reset Name clears a model input's override to restore its
base/default, or restores a local's declared initial value. This differs from
assigning BLANK. Local setup writes use the View's constraints, and on opens the
declared page with its current parameter values. Later interactive edits run
normal rules. Inputs omitted from the setup retain their copied current values.
Controls and page inputs stay unavailable until setup finishes. Reset copy starts over with the same CHECK setup; Experiment in a copy starts without it. A failed or interrupted setup requires a fresh copy. Setup supports 1 to 256 initial steps and never runs later interactions or expectations. Use Run checks for the complete CHECK and its result. Changing source or host support returns to Inspect.
Save setup in Code turns staged inputs or locals into a named source CHECK. Stage current values or load a preparation, edit the values and default choices, enter a CHECK name, then choose Save staged setup in Code. This adds a compiler-checked block to the current draft and selects its header. Code Undo reverses the insertion. The source change ends the current preview; open the new CHECK setup in a copy to try it again. Add interactions and expectations in Code before using Run checks to test behavior.
The Checks panel offers Run beside each CHECK and Rerun failed checks after a failure. Run checks runs all checks in source order; an individual run creates only its selected copy. Results appear as each copy finishes and is released, alongside the current check and progress. Retrying a check clears its old result while retaining the others. Results describe separate runs against copied model values, so they do not certify later input changes.
Cancel checks stops further steps and remaining checks, then releases the current copy. If copy creation is in progress, cancellation waits for its identifier so that the copy can be released. Completed results remain visible; unfinished checks receive no pass or failure. A cleanup failure appears with recovery guidance. Changing source, model, partition, dependencies or host support clears the displayed results and ignores late replies. Select a result to open its failing step or CHECK declaration in Code.
Try the report button example:
run either CHECK alone, then run both. Each resets the count in its own copy.
To try failure recovery, change the first expectation from 2 to 3, run both,
and use Rerun failed checks. Correct the expectation in Code and run again;
source edits clear the previous results.
Capture retains literal types, structured JSON and exact numeric strings, using
literal JSON and TYPE_TAG annotations where needed. Default choices become
reset steps. Local records are not interpreted as model input envelopes. Save
one scope at a time, with up to 256 values and 64 KiB of combined capture fields.
For larger sets, select fewer values or download a preparation file. Omitted
inputs, page, clock and external state are not captured. A name shadowed by a
local or a duplicate CHECK name is refused rather than silently changing meaning.
Capture requires editable source and host authoring support; source, model,
partition or copy changes cancel pending edits.
For the report-counter example, open each click runs the report as a setup. The counter starts at zero; the two presses and expectation have not run. Click Run report in the preview to try the interaction, then reset the copy to start at zero again.
Use a saved input scenario reuses scenarios saved under Saved scenarios in I/O. Choose Refresh scenarios, select one from this model's Files, and choose Stage scenario inputs. You can also open a scenario JSON file. Its source text and source identity must match the private copy. The preparation opens for review: saved overrides retain their value types, and inputs absent from the scenario are marked Restore default. Apply or discard existing staged edits before loading another scenario.
Applying uses the private copy's current revisions and ordinary validated input
writes. Model rules run normally; the connected model is unchanged. Scenario
files are limited to 2 MiB, and preparation supports up to 256 public inputs
with 64 KiB per saved value. Native resource references and unknown or ambiguous
inputs are refused. This imports model inputs only: local state, page, clock,
external data, runtime and package versions are not restored or verified. Use
Start from a CHECK setup for source-authored fixtures. For the canonical Order View, save an I/O
scenario for qty, change its value, then stage the saved scenario in a copy and
review the recalculated total.
Advance rule time steps the private native rule clock by 1 to 60,000 milliseconds. It runs due scheduled rules, debounce and held outcomes through the same evaluator and refreshes the preview. Rule time stays paused between operations; a step is one scheduler operation, not a replay of every elapsed tick. JavaScript timers, animations and date functions continue to use real time. An uncertain time-step reply requires Reset copy before continuing. Older hosts without a reported rule clock keep input experimentation available.
For a starting exercise, open the canonical Order View, choose Experiment in a
copy, stage a different qty, and apply it. Check the updated total, then
restore the input's default. Reset the copy to discard all experimental changes.
The copy uses the same native evaluator and the private CHECK session boundary.
It has no live jobs, connectors, external page fetches, source writes, or navigation
to another View. Clipboard and download actions are refused. It does not run a
background scheduler: delayed native outcomes advance only with explicit rule-time steps.
Values tagged button, including computed and named cells, can trigger their
normal WHEN rules in the copy and in CHECK runs. Each acknowledged click has a
distinct touch value even while rule time is paused. These are button operations;
other computed values remain read-only. An uncertain touch acknowledgement
requires Reset copy. The computed-button example
shows a report counter with a two-click CHECK. In the live workbench, named button
cells use the same input-override path as cell-address button targets.
Trusted scripts and imported components retain their usual browser permissions;
model-copy isolation is not JavaScript sandboxing. A host that lacks private
session support reports that limit before experimentation when it advertises its
tools. Older hosts can report an error with Reset and Inspect as recovery choices;
the experiment never switches to live writes. Copies expire after 15 idle minutes.
Availability and recovery shows the connected host's authoring tools, dependency resolutions observed during analysis, the View's required capabilities, and the workspace's actual custom-UI trust policy. A resolved import does not prove that its runtime assets, credentials or model actions are ready. Unsupported advertised tools are disabled; an older host without this report retains its existing compiler-provided source controls and is identified as unreported. Trust restrictions disable live interaction, private experimentation and checks that would require authored code. They do not prevent editing the source or inspecting installed library locks.
Choose Check library locks to inspect the exact libraries recorded in this View's compiler manifest. This checks installed lock metadata and retained contract source without loading executable runtime assets. Preview loading separately verifies those assets. A missing runtime pin, changed contract or missing source remains an error; a newer installed version is not substituted. Results belong to the current model, partition and draft, and are discarded when those change.
Review model dependencies opens the existing dependency tools; Review Connections opens the service connection workspace. After repairing an import or reconnecting a host, Refresh availability repeats the current draft's analysis across open Code panes. Package environment changes also refresh that analysis. Reload preview retries the renderer and its library loading, discards preview form drafts and navigation, and returns to Inspect; it does not save source or reverse acknowledged model writes. To see the pure-data case, open the order example and inspect its manifest before adding imported components. Publishing a sandboxed app and changing deployment trust remain separate, explicit operations.
Failed checks and private-copy preparation now show recovery controls beside the host message. Open related source opens the failed CHECK step or its setup; a failure before an individual check opens the View declaration. Open Dependencies, Open Connections and Refresh host availability use the existing workspaces and source analysis. Structured authentication and permission refusals explain when to reconnect or review access with the deployment owner; error wording alone is not interpreted as an authorization decision.
For a private-copy failure, Start a fresh copy repeats admission and any selected CHECK setup; Return to Inspect leaves experimentation. These controls remain above the preview and usable even when a failed setup disables its controls. They never enable live interaction or replay an uncertain model action. The report counter example includes a CHECK setup that restores its input before clicking the computed button.
Check outcomes describe the last run. Starting another run removes the old result; a failed attempt cannot leave an earlier pass showing. Changing model, partition, source, host capability or the observed package environment clears results and cancels pending runs. A changed model, partition or environment also returns the preview to Inspect. Later input edits do not continuously rerun or revalidate checks. Deployment-specific credential discovery remains with the connected host's administration tools.
Preview failures collects errors observed while this preview runs: script or locked-library startup, SCRIPT callbacks, imported property preparation and rendering, embedded surface rendering, and providers around the View. Select Open related source to reach a compiler-reported declaring line; a failure without an exact location opens the View declaration. Imported internals remain in their library source. Bounded argument descriptions can help reproduce a callback failure. Views with sensitive locals redact both messages and arguments.
Each Code editor, model, partition, source and environment has its own report. Private drafts can contribute diagnostics from their own copy; the last accepted preview cannot place runtime markers on a newer draft. Reset, reload, leaving a copy or changing context discards that attempt's report, and late failures cannot enter the replacement attempt. At most 64 distinct failures are retained per attempt. These are observed failures, not a promise that every JavaScript task succeeded: exceptions outside the renderer and its wrapped callbacks still use the browser's debugging tools. Native component inputs or source changes can retry a failed render; merely displaying its error does not retry it.
These edits preserve surrounding source, comments, and embedded payloads. The compiler checks the candidate using the model's imports before returning an edit; stale source, unsupported fields, and invalid candidates are refused. Scope-changing structural edits, validation for indirect/local/cell-address bindings and library definitions remain authored in Code. Imported expansions stay at their caller's use line, and embedded modules are opaque outline leaves. Generated surface slots remain read-only.
See Code authoring for editing and inspecting source-declared interfaces.
Renderer style layers
Source Views apply first-party element defaults in grid-view-base, imported
library styles in grid-view-library, kit styles in grid-view-kit, and author
STYLE in grid-view-author. The frontend CSS transform is restricted to the
three existing first-party App/input sheets. It preserves their ordinary App
selectors outside .grid-view and places copies inside the View base layer;
third-party and project styles are not transformed.
This has a measured frontend download cost. On the implementation source, those three sheets increased from 610,056 to 660,212 minified bytes (+50,156), and from 91,864 to 95,329 gzip bytes (+3,465), using esbuild CSS minification and Node gzip on their combined contents. The unminified increase was 54,979 bytes. These are source-sheet measurements, not a full product bundle or a browser-latency result. Ordinary App style matching also gains an exclusion selector; that rendering cost is not represented by the byte measurements. The transform runs at frontend compilation, creates no browser worker, observer, or polling loop, and adds no work to model evaluation or resolve.
Live View nodes retain their existing renderer hooks and add stable app-*
semantic classes. Framework archives can select Plain, DaisyUI, or Tailwind.
For an external kit, the integrating project passes its compiled stylesheet
text as kitCss and includes the exported view-kit.js in class scanning.
The adapter applies this stylesheet inside its shadow root, in the kit layer;
root variables and nested rules remain scoped to the mounted View. The author
layer follows the kit. Stylesheets are supplied by the host project, never
fetched or installed implicitly.
The shared App renderer adds one optional callback check at each converted class site. An ordinary App returns the original class string and does not allocate a class map or cache. A selected View allocates one mapping cache per kit identity and expands each distinct class string once. The shared renderer module alone grows from 54,690 to 56,820 minified JavaScript bytes (+2,130), and from 15,488 to 15,866 gzip bytes (+378), using esbuild ES2022/automatic-JSX module transformation. This is not a full bundle measurement. Callback latency has not yet been measured in a browser. This UI adaptation creates no model-evaluation or resolve work.
In Code, the source outline follows the compiler's declaration tree. Expand pages and containers with the arrow keys, then select an item to reveal its exact source. For an accepted View, Inspect reveals the clicked element without executing its action. Source selection and live interaction are unavailable while the preview belongs to older source. The outline remains a source projection; its property controls apply compiler-checked edits to the same source editor.
Follow a displayed value to its calculation
In Inspect, select a rendered element and choose a value under Model values for this element. Code opens its live value inspector. Why this value? requests a bounded runtime trace; each dependency offers source navigation and further value inspection. Selection alone does not load values or request a trace.
For compiler-generated values, Code shows the View name, authored expression and source line. Go to definition returns to that line, including from a projected View declaration. A reused expression may lead to its first recorded source site. Code withholds locations when the source is stale, the compiler has no exact location, or the metadata is ambiguous. Save or revert the draft and refresh source analysis before trying again.
These values belong to the accepted model. Unsaved Form fields, browser-local values and repeated row fields remain in their preview. Entering live interaction or a private copy clears the selected-element list; private-copy values are not presented as live-model provenance. Choosing a container with no direct reads does not select all of its children.
Try the Order example: select the
total in Inspect, open its value and choose Why this value? to follow qty and
price. To try an inline calculation, add Metric qty * price AS "Calculated total"
inside the View, save, and inspect it. Its generated value should show the authored
expression and line; use Go to definition to return to that line. Full
native-host acceptance and native/CLI diagnostic lineage remain under
development.
Recover a failed model value
A failed model read in a mounted Code preview now appears under Preview failures and in Code's Problems list. Open related source uses the compiler's expression span when one is available; otherwise it opens the declaring element or View. Known generated names in error details are shown as authored expressions and source lines. Unknown generated names do not acquire a guessed location. Native error objects display their message or error code, rather than an object placeholder.
For accepted live-model previews, Inspect failed value opens the same value inspector and explicit Why this value? trace. The report never retries a write or requests provenance automatically. Private copies retain their own source links and reset controls, with no shortcut into live-model value inspection. Reports describe observations in the current preview attempt; reloading or starting a fresh copy clears them. A report may remain after an input recovers.
To try this with the Order example,
add Metric qty / (qty - 2) AS "Review calculation" inside the View and save.
The default quantity exercises division by zero. Open the reported expression,
inspect the failed value, then repair the expression and save again. The preview
only observes values it already has; it does not load every model calculation to
search for errors. Ordinary text and record values are not rewritten as messages.
Framework exports and static capture
Views export through the same renderer into React, Vue, Svelte, and Solid. The Vue/Svelte/Solid artifacts contain a lifecycle adapter and a closed copy of the Grid renderer; the integrating app supplies its existing Grid transport and viewer services. Input edits require acknowledged atomic commits. A host that cannot acknowledge a write cannot silently report it as saved. Native React library and embedded native surface nodes retain explicit target diagnostics on non-React exports.
React’s ui.GridView and exported Vue, Svelte and Solid components accept
onReady and onUpdate callbacks.
onReady gives your app the mounted View handle, and receives null when it is
removed. Before changing your router URL, await handle.navigate(path) and move
the URL only when it returns true. The View asks about unsaved Forms and waits
for page inputs to be accepted. Cancellation or validation failure keeps the
current page and draft. Failures appear beside the View.
You can also pass a path prop. onUpdate reports { accepted, path, message? };
on refusal, path is the retained route so your app can restore its URL. Changing
the source or model connection also asks before replacing a dirty Form. Updating
kit styles, or echoing the already accepted route, keeps the same draft. A newer
update or unmount cancels pending page checks; a late reply cannot write through
the retired attempt. Writes already accepted by the host are not undone.
Before removing the component, call handle.canLeave() and keep it mounted if
the answer is false. handle.hasUnsavedChanges() supplies close/update status.
Hosts with an existing navigation owner may pass app.registerNavigationGuard
instead and consult that registration before removal. Framework cleanup and
handle.destroy() always release resources; they cannot veto a route that has
already removed the component. The Vue/Svelte/Solid README includes this contract.
React receives its host registration through the Component’s binding connection;
a direct GridViewComponent integration supplies it through bindings.
React unmount owns cleanup, so its handle exposes navigation and status without a
destroy method. Changes to compiled source, SCRIPT source, model connection or
partition retain the current React View until its dirty Form can be left. Native
React elements keep their existing renderer and library support. A refused
replacement cannot expand the retained View’s write permissions; reductions
still apply immediately. Equivalent document props and host callback updates
keep the current Form.
A home page with parameters requires an explicit concrete initial path.
Try exporting or ejecting the model field validation example:
change Reference without saving, then request removal through the handle's guard.
Cancel and confirm that Reference is still editable with its draft intact. With a
paged View, try a refused page parameter and then an accepted one before updating
your router URL. In a React Component, pass onReady to ui.GridView, keep its
handle in a ref, and check handle.canLeave() before changing the state that
removes it. Cancel once to retain the draft, then allow removal and confirm that
onReady receives null.
Static HTML capture records the currently rendered page and local draft values. It does not execute new model operations, and its controls are inert. It is not a full paged-table export: rows outside the current rendered page are not added. The snapshot says that it contains only displayed rows. Printing includes all already loaded array rows; a resident table prints its current page with a partial-data notice.
Running CHECK outside the Code layer
The maintained CLI operation runs the same accessible-control driver and view renderer as the Code layer. It requires an already running model host, an already built matching view runtime, and explicitly installed Playwright with Chromium. It never starts a Build or installs browser dependencies. The reusable local helper belongs to the Testing domain:
node tools/testing/run-view-checks.mjs --runtime dist/frontend/grid-view-runtime --project . --source model.grid --surface Desk --model MODEL_ID --api-host http://127.0.0.1:3000--token-env NAME can select an existing API bearer-token environment variable.
The token stays in the Node process. A temporary loopback server admits only
source analysis, private CHECK sessions it created, and reads of pinned library
assets for the selected model. The browser cannot use it for live-model writes
or arbitrary host requests. Every check still executes in the host's private
native calculation session. Completion and cancellation retire those sessions; an
unreachable host retains its existing expiry fallback. A deployment that
selects lifecycle-v3 durable receipts refuses CHECK session and library
requests before reading their bodies, because starting a session loads the
live model; run checks against an ordinary deployment.
The helper verifies selected runtime bytes before starting Chromium. Its JSON report carries check names, outcomes, and source locations. Embedded JavaScript is typechecked before interaction checks, using the Code layer's compiler-owned virtual documents and the project's explicitly installed TypeScript 5.x. Unknown names, malformed JavaScript, and incompatible expression result types map back to their original Grid source. Typechecking never evaluates author scripts or loads their imports. A failed typecheck or CHECK returns an unsuccessful exit status; a source without checks or JavaScript reports that no checks ran. This is an explicit Testing operation, never a condition for Build, Release, Deploy, or Verify.
The independent CLI's gridctl surface check Desk --model MODEL_ID --source model.grid --runtime dist/frontend/grid-view-runtime --project . also discovers
immediate sibling *.test.js, *.test.mjs, *.test.cjs and matching *.spec.*
files. It runs ordinary node:test tests with installed Node 22 or later, under
one bounded deadline with the browser checks. Author tests use ordinary local
Node authority. The CLI clears its API credential variables before running
them, pins the model source for both phases, and reports source or test-file
drift. Missing, skipped, passed and failed JavaScript tests remain distinct in
the combined report.
The runtime manifest records a dependency closure for each entry. Published views copy the publication closure; framework exports add their mount adapter; CHECK selects its own closure. Optional check-driver JavaScript is not included in ordinary published View artifacts.
View theme CSS properties
The View root exposes --accent and --surface-accent for the theme accent,
--radius for the selected corner radius, and --gap for density spacing.
Mode supplies --bg-surface, --app-fg, --app-muted, --border-default, and
--border-subtle. Imported components inherit these properties. Use them in
STYLE with var(--accent), for example; Code offers the same property names.
mode :system follows the viewer's light/dark preference. contentWidth :reading limits the view to 72rem; :full uses the available width.
Responsive hide phrases use the view's available container width: mobile up to
640px, tablet from 641px to 1024px, and desktop above 1024px. Universal classes
survive document loading and reach imported components as className.
Default-mode Views retain an outer layout/paint boundary that author root styles
cannot remove. Print mode lifts that boundary for pagination.
Declared VALIDATE IN lists and CHOICE members supply selection options.
Model-dependent numeric bounds remain reactive. Literal validation on locals
also supplies options and bounds; small bounded integer ranges infer a slider,
including inside a Form. A placement before the first PAGE is shared by all
pages; a top-level placement after a PAGE belongs to that page.
When a Form has unsaved fields, changing the host's page, View, workbook tab, layer, model or folder asks before leaving. Cancel keeps the current route and draft. Browser Back and Forward use the same protection; for routes created by Grid, canceling restores the history position so you can retry the same move. The desktop close prompt and update protection also include these drafts. A private Code copy registers its Form draft with the workbench without receiving live model write access. An approved prompt alone does not save or clear fields; they remain until the navigation succeeds or the View closes.
Published Views use the same outer-app protection, including Home, Edit model, browser Back/Forward and desktop close. Their own page buttons ask through the host, so canceling keeps the Form inside its sandbox. The View sends only whether it has unsaved fields; it does not send the draft's values for this protection. Newly published Views require an updated Grid host and show an update message when that host support is missing. If a page's input validation refuses a move, the old Form remains protected.
Try the model field validation example: change Reference without saving, then choose Code or open the model browser. Cancel and continue editing the same draft. Save the Form before leaving to avoid the prompt. After publishing this example on an updated Grid host, repeat the same canceled move from its app page. Explicit Reload preview and Reset copy retain their documented discard behavior. Hosts embedding a View outside the workbench must supply their own navigation protection; the View still protects its own page actions and browser unload.
Model VALIDATE LIKE, ILIKE, STARTS WITH, ENDS WITH and CONTAINS
clauses supply control feedback, as does equality on text and Boolean inputs.
The author's MESSAGE appears beside an invalid draft. The draft is retained,
and a Form's Save action stays unavailable until its fields satisfy these rules.
Changing a referenced input refreshes both the field message and the Save action;
this does not modify the committed value.
A direct model operand, including a plain alias, reads that input's staged value inside the same Form. On hosts with native validation, Check values checks the complete proposed batch without writing it and displays returned field messages. Save checks again before committing. Computed operands are evaluated by the native host; the browser does not evaluate their expressions against unsaved inputs. Native input validation remains authoritative when saving: the host checks the complete proposed input batch, so a computed operand sees every submitted change and any restored defaults. It rejects a failing batch before writing any member. This requires the updated native host. The detached native check uses resident data; a missing/error operand or a dependency requiring new external work refuses the batch without launching that work. Models whose native capabilities cannot be cloned also refuse the check. Wider capability coverage remains unfinished. Native field messages also cover numeric and structured comparisons, including exact numbers and typed records; there is no separate browser implementation of those comparisons. Hosts without native validation retain limited local feedback and cannot offer Check values.
Open examples/canonical/grid-view-model-text-validation.grid. Change the
reference to NEW-101, then change its prefix to NEW-: the Form clears the
message and saves both inputs together. Select Reference in Code's outline and
edit Input validation to change the owning rule and its message. The checked
source change shares Code Undo and applies everywhere the input is used. These
are input contracts; separate record/table field declarations remain unfinished.
Use Link "Documentation" to "https://example.com/docs" for a captioned
external link; the destination can also be a model binding. SCRIPT exports must
not shadow a binding or element. Import a library element with an explicit
alias when its exported name matches a standard element.
Universal token scales are checked on literals at compile time and on bound/glue
values when they arrive. tone accepts :neutral, :info, :success,
:warning, or :danger; align accepts :start, :center, :end, :stretch,
:left, :right, or :justify. gap is a number from 0 through 64 pixels;
span is a positive integer. Width tokens remain :narrow, :half, :wide,
and :full; imported element contracts with a numeric width keep that
numeric setting. Invalid dynamic tokens show a property error instead of being
coerced into an unrelated style.
Computed drawings in Views
Grid 0.67.0 includes an initial drawing workspace. Large-drawing latency targets and broader host, image/export and resource-retention coverage remain follow-up work for 0.67.1. See the release notes.
A Drawing is a coordinate viewport in an ordinary View. Geometry and appearance
use the same native Grid expressions as the rest of the model. A Graphic embeds
an existing DRAW_* result, numeric pixel array, native image, or inert SVG image.
Use Label for vector text and Overlay for ordinary controls and View layout.
input center = [180, 140]
input radius = 40
input caption = "Station"
SURFACE Diagram AS "Diagram"
Drawing size [640, 360] label "Station diagram"
Circle at center radius radius
fill MAKE_COLOR(47, 111, 237)
stroke "#173875"
stroke width 2
Label caption at [24, 28] font size 18
Handle center label "Station position"
Overlay at [400, 24] size [216, 180]
Stack gap 12
TextInput caption label "Caption"
NumberInput radius label "Radius"
ENDSee the complete example.
Positions, sizes, and radii pairs are ordinary two-number arrays. X increases to
the right, Y downwards. Group translate [x, y] rotate degrees scale [sx, sy]
applies scale, clockwise rotation, then translation. A Label position is its
text baseline. A Drawing defaults to [512, 512]; Code insertion writes
size [640, 360] explicitly. fit :contain centers and preserves aspect ratio;
fit :actual uses one CSS pixel per logical unit. The viewport clips all layers.
Graphics follow source order, overlays sit above graphics, and handles sit above
both. HTML controls do not interleave with individual strokes.
The standard drawing elements are Drawing, Group, Graphic, Line,
Polyline, Rect, Circle, Ellipse, Arc, Path, Label, Overlay, and
Handle. Their Properties and insertion forms come from the standard component
catalog. Specific geometry phrases take precedence over presentation phrases:
Drawing size [640, 360] accepts a pair, while text size tokens retain their
existing meaning. Use ordinary FOR with stable keys, conditions, reusable
ELEMENT definitions, and native calculations. Shapes and groups support
on click -> ...; keyboard activation invokes that same action.
A Handle edits one explicitly writable coordinate pair atomically. A stored
record field such as Handle Station.position is also supported when the
compiler proves its writable owner and pair shape. The guarded transaction
preserves sibling fields and checks the revision of the owning input. Dynamic
indexing and computed inverses are not writable handle targets. commit :live is the default; commit :release sends the final
position when released. axis :x or axis :y restricts motion. A positive step
snaps positions; without it, pointer movement is continuous and arrow keys move
one logical unit. Enter opens numeric coordinates. Updates use guarded native
writes with revision receipts, one write in flight and one latest pending value.
Handles keep their on-screen size when the drawing is zoomed or fitted.
Conflicts stop the gesture and retain its draft. Escape cancels unsent movement
and pending input validation; it does not reverse a committed write.
Revert gesture restores the initial position only if its revision guard still
matches. Within a Form, the handle stages and previews until Save. A successful
Save or Discard clears the previous gesture feedback. Hosts without
the required guarded-write capability show the handle as unavailable.
In Code, Draw adds selection, movement, resizing, group rotation, pan, zoom, and fit to the View preview. Shift-click selects multiple authored objects. The insertion panel and Properties retain ordinary source expressions. A literal can be moved visually; an input requires an explicit Handle or control; a derived expression stays intact. Add transform wraps computed geometry in a Group. Repeated instances edit their common template. Selecting an imported drawing identifies its caller placement and library owner. Its arguments remain editable in the caller; Add transform wraps that placement without changing the library or replacing its formulas. Completed source gestures use checked compiler edits and one Code Undo step. Live model gestures are separate from source Undo. Ungroup distributes literal transforms into individual placement Groups, preserving computed child geometry. A shared computed transform or behavior keeps its Group and receives an explanation instead of an inferred rewrite.
Artwork SVG exports the resolved graphics layer; interactive overlays are
excluded. View print/snapshot opens Print Studio with the saved View selected
and includes the complete composition and ordinary controls. It does not export
private experimental input values; save or inspect those separately.
Neither writes model inputs. Native raster exports through DRAW_RENDER and
DRAW_RENDER_RGB explicitly reject text until native text rasterization exists.
Paths support absolute M, L, C, and Z, not general SVG path syntax.
Graphic grayscale values use range :unit by default. Choose :byte for
0–255 values or :auto for legacy automatic normalization. Existing Canvas
configurations keep automatic normalization when no range is specified. SVG text
is displayed as an inert image document and is never inserted as application HTML.
A Drawing admits at most 10,000 expanded graphic primitives and handles, 256 Overlay roots, and 64 nested Groups. Invalid or oversized updates retain the last valid artwork and show a source-linked error. The native scene and raster limits still apply. These limits are admission rules, not performance claims.