<script setup>
import { ref, reactive, onMounted } from 'vue'
import { NEmpty, NSpin, NIcon, NPopconfirm, NButton, useMessage } from 'naive-ui'
import { getWorkflowsWithRuns, getRunDetail, deleteRun } from '../../api/runHistory'
import {
  PlayCircleOutline,
  ChevronForwardOutline,
  TrashOutline,
  ChatbubbleEllipsesOutline,
  SparklesOutline,
  FlaskOutline,
  RocketOutline,
  ColorWandOutline,
  GitBranchOutline,
  ExitOutline,
  DownloadOutline
} from '@vicons/ionicons5'
import { unwrapNodeOutput, nodeOutputStatus, nodeOutputMessage } from '../../composables/useWorkflowRunner'

const message = useMessage()

const loading = ref(false)
const workflows = ref([])   // [{ workflowId, workflowName, runs }]
const selectedRun = ref(null)
const runDetail = ref(null) // { run, nodes }
const expandedLogs = reactive({})
const expandedContext = reactive({})
/** 思考过程折叠状态：key=nodeId，未设置默认展开 */
const expandedThinking = reactive({})
/** 折叠的智能体（workflowId 集合），默认全部展开 */
const collapsedWfs = reactive({})

function toggleWfCollapse(workflowId) {
  collapsedWfs[workflowId] = !collapsedWfs[workflowId]
}

const nodeTypeInfo = {
  userInput:   { color: '#10B981', typeLabel: '用户输入', icon: ChatbubbleEllipsesOutline },
  llm:         { color: '#8B5CF6', typeLabel: '大模型',   icon: SparklesOutline },
  skill:       { color: '#06B6D4', typeLabel: '技能',     icon: FlaskOutline },
  agent:       { color: '#F59E0B', typeLabel: '智能体',   icon: RocketOutline },
  smartAction: { color: '#EC4899', typeLabel: '智能操作', icon: ColorWandOutline },
  condition:   { color: '#F97316', typeLabel: '条件',     icon: GitBranchOutline },
  output:      { color: '#EF4444', typeLabel: '输出',     icon: ExitOutline }
}

onMounted(loadData)

async function loadData() {
  loading.value = true
  try {
    const res = await getWorkflowsWithRuns()
    workflows.value = res.data || []
  } catch (e) {
    message.error('加载运行记录失败')
  } finally {
    loading.value = false
  }
}

async function selectRun(run) {
  selectedRun.value = run
  // reactive 对象无 .value，需逐键删除以正确重置折叠状态
  Object.keys(expandedLogs).forEach(k => delete expandedLogs[k])
  Object.keys(expandedContext).forEach(k => delete expandedContext[k])
  Object.keys(expandedThinking).forEach(k => delete expandedThinking[k])
  try {
    const res = await getRunDetail(run.id)
    runDetail.value = res.data
  } catch (e) {
    message.error('加载运行详情失败')
  }
}

async function handleDelete(runId) {
  try {
    await deleteRun(runId)
    message.success('已删除')
    if (selectedRun.value?.id === runId) {
      selectedRun.value = null
      runDetail.value = null
    }
    await loadData()
  } catch (e) {
    message.error('删除失败')
  }
}

function statusText(s) {
  return { running: '执行中', success: '成功', failed: '失败', skipped: '跳过' }[s?.toLowerCase()] || s || '未知'
}

function statusClass(s) {
  return 'status-' + (s || 'unknown').toLowerCase()
}

function formatTime(t) {
  if (!t) return '-'
  // 兼容数组和字符串格式
  if (Array.isArray(t)) {
    const [y, m, d, h, min, s] = t
    return `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')} ${String(h).padStart(2, '0')}:${String(min).padStart(2, '0')}:${String(s || 0).padStart(2, '0')}`
  }
  return String(t).replace('T', ' ').substring(0, 19)
}

function duration(run) {
  if (!run.startedAt || !run.completedAt) return '-'
  let start = new Date(Array.isArray(run.startedAt) ? run.startedAt.join('-') : run.startedAt)
  let end = new Date(Array.isArray(run.completedAt) ? run.completedAt.join('-') : run.completedAt)
  let diff = Math.round((end - start) / 1000)
  if (diff < 60) return diff + ' 秒'
  if (diff < 3600) return Math.floor(diff / 60) + ' 分 ' + (diff % 60) + ' 秒'
  return Math.floor(diff / 3600) + ' 时 ' + Math.floor((diff % 3600) / 60) + ' 分'
}

function parseJson(str) {
  if (!str) return null
  try { return JSON.parse(str) } catch { return null }
}

function toggleLogs(nodeId) {
  expandedLogs[nodeId] = !expandedLogs[nodeId]
}

function toggleContext(nodeId) {
  expandedContext[nodeId] = !expandedContext[nodeId]
}

function toggleThinking(nodeId) {
  expandedThinking[nodeId] = !expandedThinking[nodeId]
}

/**
 * 将 contextSnapshot 中的值格式化为可显示字符串：
 * - 对象/数组：JSON.stringify（缩进 2）
 * - 字符串：原样返回，超长时由 CSS 限制高度
 * - 其他：String(value)
 */
function formatContextValue(value) {
  if (value === null || value === undefined) return 'null'
  if (typeof value === 'object') return JSON.stringify(value, null, 2)
  return String(value)
}

function logTypeIcon(type) {
  return { TOOL_CALL: '🔧', TOOL_RESULT: '📋', THINKING: '💭', ERROR: '❌', INFO: 'ℹ️' }[type] || '•'
}

function logTypeLabel(type) {
  return { TOOL_CALL: '调用工具', TOOL_RESULT: '工具返回', THINKING: '思考', ERROR: '错误', INFO: '信息' }[type] || type
}

/**
 * 从日志数组中提取思考过程内容（THINKING 类型日志）。
 * 工作流编辑器的运行结果是实时流式累加，运行记录页则从持久化日志中还原。
 */
function extractThinking(logsJson) {
  const logs = parseJson(logsJson)
  if (!Array.isArray(logs)) return null
  const thinking = logs.filter(l => l.type === 'THINKING').map(l => l.message || '').join('')
  return thinking || null
}

/**
 * 过滤掉 THINKING 类型日志：思考过程已在节点卡片上方集中展示，
 * 列表展开时再显示会造成内容重复（流式 token 拼接的多条 message 与上方完整思考重复）。
 */
function nonThinkingLogs(logsJson) {
  const logs = parseJson(logsJson)
  if (!Array.isArray(logs)) return []
  return logs.filter(l => l.type !== 'THINKING')
}

/**
 * 下载某次运行的工作目录 zip。
 * 后端接口：GET /api/workflows/{workflowId}/runs/{runId}/download
 * 响应已是 attachment + Content-Disposition，浏览器直接导航即可触发下载，
 * 无需经 axios 拦截器（拦截器要求 res.code===200，二进制响应会失败）。
 */
function downloadWorkspace(run) {
  if (!run?.workflowId || !run?.runId) {
    message.error('运行信息缺失，无法下载')
    return
  }
  window.location.href = `/api/workflows/${run.workflowId}/runs/${run.runId}/download`
}
</script>

<template>
  <div class="run-history-page">
    <div class="page-header">
      <h2>运行记录</h2>
    </div>

    <div v-if="loading" style="text-align: center; padding: 60px;">
      <n-spin size="large" />
    </div>

    <n-empty v-else-if="workflows.length === 0" description="暂无运行记录" style="margin-top: 80px;" />

    <div v-else class="history-layout">
      <!-- 左侧：工作流列表 -->
      <div class="left-panel">
        <div v-for="wf in workflows" :key="wf.workflowId" class="wf-group">
          <div class="wf-group-title" @click="toggleWfCollapse(wf.workflowId)">
            <n-icon size="12" class="wf-collapse-icon" :class="{ collapsed: collapsedWfs[wf.workflowId] }">
              <ChevronForwardOutline />
            </n-icon>
            <n-icon size="14"><PlayCircleOutline /></n-icon>
            <span>{{ wf.displayName || wf.workflowName }}</span>
            <span class="wf-run-count">{{ wf.runs.length }}</span>
          </div>
          <div v-if="wf.tags && wf.tags.length" class="wf-tags">
            <span v-for="tag in wf.tags" :key="tag.tagId" class="wf-tag">{{ tag.tagName }}</span>
          </div>
          <div v-show="!collapsedWfs[wf.workflowId]">
            <div
              v-for="run in wf.runs"
              :key="run.id"
              class="run-item"
              :class="{ active: selectedRun?.id === run.id }"
              @click="selectRun(run)"
            >
              <div class="run-item-top">
                <span class="run-status-dot" :class="statusClass(run.status)"></span>
                <span class="run-id">#{{ run.runId }}</span>
                <span class="run-badge" :class="statusClass(run.status)">{{ statusText(run.status) }}</span>
              </div>
              <div class="run-item-bottom">
                <span>{{ formatTime(run.startedAt) }}</span>
                <span v-if="run.completedAt" class="run-duration">{{ duration(run) }}</span>
              </div>
              <n-popconfirm @positive-click.stop="handleDelete(run.id)">
                <template #trigger>
                  <button class="run-delete-btn" @click.stop>
                    <n-icon size="12"><TrashOutline /></n-icon>
                  </button>
                </template>
                确定删除此运行记录？
              </n-popconfirm>
            </div>
          </div>
        </div>
      </div>

      <!-- 右侧：运行详情 -->
      <div class="right-panel">
        <div v-if="!runDetail" class="empty-detail">
          <n-empty description="选择左侧运行记录查看详情" />
        </div>
        <template v-else>
          <div class="detail-header">
            <div class="detail-title">
              <span class="run-status-dot" :class="statusClass(runDetail.run.status)"></span>
              运行 #{{ runDetail.run.runId }}
              <span class="run-badge" :class="statusClass(runDetail.run.status)">{{ statusText(runDetail.run.status) }}</span>
            </div>
            <div class="detail-meta">
              <span>开始: {{ formatTime(runDetail.run.startedAt) }}</span>
              <span v-if="runDetail.run.completedAt">完成: {{ formatTime(runDetail.run.completedAt) }}</span>
              <span v-if="runDetail.run.completedAt">耗时: {{ duration(runDetail.run) }}</span>
            </div>
            <div v-if="runDetail.run.error" class="detail-error">{{ runDetail.run.error }}</div>
          </div>

          <!-- 节点结果列表 -->
          <div class="detail-nodes">
            <div
              v-for="node in runDetail.nodes"
              :key="node.id"
              class="node-result-card"
              :style="{ borderLeftColor: nodeTypeInfo[node.nodeType]?.color || '#666' }"
            >
              <div class="nrc-header" @click="toggleLogs(node.nodeId)" style="cursor: pointer">
                <div class="nrc-icon" :style="{ background: nodeTypeInfo[node.nodeType]?.color || '#666' }">
                  <n-icon size="12"><component :is="nodeTypeInfo[node.nodeType]?.icon || PlayCircleOutline" /></n-icon>
                </div>
                <div class="nrc-info">
                  <span class="nrc-label">{{ node.label || node.nodeId }}</span>
                  <span class="nrc-type">{{ nodeTypeInfo[node.nodeType]?.typeLabel || node.nodeType }}</span>
                </div>
                <span v-if="node.logs && nonThinkingLogs(node.logs).length" class="nrc-log-count">{{ nonThinkingLogs(node.logs).length }} 条日志</span>
                <span class="nrc-status" :class="statusClass(node.status)">{{ statusText(node.status) }}</span>
                <span v-if="node.logs && nonThinkingLogs(node.logs).length" class="nrc-expand" :class="{ expanded: expandedLogs[node.nodeId] }">▶</span>
              </div>
              <div class="nrc-vars" v-if="parseJson(node.output)">
                <!-- envelope 失败（status=400）时显示红色徽章 + message -->
                <div v-if="nodeOutputStatus(parseJson(node.output)) === 400" class="nrc-env-fail">
                  <span class="nrc-env-badge failed">失败</span>
                  <span class="nrc-env-msg">{{ nodeOutputMessage(parseJson(node.output)) }}</span>
                </div>
                <template v-if="Object.keys(unwrapNodeOutput(parseJson(node.output))).length">
                  <div class="nrc-var" v-for="(value, key) in unwrapNodeOutput(parseJson(node.output))" :key="key">
                    <span class="nrc-var-key" :style="{ color: nodeTypeInfo[node.nodeType]?.color || '#666' }">{{ key }}</span>
                    <span class="nrc-var-value">{{ typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value) }}</span>
                  </div>
                </template>
              </div>
              <!-- 思考过程（从持久化日志中提取并集中展示），支持折叠 -->
              <div v-if="extractThinking(node.logs)" class="nrc-thinking">
                <div class="nrc-thinking-header" @click.stop="toggleThinking(node.nodeId)" style="cursor: pointer">
                  <span class="nrc-thinking-tag">💭 思考过程</span>
                  <span class="nrc-thinking-static">{{ extractThinking(node.logs).length }} 字</span>
                  <span class="nrc-thinking-expand" :class="{ expanded: expandedThinking[node.nodeId] !== false }">▶</span>
                </div>
                <pre v-show="expandedThinking[node.nodeId] !== false" class="nrc-thinking-body">{{ extractThinking(node.logs) }}</pre>
              </div>
              <!-- 完整上下文快照（节点执行后的全部 variables，debug 视图） -->
              <div v-if="parseJson(node.contextSnapshot)" class="nrc-ctx-toggle" @click.stop="toggleContext(node.nodeId)">
                <span class="nrc-ctx-icon" :class="{ expanded: expandedContext[node.nodeId] }">▶</span>
                <span class="nrc-ctx-label">查看完整上下文（{{ Object.keys(parseJson(node.contextSnapshot)).length }} 个变量）</span>
              </div>
              <div v-if="expandedContext[node.nodeId] && parseJson(node.contextSnapshot)" class="nrc-ctx-panel">
                <div class="nrc-ctx-var" v-for="(value, key) in parseJson(node.contextSnapshot)" :key="key">
                  <span class="nrc-ctx-key">{{ key }}</span>
                  <span class="nrc-ctx-value">{{ formatContextValue(value) }}</span>
                </div>
              </div>
              <div v-if="node.error" class="nrc-error">{{ node.error }}</div>
              <div v-if="expandedLogs[node.nodeId] && nonThinkingLogs(node.logs).length" class="nrc-logs">
                <div class="nrc-log" v-for="(log, idx) in nonThinkingLogs(node.logs)" :key="idx" :class="'log-type-' + (log.type || 'info').toLowerCase()">
                  <span class="nrc-log-icon">{{ logTypeIcon(log.type) }}</span>
                  <span class="nrc-log-msg">{{ log.message }}</span>
                  <span class="nrc-log-tag">{{ logTypeLabel(log.type) }}</span>
                  <div v-if="log.detail" class="nrc-log-detail">{{ log.detail }}</div>
                </div>
              </div>
            </div>
          </div>

          <!-- 工作空间下载：下载本次运行的工作目录 zip（应用 .agentignore 过滤） -->
          <div class="detail-footer">
            <n-button quaternary size="small" @click="downloadWorkspace(runDetail.run)">
              <template #icon>
                <n-icon size="14"><DownloadOutline /></n-icon>
              </template>
              下载工作空间
            </n-button>
          </div>
        </template>
      </div>
    </div>
  </div>
</template>

<style scoped>
.run-history-page {
  padding: 24px 32px;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.page-header {
  margin-bottom: 20px;
  flex-shrink: 0;
}

.page-header h2 {
  font-size: 20px;
  font-weight: 700;
  color: var(--text-primary, #e0e0e0);
  margin: 0;
}

.history-layout {
  flex: 1;
  display: flex;
  gap: 20px;
  min-height: 0;
}

/* 左侧面板 */
.left-panel {
  width: 320px;
  flex-shrink: 0;
  overflow-y: auto;
  padding-right: 8px;
}

.wf-group {
  margin-bottom: 20px;
}

.wf-group-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary, #e0e0e0);
  margin-bottom: 8px;
  padding: 4px;
  cursor: pointer;
  user-select: none;
  border-radius: 6px;
  transition: background 0.15s;
}

.wf-group-title:hover { background: rgba(255,255,255,0.04); }

.wf-collapse-icon {
  transition: transform 0.2s;
  color: var(--text-tertiary, #888);
}

/* 默认展开：图标旋转 90° 朝下；折叠时复原朝右 */
.wf-collapse-icon:not(.collapsed) { transform: rotate(90deg); }

.wf-run-count {
  font-size: 11px;
  background: rgba(99, 102, 241, 0.15);
  color: #818cf8;
  padding: 1px 6px;
  border-radius: 8px;
  margin-left: auto;
}

.wf-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 4px 8px;
}

.wf-tag {
  font-size: 10px;
  padding: 2px 8px;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 10px;
  color: rgba(255, 255, 255, 0.6);
  white-space: nowrap;
  line-height: 1.4;
}

.run-item {
  position: relative;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-color, rgba(255,255,255,0.08));
  margin-bottom: 6px;
  cursor: pointer;
  transition: all 0.15s;
}

.run-item:hover {
  background: rgba(37, 99, 235, 0.06);
  border-color: rgba(37, 99, 235, 0.2);
}

.run-item.active {
  background: rgba(37, 99, 235, 0.1);
  border-color: rgba(37, 99, 235, 0.3);
}

.run-item-top {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
}

.run-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}

.run-status-dot.status-running { background: #818cf8; }
.run-status-dot.status-success { background: #34d399; }
.run-status-dot.status-failed  { background: #f87171; }

.run-id {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary, #e0e0e0);
  font-family: 'Cascadia Code', monospace;
}

.run-badge {
  font-size: 10px;
  padding: 1px 5px;
  border-radius: 4px;
  font-weight: 600;
  margin-left: auto;
}

.run-badge.status-running  { background: rgba(99,102,241,0.15); color: #818cf8; }
.run-badge.status-success   { background: rgba(16,185,129,0.15); color: #34d399; }
.run-badge.status-failed    { background: rgba(239,68,68,0.15);  color: #f87171; }

.run-item-bottom {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--text-tertiary, #888);
}

.run-duration {
  margin-left: auto;
  color: var(--text-secondary, #aaa);
}

.run-delete-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  background: none;
  border: none;
  color: var(--text-tertiary, #666);
  cursor: pointer;
  padding: 2px;
  border-radius: 4px;
  opacity: 0;
  transition: opacity 0.15s;
}

.run-item:hover .run-delete-btn {
  opacity: 1;
}

.run-delete-btn:hover {
  color: #f87171;
  background: rgba(239,68,68,0.1);
}

/* 右侧详情面板 */
.right-panel {
  flex: 1;
  overflow-y: auto;
  min-width: 0;
}

.empty-detail {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}

.detail-footer {
  display: flex;
  justify-content: center;
  padding: 16px 0 4px;
  margin-top: 12px;
  border-top: 1px dashed var(--border-color, rgba(255,255,255,0.08));
}

.detail-header {
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.08));
}

.detail-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 16px;
  font-weight: 600;
  color: var(--text-primary, #e0e0e0);
  margin-bottom: 6px;
}

.detail-meta {
  display: flex;
  gap: 16px;
  font-size: 12px;
  color: var(--text-tertiary, #888);
}

.detail-error {
  margin-top: 8px;
  padding: 8px 12px;
  background: rgba(239,68,68,0.1);
  border: 1px solid rgba(239,68,68,0.2);
  border-radius: 6px;
  color: #f87171;
  font-size: 12px;
}

/* 节点结果卡片 - 复用 WorkflowEditor 样式 */
.node-result-card {
  background: rgba(255,255,255,0.03);
  border-radius: 8px;
  border-left: 3px solid #666;
  margin-bottom: 10px;
  overflow: hidden;
}

.nrc-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}

.nrc-icon {
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 5px;
  color: #fff;
  flex-shrink: 0;
}

.nrc-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.nrc-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary, #e0e0e0);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nrc-type {
  font-size: 10px;
  color: var(--text-tertiary, #666);
}

.nrc-status {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 4px;
  font-weight: 600;
  flex-shrink: 0;
}

.nrc-status.status-running  { background: rgba(99,102,241,0.15); color: #818cf8; }
.nrc-status.status-success   { background: rgba(16,185,129,0.15); color: #34d399; }
.nrc-status.status-failed    { background: rgba(239,68,68,0.15);  color: #f87171; }
.nrc-status.status-skipped   { background: rgba(107,114,128,0.15); color: #9ca3af; }

.nrc-log-count {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(99,102,241,0.1);
  color: #818cf8;
  flex-shrink: 0;
}

.nrc-expand {
  font-size: 10px;
  color: var(--text-tertiary, #666);
  transition: transform 0.2s;
  flex-shrink: 0;
}

.nrc-expand.expanded { transform: rotate(90deg); }

.nrc-vars { padding: 6px 10px; }

/* envelope 失败提示条：status=400 时显示在变量列表顶部 */
.nrc-env-fail {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  margin-bottom: 6px;
  background: rgba(239, 68, 68, 0.08);
  border-left: 2px solid #ef4444;
  border-radius: 3px;
  font-size: 11px;
}

.nrc-env-badge {
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 3px;
  flex-shrink: 0;
}

.nrc-env-badge.failed {
  background: rgba(239, 68, 68, 0.15);
  color: #f87171;
}

.nrc-env-msg {
  color: #f87171;
  word-break: break-all;
}

.nrc-var {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px 0;
  border-bottom: 1px solid rgba(255,255,255,0.02);
}

.nrc-var:last-child { border-bottom: none; }

.nrc-var-key {
  font-family: 'Cascadia Code', 'Fira Code', monospace;
  font-size: 11px;
  font-weight: 600;
}

.nrc-var-value {
  font-family: 'Cascadia Code', 'Fira Code', monospace;
  font-size: 11px;
  color: var(--text-secondary, #aaa);
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 120px;
  overflow-y: auto;
}

/* 思考过程展示（与 WorkflowEditor 实时思考区样式保持一致） */
.nrc-thinking {
  margin: 6px 10px;
  border-radius: 6px;
  background: rgba(168, 85, 247, 0.05);
  border: 1px solid rgba(168, 85, 247, 0.2);
  overflow: hidden;
}

.nrc-thinking-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  font-size: 11px;
  background: rgba(168, 85, 247, 0.08);
}

.nrc-thinking-tag {
  color: #c084fc;
  font-weight: 600;
}

.nrc-thinking-static {
  color: var(--text-tertiary, #888);
  font-size: 10px;
}

.nrc-thinking-expand {
  margin-left: auto;
  font-size: 10px;
  color: var(--text-tertiary, #666);
  transition: transform 0.2s;
  flex-shrink: 0;
}
.nrc-thinking-expand.expanded {
  transform: rotate(90deg);
}

.nrc-thinking-body {
  margin: 0;
  padding: 8px 12px;
  max-height: 320px;
  overflow: auto;
  font-family: 'Cascadia Code', 'Fira Code', monospace;
  font-size: 11px;
  color: var(--text-secondary, #ccc);
  white-space: pre-wrap;
  word-break: break-all;
}

.nrc-error {
  padding: 6px 10px;
  color: #f87171;
  font-size: 11px;
}

/* 完整上下文快照折叠区 */
.nrc-ctx-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  cursor: pointer;
  border-top: 1px solid rgba(255,255,255,0.04);
  font-size: 11px;
  color: var(--text-secondary, #aaa);
  user-select: none;
}

.nrc-ctx-toggle:hover { background: rgba(99,102,241,0.05); }

.nrc-ctx-icon {
  font-size: 9px;
  color: var(--text-tertiary, #888);
  transition: transform 0.2s;
}

.nrc-ctx-icon.expanded { transform: rotate(90deg); }

.nrc-ctx-label { font-weight: 500; }

.nrc-ctx-panel {
  padding: 6px 10px;
  border-top: 1px solid rgba(255,255,255,0.04);
  max-height: 280px;
  overflow-y: auto;
  background: rgba(0,0,0,0.15);
}

.nrc-ctx-var {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px 0;
  border-bottom: 1px solid rgba(255,255,255,0.02);
}

.nrc-ctx-var:last-child { border-bottom: none; }

.nrc-ctx-key {
  font-family: 'Cascadia Code', 'Fira Code', monospace;
  font-size: 11px;
  font-weight: 600;
  color: #818cf8;
}

.nrc-ctx-value {
  font-family: 'Cascadia Code', 'Fira Code', monospace;
  font-size: 11px;
  color: var(--text-secondary, #ccc);
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 80px;
  overflow-y: auto;
}

.nrc-logs {
  border-top: 1px solid rgba(255,255,255,0.04);
  padding: 6px 10px;
  max-height: 400px;
  overflow-y: auto;
}

.nrc-log {
  padding: 4px 0;
  border-bottom: 1px solid rgba(255,255,255,0.02);
  font-size: 11px;
  line-height: 1.5;
}

.nrc-log:last-child { border-bottom: none; }

.nrc-log-icon { margin-right: 4px; }

.nrc-log-tag {
  font-size: 10px;
  padding: 1px 4px;
  border-radius: 3px;
  margin-right: 6px;
  font-weight: 600;
  flex-shrink: 0;
}

.log-type-tool_call .nrc-log-tag { background: rgba(59,130,246,0.15); color: #60a5fa; }
.log-type-tool_result .nrc-log-tag { background: rgba(16,185,129,0.15); color: #34d399; }
.log-type-thinking .nrc-log-tag { background: rgba(168,85,247,0.15); color: #c084fc; }
.log-type-error .nrc-log-tag { background: rgba(239,68,68,0.15); color: #f87171; }
.log-type-info .nrc-log-tag { background: rgba(107,114,128,0.15); color: #9ca3af; }

.nrc-log-msg {
  color: var(--text-secondary, #ccc);
  word-break: break-all;
}

.nrc-log-detail {
  margin-top: 3px;
  margin-left: 22px;
  font-family: 'Cascadia Code', 'Fira Code', monospace;
  font-size: 10px;
  color: var(--text-tertiary, #888);
  background: rgba(0,0,0,0.2);
  padding: 4px 6px;
  border-radius: 4px;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 100px;
  overflow-y: auto;
}
</style>
