@fundamental-engine/elements 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/custom-elements.json +2832 -0
- package/dist/base.d.ts +9 -0
- package/dist/base.d.ts.map +1 -0
- package/dist/base.js +12 -0
- package/dist/base.js.map +1 -0
- package/dist/cell-force.d.ts +10 -0
- package/dist/cell-force.d.ts.map +1 -0
- package/dist/cell-force.js +38 -0
- package/dist/cell-force.js.map +1 -0
- package/dist/field-cell.d.ts +62 -0
- package/dist/field-cell.d.ts.map +1 -0
- package/dist/field-cell.js +220 -0
- package/dist/field-cell.js.map +1 -0
- package/dist/index.d.ts +174 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +476 -0
- package/dist/index.js.map +1 -0
- package/dist/mount.d.ts +8 -0
- package/dist/mount.d.ts.map +1 -0
- package/dist/mount.js +7 -0
- package/dist/mount.js.map +1 -0
- package/dist/platform-runtime.d.ts +92 -0
- package/dist/platform-runtime.d.ts.map +1 -0
- package/dist/platform-runtime.js +244 -0
- package/dist/platform-runtime.js.map +1 -0
- package/package.json +64 -0
package/dist/base.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR-safe `HTMLElement` base. In the browser it is the real `HTMLElement`; under Node — an SSR
|
|
3
|
+
* pass, a build step, or the dist smoke check — it is an inert stand-in, so merely *importing*
|
|
4
|
+
* the package never throws `HTMLElement is not defined`. The elements only do anything once a
|
|
5
|
+
* browser upgrades them (every `customElements.define` is guarded too), so the stand-in is never
|
|
6
|
+
* constructed.
|
|
7
|
+
*/
|
|
8
|
+
export declare const HTMLElementBase: typeof HTMLElement;
|
|
9
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,WAGY,CAAC"}
|
package/dist/base.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR-safe `HTMLElement` base. In the browser it is the real `HTMLElement`; under Node — an SSR
|
|
3
|
+
* pass, a build step, or the dist smoke check — it is an inert stand-in, so merely *importing*
|
|
4
|
+
* the package never throws `HTMLElement is not defined`. The elements only do anything once a
|
|
5
|
+
* browser upgrades them (every `customElements.define` is guarded too), so the stand-in is never
|
|
6
|
+
* constructed.
|
|
7
|
+
*/
|
|
8
|
+
export const HTMLElementBase = typeof HTMLElement !== 'undefined'
|
|
9
|
+
? HTMLElement
|
|
10
|
+
: class {
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=base.js.map
|
package/dist/base.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAC1B,OAAO,WAAW,KAAK,WAAW;IAChC,CAAC,CAAC,WAAW;IACb,CAAC,CAAE;KAA0C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Field Cell's in-frame force math (§25.1) — a deliberately simplified,
|
|
3
|
+
* centre-of-frame model for demos. NOT the canonical force math (§6); the cell is
|
|
4
|
+
* a poster engine. `dx,dy` point from the particle TO the frame centre.
|
|
5
|
+
*/
|
|
6
|
+
export declare function cellForce(force: string, dx: number, dy: number, reach: number): {
|
|
7
|
+
ax: number;
|
|
8
|
+
ay: number;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=cell-force.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-force.d.ts","sourceRoot":"","sources":["../src/cell-force.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CA8B5B"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Field Cell's in-frame force math (§25.1) — a deliberately simplified,
|
|
3
|
+
* centre-of-frame model for demos. NOT the canonical force math (§6); the cell is
|
|
4
|
+
* a poster engine. `dx,dy` point from the particle TO the frame centre.
|
|
5
|
+
*/
|
|
6
|
+
export function cellForce(force, dx, dy, reach) {
|
|
7
|
+
const d = Math.hypot(dx, dy) || 1;
|
|
8
|
+
// stream and buoyancy are uniform fields — not distance-gated.
|
|
9
|
+
const uniform = force === 'stream' || force === 'buoyancy';
|
|
10
|
+
if (!uniform && d >= reach)
|
|
11
|
+
return { ax: 0, ay: 0 };
|
|
12
|
+
const ux = dx / d;
|
|
13
|
+
const uy = dy / d;
|
|
14
|
+
const f = (1 - Math.min(d, reach) / reach) * 0.4;
|
|
15
|
+
switch (force) {
|
|
16
|
+
case 'repel':
|
|
17
|
+
return { ax: -ux * f, ay: -uy * f };
|
|
18
|
+
case 'swirl':
|
|
19
|
+
return { ax: -uy * f, ay: ux * f };
|
|
20
|
+
case 'stream':
|
|
21
|
+
return { ax: 0.12, ay: 0 };
|
|
22
|
+
case 'buoyancy':
|
|
23
|
+
return { ax: 0, ay: -0.12 }; // a steady lift (−y is up)
|
|
24
|
+
case 'gravity': {
|
|
25
|
+
const g = Math.min(0.8, ((reach * reach) / (d * d)) * 0.02); // true-ish 1/d²
|
|
26
|
+
return { ax: ux * g, ay: uy * g };
|
|
27
|
+
}
|
|
28
|
+
case 'tether': {
|
|
29
|
+
const rest = reach * 0.45;
|
|
30
|
+
const s = (d - rest) * 0.025; // pull in past the shell, push out within it
|
|
31
|
+
return { ax: ux * s, ay: uy * s };
|
|
32
|
+
}
|
|
33
|
+
case 'attract':
|
|
34
|
+
default:
|
|
35
|
+
return { ax: ux * f, ay: uy * f };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=cell-force.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-force.js","sourceRoot":"","sources":["../src/cell-force.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,KAAa,EACb,EAAU,EACV,EAAU,EACV,KAAa;IAEb,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAClC,+DAA+D;IAC/D,MAAM,OAAO,GAAG,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU,CAAC;IAC3D,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IACpD,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAClB,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAClB,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;IACjD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,OAAO;YACV,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QACtC,KAAK,OAAO;YACV,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;QACrC,KAAK,QAAQ;YACX,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;QAC7B,KAAK,UAAU;YACb,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,2BAA2B;QAC1D,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,gBAAgB;YAC7E,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;QACpC,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC;YAC1B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,6CAA6C;YAC3E,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;QACpC,CAAC;QACD,KAAK,SAAS,CAAC;QACf;YACE,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;IACtC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { HTMLElementBase } from './base.ts';
|
|
2
|
+
/**
|
|
3
|
+
* `<field-cell force="swirl" color="#2dd4bf">` — an in-frame field surface (§25.1).
|
|
4
|
+
*
|
|
5
|
+
* A standalone field sized to its container that renders *one* force, with its own
|
|
6
|
+
* lightweight particle pool, its own pointer interaction, and a lifecycle that pauses
|
|
7
|
+
* when off-screen. It is **not** the §-engine: it's a lighter "demo/poster" engine,
|
|
8
|
+
* completely separate from the page `<field-root>` and the core field loop.
|
|
9
|
+
*
|
|
10
|
+
* - `ResizeObserver` re-fits the canvas (DPR-aware) and rebuilds the pool.
|
|
11
|
+
* - `IntersectionObserver` gates the rAF loop — paused when off-screen.
|
|
12
|
+
* - Honours `prefers-reduced-motion`: renders one static frame, no animation.
|
|
13
|
+
*
|
|
14
|
+
* @summary A standalone, in-frame demo field that renders one force with its own
|
|
15
|
+
* particle pool, in-view-gated. Registered as `<field-cell>`.
|
|
16
|
+
* @attr {string} force - The single force token rendered: `attract` | `repel` | `swirl` | `gravity` | `stream` | `buoyancy` | `tether`.
|
|
17
|
+
* @attr {string} color - Accent color (hex) for the cell's particles.
|
|
18
|
+
* @attr {number} count - Number of particles in the cell's pool.
|
|
19
|
+
*/
|
|
20
|
+
export declare class FieldCell extends HTMLElementBase {
|
|
21
|
+
static readonly observedAttributes: string[];
|
|
22
|
+
private readonly canvas;
|
|
23
|
+
private ctx;
|
|
24
|
+
private particles;
|
|
25
|
+
private raf;
|
|
26
|
+
private resizeObserver?;
|
|
27
|
+
private intersectionObserver?;
|
|
28
|
+
/** frame box in CSS pixels (post-DPR transform space). */
|
|
29
|
+
private boxW;
|
|
30
|
+
private boxH;
|
|
31
|
+
/** cursor in frame-local CSS px, or null when absent. */
|
|
32
|
+
private cursorX;
|
|
33
|
+
private cursorY;
|
|
34
|
+
private readonly onPointerMove;
|
|
35
|
+
private readonly onPointerLeave;
|
|
36
|
+
private readonly tick;
|
|
37
|
+
constructor();
|
|
38
|
+
/** the force this cell demonstrates (§25.1). */
|
|
39
|
+
get force(): string;
|
|
40
|
+
/** dot color. */
|
|
41
|
+
get color(): string;
|
|
42
|
+
/** particle count; `0` (default) means auto-size to the frame area. */
|
|
43
|
+
get count(): number;
|
|
44
|
+
attributeChangedCallback(): void;
|
|
45
|
+
connectedCallback(): void;
|
|
46
|
+
disconnectedCallback(): void;
|
|
47
|
+
private isPrefersReducedMotion;
|
|
48
|
+
/** Fit the canvas to the element box (DPR-aware) and rebuild the pool. */
|
|
49
|
+
private fit;
|
|
50
|
+
/** Build the particle pool sized to the frame area. */
|
|
51
|
+
private buildPool;
|
|
52
|
+
private start;
|
|
53
|
+
private stop;
|
|
54
|
+
/** Advance + render one frame. */
|
|
55
|
+
private step;
|
|
56
|
+
}
|
|
57
|
+
declare global {
|
|
58
|
+
interface HTMLElementTagNameMap {
|
|
59
|
+
'field-cell': FieldCell;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=field-cell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-cell.d.ts","sourceRoot":"","sources":["../src/field-cell.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAa5C;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,SAAU,SAAQ,eAAe;IAC5C,MAAM,CAAC,QAAQ,CAAC,kBAAkB,WAA+B;IAEjE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,GAAG,CAAyC;IAEpD,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,oBAAoB,CAAC,CAAuB;IAEpD,0DAA0D;IAC1D,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,IAAI,CAAK;IAEjB,yDAAyD;IACzD,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,OAAO,CAAuB;IAEtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAa;IAC5C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;;IA2BlC,gDAAgD;IAChD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,iBAAiB;IACjB,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,uEAAuE;IACvE,IAAI,KAAK,IAAI,MAAM,CAGlB;IAED,wBAAwB,IAAI,IAAI;IAKhC,iBAAiB,IAAI,IAAI;IAqBzB,oBAAoB,IAAI,IAAI;IAU5B,OAAO,CAAC,sBAAsB;IAO9B,0EAA0E;IAC1E,OAAO,CAAC,GAAG;IAmBX,uDAAuD;IACvD,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,IAAI;IAKZ,kCAAkC;IAClC,OAAO,CAAC,IAAI;CAwDb;AAMD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,YAAY,EAAE,SAAS,CAAC;KACzB;CACF"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { cellForce } from "./cell-force.js";
|
|
2
|
+
import { HTMLElementBase } from "./base.js";
|
|
3
|
+
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
4
|
+
/**
|
|
5
|
+
* `<field-cell force="swirl" color="#2dd4bf">` — an in-frame field surface (§25.1).
|
|
6
|
+
*
|
|
7
|
+
* A standalone field sized to its container that renders *one* force, with its own
|
|
8
|
+
* lightweight particle pool, its own pointer interaction, and a lifecycle that pauses
|
|
9
|
+
* when off-screen. It is **not** the §-engine: it's a lighter "demo/poster" engine,
|
|
10
|
+
* completely separate from the page `<field-root>` and the core field loop.
|
|
11
|
+
*
|
|
12
|
+
* - `ResizeObserver` re-fits the canvas (DPR-aware) and rebuilds the pool.
|
|
13
|
+
* - `IntersectionObserver` gates the rAF loop — paused when off-screen.
|
|
14
|
+
* - Honours `prefers-reduced-motion`: renders one static frame, no animation.
|
|
15
|
+
*
|
|
16
|
+
* @summary A standalone, in-frame demo field that renders one force with its own
|
|
17
|
+
* particle pool, in-view-gated. Registered as `<field-cell>`.
|
|
18
|
+
* @attr {string} force - The single force token rendered: `attract` | `repel` | `swirl` | `gravity` | `stream` | `buoyancy` | `tether`.
|
|
19
|
+
* @attr {string} color - Accent color (hex) for the cell's particles.
|
|
20
|
+
* @attr {number} count - Number of particles in the cell's pool.
|
|
21
|
+
*/
|
|
22
|
+
export class FieldCell extends HTMLElementBase {
|
|
23
|
+
static observedAttributes = ['force', 'color', 'count'];
|
|
24
|
+
canvas;
|
|
25
|
+
ctx = null;
|
|
26
|
+
particles = [];
|
|
27
|
+
raf = 0;
|
|
28
|
+
resizeObserver;
|
|
29
|
+
intersectionObserver;
|
|
30
|
+
/** frame box in CSS pixels (post-DPR transform space). */
|
|
31
|
+
boxW = 0;
|
|
32
|
+
boxH = 0;
|
|
33
|
+
/** cursor in frame-local CSS px, or null when absent. */
|
|
34
|
+
cursorX = null;
|
|
35
|
+
cursorY = null;
|
|
36
|
+
onPointerMove;
|
|
37
|
+
onPointerLeave;
|
|
38
|
+
tick;
|
|
39
|
+
constructor() {
|
|
40
|
+
super();
|
|
41
|
+
const root = this.attachShadow({ mode: 'open' });
|
|
42
|
+
const style = document.createElement('style');
|
|
43
|
+
style.textContent =
|
|
44
|
+
':host{display:block;position:relative}' +
|
|
45
|
+
'canvas{width:100%;height:100%;display:block}';
|
|
46
|
+
this.canvas = document.createElement('canvas');
|
|
47
|
+
root.append(style, this.canvas);
|
|
48
|
+
this.onPointerMove = (e) => {
|
|
49
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
50
|
+
this.cursorX = e.clientX - rect.left;
|
|
51
|
+
this.cursorY = e.clientY - rect.top;
|
|
52
|
+
};
|
|
53
|
+
this.onPointerLeave = () => {
|
|
54
|
+
this.cursorX = null;
|
|
55
|
+
this.cursorY = null;
|
|
56
|
+
};
|
|
57
|
+
this.tick = () => {
|
|
58
|
+
this.step();
|
|
59
|
+
this.raf = requestAnimationFrame(this.tick);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** the force this cell demonstrates (§25.1). */
|
|
63
|
+
get force() {
|
|
64
|
+
return this.getAttribute('force') ?? 'attract';
|
|
65
|
+
}
|
|
66
|
+
/** dot color. */
|
|
67
|
+
get color() {
|
|
68
|
+
return this.getAttribute('color') ?? '#4da3ff';
|
|
69
|
+
}
|
|
70
|
+
/** particle count; `0` (default) means auto-size to the frame area. */
|
|
71
|
+
get count() {
|
|
72
|
+
const v = Number(this.getAttribute('count'));
|
|
73
|
+
return Number.isFinite(v) && v > 0 ? Math.round(v) : 0;
|
|
74
|
+
}
|
|
75
|
+
attributeChangedCallback() {
|
|
76
|
+
// force/color read live each frame; count change rebuilds on next resize.
|
|
77
|
+
if (this.isPrefersReducedMotion() && this.ctx)
|
|
78
|
+
this.step();
|
|
79
|
+
}
|
|
80
|
+
connectedCallback() {
|
|
81
|
+
// a decorative demo surface — hide it from assistive tech (§18 a11y).
|
|
82
|
+
if (!this.hasAttribute('aria-hidden'))
|
|
83
|
+
this.setAttribute('aria-hidden', 'true');
|
|
84
|
+
this.ctx = this.canvas.getContext('2d');
|
|
85
|
+
if (!this.ctx)
|
|
86
|
+
return;
|
|
87
|
+
this.resizeObserver = new ResizeObserver(() => this.fit());
|
|
88
|
+
this.resizeObserver.observe(this);
|
|
89
|
+
this.fit();
|
|
90
|
+
this.intersectionObserver = new IntersectionObserver((entries) => {
|
|
91
|
+
const entry = entries[0];
|
|
92
|
+
if (entry?.isIntersecting)
|
|
93
|
+
this.start();
|
|
94
|
+
else
|
|
95
|
+
this.stop();
|
|
96
|
+
});
|
|
97
|
+
this.intersectionObserver.observe(this);
|
|
98
|
+
this.addEventListener('pointermove', this.onPointerMove);
|
|
99
|
+
this.addEventListener('pointerleave', this.onPointerLeave);
|
|
100
|
+
}
|
|
101
|
+
disconnectedCallback() {
|
|
102
|
+
this.stop();
|
|
103
|
+
this.resizeObserver?.disconnect();
|
|
104
|
+
this.resizeObserver = undefined;
|
|
105
|
+
this.intersectionObserver?.disconnect();
|
|
106
|
+
this.intersectionObserver = undefined;
|
|
107
|
+
this.removeEventListener('pointermove', this.onPointerMove);
|
|
108
|
+
this.removeEventListener('pointerleave', this.onPointerLeave);
|
|
109
|
+
}
|
|
110
|
+
isPrefersReducedMotion() {
|
|
111
|
+
return (typeof matchMedia !== 'undefined' &&
|
|
112
|
+
matchMedia('(prefers-reduced-motion: reduce)').matches);
|
|
113
|
+
}
|
|
114
|
+
/** Fit the canvas to the element box (DPR-aware) and rebuild the pool. */
|
|
115
|
+
fit() {
|
|
116
|
+
if (!this.ctx)
|
|
117
|
+
return;
|
|
118
|
+
const rect = this.getBoundingClientRect();
|
|
119
|
+
const w = Math.max(1, Math.round(rect.width));
|
|
120
|
+
const h = Math.max(1, Math.round(rect.height));
|
|
121
|
+
this.boxW = w;
|
|
122
|
+
this.boxH = h;
|
|
123
|
+
const dpr = Math.min(2, typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1);
|
|
124
|
+
this.canvas.width = Math.max(1, Math.round(w * dpr));
|
|
125
|
+
this.canvas.height = Math.max(1, Math.round(h * dpr));
|
|
126
|
+
this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
127
|
+
this.buildPool();
|
|
128
|
+
// Static cells (reduced motion or off-screen) get one fresh frame.
|
|
129
|
+
if (!this.raf)
|
|
130
|
+
this.step();
|
|
131
|
+
}
|
|
132
|
+
/** Build the particle pool sized to the frame area. */
|
|
133
|
+
buildPool() {
|
|
134
|
+
const area = this.boxW * this.boxH;
|
|
135
|
+
const n = this.count > 0 ? this.count : clamp(Math.round(area / 9000), 16, 90);
|
|
136
|
+
const pool = [];
|
|
137
|
+
for (let i = 0; i < n; i++) {
|
|
138
|
+
pool.push({
|
|
139
|
+
x: Math.random() * this.boxW,
|
|
140
|
+
y: Math.random() * this.boxH,
|
|
141
|
+
vx: (Math.random() - 0.5) * 0.4,
|
|
142
|
+
vy: (Math.random() - 0.5) * 0.4,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
this.particles = pool;
|
|
146
|
+
}
|
|
147
|
+
start() {
|
|
148
|
+
if (this.raf)
|
|
149
|
+
return;
|
|
150
|
+
if (this.isPrefersReducedMotion()) {
|
|
151
|
+
this.step(); // one static frame, no loop
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.raf = requestAnimationFrame(this.tick);
|
|
155
|
+
}
|
|
156
|
+
stop() {
|
|
157
|
+
if (this.raf)
|
|
158
|
+
cancelAnimationFrame(this.raf);
|
|
159
|
+
this.raf = 0;
|
|
160
|
+
}
|
|
161
|
+
/** Advance + render one frame. */
|
|
162
|
+
step() {
|
|
163
|
+
const ctx = this.ctx;
|
|
164
|
+
if (!ctx)
|
|
165
|
+
return;
|
|
166
|
+
const W = this.boxW;
|
|
167
|
+
const H = this.boxH;
|
|
168
|
+
const cx = W / 2;
|
|
169
|
+
const cy = H / 2;
|
|
170
|
+
const reach = Math.min(W, H) * 0.62;
|
|
171
|
+
const force = this.force;
|
|
172
|
+
const color = this.color;
|
|
173
|
+
const animate = this.raf !== 0;
|
|
174
|
+
ctx.clearRect(0, 0, W, H);
|
|
175
|
+
ctx.fillStyle = color;
|
|
176
|
+
ctx.shadowColor = color;
|
|
177
|
+
ctx.shadowBlur = 6;
|
|
178
|
+
for (const p of this.particles) {
|
|
179
|
+
if (animate) {
|
|
180
|
+
const dx = cx - p.x;
|
|
181
|
+
const dy = cy - p.y;
|
|
182
|
+
const { ax, ay } = cellForce(force, dx, dy, reach);
|
|
183
|
+
p.vx += ax;
|
|
184
|
+
p.vy += ay;
|
|
185
|
+
// Per-cell cursor repel (§25.1).
|
|
186
|
+
if (this.cursorX !== null && this.cursorY !== null) {
|
|
187
|
+
const rx = p.x - this.cursorX;
|
|
188
|
+
const ry = p.y - this.cursorY;
|
|
189
|
+
const dd = Math.hypot(rx, ry);
|
|
190
|
+
if (dd > 0 && dd < 80) {
|
|
191
|
+
const push = (1 - dd / 80) * 1.8;
|
|
192
|
+
p.vx += (rx / dd) * push;
|
|
193
|
+
p.vy += (ry / dd) * push;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
p.x += p.vx;
|
|
197
|
+
p.y += p.vy;
|
|
198
|
+
p.vx *= 0.94;
|
|
199
|
+
p.vy *= 0.94;
|
|
200
|
+
// Wrap edges.
|
|
201
|
+
if (p.x < 0)
|
|
202
|
+
p.x += W;
|
|
203
|
+
else if (p.x >= W)
|
|
204
|
+
p.x -= W;
|
|
205
|
+
if (p.y < 0)
|
|
206
|
+
p.y += H;
|
|
207
|
+
else if (p.y >= H)
|
|
208
|
+
p.y -= H;
|
|
209
|
+
}
|
|
210
|
+
ctx.beginPath();
|
|
211
|
+
ctx.arc(p.x, p.y, 1.4, 0, Math.PI * 2);
|
|
212
|
+
ctx.fill();
|
|
213
|
+
}
|
|
214
|
+
ctx.shadowBlur = 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (typeof customElements !== 'undefined' && !customElements.get('field-cell')) {
|
|
218
|
+
customElements.define('field-cell', FieldCell);
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=field-cell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-cell.js","sourceRoot":"","sources":["../src/field-cell.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { type AgentHandle, type AgentSpec, type AtomPayload, type ThreadLink, type FlowOptions, type OverlayInput, type ScalarGrid, type FieldEventType, type FieldEventMap, type BodySpec, type BodyHandle, type FieldChannelHandle } from '@fundamental-engine/core';
|
|
2
|
+
import { type FieldPlatform } from '@fundamental-engine/platform';
|
|
3
|
+
import { HTMLElementBase } from './base.ts';
|
|
4
|
+
import { type PlatformRuntime } from './platform-runtime.ts';
|
|
5
|
+
export { usePlatformRuntime, isPlatformRuntimeDefault, shouldUsePlatformRuntime, startPlatformRuntime } from './platform-runtime.ts';
|
|
6
|
+
export type { PlatformRuntime } from './platform-runtime.ts';
|
|
7
|
+
export declare class FieldField extends HTMLElementBase {
|
|
8
|
+
/**
|
|
9
|
+
* The engine options `<field-root>` forwards to `createBrowserField`, as ONE declarative table —
|
|
10
|
+
* the single source of truth for the option object built in `start()`, so a new forwarded
|
|
11
|
+
* `FieldOption` can never be silently dropped from forwarding the way `depth` once was. `accent`
|
|
12
|
+
* (raw passthrough so a `palette` with no `accent` adopts the palette stop) and
|
|
13
|
+
* `overlayCanvas`/`feedbackSink` (managed internally) are special-cased in `start()` and absent here.
|
|
14
|
+
*
|
|
15
|
+
* `observedAttributes` stays an explicit literal below — the Custom-Elements-Manifest analyzer reads
|
|
16
|
+
* it statically and can't enumerate a computed array — but the `option-attrs-observed` test pins it
|
|
17
|
+
* to this table (every `attr` here must be observed), so the two lists can't drift apart.
|
|
18
|
+
*/
|
|
19
|
+
private static readonly OPTIONS;
|
|
20
|
+
static readonly observedAttributes: string[];
|
|
21
|
+
private readonly canvas;
|
|
22
|
+
private field?;
|
|
23
|
+
/** Field Surfaces: the optional front overlay surface (light-DOM, above content). */
|
|
24
|
+
private overlayCanvas?;
|
|
25
|
+
/** element-level visibility: pages can hide the field (display:none) — skip draw work then. */
|
|
26
|
+
private visibilityObserver?;
|
|
27
|
+
private fieldVisible;
|
|
28
|
+
/** experimental platform runtime (Phase D); present only when the flag is on. */
|
|
29
|
+
platformRuntime?: PlatformRuntime;
|
|
30
|
+
/**
|
|
31
|
+
* The live `@fundamental-engine/platform` instance backing this field (Phase D default), or `undefined` on
|
|
32
|
+
* the legacy path. Read-only introspection for tools like the Inspector — the registries here are
|
|
33
|
+
* the real running state (measurements, state, feedback bindings, relationships, overlays, lint).
|
|
34
|
+
* Read with `measure.last()` etc.; don't call `measure.measure()` off the read phase.
|
|
35
|
+
*/
|
|
36
|
+
get platform(): FieldPlatform | undefined;
|
|
37
|
+
constructor();
|
|
38
|
+
/** the travelling accent (§9); defaults to the first palette color. */
|
|
39
|
+
get accent(): string;
|
|
40
|
+
/** particle-count multiplier (§2.5). */
|
|
41
|
+
get density(): number;
|
|
42
|
+
/** draw the background Currents (§24). */
|
|
43
|
+
get waves(): boolean;
|
|
44
|
+
/** render mode (§20.6); `none` = the signals-only engine — simulate + feed back, never draw (#297). */
|
|
45
|
+
get renderMode(): 'dots' | 'trails' | 'links' | 'metaballs' | 'voronoi' | 'streamlines' | 'flow' | 'none';
|
|
46
|
+
/** substrate background: `transparent` (present and not `"false"`) clears to transparent so the
|
|
47
|
+
* underlay composites over light content; default `opaque` paints the near-black substrate. */
|
|
48
|
+
get background(): 'opaque' | 'transparent';
|
|
49
|
+
/** Field Surfaces: the overlay reading(s) — one mode or a space-separated additive stack. Default `off`. */
|
|
50
|
+
get overlay(): OverlayInput;
|
|
51
|
+
/** color template name for the travelling accent (§9), or undefined for `ours`. */
|
|
52
|
+
get palette(): string | undefined;
|
|
53
|
+
/** conserved attention (§2.4): present and not `"false"` → one finite strength budget. */
|
|
54
|
+
get attention(): boolean;
|
|
55
|
+
/** cross-boundary causality (Concept 4): present and not `"false"` → density spills. */
|
|
56
|
+
get causality(): boolean;
|
|
57
|
+
/** first-class mass (§21.3): present and not `"false"` → particle mass ∝ size. */
|
|
58
|
+
get mass(): boolean;
|
|
59
|
+
/** density heatmap (field-systems H1): present and not `"false"` → the pooling glow underlay. */
|
|
60
|
+
get heatmap(): boolean;
|
|
61
|
+
/** `dpr-cap` — backing-store DPR ceiling (#410); undefined (engine default 2) if absent/invalid. */
|
|
62
|
+
get dprCap(): number | undefined;
|
|
63
|
+
/** `depth` — optional z-volume; undefined (engine default 0, the flat field) if absent/invalid. */
|
|
64
|
+
get depth(): number | undefined;
|
|
65
|
+
/** re-scan the document for `[data-body]` bodies after a DOM change. */
|
|
66
|
+
scan(): void;
|
|
67
|
+
/** alias of `scan`. */
|
|
68
|
+
rescan(): void;
|
|
69
|
+
/** recolor the travelling accent (§9). */
|
|
70
|
+
setAccent(hex: string): void;
|
|
71
|
+
/** swap the accent color template live (§9). */
|
|
72
|
+
setPalette(palette: string | readonly string[]): void;
|
|
73
|
+
/** switch the global formation (§7). */
|
|
74
|
+
setFormation(name: string): void;
|
|
75
|
+
/** toggle conserved attention (§2.4) live. */
|
|
76
|
+
setAttention(on: boolean): void;
|
|
77
|
+
/** toggle cross-boundary causality (Concept 4) live. */
|
|
78
|
+
setCausality(on: boolean): void;
|
|
79
|
+
/** switch the substrate background live: `transparent` composites the underlay over light content. */
|
|
80
|
+
setBackground(mode: 'opaque' | 'transparent'): void;
|
|
81
|
+
/** toggle the density heatmap layer (field-systems H1) live. */
|
|
82
|
+
setHeatmap(on: boolean): void;
|
|
83
|
+
/** lower/raise the backing-store DPR ceiling at runtime (the dominant fill-rate lever). */
|
|
84
|
+
setDprCap(cap: number): void;
|
|
85
|
+
/** switch the underlay render mode (§20.6) live; `none` = signals-only — stop drawing, keep the signals (#297). */
|
|
86
|
+
setRender(mode: 'dots' | 'trails' | 'links' | 'metaballs' | 'voronoi' | 'streamlines' | 'flow' | 'none'): void;
|
|
87
|
+
/** render field readings on the overlay surface (Field Surfaces — in front of content); one mode or an additive stack. */
|
|
88
|
+
setOverlay(mode: OverlayInput): void;
|
|
89
|
+
/** wire glowing connector lines between a set, or clear with null (§10). */
|
|
90
|
+
threads(list: ThreadLink[] | null): void;
|
|
91
|
+
/** a discrete one-shot: shove + heat matter near (x, y), optionally tinting it (§11). */
|
|
92
|
+
burst(x: number, y: number, hex?: string): void;
|
|
93
|
+
/** place/move a dynamic flow focus the field bends toward — pulls matter, curves the streamlines. */
|
|
94
|
+
flowTo(x: number, y: number, opts?: FlowOptions): void;
|
|
95
|
+
/** remove the flow focus. */
|
|
96
|
+
clearFlow(): void;
|
|
97
|
+
/** bind a data record to each base particle (its `weight` scales mass + size); pick back with `atomAt`. */
|
|
98
|
+
seed(atoms: readonly AtomPayload[]): void;
|
|
99
|
+
/** add an engine-stepped agent; returns an inert no-op handle if the field hasn't started. */
|
|
100
|
+
addAgent(spec: AgentSpec): AgentHandle;
|
|
101
|
+
/** add a programmatic body (no DOM) from a spec; a no-op handle until the field starts. */
|
|
102
|
+
addBody(spec: BodySpec): BodyHandle;
|
|
103
|
+
/** register a named external scalar field channel the engine samples; inert handle until the field starts. */
|
|
104
|
+
addField(name: string, sampler: (x: number, y: number) => number): FieldChannelHandle;
|
|
105
|
+
/** sample a registered field channel at (x, y); 0 for an unknown channel or before the field starts. */
|
|
106
|
+
sampleField(name: string, x: number, y: number): number;
|
|
107
|
+
/** the seeded record on the nearest particle to (x, y), or null — for hover-to-inspect. */
|
|
108
|
+
atomAt(x: number, y: number): AtomPayload | null;
|
|
109
|
+
/** focus the nearest seeded particle (hold + light it), returning its record — the dwell affordance. */
|
|
110
|
+
focusAt(x: number, y: number): AtomPayload | null;
|
|
111
|
+
/** release the focused particle. */
|
|
112
|
+
clearFocus(): void;
|
|
113
|
+
/** live particle-pool size — `store.size` forwarded through the public handle. */
|
|
114
|
+
particleCount(): number;
|
|
115
|
+
/** copy live particle state into `out` (stride 5: x, y, z, heat, size); returns the count written
|
|
116
|
+
* (0 before the field starts) — the render-agnostic swarm read-out an alternative surface draws. */
|
|
117
|
+
readParticles(out: Float32Array): number;
|
|
118
|
+
/** copy each live particle's stable id into a Uint32Array, parallel to readParticles. */
|
|
119
|
+
readParticleIds(out: Uint32Array): number;
|
|
120
|
+
/** kinetic/thermal/total energy snapshot for the current frame. */
|
|
121
|
+
energy(): {
|
|
122
|
+
kinetic: number;
|
|
123
|
+
thermal: number;
|
|
124
|
+
total: number;
|
|
125
|
+
count: number;
|
|
126
|
+
};
|
|
127
|
+
/** sample the live field at `(x, y)` — the net force vector a still test particle would feel
|
|
128
|
+
* (zero before the field starts). The seam external visualizers consume to build field geometry. */
|
|
129
|
+
sample(x: number, y: number): {
|
|
130
|
+
x: number;
|
|
131
|
+
y: number;
|
|
132
|
+
};
|
|
133
|
+
/** sample the smooth density scalar ∈ [0,1] at `(x, y)` (needs `heatmap`); 0 when off/not started. */
|
|
134
|
+
sampleScalar(x: number, y: number): number;
|
|
135
|
+
/** sample the density gradient ∇ at `(x, y)` — up-density direction (needs `heatmap`); `{0,0}` when off/not started. */
|
|
136
|
+
sampleGradient(x: number, y: number): {
|
|
137
|
+
x: number;
|
|
138
|
+
y: number;
|
|
139
|
+
};
|
|
140
|
+
/** open a named host-authorable scalar grid (deposit/sample/gradient/decay); a no-op grid until the field starts. */
|
|
141
|
+
grid(name: string): ScalarGrid;
|
|
142
|
+
/** subscribe to a discrete field event (absorb/release/settle); a no-op unsubscribe until the field starts. */
|
|
143
|
+
on<K extends FieldEventType>(type: K, cb: (e: FieldEventMap[K]) => void): () => void;
|
|
144
|
+
connectedCallback(): void;
|
|
145
|
+
disconnectedCallback(): void;
|
|
146
|
+
/**
|
|
147
|
+
* React to live attribute changes after mount (§13). The color / render / toggle attributes
|
|
148
|
+
* apply through the field's setters; the construction-time ones (`density`, `waves`, `mass`)
|
|
149
|
+
* rebuild the field. Fires before `connectedCallback` for the initial attributes too, which the
|
|
150
|
+
* `this.field` guard skips — the first mount reads every attribute itself.
|
|
151
|
+
*/
|
|
152
|
+
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
153
|
+
/** (re)create the engine on the canvas, reading the current attributes. */
|
|
154
|
+
private start;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* `<field-root>` — the recommended tag for the singleton field. The registry rejects registering one
|
|
158
|
+
* constructor under two tag names, so this is a thin subclass of {@link FieldField} with identical
|
|
159
|
+
* behaviour, attributes, and body contract.
|
|
160
|
+
*/
|
|
161
|
+
export declare class FieldRoot extends FieldField {
|
|
162
|
+
}
|
|
163
|
+
declare global {
|
|
164
|
+
interface HTMLElementTagNameMap {
|
|
165
|
+
'field-field': FieldField;
|
|
166
|
+
'field-root': FieldRoot;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
export * from './field-cell.ts';
|
|
170
|
+
export * from './cell-force.ts';
|
|
171
|
+
export * from './mount.ts';
|
|
172
|
+
export { FieldController, REGISTER_BODY, UNREGISTER_BODY, UPDATE_BODY, } from '@fundamental-engine/core';
|
|
173
|
+
export type { RegisterBodyDetail } from '@fundamental-engine/core';
|
|
174
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|