@geometra/core 0.2.0 → 0.2.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.
- package/README.md +42 -0
- package/dist/a11y.d.ts +19 -0
- package/dist/a11y.d.ts.map +1 -0
- package/dist/a11y.js +78 -0
- package/dist/a11y.js.map +1 -0
- package/dist/animation.d.ts +29 -0
- package/dist/animation.d.ts.map +1 -0
- package/dist/animation.js +102 -0
- package/dist/animation.js.map +1 -0
- package/dist/app.d.ts +17 -2
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +41 -9
- package/dist/app.js.map +1 -1
- package/dist/elements.d.ts +10 -1
- package/dist/elements.d.ts.map +1 -1
- package/dist/elements.js +18 -1
- package/dist/elements.js.map +1 -1
- package/dist/focus.d.ts +19 -0
- package/dist/focus.d.ts.map +1 -0
- package/dist/focus.js +61 -0
- package/dist/focus.js.map +1 -0
- package/dist/fonts.d.ts +14 -0
- package/dist/fonts.d.ts.map +1 -0
- package/dist/fonts.js +67 -0
- package/dist/fonts.js.map +1 -0
- package/dist/hit-test.d.ts +13 -2
- package/dist/hit-test.d.ts.map +1 -1
- package/dist/hit-test.js +74 -7
- package/dist/hit-test.js.map +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/dist/keyboard.d.ts +10 -0
- package/dist/keyboard.d.ts.map +1 -0
- package/dist/keyboard.js +49 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/selection.d.ts.map +1 -1
- package/dist/selection.js +3 -1
- package/dist/selection.js.map +1 -1
- package/dist/seo.d.ts.map +1 -1
- package/dist/seo.js +5 -0
- package/dist/seo.js.map +1 -1
- package/dist/text-input-history.d.ts +15 -0
- package/dist/text-input-history.d.ts.map +1 -0
- package/dist/text-input-history.js +62 -0
- package/dist/text-input-history.js.map +1 -0
- package/dist/text-input.d.ts +42 -0
- package/dist/text-input.d.ts.map +1 -0
- package/dist/text-input.js +249 -0
- package/dist/text-input.js.map +1 -0
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +9 -3
- package/dist/tree.js.map +1 -1
- package/dist/types.d.ts +65 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @geometra/core
|
|
2
|
+
|
|
3
|
+
DOM-free UI framework core. Build declarative interfaces rendered to Canvas, terminal, or server-streamed layouts.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @geometra/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Key Exports
|
|
12
|
+
|
|
13
|
+
- `signal`, `computed`, `effect`, `batch` -- reactive primitives
|
|
14
|
+
- `box`, `text`, `image` -- element constructors
|
|
15
|
+
- `createApp` -- application entry point
|
|
16
|
+
- `toSemanticHTML` -- SEO-friendly HTML generation
|
|
17
|
+
- `toAccessibilityTree` -- runtime accessibility tree from geometry
|
|
18
|
+
- `insertInputText`, `backspaceInput`, `deleteInput` -- text-input editing primitives
|
|
19
|
+
- `getInputCaretGeometry` -- caret x/y/height from measured text lines
|
|
20
|
+
- `createTextInputHistory`, `undoTextInputHistory` -- undo/redo state helpers
|
|
21
|
+
- `transition`, `spring` -- animation utilities
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { box, text, createApp, signal } from '@geometra/core'
|
|
27
|
+
|
|
28
|
+
const count = signal(0)
|
|
29
|
+
|
|
30
|
+
const app = createApp(() =>
|
|
31
|
+
box({ width: 300, height: 200, padding: 20, gap: 10 }, [
|
|
32
|
+
text({ text: `Count: ${count.value}`, font: 'bold 24px sans-serif', lineHeight: 30 }),
|
|
33
|
+
box({ width: 100, height: 40, backgroundColor: '#07f', onClick: () => count.set(count.peek() + 1) }, [
|
|
34
|
+
text({ text: 'Click me', font: '16px sans-serif', lineHeight: 20, color: '#fff' }),
|
|
35
|
+
]),
|
|
36
|
+
])
|
|
37
|
+
)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Links
|
|
41
|
+
|
|
42
|
+
- [Main repo](https://github.com/AiGeekz/geometra)
|
package/dist/a11y.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComputedLayout } from 'textura';
|
|
2
|
+
import type { UIElement } from './types.js';
|
|
3
|
+
export interface AccessibilityBounds {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
export interface AccessibilityNode {
|
|
10
|
+
role: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
bounds: AccessibilityBounds;
|
|
13
|
+
path: number[];
|
|
14
|
+
children: AccessibilityNode[];
|
|
15
|
+
focusable: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Build an accessibility tree from UI elements and computed layout geometry. */
|
|
18
|
+
export declare function toAccessibilityTree(tree: UIElement, layout: ComputedLayout): AccessibilityNode;
|
|
19
|
+
//# sourceMappingURL=a11y.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a11y.d.ts","sourceRoot":"","sources":["../src/a11y.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAyC,MAAM,YAAY,CAAA;AAElF,MAAM,WAAW,mBAAmB;IAClC,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,mBAAmB,CAAA;IAC3B,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,QAAQ,EAAE,iBAAiB,EAAE,CAAA;IAC7B,SAAS,EAAE,OAAO,CAAA;CACnB;AA8ED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,iBAAiB,CAE9F"}
|
package/dist/a11y.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function inferTextRole(element) {
|
|
2
|
+
if (element.semantic?.role)
|
|
3
|
+
return element.semantic.role;
|
|
4
|
+
const tag = element.semantic?.tag;
|
|
5
|
+
if (tag && /^h[1-6]$/.test(tag))
|
|
6
|
+
return 'heading';
|
|
7
|
+
return 'text';
|
|
8
|
+
}
|
|
9
|
+
function inferBoxRole(element) {
|
|
10
|
+
if (element.semantic?.role)
|
|
11
|
+
return element.semantic.role;
|
|
12
|
+
const tag = element.semantic?.tag;
|
|
13
|
+
if (tag === 'nav')
|
|
14
|
+
return 'navigation';
|
|
15
|
+
if (tag === 'main')
|
|
16
|
+
return 'main';
|
|
17
|
+
if (tag === 'article')
|
|
18
|
+
return 'article';
|
|
19
|
+
if (element.handlers?.onClick)
|
|
20
|
+
return 'button';
|
|
21
|
+
return 'group';
|
|
22
|
+
}
|
|
23
|
+
function inferImageRole(element) {
|
|
24
|
+
if (element.semantic?.role)
|
|
25
|
+
return element.semantic.role;
|
|
26
|
+
return 'img';
|
|
27
|
+
}
|
|
28
|
+
function inferName(element) {
|
|
29
|
+
if (element.semantic?.ariaLabel)
|
|
30
|
+
return element.semantic.ariaLabel;
|
|
31
|
+
if (element.kind === 'text')
|
|
32
|
+
return element.props.text;
|
|
33
|
+
if (element.kind === 'image')
|
|
34
|
+
return element.semantic?.alt ?? element.props.alt;
|
|
35
|
+
return element.semantic?.alt;
|
|
36
|
+
}
|
|
37
|
+
function isFocusable(element) {
|
|
38
|
+
if (element.kind !== 'box')
|
|
39
|
+
return false;
|
|
40
|
+
return !!(element.handlers?.onClick ||
|
|
41
|
+
element.handlers?.onKeyDown ||
|
|
42
|
+
element.handlers?.onKeyUp);
|
|
43
|
+
}
|
|
44
|
+
function roleFor(element) {
|
|
45
|
+
if (element.kind === 'text')
|
|
46
|
+
return inferTextRole(element);
|
|
47
|
+
if (element.kind === 'image')
|
|
48
|
+
return inferImageRole(element);
|
|
49
|
+
return inferBoxRole(element);
|
|
50
|
+
}
|
|
51
|
+
function walk(element, layout, offsetX, offsetY, path) {
|
|
52
|
+
const x = offsetX + layout.x;
|
|
53
|
+
const y = offsetY + layout.y;
|
|
54
|
+
const children = [];
|
|
55
|
+
if (element.kind === 'box') {
|
|
56
|
+
const childOffsetX = x - (element.props.scrollX ?? 0);
|
|
57
|
+
const childOffsetY = y - (element.props.scrollY ?? 0);
|
|
58
|
+
for (let i = 0; i < element.children.length; i++) {
|
|
59
|
+
const childLayout = layout.children[i];
|
|
60
|
+
if (childLayout) {
|
|
61
|
+
children.push(walk(element.children[i], childLayout, childOffsetX, childOffsetY, [...path, i]));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
role: roleFor(element),
|
|
67
|
+
name: inferName(element),
|
|
68
|
+
bounds: { x, y, width: layout.width, height: layout.height },
|
|
69
|
+
path,
|
|
70
|
+
children,
|
|
71
|
+
focusable: isFocusable(element),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Build an accessibility tree from UI elements and computed layout geometry. */
|
|
75
|
+
export function toAccessibilityTree(tree, layout) {
|
|
76
|
+
return walk(tree, layout, 0, 0, []);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=a11y.js.map
|
package/dist/a11y.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a11y.js","sourceRoot":"","sources":["../src/a11y.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Signal } from './signals.js';
|
|
2
|
+
/** Common easing functions. */
|
|
3
|
+
export declare const easing: {
|
|
4
|
+
linear: (t: number) => number;
|
|
5
|
+
easeIn: (t: number) => number;
|
|
6
|
+
easeOut: (t: number) => number;
|
|
7
|
+
easeInOut: (t: number) => number;
|
|
8
|
+
};
|
|
9
|
+
export type EasingFn = (t: number) => number;
|
|
10
|
+
/**
|
|
11
|
+
* Create an animated signal that transitions from `from` to `to` over `duration` ms.
|
|
12
|
+
* Returns a signal whose `.value` tracks the current interpolated value.
|
|
13
|
+
*/
|
|
14
|
+
export declare function transition(from: number, to: number, duration: number, easingFn?: EasingFn): Signal<number>;
|
|
15
|
+
/**
|
|
16
|
+
* Create a spring-physics animated signal that follows a target value.
|
|
17
|
+
* Returns a signal that smoothly converges to `target.value`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function spring(target: Signal<number>, config?: {
|
|
20
|
+
stiffness?: number;
|
|
21
|
+
damping?: number;
|
|
22
|
+
mass?: number;
|
|
23
|
+
}): Signal<number>;
|
|
24
|
+
/**
|
|
25
|
+
* Run a raw animation loop. The callback receives delta time in seconds.
|
|
26
|
+
* Return `false` from the callback to stop. Returns a stop function.
|
|
27
|
+
*/
|
|
28
|
+
export declare function animationLoop(callback: (dt: number) => boolean): () => void;
|
|
29
|
+
//# sourceMappingURL=animation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAU1C,+BAA+B;AAC/B,eAAO,MAAM,MAAM;gBACL,MAAM;gBACN,MAAM;iBACL,MAAM;mBACJ,MAAM;CACtB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;AAE5C;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,QAA2B,GACpC,MAAM,CAAC,MAAM,CAAC,CAahB;AAED;;;GAGG;AACH,wBAAgB,MAAM,CACpB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EACtB,MAAM,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GACnE,MAAM,CAAC,MAAM,CAAC,CAgDhB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,IAAI,CAmB3E"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { signal } from './signals.js';
|
|
2
|
+
const raf = typeof requestAnimationFrame !== 'undefined'
|
|
3
|
+
? requestAnimationFrame
|
|
4
|
+
: (cb) => setTimeout(() => cb(Date.now()), 16);
|
|
5
|
+
const cancelRaf = typeof cancelAnimationFrame !== 'undefined'
|
|
6
|
+
? cancelAnimationFrame
|
|
7
|
+
: (id) => clearTimeout(id);
|
|
8
|
+
/** Common easing functions. */
|
|
9
|
+
export const easing = {
|
|
10
|
+
linear: (t) => t,
|
|
11
|
+
easeIn: (t) => t * t * t,
|
|
12
|
+
easeOut: (t) => 1 - Math.pow(1 - t, 3),
|
|
13
|
+
easeInOut: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Create an animated signal that transitions from `from` to `to` over `duration` ms.
|
|
17
|
+
* Returns a signal whose `.value` tracks the current interpolated value.
|
|
18
|
+
*/
|
|
19
|
+
export function transition(from, to, duration, easingFn = easing.easeInOut) {
|
|
20
|
+
const s = signal(from);
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
function tick() {
|
|
23
|
+
const elapsed = Date.now() - start;
|
|
24
|
+
const t = Math.min(elapsed / duration, 1);
|
|
25
|
+
s.set(from + (to - from) * easingFn(t));
|
|
26
|
+
if (t < 1)
|
|
27
|
+
raf(tick);
|
|
28
|
+
}
|
|
29
|
+
raf(tick);
|
|
30
|
+
return s;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a spring-physics animated signal that follows a target value.
|
|
34
|
+
* Returns a signal that smoothly converges to `target.value`.
|
|
35
|
+
*/
|
|
36
|
+
export function spring(target, config = {}) {
|
|
37
|
+
const { stiffness = 170, damping = 26, mass = 1 } = config;
|
|
38
|
+
const s = signal(target.peek());
|
|
39
|
+
let velocity = 0;
|
|
40
|
+
let prevTarget = target.peek();
|
|
41
|
+
let id = null;
|
|
42
|
+
function tick() {
|
|
43
|
+
const currentTarget = target.peek();
|
|
44
|
+
const current = s.peek();
|
|
45
|
+
const displacement = current - currentTarget;
|
|
46
|
+
const springForce = -stiffness * displacement;
|
|
47
|
+
const dampingForce = -damping * velocity;
|
|
48
|
+
const acceleration = (springForce + dampingForce) / mass;
|
|
49
|
+
velocity += acceleration * (1 / 60);
|
|
50
|
+
const next = current + velocity * (1 / 60);
|
|
51
|
+
if (Math.abs(velocity) < 0.01 && Math.abs(displacement) < 0.01) {
|
|
52
|
+
s.set(currentTarget);
|
|
53
|
+
id = null;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
s.set(next);
|
|
57
|
+
id = raf(tick);
|
|
58
|
+
}
|
|
59
|
+
// Watch for target changes
|
|
60
|
+
function start() {
|
|
61
|
+
if (id === null) {
|
|
62
|
+
id = raf(tick);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Check target periodically (or rely on external calls)
|
|
66
|
+
function checkTarget() {
|
|
67
|
+
const curr = target.peek();
|
|
68
|
+
if (curr !== prevTarget) {
|
|
69
|
+
prevTarget = curr;
|
|
70
|
+
start();
|
|
71
|
+
}
|
|
72
|
+
raf(checkTarget);
|
|
73
|
+
}
|
|
74
|
+
raf(checkTarget);
|
|
75
|
+
start();
|
|
76
|
+
return s;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Run a raw animation loop. The callback receives delta time in seconds.
|
|
80
|
+
* Return `false` from the callback to stop. Returns a stop function.
|
|
81
|
+
*/
|
|
82
|
+
export function animationLoop(callback) {
|
|
83
|
+
let lastTime = Date.now();
|
|
84
|
+
let running = true;
|
|
85
|
+
let id;
|
|
86
|
+
function tick() {
|
|
87
|
+
if (!running)
|
|
88
|
+
return;
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
const dt = (now - lastTime) / 1000;
|
|
91
|
+
lastTime = now;
|
|
92
|
+
if (callback(dt) !== false) {
|
|
93
|
+
id = raf(tick);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
running = false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
id = raf(tick);
|
|
100
|
+
return () => { running = false; cancelRaf(id); };
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=animation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animation.js","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"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"}
|
package/dist/app.d.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import type { ComputedLayout } from 'textura';
|
|
2
|
-
import type { UIElement, Renderer } from './types.js';
|
|
2
|
+
import type { UIElement, Renderer, EventHandlers, KeyboardHitEvent } from './types.js';
|
|
3
3
|
export interface AppOptions {
|
|
4
4
|
/** Root width for layout computation. */
|
|
5
5
|
width?: number;
|
|
6
6
|
/** Root height for layout computation. */
|
|
7
7
|
height?: number;
|
|
8
|
+
/** Called when an error occurs during update. */
|
|
9
|
+
onError?: (error: unknown) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Await `document.fonts` for families used in the initial view (browser only).
|
|
12
|
+
* Reduces first-paint layout shift for web fonts.
|
|
13
|
+
*/
|
|
14
|
+
waitForFonts?: boolean;
|
|
15
|
+
/** Max time to wait for fonts when `waitForFonts` is true. Default 10000. */
|
|
16
|
+
fontLoadTimeoutMs?: number;
|
|
8
17
|
}
|
|
9
18
|
export interface App {
|
|
10
19
|
/** The current computed layout. */
|
|
@@ -14,7 +23,13 @@ export interface App {
|
|
|
14
23
|
/** Manually trigger a re-render. */
|
|
15
24
|
update(): void;
|
|
16
25
|
/** Dispatch a pointer event at (x, y). */
|
|
17
|
-
dispatch(eventType:
|
|
26
|
+
dispatch(eventType: keyof EventHandlers, x: number, y: number, extra?: Record<string, unknown>): boolean;
|
|
27
|
+
/** Dispatch a keyboard event to the focused element. */
|
|
28
|
+
dispatchKey(eventType: 'onKeyDown' | 'onKeyUp', event: Omit<KeyboardHitEvent, 'target'>): boolean;
|
|
29
|
+
/** Dispatch an IME composition event to the focused element. */
|
|
30
|
+
dispatchComposition(eventType: 'onCompositionStart' | 'onCompositionUpdate' | 'onCompositionEnd', event: {
|
|
31
|
+
data: string;
|
|
32
|
+
}): boolean;
|
|
18
33
|
/** Tear down the app. */
|
|
19
34
|
destroy(): void;
|
|
20
35
|
}
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAQtF,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,GAAG;IAClB,mCAAmC;IACnC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAA;IAC7B,gCAAgC;IAChC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;IACtB,oCAAoC;IACpC,MAAM,IAAI,IAAI,CAAA;IACd,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAA;IACxG,wDAAwD;IACxD,WAAW,CAAC,SAAS,EAAE,WAAW,GAAG,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAA;IACjG,gEAAgE;IAChE,mBAAmB,CACjB,SAAS,EAAE,oBAAoB,GAAG,qBAAqB,GAAG,kBAAkB,EAC5E,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GACtB,OAAO,CAAA;IACV,yBAAyB;IACzB,OAAO,IAAI,IAAI,CAAA;CAChB;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,SAAS,EACrB,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,GAAG,CAAC,CAwDd"}
|
package/dist/app.js
CHANGED
|
@@ -2,6 +2,9 @@ import { init, computeLayout } from 'textura';
|
|
|
2
2
|
import { toLayoutTree } from './tree.js';
|
|
3
3
|
import { dispatchHit } from './hit-test.js';
|
|
4
4
|
import { effect } from './signals.js';
|
|
5
|
+
import { focusedElement, setFocus } from './focus.js';
|
|
6
|
+
import { collectFontFamiliesFromTree, waitForFonts } from './fonts.js';
|
|
7
|
+
import { dispatchKeyboardEvent, dispatchCompositionEvent } from './keyboard.js';
|
|
5
8
|
/**
|
|
6
9
|
* Mount a reactive UI tree onto a renderer.
|
|
7
10
|
*
|
|
@@ -10,22 +13,50 @@ import { effect } from './signals.js';
|
|
|
10
13
|
*/
|
|
11
14
|
export async function createApp(view, renderer, options = {}) {
|
|
12
15
|
await init();
|
|
16
|
+
if (options.waitForFonts && typeof document !== 'undefined') {
|
|
17
|
+
const initialTree = view();
|
|
18
|
+
await waitForFonts(collectFontFamiliesFromTree(initialTree), options.fontLoadTimeoutMs ?? 10_000);
|
|
19
|
+
}
|
|
13
20
|
const app = {
|
|
14
21
|
layout: null,
|
|
15
22
|
tree: null,
|
|
16
23
|
update() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
try {
|
|
25
|
+
app.tree = view();
|
|
26
|
+
const layoutTree = toLayoutTree(app.tree);
|
|
27
|
+
app.layout = computeLayout(layoutTree, {
|
|
28
|
+
width: options.width,
|
|
29
|
+
height: options.height,
|
|
30
|
+
});
|
|
31
|
+
renderer.render(app.layout, app.tree);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (options.onError) {
|
|
35
|
+
options.onError(err);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.error('Geometra render error:', err);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
24
41
|
},
|
|
25
|
-
dispatch(eventType, x, y) {
|
|
42
|
+
dispatch(eventType, x, y, extra) {
|
|
26
43
|
if (!app.tree || !app.layout)
|
|
27
44
|
return false;
|
|
28
|
-
|
|
45
|
+
const { handled, focusTarget } = dispatchHit(app.tree, app.layout, eventType, x, y, extra);
|
|
46
|
+
if (eventType === 'onClick' && focusTarget) {
|
|
47
|
+
setFocus(focusTarget.element, focusTarget.layout);
|
|
48
|
+
}
|
|
49
|
+
return handled;
|
|
50
|
+
},
|
|
51
|
+
dispatchKey(eventType, partialEvent) {
|
|
52
|
+
if (!app.tree || !app.layout)
|
|
53
|
+
return false;
|
|
54
|
+
return dispatchKeyboardEvent(app.tree, app.layout, eventType, partialEvent);
|
|
55
|
+
},
|
|
56
|
+
dispatchComposition(eventType, partialEvent) {
|
|
57
|
+
if (!app.tree || !app.layout)
|
|
58
|
+
return false;
|
|
59
|
+
return dispatchCompositionEvent(app.tree, app.layout, eventType, partialEvent);
|
|
29
60
|
},
|
|
30
61
|
destroy() {
|
|
31
62
|
dispose();
|
|
@@ -33,6 +64,7 @@ export async function createApp(view, renderer, options = {}) {
|
|
|
33
64
|
},
|
|
34
65
|
};
|
|
35
66
|
const dispose = effect(() => {
|
|
67
|
+
void focusedElement.value;
|
|
36
68
|
app.update();
|
|
37
69
|
});
|
|
38
70
|
return app;
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
package/dist/elements.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FlexProps } from 'textura';
|
|
2
|
-
import type { StyleProps, BoxElement, TextElement, UIElement, EventHandlers, SemanticProps } from './types.js';
|
|
2
|
+
import type { StyleProps, BoxElement, TextElement, ImageElement, UIElement, EventHandlers, SemanticProps } from './types.js';
|
|
3
3
|
type BoxProps = FlexProps & StyleProps & EventHandlers & {
|
|
4
4
|
key?: string;
|
|
5
5
|
semantic?: SemanticProps;
|
|
@@ -13,9 +13,18 @@ type TextProps = FlexProps & StyleProps & {
|
|
|
13
13
|
key?: string;
|
|
14
14
|
semantic?: SemanticProps;
|
|
15
15
|
};
|
|
16
|
+
type ImageProps = FlexProps & StyleProps & {
|
|
17
|
+
src: string;
|
|
18
|
+
alt?: string;
|
|
19
|
+
objectFit?: 'fill' | 'contain' | 'cover';
|
|
20
|
+
key?: string;
|
|
21
|
+
semantic?: SemanticProps;
|
|
22
|
+
};
|
|
16
23
|
/** Create a box (container) element. */
|
|
17
24
|
export declare function box(props: BoxProps, children?: UIElement[]): BoxElement;
|
|
18
25
|
/** Create a text leaf element. */
|
|
19
26
|
export declare function text(props: TextProps): TextElement;
|
|
27
|
+
/** Create an image element. */
|
|
28
|
+
export declare function image(props: ImageProps): ImageElement;
|
|
20
29
|
export {};
|
|
21
30
|
//# sourceMappingURL=elements.d.ts.map
|
package/dist/elements.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elements.d.ts","sourceRoot":"","sources":["../src/elements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"elements.d.ts","sourceRoot":"","sources":["../src/elements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE5H,KAAK,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,aAAa,CAAA;CAAE,CAAA;AACnG,KAAK,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG;IACxC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;IAClC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,aAAa,CAAA;CACzB,CAAA;AACD,KAAK,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG;IACzC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;IACxC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,aAAa,CAAA;CACzB,CAAA;AAED,wCAAwC;AACxC,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAE,SAAS,EAAO,GAAG,UAAU,CAoC3E;AAED,kCAAkC;AAClC,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,CAGlD;AAED,+BAA+B;AAC/B,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CAGrD"}
|
package/dist/elements.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Create a box (container) element. */
|
|
2
2
|
export function box(props, children = []) {
|
|
3
|
-
const { onClick, onPointerDown, onPointerUp, onPointerMove, key, semantic, ...rest } = props;
|
|
3
|
+
const { onClick, onPointerDown, onPointerUp, onPointerMove, onWheel, onKeyDown, onKeyUp, onCompositionStart, onCompositionUpdate, onCompositionEnd, key, semantic, ...rest } = props;
|
|
4
4
|
const handlers = {};
|
|
5
5
|
if (onClick)
|
|
6
6
|
handlers.onClick = onClick;
|
|
@@ -10,6 +10,18 @@ export function box(props, children = []) {
|
|
|
10
10
|
handlers.onPointerUp = onPointerUp;
|
|
11
11
|
if (onPointerMove)
|
|
12
12
|
handlers.onPointerMove = onPointerMove;
|
|
13
|
+
if (onWheel)
|
|
14
|
+
handlers.onWheel = onWheel;
|
|
15
|
+
if (onKeyDown)
|
|
16
|
+
handlers.onKeyDown = onKeyDown;
|
|
17
|
+
if (onKeyUp)
|
|
18
|
+
handlers.onKeyUp = onKeyUp;
|
|
19
|
+
if (onCompositionStart)
|
|
20
|
+
handlers.onCompositionStart = onCompositionStart;
|
|
21
|
+
if (onCompositionUpdate)
|
|
22
|
+
handlers.onCompositionUpdate = onCompositionUpdate;
|
|
23
|
+
if (onCompositionEnd)
|
|
24
|
+
handlers.onCompositionEnd = onCompositionEnd;
|
|
13
25
|
return {
|
|
14
26
|
kind: 'box',
|
|
15
27
|
props: rest,
|
|
@@ -24,4 +36,9 @@ export function text(props) {
|
|
|
24
36
|
const { key, semantic, ...rest } = props;
|
|
25
37
|
return { kind: 'text', props: rest, key, semantic };
|
|
26
38
|
}
|
|
39
|
+
/** Create an image element. */
|
|
40
|
+
export function image(props) {
|
|
41
|
+
const { key, semantic, ...rest } = props;
|
|
42
|
+
return { kind: 'image', props: rest, key, semantic };
|
|
43
|
+
}
|
|
27
44
|
//# sourceMappingURL=elements.js.map
|
package/dist/elements.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elements.js","sourceRoot":"","sources":["../src/elements.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"elements.js","sourceRoot":"","sources":["../src/elements.ts"],"names":[],"mappings":"AAqBA,wCAAwC;AACxC,MAAM,UAAU,GAAG,CAAC,KAAe,EAAE,WAAwB,EAAE;IAC7D,MAAM,EACJ,OAAO,EACP,aAAa,EACb,WAAW,EACX,aAAa,EACb,OAAO,EACP,SAAS,EACT,OAAO,EACP,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,GAAG,EACH,QAAQ,EACR,GAAG,IAAI,EACR,GAAG,KAAK,CAAA;IACT,MAAM,QAAQ,GAAkB,EAAE,CAAA;IAClC,IAAI,OAAO;QAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAA;IACvC,IAAI,aAAa;QAAE,QAAQ,CAAC,aAAa,GAAG,aAAa,CAAA;IACzD,IAAI,WAAW;QAAE,QAAQ,CAAC,WAAW,GAAG,WAAW,CAAA;IACnD,IAAI,aAAa;QAAE,QAAQ,CAAC,aAAa,GAAG,aAAa,CAAA;IACzD,IAAI,OAAO;QAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAA;IACvC,IAAI,SAAS;QAAE,QAAQ,CAAC,SAAS,GAAG,SAAS,CAAA;IAC7C,IAAI,OAAO;QAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAA;IACvC,IAAI,kBAAkB;QAAE,QAAQ,CAAC,kBAAkB,GAAG,kBAAkB,CAAA;IACxE,IAAI,mBAAmB;QAAE,QAAQ,CAAC,mBAAmB,GAAG,mBAAmB,CAAA;IAC3E,IAAI,gBAAgB;QAAE,QAAQ,CAAC,gBAAgB,GAAG,gBAAgB,CAAA;IAElE,OAAO;QACL,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,IAAI;QACX,QAAQ;QACR,GAAG;QACH,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;QACjE,QAAQ;KACT,CAAA;AACH,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,IAAI,CAAC,KAAgB;IACnC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAA;IACxC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAA;AACrD,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,KAAK,CAAC,KAAiB;IACrC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAA;IACxC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAA;AACtD,CAAC"}
|
package/dist/focus.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComputedLayout } from 'textura';
|
|
2
|
+
import type { UIElement, BoxElement } from './types.js';
|
|
3
|
+
import type { Signal } from './signals.js';
|
|
4
|
+
interface FocusTarget {
|
|
5
|
+
element: BoxElement;
|
|
6
|
+
layout: ComputedLayout;
|
|
7
|
+
}
|
|
8
|
+
/** Signal tracking the currently focused element. */
|
|
9
|
+
export declare const focusedElement: Signal<FocusTarget | null>;
|
|
10
|
+
/** Set focus to an element. */
|
|
11
|
+
export declare function setFocus(element: BoxElement, layout: ComputedLayout): void;
|
|
12
|
+
/** Clear the current focus. */
|
|
13
|
+
export declare function clearFocus(): void;
|
|
14
|
+
/** Move focus to the next focusable element. */
|
|
15
|
+
export declare function focusNext(tree: UIElement, layout: ComputedLayout): void;
|
|
16
|
+
/** Move focus to the previous focusable element. */
|
|
17
|
+
export declare function focusPrev(tree: UIElement, layout: ComputedLayout): void;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=focus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus.d.ts","sourceRoot":"","sources":["../src/focus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,UAAU,WAAW;IACnB,OAAO,EAAE,UAAU,CAAA;IACnB,MAAM,EAAE,cAAc,CAAA;CACvB;AAED,qDAAqD;AACrD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,WAAW,GAAG,IAAI,CAAoC,CAAA;AAE1F,+BAA+B;AAC/B,wBAAgB,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAE1E;AAED,+BAA+B;AAC/B,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AA4BD,gDAAgD;AAChD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAcvE;AAED,oDAAoD;AACpD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAcvE"}
|
package/dist/focus.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { signal } from './signals.js';
|
|
2
|
+
/** Signal tracking the currently focused element. */
|
|
3
|
+
export const focusedElement = signal(null);
|
|
4
|
+
/** Set focus to an element. */
|
|
5
|
+
export function setFocus(element, layout) {
|
|
6
|
+
focusedElement.set({ element, layout });
|
|
7
|
+
}
|
|
8
|
+
/** Clear the current focus. */
|
|
9
|
+
export function clearFocus() {
|
|
10
|
+
focusedElement.set(null);
|
|
11
|
+
}
|
|
12
|
+
/** Collect all focusable elements (those with keyboard or click handlers) in document order. */
|
|
13
|
+
function collectFocusable(element, layout, results) {
|
|
14
|
+
if (element.kind === 'box') {
|
|
15
|
+
if (element.handlers?.onKeyDown ||
|
|
16
|
+
element.handlers?.onKeyUp ||
|
|
17
|
+
element.handlers?.onCompositionStart ||
|
|
18
|
+
element.handlers?.onCompositionUpdate ||
|
|
19
|
+
element.handlers?.onCompositionEnd ||
|
|
20
|
+
element.handlers?.onClick) {
|
|
21
|
+
results.push({ element, layout });
|
|
22
|
+
}
|
|
23
|
+
for (let i = 0; i < element.children.length; i++) {
|
|
24
|
+
const childLayout = layout.children[i];
|
|
25
|
+
if (childLayout) {
|
|
26
|
+
collectFocusable(element.children[i], childLayout, results);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Move focus to the next focusable element. */
|
|
32
|
+
export function focusNext(tree, layout) {
|
|
33
|
+
const targets = [];
|
|
34
|
+
collectFocusable(tree, layout, targets);
|
|
35
|
+
if (targets.length === 0)
|
|
36
|
+
return;
|
|
37
|
+
const current = focusedElement.peek();
|
|
38
|
+
if (!current) {
|
|
39
|
+
focusedElement.set(targets[0]);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const idx = targets.findIndex(t => t.element === current.element);
|
|
43
|
+
const next = targets[(idx + 1) % targets.length];
|
|
44
|
+
focusedElement.set(next);
|
|
45
|
+
}
|
|
46
|
+
/** Move focus to the previous focusable element. */
|
|
47
|
+
export function focusPrev(tree, layout) {
|
|
48
|
+
const targets = [];
|
|
49
|
+
collectFocusable(tree, layout, targets);
|
|
50
|
+
if (targets.length === 0)
|
|
51
|
+
return;
|
|
52
|
+
const current = focusedElement.peek();
|
|
53
|
+
if (!current) {
|
|
54
|
+
focusedElement.set(targets[targets.length - 1]);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const idx = targets.findIndex(t => t.element === current.element);
|
|
58
|
+
const prev = targets[(idx - 1 + targets.length) % targets.length];
|
|
59
|
+
focusedElement.set(prev);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=focus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus.js","sourceRoot":"","sources":["../src/focus.ts"],"names":[],"mappings":"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"}
|
package/dist/fonts.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { UIElement } from './types.js';
|
|
2
|
+
/**
|
|
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.
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractFontFamiliesFromCSSFont(font: string): string[];
|
|
7
|
+
/** Collect unique font families referenced by text nodes in a UI tree. */
|
|
8
|
+
export declare function collectFontFamiliesFromTree(root: UIElement): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Wait for web fonts used by the app. Browser only; no-op on server.
|
|
11
|
+
* Uses `document.fonts.load` per family; timeouts are swallowed so startup never hard-fails.
|
|
12
|
+
*/
|
|
13
|
+
export declare function waitForFonts(families: string[], timeoutMs?: number): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=fonts.d.ts.map
|
|
@@ -0,0 +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"}
|