From c6c26a35fa1b93ff47c8eaad9b70503dc3944a34 Mon Sep 17 00:00:00 2001 From: rubz Date: Thu, 13 Aug 2026 16:29:25 +0200 Subject: [PATCH] fix: read live node width in draw() instead of a stale LiteGraph cache; cache-bust shared module imports - LiteGraph caches a widget's .width the first time it lays a custom-typed widget out and reuses that cached value on every later draw() call. For a divider/status/preset widget added before the node reaches its final size, that cached width goes stale and never updates, making section-divider lines and the status box stop partway across the node instead of spanning it, and clipping/unevenly sizing the preset chips. All three shared widgets now read node.size[0] directly in draw() instead of trusting the widgetWidth parameter, which sidesteps the stale cache entirely. - Add a ?v=2 query string to every import of the shared/*.mjs modules. aiohttp's static file server doesn't send Cache-Control: no-store for these (unlike the top-level registered extension .js files), so browsers were free to keep serving an old cached copy even across a hard reload. A versioned import URL forces a fresh fetch regardless of cache state. --- js/shared/canvas_widgets.mjs | 18 +++++++++++++++--- js/shared/preset_picker_widget.mjs | 5 ++++- js/smart_batch_resize.js | 13 ++++++++++--- js/smart_profile_switch.js | 2 +- js/smart_prompt_controller.js | 2 +- js/smart_resize.js | 10 ++++++++-- 6 files changed, 39 insertions(+), 11 deletions(-) diff --git a/js/shared/canvas_widgets.mjs b/js/shared/canvas_widgets.mjs index e823acb..69dfd4e 100644 --- a/js/shared/canvas_widgets.mjs +++ b/js/shared/canvas_widgets.mjs @@ -6,6 +6,16 @@ // rather than a hardcoded value — a fixed/wrong computeSize previously broke // node layout on click when this was tried without care (see the comment on // the preset-chip widget in smart_resize.js's git history). +// +// draw() intentionally ignores its own `widgetWidth` parameter and reads +// `node.size[0]` instead. LiteGraph caches a `.width` on custom-typed widget +// objects the first time it lays them out and reuses that cached value on +// later draws — for a widget added before the node reaches its final size +// (e.g. before later Python widgets are added, or before an explicit +// node.setSize() call), that cache goes stale and draw() keeps receiving the +// old, too-narrow width forever, making the divider line/status box stop +// partway across the node instead of spanning it. node.size[0] is always +// live, so reading it directly sidesteps the stale cache entirely. const DIVIDER_HEIGHT = 20; @@ -20,6 +30,7 @@ export function createDividerWidget(label) { return [width, DIVIDER_HEIGHT]; }, draw(ctx, node, widgetWidth, y, height) { + const w = node?.size?.[0] || widgetWidth; const lx = 14; ctx.save(); ctx.font = "bold 10px sans-serif"; @@ -30,7 +41,7 @@ export function createDividerWidget(label) { const lineX = lx + ctx.measureText(label).width + 8; ctx.beginPath(); ctx.moveTo(lineX, y + height / 2); - ctx.lineTo(widgetWidth - 14, y + height / 2); + ctx.lineTo(w - 14, y + height / 2); ctx.strokeStyle = "#333"; ctx.lineWidth = 1; ctx.stroke(); @@ -53,8 +64,9 @@ export function createStatusWidget(placeholderText) { return [width, STATUS_HEIGHT]; }, draw(ctx, node, widgetWidth, y, height) { + const w = node?.size?.[0] || widgetWidth; const margin = 14; - const innerW = widgetWidth - margin * 2; + const innerW = w - margin * 2; ctx.save(); ctx.beginPath(); if (ctx.roundRect) ctx.roundRect(margin, y + 2, innerW, height - 4, 4); @@ -68,7 +80,7 @@ export function createStatusWidget(placeholderText) { ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillStyle = this._text ? "#f28f41" : "#666"; - ctx.fillText(this._text || placeholderText, widgetWidth / 2, y + height / 2); + ctx.fillText(this._text || placeholderText, w / 2, y + height / 2); ctx.restore(); }, }; diff --git a/js/shared/preset_picker_widget.mjs b/js/shared/preset_picker_widget.mjs index b19c555..f3373c5 100644 --- a/js/shared/preset_picker_widget.mjs +++ b/js/shared/preset_picker_widget.mjs @@ -15,9 +15,12 @@ function createClassicPresetWidget(node, targetWidget, presets) { return [width, PRESET_ROW_HEIGHT]; }, draw(ctx, node, widgetWidth, y, height) { + // Read the live node width rather than the (possibly stale-cached) + // widgetWidth parameter — see the comment in canvas_widgets.mjs. + const w = node?.size?.[0] || widgetWidth; const margin = 14; const gap = 5; - const innerW = Math.max(40, widgetWidth - margin * 2); + const innerW = Math.max(40, w - margin * 2); const n = presets.length; const cellW = Math.max(10, (innerW - gap * (n - 1)) / n); const h = Math.min(height - 2, 18); diff --git a/js/smart_batch_resize.js b/js/smart_batch_resize.js index 44503a0..5bc22eb 100644 --- a/js/smart_batch_resize.js +++ b/js/smart_batch_resize.js @@ -1,7 +1,7 @@ import { app } from "../../scripts/app.js"; -import { createDividerWidget, createStatusWidget } from "./shared/canvas_widgets.mjs"; -import { createPresetPickerWidget } from "./shared/preset_picker_widget.mjs"; -import { isVueNodes } from "./shared/nodes2.mjs"; +import { createDividerWidget, createStatusWidget } from "./shared/canvas_widgets.mjs?v=2"; +import { createPresetPickerWidget } from "./shared/preset_picker_widget.mjs?v=2"; +import { isVueNodes } from "./shared/nodes2.mjs?v=2"; app.registerExtension({ name: "Craftopia.SmartBatchResize", @@ -149,5 +149,12 @@ app.registerExtension({ node.setDirtyCanvas(true); }; + // Force a full size/layout recompute now that every widget has been + // added and repositioned. Without this, the preset-chip row's very + // first paint can use a stale/undersized widgetWidth (from before the + // node's final layout settled), rendering uneven or clipped chips + // until the next unrelated redraw fixes it. + node.setSize(node.computeSize()); + node.setDirtyCanvas(true, true); }, }); diff --git a/js/smart_profile_switch.js b/js/smart_profile_switch.js index 60b2d3c..d698d5c 100644 --- a/js/smart_profile_switch.js +++ b/js/smart_profile_switch.js @@ -1,5 +1,5 @@ import { app } from "../../scripts/app.js"; -import { createDividerWidget, createStatusWidget } from "./shared/canvas_widgets.mjs"; +import { createDividerWidget, createStatusWidget } from "./shared/canvas_widgets.mjs?v=2"; app.registerExtension({ name: "Craftopia.SmartProfileSwitch", diff --git a/js/smart_prompt_controller.js b/js/smart_prompt_controller.js index 5e9356a..82821aa 100644 --- a/js/smart_prompt_controller.js +++ b/js/smart_prompt_controller.js @@ -1,5 +1,5 @@ import { app } from "../../scripts/app.js"; -import { createStatusWidget } from "./shared/canvas_widgets.mjs"; +import { createStatusWidget } from "./shared/canvas_widgets.mjs?v=2"; app.registerExtension({ name: "Craftopia.SmartPromptController", diff --git a/js/smart_resize.js b/js/smart_resize.js index c186643..210fffd 100644 --- a/js/smart_resize.js +++ b/js/smart_resize.js @@ -1,6 +1,6 @@ import { app } from "../../scripts/app.js"; -import { createPresetPickerWidget } from "./shared/preset_picker_widget.mjs"; -import { isVueNodes } from "./shared/nodes2.mjs"; +import { createPresetPickerWidget } from "./shared/preset_picker_widget.mjs?v=2"; +import { isVueNodes } from "./shared/nodes2.mjs?v=2"; app.registerExtension({ name: "Craftopia.SmartResize", @@ -40,5 +40,11 @@ app.registerExtension({ node.widgets.splice(lsIdx + 1, 0, presetWidget); } } + + // Force a full size/layout recompute now that the preset widget has + // been added and repositioned — see the comment in + // smart_batch_resize.js for why this is needed. + node.setSize(node.computeSize()); + node.setDirtyCanvas(true, true); }, });