<script setup>
import { ref, computed, onMounted, watch, nextTick, onUnmounted } from 'vue';
import { VueFlow, useVueFlow, Panel } from '@vue-flow/core';
import { Controls, ControlButton } from '@vue-flow/controls';
import { MiniMap } from '@vue-flow/minimap';
import { Background } from '@vue-flow/background';
import { useFlowStore } from './store/flowStore';
import { flowExecutor } from './executors/flowExecutor.js';
import NodePalette from './components/Sidebar/NodePalette.vue';
import ExecutionResults from './components/ExecutionResults.vue';
import { getNodeComponents, getNodeMetadataByType } from './utils/nodeMetadata';

const store = useFlowStore();

const vueFlowInstance = ref(null);

const selectedNode = ref(null);
const selectedEdge = ref(null);
const showSidebar = ref(true);
const draggedNodeType = ref(null);

const showTemplateData = ref(false);
const openTemplateData = () => { showTemplateData.value = true; };
const closeTemplateData = () => { showTemplateData.value = false; };

// 执行结果显示
const showExecutionResults = ref(false);
const toggleExecutionResults = () => { 
  showExecutionResults.value = !showExecutionResults.value; 
};
const closeExecutionResults = () => { 
  showExecutionResults.value = false; 
};

const isExecuting = ref(false);
const executionResult = ref(null);
const executionHistory = ref([]);

// 全屏相关
const isFullscreen = ref(false);
const flowBuilderRoot = ref(null);

// 动态 Teleport 目标
const nodePropertyTeleportTarget = computed(() => {
  if (isFullscreen.value && flowBuilderRoot.value) {
    return flowBuilderRoot.value;
  }
  return 'body';
});

const toggleFullscreen = () => {
  const el = flowBuilderRoot.value;
  if (!el) return;
  if (!isFullscreen.value) {
    if (el.requestFullscreen) {
      el.requestFullscreen();
    } else if (el.webkitRequestFullscreen) {
      el.webkitRequestFullscreen();
    } else if (el.mozRequestFullScreen) {
      el.mozRequestFullScreen();
    } else if (el.msRequestFullscreen) {
      el.msRequestFullscreen();
    }
  } else {
    if (document.exitFullscreen) {
      document.exitFullscreen();
    } else if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
    } else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if (document.msExitFullscreen) {
      document.msExitFullscreen();
    }
  }
};

const fullscreenChangeHandler = () => {
  isFullscreen.value = !!(
    document.fullscreenElement ||
    document.webkitFullscreenElement ||
    document.mozFullScreenElement ||
    document.msFullscreenElement
  );
};

// 在 setup 顶部调用 useVueFlow
const { fitView } = useVueFlow();

// 添加 onPaneReady 钩子
const onPaneReady = () => {
  if (store.nodes.length > 0) {
    fitView({ padding: 0.7 });
  }
};

// 自动化获取所有节点组件
const nodeTypes = getNodeComponents();

// 获取节点颜色函数
const getNodeColor = (nodeType) => {
  const metadata = getNodeMetadataByType(nodeType);
  return metadata ? metadata.color : '#6b7280';
};

// 计算属性
const nodes = computed(() => store.nodes);
const edges = computed(() => store.edges);
const hasSelection = computed(() => selectedNode.value || selectedEdge.value);

// 节点选择处理
const onNodeClick = (event) => {
  selectedNode.value = event.node;
  selectedEdge.value = null;
  // 不弹窗
};

// 监听自定义节点的 edit-node 事件
function onEditNode(nodeId) {
  // 删除这个函数，因为节点组件现在自己处理编辑
}

// 边选择处理
const onEdgeClick = (event) => {
  selectedEdge.value = event.edge;
  selectedNode.value = null;
};

// 取消选择
const onPaneClick = () => {
  selectedNode.value = null;
  selectedEdge.value = null;
};

// 连接处理
const onConnect = (params) => {
  
  // 基本验证
  if (!params.source || !params.target) {
    return;
  }
  
  // 检查 handle 类型，禁止输出连输出
  const sourceHandle = params.sourceHandle || 'output';
  const targetHandle = params.targetHandle || 'input';
  if (sourceHandle === 'output' && targetHandle === 'output') {
    showBubbleTip('输出不能连接到输出', 'error');
    return;
  }

  // 检查是否已经存在相同的连接
  if (store.hasConnection(params.source, params.target, sourceHandle, targetHandle)) {
    // 如果已存在相同的连接，显示提示并返回
    showBubbleTip('连接已存在，请勿重复连线', 'error');
    return;
  }
  
  // 检查是否形成自环（节点连接到自己）
  if (params.source === params.target) {
    showBubbleTip('节点不能连接到自己', 'error');
    return;
  }
  
  const edge = {
    id: `edge-${Date.now()}`,
    source: params.source,
    target: params.target,
    sourceHandle: sourceHandle,
    targetHandle: targetHandle,
    type: 'smoothstep', // 现代平滑线
    animated: true,
    style: { 
      stroke: '#6366f1', 
      strokeWidth: 2.5,
      filter: 'drop-shadow(0 2px 8px #6366f133)'
    },
    markerEnd: {
      type: 'arrowclosed',
      color: '#6366f1'
    }
  };
  
  store.addEdge(edge);
  // 不再自动生成依赖声明（inputs），只保留连线本身
  store.markDirty();
};

// 连接开始处理
const onConnectStart = (event) => {
  
};

// 连接结束处理  
const onConnectEnd = (event) => {
  
};

// 节点拖拽处理
const onNodeDragStop = (event) => {
  store.updateNodePosition(event.node.id, event.node.position);
  store.markDirty();
};

// 删除选中元素
const deleteSelected = () => {
  if (selectedNode.value) {
    store.removeNode(selectedNode.value.id);
    selectedNode.value = null;
  }
  if (selectedEdge.value) {
    store.removeEdge(selectedEdge.value.id);
    selectedEdge.value = null;
  }
  store.markDirty();
};

// 拖放处理
const onDragOver = (event) => {
  event.preventDefault();
  event.dataTransfer.dropEffect = 'move';
};

// 新建节点时自动错开位置，避免重叠
let nodeCount = 0;
const getNextNodePosition = () => {
  const baseX = 100, baseY = 100, offset = 60;
  const x = baseX + (nodeCount % 5) * offset;
  const y = baseY + Math.floor(nodeCount / 5) * offset;
  nodeCount++;
  return { x, y };
};

const onDrop = (event) => {
  event.preventDefault();
  const type = event.dataTransfer.getData('application/vueflow');
  if (!type) return;
  // 获取 vue-flow 容器的 boundingRect
  const container = document.querySelector('.vue-flow-container');
  let position = { x: 100, y: 100 };
  if (container && vueFlowInstance.value && typeof vueFlowInstance.value.project === 'function') {
    const rect = container.getBoundingClientRect();
    const xInContainer = event.clientX - rect.left;
    const yInContainer = event.clientY - rect.top;
    position = vueFlowInstance.value.project({ x: xInContainer, y: yInContainer });
    // 加微小随机偏移，防止重叠
    position.x += Math.random() * 20 - 10;
    position.y += Math.random() * 20 - 10;
  }
  const newNode = {
    id: `${type}-${Date.now()}`,
    type,
    position,
    data: {
      ...getDefaultNodeData(type)
    }
  };
  store.addNode(newNode);
  store.markDirty();
  // 拖拽也加入最近使用
  try {
    let recent = JSON.parse(localStorage.getItem('ai-agent-node-recent') || '[]');
    recent = [type, ...recent.filter(t => t !== type)].slice(0, 12);
    localStorage.setItem('ai-agent-node-recent', JSON.stringify(recent));
    window.dispatchEvent(new Event('recent-node-updated'));
  } catch(e) {}
};

// 获取默认节点数据
const getDefaultNodeData = (type) => {
  // 只返回结构，不做任何参数内容硬编码
  return {
    inputs: {},
    config: {}
  };
};

// 键盘事件处理
const onKeyDown = (event) => {
  if (event.key === 'Delete' || event.key === 'Backspace') {
    deleteSelected();
  }
};

// 生命周期
onMounted(() => {
  nextTick(async () => {
    store.saveToHistory();
  });
  
  // 暴露 teleport 目标到全局，供节点组件使用
  window.nodePropertyTeleportTarget = {
    get value() {
      return nodePropertyTeleportTarget.value;
    }
  };
  
  // 添加键盘快捷键
  const handleKeyDown = (event) => {
    // 检查是否在输入框或可编辑元素中
    const isEditingText = event.target.tagName === 'INPUT' || 
                         event.target.tagName === 'TEXTAREA' || 
                         event.target.isContentEditable ||
                         event.target.closest('.form-control') ||
                         event.target.closest('input') ||
                         event.target.closest('textarea');
    
    // 如果在编辑文本，不处理删除键
    if (isEditingText && (event.key === 'Delete' || event.key === 'Backspace')) {
      return;
    }
    
    // Ctrl+Z 撤销
    if (event.ctrlKey && event.key === 'z' && !event.shiftKey) {
      event.preventDefault();
      store.undo();
    }
    // Ctrl+Y 或 Ctrl+Shift+Z 重做
    else if ((event.ctrlKey && event.key === 'y') || (event.ctrlKey && event.shiftKey && event.key === 'Z')) {
      event.preventDefault();
      store.redo();
    }
    // Delete 键删除选中的节点或边（仅当不在编辑状态时）
    else if (event.key === 'Delete' || event.key === 'Backspace') {
      deleteSelected();
    }
  };
  
  document.addEventListener('keydown', handleKeyDown);
  document.addEventListener('fullscreenchange', fullscreenChangeHandler);
  document.addEventListener('webkitfullscreenchange', fullscreenChangeHandler);
  document.addEventListener('mozfullscreenchange', fullscreenChangeHandler);
  document.addEventListener('MSFullscreenChange', fullscreenChangeHandler);
  
  // 清理事件监听器
  onUnmounted(() => {
    document.removeEventListener('keydown', handleKeyDown);
    document.removeEventListener('fullscreenchange', fullscreenChangeHandler);
    document.removeEventListener('webkitfullscreenchange', fullscreenChangeHandler);
    document.removeEventListener('mozfullscreenchange', fullscreenChangeHandler);
    document.removeEventListener('MSFullscreenChange', fullscreenChangeHandler);
  });
  
  // 加载保存的数据
  store.loadFromStorage();
  // 删除 window.openNodeProperties，因为不再需要
  window.vueFlowInstance = vueFlowInstance.value;
});

// 监听数据变化
watch(
  () => [store.nodes, store.edges],
  () => {
    store.saveToStorage();
  },
  { deep: true }
);

// 流程执行操作
const executeFlow = async () => {
  if (isExecuting.value) {
    jingrow.msgprint('流程正在执行中，请等待完成');
    return;
  }

  try {
    isExecuting.value = true;
    executionResult.value = null;
    executionHistory.value = [];

    // 获取智能体信息
    let agentName = null;
    if (store.frm && store.frm.pg && store.frm.pg.name) {
      agentName = store.frm.pg.name;
    }

    // 执行流程，传递智能体信息
    const result = await flowExecutor.executeFlow(
      store.nodes,
      store.edges,
      { agent_name: agentName }  // 传递智能体名称
    );

    executionResult.value = result;
    executionHistory.value = result.history || [];

    if (result.success) {
      // 不再弹出顶部提示，只保留主弹窗
    } else {
      // 不再弹出顶部提示，只保留主弹窗
    }
  } catch (error) {
    jingrow.msgprint(`执行出错: ${error.message}`, 'Error');
    } finally {
      isExecuting.value = false;
      // 执行完成后自动切换到执行结果画面
      if (executionResult.value) {
        showExecutionResults.value = true;
      }
    }
};

// 工具栏操作
const resetFlow = () => {
  store.resetFlow();
  selectedNode.value = null;
  selectedEdge.value = null;
};

// 删除Redo和验证流程按钮

const toggleSidebar = () => {
  showSidebar.value = !showSidebar.value;
};

// 删除 updateNodeData 函数，因为不再需要

// 状态提示相关
const showSaveStatus = ref(false);
const saveStatusText = ref('');
let saveStatusTimer = null;

// 监听 hasUnsavedChanges 变化，控制提示显示与自动消失
watch(
  () => store.hasUnsavedChanges,
  (val, oldVal) => {
    // 只有用户有过编辑操作后才显示
    if (oldVal !== undefined) {
      showSaveStatus.value = true;
      saveStatusText.value = val ? __('Unsaved') : __('Saved');
      if (saveStatusTimer) clearTimeout(saveStatusTimer);
      saveStatusTimer = setTimeout(() => {
        showSaveStatus.value = false;
      }, 3000);
    }
  }
);

const showCopyTip = ref(false);

// 复制流程数据到剪贴板
const copyTemplateData = async () => {
  try {
    const text = JSON.stringify({ nodes: nodes.value, edges: edges.value }, null, 2);
    await navigator.clipboard.writeText(text);
    showCopyTip.value = true;
    setTimeout(() => { showCopyTip.value = false; }, 2000);
  } catch (e) {
    jingrow.msgprint(__('复制失败，请手动复制'), 'Error');
  }
};

const bubbleTip = ref({ show: false, text: '', timeout: null, type: '' });
const showBubbleTip = (msg, type = '') => {
  bubbleTip.value.text = msg;
  bubbleTip.value.type = type;
  bubbleTip.value.show = true;
  if (bubbleTip.value.timeout) clearTimeout(bubbleTip.value.timeout);
  bubbleTip.value.timeout = setTimeout(() => {
    bubbleTip.value.show = false;
    bubbleTip.value.type = '';
  }, 2000);
};
</script>

<template>
  <div class="ai-agent-flow-builder" :class="{ fullscreen: isFullscreen }" ref="flowBuilderRoot">
    <!-- 顶部工具栏 -->
    <div class="toolbar">
      <div class="toolbar-left">
        <span class="node-count">
          {{ __("Nodes: {0}  Edges: {1}", [nodes.length, edges.length]) }}
        </span>
      </div>
              <div class="toolbar-right">
        <button 
          v-if="store.canUndo"
          class="btn btn-sm btn-outline-secondary"
          @click="store.undo"
          :title="__('Undo')"
        >
          <i class="fa fa-undo"></i>
          {{ __("Undo") }}
        </button>
        <button 
          class="btn btn-sm btn-outline-danger"
          @click="resetFlow"
        >
          <i class="fa fa-refresh"></i>
          {{ __("Reset") }}
        </button>
        <button 
          class="btn btn-sm btn-outline-info"
          @click="openTemplateData"
        >
          <i class="fa fa-code"></i>
          {{ __("View Data") }}
        </button>
        <button 
          class="btn btn-sm btn-outline-info"
          @click="toggleExecutionResults"
        >
          <i class="fa fa-list-alt"></i>
          {{ showExecutionResults ? __("Back to Flow") : __("Execution Results") }}
        </button>
        <button 
          class="btn btn-sm btn-outline-success"
          @click="executeFlow"
          :disabled="isExecuting"
        >
          <i class="fa fa-play"></i>
          {{ isExecuting ? __("Executing...") : __("Execute") }}
        </button>
        <button 
          class="btn btn-sm btn-outline-primary"
          @click="fitView"
        >
          <i class="fa fa-expand"></i>
          {{ __("Fit View") }}
        </button>
        <button 
          class="btn btn-sm add-node-btn"
          @click="toggleSidebar"
        >
          <i class="fa fa-plus"></i>
          {{ showSidebar ? __("Add Nodes") : __("Add Nodes") }}
        </button>
        <button class="btn btn-sm btn-outline-primary" @click="toggleFullscreen">
          <i :class="isFullscreen ? 'fa fa-compress' : 'fa fa-expand'"></i>
          {{ isFullscreen ? '退出全屏' : '全屏' }}
        </button>
      </div>
    </div>

    <!-- 主要内容区域 -->
    <div class="main-content">
      <!-- 执行结果全屏显示 -->
      <div v-if="showExecutionResults" class="execution-results-container">
        <ExecutionResults 
          :executionResult="executionResult"
          :executionHistory="executionHistory"
          @close="closeExecutionResults"
        />
      </div>
      
      <!-- 正常内容（当不显示执行结果时） -->
      <template v-else>
        <!-- Vue Flow 容器 -->
        <div class="flow-container">
        <!-- 状态提示：右上角绝对定位 -->
        <div v-if="showSaveStatus" class="status-indicator-fixed">
          <div v-if="store.hasUnsavedChanges" class="unsaved-indicator">
            <i class="fa fa-circle text-warning"></i>
            {{ saveStatusText }}
          </div>
          <div v-else class="saved-indicator">
            <i class="fa fa-check-circle text-success"></i>
            {{ saveStatusText }}
          </div>
        </div>
        <!-- 气泡提示，和保存提示同级同位 -->
        <transition name="fade">
          <div v-if="bubbleTip.show" :class="['bubble-tip-global', bubbleTip.type]">
            <template v-if="bubbleTip.type === 'error'">
              <i class="fa fa-exclamation-circle" style="margin-right:6px;"></i>
            </template>
            <template v-else-if="bubbleTip.type === 'warn'">
              <i class="fa fa-exclamation-triangle" style="margin-right:6px;"></i>
            </template>
            <template v-else>
              <i class="fa fa-check-circle" style="margin-right:6px;"></i>
            </template>
            {{ bubbleTip.text }}
          </div>
        </transition>
        <VueFlow
          ref="vueFlowInstance"
          :nodes="nodes"
          :edges="edges"
          :node-types="nodeTypes"
          class="vue-flow-container"
          @node-click="onNodeClick"
          @edge-click="onEdgeClick"
          @pane-click="onPaneClick"
          @connect="onConnect"
          @connect-start="onConnectStart"
          @connect-end="onConnectEnd"
          @node-drag-stop="onNodeDragStop"
          @drop="onDrop"
          @dragover="onDragOver"
          @edit-node="onEditNode"
          :min-zoom="0.2"
          :max-zoom="4"
          nodes-connectable
          nodes-draggable
          edges-updatable
          delete-key-code="Delete"
          :default-edge-options="{
            type: 'smoothstep', // 现代平滑线
            animated: true,
            style: {
              stroke: '#6366f1',
              strokeWidth: 2.5,
              filter: 'drop-shadow(0 2px 8px #6366f133)'
            },
            markerEnd: { type: 'arrowclosed', color: '#6366f1' }
          }"
          :connection-line-type="'smoothstep'"
          :connection-line-style="{ stroke: '#94a3b8', strokeWidth: 2, strokeDasharray: '5,5' }"
          @pane-ready="onPaneReady"
        >
          <!-- 背景 -->
          <Background pattern="dots" :gap="20" />
          
          <!-- 控制面板 -->
          <Controls>
            <ControlButton 
              v-if="hasSelection"
              @click="deleteSelected"
              title="Delete Selected"
            >
              <i class="fa fa-trash"></i>
            </ControlButton>
          </Controls>
          
          <!-- 小地图 -->
          <MiniMap 
            :node-stroke-width="3"
            :node-color="(node) => {
              return getNodeColor(node.type);
            }"
          />
          
          <!-- 顶部面板 -->
          <!-- Panel position="top-center" class="status-panel" 移除 -->
        </VueFlow>
      </div>
      <!-- 调试：显示 edges 和 nodes 的 JSON -->
      <!-- 模板数据弹窗 -->
      <div v-if="showTemplateData" class="template-data-modal">
        <div class="template-data-content">
          <div class="template-data-header">
            <span>{{ __("流程数据") }}</span>
            <div style="display: flex; align-items: center; gap: 8px;">
              <button 
                class="copy-btn" 
                @click="copyTemplateData"
                :title="__('复制')"
              >
                <i class="fa fa-copy"></i>
              </button>
              <button class="close-btn" @click="closeTemplateData">×</button>
            </div>
          </div>
          <pre style="max-height:60vh;overflow:auto;background:#f3f4f6;color:#222;font-size:12px;">
{{ JSON.stringify({ nodes, edges }, null, 2) }}
          </pre>
          <transition name="fade">
            <div v-if="showCopyTip" class="copy-tip">{{ __('已复制') }}</div>
          </transition>
        </div>
        <div class="template-data-mask" @click="closeTemplateData"></div>
      </div>


        <!-- 侧边栏 -->
        <div v-if="showSidebar" class="sidebar">
          <div class="sidebar-content">
            <!-- 只显示节点面板 -->
            <NodePalette class="node-palette" />
          </div>
        </div>

        <!-- 节点属性弹窗 -->
        <!-- 移除 NodeProperties 组件 -->
      </template>
    </div>
  </div>
</template>

<style scoped>
.ai-agent-flow-builder {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #f8fafc;
}
.ai-agent-flow-builder.fullscreen {
  position: fixed !important;
  top: 0; left: 0; right: 0; bottom: 0;
  width: 100vw;
  height: 100vh;
  z-index: 99999;
  background: #f8fafc;
  box-shadow: none;
}

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: white;
  border-bottom: 1px solid #e5e7eb;
  flex-shrink: 0;
}

.toolbar-left {
  display: flex;
  align-items: center;
  gap: 16px;
}

.toolbar-left h4 {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  color: #1f2937;
}

.node-count {
  font-size: 0.875rem;
  color: #6b7280;
}

.toolbar-right {
  display: flex;
  gap: 8px;
}

.main-content {
  display: flex;
  flex: 1;
  overflow: hidden;
}

.execution-results-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Vue Flow Handle基础样式 - 避免冲突 */
:deep(.vue-flow__handle) {
  width: 14px !important;
  height: 14px !important;
  background: #fff !important;
  border: 2.5px solid #6366f1 !important;
  box-shadow: 0 2px 8px rgba(99,102,241,0.15) !important;
  border-radius: 50% !important;
  cursor: crosshair !important;
  z-index: 10 !important;
  pointer-events: auto !important;
  transition: box-shadow 0.2s, border-color 0.2s;
}

:deep(.vue-flow__handle:hover) {
  box-shadow: 0 0 0 4px #6366f133 !important;
  border-color: #4338ca !important;
}

:deep(.vue-flow__handle.connecting) {
  animation: handle-pulse 1s infinite !important;
  cursor: crosshair !important;
}

:deep(.vue-flow__handle.connectingfrom) {
  animation: handle-pulse 1s infinite !important;
  cursor: crosshair !important;
}

:deep(.vue-flow__handle.connectingto) {
  animation: handle-connecting 0.5s ease-out !important;
  cursor: crosshair !important;
}

@keyframes handle-pulse {
  0% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.3); opacity: 0.8; }
  100% { transform: scale(1); opacity: 1; }
}

@keyframes handle-connecting {
  0% { transform: scale(1); }
  50% { transform: scale(1.4); }
  100% { transform: scale(1.2); }
}

/* 连接线样式优化 */
:deep(.vue-flow__edge.animated path) {
  stroke-dasharray: 5;
  animation: dashdraw 0.5s linear infinite;
}

:deep(.vue-flow__edge.selected path) {
  stroke-width: 3;
}

:deep(.vue-flow__edge:hover path) {
  stroke-width: 3;
}

@keyframes dashdraw {
  to {
    stroke-dashoffset: -10;
  }
}

/* 连接线指示器 */
:deep(.vue-flow__connectionline) {
  stroke: #94a3b8 !important;
  stroke-width: 2 !important;
  stroke-dasharray: 5,5 !important;
}

/* 背景网格优化 */
:deep(.vue-flow__background) {
  background-color: #fafafa;
}

:deep(.vue-flow__background .react-flow__background-pattern) {
  color: #e2e8f0;
}

/* 节点样式全局覆盖 */
:deep(.vue-flow__node) {
  font-size: 14px !important;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important;
}

:deep(.vue-flow__node .title) {
  font-size: 14px !important;
  font-weight: 600 !important;
  letter-spacing: 0.3px !important;
}

:deep(.vue-flow__node .form-group label) {
  font-size: 11px !important;
  font-weight: 600 !important;
}

:deep(.vue-flow__node .value) {
  font-size: 13px !important;
  font-weight: 500 !important;
}

.flow-container {
  flex: 1;
  position: relative;
}

.vue-flow-container {
  height: 100%;
  background: #f8fafc;
}

.sidebar {
  width: 360px;
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}

.sidebar-tabs {
  display: flex;
  border-bottom: 1px solid #e5e7eb;
}

.tab-button {
  flex: 1;
  padding: 12px;
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 0.875rem;
  color: #6b7280;
  transition: all 0.2s;
}

.tab-button.active {
  color: #3b82f6;
  border-bottom: 2px solid #3b82f6;
  background: #f8fafc;
}

.tab-button:hover {
  background: #f3f4f6;
}

.sidebar-content {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
}

.status-panel {
  background: white;
  border-radius: 6px;
  padding: 8px 12px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  font-size: 0.875rem;
}

.unsaved-indicator {
  color: #f59e0b;
  display: flex;
  align-items: center;
  gap: 4px;
}

.saved-indicator {
  color: #22c55e;
  display: flex;
  align-items: center;
  gap: 4px;
}

.edge-properties {
  .form-group {
    margin-bottom: 16px;
  }

  .form-group label {
    display: block;
    margin-bottom: 4px;
    font-weight: 500;
    color: #374151;
  }

  .form-control {
    width: 100%;
    padding: 8px;
    border: 1px solid #d1d5db;
    border-radius: 4px;
    font-size: 0.875rem;
  }
}

/* Vue Flow 样式覆盖 */
:deep(.vue-flow__node) {
  font-family: inherit;
}

:deep(.vue-flow__edge) {
  cursor: pointer;
}

:deep(.vue-flow__edge:hover) {
  stroke-width: 3;
}

:deep(.vue-flow__controls) {
  button {
    border: 1px solid #d1d5db;
    background: white;
    color: #374151;
    
    &:hover {
      background: #f3f4f6;
    }
  }
}

:deep(.vue-flow__minimap) {
  border: 1px solid #d1d5db;
  border-radius: 4px;
}

/* 模板数据弹窗样式 */
.template-data-modal {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
}
.template-data-content {
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 32px rgba(0,0,0,0.18);
  padding: 0;
  min-width: 420px;
  max-width: 90vw;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
}
.template-data-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  border-bottom: 1px solid #e5e7eb;
  font-weight: 600;
  font-size: 15px;
}
.close-btn {
  background: none;
  border: none;
  font-size: 22px;
  cursor: pointer;
  color: #888;
  margin-left: 12px;
}
.template-data-content pre {
  margin: 0;
  padding: 18px 20px;
  background: #f3f4f6;
  border-radius: 0 0 8px 8px;
  font-size: 12px;
  color: #222;
  flex: 1;
}
.template-data-mask {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.18);
  z-index: -1;
}


.status-indicator-fixed {
  position: absolute;
  top: 18px;
  right: 28px;
  z-index: 20;
  background: white;
  border-radius: 6px;
  padding: 8px 14px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.10);
  font-size: 0.95rem;
  min-width: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  text-align: center;
}

.copy-btn {
  background: none;
  border: none;
  color: #6B7280;
  font-size: 18px;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 5px;
  transition: background 0.15s;
}
.copy-btn:hover {
  background: #f3f3f3;
  color: #1fc76f;
}
.copy-tip {
  position: absolute;
  top: 18px;
  right: 60px;
  background: #fff;
  color: #222;
  padding: 8px 20px;
  border-radius: 5px;
  font-size: 13px;
  opacity: 0.92;
  z-index: 10001;
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(0,0,0,0.10);
  transition: opacity 0.2s;
}
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.2s;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}
.add-node-btn {
  color: #1A1A1A;
  background: #fff;
  transition: background 0.2s, color 0.2s;
}
.add-node-btn:hover, .add-node-btn:focus {
  background: #1fc76f;
  color: #fff;
  border-color: #1fc76f;
}
.bubble-tip-global {
  position: absolute;
  top: 18px;
  right: 28px;
  z-index: 100000;
  background: #fff;
  color: #6B7280; /* 默认红色，符合错误提示 */
  font-size: 0.95rem;
  border-radius: 6px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.10);
  padding: 8px 14px;
  min-width: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  text-align: center;
  font-weight: 500;
  transition: opacity 0.18s;
}
.bubble-tip-global.warn {
  color: #f59e0b;
}
.bubble-tip-global.error {
  color: #ef4444;
}
.bubble-tip-global.success {
  color: #22c55e;
}
.ai-agent-flow-builder.fullscreen .bubble-tip-global {
  z-index: 100000;
}
</style> 