QwenPersona

一个便于用户自定义、保存并同步 Qwen Chat 自定义角色的 Tampermonkey 脚本。A Tampermonkey script for customizing user-defined personas in Qwen Chat.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         QwenPersona
// @namespace    https://www.kev1nweng.space
// @version      1764268716
// @description  一个便于用户自定义、保存并同步 Qwen Chat 自定义角色的 Tampermonkey 脚本。A Tampermonkey script for customizing user-defined personas in Qwen Chat.
// @author       小翁同学 (kev1nweng)
// @license      AGPL-3.0
// @match        https://chat.qwen.ai/*
// @grant        none
// ==/UserScript==

(function () {
  "use strict";

  // ==================== Constants & Configuration ====================
  const CONSTANTS = {
    STORAGE: {
      PERSONAS: "qwen_personas",
      SELECTED: "qwen_selected_persona",
      MODELS_CACHE: "qwen_models_cache",
      CHAT_MAP: "qwen_chat_persona_map",
    },
    SELECTORS: {
      // Custom UI IDs
      CONTAINER: "persona-dropdown-container",
      TRIGGER: "persona-trigger",
      MENU: "persona-dropdown-menu",
      MODAL_OVERLAY: "persona-modal-overlay",
      MODAL: "persona-modal",
      CREATE_BTN: "persona-create-btn",
      IMPORT_BTN: "persona-import-btn",
      SAVE_BTN: "persona-save-btn",
      STYLE_ID: "persona-manager-styles",

      // Form Inputs
      INPUT_NAME: "persona-name-input",
      INPUT_EMOJI: "persona-emoji-input",
      INPUT_MODEL: "persona-model-input",
      INPUT_PROMPT: "persona-prompt-input",
      INPUT_DEEP_THINKING: "persona-deep-thinking-input",
      INPUT_WEB_SEARCH: "persona-web-search-input",

      // Qwen UI Elements
      HEADER_DESKTOP: ".header-desktop",
      HEADER_LEFT: ".header-desktop .header-content .header-left",
      MODEL_SELECTOR: '[class*="index-module__web-model-selector"]',
      MODEL_SELECTOR_CONTENT: '[class*="index-module__model-selector-content"]',
      MODEL_SELECTOR_DROPDOWN:
        '[class*="index-module__model-selector-dropdown"]',
      MODEL_SELECTOR_ITEM: '[class*="index-module__model-selector-item"]',
      MODEL_NAME_TEXT: '[class*="index-module__model-name-text"]',
      MODEL_SELECTOR_VIEW_MORE:
        '[class*="index-module__model-selector-view-more"]',
      MODEL_VIEW_MORE_TEXT: '[class*="index-module__view-more-text"]',
      ANT_DROPDOWN_TRIGGER: ".ant-dropdown-trigger",
      CHAT_INPUT_FEATURE_BTN: ".chat-input-feature-btn",
      CHAT_INPUT_FEATURE_TEXT: ".chat-input-feature-btn-text",
      WEB_SEARCH_BTN: "button.websearch_button",
      TEXTAREA: "textarea",
      INPUT_CONTAINER: ".chat-message-input-container-inner",

      // Classes
      TRIGGER_COLLAPSED: "collapsed",
      TRIGGER_ACTIVE: "active",
      MENU_VISIBLE: "visible",
      ARROW_OPEN: "open",
      ITEM_SELECTED: "selected",
      INPUT_DISABLED: "persona-input-disabled",
      INTERACTION_DISABLED: "persona-interaction-disabled",
      TRANSITION: "persona-input-transition",
      AGENT_ACTIVE: "persona-agent-active",
    },
  };

  // ==================== State Management ====================
  const State = {
    personas: [],
    selectedPersonaId: null,
    availableModels: [],
    dropdownVisible: false,
    modalVisible: false,
    editingPersona: null,
    lastUrl: location.href,
    chatPersonaMap: {},
  };

  // ==================== I18n Service ====================
  const I18n = {
    locale: "en",
    translations: {
      en: {
        noPersona: "No Persona",
        selectPersona: "Select Persona",
        useDefaultSettings: "Use default settings",
        defaultModel: "Default Model",
        edit: "Edit",
        delete: "Delete",
        createPersona: "Create new Persona...",
        personaName: "Persona Name",
        namePlaceholder: "e.g. Coding Assistant, Translator...",
        icon: "Icon",
        model: "Model",
        useCurrentModel: "Use current model",
        modelHint:
          "Select the model for this Persona. Leave empty to use the current model.",
        systemPrompt: "System Prompt",
        promptPlaceholder:
          "Enter custom system prompt to define AI behavior and role...",
        promptHint:
          "This prompt will be injected as a system message, overriding default prompts.",
        features: "Features",
        deepThinking: "Deep Thinking",
        webSearch: "Web Search",
        featuresHint:
          "Enabled features will be automatically activated for each chat.",
        cancel: "Cancel",
        save: "Save",
        deleteConfirm: "Are you sure you want to delete this Persona?",
        editPersona: "Edit Persona",
        createNewPersona: "Create New Persona",
        importFromUrl: "Import from URL",
        enterUrl: "Enter the URL of the persona configuration JSON:",
        importSuccess: "Personas imported successfully!",
        importError:
          "Failed to import personas. Please check the URL and JSON format.",
      },
      zh: {
        noPersona: "无 Persona",
        selectPersona: "选择 Persona",
        useDefaultSettings: "使用默认设置",
        defaultModel: "默认模型",
        edit: "编辑",
        delete: "删除",
        createPersona: "创建新 Persona...",
        personaName: "Persona 名称",
        namePlaceholder: "例如:代码助手、翻译专家...",
        icon: "图标",
        model: "模型",
        useCurrentModel: "使用当前选择的模型",
        modelHint: "选择此 Persona 使用的模型,留空则使用页面当前选择的模型",
        systemPrompt: "系统提示词",
        promptPlaceholder: "输入自定义系统提示词,定义 AI 的行为和角色...",
        promptHint: "此提示词将作为系统消息注入到对话中,优先于默认系统提示",
        features: "增强功能",
        deepThinking: "深度思考",
        webSearch: "联网搜索",
        featuresHint: "启用后将在每次对话时自动开启对应功能",
        cancel: "取消",
        save: "保存",
        deleteConfirm: "确定要删除这个 Persona 吗?",
        editPersona: "编辑 Persona",
        createNewPersona: "创建新 Persona",
        importFromUrl: "从 URL 导入",
        enterUrl: "请输入 Persona 配置 JSON 的 URL:",
        importSuccess: "Persona 导入成功!",
        importError: "导入失败,请检查 URL 和 JSON 格式。",
      },
    },

    init() {
      const lang = navigator.language || navigator.userLanguage;
      this.locale = lang.startsWith("zh") ? "zh" : "en";
    },

    t(key) {
      return (
        this.translations[this.locale]?.[key] ||
        this.translations["en"][key] ||
        key
      );
    },
  };

  // ==================== Utilities ====================
  const Utils = {
    escapeHtml(str) {
      if (!str) return "";
      const div = document.createElement("div");
      div.textContent = str;
      return div.innerHTML;
    },

    sleep(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    },

    waitForElement(selector, timeout = 2000) {
      return new Promise((resolve) => {
        if (document.querySelector(selector)) {
          return resolve(document.querySelector(selector));
        }

        const observer = new MutationObserver((mutations) => {
          if (document.querySelector(selector)) {
            resolve(document.querySelector(selector));
            observer.disconnect();
          }
        });

        observer.observe(document.body, {
          childList: true,
          subtree: true,
        });

        setTimeout(() => {
          observer.disconnect();
          resolve(null);
        }, timeout);
      });
    },
  };

  // ==================== Storage Service ====================
  const Storage = {
    loadPersonas() {
      try {
        const stored = localStorage.getItem(CONSTANTS.STORAGE.PERSONAS);
        State.personas = stored ? JSON.parse(stored) : [];
      } catch (e) {
        console.error("[QwenPersona] Failed to load personas:", e);
        State.personas = [];
      }
    },

    savePersonas() {
      try {
        localStorage.setItem(
          CONSTANTS.STORAGE.PERSONAS,
          JSON.stringify(State.personas)
        );
      } catch (e) {
        console.error("[QwenPersona] Failed to save personas:", e);
      }
    },

    loadSelectedPersona() {
      try {
        State.selectedPersonaId =
          localStorage.getItem(CONSTANTS.STORAGE.SELECTED) || null;
      } catch (e) {
        State.selectedPersonaId = null;
      }
    },

    saveSelectedPersona() {
      try {
        if (State.selectedPersonaId) {
          localStorage.setItem(
            CONSTANTS.STORAGE.SELECTED,
            State.selectedPersonaId
          );
        } else {
          localStorage.removeItem(CONSTANTS.STORAGE.SELECTED);
        }
      } catch (e) {
        console.error("[QwenPersona] Failed to save selected persona:", e);
      }
    },

    loadChatPersonaMap() {
      try {
        const stored = localStorage.getItem(CONSTANTS.STORAGE.CHAT_MAP);
        State.chatPersonaMap = stored ? JSON.parse(stored) : {};
      } catch (e) {
        State.chatPersonaMap = {};
      }
    },

    saveChatPersonaMap() {
      try {
        localStorage.setItem(
          CONSTANTS.STORAGE.CHAT_MAP,
          JSON.stringify(State.chatPersonaMap)
        );
      } catch (e) {
        console.error("[QwenPersona] Failed to save chat persona map:", e);
      }
    },
  };

  // ==================== UI Service ====================
  const UI = {
    injectStyles() {
      const style = document.createElement("style");
      style.id = CONSTANTS.SELECTORS.STYLE_ID;
      style.textContent = `
            /* Persona Dropdown Container */
            .persona-dropdown-container {
                position: relative;
                display: flex;
                align-items: center;
                margin-left: 6px;
                z-index: 100;
            }

            /* Transition Helper */
            .persona-input-transition {
                transition: filter 0.3s ease, opacity 0.3s ease;
            }

            /* Disabled Input State */
            .persona-input-disabled {
                filter: grayscale(100%);
                opacity: 0.7;
                pointer-events: none;
                cursor: not-allowed;
                position: relative;
            }

            .persona-input-disabled::after {
                content: "";
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                z-index: 10;
                border-radius: inherit;
            }

            /* Persona Trigger Button */
            .persona-trigger {
                display: flex;
                align-items: center;
              justify-content: flex-start;
              gap: 8px;
              width: var(--persona-trigger-expanded-width, 10rem);
              min-width: 36px;
              height: 36px;
              padding: 0 12px;
              box-sizing: border-box;
              border-radius: 999px;
              background: var(--container-secondary-fill, #f7f8fc);
              cursor: pointer;
              border: 1px solid var(--line-secondary-border, #e0e2eb);
              font-size: 14px;
              color: var(--character-primary-text, #2c2c36);
              overflow: hidden;
              transition:
                width 0.5s cubic-bezier(0.22, 1, 0.36, 1),
                padding 0.5s cubic-bezier(0.22, 1, 0.36, 1),
                gap 0.5s cubic-bezier(0.22, 1, 0.36, 1),
                background 0.2s ease,
                border 0.2s ease,
                color 0.2s ease,
                filter 0.3s ease,
                opacity 0.3s ease;
            }

            /* Collapsed State (Circular) */
            .persona-trigger.collapsed {
              width: 36px;
              padding: 0;
              justify-content: center;
              background: transparent;
              gap: 0;
              margin-right: 4px;
            }

            .persona-trigger.collapsed:hover {
                background: var(--container-secondary-fill, #f7f8fc);
            }

            .persona-trigger.collapsed .persona-trigger-text,
            .persona-trigger.collapsed .persona-trigger-arrow {
                opacity: 0;
                width: 0;
                margin: 0;
                pointer-events: none;
            }

            .persona-trigger.collapsed .persona-trigger-icon {
              margin: 0;
              font-size: 20px;
              width: 100%;
              height: 100%;
              justify-content: center;
            }

            .persona-trigger:hover {
                background: var(--container-tertiary-fill, #eef0f5);
            }

            .persona-trigger.active {
                border-color: var(--btn-brandprimary-fill, #615ced);
                background: var(--container-brandquinary-fill, #eeedff);
            }

            .persona-trigger-icon {
              font-size: 18px;
              flex-shrink: 0;
              width: 24px;
              height: 24px;
              display: flex;
              align-items: center;
              justify-content: center;
              line-height: 1;
            }

            .persona-trigger-text {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                flex: 1;
                transition: opacity 0.2s ease, width 0.2s ease;
            }

            .persona-trigger-arrow {
                font-size: 16px;
                transition: transform 0.2s ease, opacity 0.2s ease, width 0.2s ease;
                flex-shrink: 0;
                display: flex;
                align-items: center;
                justify-content: center;
                transform-origin: center;
            }

            .persona-trigger-arrow.open {
                transform: rotate(180deg);
            }

            /* Dropdown Menu */
            .persona-dropdown-menu {
                position: fixed;
                min-width: 240px;
                max-width: 320px;
                background: var(--container-primary-fill, #fff);
                border-radius: 16px;
                box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
                border: 1px solid var(--line-secondary-border, #e0e2eb);
                z-index: 99999;
                opacity: 0;
                transform: translateY(-8px);
                pointer-events: none;
                transition: opacity 0.2s ease, transform 0.2s ease;
                overflow: visible;
            }

            .persona-dropdown-menu.visible {
                opacity: 1;
                transform: translateY(0);
                pointer-events: auto;
            }

            .persona-dropdown-header {
                padding: 12px 16px;
                font-size: 12px;
                font-weight: 500;
                color: var(--character-tertiary-text, #8f91a8);
                border-bottom: 1px solid var(--line-secondary-border, #e0e2eb);
            }

            .persona-dropdown-list {
                max-height: 66vh;
                overflow-y: auto;
                padding: 0 8px;
            }

            .persona-dropdown-item {
                display: flex;
                align-items: center;
                gap: 10px;
                padding: 10px 12px;
                cursor: pointer !important;
                border-radius: 12px;
                transition: all 0.15s ease;
                margin: 8px 0;
                user-select: none;
            }

            .persona-dropdown-item:hover {
                background: var(--container-secondary-fill, #f7f8fc) !important;
            }

            .persona-dropdown-item.selected {
                background: var(--container-brandquinary-fill, #eeedff);
            }

            .persona-dropdown-item.selected:hover {
                background: var(--container-brandquinary-fill, #eeedff) !important;
            }

            .persona-dropdown-item-icon {
                width: 32px;
                height: 32px;
                border-radius: 8px;
                background: var(--container-tertiary-fill, #eef0f5);
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 16px;
                flex-shrink: 0;
            }

            .persona-dropdown-item.selected .persona-dropdown-item-icon {
                background: var(--btn-brandprimary-fill, #615ced);
                color: white;
            }

            .persona-dropdown-item-content {
                flex: 1;
                overflow: hidden;
            }

            .persona-dropdown-item-name {
                font-size: 14px;
                font-weight: 500;
                color: var(--character-primary-text, #2c2c36);
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }

            .persona-dropdown-item-model {
                font-size: 12px;
                color: var(--character-tertiary-text, #8f91a8);
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }

            .persona-dropdown-item-actions {
                display: flex;
                gap: 4px;
                opacity: 0;
                transition: opacity 0.15s ease;
            }

            .persona-dropdown-item:hover .persona-dropdown-item-actions {
                opacity: 1;
            }

            .persona-action-btn {
                width: 24px;
                height: 24px;
                border-radius: 6px;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: all 0.15s ease;
                color: var(--character-tertiary-text, #8f91a8);
            }

            .persona-action-btn:hover {
                background: var(--container-tertiary-fill, #eef0f5);
                color: var(--character-primary-text, #2c2c36);
            }

            .persona-action-btn.delete:hover {
                background: #fee2e2;
                color: #dc2626;
            }

            .persona-dropdown-divider {
                height: 1px;
                background: var(--line-secondary-border, #e0e2eb);
            }

            .persona-dropdown-footer {
                padding: 4px;
            }

            .persona-create-btn {
                display: flex;
                align-items: center;
                gap: 8px;
                padding: 10px 12px;
                cursor: pointer !important;
                border-radius: 12px;
                transition: all 0.15s ease;
                color: var(--btn-brandprimary-fill, #615ced);
                font-size: 14px;
                font-weight: 500;
                user-select: none;
            }

            .persona-create-btn:hover {
                background: var(--container-brandquinary-fill, #eeedff) !important;
            }

            /* Modal Styles */
            .persona-modal-overlay {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background: rgba(0, 0, 0, 0.5);
                display: flex;
                align-items: center;
                justify-content: center;
                z-index: 100000;
                opacity: 0;
                pointer-events: none;
                transition: opacity 0.2s ease;
            }

            .persona-modal-overlay.visible {
                opacity: 1;
                pointer-events: auto;
            }

            .persona-modal {
                background: var(--container-primary-fill, #fff);
                border-radius: 20px;
                width: 480px;
                max-width: 90vw;
                max-height: 90vh;
                overflow: hidden;
                transform: scale(0.95);
                transition: transform 0.2s ease;
            }

            .persona-modal-overlay.visible .persona-modal {
                transform: scale(1);
            }

            .persona-modal-header {
                display: flex;
                align-items: center;
                justify-content: space-between;
                padding: 20px 24px;
                border-bottom: 1px solid var(--line-secondary-border, #e0e2eb);
            }

            .persona-modal-title {
                font-size: 18px;
                font-weight: 600;
                color: var(--character-primary-text, #2c2c36);
            }

            .persona-modal-close {
                width: 32px;
                height: 32px;
                border-radius: 8px;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: all 0.15s ease;
                color: var(--character-tertiary-text, #8f91a8);
            }

            .persona-modal-close:hover {
                background: var(--container-secondary-fill, #f7f8fc);
            }

            .persona-modal-body {
                padding: 24px;
                overflow-y: auto;
                max-height: calc(90vh - 180px);
            }

            .persona-form-group {
                margin-bottom: 20px;
            }

            .persona-form-label {
                display: block;
                font-size: 14px;
                font-weight: 500;
                color: var(--character-primary-text, #2c2c36);
                margin-bottom: 8px;
            }

            .persona-form-input {
                width: 100%;
                padding: 12px 16px;
                border: 1px solid var(--line-secondary-border, #e0e2eb);
                border-radius: 12px;
                font-size: 14px;
                color: var(--character-primary-text, #2c2c36);
                background: var(--container-primary-fill, #fff);
                transition: all 0.15s ease;
                box-sizing: border-box;
            }

            .persona-form-input:focus {
                outline: none;
                border-color: var(--btn-brandprimary-fill, #615ced);
                box-shadow: 0 0 0 3px rgba(97, 92, 237, 0.1);
            }

            .persona-form-textarea {
                resize: vertical;
                min-height: 120px;
                font-family: inherit;
                line-height: 1.5;
            }

            .persona-form-select {
                appearance: none;
                background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238f91a8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
                background-repeat: no-repeat;
                background-position: right 12px center;
                padding-right: 40px;
            }

            .persona-form-hint {
                font-size: 12px;
                color: var(--character-tertiary-text, #8f91a8);
                margin-top: 6px;
            }

            .persona-modal-footer {
                display: flex;
                gap: 12px;
                padding: 20px 24px;
                border-top: 1px solid var(--line-secondary-border, #e0e2eb);
            }

            .persona-btn {
                flex: 1;
                padding: 12px 24px;
                border-radius: 999px;
                font-size: 14px;
                font-weight: 500;
                cursor: pointer;
                transition: all 0.15s ease;
                border: none;
            }

            .persona-btn-secondary {
                background: var(--container-secondary-fill, #f7f8fc);
                color: var(--character-primary-text, #2c2c36);
            }

            .persona-btn-secondary:hover {
                background: var(--container-tertiary-fill, #eef0f5);
            }

            .persona-btn-primary {
                background: var(--btn-brandprimary-fill, #615ced);
                color: white;
            }

            .persona-btn-primary:hover {
                background: #5248d9;
            }

            .persona-btn-primary:disabled {
                opacity: 0.5;
                cursor: not-allowed;
            }

            /* Dark mode adjustments */
            html.dark .persona-trigger {
                background: var(--container-secondary-fill, #2a2a2a);
            }

            html.dark .persona-trigger:hover {
                background: var(--container-tertiary-fill, #3a3a3a);
            }

            html.dark .persona-dropdown-menu {
                background: var(--container-primary-fill, #1f1f1f);
                border-color: var(--line-secondary-border, #424554);
            }

            html.dark .persona-modal {
                background: var(--container-primary-fill, #1f1f1f);
            }

            html.dark .persona-form-input {
                background: var(--container-secondary-fill, #2a2a2a);
                border-color: var(--line-secondary-border, #424554);
            }

            /* Animation */
            @keyframes persona-pulse {
                0%, 100% { opacity: 1; }
                50% { opacity: 0.7; }
            }

            .persona-indicator {
                width: 6px;
                height: 6px;
                border-radius: 50%;
                background: var(--btn-brandprimary-fill, #615ced);
                animation: persona-pulse 2s ease-in-out infinite;
            }

            /* Scrollbar styling */
            .persona-dropdown-list::-webkit-scrollbar {
                width: 6px;
            }

            .persona-dropdown-list::-webkit-scrollbar-track {
                background: transparent;
            }

            .persona-dropdown-list::-webkit-scrollbar-thumb {
                background: var(--line-secondary-border, #e0e2eb);
                border-radius: 3px;
            }

            .persona-dropdown-list::-webkit-scrollbar-thumb:hover {
                background: var(--character-quaternary-text, #c8cad9);
            }

            /* Checkbox group styling */
            .persona-form-checkbox-group {
                display: flex;
                gap: 24px;
                flex-wrap: wrap;
            }

            .persona-form-checkbox-item {
                display: flex;
                align-items: center;
                gap: 8px;
                cursor: pointer;
                user-select: none;
            }

            .persona-form-checkbox {
                width: 18px;
                height: 18px;
                border: 2px solid var(--line-secondary-border, #e0e2eb);
                border-radius: 4px;
                appearance: none;
                cursor: pointer;
                position: relative;
                transition: all 0.15s ease;
                background: var(--container-primary-fill, #fff);
            }

            .persona-form-checkbox:checked {
                background: var(--btn-brandprimary-fill, #615ced);
                border-color: var(--btn-brandprimary-fill, #615ced);
            }

            .persona-form-checkbox:checked::after {
                content: '';
                position: absolute;
                left: 5px;
                top: 2px;
                width: 4px;
                height: 8px;
                border: solid white;
                border-width: 0 2px 2px 0;
                transform: rotate(45deg);
            }

            .persona-form-checkbox:focus {
                outline: none;
                box-shadow: 0 0 0 3px rgba(97, 92, 237, 0.1);
            }

            .persona-form-checkbox-label {
                font-size: 14px;
                color: var(--character-primary-text, #2c2c36);
            }

            /* Feature badges in dropdown */
            .persona-dropdown-item-features {
                display: flex;
                gap: 4px;
                margin-top: 2px;
            }

            .persona-feature-badge {
                font-size: 10px;
                padding: 1px 6px;
                border-radius: 4px;
                background: var(--container-tertiary-fill, #eef0f5);
                color: var(--character-tertiary-text, #8f91a8);
            }

            .persona-feature-badge.active {
                background: var(--container-brandquinary-fill, #eeedff);
                color: var(--btn-brandprimary-fill, #615ced);
            }

            /* Adjust model selector margin */
            [class*="index-module__web-model-selector"] {
                margin-left: 0;
            }

            /* Hide native model selector when agent is active */
            body.persona-agent-active [class*="index-module__web-model-selector"] {
                display: none !important;
            }

            /* Hide the "设为默认/取消默认" button when agent is active */
            /* This might need adjustment based on new structure, but usually it's near the selector */
            body.persona-agent-active [class*="index-module__add-model-icon"] {
                display: none !important;
            }

            /* Adjust navbar layout when model selector is hidden */
            body.persona-agent-active .persona-dropdown-container {
                margin-left: 0;
                margin-right: 16px;
            }

            /* Emoji Picker Styles */
            .persona-emoji-wrapper {
                position: relative;
            }

            .persona-emoji-trigger {
                font-size: 24px;
                width: 48px;
                height: 48px;
                display: flex;
                align-items: center;
                justify-content: center;
                border: 1px solid var(--line-secondary-border, #e0e2eb);
                border-radius: 12px;
                cursor: pointer;
                background: var(--container-primary-fill, #fff);
                transition: all 0.15s ease;
            }

            .persona-emoji-trigger:hover {
                background: var(--container-secondary-fill, #f7f8fc);
                border-color: var(--btn-brandprimary-fill, #615ced);
            }

            .persona-emoji-picker-container {
                position: absolute;
                top: 100%;
                left: 0;
                z-index: 100;
                margin-top: 8px;
                box-shadow: 0 8px 24px rgba(0,0,0,0.15);
                border: 1px solid var(--line-secondary-border, #e0e2eb);
                border-radius: 12px;
                overflow: hidden;
                display: none;
                width: 380px;
                max-width: 90vw;
            }

            .persona-emoji-picker-container.visible {
                display: block;
            }

            emoji-picker {
                --background: var(--container-primary-fill, #fff);
                --border-color: var(--line-secondary-border, #e0e2eb);
                --input-border-color: var(--line-secondary-border, #e0e2eb);
                --input-font-color: var(--character-primary-text, #2c2c36);
                --button-hover-background: var(--container-secondary-fill, #f7f8fc);
                width: 100%;
                height: 320px;
            }

            html.dark emoji-picker {
                --background: #1f1f1f;
                --border-color: #424554;
                --input-border-color: #424554;
                --input-font-color: #e0e2eb;
                --button-hover-background: #2a2a2a;
            }
      `;
      document.head.appendChild(style);
    },

    createDropdownUI() {
      const container = document.createElement("div");
      container.className = "persona-dropdown-container";
      container.id = CONSTANTS.SELECTORS.CONTAINER;

      const trigger = document.createElement("div");
      trigger.className = `persona-trigger ${CONSTANTS.SELECTORS.TRIGGER_COLLAPSED}`;
      trigger.id = CONSTANTS.SELECTORS.TRIGGER;
      trigger.innerHTML = `
            <span class="persona-trigger-icon">🤖</span>
            <span class="persona-trigger-text">${I18n.t("noPersona")}</span>
            <span class="persona-trigger-arrow">
                <svg width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false"><use xlink:href="#icon-line-chevron-down"></use></svg>
            </span>
        `;

      trigger.onclick = (e) => {
        e.stopPropagation();
        UI.toggleDropdown();
      };

      trigger.addEventListener("mouseenter", () => {
        trigger.classList.remove(CONSTANTS.SELECTORS.TRIGGER_COLLAPSED);
      });

      trigger.addEventListener("mouseleave", () => {
        if (!State.selectedPersonaId && !State.dropdownVisible) {
          trigger.classList.add(CONSTANTS.SELECTORS.TRIGGER_COLLAPSED);
        }
      });

      container.appendChild(trigger);

      if (!document.getElementById(CONSTANTS.SELECTORS.MENU)) {
        const menu = document.createElement("div");
        menu.className = "persona-dropdown-menu";
        menu.id = CONSTANTS.SELECTORS.MENU;
        document.body.appendChild(menu);
      }

      return container;
    },

    renderDropdownMenu() {
      const menu = document.getElementById(CONSTANTS.SELECTORS.MENU);
      if (!menu) return;

      let html = `
            <div class="persona-dropdown-header">${I18n.t(
              "selectPersona"
            )}</div>
            <div class="persona-dropdown-list">
                <div class="persona-dropdown-item ${
                  !State.selectedPersonaId
                    ? CONSTANTS.SELECTORS.ITEM_SELECTED
                    : ""
                }" data-id="">
                    <div class="persona-dropdown-item-icon">🚫</div>
                    <div class="persona-dropdown-item-content">
                        <div class="persona-dropdown-item-name">${I18n.t(
                          "noPersona"
                        )}</div>
                        <div class="persona-dropdown-item-model">${I18n.t(
                          "useDefaultSettings"
                        )}</div>
                    </div>
                </div>
        `;

      State.personas.forEach((persona) => {
        let featureBadges = "";
        if (persona.deepThinking || persona.webSearch) {
          featureBadges = '<div class="persona-dropdown-item-features">';
          if (persona.deepThinking) {
            featureBadges += `<span class="persona-feature-badge active">${I18n.t(
              "deepThinking"
            )}</span>`;
          }
          if (persona.webSearch) {
            featureBadges += `<span class="persona-feature-badge active">${I18n.t(
              "webSearch"
            )}</span>`;
          }
          featureBadges += "</div>";
        }

        html += `
                <div class="persona-dropdown-item ${
                  persona.id === State.selectedPersonaId
                    ? CONSTANTS.SELECTORS.ITEM_SELECTED
                    : ""
                }" data-id="${persona.id}">
                    <div class="persona-dropdown-item-icon">${
                      persona.emoji || "🚫"
                    }</div>
                    <div class="persona-dropdown-item-content">
                        <div class="persona-dropdown-item-name">${Utils.escapeHtml(
                          persona.name
                        )}</div>
                        <div class="persona-dropdown-item-model">${Utils.escapeHtml(
                          persona.modelName ||
                            persona.model ||
                            I18n.t("defaultModel")
                        )}</div>
                        ${featureBadges}
                    </div>
                    <div class="persona-dropdown-item-actions">
                        <div class="persona-action-btn edit" data-action="edit" data-id="${
                          persona.id
                        }" title="${I18n.t("edit")}">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
                                <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
                            </svg>
                        </div>
                        <div class="persona-action-btn delete" data-action="delete" data-id="${
                          persona.id
                        }" title="${I18n.t("delete")}">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <polyline points="3 6 5 6 21 6"></polyline>
                                <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                                <line x1="10" y1="11" x2="10" y2="17"></line>
                                <line x1="14" y1="11" x2="14" y2="17"></line>
                            </svg>
                        </div>
                    </div>
                </div>
            `;
      });

      html += `
            </div>
            <div class="persona-dropdown-divider"></div>
            <div class="persona-dropdown-footer">
                <div class="persona-create-btn" id="${
                  CONSTANTS.SELECTORS.CREATE_BTN
                }">
                    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <line x1="12" y1="5" x2="12" y2="19"></line>
                        <line x1="5" y1="12" x2="19" y2="12"></line>
                    </svg>
                    <span>${I18n.t("createPersona")}</span>
                </div>
                <div class="persona-create-btn" id="${
                  CONSTANTS.SELECTORS.IMPORT_BTN
                }" style="margin-top: 4px;">
                    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                        <polyline points="7 10 12 15 17 10"></polyline>
                        <line x1="12" y1="15" x2="12" y2="3"></line>
                    </svg>
                    <span>${I18n.t("importFromUrl")}</span>
                </div>
            </div>
        `;

      menu.innerHTML = html;

      menu.querySelectorAll(".persona-dropdown-item").forEach((item) => {
        item.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          if (e.target.closest(".persona-action-btn")) return;
          const id = item.dataset.id;
          console.log("[QwenPersona] Item clicked, id:", id);
          PersonaManager.selectPersona(id || null);
        });
      });

      menu.querySelectorAll(".persona-action-btn").forEach((btn) => {
        btn.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          const action = btn.dataset.action;
          const id = btn.dataset.id;
          console.log("[QwenPersona] Action clicked:", action, id);
          if (action === "edit") {
            PersonaManager.editPersona(id);
          } else if (action === "delete") {
            PersonaManager.deletePersona(id);
          }
        });
      });

      const createBtn = document.getElementById(CONSTANTS.SELECTORS.CREATE_BTN);
      if (createBtn) {
        createBtn.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          console.log("[QwenPersona] Create button clicked");
          UI.openModal();
        });
      }

      const importBtn = document.getElementById(CONSTANTS.SELECTORS.IMPORT_BTN);
      if (importBtn) {
        importBtn.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          console.log("[QwenPersona] Import button clicked");
          const url = prompt(I18n.t("enterUrl"));
          if (url) {
            PersonaManager.importFromUrl(url);
          }
        });
      }
    },

    updateTriggerUI() {
      const trigger = document.getElementById(CONSTANTS.SELECTORS.TRIGGER);
      if (!trigger) return;

      const selectedPersona = State.personas.find(
        (p) => p.id === State.selectedPersonaId
      );
      const iconSpan = trigger.querySelector(".persona-trigger-icon");
      const textSpan = trigger.querySelector(".persona-trigger-text");

      if (selectedPersona) {
        trigger.classList.remove(CONSTANTS.SELECTORS.TRIGGER_COLLAPSED);
        trigger.classList.add(CONSTANTS.SELECTORS.TRIGGER_ACTIVE);
        iconSpan.textContent = selectedPersona.emoji || "🚫";
        textSpan.textContent = selectedPersona.name;
        if (selectedPersona.model) {
          document.body.classList.add(CONSTANTS.SELECTORS.AGENT_ACTIVE);
        } else {
          document.body.classList.remove(CONSTANTS.SELECTORS.AGENT_ACTIVE);
        }
      } else {
        trigger.classList.add(CONSTANTS.SELECTORS.TRIGGER_COLLAPSED);
        trigger.classList.remove(CONSTANTS.SELECTORS.TRIGGER_ACTIVE);
        iconSpan.textContent = "🤖";
        textSpan.textContent = I18n.t("noPersona");
        document.body.classList.remove(CONSTANTS.SELECTORS.AGENT_ACTIVE);
      }
    },

    createModalUI() {
      const overlay = document.createElement("div");
      overlay.className = "persona-modal-overlay";
      overlay.id = CONSTANTS.SELECTORS.MODAL_OVERLAY;
      overlay.onclick = (e) => {
        if (e.target === overlay) UI.closeModal();
      };

      const modal = document.createElement("div");
      modal.className = "persona-modal";
      modal.id = CONSTANTS.SELECTORS.MODAL;

      overlay.appendChild(modal);
      document.body.appendChild(overlay);
    },

    renderModal(persona = null) {
      const modal = document.getElementById(CONSTANTS.SELECTORS.MODAL);
      if (!modal) return;

      const isEdit = !!persona;
      const title = isEdit ? I18n.t("editPersona") : I18n.t("createNewPersona");

      let modelOptions = "";
      State.availableModels.forEach((m) => {
        const selected = persona && persona.model === m.id ? "selected" : "";
        modelOptions += `<option value="${m.id}" ${selected}>${
          m.name || m.id
        }</option>`;
      });

      modal.innerHTML = `
            <div class="persona-modal-header">
                <div class="persona-modal-title">${title}</div>
                <div class="persona-modal-close" onclick="window.personaManager.closeModal()">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <line x1="18" y1="6" x2="6" y2="18"></line>
                        <line x1="6" y1="6" x2="18" y2="18"></line>
                    </svg>
                </div>
            </div>
            <div class="persona-modal-body">
                <div class="persona-form-group">
                    <label class="persona-form-label">${I18n.t(
                      "personaName"
                    )}</label>
                    <input type="text" class="persona-form-input" id="${
                      CONSTANTS.SELECTORS.INPUT_NAME
                    }"
                           value="${
                             persona ? Utils.escapeHtml(persona.name) : ""
                           }"
                           placeholder="${I18n.t("namePlaceholder")}">
                </div>
                <div class="persona-form-group">
                    <label class="persona-form-label">${I18n.t("icon")}</label>
                    <div class="persona-emoji-wrapper">
                        <button class="persona-emoji-trigger" id="persona-emoji-trigger-btn">
                            ${persona ? persona.emoji : "🤖"}
                        </button>
                        <input type="hidden" id="${
                          CONSTANTS.SELECTORS.INPUT_EMOJI
                        }" value="${persona ? persona.emoji : "🤖"}">
                        <div class="persona-emoji-picker-container" id="persona-emoji-picker">
                            <emoji-picker></emoji-picker>
                        </div>
                    </div>
                </div>
                <div class="persona-form-group">
                    <label class="persona-form-label">${I18n.t("model")}</label>
                    <select class="persona-form-input persona-form-select" id="${
                      CONSTANTS.SELECTORS.INPUT_MODEL
                    }">
                        <option value="">${I18n.t("useCurrentModel")}</option>
                        ${modelOptions}
                    </select>
                    <div class="persona-form-hint">${I18n.t("modelHint")}</div>
                </div>
                <div class="persona-form-group">
                    <label class="persona-form-label">${I18n.t(
                      "systemPrompt"
                    )}</label>
                    <textarea class="persona-form-input persona-form-textarea" id="${
                      CONSTANTS.SELECTORS.INPUT_PROMPT
                    }"
                              placeholder="${I18n.t("promptPlaceholder")}">${
        persona ? Utils.escapeHtml(persona.prompt) : ""
      }</textarea>
                    <div class="persona-form-hint">${I18n.t("promptHint")}</div>
                </div>
                <div class="persona-form-group">
                    <label class="persona-form-label">${I18n.t(
                      "features"
                    )}</label>
                    <div class="persona-form-checkbox-group">
                        <label class="persona-form-checkbox-item">
                            <input type="checkbox" class="persona-form-checkbox" id="${
                              CONSTANTS.SELECTORS.INPUT_DEEP_THINKING
                            }"
                                   ${
                                     persona && persona.deepThinking
                                       ? "checked"
                                       : ""
                                   }>
                            <span class="persona-form-checkbox-label">${I18n.t(
                              "deepThinking"
                            )}</span>
                        </label>
                        <label class="persona-form-checkbox-item">
                            <input type="checkbox" class="persona-form-checkbox" id="${
                              CONSTANTS.SELECTORS.INPUT_WEB_SEARCH
                            }"
                                   ${
                                     persona && persona.webSearch
                                       ? "checked"
                                       : ""
                                   }>
                            <span class="persona-form-checkbox-label">${I18n.t(
                              "webSearch"
                            )}</span>
                        </label>
                    </div>
                    <div class="persona-form-hint">${I18n.t(
                      "featuresHint"
                    )}</div>
                </div>
            </div>
            <div class="persona-modal-footer">
                <button class="persona-btn persona-btn-secondary" onclick="window.personaManager.closeModal()">${I18n.t(
                  "cancel"
                )}</button>
                <button class="persona-btn persona-btn-primary" id="${
                  CONSTANTS.SELECTORS.SAVE_BTN
                }">${I18n.t("save")}</button>
            </div>
        `;

      // Emoji Picker Logic
      const emojiBtn = document.getElementById("persona-emoji-trigger-btn");
      const emojiInput = document.getElementById(
        CONSTANTS.SELECTORS.INPUT_EMOJI
      );
      const emojiPickerContainer = document.getElementById(
        "persona-emoji-picker"
      );
      const emojiPicker = emojiPickerContainer
        ? emojiPickerContainer.querySelector("emoji-picker")
        : null;

      if (emojiBtn && emojiPickerContainer) {
        emojiBtn.onclick = (e) => {
          e.stopPropagation();
          emojiPickerContainer.classList.toggle("visible");
        };
      }

      if (emojiPicker) {
        if (I18n.locale === "zh") {
          emojiPicker.dataSource =
            "https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/zh/cldr/data.json";
          emojiPicker.locale = "zh";
        }
        emojiPicker.addEventListener("emoji-click", (event) => {
          const emoji = event.detail.unicode;
          emojiBtn.textContent = emoji;
          emojiInput.value = emoji;
          emojiPickerContainer.classList.remove("visible");
        });
      }

      document.getElementById(CONSTANTS.SELECTORS.SAVE_BTN).onclick = () =>
        PersonaManager.savePersonaFromModal(persona?.id);
    },

    toggleDropdown() {
      State.dropdownVisible = !State.dropdownVisible;
      const trigger = document.getElementById(CONSTANTS.SELECTORS.TRIGGER);
      const menu = document.getElementById(CONSTANTS.SELECTORS.MENU);
      const arrow = document.querySelector(".persona-trigger-arrow");

      if (State.dropdownVisible) {
        trigger.classList.remove(CONSTANTS.SELECTORS.TRIGGER_COLLAPSED);
        UI.renderDropdownMenu();

        if (trigger && menu) {
          const rect = trigger.getBoundingClientRect();
          menu.style.top = rect.bottom + 8 + "px";
          menu.style.left = rect.left + "px";
          console.log("[QwenPersona] Dropdown position:", {
            top: menu.style.top,
            left: menu.style.left,
            rect,
          });
        } else {
          console.warn("[QwenPersona] Missing elements:", {
            trigger: !!trigger,
            menu: !!menu,
          });
        }

        menu.classList.add(CONSTANTS.SELECTORS.MENU_VISIBLE);
        if (arrow) arrow.classList.add(CONSTANTS.SELECTORS.ARROW_OPEN);
        console.log(
          "[QwenPersona] Dropdown opened, menu classes:",
          menu.className
        );
      } else {
        if (!State.selectedPersonaId) {
          trigger.classList.add(CONSTANTS.SELECTORS.TRIGGER_COLLAPSED);
        }
        menu.classList.remove(CONSTANTS.SELECTORS.MENU_VISIBLE);
        if (arrow) arrow.classList.remove(CONSTANTS.SELECTORS.ARROW_OPEN);
        console.log("[QwenPersona] Dropdown closed");
      }
    },

    closeDropdown() {
      State.dropdownVisible = false;
      const trigger = document.getElementById(CONSTANTS.SELECTORS.TRIGGER);
      const menu = document.getElementById(CONSTANTS.SELECTORS.MENU);
      const arrow = document.querySelector(".persona-trigger-arrow");

      if (trigger && !State.selectedPersonaId) {
        trigger.classList.add(CONSTANTS.SELECTORS.TRIGGER_COLLAPSED);
      }

      if (menu) menu.classList.remove(CONSTANTS.SELECTORS.MENU_VISIBLE);
      if (arrow) arrow.classList.remove(CONSTANTS.SELECTORS.ARROW_OPEN);
    },

    openModal(persona = null) {
      State.editingPersona = persona;
      UI.closeDropdown();
      UI.renderModal(persona);
      const overlay = document.getElementById(
        CONSTANTS.SELECTORS.MODAL_OVERLAY
      );
      if (overlay) {
        overlay.classList.add(CONSTANTS.SELECTORS.MENU_VISIBLE);
        State.modalVisible = true;
      }
    },

    closeModal() {
      const overlay = document.getElementById(
        CONSTANTS.SELECTORS.MODAL_OVERLAY
      );
      if (overlay) {
        overlay.classList.remove(CONSTANTS.SELECTORS.MENU_VISIBLE);
        State.modalVisible = false;
      }
      State.editingPersona = null;
    },

    setInteractionState(disabled) {
      const personaTrigger = document.getElementById(
        CONSTANTS.SELECTORS.TRIGGER
      );
      if (personaTrigger) {
        if (disabled) {
          personaTrigger.classList.add(CONSTANTS.SELECTORS.INPUT_DISABLED);
        } else {
          personaTrigger.classList.remove(CONSTANTS.SELECTORS.INPUT_DISABLED);
        }
      }

      const textareas = document.querySelectorAll(CONSTANTS.SELECTORS.TEXTAREA);
      textareas.forEach((t) => {
        const container =
          t.closest(CONSTANTS.SELECTORS.INPUT_CONTAINER) || t.parentElement;

        if (container) {
          container.classList.add(CONSTANTS.SELECTORS.TRANSITION);
        }

        if (disabled) {
          if (!t.disabled) {
            t.dataset.originalPlaceholder = t.placeholder || "";
            t.placeholder = "正在切换 Persona...";
            t.disabled = true;
            t.classList.add(CONSTANTS.SELECTORS.INTERACTION_DISABLED);

            if (container) {
              container.classList.add(CONSTANTS.SELECTORS.INPUT_DISABLED);
            }
          }
        } else {
          if (
            t.disabled &&
            t.classList.contains(CONSTANTS.SELECTORS.INTERACTION_DISABLED)
          ) {
            t.disabled = false;
            t.placeholder = t.dataset.originalPlaceholder || "";
            t.classList.remove(CONSTANTS.SELECTORS.INTERACTION_DISABLED);

            if (container) {
              container.classList.remove(CONSTANTS.SELECTORS.INPUT_DISABLED);
            }
          }
        }
      });
    },

    waitForNavbar() {
      const maxAttempts = 50;
      let attempts = 0;

      const checkNavbar = () => {
        const headerLeft = document.querySelector(
          CONSTANTS.SELECTORS.HEADER_LEFT
        );

        if (
          headerLeft &&
          !document.getElementById(CONSTANTS.SELECTORS.CONTAINER)
        ) {
          const modelSelector = headerLeft.querySelector(
            CONSTANTS.SELECTORS.MODEL_SELECTOR
          );

          const container = UI.createDropdownUI();

          if (modelSelector) {
            headerLeft.insertBefore(container, modelSelector);
          } else {
            headerLeft.appendChild(container);
          }

          UI.updateTriggerUI();
          console.log("[QwenPersona] UI injected");

          PersonaManager.autoSelectPersonaForChat();
          return;
        }

        attempts++;
        if (attempts < maxAttempts) {
          setTimeout(checkNavbar, 200);
        } else {
          console.warn(
            "[QwenPersona] Failed to find navbar after",
            maxAttempts,
            "attempts"
          );
        }
      };

      checkNavbar();
    },
  };

  // ==================== Model Service ====================
  const ModelManager = {
    async fetchModels() {
      try {
        const response = await fetch("/api/models", {
          method: "GET",
          headers: {
            Accept: "application/json",
            source: "web",
          },
        });
        if (response.ok) {
          const data = await response.json();
          State.availableModels = data.data || data || [];
          localStorage.setItem(
            CONSTANTS.STORAGE.MODELS_CACHE,
            JSON.stringify(State.availableModels)
          );
          console.log(
            "[QwenPersona] Models loaded:",
            State.availableModels.length
          );
        }
      } catch (e) {
        console.error("[QwenPersona] Failed to fetch models:", e);
        try {
          const cached = localStorage.getItem(CONSTANTS.STORAGE.MODELS_CACHE);
          if (cached) State.availableModels = JSON.parse(cached);
        } catch (err) {}
      }
    },

    async simulateModelSelection(modelId) {
      if (!modelId) return false;

      console.log("[QwenPersona] Attempting to select model via UI:", modelId);

      const hideStyle = document.createElement("style");
      hideStyle.id = "persona-hide-model-selector";
      hideStyle.textContent = `
            ${CONSTANTS.SELECTORS.MODEL_SELECTOR_DROPDOWN},
            .ant-dropdown,
            .ant-select-dropdown {
                opacity: 0 !important;
                pointer-events: none !important;
                visibility: hidden !important;
                display: none !important;
                transition: none !important;
                animation: none !important;
            }
        `;
      document.head.appendChild(hideStyle);

      try {
        const modelTriggerContent = document.querySelector(
          CONSTANTS.SELECTORS.MODEL_SELECTOR_CONTENT
        );

        if (!modelTriggerContent) {
          console.warn(
            "[QwenPersona] Model selector trigger content not found"
          );
          return false;
        }

        const modelTrigger = modelTriggerContent.closest(
          CONSTANTS.SELECTORS.ANT_DROPDOWN_TRIGGER
        );

        if (!modelTrigger) {
          console.warn("[QwenPersona] Model selector trigger not found");
          return false;
        }

        modelTrigger.click();
        console.log("[QwenPersona] Clicked model selector trigger");

        const menuSelector = CONSTANTS.SELECTORS.MODEL_SELECTOR_DROPDOWN;
        let menu = await Utils.waitForElement(menuSelector, 2000);
        if (!menu) {
          console.warn("[QwenPersona] Model selector menu not found");
          return false;
        }

        let modelButton = ModelManager.findModelButton(menu, modelId);

        if (!modelButton) {
          const expandBtn = menu.querySelector(
            CONSTANTS.SELECTORS.MODEL_SELECTOR_VIEW_MORE
          );
          console.log("[QwenPersona] Expand button:", expandBtn);

          if (expandBtn) {
            const textEl = expandBtn.querySelector(
              CONSTANTS.SELECTORS.MODEL_VIEW_MORE_TEXT
            );
            const text = textEl ? textEl.textContent : "";

            const isExpanded = text.includes("折叠");
            console.log("[QwenPersona] Menu expanded state:", isExpanded);

            if (!isExpanded) {
              console.log("[QwenPersona] Clicking expand button...");
              expandBtn.click();
              await Utils.sleep(400);

              menu = document.querySelector(menuSelector);
              if (menu) {
                modelButton = ModelManager.findModelButton(menu, modelId);
              }
            }
          } else {
            console.log("[QwenPersona] No expand button found in menu");
          }
        }

        if (modelButton) {
          const itemContainer = modelButton.closest(
            CONSTANTS.SELECTORS.MODEL_SELECTOR_ITEM
          );

          if (
            itemContainer &&
            Array.from(itemContainer.classList).some((c) =>
              c.includes("index-module__model-selector-item-selected")
            )
          ) {
            console.log("[QwenPersona] Model already selected:", modelId);
            ModelManager.closeModelSelector();
            return true;
          }

          modelButton.click();
          console.log("[QwenPersona] Clicked model button:", modelId);
          return true;
        } else {
          console.warn("[QwenPersona] Model button not found for:", modelId);
          ModelManager.closeModelSelector();
          return false;
        }
      } catch (e) {
        console.error("[QwenPersona] Error selecting model:", e);
        return false;
      } finally {
        await Utils.sleep(50);
        const style = document.getElementById("persona-hide-model-selector");
        if (style) style.remove();
      }
    },

    findModelButton(menu, modelId) {
      const modelItems = menu.querySelectorAll(
        CONSTANTS.SELECTORS.MODEL_SELECTOR_ITEM
      );

      console.log(
        "[QwenPersona] Looking for model:",
        modelId,
        "in",
        modelItems.length,
        "items"
      );

      const normalizedId = modelId.toLowerCase().replace(/[-_]/g, "");

      for (const item of modelItems) {
        const nameEl = item.querySelector(CONSTANTS.SELECTORS.MODEL_NAME_TEXT);
        if (nameEl) {
          const modelName = nameEl.textContent.trim();
          const normalizedName = modelName.toLowerCase().replace(/[-_]/g, "");

          if (normalizedName === normalizedId) {
            console.log("[QwenPersona] Found exact match:", modelName);
            return item;
          }

          if (
            normalizedId.includes(normalizedName) ||
            normalizedName.includes(normalizedId)
          ) {
            console.log("[QwenPersona] Found partial match:", modelName);
            return item;
          }
        }
      }

      for (const item of modelItems) {
        const nameEl = item.querySelector(CONSTANTS.SELECTORS.MODEL_NAME_TEXT);
        if (nameEl) {
          const modelName = nameEl.textContent.trim();
          const mainPart = modelId
            .split("-")
            .slice(0, 2)
            .join("-")
            .toLowerCase();
          const namePart = modelName
            .split("-")
            .slice(0, 2)
            .join("-")
            .toLowerCase();
          if (mainPart === namePart) {
            console.log("[QwenPersona] Found loose match:", modelName);
            return item;
          }
        }
      }

      console.log("[QwenPersona] No match found for:", modelId);
      return null;
    },

    closeModelSelector() {
      document.dispatchEvent(
        new KeyboardEvent("keydown", {
          key: "Escape",
          code: "Escape",
          keyCode: 27,
          which: 27,
          bubbles: true,
        })
      );

      setTimeout(() => {
        const backdrop = document
          .querySelector(".selector-modal-list")
          ?.closest(".fixed");
        if (backdrop) {
          const event = new MouseEvent("click", {
            bubbles: true,
            cancelable: true,
          });
          document.body.dispatchEvent(event);
        }
      }, 50);
    },
  };

  // ==================== Feature Service ====================
  const FeatureManager = {
    findDeepThinkingButton() {
      const buttons = document.querySelectorAll(
        CONSTANTS.SELECTORS.CHAT_INPUT_FEATURE_BTN
      );
      for (const btn of buttons) {
        const use = btn.querySelector("use");
        if (
          use &&
          use.getAttribute("xlink:href")?.includes("icon-line-deepthink-01")
        ) {
          return btn;
        }
        if (btn.querySelector(".icon-line-deepthink-01")) {
          return btn;
        }
      }

      for (const btn of buttons) {
        const textEl = btn.querySelector(
          CONSTANTS.SELECTORS.CHAT_INPUT_FEATURE_TEXT
        );
        if (textEl && textEl.textContent.includes("深度思考")) {
          return btn;
        }
      }

      return null;
    },

    findWebSearchButton() {
      const btn = document.querySelector(CONSTANTS.SELECTORS.WEB_SEARCH_BTN);
      if (btn) return btn;

      const buttons = document.querySelectorAll(
        CONSTANTS.SELECTORS.CHAT_INPUT_FEATURE_BTN
      );
      for (const b of buttons) {
        const use = b.querySelector("use");
        if (
          use &&
          use.getAttribute("xlink:href")?.includes("icon-line-globe-01")
        ) {
          return b;
        }
        if (b.querySelector(".icon-line-globe-01")) {
          return b;
        }
      }

      for (const b of buttons) {
        const textEl = b.querySelector(
          CONSTANTS.SELECTORS.CHAT_INPUT_FEATURE_TEXT
        );
        if (textEl && textEl.textContent.includes("搜索")) {
          return b;
        }
      }

      return null;
    },

    isFeatureButtonActive(button) {
      if (!button) return false;

      if (button.classList.contains("active")) return true;
      if (button.getAttribute("aria-pressed") === "true") return true;
      if (button.dataset.state === "active" || button.dataset.state === "on")
        return true;

      const icon = button.querySelector('i[class*="icon-"]');
      if (icon && icon.classList.contains("icon-fill-deepthink-01"))
        return true;
      if (icon && icon.classList.contains("icon-fill-globe-01")) return true;

      const style = window.getComputedStyle(button);
      const bgColor = style.backgroundColor;
      if (
        bgColor &&
        bgColor !== "rgba(0, 0, 0, 0)" &&
        bgColor !== "transparent"
      ) {
        // Active
      }

      return false;
    },

    async simulateDeepThinking(enabled) {
      console.log("[QwenPersona] Setting deep thinking to:", enabled);

      const button = FeatureManager.findDeepThinkingButton();
      if (!button) {
        console.warn("[QwenPersona] Deep thinking button not found");
        return false;
      }

      const currentlyActive = FeatureManager.isFeatureButtonActive(button);
      console.log(
        "[QwenPersona] Deep thinking currently active:",
        currentlyActive
      );

      if (currentlyActive !== enabled) {
        button.click();
        console.log("[QwenPersona] Clicked deep thinking button");
        await Utils.sleep(100);
        return true;
      } else {
        console.log("[QwenPersona] Deep thinking already in desired state");
        return true;
      }
    },

    async simulateWebSearch(enabled) {
      console.log("[QwenPersona] Setting web search to:", enabled);

      const button = FeatureManager.findWebSearchButton();
      if (!button) {
        console.warn("[QwenPersona] Web search button not found");
        return false;
      }

      const currentlyActive = FeatureManager.isFeatureButtonActive(button);
      console.log(
        "[QwenPersona] Web search currently active:",
        currentlyActive
      );

      if (currentlyActive !== enabled) {
        button.click();
        console.log("[QwenPersona] Clicked web search button");
        await Utils.sleep(100);
        return true;
      } else {
        console.log("[QwenPersona] Web search already in desired state");
        return true;
      }
    },

    async applyFeatureSettings(persona) {
      if (!persona) return;

      await Utils.sleep(200);

      const deepThinkingEnabled = persona.deepThinking === true;
      await FeatureManager.simulateDeepThinking(deepThinkingEnabled);

      const webSearchEnabled = persona.webSearch === true;
      await FeatureManager.simulateWebSearch(webSearchEnabled);
    },
  };

  // ==================== Chat Service ====================
  const ChatManager = {
    getCurrentChatId(url = location.pathname) {
      const match = url.match(/\/(?:c|chat)\/([a-zA-Z0-9-]+)/);
      return match ? match[1] : null;
    },

    setPersonaForCurrentChat(personaId) {
      const chatId = ChatManager.getCurrentChatId();
      if (chatId) {
        if (personaId) {
          State.chatPersonaMap[chatId] = personaId;
        } else {
          delete State.chatPersonaMap[chatId];
        }
        Storage.saveChatPersonaMap();
        console.log(
          "[QwenPersona] Mapped chat",
          chatId,
          "to persona",
          personaId
        );
      }
    },

    getPersonaForCurrentChat() {
      const chatId = ChatManager.getCurrentChatId();
      if (chatId && State.chatPersonaMap[chatId]) {
        return State.chatPersonaMap[chatId];
      }
      return null;
    },

    startUrlMonitor() {
      State.lastUrl = location.href;

      window.addEventListener("popstate", ChatManager.handleUrlChange);

      const originalPushState = history.pushState;
      const originalReplaceState = history.replaceState;

      history.pushState = function (...args) {
        originalPushState.apply(this, args);
        ChatManager.handleUrlChange();
      };

      history.replaceState = function (...args) {
        originalReplaceState.apply(this, args);
        ChatManager.handleUrlChange();
      };

      const navbarObserver = new MutationObserver(() => {
        if (!document.getElementById(CONSTANTS.SELECTORS.CONTAINER)) {
          console.log("[QwenPersona] Dropdown removed, re-injecting...");
          UI.waitForNavbar();
        }
      });

      const startNavbarObserver = () => {
        const navbar = document.querySelector(
          CONSTANTS.SELECTORS.HEADER_DESKTOP
        );
        if (navbar) {
          navbarObserver.observe(navbar, { childList: true, subtree: true });
          console.log("[QwenPersona] Navbar observer started");
        } else {
          setTimeout(startNavbarObserver, 500);
        }
      };
      startNavbarObserver();

      console.log("[QwenPersona] URL monitor started");
    },

    handleUrlChange() {
      const currentUrl = location.href;
      if (currentUrl === State.lastUrl) return;

      console.log(
        "[QwenPersona] URL changed:",
        State.lastUrl,
        "->",
        currentUrl
      );

      const prevChatId = ChatManager.getCurrentChatId(State.lastUrl);
      const currChatId = ChatManager.getCurrentChatId(currentUrl);

      if (
        !prevChatId &&
        currChatId &&
        State.selectedPersonaId &&
        !State.chatPersonaMap[currChatId]
      ) {
        console.log(
          "[QwenPersona] New chat detected, mapping current persona:",
          State.selectedPersonaId
        );
        State.chatPersonaMap[currChatId] = State.selectedPersonaId;
        Storage.saveChatPersonaMap();
      }

      State.lastUrl = currentUrl;

      setTimeout(() => {
        if (!document.getElementById(CONSTANTS.SELECTORS.CONTAINER)) {
          console.log("[QwenPersona] Re-injecting UI after navigation");
          UI.waitForNavbar();
        }

        PersonaManager.autoSelectPersonaForChat();
      }, 300);
    },
  };

  // ==================== Persona Manager ====================
  const PersonaManager = {
    selectPersona(id) {
      State.selectedPersonaId = id || null;
      Storage.saveSelectedPersona();
      UI.updateTriggerUI();
      UI.closeDropdown();
      console.log("[QwenPersona] Selected:", id || "None");

      ChatManager.setPersonaForCurrentChat(id);

      setTimeout(async () => {
        UI.setInteractionState(true);
        try {
          if (id) {
            const persona = State.personas.find((p) => p.id === id);
            if (persona) {
              if (persona.model || persona.modelName) {
                const modelToSelect = persona.modelName || persona.model;
                await ModelManager.simulateModelSelection(modelToSelect);
              }

              await FeatureManager.applyFeatureSettings(persona);
            }
          } else {
            await FeatureManager.applyFeatureSettings({
              deepThinking: false,
              webSearch: false,
            });
          }
        } finally {
          UI.setInteractionState(false);
        }
      }, 100);
    },

    editPersona(id) {
      const persona = State.personas.find((p) => p.id === id);
      if (persona) {
        UI.openModal(persona);
      }
    },

    deletePersona(id) {
      if (!confirm(I18n.t("deleteConfirm"))) return;

      State.personas = State.personas.filter((p) => p.id !== id);
      Storage.savePersonas();

      if (State.selectedPersonaId === id) {
        State.selectedPersonaId = null;
        Storage.saveSelectedPersona();
        UI.updateTriggerUI();
      }

      UI.renderDropdownMenu();
      console.log("[QwenPersona] Deleted:", id);
    },

    savePersonaFromModal(existingId = null) {
      const name = document
        .getElementById(CONSTANTS.SELECTORS.INPUT_NAME)
        .value.trim();
      const emoji = document.getElementById(
        CONSTANTS.SELECTORS.INPUT_EMOJI
      ).value;
      const model = document.getElementById(
        CONSTANTS.SELECTORS.INPUT_MODEL
      ).value;
      const prompt = document
        .getElementById(CONSTANTS.SELECTORS.INPUT_PROMPT)
        .value.trim();
      const deepThinking = document.getElementById(
        CONSTANTS.SELECTORS.INPUT_DEEP_THINKING
      ).checked;
      const webSearch = document.getElementById(
        CONSTANTS.SELECTORS.INPUT_WEB_SEARCH
      ).checked;

      if (!name) {
        alert("请输入 Persona 名称");
        return;
      }

      const modelObj = State.availableModels.find((m) => m.id === model);
      const modelName = modelObj ? modelObj.name : "";

      if (existingId) {
        const idx = State.personas.findIndex((p) => p.id === existingId);
        if (idx !== -1) {
          State.personas[idx] = {
            ...State.personas[idx],
            name,
            emoji,
            model,
            modelName,
            prompt,
            deepThinking,
            webSearch,
          };
        }
      } else {
        const newPersona = {
          id: "persona_" + Date.now(),
          name,
          emoji,
          model,
          modelName,
          prompt,
          deepThinking,
          webSearch,
          createdAt: Date.now(),
        };
        State.personas.push(newPersona);
      }

      Storage.savePersonas();
      UI.updateTriggerUI();
      UI.closeModal();
      console.log("[QwenPersona] Saved persona:", name);
    },

    async importFromUrl(url) {
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error("Network response was not ok");
        const data = await response.json();

        if (!Array.isArray(data))
          throw new Error("Invalid JSON format: expected an array");

        // Basic validation
        const isValid = data.every((p) => p.id && p.name);
        if (!isValid) throw new Error("Invalid persona data");

        State.personas = data;
        Storage.savePersonas();

        // Reset selection if current selection is not in new list
        if (
          State.selectedPersonaId &&
          !State.personas.find((p) => p.id === State.selectedPersonaId)
        ) {
          State.selectedPersonaId = null;
          Storage.saveSelectedPersona();
          UI.updateTriggerUI();
        }

        UI.renderDropdownMenu();
        alert(I18n.t("importSuccess"));
      } catch (e) {
        console.error("[QwenPersona] Import failed:", e);
        alert(I18n.t("importError") + "\n" + e.message);
      }
    },

    autoSelectPersonaForChat() {
      const chatId = ChatManager.getCurrentChatId();

      if (!chatId) {
        if (State.selectedPersonaId) {
          console.log("[QwenPersona] No chat ID (Home/New), resetting to None");
          PersonaManager.selectPersona(null);
        }
        return;
      }

      const recordedPersonaId = State.chatPersonaMap[chatId];

      if (recordedPersonaId) {
        const personaExists = State.personas.find(
          (p) => p.id === recordedPersonaId
        );
        if (personaExists) {
          if (recordedPersonaId !== State.selectedPersonaId) {
            console.log(
              "[QwenPersona] Auto-selecting persona for chat:",
              chatId,
              "->",
              recordedPersonaId
            );
            PersonaManager.selectPersona(recordedPersonaId);
          }
        } else {
          if (State.selectedPersonaId) PersonaManager.selectPersona(null);
        }
      } else {
        if (State.selectedPersonaId) {
          console.log(
            "[QwenPersona] No mapping for this chat, resetting to None"
          );
          PersonaManager.selectPersona(null);
        }
      }
    },
  };

  // ==================== Network Service ====================
  const NetworkManager = {
    interceptFetch() {
      const originalFetch = window.fetch;

      window.fetch = async function (url, options = {}) {
        if (
          typeof url === "string" &&
          url.includes("/api/v2/chat/completions")
        ) {
          const persona = State.personas.find(
            (p) => p.id === State.selectedPersonaId
          );

          if (persona && options.body) {
            try {
              let body = JSON.parse(options.body);
              let modified = false;

              // Detect if this is an edit action (user editing a previous message)
              const isEditAction =
                Array.isArray(body.messages) &&
                body.messages.some((m) => m.user_action === "edit");

              // Detect if this is truly a new chat (no existing conversation context)
              const isNewChat =
                (!body.conversation_id &&
                  !body.conversationId &&
                  !body.chat_id) ||
                (!body.parent_id &&
                  !body.parentId &&
                  body.messages &&
                  body.messages.length === 1 &&
                  !isEditAction);

              const hasSystemMessage =
                Array.isArray(body.messages) &&
                body.messages.some((m) => m.role === "system");

              // Check if chat_id exists in URL (indicates existing conversation)
              const urlChatIdMatch = url.match(/chat_id=([a-zA-Z0-9-]+)/);
              const urlHasChatId = !!urlChatIdMatch;

              console.log("[QwenPersona] Debug - Request Check:", {
                url,
                isNewChat,
                isEditAction,
                hasSystemMessage,
                urlHasChatId,
                chat_id: body.chat_id,
                parent_id: body.parent_id || body.parentId,
                messageCount: body.messages ? body.messages.length : 0,
                personaPrompt: !!persona.prompt,
              });

              if (persona.prompt && Array.isArray(body.messages)) {
                const systemMsgIndex = body.messages.findIndex(
                  (m) => m.role === "system"
                );

                // For edit actions: the server does NOT retain the system message
                // We need to inject the system prompt into the user message instead
                // because the API only allows one system message at the conversation root
                if (isEditAction && urlHasChatId) {
                  // Remove any existing system message first (to avoid duplicates)
                  if (systemMsgIndex !== -1) {
                    console.log(
                      "[QwenPersona] Debug - Removing existing System Message for edit action"
                    );
                    body.messages.splice(systemMsgIndex, 1);
                  }
                  // Prepend system prompt to user message content
                  const userMsgIndex = body.messages.findIndex(
                    (m) => m.role === "user"
                  );
                  if (userMsgIndex !== -1) {
                    const userMsg = body.messages[userMsgIndex];
                    if (!userMsg.content.startsWith(persona.prompt)) {
                      console.log(
                        "[QwenPersona] Debug - Prepending System Prompt to User Message (Edit Action)"
                      );
                      userMsg.content = `[System Instruction]\n${persona.prompt}\n\n[User Message]\n${userMsg.content}`;
                      modified = true;
                    }
                  }
                } else if (systemMsgIndex !== -1) {
                  console.log(
                    "[QwenPersona] Debug - Updating existing System Prompt"
                  );
                  body.messages[systemMsgIndex].content = persona.prompt;

                  // Ensure it is the first message
                  if (systemMsgIndex !== 0) {
                    const [msg] = body.messages.splice(systemMsgIndex, 1);
                    body.messages.unshift(msg);
                  }
                  modified = true;
                } else {
                  // No system message found
                  // Check if this is the start of a conversation (no parent_id)
                  // If parent_id exists, it's a continuation, and we CANNOT inject a system message (server restriction)
                  const isStartOfConversation =
                    !body.parent_id && !body.parentId && !isEditAction;

                  if (isStartOfConversation && !urlHasChatId) {
                    console.log(
                      "[QwenPersona] Debug - Injecting System Prompt (New Chat/Root)"
                    );
                    body.messages.unshift({
                      role: "system",
                      content: persona.prompt,
                    });
                    modified = true;
                  } else {
                    console.log(
                      "[QwenPersona] Debug - Prepending System Prompt to User Message (Continuation)"
                    );
                    const lastMsg = body.messages[body.messages.length - 1];
                    if (
                      lastMsg &&
                      lastMsg.role === "user" &&
                      !lastMsg.content.startsWith(persona.prompt) &&
                      !lastMsg.content.startsWith("[System Instruction]")
                    ) {
                      lastMsg.content = `[System Instruction]\n${persona.prompt}\n\n[User Message]\n${lastMsg.content}`;
                      modified = true;
                    }
                  }
                }
              }

              if (persona.model) {
                body.model = persona.model;
                modified = true;
              }

              if (modified) {
                options.body = JSON.stringify(body);
                console.log(
                  "[QwenPersona] Request modified with persona:",
                  persona.name
                );
              }
            } catch (e) {
              console.error("[QwenPersona] Failed to modify request:", e);
            }
          }
        }

        return originalFetch.call(this, url, options);
      };

      console.log("[QwenPersona] Fetch intercepted");
    },
  };

  // ==================== Initialization ====================
  function init() {
    console.log("[QwenPersona] Initializing...");

    I18n.init();
    Storage.loadPersonas();
    Storage.loadSelectedPersona();
    Storage.loadChatPersonaMap();
    ModelManager.fetchModels();

    UI.injectStyles();
    UI.createModalUI();

    // Load emoji-picker-element
    const emojiScript = document.createElement("script");
    emojiScript.type = "module";
    emojiScript.src =
      "https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js";
    document.head.appendChild(emojiScript);

    NetworkManager.interceptFetch();

    UI.waitForNavbar();

    ChatManager.startUrlMonitor();

    window.personaManager = {
      closeModal: UI.closeModal,
      openModal: UI.openModal,
      refresh: () => {
        Storage.loadPersonas();
        Storage.loadSelectedPersona();
        UI.updateTriggerUI();
      },
    };

    document.addEventListener("click", (e) => {
      const container = document.getElementById(CONSTANTS.SELECTORS.CONTAINER);
      const menu = document.getElementById(CONSTANTS.SELECTORS.MENU);
      const isClickInsideContainer = container && container.contains(e.target);
      const isClickInsideMenu = menu && menu.contains(e.target);

      if (!isClickInsideContainer && !isClickInsideMenu) {
        UI.closeDropdown();
      }

      // Close emoji picker when clicking outside
      const emojiPickerContainer = document.getElementById(
        "persona-emoji-picker"
      );
      const emojiBtn = document.getElementById("persona-emoji-trigger-btn");
      if (emojiPickerContainer && emojiBtn) {
        if (
          !emojiPickerContainer.contains(e.target) &&
          !emojiBtn.contains(e.target)
        ) {
          emojiPickerContainer.classList.remove("visible");
        }
      }
    });
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }
})();