Files
feuerwerk/src/editor/debug/vectorArtHooks.ts
T
adminandClaude Opus 5.5 ed29b2f3dc Split the debug hook into one file per topic (#215)
debugHook.ts had grown to 1097 lines of unrelated test hooks. They
now live in seven files under src/editor/debug/, grouped by topic.
debugHook.ts only assembles them and is down to 86 lines.

- The shape of window.__gm is unchanged for tools/. Measured in the
  browser before and after: 120 top-level keys and 313 leaves with
  their arity, byte-identical JSON.
- The Text import is explicit in the helpers. Without it the name
  fell back to the DOM type and instanceof would never have matched.

Findings:
- The comment for vectorArt sat above pixelArt. It is back in place.
- An orphaned duplicate comment on assetRow is gone.

Tests:
- ./check.sh: 145 files, 2192 tests, CHECK EXIT 0 (unchanged)
- e2e 1-16,37,39-43,47,48: ALL PASSED, 734 checks
- The shape probe turns red against a deliberately misnested hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 10:20:06 +02:00

81 lines
4.1 KiB
TypeScript

import { findAsset } from '../../core/model/Project';
import { currentGradientBar, currentShapeList } from '../inspector/shapeSection';
import { centreOf, snapButton, type HookContext } from './hookHelpers';
/** The Vector art tab (#129). */
export function vectorArtHooks({ state, editor }: HookContext) {
const { vectorArtEditor } = editor;
return {
/** The Vector art tab (#129, stage 3): where a canvas pixel is, and which tool a press uses. */
vectorArt: {
current: () => vectorArtEditor.currentAssetId,
info: () => vectorArtEditor.shownInfo,
pointAt: (x: number, y: number) => vectorArtEditor.canvasToScreen(x, y),
rect: () => vectorArtEditor.canvasRect(),
tool: () => vectorArtEditor.canvas.currentTool,
snapping: () => vectorArtEditor.canvas.snapping,
snapButton: () => snapButton(vectorArtEditor.snap),
/** Whether the pen has a line open that the next click would add a point to. */
drawing: () => vectorArtEditor.canvas.drawing,
/** The header's point type (#195): where to click it, what it says, and the type it reads. */
pointButton: () => ({
...centreOf(vectorArtEditor.pointButton),
text: vectorArtEditor.pointButton.text,
enabled: vectorArtEditor.pointButton.enabled,
kind: vectorArtEditor.canvas.pickedKind,
}),
/** The shapes as the model holds them, so a check can read points and handles exactly. */
shapes: (assetId: string) => {
const asset = findAsset(state.project, assetId);
if (!asset || asset.type !== 'vectorart') return null;
return asset.shapes.map((shape) => ({
id: shape.id,
name: shape.name,
fill: shape.fill,
opacity: shape.opacity,
stroke: shape.stroke ? (JSON.parse(JSON.stringify(shape.stroke)) as unknown) : null,
paint: shape.paint ? (JSON.parse(JSON.stringify(shape.paint)) as unknown) : null,
rings: shape.rings.map((ring) => ({
closed: ring.closed,
points: ring.points.map((point) => ({
x: point.x,
y: point.y,
in: point.in ? { ...point.in } : null,
out: point.out ? { ...point.out } : null,
mode: point.mode ?? null,
})),
})),
}));
},
selected: () => (state.selection?.kind === 'shape' ? state.selection.shapeId : ''),
selectedIds: () => state.selectedShapeIds,
/**
* The shape list in the inspector (#129, stage 4): the order it shows, top first, and where a
* row sits. A row carries a name that also stands in the tab, so a check cannot click it by text.
*/
shapeRows: () => currentShapeList()?.shownIds ?? null,
shapeRowAt: (shapeId: string) => currentShapeList()?.rowGlobal(shapeId) ?? null,
/** The cross on a shape's row. */
shapeRemoveAt: (shapeId: string) => currentShapeList()?.removeGlobal(shapeId) ?? null,
/** The duplicate mark on a shape's row, and where a double-click edits its name. */
shapeDuplicateAt: (shapeId: string) => currentShapeList()?.duplicateGlobal(shapeId) ?? null,
shapeNameAt: (shapeId: string) => currentShapeList()?.nameGlobal(shapeId) ?? null,
/** The field a shape's name is being edited in, with what it holds; null while none is. */
shapeRenaming: () => {
const field = currentShapeList()?.renameField;
return field ? { ...centreOf(field), value: field.value } : null;
},
/** The Paint tool (#186): its button, the "Handles" menu, and which part it shows. */
paintButton: () => centreOf(vectorArtEditor.paintButton),
paintTarget: () => ({
...centreOf(vectorArtEditor.paintTargetButton),
text: vectorArtEditor.paintTargetButton.text,
target: vectorArtEditor.canvas.paintTarget,
}),
/** A colour stop's marker, or the bar at an offset, in the inspector's fill or outline bar. */
stopAt: (part: 'fill' | 'stroke', index: number) => currentGradientBar(part)?.stopGlobal(index) ?? null,
barAt: (part: 'fill' | 'stroke', offset: number) => currentGradientBar(part)?.barGlobal(offset) ?? null,
},
};
}