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>
81 lines
4.1 KiB
TypeScript
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,
|
|
},
|
|
};
|
|
}
|