// frontend/js/components/customSelect.js
var CustomSelect = class _CustomSelect {
  constructor(container) {
    this.container = container;
    this.trigger = this.container.querySelector(".custom-select-trigger");
    this.panel = this.container.querySelector(".custom-select-panel");
    if (!this.trigger || !this.panel) {
      console.warn("CustomSelect cannot initialize: missing .custom-select-trigger or .custom-select-panel.", this.container);
      return;
    }
    this.nativeSelect = this.container.querySelector("select");
    this.triggerText = this.trigger.querySelector("span");
    this.template = this.panel.querySelector(".custom-select-option-template");
    if (typeof _CustomSelect.openInstance === "undefined") {
      _CustomSelect.openInstance = null;
      _CustomSelect.initGlobalListener();
    }
    if (this.nativeSelect) {
      this.generateOptions();
      this.updateTriggerText();
    }
    this.bindEvents();
  }
  static initGlobalListener() {
    document.addEventListener("click", (event) => {
      if (_CustomSelect.openInstance && !_CustomSelect.openInstance.container.contains(event.target)) {
        _CustomSelect.openInstance.close();
      }
    });
  }
  generateOptions() {
    this.panel.querySelectorAll(":scope > *:not(.custom-select-option-template)").forEach((child) => child.remove());
    Array.from(this.nativeSelect.options).forEach((option) => {
      let item;
      if (this.template) {
        item = this.template.cloneNode(true);
        item.classList.remove("custom-select-option-template");
        item.removeAttribute("hidden");
      } else {
        item = document.createElement("a");
        item.href = "#";
        item.className = "block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-100 dark:text-zinc-200 dark:hover:bg-zinc-600";
      }
      item.classList.add("custom-select-option");
      item.textContent = option.textContent;
      item.dataset.value = option.value;
      if (option.selected) {
        item.classList.add("is-selected");
      }
      this.panel.appendChild(item);
    });
  }
  bindEvents() {
    this.trigger.addEventListener("click", (event) => {
      if (this.trigger.classList.contains("is-disabled")) {
        return;
      }
      event.stopPropagation();
      if (_CustomSelect.openInstance && _CustomSelect.openInstance !== this) {
        _CustomSelect.openInstance.close();
      }
      this.toggle();
    });
    if (this.nativeSelect) {
      this.panel.addEventListener("click", (event) => {
        event.preventDefault();
        const option = event.target.closest(".custom-select-option");
        if (option) {
          this.selectOption(option);
        }
      });
    }
  }
  selectOption(optionEl) {
    const selectedValue = optionEl.dataset.value;
    if (this.nativeSelect.value !== selectedValue) {
      this.nativeSelect.value = selectedValue;
      this.nativeSelect.dispatchEvent(new Event("change", { bubbles: true }));
    }
    this.updateTriggerText();
    this.panel.querySelectorAll(".custom-select-option").forEach((el) => el.classList.remove("is-selected"));
    optionEl.classList.add("is-selected");
    this.close();
  }
  updateTriggerText() {
    if (!this.nativeSelect || !this.triggerText) return;
    const selectedOption = this.nativeSelect.options[this.nativeSelect.selectedIndex];
    if (selectedOption) {
      this.triggerText.textContent = selectedOption.textContent;
    }
  }
  toggle() {
    this.panel.classList.toggle("hidden");
    if (this.panel.classList.contains("hidden")) {
      if (_CustomSelect.openInstance === this) {
        _CustomSelect.openInstance = null;
      }
    } else {
      _CustomSelect.openInstance = this;
    }
  }
  open() {
    this.panel.classList.remove("hidden");
    _CustomSelect.openInstance = this;
  }
  close() {
    this.panel.classList.add("hidden");
    if (_CustomSelect.openInstance === this) {
      _CustomSelect.openInstance = null;
    }
  }
};

// frontend/js/components/ui.js
var ModalManager = class {
  /**
   * Shows a generic modal by its ID.
   * @param {string} modalId The ID of the modal element to show.
   */
  show(modalId) {
    const modal = document.getElementById(modalId);
    if (modal) {
      modal.classList.remove("hidden");
    } else {
      console.error(`Modal with ID "${modalId}" not found.`);
    }
  }
  /**
   * Hides a generic modal by its ID.
   * @param {string} modalId The ID of the modal element to hide.
   */
  hide(modalId) {
    const modal = document.getElementById(modalId);
    if (modal) {
      modal.classList.add("hidden");
    } else {
      console.error(`Modal with ID "${modalId}" not found.`);
    }
  }
  /**
   * Shows a confirmation dialog. This is a versatile method for 'Are you sure?' style prompts.
   * It dynamically sets the title, message, and confirm action for a generic confirmation modal.
   * @param {object} options - The options for the confirmation modal.
   * @param {string} options.modalId - The ID of the confirmation modal element (e.g., 'resetModal', 'deleteConfirmModal').
   * @param {string} options.title - The title to display in the modal header.
   * @param {string} options.message - The message to display in the modal body. Can contain HTML.
   * @param {function} options.onConfirm - The callback function to execute when the confirm button is clicked.
   * @param {boolean} [options.disableConfirm=false] - Whether the confirm button should be initially disabled.
   */
  showConfirm({ modalId, title, message, onConfirm, disableConfirm = false }) {
    const modalElement = document.getElementById(modalId);
    if (!modalElement) {
      console.error(`Confirmation modal with ID "${modalId}" not found.`);
      return;
    }
    const titleElement = modalElement.querySelector('[id$="ModalTitle"]');
    const messageElement = modalElement.querySelector('[id$="ModalMessage"]');
    const confirmButton = modalElement.querySelector('[id^="confirm"]');
    if (!titleElement || !messageElement || !confirmButton) {
      console.error(`Modal "${modalId}" is missing required child elements (title, message, or confirm button).`);
      return;
    }
    titleElement.textContent = title;
    messageElement.innerHTML = message;
    confirmButton.disabled = disableConfirm;
    const newConfirmButton = confirmButton.cloneNode(true);
    confirmButton.parentNode.replaceChild(newConfirmButton, confirmButton);
    newConfirmButton.onclick = () => onConfirm();
    this.show(modalId);
  }
  /**
   * Shows a result modal to indicate the outcome of an operation (success or failure).
   * @param {boolean} success - If true, displays a success icon and title; otherwise, shows failure indicators.
   * @param {string|Node} message - The message to display. Can be a simple string or a complex DOM Node for rich content.
   * @param {boolean} [autoReload=false] - If true, the page will automatically reload when the modal is closed.
   */
  showResult(success, message, autoReload = false) {
    const modalElement = document.getElementById("resultModal");
    if (!modalElement) {
      console.error("Result modal with ID 'resultModal' not found.");
      return;
    }
    const titleElement = document.getElementById("resultModalTitle");
    const messageElement = document.getElementById("resultModalMessage");
    const iconElement = document.getElementById("resultIcon");
    const confirmButton = document.getElementById("resultModalConfirmBtn");
    if (!titleElement || !messageElement || !iconElement || !confirmButton) {
      console.error("Result modal is missing required child elements.");
      return;
    }
    titleElement.textContent = success ? "\u64CD\u4F5C\u6210\u529F" : "\u64CD\u4F5C\u5931\u8D25";
    if (success) {
      iconElement.innerHTML = '<i class="fas fa-check-circle text-success-500"></i>';
      iconElement.className = "text-6xl mb-3 text-success-500";
    } else {
      iconElement.innerHTML = '<i class="fas fa-times-circle text-danger-500"></i>';
      iconElement.className = "text-6xl mb-3 text-danger-500";
    }
    messageElement.innerHTML = "";
    if (typeof message === "string") {
      const messageDiv = document.createElement("div");
      messageDiv.innerText = message;
      messageElement.appendChild(messageDiv);
    } else if (message instanceof Node) {
      messageElement.appendChild(message);
    } else {
      const messageDiv = document.createElement("div");
      messageDiv.innerText = String(message);
      messageElement.appendChild(messageDiv);
    }
    confirmButton.onclick = () => this.closeResult(autoReload);
    this.show("resultModal");
  }
  /**
   * Closes the result modal.
   * @param {boolean} [reload=false] - If true, reloads the page after closing the modal.
   */
  closeResult(reload = false) {
    this.hide("resultModal");
    if (reload) {
      location.reload();
    }
  }
  /**
   * Shows and initializes the progress modal for long-running operations.
   * @param {string} title - The title to display for the progress modal.
   */
  showProgress(title) {
    const modal = document.getElementById("progressModal");
    if (!modal) {
      console.error("Progress modal with ID 'progressModal' not found.");
      return;
    }
    const titleElement = document.getElementById("progressModalTitle");
    const statusText = document.getElementById("progressStatusText");
    const progressBar = document.getElementById("progressBar");
    const progressPercentage = document.getElementById("progressPercentage");
    const progressLog = document.getElementById("progressLog");
    const closeButton = document.getElementById("progressModalCloseBtn");
    const closeIcon = document.getElementById("closeProgressModalBtn");
    if (!titleElement || !statusText || !progressBar || !progressPercentage || !progressLog || !closeButton || !closeIcon) {
      console.error("Progress modal is missing required child elements.");
      return;
    }
    titleElement.textContent = title;
    statusText.textContent = "\u51C6\u5907\u5F00\u59CB...";
    progressBar.style.width = "0%";
    progressPercentage.textContent = "0%";
    progressLog.innerHTML = "";
    closeButton.disabled = true;
    closeIcon.disabled = true;
    this.show("progressModal");
  }
  /**
   * Updates the progress bar and status text within the progress modal.
   * @param {number} processed - The number of items that have been processed.
   * @param {number} total - The total number of items to process.
   * @param {string} status - The current status message to display.
   */
  updateProgress(processed, total, status) {
    const modal = document.getElementById("progressModal");
    if (!modal || modal.classList.contains("hidden")) return;
    const progressBar = document.getElementById("progressBar");
    const progressPercentage = document.getElementById("progressPercentage");
    const statusText = document.getElementById("progressStatusText");
    const closeButton = document.getElementById("progressModalCloseBtn");
    const closeIcon = document.getElementById("closeProgressModalBtn");
    const percentage = total > 0 ? Math.round(processed / total * 100) : 0;
    progressBar.style.width = `${percentage}%`;
    progressPercentage.textContent = `${percentage}%`;
    statusText.textContent = status;
    if (processed === total) {
      closeButton.disabled = false;
      closeIcon.disabled = false;
    }
  }
  /**
   * Adds a log entry to the progress modal's log area.
   * @param {string} message - The log message to append.
   * @param {boolean} [isError=false] - If true, styles the log entry as an error.
   */
  addProgressLog(message, isError = false) {
    const progressLog = document.getElementById("progressLog");
    if (!progressLog) return;
    const logEntry = document.createElement("div");
    logEntry.textContent = message;
    logEntry.className = isError ? "text-danger-600" : "text-gray-700";
    progressLog.appendChild(logEntry);
    progressLog.scrollTop = progressLog.scrollHeight;
  }
  /**
   * Closes the progress modal.
   * @param {boolean} [reload=false] - If true, reloads the page after closing.
   */
  closeProgress(reload = false) {
    this.hide("progressModal");
    if (reload) {
      location.reload();
    }
  }
};
var UIPatterns = class {
  /**
   * Animates numerical values in elements from 0 to their target number.
   * The target number is read from the element's text content.
   * @param {string} selector - The CSS selector for the elements to animate (e.g., '.stat-value').
   * @param {number} [duration=1500] - The duration of the animation in milliseconds.
   */
  animateCounters(selector = ".stat-value", duration = 1500) {
    const statValues = document.querySelectorAll(selector);
    statValues.forEach((valueElement) => {
      const finalValue = parseInt(valueElement.textContent, 10);
      if (isNaN(finalValue)) return;
      if (!valueElement.dataset.originalValue) {
        valueElement.dataset.originalValue = valueElement.textContent;
      }
      let startValue = 0;
      const startTime = performance.now();
      const updateCounter = (currentTime) => {
        const elapsedTime = currentTime - startTime;
        if (elapsedTime < duration) {
          const progress = elapsedTime / duration;
          const easeOutValue = 1 - Math.pow(1 - progress, 3);
          const currentValue = Math.floor(easeOutValue * finalValue);
          valueElement.textContent = currentValue;
          requestAnimationFrame(updateCounter);
        } else {
          valueElement.textContent = valueElement.dataset.originalValue;
        }
      };
      requestAnimationFrame(updateCounter);
    });
  }
  /**
   * Toggles the visibility of a content section with a smooth height animation.
   * It expects a specific HTML structure where the header and content are within a common parent (e.g., a card).
   * The content element should have a `collapsed` class when hidden.
   * @param {HTMLElement} header - The header element that was clicked to trigger the toggle.
   */
  toggleSection(header) {
    const card = header.closest(".stats-card");
    if (!card) return;
    const content = card.querySelector(".key-content");
    const toggleIcon = header.querySelector(".toggle-icon");
    if (!content || !toggleIcon) {
      console.error("Toggle section failed: Content or icon element not found.", { header });
      return;
    }
    const isCollapsed = content.classList.contains("collapsed");
    toggleIcon.classList.toggle("collapsed", !isCollapsed);
    if (isCollapsed) {
      content.classList.remove("collapsed");
      content.style.maxHeight = null;
      content.style.opacity = null;
      content.style.paddingTop = null;
      content.style.paddingBottom = null;
      content.style.overflow = "hidden";
      requestAnimationFrame(() => {
        const targetHeight = content.scrollHeight;
        content.style.maxHeight = `${targetHeight}px`;
        content.style.opacity = "1";
        content.style.paddingTop = "1rem";
        content.style.paddingBottom = "1rem";
        content.addEventListener("transitionend", function onExpansionEnd() {
          content.removeEventListener("transitionend", onExpansionEnd);
          if (!content.classList.contains("collapsed")) {
            content.style.maxHeight = "";
            content.style.overflow = "visible";
          }
        }, { once: true });
      });
    } else {
      const currentHeight = content.scrollHeight;
      content.style.maxHeight = `${currentHeight}px`;
      content.style.overflow = "hidden";
      requestAnimationFrame(() => {
        content.style.maxHeight = "0px";
        content.style.opacity = "0";
        content.style.paddingTop = "0";
        content.style.paddingBottom = "0";
        content.classList.add("collapsed");
      });
    }
  }
};
var modalManager = new ModalManager();
var uiPatterns = new UIPatterns();

// frontend/js/components/taskCenter.js
var TaskCenterManager = class {
  constructor() {
    this.tasks = [];
    this.activePolls = /* @__PURE__ */ new Map();
    this.heartbeatInterval = null;
    this.MINIMUM_TASK_DISPLAY_TIME_MS = 800;
    this.hasUnreadCompletedTasks = false;
    this.isAnimating = false;
    this.countdownTimer = null;
    this.trigger = document.getElementById("task-hub-trigger");
    this.panel = document.getElementById("task-hub-panel");
    this.countdownBar = document.getElementById("task-hub-countdown-bar");
    this.countdownRing = document.getElementById("task-hub-countdown-ring");
    this.indicator = document.getElementById("task-hub-indicator");
    this.clearBtn = document.getElementById("task-hub-clear-btn");
    this.taskListContainer = document.getElementById("task-list-container");
    this.emptyState = document.getElementById("task-list-empty");
  }
  // [THE FINAL, DEFINITIVE VERSION]
  init() {
    if (!this.trigger || !this.panel) {
      console.warn("Task Center UI core elements not found. Initialization skipped.");
      return;
    }
    this.trigger.addEventListener("click", (event) => {
      event.stopPropagation();
      if (this.isAnimating) return;
      if (this.panel.classList.contains("hidden")) {
        this._handleUserInteraction();
        this.openPanel();
      } else {
        this.closePanel();
      }
    });
    document.addEventListener("click", (event) => {
      if (!this.panel.classList.contains("hidden") && !this.isAnimating && !this.panel.contains(event.target) && !this.trigger.contains(event.target)) {
        this.closePanel();
      }
    });
    this.trigger.addEventListener("mouseenter", this._stopCountdown.bind(this));
    this.panel.addEventListener("mouseenter", this._handleUserInteraction.bind(this));
    const handleMouseLeave = () => {
      if (!this.panel.classList.contains("hidden")) {
        this._startCountdown();
      }
    };
    this.panel.addEventListener("mouseleave", handleMouseLeave);
    this.trigger.addEventListener("mouseleave", handleMouseLeave);
    this.clearBtn?.addEventListener("click", this.clearCompletedTasks.bind(this));
    this.taskListContainer.addEventListener("click", (event) => {
      const toggleHeader = event.target.closest("[data-task-toggle]");
      if (!toggleHeader) return;
      this._handleUserInteraction();
      const taskItem = toggleHeader.closest(".task-list-item");
      const content = taskItem.querySelector("[data-task-content]");
      if (!content) return;
      const isCollapsed = content.classList.contains("collapsed");
      toggleHeader.classList.toggle("expanded", isCollapsed);
      if (isCollapsed) {
        content.classList.remove("collapsed");
        content.style.maxHeight = `${content.scrollHeight}px`;
        content.style.opacity = "1";
        content.addEventListener("transitionend", () => {
          if (!content.classList.contains("collapsed")) content.style.maxHeight = "none";
        }, { once: true });
      } else {
        content.style.maxHeight = `${content.scrollHeight}px`;
        requestAnimationFrame(() => {
          content.style.maxHeight = "0px";
          content.style.opacity = "0";
          content.classList.add("collapsed");
        });
      }
    });
    this._render();
    this._startHeartbeat();
    console.log("Task Center UI Initialized [Multi-Task Heartbeat Polling Architecture - IGNITED].");
  }
  async startTask(taskDefinition) {
    try {
      const initialTaskData = await taskDefinition.start();
      if (!initialTaskData || !initialTaskData.id) throw new Error("Task definition did not return a valid initial task object.");
      const newTask = {
        id: initialTaskData.id,
        definition: taskDefinition,
        data: initialTaskData,
        timestamp: /* @__PURE__ */ new Date(),
        startTime: Date.now()
      };
      if (!initialTaskData.is_running) {
        console.log(`[TaskCenter] Task ${newTask.id} completed synchronously. Skipping poll.`);
        taskDefinition.renderToastNarrative(newTask.data, {}, toastManager);
        this.tasks.unshift(newTask);
        this._render();
        this._handleTaskCompletion(newTask);
        return;
      }
      this.tasks.unshift(newTask);
      this.activePolls.set(newTask.id, newTask);
      this._render();
      this.openPanel();
      taskDefinition.renderToastNarrative(newTask.data, {}, toastManager);
      this._updateIndicatorState();
    } catch (error) {
      console.error("Failed to start task:", error);
      toastManager.show(`\u4EFB\u52A1\u542F\u52A8\u5931\u8D25: ${error.message}`, "error");
    }
  }
  _startHeartbeat() {
    if (this.heartbeatInterval) return;
    this.heartbeatInterval = setInterval(this._tick.bind(this), 1500);
  }
  _stopHeartbeat() {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
      this.heartbeatInterval = null;
    }
  }
  async _tick() {
    if (this.activePolls.size === 0) {
      return;
    }
    for (const taskId of [...this.activePolls.keys()]) {
      const task = this.activePolls.get(taskId);
      if (!task) continue;
      try {
        const response = await task.definition.poll(taskId);
        if (!response.success || !response.data) throw new Error(response.message || "Polling failed");
        const oldData = { ...task.data };
        task.data = response.data;
        this._updateTaskItemInHistory(task.id, task.data);
        task.definition.renderToastNarrative(task.data, oldData, toastManager);
        if (!task.data.is_running) {
          this._handleTaskCompletion(task);
        }
      } catch (error) {
        console.error(`Polling for task ${taskId} failed:`, error);
        task.data.error = error.message;
        this._updateTaskItemInHistory(task.id, task.data);
        this._handleTaskCompletion(task);
      }
    }
  }
  _handleTaskCompletion(task) {
    this.activePolls.delete(task.id);
    this._updateIndicatorState();
    const toastId = `task-${task.id}`;
    const finalize = async () => {
      await toastManager.dismiss(toastId, !task.data.error);
      this._updateTaskItemInDom(task);
      this.hasUnreadCompletedTasks = true;
      this._updateIndicatorState();
      if (task.data.error) {
        if (task.definition.onError) task.definition.onError(task.data);
      } else {
        if (task.definition.onSuccess) task.definition.onSuccess(task.data);
      }
    };
    const elapsedTime = Date.now() - task.startTime;
    const remainingTime = this.MINIMUM_TASK_DISPLAY_TIME_MS - elapsedTime;
    if (remainingTime > 0) {
      setTimeout(finalize, remainingTime);
    } else {
      finalize();
    }
  }
  // [REFACTORED for robustness]
  _updateIndicatorState() {
    const hasRunningTasks = this.activePolls.size > 0;
    const shouldBeVisible = hasRunningTasks || this.hasUnreadCompletedTasks;
    this.indicator.classList.toggle("hidden", !shouldBeVisible);
  }
  // [REFACTORED for robustness]
  clearCompletedTasks() {
    this.tasks = this.tasks.filter((task) => this.activePolls.has(task.id));
    this.hasUnreadCompletedTasks = false;
    this._render();
  }
  // [NEW SAFETY METHOD]
  _updateTaskItemInHistory(taskId, newData) {
    const taskInHistory = this.tasks.find((t) => t.id === taskId);
    if (taskInHistory) {
      taskInHistory.data = newData;
    }
  }
  // --- 渲染与DOM操作 ---
  _render() {
    this.taskListContainer.innerHTML = this.tasks.map((task) => this._createTaskItemHtml(task)).join("");
    const hasTasks = this.tasks.length > 0;
    this.taskListContainer.classList.toggle("hidden", !hasTasks);
    this.emptyState.classList.toggle("hidden", hasTasks);
    this._updateIndicatorState();
  }
  _createTaskItemHtml(task) {
    const innerHtml = task.definition.renderTaskCenterItem(task.data, task.timestamp, this._formatTimeAgo);
    return `<div class="task-list-item" data-task-id="${task.id}">${innerHtml}</div>`;
  }
  _updateTaskItemInDom(task) {
    const item = this.taskListContainer.querySelector(`[data-task-id="${task.id}"]`);
    if (item) {
      item.innerHTML = task.definition.renderTaskCenterItem(task.data, task.timestamp, this._formatTimeAgo);
    }
  }
  // --- 核心面板开关逻辑 ---
  openPanel() {
    if (this.isAnimating || !this.panel.classList.contains("hidden")) return;
    this.isAnimating = true;
    this.panel.classList.remove("hidden");
    this.panel.classList.add("animate-panel-in");
    setTimeout(() => {
      this.panel.classList.remove("animate-panel-in");
      this.isAnimating = false;
      this._startCountdown();
    }, 150);
  }
  closePanel() {
    if (this.isAnimating || this.panel.classList.contains("hidden")) return;
    this._stopCountdown();
    this.isAnimating = true;
    this.panel.classList.add("animate-panel-out");
    setTimeout(() => {
      this.panel.classList.remove("animate-panel-out");
      this.panel.classList.add("hidden");
      this.isAnimating = false;
    }, 50);
  }
  // --- [新增] 倒计时管理方法 ---
  /**
   * 启动或重启倒计时和进度条动画
   * @private
   */
  _startCountdown() {
    this._stopCountdown();
    this.countdownBar.classList.add("w-full", "duration-[4950ms]");
    this.countdownRing.style.transition = "none";
    this.countdownRing.style.strokeDashoffset = "72.26";
    void this.countdownRing.offsetHeight;
    this.countdownRing.style.transition = "stroke-dashoffset 4.95s linear";
    this.countdownRing.style.strokeDashoffset = "0";
    this.countdownTimer = setTimeout(() => {
      this.closePanel();
    }, 4950);
  }
  /**
   * 停止倒计时并重置进度条
   * @private
   */
  _stopCountdown() {
    if (this.countdownTimer) {
      clearTimeout(this.countdownTimer);
      this.countdownTimer = null;
    }
    this.countdownBar.classList.remove("w-full");
    this.countdownRing.style.transition = "none";
    this.countdownRing.style.strokeDashoffset = "72.26";
  }
  // [NEW] A central handler for any action that confirms the user has seen the panel.
  _handleUserInteraction() {
    this._stopCountdown();
    if (this.hasUnreadCompletedTasks) {
      this.hasUnreadCompletedTasks = false;
      this._updateIndicatorState();
    }
  }
  _formatTimeAgo(date) {
    if (!date) return "";
    const seconds = Math.floor((/* @__PURE__ */ new Date() - new Date(date)) / 1e3);
    if (seconds < 2) return "\u521A\u521A";
    if (seconds < 60) return `${seconds}\u79D2\u524D`;
    const minutes = Math.floor(seconds / 60);
    if (minutes < 60) return `${minutes}\u5206\u949F\u524D`;
    const hours = Math.floor(minutes / 60);
    if (hours < 24) return `${hours}\u5C0F\u65F6\u524D`;
    const days = Math.floor(hours / 24);
    return `${days}\u5929\u524D`;
  }
};
var ToastManager = class {
  constructor() {
    this.container = document.getElementById("toast-container");
    if (!this.container) {
      this.container = document.createElement("div");
      this.container.id = "toast-container";
      this.container.className = "fixed bottom-4 right-4 z-[100] w-full max-w-sm space-y-3";
      document.body.appendChild(this.container);
    }
    this.activeToasts = /* @__PURE__ */ new Map();
  }
  /**
   * 显示一个 Toast 通知
   * @param {string} message - The message to display.
   * @param {string} [type='info'] - 'info', 'success', or 'error'.
   * @param {number} [duration=4000] - Duration in milliseconds.
   */
  show(message, type = "info", duration = 4e3) {
    const toastElement = this._createToastHtml(message, type);
    this.container.appendChild(toastElement);
    requestAnimationFrame(() => {
      toastElement.classList.remove("opacity-0", "translate-y-2");
      toastElement.classList.add("opacity-100", "translate-y-0");
    });
    setTimeout(() => {
      toastElement.classList.remove("opacity-100", "translate-y-0");
      toastElement.classList.add("opacity-0", "translate-y-2");
      toastElement.addEventListener("transitionend", () => toastElement.remove(), { once: true });
    }, duration);
  }
  // [NEW] 创建或更新一个带进度条的Toast
  showProgressToast(toastId, title, message, progress) {
    if (this.activeToasts.has(toastId)) {
      const toastElement = this.activeToasts.get(toastId);
      const messageEl = toastElement.querySelector(".toast-message");
      const progressBar = toastElement.querySelector(".toast-progress-bar");
      messageEl.textContent = `${message} - ${Math.round(progress)}%`;
      anime({
        targets: progressBar,
        width: `${progress}%`,
        duration: 400,
        easing: "easeOutQuad"
      });
    } else {
      const toastElement = this._createProgressToastHtml(toastId, title, message, progress);
      this.container.appendChild(toastElement);
      this.activeToasts.set(toastId, toastElement);
      requestAnimationFrame(() => {
        toastElement.classList.remove("opacity-0", "translate-x-full");
        toastElement.classList.add("opacity-100", "translate-x-0");
      });
    }
  }
  // [NEW] 移除一个进度Toast
  dismiss(toastId, success = null) {
    return new Promise((resolve) => {
      if (!this.activeToasts.has(toastId)) {
        resolve();
        return;
      }
      const toastElement = this.activeToasts.get(toastId);
      const performFadeOut = () => {
        toastElement.classList.remove("opacity-100", "translate-x-0");
        toastElement.classList.add("opacity-0", "translate-x-full");
        toastElement.addEventListener("transitionend", () => {
          toastElement.remove();
          this.activeToasts.delete(toastId);
          resolve();
        }, { once: true });
      };
      if (success === null) {
        performFadeOut();
      } else {
        const iconContainer = toastElement.querySelector(".toast-icon");
        const messageEl = toastElement.querySelector(".toast-message");
        if (success) {
          const progressBar = toastElement.querySelector(".toast-progress-bar");
          messageEl.textContent = "\u5DF2\u5B8C\u6210";
          anime({
            targets: progressBar,
            width: "100%",
            duration: 300,
            easing: "easeOutQuad",
            complete: () => {
              iconContainer.innerHTML = `<i class="fas fa-check-circle text-white"></i>`;
              iconContainer.className = `toast-icon bg-green-500`;
              setTimeout(performFadeOut, 900);
            }
          });
        } else {
          iconContainer.innerHTML = `<i class="fas fa-times-circle text-white"></i>`;
          iconContainer.className = `toast-icon bg-red-500`;
          messageEl.textContent = "\u5931\u8D25";
          setTimeout(performFadeOut, 1200);
        }
      }
    });
  }
  _createToastHtml(message, type) {
    const icons = {
      info: { class: "bg-blue-500", icon: "fa-info-circle" },
      success: { class: "bg-green-500", icon: "fa-check-circle" },
      error: { class: "bg-red-500", icon: "fa-exclamation-triangle" }
    };
    const typeInfo = icons[type] || icons.info;
    const toast = document.createElement("div");
    toast.className = "toast-item opacity-0 translate-y-2 transition-all duration-300 ease-out";
    toast.innerHTML = `
            <div class="toast-icon ${typeInfo.class}">
                <i class="fas ${typeInfo.icon}"></i>
            </div>
            <div class="toast-content">
                <p class="toast-title">${this._capitalizeFirstLetter(type)}</p>
                <p class="toast-message">${message}</p>
            </div>
        `;
    return toast;
  }
  // [NEW] 创建带进度条Toast的HTML结构
  _createProgressToastHtml(toastId, title, message, progress) {
    const toast = document.createElement("div");
    toast.className = "toast-item opacity-0 translate-x-full transition-all duration-300 ease-out";
    toast.dataset.toastId = toastId;
    toast.innerHTML = `
            <div class="toast-icon bg-blue-500">
                <i class="fas fa-spinner animate-spin"></i>
            </div>
            <div class="toast-content">
                <p class="toast-title">${title}</p>
                <p class="toast-message">${message} - ${Math.round(progress)}%</p>
                <div class="w-full bg-slate-200 dark:bg-zinc-700 rounded-full h-1 mt-1.5 overflow-hidden">
                    <div class="toast-progress-bar bg-blue-500 h-1 rounded-full" style="width: ${progress}%;"></div>
                </div>
            </div>
        `;
    return toast;
  }
  _capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
  }
};
var taskCenterManager = new TaskCenterManager();
var toastManager = new ToastManager();

export {
  CustomSelect,
  modalManager,
  uiPatterns,
  taskCenterManager,
  toastManager
};
