# `DemoDirector`
[🔗](https://github.com/ralmidani/demo_director/blob/v0.1.1/lib/demo_director.ex#L1)

Direct AI-driven product demos for Phoenix apps, right from your
Tidewave Web tab.

DemoDirector gives an AI agent (or, eventually, a saved script)
the seams to drive a Phoenix LiveView application as a guided tour:
subtitled explanations appear in an overlay, the next element to
interact with is highlighted, typing is slowed enough to read, and
selectors stay stable via the `data-demo-id` convention.

## Concept

The package is intentionally small. It does not run demos itself —
it produces JavaScript strings that an agent passes to Tidewave's
`browser_eval` (or any equivalent in-browser evaluator), plus HEEx
components and JS/CSS that render the demo overlay in the page.

Two layers:

1. **Top-level helpers in this module** (`subtitle/1`, `highlight/1`,
   `fill/2`, `fill_typed/3`, `click/1`, `wait/1`) return JS source
   strings. The agent drives the demo by emitting them in sequence.
2. **Overlay components in `DemoDirector.Components`** render
   the subtitle bar and highlight ring. Apps mount these once on a
   layout.

## Quick start

    # In your layout (Phoenix.Component or LiveView):
    import DemoDirector.Components

    ~H"""
    <.demo_director_overlay />
    """

    # In your HEEx templates, mark interactive elements:
    import DemoDirector.HEEx

    ~H"""
    <button {demo_id("save-prescription")}>Save</button>
    """

    # Then the agent emits, in sequence, things like:
    DemoDirector.subtitle("First we'll add a diagnosis.")
    DemoDirector.highlight("save-prescription")
    DemoDirector.fill_typed("notes", "Patient stable.")
    DemoDirector.click("save-prescription")

Each return value is a JS string. The agent passes it to
`browser.eval(...)` (Tidewave) or any evaluator with a JavaScript
execution context for the page.

## Selectors

All helpers default to `data-demo-id` lookups. To target an element
the LiveView source doesn't yet expose, the agent should ask before
inventing a CSS selector — fragile selectors (`:nth-child`, deep
descendant chains) are exactly what `data-demo-id` exists to avoid.

# `demo_id`

```elixir
@type demo_id() :: String.t()
```

A demo-id string. Maps to the value of a `data-demo-id` attribute
on one or more elements in the rendered page.

# `type_opts`

```elixir
@type type_opts() :: [{:per_char_ms, pos_integer()}]
```

Options accepted by typing-driven helpers.

  * `:per_char_ms` — delay between simulated keystrokes (default: 35).

# `click`

```elixir
@spec click(demo_id()) :: String.t()
```

Clicks the element with the given demo-id.

# `fill`

```elixir
@spec fill(demo_id(), String.t()) :: String.t()
```

Fills the element with the given demo-id with `value` instantly.

Useful for fields where typing animation would distract — uuids,
prefilled fields, anything the user shouldn't be drawn to.

# `fill_typed`

```elixir
@spec fill_typed(demo_id(), String.t(), type_opts()) :: String.t()
```

Fills the element with the given demo-id one character at a time,
dispatching input events between keystrokes.

Default speed is 35ms per character. Pass `per_char_ms:` to
override:

    DemoDirector.fill_typed("note", "Patient stable.", per_char_ms: 60)

# `highlight`

```elixir
@spec highlight(demo_id() | nil) :: String.t()
```

Highlights the element with the given demo-id.

Renders a focus ring around the matching element and scrolls it
into view. Passing `nil` clears any active highlight.

# `subtitle`

```elixir
@spec subtitle(String.t() | nil) :: String.t()
```

Sets the subtitle overlay text.

Returns JS that finds the subtitle overlay (rendered by
`DemoDirector.Components.demo_director_overlay/1`) and
updates its text content.

# `wait`

```elixir
@spec wait(pos_integer()) :: String.t()
```

Pauses for `ms` milliseconds. Useful between steps to let the user
read a subtitle or watch a transition complete.

Returned JS uses `await`, so the agent must wrap its sequence in
an async function (Tidewave's `browser.eval` supports this).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
