/** 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); } // The back buttons of the pages one level down: a model's details, one combo, one recipe. const BACK_BUTTONS = '.anomalous-detail-back, .anomalous-nb-back, .anomalous-recipe-back-tab'; /** * Esc on the browser: a page one level down (a model's details, one combo or one recipe) goes * back as its own back button does; any other page closes the browser. Not while one of this * plugin's dialogs, overlays or menus, or a ComfyUI dialog, sits above it (they close * themselves), and not while a text field has the key (Esc there belongs to the field). It * listens first (capture), so a dialog that closes on this same Esc still counts as above. * ComfyUI's own keybindings mark Esc as handled, so `defaultPrevented` says nothing here. */ export function bindBrowserEscape(owner) { window.addEventListener('keydown', (e) => { if (e.key !== 'Escape' || !owner.modal?.classList.contains('visible')) return; if (e.target?.closest?.('input, textarea, select, [contenteditable="true"]')) return; const layered = [...document.body.children, ...(owner.nbPanel?.children || [])] .some(el => el !== owner.modal && el.getClientRects().length && (el.getAttribute('role') === 'menu' || [...el.classList].some(c => c.startsWith('anomalous-') && c.endsWith('overlay')))); if (layered || document.querySelector('dialog[open], .p-dialog-mask')) return; const back = [...owner.modal.querySelectorAll(BACK_BUTTONS)].find(button => button.getClientRects().length); if (back) back.click(); else owner.close(); }, true); } 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; } }