Greasy Fork

Greasy Fork is available in English.

抖音推荐影响器 (Smart Feed Assistant)

通过AI智能分析内容,优化你的信息流体验

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         抖音推荐影响器 (Smart Feed Assistant)
// @namespace    https://github.com/baianjo/Douyin-Smart-Feed-Assistant
// @version      2.3.2
// @description  通过AI智能分析内容,优化你的信息流体验
// @author       Baianjo
// @match        *://www.douyin.com/*
// @connect      api.moonshot.cn
// @connect      api.deepseek.com
// @connect      dashscope.aliyuncs.com
// @connect      dashscope-intl.aliyuncs.com
// @connect      open.bigmodel.cn
// @connect      *
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        unsafeWindow
// @run-at       document-end
// @homepageURL  https://github.com/baianjo/Douyin-Smart-Feed-Assistant
// @supportURL   mailto:[email protected]
// @license      MIT
// ==/UserScript==
(() => {
  // src/config/catalog.ts
  var CONFIG = {
    // 默认配置
    defaults: {
      // API设置
      apiKey: "",
      customEndpoint: "https://api.deepseek.com/v1",
      // OpenAI 兼容 API Base URL(旧字段名,兼容现有 GM 存储)
      customModel: "",
      // 自定义模型名称
      customApiProfile: {
        baseUrl: "",
        apiKey: "",
        model: "",
        modelIds: [],
        fetchedAt: ""
      },
      apiProvider: "deepseek",
      judgeMode: "single",
      // 记住用户选择的模板
      selectedTemplate: "",
      // 空字符串表示"自定义规则"
      // 提示词
      promptLike: "\u6211\u5E0C\u671B\u770B\u5230\u79EF\u6781\u5411\u4E0A\u3001\u6709\u6559\u80B2\u610F\u4E49\u3001\u5C55\u793A\u7F8E\u597D\u4E8B\u7269\u7684\u5185\u5BB9\u3002",
      promptNeutral: "\u666E\u901A\u7684\u5A31\u4E50\u5185\u5BB9\u3001\u65E5\u5E38\u751F\u6D3B\u8BB0\u5F55\uFF0C\u4E0D\u7279\u522B\u63A8\u8350\u4E5F\u4E0D\u53CD\u5BF9\u3002",
      promptDislike: "\u4F4E\u4FD7\u3001\u66B4\u529B\u3001\u865A\u5047\u4FE1\u606F\u3001\u8FC7\u5EA6\u8425\u9500\u7684\u5185\u5BB9\u5E94\u8BE5\u88AB\u8FC7\u6EE4\u3002",
      // 行为控制
      minDelay: 1,
      maxDelay: 3,
      runDuration: 15,
      // 高级选项
      skipProbability: 8,
      watchBeforeLike: [2, 4],
      maxRetries: 3,
      enableComments: false,
      // UI状态
      panelMinimized: true,
      panelPosition: { x: window.innerWidth - 80, y: 100 }
    },
    /*
     * ⚠️ 重要:DOM选择器配置
     * 这是最容易失效的部分,抖音每次更新可能都需要调整
     *
     * 调试技巧:
     * 1. 打开F12开发者工具
     * 2. 点击左上角的"选择元素"图标
     * 3. 鼠标悬停在视频标题/作者/标签上
     * 4. 查看右侧高亮的HTML结构
     * 5. 复制类名或结构特征
     * 6. 添加到下面的数组中(优先级从上到下)
     */
    selectors: {
      // 视频标题
      title: [
        'div[class*="pQBVl"] span span span',
        // 当前主方案 (2025-10)
        '#slidelist [data-e2e="feed-item"] div[style*="lineClamp"]',
        ".video-info-detail span",
        '[data-e2e="feed-title"]'
      ],
      // 作者名称
      author: [
        '[data-e2e="feed-author-name"]',
        ".author-name",
        'a[class*="author"]',
        '[class*="AuthorName"]'
      ],
      // 标签(话题)
      tags: [
        'a[href*="/search/"]',
        ".tag-link",
        '[class*="hashtag"]',
        'a[class*="SLdJu"]'
        // 当前发现的标签类名
      ]
    },
    // ⚠️ 开发者维护区域:OpenAI 兼容 API Base URL 预设
    //
    // 📌 通用请求参数说明:
    //   - 填写具体值(如 temperature: 0.3)→ 发送到 API
    //   - 注释掉或删除该行 → 不发送,使用 API 默认值
    //   - stream: false 是必填项(禁用流式输出)
    //
    // 预设只负责回填 Base URL 和默认模型;实际请求始终走同一套 OpenAI 兼容逻辑。
    openAICompatibleRequestParams: {
      temperature: 0.3,
      max_tokens: 500,
      stream: false
    },
    modelLabelNotes: {
      "gemini-3.1-flash-lite-preview": "2026.5\uFF1A\u63A8\u8350\uFF0C\u514D\u8D39/\u4F4E\u6210\u672C",
      "glm-4.7-flash": "2026.5\uFF1A\u63A8\u8350\uFF0C/models \u53EF\u80FD\u4E0D\u8FD4\u56DE\u4F46\u53EF\u6B63\u5E38\u8C03\u7528",
      "glm-4-flash": "2026.5\uFF1A\u514D\u8D39",
      "qwen-flash": "2026.5\uFF1A\u4FBF\u5B9C\u5FEB\u901F"
    },
    // 手工维护的额外候选:只补充 /models 可能不返回但已验证可调用的模型,默认选择仍由成本启发式决定。
    modelSelectionOverrides: {
      gemini: {
        extraModelIds: ["gemini-3.1-flash-lite-preview"]
      },
      glm: {
        extraModelIds: ["glm-4.7-flash"]
      }
    },
    apiProviders: {
      gpt: {
        name: "GPT / OpenAI",
        baseUrl: "https://api.openai.com/v1",
        defaultModel: "gpt-4o-mini",
        models: [
          { value: "gpt-4o-mini", label: "gpt-4o-mini" },
          { value: "gpt-4o", label: "gpt-4o" }
        ]
      },
      deepseek: {
        name: "DeepSeek\uFF08\u63A8\u8350\uFF1A\u6700\u4FBF\u5B9C\uFF09",
        baseUrl: "https://api.deepseek.com/v1",
        defaultModel: "deepseek-chat",
        models: [
          { value: "deepseek-chat", label: "deepseek-chat (V3.2\u63A8\u8350)" }
        ]
      },
      kimi: {
        name: "Kimi / \u6708\u4E4B\u6697\u9762",
        baseUrl: "https://api.moonshot.cn/v1",
        defaultModel: "kimi-k2-0905-preview",
        models: [
          { value: "kimi-k2-0905-preview", label: "kimi-k2-0905-preview" }
        ]
      },
      qwen: {
        name: "Qwen / \u901A\u4E49\u5343\u95EE",
        baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
        defaultModel: "qwen-flash",
        models: [
          { value: "qwen-max", label: "qwen-max\uFF08\u6700\u5F3A\uFF09" },
          { value: "qwen-plus", label: "qwen-plus\uFF08\u63A8\u8350\uFF09" },
          { value: "qwen-flash", label: "qwen-flash\uFF08\u5FEB\u901F\uFF09" }
        ]
      },
      glm: {
        name: "GLM / \u667A\u8C31AI",
        baseUrl: "https://open.bigmodel.cn/api/paas/v4",
        defaultModel: "glm-4.7-flash",
        models: [
          { value: "glm-4.7-flash", label: "glm-4.7-flash" },
          { value: "glm-4.6", label: "glm-4.6" },
          { value: "glm-4-flash", label: "glm-4-flash\uFF08\u514D\u8D39\uFF09" }
        ]
      },
      gemini: {
        name: "Google / Gemini",
        baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai",
        defaultModel: "gemini-3.1-flash-lite-preview",
        models: [
          { value: "gemini-3.1-flash-lite-preview", label: "gemini-3.1-flash-lite-preview" },
          { value: "gemini-2.5-flash", label: "gemini-2.5-flash" },
          { value: "gemini-3-flash-preview", label: "gemini-3-flash-preview" }
        ]
      }
    },
    // ✅ 简化:从统一配置中获取默认模型
    getDefaultModel: (provider) => {
      return CONFIG.apiProviders[provider]?.defaultModel || "";
    },
    getProviderBaseUrl: (provider) => {
      return CONFIG.apiProviders[provider]?.baseUrl || "";
    },
    // 预设模板
    templates: {
      "\u7834\u9664\u4FE1\u606F\u8327\u623F": {
        like: "\u4E0D\u70B9\u8D5E\u3002",
        neutral: "\u5FFD\u7565\u548C\u4E0D\u611F\u5174\u8DA3\u4EFB\u610F\u70B9\u51FB\u3002",
        dislike: "\u5FFD\u7565\u548C\u4E0D\u611F\u5174\u8DA3\u4EFB\u610F\u70B9\u51FB\u3002"
      },
      "\u5C0F\u5B66\u5185\u5BB9\u5F15\u5BFC": {
        like: "\u5BF9\u5C0F\u5B66\u751F\u8DA3\u5473\u751F\u52A8\u7684STEM\u79D1\u666E\u3001\u5386\u53F2\u6545\u4E8B\uFF0C\u542F\u53D1\u5B66\u4E60\u5174\u8DA3\u4E0E\u597D\u5947\u5FC3\uFF1B\u5C55\u73B0\u4E2D\u56FD\u666E\u901A\u52B3\u52A8\u8005\u7684\u5949\u732E\uFF0C\u6216\u9002\u5408\u5C0F\u5B66\u751F\u540C\u60C5\u7684\u611F\u4EBA\u7684\u5BB6\u5EAD\u3001\u5E08\u751F\u3001\u540C\u5B66\u3001\u793E\u4F1A\u767E\u6001\u3001\u5BB6\u56FD\u60C5\u8C0A\u3001\u4E61\u571F\u60C5\u7ED3\uFF1B\u5206\u4EAB\u5C0F\u5347\u521D\u7684\u7ECF\u9A8C\u3001\u540D\u6821\u98CE\u5149\u3001\u4E3A\u4EC0\u4E48\u5B66\u4E60\u7B49\u5408\u7406\u7126\u8651\u7684\u6B63\u9762\u8BDD\u9898\uFF1B\u57F9\u517B\u81EA\u5F8B\u3001\u8BDA\u4FE1\u3001\u7231\u62A4\u5BB6\u4EBA\u3001\u5C0A\u91CD\u4ED6\u4EBA\u7684\u54C1\u683C\uFF1B\u5C55\u73B0\u81EA\u7136\u98CE\u5149\u3001\u521B\u610F\u624B\u5DE5\u3001\u5C0F\u5B66\u751F\u5065\u5EB7\u8FD0\u52A8\uFF0C\u57F9\u517B\u5BA1\u7F8E\u4E0E\u52A8\u624B\u80FD\u529B\uFF1B\u5B66\u4E60\u826F\u597D\u7684\u4EF7\u503C\u89C2\u548C\u91D1\u94B1\u89C2\u3002",
        neutral: "\u4E0D\u542B\u5F3A\u70C8\u4EF7\u503C\u89C2\u8F93\u51FA\u7684\u65E5\u5E38\u751F\u6D3B\u8BB0\u5F55\u3001\u793E\u4F1A\u65B0\u95FB\u3001\u840C\u5BA0\u3001\u7F8E\u98DF\u3001\u65C5\u884C\u7247\u6BB5\uFF1B\u975E\u4F4E\u4FD7\u7684\u5531\u6B4C\u3001\u4E50\u5668\u5F39\u594F\u7B49\u624D\u827A\u8868\u6F14\uFF1B\u975E\u66B4\u529B\u3001\u975E\u4E0A\u763E\u7684\u76CA\u667A\u7C7B\u6216\u521B\u610F\u7C7B\u6E38\u620F\u77ED\u89C6\u9891\uFF1B\u6B7B\u677F/\u4E0D\u591F\u901A\u4FD7/\u4E0D\u591F\u5F15\u4EBA\u5165\u80DC\u7684\u77E5\u8BC6\u3002",
        dislike: "\u65E0\u610F\u4E49\u7684\u73A9\u6897\u3001\u964D\u667A\u6076\u641E\uFF1B\u70AB\u5BCC\u6500\u6BD4\u3001\u5BA3\u626C\u8FC7\u5EA6\u6D88\u8D39\uFF1B\u5C55\u73B0\u4E0D\u5C0A\u91CD\u957F\u8F88\u3001\u5E08\u957F\uFF0C\u6076\u610F\u6349\u5F04\u4ED6\u4EBA\uFF0C\u6216\u4F20\u64AD\u8D1F\u9762\u60C5\u7EEA\u7684\u5185\u5BB9\uFF1B\u6613\u4E0A\u763E\u7684\u957F\u65F6\u95F4\u6E38\u620F\u76F4\u64AD/\u5F55\u64AD\uFF1B\u5305\u542B\u3001\u4F4E\u4FD7\u3001\u6027\u6697\u793A\u7684\u5185\u5BB9\u3002"
      },
      "\u4E2D\u5B66\u5185\u5BB9\u5F15\u5BFC": {
        like: "\u7CFB\u7EDF\u8BB2\u89E3\u79D1\u5B66\u3001\u6280\u672F\u3001\u5386\u53F2\u3001\u5546\u4E1A\u7B49\u9886\u57DF\u77E5\u8BC6\uFF0C\u6784\u5EFA\u77E5\u8BC6\u4F53\u7CFB\uFF1B\u4E13\u4E1A\u6280\u80FD\uFF08\u5982\u7F16\u7A0B\u3001\u8BBE\u8BA1\u3001\u6444\u5F71\uFF09\u7684\u5B66\u4E60\u5B9E\u8DF5\u8FC7\u7A0B\uFF1B\u5BF9\u65F6\u4E8B\u4E0E\u793E\u4F1A\u73B0\u8C61\u6709\u7406\u6709\u636E\u7684\u903B\u8F91\u5206\u6790\uFF0C\u63D0\u4F9B\u591A\u5143\u89C6\u89D2\uFF0C\u57F9\u517B\u72EC\u7ACB\u601D\u8003\uFF1B\u9876\u5C16\u5B66\u5E9C\u751F\u6D3B\u3001\u804C\u4E1A\u89C4\u5212\u4E0E\u4E2A\u4EBA\u6210\u957F\u7ECF\u9A8C\uFF1B\u9AD8\u8D28\u91CF\u7EAA\u5F55\u7247\uFF0C\u5C55\u73B0\u81EA\u7136\u4E0E\u6587\u5316\u539A\u91CD\uFF0C\u57F9\u517B\u4EBA\u6587\u5173\u6000\u4E0E\u793E\u4F1A\u8D23\u4EFB\u611F\u3002",
        neutral: "\u4E0D\u542B\u5F3A\u70C8\u4EF7\u503C\u89C2\u8F93\u51FA\u7684\u65E5\u5E38\u751F\u6D3BVlog\u3001\u7F8E\u98DF\u63A2\u5E97\u3001\u65C5\u884C\u8BB0\u5F55\uFF1B\u4E0D\u542B\u653B\u51FB\u6027\u7684\u666E\u901A\u65B0\u95FB\u8D44\u8BAF\uFF1B\u975E\u4E13\u4E1A\u3001\u7EAF\u5A31\u4E50\u6027\u8D28\u7684\u624D\u827A\u8868\u6F14\u3002",
        dislike: "\u7EAF\u7CB9\u73A9\u6897\u3001\u903B\u8F91\u7F3A\u5931\u7684\u62BD\u8C61\u5185\u5BB9\uFF1B\u65E0\u8282\u5236\u5BA3\u626C\u6D88\u8D39\u4E3B\u4E49\u3001\u70AB\u5BCC\uFF1B\u4F20\u64AD\u8D1F\u9762\u60C5\u7EEA\u3001\u5236\u9020\u6027\u522B\u5BF9\u7ACB\u6216\u793E\u4F1A\u77DB\u76FE\uFF1B\u5305\u542B\u6027\u6697\u793A\u3001\u89C2\u611F\u4E0D\u9002\u7684\u821E\u8E48\u3001\u4F4E\u4FD7\u7B11\u8BDD\u3002"
      },
      "\u6548\u7387\u4E0E\u77E5\u8BC6": {
        like: "\u5546\u4E1A\u5206\u6790\u3001\u79D1\u6280\u524D\u6CBF\u3001\u6280\u80FD\u5B66\u4E60\u3001\u6548\u7387\u5DE5\u5177\u3001\u6DF1\u5EA6\u601D\u8003\u7C7B\u5185\u5BB9\u3002\u6709\u4EF7\u503C\u3001\u6709\u542F\u53D1\u3002",
        neutral: "\u65B0\u95FB\u8D44\u8BAF\u3001\u884C\u4E1A\u52A8\u6001\u7B49\u4FE1\u606F\u7C7B\u5185\u5BB9\u3002",
        dislike: "\u5A31\u4E50\u516B\u5366\u3001\u60C5\u611F\u9E21\u6C64\u3001\u65E0\u610F\u4E49\u7684\u641E\u7B11\u89C6\u9891\u3001\u6807\u9898\u515A\u3002"
      },
      "\u65B0\u95FB\u4E0E\u65F6\u4E8B": {
        like: "\u4E25\u8083\u65B0\u95FB\u3001\u793E\u4F1A\u4E8B\u4EF6\u3001\u653F\u7B56\u89E3\u8BFB\u3001\u56FD\u9645\u5C40\u52BF\u3001\u7ECF\u6D4E\u5206\u6790\u7B49\u5BA2\u89C2\u7406\u6027\u7684\u5185\u5BB9\u3002",
        neutral: "\u5730\u65B9\u65B0\u95FB\u3001\u793E\u533A\u6545\u4E8B\u7B49\u533A\u57DF\u6027\u5185\u5BB9\u3002",
        dislike: "\u672A\u7ECF\u8BC1\u5B9E\u7684\u4F20\u8A00\u3001\u60C5\u7EEA\u5316\u717D\u52A8\u3001\u6781\u7AEF\u89C2\u70B9\u3002"
      },
      "\u5065\u5EB7\u751F\u6D3B": {
        like: "\u5065\u8EAB\u8FD0\u52A8\u3001\u8425\u517B\u996E\u98DF\u3001\u5FC3\u7406\u5065\u5EB7\u3001\u533B\u5B66\u79D1\u666E\u3001\u6237\u5916\u6D3B\u52A8\u7B49\u4FC3\u8FDB\u8EAB\u5FC3\u5065\u5EB7\u7684\u5185\u5BB9\u3002",
        neutral: "\u7F8E\u98DF\u63A2\u5E97\u3001\u65C5\u6E38vlog\u7B49\u751F\u6D3B\u65B9\u5F0F\u5185\u5BB9\u3002",
        dislike: "\u4F2A\u79D1\u5B66\u517B\u751F\u3001\u6781\u7AEF\u51CF\u80A5\u3001\u5371\u9669\u8FD0\u52A8\u3001\u4E0D\u5065\u5EB7\u7684\u751F\u6D3B\u65B9\u5F0F\u3002"
      },
      "\u827A\u672F\u5BA1\u7F8E": {
        like: "\u7ED8\u753B\u3001\u97F3\u4E50\u3001\u821E\u8E48\u3001\u6444\u5F71\u3001\u8BBE\u8BA1\u3001\u5EFA\u7B51\u7B49\u827A\u672F\u521B\u4F5C\u548C\u6B23\u8D4F\u5185\u5BB9\u3002\u6709\u7F8E\u611F\u3001\u6709\u6DF1\u5EA6\u3002",
        neutral: "\u666E\u901A\u7684\u624D\u827A\u5C55\u793A\u3001\u624B\u5DE5DIY\u7B49\u521B\u610F\u5185\u5BB9\u3002",
        dislike: "\u4F4E\u4FD7\u6A21\u4EFF\u3001\u5BA1\u7F8E\u5EB8\u4FD7\u3001\u6284\u88AD\u4F5C\u54C1\u3002"
      },
      "\u7F8E\u5973\u5BA1\u7F8E": {
        like: "\u9AD8\u989C\u503C\u3001\u8EAB\u6750\u59E3\u597D\u7684\u5E74\u8F7B\u5973\u6027\u4E3A\u7EDD\u5BF9\u4E3B\u89D2\u7684\u89C6\u9891\u3002tag\u53EF\u80FD\u662F\u821E\u8E48\u3001\u5FA1\u59D0\u3001\u9ED1\u4E1D\u3001cos\u3001\u5973\u53CB\u3001\u64E6\u8FB9\u3001\u6CF3\u88C5\u3001\u7A7F\u642D\u7B49\u3002",
        neutral: "\u5973\u6027\u7684\u5C55\u793A\u5185\u5BB9\uFF0C\u6216\u65E0\u6CD5\u5206\u8FA8\u662F\u4EC0\u4E48\u89C6\u9891\u7C7B\u578B\u3002\u89C6\u9891\u672A\u5B8C\u5168\u6EE1\u8DB3like\u6807\u51C6\u4E2D\u7684\u6210\u54C1\u8D28\u91CF\u548C\u89C6\u89C9\u805A\u7126\u8981\u6C42\uFF0C\u4F46\u53EA\u8981\u53EF\u80FD\u548C\u5973\u6027\u76F8\u5173\u5373\u53EF\uFF0C\u5373\u4F7F\u9700\u8981\u731C\u6D4B\u3002tag\u53EF\u80FD\u662F\u8868\u60C5\u7BA1\u7406\u3001\u745C\u4F3D\u3001\u7F8E\u989C\u7B49\u3002\u8FD9\u7C7B\u89C6\u9891\u6807\u9898\u5F80\u5F80\u662F\u65E0\u610F\u4E49\u7684\u8BDD\u751A\u81F3\u51E0\u4E4E\u65E0\u6807\u9898\uFF0C\u5982\u300C\u5FC3\u5F88\u8D35 \u4E00\u5B9A\u8981\u88C5\u6700\u7F8E\u7684\u4E1C\u897F/\u4F60\u60F3\u6211\u4E86\u5417\u300D",
        dislike: "\u4E25\u683C\u6392\u9664\u6240\u6709\u975E\u4E0A\u8FF0\u5B9A\u4E49\u7684\u89C6\u9891\u3002\u5305\u62EC\u4F46\u4E0D\u9650\u4E8E\uFF1A\u7EAF\u98CE\u666F\u3001\u65B0\u95FB\u3001\u65F6\u653F\u3001\u79D1\u666E\u3001\u6559\u80B2\u3001\u5F71\u89C6\u526A\u8F91\u3001\u52A8\u6F2B\u3001\u6E38\u620F\u3001\u7F8E\u98DF\u3001\u840C\u5BA0\u3001Vlog\u3001\u751F\u6D3B\u8BB0\u5F55\u3001\u5267\u60C5\u77ED\u5267\u3001\u624B\u5DE5\u3001\u7ED8\u753B\u7B49\u3002"
      },
      "\u5E05\u54E5\u5BA1\u7F8E": {
        "like": "\u9AD8\u989C\u503C\u3001\u8EAB\u6750\u59E3\u597D\u7684\u5E74\u8F7B\u7537\u6027\u4E3A\u7EDD\u5BF9\u4E3B\u89D2\u7684\u89C6\u9891\u3002tag\u53EF\u80FD\u662F\u821E\u8E48\u3001\u578B\u7537\u3001\u897F\u88C5\u3001\u808C\u8089\u3001\u8179\u808C\u3001cos\u3001\u7537\u53CB\u3001\u7537\u53CB\u89C6\u89D2\u3001\u64E6\u8FB9\u3001\u6CF3\u88E4\u3001\u7A7F\u642D\u3001\u7537\u795E\u7B49\u3002",
        "neutral": "\u7537\u6027\u7684\u5C55\u793A\u5185\u5BB9\uFF0C\u6216\u65E0\u6CD5\u5206\u8FA8\u662F\u4EC0\u4E48\u89C6\u9891\u7C7B\u578B\u3002\u89C6\u9891\u672A\u5B8C\u5168\u6EE1\u8DB3like\u6807\u51C6\u4E2D\u7684\u6210\u54C1\u8D28\u91CF\u548C\u89C6\u89C9\u805A\u7126\u8981\u6C42\uFF0C\u4F46\u53EA\u8981\u53EF\u80FD\u548C\u7537\u6027\u76F8\u5173\u5373\u53EF\uFF0C\u5373\u4F7F\u9700\u8981\u731C\u6D4B\u3002tag\u53EF\u80FD\u662F\u8868\u60C5\u7BA1\u7406\u3001\u5065\u8EAB\u3001\u8FD0\u52A8\u3001\u7F8E\u989C\u7B49\u3002\u8FD9\u7C7B\u89C6\u9891\u6807\u9898\u5F80\u5F80\u662F\u65E0\u610F\u4E49\u7684\u8BDD\u751A\u81F3\u51E0\u4E4E\u65E0\u6807\u9898\uFF0C\u5982\u300C\u4ECA\u5929\u7684\u5FC3\u60C5... / \u731C\u6211\u5728\u60F3\u4EC0\u4E48\u300D",
        "dislike": "\u4E25\u683C\u6392\u9664\u6240\u6709\u975E\u4E0A\u8FF0\u5B9A\u4E49\u7684\u89C6\u9891\u3002\u5305\u62EC\u4F46\u4E0D\u9650\u4E8E\uFF1A\u7EAF\u98CE\u666F\u3001\u65B0\u95FB\u3001\u65F6\u653F\u3001\u79D1\u666E\u3001\u6559\u80B2\u3001\u5F71\u89C6\u526A\u8F91\u3001\u52A8\u6F2B\u3001\u6E38\u620F\u3001\u7F8E\u98DF\u3001\u840C\u5BA0\u3001Vlog\u3001\u751F\u6D3B\u8BB0\u5F55\u3001\u5267\u60C5\u77ED\u5267\u3001\u624B\u5DE5\u3001\u7ED8\u753B\u7B49\u3002"
      }
    }
  };

  // src/runtime/context.ts
  var controllerRef = null;
  var uiRef = null;
  var setController = (controller) => {
    controllerRef = controller;
  };
  var getController = () => {
    if (!controllerRef) {
      throw new Error("Controller context has not been initialized yet.");
    }
    return controllerRef;
  };
  var setUI = (ui) => {
    uiRef = ui;
  };
  var getUI = () => {
    if (!uiRef) {
      throw new Error("UI context has not been initialized yet.");
    }
    return uiRef;
  };

  // src/ai/ai-service.ts
  var THINKING_TAG_PATTERN = /<(think|thinking)\b[^>]*>[\s\S]*?<\/\1>/gi;
  var ORPHAN_THINKING_END_TAG_PATTERN = /<\/(?:think|thinking)>\s*/gi;
  var normalizeContent = (rawContent) => {
    if (typeof rawContent === "string") {
      return rawContent;
    }
    if (Array.isArray(rawContent)) {
      return rawContent.map((part) => {
        if (typeof part === "string") {
          return part;
        }
        if (!part || typeof part !== "object") {
          return "";
        }
        if (typeof part.text === "string") {
          return part.text;
        }
        if (typeof part.content === "string") {
          return part.content;
        }
        if (part.type === "text" && typeof part.value === "string") {
          return part.value;
        }
        return "";
      }).join("");
    }
    if (rawContent && typeof rawContent === "object") {
      if (typeof rawContent.text === "string") {
        return rawContent.text;
      }
      if (typeof rawContent.content === "string") {
        return rawContent.content;
      }
    }
    return "";
  };
  var stripReasoningTags = (content) => {
    return content.replace(THINKING_TAG_PATTERN, "").replace(ORPHAN_THINKING_END_TAG_PATTERN, "").trim();
  };
  var extractReasoningText = (message) => {
    if (!message || typeof message !== "object") {
      return "";
    }
    return normalizeContent(message.reasoning_content) || normalizeContent(message.reasoning) || normalizeContent(message.thinking) || normalizeContent(message.thoughts);
  };
  var extractFinalContent = (data) => {
    let message = null;
    if (data?.choices?.[0]?.message) {
      message = data.choices[0].message;
    } else if (data?.message) {
      message = data.message;
    }
    if (!message) {
      return {
        content: "",
        hasReasoning: false,
        hasSupportedMessageShape: false
      };
    }
    const content = stripReasoningTags(normalizeContent(message.content));
    const reasoning = extractReasoningText(message);
    return {
      content,
      hasReasoning: Boolean(reasoning),
      hasSupportedMessageShape: true
    };
  };
  var cloneRequestParams = (params) => JSON.parse(JSON.stringify(params || {}));
  var normalizeOpenAICompatibleBaseUrl = (apiBaseUrl) => {
    let trimmed = (apiBaseUrl || "").trim().replace(/\/+$/, "");
    if (!trimmed) {
      return "";
    }
    trimmed = trimmed.replace(/\/chat\/completions$/i, "").replace(/\/models$/i, "").replace(/\/+$/, "");
    if (/\/v[\w.-]+$/i.test(trimmed) || /\/openai$/i.test(trimmed)) {
      return trimmed;
    }
    return `${trimmed}/v1`;
  };
  var getOpenAICompatibleChatEndpoint = (apiBaseUrl) => {
    const baseUrl = normalizeOpenAICompatibleBaseUrl(apiBaseUrl);
    return baseUrl ? `${baseUrl}/chat/completions` : "";
  };
  var getOpenAICompatibleModelsEndpoint = (apiBaseUrl) => {
    const baseUrl = normalizeOpenAICompatibleBaseUrl(apiBaseUrl);
    return baseUrl ? `${baseUrl}/models` : "";
  };
  var parseModelIds = (data) => {
    const entries = Array.isArray(data) ? data : data?.data;
    if (!Array.isArray(entries)) {
      return [];
    }
    const seen = /* @__PURE__ */ new Set();
    const models = [];
    entries.forEach((entry) => {
      const id = typeof entry === "string" ? entry : entry?.id;
      if (typeof id !== "string") {
        return;
      }
      const trimmed = id.trim();
      if (!trimmed || seen.has(trimmed)) {
        return;
      }
      seen.add(trimmed);
      models.push(trimmed);
    });
    return models;
  };
  var uniqueModelIds = (modelIds) => {
    const seen = /* @__PURE__ */ new Set();
    const unique = [];
    modelIds.forEach((modelId) => {
      if (typeof modelId !== "string") {
        return;
      }
      const trimmed = modelId.trim();
      if (!trimmed || seen.has(trimmed)) {
        return;
      }
      seen.add(trimmed);
      unique.push(trimmed);
    });
    return unique;
  };
  var getManualModelIdsForProvider = (providerId) => {
    const override = CONFIG.modelSelectionOverrides?.[providerId];
    if (!override) {
      return [];
    }
    return uniqueModelIds(Array.isArray(override.extraModelIds) ? override.extraModelIds : []);
  };
  var mergeModelIdsWithManualSelections = (modelIds, providerId) => {
    if (!providerId || providerId === "custom") {
      return uniqueModelIds(modelIds);
    }
    return uniqueModelIds([
      ...getManualModelIdsForProvider(providerId),
      ...modelIds
    ]);
  };
  var scoreModelForCost = (modelId) => {
    const id = modelId.toLowerCase();
    let score = 1e3;
    if (id.includes("free")) score -= 600;
    if (id.includes("flash-lite")) score -= 520;
    if (id.includes("lite")) score -= 500;
    if (id.includes("flash")) score -= 450;
    if (id.includes("mini")) score -= 400;
    if (id.includes("nano")) score -= 380;
    if (id.includes("small")) score -= 300;
    if (/(image|vision|embedding|audio|tts|whisper|moderation|rerank)/.test(id)) score += 4e3;
    if (/(reasoner|thinking|r1)/.test(id)) score += 700;
    if (id.includes("codex")) score += 800;
    if (id.includes("pro")) score += 300;
    if (id.includes("max")) score += 250;
    if (id.includes("plus")) score += 150;
    return score;
  };
  var getModelVersionParts = (modelId) => {
    const versionMatch = modelId.match(/(?:^|[-_/])v?(\d+(?:\.\d+)*)(?=$|[-_/])/i);
    if (!versionMatch) {
      return [];
    }
    return versionMatch[1].split(".").map((part) => Number(part));
  };
  var compareModelVersionDesc = (a, b) => {
    const aParts = getModelVersionParts(a);
    const bParts = getModelVersionParts(b);
    const maxLength = Math.max(aParts.length, bParts.length);
    for (let index = 0; index < maxLength; index += 1) {
      const aPart = aParts[index] ?? 0;
      const bPart = bParts[index] ?? 0;
      if (aPart !== bPart) {
        return bPart - aPart;
      }
    }
    return 0;
  };
  var chooseDefaultModel = (modelIds, mode = "preset") => {
    if (mode === "custom" || !Array.isArray(modelIds) || modelIds.length === 0) {
      return "";
    }
    const uniqueModels = uniqueModelIds(modelIds);
    return [...uniqueModels].sort((a, b) => {
      const scoreDiff = scoreModelForCost(a) - scoreModelForCost(b);
      if (scoreDiff !== 0) {
        return scoreDiff;
      }
      const versionDiff = compareModelVersionDesc(a, b);
      if (versionDiff !== 0) {
        return versionDiff;
      }
      const lengthDiff = a.length - b.length;
      if (lengthDiff !== 0) {
        return lengthDiff;
      }
      return a.localeCompare(b);
    })[0];
  };
  var formatModelOptionLabel = (modelId, fallbackLabel = modelId) => {
    const note = CONFIG.modelLabelNotes?.[modelId];
    return note ? `${fallbackLabel}\uFF08${note}\uFF09` : fallbackLabel;
  };
  var getProviderConfig = (providerId) => {
    return CONFIG.apiProviders[providerId];
  };
  var getProviderModel = (providerId, savedModel) => {
    if (savedModel) {
      return savedModel;
    }
    return CONFIG.getDefaultModel(providerId);
  };
  var isReasoningField = (key) => {
    return [
      "reasoning_content",
      "reasoning",
      "internal_reasoning",
      "thinking",
      "thought",
      "thoughts"
    ].includes(key.toLowerCase());
  };
  var redactReasoningFields = (value) => {
    if (Array.isArray(value)) {
      return value.map(redactReasoningFields);
    }
    if (!value || typeof value !== "object") {
      return value;
    }
    return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
      if (isReasoningField(key)) {
        const length = typeof entry === "string" ? entry.length : JSON.stringify(entry ?? "").length;
        return [key, `[\u5DF2\u7701\u7565\u601D\u8003\u5185\u5BB9\uFF0C\u957F\u5EA6\u7EA6 ${length} \u5B57\u7B26]`];
      }
      return [key, redactReasoningFields(entry)];
    }));
  };
  var sanitizeDebugResponse = (responseText, maxLength = 1e3) => {
    try {
      return JSON.stringify(redactReasoningFields(JSON.parse(responseText)), null, 2).substring(0, maxLength);
    } catch {
      return responseText.substring(0, maxLength);
    }
  };
  var AIService = {
    /*
     * 调用AI API
     *
     * 所有预设都按 OpenAI 兼容 API 处理;厂商选项只负责预填 Base URL 和模型。
     */
    callAPI: (messages, config) => {
      return new Promise((resolve, reject) => {
        const provider = getProviderConfig(config.apiProvider);
        const apiBaseUrl = config.apiProvider === "custom" ? config.customEndpoint : CONFIG.getProviderBaseUrl(config.apiProvider);
        const endpoint = getOpenAICompatibleChatEndpoint(apiBaseUrl);
        if (!endpoint) {
          reject(new Error("\u8BF7\u586B\u5199 OpenAI \u517C\u5BB9 API Base URL"));
          return;
        }
        if (config.apiProvider !== "custom" && !provider) {
          reject(new Error("\u672A\u77E5\u7684 API Base URL \u9884\u8BBE"));
          return;
        }
        const headers = {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${config.apiKey}`
        };
        const modelName = config.apiProvider === "custom" ? config.customModel : getProviderModel(config.apiProvider, config.customModel);
        if (!modelName) {
          reject(new Error("\u8BF7\u5148\u9009\u62E9\u6A21\u578B\u3002\u5EFA\u8BAE\u5148\u70B9\u51FB\u201C\u2460 \u83B7\u53D6\u6A21\u578B\u201D\uFF0C\u518D\u9009\u62E9\u6A21\u578B\u5E76\u6D4B\u8BD5\u8FDE\u63A5"));
          return;
        }
        const baseBody = {
          model: modelName,
          messages
        };
        const body = {
          ...baseBody,
          ...cloneRequestParams(CONFIG.openAICompatibleRequestParams)
        };
        getUI().log("\u2139\uFE0F \u4F7F\u7528 OpenAI \u517C\u5BB9\u901A\u7528\u8BF7\u6C42\u4F53\uFF0C\u4E0D\u6CE8\u5165\u5382\u5546\u4E13\u5C5E\u53C2\u6570", "info", "debug");
        getUI().log(`\u{1F4E1} \u8BF7\u6C42\u5730\u5740: ${endpoint}`, "info", "debug");
        getUI().log(`\u{1F916} \u4F7F\u7528\u6A21\u578B: ${body.model}`, "info", "debug");
        getUI().log(`\u2699\uFE0F \u53C2\u6570: temperature=${body.temperature}, max_tokens=${body.max_tokens}, stream=${body.stream}`, "info", "debug");
        getUI().log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u{1F4E1} \u8BF7\u6C42\u8BE6\u60C5 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", "info", "debug");
        getUI().log(`\u{1F310} \u5B8C\u6574 URL: ${endpoint}`, "info", "debug");
        getUI().log("\u{1F511} Authorization: Bearer [\u5DF2\u9690\u85CF]", "info", "debug");
        getUI().log(`\u{1F4E6} \u8BF7\u6C42\u4F53\u5173\u952E\u5B57\u6BB5:`, "info", "debug");
        getUI().log(`  \u2022 model: ${body.model}`, "info", "debug");
        getUI().log(`  \u2022 temperature: ${body.temperature}`, "info", "debug");
        getUI().log(`  \u2022 max_tokens: ${body.max_tokens}`, "info", "debug");
        getUI().log(`  \u2022 stream: ${body.stream}`, "info", "debug");
        if (body.thinking) {
          getUI().log(`  \u2022 thinking: ${JSON.stringify(body.thinking)}`, "warning", "debug");
        }
        getUI().log(`\u{1F4C4} \u5B8C\u6574\u8BF7\u6C42\u4F53 JSON (\u524D 800 \u5B57\u7B26):`, "info", "debug");
        getUI().log(JSON.stringify(body, null, 2).substring(0, 800), "info", "debug");
        getUI().log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", "info", "debug");
        getUI().log("\u23F3 \u6B63\u5728\u53D1\u9001\u8BF7\u6C42...", "info", "debug");
        let waitCount = 0;
        const waitTimer = setInterval(() => {
          waitCount++;
          getUI().log(`\u23F3 \u7B49\u5F85\u670D\u52A1\u5668\u54CD\u5E94... (${waitCount * 2}\u79D2)`, "info", "debug");
        }, 2e3);
        GM_xmlhttpRequest({
          method: "POST",
          url: endpoint,
          headers,
          data: JSON.stringify(body),
          timeout: 3e4,
          onload: (response) => {
            clearInterval(waitTimer);
            getUI().log("\u2705 \u6536\u5230\u54CD\u5E94", "success");
            getUI().log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u{1F4E5} \u54CD\u5E94\u8BE6\u60C5 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", "info", "debug");
            getUI().log(`\u{1F4CA} \u72B6\u6001\u7801: ${response.status} ${response.statusText}`, "info", "debug");
            getUI().log(`\u{1F4C4} \u54CD\u5E94\u4F53\u524D 1000 \u5B57\u7B26\uFF08\u601D\u8003\u5185\u5BB9\u5DF2\u7701\u7565\uFF09:`, "info", "debug");
            getUI().log(sanitizeDebugResponse(response.responseText, 1e3), "info", "debug");
            getUI().log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", "info", "debug");
            try {
              if (response.status !== 200) {
                getUI().log(`\u274C HTTP ${response.status}: ${response.statusText}`, "error");
                reject(new Error(`HTTP ${response.status}: ${sanitizeDebugResponse(response.responseText, 200)}`));
                return;
              }
              const data = JSON.parse(response.responseText);
              const extraction = extractFinalContent(data);
              if (!extraction.hasSupportedMessageShape) {
                getUI().log(`\u26A0\uFE0F \u672A\u77E5\u54CD\u5E94\u683C\u5F0F: ${JSON.stringify(data).substring(0, 300)}`, "error");
                throw new Error("API \u8FD4\u56DE\u4E86\u4E0D\u652F\u6301\u7684\u683C\u5F0F\uFF0C\u8BF7\u68C0\u67E5\u6A21\u578B\u662F\u5426\u6B63\u786E");
              }
              const content = extraction.content;
              if (extraction.hasReasoning) {
                getUI().log("\u{1F9E0} \u68C0\u6D4B\u5230\u6A21\u578B\u8FD4\u56DE\u601D\u8003\u5185\u5BB9\uFF0C\u5DF2\u5FFD\u7565\uFF0C\u4EC5\u4F7F\u7528\u6700\u7EC8\u56DE\u7B54", "info", "debug");
              }
              if (!content) {
                if (extraction.hasReasoning) {
                  throw new Error(
                    "\u6A21\u578B\u672A\u8FD4\u56DE\u6700\u7EC8\u56DE\u7B54\n\nAPI \u53EA\u8FD4\u56DE\u4E86\u601D\u8003\u5185\u5BB9\uFF0C\u811A\u672C\u4E0D\u4F1A\u628A\u601D\u8003\u8FC7\u7A0B\u5F53\u4F5C\u5224\u5B9A\u7ED3\u679C\u3002\n\u8BF7\u964D\u4F4E/\u5173\u95ED\u601D\u8003\u6A21\u5F0F\uFF0C\u6216\u5207\u6362\u5230\u4F1A\u8FD4\u56DE\u6700\u7EC8 content \u7684\u6A21\u578B\u3002"
                  );
                }
                throw new Error("API \u8FD4\u56DE\u7A7A\u5185\u5BB9\n\n\u539F\u59CB\u54CD\u5E94\u7247\u6BB5:\n" + sanitizeDebugResponse(response.responseText, 500));
              }
              getUI().log("\u2705 AI \u54CD\u5E94\u6210\u529F", "success");
              resolve(content);
            } catch (e) {
              getUI().log(`\u{1F4A5} \u89E3\u6790\u5931\u8D25: ${e.message}`, "error");
              reject(new Error(`${e.message}
\u539F\u59CB\u54CD\u5E94: ${sanitizeDebugResponse(response.responseText, 500)}`));
            }
          },
          onerror: (error) => {
            clearInterval(waitTimer);
            const msg = `\u{1F310} \u7F51\u7EDC\u9519\u8BEF - ${error.statusText || error.error || "\u8FDE\u63A5\u5931\u8D25"}`;
            getUI().log(msg, "error");
            reject(new Error(msg));
          },
          ontimeout: () => {
            clearInterval(waitTimer);
            getUI().log("\u23F1\uFE0F \u8BF7\u6C42\u8D85\u65F6\uFF0830\u79D2\uFF09", "error");
            reject(new Error("\u8BF7\u6C42\u8D85\u65F6\uFF0C\u53EF\u80FD\u662F\u7F51\u7EDC\u95EE\u9898\u6216\u6A21\u578B\u54CD\u5E94\u8FC7\u6162"));
          }
        });
      });
    },
    fetchModels: async (config) => {
      getUI().log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "info");
      getUI().log("\u{1F4DA} \u5F00\u59CB\u83B7\u53D6\u53EF\u7528\u6A21\u578B", "info");
      getUI().log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "info");
      return new Promise((resolve, reject) => {
        const apiBaseUrl = config.apiProvider === "custom" ? config.customEndpoint : CONFIG.getProviderBaseUrl(config.apiProvider);
        const endpoint = getOpenAICompatibleModelsEndpoint(apiBaseUrl);
        if (!endpoint) {
          reject(new Error("\u8BF7\u586B\u5199 OpenAI \u517C\u5BB9 API Base URL"));
          return;
        }
        getUI().log(`\u{1F310} \u6A21\u578B\u5217\u8868 URL: ${endpoint}`, "info", "debug");
        getUI().log("\u{1F511} Authorization: Bearer [\u5DF2\u9690\u85CF]", "info", "debug");
        GM_xmlhttpRequest({
          method: "GET",
          url: endpoint,
          headers: {
            "Authorization": `Bearer ${config.apiKey}`
          },
          timeout: 3e4,
          onload: (response) => {
            try {
              if (response.status !== 200) {
                reject(new Error(`HTTP ${response.status}: ${sanitizeDebugResponse(response.responseText, 300)}`));
                return;
              }
              const data = JSON.parse(response.responseText);
              const parsedModels = parseModelIds(data);
              const models = config.apiProvider === "custom" ? parsedModels : mergeModelIdsWithManualSelections(parsedModels, config.apiProvider);
              if (models.length === 0) {
                reject(new Error("API \u6CA1\u6709\u8FD4\u56DE\u53EF\u7528\u6A21\u578B\uFF0C\u8BF7\u624B\u52A8\u586B\u5199\u6A21\u578B\u540D\u79F0\u6216\u68C0\u67E5 /models \u63A5\u53E3"));
                return;
              }
              const defaultModel = chooseDefaultModel(
                models,
                config.apiProvider === "custom" ? "custom" : "preset"
              );
              getUI().log(`\u2705 \u6210\u529F\u83B7\u53D6 ${models.length} \u4E2A\u6A21\u578B`, "success");
              resolve({ models, defaultModel });
            } catch (e) {
              reject(new Error(`\u6A21\u578B\u5217\u8868\u89E3\u6790\u5931\u8D25: ${e.message}`));
            }
          },
          onerror: (error) => {
            reject(new Error(`\u83B7\u53D6\u6A21\u578B\u5931\u8D25 - ${error.statusText || error.error || "\u8FDE\u63A5\u5931\u8D25"}`));
          },
          ontimeout: () => {
            reject(new Error("\u83B7\u53D6\u6A21\u578B\u8D85\u65F6\uFF0830\u79D2\uFF09"));
          }
        });
      });
    },
    // 测试API连接
    testAPI: async (config) => {
      getUI().log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "info");
      getUI().log("\u{1F9EA} \u5F00\u59CB\u6D4B\u8BD5 API \u8FDE\u63A5", "info");
      getUI().log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "info");
      getUI().log(`\u{1F4CC} \u914D\u7F6E\u5FEB\u7167:`, "info", "debug");
      getUI().log(`  \u2022 API Base URL \u9884\u8BBE: ${config.apiProvider}`, "info", "debug");
      getUI().log(`  \u2022 API Key: ${config.apiKey ? "\u5DF2\u586B\u5199" : "\u672A\u586B\u5199"}`, "info", "debug");
      getUI().log(`  \u2022 API Base URL: ${config.customEndpoint || "(\u7A7A)"}`, "info");
      getUI().log(`  \u2022 \u6A21\u578B: ${config.customModel || "(\u7A7A - \u4F7F\u7528\u9884\u8BBE\u9ED8\u8BA4)"}`, "info");
      getUI().log("", "info");
      const testMessages = [
        { role: "user", content: '\u8BF7\u53EA\u56DE\u590D"\u8FDE\u63A5\u6210\u529F"\uFF0C\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u601D\u8003\u8FC7\u7A0B\u3002' }
      ];
      try {
        const response = await AIService.callAPI(testMessages, config);
        getUI().log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "success");
        getUI().log("\u2705 API \u6D4B\u8BD5\u6210\u529F\uFF01", "success");
        getUI().log(`\u{1F4E8} AI \u54CD\u5E94\u5185\u5BB9: ${response.substring(0, 100)}`, "success");
        getUI().log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "success");
        return { success: true, message: response };
      } catch (e) {
        getUI().log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "error");
        getUI().log("\u274C API \u6D4B\u8BD5\u5931\u8D25\uFF01", "error");
        getUI().log(`\u{1F4DB} \u9519\u8BEF\u6D88\u606F: ${e.message}`, "error");
        getUI().log("\u{1F4A1} \u8BF7\u68C0\u67E5\u4E0A\u65B9\u7684\u8BF7\u6C42/\u54CD\u5E94\u8BE6\u60C5", "warning");
        getUI().log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "error");
        return { success: false, message: e.message };
      }
    },
    // 单次判定模式(推荐)
    judgeSingle: async (dossier, config) => {
      const prompt = `\u4F60\u662F\u4E00\u4E2A\u5185\u5BB9\u5206\u7C7B\u52A9\u624B\u3002\u73B0\u5728\u7ED9\u51FA\u4E09\u79CD\u89C4\u5219\uFF1A\u300C
\u3010\u70B9\u8D5E\u89C4\u5219\u3011
${config.promptLike}

\u3010\u5FFD\u7565\u89C4\u5219\u3011
${config.promptNeutral}

\u3010\u4E0D\u611F\u5174\u8DA3\u89C4\u5219\u3011
${config.promptDislike}
\u300D
\u8BF7\u6839\u636E\u4EE5\u4E0A\u89C4\u5219\u5224\u65AD\u4E0B\u8FF0\u89C6\u9891\u5185\u5BB9\uFF1A\u300C
\u3010\u89C6\u9891\u5185\u5BB9\u3011
${dossier}
\u300D
**\u91CD\u8981\u63D0\u793A**\uFF1A\u6807\u7B7E\u53EF\u80FD\u5305\u542B\u5E72\u6270\u6216\u5BF9\u4E0D\u4E0A\u8BE5\u89C6\u9891\u6807\u9898\u7684\u4FE1\u606F\u3002
\u8BF7\u76F4\u63A5\u56DE\u7B54\u4EE5\u4E0BJSON\u683C\u5F0F\uFF0C\u4E0D\u8981\u6709\u4EFB\u4F55\u5176\u4ED6\u5185\u5BB9\uFF1B\u4E0D\u8981\u8F93\u51FA\u63A8\u7406/\u601D\u8003\u8FC7\u7A0B\uFF0C\u4E0D\u8981\u5305\u542B <think> \u6807\u7B7E\uFF1A
{"action": "like/neutral/dislike", "reason": "\u7B80\u77ED\u7406\u7531"}`;
      const messages = [{ role: "user", content: prompt }];
      const response = await AIService.callAPI(messages, config);
      const jsonMatch = response.match(/\{[^}]+\}/);
      if (jsonMatch) {
        return JSON.parse(jsonMatch[0]);
      }
      if (response.includes("like") || response.includes("\u70B9\u8D5E")) {
        return { action: "like", reason: response };
      }
      if (response.includes("dislike") || response.includes("\u4E0D\u611F\u5174\u8DA3")) {
        return { action: "dislike", reason: response };
      }
      return { action: "neutral", reason: response };
    },
    // 主判定入口
    judge: async (dossier, config) => {
      return await AIService.judgeSingle(dossier, config);
    }
  };

  // src/utils/index.ts
  var Utils = {
    // 随机延迟(模拟人类行为)
    randomDelay: (min, max) => {
      return new Promise((resolve) => {
        const delay = (Math.random() * (max - min) + min) * 1e3;
        setTimeout(resolve, delay);
      });
    },
    // 查找元素(支持多套备用选择器)
    findElement: (selectors, root = document) => {
      for (const selector of selectors) {
        try {
          const el = root.querySelector(selector);
          if (el) return el;
        } catch (e) {
          console.warn(`[\u667A\u80FD\u52A9\u624B] \u9009\u62E9\u5668\u5931\u8D25: ${selector}`, e);
        }
      }
      return null;
    },
    // 查找所有元素
    findElements: (selectors, root = document) => {
      for (const selector of selectors) {
        try {
          const els = root.querySelectorAll(selector);
          if (els.length > 0) return Array.from(els);
        } catch (e) {
          console.warn(`[\u667A\u80FD\u52A9\u624B] \u9009\u62E9\u5668\u5931\u8D25: ${selector}`, e);
        }
      }
      return [];
    },
    /*
     * 模拟键盘快捷键
     *
     * 抖音网页版快捷键(可能随版本变化):
     * - Z: 点赞/取消点赞
     * - X: 打开/关闭评论区
     * - R: 标记"不感兴趣"
     * - ArrowDown/↓: 下一个视频
     * - ArrowUp/↑: 上一个视频
     * - Space: 播放/暂停
     *
     * 如果抖音修改了快捷键,请在这里更新
     */
    pressKey: (key) => {
      const keyMap = {
        "z": { key: "z", code: "KeyZ", keyCode: 90 },
        "x": { key: "x", code: "KeyX", keyCode: 88 },
        "r": { key: "r", code: "KeyR", keyCode: 82 },
        "ArrowDown": { key: "ArrowDown", code: "ArrowDown", keyCode: 40 },
        "ArrowUp": { key: "ArrowUp", code: "ArrowUp", keyCode: 38 },
        "Space": { key: " ", code: "Space", keyCode: 32 }
      };
      const config = keyMap[key] || { key, code: key, keyCode: key.charCodeAt(0) };
      const event = new KeyboardEvent("keydown", {
        key: config.key,
        code: config.code,
        keyCode: config.keyCode,
        bubbles: true,
        cancelable: true
      });
      document.dispatchEvent(event);
    },
    // 等待元素出现
    waitForElement: (selectors, timeout = 5e3) => {
      return new Promise((resolve) => {
        const startTime = Date.now();
        const timer = setInterval(() => {
          const el = Utils.findElement(selectors);
          if (el || Date.now() - startTime > timeout) {
            clearInterval(timer);
            resolve(el);
          }
        }, 100);
      });
    },
    // 提取文本
    extractText: (element) => {
      if (!element) return "";
      return element.innerText || element.textContent || "";
    },
    // 格式化时间
    formatTime: (seconds) => {
      const m = Math.floor(seconds / 60);
      const s = Math.floor(seconds % 60);
      return `${m}:${s.toString().padStart(2, "0")}`;
    }
  };

  // src/extractor/video-extractor.ts
  var VideoExtractor = {
    // 🆕 通过视口中心定位当前视频容器
    getCurrentFeedItem: () => {
      const centerX = window.innerWidth / 2;
      const centerY = window.innerHeight / 2;
      const centerEl = document.elementFromPoint(centerX, centerY);
      if (!centerEl) {
        getUI().log("\u26A0\uFE0F \u65E0\u6CD5\u5B9A\u4F4D\u4E2D\u5FC3\u5143\u7D20", "warning");
        return null;
      }
      const feedItem = centerEl.closest('[data-e2e="feed-item"]');
      if (!feedItem) {
        getUI().log("\u26A0\uFE0F \u672A\u627E\u5230 feed-item \u5BB9\u5668", "warning");
      }
      return feedItem;
    },
    // 🆕 获取完整标题(改进版:不主动点击展开)
    getFullTitle: (container) => {
      if (!container) return "";
      const titleSelectors = [
        'div[class*="pQBVl"]',
        // 🆕 改为选择整个容器,而不是内部 span
        'div[data-e2e="video-desc"]',
        ".video-info-detail",
        '[data-e2e="feed-title"]'
      ];
      for (const selector of titleSelectors) {
        const el = container.querySelector(selector);
        if (el) {
          const text = el.innerText || el.textContent || "";
          const lines = text.split("\n");
          let cleanText = "";
          for (const line of lines) {
            if (line.trim().startsWith("#")) break;
            cleanText += line + " ";
          }
          cleanText = cleanText.trim();
          cleanText = cleanText.replace(/展开$/, "").trim();
          if (cleanText.length > 2) {
            return cleanText;
          }
        }
      }
      return "";
    },
    // 获取当前视频信息
    getCurrentVideoInfo: async (_config) => {
      await new Promise((r) => setTimeout(r, 500));
      let feedItem = null;
      const maxAttempts = 15;
      const retryDelayMs = 250;
      for (let attempt = 0; attempt < maxAttempts; attempt++) {
        feedItem = VideoExtractor.getCurrentFeedItem();
        if (feedItem) {
          const rect = feedItem.getBoundingClientRect();
          const isInView = rect.top < window.innerHeight && rect.bottom > 0;
          if (isInView) {
            if (attempt > 0) {
              getUI().log(`\u2705 \u91CD\u8BD5\u6210\u529F\uFF08\u7B2C ${attempt + 1} \u6B21\uFF09`, "success");
            }
            break;
          } else {
            getUI().log(`\u26A0\uFE0F \u627E\u5230\u5143\u7D20\u4F46\u4E0D\u5728\u89C6\u53E3 (y: ${rect.top.toFixed(0)})\uFF0C\u7B49\u5F85 ${retryDelayMs}ms \u540E\u91CD\u8BD5`, "warning");
            feedItem = null;
          }
        } else {
          if (attempt === 0) {
            const centerEl = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2);
            if (centerEl) {
              getUI().log(`\u{1F4CD} \u4E2D\u5FC3\u5143\u7D20: <${centerEl.tagName.toLowerCase()}> class="${centerEl.className?.substring(0, 60) || "(\u65E0)"}"`, "warning", "debug");
            }
          }
          getUI().log(`\u26A0\uFE0F \u672A\u627E\u5230 feed-item\uFF08\u5C1D\u8BD5 ${attempt + 1}/${maxAttempts}\uFF09\uFF0C\u7B49\u5F85 ${retryDelayMs}ms \u540E\u91CD\u8BD5`, "warning");
        }
        if (attempt < maxAttempts - 1) {
          await new Promise((r) => setTimeout(r, retryDelayMs));
        }
      }
      if (!feedItem) {
        return null;
      }
      const info = {
        title: "",
        author: "",
        tags: [],
        url: window.location.href,
        isLive: false
      };
      info.isLive = !!(feedItem.querySelector('[data-e2e="feed-live"]') || feedItem.querySelector(".live-icon") || feedItem.querySelector('a[data-e2e="live-slider"]'));
      if (info.isLive) {
        getUI().log("\u{1F534} \u68C0\u6D4B\u5230\u76F4\u64AD\uFF0C\u8DF3\u8FC7\u4FE1\u606F\u63D0\u53D6", "info");
        return info;
      }
      info.title = VideoExtractor.getFullTitle(feedItem);
      if (info.title.length < 3) {
        await Utils.randomDelay(0.5, 0.5);
        info.title = VideoExtractor.getFullTitle(feedItem);
      }
      const authorSelectors = [
        '[data-e2e="feed-author-name"]',
        ".author-name",
        'a[class*="author"]',
        '[class*="AuthorName"]'
      ];
      for (const selector of authorSelectors) {
        const el = feedItem.querySelector(selector);
        if (el) {
          info.author = Utils.extractText(el).trim();
          break;
        }
      }
      const tagEls = feedItem.querySelectorAll('a[href*="/search/"]');
      info.tags = Array.from(tagEls).slice(0, 3).map((el) => Utils.extractText(el).trim()).filter((t) => t.startsWith("#"));
      getUI().log(`\u{1F4FA} \u6807\u9898: ${info.title.substring(0, 40)}${info.title.length > 40 ? "..." : ""}`, "success");
      if (info.author) getUI().log(`\u{1F464} \u4F5C\u8005: ${info.author}`, "info");
      if (info.tags.length > 0) getUI().log(`\u{1F3F7}\uFE0F \u6807\u7B7E: ${info.tags.join(", ")}`, "info");
      return info;
    },
    // 构建内容档案
    buildDossier: (info) => {
      const parts = [];
      if (info.author) parts.push(`\u4F5C\u8005\uFF1A${info.author}`);
      if (info.title) parts.push(`\u6807\u9898\uFF1A${info.title}`);
      if (info.tags.length > 0) parts.push(`\u6807\u7B7E\uFF1A${info.tags.join(", ")}`);
      return parts.join("\u3002");
    },
    // 执行操作(简化版,不再需要回滚)
    executeAction: async (action, config) => {
      const [minWatch, maxWatch] = config.watchBeforeLike;
      const watchTime = Math.random() * (maxWatch - minWatch) + minWatch;
      getUI().log(`\u23F1\uFE0F \u89C2\u770B ${watchTime.toFixed(1)} \u79D2...`, "info");
      await Utils.randomDelay(minWatch, maxWatch);
      switch (action) {
        case "like":
          getUI().log("\u{1F44D} \u6267\u884C: \u70B9\u8D5E", "success");
          Utils.pressKey("z");
          await Utils.randomDelay(2, 3);
          break;
        case "dislike":
          getUI().log("\u{1F44E} \u6267\u884C: \u4E0D\u611F\u5174\u8DA3", "warning");
          Utils.pressKey("r");
          await Utils.randomDelay(0.5, 1);
          return;
        // 不感兴趣会自动跳转,不需要手动下滚
        case "neutral":
          getUI().log("\u27A1\uFE0F \u6267\u884C: \u5FFD\u7565", "info");
          break;
      }
      getUI().log("\u2B07\uFE0F \u5207\u6362\u5230\u4E0B\u4E00\u4E2A\u89C6\u9891...", "info");
      Utils.pressKey("ArrowDown");
      await Utils.randomDelay(1, 1.5);
    }
  };

  // src/storage/config-storage.ts
  var createDefaultCustomApiProfile = () => JSON.parse(JSON.stringify(CONFIG.defaults.customApiProfile));
  var normalizeCustomApiProfile = (profile) => {
    const normalized = createDefaultCustomApiProfile();
    if (!profile || typeof profile !== "object") {
      return normalized;
    }
    ["baseUrl", "apiKey", "model", "fetchedAt"].forEach((key) => {
      if (typeof profile[key] === "string") {
        normalized[key] = profile[key];
      }
    });
    if (Array.isArray(profile.modelIds)) {
      normalized.modelIds = [...new Set(profile.modelIds.filter((id) => typeof id === "string").map((id) => id.trim()).filter(Boolean))];
    }
    return normalized;
  };
  var loadConfig = () => {
    try {
      const saved = GM_getValue("config", null);
      if (!saved || typeof saved !== "object") {
        console.log("[\u667A\u80FD\u52A9\u624B] \u{1F4CB} \u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E");
        return JSON.parse(JSON.stringify(CONFIG.defaults));
      }
      const merged = { ...CONFIG.defaults, ...saved };
      const numberFields = [
        { key: "minDelay", min: 1, max: 60 },
        { key: "maxDelay", min: 1, max: 60 },
        { key: "runDuration", min: 1, max: 180 },
        { key: "skipProbability", min: 0, max: 100 },
        { key: "maxRetries", min: 1, max: 10 }
      ];
      numberFields.forEach(({ key, min, max }) => {
        const val = merged[key];
        if (typeof val !== "number" || isNaN(val) || !isFinite(val) || // 排除Infinity
        val < min || val > max) {
          console.warn(`[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F \u914D\u7F6E\u9879 ${key} \u65E0\u6548 (${val})\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C (${CONFIG.defaults[key]})`);
          merged[key] = CONFIG.defaults[key];
        }
      });
      const stringFields = [
        "apiKey",
        "customEndpoint",
        "customModel",
        "apiProvider",
        "selectedTemplate",
        "promptLike",
        "promptNeutral",
        "promptDislike"
      ];
      stringFields.forEach((key) => {
        if (typeof merged[key] !== "string") {
          console.warn(`[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F \u914D\u7F6E\u9879 ${key} \u7C7B\u578B\u9519\u8BEF\uFF0C\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C`);
          merged[key] = CONFIG.defaults[key];
        }
      });
      const hasSavedCustomProfile = Object.prototype.hasOwnProperty.call(saved, "customApiProfile");
      merged.customApiProfile = normalizeCustomApiProfile(merged.customApiProfile);
      const customProfileHasData = Boolean(
        merged.customApiProfile.baseUrl || merged.customApiProfile.apiKey || merged.customApiProfile.model || merged.customApiProfile.modelIds.length > 0
      );
      if ((!hasSavedCustomProfile || !customProfileHasData) && saved.apiProvider === "custom") {
        merged.customApiProfile = {
          ...createDefaultCustomApiProfile(),
          baseUrl: typeof saved.customEndpoint === "string" ? saved.customEndpoint : "",
          apiKey: typeof saved.apiKey === "string" ? saved.apiKey : "",
          model: typeof saved.customModel === "string" ? saved.customModel : ""
        };
      }
      const boolFields = ["panelMinimized", "enableComments"];
      boolFields.forEach((key) => {
        if (typeof merged[key] !== "boolean") {
          console.warn(`[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F \u914D\u7F6E\u9879 ${key} \u7C7B\u578B\u9519\u8BEF\uFF0C\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C`);
          merged[key] = CONFIG.defaults[key];
        }
      });
      if (!merged.panelPosition || typeof merged.panelPosition !== "object" || typeof merged.panelPosition.x !== "number" || typeof merged.panelPosition.y !== "number" || isNaN(merged.panelPosition.x) || isNaN(merged.panelPosition.y) || !isFinite(merged.panelPosition.x) || !isFinite(merged.panelPosition.y)) {
        console.warn("[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F panelPosition \u6570\u636E\u65E0\u6548\uFF0C\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C");
        merged.panelPosition = {
          x: CONFIG.defaults.panelPosition.x,
          y: CONFIG.defaults.panelPosition.y
        };
      } else {
        const maxX = window.innerWidth - 60;
        const maxY = window.innerHeight - 60;
        if (merged.panelPosition.x < 0 || merged.panelPosition.x > maxX) {
          console.warn("[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F panelPosition.x \u8D85\u51FA\u8303\u56F4\uFF0C\u81EA\u52A8\u4FEE\u6B63");
          merged.panelPosition.x = Math.max(0, Math.min(maxX, merged.panelPosition.x));
        }
        if (merged.panelPosition.y < 0 || merged.panelPosition.y > maxY) {
          console.warn("[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F panelPosition.y \u8D85\u51FA\u8303\u56F4\uFF0C\u81EA\u52A8\u4FEE\u6B63");
          merged.panelPosition.y = Math.max(0, Math.min(maxY, merged.panelPosition.y));
        }
      }
      if (!Array.isArray(merged.watchBeforeLike) || merged.watchBeforeLike.length !== 2 || typeof merged.watchBeforeLike[0] !== "number" || typeof merged.watchBeforeLike[1] !== "number" || isNaN(merged.watchBeforeLike[0]) || isNaN(merged.watchBeforeLike[1]) || merged.watchBeforeLike[0] < 0 || merged.watchBeforeLike[1] > 30 || merged.watchBeforeLike[0] > merged.watchBeforeLike[1]) {
        console.warn("[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F watchBeforeLike \u6570\u636E\u65E0\u6548\uFF0C\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C");
        merged.watchBeforeLike = [...CONFIG.defaults.watchBeforeLike];
      }
      if (merged.minDelay > merged.maxDelay) {
        console.warn("[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F minDelay > maxDelay\uFF0C\u81EA\u52A8\u4EA4\u6362");
        [merged.minDelay, merged.maxDelay] = [merged.maxDelay, merged.minDelay];
      }
      const validProviders = [...Object.keys(CONFIG.apiProviders), "custom"];
      if (!validProviders.includes(merged.apiProvider)) {
        console.warn(`[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F apiProvider \u65E0\u6548 (${merged.apiProvider})\uFF0C\u91CD\u7F6E\u4E3A deepseek`);
        merged.apiProvider = "deepseek";
      }
      if (merged.apiProvider !== "custom") {
        merged.customEndpoint = CONFIG.getProviderBaseUrl(merged.apiProvider);
      } else if (merged.customApiProfile.baseUrl || merged.customApiProfile.apiKey || merged.customApiProfile.model) {
        merged.customEndpoint = merged.customApiProfile.baseUrl;
        merged.apiKey = merged.customApiProfile.apiKey;
        merged.customModel = merged.customApiProfile.model;
      }
      console.log("[\u667A\u80FD\u52A9\u624B] \u2705 \u914D\u7F6E\u52A0\u8F7D\u5E76\u9A8C\u8BC1\u5B8C\u6210");
      return merged;
    } catch (e) {
      console.error("[\u667A\u80FD\u52A9\u624B] \u274C \u914D\u7F6E\u52A0\u8F7D\u5931\u8D25:", e);
      alert("\u26A0\uFE0F \u914D\u7F6E\u6570\u636E\u635F\u574F\uFF0C\u5DF2\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C\n\n\u5982\u679C\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u6E05\u9664\u6D4F\u89C8\u5668\u6269\u5C55\u6570\u636E\u540E\u91CD\u8BD5");
      try {
        GM_deleteValue("config");
      } catch (delErr) {
        console.error("[\u667A\u80FD\u52A9\u624B] \u65E0\u6CD5\u5220\u9664\u635F\u574F\u7684\u914D\u7F6E:", delErr);
      }
      return JSON.parse(JSON.stringify(CONFIG.defaults));
    }
  };
  var saveConfig = async (config) => {
    try {
      console.log("[\u667A\u80FD\u52A9\u624B] \u{1F4DD} \u51C6\u5907\u4FDD\u5B58\u914D\u7F6E:", {
        \u4F4D\u7F6E: config.panelPosition,
        \u6700\u5C0F\u5316: config.panelMinimized
      });
      if (config.panelPosition) {
        if (isNaN(config.panelPosition.x) || isNaN(config.panelPosition.y)) {
          console.error("[\u667A\u80FD\u52A9\u624B] \u274C \u4F4D\u7F6E\u6570\u636E\u65E0\u6548:", config.panelPosition);
          alert("\u26A0\uFE0F \u68C0\u6D4B\u5230\u65E0\u6548\u7684\u4F4D\u7F6E\u6570\u636E(NaN)\uFF0C\u5DF2\u53D6\u6D88\u4FDD\u5B58");
          return;
        }
      }
      GM_setValue("config", config);
      await new Promise((resolve) => setTimeout(resolve, 100));
      const saved = GM_getValue("config", null);
      if (saved && saved.panelPosition) {
        const match = saved.panelPosition.x === config.panelPosition.x && saved.panelPosition.y === config.panelPosition.y;
        console.log("[\u667A\u80FD\u52A9\u624B] \u2705 \u4FDD\u5B58\u9A8C\u8BC1:", {
          \u5199\u5165\u4F4D\u7F6E: config.panelPosition,
          \u8BFB\u53D6\u4F4D\u7F6E: saved.panelPosition,
          \u5339\u914D\u72B6\u6001: match ? "\u2713 \u6210\u529F" : "\u2717 \u5931\u8D25"
        });
        if (!match) {
          console.error("[\u667A\u80FD\u52A9\u624B] \u274C \u4FDD\u5B58\u9A8C\u8BC1\u5931\u8D25\uFF01\u5199\u5165\u7684\u503C\u548C\u8BFB\u53D6\u7684\u503C\u4E0D\u4E00\u81F4");
        }
      } else {
        console.error("[\u667A\u80FD\u52A9\u624B] \u274C \u4FDD\u5B58\u9A8C\u8BC1\u5931\u8D25\uFF0C\u8BFB\u53D6\u5230\u7A7A\u6570\u636E");
      }
    } catch (e) {
      console.error("[\u667A\u80FD\u52A9\u624B] \u274C GM_setValue \u5931\u8D25:", e);
      alert("\u26A0\uFE0F \u914D\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF01\n" + e.message);
    }
  };

  // src/ui/ui.ts
  var UI = {
    panel: null,
    floatingButton: null,
    create: () => {
      GM_addStyle(`
            /* \u60AC\u6D6E\u6309\u94AE - \u6C34\u6676\u98CE\u683C */
            .smart-feed-float-btn {
                position: fixed;
                width: 60px;
                height: 60px;
                border-radius: 50%;
                background: linear-gradient(135deg, rgba(139, 162, 251, 0.85) 0%, rgba(185, 163, 251, 0.85) 100%);
                backdrop-filter: blur(10px);
                box-shadow: 0 8px 32px rgba(139, 162, 251, 0.3),
                            inset 0 1px 0 rgba(255, 255, 255, 0.4);
                border: 1px solid rgba(255, 255, 255, 0.2);
                cursor: move;
                z-index: 999999;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 24px;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                user-select: none;
            }

            .smart-feed-float-btn:hover {
                transform: scale(1.05);
                box-shadow: 0 12px 40px rgba(139, 162, 251, 0.4),
                            inset 0 1px 0 rgba(255, 255, 255, 0.5);
            }

            .smart-feed-float-btn.running {
                background: linear-gradient(135deg, rgba(99, 230, 190, 0.85) 0%, rgba(56, 178, 172, 0.85) 100%);
                animation: pulse-glow 2s infinite;
            }

            @keyframes pulse-glow {
                0%, 100% {
                    box-shadow: 0 8px 32px rgba(99, 230, 190, 0.3),
                                inset 0 1px 0 rgba(255, 255, 255, 0.4);
                }
                50% {
                    box-shadow: 0 12px 48px rgba(99, 230, 190, 0.6),
                                inset 0 1px 0 rgba(255, 255, 255, 0.5);
                }
            }

            /* \u4E3B\u9762\u677F - \u6BDB\u73BB\u7483\u6548\u679C */
            .smart-feed-panel {
                position: fixed;
                width: 420px;
                max-height: 80vh;
                background: rgba(255, 255, 255, 0.5);
                backdrop-filter: blur(20px) saturate(180%);
                -webkit-backdrop-filter: blur(20px) saturate(180%);
                border-radius: 20px;
                box-shadow: 0 20px 60px rgba(100, 100, 150, 0.15),
                            0 0 0 1px rgba(255, 255, 255, 0.3);
                z-index: 999998;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
                color: #1f2937;
                overflow: hidden;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            }

            /* \u9876\u90E8\u6807\u9898\u680F - \u6C34\u6676\u98CE\u683C + \u96C6\u6210\u5F00\u59CB\u6309\u94AE */
            .smart-feed-header {
                padding: 16px 20px;
                background: linear-gradient(135deg, rgba(139, 162, 251, 0.65) 0%, rgba(185, 163, 251, 0.65) 100%);
                backdrop-filter: blur(10px);
                border-bottom: 1px solid rgba(255, 255, 255, 0.2);
                display: flex;
                justify-content: space-between;
                align-items: center;
                cursor: move;
                user-select: none;
            }

            .smart-feed-title {
                font-size: 16px;
                font-weight: 700;
                color: rgba(255, 255, 255, 0.95);
                display: flex;
                align-items: center;
                gap: 8px;
                text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
            }

            /* \u9876\u90E8\u6309\u94AE\u7EC4 */
            .smart-feed-header-actions {
                display: flex;
                gap: 8px;
                align-items: center;
            }

            /* \u5F00\u59CB\u8FD0\u884C\u6309\u94AE\uFF08\u5728\u9876\u90E8\uFF09 */
            .smart-feed-start-btn {
                padding: 8px 16px;
                border-radius: 10px;
                border: none;
                background: rgba(255, 255, 255, 0.9);
                color: #10b981;
                font-size: 14px;
                font-weight: 600;
                cursor: pointer;
                transition: all 0.2s;
                box-shadow: 0 2px 8px rgba(16, 185, 129, 0.2);
            }

            .smart-feed-start-btn:hover {
                background: rgba(255, 255, 255, 1);
                transform: translateY(-1px);
                box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
            }

            .smart-feed-start-btn.running {
                background: rgba(239, 68, 68, 0.9);
                color: white;
            }

            .smart-feed-start-btn.running:hover {
                background: rgba(239, 68, 68, 1);
            }

            .smart-feed-close {
                width: 32px;
                height: 32px;
                border-radius: 50%;
                background: rgba(255, 255, 255, 0.25);
                backdrop-filter: blur(10px);
                border: 1px solid rgba(255, 255, 255, 0.3);
                color: rgba(255, 255, 255, 0.95);
                font-size: 20px;
                cursor: pointer;
                transition: all 0.2s;
                display: flex;
                align-items: center;
                justify-content: center;
            }

            .smart-feed-close:hover {
                background: rgba(255, 255, 255, 0.35);
                transform: rotate(90deg);
            }

            .smart-feed-body {
                max-height: calc(80vh - 70px);
                overflow-y: auto;
                padding: 20px;
                background: rgba(255, 255, 255, 0.5);
            }

            .smart-feed-body::-webkit-scrollbar {
                width: 6px;
            }

            .smart-feed-body::-webkit-scrollbar-thumb {
                background: rgba(139, 162, 251, 0.3);
                border-radius: 3px;
            }

            .smart-feed-body::-webkit-scrollbar-thumb:hover {
                background: rgba(139, 162, 251, 0.5);
            }

            /* \u6807\u7B7E\u9875 */
            .smart-feed-tabs {
                display: flex;
                gap: 8px;
                margin-bottom: 20px;
                background: rgba(241, 245, 249, 0.6);
                backdrop-filter: blur(10px);
                padding: 4px;
                border-radius: 12px;
            }

            .smart-feed-tab {
                flex: 1;
                padding: 10px;
                border: none;
                background: transparent;
                color: #64748b;
                border-radius: 8px;
                cursor: pointer;
                font-size: 14px;
                font-weight: 500;
                transition: all 0.2s;
            }

            .smart-feed-tab:hover {
                color: #475569;
                background: rgba(255, 255, 255, 0.5);
            }

            .smart-feed-tab.active {
                background: rgba(255, 255, 255, 0.9);
                color: rgba(139, 162, 251, 1);
                box-shadow: 0 2px 8px rgba(139, 162, 251, 0.15);
            }

            /* \u8868\u5355\u5143\u7D20 */
            .smart-feed-section {
                margin-bottom: 20px;
            }

            .smart-feed-label {
                display: flex;
                align-items: center;
                gap: 8px;
                margin-bottom: 10px;
                font-size: 14px;
                font-weight: 600;
                color: #374151;
            }

            .smart-feed-help {
                cursor: help;
                width: 18px;
                height: 18px;
                border-radius: 50%;
                background: rgba(139, 162, 251, 0.2);
                color: rgba(139, 162, 251, 1);
                display: inline-flex;
                align-items: center;
                justify-content: center;
                font-size: 12px;
                font-weight: bold;
                transition: all 0.2s;
            }

            .smart-feed-help:hover {
                background: rgba(139, 162, 251, 0.9);
                color: white;
                transform: scale(1.1);
            }

            .smart-feed-input, .smart-feed-textarea, .smart-feed-select {
                width: 100%;
                padding: 12px;
                border: 2px solid rgba(229, 231, 235, 0.8);
                border-radius: 10px;
                background: rgba(255, 255, 255, 0.8);
                backdrop-filter: blur(10px);
                color: #1f2937;
                font-size: 14px;
                transition: all 0.2s;
                box-sizing: border-box;
            }

            .smart-feed-input:focus, .smart-feed-textarea:focus, .smart-feed-select:focus {
                outline: none;
                border-color: rgba(139, 162, 251, 0.8);
                background: rgba(255, 255, 255, 0.95);
                box-shadow: 0 0 0 3px rgba(139, 162, 251, 0.1);
            }

            .smart-feed-textarea {
                min-height: 80px;
                resize: vertical;
                font-family: inherit;
            }

            .smart-feed-button {
                width: 100%;
                padding: 14px;
                border: none;
                border-radius: 12px;
                font-size: 15px;
                font-weight: 600;
                cursor: pointer;
                transition: all 0.2s;
                margin-top: 10px;
            }

            .smart-feed-action-row {
                display: flex;
                gap: 10px;
                margin-top: 10px;
            }

            .smart-feed-action-row .smart-feed-button {
                flex: 1;
                width: auto;
                margin-top: 0;
            }

            .smart-feed-button-primary {
                background: linear-gradient(135deg, rgba(16, 185, 129, 0.9) 0%, rgba(5, 150, 105, 0.9) 100%);
                color: white;
                box-shadow: 0 4px 12px rgba(16, 185, 129, 0.2);
            }

            .smart-feed-button-primary:hover {
                transform: translateY(-2px);
                box-shadow: 0 6px 20px rgba(16, 185, 129, 0.3);
            }

            .smart-feed-button-stop {
                background: linear-gradient(135deg, rgba(239, 68, 68, 0.9) 0%, rgba(220, 38, 38, 0.9) 100%);
                color: white;
            }

            .smart-feed-button-secondary {
                background: rgba(243, 244, 246, 0.8);
                backdrop-filter: blur(10px);
                color: #374151;
            }

            .smart-feed-button-secondary:hover {
                background: rgba(229, 231, 235, 0.9);
            }

            /* \u65E5\u5FD7 */
            .smart-feed-log {
                background: rgba(248, 250, 252, 0.8);
                backdrop-filter: blur(10px);
                border: 1px solid rgba(226, 232, 240, 0.8);
                border-radius: 10px;
                padding: 15px;
                max-height: 300px;
                overflow-y: auto;
                font-size: 12px;
                font-family: 'Courier New', monospace;
            }

            /* \u{1F195} \u5728\u8FD9\u91CC\u6DFB\u52A0\u4EE5\u4E0B\u65B0\u6837\u5F0F\uFF08\u7EA6\u7B2C352\u884C\uFF09 */

            /* \u53EF\u6298\u53E0\u65E5\u5FD7\u5BB9\u5668 */
            .collapsible-log {
                position: relative;
                display: inline-block;
                width: 100%;
            }

            /* \u9884\u89C8\u6587\u672C\uFF08\u9ED8\u8BA4\u663E\u793A\uFF09 */
            .collapsible-log .log-preview {
                display: inline;
                color: inherit;
            }

            /* \u5B8C\u6574\u6587\u672C\uFF08\u9ED8\u8BA4\u9690\u85CF\uFF09 */
            .collapsible-log .log-full {
                display: none;
                margin-top: 8px;
                padding: 10px;
                background: rgba(241, 245, 249, 0.9);
                border-radius: 6px;
                border: 1px solid rgba(226, 232, 240, 0.6);
                font-size: 11px;
                line-height: 1.6;
                overflow-x: auto;
                white-space: pre-wrap;
                word-break: break-all;
            }

            /* \u5C55\u5F00\u6309\u94AE */
            .collapsible-log .expand-btn {
                margin-left: 8px;
                padding: 2px 8px;
                border: none;
                background: rgba(139, 162, 251, 0.15);
                color: rgba(139, 162, 251, 1);
                border-radius: 4px;
                cursor: pointer;
                font-size: 11px;
                font-weight: 600;
                transition: all 0.2s;
                vertical-align: middle;
            }

            .collapsible-log .expand-btn:hover {
                background: rgba(139, 162, 251, 0.25);
                transform: translateY(-1px);
            }

            /* \u5C55\u5F00\u72B6\u6001 */
            .collapsible-log.expanded .log-preview {
                display: none;
            }

            .collapsible-log.expanded .log-full {
                display: block;
            }

            .collapsible-log.expanded .expand-btn {
                background: rgba(239, 68, 68, 0.15);
                color: rgba(239, 68, 68, 1);
            }

            .collapsible-log.expanded .expand-btn::before {
                content: '\u6536\u8D77 ';
            }

            .collapsible-log:not(.expanded) .expand-btn::before {
                content: '\u5C55\u5F00 ';
            }

            .smart-feed-log-item {
                margin-bottom: 8px;
                padding: 6px 0;
                border-bottom: 1px solid rgba(226, 232, 240, 0.5);
                display: flex;
                gap: 10px;
            }

            .smart-feed-log-time {
                color: #94a3b8;
                flex-shrink: 0;
            }

            .smart-feed-log-text {
                flex: 1;
            }

            /* \u5176\u4ED6 */
            .smart-feed-range-group {
                display: flex;
                gap: 10px;
                align-items: center;
            }

            .smart-feed-range-input {
                flex: 1;
            }

            .smart-feed-checkbox-group {
                display: flex;
                align-items: center;
                gap: 10px;
                padding: 12px;
                background: rgba(248, 250, 252, 0.8);
                backdrop-filter: blur(10px);
                border-radius: 10px;
            }

            .smart-feed-checkbox {
                width: 20px;
                height: 20px;
                cursor: pointer;
            }

            .smart-feed-info-box {
                background: rgba(254, 243, 199, 0.8);
                backdrop-filter: blur(10px);
                border-left: 4px solid rgba(245, 158, 11, 0.8);
                padding: 12px;
                border-radius: 8px;
                font-size: 13px;
                color: #92400e;
                margin-bottom: 15px;
            }

            .smart-feed-link {
                color: rgba(139, 162, 251, 1);
                text-decoration: none;
                font-weight: 600;
            }

            .smart-feed-link:hover {
                text-decoration: underline;
            }

            /* \u7EDF\u8BA1\u5361\u7247 */
            .smart-feed-stats {
                display: grid;
                grid-template-columns: repeat(2, 1fr);
                gap: 10px;
                margin-bottom: 20px;
            }

            .smart-feed-stat-card {
                background: linear-gradient(135deg, rgba(240, 249, 255, 0.8) 0%, rgba(224, 242, 254, 0.8) 100%);
                backdrop-filter: blur(10px);
                padding: 15px;
                border-radius: 12px;
                text-align: center;
                border: 1px solid rgba(186, 230, 253, 0.3);
            }

            .smart-feed-stat-value {
                font-size: 24px;
                font-weight: 700;
                color: #0284c7;
            }

            .smart-feed-stat-label {
                font-size: 12px;
                color: #64748b;
                margin-top: 5px;
            }

            /* \u6027\u80FD\u4F18\u5316\uFF1A\u542F\u7528 GPU \u52A0\u901F */
            .smart-feed-panel,
            .smart-feed-float-btn,
            .smart-feed-button {
                will-change: transform;
                transform: translateZ(0);
            }

            /* \u53EF\u6298\u53E0\u5E2E\u52A9\u6846 */
            .collapsible-help-box .help-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                cursor: pointer;
                user-select: none;
            }

            .collapsible-help-box .help-toggle-btn {
                padding: 4px 12px;
                border: none;
                background: rgba(139, 162, 251, 0.2);
                color: rgba(139, 162, 251, 1);
                border-radius: 6px;
                cursor: pointer;
                font-size: 12px;
                font-weight: 600;
                transition: all 0.2s;
            }

            .collapsible-help-box .help-toggle-btn:hover {
                background: rgba(139, 162, 251, 0.3);
                transform: translateY(-1px);
            }

            .collapsible-help-box .help-content {
                display: none;
                margin-top: 12px;
                padding-top: 12px;
            }

            .collapsible-help-box.expanded .help-content {
                display: block;
            }

            .collapsible-help-box.expanded .help-toggle-btn {
                background: rgba(239, 68, 68, 0.2);
                color: rgba(239, 68, 68, 1);
            }
        `);
      const config = loadConfig();
      console.log("[\u667A\u80FD\u52A9\u624B] \u{1F527} \u521D\u59CB\u5316 - \u914D\u7F6E\u6982\u89C8:", {
        ...config,
        apiKey: config.apiKey ? "[\u5DF2\u9690\u85CF]" : ""
      });
      console.log("[\u667A\u80FD\u52A9\u624B] \u{1F4CD} panelPosition \u539F\u59CB\u503C:", config.panelPosition);
      console.log("[\u667A\u80FD\u52A9\u624B] \u{1F4CD} panelPosition \u7C7B\u578B\u68C0\u67E5:", {
        \u662F\u5BF9\u8C61: typeof config.panelPosition === "object",
        x\u7C7B\u578B: typeof config.panelPosition?.x,
        y\u7C7B\u578B: typeof config.panelPosition?.y,
        x\u503C: config.panelPosition?.x,
        y\u503C: config.panelPosition?.y
      });
      UI.floatingButton = document.createElement("div");
      UI.floatingButton.className = "smart-feed-float-btn";
      UI.floatingButton.innerHTML = "\u{1F916}";
      let savedX, savedY, useDefault = false;
      if (config.panelPosition && typeof config.panelPosition.x === "number" && typeof config.panelPosition.y === "number" && !isNaN(config.panelPosition.x) && !isNaN(config.panelPosition.y)) {
        savedX = config.panelPosition.x;
        savedY = config.panelPosition.y;
        console.log("[\u667A\u80FD\u52A9\u624B] \u2705 \u4F7F\u7528\u4FDD\u5B58\u7684\u4F4D\u7F6E:", savedX, savedY);
      } else {
        savedX = window.innerWidth - 80;
        savedY = 100;
        useDefault = true;
        console.log("[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F \u4F7F\u7528\u9ED8\u8BA4\u4F4D\u7F6E\uFF08\u539F\u56E0: panelPosition\u65E0\u6548\uFF09:", savedX, savedY);
        console.log("[\u667A\u80FD\u52A9\u624B] \u{1F4A1} \u5224\u65AD\u4F9D\u636E:", {
          \u5B58\u5728\u6027: !!config.panelPosition,
          x\u662F\u6570\u5B57: typeof config.panelPosition?.x === "number",
          y\u662F\u6570\u5B57: typeof config.panelPosition?.y === "number",
          x\u975ENaN: !isNaN(config.panelPosition?.x),
          y\u975ENaN: !isNaN(config.panelPosition?.y)
        });
      }
      savedX = Math.max(0, Math.min(window.innerWidth - 60, savedX));
      savedY = Math.max(0, Math.min(window.innerHeight - 60, savedY));
      UI.floatingButton.style.left = savedX + "px";
      UI.floatingButton.style.top = savedY + "px";
      UI.floatingButton.style.transform = "none";
      UI.floatingButton.title = "\u70B9\u51FB\u6253\u5F00\u667A\u80FD\u52A9\u624B";
      console.log("[\u667A\u80FD\u52A9\u624B] \u{1F3AF} \u6309\u94AE\u6700\u7EC8\u4F4D\u7F6E:", {
        left: UI.floatingButton.style.left,
        top: UI.floatingButton.style.top,
        \u4F7F\u7528\u9ED8\u8BA4\u503C: useDefault
      });
      UI.panel = document.createElement("div");
      UI.panel.className = "smart-feed-panel";
      UI.panel.style.display = config.panelMinimized ? "none" : "block";
      const panelLeft = Math.max(10, savedX - 360);
      const panelTop = Math.max(10, savedY);
      UI.panel.style.left = panelLeft + "px";
      UI.panel.style.top = panelTop + "px";
      console.log("[\u667A\u80FD\u52A9\u624B] \u9762\u677F\u521D\u59CB\u4F4D\u7F6E:", panelLeft, panelTop);
      UI.panel.innerHTML = `
            <div class="smart-feed-header">
                <div class="smart-feed-title">
                    \u{1F916} \u667A\u80FD\u52A9\u624B
                </div>
                <div class="smart-feed-header-actions">
                    <button class="smart-feed-start-btn" id="startBtnTop">\u25B6 \u5F00\u59CB</button>
                    <button class="smart-feed-close">\xD7</button>
                </div>
            </div>
            <div class="smart-feed-body">
                <div class="smart-feed-tabs">
                    <button class="smart-feed-tab active" data-tab="basic">\u57FA\u7840\u8BBE\u7F6E</button>
                    <button class="smart-feed-tab" data-tab="advanced">\u9AD8\u7EA7\u9009\u9879</button>
                    <button class="smart-feed-tab" data-tab="log">\u8FD0\u884C\u65E5\u5FD7</button>
                    <button class="smart-feed-tab" data-tab="about">\u5173\u4E8E</button>
                </div>

                <!-- \u57FA\u7840\u8BBE\u7F6E -->
                <div class="smart-feed-tab-content" data-content="basic">
                    <div class="smart-feed-info-box">
                        \u26A0\uFE0F \u672C\u5DE5\u5177\u53EF\u80FD\u56E0\u6296\u97F3\u66F4\u65B0\u800C\u5931\u6548\uFF0C\u9047\u5230\u95EE\u9898\u8BF7\u53CA\u65F6\u53CD\u9988\uFF01
                    </div>

                    <!-- \u{1F195} \u91CD\u8981\u63D0\u793A\u6846\uFF08\u53EF\u6298\u53E0\uFF09 -->
                    <div class="smart-feed-info-box collapsible-help-box" style="margin-top: 10px; background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border-left: 4px solid #f59e0b;">
                        <div class="help-header">
                            <strong>\u{1F3AF} \u65B0\u624B 5 \u5206\u949F\u4E0A\u624B\u6307\u5357</strong>
                            <button class="help-toggle-btn">\u5C55\u5F00 \u25BC</button>
                        </div>
                        <div class="help-content">
                            <!-- \u7B2C\u4E00\u90E8\u5206\uFF1A\u51C6\u5907\u5DE5\u4F5C -->
                            <div style="background: rgba(220, 38, 38, 0.1); border-left: 3px solid #dc2626; padding: 12px; border-radius: 6px; margin-bottom: 15px;">
                                <strong style="color: #dc2626;">\u{1F4CB} \u7B2C\u4E00\u6B21\u4F7F\u7528\u524D\uFF0C\u5148\u505A\u597D 4 \u4EF6\u4E8B</strong><br>
                                <div style="margin-top: 8px; line-height: 1.8;">
                                    1. \u6253\u5F00 <a href="https://www.douyin.com/" target="_blank" style="color: #2563eb;">\u6296\u97F3\u7F51\u9875\u7248</a>\uFF0C\u8FDB\u5165\u5DE6\u4FA7\u83DC\u5355\u7684"<strong>\u63A8\u8350</strong>"\u9875\u9762<br>
                                    2. \u5173\u95ED\u89C6\u9891\u53F3\u4E0B\u89D2"<strong>\u81EA\u52A8\u8FDE\u64AD</strong>"\uFF0C\u8BA9\u811A\u672C\u53EF\u4EE5\u81EA\u5DF1\u5207\u5230\u4E0B\u4E00\u4E2A\u89C6\u9891<br>
                                    3. \u4E0D\u8981\u4F7F\u7528\u65E0\u75D5\u6A21\u5F0F\uFF0C\u5426\u5219 API Key\u3001\u89C4\u5219\u548C\u9762\u677F\u4F4D\u7F6E\u53EF\u80FD\u4FDD\u5B58\u4E0D\u4E86<br>
                                    4. \u51C6\u5907\u4E00\u4E2A API Key\uFF1B\u6CA1\u6709\u7684\u8BDD\u53EF\u4EE5\u70B9\u4E0B\u9762\u94FE\u63A5\u53BB\u521B\u5EFA
                                </div>
                            </div>

                            <div style="background: rgba(37, 99, 235, 0.08); border-left: 3px solid #2563eb; padding: 12px; border-radius: 6px; margin-bottom: 15px;">
                                <strong style="color: #1d4ed8;">\u{1F511} API Key \u53BB\u54EA\u91CC\u62FF\uFF1F</strong><br>
                                <div style="margin-top: 8px; line-height: 1.8;">
                                    <a href="https://platform.deepseek.com/api_keys" target="_blank" style="color: #2563eb;">DeepSeek API Key</a>\uFF1A\u56FD\u5185\u65B0\u624B\u6700\u5BB9\u6613\u4E0A\u624B<br>
                                    <a href="https://platform.moonshot.cn/console/api-keys" target="_blank" style="color: #2563eb;">Kimi API Key</a>\uFF1A\u56FD\u5185\u8BBF\u95EE\u7A33\u5B9A<br>
                                    <a href="https://dashscope.console.aliyun.com/apiKey" target="_blank" style="color: #2563eb;">Qwen / \u901A\u4E49\u5343\u95EE API Key</a>\uFF1A\u963F\u91CC\u4E91\u63A7\u5236\u53F0<br>
                                    <a href="https://open.bigmodel.cn/usercenter/apikeys" target="_blank" style="color: #2563eb;">GLM / \u667A\u8C31 API Key</a>\uFF1AGLM \u6A21\u578B\u63A7\u5236\u53F0<br>
                                    <a href="https://aistudio.google.com/apikey" target="_blank" style="color: #2563eb;">Google Gemini API Key</a>\uFF1AGemini \u6A21\u578B\u63A7\u5236\u53F0<br>
                                    <span style="color: #64748b;">API Key \u50CF\u5BC6\u7801\u4E00\u6837\uFF0C\u53EA\u7C98\u8D34\u5230\u672C\u811A\u672C\u91CC\uFF0C\u4E0D\u8981\u53D1\u7ED9\u522B\u4EBA\u3002</span>
                                </div>
                            </div>
                    
                            <!-- \u7B2C\u4E8C\u90E8\u5206\uFF1A\u914D\u7F6E\u6D41\u7A0B -->
                            <strong>\u2699\uFE0F \u6309\u987A\u5E8F\u5B8C\u6210 API \u914D\u7F6E</strong><br>
                            <div style="background: rgba(255,255,255,0.7); padding: 12px; border-radius: 8px; margin: 10px 0;">
                                <table style="width: 100%; font-size: 13px; line-height: 1.8;">
                                    <tr>
                                        <td style="width: 72px; vertical-align: top; font-weight: bold; color: #7c3aed;">\u6B65\u9AA4 1</td>
                                        <td>
                                            <strong>\u5148\u9009\u6216\u586B\u5199 API Base URL</strong><br>
                                            <span style="color: #64748b;">
                                            \u2022 \u666E\u901A\u7528\u6237\uFF1A\u5728"API Base URL \u9884\u8BBE"\u91CC\u9009 DeepSeek\u3001GLM\u3001Gemini \u7B49<br>
                                            \u2022 \u672C\u5730/\u8F6C\u53D1\u670D\u52A1\uFF1A\u9009"<strong>\u81EA\u5B9A\u4E49 OpenAI \u517C\u5BB9 API</strong>"\uFF0CBase URL \u53EF\u586B <code>http://127.0.0.1:8317</code><br>
                                            \u2022 \u5982\u679C\u4F60\u586B\u7684\u662F <code>https://example.com/v1</code>\uFF0C\u811A\u672C\u4F1A\u81EA\u52A8\u62FC\u51FA chat \u548C models \u63A5\u53E3
                                            </span>
                                        </td>
                                    </tr>
                                    <tr><td colspan="2" style="padding: 8px 0;"></td></tr>
                                    <tr>
                                        <td style="vertical-align: top; font-weight: bold; color: #7c3aed;">\u6B65\u9AA4 2</td>
                                        <td>
                                            <strong>\u7C98\u8D34 API Key</strong><br>
                                            <span style="color: #64748b;">
                                            \u2022 \u628A\u670D\u52A1\u5546\u63A7\u5236\u53F0\u521B\u5EFA\u7684 Key \u7C98\u8D34\u5230"API Key"\u8F93\u5165\u6846<br>
                                            \u2022 Key \u524D\u540E\u4E0D\u8981\u591A\u7A7A\u683C\uFF1B\u5982\u679C\u590D\u5236\u9519\u4E86\uFF0C\u6D4B\u8BD5\u8FDE\u63A5\u4F1A\u5931\u8D25<br>
                                            \u2022 \u672C\u811A\u672C\u53EA\u628A Key \u5B58\u5728\u6D4F\u89C8\u5668\u672C\u5730\uFF0C\u4E0D\u4E0A\u4F20\u5230\u672C\u9879\u76EE\u670D\u52A1\u5668
                                            </span>
                                        </td>
                                    </tr>
                                    <tr><td colspan="2" style="padding: 8px 0;"></td></tr>
                                    <tr>
                                        <td style="vertical-align: top; font-weight: bold; color: #7c3aed;">\u6B65\u9AA4 3</td>
                                        <td>
                                            <strong>\u5FC5\u987B\u5148\u70B9"\u2460 \u83B7\u53D6\u6A21\u578B"</strong><br>
                                            <span style="color: #64748b;">
                                            \u2022 \u811A\u672C\u4F1A\u8BFB\u53D6\u8FD9\u4E2A API \u80FD\u7528\u7684\u6A21\u578B\uFF0C\u5E76\u5237\u65B0"\u6A21\u578B\u9009\u62E9"\u4E0B\u62C9\u6846<br>
                                            \u2022 \u9884\u8BBE API \u4F1A\u81EA\u52A8\u9009\u4E00\u4E2A\u63A8\u8350\u6A21\u578B\uFF1B\u81EA\u5B9A\u4E49 API \u4F1A\u505C\u5728 <code>&lt;\u8BF7\u9009\u62E9\u6A21\u578B&gt;</code>\uFF0C\u8BF7\u624B\u52A8\u9009<br>
                                            \u2022 \u5982\u679C\u770B\u5230\u6A21\u578B\u540E\u9762\u6709"2026.5\uFF1A\u63A8\u8350\uFF0C\u514D\u8D39"\u4E4B\u7C7B\u5907\u6CE8\uFF0C\u4F18\u5148\u9009\u5B83
                                            </span>
                                        </td>
                                    </tr>
                                    <tr><td colspan="2" style="padding: 8px 0;"></td></tr>
                                    <tr>
                                        <td style="vertical-align: top; font-weight: bold; color: #7c3aed;">\u6B65\u9AA4 4</td>
                                        <td>
                                            <strong>\u518D\u70B9"\u2461 \u6D4B\u8BD5\u8FDE\u63A5"</strong><br>
                                            <span style="color: #64748b;">
                                            \u2022 \u770B\u5230\u7EFF\u8272\u6210\u529F\u63D0\u793A\u540E\uFF0C\u8BF4\u660E URL\u3001Key\u3001\u6A21\u578B\u4E09\u4EF6\u4E8B\u90FD\u901A\u4E86<br>
                                            \u2022 \u63A5\u7740\u9009\u62E9"\u9884\u8BBE\u6A21\u677F"\u6216\u586B\u5199\u504F\u597D\u89C4\u5219<br>
                                            \u2022 <strong style="color: #dc2626;">\u6700\u540E\u70B9"\u{1F4BE} \u4FDD\u5B58\u5F53\u524D\u914D\u7F6E"</strong>\uFF0C\u518D\u70B9\u53F3\u4E0A\u89D2"\u25B6 \u5F00\u59CB"
                                            </span>
                                        </td>
                                    </tr>
                                </table>
                            </div>

                            <div style="background: rgba(139, 92, 246, 0.08); border-left: 3px solid #7c3aed; padding: 12px; border-radius: 6px; margin: 15px 0;">
                                <strong style="color: #6d28d9;">\u{1F916} \u6A21\u578B\u9009\u62E9\u5C0F\u6284</strong><br>
                                <div style="margin-top: 8px; line-height: 1.8; color: #64748b;">
                                    \u2022 Gemini \u5F53\u524D\u6309\u6210\u672C\u542F\u53D1\u5F0F\u4F1A\u503E\u5411 <code>gemini-3.1-flash-lite-preview</code> \u8FD9\u7C7B\u65B0\u7248 flash-lite \u6A21\u578B<br>
                                    \u2022 GLM \u5F53\u524D\u6309\u6210\u672C\u542F\u53D1\u5F0F\u4F1A\u503E\u5411 <code>glm-4.7-flash</code> \u8FD9\u7C7B\u65B0\u7248 flash \u6A21\u578B\uFF1B\u5982\u679C\u5B83\u4E0D\u51FA\u73B0\u5728"\u83B7\u53D6\u6A21\u578B"\u7ED3\u679C\u91CC\uFF0C\u4F1A\u624B\u5DE5\u8865\u5230\u5217\u8868\u4E2D<br>
                                    \u2022 \u4E0D\u786E\u5B9A\u9009\u54EA\u4E2A\u65F6\uFF0C\u9009\u5E26"\u63A8\u8350\u3001\u514D\u8D39\u3001\u4F4E\u6210\u672C\u3001flash\u3001lite"\u5907\u6CE8\u7684\u6A21\u578B<br>
                                    \u2022 \u5904\u7406\u6296\u97F3\u63A8\u8350\u6D41\u53EA\u9700\u8981\u5FEB\u901F\u3001\u4FBF\u5B9C\u3001\u7A33\u5B9A\u7684\u804A\u5929\u6A21\u578B\uFF0C\u4E0D\u9700\u8981\u6700\u8D35\u6700\u5F3A\u7684\u6A21\u578B
                                </div>
                            </div>
                    
                            <!-- \u7B2C\u4E09\u90E8\u5206\uFF1A\u5F00\u59CB\u4F7F\u7528 -->
                            <div style="background: rgba(16, 185, 129, 0.1); border-left: 3px solid #10b981; padding: 12px; border-radius: 6px; margin: 15px 0;">
                                <strong style="color: #059669;">\u2705 \u914D\u7F6E\u5B8C\u6210\u540E</strong><br>
                                <div style="margin-top: 8px; line-height: 1.8;">
                                    1\uFE0F\u20E3 \u70B9\u51FB\u9762\u677F\u53F3\u4E0A\u89D2"<strong>\u25B6 \u5F00\u59CB</strong>"\u6309\u94AE<br>
                                    2\uFE0F\u20E3 \u5207\u6362\u5230"<strong>\u8FD0\u884C\u65E5\u5FD7</strong>"\u6807\u7B7E\u9875\uFF0C\u770B\u5B9E\u65F6\u5904\u7406\u8FDB\u5EA6<br>
                                    3\uFE0F\u20E3 <strong style="color: #dc2626;">\u4FDD\u6301\u6296\u97F3\u6807\u7B7E\u9875\u53EF\u89C1</strong><br>
                                    4\uFE0F\u20E3 \u5EFA\u8BAE\u9996\u6B21\u8FD0\u884C 10-15 \u5206\u949F\uFF0C\u89C2\u5BDF\u6548\u679C\u540E\u518D\u8C03\u6574
                                </div>
                            </div>
                    
                            <!-- \u7B2C\u56DB\u90E8\u5206\uFF1A\u5E38\u89C1\u9519\u8BEF -->
                            <details style="margin-top: 15px;">
                                <summary style="cursor: pointer; color: #dc2626; font-weight: bold;">\u274C \u9047\u5230\u95EE\u9898\uFF1F\u70B9\u51FB\u67E5\u770B\u5E38\u89C1\u9519\u8BEF</summary>
                                <div style="margin-top: 10px; padding-left: 15px; font-size: 12px; line-height: 1.8; color: #64748b;">
                                    <strong>Q: \u70B9"\u6D4B\u8BD5\u8FDE\u63A5"\u5931\u8D25\uFF1F</strong><br>
                                    A: \u2460 \u5148\u70B9"\u2460 \u83B7\u53D6\u6A21\u578B" \u2461 \u9009\u4E2D\u4E00\u4E2A\u6A21\u578B \u2462 \u68C0\u67E5 Key \u524D\u540E\u6709\u6CA1\u6709\u591A\u4F59\u7A7A\u683C \u2463 \u786E\u8BA4 API Base URL \u80FD\u8BBF\u95EE<br><br>

                                    <strong>Q: \u70B9"\u2460 \u83B7\u53D6\u6A21\u578B"\u5931\u8D25\uFF1F</strong><br>
                                    A: \u8FD9\u4E2A API \u53EF\u80FD\u4E0D\u652F\u6301 /models\u3002\u53EF\u4EE5\u76F4\u63A5\u624B\u52A8\u586B\u5199\u6A21\u578B\u540D\uFF0C\u518D\u70B9"\u2461 \u6D4B\u8BD5\u8FDE\u63A5"\u9A8C\u8BC1\u3002<br><br>
                    
                                    <strong>Q: \u811A\u672C\u4E00\u76F4\u663E\u793A"\u65E0\u6CD5\u5B9A\u4F4D\u89C6\u9891"\uFF1F</strong><br>
                                    A: \u2460 \u786E\u8BA4\u5728"\u63A8\u8350"\u9875\u9762 \u2461 \u5173\u95ED\u4E86\u81EA\u52A8\u8FDE\u64AD \u2462 \u5237\u65B0\u9875\u9762\u91CD\u8BD5<br><br>
                    
                                    <strong>\u5176\u4ED6\u95EE\u9898\uFF1F</strong><br>
                                    \u53D1\u90AE\u4EF6\u5230 <a href="mailto:[email protected]" style="color: #2563eb;">[email protected]</a>\uFF0C\u8BB0\u5F97\u9644\u4E0A"\u8FD0\u884C\u65E5\u5FD7"\u622A\u56FE
                                </div>
                            </details>
                    
                            <hr style="border: none; border-top: 1px dashed #cbd5e1; margin: 15px 0;">
                    
                            <div style="margin-top: 15px; padding: 10px; background: rgba(139, 92, 246, 0.1); border-radius: 6px; font-size: 12px; text-align: center; color: #7c3aed;">
                                \u{1F4A1} <strong>\u5C0F\u8D34\u58EB</strong>\uFF1A\u987A\u5E8F\u8BB0\u4F4F\u5C31\u884C\uFF1ABase URL \u2192 API Key \u2192 \u2460 \u83B7\u53D6\u6A21\u578B \u2192 \u9009\u62E9\u6A21\u578B \u2192 \u2461 \u6D4B\u8BD5\u8FDE\u63A5 \u2192 \u4FDD\u5B58 \u2192 \u5F00\u59CB
                            </div>
                        </div>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">
                            \u{1F50C} API Base URL \u9884\u8BBE
                            <span class="smart-feed-help" title="\u70B9\u51FB\u201C\u5173\u4E8E\u201D\u6807\u7B7E\u67E5\u770B\u8BE6\u7EC6\u6559\u7A0B">?</span>
                        </div>
                        <select class="smart-feed-select" id="apiProvider">
                            ${Object.entries(CONFIG.apiProviders).map(
        ([key, provider]) => `<option value="${key}">${provider.name}</option>`
      ).join("")}
                            <option value="custom">\u81EA\u5B9A\u4E49 OpenAI \u517C\u5BB9 API</option>
                        </select>
                    </div>

                    <div class="smart-feed-section" id="customEndpointSection">
                        <div class="smart-feed-label">
                            \u{1F310} API Base URL
                            <span class="smart-feed-help" title="\u652F\u6301\u5B98\u65B9\u3001\u8F6C\u53D1\u3001\u672C\u5730 OpenAI \u517C\u5BB9 API">?</span>
                        </div>
                        <input type="text" class="smart-feed-input" id="customEndpoint" placeholder="\u4F8B\u5982 http://127.0.0.1:8317 \u6216 https://api.example.com/v1">
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u{1F511} API Key</div>
                        <input type="text" class="smart-feed-input" id="apiKey" placeholder="\u8F93\u5165\u4F60\u7684 API Key\uFF08\u957F\u4E32\u82F1\u6587\uFF09">
                        <small style="color: #64748b; display: block; margin-top: 5px;">
                            \u{1F4A1} \u5728\u5404\u5E73\u53F0\u7684\u63A7\u5236\u53F0/\u8BBE\u7F6E\u9875\u9762\u521B\u5EFA\u540E\uFF0C\u7C98\u8D34\u5230\u8FD9\u91CC
                        </small>
                    </div>

                    <!-- \u{1F195} \u6A21\u578B\u9009\u62E9\uFF08\u52A8\u6001\u751F\u6210\uFF09 -->
                    <div class="smart-feed-section" id="modelSection">
                        <div class="smart-feed-label">
                            \u{1F916} \u6A21\u578B\u9009\u62E9
                            <span class="smart-feed-help" title="\u4E0D\u540C\u6A21\u578B\u7684\u80FD\u529B\u548C\u4EF7\u683C\u4E0D\u540C">?</span>
                        </div>
                        <select class="smart-feed-select" id="modelSelect">
                            <!-- \u7531 JavaScript \u52A8\u6001\u751F\u6210 -->
                        </select>
                        <small style="color: #94a3b8; display: block; margin-top: 5px; font-size: 12px;">
                            \u2699\uFE0F \u9884\u8BBE\u53EA\u4F1A\u56DE\u586B Base URL \u548C\u9ED8\u8BA4\u6A21\u578B\uFF1B\u8BF7\u6C42\u59CB\u7EC8\u6309 OpenAI \u517C\u5BB9\u683C\u5F0F\u53D1\u9001
                        </small>
                    </div>

                    <div class="smart-feed-action-row">
                        <button class="smart-feed-button smart-feed-button-primary" id="fetchModelsBtn">
                            \u2460 \u70B9\u51FB\u83B7\u53D6\u6A21\u578B
                        </button>
                        <button class="smart-feed-button smart-feed-button-secondary" id="testApiBtn">
                            \u2461 \u70B9\u51FB\u6D4B\u8BD5\u8FDE\u63A5
                        </button>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u9884\u8BBE\u6A21\u677F</div>
                        <select class="smart-feed-select" id="template">
                            <option value="">\u81EA\u5B9A\u4E49\u89C4\u5219</option>
                            ${Object.keys(CONFIG.templates).map((t) => `<option value="${t}">${t}</option>`).join("")}
                        </select>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u70B9\u8D5E\u6536\u85CF\u89C4\u5219</div>
                        <textarea class="smart-feed-textarea" id="promptLike" placeholder="\u63CF\u8FF0\u4F60\u5E0C\u671B\u770B\u5230\u4EC0\u4E48\u5185\u5BB9...">${config.promptLike}</textarea>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u5FFD\u7565\u8DEF\u8FC7\u89C4\u5219</div>
                        <textarea class="smart-feed-textarea" id="promptNeutral" placeholder="\u63CF\u8FF0\u666E\u901A\u5185\u5BB9\u7684\u6807\u51C6...">${config.promptNeutral}</textarea>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u4E0D\u611F\u5174\u8DA3\u89C4\u5219</div>
                        <textarea class="smart-feed-textarea" id="promptDislike" placeholder="\u63CF\u8FF0\u4F60\u60F3\u8FC7\u6EE4\u4EC0\u4E48\u5185\u5BB9...">${config.promptDislike}</textarea>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u64CD\u4F5C\u95F4\u9694\uFF08\u79D2\uFF09</div>
                        <div class="smart-feed-range-group">
                            <input type="number" class="smart-feed-input smart-feed-range-input" id="minDelay" value="${config.minDelay}" min="1" max="60">
                            <span>\u5230</span>
                            <input type="number" class="smart-feed-input smart-feed-range-input" id="maxDelay" value="${config.maxDelay}" min="1" max="60">
                        </div>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u8FD0\u884C\u65F6\u957F\uFF08\u5206\u949F\uFF09</div>
                        <input type="number" class="smart-feed-input" id="runDuration" value="${config.runDuration}" min="1" max="180">
                    </div>

                </div>

                <!-- \u9AD8\u7EA7\u9009\u9879 -->
                <div class="smart-feed-tab-content" data-content="advanced" style="display: none;">
                    <div class="smart-feed-info-box">
                        \u2139\uFE0F \u8FD9\u4E9B\u8BBE\u7F6E\u5F71\u54CD\u5DE5\u5177\u7684\u884C\u4E3A\u6A21\u5F0F\uFF0C\u5EFA\u8BAE\u4FDD\u6301\u9ED8\u8BA4\u503C
                    </div>


                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u64CD\u4F5C\u524D\u89C2\u770B\u65F6\u957F\uFF08\u79D2\uFF09</div>
                        <div class="smart-feed-range-group">
                            <input type="number" class="smart-feed-input smart-feed-range-input" id="watchMin" value="${config.watchBeforeLike[0]}" min="0" max="30">
                            <span>\u5230</span>
                            <input type="number" class="smart-feed-input smart-feed-range-input" id="watchMax" value="${config.watchBeforeLike[1]}" min="0" max="30">
                        </div>
                        <small style="color: #64748b;">\u6A21\u62DF\u771F\u4EBA\u89C2\u770B\u4E00\u6BB5\u65F6\u95F4\u540E\u518D\u64CD\u4F5C</small>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">\u5185\u5BB9\u8DF3\u8FC7\u6982\u7387\uFF08%\uFF09</div>
                        <input type="number" class="smart-feed-input" id="skipProbability" value="${config.skipProbability}" min="0" max="50">
                        <small style="color: #64748b;">\u968F\u673A\u8DF3\u8FC7\u90E8\u5206\u89C6\u9891\uFF0C\u907F\u514D\u6BCF\u4E2A\u90FD\u64CD\u4F5C</small>
                    </div>

                    <div class="smart-feed-section">
                        <div class="smart-feed-label">API\u5931\u8D25\u91CD\u8BD5\u6B21\u6570</div>
                        <input type="number" class="smart-feed-input" id="maxRetries" value="${config.maxRetries}" min="1" max="10">
                    </div>
                </div>

                <!-- \u8FD0\u884C\u65E5\u5FD7 -->
                <div class="smart-feed-tab-content" data-content="log" style="display: none;">
                    <div class="smart-feed-stats" id="statsContainer">
                        <div class="smart-feed-stat-card">
                            <div class="smart-feed-stat-value" id="statTotal">0</div>
                            <div class="smart-feed-stat-label">\u5DF2\u5904\u7406</div>
                        </div>
                        <div class="smart-feed-stat-card">
                            <div class="smart-feed-stat-value" id="statLiked">0</div>
                            <div class="smart-feed-stat-label">\u70B9\u8D5E</div>
                        </div>
                        <div class="smart-feed-stat-card">
                            <div class="smart-feed-stat-value" id="statNeutral">0</div>
                            <div class="smart-feed-stat-label">\u5FFD\u7565</div>
                        </div>
                        <div class="smart-feed-stat-card">
                            <div class="smart-feed-stat-value" id="statDisliked">0</div>
                            <div class="smart-feed-stat-label">\u4E0D\u611F\u5174\u8DA3</div>
                        </div>
                    </div>

                    <!-- \u{1F195} \u65B0\u589E\uFF1A\u65E5\u5FD7\u63A7\u5236\u680F -->
                    <div style="display: flex; gap: 10px; margin-bottom: 10px; align-items: center; justify-content: space-between;">
                        <label style="display: flex; align-items: center; gap: 6px; font-size: 13px; color: #64748b; cursor: pointer; user-select: none;">
                            <input type="checkbox" id="verboseLog" style="width: 16px; height: 16px; cursor: pointer;">
                            <span>\u663E\u793A\u8BE6\u7EC6\u8C03\u8BD5\u4FE1\u606F</span>
                        </label>
                        <button class="smart-feed-button smart-feed-button-secondary" id="clearLog"
                                style="margin: 0; padding: 8px 16px; width: auto; font-size: 13px;">
                            \u{1F5D1}\uFE0F \u6E05\u7A7A\u65E5\u5FD7
                        </button>
                    </div>

                    <div class="smart-feed-log" id="logContainer">
                        <div class="smart-feed-log-item">
                            <span class="smart-feed-log-time">${(/* @__PURE__ */ new Date()).toLocaleTimeString()}</span>
                            <span class="smart-feed-log-text">\u7B49\u5F85\u5F00\u59CB\u8FD0\u884C...</span>
                        </div>
                    </div>
                </div>

                <!-- \u5173\u4E8E -->
                <div class="smart-feed-tab-content" data-content="about" style="display: none;">
                    <div class="smart-feed-section">
                        <h3 style="margin: 0 0 15px 0; color: #1f2937;">\u{1F4D6} \u4F7F\u7528\u8BF4\u660E</h3>
                        <div style="background: #f8fafc; padding: 15px; border-radius: 10px; font-size: 13px; line-height: 1.8; color: #475569;">
                            <p><strong>\u{1F680} \u96F6\u57FA\u7840\u542F\u52A8\u987A\u5E8F</strong></p>
                            <p>1. \u6253\u5F00 <a href="https://www.douyin.com/" target="_blank" class="smart-feed-link">\u6296\u97F3\u7F51\u9875\u7248</a>\uFF0C\u8FDB\u5165"\u63A8\u8350"\u9875\u9762\u5E76\u5173\u95ED\u81EA\u52A8\u8FDE\u64AD\u3002</p>
                            <p>2. \u5728"\u57FA\u7840\u8BBE\u7F6E"\u91CC\u9009\u62E9 API Base URL \u9884\u8BBE\uFF1B\u5982\u679C\u4F60\u7528\u672C\u5730\u4EE3\u7406\u6216\u7B2C\u4E09\u65B9\u8F6C\u53D1\uFF0C\u9009\u62E9"\u81EA\u5B9A\u4E49 OpenAI \u517C\u5BB9 API"\u3002</p>
                            <p>3. \u586B\u5199 API Base URL\uFF0C\u518D\u7C98\u8D34 API Key\u3002</p>
                            <p>4. \u5148\u70B9 <strong>\u2460 \u70B9\u51FB\u83B7\u53D6\u6A21\u578B</strong>\uFF0C\u7B49\u6A21\u578B\u5217\u8868\u5237\u65B0\u540E\u9009\u62E9\u6A21\u578B\u3002</p>
                            <p>5. \u518D\u70B9 <strong>\u2461 \u70B9\u51FB\u6D4B\u8BD5\u8FDE\u63A5</strong>\u3002\u6210\u529F\u540E\u9009\u62E9\u9884\u8BBE\u6A21\u677F\u6216\u586B\u5199\u504F\u597D\u89C4\u5219\uFF0C\u4FDD\u5B58\u914D\u7F6E\uFF0C\u6700\u540E\u70B9\u53F3\u4E0A\u89D2"\u25B6 \u5F00\u59CB"\u3002</p>

                            <hr style="border: none; border-top: 1px solid #e2e8f0; margin: 15px 0;">

                            <p><strong>\u{1F511} \u5982\u4F55\u83B7\u53D6 API Key</strong></p>
                            <p>\u2022 <a href="https://platform.deepseek.com/api_keys" target="_blank" class="smart-feed-link">DeepSeek \u5B98\u7F51</a> - \u65B0\u624B\u5BB9\u6613\u4E0A\u624B\uFF0C\u4EF7\u683C\u4F4E</p>
                            <p>\u2022 <a href="https://platform.moonshot.cn/console/api-keys" target="_blank" class="smart-feed-link">Kimi \u5B98\u7F51</a> - \u56FD\u5185\u670D\u52A1\uFF0C\u6709\u514D\u8D39\u989D\u5EA6</p>
                            <p>\u2022 <a href="https://dashscope.console.aliyun.com/apiKey" target="_blank" class="smart-feed-link">Qwen \u5B98\u7F51</a> - \u963F\u91CC\u4E91\u901A\u4E49\u5343\u95EE</p>
                            <p>\u2022 <a href="https://open.bigmodel.cn/usercenter/apikeys" target="_blank" class="smart-feed-link">GLM \u5B98\u7F51</a> - \u667A\u8C31 AI</p>
                            <p>\u2022 <a href="https://aistudio.google.com/apikey" target="_blank" class="smart-feed-link">Google AI Studio</a> - Gemini API Key</p>
                            <p>\u2022 \u7B2C\u4E09\u65B9\u8F6C\u53D1\u6216\u672C\u5730\u670D\u52A1\uFF1A\u9009\u62E9"\u81EA\u5B9A\u4E49 OpenAI \u517C\u5BB9 API"\uFF0C\u4F8B\u5982 <code>http://127.0.0.1:8317</code></p>

                            <hr style="border: none; border-top: 1px solid #e2e8f0; margin: 15px 0;">

                            <p><strong>\u{1F916} \u6A21\u578B\u600E\u4E48\u9009</strong></p>
                            <p>\u2022 \u5148\u70B9 <strong>\u2460 \u70B9\u51FB\u83B7\u53D6\u6A21\u578B</strong>\uFF0C\u811A\u672C\u4F1A\u8C03\u7528 OpenAI \u517C\u5BB9\u7684 <code>/models</code> \u63A5\u53E3\u8BFB\u53D6\u53EF\u7528\u6A21\u578B\u3002</p>
                            <p>\u2022 \u9884\u8BBE API \u4F1A\u81EA\u52A8\u9009\u63A8\u8350\u6A21\u578B\uFF1B\u81EA\u5B9A\u4E49 API \u4E0D\u4F1A\u81EA\u52A8\u9009\uFF0C\u4F1A\u663E\u793A <code>&lt;\u8BF7\u9009\u62E9\u6A21\u578B&gt;</code>\uFF0C\u9700\u8981\u4F60\u624B\u52A8\u9009\u62E9\u3002</p>
                            <p>\u2022 \u5982\u679C\u6A21\u578B\u540E\u9762\u6709\u5907\u6CE8\uFF0C\u4F8B\u5982 <code>gemini-3.1-flash-lite-preview\uFF082026.5\uFF1A\u63A8\u8350\uFF0C\u514D\u8D39/\u4F4E\u6210\u672C\uFF09</code>\uFF0C\u8BF4\u660E\u8FD9\u662F\u4EBA\u5DE5\u7EF4\u62A4\u7684\u5C55\u793A\u8BF4\u660E\uFF1B\u9ED8\u8BA4\u9009\u62E9\u4ECD\u6309\u6210\u672C\u542F\u53D1\u5F0F\u6392\u5E8F\u3002</p>
                            <p>\u2022 \u6709\u4E9B\u6A21\u578B\u80FD\u6B63\u5E38\u8C03\u7528\uFF0C\u4F46\u670D\u52A1\u5546\u7684 <code>/models</code> \u4E0D\u8FD4\u56DE\uFF1B\u672C\u9879\u76EE\u4F1A\u5728\u914D\u7F6E\u91CC\u624B\u5DE5\u8865\u5145\uFF0C\u4F8B\u5982 <code>glm-4.7-flash</code>\u3002</p>
                            <p>\u2022 \u672C\u5DE5\u5177\u53EA\u505A\u77ED\u6587\u672C\u5224\u65AD\uFF0C\u4F18\u5148\u9009\u62E9\u4FBF\u5B9C\u3001\u5FEB\u901F\u3001\u7A33\u5B9A\u7684 chat \u6A21\u578B\uFF0C\u4E0D\u9700\u8981\u56FE\u50CF\u3001\u97F3\u9891\u3001embedding\u3001rerank \u7C7B\u6A21\u578B\u3002</p>

                            <hr style="border: none; border-top: 1px solid #e2e8f0; margin: 15px 0;">

                            <p><strong>\u26A0\uFE0F \u540E\u53F0\u6302\u673A\u8BF4\u660E</strong></p>
                            <p>\u2022 \u672C\u811A\u672C<strong>\u9700\u8981\u4FDD\u6301\u6296\u97F3\u6807\u7B7E\u9875\u53EF\u89C1</strong>\uFF0C\u4E0D\u8981\u5207\u6362\u5230\u5176\u4ED6\u6D4F\u89C8\u5668\u6807\u7B7E\u9875\u3002</p>
                            <p>\u2022 \u53EF\u4EE5\u628A\u6D4F\u89C8\u5668\u7A97\u53E3\u653E\u5230\u4E00\u8FB9\uFF0C\u4F46\u6296\u97F3\u9875\u9762\u8981\u4FDD\u6301\u5728\u5F53\u524D\u6FC0\u6D3B\u6807\u7B7E\u3002</p>
                            <p>\u2022 \u539F\u56E0\uFF1A\u5FEB\u6377\u952E\u64CD\u4F5C\u3001\u89C6\u9891\u5207\u6362\u548C DOM \u76D1\u542C\u90FD\u4F9D\u8D56\u9875\u9762\u5904\u4E8E\u6D3B\u8DC3\u72B6\u6001\u3002</p>
                            <p>\u2022 \u5EFA\u8BAE\u4F7F\u7528\u72EC\u7ACB\u6D4F\u89C8\u5668\u7A97\u53E3\u8FD0\u884C\uFF0C\u9996\u6B21\u8FD0\u884C 10-15 \u5206\u949F\uFF0C\u89C2\u5BDF\u63A8\u8350\u6D41\u53D8\u5316\u540E\u518D\u8C03\u6574\u89C4\u5219\u3002</p>

                            <hr style="border: none; border-top: 1px solid #e2e8f0; margin: 15px 0;">

                            <p><strong>\u2753 \u5E38\u89C1\u95EE\u9898</strong></p>
                            <p><strong>Q: \u4EF7\u683C\u5927\u6982\u591A\u5C11\uFF1F</strong></p>
                            <p>A: \u53D6\u51B3\u4E8E API \u4F9B\u5E94\u5546\u548C\u6A21\u578B\u3002\u90E8\u5206\u5E73\u53F0\u6709\u65B0\u4EBA\u989D\u5EA6\u3001\u514D\u8D39\u6A21\u578B\u6216\u4F4E\u6210\u672C flash/lite \u6A21\u578B\u3002\u5904\u7406\u63A8\u8350\u6D41\u901A\u5E38\u7528\u4FBF\u5B9C\u6A21\u578B\u5C31\u591F\u4E86\u3002</p>

                            <p><strong>Q: \u53EF\u4EE5\u4F7F\u7528 deepseek \u6DF1\u5EA6\u601D\u8003\u3001R1 \u8FD9\u7C7B\u6A21\u578B\u5417\uFF1F</strong></p>
                            <p>A: \u53EF\u4EE5\u5C1D\u8BD5\u3002\u811A\u672C\u4F1A\u8981\u6C42\u6A21\u578B\u5C11\u8F93\u51FA\u601D\u8003\uFF0C\u5E76\u4F18\u5148\u8BFB\u53D6\u6700\u7EC8\u56DE\u7B54\u3002\u5982\u679C\u63A5\u53E3\u53EA\u8FD4\u56DE\u601D\u8003\u5185\u5BB9\u800C\u6CA1\u6709\u6700\u7EC8\u56DE\u7B54\uFF0C\u811A\u672C\u4F1A\u63D0\u793A"\u6A21\u578B\u672A\u8FD4\u56DE\u6700\u7EC8\u56DE\u7B54"\u3002\u65E5\u5E38\u4F7F\u7528\u4ECD\u5EFA\u8BAE\u4F18\u5148\u9009\u666E\u901A chat/flash/lite \u6A21\u578B\u3002</p>

                            <p><strong>Q: \u70B9"\u2460 \u70B9\u51FB\u83B7\u53D6\u6A21\u578B"\u5931\u8D25\u600E\u4E48\u529E\uFF1F</strong></p>
                            <p>A: \u68C0\u67E5 Base URL \u548C Key\uFF1B\u5982\u679C\u4F60\u7684 API \u4E0D\u652F\u6301 <code>/models</code>\uFF0C\u53EF\u4EE5\u624B\u52A8\u586B\u5199\u6A21\u578B\u540D\uFF0C\u7136\u540E\u76F4\u63A5\u70B9"\u2461 \u70B9\u51FB\u6D4B\u8BD5\u8FDE\u63A5"\u3002</p>

                            <p><strong>Q: \u51FA\u73B0 400 / 401 / 422 \u9519\u8BEF\u600E\u4E48\u529E\uFF1F</strong></p>
                            <p>A: 400/422 \u591A\u534A\u662F Base URL\u3001\u6A21\u578B\u540D\u6216\u8BF7\u6C42\u683C\u5F0F\u4E0D\u5339\u914D\uFF1B401 \u591A\u534A\u662F Key \u9519\u4E86\u3001\u8FC7\u671F\u4E86\u6216\u6CA1\u6743\u9650\u3002\u6309\u987A\u5E8F\u68C0\u67E5\uFF1ABase URL \u2192 API Key \u2192 \u83B7\u53D6\u6A21\u578B \u2192 \u9009\u62E9\u6A21\u578B \u2192 \u6D4B\u8BD5\u8FDE\u63A5\u3002</p>

                            <hr style="border: none; border-top: 1px solid #e2e8f0; margin: 15px 0;">

                            <p><strong>\u{1F527} \u5F00\u53D1\u8005\u7EF4\u62A4\u8BF4\u660E</strong></p>
                            <p>\u2022 <strong>\u7EDF\u4E00\u914D\u7F6E\u4F4D\u7F6E</strong>\uFF1A\u6240\u6709 Base URL \u9884\u8BBE\u96C6\u4E2D\u5728 <code>CONFIG.apiProviders</code></p>
                            <p>\u2022 <strong>\u65B0\u589E\u9884\u8BBE</strong>\uFF1A\u5728 <code>apiProviders</code> \u4E2D\u6DFB\u52A0\u4E00\u4E2A\u5BF9\u8C61\uFF0C\u5305\u542B name\u3001baseUrl\u3001defaultModel\u3001models</p>
                            <p>\u2022 <strong>\u65B0\u589E\u6A21\u578B</strong>\uFF1A\u5728\u5BF9\u5E94\u5382\u5546\u7684 <code>models</code> \u6570\u7EC4\u4E2D\u6DFB\u52A0 <code>{ value: 'model-id', label: '\u663E\u793A\u540D\u79F0' }</code></p>
                            <p>\u2022 <strong>\u4EBA\u5DE5\u8865\u5145</strong>\uFF1A\u5728 <code>modelSelectionOverrides</code> \u91CC\u8865\u5145 /models \u4E0D\u8FD4\u56DE\u4F46\u53EF\u8C03\u7528\u7684\u5019\u9009\u6A21\u578B\uFF0C\u5728 <code>modelLabelNotes</code> \u91CC\u7EF4\u62A4\u5C55\u793A\u5907\u6CE8\uFF1B\u9ED8\u8BA4\u9009\u62E9\u4ECD\u7531\u542F\u53D1\u5F0F\u51B3\u5B9A</p>
                            <p>\u2022 <strong>\u8BF7\u6C42\u53C2\u6570\u7B56\u7565</strong>\uFF1A\u9ED8\u8BA4\u53EA\u53D1 OpenAI \u517C\u5BB9\u7684\u901A\u7528\u5B57\u6BB5\uFF1B\u5382\u5546\u4E13\u5C5E thinking \u53C2\u6570\u4E0D\u8981\u4F5C\u4E3A\u5E38\u89C4\u9002\u914D\u624B\u6BB5</p>
                            <p>\u2022 <strong>\u65E0\u9700\u5206\u6563\u4FEE\u6539</strong>\uFF1A\u6A21\u578B\u548C Base URL \u5168\u90E8\u5728\u4E00\u4E2A\u914D\u7F6E\u5BF9\u8C61\u4E2D</p>

                            <p><strong>\u{1F4A1} \u4F7F\u7528\u6280\u5DE7\uFF1A</strong></p>
                            <p>\u2022 \u9996\u6B21\u4F7F\u7528\u5EFA\u8BAE\u5148\u83B7\u53D6\u6A21\u578B\uFF0C\u518D\u6D4B\u8BD5\u8FDE\u63A5\uFF0C\u786E\u4FDD API \u53EF\u7528</p>
                            <p>\u2022 \u8FD0\u884C\u65F6\u957F\u8BBE\u7F6E10-20\u5206\u949F\u5373\u53EF\uFF0C\u907F\u514D\u957F\u65F6\u95F4\u6302\u673A</p>
                        </div>
                    </div>

                    <div class="smart-feed-section">
                        <h3 style="margin: 0 0 15px 0; color: #1f2937;">\u{1F41B} \u53CD\u9988\u4E0E\u652F\u6301</h3>
                        <div style="background: #fef3c7; padding: 15px; border-radius: 10px; font-size: 13px; line-height: 1.8; color: #92400e;">
                            <p><strong>\u672C\u5DE5\u5177\u53EF\u80FD\u56E0\u6296\u97F3\u66F4\u65B0\u800C\u5931\u6548\uFF01</strong></p>
                            <p>\u9047\u5230\u95EE\u9898\u8BF7\u53CA\u65F6\u53CD\u9988\uFF0C\u5E2E\u52A9\u6211\u4EEC\u6539\u8FDB\uFF1A</p>
                            <p>\u2022 \u{1F4E7} \u90AE\u4EF6\u53CD\u9988\uFF1A<a href="mailto:[email protected]" class="smart-feed-link">[email protected]</a></p>
                            <p>\u2022 \u{1F31F} GitHub\u9879\u76EE\uFF1A<a href="https://github.com/baianjo/Douyin-Smart-Feed-Assistant" target="_blank" class="smart-feed-link">\u70B9\u51FB\u8BBF\u95EE</a></p>
                            <p>\u2022 \u5982\u679C\u89C9\u5F97\u6709\u7528\uFF0C\u8BF7\u7ED9\u9879\u76EE\u70B9\u4E2A\u2B50Star\u652F\u6301\u4E00\u4E0B\uFF01</p>
                            <p style="margin-top: 10px; font-size: 12px; color: #78716c;">\u53CD\u9988\u65F6\u8BF7\u9644\u4E0A\u9519\u8BEF\u622A\u56FE\u548C\u65E5\u5FD7\uFF0C\u65B9\u4FBF\u5FEB\u901F\u5B9A\u4F4D\u95EE\u9898</p>
                        </div>
                    </div>

                    <div class="smart-feed-section">
                        <h3 style="margin: 0 0 15px 0; color: #1f2937;">\u2696\uFE0F \u514D\u8D23\u58F0\u660E</h3>
                        <div style="background: #fee2e2; padding: 15px; border-radius: 10px; font-size: 12px; line-height: 1.8; color: #991b1b;">
                            <p>\u2022 \u672C\u5DE5\u5177\u4EC5\u4F9B\u5B66\u4E60\u548C\u4E2A\u4EBA\u7814\u7A76\u4F7F\u7528</p>
                            <p>\u2022 \u4F7F\u7528\u672C\u5DE5\u5177\u53EF\u80FD\u8FDD\u53CD\u6296\u97F3\u670D\u52A1\u6761\u6B3E</p>
                            <p>\u2022 \u56E0\u4F7F\u7528\u672C\u5DE5\u5177\u5BFC\u81F4\u7684\u8D26\u53F7\u95EE\u9898\uFF0C\u4F5C\u8005\u4E0D\u627F\u62C5\u4EFB\u4F55\u8D23\u4EFB</p>
                            <p>\u2022 \u8BF7\u9075\u5B88\u76F8\u5173\u6CD5\u5F8B\u6CD5\u89C4\uFF0C\u7406\u6027\u4F7F\u7528AI\u6280\u672F</p>
                            <p>\u2022 API Key\u4EC5\u5B58\u50A8\u5728\u672C\u5730\u6D4F\u89C8\u5668\uFF0C\u4E0D\u4F1A\u4E0A\u4F20\u5230\u4EFB\u4F55\u670D\u52A1\u5668</p>
                        </div>
                    </div>
                </div>
            </div>
        `;
      document.body.appendChild(UI.floatingButton);
      document.body.appendChild(UI.panel);
      UI.bindEvents();
    },
    bindEvents: () => {
      let isDraggingBtn = false;
      let isDraggingPanel = false;
      let btnStartX = 0, btnStartY = 0, btnStartLeft = 0, btnStartTop = 0;
      let panelStartX = 0, panelStartY = 0, panelStartLeft = 0, panelStartTop = 0;
      let wasDragging = false;
      const config = loadConfig();
      function showSaveNotice() {
        const notice = document.createElement("div");
        notice.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                background: #10b981;
                color: white;
                padding: 12px 20px;
                border-radius: 8px;
                font-size: 14px;
                z-index: 9999999;
                box-shadow: 0 4px 12px rgba(16,185,129,0.3);
                animation: slideIn 0.3s ease;
            `;
        notice.textContent = "\u2713 \u914D\u7F6E\u5DF2\u4FDD\u5B58";
        document.body.appendChild(notice);
        setTimeout(() => {
          notice.style.animation = "slideOut 0.3s ease";
          setTimeout(() => notice.remove(), 300);
        }, 2e3);
      }
      function getModelControlValue() {
        const modelEl = document.getElementById("modelSelect");
        return modelEl?.value?.trim() || "";
      }
      function getCurrentModelIds(fallbackIds = []) {
        const modelEl = document.getElementById("modelSelect");
        if (modelEl?.tagName === "SELECT") {
          return Array.from(modelEl.options).map((option) => option.value).filter(Boolean);
        }
        return fallbackIds;
      }
      function updateCustomApiProfileFromForm(cfg, modelIds = null, fetchedAt = null) {
        const currentProfile = cfg.customApiProfile || CONFIG.defaults.customApiProfile;
        const customEndpointEl = document.getElementById("customEndpoint");
        const apiKeyEl = document.getElementById("apiKey");
        cfg.customApiProfile = {
          baseUrl: customEndpointEl?.value?.trim() || cfg.customEndpoint || currentProfile.baseUrl || "",
          apiKey: apiKeyEl?.value?.trim() || cfg.apiKey || currentProfile.apiKey || "",
          model: getModelControlValue() || cfg.customModel || currentProfile.model || "",
          modelIds: Array.isArray(modelIds) ? modelIds : getCurrentModelIds(currentProfile.modelIds || []),
          fetchedAt: fetchedAt || currentProfile.fetchedAt || ""
        };
        cfg.customEndpoint = cfg.customApiProfile.baseUrl;
        cfg.apiKey = cfg.customApiProfile.apiKey;
        cfg.customModel = cfg.customApiProfile.model;
      }
      function readApiFormConfig() {
        const selectedProvider = document.getElementById("apiProvider").value;
        const apiBaseUrl = document.getElementById("customEndpoint").value.trim();
        const effectiveProvider = selectedProvider !== "custom" && apiBaseUrl !== CONFIG.getProviderBaseUrl(selectedProvider) ? "custom" : selectedProvider;
        return {
          apiKey: document.getElementById("apiKey").value.trim(),
          apiProvider: effectiveProvider,
          customEndpoint: apiBaseUrl,
          customModel: getModelControlValue()
        };
      }
      function syncProviderPresetFromBaseUrl(cfg) {
        const apiProviderEl = document.getElementById("apiProvider");
        const customEndpointEl = document.getElementById("customEndpoint");
        if (!apiProviderEl || !customEndpointEl) {
          return;
        }
        const selectedProvider = apiProviderEl.value;
        const apiBaseUrl = customEndpointEl.value.trim();
        cfg.customEndpoint = apiBaseUrl;
        if (selectedProvider !== "custom" && apiBaseUrl !== CONFIG.getProviderBaseUrl(selectedProvider)) {
          cfg.apiProvider = "custom";
          cfg.customEndpoint = apiBaseUrl;
          cfg.apiKey = document.getElementById("apiKey")?.value?.trim() || cfg.apiKey;
          cfg.customModel = getModelControlValue();
          updateCustomApiProfileFromForm(cfg);
          apiProviderEl.value = "custom";
          updateModelOptions("custom");
          return;
        }
        cfg.apiProvider = selectedProvider;
        if (selectedProvider === "custom") {
          updateCustomApiProfileFromForm(cfg);
        }
      }
      function updateModelOptions(provider, modelIdsOverride = null, selectedModelOverride = null) {
        const modelSelect = document.getElementById("modelSelect");
        const modelSection = document.getElementById("modelSection");
        if (!modelSelect || !modelSection) {
          console.warn("[\u667A\u80FD\u52A9\u624B] \u26A0\uFE0F \u6A21\u578B\u9009\u62E9\u5143\u7D20\u672A\u627E\u5230\uFF0C\u8DF3\u8FC7\u521D\u59CB\u5316");
          return;
        }
        const providerConfig = CONFIG.apiProviders[provider];
        const savedConfig = loadConfig();
        const profile = savedConfig.customApiProfile || CONFIG.defaults.customApiProfile;
        const overrideIds = Array.isArray(modelIdsOverride) ? modelIdsOverride : null;
        const isCustom = provider === "custom";
        const customModelIds = overrideIds || profile.modelIds || [];
        if (isCustom && customModelIds.length === 0) {
          modelSelect.outerHTML = '<input type="text" class="smart-feed-input" id="modelSelect" placeholder="\u8F93\u5165\u6A21\u578B\u540D\u79F0\uFF08\u5982 gpt-4o-mini\uFF09">';
          const modelInput = document.getElementById("modelSelect");
          if (modelInput) {
            modelInput.value = selectedModelOverride ?? profile.model ?? savedConfig.customModel ?? "";
            modelInput.addEventListener("blur", async (e) => {
              const cfg = loadConfig();
              cfg.customModel = e.target.value.trim();
              updateCustomApiProfileFromForm(cfg);
              await saveConfig(cfg);
              showSaveNotice();
            });
          }
          const smallEl = modelSection.querySelector("small");
          if (smallEl) smallEl.style.display = "none";
        } else {
          modelSelect.outerHTML = '<select class="smart-feed-select" id="modelSelect"></select>';
          const newSelect = document.getElementById("modelSelect");
          if (!newSelect) return;
          const options = isCustom ? customModelIds.map((id) => ({ value: id, label: formatModelOptionLabel(id) })) : overrideIds ? overrideIds.map((id) => ({ value: id, label: formatModelOptionLabel(id) })) : (providerConfig?.models || []).map((opt) => ({
            value: opt.value,
            label: formatModelOptionLabel(opt.value, opt.label)
          }));
          if (isCustom) {
            const placeholder = document.createElement("option");
            placeholder.value = "";
            placeholder.textContent = "<\u8BF7\u9009\u62E9\u6A21\u578B>";
            newSelect.appendChild(placeholder);
          }
          options.forEach((opt) => {
            const option = document.createElement("option");
            option.value = opt.value;
            option.textContent = opt.label;
            newSelect.appendChild(option);
          });
          if (!isCustom && savedConfig.customModel && !options.find((opt) => opt.value === savedConfig.customModel)) {
            const option = document.createElement("option");
            option.value = savedConfig.customModel;
            option.textContent = formatModelOptionLabel(savedConfig.customModel);
            newSelect.appendChild(option);
          }
          const smallEl = modelSection.querySelector("small");
          if (smallEl) smallEl.style.display = "block";
          const fallbackModel = isCustom ? "" : selectedModelOverride ?? savedConfig.customModel ?? providerConfig?.defaultModel ?? options[0]?.value ?? "";
          const customSelectedModel = selectedModelOverride ?? profile.model ?? "";
          const selectedModel = isCustom ? customSelectedModel : fallbackModel;
          if (selectedModel && Array.from(newSelect.options).some((option) => option.value === selectedModel)) {
            newSelect.value = selectedModel;
          } else {
            newSelect.value = "";
          }
          newSelect.addEventListener("change", async (e) => {
            const cfg = loadConfig();
            cfg.customModel = e.target.value;
            if (document.getElementById("apiProvider")?.value === "custom") {
              updateCustomApiProfileFromForm(cfg);
            }
            await saveConfig(cfg);
            showSaveNotice();
          });
        }
      }
      const saveConfigDebounced = /* @__PURE__ */ (() => {
        let timer = null;
        return (showNotice = false) => {
          clearTimeout(timer);
          timer = setTimeout(async () => {
            const cfg = loadConfig();
            const apiKeyEl = document.getElementById("apiKey");
            const customEndpointEl = document.getElementById("customEndpoint");
            const modelSelectEl = document.getElementById("modelSelect");
            const apiProviderEl = document.getElementById("apiProvider");
            if (apiKeyEl) cfg.apiKey = apiKeyEl.value;
            if (modelSelectEl) cfg.customModel = modelSelectEl.value;
            if (apiProviderEl && customEndpointEl) syncProviderPresetFromBaseUrl(cfg);
            cfg.promptLike = document.getElementById("promptLike")?.value || cfg.promptLike;
            cfg.promptNeutral = document.getElementById("promptNeutral")?.value || cfg.promptNeutral;
            cfg.promptDislike = document.getElementById("promptDislike")?.value || cfg.promptDislike;
            cfg.minDelay = parseInt(document.getElementById("minDelay")?.value || cfg.minDelay);
            cfg.maxDelay = parseInt(document.getElementById("maxDelay")?.value || cfg.maxDelay);
            cfg.runDuration = parseInt(document.getElementById("runDuration")?.value || cfg.runDuration);
            cfg.enableComments = document.getElementById("enableComments")?.checked || false;
            cfg.skipProbability = parseInt(document.getElementById("skipProbability")?.value || cfg.skipProbability);
            cfg.maxRetries = parseInt(document.getElementById("maxRetries")?.value || cfg.maxRetries);
            cfg.watchBeforeLike = [
              parseInt(document.getElementById("watchMin")?.value || "2"),
              parseInt(document.getElementById("watchMax")?.value || "8")
            ];
            await saveConfig(cfg);
            if (showNotice) {
              showSaveNotice();
            }
          }, 300);
        };
      })();
      document.getElementById("apiProvider").value = config.apiProvider || "deepseek";
      document.getElementById("apiKey").value = config.apiKey || "";
      document.getElementById("customEndpoint").value = config.customEndpoint || "";
      if (config.selectedTemplate) {
        document.getElementById("template").value = config.selectedTemplate;
      }
      document.getElementById("minDelay").value = config.minDelay || 2;
      document.getElementById("maxDelay").value = config.maxDelay || 8;
      document.getElementById("runDuration").value = config.runDuration || 20;
      document.getElementById("watchMin").value = config.watchBeforeLike?.[0] || 2;
      document.getElementById("watchMax").value = config.watchBeforeLike?.[1] || 8;
      document.getElementById("skipProbability").value = config.skipProbability || 8;
      document.getElementById("maxRetries").value = config.maxRetries || 3;
      UI.floatingButton.addEventListener("click", async () => {
        if (wasDragging) {
          console.log("[\u667A\u80FD\u52A9\u624B] \u2139\uFE0F \u68C0\u6D4B\u5230\u62D6\u52A8\u6B8B\u7559\uFF0C\u5FFD\u7565\u70B9\u51FB\u4E8B\u4EF6");
          return;
        }
        const isHidden = UI.panel.style.display === "none";
        UI.panel.style.display = isHidden ? "block" : "none";
        if (!isHidden) {
          const cfg = loadConfig();
          cfg.panelMinimized = true;
          await saveConfig(cfg);
          console.log("[\u667A\u80FD\u52A9\u624B] \u{1F4BE} \u9762\u677F\u5173\u95ED\uFF0C\u5DF2\u4FDD\u5B58\u72B6\u6001");
        }
      });
      UI.panel.querySelector(".smart-feed-close").addEventListener("click", async () => {
        UI.panel.style.display = "none";
        const cfg = loadConfig();
        cfg.panelMinimized = true;
        await saveConfig(cfg);
        console.log("[\u667A\u80FD\u52A9\u624B] \u{1F4BE} \u9762\u677F\u5173\u95ED\uFF08X\u6309\u94AE\uFF09\uFF0C\u5DF2\u4FDD\u5B58\u72B6\u6001");
      });
      UI.floatingButton.addEventListener("mousedown", (e) => {
        if (e.button === 0) {
          isDraggingBtn = true;
          btnStartX = e.clientX;
          btnStartY = e.clientY;
          btnStartLeft = UI.floatingButton.offsetLeft;
          btnStartTop = UI.floatingButton.offsetTop;
          UI.floatingButton.style.transition = "none";
          if (UI.panel.style.display !== "none") {
            UI.panel.style.transition = "none";
          }
          e.preventDefault();
        }
      });
      const header = UI.panel.querySelector(".smart-feed-header");
      header.addEventListener("mousedown", (e) => {
        if (e.target.tagName !== "BUTTON") {
          isDraggingPanel = true;
          panelStartX = e.clientX;
          panelStartY = e.clientY;
          panelStartLeft = UI.panel.offsetLeft;
          panelStartTop = UI.panel.offsetTop;
          UI.panel.style.transition = "none";
        }
      });
      document.addEventListener("mousemove", (e) => {
        if (isDraggingBtn) {
          const dx = e.clientX - btnStartX;
          const dy = e.clientY - btnStartY;
          const newLeft = Math.max(0, Math.min(window.innerWidth - 60, btnStartLeft + dx));
          const newTop = Math.max(0, Math.min(window.innerHeight - 60, btnStartTop + dy));
          UI.floatingButton.style.left = newLeft + "px";
          UI.floatingButton.style.top = newTop + "px";
          if (UI.panel.style.display !== "none") {
            const panelLeft = Math.max(10, newLeft - 360);
            const panelTop = Math.max(10, newTop);
            UI.panel.style.left = panelLeft + "px";
            UI.panel.style.top = panelTop + "px";
          }
        }
        if (isDraggingPanel) {
          const dx = e.clientX - panelStartX;
          const dy = e.clientY - panelStartY;
          const newLeft = Math.max(10, Math.min(window.innerWidth - 420, panelStartLeft + dx));
          const newTop = Math.max(10, Math.min(window.innerHeight - 100, panelStartTop + dy));
          UI.panel.style.left = newLeft + "px";
          UI.panel.style.top = newTop + "px";
        }
      });
      document.addEventListener("mouseup", async () => {
        if (isDraggingBtn || isDraggingPanel) {
          UI.floatingButton.style.transition = "";
          UI.panel.style.transition = "";
          const leftStr = UI.floatingButton.style.left;
          const topStr = UI.floatingButton.style.top;
          const currentX = parseInt(leftStr.replace("px", ""));
          const currentY = parseInt(topStr.replace("px", ""));
          const moveDistance = Math.sqrt(
            Math.pow(currentX - btnStartLeft, 2) + Math.pow(currentY - btnStartTop, 2)
          );
          if (moveDistance > 5) {
            wasDragging = true;
            if (!isNaN(currentX) && !isNaN(currentY)) {
              const cfg = loadConfig();
              cfg.panelPosition = { x: currentX, y: currentY };
              cfg.panelMinimized = UI.panel.style.display === "none";
              await saveConfig(cfg);
            }
            setTimeout(() => {
              wasDragging = false;
            }, 300);
          } else {
            wasDragging = false;
          }
        }
        isDraggingBtn = false;
        isDraggingPanel = false;
      });
      UI.panel.querySelectorAll(".smart-feed-tab").forEach((tab) => {
        tab.addEventListener("click", () => {
          const tabName = tab.dataset.tab;
          UI.panel.querySelectorAll(".smart-feed-tab").forEach((t) => t.classList.remove("active"));
          tab.classList.add("active");
          UI.panel.querySelectorAll(".smart-feed-tab-content").forEach((content) => {
            content.style.display = content.dataset.content === tabName ? "block" : "none";
          });
        });
      });
      document.getElementById("apiProvider").addEventListener("change", async (e) => {
        const provider = e.target.value;
        const cfg = loadConfig();
        const previousProvider = cfg.apiProvider;
        if (previousProvider === "custom") {
          updateCustomApiProfileFromForm(cfg);
        }
        cfg.apiProvider = provider;
        if (provider === "custom") {
          const profile = cfg.customApiProfile || CONFIG.defaults.customApiProfile;
          cfg.customEndpoint = profile.baseUrl;
          cfg.apiKey = profile.apiKey;
          cfg.customModel = profile.model;
          document.getElementById("customEndpoint").value = cfg.customEndpoint;
          document.getElementById("apiKey").value = cfg.apiKey;
        } else {
          cfg.customEndpoint = CONFIG.getProviderBaseUrl(provider);
          cfg.customModel = CONFIG.getDefaultModel(provider);
          if (previousProvider === "custom") {
            cfg.apiKey = "";
          }
          document.getElementById("customEndpoint").value = cfg.customEndpoint;
          document.getElementById("apiKey").value = cfg.apiKey;
        }
        await saveConfig(cfg);
        showSaveNotice();
        updateModelOptions(provider);
      });
      setTimeout(() => {
        updateModelOptions(config.apiProvider);
      }, 100);
      UI.panel.querySelectorAll(".smart-feed-help").forEach((help) => {
        help.addEventListener("click", () => {
          const tab = UI.panel.querySelector('.smart-feed-tab[data-tab="about"]');
          tab.click();
        });
      });
      document.getElementById("fetchModelsBtn").addEventListener("click", async () => {
        const btn = document.getElementById("fetchModelsBtn");
        const originalText = btn.textContent;
        const logTab = UI.panel.querySelector('.smart-feed-tab[data-tab="log"]');
        if (logTab) {
          logTab.click();
          document.getElementById("logContainer").innerHTML = "";
        }
        btn.textContent = "\u83B7\u53D6\u4E2D...";
        btn.disabled = true;
        const fetchConfig = readApiFormConfig();
        UI.log("\u{1F50D} \u68C0\u67E5 API Base URL \u548C Key...", "info", "debug");
        if (!fetchConfig.customEndpoint) {
          UI.log("\u274C \u68C0\u6D4B\u5230\u7A7A\u7684 API Base URL\uFF01", "error");
          UI.log("\u{1F4A1} \u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9884\u8BBE\uFF0C\u6216\u586B\u5199\u672C\u5730/\u8F6C\u53D1 API \u5730\u5740", "warning");
          btn.textContent = originalText;
          btn.disabled = false;
          return;
        }
        if (!fetchConfig.apiKey) {
          UI.log("\u274C \u68C0\u6D4B\u5230\u7A7A\u7684 API Key\uFF01", "error");
          UI.log("\u{1F4A1} \u8BF7\u5148\u7C98\u8D34 API Key\uFF0C\u518D\u70B9\u51FB\u201C\u2460 \u83B7\u53D6\u6A21\u578B\u201D", "warning");
          btn.textContent = originalText;
          btn.disabled = false;
          return;
        }
        try {
          UI.log("\u{1F4DA} \u6B63\u5728\u83B7\u53D6\u6A21\u578B\u5217\u8868...", "info");
          const result = await AIService.fetchModels(fetchConfig);
          const cfg = loadConfig();
          if (fetchConfig.apiProvider === "custom") {
            cfg.apiProvider = "custom";
            document.getElementById("apiProvider").value = "custom";
            cfg.customEndpoint = fetchConfig.customEndpoint;
            cfg.apiKey = fetchConfig.apiKey;
            cfg.customModel = "";
            cfg.customApiProfile = {
              baseUrl: fetchConfig.customEndpoint,
              apiKey: fetchConfig.apiKey,
              model: "",
              modelIds: result.models,
              fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
            };
            updateModelOptions("custom", result.models, "");
            UI.log("\u2705 \u5DF2\u83B7\u53D6\u6A21\u578B\u5217\u8868\uFF0C\u8BF7\u5148\u5728\u201C\u6A21\u578B\u9009\u62E9\u201D\u4E2D\u9009\u4E00\u4E2A\u6A21\u578B\uFF0C\u518D\u70B9\u201C\u2461 \u6D4B\u8BD5\u8FDE\u63A5\u201D", "success");
          } else {
            cfg.apiProvider = fetchConfig.apiProvider;
            cfg.customEndpoint = CONFIG.getProviderBaseUrl(fetchConfig.apiProvider);
            cfg.apiKey = fetchConfig.apiKey;
            cfg.customModel = result.defaultModel || chooseDefaultModel(result.models, "preset");
            updateModelOptions(fetchConfig.apiProvider, result.models, cfg.customModel);
            UI.log(`\u2705 \u5DF2\u81EA\u52A8\u9009\u62E9\u6A21\u578B: ${cfg.customModel}`, "success");
            UI.log("\u{1F4A1} \u4E0B\u4E00\u6B65\uFF1A\u70B9\u51FB\u201C\u2461 \u6D4B\u8BD5\u8FDE\u63A5\u201D", "info");
          }
          await saveConfig(cfg);
          showSaveNotice();
        } catch (e) {
          UI.log(`\u274C \u83B7\u53D6\u6A21\u578B\u5931\u8D25: ${e.message}`, "error");
          UI.log("\u{1F4A1} \u5982\u679C\u4F60\u7684 API \u4E0D\u652F\u6301 /models\uFF0C\u53EF\u4EE5\u624B\u52A8\u586B\u5199\u6A21\u578B\u540D\u79F0\u540E\u76F4\u63A5\u6D4B\u8BD5\u8FDE\u63A5", "warning");
        }
        btn.textContent = originalText;
        btn.disabled = false;
      });
      document.getElementById("testApiBtn").addEventListener("click", async () => {
        const btn = document.getElementById("testApiBtn");
        const originalText = btn.textContent;
        const logTab = UI.panel.querySelector('.smart-feed-tab[data-tab="log"]');
        if (logTab) {
          logTab.click();
          document.getElementById("logContainer").innerHTML = "";
        }
        btn.textContent = "\u6D4B\u8BD5\u4E2D...";
        btn.disabled = true;
        const testConfig = readApiFormConfig();
        UI.log("\u{1F50D} \u6267\u884C\u524D\u7F6E\u68C0\u67E5...", "info", "debug");
        if (!testConfig.apiKey) {
          UI.log("\u274C \u68C0\u6D4B\u5230\u7A7A\u7684 API Key\uFF01", "error");
          UI.log('\u{1F4A1} \u8BF7\u5728"\u57FA\u7840\u8BBE\u7F6E"\u4E2D\u586B\u5199 API Key \u540E\u518D\u6D4B\u8BD5', "warning");
          btn.textContent = originalText;
          btn.disabled = false;
          return;
        }
        if (!testConfig.customEndpoint) {
          UI.log("\u274C \u68C0\u6D4B\u5230\u7A7A\u7684 API Base URL\uFF01", "error");
          UI.log("\u{1F4A1} \u8BF7\u9009\u62E9\u4E00\u4E2A\u9884\u8BBE\uFF0C\u6216\u586B\u5199\u672C\u5730/\u8F6C\u53D1 API \u5730\u5740", "warning");
          btn.textContent = originalText;
          btn.disabled = false;
          return;
        }
        if (!testConfig.customModel) {
          UI.log("\u274C \u8FD8\u6CA1\u6709\u9009\u62E9\u6A21\u578B\uFF01", "error");
          UI.log("\u{1F4A1} \u8BF7\u5148\u70B9\u51FB\u201C\u2460 \u83B7\u53D6\u6A21\u578B\u201D\uFF0C\u7136\u540E\u5728\u201C\u6A21\u578B\u9009\u62E9\u201D\u91CC\u9009\u4E00\u4E2A\u6A21\u578B", "warning");
          btn.textContent = originalText;
          btn.disabled = false;
          return;
        }
        UI.log("\u2705 \u524D\u7F6E\u68C0\u67E5\u901A\u8FC7\uFF0C\u5F00\u59CB\u6D4B\u8BD5...", "success");
        UI.log("", "info");
        const result = await AIService.testAPI(testConfig);
        if (result.success) {
          UI.log("", "success");
          UI.log("\u{1F389} \u6D4B\u8BD5\u6210\u529F\uFF01\u53EF\u4EE5\u5F00\u59CB\u4F7F\u7528\u4E86", "success");
          UI.log('\u{1F4A1} \u5982\u9700\u4FEE\u6539\u914D\u7F6E\uFF0C\u8BF7\u5728"\u57FA\u7840\u8BBE\u7F6E"\u6807\u7B7E\u9875\u8C03\u6574', "info");
          const cfg = loadConfig();
          cfg.apiProvider = testConfig.apiProvider;
          cfg.customEndpoint = testConfig.customEndpoint;
          cfg.apiKey = testConfig.apiKey;
          cfg.customModel = testConfig.customModel;
          if (testConfig.apiProvider === "custom") {
            updateCustomApiProfileFromForm(cfg);
          }
          await saveConfig(cfg);
        } else {
          UI.log("", "error");
          UI.log("\u{1F48A} \u6545\u969C\u6392\u67E5\u5EFA\u8BAE:", "warning");
          UI.log("  1. \u68C0\u67E5 API Key \u662F\u5426\u6B63\u786E\uFF08\u6CE8\u610F\u524D\u540E\u7A7A\u683C\uFF09", "warning");
          UI.log("  2. \u786E\u8BA4 API Base URL \u548C\u5B9E\u9645 Key \u5339\u914D", "warning");
          UI.log("  3. \u68C0\u67E5\u6D4F\u89C8\u5668\u662F\u5426\u80FD\u8BBF\u95EE\u5BF9\u5E94 API \u5730\u5740", "warning");
          UI.log("  4. \u67E5\u770B\u4E0A\u65B9\u54CD\u5E94\u4F53\u4E2D\u7684\u5177\u4F53\u9519\u8BEF\u4FE1\u606F", "warning");
        }
        btn.textContent = originalText;
        btn.disabled = false;
      });
      document.getElementById("template").addEventListener("change", async (e) => {
        const templateName = e.target.value;
        const cfg = loadConfig();
        cfg.selectedTemplate = templateName;
        if (templateName && CONFIG.templates[templateName]) {
          const tpl = CONFIG.templates[templateName];
          document.getElementById("promptLike").value = tpl.like;
          document.getElementById("promptNeutral").value = tpl.neutral;
          document.getElementById("promptDislike").value = tpl.dislike;
          cfg.promptLike = tpl.like;
          cfg.promptNeutral = tpl.neutral;
          cfg.promptDislike = tpl.dislike;
        }
        await saveConfig(cfg);
        showSaveNotice();
      });
      const inputs = [
        "apiKey",
        "customEndpoint",
        "promptLike",
        "promptNeutral",
        "promptDislike",
        "minDelay",
        "maxDelay",
        "runDuration",
        "watchMin",
        "watchMax",
        "skipProbability",
        "maxRetries"
      ];
      inputs.forEach((id) => {
        const el = document.getElementById(id);
        if (el) {
          el.addEventListener("blur", async () => {
            const cfg = loadConfig();
            if (el.type === "checkbox") {
              cfg[id] = el.checked;
            } else if (id === "watchMin" || id === "watchMax") {
              cfg.watchBeforeLike = [
                parseInt(document.getElementById("watchMin").value),
                parseInt(document.getElementById("watchMax").value)
              ];
            } else if (id === "customEndpoint") {
              syncProviderPresetFromBaseUrl(cfg);
            } else {
              cfg[id] = el.type === "number" ? parseInt(el.value) : el.value;
            }
            if (id === "apiKey" && document.getElementById("apiProvider")?.value === "custom") {
              updateCustomApiProfileFromForm(cfg);
            }
            await saveConfig(cfg);
            showSaveNotice();
          });
        }
      });
      const saveBtn = document.createElement("button");
      saveBtn.className = "smart-feed-button smart-feed-button-secondary";
      saveBtn.textContent = "\u{1F4BE} \u4FDD\u5B58\u5F53\u524D\u914D\u7F6E";
      saveBtn.style.marginTop = "10px";
      saveBtn.onclick = () => saveConfigDebounced(true);
      const basicContent = document.querySelector('[data-content="basic"]');
      if (basicContent) {
        basicContent.appendChild(saveBtn);
      }
      document.getElementById("startBtnTop").addEventListener("click", () => {
        if (getController().isRunning) {
          getController().stop();
        } else {
          getController().start();
        }
      });
      document.getElementById("clearLog").addEventListener("click", () => {
        document.getElementById("logContainer").innerHTML = "";
        UI.log("\u65E5\u5FD7\u5DF2\u6E05\u7A7A", "info");
      });
      const helpBox = document.querySelector(".collapsible-help-box");
      if (helpBox) {
        const header2 = helpBox.querySelector(".help-header");
        const btn = helpBox.querySelector(".help-toggle-btn");
        header2.addEventListener("click", () => {
          helpBox.classList.toggle("expanded");
          btn.textContent = helpBox.classList.contains("expanded") ? "\u6536\u8D77 \u25B2" : "\u5C55\u5F00 \u25BC";
        });
      }
    },
    log: (message, type = "info", level = "normal") => {
      const logContainer = document.getElementById("logContainer");
      if (!logContainer) return;
      const verboseCheckbox = document.getElementById("verboseLog");
      const isVerboseMode = verboseCheckbox?.checked || false;
      if (level === "debug" && !isVerboseMode) {
        return;
      }
      const item = document.createElement("div");
      item.className = "smart-feed-log-item";
      const colors = {
        info: "#64748b",
        success: "#10b981",
        warning: "#f59e0b",
        error: "#ef4444"
      };
      const displayText = message;
      const isLongText = message.length > 300;
      const isStructuredData = message.includes("{") || message.includes("JSON") || message.includes("\u8BF7\u6C42\u4F53") || message.includes("\u54CD\u5E94\u4F53");
      const timeSpan = document.createElement("span");
      timeSpan.className = "smart-feed-log-time";
      timeSpan.textContent = (/* @__PURE__ */ new Date()).toLocaleTimeString();
      const textSpan = document.createElement("span");
      textSpan.className = "smart-feed-log-text";
      textSpan.style.color = colors[type];
      if (isLongText && isStructuredData) {
        const wrapper = document.createElement("span");
        wrapper.className = "collapsible-log";
        const preview = document.createElement("span");
        preview.className = "log-preview";
        preview.textContent = message.substring(0, 120).replace(/\n/g, " ") + "...";
        const expandBtn = document.createElement("button");
        expandBtn.className = "expand-btn";
        expandBtn.addEventListener("click", function() {
          this.parentElement.classList.toggle("expanded");
        });
        const fullDiv = document.createElement("div");
        fullDiv.className = "log-full";
        fullDiv.textContent = message;
        wrapper.appendChild(preview);
        wrapper.appendChild(expandBtn);
        wrapper.appendChild(fullDiv);
        textSpan.appendChild(wrapper);
      } else {
        textSpan.textContent = displayText;
      }
      item.appendChild(timeSpan);
      item.appendChild(textSpan);
      logContainer.appendChild(item);
      logContainer.scrollTop = logContainer.scrollHeight;
      while (logContainer.children.length > 400) {
        logContainer.removeChild(logContainer.firstChild);
      }
    },
    updateStats: (stats) => {
      document.getElementById("statTotal").textContent = stats.total;
      document.getElementById("statLiked").textContent = stats.liked;
      document.getElementById("statNeutral").textContent = stats.neutral;
      document.getElementById("statDisliked").textContent = stats.disliked;
    }
  };

  // src/controller/controller.ts
  var Controller = {
    isRunning: false,
    startTime: null,
    consecutiveErrors: 0,
    stats: {
      total: 0,
      liked: 0,
      neutral: 0,
      disliked: 0,
      skipped: 0,
      errors: 0
    },
    cleanup: async () => {
      try {
        UI.log("\u{1F9F9} \u6B63\u5728\u6E05\u7406\u8FD0\u884C\u72B6\u6001...", "info");
        const video = document.querySelector("video");
        if (video && video.paused) {
          video.play().catch((e) => {
            console.warn("[\u667A\u80FD\u52A9\u624B] \u89C6\u9891\u6062\u590D\u64AD\u653E\u5931\u8D25:", e);
          });
        }
        UI.log("\u2705 \u6E05\u7406\u5B8C\u6210", "success");
      } catch (e) {
        console.warn("[\u667A\u80FD\u52A9\u624B] \u6E05\u7406\u8FC7\u7A0B\u51FA\u9519:", e);
        UI.log("\u26A0\uFE0F \u6E05\u7406\u65F6\u51FA\u73B0\u5F02\u5E38\uFF08\u53EF\u5FFD\u7565\uFF09", "warning");
      }
    },
    start: async () => {
      const config = loadConfig();
      if (!config.apiKey) {
        alert('\u274C \u8BF7\u5148\u914D\u7F6EAPI Key\uFF01\n\n\u70B9\u51FB\u53F3\u4E0A\u89D2"\u5173\u4E8E"\u6807\u7B7E\u67E5\u770B\u83B7\u53D6\u6559\u7A0B');
        return;
      }
      if (Controller.isRunning) {
        UI.log("\u26A0\uFE0F \u811A\u672C\u5DF2\u5728\u8FD0\u884C\u4E2D", "warning");
        return;
      }
      Controller.isRunning = true;
      Controller.startTime = Date.now();
      Controller.consecutiveErrors = 0;
      Controller.stats = { total: 0, liked: 0, neutral: 0, disliked: 0, skipped: 0, errors: 0 };
      const btn = document.getElementById("startBtnTop");
      btn.textContent = "\u23F8 \u505C\u6B62";
      btn.className = "smart-feed-start-btn running";
      UI.floatingButton.classList.add("running");
      UI.floatingButton.title = "\u8FD0\u884C\u4E2D...\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5";
      UI.log("========================================", "info");
      UI.log("\u{1F680} \u667A\u80FD\u52A9\u624B\u542F\u52A8\u6210\u529F", "success");
      UI.log(`\u{1F4CB} \u8FD0\u884C\u914D\u7F6E: ${config.judgeMode === "single" ? "\u5355\u6B21\u8C03\u7528" : "\u53CC\u91CD\u5224\u5B9A"} | \u95F4\u9694${config.minDelay}-${config.maxDelay}\u79D2 | \u65F6\u957F${config.runDuration}\u5206\u949F`, "info");
      UI.log("========================================", "info");
      while (Controller.isRunning) {
        try {
          if (!Controller.isRunning) {
            UI.log("\u23F9\uFE0F \u68C0\u6D4B\u5230\u505C\u6B62\u4FE1\u53F7\uFF0C\u9000\u51FA\u5FAA\u73AF", "info");
            break;
          }
          const elapsed = (Date.now() - Controller.startTime) / 1e3 / 60;
          if (elapsed >= config.runDuration) {
            UI.log("\u23F0 \u5DF2\u8FBE\u5230\u8BBE\u5B9A\u8FD0\u884C\u65F6\u957F\uFF0C\u81EA\u52A8\u505C\u6B62", "warning");
            break;
          }
          Controller.stats.total++;
          UI.updateStats(Controller.stats);
          UI.log(`
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u89C6\u9891 #${Controller.stats.total} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`, "info");
          if (Math.random() * 100 < config.skipProbability) {
            Controller.stats.skipped++;
            UI.log("\u23ED\uFE0F \u968F\u673A\u8DF3\u8FC7\u6B64\u89C6\u9891", "info");
            Utils.pressKey("ArrowDown");
            await Utils.randomDelay(config.minDelay, config.maxDelay);
            continue;
          }
          UI.log("\u{1F4E5} \u6B63\u5728\u5206\u6790\u5F53\u524D\u89C6\u9891...", "info");
          const videoInfo = await VideoExtractor.getCurrentVideoInfo(config);
          if (!Controller.isRunning) {
            UI.log("\u23F9\uFE0F \u68C0\u6D4B\u5230\u505C\u6B62\u4FE1\u53F7\uFF0C\u9000\u51FA\u5FAA\u73AF", "info");
            break;
          }
          if (!videoInfo) {
            UI.log("\u26A0\uFE0F \u65E0\u6CD5\u5B9A\u4F4D\u5F53\u524D\u89C6\u9891\uFF0C\u5C1D\u8BD5\u6062\u590D...", "warning");
            Controller.stats.errors++;
            Controller.consecutiveErrors++;
            UI.log("\u{1F504} \u6267\u884C\u6062\u590D\u64CD\u4F5C...", "info");
            Utils.pressKey("ArrowDown");
            await Utils.randomDelay(2, 2.5);
            if (!Controller.isRunning) break;
            Utils.pressKey("ArrowDown");
            await Utils.randomDelay(2, 2.5);
            if (Controller.consecutiveErrors >= 5) {
              UI.log("\u274C \u8FDE\u7EED\u5931\u8D255\u6B21\uFF0C\u811A\u672C\u53EF\u80FD\u5DF2\u5931\u6548", "error");
              UI.log("\u{1F4A1} \u6700\u5E38\u89C1\u539F\u56E0\uFF1A\u6296\u97F3\u66F4\u65B0\u4E86\u9875\u9762\u7ED3\u6784\uFF0C\u5BFC\u81F4DOM\u9009\u62E9\u5668\u5931\u6548", "warning");
              UI.log("\u{1F4E7} \u8BF7\u5C06\u6B64\u95EE\u9898\u53CD\u9988\u7ED9\u4F5C\u8005\[email protected]", "warning");
              UI.log('\u{1F31F} \u6216\u8BBF\u95EEGitHub\u63D0\u4EA4Issue\uFF08\u70B9\u51FB\u9762\u677F"\u5173\u4E8E"\u6807\u7B7E\u67E5\u770B\u94FE\u63A5\uFF09', "info");
              alert('\u26A0\uFE0F \u811A\u672C\u53EF\u80FD\u5DF2\u5931\u6548\n\n\u3010\u6700\u53EF\u80FD\u7684\u539F\u56E0\u3011\n\u2717 \u6296\u97F3\u66F4\u65B0\u4E86\u9875\u9762\u7ED3\u6784\uFF08DOM\u9009\u62E9\u5668\u5931\u6548\uFF09\n\n\u3010\u5176\u4ED6\u53EF\u80FD\u539F\u56E0\u3011\n\u2022 \u9875\u9762\u957F\u65F6\u95F4\u8FD0\u884C\u5BFC\u81F4DOM\u6DF7\u4E71\n\u2022 \u7F51\u7EDC\u4E0D\u7A33\u5B9A\n\n\u3010\u5EFA\u8BAE\u64CD\u4F5C\u3011\n1. \u5148\u5237\u65B0\u9875\u9762\u540E\u91CD\u8BD5\n2. \u5982\u679C\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u53CD\u9988\u7ED9\u4F5C\u8005\n\n\u{1F4E7} \u53CD\u9988\u90AE\u7BB1\[email protected]\n\u{1F31F} GitHub\uFF1A\u67E5\u770B\u9762\u677F"\u5173\u4E8E"\u6807\u7B7E');
              Controller.stop();
              break;
            }
            continue;
          }
          if (videoInfo.isLive) {
            UI.log("\u{1F534} \u68C0\u6D4B\u5230\u76F4\u64AD\uFF0C\u76F4\u63A5\u8DF3\u8FC7", "warning");
            Utils.pressKey("ArrowDown");
            await Utils.randomDelay(2, 3);
            if (!Controller.isRunning) break;
            Controller.stats.skipped++;
            UI.updateStats(Controller.stats);
            continue;
          }
          if (!videoInfo.title || videoInfo.title.length < 3) {
            UI.log("\u26A0\uFE0F \u6807\u9898\u4FE1\u606F\u4E0D\u8DB3\uFF0C\u8DF3\u8FC7", "warning");
            Controller.stats.errors++;
            Controller.consecutiveErrors++;
            if (!videoInfo.title && !videoInfo.author && videoInfo.tags.length === 0) {
              UI.log("\u26A0\uFE0F \u5B8C\u5168\u65E0\u6CD5\u63D0\u53D6\u89C6\u9891\u4FE1\u606F\uFF08\u53EF\u80FD\u662FDOM\u9009\u62E9\u5668\u5931\u6548\uFF09", "warning");
              if (Controller.consecutiveErrors >= 3) {
                UI.log("\u274C \u8FDE\u7EED3\u6B21\u5B8C\u5168\u65E0\u6CD5\u63D0\u53D6\u4FE1\u606F\uFF0C\u5224\u5B9A\u811A\u672C\u5DF2\u5931\u6548", "error");
                UI.log("\u{1F4A1} \u6296\u97F3\u5F88\u53EF\u80FD\u66F4\u65B0\u4E86\u9875\u9762HTML\u7ED3\u6784", "warning");
                UI.log("\u{1F4E7} \u8BF7\u53CD\u9988\u6B64\u95EE\u9898\[email protected]", "warning");
                UI.log("\u{1F48A} \u53CD\u9988\u65F6\u8BF7\u8BF4\u660E\u53D1\u73B0\u65F6\u95F4\u548C\u6D4F\u89C8\u5668\u7248\u672C", "info");
                alert("\u26A0\uFE0F \u68C0\u6D4B\u5230DOM\u9009\u62E9\u5668\u5931\u6548\n\n\u811A\u672C\u8FDE\u7EED3\u6B21\u65E0\u6CD5\u8BC6\u522B\u89C6\u9891\u4FE1\u606F\uFF0C\n\u8FD9\u901A\u5E38\u610F\u5473\u7740\u6296\u97F3\u66F4\u65B0\u4E86\u9875\u9762HTML\u7ED3\u6784\u3002\n\n\u8BF7\u5C06\u6B64\u95EE\u9898\u53CD\u9988\u7ED9\u4F5C\u8005\uFF1A\n\u{1F4E7} [email protected]\n\n\u3010\u53CD\u9988\u65F6\u8BF7\u63D0\u4F9B\u3011\n\u2022 \u53D1\u73B0\u65F6\u95F4\uFF08\u5982 2025-01-15\uFF09\n\u2022 \u6D4F\u89C8\u5668\u7248\u672C\uFF08\u6309F12\u67E5\u770BConsole\uFF09\n\u2022 \u89C6\u9891\u662F\u5426\u80FD\u6B63\u5E38\u64AD\u653E");
                Controller.stop();
                break;
              }
            }
            Utils.pressKey("ArrowDown");
            await Utils.randomDelay(2, 3);
            if (!Controller.isRunning) break;
            continue;
          }
          Controller.consecutiveErrors = 0;
          const dossier = VideoExtractor.buildDossier(videoInfo);
          let retries = 0;
          let result = null;
          while (retries < config.maxRetries && !result && Controller.isRunning) {
            try {
              UI.log(`\u{1F916} AI\u5206\u6790\u4E2D${retries > 0 ? ` (\u91CD\u8BD5 ${retries}/${config.maxRetries})` : ""}...`, "info");
              result = await AIService.judge(dossier, config);
              if (!Controller.isRunning) {
                UI.log("\u23F9\uFE0F AI\u5206\u6790\u5B8C\u6210\uFF0C\u4F46\u68C0\u6D4B\u5230\u505C\u6B62\u4FE1\u53F7", "info");
                break;
              }
            } catch (e) {
              if (!Controller.isRunning) {
                UI.log("\u23F9\uFE0F \u68C0\u6D4B\u5230\u505C\u6B62\u4FE1\u53F7\uFF0C\u4E2D\u6B62\u91CD\u8BD5", "info");
                break;
              }
              retries++;
              UI.log(`\u274C AI\u8C03\u7528\u5931\u8D25 (${retries}/${config.maxRetries}): ${e.message}`, "error");
              if (retries < config.maxRetries) {
                const waitTime = Math.pow(2, retries);
                UI.log(`\u23F3 \u7B49\u5F85 ${waitTime} \u79D2\u540E\u91CD\u8BD5...`, "warning");
                await Utils.randomDelay(waitTime, waitTime + 2);
                if (!Controller.isRunning) {
                  UI.log("\u23F9\uFE0F \u7B49\u5F85\u671F\u95F4\u68C0\u6D4B\u5230\u505C\u6B62\u4FE1\u53F7", "info");
                  break;
                }
              }
            }
          }
          if (!Controller.isRunning) {
            UI.log("\u23F9\uFE0F \u9000\u51FA\u91CD\u8BD5\u5FAA\u73AF\uFF0C\u68C0\u6D4B\u5230\u505C\u6B62\u4FE1\u53F7", "info");
            break;
          }
          if (!result) {
            Controller.stats.errors++;
            UI.log("\u{1F480} \u591A\u6B21\u91CD\u8BD5\u5931\u8D25\uFF0C\u8DF3\u8FC7\u8BE5\u89C6\u9891", "error");
            Utils.pressKey("ArrowDown");
            await Utils.randomDelay(2, 3);
            if (!Controller.isRunning) break;
            continue;
          }
          const actionMap = { like: "\u70B9\u8D5E \u{1F44D}", neutral: "\u5FFD\u7565 \u27A1\uFE0F", dislike: "\u4E0D\u611F\u5174\u8DA3 \u{1F44E}" };
          Controller.stats[result.action === "like" ? "liked" : result.action === "dislike" ? "disliked" : "neutral"]++;
          UI.log(`\u2728 AI\u5224\u65AD: ${actionMap[result.action]}`, "success");
          UI.log(`\u{1F4AD} \u7406\u7531: ${result.reason}`, "info");
          await VideoExtractor.executeAction(result.action, config);
          if (!Controller.isRunning) {
            UI.log("\u23F9\uFE0F \u64CD\u4F5C\u5B8C\u6210\uFF0C\u4F46\u68C0\u6D4B\u5230\u505C\u6B62\u4FE1\u53F7", "info");
            break;
          }
          UI.updateStats(Controller.stats);
          const delay = Math.random() * (config.maxDelay - config.minDelay) + config.minDelay;
          UI.log(`\u23F1\uFE0F \u7B49\u5F85 ${delay.toFixed(1)} \u79D2\u540E\u7EE7\u7EED...`, "info");
          await Utils.randomDelay(config.minDelay, config.maxDelay);
        } catch (e) {
          if (!Controller.isRunning) {
            UI.log("\u23F9\uFE0F \u5F02\u5E38\u5904\u7406\u4E2D\u68C0\u6D4B\u5230\u505C\u6B62\u4FE1\u53F7", "info");
            break;
          }
          Controller.stats.errors++;
          Controller.consecutiveErrors++;
          UI.log(`\u{1F4A5} \u53D1\u751F\u672A\u9884\u671F\u9519\u8BEF: ${e.message}`, "error");
          console.error("[\u667A\u80FD\u52A9\u624B]", e);
          UI.log("\u{1F504} \u5C1D\u8BD5\u81EA\u52A8\u6062\u590D...", "warning");
          Utils.pressKey("ArrowDown");
          await Utils.randomDelay(3, 5);
        }
      }
      Controller.stop();
    },
    stop: async () => {
      if (!Controller.isRunning) return;
      Controller.isRunning = false;
      await Controller.cleanup();
      const btn = document.getElementById("startBtnTop");
      if (btn) {
        btn.textContent = "\u25B6 \u5F00\u59CB";
        btn.className = "smart-feed-start-btn";
      }
      UI.floatingButton.classList.remove("running");
      UI.floatingButton.title = "\u70B9\u51FB\u6253\u5F00\u667A\u80FD\u52A9\u624B";
      const stats = Controller.stats;
      UI.log("\n========================================", "info");
      UI.log("\u{1F3C1} \u8FD0\u884C\u7ED3\u675F", "success");
      UI.log(`\u{1F4CA} \u7EDF\u8BA1\u6570\u636E:`, "info");
      UI.log(`   \u603B\u8BA1: ${stats.total} \u4E2A\u89C6\u9891`, "info");
      UI.log(`   \u70B9\u8D5E \u{1F44D}: ${stats.liked} | \u5FFD\u7565 \u27A1\uFE0F: ${stats.neutral} | \u4E0D\u611F\u5174\u8DA3 \u{1F44E}: ${stats.disliked}`, "info");
      UI.log(`   \u8DF3\u8FC7 \u23ED\uFE0F: ${stats.skipped} | \u9519\u8BEF \u274C: ${stats.errors}`, "info");
      const runTime = Controller.startTime ? (Date.now() - Controller.startTime) / 1e3 / 60 : 0;
      UI.log(`\u23F1\uFE0F \u8FD0\u884C\u65F6\u957F: ${runTime.toFixed(1)} \u5206\u949F`, "info");
      UI.log("========================================", "info");
    }
  };

  // src/bootstrap/init.ts
  setUI(UI);
  setController(Controller);
  var init = () => {
    if (!window.location.hostname.includes("douyin.com")) {
      return;
    }
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", init);
      return;
    }
    setTimeout(() => {
      try {
        UI.create();
        console.log("[\u667A\u80FD\u52A9\u624B] \u5DF2\u52A0\u8F7D\u6210\u529F");
        console.log("[\u667A\u80FD\u52A9\u624B] \u5F00\u53D1\u8005\uFF1A\u8BF7\u67E5\u770B\u4EE3\u7801\u5F00\u5934\u7684\u6CE8\u91CA\u4E86\u89E3\u7EF4\u62A4\u8BF4\u660E");
      } catch (e) {
        console.error("[\u667A\u80FD\u52A9\u624B] \u521D\u59CB\u5316\u5931\u8D25:", e);
      }
    }, 2e3);
  };

  // src/entry/userscript.ts
  (() => {
    "use strict";
    init();
  })();
})();