# irid 0.3.0

## Breaking changes

* Event and binding dispatch config now rides the slot it configures via a
  single carrier, `wire(subject, timing, coalesce, dom_opts)`. The
  element-level `.event` and `.prevent_default` props are removed, along
  with `event_immediate()` / `event_throttle()` / `event_debounce()` —
  replaced by the pure timing shapes `wire_immediate()` /
  `wire_throttle(ms, leading)` / `wire_debounce(ms)` (with `coalesce`
  hoisted onto `wire`) and `wire_dom_opts(prevent_default,
  stop_propagation, capture, passive)`. A bare handler/reactive in a slot
  still works as sugar for `wire(callable)`.
* A DOM event is now bound *or* handled, never both. Combining a
  `value`/`checked` binding with an explicit `on*` handler for the same
  event (e.g. `value = rv, onInput = ...`) is an error; use
  `value = reactiveProxy(get, set)` for a synchronous write side-effect, or
  observe the bound reactive for an async reaction. Two explicit handlers on
  the same event also error (no composition).

## New features

* `iridExample()` — run a shipped example app (mirroring `shiny::runExample()`,
  source shown alongside via showcase mode by default). Called with no argument
  it lists them; missing per-example dependencies are reported up front.

* `IridWidget()` — wrap arbitrary JavaScript libraries (CodeMirror,
  Plotly, Leaflet, ...) as reactive irid components. Props are
  two-way-capable by default (symmetric with DOM `value`/`checked`): a
  callable prop reads inbound and accepts write-back when the widget JS
  calls `setProp(key, value)`. Genuine notifications flow back via `events`
  (`sendEvent`). Composes inside `When`, `Each`, and `Match` like any other
  irid construct. See `inst/examples/codemirror/app.R`.

* `PlotlyOutput()` — a plotly component built on `IridWidget()`. UI state
  (axis ranges, drag mode, selection, trace visibility) is exposed as two-way
  reactive props; discrete interactions (clicks, hovers, legend) are `on*`
  callbacks. Data changes diff via `Plotly.react()`, preserving zoom, pan, and
  selection. See `inst/examples/plotly/app.R`.

* Per-item (`Each`) and per-case (`Match`) `reactiveVal`s are now reclaimed on
  unmount, rather than leaking until session end. `make_scope` feature-detects
  [shiny#4372](https://github.com/rstudio/shiny/pull/4372)'s scoped-teardown
  API at runtime: on a Shiny that carries it, `scope$destroy()` reclaims the
  per-item / per-case observers **and** `reactiveVal`s — plus any user
  `observe()` in a component body. Pre-#4372 Shiny (including current CRAN) is
  unchanged; no irid change is required at upgrade time.

## Bug fixes

* Reactive bindings no longer flicker when user activity outpaces the
  server's echo. The optimistic-update protocol's stale-echo skip
  used to apply only to focused text inputs; it now covers all
  reactive bindings.

# irid 0.2.0

## Breaking changes

* Auto-bind: `value` and `checked` accept any callable (`reactiveVal`,
  store leaf, `reactiveProxy`, plain function) and two-way bind
  automatically. Reads populate the IDL property; DOM events write back
  through the same callable. When auto-bind coexists with an explicit
  `on*` handler on the same event, the auto-bind write always lands
  first regardless of attribute order. Works on `<input>`, `<textarea>`,
  `<select>` for `value`; checkboxes and radios for `checked`.
* `event_immediate()` / `event_throttle()` / `event_debounce()` no longer
  wrap a handler. They return a config used with the element-level
  `.event` prop. Per-handler timing is gone:

  ```r
  # OLD
  tags$input(value = field, onInput = event_debounce(\(e) field(e$value), ms = 500))

  # NEW
  tags$input(value = field, .event = event_debounce(500))
  ```

  `.event` takes a single config or a named list keyed by lowercase DOM
  event for per-event overrides.
* `prevent_default` moved off the event constructors onto the element as
  `.prevent_default`. Logical scalar broadcasts; named list overrides per
  event; unmapped events default to `FALSE`.
* `Each()` redesigned. The callback receives a per-item callable (mini-
  store for records, scalar accessor for atomics) and an optional `pos`.
  Reconciliation moves to `by`: `NULL` (default) for positional, `\(x)
  x$id` for keyed. In-place value changes update accessors without DOM
  recreation; keyed reorders move DOM nodes.
* `Match()` projects the bound value as a mini-store for the active
  case's body (records) or the bare callable (scalars). Active-case
  change fully tears down the previous case and mounts the new one fresh.
* `When()` is a fixed-shape binary specialization of `Match()`. `yes` /
  `otherwise` must be 0-arg functions returning tag trees; each
  activation builds a fresh tree.
* `Index()` removed. Covered by `Each(items, fn)` with default `by = NULL`.

## New features

* `reactiveProxy()` — callable built from a `get` reader and optional
  `set` writer for validation, transforms, and read-only views at
  component boundaries.
* `reactiveStore()` — hierarchical reactive state container. Bare named
  lists become navigable sub-nodes; everything else (including
  `I()`-wrapped lists) is a `reactiveVal` leaf. Branch writes replace
  and must list every locked key; `node$key(value)` is the single-slot
  path.
* `innerHTML` is now a DOM property, so reactive writes hit the IDL
  property directly.

## Bug fixes

* Reactive text children use a comment-anchor pair instead of a `<span>`
  wrapper, so reactive text no longer adds spurious wrappers to the DOM.

# irid 0.1.0

Initial release.

## Features

* Reactive attributes and children — pass functions to any tag attribute or
  text child for fine-grained DOM updates without re-rendering
* Controlled inputs — bind `value` directly to a `reactiveVal` for two-way
  binding without `update*Input()` callbacks
* Composable components — plain functions that accept `reactiveVal`s for
  natural state sharing
* Control flow primitives: `When()`, `Each()`, `Index()`, `Match()`
* Output bindings: `Output()`, `PlotOutput()`, `TableOutput()`, `DTOutput()`
* Event handling with `event_immediate()`, `event_throttle()`, `event_debounce()`
* Embed in existing Shiny apps via `iridOutput()` / `renderIrid()`, or build
  standalone apps with `iridApp()`
* Optimistic updates with sequence-number tracking
