Pyric
Navigate

API reference

@pyric/ui/events

38 published symbols from @pyric/ui

Generated from the TypeScript declarations shipped at this import path.

Interfaces

ActivityActionItem

A single action item — a mechanical fact that invites a decision.

Properties

PropertyTypeDescription
keystringStable key.
meta?stringSub-line — attribution / cause.
rowsActivityRow[]The rows this item summarizes — for drill-in / linking into the matching Activity band (items LINK to bands, they don’t duplicate).
titlestringThe headline (mechanical: “4 writes to /notes were denied”).
type"denied"What it is — drives data-pyric-action-type. Today: denied.

ActivityActionItemsProps

Properties

PropertyTypeDescription
className?string-
digestActivityDigest-
emptyState?ReactNodeRendered when there are no action items — default renders nothing (calm by default; the region collapses).
renderAction?(item: ActivityActionItem) => ReactNodeRender the action button/affordance for an item (e.g. a “Debug” link). Returns null to render no action.
renderTitle?(item: ActivityActionItem) => ReactNodeOverride item-title composition (host owns app-semantic copy).

ActivityAuthState

Identity in effect for an op. null is anonymous / signed out.

Properties

PropertyType
token?Record<string, unknown>
uidstring

ActivityBand

One category band: a header (label · count · attribution) plus its rows. Mirrors the mock’s .band + .r.data structure.

Extended by

Properties

PropertyTypeDescription
attribution?stringA short attribution phrase when the band has a dominant actor / subject (all by alice, by the app, by agent atlas). Absent when attribution is mixed — the header then shows just the count.
countnumberTotal rows in the band (== rows.length; explicit for the header).
keyActivityBandKey-
labelstringDisplay label — Denied, Added, Updated, …
rowsActivityRow[]-

ActivityBandWithGroups

A band that may carry pivot sub-groups. subgroups is populated only when the digest was computed with groupBy !== 'none'; otherwise the band is flat (rows only). computeActivityDigest always returns bands of this shape so consumers can branch on subgroups presence.

Extends

Properties

PropertyTypeDescription
attribution?stringA short attribution phrase when the band has a dominant actor / subject (all by alice, by the app, by agent atlas). Absent when attribution is mixed — the header then shows just the count.
countnumberTotal rows in the band (== rows.length; explicit for the header).
keyActivityBandKey-
labelstringDisplay label — Denied, Added, Updated, …
rowsActivityRow[]-
subgroups?ActivitySubgroup[]Present only when groupBy !== 'none'.

ActivityDigest

The banded digest — the activity grid’s entire model. Bands are pre-sorted lead-with-consequence; denials is a flat projection of the consequential rows for the action-items tier.

Properties

PropertyTypeDescription
bandsActivityBandWithGroups[]Bands in render order — highest-consequence first. Empty bands omitted. Each band may carry subgroups when groupBy !== 'none'.
denialsActivityRow[]The denial rows, flat + recency-sorted — the source for the action-items tier (“4 writes to /notes were denied”). A projection, not a separate aggregation: these rows also live in the denied band.
deniedCountnumberOf those, how many were denials.
totalnumberTotal rows across all bands (events the digest categorized).

ActivityDigestOptions

Extended by

Properties

PropertyTypeDescription
groupBy?ActivityGroupByPivot rows within each band by an attribution axis. 'none' (default) leaves the band flat. Any other value splits each band’s rows into sub-groups keyed by that axis — surfaced on the row via groupKey and exposed as band.subgroups. The flat rows are always present regardless.
order?"recency" | "chronological"Row order within a band. recency (default) is newest-first to match the mock’s when column. chronological is oldest-first.
rowsPerBand?numberCap rows kept per band (the mock shows ~3–4 then “N more”). The digest keeps ALL rows but records the overflow via band.count vs rows.length. Set to a number to actually trim rows; the header count still reflects the true total. Default: keep all.

ActivityGridProps

Properties

PropertyTypeDescription
className?string-
digest?ActivityDigestA precomputed digest from useActivityDigest / computeActivity Digest. Pass this when the host already memoizes the fold (so the grid doesn’t recompute). Mutually exclusive with events.
emptyState?ReactNodeShown when the digest has no rows.
events?readonly AnyActivityEvent[]The unified event stream — sandbox.history() and/or a live buffer (see useActivityStream). The grid folds it into the banded digest internally. Mutually exclusive with digest.
formatWhen?(at: number, now: number) => stringOverride the when column rendering.
maxRowsPerBand?numberClamp rows shown per band; the rest collapse into a “N more” stub. Independent of the reducer’s rowsPerBand (which trims the data) — this is a pure display clamp that keeps the true count visible. Default: show all rows.
now?number”Now” anchor for formatWhen.
onSelect?(row: ActivityRow) => void-
options?ActivityDigestOptions & { now?: number; }Grouping / ordering options, forwarded to the reducer when the grid folds events itself. Ignored when digest is supplied.
renderBandMore?(band: ActivityBandWithGroups, hidden: number) => ReactNodeRender the per-band overflow stub. Default renders a [data-pyric-band-more] element reading “N more {label}”.
selectedId?stringThe selected row id (data-pyric-selected).
showColumnHeader?booleanRender a leading column-header row (target change for lens when). Default true — matches the mock’s .colhead.

ActivityGridRowProps

Properties

PropertyTypeDescription
className?string-
formatWhen?(at: number, now: number) => stringOverride the when rendering. Receives (at, now); default is the session-relative duration.
now?numberThe “now” anchor passed to formatWhen (and row.when is used as a fallback when omitted).
onSelect?(row: ActivityRow) => void-
rowActivityRow-
selected?booleanMarks the row as the active selection (data-pyric-selected).

ActivityProvenance

Provenance carried by every event. All optional + additive — pre- provenance emitters omit them (read as firestore / app / app-session).

Extended by

Properties

PropertyTypeDescription
actor?ActivityActor-
authLens?ActivityLens-
planId?stringSet when the op is part of an agent plan (dry-run / accept).
service?ActivityService-

ActivityRequestEvent

A Firestore request event — one per evaluated op. The digest reads these for denials (result === 'deny') and, when no write event is present, for the allow trail. Structurally a subset of RequestEvent.

Extends

Properties

PropertyTypeDescription
actor?ActivityActor-
atnumber-
authActivityAuthState-
authLens?ActivityLens-
groupId?string-
idstring-
kind"request"-
matchedRule?{ operations: string[]; ruleIndex: number; }-
matchedRule.operationsstring[]-
matchedRule.ruleIndexnumber-
method"delete" | "get" | "list" | "create" | "update" | "set"-
origin"user" | "transaction" | "listener" | "batch"-
pathstring-
planId?stringSet when the op is part of an agent plan (dry-run / accept).
reasonsstring[]Simulator debug trail. Used to surface the deciding rule on denials.
request?{ resourceData?: Record<string, unknown>; }-
request.resourceData?Record<string, unknown>-
resourceAfter?{ data: Record<string, unknown>; exists: boolean; }-
resourceAfter.dataRecord<string, unknown>-
resourceAfter.existsboolean-
resourceBefore?{ data: Record<string, unknown>; exists: boolean; }-
resourceBefore.dataRecord<string, unknown>-
resourceBefore.existsboolean-
resultActivityResult-
service?ActivityService-
triggeredBy?{ method: string; path: string; }-
triggeredBy.methodstring-
triggeredBy.pathstring-

ActivityRow

A single grid row — the projection of one event onto the target · change · for · lens · when column contract from c-result.html. All display-ready strings plus the structured provenance the host may style on.

Properties

PropertyTypeDescription
actorActivityActor-
atnumberDate.now() at the op. Drives the when column + recency sort.
authLensActivityLens-
bandActivityBandKeyWhich band this row was sorted into.
changestringchange — a short human description of the mutation (update, owner rule, done → true, created, signed in).
deniedbooleanTrue for denials — first-class so hosts flag the row distinctly.
eventActivityEventThe original event, for drill-in.
forstringfor — the subject the op acted on behalf of: request.auth.uid for firestore, the affected uid for auth, else the acting identity. Empty when anonymous / not applicable.
groupKey?stringThe pivot key this row fell under — set only when the digest was computed with groupBy !== 'none'. Mirrors the row’s subgroup.
idstringThe originating event’s id — stable React key.
lensstringlens — the privilege the op ran under, display-ready (app, as alice, admin).
planId?stringAgent plan id, when the op was part of a plan.
serviceActivityServiceOriginating service.
subjectUidstringThe on-behalf-of subject uid, structured (mirrors for).
targetstringtarget — what was mutated, in the service’s addressing scheme (a doc path notes/3agHoZHZ, a uid, a storage fullPath, an rtdb path). Empty string when the event has no addressable target.
whenstringwhen — left undterived here; the grid formats at itself, but a pre-rendered relative string is offered for headless consumers.

ActivityServiceMutationEvent

Cross-service mutation — the unified envelope auth / storage / rtdb emit. The digest maps service + op to a band (signed-in, added, updated, removed, …). Subset of ServiceMutationEvent.

Extends

Properties

PropertyTypeDescriptionOverrides
actor?ActivityActor--
after?unknown--
atnumber--
authActivityAuthState--
authLens?ActivityLens--
before?unknown--
detail?Record<string, unknown>--
idstring--
kind"service_mutation"--
opstring--
path?string--
planId?stringSet when the op is part of an agent plan (dry-run / accept).-
service"rtdb" | "auth" | "storage"-ActivityProvenance.service

ActivitySubgroup

Properties

PropertyType
countnumber
keystring
rowsActivityRow[]

ActivityWriteEvent

A committed Firestore write — create/update/set/delete that the rule engine allowed and the keyspace applied. The digest leads with these for the added/updated/removed bands (richer than request: carries prior/next state for the change summary). Subset of WriteSandboxEvent.

Extends

Properties

PropertyTypeDescription
actor?ActivityActor-
atnumber-
authActivityAuthState-
authLens?ActivityLens-
data?Record<string, unknown>-
groupId?string-
idstring-
kind"write"-
method"delete" | "create" | "update" | "set"-
nextStateRecord<string, unknown>-
pathstring-
planId?stringSet when the op is part of an agent plan (dry-run / accept).
priorStateRecord<string, unknown>-
service?ActivityService-

CreatedAuthUser

An auth user a staged proposal creates (a sign-in account, distinct from Firestore documents). The host adapts its backend request (e.g. a CreateUserRequest) into this UI-level shape.

Properties

PropertyType
displayName?string
email?string
emailVerified?boolean
provider?string
uidstring

FieldChange

One field-level change in a staged proposal. A UI-level diff row: the host adapts its backend diff (e.g. the sandbox’s Divergence[]) into these so the component stays decoupled from any backend type.

Properties

PropertyTypeDescription
afterunknown-
beforeunknown-
docPathstringFull document path, e.g. notes/abc123.
fieldstringThe field that changed.
kind"added" | "removed" | "changed"added = new field, removed = cleared, changed = value differs.

ProposedChangeDiffProps

Properties

PropertyTypeDescription
authUsers?readonly CreatedAuthUser[]Auth users the proposal creates, shown as a leading “auth users” group.
changesFieldChange[]-
className?string-
emptyState?ReactNodeRendered when there are no changes. Defaults to nothing.
formatValue?(value: unknown) => ReactNodeFormat a value for display. Default: JSON-ish, empty for undefined.

UseActivityDigestOptions

Extends

Properties

PropertyTypeDescription
groupBy?ActivityGroupByPivot rows within each band by an attribution axis. 'none' (default) leaves the band flat. Any other value splits each band’s rows into sub-groups keyed by that axis — surfaced on the row via groupKey and exposed as band.subgroups. The flat rows are always present regardless.
now?numberClock injection for the relative when column. Defaults to Date.now() read once per recompute. Pass a fixed value (or a frozen “session now”) for deterministic rendering / tests. NOTE: changing now between renders re-folds the digest, so don’t pass a fresh Date.now() inline unless you want a recompute every render — pin it (e.g. a ticking value updated on an interval).
order?"recency" | "chronological"Row order within a band. recency (default) is newest-first to match the mock’s when column. chronological is oldest-first.
rowsPerBand?numberCap rows kept per band (the mock shows ~3–4 then “N more”). The digest keeps ALL rows but records the overflow via band.count vs rows.length. Set to a number to actually trim rows; the header count still reflects the true total. Default: keep all.

UseActivityStreamOptions

Properties

PropertyTypeDescription
bufferSize?numberRing-buffer cap. Once exceeded, the oldest events drop. Default 5000 (mirrors the traffic monitor’s cap — a load-test session can emit 100k+ events).
initial?readonly AnyActivityEvent[]Seed the buffer with a snapshot before the live subscription attaches — typically sandbox.history(), so a late-attaching consumer sees the whole session, not just events from subscribe onward. Read once on mount.
paused?booleanStart paused — incoming events drop (not queued) while paused.
sourceActivitySourceThe subscription — sandbox.onEvent satisfies this directly. Pass a stable reference; the hook re-subscribes on identity change.

UseActivityStreamResult

Properties

PropertyTypeDescription
clear() => voidEmpty the buffer (does NOT re-seed from initial).
eventsAnyActivityEvent[]The buffered events, oldest-first. Stable per emission — safe to hand straight to useActivityDigest.
isPausedboolean-
pause() => void-
resume() => void-

Type Aliases

ActivityActor

type ActivityActor =
  | {
  kind: "app";
}
  | {
  kind: "studio";
}
  | {
  kind: "agent";
  name: string;
}
  | {
  kind: "app-builder";
};

Who initiated the op behind an event. Absent ⇒ the served app.


ActivityBandKey

type ActivityBandKey =
  | "denied"
  | "errored"
  | "added"
  | "updated"
  | "removed"
  | "signed-in"
  | "signed-out"
  | "read"
  | "other";

The band a row falls into. denied leads; the write bands (added/updated/removed) and the auth bands (signed-in/signed-out) follow; read, errored, and other are the long-tail catch-alls.

  • denied — a rules denial (firestore request with result: 'deny').
  • errored — an operational failure (request with result: 'unsupported').
  • added — a doc/object created, or a user created.
  • updated — a doc/object/user/rtdb path mutated in place.
  • removed — a doc/object/user deleted.
  • signed-in — an auth sign-in.
  • signed-out — an auth sign-out.
  • read — an allowed firestore read (get/list).
  • other — anything modelled but uncategorized (e.g. an unknown service op).

ActivityEvent

type ActivityEvent =
  | ActivityRequestEvent
  | ActivityWriteEvent
  | ActivityServiceMutationEvent;

The union members the activity digest aggregates. This is a SUBSET of pyric’s SandboxEvent — the listener-lifecycle / snapshot-delivery / session-boundary kinds carry no user-visible mutation, so the digest ignores them. The reducer accepts any object with a kind and skips the kinds it doesn’t model, so a full SandboxEvent[] passes through cleanly (the extra kinds fall into the “unknown ⇒ skipped” branch).


ActivityGroupBy

type ActivityGroupBy = "none" | "actor" | "lens" | "subject" | "service";

How rows within each band are pivoted/grouped.


ActivityLens

type ActivityLens =
  | {
  mode: "admin";
}
  | {
  mode: "as";
  uid: string;
}
  | {
  mode: "app-session";
};

The auth lens an op ran under. admin bypasses rules, as evaluates rules as a specific uid (impersonation), app-session is the app’s own signed-in user. Absent ⇒ app-session.


ActivityResult

type ActivityResult = "allow" | "deny" | "unsupported";

Firestore op outcome — present on request events only.


ActivityService

type ActivityService = "firestore" | "auth" | "storage" | "rtdb";

Which sandbox service emitted an event. Absent ⇒ 'firestore'.


ActivitySource()

type ActivitySource = (cb: (event: AnyActivityEvent) => void) => () => void;

A subscription: register a callback, get an unsubscribe. sandbox.onEvent matches this signature.

Parameters

ParameterType
cb(event: AnyActivityEvent) => void

Returns

(): void;
Returns

void


AnyActivityEvent

type AnyActivityEvent =
  | ActivityEvent
  | {
[k: string]: unknown;
  kind: string;
};

The widened input the reducer accepts: any ActivityEvent, plus a permissive escape for unmodelled SandboxEvent kinds carrying a string kind (skipped). Lets sandbox.history() flow in unfiltered.

Functions

ActivityActionItems()

function ActivityActionItems(__namedParameters: ActivityActionItemsProps): ReactNode;

The action-items tier — the few things wanting a decision, surfaced ABOVE the activity grid (design-ideation Tier 2 / “Needs you”). Denials lead and are first-class. Mechanical copy by default; the host supplies the action affordance (e.g. a “Debug” link into the rules debugger) via renderAction.

Data contract:

  • [data-pyric-ui="activity-action-items"] — the root (absent when empty and no emptyState).
  • [data-pyric-action-item] (+ data-pyric-action-type, data-pyric-action-count) — one item.
  • [data-pyric-action-title] / [data-pyric-action-meta] — the copy.
  • [data-pyric-action-affordance] — wraps the host’s action node.

Parameters

ParameterType
__namedParametersActivityActionItemsProps

Returns

ReactNode


ActivityGrid()

function ActivityGrid(__namedParameters: ActivityGridProps): ReactNode;

Headless activity grid over the unified SandboxEvent stream. Folds events into category bands (Denied / Added / Updated / Removed / Signed in / …), each band a target · change · for · lens · when column grid grouped under a label · count · attribution header. Denials lead (lead-with-consequence) and are flagged first-class.

Ships ZERO styling — the host (Pyric Studio) applies the rigid column grid + band typography via the data-pyric-* contract:

  • [data-pyric-ui="activity-grid"] — the root.
  • [data-pyric-band] — a band header, with data-pyric-band-key, data-pyric-band-count, data-pyric-band-denied (on the denied band). Children: [data-pyric-band-label], [data-pyric-band-n], [data-pyric-band-attr] (omitted when attribution is mixed).
  • [data-pyric-band-rows] — the row container; with grouping, [data-pyric-band-subgroup] (+ data-pyric-subgroup-key) wraps each pivot bucket.
  • [data-pyric-band-more] — the “N more” overflow stub.
  • the <ActivityGridRow> contract for each row.

Parameters

ParameterType
__namedParametersActivityGridProps

Returns

ReactNode


ActivityGridRow()

function ActivityGridRow(__namedParameters: ActivityGridRowProps): ReactNode;

One activity grid row — the target · change · for · lens · when column contract from c-result.html, plus a trailing drill affordance column. Headless: every cell is a data-pyric-event-* span the host styles into the rigid column grid.

Styling / data contract:

  • [data-pyric-event-row] — the row, with data-pyric-event-band, data-pyric-event-service, data-pyric-event-lens, data-pyric-event-denied (present only on denials), and data-pyric-selected when active.
  • [data-pyric-event-target] / -change / -for / -lens / -when — the five columns, in order.

Parameters

ParameterType
__namedParametersActivityGridRowProps

Returns

ReactNode


computeActivityDigest()

function computeActivityDigest(events: readonly AnyActivityEvent[], opts?: ActivityDigestOptions & {
  now?: number;
}): ActivityDigest;

Fold a unified event stream into the banded activity digest. PURE — no React, no clock reads except the injected now (defaults to Date.now(), but pass a fixed value in tests for determinism).

Events the digest doesn’t model (listener lifecycle, snapshot delivery, session boundaries, unknown kinds) are skipped — a full SandboxEvent[] from sandbox.history() flows in unfiltered.

Parameters

ParameterType
eventsreadonly AnyActivityEvent[]
opts?ActivityDigestOptions & { now?: number; }

Returns

ActivityDigest


defaultFormatWhen()

function defaultFormatWhen(at: number, now?: number): string;

Default when-column formatter: a session-relative duration (now / 12s / 3m / 1h), matching the mock’s c-when strings. The grid is session-scoped (see design-ideation “FRAME CORRECTION”) so anchors are relative, never absolute calendar dates.

Override via <ActivityGrid formatWhen={...} /> when the host has a better clock anchor (e.g. a ticking “session now”).

Parameters

ParameterType
atnumber
now?number

Returns

string


ProposedChangeDiff()

function ProposedChangeDiff(__namedParameters: ProposedChangeDiffProps): Element;

Headless renderer for a staged change: the documents a proposal touches, with per-field before/after. Grouped by collection. Ships zero styling; the host styles the data-pyric-* contract (proposed-change-diff / data-pyric-change-*). The c-review diff grid is this, styled.

Parameters

ParameterType
__namedParametersProposedChangeDiffProps

Returns

Element


useActivityDigest()

function useActivityDigest(events: readonly AnyActivityEvent[], options?: UseActivityDigestOptions): ActivityDigest;

React wrapper over computeActivityDigest — memoizes the pure fold over the unified SandboxEvent stream into the banded activity digest. Feed it sandbox.history() (a snapshot) or the live buffer from useActivityStream; the reducer is identical either way.

The fold re-runs when events identity, any grouping option, or now changes. Keep events referentially stable across renders that shouldn’t recompute (the stream hook already returns a stable array per emission).

Parameters

ParameterType
eventsreadonly AnyActivityEvent[]
options?UseActivityDigestOptions

Returns

ActivityDigest


useActivityStream()

function useActivityStream(__namedParameters: UseActivityStreamOptions): UseActivityStreamResult;

Buffers the unified sandbox event stream into a capped ring buffer with optional history seeding + pause/resume/clear. Decoupled from pyricsource is just a (cb) => unsubscribe.

Sibling to the traffic monitor’s useTrafficMonitor; the difference is the wider event type (AnyActivityEvent, the full union) and the initial seed so history() + live compose into one buffer.

Parameters

ParameterType
__namedParametersUseActivityStreamOptions

Returns

UseActivityStreamResult