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 -52
View File
@@ -2,16 +2,11 @@ import type { Asset } from '../model/Asset';
import { type AssetType, isPlaceableType } from '../model/AssetTypes';
import type { PropertyBag, PropertyValue } from '../model/Properties';
/**
* Declarative description of one editable property. The inspector is generated from
* these entries, so adding a property to a type is a single line in a schema below.
*/
/** The inspector is generated from these entries, so a new property is one line in a schema below. */
export type PropertyDef =
/**
* `decimals` is the number of places the field displays; it belongs only on properties that
* are integral by their meaning (counts, indices). Geometry does not qualify: dragging in the
* viewport puts fractions into a position or a size, and a field that hid them would commit
* the rounded number back into the project.
* `decimals` belongs only on properties that are integral by meaning (counts, indices), not on
* geometry: dragging produces fractions, and a field that hid them would commit the rounded value.
*/
| {
key: string;
@@ -26,40 +21,28 @@ export type PropertyDef =
| { key: string; label: string; kind: 'text'; default: string }
| { key: string; label: string; kind: 'color'; default: number }
| { key: string; label: string; kind: 'boolean'; default: boolean }
/** One of a fixed set of string values, shown as a dropdown. */
| { key: string; label: string; kind: 'enum'; default: string; options: { value: string; label: string }[] }
/** A keyboard key name (`KeyboardEvent.key`), captured by pressing it. */
| { key: string; label: string; kind: 'key'; default: string }
/**
* A reference to another asset, stored as its id (`''` = none) and picked from a
* searchable dropdown. `assetTypes` limits what the picker offers; without it every
* placeable asset is offered, which is what graph nodes acting on an object want.
*
* The id is not validated on load – a reference can dangle, exactly like a graph node's
* asset parameter, and the places that read one have to cope with that.
* An asset id (`''` = none); without `assetTypes` the picker offers every placeable asset.
* Ids are not validated on load, so a reference can dangle and readers must cope.
*/
| { key: string; label: string; kind: 'asset'; default: string; assetTypes?: readonly AssetType[] }
/**
* A picture: the id of a texture asset (the whole image) or of one of its frames. Its own
* kind rather than an `asset` reference with `assetTypes: ['texture']`, because a frame is
* not an asset – it is a rectangle inside one, and the picker has to offer both.
*
* Like an asset reference the id is not validated on load and may dangle; everything that
* reads one falls back to drawing no picture.
* The id of a texture asset or of one of its frames. Its own kind rather than an `asset`
* reference, because a frame is not an asset and the picker has to offer both. May dangle.
*/
| { key: string; label: string; kind: 'texture'; default: string };
export type PropertySchema = readonly PropertyDef[];
/** Kinds a behavior variable may have (what the node graph can compute with). */
export const VARIABLE_KINDS = ['number', 'boolean', 'text'] as const;
export type VariableKind = (typeof VARIABLE_KINDS)[number];
/**
* Properties every *placeable* asset has, whatever its type. `enabled` is the master switch:
* a disabled object is not drawn, does not collide and its behavior does not run – and the
* same goes for everything parented below it. Assets default to on, so switching a single
* instance off is an ordinary override. Scenes have no such switch (they are not placed).
* Properties every placeable asset has. A disabled object is not drawn, does not collide and its
* behavior does not run – and the same goes for everything parented below it.
*/
export const COMMON_PROPS: PropertySchema = [
{ key: 'enabled', label: 'Enabled', kind: 'boolean', default: true },
@@ -67,12 +50,11 @@ export const COMMON_PROPS: PropertySchema = [
const COMMON_KEYS = new Set(COMMON_PROPS.map((d) => d.key));
/** Whether a key belongs to the common properties (the inspector groups those separately). */
export function isCommonProp(key: string): boolean {
return COMMON_KEYS.has(key);
}
/** Property schema per asset type. Position is deliberately absent: it belongs to scene objects. */
/** Position is deliberately absent: it belongs to scene objects, not to asset types. */
const ASSET_SCHEMAS: Record<AssetType, PropertySchema> = {
scene: [{ key: 'backgroundColor', label: 'Background', kind: 'color', default: 0x2a2d34 }],
camera: [
@@ -83,20 +65,11 @@ const ASSET_SCHEMAS: Record<AssetType, PropertySchema> = {
sprite: [
{ key: 'width', label: 'Width', kind: 'number', default: 100, min: 1, step: 1 },
{ key: 'height', label: 'Height', kind: 'number', default: 100, min: 1, step: 1 },
/*
* The colour of the sprite: the fill of the box while it has no picture, and a multiplier
* on the picture once it has one. White therefore means "no tint" – which is why it is the
* default. A coloured default would silently stain every texture the moment it is applied,
* and the first thing anyone would do is set it back to white.
*
* Tinting is worth keeping rather than suppressing for textured sprites: it is how one
* white coin picture becomes ten differently coloured coins, per instance, for free.
*/
// The fill while there is no picture, and a tint multiplier once there is one: white = no tint.
{ key: 'color', label: 'Color', kind: 'color', default: 0xffffff },
{ key: 'texture', label: 'Texture', kind: 'texture', default: '' },
],
// The picture itself is intrinsic data on the asset (see TextureAsset), not a property:
// only how it is sampled is worth deciding, and that decision is the whole schema.
// The picture itself is intrinsic data on the asset (see TextureAsset), not a property.
texture: [
{
key: 'filter',
@@ -125,7 +98,6 @@ const ASSET_SCHEMAS: Record<AssetType, PropertySchema> = {
{ key: 'width', label: 'Width', kind: 'number', default: 100, min: 1, step: 1 },
{ key: 'height', label: 'Height', kind: 'number', default: 100, min: 1, step: 1 },
{ key: 'radius', label: 'Radius', kind: 'number', default: 50, min: 1, step: 1 },
// Triggers report collisions but never block a colliding `Move`; solid colliders do both.
{ key: 'isTrigger', label: 'Trigger (no blocking)', kind: 'boolean', default: false },
],
// A container is pure structure: its content is the template, not a property.
@@ -148,11 +120,6 @@ const ASSET_SCHEMAS: Record<AssetType, PropertySchema> = {
],
};
/**
* Type schema with the common properties in front – the effective schema of an asset type.
* Only placeable types get them: `enabled` switches off a placed object, and a scene or a
* texture is never placed, so the switch would have nothing to act on.
*/
const FULL_SCHEMAS: Record<AssetType, PropertySchema> = Object.fromEntries(
(Object.keys(ASSET_SCHEMAS) as AssetType[]).map((type) => [
type,
@@ -160,17 +127,15 @@ const FULL_SCHEMAS: Record<AssetType, PropertySchema> = Object.fromEntries(
]),
) as Record<AssetType, PropertySchema>;
/** Schema shared by every asset of a type (common properties first). */
export function schemaFor(type: AssetType): PropertySchema {
return FULL_SCHEMAS[type];
}
/** The effective schema of one asset: the type schema, plus declared variables for behaviors. */
/** The type schema, or for behaviors their declared variables. */
export function schemaForAsset(asset: Asset): PropertySchema {
return asset.type === 'behavior' ? [...COMMON_PROPS, ...asset.variables] : FULL_SCHEMAS[asset.type];
}
/** The definition of `key` in the effective schema of `asset`, if it has one. */
export function propDef(asset: Asset, key: string): PropertyDef | undefined {
return schemaForAsset(asset).find((d) => d.key === key);
}
@@ -186,7 +151,6 @@ export function defaultsOf(schema: PropertySchema): PropertyBag {
return bag;
}
/** Builds a variable definition of the given kind with the kind's natural default. */
export function variableDef(key: string, kind: VariableKind, defaultValue?: PropertyValue): PropertyDef {
switch (kind) {
case 'number':
@@ -198,7 +162,7 @@ export function variableDef(key: string, kind: VariableKind, defaultValue?: Prop
}
}
/** Clamp / coerce a raw value so it satisfies the definition. */
/** Clamps / coerces a raw value so it satisfies the definition. */
export function coerceValue(def: PropertyDef, value: PropertyValue): PropertyValue {
switch (def.kind) {
case 'number': {
@@ -214,8 +178,6 @@ export function coerceValue(def: PropertyDef, value: PropertyValue): PropertyVal
}
case 'boolean':
return Boolean(value);
// An asset or texture reference is an id like the other two are plain strings; whether it
// still resolves is not decidable from a single property definition.
case 'text':
case 'key':
case 'asset':