zsui 0.3.0-alpha.2

Rust-first feature-gated native UI framework for Win32, AppKit, and Linux.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# ZSUI Framework Goals

This is the long-range target for the standalone ZSUI framework. The
machine-readable version lives in `src/framework_goals.rs` and is exposed by
`zsui_rust_first_goals()`.

ZSUI should feel like Rust, not like a C++ or C# control hierarchy ported into
Rust. The public API should be safe, typed, explicit and easy to trim at build
time. Native platform details belong behind host traits and backend modules.

The revised target is: build a standalone Rust-first native UI framework, keep
product behavior outside the crate, and
make the ordinary app path as small as:

```rust,no_run
zsui::native_window("Example").size(900, 620).run()?;
# Ok::<(), zsui::ZsuiError>(())
```

That one-line entry is the desktop target for Windows, macOS and Linux. Android
remains a first-class platform target, but it needs a real Activity runtime
host and device smoke proof before this same public
shape can create mobile native surfaces.

## Highest Priority: Fully Unified Application Authoring

Windows, macOS and Linux applications must use one ordinary Rust authoring
model. The same source owns `State`, `Msg`, `view`, `update`, semantic component
declarations, theme overrides and the `native_window(...)` entry. Application
view code must not select a platform, renderer or host and must not contain
target `cfg`, `PlatformStyle` matches, raw handles, Objective-C/GTK objects or
duplicated per-platform view trees. Platform packaging metadata is generated or
configured outside the application view source.

"Unified" applies to the public authoring model, not to the backend
implementation or to visual output. ZSUI must preserve each platform's own
composition and interaction conventions:

- `PlatformExperience` resolves semantic navigation, toolbar/header, tabs,
  forms, dialogs, popups, metrics, typography and icon sources inside the
  framework. It may produce different component trees for the same semantic
  declaration. `src/platform/identity.rs` owns the canonical target/toolkit
  types, and `NativeUiPlatform::current_target` selects the build target;
  backend inventory and launch metadata are derived from the experience
  registration rather than maintained as parallel platform tables.
- Low-level render and proof APIs share one `ZsPlatformStyle` profile selected
  by `PlatformExperience`. Existing component-specific style names remain
  compatibility aliases, not separate selectors or application-level choices.
  `PlatformExperience::shared_component_style` is the only target-experience
  to component-profile mapping; component modules consume the resolved shared
  profile and never select a target experience themselves.
  `src/platform/component_profile/` defines the shared component-profile
  contracts and resolver while keeping each desktop platform's defaults in a
  separately maintainable module. The profiles own semantic sections,
  adaptive navigation, foundational controls and navigation rows, command bars,
  tabs, content-dialog action composition, InfoBar/TeachingTip/Toast feedback,
  BreadcrumbBar, ToggleButton, NumberBox, AutoSuggestBox, GridView, TreeView,
  DataGrid, TimePicker, ColorPicker, CommandPalette and the legacy
  navigation/card shell while remaining independent from native backend
  selection. Shared component rendering consumes those resolved profiles
  without direct desktop-platform variant branches. Shared composite surfaces
  consume the same tokens instead of Windows-specific
  compatibility constants. Feature-gated compatibility layouts for Document
  Shell and Calculator Shell resolve through dedicated desktop profiles rather
  than embedding one platform's dimensions. The same semantic declaration
  resolves to Fluent, AppKit or GTK composition without an application-level
  platform branch.
- A compile-time backend profile owns `Host`, `Text`, `Raster`, `Presenter` and
  `Services`. Windows, AppKit and Linux implementations remain independent and
  can use different event loops, text stacks, rasterizers and system services.
- Every text backend must expose one canonical layout result used by measure,
  paint, hit testing, caret/selection geometry and accessibility. A demo-local
  font or clipping correction is not an acceptable fix.
- Applications may customize semantic specs and public theme/token values once;
  ZSUI resolves those values against the current platform without requiring an
  application branch.
- Cargo features remain the build boundary. A unified API must not force every
  widget, renderer or platform integration into the final binary.

The acceptance gate is one unchanged Gallery, Notepad or utility application
source compiled for all three desktop targets. Each target must launch its real
host, execute the same semantic scenario and emit final-surface screenshots and
structured evidence. Example-only platform adjustments do not satisfy this
goal; reusable corrections belong in framework contracts and backends first.

## v0.2 Authoring Addition: Reloadable UI Documents

The existing Rust `State`/`Msg`/`view`/`update` API remains the ordinary,
fully typed application surface. v0.2 additionally requires a versioned
platform-neutral `UiDocument` for semantic component trees and an explicit
typed binding manifest that connects document action/property slots to Rust
state and messages without reflection or a global string event bus.

A prebuilt native Viewer must validate and reload compatible document changes
without invoking Cargo. Stable IDs preserve focus, text selection, scrolling
and compatible component state. The same validated document must enter Win32,
AppKit and Linux hosts, produce deterministic structured data for AI editing,
and support release-time compilation or embedding without shipping file
watchers, preview transport or another mandatory process.

This is an additional v0.2 gate, not a replacement for any unfinished native
runtime, service, IME, accessibility, interaction, feature-trimming or target-
proof requirement. A browser/WASM projection may later provide fast simulated
platform comparison, but it is not target-native proof. A complete drag-and-
drop designer, collaboration system and arbitrary Rust-logic visual editor are
outside the v0.2 completion definition.

## v0.3 Core Milestone: Native Proof CI

ZSUI 0.3.0 prioritizes repeatable native runtime evidence over adding more
component declarations. Every UI-affecting change must eventually launch the
real Win32, AppKit and Linux application paths on target runners, execute fixed
interaction scenarios, capture the final platform view, emit structured
layout/focus/event evidence and compare it against reviewed baselines.

The original first blocking target—AppKit on the fixed GitHub-hosted
`macos-15` ARM64 runner—is now operational. Shared `DrawPlan` images are not
target evidence; the AppKit gate captures the final `NSView`. The current
blocking target is one unchanged application-authoring path with aligned
Win32, AppKit and Linux evidence and reviewed regression baselines. Baselines
are read-only in CI and can change only through an explicit reviewed commit.
The complete milestone, phased delivery order and release gates are defined in
[`v0.3-native-proof-ci.md`](v0.3-native-proof-ci.md).

Text-capable controls follow the staged input-experience plan in
[`v0.3-input-experience.md`](v0.3-input-experience.md). Its first stage closes
placeholder parity across the Rust View, UiDocument, rendering, IME and
accessibility paths; later stages add field/error composition and shared native
proof without moving validation policy into the framework.

## Delivery Order

ZSUI is delivered through runnable vertical slices rather than contract count.
The current priority order is:

1. Make fully unified application authoring the enforced desktop boundary:
   remove remaining application platform branches and renderer selection, move
   platform composition into framework-owned experience contracts, and prove
   one unchanged source on Win32, AppKit and Linux.
2. Harden the operational AppKit Native Proof CI gate on `macos-15`: retain
   real `NSApplication`/`NSWindow`/`NSView` execution and deterministic Gallery
   and Notepad scenarios, then add reviewed visual baselines.
3. Move the existing Win32 proof and the Linux target runtime onto the same
   versioned proof schema and regression tool so all three desktop paths block
   regressions before the 0.3.0 release.
4. Stabilize the Rust-first application loop so typed messages update explicit
   state and repaint the live window, then cover IME, accessibility, focus,
   menus and dialogs required by native utility applications.
5. Tighten Cargo feature boundaries and split crates only after the public
   widget/runtime boundaries are proven by real applications.
6. Replace the Android Activity scaffold with a real FFI host and device smoke
   artifacts.

Protocol manifests, AI handoff metadata and mobile bridge contracts support
these slices, but they do not advance product readiness by themselves. New
contract-only work should be deferred when a runnable slice still has an
unclosed input, state, paint or target-verification gap.

阶段更新和目标证据使用低并发构建:本地完整测试、特性矩阵和目标 smoke 默认以
`CARGO_BUILD_JOBS=1`(或 `cargo -j1`)运行,避免并行链接造成不必要的内存峰值。
每个阶段记录 focused test、全特性结果、目标截图/报告和运行时内存采样,再推进
下一阶段;单纯的 contract 或 demo 编译不视为阶段完成。

## 桌面可靠性与应用可用性

执行顺序、问题状态及验收门槛见
[桌面框架完善计划](desktop-hardening-plan.md)。桌面应用基础完善阶段按 A—G 交付:
可靠输入与效果调度、稳定入口和视觉布局、共享交互、虚拟数据控件、富文本与文档标签、
开发工具,以及三平台验收。已有单行横滚与标签收缩进入验收,效果队列预算后续调度优先收口。
原生子视图属于独立可选能力。

每项分别记录实现、目标交互和人工体验状态。组件覆盖率、没有 TODO 或通过编译,
不能替代真实应用场景验收;既有 v0.2 发布门槛与 0.3 Native Proof CI 目标保持独立。

Buffered no-flicker self-draw is the Windows rendering baseline:
avoid background erase, paint into an owned buffer when possible, then present
once to the target surface. Wider platform APIs such as `windows-rs` should be
introduced only when a concrete backend surface needs them; core declarations
must stay independent of raw platform handles and broad native dependencies.

## Component Coverage

`zsui_component_catalog()` is the component-level source of truth. The current
catalog covers 47 desktop component families, all with first-pass runtime
surfaces and none contract-only or not started. Composite shells do not change
those statuses.
Embedded browser controls are intentionally outside the v0.2 product boundary.

`document-shell` provides the same product-neutral boundary for text-oriented
utilities: a document tab, command bar, rounded editor frame, status surface,
semantic icons and stable hit regions. Its `ZsTextDocument` adds UTF-8 and
BOM-tagged UTF-16 loading, explicit dirty state and transactional UTF-8 save or
save-as without platform handles. The Windows notepad benchmark proves the
hybrid route with a native text service and the target-dispatched
`NativeFileDialogService`. Parent-window modality, multi-tab state, target
interaction proof and a reusable rich-text engine remain incomplete.

`calculator` provides a complete standard-mode vertical slice at the framework
level: typed decimal operations, memory, history, a platform-adaptive View
keypad, semantic icons and stable namespaced actions. One unchanged acceptance
source now enters Win32, AppKit or Linux through the normal stateful View
builder. Windows proves its real input-state-paint loop; calculator-specific
AppKit/Linux target screenshots remain separate evidence gates. This composite
does not increase the component-catalog count or imply scientific, programmer,
graphing, conversion, localization or accessibility parity.

First-pass component status is not Fluent conformance. A built-in component
may advance toward ready only when it uses the shared typography, spacing,
radius, control-metric and semantic-color tokens; emits semantic icons instead
of private glyph text; and has hover, pressed, disabled, focus-visible, dark
theme and high-contrast evidence.

## Rust-First API Shape

Use composition and traits instead of inheritance. The direction is:

```rust
trait View<Msg> {
    fn layout(&mut self, cx: &mut ViewLayoutCx);
    fn event(&mut self, cx: &mut ViewEventCx<Msg>, event: ViewEvent);
    fn paint(&self, cx: &mut ViewPaintCx);
}
```

Application UI should compose values instead of subclassing controls:

```rust
button("Save")
    .padding(Dp::new(12.0))
    .radius(RadiusToken::Medium)
    .on_click(Msg::SaveClicked)
```

Messages should be typed. Prefer `enum Msg` over string event names:

```rust
enum Msg {
    SaveClicked,
    NameChanged(String),
    WindowClosed,
}

button("Save").on_click(Msg::SaveClicked)
```

This keeps event handling exhaustive, refactorable and visible to
rust-analyzer. Do not introduce string event APIs such as
`button.on("click", callback)` for framework-level controls.

Document-backed UI uses the same rule. A serialized action key is accepted
only when a generated or explicit binding manifest maps it to a typed message;
the document format is not permission to add reflective property mutation or
an unvalidated string event bus. Existing Rust builders remain supported and
may host document-backed subtrees through typed component slots.

## Ownership And State

Native resources should be RAII-owned. Users should create windows, icons,
tray items, fonts and bitmaps as Rust values, and cleanup should happen through
`Drop`. Unsafe platform calls belong inside the backend:

```rust
let icon = Icon::from_file("app.ico")?;
let tray = TrayIcon::new(icon)?;

let window = Window::new()
    .title("ZSUI")
    .size(900, 600)
    .build()?;
```

Public APIs must not require users to call `DestroyWindow`, `DeleteObject`,
`Release` or equivalent platform cleanup functions.

Application state should be explicit:

```rust
struct AppState {
    input: String,
    dark_mode: bool,
    selected_index: Option<usize>,
}

fn view(state: &AppState) -> impl View<Msg> {
    column((
        textbox(&state.input).on_change(Msg::NameChanged),
        row([text("Dark mode"), toggle(state.dark_mode).on_toggle(Msg::ToggleDark)]),
    ))
}
```

Do not hide product state inside controls or global registries. State changes
should flow through typed messages and explicit contexts such as `AppCx`,
`ViewEventCx`, `ViewLayoutCx` and `ViewPaintCx`.
`AppCx::command(...)` and `AppCx::ui_command(...)` must leave the View runtime
through explicit shared executors. Native hosts execute them after releasing
internal route locks, and product `UiCommand` values delegate through
`ProductAdapterUiCommandExecutor` rather than a global event bus.

## Typed Data

Avoid raw `i32` and `f32` for UI units when the meaning matters. Use typed
units at API boundaries:

```rust
let padding = Dp::new(12.0);
let real_px = padding.to_px(dpi);
```

Geometry and layout should move toward `Px`, `Dp`, `Dpi` and `UiLength` rather
than loose width/height/DPI numbers.

Use strong IDs instead of strings:

```rust
let input_id = WidgetId::new();
let settings_id = WindowId::new();
```

`typed_native_window(...)` now provides an opt-in content typestate:
`NativeWindowContentMissing` can be configured but cannot build or run, while
attaching a View, live View, draw plan or shell layout produces
`NativeWindowContentReady`. Keep `native_window(...)` as the concise path for
legitimate empty native surfaces; add more typestate only when it prevents a
real invalid state.

## Styling

Theme values should be tokens, not scattered literals:

```rust
theme.color.surface;
theme.color.text_primary;
theme.radius.medium;
theme.spacing.md;
```

This keeps Windows 11 styling, dark mode, high contrast and brand themes
replaceable without rewriting widgets.
The internal platform component profile owns each desktop family's radius,
spacing, control density, semantic type ramp, fallback font identity and focus
treatment. Shared widgets consume those resolved values; Win32, AppKit and
Linux backends remain responsible for system font discovery, shaping,
rasterization and native resources.

Reusable self-drawn layout patterns should stay product-neutral and preserve
their verified interaction and rendering invariants. A semantic navigation/
content shell should be expressed as layout data and typed draw commands, not
as a product settings screen or a fixed platform skin. Grouped cards or forms,
row titles, description text, row accessories, viewport masks, scrollbars and
action-button areas belong in reusable contracts such as `ZsShellLayoutSpec`;
the internal component profile resolves Fluent pane/cards, AppKit source-list/
forms or GTK sidebar/boxed-list composition. The product crate owns the actual
data and command behavior.

## Error And Platform Boundaries

Fallible host operations return `Result<T, ZsuiError>`. Backend creation,
DPI discovery, icon loading, font creation and native service setup should not
panic or show framework-owned error message boxes for recoverable failures.

Platform capabilities must be trait-based and honest:

```rust
trait TrayBackend {
    fn create_tray(&self, desc: TrayDesc) -> Result<TrayId, ZsuiError>;
}

trait HotkeyBackend {
    fn register_hotkey(&self, hotkey: Hotkey) -> Result<HotkeyId, ZsuiError>;
}
```

Expose a coherent ZSUI API, but do not pretend Windows, macOS, Linux and Android
support identical native services. Unsupported or partial behavior
must be visible through capability reports or `ZsuiError::Unsupported`.

Android should use an explicit mobile host contract instead of desktop
metaphors. It maps to Activity, lifecycle, Intent, ClipboardManager,
Storage Access Framework, IME and notification surfaces. The target requires
FFI/lifecycle bindings and target-device smoke artifacts before completion
claims. The current bridge contracts must remain explicit about callback
symbols, lifecycle/surface/input routes, safety rules and device-smoke artifact
names until real native FFI implementations replace the scaffold state.
Artifact reviewers should validate captured device proof without generating
fake screenshots or lifecycle logs.

## Build Trimming

ZSUI should be designed for explicit feature and crate boundaries:

```toml
[dependencies]
zsui = { version = "0.1", default-features = false, features = [
    "window",
    "button",
    "list",
    "scroll",
    "dark-mode",
] }
```

The goal is feature/crate based trimming, not a claim that Cargo automatically
removes every unused symbol from an enabled crate. Keep optional dependencies
behind feature gates, keep defaults small, and move large widget families or
backend integrations into modules or crates that users opt into.
Every public feature and the supported family combinations must compile through
`scripts/check-feature-matrix.ps1`; `.github/workflows/ci.yml` keeps this gate on
Windows and also checks the default/full surfaces on Linux and macOS.

Cargo features are additive across a dependency graph. If another crate enables
`zsui/textbox`, the final build of the shared `zsui` dependency includes that
feature. The framework target is therefore to make the default set small, keep
heavy dependencies optional, and split larger surfaces when feature unification
would otherwise pull too much code into unrelated applications.

The crate split target is:

- `zsui-core`: ids, errors, events, layout/state traits and basic protocols.
- `zsui-shell`: windows, tray/status items, DPI, hotkeys and shell services.
- `zsui-render`: Direct2D, Skia, WGPU, GDI or other renderer backends.
- `zsui-style`: theme tokens, colors, radius, spacing and typography.
- `zsui-widgets-base`: Button, Label, Icon and Panel.
- `zsui-widgets-input`: TextBox, CheckBox, Slider and IME-facing controls.
- `zsui-widgets-list`: List, Tree and Table families.
- `zsui-widgets-extra`: Dialog, Toast, Chart and advanced widgets.

Per-widget crates such as `zsui-button`, `zsui-textbox`, `zsui-list` or
`zsui-dialog` are acceptable later if a widget family becomes large enough to
justify that granularity.

Avoid eager global widget registration:

```rust
register_widget(Button::new());
register_widget(TextBox::new());
register_widget(Table::new());
register_widget(TreeView::new());
```

Prefer imports and builders that only reference the controls the user enabled
and used:

```rust
use zsui::prelude::*;
use zsui::widgets::{Button, List};

fn app() -> impl View<Msg> {
    column((
        Button::new("Save"),
        List::new(items),
    ))
}
```

Release builds can add size-focused profiles such as `opt-level = "z"`, thin
LTO, `codegen-units = 1`, symbol stripping and `panic = "abort"`, but those are
secondary to clean Cargo feature boundaries.

## AI Context Trimming

AI context should be composable in the same way as Cargo features. The default
prompt starts with `docs/ai-agent.md`, then selects one task pack from
`docs/ai/context-packs.json`. A pack names a bounded required set, optional
follow-up files and focused verification commands.

Avoid making every agent load the complete readiness report, every platform
backend, all examples and generated artifacts before a local change. Use `rg`
inside the selected files, read focused ranges and add another pack only when
the task crosses a real ownership boundary. `scripts/ai-context.ps1 -Validate`
must keep pack paths valid; CI should reject stale routing metadata.

The long-range target is measurable: keep the bootstrap small, keep normal
required packs within one module family, and move detailed status/history into
optional references. This reduces repeated prompt tokens without hiding
architecture rules or weakening verification.

## Completion Definition

The framework is not complete just because declarations compile. A surface is
complete only when the Rust API is product-neutral, feature-gated where needed,
covered by tests or examples, backed by a real host implementation when claimed
as native, and has target smoke artifacts for the OS or device. Until then,
report code-level readiness, target-smoke readiness and system-complete status
separately.

Progress must be reported on three separate scales:

- implementation readiness: framework code has been implemented and tested;
- runnable-platform readiness: a real host can create, interact with and
  repaint the surface on the target OS;
- framework product readiness: an external application can build and ship the
  workflow without depending on application internals.

An overall percentage must refer to framework product readiness. Internal
implementation milestones must not be presented as overall framework completion.