Created
June 14, 2026 09:10
-
-
Save Swoorup/59e5e279e05ef3630f2211eb0cce1f71 to your computer and use it in GitHub Desktop.
ssssssssss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| You are working in my Rust trading bot repository. | |
| Goal: | |
| Create a Rust UI library prototype that supports a retained-mode GUI suitable for a trading bot, with declarative function-call component authoring, signal-based reactivity, dirty region tracking, logical layers, custom nodes/widgets, custom GPU passes, and a demo app showing the system working. | |
| This should be a real working vertical slice, not only a design document. | |
| Important: | |
| Do not copy browser DOM architecture. | |
| Do not create a CSS/HTML clone. | |
| Do not use a required `view!` macro. | |
| Do not make `ui.add(...)` the main public API. | |
| Do not use Elm/Iced-style root messages as the primary internal state mechanism. | |
| Do not create one retained node per candle, tick, heatmap cell, or order-book level. | |
| Do not make the UI directly mutate trading-critical state. | |
| Do not require one logical layer to equal one GPU texture. | |
| Do not perform a huge unreviewable rewrite of unrelated trading code. | |
| The library should feel declarative to use, but internally it should be retained. | |
| The desired mental model is: | |
| User-facing API: | |
| Component functions returning declarative view values. | |
| Runtime: | |
| Retained tree with stable identity, layout cache, effect state, | |
| dirty flags, damage regions, logical layers, render caches, | |
| signal dependency tracking, and GPU resource management. | |
| Renderer: | |
| Can redraw dirty regions/layers when possible, | |
| can cache layers into textures when useful, | |
| can fall back to full repaint when needed. | |
| Trading boundary: | |
| UI observes UI-facing signals/resources. | |
| Risk-sensitive trading actions are emitted as typed commands/actions | |
| to the trading engine, not applied directly by widgets. | |
| Start by inspecting the existing repository structure. | |
| If there is already a UI crate, integrate this as an experimental module/crate without breaking existing behavior. | |
| If there is no suitable UI crate, create a new crate in the workspace. | |
| Suggested crate names, unless the repo has better conventions: | |
| - `trader_ui` | |
| - `trader_ui_wgpu` | |
| - `trader_ui_demo` | |
| Or, if keeping it simple for the first vertical slice: | |
| - `crates/ui` | |
| - `crates/ui_wgpu` | |
| - `examples/trading_dashboard.rs` | |
| Use the existing repository naming conventions where possible. | |
| Core requirements: | |
| 1. Public API must be declarative function calls | |
| Components should be ordinary Rust functions returning a view type. | |
| Example target usage: | |
| ```rust | |
| pub fn dashboard(props: DashboardProps) -> impl View<TradingCommand> { | |
| Dock::new("dashboard") | |
| .top(px(44), top_bar(TopBarProps { | |
| store: props.store.clone(), | |
| })) | |
| .left(px(320), watchlist(WatchlistProps { | |
| store: props.store.clone(), | |
| })) | |
| .center(trading_chart(ChartProps { | |
| store: props.store.clone(), | |
| })) | |
| .right( | |
| Column::new("right").children(( | |
| positions_panel(PositionsProps { | |
| store: props.store.clone(), | |
| }), | |
| orders_panel(OrdersProps { | |
| store: props.store.clone(), | |
| }), | |
| )), | |
| ) | |
| .overlay(alert_stack(AlertStackProps { | |
| store: props.store.clone(), | |
| })) | |
| } | |
| This should not require: | |
| view! { ... } | |
| And the main authoring style should not be: | |
| ui.add(...) | |
| A low-level imperative API may exist internally or for debug/testing, but the main library API should be declarative. | |
| Signals should be the primary UI state primitive | |
| Use these public concepts: | |
| Signal<T> // read-only reactive value | |
| State<T> // writable owner of reactive state | |
| Binding<T> // read/write binding for controls | |
| Memo<T> // optional public name; derived signals may simply be Signal<T> | |
| Do not expose ReadSignal<T> and RwSignal<T> as the primary public vocabulary unless strongly justified. | |
| Desired public API shape: | |
| let expanded = cx.state("expanded", || true); | |
| Panel::new("risk") | |
| .title("Risk") | |
| .children(( | |
| DisclosureHeader::new("risk-header") | |
| .expanded(expanded.signal()) | |
| .on_press(expanded.toggle()), | |
| Show::when(expanded.signal()).then(|| { | |
| risk_meter(RiskMeterProps { | |
| account: props.account.clone(), | |
| }) | |
| }), | |
| )); | |
| Signal<T> should support: | |
| get() | |
| with(...) | |
| get_untracked() | |
| map(...) | |
| State<T> should support: | |
| signal() | |
| get() | |
| set(...) | |
| update(...) | |
| binding() | |
| set_to(...) | |
| toggle() // for bool-like state | |
| Binding<T> should support controls like: | |
| TextInput::new("filter") | |
| .placeholder("Filter orders") | |
| .bind(order_filter.binding()); | |
| Signals must automatically track dependencies when read inside tracked contexts: | |
| property bindings | |
| memos | |
| effects | |
| component scopes, where appropriate | |
| structural regions such as Show, For, Switch, VirtualFor | |
| Signals should not track when read inside event handlers unless explicitly requested. | |
| Event handlers should run untracked by default. | |
| Provide batch(...) support so many signal writes can flush once. | |
| Runtime must be retained mode internally | |
| The declarative view tree should be temporary. | |
| The runtime tree should be retained. | |
| Implement or scaffold: | |
| Runtime | |
| RuntimeNode | |
| NodeId | |
| Key | |
| KeyPath | |
| NodeArena | |
| LayoutStore | |
| PaintStore | |
| EffectStore | |
| LayerGraph | |
| DamageTracker | |
| RenderList | |
| GpuResourceCache | |
| Internal nodes should have stable identity based on: | |
| parent key path + explicit key + node/component type | |
| Use generation-safe IDs internally where practical: | |
| pub struct NodeId { | |
| index: u32, | |
| generation: u32, | |
| } | |
| Each retained node should track at least: | |
| id | |
| key | |
| parent | |
| children | |
| node kind/type | |
| layout rect | |
| paint bounds | |
| previous paint bounds | |
| dirty flags | |
| layer id | |
| z order | |
| effect state | |
| cached render data or render item references | |
| Use dirty flags similar to: | |
| STATE | |
| LAYOUT | |
| PAINT | |
| INPUT | |
| EFFECT | |
| CHILDREN | |
| LAYER | |
| Dirty region and damage tracking | |
| Implement conservative dirty region tracking. | |
| The system should track: | |
| layout bounds | |
| paint bounds | |
| previous paint bounds | |
| effect-expanded bounds | |
| damage regions | |
| full repaint fallback | |
| Rules: | |
| If a property changes, mark paint dirty where appropriate. | |
| If layout changes, damage old and new bounds. | |
| If an effect moves/fades/glows, damage previous and current paint bounds. | |
| If damage is too complex, repaint the full layer/window. | |
| Never repaint less than required for correctness. | |
| Repainting too much is acceptable. | |
| Repainting too little is a correctness bug. | |
| Provide a policy type like: | |
| pub struct DamagePolicy { | |
| pub max_rects: usize, | |
| pub max_area_fraction: f32, | |
| } | |
| Possible damage modes: | |
| DamageMode::Full | |
| DamageMode::Viewport | |
| DamageMode::Precise | |
| DamageMode::Auto | |
| Include a debug overlay in the demo that visualizes damage rectangles. | |
| Logical layers | |
| Implement public logical layers. | |
| Important: | |
| A logical layer does not have to map to exactly one texture. | |
| A layer is a composition/cache/damage boundary. | |
| A logical layer may be rendered as: | |
| no texture, direct draw | |
| one cached texture | |
| multiple textures | |
| temporary effect textures | |
| tiled textures | |
| custom renderer-owned resources | |
| Expose something like: | |
| Layer::new("main-dashboard") | |
| .cache(CachePolicy::Auto) | |
| .damage(DamageMode::Auto) | |
| .child(...) | |
| OverlayLayer::new("alerts") | |
| .z_index(1000) | |
| .cache(CachePolicy::DuringAnimation) | |
| .damage(DamageMode::Precise) | |
| .child(...) | |
| Cache policies may include: | |
| CachePolicy::None | |
| CachePolicy::Auto | |
| CachePolicy::Always | |
| CachePolicy::WhileClean | |
| CachePolicy::DuringAnimation | |
| CachePolicy::SplitStaticAndDynamic | |
| The renderer should be allowed to choose how to realize a layer. | |
| Effects and animation | |
| Implement an effects model suitable for trading UI. | |
| Support at least a few working effects: | |
| price flash on value change | |
| numeric value transition | |
| fade | |
| slide/fade transition | |
| pulse/glow warning | |
| stale dimming | |
| Effects should be declarative in component usage: | |
| PriceText::new("last") | |
| .value(last_price) | |
| .precision(2) | |
| .flash_on_change(Flash::price().duration(ms(140))); | |
| Badge::new("connection") | |
| .text(connection_label) | |
| .tone(connection_tone) | |
| .pulse_when(is_connecting) | |
| .dimmed(is_stale); | |
| Internally: | |
| effects should be retained | |
| effects should tick independently of rebuilding the whole view | |
| effects should mark paint/effect dirty | |
| effects should expand paint bounds when needed | |
| effects should request animation frames while active | |
| Animation frame values should generally live in the effect system, not as ordinary public signals that update every frame. | |
| Custom nodes/widgets | |
| Support custom widgets/nodes. | |
| There should be a trait or equivalent API for custom retained widgets. | |
| It should allow: | |
| typed props | |
| retained local state | |
| update hook | |
| layout hook | |
| input hook, if needed | |
| paint hook | |
| paint bounds hook | |
| effect hook or access to effect context | |
| debug metadata, if easy | |
| Possible shape: | |
| pub trait Widget { | |
| type Props: Clone + PartialEq + 'static; | |
| type State: Default + 'static; | |
| type Command: 'static; | |
| fn update( | |
| state: &mut Self::State, | |
| old: Option<&Self::Props>, | |
| new: &Self::Props, | |
| cx: &mut UpdateCx<Self::Command>, | |
| ); | |
| fn layout( | |
| state: &mut Self::State, | |
| props: &Self::Props, | |
| constraints: Constraints, | |
| cx: &mut LayoutCx, | |
| ) -> Size; | |
| fn paint_bounds( | |
| state: &Self::State, | |
| props: &Self::Props, | |
| rect: Rect, | |
| cx: &PaintBoundsCx, | |
| ) -> Rect { | |
| rect | |
| } | |
| fn paint( | |
| state: &Self::State, | |
| props: &Self::Props, | |
| rect: Rect, | |
| cx: &mut PaintCx, | |
| ); | |
| } | |
| Adjust this shape if the implementation suggests a better one. | |
| Users should be able to author custom widgets and use them declaratively: | |
| RiskMeter::new("risk-meter") | |
| .used_margin(account.used_margin) | |
| .max_margin(account.max_margin) | |
| .status(account.risk_status) | |
| or: | |
| custom_widget::<RiskMeter>("risk-meter", RiskMeterProps { ... }) | |
| Custom GPU passes / GPU views | |
| Support a custom GPU view or custom GPU pass API for dense visualizations. | |
| This is required for: | |
| charts | |
| order book heatmaps | |
| DOM ladders | |
| dense market visualizations | |
| large animated overlays | |
| Do not model dense chart data as normal retained child nodes. | |
| Provide an escape hatch similar to: | |
| pub trait GpuView { | |
| type Props: Clone + PartialEq + 'static; | |
| type State: Default + 'static; | |
| fn update( | |
| state: &mut Self::State, | |
| old: Option<&Self::Props>, | |
| new: &Self::Props, | |
| cx: &mut UpdateCx, | |
| ); | |
| fn layout( | |
| state: &mut Self::State, | |
| props: &Self::Props, | |
| constraints: Constraints, | |
| cx: &mut LayoutCx, | |
| ) -> Size; | |
| fn prepare_gpu( | |
| state: &mut Self::State, | |
| props: &Self::Props, | |
| cx: &mut PrepareGpuCx, | |
| ); | |
| fn render_gpu( | |
| state: &Self::State, | |
| props: &Self::Props, | |
| rect: Rect, | |
| cx: &mut GpuRenderCx, | |
| ); | |
| } | |
| The demo should include at least one custom GPU view, even if simple. | |
| For example: | |
| a toy candlestick chart | |
| a sparkline rendered through the GPU path | |
| a heatmap grid | |
| a simple animated order-book depth visualization | |
| The GPU view should demonstrate: | |
| updating GPU buffers/resources only when data version changes | |
| using layout rect from the UI runtime | |
| integrating with layers/damage | |
| not creating one retained node per data point | |
| Layout primitives | |
| Implement enough layout primitives for a useful demo. | |
| Suggested primitives: | |
| Row | |
| Column | |
| Stack | |
| Dock | |
| Panel | |
| Splitter, optional | |
| ScrollArea | |
| VirtualList | |
| VirtualTable | |
| Layer | |
| OverlayLayer | |
| Show | |
| For | |
| VirtualFor | |
| The first vertical slice does not need a perfect layout engine, but it should be structured so layout can evolve. | |
| The demo should use: | |
| top bar | |
| left watchlist panel | |
| center chart panel | |
| right orders/positions panel | |
| overlay alert/toast layer | |
| Virtualization | |
| Implement or scaffold virtualization. | |
| At minimum, implement a working VirtualList or VirtualTable that only materializes visible rows. | |
| For the demo: | |
| watchlist may have 100+ symbols | |
| orders/logs may have 1,000+ rows | |
| only visible rows should be built/rendered | |
| rows should have stable keys | |
| row updates should preserve state/effects | |
| Example desired usage: | |
| VirtualTable::new("watchlist-table") | |
| .items(store.quotes.keys()) | |
| .row_height(px(28)) | |
| .row_key(|symbol| *symbol) | |
| .columns(...) | |
| .row(move |symbol| { | |
| quote_row(QuoteRowProps { | |
| symbol, | |
| quote: store.quotes.signal(symbol), | |
| selected: selected_symbol.map(move |s| s == Some(symbol)), | |
| }) | |
| }) | |
| Trading UI store | |
| Create a UI-facing store for the demo. | |
| Use signals and keyed collections. | |
| Possible types: | |
| pub struct TradingUiStore { | |
| pub selected_symbol: State<Option<Symbol>>, | |
| pub chart_range: State<TimeRange>, | |
| pub order_filter: State<String>, | |
| pub connection: State<ConnectionSnapshot>, | |
| pub account: State<AccountSnapshot>, | |
| pub strategy: State<StrategySnapshot>, | |
| pub quotes: SignalMap<Symbol, QuoteRow>, | |
| pub orders: SignalMap<OrderId, OrderRow>, | |
| pub positions: SignalMap<PositionId, PositionRow>, | |
| pub alerts: SignalMap<AlertId, AlertRow>, | |
| } | |
| The demo can use simulated market data. | |
| The UI store should be fed in batches: | |
| runtime.batch(|| { | |
| store.apply_demo_tick(...); | |
| }); | |
| Do not require real broker/trading engine integration. | |
| Commands/actions boundary | |
| Do not use root messages for every minor UI state change. | |
| Use: | |
| Action | |
| Command<T> | |
| or equivalent. | |
| Local UI state should be changed through signals/state/actions: | |
| DisclosureHeader::new("risk-header") | |
| .on_press(expanded.toggle()); | |
| Risk-sensitive actions should emit typed commands: | |
| Button::new("cancel-order") | |
| .text("Cancel") | |
| .tone(Tone::Danger) | |
| .enabled(can_cancel) | |
| .on_press(Command::new(move || { | |
| TradingCommand::CancelOrder(order_id) | |
| })); | |
| Demo command enum: | |
| pub enum TradingCommand { | |
| SelectSymbol(Symbol), | |
| CancelOrder(OrderId), | |
| ClosePosition(PositionId), | |
| StartStrategy(StrategyId), | |
| StopStrategy(StrategyId), | |
| AcknowledgeAlert(AlertId), | |
| ChangeChartRange(Symbol, TimeRange), | |
| } | |
| The demo may log commands rather than executing real trades. | |
| Renderer | |
| Implement a minimal renderer sufficient for the demo. | |
| Prefer wgpu + winit if the repository already uses them or if no renderer exists. | |
| If adding these dependencies is too much for the current repo, explain why and provide a simpler renderer abstraction plus a minimal backend. | |
| Renderer should support, at least: | |
| colored rectangles | |
| text, if practical | |
| clipping/scissor | |
| z-order | |
| opacity | |
| layers | |
| dirty region visualization | |
| custom GPU view hook | |
| Text rendering can use an existing crate if appropriate. | |
| If text rendering is too much for the first slice, implement a simple placeholder and document the limitation, but the demo should still visually communicate layout and updates. | |
| Component authoring and playground friendliness | |
| Structure components so they are easy to preview and inspect. | |
| Prefer typed props structs: | |
| #[derive(Clone, PartialEq)] | |
| pub struct PriceTextProps { | |
| pub value: Signal<f64>, | |
| pub precision: u8, | |
| pub stale: Signal<bool>, | |
| } | |
| Provide a simple component registry or preview harness if feasible. | |
| At minimum, create a demo mode that can preview: | |
| PriceText | |
| Badge | |
| Button | |
| Toast | |
| RiskMeter | |
| Watchlist | |
| TradingChart | |
| If possible, add a simple playground/example app with: | |
| component selector | |
| editable demo signals | |
| damage overlay toggle | |
| layer/cache overlay toggle | |
| effect timeline/debug text | |
| Do not overbuild the playground if it prevents the core vertical slice from compiling. | |
| Demo app requirements | |
| Create a demo trading dashboard. | |
| The demo should include: | |
| A. Top bar | |
| app title | |
| connection badge | |
| strategy start/stop buttons | |
| connection status transitions | |
| stale/disconnected visual state | |
| B. Watchlist | |
| 100+ simulated symbols | |
| prices updating periodically | |
| price flash on change | |
| stale dimming | |
| selected symbol state | |
| virtualized rows if enough symbols | |
| C. Chart | |
| custom GPU view | |
| simple candlestick/sparkline/heatmap visualization | |
| selected symbol drives chart | |
| chart does not create one node per candle | |
| chart data updates via version/resource handle | |
| D. Orders panel | |
| simulated orders | |
| status badges | |
| fill progress bars | |
| animated status/fill changes | |
| cancel button emits TradingCommand::CancelOrder | |
| E. Positions panel | |
| simulated positions | |
| PnL display | |
| numeric transitions | |
| tone by sign | |
| F. Alerts overlay | |
| toast notifications | |
| slide/fade animation | |
| warning pulse for critical alerts | |
| acknowledge/dismiss action | |
| G. Debug overlays | |
| dirty region overlay | |
| layer bounds overlay | |
| FPS/frame timing if feasible | |
| signal/update statistics if feasible | |
| Tests | |
| Add meaningful tests. | |
| Required tests: | |
| signal read/write | |
| automatic dependency tracking | |
| dynamic dependency cleanup | |
| batched updates flush once | |
| derived signal/memo only notifies when derived value changes | |
| State<T> can produce Signal<T> and Binding<T> | |
| event handlers do not track dependencies by default | |
| retained node identity is stable across view rebuilds | |
| stale node IDs are rejected if using generational IDs | |
| dirty flags propagate correctly | |
| damage regions include old and new bounds | |
| damage policy falls back to full repaint when threshold exceeded | |
| effect animation requests frames while active | |
| virtual list computes visible range correctly | |
| keyed list preserves identity across reorder/insert/delete | |
| Add renderer tests where practical, but do not make GPU tests fragile in CI unless the repo already supports them. | |
| Documentation | |
| Add documentation explaining the architecture and usage. | |
| Create or update: | |
| README section for the UI library | |
| docs/ui-library-architecture.md | |
| example source comments | |
| The docs should explain: | |
| public component model | |
| why there is no required view! macro | |
| Signal<T> vs State<T> vs Binding<T> | |
| automatic dependency tracking | |
| batching | |
| retained runtime tree | |
| dirty flags and damage regions | |
| logical layers and why a layer does not necessarily equal one texture | |
| custom widget API | |
| custom GPU view/pass API | |
| trading command boundary | |
| demo app structure | |
| known limitations | |
| Implementation phases | |
| Work incrementally. | |
| Phase 1: | |
| Create crate structure and core types. | |
| View trait/value | |
| Key | |
| basic nodes | |
| Runtime skeleton | |
| Signal/State/Binding | |
| simple tests | |
| Phase 2: | |
| Implement retained reconciliation. | |
| declarative component functions produce view descriptions | |
| retained node arena | |
| stable identity | |
| keyed children | |
| basic dirty flags | |
| tests for identity and dirty behavior | |
| Phase 3: | |
| Implement layout and basic widgets. | |
| Row | |
| Column | |
| Dock | |
| Panel | |
| Text placeholder or real text | |
| Button | |
| Badge | |
| Show | |
| basic input/action handling | |
| Phase 4: | |
| Implement signal property bindings. | |
| widget properties can be static or signal-backed | |
| signal changes update only dependent node/property | |
| tests for property reactivity | |
| Phase 5: | |
| Implement effects. | |
| flash on change | |
| fade | |
| pulse | |
| numeric transition | |
| effect ticking independent of full view rebuild | |
| damage expansion for effects | |
| Phase 6: | |
| Implement layers and damage tracking. | |
| logical layers | |
| damage region accumulator | |
| layer dirty flags | |
| debug damage overlay | |
| full repaint fallback | |
| Phase 7: | |
| Implement virtualization. | |
| VirtualList or VirtualTable | |
| keyed rows | |
| visible range | |
| row identity preservation | |
| Phase 8: | |
| Implement renderer backend. | |
| winit/wgpu if appropriate | |
| render commands | |
| clipping/scissor where possible | |
| layer bounds/debug overlays | |
| custom GPU view hook | |
| Phase 9: | |
| Implement custom widget and custom GPU APIs. | |
| Widget trait | |
| GpuView trait | |
| example RiskMeter widget | |
| example chart/heatmap GPU view | |
| Phase 10: | |
| Build the trading dashboard demo. | |
| simulated store | |
| batched market updates | |
| watchlist | |
| chart | |
| orders | |
| positions | |
| alerts | |
| debug overlays | |
| Phase 11: | |
| Polish and validate. | |
| cargo fmt | |
| cargo check | |
| cargo test | |
| clippy if configured | |
| run demo | |
| update docs | |
| summarize limitations | |
| If the repo already has some of these pieces, adapt rather than duplicate. | |
| Acceptance criteria | |
| The task is complete when: | |
| the project builds | |
| tests pass | |
| there is a working demo app/example | |
| the demo visually shows retained UI behavior | |
| signals update dependent UI without root message plumbing | |
| local UI state uses State<T> | |
| read-only props use Signal<T> | |
| editable controls can use Binding<T> | |
| dirty regions are tracked and can be visualized | |
| effects animate and mark damage | |
| layers exist as logical cache/damage/composition boundaries | |
| custom node/widget API exists and has at least one example | |
| custom GPU view/pass API exists and has at least one example | |
| docs explain how to use and extend the system | |
| Desired API examples to support | |
| Try to make code close to these examples work. | |
| Dashboard: | |
| pub fn dashboard(props: DashboardProps) -> impl View<TradingCommand> { | |
| Dock::new("dashboard") | |
| .theme(TradingTheme::dark()) | |
| .top(px(44), top_bar(TopBarProps { | |
| store: props.store.clone(), | |
| })) | |
| .left(px(320), watchlist(WatchlistProps { | |
| store: props.store.clone(), | |
| })) | |
| .center(trading_chart(ChartProps { | |
| store: props.store.clone(), | |
| })) | |
| .right( | |
| Column::new("right").children(( | |
| positions_panel(PositionsProps { | |
| store: props.store.clone(), | |
| }), | |
| orders_panel(OrdersProps { | |
| store: props.store.clone(), | |
| }), | |
| )), | |
| ) | |
| .overlay(alert_stack(AlertStackProps { | |
| store: props.store.clone(), | |
| })) | |
| } | |
| Watchlist row: | |
| pub fn quote_row(props: QuoteRowProps) -> impl View<TradingCommand> { | |
| let quote = props.quote; | |
| let last = quote.map(|q| q.last); | |
| let bid = quote.map(|q| q.bid); | |
| let ask = quote.map(|q| q.ask); | |
| let stale = quote.map(|q| q.freshness.is_stale()); | |
| TableRow::new(props.symbol) | |
| .selected(props.selected) | |
| .on_press(props.on_select) | |
| .children(( | |
| Text::new("symbol") | |
| .text(props.symbol.to_string()) | |
| .font(FontToken::Mono), | |
| PriceText::new("last") | |
| .value(last) | |
| .precision(2) | |
| .dimmed(stale) | |
| .flash_on_change(Flash::price().duration(ms(140))), | |
| PriceText::new("bid") | |
| .value(bid) | |
| .precision(2) | |
| .flash_on_change(Flash::subtle().duration(ms(100))), | |
| PriceText::new("ask") | |
| .value(ask) | |
| .precision(2) | |
| .flash_on_change(Flash::subtle().duration(ms(100))), | |
| )) | |
| } | |
| Orders: | |
| pub fn order_row(props: OrderRowProps) -> impl View<TradingCommand> { | |
| let order = props.order; | |
| let order_id = props.order_id; | |
| TableRow::new(order_id) | |
| .transition(RowTransition { | |
| enter: Transition::slide_fade(ms(140)), | |
| update: Transition::highlight(ms(180)), | |
| exit: Transition::fade(ms(120)), | |
| }) | |
| .children(( | |
| Text::new("symbol") | |
| .text(order.map(|o| o.symbol.to_string())) | |
| .font(FontToken::Mono), | |
| StatusBadge::new("status") | |
| .text(order.map(|o| o.status.label())) | |
| .tone(order.map(|o| o.status.tone())) | |
| .flash_on_change(Flash::status().duration(ms(160))), | |
| ProgressBar::new("fill") | |
| .value(order.map(|o| o.fill_ratio)) | |
| .animate_value(ms(220), Easing::OutCubic), | |
| Button::new("cancel") | |
| .text("Cancel") | |
| .tone(Tone::Danger) | |
| .enabled(order.map(|o| o.can_cancel)) | |
| .on_press(Command::new(move || { | |
| TradingCommand::CancelOrder(order_id) | |
| })), | |
| )) | |
| } | |
| Layer example: | |
| Layer::new("main-dashboard") | |
| .cache(CachePolicy::Auto) | |
| .damage(DamageMode::Auto) | |
| .child(...); | |
| OverlayLayer::new("alerts") | |
| .z_index(1000) | |
| .cache(CachePolicy::DuringAnimation) | |
| .damage(DamageMode::Precise) | |
| .child(alert_stack(...)); | |
| Custom GPU view example: | |
| TradingChart::new("chart") | |
| .symbol(store.selected_symbol.signal()) | |
| .candles(store.chart.candles_resource()) | |
| .data_version(store.chart.version.signal()) | |
| .range(store.chart_range.signal()) | |
| .cache(ChartCachePolicy::split_static_dynamic()); | |
| Performance and safety expectations | |
| Optimize for: | |
| correctness first | |
| trading safety | |
| clean Rust ownership | |
| predictable reactivity | |
| minimal unnecessary rebuilds | |
| dirty region/layer redraw where useful | |
| graceful full repaint fallback | |
| avoiding hidden lock contention | |
| no UI work on trading-critical paths | |
| Normal UI signals should be UI-thread local unless there is a strong reason otherwise. | |
| External trading data should enter through snapshots, channels, resources, or batched UI-store updates. | |
| Avoid Arc<Mutex<T>> in hot render/layout paths unless justified. | |
| Final response expected from you | |
| When finished, provide: | |
| Summary of what was created | |
| Crates/files added or modified | |
| How to run the demo | |
| How the signal system works | |
| How retained identity works | |
| How dirty region tracking works | |
| How layers map to rendering resources | |
| How to author a custom widget | |
| How to author a custom GPU view/pass | |
| Current limitations | |
| Suggested next implementation steps | |
| Before making large architectural compromises, document the tradeoff in the code or docs. | |
| Begin by inspecting the repository, then implement the smallest working retained-mode signal-driven UI library and demo that satisfies the vertical slice. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment