The rail's Prompts entry used to fold the browser away and open Prompt Studio in a drawer by the canvas, the one page that left the window. It is now the browser's Prompts page (owner.promptPanel): the rail, the 7 key, opening the browser with a text node selected, Current node's "Edit in Prompt Studio" and a kept prompt all go there, and reopening the browser returns to it. "Beside the canvas" on the page moves the studio into the drawer as before (side and width remembered, browser folded away); the drawer has "Back to the window" and Close. Only one studio is shown at a time. The page's studio stops when another page is shown, a workspace opens or the browser closes (its 400 ms poll with it) and is made again when the page is shown; closing a workspace opened from the Prompts page returns to it. Checked live on a test ComfyUI: a CLIP Text Encode opens the page aimed at it; docking, Back to the window and Close; 3 then 7; Workflows opened and closed from the page; reopening the browser. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
5.4 KiB
JavaScript
120 lines
5.4 KiB
JavaScript
/** Main browser panel visibility and recoverable detail cleanup. */
|
|
|
|
import { stopAudioStudioPlayback } from './ui_audio_studio.js';
|
|
import { stopGalleryAudio } from './ui_audio_gallery.js';
|
|
import { leaveScanPage } from './ui_scan_page.js';
|
|
import { leavePromptPage } from './ui_prompt_composer.js';
|
|
|
|
function restoreWorkspaceReturnPanel(owner) {
|
|
const state = owner.workspaceReturnState;
|
|
owner.workspaceReturnState = null;
|
|
const panels = [
|
|
['grid', owner.grid],
|
|
['detail', owner.detailPanel],
|
|
['gallery', owner.galleryPanel],
|
|
['doctor', owner.doctorPanel],
|
|
['assistant', owner.assistantPanel],
|
|
['audioStudio', owner.audioStudioPanel],
|
|
['script', owner.scriptPanel],
|
|
['audioGallery', owner.audioGalleryPanel],
|
|
['home', owner.homePanel],
|
|
['activity', owner.activityPanel],
|
|
['prompt', owner.promptPanel],
|
|
['scan', owner.scanPanel],
|
|
['settings', owner.settingsPanel],
|
|
];
|
|
if (state) {
|
|
for (const [key, panel] of panels) {
|
|
if (panel && Object.prototype.hasOwnProperty.call(state, key)) panel.style.display = state[key];
|
|
}
|
|
}
|
|
const hasVisiblePanel = panels.some(([, panel]) => panel && panel.style.display !== 'none');
|
|
if (!hasVisiblePanel && owner.grid) owner.grid.style.display = 'grid';
|
|
}
|
|
|
|
export function closeWorkspace() {
|
|
this.recipeDetailFinish?.('closed');
|
|
const abandonedRecipeModel = typeof this.recipeModelReturn === 'function';
|
|
this.recipeModelReturn = null;
|
|
if (abandonedRecipeModel) {
|
|
this.recipeReturnState = null;
|
|
delete this.recipeDetailPayload;
|
|
if (this.recipeListContainer) this.recipeListContainer.style.display = '';
|
|
const actionbar = this.recipeView?.querySelector('.anomalous-recipe-actionbar');
|
|
if (actionbar) actionbar.style.display = '';
|
|
if (this.detailPanel) {
|
|
this.stopMediaInContainer?.(this.detailPanel);
|
|
this.detailPanel.replaceChildren();
|
|
this.detailPanel.style.display = 'none';
|
|
}
|
|
this.currentDetailModel = null;
|
|
this.historyStack = [];
|
|
}
|
|
if (this.paramPanel) this.paramPanel.style.display = 'none';
|
|
if (this.recipeView) this.recipeView.style.display = 'none';
|
|
if (this.notebookBody) this.notebookBody.style.display = 'none';
|
|
if (this.recipeContainer) this.recipeContainer.style.display = 'none';
|
|
if (this.nbPanel) this.nbPanel.style.display = 'none';
|
|
restoreWorkspaceReturnPanel(this);
|
|
}
|
|
|
|
/**
|
|
* Esc on the recipe / combo workspace does what a click beside it does:
|
|
* `closeWorkspace()`. Not while one of this plugin's dialogs or a ComfyUI dialog sits
|
|
* above it, and not while a text field has the key (Esc there belongs to the field).
|
|
* ComfyUI's own keybindings mark Esc as handled, so `defaultPrevented` says nothing here.
|
|
*/
|
|
export function bindWorkspaceEscape(owner) {
|
|
window.addEventListener('keydown', (e) => {
|
|
if (e.key !== 'Escape') return;
|
|
if (!owner.modal?.classList.contains('visible') || owner.nbPanel?.style.display !== 'flex') return;
|
|
if (e.target?.closest?.('input, textarea, select, [contenteditable="true"]')) return;
|
|
const layered = [...document.body.children, ...owner.nbPanel.children]
|
|
.some(el => [...el.classList].some(c => c.startsWith('anomalous-') && c.endsWith('overlay')) && el.getClientRects().length);
|
|
if (layered || document.querySelector('dialog[open], .p-dialog-mask')) return;
|
|
owner.closeWorkspace();
|
|
});
|
|
}
|
|
|
|
export function hideAllPanels() {
|
|
const abandonedRecipeModel = typeof this.recipeModelReturn === 'function';
|
|
this.recipeModelReturn = null;
|
|
if (abandonedRecipeModel) {
|
|
this.recipeReturnState = null;
|
|
delete this.recipeDetailPayload;
|
|
if (this.recipeListContainer) this.recipeListContainer.style.display = '';
|
|
const actionbar = this.recipeView?.querySelector('.anomalous-recipe-actionbar');
|
|
if (actionbar) actionbar.style.display = '';
|
|
if (this.detailPanel) {
|
|
this.stopMediaInContainer?.(this.detailPanel);
|
|
this.detailPanel.replaceChildren();
|
|
}
|
|
this.currentDetailModel = null;
|
|
this.historyStack = [];
|
|
}
|
|
this.grid.style.display = 'none';
|
|
this.detailPanel.style.display = 'none';
|
|
if (this.galleryPanel) this.galleryPanel.style.display = 'none';
|
|
if (this.nbPanel) this.nbPanel.style.display = 'none';
|
|
if (this.doctorPanel) this.doctorPanel.style.display = 'none';
|
|
if (this.assistantPanel) this.assistantPanel.style.display = 'none';
|
|
if (this.paramPanel) this.paramPanel.style.display = 'none';
|
|
if (this.audioStudioPanel) this.audioStudioPanel.style.display = 'none';
|
|
if (this.scriptPanel) this.scriptPanel.style.display = 'none';
|
|
if (this.audioGalleryPanel) this.audioGalleryPanel.style.display = 'none';
|
|
if (this.homePanel) this.homePanel.style.display = 'none';
|
|
if (this.activityPanel) this.activityPanel.style.display = 'none';
|
|
if (this.promptPanel) this.promptPanel.style.display = 'none';
|
|
leavePromptPage();
|
|
if (this.scanPanel) this.scanPanel.style.display = 'none';
|
|
if (this.settingsPanel) this.settingsPanel.style.display = 'none';
|
|
document.getElementById('anomalous-global-settings-btn')?.classList.remove('is-active');
|
|
leaveScanPage();
|
|
stopAudioStudioPlayback();
|
|
stopGalleryAudio();
|
|
if (this.currentDetailObserver) {
|
|
this.currentDetailObserver.disconnect();
|
|
this.currentDetailObserver = null;
|
|
}
|
|
}
|