# Quickstart

A three-step walkthrough from zero to driving the bundled `example/` Flutter app with a snapshot, a tap and a screenshot through the tapir CLI.

Prerequisites: Flutter 3.47 or newer (Dart 3.13, the package and example floor) and a clone of the repository. The `example/` app already calls `TapirPlugin.install()` inside `!kReleaseMode` and resolves `tapir` from the parent directory. To drive your own app instead, follow [Installation](https://anilcancakir.com/tapir/docs/v0.0/getting-started/installation.md) first.

- [1. Start the example app and capture a snapshot](#1-start-the-example-app)
- [2. Tap a widget using its ref token](#2-tap-a-widget)
- [3. Capture a screenshot](#3-capture-a-screenshot)
- [Gotchas](#gotchas)
- [Related Documentation](#related-documentation)

---

## 1. Start the example app and capture a snapshot

Start the app through dowel, so the session (the VM Service URI every `tapir:*` command dials) is recorded. The `--cdp-port` makes web screenshots reliable in step 3; on a native target (`--device=macos`, for example) leave it out:

```bash
cd example
flutter pub get
dart run tapir start --device=chrome --cdp-port=9333
```

Once the app is up, run `tapir:snap` in the same directory to walk the live Semantics tree. The example has many widgets, so narrow the walk with `--grep` to the counter button:

```bash
dart run tapir tapir:snap --grep=Counter
```

The command connects to the running app via the VM Service and prints the snapshot to stdout. An illustrative output (ancestor lines trimmed):

```yaml
- button "Counter: 0" [ref=e1]
```

Each interactive node carries a `[ref=eN]` token (here `e1`). Tokens are tied to the snapshot that issued them: a node that appears again in a later `tapir:snap` may keep its token, a node that was rebuilt gets a new one, so read the refs from the latest snapshot. They are what you pass to action commands in the next step.

---

## 2. Tap a widget using its ref token

Pass the ref token from the snapshot to `tapir:tap`:

```bash
dart run tapir tapir:tap --ref=e1
```

The extension looks up `e1` in the snapshot registry, runs the actionability gate (see [Reference: Actionability gate](https://anilcancakir.com/tapir/docs/v0.0/reference/actionability-gate.md): the widget must be enabled, have a non-zero rect, be on-viewport, stable, and receive pointer events), then synthesises a pointer-down and pointer-up pair at the widget's centre. The app responds as if a real user tapped the button.

A successful tap prints one line:

```
✓ Tapped e1
```

When nothing observable changed, the line reads `Tapped e1 (no observable change)`; read it before concluding the tap worked. Add `--json` for the full envelope, which carries the `effect` block.

If the widget fails the actionability gate, the command exits with an error message:

```
Widget ref=e1 is not actionable: not enabled
```

Re-snap after a tap to confirm the UI updated. The refreshed snapshot shows the button as `Counter: 1`; read its ref again, because it may or may not match the earlier one:

```bash
dart run tapir tapir:snap --grep=Counter
```

---

## 3. Capture a screenshot

Capture an image of the current viewport to verify the app state visually or to save a baseline:

```bash
dart run tapir tapir:screenshot --output=counter_after_tap.png
```

On web with `--cdp-port` the command captures through Chrome DevTools Protocol; on a native target it asks the app's `RepaintBoundary` for a frame. The file lands relative to your current working directory, and the format follows the `--output` extension.

Expected output:

```
✓ Wrote 48213 bytes (47.1 KB, png) to counter_after_tap.png
```

Open the file to confirm the button reads `Counter: 1`. You now have the full snap, tap and screenshot loop working end to end. Stop the app with `dart run tapir stop`.

---

## Gotchas

| Mistake | Fix |
|:--------|:----|
| Running `flutter run` by hand, then `tapir:snap` finds no app. | Start the app with `dart run tapir start`, which records the session the `tapir:*` commands dial. |
| Tapping an `eN` ref from an earlier snapshot after the screen changed. | Re-snap and use the ref from that snapshot, or mint a `qN` handle with `tapir:find` that survives rebuilds. |
| The web screenshot hangs or returns an error. | Start with `--cdp-port=<port>` so the CLI captures through Chrome DevTools Protocol. |
| The tap printed `no observable change`. | Nothing changed in the target's own subtree or the route; that does not prove the handler did not fire. Re-snap and check the intended result before retrying. |

## Related Documentation

- [Commands](https://anilcancakir.com/tapir/docs/v0.0/commands/index.md): every `tapir:*` command with its flags.
- [MCP setup](https://anilcancakir.com/tapir/docs/v0.0/mcp/setup.md): let Claude Code or Cursor call the tapir tools directly during a conversation.
- [Magic integration](https://anilcancakir.com/tapir/docs/v0.0/plugins/magic-integration.md) and [Wind integration](https://anilcancakir.com/tapir/docs/v0.0/plugins/wind-integration.md): enrich snapshots with framework-specific metadata (`MagicTapirIntegration.install()`, `Wind.installDebugResolver()`).
- [Reference: Actionability gate](https://anilcancakir.com/tapir/docs/v0.0/reference/actionability-gate.md): how tapir decides whether a widget is safe to interact with.
