Skip to main content

windows-a2ui

Generate A2UI v0.8 surfaces for the OpenClaw Windows tray node, which renders A2UI natively in WinUI/XAML (not a WebView). Read this when pushing canvas.a2ui.push/canvas.a2ui.pushJSONL to a Windows node - covers the 18 supported components (Row, Column, List, Card, Tabs, Modal, Divider, Text, Image, Icon, Video, AudioPlayer, Button, CheckBox, TextField, DateTimeInput, MultipleChoice, Slider), the icon-name enum, the A2UIValue tagged union, JSONL envelope shapes, and action.context security / dataBinding rules. Catalog-strict: components outside this list render as the fallback Unknown placeholder.

Informations de source

Dépôt
openclaw/openclaw-windows-node
Dernière activité de la source
7 août 2026 à 01:09
Langue détectée de SKILL.md
anglais
Étoiles
2 148
Forks
313

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
windows-a2ui
description
Generate A2UI v0.8 surfaces for the OpenClaw Windows tray node, which renders A2UI natively in WinUI/XAML (not a WebView). Read this when pushing canvas.a2ui.push/canvas.a2ui.pushJSONL to a Windows node - covers the 18 supported components (Row, Column, List, Card, Tabs, Modal, Divider, Text, Image, Icon, Video, AudioPlayer, Button, CheckBox, TextField, DateTimeInput, MultipleChoice, Slider), the icon-name enum, the A2UIValue tagged union, JSONL envelope shapes, and action.context security / dataBinding rules. Catalog-strict: components outside this list render as the fallback Unknown placeholder.
# Windows Node A2UI The Windows tray node (`OpenClaw.Tray.WinUI`) implements an A2UI v0.8 renderer in **native WinUI 3 / XAML**. Each A2UI component maps to a real `FrameworkElement` (StackPanel, Border, TabView, TextBox, Slider, etc.) - there is no WebView, no Lit, no shadow DOM. The renderer is **catalog-strict**: a `surfaceUpdate` referencing a component name not in the table below renders as a single Unknown placeholder, not as an attempted best-effort. Use this skill whenever the user asks the agent to push UI to a Windows node, or whenever you'd otherwise generate a generic A2UI payload - Windows specifics (component set, icon enum, secret-path rules) differ from the Lit reference renderer. ## Wire shape A2UI is delivered to the node via the gateway RPC `canvas.a2ui.push` (or the alias `canvas.a2ui.pushJSONL`), with a single string parameter `jsonl` holding **newline-separated JSON envelopes**. Surfaces are torn down with `canvas.a2ui.reset` (no parameters). Four envelope kinds, sent in order: ```jsonl {"surfaceUpdate":{"surfaceId":"main","components":[ /* component defs */ ]}} {"dataModelUpdate":{"surfaceId":"main","path":"/","contents":[ /* entries */ ]}} {"beginRendering":{"surfaceId":"main","root":"<root-component-id>","catalogId":"a2ui-v0.8","styles":{}}} {"deleteSurface":{"surfaceId":"main"}} ``` - `surfaceUpdate` declares (or replaces) the component set for a surface. There is no separate createSurface envelope in v0.8 - the surface is implied by its first surfaceUpdate. - `dataModelUpdate` writes typed entries into the surface's data model. `path` scopes the keys; omit (or use `"/"`) to replace the whole model. - `beginRendering` picks the root component for the surface and applies optional styles. Send it after the surfaceUpdate that introduces the components. - `deleteSurface` tears a surface down. Use `canvas.a2ui.reset` if you want every surface gone. Lines exceeding 1 MiB are dropped server-side. Malformed lines are logged and skipped - they do not abort the stream. ## Component definition shape Each entry of `surfaceUpdate.components`: ```json { "id": "btnPrimary", "component": { "<ComponentName>": { /* properties */ } }, "weight": 1.0 } ``` - `id` is the surface-unique handle used as a child reference and as `sourceComponentId` on outbound actions. - `component` is a one-key wrapper whose key is the discriminator. Names are case-sensitive (the registry is loaded with `OrdinalIgnoreCase` lookup, but match the wire exactly). - `weight` is optional and currently ignored by the Windows renderer's StackPanel layout. ## A2UIValue (the literal-or-bound tagged union) Almost every component property accepts a value of this shape: ```json { "literalString": "Hello" } { "literalNumber": 42 } { "literalBoolean": true } { "literalArray": ["a", "b"] } { "path": "/user/name" } ``` A `path` is a JSON Pointer into the surface's data model. The renderer subscribes to the path and re-renders the affected element on `dataModelUpdate`. Two-way bindings (TextField, CheckBox, Slider, DateTimeInput, MultipleChoice) write back to the same path on user interaction. ## Data model entries `dataModelUpdate.contents[]` items: ```json { "key": "name", "valueString": "Alice" } { "key": "count", "valueNumber": 3 } { "key": "enabled", "valueBoolean": true } { "key": "user", "valueMap": [ { "key": "first", "valueString": "Alice" }, { "key": "last", "valueString": "Smith" } ] } { "key": "tags", "valueArray": [ { "valueString": "admin" }, { "valueString": "beta" } ] } ``` Exactly one of `valueString` / `valueNumber` / `valueBoolean` / `valueMap` / `valueArray` should be set per entry. `valueMap` is a recursive adjacency list (each item is itself an entry). `valueArray` is an ordered list whose items are value-typed entries with no `key` (they may be scalars, maps, or nested arrays) - this is the first-class way to seed an array the renderer consumes, e.g. `MultipleChoice.selections`. Bare-primitive arrays (`["a","b"]`) are also tolerated. ## Components ### Containers | Component | Children | Properties (Windows-renderer) | |---|---|---| | **Row** | `children: { "explicitList": ["id1","id2",...] }` | `distribution`: `start`/`center`/`end`/`spaceBetween`/`spaceAround`/`spaceEvenly` (main-axis); `alignment`: `start`/`center`/`end`/`stretch` (cross-axis). Renders as horizontal `StackPanel`; default spacing 8. | | **Column** | `children: { "explicitList": [...] }` | Same as Row, vertical. | | **List** | `children: { "explicitList": [...] }` | `direction`: `vertical` (default) or `horizontal`; `alignment`. Wrapped in a `ScrollViewer`. **Template/data-bound rows are not supported in v1** - supply explicit child ids. | | **Card** | `child: "<id>"` (single string, not explicitList) | None. Renders as a themed `Border` with 16 px padding and rounded corners. | | **Tabs** | per-tab via `tabItems` | `tabItems: [{ "title": <A2UIValue>, "child": "<id>" }, ...]`. Renders as a `TabView` with non-closable, non-reorderable tabs. | | **Modal** | `entryPointChild: "<id>"`, `contentChild: "<id>"` | `title`: optional `A2UIValue<string>`. Renders as a native `ContentDialog` triggered by the entryPointChild; the contentChild becomes the dialog body. | | **Divider** | - | `axis`: `horizontal` (default) or `vertical`. Renders as a 1 px rectangle. | Children are referenced by **id**, not nested in place. Build the surface as a flat array of components and let `beginRendering.root` pick the entry point. ### Display | Component | Properties | |---|---| | **Text** | `text`: A2UIValue; `usageHint`: `h1`/`h2`/`h3`/`h4`/`h5`/`body` (default) /`caption`. Maps to WinUI text styles (`TitleLargeTextBlockStyle` etc.). Wraps. | | **Image** | `url`: A2UIValue; `fit`: `contain`/`cover`/`fill`/`none`/`scale-down` (defaults to `contain`); `usageHint`: `icon` (24 px square), `avatar` (40 px square), `smallFeature` (h=80), `mediumFeature` (h=160), `largeFeature` (h=240), `header` (stretch). `description` or `label` is used as the automation/alt name. Subject to MediaResolver allowlist. SVG is supported via the same `url` slot. Accepted as `data:image/svg+xml;base64,…` (≤ 2 MiB) or as `https://allowed-host/…svg` (host must be in `Settings.A2UIImageHosts`). The renderer uses Direct2D's static SVG 1.1 subset - no scripts, no animation, no `<foreignObject>`, no external references. Self-contained SVGs only. | | **Icon** | `name`: A2UIValue resolving to an entry in the icon enum below. | | **Video** | `url`: A2UIValue. Renders as `MediaPlayerElement` with transport controls. URL must pass MediaResolver allowlist. | | **AudioPlayer** | `url`: A2UIValue; optional `description`: A2UIValue (caption above the player). | | **Divider** | (listed under Containers) | #### Icon enum The Windows renderer recognizes only the v0.8 Material-derived icon-name enum, mapped onto Segoe Fluent Icons glyphs. Anything outside this set falls back to the Help glyph: ``` accountCircle, add, arrowBack, arrowForward, attachFile, calendarToday, call, camera, check, close, delete, download, edit, event, error, favorite, favoriteOff, folder, help, home, info, locationOn, lock, lockOpen, mail, menu, moreVert, moreHoriz, notificationsOff, notifications, payment, person, phone, photo, print, refresh, search, send, settings, share, shoppingCart, star, starHalf, starOff, upload, visibility, visibilityOff, warning ``` `moreHoriz` falls back to the same glyph as `moreVert` (no horizontal-three-dots in MDL2). Use one of these names verbatim - arbitrary glyph strings will not work. ### Interactive | Component | Properties | |---|---| | **Button** | `child`: `<id>` of the label/icon content; `primary`: bool (accent style); `action`: `{ "name": "<actionName>", "context": [{ "key":"...", "value":<A2UIValue> }, ...] }`. Click raises an outbound A2UIAction (see below). Auto-applies `action.name` as the accessibility name when no label/description is set. | | **CheckBox** | `label`: A2UIValue; `value`: A2UIValue (boolean). When `value.path` is set, toggling the checkbox writes the new boolean back to the path. | | **TextField** | `label`: A2UIValue; `text`: A2UIValue; `textFieldType`: `shortText` (default), `longText` (multiline TextBox, min height 80), `obscured` (PasswordBox - see secret-path rules below), `number` (numeric InputScope), `date` (date InputScope). When `text.path` is set, edits write back. | | **DateTimeInput** | `value`: A2UIValue (ISO 8601 string round-trip); `enableDate`: bool (default `true`); `enableTime`: bool (default `false`); `outputFormat`: optional .NET format string applied to the writeback (defaults to ISO 8601 `o`). | | **MultipleChoice** | `options`: `[{ "label": <A2UIValue>, "value": "<string>" }, ...]`; `selections`: A2UIValue resolving to an array of selected `value` strings; `maxAllowedSelections`: int. **`maxAllowedSelections == 1` renders as a `ComboBox` (single-select)**; everything else renders as a multi-select `ListView`. The path is always written back as a JSON array. | | **Slider** | `value`: A2UIValue (number); `minValue` (default 0); `maxValue` (default 100); `step` or `stepSize` (default 1.0) - wired to `Slider.StepFrequency`. | Any property accepting an A2UIValue accepts both literals and `path` bindings interchangeably - pick `path` whenever the value should react to `dataModelUpdate`. ## Outbound actions When a Button is clicked (the only action source in v1), the node sends an A2UIAction back over the gateway: ```json { "name": "submitForm", "surfaceId": "main", "sourceComponentId": "btnPrimary", "context": { "email": "alice@example.com" }, "timestamp": "2026-04-27T18:12:34.000Z" } ``` `context` is the flattened result of the button's `action.context` array, with each entry's value resolved (literal or path read). ### Action-context security (dataBinding) The renderer scopes which paths are allowed to flow into `context`: - **Explicit scope (preferred):** declare `dataBinding` on the source component as an array of paths or `{ "path": "..." }` objects. Only those paths (and their subtrees) can be read into `context`. A `dataBinding: ["/"]` opts in to everything. - **Implicit scope (fallback):** if no `dataBinding` is set, the scope is the union of all paths referenced by the component's *non-action* properties. Useful but easy to misjudge. - **Secret paths** (any path bound to a `TextField` of type `obscured`) are dropped from `context` unless `dataBinding` lists them **explicitly**. The implicit scope never picks up secrets, even if they're trivially within reach. Paths outside the allowed set are silently dropped from the action envelope. If you need a value to round-trip back to the agent, either bind a property to it on the source component, or list it in `dataBinding`. ## Capability gate Three RPCs are involved, all of which must be on the gateway's `gateway.nodes.allowCommands` list (and the node must advertise them via `node.describe`): ``` canvas.a2ui.push canvas.a2ui.pushJSONL canvas.a2ui.reset ``` The Windows node advertises all three. The richer v0.9 envelopes (e.g. `canvas.a2ui.schema`, `canvas.a2ui.dump`) are **not** implemented; do not attempt them. ## Worked example Sign-in form with a path-bound text field and a primary button: ```jsonl {"surfaceUpdate":{"surfaceId":"main","components":[ {"id":"root","component":{"Column":{"alignment":"stretch","children":{"explicitList":["title","emailField","pwField","submit"]}}}}, {"id":"title","component":{"Text":{"text":{"literalString":"Sign in"},"usageHint":"h2"}}}, {"id":"emailField","component":{"TextField":{"label":{"literalString":"Email"},"text":{"path":"/form/email"},"textFieldType":"shortText"}}},
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub