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:
1 parent
3234eb480f
commit
1eb28314cf
189 files changed
+6231
-5543
No files matched your search
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user