Agent Skills › feigeCode/navop

feigeCode/navop

GitHub

提供GPUI中声明式键盘驱动UI交互的实现指南,涵盖动作定义、快捷键绑定、参数传递、上下文感知及命名规范。

13 个 Skill 99

安装全部 Skills

npx skills add feigeCode/navop --all -g -y
更多选项

预览集合内 Skills

npx skills add feigeCode/navop --list

集合内 Skills (13)

提供GPUI中声明式键盘驱动UI交互的实现指南,涵盖动作定义、快捷键绑定、参数传递、上下文感知及命名规范。
实现GPUI动作 配置键盘快捷键 设置按键绑定
.codex/skills/gpui-action/SKILL.md
npx skills add feigeCode/navop --skill gpui-action -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-action",
    "description": "Action definitions and keyboard shortcuts in GPUI. Use when implementing actions, keyboard shortcuts, or key bindings."
}

Overview

Actions provide declarative keyboard-driven UI interactions in GPUI.

Key Concepts:

  • Define actions with actions! macro or #[derive(Action)]
  • Bind keys with cx.bind_keys()
  • Handle with .on_action() on elements
  • Context-aware via key_context()

Quick Start

Simple Actions

use gpui::actions;

actions!(editor, [MoveUp, MoveDown, Save, Quit]);

const CONTEXT: &str = "Editor";

pub fn init(cx: &mut App) {
    cx.bind_keys([
        KeyBinding::new("up", MoveUp, Some(CONTEXT)),
        KeyBinding::new("down", MoveDown, Some(CONTEXT)),
        KeyBinding::new("cmd-s", Save, Some(CONTEXT)),
        KeyBinding::new("cmd-q", Quit, Some(CONTEXT)),
    ]);
}

impl Render for Editor {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .key_context(CONTEXT)
            .on_action(cx.listener(Self::move_up))
            .on_action(cx.listener(Self::move_down))
            .on_action(cx.listener(Self::save))
    }
}

impl Editor {
    fn move_up(&mut self, _: &MoveUp, cx: &mut Context<Self>) {
        // Handle move up
        cx.notify();
    }

    fn move_down(&mut self, _: &MoveDown, cx: &mut Context<Self>) {
        cx.notify();
    }

    fn save(&mut self, _: &Save, cx: &mut Context<Self>) {
        // Save logic
        cx.notify();
    }
}

Actions with Parameters

#[derive(Clone, PartialEq, Action, Deserialize)]
#[action(namespace = editor)]
pub struct InsertText {
    pub text: String,
}

#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = editor, no_json)]
pub struct Digit(pub u8);

cx.bind_keys([
    KeyBinding::new("0", Digit(0), Some(CONTEXT)),
    KeyBinding::new("1", Digit(1), Some(CONTEXT)),
    // ...
]);

impl Editor {
    fn on_digit(&mut self, action: &Digit, cx: &mut Context<Self>) {
        self.insert_digit(action.0, cx);
    }
}

Key Formats

// Modifiers
"cmd-s"         // Command (macOS) / Ctrl (Windows/Linux)
"ctrl-c"        // Control
"alt-f"         // Alt
"shift-tab"     // Shift
"cmd-ctrl-f"    // Multiple modifiers

// Keys
"a-z", "0-9"    // Letters and numbers
"f1-f12"        // Function keys
"up", "down", "left", "right"
"enter", "escape", "space", "tab"
"backspace", "delete"
"-", "=", "[", "]", etc.  // Special characters

Action Naming

Prefer verb-noun pattern:

actions!([
    OpenFile,      // ✅ Good
    CloseWindow,   // ✅ Good
    ToggleSidebar, // ✅ Good
    Save,          // ✅ Good (common exception)
]);

Context-Aware Bindings

const EDITOR_CONTEXT: &str = "Editor";
const MODAL_CONTEXT: &str = "Modal";

// Same key, different contexts
cx.bind_keys([
    KeyBinding::new("escape", CloseModal, Some(MODAL_CONTEXT)),
    KeyBinding::new("escape", ClearSelection, Some(EDITOR_CONTEXT)),
]);

// Set context on element
div()
    .key_context(EDITOR_CONTEXT)
    .child(editor_content)

Best Practices

✅ Use Contexts

// ✅ Good: Context-aware
div()
    .key_context("MyComponent")
    .on_action(cx.listener(Self::handle))

✅ Name Actions Clearly

// ✅ Good: Clear intent
actions!([
    SaveDocument,
    CloseTab,
    TogglePreview,
]);

✅ Handle with Listeners

// ✅ Good: Proper handler naming
impl MyComponent {
    fn on_action_save(&mut self, _: &Save, cx: &mut Context<Self>) {
        // Handle save
        cx.notify();
    }
}

div().on_action(cx.listener(Self::on_action_save))

Reference Documentation

  • Complete Guide: See reference.md
    • Action definition, keybinding, dispatch
    • Focus-based routing, best practices
    • Performance, accessibility
提供GPUI中异步操作与后台任务的处理指南,涵盖前台UI更新(cx.spawn)、后台计算(cx.background_spawn)及任务生命周期管理。包含数据获取、周期性任务等核心模式,并警示禁止从后台线程直接更新实体,确保UI线程安全。
GPUI异步编程 后台任务处理 UI线程更新 并发操作协调
.codex/skills/gpui-async/SKILL.md
npx skills add feigeCode/navop --skill gpui-async -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-async",
    "description": "Async operations and background tasks in GPUI. Use when working with async, spawn, background tasks, or concurrent operations. Essential for handling async I\/O, long-running computations, and coordinating between foreground UI updates and background work."
}

Overview

GPUI provides integrated async runtime for foreground UI updates and background computation.

Key Concepts:

  • Foreground tasks: UI thread, can update entities (cx.spawn)
  • Background tasks: Worker threads, CPU-intensive work (cx.background_spawn)
  • All entity updates happen on foreground thread

Quick Start

Foreground Tasks (UI Updates)

impl MyComponent {
    fn fetch_data(&mut self, cx: &mut Context<Self>) {
        let entity = cx.entity().downgrade();

        cx.spawn(async move |cx| {
            // Runs on UI thread, can await and update entities
            let data = fetch_from_api().await;

            entity.update(cx, |state, cx| {
                state.data = Some(data);
                cx.notify();
            }).ok();
        }).detach();
    }
}

Background Tasks (Heavy Work)

impl MyComponent {
    fn process_file(&mut self, cx: &mut Context<Self>) {
        let entity = cx.entity().downgrade();

        cx.background_spawn(async move {
            // Runs on background thread, CPU-intensive
            let result = heavy_computation().await;
            result
        })
        .then(cx.spawn(move |result, cx| {
            // Back to foreground to update UI
            entity.update(cx, |state, cx| {
                state.result = result;
                cx.notify();
            }).ok();
        }))
        .detach();
    }
}

Task Management

struct MyView {
    _task: Task<()>,  // Prefix with _ if stored but not accessed
}

impl MyView {
    fn new(cx: &mut Context<Self>) -> Self {
        let entity = cx.entity().downgrade();

        let _task = cx.spawn(async move |cx| {
            // Task automatically cancelled when dropped
            loop {
                tokio::time::sleep(Duration::from_secs(1)).await;
                entity.update(cx, |state, cx| {
                    state.tick();
                    cx.notify();
                }).ok();
            }
        });

        Self { _task }
    }
}

Core Patterns

1. Async Data Fetching

cx.spawn(async move |cx| {
    let data = fetch_data().await?;
    entity.update(cx, |state, cx| {
        state.data = Some(data);
        cx.notify();
    })?;
    Ok::<_, anyhow::Error>(())
}).detach();

2. Background Computation + UI Update

cx.background_spawn(async move {
    heavy_work()
})
.then(cx.spawn(move |result, cx| {
    entity.update(cx, |state, cx| {
        state.result = result;
        cx.notify();
    }).ok();
}))
.detach();

3. Periodic Tasks

cx.spawn(async move |cx| {
    loop {
        tokio::time::sleep(Duration::from_secs(5)).await;
        // Update every 5 seconds
    }
}).detach();

4. Task Cancellation

Tasks are automatically cancelled when dropped. Store in struct to keep alive.

Common Pitfalls

❌ Don't: Update entities from background tasks

// ❌ Wrong: Can't update entities from background thread
cx.background_spawn(async move {
    entity.update(cx, |state, cx| { // Compile error!
        state.data = data;
    });
});

✅ Do: Use foreground task or chain

// ✅ Correct: Chain with foreground task
cx.background_spawn(async move { data })
    .then(cx.spawn(move |data, cx| {
        entity.update(cx, |state, cx| {
            state.data = data;
            cx.notify();
        }).ok();
    }))
    .detach();

Reference Documentation

Complete Guides

  • API Reference: See api-reference.md

    • Task types, spawning methods, contexts
    • Executors, cancellation, error handling
  • Patterns: See patterns.md

    • Data fetching, background processing
    • Polling, debouncing, parallel tasks
    • Pattern selection guide
  • Best Practices: See best-practices.md

    • Error handling, cancellation
    • Performance optimization, testing
    • Common pitfalls and solutions
管理GPUI中的App、Window及异步上下文,涵盖实体创建更新、窗口操作、通知事件及异步任务调度,提供各层级Context的使用示例与API参考。
需要管理GPUI应用状态或实体生命周期 执行窗口特定操作如布局或焦点控制 在组件中触发重渲染或处理异步任务
.codex/skills/gpui-context/SKILL.md
npx skills add feigeCode/navop --skill gpui-context -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-context",
    "description": "Context management in GPUI including App, Window, and AsyncApp. Use when working with contexts, entity updates, or window operations. Different context types provide different capabilities for UI rendering, entity management, and async operations."
}

Overview

GPUI uses different context types for different scenarios:

Context Types:

  • App: Global app state, entity creation
  • Window: Window-specific operations, painting, layout
  • Context<T>: Entity-specific context for component T
  • AsyncApp: Async context for foreground tasks
  • AsyncWindowContext: Async context with window access

Quick Start

Context<T> - Component Context

impl MyComponent {
    fn update_state(&mut self, cx: &mut Context<Self>) {
        self.value = 42;
        cx.notify(); // Trigger re-render

        // Spawn async task
        cx.spawn(async move |cx| {
            // Async work
        }).detach();

        // Get current entity
        let entity = cx.entity();
    }
}

App - Global Context

fn main() {
    let app = Application::new();
    app.run(|cx: &mut App| {
        // Create entities
        let entity = cx.new(|cx| MyState::default());

        // Open windows
        cx.open_window(WindowOptions::default(), |window, cx| {
            cx.new(|cx| Root::new(view, window, cx))
        });
    });
}

Window - Window Context

impl Render for MyView {
    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        // Window operations
        let is_focused = window.is_window_focused();
        let bounds = window.bounds();

        div().child("Content")
    }
}

AsyncApp - Async Context

cx.spawn(async move |cx: &mut AsyncApp| {
    let data = fetch_data().await;

    entity.update(cx, |state, inner_cx| {
        state.data = data;
        inner_cx.notify();
    }).ok();
}).detach();

Common Operations

Entity Operations

// Create entity
let entity = cx.new(|cx| MyState::default());

// Update entity
entity.update(cx, |state, cx| {
    state.value = 42;
    cx.notify();
});

// Read entity
let value = entity.read(cx).value;

Notifications and Events

// Trigger re-render
cx.notify();

// Emit event
cx.emit(MyEvent::Updated);

// Observe entity
cx.observe(&entity, |this, observed, cx| {
    // React to changes
}).detach();

// Subscribe to events
cx.subscribe(&entity, |this, source, event, cx| {
    // Handle event
}).detach();

Window Operations

// Window state
let focused = window.is_window_focused();
let bounds = window.bounds();
let scale = window.scale_factor();

// Close window
window.remove_window();

Async Operations

// Spawn foreground task
cx.spawn(async move |cx| {
    // Async work with entity access
}).detach();

// Spawn background task
cx.background_spawn(async move {
    // Heavy computation
}).detach();

Context Hierarchy

App (Global)
  └─ Window (Per-window)
       └─ Context<T> (Per-component)
            └─ AsyncApp (In async tasks)
                 └─ AsyncWindowContext (Async + Window)

Reference Documentation

  • API Reference: See api-reference.md
    • Complete context API, methods, conversions
    • Entity operations, window operations
    • Async contexts, best practices
用于在GPUI中通过低层Element API实现自定义UI组件。适用于需要精细控制布局、预绘制和绘制阶段的高性能复杂场景,弥补高层API的不足。
需要自定义复杂布局算法 构建高性能关键UI组件 高层Render/RenderOnce API无法满足需求
.codex/skills/gpui-element/SKILL.md
npx skills add feigeCode/navop --skill gpui-element -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-element",
    "description": "Implementing custom elements using GPUI's low-level Element API (vs. high-level Render\/RenderOnce APIs). Use when you need maximum control over layout, prepaint, and paint phases for complex, performance-critical custom UI components that cannot be achieved with Render\/RenderOnce traits."
}

When to Use

Use the low-level Element trait when:

  • Need fine-grained control over layout calculation
  • Building complex, performance-critical components
  • Implementing custom layout algorithms (masonry, circular, etc.)
  • High-level Render/RenderOnce APIs are insufficient

Prefer Render/RenderOnce for: Simple components, standard layouts, declarative UI

Quick Start

The Element trait provides direct control over three rendering phases:

impl Element for MyElement {
    type RequestLayoutState = MyLayoutState;  // Data passed to later phases
    type PrepaintState = MyPaintState;        // Data for painting

    fn id(&self) -> Option<ElementId> {
        Some(self.id.clone())
    }

    fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
        None
    }

    // Phase 1: Calculate sizes and positions
    fn request_layout(&mut self, .., window: &mut Window, cx: &mut App)
        -> (LayoutId, Self::RequestLayoutState)
    {
        let layout_id = window.request_layout(
            Style { size: size(px(200.), px(100.)), ..default() },
            vec![],
            cx
        );
        (layout_id, MyLayoutState { /* ... */ })
    }

    // Phase 2: Create hitboxes, prepare for painting
    fn prepaint(&mut self, .., bounds: Bounds<Pixels>, layout: &mut Self::RequestLayoutState,
                window: &mut Window, cx: &mut App) -> Self::PrepaintState
    {
        let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
        MyPaintState { hitbox }
    }

    // Phase 3: Render and handle interactions
    fn paint(&mut self, .., bounds: Bounds<Pixels>, layout: &mut Self::RequestLayoutState,
             paint_state: &mut Self::PrepaintState, window: &mut Window, cx: &mut App)
    {
        window.paint_quad(paint_quad(bounds, Corners::all(px(4.)), cx.theme().background));

        window.on_mouse_event({
            let hitbox = paint_state.hitbox.clone();
            move |event: &MouseDownEvent, phase, window, cx| {
                if hitbox.is_hovered(window) && phase.bubble() {
                    // Handle interaction
                    cx.stop_propagation();
                }
            }
        });
    }
}

// Enable element to be used as child
impl IntoElement for MyElement {
    type Element = Self;
    fn into_element(self) -> Self::Element { self }
}

Core Concepts

Three-Phase Rendering

  1. request_layout: Calculate sizes and positions, return layout ID and state
  2. prepaint: Create hitboxes, compute final bounds, prepare for painting
  3. paint: Render element, set up interactions (mouse events, cursor styles)

State Flow

RequestLayoutState → PrepaintState → paint

State flows in one direction through associated types, passed as mutable references between phases.

Key Operations

  • Layout: window.request_layout(style, children, cx) - Create layout node
  • Hitboxes: window.insert_hitbox(bounds, behavior) - Create interaction area
  • Painting: window.paint_quad(...) - Render visual content
  • Events: window.on_mouse_event(handler) - Handle user input

Reference Documentation

Complete API Documentation

  • Element Trait API: See api-reference.md
    • Associated types, methods, parameters, return values
    • Hitbox system, event handling, cursor styles

Implementation Guides

  • Examples: See examples.md

    • Simple text element with highlighting
    • Interactive element with selection
    • Complex element with child management
  • Best Practices: See best-practices.md

    • State management, performance optimization
    • Interaction handling, layout strategies
    • Error handling, testing, common pitfalls
  • Common Patterns: See patterns.md

    • Text rendering, container, interactive, composite, scrollable patterns
    • Pattern selection guide
  • Advanced Patterns: See advanced-patterns.md

    • Custom layout algorithms (masonry, circular)
    • Element composition with traits
    • Async updates, memoization, virtual lists
GPUI实体管理技能,提供状态安全访问、更新及异步处理。支持组件间状态协调与响应式模式,强调使用弱引用防循环引用、避免嵌套更新等核心原则。
需要管理应用状态或组件数据时 实现组件间共享状态或通信时 进行异步操作并需安全更新UI状态时 处理回调以避免内存泄漏或循环引用时
.codex/skills/gpui-entity/SKILL.md
npx skills add feigeCode/navop --skill gpui-entity -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-entity",
    "description": "Entity management and state handling in GPUI. Use when working with entities, managing component state, coordinating between components, handling async operations with state updates, or implementing reactive patterns. Entities provide safe concurrent access to application state."
}

Overview

An Entity<T> is a handle to state of type T, providing safe access and updates.

Key Methods:

  • entity.read(cx)&T - Read-only access
  • entity.read_with(cx, |state, cx| ...)R - Read with closure
  • entity.update(cx, |state, cx| ...)R - Mutable update
  • entity.downgrade()WeakEntity<T> - Create weak reference
  • entity.entity_id()EntityId - Unique identifier

Entity Types:

  • Entity<T>: Strong reference (increases ref count)
  • WeakEntity<T>: Weak reference (doesn't prevent cleanup, returns Result)

Quick Start

Creating and Using Entities

// Create entity
let counter = cx.new(|cx| Counter { count: 0 });

// Read state
let count = counter.read(cx).count;

// Update state
counter.update(cx, |state, cx| {
    state.count += 1;
    cx.notify(); // Trigger re-render
});

// Weak reference (for closures/callbacks)
let weak = counter.downgrade();
let _ = weak.update(cx, |state, cx| {
    state.count += 1;
    cx.notify();
});

In Components

struct MyComponent {
    shared_state: Entity<SharedData>,
}

impl MyComponent {
    fn new(cx: &mut App) -> Entity<Self> {
        let shared = cx.new(|_| SharedData::default());

        cx.new(|cx| Self {
            shared_state: shared,
        })
    }

    fn update_shared(&mut self, cx: &mut Context<Self>) {
        self.shared_state.update(cx, |state, cx| {
            state.value = 42;
            cx.notify();
        });
    }
}

Async Operations

impl MyComponent {
    fn fetch_data(&mut self, cx: &mut Context<Self>) {
        let weak_self = cx.entity().downgrade();

        cx.spawn(async move |cx| {
            let data = fetch_from_api().await;

            // Update entity safely
            let _ = weak_self.update(cx, |state, cx| {
                state.data = Some(data);
                cx.notify();
            });
        }).detach();
    }
}

Core Principles

Always Use Weak References in Closures

// ✅ Good: Weak reference prevents retain cycles
let weak = cx.entity().downgrade();
callback(move || {
    let _ = weak.update(cx, |state, cx| cx.notify());
});

// ❌ Bad: Strong reference may cause memory leak
let strong = cx.entity();
callback(move || {
    strong.update(cx, |state, cx| cx.notify());
});

Use Inner Context

// ✅ Good: Use inner cx from closure
entity.update(cx, |state, inner_cx| {
    inner_cx.notify(); // Correct
});

// ❌ Bad: Use outer cx (multiple borrow error)
entity.update(cx, |state, inner_cx| {
    cx.notify(); // Wrong!
});

Avoid Nested Updates

// ✅ Good: Sequential updates
entity1.update(cx, |state, cx| { /* ... */ });
entity2.update(cx, |state, cx| { /* ... */ });

// ❌ Bad: Nested updates (may panic)
entity1.update(cx, |_, cx| {
    entity2.update(cx, |_, cx| { /* ... */ });
});

Common Use Cases

  1. Component State: Internal state that needs reactivity
  2. Shared State: State shared between multiple components
  3. Parent-Child: Coordinating between related components (use weak refs)
  4. Async State: Managing state that changes from async operations
  5. Observations: Reacting to changes in other entities

Reference Documentation

Complete API Documentation

  • Entity API: See api-reference.md
    • Entity types, methods, lifecycle
    • Context methods, async operations
    • Error handling, type conversions

Implementation Guides

  • Patterns: See patterns.md

    • Model-view separation, state management
    • Cross-entity communication, async operations
    • Observer pattern, event subscription
    • Pattern selection guide
  • Best Practices: See best-practices.md

    • Avoiding common pitfalls, memory leaks
    • Performance optimization, batching updates
    • Lifecycle management, cleanup
    • Async best practices, testing
  • Advanced Patterns: See advanced.md

    • Entity collections, registry pattern
    • Debounced/throttled updates, state machines
    • Entity snapshots, transactions, pools
GPUI事件处理技能,支持自定义事件定义与发射、实体状态观察及组件间订阅。涵盖父子通信、全局广播和观察者模式,助力实现类型安全的事件驱动架构与组件协调。
需要实现组件间通信 监听实体状态变化 构建事件驱动系统 父向子传递消息
.codex/skills/gpui-event/SKILL.md
npx skills add feigeCode/navop --skill gpui-event -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-event",
    "description": "Event handling and subscriptions in GPUI. Use when implementing events, observers, or event-driven patterns. Supports custom events, entity observations, and event subscriptions for coordinating between components."
}

Overview

GPUI provides event system for component coordination:

Event Mechanisms:

  • Custom Events: Define and emit type-safe events
  • Observations: React to entity state changes
  • Subscriptions: Listen to events from other entities
  • Global Events: App-wide event handling

Quick Start

Define and Emit Events

#[derive(Clone)]
enum MyEvent {
    DataUpdated(String),
    ActionTriggered,
}

impl MyComponent {
    fn update_data(&mut self, data: String, cx: &mut Context<Self>) {
        self.data = data.clone();

        // Emit event
        cx.emit(MyEvent::DataUpdated(data));
        cx.notify();
    }
}

Subscribe to Events

impl Listener {
    fn new(source: Entity<MyComponent>, cx: &mut App) -> Entity<Self> {
        cx.new(|cx| {
            // Subscribe to events
            cx.subscribe(&source, |this, emitter, event: &MyEvent, cx| {
                match event {
                    MyEvent::DataUpdated(data) => {
                        this.handle_update(data.clone(), cx);
                    }
                    MyEvent::ActionTriggered => {
                        this.handle_action(cx);
                    }
                }
            }).detach();

            Self { source }
        })
    }
}

Observe Entity Changes

impl Observer {
    fn new(target: Entity<Target>, cx: &mut App) -> Entity<Self> {
        cx.new(|cx| {
            // Observe entity for any changes
            cx.observe(&target, |this, observed, cx| {
                // Called when observed.update() calls cx.notify()
                println!("Target changed");
                cx.notify();
            }).detach();

            Self { target }
        })
    }
}

Common Patterns

1. Parent-Child Communication

// Parent emits events
impl Parent {
    fn notify_children(&mut self, cx: &mut Context<Self>) {
        cx.emit(ParentEvent::Updated);
        cx.notify();
    }
}

// Children subscribe
impl Child {
    fn new(parent: Entity<Parent>, cx: &mut App) -> Entity<Self> {
        cx.new(|cx| {
            cx.subscribe(&parent, |this, parent, event, cx| {
                this.handle_parent_event(event, cx);
            }).detach();

            Self { parent }
        })
    }
}

2. Global Event Broadcasting

struct EventBus {
    listeners: Vec<WeakEntity<dyn Listener>>,
}

impl EventBus {
    fn broadcast(&mut self, event: GlobalEvent, cx: &mut Context<Self>) {
        self.listeners.retain(|weak| {
            weak.update(cx, |listener, cx| {
                listener.on_event(&event, cx);
            }).is_ok()
        });
    }
}

3. Observer Pattern

cx.observe(&entity, |this, observed, cx| {
    // React to any state change
    let state = observed.read(cx);
    this.sync_with_state(state, cx);
}).detach();

Best Practices

✅ Detach Subscriptions

// ✅ Detach to keep alive
cx.subscribe(&entity, |this, source, event, cx| {
    // Handle event
}).detach();

✅ Clean Event Types

#[derive(Clone)]
enum AppEvent {
    DataChanged { id: usize, value: String },
    ActionPerformed(ActionType),
    Error(String),
}

❌ Avoid Event Loops

// ❌ Don't create mutual subscriptions
entity1.subscribe(entity2) → emits event
entity2.subscribe(entity1) → emits event → infinite loop!

Reference Documentation

  • API Reference: See api-reference.md

    • Event definition, emission, subscriptions
    • Observations, global events
    • Subscription lifecycle
  • Patterns: See patterns.md

    • Event-driven architectures
    • Communication patterns
    • Best practices and pitfalls
用于GPUI界面中的焦点管理与键盘导航。支持创建焦点句柄、追踪当前焦点元素、处理Tab键切换及On_focus/On_blur事件,实现键盘驱动的交互体验。
需要管理UI元素的焦点状态 实现键盘快捷键或Tab顺序导航 监听和处理焦点获取与失去事件
.codex/skills/gpui-focus-handle/SKILL.md
npx skills add feigeCode/navop --skill gpui-focus-handle -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-focus-handle",
    "description": "Focus management and keyboard navigation in GPUI. Use when handling focus, focus handles, or keyboard navigation. Enables keyboard-driven interfaces with proper focus tracking and navigation between focusable elements."
}

Overview

GPUI's focus system enables keyboard navigation and focus management.

Key Concepts:

  • FocusHandle: Reference to focusable element
  • Focus tracking: Current focused element
  • Keyboard navigation: Tab/Shift-Tab between elements
  • Focus events: on_focus, on_blur

Quick Start

Creating Focus Handles

struct FocusableComponent {
    focus_handle: FocusHandle,
}

impl FocusableComponent {
    fn new(cx: &mut Context<Self>) -> Self {
        Self {
            focus_handle: cx.focus_handle(),
        }
    }
}

Making Elements Focusable

impl Render for FocusableComponent {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .track_focus(&self.focus_handle)
            .on_action(cx.listener(Self::on_enter))
            .child("Focusable content")
    }

    fn on_enter(&mut self, _: &Enter, cx: &mut Context<Self>) {
        // Handle Enter key when focused
        cx.notify();
    }
}

Focus Management

impl MyComponent {
    fn focus(&mut self, cx: &mut Context<Self>) {
        self.focus_handle.focus(cx);
    }

    fn is_focused(&self, cx: &App) -> bool {
        self.focus_handle.is_focused(cx)
    }

    fn blur(&mut self, cx: &mut Context<Self>) {
        cx.blur();
    }
}

Focus Events

Handling Focus Changes

impl Render for MyInput {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        let is_focused = self.focus_handle.is_focused(cx);

        div()
            .track_focus(&self.focus_handle)
            .on_focus(cx.listener(|this, _event, cx| {
                this.on_focus(cx);
            }))
            .on_blur(cx.listener(|this, _event, cx| {
                this.on_blur(cx);
            }))
            .when(is_focused, |el| {
                el.bg(cx.theme().focused_background)
            })
            .child(self.render_content())
    }
}

impl MyInput {
    fn on_focus(&mut self, cx: &mut Context<Self>) {
        // Handle focus gained
        cx.notify();
    }

    fn on_blur(&mut self, cx: &mut Context<Self>) {
        // Handle focus lost
        cx.notify();
    }
}

Keyboard Navigation

Tab Order

Elements with track_focus() automatically participate in Tab navigation.

div()
    .child(
        input1.track_focus(&focus1)  // Tab order: 1
    )
    .child(
        input2.track_focus(&focus2)  // Tab order: 2
    )
    .child(
        input3.track_focus(&focus3)  // Tab order: 3
    )

Focus Within Containers

impl Container {
    fn focus_first(&mut self, cx: &mut Context<Self>) {
        if let Some(first) = self.children.first() {
            first.update(cx, |child, cx| {
                child.focus_handle.focus(cx);
            });
        }
    }

    fn focus_next(&mut self, cx: &mut Context<Self>) {
        // Custom focus navigation logic
    }
}

Common Patterns

1. Auto-focus on Mount

impl MyDialog {
    fn new(cx: &mut Context<Self>) -> Self {
        let focus_handle = cx.focus_handle();

        // Focus when created
        focus_handle.focus(cx);

        Self { focus_handle }
    }
}

2. Focus Trap (Modal)

impl Modal {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .track_focus(&self.focus_handle)
            .on_key_down(cx.listener(|this, event: &KeyDownEvent, cx| {
                if event.key == Key::Tab {
                    // Keep focus within modal
                    this.focus_next_in_modal(cx);
                    cx.stop_propagation();
                }
            }))
            .child(self.render_content())
    }
}

3. Conditional Focus

impl Searchable {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .track_focus(&self.focus_handle)
            .when(self.search_active, |el| {
                el.on_mount(cx.listener(|this, _, cx| {
                    this.focus_handle.focus(cx);
                }))
            })
            .child(self.search_input())
    }
}

Best Practices

✅ Track Focus on Interactive Elements

// ✅ Good: Track focus for keyboard interaction
input()
    .track_focus(&self.focus_handle)
    .on_action(cx.listener(Self::on_enter))

✅ Provide Visual Focus Indicators

let is_focused = self.focus_handle.is_focused(cx);

div()
    .when(is_focused, |el| {
        el.border_color(cx.theme().focused_border)
    })

❌ Don't: Forget to Track Focus

// ❌ Bad: No track_focus, keyboard navigation won't work
div()
    .on_action(cx.listener(Self::on_enter))

Reference Documentation

  • API Reference: See api-reference.md
    • FocusHandle API, focus management
    • Events, keyboard navigation
    • Best practices
管理GPUI应用全局状态,支持通过实现Global trait定义、设置和访问应用级共享数据。涵盖配置、功能开关及共享服务场景,强调使用Arc优化资源克隆与手动通知机制。
需要跨组件共享应用配置 实现全局功能标志位控制 注册和管理全局共享服务
.codex/skills/gpui-global/SKILL.md
npx skills add feigeCode/navop --skill gpui-global -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-global",
    "description": "Global state management in GPUI. Use when implementing global state, app-wide configuration, or shared resources."
}

Overview

Global state in GPUI provides app-wide shared data accessible from any context.

Key Trait: Global - Implement on types to make them globally accessible

Quick Start

Define Global State

use gpui::Global;

#[derive(Clone)]
struct AppSettings {
    theme: Theme,
    language: String,
}

impl Global for AppSettings {}

Set and Access Globals

fn main() {
    let app = Application::new();
    app.run(|cx: &mut App| {
        // Set global
        cx.set_global(AppSettings {
            theme: Theme::Dark,
            language: "en".to_string(),
        });

        // Access global (read-only)
        let settings = cx.global::<AppSettings>();
        println!("Theme: {:?}", settings.theme);
    });
}

Update Globals

impl MyComponent {
    fn change_theme(&mut self, new_theme: Theme, cx: &mut Context<Self>) {
        cx.update_global::<AppSettings, _>(|settings, cx| {
            settings.theme = new_theme;
            // Global updates don't trigger automatic notifications
            // Manually notify components that care
        });

        cx.notify(); // Re-render this component
    }
}

Common Use Cases

1. App Configuration

#[derive(Clone)]
struct AppConfig {
    api_endpoint: String,
    max_retries: u32,
    timeout: Duration,
}

impl Global for AppConfig {}

// Set once at startup
cx.set_global(AppConfig {
    api_endpoint: "https://api.example.com".to_string(),
    max_retries: 3,
    timeout: Duration::from_secs(30),
});

// Access anywhere
let config = cx.global::<AppConfig>();

2. Feature Flags

#[derive(Clone)]
struct FeatureFlags {
    enable_beta_features: bool,
    enable_analytics: bool,
}

impl Global for FeatureFlags {}

impl MyComponent {
    fn render_beta_feature(&self, cx: &App) -> Option<impl IntoElement> {
        let flags = cx.global::<FeatureFlags>();

        if flags.enable_beta_features {
            Some(div().child("Beta feature"))
        } else {
            None
        }
    }
}

3. Shared Services

#[derive(Clone)]
struct ServiceRegistry {
    http_client: Arc<HttpClient>,
    logger: Arc<Logger>,
}

impl Global for ServiceRegistry {}

impl MyComponent {
    fn fetch_data(&mut self, cx: &mut Context<Self>) {
        let registry = cx.global::<ServiceRegistry>();
        let client = registry.http_client.clone();

        cx.spawn(async move |cx| {
            let data = client.get("api/data").await?;
            // Process data...
            Ok::<_, anyhow::Error>(())
        }).detach();
    }
}

Best Practices

✅ Use Arc for Shared Resources

#[derive(Clone)]
struct GlobalState {
    database: Arc<Database>,  // Cheap to clone
    cache: Arc<RwLock<Cache>>,
}

impl Global for GlobalState {}

✅ Immutable by Default

Globals are read-only by default. Use interior mutability when needed:

#[derive(Clone)]
struct Counter {
    count: Arc<AtomicUsize>,
}

impl Global for Counter {}

impl Counter {
    fn increment(&self) {
        self.count.fetch_add(1, Ordering::SeqCst);
    }

    fn get(&self) -> usize {
        self.count.load(Ordering::SeqCst)
    }
}

❌ Don't: Overuse Globals

// ❌ Bad: Too many globals
cx.set_global(UserState { ... });
cx.set_global(CartState { ... });
cx.set_global(CheckoutState { ... });

// ✅ Good: Use entities for component state
let user_entity = cx.new(|_| UserState { ... });

When to Use

Use Globals for:

  • App-wide configuration
  • Feature flags
  • Shared services (HTTP client, logger)
  • Read-only reference data

Use Entities for:

  • Component-specific state
  • State that changes frequently
  • State that needs notifications

Reference Documentation

  • API Reference: See api-reference.md
    • Global trait, set_global, update_global
    • Interior mutability patterns
    • Best practices and anti-patterns
提供GPUI中类似CSS的Rust类型安全样式与布局指南。涵盖Flexbox、尺寸单位、颜色边框及主题集成,支持链式调用实现响应式间距、卡片布局和条件样式,帮助开发者高效构建界面组件。
需要设置GPUI组件的宽高或位置 配置Flexbox布局属性 应用背景色、文本颜色或边框样式 集成主题系统或处理悬停状态
.codex/skills/gpui-layout-and-style/SKILL.md
npx skills add feigeCode/navop --skill gpui-layout-and-style -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-layout-and-style",
    "description": "Layout and styling in GPUI. Use when styling components, layout systems, or CSS-like properties."
}

Overview

GPUI provides CSS-like styling with Rust type safety.

Key Concepts:

  • Flexbox layout system
  • Styled trait for chaining styles
  • Size units: px(), rems(), relative()
  • Colors, borders, shadows

Quick Start

Basic Styling

use gpui::*;

div()
    .w(px(200.))
    .h(px(100.))
    .bg(rgb(0x2196F3))
    .text_color(rgb(0xFFFFFF))
    .rounded(px(8.))
    .p(px(16.))
    .child("Styled content")

Flexbox Layout

div()
    .flex()
    .flex_row()  // or flex_col() for column
    .gap(px(8.))
    .items_center()
    .justify_between()
    .children([
        div().child("Item 1"),
        div().child("Item 2"),
        div().child("Item 3"),
    ])

Size Units

div()
    .w(px(200.))           // Pixels
    .h(rems(10.))          // Relative to font size
    .w(relative(0.5))      // 50% of parent
    .min_w(px(100.))
    .max_w(px(400.))

Common Patterns

Centered Content

div()
    .flex()
    .items_center()
    .justify_center()
    .size_full()
    .child("Centered")

Card Layout

div()
    .w(px(300.))
    .bg(cx.theme().surface)
    .rounded(px(8.))
    .shadow_md()
    .p(px(16.))
    .gap(px(12.))
    .flex()
    .flex_col()
    .child(heading())
    .child(content())

Responsive Spacing

div()
    .p(px(16.))           // Padding all sides
    .px(px(20.))          // Padding horizontal
    .py(px(12.))          // Padding vertical
    .pt(px(8.))           // Padding top
    .gap(px(8.))          // Gap between children

Styling Methods

Dimensions

.w(px(200.))              // Width
.h(px(100.))              // Height
.size(px(200.))           // Width and height
.min_w(px(100.))          // Min width
.max_w(px(400.))          // Max width

Colors

.bg(rgb(0x2196F3))        // Background
.text_color(rgb(0xFFFFFF)) // Text color
.border_color(rgb(0x000000)) // Border color

Borders

.border(px(1.))           // Border width
.rounded(px(8.))          // Border radius
.rounded_t(px(8.))        // Top corners
.border_color(rgb(0x000000))

Spacing

.p(px(16.))               // Padding
.m(px(8.))                // Margin
.gap(px(8.))              // Gap between flex children

Flexbox

.flex()                   // Enable flexbox
.flex_row()               // Row direction
.flex_col()               // Column direction
.items_center()           // Align items center
.justify_between()        // Space between items
.flex_grow()              // Grow to fill space

Theme Integration

div()
    .bg(cx.theme().surface)
    .text_color(cx.theme().foreground)
    .border_color(cx.theme().border)
    .when(is_hovered, |el| {
        el.bg(cx.theme().hover)
    })

Conditional Styling

div()
    .when(is_active, |el| {
        el.bg(cx.theme().primary)
    })
    .when_some(optional_color, |el, color| {
        el.bg(color)
    })

Reference Documentation

  • Complete Guide: See reference.md
    • All styling methods
    • Layout strategies
    • Theming, responsive design
基于gpui-component代码库分析的组件风格指南,涵盖基本与状态组件结构、Trait实现及命名规范。用于编写新组件、代码审查及确保与现有实现的一致性。
编写新的GPUI组件 审查GPUI组件代码 确保代码风格一致性
.codex/skills/gpui-style-guide/SKILL.md
npx skills add feigeCode/navop --skill gpui-style-guide -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-style-guide",
    "description": "GPUI Component project style guide based on gpui-component code patterns. Use when writing new components, reviewing code, or ensuring consistency with existing gpui-component implementations. Covers component structure, trait implementations, naming conventions, and API patterns observed in the actual codebase."
}

Overview

Code style guide derived from gpui-component implementation patterns.

Based on: Analysis of Button, Checkbox, Input, Select, and other components in crates/ui

Component Structure

Basic Component Pattern

use gpui::{
    div, prelude::FluentBuilder as _, AnyElement, App, Div, ElementId,
    InteractiveElement, IntoElement, ParentElement, RenderOnce,
    StatefulInteractiveElement, StyleRefinement, Styled, Window,
};

#[derive(IntoElement)]
pub struct MyComponent {
    id: ElementId,
    base: Div,
    style: StyleRefinement,

    // Configuration fields
    size: Size,
    disabled: bool,
    selected: bool,

    // Content fields
    label: Option<Text>,
    children: Vec<AnyElement>,

    // Callbacks (use Rc for Clone)
    on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
}

impl MyComponent {
    pub fn new(id: impl Into<ElementId>) -> Self {
        Self {
            id: id.into(),
            base: div(),
            style: StyleRefinement::default(),
            size: Size::default(),
            disabled: false,
            selected: false,
            label: None,
            children: Vec::new(),
            on_click: None,
        }
    }

    // Builder methods
    pub fn label(mut self, label: impl Into<Text>) -> Self {
        self.label = Some(label.into());
        self
    }

    pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
        self.on_click = Some(Rc::new(handler));
        self
    }
}

impl InteractiveElement for MyComponent {
    fn interactivity(&mut self) -> &mut gpui::Interactivity {
        self.base.interactivity()
    }
}

impl StatefulInteractiveElement for MyComponent {}

impl Styled for MyComponent {
    fn style(&mut self) -> &mut StyleRefinement {
        &mut self.style
    }
}

impl RenderOnce for MyComponent {
    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
        // Implementation
        self.base
    }
}

Stateful Component Pattern

#[derive(IntoElement)]
pub struct Select {
    state: Entity<SelectState>,
    style: StyleRefinement,
    size: Size,
    // ...
}

pub struct SelectState {
    open: bool,
    selected_index: Option<usize>,
    // ...
}

impl Select {
    pub fn new(state: &Entity<SelectState>) -> Self {
        Self {
            state: state.clone(),
            size: Size::default(),
            style: StyleRefinement::default(),
        }
    }
}

Trait Implementations

Sizable

impl Sizable for MyComponent {
    fn with_size(mut self, size: impl Into<Size>) -> Self {
        self.size = size.into();
        self
    }
}

Selectable

impl Selectable for MyComponent {
    fn selected(mut self, selected: bool) -> Self {
        self.selected = selected;
        self
    }

    fn is_selected(&self) -> bool {
        self.selected
    }
}

Disableable

impl Disableable for MyComponent {
    fn disabled(mut self, disabled: bool) -> Self {
        self.disabled = disabled;
        self
    }

    fn is_disabled(&self) -> bool {
        self.disabled
    }
}

Variant Patterns

Enum Variants

#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub enum ButtonVariant {
    Primary,
    #[default]
    Secondary,
    Danger,
    Success,
    Warning,
    Ghost,
    Link,
}

Trait-Based Variant API

pub trait ButtonVariants: Sized {
    fn with_variant(self, variant: ButtonVariant) -> Self;

    /// With the primary style for the Button.
    fn primary(self) -> Self {
        self.with_variant(ButtonVariant::Primary)
    }

    /// With the danger style for the Button.
    fn danger(self) -> Self {
        self.with_variant(ButtonVariant::Danger)
    }

    // ... more variants
}

Custom Variant Pattern

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ButtonCustomVariant {
    color: Hsla,
    foreground: Hsla,
    border: Hsla,
    hover: Hsla,
    active: Hsla,
    shadow: bool,
}

impl ButtonCustomVariant {
    pub fn new(cx: &App) -> Self {
        Self {
            color: cx.theme().transparent,
            foreground: cx.theme().foreground,
            // ...
            shadow: false,
        }
    }

    pub fn color(mut self, color: Hsla) -> Self {
        self.color = color;
        self
    }

    // ... more builder methods
}

Action and Keybinding Patterns

Context Constant

const CONTEXT: &str = "Select";

Init Function

pub(crate) fn init(cx: &mut App) {
    cx.bind_keys([
        KeyBinding::new("up", SelectUp, Some(CONTEXT)),
        KeyBinding::new("down", SelectDown, Some(CONTEXT)),
        KeyBinding::new("enter", Confirm { secondary: false }, Some(CONTEXT)),
        KeyBinding::new("escape", Cancel, Some(CONTEXT)),
    ])
}

Action Usage

use crate::actions::{Cancel, Confirm, SelectDown, SelectUp};

div()
    .key_context(CONTEXT)
    .on_action(cx.listener(Self::on_action_select_up))
    .on_action(cx.listener(Self::on_action_confirm))

Trait Definitions

Item Traits

pub trait SelectItem: Clone {
    type Value: Clone;

    fn title(&self) -> SharedString;

    fn display_title(&self) -> Option<AnyElement> {
        None
    }

    fn render(&self, _: &mut Window, _: &mut App) -> impl IntoElement {
        self.title().into_element()
    }

    fn value(&self) -> &Self::Value;

    fn matches(&self, query: &str) -> bool {
        self.title().to_lowercase().contains(&query.to_lowercase())
    }
}

Implement for Common Types

impl SelectItem for String {
    type Value = Self;

    fn title(&self) -> SharedString {
        SharedString::from(self.to_string())
    }

    fn value(&self) -> &Self::Value {
        &self
    }
}

impl SelectItem for SharedString { /* ... */ }
impl SelectItem for &'static str { /* ... */ }

Icon Pattern

IconNamed Trait

pub trait IconNamed {
    fn path(self) -> SharedString;
}

impl<T: IconNamed> From<T> for Icon {
    fn from(value: T) -> Self {
        Icon::build(value)
    }
}

IconName Enum

#[derive(IntoElement, Clone)]
pub enum IconName {
    ArrowDown,
    ArrowUp,
    Check,
    Close,
    // ... all icon names
}

Documentation Patterns

Component Documentation

/// A Checkbox element.
#[derive(IntoElement)]
pub struct Checkbox { }

Method Documentation

impl Checkbox {
    /// Create a new Checkbox with the given id.
    pub fn new(id: impl Into<ElementId>) -> Self { }

    /// Set the label for the checkbox.
    pub fn label(mut self, label: impl Into<Text>) -> Self { }

    /// Set the click handler for the checkbox.
    ///
    /// The `&bool` parameter indicates the new checked state after the click.
    pub fn on_click(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self { }
}

Import Organization Pattern

// 1. External crate imports
use std::rc::Rc;

// 2. Crate imports
use crate::{
    ActiveTheme, Disableable, FocusableExt, Icon, IconName,
    Selectable, Sizable, Size, StyledExt,
};

// 3. GPUI imports
use gpui::{
    div, prelude::FluentBuilder as _, px, relative, rems,
    AnyElement, App, Div, ElementId, InteractiveElement,
    IntoElement, ParentElement, RenderOnce,
    StatefulInteractiveElement, StyleRefinement, Styled, Window,
};

Field Organization

pub struct Component {
    // 1. Identity
    id: ElementId,
    base: Div,
    style: StyleRefinement,

    // 2. Configuration
    size: Size,
    disabled: bool,
    selected: bool,
    tab_stop: bool,
    tab_index: isize,

    // 3. Content/children
    label: Option<Text>,
    children: Vec<AnyElement>,
    prefix: Option<AnyElement>,
    suffix: Option<AnyElement>,

    // 4. Callbacks (last)
    on_click: Option<Rc<dyn Fn(Args, &mut Window, &mut App) + 'static>>,
}

Common Patterns

Optional Elements

pub fn prefix(mut self, prefix: impl IntoElement) -> Self {
    self.prefix = Some(prefix.into_any_element());
    self
}

Callback Patterns

// Pattern 1: Event parameter first
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
    self.on_click = Some(Rc::new(handler));
    self
}

// Pattern 2: State parameter
pub fn on_change(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
    self.on_change = Some(Rc::new(handler));
    self
}

Static Handler Functions

fn handle_click(
    on_click: &Option<Rc<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
    checked: bool,
    window: &mut Window,
    cx: &mut App,
) {
    let new_checked = !checked;
    if let Some(f) = on_click {
        (f)(&new_checked, window, cx);
    }
}

Boolean Methods

// Enable/disable patterns
pub fn cleanable(mut self, cleanable: bool) -> Self {
    self.cleanable = cleanable;
    self
}

// Toggle methods (no parameter)
pub fn mask_toggle(mut self) -> Self {
    self.mask_toggle = true;
    self
}

Size Methods

Size Trait

impl Sizable for Component {
    fn with_size(mut self, size: impl Into<Size>) -> Self {
        self.size = size.into();
        self
    }
}

Convenience Size Methods (from StyleSized trait)

Components get .xsmall(), .small(), .medium(), .large() automatically via StyleSized trait.

Rendering Patterns

RenderOnce Pattern

impl RenderOnce for MyComponent {
    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
        let (width, height) = self.size.input_size();

        self.base
            .id(self.id)
            .flex()
            .items_center()
            .gap(px(8.))
            .min_w(width)
            .h(height)
            .when(self.disabled, |this| {
                this.opacity(0.5).cursor_not_allowed()
            })
            .children(self.children)
    }
}

Theme Usage

// Access theme colors
cx.theme().surface
cx.theme().foreground
cx.theme().border
cx.theme().primary
cx.theme().transparent

// Use in components
div()
    .bg(cx.theme().surface)
    .text_color(cx.theme().foreground)
    .border_color(cx.theme().border)

Reference Documentation

  • Component Examples: See component-examples.md

    • Full component implementations
    • Common patterns in action
  • Trait Patterns: See trait-patterns.md

    • Detailed trait implementation guides
    • Custom trait design patterns

Quick Checklist

When creating a new component in crates/ui:

  • #[derive(IntoElement)] on struct
  • Include id: ElementId, base: Div, style: StyleRefinement
  • Implement InteractiveElement, StatefulInteractiveElement, Styled
  • Implement RenderOnce trait
  • Implement Sizable if component has sizes
  • Implement Selectable if component can be selected
  • Implement Disableable if component can be disabled
  • Use Rc<dyn Fn> for callbacks
  • Use Option<AnyElement> for optional child elements
  • Import prelude::FluentBuilder as _
  • Use theme colors via cx.theme()
  • Follow field organization pattern
用于编写GPUI应用的测试,支持组件、异步操作和UI行为测试。提供基础、异步及属性测试属性,以及TestAppContext和VisualTestContext两种上下文环境,并包含优化规则以减少不必要的依赖。
需要测试GPUI UI组件 需要验证异步操作逻辑 需要测试分布式系统或窗口相关行为
.codex/skills/gpui-test/SKILL.md
npx skills add feigeCode/navop --skill gpui-test -g -y
SKILL.md
Frontmatter
{
    "name": "gpui-test",
    "description": "Writing tests for GPUI applications. Use when testing components, async operations, or UI behavior."
}

Overview

GPUI provides a comprehensive testing framework that allows you to test UI components, async operations, and distributed systems. Tests run on a single-threaded executor that provides deterministic execution and the ability to test complex async scenarios. GPUI tests use the #[gpui::test] attribute and work with TestAppContext for basic testing and VisualTestContext for window-dependent tests.

Rules

  • If test does not require windows or rendering, we can avoid use [gpui::test] and TestAppContext, just write simple rust test.

Core Testing Infrastructure

Test Attributes

Basic Test

#[gpui::test]
fn my_test(cx: &mut TestAppContext) {
    // Test implementation
}

Async Test

#[gpui::test]
async fn my_async_test(cx: &mut TestAppContext) {
    // Async test implementation
}

Property Test with Iterations

#[gpui::test(iterations = 10)]
fn my_property_test(cx: &mut TestAppContext, mut rng: StdRng) {
    // Property testing with random data
}

Test Contexts

TestAppContext

TestAppContext provides access to GPUI's core functionality without windows:

#[gpui::test]
fn test_entity_operations(cx: &mut TestAppContext) {
    // Create entities
    let entity = cx.new(|cx| MyComponent::new(cx));

    // Update entities
    entity.update(cx, |component, cx| {
        component.value = 42;
        cx.notify();
    });

    // Read entities
    let value = entity.read_with(cx, |component, _| component.value);
    assert_eq!(value, 42);
}

VisualTestContext

VisualTestContext extends TestAppContext with window support:

#[gpui::test]
fn test_with_window(cx: &mut TestAppContext) {
    // Create window with component
    let window = cx.update(|cx| {
        cx.open_window(Default::default(), |_, cx| {
            cx.new(|cx| MyComponent::new(cx))
        }).unwrap()
    });

    // Convert to visual context
    let mut cx = VisualTestContext::from_window(window.into(), cx);

    // Access window and component
    let component = window.root(&mut cx).unwrap();
}

Additional Resources

  • For detailed testing patterns and examples, see reference.md
  • For best practices and running tests, see examples.md
用于Navop仓库生成、审查和发布中英双语GitHub Release说明。基于git历史总结用户可见变更,遵循既定或默认模板,通过gh CLI操作并验证远程发布内容。
需要为Navop项目版本标签生成Release Notes 需要审查或发布GitHub Releases 需要验证已发布的Release内容
.codex/skills/navop-release-notes/SKILL.md
npx skills add feigeCode/navop --skill navop-release-notes -g -y
SKILL.md
Frontmatter
{
    "name": "navop-release-notes",
    "description": "Use when working in the Navop repository on version tags or GitHub Releases, especially when release notes must be generated, reviewed, published, or verified in Chinese and English."
}

Navop Release Notes

Overview

Generate Navop GitHub Release notes from git history. Preserve established Release style when it exists; otherwise use the bilingual default in this skill. Use gh for GitHub reads and writes, and verify the remote Release after saving.

Required Context

Work from the Navop repo root and confirm the remote points to feigeCode/navop:

rtk git status --short --branch
rtk git remote -v
rtk git tag --sort=-creatordate
rtk gh release list --limit 8

Default version boundary is the newest two version tags, for example v0.8.8..v0.8.9. If the user names versions, use those exact tags. Never update a release until the target tag and previous tag are explicit.

Before writing notes, inspect recent Release style:

rtk gh release view <target-tag> --json tagName,name,body,publishedAt,url
rtk gh release view <previous-tag> --json tagName,name,body,publishedAt,url
rtk gh release view <older-tag> --json tagName,name,body,publishedAt,url

If Navop has no established Release style yet, use this bilingual shape:

## 中文

### 更新内容

- ...

### 修复与调整

- ...

---

## English

### Changes

- ...

### Fixes

- ...

**Full Changelog**: https://github.com/feigeCode/navop/compare/<previous-tag>...<target-tag>

For larger releases, add short overview paragraphs and extra sections only when prior style or commit volume justifies it.

Generate Notes

Read commits and changed files:

rtk git log --reverse --format='%H%n%s%n%b%n---END---' <previous-tag>..<target-tag>
rtk git diff --stat <previous-tag>..<target-tag>
rtk git diff --name-status <previous-tag>..<target-tag>

For unclear commits, inspect targeted diffs:

rtk git show --stat --oneline --find-renames <commit>
rtk git show --format=medium --find-renames <commit> -- <path>

Summarize user-facing behavior, not implementation trivia. Use categories:

  • Chinese 更新内容: features, UX improvements, performance, workflow improvements.
  • Chinese 修复与调整: bug fixes, compatibility, stability, maintenance with user impact.
  • English Changes: faithful English version of 更新内容.
  • English Fixes: faithful English version of 修复与调整.

Include maintenance bullets only when visible in commits and useful to release readers. Keep internal refactors out unless they explain a visible behavior change.

Save Locally

Save the generated body to a temporary Markdown file first. Prefer /private/tmp on this machine:

NOTES=/private/tmp/navop-<target-tag>-release-notes.md

Create the file with normal editing tools, then review it:

rtk sed -n '1,240p' /private/tmp/navop-<target-tag>-release-notes.md

The file must contain both ## 中文 and ## English, and the final compare link must use three dots:

**Full Changelog**: https://github.com/feigeCode/navop/compare/<previous-tag>...<target-tag>

Save To GitHub

Use GitHub CLI release commands, not manual browser editing:

rtk gh release edit <target-tag> --notes-file /private/tmp/navop-<target-tag>-release-notes.md

If the release name is also wrong, set it explicitly:

rtk gh release edit <target-tag> --title "Navop <target-tag>" --notes-file /private/tmp/navop-<target-tag>-release-notes.md

Do not overwrite a non-empty hand-written Release body until you have read it and intentionally preserved or replaced the relevant parts.

Verify

Before saying the release notes are saved, run a fresh read:

rtk gh release view <target-tag> --json tagName,name,body,publishedAt,url

Verify:

  • tagName is the intended target tag.
  • body contains ## 中文, ## English, and the expected compare URL.
  • The visible content matches the local notes file.

Also check the working tree so unrelated local edits are not mistaken for release-note work:

rtk git status --short --branch

Report the target URL and the verification command result summary.

Common Mistakes

Mistake Correct Action
Using only Chinese notes Publish matching Chinese and English sections.
Assuming legacy repository details Confirm the remote is feigeCode/navop and use Navop URLs, titles, and temp-file names.
Comparing the wrong tags Confirm newest two tags or use user-specified tags before writing.
Listing raw commits Group commits into release-reader categories.
Losing existing release text Read the existing body first and decide whether to preserve or replace.
Claiming saved without verification Re-read the GitHub Release with gh release view first.
指导创建GPUI组件,需遵循现有代码模式与Shadcn UI风格。根据复杂度选择无状态或有状态实现,保持API一致性,并生成文档及Story用例。
构建新组件 编写UI元素 创建组件实现
.codex/skills/new-component/SKILL.md
npx skills add feigeCode/navop --skill new-component -g -y
SKILL.md
Frontmatter
{
    "name": "new-component",
    "description": "Create new GPUI components. Use when building components, writing UI elements, or creating new component implementations."
}

Instructions

When creating new GPUI components:

  1. Follow existing patterns: Base implementation on components in crates/ui/src (examples: Button, Select)
  2. Style consistency: Follow existing component styles and Shadcn UI patterns
  3. Component type decision:
    • Use stateless elements for simple components (like Button)
    • Use stateful elements for complex components with data (like Select and SelectState)
  4. API consistency: Maintain the same API style as other elements
  5. Documentation: Create component documentation
  6. Stories: Write component stories in the story folder

Component Types

  • Stateless: Pure presentation components without internal state
  • Stateful: Components that manage their own state and data

首页 - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-07-22 05:10
浙ICP备14020137号-1 $访客地图$