// PNG 序列播放工具 - 主逻辑
// 负责动画播放、帧管理等功能

(function () {
  // 调试日志开关
  const DEBUG = true;
  function log(...args) {
    if (DEBUG) console.log("[SeqAniPlayer]", ...args);
  }
  function logError(...args) {
    console.error("[SeqAniPlayer ERROR]", ...args);
  }
  function logWarn(...args) {
    console.warn("[SeqAniPlayer WARN]", ...args);
  }

  const foldersApi = "http://localhost:3000/api/folders";

  // 获取当前登录用户名
  let currentUsername = null;
  function getCurrentUsername() {
    if (currentUsername) {
      return currentUsername;
    }
    try {
      const loginDataStr = localStorage.getItem('loginData');
      if (!loginDataStr) {
        return null;
      }
      
      const loginData = JSON.parse(loginDataStr);
      const now = Date.now();
      
      // 检查是否过期
      if (now >= loginData.expireTime) {
        localStorage.removeItem('loginData');
        return null;
      }
      
      currentUsername = loginData.user ? loginData.user.username : null;
      return currentUsername;
    } catch (error) {
      console.error('[SeqAniPlayer] 获取用户名失败:', error);
      return null;
    }
  }

  const fpsSlider = document.getElementById("fpsSlider");
  const fpsValue = document.getElementById("fpsValue");
  const cardsGrid = document.getElementById("cardsGrid");
  const cardCountLabel = document.getElementById("cardCount");
  const folderNameLabel = document.getElementById("folderName");
  const folderCounterLabel = document.getElementById("folderCounter");
  const frameCounterLabel = document.getElementById("frameCounter");
  const downloadBtn = document.getElementById("downloadBtn");
  const prevBtn = document.getElementById("prevFolderBtn");
  const nextBtn = document.getElementById("nextFolderBtn");
  const playerImage = document.getElementById("playerImage");
  const playerLoadingOverlay = document.getElementById("playerLoadingOverlay");
  const playerError = document.getElementById("playerError");
  const playerShell = document.querySelector(".player-image-shell");
  const dropHint = document.getElementById("dropHint");

  let availableFolders = [];
  let cards = [];
  let cardTemplate = null;
  let cardTemplatePromise = null;

  let currentIndex = -1;
  let currentFps = 8;
  let currentFolder = "";
  let frameList = [];
  let currentFrameCursor = 0;
  let stageTimer = null;
  let frameSourceMode = "remote";
  let localFrameResources = [];

  function padFrame(index) {
    return index.toString().padStart(2, "0");
  }

  function buildFrameSrc(folder, index) {
    const frameName = padFrame(index);
    const username = getCurrentUsername();
    if (!username) {
      logWarn('未登录，无法加载图片');
      return '';
    }
    // 使用API路径，从用户目录加载
    const imagePath = `${folder}/${frameName}.png`;
    return `/api/disk/preview?username=${encodeURIComponent(username)}&path=${encodeURIComponent(imagePath)}`;
  }

  function buildFolderName(prefix, index, padding) {
    const num = index.toString().padStart(padding, "0");
    return `${prefix}${num}`;
  }

  async function ensureCardTemplate() {
    if (cardTemplate) {
      return cardTemplate;
    }
    if (cardTemplatePromise) {
      return cardTemplatePromise;
    }
    cardTemplatePromise = fetch("./card.html")
      .then((response) => {
        if (!response.ok) {
          throw new Error("Failed to load card template");
        }
        return response.text();
      })
      .then((html) => {
        const wrapper = document.createElement("div");
        wrapper.innerHTML = html.trim();
        const template = wrapper.querySelector("#card-template");
        if (!template) {
          throw new Error("Card template missing");
        }
        cardTemplate = template;
        return cardTemplate;
      })
      .catch((error) => {
        console.error(error);
        cardTemplatePromise = null;
        throw error;
      });
    return cardTemplatePromise;
  }

  function destroyCards() {
    cards.forEach((card) => card.destroy && card.destroy());
    cards = [];
    if (cardsGrid) {
      cardsGrid.innerHTML = "";
    }
  }

  function updateCardCount() {
    if (!cardCountLabel) return;
    cardCountLabel.textContent = `${availableFolders.length} 个动画`;
  }

  function updateNavDisabledState() {
    const disabled = availableFolders.length <= 1;
    if (prevBtn) prevBtn.disabled = disabled;
    if (nextBtn) nextBtn.disabled = disabled;
  }

  function updateMeta() {
    const total = availableFolders.length;
    const safeIndex = currentIndex >= 0 ? currentIndex : 0;
    const folderName = availableFolders[safeIndex] || "--";
    if (folderNameLabel) {
      folderNameLabel.textContent = folderName;
    }
    if (folderCounterLabel) {
      folderCounterLabel.textContent = total ? `${safeIndex + 1} / ${total}` : "0 / 0";
    }
  }

  function showLoading(isLoading) {
    if (!playerLoadingOverlay) return;
    playerLoadingOverlay.classList.toggle("is-visible", isLoading);
  }

  function showPlayerError(message) {
    if (!playerError) return;
    playerError.textContent = message;
    playerError.hidden = false;
  }

  function hidePlayerError() {
    if (!playerError) return;
    playerError.hidden = true;
  }

  function stopStageAnimation() {
    if (stageTimer) {
      clearInterval(stageTimer);
      stageTimer = null;
    }
  }

  function setStagePlaceholderVisible(isVisible) {
    if (!playerImage) {
      return;
    }
    playerImage.classList.toggle("is-hidden", Boolean(isVisible));
    if (isVisible) {
      playerImage.removeAttribute("src");
    }
  }

  function cleanupLocalFrames() {
    if (!localFrameResources.length) {
      return;
    }
    localFrameResources.forEach((frame) => {
      if (frame && frame.url) {
        URL.revokeObjectURL(frame.url);
      }
    });
    localFrameResources = [];
  }

  function switchToRemoteSource() {
    if (frameSourceMode !== "remote") {
      cleanupLocalFrames();
      frameSourceMode = "remote";
    }
  }

  function startStageLoop() {
    log("🎬 启动动画循环");
    stopStageAnimation();
    if (!frameList.length) {
      logWarn("帧列表为空，无法启动循环");
      return;
    }
    const interval = 1000 / currentFps;
    log(`⏱️ 动画循环启动，帧数: ${frameList.length}, FPS: ${currentFps}, 间隔: ${interval}ms`);
    stageTimer = setInterval(() => {
      if (!frameList.length) {
        stopStageAnimation();
        return;
      }
      currentFrameCursor = (currentFrameCursor + 1) % frameList.length;
      updateStageImage(frameList[currentFrameCursor]);
    }, interval);
  }

  function updateStageImage(frameData) {
    if (!playerImage) {
      logWarn("playerImage 元素不存在");
      return;
    }

    if (frameSourceMode === "local") {
      const frame = typeof frameData === "object" ? frameData : frameList[frameData];
      if (!frame || !frame.url) {
        logWarn("本地模式：帧数据无效");
        return;
      }
      setStagePlaceholderVisible(false);
      if (playerImage.src !== frame.url) {
        log(`🖼️ 更新图片: ${frame.name}`);
        playerImage.src = frame.url;
      }
      return;
    }

    if (!currentFolder) {
      logWarn("远程模式：文件夹未设置");
      return;
    }
    const frameNumber = typeof frameData === "number" ? frameData : parseInt(frameData, 10);
    if (Number.isNaN(frameNumber)) {
      logWarn("远程模式：帧编号无效");
      return;
    }
    const nextSrc = buildFrameSrc(currentFolder, frameNumber);
    setStagePlaceholderVisible(false);
    if (playerImage.src !== nextSrc) {
      playerImage.src = nextSrc;
    }
  }

  function setFps(fps) {
    currentFps = fps;
    if (frameList.length > 0) {
      startStageLoop();
    }
  }

  async function buildCards() {
    if (!cardsGrid || !window.SequenceCard) {
      return;
    }
    const template = await ensureCardTemplate();
    destroyCards();

    availableFolders.forEach((folderName, index) => {
      const templateContent = template.content.firstElementChild;
      if (!templateContent) {
        return;
      }
      const cardElement = templateContent.cloneNode(true);
      cardsGrid.appendChild(cardElement);
      const cardInstance = new window.SequenceCard(
        cardElement,
        folderName,
        index,
        buildFrameSrc,
        handleCardSelect
      );
      cardInstance.loadPreview();
      cards.push(cardInstance);
    });

    updateCardCount();
  }

  function handleCardSelect(folderName, cardIndex) {
    if (typeof cardIndex !== "number") {
      return;
    }
    selectCardByIndex(cardIndex);
  }

  function highlightActiveCard() {
    cards.forEach((card) => {
      if (typeof card.setActive === "function") {
        card.setActive(card.index === currentIndex);
      }
    });
  }

  function resetStage() {
    stopStageAnimation();
    switchToRemoteSource();
    frameList = [];
    currentFolder = "";
    currentFrameCursor = 0;
    showLoading(false);
    hidePlayerError();
    setStagePlaceholderVisible(true);
    if (frameCounterLabel) {
      frameCounterLabel.textContent = "0 帧";
    }
    if (folderNameLabel) {
      folderNameLabel.textContent = "--";
    }
    if (folderCounterLabel) {
      folderCounterLabel.textContent = "0 / 0";
    }
  }

  async function selectCardByIndex(index) {
    if (!availableFolders.length) {
      resetStage();
      return;
    }

    const total = availableFolders.length;
    currentIndex = ((index % total) + total) % total;
    currentFolder = availableFolders[currentIndex];
    updateMeta();
    highlightActiveCard();
    await startStageForFolder(currentFolder);
  }

  function sanitizeFrameList(frameInfo) {
    if (frameInfo && Array.isArray(frameInfo.frames) && frameInfo.frames.length > 0) {
      return frameInfo.frames;
    }
    const maxFrame = frameInfo && frameInfo.maxFrame ? frameInfo.maxFrame : 0;
    if (!maxFrame) {
      return [];
    }
    return Array.from({ length: maxFrame }, (_, idx) => idx + 1);
  }

  async function startStageForFolder(folderName) {
    stopStageAnimation();
    switchToRemoteSource();
    frameList = [];
    currentFrameCursor = 0;
    showLoading(true);
    hidePlayerError();
    if (frameCounterLabel) {
      frameCounterLabel.textContent = "0 帧";
    }

    if (!folderName) {
      showLoading(false);
      showPlayerError("未选择动画");
      return;
    }

    try {
      const response = await fetch(`http://localhost:3000/api/frames/${folderName}`);
      if (!response.ok) {
        throw new Error("Failed to fetch frames");
      }
      const data = await response.json();
      frameList = sanitizeFrameList(data);

      if (!frameList.length) {
        showPlayerError("暂无可用帧");
        setStagePlaceholderVisible(true);
        showLoading(false);
        return;
      }

      if (frameCounterLabel) {
        frameCounterLabel.textContent = `${frameList.length} 帧`;
      }

      updateStageImage(frameList[0]);
      showLoading(false);
      startStageLoop();
    } catch (error) {
      console.error(error);
      showLoading(false);
      showPlayerError("加载失败");
    }
  }

  async function loadAvailableFolders() {
    availableFolders = [];
    try {
      const response = await fetch(foldersApi);
      if (!response.ok) {
        throw new Error("Failed to fetch folders");
      }
      const folders = await response.json();
      availableFolders = Array.isArray(folders) ? folders : [];
    } catch (error) {
      console.warn("使用回退文件夹列表:", error.message);
      const maxCount = 3002;
      for (let i = 1; i <= maxCount; i++) {
        availableFolders.push(buildFolderName("player_", i, 4));
      }
    }

    updateNavDisabledState();
    await buildCards();

    if (!availableFolders.length) {
      resetStage();
    }
  }

  async function startLocalPreview(files, folderLabel) {
    log("🎬 开始本地预览，文件数:", files?.length, "文件夹:", folderLabel);
    
    if (!Array.isArray(files) || !files.length) {
      logWarn("文件数组为空或无效");
      showPlayerError("未检测到 PNG 图片");
      return;
    }

    log("文件列表:");
    files.forEach((file, idx) => {
      log(`  [${idx}] ${file.name} - ${file.size} bytes`);
    });

    stopStageAnimation();
    showLoading(true);
    hidePlayerError();
    setStagePlaceholderVisible(false);
    cleanupLocalFrames();

    const orderedFiles = sortFilesForPlayback(files);
    log("排序后的文件数:", orderedFiles.length);
    
    localFrameResources = orderedFiles.map((file, index) => ({
      url: URL.createObjectURL(file),
      name: file.name || `frame_${index + 1}`,
      loaded: false,
    }));

    frameSourceMode = "local";
    frameList = localFrameResources;
    currentFrameCursor = 0;
    currentFolder = folderLabel || "本地导入";
    currentIndex = -1;

    if (folderNameLabel) {
      folderNameLabel.textContent = currentFolder;
    }
    if (folderCounterLabel) {
      folderCounterLabel.textContent = "-";
    }
    if (frameCounterLabel) {
      frameCounterLabel.textContent = `${localFrameResources.length} 帧`;
    }

    // 预加载所有图片后再开始播放
    try {
      log("开始预加载图片...");
      await preloadLocalFrames(localFrameResources);
      if (localFrameResources.length > 0) {
        log("✅ 预加载完成，开始播放");
        updateStageImage(localFrameResources[0]);
        showLoading(false);
        startStageLoop();
      } else {
        logWarn("预加载后资源为空");
        showLoading(false);
        showPlayerError("图片加载失败");
      }
    } catch (error) {
      logError("预加载图片失败:", error);
      showLoading(false);
      showPlayerError("图片加载失败");
    }
  }

  function preloadLocalFrames(frames) {
    return new Promise((resolve) => {
      if (!frames || !frames.length) {
        log("⚠️ 预加载：帧列表为空");
        resolve();
        return;
      }

      let loadedCount = 0;
      let hasError = false;
      const totalFrames = frames.length;
      log(`🖼️ 预加载 ${totalFrames} 个帧...`);

      frames.forEach((frame, index) => {
        const img = new Image();
        
        img.onload = () => {
          frame.loaded = true;
          loadedCount++;
          log(`  ✅ [${loadedCount}/${totalFrames}] ${frame.name} 加载成功`);
          if (loadedCount === totalFrames) {
            log(`🎉 所有帧加载完成！`);
            resolve();
          }
        };

        img.onerror = () => {
          hasError = true;
          loadedCount++;
          logWarn(`  ❌ [${loadedCount}/${totalFrames}] 帧 ${index + 1} 加载失败: ${frame.name}`);
          if (loadedCount === totalFrames) {
            logWarn(`预加载完成，但有 ${hasError ? '错误' : '部分失败'}`);
            resolve();
          }
        };

        log(`  ⏳ 开始加载 [${index + 1}/${totalFrames}]: ${frame.name}`);
        img.src = frame.url;
      });

      // 超时保护：最多等待 10 秒
      setTimeout(() => {
        if (loadedCount < totalFrames) {
          logWarn(`⏰ 预加载超时，已加载 ${loadedCount}/${totalFrames} 帧`);
          resolve();
        }
      }, 10000);
    });
  }

  function sortFilesForPlayback(files) {
    return files
      .slice()
      .sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: "base" }));
  }

  function bindDropZone() {
    if (!playerShell) {
      return;
    }
    playerShell.addEventListener("dragenter", handleDropZoneDragEnter);
    playerShell.addEventListener("dragover", handleDropZoneDragOver);
    playerShell.addEventListener("dragleave", handleDropZoneDragLeave);
    playerShell.addEventListener("drop", handleDropZoneDrop);
  }

  function preventDragDefaults(event) {
    if (!event) {
      return;
    }
    event.preventDefault();
    event.stopPropagation();
  }

  function setDropZoneState(isActive) {
    if (!playerShell) {
      return;
    }
    playerShell.classList.toggle("is-dragging", Boolean(isActive));
  }

  function handleDropZoneDragEnter(event) {
    preventDragDefaults(event);
    setDropZoneState(true);
  }

  function handleDropZoneDragOver(event) {
    preventDragDefaults(event);
    setDropZoneState(true);
  }

  function handleDropZoneDragLeave(event) {
    preventDragDefaults(event);
    if (!playerShell) {
      return;
    }
    const related = event.relatedTarget;
    if (related && playerShell.contains(related)) {
      return;
    }
    setDropZoneState(false);
  }

  async function handleDropZoneDrop(event) {
    log("📥 Drop事件触发");
    preventDragDefaults(event);
    setDropZoneState(false);
    const transfer = event.dataTransfer;
    if (!transfer) {
      logWarn("dataTransfer 为空");
      return;
    }

    log("dataTransfer.items.length:", transfer.items?.length);
    log("dataTransfer.files.length:", transfer.files?.length);

    try {
      const { files, folderLabel } = await collectDroppedPngFiles(transfer);
      log("✅ 收集到的文件数:", files.length, "文件夹名:", folderLabel);
      if (!files.length) {
        logWarn("未检测到 PNG 图片");
        showPlayerError("未检测到 PNG 图片");
        return;
      }
      await startLocalPreview(files, folderLabel);
    } catch (error) {
      logError("拖放处理错误:", error);
      showPlayerError("读取文件夹失败");
    }
  }

  async function collectDroppedPngFiles(dataTransfer) {
    const items = Array.from((dataTransfer && dataTransfer.items) || []);
    log("🔍 开始收集文件，items数量:", items.length);
    let collected = [];

    if (items.length) {
      log("处理 dataTransfer.items...");
      const results = await Promise.all(items.map((item, idx) => {
        log(`  - Item ${idx}: kind=${item.kind}, type=${item.type}`);
        return readDataTransferItem(item);
      }));
      collected = results.flat();
      log("从 items 收集到:", collected.length, "个条目");
    }

    if (!collected.length && dataTransfer && dataTransfer.files) {
      log("Items为空，尝试使用 dataTransfer.files...");
      collected = Array.from(dataTransfer.files).map((file) => {
        log(`  - File: ${file.name}, type=${file.type}, size=${file.size}`);
        return { file };
      });
      log("从 files 收集到:", collected.length, "个文件");
    }

    log("过滤前总数:", collected.length);
    collected.forEach((entry, idx) => {
      if (entry.file) {
        log(`  [${idx}] ${entry.file.name} - type: ${entry.file.type}, path: ${entry.file.webkitRelativePath || '(无路径)'}`);
      }
    });

    const pngEntries = collected.filter(({ file }) => {
      const isPng = isTopLevelPngFile(file);
      if (!isPng && file) {
        log(`  ❌ 过滤掉: ${file.name} (type=${file.type})`);
      }
      return isPng;
    });
    
    log("✅ 过滤后 PNG 文件数:", pngEntries.length);
    
    return {
      files: pngEntries.map((entry) => entry.file),
      folderLabel: deriveFolderLabel(pngEntries),
    };
  }

  function isTopLevelPngFile(file) {
    if (!file) {
      log("    🔍 isTopLevelPngFile: file 为空");
      return false;
    }
    const isPng = file.type === "image/png" || /\.png$/i.test(file.name || "");
    log(`    🔍 isTopLevelPngFile: ${file.name}, type="${file.type}", isPng=${isPng}`);
    if (!isPng) {
      return false;
    }
    if (!file.webkitRelativePath) {
      log(`      ✅ 无相对路径，视为顶级文件`);
      return true;
    }
    const segments = file.webkitRelativePath.split("/").filter(Boolean);
    const isTopLevel = segments.length <= 2;
    log(`      📂 相对路径: ${file.webkitRelativePath}, 层级=${segments.length}, isTopLevel=${isTopLevel}`);
    return isTopLevel;
  }

  function deriveFolderLabel(entries) {
    if (!entries || !entries.length) {
      return "本地导入";
    }
    for (const entry of entries) {
      if (entry.rootName) {
        return entry.rootName;
      }
      const inferred = inferFolderFromFile(entry.file);
      if (inferred) {
        return inferred;
      }
    }
    const fallback = entries[0] && entries[0].file && entries[0].file.name;
    return fallback ? fallback.replace(/\.png$/i, "") : "本地导入";
  }

  function inferFolderFromFile(file) {
    if (!file || !file.webkitRelativePath) {
      return "";
    }
    const segments = file.webkitRelativePath.split("/").filter(Boolean);
    return segments.length ? segments[0] : "";
  }

  function inferFolderFromEntry(entry) {
    if (!entry || !entry.fullPath) {
      return "";
    }
    const segments = entry.fullPath.split("/").filter(Boolean);
    if (segments.length >= 2) {
      return segments[segments.length - 2];
    }
    return segments[0] || "";
  }

  function readDataTransferItem(item) {
    return new Promise((resolve) => {
      if (!item || item.kind !== "file") {
        log("  ⚠️ Item 不是文件类型");
        resolve([]);
        return;
      }
      const entry = item.webkitGetAsEntry ? item.webkitGetAsEntry() : null;
      if (!entry) {
        log("  ⚠️ 无法获取 entry，使用 getAsFile");
        const file = item.getAsFile();
        resolve(file ? [{ file }] : []);
        return;
      }
      log(`  📁 Entry: ${entry.name}, isFile=${entry.isFile}, isDirectory=${entry.isDirectory}`);
      if (entry.isFile) {
        entry.file(
          (file) => {
            log(`    ✅ 读取文件成功: ${file.name}`);
            resolve(file ? [{ file, rootName: inferFolderFromEntry(entry) }] : []);
          },
          (err) => {
            logError(`    ❌ 读取文件失败:`, err);
            resolve([]);
          }
        );
        return;
      }
      if (entry.isDirectory) {
        log(`    📂 开始读取目录: ${entry.name}`);
        readDirectoryImmediateFiles(entry).then((files) => {
          log(`    ✅ 目录读取完成，文件数: ${files.length}`);
          resolve(
            files.map((file) => ({
              file,
              rootName: entry.name || inferFolderFromFile(file),
            }))
          );
        });
        return;
      }
      resolve([]);
    });
  }

  function readDirectoryImmediateFiles(directoryEntry) {
    return new Promise((resolve) => {
      if (!directoryEntry || !directoryEntry.isDirectory) {
        logWarn("directoryEntry 无效或不是目录");
        resolve([]);
        return;
      }
      const reader = directoryEntry.createReader();
      const files = [];
      let batchCount = 0;

      const readBatch = () => {
        reader.readEntries(
          (entries) => {
            batchCount++;
            log(`      📦 读取批次 ${batchCount}: ${entries.length} 个条目`);
            if (!entries.length) {
              log(`      ✅ 目录读取完成，总文件数: ${files.length}`);
              resolve(files);
              return;
            }
            let pending = entries.length;
            entries.forEach((entry) => {
              if (entry.isFile) {
                log(`        📄 文件: ${entry.name}`);
                entry.file(
                  (file) => {
                    if (file) {
                      log(`          ✅ 文件读取成功: ${file.name}, size=${file.size}, type=${file.type}`);
                      files.push(file);
                    }
                    pending -= 1;
                    if (pending === 0) {
                      readBatch();
                    }
                  },
                  (err) => {
                    logError(`          ❌ 文件读取失败: ${entry.name}`, err);
                    pending -= 1;
                    if (pending === 0) {
                      readBatch();
                    }
                  }
                );
              } else {
                log(`        📁 跳过子目录: ${entry.name}`);
                // skip subdirectories completely
                pending -= 1;
                if (pending === 0) {
                  readBatch();
                }
              }
            });
          },
          (err) => {
            logError("      ❌ readEntries 失败:", err);
            resolve(files);
          }
        );
      };

      readBatch();
    });
  }

  function bindControls() {
    if (fpsSlider && fpsValue) {
      fpsSlider.value = currentFps;
      fpsValue.textContent = `${currentFps} FPS`;
      fpsSlider.addEventListener("input", () => {
        const value = parseInt(fpsSlider.value, 10) || currentFps;
        setFps(value);
        fpsValue.textContent = `${value} FPS`;
      });
    }

    if (prevBtn) {
      prevBtn.addEventListener("click", () => selectCardByIndex(currentIndex - 1));
    }

    if (nextBtn) {
      nextBtn.addEventListener("click", () => selectCardByIndex(currentIndex + 1));
    }

    if (downloadBtn) {
      downloadBtn.addEventListener("click", () => {
        if (!currentFolder) {
          return;
        }
        if (window.SpriteSheetMaker && typeof window.SpriteSheetMaker.handleDownloadClick === "function") {
          window.SpriteSheetMaker.handleDownloadClick(currentFolder, currentIndex);
        }
      });
    }

    document.addEventListener("keydown", (event) => {
      if (event.key === "ArrowLeft") {
        selectCardByIndex(currentIndex - 1);
      } else if (event.key === "ArrowRight") {
        selectCardByIndex(currentIndex + 1);
      }
    });

    bindDropZone();
  }

  let previewCard = null;

  window.addEventListener("DOMContentLoaded", async () => {
    // 初始化预览卡片
    const container = document.getElementById('previewCardContainer');
    if (container && window.PreviewCard) {
      try {
        previewCard = new window.PreviewCard(container, {
          fps: 8,
          onFpsChange: (fps) => {
            // log('FPS changed to:', fps);
            setFps(fps);
          }
        });
        // log('✅ PreviewCard initialized');
      } catch (error) {
        logError('Failed to initialize PreviewCard:', error);
      }
    }
    
    bindControls();
    loadAvailableFolders();
  });

  window.addEventListener("beforeunload", cleanupLocalFrames);
})();


