Number fields derived their display precision from `step`, so `step: 1`
(X/Y/Z, width, height, font size) rendered a stored 100.234 as "100" — and
because `onChange` parses the text, touching the field wrote the rounded
value back into the project. `formatNumber` now renders every number with
three decimals, independent of `step`, which is left to control the scrub
and arrow-key increment only.
Dragging now snaps to the increment's grid (`snapToStep`), so a scrub at
`step: 1` always lands on whole numbers even from a fractional start, while
Alt (x0.1) and the 0.1-step fields keep their precision. Typed values and
arrow keys are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Menu entries are left-aligned and live in a ScrollContainer capped at 320 px, so a
long list (the parent picker holds every object of the scene) scrolls instead of
running off the screen. `Menu(items, { searchable: true })` puts a filter box above
the scroll area; Enter takes the first match. Filtering forces a relayout because
two queries with the same hit count leave the height unchanged, which would keep
the stale row positions.
Fields that open a menu are a new `Dropdown` widget: the value sits flush left and
the chevron is drawn flush right instead of being written into the label. A menu
opened below a field is never narrower than that field.
New debug hooks `overlayTexts()` / `overlayRect()` make the popup contents
assertable; e2e step 11 covers the cap, the scrolling and the search box.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tooling:
- Prettier as the single formatting authority (110 columns, single quotes,
measured against the existing style); Markdown excluded because table-cell
padding turns the README glossary into 1000-character lines.
- Type-aware ESLint ending in eslint-config-prettier, so a rule always means
"this is a bug", never "this looks different".
- .editorconfig and an `npm run check` that runs typecheck, lint, format and
tests in one go.
Findings fixed on the first run: three more duplicate imports, four
String(unknown) calls (parameter types narrowed instead of casts), two dead
initialisers, two redundant casts, a console.log for a real error. Two
warnings were false here and are disabled with a reason in place.
Tests 132 -> 269:
- node-catalog.test.ts covers every node with a run(), with a guard that fails
when a new node has no row.
- behavior-events.test.ts covers the ten event nodes through a running World,
checking timing rather than existence.
- Input's state machine (press/release/releaseAll) is reachable without DOM
events now; the DOM handlers are thin adapters.
Compare fell back to == without a configured operator although the schema
default is <; aligned.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- SceneObject.editorOffset (x/y): shifts only where an object is drawn in
the Scene tab, cascades to children, undo-able with a reset action.
Never written into container templates.
- On click without an owner now fires for any click inside the camera
frame instead of hit-testing the (editor-only) behavior symbol.
- seed-jumprun: coins are a "Coin" container (sprite + trigger), nested
containers are expanded recursively, Destroy targets the coin's parent,
and the clock uses a Ceil node instead of ToText.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Inspector rows compute their reset/apply entries through providers, so an
entry that would not change anything is dropped; a single remaining entry
renders as a direct button instead of a menu, none removes it entirely.
- Bold and buttons now follow the same rule (deviatesFromAsset /
deviatesFromTemplate in core/properties/resolve.ts).
- Hierarchy panel is a collapsible tree: chevrons, arrow keys, double click,
expand/collapse all. Everything starts collapsed; selecting an object
expands its ancestors and scrolls it into view.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Engine additions that the example game needed:
- `enabled` as a shared property of every placeable asset (default on, an
override on the scene object). A switched-off object is not drawn, does not
collide and its behavior does not run - and neither does anything parented
below it. Disabled objects are now hidden in the scene view as well, so the
editor shows what the game shows; they stay in the hierarchy panel, which is
where they are selected and switched back on.
- Asset type `text` (text, fontSize, color, align), rendered in scene and game.
- Pointer input in the game view: `On click`, `Mouse position`, `Mouse button`,
`Is hovered`, hit-tested against colliders, else sprite/text footprints.
- New nodes: Set/Is enabled, Set text, Is asset, Name of, Equals, To text,
Join text, Floor/Ceil/Round, Modulo, Random, Time, Restart game.
Tooling: shared CDP harness (tools/harness.mjs), central project registry with
a guard that keeps destructive scripts off the playground (tools/projects.mjs),
plus smoke suites for the demo scene and the jump-and-run game.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Move with "Collide with solids" now steps in sub-steps of at most half the
thinnest collider, so a fast step can no longer jump across a collider (the
crates are only 60 high, which is why it showed up mostly on the y axis). A
pre-existing overlap is still never resolved, but it can no longer be
deepened either, so a small overlap cannot open a wall permanently.
seed-demo.mjs now writes the saved demo scene: a "Player" container (green
sprite with collider and the Mover behavior) against two "Enemy" container
instances, camera at zoom 0.5, Spawner dropping another enemy on Space.
The e2e checks assert against the old two-crate layout and locate widgets by
text, which the demo can no longer provide (three identically named sprite
instances, both crates nested in containers). Its fixture therefore moves to
tools/seed-e2e.mjs so the shipped demo can evolve without rewriting 100+
checks: seed-e2e -> e2e -> seed-demo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Collider property isTrigger (no blocking, still reports enter/stay/exit)
- Node 'Move and slide' with blocked x/y outputs, axis-wise resolution
- axisSeparation/axisCorrection geometry for box/box, circle/circle, box/circle
- Amber outline for trigger colliders in the viewport
- Demo mover uses Move and slide; E2E step 15b, 7 new unit tests
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Asset type 'collider' (box/circle) placed as child of a body object
- 'Add collider' shortcut (hierarchy context menu + inspector)
- CollisionSystem with enter/stay/exit pairs, sibling colliders excluded
- Nodes: On collision enter/stay/exit, Is touching
- Green outline in edit mode, hidden in play
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Position is now { x, y, z }; higher z draws on top, ties keep hierarchy order
- Viewport sorts the objects layer deterministically (syncDrawOrder)
- Inspector shows a Z field; asset properties that differ from the schema
default are marked bold with a reset button (ResetAssetPropertyCommand)
- isModified() helper, unit tests, E2E checks and seed updated
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Camera assets get width/height properties; play mode renders only the
camera frame (letterboxed, masked, culled) and nothing without a camera
- Remove demo arrow-key/camera-follow systems from play mode
- Clip world and gizmos to the viewport (selection frame no longer leaks
over the left panel)
- Blur focused widget on pointerdown outside it so inspector edits commit
before selection changes
- Separate objects/overlay layers so camera symbols always render on top
- Add tools/seed-demo.mjs for reproducible E2E runs
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Canvas-only 2D game editor/runtime built on PixiJS v8 + Vite + TypeScript.
- core: asset model (Scene/Camera/Sprite), scene objects with sparse
overrides, property schema + resolve, command stack (undo/redo),
serialization and project store interface (16 unit tests)
- ui: custom in-canvas widget toolkit (panels, fields, color picker,
list/scroll, popup) with a hidden <input> keyboard bridge
- editor: asset/hierarchy panels, schema-driven inspector, toolbar
- viewport: box rendering, camera frame, selection gizmo, drag/zoom/pan
- runtime: world/entity instantiation for play mode, input, camera follow
- api: PHP JSON project store with revision counter (409 on conflict)
- tools: CDP-based e2e smoke test
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>