@vectojs/core 1.39.1 → 1.40.0
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/dist/{chunk-7PYD5UDX.mjs → chunk-CIRZ3S2Z.mjs} +48 -3
- package/dist/{chunk-POPXUPKR.js → chunk-HC5EZOZJ.js} +80 -35
- package/dist/components/VirtualizedSetAggregate.d.ts +110 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1040 -206
- package/dist/index.mjs +839 -5
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +59 -1
- package/dist/tree/Scene.d.ts +195 -1
- package/dist/tree/scene/HitResult.d.ts +50 -0
- package/dist/tree/scene/HitTester.d.ts +30 -0
- package/dist/tree/scene/ProjectionBackend.d.ts +64 -0
- package/dist/tree/scene/ProjectionPolicy.d.ts +162 -0
- package/dist/tree/scene/SemanticProjectionPolicy.d.ts +119 -0
- package/dist/wasm/vectojs_core.wasm +0 -0
- package/package.json +2 -2
package/dist/text.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkHC5EZOZJjs = require('./chunk-HC5EZOZJ.js');
|
|
6
6
|
|
|
7
7
|
// src/text/index.ts
|
|
8
8
|
var _text = require('@vectojs/text'); _createStarExport(_text);
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
exports.MSDFTextEntity =
|
|
12
|
+
exports.MSDFTextEntity = _chunkHC5EZOZJjs.MSDFTextEntity; exports.SVGEntity = _chunkHC5EZOZJjs.SVGEntity;
|
package/dist/text.mjs
CHANGED
package/dist/tree/Entity.d.ts
CHANGED
|
@@ -502,6 +502,20 @@ export interface ListenerOptions {
|
|
|
502
502
|
/** Register the listener for the capture phase (root→target) instead of bubble. */
|
|
503
503
|
capture?: boolean;
|
|
504
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* Origin transport of a dispatched {@link VectoJSEvent} (input-dispatch-contract
|
|
507
|
+
* v2 §4 rule 1: source attribution; gesture stickiness and single delivery are
|
|
508
|
+
* judged against this field).
|
|
509
|
+
*
|
|
510
|
+
* `'mirror'` is the a11y-shadow transport that ships today; `'canvas'` and
|
|
511
|
+
* `'at'` are reserved for the canvas-first dispatcher and AT-synthesized
|
|
512
|
+
* reflection. `'dom'` is owned by `@vectojs/dom` (RFC2): the live projected
|
|
513
|
+
* element counts as a materialized target at its point, so its bridge tags
|
|
514
|
+
* every event it forwards. The open tail leaves room for future backends
|
|
515
|
+
* without touching this type. `undefined` means "unattributed" (legacy call
|
|
516
|
+
* sites and synthetic dispatches predate the contract).
|
|
517
|
+
*/
|
|
518
|
+
export type VectoEventSource = 'mirror' | 'canvas' | 'at' | (string & {});
|
|
505
519
|
/**
|
|
506
520
|
* A propagating event dispatched through the entity tree by
|
|
507
521
|
* {@link Entity.dispatchEvent} (DOM-like capture + bubble).
|
|
@@ -523,10 +537,15 @@ export declare class VectoJSEvent<N = unknown> {
|
|
|
523
537
|
readonly nativeEvent: N | undefined;
|
|
524
538
|
/** Whether the event bubbles past its target (capture always runs). */
|
|
525
539
|
readonly bubbles: boolean;
|
|
540
|
+
/**
|
|
541
|
+
* Which transport delivered this event ({@link VectoEventSource}).
|
|
542
|
+
* `undefined` for unattributed legacy/synthetic dispatches.
|
|
543
|
+
*/
|
|
544
|
+
readonly source: VectoEventSource | undefined;
|
|
526
545
|
private readonly explicitScenePoint;
|
|
527
546
|
private stopped;
|
|
528
547
|
private stoppedImmediate;
|
|
529
|
-
constructor(type: VectoEvent, target: Entity, nativeEvent?: N, bubbles?: boolean, scenePoint?: Point);
|
|
548
|
+
constructor(type: VectoEvent, target: Entity, nativeEvent?: N, bubbles?: boolean, scenePoint?: Point, source?: VectoEventSource);
|
|
530
549
|
/** Stop the event from reaching the next node in the propagation path. */
|
|
531
550
|
stopPropagation(): void;
|
|
532
551
|
/** Stop propagation AND skip any remaining listeners on the current node. */
|
|
@@ -712,6 +731,41 @@ export declare abstract class Entity {
|
|
|
712
731
|
* faint-but-live control.
|
|
713
732
|
*/
|
|
714
733
|
a11yHidden: boolean;
|
|
734
|
+
/**
|
|
735
|
+
* Opt-in to the DOM visual projection (RFC2, implemented by `@vectojs/dom`).
|
|
736
|
+
* Plain data only — core never materializes an element from this; a
|
|
737
|
+
* registered `ProjectionBackend` does.
|
|
738
|
+
*
|
|
739
|
+
* Name mapping: this field IS the RFC4 §2 `projection` policy
|
|
740
|
+
* (`ProjectionPolicy`: `'canvas' | 'dom' | 'auto'` in
|
|
741
|
+
* `tree/scene/ProjectionPolicy.ts`) — predates the RFC, so the field keeps
|
|
742
|
+
* its name and the RFC spelling lives on the value type.
|
|
743
|
+
*
|
|
744
|
+
* - `'canvas'` (default): today's rendering. Zero behavior change.
|
|
745
|
+
* - `'dom'`: the node materializes as a live `HTMLElement` positioned by its
|
|
746
|
+
* world matrix. The live element replaces the transparent a11y mirror
|
|
747
|
+
* (same single-delivery reasoning as `DOMPortalEntity`), so opting in
|
|
748
|
+
* requires a DOM backend and a DOM environment for the AT representation.
|
|
749
|
+
* - `'auto'`: negotiated per node per frame by `Scene.resolveProjectionFor`
|
|
750
|
+
* (RFC4 §3: explicit beats automatic, fallbacks reported with reasons,
|
|
751
|
+
* hysteresis against flip-flop, no-DOM short-circuits to canvas).
|
|
752
|
+
*/
|
|
753
|
+
domPolicy: 'canvas' | 'dom' | 'auto';
|
|
754
|
+
/**
|
|
755
|
+
* Backend-side creation hint for a `'dom'`-policy node (tag/content mapping
|
|
756
|
+
* key, e.g. `'text' | 'button' | 'input' | 'container' | 'transform'`). Plain
|
|
757
|
+
* string so custom nodes need no DOM types in core; `@vectojs/dom` owns the
|
|
758
|
+
* registry. `''` means a plain positioned `div` with no content sync.
|
|
759
|
+
*/
|
|
760
|
+
domKind: string;
|
|
761
|
+
/**
|
|
762
|
+
* Internal cache owned by the DOM backend: true while the node has a live
|
|
763
|
+
* projected element. The render walk reads it to skip canvas paint for
|
|
764
|
+
* DOM-resident nodes (so a mounted node is never double-drawn) and falls
|
|
765
|
+
* through to canvas while false (SSR / no backend registered). Do not set
|
|
766
|
+
* by hand — the backend sets it on mount and clears it on unmount.
|
|
767
|
+
*/
|
|
768
|
+
domResident: boolean;
|
|
715
769
|
/**
|
|
716
770
|
* Clip this node's children to its local box (`[0,0]–[width,height]`) while
|
|
717
771
|
* rendering. Combined with translating a content child, this is how
|
|
@@ -1123,6 +1177,10 @@ export declare abstract class Entity {
|
|
|
1123
1177
|
* `selectable` is set — natively selectable. Returns `null` by default.
|
|
1124
1178
|
* Read on the a11y sync cadence, so text changes propagate automatically.
|
|
1125
1179
|
*
|
|
1180
|
+
* This is the descriptor half of the `ContentProjection` row of
|
|
1181
|
+
* `ProjectionBackend` (`tree/scene/ProjectionBackend.ts`): the entity
|
|
1182
|
+
* describes *what* to project, the scene owns *how* it is materialized.
|
|
1183
|
+
*
|
|
1126
1184
|
* @param hint - Optional advice about which part of the entity is worth
|
|
1127
1185
|
* describing. Purely an optimization: ignoring it is always correct, which
|
|
1128
1186
|
* is why it is a parameter rather than a required contract change. See
|
package/dist/tree/Scene.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface IWebGPUParticleSystemManager {
|
|
|
13
13
|
import { Entity } from './Entity';
|
|
14
14
|
import { IRenderer } from '../renderer/IRenderer';
|
|
15
15
|
import type { WebGLDrawStats } from '../renderer/WebGLPointRenderer';
|
|
16
|
+
import type { ProjectionBackend, ProjectionBackendKind } from './scene/ProjectionBackend';
|
|
16
17
|
import { type WasmModuleSource, type WasmTransformBackend } from '../wasm/backend';
|
|
17
18
|
import type { CoreWasmRuntime } from '../wasm/runtime';
|
|
18
19
|
import { type HitModuleSource, type HitTestBackend } from '../wasm/hit-backend';
|
|
@@ -20,9 +21,12 @@ import { type AnimModuleSource, type AnimBackend } from '../wasm/anim-backend';
|
|
|
20
21
|
import { type ParticleModuleSource, type ParticleBackend } from '../wasm/particle-backend';
|
|
21
22
|
import { type OverlayGeometry } from './scene/CanvasGeometry';
|
|
22
23
|
import { type DirtyReasonEntry, type DirtySource } from './scene/DirtyTracker';
|
|
24
|
+
import type { DomHitCandidate, HitResult } from './scene/HitResult';
|
|
23
25
|
import { type RenderPhase, type RenderPhaseEntry } from './scene/PhaseTimer';
|
|
24
26
|
import { type AcceleratorReason, type AcceleratorReport, type AcceleratorStatus } from './scene/WasmBackendFacade';
|
|
25
27
|
import { type SceneKeyEvent, type SceneShortcutSpec } from './scene/keyboard';
|
|
28
|
+
import { type SemanticProjectionPolicy } from './scene/SemanticProjectionPolicy';
|
|
29
|
+
import { type ProjectionCapabilityRow, type ProjectionResolution, type SceneProjectionCapabilities } from './scene/ProjectionPolicy';
|
|
26
30
|
export type { RenderPhase, RenderPhaseEntry };
|
|
27
31
|
export { normalizeChord } from './scene/keyboard';
|
|
28
32
|
export type { SceneKeyEvent, SceneShortcutSpec } from './scene/keyboard';
|
|
@@ -96,6 +100,28 @@ export interface SceneOptions {
|
|
|
96
100
|
* Also settable later via {@link Scene.a11ySyncInterval}.
|
|
97
101
|
*/
|
|
98
102
|
a11ySyncInterval?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Per-node semantic projection policy (RFC3 §5, CTX-0599). Consulted by the
|
|
105
|
+
* single per-node decision point (`shouldProjectA11y`); the framework-known
|
|
106
|
+
* default projects everything the legacy predicate projects, so omitting
|
|
107
|
+
* this changes nothing. Also settable later via
|
|
108
|
+
* {@link Scene.semanticProjectionPolicy}.
|
|
109
|
+
*/
|
|
110
|
+
semanticProjectionPolicy?: SemanticProjectionPolicy;
|
|
111
|
+
/**
|
|
112
|
+
* Consecutive syncs an `'auto'`-policy node must keep voting for the other
|
|
113
|
+
* backend before its sticky resolution flips (RFC4 §3 rule 3). Defaults to
|
|
114
|
+
* {@link PROJECTION_AUTO_HYSTERESIS_FRAMES}. Also settable later via
|
|
115
|
+
* {@link Scene.projectionHysteresisFrames}.
|
|
116
|
+
*/
|
|
117
|
+
projectionHysteresisFrames?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Maximum `'auto'`-resolved DOM residents per scene per frame (RFC4 §4
|
|
120
|
+
* particle-row backstop). Explicit `'dom'` requests bypass it. Defaults to
|
|
121
|
+
* {@link PROJECTION_AUTO_DOM_BUDGET}. Also settable later via
|
|
122
|
+
* {@link Scene.projectionAutoDomBudget}.
|
|
123
|
+
*/
|
|
124
|
+
projectionAutoDomBudget?: number;
|
|
99
125
|
/**
|
|
100
126
|
* Custom renderer implementation (e.g., ThreeRenderer from @vectojs/three).
|
|
101
127
|
* If provided, this renderer will be used for drawing rather than the default CanvasRenderer.
|
|
@@ -261,7 +287,7 @@ export interface SceneOptions {
|
|
|
261
287
|
* against. A new option must be added here too — the test suite asserts the two
|
|
262
288
|
* stay in sync.
|
|
263
289
|
*/
|
|
264
|
-
export declare const SCENE_OPTION_KEYS: readonly ['a11ySyncInterval', 'autoThrottle', 'contentProjection', 'contentProjectionMargin', 'contentSemanticBudget', 'contentSemanticMargin', 'debugA11y', 'disableWindowResize', 'idleFPS', 'maxDPR', 'maxFPS', 'particleBackend', 'pointBackend', 'readingDirection', 'renderer', 'renderMode', 'respectReducedMotion', 'userTiming'];
|
|
290
|
+
export declare const SCENE_OPTION_KEYS: readonly ['a11ySyncInterval', 'autoThrottle', 'contentProjection', 'contentProjectionMargin', 'contentSemanticBudget', 'contentSemanticMargin', 'debugA11y', 'disableWindowResize', 'idleFPS', 'maxDPR', 'maxFPS', 'particleBackend', 'pointBackend', 'readingDirection', 'renderer', 'renderMode', 'respectReducedMotion', 'semanticProjectionPolicy', 'userTiming'];
|
|
265
291
|
/** Frame-rate the loop is capped to when the OS requests reduced motion. */
|
|
266
292
|
export declare const REDUCED_MOTION_FPS = 30;
|
|
267
293
|
export type { AcceleratorReason, AcceleratorStatus, AcceleratorReport };
|
|
@@ -507,6 +533,26 @@ export declare class Scene {
|
|
|
507
533
|
* frame. See {@link SceneOptions.a11ySyncInterval}.
|
|
508
534
|
*/
|
|
509
535
|
a11ySyncInterval: number;
|
|
536
|
+
/**
|
|
537
|
+
* Per-node semantic projection policy (RFC3 §5, CTX-0599). Consulted once
|
|
538
|
+
* per node by {@link shouldProjectA11y}; the default projects everything
|
|
539
|
+
* the legacy predicate projects, so reassigning nothing changes nothing.
|
|
540
|
+
* See {@link SceneOptions.semanticProjectionPolicy}.
|
|
541
|
+
*/
|
|
542
|
+
semanticProjectionPolicy: SemanticProjectionPolicy;
|
|
543
|
+
/**
|
|
544
|
+
* Consecutive syncs an `'auto'`-policy node must keep voting for the other
|
|
545
|
+
* backend before its sticky resolution flips (RFC4 §3 rule 3, CTX-0601).
|
|
546
|
+
* Documented tunable — see {@link PROJECTION_AUTO_HYSTERESIS_FRAMES}.
|
|
547
|
+
*/
|
|
548
|
+
projectionHysteresisFrames: number;
|
|
549
|
+
/**
|
|
550
|
+
* Maximum `'auto'`-resolved DOM residents per scene per frame (RFC4 §4
|
|
551
|
+
* particle-row backstop: bulk-count nodes stay canvas). Explicit `'dom'`
|
|
552
|
+
* requests bypass it. Documented tunable — see
|
|
553
|
+
* {@link PROJECTION_AUTO_DOM_BUDGET}.
|
|
554
|
+
*/
|
|
555
|
+
projectionAutoDomBudget: number;
|
|
510
556
|
/** Timestamp of the last a11y sync, for throttling. */
|
|
511
557
|
private lastA11ySync;
|
|
512
558
|
/** True if we skipped an a11y sync during animation and need to sync when at rest. */
|
|
@@ -700,6 +746,30 @@ export declare class Scene {
|
|
|
700
746
|
private activePortalsThisFrame;
|
|
701
747
|
private activePortalsPrevFrame;
|
|
702
748
|
private portalEntities;
|
|
749
|
+
/**
|
|
750
|
+
* Generic projection backends (RFC1 §5 vocabulary, `tree/scene/ProjectionBackend.ts`).
|
|
751
|
+
* Render-independent: the interface exchanges only node identity, world
|
|
752
|
+
* matrix, and lifecycle calls, so registering a DOM backend adds no
|
|
753
|
+
* `HTMLElement` surface to core. `@vectojs/dom` owns the only implementation.
|
|
754
|
+
*/
|
|
755
|
+
private projectionBackends;
|
|
756
|
+
/** Per-frame seen-sets driving {@link pruneProjectionBackends} (portal-precedent). */
|
|
757
|
+
private domSeenPrevFrame;
|
|
758
|
+
private domSeenThisFrame;
|
|
759
|
+
private domSeenNodes;
|
|
760
|
+
/**
|
|
761
|
+
* Negotiation state (RFC4 §3, CTX-0601): last resolution per node (the
|
|
762
|
+
* per-scene queryable surface behind {@link getProjectionResolutions}),
|
|
763
|
+
* hysteresis votes, per-kind capability overrides, and explicit gesture
|
|
764
|
+
* pins. Plain data only — no `HTMLElement` surface in core.
|
|
765
|
+
*/
|
|
766
|
+
private projectionResolutions;
|
|
767
|
+
private projectionHysteresis;
|
|
768
|
+
private projectionCapabilityOverrides;
|
|
769
|
+
private projectionGesturePins;
|
|
770
|
+
/** Frame id the `'auto'` DOM budget count belongs to (bulk backstop). */
|
|
771
|
+
private projectionBudgetFrame;
|
|
772
|
+
private projectionDomAutoCount;
|
|
703
773
|
private renderOrderCounter;
|
|
704
774
|
/**
|
|
705
775
|
* Monotonic render-frame counter, bumped once per authoritative `render()`
|
|
@@ -1188,6 +1258,24 @@ export declare class Scene {
|
|
|
1188
1258
|
* (`DEC-0019` rule 5).
|
|
1189
1259
|
*/
|
|
1190
1260
|
findEntityAt(x: number, y: number): Entity | null;
|
|
1261
|
+
/**
|
|
1262
|
+
* The merged hit list for a scene-space point: the canvas spatial test
|
|
1263
|
+
* plus caller-observed DOM-native candidates (mirror / portal /
|
|
1264
|
+
* `dom-visual` extension point for CTX-0598) as ONE ordered candidate
|
|
1265
|
+
* list (`HitResult`, overlay order authoritative).
|
|
1266
|
+
*
|
|
1267
|
+
* Query API alongside {@link findEntityAt} — no dispatch change:
|
|
1268
|
+
* `findEntityAt` keeps its single topmost answer byte-for-byte, keyboard
|
|
1269
|
+
* and AT flows are untouched. For browser pointer coordinates use
|
|
1270
|
+
* {@link findHitsAtClient}, which maps through {@link clientToScene} first.
|
|
1271
|
+
*/
|
|
1272
|
+
findHitsAt(x: number, y: number, domCandidates?: DomHitCandidate[]): HitResult[];
|
|
1273
|
+
/**
|
|
1274
|
+
* {@link findHitsAt} for browser viewport coordinates: maps through
|
|
1275
|
+
* {@link clientToScene} so coordinates enter in scene space (RFC5 §2
|
|
1276
|
+
* rule 3) and backend attribution follows.
|
|
1277
|
+
*/
|
|
1278
|
+
findHitsAtClient(clientX: number, clientY: number, domCandidates?: DomHitCandidate[]): HitResult[];
|
|
1191
1279
|
/** Convert browser viewport coordinates into this Scene's logical coordinates. */
|
|
1192
1280
|
clientToScene(clientX: number, clientY: number): {
|
|
1193
1281
|
x: number;
|
|
@@ -1481,6 +1569,23 @@ export declare class Scene {
|
|
|
1481
1569
|
* predicate, which is only tractable while it has one home.
|
|
1482
1570
|
*/
|
|
1483
1571
|
private shouldProjectA11y;
|
|
1572
|
+
/**
|
|
1573
|
+
* Policy half of {@link shouldProjectA11y} (RFC3 §5, CTX-0599).
|
|
1574
|
+
*
|
|
1575
|
+
* The legacy gates above (interactive, box, `a11yProjection` engagement)
|
|
1576
|
+
* are unchanged; this only maps a {@link SemanticProjectionPolicy} decision
|
|
1577
|
+
* onto project/suppress. Out of the box the default policy returns
|
|
1578
|
+
* `'project'`, so behaviour is identical to having no policy.
|
|
1579
|
+
*
|
|
1580
|
+
* `'defer-to-browser'` always falls back to projection today: no deferral
|
|
1581
|
+
* backend exists (`supportsHTMLInCanvas()` is `false`), so deferring would
|
|
1582
|
+
* silently drop semantics — the exact failure RFC §3 rules out. When a
|
|
1583
|
+
* backend lands, only allow-listed plain display text
|
|
1584
|
+
* ({@link isDeferrableSemanticNode}; never controls) may actually defer. A
|
|
1585
|
+
* throwing policy likewise falls back to projection: a policy must never be
|
|
1586
|
+
* able to drop semantics by accident.
|
|
1587
|
+
*/
|
|
1588
|
+
private resolveSemanticProjection;
|
|
1484
1589
|
/**
|
|
1485
1590
|
* Whether an `a11yProjection: 'onDemand'` entity is currently engaged enough to
|
|
1486
1591
|
* deserve a shadow node.
|
|
@@ -1610,6 +1715,89 @@ export declare class Scene {
|
|
|
1610
1715
|
getA11yTree(): A11yTreeNode[];
|
|
1611
1716
|
private renderPortalDOM;
|
|
1612
1717
|
private reconcilePortals;
|
|
1718
|
+
/**
|
|
1719
|
+
* Register a generic projection backend (RFC1 §5, RFC2 P1 CTX-0598).
|
|
1720
|
+
* Registration is idempotent per backend instance. Core drives
|
|
1721
|
+
* mount/update/unmount from the render walk; the backend owns all
|
|
1722
|
+
* medium-specific state.
|
|
1723
|
+
*/
|
|
1724
|
+
addProjectionBackend(backend: ProjectionBackend): this;
|
|
1725
|
+
/**
|
|
1726
|
+
* Remove a previously registered projection backend by instance or
|
|
1727
|
+
* {@link ProjectionBackendKind | kind}. Removing does not unmount resident
|
|
1728
|
+
* nodes — unmount the backend first if teardown order matters.
|
|
1729
|
+
*/
|
|
1730
|
+
removeProjectionBackend(backend: ProjectionBackend | ProjectionBackendKind): this;
|
|
1731
|
+
/**
|
|
1732
|
+
* Scene-level projection capabilities (RFC4 §3 rule 2, CTX-0601).
|
|
1733
|
+
*
|
|
1734
|
+
* Plain-data feature detection in the shape of the proposed
|
|
1735
|
+
* `scene.inputCapabilities` (input-dispatch-contract-v2 §4): apps and
|
|
1736
|
+
* devtools query where a node _would_ materialize and why, without touching
|
|
1737
|
+
* any backend. Additive only — no existing surface moves.
|
|
1738
|
+
*/
|
|
1739
|
+
get projectionCapabilities(): SceneProjectionCapabilities;
|
|
1740
|
+
/**
|
|
1741
|
+
* Register (or replace) the Capability Matrix row for one `domKind`
|
|
1742
|
+
* (RFC4 §4, CTX-0601). Custom kinds start at the unknown-kind default
|
|
1743
|
+
* (`unsupported-kind` → canvas); registering a row is how a custom backend
|
|
1744
|
+
* kind opts into `'auto'` resolution and how tests drive oscillating costs.
|
|
1745
|
+
*/
|
|
1746
|
+
registerProjectionCapability(domKind: string, row: ProjectionCapabilityRow): this;
|
|
1747
|
+
/** Matrix row for a node's `domKind` (override wins, unknown → canvas). */
|
|
1748
|
+
getProjectionCapability(node: Entity): ProjectionCapabilityRow;
|
|
1749
|
+
/**
|
|
1750
|
+
* Pin a node's backend for the duration of an active gesture (RFC4 §5 +
|
|
1751
|
+
* input-dispatch-contract-v2 §4 gesture stickiness: no mid-gesture handoff).
|
|
1752
|
+
* The a11y-mirror pointerdown/up/cancel listeners maintain this; DOM-side
|
|
1753
|
+
* gestures are consulted through
|
|
1754
|
+
* {@link ProjectionBackend.hasActiveGesture | backend.hasActiveGesture}.
|
|
1755
|
+
* Refcounted so multi-pointer gestures on one node unpin exactly once.
|
|
1756
|
+
*/
|
|
1757
|
+
pinProjectionForGesture(node: Entity): void;
|
|
1758
|
+
/** Release one gesture pin taken by {@link pinProjectionForGesture}. */
|
|
1759
|
+
unpinProjectionForGesture(node: Entity): void;
|
|
1760
|
+
/** Whether `node` currently owns an active gesture on a core mirror. */
|
|
1761
|
+
isProjectionPinned(node: Entity): boolean;
|
|
1762
|
+
/**
|
|
1763
|
+
* Last negotiation for one node, if it was ever resolved on this scene.
|
|
1764
|
+
* Accepts the node or its id; returns the stored record (not a copy — treat
|
|
1765
|
+
* as read-only).
|
|
1766
|
+
*/
|
|
1767
|
+
getProjectionResolution(node: Entity | string): ProjectionResolution | undefined;
|
|
1768
|
+
/** Every node's last negotiation, in first-resolution order. */
|
|
1769
|
+
getProjectionResolutions(): readonly ProjectionResolution[];
|
|
1770
|
+
/**
|
|
1771
|
+
* Resolve one node's `domPolicy` to its effective backend (RFC4 §3, CTX-0601).
|
|
1772
|
+
*
|
|
1773
|
+
* Memoized per main frame (`currentFrame`, bumped once per authoritative
|
|
1774
|
+
* render): the render walk and the later a11y sync must agree within a
|
|
1775
|
+
* frame, or a node could lose its mirror the same frame it gains an element.
|
|
1776
|
+
* `'auto'` applies, in order: no-DOM short-circuit → pure negotiation →
|
|
1777
|
+
* gesture/focus hard bars (a pinned node keeps its current backend, never
|
|
1778
|
+
* flips mid-gesture, focus is preserved-or-moved never dropped to `body` per
|
|
1779
|
+
* input-dispatch-contract-v2 §4) → hysteresis stickiness → bulk budget.
|
|
1780
|
+
* Every outcome is recorded on the per-scene queryable surface
|
|
1781
|
+
* ({@link getProjectionResolutions}) with its reason — fallbacks are
|
|
1782
|
+
* reported, not silent (§3 rule 2).
|
|
1783
|
+
*/
|
|
1784
|
+
resolveProjectionFor(node: Entity): 'canvas' | 'dom';
|
|
1785
|
+
/**
|
|
1786
|
+
* Whether `node`'s a11y mirror currently owns browser focus (RFC4 §5 focus
|
|
1787
|
+
* rule, input-dispatch-contract-v2 §4: focus stays DOM-based; flipping
|
|
1788
|
+
* `projection` must preserve or deliberately move focus, never drop it to
|
|
1789
|
+
* `body`). A focused node keeps its backend until blur — the actual move, if
|
|
1790
|
+
* any, goes through the existing sentinels (`preserveFocusOnRemoval`, the
|
|
1791
|
+
* DOM backend's own fallback), never a bare removal.
|
|
1792
|
+
*/
|
|
1793
|
+
private ownsProjectionFocus;
|
|
1794
|
+
/**
|
|
1795
|
+
* RFC2 P1 (CTX-0598): unmount DOM-projection residents the walk did not see
|
|
1796
|
+
* this frame (viewport-culled, removed without `remove()`, policy-flipped).
|
|
1797
|
+
* Mirrors {@link reconcilePortals} above; unmount is idempotent per the
|
|
1798
|
+
* `ProjectionBackend` contract so notifying every backend is safe.
|
|
1799
|
+
*/
|
|
1800
|
+
private pruneProjectionBackends;
|
|
1613
1801
|
/**
|
|
1614
1802
|
* The frame-rate cap actually in effect: the explicit {@link maxFPS}, further
|
|
1615
1803
|
* lowered to {@link REDUCED_MOTION_FPS} when the OS requests reduced motion
|
|
@@ -1620,6 +1808,12 @@ export declare class Scene {
|
|
|
1620
1808
|
/**
|
|
1621
1809
|
* Render the entire scene graph onto the specified renderer.
|
|
1622
1810
|
*
|
|
1811
|
+
* This is the `CanvasProjection` row of {@link ProjectionBackend}:
|
|
1812
|
+
* the per-frame materialization of scene semantics into pixels through the
|
|
1813
|
+
* backend-agnostic `IRenderer` contract. (The `A11yProjection` /
|
|
1814
|
+
* `ContentProjection` rows run on the same cadence via {@link Scene.syncA11y}
|
|
1815
|
+
* below; the `DOMProjection` row does not exist yet — RFC2/CTX-0598.)
|
|
1816
|
+
*
|
|
1623
1817
|
* Main-frame causal order is a correctness contract:
|
|
1624
1818
|
*
|
|
1625
1819
|
* 1. Browser/input callbacks finish before the scheduled frame begins.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Entity, Point } from '../Entity';
|
|
2
|
+
/**
|
|
3
|
+
* Which medium painted the hit node (RFC5 §2, CTX-0600).
|
|
4
|
+
*
|
|
5
|
+
* - `'canvas'` — the canvas spatial test (`Scene.findEntityAt`).
|
|
6
|
+
* - `'mirror'` — an `a11yRoot` mirror element under the cursor.
|
|
7
|
+
* - `'portal'` — a `portalRoot` element (`DOMPortalEntity` bridge precedent,
|
|
8
|
+
* `tree/DOMPortalEntity.ts:64-99`).
|
|
9
|
+
* - `'dom-visual'` — extension point for CTX-0598's DOM backend: accepted and
|
|
10
|
+
* attributed through the same predicate path today, without depending on
|
|
11
|
+
* that package existing yet.
|
|
12
|
+
*/
|
|
13
|
+
export type HitBackend = 'canvas' | 'dom-visual' | 'mirror' | 'portal';
|
|
14
|
+
/**
|
|
15
|
+
* One candidate in the merged hit list (RFC5 §2).
|
|
16
|
+
*
|
|
17
|
+
* Coordinates enter in scene space and attribution follows (RFC5 §2 rule 3):
|
|
18
|
+
* `worldPoint` is the queried scene-space point, `localPoint` is that point
|
|
19
|
+
* in the node's local space (`Entity.worldToLocal`, `null` when the
|
|
20
|
+
* accumulated transform is singular).
|
|
21
|
+
*
|
|
22
|
+
* `priority` is compositor-layer order, then paint order (RFC5 §2 rule 2 and
|
|
23
|
+
* §3): the overlay subtree sorts above the main tree, and within one subtree
|
|
24
|
+
* DOM-native backends sort above canvas. Computed as
|
|
25
|
+
* `(inOverlaySubtree ? 2 : 0) + (backend === 'canvas' ? 0 : 1)`, so
|
|
26
|
+
* overlay-DOM (3) > overlay-canvas (2) > main-DOM (1) > main-canvas (0).
|
|
27
|
+
* Sort descending; ties keep insertion order (stable sort).
|
|
28
|
+
*/
|
|
29
|
+
export interface HitResult {
|
|
30
|
+
node: Entity;
|
|
31
|
+
backend: HitBackend;
|
|
32
|
+
localPoint: Point | null;
|
|
33
|
+
worldPoint: Point;
|
|
34
|
+
priority: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A caller-observed DOM-native hit offered to the merge
|
|
38
|
+
* (`HitTester.findHitsAt`). The browser's own hit test over the
|
|
39
|
+
* `pointer-events: auto` element is the geometric authority, so the merge
|
|
40
|
+
* gates the owning node with the same visibility/input predicate as the
|
|
41
|
+
* canvas paths (`disabled` / `pointerEvents: 'none'` exclusion, opacity,
|
|
42
|
+
* `clipChildren`) but does not re-check scene-space `isPointInside`.
|
|
43
|
+
*
|
|
44
|
+
* `'canvas'` is excluded: canvas candidates come from the spatial test, never
|
|
45
|
+
* from the caller.
|
|
46
|
+
*/
|
|
47
|
+
export interface DomHitCandidate {
|
|
48
|
+
node: Entity;
|
|
49
|
+
backend: Exclude<HitBackend, 'canvas'>;
|
|
50
|
+
}
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
* captured at construction without going stale.
|
|
57
57
|
*/
|
|
58
58
|
import type { Bounds, Entity } from '../Entity';
|
|
59
|
+
import type { DomHitCandidate, HitResult } from './HitResult';
|
|
59
60
|
import type { WasmBackendFacade } from './WasmBackendFacade';
|
|
60
61
|
export declare class HitTester {
|
|
61
62
|
private readonly root;
|
|
@@ -76,6 +77,35 @@ export declare class HitTester {
|
|
|
76
77
|
* @param height - Scene logical height, for the grid's extent.
|
|
77
78
|
*/
|
|
78
79
|
findEntityAt(x: number, y: number, frame: number, width: number, height: number): Entity | null;
|
|
80
|
+
/**
|
|
81
|
+
* The merged hit list (RFC5 §2, CTX-0600): the canvas spatial test plus
|
|
82
|
+
* caller-observed DOM-native candidates (mirror / portal / `dom-visual`
|
|
83
|
+
* extension point for CTX-0598) as ONE ordered candidate list.
|
|
84
|
+
*
|
|
85
|
+
* Query API only — no dispatch change: `findEntityAt` keeps its single
|
|
86
|
+
* topmost answer byte-for-byte. Coordinates enter in scene space (callers
|
|
87
|
+
* map client coordinates first, e.g. `Scene.findHitsAtClient`); backend
|
|
88
|
+
* attribution follows. DOM candidates pass through the same
|
|
89
|
+
* `disabled` / `pointerEvents: 'none'` predicate as the canvas paths
|
|
90
|
+
* (RFC5 §2 rule 4); the browser's hit test is their geometric authority,
|
|
91
|
+
* so scene-space `isPointInside` is not re-checked.
|
|
92
|
+
*
|
|
93
|
+
* Overlay order is authoritative (RFC5 §2 rule 2): overlay-subtree
|
|
94
|
+
* candidates sort above main-tree candidates, DOM-native backends above
|
|
95
|
+
* canvas within one subtree. Sorted descending by `priority`; ties keep
|
|
96
|
+
* insertion order (canvas overlay, canvas main, then DOM in caller order).
|
|
97
|
+
*/
|
|
98
|
+
findHitsAt(x: number, y: number, frame: number, width: number, height: number, domCandidates?: DomHitCandidate[]): HitResult[];
|
|
99
|
+
/**
|
|
100
|
+
* Attribute one candidate: scene-space point plus node-local point, with
|
|
101
|
+
* the compositor-layer priority (overlay subtree above main tree,
|
|
102
|
+
* DOM-native backends above canvas within one subtree).
|
|
103
|
+
*/
|
|
104
|
+
private toHitResult;
|
|
105
|
+
/** Whether `node` lives under the overlay root (drawn above the main tree). */
|
|
106
|
+
private isInOverlaySubtree;
|
|
107
|
+
/** Main-tree arm of {@link findEntityAt}, shared with {@link findHitsAt}. */
|
|
108
|
+
private findMainTreeHit;
|
|
79
109
|
/**
|
|
80
110
|
* Refresh the hit-test grid for the CURRENT tree state if it is stale (a
|
|
81
111
|
* structural or transform change may have happened since the last build —
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { AffineTransform, Entity } from '../Entity';
|
|
2
|
+
/**
|
|
3
|
+
* Stable backend id for telemetry and capability negotiation (cf. RFC4).
|
|
4
|
+
*
|
|
5
|
+
* The three literals name the projections that exist today; the open-ended
|
|
6
|
+
* tail leaves room for RFC2+ backends without touching this type. `'content'`
|
|
7
|
+
* extends the RFC1 sketch's `canvas | dom | a11y` union: the sketch's table
|
|
8
|
+
* already lists `ContentProjection` as a first-class row, so the kind union
|
|
9
|
+
* names it too.
|
|
10
|
+
*/
|
|
11
|
+
export type ProjectionBackendKind = 'canvas' | 'dom' | 'a11y' | 'content' | (string & {});
|
|
12
|
+
/**
|
|
13
|
+
* A materialization of scene nodes in one medium: canvas pixels, an
|
|
14
|
+
* `HTMLElement` subtree, an accessibility node, or a future backend's own
|
|
15
|
+
* representation.
|
|
16
|
+
*
|
|
17
|
+
* This is vocabulary, not machinery (RFC1 §5, CTX-0597): it names the
|
|
18
|
+
* mount/update/unmount shape the existing projections already follow so
|
|
19
|
+
* RFC2 (DOM projection), RFC3 (semantic/a11y projection), and RFC4
|
|
20
|
+
* (projection policy) can each name which row they implement or consume.
|
|
21
|
+
* Nothing implements this interface yet — the rows below map each proposed
|
|
22
|
+
* backend onto the code that performs its role today, with the invariant
|
|
23
|
+
* from RFC1 §2: two projections of the same node must agree on geometry
|
|
24
|
+
* (world matrix), visibility, and lifecycle state, while each keeps its own
|
|
25
|
+
* medium-specific representation.
|
|
26
|
+
*
|
|
27
|
+
* | Backend (proposed name) | Exists today as | Mount site |
|
|
28
|
+
* | ----------------------- | --------------- | ---------- |
|
|
29
|
+
* | `CanvasProjection` (`'canvas'`) | the main render pass via `IRenderer` | `Scene.render(renderer)` (`tree/Scene.ts`) → per-entity `Entity.render` (`tree/Entity.ts`) |
|
|
30
|
+
* | `A11yProjection` (`'a11y'`) | `A11yProjectionManager` + `a11yRoot` mirrors | mirror creation in the `Scene.syncA11y` walk (`tree/Scene.ts`); ordering in `A11yProjectionManager` |
|
|
31
|
+
* | `ContentProjection` (`'content'`) | `ContentProjectionManager` + `Entity.getContentProjection` | `Scene.syncContentProjection`, driven on the a11y sync cadence (`tree/Scene.ts`) |
|
|
32
|
+
* | `DOMProjection` (`'dom'`) | **does not exist** — RFC2's work item (CTX-0598) | — (`DOMPortalEntity` is the pilot consumer, not the backend) |
|
|
33
|
+
*
|
|
34
|
+
* Open points deliberately left to RFC2/RFC4 (RFC1 §5): whether `update`
|
|
35
|
+
* receives the full matrix or a decomposed transform, how z-order/layering
|
|
36
|
+
* composes canvas pixels under/over DOM elements, and event forwarding
|
|
37
|
+
* ownership (constrained by `forge/input-dispatch-contract-v2-design.md` §4).
|
|
38
|
+
*/
|
|
39
|
+
export interface ProjectionBackend {
|
|
40
|
+
/** Stable backend id for telemetry and capability negotiation (cf. RFC4). */
|
|
41
|
+
readonly kind: ProjectionBackendKind;
|
|
42
|
+
/** Create backend state for a newly materialized node. Idempotent. */
|
|
43
|
+
mount(node: Entity): void;
|
|
44
|
+
/**
|
|
45
|
+
* Reconcile backend state after a layout/transform/visibility change.
|
|
46
|
+
*
|
|
47
|
+
* Takes the node's {@link Entity.getWorldTransform | world transform} as
|
|
48
|
+
* {@link AffineTransform} rather than the sketch's `Float64Array`: that is
|
|
49
|
+
* what the scene computes today, and it converts losslessly to the
|
|
50
|
+
* `matrix3d()` a DOM backend wants. The matrix-vs-decomposed question stays
|
|
51
|
+
* open for RFC2/RFC4 per RFC1 §5.
|
|
52
|
+
*/
|
|
53
|
+
update(node: Entity, worldMatrix: AffineTransform): void;
|
|
54
|
+
/** Tear down backend state on removal or policy change. Idempotent. */
|
|
55
|
+
unmount(node: Entity): void;
|
|
56
|
+
/**
|
|
57
|
+
* Whether `node` currently owns an active gesture on this backend (RFC4 §5,
|
|
58
|
+
* CTX-0601). Consulted by the scene's capability negotiation alongside its
|
|
59
|
+
* own mirror pins: a pinned node keeps its backend until gesture end —
|
|
60
|
+
* input-dispatch-contract-v2 §4 gesture stickiness, no mid-gesture handoff.
|
|
61
|
+
* Optional so test fakes stay three methods; absent means "no gestures".
|
|
62
|
+
*/
|
|
63
|
+
hasActiveGesture?(node: Entity): boolean;
|
|
64
|
+
}
|