Draft
Conversation
Design spec for clayterm transitions: frame-snapshot-compatible interpolation of element position, size, and color properties. Defines the deltaTime convention, the animating signal on RenderResult, declarative enter/exit semantics that replace Clay's function-pointer callbacks, and cancellation as a structural consequence of re-describing state. Implementation is gated on bumping the Clay submodule past the upstream transition commit.
Scope v1 to what Clay currently supports without userData on transition callbacks: one duration and one easing per element, applied to all listed properties. Drop per-property longhand, enter/exit deltas, cubicBezier, and corner radius — each with an explicit "Deferred Until Upstream Clay" entry in §13 referencing nicbarker/clay#603 and the forthcoming exit-flag work. Easings are plain string literals ("linear" | "easeIn" | "easeOut" | "easeInOut") since v1 has no parametric easings.
Co-Authored-By:
Ports the spirit of the raylib-transitions demo to clayterm: a 4×4 grid of colored boxes that animate position, size, and bg color. Shuffle (s) animates positions via Clay's transition system; recolor (c) toggles between two palettes with animated bg interpolation; hover tints each box by blending its bg toward white (overlay-color field is not yet in the v1 command buffer, so lighten-on-hover substitutes). Full mouse tracking is wired via mouseTracking() + pointer state from input events.
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.
Motivation
The renderer has no way to express time-based interpolation of visual properties. Every frame is a hard cut, which makes the terminal UI feel brittle for anything beyond static layouts — a collapsing sidebar jumps, a focus change flashes, a list reorder teleports. Transitions fill that gap: they let callers describe the desired state each frame and let the renderer smoothly interpolate between frames, without introducing a component tree or violating the frame-snapshot contract.
This branch also needs to live within what the upstream Clay engine can currently do. Clay ships a transition API but its callbacks carry no per-element userData, which means several features the rendering model naturally invites — per-property easing, per-element enter/exit deltas, custom bezier curves — can't be expressed without upstream changes (see Clay #603 and related discussion). Rather than paper over that constraint with fragile workarounds, this PR ships the v1 subset that Clay genuinely supports today and tombstones the rest behind specific upstream unblockers.
Approach
The contract is established in
specs/transitions-spec.md: six normative invariants (time driven by delta, no callbacks across the WASM boundary, animating signal accuracy, cancellation-as-structural), a single declarativetransition: { duration, easing, properties, interactive? }shape onopen(), a fixed 8-byte wire encoding, and a §13 "Deferred Until Upstream Clay" section that makes the current limits explicit and points at the fixes that unblock them.On the rendering side,
Termnow tracksperformance.now()across frames and passesdeltaTimethrough toClay_EndLayout. The C side pre-registers four handlers (one per easing kind —linear,easeIn,easeOut,easeInOut) and picks the right one at element-configuration time from an enum byte in the directive buffer. Ananimating_counton the Term context is incremented by in-progress handlers and exposed asRenderResult.animating, so callers know whether to schedule another frame. The Clay submodule was bumped to pick up the transitions API (pinned to938967ato work around an upstream copy-paste typo in a later commit that duplicated a WASM export name).One subtlety worth calling out: Clay is delta-based and has no notion of when a transition began, so idle time between renders would otherwise inflate the elapsed clock and cause transitions to complete instantly after a long idle gap.
Termhandles this by passingdeltaTime=0whenever the previous render reportedanimating=false, giving each new transition a clean elapsed=0 starting point. Real deltas flow through on every frame where a transition is already in flight.Two demos land with the feature: a minimal interactive sidebar that expands and collapses on Enter/ESC, and a port of Clay's raylib-transitions example (a grid of colored boxes that shuffle position and recolor their backgrounds, with mouse hover tints).
Alternate Designs
userData. Would have enabled the full spec (per-property easing, enter/exit deltas, cubic bezier) immediately, but at the cost of maintaining a fork across every Clay bump. Rejected in favor of shipping the Clay-native subset now and restoring the deferred pieces once upstream lands nicbarker/clay#603.startTimeand computing progress from wall-clock deltas would sidestep the deltaTime-vs-mutation-time ambiguity entirely. Rejected because Clay's API is irreducibly delta-based and reworking that upstream is out of scope for this change. The idle-reset behavior approximates the timestamp model where it matters most.Possible Drawbacks or Risks
The Clay submodule pin is one commit behind upstream main so we can sidestep the duplicated-WASM-export bug. When that bug is fixed upstream, we can bump forward and pick up the small number of transition fixes we're currently missing.
The spec's §7.3 lists
"overlay"as a transition property, butOpenElementhas nooverlay?field in v1, so supplying an overlay color from the TS side isn't possible yet. Theclay-transitionsdemo sidesteps this by tintingbgfor hover feedback. Addingoverlay?: numbertoOpenElementwith aPROP_OVERLAY_COLORwire bit is a small follow-up if anyone wants it.TODOs and Open Questions
cubicBeziereasing.overlay?: numbertoOpenElementso the spec's"overlay"property is actually reachable from the TS side.Learning