diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bd6f7e..3810817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.5.5 + +- Bind the inline canvas, inpaint parameter panel and comparison widget width directly to the node's logical graph width. Cached screen-space measurements cannot shrink the widgets a second time at reduced graph zoom. +- Keep native widget margins and vertical sizing; horizontal and vertical node resizing remain available. +- Calculate source-image automatic canvas height from untransformed client dimensions rather than dividing screen measurements by graph zoom. +- Verify stale-width fault injection, horizontal/vertical resize, zoom, Fit/mask preservation and workflow reload on frontend 1.42.11 and 1.52.7 in both renderers. + ## 0.5.4 - Prevent the inline Fit button click from bubbling into ComfyUI node selection / bring-to-front handlers, which can remeasure a saved node's DOM editor. diff --git a/ROLLBACK.md b/ROLLBACK.md index 989496d..d80bd52 100644 --- a/ROLLBACK.md +++ b/ROLLBACK.md @@ -1,10 +1,11 @@ # Git versions and rollback -The current local version is **v0.5.4**. Both the installed plugin and the source repository retain local commits and tags. No GitHub push is part of this update. +The current local version is **v0.5.5**. Both the installed plugin and the source repository retain local commits and tags. No GitHub push is part of this update. | Tag | Contents | |---|---| -| `v0.5.4` | Isolate Fit clicks and refresh editor assets on upgrade | +| `v0.5.5` | Bind canvas/panel widths to node geometry and retain vertical resizing | +| `v0.5.4` / `local-before-v0.5.5` | Isolate Fit clicks and refresh editor assets on upgrade | | `v0.5.3` / `local-before-v0.5.4` | Fit beside Load image in the inline mask, plus the 5D VAE fix | | `v0.5.2` / `local-before-v0.5.3` | Single-frame 5D VAE support for image models, plus all previous features | | `local-before-v0.5.2` | Exact baseline before the VAE compatibility fix in each local repository | @@ -16,13 +17,13 @@ To undo only this update, run in the plugin folder: ```powershell git status --short -git switch --detach local-before-v0.5.4 +git switch --detach local-before-v0.5.5 ``` Return to this version with: ```powershell -git switch --detach v0.5.4 +git switch --detach v0.5.5 ``` Restart ComfyUI and hard-refresh the browser (`Ctrl+F5`) after switching versions. diff --git a/VERIFICATION.md b/VERIFICATION.md index a51e40a..4d04a5f 100644 --- a/VERIFICATION.md +++ b/VERIFICATION.md @@ -40,6 +40,9 @@ Environment: Windows, local ComfyUI 0.19.3, ComfyUI frontend 1.42.11, PyTorch 2. - 0.5.4: frontend 1.52.7 was downloaded as a wheel and extracted into an isolated test directory, leaving the installed frontend unchanged. With saved node dimensions from the user's workflow, the previous bubbling Fit click changed the classic editor viewport from 396x625 to 396x594. Stopping propagation keeps it at 396x625. Nodes 2.0 also retains its viewport and node dimensions. - 0.5.4: actual browser clicks pass with the saved workflow geometry on frontend 1.42.11 and 1.52.7 in both renderers. Fit resets zoom/pan, remains immediately after Load image, and the inpaint panel occupies over 94% of its node's logical width. The remote server and its installed extension set were not accessed, so the separate narrow-panel screenshot cannot be claimed reproduced/resolved remotely. +- 0.5.5: injecting a cached widget width equal to 35% of its node width reproduces the narrow-column shape on frontend 1.52.7 classic nodes. Before the fix, a 380px inline node had a 113px editor; afterwards its editor is 360px wide, with the remaining 20px reserved for native margins. Inpaint and comparison widgets recover their full widths as well. +- 0.5.5: browser checks on frontend 1.42.11 and 1.52.7, classic nodes and Nodes 2.0, cover attempted stale width writes, resizing node widths to 800/450/1050px, independent height expansion, graph zoom at 0.3/0.6/1.15, Fit with mask preservation, and saved workflow reload. The simulated failure is covered; the remote server itself was not accessed. + The sampling path was also checked for noise-schedule continuity against the real `common_ksampler`/`KSampler` functions in the local ComfyUI; an actual checkpoint has not been loaded to test GPU image quality or every model family, so CPU/interface tests must not be treated as model-quality verification. References used during implementation: diff --git a/pyproject.toml b/pyproject.toml index df052f9..d9240e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "adachi-inpaint-workbench" -version = "0.5.4" +version = "0.5.5" description = "Inline mask painting and WebUI-style inpainting nodes for ComfyUI" requires-python = ">=3.10" license = { file = "LICENSE" } diff --git a/web/compare_node.js b/web/compare_node.js index dca2ff8..d02186d 100644 --- a/web/compare_node.js +++ b/web/compare_node.js @@ -3,6 +3,7 @@ import { api } from "../../scripts/api.js"; import { ImageCompareView } from "./compare.js"; import { routeWheelOutsideImage } from "./wheel_routing.js"; import { T } from "./i18n.js"; +import { bindWidgetToNodeWidth } from "./widget_layout.js?v=0.5.5"; function viewUrl(image) { const type = ["input", "output", "temp"].includes(image?.type) ? image.type : "temp"; @@ -55,6 +56,7 @@ app.registerExtension({ serialize: false, hideOnZoom: false, getMinHeight: () => 420, getMaxHeight: () => 4096, }); widget.serialize = false; + bindWidgetToNodeWidth(node, widget, compare.root); const executed = node.onExecuted; node.onExecuted = function (output) { const result = executed?.apply(this, arguments); diff --git a/web/inline_inpaint.js b/web/inline_inpaint.js index 27f76b2..bf3e0c9 100644 --- a/web/inline_inpaint.js +++ b/web/inline_inpaint.js @@ -1,14 +1,15 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; -import { InlineMaskEditor } from "./editor.js?v=0.5.4"; -import { installInpaintWorkbench } from "./inpaint_node.js"; +import { InlineMaskEditor } from "./editor.js?v=0.5.5"; +import { installInpaintWorkbench } from "./inpaint_node.js?v=0.5.5"; +import { bindWidgetToNodeWidth } from "./widget_layout.js?v=0.5.5"; import { routeWheelOutsideImage } from "./wheel_routing.js"; import { T } from "./i18n.js"; const styleId = "adachi-inpaint-style"; const stylesheet = document.getElementById(styleId) || document.createElement("link"); stylesheet.id = styleId; stylesheet.rel = "stylesheet"; -stylesheet.href = new URL("./editor.css?v=0.5.4", import.meta.url).href; +stylesheet.href = new URL("./editor.css?v=0.5.5", import.meta.url).href; if (!stylesheet.isConnected) document.head.append(stylesheet); function hide(widget) { @@ -63,12 +64,11 @@ app.registerExtension({ cancelAnimationFrame(fitFrame); fitFrame = requestAnimationFrame(() => { if (!editor.ready || editor.destroyed || !node.graph) return; - const rect = editor.canvas.getBoundingClientRect(); - const scale = app.canvas?.ds?.scale || 1; - if (!rect.width || !rect.height) return; - const overhead = node.size[1] - rect.height / scale; - const height = overhead + rect.width / scale * editor.image.height / editor.image.width; - node.setSize([node.size[0], Math.max(240, Math.min(4200, Math.ceil(height)))]); + const width = editor.canvas.clientWidth, height = editor.canvas.clientHeight; + if (!width || !height) return; + const overhead = node.size[1] - height; + const fittedHeight = overhead + width * editor.image.height / editor.image.width; + node.setSize([node.size[0], Math.max(240, Math.min(4200, Math.ceil(fittedHeight)))]); node.properties.adachi_compact_ui = true; editor.schedule(); }); @@ -115,7 +115,7 @@ app.registerExtension({ markChanged(); }, }); - editor.root.dataset.adachiVersion = "0.5.4"; + editor.root.dataset.adachiVersion = "0.5.5"; node.adachiEditor = editor; const stopWheelRouting = routeWheelOutsideImage(editor.root, editor.canvas, () => editor.ready); node.adachiUseResult = async file => { @@ -131,6 +131,7 @@ app.registerExtension({ getMaxHeight: () => 4096, }); domWidget.serialize = false; + bindWidgetToNodeWidth(node, domWidget, editor.root); // Leave computeLayoutSize in charge: a fixed computeSize height // prevents the DOM canvas from growing with the node vertically. const resizeHandle = document.createElement("div"); diff --git a/web/inpaint_node.js b/web/inpaint_node.js index 6b7c02f..57d8115 100644 --- a/web/inpaint_node.js +++ b/web/inpaint_node.js @@ -3,6 +3,7 @@ import { InpaintParameterPanel } from "./inpaint_panel.js"; import { connectedEditor, connectedSamplers, relocateWidgets } from "./parameter_bridge.js"; import { routeWheelOutsideImage } from "./wheel_routing.js"; import { T } from "./i18n.js"; +import { bindWidgetToNodeWidth } from "./widget_layout.js?v=0.5.5"; export function installInpaintWorkbench(nodeType) { const created = nodeType.prototype.onNodeCreated; @@ -58,6 +59,7 @@ export function installInpaintWorkbench(nodeType) { serialize: false, hideOnZoom: false, getMinHeight: contentHeight, getMaxHeight: () => Math.max(4096, contentHeight()), }); widget.serialize = false; + bindWidgetToNodeWidth(node, widget, root); const observer = new ResizeObserver(scheduleSize); observer.observe(tools); const configured = node.onConfigure; node.onConfigure = function () { const result = configured?.apply(this, arguments); queueMicrotask(() => { if (!removed) { panel.refresh(); scheduleSize(); } }); return result; }; diff --git a/web/widget_layout.js b/web/widget_layout.js new file mode 100644 index 0000000..85992bd --- /dev/null +++ b/web/widget_layout.js @@ -0,0 +1,16 @@ +/** Fill the node in graph coordinates, independently of cached DOM measurements. */ +export function bindWidgetToNodeWidth(node, widget, element) { + // Classic ComfyUI uses widget.width before node.width for its DOM host. + // A cached screen-space width is scaled a second time when zoomed out. + // These single-column widgets always occupy the node's available width; + // the frontend still applies its own widget margins and vertical layout. + Object.defineProperty(widget, "width", { + configurable: true, + enumerable: true, + get: () => Math.max(1, Number(node.size[0]) || 1), + set: () => {}, + }); + element.style.width = "100%"; + element.style.maxWidth = "100%"; + element.style.minWidth = "0"; +}