@fundamental-engine/dom 0.7.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/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/apply-recipe.d.ts +103 -0
- package/dist/apply-recipe.d.ts.map +1 -0
- package/dist/apply-recipe.js +271 -0
- package/dist/apply-recipe.js.map +1 -0
- package/dist/bind-data.d.ts +72 -0
- package/dist/bind-data.d.ts.map +1 -0
- package/dist/bind-data.js +164 -0
- package/dist/bind-data.js.map +1 -0
- package/dist/browser-host.d.ts +11 -0
- package/dist/browser-host.d.ts.map +1 -0
- package/dist/browser-host.js +41 -0
- package/dist/browser-host.js.map +1 -0
- package/dist/contours.d.ts +79 -0
- package/dist/contours.d.ts.map +1 -0
- package/dist/contours.js +88 -0
- package/dist/contours.js.map +1 -0
- package/dist/env.d.ts +39 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +47 -0
- package/dist/env.js.map +1 -0
- package/dist/export-dom.d.ts +7 -0
- package/dist/export-dom.d.ts.map +1 -0
- package/dist/export-dom.js +28 -0
- package/dist/export-dom.js.map +1 -0
- package/dist/feedback.d.ts +57 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +134 -0
- package/dist/feedback.js.map +1 -0
- package/dist/field-nav.d.ts +35 -0
- package/dist/field-nav.d.ts.map +1 -0
- package/dist/field-nav.js +82 -0
- package/dist/field-nav.js.map +1 -0
- package/dist/flip.d.ts +31 -0
- package/dist/flip.d.ts.map +1 -0
- package/dist/flip.js +65 -0
- package/dist/flip.js.map +1 -0
- package/dist/governor.d.ts +37 -0
- package/dist/governor.d.ts.map +1 -0
- package/dist/governor.js +72 -0
- package/dist/governor.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/lint.d.ts +78 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +153 -0
- package/dist/lint.js.map +1 -0
- package/dist/measurement.d.ts +44 -0
- package/dist/measurement.d.ts.map +1 -0
- package/dist/measurement.js +95 -0
- package/dist/measurement.js.map +1 -0
- package/dist/metrics.d.ts +70 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +119 -0
- package/dist/metrics.js.map +1 -0
- package/dist/overlays.d.ts +48 -0
- package/dist/overlays.d.ts.map +1 -0
- package/dist/overlays.js +48 -0
- package/dist/overlays.js.map +1 -0
- package/dist/perf.d.ts +62 -0
- package/dist/perf.d.ts.map +1 -0
- package/dist/perf.js +94 -0
- package/dist/perf.js.map +1 -0
- package/dist/platform.d.ts +40 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +61 -0
- package/dist/platform.js.map +1 -0
- package/dist/relationships.d.ts +79 -0
- package/dist/relationships.d.ts.map +1 -0
- package/dist/relationships.js +155 -0
- package/dist/relationships.js.map +1 -0
- package/dist/schedule.d.ts +84 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +91 -0
- package/dist/schedule.js.map +1 -0
- package/dist/state.d.ts +36 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +113 -0
- package/dist/state.js.map +1 -0
- package/dist/text-bodies.d.ts +71 -0
- package/dist/text-bodies.d.ts.map +1 -0
- package/dist/text-bodies.js +159 -0
- package/dist/text-bodies.js.map +1 -0
- package/dist/thread-overlay.d.ts +63 -0
- package/dist/thread-overlay.d.ts.map +1 -0
- package/dist/thread-overlay.js +110 -0
- package/dist/thread-overlay.js.map +1 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/visual-bindings.d.ts +95 -0
- package/dist/visual-bindings.d.ts.map +1 -0
- package/dist/visual-bindings.js +211 -0
- package/dist/visual-bindings.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* threadOverlay — the hover-thread SVG overlay extracted from the invisible-fields example
|
|
3
|
+
* runtimes (the `wireThreads`/`centerIn` pattern in the Evidence page, adapted by the Backlog
|
|
4
|
+
* board and the Dependencies spill). Three hand-rolled copies drew the same picture; this is
|
|
5
|
+
* the common core: an absolutely-positioned, `aria-hidden`, pointer-events-none SVG prepended
|
|
6
|
+
* into a host, with one cubic-bezier path per (from → target) edge and the family's `lit` /
|
|
7
|
+
* `cited` class marks on the endpoints.
|
|
8
|
+
*
|
|
9
|
+
* GEOMETRY + CLASSES ONLY — no event wiring. The pages own hover semantics (pointerenter →
|
|
10
|
+
* `draw`, pointerleave → `clear`), reveal pacing, and color choice; this primitive owns the
|
|
11
|
+
* SVG lifecycle and the math.
|
|
12
|
+
*
|
|
13
|
+
* The CSS contract (what the three pages already style, unchanged):
|
|
14
|
+
* - the overlay carries `opts.className` (default `field-threads`; the example family passes
|
|
15
|
+
* `ev-threads`) — pages style `.<className> path { stroke: var(--thread, …); fill: none; }`.
|
|
16
|
+
* - `draw(…, { color })` sets the overlay's `--thread` custom property; omitting `color`
|
|
17
|
+
* removes it, so the page's `var(--thread, fallback)` fallback shows.
|
|
18
|
+
* - the hovered element gains `.lit`, each resolved target gains `.cited`; `clear()` removes
|
|
19
|
+
* both from everything this overlay marked.
|
|
20
|
+
* - inline geometry (position:absolute; inset:0; width/height:100%; pointer-events:none) is
|
|
21
|
+
* written on creation so the overlay covers the host before page CSS loads. The HOST must
|
|
22
|
+
* be a containing block (`position: relative` or similar) — that part stays page CSS.
|
|
23
|
+
*
|
|
24
|
+
* Coordinates are host-relative (the `centerIn` math): the viewBox is sized from the host's
|
|
25
|
+
* current rect at each `draw`, and every path runs center-to-center through the family's
|
|
26
|
+
* midpoint-y cubic — `M ax ay C ax my, bx my, bx by` with `my = (ay + by) / 2`.
|
|
27
|
+
*
|
|
28
|
+
* Geometry is sampled AT DRAW TIME and not observed afterwards: callers must re-draw (or
|
|
29
|
+
* `clear()`) after layout changes — FLIP re-sorts, batch reveals, container resize. The
|
|
30
|
+
* example pages clear on re-sort and re-draw on the next hover; that discipline is the
|
|
31
|
+
* caller's.
|
|
32
|
+
*
|
|
33
|
+
* SSR-safe: construction touches no globals (the SVG is created lazily via
|
|
34
|
+
* `host.ownerDocument` on first `draw`); `clear()`/`destroy()` are no-ops before that.
|
|
35
|
+
*/
|
|
36
|
+
export interface ThreadOverlayOptions {
|
|
37
|
+
/** class for the overlay SVG (default `field-threads`; the example family uses `ev-threads`). */
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface ThreadDrawOptions {
|
|
41
|
+
/** value for the overlay's `--thread` custom property (the stroke channel pages style against). Omitted → the property is removed and the page's CSS fallback applies. */
|
|
42
|
+
color?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface ThreadOverlay {
|
|
45
|
+
/**
|
|
46
|
+
* Draw one thread per target: host-relative center-to-center cubic beziers from `from`,
|
|
47
|
+
* marking `from` with `.lit` and each target with `.cited`. Replaces the previous draw
|
|
48
|
+
* (paths and class marks). Reads layout — call outside the platform's write phase.
|
|
49
|
+
*/
|
|
50
|
+
draw(from: HTMLElement, targets: readonly HTMLElement[], opts?: ThreadDrawOptions): void;
|
|
51
|
+
/** Empty the overlay's paths and remove every `lit`/`cited` mark this overlay applied. */
|
|
52
|
+
clear(): void;
|
|
53
|
+
/** `clear()` + remove the SVG from the host. `clear()` is a no-op afterwards; a later `draw()` recreates the overlay. */
|
|
54
|
+
destroy(): void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create (or adopt) a thread overlay on `host`. If a `svg.<className>` already exists in the
|
|
58
|
+
* host it is reused — so a page that server-renders the overlay shell, or re-inits its
|
|
59
|
+
* runtime, never stacks duplicates. Otherwise the SVG is created lazily on the first `draw`
|
|
60
|
+
* and PREPENDED (the evidence pattern: threads paint under the host's content in DOM order).
|
|
61
|
+
*/
|
|
62
|
+
export declare function threadOverlay(host: HTMLElement, opts?: ThreadOverlayOptions): ThreadOverlay;
|
|
63
|
+
//# sourceMappingURL=thread-overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-overlay.d.ts","sourceRoot":"","sources":["../src/thread-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,MAAM,WAAW,oBAAoB;IACnC,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,0KAA0K;IAC1K,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACzF,0FAA0F;IAC1F,KAAK,IAAI,IAAI,CAAC;IACd,yHAAyH;IACzH,OAAO,IAAI,IAAI,CAAC;CACjB;AAQD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,GAAE,oBAAyB,GAAG,aAAa,CA8D/F"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* threadOverlay — the hover-thread SVG overlay extracted from the invisible-fields example
|
|
3
|
+
* runtimes (the `wireThreads`/`centerIn` pattern in the Evidence page, adapted by the Backlog
|
|
4
|
+
* board and the Dependencies spill). Three hand-rolled copies drew the same picture; this is
|
|
5
|
+
* the common core: an absolutely-positioned, `aria-hidden`, pointer-events-none SVG prepended
|
|
6
|
+
* into a host, with one cubic-bezier path per (from → target) edge and the family's `lit` /
|
|
7
|
+
* `cited` class marks on the endpoints.
|
|
8
|
+
*
|
|
9
|
+
* GEOMETRY + CLASSES ONLY — no event wiring. The pages own hover semantics (pointerenter →
|
|
10
|
+
* `draw`, pointerleave → `clear`), reveal pacing, and color choice; this primitive owns the
|
|
11
|
+
* SVG lifecycle and the math.
|
|
12
|
+
*
|
|
13
|
+
* The CSS contract (what the three pages already style, unchanged):
|
|
14
|
+
* - the overlay carries `opts.className` (default `field-threads`; the example family passes
|
|
15
|
+
* `ev-threads`) — pages style `.<className> path { stroke: var(--thread, …); fill: none; }`.
|
|
16
|
+
* - `draw(…, { color })` sets the overlay's `--thread` custom property; omitting `color`
|
|
17
|
+
* removes it, so the page's `var(--thread, fallback)` fallback shows.
|
|
18
|
+
* - the hovered element gains `.lit`, each resolved target gains `.cited`; `clear()` removes
|
|
19
|
+
* both from everything this overlay marked.
|
|
20
|
+
* - inline geometry (position:absolute; inset:0; width/height:100%; pointer-events:none) is
|
|
21
|
+
* written on creation so the overlay covers the host before page CSS loads. The HOST must
|
|
22
|
+
* be a containing block (`position: relative` or similar) — that part stays page CSS.
|
|
23
|
+
*
|
|
24
|
+
* Coordinates are host-relative (the `centerIn` math): the viewBox is sized from the host's
|
|
25
|
+
* current rect at each `draw`, and every path runs center-to-center through the family's
|
|
26
|
+
* midpoint-y cubic — `M ax ay C ax my, bx my, bx by` with `my = (ay + by) / 2`.
|
|
27
|
+
*
|
|
28
|
+
* Geometry is sampled AT DRAW TIME and not observed afterwards: callers must re-draw (or
|
|
29
|
+
* `clear()`) after layout changes — FLIP re-sorts, batch reveals, container resize. The
|
|
30
|
+
* example pages clear on re-sort and re-draw on the next hover; that discipline is the
|
|
31
|
+
* caller's.
|
|
32
|
+
*
|
|
33
|
+
* SSR-safe: construction touches no globals (the SVG is created lazily via
|
|
34
|
+
* `host.ownerDocument` on first `draw`); `clear()`/`destroy()` are no-ops before that.
|
|
35
|
+
*/
|
|
36
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
37
|
+
/** An element's center in host-rect coordinates (the family's `centerIn`). */
|
|
38
|
+
function centerIn(el, hostRect) {
|
|
39
|
+
const r = el.getBoundingClientRect();
|
|
40
|
+
return { x: r.left - hostRect.left + r.width / 2, y: r.top - hostRect.top + r.height / 2 };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create (or adopt) a thread overlay on `host`. If a `svg.<className>` already exists in the
|
|
44
|
+
* host it is reused — so a page that server-renders the overlay shell, or re-inits its
|
|
45
|
+
* runtime, never stacks duplicates. Otherwise the SVG is created lazily on the first `draw`
|
|
46
|
+
* and PREPENDED (the evidence pattern: threads paint under the host's content in DOM order).
|
|
47
|
+
*/
|
|
48
|
+
export function threadOverlay(host, opts = {}) {
|
|
49
|
+
const className = opts.className ?? 'field-threads';
|
|
50
|
+
const selector = 'svg.' + className.trim().split(/\s+/).join('.');
|
|
51
|
+
let svg = null;
|
|
52
|
+
const marked = new Set();
|
|
53
|
+
const ensure = () => {
|
|
54
|
+
if (svg)
|
|
55
|
+
return svg;
|
|
56
|
+
svg = host.querySelector(selector);
|
|
57
|
+
if (!svg) {
|
|
58
|
+
svg = host.ownerDocument.createElementNS(SVG_NS, 'svg');
|
|
59
|
+
svg.setAttribute('class', className);
|
|
60
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
61
|
+
// cover the host even before page CSS loads; the host supplies the containing block.
|
|
62
|
+
svg.style.setProperty('position', 'absolute');
|
|
63
|
+
svg.style.setProperty('inset', '0');
|
|
64
|
+
svg.style.setProperty('width', '100%');
|
|
65
|
+
svg.style.setProperty('height', '100%');
|
|
66
|
+
svg.style.setProperty('pointer-events', 'none');
|
|
67
|
+
host.prepend(svg);
|
|
68
|
+
}
|
|
69
|
+
return svg;
|
|
70
|
+
};
|
|
71
|
+
const unmark = () => {
|
|
72
|
+
for (const el of marked)
|
|
73
|
+
el.classList.remove('lit', 'cited');
|
|
74
|
+
marked.clear();
|
|
75
|
+
};
|
|
76
|
+
const draw = (from, targets, drawOpts = {}) => {
|
|
77
|
+
const overlay = ensure();
|
|
78
|
+
unmark();
|
|
79
|
+
const box = host.getBoundingClientRect();
|
|
80
|
+
overlay.setAttribute('viewBox', `0 0 ${box.width} ${box.height}`);
|
|
81
|
+
if (drawOpts.color != null && drawOpts.color !== '')
|
|
82
|
+
overlay.style.setProperty('--thread', drawOpts.color);
|
|
83
|
+
else
|
|
84
|
+
overlay.style.removeProperty('--thread');
|
|
85
|
+
const a = centerIn(from, box);
|
|
86
|
+
from.classList.add('lit');
|
|
87
|
+
marked.add(from);
|
|
88
|
+
let d = '';
|
|
89
|
+
for (const t of targets) {
|
|
90
|
+
t.classList.add('cited');
|
|
91
|
+
marked.add(t);
|
|
92
|
+
const b = centerIn(t, box);
|
|
93
|
+
const my = (a.y + b.y) / 2;
|
|
94
|
+
d += `<path d="M${a.x} ${a.y} C ${a.x} ${my}, ${b.x} ${my}, ${b.x} ${b.y}"/>`;
|
|
95
|
+
}
|
|
96
|
+
overlay.innerHTML = d;
|
|
97
|
+
};
|
|
98
|
+
const clear = () => {
|
|
99
|
+
if (svg)
|
|
100
|
+
svg.innerHTML = '';
|
|
101
|
+
unmark();
|
|
102
|
+
};
|
|
103
|
+
const destroy = () => {
|
|
104
|
+
clear();
|
|
105
|
+
svg?.remove();
|
|
106
|
+
svg = null;
|
|
107
|
+
};
|
|
108
|
+
return { draw, clear, destroy };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=thread-overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-overlay.js","sourceRoot":"","sources":["../src/thread-overlay.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fundamental-engine/dom — shared contracts for the platform-adjacent registries. These describe the
|
|
3
|
+
* native primitives Fundamental wishes the browser had (frame-stable geometry, typed element state,
|
|
4
|
+
* relationships, visual-semantic pairing) built on the ones it has. Pure data shapes; no DOM here.
|
|
5
|
+
*/
|
|
6
|
+
/** A measured rectangle in a coordinate space, with the centre precomputed. */
|
|
7
|
+
export interface FieldRect {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
cx: number;
|
|
13
|
+
cy: number;
|
|
14
|
+
top: number;
|
|
15
|
+
right: number;
|
|
16
|
+
bottom: number;
|
|
17
|
+
left: number;
|
|
18
|
+
}
|
|
19
|
+
export type CoordinateSpace = 'viewport' | 'document' | 'field-root' | 'canvas';
|
|
20
|
+
/** One element's frame-stable measurement. */
|
|
21
|
+
export interface FieldMeasurement {
|
|
22
|
+
element: Element;
|
|
23
|
+
rect: FieldRect;
|
|
24
|
+
visible: boolean;
|
|
25
|
+
/** fraction of the element's area within the viewport, ∈ [0,1]. */
|
|
26
|
+
visibilityRatio: number;
|
|
27
|
+
coordinateSpace: CoordinateSpace;
|
|
28
|
+
/** the frame time this snapshot was taken. */
|
|
29
|
+
timestamp: number;
|
|
30
|
+
}
|
|
31
|
+
/** A typed, observable element state value — numeric/boolean/string/vector2 (NOT ARIA). */
|
|
32
|
+
export type FieldStateValue = {
|
|
33
|
+
type: 'number';
|
|
34
|
+
value: number;
|
|
35
|
+
} | {
|
|
36
|
+
type: 'boolean';
|
|
37
|
+
value: boolean;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'string';
|
|
40
|
+
value: string;
|
|
41
|
+
} | {
|
|
42
|
+
type: 'vector2';
|
|
43
|
+
x: number;
|
|
44
|
+
y: number;
|
|
45
|
+
};
|
|
46
|
+
/** A viewport box for visibility computation (so measurement is testable without a window). */
|
|
47
|
+
export interface Viewport {
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEhF,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,eAAe,CAAC;IACjC,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,2FAA2F;AAC3F,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,+FAA+F;AAC/F,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fundamental-engine/dom — shared contracts for the platform-adjacent registries. These describe the
|
|
3
|
+
* native primitives Fundamental wishes the browser had (frame-stable geometry, typed element state,
|
|
4
|
+
* relationships, visual-semantic pairing) built on the ones it has. Pure data shapes; no DOM here.
|
|
5
|
+
*/
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VisualBindingRegistry — bind an expressive visual layer (SVG, Canvas, WebGL) to its semantic DOM
|
|
3
|
+
* source without duplicating meaning or harming accessibility. The native primitive Fundamental wishes
|
|
4
|
+
* existed: a declarative "this visual represents that semantic element; don't double-expose it".
|
|
5
|
+
*
|
|
6
|
+
* The semantic source stays real HTML; the visual is `aria-hidden` unless it carries independent
|
|
7
|
+
* meaning. `lint()` flags orphan visuals and accessibility hazards.
|
|
8
|
+
*
|
|
9
|
+
* Declarative authoring: mark the visual with `data-field-visual-for` (id/selector of the semantic
|
|
10
|
+
* source) and `data-field-visual-role`, then call `scan(root)` to register it. This is the bridge from
|
|
11
|
+
* semantic HTML + data attributes → VisualBindingRegistry
|
|
12
|
+
* It does NOT extract glyph outlines or generate vector geometry — it only binds an authored visual
|
|
13
|
+
* layer to its source so the field can lint and inspect the pairing.
|
|
14
|
+
*
|
|
15
|
+
* State mirroring (Body Matter Interaction — the Bound Visual tier): the element absorbs field
|
|
16
|
+
* matter; the visual layer shows what that absorption means; the semantic text remains the source
|
|
17
|
+
* of meaning. CSS custom properties don't cross to siblings, so with mirroring enabled
|
|
18
|
+
* (`setMirroring(true)` — the platform default) every `representation`/`measurement` visual
|
|
19
|
+
* receives its source's feedback channels (`--d`/`--field-density`, `--load`/`--mass`, the measured
|
|
20
|
+
* metrics — see MIRRORED_CHANNELS) copied onto its own inline style: an aria-hidden SVG beside a
|
|
21
|
+
* sink heading thickens its contours from `var(--load)` exactly as if it were the body itself.
|
|
22
|
+
* Change-gated via a MutationObserver on the source's style attribute — no polling, no frame cost
|
|
23
|
+
* while the field is quiet.
|
|
24
|
+
*/
|
|
25
|
+
export type VisualRole = 'decorative' | 'representation' | 'debug' | 'relationship' | 'measurement';
|
|
26
|
+
export interface VisualBinding {
|
|
27
|
+
visual: Element;
|
|
28
|
+
semanticSource: Element | null;
|
|
29
|
+
role: VisualRole;
|
|
30
|
+
accessibility: {
|
|
31
|
+
ariaHidden: boolean;
|
|
32
|
+
/** does this role require a semantic source to exist? */
|
|
33
|
+
semanticSourceRequired: boolean;
|
|
34
|
+
/** would screen readers see duplicate text from the visual? (source-bound but not hidden) */
|
|
35
|
+
duplicateSemantics: boolean;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export interface VisualLintWarning {
|
|
39
|
+
visual: Element;
|
|
40
|
+
code: 'orphan-representation' | 'visual-not-hidden' | 'duplicate-semantics';
|
|
41
|
+
severity: 'warning' | 'error';
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
/** A warning raised while scanning the DOM for declarative bindings (resolution-time, not a11y). */
|
|
45
|
+
export interface VisualBindingScanWarning {
|
|
46
|
+
element: Element;
|
|
47
|
+
/** 'unresolved-source' | 'missing-source' | 'invalid-role' */
|
|
48
|
+
reason: string;
|
|
49
|
+
value?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface VisualBindingScanResult {
|
|
52
|
+
total: number;
|
|
53
|
+
bound: number;
|
|
54
|
+
unresolved: number;
|
|
55
|
+
warnings: VisualBindingScanWarning[];
|
|
56
|
+
}
|
|
57
|
+
/** The feedback channels a bound visual mirrors from its semantic source — the engine's write-back
|
|
58
|
+
* lanes (density, sink load, causality, measured metrics). One list, shared with the docs. */
|
|
59
|
+
export declare const MIRRORED_CHANNELS: readonly string[];
|
|
60
|
+
export declare class VisualBindingRegistry {
|
|
61
|
+
private readonly bindings;
|
|
62
|
+
private mirroring;
|
|
63
|
+
private readonly observers;
|
|
64
|
+
/** Bind a visual layer to a semantic source. `representation`/`relationship` need a source. */
|
|
65
|
+
bind(opts: {
|
|
66
|
+
visual: Element;
|
|
67
|
+
source?: Element | null;
|
|
68
|
+
role: VisualRole;
|
|
69
|
+
}): VisualBinding;
|
|
70
|
+
/**
|
|
71
|
+
* Turn source→visual state mirroring on or off for every current and future binding (the Bound
|
|
72
|
+
* Visual tier of Body Matter Interaction). On: each `representation`/`measurement` visual gets an
|
|
73
|
+
* immediate copy of its source's MIRRORED_CHANNELS plus a style-attribute observer that re-copies
|
|
74
|
+
* whenever the engine writes the source (change-gated — quiet field, zero work). Off: observers
|
|
75
|
+
* disconnect; already-mirrored values are left in place (the visual keeps its last honest state).
|
|
76
|
+
*/
|
|
77
|
+
setMirroring(on: boolean): void;
|
|
78
|
+
/** Copy the source's current feedback channels onto the visual's inline style (one pass). */
|
|
79
|
+
mirrorNow(visual: Element): void;
|
|
80
|
+
private watch;
|
|
81
|
+
private unwatch;
|
|
82
|
+
/**
|
|
83
|
+
* Discover declarative bindings under `root`: elements carrying `data-field-visual-for` (the source
|
|
84
|
+
* id/selector) and/or `data-field-visual-role`. Calls `bind()` for each. Idempotent — bindings are
|
|
85
|
+
* keyed by the visual element, so re-scanning updates role/source/aria-hidden without duplicating,
|
|
86
|
+
* and disconnected visuals are pruned. `resolve` defaults to a document-backed resolver.
|
|
87
|
+
*/
|
|
88
|
+
scan(root: ParentNode, resolve?: (ref: string) => Element | null): VisualBindingScanResult;
|
|
89
|
+
get(visual: Element): VisualBinding | undefined;
|
|
90
|
+
all(): VisualBinding[];
|
|
91
|
+
get size(): number;
|
|
92
|
+
/** Accessibility lint over the bindings (visual-language §16 rules). */
|
|
93
|
+
lint(): VisualLintWarning[];
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=visual-bindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-bindings.d.ts","sourceRoot":"","sources":["../src/visual-bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,gBAAgB,GAAG,OAAO,GAAG,cAAc,GAAG,aAAa,CAAC;AAOpG,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,UAAU,CAAC;IACjB,aAAa,EAAE;QACb,UAAU,EAAE,OAAO,CAAC;QACpB,yDAAyD;QACzD,sBAAsB,EAAE,OAAO,CAAC;QAChC,6FAA6F;QAC7F,kBAAkB,EAAE,OAAO,CAAC;KAC7B,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,uBAAuB,GAAG,mBAAmB,GAAG,qBAAqB,CAAC;IAC5E,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,oGAAoG;AACpG,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,wBAAwB,EAAE,CAAC;CACtC;AAID;+FAC+F;AAC/F,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAU9C,CAAC;AA8BF,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAElE,+FAA+F;IAC/F,IAAI,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,GAAG,aAAa;IAmBzF;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI;IAU/B,6FAA6F;IAC7F,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAYhC,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,OAAO;IAQf;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GAAG,IAA4B,GAAG,uBAAuB;IAwCjH,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS;IAI/C,GAAG,IAAI,aAAa,EAAE;IAItB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,wEAAwE;IACxE,IAAI,IAAI,iBAAiB,EAAE;CAc5B"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VisualBindingRegistry — bind an expressive visual layer (SVG, Canvas, WebGL) to its semantic DOM
|
|
3
|
+
* source without duplicating meaning or harming accessibility. The native primitive Fundamental wishes
|
|
4
|
+
* existed: a declarative "this visual represents that semantic element; don't double-expose it".
|
|
5
|
+
*
|
|
6
|
+
* The semantic source stays real HTML; the visual is `aria-hidden` unless it carries independent
|
|
7
|
+
* meaning. `lint()` flags orphan visuals and accessibility hazards.
|
|
8
|
+
*
|
|
9
|
+
* Declarative authoring: mark the visual with `data-field-visual-for` (id/selector of the semantic
|
|
10
|
+
* source) and `data-field-visual-role`, then call `scan(root)` to register it. This is the bridge from
|
|
11
|
+
* semantic HTML + data attributes → VisualBindingRegistry
|
|
12
|
+
* It does NOT extract glyph outlines or generate vector geometry — it only binds an authored visual
|
|
13
|
+
* layer to its source so the field can lint and inspect the pairing.
|
|
14
|
+
*
|
|
15
|
+
* State mirroring (Body Matter Interaction — the Bound Visual tier): the element absorbs field
|
|
16
|
+
* matter; the visual layer shows what that absorption means; the semantic text remains the source
|
|
17
|
+
* of meaning. CSS custom properties don't cross to siblings, so with mirroring enabled
|
|
18
|
+
* (`setMirroring(true)` — the platform default) every `representation`/`measurement` visual
|
|
19
|
+
* receives its source's feedback channels (`--d`/`--field-density`, `--load`/`--mass`, the measured
|
|
20
|
+
* metrics — see MIRRORED_CHANNELS) copied onto its own inline style: an aria-hidden SVG beside a
|
|
21
|
+
* sink heading thickens its contours from `var(--load)` exactly as if it were the body itself.
|
|
22
|
+
* Change-gated via a MutationObserver on the source's style attribute — no polling, no frame cost
|
|
23
|
+
* while the field is quiet.
|
|
24
|
+
*/
|
|
25
|
+
const VISUAL_ROLES = ['decorative', 'representation', 'debug', 'relationship', 'measurement'];
|
|
26
|
+
const isVisualRole = (v) => v != null && VISUAL_ROLES.includes(v);
|
|
27
|
+
/** roles that re-present existing meaning and therefore require a semantic source. */
|
|
28
|
+
const requiresSource = (role) => role === 'representation' || role === 'relationship';
|
|
29
|
+
const ariaHidden = (el) => el.getAttribute('aria-hidden') === 'true';
|
|
30
|
+
/** The feedback channels a bound visual mirrors from its semantic source — the engine's write-back
|
|
31
|
+
* lanes (density, sink load, causality, measured metrics). One list, shared with the docs. */
|
|
32
|
+
export const MIRRORED_CHANNELS = [
|
|
33
|
+
'--d',
|
|
34
|
+
'--field-density',
|
|
35
|
+
'--load',
|
|
36
|
+
'--mass',
|
|
37
|
+
'--lit',
|
|
38
|
+
'--entropy',
|
|
39
|
+
'--coherence',
|
|
40
|
+
'--temperature',
|
|
41
|
+
'--field-heatmap-density',
|
|
42
|
+
];
|
|
43
|
+
/** roles that re-present source state and therefore receive mirrored feedback channels. */
|
|
44
|
+
const mirrorsState = (b) => b.semanticSource != null && (b.role === 'representation' || b.role === 'measurement');
|
|
45
|
+
/** Build the default document-backed source resolver for a scan root (the §"resolution policy"). */
|
|
46
|
+
function defaultResolver(root) {
|
|
47
|
+
const doc = root.nodeType === 9 ? root : (root.ownerDocument ?? null);
|
|
48
|
+
const trySelector = (host, sel) => {
|
|
49
|
+
if (!host || typeof host.querySelector !== 'function')
|
|
50
|
+
return null;
|
|
51
|
+
try {
|
|
52
|
+
return host.querySelector(sel);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null; // invalid selector → not a crash
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return (ref) => {
|
|
59
|
+
if (!ref)
|
|
60
|
+
return null;
|
|
61
|
+
if (ref.startsWith('#'))
|
|
62
|
+
return trySelector(root, ref) ?? trySelector(doc, ref);
|
|
63
|
+
// bare value: prefer getElementById, then fall back to a (validated) selector
|
|
64
|
+
const byId = doc?.getElementById?.(ref) ?? null;
|
|
65
|
+
if (byId)
|
|
66
|
+
return byId;
|
|
67
|
+
return trySelector(root, ref);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export class VisualBindingRegistry {
|
|
71
|
+
bindings = new Map();
|
|
72
|
+
mirroring = false;
|
|
73
|
+
observers = new Map();
|
|
74
|
+
/** Bind a visual layer to a semantic source. `representation`/`relationship` need a source. */
|
|
75
|
+
bind(opts) {
|
|
76
|
+
const required = requiresSource(opts.role);
|
|
77
|
+
const hidden = ariaHidden(opts.visual);
|
|
78
|
+
const b = {
|
|
79
|
+
visual: opts.visual,
|
|
80
|
+
semanticSource: opts.source ?? null,
|
|
81
|
+
role: opts.role,
|
|
82
|
+
accessibility: {
|
|
83
|
+
ariaHidden: hidden,
|
|
84
|
+
semanticSourceRequired: required,
|
|
85
|
+
// a source-bound visual that is NOT hidden re-exposes the source's meaning to AT
|
|
86
|
+
duplicateSemantics: required && !hidden && !!opts.source,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
this.bindings.set(opts.visual, b);
|
|
90
|
+
if (this.mirroring)
|
|
91
|
+
this.watch(b);
|
|
92
|
+
return b;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Turn source→visual state mirroring on or off for every current and future binding (the Bound
|
|
96
|
+
* Visual tier of Body Matter Interaction). On: each `representation`/`measurement` visual gets an
|
|
97
|
+
* immediate copy of its source's MIRRORED_CHANNELS plus a style-attribute observer that re-copies
|
|
98
|
+
* whenever the engine writes the source (change-gated — quiet field, zero work). Off: observers
|
|
99
|
+
* disconnect; already-mirrored values are left in place (the visual keeps its last honest state).
|
|
100
|
+
*/
|
|
101
|
+
setMirroring(on) {
|
|
102
|
+
this.mirroring = on;
|
|
103
|
+
if (!on) {
|
|
104
|
+
for (const obs of this.observers.values())
|
|
105
|
+
obs.disconnect();
|
|
106
|
+
this.observers.clear();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
for (const b of this.bindings.values())
|
|
110
|
+
this.watch(b);
|
|
111
|
+
}
|
|
112
|
+
/** Copy the source's current feedback channels onto the visual's inline style (one pass). */
|
|
113
|
+
mirrorNow(visual) {
|
|
114
|
+
const b = this.bindings.get(visual);
|
|
115
|
+
if (!b || !mirrorsState(b))
|
|
116
|
+
return;
|
|
117
|
+
const src = b.semanticSource;
|
|
118
|
+
const dst = b.visual;
|
|
119
|
+
if (!src.style?.getPropertyValue || !dst.style?.setProperty)
|
|
120
|
+
return; // non-styled host (tests, SSR)
|
|
121
|
+
for (const channel of MIRRORED_CHANNELS) {
|
|
122
|
+
const value = src.style.getPropertyValue(channel);
|
|
123
|
+
if (value !== '' && value !== dst.style.getPropertyValue(channel))
|
|
124
|
+
dst.style.setProperty(channel, value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
watch(b) {
|
|
128
|
+
this.unwatch(b.visual);
|
|
129
|
+
if (!mirrorsState(b))
|
|
130
|
+
return;
|
|
131
|
+
this.mirrorNow(b.visual);
|
|
132
|
+
if (typeof MutationObserver === 'undefined' || !b.semanticSource)
|
|
133
|
+
return;
|
|
134
|
+
const obs = new MutationObserver(() => this.mirrorNow(b.visual));
|
|
135
|
+
obs.observe(b.semanticSource, { attributes: true, attributeFilter: ['style'] });
|
|
136
|
+
this.observers.set(b.visual, obs);
|
|
137
|
+
}
|
|
138
|
+
unwatch(visual) {
|
|
139
|
+
const obs = this.observers.get(visual);
|
|
140
|
+
if (obs) {
|
|
141
|
+
obs.disconnect();
|
|
142
|
+
this.observers.delete(visual);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Discover declarative bindings under `root`: elements carrying `data-field-visual-for` (the source
|
|
147
|
+
* id/selector) and/or `data-field-visual-role`. Calls `bind()` for each. Idempotent — bindings are
|
|
148
|
+
* keyed by the visual element, so re-scanning updates role/source/aria-hidden without duplicating,
|
|
149
|
+
* and disconnected visuals are pruned. `resolve` defaults to a document-backed resolver.
|
|
150
|
+
*/
|
|
151
|
+
scan(root, resolve = defaultResolver(root)) {
|
|
152
|
+
// navigation hygiene: drop bindings whose visual OR source left the DOM (and stop mirroring
|
|
153
|
+
// them). Pruning only on the visual leaves a MutationObserver pinned to a removed source —
|
|
154
|
+
// holding that detached source subtree alive for as long as the visual stays mounted.
|
|
155
|
+
for (const [el, b] of this.bindings)
|
|
156
|
+
if (el.isConnected === false || b.semanticSource?.isConnected === false) {
|
|
157
|
+
this.unwatch(el);
|
|
158
|
+
this.bindings.delete(el);
|
|
159
|
+
}
|
|
160
|
+
const visuals = [...root.querySelectorAll('[data-field-visual-for], [data-field-visual-role], [data-visual-for]')];
|
|
161
|
+
const warnings = [];
|
|
162
|
+
let unresolved = 0;
|
|
163
|
+
for (const visual of visuals) {
|
|
164
|
+
const value = visual.getAttribute('data-field-visual-for') ?? visual.getAttribute('data-visual-for');
|
|
165
|
+
const roleAttr = visual.getAttribute('data-field-visual-role');
|
|
166
|
+
let role;
|
|
167
|
+
if (roleAttr == null) {
|
|
168
|
+
role = 'representation'; // role omitted but selected via data-field-visual-for → representation
|
|
169
|
+
}
|
|
170
|
+
else if (isVisualRole(roleAttr)) {
|
|
171
|
+
role = roleAttr;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
role = 'representation'; // invalid role → default + warn
|
|
175
|
+
warnings.push({ element: visual, reason: 'invalid-role', value: roleAttr });
|
|
176
|
+
}
|
|
177
|
+
const source = value ? resolve(value) : null;
|
|
178
|
+
if (requiresSource(role) && !source) {
|
|
179
|
+
unresolved++;
|
|
180
|
+
warnings.push({ element: visual, reason: value ? 'unresolved-source' : 'missing-source', value: value ?? undefined });
|
|
181
|
+
}
|
|
182
|
+
this.bind({ visual, source, role });
|
|
183
|
+
}
|
|
184
|
+
return { total: visuals.length, bound: visuals.length, unresolved, warnings };
|
|
185
|
+
}
|
|
186
|
+
get(visual) {
|
|
187
|
+
return this.bindings.get(visual);
|
|
188
|
+
}
|
|
189
|
+
all() {
|
|
190
|
+
return [...this.bindings.values()];
|
|
191
|
+
}
|
|
192
|
+
get size() {
|
|
193
|
+
return this.bindings.size;
|
|
194
|
+
}
|
|
195
|
+
/** Accessibility lint over the bindings (visual-language §16 rules). */
|
|
196
|
+
lint() {
|
|
197
|
+
const out = [];
|
|
198
|
+
for (const b of this.bindings.values()) {
|
|
199
|
+
if (b.accessibility.semanticSourceRequired && !b.semanticSource)
|
|
200
|
+
out.push({ visual: b.visual, code: 'orphan-representation', severity: 'error', message: `a ${b.role} visual must bind a semantic source` });
|
|
201
|
+
else if (!b.accessibility.ariaHidden) {
|
|
202
|
+
if (requiresSource(b.role))
|
|
203
|
+
out.push({ visual: b.visual, code: 'duplicate-semantics', severity: 'warning', message: `a ${b.role} visual must be aria-hidden so assistive tech reads only the semantic source` });
|
|
204
|
+
else
|
|
205
|
+
out.push({ visual: b.visual, code: 'visual-not-hidden', severity: 'warning', message: `a ${b.role} visual with no independent meaning should be aria-hidden` });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=visual-bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-bindings.js","sourceRoot":"","sources":["../src/visual-bindings.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fundamental-engine/dom",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "The platform-adjacent layer for Fundamental — native-first registries for measurement, semantic state, feedback, relationships, visual-semantic bindings, and overlays. The browser primitives Fundamental wishes existed, built on the ones it has.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Zach Shallbetter <hi@zachshallbetter.com> (https://zachshallbetter.com)",
|
|
8
|
+
"homepage": "https://fundamental-engine.com",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/zachshallbetter/fundamental-engine.git",
|
|
12
|
+
"directory": "packages/dom"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/zachshallbetter/fundamental-engine/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"Fundamental",
|
|
19
|
+
"dom",
|
|
20
|
+
"measurement",
|
|
21
|
+
"relationships",
|
|
22
|
+
"state",
|
|
23
|
+
"feedback",
|
|
24
|
+
"overlays",
|
|
25
|
+
"web"
|
|
26
|
+
],
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@fundamental-engine/core": "0.7.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsc -p tsconfig.json",
|
|
56
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
57
|
+
"test": "node --test"
|
|
58
|
+
}
|
|
59
|
+
}
|