131 lines
13 KiB
JavaScript
131 lines
13 KiB
JavaScript
import fs from "node:fs";
|
|
|
|
const source = fs.readFileSync("web/js/ttp_smart_tile_interactive.js", "utf8");
|
|
|
|
function assert(condition, message) {
|
|
if (!condition) {
|
|
throw new Error(message);
|
|
}
|
|
}
|
|
|
|
function sliceBetween(startText, endText) {
|
|
const start = source.indexOf(startText);
|
|
assert(start >= 0, `missing start marker: ${startText}`);
|
|
const end = source.indexOf(endText, start);
|
|
assert(end > start, `missing end marker: ${endText}`);
|
|
return source.slice(start, end);
|
|
}
|
|
|
|
const beginDrag = sliceBetween("const beginDrag = (event, tileIndex, mode, region) =>", "for (const index of renderOrder)");
|
|
const createTile = sliceBetween("stage.addEventListener(\"pointerdown\", (event) =>", "const controls = document.createElement(\"div\")");
|
|
const createTileBeforeMove = createTile.slice(0, createTile.indexOf("const apply ="));
|
|
|
|
assert(/const dragThresholdPx = 3/.test(source), "tile editor should use a small drag threshold");
|
|
assert(!/apply\(event\);/.test(beginDrag), "clicking an existing tile must not immediately apply a drag");
|
|
assert(/if \(!hasDragged && movedPx < dragThresholdPx\)/.test(beginDrag), "existing tile drag should wait until movement crosses the threshold");
|
|
assert(/renderEditor\(node\);/.test(beginDrag), "existing tile click should still re-render selection on pointerup");
|
|
assert(/pointercancel/.test(beginDrag), "existing tile drag should clean up pointercancel");
|
|
assert(/blur/.test(beginDrag), "existing tile drag should clean up window blur");
|
|
assert(!/setPointerCapture/.test(beginDrag), "existing tile drag should not rely on pointer capture");
|
|
assert(!/releasePointerCapture/.test(beginDrag), "existing tile drag should not rely on pointer capture release");
|
|
assert(!/writeLayout\(node, nextTiles, newIndex\);/.test(createTileBeforeMove), "empty-stage pointerdown should not create a tile before dragging");
|
|
assert(/if \(!hasDragged && movedPx < dragThresholdPx\)/.test(createTile), "new tile creation should wait until movement crosses the threshold");
|
|
assert(/async function inferSmartTileLayout/.test(source), "tile editor should expose an Infer action");
|
|
assert(/app\.queuePrompt\(0,\s*1,\s*\[String\(node\.id\)\]\)/.test(source), "Infer action should queue only the current Smart Tile node");
|
|
assert(/ttp-smart-tile-layout/.test(source), "Infer action should listen for backend layout updates");
|
|
assert(/auto_detect_request/.test(source), "Infer action should increment the node inference request widget");
|
|
assert(/options\.fillGaps !== false/.test(source), "Infer action should allow disabling automatic gap filling");
|
|
assert(/modeOverride: "sam3\.1"/.test(source), "Auto SAM should force SAM3.1 detection");
|
|
assert(!/\/ttp\/smart_tile\/analyze/.test(source), "Infer action should not use the old analysis route");
|
|
assert(/occlusion_priority/.test(source), "interactive layout should preserve auto tile priority metadata");
|
|
assert(/TTP_Smart_Tile_Loop_Source_Experimental/.test(source), "frontend should know the Smart Tile Loop Source node");
|
|
assert(/Process All Tiles/.test(source), "loop source should expose a Process All Tiles button");
|
|
assert(/Start Loop \/ Process All Tiles/.test(source), "loop source start button should be clearly labeled");
|
|
assert(/function highlightLoopStartButton/.test(source), "loop source should highlight the start loop button");
|
|
assert(/widget\.computeSize = \(width\) => \[Math\.max\(300/.test(source), "loop source start button should be larger than a default widget");
|
|
assert(/Stop Tile Loop/.test(source), "loop source should expose a Stop Tile Loop button");
|
|
assert(/ttp-smart-tile-loop/.test(source), "frontend should listen for Smart Tile loop progress events");
|
|
assert(/queueSmartTileLoop\(node, false\)/.test(source), "loop event handler should queue the next tile automatically");
|
|
assert(/restart_request/.test(source), "loop source should increment restart requests");
|
|
assert(/loop_request/.test(source), "loop source should increment loop requests to avoid stale cached execution");
|
|
assert(/snapGuideThresholdPx = 10/.test(source), "tile editor should define a pixel threshold for guide snapping");
|
|
assert(/const snapTargets = \(axis, skipIndex\) =>/.test(source), "tile editor should build snap targets");
|
|
assert(/snapMovedTile/.test(source), "tile editor should snap moved tiles");
|
|
assert(/snapResizedTile/.test(source), "tile editor should snap resized tiles");
|
|
assert(/snapCreatedTile/.test(source), "tile editor should snap newly drawn tiles");
|
|
assert(/function fillTileGaps/.test(source), "tile editor should share automatic gap filling logic");
|
|
assert(/function autoMaxTiles/.test(source), "tile editor should read the Auto Tile max tile limit");
|
|
assert(/const filled = fillTileGaps\(node, layout\.tiles, autoMaxTiles\(node\)\);/.test(source), "Auto Tile gap fill should respect auto_max_tiles");
|
|
assert(/if \(fillGaps\) \{\s*const filled = fillTileGaps\(node, layout\.tiles, autoMaxTiles\(node\)\);/s.test(source), "Auto Tile should only fill gaps when requested");
|
|
assert(/writeLayout\(node, layout\.tiles, 0\);\s*const coverage = analyzeCoverage\(layout\.tiles\);/s.test(source), "Auto SAM should write detected tiles without gap filling");
|
|
assert(/source: "auto_gap"/.test(source), "auto-filled gap tiles should be marked as background gap tiles");
|
|
assert(/function editorStageSize/.test(source), "tile editor should compute a fixed source-ratio stage size");
|
|
assert(!/aspect-ratio:/.test(source), "tile editor should not rely on CSS aspect-ratio when the node is resized");
|
|
assert(!/max-height:720px/.test(source), "tile editor should not clamp only stage height and distort the image");
|
|
assert(/"object_mask"/.test(source), "tile editor should preserve SAM object masks in layout metadata");
|
|
assert(/"object_mask_source"/.test(source), "tile editor should preserve parent object masks for refresh");
|
|
assert(/GRID_MASK_MODES/.test(source), "tile editor should expose grid mask inheritance modes");
|
|
assert(/async function gridTilesWithInheritedMask/.test(source), "Grid in should be able to inherit and crop object masks");
|
|
assert(/async function refreshInheritedMasks/.test(source), "tile editor should refresh inherited masks after manual edits");
|
|
assert(/function cropObjectMaskForTile/.test(source), "Grid in should crop object masks per child tile");
|
|
assert(/function isLargeContextTile/.test(source), "Grid in should detect huge context tiles");
|
|
assert(/function contextGridTileMetadata/.test(source), "Grid in should keep huge context children on the lowest layer");
|
|
assert(/tileAreaRatio\(tile\) >= largeContextAreaRatio/.test(source), "huge non-detail tiles should be treated as context tiles");
|
|
assert(/const maskData = sourceObjectMaskData\(sourceTile\);/.test(source), "huge context Grid in should still preserve and crop the body mask");
|
|
assert(!/sourceIsLargeContext \? null : sourceObjectMaskData/.test(source), "huge context Grid in must not drop the source body mask");
|
|
assert(/crop_mask_skip_empty/.test(source), "Grid in should allow skipping empty mask child tiles");
|
|
assert(/createButton\(`Grid in T\$\{selectedIndex \+ 1\}`, async \(\) =>/.test(source), "Grid in should run asynchronously for mask cropping");
|
|
assert(/ensurePaintMaskCanvas/.test(source), "tile editor should create a paint mask canvas");
|
|
assert(/auto_paint_mask/.test(source), "tile editor should sync painted masks into the hidden backend input");
|
|
assert(/MASK_OVERLAY_COLORS/.test(source), "tile editor should define distinct colors for object-mask overlays");
|
|
assert(/function maskOverlayEnabled/.test(source), "tile editor should expose a mask overlay toggle state");
|
|
assert(/function stitchMaskPreviewEnabled/.test(source), "tile editor should expose a stitch mask preview toggle state");
|
|
assert(/async function renderMaskOverlay/.test(source), "tile editor should render object masks as a colored overlay");
|
|
assert(/tintedMaskCanvas/.test(source), "tile editor should tint mask pixels before showing overlays");
|
|
assert(/stitchedMaskValues/.test(source), "tile editor should preview hardened replace stitch masks");
|
|
assert(!/canvas\.isConnected/.test(source), "mask overlay rendering should not require the staged canvas to be connected before renderEditor appends it");
|
|
assert(/Show masks/.test(source), "tile editor should expose a show masks toggle");
|
|
assert(/Hide masks/.test(source), "tile editor should expose a hide masks toggle");
|
|
assert(/Stitch mask/.test(source), "tile editor should expose a stitch mask preview toggle");
|
|
assert(/Raw masks/.test(source), "tile editor should expose a raw mask preview toggle");
|
|
assert(/ttpSmartTileShowMasks/.test(source), "mask overlay visibility should be stored on the editor node instance");
|
|
assert(/ttpSmartTilePreviewStitchMask/.test(source), "stitch mask preview should be stored on the editor node instance");
|
|
assert(/renderMaskOverlay\(node, maskOverlay, tiles\)/.test(source), "renderEditor should draw the colored mask overlay when enabled");
|
|
assert(/tintedMaskCanvas\(maskImage, color, \{/.test(source), "renderMaskOverlay should pass preview options into mask tinting");
|
|
assert(/node\.ttpSmartTileShowMasks = true;/.test(source), "stitch mask preview should auto-enable colored mask overlays");
|
|
assert(/Move/.test(source), "tile editor should expose an explicit move mode");
|
|
assert(/node\.ttpSmartTilePaintMode = "off";\s*renderEditor\(node\);/s.test(source), "Move should restore tile drag and resize mode");
|
|
assert(/pointer-events:" \+ \(paintMode === "off" \? "auto" : "none"\)/.test(source), "tile regions should only block dragging while Brush or Erase is active");
|
|
assert(/Brush/.test(source), "tile editor should expose a paint brush action");
|
|
assert(/Erase/.test(source), "tile editor should expose an erase action");
|
|
assert(/Mask to Tile/.test(source), "tile editor should expose a button that commits painted masks into layout tiles");
|
|
assert(/Mask Replace/.test(source), "tile editor should expose a button that replaces the layout with painted mask tiles");
|
|
assert(/Refresh masks/.test(source), "tile editor should expose a refresh masks action");
|
|
assert(/refreshInheritedMasks\(node, tiles, gridMaskMode\(node\)\)/.test(source), "Refresh masks should use the current grid mask mode");
|
|
assert(/function selectedTileIndexes/.test(source), "tile editor should track multi-selected tiles");
|
|
assert(/function setTileSelection/.test(source), "tile editor should support modifier-click multi-selection");
|
|
assert(/boolEventModifier\(event\)/.test(beginDrag), "modifier-clicking an existing tile should toggle multi-selection");
|
|
assert(/async function mergedObjectMaskForTiles/.test(source), "tile editor should union selected tile masks");
|
|
assert(/async function mergeSelectedMasks/.test(source), "tile editor should expose selected mask merging logic");
|
|
assert(/async function mergeSelectedTiles/.test(source), "tile editor should expose selected tile merging logic");
|
|
assert(/Merge masks/.test(source), "tile editor should expose a merge masks action");
|
|
assert(/Merge tiles/.test(source), "tile editor should expose a merge tiles action");
|
|
assert(/ownObjectMaskData/.test(source), "mask merging should prefer each tile's cropped mask over parent source masks");
|
|
assert(/function addPaintMaskTiles/.test(source), "tile editor should convert painted masks into persisted tiles");
|
|
assert(/auto_mask_expand/.test(source), "paint-created tiles should read the auto mask expansion widget");
|
|
assert(/expandMaskImageData/.test(source), "paint-created masks should support foreground expansion");
|
|
assert(/maskCropData\(canvas, box, maskExpand\)/.test(source), "paint-created masks should apply the requested mask expansion");
|
|
assert(/source: "paint_mask"/.test(source), "paint-created tiles should be marked as paint mask tiles");
|
|
assert(/object_mask:\s*\{/.test(source), "paint-created tiles should persist object masks in layout metadata");
|
|
assert(/Clear mask/.test(source), "tile editor should expose a clear mask action");
|
|
assert(/syncPaintMaskWidget\(node\);\s*inferSmartTileLayout\(node\);/s.test(source), "Auto Tile should submit the latest painted mask before inference");
|
|
assert(/Auto SAM/.test(source), "tile editor should expose an Auto SAM action");
|
|
assert(/syncPaintMaskWidget\(node\);\s*inferSmartTileLayout\(node, \{\s*fillGaps: false,/s.test(source), "Auto SAM should submit masks and disable automatic gap filling");
|
|
assert(!/PROMPT_BUILDER_NAME/.test(source), "frontend must not hook the QwenVL prompt builder node");
|
|
assert(!/repairPromptBuilderWidgets/.test(source), "prompt builder values must be left to native ComfyUI widget serialization");
|
|
assert(!/ttp_prompt_builder_values/.test(source), "prompt builder must not restore stale named widget values from properties");
|
|
assert(!/ttp_prompt_builder_named_values/.test(source), "prompt builder should not use custom frontend persistence");
|
|
assert(!/node\.widgets\?\.(?:sort|splice|reverse)/.test(source), "frontend must not reorder the LiteGraph widget array");
|
|
|
|
console.log("smoke_smart_tile_interactive: ok");
|