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-kitupstreamcomponenttree.md

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

Tree

A versatile tree component for displaying hierarchical data with expand/collapse functionality, keyboard navigation, and custom item rendering. Perfect for file explorers, navigation menus, or any nested data structure.

Import

use gpui_kit::component::tree::{tree, TreeState, TreeItem, TreeEntry};

Usage

Basic Tree

// Create tree state
let tree_state = cx.new(|cx| {
    TreeState::new(cx).items(vec![
        TreeItem::new("src", "src")
            .expanded(true)
            .child(TreeItem::new("src/lib.rs", "lib.rs"))
            .child(TreeItem::new("src/main.rs", "main.rs")),
        TreeItem::new("Cargo.toml", "Cargo.toml"),
        TreeItem::new("README.md", "README.md"),
    ])
});

// Render tree
tree(&tree_state, |ix, entry, selected, window, cx| {
    ListItem::new(ix)
        .child(
            h_flex()
                .gap_2()
                .child(entry.item().label.clone())
        )
})

File Tree with Icons

use gpui_kit::component::{ListItem, IconName, h_flex};

tree(&tree_state, |ix, entry, selected, window, cx| {
    let item = entry.item();
    let icon = if !entry.is_folder() {
        IconName::File
    } else if entry.is_expanded() {
        IconName::FolderOpen
    } else {
        IconName::Folder
    };

    ListItem::new(ix)
        .selected(selected)
        .pl(px(16.) * entry.depth() + px(12.)) // Indent based on depth
        .child(
            h_flex()
                .gap_2()
                .child(icon)
                .child(item.label.clone())
        )
        .on_click(cx.listener(move |_, _, _, _| {
            // Handle item click
        }))
})

Dynamic Tree Loading

impl MyView {
    fn load_files(&mut self, path: PathBuf, cx: &mut Context<Self>) {
        let tree_state = self.tree_state.clone();
        cx.spawn(async move |cx| {
            let items = build_file_items(&path).await;
            tree_state.update(cx, |state, cx| {
                state.set_items(items, cx);
            })
        }).detach();
    }
}

fn build_file_items(path: &Path) -> Vec<TreeItem> {
    let mut items = Vec::new();
    if let Ok(entries) = std::fs::read_dir(path) {
        for entry in entries.flatten() {
            let path = entry.path();
            let name = path.file_name()
                .and_then(|n| n.to_str())
                .unwrap_or("Unknown")
                .to_string();

            if path.is_dir() {
                let children = build_file_items(&path);
                items.push(TreeItem::new(path.to_string_lossy(), name)
                    .children(children));
            } else {
                items.push(TreeItem::new(path.to_string_lossy(), name));
            }
        }
    }
    items
}

Tree with Selection Handling

struct MyTreeView {
    tree_state: Entity<TreeState>,
    selected_item: Option<TreeItem>,
}

impl MyTreeView {
    fn handle_selection(&mut self, item: TreeItem, cx: &mut Context<Self>) {
        self.selected_item = Some(item.clone());
        println!("Selected: {} ({})", item.label, item.id);
        cx.notify();
    }
}

// In render method
tree(&self.tree_state, {
    let view = cx.entity();
    move |ix, entry, selected, window, cx| {
        view.update(cx, |this, cx| {
            ListItem::new(ix)
                .selected(selected)
                .child(entry.item().label.clone())
                .on_click(cx.listener({
                    let item = entry.item().clone();
                    move |this, _, _, cx| {
                        this.handle_selection(item.clone(), cx);
                    }
                }))
        })
    }
})

Disabled Items

TreeItem::new("protected", "Protected Folder")
    .disabled(true)
    .child(TreeItem::new("secret.txt", "secret.txt"))

Programmatic Tree Control

// Get current selection
if let Some(entry) = tree_state.read(cx).selected_entry() {
    println!("Current selection: {}", entry.item().label);
}

// Set selection programmatically (by selected_index)
tree_state.update(cx, |state, cx| {
    state.set_selected_index(Some(2), cx); // Select third item
});

// Set selection programmatically (by tree item)
tree_state.update(cx, |state, cx| {
    state.set_selected_item(Some(item), cx); // Select third item
});

// Scroll to specific item
tree_state.update(cx, |state, _| {
    state.scroll_to_item(5, gpui_kit::ScrollStrategy::Center);
});

// Clear selection (by selected_index)
tree_state.update(cx, |state, cx| {
    state.set_selected_index(None, cx);
});

// Clear selection (by tree item)
tree_state.update(cx, |state, cx| {
    state.set_selected_item(None, cx);
});

API Reference

TreeState

Method Description
new(cx) Create a new tree state
items(items) Set initial tree items
set_items(items, cx) Update tree items and notify
selected_index() Get currently selected index
set_selected_index(ix, cx) Set selected index
set_selected_item(item, cx) Set selected by tree item
selected_item(item, cx) Get currently selected tree item
selected_entry() Get currently selected entry
scroll_to_item(ix, strategy) Scroll to specific item

TreeItem

Method Description
new(id, label) Create new tree item with ID and label
child(item) Add single child item
children(items) Add multiple child items
expanded(bool) Set expanded state
disabled(bool) Set disabled state
is_folder() Check if item has children
is_expanded() Check if item is expanded
is_disabled() Check if item is disabled

TreeEntry

Method Description
item() Get the source TreeItem
depth() Get item depth in tree
is_folder() Check if entry has children
is_expanded() Check if entry is expanded
is_disabled() Check if entry is disabled

tree() Function

Parameter Description
state Entity<TreeState> for managing tree
render_item Closure for rendering each item
Render Item Closure
Fn(usize, &TreeEntry, bool, &mut Window, &mut App) -> ListItem
  • usize: Item index in flattened tree
  • &TreeEntry: Tree entry with item and metadata
  • bool: Whether item is currently selected
  • &mut Window: Current window context
  • &mut App: Application context
  • Returns: ListItem for rendering

Examples

Lazy Loading Tree

struct LazyTreeView {
    tree_state: Entity<TreeState>,
    loaded_paths: HashSet<String>,
}

impl LazyTreeView {
    fn load_children(&mut self, item_id: &str, cx: &mut Context<Self>) {
        if self.loaded_paths.contains(item_id) {
            return;
        }

        let path = PathBuf::from(item_id);
        if path.is_dir() {
            let tree_state = self.tree_state.clone();
            let item_id = item_id.to_string();

            cx.spawn(async move |cx| {
                let children = load_directory_children(&path).await;
                tree_state.update(cx, |state, cx| {
                    // Update specific item with loaded children
                    state.update_item_children(&item_id, children, cx);
                })
            }).detach();

            self.loaded_paths.insert(item_id.to_string());
        }
    }
}

Search and Filter

struct SearchableTree {
    tree_state: Entity<TreeState>,
    original_items: Vec<TreeItem>,
    search_query: String,
}

impl SearchableTree {
    fn filter_tree(&mut self, query: &str, cx: &mut Context<Self>) {
        self.search_query = query.to_string();

        let filtered_items = if query.is_empty() {
            self.original_items.clone()
        } else {
            filter_tree_items(&self.original_items, query)
        };

        self.tree_state.update(cx, |state, cx| {
            state.set_items(filtered_items, cx);
        });
    }
}

fn filter_tree_items(items: &[TreeItem], query: &str) -> Vec<TreeItem> {
    items.iter()
        .filter_map(|item| {
            if item.label.to_lowercase().contains(&query.to_lowercase()) {
                Some(item.clone().expanded(true)) // Auto-expand matches
            } else {
                // Check if any children match
                let filtered_children = filter_tree_items(&item.children, query);
                if !filtered_children.is_empty() {
                    Some(item.clone()
                        .children(filtered_children)
                        .expanded(true))
                } else {
                    None
                }
            }
        })
        .collect()
}

Multi-Select Tree

struct MultiSelectTree {
    tree_state: Entity<TreeState>,
    selected_items: HashSet<String>,
}

impl MultiSelectTree {
    fn toggle_selection(&mut self, item_id: &str, cx: &mut Context<Self>) {
        if self.selected_items.contains(item_id) {
            self.selected_items.remove(item_id);
        } else {
            self.selected_items.insert(item_id.to_string());
        }
        cx.notify();
    }

    fn is_selected(&self, item_id: &str) -> bool {
        self.selected_items.contains(item_id)
    }
}

// In render method
tree(&self.tree_state, {
    let view = cx.entity();
    move |ix, entry, _selected, window, cx| {
        view.update(cx, |this, cx| {
            let item = entry.item();
            let is_multi_selected = this.is_selected(&item.id);

            ListItem::new(ix)
                .selected(is_multi_selected)
                .child(
                    h_flex()
                        .gap_2()
                        .child(checkbox().checked(is_multi_selected))
                        .child(item.label.clone())
                )
                .on_click(cx.listener({
                    let item_id = item.id.clone();
                    move |this, _, _, cx| {
                        this.toggle_selection(&item_id, cx);
                    }
                }))
        })
    }
})

Keyboard Navigation

The Tree component supports comprehensive keyboard navigation:

Key Action
↑ Select previous item
↓ Select next item
← Collapse current folder or move to parent
→ Expand current folder
Enter Toggle expand/collapse for folders
Space Custom action (configurable)
// Custom keyboard handling
tree(&tree_state)
    .key_context("MyTree")
    .on_action(cx.listener(|this, action: &MyCustomAction, _, cx| {
        // Handle custom actions
    }))

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/component/tree) 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