This guide covers the shortest path from an issue to a reviewable Argui change. Read architecture first when the change crosses crates.
Start with the type or behavior, then edit the crate that owns it:
| Change | Primary crate | Common follow-up |
|---|---|---|
| Geometry, color, input value type | argui-core |
consumers that translate the type |
| Element, style, focus, event, semantics | argui-ui |
layout or runtime integration |
| Flexbox/Grid calculation | argui-layout |
UI invalidation test |
| Text shaping, bidi, cursor geometry | argui-text |
renderer glyph preparation |
| Display-list primitive | argui-paint |
WGPU implementation in argui-render |
| Window or OS capability | argui-platform |
command/result routing in argui-runtime |
| Model, task, lifecycle, frame scheduling | argui-runtime |
applications using the runtime APIs |
| TSX host contract or transaction | argui-schema / argui-host |
matching package under packages/host |
| Reusable TSX control | packages/widgets |
Solid/React adapter and gallery page |
| TSX application example | apps/gallery/ |
gallery page or package test |
Use rg before adding a type. Geometry, colors, keys, actions, and window
commands already have shared representations; parallel versions create
conversion code and inconsistent behavior.
For an interaction change, trace:
platform event -> runtime normalization -> UI dispatch -> model update
-> reconciliation -> layout/text/paint invalidation -> frame
For a rendering change, keep the description and execution separate:
Element builder -> argui-paint command -> argui-render resource/pipeline
The Solid and React adapters translate TSX into host transactions. Keep
framework-specific code in packages/; add a Rust engine primitive only when
the existing host schema and retained elements cannot express the behavior.
For an OS integration, keep native handles inside the platform adapter and return an ordinary command result to the application.
Render::render describes the current view. Event listeners mutate the owning
model and call cx.notify() when the description may change. Use narrower
requests when the tree stays identical:
request_paint()for paint data produced outside a model rebuild;request_animation_frame()while an animation remains active;scroll()for a retained scroll request;request_focus()for a stable focus target.
Keep durable state in an Entity<T>. Keep presentation-local handlers, tasks,
and caches in its Mount<T> or Context<T>. Do not store Winit or WGPU handles
in application models.
Use stable keys for reordered siblings, virtualized rows, focus targets, and elements observed after layout. A key is identity, not visible copy.
- Put framework-neutral types and behavior in
packages/widgets/src/shared/. - Add the Solid or React view under the corresponding
src/solid/orsrc/react/directory and export it from that package entry point. - Use the shared host primitives and contracts. If an engine primitive or
transaction is genuinely missing, extend
crates/argui-schemaandcrates/argui-hostbefore adding framework-specific workarounds. - Add or update tests in the owning TypeScript package, and cover gallery
behavior in
apps/gallery/tests/when it depends on the native host. - Add a page to
apps/gallery/when the control needs an interactive example. The gallery guide documents the package imports and native development loop.
Do not put network requests, persistence, or application validation inside a generic widget.
Change the lowest owning crate first. Add the next layer only when translation is required. A new paint primitive, for example, normally needs:
- an immutable command and validation in
argui-paint; - collection from an element or custom element in
argui-ui; - batching or pipeline execution in
argui-render; - renderer-neutral tests before a GPU capture test.
State the invalidation cost in the API: whether a value changes layout, shaped text, paint, hit geometry, or only semantics. Reuse retained resources and bound all caches.
Keep default builds small. Add optional functionality behind a feature in the crate that owns it, and let applications depend on that crate directly. Use Cargo target dependencies for OS libraries. Android and iOS entry crates stay explicit dependencies.
Compile the actual affected targets. A successful Linux build does not validate
macOS, Windows, Android, iOS, or WebAssembly code selected by cfg. The
repository's iOS engine cross-check does not build a sample iOS application;
the checked-in packaged gallery currently targets Android.
Keep the feature set stable during the loop:
cargo fmt --all
cargo nextest run -p CRATE --all-features
cargo clippy -p CRATE --all-targets --all-features -- -D warningsFor a UI change, run the smallest relevant gallery scenario and inspect its PNG
on the private display. For performance work, establish a reproducible workload
before changing code and keep raw results under target/performance/ while
investigating. Check in only the results needed to support a maintained guide.
For TSX changes, run bun run check:ts and bun run test:ts. The native
gallery's Solid/React hot reload is documented in the
gallery guide; run its desktop window through
the private-display helper when checking visuals.
When implementation is complete, follow the one-time final gate in code quality.
- Public functions and methods get Rustdoc at their definition.
- The maintained native TSX gallery and its runnable examples live in
apps/gallery/. The documentation site will be rebuilt separately. docs/owns architecture, lifecycle, platform limits, and contributor work.- Raw benchmark data stays machine-readable; the performance guide states only conclusions supported by it.
Update the owning document in the same change as the behavior. Remove outdated text instead of appending a correction below it.