/**
 * ============================================================================
 * MyClaw UI Inject — 浏览器端注入脚本
 * ============================================================================
 *
 * 功能：
 *   1. 页面顶部 fixed 显示 myclaw 版本号
 *   2. 在聊天工具栏新增讯飞语音输入按钮
 *      - 光标处插入文字
 *      - 持续录入，手动停止
 *      - 讯飞 60 秒断开后自动重连
 *
 * 依赖：
 *   - voice-input.js（讯飞 VoiceInput SDK）
 *   - myclaw-tts.js（TTS 按钮绑定，由 myclaw-tts.js 提供）
 * ============================================================================
 */
(function () {
  "use strict";

  var MYCLAW_VERSION = "__MYCLAW_VERSION__";

  // ═══ 状态 ═══
  var voice = null;           // VoiceInput 实例
  var recording = false;      // 用户层面的录音状态（独立于 SDK 的 status）
  var pendingText = "";       // 当前这轮识别的文字（实时更新）
  var committedText = "";     // 已经提交到 textarea 的文字（上一轮累积）
  var cursorOffset = 0;       // 录音开始时光标在 textarea 中的位置
  var injected = false;
  var stopping = false;      // 正在等待最终识别结果（stopVoice 的 2 秒窗口）

  // ═══ 1. 右下角版本标签（点击测试麦克风） ═══
  function createVersionBar() {
    if (document.querySelector("#myclaw-version-bar")) return;

    var bar = document.createElement("div");
    bar.id = "myclaw-version-bar";
    bar.style.cssText = [
      "position: fixed",
      "bottom: 8px",
      "right: 40px",
      "padding: 2px 8px",
      "background: none",
      "color: rgba(150, 150, 150, 0.4)",
      "font-size: 10px",
      "font-family: monospace",
      "z-index: 99999",
      "user-select: none",
      "cursor: pointer",
      "transition: color 0.2s",
    ].join(";");
    bar.textContent = "\uD83D\uDC3E v" + MYCLAW_VERSION;
    bar.title = "\u70B9\u51FB\u6D4B\u8BD5\u9EA6\u514B\u98CE";

    bar.onmouseenter = function () { bar.style.color = "rgba(150, 150, 150, 0.8)"; };
    bar.onmouseleave = function () { bar.style.color = "rgba(150, 150, 150, 0.4)"; };
    bar.onclick = function (e) {
      e.stopPropagation();
      testMicrophone();
    };

    document.body.appendChild(bar);
  }

  // ═══ 1.1 右下角文档按钮 ═══
  function createDocButton() {
    if (document.querySelector("#myclaw-doc-btn")) return;

    var btn = document.createElement("div");
    btn.id = "myclaw-doc-btn";
    btn.style.cssText = [
      "position: fixed",
      "bottom: 8px",
      "right: 120px",
      "padding: 2px 8px",
      "background: none",
      "color: rgba(150, 150, 150, 0.4)",
      "font-size: 10px",
      "font-family: monospace",
      "z-index: 99999",
      "user-select: none",
      "cursor: pointer",
      "transition: color 0.2s",
    ].join(";");
    btn.textContent = "\uD83D\uDCD6 \u6587\u6863";
    btn.title = "\u6253\u5F00\u5E2E\u52A9\u6587\u6863";

    btn.onmouseenter = function () { btn.style.color = "rgba(150, 150, 150, 0.8)"; };
    btn.onmouseleave = function () { btn.style.color = "rgba(150, 150, 150, 0.4)"; };
    btn.onclick = function (e) {
      e.stopPropagation();
      window.open("https://www.yiranlaoshi.com/blog-posts/2026-04-08-10-00-00_%E7%AC%AC1%E8%AF%BE%E6%99%BA%E8%83%BD%E4%BD%93%E5%85%A5%E9%97%A8%E9%97%AF%E5%85%B3%E5%8D%A1/index.html", "_blank");
    };

    document.body.appendChild(btn);
  }

  // ═══ 1.2 右下角 CMD 按钮（弹框 iframe） ═══
  var cmdOpen = false;

  function createCmdButton() {
    if (document.querySelector("#myclaw-cmd-btn")) return;

    var btn = document.createElement("div");
    btn.id = "myclaw-cmd-btn";
    btn.style.cssText = [
      "position: fixed",
      "bottom: 8px",
      "right: 190px",
      "padding: 3px 10px",
      "background: rgba(59, 130, 246, 0.85)",
      "color: #fff",
      "font-size: 11px",
      "font-weight: bold",
      "font-family: monospace",
      "border-radius: 4px",
      "z-index: 99999",
      "user-select: none",
      "cursor: pointer",
      "transition: all 0.2s",
      "letter-spacing: 0.5px",
    ].join(";");
    btn.textContent = "\u2328 CMD";
    btn.title = "\u6253\u5F00\u547D\u4EE4\u884C";

    btn.onmouseenter = function () { btn.style.background = "rgba(37, 99, 235, 1)"; btn.style.transform = "scale(1.05)"; };
    btn.onmouseleave = function () { if (!cmdOpen) { btn.style.background = "rgba(59, 130, 246, 0.85)"; } btn.style.transform = "scale(1)"; };
    btn.onclick = function (e) {
      e.stopPropagation();
      if (cmdOpen) {
        closeCmdModal();
      } else {
        openCmdModal();
      }
    };

    document.body.appendChild(btn);
  }

  function openCmdModal() {
    if (document.querySelector("#myclaw-cmd-modal")) return;
    cmdOpen = true;

    // 按钮 active 态
    var btn = document.querySelector("#myclaw-cmd-btn");
    if (btn) btn.style.background = "rgba(37, 99, 235, 1)";

    // 遮罩
    var overlay = document.createElement("div");
    overlay.id = "myclaw-cmd-modal";
    overlay.style.cssText = [
      "position: fixed",
      "top: 0",
      "left: 0",
      "width: 100vw",
      "height: 100vh",
      "background: rgba(0, 0, 0, 0.3)",
      "z-index: 99998",
      "display: flex",
      "align-items: center",
      "justify-content: center",
      "animation: myclaw-fade-in 0.15s ease",
    ].join(";");

    // 弹框容器 — 尽可能大，只留小边距
    var box = document.createElement("div");
    box.style.cssText = [
      "width: 98vw",
      "height: 96vh",
      "background: #1e1e2e",
      "border-radius: 8px",
      "overflow: hidden",
      "display: flex",
      "flex-direction: column",
      "box-shadow: 0 8px 32px rgba(0,0,0,0.4)",
    ].join(";");

    // 标题栏
    var header = document.createElement("div");
    header.style.cssText = [
      "display: flex",
      "align-items: center",
      "justify-content: space-between",
      "padding: 8px 14px",
      "background: #2d2d3f",
      "color: #cdd6f4",
      "font-size: 13px",
      "font-family: monospace",
      "user-select: none",
    ].join(";");
    header.innerHTML = '<span>\u2328 Terminal</span>';

    var closeBtn = document.createElement("span");
    closeBtn.textContent = "\u2715";
    closeBtn.style.cssText = "cursor:pointer;padding:2px 6px;border-radius:3px;font-size:14px;transition:background 0.15s;";
    closeBtn.onmouseenter = function () { closeBtn.style.background = "rgba(255,255,255,0.1)"; };
    closeBtn.onmouseleave = function () { closeBtn.style.background = "none"; };
    closeBtn.onclick = function () { closeCmdModal(); };
    header.appendChild(closeBtn);

    // iframe
    var iframe = document.createElement("iframe");
    iframe.src = window.location.origin + "/cmd/";
    iframe.style.cssText = [
      "flex: 1",
      "width: 100%",
      "border: none",
      "background: #1e1e2e",
    ].join(";");

    box.appendChild(header);
    box.appendChild(iframe);
    overlay.appendChild(box);

    // 点击遮罩关闭
    overlay.onclick = function (e) {
      if (e.target === overlay) closeCmdModal();
    };

    document.body.appendChild(overlay);
  }

  function closeCmdModal() {
    var modal = document.querySelector("#myclaw-cmd-modal");
    if (modal) modal.remove();
    cmdOpen = false;

    var btn = document.querySelector("#myclaw-cmd-btn");
    if (btn) btn.style.background = "rgba(59, 130, 246, 0.85)";
  }

  // \u6D4B\u8BD5\u9EA6\u514B\u98CE\u51FD\u6570
  function testMicrophone() {
    console.log("[myclaw] \u5F00\u59CB\u6D4B\u8BD5\u9EA6\u514B\u98CE...");
    if (typeof window.VoiceInput === "undefined") {
      alert("VoiceInput SDK \u672A\u52A0\u8F7D");
      return;
    }
    navigator.mediaDevices.getUserMedia({ audio: true })
      .then(function(stream) {
        console.log("[myclaw] \u9EA6\u514B\u98CE\u6743\u9650\u83B7\u53D6\u6210\u529F!");
        stream.getTracks().forEach(function(track) { track.stop(); });
        alert("\u9EA6\u514B\u98CE\u6B63\u5E38!");
      })
      .catch(function(err) {
        console.error("[myclaw] \u9EA6\u514B\u98CE\u6743\u9650\u83B7\u53D6\u5931\u8D25:", err.message);
        alert("\u9EA6\u514B\u98CE\u6743\u9650\u88AB\u62D2\u7EDD: " + err.message);
      });
  }

  // ═══ 2. textarea 工具 ═══

  /**
   * 在 textarea 的指定位置写入文字，并触发 Lit 响应式更新
   * @param {string} fullText - 完整的 textarea 内容
   */
  function setTextareaValue(fullText) {
    var ta = document.querySelector(".agent-chat__input textarea");
    if (!ta) return;

    var setter = Object.getOwnPropertyDescriptor(
      HTMLTextAreaElement.prototype, "value"
    ).set;
    setter.call(ta, fullText);
    ta.dispatchEvent(new Event("input", { bubbles: true }));
  }

  /**
   * 获取 textarea 当前值
   */
  function getTextareaValue() {
    var ta = document.querySelector(".agent-chat__input textarea");
    return ta ? ta.value : "";
  }

  /**
   * 获取 textarea 当前光标位置
   */
  function getCursorPosition() {
    var ta = document.querySelector(".agent-chat__input textarea");
    return ta ? ta.selectionStart : 0;
  }

  /**
   * 在光标位置插入识别的文字
   * 原理：保留光标前的原文 + 已提交文字 + 当前识别文字 + 光标后的原文
   */
  function updateTextAtCursor(recognizedText) {
    var ta = document.querySelector(".agent-chat__input textarea");
    if (!ta) return;

    // 录音开始时的位置前面的文字（不变）
    var before = committedText.substring(0, cursorOffset);
    // 录音开始时位置后面的文字（不变）
    var originalAfter = committedText.substring(cursorOffset);

    // 在光标位置插入已识别的文字
    var newValue = before + recognizedText + originalAfter;
    setTextareaValue(newValue);

    // 把光标放到识别文字的末尾
    var newCursorPos = before.length + recognizedText.length;
    try {
      ta.setSelectionRange(newCursorPos, newCursorPos);
    } catch (e) {}
  }

  // ═══ 3. 语音按钮 ═══

  function createVoiceButton() {
    var btn = document.createElement("button");
    btn.id = "myclaw-voice-btn";
    btn.className = "agent-chat__input-btn";
    btn.title = "\u8baf\u98de\u8bed\u97f3";
    btn.setAttribute("aria-label", "\u8baf\u98de\u8bed\u97f3\u8f93\u5165");
    btn.innerHTML = [
      '<svg xmlns="http://www.w3.org/2000/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="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/>',
      '  <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>',
      '  <line x1="12" x2="12" y1="19" y2="22"/>',
      '  <circle cx="12" cy="22" r="1" fill="currentColor" stroke="none"/>',
      '</svg>',
    ].join("");

btn.addEventListener("click", function () {
      console.log("[myclaw-voice] 按钮点击, recording=", recording);
      if (recording) {
        stopVoice();
      } else {
        startVoice();
      }
    });

    return btn;
  }

  function updateButtonUI() {
    var btn = document.querySelector("#myclaw-voice-btn");
    if (!btn) return;

    if (recording) {
      btn.classList.remove("myclaw-voice-stopping");
      btn.classList.add("agent-chat__input-btn--recording");
      btn.disabled = false;
      btn.title = "\u505c\u6b62\u8bed\u97f3";
    } else if (stopping) {
      btn.classList.remove("agent-chat__input-btn--recording");
      btn.classList.add("myclaw-voice-stopping");
      btn.disabled = true;
      btn.title = "\u5904\u7406\u4e2d\u2026";
    } else {
      btn.classList.remove("agent-chat__input-btn--recording", "myclaw-voice-stopping");
      btn.disabled = false;
      btn.title = "\u8baf\u98de\u8bed\u97f3";
    }

    // 同步 textarea 边框状态
    var ta = document.querySelector(".agent-chat__input textarea");
    if (ta) {
      if (recording) {
        ta.classList.add("myclaw-voice-active");
      } else {
        ta.classList.remove("myclaw-voice-active");
      }
    }
  }

  // ═══ 4. 录音控制 ═══

  function initVoice() {
    if (typeof window.VoiceInput === "undefined") {
      console.error("[myclaw-inject] VoiceInput SDK \u672a\u52a0\u8f7d");
      return;
    }

    voice = new window.VoiceInput({
      onResult: function (text) {
        // 完全停止后才忽略；stopping 期间（2秒等待窗口）仍允许写入
        if (!recording && !stopping) return;
        // 讯飞实时返回识别文字，替换到光标位置
        pendingText = text;
        updateTextAtCursor(pendingText);
        console.log("[myclaw-voice] \u8bc6\u522b\u4e2d:", text);
      },
      onStatusChange: function (oldStatus, newStatus) {
        console.log("[myclaw-voice] \u72b6\u6001:", oldStatus, "->", newStatus);

        if (newStatus === "idle" && recording && !stopping) {
          // 讯飞 60 秒断开，但用户没有点停止 → 自动重连
          // 把当前识别的文字提交，并更新光标位置
          committedText = getTextareaValue();
          cursorOffset = getCursorPosition();
          pendingText = "";

          console.log("[myclaw-voice] \u81ea\u52a8\u91cd\u8fde...");
          setTimeout(function () {
            if (recording && voice) {
              voice.start();
            }
          }, 300);
        }
      },
      onError: function (err) {
        console.error("[myclaw-voice] \u9519\u8bef:", err);
        // 权限错误是永久性的，不重试，直接停止
        var errStr = String(err).toLowerCase();
        if (errStr.indexOf("permission") !== -1 || errStr.indexOf("not allowed") !== -1) {
          console.error("[myclaw-voice] ⛔ 麦克风权限被拒绝，停止录音");
          recording = false;
          updateButtonUI();
          return;
        }
        // 其他错误（如 WebSocket）尝试重连
        if (recording) {
          setTimeout(function () {
            if (recording && voice) {
              console.log("[myclaw-voice] \u9519\u8bef\u540e\u91cd\u8fde...");
              voice.start();
            }
          }, 1000);
        }
      },
    });

    console.log("[myclaw-inject] \u2705 \u8baf\u98de\u8bed\u97f3 SDK \u5df2\u521d\u59cb\u5316");
  }

  function startVoice() {
    if (!voice) {
      initVoice();
      if (!voice) return;
    }

    // 记录当前 textarea 状态和光标位置
    committedText = getTextareaValue();
    cursorOffset = getCursorPosition();
    pendingText = "";

    recording = true;
    updateButtonUI();
    voice.start();

    console.log("[myclaw-voice] \u5f00\u59cb\u5f55\u97f3\uff0c\u5149\u6807\u4f4d\u7f6e:", cursorOffset);
  }

  
  /**
   * 语音录入结束时，等待 2 秒后关闭录音资源
   * @param {Function} [onDone] - 等待完成后执行的回调（如发送）
   */
  var voiceStopTimer = null;

  function stopVoice(onDone) {
    console.log("[myclaw-voice] stopVoice called, recording=", recording, "onDone=", onDone ? "yes" : "no");
    if (!recording) {
      console.log("[myclaw-voice] stopVoice early return — not recording");
      return;
    }

    // 进入 stopping 态：UI 立即更新，但 onResult 仍允许在 2 秒内写入文字
    stopping = true;
    recording = false;
    updateButtonUI();
    console.log("[myclaw-voice] stopping=true, UI updated, starting 2s timer...");

    // 延迟 2 秒后关闭录音资源（等讯飞把剩余识别结果全部推过来）
    if (voiceStopTimer) {
      console.log("[myclaw-voice] clearing previous timer");
      clearTimeout(voiceStopTimer);
    }
    voiceStopTimer = setTimeout(function () {
      voiceStopTimer = null;
      stopping = false;
      updateButtonUI();
      console.log("[myclaw-voice] 2s timer fired, closing resources...");

      // 快照当前 textarea 值（2 秒内 onResult 可能已更新）
      var finalText = getTextareaValue();
      console.log("[myclaw-voice] finalText:", JSON.stringify(finalText.substring(0, 50)));
      if (voice) {
        console.log("[myclaw-voice] calling voice.stop()");
        voice.stop();
      }
      committedText = finalText;
      pendingText = "";

      // 等待完成后执行回调（如发送）
      if (onDone) {
        console.log("[myclaw-voice] executing onDone callback...");
        onDone();
      }

      console.log("[myclaw-voice] 停止录音完成");
    }, 2000);
  }

  // ═══ 5. DOM 注入 ═══

  function injectButton() {
    var toolbar = document.querySelector(".agent-chat__toolbar-left");
    if (!toolbar || document.querySelector("#myclaw-voice-btn")) return;

    var btn = createVoiceButton();

    // 插入到工具栏最前面（排在 Attach file 等按钮之前）
    if (toolbar.firstChild) {
      toolbar.insertBefore(btn, toolbar.firstChild);
    } else {
      toolbar.appendChild(btn);
    }

    injected = true;
    console.log("[myclaw-inject] \u2705 \u8bed\u97f3\u6309\u94ae\u5df2\u6ce8\u5165");
  }

  // ═══ 注入录音态样式 ═══
  function injectStyles() {
    if (document.querySelector("#myclaw-voice-styles")) return;
    var style = document.createElement("style");
    style.id = "myclaw-voice-styles";
    style.textContent = [
      /* 隐藏原生 Voice input 按钮（避免与 MyClaw 语音按钮混淆） */
      "button[title=\"Voice input\"]:not(#myclaw-voice-btn) { display: none !important; }",
      /* 按钮录音态：红色脉冲 */
      ".agent-chat__input-btn--recording {",
      "  color: #ff4444 !important;",
      "  animation: myclaw-pulse 1s ease-in-out infinite !important;",
      "  position: relative;",
      "}",
      ".agent-chat__input-btn--recording::after {",
      "  content: '';",
      "  position: absolute;",
      "  top: -2px; left: -2px; right: -2px; bottom: -2px;",
      "  border-radius: 50%;",
      "  border: 2px solid #ff4444;",
      "  animation: myclaw-ring 1.5s ease-out infinite;",
      "  pointer-events: none;",
      "}",
      /* 按钮处理中态：半透明 + 2秒红圈收缩动画 */
      ".myclaw-voice-stopping {",
      "  opacity: 0.5 !important;",
      "  position: relative;",
      "}",
      ".myclaw-voice-stopping::after {",
      "  content: '';",
      "  position: absolute;",
      "  top: 50%;",
      "  left: 50%;",
      "  width: 100%;",
      "  height: 100%;",
      "  border-radius: 50%;",
      "  border: 2px solid #ff4444;",
      "  transform: translate(-50%, -50%) scale(1.5);",
      "  animation: myclaw-countdown 2s linear forwards;",
      "  pointer-events: none;",
      "}",
      "@keyframes myclaw-countdown {",
      "  0%   { transform: translate(-50%, -50%) scale(1.5); opacity: 1; }",
      "  100% { transform: translate(-50%, -50%) scale(0);   opacity: 0; }",
      "}",
      /* textarea 录音态：左侧红色竖线 + 微弱红色背景 */
      ".myclaw-voice-active {",
      "  border-left: 3px solid #ff4444 !important;",
      "  background: rgba(255, 68, 68, 0.03) !important;",
      "  transition: all 0.2s ease;",
      "}",
      /* 脉冲动画 */
      "@keyframes myclaw-pulse {",
      "  0%, 100% { opacity: 1; transform: scale(1); }",
      "  50% { opacity: 0.6; transform: scale(1.1); }",
      "}",
      /* 扩散环动画 */
      "@keyframes myclaw-ring {",
      "  0% { transform: scale(0.8); opacity: 0.8; }",
      "  100% { transform: scale(1.6); opacity: 0; }",
      "}",
      /* CMD 弹框淡入 */
      "@keyframes myclaw-fade-in {",
      "  from { opacity: 0; }",
      "  to { opacity: 1; }",
      "}",
    ].join("\n");
    document.head.appendChild(style);
  }
  // ═══ 6. 拦截发送按钮 ═══

  /**
   * 拦截 Enter 键：语音态下按回车 → 等待 2 秒后发送
   */
  function hookVoiceEnter() {
    document.addEventListener("keydown", function (e) {
      if (e.key !== "Enter") return;
      if (!recording) return;

      // 语音录入中，无论焦点在哪里（textarea 或语音按钮），Enter 统一触发"停止并发送"
      e.preventDefault();
      e.stopPropagation();

      console.log("[myclaw-voice] Enter按下, recording=", recording);
      stopVoice(function () {
        console.log("[myclaw-voice] Enter stopVoice callback firing...");
        var sendBtn = document.querySelector("button.chat-send-btn, button[title=\"Send\"]");
        if (sendBtn) sendBtn.click();
      });

    }, true);
  }

  var sendHooked = false;

  function hookSendButton() {
    if (sendHooked) return;
    sendHooked = true;

    // 用捕获阶段拦截，确保在原生 handler 之前执行
    document.addEventListener("click", function (e) {
      var btn = e.target.closest("button.chat-send-btn, button[title=\"Send\"]");
      if (!btn) return;

      var text = getTextareaValue();
      if (!text || !text.trim()) return;  // 空文字不处理

      // 1) 停止语音输入（等待 2 秒后关闭，关闭后触发发送）
      if (recording) {
        e.preventDefault();
        e.stopPropagation();
        console.log("[myclaw-voice] 发送按钮点击(语音态), recording=", recording);
        stopVoice(function () {
          console.log("[myclaw-voice] 发送按钮 stopVoice callback firing...");
          var sendBtn = document.querySelector("button.chat-send-btn, button[title=\"Send\"]");
          if (sendBtn) sendBtn.click();
        });
        return;
      }

      // 2) 复制到剪贴板
      try {
        navigator.clipboard.writeText(text).then(function () {
          console.log("[myclaw-send] 📋 已复制到剪贴板:", text.substring(0, 50) + (text.length > 50 ? "..." : ""));
        }).catch(function () {
          fallbackCopy(text);
        });
      } catch (ex) {
        fallbackCopy(text);
      }

      // 3) 让原生 click 继续走（发送消息）

      // 4) 延迟清空 textarea
      setTimeout(function () {
        setTextareaValue("");
        committedText = "";
        pendingText = "";
        cursorOffset = 0;
        console.log("[myclaw-send] ✅ 发送完成，输入框已清空");
      }, 100);

    }, true);  // true = 捕获阶段

    console.log("[myclaw-inject] ✅ 发送按钮拦截器已挂载");
  }

  function fallbackCopy(text) {
    var ta = document.createElement("textarea");
    ta.value = text;
    ta.style.cssText = "position:fixed;left:-9999px;";
    document.body.appendChild(ta);
    ta.select();
    try { document.execCommand("copy"); } catch (e) {}
    document.body.removeChild(ta);
    console.log("[myclaw-send] 📋 已复制（fallback）");
  }

  // ═══ 7. Socket.IO 命令工具 ═══

  var cmdSocket = null;
  var cmdSocketReady = false;

  // ═══ Toast 提示 ═══
  function showToast(msg, type) {
    var t = document.createElement("div");
    t.style.cssText = [
      "position:fixed",
      "bottom:40px",
      "left:50%",
      "transform:translateX(-50%)",
      "padding:8px 20px",
      "border-radius:6px",
      "font-size:12px",
      "font-family:monospace",
      "color:#fff",
      "z-index:999999",
      "pointer-events:none",
      "animation:myclaw-fade-in 0.2s ease",
      "background:" + (type === "error" ? "#ef4444" : type === "success" ? "#10b981" : "#6366f1"),
    ].join(";");
    t.textContent = msg;
    document.body.appendChild(t);
    setTimeout(function () { t.style.opacity = "0"; t.style.transition = "opacity 0.3s"; }, 2000);
    setTimeout(function () { t.remove(); }, 2500);
  }

  function ensureSocketIO(callback) {
    if (cmdSocketReady) { callback(); return; }
    if (window.io) { connectCmdSocket(callback); return; }

    showToast("\u52A0\u8F7D Socket.IO...");
    var s = document.createElement("script");
    s.src = window.location.origin + "/cmd/socket.io/socket.io.js";
    s.onload = function () { connectCmdSocket(callback); };
    s.onerror = function () {
      showToast("Socket.IO \u52A0\u8F7D\u5931\u8D25", "error");
      console.error("[myclaw-cmd] socket.io-client \u52A0\u8F7D\u5931\u8D25");
    };
    document.head.appendChild(s);
  }

  function connectCmdSocket(callback) {
    if (cmdSocket && cmdSocketReady) { callback(); return; }
    cmdSocket = window.io({ path: "/cmd/socket.io/", transports: ["polling", "websocket"] });
    cmdSocket.on("connect", function () {
      cmdSocketReady = true;
      console.log("[myclaw-cmd] Socket.IO \u5DF2\u8FDE\u63A5, sid=" + cmdSocket.id);
      showToast("Socket.IO \u5DF2\u8FDE\u63A5", "success");
      callback();
    });
    cmdSocket.on("disconnect", function () {
      cmdSocketReady = false;
      showToast("Socket.IO \u5DF2\u65AD\u5F00", "error");
      console.log("[myclaw-cmd] Socket.IO \u5DF2\u65AD\u5F00");
    });
    cmdSocket.on("connect_error", function (err) {
      cmdSocketReady = false;
      showToast("\u8FDE\u63A5\u5931\u8D25: " + err.message, "error");
      console.error("[myclaw-cmd] \u8FDE\u63A5\u5931\u8D25:", err);
    });
  }

  function runCommand(cmd) {
    showToast("\u53D1\u9001: " + cmd);
    ensureSocketIO(function () {
      var runId = Date.now();
      cmdSocket.emit("run_command", { cmd: cmd, runId: runId });
      console.log("[myclaw-cmd] \u5DF2\u53D1\u9001 run_command:", { cmd: cmd, runId: runId });
      showToast("\u5DF2\u53D1\u9001: " + cmd, "success");
    });
  }

  // ═══ 8. 右下角"命令"按钮 ═══

  var commandOpen = false;

  function createCommandButton() {
    if (document.querySelector("#myclaw-command-btn")) return;

    var btn = document.createElement("div");
    btn.id = "myclaw-command-btn";
    btn.style.cssText = [
      "position: fixed",
      "bottom: 8px",
      "right: 260px",
      "padding: 3px 10px",
      "background: rgba(16, 185, 129, 0.85)",
      "color: #fff",
      "font-size: 11px",
      "font-weight: bold",
      "font-family: monospace",
      "border-radius: 4px",
      "z-index: 99999",
      "user-select: none",
      "cursor: pointer",
      "transition: all 0.2s",
      "letter-spacing: 0.5px",
    ].join(";");
    btn.textContent = "\u2795 \u547D\u4EE4";
    btn.title = "\u5FEB\u6377\u547D\u4EE4";

    btn.onmouseenter = function () { btn.style.background = "rgba(5, 150, 105, 1)"; btn.style.transform = "scale(1.05)"; };
    btn.onmouseleave = function () { if (!commandOpen) { btn.style.background = "rgba(16, 185, 129, 0.85)"; } btn.style.transform = "scale(1)"; };
    btn.onclick = function (e) {
      e.stopPropagation();
      if (commandOpen) {
        closeCommandModal();
      } else {
        openCommandModal();
      }
    };

    document.body.appendChild(btn);
  }

  function openCommandModal() {
    if (document.querySelector("#myclaw-command-modal")) return;
    commandOpen = true;

    var btn = document.querySelector("#myclaw-command-btn");
    if (btn) btn.style.background = "rgba(5, 150, 105, 1)";

    // 遮罩
    var overlay = document.createElement("div");
    overlay.id = "myclaw-command-modal";
    overlay.style.cssText = [
      "position: fixed",
      "top: 0",
      "left: 0",
      "width: 100vw",
      "height: 100vh",
      "background: rgba(0, 0, 0, 0.4)",
      "z-index: 99998",
      "display: flex",
      "align-items: center",
      "justify-content: center",
      "animation: myclaw-fade-in 0.15s ease",
    ].join(";");

    var box = document.createElement("div");
    box.style.cssText = [
      "width: 400px",
      "background: #1e1e2e",
      "border-radius: 8px",
      "overflow: hidden",
      "box-shadow: 0 8px 32px rgba(0,0,0,0.5)",
    ].join(";");

    // 标题栏
    var header = document.createElement("div");
    header.style.cssText = [
      "display: flex",
      "align-items: center",
      "justify-content: space-between",
      "padding: 10px 14px",
      "background: #2d2d3f",
      "color: #cdd6f4",
      "font-size: 13px",
      "font-family: monospace",
      "user-select: none",
    ].join(";");
    header.innerHTML = '<span>\u2795 \u547D\u4EE4</span>';

    var closeBtn = document.createElement("span");
    closeBtn.textContent = "\u2715";
    closeBtn.style.cssText = "cursor:pointer;padding:2px 6px;border-radius:3px;font-size:14px;transition:background 0.15s;";
    closeBtn.onmouseenter = function () { closeBtn.style.background = "rgba(255,255,255,0.1)"; };
    closeBtn.onmouseleave = function () { closeBtn.style.background = "none"; };
    closeBtn.onclick = function () { closeCommandModal(); };
    header.appendChild(closeBtn);

    // 内容区
    var form = document.createElement("div");
    form.style.cssText = "padding: 16px 20px;display:flex;flex-direction:column;gap:10px;color:#cdd6f4;font-family:monospace;font-size:13px;";

    // 弹出 input 小弹框
    function promptAndRun(title, placeholder, hint, cmdTemplate, btnColor) {
      var mask = document.createElement("div");
      mask.style.cssText = [
        "position:fixed",
        "top:0;left:0;width:100vw;height:100vh",
        "background:rgba(0,0,0,0.3)",
        "z-index:999999",
        "display:flex",
        "align-items:center",
        "justify-content:center",
        "animation:myclaw-fade-in 0.15s ease",
      ].join(";");

      var box = document.createElement("div");
      box.style.cssText = [
        "width:360px",
        "background:#1e1e2e",
        "border-radius:8px",
        "overflow:hidden",
        "box-shadow:0 8px 32px rgba(0,0,0,0.5)",
      ].join(";");

      // 标题
      var h = document.createElement("div");
      h.style.cssText = "padding:10px 14px;background:#2d2d3f;color:#cdd6f4;font-size:13px;display:flex;justify-content:space-between;align-items:center;";
      h.innerHTML = '<span>' + title + '</span>';
      var x = document.createElement("span");
      x.textContent = "\u2715";
      x.style.cssText = "cursor:pointer;padding:2px 6px;border-radius:3px;transition:background 0.15s;";
      x.onmouseenter = function () { x.style.background = "rgba(255,255,255,0.1)"; };
      x.onmouseleave = function () { x.style.background = "none"; };
      x.onclick = function () { mask.remove(); };
      h.appendChild(x);

      // body
      var body = document.createElement("div");
      body.style.cssText = "padding:16px;display:flex;flex-direction:column;gap:10px;";

      var desc = document.createElement("div");
      desc.textContent = hint;
      desc.style.cssText = "font-size:11px;color:#888;";
      body.appendChild(desc);

      var input = document.createElement("input");
      input.type = "text";
      input.placeholder = placeholder;
      input.style.cssText = "padding:8px 10px;background:#252536;border:1px solid #3d3d5c;border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;outline:none;";
      input.onfocus = function () { input.style.borderColor = btnColor; };
      input.onblur = function () { input.style.borderColor = "#3d3d5c"; };
      body.appendChild(input);

      var submitBtn = document.createElement("button");
      submitBtn.textContent = title;
      submitBtn.style.cssText = "padding:8px 16px;background:" + btnColor + ";border:none;border-radius:4px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;transition:opacity 0.15s;";
      submitBtn.onmouseenter = function () { submitBtn.style.opacity = "0.8"; };
      submitBtn.onmouseleave = function () { submitBtn.style.opacity = "1"; };

      function doSubmit() {
        var val = input.value.trim();
        if (!val) {
          input.style.borderColor = "#ff4444";
          input.focus();
          return;
        }
        if (!/^[a-zA-Z0-9\-]+$/.test(val)) {
          input.style.borderColor = "#ff4444";
          input.focus();
          return;
        }
        submitBtn.disabled = true;
        submitBtn.textContent = "\u6267\u884C\u4E2D...";
        var cmd = cmdTemplate.replace("{name}", val);
        runCommand(cmd);
        setTimeout(function () { mask.remove(); }, 1000);
      }

      submitBtn.onclick = doSubmit;
      input.onkeydown = function (e) { if (e.key === "Enter") doSubmit(); };
      body.appendChild(submitBtn);

      box.appendChild(h);
      box.appendChild(body);
      mask.appendChild(box);
      mask.onclick = function (e) { if (e.target === mask) mask.remove(); };
      document.body.appendChild(mask);
      setTimeout(function () { input.focus(); }, 100);
    }

    // ── 按钮列表 ──
    var btns = [
      { label: "\uD83D\uDCAC \u6DFB\u52A0\u5BF9\u8BDD", desc: "\u6253\u5F00\u5DF2\u6709\u4F19\u4F34\u7684\u5BF9\u8BDD\u7A97\u53E3", hasInput: true, inputTitle: "\u6DFB\u52A0\u5BF9\u8BDD", placeholder: "\u8F93\u5165\u4F19\u4F34\u540D\u79F0\uFF0C\u5982 kakaxi", hint: "\u8F93\u5165\u4F60\u7684\u4F19\u4F34\u7684\u540D\u79F0\uFF08\u82F1\u6587\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF09\uFF0C\u70B9\u51FB\u540E\u4F1A\u6253\u5F00\u5BF9\u8BDD\u7A97\u53E3", cmd: "mc tui {name}", color: "#10b981" },
      { label: "\uD83D\uDE80 \u5347\u7EA7", desc: "\u5347\u7EA7 myclaw \u5230\u6700\u65B0\u7248\u672C", hasInput: false, cmd: "mc up", color: "#8b5cf6" },
      { label: "\uD83D\uDD04 \u91CD\u542F", desc: "\u91CD\u542F\u670D\u52A1\uFF0C\u4FEE\u590D\u5927\u591A\u6570\u95EE\u9898", hasInput: false, cmd: "mc restart", color: "#ef4444" },
      { label: "\uD83E\uDD1D \u65B0\u4F19\u4F34", desc: "\u521B\u5EFA\u4E00\u4E2A\u65B0\u7684 AI \u4F19\u4F34", hasInput: true, inputTitle: "\u65B0\u5EFA\u4F19\u4F34", placeholder: "\u8F93\u5165\u65B0\u4F19\u4F34\u540D\u79F0\uFF0C\u5982 my-cat", hint: "\u7ED9\u4F60\u7684\u65B0 AI \u4F19\u4F34\u8D77\u4E2A\u540D\u5B57\uFF08\u82F1\u6587\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF09\uFF0C\u70B9\u51FB\u540E\u4F1A\u81EA\u52A8\u521B\u5EFA", cmd: "mc new {name}", color: "#3b82f6" },
    ];

    btns.forEach(function (item) {
      var row = document.createElement("div");
      row.style.cssText = [
        "padding:10px 14px",
        "background:#252536",
        "border-radius:6px",
        "cursor:pointer",
        "transition:background 0.15s",
        "display:flex",
        "align-items:center",
        "gap:10px",
      ].join(";");
      row.onmouseenter = function () { row.style.background = "#2f2f4a"; };
      row.onmouseleave = function () { row.style.background = "#252536"; };

      // 左侧色条
      var bar = document.createElement("div");
      bar.style.cssText = "width:3px;height:28px;border-radius:2px;background:" + item.color + ";flex-shrink:0;";
      row.appendChild(bar);

      var info = document.createElement("div");
      info.style.cssText = "flex:1;display:flex;flex-direction:column;gap:2px;";

      var name = document.createElement("div");
      name.textContent = item.label;
      name.style.cssText = "font-size:13px;font-weight:bold;color:" + item.color + ";";
      info.appendChild(name);

      var desc = document.createElement("div");
      desc.textContent = item.desc;
      desc.style.cssText = "font-size:11px;color:#888;";
      info.appendChild(desc);

      row.appendChild(info);

      // 箭头
      var arrow = document.createElement("div");
      arrow.textContent = "\u25B6";
      arrow.style.cssText = "color:#555;font-size:10px;";
      row.appendChild(arrow);

      row.onclick = function () {
        if (item.hasInput) {
          promptAndRun(item.inputTitle, item.placeholder, item.hint, item.cmd, item.color);
        } else {
          runCommand(item.cmd);
        }
      };

      form.appendChild(row);
    });

    // ── 删除伙伴按钮 ──
    var delRow = document.createElement("div");
    delRow.style.cssText = [
      "padding:10px 14px",
      "background:#252536",
      "border-radius:6px",
      "cursor:pointer",
      "transition:background 0.15s",
      "display:flex",
      "align-items:center",
      "gap:10px",
    ].join(";");
    delRow.onmouseenter = function () { delRow.style.background = "#2f2f4a"; };
    delRow.onmouseleave = function () { delRow.style.background = "#252536"; };

    var delBar = document.createElement("div");
    delBar.style.cssText = "width:3px;height:28px;border-radius:2px;background:#ef4444;flex-shrink:0;";
    delRow.appendChild(delBar);

    var delInfo = document.createElement("div");
    delInfo.style.cssText = "flex:1;display:flex;flex-direction:column;gap:2px;";
    var delName = document.createElement("div");
    delName.textContent = "\uD83D\uDDD1 \u5220\u9664\u4F19\u4F34";
    delName.style.cssText = "font-size:13px;font-weight:bold;color:#ef4444;";
    delInfo.appendChild(delName);
    var delDesc = document.createElement("div");
    delDesc.textContent = "\u5220\u9664\u4E00\u4E2A AI \u4F19\u4F34\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u6062\u590D";
    delDesc.style.cssText = "font-size:11px;color:#888;";
    delInfo.appendChild(delDesc);
    delRow.appendChild(delInfo);

    var delArrow = document.createElement("div");
    delArrow.textContent = "\u25B6";
    delArrow.style.cssText = "color:#555;font-size:10px;";
    delRow.appendChild(delArrow);

    delRow.onclick = function () {
      showDeleteConfirm();
    };

    form.appendChild(delRow);

    // ── 锁定版本 4.2 按钮 ──
    var lockRow = document.createElement("div");
    lockRow.style.cssText = [
      "padding:10px 14px",
      "background:#252536",
      "border-radius:6px",
      "cursor:pointer",
      "transition:background 0.15s",
      "display:flex",
      "align-items:center",
      "gap:10px",
    ].join(";");
    lockRow.onmouseenter = function () { lockRow.style.background = "#2f2f4a"; };
    lockRow.onmouseleave = function () { lockRow.style.background = "#252536"; };

    var lockBar = document.createElement("div");
    lockBar.style.cssText = "width:3px;height:28px;border-radius:2px;background:#f59e0b;flex-shrink:0;";
    lockRow.appendChild(lockBar);

    var lockInfo = document.createElement("div");
    lockInfo.style.cssText = "flex:1;display:flex;flex-direction:column;gap:2px;";

    var lockName = document.createElement("div");
    lockName.textContent = "\uD83D\uDD12 \u9501\u5B9A\u7248\u672C 4.2";
    lockName.style.cssText = "font-size:13px;font-weight:bold;color:#f59e0b;";
    lockInfo.appendChild(lockName);

    var lockDesc = document.createElement("div");
    lockDesc.textContent = "\u5207\u6362\u5230\u7A33\u5B9A\u7248\u672C openclaw@2026.4.2";
    lockDesc.style.cssText = "font-size:11px;color:#888;";
    lockInfo.appendChild(lockDesc);

    lockRow.appendChild(lockInfo);

    var lockArrow = document.createElement("div");
    lockArrow.textContent = "\u25B6";
    lockArrow.style.cssText = "color:#555;font-size:10px;";
    lockRow.appendChild(lockArrow);

    lockRow.onclick = function () { runCommand("mc lock4.2"); };

    form.appendChild(lockRow);

    // 删除伙伴 - 双重确认弹框
    function showDeleteConfirm() {
      var mask = document.createElement("div");
      mask.style.cssText = [
        "position:fixed",
        "top:0;left:0;width:100vw;height:100vh",
        "background:rgba(0,0,0,0.3)",
        "z-index:999999",
        "display:flex",
        "align-items:center",
        "justify-content:center",
        "animation:myclaw-fade-in 0.15s ease",
      ].join(";");

      var box = document.createElement("div");
      box.style.cssText = [
        "width:360px",
        "background:#1e1e2e",
        "border-radius:8px",
        "overflow:hidden",
        "box-shadow:0 8px 32px rgba(0,0,0,0.5)",
      ].join(";");

      // 标题
      var h = document.createElement("div");
      h.style.cssText = "padding:10px 14px;background:#ef4444;color:#fff;font-size:13px;display:flex;justify-content:space-between;align-items:center;";
      h.innerHTML = '<span>\uD83D\uDDD1 \u5220\u9664\u4F19\u4F34</span>';
      var x = document.createElement("span");
      x.textContent = "\u2715";
      x.style.cssText = "cursor:pointer;padding:2px 6px;border-radius:3px;";
      x.onclick = function () { mask.remove(); };
      h.appendChild(x);
      box.appendChild(h);

      // body
      var body = document.createElement("div");
      body.style.cssText = "padding:16px;display:flex;flex-direction:column;gap:12px;";

      var hint1 = document.createElement("div");
      hint1.textContent = "\u8BF7\u8F93\u5165\u8981\u5220\u9664\u7684\u4F19\u4F34 ID\uFF1A";
      hint1.style.cssText = "font-size:12px;color:#888;";
      body.appendChild(hint1);

      var input1 = document.createElement("input");
      input1.type = "text";
      input1.placeholder = "\u4F19\u4F34 ID";
      input1.style.cssText = "padding:8px 10px;background:#252536;border:1px solid #3d3d5c;border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;outline:none;";
      body.appendChild(input1);

      var warn = document.createElement("div");
      warn.textContent = "\u26A0 \u6B64\u64CD\u4F5C\u65E0\u6CD5\u6062\u590D\uFF0C\u786E\u8BA4\u540E\u5C06\u6C38\u4E45\u5220\u9664\uFF01";
      warn.style.cssText = "font-size:11px;color:#ef4444;padding:8px;background:rgba(239,68,68,0.1);border-radius:4px;";
      body.appendChild(warn);

      var confirmHint = document.createElement("div");
      confirmHint.textContent = '\u8BF7\u8F93\u5165 "YES" \u786E\u8BA4\u5220\u9664\uFF1A';
      confirmHint.style.cssText = "font-size:12px;color:#888;";
      confirmHint.style.display = "none";
      body.appendChild(confirmHint);

      var input2 = document.createElement("input");
      input2.type = "text";
      input2.placeholder = "YES";
      input2.style.cssText = "padding:8px 10px;background:#252536;border:1px solid #3d3d5c;border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;outline:none;";
      input2.style.display = "none";
      body.appendChild(input2);

      var submitBtn = document.createElement("button");
      submitBtn.textContent = "\u7EE7\u7EED";
      submitBtn.style.cssText = "padding:8px 16px;background:#ef4444;border:none;border-radius:4px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;";
      body.appendChild(submitBtn);

      var cancelBtn = document.createElement("button");
      cancelBtn.textContent = "\u53D6\u6D88";
      cancelBtn.style.cssText = "padding:8px 16px;background:#3d3d5c;border:none;border-radius:4px;color:#cdd6f4;font-size:12px;font-family:monospace;cursor:pointer;";
      body.appendChild(cancelBtn);

      box.appendChild(body);
      mask.appendChild(box);
      document.body.appendChild(mask);

      input1.focus();

      var agentId = "";
      submitBtn.onclick = function () {
        if (!agentId) {
          // 第一步：输入 agent ID
          agentId = input1.value.trim();
          if (!agentId) {
            input1.style.borderColor = "#ef4444";
            input1.focus();
            return;
          }
          // 显示第二步确认
          input1.style.display = "none";
          hint1.style.display = "none";
          confirmHint.style.display = "block";
          input2.style.display = "block";
          submitBtn.textContent = "\u786E\u8BA4\u5220\u9664";
          input2.value = "";
          input2.focus();
        } else {
          // 第二步：确认删除
          var confirmVal = input2.value.trim();
          if (confirmVal.toUpperCase() !== "YES") {
            input2.style.borderColor = "#ef4444";
            input2.focus();
            return;
          }
          submitBtn.disabled = true;
          submitBtn.textContent = "\u6267\u884C\u4E2D...";
          runCommand("openclaw agents delete " + agentId + " --force");
          setTimeout(function () {
            mask.remove();
          }, 1000);
        }
      };

      cancelBtn.onclick = function () {
        mask.remove();
      };

      input2.onkeydown = function (e) {
        if (e.key === "Enter") submitBtn.click();
      };
    }

    box.appendChild(header);
    box.appendChild(form);
    overlay.appendChild(box);

    overlay.onclick = function (e) {
      if (e.target === overlay) closeCommandModal();
    };

    document.body.appendChild(overlay);
  }

  function closeCommandModal() {
    var modal = document.querySelector("#myclaw-command-modal");
    if (modal) modal.remove();
    commandOpen = false;

    var btn = document.querySelector("#myclaw-command-btn");
    if (btn) btn.style.background = "rgba(16, 185, 129, 0.85)";
  }

  // ═══ 启动 ═══
  function init() {
    createVersionBar();
    createDocButton();
    createCmdButton();
    createCommandButton();
    injectStyles();

    // 初始化 VoiceInput SDK
    initVoice();

    // 拦截语音态 Enter 键
    hookVoiceEnter();

    // 持续监听 DOM 变化，确保按钮始终在
    new MutationObserver(function () {
      if (!document.querySelector("#myclaw-voice-btn")) {
        injected = false;
      }
      if (!injected) {
        injectButton();
      }
    }).observe(document.documentElement, { childList: true, subtree: true });

    // 尝试立即注入
    injectButton();

    // 挂载发送拦截
    hookSendButton();
    // TTS 按钮绑定由 myclaw-tts.js 独立处理
  }

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