crates/nornir-saga/src/ui/mod.rs
//! # `nornir-saga::ui` — live UI-movement viewer + deterministic replay
//!
//! View live UI movements from a saga (an Elm `Msg` stream) and **replay them so
//! a web page actually moves**. This is the consumer/viewer/replay side of a
//! saga-enabled Njord (the Njord emitter is a separate, coordinated change — see
//! `.nornir/nornir-saga.md`); everything here is demoable with **synthetic
//! sagas**, no live Njord and no real browser required.
//!
//! ```text
//! saga-enabled app (facett Elm component OR Njord-alike)
//! │ ui::tap(&msg) ← the ONE dev-only tap at the Elm `update` choke point
//! │ (feature `saga`; compiled OUT of release)
//! ▼
//! ui::capture buffer ── drain_saga() ──▶ Saga (Vec<TimedUiMsg>) ← the contract
//! │
//! ┌───────────────────────────────────┼──────────────────────────────┐
//! ▼ LIVE ▼ REPLAY ▼ DRIVE REAL PAGE
//! SagaSource → Viewer (ELM) Player (seek/scale/ WebReplayDriver
//! fold → state_json deterministic) → frames (Mock · Noop · WebDriver)
//! │ │ │
//! └────────────── web frontend (axum + SSE, feature `web`) ───────────┘
//! ```
//!
//! ## Pieces
//! * [`msg`] — the **contract**: [`UiMsg`] / [`TimedUiMsg`] / [`Saga`].
//! * [`capture`] — the DEV-ONLY **tap** ([`tap`] / [`SagaMsg`] / [`drain_saga`]),
//! gated behind the off-by-default `saga` feature (release ships nothing).
//! * [`emit`] — the pluggable **emit seam** ([`Emitter`] + [`UiSink`]:
//! [`VecSink`] / [`ChannelSink`] / [`NullSink`]) a host feeds its `Msg` stream
//! through (LIVE via [`ChannelSink`] → [`ChannelSource`]).
//! * [`viewer`] — the **ELM viewer** ([`Viewer`] / [`ViewerModel`] / `state_json`).
//! * [`source`] — the **source seam** ([`SagaSource`], [`RecordedSource`],
//! [`ChannelSource`]).
//! * [`player`] — the **replay engine** ([`Player`]: seek / time-scale /
//! deterministic [`Frame`]s).
//! * [`driver`] — the **[`WebReplayDriver`]** seam ([`DriveAction`],
//! [`MockDriver`], [`NoopDriver`], [`drive_saga`]; real WebDriver behind
//! feature `drive`).
//! * [`synthetic`] — [`demo_saga`] plus the **wire-against fixture**
//! ([`SyntheticApp`] / [`wire_synthetic`] / [`wired_saga`]): a saga-enabled ELM
//! app that emits a scripted `Msg` stream through the emit seam, proving
//! capture → replay end to end with no real Njord.
//! * [`web`] — the axum + SSE frontend (feature `web`).
pub mod capture;
pub mod driver;
pub mod emit;
pub mod msg;
pub mod player;
pub mod source;
pub mod synthetic;
pub mod viewer;
#[cfg(feature = "web")]
pub mod web;
/// **Njord-support replay primitives** (feature `web`): the DOM query/assert
/// surface ([`njord::DomAssertDriver`]), the PII-redacted input-injection seam
/// ([`njord::RedactedInputs`] / [`njord::inject_inputs`]), and the blessed
/// `data-saga` / `data-saga-field` locator convention. Gated HARD behind `web`
/// so the lean core (and Njord's own build) link none of it.
#[cfg(feature = "web")]
pub mod njord;
pub use capture::{capture, drain_saga, tap, SagaMsg};
pub use driver::{drive_plan, drive_saga, DriveAction, MockDriver, NoopDriver, WebReplayDriver};
pub use emit::{ChannelSink, Emitter, NullSink, UiSink, VecSink};
pub use msg::{saga_to_ui, Saga, TimedUiMsg, UiMsg};
pub use player::{Frame, Player, ReplayVerdict};
pub use source::{ChannelSource, RecordedSource, SagaSource};
pub use synthetic::{
demo_saga, synthetic_movement_count, synthetic_movements, synthetic_script, wire_synthetic,
wired_saga, AppMsg, SyntheticApp,
};
pub use viewer::{Counts, Point, Viewer, ViewerEffect, ViewerModel, ViewerMsg};