Repository navigation
Framing build animation: a lazy nested reveal, an animation speed, and a level that follows the project - #1093
Conversation
…r undeclared kinds The framing level now plays on its own schedule (`planLazyReveal`, `LAZY_REVEAL_TIMING`): floors a beat apart, the rooms of a floor a beat apart, the pieces of a room one by one around it, every start jittered; roofs once every floor is up, then furniture room by room. A big build takes proportionally longer and is pulled together only past the 40 s cap. Full keeps `planReveal` and its timing unchanged. Candidates carry their room: the zone a node stands in (a wall, the zone on either side of its middle), else runs of walls that meet end to end. A plugin may ask for a preroll before nodes of the lazy schedule (`setRevealPreroll`) and reads each node's turn on the reveal's clock (`getRevealTurn`, `revealClockMs`); a preroll already playing keeps its turn when the plan is made again. `emitRevealPuff` lends the reveal's dust. On the lazy schedule a rise, scale or settle overshoots and settles (back.out), drops and settles fall from higher, and drops anticipate. A new node whose kind declares no reveal, added where its host was already there, now comes in on a default reveal instead of popping; under a host still coming in it comes in with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At 1.6 a rising wall peaked 9 % past its top, into the slab over it while the floors build side by side. The lazy schedule's overshoot is now per style: rise 0.7 (about 2 % past), scale and settle 1.6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner's case: one add_volume commit (two storeys, three rooms below, a hip roof) staged at the framing level. Every wall, slab, room and roof it makes goes through the timeline; its walls are grouped by the zones it makes and the rooms go up side by side; slabs first, floors offset, the roof once the walls are up, at most two starts in a 60 fps frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_PACE times as long FRAMING_PACE (1.75) multiplies every offset, gap, beat, motion duration and the cap of the lazy schedule, so a build that took 20 s takes 35 s and a smaller or larger one scales the same; jitter, overshoots and heights do not move. The original numbers stay as LAZY_REVEAL_BASE_TIMING and LAZY_REVEAL_TIMING is pacedLazyTiming(base, FRAMING_PACE). Full reads REVEAL_TIMING and RETRACT_TIMING and is untouched; a test pins its tables as literals. The viewer exports commitRevealCandidates and the pace for Bones and its measurements. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A speed of x0.5 to x2 divides every time of a schedule: Full's REVEAL_TIMING, Framing's lazy table (at its pace) and the undo's rewind, caps included. x1 returns the shipped tables themselves. ConstructionReveal takes a speed; it is read when a plan is made, the plan's speed is kept until what waits has started, and what is already moving keeps its own durations, so a change applies to the next build and never moves one playing. getRevealPlanSpeed tells a plugin that stages its own pieces which speed the plan in force was made at. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… project
Settings, Preferences: Animation speed (x0.5, x0.75, x1, x1.5, x2), persisted with the level under
the same key and normalised to a step. The level that plays is the person's choice, or, with none of
their own, Framing where the project has the plugin that draws the framing and Full where it has not;
Framing is greyed out with a tip ("Framing needs the Bones plugin.") without it and falls back to
Full, and the saved choice comes back with the plugin. The plugin is read through the registered
framing kind and the project's installed plugins, so it holds wherever the kind lives. Hosts read the
level through useConstructionRevealLevel; a segmented control's option can be disabled.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The plugin that draws the framing is a Pro one, so its level wears the mark the plugin manager shows next to a plugin that needs a plan. The manager's badge becomes a shared primitive, used by both.
Where the framing plugin is missing the Framing option is greyed out, and its tip no longer names Bones: on the hosted product the plugin a person installs is Architect, which brings Procedural and with it the framing kinds.
…e level display hides; an undo plays back the kinds that came in with a default reveal A build above the selected level, in the solo view of another, or an undo of such a build, used to start at once and unseen. The reveal now publishes the levels it has work on (levelsRevealing); the level system shows them, the node batch re-offers its sources when they change, and Follow's line of sight counts them. Only the author's own eye on a level still keeps its build instant.
… by the reveal, on a level and on a roof, at the full and framing levels
…s above shown, and the person's display comes back when it ends
… levels it works on
…es), and a kind lists the fields that make its picture (revealOnEdit)
…kly, and a deletion plays the creation backwards from a snapshot Edits: nodes list revealOnEdit fields (wall, fence, slab, ceiling, column, stair, openings, cabinet, shelf, item scale, roof segment); a change to one of them from an edit, not from reconciliation, re-rises (or re-scales, re-settles) the mounted node at the animation speed, in the changes' groups. Deletions: the node is out of the store at once; a clone of its root, taken as the commit lands, plays the reverse reveal staggered like a build in reverse, and is removed. The host chooses which commits play deletions. Follow looks at an edited piece. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t frame The edits' stagger is the cap divided over the pieces. The wiki says how an edit and a deletion play. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… schemas main has Main's shelf and cabinet have no curveRadius, its roof segment no sweptProfile, and it has no facade kind. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…is laid over it in place, from eave to ridge An assembled node (the roof) played the same drop of its whole shell at the Framing level as at Full, after Bones' frame had been shown and taken away. Framing now plays it as a covering: the node holds still and its meshes wear a world-height mask that opens row by row from the lowest edge (the eave) to the highest (the ridge); a flat roof sweeps along its longer plan side. Its parts are not staged on their own. Undo and a deletion lift the covering off from the ridge in reverse, then the bare node goes. The speed setting scales the sweep. Full, Simple and Off are as they were: the Full tables are unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ki says how a roof is covered at Framing Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… room by room, as it went up At the Framing level a deletion (and an undo) plays the build's own plan read from its end: the furniture lifts out first, then the roof's covering, the openings, the walls and the slabs last, the floors from the top down, each room as it went up, with the prerolls a plugin asked kept after each piece's going (getRetractTurn). A deleted floor's pictures stand in their building, so they outlive the floor's own object. Full keeps its rewind. The reverse plan is capped at 14 s however many pieces it takes back. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t stood in Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… a room is deleted The slabs and ceilings of a floor are all derived by the reconciler, and a deletion skipped every derived surface, so the last pieces of a floor never came apart. They play when the commit also takes a wall or a room off their level; a plate the reconciler merely derives anew still is not a deletion. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… its size The covering was laid in 4 to 14 steps, one row at a time, over a fixed 2.6 s. Its mask level now moves with every frame from the lowest edge to the highest (an ease in and out, no steps), over a time of 450 ms for each metre of sweep at pace 1, never under 1.4 s nor over 4.2 s (at the Framing pace 0.79 s a metre, 2.45 s to 7.35 s); a lifting off, for an undo or a deletion, plays the same sweep backwards in 0.6 of that. The speed setting scales all of it; Full is unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An exterior wall faces the way its exterior side does: what a camera that follows an edit of it looks at it along. An interior wall, and a wall with no exterior side, still answer null. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The reveal publishes the nodes playing (moving in, going out, carried, an edit replaying) in nodesRevealing; in cutaway the wall cutout cache keeps a wall stamped visible for as long as it, an opening it hosts or a node it stands under plays, and goes back to what the view wants after. The person's wall mode is never written; walls down stays their choice. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…om outside, with the walls up An exterior wall bent, a window moved along one, a roof or the ground edited is an outside step: Follow sets the walls up for it and frames the exterior face (it would have fallen back to the cutaway, which hides the wall that plays). A commit that changes one such piece takes the outside view for all of it. A build keeps its single cutaway and never writes the walls up in between. The walls going up is a presentation change Follow puts back; a mode the person picks meanwhile is theirs. A window or door moved along its wall replays (position joins revealOnEdit). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eplay its windows The viewer index exports commitEditCandidates for the test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…er the cutaway The picture a deleted wall plays is a copy of what stood there and wore the material its source had in that frame: for a wall the cutaway was hiding, the stipple, for the whole reverse play. The copy now wears the wall's own visible variant, as a capture draws it; a wall the cutaway showed and the person's other wall modes keep what they wear. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…'s stricter toEqual
…glazing main added
…0261016 # Conflicts: # packages/viewer/src/systems/wall/wall-cutout-cache.ts
…not draw its groups
…t a list of kind names
…nd dresses a deletion ghost from the wall cutout
… with a no-op commit A derived write that set a value a node already had, deleted a node that was not there, or cancelled out inside a history step left its note in the module map. The next real commit inherited it, and the reveal read that commit's own edit as derived and skipped its replay. Notes now take only the fields whose value changed and the nodes that exist, a no-op commit or transaction clears them, and applyNodeChanges notes its deletes as the whole node.
…loor comes apart, then eases down
…0261016 # Conflicts: # packages/nodes/src/fence/definition.ts # packages/nodes/src/stair/definition.ts
…opening keeps its wall in play, Follow reads an outside edit per commit - A run of loose walls is walked along the outline (an open chain from one of its ends), so a run is one stretch of it and the pieces come up around the outline. - A deleted node's picture plays from a snapshot out of the scene: the node it hung on (when it stays and is not a storey or a building) is published in nodesRevealing, so the cutaway leaves the wall a departing opening stood in. - Follow takes a commit outdoors only from the edit it is hearing: a build, a deletion or the edit after a finished play never inherits what another commit queued.
… Framing the plugin cannot play yet
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit faa6e27. Configure here.
| if (played.current) outsideBuild.current = false | ||
| const changed = outsideBuild.current ? [] : revealChangedNodeIds() | ||
| if (changed.length && anyExterior(useScene.getState().nodes, changed)) | ||
| outsideBuild.current = true |
There was a problem hiding this comment.
Exterior latch clears mid-commit
Medium Severity
The Follow Pascal outdoor latch turns off on every reveal event that is not changed: true. A single commit can both replay an exterior edit and start other staged pieces (a new slab the reconciler just created, furniture in the same turn). Those starts are not marked changed, so they clear outsideBuild. The camera then falls back to cutaway and hides the wall that is still playing.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit faa6e27. Configure here.


Framing build animation: a lazy nested reveal at the Framing level, one pace, an animation speed, a level that follows the project, walls that keep their finish through the rise, and a reveal that follows edits, deletions and the person's display
Base:
pascalorg/editormain (bb7d014c0, #1001), merged in, not rebased. Head: branchfeat/framing-build-animation-20261015(faa6e27c3), from the AxiomeCG fork. 47 commits; squash-merge.Pairs with the private-editor PR "Framing build animation: Bones builds each wall in its preroll". That PR pins this head and must be re-pinned to the squash commit once this one merges.
What is in it (read this first)
FRAMING_PACE1.75) and with a cap that grows with the build. Full, Simple and Off keep their tables (a test pins them).setRevealPreroll,getRevealTurn,revealClockMs,getRevealPlanSpeed,emitRevealPuff,commitRevealCandidates,planLazyReveal).derivedWriteson a commit;revealOnEdit,exteriorandrevealLayouton a kind). No schema, no migration, no mcp or app change.What changes for a person
The Construction animation setting already offers Framing, described as "Full, plus the framing members dropping in first, where the framing plugin draws them". Today the viewer treats Framing exactly as Full and nothing reads it. This PR gives Framing its own schedule in the construction reveal and two settings around it.
The Framing schedule
FRAMING_PACE= 1.75: a build that took 20 s now takes about 35 s, and a smaller or larger one scales the same;Full, Simple and Off keep their schedules. Full reads
REVEAL_TIMING/RETRACT_TIMINGand a test pins their tables as literals.Animation speed
Settings, Preferences, under Construction animation: Animation speed ×0.5, ×0.75, ×1, ×1.5, ×2 (default ×1). Every time of a schedule is divided by the speed, caps included: Full, Framing (at its pace) and the undo's rewind. ×1 returns the shipped tables themselves. The speed is read when a plan is made: what is already moving keeps its own durations, so a change applies to the next build and never moves one that is playing. Persisted with the level under the same key (
pascal-construction-reveal); a person saved before it is at ×1.The level follows the project
The level that plays is the person's choice, or, with none of their own, Framing where the project has the plugin that draws the framing and Full where it has not. Nothing is stored for the default; it is read per project.
"Available" is one read: the kind
bones:framingis registered andisNodeKindEnabledsays its plugin is installed in the project. The editor never names a plugin id (on main it resolves topascal:procedural). Hosts readuseConstructionRevealLevel()/getConstructionRevealLevel()instead of the savedlevel. ASegmentedControloption can bedisabled(greyed, stays hoverable so a tooltip can say why).Walls keep their finish through the rise
A wall rises by scaling in Y, which squashed its textures while it moved and let a preset finish go stale. Now:
reveal-texture-scale.ts: the mesh draws a clone of its geometry, shared materials and cached geometry untouched; the clone goes when the reveal ends);This is the same rise Full plays, so Full gains it too.
The animation follows what is built, edited and removed
levelsRevealing); the reveal publishes the levels it works on. An undo plays back the kinds that came in with a default reveal.revealOnEdit); a commit that changes one replays the node's reveal. What the structure reconciler writes as a consequence of an edit (derivedWrites) is not an edit and does not replay. Many edits begin spread over one second (the cap spread over the pieces), not piled on its last frame.deletesprop.nodesRevealing); in cutaway the wall cutout cache keeps a wall visible for as long as it, an opening it hosts or a node it stands under plays, and goes back to what the view wants after. The person's wall mode is never written; walls down stays their choice. A deleted wall plays its deletion in its own picture (the wall's visible variant, as a capture draws it), not under the cutaway, unless that palette cannot draw the groups the copy carries (a room-wide paint preview was live): then the copy keeps what it wears.positionjoinsrevealOnEdit); a wall moved does not replay its windows.sceneViewFacinganswers for an exterior wall (the way its exterior side faces).systems/level/level-hold.ts, read byLevelSystemahead of the stacked elevation; presentation only, no scene write). Everything a held level holds goes with it (the roof, its Bones frame). A load, a capture, an export, the reveal stopping and an undo release the hold at once. A level object that has just mounted (a restored floor, a new storey, a load) now cuts every level to its height in the same frame instead of sliding about 250 ms, so an undo never runs a floor and its roof through each other.reveal-coverage.test.ts): every built-in kind an agent can create is staged by the reveal, on a level and on a roof, at the Full and Framing levels; a kind that declaresrevealOnEditlists only fields its schema has.Framing wears the Pro badge
The plugin that draws the framing is a Pro one on main (the Architect plugin pulls it in), so the Framing option wears the mark the plugin manager shows next to a plugin that needs a plan. The manager's
Badgebecomes a shared primitive used by both.For plugins
The reveal exposes what a plugin needs to stage its own members at the Framing level:
setRevealPrerollasks for a preroll before nodes of the lazy schedule move in; the schedule leaves the time and the node starts after it;getRevealTurnandrevealClockMsgive each node's turn on the reveal's clock (a preroll already playing keeps its turn when the plan is made again);getRevealPlanSpeedtells a plugin which speed the plan in force was made at;emitRevealPufflends the reveal's dust;commitRevealCandidates,planLazyReveal,LAZY_REVEAL_BASE_TIMING,LAZY_REVEAL_TIMING,pacedLazyTiming,FRAMING_PACEare exported from@pascal-app/viewer.Candidates carry their room (
cluster): the room a node stands in (for a node on a line, the room on either side of its middle), else runs of the lines that meet end to end. What is a room and what is a line is declared by the kind (capabilities.revealLayout: the zone and the wall declare it), so a plugin kind that declares the same is laid out the same way.Scope
packages/viewer(reveal schedule, candidates, pose, texture scale, wall material signature, construction-reveal system, level system, exports),packages/editor(the construction-reveal store, the two settings, the Pro badge,SegmentedControl's per-optiondisabled, Follow Pascal),packages/core(derivedWrites,revealOnEdit,exterior,revealLayout), andpackages/nodes(the wall batch's library-version watch, curtain-wall preset refs, each kind'srevealOnEditlist,exteriorandrevealLayoutdeclarations, the coverage guard). 99 files, +8,587 / -330, of which 1,090 lines are one JSON test fixture and ~3,800 are tests and fixtures. Core gains four optional registry fields (derivedWriteson a commit;revealOnEdit,exteriorandrevealLayouton a kind), nothing else; no mcp or app change. No schema, no migration.How to test
Architecture review (16 Oct)
A read-only architecture review (the editor's
review-architectureskill) found two blockers, both the same rule broken (DECISIONS.mdE-002: no kind-specific branching keyed onnode.typeinpackages/viewerorpackages/editor; the behaviour belongs on the kind's definition). Both are fixed in this PR; the review's suggestions and nits are not part of this change.follow-pascal-exterior.tsheld a hand-kept set of 22 kind names andnode.typebranches for wall, door, window, slab and the storey kinds. It now asks the registry: the new optionalcapabilities.exterior?: boolean | ((node, nodes) => boolean | undefined).truefor the roof, roof segment, every roof accessory, the fence and its gates, the site;falsefor level, building and zone; a function for the wall (an exterior face) and for the door and the window (in a roof segment, or in such a wall). The helper keeps its walk up the parents (atruekind outdoors, up to the firstfalseone) and its "stands in no storey" fallback. Six of the names were not kinds on main (ramp,rock,terrain-tile,terrain-break,facade, andfence-feature, whose real kinds arefence-gateandfence-opening) and a slab'sterrainModeis a field no schema has: they are gone, and no real node's answer changes. A plugin kind now says it stands outdoors the same way.parent.type === 'roof': it is now any ancestor whosecapabilities.reveal.styleisassemble(only the roof declares it, so the result is the same). (2) The deletion ghost of a wall the cutaway was hiding was dressed through anode.type === 'wall'check and the wall's own palette builder: the wall cutout (WallCutoutCache) now keeps the wall's visible palette on the mesh it cuts away (visibleMaterialsOf), and the ghost wears it, with thehasMaterialsForGroupsguard kept; the reveal names no wall kind. (3) The room-by-room layout needed "this node outlines a room" and "this node stands on a line": the new optionalcapabilities.revealLayout?: { room?, line? }, set by the zone (room: its polygon) and the wall (line: its two ends). Theas unknown as { start; end }casts are gone, and the plates and ceilings a deleted room or wall takes with it are those of any kind that declares arevealLayout.wiki/architecture/node-definitions.md(capability reference) with their doc comments inpackages/core/src/registry/types.ts.reveal-declared-kinds.test.ts,follow-pascal-exterior.test.ts); what the built-in kinds declare (exterior-declarations.test.ts); the cutout keeps the visible palette (wall-cutout-cache.test.ts).Bugbot (16 Oct)
history-control.ts): real. A derived write that set a value the node already had, deleted a node that was not there, or cancelled out inside a history step left its note in the module map, and the next real commit inherited it;applyNodeChangesActionImplnever notedchanges.delete. Notes are now taken only for the fields whose value changes and the nodes that exist, after the last early return; a no-op commit or transaction clears the map; deletes throughapplyNodeChangesare noted as the whole node. Six tests inderived-commit-nodes.test.ts(they fail before the fix).reveal-candidates.ts):layoutRunswalked an outline breadth-first, so a run of six was two stretches; it walks depth-first from a free end (open chain) or the first wall (closed outline), so a run is one stretch. Newreveal-candidates-runs.test.ts(3 tests).follow-pascal.tsx):outsideBuildwas read from the global edit queue at every word and kept for the session, so an exterior edit queued by another commit, or an exterior commit just before an interior one, took a build outdoors and wrote the walls up between its steps. Only an edit's own words (changed) read the queue now, a build or a deletion clears the latch, and the first edit after the reveal saidcompletestarts from false. Two tests infollow-pascal.test.tsx.nodesRevealing(construction-reveal.tsx): a ghost is out of the scene, so the cutaway could not find the wall a departing opening stood in. Each ghost keeps its host (the nearest node the commit leaves in the scene that is not a storey, building or site;CONTAINERSis exported fromreveal-candidates.ts) andnodesAtPlaypublishes it. Test "the wall an opening is leaving stays in play for as long as its picture plays".construction-animation-setting.tsx,use-construction-reveal.ts): the control shows the level that plays, so a saved Framing shows as Full while the plugin is gone, and a click on that pressed Full wrotechosen: true, level: 'full'over the kept Framing.chooseConstructionLevelnow ignores a click on the level that plays when the person has already chosen (the kept choice comes back with the plugin); any other level, or a click with no choice yet, is a choice as before. Tests: "clicking the level that plays" (3).Merged with main again on 16 Oct (#1001)
upstream/mainmoved tobb7d014c0(#1001: local plugins, surface stairs and deck railings) and merged in (ba4cc7323). Two content conflicts, innodes/src/fence/definition.tsandnodes/src/stair/definition.ts: main addedhostRefFieldsbeside this PR'srevealOnEditin the samecapabilitiesblock; both are kept. Every suite green after it (viewer 805, editor 2587, core 4955, nodes 4960, plugin-bones 1404, hosted 2682, typecheck 26/26; load timeouts green when rerun alone).Merged with main on 16 Oct
upstream/mainmoved to3dd378b8e(#1085 kitchen catalog and magnetic placement, #1061@types/three0.186, #1091 room-wide paint preview keeps walls raycastable) and merged in (d7e00a05a, a merge commit, no rebase). One content conflict, inpackages/viewer/src/systems/wall/wall-cutout-cache.ts:update()at the same spot: this PR's in-play stamps (wallsInPlay,playChanged) and main's loop that re-applies the walls a paint preview deferred. Both are kept. The in-play block runs first, sothis.inPlayis current when main's loop callsapply, which now reads it forhidden; then main's deferred loop;playChangedstill joinsinvalidatedand the per-wallapplycondition. Inapplythe branch'shiddenexpression and main'smayAssignguard aroundwall.mesh.material = …are both kept.wall-cutout-cache.test.ts("a wall in play while a room-wide preview holds its groups is stamped now and given its palette when the preview lets go"): the play starts under a preview that holds the wall's groups (stamp visible at once, preview kept), the preview lets go while it still plays (visible palette), the play ends (hidden palette and stamp). It fails if main's loop orplayChangedis dropped.construction-reveal/ghost-dress.ts) assigned the wall's visible palette to its copy without main's guard. It now assigns it only whenhasMaterialsForGroups(copy, palette)holds, so a deletion during a room-wide preview cannot leave a copy with a group and no material (a raycast over it would throw onmaterial.side). Test inghost-dress.test.ts; two tests that used a six-group box with a three-entry palette now give the mesh the one group a wall's geometry has (ghost-dress.test.ts,construction-reveal.test.tsx).@types/three0.186 raised no type error in this PR's code.Merge with main (15 Oct)
Main was merged in twice (
upstream/main683ce0c22thendcd304a30); the Framing work is unchanged by it except for what is listed here.SegmentedControl: main added a per-optiondisabledat the same time. Main's version is kept (isDisabled,opacity-40); the one difference is that the nativedisabledattribute stays the control's own, so a greyed option keepsaria-disabled, ignores its click and stays hoverable for its tooltip.roof-segment/definition.ts: main addedpaintandslots, this PRrevealOnEdit; both are kept. The list also gainsbarrelRise,barrelSegments,roofSystemandglazing(the fields main's barrel roofs and glazed roofs added), with a test.node-batch.test.tsandfollow-pascal.test.tsx: main's Biome 2.5 reformatted the tests this PR also edits; main's layout is taken and the PR's cases (revealing, the floors-above-shown assertion, the 1.5 s build) are kept. Three other test files are reformatted with Biome 2.5.follow-pascal.test.tsx(main'stoEqualis stricter).How it was checked
Run on the head (
f936603cd), with the dists rebuilt first (turbo build of the editor packages, 7 of 7 tasks),bun install --frozen-lockfileon the merged lockfile:packages/viewer:bun test src803 pass, 0 fail, Ran 803 tests across 90 files (with the held levels above a deleted floor: Full, Framing, speed, Off, undo mid-dismantle and after).packages/editor:bun test src2534 pass, 0 fail.packages/core:bun test src4939 pass, 0 fail.packages/nodes:bun test4997 pass, 1 skip, 0 fail; the rise test mounts real walls (solid and rendered shading), drives a construction reveal and checks each frame that the faces keep their materials and metre-scale UVs, then that the settled batch has the same UVs.reveal-add-volume.test.tsruns a frozen two-storey, three-room commit (__fixtures__/add-volume-commit.json): every wall, slab, room and roof goes through the timeline; walls are grouped by room; slabs first; at most two starts in a 60 fps frame; the build is never a pulse.turbo run check-types --continue: 11 of 13 tasks pass. Two fail, both main's:editor#check-types(8TS2307forreact-scan,geist/font/pixeland@webxr/plugin, not installed in the test tree; the same 8 as on a cleanupstream/main) and@pascal-app/editor#check-types(ceiling-bracket-batch.test.tsTS2578 and TS2352 on main'sthree/src/renderers/common/Attributes.jscast from [Deps] upgrade @types/three to 0.186 to match three r186 #1061; the file is not touched here, and a cleanupstream/maincheckout gives the same two errors and no other).node-batch.test.ts, from main).Limits
revealspolicy); a person's own edits stay instant.🤖 Generated with Claude Code