Trim needless comments and fix the picker width

Remove comments that only restate the code, keeping the section banners
and the notes that carry a reason, a measured fact or an external
contract.

ButtonOptions.width claimed to be a fixed width but only set minWidth,
so the Graph and Texture pickers grew with the name they showed. It is
now what it promises, with a separate minWidth for the buttons that have
two forms. Narrow buttons shrink their padding, otherwise a 24px icon
button had 8px left for its glyph and truncate dropped it entirely.

__gm.labelsDropped() guards that class: the overflow guard only saw text
that was too wide, never a label truncated to nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5 committed 2026-09-08 15:52:05 +02:00
1 parent f16801aef3
commit 4d84231b9f
115 files changed
+707 -1564

No files matched your search

+14 -30
View File
@@ -4,13 +4,8 @@ import type { SceneObject } from './SceneObject';
import { descendantsBelow, parentChain, sumAlongChain } from './walk';
/**
* Parent/child structure of a scene. `scene.objects` stays a flat list (storage order =
* creation order); the tree is implied by `SceneObject.parentId`. Positions are local to
* the parent, so the world position is the sum along the chain – there is no rotation or
* scale yet, which keeps this trivially invertible.
*
* The helpers work on anything that holds a flat object list – a scene, or the template
* inside a container asset.
* `objects` stays a flat list in creation order; the tree is implied by `SceneObject.parentId`.
* Anything holding such a list fits – a scene, or the template inside a container asset.
*/
export interface ObjectTree {
objects: SceneObject[];
@@ -24,12 +19,11 @@ function childrenOf(tree: ObjectTree, parentId: string): SceneObject[] {
return tree.objects.filter((o) => o.parentId === parentId);
}
/** The parent of an object, or undefined for a top-level one (and for a dangling link). */
function parentOf(tree: ObjectTree, object: SceneObject): SceneObject | undefined {
return object.parentId ? findObject(tree, object.parentId) : undefined;
}
/** All objects below `id`, depth-first, parents before children. */
/** Depth-first, parents before children. */
export function descendantsOf(tree: ObjectTree, id: string): SceneObject[] {
return descendantsBelow(
id,
@@ -38,7 +32,6 @@ export function descendantsOf(tree: ObjectTree, id: string): SceneObject[] {
);
}
/** The ids of `rootIds` and of everything below them – what group operations act on. */
export function subtreeIds(tree: ObjectTree, rootIds: Iterable<string>): Set<string> {
const ids = new Set<string>();
for (const rootId of rootIds) {
@@ -48,18 +41,18 @@ export function subtreeIds(tree: ObjectTree, rootIds: Iterable<string>): Set<str
return ids;
}
/** Takes the subtrees of `rootIds` out of the tree; returns them with their former indices (see `restoreAt`). */
/** Returns the removed objects with their former indices (see `restoreAt`). */
export function removeSubtrees(tree: ObjectTree, rootIds: readonly string[]): Removed<SceneObject>[] {
const ids = subtreeIds(tree, rootIds);
return removeWhere(tree.objects, (o) => ids.has(o.id));
}
/** An object and every parent above it, nearest first. Stops at a cycle (defensive). */
/** An object and every parent above it, nearest first. */
export function chainOf(tree: ObjectTree, object: SceneObject): SceneObject[] {
return [...parentChain(object, (o) => parentOf(tree, o))];
}
/** Ids of the ancestors of `id`, nearest parent first. Empty for a top-level object. */
/** Nearest parent first. */
export function ancestorIdsOf(tree: ObjectTree, id: string): string[] {
const object = findObject(tree, id);
return object
@@ -69,16 +62,12 @@ export function ancestorIdsOf(tree: ObjectTree, id: string): string[] {
: [];
}
/** Whether `ancestorId` is `objectId` itself or one of its ancestors. */
export function isSelfOrAncestor(tree: ObjectTree, ancestorId: string, objectId: string): boolean {
const object = findObject(tree, objectId);
return object !== undefined && chainOf(tree, object).some((o) => o.id === ancestorId);
}
/**
* Of the given ids, those that have no ancestor among the ids themselves – the tops of the
* selected subtrees. Whole subtrees are what group operations (move, remove, group) act on.
*/
/** Of the given ids, those with no ancestor among the ids themselves. */
export function subtreeRoots(tree: ObjectTree, ids: readonly string[]): string[] {
const set = new Set(ids);
return ids.filter((id) => !ancestorIdsOf(tree, id).some((ancestorId) => set.has(ancestorId)));
@@ -95,15 +84,13 @@ function positionAlongChain(tree: ObjectTree, object: SceneObject, withEditorOff
);
}
/** Position of an object in world units (all local positions along the chain summed up). */
export function worldPosition(tree: ObjectTree, object: SceneObject): Position {
return positionAlongChain(tree, object, false);
}
/**
* Where the scene view draws an object: its world position plus the editor offsets along the
* chain, so shifting a parent shifts everything below it. This is a view aid only – nothing
* in the game, in collisions or in the graph ever sees it (they all use `worldPosition`).
* World position plus the editor offsets along the chain. A view aid only – the game, collisions
* and the graph all use `worldPosition`.
*/
export function editorPosition(tree: ObjectTree, object: SceneObject): Position {
return positionAlongChain(tree, object, true);
@@ -123,8 +110,8 @@ export interface TreeEntry {
}
/**
* Objects in tree order: top-level objects in storage order, each followed by its subtree.
* This is the order the hierarchy panel shows and the tie-break order for equal `z`.
* Top-level objects in storage order, each followed by its subtree – the order the hierarchy panel
* shows and the tie-break order for equal `z`.
*/
export function treeOrder(tree: ObjectTree): TreeEntry[] {
const result: TreeEntry[] = [];
@@ -137,7 +124,7 @@ export function treeOrder(tree: ObjectTree): TreeEntry[] {
}
};
visit('', 0);
// Objects whose parent is missing (should not happen after loading) are listed at the top level.
// Objects whose parent is missing are listed at the top level rather than dropped.
for (const object of tree.objects) {
if (object.parentId && !ids.has(object.parentId) && !result.some((e) => e.object === object))
result.push({ object, depth: 0 });
@@ -146,15 +133,12 @@ export function treeOrder(tree: ObjectTree): TreeEntry[] {
}
export interface VisibleEntry extends TreeEntry {
/** Whether the object has children at all – only those rows offer an expand/collapse chevron. */
hasChildren: boolean;
}
/**
* Tree order with the subtrees of collapsed objects left out: a row is shown when *every*
* ancestor is expanded. Expansion is a view state (which ids are open), never stored in the
* project – anything not listed counts as collapsed, so a freshly loaded scene shows only its
* top level.
* Tree order with the subtrees of collapsed objects left out. Anything not in `expanded` counts as
* collapsed, so a freshly loaded scene shows only its top level.
*/
export function visibleTreeOrder(tree: ObjectTree, expanded: ReadonlySet<string>): VisibleEntry[] {
const parents = new Set(tree.objects.map((o) => o.parentId).filter(Boolean));