Compare commits

..

3 Commits

Author SHA1 Message Date
BenStullsBets 950c1e6c8c feat(deploy): move app URL to /human-machine-strata + 301 from the legacy path
Build default app-path → /human-machine-strata; deploy.sh APP_PATH default too.
_write_redirects now emits a permanent redirect /human-experience-simulator/* →
/human-machine-strata/:splat so old links still land. Media (R2 bucket) + Pages
project names are unchanged (not in the benstull.art URL). Updated the build
test (asserts the legacy redirect) + static-build e2e path + README paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 15:00:40 -07:00
BenStullsBets 958c3060d2 feat(rename): retitle the project to "Human/Machine Strata"
Page <title>, on-page <h1> (app.title — kept as the proper title, same across
all 4 languages), the About + Credits headings/prose, and the author dev tool.
"Strata" = the layered cosmos→abyss descent; the slash = the human↔machine
membrane the work is about.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 15:00:40 -07:00
BenStullsBets c36f995463 feat(panel): move Altitude to the top of the panel (mobile: reachable with video on screen)
On mobile the panel wraps below the (sticky) stage; putting the Altitude knob
first means it sits right under the video instead of behind Output + knobs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:29:02 -07:00
10 changed files with 47 additions and 36 deletions
+4 -4
View File
@@ -1,7 +1,7 @@
# Deploying to benstull.art — Cloudflare Pages + R2
Publishes the simulator as a fully-static site: the frontend on **Cloudflare
Pages** (`benstull.art`, app at `/human-experience-simulator`, apex redirects to
Pages** (`benstull.art`, app at `/human-machine-strata`, apex redirects to
it) and the video/audio on **R2** behind `static.benstull.art`. No origin server.
Design: `docs/superpowers/specs/2026-06-30-cloudflare-static-publish-design.md`.
@@ -48,7 +48,7 @@ as a script):
```
Produces:
- `dist/` — Pages output: `dist/human-experience-simulator/` (frontend + baked
- `dist/` — Pages output: `dist/human-machine-strata/` (frontend + baked
`clips.json`/`ring.json`/`media-versions.json` + generated `config.js`) and a
root `dist/_redirects` (apex → app path, and no-slash → slashed).
- `dist-media/` — R2 upload tree, ~2.3 GB / 604 files, manifest-referenced only
@@ -109,7 +109,7 @@ Then **in the dashboard** (Workers & Pages → project → Custom domains) — t
no wrangler CLI for Pages custom domains:
- Bind **`benstull.art`** to the project. (Allow a few minutes — a fresh bind
serves `522` until the cert/routing provisions.)
- The app serves at `benstull.art/human-experience-simulator/`; the shipped
- The app serves at `benstull.art/human-machine-strata/`; the shipped
`dist/_redirects` sends `/` and the no-slash form to it.
## 4. Verify before/after going live
@@ -121,7 +121,7 @@ curl -sI -H "Origin: https://benstull.art" -H "Range: bytes=0-1" \
```
Open `https://benstull.art/` → should redirect to
`https://benstull.art/human-experience-simulator/`. Turn the **Feel** knob to max
`https://benstull.art/human-machine-strata/`. Turn the **Feel** knob to max
and confirm the dream effect renders (no `SecurityError` in the console — that
would mean CORS is misconfigured and the GL texture is tainted). The local
static-build E2E (`simulator/e2e/tests/static-build.spec.ts`) exercises this same
+1 -1
View File
@@ -15,7 +15,7 @@ set -euo pipefail
BUCKET="${BUCKET:-human-experience-simulator-media}"
PROJECT="${PROJECT:-human-experience-simulator}"
MEDIA_BASE="${MEDIA_BASE:-https://static.benstull.art/}"
APP_PATH="${APP_PATH:-/human-experience-simulator}"
APP_PATH="${APP_PATH:-/human-machine-strata}"
BRANCH="${BRANCH:-main}"
JOBS="${JOBS:-10}"
+1 -1
View File
@@ -3,7 +3,7 @@ import { test, expect, Page } from "@playwright/test";
// Runs against the BUILT static site: dist/ served on :8077, media (CORS) on :8078
// by serve-static.mjs. Build with `--media-base http://localhost:8078/` so config.js
// points media cross-origin — the production CORS path, locally. See README.md.
const APP = "http://localhost:8077/human-experience-simulator/";
const APP = "http://localhost:8077/human-machine-strata/";
// Enter the simulation via the welcome screen (Video defaults on → media plays).
async function enter(page: Page) {
+2 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>About — Human Experience Filter</title>
<title>About — Human/Machine Strata</title>
<link rel="stylesheet" href="style.css" />
</head>
<body class="credits-page">
@@ -15,7 +15,7 @@
<section>
<h2>For everyone</h2>
<p>The <em>Human Experience Filter</em> is built to be accessible to — and
<p><em>Human/Machine Strata</em> is built to be accessible to — and
representative of — the full range of human experience. That is why it works
with a keyboard, with a screen reader, with reduced motion, and away from any
single screen or kiosk: an experience about being human should be open to as
+2 -2
View File
@@ -3,12 +3,12 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>HEF — Label Author Mode</title>
<title>Human/Machine Strata — Label Author Mode</title>
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/author.css" />
</head>
<body class="author">
<header><h1>HEF — Label Author Mode</h1>
<header><h1>Human/Machine Strata — Label Author Mode</h1>
<p class="hint">Scrub a pool clip · drag a box on a subject · run the tracker · author the tiers · save to the manifest. Geometry is propagated; <strong>semantics are hand-authored</strong> (content-pipeline §11.5). <a href="/">← back to preview</a></p>
</header>
<main>
+2 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Credits — Human Experience Filter</title>
<title>Credits — Human/Machine Strata</title>
<link rel="stylesheet" href="style.css" />
</head>
<body class="credits-page">
@@ -19,7 +19,7 @@
<section class="declaration">
<h2>License &amp; reuse</h2>
<p>
The <em>Human Experience Filter</em> alters its source footage in real time
<em>Human/Machine Strata</em> alters its source footage in real time
(overlays, an emotion/affect channel, and a WebGL “dream” shader), so every
frame you see is a <strong>derivative</strong> of the original clip.
</p>
+1 -1
View File
@@ -17,7 +17,7 @@
// Visitor-facing control chrome. Keys match `data-i18n` attributes in index.html.
const UI_STRINGS = {
"app.title": { en: "Human Experience Filter Simulation", es: "Simulación del Filtro de Experiencia Humana", fr: "Simulation du Filtre dExpérience Humaine", ja: "ヒューマン・エクスペリエンス・フィルター・シミュレーション" },
"app.title": { en: "Human/Machine Strata", es: "Human/Machine Strata", fr: "Human/Machine Strata", ja: "Human/Machine Strata" },
"loading.title": { en: "Loading Universe", es: "Cargando el universo", fr: "Chargement de lunivers", ja: "宇宙を読み込み中" },
"welcome.launch": { en: "Launch Simulator", es: "Iniciar simulador", fr: "Lancer le simulateur", ja: "シミュレーターを起動" },
"credits.link": { en: "ⓘ Credits & licenses", es: "ⓘ Créditos y licencias", fr: "ⓘ Crédits et licences", ja: "ⓘ クレジットとライセンス" },
+12 -12
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Human Experience Filter Simulation</title>
<title>Human/Machine Strata</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
@@ -44,7 +44,7 @@
</div>
</div>
<header>
<h1 data-i18n="app.title">Human Experience Filter Simulation</h1>
<h1 data-i18n="app.title">Human/Machine Strata</h1>
<a href="about.html" class="credits-link" data-i18n="about.link">About</a>
<a href="credits.html" class="credits-link" data-i18n="credits.link">ⓘ Credits &amp; licenses</a>
</header>
@@ -65,6 +65,16 @@
<section class="panel">
<div id="sr-status" class="visually-hidden" role="status" aria-live="polite"></div>
<fieldset>
<legend data-i18n="altitude.legend">Altitude</legend>
<div class="dial-wrap">
<svg id="dial" viewBox="0 0 100 100" role="slider" tabindex="0"
aria-label="Altitude — turn or use arrow keys to change scale"
aria-valuemin="0" aria-valuenow="0" aria-valuetext="cosmos"></svg>
</div>
<span id="scale-name" class="scale-name"></span>
</fieldset>
<fieldset>
<legend data-i18n="output.legend">Output</legend>
<label class="dev-switch" for="visual">
@@ -103,16 +113,6 @@
<label><span data-i18n="knobs.mood">Mood — dark ◀ 0 ▶ light</span> <input type="range" id="mood" min="-4" max="4" value="0" /></label>
</fieldset>
<fieldset>
<legend data-i18n="altitude.legend">Altitude</legend>
<div class="dial-wrap">
<svg id="dial" viewBox="0 0 100 100" role="slider" tabindex="0"
aria-label="Altitude — turn or use arrow keys to change scale"
aria-valuemin="0" aria-valuenow="0" aria-valuetext="cosmos"></svg>
</div>
<span id="scale-name" class="scale-name"></span>
</fieldset>
<!-- Dev Mode: a single toggle; everything dev (pool picker + analysis) lives below it. -->
<label class="dev-switch" for="dev-mode">
<input type="checkbox" id="dev-mode" />
+6 -5
View File
@@ -9,9 +9,9 @@ def test_build_emits_frontend_baked_json_and_only_referenced_media(tmp_path):
result = build_static(
out, media,
media_base="https://static.benstull.art/",
app_path="/human-experience-simulator",
app_path="/human-machine-strata",
)
app_dir = out / "human-experience-simulator"
app_dir = out / "human-machine-strata"
# frontend present under the app path; dev/author surfaces excluded
assert (app_dir / "index.html").exists()
@@ -25,10 +25,11 @@ def test_build_emits_frontend_baked_json_and_only_referenced_media(tmp_path):
assert not (app_dir / "author.html").exists()
assert not list(app_dir.glob("review*.html"))
# apex + no-slash redirect rules
# apex + no-slash redirect rules, plus the permanent legacy-path redirect
redirects = (out / "_redirects").read_text()
assert "/ /human-experience-simulator/ 308" in redirects
assert "/human-experience-simulator /human-experience-simulator/ 308" in redirects
assert "/ /human-machine-strata/ 308" in redirects
assert "/human-machine-strata /human-machine-strata/ 308" in redirects
assert "/human-experience-simulator/* /human-machine-strata/:splat 301" in redirects
# baked JSON matches the API shape
clips = json.loads((app_dir / "clips.json").read_text())
+16 -6
View File
@@ -59,13 +59,23 @@ def _write_config(app_dir: Path, media_base: str) -> None:
)
# The app's original deploy path, kept as a permanent redirect so old links to
# /human-experience-simulator/… still land on the renamed /human-machine-strata/….
LEGACY_APP_PATH = "human-experience-simulator"
def _write_redirects(out: Path, seg: str) -> None:
# Apex → app path, and the no-trailing-slash form → slashed (so RELATIVE asset
# urls resolve under the prefix). Cloudflare Pages `_redirects` syntax.
(out / "_redirects").write_text(
f"/ /{seg}/ 308\n"
f"/{seg} /{seg}/ 308\n"
)
# urls resolve under the prefix). Plus a permanent (301) redirect from the legacy
# path to the current one. Cloudflare Pages `_redirects` syntax.
lines = [
f"/ /{seg}/ 308\n",
f"/{seg} /{seg}/ 308\n",
]
if LEGACY_APP_PATH and LEGACY_APP_PATH != seg:
lines.append(f"/{LEGACY_APP_PATH}/* /{seg}/:splat 301\n")
lines.append(f"/{LEGACY_APP_PATH} /{seg}/ 301\n")
(out / "_redirects").write_text("".join(lines))
def _write_headers(out: Path, seg: str) -> None:
@@ -158,7 +168,7 @@ if __name__ == "__main__":
ap.add_argument("--out", default="dist")
ap.add_argument("--media-out", default="dist-media")
ap.add_argument("--media-base", default="https://static.benstull.art/")
ap.add_argument("--app-path", default="/human-experience-simulator")
ap.add_argument("--app-path", default="/human-machine-strata")
a = ap.parse_args()
r = build_static(a.out, a.media_out, media_base=a.media_base, app_path=a.app_path)
print(json.dumps(r, indent=2))