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>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-23 10:20:06 +02:00
1 parent b830f9c747
commit ed29b2f3dc
9 files changed
+1114 -1031

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
import { findAsset, findAssetOf } from '../../core/model/Project';
import { previewObjectId } from '../../core/model/animationPreview';
import { currentFramesDialog } from '../animation/FramesDialog';
import { currentRowDialog } from '../animation/RowDialog';
import { centreOf, type HookContext } from './hookHelpers';
/** The Texture, Animation, Tile and List tabs. */
export function documentHooks({ state, editor }: HookContext) {
const { textureEditor } = editor;
return {
texture: {
current: () => textureEditor.currentAssetId,
/** Where an image pixel of the shown sheet sits on screen (frames are dragged in image coordinates). */
pointAt: (x: number, y: number) => textureEditor.imageToScreen(x, y),
rect: () => textureEditor.canvasRect(),
/** Whether a press edits the selected frame's collision outline (#141). */
collisionMode: () => textureEditor.canvas.collisionMode,
/** The open "Generate collision…" dialog (#161), or null. */
generateDialog: () => textureEditor.generateDialog.current()?.info ?? null,
},
animation: {
current: () => editor.animationEditor.currentAssetId,
info: () => editor.animationEditor.shownInfo,
rootPickerAt: () => centreOf(editor.animationEditor.rootPicker),
addRowAt: () => centreOf(editor.animationEditor.addRowButton),
playAt: () => ({
...centreOf(editor.animationEditor.playButton),
text: editor.animationEditor.playButton.text,
}),
/** Where a cell, a row's header and a frame on the ruler sit – no text names a cell. */
cellAt: (trackId: string, frame: number) => editor.animationEditor.canvas.cellGlobal(trackId, frame),
headerAt: (trackId: string) => editor.animationEditor.canvas.headerGlobal(trackId),
rulerAt: (frame: number) => editor.animationEditor.canvas.rulerGlobal(frame),
keys: () => editor.animationEditor.canvas.keysDrawn(),
rows: () => editor.animationEditor.canvas.rowsDrawn(),
view: () => editor.animationEditor.canvas.viewInfo(),
scrub: (frame: number) => editor.animationEditor.canvas.scrub(frame),
/** The open row dialog: what it offers and holds, and a way to pick without clicking through menus. */
rowDialog: () => currentRowDialog()?.debugInfo() ?? null,
rowDialogPick: (target: string, property: string) => currentRowDialog()?.pick(target, property),
/** Copy/paste and frames (#102): what the clipboard holds, and the open frames dialog. */
clipboard: () => structuredClone(state.keyClipboard),
framesDialog: () => currentFramesDialog()?.debugInfo() ?? null,
framesDialogCount: (count: number) => currentFramesDialog()?.setCount(count),
framesDialogConfirm: () => {
const dialog = currentFramesDialog();
if (!dialog) return null;
const g = dialog.confirm.getGlobalPosition();
return { x: g.x + dialog.confirm.w / 2, y: g.y + dialog.confirm.h / 2 };
},
/** The preview above the timeline (#99): where a row's target is drawn and with which values. */
preview: {
/** The id the preview draws a row's target under (`''` = the root itself). */
objectId: (target: string) => {
const asset = findAssetOf(state.project, editor.animationEditor.currentAssetId, 'animation');
return previewObjectId(asset && findAsset(state.project, asset.root), target);
},
rect: (objectId: string) => editor.animationEditor.preview.objectRect(objectId),
item: (objectId: string) => editor.animationEditor.preview.itemDrawn(objectId),
info: () => editor.animationEditor.preview.debugInfo(),
fitAt: () => centreOf(editor.animationEditor.fitButton),
},
},
tile: {
current: () => editor.tileEditor.currentAssetId,
info: () => editor.tileEditor.shownInfo,
/** What the board drew: tiles, the picture it is showing and what stands in the way. */
board: () => editor.tileEditor.canvas.debugInfo(),
pictures: () => editor.tileEditor.strip.debugInfo(),
/** A tile of the board: its quarters and where each of them is drawn. */
tile: (index: number) => editor.tileEditor.canvas.tileDrawn(index),
/** Which board tile a cell of the sample map became – `-1` when that cell is empty. */
sampleTile: (row: number, column: number) => editor.tileEditor.canvas.sampleTile(row, column),
kindPickerAt: () => centreOf(editor.tileEditor.kindPicker),
addPictureAt: () => centreOf(editor.tileEditor.strip.addButton),
removePictureAt: () => centreOf(editor.tileEditor.strip.removeButton),
pictureRowAt: (index: number) => editor.tileEditor.strip.list.rowGlobal(String(index)),
fitAt: () => centreOf(editor.tileEditor.fitButton),
},
list: {
info: () => editor.listEditor.debugInfo(),
/** Where an item's widget sits, so a check can click it – a number field has no findable text. */
fieldAt: (index: number, field = '') => editor.listEditor.fieldAt(index, field),
/** The kind mark of a row, or of one field of a struct item – both are pickers. */
markAt: (index: number, field = '') => editor.listEditor.markAt(index, field),
nameAt: (index: number, field: string) => editor.listEditor.nameAt(index, field),
},
};
}