Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71baccdacd | ||
|
|
6ace9fc662 |
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 2.1.1 · 2026-11-12
|
||||
|
||||
- Remove the duplicate lower Fit button in the standalone workspace; the top Fit control and the workflow comparison node's button remain available.
|
||||
- Move the whole-image and masked-area choices directly below Generate in the workspace.
|
||||
- Place color-paint controls in the existing lower toolbar for both editing modes.
|
||||
|
||||
## 2.1.0 · 2026-11-12
|
||||
|
||||
- Add an independent color-paint layer to Mask and Crop & Blend modes. Alt-click samples the visible image color, Ctrl + wheel changes brush size, and the painted image is used for inpainting.
|
||||
|
||||
@@ -312,6 +312,7 @@ See [verification notes](VERIFICATION.md) for tested configurations and limits,
|
||||
|
||||
| Versions | Highlights and fixes |
|
||||
| --- | --- |
|
||||
| 2.1.1 | Streamlined comparison Fit controls, moved repaint-area choices below Generate, and combined color-paint controls with the lower toolbar. |
|
||||
| 2.1.0 | Color-paint layer, crop merge progress, workspace comparison zoom, reordered controls, collapsible prompts and GPL-3.0-only licensing. |
|
||||
| 2.0.1 | Weak-network retry and cancellation, preview resource cleanup, rollback races, dense-mask memory reduction and safer malformed-workflow handling. |
|
||||
| 2.0.0 | Standalone light/dark workbench, workflow discovery, editable generation, task progress, result recovery and unified template. |
|
||||
|
||||
@@ -312,6 +312,7 @@ node --experimental-default-type=module tests/test_compare_history.mjs
|
||||
|
||||
| 版本 | 主要功能和修复 |
|
||||
| --- | --- |
|
||||
| 2.1.1 | 精简对比适应按钮、将重绘画布选项移至生成按钮下方,并把彩色画笔并入左下角工具栏。 |
|
||||
| 2.1.0 | 彩色画笔图层、裁切合并进度、工作台对比缩放、常用参数上移、提示词折叠及 GPL-3.0-only 许可证。 |
|
||||
| 2.0.1 | 弱网重试与取消、预览资源清理、回退竞态、大蒙版内存优化和异常工作流校验。 |
|
||||
| 2.0.0 | 独立黑白工作台、工作流发现、生成中编辑、进度、结果恢复和统一模板。 |
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "adachi-inpaint-workbench"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
description = "Inline mask painting, crop blending and a standalone inpainting workbench for ComfyUI"
|
||||
requires-python = ">=3.10"
|
||||
license = { file = "LICENSE" }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from 'node:assert/strict';
|
||||
class Element {
|
||||
constructor(tag){this.tag=tag;this.style={};this.dataset={};this.classList={toggle(){}};this.children=[];this.width=0;this.height=0;this.value='';}
|
||||
append(...children){this.children.push(...children)} setAttribute(){} hasPointerCapture(){return false} setPointerCapture(){}
|
||||
append(...children){this.children.push(...children)} prepend(...children){this.children.unshift(...children)} setAttribute(){} hasPointerCapture(){return false} setPointerCapture(){}
|
||||
getContext(){return {save(){},restore(){},beginPath(){},rect(){},clip(){},arc(){},fill(){},moveTo(){},lineTo(){},stroke(){},translate(){},clearRect(){},drawImage(){},getImageData(){return {data:[12,34,56,255]}}};}
|
||||
toDataURL(){return 'data:image/png;base64,paint';}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ globalThis.Image=class {constructor(){this.width=16;this.height=12;} async decod
|
||||
const {ColorPaint}=await import('../web/color_paint.js');
|
||||
const editor={image:{width:16,height:12},ready:true,view:'overlay',brushSize:8,brushOpacity:100,stage:new Element('div'),root:new Element('div'),canvas:new Element('canvas'),options:{drawSource:()=>false},point:e=>({x:e.x,y:e.y}),pointerDown(){},pointerMove(){},pointerUp(){},schedule(){},status(){}};
|
||||
const crop={mode:'mask',bar:new Element('div'),region:{locked:false}},widget={value:''};let changes=0;
|
||||
const paint=new ColorPaint(editor,crop,widget,()=>changes++);paint.button.onclick({preventDefault(){},stopPropagation(){}});
|
||||
const paint=new ColorPaint(editor,crop,widget,()=>changes++);assert.equal(crop.bar.children[0],paint.controls);paint.button.onclick({preventDefault(){},stopPropagation(){}});
|
||||
const event={button:0,pointerId:1,x:2,y:3,preventDefault(){},stopPropagation(){}};
|
||||
editor.pointerDown(event);assert.equal(editor.pointerId,1);editor.pointerUp(event);assert.equal(editor.pointerId,null);assert.match(widget.value,/data:image\/png/);assert.equal(changes,1);
|
||||
const saved=widget.value;editor.pointerDown({...event,altKey:true});assert.equal(paint.color,'#0c2238');assert.equal(widget.value,saved);
|
||||
|
||||
+2
-3
@@ -12,8 +12,7 @@ export class ColorPaint {
|
||||
this.picker=document.createElement('input');this.picker.type='color';this.picker.value=this.color;this.picker.title=T('Brush color');this.picker.setAttribute('aria-label',this.picker.title);this.picker.oninput=()=>{this.color=this.picker.value;};
|
||||
this.undoButton=document.createElement('button');this.undoButton.type='button';this.undoButton.textContent='↶';this.undoButton.title=T('Undo color painting');this.undoButton.onclick=()=>this.step(this.undo,this.redo);
|
||||
this.redoButton=document.createElement('button');this.redoButton.type='button';this.redoButton.textContent='↷';this.redoButton.title=T('Redo color painting');this.redoButton.onclick=()=>this.step(this.redo,this.undo);
|
||||
controls.append(this.button,this.picker,this.undoButton,this.redoButton);editor.stage.append(controls);this.controls=controls;
|
||||
this.barObserver=new ResizeObserver(()=>editor.root.style.setProperty('--dd-color-offset',`${Math.ceil(crop.bar.getBoundingClientRect().height)+14}px`));this.barObserver.observe(crop.bar);
|
||||
controls.append(this.button,this.picker,this.undoButton,this.redoButton);crop.bar.prepend(controls);this.controls=controls;
|
||||
const draw=editor.options.drawSource,revision=editor.options.sceneRevision;
|
||||
editor.options.drawSource=ctx=>{const done=draw?.(ctx);if(!done)ctx.drawImage(editor.image,0,0);if(this.layer.width&&!crop.previewOriginal)ctx.drawImage(this.layer,0,0);return true;};
|
||||
editor.options.sceneRevision=()=>`${revision?.()||''}:paint:${this.revision}`;
|
||||
@@ -35,7 +34,7 @@ export class ColorPaint {
|
||||
remember(){this.undo.push(this.layer.toDataURL('image/png'));let bytes=this.undo.reduce((n,s)=>n+s.length*2,0);while(this.undo.length>1&&(this.undo.length>20||bytes>64*1024*1024))bytes-=this.undo.shift().length*2;}
|
||||
async step(from,to){if(this.drawing||this.restoring||!from.length)return;this.restoring=true;const current=this.layer.toDataURL('image/png'),data=from.pop(),image=new Image();image.src=data;try{await image.decode();to.push(current);const ctx=this.layer.getContext('2d');ctx.clearRect(0,0,this.layer.width,this.layer.height);ctx.drawImage(image,0,0);this.revision++;this.flush();this.editor.schedule();this.sync();}catch(e){from.push(data);this.editor.status(e.message,true);}finally{this.restoring=false;}}
|
||||
clear(){this.undo=[];this.redo=[];this.layer.width=this.layer.height=0;if(this.widget)this.widget.value='';this.revision++;this.sync();}
|
||||
destroy(){this.barObserver.disconnect();this.layer.width=this.layer.height=0;this.controls.remove();}
|
||||
destroy(){this.layer.width=this.layer.height=0;this.controls.remove();}
|
||||
clearRegion(r){if(!this.layer.width)return;this.remember();this.redo=[];this.layer.getContext('2d').clearRect(r.x,r.y,r.width,r.height);this.revision++;this.flush();this.editor.schedule();this.sync();}
|
||||
sync(){this.editor.stage.classList.toggle('dd-color-active',this.active);this.button.classList.toggle('dd-active',this.active);this.button.setAttribute('aria-pressed',String(this.active));this.picker.hidden=this.undoButton.hidden=this.redoButton.hidden=!this.active;this.undoButton.disabled=!this.undo.length;this.redoButton.disabled=!this.redo.length;this.editor.updateButtons?.();}
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ export class ImageCompareView {
|
||||
this.downloadButton.setAttribute("aria-label", this.downloadButton.title);
|
||||
this.downloadButton.onclick = event => { event.preventDefault(); event.stopPropagation(); this.downloadLatest(); };
|
||||
toolbar.append(this.downloadButton);
|
||||
const fit = element("button", "", T("Fit")); fit.type = "button";
|
||||
const fit = element("button", "dd-compare-fit", T("Fit")); fit.type = "button";
|
||||
fit.onclick = event => { event.preventDefault(); event.stopPropagation(); this.fitView(); }; toolbar.append(fit);
|
||||
this.stage = element("div", "dd-stage dd-compare-stage");
|
||||
this.canvas = document.createElement("canvas"); this.canvas.tabIndex = 0;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { ImageCompareView } from "./compare.js?v=2.1.0";
|
||||
import { ImageCompareView } from "./compare.js?v=toolbar-layout-20261112";
|
||||
import { readPreview } from "./preview_transport.js";
|
||||
import { installComparisonRecovery } from "./result_recovery.js";
|
||||
import {executionId,executionNode,graphNodeEntries} from "./workspace_flow.js";
|
||||
|
||||
+2
-1
@@ -26,7 +26,7 @@
|
||||
.dd-inpaint input[type="range"] { display: block; width: 100%; min-width: 0; height: 18px; margin: 3px 0 0; accent-color: #f3b06a; cursor: pointer; }
|
||||
.dd-inpaint .dd-stage { position: relative; flex: 1 1 290px; min-height: 170px; overflow: hidden; border-top: 1px solid var(--dd-line); border-bottom: 1px solid var(--dd-line); background-color: #161a20; background-image: linear-gradient(45deg, #222831 25%, transparent 25%), linear-gradient(-45deg, #222831 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #222831 75%), linear-gradient(-45deg, transparent 75%, #222831 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0; }
|
||||
.dd-inpaint .dd-stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; cursor: crosshair; }
|
||||
.dd-inpaint .dd-color-paint {position:absolute;left:8px;bottom:var(--dd-color-offset,45px);z-index:5;display:flex;gap:4px;align-items:center;padding:3px;border-radius:5px;background:#1b222bdd;}
|
||||
.dd-inpaint .dd-color-paint {display:flex;flex:none;gap:3px;align-items:center;}
|
||||
.dd-inpaint .dd-color-paint button {min-width:26px;padding:3px 6px;}
|
||||
.dd-inpaint .dd-color-paint input[type=color] {width:26px;height:25px;padding:1px;border:0;background:transparent;cursor:pointer;}
|
||||
.dd-inpaint .dd-color-paint [hidden] {display:none;}
|
||||
@@ -102,6 +102,7 @@
|
||||
.dd-comparer .dd-compare-stage { border: 0; }
|
||||
.dd-comparer .dd-compare-toolbar { padding-top: 5px; padding-bottom: 2px; }
|
||||
.dd-comparer .dd-compare-toolbar .dd-dimensions { margin-left: 5px; }
|
||||
.adachi-workspace .dd-comparer .dd-compare-fit { display: none; }
|
||||
.dd-comparer .dd-adoption { padding-top: 3px; padding-bottom: 5px; }
|
||||
.dd-comparer .dd-compare-captions { position: absolute; top: 8px; left: 8px; right: 8px; display: flex; justify-content: space-between; pointer-events: none; }
|
||||
.dd-comparer .dd-compare-captions span { background: #171d27cc; color: #f1e3cf; padding: 3px 7px; border-radius: 3px; font-size: 11px; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { InlineMaskEditor } from "./editor.js?v=2.1.0";
|
||||
import { installInpaintWorkbench } from "./inpaint_node.js?v=workspace-zoom-20261112";
|
||||
import { installInpaintWorkbench } from "./inpaint_node.js?v=toolbar-layout-20261112";
|
||||
import { bindWidgetToNodeWidth, createWidgetHost } from "./widget_layout.js?v=stable-host-20261112";
|
||||
import { routeWheelOutsideImage } from "./wheel_routing.js";
|
||||
import { InlineCropController } from "./inline_crop.js?v=2.1.0";
|
||||
@@ -9,13 +9,13 @@ import { canStoreDraft, prepareInlineDraft, syncInlineWidgets } from "./draft_gu
|
||||
import { T } from "./i18n.js";
|
||||
import { removeLegacyFeedback } from "./legacy_feedback.js?v=0.6.4";
|
||||
import { uploadImageWithProgress } from "./upload_transport.js?v=stability1";
|
||||
import { ColorPaint } from "./color_paint.js";
|
||||
import { ColorPaint } from "./color_paint.js?v=toolbar-layout-20261112";
|
||||
import { createPaintDraftProtection } from "./paint_draft.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=color-paint-20261112", import.meta.url).href;
|
||||
stylesheet.href = new URL("./editor.css?v=toolbar-layout-20261112", import.meta.url).href;
|
||||
if (!stylesheet.isConnected) document.head.append(stylesheet);
|
||||
|
||||
function hide(widget) {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { InpaintParameterPanel } from "./inpaint_panel.js?v=workspace-zoom-20261112";
|
||||
import { InpaintParameterPanel } from "./inpaint_panel.js?v=toolbar-layout-20261112";
|
||||
import { connectedEditor, connectedSamplers, relocateWidgets, syncCurrentDimensions } from "./parameter_bridge.js?v=0.8.2";
|
||||
import { routeWheelOutsideImage } from "./wheel_routing.js";
|
||||
import { T } from "./i18n.js";
|
||||
|
||||
@@ -58,7 +58,7 @@ export class InpaintParameterPanel {
|
||||
enum(parent, name, label, pills = false) {
|
||||
const widget = this.widget(name); if (!widget) return;
|
||||
label = T(label);
|
||||
const wrapper = el("div", "dd-param-field"); wrapper.append(el("span", "dd-param-label", label));
|
||||
const wrapper = el("div", "dd-param-field"); wrapper.dataset.adachiParam = name; wrapper.append(el("span", "dd-param-label", label));
|
||||
let values = widget.options?.values; if (typeof values === "function") values = values();
|
||||
if (!Array.isArray(values)) values = Object.keys(choices[name] || {});
|
||||
if (!values.includes(widget.value)) values = [widget.value, ...values];
|
||||
|
||||
@@ -98,7 +98,6 @@ body.adachi-workspace-open > :not(.adachi-workspace):not(script):not(style):not(
|
||||
.adachi-workspace .aw-canvas .dd-crop-bar button,.adachi-workspace .aw-canvas .dd-crop-bar select,.adachi-workspace .aw-canvas .dd-crop-bar input {
|
||||
height:28px;min-height:28px;font-size:12px;color:var(--aw-text);
|
||||
}
|
||||
.adachi-workspace .aw-canvas .dd-color-paint {bottom:calc(var(--aw-history-height,40px) + var(--aw-crop-height,32px) + 24px);background:var(--aw-panel);border:1px solid var(--aw-line);box-shadow:0 2px 8px #0002;}
|
||||
.adachi-workspace .aw-canvas .dd-files button {opacity:1}
|
||||
.adachi-workspace .aw-canvas .dd-adoption {display:none}
|
||||
.adachi-workspace .aw-canvas .dd-compare-stage {position:absolute;inset:0;min-height:0}
|
||||
|
||||
+2
-2
@@ -72,7 +72,7 @@ export class Workspace {
|
||||
// Move the existing controls, rather than copying widgets: linked inputs,
|
||||
// source-size synchronization and sampler updates keep their native wiring.
|
||||
this.sampler.adachiParameters.refresh();
|
||||
for(const name of ['resolution','width','height','denoise']){
|
||||
for(const name of ['area','resolution','width','height','denoise']){
|
||||
const field=this.sampler.adachiParameters.body.querySelector(`:scope > [data-adachi-param="${name}"]`);
|
||||
if(field)this.mount(field,this.quickParams);
|
||||
}
|
||||
@@ -219,6 +219,6 @@ export class Workspace {
|
||||
close(){if(this.closed)return;this.closed=true;this.sessionController.abort();this.checkController?.abort();++this.checkVersion;clearInterval(this.timer);clearInterval(this.pollTimer);clearTimeout(this.checkTimer);this.stopSplitter?.();for(const dispose of this.disposers.reverse())dispose();if(this.history){this.history.onChange=null;this.history.onRestore=null;}if(this.taskProgress)this.taskProgress.onChange=()=>{};this.bindings?.destroy();for(const {element,marker,style}of this.mounts.reverse()){element.classList.remove('aw-embedded');const removed=(element===this.editor?.root&&(this.editor.destroyed||!this.editorNode?.graph))||(element===this.compare?.root&&!this.compareNode?.graph)||(element===this.sampler?.adachiWorkbench?.root&&!this.sampler?.graph);if(removed){marker.remove();element.remove();continue;}element.hidden=false;element.inert=false;if(marker.parentNode)marker.replaceWith(element);if(style===null)element.removeAttribute('style');else element.setAttribute('style',style);}this.root.remove();document.body.classList.remove('adachi-workspace-open');this.editor?.schedule();this.compare?.schedule();this.compare?.updateButton();currentWorkspace=null;}
|
||||
}
|
||||
app.registerExtension({name:'Adachi.InpaintWorkspace',setup(){
|
||||
const css=document.createElement('link');css.rel='stylesheet';css.href=new URL('./workspace.css?v=2.1.0',import.meta.url).href;document.head.append(css);
|
||||
const css=document.createElement('link');css.rel='stylesheet';css.href=new URL('./workspace.css?v=toolbar-layout-20261112',import.meta.url).href;document.head.append(css);
|
||||
document.addEventListener('adachi-open-workspace',event=>{if(!currentWorkspace)currentWorkspace=new Workspace(event.detail?.sampler||null);});
|
||||
}});
|
||||
|
||||
Reference in New Issue
Block a user