/* ============================================================
 * HECIAN · AI Copilot 入口 chip(2026-05-19 加 · B5 基建)
 *
 * 位置:Sidebar 顶部(primary)+ Topbar 备份
 * 作用:用户点击 → 弹出 AI Copilot panel · 自动注入当前页上下文
 *
 * 依赖:
 * - window.HX_AI (ai-context.js · 必须先加载)
 * - window.HX_MockAPI (mock-api.js · 工具调用层)
 *
 * 引入(各 design-refs/<模块>/index.html):
 *   <script src="../../prototypes/.../shared/ai-context.js?v=20260519-b5"></script>
 *   <script type="text/babel" data-presets="react" src="../../prototypes/.../shared/ai-chip.jsx?v=20260519-b5"></script>
 *
 * chrome.jsx 已支持自动 mount(SidebarChip + TopbarChip)· 未加载时优雅降级
 *
 * 暴露:window.HX_AIChip = { SidebarChip, TopbarChip, Panel }
 * ============================================================ */

(function() {
  if (typeof React === 'undefined') {
    console.warn('[HX_AIChip] React 未加载');
    return;
  }

  // ============================================================
  // 0a. 自动加载 marked.js CDN(AI bubble markdown 渲染 · 2026-05-19)
  //     失败 → 降级 plain text · 不破坏功能
  // ============================================================
  (function autoLoadMarked(){
    if (window.marked || document.getElementById('hx-marked-loader')) return;
    var s = document.createElement('script');
    s.id = 'hx-marked-loader';
    s.src = 'https://cdn.jsdelivr.net/npm/marked@13/marked.min.js';
    s.async = true;
    s.onerror = function(){ console.warn('[HX_AIChip] marked.js 加载失败 · 降级 plain text'); };
    document.head.appendChild(s);
  })();

  // ============================================================
  // 0b. 注入 AI bubble markdown 样式(.ai-md-content)
  // ============================================================
  (function injectMdStyles(){
    if (document.getElementById('hx-ai-md-styles')) return;
    var st = document.createElement('style');
    st.id = 'hx-ai-md-styles';
    st.textContent = ''
      + '.ai-md-content p { margin: 4px 0; }'
      + '.ai-md-content ul, .ai-md-content ol { margin: 4px 0; padding-left: 18px; }'
      + '.ai-md-content li { margin: 2px 0; }'
      + '.ai-md-content h1, .ai-md-content h2, .ai-md-content h3 { font-size: 14px; font-weight: 600; margin: 6px 0 3px; color: var(--ink); }'
      + '.ai-md-content code { background: rgba(0,0,0,.06); padding: 1px 4px; border-radius: 3px; font-size: 12px; font-family: var(--font-mono, monospace); }'
      + '.ai-md-content pre { background: rgba(0,0,0,.06); padding: 6px 8px; border-radius: 4px; overflow-x: auto; margin: 4px 0; }'
      + '.ai-md-content pre code { background: none; padding: 0; }'
      + '.ai-md-content strong { font-weight: 600; }'
      + '.ai-md-content em { font-style: italic; }'
      + '.ai-md-content blockquote { border-left: 3px solid var(--line); padding-left: 8px; margin: 4px 0; color: var(--ink-3); }'
      + '.ai-md-content a { color: var(--ai-ink); text-decoration: underline; }'
      + '.ai-md-content hr { border: none; border-top: 1px dashed var(--line); margin: 8px 0; }';
    document.head.appendChild(st);
  })();

  // ============================================================
  // 0c. 对话历史持久化(2026-05-22 加 · jarvi E2E 反馈)
  //
  // Panel 用 unmount 模式(SidebarChip 的 open && Panel(...))· 关闭再开
  // useState messages 全部丢失。改 sessionStorage 持久化 · tab 关闭清空。
  //
  // 跨用户安全:存进 sessionStorage 时绑 user_id · load 时校验 · 不同用户登录返 []
  // (sessionStorage 本身按 tab 隔离 · 但同一 tab 切账号(登出 → 登入)就要靠这个 user_id 校验)
  // ============================================================
  var CONV_STORAGE_KEY = 'hx_ai_copilot_conv_v1';

  function _currentUserId() {
    try {
      var session = window.HX_getAuthSession();
      return session && session.user && session.user.id ? session.user.id : null;
    } catch (e) { return null; }
  }

  function loadConversation() {
    try {
      var uid = _currentUserId();
      if (!uid) return [];
      var raw = sessionStorage.getItem(CONV_STORAGE_KEY);
      if (!raw) return [];
      var parsed = JSON.parse(raw);
      if (parsed && parsed.user_id !== uid) return []; // 跨用户安全 · 别人历史不加载
      return (parsed && Array.isArray(parsed.messages)) ? parsed.messages : [];
    } catch (e) {
      console.warn('[HX_AIChip] sessionStorage load failed', e && e.message);
      return [];
    }
  }

  function saveConversation(messages) {
    try {
      var uid = _currentUserId();
      if (!uid) return; // 未登录不存 · 防匿名残留
      sessionStorage.setItem(CONV_STORAGE_KEY, JSON.stringify({
        user_id: uid,
        messages: messages || [],
        updated_at: Date.now(),
      }));
    } catch (e) {
      console.warn('[HX_AIChip] sessionStorage save failed', e && e.message);
    }
  }

  function clearConversation() {
    try { sessionStorage.removeItem(CONV_STORAGE_KEY); } catch (e) {}
  }

  // ============================================================
  // 1. SidebarChip · 嵌入 Sidebar 顶部(brand 下方,nav 上方)
  //    primary 视觉:大按钮 + AI 紫色渐变 + 2 行布局
  //    高度 ~88px · 跟主区域 KPI 卡视觉对齐(2026-05-19 调整)
  // ============================================================
  function SidebarChip() {
    var ts = React.useState(false);
    var open = ts[0];
    var setOpen = ts[1];

    return React.createElement(React.Fragment, null,
      React.createElement('button', {
        onClick: function(){ setOpen(true); },
        style: {
          margin: '12px 10px 8px',
          padding: '14px 14px',
          display: 'flex', flexDirection: 'column',
          alignItems: 'stretch', justifyContent: 'center',
          gap: 8,
          background: 'linear-gradient(135deg, var(--ai-soft) 0%, transparent 100%)',
          border: '1px solid var(--ai)',
          borderRadius: 8,
          cursor: 'pointer',
          color: 'var(--ai-ink)',
          fontFamily: 'inherit',
          width: 'calc(100% - 20px)',
          minHeight: 88,
          boxSizing: 'border-box',
          textAlign: 'left',
          transition: 'background 0.2s',
        },
        title: '问小绚 · 承当前页上下文',
      },
        // 主行: emoji + 标题 + 快捷键
        React.createElement('div', {
          style: { display: 'flex', alignItems: 'center', gap: 10 }
        },
          React.createElement('span', { style: { fontSize: 18, lineHeight: 1 } }, '✨'),
          React.createElement('span', { style: { flex: 1, fontSize: 14, fontWeight: 600 } }, '小绚'),
          React.createElement('span', { className: 'mono', style: { fontSize: 9, opacity: 0.7, fontWeight: 500 } }, '⌘J')
        ),
        // 副行: 上下文描述
        React.createElement('div', {
          style: {
            fontSize: 10, fontWeight: 400, opacity: 0.75,
            letterSpacing: '.02em', lineHeight: 1.4,
          }
        }, '承当前页上下文 · 等 LLM 接入')
      ),
      open && React.createElement(Panel, { onClose: function(){ setOpen(false); } })
    );
  }

  // ============================================================
  // 2. TopbarChip · Topbar 内嵌 chip(轻量备份入口)
  // ============================================================
  function TopbarChip() {
    var ts = React.useState(false);
    var open = ts[0];
    var setOpen = ts[1];

    return React.createElement(React.Fragment, null,
      React.createElement('button', {
        onClick: function(){ setOpen(true); },
        title: '问小绚 · 承当前页上下文',
        style: {
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '6px 10px',
          border: '1px solid var(--ai)',
          background: 'var(--ai-soft)',
          color: 'var(--ai-ink)',
          borderRadius: 6,
          cursor: 'pointer',
          fontSize: 12,
          fontWeight: 600,
          fontFamily: 'inherit',
        }
      },
        React.createElement('span', null, '✨'),
        React.createElement('span', null, '小绚')
      ),
      open && React.createElement(Panel, { onClose: function(){ setOpen(false); } })
    );
  }

  // ============================================================
  // 3. Panel · AI Copilot 主面板(右抽屉)· 2026-05-19 接入真 LLM
  //    复用 GoNoGo v3 SSE pattern · 调 /api/ai-copilot/stream(DeepSeek)
  //    上下文 + tools 自动注入 system prompt · 多轮对话(近 8 轮)
  // ============================================================
  function Panel(props) {
    var onClose = props.onClose;
    var cs = React.useState(null);          var ctx = cs[0];            var setCtx = cs[1];
    var ts = React.useState([]);            var tools = ts[0];          var setTools = ts[1];
    // ⚠ 2026-05-22 加 · lazy init 从 sessionStorage 恢复历史(Panel unmount → mount 时复用)
    var ms = React.useState(function(){ return loadConversation(); });
    var messages = ms[0];       var setMessages = ms[1];
    var ins = React.useState('');           var input = ins[0];         var setInput = ins[1];
    var st = React.useState('');            var streaming = st[0];      var setStreaming = st[1];
    var ss = React.useState('idle');        var status = ss[0];         var setStatus = ss[1]; // idle / streaming / error
    var es = React.useState(null);          var error = es[0];          var setError = es[1];
    var ta = React.useState([]);            var toolActivity = ta[0];   var setToolActivity = ta[1];
    var scrollRef = React.useRef(null);
    var inputRef = React.useRef(null);

    React.useEffect(function(){
      if (window.HX_AI) {
        setCtx(window.HX_AI.getCurrentContext());
        setTools(window.HX_AI.getAvailableTools());
      }
      function onKey(e){ if (e.key === 'Escape') onClose(); }
      document.addEventListener('keydown', onKey);
      if (inputRef.current) { try { inputRef.current.focus(); } catch (e) {} }
      return function(){ document.removeEventListener('keydown', onKey); };
    }, []);

    // 自动滚到底
    React.useEffect(function(){
      if (scrollRef.current) {
        scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
      }
    }, [messages.length, streaming]);

    // ⚠ 2026-05-22 加 · 每次 messages 变化 → sessionStorage 持久化
    //   (含 user/assistant/tool_call/tool_result · 所有 message types · 含 toolActivity 子结构)
    React.useEffect(function(){
      saveConversation(messages);
    }, [messages]);

    // Y 方案 2026-05-22 · 用户点 ConfirmationCard "确认"按钮 → 调 endpoint
    function handleConfirmDraft(draftId, msgIndex) {
      // 拿 access_token(跟 handleSend 同款)
      var headers = window.HX_authHeaders({ 'Content-Type': 'application/json' });

      // mark message 'submitting' 防重复点击
      setMessages(function(ms){
        return ms.map(function(m, i){
          if (i === msgIndex && m.role === 'confirmation') {
            return Object.assign({}, m, { status: 'submitting' });
          }
          return m;
        });
      });

      fetch('/api/ai-copilot/confirm-draft', {
        method: 'POST',
        headers: headers,
        body: JSON.stringify({ draft_id: draftId }),
      }).then(function(resp){
        return resp.json().then(function(data){
          return { ok: resp.ok, status: resp.status, data: data };
        });
      }).then(function(out){
        if (out.ok && out.data.success) {
          // 成功:mark message confirmed + append system success message
          var result = out.data.result || {};
          var toolName = out.data.tool_name;
          var idShort = (result.customer_id || result.interaction_id || '').slice(0, 8);
          var actionLabel = toolName === 'create_customer' ? '已录入新客户'
            : toolName === 'update_customer_status' ? '已更新状态'
            : toolName === 'log_customer_interaction' ? '已记录跟进'
            : '已完成';
          var successText = '✅ ' + actionLabel + (idShort ? ' (id: ' + idShort + '...)' : '');
          setMessages(function(ms){
            var updated = ms.map(function(m, i){
              if (i === msgIndex && m.role === 'confirmation') {
                return Object.assign({}, m, { status: 'confirmed' });
              }
              return m;
            });
            return updated.concat([{ role: 'system', content: successText }]);
          });
        } else {
          // 失败:展示 error + mark cancelled(让用户重试 = 重新触发 LLM)
          var errMsg = (out.data && (out.data.message || out.data.error)) || ('HTTP ' + out.status);
          setMessages(function(ms){
            var updated = ms.map(function(m, i){
              if (i === msgIndex && m.role === 'confirmation') {
                return Object.assign({}, m, { status: 'error' });
              }
              return m;
            });
            return updated.concat([{ role: 'system', content: '❌ ' + errMsg }]);
          });
        }
      }).catch(function(err){
        setMessages(function(ms){
          var updated = ms.map(function(m, i){
            if (i === msgIndex && m.role === 'confirmation') {
              return Object.assign({}, m, { status: 'error' });
            }
            return m;
          });
          return updated.concat([{ role: 'system', content: '❌ 网络错误: ' + String(err && err.message || err) }]);
        });
      });
    }

    function handleCancelDraft(draftId, msgIndex) {
      setMessages(function(ms){
        var updated = ms.map(function(m, i){
          if (i === msgIndex && m.role === 'confirmation') {
            return Object.assign({}, m, { status: 'cancelled' });
          }
          return m;
        });
        return updated.concat([{ role: 'system', content: '🚫 已取消' }]);
      });
    }

    function handleSend() {
      var text = (input || '').trim();
      if (!text || status === 'streaming') return;
      var userMsg = { role: 'user', content: text };
      var prevMessages = messages;
      var newMessages = prevMessages.concat([userMsg]);
      setMessages(newMessages);
      setInput('');
      setStreaming('');
      setToolActivity([]);
      setStatus('streaming');
      setError(null);

      // 本轮 tool activity 累积 · 完成时挂到 aiMsg
      var localToolActivity = [];
      // Y 方案 · 本轮如果 server 返了 requires_confirmation · 收尾时插入 confirmation message
      var pendingConfirmation = null;

      // 从 localStorage 拿 supabase session access_token 放 Authorization header
      // (prototypes/index.html 用 supabase-js UMD · session 存 localStorage 不写 cookies ·
      //  所以 server-side ai-copilot/stream 必须从 header 读 token verify · 不能从 cookie)
      var headers = window.HX_authHeaders({ 'Content-Type': 'application/json' });

      // AI 端点单源(2026-07-01):默认走哪条路由后端 /api/ai-copilot/config(同一个 AGENT_SERVICE_* env 单源):
      //   服务机(配了 env)→ 新底座 stream-via-agent · Vercel(未配)→ 老路 stream · 不再靠改本机文件。
      //   localStorage.hx_ai_via_agent 可临时覆盖('1' 强制新 / '0' 强制老)。ai-pet.jsx 同源共用此解析器 + 缓存。
      if (!window.HX_resolveAiEndpoint) {
        window.HX_resolveAiEndpoint = function () {
          try {
            var ls = localStorage.getItem('hx_ai_via_agent');
            if (ls === '1') return Promise.resolve('/api/ai-copilot/stream-via-agent');
            if (ls === '0') return Promise.resolve('/api/ai-copilot/stream');
          } catch (e) {}
          if (!window.__hxAiEndpointPromise) {
            window.__hxAiEndpointPromise = fetch('/api/ai-copilot/config')
              .then(function (r) { return r.ok ? r.json() : { viaAgent: false }; })
              .then(function (c) { return (c && c.viaAgent) ? '/api/ai-copilot/stream-via-agent' : '/api/ai-copilot/stream'; })
              .catch(function () { return '/api/ai-copilot/stream'; });
          }
          return window.__hxAiEndpointPromise;
        };
      }
      window.HX_resolveAiEndpoint().then(function (AI_ENDPOINT) {
        return fetch(AI_ENDPOINT, {
          method: 'POST',
          headers: headers,
          body: JSON.stringify({
            query: text,
            context: ctx || {},
            history: prevMessages,
          }),
        });
      }).then(function(resp){
        if (!resp.ok || !resp.body) {
          return resp.json().catch(function(){ return {}; }).then(function(errBody){
            throw new Error(errBody.error || 'HTTP ' + resp.status);
          });
        }
        var reader = resp.body.getReader();
        var decoder = new TextDecoder('utf-8');
        var buffer = '';
        var currentEvent = null;
        var full = '';

        function pump() {
          return reader.read().then(function(chunk){
            if (chunk.done) {
              // 收尾:push final AI msg(含本轮 toolActivity · 保留 history)
              var aiMsg = {
                role: 'assistant',
                content: full || '(空回答)',
                toolActivity: localToolActivity.slice(),
              };
              var newMsgs = [aiMsg];
              // Y 方案 · 如本轮 server 返了 requires_confirmation · 追加 confirmation card 到对话流
              if (pendingConfirmation) {
                newMsgs.push(pendingConfirmation);
              }
              setMessages(function(ms){ return ms.concat(newMsgs); });
              setStreaming('');
              setToolActivity([]);
              setStatus('idle');
              return;
            }
            buffer += decoder.decode(chunk.value, { stream: true });
            var lines = buffer.split(/\r?\n/);
            buffer = lines.pop() || '';
            for (var i = 0; i < lines.length; i++) {
              var line = lines[i];
              if (line.indexOf('event:') === 0) {
                currentEvent = line.slice(6).trim();
              } else if (line.indexOf('data:') === 0) {
                var payload = line.slice(5).trim();
                if (!payload) continue;
                var json;
                try { json = JSON.parse(payload); } catch (e) { continue; }
                if (currentEvent === 'chunk') {
                  var delta = json.text || '';
                  full += delta;
                  setStreaming(function(s){ return s + delta; });
                } else if (currentEvent === 'tool_call') {
                  localToolActivity.push({ type: 'call', name: json.name, args: json.args });
                  setToolActivity(localToolActivity.slice());
                } else if (currentEvent === 'tool_result') {
                  localToolActivity.push({ type: 'result', name: json.name, result: json.result });
                  setToolActivity(localToolActivity.slice());
                  // Y 方案 · 拦截 requires_confirmation tool_result → 转化成 confirmation card
                  if (json.result && json.result.requires_confirmation === true && json.result.draft_id) {
                    pendingConfirmation = {
                      role: 'confirmation',
                      tool_name: json.name,
                      draft_id: json.result.draft_id,
                      human_preview: json.result.human_preview || '请确认',
                      status: 'pending', // pending / submitting / confirmed / cancelled / error
                    };
                  }
                } else if (currentEvent === 'end') {
                  if (json.full) full = json.full;
                } else if (currentEvent === 'error') {
                  throw new Error(json.message || '未知错误');
                }
              } else if (line === '') {
                currentEvent = null;
              }
            }
            return pump();
          });
        }
        return pump();
      }).catch(function(e){
        setError(String(e && e.message || e));
        setStatus('error');
        setStreaming('');
      });
    }

    // 渲染 tool activity · 用于 messages 历史 + 当前 streaming 时
    var renderToolActivity = function(activity) {
      if (!activity || activity.length === 0) return null;
      return React.createElement('div', {
        style: {
          marginBottom: 6,
          maxWidth: '85%',
          alignSelf: 'flex-start',
          display: 'flex', flexDirection: 'column', gap: 3,
        }
      },
        activity.map(function(a, idx){
          if (a.type === 'call') {
            var argsStr = '';
            try { argsStr = JSON.stringify(a.args).slice(0, 80); } catch (e) { argsStr = ''; }
            return React.createElement('div', {
              key: idx,
              style: {
                display: 'flex', alignItems: 'center', gap: 6,
                padding: '4px 8px',
                background: 'var(--ai-soft)',
                color: 'var(--ai-ink)',
                borderRadius: 4,
                fontSize: 10,
                fontFamily: 'var(--font-mono, monospace)',
              }
            },
              React.createElement('span', null, '✦'),
              React.createElement('code', { style: { fontSize: 10, background: 'none', color: 'var(--ai-ink)' } }, a.name + ' ' + argsStr)
            );
          }
          if (a.type === 'result') {
            // result 摘要(取最有意义字段)
            var summary = '';
            var r = a.result || {};
            if (r.error) summary = '❌ ' + String(r.error).slice(0, 60);
            else if (r.name) summary = r.name + (r.rating ? ' · ' + r.rating + ' 级' : '') + (r.industry ? ' · ' + r.industry : '');
            else if (r.suggest) summary = '建议 ' + r.suggest + ' · 置信度 ' + (r.confidence || '?');
            else if (r.hits) summary = r.hits.length + ' hits · ' + (r.hits[0] && r.hits[0].title || '');
            else if (r.candidates) summary = r.candidates.length + ' 候选 · ' + (r.candidates[0] && r.candidates[0].name || '');
            else summary = '✓';
            return React.createElement('div', {
              key: idx,
              style: {
                display: 'flex', alignItems: 'center', gap: 6,
                padding: '4px 8px',
                color: 'var(--ink-3)',
                fontSize: 10,
                fontFamily: 'var(--font-mono, monospace)',
              }
            },
              React.createElement('span', { style: { color: 'var(--ok, #2f6b3a)' } }, '✓'),
              React.createElement('code', { style: { fontSize: 10, background: 'none', color: 'var(--ink-2)' } }, a.name),
              React.createElement('span', { style: { fontSize: 10 } }, '→ ' + summary)
            );
          }
          return null;
        })
      );
    };

    function handleKeyDown(e) {
      if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault();
        handleSend();
      }
    }

    // ============================================================
    // 渲染
    // ============================================================
    var ctxCompactItems = [];
    if (ctx) {
      ctxCompactItems.push(React.createElement('code', { key: 'p', style: { fontSize: 11, color: 'var(--ai-ink)', background: 'var(--ai-soft)', padding: '2px 6px', borderRadius: 3 } }, ctx.page));
      if (ctx.entity) ctxCompactItems.push(React.createElement('span', { key: 'e', style: { fontSize: 11, color: 'var(--ink-3)' } }, ctx.entity + (ctx.entityId ? ' · ' + ctx.entityId : '')));
      if (ctx.role) ctxCompactItems.push(React.createElement('span', { key: 'r', className: 'mono', style: { fontSize: 10, color: 'var(--ink-4)' } }, ctx.role));
    }

    // Y 方案 2026-05-22 · 渲染 ConfirmationCard(对话流内嵌 · 非 modal)
    var renderConfirmationCard = function(m, i) {
      var s = m.status || 'pending';
      var isDone = s === 'confirmed' || s === 'cancelled' || s === 'error';
      var statusBadge = s === 'confirmed' ? '✅ 已确认'
        : s === 'cancelled' ? '🚫 已取消'
        : s === 'error' ? '❌ 失败'
        : s === 'submitting' ? '⏳ 提交中...'
        : '📋 待确认';
      var statusColor = s === 'confirmed' ? 'var(--ok, #2f6b3a)'
        : s === 'cancelled' || s === 'error' ? 'var(--danger, #a52a2a)'
        : 'var(--ai-ink)';

      return React.createElement('div', {
        key: i,
        style: {
          maxWidth: '90%',
          margin: '6px 0 10px',
          padding: '12px 14px',
          background: 'var(--ai-soft, #ede9fe)',
          border: '1px solid var(--ai, #5b21b6)',
          borderRadius: 10,
          fontSize: 13, lineHeight: 1.55,
        }
      },
        // Header: 状态徽章
        React.createElement('div', {
          style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, fontWeight: 600, color: statusColor, marginBottom: 6 }
        }, statusBadge),
        // human_preview 文本
        React.createElement('div', {
          style: { color: 'var(--ink)', marginBottom: isDone ? 0 : 10, whiteSpace: 'pre-wrap' }
        }, m.human_preview),
        // 按钮组(仅 pending / submitting 显示)
        !isDone && React.createElement('div', {
          style: { display: 'flex', gap: 8, justifyContent: 'flex-end' }
        },
          React.createElement('button', {
            onClick: function(){ if (s !== 'submitting') handleCancelDraft(m.draft_id, i); },
            disabled: s === 'submitting',
            style: {
              padding: '6px 14px', borderRadius: 6, border: '1px solid var(--line)',
              background: 'var(--panel)', color: 'var(--ink-3)', fontSize: 12,
              cursor: s === 'submitting' ? 'not-allowed' : 'pointer', fontFamily: 'inherit',
            }
          }, '取消'),
          React.createElement('button', {
            onClick: function(){ if (s !== 'submitting') handleConfirmDraft(m.draft_id, i); },
            disabled: s === 'submitting',
            style: {
              padding: '6px 14px', borderRadius: 6, border: 'none',
              background: 'var(--accent, #8a3a2a)', color: '#fff', fontSize: 12, fontWeight: 600,
              cursor: s === 'submitting' ? 'not-allowed' : 'pointer', fontFamily: 'inherit',
            }
          }, s === 'submitting' ? '⏳ 提交中' : '✓ 确认')
        )
      );
    };

    // Y 方案 · 渲染 system message(✅ 已录入 / 🚫 已取消 / ❌ 错误等)
    var renderSystemMsg = function(m, i) {
      return React.createElement('div', {
        key: i,
        style: {
          margin: '4px 0 10px',
          padding: '6px 12px',
          background: 'var(--bg-elev-2, #fafafa)',
          border: '1px dashed var(--line-2)',
          borderRadius: 6,
          fontSize: 12, color: 'var(--ink-3)',
          fontFamily: 'inherit',
        }
      }, m.content);
    };

    var renderMsgBubble = function(m, i) {
      // Y 方案分发:confirmation / system 类型走专门渲染
      if (m.role === 'confirmation') return renderConfirmationCard(m, i);
      if (m.role === 'system') return renderSystemMsg(m, i);

      var isUser = m.role === 'user';
      // AI message · marked.js 渲染 markdown · failed → plain text fallback(2026-05-19)
      var content;
      if (isUser) {
        content = m.content;
      } else if (window.marked && typeof window.marked.parse === 'function') {
        try {
          content = React.createElement('div', {
            className: 'ai-md-content',
            dangerouslySetInnerHTML: { __html: window.marked.parse(m.content || '') }
          });
        } catch (e) {
          content = m.content;
        }
      } else {
        content = m.content;
      }
      var bubble = React.createElement('div', {
        style: {
          maxWidth: '85%',
          padding: '8px 12px',
          borderRadius: isUser ? '12px 12px 4px 12px' : '12px 12px 12px 4px',
          background: isUser ? 'var(--ai)' : 'var(--panel)',
          color: isUser ? '#fff' : 'var(--ink)',
          fontSize: 13, lineHeight: 1.6,
          whiteSpace: isUser ? 'pre-wrap' : 'normal',
          wordBreak: 'break-word',
          border: isUser ? 'none' : '1px solid var(--line-2)',
        }
      }, content);
      if (isUser) {
        return React.createElement('div', {
          key: i,
          style: { display: 'flex', justifyContent: 'flex-end', marginBottom: 10 }
        }, bubble);
      }
      // AI message · tool activity 在 bubble 之上
      return React.createElement('div', {
        key: i,
        style: {
          display: 'flex', flexDirection: 'column', alignItems: 'flex-start',
          marginBottom: 10,
        }
      },
        m.toolActivity && m.toolActivity.length > 0 ? renderToolActivity(m.toolActivity) : null,
        bubble
      );
    };

    return React.createElement('div', {
      onClick: onClose,
      style: {
        position: 'fixed', top: 0, right: 0, bottom: 0, left: 0,
        background: 'rgba(0,0,0,.3)', zIndex: 9999,
      }
    },
      React.createElement('aside', {
        onClick: function(e){ e.stopPropagation(); },
        style: {
          position: 'absolute', top: 0, right: 0, bottom: 0,
          width: 460, background: 'var(--bg-elev)',
          borderLeft: '1px solid var(--line)',
          display: 'flex', flexDirection: 'column',
          boxShadow: '-8px 0 24px rgba(0,0,0,.12)',
        }
      },
        // Header
        React.createElement('div', {
          style: {
            padding: '14px 18px',
            borderBottom: '1px solid var(--line-2)',
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          }
        },
          React.createElement('div', null,
            React.createElement('div', { style: { fontSize: 14, fontWeight: 700, color: 'var(--ai-ink)' } },
              React.createElement('span', { style: { marginRight: 6 } }, '✨'),
              '小绚 · Copilot'
            ),
            React.createElement('div', { className: 'mono', style: { fontSize: 10, color: 'var(--ink-4)', marginTop: 2 } },
              '搜索框形态 · 跟右下小绚悬浮宠物同源 · 同一套 DeepSeek + MCP tool'
            )
          ),
          // 右侧按钮组:[清空对话] + [×]
          React.createElement('div', {
            style: { display: 'flex', gap: 6, alignItems: 'center' }
          },
            // ⚠ 2026-05-22 加 · 清空对话按钮(messages 非空才显示 · 防误点 + window.confirm 二次确认)
            messages.length > 0 && React.createElement('button', {
              onClick: function() {
                if (window.confirm('清空当前对话历史?(无法撤销)')) {
                  clearConversation();
                  setMessages([]);
                  setStreaming('');
                  setToolActivity([]);
                  setError(null);
                }
              },
              title: '清空当前对话历史(sessionStorage · 本 tab)',
              style: {
                padding: '4px 10px', borderRadius: 4,
                border: '1px solid var(--line)', background: 'var(--bg-elev-2,#fafafa)',
                cursor: 'pointer', color: 'var(--ink-3)', fontSize: 11,
                fontFamily: 'inherit',
              }
            }, '🗑 清空'),
            React.createElement('button', {
              onClick: onClose,
              style: {
                width: 28, height: 28, borderRadius: 4,
                border: 'none', background: 'transparent',
                cursor: 'pointer', color: 'var(--ink-3)', fontSize: 18,
              }
            }, '×')
          )
        ),

        // Compact ctx + tools (1 行 chip)
        React.createElement('div', {
          style: {
            padding: '10px 18px',
            borderBottom: '1px solid var(--line-2)',
            background: 'var(--bg-elev-2,#fafafa)',
            display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6,
          }
        },
          ctxCompactItems.length > 0
            ? ctxCompactItems
            : React.createElement('span', { style: { fontSize: 11, color: 'var(--ink-4)', fontStyle: 'italic' } }, '当前页未声明 ai-context · AI 仍可对话 · 子页加 data-ai-page="..." 上下文更精准'),
          tools.length > 0 && React.createElement('span', { style: { marginLeft: 'auto', fontSize: 10, color: 'var(--ink-4)' } },
            tools.filter(function(t){ return t.available; }).length + ' / ' + tools.length + ' tools ✓'
          )
        ),

        // Chat 滚动区
        React.createElement('div', {
          ref: scrollRef,
          style: {
            flex: 1, overflowY: 'auto',
            padding: '14px 18px',
            display: 'flex', flexDirection: 'column',
          }
        },
          // 空态欢迎
          messages.length === 0 && !streaming && React.createElement('div', {
            style: {
              padding: '20px 16px',
              background: 'var(--ai-soft)',
              borderRadius: 8,
              border: '1px dashed var(--ai)',
              fontSize: 12, color: 'var(--ai-ink)', lineHeight: 1.7,
            }
          },
            React.createElement('div', { style: { fontWeight: 600, marginBottom: 6, fontSize: 13 } }, '✨ 你好,我是小绚'),
            ctx
              ? React.createElement('div', null,
                  '我看到你在 ',
                  React.createElement('code', { style: { background: 'var(--bg-elev)', padding: '1px 5px', borderRadius: 3, fontSize: 11 } }, ctx.page),
                  ctx.desc ? ' · ' + ctx.desc : '',
                  '。',
                  React.createElement('br', null),
                  '可以问我:这页的数据 / 产品操作 / 业务行业问题。'
                )
              : React.createElement('div', null, '随便问 · 我会基于当前页面 + 和绚业务给你回答。')
          ),
          // 历史消息
          messages.map(renderMsgBubble),
          // 当前 streaming round 的 tool activity(显示在 streaming bubble 上方)
          toolActivity.length > 0 && React.createElement('div', {
            style: { display: 'flex', justifyContent: 'flex-start', marginBottom: 6 }
          }, renderToolActivity(toolActivity)),
          // streaming(AI 当前打字机)
          streaming && React.createElement('div', {
            style: { display: 'flex', justifyContent: 'flex-start', marginBottom: 10 }
          },
            React.createElement('div', {
              style: {
                maxWidth: '85%',
                padding: '8px 12px',
                borderRadius: '12px 12px 12px 4px',
                background: 'var(--panel)',
                color: 'var(--ink)',
                fontSize: 13, lineHeight: 1.6,
                whiteSpace: 'pre-wrap', wordBreak: 'break-word',
                border: '1px solid var(--line-2)',
              }
            }, streaming, React.createElement('span', { style: { color: 'var(--ai)', marginLeft: 2 } }, '▊'))
          ),
          // status: streaming + 还没字
          status === 'streaming' && !streaming && React.createElement('div', {
            style: { fontSize: 11, color: 'var(--ink-4)', fontStyle: 'italic', padding: '8px 0' }
          }, '✨ 思考中...'),
          // 错误
          error && React.createElement('div', {
            style: {
              padding: '10px 12px',
              border: '1px solid var(--danger,#a52a2a)',
              background: 'rgba(165, 42, 42, .08)',
              borderRadius: 6,
              fontSize: 12, color: 'var(--danger,#a52a2a)',
              marginBottom: 10,
            }
          },
            React.createElement('div', { style: { fontWeight: 600, marginBottom: 4 } }, '调用失败'),
            React.createElement('div', { className: 'mono', style: { fontSize: 11 } }, error)
          )
        ),

        // Input footer
        // ⚠ 临时方案 2026-05-22 · jarvi 反馈:右下角底部 nav-hud(.nav-hud · shell.css L250)
        //   跟 AI 输入框区域撞在屏幕右下角。临时把 footer 底部 padding 从 12 → 80
        //   把输入框整体抬高 ≈ 68px(HUD 高度 40 + bottom 16 + 呼吸 24)避让。
        //   未来 HUD bar 拿掉后,把 paddingBottom 改回 12 即可恢复(只改一处 · 单源)。
        React.createElement('div', {
          style: {
            padding: '12px 18px 80px 18px', // ⚠ TEMP: bottom=80 避让底部 HUD bar
            borderTop: '1px solid var(--line-2)',
            background: 'var(--bg-elev-2,#fafafa)',
          }
        },
          React.createElement('div', {
            style: {
              display: 'flex', gap: 8, alignItems: 'flex-end',
            }
          },
            React.createElement('textarea', {
              ref: inputRef,
              value: input,
              onChange: function(e){ setInput(e.target.value); },
              onKeyDown: handleKeyDown,
              placeholder: status === 'streaming' ? 'AI 回复中...' : '问点什么(Enter 发送 · Shift+Enter 换行)',
              disabled: status === 'streaming',
              rows: 2,
              style: {
                flex: 1,
                padding: '8px 10px',
                fontSize: 13,
                fontFamily: 'inherit',
                color: 'var(--ink)',
                background: 'var(--panel)',
                border: '1px solid var(--line)',
                borderRadius: 6,
                resize: 'none',
                outline: 'none',
                opacity: status === 'streaming' ? 0.6 : 1,
              }
            }),
            React.createElement('button', {
              onClick: handleSend,
              disabled: status === 'streaming' || !(input || '').trim(),
              style: {
                padding: '8px 14px',
                background: status === 'streaming' || !(input || '').trim() ? 'var(--chip)' : 'var(--ai)',
                color: status === 'streaming' || !(input || '').trim() ? 'var(--ink-4)' : '#fff',
                border: 'none',
                borderRadius: 6,
                fontSize: 13,
                fontWeight: 600,
                fontFamily: 'inherit',
                cursor: status === 'streaming' || !(input || '').trim() ? 'not-allowed' : 'pointer',
                alignSelf: 'stretch',
              }
            }, status === 'streaming' ? '...' : '发送')
          ),
          React.createElement('div', {
            style: { marginTop: 6, fontSize: 10, color: 'var(--ink-4)', display: 'flex', justifyContent: 'space-between' }
          },
            React.createElement('span', null, messages.length > 0 ? '已 ' + messages.length + ' 条 · 多轮上下文' : '空对话'),
            React.createElement('span', { className: 'mono' }, 'DeepSeek · SSE 流式')
          )
        )
      )
    );
  }

  window.HX_AIChip = {
    SidebarChip: SidebarChip,
    TopbarChip: TopbarChip,
    Panel: Panel,
  };

  console.info('[HX_AIChip] loaded · SidebarChip / TopbarChip / Panel');
})();
