@hypen-space/web 0.4.980 → 0.5.3

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 (229) hide show
  1. package/README.md +20 -13
  2. package/dist/canvas/accessibility.d.ts +118 -11
  3. package/dist/canvas/accessibility.js +470 -108
  4. package/dist/canvas/accessibility.js.map +1 -1
  5. package/dist/canvas/dispatch.d.ts +24 -0
  6. package/dist/canvas/dispatch.js +62 -0
  7. package/dist/canvas/dispatch.js.map +1 -0
  8. package/dist/canvas/editing.d.ts +178 -0
  9. package/dist/canvas/editing.js +590 -0
  10. package/dist/canvas/editing.js.map +1 -0
  11. package/dist/canvas/events.d.ts +28 -23
  12. package/dist/canvas/events.js +90 -103
  13. package/dist/canvas/events.js.map +1 -1
  14. package/dist/canvas/focus.d.ts +78 -0
  15. package/dist/canvas/focus.js +182 -0
  16. package/dist/canvas/focus.js.map +1 -0
  17. package/dist/canvas/index.d.ts +3 -2
  18. package/dist/canvas/index.js +3 -2
  19. package/dist/canvas/index.js.map +1 -1
  20. package/dist/canvas/layout.js +68 -98
  21. package/dist/canvas/layout.js.map +1 -1
  22. package/dist/canvas/paint.d.ts +16 -3
  23. package/dist/canvas/paint.js +449 -110
  24. package/dist/canvas/paint.js.map +1 -1
  25. package/dist/canvas/renderer.d.ts +28 -4
  26. package/dist/canvas/renderer.js +201 -81
  27. package/dist/canvas/renderer.js.map +1 -1
  28. package/dist/canvas/selection.js +31 -177
  29. package/dist/canvas/selection.js.map +1 -1
  30. package/dist/canvas/text-geometry.d.ts +72 -0
  31. package/dist/canvas/text-geometry.js +244 -0
  32. package/dist/canvas/text-geometry.js.map +1 -0
  33. package/dist/canvas/text.js +44 -18
  34. package/dist/canvas/text.js.map +1 -1
  35. package/dist/canvas/types.d.ts +10 -1
  36. package/dist/canvas/utils.d.ts +0 -23
  37. package/dist/canvas/utils.js +3 -67
  38. package/dist/canvas/utils.js.map +1 -1
  39. package/dist/canvas/variants.d.ts +52 -0
  40. package/dist/canvas/variants.js +156 -0
  41. package/dist/canvas/variants.js.map +1 -0
  42. package/dist/dom/a11y-styles.d.ts +20 -0
  43. package/dist/dom/a11y-styles.js +61 -0
  44. package/dist/dom/a11y-styles.js.map +1 -0
  45. package/dist/dom/applicators/aria.d.ts +19 -0
  46. package/dist/dom/applicators/aria.js +36 -0
  47. package/dist/dom/applicators/aria.js.map +1 -0
  48. package/dist/dom/applicators/events.d.ts +7 -0
  49. package/dist/dom/applicators/events.js +36 -8
  50. package/dist/dom/applicators/events.js.map +1 -1
  51. package/dist/dom/applicators/font.d.ts +11 -6
  52. package/dist/dom/applicators/font.js +16 -27
  53. package/dist/dom/applicators/font.js.map +1 -1
  54. package/dist/dom/applicators/index.js +51 -45
  55. package/dist/dom/applicators/index.js.map +1 -1
  56. package/dist/dom/canvas/index.js +2 -2
  57. package/dist/dom/canvas/index.js.map +1 -1
  58. package/dist/dom/components/app.js +2 -2
  59. package/dist/dom/components/app.js.map +1 -1
  60. package/dist/dom/components/audio.js +2 -2
  61. package/dist/dom/components/audio.js.map +1 -1
  62. package/dist/dom/components/avatar.js +3 -4
  63. package/dist/dom/components/avatar.js.map +1 -1
  64. package/dist/dom/components/badge.js +3 -4
  65. package/dist/dom/components/badge.js.map +1 -1
  66. package/dist/dom/components/button.js +2 -2
  67. package/dist/dom/components/button.js.map +1 -1
  68. package/dist/dom/components/card.js +2 -2
  69. package/dist/dom/components/card.js.map +1 -1
  70. package/dist/dom/components/center.js +2 -2
  71. package/dist/dom/components/center.js.map +1 -1
  72. package/dist/dom/components/checkbox.js +4 -5
  73. package/dist/dom/components/checkbox.js.map +1 -1
  74. package/dist/dom/components/column.js +2 -2
  75. package/dist/dom/components/column.js.map +1 -1
  76. package/dist/dom/components/container.js +2 -2
  77. package/dist/dom/components/container.js.map +1 -1
  78. package/dist/dom/components/divider.js +2 -2
  79. package/dist/dom/components/divider.js.map +1 -1
  80. package/dist/dom/components/grid.js +10 -10
  81. package/dist/dom/components/grid.js.map +1 -1
  82. package/dist/dom/components/heading.js +4 -6
  83. package/dist/dom/components/heading.js.map +1 -1
  84. package/dist/dom/components/hypenapp.d.ts +25 -1
  85. package/dist/dom/components/hypenapp.js +215 -247
  86. package/dist/dom/components/hypenapp.js.map +1 -1
  87. package/dist/dom/components/icon.js +4 -5
  88. package/dist/dom/components/icon.js.map +1 -1
  89. package/dist/dom/components/image.js +2 -2
  90. package/dist/dom/components/image.js.map +1 -1
  91. package/dist/dom/components/index.d.ts +2 -2
  92. package/dist/dom/components/index.js +9 -2
  93. package/dist/dom/components/index.js.map +1 -1
  94. package/dist/dom/components/input.js +2 -2
  95. package/dist/dom/components/input.js.map +1 -1
  96. package/dist/dom/components/link.js +2 -2
  97. package/dist/dom/components/link.js.map +1 -1
  98. package/dist/dom/components/list.js +2 -2
  99. package/dist/dom/components/list.js.map +1 -1
  100. package/dist/dom/components/paragraph.js +3 -4
  101. package/dist/dom/components/paragraph.js.map +1 -1
  102. package/dist/dom/components/progressbar.js +3 -3
  103. package/dist/dom/components/progressbar.js.map +1 -1
  104. package/dist/dom/components/route.js +2 -2
  105. package/dist/dom/components/route.js.map +1 -1
  106. package/dist/dom/components/router.js +2 -2
  107. package/dist/dom/components/router.js.map +1 -1
  108. package/dist/dom/components/row.js +10 -10
  109. package/dist/dom/components/row.js.map +1 -1
  110. package/dist/dom/components/select.js +3 -4
  111. package/dist/dom/components/select.js.map +1 -1
  112. package/dist/dom/components/slider.js +2 -2
  113. package/dist/dom/components/slider.js.map +1 -1
  114. package/dist/dom/components/spacer.js +2 -2
  115. package/dist/dom/components/spacer.js.map +1 -1
  116. package/dist/dom/components/spinner.js +27 -13
  117. package/dist/dom/components/spinner.js.map +1 -1
  118. package/dist/dom/components/stack.js +10 -10
  119. package/dist/dom/components/stack.js.map +1 -1
  120. package/dist/dom/components/switch.js +5 -6
  121. package/dist/dom/components/switch.js.map +1 -1
  122. package/dist/dom/components/tabs.d.ts +27 -0
  123. package/dist/dom/components/tabs.js +69 -0
  124. package/dist/dom/components/tabs.js.map +1 -0
  125. package/dist/dom/components/text.js +4 -7
  126. package/dist/dom/components/text.js.map +1 -1
  127. package/dist/dom/components/textarea.js +2 -2
  128. package/dist/dom/components/textarea.js.map +1 -1
  129. package/dist/dom/components/video.js +2 -2
  130. package/dist/dom/components/video.js.map +1 -1
  131. package/dist/dom/components/visuallyhidden.d.ts +9 -0
  132. package/dist/dom/components/visuallyhidden.js +26 -0
  133. package/dist/dom/components/visuallyhidden.js.map +1 -0
  134. package/dist/dom/debug.js +6 -8
  135. package/dist/dom/debug.js.map +1 -1
  136. package/dist/dom/index.d.ts +0 -1
  137. package/dist/dom/index.js +0 -1
  138. package/dist/dom/index.js.map +1 -1
  139. package/dist/dom/operability.d.ts +100 -0
  140. package/dist/dom/operability.js +298 -0
  141. package/dist/dom/operability.js.map +1 -0
  142. package/dist/dom/renderer.d.ts +98 -5
  143. package/dist/dom/renderer.js +400 -66
  144. package/dist/dom/renderer.js.map +1 -1
  145. package/dist/dom/route-focus.d.ts +42 -0
  146. package/dist/dom/route-focus.js +88 -0
  147. package/dist/dom/route-focus.js.map +1 -0
  148. package/dist/dom/semantics.d.ts +35 -0
  149. package/dist/dom/semantics.js +184 -0
  150. package/dist/dom/semantics.js.map +1 -0
  151. package/dist/variants.d.ts +90 -0
  152. package/dist/variants.js +206 -0
  153. package/dist/variants.js.map +1 -0
  154. package/package.json +2 -2
  155. package/src/canvas/QUICKSTART.md +16 -11
  156. package/src/canvas/README.md +63 -34
  157. package/src/canvas/accessibility.ts +507 -115
  158. package/src/canvas/dispatch.ts +78 -0
  159. package/src/canvas/editing.ts +697 -0
  160. package/src/canvas/events.ts +104 -115
  161. package/src/canvas/focus.ts +216 -0
  162. package/src/canvas/index.ts +8 -9
  163. package/src/canvas/layout.ts +68 -103
  164. package/src/canvas/paint.ts +509 -109
  165. package/src/canvas/renderer.ts +239 -99
  166. package/src/canvas/selection.ts +52 -210
  167. package/src/canvas/text-geometry.ts +311 -0
  168. package/src/canvas/text.ts +45 -18
  169. package/src/canvas/types.ts +28 -1
  170. package/src/canvas/utils.ts +3 -69
  171. package/src/canvas/variants.ts +172 -0
  172. package/src/dom/README.md +8 -7
  173. package/src/dom/a11y-styles.ts +65 -0
  174. package/src/dom/applicators/aria.ts +41 -0
  175. package/src/dom/applicators/events.ts +39 -7
  176. package/src/dom/applicators/font.ts +16 -29
  177. package/src/dom/applicators/index.ts +61 -46
  178. package/src/dom/canvas/index.ts +2 -2
  179. package/src/dom/components/app.ts +2 -2
  180. package/src/dom/components/audio.ts +2 -2
  181. package/src/dom/components/avatar.ts +3 -4
  182. package/src/dom/components/badge.ts +3 -4
  183. package/src/dom/components/button.ts +2 -2
  184. package/src/dom/components/card.ts +2 -2
  185. package/src/dom/components/center.ts +2 -2
  186. package/src/dom/components/checkbox.ts +4 -5
  187. package/src/dom/components/column.ts +2 -2
  188. package/src/dom/components/container.ts +2 -2
  189. package/src/dom/components/divider.ts +2 -2
  190. package/src/dom/components/grid.ts +10 -10
  191. package/src/dom/components/heading.ts +4 -6
  192. package/src/dom/components/hypenapp.ts +246 -274
  193. package/src/dom/components/icon.ts +4 -5
  194. package/src/dom/components/image.ts +2 -2
  195. package/src/dom/components/index.ts +10 -3
  196. package/src/dom/components/input.ts +2 -2
  197. package/src/dom/components/link.ts +2 -2
  198. package/src/dom/components/list.ts +2 -2
  199. package/src/dom/components/paragraph.ts +3 -4
  200. package/src/dom/components/progressbar.ts +3 -3
  201. package/src/dom/components/route.ts +2 -2
  202. package/src/dom/components/router.ts +2 -2
  203. package/src/dom/components/row.ts +10 -10
  204. package/src/dom/components/select.ts +3 -4
  205. package/src/dom/components/slider.ts +2 -2
  206. package/src/dom/components/spacer.ts +2 -2
  207. package/src/dom/components/spinner.ts +34 -16
  208. package/src/dom/components/stack.ts +10 -10
  209. package/src/dom/components/switch.ts +5 -6
  210. package/src/dom/components/tabs.ts +76 -0
  211. package/src/dom/components/text.ts +4 -7
  212. package/src/dom/components/textarea.ts +2 -2
  213. package/src/dom/components/video.ts +2 -2
  214. package/src/dom/components/visuallyhidden.ts +30 -0
  215. package/src/dom/debug.ts +6 -8
  216. package/src/dom/index.ts +0 -8
  217. package/src/dom/operability.ts +312 -0
  218. package/src/dom/renderer.ts +457 -66
  219. package/src/dom/route-focus.ts +98 -0
  220. package/src/dom/semantics.ts +199 -0
  221. package/src/variants.ts +249 -0
  222. package/dist/canvas/input.d.ts +0 -76
  223. package/dist/canvas/input.js +0 -207
  224. package/dist/canvas/input.js.map +0 -1
  225. package/dist/client.d.ts +0 -36
  226. package/dist/client.js +0 -47
  227. package/dist/client.js.map +0 -1
  228. package/src/canvas/input.ts +0 -251
  229. package/src/client.ts +0 -88
package/README.md CHANGED
@@ -127,21 +127,24 @@ await hypen.unmount();
127
127
 
128
128
  ## DOM Renderer (Low-Level)
129
129
 
130
- For custom integrations, use the `createHypenClient` helper to wire a renderer and patch subscription in one call. The bare `DOMRenderer` constructor is still exported for advanced cases.
130
+ For custom integrations:
131
131
 
132
132
  ```typescript
133
133
  import { app } from "@hypen-space/core";
134
134
  import { Engine } from "@hypen-space/web-engine";
135
- import { createHypenClient } from "@hypen-space/web/dom";
135
+ import { DOMRenderer } from "@hypen-space/web";
136
136
 
137
137
  const engine = new Engine();
138
138
  await engine.init({ wasmUrl: "/hypen_engine_bg.wasm" });
139
139
 
140
- const { renderer } = createHypenClient(
141
- document.getElementById("app")!,
142
- engine,
143
- { debug: { enabled: true, showHeatmap: true } },
144
- );
140
+ const renderer = new DOMRenderer(document.getElementById("app")!, engine, {
141
+ enabled: true,
142
+ showHeatmap: true,
143
+ });
144
+
145
+ engine.setRenderCallback((patches) => {
146
+ renderer.applyPatches(patches);
147
+ });
145
148
 
146
149
  engine.renderSource(`Column { Text("Hello!") }`);
147
150
  ```
@@ -237,17 +240,21 @@ Text("Hello")
237
240
 
238
241
  ## Canvas Renderer
239
242
 
240
- Hardware-accelerated rendering. `createHypenClient` is the recommended one-call wiring; `CanvasRenderer` remains exported for advanced use.
243
+ Hardware-accelerated rendering:
241
244
 
242
245
  ```typescript
243
- import { createHypenClient } from "@hypen-space/web/canvas";
246
+ import { CanvasRenderer } from "@hypen-space/web";
244
247
 
245
248
  const canvas = document.querySelector("canvas")!;
246
- const { renderer } = createHypenClient(canvas, engine, {
249
+ const renderer = new CanvasRenderer(canvas, engine, {
247
250
  devicePixelRatio: window.devicePixelRatio,
248
251
  enableAccessibility: true,
249
252
  enableHitTesting: true,
250
253
  });
254
+
255
+ engine.setRenderCallback((patches) => {
256
+ renderer.applyPatches(patches);
257
+ });
251
258
  ```
252
259
 
253
260
  ## Remote UI
@@ -256,15 +263,15 @@ Connect to a server-driven UI:
256
263
 
257
264
  ```typescript
258
265
  import { RemoteEngine } from "@hypen-space/core";
259
- import { createHypenClient } from "@hypen-space/web/dom";
266
+ import { DOMRenderer } from "@hypen-space/web";
260
267
 
268
+ const renderer = new DOMRenderer(document.getElementById("app")!, engine);
261
269
  const remote = new RemoteEngine("ws://localhost:3000", {
262
270
  autoReconnect: true,
263
271
  });
264
272
 
265
- const { renderer } = createHypenClient(document.getElementById("app")!, remote);
266
-
267
273
  remote
274
+ .onPatches((patches) => renderer.applyPatches(patches))
268
275
  .onStateUpdate((state) => renderer.updateState(state))
269
276
  .onConnect(() => console.log("Connected"));
270
277
 
@@ -3,27 +3,130 @@
3
3
  *
4
4
  * Maintain shadow DOM for screen readers
5
5
  */
6
+ import type { Semantics } from "@hypen-space/core/types";
6
7
  import type { VirtualNode } from "./types.js";
7
8
  /**
8
- * Accessibility Manager
9
+ * Apply engine-derived accessibility semantics to a shadow-tree element.
10
+ *
11
+ * Unlike the DOM renderer, the canvas paints pixels — the accessible name is
12
+ * *not* otherwise exposed — so the derived name is applied directly as
13
+ * `aria-label` (whether derived or explicit). Roles are set only on generic
14
+ * hosts that don't convey them natively. `hidden` is handled by the caller
15
+ * (the node is omitted from the shadow tree entirely).
16
+ *
17
+ * Called at shadow-node create and again on every reactive `setSemantics`
18
+ * re-apply. Idempotent and clearing: attributes set on a previous pass that
19
+ * the new block no longer produces are removed.
20
+ */
21
+ export declare function applyShadowSemantics(element: HTMLElement, semantics?: Semantics): void;
22
+ /**
23
+ * Accessibility Mirror — positioned transparent semantics overlay
24
+ *
25
+ * A live DOM mirror of the virtual tree, rendered INVISIBLY over the canvas
26
+ * with every element absolutely positioned at its painted bounds (the
27
+ * Flutter-web / Google-Docs approach). Canvas *fallback content* was tried
28
+ * first and rejected on evidence: Chromium exposes fallback elements to the
29
+ * accessibility tree (names, roles, focus all work) but gives them ZERO
30
+ * geometry — and screen-reader browse modes (VoiceOver cursor, rotor, touch
31
+ * exploration) are geometry-driven, so they skip boundless elements. A
32
+ * rendered-but-transparent overlay gets real boxes, which makes browse mode
33
+ * work and puts native focus rings exactly over the painted controls.
34
+ *
35
+ * Real DOM focus on mirror elements is the single source of truth for
36
+ * `node.focused`; the overlay is `pointer-events: none` throughout so the
37
+ * canvas keeps all pointer interaction.
38
+ *
39
+ * The mirror is synced **incrementally** from the same patch stream that
40
+ * drives the canvas (`create/insert/move/remove/detach/attach/...`), so
41
+ * mirror elements keep their identity across re-renders — a screen reader's
42
+ * virtual cursor or a keyboard user's focus survives reactive updates
43
+ * instead of being destroyed by a rebuild. Element positions are refreshed
44
+ * after each canvas render via {@link syncPositions}.
9
45
  */
10
46
  export declare class AccessibilityLayer {
11
- private shadowRoot;
47
+ private mirrorRoot;
12
48
  private nodeMap;
13
49
  private enabled;
14
- constructor(container: HTMLElement | null, enabled?: boolean);
50
+ /** Whether the environment can host a mirror at all. */
51
+ private supported;
52
+ private canvas;
53
+ /**
54
+ * Last box written to each mirror element, so the per-frame position sync
55
+ * only touches elements whose geometry actually changed — style writes on
56
+ * n absolutely-positioned elements per frame are what they cost.
57
+ */
58
+ private lastSyncedBounds;
59
+ private lastRootRect;
60
+ private boundReposition;
61
+ constructor(canvas: HTMLElement | null, enabled?: boolean);
62
+ private mount;
63
+ private unmount;
64
+ /** Pin the overlay root to the canvas's page rect. */
65
+ private repositionRoot;
66
+ /**
67
+ * Mirror a `create` patch: build the element (detached — a subsequent
68
+ * `insert` places it). Elements mirror the virtual tree 1:1; visibility
69
+ * and decorative hiding are expressed with `display:none` (which removes
70
+ * the subtree from the AT tree) instead of omission, so sibling order
71
+ * always matches the virtual tree and `beforeId` mapping stays exact.
72
+ *
73
+ * The mirror's accessibility metadata (role, name, busy, hidden) comes
74
+ * from the engine-derived {@link Semantics} block — the single source of
75
+ * truth shared with every other renderer — not from ad-hoc prop sniffing.
76
+ * Form *values* (input value/placeholder) still come from props, as those
77
+ * aren't semantics.
78
+ */
79
+ createNode(node: VirtualNode): void;
80
+ /**
81
+ * Mirror an `insert` or `attach` patch. Re-inserting an element that is
82
+ * already in the tree moves it (native `insertBefore` semantics), so
83
+ * `move` routes here too. An `attach` reinserts the exact element kept
84
+ * alive by {@link detachNode} — AT identity survives router navigation.
85
+ */
86
+ insertNode(parentId: string, id: string, beforeId?: string): void;
87
+ /**
88
+ * Mirror a `remove` patch: drop the element and purge the subtree's
89
+ * id mappings.
90
+ */
91
+ removeNode(node: VirtualNode): void;
92
+ private purge;
93
+ /**
94
+ * Mirror a `detach` patch: unlink the element but keep the subtree and
95
+ * all id mappings alive so a later `attach` reinserts the same elements
96
+ * (router subtree cache parity).
97
+ */
98
+ detachNode(id: string): void;
99
+ /**
100
+ * Full rebuild from a virtual tree. Only used when re-enabling the mirror
101
+ * (`setEnabled(true)`) and by `clear()` — steady-state sync is incremental.
102
+ */
103
+ rebuild(root: VirtualNode | null): void;
104
+ private mountSubtree;
105
+ /**
106
+ * `display:none` removes the subtree from the accessibility tree — used
107
+ * both for `visible: false` and for decorative (`semantics.hidden`) nodes.
108
+ */
109
+ private syncVisibility;
15
110
  /**
16
- * Sync shadow DOM with virtual tree
111
+ * Write each mirror element's box to its node's painted (scroll-aware)
112
+ * bounds — called by the renderer after every canvas render, so AT
113
+ * geometry follows layout changes and container scrolling. Positions are
114
+ * parent-relative (every mirror element is absolutely positioned).
17
115
  */
18
- syncTree(root: VirtualNode): void;
116
+ syncPositions(root: VirtualNode | null): void;
19
117
  /**
20
- * Create shadow DOM element for virtual node
118
+ * `scrollX`/`scrollY` accumulate the ancestors' scroll offsets down the
119
+ * recursion (the same subtraction `getScrollAwareBounds` derives by
120
+ * walking up), keeping the pass O(n) instead of O(n·depth). Writes are
121
+ * skipped when the element's box is unchanged since the last sync.
21
122
  */
22
- private createShadowNode;
123
+ private syncNodePosition;
23
124
  /**
24
- * Get text content from node tree
125
+ * Choose the semantic HTML tag for a node's shadow element and set its
126
+ * non-accessibility content (text, input value/placeholder). Roles/names are
127
+ * applied separately from the semantics block.
25
128
  */
26
- private getNodeText;
129
+ private createShadowElement;
27
130
  /**
28
131
  * Focus node in shadow DOM
29
132
  */
@@ -36,10 +139,14 @@ export declare class AccessibilityLayer {
36
139
  * Get shadow element by node ID
37
140
  */
38
141
  getElement(nodeId: string): HTMLElement | undefined;
142
+ /** The fallback-content root element (for focus delegation wiring). */
143
+ getRoot(): HTMLElement | null;
144
+ isEnabled(): boolean;
39
145
  /**
40
- * Enable or disable accessibility layer
146
+ * Enable or disable the mirror. Re-enabling rebuilds from the given
147
+ * virtual tree (incremental sync has no history to replay).
41
148
  */
42
- setEnabled(enabled: boolean): void;
149
+ setEnabled(enabled: boolean, root?: VirtualNode | null): void;
43
150
  /**
44
151
  * Cleanup
45
152
  */