@hypen-space/web 0.4.947 → 0.4.949

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/canvas/paint.js +23 -33
  2. package/dist/canvas/paint.js.map +1 -1
  3. package/dist/canvas/utils.js +6 -1
  4. package/dist/canvas/utils.js.map +1 -1
  5. package/dist/dom/applicators/advanced-layout.js +9 -8
  6. package/dist/dom/applicators/advanced-layout.js.map +1 -1
  7. package/dist/dom/applicators/border.js +10 -17
  8. package/dist/dom/applicators/border.js.map +1 -1
  9. package/dist/dom/applicators/effects.js +9 -8
  10. package/dist/dom/applicators/effects.js.map +1 -1
  11. package/dist/dom/applicators/font.js +2 -1
  12. package/dist/dom/applicators/font.js.map +1 -1
  13. package/dist/dom/applicators/layout.js +2 -1
  14. package/dist/dom/applicators/layout.js.map +1 -1
  15. package/dist/dom/applicators/margin.js +6 -7
  16. package/dist/dom/applicators/margin.js.map +1 -1
  17. package/dist/dom/applicators/padding.js +9 -10
  18. package/dist/dom/applicators/padding.js.map +1 -1
  19. package/dist/dom/applicators/size.d.ts +19 -0
  20. package/dist/dom/applicators/size.js +35 -9
  21. package/dist/dom/applicators/size.js.map +1 -1
  22. package/dist/dom/applicators/transform.js +5 -4
  23. package/dist/dom/applicators/transform.js.map +1 -1
  24. package/dist/dom/applicators/typography.js +14 -5
  25. package/dist/dom/applicators/typography.js.map +1 -1
  26. package/package.json +4 -4
  27. package/src/canvas/QUICKSTART.md +0 -2
  28. package/src/canvas/paint.ts +21 -32
  29. package/src/canvas/utils.ts +6 -1
  30. package/src/dom/applicators/advanced-layout.ts +9 -8
  31. package/src/dom/applicators/border.ts +10 -17
  32. package/src/dom/applicators/effects.ts +9 -8
  33. package/src/dom/applicators/font.ts +2 -1
  34. package/src/dom/applicators/layout.ts +2 -1
  35. package/src/dom/applicators/margin.ts +6 -6
  36. package/src/dom/applicators/padding.ts +9 -9
  37. package/src/dom/applicators/size.ts +34 -9
  38. package/src/dom/applicators/transform.ts +5 -4
  39. package/src/dom/applicators/typography.ts +13 -5
  40. package/src/canvas/PARITY.md +0 -254
@@ -1,254 +0,0 @@
1
- # Canvas Renderer — DOM Parity Gaps
2
-
3
- Tracked list of gaps between `CanvasRenderer` and `DOMRenderer` when driven by
4
- the real WASM engine (the social example is the reference workload).
5
-
6
- The renderer currently handles the basic cases its unit tests cover (simple
7
- column/row flex, flat prop names), but has not been exercised end-to-end
8
- against engine output. Recent fixes landed the obvious bridging layer —
9
- applicator-namespaced props, rem/em length parsing, `scrollable`↔`overflow`,
10
- `aspectRatio` — and an additional batch (Stack overlay, image intrinsic
11
- aspect, per-axis scroll, hit-test scroll offset, intrinsic-size flex-shrink)
12
- landed alongside the `tests/canvas-parity.test.ts` fixture suite. The
13
- remaining items below still block full parity.
14
-
15
- ## Known gaps
16
-
17
- ### Text
18
-
19
- - **Line wrapping inside flex rows.** Text that wraps across lines in the
20
- DOM currently either overflows horizontally or reports the wrong measured
21
- height back to Taffy. Columns below it then collapse because subsequent
22
- children layout against stale content height.
23
- - **Measurement before first layout pass.** Text nodes report width based on
24
- `ctx.measureText` at the moment the Taffy tree is built; when the Taffy
25
- WASM layer initialises async, early frames get the JS-flexbox fallback and
26
- measurements can disagree between the two paths.
27
-
28
- ### Layout
29
-
30
- - **`Stack` absolute positioning.** Fixed — Stack now uses Taffy Grid with a
31
- single 1×1 cell and every child placed at `gridColumn: 1 / 2; gridRow: 1
32
- / 2`, mirroring the DOM `grid-template-areas: "stack"` strategy.
33
- `horizontalAlignment` / `verticalAlignment` map to `justifyItems` /
34
- `alignItems` so the badge can pin to any corner.
35
- - **`Image` natural-size fallback.** Fixed — when an Image declares only one
36
- of `width`/`height` and its intrinsic size is known (loaded image, or
37
- seeded via `setImageNaturalSize` for tests), the other dimension is
38
- derived from `naturalWidth / naturalHeight` and pinned explicitly so the
39
- flex parent's default `align-items: stretch` can't override it.
40
- - **Per-axis scroll.** Fixed — `getScrollAxes(node)` returns `{ x, y }` and
41
- the wheel/drag/clamp paths in `ScrollManager` honour each axis
42
- independently. A horizontal strip with `scrollable: "horizontal"` no
43
- longer accumulates Y scroll range from tall children, and the layout
44
- builder additionally drops `flex-shrink` to 0 on direct children of a
45
- main-axis-scrollable parent so items overflow instead of collapsing.
46
- - **Scrolled hit-testing / event routing.** Fixed — `getScrollAwareBounds`
47
- no longer double-walks the parent chain to add `contentX/Y` (the layout
48
- pass already writes absolute coordinates), and only subtracts each
49
- scrollable ancestor's scroll offset. `getAbsoluteBounds` simplified to
50
- match.
51
-
52
- ### Painters
53
-
54
- - **Button transparent background.** Fixed — explicit backgrounds now win
55
- over the default-blue hover swap. Mentioned here as the canary for
56
- per-component painters that still assume flat-prop defaults; audit the
57
- rest of `paint.ts` against engine output.
58
- - **Icon sizing inside buttons.** Fixed — intrinsic-sized components (icon,
59
- avatar, badge, switch, checkbox, radio, spinner) now default to
60
- `flex-shrink: 0`. They keep their default size in tight cross-axis
61
- containers instead of collapsing to 0×0.
62
- - **Border-radius clamping.** Fixed — Tailwind's `rounded-full` lands as
63
- `border-radius: 9999px`. Without clamping, `drawRoundedRect`'s arcs
64
- wrapped outside the box and a 20×20 blue badge painted as a 470×250
65
- rectangle, swamping the Stories row. `drawRoundedRect` and
66
- `isPointInRoundedRect` now clamp `radius` to `min(width, height) / 2`.
67
-
68
- ### Hosting
69
-
70
- - **Taffy WASM 404 in the social example.** Fixed in
71
- `examples/social/typescript/web_canvas/serve.ts` — the bundled canvas
72
- references `taffy_wasm_bg.wasm` at the page origin, so the dev server
73
- needs to serve it. Without this route the renderer silently falls back
74
- to the JS flexbox path, which drops most of the parity work above.
75
-
76
- ### Engine prop shape
77
-
78
- - **Element type case sensitivity.** Fixed — engine emits types
79
- capitalised (`"Text"`, `"Button"`, `"Input"`, …). The Taffy `buildTree`
80
- text-leaf check used `node.type === "text"` and missed; every Text
81
- became an empty container with size 0×0 and downstream siblings landed
82
- on top of each other (the visible symptom: "1,431 likes" overlapped the
83
- caption row). The renderer's `clickable`/`focusable` flags had the same
84
- bug — Buttons weren't flagged clickable, so cursor barely fired.
85
- All per-type checks now lowercase the type before comparing.
86
-
87
- ### Painters
88
-
89
- - **Image circular clipping.** Fixed — `paintImage` clips to the rounded
90
- box before drawing, so `rounded-full` avatars actually paint as circles
91
- (story rings + post-header avatars). Also added `objectFit: cover`
92
- semantics — the source is centered and cropped instead of stretched.
93
-
94
- ### Scrolling
95
-
96
- - **Root inflated by overflowing children.** Fixed — when the root has no
97
- explicit width/height, `computeLayoutTaffy` now pins it to the canvas
98
- dimensions. Previously a horizontally-scrollable Stories row with 5+
99
- w-24 items inflated the entire feed to 607px on a 470px canvas and
100
- pushed the BottomNav off-screen.
101
- - **Scroll containers contribute their min-content to parent layout.**
102
- Fixed — scrollable nodes set `style.overflow = Scroll` on the relevant
103
- axis. Children still flow at intrinsic positions (so
104
- `ScrollManager.updateScrollBounds` sees the correct scrollHeight) but
105
- the container itself stops bubbling its min-content sideways.
106
-
107
- ### Events
108
-
109
- - **Cursor flash on hover over Buttons.** Fixed — the hit test returns
110
- the deepest visible node, which for a Button-with-Icon is the Icon
111
- (not clickable). `events.ts` now lifts the hit to the nearest
112
- clickable ancestor for hover/cursor and click/mousedown/mouseup
113
- dispatch, so the cursor stays a pointer over the whole Button surface.
114
- - **Clicks silently dropped after `mouseup`.** Fixed — `onMouseUp`
115
- cleared `mouseDownNode = null`, but the browser fires `click` AFTER
116
- `mouseup`, and `onClick`'s press+release sameness check then always
117
- failed (`null !== <Button>`). The clear is now done in `onClick`.
118
-
119
- ### Text rendering
120
-
121
- - **`View all X comments` wraps to 2 lines.** Fixed — pretext returns a
122
- fractional pixel width (e.g. `141.71875` for "View all 87 comments"),
123
- Taffy gives the Button exactly that, but at paint time `renderText`
124
- re-measures with a maxWidth of the integer-rounded box width and
125
- pretext wraps because `141.71 > 141`. The Taffy measure callback now
126
- returns `Math.ceil(metrics.width)` so the box reserves the next whole
127
- pixel up.
128
-
129
- ### Components
130
-
131
- - **`List` rendered as a row.** Fixed — `List` is the DSL's vertical-
132
- iteration container (DOM uses `flex-direction: column`). The canvas
133
- buildTaffyStyle now treats `type === "list"` as Column unless the
134
- `direction` prop says `"horizontal"`. Notifications stack vertically
135
- again.
136
- - **`Grid(...).gridColumns(N)` rendered as 1 column.** Fixed — the
137
- canvas was reading `gridTemplateColumns`/`columns` only, but the
138
- engine emits the applicator name (`gridColumns`) on props. Also,
139
- `parseGridTemplate(N)` now expands a bare number to N equal `1fr`
140
- tracks (matching `dom/applicators/advanced-layout.ts`).
141
- - **Grid items with `width: "100%"` collapsed track sizing.** Fixed —
142
- on a Grid child, `width: "100%"` creates a circular dependency with
143
- `1fr` track sizing (the cell wants to be `1fr` of the grid; the item
144
- wants to be `100%` of the cell). Children of a Grid container now
145
- ignore `width: "100%"` / `height: "100%"` (use `auto`) so Taffy's
146
- default grid stretch fills the cell.
147
- - **Grid items with `aspect-ratio: 1` swallowed siblings.** Fixed —
148
- default `min-width: auto` (= min-content) on grid items collapsed
149
- tracks. Grid children now default to `min-width: 0; min-height: 0`,
150
- the standard CSS workaround.
151
-
152
- ### Flex defaults
153
-
154
- - **`flex: 1` shorthand left basis at `auto`.** Fixed — `flex: 1` in
155
- CSS expands to `flex: 1 1 0%`, but the canvas was only reading the
156
- grow value. Without the `0%` basis, an overflowing flex item started
157
- at its intrinsic content size and Taffy redistributed from there,
158
- squeezing the BottomNav off-screen on long routes (the home feed
159
- with N posts; the search grid with 100s of items).
160
-
161
- ### Z-order
162
-
163
- - **Absolute-positioned overlays painted under in-flow siblings.**
164
- Fixed — Story declares its close-button header BEFORE the full-screen
165
- Image; CSS `z-index` puts the header on top, but Canvas paints in
166
- tree order, so the Image covered the header (and the Hover/click
167
- for the close button hit the Image instead). `paintNode` now paints
168
- flow children first, then absolute children on top; `events.ts`
169
- hit-tests absolutes first (front-to-back). Story's ✕ navigates
170
- back to `/` again.
171
-
172
- ### Form controls
173
-
174
- - **Input / Textarea collapse to padding-only height.** Fixed —
175
- `Input`, `Textarea`, and `Select` paint via the `InputOverlay` HTML
176
- element, so the canvas layout doesn't see their text content as
177
- children. The default fall-through gave them `size.auto`, and Taffy
178
- (border-box by default) collapsed the box to padding only — the
179
- social Search bar rendered as a 4-tall pill instead of the ~36 the
180
- DOM produces, with the `Search` placeholder peeking out of a sliver
181
- that looked nothing like the iOS / Android equivalent. Each control
182
- now sets `size.height = lineHeight × rows + padding + border` so the
183
- outer box matches the DOM, with Textarea defaulting to `rows: 3`.
184
-
185
- ### Grid auto rows
186
-
187
- - **Aspect-ratio image grid collapses to gap-tall rows.** Fixed —
188
- Taffy's grid auto-row sizing queries each leaf's max-content height
189
- before column widths are resolved. For an Image leaf with no fixed
190
- height, max-content = 0, so a `Grid.gridColumns(3)` of `aspect-square`
191
- images sized the implicit rows to gap-only and items overlapped by
192
- ~150px each (the Search route's explore grid was a single squashed
193
- band of every image). `computeLayout` now runs a second Taffy pass
194
- when `annotateCollapsedAspectGrids` finds a Grid where the next-row
195
- child sits closer than `firstChild.width / aspectRatio` — the second
196
- pass reads a private `__autoRowsPx` hint set on the grid (column
197
- width / aspectRatio) and pins implicit rows to that height. Two-pass
198
- cost is bounded: only fires when an aspect-ratio overflow is observed,
199
- not on every layout.
200
-
201
- ### HiDPI
202
-
203
- - **Hit tests offset by `dpr` on Mac retina screens.** Fixed —
204
- `events.ts` `getCanvasCoordinates` (and the same helpers in `scroll.ts`
205
- / `selection.ts` / `input.ts`) used to multiply client coords by
206
- `canvas.width / rect.width`, which equals `devicePixelRatio` because
207
- the renderer scales the ctx by dpr at setup. Layout (and therefore
208
- the hit-test bounds) live in CSS pixels — multiplying again landed
209
- every click at 2× the bounds. Visible symptoms on a Mac (dpr=2):
210
- the cursor turned to a pointer ABOVE the actual story avatar instead
211
- of on it, and bottom-nav clicks/hovers fell off the bottom of the
212
- canvas entirely (y=850 logical → y=1700 canvas-pixel, past the 900
213
- canvas height). Drag-scroll deltas and Input/Textarea overlay
214
- positioning had the same family of bug. All four now use logical
215
- CSS pixels directly.
216
-
217
- ### Action wire format
218
-
219
- - **`@router.push` dispatched verbatim.** Fixed — DOM strips the `@`
220
- prefix and the `actions.` namespace before sending to the engine,
221
- but the canvas was sending `@router.push` literal. The engine
222
- silently dropped it, so every nav click was a no-op. `props.ts`
223
- `resolveEventAction` now mirrors `dom/applicators/events.ts`
224
- `extractActionDetails`.
225
-
226
- ### Engine prop shape (continued)
227
-
228
- - **Click was always `null !== <Button>`.** Fixed — `onMouseUp`
229
- cleared `this.mouseDownNode = null`, but the browser fires `click`
230
- AFTER `mouseup`. The `node === mouseDownNode` press+release check
231
- in `onClick` always saw `null`. The clear is now done at the end
232
- of `onClick` instead.
233
-
234
- ## Not yet assessed
235
-
236
- - Input / Textarea focus + caret positioning against the InputOverlay DOM
237
- inputs.
238
- - Select / Switch / Slider under engine-driven props.
239
- - Router attach/detach patches (subtree caching) — the canvas renderer
240
- implements `detach` / `attach` but hasn't been tested against the
241
- ManagedRouter persist-cache path.
242
-
243
- ## How to make progress
244
-
245
- The shape of each of these is the same: write a small fixture in
246
- `tests/canvas-*.test.ts` that reproduces the engine-output shape (use the
247
- patches captured in `ws://localhost:3000` as the input), assert the expected
248
- layout / paint output, then fix the canvas code until the test passes. The
249
- screenshot-driven loop we've been in is too expensive; the real investment
250
- is in fixture tests that pin behaviour.
251
-
252
- The social example under `examples/social/typescript/web_canvas/` is the
253
- end-to-end integration check, not a regression target — carry a visible
254
- "partial" banner until parity is reached.