Browse documentation
Docs/Language

Grid Views

Start with Your first reusable Grid View or the composition cookbook; this page is the full reference.

Grid Views

Start with Your first reusable Grid View or the composition cookbook; this page is the full reference.

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
END

SURFACE 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.

Text positions such as a page title, button label, confirmation or download filename accept backtick interpolation. Comments and parentheses around the template preserve that meaning: confirm `Save {Quantity}?` and confirm /* review */ (`Save {Quantity}?`) read the same live binding. The text example shows these forms together. Literal-only positions, including a drawer title, page path and table action's column heading, keep their existing restrictions.

MATCH subject opens a block whose lines are the ordinary arms, one case per line; the right side of each arrow is an outline line and may carry its own indented block. The arm grammar and the exhaustiveness check are the call form's: a missing CHOICE arm is GRID_MATCH_NON_EXHAUSTIVE_ADT, never a silent gap.

CHOICE Load = Waiting | Ready(rows: array) | Failed(message: string)
STATE current = Load.Waiting()

SURFACE Desk
  # One arm per case; a missing arm is the ordinary MATCH diagnostic.
  MATCH current
    Load.Waiting()       -> "Loading…"
    Load.Ready(rows)     -> rows
      ELSE "Nothing to show yet."
    Load.Failed(message) -> Alert message tone :danger
END

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"
END

Repeated 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". Each parameter must name a declared input, default or local; the URL carries its value. Declare a parameter local when only the View reads it. A page's lines may be written flat after its PAGE line or indented beneath it. Either way, the locals, named actions and ELEMENTs among them belong to that page alone; declare one before the first PAGE to share it across pages.

A shell drawer keeps part of the View beside every page in the workbench's left or right drawer. Write it before the first PAGE, at most one per side:

input CustomerSearch = ""
input ShowClosed = FALSE

SURFACE Orders
  # The filters stay open while the viewer moves between pages.
  DRAWER left "Filters"
    CustomerSearch AS "Customer"
    ShowClosed AS "Include closed orders"

  PAGE Queue AS "Open orders"
    "Orders waiting for review"
  PAGE Archive AS "Closed orders"
    "Orders already settled"
END

The side is left or right; the optional title is plain text ("Filters" or AS "Filters"). Outside the workbench (Run mode, print, packaged apps and exports) the drawer's lines stay in the page where they are written. Its locals, named actions and ELEMENTs belong to the drawer, as a page's do. A line such as Drawer ShowFilters side :left is still the standard in-canvas Drawer element. In Builder mode, Shell drawer in Add element or page adds one before the first page, and deleting the drawer's outline item removes it with its lines.

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. A contract phrase that begins with show, such as react-day-picker's show outside days, is read as the element's phrase on its indented line rather than as a show placement.

Composite parameters can declare amount: number (read-only), amount: edit number (a direct writable input/local), or approve: action(number) (a named action with one argument). Supported value roots are number, string, boolean, array, object and unknown. source: read table requires a resident table handle and preserves native paging with private query parameters. It has no literal default or edit table variant; declare row actions separately with their existing permissions. Source packages retain this handle contract and recheck the receiving binding. Known incompatible types and callback arities are refused at admission. Trailing read parameters may have literal defaults, such as title: string = "Amount"; edit and action parameters require explicit arguments. Unannotated parameters retain their existing inferred behavior.

Scoped content uses BODY "summary"(amount) in the definition and SLOT "summary"(current) at its use. The definition supplies expressions; the fill declares distinct aliases while keeping caller scope. Repeated BODY placements must agree on arity. Defaults keep definition scope and per-instance locals. The cookbook explains ownership and current limits. Source packages preserve and verify these interfaces. App-template export refuses explicit parameter contracts and scoped slots instead of erasing them.

local declares private browser state. Add remembered after the initial value and its clauses (local tab = :queue remembered) to keep a local's last committed value in the viewer's browser across reloads of the same model, partition and View; a stored value that no longer fits the declaration's initial, tag or VALIDATE is discarded. Nothing reaches the host or other viewers, and a sensitive local cannot be remembered.

A Grid expression that reads a local or a viewer fact runs in the viewer's browser, on the same Grid evaluator as the model: a placed value such as show (ListPrice * quantity), a guard such as "Wide" IF viewer.width >= 640 AND open, or a local action's value such as quantity = MIN(quantity + 1, 99). The compiler collects those expressions into the surface's browser module. Model values they name are read from the host as usual; nothing a local holds reaches the model except as an action's payload. The module is checked against the portable function catalog when the model compiles, so a function it cannot run is GRID_VIEW_NOT_STAGEABLE naming that function. Rows held in a local work the same way: FOR line IN lines IF line.amount > 50 key line.id, a computed field such as Text line.amount * (1 + TaxRate), or a computed column in a Table over the local runs in the browser too, and each row keeps its stored fields. Rows prepared by JavaScript (FOR r IN <js>…</js>) are filtered and computed in that JavaScript. Event fields and callbacks handed to imported components still use JavaScript glue. The price check example places a private total, a shipping guard and a clamped local action; the private rows example filters and extends a local list.

In the body of a FOR, such an expression may also read the row and that row's own locals. Each row keeps its own value: with local qty = 1 in the body, show (item.price * qty) shows each row's price times that row's quantity, and editing one row's quantity recomputes that row only. The row can come from the model or from a local. An expression in an inner FOR cannot read the outer row or the outer row's locals. A filter or computed column over model rows that reads a local also runs in the browser; a resident table handle cannot, because its rows stay on the host. The private quantities example adds a private quantity and cost to each model row.

A local inside an element belongs to that element by position: the second Card in a block is card-1, so inserting another Card above it hands its local, remembered value included, to the new one. A literal key on the element's line pins the element instead, and everything inside it:

SURFACE Desk
  Card key :summary
    local open = FALSE remembered
    edit open
END

The key is a symbol, nonempty string, finite number or boolean; sibling keys must differ, and a computed key is refused because it has no value when the View compiles. Each placement of an ELEMENT owns its own locals, including those declared in a nested block of its body.

SCRIPT <js>…</js> and inline <js>…</js> contain real JavaScript; STYLE <css>…</css> contains real CSS. These payloads are verbatim. Tags are the canonical form; the js"…" string form still parses and the formatter rewrites it to the tag. Inline <js> reads the model bindings it names, by their exact spelling, and each such name is a recorded read of the View; USING … AS renames a value, passes a transport status (pending Quote) or hands a value to a SCRIPT export, which receives nothing it was not given. JavaScript 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.

A css"…" typed literal (css"""…""" across lines) is verbatim text as well, but it is not a stylesheet. Placed or shown in a View it renders as read-only code, the way <js>…</js>, json"…" and the other code-text literals do, and it is never injected; no element takes it as a style phrase. The surface's stylesheet is only STYLE <css>…</css>.

# A css literal is verbatim text; a View shows it as code and never injects it.
Snippet = css".hero { padding: 2rem; border-radius: var(--radius); }"

SURFACE Desk
  Snippet AS "Copy this rule"
END

Every setting an imported component declares is a phrase (ruling 37). Settings no model value can supply — refs, component-valued props and callbacks with nonserializable arguments — take JavaScript at the use site: ref <js>(el) => el && el.focus()</js>, render cell cell naming a SCRIPT export, or an imported element by name for a component-valued setting (as Anchor). A model value there is GRID_ELEMENT_PHRASE_TYPE. className and style are the universal class and style phrases.

A surface can react to the keyboard, the window and its own lifecycle. Write an on event at the surface's top level, or directly in a PAGE to scope it to that page. Its body is an ordinary arrow body:

STATE refreshed = 0

SURFACE Desk
  local q = ""

  on key "/" -> focus q
  on key "mod+r" -> refreshed = refreshed + 1
  on visible -> refreshed = refreshed + 1

  q AS "Search"
END

The events are key "<combo>", resize, visible, mount and unmount. A plain key is ignored while someone is typing in a field. A row source can be prepared in the browser with <js>, with no model round trip:

input Requests = [{id: 1, customer: "Ada"}]

SURFACE Queue
  local q = ""

  q AS "Filter"
  FOR r IN <js>Requests.filter(r => r.customer.includes(q))</js> key r.id
    Text r.customer
  ELSE "No matching requests."
END

A line that is one <jsx>…</jsx> block is a React subtree (ruling 36). It sees the model bindings its {…} expressions name, each local with a React-style setter, viewer, and imported elements by their tag names:

input Forecast = [3, 5, 8]
USE "react:recharts" EXPOSING (LineChart)

SURFACE Outlook
  local q = ""

  <jsx><input value={q} onChange={e => setQ(e.target.value)} placeholder="Filter" /></jsx>
  <jsx><LineChart data={Forecast} width={Math.max(viewer.width, 240)} /></jsx>
END

An indented block under a <jsx> line is its children, so a React wrapper can hold ordinary outline lines. To edit a model input from JSX, name its setter: setQty(5) writes the input qty as a placed control would. For any other change, declare a named action and call it; each call is one ordinary action run:

input qty IS integer = 2 VALIDATE 1..10
STATE decision = ""

SURFACE Review
  decide(choice) -> decision = choice

  <jsx><section className="card">{children}</section></jsx>
    qty AS "How many?"
    <jsx><button onClick={() => decide("approved")}>Approve</button></jsx>
END

A <jsx> line does not have the ui.* kit or the model.* verbs: use outline elements, setters and named actions instead, or write the whole surface as React (below) when you need them.

A model value that holds JSX text still displays as code.

An element may place itself where the data can stop it. Each level reads its own row's children, so only the levels the data has are shown:

input Folders = [
  {id: "docs", name: "Docs", children: [{id: "specs", name: "Specs", children: []}]}
]

SURFACE Files
  ELEMENT Folder(f)
    Text f.name
    FOR child IN f.children key child.id
      Folder child
    ELSE "Empty."
  END

  FOR f IN Folders key f.id
    Folder f
  ELSE "No folders."
END

Expansion stops at 32 levels and says so. A self-placement outside any FOR, IF or MATCH is GRID_VIEW_RECURSION, since nothing could end it.

Eject to Component turns a view into a React module: its outline as JSX, one G.* element per line, which you can edit by adding ordinary React between the elements. The module runs through the same renderer as the view.

A whole React program as the surface

When the surface is entirely your own React, put the program in one <jsx> block and end it with render(…). This is a Component surface declared in source: it has the ui.* kit, the model.* verbs and every React hook in scope.

input Price = 2
input Quantity = 3
Revenue = Price * Quantity

SURFACE Pricing AS "Price check"
  <jsx>
function Panel() {
  // Reads are the names this program states.
  const price = useCell("Price");
  const { Quantity, Revenue } = useCells(["Quantity", "Revenue"]);

  return (
    <ui.Stack>
      <ui.Text>{price} x {Quantity} = {Revenue}</ui.Text>
      <ui.NumberInput value={Quantity} onChange={value => setCell("Quantity", value)} />
    </ui.Stack>
  );
}
render(<Panel />);
</jsx>
END

You do not list what the program reads and writes. The compiler takes the names from the calls themselves, so the surface above may read Price, Quantity and Revenue and write Quantity, and nothing else.

  • Write each name as a string literal. A name the code computes, such as useCell(key), is GRID_VIEW_NOT_STAGEABLE, because nothing can be derived from it.
  • A write must name an input or a cell. Writing a formula or STATE is GRID_VIEW_NOT_WRITABLE.
  • The program must be the surface's only line. A <jsx> block that does not end in render(…) is a React subtree inside an ordinary view.

A Component created before this form keeps working. To move it, open it in Code and choose Convert to SURFACE. The review shows the contract you declared by hand beside the one derived from the program, and lists what narrows and what widens. Your program is carried over unchanged. Accepting the review opens the change in Whole model as one Undo step; nothing is applied until you save.

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 renderControl

The 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.

Formatting does not wait for a lock. The formatter's syntax-only parse reads an element whose declaration is unavailable, such as a React import that is not yet locked, by shape: the word after its name is its subject, its indented lowercase lines are its phrases, and its on … lines are its events. Nothing is compiled from it; compiling still requires the import and its lock and reports the use exactly as before.

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.

Write the converters as contract lines under the imported element. subject goes in as … maps the model value to what the component expects, and subject comes out as … maps the component's value back before the write. A Grid lambda on goes in as is hoisted into the model; a Grid lambda on comes out as runs in the rule, on the raw payload; JavaScript glue runs in the browser on either side. A converter that is neither a Grid lambda nor glue is GRID_ELEMENT_ARGUMENTS. This example needs its package locked first, with the View Library installation command printed by GRID_LIBRARY_NOT_LOCKED.

input DueDate IS date = date"2026-10-01"

# The component wants ISO text; the model keeps a date.
ELEMENT DayPicker FROM "react:react-day-picker@10.0"
  subject means selected, set by onSelect
  subject goes in as d => TEXT(d, "yyyy-MM-dd")
  subject comes out as s => DATEVALUE(s)
END

SURFACE Desk
  DayPicker DueDate
END

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 = TRUE

Changing 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. Without a key, a literal-label arrow uses its existing path and label. An interpolated label uses canonical syntax: comments, parentheses and locations do not rename the action, while changing the label or a hole expression does. Current input values do not participate in its identity.

Client event actions use the recognized event kind and, for keyboard events, the decoded key and modifiers. Comments and equivalent key escapes do not rename their touch inputs or outcomes. V3 changes existing unkeyed template and event action IDs; literal-label and explicitly keyed button IDs stay the same. These changes apply on the V3 development branch.

Actions, outcomes and pacing

An arrow body that writes model values runs as one rule in the runtime. A body that writes only local values runs in the browser and never reaches the model. One body cannot write both.

Viewer actions

Viewer actions run only for the person who clicked, in source order, after the model writes commit. They never reach the runtime. Each one adds the capability shown to the View's manifest.

Action Effect Capability
go Page(args), go back Navigate app.route
notify "text" tone :success Show a notice app.notify
copy expr Put a value on the clipboard clipboard.write
download expr AS "name.csv" Hand the viewer a file file.download
focus Name Move focus to a placed control app.focus

A notice's tone is :info, :success, :warning or :danger. Notices use the View's live region, and a danger notice is announced assertively. A download needs a file name without a path. focus names a binding or local that the View places as an interactive control.

toggle name flips a boolean local. The other local actions are name = value, name += number and name -= number, where the value is a literal, another browser value or JavaScript glue, and RESET name, which restores the declared initial value.

input qty IS integer = 2 VALIDATE 1..10 MESSAGE "Order between one and ten."
STATE placed = FALSE
Summary = `Order of {qty}`

SURFACE Order
  local showNotes = FALSE
  local notes = ""

  qty

  # Commit the order, then confirm it on this viewer only.
  "Place order" ->
    placed = TRUE
    notify "Order placed." tone :success
    focus qty

  "Copy summary" -> copy Summary
  "Download summary" -> download Summary AS "order.txt"

  # Browser-only state: nothing reaches the model.
  "Notes" -> toggle showNotes
  notes IF showNotes
END

The compiler derives the View's capability manifest from what it places and does: model.read for reads, model.write for lenses and touch inputs, app.route for go, and the viewer-action capabilities above. ALLOW, once per surface at its top level, states a ceiling for that manifest. A View that needs more than it allows is GRID_VIEW_CAPABILITY_EXCEEDS, which names the missing capability, so review becomes a diff of the derived manifest against the stated intent.

input qty IS integer = 2
total IS currency = qty * 19.99

SURFACE Order
  # The derived manifest may not exceed this ceiling.
  ALLOW model.read, model.write, app.route
  qty AS "How many?"
  Metric total
  "Checkout" -> go Receipt

  PAGE Receipt
    "Thanks for your order."
END

Outcomes and ELSE

Each model arrow reports an outcome. The button is busy while the action runs, and it finishes as done, rejected or failed. rejected means a VALIDATE contract or write guard refused a write; the message is the contract's MESSAGE. failed means an effect in the body errored after the values committed. When effects are still running, or held by undoable within, the View treats the committed values as done and runs the viewer actions. A later effect failure is reported on the same button.

Every click ends with an outcome, so a button never stays busy. A click that THROTTLE, DEBOUNCE or SERIAL LATEST skips finishes quietly as superseded: nothing ran for it, and no viewer actions or ELSE follow. If no result arrives within 30 seconds, the click fails with "No result arrived for this action within 30 seconds. It may still complete; check before trying again." and its ELSE runs. If a viewer action fails after the values committed, the button still reports done and a notice says that a follow-up step failed; ELSE does not run.

An ELSE block directly after the arrow runs when the action is refused, in place of the viewer actions, and when a later effect fails. It contains viewer actions only, and outcome.message reads the message:

input amount IS currency = 1000 VALIDATE <= 5000 MESSAGE "Amounts above 5,000 need a manager."

SURFACE Request
  amount

  # A refused write runs the ELSE block instead of the follow-up actions.
  "Add 1,000" ->
    amount += 1000
    notify "Amount raised." tone :success
  ELSE
    notify outcome.message tone :danger
END

Prediction

A body whose model writes are all plain = assignments of literals, browser values (a local, a row field or an event field) or JavaScript glue results is predicted. The View shows the new values at once, and the committed result confirms or replaces them; a glue result is predicted with exactly the value the click sends, and the glue runs once. Bodies with +=, -=, ?=, table mutations, effects or a sensitive local in the payload (directly or through glue) are not predicted; their button stays busy for one round trip. In the viewer-action example above, placed = TRUE is predicted.

Pacing clicks

A model arrow admits every click in order, keeping up to 256 waiting clicks. A phrase on its own line in the body changes that, with the meaning the same clause has on a WHEN rule (see rules and schedules, §2.1 and §6.1):

Phrase Effect
THROTTLE 1s Run the first click, then ignore clicks for the duration
DEBOUNCE 300ms Wait for a quiet period, then run the latest click
SERIAL LATEST Keep only the newest click waiting behind a run in flight
SERIAL QUEUE n Keep up to n waiting clicks, in order
SERIAL LEDGER Keep every waiting click
CONCURRENT k … Allow up to k runs with effects in flight
STATE refreshes = 0
STATE saves = 0

SURFACE Dashboard
  # Run the first click at once, then ignore clicks for one second.
  "Refresh" ->
    refreshes += 1
    THROTTLE 1s

  # Keep only the newest click waiting behind a run in flight.
  "Save draft" ->
    saves += 1
    SERIAL LATEST
END

A rate phrase may also end the arrow line, as in "Refresh" -> refreshes += 1 THROTTLE 1s. These phrases apply to arrows that write model values; on an arrow that only changes locals or runs viewer actions they have no effect, and the compiler warns where they are written. Control edits pace themselves: lens writes coalesce through a short write buffer, and a text control writes when it loses focus (commit on :blur, the default). commit on :change writes as the person types:

input notes = ""

SURFACE Editor
  # Save each change without waiting for the field to lose focus.
  notes commit on :change
END

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
END

The 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 += …, ^=, *= by id, -= by id) restore the rows they touched. A where form 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)
END

undoable 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.

When you are signed in and the host keeps an undo journal, the host also keeps your plain undoable clicks (not held or undo -> ones) for 30 days. Reopen the View, or open it in another session, and Earlier beside Undo and Redo lists them, newest first, whenever that View's own Undo history is empty. Choosing Undo or Redo there replays the change through the host under your account. Only you can list or replay your entries. Ctrl/Cmd+Z stays with the open view's own history. Hosts without durable model tables, and signed-out or anonymous viewers, keep only the browser history above.

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. Renaming shows compiler-proven field uses and updates their source together, including nested paths, plain aliases, private locals and model field-validation targets and operands. Dynamic keys, shared expressions with different record owners, embedded code, whole-record writes and CHECK record fixtures or comparisons can leave unresolved uses. CHECK resets do not carry field keys; unrelated fixture records retain their source. Their source links explain the refusal; no partial rename is applied. A user-defined function with a built-in member-access name keeps its own semantics: its literal arguments are not proven field keys. Review linked opaque uses in Code before renaming the record field. This changes the source schema; already stored record values still require migration. Private records inside pages, component instances, repeated bodies and Tree row slots keep their declaration owner. Imported source component records are editable in the receiving source; imported library declarations stay with their library. Choose the private record under Validation owner when a repeated control also exposes a collection owner. Row and scoped-slot parameters retain their separate scope, even when they share the outer record's name. Shared record-contract keys require review with their uses; the refusal links to the local contract declaration. A computed default value is not an input-record schema editor: follow its definition in Code.

Removing a field does not rewrite remaining references; review them in Code.

Model-input field rules can be reused through named record contracts, including nested address rules shared by several owners.

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, a rename includes their proven targets and operand references. Removal still requires reviewing the linked declarations in Code; its conservative links include all model field rules. Additions and default edits remain available. Private record locals can apply shared literal rules in their own scope. Every reached private record bound, including a direct or imported literal, admits at most 64 KiB of JSON-serialized value. Comments, grouping and source locations do not count, and unused bounds are not checked. The total expanded private constraints remain limited to 8 MiB per View. VALIDATE ROWS Lines USING LineRules applies a contract to every row of a private list of records, and a failure names the row and field; the private row contract example uses it on two independent draft tables. At model scope, the same syntax checks native input rows, including exact nested collections such as Order.lines. Bounds read the proposed batch; a rejected write leaves the owner unchanged and identifies the first failing row for each field clause. See the model row example and 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, filtering and table paging retain scalar and structured drafts by model partition, owner, parent keys, row key and field. Returning to a hidden row restores its draft and opening types; a changed current value still requires explicit reapplication. Native table drafts also keep the key's original carrier: changing an exact numeric key to text with the same displayed digits starts a separate draft. Changing the key's native tag also separates drafts conservatively. This retention rule does not change the model's equality rules. Saves settle even while a row is hidden: success clears the draft, while rejection keeps it for retry. These drafts remain private to the open View and participate in its unsaved-change navigation protection. Leaving the page after approval discards them; they are not recovery storage across browser reloads. 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.

In the nested model rows example, open Details, change the JSON without saving, then choose Next and Previous on that table. The first row's draft returns. Cancel discards it; Save value submits it through the original owner and validation.

Retargeting a named row alias to a different model record or stored list also changes its draft owner. An old draft cannot appear in the replacement owner's editor, even if the table, displayed alias and row keys have the same names. Changing only the filter keeps the owner and its drafts.

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. Other computed transformations, 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.

Record-list editors and pickers retain the exact spelling of their identity field. They use id when present; otherwise a single case variant such as ID or Id is accepted. If several variants exist without an exact id, declare one unambiguous identity before editing. Typed keys keep their native type through nested and structured editors. See the row identity example. An editable identity field can be renamed, but its replacement must be nonblank and unique in the owning list. The native commit checks the current owner, including rows hidden by a filter. A rejected rename preserves the whole owner; choose another key and retry. The same rule applies to private local lists.

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. A plain eager name for the stored path also preserves ownership: Lines = Order.shipping.lines can feed Table Lines or a reusable LineItems(Lines) element. Chained names such as Shipping = Order.shipping and Lines = Shipping.lines keep the same original owner and validation. The named expression remains a calculation, not a new input. See the stored-path alias example. A two-argument FILTER also preserves whole-record ownership: Lines = FILTER(Order.shipping.lines, line => line.amount > 0) can feed the same editor. Boolean masks work too. Filters may be chained or used as the source of a keyed FOR that contains child editors. The compiler follows at most 32 filters; three-argument fallback filters and row-shaping calculations do not acquire editable ownership. See the filtered rows example.

A filter controls which rows are displayed. As with FOR … IF, it does not constrain writes: an edit can move a row out of the result, and a captured edit can still address a row whose filter membership changed. The current owning collection must contain exactly one matching key at every level. Missing or duplicate keys reject the edit, including duplicates outside the filtered result. The original owner's validation always checks the proposed value. Inputs used only by validation bounds, including computed bounds, travel with the native rule's event snapshot just like inputs used by the action itself.

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
END

Each 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. Scalar columns use the same native transport for root lists, stored paths, aliases and filtered rows. Editing an amount keeps its currency or unit; exact numbers and dates retain their native text. A type change during an edit keeps the draft and requires Reapply my value, using the draft's opening type. Blank or error scalar fields can be repaired using the declared column type. The inherited row formats example includes a currency column whose edits retain USD. 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.

For native model repeats that own nested edits, row-local state and child table drafts retain the original key carrier. Changing a parent's exact numeric key to text with identical displayed digits creates a separate instance. Returning the original native key restores its own state. This also applies to explicit stored keys such as a batch's code; read-only repeats still allow unkeyed rows.

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.

Pickers over compiler-proven native record lists keep exact numeric keys, dates and semantic tags when writing a model selection. Multiple selections retain each key through range selection, removal and Form submission. The selected field is separate from the row identity used by editable cells: selecting name does not change an edit's owning id. See the exact asset keys example. Private browser-local pickers keep their literal-value behavior.

Newly compiled native model pickers use the model's equality rules for selected rows, removal and range selection. A bigint and an equal ordinary integer match; text with the same digits stays separate. Editable rows also check duplicates using native equality. Row retention and range anchors keep the owning row ID separate from the selected field, so repeated selected values do not move an anchor to an earlier row. See the reading equality example. Read-only native rows without a separate row ID retain the clicked row within the current source snapshot. Repeated selected values do not move the range anchor; replacing that snapshot retires the anchor. See the unowned readings example.

If another control in the same Form stages a plain value for an admitted model picker, the model previews its converted selection and equality evidence together. The proposal stays staged until Submit. Replacing or discarding the draft retires that preview. A host without native preview support refuses the interaction rather than guessing how the value should compare.

If native key evidence is missing or still loading, the View refuses the edit or selection until that evidence is available. Key evidence and scoped parent publications have a separate bounded admission budget; a large collection does not consume a single field's edit allowance. Individual structured edits keep their existing size and depth limits.

If another control in the same Form stages a plain value for an admitted model picker, the model previews the converted selection and equality evidence together. The proposal stays staged until Submit. Replacement or discard retires the preview; a host without native preview support refuses interaction. Proven stored collections inside keyed repeats keep their parents through reordering, with refusal for missing, duplicate or changed parent keys. See the nested reading example. Tree row slots retain this preview path through their native occurrence keys, including Trees inside keyed repeats. See the Tree reading example. Sibling pickers reading the same projection share their pending model preview. Leaving one picker does not cancel the others; stale model responses refuse interaction and are never reused as a later preview.

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, remembered, 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. The runtime checks a requirement over model values again when the action runs. If it no longer holds, the run is refused with the explanation and nothing changes. This covers a value that changed after the page rendered, and an action that arrived from an agent or another client. A requirement that reads a browser-local value is checked in the browser only.

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. A page's own locals, reusable elements and named actions move and are removed with it. Declarations that belong to the whole View stay in the source, including 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 declares reviewNote before the first page, so it remains a single shared local. Its flat Text placement belongs to Details and is copied with that page. A page's own locals, elements and named actions are copied with it; declarations that belong to the whole View, 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. For a model input, given Name = BLANK does the same: Grid treats a blank input write as a clear, so setup cannot stage a blank model input whose default is not blank. A CHECK reaches that state by pressing an action whose body runs CLEAR Name, which pins a blank override. A local does hold 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.

Use type "Ada" in textbox "Name" when a CHECK must keep focus and leave text as a draft. Follow it with key "Enter" in textbox "Name" to reproduce an Enter commit, or key "Escape" in textbox "Name" to cancel the draft. enter continues to fill the field and move focus away. Key steps accept Enter, Escape, the four arrows, Home, End, Tab and Space; they do not accept executable key sequences or modifier combinations. See the keyboard draft example.

Tree checks address named rows as treeitem, for example key "ArrowLeft" in treeitem "Root draft" and press treeitem "Root draft". A named Tree container can supply a tree control expectation. Recording retains row navigation and selection gestures; ambiguous row labels require distinct accessible names before capture.

Authored controls rendered outside the preview canvas in a React-owned portal cannot be captured. Their gestures explicitly invalidate the recording before running the control action. Review pauses those controls as well as the canvas; recorder tools remain available. Describe such interactions in Code.

Commit or discard existing Form drafts before starting a recording. Value expectations describe committed model/private values; a staged target refuses value capture. Use a reviewed control-value expectation to assert the visible draft, or commit/discard it before adding the value expectation.

Interaction recording also retains initialized private locals inside repeated rows, including state kept while a row is off-page. Use Refresh values after admitting or restoring rows to review their current expectation targets. Generated CHECK targets use local "declaration" in row "captured instance": both strings come from the compiled View and its exact row occurrence. Keep these addresses intact; they are not row numbers. Setup restores the model inputs, global locals and page before seeding row-local values. A value expectation requires the addressed row to have rendered in the copy; seed data alone cannot satisfy it. Showing another row does not borrow its state. A changed repetition boundary or missing local fails admission. Try recording the per-row draft example.

Nested row bodies can read enclosing row fields through their lexical binder. For example, a control under FOR batch IN order.batches can use label order.id. The compiler identifies the owning row boundary; repeated child keys under other parents do not change that binding. Computed model-row expressions remain native Grid expressions, with their enclosing parameters retained. Private row sources keep their existing restriction to stored fields or explicit JavaScript glue for computation.

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.

Builder mode over View source

Code's Builder mode arranges a View by direct manipulation, and every gesture still ends as ordinary Grid source. Each gesture sends one operation for the draft the compiler last analyzed; the compiler writes the candidate source, analyzes it again and refuses anything it would not accept, with its own reason. An accepted gesture is one editor Undo step, and saving in Code applies it to the model as usual. Comments attached to a moved or wrapped element travel with it, and embedded JavaScript and CSS payloads keep their exact text. Dragging, moving a selection and Wrap in are offered while Builder mode is on, the connected host applies View transactions and the analysis matches the current draft; otherwise the outline keeps its static controls. Property fields need a host that applies source edits, as they do in Code mode.

Gesture What it writes
Drag an outline row One move of that element's block. The top quarter of the target row places it before, the bottom quarter after, and the middle inside a container. A drop into the dragged block itself, or one that leaves it where it is, is refused; Escape cancels the drag.
Drag a choice from Add element or page One insertion at the highlighted place, written exactly as the insertion editor writes it.
Drag a region onto a cell of a named-region layout The region assignment in the ARRANGE matrix; the region's content stays where it is.
Select several rows (Shift-click, or Command/Ctrl-click), then drag them or choose Move to One move of the whole selection, landing together in outline order as a single source edit. A selected element inside another selected element travels with its parent. An element that owns definitions or page content moves on its own, after a review.
Wrap in a Section, Form or named region layout A new container line above the selected run of consecutive siblings, with the run indented beneath it: "New section", Form, or ARRANGE [main] with a main: label. A page cannot be wrapped.
Drop on a page row The block moves into that page, after its last line and in the page's own spelling (flat or indented). A block that reads a local of its current page cannot leave it.
Subject in a placed line's properties Only the subject name of show x, edit x, Kind x or a bare x. The label, phrases and guards stay. The compiler resolves the new name and refuses a target the line cannot write or an ambiguous name. When the new subject would lower the line to a different kind of element, it asks you to edit the line in Code instead.
Page title and Page path in a page's properties The AS "Title" and AT "path" clauses of the PAGE line, adding the clause when the page uses its default. CHECK steps that select the page by title follow the new title; navigation by page name is unchanged. A page with parameters keeps one :parameter per parameter in its path.
Theme in the View's properties The surface's THEME line only (see below).
Shell drawer in Add element or page A DRAWER left or DRAWER right block before the first PAGE. Deleting the drawer's outline item removes it with its lines. Drawer title in its properties rewrites the header's title.

Wrap in runs as a single edit. Wrapping in a Section changes no scope and applies directly. A Form wrap is always reviewed first, because a Form stages its fields' edits and makes the arrows inside it submit them; the review shows the new enclosing block before Apply. Wrapping elements that own definitions is reviewed the same way.

input Quantity = 2
input DeliveryNote = ""
STATE submitted = FALSE

SURFACE Order
  # Wrap in → Form: both fields now stage their edits until the arrow submits.
  Form
    Quantity AS "How many?"
    DeliveryNote AS "Delivery note"
    "Save order" -> submitted = TRUE
END

The theme editor offers accent presets and a custom colour, and choices for density, corner radius, font, colour mode and content width. It changes only the tokens you edit and keeps the rest of the line's text and order. New tokens join the last top-level THEME line; when the surface has none, the editor adds one at its top level. Not set (kit default) removes a token, and removing the last token removes the line. A token whose value is a Grid expression is shown read-only, for example "Accent follows BrandColor"; edit it in Code. The compiler checks each value as it checks authored THEME: an invalid value is refused with GRID_VIEW_THEME, and low contrast is reported as the GRID_VIEW_CONTRAST warning. The editor never writes STYLE.

total IS currency = 1250

SURFACE Desk
  # The theme editor added density and font to the existing THEME line.
  THEME accent :teal density :compact font :serif
  Metric total
END

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.

Adjacent JavaScript tests run through the sibling Testing helper,

npm run test:view:javascript -- --source model.grid --source-sha256 <sha>

(tools/testing/run-view-javascript-tests.mjs). It discovers immediate sibling *.test.js, *.test.mjs, *.test.cjs and matching *.spec.* files beside the model source and runs them as ordinary node:test tests with installed Node 22 or later, under one bounded deadline. Author tests use ordinary local Node authority. The helper clears its API credential variables before running them, refuses to start if the pinned source changed, and reports source or test-file drift. Missing, skipped, passed and failed JavaScript tests remain distinct in its report. The independent CLI's gridctl surface check Desk --model MODEL_ID --source model.grid --runtime dist/frontend/grid-view-runtime --project ., which combines both phases in one report, exists only on the CLI's codex/0.66.8 line; the CLI's main offers gridctl surface explain.

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.

Theme tokens and responsive hides

THEME, once per surface at its top level or directly in a PAGE, sets the theme's fields as data: accent, density :compact | :comfortable, radius, font, mode :light | :dark | :system and contentWidth. A value may be a literal or a Grid expression; an expression is a model read like any other, delivered as the value of the matching CSS custom property, so theming follows the model with no CSS written. Beyond tokens, write real CSS in STYLE. Builder mode's theme editor writes this THEME line, never STYLE; a STYLE rule that assigns --accent, --surface-accent, --radius, --gap, --font or --content-width overrides the token and is reported as GRID_VIEW_THEME_OVERRIDE.

BrandColor = color"#2F6F9F"
total IS currency = 1250

SURFACE Desk
  # Tokens are data; the accent follows the model value.
  THEME accent BrandColor density :compact radius :soft mode :system
  Metric total
END

hide on is a universal phrase on an element line, a text line, a button or a bare placement. Its targets are :mobile, :tablet, :desktop and :print; any other target is GRID_ELEMENT_ARGUMENTS. A FOR, show or edit line does not take it: hide the element line instead (Metric total hide on :mobile), or guard the line by comparing viewer.width to a literal.

total IS currency = 1250
notes = "Quarterly figures, reviewed by finance."

SURFACE Desk
  Metric total hide on :mobile
  Text notes hide on :print
END

View theme CSS properties

The View root exposes --accent and --surface-accent for the theme accent, --radius for the selected corner radius, --gap for density spacing, --font for the selected font, and --content-width for the content width. 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"
END

See 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.

Tree hierarchies (0.68.0)

Tree presents model records with globally unique text or finite numeric keys. key, parent, and label take ordinary expressions in its row scope. A blank parent denotes a root. Roots and siblings retain source order; missing parents, cycles, duplicate keys, more than 10,000 occurrences or more than 64 levels refuse the complete update. A previous valid hierarchy stays visible and read-only.

items = [
  { id: "office", parent: BLANK, name: "Office" },
  { id: "desk", parent: "office", name: "Desk" }
]

SURFACE Outline
  local selected = BLANK
  local expanded = []
  Tree items key node.id parent node.parent label node.name
    selected selected
    expanded expanded
    row(node)
      Text node.name
END

Without a template, the label is displayed; without a label expression, model rows display their key. row(node) receives the original row fields, including native tagged values. Arrow keys navigate and expand/collapse; Home/End move to the first/last visible item; Enter/Space select. Unbound selection and expansion are temporary state for the mounted View. Explicit input/local bindings follow guarded writes and Form staging. Selection and expansion may also bind to stored fields of input or local records, such as selected state.selected and expanded state.expanded. The compiler requires an existing stored field; computed or dynamic fields are refused. Changes preserve sibling fields and use the owning input's revision. Empty text remains a valid Tree key. Removed rows and replaced native occurrences retire incompatible temporary selection and expansion; a rejected draft cannot be applied to a replacement occurrence. Scoped row locals survive collapse and are released when their occurrence is removed or their Tree unmounts. Pending validation is canceled if the addressed row disappears; write permission is checked again before native dispatch. The 0.68.0 local native-host record covers model rows, syntax/pattern/term inspection, keyboard controls, keyed state, Form/private staging and invalid-to-valid recovery. Native input updates may use ordinary JSON records; selection/expansion preserve the returned native carriers. Its browser/profile and remaining assessment limits are recorded separately.

Native syntax trees, patterns and terms use bounded inspection records with owner/occurrence identities, kind, sort, label, symbol, constructor, terminal, and childField. Equal shared subtrees at different occurrence paths remain distinct. Other tree-family authority values expose a summary; displaying one never executes it. Browser-local computed row relationships receive a staging diagnostic. Legacy App export targets report Tree as unsupported.

Opening a declared View in Builder routes to its Grid source. Existing Builder-authored App documents retain their current editor and storage. Code's Builder mode uses the same source buffer, outline, Properties, Draw preview and source Undo history as Code mode. Reopening the workspace retains the selected authored node for the same source draft. Replacing the source clears that retained selection so it cannot target a different declaration. It does not save a second graphics or UI document. Multi-operation edits validate the original source hash and all node identities together, and refuse overlapping changes as one operation. Drawing gestures retain their proposal when source analysis, editing permission, or the selected source node becomes unavailable; a late reply cannot apply the canceled proposal.

Whole-record View edits and Form checks/commits preserve existing nested native scalar tags, including mixed currencies, percentages, dates and exact strings. Untouched complex/error fields survive sibling edits; those fields remain read-only in structured editing except for explicit clearing to blank. Preparation happens at the write boundary. Unsupported native carriers or lossy field-type changes are refused with guidance to use Code; untagged arithmetic keeps its existing meaning. Numeric and string refinements keep scalar equality with their payloads: 12USD = 12 and url"https://example.test/a" = "https://example.test/a" are true. Numeric-looking text remains distinct from numbers, and symbols retain their identity. Filtering retains the selected records' tags. Offset-bearing datetimes keep their exact timestamp and temporal carrier inside records and saved input values; they are not converted to another timezone. The semantic values example shows mixed currencies, URLs and a native filter. Focused native coverage audits numeric, string, structured and spatial literal families; complete View export and interaction acceptance remains separately tracked.

The independent CLI provides gridctl surface explain <model-id> <surface> (and active-model shorthand). It asks the model-aware native compiler for source ownership, presentation, capabilities and diagnostics, with the usual human and JSON output modes. It does not write inputs or source.

See the 0.67.1 completion record for the remaining interaction, export, performance and product-journey work.

Complete View printing in 0.68.0

Complete View output includes overlay values and available native semantic labels, while artwork SVG excludes interactive overlays. Printing hides the sibling Code workspace. Contained drawings fit the actual page width; fit "actual" retains logical size. Export does not save private drafts or write model inputs.

Inspect saved native workbenches

A View can embed an existing Train job, Evaluate run, Process definition, Ledger accounts/journal pair, or Symbolic worksheet using literal owner identities:

SURFACE ReviewDesk
  Train saved job "job-1"
  Evaluate saved run "run-1"
  Process "Review"
  Ledger accounts "Accounts" journal "Journal"
  Symbolic symbols ["Expression"]
END

These embeds inspect their native owners. Opening or printing them starts no training, evaluation, process execution, posting, or algebra action. Missing saved identities remain explicit. Dynamic identities and operation callbacks are refused. The enclosing View does not pass model-writing capabilities to these inspection nodes. Standalone workbenches remain the place to perform operations. Framework exports retain explicit unsupported-native-surface errors; an inspection embed does not export its native engine to a browser. See examples/canonical/view-saved-workbenches.grid.

Assistants and Process view access

A view with supported actions offers Let assistants act in this view in its ask box. Access starts disabled. Turning it on permits an assistant to use the open view's supported actions and selections for the current tab session. Actions retain their input validation, availability requirements, and history attribution. Browser agents use WebMCP where the browser provides it; actions that require a person's confirmation are not offered through that interface.

A Process shares a model view with a role using SHARE VIEW <view> WITH <role>. Add TO ACT to permit supported actions as well as reading. Process Start lets you select views within those source-defined permissions and choose Read or Act for a view that permits acting; Read is the default. UNSHARE VIEW <view> WITH <role> withdraws both kinds of access.

The backend must select the hosted view endpoint with GRID_VIEW_TOOLS_HTTP=1 to start a Process with view grants. Grants last for that run, and actions that need a person's confirmation are not offered to roles. An interrupted command is retained as interrupted and is never automatically resent.

Fixed-vocabulary name conflicts in reusable definitions are checked even without a placement. For example, ELEMENT Conflicted(Stack) with a bare Stack body is ambiguous between the parameter and the standard element. Write show Stack to select the value, or rename the parameter. This check does not instantiate or execute the unused definition. Binding-dependent body checks still occur at placement.