### Run Example Command Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Execute the example using the cargo run command with the --example flag. ```bash cargo run --example basic ``` -------------------------------- ### Run Basic Example Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Command to compile and run the basic usage example for GPUI Hooks. ```bash # 运行基础示例 cargo run --example basic # 构建示例 cargo build --examples ``` -------------------------------- ### Import Order and Grouping Example Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Demonstrates the recommended order and grouping for Rust imports to maintain consistency. ```rust // 标准库 use std::cell::{Ref, RefCell, RefMut}; use std::any::Any; // 外部 crate use gpui::{Context, IntoElement, Window}; use gpui_hooks_macros::hook_element; // 当前 crate use super::Hook; use crate::hooks::{HasHooks, UseStateHook}; ``` -------------------------------- ### Install gpui-hooks and gpui Source: https://context7.com/leset0ng/gpui-hooks/llms.txt Add `gpui-hooks` as a dependency in your `Cargo.toml`. `gpui` is a required peer dependency. ```toml # Cargo.toml [dependencies] gpui-hooks = "0.1" # gpui is a required peer dependency gpui = "0.2.2" ``` -------------------------------- ### Documentation Comment Example Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Illustrates the use of documentation comments (///) for public APIs, including descriptions and potential panics. ```rust /// Provides a brief description of the function. /// /// # Examples /// /// ``` /// // Example usage /// ``` /// /// # Panics /// /// Panics if a certain condition is met. ``` -------------------------------- ### Use Memo Hook Example Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Shows how to use the `use_memo` hook to memoize expensive computations. The computation is re-executed only when its dependencies change. ```rust let memoized = self.use_memo(|| compute_expensive_value(), deps); ``` -------------------------------- ### Use State Hook Example Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Demonstrates the usage of the `use_state` hook to manage and update component state. It returns a getter and a setter function. ```rust let (value, set_value) = self.use_state(|| initial_value); ``` -------------------------------- ### Hook Element Macro Example Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Example of applying the `#[hook_element]` attribute macro to a struct to enable Hook support and automatically implement necessary traits. ```rust #[hook_element] struct MyComponent { // Custom fields } ``` -------------------------------- ### Use Effect Hook Example Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Illustrates how to use the `use_effect` hook for handling side effects. It accepts a closure for the effect and an optional cleanup function, along with dependencies. ```rust self.use_effect(|| { // Side effect logic Some(|| { // Cleanup function (optional) }) }, deps); ``` -------------------------------- ### Error Handling: Hook Order Panic Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Example of a panic message for incorrect hook call order, which is considered a programming error. ```rust panic!( "Hook count changed from {} to {}. Hooks must be called in the same order every render.", prev, current ); ``` -------------------------------- ### Correct Use of State Hook at Top Level Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md This example shows the correct way to use `use_state` at the top level of a component, followed by conditional logic that utilizes the state. ```rust let (value, set_value) = self.use_state(|| 0); if condition { // Use value() } ``` -------------------------------- ### Unsafe Code: Raw Pointer Closure Capture Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Example of using a raw pointer to create a stable closure reference for capturing state within hooks. ```rust // 安全:hook_ptr 在闭包生命周期内有效,因为 hooks 向量在组件生命周期内稳定 let getter: Box T> = { let state_ptr = hook_ptr as *const UseState; Box::new(move || unsafe { (*state_ptr).get().clone() }) }; ``` -------------------------------- ### Incorrect Use of State Hook Inside Conditional Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md This example demonstrates an incorrect usage of `use_state` within a conditional block, violating the rule that hooks must be called at the top level. ```rust if condition { let (value, set_value) = self.use_state(|| 0); // Wrong! } ``` -------------------------------- ### Create Custom UseCounter Hook in Rust Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Defines a custom hook `use_counter` that manages a stateful counter. It returns functions to get the count, set the count, and increment the count. Requires a `UseStateHook` trait implementation. ```rust trait UseCounter { fn use_counter(&self, initial: i32) -> (Box i32>, Box, Box); } impl UseCounter for T { fn use_counter(&self, initial: i32) -> (Box i32>, Box, Box) { let (count, set_count) = self.use_state(|| initial); let increment = { let count = count.clone(); let set_count = set_count.clone(); Box::new(move || set_count(count() + 1)) }; (count, set_count, increment) } } ``` -------------------------------- ### View Documentation in Bash Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Command to generate and open the project's documentation in a web browser. ```bash cargo doc --open ``` -------------------------------- ### Run the GPUI Hooks Application Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Initialize and run the GPUI application, opening a window that hosts the `CounterApp` component. ```rust fn main() { Application::new().run(|cx: &mut App| { let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx); cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), ..Default::default() }, |_, cx| { cx.new(|_| CounterApp::default()) }, ).unwrap(); }); } ``` -------------------------------- ### Build Project in Bash Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Commands to build the project using Cargo. Use `--release` for an optimized build. ```bash cargo build cargo build --release ``` -------------------------------- ### Single Crate Build Commands Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Commands to navigate into a specific crate directory and build it. ```bash cd gpui-hooks && cargo build cd gpui-hooks-macros && cargo build ``` -------------------------------- ### Rustfmt for Code Formatting Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Commands to check code formatting and format all code within the project or a specific crate. ```bash # 检查格式化 cargo fmt --check # 格式化所有代码 cargo fmt # 格式化特定 crate cd gpui-hooks && cargo fmt ``` -------------------------------- ### Basic Build Commands Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Standard Cargo commands for building the workspace members in debug or release modes, or for specific crates. ```bash # 构建所有 workspace 成员(debug 模式) cargo build # 构建所有 workspace 成员(release 模式) cargo build --release # 构建特定 crate cargo build -p gpui-hooks cargo build -p gpui-hooks-macros # 检查编译(不生成二进制) cargo check cargo check --release ``` -------------------------------- ### Workspace Structure Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Illustrates the directory and file layout of the GPUI Hooks project, including the main library and macro crates. ```text gpui-hooks/ ├── Cargo.toml # Workspace 配置 ├── gpui-hooks/ │ ├── Cargo.toml # 主库配置 │ ├── src/ │ │ ├── lib.rs # 主库入口,定义核心 trait │ │ ├── hooks.rs # Hook trait 定义和通用实现 │ │ └── hooks/ # 具体 Hook 实现 │ │ ├── use_state.rs │ │ ├── use_effect.rs │ │ └── use_memo.rs │ └── examples/ │ └── basic.rs # 使用示例 └── gpui-hooks-macros/ ├── Cargo.toml # 宏库配置 └── src/ └── lib.rs # `#[hook_element]` 宏实现 ``` -------------------------------- ### Pre-commit Check Command Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md A combined command to run formatting, linting, and tests before committing. ```bash cargo fmt && cargo clippy -- -D warnings && cargo test ``` -------------------------------- ### Run All Tests Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Commands to execute all tests within the workspace or for specific crates, with an option for verbose output. ```bash # 运行 workspace 中所有测试 cargo test # 运行特定 crate 的测试 cargo test -p gpui-hooks cargo test -p gpui-hooks-macros # 显示详细输出 cargo test --verbose ``` -------------------------------- ### Run Tests in Bash Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Command to execute the project's tests using Cargo. ```bash cargo test ``` -------------------------------- ### Run Specific Tests Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Commands to run tests from a specific file or a single test function. ```bash # 运行特定测试文件中的所有测试 cargo test --test # 运行单个测试函数 cargo test # 注意:当前项目尚未添加测试,以上命令为通用模式 ``` -------------------------------- ### Create a Hook Component with GPUI Hooks Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Define a component using the `#[hook_element]` macro and implement `HookedRender` to manage state, effects, and memoized values within the GPUI framework. ```rust use gpui::{div, prelude::*, px, rgb, size, App, Application, Bounds, Context, Window, WindowBounds, WindowOptions}; use gpui_hooks::{hook_element, HookedRender}; use gpui_hooks::hooks::{UseEffectHook, UseMemoHook, UseStateHook}; #[hook_element] struct CounterApp {} impl HookedRender for CounterApp { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { // useState - manage counter state let (count, set_count) = self.use_state(|| 0i32); // useMemo - compute doubled value let count_val = count(); let doubled = self.use_memo(|| count_val * 2, [count_val]); // useEffect - side effect when count changes self.use_effect(|| { println!("Effect: count changed to {}", count_val); Some(|| println!("Effect cleanup")) }, [count_val]); div() .child(format!("Count: {}", count())) .child(format!("Doubled (useMemo): {}", doubled())) .child(div().child("click me").on_click(cx.listener( move |_this, _, _window, cx| { set_count(count() + 1); cx.notify(); }, ))) } } ``` -------------------------------- ### Clippy for Code Quality Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Commands to run Clippy for code quality checks, including strict warnings and automatic fixes. ```bash # 运行 clippy 检查 cargo clippy # 以更严格的规则运行 cargo clippy -- -D warnings # 修复可自动修复的问题 cargo clippy --fix # 针对特定 crate cargo clippy -p gpui-hooks ``` -------------------------------- ### Add gpui-hooks to Cargo.toml Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Add this dependency to your project's Cargo.toml file to include the gpui-hooks library. ```toml [dependencies] gpui-hooks = "0.1" ``` -------------------------------- ### Check Code Quality in Bash Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Commands to check code quality using `cargo clippy` for linting and `cargo fmt --check` for formatting. ```bash cargo clippy cargo fmt --check ``` -------------------------------- ### Manual `HookedElement` Implementation Source: https://context7.com/leset0ng/gpui-hooks/llms.txt Provides a manual implementation of the `HookedElement` trait for custom base structs when the `#[hook_element]` macro cannot be used. This ensures hook storage and management are correctly wired. ```rust // Manual implementation (only if you cannot use the macro, e.g. for a custom base struct): use std::cell::{Cell, RefCell}; use gpui_hooks::{HookedElement, hooks::Hook}; struct MyManualComponent { _hooks: RefCell>>, _hook_index: Cell, _prev: Cell, } impl HookedElement for MyManualComponent { fn _hooks_ref(&self) -> &RefCell>> { &self._hooks } fn _hook_index(&self) -> usize { self._hook_index.get() } fn _set_hook_index(&self, i: usize) { self._hook_index.set(i) } fn _prev(&self) -> usize { self._prev.get() } fn _set_prev(&self, p: usize) { self._prev.set(p) } } // After this, all UseStateHook / UseEffectHook / UseMemoHook / UseRefHook / UseCallbackHook // blanket impls are automatically satisfied. ``` -------------------------------- ### Test Coverage with Tarpaulin Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Command to generate test coverage reports for the workspace using tarpaulin. ```bash cargo tarpaulin --workspace --ignore-tests ``` -------------------------------- ### Manage Component State with use_state Source: https://context7.com/leset0ng/gpui-hooks/llms.txt The `use_state` hook initializes and tracks component-local state. It returns a `(getter, setter)` pair. The getter reads the current value, and the setter updates it from an event handler. The state type must be `Clone + 'static`. The initializer runs only once; subsequent renders reuse the stored value. ```rust use gpui::{Context, Window, div, prelude::*}; use gpui_hooks::{hook_element, HookedRender}; use gpui_hooks::hooks::UseStateHook; #[hook_element] struct CounterApp {} impl HookedRender for CounterApp { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { // Initializer runs only once; subsequent renders reuse the stored value. let (count, set_count) = self.use_state(|| 0i32); // Multiple independent state slices are allowed. let (label, set_label) = self.use_state(|| String::from("clicks")); div() .child(format!("{}: {}", label(), count())) .child(div().child("+1").on_click(cx.listener(move |_, _, _, cx| { set_count(count() + 1); if count() + 1 >= 10 { set_label(String::from("many clicks")); } cx.notify(); // tell GPUI to re-render }))) } } // Expected console output on each render: "clicks: 0", "clicks: 1", …, "many clicks: 10" ``` -------------------------------- ### Combine Multiple Hooks in MyComponent in Rust Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Demonstrates combining multiple state hooks (`use_state`) and an effect hook (`use_effect`) within a component's render method. The effect hook logs the count whenever it changes. ```rust impl HookedRender for MyComponent { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let (count, set_count) = self.use_state(|| 0); let (name, set_name) = self.use_state(|| String::from("World")); self.use_effect(|| { println!("Count is now: {}", count()); None }, [count()]); // ... rendering logic } } ``` -------------------------------- ### use_state Hook Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Manages component state by providing a getter and a setter function. The initial state is determined by a closure. ```APIDOC ## use_state ### Description Manages component state. ### Signature ```rust let (value, set_value) = self.use_state(|| initial_value); ``` ### Parameters - `initial_value`: A closure that returns the initial state value. ### Returns - A tuple containing `(getter, setter)` functions. ### Type Constraint - `T: Clone + 'static` ``` -------------------------------- ### Use #[hook_element] Macro for GPUI Components Source: https://context7.com/leset0ng/gpui-hooks/llms.txt The `#[hook_element]` macro transforms a struct into a hook-enabled GPUI component. It injects hidden fields, derives `Default`, implements `HookedElement`, and auto-implements `gpui::Render` by delegating to `execute_hooked_render`. Custom fields must implement `Default`. ```rust use gpui::{App, Application, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, size}; use gpui_hooks::{hook_element, HookedRender}; use gpui_hooks::hooks::UseStateHook; // Before expansion: a plain struct with custom fields. // After expansion: _hooks, _hook_index, _prev are injected; // Default, HookedElement and gpui::Render are all implemented automatically. #[hook_element] struct GreetingApp { greeting: String, // custom field — must implement Default } impl HookedRender for GreetingApp { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let (name, set_name) = self.use_state(|| String::from("World")); div() .child(format!("Hello, {}!", name())) .child( div().child("Change name").on_click(cx.listener(move |_, _, _, cx| { set_name(String::from("GPUI")); cx.notify(); })), ) } } fn main() { Application::new().run(|cx: &mut App| { let bounds = Bounds::centered(None, size(px(400.), px(200.)), cx); cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), ..Default::default() }, |_, cx| cx.new(|_| GreetingApp { greeting: String::new(), ..Default::default() }), ).unwrap(); }); } ``` -------------------------------- ### Memoize Computations with `use_memo` Source: https://context7.com/leset0ng/gpui-hooks/llms.txt Use `use_memo` to cache the result of expensive computations. The computation re-runs only when its dependencies change. The computed value must implement `Clone + 'static`. Returns a getter for the memoized value. ```rust use gpui::{Context, Window, div, prelude::*}; use gpui_hooks::{hook_element, HookedRender}; use gpui_hooks::hooks::{UseMemoHook, UseStateHook}; #[hook_element] struct FibApp {} fn fibonacci(n: u64) -> u64 { match n { 0 => 0, 1 => 1, n => fibonacci(n - 1) + fibonacci(n - 2) } } impl HookedRender for FibApp { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let (n, set_n) = self.use_state(|| 10u64); let n_val = n(); // fibonacci(n_val) is only recomputed when n_val changes. let fib = self.use_memo(|| fibonacci(n_val), [n_val]); // A second memo with multiple deps — recomputes when either changes. let (scale, _) = self.use_state(|| 2u64); let scaled_fib = self.use_memo(|| fib() * scale(), [fib(), scale()]); div() .child(format!("fib({})", n_val)) .child(format!(" = {}", fib())) .child(format!("scaled = {}", scaled_fib())) .child(div().child("+1 n").on_click(cx.listener(move |_, _, _, cx| { set_n(n_val + 1); cx.notify(); }))) } } ``` -------------------------------- ### #[hook_element] Macro Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md An attribute macro that simplifies adding Hook support to structs. It automatically implements necessary traits and adds required fields. ```APIDOC ## #[hook_element] ### Description Attribute macro that automatically adds Hook support to structs. ### Usage ```rust #[hook_element] struct MyComponent { // Custom fields } ``` ### Functionality The macro automatically: 1. Adds `_hooks`, `_hook_index`, `_prev` fields. 2. Implements the `Default` trait. 3. Implements the `HookedElement` trait. 4. Implements the `gpui::Render` trait. ``` -------------------------------- ### use_memo Hook Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Memoizes computed values, re-computing only when specified dependencies change. This optimizes performance by avoiding redundant calculations. ```APIDOC ## use_memo ### Description Memoizes computed values. ### Signature ```rust let memoized = self.use_memo(|| compute_expensive_value(), deps); ``` ### Parameters - `deps`: A dependency array. The computation re-runs when these dependencies change. - `compute`: A closure that performs the computation. ### Returns - A getter function that returns the memoized value. ``` -------------------------------- ### Memoize Callbacks with `use_callback` Source: https://context7.com/leset0ng/gpui-hooks/llms.txt Memoizes a factory-produced closure, recreating it only when dependencies change. Useful for optimizing performance by avoiding unnecessary closure re-creation when passing handlers to child elements. ```rust use gpui::{Context, Window, div, prelude::*}; use gpui_hooks::{hook_element, HookedRender}; use gpui_hooks::hooks::{UseCallbackHook, UseStateHook}; #[hook_element] struct CallbackApp {} impl HookedRender for CallbackApp { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let (count, set_count) = self.use_state(|| 0i32); let current = count(); // The factory closure (and thus the returned Fn) is recreated only when `current` changes. let next_value = self.use_callback( || { Box::new(move || { println!("Callback: computing next from {}", current); current + 10 }) as Box i32> }, [current], ); div() .child(format!("Count: {}", current)) .child( div().child("Add 10 via callback").on_click(cx.listener(move |_, _, _, cx| { let new_val = next_value(); // calls the memoized closure set_count(new_val); cx.notify(); })), ) } } // Console: "Callback: computing next from 0" → count becomes 10 // Console: "Callback: computing next from 10" → count becomes 20 ``` -------------------------------- ### Commit Message Format Source: https://github.com/leset0ng/gpui-hooks/blob/master/AGENTS.md Specifies the conventional commit message format, including type, scope, and description. ```text 类型(范围): 简要描述 详细说明(可选) - 列表项(可选) 类型包括:feat, fix, docs, style, refactor, test, chore ``` -------------------------------- ### Run Side Effects with `use_effect` Source: https://context7.com/leset0ng/gpui-hooks/llms.txt Use `use_effect` to run closures that have side effects, like logging or subscriptions. The effect re-runs when dependencies change. An optional cleanup function can be returned to run before the next effect or on component unmount. Requires a manual `Drop` implementation that calls `self.cleanup_effects()`. ```rust use gpui::{Context, Window, div, prelude::*}; use gpui_hooks::{hook_element, HookedRender}; use gpui_hooks::hooks::{UseEffectHook, UseStateHook}; #[hook_element] struct TimerApp {} impl Drop for TimerApp { fn drop(&mut self) { // Required: flushes all pending cleanup closures. self.cleanup_effects(); } } impl HookedRender for TimerApp { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let (tick, set_tick) = self.use_state(|| 0u32); let tick_val = tick(); // Effect runs on mount and whenever `tick_val` changes. self.use_effect( || { println!("[effect] tick is now {}", tick_val); // Cleanup runs before the next effect execution. Some(move || println!("[cleanup] leaving tick {}", tick_val)) }, [tick_val], // dependency array — any PartialEq + Clone + 'static type works ); // Empty dependency array [] → runs only once on mount, never again. self.use_effect(|| { println!("[mount] component mounted"); None:: }, []); div() .child(format!("Tick: {}", tick_val)) .child(div().child("tick").on_click(cx.listener(move |_, _, _, cx| { set_tick(tick_val + 1); cx.notify(); }))) } } ``` -------------------------------- ### Manage Mutable State with `use_ref` Source: https://context7.com/leset0ng/gpui-hooks/llms.txt Use `use_ref` to store mutable values that persist across renders without causing re-renders when mutated. This is similar to React's `useRef` and is useful for storing imperative values like timers or previous state. ```rust use gpui::{Context, Window, div, prelude::*}; use gpui_hooks::{hook_element, HookedRender}; use gpui_hooks::hooks::{UseRefHook, UseStateHook}; #[hook_element] struct DeltaApp {} impl HookedRender for DeltaApp { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let (count, set_count) = self.use_state(|| 0i32); // use_ref persists across renders; mutating it does NOT schedule a re-render. let prev_ref = self.use_ref(|| 0i32); let current = count(); let prev = *prev_ref.borrow(); let delta = current - prev; // Imperatively update the ref after reading. if current != prev { *prev_ref.borrow_mut() = current; } div() .child(format!("Count: {}", current)) .child(format!("Delta from previous: {}", delta)) .child(div().child("+1").on_click(cx.listener(move |_, _, _, cx| { set_count(current + 1); cx.notify(); }))) } } ``` -------------------------------- ### Drop Implementation for Effect Cleanup Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Components utilizing `use_effect` must implement the `Drop` trait to ensure `cleanup_effects()` is called, preventing memory leaks. ```rust impl Drop for MyComponent { fn drop(&mut self) { self.cleanup_effects(); } } ``` -------------------------------- ### use_effect Hook Source: https://github.com/leset0ng/gpui-hooks/blob/master/README.md Executes side effects within a component, with optional cleanup logic. The effect re-runs when its dependencies change. ```APIDOC ## use_effect ### Description Executes side effects. ### Signature ```rust self.use_effect(|| { // Side effect logic Some(|| { // Cleanup function (optional) }) }, deps); ``` ### Parameters - `deps`: A dependency array. The effect re-executes when these dependencies change. - `effect`: A closure representing the side effect. It can optionally return a cleanup function. ### Note Components must call `cleanup_effects()` in their `Drop` implementation. ``` -------------------------------- ### Create Custom Counter Hook Source: https://context7.com/leset0ng/gpui-hooks/llms.txt Defines a reusable counter hook as an extension trait over `UseStateHook`. This custom hook bundles state management and increment/decrement logic, making it composable within any `#[hook_element]` struct. ```rust use gpui_hooks::hooks::UseStateHook; /// A reusable counter hook that bundles state + increment/decrement helpers. trait UseCounter: UseStateHook { fn use_counter(&self, initial: i32) -> (Box i32>, Box, Box, Box) { let (count, set_count) = self.use_state(|| initial); let increment = { let (count, set_count) = (count.clone(), set_count.clone()); Box::new(move || set_count(count() + 1)) as Box }; let decrement = { let (count, set_count) = (count.clone(), set_count.clone()); Box::new(move || set_count(count() - 1)) as Box }; (count, set_count, increment, decrement) } } // Blanket: every HookedElement automatically gets use_counter. impl UseCounter for T {} // Usage inside a component: use gpui::{Context, Window, div, prelude::*}; use gpui_hooks::{hook_element, HookedRender}; #[hook_element] struct ScoreBoard {} impl HookedRender for ScoreBoard { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let (score, _, inc, dec) = self.use_counter(0); div() .child(format!("Score: {}", score())) .child(div().child("+").on_click(cx.listener(move |_, _, _, cx| { inc(); cx.notify(); }))) .child(div().child("-").on_click(cx.listener(move |_, _, _, cx| { dec(); cx.notify(); }))) } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.