// ============================================================
// RDC 提案库 - 前端 JS（browser.js）
// 由 browser.rb 通过 execute_script 注入
// ============================================================

var app = document.getElementById('app');

// 浏览模式状态
var _currentPath = '';
var _treeData = [];
var _searchMode = false;

// 标签导航状态
var _categoryGroups = {};  // { 'B': [{ name, path, label }] }
var _activeTab = '';       // 当前一级标签字母
var _activeSubTab = '';    // 当前二级标签路径
var _lastPathByLetter = {}; // 每个一级标签记住上次选的完整路径
var _lastNavSelection = {}; // 每层导航行的选中记忆 { parentPath: selectedChildPath }

// 一级标签映射（文件夹首字母 → 中文名）
var CATEGORY_MAP = {
  'A': '户型', 'B': '硬装', 'C': '柜体', 'D': '空间', 'E': '家具', 'F': '灯具', 'G': '摆件', 'H': '其它'
};

// 一级标签悬停提示（字母 → 说明文字，与 CATEGORY_MAP 配合）
var CATEGORY_TOOLTIP_MAP = {
  'C': '',
  'B': '',
  'A': '',
  'D': '',
  'E': '',
  'F': '',
  'G': '',
  'H': ''
};

// 插件映射（文件夹前缀 → ToolPanel 注册的 plugin_id）
var PLUGIN_MAP = {
  'C':  'rdc_cabinet_modeling',
  'A':  'rdc_house_modeling'
};

// ============================================================
// 状态路由
// ============================================================

function renderInit(data) {
  _searchMode = false;
  _currentPath = '';
  if (!data.logged_in) {
    renderLoginPage();
  } else if (!data.member) {
    if (data.expired && data.expires_at) {
      renderExpiredPage(data.expires_at);
    } else {
      renderMemberRequiredPage();
    }
  } else if (!data.installed) {
    renderGuidePage(data);
  } else {
    renderBrowseMode(data);
  }
}

function renderState(data) {
  var state = data.state || 'init';
  if (state === 'downloading') renderDownloadPage();
  else if (state === 'paused') renderPausedPage(data);
  else if (state === 'extracting') renderExtractingPage();
  else if (state === 'checking') renderCheckingPage();
  else if (state === 'updating') renderUpdatingPage();
  else if (state === 'installed') renderInit(data);
  else renderInit(data);
}

// ============================================================
// 管理模式页面（未安装/下载/更新）
// ============================================================

function renderLoginPage() {
  app.innerHTML =
    '<div class="container">' +
      '<div class="card text-center">' +
        '<div class="icon-box">&#128272;</div>' +
        '<h2>请先登录</h2>' +
        '<p class="subtitle">登录后即可下载提案库</p>' +
        '<div class="btn-group" style="justify-content:center">' +
          '<button class="btn btn-primary" onclick="sketchup.show_login()">去登录</button>' +
        '</div>' +
      '</div>' +
    '</div>';
}

function renderMemberRequiredPage() {
  app.innerHTML =
    '<div class="container">' +
      '<div class="card text-center">' +
        '<div class="icon-box">&#128081;</div>' +
        '<h2>需要会员权限</h2>' +
        '<p class="subtitle">提案库下载为会员专属功能<br>开通会员后即可使用</p>' +
        '<div class="btn-group" style="justify-content:center">' +
          '<button class="btn btn-primary" onclick="sketchup.open_subscription()">开通会员</button>' +
        '</div>' +
      '</div>' +
    '</div>';
}

function renderExpiredPage(expiresAt) {
  var dateStr = '';
  if (expiresAt) {
    try {
      var d = new Date(expiresAt);
      dateStr = d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2);
    } catch(e) { dateStr = expiresAt; }
  }
  var expiredInfo = dateStr ? '您的会员已于 ' + dateStr + ' 到期' : '您的会员已到期';
  app.innerHTML =
    '<div class="container">' +
      '<div class="card text-center">' +
        '<div class="icon-box">&#9203;</div>' +
        '<h2>会员已过期</h2>' +
        '<p class="subtitle">' + expiredInfo + '<br>续费后即可继续使用提案库</p>' +
        '<div class="btn-group" style="justify-content:center">' +
          '<button class="btn btn-primary" onclick="sketchup.open_subscription()">立即续费</button>' +
          '<button class="btn btn-secondary" onclick="sketchup.redeem_activation()">兑换激活码</button>' +
        '</div>' +
      '</div>' +
    '</div>';
}

function renderGuidePage(data) {
  var catHtml = '';
  if (data.categories && data.categories.length > 0) {
    catHtml = '<div class="stat" style="flex-wrap:wrap">';
    data.categories.slice(0, 8).forEach(function(c) {
      catHtml += '<div class="stat-item"><div class="stat-value">' + c.count +
                 '</div><div class="stat-label">' + c.name + '</div></div>';
    });
    catHtml += '</div>';
  }
  app.innerHTML =
    '<div class="container">' +
      '<div class="card text-center">' +
        '<div class="icon-box">&#128230;</div>' +
        '<h2>RDC 提案库</h2>' +
        '<p class="subtitle">住宅设计师必备库<br>22 分类 · 2000+ 原创动态组件</p>' +
        catHtml +
        '<div class="btn-group" style="justify-content:center">' +
          '<button class="btn btn-primary" id="dlBtn" onclick="startDownload()">' +
            '点击下载' +
          '</button>' +
        '</div>' +
        '<p class="subtitle mt-8">首次下载约2~3分钟，后续一键更新</p>' +
      '</div>' +
    '</div>';
}

function renderDownloadPage() {
  app.innerHTML =
    '<div class="container">' +
      '<div class="card">' +
        '<h2>&#128229; 正在下载提案库</h2>' +
        '<div class="progress-text">' +
          '<span id="dlInfo">准备中...</span>' +
          '<span id="dlPct">0%</span>' +
        '</div>' +
        '<div class="progress-bar"><div class="progress-fill" id="dlBar" style="width:0%"></div></div>' +
        '<div class="progress-text">' +
          '<span id="dlSpeed"></span>' +
          '<span id="dlElapsed"></span>' +
          '<span id="dlEta"></span>' +
        '</div>' +
        '<div class="btn-group">' +
          '<button class="btn btn-secondary" onclick="sketchup.pause_download()">暂停</button>' +
          '<button class="btn btn-danger" onclick="confirmCancel()">取消</button>' +
        '</div>' +
      '</div>' +
    '</div>';
}

function renderProgress(data) {
  var bar = document.getElementById('dlBar');
  var info = document.getElementById('dlInfo');
  var pct = document.getElementById('dlPct');
  var spd = document.getElementById('dlSpeed');
  var eta = document.getElementById('dlEta');
  var elapsed = document.getElementById('dlElapsed');
  if (bar) bar.style.width = data.percent + '%';
  if (pct) pct.textContent = data.percent + '%';
  if (info) {
    info.textContent = data.downloaded_mb + ' MB / ' + data.total_mb + ' MB';
  }
  if (spd) spd.textContent = data.speed_kbps > 0 && data.percent < 100 ? (data.speed_kbps / 1024).toFixed(1) + ' MB/s' : '';
  if (elapsed && data.elapsed_seconds > 0) elapsed.textContent = '已用时 ' + formatEta(data.elapsed_seconds);
  if (eta) eta.textContent = data.eta_seconds > 0 && data.percent < 100 ? '预计剩余 ' + formatEta(data.eta_seconds) : '';
}

function renderPausedPage(data) {
  app.innerHTML =
    '<div class="container">' +
      '<div class="card">' +
        '<h2>&#9208; 下载已暂停</h2>' +
        '<p class="subtitle">已下载 ' + (data.downloaded_mb || '?') + ' MB</p>' +
        '<div class="btn-group">' +
          '<button class="btn btn-primary" onclick="sketchup.resume_download()">恢复下载</button>' +
          '<button class="btn btn-danger" onclick="confirmCancel()">取消</button>' +
        '</div>' +
      '</div>' +
    '</div>';
}

function renderVerifyingPage(message) {
  var text = message || '下载完成，正在校验文件大小';
  app.innerHTML =
    '<div class="container">' +
      '<div class="card text-center">' +
        '<div class="spinner"></div>' +
        '<h2 class="mt-8">正在校验文件完整性...</h2>' +
        '<p class="subtitle">' + escapeHtml(text) + '</p>' +
      '</div>' +
    '</div>';
}

function renderExtractingPage(count, total, pct, phaseMsg) {
  var c = count || 0, t = total || 0, p = pct || 0;
  var info = t > 0 ? (c + ' / ' + t + ' 个文件') : (c > 0 ? ('已解压 ' + c + ' 个文件') : '正在准备解压...');
  var percentText = t > 0 ? (p + '%') : '';
  var barWidth = t > 0 ? p : 0;
  var subtitle = phaseMsg ? phaseMsg : '请勿关闭 SketchUp';
  app.innerHTML =
    '<div class="container">' +
      '<div class="card text-center">' +
        '<h2 class="mt-8">正在解压组件文件...</h2>' +
        '<div class="progress-header">' +
          '<span>' + info + '</span>' +
          '<span>' + percentText + '</span>' +
        '</div>' +
        '<div class="progress-bar"><div class="progress-fill" style="width:' + barWidth + '%"></div></div>' +
        '<p class="subtitle" style="font-size:12px;color:#999;margin-top:12px">' + subtitle + '</p>' +
      '</div>' +
    '</div>';
}
function renderCheckingPage() {
  app.innerHTML =
    '<div class="container">' +
      '<div class="card text-center">' +
        '<div class="spinner"></div>' +
        '<p class="subtitle mt-8">正在检查更新...</p>' +
        '<div class="btn-group" style="justify-content:center;margin-top:12px">' +
          '<button class="btn btn-secondary" onclick="sketchup.cancel_check()">取消</button>' +
        '</div>' +
      '</div>' +
    '</div>';
}

function renderUpdatingPage() {
  app.innerHTML =
    '<div class="container">' +
      '<div class="card">' +
        '<h2>&#128260; 正在更新提案库</h2>' +
        '<div class="progress-text">' +
          '<span id="updInfo">准备中...</span>' +
          '<span id="updPct">0%</span>' +
        '</div>' +
        '<div class="progress-bar"><div class="progress-fill" id="updBar" style="width:0%"></div></div>' +
        '<div class="progress-text" style="margin-top:6px;font-size:12px;color:#888">' +
          '<span id="updEta"></span>' +
        '</div>' +
      '</div>' +
    '</div>';
}

function renderUpdateProgress(data) {
  var bar = document.getElementById('updBar');
  var info = document.getElementById('updInfo');
  var pct = document.getElementById('updPct');
  var eta = document.getElementById('updEta');
  if (bar) bar.style.width = data.percent + '%';
  if (pct) pct.textContent = data.percent + '%';
  if (info) {
    if (data.is_zip) {
      // 增量 ZIP 包下载：显示字节进度
      var dlMb = data.downloaded_mb || (data.downloaded_bytes / 1048576).toFixed(1);
      var totMb = data.total_mb || (data.total_bytes / 1048576).toFixed(1);
      info.textContent = '正在下载增量包 ' + dlMb + ' / ' + totMb + ' MB';
    } else {
      info.textContent = '正在下载 ' + data.current_file + ' / ' + data.total_files + ' 个文件';
    }
  }
  if (eta) {
    if (data.eta_seconds && data.eta_seconds > 0) {
      eta.textContent = '预计剩余 ' + formatEta(data.eta_seconds);
    } else if (data.elapsed_seconds) {
      eta.textContent = '已用时 ' + formatEta(data.elapsed_seconds);
    } else {
      eta.textContent = '';
    }
  }
}

function formatEta(seconds) {
  if (!seconds || seconds < 0) return '';
  seconds = Math.round(seconds);
  if (seconds < 60) return seconds + ' 秒';
  var m = Math.floor(seconds / 60);
  var s = seconds % 60;
  if (m < 60) return m + ' 分' + (s > 0 ? ' ' + s + ' 秒' : '');
  var h = Math.floor(m / 60);
  m = m % 60;
  return h + ' 小时 ' + m + ' 分';
}

function renderUpdateAvailable(data) {
  var area = document.getElementById('updateArea');
  if (!area) { app.innerHTML = ''; area = app; }
  area.innerHTML =
    '<div class="card">' +
      '<div class="alert alert-info">发现新版本 v' + data.remote_version +
        '（当前 v' + data.local_version + '）</div>' +
      (data.changelog ? '<p class="subtitle">' + data.changelog + '</p>' : '') +
      '<div class="btn-group">' +
        '<button class="btn btn-primary" onclick="sketchup.confirm_update()">立即更新</button>' +
        '<button class="btn btn-secondary" onclick="sketchup.skip_library_version(\'' +
          data.remote_version + '\');renderInit({logged_in:true,member:true,installed:true,local_version:\'' +
          data.local_version + '\',component_count:0})">跳过此版本</button>' +
        '<button class="btn btn-secondary" onclick="renderInit({logged_in:true,member:true,installed:true,local_version:\'' +
          data.local_version + '\',component_count:0})">稍后</button>' +
      '</div>' +
    '</div>';
  area.classList.remove('hidden');
}

function renderNoUpdate() {
  var area = document.getElementById('updateArea');
  if (!area) { app.innerHTML = ''; area = app; }
  area.innerHTML =
    '<div class="container"><div class="card text-center">' +
      '<div class="alert alert-success">&#9989; 当前已是最新版本 v' + (window.__localVersion || '') + '</div>' +
      '<div class="btn-group" style="justify-content:center;margin-top:12px">' +
        '<button class="btn btn-secondary" onclick="sketchup.get_init_data()">返回</button>' +
      '</div>' +
    '</div></div>';
  area.classList.remove('hidden');
}

function renderError(msg) {
  app.innerHTML =
    '<div class="container">' +
      '<div class="card">' +
        '<div class="alert alert-error">&#10060; ' + msg + '</div>' +
        '<div class="btn-group">' +
          '<button class="btn btn-primary" onclick="sketchup.get_init_data()">返回</button>' +
        '</div>' +
      '</div>' +
    '</div>';
}

// ============================================================
// 浏览模式
// ============================================================

function renderBrowseMode(data) {
  app.innerHTML =
    '<div class="browse-layout">' +
      // 顶部工具栏（含搜索）
      '<div class="browse-toolbar">' +
        '<div class="tb-left">' +
          '<strong style="font-size:13px">RDC·提案库</strong>' +
          '<span class="version-badge">v' + (data.local_version || '-') + '</span>' +
        '</div>' +
        '<div class="search-wrap"><input class="search-input" id="searchInput" type="text" placeholder="搜索组件..." oninput="onSearchInput()"><span class="search-clear" id="searchClear" onclick="clearSearch()">×</span></div>' +
        '<div class="tb-right">' +
          '<span class="count-badge">' + (data.component_count || 0) + ' 组件</span>' +
          '<button class="btn btn-small btn-secondary" onclick="checkUpdate()">检查更新</button>' +
        '</div>' +
      '</div>' +
      // 一级标签 + 二级标签 + 内容
      '<div class="browse-body">' +
        '<div class="tab-scroll-wrap" id="tabBarWrap"><div class="tab-bar" id="tabBar"></div></div>' +
        '<div class="tab-scroll-wrap" id="subTabWrap"><div class="sub-tab-bar" id="subTabBar"></div></div>' +
        '<div id="navTabsContainer" style="flex-shrink:0;overflow-y:auto"></div>' +
        '<div class="content-area">' +
          '<div class="grid-container" id="gridContainer">' +
            '<div class="empty-state"><div class="icon">&#128194;</div><p>加载中...</p></div>' +
          '</div>' +
        '</div>' +
      '</div>' +
      // 悬浮插件工具条（居中，浮在内容区底部上方）
      '<div class="plugin-float hidden" id="pluginBar"></div>' +
      // 底部状态栏（仅视图控制按钮）
      '<div class="status-bar">' +
        '<div style="position:relative">' +
          '<button class="sb-view-btn" id="viewBtn" onclick="toggleSizePopover()" title="缩略图大小">' +
            '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">' +
              '<rect x="1" y="1" width="6" height="6" rx="1"/>' +
              '<rect x="9" y="1" width="6" height="6" rx="1"/>' +
              '<rect x="1" y="9" width="6" height="6" rx="1"/>' +
              '<rect x="9" y="9" width="6" height="6" rx="1"/>' +
            '</svg>' +
          '</button>' +
          '<div class="size-popover hidden" id="sizePopover">' +
            '<div class="sp-item" data-preset="large" onclick="setThumbPreset(\'large\')"><span>大缩略图</span></div>' +
            '<div class="sp-item active" data-preset="medium" onclick="setThumbPreset(\'medium\')"><span>中缩略图</span><span style="color:#0984e3;font-weight:700">✓</span></div>' +
            '<div class="sp-item" data-preset="small" onclick="setThumbPreset(\'small\')"><span>小缩略图</span></div>' +
            '<div class="sp-sep"></div>' +
            '<div class="sp-slider">' +
              '<div class="sp-slider-labels"><span>小</span><span>大</span></div>' +
              '<input type="range" min="60" max="200" value="110" oninput="onThumbSlider(this.value)">' +
            '</div>' +
          '</div>' +
        '</div>' +
      '</div>' +
      // 更新区域（隐藏）
      '<div id="updateArea" class="hidden"></div>' +
    '</div>';

  // 恢复上次的缩略图尺寸
  restoreThumbSize();

  // 初始化悬停提示系统
  initTooltipSystem();

  // 请求目录树数据
  sketchup.browse_tree();
}

// ===== 标签导航 =====

// 接收树数据，按首字母分组，渲染一级标签
function renderTreeData(data) {
  _treeData = data || [];
  if (_treeData.length === 0) return;

  // 如果只有一个根节点（如“提案库”），取其 children
  var rootFolders = _treeData;
  if (_treeData.length === 1 && _treeData[0].children && _treeData[0].children.length > 0) {
    rootFolders = _treeData[0].children;
  }

  // 按首字母分组
  _categoryGroups = {};
  rootFolders.forEach(function(folder) {
    var letter = folder.name.charAt(0).toUpperCase();
    if (!CATEGORY_MAP[letter]) letter = 'H'; // 不匹配的归入 H其它
    if (!_categoryGroups[letter]) _categoryGroups[letter] = [];
    _categoryGroups[letter].push({
      name: folder.name,
      path: folder.path,
      label: stripPrefix(folder.name),
      tooltip: folder.tooltip || ''
    });
  });

  // 渲染一级标签
  var tabBar = document.getElementById('tabBar');
  if (!tabBar) return;
  tabBar.innerHTML = '';

  // 按 CATEGORY_MAP 定义的字母顺序渲染
  var letters = Object.keys(CATEGORY_MAP);
  var defaultLetter = '';
  letters.forEach(function(l) {
    if (!_categoryGroups[l] || _categoryGroups[l].length === 0) return;
    if (!defaultLetter) defaultLetter = l; // 记录第一个可用标签
    var span = document.createElement('span');
    span.className = 'tab-item';
    span.dataset.letter = l;
    span.textContent = l + CATEGORY_MAP[l];
    // 一级标签 tooltip（优先用 JS 映射，其次用文件夹的 _tooltip.txt）
    var catTooltip = CATEGORY_TOOLTIP_MAP[l] || ((_categoryGroups[l] && _categoryGroups[l][0]) ? _categoryGroups[l][0].tooltip : '');
    if (catTooltip) span.dataset.tooltip = catTooltip;
    span.onclick = function() { onTabClick(l); };
    tabBar.appendChild(span);
  });

  // 添加滚动箭头（溢出时显示）
  addScrollArrows(tabBar);

  // 默认选中 A户型，如果没有则选第一个
  var target = (_categoryGroups['A'] && _categoryGroups['A'].length > 0) ? 'A' : defaultLetter;
  if (target) onTabClick(target);
}

// 去掉文件夹/文件名的品牌前缀和编码（如 "RDC 半圆窗_01" → "半圆窗_01"，"B3_柜体" → "柜体"，"01燃气热水" → "燃气热水"）
function stripPrefix(name) {
  // 0. 去掉品牌前缀：RDC 半圆窗 → 半圆窗
  name = name.replace(/^RDC\s*/i, '');
  // 1. 去掉字母+数字前缀：B3_柜体 → 柜体
  var match = name.match(/^[A-Z]\d+[_\-\s](.+)/);
  if (match) name = match[1];
  // 2. 去掉纯数字前缀：01燃气热水 → 燃气热水
  name = name.replace(/^\d+[\s_\-]*/, '');
  // 3. 去掉英文后缀：空调新风_SK → 空调新风
  name = name.replace(/[_\s][A-Za-z]\w*$/, '');
  return name;
}

// 一级标签点击
function onTabClick(letter) {
  _activeTab = letter;
  _searchMode = false;
  var input = document.getElementById('searchInput');
  if (input) input.value = '';
  var clearBtn = document.getElementById('searchClear');
  if (clearBtn) clearBtn.style.display = 'none';

  // 高亮一级标签
  var tabs = document.querySelectorAll('.tab-item');
  tabs.forEach(function(t) { t.classList.toggle('active', t.dataset.letter === letter); });

  // 渲染二级标签
  var folders = _categoryGroups[letter] || [];
  var subBar = document.getElementById('subTabBar');
  if (!subBar) return;
  subBar.innerHTML = '';

  folders.forEach(function(f, i) {
    var span = document.createElement('span');
    span.className = 'sub-tab-item';
    span.dataset.path = f.path;
    span.textContent = f.label;
    if (f.tooltip) span.dataset.tooltip = f.tooltip;
    span.onclick = function() { onSubTabClick(f.path, span); };
    subBar.appendChild(span);
  });

  // 添加滚动箭头（溢出时显示）
  addScrollArrows(subBar);

  // 恢复上次选中的路径；无记忆时不自动选择，等待用户手动点击二级标签
  if (folders.length > 0) {
    var remembered = _lastPathByLetter[letter];
    var targetPath = null;
    var targetSub = null;

    if (remembered) {
      var firstSeg = remembered.indexOf('/') >= 0 ? remembered.split('/')[0] : remembered;
      var subEl = subBar.querySelector('.sub-tab-item[data-path="' + firstSeg + '"]');
      if (subEl) {
        targetPath = remembered;
        targetSub = subEl;
      }
    }

    if (targetPath) {
      _activeSubTab = targetPath.indexOf('/') >= 0 ? targetPath.split('/')[0] : targetPath;
      _currentPath = targetPath;

      // 高亮二级标签
      var subs = subBar.querySelectorAll('.sub-tab-item');
      subs.forEach(function(s) { s.classList.toggle('active', s === targetSub); });

      clearNavRows();
      renderPluginBar(_activeSubTab);

      // 使用 restore_nav_path 恢复完整导航层级（支持深层路径）
      // targetPath 已是完整路径（如“G3 场景叙事/01 厨房摆件”），无需拼接分类根
      sketchup.restore_nav_path(JSON.stringify(targetPath));
    } else {
      // 无记忆：汇总该一级分类下所有二级文件夹的 SKP 文件平铺展示
      clearNavRows();
      var allPaths = [];
      folders.forEach(function(f) { allPaths.push(f.path); });
      _activeSubTab = '';
      _currentPath = '';
      sketchup.browse_all_under(JSON.stringify(allPaths));
    }
  }
}

// 二级标签点击
function onSubTabClick(path, el) {
  // 防重复点击：如果已经在当前标签且无搜索，忽略
  if (_activeSubTab === path && !_searchMode) return;

  _activeSubTab = path;
  _currentPath = path;
  _searchMode = false;

  // 记住当前一级标签对应的完整路径
  if (_activeTab) _lastPathByLetter[_activeTab] = path;

  // 清空深层导航行
  clearNavRows();

  // 高亮当前二级标签（仅 #subTabBar 内）
  var subBar = document.getElementById('subTabBar');
  if (subBar) {
    var subs = subBar.querySelectorAll('.sub-tab-item');
    subs.forEach(function(s) { s.classList.toggle('active', s === el); });
  }

  // 更新插件区
  renderPluginBar(path);

  // 请求目录数据
  sketchup.browse_folder(path);
}

// 插件区渲染（动态拉取 ToolPanel 工具列表）
function renderPluginBar(folderPath) {
  var bar = document.getElementById('pluginBar');
  if (!bar) { return; }

  // 提取文件夹前缀（如 "B3_柜体" → "B3"，"A1_户型" → "A1"）
  var folderName = folderPath.split('/').pop();
  var prefixMatch = folderName.match(/^([A-Z]\d+)/);
  if (!prefixMatch) { bar.innerHTML = ''; bar.classList.add('hidden'); return; }

  var prefix = prefixMatch[1]; // 如 "B3"
  var letter = prefix.charAt(0); // 如 "B"

  // 查找匹配的 plugin_id：先精确匹配前缀，再匹配首字母
  var pluginId = PLUGIN_MAP[prefix] || PLUGIN_MAP[letter];
  if (!pluginId) { bar.innerHTML = ''; bar.classList.add('hidden'); return; }

  // 先显示加载中状态
  bar.classList.remove('hidden');
  bar.innerHTML =
    '<div class="plugin-bar">' +
      '<span class="plugin-label">\uD83D\uDD27 插件推荐</span>' +
      '<span style="font-size:12px;color:#b2bec3">加载中...</span>' +
    '</div>';

  // 从 Ruby 端拉取工具列表
  sketchup.get_plugin_tools(pluginId);
}

// 接收 Ruby 返回的工具列表并渲染按钮
function renderPluginTools(tools) {
  var bar = document.getElementById('pluginBar');
  if (!bar) return;

  if (!tools || tools.length === 0) {
    bar.innerHTML = '';
    bar.classList.add('hidden');
    return;
  }

  bar.classList.remove('hidden');
  var html = '<div class="plugin-bar">' +
    '<span class="plugin-label">\uD83D\uDD27 插件推荐</span>' +
    '<div class="plugin-tools-grid">';

  tools.forEach(function(tool) {
    var iconHtml = tool.icon
      ? '<img src="' + tool.icon + '" class="tool-btn-icon">'
      : '<span class="tool-btn-icon" style="font-size:20px">\uD83D\uDEE0\uFE0F</span>';
    html +=
      '<div class="plugin-tool-btn" onclick="sketchup.invoke_plugin_tool(\'' + escapeAttr(tool.id) + '\')" title="' + escapeAttr(tool.name) + '">' +
        iconHtml +
      '</div>';
  });

  html += '</div></div>';
  bar.innerHTML = html;
}

// ===== 目录数据渲染 =====

function renderFolderData(data) {
  _currentPath = data.current_path || '';
  _searchMode = !!(data.keywords);

  var folders = data.folders || [];
  var files = data.files || [];

  var container = document.getElementById('gridContainer');
  if (!container) return;

  // 文件夹：在导航区添加一层标签行（不在内容区渲染）
  if (folders.length > 0) {
    renderNavRow(folders);
  }

  if (folders.length === 0 && files.length === 0) {
    container.innerHTML =
      '<div class="empty-state">' +
        '<div class="icon">' + (_searchMode ? '&#128269;' : '&#128193;') + '</div>' +
        '<p>' + (_searchMode ? '未找到匹配的组件' : '此目录为空') + '</p>' +
      '</div>';
    return;
  }

  var html = '';

  // 搜索结果标签
  if (_searchMode) {
    html += '<div class="search-result-label">搜索 "' + data.keywords + '"：找到 ' +
            files.length + ' 个组件</div>';
  }
  
  html += '<div class="grid">';

  // 文件网格（仅展示 SKP 文件，文件夹仅在导航标签行显示）
  if (files.length > 0) {
  
    // 文件（带 hover 操作按钮 + 实例替换"实"字按钮）
    files.forEach(function(f) {
      var thumbHtml = f.thumbnail
        ? '<img src="' + f.thumbnail + '" onerror="this.style.display=\'none\';this.parentNode.classList.add(\'thumb-failed\')">'
        : '<span class="placeholder">&#128451;</span>';
      var ap = escapeAttr(f.ap);
      var overlay =
        '<div class="grid-overlay">' +
          '<span class="act-btn" onclick="onModeClick(event,\'' + ap + '\',\'对角\')" title="对角放置">对</span>' +
          '<span class="act-btn" onclick="onModeClick(event,\'' + ap + '\',\'识别\')" title="识别内空">识</span>' +
          '<span class="act-btn" onclick="onModeClick(event,\'' + ap + '\',\'替换\')" title="等比替换">等</span>' +
          '<span class="act-btn" onclick="onModeClick(event,\'' + ap + '\',\'取面放置\')" title="普通替换（非等比）">替</span>' +
        '</div>';
      var instBtn = f.inst_replace
        ? '<span class="act-btn inst" onclick="onModeClick(event,\'' + ap + '\',\'替换\')">实</span>'
        : '';
      html +=
        '<div class="grid-item" onclick="onFileClick(\'' + ap + '\')">' +
          '<div class="grid-thumb">' + thumbHtml + overlay + instBtn + '</div>' +
          '<div class="grid-name" title="' + escapeAttr(f.name) + '">' + escapeHtml(stripPrefix(f.name)) + '</div>' +
        '</div>';
    });
  }

  html += '</div>';
  container.innerHTML = html;

  // 网格中有模型文件时，通知 Ruby 端检查是否需要动态组件引导
  if (files.length > 0 && window.sketchup && sketchup.dc_guide_check) {
    sketchup.dc_guide_check(true);
  }
}

// 恢复深层导航路径：渲染多层导航标签行 + 文件网格
function renderNavHierarchy(data) {
  var navLevels = data.navLevels || [];
  var files = data.files || [];
  _searchMode = false;

  console.log('[RDC][renderNavHierarchy] navLevels=' + navLevels.length + ', files=' + files.length + ', currentPath=' + (data.currentPath || ''));
  for (var k = 0; k < navLevels.length; k++) {
    var lv = navLevels[k];
    console.log('[RDC][renderNavHierarchy] level[' + k + '] navPath=' + (lv.navPath || '') + ', folders=' + ((lv.folders || []).length));
  }

  // 跳过 navLevels[0]（二级标签已在 subTabBar 中渲染）
  // 从 navLevels[1] 开始渲染深层导航行
  for (var i = 1; i < navLevels.length; i++) {
    var level = navLevels[i];
    _currentPath = level.navPath || '';
    if (level.folders && level.folders.length > 0) {
      renderNavRow(level.folders);
    }
  }

  // 恢复为最终路径
  _currentPath = data.currentPath || '';

  // 高亮深层导航行中的选中项（基于最终路径逐层匹配）
  var finalPath = data.currentPath || '';
  var navContainer = document.getElementById('navTabsContainer');
  if (navContainer && finalPath) {
    var rows = navContainer.querySelectorAll('.nav-level');
    rows.forEach(function(row) {
      var items = row.querySelectorAll('.sub-tab-item');
      items.forEach(function(item) {
        var itemPath = item.dataset.path || '';
        var isActive = finalPath === itemPath || finalPath.indexOf(itemPath + '/') === 0;
        item.classList.toggle('active', isActive);
      });
    });
  }

  // 渲染文件网格
  var container = document.getElementById('gridContainer');
  if (!container) return;

  if (files.length === 0) {
    container.innerHTML =
      '<div class="empty-state">' +
        '<div class="icon">&#128193;</div>' +
        '<p>此目录为空</p>' +
      '</div>';
    return;
  }

  var html = '<div class="grid">';
  files.forEach(function(f) {
    var thumbHtml = f.thumbnail
      ? '<img src="' + f.thumbnail + '" onerror="this.style.display=\'none\';this.parentNode.classList.add(\'thumb-failed\')">'
      : '<span class="placeholder">&#128451;</span>';
    var ap = escapeAttr(f.ap);
    var overlay =
      '<div class="grid-overlay">' +
        '<span class="act-btn" onclick="onModeClick(event,\'' + ap + '\',\'对角\')" title="对角放置">对</span>' +
        '<span class="act-btn" onclick="onModeClick(event,\'' + ap + '\',\'识别\')" title="识别内空">识</span>' +
        '<span class="act-btn" onclick="onModeClick(event,\'' + ap + '\',\'替换\')" title="等比替换">等</span>' +
        '<span class="act-btn" onclick="onModeClick(event,\'' + ap + '\',\'取面放置\')" title="普通替换（非等比）">替</span>' +
      '</div>';
    var instBtn = f.inst_replace
      ? '<span class="act-btn inst" onclick="onModeClick(event,\'' + ap + '\',\'替换\')">实</span>'
      : '';
    html +=
      '<div class="grid-item" onclick="onFileClick(\'' + ap + '\')">' +
        '<div class="grid-thumb">' + thumbHtml + overlay + instBtn + '</div>' +
        '<div class="grid-name" title="' + escapeAttr(f.name) + '">' + escapeHtml(stripPrefix(f.name)) + '</div>' +
      '</div>';
  });
  html += '</div>';
  container.innerHTML = html;
}

// ===== 面包屑 =====

function renderBreadcrumb(path, keywords) {
  var el = document.getElementById('breadcrumb');
  if (!el) return;

  // 回到当前二级标签的导航
  var backAction = 'resetToCurrentTab()';

  if (keywords) {
    el.innerHTML =
      '<span onclick="' + backAction + '">全部</span>' +
      '<span class="sep">/</span>' +
      '<span class="current">搜索：' + escapeHtml(keywords) + '</span>';
    return;
  }

  // 如果路径就是当前二级标签，不显示面包屑
  if (path === _activeSubTab) {
    el.innerHTML = '<span class="current">' + escapeHtml(stripPrefix(path.split('/').pop() || '')) + '</span>';
    return;
  }

  var parts = path ? path.split('/') : [];
  var html = '<span onclick="' + backAction + '">全部</span>';
  var accumulated = '';

  parts.forEach(function(part, i) {
    accumulated += (accumulated ? '/' : '') + part;
    html += '<span class="sep">/</span>';
    if (i === parts.length - 1) {
      html += '<span class="current">' + escapeHtml(part) + '</span>';
    } else {
      var p = accumulated;
      html += '<span onclick="navToPath(\'' + escapeAttr(p) + '\')">' + escapeHtml(part) + '</span>';
    }
  });

  el.innerHTML = html;
}

// 回到当前二级标签
function resetToCurrentTab() {
  _searchMode = false;
  var input = document.getElementById('searchInput');
  if (input) input.value = '';
  var clearBtn = document.getElementById('searchClear');
  if (clearBtn) clearBtn.style.display = 'none';
  clearNavRows();
  if (_activeSubTab) {
    sketchup.browse_folder(_activeSubTab);
  }
}

// ===== 搜索 =====

var _searchTimer = null;
function onSearchInput() {
  clearTimeout(_searchTimer);
  // 显示/隐藏清除按钮
  var input = document.getElementById('searchInput');
  var clearBtn = document.getElementById('searchClear');
  if (clearBtn) clearBtn.style.display = (input && input.value.length > 0) ? 'block' : 'none';
  _searchTimer = setTimeout(function() {
    var input = document.getElementById('searchInput');
    var kw = input ? input.value.trim() : '';
    if (kw.length > 0) {
      sketchup.browse_search(kw);
    } else if (_searchMode) {
      // 搜索清空，回到当前二级标签
      _searchMode = false;
      sketchup.browse_folder(_activeSubTab || _currentPath);
    }
  }, 300);
}

function clearSearch() {
  var input = document.getElementById('searchInput');
  if (input) { input.value = ''; input.focus(); }
  var clearBtn = document.getElementById('searchClear');
  if (clearBtn) clearBtn.style.display = 'none';
  _searchMode = false;
  if (_activeSubTab || _currentPath) {
    sketchup.browse_folder(_activeSubTab || _currentPath);
  }
}

// ===== 文件夹点击（进入子目录，展开下一层标签栏）=====

function onFolderClick(path) {
  _currentPath = path;
  _searchMode = false;
  var input = document.getElementById('searchInput');
  if (input) input.value = '';
  var clearBtn = document.getElementById('searchClear');
  if (clearBtn) clearBtn.style.display = 'none';
  navToPath(path);
}

// 清空所有深层导航行
function clearNavRows() {
  var c = document.getElementById('navTabsContainer');
  if (!c) return;
  // 清除 scroll 事件监听
  var rows = c.querySelectorAll('.nav-level');
  for (var i = 0; i < rows.length; i++) {
    if (rows[i]._scrollArrowCleanup) {
      rows[i].removeEventListener('scroll', rows[i]._scrollArrowCleanup);
      rows[i]._scrollArrowCleanup = null;
    }
  }
  c.innerHTML = '';
}

// 同步删除指定索引之后的所有导航行（含 scroll 事件清理）
function _removeNavRowsFrom(navContainer, fromIndex) {
  var rows = navContainer.querySelectorAll('.nav-level');
  for (var i = rows.length - 1; i >= fromIndex; i--) {
    var row = rows[i];
    if (!row || !row.parentElement) continue;
    if (row._scrollArrowCleanup) {
      row.removeEventListener('scroll', row._scrollArrowCleanup);
      row._scrollArrowCleanup = null;
    }
    var wrapper = row.parentElement;
    if (wrapper && wrapper.classList.contains('tab-scroll-wrap') && wrapper.parentElement === navContainer) {
      navContainer.removeChild(wrapper);
    } else if (wrapper === navContainer) {
      navContainer.removeChild(row);
    }
  }
}

// 通用导航：找到点击项所在的行，同步删掉更深的行，请求数据
function navToPath(path) {
  _currentPath = path;
  _searchMode = false;

  var navContainer = document.getElementById('navTabsContainer');
  if (!navContainer) return;

  // 找到点击项所在的导航行（该行的 navPath 是点击项的父路径）
  var existingRows = navContainer.querySelectorAll('.nav-level');
  var matchIndex = -1;
  for (var i = 0; i < existingRows.length; i++) {
    var np = existingRows[i].dataset.navPath;
    if (np === path || path.indexOf(np + '/') === 0) { matchIndex = i; }
  }

  // 判断下一行是否已展开且路径相同（已展开 → 只刷新网格，不删不建）
  if (matchIndex >= 0) {
    var nextRow = existingRows[matchIndex + 1];
    if (nextRow && nextRow.dataset.navPath === path) {
      // 已展开，跳过删除和重建
      if (_activeTab) _lastPathByLetter[_activeTab] = path;
      sketchup.browse_folder(path);
      return;
    }
    // 同步删除匹配行之后的所有更深层行
    _removeNavRowsFrom(navContainer, matchIndex + 1);
  }

  // 记住深层路径
  if (_activeTab) _lastPathByLetter[_activeTab] = path;

  sketchup.browse_folder(path);
}

// 在当前导航层级下方新增一层标签行
function renderNavRow(folders) {
  var navContainer = document.getElementById('navTabsContainer');
  if (!navContainer) return;

  // 用 tab-scroll-wrap 包裹，以支持滚动箭头
  var rowWrap = document.createElement('div');
  rowWrap.className = 'tab-scroll-wrap';

  var row = document.createElement('div');
  row.className = 'sub-tab-bar nav-level';
  row.style.cssText = 'border:none;border-bottom:1px solid #f0f0f0;background:#fafbfc;padding:0 8px';
  row.dataset.navPath = _currentPath;

  folders.forEach(function(f) {
    var span = document.createElement('span');
    span.className = 'sub-tab-item';
    span.dataset.path = f.path;
    span.textContent = stripPrefix(f.name);
    if (f.tooltip) span.dataset.tooltip = f.tooltip;
    span.onclick = function() {
      var items = row.querySelectorAll('.sub-tab-item');
      items.forEach(function(s) { s.classList.toggle('active', s === span); });
      _lastNavSelection[_currentPath] = f.path;
      onFolderClick(f.path);
    };
    row.appendChild(span);
  });

  rowWrap.appendChild(row);
  navContainer.appendChild(rowWrap);

  // 恢复上次的选中项（选择历史）
  var saved = _lastNavSelection[_currentPath];
  if (saved) {
    var selEl = row.querySelector('.sub-tab-item[data-path="' + saved + '"]');
    if (selEl) selEl.classList.add('active');
  }

  // 添加滚动箭头
  addScrollArrows(row);

  rowWrap.scrollIntoView({ block: 'nearest' });
}

// ===== 文件点击（载入）+ hover 按钮（其他模式）=====

function onFileClick(absPath) {
  sketchup.insert_component(absPath, '载入');
}

function onModeClick(e, absPath, mode) {
  e.stopPropagation();
  sketchup.insert_component(absPath, mode);
}



// ============================================================
// 标签栏滚动箭头
// ============================================================

// 为标签栏添加左右滚动箭头（仅溢出时显示）
function addScrollArrows(tabBar) {
  if (!tabBar) return;
  var wrap = tabBar.parentElement;
  if (!wrap) return;

  // 确保父容器是 tab-scroll-wrap
  if (!wrap.classList.contains('tab-scroll-wrap')) {
    var newWrap = document.createElement('div');
    newWrap.className = 'tab-scroll-wrap';
    tabBar.parentNode.insertBefore(newWrap, tabBar);
    newWrap.appendChild(tabBar);
    wrap = newWrap;
  }

  // 移除已有箭头
  var oldLeft = wrap.querySelector('.tab-scroll-arrow.left');
  var oldRight = wrap.querySelector('.tab-scroll-arrow.right');
  if (oldLeft) oldLeft.remove();
  if (oldRight) oldRight.remove();

  // 清除旧的 scroll 监听
  if (tabBar._scrollArrowCleanup) {
    tabBar.removeEventListener('scroll', tabBar._scrollArrowCleanup);
    tabBar._scrollArrowCleanup = null;
  }
  // 清除旧的 resize 监听
  if (tabBar._resizeCleanup) {
    window.removeEventListener('resize', tabBar._resizeCleanup);
    tabBar._resizeCleanup = null;
  }

  // 对齐方式判断函数（溢出左对齐，不溢出居中）
  function checkAlign() {
    tabBar.style.justifyContent = (tabBar.scrollWidth > tabBar.clientWidth) ? 'flex-start' : 'center';
  }

  // 检测是否溢出（延迟一帧，确保 DOM 布局完成）
  setTimeout(function() {
    checkAlign();
    if (tabBar.scrollWidth <= tabBar.clientWidth) return;

    // 平滑滚动控制：按住持续滚动，松开停止
    var scrollTimer = null;
    function startScroll(dir) {
      stopScroll();
      // 先立即滚一步
      tabBar.scrollLeft += dir * 5;
      // 然后启动定时器持续滚动
      scrollTimer = setInterval(function() {
        tabBar.scrollLeft += dir * 5;
      }, 40);
    }
    function stopScroll() {
      if (scrollTimer) { clearInterval(scrollTimer); scrollTimer = null; }
    }

    // 创建左箭头
    var leftArrow = document.createElement('div');
    leftArrow.className = 'tab-scroll-arrow left';
    leftArrow.textContent = '\u25C0';
    leftArrow.addEventListener('mousedown', function(e) { e.preventDefault(); startScroll(-1); });
    leftArrow.addEventListener('mouseup', stopScroll);
    leftArrow.addEventListener('mouseleave', stopScroll);

    // 创建右箭头
    var rightArrow = document.createElement('div');
    rightArrow.className = 'tab-scroll-arrow right';
    rightArrow.textContent = '\u25B6';
    rightArrow.addEventListener('mousedown', function(e) { e.preventDefault(); startScroll(1); });
    rightArrow.addEventListener('mouseup', stopScroll);
    rightArrow.addEventListener('mouseleave', stopScroll);

    wrap.appendChild(leftArrow);
    wrap.appendChild(rightArrow);

    // 根据滚动位置更新箭头可见性
    function updateArrows() {
      var sl = tabBar.scrollLeft;
      var maxScroll = tabBar.scrollWidth - tabBar.clientWidth;
      leftArrow.classList.toggle('hidden', sl <= 1);
      rightArrow.classList.toggle('hidden', sl >= maxScroll - 1);
    }

    tabBar.addEventListener('scroll', updateArrows);
    tabBar._scrollArrowCleanup = updateArrows;
    updateArrows();

    // 窗口 resize 时重新判断对齐 + 箭头显隐
    var resizeHandler = function() {
      checkAlign();
      if (tabBar.scrollWidth > tabBar.clientWidth) {
        if (!leftArrow.parentNode) { wrap.appendChild(leftArrow); wrap.appendChild(rightArrow); }
        updateArrows();
      } else {
        if (leftArrow.parentNode) { leftArrow.remove(); rightArrow.remove(); }
      }
    };
    window.addEventListener('resize', resizeHandler);
    tabBar._resizeCleanup = resizeHandler;
  }, 50);
}

// ============================================================
// 全局悬停提示（JS 实现，不受父容器 overflow 截断）
// ============================================================

var _tipBubble = null;
var _tipTimer = null;

// 初始化全局 tooltip 气泡（事件委托，自动绑定所有 data-tooltip 元素）
function initTooltipSystem() {
  if (_tipBubble) return;
  _tipBubble = document.createElement('div');
  _tipBubble.className = 'tip-bubble';
  document.body.appendChild(_tipBubble);

  document.addEventListener('mouseover', function(e) {
    var el = e.target;
    while (el && el !== document.body) {
      if (el.dataset && el.dataset.tooltip) {
        clearTimeout(_tipTimer);
        _tipTimer = setTimeout(function() { showTip(el); }, 400);
        return;
      }
      el = el.parentElement;
    }
  });

  document.addEventListener('mouseout', function(e) {
    var el = e.target;
    while (el && el !== document.body) {
      if (el.dataset && el.dataset.tooltip) {
        hideTip();
        return;
      }
      el = el.parentElement;
    }
  });
}

function showTip(el) {
  if (!_tipBubble) return;
  var text = el.dataset.tooltip;
  if (!text) return;
  _tipBubble.textContent = text;
  _tipBubble.style.display = 'block';
  // 计算位置：元素正上方居中
  var rect = el.getBoundingClientRect();
  var tipW = _tipBubble.offsetWidth;
  var left = rect.left + rect.width / 2 - tipW / 2;
  var top = rect.top - _tipBubble.offsetHeight - 6;
  if (left < 4) left = 4;
  if (top < 4) top = rect.bottom + 6; // 空间不够时显示在下方
  _tipBubble.style.left = left + 'px';
  _tipBubble.style.top = top + 'px';
  _tipBubble.classList.add('show');
}

function hideTip() {
  clearTimeout(_tipTimer);
  if (_tipBubble) {
    _tipBubble.classList.remove('show');
    setTimeout(function() { if (_tipBubble && !_tipBubble.classList.contains('show')) _tipBubble.style.display = 'none'; }, 200);
  }
}

// ============================================================
// 辅助函数
// ============================================================

function formatEta(seconds) {
  seconds = Math.round(seconds);
  if (seconds < 60) return seconds + ' 秒';
  if (seconds < 3600) return Math.floor(seconds / 60) + ' 分钟';
  return Math.floor(seconds / 3600) + ' 小时 ' + Math.floor((seconds % 3600) / 60) + ' 分';
}

function startDownload() {
  var btn = document.getElementById('dlBtn');
  if (btn) { btn.disabled = true; btn.textContent = '正在启动...'; }
  sketchup.start_download();
}

function checkUpdate() {
  sketchup.check_update();
}

function confirmCancel() {
  if (confirm('确定取消下载？已下载的部分将被删除。')) {
    sketchup.cancel_download();
  }
}

function escapeHtml(str) {
  if (!str) return '';
  return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

function escapeAttr(str) {
  if (!str) return '';
  return String(str).replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '&quot;');
}

// ===== 缩略图尺寸控制 =====

var _thumbSizeKey = 'rdc_thumb_card_w';

var THUMB_PRESETS = {
  small:  { card: 80,  thumb: 64 },
  medium: { card: 110, thumb: 94 },
  large:  { card: 160, thumb: 144 }
};

function applyThumbSize(cardW) {
  cardW = Math.max(60, Math.min(200, parseInt(cardW, 10) || 110));
  var thumbW = Math.round(cardW * 0.85);
  var grid = document.getElementById('gridContainer');
  if (grid) {
    grid.style.setProperty('--card-w', cardW + 'px');
    grid.style.setProperty('--thumb-w', thumbW + 'px');
  }
  // 更新滑杆值
  var slider = document.querySelector('#sizePopover input[type=range]');
  if (slider) slider.value = cardW;
  // 更新预设高亮
  var items = document.querySelectorAll('#sizePopover .sp-item');
  var matchedPreset = '';
  for (var k in THUMB_PRESETS) {
    if (THUMB_PRESETS[k].card === cardW) matchedPreset = k;
  }
  for (var i = 0; i < items.length; i++) {
    var p = items[i].dataset.preset;
    var isActive = (p === matchedPreset);
    items[i].classList.toggle('active', isActive);
    // 更新 ✓ 标记
    var check = items[i].querySelector('span:last-child');
    if (isActive) {
      if (!check || check.textContent !== '✓') {
        var s = document.createElement('span');
        s.style.cssText = 'color:#0984e3;font-weight:700';
        s.textContent = '✓';
        items[i].appendChild(s);
      }
    } else if (check && check.textContent === '✓') {
      check.remove();
    }
  }
}

function setThumbPreset(preset) {
  var cfg = THUMB_PRESETS[preset];
  if (!cfg) return;
  applyThumbSize(cfg.card);
  try { localStorage.setItem(_thumbSizeKey, cfg.card); } catch(e) {}
  closeSizePopover();
}

function onThumbSlider(val) {
  applyThumbSize(val);
  try { localStorage.setItem(_thumbSizeKey, val); } catch(e) {}
}

function restoreThumbSize() {
  var saved = 110;
  try { saved = parseInt(localStorage.getItem(_thumbSizeKey), 10) || 110; } catch(e) {}
  applyThumbSize(saved);
}

function toggleSizePopover() {
  var pop = document.getElementById('sizePopover');
  var btn = document.getElementById('viewBtn');
  if (!pop) return;
  var isHidden = pop.classList.contains('hidden');
  pop.classList.toggle('hidden');
  if (btn) btn.classList.toggle('active', isHidden);
  if (isHidden) {
    // 打开时绑定点击外部关闭
    setTimeout(function() {
      document.addEventListener('click', _closePopoverOnClick, true);
    }, 0);
  } else {
    document.removeEventListener('click', _closePopoverOnClick, true);
  }
}

function closeSizePopover() {
  var pop = document.getElementById('sizePopover');
  var btn = document.getElementById('viewBtn');
  if (pop) pop.classList.add('hidden');
  if (btn) btn.classList.remove('active');
  document.removeEventListener('click', _closePopoverOnClick, true);
}

function _closePopoverOnClick(e) {
  var pop = document.getElementById('sizePopover');
  var btn = document.getElementById('viewBtn');
  if (!pop || !btn) return;
  // 如果点击的不是浮层内部也不是按钮，则关闭
  if (!pop.contains(e.target) && !btn.contains(e.target)) {
    closeSizePopover();
  }
}
