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-kitgpuientity-advanced.md

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

Advanced Entity Patterns

Contents: Entity Collections Management · Conditional Update Patterns · Entity State Machine Pattern · Entity Proxy Pattern · Cascading Updates Pattern · Entity Snapshot Pattern · Entity Transaction Pattern · Entity Pool Pattern

Entity Collections Management

Dynamic Collection with Cleanup

struct EntityCollection<T> {
    strong_refs: Vec<Entity<T>>,
    weak_refs: Vec<WeakEntity<T>>,
}

impl<T> EntityCollection<T> {
    fn new() -> Self {
        Self {
            strong_refs: Vec::new(),
            weak_refs: Vec::new(),
        }
    }

    fn add(&mut self, entity: Entity<T>, cx: &mut App) {
        self.strong_refs.push(entity.clone());
        self.weak_refs.push(entity.downgrade());
    }

    fn remove(&mut self, entity_id: EntityId, cx: &mut App) {
        self.strong_refs.retain(|e| e.entity_id() != entity_id);
        self.weak_refs.retain(|w| {
            w.upgrade()
                .map(|e| e.entity_id() != entity_id)
                .unwrap_or(false)
        });
    }

    fn cleanup_invalid(&mut self, cx: &mut App) {
        self.weak_refs.retain(|weak| weak.upgrade().is_some());
    }

    fn for_each<F>(&self, cx: &mut App, mut f: F)
    where
        F: FnMut(&Entity<T>, &mut App),
    {
        for entity in &self.strong_refs {
            f(entity, cx);
        }
    }

    fn for_each_weak<F>(&mut self, cx: &mut App, mut f: F)
    where
        F: FnMut(Entity<T>, &mut App),
    {
        self.weak_refs.retain(|weak| {
            if let Some(entity) = weak.upgrade() {
                f(entity, cx);
                true
            } else {
                false // Remove invalid weak references
            }
        });
    }
}

Entity Registry Pattern

use std::collections::HashMap;

struct EntityRegistry<T> {
    entities: HashMap<EntityId, WeakEntity<T>>,
}

impl<T> EntityRegistry<T> {
    fn new() -> Self {
        Self {
            entities: HashMap::new(),
        }
    }

    fn register(&mut self, entity: &Entity<T>) {
        self.entities.insert(entity.entity_id(), entity.downgrade());
    }

    fn unregister(&mut self, entity_id: EntityId) {
        self.entities.remove(&entity_id);
    }

    fn get(&self, entity_id: EntityId) -> Option<Entity<T>> {
        self.entities.get(&entity_id)?.upgrade()
    }

    fn cleanup(&mut self) {
        self.entities.retain(|_, weak| weak.upgrade().is_some());
    }

    fn count(&self) -> usize {
        self.entities.len()
    }

    fn all_entities(&self) -> Vec<Entity<T>> {
        self.entities
            .values()
            .filter_map(|weak| weak.upgrade())
            .collect()
    }
}

Conditional Update Patterns

Debounced Updates

use std::time::{Duration, Instant};

struct DebouncedEntity<T> {
    entity: Entity<T>,
    last_update: Instant,
    debounce_duration: Duration,
    pending_update: Option<Box<dyn FnOnce(&mut T, &mut Context<T>)>>,
}

impl<T: 'static> DebouncedEntity<T> {
    fn new(entity: Entity<T>, debounce_ms: u64) -> Self {
        Self {
            entity,
            last_update: Instant::now(),
            debounce_duration: Duration::from_millis(debounce_ms),
            pending_update: None,
        }
    }

    fn update<F>(&mut self, cx: &mut App, update_fn: F)
    where
        F: FnOnce(&mut T, &mut Context<T>) + 'static,
    {
        let now = Instant::now();
        let elapsed = now.duration_since(self.last_update);

        if elapsed >= self.debounce_duration {
            // Execute immediately
            self.entity.update(cx, update_fn);
            self.last_update = now;
            self.pending_update = None;
        } else {
            // Store for later
            self.pending_update = Some(Box::new(update_fn));

            // Schedule execution
            let entity = self.entity.clone();
            let delay = self.debounce_duration - elapsed;

            cx.spawn(async move |cx| {
                tokio::time::sleep(delay).await;

                if let Some(update) = self.pending_update.take() {
                    entity.update(cx, |state, inner_cx| {
                        update(state, inner_cx);
                    });
                }
            }).detach();
        }
    }
}

Throttled Updates

struct ThrottledEntity<T> {
    entity: Entity<T>,
    last_update: Instant,
    throttle_duration: Duration,
}

impl<T: 'static> ThrottledEntity<T> {
    fn new(entity: Entity<T>, throttle_ms: u64) -> Self {
        Self {
            entity,
            last_update: Instant::now(),
            throttle_duration: Duration::from_millis(throttle_ms),
        }
    }

    fn try_update<F>(&mut self, cx: &mut App, update_fn: F) -> bool
    where
        F: FnOnce(&mut T, &mut Context<T>),
    {
        let now = Instant::now();
        let elapsed = now.duration_since(self.last_update);

        if elapsed >= self.throttle_duration {
            self.entity.update(cx, update_fn);
            self.last_update = now;
            true
        } else {
            false // Update throttled
        }
    }
}

Entity State Machine Pattern

enum AppState {
    Idle,
    Loading,
    Loaded(String),
    Error(String),
}

struct StateMachine {
    state: AppState,
}

impl StateMachine {
    fn new() -> Self {
        Self {
            state: AppState::Idle,
        }
    }

    fn start_loading(&mut self, cx: &mut Context<Self>) {
        if matches!(self.state, AppState::Idle | AppState::Error(_)) {
            self.state = AppState::Loading;
            cx.notify();

            let weak_entity = cx.entity().downgrade();

            cx.spawn(async move |cx| {
                let result = perform_load().await;

                let _ = weak_entity.update(cx, |state, cx| {
                    match result {
                        Ok(data) => state.on_load_success(data, cx),
                        Err(e) => state.on_load_error(e.to_string(), cx),
                    }
                });
            }).detach();
        }
    }

    fn on_load_success(&mut self, data: String, cx: &mut Context<Self>) {
        if matches!(self.state, AppState::Loading) {
            self.state = AppState::Loaded(data);
            cx.notify();
        }
    }

    fn on_load_error(&mut self, error: String, cx: &mut Context<Self>) {
        if matches!(self.state, AppState::Loading) {
            self.state = AppState::Error(error);
            cx.notify();
        }
    }

    fn reset(&mut self, cx: &mut Context<Self>) {
        self.state = AppState::Idle;
        cx.notify();
    }
}

async fn perform_load() -> Result<String, anyhow::Error> {
    // Actual load implementation
    Ok("Data".to_string())
}

Entity Proxy Pattern

struct EntityProxy<T> {
    entity: WeakEntity<T>,
}

impl<T> EntityProxy<T> {
    fn new(entity: &Entity<T>) -> Self {
        Self {
            entity: entity.downgrade(),
        }
    }

    fn with<F, R>(&self, cx: &mut App, f: F) -> Result<R, anyhow::Error>
    where
        F: FnOnce(&T, &App) -> R,
    {
        self.entity.read_with(cx, f)
    }

    fn update<F, R>(&self, cx: &mut App, f: F) -> Result<R, anyhow::Error>
    where
        F: FnOnce(&mut T, &mut Context<T>) -> R,
    {
        self.entity.update(cx, f)
    }

    fn is_valid(&self, cx: &App) -> bool {
        self.entity.upgrade().is_some()
    }
}

Cascading Updates Pattern

struct CascadingUpdater {
    entities: Vec<WeakEntity<UpdateTarget>>,
}

impl CascadingUpdater {
    fn new() -> Self {
        Self {
            entities: Vec::new(),
        }
    }

    fn add_target(&mut self, entity: &Entity<UpdateTarget>) {
        self.entities.push(entity.downgrade());
    }

    fn cascade_update<F>(&mut self, cx: &mut App, update_fn: F)
    where
        F: Fn(&mut UpdateTarget, &mut Context<UpdateTarget>) + Clone,
    {
        // Update all entities in sequence
        self.entities.retain(|weak| {
            if let Ok(_) = weak.update(cx, |state, inner_cx| {
                update_fn.clone()(state, inner_cx);
            }) {
                true // Keep valid entity
            } else {
                false // Remove invalid entity
            }
        });
    }
}

struct UpdateTarget {
    value: i32,
}

Entity Snapshot Pattern

use serde::{Serialize, Deserialize};

#[derive(Clone, Serialize, Deserialize)]
struct EntitySnapshot {
    data: String,
    timestamp: u64,
}

struct SnapshotableEntity {
    data: String,
    snapshots: Vec<EntitySnapshot>,
}

impl SnapshotableEntity {
    fn new(data: String) -> Self {
        Self {
            data,
            snapshots: Vec::new(),
        }
    }

    fn take_snapshot(&mut self, cx: &mut Context<Self>) {
        let snapshot = EntitySnapshot {
            data: self.data.clone(),
            timestamp: current_timestamp(),
        };
        self.snapshots.push(snapshot);
        cx.notify();
    }

    fn restore_snapshot(&mut self, index: usize, cx: &mut Context<Self>) -> Result<(), String> {
        if let Some(snapshot) = self.snapshots.get(index) {
            self.data = snapshot.data.clone();
            cx.notify();
            Ok(())
        } else {
            Err("Invalid snapshot index".to_string())
        }
    }

    fn clear_old_snapshots(&mut self, keep_last: usize, cx: &mut Context<Self>) {
        if self.snapshots.len() > keep_last {
            self.snapshots.drain(0..self.snapshots.len() - keep_last);
            cx.notify();
        }
    }
}

fn current_timestamp() -> u64 {
    use std::time::{SystemTime, UNIX_EPOCH};
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs()
}

Entity Transaction Pattern

struct Transaction<T> {
    entity: Entity<T>,
    original_state: Option<T>,
}

impl<T: Clone> Transaction<T> {
    fn begin(entity: Entity<T>, cx: &mut App) -> Self {
        let original_state = entity.read(cx).clone();

        Self {
            entity,
            original_state: Some(original_state),
        }
    }

    fn update<F>(&mut self, cx: &mut App, update_fn: F)
    where
        F: FnOnce(&mut T, &mut Context<T>),
    {
        self.entity.update(cx, update_fn);
    }

    fn commit(mut self, cx: &mut App) {
        self.original_state = None; // Don't rollback
        self.entity.update(cx, |_, cx| {
            cx.notify();
        });
    }

    fn rollback(mut self, cx: &mut App) {
        if let Some(original) = self.original_state.take() {
            self.entity.update(cx, |state, cx| {
                *state = original;
                cx.notify();
            });
        }
    }
}

impl<T> Drop for Transaction<T> {
    fn drop(&mut self) {
        // Auto-rollback if not committed
        if self.original_state.is_some() {
            eprintln!("Warning: Transaction dropped without commit");
        }
    }
}

// Usage
fn perform_transaction(entity: Entity<MyState>, cx: &mut App) -> Result<(), String> {
    let mut tx = Transaction::begin(entity, cx);

    tx.update(cx, |state, cx| {
        state.value = 42;
    });

    if validate_state(&tx.entity, cx)? {
        tx.commit(cx);
        Ok(())
    } else {
        tx.rollback(cx);
        Err("Validation failed".to_string())
    }
}

Entity Pool Pattern

struct EntityPool<T> {
    available: Vec<Entity<T>>,
    in_use: Vec<WeakEntity<T>>,
    factory: Box<dyn Fn(&mut App) -> Entity<T>>,
}

impl<T: 'static> EntityPool<T> {
    fn new<F>(factory: F) -> Self
    where
        F: Fn(&mut App) -> Entity<T> + 'static,
    {
        Self {
            available: Vec::new(),
            in_use: Vec::new(),
            factory: Box::new(factory),
        }
    }

    fn acquire(&mut self, cx: &mut App) -> Entity<T> {
        let entity = if let Some(entity) = self.available.pop() {
            entity
        } else {
            (self.factory)(cx)
        };

        self.in_use.push(entity.downgrade());
        entity
    }

    fn release(&mut self, entity: Entity<T>, cx: &mut App) {
        // Reset entity state if needed
        entity.update(cx, |state, cx| {
            // Reset logic here
            cx.notify();
        });

        self.available.push(entity);
        self.cleanup_in_use();
    }

    fn cleanup_in_use(&mut self) {
        self.in_use.retain(|weak| weak.upgrade().is_some());
    }

    fn pool_size(&self) -> (usize, usize) {
        (self.available.len(), self.in_use.len())
    }
}

These advanced patterns provide powerful abstractions for managing complex entity scenarios while maintaining code quality and performance.

Merged from GPUI Kit skill. Apache-2.0. Imported 2026-09-28; local links and version-sensitive guidance adapted. The current documentation and locked source take precedence over example signatures.

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