206 lines
25 KiB
JavaScript
206 lines
25 KiB
JavaScript
import {T} from './i18n.js';
|
|
import { app } from '../../scripts/app.js';
|
|
import { api } from '../../scripts/api.js';
|
|
import {selectWorkbenchPipeline} from './workbench_analysis.js';
|
|
import {executionId} from './workspace_flow.js';
|
|
import { WorkspaceBindings } from './workspace_bindings.js';
|
|
import { WorkspaceHistory } from './workspace_history.js';
|
|
import { WorkspaceProgress } from './workspace_progress.js';
|
|
import { installWorkspaceSplitter } from './workspace_splitter.js';
|
|
import { prepareWorkspacePrompt, validateWorkspace, submitWorkspace } from './workspace_execution.js';
|
|
import { syncCurrentDimensions } from './parameter_bridge.js?v=0.8.2';
|
|
const el=(tag,text,cls)=>{const e=document.createElement(tag);if(text)e.textContent=text;if(cls)e.className=cls;return e;};
|
|
const btn=(text,fn)=>{const b=el('button',text);b.type='button';b.onclick=fn;return b;};
|
|
const icons={edit:'<path d="m14 3 7 7-11 11H3v-7L14 3Z"/><path d="m11 6 7 7"/>',compare:'<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M12 2v20M3 8h9M3 13h9M3 18h9"/>',side:'<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M12 4v16M6 12h12m-3-3 3 3-3 3"/>',theme:'<path d="M20 15.5A9 9 0 0 1 8.5 4 9 9 0 1 0 20 15.5Z"/>',check:'<circle cx="12" cy="12" r="9"/><path d="m8 12 3 3 5-6"/>',back:'<path d="m9 5-7 7 7 7M3 12h19"/>'};
|
|
const iconButton=(label,kind,fn)=>{const b=btn('',fn);b.title=label;b.setAttribute('aria-label',label);b.innerHTML=`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${icons[kind]}</svg>`;b.className='aw-icon';return b;};
|
|
let currentWorkspace=null;
|
|
export class Workspace {
|
|
constructor(samplerNode=null,options={}){
|
|
this.preferredSampler=samplerNode;this.pipelineOptions=options;this.mounts=[];this.disposers=[];this.closed=false;this.running=false;this.checkVersion=0;
|
|
this.sessionController=new AbortController();
|
|
this.root=el('div','','adachi-workspace');this.root.adachiWorkspace=this;this.root.setAttribute('role','dialog');this.root.setAttribute('aria-label','Adachi Workbench');
|
|
const head=el('header'),title=el('h1','Adachi Workbench');head.append(title);
|
|
this.status=el('span',T("Reading the current workflow…"),'aw-status');this.themeButton=iconButton(T("Switch theme"),'theme',()=>this.toggleTheme());head.append(this.status,this.themeButton,iconButton(T("Check connections"),'check',()=>this.check()),iconButton(T("Return to workflow"),'back',()=>this.close()));this.root.append(head);
|
|
const main=el('main'),canvas=el('section','','aw-canvas'),toolbar=el('div','','aw-toolbar');
|
|
this.tabs={edit:iconButton(T("Edit mask"),'edit',()=>this.show('edit')),compare:iconButton(T("Slide to compare"),'compare',()=>this.show('compare'))};
|
|
this.sideButton=iconButton(T("Show before"),'side',()=>this.show(this.view==='before'?'after':'before'));
|
|
toolbar.append(this.tabs.edit,this.tabs.compare,this.sideButton);
|
|
toolbar.append(el('span','','spacer'));this.adoptButton=btn(T("Adopt"),()=>this.adopt());this.adoptButton.title=T("Use this result as the next source image and keep the mask; restore earlier images below");this.adoptButton.className='primary';toolbar.append(this.adoptButton);
|
|
this.surface=el('div','','aw-surface');this.still=el('img','','aw-result-image');this.still.alt=T("Selected source image or generated result");this.still.hidden=true;this.surface.append(this.still);
|
|
const foot=el('div','','aw-footer');this.undoButton=btn(T("↶ Undo"),()=>this.restoreHistory('undo'));this.originalButton=btn(T("Initial source"),()=>this.restoreHistory('original'));this.historySelect=el('select');this.historySelect.setAttribute('aria-label',T("Choose an adopted source image"));this.historySelect.onchange=()=>this.restoreHistory(Number(this.historySelect.value));foot.append(this.undoButton,this.originalButton,this.historySelect);
|
|
this.footer=foot;this.undoButton.title=T("Restore the previous source image, mask and crop");this.originalButton.title=T("Restore the initial source image of this session");this.historySelect.title=T("Choose an earlier adopted or uploaded source image");this.note=el('div','','aw-note');canvas.append(toolbar,this.surface,foot,this.note);
|
|
this.side=el('aside');this.generationPanel=el('div','','aw-generation-progress');this.generationPanel.hidden=true;this.generationLabel=el('span');this.generationBar=el('progress');this.generationBar.max=1;this.generationBar.setAttribute('aria-label',T("Current stage progress"));this.generationPanel.title=T("Actual progress from the current node; stages without step counts show an indeterminate bar");this.generationPanel.append(this.generationLabel,this.generationBar);this.side.append(this.generationPanel,el('h2',T("Workflow settings")));this.settings=el('div');this.params=el('div','','aw-parameters');this.runButton=btn(T("Generate"),()=>this.run());this.runButton.disabled=true;this.runButton.className='primary';this.runButton.style.width='100%';this.side.append(this.settings,this.runButton,this.params);this.divider=el('div','','aw-splitter');main.append(canvas,this.divider,this.side);this.root.append(main);
|
|
this.root.addEventListener('keydown',e=>{if(e.key==='Escape'&&e.target===this.root)this.close();});
|
|
this.root.addEventListener('pointerdown',e=>e.stopPropagation());document.body.append(this.root);document.body.classList.add('adachi-workspace-open');this.stopSplitter=installWorkspaceSplitter(this.root,main,this.divider);
|
|
// Render first. Discovery/catalog and actual server validation run afterward.
|
|
let theme='light';try{theme=localStorage.getItem('adachi-workspace-theme')||theme;}catch{}this.setTheme(theme);
|
|
setTimeout(()=>this.initialize().catch(e=>this.setStatus(e.message,'error')),0);
|
|
}
|
|
setTheme(theme){this.root.dataset.theme=theme==='dark'?'dark':'light';const label=this.root.dataset.theme==='dark'?T("Switch to light theme"):T("Switch to dark theme");this.themeButton.title=label;this.themeButton.setAttribute('aria-label',label);try{localStorage.setItem('adachi-workspace-theme',this.root.dataset.theme);}catch{}}
|
|
toggleTheme(){this.setTheme(this.root.dataset.theme==='dark'?'light':'dark');}
|
|
setStatus(text,state='pending'){if(this.closed)return;this.status.textContent=text;this.status.title=text;this.status.dataset.state=state;}
|
|
renderTaskProgress(){
|
|
const p=this.taskProgress;if(!p || this.closed)return;
|
|
this.generationPanel.hidden=p.state==='idle';this.generationPanel.dataset.state=p.state;
|
|
const known=p.max>0 && p.value!=null;
|
|
this.generationLabel.textContent=p.label+(known && p.max>1?` · ${Math.round(p.value/p.max*100)}%`:'');
|
|
this.generationBar.setAttribute('aria-valuetext',this.generationLabel.textContent);
|
|
if(known)this.generationBar.value=p.value/p.max;else this.generationBar.removeAttribute('value');
|
|
if(this.running && p.state!=='idle')this.setStatus(p.state==='complete'?T("Generation complete"):p.state==='error'?p.label:T("Generating"),p.state==='error'?'error':p.state==='complete'?'ok':'pending');
|
|
}
|
|
showGeneratedResult(){if(this.editor?.stroke || this.editor?.drag || this.editor?.pointerId!=null){this.pendingResultView=true;return;}this.pendingResultView=false;this.show('compare');}
|
|
mount(element,target,canvas=false){
|
|
if(!element?.parentNode)throw Error(T("Controls are not ready; reopen the workbench shortly"));
|
|
const marker=document.createComment('workspace');element.before(marker);this.mounts.push({element,marker,style:element.getAttribute('style')});target.append(element);if(canvas)element.classList.add('aw-embedded');
|
|
}
|
|
async initialize(){
|
|
if(this.closed)return;this.graph=app.rootGraph||app.graph;this.nodes=selectWorkbenchPipeline(this.graph,this.preferredSampler,this.pipelineOptions);
|
|
this.editorNode=this.nodes.editor;this.sampler=this.nodes.sampler;this.compareNode=this.nodes.compare;this.connectionsConfirmed=!this.nodes.ambiguous||!!this.pipelineOptions.confirmed;
|
|
this.editor=this.editorNode?.adachiEditor;this.compare=this.compareNode?.adachiCompare;
|
|
if(!this.editor||!this.sampler?.adachiWorkbench||!this.compare)throw Error(T("This workflow needs Inline Mask, Adachi Inpaint Workbench and Image Compare nodes"));
|
|
this.taskProgress=this.sampler.adachiWorkspaceProgress ||= new WorkspaceProgress();this.taskProgress.onChange=()=>this.renderTaskProgress();this.renderTaskProgress();
|
|
this.history=this.editorNode.adachiWorkspaceHistory ||= new WorkspaceHistory(this.editorNode);
|
|
this.history.onChange=()=>this.syncHistory();
|
|
this.history.onRestore=()=>{syncCurrentDimensions(this.editorNode);app.canvas?.emitBeforeChange?.();app.canvas?.emitAfterChange?.();this.show('edit');this.scheduleCheck();};
|
|
if(this.editor.ready)await this.history.checkpoint(T("Source when opened"));
|
|
if(this.closed)return;
|
|
if((app.rootGraph||app.graph)!==this.graph){this.close();return;}
|
|
const originalTargets=this.compare.options.getTargets,originalManual=this.compare.manualTarget,originalTarget=this.compare.targetSelect.value;
|
|
this.compare.options.getTargets=()=>({...originalTargets(),preferred:executionId(this.graph,this.editorNode)});this.compare.manualTarget=false;this.compare.refreshTargets();
|
|
this.disposers.push(()=>{this.compare.options.getTargets=originalTargets;this.compare.manualTarget=originalManual;this.compare.targetSelect.value=originalTarget;this.compare.refreshTargets();});
|
|
this.mount(this.editor.root,this.surface,true);this.mount(this.compare.root,this.surface,true);this.mount(this.sampler.adachiWorkbench.root,this.params);
|
|
// The dock can wrap on narrow screens or with long history labels. Keep
|
|
// crop/result controls above its measured height instead of a fixed offset.
|
|
const dockObserver=new ResizeObserver(()=>{
|
|
this.root.style.setProperty('--aw-history-height',`${this.footer.getBoundingClientRect().height}px`);
|
|
const crop=this.editorNode.adachiCrop?.bar;
|
|
if(crop)this.root.style.setProperty('--aw-crop-height',`${crop.getBoundingClientRect().height}px`);
|
|
});
|
|
dockObserver.observe(this.footer);if(this.editorNode.adachiCrop?.bar)dockObserver.observe(this.editorNode.adachiCrop.bar);
|
|
this.disposers.push(()=>dockObserver.disconnect());
|
|
const editorWheel=this.editor.options.onGraphWheel,compareWheel=this.compare.options.onGraphWheel;
|
|
this.editor.options.onGraphWheel=()=>{};this.compare.options.onGraphWheel=()=>{};
|
|
this.disposers.push(()=>{this.editor.options.onGraphWheel=editorWheel;this.compare.options.onGraphWheel=compareWheel;});
|
|
this.bindings=new WorkspaceBindings({graph:this.graph,sampler:this.sampler,canvas:app.canvas,onChange:()=>this.scheduleCheck(),markChanged:()=>{app.canvas?.emitBeforeChange?.();app.canvas?.emitAfterChange?.();}});
|
|
this.settings.append(this.connectionPicker(),this.bindings.createPanel());
|
|
this.prepare(true).catch(e=>this.setStatus(e.message,'error'));
|
|
this.bindings.loadCatalog(api.fetchApi.bind(api)).then(()=>this.scheduleCheck()).catch(e=>this.setStatus(e.message,'error'));
|
|
const oldAdopt=this.compare.options.onAdopt;
|
|
this.compare.options.onAdopt=async(...args)=>{const result=await this.history.transact(T("Adopted result"),()=>oldAdopt(...args));if(!this.closed && this.editorNode.adachiCrop?.mode==='crop')this.show('edit');return result;};
|
|
this.disposers.push(()=>{this.compare.options.onAdopt=oldAdopt;});
|
|
const oldLoad=this.compare.loadSelected;
|
|
this.compare.loadSelected=async(...args)=>{const result=await oldLoad.apply(this.compare,args);if(!this.closed){this.show(this.view||'edit');this.updateButtons();}return result;};
|
|
this.disposers.push(()=>{this.compare.loadSelected=oldLoad;});
|
|
const oldUpload=this.editor.upload;
|
|
this.editor.upload=async(...args)=>{await oldUpload(...args);if(this.editor.ready&&!this.closed){await this.history.checkpoint(T("Uploaded source"));this.syncHistory();this.scheduleCheck();}};
|
|
this.disposers.push(()=>{this.editor.upload=oldUpload;});
|
|
const onExecuted=this.compareNode.onExecuted;
|
|
this.compareNode.onExecuted=(output)=>{const ret=onExecuted?.call(this.compareNode,output);if(output?.adachi_results?.length){this.showGeneratedResult();this.note.textContent='';this.updateButtons();}return ret;};
|
|
this.disposers.push(()=>{this.compareNode.onExecuted=onExecuted;});
|
|
const event=(name,fn)=>{api.addEventListener(name,fn);this.disposers.push(()=>api.removeEventListener(name,fn));};
|
|
for(const name of ['execution_start','executing','execution_cached','progress','progress_state','execution_success','execution_error','execution_interrupted'])event(name,e=>this.taskProgress.handle(name,e.detail));
|
|
event('reconnecting',()=>this.taskProgress.connection(false));event('reconnected',()=>this.taskProgress.connection(true));
|
|
const queued=e=>{if(e.detail.sampler!==this.sampler)return;this.promptId=e.detail.promptId;this.running=!['complete','error'].includes(this.taskProgress.state);this.runButton.textContent=this.running?T("Generating…"):T("Generate");this.renderTaskProgress();this.updateButtons();};
|
|
document.addEventListener('adachi-workspace-queued',queued);this.disposers.push(()=>document.removeEventListener('adachi-workspace-queued',queued));
|
|
const recovered=e=>{if(e.detail.node!==this.compareNode || e.detail.promptId!==this.promptId)return;if(e.detail.entry.outputs?.[executionId(this.graph,this.compareNode)]?.adachi_results?.length)this.showGeneratedResult();this.taskProgress.finish(e.detail.entry.status?.status_str==='success');this.running=false;this.sampler.adachiWorkspaceTask=null;this.runButton.disabled=false;this.runButton.textContent=T("Generate");this.setStatus(e.detail.entry.status?.status_str==='success'?T("Generation complete"):T("Generation failed; check the ComfyUI task history"),e.detail.entry.status?.status_str==='success'?'ok':'error');this.updateButtons();};
|
|
document.addEventListener('adachi-result-recovered',recovered);this.disposers.push(()=>document.removeEventListener('adachi-result-recovered',recovered));
|
|
for(const name of ['execution_success','execution_error','execution_interrupted'])event(name,e=>{if(e.detail.prompt_id===this.promptId){this.running=false;this.sampler.adachiWorkspaceTask=null;this.runButton.disabled=false;this.runButton.textContent=T("Generate");this.setStatus(name==='execution_success'?T("Generation complete"):e.detail.exception_message||T("Generation stopped"),name==='execution_success'?'ok':'error');}});
|
|
this.timer=setInterval(()=>{if((app.rootGraph||app.graph)!==this.graph||this.editor.destroyed)this.close();else{this.updateButtons();if(this.pendingResultView)this.showGeneratedResult();}},500);
|
|
// The comparison's exact-task recovery also serves the workspace. A second
|
|
// history poller doubled weak-network traffic and could replay old results.
|
|
this.promptId=this.sampler.adachiWorkspaceTask;this.running=!!this.promptId;if(this.running){if(this.taskProgress.state==='idle')this.taskProgress.begin({},this.promptId);this.compareNode.adachiTrackPrompt?.(this.promptId);this.runButton.disabled=true;this.runButton.textContent=T("Generating…");}this.show('edit');this.syncHistory();this.scheduleCheck();
|
|
}
|
|
connectionPicker(){
|
|
const panel=el('details','','aw-connections');panel.open=!this.connectionsConfirmed;panel.append(el('summary',T("Workflow connections")));
|
|
if(!this.connectionsConfirmed)panel.append(el('p',T("Multiple candidates found; select the nodes to use")));
|
|
const controls={};
|
|
for(const [name,label,list,current]of [['sampler',T("Inpaint node"),this.nodes.samplers,this.sampler],['editor',T("Image editor"),this.nodes.editors,this.editorNode],['compare',T("Image comparison"),this.nodes.comparisons,this.compareNode]]){
|
|
const row=el('label',label),select=el('select');for(const node of list){const id=executionId(this.graph,node),option=el('option',`${node.title||node.type} #${id}`);option.value=id;select.append(option);}select.value=executionId(this.graph,current);row.append(select);panel.append(row);controls[name]=select;
|
|
}
|
|
const presetRow=el('label',T("Group presets")),preset=el('select'),none=el('option',T("No preset controller"));none.value='';preset.append(none);
|
|
for(const node of this.nodes.presets.filter(n=>n.mode===0)){const id=executionId(this.graph,node),option=el('option',`${node.title||node.type} #${id}`);option.value=id;preset.append(option);}preset.value=this.bindings.controller?executionId(this.graph,this.bindings.controller.node):'';presetRow.append(preset);panel.append(presetRow);
|
|
const apply=btn(T("Use these connections"),()=>{
|
|
const find=(list,value)=>list.find(node=>executionId(this.graph,node)===value),sampler=find(this.nodes.samplers,controls.sampler.value),editor=find(this.nodes.editors,controls.editor.value),compare=find(this.nodes.comparisons,controls.compare.value);
|
|
if(!sampler||!editor||!compare)return;sampler.properties.adachi_workspace_connections={editorId:executionId(this.graph,editor),compareId:executionId(this.graph,compare),controllerId:preset.value};
|
|
this.close();currentWorkspace=new Workspace(sampler,{editorNode:editor,compareNode:compare,confirmed:true});
|
|
});apply.title=T("Select nodes from this workflow; this inpaint node remembers your choices");panel.append(apply);return panel;
|
|
}
|
|
syncHistory(){if(!this.history)return;this.historySelect.replaceChildren();this.history.entries.forEach((item,i)=>{const o=el('option',`${i+1} · ${item.label}`);o.value=i;this.historySelect.append(o);});this.historySelect.value=String(this.history.index);this.undoButton.disabled=!this.history.canUndo;this.originalButton.disabled=this.history.index===0;}
|
|
updateButtons(){const busy=this.running||this.preparing||this.submitting||this.sampler?.adachiWorkspaceSubmitting;this.runButton.disabled=!!(this.running||this.submitting||this.sampler?.adachiWorkspaceSubmitting);this.adoptButton.disabled=!this.compare?.ready||!!this.compare?.busy||this.compare?.pendingOutputs>0||busy;this.tabs.compare.disabled=!this.compare?.ready;this.sideButton.disabled=!this.compare?.ready;if(!this.preparing)this.bindings?.refreshProfiles();if(this.editor)this.editor.root.inert=(!this.connectionsConfirmed)||!!this.preparing&&!this.discovering;this.settings.inert=!!this.preparing;this.params.inert=!!this.preparing;this.historySelect.disabled=this.preparing||!!this.history?.busy;this.undoButton.disabled=this.preparing||!this.history?.canUndo;this.originalButton.disabled=this.preparing||this.history?.index<=0;if(this.compare?.adoptButton)this.compare.adoptButton.disabled=this.adoptButton.disabled;}
|
|
show(which){this.view=which;if(this.editor)this.editor.root.hidden=which!=='edit';if(this.compare)this.compare.root.hidden=which!=='compare';this.still.hidden=!['before','after'].includes(which);if(!this.still.hidden){const image=which==='before'?this.compare?.before:this.compare?.after;if(image)this.still.src=image.src;}for(const [id,b]of Object.entries(this.tabs)){b.classList.toggle('active',id===which);b.setAttribute('aria-pressed',String(id===which));}this.sideButton.classList.toggle('active',['before','after'].includes(which));this.sideButton.setAttribute('aria-pressed',String(which==='before'));this.sideButton.title=which==='before'?T("Show after"):T("Show before");this.sideButton.setAttribute('aria-label',this.sideButton.title);this.editor?.schedule();this.compare?.schedule();}
|
|
async adopt(){if(!this.compare?.ready||this.running)return;await this.compare.adopt();this.syncHistory();this.updateButtons();if(!this.compare.message.classList.contains('dd-error')){this.show(this.editorNode.adachiCrop?.mode==='crop'?'edit':'compare');this.note.textContent='';this.scheduleCheck();}}
|
|
async restoreHistory(which){try{if(this.preparing||this.history?.busy)return;if(which==='undo')await this.history.undo();else if(which==='original')await this.history.original();else await this.history.restore(which);this.syncHistory();}catch(e){this.setStatus(e.message,'error');}}
|
|
scheduleCheck(){++this.checkVersion;this.checkController?.abort();clearTimeout(this.checkTimer);if(!this.closed)this.checkTimer=setTimeout(()=>this.check(),200);}
|
|
prepare(discovery=false){
|
|
const task=(this.prepareTask||Promise.resolve()).catch(()=>{}).then(async()=>{
|
|
if(this.closed||(app.rootGraph||app.graph)!==this.graph)throw Error(T("The workflow changed"));
|
|
if(!this.connectionsConfirmed&&!discovery)throw Error(T("Select the nodes to use in Workflow connections first"));
|
|
if(!this.editor?.ready&&!discovery)throw Error(T("Upload a source image first"));
|
|
this.preparing=true;this.discovering=discovery;this.updateButtons();
|
|
try{return await prepareWorkspacePrompt({editorNode:this.editorNode,samplerNode:this.sampler,compareNode:this.compareNode,allowEmpty:discovery,controller:this.bindings.controller,getBinding:(output,id)=>{this.bindings.updateExecutionGraph(output,id);return this.bindings.getExecutionBinding();}});}
|
|
finally{this.preparing=false;this.discovering=false;if(!this.closed)this.updateButtons();}
|
|
});
|
|
this.prepareTask=task;return task;
|
|
}
|
|
async check(){
|
|
if(this.closed||this.running||this.submitting||this.sampler?.adachiWorkspaceSubmitting)return false;
|
|
if(this.checking){this.recheck=true;return false;}
|
|
const version=++this.checkVersion,controller=this.checkController=new AbortController();this.checking=true;
|
|
this.setStatus(T("Checking in the background…"));
|
|
try{
|
|
const prepared=await this.prepare();
|
|
if(this.closed||this.recheck||version!==this.checkVersion)return false;
|
|
const result=await validateWorkspace(prepared,{signal:controller.signal});
|
|
if(this.closed||this.running||this.recheck||version!==this.checkVersion)return false;
|
|
if(result.valid===false||result.ok===false)throw Error(result.error?.message||result.error||T("Workflow validation failed"));
|
|
this.setStatus(T("Ready to generate"),'ok');return true;
|
|
}catch(e){if(version===this.checkVersion&&!this.running)this.setStatus(e.message||String(e),'error');return false;}
|
|
finally{this.checking=false;if(this.recheck){this.recheck=false;this.scheduleCheck();}}
|
|
}
|
|
|
|
async run() {
|
|
if(this.closed||this.running||this.submitting||this.sampler?.adachiWorkspaceSubmitting)return;
|
|
if(!this.sampler||!this.taskProgress){this.setStatus(T("Controls are not ready; reopen the workbench shortly"),'error');return;}
|
|
this.submitting=true;this.sampler.adachiWorkspaceSubmitting=true;
|
|
++this.checkVersion;this.checkController?.abort();clearTimeout(this.checkTimer);
|
|
this.runButton.disabled=true; this.runButton.textContent=T("Checking…");
|
|
const seed=this.sampler?.widgets?.find(w=>w.name==='seed'), oldSeed=seed?.value;
|
|
let queuedSeed=oldSeed;
|
|
try {
|
|
for(const widget of this.sampler.widgets || []) widget.beforeQueued?.();
|
|
queuedSeed=seed?.value;
|
|
const prepared=structuredClone(await this.prepare());
|
|
await validateWorkspace(prepared,{signal:this.sessionController.signal});
|
|
if(this.closed)return;
|
|
if((app.rootGraph||app.graph)!==this.graph||!this.sampler.graph||!this.compareNode.graph||this.editor.destroyed)throw Error(T("The workflow changed; check connections again"));
|
|
this.running=true;this.taskProgress.begin(prepared.prompt);this.promptId=null; this.updateButtons();
|
|
const queued=await submitWorkspace(prepared);
|
|
this.promptId=queued.prompt_id;this.taskProgress.attach(this.promptId);
|
|
for(const widget of this.sampler.widgets || []) widget.afterQueued?.();
|
|
this.sampler.adachiParameters.refresh();
|
|
this.running=!['complete','error'].includes(this.taskProgress.state);
|
|
this.sampler.adachiWorkspaceTask=this.running?this.promptId:null;
|
|
document.dispatchEvent(new CustomEvent('adachi-workspace-queued',{detail:{sampler:this.sampler,promptId:this.promptId}}));
|
|
this.compareNode.adachiTrackPrompt?.(this.promptId,this.graph);
|
|
if(this.closed)return;
|
|
this.runButton.textContent=this.running?T("Generating…"):T("Generate");
|
|
this.setStatus(this.running?T("Queued for generation"):this.taskProgress.label,this.running?'pending':this.taskProgress.state==='error'?'error':'ok');
|
|
} catch(e) {
|
|
this.running=false;
|
|
if(['queued','running'].includes(this.taskProgress.state))this.taskProgress.finish(false,e.message||String(e));
|
|
if(seed && seed.value===queuedSeed)seed.value=oldSeed;
|
|
this.setStatus(e.message||String(e),'error');
|
|
} finally {
|
|
this.submitting=false;this.sampler.adachiWorkspaceSubmitting=false;
|
|
if(!this.running){this.runButton.disabled=false;this.runButton.textContent=T("Generate");}
|
|
if(!this.closed)this.updateButtons();
|
|
}
|
|
}
|
|
|
|
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');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.0.0',import.meta.url).href;document.head.append(css);
|
|
document.addEventListener('adachi-open-workspace',event=>{if(!currentWorkspace)currentWorkspace=new Workspace(event.detail?.sampler||null);});
|
|
}});
|