the interaction protocol in prysm
interaction is how a neuron acts on the element tree $\mathcal{T}$. the layout protocol (§4) produces coordinates. the prysm/emotion function produces colors. the interaction protocol produces state transitions. three independent systems, one interface
input events
six primitive events. every interaction in cyb decomposes into these
| event | trigger | data |
|---|---|---|
| tap | finger down + up within $4g$ radius, < $300\text{ms}$ | $(p_x, p_y)$ |
| long-press | finger down, held $\geq 500\text{ms}$, no movement | $(p_x, p_y)$ |
| drag | finger down + movement $> g/2$ | $(p_{x_0}, p_{y_0}, p_{x_t}, p_{y_t}, \delta_x, \delta_y)$ |
| hover | pointer enters organelle bounds (pointer devices only) | $(p_x, p_y)$ |
| key | keyboard key pressed | $(keycode, modifiers)$ |
| scroll | wheel or two-finger gesture | $(\delta_x, \delta_y)$ |
tap and long-press are mutually exclusive: if hold exceeds $500\text{ms}$, tap is cancelled and long-press fires. drag cancels both if movement exceeds $g/2$ before release
hit testing
given input coordinates $(p_x, p_y)$, find the target organelle:
$$\text{hit}(p_x, p_y) = e_i \quad \text{where} \quad z(e_i) = \max\{z(e_j) : (p_x, p_y) \in \text{bounds}(e_j)\}$$
the organelle with the highest $z$ containing the point receives the event. this follows from the urgency function $\mathcal{U}$ (§6.3): a modal at $z = 50$ captures input before the space zone at $z = 0$
when no interactive organelle contains the point: event is discarded
ECS: HitTestSystem reads Position, OccupiedSize, input coordinates. writes EventTarget { entity }
event propagation
events do not bubble. the target organelle handles the event or ignores it. no capture phase, no propagation chain
rationale: bubbling introduces implicit coupling between membrane and organelle. the layout protocol (§4.0) guarantees one-directional information flow (constraints down, sizes up). interaction follows the same principle — events arrive at target, state changes propagate through explicit systems, not implicit bubbling
exception: scroll. when the target organelle does not handle scroll, the event passes to the nearest ancestor with Overflow::Scroll. this is the only propagation — it follows the containment hierarchy, not an event chain
focus
exactly one organelle holds focus at any time. focus determines which organelle receives key events
$$\text{focus}: \mathcal{T} \to e_i \quad |\quad \exists!\; e_i : \text{focused}(e_i) = \text{true}$$
focus transitions:
- tap on a focusable organelle → that organelle gains focus
- tab key → focus moves to the next organelle in
NavigationOrder(§17.2 of layout protocol) - shift+tab → previous in
NavigationOrder - escape → focus moves to commander (prysm/mind)
the commander is the focus root — pressing / from anywhere focuses the commander. this is the universal entry point
ECS: Focus { active: bool } component. FocusSystem reads input events, NavigationOrder, writes Focus
state machine
every interactive organelle defines a finite state machine:
$$\mathcal{S} = (S,\; E,\; \delta,\; s_0)$$
$S$ — finite set of states. $E$ — set of input events the organelle handles. $\delta: S \times E \to S$ — transition function. $s_0 \in S$ — initial state
common state patterns
three patterns recur across all prysm components:
pattern 1: pressable
used by: prysm/button, prysm/tabs, prysm/pill, prysm/toggle, prysm/aip, prysm/menu items
states: { default, hover, active, disabled }
transitions:
default + hover → hover
hover + leave → default
hover + tap → active → default (fires action)
default + tap → active → default (fires action)
disabled + * → disabled (absorbs all events)
active is transient: $50\text{ms}$ visual feedback, then returns to default/hover. the action fires on transition to active, not on exit
pattern 2: focusable
used by: prysm/input, prysm/mind
states: { idle, focus, disabled }
transitions:
idle + tap → focus (gain focus, show cursor)
focus + escape → idle (lose focus)
focus + tab → idle (focus moves to next)
focus + key → focus (input character)
disabled + * → disabled
pattern 3: draggable
used by: prysm/slider, prysm/stars (reorder), prysm/brain (graph nodes)
states: { idle, hover, dragging }
transitions:
idle + hover → hover
hover + leave → idle
hover + drag-start → dragging
dragging + drag-move → dragging (update value/position)
dragging + drag-end → idle (commit value)
composition
a component can compose multiple patterns. a prysm/slider is draggable (handle) + pressable (track tap to jump). composition is union of state machines running in parallel — no conflicts because they operate on different sub-organelles (handle vs track)
ECS: InteractionState { current: State } component per organelle. InteractionSystem reads input events, InteractionState, writes new InteractionState + fires Action events
actions
state transitions produce actions — side effects outside the interaction protocol:
| action | produced by | effect |
|---|---|---|
| navigate | tap on link, menu item, star, tab | router changes active aip / particle |
| submit | tap confirm button, enter key in input | commander sends cyberlink / search / tx |
| toggle | tap on toggle, pill (on/off mode) | boolean state flips |
| expand | tap on context, avatar, collapsible group | fold/unfold reveals/hides content |
| drag-commit | drag-end on slider, star reorder | value or position written to state |
| focus-change | tap on input, tab key, escape | focus moves between organelles |
actions are events emitted by InteractionSystem, consumed by domain systems (router, tx signer, etc.). the interaction protocol defines when actions fire. domain systems define what actions do
visual feedback
state changes produce visual feedback through the prysm/emotion function and motion convention (§16 of layout protocol):
| state change | visual | timing |
|---|---|---|
| → hover | scale 1.1× or glow increase | $150\text{ms}$ ease |
| → active | scale 0.95× (pressed feel) | $50\text{ms}$ ease |
| → focus | saber underline changes to blue (#00acff) | $150\text{ms}$ ease |
| → dragging | element follows pointer | immediate (0ms, locked to input) |
| → disabled | opacity 0.5, gray color | $150\text{ms}$ ease |
drag feedback is immediate (no easing) — the element must track the pointer with zero perceived latency. all other feedback uses the standard motion convention ($150\text{ms}$ ease)
hotkeys
global keyboard shortcuts — handled before focus-based key dispatch:
| key | page | action |
|---|---|---|
| / | all | focus commander |
| tab | all | next focusable organelle |
| shift+tab | all | previous focusable organelle |
| enter | all | activate focused organelle (equivalent to tap) |
| escape | all | lose focus → commander |
| f | brain | toggle fullscreen |
hotkeys are defined in prysm/settings-cell (Hotkeys section). the table above is the default set
ECS: HotkeyMap { list of (key, modifier, action) }. HotkeySystem reads key events before FocusSystem — if a hotkey matches, the event is consumed and does not reach focus dispatch
the interaction function
$$\text{interact}(\mathcal{T},\; e_{input}) \;\to\; \{(e_i,\; s'_i,\; a_i)\}$$
$\mathcal{T}$ — element tree (with current states). $e_{input}$ — input event. output: for each affected organelle $e_i$, its new state $s'_i$ and optional action $a_i$
the interaction function is deterministic: same tree + same states + same input → same output. this follows from $\delta$ being a pure function and hit testing being deterministic (highest $z$ wins)
ECS system order
interaction systems execute after layout, before render:
HitTestSystem → HotkeySystem → FocusSystem → InteractionSystem → ActionSystem
all five run in sequence within one frame. total cost: $\mathcal{O}(n)$ for hit test (spatial index), $\mathcal{O}(1)$ for the rest (single event, single target)