Files
2026-01-10 17:38:47 +08:00

912 lines
34 KiB
JavaScript

/**
* 弹窗工具类
* 提供通用的弹窗管理功能
*/
import { logger } from './logger.js';
import { UIToolkit } from "./UIToolkit.js";
import { promptAssistant } from "../modules/PromptAssistant.js";
import { ResourceManager } from './resourceManager.js';
class PopupManager {
// 保存当前活动弹窗及其信息
static activePopup = null;
static activePopupInfo = null;
static eventHandlers = {
mousedown: null,
keydown: null,
focus: null,
mousemove: null,
mouseup: null
};
/**
* 显示弹窗,确保同时只有一个弹窗显示
*/
static async showPopup(options) {
const { popup, anchorButton, buttonInfo, onClose, preventCloseOnElementTypes = [], enableResize = false } = options;
// Close any open context menus first
const { buttonMenu } = await import('../services/btnMenu.js');
buttonMenu.hideMenu();
// 如果已有其他弹窗,先关闭它
if (this.activePopup && this.activePopup !== popup) {
// 【关键】标记正在切换弹窗
this._isTransitioning = true;
// 清除当前活动弹窗引用,避免hidePopup中的状态冲突
this.activePopup = null;
this.activePopupInfo = null;
// 清理所有事件监听器
this.cleanupAllEventListeners();
// 关闭旧弹窗
if (oldPopupInfo && oldPopupInfo.onClose) {
try {
oldPopupInfo.onClose();
} catch (error) {
logger.error(`执行关闭回调失败: ${error.message}`);
}
}
// 添加关闭动画
const isPopupUp = oldPopup.classList.contains('popup-up');
oldPopup.classList.add(isPopupUp ? 'popup-closing-up' : 'popup-closing-down');
// 等待动画完成后再显示新弹窗
return new Promise(resolve => {
setTimeout(() => {
if (oldPopup.parentNode) {
oldPopup.parentNode.removeChild(oldPopup);
}
// 显示新弹窗
this._showNewPopup(options);
// 清除切换标记
this._isTransitioning = false;
resolve();
}, 200);
});
} else {
// 没有活动弹窗,直接显示新弹窗
return Promise.resolve(this._showNewPopup(options));
}
}
/**
* 显示新弹窗的内部方法
*/
static _showNewPopup(options) {
const { popup, anchorButton, buttonInfo, onClose, preventCloseOnElementTypes = [], enableResize = false } = options;
// 在所有其他窗口关闭后,设置活动按钮状态
if (buttonInfo) {
UIToolkit.setActiveButton(buttonInfo);
}
// 保存当前弹窗信息
this.activePopup = popup;
this.activePopupInfo = {
anchorButton,
buttonInfo,
onClose,
preventCloseOnElementTypes
};
// 计算弹窗位置
this.positionPopup(popup, anchorButton);
// 添加到文档
document.body.appendChild(popup);
// 设置关闭事件
this.setupCloseEvents(popup, () => {
this.hidePopup(popup, onClose);
});
// 设置拖动事件
this.setupDragEvents(popup);
// 如果启用窗口大小调节,在弹窗完全初始化后设置
if (enableResize) {
// 使用 setTimeout 确保在 DOM 完全渲染后再添加调节功能
setTimeout(() => {
this.setupResizeEvents(popup);
}, 0);
}
// 返回弹窗元素
return popup;
}
/**
* 清理所有事件监听器
*/
static cleanupAllEventListeners() {
// 清理全局事件监听器
Object.entries(this.eventHandlers).forEach(([event, handler]) => {
if (handler) {
try {
if (event === 'focus') {
document.removeEventListener(event, handler, true);
} else {
document.removeEventListener(event, handler);
}
} catch (error) {
logger.error(`移除事件监听器失败: ${event}, ${error.message}`);
}
this.eventHandlers[event] = null;
}
});
// 恢复原始的选择变化事件处理函数
if (window.app?.canvas && this.activePopup?._originalOnSelectionChange) {
window.app.canvas.onSelectionChange = this.activePopup._originalOnSelectionChange;
this.activePopup._originalOnSelectionChange = null;
}
// 执行弹窗自身的清理函数
if (this.activePopup && typeof this.activePopup._cleanup === 'function') {
try {
this.activePopup._cleanup();
} catch (error) {
logger.error(`执行弹窗清理函数失败: ${error.message}`);
}
}
// 确保所有事件处理函数引用都被重置
this.eventHandlers = {
mousedown: null,
keydown: null,
focus: null,
mousemove: null,
mouseup: null
};
// logger.debug('弹窗管理 | 动作:清理所有事件监听器');
}
/**
* 设置拖动事件
*/
static setupDragEvents(popup) {
const titleBar = popup.querySelector('.popup_title_bar');
if (!titleBar) return;
// 设置鼠标样式,表示可拖动
titleBar.style.cursor = 'move';
// 拖动相关变量
let isDragging = false;
let dragOffset = { x: 0, y: 0 };
const handleMouseDown = (e) => {
// 如果点击的是按钮、输入框或调节手柄,不启动拖动
if (e.target.closest('.popup_btn') ||
e.target.closest('.popup_action_btn') ||
e.target.closest('.popup_resize_handle') ||
e.target.tagName === 'INPUT' ||
e.target.closest('input')) {
return;
}
// 如果点击的是标题栏或标题文本,允许拖动
if (e.target.closest('.popup_title') || e.target === titleBar || e.target.closest('.popup_search_container')) {
e.preventDefault();
isDragging = true;
// 添加拖动状态类
popup.classList.add('dragging');
titleBar.classList.add('dragging');
// 计算鼠标点击位置与弹窗左上角的偏移
// 使用强制回流确保获取准确的位置信息
void popup.offsetWidth; // 强制回流
const rect = popup.getBoundingClientRect();
dragOffset = {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
// 添加拖动时的样式
popup.style.transition = 'none';
titleBar.style.cursor = 'grabbing';
// 添加临时的全局事件监听器
document.addEventListener('mousemove', handleMouseMove, { passive: false });
document.addEventListener('mouseup', handleMouseUp, { once: true });
}
};
const handleMouseMove = (e) => {
if (!isDragging) return;
e.preventDefault();
// 计算新位置,确保不超出视窗边界
const newLeft = Math.max(0, Math.min(
e.clientX - dragOffset.x,
window.innerWidth - popup.offsetWidth
));
const newTop = Math.max(0, Math.min(
e.clientY - dragOffset.y,
window.innerHeight - popup.offsetHeight
));
// 更新弹窗位置
popup.style.left = `${newLeft}px`;
popup.style.top = `${newTop}px`;
};
const handleMouseUp = () => {
if (!isDragging) return;
isDragging = false;
// 移除拖动状态类
popup.classList.remove('dragging');
titleBar.classList.remove('dragging');
titleBar.style.cursor = 'move';
popup.style.transition = ''; // 恢复过渡效果
// 移除临时的事件监听器
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
// 只添加初始的mousedown事件监听
titleBar.addEventListener('mousedown', handleMouseDown);
// 改进清理函数的保存方式,避免覆盖
if (!popup._cleanupFunctions) {
popup._cleanupFunctions = [];
}
popup._cleanupFunctions.push(() => {
titleBar.removeEventListener('mousedown', handleMouseDown);
// 确保清理可能残留的事件监听器
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
});
// 如果还没有总的清理函数,创建一个
if (!popup._cleanup) {
popup._cleanup = () => {
if (popup._cleanupFunctions) {
popup._cleanupFunctions.forEach(cleanup => cleanup());
popup._cleanupFunctions = [];
}
};
}
}
/**
* 设置窗口大小调节事件
*/
static setupResizeEvents(popup) {
// ---窗口大小调节功能---
// 检查是否已经有调节手柄,避免重复创建
if (popup.querySelector('.popup_resize_handle')) {
return;
}
// 创建右下角拖拽手柄
const resizeHandle = document.createElement('div');
resizeHandle.className = 'popup_resize_handle';
// 添加调节手柄图标
const handleIcon = ResourceManager.getIcon('icon-resize-handle.svg');
if (handleIcon) {
resizeHandle.appendChild(handleIcon);
}
popup.appendChild(resizeHandle);
// 强制回流确保手柄正确渲染
void resizeHandle.offsetWidth;
// 窗口大小调节相关变量
let isResizing = false;
let startX, startY, startWidth, startHeight;
const minWidth = 300; // 最小宽度
const minHeight = 200; // 最小高度
const handleResizeStart = (e) => {
// 阻止事件冒泡到拖动处理器
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
isResizing = true;
startX = e.clientX;
startY = e.clientY;
// 强制回流确保获取准确尺寸
void popup.offsetWidth;
const rect = popup.getBoundingClientRect();
startWidth = rect.width;
startHeight = rect.height;
// 添加调节状态类
popup.classList.add('resizing');
// 禁用过渡效果
popup.style.transition = 'none';
// 阻止文本选择
document.body.style.userSelect = 'none';
document.body.style.cursor = 'nw-resize';
// 添加临时的全局事件监听器
document.addEventListener('mousemove', handleResizeMove, { passive: false, capture: true });
document.addEventListener('mouseup', handleResizeEnd, { once: true, capture: true });
logger.debug('开始调节窗口大小');
};
const handleResizeMove = (e) => {
if (!isResizing) return;
e.preventDefault();
e.stopPropagation();
// 计算新的尺寸
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
let newWidth = Math.max(minWidth, startWidth + deltaX);
let newHeight = Math.max(minHeight, startHeight + deltaY);
// 确保不超出视窗边界
const rect = popup.getBoundingClientRect();
const maxWidth = window.innerWidth - rect.left - 8;
const maxHeight = window.innerHeight - rect.top - 8;
newWidth = Math.min(newWidth, maxWidth);
newHeight = Math.min(newHeight, maxHeight);
// 应用新尺寸
popup.style.width = `${newWidth}px`;
popup.style.height = `${newHeight}px`;
};
const handleResizeEnd = (e) => {
if (!isResizing) return;
isResizing = false;
// 移除调节状态类
popup.classList.remove('resizing');
// 恢复过渡效果
popup.style.transition = '';
// 恢复默认样式
document.body.style.userSelect = '';
document.body.style.cursor = '';
// 移除临时的事件监听器
document.removeEventListener('mousemove', handleResizeMove, { capture: true });
document.removeEventListener('mouseup', handleResizeEnd, { capture: true });
// 保存标签弹窗的尺寸
if (popup.classList.contains('tag_popup')) {
const rect = popup.getBoundingClientRect();
// 动态导入 TagManager 并保存尺寸
import('../modules/tag.js').then(({ TagManager }) => {
TagManager.setPopupSize(rect.width, rect.height);
}).catch(err => {
logger.error(`保存窗口大小失败: ${err.message}`);
});
}
logger.debug('完成调节窗口大小');
};
// 添加初始的mousedown事件监听,使用 capture 模式确保优先处理
resizeHandle.addEventListener('mousedown', handleResizeStart, { capture: true });
// 添加鼠标悬停效果来提高可见性
resizeHandle.addEventListener('mouseenter', () => {
resizeHandle.style.opacity = '1';
});
resizeHandle.addEventListener('mouseleave', () => {
if (!isResizing) {
resizeHandle.style.opacity = '0.7';
}
});
// 改进清理函数的保存方式,避免覆盖
if (!popup._cleanupFunctions) {
popup._cleanupFunctions = [];
}
popup._cleanupFunctions.push(() => {
resizeHandle.removeEventListener('mousedown', handleResizeStart, { capture: true });
resizeHandle.removeEventListener('mouseenter', () => { });
resizeHandle.removeEventListener('mouseleave', () => { });
// 确保清理可能残留的事件监听器
document.removeEventListener('mousemove', handleResizeMove, { capture: true });
document.removeEventListener('mouseup', handleResizeEnd, { capture: true });
// 恢复默认样式
document.body.style.userSelect = '';
document.body.style.cursor = '';
});
// 如果还没有总的清理函数,创建一个
if (!popup._cleanup) {
popup._cleanup = () => {
if (popup._cleanupFunctions) {
popup._cleanupFunctions.forEach(cleanup => cleanup());
popup._cleanupFunctions = [];
}
};
}
// logger.debug('窗口大小调节功能已启用');
}
/**
* 定位弹窗
*/
static positionPopup(popup, anchorButton) {
try {
const buttonRect = anchorButton.getBoundingClientRect();
const viewportHeight = window.innerHeight;
const viewportWidth = window.innerWidth;
// 设置初始显示以获取尺寸
popup.style.visibility = 'hidden';
popup.style.display = 'flex'; // 使用flex布局
popup.style.flexDirection = 'column';
document.body.appendChild(popup);
// 强制回流以确保获取准确的尺寸
void popup.offsetWidth;
const popupRect = popup.getBoundingClientRect();
document.body.removeChild(popup);
popup.style.visibility = 'visible';
// 计算垂直位置
const spaceAbove = buttonRect.top;
const spaceBelow = viewportHeight - buttonRect.bottom;
const showBelow = spaceBelow >= popupRect.height || spaceBelow > spaceAbove;
// 设置垂直位置
if (showBelow) {
popup.style.top = `${buttonRect.bottom}px`;
popup.classList.remove('popup-up');
popup.classList.add('popup-down');
} else {
popup.style.top = `${buttonRect.top - popupRect.height}px`;
popup.classList.remove('popup-down');
popup.classList.add('popup-up');
}
// 计算水平位置
const buttonCenterX = buttonRect.left + buttonRect.width / 2;
const centeredLeftEdge = buttonCenterX - popupRect.width / 2;
const centeredRightEdge = buttonCenterX + popupRect.width / 2;
const canCenterHorizontally = centeredLeftEdge >= 0 && centeredRightEdge <= viewportWidth;
if (showBelow) {
// 下方显示时,依然居中
if (canCenterHorizontally) {
popup.style.left = `${centeredLeftEdge}px`;
} else if (centeredLeftEdge < 0) {
popup.style.left = '8px';
} else {
popup.style.left = `${viewportWidth - popupRect.width - 8}px`;
}
} else {
// 上方显示时,左对齐(弹窗左侧与按钮左侧对齐),但保证不超出右侧和左侧
let left = buttonRect.left;
if (left + popupRect.width > viewportWidth - 8) {
left = viewportWidth - popupRect.width - 8;
}
if (left < 8) {
left = 8;
}
popup.style.left = `${left}px`;
}
} catch (error) {
logger.error(`弹窗 | 位置计算失败 | 错误:${error.message}`);
}
}
/**
* 设置关闭事件
*/
static setupCloseEvents(popup, onClose) {
// 清理之前的事件监听器
this.cleanupAllEventListeners();
// 保存原始的选择变化事件处理函数
if (window.app?.canvas) {
popup._originalOnSelectionChange = window.app.canvas.onSelectionChange;
// 重写选择变化事件处理函数
window.app.canvas.onSelectionChange = (...args) => {
// 如果弹窗已经不存在,恢复原始处理函数
if (!popup.isConnected) {
if (popup._originalOnSelectionChange) {
window.app.canvas.onSelectionChange = popup._originalOnSelectionChange;
}
return;
}
// 调用原始处理函数
if (popup._originalOnSelectionChange) {
popup._originalOnSelectionChange.apply(window.app.canvas, args);
}
// 获取当前聚焦的元素
const activeElement = document.activeElement;
// 检查是否为标签相关的输入框获得焦点
const preventCloseTypes = this.activePopupInfo?.preventCloseOnElementTypes || [];
const isSpecialInput = activeElement &&
preventCloseTypes.some(type => {
return activeElement.classList.contains(type) ||
activeElement.closest(`.${type}`) !== null;
});
// 检查是否为目标输入框
const isTargetInput = activeElement && (
activeElement.classList.contains('comfy-multiline-input') ||
activeElement.closest('.comfy-multiline-input') !== null ||
activeElement.closest('.comfy-markdown') !== null ||
activeElement.closest('.widget-markdown') !== null ||
activeElement.closest('.tiptap') !== null ||
activeElement.closest('.ProseMirror') !== null
);
// 检查是否是当前节点的输入框
const isCurrentNodeInput = this.activePopupInfo?.buttonInfo?.widget &&
Object.values(window.PromptAssistantInputWidgetMap || {}).some(mapping => {
return (mapping.inputEl === activeElement || mapping.inputEl?.contains(activeElement)) &&
mapping.widget === this.activePopupInfo.buttonInfo.widget;
});
// 只有当是特殊输入框或当前节点的输入框时才阻止关闭
if (isSpecialInput || isCurrentNodeInput || isTargetInput) {
logger.debug('弹窗 | 保持打开 | 原因:' + (isSpecialInput ? '特殊输入框聚焦' : (isCurrentNodeInput ? '当前节点输入框聚焦' : '目标输入框聚焦')));
return;
}
// 其他情况关闭弹窗
if (typeof onClose === 'function') {
onClose();
}
};
}
// 记录鼠标点击时间,用于避免双击问题
popup._lastClickTime = 0;
// 点击外部关闭
const handleOutsideClick = (e) => {
// 如果弹窗已经不存在,移除事件监听
if (!popup.isConnected) {
document.removeEventListener('mousedown', handleOutsideClick);
return;
}
// 防止快速连续点击导致的问题
const now = Date.now();
if (now - popup._lastClickTime < 200) {
return;
}
popup._lastClickTime = now;
// 检查点击目标是否在弹窗内或是触发按钮
const isInPopup = popup.contains(e.target);
const isInButton = e.target.closest('.prompt-assistant-button');
const isInActiveButton = this.activePopupInfo?.anchorButton === e.target ||
this.activePopupInfo?.anchorButton?.contains(e.target);
// 检查是否点击的是需要阻止关闭的元素类型
const preventCloseTypes = this.activePopupInfo?.preventCloseOnElementTypes || [];
const isPreventCloseElement = preventCloseTypes.some(className => {
return e.target.classList.contains(className) ||
e.target.closest(`.${className}`) !== null;
});
// 检查是否点击的是上下文菜单、确认弹窗或设置对话框
const isContextMenu = e.target.closest('.pa-context-menu') || e.target.closest('.pa-confirm-popup');
const isSettingsModal = e.target.closest('.settings-modal') || e.target.closest('.settings-modal-overlay');
if (isContextMenu || isSettingsModal) return;
// 检查是否点击的是目标输入框
const isTargetInput = e.target.classList.contains('comfy-multiline-input') ||
e.target.closest('.comfy-multiline-input') !== null ||
e.target.closest('.comfy-markdown') !== null ||
e.target.closest('.widget-markdown') !== null ||
e.target.closest('.tiptap') !== null ||
e.target.closest('.ProseMirror') !== null;
// 检查是否是当前节点的输入框
const isCurrentNodeInput = this.activePopupInfo?.buttonInfo?.widget &&
Object.values(window.PromptAssistantInputWidgetMap || {}).some(mapping => {
return (mapping.inputEl === e.target || mapping.inputEl?.contains(e.target)) &&
mapping.widget === this.activePopupInfo.buttonInfo.widget;
});
// 如果点击在弹窗外且不是点击激活按钮,且不是阻止关闭的元素,且不是当前节点的输入框/目标输入框,则关闭弹窗
if (!isInPopup && !(isInButton && isInActiveButton) &&
!isPreventCloseElement && !isCurrentNodeInput && !isTargetInput) {
if (typeof onClose === 'function') {
onClose();
}
}
};
// 添加聚焦事件监听,防止标签相关的输入框聚焦导致弹窗关闭
const handleFocus = (e) => {
// 如果弹窗已经不存在,移除事件监听
if (!popup.isConnected) {
document.removeEventListener('focus', handleFocus, true);
return;
}
const preventCloseTypes = this.activePopupInfo?.preventCloseOnElementTypes || [];
const isSpecialInput = preventCloseTypes.some(type => {
return e.target.classList.contains(type) ||
e.target.closest(`.${type}`) !== null;
});
// 检查是否是当前节点的输入框
const isCurrentNodeInput = this.activePopupInfo?.buttonInfo?.widget &&
Object.values(window.PromptAssistantInputWidgetMap || {}).some(mapping => {
return (mapping.inputEl === e.target || mapping.inputEl?.contains(e.target)) &&
mapping.widget === this.activePopupInfo.buttonInfo.widget;
});
// 只有当是特殊输入框或当前节点的输入框获得焦点时,才阻止关闭
if (isSpecialInput || isCurrentNodeInput) {
e.stopPropagation();
logger.debug('弹窗 | 阻止关闭 | 原因:' + (isSpecialInput ? '特殊输入框聚焦' : '当前节点输入框聚焦'));
}
};
// ESC键关闭
const handleEscKey = (e) => {
// 如果弹窗已经不存在,移除事件监听
if (!popup.isConnected) {
document.removeEventListener('keydown', handleEscKey);
return;
}
if (e.key === 'Escape') {
// 如果存在设置对话框,不关闭标签弹窗(让设置对话框处理 ESC)
const settingsModal = document.querySelector('.settings-modal');
if (settingsModal) return;
if (typeof onClose === 'function') {
onClose();
}
}
};
// 保存事件处理函数引用
this.eventHandlers.mousedown = handleOutsideClick;
this.eventHandlers.keydown = handleEscKey;
this.eventHandlers.focus = handleFocus;
// 确保移除可能存在的旧事件监听器
try {
document.removeEventListener('mousedown', this.eventHandlers.mousedown);
document.removeEventListener('keydown', this.eventHandlers.keydown);
document.removeEventListener('focus', this.eventHandlers.focus, true);
} catch (error) {
// 忽略可能的错误
}
// 添加事件监听
document.addEventListener('mousedown', handleOutsideClick);
document.addEventListener('keydown', handleEscKey);
document.addEventListener('focus', handleFocus, true); // 使用捕获模式
// 保存清理函数到弹窗实例
popup._cleanup = () => {
document.removeEventListener('mousedown', handleOutsideClick);
document.removeEventListener('keydown', handleEscKey);
document.removeEventListener('focus', handleFocus, true);
// 恢复原始的选择变化事件处理函数
if (window.app?.canvas && popup._originalOnSelectionChange) {
window.app.canvas.onSelectionChange = popup._originalOnSelectionChange;
popup._originalOnSelectionChange = null;
}
};
// logger.debug('弹窗管理 | 动作:设置关闭事件');
}
/**
* 隐藏弹窗
*/
static hidePopup(popup, onClose) {
if (!popup) return;
// 如果是当前活动弹窗,则清除引用
if (this.activePopup === popup) {
// 保存信息用于下面的回调
const popupInfo = this.activePopupInfo;
// 执行弹窗自身的清理函数
if (popup._cleanup && typeof popup._cleanup === 'function') {
try {
popup._cleanup();
popup._cleanup = null; // 防止重复调用
} catch (error) {
logger.error(`执行弹窗清理函数失败: ${error.message}`);
}
}
// 清除活跃弹窗引用
this.activePopup = null;
this.activePopupInfo = null;
// 清理所有事件监听器
this.cleanupAllEventListeners();
// 重置中央按钮状态
if (popupInfo && popupInfo.buttonInfo) {
const { widget, buttonId } = popupInfo.buttonInfo;
// 无论按钮状态如何,都强制重置
UIToolkit.setActiveButton(null);
// 强制更新所有实例的可见性
promptAssistant.updateAllInstancesVisibility();
// 强制更新当前widget的状态
if (widget) {
promptAssistant.updateAssistantVisibility(widget);
}
}
// 即使没有buttonInfo,也尝试更新所有实例状态
if (!popupInfo || !popupInfo.buttonInfo) {
UIToolkit.setActiveButton(null);
promptAssistant.updateAllInstancesVisibility();
}
// 执行关闭回调
if (typeof onClose === 'function') {
try {
onClose();
} catch (error) {
logger.error(`执行关闭回调失败: ${error.message}`);
}
}
}
// 添加关闭动画
const isPopupUp = popup.classList.contains('popup-up');
popup.classList.add(isPopupUp ? 'popup-closing-up' : 'popup-closing-down');
// 动画结束后移除元素
return new Promise(resolve => {
setTimeout(() => {
if (popup.parentNode) {
popup.parentNode.removeChild(popup);
}
resolve();
}, 200);
});
}
/**
* 关闭所有打开的弹窗
*/
static async closeAllPopups() {
if (this.activePopup) {
// 保存当前活动弹窗信息
const popupInfo = this.activePopupInfo;
const activePopup = this.activePopup;
// 执行弹窗自身的清理函数
if (activePopup._cleanup && typeof activePopup._cleanup === 'function') {
try {
activePopup._cleanup();
activePopup._cleanup = null; // 防止重复调用
} catch (error) {
logger.error(`执行弹窗清理函数失败: ${error.message}`);
}
}
// 清除活跃弹窗引用
this.activePopup = null;
this.activePopupInfo = null;
// 清理所有事件监听器
this.cleanupAllEventListeners();
// 重置中央按钮状态
if (popupInfo && popupInfo.buttonInfo) {
const { widget, buttonId } = popupInfo.buttonInfo;
// 无论按钮状态如何,都强制重置
UIToolkit.setActiveButton(null);
// 强制更新所有实例的可见性
promptAssistant.updateAllInstancesVisibility();
// 强制更新当前widget的状态
if (widget) {
promptAssistant.updateAssistantVisibility(widget);
}
}
// 即使没有buttonInfo,也尝试更新所有实例状态
if (!popupInfo || !popupInfo.buttonInfo) {
UIToolkit.setActiveButton(null);
promptAssistant.updateAllInstancesVisibility();
}
// 执行关闭回调
if (popupInfo && popupInfo.onClose) {
try {
popupInfo.onClose();
} catch (error) {
logger.error(`执行关闭回调失败: ${error.message}`);
}
}
}
// 获取所有弹窗
const popups = document.querySelectorAll('.popup_container');
const closePromises = [];
// 为所有弹窗添加关闭动画
popups.forEach(popup => {
// 执行弹窗自身的清理函数
if (popup._cleanup && typeof popup._cleanup === 'function') {
try {
popup._cleanup();
popup._cleanup = null; // 防止重复调用
} catch (error) {
logger.error(`执行弹窗清理函数失败: ${error.message}`);
}
}
const isPopupUp = popup.classList.contains('popup-up');
popup.classList.add(isPopupUp ? 'popup-closing-up' : 'popup-closing-down');
// 添加到Promise数组
closePromises.push(new Promise(resolve => {
setTimeout(() => {
if (popup.parentNode) {
popup.parentNode.removeChild(popup);
}
resolve();
}, 200);
}));
});
// 等待所有弹窗关闭完成
await Promise.all(closePromises);
logger.debug('弹窗管理 | 动作:关闭所有弹窗');
}
/**
* 写入内容到输入框
*/
static writeToInput(content, nodeId, inputId) {
return UIToolkit.writeToInput(content, nodeId, inputId, { highlight: true, focus: false });
}
/**
* 在光标位置插入内容
*/
static insertAtCursor(content, nodeId, inputId) {
return UIToolkit.insertAtCursor(content, nodeId, inputId, { highlight: true });
}
}
export { PopupManager };