- `.gitignore` said `index.html` unanchored, which also caught the Vite source page; a fresh clone could neither `dev` nor `build`. Anchored to `/index.html`, page added. - Removed GM_REMOTE (tools/remote/gm-browser.ps1 and the ssh/scp branch in browser.mjs): the Windows test machine is gone and the only flaky check that lived on it is closed (#37). - Removed `npm run preview`, undocumented and unused. - Comments: wrong number in collider.test.ts (250 → 200), the "declarations used to…" history in listItems.ts and friends, the 24 px/28 contradiction, section banners in nodes/index.ts, node-catalog.test.ts and api/index.php, name paraphrases, and the stale step numbers in tools/e2e/11-…23- from the old global count. - README: asset types include `animation`, kind order Number/Text/Boolean, tagged colour and texture items, 6/5 kinds, the `Texture` node, `./build.sh`, PROJECT_FORMAT_VERSION, and the file tree (kinds.ts, listItems.ts, propertyPorts.ts, Breadcrumb.ts, beads.ts, tools/assets/). - tests/helpers.ts takes the duplicated `findObject` and `sceneWithCamera`. Proof: ./check.sh 797 tests, full e2e ALL PASSED 633 checks against this bundle. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
import { type Asset, type ContainerAsset, isSceneAsset, type SceneAsset } from './Asset';
|
|
|
|
/** 2: a placed container carries no copy of its template. */
|
|
export const PROJECT_FORMAT_VERSION = 2;
|
|
|
|
export interface Project {
|
|
formatVersion: number;
|
|
id: string;
|
|
name: string;
|
|
assets: Asset[];
|
|
/** Empty string = none. */
|
|
activeSceneId: string;
|
|
}
|
|
|
|
export function createEmptyProject(id: string, name: string): Project {
|
|
return { formatVersion: PROJECT_FORMAT_VERSION, id, name, assets: [], activeSceneId: '' };
|
|
}
|
|
|
|
export function findAsset(project: Project, assetId: string): Asset | undefined {
|
|
return project.assets.find((a) => a.id === assetId);
|
|
}
|
|
|
|
export function findAssetOf<T extends Asset['type']>(
|
|
project: Project,
|
|
assetId: string,
|
|
type: T,
|
|
): Extract<Asset, { type: T }> | undefined {
|
|
const asset = findAsset(project, assetId);
|
|
return asset?.type === type ? (asset as Extract<Asset, { type: T }>) : undefined;
|
|
}
|
|
|
|
export function findScene(project: Project, sceneId: string): SceneAsset | undefined {
|
|
const asset = findAsset(project, sceneId);
|
|
return asset && isSceneAsset(asset) ? asset : undefined;
|
|
}
|
|
|
|
export function listScenes(project: Project): SceneAsset[] {
|
|
return project.assets.filter(isSceneAsset);
|
|
}
|
|
|
|
/**
|
|
* Everything that holds placed objects. A container's `objects` are a template, so anything
|
|
* walking "all placed objects" must cover both or it silently skips the templates.
|
|
*/
|
|
export type ObjectHolder = SceneAsset | ContainerAsset;
|
|
|
|
export function isObjectHolder(asset: Asset): asset is ObjectHolder {
|
|
return asset.type === 'scene' || asset.type === 'container';
|
|
}
|
|
|
|
export function objectHolders(project: Project): ObjectHolder[] {
|
|
return project.assets.filter(isObjectHolder);
|
|
}
|
|
|
|
/**
|
|
* The scene or container asset an object belongs to. Editing a container's template is the same
|
|
* work as editing a scene, so the object commands resolve their target through this rather than
|
|
* through `findScene`.
|
|
*/
|
|
export function findObjectHolder(project: Project, holderId: string): ObjectHolder | undefined {
|
|
const asset = findAsset(project, holderId);
|
|
return asset && isObjectHolder(asset) ? asset : undefined;
|
|
}
|