Feature/schemadoc#25
Open
ronnorthrip wants to merge 219 commits into
Open
Conversation
Add onPostTick(callback) to AnimationEngine. Post-tick callbacks fire after all regular tick callbacks complete each frame, before the next rAF is scheduled. Returns an EngineHandle with stop() to unregister. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ng a private one Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eout The test asserts register was called then stops the timeline cleanly instead of awaiting the play promise (which would hang under fake timers without advancing the injected engine).
…erformance.now() Replace wall-clock pause tracking (performance.now()) with engine-elapsed values so pause/resume works correctly with fake schedulers and virtual replay engines. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mer, track position internally Replace performance.now() with engine-provided elapsed parameter for particle timing, ensuring correct behavior when rAF is throttled in background tabs. Remove wall-clock setTimeout safety timer in favor of engine-based 2x-duration check. Track particle position on the object itself instead of reading DOM attributes, decoupling getCurrentPosition() from the SVG renderer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The `elapsed > ms * 2` branch was mathematically unreachable since `progress >= 1` always fires first. Removed it, leaving only the `progress >= 1 || !parentNode` check. Also removed the never-read `done` field from ActiveParticle and all construction sites/mocks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Edge() for O(1) lookup
…ine, expo variants)
- Add LRU cache to svgPathToFunction to reuse parsed functions for identical path strings - Cache size limited to 64 entries; evicts oldest entry when limit reached - Export _clearPathCache() for test isolation - Add memoization tests verifying same path returns cached instance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ON roundtrip BREAKING: node.data containing non-cloneable values (functions, Symbols) will now throw DataCloneError during snapshot instead of silently dropping them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…text Adds a generic type parameter to FlowTimeline, TimelineStep, StepContext, and StepEntry so consumers can type their step context. Default is Record<string, any> for full backwards compatibility. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Split the ~500-line method into _validateStepTargets, _isEmptyStep, _captureNodeFromValues, _captureEdgeFromValues, _resolveFollowPath, _createGuidePath, _executeInstantStep, _prepareAnimatedEdges, _executeFollowPathStep, _executeAnimatedStep, _buildAnimateTargets, _executeEdgeLifecycleOnly, _interpolateFollowPathTick, _tickEdgeTransitions, and _cleanupEdgeTransitions. No behavior changes — existing 87 timeline tests are the guard. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…to canvas-particles module Moves sendParticle, _tickParticles, and particle lifecycle into a focused module. Prepares for the Tier C particle renderer system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds destroy() to the animation mixin that stops all in-flight animations, particles, and active timelines. Wires it to flow-canvas.ts destroy() so the engine is fully cleaned up when the canvas element leaves the DOM. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ve engine idle PostTick callbacks with keepAlive: true now keep the rAF loop running even when no regular tick callbacks are registered. This ensures recorders (Tier E) receive every frame, including idle gaps between animation bursts. Default behavior (keepAlive: false) is unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ter methods Introduces a StepExecutionContext interface that bundles all computed step values into a single object. Refactors _executeFollowPathStep (18 params), _executeAnimatedStep (12 params), _executeInstantStep (6 params), and _executeEdgeLifecycleOnly (9 params) to accept the context object instead of positional parameters. All 87 timeline tests pass unchanged, confirming no behavioral regression. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- M-1: add comment to EASING_MAP documenting easeCirc*/easeExpo* abbreviations vs d3-ease names - M-2: narrow isReducedMotion config param from `any` to typed shape - M-3: remove duplicate animator/timelines/particles cleanup block in flow-canvas destroy() - M-4: extract checkReducedMotion() to easing.ts; replace divergent implementations in FlowTimeline and canvas-animation mixin Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e() entry Adds _snapshot and _target readonly getters to AnimationHandle. Both Maps are populated at animate() call time (capturing the blend/compose from-value for in-flight keys) and persist for the handle's lifetime after completion, forming the foundation for rollback stop mode and the reverse direction state machine. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ze, superseded) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, play/restart, startAt, ping-pong BREAKING: handle.reversed replaced by handle.direction. loop: 'reverse' kept as alias for 'ping-pong'. Finished handles now survive — reverse() plays backward instead of being a no-op. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nish Introduces HandleRegistry for tag-based animation lifecycle control. Adds tag/tags options to AnimateOptions and AnimateInternalOptions, registers handles on the Animator.registry, and auto-deregisters via queueMicrotask on natural completion or explicit stop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…roup() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e auto-cancellation
Adds `while` predicate support to AnimateInternalOptions and ActiveGroup so animations
auto-cancel each frame when the predicate returns false, using the configurable
`whileStopMode` ('jump-end' | 'rollback' | 'freeze', default 'jump-end'). Also exposes
`while`, `boundTo`, and `whileStopMode` on the public AnimateOptions type in types.ts —
`boundTo` is defined for the canvas integration layer (Task 8) to compile into a `while`
predicate; the Animator itself only handles `while`. Five new tests cover all stop modes
and the paused-animation safety invariant.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hot, boundTo on $flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rection timing, group tag merge - I-1: _revive() now re-registers handle in HandleRegistry; microtask unregister is guarded to skip if group was revived before flush - I-2: _playDirection() applies the same startTime adjustment as _reverse() for seamless mid-flight direction changes - I-3: FlowGroup.animate()/update() preserve user-provided tag in the tags array instead of silently overwriting it - I-4: ActiveGroup.loop type narrowed from 'boolean | reverse | ping-pong' to 'boolean | reverse' to match normalized runtime value Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… and timeout safety Timeline steps can now include an `await` field that blocks execution until a promise resolves. Supports raw Promises, objects with `.finished` (animation handles), and thunks evaluated lazily at step activation. Optional `timeout` prevents hanging by emitting `step-timeout` and advancing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… }) with pause/stop propagation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…... })
Adds FlowTimeline#timeline(builder, options?) as thin sugar over step({ timeline: sub }). The method creates a new sub-timeline, passes it to the builder callback, pushes it as a step entry, and returns the sub for individual targeting. Includes three passing tests covering sequential execution, return type assertion, and the independent option.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8 issue codes: dangling-edge, duplicate-node-id, missing-condition, condition-missing-branch, unhandled-source-handle, wait-missing-duration (errors); unreachable-node, cycle (warnings). Mirrors validateSchema — pure helper, no mutation, attached onto canvas via the workflow addon setup callback. 14 vitest tests covering each code + happy path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tted duration Renders .flow-wait-node host with header (optional icon, label, formatted duration) plus top target and bottom source handles. Reads node.data (durationMs, label, icon) reactively via Alpine effect. textContent only, never innerHTML, for XSS safety. Structural CSS in css/structural.css and theme defaults in css/theme-default.css using existing tokens — no new CSS variables introduced. 12 vitest tests covering class/attribute stamping, default and custom labels, duration formatting (ms/s/m), handle positions, icon rendering, and missing-data fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ndations feat(workflow): addon foundations — validate + wait
Adds the FlowRunHandle tracking surface every workflow UI primitive needs: - canvas._currentRunHandle: FlowRunHandle | null — set by run.ts during a run, cleared in the finally cleanup path. - canvas.runState: 'idle' | 'running' | 'paused' | 'stopped' getter derived from the handle's flags. - canvas.stopRun(): forwards stop() to the active handle. - New WorkflowRunState type exported. - Setup callback also wraps any pre-existing resetStates() so condition nodes get _branchTaken cleared (Task 3 lands the run/replay write side). 5 vitest tests covering idle/running/paused/stopped transitions plus the handle lifecycle. Module augmentation extended on FlowInstance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When run picks an outgoing edge from a flow-condition node — either via the declarative resolveConditionBranch path or via a pickBranch handler that returns an edge with a sourceHandle — write the chosen sourceHandle to node.data._branchTaken. The directive (Task 5) reflects this via data-flow-condition-branch-taken so the theme can highlight the chosen branch and mute the other. resetStates() (wired in Task 1's setup) clears the flag. 5 vitest tests covering both pick paths, the non-condition no-op, and the resetStates clear behaviour. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When replaying an edge:taken event whose source node is a flow-condition, mirror the edge's sourceHandle onto sourceNode.data._branchTaken so the directive's branch decoration matches the original run. Combined with the resetStates() wrap in workflow setup (Task 1), the flag is cleared on re-entry. 2 vitest tests covering condition + non-condition edge sources. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure utility that turns a FlowCondition descriptor into a compact human-readable string for the condition-node body. Mirrored on the PHP side by FlowConditionNode::prettyPrintCondition() so the SSR fallback matches the runtime render. 11 vitest tests covering every operator (equals/notEquals, comparisons, in/notIn, exists, matches), nested field paths, null values, and unknown-op fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…h-taken decoration Renders the condition node: header (defaults to 'Condition'), pretty-printed expression body (or evaluateLabel / '[custom evaluator]' for evaluate-fn nodes), target handle, and labelled true/false source handles. Direction defaults to horizontal, switchable via the directive expression or node.data.direction. Reflects node.data._branchTaken via data-flow-condition-branch-taken so themes can highlight the chosen branch and mute the other. textContent only — no innerHTML anywhere. 12 vitest tests covering host-class stamping, direction resolution, body rendering for both condition and evaluate paths, three handles with sourceHandle metadata, and branch-taken attribute reflection. Registered in src/index.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ation Structural CSS positions the target handle and the two source handles (true/false) for both horizontal (default) and vertical directions — target on the leading edge; true/false on the trailing edge at 35/75% horizontally, 30/70% vertically. Theme defaults reuse existing tokens (--flow-node-bg, --flow-border-subtle, --flow-text-body/-muted, --flow-surface-alt, --flow-edge-stroke, --flow-edge-taken-stroke) — no new CSS variables. Branch-taken decoration via attribute selectors: chosen handle gets a soft glow, the other dims to 0.4 opacity. False handle uses literal #ef4444 to match the existing failed-state node border accent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Registered alongside Alpine.magic('workflowRun') so the factory ships
with the workflow addon (animate-extraction safety) without
re-registering on every canvas mount. Auto-binds to canvas.lastReplayHandle
or lazy-builds replayExecution from canvas.executionLog. Capability
detection: scrubber when handle.scrubTo exists, progress bar otherwise;
time readout via formatTime(ms) → m:ss. Polling on play/restart for
non-reactive handles, cleared on pause/stop. Scrubber drag wires
pointerdown/move/up via _applyScrub with bounded ratio.
Test stubs in run.test.ts and validate.test.ts updated to provide
data/ alongside magic.
Structural CSS for .flow-replay-controls + buttons + progress + scrubber
+ time + speed dropdown. Theme defaults reuse existing tokens (--flow-
node-bg, --flow-border-subtle, --flow-text-body/-muted, --flow-edge-stroke,
--flow-edge-taken-stroke). No new CSS variables.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the flowExecutionLog factory alongside flowReplayControls in the
workflow plugin entry. Resolves the canvas via the same resolveCanvas()
helper, reads either canvas.executionLog (default) or a named expression
from sourceExpr. Filter modes: 'all' / 'errors' / 'lifecycle'. Caps
visible events at maxEvents (FIFO display window — separate from the
addon's own logLimit storage cap). Auto-scrolls to the tail while events
arrive; the auto-scroll flag is reset by onUserScroll() based on whether
the body is at the bottom.
Click handler dispatches a flow:highlight-node CustomEvent with
{ detail: { nodeId } } and bubbles — consumers wire focus or animation
externally. iconFor / iconClassFor map event types to single characters
+ palette tokens.
Structural CSS for .flow-execution-log + header + body + row layout
grid + empty state. Theme defaults reuse existing tokens (--flow-node-bg,
--flow-surface-alt, --flow-border-subtle, --flow-text-body/-muted,
--flow-edge-entering-stroke, --flow-edge-taken-stroke). Error icon uses
literal #ef4444. No new CSS variables.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s + theming Three Alpine.data factories, registered alongside the other workflow UI factories so they ship with the addon. flowRunButton reads handlers from the canvas DOM element via config.handlersKey (default 'runHandlers'); auto-disables while canvas.runState is running|paused. flowStopButton hides itself when idle (unless config.alwaysVisible) and calls canvas.stopRun(). flowResetButton calls canvas.resetStates() then canvas.resetExecutionLog() so a fresh run can start from a clean slate. Structural CSS sizes the trio uniformly (h32, rounded). Theme defaults reuse existing tokens — primary uses --flow-edge-taken-stroke for the run accent; stop/reset use --flow-border-subtle outlines with subtle hover lift. No new CSS variables. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…factories Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Alpine injects $el, $watch, $nextTick, $refs onto the data scope at runtime but they are not present on the factory return type, so strict tsc fails the build. Casting via (this as any).$X (or a self alias) silences the strict check without affecting runtime behaviour. Same pattern used elsewhere in the codebase for Alpine magics on data factories. Bundled with the dist rebuild — workflow factories (flowReplayControls, flowExecutionLog, flowRunButton, flowStopButton, flowResetButton) land in alpineflow-workflow.esm.js and x-flow-condition + x-flow-wait register in the core bundle. CSS classes emit in alpineflow.css and theme defaults in alpineflow-theme.css. No new CSS variables. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…primitives feat(workflow): UI primitives — condition directive + canvas runState + factories
Five Alpine.data factories (flowRunButton, flowStopButton, flowResetButton,
flowReplayControls, flowExecutionLog) cached `this._canvas` at init() time
via Alpine.$data(.flow-container). When a button is rendered as a
sibling/ancestor of the canvas in DOM order, button-init runs before the
canvas's flowCanvas init's _initAddons(). The cached _canvas was a stale
empty proxy missing run/stopRun/resetStates/runState/executionLog, so
click-time method calls threw TypeError.
Add an internal `ensureCanvas(scope, requiredMethod)` helper that lazy
re-resolves scope._canvas from document.querySelector('.flow-container')
if the cached reference doesn't have the required method. Called at the
top of every method that touches canvas internals. Idempotent — no-ops
when _canvas is fresh. Also rewrite flowExecutionLog.filteredEvents to
read source from the canvas at getter time rather than from a cached
_source array.
Adds 10 new vitest tests covering button-before-canvas DOM order across
all five factories. Pre-existing 2568 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tion fix(workflow): lazy re-resolve canvas in factory methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Schema doc examples added