@hypen-space/web 0.4.956 → 0.5.2
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.
- package/README.md +20 -13
- package/dist/canvas/events.js +46 -7
- package/dist/canvas/events.js.map +1 -1
- package/dist/canvas/index.d.ts +0 -1
- package/dist/canvas/index.js +0 -1
- package/dist/canvas/index.js.map +1 -1
- package/dist/canvas/layout.js +57 -44
- package/dist/canvas/layout.js.map +1 -1
- package/dist/canvas/paint.js +159 -60
- package/dist/canvas/paint.js.map +1 -1
- package/dist/canvas/renderer.d.ts +8 -3
- package/dist/canvas/renderer.js +32 -43
- package/dist/canvas/renderer.js.map +1 -1
- package/dist/canvas/types.d.ts +3 -0
- package/dist/canvas/utils.d.ts +0 -8
- package/dist/canvas/utils.js +3 -34
- package/dist/canvas/utils.js.map +1 -1
- package/dist/canvas/variants.d.ts +49 -0
- package/dist/canvas/variants.js +137 -0
- package/dist/canvas/variants.js.map +1 -0
- package/dist/dom/applicators/events.js +1 -0
- package/dist/dom/applicators/events.js.map +1 -1
- package/dist/dom/applicators/font.d.ts +11 -6
- package/dist/dom/applicators/font.js +16 -27
- package/dist/dom/applicators/font.js.map +1 -1
- package/dist/dom/applicators/index.js +47 -45
- package/dist/dom/applicators/index.js.map +1 -1
- package/dist/dom/canvas/index.js +2 -2
- package/dist/dom/canvas/index.js.map +1 -1
- package/dist/dom/components/app.js +2 -2
- package/dist/dom/components/app.js.map +1 -1
- package/dist/dom/components/audio.js +2 -2
- package/dist/dom/components/audio.js.map +1 -1
- package/dist/dom/components/avatar.js +3 -4
- package/dist/dom/components/avatar.js.map +1 -1
- package/dist/dom/components/badge.js +2 -2
- package/dist/dom/components/badge.js.map +1 -1
- package/dist/dom/components/button.js +2 -2
- package/dist/dom/components/button.js.map +1 -1
- package/dist/dom/components/card.js +2 -2
- package/dist/dom/components/card.js.map +1 -1
- package/dist/dom/components/center.js +2 -2
- package/dist/dom/components/center.js.map +1 -1
- package/dist/dom/components/checkbox.js +4 -5
- package/dist/dom/components/checkbox.js.map +1 -1
- package/dist/dom/components/column.js +2 -2
- package/dist/dom/components/column.js.map +1 -1
- package/dist/dom/components/container.js +2 -2
- package/dist/dom/components/container.js.map +1 -1
- package/dist/dom/components/divider.js +2 -2
- package/dist/dom/components/divider.js.map +1 -1
- package/dist/dom/components/grid.js +10 -10
- package/dist/dom/components/grid.js.map +1 -1
- package/dist/dom/components/heading.js +3 -4
- package/dist/dom/components/heading.js.map +1 -1
- package/dist/dom/components/hypenapp.js +2 -2
- package/dist/dom/components/hypenapp.js.map +1 -1
- package/dist/dom/components/icon.js +4 -5
- package/dist/dom/components/icon.js.map +1 -1
- package/dist/dom/components/image.js +2 -2
- package/dist/dom/components/image.js.map +1 -1
- package/dist/dom/components/index.d.ts +2 -2
- package/dist/dom/components/index.js +2 -2
- package/dist/dom/components/index.js.map +1 -1
- package/dist/dom/components/input.js +2 -2
- package/dist/dom/components/input.js.map +1 -1
- package/dist/dom/components/link.js +2 -2
- package/dist/dom/components/link.js.map +1 -1
- package/dist/dom/components/list.js +2 -2
- package/dist/dom/components/list.js.map +1 -1
- package/dist/dom/components/paragraph.js +2 -2
- package/dist/dom/components/paragraph.js.map +1 -1
- package/dist/dom/components/progressbar.js +3 -3
- package/dist/dom/components/progressbar.js.map +1 -1
- package/dist/dom/components/route.js +2 -2
- package/dist/dom/components/route.js.map +1 -1
- package/dist/dom/components/router.js +2 -2
- package/dist/dom/components/router.js.map +1 -1
- package/dist/dom/components/row.js +10 -10
- package/dist/dom/components/row.js.map +1 -1
- package/dist/dom/components/select.js +3 -4
- package/dist/dom/components/select.js.map +1 -1
- package/dist/dom/components/slider.js +2 -2
- package/dist/dom/components/slider.js.map +1 -1
- package/dist/dom/components/spacer.js +2 -2
- package/dist/dom/components/spacer.js.map +1 -1
- package/dist/dom/components/spinner.js +4 -4
- package/dist/dom/components/spinner.js.map +1 -1
- package/dist/dom/components/stack.js +10 -10
- package/dist/dom/components/stack.js.map +1 -1
- package/dist/dom/components/switch.js +5 -6
- package/dist/dom/components/switch.js.map +1 -1
- package/dist/dom/components/text.js +2 -2
- package/dist/dom/components/text.js.map +1 -1
- package/dist/dom/components/textarea.js +2 -2
- package/dist/dom/components/textarea.js.map +1 -1
- package/dist/dom/components/video.js +2 -2
- package/dist/dom/components/video.js.map +1 -1
- package/dist/dom/debug.js +6 -8
- package/dist/dom/debug.js.map +1 -1
- package/dist/dom/index.d.ts +0 -1
- package/dist/dom/index.js +0 -1
- package/dist/dom/index.js.map +1 -1
- package/dist/dom/renderer.js +2 -3
- package/dist/dom/renderer.js.map +1 -1
- package/dist/variants.d.ts +90 -0
- package/dist/variants.js +206 -0
- package/dist/variants.js.map +1 -0
- package/package.json +2 -2
- package/src/canvas/QUICKSTART.md +12 -7
- package/src/canvas/README.md +8 -5
- package/src/canvas/events.ts +49 -7
- package/src/canvas/index.ts +0 -8
- package/src/canvas/layout.ts +57 -49
- package/src/canvas/paint.ts +163 -58
- package/src/canvas/renderer.ts +38 -46
- package/src/canvas/types.ts +14 -0
- package/src/canvas/utils.ts +3 -37
- package/src/canvas/variants.ts +153 -0
- package/src/dom/README.md +8 -7
- package/src/dom/applicators/events.ts +1 -0
- package/src/dom/applicators/font.ts +16 -29
- package/src/dom/applicators/index.ts +56 -46
- package/src/dom/canvas/index.ts +2 -2
- package/src/dom/components/app.ts +2 -2
- package/src/dom/components/audio.ts +2 -2
- package/src/dom/components/avatar.ts +3 -4
- package/src/dom/components/badge.ts +2 -2
- package/src/dom/components/button.ts +2 -2
- package/src/dom/components/card.ts +2 -2
- package/src/dom/components/center.ts +2 -2
- package/src/dom/components/checkbox.ts +4 -5
- package/src/dom/components/column.ts +2 -2
- package/src/dom/components/container.ts +2 -2
- package/src/dom/components/divider.ts +2 -2
- package/src/dom/components/grid.ts +10 -10
- package/src/dom/components/heading.ts +3 -4
- package/src/dom/components/hypenapp.ts +2 -2
- package/src/dom/components/icon.ts +4 -5
- package/src/dom/components/image.ts +2 -2
- package/src/dom/components/index.ts +3 -3
- package/src/dom/components/input.ts +2 -2
- package/src/dom/components/link.ts +2 -2
- package/src/dom/components/list.ts +2 -2
- package/src/dom/components/paragraph.ts +2 -2
- package/src/dom/components/progressbar.ts +3 -3
- package/src/dom/components/route.ts +2 -2
- package/src/dom/components/router.ts +2 -2
- package/src/dom/components/row.ts +10 -10
- package/src/dom/components/select.ts +3 -4
- package/src/dom/components/slider.ts +2 -2
- package/src/dom/components/spacer.ts +2 -2
- package/src/dom/components/spinner.ts +4 -4
- package/src/dom/components/stack.ts +10 -10
- package/src/dom/components/switch.ts +5 -6
- package/src/dom/components/text.ts +2 -2
- package/src/dom/components/textarea.ts +2 -2
- package/src/dom/components/video.ts +2 -2
- package/src/dom/debug.ts +6 -8
- package/src/dom/index.ts +0 -8
- package/src/dom/renderer.ts +2 -3
- package/src/variants.ts +249 -0
- package/dist/client.d.ts +0 -36
- package/dist/client.js +0 -47
- package/dist/client.js.map +0 -1
- package/src/client.ts +0 -88
package/dist/variants.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared variant (responsive breakpoint + interaction state) support.
|
|
3
|
+
*
|
|
4
|
+
* The engine emits applicator props using the canonical key format:
|
|
5
|
+
*
|
|
6
|
+
* <camelBase><variant?><argSuffix>
|
|
7
|
+
*
|
|
8
|
+
* - camelBase: camelCase applicator name, e.g. "padding", "backgroundColor"
|
|
9
|
+
* - variant?: "@<bp>" and/or ":<state>", in that order if combined.
|
|
10
|
+
* bp in { sm, md, lg, xl, 2xl }
|
|
11
|
+
* state in { hover, focus, active, disabled,
|
|
12
|
+
* focus-visible, focus-within }
|
|
13
|
+
* combined example: "backgroundColor@md:hover" (BOTH md AND hover)
|
|
14
|
+
* - argSuffix: ".<index>" (almost always ".0") or ".<name>" for named args.
|
|
15
|
+
*
|
|
16
|
+
* Examples emitted by the engine:
|
|
17
|
+
* "padding.0", "padding@md.0", "backgroundColor:hover.0",
|
|
18
|
+
* "backgroundColor@md:hover.0".
|
|
19
|
+
*
|
|
20
|
+
* IMPORTANT: the variant marker sits BETWEEN the base name and the ".0" arg
|
|
21
|
+
* suffix. A lookup that forgets the ".0" (e.g. building just "padding@md")
|
|
22
|
+
* MISSES the real key. Always parse the variant out of the full key.
|
|
23
|
+
*
|
|
24
|
+
* Both the DOM applicator path and the Canvas renderer import this module so
|
|
25
|
+
* breakpoint pixels, state names, and precedence stay byte-compatible across
|
|
26
|
+
* renderers. The DOM renderer leans on the CSS engine for actual resolution
|
|
27
|
+
* (media queries + pseudo-classes), so it only needs PARSE_* here; Canvas does
|
|
28
|
+
* its own paint/layout and therefore also uses the runtime resolver.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Tailwind breakpoint min-widths (px). Order matters: ascending min-width.
|
|
32
|
+
* Mirrors the DOM `BREAKPOINTS` table and the iOS reference.
|
|
33
|
+
*/
|
|
34
|
+
export const BREAKPOINTS = {
|
|
35
|
+
sm: 640,
|
|
36
|
+
md: 768,
|
|
37
|
+
lg: 1024,
|
|
38
|
+
xl: 1280,
|
|
39
|
+
"2xl": 1536,
|
|
40
|
+
};
|
|
41
|
+
/** Breakpoint names in ascending min-width order (sm < md < lg < xl < 2xl). */
|
|
42
|
+
export const BREAKPOINT_ORDER = ["sm", "md", "lg", "xl", "2xl"];
|
|
43
|
+
/** Interaction-state names recognised in variant keys (same set as the DOM). */
|
|
44
|
+
export const VALID_STATES = [
|
|
45
|
+
"hover",
|
|
46
|
+
"focus",
|
|
47
|
+
"active",
|
|
48
|
+
"disabled",
|
|
49
|
+
"focus-visible",
|
|
50
|
+
"focus-within",
|
|
51
|
+
];
|
|
52
|
+
const VALID_STATE_SET = new Set(VALID_STATES);
|
|
53
|
+
/**
|
|
54
|
+
* Parse a prop key into `{ base, breakpoint, state, arg }`.
|
|
55
|
+
*
|
|
56
|
+
* Splits the arg suffix first (the LAST `.<token>`), then peels the variant
|
|
57
|
+
* markers (`@bp` and/or `:state`) off the remainder. An unknown breakpoint or
|
|
58
|
+
* state is left as part of the base name (caller decides what to do); this
|
|
59
|
+
* matches the DOM behaviour where an invalid `@invalid` / `:invalid` simply
|
|
60
|
+
* never matches a real variant.
|
|
61
|
+
*
|
|
62
|
+
* Examples:
|
|
63
|
+
* "padding.0" -> { base:"padding", breakpoint:null, state:null, arg:"0" }
|
|
64
|
+
* "padding@md.0" -> { base:"padding", breakpoint:"md", state:null, arg:"0" }
|
|
65
|
+
* "backgroundColor:hover.0" -> { base:"backgroundColor", breakpoint:null, state:"hover", arg:"0" }
|
|
66
|
+
* "backgroundColor@md:hover.0" -> { base:"backgroundColor", breakpoint:"md", state:"hover", arg:"0" }
|
|
67
|
+
* "backgroundColor@md:hover" -> { base:"backgroundColor", breakpoint:"md", state:"hover", arg:null }
|
|
68
|
+
*/
|
|
69
|
+
export function parseVariantKey(key) {
|
|
70
|
+
let work = key;
|
|
71
|
+
let arg = null;
|
|
72
|
+
// Peel the arg suffix: the segment after the LAST dot, but only if that dot
|
|
73
|
+
// comes AFTER any variant markers (so we never split inside "focus-within"
|
|
74
|
+
// — that has no dot — and never treat "padding" alone as having an arg).
|
|
75
|
+
const lastDot = work.lastIndexOf(".");
|
|
76
|
+
if (lastDot > 0) {
|
|
77
|
+
arg = work.slice(lastDot + 1);
|
|
78
|
+
work = work.slice(0, lastDot);
|
|
79
|
+
}
|
|
80
|
+
// Peel the state marker (":state") off the end of the remainder.
|
|
81
|
+
let state = null;
|
|
82
|
+
const colon = work.indexOf(":");
|
|
83
|
+
if (colon !== -1) {
|
|
84
|
+
const candidate = work.slice(colon + 1);
|
|
85
|
+
if (VALID_STATE_SET.has(candidate)) {
|
|
86
|
+
state = candidate;
|
|
87
|
+
work = work.slice(0, colon);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Peel the breakpoint marker ("@bp").
|
|
91
|
+
let breakpoint = null;
|
|
92
|
+
const at = work.indexOf("@");
|
|
93
|
+
if (at !== -1) {
|
|
94
|
+
const candidate = work.slice(at + 1);
|
|
95
|
+
if (candidate in BREAKPOINTS) {
|
|
96
|
+
breakpoint = candidate;
|
|
97
|
+
work = work.slice(0, at);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { base: work, breakpoint, state, arg };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Precedence rank for a parsed variant (higher wins). Combines the breakpoint
|
|
104
|
+
* tier and the state tier into a single sortable number.
|
|
105
|
+
*
|
|
106
|
+
* Precedence (low -> high), later overrides earlier:
|
|
107
|
+
* base
|
|
108
|
+
* < breakpoints ascending (sm < md < lg < xl < 2xl)
|
|
109
|
+
* < disabled < hover < focus < active
|
|
110
|
+
*
|
|
111
|
+
* State always outranks any breakpoint (a `:hover` override beats a `@2xl`
|
|
112
|
+
* sizing override on the same prop), matching the iOS reference where state
|
|
113
|
+
* overrides are merged AFTER the responsive base. Within the same state tier,
|
|
114
|
+
* a higher breakpoint still breaks the tie so `@md:hover` beats `:hover`.
|
|
115
|
+
*/
|
|
116
|
+
const STATE_RANK = {
|
|
117
|
+
disabled: 1,
|
|
118
|
+
hover: 2,
|
|
119
|
+
// focus-visible / focus-within are focus-flavoured states: they share the
|
|
120
|
+
// `focus` precedence slot (NOT base/0) so they outrank hover and lose to
|
|
121
|
+
// active, matching the engine's `state_rank` in portable/variant.rs.
|
|
122
|
+
focus: 3,
|
|
123
|
+
"focus-visible": 3,
|
|
124
|
+
"focus-within": 3,
|
|
125
|
+
active: 4,
|
|
126
|
+
};
|
|
127
|
+
// State tier must be weighted by a value strictly greater than the largest
|
|
128
|
+
// possible breakpoint tier so the bp tiebreak can never leak across state
|
|
129
|
+
// bands (mirrors the engine's STATE_STEP > max-breakpoint invariant). Derived
|
|
130
|
+
// from BREAKPOINT_ORDER.length so adding breakpoints can't silently break it.
|
|
131
|
+
const STATE_WEIGHT = BREAKPOINT_ORDER.length + 1;
|
|
132
|
+
function variantRank(breakpoint, state) {
|
|
133
|
+
const bpTier = breakpoint ? BREAKPOINT_ORDER.indexOf(breakpoint) + 1 : 0; // 0..len
|
|
134
|
+
const stateTier = state ? STATE_RANK[state] ?? 0 : 0; // 0..4
|
|
135
|
+
// State tier dominates; breakpoint tier breaks ties within a state tier.
|
|
136
|
+
return stateTier * STATE_WEIGHT + bpTier;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Is a parsed variant currently applicable, given the viewport width and the
|
|
140
|
+
* active interaction states? A combined key requires BOTH conditions.
|
|
141
|
+
*/
|
|
142
|
+
function variantApplies(parsed, width, states) {
|
|
143
|
+
if (parsed.breakpoint) {
|
|
144
|
+
const minWidth = BREAKPOINTS[parsed.breakpoint];
|
|
145
|
+
if (minWidth === undefined || width < minWidth)
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (parsed.state) {
|
|
149
|
+
if (!states[parsed.state])
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Resolve variant-bearing props against the current viewport width and active
|
|
156
|
+
* interaction states, producing a flat map of `base -> winning value`.
|
|
157
|
+
*
|
|
158
|
+
* Input keys are the already-normalised flat applicator keys (variant markers
|
|
159
|
+
* present, arg suffix already stripped by the applicator-grouping pass), e.g.
|
|
160
|
+
* `{ padding: 8, "padding@md": 16, "backgroundColor:hover": "red" }`.
|
|
161
|
+
*
|
|
162
|
+
* Only bases that actually have at least one applicable variant appear in the
|
|
163
|
+
* result with an overridden value; a base with only its plain entry resolves to
|
|
164
|
+
* that plain value. The caller merges the result over the node's base props.
|
|
165
|
+
*/
|
|
166
|
+
export function resolveVariantProps(props, width, states) {
|
|
167
|
+
// base -> { value, rank }
|
|
168
|
+
const winners = new Map();
|
|
169
|
+
for (const key of Object.keys(props)) {
|
|
170
|
+
const parsed = parseVariantKey(key);
|
|
171
|
+
// Only consider entries that carry a variant marker. Plain keys (no bp/state)
|
|
172
|
+
// are the base and are handled implicitly via rank 0 below so a base with a
|
|
173
|
+
// single applicable variant still falls back to base when the variant is off.
|
|
174
|
+
if (!parsed.breakpoint && !parsed.state) {
|
|
175
|
+
// Seed the base value at rank 0 so variant lookups have a fallback and so
|
|
176
|
+
// a base that ALSO has variants resolves correctly when none apply.
|
|
177
|
+
const existing = winners.get(parsed.base);
|
|
178
|
+
if (!existing || existing.rank === 0) {
|
|
179
|
+
winners.set(parsed.base, { value: props[key], rank: 0 });
|
|
180
|
+
}
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (!variantApplies(parsed, width, states))
|
|
184
|
+
continue;
|
|
185
|
+
const rank = variantRank(parsed.breakpoint, parsed.state);
|
|
186
|
+
const existing = winners.get(parsed.base);
|
|
187
|
+
if (!existing || rank >= existing.rank) {
|
|
188
|
+
winners.set(parsed.base, { value: props[key], rank });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const out = {};
|
|
192
|
+
for (const [base, win] of winners) {
|
|
193
|
+
out[base] = win.value;
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/** True if any key in `props` carries a variant marker (`@bp` or `:state`). */
|
|
198
|
+
export function hasVariantProps(props) {
|
|
199
|
+
for (const key of Object.keys(props)) {
|
|
200
|
+
const parsed = parseVariantKey(key);
|
|
201
|
+
if (parsed.breakpoint || parsed.state)
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=variants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variants.js","sourceRoot":"","sources":["../src/variants.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypen-space/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Hypen web renderers - DOM and Canvas rendering for browsers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@chenglou/pretext": "^0.0.3",
|
|
46
|
-
"@hypen-space/core": "^0.
|
|
46
|
+
"@hypen-space/core": "^0.5.2",
|
|
47
47
|
"taffy-layout": "^2.0.3"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
package/src/canvas/QUICKSTART.md
CHANGED
|
@@ -30,7 +30,7 @@ bun add @hypen-space/core @hypen-space/web @hypen-space/web-engine
|
|
|
30
30
|
```typescript
|
|
31
31
|
import { app, HypenModuleInstance } from "@hypen-space/core";
|
|
32
32
|
import { Engine } from "@hypen-space/web-engine";
|
|
33
|
-
import {
|
|
33
|
+
import { CanvasRenderer } from "@hypen-space/web/canvas";
|
|
34
34
|
|
|
35
35
|
// Define state
|
|
36
36
|
type AppState = {
|
|
@@ -54,12 +54,17 @@ async function main() {
|
|
|
54
54
|
const engine = new Engine();
|
|
55
55
|
await engine.init();
|
|
56
56
|
|
|
57
|
-
// Create renderer
|
|
58
|
-
|
|
57
|
+
// Create renderer
|
|
58
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
59
59
|
devicePixelRatio: window.devicePixelRatio,
|
|
60
60
|
backgroundColor: "#ffffff",
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
// Connect renderer
|
|
64
|
+
engine.setRenderCallback((patches) => {
|
|
65
|
+
renderer.applyPatches(patches);
|
|
66
|
+
});
|
|
67
|
+
|
|
63
68
|
// Create module
|
|
64
69
|
const instance = new HypenModuleInstance(engine, appModule);
|
|
65
70
|
|
|
@@ -244,7 +249,7 @@ Container {
|
|
|
244
249
|
## Configuration Options
|
|
245
250
|
|
|
246
251
|
```typescript
|
|
247
|
-
const
|
|
252
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
248
253
|
// Display
|
|
249
254
|
devicePixelRatio: window.devicePixelRatio, // HiDPI support
|
|
250
255
|
backgroundColor: "#ffffff", // Canvas background
|
|
@@ -349,7 +354,7 @@ let timeoutId: number;
|
|
|
349
354
|
### 3. Measure Performance
|
|
350
355
|
|
|
351
356
|
```typescript
|
|
352
|
-
const
|
|
357
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
353
358
|
logPerformance: true, // Logs FPS and frame times
|
|
354
359
|
});
|
|
355
360
|
```
|
|
@@ -361,7 +366,7 @@ const { renderer } = createHypenClient(canvas, engine, {
|
|
|
361
366
|
Make sure to set `devicePixelRatio`:
|
|
362
367
|
|
|
363
368
|
```typescript
|
|
364
|
-
const
|
|
369
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
365
370
|
devicePixelRatio: window.devicePixelRatio,
|
|
366
371
|
});
|
|
367
372
|
```
|
|
@@ -371,7 +376,7 @@ const { renderer } = createHypenClient(canvas, engine, {
|
|
|
371
376
|
Ensure `enableInputOverlay` is true:
|
|
372
377
|
|
|
373
378
|
```typescript
|
|
374
|
-
const
|
|
379
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
375
380
|
enableInputOverlay: true,
|
|
376
381
|
});
|
|
377
382
|
```
|
package/src/canvas/README.md
CHANGED
|
@@ -13,12 +13,10 @@ The Canvas Renderer is a complete alternative to the DOM renderer that draws all
|
|
|
13
13
|
|
|
14
14
|
## Quick Start
|
|
15
15
|
|
|
16
|
-
`createHypenClient` is the recommended one-call wiring (constructs the renderer and subscribes to engine patches). `CanvasRenderer` is still exported directly for advanced setups.
|
|
17
|
-
|
|
18
16
|
```typescript
|
|
19
17
|
import { app } from "@hypen-space/core";
|
|
20
18
|
import { Engine } from "@hypen-space/web-engine";
|
|
21
|
-
import {
|
|
19
|
+
import { CanvasRenderer } from "@hypen-space/web/canvas";
|
|
22
20
|
|
|
23
21
|
// Setup canvas
|
|
24
22
|
const canvas = document.getElementById("app") as HTMLCanvasElement;
|
|
@@ -27,8 +25,13 @@ const canvas = document.getElementById("app") as HTMLCanvasElement;
|
|
|
27
25
|
const engine = new Engine();
|
|
28
26
|
await engine.init();
|
|
29
27
|
|
|
30
|
-
// Create canvas renderer
|
|
31
|
-
|
|
28
|
+
// Create canvas renderer
|
|
29
|
+
const renderer = new CanvasRenderer(canvas, engine);
|
|
30
|
+
|
|
31
|
+
// Set render callback
|
|
32
|
+
engine.setRenderCallback((patches) => {
|
|
33
|
+
renderer.applyPatches(patches);
|
|
34
|
+
});
|
|
32
35
|
|
|
33
36
|
// Render UI
|
|
34
37
|
await engine.renderSource(`
|
package/src/canvas/events.ts
CHANGED
|
@@ -14,6 +14,21 @@ interface IEngine {
|
|
|
14
14
|
dispatchAction(name: string, payload?: any): void;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// Maps a DOM event type to the applicator prop names the engine may have set
|
|
18
|
+
// on a node, in priority order. Multi-word events (`mouseenter`) need their
|
|
19
|
+
// proper camelCase form (`onMouseEnter`) since the engine emits applicator
|
|
20
|
+
// names verbatim. `mouseenter` also accepts `onHover` as an alias.
|
|
21
|
+
const CANVAS_EVENT_PROP_NAMES: Record<string, string[]> = {
|
|
22
|
+
mouseenter: ["onMouseEnter", "onHover", "onmouseenter", "mouseenter"],
|
|
23
|
+
mouseleave: ["onMouseLeave", "onmouseleave", "mouseleave"],
|
|
24
|
+
mousedown: ["onMouseDown", "onmousedown", "mousedown"],
|
|
25
|
+
mouseup: ["onMouseUp", "onmouseup", "mouseup"],
|
|
26
|
+
dblclick: ["onDblClick", "onDoubleClick", "ondblclick", "dblclick"],
|
|
27
|
+
contextmenu: ["onContextMenu", "oncontextmenu", "contextmenu"],
|
|
28
|
+
keydown: ["onKeyDown", "onkeydown", "keydown"],
|
|
29
|
+
keyup: ["onKeyUp", "onkeyup", "keyup"],
|
|
30
|
+
};
|
|
31
|
+
|
|
17
32
|
/**
|
|
18
33
|
* Canvas Event Manager
|
|
19
34
|
*/
|
|
@@ -210,6 +225,13 @@ export class CanvasEventManager {
|
|
|
210
225
|
|
|
211
226
|
this.mouseDownNode = node;
|
|
212
227
|
|
|
228
|
+
// Track pressed (`:active`) state so paint-time `:active` variants resolve.
|
|
229
|
+
// Repaint so the active style appears immediately on press.
|
|
230
|
+
if (node) {
|
|
231
|
+
node.pressed = true;
|
|
232
|
+
this.requestRedraw();
|
|
233
|
+
}
|
|
234
|
+
|
|
213
235
|
if (node && node.clickable) {
|
|
214
236
|
this.dispatchNodeEvent(node, "mousedown", {
|
|
215
237
|
button: e.button,
|
|
@@ -234,6 +256,13 @@ export class CanvasEventManager {
|
|
|
234
256
|
const hit = this.hitTest(point);
|
|
235
257
|
const node = this.findClickableAncestor(hit) ?? hit;
|
|
236
258
|
|
|
259
|
+
// Clear the pressed (`:active`) flag from the node that was pressed —
|
|
260
|
+
// release ends `:active` even if the pointer drifted off the node first.
|
|
261
|
+
if (this.mouseDownNode && this.mouseDownNode.pressed) {
|
|
262
|
+
this.mouseDownNode.pressed = false;
|
|
263
|
+
this.requestRedraw();
|
|
264
|
+
}
|
|
265
|
+
|
|
237
266
|
if (node && node.clickable) {
|
|
238
267
|
this.dispatchNodeEvent(node, "mouseup", {
|
|
239
268
|
button: e.button,
|
|
@@ -383,13 +412,25 @@ export class CanvasEventManager {
|
|
|
383
412
|
* Dispatch event to engine
|
|
384
413
|
*/
|
|
385
414
|
private dispatchNodeEvent(node: VirtualNode, eventType: string, data: any): void {
|
|
386
|
-
// Engine emits event applicators in camelCase (`onClick`, `
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
415
|
+
// Engine emits event applicators in camelCase (`onClick`, `onMouseEnter`).
|
|
416
|
+
// Multi-word DOM events like `mouseenter` must map to `onMouseEnter`, not
|
|
417
|
+
// the naive `onMouseenter` that `on${capitalize(eventType)}` would produce.
|
|
418
|
+
// The older flat form `onclick`/`onmouseenter` is still accepted. After
|
|
419
|
+
// prop normalisation the value is either a string (action name) or an
|
|
420
|
+
// object carrying an action name at `"0"` plus an auxiliary payload.
|
|
421
|
+
const propNames = CANVAS_EVENT_PROP_NAMES[eventType] ?? [
|
|
422
|
+
`on${eventType.charAt(0).toUpperCase()}${eventType.slice(1)}`,
|
|
423
|
+
`on${eventType}`,
|
|
424
|
+
eventType,
|
|
425
|
+
];
|
|
426
|
+
|
|
427
|
+
let spec: unknown;
|
|
428
|
+
for (const name of propNames) {
|
|
429
|
+
if (node.props[name] != null) {
|
|
430
|
+
spec = node.props[name];
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
393
434
|
|
|
394
435
|
// Actionable components fall back to the bare `action` prop on click.
|
|
395
436
|
if (spec == null && eventType === "click") {
|
|
@@ -429,6 +470,7 @@ export class CanvasEventManager {
|
|
|
429
470
|
this.canvas.removeEventListener("contextmenu", this.boundOnContextMenu);
|
|
430
471
|
this.canvas.removeEventListener("keydown", this.boundOnKeyDown);
|
|
431
472
|
this.canvas.removeEventListener("keyup", this.boundOnKeyUp);
|
|
473
|
+
if (this.mouseDownNode) this.mouseDownNode.pressed = false;
|
|
432
474
|
this.rootNode = null;
|
|
433
475
|
this.hoveredNode = null;
|
|
434
476
|
this.focusedNode = null;
|
package/src/canvas/index.ts
CHANGED
|
@@ -5,14 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export { CanvasRenderer } from "./renderer.js";
|
|
8
|
-
export {
|
|
9
|
-
createCanvasClient as createHypenClient,
|
|
10
|
-
createCanvasClient,
|
|
11
|
-
type CanvasClient as HypenClient,
|
|
12
|
-
type CanvasClient,
|
|
13
|
-
type CanvasClientOptions as HypenClientOptions,
|
|
14
|
-
type CanvasClientOptions,
|
|
15
|
-
} from "../client.js";
|
|
16
8
|
export { registerPainter } from "./paint.js";
|
|
17
9
|
export { CanvasEventManager } from "./events.js";
|
|
18
10
|
export { InputOverlay } from "./input.js";
|
package/src/canvas/layout.ts
CHANGED
|
@@ -7,14 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { VirtualNode, Layout, BoxSpacing } from "./types.js";
|
|
10
|
-
import {
|
|
11
|
-
parseSpacing,
|
|
12
|
-
parseSize,
|
|
13
|
-
cssLengthToPx,
|
|
14
|
-
cssLengthToDimension,
|
|
15
|
-
setCurrentViewport,
|
|
16
|
-
getCurrentViewport,
|
|
17
|
-
} from "./utils.js";
|
|
10
|
+
import { parseSpacing, parseSize, cssLengthToPx, cssLengthToDimension } from "./utils.js";
|
|
18
11
|
import { measureText } from "./text.js";
|
|
19
12
|
import { getImageNaturalAspect } from "./paint.js";
|
|
20
13
|
|
|
@@ -54,6 +47,19 @@ export async function initTaffyLayout(): Promise<boolean> {
|
|
|
54
47
|
taffyInitPromise = (async () => {
|
|
55
48
|
try {
|
|
56
49
|
taffy = await import("taffy-layout");
|
|
50
|
+
// In a server runtime (Bun/Node — including test runs where a JSDOM
|
|
51
|
+
// `window` may be globally registered by another test file), load the
|
|
52
|
+
// WASM from node_modules via fs. This path is deterministic; the
|
|
53
|
+
// fetch-based branch below depends on a reachable origin/CDN and must
|
|
54
|
+
// never be selected just because a test polyfilled `window`.
|
|
55
|
+
const isServerRuntime =
|
|
56
|
+
typeof process !== "undefined" &&
|
|
57
|
+
!!(process.versions?.bun || process.versions?.node);
|
|
58
|
+
if (isServerRuntime) {
|
|
59
|
+
await taffy.loadTaffy();
|
|
60
|
+
taffyReady = true;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
57
63
|
// Bundlers (esp. Bun's browser bundler) sometimes inline a file:// URL
|
|
58
64
|
// for import.meta.url inside taffy_wasm.js, which the browser blocks
|
|
59
65
|
// ("Not allowed to load local resource"). The package's own `loadTaffy`
|
|
@@ -61,26 +67,40 @@ export async function initTaffyLayout(): Promise<boolean> {
|
|
|
61
67
|
// raw `__wbg_init` (default export of the `taffy-layout/wasm` subpath)
|
|
62
68
|
// directly with an explicit Response.
|
|
63
69
|
let usedExplicit = false;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
70
|
+
// Only take the fetch path in a real browser. Test environments (and
|
|
71
|
+
// anything else that installs a fake `window` global) must fall through
|
|
72
|
+
// to `loadTaffy()`, which resolves the WASM from the package on disk —
|
|
73
|
+
// a fake window without `location.origin` used to throw here and
|
|
74
|
+
// silently disable Taffy for the rest of the process.
|
|
75
|
+
const isRealBrowser =
|
|
76
|
+
typeof window !== "undefined" &&
|
|
77
|
+
typeof fetch === "function" &&
|
|
78
|
+
typeof window.location?.origin === "string" &&
|
|
79
|
+
window.document?.defaultView === window;
|
|
80
|
+
if (isRealBrowser) {
|
|
81
|
+
try {
|
|
82
|
+
const candidates = [
|
|
83
|
+
new URL("/taffy_wasm_bg.wasm", window.location.origin),
|
|
84
|
+
new URL("https://cdn.jsdelivr.net/npm/taffy-layout@2.0.3/pkg/taffy_wasm_bg.wasm"),
|
|
85
|
+
new URL("https://unpkg.com/taffy-layout@2.0.3/pkg/taffy_wasm_bg.wasm"),
|
|
86
|
+
];
|
|
87
|
+
const rawWasm = await import(
|
|
88
|
+
/* @vite-ignore */ "taffy-layout/wasm" as string
|
|
89
|
+
);
|
|
90
|
+
const wbgInit = rawWasm.default as (input?: any) => Promise<unknown>;
|
|
91
|
+
for (const url of candidates) {
|
|
92
|
+
try {
|
|
93
|
+
const res = await fetch(url);
|
|
94
|
+
if (!res.ok) continue;
|
|
95
|
+
await wbgInit({ module_or_path: res });
|
|
96
|
+
usedExplicit = true;
|
|
97
|
+
break;
|
|
98
|
+
} catch {
|
|
99
|
+
// Try next candidate
|
|
100
|
+
}
|
|
83
101
|
}
|
|
102
|
+
} catch {
|
|
103
|
+
// Fall through to loadTaffy()
|
|
84
104
|
}
|
|
85
105
|
}
|
|
86
106
|
if (!usedExplicit) {
|
|
@@ -1088,30 +1108,18 @@ export function computeLayout(
|
|
|
1088
1108
|
x: number = 0,
|
|
1089
1109
|
y: number = 0,
|
|
1090
1110
|
): void {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
try {
|
|
1100
|
-
if (taffyReady && taffy) {
|
|
1111
|
+
if (taffyReady && taffy) {
|
|
1112
|
+
computeLayoutTaffy(ctx, node, availableWidth, availableHeight, x, y);
|
|
1113
|
+
// After the first pass, Taffy's grid auto-row sizing for aspect-ratio
|
|
1114
|
+
// image leaves can collapse rows (the leaf's max-content height is 0
|
|
1115
|
+
// before column widths are known). Detect that pattern and re-run
|
|
1116
|
+
// with explicit `gridAutoRows` derived from the now-known column
|
|
1117
|
+
// width — see `annotateCollapsedAspectGrids`.
|
|
1118
|
+
if (annotateCollapsedAspectGrids(node)) {
|
|
1101
1119
|
computeLayoutTaffy(ctx, node, availableWidth, availableHeight, x, y);
|
|
1102
|
-
// After the first pass, Taffy's grid auto-row sizing for aspect-ratio
|
|
1103
|
-
// image leaves can collapse rows (the leaf's max-content height is 0
|
|
1104
|
-
// before column widths are known). Detect that pattern and re-run
|
|
1105
|
-
// with explicit `gridAutoRows` derived from the now-known column
|
|
1106
|
-
// width — see `annotateCollapsedAspectGrids`.
|
|
1107
|
-
if (annotateCollapsedAspectGrids(node)) {
|
|
1108
|
-
computeLayoutTaffy(ctx, node, availableWidth, availableHeight, x, y);
|
|
1109
|
-
}
|
|
1110
|
-
} else {
|
|
1111
|
-
computeLayoutFallback(ctx, node, availableWidth, availableHeight, x, y);
|
|
1112
1120
|
}
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1121
|
+
} else {
|
|
1122
|
+
computeLayoutFallback(ctx, node, availableWidth, availableHeight, x, y);
|
|
1115
1123
|
}
|
|
1116
1124
|
}
|
|
1117
1125
|
|