diff --git a/docs/content-candidate-pool.md b/docs/content-candidate-pool.md index bd4b57d..bb74a07 100644 --- a/docs/content-candidate-pool.md +++ b/docs/content-candidate-pool.md @@ -27,16 +27,27 @@ stays non‑commercial**. | id | clip | source | window (s) | license / credit | |----|------|--------|-----------|------------------| -| `cosmos` | Orion Nebula flythrough | NASA/JPL‑Caltech — https://images.nasa.gov/details/JPL-20221122-SOLSYSf-0001-Orion%20Dust%20and%20Death | ~30–54 | PD (17 U.S.C. §105) | +| `cosmos` | Cosmic Cliffs — Carina Nebula flythrough | NASA SVS 31348 — https://svs.gsfc.nasa.gov/31348/ (`Clifs-3d-STScI.mp4`, *Exploring the Cosmic Cliffs in 3D*, Webb NIRCam, **4K**) | 10–34 | CC‑BY‑class — credit **NASA, ESA, CSA, STScI** | + +> ℹ️ **Cosmos primary swapped to the Webb "Cosmic Cliffs" flythrough (2026-06-25, session 0018).** +> History: the original primary was NASA/JPL's *"Orion: Dust and Death"* (captions +> burned in → cropping lost framing); it was then swapped to STScI's text-free +> *Flight Through the Orion Nebula* (SVS 30957, visible light). The operator found +> the Orion clip merely good and asked for a more dramatic nebula, so the primary is +> now STScI's **Exploring the Cosmic Cliffs in 3D** (Carina Nebula, Webb NIRCam, +> **SVS 31348**) — a full-frame, text-free 3D flythrough sourced from its **4K** +> master (`Clifs-3d-STScI.mp4`, 3840×2160) → supersampled 1080p base + 4K master. +> Trim 10–34s clears the opening title card (gone by ~8s) and the end-credit card +> (~102s). The earlier `decaption_cosmos` crop helper remains removed. | `cosmos_galaxies` | Flying Through Galaxies | NASA SVS — https://svs.gsfc.nasa.gov/vis/a010000/a014900/a014950/14950_Galaxies_FlyThrough_4k.mp4 | 8–32 | PD | | `cosmos_hudf` | Hubble Ultra Deep Field zoom | NASA SVS — https://svs.gsfc.nasa.gov/vis/a030000/a030600/a030687/hudf-b-1920x1080p30.mov | 20–44 | CC‑BY‑class — credit **NASA, ESA, STScI** | | `cosmos_xdf` | eXtreme Deep Field flythrough | NASA SVS — https://svs.gsfc.nasa.gov/vis/a030000/a030600/a030681/hxdf_fly-b-1920x1080p30.mov | 2–26 | CC‑BY‑class — credit **NASA, ESA, STScI** | > ⚠️ **DISCREPANCY to fix next session.** Operator selected cosmos **#1, #3 > (Orion), #4, #5**. During processing, **#6 Galaxy Traverse was run by mistake -> instead of #3 Orion**. The Orion clip (#3) is text‑free and already lives at -> `simulator/sample_media/cosmos/base.mp4` (the prior real base), so the pool above -> is correct. **`cosmos_traverse` is an UNSELECTED extra on disk — drop it** (or +> instead of #3 Orion**. The cosmos primary now lives at +> `simulator/sample_media/cosmos/base.mp4` as the Webb Cosmic Cliffs flythrough +> (SVS 31348 — see the cosmos-primary note above). **`cosmos_traverse` is an UNSELECTED extra on disk — drop it** (or > confirm with operator). The current ring scale id is `cosmos`; when the pool is > wired, `cosmos` becomes one pool member. @@ -108,5 +119,5 @@ Removed from local media (all were gitignored, so repo-invisible): - `forest` — the old Yosemite base, superseded by the `coast` pool. **dropped.** - `reef` — the original placeholder/early reef base, superseded by `reef_*`. **dropped.** - `orbit` / `abyss` — the Increment‑1 single real bases (NASA ISS Exp65 / ctenophore), - not in the new pools. **dropped** (the cosmos Orion base stays as the cosmos pool primary). + not in the new pools. **dropped** (the cosmos base — now the Webb Cosmic Cliffs flythrough, SVS 31348 — stays as the cosmos pool primary). - `review.html` gallery — now `.gitignore`d (local‑only, re‑buildable). diff --git a/docs/superpowers/specs/2026-06-25-simulator-dev-mode-design.md b/docs/superpowers/specs/2026-06-25-simulator-dev-mode-design.md new file mode 100644 index 0000000..3bafe46 --- /dev/null +++ b/docs/superpowers/specs/2026-06-25-simulator-dev-mode-design.md @@ -0,0 +1,64 @@ +# Simulator Dev Mode — design + +> 2026-06-25 · session 0017 · planning-and-executing +> Anchor: leaf task (simulator dev-tooling — R2b, no upstream design needed) + +## Problem + +While previewing the experience, the operator can't choose *which* clip in an +altitude's rotating pool is shown — the server picks a random pool member on each +landing (content-pipeline §11.1). For by-eye review they want to step through every +clip in a pool on demand, and to see the engine/clip data behind what's on screen. +The RenderPlan JSON readout is also always visible, cluttering what should read as +an audience-facing preview. + +## Goal + +A single **Dev Mode** toggle at the bottom of the control panel. Off by default +(persisted in `localStorage`). When on, a panel appears directly below it with all +dev controls and data; when off, the normal view is just the experience + the three +control fieldsets. + +## Scope + +Frontend only — `simulator/static/{index.html,app.js,style.css}`. No API changes: +every datum Dev Mode shows is already available client-side (`/api/clips`, +`/api/ring`, the `/api/alteration` response, and the in-memory preload cache). The +separate `/author.html` remains the place to *edit* labels; Dev Mode is +inspect-and-select only. + +## Dev panel contents (top → bottom) + +1. **Pool picker** — a ` + + -
- RenderPlan readout -
-
+ diff --git a/simulator/static/style.css b/simulator/static/style.css index 4e41e39..f64d528 100644 --- a/simulator/static/style.css +++ b/simulator/static/style.css @@ -66,3 +66,37 @@ input[type=range], select { width: 100%; } .dial-hub { fill: #2c3c5c; stroke: #9cf; stroke-width: 0.6; } .scale-name { display: block; text-align: center; font-size: 12px; color: #cde; } .hint { margin: 0.3rem 0 0; font-size: 11px; color: #789; } + +/* --- Dev Mode --- a single switch; all dev controls/data live in #dev-panel below it. */ +.dev-switch { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; + padding: 0.5rem 0.2rem 0.2rem; border-top: 1px solid #222; user-select: none; } +.dev-switch input { position: absolute; opacity: 0; width: 0; height: 0; } +.dev-switch-track { position: relative; width: 34px; height: 18px; border-radius: 9px; + background: #2a2a2a; border: 1px solid #444; transition: background 0.15s; } +.dev-switch-thumb { position: absolute; top: 1px; left: 1px; width: 14px; height: 14px; + border-radius: 50%; background: #888; transition: transform 0.15s, background 0.15s; } +.dev-switch input:checked + .dev-switch-track { background: #1d3a2a; border-color: #2e6; } +.dev-switch input:checked + .dev-switch-track .dev-switch-thumb { transform: translateX(16px); background: #4e9; } +.dev-switch input:focus-visible + .dev-switch-track { outline: 2px solid #9af; outline-offset: 1px; } +.dev-switch-label { color: #9af; font-weight: 600; letter-spacing: 0.3px; } +.dev-panel { display: flex; flex-direction: column; gap: 0.8rem; } +/* Beat the generic `.hidden` (defined earlier, equal specificity): two classes + win, so the panel truly collapses when Dev Mode is off. */ +.dev-panel.hidden { display: none; } +.dev-panel legend { color: #4e9; } +.dev-btn { margin-top: 0.5rem; width: 100%; padding: 0.4rem; cursor: pointer; + background: #182028; color: #cde; border: 1px solid #2c3c5c; border-radius: 4px; } +.dev-btn:hover { background: #20303f; } +.dev-dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.6rem; font-size: 12px; } +.dev-dl dt { color: #789; } +.dev-dl dd { margin: 0; color: #dde; word-break: break-word; } +.dev-dl dd.mem { color: #4e9; } +.dev-dl dd.net { color: #fc6; } +.dev-anno { font-size: 11px; max-height: 220px; overflow: auto; + font-family: ui-monospace, monospace; } +.dev-anno .anno-row { padding: 0.2rem 0; border-bottom: 1px solid #1d1d1d; } +.dev-anno .anno-key { color: #aee6ff; } +.dev-anno .anno-key.affect { color: #e3cfff; } +.dev-anno .anno-key.measure { color: #ffd79a; } +.dev-anno .anno-meta { color: #678; } +.dev-anno .anno-empty { color: #567; font-style: italic; } diff --git a/tests/test_simulator_api.py b/tests/test_simulator_api.py index 0fcc4d8..b3c5669 100644 --- a/tests/test_simulator_api.py +++ b/tests/test_simulator_api.py @@ -113,8 +113,11 @@ def test_media_is_cacheable(tmp_path, monkeypatch): resp = client.get("/media/cosmos/base.mp4") assert resp.status_code == 200 cc = resp.headers.get("cache-control", "") + # Stored but revalidated: not no-store (so the blob preload can cache it), and + # not pinned immutable (so a re-sourced/re-cropped clip shows on a plain reload). assert "no-store" not in cc - assert "max-age" in cc + assert "immutable" not in cc + assert "no-cache" in cc @pytest.fixture