Refactoring: one file per node, consistent comments, shared helpers, split large modules

- Graph: nodes.ts split into nodes/<category>/<Type>.ts (62 files) with
  shared.ts factories and index.ts registry; NodeContext/values/NodeRegistry
  separated, writeTyped() replaces duplicated typed writes
- Comments: one style (JSDoc on non-obvious exports, // for decisions and
  traps), wrong/stale comments fixed or removed, ids.ts now real 10 base-36 chars
- Core: chainOf, shared worldPosition/editorPosition core, commands/require.ts,
  commands/naming.ts, util/list.ts, setBagValue/toPosition/propDef; dead code
  removed (isBehaviorAsset, SCENE_OBJECT_SCHEMA, CommandStack.subscribe,
  Input.axis, setMode, titleText, World.worldPosition, unused ctor params)
- Editor: Inspector split into inspector/ sections, App.ts into Editor.ts +
  debugHook.ts + shortcuts.ts, Viewport into items.ts + symbols.ts, shared
  "Add collider" action, unified type labels/swatches
- UI toolkit: Widget.trackHover/onPointerRelease, drawControlBox, math helpers,
  Popup.openBelow, Menu/Dialog/ColorPicker/ListRow in own files, named Theme sizes
- Tools: seed-demo/smoke-demo and demo-check project removed, tools/lib
  (harness, projects, builders), e2e.mjs as runner over tools/e2e/*.mjs sections
- Tests: tests/helpers.ts replaces copied fixtures and NodeContext doubles

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-08 08:37:22 +02:00
1 parent 3234eb480f
commit 1eb28314cf
189 files changed
+6231 -5543

No files matched your search

+23 -31
View File
@@ -1,38 +1,28 @@
import { describe, expect, it } from 'vitest';
import { CreateAssetCommand, SetAssetPropertyCommand } from '../../src/core/commands/assetCommands';
import { CommandStack } from '../../src/core/commands/CommandStack';
import { ClearOverrideCommand, PlaceAssetCommand, RemoveSceneObjectCommand, SetOverrideCommand, SetParentCommand } from '../../src/core/commands/sceneObjectCommands';
import { createEmptyProject, findScene } from '../../src/core/model/Project';
import { deserializeProject, serializeProject } from '../../src/core/store/serialize';
import { SetAssetPropertyCommand } from '../../src/core/commands/assetCommands';
import { ClearOverrideCommand, RemoveSceneObjectCommand, SetOverrideCommand, SetParentCommand } from '../../src/core/commands/sceneObjectCommands';
import { findScene } from '../../src/core/model/Project';
import { World } from '../../src/runtime/World';
import { makeSpriteScene, place, roundTrip, startWorld } from '../helpers';
/** A scene with a 100×100 box asset and two placed boxes. */
/** Two boxes: a at the origin and b at (300,0). */
function setup() {
const project = createEmptyProject('p', 'Sync');
const stack = new CommandStack(project);
const scene = new CreateAssetCommand('scene', 'Level');
const sprite = new CreateAssetCommand('sprite', 'Box');
stack.execute(scene);
stack.execute(sprite);
const a = new PlaceAssetCommand(scene.asset.id, sprite.asset.id, { x: 0, y: 0 });
const b = new PlaceAssetCommand(scene.asset.id, sprite.asset.id, { x: 300, y: 0 });
stack.execute(a);
stack.execute(b);
const sceneOf = () => findScene(project, scene.asset.id)!;
const object = (id: string) => sceneOf().objects.find((o) => o.id === id)!;
return { project, stack, sceneId: scene.asset.id, spriteId: sprite.asset.id, a: a.objectId, b: b.objectId, sceneOf, object };
const f = makeSpriteScene();
const a = place(f, f.spriteId, { x: 0, y: 0 });
const b = place(f, f.spriteId, { x: 300, y: 0 });
return { ...f, a, b };
}
describe('world ↔ model synchronisation (editing while playing)', () => {
it('syncFromModel picks up placed, moved, edited, re-parented and removed objects', () => {
const { project, stack, sceneId, spriteId, a, b, object } = setup();
const world = World.instantiate(project, sceneId);
const f = setup();
const { stack, sceneId, spriteId, a, b, object } = f;
const { world } = startWorld(f);
expect(world.entities.map((e) => e.objectId)).toEqual([a, b]);
// Placed while playing → new entity, existing ones keep their identity.
const entityA = world.findByObjectId(a)!;
const c = new PlaceAssetCommand(sceneId, spriteId, { x: 50, y: 50 });
stack.execute(c);
const c = place(f, spriteId, { x: 50, y: 50 });
world.syncFromModel();
expect(world.entities).toHaveLength(3);
expect(world.findByObjectId(a)).toBe(entityA);
@@ -50,14 +40,15 @@ describe('world ↔ model synchronisation (editing while playing)', () => {
expect(world.findByObjectId(b)!.x).toBe(object(b).position.x);
// Removed while playing → entity gone.
stack.execute(new RemoveSceneObjectCommand(sceneId, c.objectId));
stack.execute(new RemoveSceneObjectCommand(sceneId, c));
world.syncFromModel();
expect(world.entities.map((e) => e.objectId)).toEqual([a, b]);
});
it('syncToModel writes positions, changed values as overrides and destroyed entities back', () => {
const { project, sceneId, a, b, object, sceneOf } = setup();
const world = World.instantiate(project, sceneId);
const f = setup();
const { project, sceneId, a, b, object, sceneOf } = f;
const { world } = startWorld(f);
const entityA = world.findByObjectId(a)!;
entityA.x = 123;
entityA.props.width = 999; // what a Set property node would do
@@ -83,8 +74,9 @@ describe('world ↔ model synchronisation (editing while playing)', () => {
});
it('spawn adds an object to the runtime scene and an entity at once, without resetting others', () => {
const { project, sceneId, spriteId, a, sceneOf } = setup();
const world = World.instantiate(project, sceneId);
const f = setup();
const { sceneId, spriteId, a, sceneOf } = f;
const { world } = startWorld(f);
const entityA = world.findByObjectId(a)!;
entityA.x = 77; // moved earlier in this tick – a spawn must not pull the model value back
@@ -118,14 +110,14 @@ describe('world ↔ model synchronisation (editing while playing)', () => {
});
it('a play-mode copy leaves the original model untouched', () => {
const { project, sceneId, a, object } = setup();
const copy = deserializeProject(serializeProject(project), project.id);
const { project, sceneId, a, object, sceneOf } = setup();
const copy = roundTrip(project);
const world = World.instantiate(copy, sceneId);
world.findByObjectId(a)!.x = 500;
world.remove(world.findByObjectId(a)!);
world.syncToModel();
expect(findScene(copy, sceneId)!.objects).toHaveLength(1);
expect(object(a).position.x).toBe(0);
expect(findScene(project, sceneId)!.objects).toHaveLength(2);
expect(sceneOf().objects).toHaveLength(2);
});
});