Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@
const j = await r.json().catch(() => null);
if (!r.ok) { status.textContent = (j && j.detail) || `accept failed (${r.status})`; status.className = 'small agent-line-no'; btn.disabled = false; return; }
host.innerHTML = '';
status.textContent = `Accepted — saved to data/protocols/${j.protocol_id || pid}.json. An accepted protocol fills the sandbox form step by step, each step still confirmed.`;
status.textContent = `Accepted — saved to data/protocols/${j.protocol_id || pid}.json. An accepted protocol fills the Explore form step by step, each step still confirmed.`;
status.className = 'small agent-line-ok';
} catch (e) {
status.textContent = 'the accept request failed: ' + (e && e.message || e);
Expand Down
2 changes: 1 addition & 1 deletion web/figures.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
}

// ---------------------------------------------------------------- n · T · tau_E
const PRESET = { // ITER: this app's sandbox baseline (physics.simulate). MAST: shot #30166 at t = 0.27 s (n̄e, Te0, W/P_loss).
const PRESET = { // ITER: this app's Explore baseline (physics.simulate). MAST: shot #30166 at t = 0.27 s (n̄e, Te0, W/P_loss).
iter: { n: 1.015, T: 7.6, tau: 3.81, label: 'ITER: this app’s model, volume-average T' },
mast: { n: 0.386, T: 1.22, tau: 0.049, label: 'MAST #30166 at 0.27 s: measured, core electron T' },
};
Expand Down
4 changes: 2 additions & 2 deletions web/guide.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
['mouseover', 'focusin'].forEach((ev) => document.addEventListener(ev, (e) => { const t = e.target.closest && e.target.closest('.term'); if (t) showTip(t); }));
['mouseout', 'focusout'].forEach((ev) => document.addEventListener(ev, (e) => { if (e.target.closest && e.target.closest('.term')) tip.hidden = true; }));

// Lab-mode help, from the same glossary: tooltips on panel labels, one line under each sandbox slider, "?" back to a lesson.
// Lab-mode help, from the same glossary: tooltips on panel labels, one line under each Explore slider, "?" back to a lesson.
function labHelp() {
Object.values(G).forEach((g) => (g.on || []).forEach((name) => { const el = target(name); if (el && !el.title) el.title = `${g.name}: ${g.def}`; }));
const KNOB = { Ip: 'ip', B: 'bt', n: 'density', P_aux: 'heating', H: 'h-factor', Zeff: 'zeff' };
Expand Down Expand Up @@ -107,7 +107,7 @@
const keep = new Set(); // the lit elements and every ancestor up to the tab
inTab.forEach((el) => { for (let n = el; n && n !== section.parentNode; n = n.parentNode) keep.add(n); });
const dock = parseFloat(getComputedStyle(document.body).getPropertyValue('--dock')) || 0, r = inTab[0].getBoundingClientRect();
if (r.top < 0 || r.top > innerHeight - dock - 80) inTab[0].scrollIntoView({ block: 'start', behavior: 'smooth' }); // its top is off screen (pages that scroll)
if (r.top < 0 || r.top > innerHeight - dock - 80) inTab[0].scrollIntoView({ block: 'start', behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' }); // its top is off screen (pages that scroll)
keep.forEach((node) => {
if (inTab.includes(node)) return; // a lit element keeps all of its children
[...node.children].forEach((c) => {
Expand Down
2 changes: 1 addition & 1 deletion web/lessons.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@

{ "id": "5.5", "ch": 5, "title": "What the comparison shows",
"tab": "replay", "shot": 30166, "slice": { "t": 0.27 }, "focus": ["insight", "logbook"],
"body": "Two readings of the same shot, side by side. On the left, what the twin works out from the data: confinement relative to each law, the closest approach to a limit and when, and how the learned correction did on this shot. On the right, what the physicist in the control room wrote. When they agree, you can trust both a little more. When they differ, you have found something worth a closer look." },
"body": "Two readings of the same shot, side by side. On the left, what the twin works out from the data: confinement relative to each law, which limit the shot came closest to, and how the learned correction did on this shot. On the right, what the physicist in the control room wrote. When they agree, you can trust both a little more. When they differ, you have found something worth a closer look." },

{ "id": "6.1", "ch": 6, "title": "Same recipe, more power",
"tab": "replay", "shot": 30192, "slice": 0, "view": "cutaway", "highlight": null, "focus": ["bar", "strip-p", "strip-w", "strip-lim"],
Expand Down
8 changes: 5 additions & 3 deletions web/replay.js
Original file line number Diff line number Diff line change
Expand Up @@ -583,9 +583,11 @@
if (q.H98_median !== null)
out.push(`Over ${q.n_steady} near-steady slices the measured confinement time is <b>${fmt(q.H98_median)}×</b> the IPB98(y,2) H-mode law and <b>${fmt(q.H89_median)}×</b> the ITER89-P L-mode law.`);
const name = LIMIT_LABEL[q.peak_limit] || q.peak_limit;
// Limit proximity is drawn twice already — gauges for the instant, the limits strip for the run —
// so the prose names the limit and points at them instead of restating the same numbers a third time.
out.push(q.peak_limit_fraction >= 1
? `<b>${name}</b> limit exceeded: ${fmt(q.peak_limit_fraction)}× at t = ${fmt(q.peak_limit_t_s, 3)} s.`
: `Closest approach to a limit: <b>${name}</b> at ${fmt(q.peak_limit_fraction)}× (t = ${fmt(q.peak_limit_t_s, 3)} s).`);
? `The <b>${name}</b> limit is crossed during this shot — its gauge reads red at the crossing, and the limits strip shows when.`
: `Closest approach to a limit: <b>${name}</b>. Watch its gauge as you scrub, and read the approach off the limits strip.`);
// Only claimed for ohmic shots: with beams the energy holds fast ions and the late phase may well be H-mode.
const ohmic = (s.meta.heating || '').toLowerCase() === 'ohmic';
if (ohmic && q.P_loss_median_MW !== null && q.P_loss_median_MW > 2 * q.P_LH_median_MW && Math.abs(q.H89_median - 1) < 0.25)
Expand Down Expand Up @@ -1286,7 +1288,7 @@
if (!b) return;
if (b.dataset.act === 'cancel') return SL.cancelAll(); // fetched shots stay plotted
if (b.dataset.act === 'exit') return exitCompare();
if (b.dataset.act === 'search') return $('rp-srch-panel').scrollIntoView({ behavior: 'smooth', block: 'start' });
if (b.dataset.act === 'search') return $('rp-srch-panel').scrollIntoView({ behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth', block: 'start' });
if (b.dataset.act === 'share') return void shareCompare(b);
if (b.dataset.act === 'retry') // "Retry failed": every failed chip refetches under the shared machine
return void cmp.ids.filter(x => SL.get(x) === 'error').forEach(id => cmpFetch(id, id === cmp.ref ? p => renderShot(p) : null));
Expand Down
23 changes: 6 additions & 17 deletions web/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@ body.guided header { margin-left:calc(-1 * var(--rail)); }
.entry-action { margin:14px 0 0; }
.entry-action a { color:var(--accent); font:600 13px/1.4 system-ui, sans-serif; text-decoration:underline; }
.entry-action a:hover { color:var(--fg); }
.entry-action a:focus-visible { outline:1px solid var(--accent); outline-offset:2px; }

/* command palette (web/palette.js): native <dialog> in the top layer — no z-index, no overlay div.
Shares the control-room look: panel surface, hairlines, mono hints. */
Expand All @@ -321,23 +320,12 @@ dialog.palette::backdrop { background:rgba(8,10,13,.72); backdrop-filter:blur(2p
.pal-status { margin:6px 2px 0; min-height:1em; color:var(--accent); font:12px/1.5 var(--mono); }
@media (max-width:760px) { dialog.palette { margin-top:6vh; } #pal-list { max-height:60vh; } }

/* Mobile header: the legend (its tags repeat on every panel) and the subtitle step aside, and the
tabs take their own row, so the title never clips (audit defect at 375 px, screenshot fl_AmO2E2fS).
Last in the file: it must out-order the base .prov/.tabs rules it overrides (equal specificity). */
@media (max-width:760px) {
header { flex-wrap:wrap; height:auto; min-height:var(--head); padding:8px 16px; row-gap:2px; }
.prov, header h1 .dim { display:none; }
header .tabs { order:9; flex:1 1 100%; align-self:auto; height:34px; }
/* Reduced motion: the gauge bars' width tween and the guided-study spotlight dim are orientation
aids only — under the user's preference they apply instantly. The two JS smooth scrolls
(guide.js spotlight, the catalog panel jump) gate on the same query at their call sites. */
@media (prefers-reduced-motion: reduce) {
.gauge > .bar, .g-dim { transition: none; }
}
auto; }
.srch-rows td { white-space:nowrap; }
.bad { color:var(--bad); }

/* Explore workspace: the visible path into the anchored editor (Replay, edit intent in the URL) */
.entry-action { margin:14px 0 0; }
.entry-action a { color:var(--accent); font:600 13px/1.4 system-ui, sans-serif; text-decoration:underline; }
.entry-action a:hover { color:var(--fg); }
.entry-action a:focus-visible { outline:1px solid var(--accent); outline-offset:2px; }

/* Mobile header: the legend (its tags repeat on every panel) and the subtitle step aside, and the
tabs take their own row, so the title never clips (audit defect at 375 px, screenshot fl_AmO2E2fS).
Expand All @@ -347,3 +335,4 @@ auto; }
.prov, header h1 .dim { display:none; }
header .tabs { order:9; flex:1 1 100%; align-self:auto; height:34px; }
}

Loading