Skip to content

Instantly share code, notes, and snippets.

@Swoorup
Created June 14, 2026 09:10
Show Gist options
  • Select an option

  • Save Swoorup/59e5e279e05ef3630f2211eb0cce1f71 to your computer and use it in GitHub Desktop.

Select an option

Save Swoorup/59e5e279e05ef3630f2211eb0cce1f71 to your computer and use it in GitHub Desktop.
ssssssssss
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