# dusk:snap Capture the Semantics tree of the running Flutter app as YAML, tagging every interactive node with a `[ref=eN]` token. `dusk:snap` is the foundational read command: every action command (`dusk:tap`, `dusk:type`, `dusk:drag`, etc.) consumes one of its `eN` refs to locate the target widget. The `eN` namespace is snapshot-frozen: every fresh `dusk:snap` clears the registry and mints new tokens. For long-lived handles that survive rebuilds, use `dusk:find` (which mints `qN` tokens) or `dusk:observe`. Interactive nodes inside a currently-overflowing render ancestor carry an additive `overflow: true` sub-line in the YAML output. This is a live current-state check (`RenderFlex.toStringShort()` appends ` OVERFLOWING` while the constraint is violated); it is not a historical record. For the full non-fatal error history including overflow details, use `dusk:exceptions`. Text fields collapse to a single typeable ref. A field such as a wind `WInput` produces two nested `textbox` Semantics nodes (the outer wrapper plus the `RenderEditable` leaf that always owns its own node). `dusk:snap` suppresses the inner duplicate when its render object is a render-tree descendant of the outer `textbox` node's render object, emitting one ref for the surviving node and tagging it with an additive `typeable: true` sub-line. Collapse is by render-object containment only, so two sibling fields sharing the same label stay two distinct refs. Target the node carrying `typeable: true` when typing: `dusk:type` resolves the inner `EditableText` from it. --- ## Table of contents - [Synopsis](#synopsis) - [Arguments](#arguments) - [Returns](#returns) - [Enricher fragments](#enricher-fragments) - [Examples](#examples) - [See also](#see-also) --- ## Synopsis ``` dart run fluttersdk_dusk dusk:snap [--depth=] [--includeEnrichers] ``` `dusk:snap` requires a running Flutter session (`CommandBoot.connected`). It dials the VM Service URI recorded in `~/.artisan/state.json`, calls `ext.dusk.snap`, and prints the snapshot YAML to stdout. --- ## Arguments | Option | Type | Default | Description | |--------|------|---------|-------------| | `--depth` | int (string-parsed) | unset (full tree) | Optional max tree depth. Caps the walk so very deep widget trees stay readable. Omit for the full tree. | | `--includeEnrichers` | flag | `false` | Emit Magic and Wind enricher fragments under each ref entry. Default off matches the Playwright-style minimal snapshot; turn on when the agent needs the className tokens, route name, or form field metadata. | The flag is parsed via `(ctx.input.option('includeEnrichers') as bool?) ?? false` and serialised as a string into the VM Service params map. --- ## Returns The VM Service handler returns a JSON envelope `{ "snapshot": "" }`. The CLI unwraps the `snapshot` field and writes the raw YAML to stdout; when the field is missing the entire JSON object is dumped instead. **Success envelope (illustrative):** ```yaml [ref=e1] role=button label="Sign in" rect=(120,400,120,48) actions=[tap] [ref=e2] role=textbox label="Email" rect=(20,200,335,56) actions=[tap, focus, type] typeable: true [ref=e3] role=text label="Welcome back" rect=(20,80,335,32) ``` The `typeable: true` sub-line marks the single surviving `textbox` node after the nested-field collapse described above; it is the node `dusk:type` resolves. When `--includeEnrichers` is set, each entry gains indented lines contributed by the registered enrichers (see [Enricher fragments](#enricher-fragments)). **Error envelope:** The VM Service handler propagates errors as `ServiceExtensionResponse.error(extensionError, message)`. The CLI surfaces the exception via `ArtisanContext.callExtension` and exits with a non-zero status. Typical failure modes: - No running app at the recorded URI (the artisan dispatcher reports the dial failure before `dusk:snap` runs). - `DuskPlugin.install()` not wired in `lib/main.dart` (run `dusk:install`). --- ## Enricher fragments When `--includeEnrichers` is true, every `DuskSnapshotEnricher` appended to the `DuskPlugin.enrichers` live list contributes indented lines under each ref. The two first-party enrichers: - **MagicDuskIntegration** ships seven enrichers covering forms, routes, controllers, models, http, cache, and the policy gate. - **Wind alpha-10** surfaces a six-field `wind:` block (breakpoint, brightness, platform, states, bgColor, textColor) through the neutral `fluttersdk_wind_diagnostics_contracts.WindDebugRegistry` bridge rather than via an enricher, so the data appears under each W-prefixed widget ref without any enricher registration. Each enricher is synchronous and stateless; the `Element` reference is never retained across calls. ### Overflow annotation The `overflow: true` sub-line is emitted by the snapshot dispatcher itself (not via an enricher) when the resolved `RenderObject.toStringShort()` contains the Flutter debug suffix ` OVERFLOWING`. It appears for any interactive node whose nearest render ancestor is currently violating its constraints, regardless of whether enrichers are enabled. Example output: ```yaml [ref=e5] role=button label="Submit" rect=(0,0,320,48) actions=[tap] overflow: true ``` ### Typeable annotation and textbox collapse A text field commonly produces two nested `textbox` Semantics nodes: an outer wrapper (e.g. a wind `WInput`'s `Semantics(textField:true)`) and the `RenderEditable` leaf, which always owns its own `textField` node and which `MergeSemantics` cannot absorb. Both nodes used to mint a ref; typing on the inner leaf threw `-32000`. The snapshot walk now suppresses the inner node when its render object is a render-tree descendant of the outer `textbox` node's render object, leaving one ref for the surviving node and appending `typeable: true` beneath it: ```yaml [ref=e2] role=textbox label="Email" rect=(20,200,335,56) actions=[tap, focus, type] typeable: true ``` The collapse is by render-object containment only, never label/value equality, so two sibling fields sharing a label remain two distinct refs. The `typeable: true` node is the one `dusk:type` resolves to the inner `EditableText`. --- ## Examples ### 1. Minimal snapshot of the current screen ```bash dart run fluttersdk_dusk dusk:snap ``` Expected output (illustrative; truncated): ```yaml [ref=e1] role=text label="Monitors" rect=(20,80,200,32) [ref=e2] role=button label="New monitor" rect=(20,140,335,48) actions=[tap] [ref=e3] role=button label="Settings" rect=(355,140,40,48) actions=[tap] ``` ### 2. Snapshot with depth cap ```bash dart run fluttersdk_dusk dusk:snap --depth=4 ``` Caps the walk at four levels of nesting. Useful for very dense screens (long lists, complex forms) where the full tree is noisy. ### 3. Snapshot with enrichers turned on ```bash dart run fluttersdk_dusk dusk:snap --includeEnrichers ``` Expected output (illustrative): ```yaml [ref=e2] role=button label="New monitor" rect=(20,140,335,48) actions=[tap] magicRoute: /monitors windClassName: bg-primary-600 dark:bg-primary-500 text-white ``` --- ## See also - [dusk:tap](dusk-tap.md): consume an `eN` token to synthesise a tap. - [dusk:find](dusk-find.md): mint a long-lived `qN` handle that survives rebuilds. - [dusk:observe](dusk-observe.md): structured candidate list when the agent needs more than the raw Semantics tree. - [Plugins: Enricher authoring](../plugins/enricher-authoring.md): how to ship a custom enricher that contributes additional fragments.