@descix/frqtl-node 0.6.0-p0.2 → 0.6.0-p4.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/CLAUDE.md +2 -2
- package/README.md +8 -4
- package/dist/dist-manifest.json +70 -62
- package/dist/eyes/index.js +5 -2
- package/dist/frqtl/assets/vendor/acorn.LICENSE +21 -0
- package/dist/frqtl/assets/vendor/acorn.PINNED.json +9 -0
- package/dist/frqtl/assets/vendor/acorn.mjs +6266 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +41 -0
- package/dist/frqtl/experiments/_shared/boot-failure.js +60 -0
- package/dist/frqtl/objects/FrqtlApp.js +136 -30
- package/dist/frqtl/objects/ObjectFactory.js +4 -5
- package/dist/frqtl/objects/RecipeContext.js +617 -9
- package/dist/frqtl/objects/instruments/hosted-eyes.js +268 -57
- package/dist/frqtl/objects/objects/QuantumEmitter.js +1 -1
- package/dist/frqtl/objects/record/Record.js +326 -0
- package/dist/frqtl/objects/record/open-record.js +22 -0
- package/dist/frqtl/objects/schema/index.js +72 -0
- package/dist/frqtl/parts/DetectorWall.js +1 -1
- package/dist/frqtl/parts/FrqtlControlsPanel.js +4 -2
- package/dist/frqtl/parts/FrqtlVision.js +21 -6
- package/dist/frqtl/parts/Wall.js +2 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +18 -14
- package/dist/frqtl/scenes/absorbing-wall.js +18 -14
- package/dist/frqtl/scenes/atomic-model.js +3 -0
- package/dist/frqtl/scenes/big-bang.js +3 -0
- package/dist/frqtl/scenes/blackbody.js +14 -11
- package/dist/frqtl/scenes/circuit-sat.js +7 -4
- package/dist/frqtl/scenes/collision-matrix-3d.js +3 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +19 -15
- package/dist/frqtl/scenes/counting-wall.js +19 -15
- package/dist/frqtl/scenes/double-slit.js +3 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +64 -58
- package/dist/frqtl/scenes/dse-one-slit.js +59 -55
- package/dist/frqtl/scenes/dse-photoelectric.js +28 -24
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +124 -99
- package/dist/frqtl/scenes/dse-two-emitters.js +64 -58
- package/dist/frqtl/scenes/dse-two-slits.js +69 -65
- package/dist/frqtl/scenes/empty.js +3 -0
- package/dist/frqtl/scenes/laser-buildup.js +23 -16
- package/dist/frqtl/scenes/mirror-3d.js +31 -27
- package/dist/frqtl/scenes/mirror-route.js +4 -1
- package/dist/frqtl/scenes/mirror.js +24 -20
- package/dist/frqtl/scenes/omni-3d.js +7 -4
- package/dist/frqtl/scenes/omni-source.js +7 -4
- package/dist/frqtl/scenes/self-interference.js +13 -10
- package/dist/frqtl/scenes/smoking-gun.js +15 -11
- package/dist/frqtl/scenes/straight-walk.js +7 -4
- package/dist/frqtl/scenes/sun.js +11 -8
- package/dist/frqtl/scenes/two-source.js +22 -17
- package/dist/frqtl/scenes/wave.js +10 -7
- package/dist/shim/bundle/index.js +112 -96
- package/dist/shim/bundle/indexHtml.js +66 -39
- package/dist/shim/engine/router.js +2 -2
- package/dist/shim/index.js +1 -1
- package/dist/shim/notebook/format.js +1 -1
- package/dist/shim/notebook/store.js +2 -3
- package/dist/shim/page-params/index.js +2 -2
- package/dist/shim/project/index.js +20 -5
- package/dist/shim/project/link.js +182 -37
- package/dist/shim/project/manifest.js +151 -0
- package/dist/shim/project/parts-module.js +67 -0
- package/dist/shim/project/parts-writer.js +457 -0
- package/dist/shim/project/record-format.js +345 -0
- package/dist/shim/project/specifiers.js +41 -13
- package/dist/shim/registry/index.js +1 -1
- package/dist/shim/registry/scene-source.js +44 -30
- package/dist/shim/registry/walkthrough.js +13 -12
- package/dist/shim/render-state/apply.js +11 -14
- package/dist/shim/storage/codec-chains.js +88 -0
- package/dist/shim/storage/folders.js +205 -0
- package/dist/shim/storage/index.js +64 -8
- package/dist/shim/storage/make-namespace-store.js +5 -5
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/dist/shim/bundle/frqnb.js +0 -112
- package/dist/shim/bundle/manifest.js +0 -95
- package/dist/shim/storage/modules.js +0 -249
- package/dist/shim/storage/projects.js +0 -150
|
@@ -28,6 +28,16 @@
|
|
|
28
28
|
* z absent → [cx − w/2, cy − h/2, w, h], and cz must be 0 — a z-less rect sits at z-centre
|
|
29
29
|
* 0 by the rule above, so any other cz THROWS rather than being dropped.
|
|
30
30
|
*
|
|
31
|
+
* A HAND places on the pixel lattice (frames sit on integer pixels; a hand between pixels is a
|
|
32
|
+
* screen-scale artifact), so every hand write is snapped once, here:
|
|
33
|
+
*
|
|
34
|
+
* snapRectPosition(t) → a rect write tuple with its POSITION on the lattice: `Math.round` of left,
|
|
35
|
+
* top (and z of a 6-tuple); the size (w, h, d) is returned byte-identical — a hand moves, it never
|
|
36
|
+
* resizes.
|
|
37
|
+
* snapCenter(c) → a centre ([x, y] / [x, y, z]) with every component rounded.
|
|
38
|
+
* Both are called by the hand doors only (FrqtlApp.placeDeclaredAt; STUDIO's drag / resize write). A
|
|
39
|
+
* value typed into a field is the user's literal and is never snapped; no build path calls them.
|
|
40
|
+
*
|
|
31
41
|
* ── The drawn VOLUME and the center-ray hit (STUDIO's aim) ──────────────────────
|
|
32
42
|
*
|
|
33
43
|
* The same rect also owns the VOLUME the aim ray tests and the shadow-surround paints, so the
|
|
@@ -94,6 +104,7 @@ function rectAtCenter(rect, center) {
|
|
|
94
104
|
var DEG = Math.PI / 180;
|
|
95
105
|
|
|
96
106
|
/** The rect's z-extent: d ?? w when it carries z (the engine's z-capable rectangle), else 0. */
|
|
107
|
+
function zExtent(rect) { return _zExtent(rect); }
|
|
97
108
|
function _zExtent(rect) {
|
|
98
109
|
if (!hasZ(rect)) { return 0; }
|
|
99
110
|
return (typeof rect.d === 'number') ? rect.d : rect.w;
|
|
@@ -220,9 +231,39 @@ function firstHit(origin, dir, volumes, margin) {
|
|
|
220
231
|
return best;
|
|
221
232
|
}
|
|
222
233
|
|
|
234
|
+
function _finiteTuple(v, who, lengths) {
|
|
235
|
+
if (!Array.isArray(v) || lengths.indexOf(v.length) === -1 ||
|
|
236
|
+
!v.every(function (n) { return typeof n === 'number' && isFinite(n); })) {
|
|
237
|
+
throw new TypeError('[LoDrawnCenter.' + who + '] ' + lengths.join(' or ') + ' finite numbers; got ' +
|
|
238
|
+
JSON.stringify(v) + '. (FAIL-LOUD.)');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* snapRectPosition(t) — a hand's rect write with its position on the pixel lattice, its size untouched.
|
|
244
|
+
* @param {number[]} t — [left, top, w, h] or [left, top, z, w, h, d] (RECT_TUPLE order) @returns {number[]}
|
|
245
|
+
*/
|
|
246
|
+
function snapRectPosition(t) {
|
|
247
|
+
_finiteTuple(t, 'snapRectPosition', [4, 6]);
|
|
248
|
+
var npos = t.length === 6 ? 3 : 2;
|
|
249
|
+
return t.map(function (n, i) { return i < npos ? Math.round(n) : n; });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* snapCenter(c) — a hand's centre write on the pixel lattice: every component rounded.
|
|
254
|
+
* @param {number[]} c — [x, y] or [x, y, z] @returns {number[]}
|
|
255
|
+
*/
|
|
256
|
+
function snapCenter(c) {
|
|
257
|
+
_finiteTuple(c, 'snapCenter', [2, 3]);
|
|
258
|
+
return c.map(function (n) { return Math.round(n); });
|
|
259
|
+
}
|
|
260
|
+
|
|
223
261
|
export {
|
|
224
262
|
drawnCenter,
|
|
225
263
|
rectAtCenter,
|
|
264
|
+
snapRectPosition,
|
|
265
|
+
snapCenter,
|
|
266
|
+
zExtent,
|
|
226
267
|
hasZ,
|
|
227
268
|
drawnVolume,
|
|
228
269
|
rayIntersect,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* boot-failure.js — THE one boot-failure overlay painter.
|
|
5
|
+
*
|
|
6
|
+
* paintBootFailure(axis, msg) paint a FAIL-LOUD overlay for a boot axis ('engine' | 'renderer')
|
|
7
|
+
* setBootOverlayHost(el) scope later overlays into `el` (the boot's `overlayHost`)
|
|
8
|
+
*
|
|
9
|
+
* A leaf: the boot module (boot.js) and any runner that discovers a boot fault after the boot
|
|
10
|
+
* (FrqtlApp's empty-display-slot diagnosis) paint through this ONE painter.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const AXIS_STYLE = {
|
|
14
|
+
engine: { id: 'egpt-engine-error', marker: 'data-egpt-engine-error', bg: '#1a0000', fg: '#ff6b6b', border: '#b6161c' },
|
|
15
|
+
renderer: { id: 'egpt-renderer-error', marker: 'data-egpt-renderer-error', bg: '#000a1a', fg: '#6b9eff', border: '#1640b6' }
|
|
16
|
+
};
|
|
17
|
+
let _overlayHost = null;
|
|
18
|
+
|
|
19
|
+
/** The element the next overlays paint into (null = the full viewport) — set by the boot from its opts. */
|
|
20
|
+
export function setBootOverlayHost(el) { _overlayHost = el || null; }
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* THE boot-failure overlay: scoped into the boot's overlay host when one was given, else the full
|
|
24
|
+
* viewport. One overlay per axis (a second failure on the same axis does not stack). Console-loud too.
|
|
25
|
+
*
|
|
26
|
+
* @param {'engine'|'renderer'} axis 'engine' covers every non-renderer boot failure (engine, game layer, sketch)
|
|
27
|
+
* @param {string} msg
|
|
28
|
+
*/
|
|
29
|
+
export function paintBootFailure(axis, msg) {
|
|
30
|
+
const st = AXIS_STYLE[axis];
|
|
31
|
+
if (!st) { throw new Error('[boot] paintBootFailure: axis must be engine | renderer (got ' + JSON.stringify(axis) + ')'); }
|
|
32
|
+
if (typeof console !== 'undefined' && console.error) { console.error(msg); }
|
|
33
|
+
function paint() {
|
|
34
|
+
if (_overlayHost) {
|
|
35
|
+
if (_overlayHost.querySelector && _overlayHost.querySelector('[' + st.marker + ']')) { return; }
|
|
36
|
+
const b = document.createElement('div');
|
|
37
|
+
b.setAttribute(st.marker, '1');
|
|
38
|
+
b.setAttribute('data-egpt-boot-error', '1');
|
|
39
|
+
b.style.cssText = 'position:absolute;inset:0;z-index:9998;background:' + st.bg + ';color:' + st.fg + ';' +
|
|
40
|
+
'font:12px/1.5 ui-monospace,Menlo,Consolas,monospace;padding:14px;white-space:pre-wrap;overflow:auto;' +
|
|
41
|
+
'border:2px solid ' + st.border + ';';
|
|
42
|
+
b.textContent = msg;
|
|
43
|
+
const cs = getComputedStyle(_overlayHost);
|
|
44
|
+
if (!cs || cs.position === 'static') { _overlayHost.style.position = 'relative'; }
|
|
45
|
+
_overlayHost.appendChild(b);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (document.getElementById(st.id)) { return; }
|
|
49
|
+
const b = document.createElement('div');
|
|
50
|
+
b.id = st.id;
|
|
51
|
+
b.setAttribute('data-egpt-boot-error', '1');
|
|
52
|
+
b.style.cssText = 'position:fixed;inset:0;z-index:99999;background:' + st.bg + ';color:' + st.fg + ';' +
|
|
53
|
+
'font:13px/1.55 ui-monospace,Menlo,Consolas,monospace;padding:22px;white-space:pre-wrap;overflow:auto;' +
|
|
54
|
+
'border:2px solid ' + st.border + ';';
|
|
55
|
+
b.textContent = msg;
|
|
56
|
+
(document.body || document.documentElement).appendChild(b);
|
|
57
|
+
}
|
|
58
|
+
if (document.body) { paint(); } else { addEventListener('DOMContentLoaded', paint); }
|
|
59
|
+
}
|
|
60
|
+
|
|
@@ -62,6 +62,8 @@
|
|
|
62
62
|
*/
|
|
63
63
|
|
|
64
64
|
import { ObjectSchema } from './schema/index.js';
|
|
65
|
+
import { kindOf as _kindRow } from './kinds.js';
|
|
66
|
+
import { moduleOfKind } from '../../shim/project/parts-module.js';
|
|
65
67
|
import { FrqtlCapture } from './instruments/capture.js';
|
|
66
68
|
import * as FrqtlDisplay from '../compat/renderer/display-slot.js';
|
|
67
69
|
import * as RendererProviderContract from '../compat/renderer/renderer-provider-contract.js';
|
|
@@ -867,9 +869,19 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
867
869
|
* THROWS here instead of leaking an unresolvable LO into the scene. `tickFns` are the host
|
|
868
870
|
* tick-functions the build pushed (an emitter's emit fn, an absorb radiator): what the
|
|
869
871
|
* declaration owns besides what it draws. THROWS on a re-declaration.
|
|
870
|
-
*
|
|
871
|
-
|
|
872
|
-
|
|
872
|
+
*
|
|
873
|
+
* `row` = { kind } (ObjectFactory.create): the census fields every declared object carries, read as
|
|
874
|
+
* `d.inParts` / `d.module` off a runner.declared entry (not enumerable, not writable):
|
|
875
|
+
* inParts — true when the object came from the sketch's `export const parts` block (exp.declare),
|
|
876
|
+
* or was written into it since (RecipeContext.toSketch); false for a code declaration.
|
|
877
|
+
* Set only by RecipeContext, the parts block's one owner (_adoptIntoParts)
|
|
878
|
+
* module — the specifier a sketch imports its kind by (lib/shim/project/parts-module.js
|
|
879
|
+
* moduleOfKind over the kind row's `from` and the project files the facade last linked):
|
|
880
|
+
* '@descix/frqtl-web/parts', a project path, or null (an internal kind)
|
|
881
|
+
* @param {Object} handle @param {Array} members @param {Array} [tickFns] @param {{kind: string}} [row]
|
|
882
|
+
* @returns {Object} handle
|
|
883
|
+
*/
|
|
884
|
+
FrqtlApp.prototype.declare = function (handle, members, tickFns, row) {
|
|
873
885
|
if (!handle || typeof handle !== 'object') {
|
|
874
886
|
throw new TypeError('[FrqtlApp.declare] a declared object is a handle, got ' + typeof handle + '. (FAIL-LOUD.)');
|
|
875
887
|
}
|
|
@@ -893,15 +905,67 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
893
905
|
'object. (FAIL-LOUD.)');
|
|
894
906
|
}
|
|
895
907
|
}
|
|
908
|
+
var kind = (row && row.kind) || handle._schemaKind;
|
|
909
|
+
var kr = kind ? _kindRow(kind) : null;
|
|
910
|
+
_setCensusRow(handle, {
|
|
911
|
+
inParts: false,
|
|
912
|
+
module: kr ? moduleOfKind(kr.from, this._projectUrls || null) : null
|
|
913
|
+
});
|
|
896
914
|
return handle;
|
|
897
915
|
};
|
|
898
916
|
|
|
917
|
+
// The census fields of each declared object (FrqtlApp.declare). Keyed by the handle; read through two
|
|
918
|
+
// getters defined on it, so `runner.declared[i].inParts` / `.module` are fields of the one census.
|
|
919
|
+
var _censusRows = new WeakMap();
|
|
920
|
+
function _setCensusRow(handle, r) {
|
|
921
|
+
_censusRows.set(handle, r);
|
|
922
|
+
if (!Object.prototype.hasOwnProperty.call(handle, 'inParts')) {
|
|
923
|
+
Object.defineProperty(handle, 'inParts', { configurable: true, enumerable: false,
|
|
924
|
+
get: function () { var x = _censusRows.get(this); return x ? x.inParts : false; } });
|
|
925
|
+
Object.defineProperty(handle, 'module', { configurable: true, enumerable: false,
|
|
926
|
+
get: function () { var x = _censusRows.get(this); return x ? x.module : null; } });
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// THE HAND MARK: a declared object a device door wrote since the writer last ran (updateObjectProperty —
|
|
931
|
+
// a drag, a resize, an Inspector or knob write — and placeDeclaredAt — a carry, a lab's hand). The writer
|
|
932
|
+
// (RecipeContext.toSketch) writes, or refuses, ONLY marked objects: a part its own scene code moves (a
|
|
933
|
+
// bullet's flight tick-function) is the run, not a hand.
|
|
934
|
+
function _markHand(app, x) {
|
|
935
|
+
var list = app._declared;
|
|
936
|
+
if (!list || !list.length) { return; } // nothing declared: no object a writer could write
|
|
937
|
+
var d = app.declaredOf(x);
|
|
938
|
+
if (d) { (app._handTouched || (app._handTouched = new Set())).add(d); }
|
|
939
|
+
}
|
|
940
|
+
/** _takeHandTouched() → the declared objects a device door wrote since the last call; the call clears the mark. */
|
|
941
|
+
FrqtlApp.prototype._takeHandTouched = function () {
|
|
942
|
+
var out = this._handTouched || new Set();
|
|
943
|
+
this._handTouched = new Set();
|
|
944
|
+
return out;
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* _adoptIntoParts(d) — `d` is an entry of the parts block (its key is its name): declared there (exp.declare)
|
|
949
|
+
* or written there (RecipeContext.toSketch). RecipeContext's alone, the parts block's one owner.
|
|
950
|
+
*/
|
|
951
|
+
FrqtlApp.prototype._adoptIntoParts = function (d) {
|
|
952
|
+
var x = _censusRows.get(d);
|
|
953
|
+
if (!x) { throw new Error('[FrqtlApp] _adoptIntoParts: "' + (d && d.name) + '" is not a declared object. (FAIL-LOUD.)'); }
|
|
954
|
+
x.inParts = true;
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
/** beginWorld() — a (re-)inited world has not stepped: its last TickData is gone (worldTick reads 0, repaint draws nothing). */
|
|
958
|
+
FrqtlApp.prototype.beginWorld = function () {
|
|
959
|
+
this._lastTickData = null;
|
|
960
|
+
};
|
|
961
|
+
|
|
899
962
|
/** clearDeclared() — empty the registry. Called where the world re-inits (FrqtlApp.setup, and the
|
|
900
963
|
* RecipeContext co-init exp.createCanvas / exp.createScene): a fresh world has no declared objects. */
|
|
901
964
|
FrqtlApp.prototype.clearDeclared = function () {
|
|
902
965
|
this._declared = [];
|
|
903
966
|
this._declaredMembers = new Map();
|
|
904
967
|
this._declaredTicks = new Map();
|
|
968
|
+
this._handTouched = new Set(); // a fresh world holds no hand
|
|
905
969
|
};
|
|
906
970
|
|
|
907
971
|
/**
|
|
@@ -1077,6 +1141,14 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
1077
1141
|
*/
|
|
1078
1142
|
FrqtlApp.prototype.updateObjectProperty = function (lo, prop, value) {
|
|
1079
1143
|
if (!lo) return;
|
|
1144
|
+
// A parts entry's name IS its key: no door renames it (toSketch refuses, setDeclared throws, and so does this),
|
|
1145
|
+
// whether addressed by its declared handle or by a member LO of it (declaredOf walks the owner chain).
|
|
1146
|
+
var entry = prop === 'name' ? this.declaredOf(lo) : null;
|
|
1147
|
+
if (entry && entry.inParts) {
|
|
1148
|
+
throw new TypeError('[FrqtlApp.updateObjectProperty] "name" of the parts entry "' + entry.name + '": ' +
|
|
1149
|
+
FrqtlApp.RENAME_REFUSAL + ' (FAIL-LOUD.)');
|
|
1150
|
+
}
|
|
1151
|
+
_markHand(this, lo);
|
|
1080
1152
|
|
|
1081
1153
|
// Every shipped object class STAMPS `_schemaKind` (LargeObject, QuantumEmitter,
|
|
1082
1154
|
// PointSource, Wall, DetectorWall, SphereEmitter, …), so the
|
|
@@ -1443,6 +1515,7 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
1443
1515
|
* where the object now IS (`centerOf`).
|
|
1444
1516
|
*/
|
|
1445
1517
|
FrqtlApp.prototype.placeDeclaredAt = function (d, point) {
|
|
1518
|
+
_markHand(this, d);
|
|
1446
1519
|
this._requireDeclared(d, 'placeDeclaredAt');
|
|
1447
1520
|
if (!Array.isArray(point) || (point.length !== 2 && point.length !== 3) ||
|
|
1448
1521
|
!point.every(function (v) { return typeof v === 'number' && isFinite(v); })) {
|
|
@@ -1463,9 +1536,10 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
1463
1536
|
zHeld = Math.abs(k[2]) > FrqtlApp.Z_HOLD_EPS;
|
|
1464
1537
|
k = [k[0], k[1], 0];
|
|
1465
1538
|
}
|
|
1466
|
-
|
|
1539
|
+
// A hand places on the pixel lattice (LoDrawnCenter: the position snaps, the size is kept).
|
|
1540
|
+
this.updateObjectProperty(d, 'rect', L.snapRectPosition(L.rectAtCenter(d.rect, k)));
|
|
1467
1541
|
} else {
|
|
1468
|
-
this.updateObjectProperty(d, 'center', k);
|
|
1542
|
+
this.updateObjectProperty(d, 'center', LoDrawnCenter.snapCenter(k));
|
|
1469
1543
|
}
|
|
1470
1544
|
return { center: this.centerOf(d), zHeld: zHeld };
|
|
1471
1545
|
};
|
|
@@ -1556,6 +1630,9 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
1556
1630
|
/** Beyond this |z| (world px) a carried centre is not expressible by a z-LESS rect (it sits
|
|
1557
1631
|
* at z-centre 0 — lo-drawn-center): the object stays on the z = 0 plane and says Z HELD. */
|
|
1558
1632
|
FrqtlApp.Z_HOLD_EPS = 1e-6;
|
|
1633
|
+
/** The one wording of "a parts entry is never renamed by a hand" (updateObjectProperty, RecipeContext). */
|
|
1634
|
+
FrqtlApp.RENAME_REFUSAL = 'the key is the name — a rename is a new key in `parts` (with every reference to it, ' +
|
|
1635
|
+
'in code) or a fork of the sketch; a hand never writes a `name` into an entry.';
|
|
1559
1636
|
|
|
1560
1637
|
var AIM_OFF = 'off', AIM_ROAMING = 'roaming', AIM_READY = 'grab-ready', AIM_GRABBED = 'grabbed';
|
|
1561
1638
|
|
|
@@ -2182,9 +2259,10 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
2182
2259
|
/**
|
|
2183
2260
|
* retainSceneReplay(fn) — the ONE retainer of "how this runner's scene was built", so
|
|
2184
2261
|
* `restart()` can replay it. Whichever path built the scene sets it: `setup(factory, opts)`
|
|
2185
|
-
* retains its factory replay; `RecipeContext.build(sketch)` retains
|
|
2186
|
-
*
|
|
2187
|
-
*
|
|
2262
|
+
* retains its factory replay; `RecipeContext.build(sketch)` retains its rebuild (a sketch built from
|
|
2263
|
+
* linked text re-links the facade's CURRENT text — what the writer last wrote — and builds that). A
|
|
2264
|
+
* later build replaces the earlier retention (the runner holds ONE scene).
|
|
2265
|
+
* @param {Function} fn — replays the scene; its return value (or the value its promise resolves) is restart()'s.
|
|
2188
2266
|
*/
|
|
2189
2267
|
FrqtlApp.prototype.retainSceneReplay = function (fn) {
|
|
2190
2268
|
if (typeof fn !== 'function') {
|
|
@@ -2195,25 +2273,24 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
2195
2273
|
};
|
|
2196
2274
|
|
|
2197
2275
|
/**
|
|
2198
|
-
* restart() — the PAGE-LEVEL restart primitive
|
|
2199
|
-
*
|
|
2200
|
-
*
|
|
2201
|
-
*
|
|
2202
|
-
*
|
|
2203
|
-
*
|
|
2276
|
+
* restart() → Promise — the PAGE-LEVEL restart primitive: stop → replay the RETAINED scene
|
|
2277
|
+
* (`retainSceneReplay` — a setup factory's world + part steps with its ORIGINAL opts, a sketch's
|
|
2278
|
+
* rebuild on the same runner and canvas, or — for a sketch built from linked text — a re-link of the
|
|
2279
|
+
* facade's current text, then its build) → start. The scene returns to tick 0 IN-PAGE: no reload, so
|
|
2280
|
+
* the camera orbit, render options and any host wiring survive. The declared `restart` control
|
|
2281
|
+
* (controlAction.js) routes here for every host.
|
|
2204
2282
|
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
*
|
|
2208
|
-
* stacking-regression this seam was built to avoid).
|
|
2283
|
+
* ALWAYS a Promise. A replay that returns a plain value (a factory, a function-built sketch) has run
|
|
2284
|
+
* and the loop has restarted before restart() returns; its promise resolves at once. A replay that
|
|
2285
|
+
* returns a promise (a re-link) resolves after the rebuild and the start. Every caller awaits it.
|
|
2209
2286
|
*
|
|
2210
|
-
*
|
|
2211
|
-
* a
|
|
2212
|
-
*
|
|
2287
|
+
* Teardown runs EXACTLY ONCE — the replay owns it (`setup()` calls the previous setupResult's
|
|
2288
|
+
* `dispose()`; a sketch build tears the previous scene down first — RecipeContext.build), so this
|
|
2289
|
+
* method must NOT dispose again.
|
|
2213
2290
|
*
|
|
2214
|
-
* @returns {
|
|
2215
|
-
* @throws {Error} FAIL-LOUD when nothing built a scene on this runner (there is nothing
|
|
2216
|
-
*
|
|
2291
|
+
* @returns {Promise<*>} the replay's result (a setup result, or the sketch's facade).
|
|
2292
|
+
* @throws {Error} FAIL-LOUD when nothing built a scene on this runner (there is nothing to replay) —
|
|
2293
|
+
* never a silent no-op; a failed replay REJECTS.
|
|
2217
2294
|
*/
|
|
2218
2295
|
FrqtlApp.prototype.restart = function () {
|
|
2219
2296
|
if (typeof this._sceneReplay !== 'function') {
|
|
@@ -2222,12 +2299,19 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
2222
2299
|
'(FAIL-LOUD — a restart with nothing to replay would silently do nothing.)');
|
|
2223
2300
|
}
|
|
2224
2301
|
this.stop();
|
|
2225
|
-
var
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2302
|
+
var self = this;
|
|
2303
|
+
function resume(result) {
|
|
2304
|
+
self.beginWorld();
|
|
2305
|
+
// Always resume: "restart" means run the scene again from tick 0 — a restart
|
|
2306
|
+
// that left the loop paused would read as a dead button.
|
|
2307
|
+
self.start();
|
|
2308
|
+
return result;
|
|
2309
|
+
}
|
|
2310
|
+
var result;
|
|
2311
|
+
try { result = this._sceneReplay(); }
|
|
2312
|
+
catch (err) { return Promise.reject(err); }
|
|
2313
|
+
if (result && typeof result.then === 'function') { return result.then(resume); }
|
|
2314
|
+
return Promise.resolve(resume(result));
|
|
2231
2315
|
};
|
|
2232
2316
|
|
|
2233
2317
|
// ── Renderer resolution ────────────────────────────────────────────────
|
|
@@ -2493,6 +2577,28 @@ import { paintBootFailure } from '../experiments/_shared/boot-failure.js';
|
|
|
2493
2577
|
return this._tickSuspend;
|
|
2494
2578
|
};
|
|
2495
2579
|
|
|
2580
|
+
/**
|
|
2581
|
+
* runner.worldTick — READ-ONLY: the tick of THIS world: 0 before its first step, else the last delivered tick.
|
|
2582
|
+
* The 0 is a stated fact, not a default: `_lastTickData` is null until the world's first step, and beginWorld()
|
|
2583
|
+
* (the co-init and restart()) is the one writer of that null. It does NOT read `universe.tick`: the compat wrapper
|
|
2584
|
+
* keeps that field across a re-init until the next doTick, so after a restart it still answers the previous
|
|
2585
|
+
* world's tick. Every runner holds a world (its universe is built at construction), whether a sketch built it
|
|
2586
|
+
* through the replay door or a page built it by hand, so this always answers. THROWS when a delivered tick is not
|
|
2587
|
+
* a whole number ≥ 0.
|
|
2588
|
+
*/
|
|
2589
|
+
Object.defineProperty(FrqtlApp.prototype, 'worldTick', {
|
|
2590
|
+
configurable: true,
|
|
2591
|
+
get: function () {
|
|
2592
|
+
if (this._lastTickData === null) { return 0; }
|
|
2593
|
+
var t = this._lastTickData && this._lastTickData.universeData && this._lastTickData.universeData.tick;
|
|
2594
|
+
if (!(typeof t === 'number' && t >= 0 && t % 1 === 0)) {
|
|
2595
|
+
throw new Error('[FrqtlApp.worldTick] the last delivered TickData carries tick ' + String(t) +
|
|
2596
|
+
', not a whole number ≥ 0. (FAIL-LOUD.)');
|
|
2597
|
+
}
|
|
2598
|
+
return t;
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
|
|
2496
2602
|
/** @returns {boolean} whether the tick clock is currently held. */
|
|
2497
2603
|
Object.defineProperty(FrqtlApp.prototype, 'ticksSuspended', {
|
|
2498
2604
|
configurable: true,
|
|
@@ -95,8 +95,7 @@ import { V1_REPLACEMENTS } from './v1-replacements.js';
|
|
|
95
95
|
// `preset` is expanded by the OBJECT constructor (Wall.expandWallPreset), not by
|
|
96
96
|
// a builder — it reaches the constructor through the forwarded props bag.
|
|
97
97
|
'preset',
|
|
98
|
-
//
|
|
99
|
-
// writes back onto its config (lib/features/ide/codegen.js).
|
|
98
|
+
// A PointSource's construction-only opts (its builder reads them; no descriptor field).
|
|
100
99
|
'_wavelength', '_optColor', '_emergentPhysics'
|
|
101
100
|
];
|
|
102
101
|
|
|
@@ -187,7 +186,7 @@ import { V1_REPLACEMENTS } from './v1-replacements.js';
|
|
|
187
186
|
* returns (never one of its members) is recorded in `ctx.runner.declared` with the census LOs
|
|
188
187
|
* the build added as its members and the host tick-functions the build pushed
|
|
189
188
|
* (`FrqtlApp.declare` — this is its ONE call site). What a
|
|
190
|
-
* declaring call (`exp.addObject`, a palette drop
|
|
189
|
+
* declaring call (`exp.addObject`, `exp.declare`, a palette drop) produced is what STUDIO
|
|
191
190
|
* holds; a part's composition is never declared.
|
|
192
191
|
*
|
|
193
192
|
* FAIL-LOUD: an unknown kind THROWS naming the registered kinds and, when
|
|
@@ -264,11 +263,11 @@ import { V1_REPLACEMENTS } from './v1-replacements.js';
|
|
|
264
263
|
'scene. (FAIL-LOUD.)');
|
|
265
264
|
}
|
|
266
265
|
// ... and the host tick-functions it pushed are the declaration's too (an emitter's emit
|
|
267
|
-
// fn, an absorb radiator): behaviour the declaration owns
|
|
266
|
+
// fn, an absorb radiator): behaviour the declaration owns.
|
|
268
267
|
var tfsAfter = (runner.universe && Array.isArray(runner.universe.experimentTickFunctions))
|
|
269
268
|
? runner.universe.experimentTickFunctions : [];
|
|
270
269
|
var ticks = tfsAfter.filter(function (fn) { return !ticksBefore.has(fn); });
|
|
271
|
-
runner.declare(handle, members, ticks);
|
|
270
|
+
runner.declare(handle, members, ticks, { kind: kind });
|
|
272
271
|
return handle;
|
|
273
272
|
},
|
|
274
273
|
|