Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2e84c18
[feat] 24-A A1: the knock - a hand or a walking player hits a dynamic…
AlexZ005 Sep 5, 2026
6129314
[feat] F3: proportional translate replicates its falloff neighbours
AlexZ005 Sep 16, 2026
d701b6d
[fix] F2: vertex slide says why it stands down under a placed pivot
AlexZ005 Sep 16, 2026
52a3b0e
[feat] 24-A A2: onhit node, api.onHit/hitLog, Knock rows, haptics, si…
AlexZ005 Sep 16, 2026
0030bf3
[feat] 24-A A3: one graphBuilder, a remap that walks every string field
AlexZ005 Sep 16, 2026
661d7c5
[feat] F1: proportional falloff for rotate and scale
AlexZ005 Sep 16, 2026
2ebd781
[feat] P2 watch-look: watching a peer adopts their look state
AlexZ005 Sep 12, 2026
58606cd
[feat] 81.4: docked edge splits
AlexZ005 Sep 16, 2026
3447f55
[feat] P4 post domain: a shader graph that IS a post-processing effect
AlexZ005 Sep 16, 2026
161ac03
[feat] P5 scene default material: the local render gate, and the clai…
AlexZ005 Sep 16, 2026
128ed57
[feat] 114: flow editor mouse bindings
AlexZ005 Sep 16, 2026
6c69a68
[feat] P6 integration sweep: one Scene look story, one cost line, two…
AlexZ005 Sep 16, 2026
086a18a
[feat] D2 shared materials: an opt-in link, with the identity the sha…
AlexZ005 Sep 16, 2026
f3abca7
[feat] 24-A A4: the Stars Room, and two core bugs the authoring found
AlexZ005 Sep 16, 2026
8093713
Merge release/next (1.12.0, the hardening batch) into feat/mesh-windo…
AlexZ005 Sep 17, 2026
5a2f08a
Merge release/next (1.12.0, the hardening batch) into feat/24-stars
AlexZ005 Sep 17, 2026
a3ccdf5
Merge remote-tracking branch 'origin/feat/24-stars' into feat/1.13
AlexZ005 Sep 17, 2026
9db4259
Merge release/next (1.12.0, the hardening batch) into feat/looks-mate…
AlexZ005 Sep 17, 2026
d1fab11
Merge feat/looks-materials into feat/1.13 β€” the three core lanes stacked
AlexZ005 Sep 17, 2026
4314e85
[docs] 1.13: the changelog entry, the architecture map and the gotcha…
AlexZ005 Sep 17, 2026
b8d1b7c
[docs] 1.13: the Football entry says what actually ships
AlexZ005 Sep 17, 2026
dee0d27
Merge pull request #216 from theprototype-app/feat/1.13
AlexZ005 Sep 18, 2026
2af07d9
[docs] 1.13.0: name the release, before the bump renames it for us
AlexZ005 Sep 18, 2026
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
71 changes: 71 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,77 @@
per release, newest first. HTML comments like this one are stripped before
rendering, so maintainer notes stay out of the user-facing window. -->

## 1.13.0 β€” Knock it about πŸͺ

### ✨ Knock things about (roadmap #24)

- 🌠 **Hit a floating object with your hand and it flies off.** In VR your hands, and on
desktop the camera you walk with, now knock a physics object at the speed you hit it β€”
the first thing a zero-g room needs and the thing grab-and-throw never covered. Configure
Scene β–Έ Physics β–Έ **Knock** turns it on for a scene and sets the strength, the reach and
the spin; it is off in every scene that does not ask for it.
- πŸ’₯ **An On Hit node** fires when something is knocked, with how hard (`speed`) and whether
it was you (`byMe`), so a graph can burst particles in proportion or count only your own
touches. **Who** can be narrowed to anyone / me / others.
- 🌟 **The Stars Room** β€” a new template in Games. Twenty-four stars and two planets float in
a room with no gravity; knock them, watch them chime and drift, add more from the HUD, or
press Start for a round that ends when every star is lit. Nothing to install.
- πŸ”„ **Someone joining a running simulation now knows it is running**, so their knocks and
their grab work from the first second instead of after the next restart.
- 🩹 Fixed on the way: a Player Variable now reaches the node it is wired to, and a colour,
shader-uniform or device effect keeps working on an object the physics simulation owns.

### 🎨 One scene look, and materials you can share

- πŸ‘€ **Watching someone shows you their look.** Watch a peer and you see the camera they are
looking through, its grade, their view mode and their scene-look switches β€” the banner says
when something cannot be adopted. It ends when you stop watching.
- πŸŒ“ **Shader graphs have a Post domain**: build a post-processing effect as a node graph and
drop it into the scene look. Ships with Posterise, Ordered dither, Edge detect (ink) and a
graph-built Ambient occlusion.
- πŸ—‚οΈ **Configure Scene β–Έ Scene look** is now one section for the whole look β€” the post stack,
the scene's default material and per-object shaders β€” with a line saying what it costs.
- πŸ™ˆ **Scene shaders can be switched off on your own screen** (Configure Scene β–Έ View β–Έ
Overrides), the way post already could. Nobody else's view changes.
- πŸ”— **Duplicate can share a material instead of copying it** (Settings β–Έ Scene β–Έ Duplicate).
A shared material carries its link through save, undo, a peer's copy and a late joiner, and
the Material section has **Unlink** when you want your own again.

### 🧱 Modelling, windows and the node editor

- 🫱 **Proportional editing reaches your peers.** Drag a vertex with a falloff radius and the
whole neighbourhood now arrives on every other screen (in one step, when you let go) β€”
before, only the vertices you had selected moved for anybody else.
- πŸ”ƒ **Proportional rotate and scale**, not just move: the falloff blends the turn and the
scale toward identity across the radius, so a rotation twists the surrounding surface
instead of leaving it behind.
- πŸ’¬ Vertex slide now says why it stands down while a custom pivot is placed, instead of
quietly doing nothing.
- πŸͺŸ **Two windows on one screen edge.** Drop a second window onto a docked one and the edge
splits into two stacked panels with a divider you can drag; the share is remembered per side.
- πŸ–±οΈ **Settings β–Έ Input β–Έ Node editor β–Έ Mouse bindings** β€” keep Classic (left-drag pans, as
always) or switch to Select-first, where a left drag draws a selection rectangle, dragging
any selected node moves the whole set, and the right button pans.

### ⚽ VR Football (a module)

- πŸ₯… **Football** is a new module in Browse: two floating gates, one ball, red against blue,
played with your hands. Install it, open its toolbox and **Build pitch** lays out the pitch,
the gates and the rules in your scene. Goals go to whoever touched the ball last, own goals
land on the right sheet, and the mode decides when the match ends (first to N, a clock, or
either). Scores, touches and own goals are per player, the gate lamps read from across the
room, and a saved scene keeps the match log. Two people in one room can play it colocated.
(A ready-made Football template for the Games tab is not published yet β€” the toolbox recipe
is how you get a pitch today.)

### πŸ› οΈ For people building on it

- πŸ₯Š **`api.onHit(cb)` and `api.hitLog()`** β€” a module can react to a knock (uuid, who, how
hard, where) or read the recent ones, which is what the football module's last-touch rule
stands on.
- 🧩 The template author script gained one graph builder and a remap that resolves every
object named in node data, so a game def can name objects instead of carrying uuids.

## 1.12.0 β€” Hold together πŸ›‘οΈ

### πŸ›‘οΈ Connections that recover, and sessions with a size (roadmap #27 + #25)
Expand Down
76 changes: 76 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2013,6 +2013,51 @@ loadable play content. Everything a user does must be visible to connected peers
`docs/plans-core/`, local `../theprototype.app-cloud`). This repo's `/docs` is
gitignored scratch space (pointer READMEs inside).

- `src/lib/knock.js` + `knockMath.js` (24-A) β€” A HAND KNOCKS A BODY. `knockMath` is the pure
leaf (THREE + `throwVelocity`): a 6-sample/100ms probe ring, `contactOf` (sphere vs bounding
sphere; `approach` = closing speed along the normal), `knockResponse` (`v' = v_body + n *
approach * gain`, infinite-mass hand, spin from the TANGENTIAL slip β€” a sphere contact is
central, so `r Γ— Ξ”v` is zero by construction), `cooldownStep`. `knock.js` is the runtime:
VR hands arrive through a seam `Scene.svelte` passes in (it never imports vrControls), the
desktop camera is a 0.35 m head probe, both carried into the objects group's frame. The
HITTER broadcasts `{type:'hit'}` whoever it is; the INITIATOR applies it (`physics.applyHit`,
the throw's sibling β€” `clampThrow` + the scene's `maxSpeed`, CCD over 5 m/s); a
non-initiator predicts locally behind `knock.predict` and withdraws after 400 ms.
`hit` is CONTENT (gateable, room-scoped), `by` is stamped from the connection, and its `at`
is **`sessionNow()`** β€” A2 folds it into the trigger log beside every other stamp, so it has
to be on 25-E's session clock, not the sender's raw one. `scenePhysics` carries the nested
`knock` block, `enabled:false`, which is what keeps every saved scene byte-identical.
- `src/lib/lookPresence.js` (P2) β€” a peer's LOOK as PRESENCE, in the `campreview` shape:
`{camera, mode, overrides:{post,shaders}, look}` sent on change and in the `getmodulestate`
reply, dropped at both `finalizeDisconnect` sites. Watching a peer resolves the post chain
from THEIR row (`Outline.svelte`), and 26-D's quality governor still applies on top β€” the
governor is this machine giving up post to hold its frames, which watching must not undo.
- `src/lib/postGraphs.js` + `postGraphPresets.js` (P4) β€” a shader graph whose output IS a
post-processing effect. `shaderGraph`'s `registerPostDomain` seam keeps the compiler core
shared (`createCompiler(graph, outputType)` in `shaderCompile.js`); the post domain differs
only in its inputs (SceneColor/SceneDepth/SceneNormal/UV/Time/Resolution) and its terminal
(`postOutput`), plus `EffectAttribute.DEPTH` and a NormalPass added on demand.
- `src/lib/materialSharing.js` (D2) β€” two objects, ONE material, by id. The id is scene data
and survives every carrier (wire, autosave, sessions, undo, GLTF rebuild) because
`startMaterialSharing()` re-unifies by id whenever the scene changes; the SENDER fans the
per-object messages a receiver already understands, so the wire is byte-unchanged and no
capability-gate entry or older-peer story is needed. `materialsHandler`'s material-TYPE
swap is the one op that replaces the instance rather than writing into it, so it hands the
new instance to the sharers too.
- `src/lib/flowPrefs.js` (114) β€” the node editor's mouse bindings as a LOCAL pref leaf
(svelte/store + safeStorage): `classic` (left-drag pans, the default and byte-identical to
every version before it) or `select` (left-drag rectangle-selects, right-drag pans).
- `docking.js` (81.4) β€” an edge holds TWO stacked panels: `docked` is
`{left: string[], right: string[]}`, `dockSplit:<side>` is the share, and a DOCKED window's
rect belongs to docking.js β€” so `dragWindow`'s and the object list's reveal clamps stand
down for it, exactly as they already do for a tab member.
- `meshEdit.js` (F1/F3) β€” a proportional drag ends in ONE whole-geometry `meshgeo` commit
(`commitFalloffSnapshot`), applied LOCALLY as well as broadcast: `applyMeshGeo` rebuilds the
receiver index-EXPANDED while a `/create Plane` is indexed, so both sides must swap together
or the sender's next `verts` indices address a layout the peer no longer holds. The selection
is re-found by POSITION after the swap. `applyPivotTransform` now covers the falloff for
rotate and scale by slerping the rotation and lerping the scale toward identity per vertex.

## Replication golden rules

1. Every mutation = apply locally + `$peers.send({type, ...})`; receivers apply WITHOUT
Expand Down Expand Up @@ -4180,6 +4225,37 @@ loadable play content. Everything a user does must be visible to connected peers
sometimes without even an unused-selector warning); svg `className` is an
SVGAnimatedString β€” e2e reads `getAttribute('class')` and selects `svg`, not `i`.

- **`git checkout --theirs -- <file>` during a merge discards the WHOLE ours side of that
file, not just the conflicted hunk.** Resolving App.svelte's debug-hook tails that way
silently dropped the one `import('./lib/knock')` line that had auto-merged cleanly 100 lines
above, leaving a destructure with one more name than the import list β€” every module after it
bound to its neighbour. The three tails (the `Promise.all` import list, the destructured
parameter list, the `window.__stores` object) must always COUNT EQUAL and be in the same
order; check that after any merge that touches them.
- **An attribute INDEX recorded before a `meshgeo` commit addresses a DIFFERENT vertex after
it** β€” the commit rebuilds the mesh index-expanded (81 entries β†’ 384 in triangle order). Any
fixture or handle map captured before it must be re-found by POSITION, not carried over.
- **A handle hung past a docked window's edge is clipped away and takes no pointer events** β€”
every docked window is `overflow-hidden`. The 81.4 divider sits INSIDE the top panel; the
width grip only works because half of its 6px is inside.
- **Chromium fires `contextmenu` on the PRESS**, so a travel check there is always zero.
Decide click-versus-drag on pointerup (114's pane menu does).
- **The node pane's empty bottom-right corner is the MINIMAP** β€” a drag there pans at the
minimap's own scale (measured 631px for an 80px gesture).
- **`.peer-watch` is worn by TWO buttons** β€” Watch, and the join-a-peer's-camera button
(`.peer-watch.peer-preview`) that only renders while that peer previews a camera. A suite
selecting the bare class joins the camera instead of watching and reads the right answer for
the wrong reason; `.spectator-exit` has the same shape.
- **A type check cannot tell a shader-driven material from its base** β€” the injected material
is a CLONE, so both read `MeshStandardMaterial`. Measure material IDENTITY instead.
- **A guard that is redundant on the happy path is not proven by the happy path**: D2's fan and
its applier link both survived deletion until the suite staged the one state each exists for.
- **A constant in a shader graph must be a Float node** β€” the arithmetic nodes take operands
from sockets and have no params, so authored `{b: 0.5}` node data is silently ignored.
- **A perRound latch count reads 0 the instant the round ends** (the Infinity cutoff), so a
suite that polls `flowValues` for "all N lit" can only ever catch it for one publish. Assert
the TRIGGER LOG instead, folded to seconds-of-day the way `retiredByRound` does.

## Verification (mandatory before commit)

PIXEL features (post-processing, outlines, AO) are asserted through the helpers
Expand Down
Loading
Loading