@geometra/core 1.0.0 → 1.0.1

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 (72) hide show
  1. package/README.md +10 -1
  2. package/dist/a11y.d.ts.map +1 -1
  3. package/dist/a11y.js +6 -2
  4. package/dist/a11y.js.map +1 -1
  5. package/dist/animation.d.ts +39 -1
  6. package/dist/animation.d.ts.map +1 -1
  7. package/dist/animation.js +148 -1
  8. package/dist/animation.js.map +1 -1
  9. package/dist/app.d.ts +4 -1
  10. package/dist/app.d.ts.map +1 -1
  11. package/dist/app.js +17 -3
  12. package/dist/app.js.map +1 -1
  13. package/dist/direction.d.ts +11 -0
  14. package/dist/direction.d.ts.map +1 -0
  15. package/dist/direction.js +18 -0
  16. package/dist/direction.js.map +1 -0
  17. package/dist/elements.d.ts +4 -4
  18. package/dist/elements.d.ts.map +1 -1
  19. package/dist/elements.js.map +1 -1
  20. package/dist/focus-trap.d.ts +12 -1
  21. package/dist/focus-trap.d.ts.map +1 -1
  22. package/dist/focus-trap.js +12 -1
  23. package/dist/focus-trap.js.map +1 -1
  24. package/dist/focus.d.ts +3 -2
  25. package/dist/focus.d.ts.map +1 -1
  26. package/dist/focus.js +6 -0
  27. package/dist/focus.js.map +1 -1
  28. package/dist/fonts.d.ts +35 -2
  29. package/dist/fonts.d.ts.map +1 -1
  30. package/dist/fonts.js +335 -12
  31. package/dist/fonts.js.map +1 -1
  32. package/dist/hit-test.d.ts +35 -4
  33. package/dist/hit-test.d.ts.map +1 -1
  34. package/dist/hit-test.js +195 -70
  35. package/dist/hit-test.js.map +1 -1
  36. package/dist/index.d.ts +21 -8
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +17 -6
  39. package/dist/index.js.map +1 -1
  40. package/dist/keyboard.d.ts +5 -2
  41. package/dist/keyboard.d.ts.map +1 -1
  42. package/dist/keyboard.js +5 -2
  43. package/dist/keyboard.js.map +1 -1
  44. package/dist/selection.d.ts +5 -2
  45. package/dist/selection.d.ts.map +1 -1
  46. package/dist/selection.js +37 -8
  47. package/dist/selection.js.map +1 -1
  48. package/dist/seo.d.ts +27 -0
  49. package/dist/seo.d.ts.map +1 -1
  50. package/dist/seo.js +240 -13
  51. package/dist/seo.js.map +1 -1
  52. package/dist/signals.d.ts.map +1 -1
  53. package/dist/signals.js +44 -7
  54. package/dist/signals.js.map +1 -1
  55. package/dist/text-input-history.d.ts +14 -4
  56. package/dist/text-input-history.d.ts.map +1 -1
  57. package/dist/text-input-history.js +37 -8
  58. package/dist/text-input-history.js.map +1 -1
  59. package/dist/text-input.d.ts +8 -8
  60. package/dist/text-input.d.ts.map +1 -1
  61. package/dist/text-input.js +45 -17
  62. package/dist/text-input.js.map +1 -1
  63. package/dist/tree.d.ts.map +1 -1
  64. package/dist/tree.js +3 -0
  65. package/dist/tree.js.map +1 -1
  66. package/dist/types.d.ts +29 -5
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/virtual-scroll.d.ts +11 -1
  69. package/dist/virtual-scroll.d.ts.map +1 -1
  70. package/dist/virtual-scroll.js +14 -5
  71. package/dist/virtual-scroll.js.map +1 -1
  72. package/package.json +2 -2
package/dist/fonts.d.ts CHANGED
@@ -1,14 +1,47 @@
1
1
  import type { UIElement } from './types.js';
2
2
  /**
3
3
  * Extract custom font family names from a CSS `font` shorthand (e.g. `600 14px Inter`).
4
- * Drops generic fallbacks like `sans-serif`. Best-effort parsing for common patterns.
4
+ * Drops generic fallbacks like `sans-serif`, CSS-wide keywords (`inherit`, `initial`, …), and
5
+ * system font keywords (`caption`, `menu`, …).
6
+ * Repeated custom names in the same list collapse
7
+ * to the first spelling (comparison is case-insensitive, e.g. `Inter, inter` → one entry).
8
+ * Best-effort parsing for common patterns.
9
+ * Commas inside quoted family names are ignored; `\\` escapes the next character inside quotes (e.g. `\"` for a literal `"`).
10
+ * Recognizes common font-size units (px, em, rem, cap/ic/lh/rlh, root-relative r* units, pt, %, viewport, viewport inline/block (`vi`/`vb` and `d*`/`s*`/`l*` variants), dynamic-viewport, container query, Q, and math units), including scientific notation on the numeric part (e.g. `1e2px`).
11
+ * CSS math functions `calc()`, `min()`, `max()`, and `clamp()` are treated as a single font-size token (balanced parentheses; commas inside are allowed).
12
+ * When a percentage is used as `font-stretch` before the real font size (e.g. `75% 14px Inter`),
13
+ * skips that leading dimension so the size + family tail is parsed correctly.
14
+ * Very long chains of leading size tokens are peeled with a bounded primary budget, then a secondary pass on modest remainders.
15
+ * If an oversized tail still looks like stacked size tokens, returns [] rather than inventing a family name or doing pathological work.
16
+ * Line-height after `/` may be numeric (with optional unit) or the `normal` keyword; a slash line-height
17
+ * with no following family list yields no families (e.g. `14px / normal` alone).
18
+ * Leading relative size keywords `smaller` and `larger` (CSS Fonts) are stripped so
19
+ * values like `smaller Inter, serif` resolve to concrete families for {@link waitForFonts}.
20
+ * Leading absolute size keywords (`medium`, `xx-small`, `large`, …) are stripped the same way.
21
+ * A comma-list segment that is exactly an absolute size keyword is dropped when unquoted; the same word
22
+ * inside quotes is kept (CSS requires quoting when a family name matches a keyword).
23
+ * Unquoted `url(...)` and `format(...)` segments (e.g. mistaken `@font-face` `src` paste) are not
24
+ * concrete family names and are skipped so {@link waitForFonts} does not call `load` with them.
25
+ * The same spellings inside quotes are kept as literal family names (CSS `font-family` rules).
5
26
  */
6
27
  export declare function extractFontFamiliesFromCSSFont(font: string): string[];
7
- /** Collect unique font families referenced by text nodes in a UI tree. */
28
+ /**
29
+ * Collect unique font families referenced by text nodes in a UI tree.
30
+ * Order is first-seen in a depth-first preorder walk (later duplicates are skipped).
31
+ */
8
32
  export declare function collectFontFamiliesFromTree(root: UIElement): string[];
33
+ /**
34
+ * Resolve a font-load timeout in milliseconds. Used by {@link waitForFonts} and `createApp`'s
35
+ * `fontLoadTimeoutMs` so `NaN`, `±Infinity`, negative values, and non-numbers all fall back to
36
+ * `defaultMs` (avoids `??` missing `NaN` and odd `setTimeout` coercion).
37
+ */
38
+ export declare function resolveFontLoadTimeoutMs(timeoutMs: number | undefined, defaultMs?: number): number;
9
39
  /**
10
40
  * Wait for web fonts used by the app. Browser only; no-op on server.
11
41
  * Uses `document.fonts.load` per family; timeouts are swallowed so startup never hard-fails.
42
+ * Empty and whitespace-only family strings are ignored; names are trimmed before load and deduped.
43
+ * `timeoutMs` must be a finite non-negative number; otherwise the default `10_000` is used (avoids
44
+ * relying on `setTimeout` coercion for `NaN`, `±Infinity`, or negative values).
12
45
  */
13
46
  export declare function waitForFonts(families: string[], timeoutMs?: number): Promise<void>;
14
47
  //# sourceMappingURL=fonts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAe3C;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQrE;AAED,0EAA0E;AAC1E,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,EAAE,CAarE;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBxF"}
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AA6K3C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAgKrE;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,EAAE,CAarE;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,SAAS,GAAG,MAAM,CAElG;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBxF"}
package/dist/fonts.js CHANGED
@@ -8,22 +8,331 @@ const GENERIC_FAMILIES = new Set([
8
8
  'ui-sans-serif',
9
9
  'ui-serif',
10
10
  'ui-monospace',
11
+ 'ui-rounded',
11
12
  'emoji',
12
13
  ]);
14
+ /** CSS-wide keywords that may appear in `font` / family lists but are not concrete family names. */
15
+ const CSS_WIDE_FONT_KEYWORDS = new Set([
16
+ 'inherit',
17
+ 'initial',
18
+ 'unset',
19
+ 'revert',
20
+ 'revert-layer',
21
+ ]);
22
+ /**
23
+ * Single-keyword `font` values that select the UA system font (CSS Fonts); not concrete families
24
+ * and must not be passed to `document.fonts.load`.
25
+ */
26
+ const SYSTEM_FONT_KEYWORDS = new Set([
27
+ 'caption',
28
+ 'icon',
29
+ 'menu',
30
+ 'message-box',
31
+ 'small-caption',
32
+ 'status-bar',
33
+ ]);
34
+ /**
35
+ * Font-size units we treat as the numeric token before the family list in `font` shorthand.
36
+ * Longer tokens precede shorter prefixes (e.g. `dvmin` before `vmin`, `rlh` before `lh`, `rch` before `ch`).
37
+ */
38
+ const FONT_SIZE_UNIT = '(?:dvmin|dvmax|svmin|svmax|lvmin|lvmax|dvh|dvw|dvi|dvb|svh|svw|svi|svb|lvh|lvw|lvi|lvb|' +
39
+ 'vmin|vmax|vh|vw|vi|vb|' +
40
+ 'cqmin|cqmax|cqi|cqb|cqw|cqh|' +
41
+ 'rlh|rcap|rch|rex|ric|' +
42
+ 'rem|cap|px|em|pt|pc|in|cm|math|mm|Q|%|ch|ex|ic|lh)';
43
+ /** Magnitude in a `font-size` dimension token (allows scientific notation, e.g. `1e2px`). */
44
+ const FONT_SIZE_NUMBER = String.raw `\d+(?:\.\d+)?(?:[eE][+-]?\d+)?`;
45
+ /** Optional `/ line-height` after font-size in shorthand (numeric length or keyword `normal`). */
46
+ const FONT_SHORTHAND_LINE_HEIGHT = String.raw `(?:\/\s*(?:` + FONT_SIZE_NUMBER + FONT_SIZE_UNIT + String.raw `?|normal))?`;
47
+ /**
48
+ * Size + required `/ line-height` before the family list. Family tail may be empty (invalid shorthand
49
+ * with no family); tried before {@link FONT_SHORTHAND_FAMILY_TAIL} so `/ normal` is not misparsed as a family.
50
+ */
51
+ const FONT_SHORTHAND_SLASH_LINE_TAIL = new RegExp(String.raw `\b(` +
52
+ FONT_SIZE_NUMBER +
53
+ FONT_SIZE_UNIT +
54
+ String.raw `)\s*\/\s*(?:` +
55
+ FONT_SIZE_NUMBER +
56
+ FONT_SIZE_UNIT +
57
+ String.raw `?|normal)\s*(.*)$`, 'i');
58
+ const FONT_SHORTHAND_FAMILY_TAIL = new RegExp(String.raw `\b(` + FONT_SIZE_NUMBER + FONT_SIZE_UNIT + String.raw `)\s*` + FONT_SHORTHAND_LINE_HEIGHT + String.raw `\s+(.+)$`, 'i');
59
+ const FONT_SIZE_ONLY = new RegExp(String.raw `^` + FONT_SIZE_NUMBER + FONT_SIZE_UNIT + String.raw `$`, 'i');
60
+ /** True when the family tail begins with a dimension token + space (another size before the real family). */
61
+ const TAIL_LEADS_WITH_SIZE_TOKEN = new RegExp(String.raw `^` + FONT_SIZE_NUMBER + FONT_SIZE_UNIT + String.raw `\s+`, 'i');
62
+ /**
63
+ * Upper bound on leading token strips for `font` shorthand parsing (pathological inputs stay finite).
64
+ * Long font-stretch / leading-percent stacks before the real size + family can consume one strip each.
65
+ */
66
+ const MAX_SHORTHAND_STRIP_ITERATIONS = 4096;
67
+ /**
68
+ * After the primary strip budget, if this many characters remain and another peel is still required,
69
+ * return [] instead of continuing (avoids O(n²) regex work and bogus synthesized family names).
70
+ */
71
+ const MAX_REMAINDER_AFTER_PRIMARY_STRIP = 16_384;
72
+ /** Extra peels allowed on modest remainders once the primary budget is exhausted. */
73
+ const EXTRA_SHORTHAND_STRIP_ITERATIONS = 8192;
74
+ /** Max leading `smaller` / `larger` peels (relative font-size keywords before family or explicit size). */
75
+ const MAX_RELATIVE_SIZE_PREFIX_STRIPS = 8;
76
+ /**
77
+ * CSS absolute font-size keywords (CSS Fonts). When they lead the shorthand they must be peeled before
78
+ * size+family parsing; when a comma-separated segment is exactly one of these (unquoted), it is not a
79
+ * family name (quoted names still pass through).
80
+ */
81
+ const ABSOLUTE_FONT_SIZE_KEYWORDS = new Set([
82
+ 'xx-small',
83
+ 'x-small',
84
+ 'small',
85
+ 'medium',
86
+ 'large',
87
+ 'x-large',
88
+ 'xx-large',
89
+ 'xxx-large',
90
+ ]);
91
+ /** Longest-match alternation so `xx-large` does not match as `x-large` + `large`. */
92
+ const ABSOLUTE_FONT_SIZE_PREFIX = /^(xxx-large|xx-large|xx-small|x-large|x-small|small|medium|large)\s+/i;
93
+ /** Max leading absolute keyword strips (pathological stacks stay bounded). */
94
+ const MAX_ABSOLUTE_SIZE_PREFIX_STRIPS = 16;
95
+ /** Index after the closing `)` that matches `s[openParenIndex]` (which must be `(`). */
96
+ function indexAfterMatchingParen(s, openParenIndex) {
97
+ let depth = 0;
98
+ for (let i = openParenIndex; i < s.length; i++) {
99
+ const c = s[i];
100
+ if (c === '(')
101
+ depth++;
102
+ else if (c === ')') {
103
+ depth--;
104
+ if (depth === 0)
105
+ return i + 1;
106
+ }
107
+ }
108
+ return null;
109
+ }
110
+ /** Split a CSS font-family list on commas not inside single or double quotes. */
111
+ function splitFontFamilyList(tail) {
112
+ const parts = [];
113
+ let cur = '';
114
+ let quote = null;
115
+ for (let i = 0; i < tail.length; i++) {
116
+ const c = tail[i];
117
+ if (quote) {
118
+ if (c === '\\' && i + 1 < tail.length) {
119
+ cur += tail[i + 1];
120
+ i++;
121
+ continue;
122
+ }
123
+ cur += c;
124
+ if (c === quote)
125
+ quote = null;
126
+ continue;
127
+ }
128
+ if (c === '"' || c === "'") {
129
+ quote = c;
130
+ cur += c;
131
+ continue;
132
+ }
133
+ if (c === ',') {
134
+ const t = cur.trim();
135
+ if (t.length > 0)
136
+ parts.push(t);
137
+ cur = '';
138
+ continue;
139
+ }
140
+ cur += c;
141
+ }
142
+ const last = cur.trim();
143
+ if (last.length > 0)
144
+ parts.push(last);
145
+ return parts;
146
+ }
13
147
  /**
14
148
  * Extract custom font family names from a CSS `font` shorthand (e.g. `600 14px Inter`).
15
- * Drops generic fallbacks like `sans-serif`. Best-effort parsing for common patterns.
149
+ * Drops generic fallbacks like `sans-serif`, CSS-wide keywords (`inherit`, `initial`, …), and
150
+ * system font keywords (`caption`, `menu`, …).
151
+ * Repeated custom names in the same list collapse
152
+ * to the first spelling (comparison is case-insensitive, e.g. `Inter, inter` → one entry).
153
+ * Best-effort parsing for common patterns.
154
+ * Commas inside quoted family names are ignored; `\\` escapes the next character inside quotes (e.g. `\"` for a literal `"`).
155
+ * Recognizes common font-size units (px, em, rem, cap/ic/lh/rlh, root-relative r* units, pt, %, viewport, viewport inline/block (`vi`/`vb` and `d*`/`s*`/`l*` variants), dynamic-viewport, container query, Q, and math units), including scientific notation on the numeric part (e.g. `1e2px`).
156
+ * CSS math functions `calc()`, `min()`, `max()`, and `clamp()` are treated as a single font-size token (balanced parentheses; commas inside are allowed).
157
+ * When a percentage is used as `font-stretch` before the real font size (e.g. `75% 14px Inter`),
158
+ * skips that leading dimension so the size + family tail is parsed correctly.
159
+ * Very long chains of leading size tokens are peeled with a bounded primary budget, then a secondary pass on modest remainders.
160
+ * If an oversized tail still looks like stacked size tokens, returns [] rather than inventing a family name or doing pathological work.
161
+ * Line-height after `/` may be numeric (with optional unit) or the `normal` keyword; a slash line-height
162
+ * with no following family list yields no families (e.g. `14px / normal` alone).
163
+ * Leading relative size keywords `smaller` and `larger` (CSS Fonts) are stripped so
164
+ * values like `smaller Inter, serif` resolve to concrete families for {@link waitForFonts}.
165
+ * Leading absolute size keywords (`medium`, `xx-small`, `large`, …) are stripped the same way.
166
+ * A comma-list segment that is exactly an absolute size keyword is dropped when unquoted; the same word
167
+ * inside quotes is kept (CSS requires quoting when a family name matches a keyword).
168
+ * Unquoted `url(...)` and `format(...)` segments (e.g. mistaken `@font-face` `src` paste) are not
169
+ * concrete family names and are skipped so {@link waitForFonts} does not call `load` with them.
170
+ * The same spellings inside quotes are kept as literal family names (CSS `font-family` rules).
16
171
  */
17
172
  export function extractFontFamiliesFromCSSFont(font) {
18
- const trimmed = font.trim();
19
- const m = trimmed.match(/\b(\d+(?:\.\d+)?(?:px|em|rem))(?:\/\s*[\d.]+(?:px|em|rem)?)?\s+(.+)$/i);
20
- const tail = m ? m[2] : trimmed;
21
- return tail
22
- .split(',')
23
- .map(s => s.trim().replace(/^["']|["']$/g, ''))
24
- .filter(f => f.length > 0 && !GENERIC_FAMILIES.has(f.toLowerCase()));
173
+ function filterFamilies(tail) {
174
+ const out = [];
175
+ const seen = new Set();
176
+ for (const seg of splitFontFamilyList(tail)) {
177
+ const t = seg.trim();
178
+ if (t.length === 0)
179
+ continue;
180
+ const doubleQuoted = t.length >= 2 && t.startsWith('"') && t.endsWith('"');
181
+ const singleQuoted = t.length >= 2 && t.startsWith("'") && t.endsWith("'");
182
+ const quoted = doubleQuoted || singleQuoted;
183
+ const inner = t.replace(/^["']|["']$/g, '');
184
+ if (inner.length === 0)
185
+ continue;
186
+ const lead = inner.trimStart();
187
+ if (!quoted && /^url\s*\(/i.test(lead))
188
+ continue;
189
+ if (!quoted && /^format\s*\(/i.test(lead))
190
+ continue;
191
+ if (!quoted && ABSOLUTE_FONT_SIZE_KEYWORDS.has(inner.toLowerCase()))
192
+ continue;
193
+ if (GENERIC_FAMILIES.has(inner.toLowerCase()) ||
194
+ CSS_WIDE_FONT_KEYWORDS.has(inner.toLowerCase()) ||
195
+ SYSTEM_FONT_KEYWORDS.has(inner.toLowerCase()) ||
196
+ FONT_SIZE_ONLY.test(inner)) {
197
+ continue;
198
+ }
199
+ const key = inner.toLowerCase();
200
+ if (seen.has(key))
201
+ continue;
202
+ seen.add(key);
203
+ out.push(inner);
204
+ }
205
+ return out;
206
+ }
207
+ function firstFamilyListSegment(tail) {
208
+ return splitFontFamilyList(tail)[0]?.trim() ?? '';
209
+ }
210
+ /** True when the family tail still begins with another size token (dimension, stacked shorthand, or CSS math size). */
211
+ function tailLeadsWithStackedSize(tail) {
212
+ const first = firstFamilyListSegment(tail);
213
+ return (FONT_SIZE_ONLY.test(first) ||
214
+ TAIL_LEADS_WITH_SIZE_TOKEN.test(tail) ||
215
+ /^(calc|min|max|clamp)\s*\(/i.test(first));
216
+ }
217
+ const MATH_SIZE_OPEN = /\b(calc|min|max|clamp)\s*\(/gi;
218
+ function tryPeelMathSlashLine(s) {
219
+ const re = new RegExp(MATH_SIZE_OPEN.source, 'gi');
220
+ let ma;
221
+ while ((ma = re.exec(s)) !== null) {
222
+ const openIdx = ma.index + ma[0].length - 1;
223
+ const afterClose = indexAfterMatchingParen(s, openIdx);
224
+ if (afterClose === null)
225
+ continue;
226
+ const restRaw = s.slice(afterClose);
227
+ if (!/^\s*\//.test(restRaw))
228
+ continue;
229
+ const slashRestMatch = restRaw.match(new RegExp(String.raw `^\s*\/\s*(?:` + FONT_SIZE_NUMBER + FONT_SIZE_UNIT + String.raw `?|normal)\s*(.*)$`, 'i'));
230
+ if (!slashRestMatch)
231
+ continue;
232
+ const tail = slashRestMatch[1] ?? '';
233
+ if (tailLeadsWithStackedSize(tail)) {
234
+ return { kind: 'peeled', next: s.slice(afterClose).trimStart() };
235
+ }
236
+ if (tail.trim().length === 0)
237
+ continue;
238
+ return { kind: 'family', tail: tail.trimStart() };
239
+ }
240
+ return null;
241
+ }
242
+ function tryPeelMathFamily(s) {
243
+ const re = new RegExp(MATH_SIZE_OPEN.source, 'gi');
244
+ let ma;
245
+ while ((ma = re.exec(s)) !== null) {
246
+ const openIdx = ma.index + ma[0].length - 1;
247
+ const afterClose = indexAfterMatchingParen(s, openIdx);
248
+ if (afterClose === null)
249
+ continue;
250
+ const restRaw = s.slice(afterClose);
251
+ if (/^\s*\//.test(restRaw))
252
+ continue;
253
+ const famM = restRaw.match(/^\s+(.+)$/);
254
+ if (!famM)
255
+ continue;
256
+ const tail = famM[1];
257
+ if (tailLeadsWithStackedSize(tail)) {
258
+ return { kind: 'peeled', next: s.slice(afterClose).trimStart() };
259
+ }
260
+ return { kind: 'family', tail };
261
+ }
262
+ return null;
263
+ }
264
+ function peelFontShorthand(s) {
265
+ const slashM = s.match(FONT_SHORTHAND_SLASH_LINE_TAIL);
266
+ if (slashM) {
267
+ const tail = slashM[2] ?? '';
268
+ if (!tailLeadsWithStackedSize(tail))
269
+ return { kind: 'family', tail };
270
+ const matchIndex = slashM.index ?? 0;
271
+ return { kind: 'peeled', next: s.slice(matchIndex + slashM[1].length).trimStart() };
272
+ }
273
+ const mathSlash = tryPeelMathSlashLine(s);
274
+ if (mathSlash)
275
+ return mathSlash;
276
+ // Before numeric size + family: math sizes can contain dimension tokens (e.g. `14px` inside
277
+ // `calc(14px + 1vmin)`) that would otherwise match {@link FONT_SHORTHAND_FAMILY_TAIL} early.
278
+ const mathFam = tryPeelMathFamily(s);
279
+ if (mathFam)
280
+ return mathFam;
281
+ const m = s.match(FONT_SHORTHAND_FAMILY_TAIL);
282
+ if (m) {
283
+ const tail = m[2];
284
+ if (!tailLeadsWithStackedSize(tail))
285
+ return { kind: 'family', tail };
286
+ const matchIndex = m.index ?? 0;
287
+ return { kind: 'peeled', next: s.slice(matchIndex + m[1].length).trimStart() };
288
+ }
289
+ return { kind: 'none' };
290
+ }
291
+ let trimmed = font.trim();
292
+ for (let p = 0; p < MAX_RELATIVE_SIZE_PREFIX_STRIPS; p++) {
293
+ const withoutRel = trimmed.replace(/^(smaller|larger)\s+/i, '');
294
+ if (withoutRel === trimmed)
295
+ break;
296
+ trimmed = withoutRel;
297
+ }
298
+ for (let p = 0; p < MAX_ABSOLUTE_SIZE_PREFIX_STRIPS; p++) {
299
+ const m = trimmed.match(ABSOLUTE_FONT_SIZE_PREFIX);
300
+ if (!m)
301
+ break;
302
+ trimmed = trimmed.slice(m[0].length).trimStart();
303
+ }
304
+ for (let i = 0; i < MAX_SHORTHAND_STRIP_ITERATIONS; i++) {
305
+ const r = peelFontShorthand(trimmed);
306
+ if (r.kind === 'none')
307
+ break;
308
+ if (r.kind === 'family')
309
+ return filterFamilies(r.tail);
310
+ trimmed = r.next;
311
+ }
312
+ if (trimmed.length > MAX_REMAINDER_AFTER_PRIMARY_STRIP) {
313
+ const r = peelFontShorthand(trimmed);
314
+ if (r.kind === 'peeled')
315
+ return [];
316
+ }
317
+ for (let i = 0; i < EXTRA_SHORTHAND_STRIP_ITERATIONS; i++) {
318
+ const r = peelFontShorthand(trimmed);
319
+ if (r.kind === 'none')
320
+ break;
321
+ if (r.kind === 'family')
322
+ return filterFamilies(r.tail);
323
+ trimmed = r.next;
324
+ }
325
+ const last = peelFontShorthand(trimmed);
326
+ if (last.kind === 'family')
327
+ return filterFamilies(last.tail);
328
+ if (last.kind === 'peeled')
329
+ return [];
330
+ return filterFamilies(trimmed);
25
331
  }
26
- /** Collect unique font families referenced by text nodes in a UI tree. */
332
+ /**
333
+ * Collect unique font families referenced by text nodes in a UI tree.
334
+ * Order is first-seen in a depth-first preorder walk (later duplicates are skipped).
335
+ */
27
336
  export function collectFontFamiliesFromTree(root) {
28
337
  const out = new Set();
29
338
  function walk(el) {
@@ -40,9 +349,20 @@ export function collectFontFamiliesFromTree(root) {
40
349
  walk(root);
41
350
  return [...out];
42
351
  }
352
+ /**
353
+ * Resolve a font-load timeout in milliseconds. Used by {@link waitForFonts} and `createApp`'s
354
+ * `fontLoadTimeoutMs` so `NaN`, `±Infinity`, negative values, and non-numbers all fall back to
355
+ * `defaultMs` (avoids `??` missing `NaN` and odd `setTimeout` coercion).
356
+ */
357
+ export function resolveFontLoadTimeoutMs(timeoutMs, defaultMs = 10_000) {
358
+ return typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs >= 0 ? timeoutMs : defaultMs;
359
+ }
43
360
  /**
44
361
  * Wait for web fonts used by the app. Browser only; no-op on server.
45
362
  * Uses `document.fonts.load` per family; timeouts are swallowed so startup never hard-fails.
363
+ * Empty and whitespace-only family strings are ignored; names are trimmed before load and deduped.
364
+ * `timeoutMs` must be a finite non-negative number; otherwise the default `10_000` is used (avoids
365
+ * relying on `setTimeout` coercion for `NaN`, `±Infinity`, or negative values).
46
366
  */
47
367
  export async function waitForFonts(families, timeoutMs = 10_000) {
48
368
  if (typeof document === 'undefined' || families.length === 0)
@@ -50,13 +370,16 @@ export async function waitForFonts(families, timeoutMs = 10_000) {
50
370
  const api = document.fonts;
51
371
  if (!api?.load)
52
372
  return;
53
- const unique = [...new Set(families)];
54
- const work = Promise.all(unique.map(f => api.load(`16px ${f}`).catch(() => undefined))).then(() => api.ready);
373
+ const unique = [...new Set(families.map(f => f.trim()).filter(f => f.length > 0))];
374
+ if (unique.length === 0)
375
+ return;
376
+ const safeTimeoutMs = resolveFontLoadTimeoutMs(timeoutMs, 10_000);
377
+ const work = Promise.all(unique.map(f => api.load(`16px ${f}`).catch(() => undefined))).then(() => Promise.resolve(api.ready).catch(() => undefined));
55
378
  try {
56
379
  await Promise.race([
57
380
  work,
58
381
  new Promise((_, reject) => {
59
- setTimeout(() => reject(new Error('Font load timeout')), timeoutMs);
382
+ setTimeout(() => reject(new Error('Font load timeout')), safeTimeoutMs);
60
383
  }),
61
384
  ]);
62
385
  }
package/dist/fonts.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,OAAO;IACP,YAAY;IACZ,WAAW;IACX,SAAS;IACT,SAAS;IACT,WAAW;IACX,eAAe;IACf,UAAU;IACV,cAAc;IACd,OAAO;CACR,CAAC,CAAA;AAEF;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAAC,IAAY;IACzD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAC3B,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAA;IAChG,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,OAAO,CAAA;IAChC,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;SAC9C,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;AACxE,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,2BAA2B,CAAC,IAAe;IACzD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAA;IAC7B,SAAS,IAAI,CAAC,EAAa;QACzB,IAAI,EAAE,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,IAAI,8BAA8B,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9D,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;YACZ,CAAC;QACH,CAAC;aAAM,IAAI,EAAE,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,QAAQ;gBAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACtC,CAAC;IACH,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,CAAA;IACV,OAAO,CAAC,GAAG,GAAG,CAAC,CAAA;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAkB,EAAE,SAAS,GAAG,MAAM;IACvE,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IACpE,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAA;IAC1B,IAAI,CAAC,GAAG,EAAE,IAAI;QAAE,OAAM;IAEtB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;IACrC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAChG,GAAG,CAAC,KAAK,CACV,CAAA;IAED,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,IAAI,CAAC;YACjB,IAAI;YACJ,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC9B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;YACrE,CAAC,CAAC;SACH,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,iBAAiB;IACnB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,49 @@
1
1
  import type { ComputedLayout } from 'textura';
2
2
  import type { UIElement, BoxElement, EventHandlers } from './types.js';
3
- /** Result of routing a pointer/keyboard-style hit to handlers. */
3
+ /**
4
+ * Result of routing a hit to {@link EventHandlers} on the deepest matching target.
5
+ * `focusTarget` is only populated for `onClick` (see {@link dispatchHit}).
6
+ */
4
7
  export interface HitDispatchResult {
5
8
  handled: boolean;
6
- /** Set when a focusable box handled `onClick` (for keyboard focus). */
9
+ /**
10
+ * Present only when `dispatchHit` was called with `'onClick'`: the deepest focusable box under the point
11
+ * (`onClick`, `onKeyDown` / `onKeyUp`, or composition handlers). Set after a successful
12
+ * `onClick` handler **or** via click-to-focus when the box has no `onClick` but should
13
+ * still receive keyboard/composition input (`handled` may be `false`). Omitted for all
14
+ * other event types.
15
+ */
7
16
  focusTarget?: {
8
17
  element: BoxElement;
9
18
  layout: ComputedLayout;
10
19
  };
11
20
  }
12
- /** Dispatch an event at (x, y) against the element tree. Returns true if any handler fired. */
21
+ /**
22
+ * Dispatch a hit at `(x, y)` against the element tree for the given handler key
23
+ * (e.g. `onClick`, `onPointerDown`, `onKeyDown`, composition events).
24
+ * The deepest hit with a matching handler runs first; only one handler runs per call.
25
+ * Optional `extra` is shallow-merged onto the `HitEvent` after base fields so callers
26
+ * can pass modifier keys, `button`, wheel deltas, and other renderer-specific metadata.
27
+ * For `onClick` only, the return value may include `focusTarget` for focus routing
28
+ * (including click-to-focus when there is no `onClick` handler).
29
+ * Layout bounds must be finite and non-negative on `width` and `height`; otherwise the node is
30
+ * skipped for hit-testing (corrupt geometry from Yoga or a bad snapshot).
31
+ */
13
32
  export declare function dispatchHit(element: UIElement, layout: ComputedLayout, eventType: keyof EventHandlers, x: number, y: number, extra?: Record<string, unknown>): HitDispatchResult;
14
- /** True when point is over an interactive element (click/key/composition handlers). */
33
+ /**
34
+ * True when the point is over an element that participates in pointer hit-testing
35
+ * (`onClick`, `onPointerDown` / `Up` / `Move`, `onWheel`). Keyboard and composition
36
+ * handlers alone do not count — use this for hover / pointer-capture style checks.
37
+ */
15
38
  export declare function hasInteractiveHitAtPoint(element: UIElement, layout: ComputedLayout, x: number, y: number): boolean;
39
+ /**
40
+ * Child indices from root to the deepest box under (x, y). Among overlapping siblings, prefers higher z-index (topmost);
41
+ * non-finite or non-number `zIndex` values match `dispatchHit` / paint order (treated as `0`).
42
+ * Boxes with `pointerEvents: 'none'` do not terminate the path when they have no deeper hit (same idea as `getCursorAtPoint` and `collectHits`).
43
+ * Returns `null` when the point misses the tree. Returns `[]` when the point hits the
44
+ * root (or a leaf box) with no deeper box hit.
45
+ */
46
+ export declare function hitPathAtPoint(element: UIElement, layout: ComputedLayout, x: number, y: number, offsetX?: number, offsetY?: number): number[] | null;
16
47
  /** Get the cursor style at a given point by walking the tree. Returns the deepest element's cursor prop. */
17
48
  export declare function getCursorAtPoint(element: UIElement, layout: ComputedLayout, x: number, y: number, offsetX?: number, offsetY?: number): string | null;
18
49
  //# sourceMappingURL=hit-test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hit-test.d.ts","sourceRoot":"","sources":["../src/hit-test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAY,MAAM,YAAY,CAAA;AAgChF,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,uEAAuE;IACvE,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAA;CAC9D;AAqDD,+FAA+F;AAC/F,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,aAAa,EAC9B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,iBAAiB,CAoDnB;AAED,uFAAuF;AACvF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,cAAc,EACtB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,OAAO,CAiBT;AAED,4GAA4G;AAC5G,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,cAAc,EACtB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,SAAI,EACX,OAAO,SAAI,GACV,MAAM,GAAG,IAAI,CA+Bf"}
1
+ {"version":3,"file":"hit-test.d.ts","sourceRoot":"","sources":["../src/hit-test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAY,MAAM,YAAY,CAAA;AA2EhF;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAA;CAC9D;AAmJD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,aAAa,EAC9B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,iBAAiB,CAEnB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,cAAc,EACtB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,OAAO,CAgBT;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,cAAc,EACtB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,SAAI,EACX,OAAO,SAAI,GACV,MAAM,EAAE,GAAG,IAAI,CA0CjB;AAED,4GAA4G;AAC5G,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,cAAc,EACtB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,SAAI,EACX,OAAO,SAAI,GACV,MAAM,GAAG,IAAI,CAoCf"}