Skip to content

Commit 4297c74

Browse files
committed
Add playable slice cauldron playbooks and holodeck measurement groundwork.
Deprecate WebGL from the default renderer chain, expand CursorLayer and measure protocol design, retire city.load-by-letter for tool shortcuts, and document Batch 1 playbooks (PB-01–PB-05) plus Holodeck cutover skeletons.
1 parent 80364c7 commit 4297c74

63 files changed

Lines changed: 4869 additions & 225 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/emscripten_build.yml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# Manual runs only — avoids automatic deploy on every push to main.
1+
# Manual runs only — does NOT run on push to main.
2+
# Protects https://micropolisweb.com/ during development: merge freely; publish only when ready.
3+
#
24
# Actions → Build Wasm Library with Emscripten → Run workflow
35
#
46
# What this does:
@@ -7,7 +9,7 @@
79
# 3. Verify monorepo structure invariants (pnpm run verify:structure)
810
# 4. Optionally generate Doxygen C++ API docs
911
# 5. Build SvelteKit app (pnpm --filter micropolis run build) — includes Jekyll
10-
# 6. Deploy static site + optional Doxygen to GitHub Pages
12+
# 6. Deploy to GitHub Pages ONLY if you check "Deploy to GitHub Pages" (default: off)
1113

1214
name: Build Wasm Library with Emscripten
1315

@@ -18,10 +20,20 @@ on:
1820
description: Generate Doxygen C++ API HTML and publish it at /doc on Pages
1921
type: boolean
2022
default: true
23+
deploy_to_pages:
24+
description: >-
25+
Deploy build to GitHub Pages (micropolisweb.com). Leave unchecked while
26+
developing — verify the build first, then re-run with this enabled.
27+
type: boolean
28+
default: false
2129

2230
permissions:
2331
contents: write
2432

33+
concurrency:
34+
group: micropolis-pages-deploy
35+
cancel-in-progress: false
36+
2537
jobs:
2638
build:
2739
runs-on: ubuntu-latest
@@ -89,7 +101,10 @@ jobs:
89101
fi
90102
91103
- name: Deploy to GitHub Pages
92-
if: success() && github.ref == 'refs/heads/main'
104+
if: >-
105+
success() &&
106+
github.ref == 'refs/heads/main' &&
107+
inputs.deploy_to_pages == true
93108
uses: peaceiris/actions-gh-pages@v4
94109
with:
95110
github_token: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/pr-checks.yml

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
# Lightweight checks that run on every push and PR — no Emscripten required.
2-
# Tests use the committed WASM artifacts in apps/micropolis/src/lib/.
3-
#
4-
# Jobs:
5-
# structure — verify monorepo layout invariants (20 assertions)
6-
# build-ts — TypeScript compile for vitamoo + mooshow + vitamoospace
7-
# test — Micropolis Vitest suite (real WASM in Node, 18 tests)
1+
# Lightweight checks on push/PR — no Emscripten, no GitHub Pages deploy.
2+
# The live demo at https://micropolisweb.com/ is unchanged by pushes to main.
3+
# Publish only via: Actions → "Build Wasm Library with Emscripten" → deploy_to_pages ✓
84

95
name: PR Checks
106

apps/micropolis/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ pnpm run build
5252

5353
Preview the production build with **`pnpm run preview`**.
5454

55+
## Publishing (micropolisweb.com)
56+
57+
The public demo at [micropolisweb.com](https://micropolisweb.com/) is **not** updated by pushes to `main`.
58+
59+
| Action | Updates live site? |
60+
|--------|------------------|
61+
| `git push origin main` | No — runs PR Checks only |
62+
| `pnpm run dev` / `pnpm run preview` locally | No |
63+
| **Actions → Build Wasm Library with Emscripten** | Only if you check **Deploy to GitHub Pages** |
64+
65+
Deploy defaults to **off**. Run the workflow without deploy to verify a production build; run again with deploy enabled when ready to publish.
66+
5567
## CLI
5668

5769
One CLI exposes city-file tools, terminal visualizations, the WASM simulator, and the command bus:

apps/micropolis/src/lib/TileView.svelte

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -354,13 +354,6 @@
354354
spacePanHeld = true;
355355
}
356356
357-
if ((key >= "a") && (key <= "z")) { // letters — load city by first letter
358-
event.preventDefault();
359-
const letter = key.charCodeAt(0) - "a".charCodeAt(0);
360-
void commandBus.dispatch('city.load-by-letter', commandContext(event, { letter }));
361-
return;
362-
}
363-
364357
switch (key) {
365358
366359
case " ": // space
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* Svelte 5 rune binding for holodeck sparse measure protocol.
3+
* @see documentation/designs/map-compositing-and-measurement.md §3.4–§3.5
4+
*
5+
* FUTURE: multi-player collaborative tools (shared refs, role-scoped writable props,
6+
* tool-as-vehicle) use the same bindProp/setProp path — see §3.5. Not implemented yet.
7+
*/
8+
import {
9+
applyMeasureConstraint,
10+
applyMeasurePatch,
11+
createMeasureSnapshot,
12+
getMeasureProp,
13+
isMeasurePropWritable,
14+
resolveMeasureConstraint,
15+
type HolodeckStage,
16+
type MeasureJson,
17+
type MeasurePropertyConstraint,
18+
type MeasureRef,
19+
type MeasureSnapshot,
20+
type MeasureValue,
21+
} from '@micropolis/render-core';
22+
23+
export interface MeasureStoreOptions {
24+
/** Refs to subscribe — sparse read/patch each frame. */
25+
refs: MeasureRef[];
26+
}
27+
28+
export type MeasureStore = ReturnType<typeof createMeasureStore>;
29+
30+
/**
31+
* Reactive measure snapshot tied to a holodeck stage.
32+
* Call `tick()` after input/state changes or from rAF; returns true if DOM should relayout.
33+
*/
34+
export function createMeasureStore(stage: HolodeckStage, options: MeasureStoreOptions) {
35+
const snapshot = $state<MeasureSnapshot>(createMeasureSnapshot(options.refs));
36+
37+
function get(ref: MeasureRef): MeasureValue | undefined {
38+
return snapshot.values[ref];
39+
}
40+
41+
function getProp(ref: MeasureRef, key: string): MeasureJson | undefined {
42+
return getMeasureProp(get(ref), key);
43+
}
44+
45+
function tick(): boolean {
46+
stage.render();
47+
const patch = stage.measurePatch(options.refs, snapshot.frame);
48+
return applyMeasurePatch(snapshot, patch);
49+
}
50+
51+
/** Push sparse property patches into plugins before render (two-way bind: UI → holodeck). */
52+
function write(patches: Record<MeasureRef, Record<string, unknown>>): void {
53+
stage.measureWrite({ op: 'write', schema_version: 1, patches });
54+
}
55+
56+
/**
57+
* Write one bindable property with constraints (lineWidth, opacity, left, …).
58+
* Returns false if holodeck marked the key read-only.
59+
*/
60+
function setProp(
61+
ref: MeasureRef,
62+
key: string,
63+
value: MeasureJson,
64+
constraint?: MeasurePropertyConstraint,
65+
): boolean {
66+
const current = get(ref);
67+
const resolved = resolveMeasureConstraint(key, constraint, current);
68+
if (!isMeasurePropWritable(key, resolved, current)) return false;
69+
const constrained = applyMeasureConstraint(value, resolved);
70+
if (constrained === undefined) return false;
71+
write({ [ref]: { [key]: constrained } });
72+
return true;
73+
}
74+
75+
/**
76+
* Two-way rune binding for one property on a ref.
77+
* Holodeck → UI on `tick()`; UI → holodeck on assignment.
78+
*/
79+
function bindProp(ref: MeasureRef, key: string, constraint?: MeasurePropertyConstraint) {
80+
let local = $state<MeasureJson | undefined>(undefined);
81+
let pushing = false;
82+
83+
function pull(): void {
84+
if (pushing) return;
85+
const raw = getProp(ref, key);
86+
const resolved = resolveMeasureConstraint(key, constraint, get(ref));
87+
local = applyMeasureConstraint(raw, resolved);
88+
}
89+
90+
function push(value: MeasureJson): void {
91+
pushing = true;
92+
if (setProp(ref, key, value, constraint)) {
93+
tick();
94+
pull();
95+
}
96+
pushing = false;
97+
}
98+
99+
pull();
100+
101+
return {
102+
get current() {
103+
return local;
104+
},
105+
set current(value: MeasureJson) {
106+
push(value);
107+
},
108+
pull,
109+
push,
110+
};
111+
}
112+
113+
return {
114+
get snapshot() {
115+
return snapshot;
116+
},
117+
get,
118+
getProp,
119+
setProp,
120+
bindProp,
121+
tick,
122+
write,
123+
};
124+
}

0 commit comments

Comments
 (0)