@descix/frqtl-node 0.6.1-p3.4 → 0.7.0-p1
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 +3 -3
- package/README.md +4 -3
- package/dist/dist-manifest.json +26 -56
- package/dist/frqtl/experiments/_shared/empty-sketch.js +17 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +6 -6
- package/dist/frqtl/objects/setup/setupFxSmoke.js +2 -2
- package/dist/frqtl/objects/setup/setupG3Collide.js +1 -1
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +2 -2
- package/dist/frqtl/objects/v1-replacements.js +1 -1
- package/dist/frqtl/parts/OvenBox.js +2 -2
- package/dist/frqtl/parts/SphereEmitter.js +1 -1
- package/dist/shim/engine/router.js +18 -6
- package/dist/shim/engine/site-assemble.mjs +268 -0
- package/dist/shim/engine/site-graph.js +42 -14
- package/dist/shim/host/device.js +1 -1
- package/dist/shim/index.js +3 -7
- package/dist/shim/notebook/catalog.js +2 -2
- package/dist/shim/notebook/format.js +1 -1
- package/dist/shim/notebook/knob.js +1 -1
- package/dist/shim/notebook/notebook-knobs.js +1 -1
- package/dist/shim/project/specifiers.js +8 -8
- package/dist/shim/registry/copy-gate.js +3 -3
- package/dist/shim/registry/index.js +9 -12
- package/dist/shim/registry/roster-link.js +142 -0
- package/dist/shim/registry/roster.js +595 -0
- package/dist/shim/registry/scene-source.js +80 -105
- package/dist/shim/registry/studio-landing.js +15 -7
- package/dist/shim/render/sun-look.js +1 -2
- package/llms.txt +4 -2
- package/package.json +1 -1
- package/dist/frqtl/scenes/absorbing-wall-3d.js +0 -37
- package/dist/frqtl/scenes/absorbing-wall.js +0 -36
- package/dist/frqtl/scenes/atomic-model.js +0 -91
- package/dist/frqtl/scenes/big-bang.js +0 -49
- package/dist/frqtl/scenes/blackbody.js +0 -84
- package/dist/frqtl/scenes/circuit-sat.js +0 -131
- package/dist/frqtl/scenes/collision-matrix-3d.js +0 -110
- package/dist/frqtl/scenes/counting-wall-3d.js +0 -40
- package/dist/frqtl/scenes/counting-wall.js +0 -42
- package/dist/frqtl/scenes/double-slit.js +0 -34
- package/dist/frqtl/scenes/dse-one-emitter.js +0 -200
- package/dist/frqtl/scenes/dse-one-slit.js +0 -118
- package/dist/frqtl/scenes/dse-photoelectric.js +0 -74
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +0 -279
- package/dist/frqtl/scenes/dse-two-emitters.js +0 -201
- package/dist/frqtl/scenes/dse-two-slits.js +0 -136
- package/dist/frqtl/scenes/empty.js +0 -17
- package/dist/frqtl/scenes/laser-buildup.js +0 -56
- package/dist/frqtl/scenes/mirror-3d.js +0 -60
- package/dist/frqtl/scenes/mirror-route.js +0 -164
- package/dist/frqtl/scenes/mirror.js +0 -42
- package/dist/frqtl/scenes/omni-3d.js +0 -30
- package/dist/frqtl/scenes/omni-source.js +0 -25
- package/dist/frqtl/scenes/self-interference.js +0 -42
- package/dist/frqtl/scenes/smoking-gun.js +0 -126
- package/dist/frqtl/scenes/straight-walk.js +0 -25
- package/dist/frqtl/scenes/sun.js +0 -42
- package/dist/frqtl/scenes/two-source.js +0 -42
- package/dist/frqtl/scenes/wave.js +0 -29
- package/dist/shim/engine/artifact-shell.js +0 -211
- package/dist/shim/registry/exhibits.js +0 -436
- package/dist/shim/registry/scene-roster.js +0 -35
- package/dist/shim/registry/showcase-link.js +0 -61
- package/dist/shim/registry/walkthrough.js +0 -752
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* exhibits.js — the SHOWCASE ROSTER ("Impossible things, running"), the run-once card of every
|
|
5
|
-
* roster scene the GYM plays, and the showcase copy around them (the intro card, the through-line
|
|
6
|
-
* and the one invitation). It is the ONE owner of every word of that copy.
|
|
7
|
-
*
|
|
8
|
-
* EXHIBITS is the ONE source for the front-page tiles (their links: showcase-link.js::showcaseHref) and the scene card:
|
|
9
|
-
* nine rows in front-page order, each
|
|
10
|
-
* `{ kind, id, mode, title, tile, what, why, how, controls, still }`.
|
|
11
|
-
* - `kind` is 'rung' (a walk-through rung, lib/shim/registry/walkthrough.js); `id` resolves there. A row of
|
|
12
|
-
* any other kind THROWS naming the rung form.
|
|
13
|
-
* - `mode` is a rung's declared mode (null for a single-mode rung).
|
|
14
|
-
* - `title` is the tile's and the card's title; `tile` is the published one-sentence tile line.
|
|
15
|
-
* - `what` (What you are seeing), `why` (Why it is impossible the usual way) and `how` (How FRQTL
|
|
16
|
-
* gets it) are the card's copy, CARD_PARTS, every one REQUIRED on every row.
|
|
17
|
-
* - `controls` (optional) is how to drive a scene's declared controls and what to read; only an
|
|
18
|
-
* interactive scene carries one.
|
|
19
|
-
* - `still` (optional) is the tile still: `{ tick, alt, pose? }` — the tick the tile-stills
|
|
20
|
-
* instrument (apps/ide/test/tile-stills-capture.spec.js) drives the scene to before capturing,
|
|
21
|
-
* the still's alt text (REQUIRED with a still; its one home), and on a 3D scene whose default
|
|
22
|
-
* camera hides its structure, the camera pose (a setOrbit patch).
|
|
23
|
-
*
|
|
24
|
-
* exhibitFor({ kind, id, modeId? }) → the roster row, or null (declared: the scene has no card)
|
|
25
|
-
* sceneKey({ kind, id, modeId? }) → the scene's run-once seen-key (the one mode normalizer)
|
|
26
|
-
* showcaseCard(row) → { kind, id, modeId, seenKey, title, paragraphs, is3D }
|
|
27
|
-
*
|
|
28
|
-
* The card is the row's title, `what`, `why`, `how`, `controls` (when present), JOYSTICK_EXPLAINER
|
|
29
|
-
* on a 3D scene (the pads themselves are CameraControls'), and SHOWCASE_INVITE last, by reference.
|
|
30
|
-
* It never renders registry prose. The bag's { seenKey, title, paragraphs } is what
|
|
31
|
-
* lib/components/run-once-card renders; SHOWCASE_INTRO is such a bag too (its own seen-key).
|
|
32
|
-
*
|
|
33
|
-
* Every word here is validated at load through lib/shim/registry/copy-gate.js (placeholder rules and
|
|
34
|
-
* segment shape). A table paragraph is allowed only in SHOWCASE_INTRO. The invitation and the
|
|
35
|
-
* intro's paragraphs appear ONCE: a row that pastes either is refused, as is an intro that pastes
|
|
36
|
-
* the through-line.
|
|
37
|
-
*
|
|
38
|
-
* Consumers: the GYM (lib/features/ide/CompareView.jsx — the intro card, the scene card and the scene
|
|
39
|
-
* select's leading rows); the roster tests (lib/shim/registry/test/exhibits.test.mjs,
|
|
40
|
-
* lib/frqtl/scenes/test/scene-files.test.mjs) and the GYM specs under apps/ide/test that walk it;
|
|
41
|
-
* the front-page generator and gates, which read this module as SERVED by a built bundle
|
|
42
|
-
* (shim/registry/exhibits.js).
|
|
43
|
-
*
|
|
44
|
-
* WHAT IS NOT HERE: the IDE's first-contact default — a walkthrough rung declared once at
|
|
45
|
-
* lib/shim/registry/walkthrough.js `FIRST_CONTACT_WALKTHROUGH_ID`.
|
|
46
|
-
*
|
|
47
|
-
* @module exhibits
|
|
48
|
-
*/
|
|
49
|
-
|
|
50
|
-
import {
|
|
51
|
-
WALKTHROUGH, walkthroughById, walkthroughModes, defaultWalkthroughModeId, walkthroughIs3D, effectiveRenderState
|
|
52
|
-
} from './walkthrough.js';
|
|
53
|
-
import { assertCopyText, assertCopyParagraphs } from './copy-gate.js';
|
|
54
|
-
|
|
55
|
-
/** The scene kinds a roster row (and a player deep link) can name. */
|
|
56
|
-
export const EXHIBIT_KINDS = Object.freeze(['rung']);
|
|
57
|
-
|
|
58
|
-
/** The copy parts a run-once CARD shows, in order; every roster row carries all of them. */
|
|
59
|
-
export const CARD_PARTS = Object.freeze(['what', 'why', 'how']);
|
|
60
|
-
|
|
61
|
-
const segText = (para) => para.map((s) => (typeof s === 'string' ? s : s.text)).join('');
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* A plain showcase paragraph: one segment array of STRINGS (no table, no link), through the copy gate.
|
|
65
|
-
* @returns the paragraph
|
|
66
|
-
*/
|
|
67
|
-
export function assertPlainParagraph(para, where) {
|
|
68
|
-
assertCopyParagraphs([para], where, { table: false });
|
|
69
|
-
if (!para.every((s) => typeof s === 'string')) {
|
|
70
|
-
throw new Error(where + ' is plain text: strings only (the page has no link renderer).');
|
|
71
|
-
}
|
|
72
|
-
return para;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** The one invitation, shown as the LAST paragraph of every scene card (by reference, never pasted). */
|
|
76
|
-
export const SHOWCASE_INVITE = Object.freeze([
|
|
77
|
-
"Every scene here is the same few objects at whatever scale you declare: open it, read its code, change anything, and decide for yourself.",
|
|
78
|
-
]);
|
|
79
|
-
assertPlainParagraph(SHOWCASE_INVITE, '[exhibits] SHOWCASE_INVITE');
|
|
80
|
-
|
|
81
|
-
/** The through-line under the front page's grid: one paragraph of plain text. */
|
|
82
|
-
export const SHOWCASE_THROUGH_LINE = Object.freeze([
|
|
83
|
-
"FRQTL is fractal, as the name sounds. Everything in it is built from three primitives, ticks, pixels and occupancy, and none of them carries a unit. Its layers nest by powers of two, each twice the size of the one below. So the engine does not know or care what scale a scene is: the sun on this page is the same 3D sphere emitter, made of the same primitives, whether you read it as a star, a candle flame or an atom. Every scene is independent cellular automata following local rules, with no equations, and every scene is the same few objects, composed at whatever scale you declare.",
|
|
84
|
-
]);
|
|
85
|
-
assertPlainParagraph(SHOWCASE_THROUGH_LINE, '[exhibits] SHOWCASE_THROUGH_LINE');
|
|
86
|
-
|
|
87
|
-
/** The caption of the intro's estimate table. */
|
|
88
|
-
const SHOWCASE_ESTIMATE_CAPTION = "A back-of-the-envelope estimate.";
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* assertShowcaseIntro — the intro card bag: seen-key, title and paragraphs through the copy gate
|
|
92
|
-
* (a table paragraph allowed; its structure is the run-once card's contract), and never a paste of
|
|
93
|
-
* the through-line. Exported so a test can show each refusal on a planted copy.
|
|
94
|
-
* @returns the intro
|
|
95
|
-
*/
|
|
96
|
-
export function assertShowcaseIntro(intro) {
|
|
97
|
-
if (!intro || typeof intro.seenKey !== 'string' || intro.seenKey === '') {
|
|
98
|
-
throw new Error('[exhibits] SHOWCASE_INTRO needs a non-empty seenKey.');
|
|
99
|
-
}
|
|
100
|
-
assertCopyText(intro.title, '[exhibits] SHOWCASE_INTRO.title');
|
|
101
|
-
assertCopyParagraphs(intro.paragraphs, '[exhibits] SHOWCASE_INTRO', { table: true });
|
|
102
|
-
const through = segText(SHOWCASE_THROUGH_LINE);
|
|
103
|
-
intro.paragraphs.forEach((para, i) => {
|
|
104
|
-
if (Array.isArray(para) && segText(para).indexOf(through) !== -1) {
|
|
105
|
-
throw new Error('[exhibits] SHOWCASE_INTRO paragraph ' + i + ' pastes SHOWCASE_THROUGH_LINE — the through-line has one place.');
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
return intro;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** The public EGPT repository (DeSciX/EGPT, main) the intro's closing paragraph cites, one document per link. */
|
|
112
|
-
const EGPT_EVIDENCE_BLOB = 'https://github.com/DeSciX/EGPT/blob/main/';
|
|
113
|
-
|
|
114
|
-
/** A link segment to one tracked document of the public EGPT repository. */
|
|
115
|
-
const egptDoc = (text, path) => Object.freeze({ text, href: EGPT_EVIDENCE_BLOB + path });
|
|
116
|
-
|
|
117
|
-
/** The run-once intro card, shown once before the first roster scene card a viewer meets. */
|
|
118
|
-
export const SHOWCASE_INTRO = Object.freeze({
|
|
119
|
-
seenKey: 'showcase-intro',
|
|
120
|
-
title: "Why these are considered \"impossible\"",
|
|
121
|
-
paragraphs: Object.freeze([
|
|
122
|
-
Object.freeze([
|
|
123
|
-
"The industry-standard projection is that simulating just 100 electrons requires a classical computer with 2^100 bits, more atoms than there are in the universe (",
|
|
124
|
-
Object.freeze({ text: "Microsoft's quantum computing overview", href: "https://learn.microsoft.com/en-us/azure/quantum/overview-understanding-quantum-computing" }),
|
|
125
|
-
"). Google's framing is that 100 photons (or qubits) would require 725 trillion times the age of the universe to calculate using the world's most powerful supercomputers, like Frontier.",
|
|
126
|
-
]),
|
|
127
|
-
Object.freeze(["You're about to see the FRQTL hyper-computer accurately simulate many thousands on your own mobile phone or laptop."]),
|
|
128
|
-
Object.freeze(["FRQTL is a patent-pending P=NP hyper-computer based on a radically different architecture than current computer chips and hypothetical qubit-based quantum computers."]),
|
|
129
|
-
Object.freeze(["FRQTL doesn't cheat, and it isn't like any \"traditional\" approach such as ray tracing, path tracing and photon mapping, which cannot produce interference or wave diffusion at any cost. They treat light as straight lines. Real-time engines fake shadows, reflections and indirect light as separate effects, with fixed bounce limits."]),
|
|
130
|
-
Object.freeze(["Matching what you will see FRQTL do here means solving Maxwell's equations directly. The standard method is FDTD: a 3D grid of about 20 cells per wavelength, updated every time step. At real optical wavelengths that cost is out of reach."]),
|
|
131
|
-
Object.freeze(["Assumptions: visible light at 500 nm; 20 cells per wavelength; about 24 bytes and 30 operations per cell per step; a time step at the standard stability limit for a 3D grid, so light advances at most 1/√3 of a cell per step and crossing the scene takes about 1.7 steps for every cell across it; a Frontier-class supercomputer (about 10^18 operations per second, about 10 PB of memory). \"One crossing\" means light travelling once across the scene."]),
|
|
132
|
-
Object.freeze({ table: Object.freeze({
|
|
133
|
-
caption: SHOWCASE_ESTIMATE_CAPTION,
|
|
134
|
-
head: Object.freeze(["Scene","Grid cells","Memory needed","Compute for one crossing","Time on Frontier"]),
|
|
135
|
-
rows: Object.freeze([
|
|
136
|
-
Object.freeze(["10 cm tabletop","6 × 10^19","about 1.5 zettabytes, about 150,000× Frontier's memory","about 10^28 operations","about 400 years"]),
|
|
137
|
-
Object.freeze(["5 m room","8 × 10^24","about 2 × 10^26 bytes, more than all the storage on Earth","about 10^35 operations","about 3 billion years"]),
|
|
138
|
-
]),
|
|
139
|
-
}) }),
|
|
140
|
-
Object.freeze(["Running indefinitely multiplies that by every further crossing. At physical wavelengths, \"impossible for supercomputers\" is literally true."]),
|
|
141
|
-
Object.freeze(["The table is about light waves. The blackbody, the logic circuit, the Big Bang and the atom are impossible the usual way for a different reason: each needs a theory, equations and a program of its own, at a scale of its own. The glowing box needs energy quanta assumed, the circuit needs a search through its inputs, the cosmos needs gravity computed over 13.8 billion years, and the atom needs quantised energy levels."]),
|
|
142
|
-
Object.freeze([
|
|
143
|
-
"The EGPT proofs are machine-checked in Lean. Rota's Entropy Theorem (RET), the Lean theorem ",
|
|
144
|
-
egptDoc("rota_all_entropy_scaled_shannon", "08_LEAN/EGPT/InformationTheory/EntropyNumber/RotaEntropy.lean"),
|
|
145
|
-
", is why P=NP is quantum computing: by Rota's proof, all the distributions of physics are scaled Shannon entropy (for the Bose-Einstein, Fermi-Dirac and Maxwell-Boltzmann distributions, the Lean theorems ",
|
|
146
|
-
egptDoc("entropy_BE_eq_C_shannon, entropy_FD_eq_C_shannon and entropy_MB_eq_C_shannon", "08_LEAN/EGPT/InformationTheory/Physics/PhysicsDist.lean"),
|
|
147
|
-
"), and all entropy, including von Neumann entropy, is scaled Shannon entropy. RET provides the framework for all three P=NP proofs, the Lean theorems ",
|
|
148
|
-
egptDoc("P_eq_NP", "08_LEAN/EGPT/InformationTheory/Complexity/SetRFL.lean"),
|
|
149
|
-
", ",
|
|
150
|
-
egptDoc("P_eq_NP_info", "08_LEAN/EGPT/InformationTheory/Complexity/PPNP.lean"),
|
|
151
|
-
" and ",
|
|
152
|
-
egptDoc("P_eq_NP_info_standard", "08_LEAN/EGPT/InformationTheory/Complexity/StandardComplexity.lean"),
|
|
153
|
-
". Together, RET and the distribution theorems are a formalisation of a Theory of Everything, as discussed under Question 1 of ",
|
|
154
|
-
egptDoc("the Claude Singularity session", "12_SINGULARITY/Claude/Cowork_Session_10.01.2026.md"),
|
|
155
|
-
". The proof of P=NP was first outlined in the paper P Probably Equals NP (January 2025).",
|
|
156
|
-
]),
|
|
157
|
-
]),
|
|
158
|
-
});
|
|
159
|
-
assertShowcaseIntro(SHOWCASE_INTRO);
|
|
160
|
-
|
|
161
|
-
// The authored roster, front-page order. `title` and `tile` are the published front-page tile copy
|
|
162
|
-
// (the egpt-frqtl front page's #proof tiles: each tile's <b> and its sentence), verbatim;
|
|
163
|
-
// `what`, `why`, `how`, `controls` and each `still.alt` are the authored captions.
|
|
164
|
-
const ROSTER = [
|
|
165
|
-
{
|
|
166
|
-
kind: 'rung', id: "cl1d-mirror-route", mode: "3d",
|
|
167
|
-
still: { tick: 3600, alt: "Ten brightly coloured mirror blocks in a ring, seen face-on, one cyan block on the right standing inside the ring out of its place; a thick, glowing band of gold, red, pink and green light swirls around a dark centre holding faint purple and green eddies, and green and pink rays reach out between the mirrors into the dark." },
|
|
168
|
-
title: "Infinite mirror",
|
|
169
|
-
tile: "Ten mirrors in a ring. Light walks the loop. No Fresnel equations.",
|
|
170
|
-
what: "Ten mirrors in a ring. Light goes round and round the loop, reflected and diffused from mirror to mirror, each mirror colouring the light it sends on.",
|
|
171
|
-
why: "Conventional renderers cut every light path off after a set number of bounces and treat shadows, reflections and indirect light as separate effects. Solving the light directly in a 5 m room would take Frontier about 3 billion years.",
|
|
172
|
-
how: "One emitter and ten walls set as mirrors. Light leaves in every direction; shadowing, reflection and obstruction are all live, with no limit on the objects in its path, and the diffusion is real. No Fresnel equations.",
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
kind: 'rung', id: "dse-tunnel-rebuild", mode: "3d",
|
|
176
|
-
still: { tick: 7200, alt: "Three red blocks with blue slit walls between them, seen at a tilt from behind the source; a fan of yellow rays spreads from a point at lower left into the slits, and beyond them, at upper right, a heat-map plate of blue and green squares shows light and dark bands with a red hot spot at its centre." },
|
|
177
|
-
title: "Double slit",
|
|
178
|
-
tile: "Two slits, one particle at a time. Feynman's “only mystery,” with no amplitudes to add.",
|
|
179
|
-
what: "Particles go through two slits one at a time, and bright and dark bands build up on the detector plate below them, live in your browser tab: the pattern Feynman called quantum mechanics' \"only mystery\". EGPT conceptually holds computer science and physics are linked by P=NP and the principle of least action - that is, everything in reality is efficiently computable. The Lean RET, the derivation of EntropyReal from an IIDParticleSource, and the P=NP capstones are modern formalizations of proofs and ideas advanced by Gian-Carlo Rota circa the late 1970s. The EGPT Lean proofs transparently show the most rigorously advanced proofs ever offered for bridging not only quantum and classical mechanics but mathematics and all the other sciences. Watch it run, read its code, and judge for yourself whether this is true P=NP quantum computing.",
|
|
180
|
-
why: "Feynman called it \"impossible, absolutely impossible, to explain in any classical way\". A ray tracer cannot draw it at all, and solving the light waves directly for a 10 cm tabletop would need about 150,000 times the memory of the Frontier supercomputer and about 400 years of its time.",
|
|
181
|
-
how: "A sphere emitter, walls, two tunnels for the slits and a detector plate. Each particle is an independent cellular automaton following local rules: no wave equation, no amplitudes to add.",
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
kind: 'rung', id: "r8-sun",
|
|
185
|
-
still: { tick: 300, alt: "A white-hot sun with a golden halo and fine rays fanning out in every direction across a dim, grainy space." },
|
|
186
|
-
title: "A sun",
|
|
187
|
-
tile: "Put light in the scene and let light do what light does. No Standard Model; god-rays from particle density.",
|
|
188
|
-
what: "Put light in the scene and let light do what light does: a blazing sun, its glow and god rays spreading in every direction through the space around it.",
|
|
189
|
-
why: "Games paint glow and god rays onto the finished picture as a separate screen effect, aimed from where the sun is on the screen rather than from any light in the scene.",
|
|
190
|
-
how: "One sphere emitter, shining in every direction. The god rays are rendered from the density of the particles themselves. No Standard Model, no equations, and the engine does not know whether you are looking at a star, a candle flame or an atom.",
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
kind: 'rung', id: "r5-fountain-pool",
|
|
194
|
-
still: { tick: 300, alt: "A dark teal surface scattered with white glints like light on water, with a faint ring around a dark dot at its centre." },
|
|
195
|
-
title: "A pool",
|
|
196
|
-
tile: "One source. Rings. Interference as the geometry of random walks, not a wave.",
|
|
197
|
-
what: "One source, and rings of ripples spread out across the water, glinting like light on a pool.",
|
|
198
|
-
why: "A ray is a straight line with no wave in it, so ray tracing cannot make interference rings at any cost, and solving the waves directly runs into the costs in the intro.",
|
|
199
|
-
how: "One sphere emitter - classical mechanics that looks like water, from the same light object, the sphere emitter that made the sun in the previous scene. Every particle zig-zags as it travels, so its path is itself a wave; the rings are the geometry of those wavy random walks crossing. The water is the same light field shown through a different renderer. No equations.",
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
kind: 'rung', id: "blackbody",
|
|
203
|
-
still: { tick: 600, alt: "A grey box packed with glowing red blobs and tiny yellow dots, with dots and a few red blobs streaming out of an opening on its right side." },
|
|
204
|
-
title: "Blackbody",
|
|
205
|
-
tile: "Particles in a box. The Planck curve appears; no quanta assumed.",
|
|
206
|
-
what: "A hot box of jostling particles glows red, and light spills out through a slit in its side. The Planck curve appears; no energy quanta are assumed.",
|
|
207
|
-
why: "Classical physics, the Rayleigh–Jeans law, predicts infinite energy at high frequencies, the \"ultraviolet catastrophe\". There is no settled traditional way to compute the plasma physics behind Planck's 1900 law from first principles.",
|
|
208
|
-
how: "A box, a thermostat that keeps it hot, and an absorbing wall that catches whatever escapes. Colliding particles merge into larger frames, and how full a frame is sets its colour. Independent cellular automata, no equations.",
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
kind: 'rung', id: "circuit-sat",
|
|
212
|
-
still: { tick: 300, alt: "A circuit of blue bars on black; two lit input lines on the left feed two gates, the top output line carries a long stream of orange beads to the right, and the bottom output holds only a short, stalled cluster." },
|
|
213
|
-
title: "Circuit SAT",
|
|
214
|
-
tile: "A half-adder settles on its own answer. No search algorithm runs.",
|
|
215
|
-
what: "A half-adder settles on its own answer, you are watching electricity simulated in a circuit component live in your browser tab. Quanta (electrons or photons if you like) flow down the wires into two gates; with input A on, the SUM line fills with flowing quanta and nothing flows along CARRY. No search algorithm runs.",
|
|
216
|
-
why: "Circuit satisfiability, the canonical P vs NP problem of computer science, is also a physical question about real circuits. Every modern circuit is built out of half-adders - put some constant number C of half-adders together and if simulating one is polynomial then C × polynomial is still polynomial. No polynomial-time algorithm is supposed to exist, so the usual way to find an input that satisfies a circuit is to search for one. The EGPT proofs, machine-checked in Lean, show that P=NP.",
|
|
217
|
-
how: "Two logic gates, the wires between them and an emitter on each input - the half-adder electrical circuit simulation settles in your browser in polynomial time, effectively at once. A gate opens only when its inputs carry the pattern it stands for, so the circuit settles into its answer. Independent cellular automata, no equations. EGPT conceptually holds computer science and physics are linked by P=NP and the principle of least action - that is, everything in reality is efficiently computable. The Lean RET, the derivation of EntropyReal from an IIDParticleSource, and the P=NP capstones are modern formalizations of proofs and ideas advanced by Gian-Carlo Rota circa the late 1970s. The EGPT Lean proofs transparently show the most rigorously advanced proofs ever offered for bridging not only quantum and classical mechanics but mathematics and all the other sciences. Watch the answer emerge with no search, read the code, and judge for yourself whether this is true P=NP quantum computing.",
|
|
218
|
-
controls: "It starts with input A on. Click the input A and input B toggles (bottom left) to switch each input on or off. Read the two outputs on the right: the top one is SUM (A xor B), the bottom one is CARRY (A and B). An output with quanta flowing along it reads 1; an empty one reads 0.",
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
kind: 'rung', id: "big-bang",
|
|
222
|
-
still: { tick: 200, alt: "A ragged red cloud of overlapping glowing discs laced with fine gold specks, clumped and broken at its edges, on black." },
|
|
223
|
-
title: "Big Bang",
|
|
224
|
-
tile: "A uniform field. Filaments and voids form. No gravity law, no constant.",
|
|
225
|
-
what: "One burst fills the space, and structure emerges on its own. This is a fractal at its heart - it could be quantum emergence or large cosmic structures ... one set of rules from which attractive forces are a matter of your chosen unit of scale. This simulation is not meant as an assertion of what the correct scales are; it is an exploration of how emergence and scale are related.",
|
|
226
|
-
why: "Large-scale cosmic structure requires gravitational attraction tuned over 13.8 billion years, and a simulation has to compute that gravity across the whole span.",
|
|
227
|
-
how: "One point source, and a world that builds larger frames from the top down. No gravity law, no cosmological constant, no equations.",
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
kind: 'rung', id: "atomic-model",
|
|
231
|
-
still: { tick: 600, alt: "A large dark-red disc with a brighter red core; at the core's left edge a small violet ball sits in a tight cluster of pale beads, and one lone pale bead lies further out at lower right." },
|
|
232
|
-
title: "Atomic model",
|
|
233
|
-
tile: "Particles bind and stay bound. Stable orbits without quantum numbers.",
|
|
234
|
-
what: "Quarks pack into a proton and stay bound, and an electron keeps its place in the atom around it. Stable orbits, without quantum numbers. Like the Big Bang, this simulation is not meant as an assertion of what the correct scales are; it is an exploration of how emergence and scale are related - the engine lets you set stable containment, or experiment with scale so that stability is emergent.",
|
|
235
|
-
why: "Classical physics says an orbiting electron radiates its energy away and spirals into the nucleus in about 16 trillionths of a second. Textbook physics avoids that only by assuming quantised energy levels.",
|
|
236
|
-
how: "Frames nested three layers deep: quarks in a proton, the proton and an electron in an atom. Each parent draws its children one step toward its centre every tick, and binding emerges from that. No force equation.",
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
kind: 'rung', id: "collision-matrix-3d",
|
|
240
|
-
still: { tick: 200, alt: "Pairs of shiny balls, red, orange, yellow, green and violet, meeting in mid-air against black.", pose: { azimuth: 45, elevation: -30 } },
|
|
241
|
-
title: "3D Collisions Lab",
|
|
242
|
-
tile: "Balls in a box. Contact, momentum, settling. The same engine that makes light makes mechanics; no second physics.",
|
|
243
|
-
what: "Balls in a box, in seven lanes coloured red to violet by density. Each pair flies together and collides: the hard ones bounce cleanly, the soft ones press into each other before parting. Contact, momentum, settling. This is a \"Collisions Lab\" because it is up to you to find the capacity-and-fill relationships that produce the desired elastic vs inelastic collisions at your desired scale.",
|
|
244
|
-
why: "In a game engine, collisions come from a separate physics engine, such as NVIDIA PhysX, solving rigid-body equations beside the renderer that draws the light. Two systems, two sets of rules.",
|
|
245
|
-
how: "The same engine that makes the light in the other scenes makes these collisions: pairs of the same frames at seven densities. No second physics, no equations.",
|
|
246
|
-
},
|
|
247
|
-
];
|
|
248
|
-
|
|
249
|
-
const ROW_KEYS = ['kind', 'id', 'mode', 'title', 'tile', 'what', 'why', 'how', 'controls', 'still'];
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* A tile still: absent, or `{ tick: <positive integer>, alt: <text>, pose?: <orbit patch> }` (THROWS
|
|
253
|
-
* otherwise). `alt` is the still's alt text, required whenever a still is declared. `pose` is a
|
|
254
|
-
* non-empty object of finite numbers handed VERBATIM to the scene's camera handle
|
|
255
|
-
* (`runner.cameraControl.setOrbit`), whose provider owns the orbit keys; the instrument asserts the
|
|
256
|
-
* camera's read-back agrees with every key it names.
|
|
257
|
-
*/
|
|
258
|
-
function stillOf(v, where) {
|
|
259
|
-
if (v === undefined || v === null) { return null; }
|
|
260
|
-
const keys = (v && typeof v === 'object') ? Object.keys(v) : [];
|
|
261
|
-
const shape = '{ tick: <positive integer>, alt: <text>, pose?: <non-empty setOrbit patch> } or absent';
|
|
262
|
-
const unknown = keys.filter((k) => k !== 'tick' && k !== 'alt' && k !== 'pose');
|
|
263
|
-
if (unknown.length || !Number.isInteger(v.tick) || v.tick < 1) {
|
|
264
|
-
throw new Error('[exhibits] ' + where + ' must be ' + shape + ' (got ' + JSON.stringify(v) + ').');
|
|
265
|
-
}
|
|
266
|
-
const alt = assertCopyText(v.alt, '[exhibits] ' + where + '.alt');
|
|
267
|
-
if (v.pose === undefined) { return Object.freeze({ tick: v.tick, alt }); }
|
|
268
|
-
const pose = v.pose;
|
|
269
|
-
if (!pose || typeof pose !== 'object' || Array.isArray(pose) || Object.keys(pose).length === 0) {
|
|
270
|
-
throw new Error('[exhibits] ' + where + '.pose must be a non-empty setOrbit patch (got ' + JSON.stringify(pose) + ').');
|
|
271
|
-
}
|
|
272
|
-
Object.keys(pose).forEach((k) => {
|
|
273
|
-
if (typeof pose[k] !== 'number' || !Number.isFinite(pose[k])) {
|
|
274
|
-
throw new Error('[exhibits] ' + where + '.pose.' + k + ' must be a finite number (got ' + String(pose[k]) + ').');
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
return Object.freeze({ tick: v.tick, alt, pose: Object.freeze(Object.assign({}, pose)) });
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function rungOrThrow(id) {
|
|
281
|
-
const rung = walkthroughById(id);
|
|
282
|
-
if (!rung) {
|
|
283
|
-
throw new Error('[exhibits] unknown walk-through rung "' + id + '" — declared rungs: [' +
|
|
284
|
-
WALKTHROUGH.map((r) => r.id).join(', ') + '].');
|
|
285
|
-
}
|
|
286
|
-
return rung;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/** A rung's effective mode: the declared one (validated), or null for a single-mode rung. */
|
|
290
|
-
function rungMode(rung, modeId) {
|
|
291
|
-
const eff = (modeId !== undefined && modeId !== null) ? modeId : defaultWalkthroughModeId(rung);
|
|
292
|
-
effectiveRenderState(rung, eff); // FAIL-LOUD on an undeclared mode (its contract)
|
|
293
|
-
return walkthroughModes(rung).length > 1 ? eff : null;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/** Copy text through the copy gate: `required` refuses an absent value; an optional one is null when absent. */
|
|
297
|
-
function text(v, where, required) {
|
|
298
|
-
if ((v === undefined || v === null) && !required) { return null; }
|
|
299
|
-
return assertCopyText(v, '[exhibits] ' + where);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/** Refuse a row part that pastes the invitation or a whole intro paragraph (each has one place). */
|
|
303
|
-
function assertNotPasted(part, where) {
|
|
304
|
-
if (part.indexOf(segText(SHOWCASE_INVITE)) !== -1) {
|
|
305
|
-
throw new Error('[exhibits] ' + where + ' pastes SHOWCASE_INVITE — the card appends it by reference.');
|
|
306
|
-
}
|
|
307
|
-
SHOWCASE_INTRO.paragraphs.forEach((para, i) => {
|
|
308
|
-
if (Array.isArray(para) && part.indexOf(segText(para)) !== -1) {
|
|
309
|
-
throw new Error('[exhibits] ' + where + ' pastes SHOWCASE_INTRO paragraph ' + i + ' — the intro\'s text has one place.');
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* assertRoster — the load-time validator: every row is a rung whose id resolves in the walk-through
|
|
316
|
-
* registry, its mode is declared, no scene repeats, no unknown field, every
|
|
317
|
-
* row has a title, a tile line and every CARD_PARTS part, `controls` is non-empty when present, all
|
|
318
|
-
* copy passes the copy gate, and no part pastes the invitation or an intro paragraph. Exported so a
|
|
319
|
-
* test can show each refusal on a planted roster. @returns the normalized, frozen rows (with `order`).
|
|
320
|
-
*/
|
|
321
|
-
export function assertRoster(rows) {
|
|
322
|
-
const seen = new Set();
|
|
323
|
-
return Object.freeze(rows.map((row, order) => {
|
|
324
|
-
Object.keys(row).forEach((k) => {
|
|
325
|
-
if (ROW_KEYS.indexOf(k) === -1) {
|
|
326
|
-
throw new Error('[exhibits] row ' + order + ' has unknown field "' + k + '" — fields: ' + ROW_KEYS.join(', ') + '.');
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
if (EXHIBIT_KINDS.indexOf(row.kind) === -1) {
|
|
330
|
-
throw new Error('[exhibits] row ' + order + ' kind ' + JSON.stringify(row.kind) + ' — an exhibit is a walk-through rung: ' +
|
|
331
|
-
'{ kind: \'rung\', id: <a lib/shim/registry/walkthrough.js id>, mode? } (EXHIBIT_KINDS: ' + EXHIBIT_KINDS.join(', ') + ').');
|
|
332
|
-
}
|
|
333
|
-
const mode = rungMode(rungOrThrow(row.id), row.mode);
|
|
334
|
-
if (row.title === undefined || row.title === null) {
|
|
335
|
-
throw new Error('[exhibits] row "' + row.id + '" has no title — every card is titled.');
|
|
336
|
-
}
|
|
337
|
-
const title = text(row.title, row.id + '.title', true);
|
|
338
|
-
const key = row.kind + ':' + row.id + ':' + mode;
|
|
339
|
-
if (seen.has(key)) { throw new Error('[exhibits] row ' + key + ' appears twice.'); }
|
|
340
|
-
seen.add(key);
|
|
341
|
-
const out = { kind: row.kind, id: row.id, mode, order, title };
|
|
342
|
-
CARD_PARTS.forEach((p) => { out[p] = text(row[p], row.id + '.' + p, true); });
|
|
343
|
-
out.controls = text(row.controls, row.id + '.controls', false);
|
|
344
|
-
out.tile = text(row.tile, row.id + '.tile', true);
|
|
345
|
-
CARD_PARTS.concat(['controls']).forEach((p) => { if (out[p] !== null) { assertNotPasted(out[p], row.id + '.' + p); } });
|
|
346
|
-
out.still = stillOf(row.still, row.id + '.still');
|
|
347
|
-
return Object.freeze(out);
|
|
348
|
-
}));
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/** The showcase roster, validated and normalized at load. */
|
|
352
|
-
export const EXHIBITS = assertRoster(ROSTER);
|
|
353
|
-
|
|
354
|
-
/** The roster rows of one kind, in roster order (throws on an unknown kind). */
|
|
355
|
-
export function exhibitsOfKind(kind) {
|
|
356
|
-
if (EXHIBIT_KINDS.indexOf(kind) === -1) {
|
|
357
|
-
throw new Error('[exhibits] exhibitsOfKind: unknown kind ' + JSON.stringify(kind) + ' — EXHIBIT_KINDS: ' + EXHIBIT_KINDS.join(', ') + '.');
|
|
358
|
-
}
|
|
359
|
-
return EXHIBITS.filter((e) => e.kind === kind);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* The roster row for a scene, or null when the scene is not on the roster. A null is a DECLARED
|
|
364
|
-
* outcome the caller records (the player plays such a scene with no card: 'not-offered-no-roster-row').
|
|
365
|
-
* The scene itself must exist: an unknown kind, id or mode throws.
|
|
366
|
-
* @param {{kind: 'rung', id: string, modeId?: string|null}} scene
|
|
367
|
-
*/
|
|
368
|
-
export function exhibitFor(scene) {
|
|
369
|
-
const kind = scene && scene.kind;
|
|
370
|
-
if (EXHIBIT_KINDS.indexOf(kind) === -1) {
|
|
371
|
-
throw new Error('[exhibits] unknown scene kind ' + JSON.stringify(kind) + ' — EXHIBIT_KINDS: ' + EXHIBIT_KINDS.join(', ') + '.');
|
|
372
|
-
}
|
|
373
|
-
const mode = rungMode(rungOrThrow(scene.id), scene.modeId);
|
|
374
|
-
for (let i = 0; i < EXHIBITS.length; i++) {
|
|
375
|
-
const e = EXHIBITS[i];
|
|
376
|
-
if (e.kind === kind && e.id === scene.id && e.mode === mode) { return e; }
|
|
377
|
-
}
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* How the MOVE/LOOK pads work, shown on every 3D roster scene's card. The claims are
|
|
383
|
-
* CameraControls.js's contract and are exercised in apps/ide/test/gym-scene-player.spec.js
|
|
384
|
-
* (MOVE pad and W/A/S/D translate, LOOK pad and the arrow keys rotate; mouse and touch both drive
|
|
385
|
-
* the pads).
|
|
386
|
-
*/
|
|
387
|
-
export const JOYSTICK_EXPLAINER = Object.freeze([
|
|
388
|
-
Object.freeze([
|
|
389
|
-
'Move and Look: this scene is 3D. Drag the MOVE pad (bottom left) to walk the camera forward, back ' +
|
|
390
|
-
'and sideways, and the LOOK pad (bottom right) to turn it. Both pads take a finger on a phone or ' +
|
|
391
|
-
'the mouse on a desktop; on a keyboard, W/A/S/D move and the arrow keys look.',
|
|
392
|
-
]),
|
|
393
|
-
]);
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* sceneKey — the run-once seen-key of a scene: kind, id and, for a multi-mode rung, its EFFECTIVE
|
|
397
|
-
* mode (the one normalizer, rungMode); never a bare id, so it cannot collide with a tab key.
|
|
398
|
-
* Throws for an unknown kind, id or mode.
|
|
399
|
-
* @param {{kind: 'rung', id: string, modeId?: string|null}} scene
|
|
400
|
-
*/
|
|
401
|
-
export function sceneKey(scene) {
|
|
402
|
-
const kind = scene && scene.kind;
|
|
403
|
-
if (EXHIBIT_KINDS.indexOf(kind) === -1) {
|
|
404
|
-
throw new Error('[exhibits] unknown scene kind ' + JSON.stringify(kind) + ' — EXHIBIT_KINDS: ' + EXHIBIT_KINDS.join(', ') + '.');
|
|
405
|
-
}
|
|
406
|
-
const mode = rungMode(rungOrThrow(scene.id), scene.modeId);
|
|
407
|
-
return 'scene-' + kind + '-' + scene.id + (mode ? '-' + mode : '');
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* The run-once card of a ROSTER row: title, then `what`, `why`, `how`, `controls` when present, the
|
|
412
|
-
* JOYSTICK_EXPLAINER on a 3D scene (3D read from walkthroughIs3D), and
|
|
413
|
-
* SHOWCASE_INVITE last, by reference. Composed from the row and the showcase constants only: no
|
|
414
|
-
* registry prose ever. The bag's { seenKey, title, paragraphs } is what lib/components/run-once-card
|
|
415
|
-
* renders.
|
|
416
|
-
* @param {object} row an element of EXHIBITS (exhibitFor's answer)
|
|
417
|
-
* @throws {Error} a row that is not on the roster.
|
|
418
|
-
*/
|
|
419
|
-
export function showcaseCard(row) {
|
|
420
|
-
if (EXHIBITS.indexOf(row) === -1) {
|
|
421
|
-
throw new Error('[exhibits] showcaseCard takes a roster row (an EXHIBITS element, e.g. exhibitFor(scene)); got ' +
|
|
422
|
-
JSON.stringify(row && { kind: row.kind, id: row.id }) + '.');
|
|
423
|
-
}
|
|
424
|
-
const is3D = walkthroughIs3D(row.id, row.mode);
|
|
425
|
-
let paragraphs = CARD_PARTS.map((p) => Object.freeze([row[p]]));
|
|
426
|
-
if (row.controls !== null) { paragraphs = paragraphs.concat([Object.freeze([row.controls])]); }
|
|
427
|
-
if (is3D) { paragraphs = paragraphs.concat(JOYSTICK_EXPLAINER); }
|
|
428
|
-
paragraphs = paragraphs.concat([SHOWCASE_INVITE]);
|
|
429
|
-
return Object.freeze({
|
|
430
|
-
kind: row.kind, id: row.id, modeId: row.mode,
|
|
431
|
-
seenKey: sceneKey({ kind: row.kind, id: row.id, modeId: row.mode }),
|
|
432
|
-
title: row.title,
|
|
433
|
-
paragraphs: Object.freeze(paragraphs),
|
|
434
|
-
is3D,
|
|
435
|
-
});
|
|
436
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* scene-roster.js — THE scene list both views show (the GYM's select and STUDIO's dropdown): one owner, read by
|
|
5
|
-
* both, so the two lists are the same rows in the same order.
|
|
6
|
-
*
|
|
7
|
-
* SHOWCASE_GROUP the label of the showcase group ('Impossible things')
|
|
8
|
-
* sceneGroups() → [{ label, rows: [{ id, title }] }] — the showcase roster first (EXHIBITS, in tile order,
|
|
9
|
-
* each row its exhibit title), then every walk-through rung in WALKTHROUGH order under its
|
|
10
|
-
* lab group (walkthroughGroup), with NO rung removed from its own group: a showcase rung
|
|
11
|
-
* appears twice, and both rows name the same rung (a deep link selects it either way).
|
|
12
|
-
*
|
|
13
|
-
* A row is a scene id a host opens by ?walkthrough=<id>. A host keeps its own non-scene rows (STUDIO's blank world
|
|
14
|
-
* and saved projects) around these groups; the GYM's default scene is the first row.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { EXHIBITS } from './exhibits.js';
|
|
18
|
-
import { WALKTHROUGH, walkthroughGroup } from './walkthrough.js';
|
|
19
|
-
|
|
20
|
-
/** The showcase group's label. */
|
|
21
|
-
export const SHOWCASE_GROUP = 'Impossible things';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @returns {Array<{label: string, rows: Array<{id: string, title: string}>}>} a new array on every call
|
|
25
|
-
*/
|
|
26
|
-
export function sceneGroups() {
|
|
27
|
-
var groups = [{ label: SHOWCASE_GROUP, rows: EXHIBITS.map(function (e) { return { id: e.id, title: e.title }; }) }];
|
|
28
|
-
WALKTHROUGH.forEach(function (r) {
|
|
29
|
-
var g = walkthroughGroup(r);
|
|
30
|
-
var last = groups[groups.length - 1];
|
|
31
|
-
if (groups.length === 1 || last.label !== g) { groups.push({ label: g, rows: [] }); last = groups[groups.length - 1]; }
|
|
32
|
-
last.rows.push({ id: r.id, title: r.title });
|
|
33
|
-
});
|
|
34
|
-
return groups;
|
|
35
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* showcase-link.js — the ONE writer of a showcase tile's link: where a roster row opens on the site that serves
|
|
5
|
-
* the IDE. Every front page (the publisher sites, Gods World) composes its tile hrefs from these two names and types
|
|
6
|
-
* no query, page or origin of its own.
|
|
7
|
-
*
|
|
8
|
-
* showcaseHref(row) → 'app.html?preset=author&walkthrough=<id>[&mode=<mode>]', relative to the serving site's root:
|
|
9
|
-
* the IDE entry page (site-graph.js::STATIC_APP_PAGE) opened on STUDIO at the row's rung. `mode`
|
|
10
|
-
* is the row's EFFECTIVE mode (exhibits.js normalizes it: on a multi-mode rung the mode the row
|
|
11
|
-
* names, else the rung's default; null on a single-mode rung, which carries no `mode`), so the
|
|
12
|
-
* link opens the mode the tile's still shows.
|
|
13
|
-
* showcaseUrl(row) → the same link, absolute on SHOWCASE_SITE.origin: what a front page on ANY other origin uses.
|
|
14
|
-
* Whether a page links relative (it is the serving site) or absolute is the page's own fact.
|
|
15
|
-
* SHOWCASE_SITE → { app, origin } — the DeSciX app that serves the IDE and its public origin.
|
|
16
|
-
*
|
|
17
|
-
* `row` must be an element of the EXHIBITS array imported from the SAME module instance (the ./registry export):
|
|
18
|
-
* a copied, JSON-deserialized or second-copy row is refused, loudly.
|
|
19
|
-
*
|
|
20
|
-
* It writes no IDE state: the query is a link, read by the one parser (lib/features/ide/deep-link.js::parseDeepLink).
|
|
21
|
-
* The shim cannot import that parser (shim → features is illegal), so the binding is a round-trip conformance test
|
|
22
|
-
* in lib/features/ide/test/showcase-link-round-trip.test.mjs: for every EXHIBITS row the parsed link shows STUDIO,
|
|
23
|
-
* names the row's rung and mode, and is refused by nothing.
|
|
24
|
-
*
|
|
25
|
-
* Host-neutral ESM: it runs unchanged in node and in a page (the `./registry` export).
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import { STATIC_APP_PAGE } from '../engine/site-graph.js';
|
|
29
|
-
import { EXHIBITS } from './exhibits.js';
|
|
30
|
-
|
|
31
|
-
/** The IDE surface a showcase tile opens: STUDIO (presets.js PRESETS.author; the round-trip test binds the two). */
|
|
32
|
-
const SHOWCASE_SURFACE = 'author';
|
|
33
|
-
|
|
34
|
-
/** The DeSciX app serving the IDE every showcase tile opens, and its public origin (no trailing slash). */
|
|
35
|
-
export const SHOWCASE_SITE = Object.freeze({ app: 'egpt-frqtl', origin: 'https://egpt-frqtl.descix.net' });
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @param {object} row an element of EXHIBITS (exhibits.js)
|
|
39
|
-
* @returns {string} the tile's link, relative to the serving site's root
|
|
40
|
-
* @throws {Error} a row that is not on the roster
|
|
41
|
-
*/
|
|
42
|
-
export function showcaseHref(row) {
|
|
43
|
-
if (EXHIBITS.indexOf(row) === -1) {
|
|
44
|
-
throw new Error('[showcase-link] showcaseHref(row): not a showcase roster row — pass an element of EXHIBITS ' +
|
|
45
|
-
'(lib/shim/registry/exhibits.js), got ' + JSON.stringify(row && { kind: row.kind, id: row.id }) + '. (FAIL-LOUD.)');
|
|
46
|
-
}
|
|
47
|
-
const params = new URLSearchParams();
|
|
48
|
-
params.set('preset', SHOWCASE_SURFACE);
|
|
49
|
-
params.set('walkthrough', row.id);
|
|
50
|
-
if (row.mode !== null) { params.set('mode', row.mode); }
|
|
51
|
-
return STATIC_APP_PAGE + '?' + params.toString();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @param {object} row an element of EXHIBITS (exhibits.js), from the same module instance
|
|
56
|
-
* @returns {string} the tile's link, absolute on SHOWCASE_SITE.origin
|
|
57
|
-
* @throws {Error} a row that is not on the roster
|
|
58
|
-
*/
|
|
59
|
-
export function showcaseUrl(row) {
|
|
60
|
-
return new URL(showcaseHref(row), SHOWCASE_SITE.origin + '/').href;
|
|
61
|
-
}
|