/**
 * ============================================================================
 * 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: 5px",
      "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",
      "transform: scale(1)",
      "border: 1px solid rgba(120, 120, 120, 0.25)",
    ].join(";");
    bar.textContent = "🐾 v" + MYCLAW_VERSION;
    bar.title = "点击升级 myclaw 到最新版本";

    bar.onmouseenter = function () { bar.style.background = "rgba(37, 99, 235, 1)"; bar.style.color = "#fff"; bar.style.transform = "scale(1.05)"; };
    bar.onmouseleave = function () { bar.style.background = "rgba(59, 130, 246, 0.85)"; bar.style.color = "#fff"; bar.style.transform = "scale(1)"; };
    bar.onclick = function (e) {
      e.stopPropagation();
      runCommand("mc up");
      setTimeout(function () {
        window.location.reload();
      }, 10000);
    };

    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: 260px",
      "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: 105px",
      "padding: 3px 10px",
      "background: rgba(120, 120, 120, 0.18)",
      "color: rgba(170, 170, 170, 0.9)",
      "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(120, 120, 120, 0.3)"; btn.style.color = "rgba(220, 220, 220, 0.95)"; btn.style.transform = "scale(1.05)"; };
    btn.onmouseleave = function () { if (!cmdOpen) { btn.style.background = "rgba(120, 120, 120, 0.18)"; btn.style.color = "rgba(170, 170, 170, 0.9)"; } 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(120, 120, 120, 0.3)"; btn.style.color = "rgba(220, 220, 220, 0.95)"; }

    // 遮罩
    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. Prompt 小助手按钮（实现见 myclaw-iteration.js） ═══

  function createPromptButton() {
    return window.myclawIteration.createButton();
  }

  // ═══ 3.5 语音按钮 ═══

  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;

    if (window.myclawIteration) {
      window.myclawIteration.init({ trySend: trySend });
    }

    var voiceBtn = createVoiceButton();
    var promptBtn = createPromptButton();

    // 插入到工具栏最前面：prompt 按钮在最左，语音按钮紧跟其右
    if (toolbar.firstChild) {
      toolbar.insertBefore(voiceBtn, toolbar.firstChild);
      toolbar.insertBefore(promptBtn, voiceBtn);
    } else {
      toolbar.appendChild(promptBtn);
      toolbar.appendChild(voiceBtn);
    }

    injected = true;
    console.log("[myclaw-inject] \u2705 \u8bed\u97f3\u6309\u94ae + prompt\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. 拦截发送按钮 ═══

  /**
   * 尝试触发发送：仅当按钮不处于 Stop 状态时才发送
   * （AI 生成中时按钮带有 chat-send-btn--stop 类，此时禁止发送）
   */
  function trySend() {
    var btn = document.querySelector("button.chat-send-btn");
    if (!btn) {
      console.log("[myclaw-send] 找不到发送按钮");
      return;
    }
    if (btn.classList.contains("chat-send-btn--stop")) {
      console.log("[myclaw-send] AI 生成中，禁止发送");
      return;
    }
    btn.click();
  }

  /**
   * 拦截 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...");
        trySend();
      });

    }, 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...");
          trySend();
        });
        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: 190px",
      "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;";

    // 从 URL 解析当前页面的伙伴名
    function getCurrentAgentName() {
      var params = new URLSearchParams(window.location.search);
      var session = params.get('session');
      if (session) {
        var decoded = decodeURIComponent(session);
        if (decoded.indexOf('agent:') === 0) {
          var parts = decoded.split(':');
          if (parts.length >= 2) return parts[1];
        }
      }
      var agent = params.get('agent');
      if (agent) return agent;
      return '';
    }

    // 弹出 input 小弹框
    function promptAndRun(title, placeholder, hint, cmdTemplate, btnColor, defaultValue) {
      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.value = defaultValue || '';
      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: "\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" },
      { 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", getDefault: getCurrentAgentName },
      { label: "\uD83D\uDD04 \u91CD\u542F\u670D\u52A1", desc: "\u91CD\u542F\u6587\u4EF6\u670D\u52A1", hasInput: false, cmd: "mc server", color: "#ef4444" },
      { label: "\uD83E\uDD9E \u91CD\u542F\u9F99\u867E", desc: "\u91CD\u542F OpenClaw \u6838\u5FC3\u670D\u52A1", hasInput: false, cmd: "mc all", color: "#f59e0b" },
    ];

    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, item.getDefault ? item.getDefault() : '');
        } 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 = "\u52FE\u9009\u5E76\u6279\u91CF\u5220\u9664 AI \u4F19\u4F34";
    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 () {
      showBatchDeleteModal();
    };

    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);

    // 批量删除伙伴弹框
    var MYCLAW_API_BASE_DEL = (function () {
      var h = window.location.hostname;
      var isRemote = h === 'claw.yiranlaoshi.com' || h.endsWith('.kekouen.cn');
      return isRemote ? window.location.origin + '/sync' : 'http://127.0.0.1:18800';
    })();

    function fmtDate(ms) {
      if (!ms) return '未知';
      var d = new Date(ms);
      return d.getFullYear() + '/' + (d.getMonth() + 1) + '/' + d.getDate()
        + ' ' + String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
    }

    function showBatchDeleteModal() {
      var mask = document.createElement('div');
      mask.style.cssText = [
        'position:fixed', 'top:0', 'left:0', 'width:100vw', 'height:100vh',
        'background:rgba(0,0,0,0.5)', '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:560px', 'max-width:95vw', 'max-height:85vh',
        'background:#1e1e2e', 'border-radius:10px', 'overflow:hidden',
        'box-shadow:0 12px 48px rgba(0,0,0,0.7)',
        'display:flex', 'flex-direction:column',
      ].join(';');

      // 标题栏
      var hdr = document.createElement('div');
      hdr.style.cssText = 'padding:11px 16px;background:#ef4444;color:#fff;font-size:13px;font-family:monospace;display:flex;justify-content:space-between;align-items:center;flex-shrink:0;';
      var hdrTitle = document.createElement('span');
      hdrTitle.textContent = '🗑 批量删除伙伴';
      hdr.appendChild(hdrTitle);
      var hdrClose = document.createElement('span');
      hdrClose.textContent = '✕';
      hdrClose.style.cssText = 'cursor:pointer;padding:2px 6px;border-radius:3px;transition:background 0.15s;';
      hdrClose.onmouseenter = function () { hdrClose.style.background = 'rgba(255,255,255,0.2)'; };
      hdrClose.onmouseleave = function () { hdrClose.style.background = 'none'; };
      hdrClose.onclick = function () { mask.remove(); };
      hdr.appendChild(hdrClose);
      box.appendChild(hdr);

      // 内容区（可滚动）
      var content = document.createElement('div');
      content.style.cssText = 'flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px;font-family:monospace;color:#cdd6f4;';

      // 底部操作栏
      var footer = document.createElement('div');
      footer.style.cssText = 'padding:12px 16px;background:#252536;border-top:1px solid #3d3d5c;display:flex;gap:8px;align-items:center;flex-shrink:0;';

      box.appendChild(content);
      box.appendChild(footer);
      mask.appendChild(box);
      mask.onclick = function (e) { if (e.target === mask) mask.remove(); };
      document.body.appendChild(mask);

      // ── Step 1: 加载列表 ──
      function showStep1() {
        content.innerHTML = '';
        footer.innerHTML = '';
        hdrTitle.textContent = '🗑 批量删除伙伴 — 选择';

        var loading = document.createElement('div');
        loading.textContent = '⏳ 加载中...';
        loading.style.cssText = 'color:#888;font-size:12px;text-align:center;padding:20px 0;';
        content.appendChild(loading);

        fetch(MYCLAW_API_BASE_DEL + '/api/agents')
          .then(function (r) { return r.json(); })
          .then(function (data) {
            content.innerHTML = '';
            var agents = (data.agents || []);

            if (agents.length === 0) {
              var empty = document.createElement('div');
              empty.textContent = '没有找到任何 Agent。';
              empty.style.cssText = 'color:#888;font-size:12px;text-align:center;padding:20px 0;';
              content.appendChild(empty);
              return;
            }

            // 说明行
            var legend = document.createElement('div');
            legend.style.cssText = 'display:flex;gap:16px;font-size:11px;color:#888;padding-bottom:4px;border-bottom:1px solid #3d3d5c;';
            legend.innerHTML = '<span><span style="color:#10b981">●</span> 已注册（JSON）</span>'
              + '<span><span style="color:#f59e0b">●</span> 孤立（仅有文件夹）</span>';
            content.appendChild(legend);

            // 全选行
            var selAllRow = document.createElement('div');
            selAllRow.style.cssText = 'display:flex;align-items:center;gap:8px;font-size:12px;color:#888;cursor:pointer;padding:4px 0;';
            var selAllCb = document.createElement('input');
            selAllCb.type = 'checkbox';
            selAllCb.style.cssText = 'width:14px;height:14px;cursor:pointer;accent-color:#ef4444;';
            var selAllLabel = document.createElement('span');
            selAllLabel.textContent = '全选 / 取消全选';
            selAllRow.appendChild(selAllCb);
            selAllRow.appendChild(selAllLabel);
            content.appendChild(selAllRow);

            // agent 列表
            var checkboxes = [];
            agents.forEach(function (ag) {
              var row = document.createElement('div');
              row.style.cssText = [
                'display:flex', 'align-items:center', 'gap:10px',
                'padding:8px 10px', 'border-radius:6px',
                'background:#252536', 'cursor:pointer',
                'transition:background 0.12s',
              ].join(';');
              row.onmouseenter = function () { row.style.background = '#2f2f4a'; };
              row.onmouseleave = function () { row.style.background = cb.checked ? '#2a1a1a' : '#252536'; };

              var cb = document.createElement('input');
              cb.type = 'checkbox';
              cb.style.cssText = 'width:14px;height:14px;cursor:pointer;flex-shrink:0;accent-color:#ef4444;';
              cb.dataset.agentId = ag.id;

              var dot = document.createElement('span');
              dot.textContent = '●';
              dot.style.cssText = 'font-size:10px;flex-shrink:0;color:' + (ag.inJson ? '#10b981' : '#f59e0b') + ';';

              var info = document.createElement('div');
              info.style.cssText = 'flex:1;min-width:0;';

              var nameEl = document.createElement('div');
              nameEl.textContent = ag.id;
              nameEl.style.cssText = 'font-size:13px;font-weight:bold;color:#cdd6f4;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
              info.appendChild(nameEl);

              var metaEl = document.createElement('div');
              metaEl.style.cssText = 'font-size:10px;color:#666;display:flex;gap:12px;margin-top:2px;';
              metaEl.innerHTML = '<span>💬 ' + ag.sessionCount + ' 个会话</span>'
                + '<span>🕐 ' + fmtDate(ag.lastUpdated) + '</span>'
                + (!ag.inJson ? '<span style="color:#f59e0b">⚠ 未注册</span>' : '');
              info.appendChild(metaEl);

              row.appendChild(cb);
              row.appendChild(dot);
              row.appendChild(info);

              row.onclick = function (e) {
                if (e.target !== cb) cb.checked = !cb.checked;
                row.style.background = cb.checked ? '#2a1a1a' : '#252536';
                updateFooter();
              };
              cb.onchange = function () {
                row.style.background = cb.checked ? '#2a1a1a' : '#252536';
                updateFooter();
              };

              checkboxes.push({ cb: cb, ag: ag });
              content.appendChild(row);
            });

            // 全选逻辑
            selAllCb.onchange = function () {
              checkboxes.forEach(function (item) {
                item.cb.checked = selAllCb.checked;
                item.cb.closest ? item.cb.closest('div[style]') : null;
              });
              // 更新每行背景
              content.querySelectorAll('input[type=checkbox][data-agent-id]').forEach(function (c) {
                var r = c.parentElement;
                if (r) r.style.background = c.checked ? '#2a1a1a' : '#252536';
              });
              updateFooter();
            };
            selAllRow.onclick = function (e) {
              if (e.target !== selAllCb) { selAllCb.checked = !selAllCb.checked; selAllCb.onchange(); }
            };

            function updateFooter() {
              var selected = checkboxes.filter(function (i) { return i.cb.checked; });
              footer.innerHTML = '';
              var countTip = document.createElement('span');
              countTip.style.cssText = 'flex:1;font-size:12px;color:#888;';
              countTip.textContent = '已选 ' + selected.length + ' / ' + agents.length + ' 个';
              footer.appendChild(countTip);

              var cancelBtn = document.createElement('button');
              cancelBtn.textContent = '取消';
              cancelBtn.style.cssText = 'padding:7px 16px;background:#3d3d5c;border:none;border-radius:5px;color:#cdd6f4;font-size:12px;font-family:monospace;cursor:pointer;';
              cancelBtn.onclick = function () { mask.remove(); };
              footer.appendChild(cancelBtn);

              var nextBtn = document.createElement('button');
              nextBtn.textContent = '下一步 →';
              nextBtn.disabled = selected.length === 0;
              nextBtn.style.cssText = 'padding:7px 18px;background:' + (selected.length > 0 ? '#ef4444' : '#5a3a3a') + ';border:none;border-radius:5px;color:#fff;font-size:12px;font-family:monospace;cursor:' + (selected.length > 0 ? 'pointer' : 'not-allowed') + ';transition:opacity 0.15s;';
              nextBtn.onclick = function () {
                if (selected.length > 0) showStep2(selected.map(function (i) { return i.ag; }));
              };
              footer.appendChild(nextBtn);
            }

            updateFooter();
          })
          .catch(function (err) {
            content.innerHTML = '';
            var errEl = document.createElement('div');
            errEl.textContent = '❌ 加载失败，请确认 mc server 已启动（端口 18800）';
            errEl.style.cssText = 'color:#ef4444;font-size:12px;text-align:center;padding:20px 0;';
            content.appendChild(errEl);
          });
      }

      // ── Step 2: 确认页 ──
      function showStep2(selected) {
        content.innerHTML = '';
        footer.innerHTML = '';
        hdrTitle.textContent = '🗑 确认删除 ' + selected.length + ' 个伙伴';

        var warn = document.createElement('div');
        warn.style.cssText = 'font-size:11px;color:#ef4444;padding:8px 10px;background:rgba(239,68,68,0.12);border-radius:5px;border:1px solid rgba(239,68,68,0.3);';
        warn.innerHTML = '⚠ 以下伙伴将被移入回收站，此操作不可在本页面撤销，请仔细确认。';
        content.appendChild(warn);

        selected.forEach(function (ag) {
          var row = document.createElement('div');
          row.style.cssText = 'padding:9px 12px;background:#252536;border-radius:6px;display:flex;flex-direction:column;gap:4px;';

          var top = document.createElement('div');
          top.style.cssText = 'display:flex;align-items:center;gap:8px;';

          var dot = document.createElement('span');
          dot.textContent = '●';
          dot.style.cssText = 'font-size:10px;color:' + (ag.inJson ? '#10b981' : '#f59e0b') + ';';
          top.appendChild(dot);

          var nameEl = document.createElement('span');
          nameEl.textContent = ag.id;
          nameEl.style.cssText = 'font-size:13px;font-weight:bold;color:#ef4444;';
          top.appendChild(nameEl);

          if (!ag.inJson) {
            var tag = document.createElement('span');
            tag.textContent = '孤立';
            tag.style.cssText = 'font-size:10px;color:#f59e0b;background:rgba(245,158,11,0.15);padding:1px 6px;border-radius:3px;';
            top.appendChild(tag);
          }
          row.appendChild(top);

          var meta = document.createElement('div');
          meta.style.cssText = 'font-size:10px;color:#666;display:flex;gap:12px;padding-left:18px;';
          meta.innerHTML = '<span>💬 ' + ag.sessionCount + ' 个会话</span>'
            + '<span>🕐 最后更新: ' + fmtDate(ag.lastUpdated) + '</span>';
          row.appendChild(meta);

          content.appendChild(row);
        });

        // 底部
        var backBtn = document.createElement('button');
        backBtn.textContent = '← 返回';
        backBtn.style.cssText = 'padding:7px 16px;background:#3d3d5c;border:none;border-radius:5px;color:#cdd6f4;font-size:12px;font-family:monospace;cursor:pointer;';
        backBtn.onclick = function () { showStep1(); };
        footer.appendChild(backBtn);

        var spacer = document.createElement('span');
        spacer.style.cssText = 'flex:1;';
        footer.appendChild(spacer);

        var confirmBtn = document.createElement('button');
        confirmBtn.textContent = '确认删除 ' + selected.length + ' 个';
        confirmBtn.style.cssText = 'padding:7px 18px;background:#ef4444;border:none;border-radius:5px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;font-weight:bold;';
        confirmBtn.onclick = function () { showStep3(selected); };
        footer.appendChild(confirmBtn);
      }

      // ── Step 3: 执行删除 ──
      function showStep3(selected) {
        content.innerHTML = '';
        footer.innerHTML = '';
        hdrTitle.textContent = '🗑 删除中...';

        var inJson = selected.filter(function (ag) { return ag.inJson; });
        var orphans = selected.filter(function (ag) { return !ag.inJson; });

        // 进度项
        var progressItems = {};
        selected.forEach(function (ag) {
          var row = document.createElement('div');
          row.style.cssText = 'padding:8px 12px;background:#252536;border-radius:6px;display:flex;align-items:center;gap:10px;';

          var status = document.createElement('span');
          status.textContent = '⏳';
          status.style.cssText = 'font-size:14px;flex-shrink:0;';

          var name = document.createElement('span');
          name.textContent = ag.id;
          name.style.cssText = 'flex:1;font-size:12px;color:#cdd6f4;';

          var note = document.createElement('span');
          note.style.cssText = 'font-size:10px;color:#666;';
          note.textContent = ag.inJson ? '已注册' : '孤立';

          row.appendChild(status);
          row.appendChild(name);
          row.appendChild(note);
          content.appendChild(row);
          progressItems[ag.id] = { row: row, status: status, note: note };
        });

        function setStatus(id, icon, color, msg) {
          var item = progressItems[id];
          if (!item) return;
          item.status.textContent = icon;
          item.row.style.background = color;
          item.note.textContent = msg;
        }

        // ── 阶段1：已注册 agent 通过 socket.io 调 openclaw agents delete --force
        //           （官方负责：JSON 注销 + workspace trash + agents/<id>/agent/ trash）
        var cmdQueue = inJson.map(function (ag) { return ag.id; });

        // ── 阶段1+2 文件系统清理路径（前端始终执行两个阶段）
        //   孤立 agent：workspace + agents/<id>/ 整个目录（含 agent/ 和 sessions/）
        //   已注册 agent：workspace + agents/<id>/ 整个目录
        //     → openclaw 已清理 workspace 和 agent/，我们负责 sessions/ 残余（阶段2）
        //     → workspace 路径不存在时 trashPath 会静默跳过，安全
        var orphanPaths = [];
        orphans.forEach(function (ag) {
          orphanPaths.push(ag.workspace);
          orphanPaths.push(ag.agentDir.replace(/\/agent$/, '')); // agents/<id>/ 整个目录
        });
        inJson.forEach(function (ag) {
          orphanPaths.push(ag.workspace);
          orphanPaths.push(ag.agentDir.replace(/\/agent$/, '')); // agents/<id>/ 整个目录（阶段2）
        });

        // 执行已注册 agent 删除
        var cmdDone = 0;
        function runNextCmd() {
          if (cmdDone >= cmdQueue.length) {
            runOrphanTrash();
            return;
          }
          var agId = cmdQueue[cmdDone++];
          setStatus(agId, '🔄', '#252536', '删除中...');
          ensureSocketIO(function () {
            var runId = Date.now() + Math.random();
            cmdSocket.emit('run_command', { cmd: 'openclaw agents delete ' + agId + ' --force', runId: runId });
            // 假定 3 秒后完成（socket.io 无回调）
            setTimeout(function () {
              setStatus(agId, '✅', 'rgba(16,185,129,0.1)', '已删除');
              runNextCmd();
            }, 2500);
          });
        }

        // 执行文件夹清理（孤立 agent + 已注册 agent 残留目录）
        function runOrphanTrash() {
          // 过滤掉空路径
          var validPaths = orphanPaths.filter(function (p) { return p && p.trim(); });
          if (validPaths.length === 0) {
            showDone();
            return;
          }
          fetch(MYCLAW_API_BASE_DEL + '/api/agents/trash', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ paths: validPaths }),
          })
            .then(function (r) { return r.json(); })
            .then(function (res) {
              orphans.forEach(function (ag) {
                var allOk = (res.results || [])
                  .filter(function (r) { return r.path && (r.path.includes(ag.id)); })
                  .every(function (r) { return r.ok; });
                setStatus(ag.id, allOk ? '✅' : '⚠️', allOk ? 'rgba(16,185,129,0.1)' : 'rgba(245,158,11,0.1)', allOk ? '已移至回收站' : '部分失败');
              });
              showDone();
            })
            .catch(function () {
              orphans.forEach(function (ag) {
                setStatus(ag.id, '❌', 'rgba(239,68,68,0.1)', 'mc server 未启动');
              });
              showDone();
            });
        }

        function showDone() {
          hdrTitle.textContent = '🗑 删除完成';
          footer.innerHTML = '';
          var doneBtn = document.createElement('button');
          doneBtn.textContent = '关闭';
          doneBtn.style.cssText = 'padding:7px 24px;background:#10b981;border:none;border-radius:5px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;margin:0 auto;';
          doneBtn.onclick = function () { mask.remove(); };
          footer.appendChild(doneBtn);
        }

        runNextCmd();
      }

      showStep1();
    }

    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();
  }
})();
