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
//! Render bundle — one call produces every artifact the agent loop needs.
//!
//! A [`Bundle`] is the textual + visual representation of a rendered tree:
//!
//! - `svg` — visual fixture (also convertible to PNG via `tools/svg_to_png.sh`).
//! - `tree_dump` — semantic walk of the laid-out tree with rects and source.
//! - `draw_ops` — flat draw-op IR (the same one a wgpu backend consumes).
//! - `shader_manifest` — every shader used by this tree, with uniform values.
//! - `lint` — findings: raw values in user code, overflows, duplicate IDs.
//!
//! [`render_bundle`] runs layout + draw-op resolution + dump + lint in one
//! call so a single `cargo run --example X` produces everything needed
//! to verify intent without further round-trips.
//!
//! The SVG output is approximate (stock shaders rendered best-effort,
//! custom shaders as placeholder rects). The wgpu renderer is the source
//! of truth for visual fidelity; SVG stays as a layout/structure
//! debugging artifact.
//!
//! # Wiring this into your app
//!
//! The bundle pipeline is also the cheapest layout-review path *during
//! app development*. It runs CPU-only, exercises the same layout +
//! draw-op stack the GPU does, and produces a diffable tree dump that
//! catches regressions long before they hit a window. The shape every
//! aetna app converges on:
//!
//! ```ignore
//! // crates/your-app/src/bin/dump_bundles.rs
//! use aetna_core::prelude::*;
//! use std::path::PathBuf;
//!
//! struct MockBackend { state: AppState }
//! impl UiBackend for MockBackend { /* return canned `state` */ }
//!
//! enum Scene { Empty, Loaded, ErrorDialog /* ... */ }
//!
//! fn main() -> std::io::Result<()> {
//! let viewport = Rect::new(0.0, 0.0, 1280.0, 800.0);
//! let out_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("out");
//!
//! for scene in Scene::ALL {
//! let mut app = MyApp::new(MockBackend { state: scene.canned_state() });
//! // Local UI flags? Drive them through the real on_event path:
//! scene.drive_setup(&mut app);
//!
//! let theme = app.theme();
//! let mut tree = app.build(&BuildCx::new(&theme));
//! let bundle = render_bundle(&mut tree, viewport);
//! write_bundle(&bundle, &out_dir, &scene.slug())?;
//! if !bundle.lint.findings.is_empty() {
//! eprint!("{}", bundle.lint.text());
//! }
//! }
//! Ok(())
//! }
//! ```
//!
//! Three to six scenes is plenty for a typical app chrome. Output goes
//! to `crates/<app>/out/` (gitignore the directory). Worked examples in
//! the workspace: `tools/src/bin/dump_showcase_bundles.rs` (aetna's own
//! showcase), and the `render_artifacts` / `dump_bundles` bins in the
//! external `aetna-volume` and `rumble-aetna` apps.
//!
//! Driving local UI state via [`crate::event::UiEvent::synthetic_click`]
//! is preferred over fixture-only setters: the dumped scene is exactly
//! what the user sees after performing the same interaction, so the
//! fixture and production code can't drift.
use Path;
use inspect;
use ;
use manifest;
use svg_from_ops;
use cratedraw_ops;
use crateDrawOp;
use cratelayout;
use crateUiState;
use crateTheme;
use cratetokens;
use crate;
/// Everything an agent loop wants from a single render.
/// Lay out, resolve to draw ops, dump, lint.
///
/// Constructs a fresh [`UiState`] internally — bundle artifacts are a
/// snapshot of the tree at rest, with no hover/press/focus state. For
/// fixtures that need to demonstrate non-trivial state (a scroll
/// position, a hovered button), see [`render_bundle_with`].
/// Same as [`render_bundle`], but resolves implicit surfaces through a
/// caller-supplied [`Theme`].
/// Same as [`render_bundle`], but threads a caller-built [`UiState`]
/// through the pipeline. Use this when the fixture wants to seed
/// runtime state (scroll offsets, hovered/focused trackers) before
/// snapshotting — the layout pass reads it, and the resulting bundle
/// reflects the seeded state.
///
/// Seed scroll offsets by calling [`crate::layout::assign_ids`] first
/// to populate `computed_id`, then calling [`UiState::set_scroll_offset`].
/// Same as [`render_bundle_with`], but resolves implicit surfaces through
/// a caller-supplied [`Theme`].
/// Write a bundle to disk under `dir`, naming files `{name}.{ext}`.
///
/// Files written:
/// - `{name}.svg`
/// - `{name}.tree.txt`
/// - `{name}.draw_ops.txt`
/// - `{name}.shader_manifest.txt`
/// - `{name}.lint.txt`