Token导航 LogoToken导航TokenDH.com
前端设计只读github未标认证来源可访问clear审计异常

gpui-patternsGPU 模式

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

643

周安装

26

GitHub Stars

8

下载量

202
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:gpui-patterns(GPU 模式)
来源仓库:https://github.com/geoffjay/claude-plugins
仓库路径:skills/gpui-patterns
安装命令:
npx skills add https://github.com/geoffjay/claude-plugins --skill gpui-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/geoffjay/claude-plugins --skill gpui-patterns

简介

gpui-patterns 辅助前端页面与组件的开发与维护工作。

  • 支持 React、Next.js、Vue 等技术栈的代码生成与审查。
  • 可用于整理组件结构或诊断布局与性能问题。
  • 需结合项目现有设计系统和路由机制使用。gpui-patterns 属于前端设计类 Skill,可作为该场景下的辅助能力补充。
  • 涉及页面改动时应配合本地预览确认效果。

SKILL.md

GPUI Patterns

Metadata

This skill provides comprehensive guidance on common GPUI patterns and best practices for building maintainable, performant applications.

Instructions

Component Composition Patterns

Basic Component Structure

use gpui::*;

// View component with state
struct MyView {
    state: Model<MyState>,
    _subscription: Subscription,
}

impl MyView {
    fn new(state: Model<MyState>, cx: &mut ViewContext<Self>) -> Self {
        let _subscription = cx.observe(&state, |_, _, cx| cx.notify());
        Self { state, _subscription }
    }
}

impl Render for MyView {
    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
        let state = self.state.read(cx);

        div()
            .flex()
            .flex_col()
            .child(format!("Value: {}", state.value))
    }
}

Container/Presenter Pattern

Container (manages state and logic):

struct Container {
    model: Model<AppState>,
    _subscription: Subscription,
}

impl Container {
    fn new(model: Model<AppState>, cx: &mut ViewContext<Self>) -> Self {
        let _subscription = cx.observe(&model, |_, _, cx| cx.notify());
        Self { model, _subscription }
    }
}

impl Render for Container {
    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
        let state = self.model.read(cx);

        // Pass data to presenter
        Presenter::new(state.data.clone())
    }
}

Presenter (pure rendering):

struct Presenter {
    data: String,
}

impl Presenter {
    fn new(data: String) -> Self {
        Self { data }
    }
}

impl Render for Presenter {
    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
        div().child(self.data.as_str())
    }
}

Compound Components

// Parent component with shared context
pub struct Tabs {
    items: Vec<TabItem>,
    active_index: usize,
}

pub struct TabItem {
    label: String,
    content: Box<dyn Fn() -> AnyElement>,
}

impl Tabs {
    pub fn new() -> Self {
        Self {
            items: Vec::new(),
            active_index: 0,
        }
    }

    pub fn add_tab(
        mut self,
        label: impl Into<String>,
        content: impl Fn() -> AnyElement + 'static,
    ) -> Self {
        self.items.push(TabItem {
            label: label.into(),
            content: Box::new(content),
        });
        self
    }

    fn set_active(&mut self, index: usize, cx: &mut ViewContext<Self>) {
        self.active_index = index;
        cx.notify();
    }
}

impl Render for Tabs {
    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
        div()
            .flex()
            .flex_col()
            .child(
                // Tab headers
                div()
                    .flex()
                    .children(
                        self.items.iter().enumerate().map(|(i, item)| {
                            tab_header(&item.label, i == self.active_index, || {
                                self.set_active(i, cx)
                            })
                        })
                    )
            )
            .child(
                // Active tab content
                (self.items[self.active_index].content)()
            )
    }
}

State Management Strategies

Model-View Pattern

// Model: Application state
#[derive(Clone)]
struct AppState {
    count: usize,
    items: Vec<String>,
}

// View: Observes and renders state
struct AppView {
    state: Model<AppState>,
    _subscription: Subscription,
}

impl AppView {
    fn new(state: Model<AppState>, cx: &mut ViewContext<Self>) -> Self {
        let _subscription = cx.observe(&state, |_, _, cx| cx.notify());
        Self { state, _subscription }
    }

    fn increment(&mut self, cx: &mut ViewContext<Self>) {
        self.state.update(cx, |state, cx| {
            state.count += 1;
            cx.notify();
        });
    }
}

Context-Based State

// Global state via context
#[derive(Clone)]
struct GlobalSettings {
    theme: Theme,
    language: String,
}

impl Global for GlobalSettings {}

// Initialize in app
fn init_app(cx: &mut AppContext) {
    cx.set_global(GlobalSettings {
        theme: Theme::Light,
        language: "en".to_string(),
    });
}

// Access in components
impl Render for MyView {
    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
        let settings = cx.global::<GlobalSettings>();

        div()
            .child(format!("Language: {}", settings.language))
    }
}

Subscription Patterns

Basic Subscription:

struct Observer {
    model: Model<Data>,
    _subscription: Subscription,
}

impl Observer {
    fn new(model: Model<Data>, cx: &mut ViewContext<Self>) -> Self {
        let _subscription = cx.observe(&model, |_, _, cx| {
            cx.notify();  // Rerender on change
        });

        Self { model, _subscription }
    }
}

Selective Updates:

impl Observer {
    fn new(model: Model<Data>, cx: &mut ViewContext<Self>) -> Self {
        let _subscription = cx.observe(&model, |this, model, cx| {
            let data = model.read(cx);

            // Only rerender if specific field changed
            if data.important_field != this.cached_field {
                this.cached_field = data.important_field.clone();
                cx.notify();
            }
        });

        Self {
            model,
            cached_field: String::new(),
            _subscription,
        }
    }
}

Multiple Subscriptions:

struct MultiObserver {
    model_a: Model<DataA>,
    model_b: Model<DataB>,
    _subscriptions: Vec<Subscription>,
}

impl MultiObserver {
    fn new(
        model_a: Model<DataA>,
        model_b: Model<DataB>,
        cx: &mut ViewContext<Self>,
    ) -> Self {
        let mut subscriptions = Vec::new();

        subscriptions.push(cx.observe(&model_a, |_, _, cx| cx.notify()));
        subscriptions.push(cx.observe(&model_b, |_, _, cx| cx.notify()));

        Self {
            model_a,
            model_b,
            _subscriptions: subscriptions,
        }
    }
}

Event Handling Patterns

Click Events

div()
    .on_click(cx.listener(|this, event: &ClickEvent, cx| {
        // Handle click
        this.handle_click(cx);
    }))
    .child("Click me")

Keyboard Events

div()
    .on_key_down(cx.listener(|this, event: &KeyDownEvent, cx| {
        match event.key.as_str() {
            "Enter" => this.submit(cx),
            "Escape" => this.cancel(cx),
            _ => {}
        }
    }))

Event Propagation

// Stop propagation
div()
    .on_click(|event, cx| {
        event.stop_propagation();
        // Handle click
    })

// Prevent default
div()
    .on_key_down(|event, cx| {
        if event.key == "Tab" {
            event.prevent_default();
            // Custom tab handling
        }
    })

Mouse Events

div()
    .on_mouse_down(cx.listener(|this, event, cx| {
        this.mouse_down_position = Some(event.position);
    }))
    .on_mouse_move(cx.listener(|this, event, cx| {
        if let Some(start) = this.mouse_down_position {
            let delta = event.position - start;
            this.handle_drag(delta, cx);
        }
    }))
    .on_mouse_up(cx.listener(|this, event, cx| {
        this.mouse_down_position = None;
    }))

Action System

Define Actions

use gpui::*;

actions!(app, [
    Increment,
    Decrement,
    Reset,
    SetValue
]);

// Action with data
#[derive(Clone, PartialEq)]
pub struct SetValue {
    pub value: i32,
}

impl_actions!(app, [SetValue]);

Register Action Handlers

impl Counter {
    fn register_actions(&mut self, cx: &mut ViewContext<Self>) {
        cx.on_action(cx.listener(|this, _: &Increment, cx| {
            this.model.update(cx, |state, cx| {
                state.count += 1;
                cx.notify();
            });
        }));

        cx.on_action(cx.listener(|this, _: &Decrement, cx| {
            this.model.update(cx, |state, cx| {
                state.count = state.count.saturating_sub(1);
                cx.notify();
            });
        }));

        cx.on_action(cx.listener(|this, action: &SetValue, cx| {
            this.model.update(cx, |state, cx| {
                state.count = action.value;
                cx.notify();
            });
        }));
    }
}

Dispatch Actions

// From within component
fn handle_button_click(&mut self, cx: &mut ViewContext<Self>) {
    cx.dispatch_action(Increment);
}

// With data
fn set_specific_value(&mut self, value: i32, cx: &mut ViewContext<Self>) {
    cx.dispatch_action(SetValue { value });
}

// Global action dispatch
cx.dispatch_action_on_window(Reset, window_id);

Keybindings

// Register global keybindings
fn register_keybindings(cx: &mut AppContext) {
    cx.bind_keys([
        KeyBinding::new("cmd-+", Increment, None),
        KeyBinding::new("cmd--", Decrement, None),
        KeyBinding::new("cmd-0", Reset, None),
    ]);
}

Element Composition

Builder Pattern

fn card(title: &str, content: impl IntoElement) -> impl IntoElement {
    div()
        .flex()
        .flex_col()
        .bg(white())
        .border_1()
        .rounded_lg()
        .shadow_sm()
        .p_6()
        .child(
            div()
                .text_lg()
                .font_semibold()
                .mb_4()
                .child(title)
        )
        .child(content)
}

Conditional Rendering

div()
    .when(condition, |this| {
        this.bg(blue_500())
    })
    .when_some(optional_value, |this, value| {
        this.child(format!("Value: {}", value))
    })
    .map(|this| {
        if complex_condition {
            this.border_1()
        } else {
            this.border_2()
        }
    })

Dynamic Children

div()
    .children(
        items.iter().map(|item| {
            div().child(item.name.as_str())
        })
    )

View Lifecycle

Initialization

impl MyView {
    fn new(cx: &mut ViewContext<Self>) -> Self {
        // Initialize state
        let model = cx.new_model(|_| MyState::default());

        // Set up subscriptions
        let subscription = cx.observe(&model, |_, _, cx| cx.notify());

        // Spawn async tasks
        cx.spawn(|this, mut cx| async move {
            // Async initialization
        }).detach();

        Self {
            model,
            _subscription: subscription,
        }
    }
}

Update Notifications

impl MyView {
    fn update_state(&mut self, new_data: Data, cx: &mut ViewContext<Self>) {
        self.model.update(cx, |state, cx| {
            state.data = new_data;
            cx.notify();  // Trigger rerender
        });
    }
}

Cleanup

impl Drop for MyView {
    fn drop(&mut self) {
        // Manual cleanup if needed
        // Subscriptions are automatically dropped
    }
}

Reactive Patterns

Derived State

impl Render for MyView {
    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
        let state = self.model.read(cx);

        // Compute derived values
        let total = state.items.iter().map(|i| i.value).sum::<i32>();
        let average = total / state.items.len() as i32;

        div()
            .child(format!("Total: {}", total))
            .child(format!("Average: {}", average))
    }
}

Async Updates

impl MyView {
    fn load_data(&mut self, cx: &mut ViewContext<Self>) {
        let model = self.model.clone();

        cx.spawn(|_, mut cx| async move {
            let data = fetch_data().await?;

            cx.update_model(&model, |state, cx| {
                state.data = data;
                cx.notify();
            })?;

            Ok::<_, anyhow::Error>(())
        }).detach();
    }
}

Resources

Official Documentation

Common Patterns Reference

  • Model-View: State management pattern
  • Container-Presenter: Separation of concerns
  • Compound Components: Related components working together
  • Action System: Command pattern for user interactions
  • Subscriptions: Observer pattern for reactive updates

Best Practices

  • Store subscriptions to prevent cleanup
  • Use cx.notify() sparingly
  • Prefer composition over inheritance
  • Keep render methods pure
  • Handle errors gracefully
  • Document component APIs
  • Test component behavior

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

27.56%
按下载量换算56

Gemini CLI

24.03%
按下载量换算49

windsurf

15.96%
按下载量换算32

OpenCode

10.86%
按下载量换算22

Codex

6.78%
按下载量换算14

Antigravity

3.15%
按下载量换算6

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

通过

权限和风险

只读

该 Skill 主要提供规则、说明或参考内容,本身偏只读;真正读写文件、联网或执行命令仍取决于宿主 Agent 的任务。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。

来源信息

继续浏览同类 Skills