<template>
  <div class="tool-page">
    <!-- 浮层提示 -->
    <Transition name="toast">
      <div v-if="toastMessage" class="toast-notification" :class="toastType">
        <div class="toast-content">
          <i v-if="toastType === 'error'" class="far fa-circle-xmark"></i>
          <i v-else class="fas fa-circle-info"></i>
          <span>{{ toastMessage }}</span>
        </div>
        <button @click="closeToast" class="toast-close-btn" title="关闭">
          <i class="fas fa-xmark"></i>
        </button>
      </div>
    </Transition>
    <div class="main-container">
      <!-- 左侧侧栏（历史记录） -->
      <div class="sidebar" :class="{ 'sidebar-open': sidebarOpen }">
        <div class="sidebar-header">
          <h3>历史记录</h3>
          <button @click="toggleSidebar" class="close-btn">×</button>
        </div>
        <div class="sidebar-content">
          <div v-if="historyList.length === 0" class="empty-history">
            暂无历史记录
          </div>
          <div
            v-for="(item, index) in historyList"
            :key="index"
            class="history-item"
            @click="loadHistory(item.json)"
          >
            <div class="history-time">{{ formatTime(item.time) }}</div>
            <div class="history-preview">{{ truncateText(item.json, 50) }}</div>
          </div>
        </div>
      </div>

      <!-- 主内容区域 -->
      <div class="content-wrapper" :class="{ 'sidebar-pushed': sidebarOpen }">
        <!-- 左侧编辑器区域 -->
        <div class="left-panel" :style="{ width: leftPanelWidth + '%' }">
          <div class="panel-toolbar">
            <div class="view-tabs">
              <button class="view-tab active">编辑器 <span class="size-limit">(最大 5MB)</span></button>
            </div>
            <div class="toolbar-actions">
              <button @click="copyToClipboard" class="toolbar-icon-btn" title="复制">
                <i class="far fa-copy"></i>
              </button>
              <button @click="pasteFromClipboard" class="toolbar-icon-btn" title="粘贴">
                <i class="far fa-paste"></i>
              </button>
              <button @click="clearAll" class="toolbar-icon-btn" title="清空">
                <i class="far fa-trash-can"></i>
              </button>
              <button @click="formatJson" class="toolbar-icon-btn" title="格式化">
                <i class="fas fa-align-left"></i>
              </button>
              <button @click="minifyJson" class="toolbar-icon-btn" title="压缩">
                <i class="fas fa-down-left-and-up-right-to-center"></i>
              </button>
              <button @click="escapeJson" class="toolbar-icon-btn" title="转义">
                <i class="fas fa-code"></i>
              </button>
              <button @click="unescapeJson" class="toolbar-icon-btn" title="取消转义">
                <svg viewBox="150 -50 1100 1250" xmlns="http://www.w3.org/2000/svg" width="17" height="17"><path d="M285.352637 0.003641h111.956538v114.687184h-111.956538v282.621991a110.682235 110.682235 0 0 1-33.313896 81.282266 110.682235 110.682235 0 0 1-81.282266 33.313896 110.682235 110.682235 0 0 1 81.282266 33.313897 110.682235 110.682235 0 0 1 33.313896 81.282266v282.621991h111.956538v114.687184h-111.956538a188.050574 188.050574 0 0 1-80.007964-40.049493 93.570179 93.570179 0 0 1-34.67922-74.637691v-226.643722a110.682235 110.682235 0 0 0-33.313896-81.282267 110.682235 110.682235 0 0 0-81.282267-33.313896H0v-111.956537h55.978269a110.682235 110.682235 0 0 0 81.282266-33.313897 110.682235 110.682235 0 0 0 33.313896-81.282266V114.690825A113.776969 113.776969 0 0 1 285.261616 0.003641z m794.61835 0a113.776969 113.776969 0 0 1 114.687184 114.687184v226.643722a113.776969 113.776969 0 0 0 114.687185 114.687184H1365.323624v111.956538h-55.978268a113.776969 113.776969 0 0 0-114.687185 114.687184v226.643722a113.776969 113.776969 0 0 1-114.687184 114.687184h-111.956537V909.309175h111.956537V626.687184a113.776969 113.776969 0 0 1 114.687184-114.687184 113.776969 113.776969 0 0 1-114.687184-114.687184V114.690825h-111.956537V0.003641h111.956537zM682.661812 682.665453a54.612945 54.612945 0 0 1 55.978269 55.978269 58.799937 58.799937 0 0 1-16.019797 41.323795 54.612945 54.612945 0 0 1-80.007965 0 58.799937 58.799937 0 0 1-16.019797-41.323795 54.612945 54.612945 0 0 1 55.978269-55.978269z m-226.643721 0a54.612945 54.612945 0 0 1 55.978268 55.978269 58.799937 58.799937 0 0 1-16.019797 41.323795 52.246384 52.246384 0 0 1-40.049493 17.294099 59.164024 59.164024 0 0 1-58.708916-58.708916 52.246384 52.246384 0 0 1 17.294099-40.049493 58.799937 58.799937 0 0 1 41.505839-15.837754z m453.287443 0a58.799937 58.799937 0 0 1 41.323795 16.019797 52.246384 52.246384 0 0 1 17.294099 40.049493 59.164024 59.164024 0 0 1-58.708916 58.708916 52.246384 52.246384 0 0 1-40.049493-17.294099 58.799937 58.799937 0 0 1-16.019797-41.323795 54.612945 54.612945 0 0 1 55.978269-55.978269z" fill="#666666" p-id="26339"></path></svg>
              </button>
            </div>
          </div>
          <div ref="editorContainerRef" class="editor-container">
            <div class="editor-body">
              <div class="line-numbers">
                <div v-for="n in lineCount" :key="n" class="line-number">{{ n }}</div>
              </div>
              <textarea
                ref="jsonEditorRef"
                v-model="inputJson"
                @paste="handlePaste"
                @input="updateLineCount"
                @focus="adjustEditorHeight"
                placeholder='请输入或粘贴JSON数据，例如：{"name":"工具箱","version":1.0}'
                class="json-editor"
              ></textarea>
            </div>
          </div>
          <div class="sidebar-toggle">
            <button @click="toggleSidebar" class="toggle-btn">
              {{ sidebarOpen ? '◀' : '▶' }}
            </button>
          </div>
        </div>

        <!-- 可拖拽分割线 -->
        <div
          class="splitter"
          @mousedown="startResize"
        ></div>

        <!-- 右侧JSON树形展示区域 -->
        <div class="right-panel" :style="{ width: rightPanelWidth + '%' }">
          <div class="panel-toolbar">
            <div class="view-tabs">
              <button class="view-tab active">树形</button>
            </div>
            <div class="toolbar-actions">
              <div class="jsonpath-input-wrapper">
                <input
                  v-model="jsonPathQuery"
                  @input="handleJsonPathInput"
                  @focus="showJsonPathHistory = true"
                  @blur="handleJsonPathBlur"
                  type="text"
                  placeholder="输入 JSONPath，例如: $.key.subkey"
                  class="jsonpath-input"
                  title="JSONPath 筛选"
                />
                <button
                  v-if="jsonPathQuery"
                  @click="clearJsonPath"
                  class="jsonpath-clear-btn"
                  title="清除筛选"
                >
                  <i class="fas fa-xmark"></i>
                </button>
                <!-- JSONPath 历史记录下拉菜单 -->
                <div
                  v-if="showJsonPathHistory && filteredJsonPathHistory.length > 0"
                  class="jsonpath-history-dropdown"
                >
                  <div
                    v-for="(item, index) in filteredJsonPathHistory"
                    :key="index"
                    class="jsonpath-history-item"
                    @mousedown="selectJsonPathHistory(item)"
                  >
                    {{ item }}
                  </div>
                </div>
              </div>
              <button
                v-if="jsonPathQuery && jsonPathQuery.trim() && matchedNodes.length > 0"
                @click="copyMatchedResults"
                class="toolbar-icon-btn"
                title="复制筛选结果"
              >
                <i class="far fa-copy"></i>
              </button>
              <button @click="expandAll" class="toolbar-icon-btn" title="展开全部">
                <i class="fas fa-chevron-down"></i>
              </button>
              <button @click="collapseAll" class="toolbar-icon-btn" title="折叠全部">
                <i class="fas fa-chevron-up"></i>
              </button>
            </div>
          </div>
          <div class="tree-container">
            <div class="tree-content">
              <div v-if="!parsedData" class="empty-state">
                在左侧输入或粘贴JSON数据，右侧将实时显示树形结构
              </div>
              <!-- 有 JSONPath 筛选时，直接显示匹配的节点列表 -->
              <div v-else-if="jsonPathQuery && jsonPathQuery.trim() && matchedNodes.length > 0" class="matched-nodes-list">
                <JsonTreeNode
                  v-for="(node, index) in matchedNodes"
                  :key="index"
                  :data="node.data"
                  :key-name="getPathKey(node.path)"
                  :path="getPathParent(node.path)"
                  :expanded="expandedNodes"
                  :matched-paths="matchedPaths"
                  :json-path-query="jsonPathQuery"
                  :show-path="true"
                  :node-path="node.path"
                  @toggle="toggleNode"
                />
              </div>
              <div v-else-if="jsonPathQuery && jsonPathQuery.trim() && matchedNodes.length === 0" class="empty-state">
                未找到匹配的节点
              </div>
              <!-- 没有筛选时，显示完整的树形结构 -->
              <JsonTreeNode
                v-else
                :data="parsedData"
                :key-name="null"
                :path="''"
                :expanded="expandedNodes"
                :matched-paths="matchedPaths"
                :json-path-query="jsonPathQuery"
                @toggle="toggleNode"
              />
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, computed, onMounted, onUnmounted } from 'vue'
import JsonTreeNode from '../components/JsonTreeNode.vue'
import { useLineNumberEditor } from '../composables/useLineNumberEditor'

// 最大输入限制：5MB（JSON格式化工具的限制）
// 主要考虑因素：
// 1. JSON.parse 可以处理更大的 JSON（10-50MB）
// 2. DOM 渲染是主要瓶颈：大型 JSON 会创建大量 DOM 节点
// 3. 路径遍历和展开/折叠状态管理也会消耗内存
const MAX_INPUT_BYTES = 5 * 1024 * 1024 // 5MB

const inputJson = ref('')
const sidebarOpen = ref(false)
const leftPanelWidth = ref(50)
const rightPanelWidth = ref(50)
const isResizing = ref(false)
const expandedNodes = ref(new Set())
const treeLineCount = ref(1)
const {
  containerRef: editorContainerRef,
  editorRef: jsonEditorRef,
  lineCount,
  updateLineCount,
  adjustEditorHeight,
  resetEditorScroll,
  initEditor,
} = useLineNumberEditor(() => inputJson.value, {
  onBeforeUpdate: () => applyInputLimit(),
})

// JSONPath 筛选
const jsonPathQuery = ref('')
const matchedPaths = ref(new Set())
const showJsonPathHistory = ref(false)
const jsonPathHistory = ref([])
const JSONPATH_HISTORY_KEY = 'jsonpath-history'
const MAX_JSONPATH_HISTORY = 10

// 提示消息
const toastMessage = ref('')
const toastType = ref('error') // 'error' 或 'info'
let toastTimer = null

// 显示提示
const showToast = (message, type = 'error', duration = 3000) => {
  toastMessage.value = message
  toastType.value = type
  
  // 清除之前的定时器
  if (toastTimer) {
    clearTimeout(toastTimer)
  }
  
  // 设置自动隐藏
  toastTimer = setTimeout(() => {
    toastMessage.value = ''
  }, duration)
}

// 关闭提示
const closeToast = () => {
  if (toastTimer) {
    clearTimeout(toastTimer)
    toastTimer = null
  }
  toastMessage.value = ''
}

// 历史记录
const historyList = ref([])
const STORAGE_KEY = 'json-formatter-history'
const MAX_HISTORY = 50

// 解析后的JSON数据
const parsedData = computed(() => {
  if (!inputJson.value.trim()) return null
  try {
    return JSON.parse(inputJson.value)
  } catch (e) {
    return null
  }
})

// JSONPath 解析和匹配
const parseJsonPath = (jsonPath) => {
  if (!jsonPath || !jsonPath.trim()) return null
  
  const path = jsonPath.trim()
  // 移除开头的 $ 或 $.
  const normalizedPath = path.replace(/^\$\.?/, '')
  if (!normalizedPath) return []
  
  // 解析路径段：支持 .key 和 [index] 或 [*]
  const segments = []
  let current = normalizedPath
  let i = 0
  
  while (i < current.length) {
    if (current[i] === '[') {
      // 数组索引
      const endIndex = current.indexOf(']', i)
      if (endIndex === -1) break
      const indexStr = current.substring(i + 1, endIndex)
      if (indexStr === '*') {
        segments.push({ type: 'wildcard', index: '*' })
      } else {
        const index = parseInt(indexStr, 10)
        if (!isNaN(index)) {
          segments.push({ type: 'index', index })
        }
      }
      i = endIndex + 1
    } else if (current[i] === '.') {
      i++
    } else {
      // 对象键
      let keyEnd = i
      while (keyEnd < current.length && current[keyEnd] !== '.' && current[keyEnd] !== '[') {
        keyEnd++
      }
      const key = current.substring(i, keyEnd)
      if (key) {
        segments.push({ type: 'key', key })
      }
      i = keyEnd
    }
  }
  
  return segments
}

// 将路径转换为 JSONPath 格式（用于匹配）
const pathToJsonPath = (path) => {
  if (path === 'root') return '$'
  return '$' + path.replace(/^root/, '')
}

// 检查路径是否匹配 JSONPath
const pathMatchesJsonPath = (path, jsonPathSegments) => {
  if (!jsonPathSegments || jsonPathSegments.length === 0) return true
  
  // 将路径转换为段数组
  const pathSegments = []
  const pathStr = path === 'root' ? '' : path.replace(/^root\.?/, '')
  
  if (!pathStr) {
    return jsonPathSegments.length === 0
  }
  
  // 解析路径段
  let current = pathStr
  let i = 0
  
  while (i < current.length) {
    if (current[i] === '[') {
      const endIndex = current.indexOf(']', i)
      if (endIndex === -1) break
      const indexStr = current.substring(i + 1, endIndex)
      const index = parseInt(indexStr, 10)
      if (!isNaN(index)) {
        pathSegments.push({ type: 'index', index })
      }
      i = endIndex + 1
    } else if (current[i] === '.') {
      i++
    } else {
      let keyEnd = i
      while (keyEnd < current.length && current[keyEnd] !== '.' && current[keyEnd] !== '[') {
        keyEnd++
      }
      const key = current.substring(i, keyEnd)
      if (key) {
        pathSegments.push({ type: 'key', key })
      }
      i = keyEnd
    }
  }
  
  // 精确匹配：路径段数必须等于 JSONPath 段数
  if (pathSegments.length !== jsonPathSegments.length) return false
  
  for (let i = 0; i < jsonPathSegments.length; i++) {
    const jsonSeg = jsonPathSegments[i]
    const pathSeg = pathSegments[i]
    
    if (!pathSeg) return false
    
    if (jsonSeg.type === 'wildcard') {
      // 通配符匹配任何索引
      if (pathSeg.type !== 'index') return false
    } else if (jsonSeg.type === 'index') {
      if (pathSeg.type !== 'index' || pathSeg.index !== jsonSeg.index) return false
    } else if (jsonSeg.type === 'key') {
      if (pathSeg.type !== 'key' || pathSeg.key !== jsonSeg.key) return false
    }
  }
  
  return true
}

// 递归获取所有路径
const getAllPathsRecursive = (obj, prefix = 'root', paths = []) => {
  paths.push(prefix)
  
  if (Array.isArray(obj)) {
    obj.forEach((item, index) => {
      const path = prefix === 'root' ? `root[${index}]` : `${prefix}[${index}]`
      getAllPathsRecursive(item, path, paths)
    })
  } else if (typeof obj === 'object' && obj !== null) {
    Object.keys(obj).forEach(key => {
      const path = prefix === 'root' ? `root.${key}` : `${prefix}.${key}`
      getAllPathsRecursive(obj[key], path, paths)
    })
  }
  
  return paths
}

// 获取路径的所有父路径
const getParentPaths = (path) => {
  if (path === 'root') return ['root']
  
  const parents = ['root']
  const pathStr = path.replace(/^root\.?/, '')
  let current = 'root'
  let i = 0
  
  while (i < pathStr.length) {
    if (pathStr[i] === '[') {
      const endIdx = pathStr.indexOf(']', i)
      const idx = pathStr.substring(i + 1, endIdx)
      current = `${current}[${idx}]`
      parents.push(current)
      i = endIdx + 1
    } else if (pathStr[i] === '.') {
      i++
    } else {
      let keyEnd = i
      while (keyEnd < pathStr.length && pathStr[keyEnd] !== '.' && pathStr[keyEnd] !== '[') {
        keyEnd++
      }
      const key = pathStr.substring(i, keyEnd)
      current = current === 'root' ? `root.${key}` : `${current}.${key}`
      parents.push(current)
      i = keyEnd
    }
  }
  
  return parents
}

// 根据路径获取数据
const getDataByPath = (obj, path) => {
  if (path === 'root') return obj
  
  const pathStr = path.replace(/^root\.?/, '')
  let current = obj
  let i = 0
  
  while (i < pathStr.length && current !== undefined && current !== null) {
    if (pathStr[i] === '[') {
      const endIdx = pathStr.indexOf(']', i)
      const idx = parseInt(pathStr.substring(i + 1, endIdx), 10)
      current = current[idx]
      i = endIdx + 1
    } else if (pathStr[i] === '.') {
      i++
    } else {
      let keyEnd = i
      while (keyEnd < pathStr.length && pathStr[keyEnd] !== '.' && pathStr[keyEnd] !== '[') {
        keyEnd++
      }
      const key = pathStr.substring(i, keyEnd)
      current = current[key]
      i = keyEnd
    }
  }
  
  return current
}

// 获取路径的显示名称（最后一个键或索引）
const getPathDisplayName = (path) => {
  if (path === 'root') return 'root'
  
  const pathStr = path.replace(/^root\.?/, '')
  const lastBracket = pathStr.lastIndexOf('[')
  const lastDot = pathStr.lastIndexOf('.')
  
  if (lastBracket > lastDot) {
    // 最后一个是数组索引
    const indexStr = pathStr.substring(lastBracket + 1, pathStr.indexOf(']', lastBracket))
    const parentPath = pathStr.substring(0, lastBracket)
    return { type: 'index', index: parseInt(indexStr, 10), parentPath: parentPath ? `root.${parentPath}` : 'root' }
  } else if (lastDot !== -1) {
    // 最后一个是对象键
    const key = pathStr.substring(lastDot + 1)
    const parentPath = pathStr.substring(0, lastDot)
    return { type: 'key', key, parentPath: parentPath ? `root.${parentPath}` : 'root' }
  } else {
    // 只有一层
    if (pathStr.includes('[')) {
      const indexStr = pathStr.substring(pathStr.indexOf('[') + 1, pathStr.indexOf(']'))
      return { type: 'index', index: parseInt(indexStr, 10), parentPath: 'root' }
    } else {
      return { type: 'key', key: pathStr, parentPath: 'root' }
    }
  }
}

// 从路径中提取最后一个键或索引
const getPathKey = (path) => {
  if (path === 'root') return null
  
  const pathStr = path.replace(/^root\.?/, '')
  const lastBracket = pathStr.lastIndexOf('[')
  const lastDot = pathStr.lastIndexOf('.')
  
  if (lastBracket > lastDot) {
    // 最后一个是数组索引
    const indexStr = pathStr.substring(lastBracket + 1, pathStr.indexOf(']', lastBracket))
    return parseInt(indexStr, 10)
  } else if (lastDot !== -1) {
    // 最后一个是对象键
    return pathStr.substring(lastDot + 1)
  } else {
    // 只有一层
    if (pathStr.includes('[')) {
      const indexStr = pathStr.substring(pathStr.indexOf('[') + 1, pathStr.indexOf(']'))
      return parseInt(indexStr, 10)
    } else {
      return pathStr
    }
  }
}

// 从路径中提取父路径
const getPathParent = (path) => {
  if (path === 'root') return ''
  
  const pathStr = path.replace(/^root\.?/, '')
  const lastBracket = pathStr.lastIndexOf('[')
  const lastDot = pathStr.lastIndexOf('.')
  
  if (lastBracket > lastDot) {
    // 最后一个是数组索引
    const parentPath = pathStr.substring(0, lastBracket)
    // 构建完整的父路径
    if (!parentPath) {
      return 'root'
    }
    // 需要将 parentPath 转换为正确的格式（处理可能的数组索引）
    return `root.${parentPath}`
  } else if (lastDot !== -1) {
    // 最后一个是对象键
    const parentPath = pathStr.substring(0, lastDot)
    if (!parentPath) {
      return 'root'
    }
    return `root.${parentPath}`
  } else {
    // 只有一层，父路径是 root
    return 'root'
  }
}

// 最终匹配的节点列表
const matchedNodes = computed(() => {
  if (!jsonPathQuery.value.trim() || !parsedData.value) return []
  
  const nodes = []
  matchedPaths.value.forEach(path => {
    const data = getDataByPath(parsedData.value, path)
    nodes.push({
      path,
      data
    })
  })
  return nodes
})

// 处理 JSONPath 输入
const handleJsonPathInput = () => {
  if (!jsonPathQuery.value.trim()) {
    matchedPaths.value.clear()
    return
  }
  
  if (!parsedData.value) {
    matchedPaths.value.clear()
    return
  }
  
  try {
    const segments = parseJsonPath(jsonPathQuery.value)
    if (!segments || segments.length === 0) {
      matchedPaths.value.clear()
      return
    }
    
    // 获取所有路径并匹配，只保留最终匹配的节点（不添加父路径）
    const allPaths = getAllPathsRecursive(parsedData.value)
    const matched = new Set()
    
    allPaths.forEach(path => {
      if (pathMatchesJsonPath(path, segments)) {
        // 只添加最终匹配的路径，不添加父路径
        matched.add(path)
        // 自动展开匹配的节点，以便显示其子节点
        expandedNodes.value.add(path)
      }
    })
    
    matchedPaths.value = matched
    
    // 如果匹配成功，保存到历史记录
    if (matched.size > 0) {
      saveJsonPathHistory(jsonPathQuery.value.trim())
    }
  } catch (e) {
    matchedPaths.value.clear()
    // JSONPath 解析错误，忽略
  }
}

// 清除 JSONPath 筛选
const clearJsonPath = () => {
  jsonPathQuery.value = ''
  matchedPaths.value.clear()
}

// 过滤后的 JSONPath 历史记录
const filteredJsonPathHistory = computed(() => {
  if (!jsonPathQuery.value.trim()) {
    return jsonPathHistory.value
  }
  const query = jsonPathQuery.value.toLowerCase()
  return jsonPathHistory.value.filter(item => 
    item.toLowerCase().includes(query)
  )
})

// 加载 JSONPath 历史记录
const loadJsonPathHistory = () => {
  try {
    const stored = localStorage.getItem(JSONPATH_HISTORY_KEY)
    if (stored) {
      jsonPathHistory.value = JSON.parse(stored)
    }
  } catch (e) {
    // 加载 JSONPath 历史记录失败，重置为空数组
    jsonPathHistory.value = []
  }
}

// 保存 JSONPath 历史记录
const saveJsonPathHistory = (jsonPath) => {
  if (!jsonPath || !jsonPath.trim()) return
  
  const trimmed = jsonPath.trim()
  
  // 移除重复项
  const index = jsonPathHistory.value.indexOf(trimmed)
  if (index !== -1) {
    jsonPathHistory.value.splice(index, 1)
  }
  
  // 添加到开头
  jsonPathHistory.value.unshift(trimmed)
  
  // 限制最多 10 条
  if (jsonPathHistory.value.length > MAX_JSONPATH_HISTORY) {
    jsonPathHistory.value = jsonPathHistory.value.slice(0, MAX_JSONPATH_HISTORY)
  }
  
  // 保存到 localStorage
  try {
    localStorage.setItem(JSONPATH_HISTORY_KEY, JSON.stringify(jsonPathHistory.value))
  } catch (e) {
    // 保存 JSONPath 历史记录失败，忽略错误
  }
}

// 选择 JSONPath 历史记录
const selectJsonPathHistory = (jsonPath) => {
  jsonPathQuery.value = jsonPath
  showJsonPathHistory.value = false
  handleJsonPathInput()
}

// 处理 JSONPath 输入框失焦
const handleJsonPathBlur = () => {
  // 延迟隐藏，以便点击历史记录项时能触发选择
  setTimeout(() => {
    showJsonPathHistory.value = false
  }, 200)
}

// 复制筛选结果
const copyMatchedResults = async () => {
  if (!matchedNodes.value || matchedNodes.value.length === 0) {
    showToast('没有可复制的结果', 'error', 2000)
    return
  }
  
  try {
    // 将匹配的节点数据转换为 JSON 数组
    const results = matchedNodes.value.map(node => node.data)
    const jsonString = JSON.stringify(results, null, 2)
    
    // 复制到剪贴板
    await navigator.clipboard.writeText(jsonString)
    showToast(`已复制 ${matchedNodes.value.length} 个匹配结果`, 'info', 2000)
  } catch (e) {
    showToast('复制失败：' + e.message, 'error', 3000)
  }
}

// 获取字符串 UTF-8 字节长度
const getByteLength = (str) => new TextEncoder().encode(str).length

// 将字符串截断到最大字节数（避免切断多字节字符）
const truncateToMaxBytes = (str, maxBytes) => {
  if (getByteLength(str) <= maxBytes) return str
  let end = str.length
  while (end > 0 && getByteLength(str.slice(0, end)) > maxBytes) end--
  return str.slice(0, end)
}

// 应用输入大小限制，超出则截断并提示
const applyInputLimit = () => {
  if (getByteLength(inputJson.value) <= MAX_INPUT_BYTES) return
  inputJson.value = truncateToMaxBytes(inputJson.value, MAX_INPUT_BYTES)
  showToast('内容已超过 5MB 限制，已自动截断', 'info', 3000)
  updateLineCount()
}

// 获取所有路径
const getAllPaths = (obj, prefix = 'root') => {
  const paths = []
  if (typeof obj === 'object' && obj !== null) {
    paths.push(prefix)
    if (Array.isArray(obj)) {
      obj.forEach((item, index) => {
        const path = `${prefix}[${index}]`
        paths.push(path)
        if (typeof item === 'object' && item !== null) {
          paths.push(...getAllPaths(item, path))
        }
      })
    } else {
      Object.keys(obj).forEach(key => {
        const path = `${prefix}.${key}`
        paths.push(path)
        if (typeof obj[key] === 'object' && obj[key] !== null) {
          paths.push(...getAllPaths(obj[key], path))
        }
      })
    }
  }
  return paths
}

// 监听输入变化，实时更新树形结构
watch(inputJson, () => {
  updateLineCount()
  if (inputJson.value.trim()) {
    // 检查大小，如果超过限制则不解析（避免性能问题）
    if (getByteLength(inputJson.value) > MAX_INPUT_BYTES) {
      treeLineCount.value = 1
      return
    }
    
    try {
      const parsed = JSON.parse(inputJson.value)
      expandedNodes.value.clear()
      // 默认展开所有节点
      const allPaths = getAllPaths(parsed)
      allPaths.forEach(path => expandedNodes.value.add(path))
      // 估算树形视图行数（基于格式化后的JSON）
      const jsonStr = JSON.stringify(parsed, null, 2)
      treeLineCount.value = Math.max(1, jsonStr.split('\n').length)
    } catch (e) {
      // JSON格式错误时不显示提示，只在用户操作时提示
      treeLineCount.value = 1
    }
  } else {
    treeLineCount.value = 1
  }
})

// 格式化JSON
const formatJson = () => {
  if (!inputJson.value.trim()) {
    showToast('请输入JSON数据')
    return
  }
  
  // 检查输入大小
  if (getByteLength(inputJson.value) > MAX_INPUT_BYTES) {
    showToast(`输入内容超过 5MB 限制，无法格式化`, 'error', 4000)
    return
  }
  
  try {
    const parsed = JSON.parse(inputJson.value)
    const formatted = JSON.stringify(parsed, null, 2)
    
    // 检查格式化后的大小
    if (getByteLength(formatted) > MAX_INPUT_BYTES) {
      showToast('格式化后的内容超过 5MB 限制，无法显示', 'error', 4000)
      return
    }
    
    inputJson.value = formatted
    updateLineCount()
    resetEditorScroll()
    showToast('格式化成功', 'info', 2000)
  } catch (e) {
    showToast('JSON格式错误：' + e.message)
  }
}

// 压缩JSON
const minifyJson = () => {
  if (!inputJson.value.trim()) {
    showToast('请输入JSON数据')
    return
  }
  
  // 检查输入大小
  if (getByteLength(inputJson.value) > MAX_INPUT_BYTES) {
    showToast(`输入内容超过 5MB 限制，无法压缩`, 'error', 4000)
    return
  }
  
  try {
    const parsed = JSON.parse(inputJson.value)
    const minified = JSON.stringify(parsed)
    
    // 检查压缩后的大小（压缩后应该更小，但为了安全还是检查）
    if (getByteLength(minified) > MAX_INPUT_BYTES) {
      showToast('压缩后的内容超过 5MB 限制，无法显示', 'error', 4000)
      return
    }
    
    inputJson.value = minified
    updateLineCount()
    resetEditorScroll()
    showToast('压缩成功', 'info', 2000)
  } catch (e) {
    showToast('JSON格式错误：' + e.message)
  }
}

// 转义JSON
const escapeJson = () => {
  if (!inputJson.value.trim()) {
    showToast('请输入JSON数据')
    return
  }
  
  // 检查输入大小
  if (getByteLength(inputJson.value) > MAX_INPUT_BYTES) {
    showToast(`输入内容超过 5MB 限制，无法转义`, 'error', 4000)
    return
  }
  
  try {
    let jsonToEscape = inputJson.value.trim()
    
    // 检查是否已经是带引号的JSON字符串
    const trimmed = jsonToEscape.trim()
    const isQuotedString = (trimmed.startsWith('"') && trimmed.endsWith('"')) || 
                           (trimmed.startsWith("'") && trimmed.endsWith("'"))
    
    if (!isQuotedString) {
      // 没有前后引号的情况
      try {
        // 尝试解析为JSON对象/数组
        const parsedJson = JSON.parse(jsonToEscape)
        // 如果解析成功，先压缩JSON
        jsonToEscape = JSON.stringify(parsedJson)
        // 然后添加引号并转义（JSON.stringify会自动添加引号和转义）
        inputJson.value = JSON.stringify(jsonToEscape)
      } catch (e) {
        // 解析失败，说明是普通字符串（没有引号）
        // 直接添加引号并转义（JSON.stringify会自动添加引号和转义特殊字符）
        inputJson.value = JSON.stringify(jsonToEscape)
      }
    } else {
      // 已经有引号的情况，先解析去掉引号
      try {
        const parsed = JSON.parse(jsonToEscape)
        // 如果解析后是对象/数组，先压缩
        if (typeof parsed === 'object' && parsed !== null) {
          jsonToEscape = JSON.stringify(parsed)
        } else {
          // 如果是基本类型，转换为字符串
          jsonToEscape = String(parsed)
        }
        // 然后添加引号并转义
        inputJson.value = JSON.stringify(jsonToEscape)
      } catch (e) {
        // 解析失败，可能是单引号字符串或其他格式，去掉首尾引号
        if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
          jsonToEscape = trimmed.slice(1, -1)
        } else if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
          jsonToEscape = trimmed.slice(1, -1)
        }
        // 去掉引号后，尝试解析为JSON对象/数组
        try {
          const parsed = JSON.parse(jsonToEscape)
          if (typeof parsed === 'object' && parsed !== null) {
            // 先压缩
            jsonToEscape = JSON.stringify(parsed)
          }
        } catch (e2) {
          // 不是JSON，保持原样
        }
        // 添加引号并转义
        const escaped = JSON.stringify(jsonToEscape)
        // 检查转义后的大小
        if (getByteLength(escaped) > MAX_INPUT_BYTES) {
          showToast('转义后的内容超过 5MB 限制，无法显示', 'error', 4000)
          return
        }
        inputJson.value = escaped
      }
    }
    
    // 最后检查一次大小（防止前面的分支没有检查）
    if (getByteLength(inputJson.value) > MAX_INPUT_BYTES) {
      showToast('转义后的内容超过 5MB 限制，无法显示', 'error', 4000)
      return
    }
    
    updateLineCount()
    resetEditorScroll()
    showToast('转义成功', 'info', 2000)
  } catch (e) {
    showToast('转义失败：' + e.message)
  }
}

// 取消转义JSON
const unescapeJson = () => {
  if (!inputJson.value.trim()) {
    showToast('请输入JSON数据')
    return
  }
  
  // 检查输入大小
  if (getByteLength(inputJson.value) > MAX_INPUT_BYTES) {
    showToast(`输入内容超过 5MB 限制，无法取消转义`, 'error', 4000)
    return
  }
  
  try {
    let jsonToParse = inputJson.value.trim()
    
    // 检查是否已经有前后引号
    const trimmed = jsonToParse.trim()
    const hasQuotes = (trimmed.startsWith('"') && trimmed.endsWith('"')) || 
                      (trimmed.startsWith("'") && trimmed.endsWith("'"))
    
    let unescaped = null
    
    if (hasQuotes) {
      // 有引号，直接解析
      try {
        unescaped = JSON.parse(jsonToParse)
      } catch (e) {
        // 如果是单引号，先去掉单引号，再添加双引号
        if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
          jsonToParse = '"' + trimmed.slice(1, -1) + '"'
          unescaped = JSON.parse(jsonToParse)
        } else {
          throw e
        }
      }
    } else {
      // 没有引号，先尝试直接解析（可能是JSON对象）
      try {
        unescaped = JSON.parse(jsonToParse)
      } catch (e) {
        // 直接解析失败，尝试添加引号后解析（可能是转义的字符串）
        try {
          jsonToParse = '"' + jsonToParse + '"'
          unescaped = JSON.parse(jsonToParse)
        } catch (e2) {
          throw new Error('无法解析：既不是有效的JSON对象，也不是有效的转义字符串')
        }
      }
    }
    
    // 如果取消转义后是字符串，尝试解析为JSON对象
    if (typeof unescaped === 'string') {
      try {
        const parsed = JSON.parse(unescaped)
        // 如果解析成功，自动格式化
        const formatted = JSON.stringify(parsed, null, 2)
        // 检查格式化后的大小
        if (getByteLength(formatted) > MAX_INPUT_BYTES) {
          showToast('取消转义后的内容超过 5MB 限制，无法显示', 'error', 4000)
          return
        }
        inputJson.value = formatted
        updateLineCount()
        resetEditorScroll()
        showToast('取消转义并格式化成功', 'info', 2000)
        return
      } catch (e) {
        // 如果解析失败，说明只是普通字符串，保持原样
        // 检查字符串大小
        if (getByteLength(unescaped) > MAX_INPUT_BYTES) {
          showToast('取消转义后的内容超过 5MB 限制，无法显示', 'error', 4000)
          return
        }
        inputJson.value = unescaped
        updateLineCount()
        resetEditorScroll()
        showToast('取消转义成功', 'info', 2000)
        return
      }
    }
    
    // 如果取消转义后是对象或数组，自动格式化
    if (typeof unescaped === 'object' && unescaped !== null) {
      const formatted = JSON.stringify(unescaped, null, 2)
      // 检查格式化后的大小
      if (getByteLength(formatted) > MAX_INPUT_BYTES) {
        showToast('取消转义后的内容超过 5MB 限制，无法显示', 'error', 4000)
        return
      }
      inputJson.value = formatted
      updateLineCount()
      resetEditorScroll()
      showToast('取消转义并格式化成功', 'info', 2000)
    } else {
      // 其他类型（数字、布尔值等），转换为字符串
      const result = String(unescaped)
      // 检查结果大小
      if (getByteLength(result) > MAX_INPUT_BYTES) {
        showToast('取消转义后的内容超过 5MB 限制，无法显示', 'error', 4000)
        return
      }
      inputJson.value = result
      updateLineCount()
      resetEditorScroll()
      showToast('取消转义成功', 'info', 2000)
    }
  } catch (e) {
    showToast('取消转义失败：' + e.message)
  }
}

// 复制到剪贴板
const copyToClipboard = async () => {
  if (!inputJson.value.trim()) {
    showToast('编辑器内容为空，无法复制')
    return
  }
  try {
    await navigator.clipboard.writeText(inputJson.value)
    showToast('已复制到剪贴板', 'info', 2000)
  } catch (e) {
    showToast('复制失败：' + e.message)
  }
}

// 从剪贴板粘贴
const pasteFromClipboard = async () => {
  // 优先使用现代 Clipboard API（需要 HTTPS 或 localhost）
  if (navigator.clipboard && navigator.clipboard.readText) {
    try {
      let text = await navigator.clipboard.readText()
      if (text.trim()) {
        // 检查大小限制
        if (getByteLength(text) > MAX_INPUT_BYTES) {
          text = truncateToMaxBytes(text, MAX_INPUT_BYTES)
          showToast('粘贴内容已超过 5MB 限制，已自动截断', 'info', 3000)
        }
        inputJson.value = text
        updateLineCount()
        // 粘贴后不重置滚动位置，保持在当前位置
        // 检查是否是有效的JSON，如果是则保存到历史记录
        try {
          JSON.parse(text)
          // 避免重复保存相同的JSON
          const lastHistory = historyList.value[0]
          if (!lastHistory || lastHistory.json !== text) {
            saveToHistory(text)
          }
        } catch (e) {
          // 如果不是有效JSON，不保存到历史记录
        }
      } else {
        showToast('剪贴板内容为空')
      }
      return
    } catch (e) {
      // 如果 Clipboard API 失败，使用备用方法
    }
  }
  
  // 备用方法：聚焦到编辑器，提示用户粘贴
  // 让现有的 handlePaste 方法处理粘贴逻辑
  if (jsonEditorRef.value) {
    jsonEditorRef.value.focus()
    showToast('请按 Ctrl+V 或 Cmd+V 粘贴内容', 'info', 3000)
  } else {
    showToast('无法访问编辑器，请手动粘贴内容')
  }
}

// 清空
const clearAll = () => {
  inputJson.value = ''
  expandedNodes.value.clear()
  treeLineCount.value = 1
  updateLineCount()
  showToast('已清空', 'info', 2000)
}

// 处理粘贴事件
const handlePaste = async (event) => {
  // 获取粘贴的文本
  const pastedText = event.clipboardData?.getData('text') || ''
  
  if (pastedText.trim()) {
    // 检查大小限制
    if (getByteLength(pastedText) > MAX_INPUT_BYTES) {
      event.preventDefault()
      const truncated = truncateToMaxBytes(pastedText, MAX_INPUT_BYTES)
      inputJson.value = truncated
      showToast('粘贴内容已超过 5MB 限制，已自动截断', 'info', 3000)
      updateLineCount()
      return
    }
    
    // 等待下一个tick，确保inputJson已更新
    await new Promise(resolve => setTimeout(resolve, 0))
    
    // 粘贴后不重置滚动位置，保持在当前位置
    
    // 检查是否是有效的JSON
    try {
      JSON.parse(inputJson.value)
      // 避免重复保存相同的JSON
      const lastHistory = historyList.value[0]
      if (!lastHistory || lastHistory.json !== inputJson.value) {
        saveToHistory(inputJson.value)
      }
    } catch (e) {
      // 如果不是有效JSON，不保存
    }
  }
}

// 保存到历史记录
const saveToHistory = (json) => {
  const historyItem = {
    json: json,
    time: Date.now()
  }
  
  // 从localStorage读取现有历史
  let history = []
  try {
    const stored = localStorage.getItem(STORAGE_KEY)
    if (stored) {
      history = JSON.parse(stored)
    }
  } catch (e) {
    // 读取历史记录失败，忽略错误
  }
  
  // 添加到开头
  history.unshift(historyItem)
  
  // 限制最多50条
  if (history.length > MAX_HISTORY) {
    history = history.slice(0, MAX_HISTORY)
  }
  
  // 保存到localStorage
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(history))
    loadHistoryList()
  } catch (e) {
    // 保存历史记录失败，忽略错误
  }
}

// 加载历史记录列表
const loadHistoryList = () => {
  try {
    const stored = localStorage.getItem(STORAGE_KEY)
    if (stored) {
      historyList.value = JSON.parse(stored)
    }
  } catch (e) {
    // 加载历史记录失败，重置为空数组
    historyList.value = []
  }
}

// 加载历史记录
const loadHistory = (json) => {
  inputJson.value = json
  expandedNodes.value.clear()
  updateLineCount()
  resetEditorScroll()
}

// 切换侧栏
const toggleSidebar = () => {
  sidebarOpen.value = !sidebarOpen.value
}

// 格式化时间
const formatTime = (timestamp) => {
  const date = new Date(timestamp)
  const now = new Date()
  const diff = now - date
  
  if (diff < 60000) return '刚刚'
  if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前'
  if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前'
  
  return date.toLocaleString('zh-CN', {
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit'
  })
}

// 截断文本
const truncateText = (text, maxLength) => {
  if (text.length <= maxLength) return text
  return text.substring(0, maxLength) + '...'
}

// 展开/折叠节点
const toggleNode = (path) => {
  if (expandedNodes.value.has(path)) {
    expandedNodes.value.delete(path)
  } else {
    expandedNodes.value.add(path)
  }
}

// 展开全部
const expandAll = () => {
  if (!parsedData.value) return
  const allPaths = getAllPaths(parsedData.value)
  allPaths.forEach(path => expandedNodes.value.add(path))
}

// 折叠全部
const collapseAll = () => {
  expandedNodes.value.clear()
}

// 拖拽调整宽度
const startResize = (e) => {
  isResizing.value = true
  document.addEventListener('mousemove', handleResize)
  document.addEventListener('mouseup', stopResize)
  e.preventDefault()
}

const handleResize = (e) => {
  if (!isResizing.value) return
  
  const container = document.querySelector('.content-wrapper')
  if (!container) return
  
  const containerWidth = container.offsetWidth
  const mouseX = e.clientX - container.getBoundingClientRect().left
  const percentage = (mouseX / containerWidth) * 100
  
  // 限制在20%到80%之间
  const leftPercent = Math.max(20, Math.min(80, percentage))
  const rightPercent = 100 - leftPercent
  
  leftPanelWidth.value = leftPercent
  rightPanelWidth.value = rightPercent
}

const stopResize = () => {
  isResizing.value = false
  document.removeEventListener('mousemove', handleResize)
  document.removeEventListener('mouseup', stopResize)
}

onMounted(() => {
  loadHistoryList()
  loadJsonPathHistory()
  initEditor()
})

onUnmounted(() => {
  document.removeEventListener('mousemove', handleResize)
  document.removeEventListener('mouseup', stopResize)
})
</script>

<style scoped>
/* 浮层提示样式 */
.toast-notification {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  min-width: 300px;
  max-width: 90%;
  padding: 0.75rem 1rem;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  display: flex;
  align-items: center;
  font-size: 0.875rem;
}

.toast-notification.error {
  background: #fff5f5;
  color: #c33;
  border: 1px solid #ffcccc;
}

.toast-notification.info {
  background: #f0f9ff;
  color: #0369a1;
  border: 1px solid #bae6fd;
}

.toast-content {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1;
  min-width: 0;
}

.toast-content svg,
.toast-content i {
  flex-shrink: 0;
}

.toast-content span {
  flex: 1;
  word-break: break-word;
}

.toast-close-btn {
  flex-shrink: 0;
  margin-left: 0.75rem;
  padding: 0.25rem;
  border: none;
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: inherit;
  opacity: 0.6;
  transition: all 0.2s;
  width: 20px;
  height: 20px;
}

.toast-close-btn:hover {
  opacity: 1;
  background: rgba(0, 0, 0, 0.08);
}

.toast-notification.error .toast-close-btn:hover {
  background: rgba(204, 51, 51, 0.15);
}

.toast-notification.info .toast-close-btn:hover {
  background: rgba(3, 105, 161, 0.15);
}

.toast-close-btn:active {
  opacity: 0.8;
}

.toast-close-btn svg,
.toast-close-btn i {
  display: block;
  font-size: 14px;
}

/* 浮层提示动画 */
.toast-enter-active {
  animation: slideUp 0.3s ease-out;
}

.toast-leave-active {
  animation: slideDown 0.3s ease-in;
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}

@keyframes slideDown {
  from {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
  to {
    opacity: 0;
    transform: translateX(-50%) translateY(20px);
  }
}

.tool-page {
  height: calc(100vh - 64px);
  display: flex;
  flex-direction: column;
  margin: -1rem;
  padding: 0;
  background: #ffffff;
}

.main-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
  background: #ffffff;
}

/* 侧栏样式 */
.sidebar {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 300px;
  background: #ffffff;
  border-right: 1px solid #e5e5e5;
  transform: translateX(-100%);
  transition: transform 0.3s ease;
  z-index: 10;
  display: flex;
  flex-direction: column;
  box-shadow: 2px 0 8px rgba(0, 0, 0, 0.05);
}

.sidebar-open {
  transform: translateX(0);
}

.content-wrapper.sidebar-pushed {
  margin-left: 300px;
}

.sidebar-header {
  padding: 1rem;
  border-bottom: 1px solid #e5e5e5;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #ffffff;
}

.sidebar-header h3 {
  margin: 0;
  font-size: 1rem;
  color: #1a1a1a;
  font-weight: 500;
}

.close-btn {
  background: none;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
  color: #666666;
  padding: 0;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.2s;
}

.close-btn:hover {
  color: #1a1a1a;
}

.sidebar-content {
  flex: 1;
  overflow-y: auto;
  padding: 0.5rem;
  background: #ffffff;
}

.empty-history {
  padding: 2rem;
  text-align: center;
  color: #999999;
  font-size: 0.875rem;
}

.history-item {
  padding: 0.75rem;
  margin-bottom: 0.5rem;
  background: #ffffff;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.2s;
  border: 1px solid #e5e5e5;
}

.history-item:hover {
  background: #f5f5f5;
  border-color: #d0d0d0;
}

.history-time {
  font-size: 0.75rem;
  color: #999999;
  margin-bottom: 0.25rem;
}

.history-preview {
  font-size: 0.875rem;
  color: #1a1a1a;
  font-family: 'Courier New', monospace;
  word-break: break-all;
}

/* 主内容区域 */
.content-wrapper {
  flex: 1;
  display: flex;
  position: relative;
  background: #ffffff;
  overflow: hidden;
  min-height: 0;
}

.left-panel,
.right-panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow: hidden;
  background: #ffffff;
  min-height: 0;
  max-height: 100%;
}

.left-panel {
  position: relative;
}

/* 面板工具栏 */
.panel-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0;
  background: #ffffff;
  border-bottom: 1px solid #e5e5e5;
  min-height: 40px;
}

.view-tabs {
  display: flex;
  gap: 0;
  padding-left: 0.5rem;
}

.view-tab {
  padding: 0.5rem 1rem;
  border: none;
  background: transparent;
  color: #666666;
  cursor: pointer;
  font-size: 0.875rem;
  font-weight: 500;
  transition: all 0.2s;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.view-tab:hover {
  color: #1a1a1a;
  background: #f5f5f5;
}

.view-tab.active {
  color: #1a1a1a;
  background: #1a1a1a;
  color: #ffffff;
  border-bottom-color: #1a1a1a;
}

.view-tab .size-limit {
  font-size: 0.75rem;
  font-weight: 400;
  opacity: 0.7;
  margin-left: 0.25rem;
}

.toolbar-actions {
  display: flex;
  gap: 0.25rem;
  padding: 0.25rem 0.5rem;
  align-items: center;
}

.toolbar-icon-btn {
  padding: 0.375rem;
  border: none;
  background: transparent;
  border-radius: 3px;
  cursor: pointer;
  transition: all 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  color: #666666;
}

.toolbar-icon-btn:hover {
  background: #f5f5f5;
  color: #1a1a1a;
}

.toolbar-icon-btn:active {
  background: #e5e5e5;
}

.toolbar-icon-btn svg,
.toolbar-icon-btn i {
  display: block;
  font-size: 14px;
}

/* JSONPath 输入框样式 */
.jsonpath-input-wrapper {
  position: relative;
  display: flex;
  align-items: center;
  margin-right: 0.5rem;
}

.jsonpath-input {
  width: 250px;
  padding: 0.375rem 0.75rem;
  padding-right: 28px;
  border: 1px solid #e5e5e5;
  border-radius: 4px;
  font-size: 0.875rem;
  font-family: 'Courier New', monospace;
  background: #ffffff;
  color: #1a1a1a;
  outline: none;
  transition: all 0.2s;
}

.jsonpath-input:focus {
  border-color: #1a1a1a;
  box-shadow: 0 0 0 2px rgba(26, 26, 26, 0.1);
}

.jsonpath-input::placeholder {
  color: #999999;
}

.jsonpath-clear-btn {
  position: absolute;
  right: 6px;
  padding: 0.25rem;
  border: none;
  background: transparent;
  border-radius: 3px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #666666;
  opacity: 0.6;
  transition: all 0.2s;
  width: 20px;
  height: 20px;
}

.jsonpath-clear-btn:hover {
  opacity: 1;
  background: #f5f5f5;
}

.jsonpath-clear-btn:active {
  opacity: 0.8;
}

.jsonpath-clear-btn svg,
.jsonpath-clear-btn i {
  display: block;
  font-size: 12px;
}

/* JSONPath 历史记录下拉菜单 */
.jsonpath-history-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 4px;
  background: #ffffff;
  border: 1px solid #e5e5e5;
  border-radius: 4px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  max-height: 200px;
  overflow-y: auto;
  z-index: 1000;
}

.jsonpath-history-item {
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  font-size: 0.875rem;
  font-family: 'Courier New', monospace;
  color: #1a1a1a;
  transition: background 0.2s;
  word-break: break-all;
}

.jsonpath-history-item:hover {
  background: #f5f5f5;
}

.jsonpath-history-item:first-child {
  border-radius: 4px 4px 0 0;
}

.jsonpath-history-item:last-child {
  border-radius: 0 0 4px 4px;
}

.json-editor {
  flex: 1;
  width: 0;
  min-width: 0;
  padding: 1rem 1rem 1rem 3rem;
  border: none;
  font-family: 'Courier New', monospace;
  font-size: 14px;
  resize: none;
  outline: none;
  background: #ffffff;
  color: #1a1a1a;
  line-height: 1.6;
  overflow: hidden;
  min-height: 0;
  box-sizing: border-box;
}

.json-editor:focus {
  background: #ffffff;
}

.json-editor::placeholder {
  color: #999999;
}

/* 侧栏切换按钮 */
.sidebar-toggle {
  position: absolute;
  left: 0;
  bottom: 1rem;
  z-index: 5;
}

.toggle-btn {
  width: 32px;
  height: 48px;
  background: #1a1a1a;
  color: #ffffff;
  border: none;
  border-radius: 0 4px 4px 0;
  cursor: pointer;
  font-size: 0.875rem;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1);
}

.toggle-btn:hover {
  background: #333333;
}

/* 分割线 */
.splitter {
  width: 1px;
  background: #e5e5e5;
  cursor: col-resize;
  position: relative;
  flex-shrink: 0;
  transition: background 0.2s;
}

.splitter:hover {
  background: #d0d0d0;
  width: 2px;
}

.splitter::before {
  content: '';
  position: absolute;
  left: -2px;
  right: -2px;
  top: 0;
  bottom: 0;
}

/* 右侧面板 */
.tree-container {
  flex: 1;
  display: flex;
  position: relative;
  overflow: hidden;
  background: #ffffff;
  min-height: 0;
}

.tree-content {
  flex: 1 1 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 1rem;
  min-height: 0;
  box-sizing: border-box;
  position: relative;
}

.empty-state {
  padding: 2rem;
  text-align: center;
  color: #999999;
  font-size: 0.875rem;
}

/* 匹配节点列表样式 */
.matched-nodes-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

@media (max-width: 768px) {
  .tool-page {
    padding: 0;
    margin: -1rem;
    height: calc(100vh - 64px + 2rem);
  }
  
  .sidebar {
    width: 80%;
    max-width: 300px;
  }
  
  .left-panel,
  .right-panel {
    min-width: 0;
  }
  
  .view-tabs {
    padding-left: 0.25rem;
  }
  
  .view-tab {
    padding: 0.5rem 0.75rem;
    font-size: 0.8125rem;
  }
  
  .json-editor {
    padding-left: 2.5rem;
  }
  
  .tree-content {
    padding-left: 1rem;
  }
  
  .jsonpath-input-wrapper {
    margin-right: 0.25rem;
  }
  
  .jsonpath-input {
    width: 180px;
    font-size: 0.8125rem;
    padding: 0.25rem 0.5rem;
    padding-right: 24px;
  }
  
  .toast-notification {
    bottom: 10px;
    left: 1rem;
    right: 1rem;
    transform: none;
    min-width: auto;
    max-width: none;
  }
  
  @keyframes slideUp {
    from {
      opacity: 0;
      transform: translateY(20px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
  
  @keyframes slideDown {
    from {
      opacity: 1;
      transform: translateY(0);
    }
    to {
      opacity: 0;
      transform: translateY(20px);
    }
  }
}
</style>
