|
2 | 2 |
|
3 | 3 | No textual content may bleed out of the device viewport. The renderer enforces this through three opt-in mechanisms, all checked by `rustmotion validate`. |
4 | 4 |
|
5 | | -## 1. Text wrapping (`style.wrap`) |
| 5 | +## 1. Text wrapping (`style.white-space`) |
6 | 6 |
|
7 | | -`text` is constraint-aware: it wraps to its parent's allocated width by default. Set `style.wrap: false` only when you intentionally want the text to render on one line and you guarantee it fits (e.g. a marquee that bleeds, a ticker, a title with a fixed `max-width`). |
| 7 | +`text` is constraint-aware: it wraps to its parent's allocated width by default. There is no `style.wrap` field — that boolean belonged to the pre-CSS `LayerStyle` model and no longer exists in `CssStyle` (`deny_unknown_fields` rejects it and silently drops the component). Wrapping is controlled by the standard CSS `white-space` property instead: |
8 | 8 |
|
9 | | -- Default: `wrap: true` (wraps at parent or `max_width`, whichever is smaller). |
10 | | -- `wrap: false` → validator fails with `unwrappable_text_overflow` if the natural width exceeds the box. |
| 9 | +- Default: unset / `"normal"` (and `"pre-line"`, `"pre-wrap"`, `"break-spaces"`) — wraps at parent width or `max-width`, whichever is smaller. |
| 10 | +- `white-space: "nowrap"` or `"pre"` → the text renders on one line. The validator measures its natural (unbounded) width and fails with `unwrappable_text_overflow` if that width exceeds the box. Only set this when you intentionally want a single line and a finite `max-width` + reasonable `font-size` guarantee it fits (e.g. a title, a ticker-like label — not a marquee, which has its own component). |
11 | 11 |
|
12 | 12 | ```json |
13 | | -{ "type": "text", "content": "Long sentence...", "style": { "wrap": true, "max-width": 800 } } |
| 13 | +{ "type": "text", "content": "Long sentence...", "style": { "white-space": "nowrap", "max-width": 800 } } |
14 | 14 | ``` |
15 | 15 |
|
16 | 16 | ## 2. Codeblock / Terminal `auto_scroll` |
@@ -39,38 +39,41 @@ CSS-like semantics: `visible` (default) lets children bleed; `hidden` clips at t |
39 | 39 |
|
40 | 40 | ## What the validator catches |
41 | 41 |
|
42 | | -`rustmotion validate scenario.json` reports three geometry violation kinds: |
| 42 | +`rustmotion validate scenario.json` reports five geometry violation kinds: |
43 | 43 |
|
44 | 44 | - `viewport_overflow` — absolute bbox crosses the device edge |
45 | | -- `unwrappable_text_overflow` — `wrap: false` but natural width > available width |
| 45 | +- `unwrappable_text_overflow` — `white-space: "nowrap"`/`"pre"` but natural width > available width |
| 46 | +- `content_overflows_box` — wrapping text needs more room than the box it was actually assigned, typically a paragraph inside a card with a fixed `height` too small for it. Text painters never clip themselves, so this paints outside its box even when the box sits comfortably inside the frame — which is why the viewport check alone never caught it. |
46 | 47 | - `auto_scroll_disabled_overflow` — `auto_scroll: false` but content > box |
| 48 | +- `animated_text_overflow` — an animated transform (scale/translate/wiggle/orbit) pushes the bbox out of the viewport at some sampled time. Only checked with `--strict-anim` (default runs check the resting, untransformed layout only). |
47 | 49 |
|
48 | | -`marquee` and `cursor` are exempt (their job is to bleed). |
| 50 | +`marquee` and `cursor` are exempt (their job is to bleed). A node is also exempt when it clips itself, or when any ancestor clips it — `overflow` set to anything other than `visible`. Deliberate bleed under a clipping parent is a composition technique, not a defect: that is how you get giant type running off the frame. |
49 | 51 |
|
50 | 52 | ## CLI usage |
51 | 53 |
|
52 | 54 | ```bash |
53 | 55 | rustmotion validate scenario.json # human-readable |
54 | 56 | rustmotion validate scenario.json --report report.json # JSON report |
55 | 57 | rustmotion validate scenario.json --fix # safe auto-fixes |
56 | | -rustmotion validate scenario.json --strict-anim # per-frame check |
| 58 | +rustmotion validate scenario.json --strict-anim # per-frame check, adds animated_text_overflow |
57 | 59 | rustmotion validate scenario.json --lenient # warnings only |
58 | 60 | ``` |
59 | 61 |
|
60 | | -`--fix` rewrites the file in place. It only applies *safe* mutations: |
61 | | -- sets `style.wrap: true` on text that overflowed because wrap was off |
62 | | -- sets `auto_scroll: true` on codeblock/terminal with overflow |
| 62 | +`--fix` rewrites the file in place: |
| 63 | +- `auto_scroll_disabled_overflow` → sets `auto_scroll: true`. Safe. |
| 64 | +- `unwrappable_text_overflow` → removes `style.white-space`, so the text falls back to the `normal` default and wraps again. Non-destructive: it only ever deletes the property that caused the violation. If you want the line to stay unbroken, widen the box or lower `font-size` by hand instead of running `--fix`. |
63 | 65 |
|
64 | | -Position/size clamping is never auto-applied — fix those by hand. |
| 66 | +Position/size clamping (`viewport_overflow`) is never auto-applied — fix those by hand too. |
65 | 67 |
|
66 | 68 | ## When to use what |
67 | 69 |
|
68 | 70 | | Symptom | Fix | |
69 | 71 | |---|---| |
70 | | -| Long sentence cut at viewport edge | leave `wrap: true` (default) and ensure parent has finite width | |
71 | | -| Need a single-line title that must fit | set `max-width` and a small enough `font-size`, leave `wrap: true` | |
72 | | -| Marquee / ticker text that intentionally scrolls past edges | use `marquee` (exempt) — never `text` with `wrap: false` | |
| 72 | +| Long sentence cut at viewport edge | leave `white-space` unset (default wraps) and ensure parent has finite width | |
| 73 | +| Need a single-line title that must fit | set `max-width` and a small enough `font-size`, leave `white-space` unset | |
| 74 | +| Marquee / ticker text that intentionally scrolls past edges | use `marquee` (exempt) — never `text` with `white-space: "nowrap"` | |
73 | 75 | | Code listing taller than its box | leave `auto_scroll: true` (default) | |
74 | 76 | | Terminal log streaming many lines | leave `auto_scroll: true` | |
75 | 77 | | Badge protruding from a card on purpose | container has `overflow: visible` (default) — no change needed | |
76 | 78 | | Hard-clip children to a card border | container `style.overflow: "hidden"` | |
| 79 | +| Animated element (wiggle/orbit/keyframe scale) might drift off-screen | run `rustmotion validate --strict-anim` to sample frames, not just the resting layout | |
0 commit comments