@fundamental-engine/platform 0.6.0 → 0.8.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/README.md +11 -101
- package/dist/index.d.ts +11 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -37
- package/dist/index.js.map +1 -1
- package/package.json +4 -10
- package/dist/apply-recipe.d.ts +0 -103
- package/dist/apply-recipe.d.ts.map +0 -1
- package/dist/apply-recipe.js +0 -271
- package/dist/apply-recipe.js.map +0 -1
- package/dist/bind-data.d.ts +0 -72
- package/dist/bind-data.d.ts.map +0 -1
- package/dist/bind-data.js +0 -164
- package/dist/bind-data.js.map +0 -1
- package/dist/browser-host.d.ts +0 -11
- package/dist/browser-host.d.ts.map +0 -1
- package/dist/browser-host.js +0 -41
- package/dist/browser-host.js.map +0 -1
- package/dist/contours.d.ts +0 -79
- package/dist/contours.d.ts.map +0 -1
- package/dist/contours.js +0 -88
- package/dist/contours.js.map +0 -1
- package/dist/env.d.ts +0 -39
- package/dist/env.d.ts.map +0 -1
- package/dist/env.js +0 -47
- package/dist/env.js.map +0 -1
- package/dist/export-dom.d.ts +0 -7
- package/dist/export-dom.d.ts.map +0 -1
- package/dist/export-dom.js +0 -28
- package/dist/export-dom.js.map +0 -1
- package/dist/feedback.d.ts +0 -57
- package/dist/feedback.d.ts.map +0 -1
- package/dist/feedback.js +0 -134
- package/dist/feedback.js.map +0 -1
- package/dist/field-nav.d.ts +0 -35
- package/dist/field-nav.d.ts.map +0 -1
- package/dist/field-nav.js +0 -82
- package/dist/field-nav.js.map +0 -1
- package/dist/flip.d.ts +0 -31
- package/dist/flip.d.ts.map +0 -1
- package/dist/flip.js +0 -65
- package/dist/flip.js.map +0 -1
- package/dist/governor.d.ts +0 -37
- package/dist/governor.d.ts.map +0 -1
- package/dist/governor.js +0 -72
- package/dist/governor.js.map +0 -1
- package/dist/lint.d.ts +0 -78
- package/dist/lint.d.ts.map +0 -1
- package/dist/lint.js +0 -153
- package/dist/lint.js.map +0 -1
- package/dist/measurement.d.ts +0 -44
- package/dist/measurement.d.ts.map +0 -1
- package/dist/measurement.js +0 -95
- package/dist/measurement.js.map +0 -1
- package/dist/metrics.d.ts +0 -70
- package/dist/metrics.d.ts.map +0 -1
- package/dist/metrics.js +0 -119
- package/dist/metrics.js.map +0 -1
- package/dist/overlays.d.ts +0 -48
- package/dist/overlays.d.ts.map +0 -1
- package/dist/overlays.js +0 -48
- package/dist/overlays.js.map +0 -1
- package/dist/perf.d.ts +0 -62
- package/dist/perf.d.ts.map +0 -1
- package/dist/perf.js +0 -94
- package/dist/perf.js.map +0 -1
- package/dist/platform.d.ts +0 -40
- package/dist/platform.d.ts.map +0 -1
- package/dist/platform.js +0 -61
- package/dist/platform.js.map +0 -1
- package/dist/relationships.d.ts +0 -79
- package/dist/relationships.d.ts.map +0 -1
- package/dist/relationships.js +0 -155
- package/dist/relationships.js.map +0 -1
- package/dist/schedule.d.ts +0 -84
- package/dist/schedule.d.ts.map +0 -1
- package/dist/schedule.js +0 -91
- package/dist/schedule.js.map +0 -1
- package/dist/state.d.ts +0 -36
- package/dist/state.d.ts.map +0 -1
- package/dist/state.js +0 -113
- package/dist/state.js.map +0 -1
- package/dist/text-bodies.d.ts +0 -71
- package/dist/text-bodies.d.ts.map +0 -1
- package/dist/text-bodies.js +0 -159
- package/dist/text-bodies.js.map +0 -1
- package/dist/thread-overlay.d.ts +0 -63
- package/dist/thread-overlay.d.ts.map +0 -1
- package/dist/thread-overlay.js +0 -110
- package/dist/thread-overlay.js.map +0 -1
- package/dist/types.d.ts +0 -51
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -7
- package/dist/types.js.map +0 -1
- package/dist/visual-bindings.d.ts +0 -95
- package/dist/visual-bindings.d.ts.map +0 -1
- package/dist/visual-bindings.js +0 -211
- package/dist/visual-bindings.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,107 +1,17 @@
|
|
|
1
|
-
# @fundamental-engine/platform
|
|
1
|
+
# @fundamental-engine/platform — deprecated
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
existed, built on the ones it has. The core is renderer-agnostic; this package owns DOM participation:
|
|
5
|
-
it supplies the browser host, the six registries that let the engine treat the DOM as a connected,
|
|
6
|
-
measurable, semantic environment, the frame scheduler that keeps reads and writes from thrashing, and
|
|
7
|
-
the runtime that runs recipes and binds data.
|
|
3
|
+
**Renamed to [`@fundamental-engine/dom`](https://www.npmjs.com/package/@fundamental-engine/dom).**
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
This package is now a thin alias that re-exports `@fundamental-engine/dom` so existing installs keep
|
|
6
|
+
working. The layer it names is the DOM binding for the engine — `browserHost()`, the six registries
|
|
7
|
+
(measurement / state / feedback / relationships / visual-bindings / overlays), the frame scheduler,
|
|
8
|
+
`lintPlatform`, and `bindData` — so the honest name is `dom`.
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
Switch your imports:
|
|
12
11
|
|
|
13
|
-
```
|
|
14
|
-
|
|
12
|
+
```diff
|
|
13
|
+
- import { browserHost } from '@fundamental-engine/platform';
|
|
14
|
+
+ import { browserHost } from '@fundamental-engine/dom';
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
The
|
|
18
|
-
[API stability](../../docs/canonical/api-stability.md)).
|
|
19
|
-
|
|
20
|
-
## The browser host
|
|
21
|
-
|
|
22
|
-
The core's `createField` is renderer-agnostic and requires a `FieldHost`. `browserHost()` is the
|
|
23
|
-
canonical DOM implementation, and `createBrowserField()` is the host-bundled shortcut:
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { createField } from '@fundamental-engine/core';
|
|
27
|
-
import { browserHost, createBrowserField } from '@fundamental-engine/platform';
|
|
28
|
-
|
|
29
|
-
const canvas = document.querySelector('canvas')!;
|
|
30
|
-
const field = createField(canvas, { host: browserHost() });
|
|
31
|
-
// or, equivalently:
|
|
32
|
-
const same = createBrowserField(canvas, {});
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## The platform
|
|
36
|
-
|
|
37
|
-
`createFieldPlatform(root)` wires the six native-first registries on a root element and a frame
|
|
38
|
-
scheduler that runs them in order: **discover → read → compute → state → write → render**.
|
|
39
|
-
|
|
40
|
-
| Registry | Role |
|
|
41
|
-
|---|---|
|
|
42
|
-
| `MeasurementRegistry` | frame-stable geometry snapshots (read-phase only) |
|
|
43
|
-
| `StateRegistry` | typed numeric / boolean / vector2 element state (not ARIA) |
|
|
44
|
-
| `FeedbackRegistry` | write-phase CSS vars + thresholded, debounced events |
|
|
45
|
-
| `RelationshipRegistry` | normalize native links (`href#id`, `aria-controls`, `for`, …) into one graph |
|
|
46
|
-
| `VisualBindingRegistry` | bind a Canvas / SVG / WebGL visual layer to its semantic source |
|
|
47
|
-
| `OverlayRegistry` | relationship / field-line / debug render layers |
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
import { createFieldPlatform } from '@fundamental-engine/platform';
|
|
51
|
-
|
|
52
|
-
const platform = createFieldPlatform(document.documentElement);
|
|
53
|
-
platform.measure.register(card, { role: 'body' });
|
|
54
|
-
platform.state.set(card, 'density', 0.72);
|
|
55
|
-
platform.feedback.bind(card, { density: '--field-density' });
|
|
56
|
-
platform.feedback.threshold(card, 'field:lit', { metric: 'density', enter: 0.7, exit: 0.45 });
|
|
57
|
-
platform.tick(); // run one frame through the scheduler
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
`createFieldPlatform` returns a `FieldPlatform` — the surface the inspector and tools read.
|
|
61
|
-
|
|
62
|
-
## Recipes and data
|
|
63
|
-
|
|
64
|
-
The platform runs recipes and binds application data to the field. `compileRecipe()` (the pure
|
|
65
|
-
compiler) lives in [the core](../core); application lives here:
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
import { applyRecipe, bindData } from '@fundamental-engine/platform';
|
|
69
|
-
import { recipeById } from '@fundamental-engine/core';
|
|
70
|
-
|
|
71
|
-
// Run a recipe over a region; inspect the live run; tear it down.
|
|
72
|
-
const applied = applyRecipe(root, recipeById('reading-field')!);
|
|
73
|
-
applied.inspect(); // { frame, measurements, relationships, lint }
|
|
74
|
-
|
|
75
|
-
// Bind records → bodies. Updates diff by id; removed records decay out.
|
|
76
|
-
const binding = bindData(listEl, tasks, (t) => ({
|
|
77
|
-
id: t.id,
|
|
78
|
-
body: { tokens: ['attract'], strength: 0.4 + t.priority },
|
|
79
|
-
metrics: { priority: t.priority },
|
|
80
|
-
label: t.title,
|
|
81
|
-
}), { recipe: 'priority-well' });
|
|
82
|
-
binding.update(nextTasks);
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
`computeMetrics()` (pure) turns measurements and relationships into the metric values recipes track.
|
|
86
|
-
|
|
87
|
-
## Lint
|
|
88
|
-
|
|
89
|
-
`lintPlatform(platform)` runs pure rules over the live registries and the markup (missing
|
|
90
|
-
relationship targets, sinks that capture without `data-feedback`, styles reading feedback
|
|
91
|
-
variables no body writes, unregistered state, overlays without links, off-phase measurement,
|
|
92
|
-
orphan visuals, …) and returns structured diagnostics. The inspector reads it each frame.
|
|
93
|
-
|
|
94
|
-
## Dependency direction
|
|
95
|
-
|
|
96
|
-
Strict and one-way: **`platform → core`**. The core stays renderer-agnostic and never imports this
|
|
97
|
-
package. During the migration window, writes mirror `--field-*` to `--forces-*` and `field:*` events to
|
|
98
|
-
`forces:*`. See [`docs/canonical/platform-architecture.md`](../../docs/canonical/platform-architecture.md).
|
|
99
|
-
|
|
100
|
-
## Related
|
|
101
|
-
|
|
102
|
-
[`Fundamental`](../core) · [`@fundamental-engine/elements`](../elements) · [`@fundamental-engine/react`](../react) ·
|
|
103
|
-
[`@fundamental-engine/vanilla`](../vanilla) · the [documentation map](../../docs/README.md).
|
|
104
|
-
|
|
105
|
-
## License
|
|
106
|
-
|
|
107
|
-
MIT © Zach Shallbetter
|
|
17
|
+
The alias will be removed in a future major release.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,39 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fundamental-engine/platform —
|
|
3
|
-
* engine treat the DOM as a connected, measurable, semantic environment. Strict dependency
|
|
4
|
-
* direction: this package depends on `Fundamental` (core) for contracts; core never depends on it.
|
|
2
|
+
* @fundamental-engine/platform — DEPRECATED.
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* This package was renamed to `@fundamental-engine/dom` (it is the DOM-binding layer:
|
|
5
|
+
* `browserHost()`, the six registries, the frame scheduler, `lintPlatform`, `bindData`).
|
|
6
|
+
* This alias re-exports it unchanged so existing installs keep working; switch your imports:
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* ```diff
|
|
9
|
+
* - import { browserHost } from '@fundamental-engine/platform';
|
|
10
|
+
* + import { browserHost } from '@fundamental-engine/dom';
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The alias will be removed in a future major. See https://fundamental-engine.com.
|
|
12
14
|
*/
|
|
13
|
-
|
|
14
|
-
export * from './types.ts';
|
|
15
|
-
export * from './env.ts';
|
|
16
|
-
export * from './schedule.ts';
|
|
17
|
-
export * from './measurement.ts';
|
|
18
|
-
export * from './state.ts';
|
|
19
|
-
export * from './feedback.ts';
|
|
20
|
-
export * from './relationships.ts';
|
|
21
|
-
export * from './visual-bindings.ts';
|
|
22
|
-
export * from './overlays.ts';
|
|
23
|
-
export * from './lint.ts';
|
|
24
|
-
export * from './platform.ts';
|
|
25
|
-
export * from './metrics.ts';
|
|
26
|
-
export * from './apply-recipe.ts';
|
|
27
|
-
export * from './field-nav.ts';
|
|
28
|
-
export * from './bind-data.ts';
|
|
29
|
-
export * from './browser-host.ts';
|
|
30
|
-
export * from './export-dom.ts';
|
|
31
|
-
export * from './governor.ts';
|
|
32
|
-
export * from './flip.ts';
|
|
33
|
-
export * from './text-bodies.ts';
|
|
34
|
-
export * from './contours.ts';
|
|
35
|
-
export * from './thread-overlay.ts';
|
|
36
|
-
export * from './perf.ts';
|
|
37
|
-
/** Start the core engine on a canvas with the default browser host — `createField` + `browserHost()`. */
|
|
38
|
-
export declare function createBrowserField(canvas: HTMLCanvasElement, opts?: Omit<FieldOptions, 'host'>): FieldHandle;
|
|
15
|
+
export * from '@fundamental-engine/dom';
|
|
39
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,42 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fundamental-engine/platform —
|
|
3
|
-
* engine treat the DOM as a connected, measurable, semantic environment. Strict dependency
|
|
4
|
-
* direction: this package depends on `Fundamental` (core) for contracts; core never depends on it.
|
|
2
|
+
* @fundamental-engine/platform — DEPRECATED.
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* This package was renamed to `@fundamental-engine/dom` (it is the DOM-binding layer:
|
|
5
|
+
* `browserHost()`, the six registries, the frame scheduler, `lintPlatform`, `bindData`).
|
|
6
|
+
* This alias re-exports it unchanged so existing installs keep working; switch your imports:
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* ```diff
|
|
9
|
+
* - import { browserHost } from '@fundamental-engine/platform';
|
|
10
|
+
* + import { browserHost } from '@fundamental-engine/dom';
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The alias will be removed in a future major. See https://fundamental-engine.com.
|
|
12
14
|
*/
|
|
13
|
-
|
|
14
|
-
import { browserHost } from "./browser-host.js";
|
|
15
|
-
export * from "./types.js";
|
|
16
|
-
export * from "./env.js";
|
|
17
|
-
export * from "./schedule.js";
|
|
18
|
-
export * from "./measurement.js";
|
|
19
|
-
export * from "./state.js";
|
|
20
|
-
export * from "./feedback.js";
|
|
21
|
-
export * from "./relationships.js";
|
|
22
|
-
export * from "./visual-bindings.js";
|
|
23
|
-
export * from "./overlays.js";
|
|
24
|
-
export * from "./lint.js";
|
|
25
|
-
export * from "./platform.js";
|
|
26
|
-
export * from "./metrics.js";
|
|
27
|
-
export * from "./apply-recipe.js";
|
|
28
|
-
export * from "./field-nav.js";
|
|
29
|
-
export * from "./bind-data.js";
|
|
30
|
-
export * from "./browser-host.js";
|
|
31
|
-
export * from "./export-dom.js";
|
|
32
|
-
export * from "./governor.js";
|
|
33
|
-
export * from "./flip.js";
|
|
34
|
-
export * from "./text-bodies.js";
|
|
35
|
-
export * from "./contours.js";
|
|
36
|
-
export * from "./thread-overlay.js";
|
|
37
|
-
export * from "./perf.js";
|
|
38
|
-
/** Start the core engine on a canvas with the default browser host — `createField` + `browserHost()`. */
|
|
39
|
-
export function createBrowserField(canvas, opts = {}) {
|
|
40
|
-
return createField(canvas, { ...opts, host: browserHost() });
|
|
41
|
-
}
|
|
15
|
+
export * from '@fundamental-engine/dom';
|
|
42
16
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,yBAAyB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fundamental-engine/platform",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "DEPRECATED — renamed to @fundamental-engine/dom. A thin alias that re-exports it so existing installs keep working; switch your imports to @fundamental-engine/dom.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Zach Shallbetter <hi@zachshallbetter.com> (https://zachshallbetter.com)",
|
|
@@ -16,13 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"keywords": [
|
|
18
18
|
"Fundamental",
|
|
19
|
-
"
|
|
20
|
-
"measurement",
|
|
21
|
-
"relationships",
|
|
22
|
-
"state",
|
|
23
|
-
"feedback",
|
|
24
|
-
"overlays",
|
|
25
|
-
"web"
|
|
19
|
+
"deprecated"
|
|
26
20
|
],
|
|
27
21
|
"sideEffects": false,
|
|
28
22
|
"files": [
|
|
@@ -46,7 +40,7 @@
|
|
|
46
40
|
"access": "public"
|
|
47
41
|
},
|
|
48
42
|
"dependencies": {
|
|
49
|
-
"@fundamental-engine/
|
|
43
|
+
"@fundamental-engine/dom": "0.8.0"
|
|
50
44
|
},
|
|
51
45
|
"devDependencies": {
|
|
52
46
|
"typescript": "^5.9.3"
|
package/dist/apply-recipe.d.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* applyRecipe — the DOM counterpart to core's `compileRecipe`. Turns a `FieldRecipe` from a record
|
|
3
|
-
* into a running field program: it registers the recipe's bodies (TOKEN lane only — concepts are never
|
|
4
|
-
* executed), binds its metrics to feedback variables, discovers its relationships, computes metric
|
|
5
|
-
* state each frame, installs a reduced-motion output when motion is reduced, and returns a handle that
|
|
6
|
-
* can be inspected and destroyed.
|
|
7
|
-
*
|
|
8
|
-
* It runs on a scoped `createFieldPlatform(root)` — the registry/feedback layer, not a particle canvas
|
|
9
|
-
* — so it works on ordinary content the way the Reading Field studies do.
|
|
10
|
-
*/
|
|
11
|
-
import { type FieldPlatform } from './platform.ts';
|
|
12
|
-
import { type FieldRecipe, type CompiledRecipe } from '@fundamental-engine/core';
|
|
13
|
-
export interface ApplyRecipeOptions {
|
|
14
|
-
/** existing elements (or a selector within root) to annotate as the recipe's bodies; if omitted, demo elements are created. */
|
|
15
|
-
bodies?: Element[] | string;
|
|
16
|
-
/** when bodies are provided, whether to overwrite their data-body attributes with the recipe's (default true). Set false to keep caller-owned tokens (e.g. bindData) while still binding the recipe's metrics. */
|
|
17
|
-
annotateBodies?: boolean;
|
|
18
|
-
/** force the reduced-motion output (defaults to the OS prefers-reduced-motion setting). */
|
|
19
|
-
reducedMotion?: boolean;
|
|
20
|
-
/** compute + bind metrics (default true). */
|
|
21
|
-
metrics?: boolean;
|
|
22
|
-
/** run the rAF loop (default true). Set false to drive `tick()` yourself. */
|
|
23
|
-
drive?: boolean;
|
|
24
|
-
/** text for created demo bodies (default: the body's tokens). */
|
|
25
|
-
label?: (recipe: FieldRecipe, index: number) => string;
|
|
26
|
-
/**
|
|
27
|
-
* Apply the recipe with its render stack stripped (`render: []`) — the scoped invisible-field
|
|
28
|
-
* idiom the example family runs: the field exists purely as metrics/feedback signals on real
|
|
29
|
-
* content, with no drawn layers. The caller's recipe object is NEVER mutated (it may be the
|
|
30
|
-
* shared catalog object); `applyRecipe` derives an effective copy, which is what the returned
|
|
31
|
-
* handle's `recipe`/`compiled` reflect.
|
|
32
|
-
*/
|
|
33
|
-
renderless?: boolean;
|
|
34
|
-
/**
|
|
35
|
-
* A live field to DRIVE with the recipe's render plan (#370) — the missing execution half of
|
|
36
|
-
* `recipe.render`. Structural: a `FieldHandle` or a `<field-root>` element both satisfy it.
|
|
37
|
-
* When provided (and not `renderless`/reduced-motion), the compiled plan executes — underlay
|
|
38
|
-
* matter mode via setRender, the additive overlay reading stack via setOverlay, the heatmap
|
|
39
|
-
* toggle — and `destroy()` resets the surfaces it drove (dots / off / false). Omitted → the
|
|
40
|
-
* recipe stays signals-only, exactly as before (fully additive).
|
|
41
|
-
*/
|
|
42
|
-
field?: RecipeFieldTarget;
|
|
43
|
-
/**
|
|
44
|
-
* Extra metric lanes appended to the recipe's `metrics` (deduped, original order preserved) —
|
|
45
|
-
* e.g. `['attention', 'recency']`. Each appended metric gains the standard feedback binding
|
|
46
|
-
* (`attention` → `--field-attention`) and flows through the per-frame metric pipeline exactly
|
|
47
|
-
* like a recipe-declared one. Same no-mutation guarantee as `renderless`.
|
|
48
|
-
*/
|
|
49
|
-
extraMetrics?: string[];
|
|
50
|
-
}
|
|
51
|
-
export interface AppliedRecipeInspection {
|
|
52
|
-
id: string;
|
|
53
|
-
frame: number;
|
|
54
|
-
measurements: number;
|
|
55
|
-
/** resolved relationships (both endpoints known). */
|
|
56
|
-
relationships: number;
|
|
57
|
-
/** declared relationships whose target id-ref resolves to no element. */
|
|
58
|
-
relationshipsUnresolved: number;
|
|
59
|
-
/** resolved / (resolved + unresolved); undefined when no relationships are declared. */
|
|
60
|
-
relationshipResolution?: number;
|
|
61
|
-
/** the unresolved declarations, so inspection can name each missing endpoint. */
|
|
62
|
-
unresolvedRelationships: Array<{
|
|
63
|
-
from: string;
|
|
64
|
-
type: string;
|
|
65
|
-
target: string;
|
|
66
|
-
}>;
|
|
67
|
-
/** elementKey → metric → value (the compiled recipe's lanes, live). */
|
|
68
|
-
metrics: Record<string, Record<string, number>>;
|
|
69
|
-
lint: number;
|
|
70
|
-
reducedMotion: boolean;
|
|
71
|
-
}
|
|
72
|
-
export interface AppliedRecipe {
|
|
73
|
-
id: string;
|
|
74
|
-
recipe: FieldRecipe;
|
|
75
|
-
compiled: CompiledRecipe;
|
|
76
|
-
platform: FieldPlatform;
|
|
77
|
-
root: Element;
|
|
78
|
-
elements: Element[];
|
|
79
|
-
reducedMotion: boolean;
|
|
80
|
-
inspect(): AppliedRecipeInspection;
|
|
81
|
-
tick(now?: number): void;
|
|
82
|
-
destroy(): void;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Apply a recipe to a root element. Validates, compiles, registers bodies, binds metrics, discovers
|
|
86
|
-
* relationships, installs the reduced-motion output, and returns a destroyable, inspectable handle.
|
|
87
|
-
*/
|
|
88
|
-
/** The slice of a live field a recipe can drive — FieldHandle and <field-root> both fit. */
|
|
89
|
-
export interface RecipeFieldTarget {
|
|
90
|
-
setRender?(mode: string): void;
|
|
91
|
-
setOverlay?(mode: string | string[]): void;
|
|
92
|
-
setHeatmap?(on: boolean): void;
|
|
93
|
-
}
|
|
94
|
-
/** Execute a compiled render plan on a field target. Exported for tests and custom hosts. */
|
|
95
|
-
export declare function driveRenderPlan(field: RecipeFieldTarget, plan: {
|
|
96
|
-
underlay: string | null;
|
|
97
|
-
overlay: string[];
|
|
98
|
-
heatmap: boolean;
|
|
99
|
-
}): void;
|
|
100
|
-
export declare function applyRecipe(root: Element, recipe: FieldRecipe, options?: ApplyRecipeOptions): AppliedRecipe;
|
|
101
|
-
/** Tear down an applied recipe (alias of `applied.destroy()`). */
|
|
102
|
-
export declare function destroyRecipe(applied: AppliedRecipe): void;
|
|
103
|
-
//# sourceMappingURL=apply-recipe.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"apply-recipe.d.ts","sourceRoot":"","sources":["../src/apply-recipe.ts"],"names":[],"mappings":"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"}
|
package/dist/apply-recipe.js
DELETED
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* applyRecipe — the DOM counterpart to core's `compileRecipe`. Turns a `FieldRecipe` from a record
|
|
3
|
-
* into a running field program: it registers the recipe's bodies (TOKEN lane only — concepts are never
|
|
4
|
-
* executed), binds its metrics to feedback variables, discovers its relationships, computes metric
|
|
5
|
-
* state each frame, installs a reduced-motion output when motion is reduced, and returns a handle that
|
|
6
|
-
* can be inspected and destroyed.
|
|
7
|
-
*
|
|
8
|
-
* It runs on a scoped `createFieldPlatform(root)` — the registry/feedback layer, not a particle canvas
|
|
9
|
-
* — so it works on ordinary content the way the Reading Field studies do.
|
|
10
|
-
*/
|
|
11
|
-
import { createFieldPlatform } from "./platform.js";
|
|
12
|
-
import { lintPlatform } from "./lint.js";
|
|
13
|
-
import { prefersReducedMotion } from "./env.js";
|
|
14
|
-
import { computeMetrics, groundedRecency, METRIC_KINDS } from "./metrics.js";
|
|
15
|
-
import { validateRecipe, compileRecipe, } from '@fundamental-engine/core';
|
|
16
|
-
const isMetricKind = (m) => METRIC_KINDS.includes(m);
|
|
17
|
-
const num = (v) => {
|
|
18
|
-
if (v == null)
|
|
19
|
-
return undefined;
|
|
20
|
-
const n = Number(v);
|
|
21
|
-
return Number.isFinite(n) ? n : undefined;
|
|
22
|
-
};
|
|
23
|
-
const elementKey = (el, i) => el.id || `${el.tagName.toLowerCase()}#${i}`;
|
|
24
|
-
/** Execute a compiled render plan on a field target. Exported for tests and custom hosts. */
|
|
25
|
-
export function driveRenderPlan(field, plan) {
|
|
26
|
-
if (plan.underlay && field.setRender)
|
|
27
|
-
field.setRender(plan.underlay);
|
|
28
|
-
if (field.setOverlay)
|
|
29
|
-
field.setOverlay(plan.overlay.length ? plan.overlay : 'off');
|
|
30
|
-
if (field.setHeatmap)
|
|
31
|
-
field.setHeatmap(plan.heatmap);
|
|
32
|
-
}
|
|
33
|
-
export function applyRecipe(root, recipe, options = {}) {
|
|
34
|
-
// Derive the effective recipe from the scoped-field options — a copy, so a shared catalog
|
|
35
|
-
// recipe object is never mutated by an applied run (`renderless` strips the render stack;
|
|
36
|
-
// `extraMetrics` appends + dedupes metric lanes). No options → the input recipe, untouched.
|
|
37
|
-
const extra = options.extraMetrics ?? [];
|
|
38
|
-
if (options.renderless || extra.length) {
|
|
39
|
-
recipe = {
|
|
40
|
-
...recipe,
|
|
41
|
-
...(options.renderless ? { render: [] } : {}),
|
|
42
|
-
...(extra.length ? { metrics: [...new Set([...(recipe.metrics ?? []), ...extra])] } : {}),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
const problems = validateRecipe(recipe);
|
|
46
|
-
if (problems.length)
|
|
47
|
-
throw new Error(`applyRecipe: invalid recipe "${recipe.id}": ${problems.map((p) => `${p.path} (${p.issue})`).join('; ')}`);
|
|
48
|
-
const compiled = compileRecipe(recipe);
|
|
49
|
-
const wantMetrics = options.metrics !== false;
|
|
50
|
-
// the execution half of recipe.render (#370): drive the supplied field with the compiled plan.
|
|
51
|
-
// Reduced motion skips the drive entirely — the recipe's static plan is the equivalent, and a
|
|
52
|
-
// field left at its resting surfaces (dots / off) IS the static reading.
|
|
53
|
-
let droveField = null;
|
|
54
|
-
const reducedMotion = options.reducedMotion ?? prefersReducedMotion();
|
|
55
|
-
const platform = createFieldPlatform(root);
|
|
56
|
-
if (options.field && !options.renderless && !reducedMotion) {
|
|
57
|
-
driveRenderPlan(options.field, compiled.render);
|
|
58
|
-
droveField = options.field;
|
|
59
|
-
}
|
|
60
|
-
// ── resolve body elements: annotate provided ones, or create demo bodies ──────────────
|
|
61
|
-
const created = [];
|
|
62
|
-
const restore = [];
|
|
63
|
-
let elements;
|
|
64
|
-
if (options.bodies) {
|
|
65
|
-
elements = typeof options.bodies === 'string' ? Array.from(root.querySelectorAll(options.bodies)) : options.bodies.slice();
|
|
66
|
-
if (options.annotateBodies !== false) {
|
|
67
|
-
elements.forEach((el, i) => {
|
|
68
|
-
const body = compiled.bodies[i % compiled.bodies.length];
|
|
69
|
-
const snap = {};
|
|
70
|
-
for (const [k, v] of Object.entries(body.attributes)) {
|
|
71
|
-
snap[k] = el.getAttribute(k);
|
|
72
|
-
el.setAttribute(k, v);
|
|
73
|
-
}
|
|
74
|
-
restore.push({ el, attrs: snap });
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
elements = compiled.bodies.map((body, i) => {
|
|
80
|
-
const el = (root.ownerDocument ?? document).createElement('span');
|
|
81
|
-
for (const [k, v] of Object.entries(body.attributes))
|
|
82
|
-
el.setAttribute(k, v);
|
|
83
|
-
el.setAttribute('data-recipe-body', recipe.id);
|
|
84
|
-
el.textContent = options.label ? options.label(recipe, i) : body.tokens.join(' ');
|
|
85
|
-
root.appendChild(el);
|
|
86
|
-
created.push(el);
|
|
87
|
-
return el;
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
// ── register for measurement + bind the metric lane to feedback variables ──────────────
|
|
91
|
-
const varMap = {};
|
|
92
|
-
for (const f of compiled.feedback)
|
|
93
|
-
varMap[f.metric] = f.var;
|
|
94
|
-
for (const el of elements) {
|
|
95
|
-
platform.measure.register(el, { role: 'recipe-body' });
|
|
96
|
-
if (wantMetrics && compiled.feedback.length)
|
|
97
|
-
platform.feedback.bind(el, varMap);
|
|
98
|
-
}
|
|
99
|
-
// ── discover relationships once ──────────────────────────────────────────────────────
|
|
100
|
-
let discovered = false;
|
|
101
|
-
platform.on('discover', () => {
|
|
102
|
-
if (!discovered) {
|
|
103
|
-
platform.relationships.discover(root);
|
|
104
|
-
discovered = true;
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
// ── metric computation (compute → state); write phase flushes state → --field-* vars ───
|
|
108
|
-
const prev = new Map();
|
|
109
|
-
if (wantMetrics) {
|
|
110
|
-
const pending = new Map();
|
|
111
|
-
platform.on('compute', (ctx) => {
|
|
112
|
-
const vh = ctx.viewport?.height ?? (typeof window !== 'undefined' ? window.innerHeight : 800);
|
|
113
|
-
const centre = vh / 2;
|
|
114
|
-
// WORLD TIME, once per frame: ctx.now is the scheduler's rAF timebase, not an epoch, so
|
|
115
|
-
// the declared-timestamp derivation samples the wall clock HERE — one instant shared by
|
|
116
|
-
// every element in the frame, never a per-element Date.now().
|
|
117
|
-
const worldNow = Date.now();
|
|
118
|
-
const rels = platform.relationships.all();
|
|
119
|
-
const unresolved = platform.relationships.unresolvedAll();
|
|
120
|
-
for (const m of platform.measure.last()) {
|
|
121
|
-
const el = m.element;
|
|
122
|
-
const proximity = Math.max(0, 1 - Math.abs(m.rect.cy - centre) / (vh * 0.55));
|
|
123
|
-
const engaged = el.matches(':hover, :focus, :focus-within') || el.hasAttribute('data-active');
|
|
124
|
-
// a body touches a relationship if either endpoint is the body or sits inside it (child anchors)
|
|
125
|
-
const touching = rels.filter((r) => el.contains(r.from) || el.contains(r.to));
|
|
126
|
-
// declared-but-unresolved edges originating in this body count toward the total but not the
|
|
127
|
-
// resolved set, so resolution is real: a citation pointing at nothing lowers it (raising entropy).
|
|
128
|
-
const touchingUnresolved = unresolved.filter((u) => el.contains(u.from));
|
|
129
|
-
const relResolved = touching.length;
|
|
130
|
-
const relTotal = touching.length + touchingUnresolved.length;
|
|
131
|
-
const relConflict = touching.filter((r) => r.type === 'contradicts' || r.type === 'opposes' || r.type === 'conflicts-with').length;
|
|
132
|
-
const supplied = {};
|
|
133
|
-
for (const k of METRIC_KINDS) {
|
|
134
|
-
const s = num(el.getAttribute(`data-field-${k}`));
|
|
135
|
-
if (s != null)
|
|
136
|
-
supplied[k] = s;
|
|
137
|
-
}
|
|
138
|
-
// A declared world timestamp (data-field-at) GROUNDS the recency lane: recency becomes
|
|
139
|
-
// freshness(at, now, halfLife) — data time, not interaction time. An explicit
|
|
140
|
-
// data-field-recency still wins; without either, computeMetrics infers recency from
|
|
141
|
-
// interaction (the existing eased behavior, unchanged).
|
|
142
|
-
if (supplied.recency == null) {
|
|
143
|
-
const r = groundedRecency(el, worldNow);
|
|
144
|
-
if (r != null)
|
|
145
|
-
supplied.recency = r;
|
|
146
|
-
}
|
|
147
|
-
pending.set(el, computeMetrics({
|
|
148
|
-
proximity,
|
|
149
|
-
visible: m.visibilityRatio,
|
|
150
|
-
engaged,
|
|
151
|
-
dtFrames: 1,
|
|
152
|
-
relResolved,
|
|
153
|
-
relTotal,
|
|
154
|
-
relConflict,
|
|
155
|
-
supplied,
|
|
156
|
-
prev: prev.get(el) ?? {},
|
|
157
|
-
}));
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
platform.on('state', () => {
|
|
161
|
-
for (const [el, computed] of pending) {
|
|
162
|
-
prev.set(el, computed);
|
|
163
|
-
for (const metric of compiled.metrics) {
|
|
164
|
-
const value = isMetricKind(metric)
|
|
165
|
-
? computed[metric]
|
|
166
|
-
: (num(el.getAttribute(`data-field-${metric}`)) ?? 0);
|
|
167
|
-
if (value == null) {
|
|
168
|
-
// The metric is absent this frame — e.g. the host supplied data-field-confidence on an
|
|
169
|
-
// earlier frame and has since removed it. Drop any stale state AND clear the bound CSS
|
|
170
|
-
// var, so the write phase neither re-emits a value nor leaves one written on a previous
|
|
171
|
-
// flush lingering on the element. Absent must read as absent, not last-known. Both calls
|
|
172
|
-
// are no-ops when nothing was ever set, so the common "never supplied" case stays cheap.
|
|
173
|
-
platform.state.delete(el, metric);
|
|
174
|
-
platform.feedback.clearVar(el, metric);
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
platform.state.set(el, metric, value);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
// ── reduced-motion output: a real static surface, not just prose ──────────────────────
|
|
183
|
-
let staticNode = null;
|
|
184
|
-
if (reducedMotion) {
|
|
185
|
-
root.dataset.recipeReduced = 'on';
|
|
186
|
-
const doc = root.ownerDocument ?? document;
|
|
187
|
-
staticNode = doc.createElement('aside');
|
|
188
|
-
staticNode.className = 'recipe-static';
|
|
189
|
-
staticNode.setAttribute('data-recipe-static', recipe.id);
|
|
190
|
-
staticNode.innerHTML =
|
|
191
|
-
`<p class="rs-note">${escapeHtml(compiled.reducedMotion.meaningWithoutMotion)}</p>` +
|
|
192
|
-
(compiled.metrics.length ? `<p class="rs-metrics">Metrics: ${compiled.metrics.map((m) => `<span>${escapeHtml(m)}</span>`).join(' ')}</p>` : '') +
|
|
193
|
-
(compiled.relationships.length ? `<p class="rs-rels">Relationships: ${compiled.relationships.map((r) => `${escapeHtml(r.from)}→${escapeHtml(r.to)}`).join(', ')}</p>` : '');
|
|
194
|
-
root.appendChild(staticNode);
|
|
195
|
-
}
|
|
196
|
-
// ── drive ─────────────────────────────────────────────────────────────────────────────
|
|
197
|
-
let raf = 0;
|
|
198
|
-
const viewport = () => (typeof window !== 'undefined' ? { width: window.innerWidth, height: window.innerHeight } : undefined);
|
|
199
|
-
const tick = (now = 0) => {
|
|
200
|
-
platform.tick(now, viewport());
|
|
201
|
-
};
|
|
202
|
-
if (options.drive !== false && typeof requestAnimationFrame !== 'undefined') {
|
|
203
|
-
const loop = (now) => {
|
|
204
|
-
tick(now);
|
|
205
|
-
raf = requestAnimationFrame(loop);
|
|
206
|
-
};
|
|
207
|
-
raf = requestAnimationFrame(loop);
|
|
208
|
-
}
|
|
209
|
-
const inspect = () => {
|
|
210
|
-
const metrics = {};
|
|
211
|
-
elements.forEach((el, i) => {
|
|
212
|
-
const row = {};
|
|
213
|
-
for (const metric of compiled.metrics)
|
|
214
|
-
row[metric] = platform.state.number(el, metric);
|
|
215
|
-
metrics[elementKey(el, i)] = row;
|
|
216
|
-
});
|
|
217
|
-
const unresolved = platform.relationships.unresolvedAll();
|
|
218
|
-
const resolvedCount = platform.relationships.all().length;
|
|
219
|
-
const declaredTotal = resolvedCount + unresolved.length;
|
|
220
|
-
return {
|
|
221
|
-
id: recipe.id,
|
|
222
|
-
frame: platform.scheduler.frame,
|
|
223
|
-
measurements: platform.measure.size,
|
|
224
|
-
relationships: resolvedCount,
|
|
225
|
-
relationshipsUnresolved: unresolved.length,
|
|
226
|
-
relationshipResolution: declaredTotal > 0 ? resolvedCount / declaredTotal : undefined,
|
|
227
|
-
unresolvedRelationships: unresolved.map((u) => ({
|
|
228
|
-
from: u.from.id || u.from.tagName.toLowerCase(),
|
|
229
|
-
type: u.type,
|
|
230
|
-
target: u.target,
|
|
231
|
-
})),
|
|
232
|
-
metrics,
|
|
233
|
-
lint: lintPlatform(platform).length,
|
|
234
|
-
reducedMotion,
|
|
235
|
-
};
|
|
236
|
-
};
|
|
237
|
-
const destroy = () => {
|
|
238
|
-
if (raf)
|
|
239
|
-
cancelAnimationFrame(raf);
|
|
240
|
-
raf = 0;
|
|
241
|
-
// release the surfaces this recipe drove — the field returns to its resting reading
|
|
242
|
-
if (droveField) {
|
|
243
|
-
driveRenderPlan(droveField, { underlay: 'dots', overlay: [], heatmap: false });
|
|
244
|
-
droveField = null;
|
|
245
|
-
}
|
|
246
|
-
// clear the feedback variables this recipe wrote, so a torn-down recipe leaves the DOM plain
|
|
247
|
-
// (typeof-guarded like every other global here, so destroy() is safe off-DOM too)
|
|
248
|
-
if (typeof HTMLElement !== 'undefined')
|
|
249
|
-
for (const el of elements)
|
|
250
|
-
if (el instanceof HTMLElement)
|
|
251
|
-
for (const f of compiled.feedback)
|
|
252
|
-
el.style.removeProperty(f.var);
|
|
253
|
-
for (const el of created)
|
|
254
|
-
el.remove();
|
|
255
|
-
for (const { el, attrs } of restore)
|
|
256
|
-
for (const [k, v] of Object.entries(attrs))
|
|
257
|
-
v == null ? el.removeAttribute(k) : el.setAttribute(k, v);
|
|
258
|
-
staticNode?.remove();
|
|
259
|
-
if (reducedMotion)
|
|
260
|
-
delete root.dataset.recipeReduced;
|
|
261
|
-
};
|
|
262
|
-
return { id: recipe.id, recipe, compiled, platform, root, elements, reducedMotion, inspect, tick, destroy };
|
|
263
|
-
}
|
|
264
|
-
/** Tear down an applied recipe (alias of `applied.destroy()`). */
|
|
265
|
-
export function destroyRecipe(applied) {
|
|
266
|
-
applied.destroy();
|
|
267
|
-
}
|
|
268
|
-
function escapeHtml(s) {
|
|
269
|
-
return s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c] ?? c);
|
|
270
|
-
}
|
|
271
|
-
//# sourceMappingURL=apply-recipe.js.map
|