All skills
lassejlv avatar

/build-gpui-apps

@52f4cfd
by Lasselassejlv/skills50 stars
1

Build, scaffold, refactor, debug, review, and validate native Rust desktop applications with GPUI. Recommend GPUI Kit and ask before adopting it; use gpui_kit imports after agreement, or preserve the chosen upstream GPUI stack. Includes the merged GPUI Kit component and design skills, full application/Base/Component/Shell documentation, coding and design guides, state, actions, async, input, accessibility, motion, themes, native integration, packaging, auto updates, testing, and production delivery. Use paper-to-gpui when the primary task is faithfully translating a selected Paper.design frame into an existing view.

Use this Skill: https://skilld.dev/gh/lassejlv/skills/build-gpui-apps

This session only. Nothing lands on disk.

referencesgpui-kitupstreambasemotion.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Motion

gpui-base owns deterministic motion sampling and lifecycle while leaving every visual choice to the application. It provides stable keyed state, interruption, reversal, animation-frame requests, and reduced-motion behavior without imposing product timing or styling.

Run the interactive companion for this guide:

cargo run -p gpui-base-examples --bin motion

The example contains five separate demos. Use the tabs at the top to inspect one capability at a time.

Capability map

Demo API What it demonstrates
Sliding time transition Four independently rolling digits, 08:00–20:00, with targets changing faster than the transition settles
Spring spring A segmented-control indicator that preserves velocity when rapidly retargeted
Keyframes Keyframes, Timing, animate_keyframes A repeating multi-stop activity signal
Stagger Stagger Allocation-free timing offsets across a list
Presence Presence Exit animation that keeps content mounted until it becomes absent
Sequence Sequence Three chained steps — slide in, fill, rest then fade — each starting when the last one ends

The library also exposes Easing, Discrete, MotionTransform, and MotionReveal. They compose with the same primitives rather than requiring separate animation runtimes.

Target transitions

Use transition for a value moving toward a target over a known duration. Every independently animated value needs a stable ID.

let opacity = transition(
    ("save-dialog", "opacity"),
    if open { 1.0 } else { 0.0 },
    Transition::new(Duration::from_millis(180)).easing(Easing::EaseOut),
    window,
    cx,
);

Retargeting starts at the currently sampled value. Direct reversal shortens the return duration, so reversing early does not spend a full duration retracing a short distance. transition_with_status additionally returns Idle, Delayed, Running, or Finished.

Easing includes CSS keyword curves, cubic Bézier curves, all CSS step positions, and piecewise linear() stops. Invalid parameters return typed errors.

Springs

Use spring when the target may change while moving. It preserves both position and velocity, which makes it suitable for selection indicators and settling spatial values.

let x = spring(
    "selected-indicator",
    selected_x,
    Spring::new(Duration::from_millis(420)).with_damping(0.72),
    window,
    cx,
);

Do not make a pointer-controlled value chase the pointer through a spring. Set with_travel(false) during direct manipulation and restore travel after release.

with_damping requires a finite, non-negative ratio; with_epsilon requires a finite value greater than zero and interprets it in the target's own units. The builders panic for invalid trusted constants. Use try_with_damping and try_with_epsilon for configuration or user-provided values. Normalized values normally keep the 0.001 default; pixel motion can use a coarser tolerance such as 0.1.

Keyframes and timing

Keyframes describes validated value stops. Timing uses absolute elapsed time and supports signed delays, finite or infinite iterations, and normal, reverse, or alternating playback.

let frames = Keyframes::try_new([
    Keyframe::new(0.0, 0.25),
    Keyframe::new(0.45, 1.0).ease(Easing::EaseOut),
    Keyframe::new(1.0, 0.25),
])?;

let opacity = animate_keyframes(
    "activity",
    &frames,
    Timing::new(Duration::from_millis(1400))
        .iterations(IterationCount::Infinite),
    window,
    cx,
).value;

Offsets must start at 0, end at 1, and be monotonic. Use Discrete when a value cannot be interpolated.

animate_keyframes retains its playback start time under the supplied stable ID. Re-rendering with the same ID continues the current sequence. To replay it, include an application-owned generation in the ID, such as ("notification-enter", generation), and increment that generation for each replay.

Presence and stagger

Presence separates logical visibility from physical mounting. Its phases are entering, present, exiting, and absent. Render while should_render() is true and use progress for the chosen visual properties. Reopening during exit reverses from the current sample.

Stagger calculates a delay for an index from the first, last, center, or a chosen origin. It does not allocate a schedule or own list identity:

let stagger = Stagger::new(Duration::from_millis(80), StaggerOrigin::First);
let delay = stagger.delay(index, item_count);

Sequences

Sequence chains transitions so each step starts when the previous one ends. It begins at from on the first frame it is sampled and plays once per ID; its sample reports the value, the step being played, and a MotionStatus that reads Finished only after the last step.

let opacity = Sequence::new(("toast", "opacity"), 0.0)
    .with_step(1.0, Transition::new(Duration::from_millis(160)))
    .with_step(0.0, Transition::new(Duration::from_millis(200)).delay(Duration::from_secs(3)))
    .sample(window, cx);

div().opacity(*opacity.value())

A step ends at an absolute instant and the next one starts there, not on the frame that noticed it, so a skipped frame does not start a step late. Zero-duration steps complete within one frame. Changing the target of the step being played restarts the sequence from its first step at the value sampled at that instant; steps not yet reached are read when the sequence gets to them. To replay, put an application-owned generation in the ID. Reduced motion adopts the last target at once with no pending frame.

Stagger composes with a sequence as a delay on its first step:

Sequence::new(("row", index), px(12.))
    .with_step(px(0.), Transition::new(Duration::from_millis(120)).delay(stagger.delay(index, count)))
    .sample(window, cx)

Measured reveal

MotionReveal measures a child at its natural size and clips its visible height by progress. Collapsible::motion_id(...) is the convenient control-level facade. Without a motion ID, the control keeps immediate mount/unmount behavior.

Reduced motion and performance

Transitions, springs, keyframes, presence, and reveal-compatible controls honor GPUI's reduced-motion preference. Finite motion snaps to the target, synchronizes retained state, and leaves no pending animation frame. Motion must never be the only way state is communicated.

The preference is the operating system's. gpui_base::init (and so gpui_component::init) reads the system setting into App::set_reduce_motion — macOS's "Reduce motion" (NSWorkspace.accessibilityDisplayShouldReduceMotion), Windows' "Animation effects" (SPI_GETCLIENTAREAANIMATION, off means reduce), and on Linux the XDG desktop portal's org.freedesktop.appearance reduced-motion key, which arrives over D-Bus a moment after init and is then followed as it changes. Other targets, wasm included, leave the flag alone. An application that calls cx.set_reduce_motion(...) itself owns the flag from then on: Base only writes it while it still holds what Base last wrote. macOS and Windows are read once, at init; call gpui_base::apply_system_reduce_motion(cx) to read them again.

The pure steady sampling paths measured by the benchmark—timing/easing, keyframe lookup, analytic spring integration, and stagger delay calculation—are allocation-free. Keyed transition, spring, presence, and reveal lifecycles are covered by GPUI retained-state and frame-request tests because those updates belong to the framework lifecycle rather than the pure sampler. Sampling uses absolute elapsed time, and keyframe lookup uses binary search. Run the release benchmark with:

cargo bench -p gpui-base --bench motion

Choose the smallest suitable primitive: transition for duration-based targets, spring for changing spatial targets, keyframes for authored sequences, Presence for exit-before-unmount, Sequence for steps that follow one another, and Stagger for list choreography.

Benchmark results

Measured on Linux x86_64 with a release build, 31 batches, and 200 iterations per batch:

Workload Median P95 Worst Allocations
1,000 scalar timing + easing samples 26.490 µs 26.567 µs 27.290 µs 0
1,000 keyframe samples, 2 frames 21.656 µs 21.707 µs 21.729 µs 0
1,000 keyframe samples, 8 frames 25.197 µs 25.251 µs 25.269 µs 0
1,000 keyframe samples, 32 frames 27.932 µs 27.969 µs 27.971 µs 0
1,000 analytic spring integration samples 6.042 µs 6.106 µs 6.216 µs 0
1,000 stagger delay calculations 0.574 µs 0.583 µs 0.587 µs 0

The scalar timing/easing workload remains below its 100 µs median budget. These figures are a reproducible development baseline rather than a cross-platform guarantee; run the benchmark on each target platform when platform-specific performance matters.

Documentation license: original prose and illustrations for which GPUI Kit holds licensing rights are also offered under CC BY 4.0. When copying or adapting, credit GPUI Kit, link the source (https://gpui-kit.com/base/motion) and https://creativecommons.org/licenses/by/4.0/, and indicate changes. Code examples and software source use Apache-2.0; third-party material retains its terms; existing Apache-2.0 permissions remain.

Bundled from GPUI Kit. Documentation prose: CC BY 4.0; code examples: Apache-2.0. Changes: documentation links localized, asset URLs made absolute, and this attribution added.

Source: SKILL.md on GitHub

No alerts3d3 checks · Risk SAFE
  • Gen Agent Trust Hub3d

    The skill provides comprehensive documentation and reference material for building native desktop applications using Rust and GPUI. No security risks, prompt injections, or malicious behaviors were identified. External dependencies originate from reputable organizations in the Rust ecosystem.

  • Socket3d

    No alerts

  • Snyk3d

    Risk: LOW · No issues

Signed by skilld at 52f4cfd. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 4 days ago

README badge

README badge for lassejlv/skills/build-gpui-apps