// 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/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,
  taskCenterManager,
  toastManager
};
