@yiyi_zhang/reactflow-ui 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,37 +1,64 @@
1
1
  # Conductor Workflow IDE (ReactFlow UI)
2
2
 
3
- 基于 React 19, ReactFlow 和 Vite 构建的企业级 [Netflix Conductor](https://conductor-oss.org/) 工作流可视化设计器。
4
-
5
- ![Workflow IDE](https://user-images.githubusercontent.com/placeholder-image.png)
6
-
7
- ## ✨ 特性
8
-
9
- - **从零创建**: 无需预先提供 JSON,通过空状态引导面板即可新建空白工作流,配合中心 [+] 引导按钮快速开始建模。
10
- - **极简设计**: 移除了冗余的开始 (START)、结束 (END) 及虚拟合并 (JOIN) 节点,工作流视图更加简洁直观。
11
- - **可视化建模**: 支持 DAG 工作流的拖拽式设计,连接线默认使用清晰的折线样式。
12
- - **智能布局**: 支持复杂图表的自动布局 (TB/LR),包括针对超大流程优化的"蛇形布局"。
13
- - **运行态监控**: 实时可视化执行状态,支持路径高亮和任务详情查看。
14
- - **强大的搜索**: 毫秒级任务搜索与高亮定位。
15
- - **AI 灵感辅助 (AI Copilot)**:
16
- - **从零生成**: 空画布下直接通过自然语言描述生成完整工作流。
17
- - **对话建模**: 通过自然语言描述直接生成 Conductor 流程草图。
18
- - **智能提示**: 在属性面板中基于上下文自动推荐参数配置 (JSONPath)。
19
- - **企业级能力**:
20
- - TypeScript 全量类型定义 (WorkflowDef, TaskDef)
21
- - 深色/浅色主题支持
22
- - 只读模式 / 编辑模式切换
23
- - 撤销/重做 & 智能剪贴板
24
- - 命令式 API (ref) 支持程序化创建、保存、导出
3
+ 基于 React 19、ReactFlow 和 Vite 构建的企业级 [Netflix Conductor](https://conductor-oss.org/) 工作流可视化设计器。
4
+
5
+ ## ✨ 核心特性
6
+
7
+ ### 🎨 可视化建模
8
+ - **从零创建**:无需预先提供 JSON,通过空状态引导面板新建空白工作流,配合中心 **[+]** 引导按钮快速开始建模
9
+ - **完整任务类型**:支持 HTTP、SIMPLE、INLINE/LAMBDA、SWITCH、FORK_JOIN、DO_WHILE、SUB_WORKFLOW、SET_VARIABLE、TERMINATE 等全量 Conductor 任务类型
10
+ - **极简视图**:移除冗余的 START/END 虚拟节点,工作流图更加聚焦业务逻辑
11
+ - **智能布局**:自动 Dagre 布局(TB/LR),超大流程自动触发蛇形布局,编辑/只读模式行为一致
12
+
13
+ ### 🔀 复杂结构编辑
14
+ - **SWITCH 分支管理**:在节点上直接添加、重命名、删除分支,全部通过页面内 Modal,无浏览器原生弹框
15
+ - **DO_WHILE 循环体**:循环任务以容器方式展示,支持在循环体内任意位置插入任务(含首位)
16
+ - **FORK_JOIN 并行分支**:支持动态分支添加和管理
17
+
18
+ ### ✏️ 编辑体验
19
+ - **全屏代码编辑器**:点击任意表达式/脚本/JSON 字段右上角 **⛶** 按钮,展开全屏编辑器,支持 Ctrl+S 保存、Esc 取消,底部状态栏显示行数和字符数
20
+ - **任务引用名实时编辑**:引用名输入框支持流畅输入,失焦/回车后提交,避免逐字符刷新
21
+ - **撤销/重做**:完整的编辑历史,支持 Ctrl+Z / Ctrl+Shift+Z
22
+ - **智能剪贴板**:任务复制粘贴
23
+
24
+ ### 👁️ 视图模式
25
+ - **Business 模式**:仅展示核心业务节点,控制/数据节点折叠为边标签
26
+ - **Standard 模式**:业务 + 控制流节点(默认)
27
+ - **Developer 模式**:显示全部节点,含数据转换等辅助节点,节点上展示表达式预览
28
+
29
+ ### 🚀 运行态监控
30
+ - 节点根据执行状态(Scheduled / In Progress / Completed / Failed / Timed Out / Skipped)实时变色
31
+ - 已完成路径连线高亮,进行中路径动态动画,编辑/只读模式使用静态实线
32
+ - 点击节点展示执行详情面板(输入/输出/耗时/重试次数)
33
+ - **运行态操作**:暂停、继续、终止、重试、重启(支持选择定义版本)、从指定任务重跑、跳过任务
34
+ - `allowOperations` 全局权限开关,快速控制操作按钮是否可用
35
+
36
+ ### 🤖 AI Copilot(智能辅助)
37
+ - **仅在编辑模式可用**,避免干扰只读/运行态场景
38
+ - 自然语言描述生成完整工作流,支持对话式迭代修改
39
+ - 差异卡片(Diff Card)预览变更内容,一键应用/撤销
40
+ - 参数智能提示:节点属性面板中基于上下文推荐 JSONPath 表达式
41
+
42
+ ### 🎯 视觉体验
43
+ - 深色/浅色主题 + 品牌主色调(科技蓝/活力橙)
44
+ - 节点边框和阴影经过调校,深色模式下更清晰可辨
45
+ - 切换工作流时自动执行 Fit View,无需手动缩放定位
46
+ - 校验错误徽章(❗⚠️)仅在编辑模式显示,不干扰只读查看
47
+
48
+ ### 🏢 企业级能力
49
+ - TypeScript 全量类型定义(WorkflowDef、TaskDef、ExecutionActions…)
50
+ - 命令式 API(`ref`)支持程序化创建、保存、导出
51
+ - 强类型 Ref API:`getWorkflowDef()`、`save()`、`createBlankWorkflow()`
52
+
53
+ ---
25
54
 
26
55
  ## 📦 安装
27
56
 
28
57
  ```bash
29
- npm install reactflow-ui
58
+ npm install reactflow-ui reactflow react react-dom
30
59
  ```
31
60
 
32
- ## 🚀 使用指南
33
-
34
- ### 库模式 (集成到 React 应用)
61
+ ## 🚀 快速开始
35
62
 
36
63
  ```tsx
37
64
  import { useRef } from 'react';
@@ -43,12 +70,11 @@ function App() {
43
70
 
44
71
  return (
45
72
  <div style={{ height: '100vh' }}>
46
- {/* 宿主应用可通过 ref 程序化新建工作流 */}
47
73
  <button onClick={() => ideRef.current?.createBlankWorkflow('my_flow')}>新建</button>
48
-
49
74
  <WorkflowIDE
50
75
  ref={ideRef}
51
76
  theme="dark"
77
+ themeColor="blue"
52
78
  onSave={(workflow) => console.log('保存:', workflow)}
53
79
  onRequestImport={() => { /* 打开文件选择器 */ }}
54
80
  />
@@ -57,52 +83,65 @@ function App() {
57
83
  }
58
84
  ```
59
85
 
60
- 不传 `workflowDef` 时,画布会显示引导面板,支持空白创建、AI 生成或导入 JSON。
86
+ 不传 `workflowDef` 时画布显示引导面板,支持空白创建、AI 生成或导入 JSON。
61
87
 
62
- 详细集成步骤和 API 参考,请查阅 **[集成指南](./INTEGRATION.md)**。
88
+ 详细集成步骤和 API 参考:**[集成指南 →](./INTEGRATION.md)**
63
89
 
64
- ## 🛠️ 开发指南 (Development Guide)
90
+ ---
65
91
 
66
- 如果您希望参与本项目的开发或进行二次开发,请参考以下步骤。
92
+ ## 🛠️ 开发指南
67
93
 
68
- ### 环境准备
94
+ ### 环境要求
69
95
  - Node.js >= 18.0.0
70
96
  - npm 或 yarn
71
97
 
72
- ### 启动本地开发环境
73
- 本项目包含一个演示应用 (`src/demo`),用于开发和调试 IDE 组件。
98
+ ### 本地开发
74
99
 
75
100
  ```bash
76
- # 1. 克隆仓库
101
+ # 克隆仓库
77
102
  git clone https://github.com/yiyisf/reactflow-ui.git
78
103
  cd reactflow-ui
79
104
 
80
- # 2. 安装依赖
105
+ # 安装依赖
81
106
  npm install
82
107
 
83
- # 3. 启动开发服务器
108
+ # 启动开发服务器(含演示应用)
84
109
  npm run dev
110
+ # 访问 http://localhost:5173
85
111
  ```
86
- 启动后访问 `http://localhost:5173` 即可看到演示页面。
87
112
 
88
- ### 构建库文件
89
- 打包生成用于发布的 ESM 和 UMD 文件:
113
+ ### 构建
90
114
 
91
115
  ```bash
92
116
  npm run build
117
+ # 产物生成在 dist/ 目录
93
118
  ```
94
- 产物将生成在 `dist/` 目录下。
95
119
 
96
120
  ### 项目结构
97
- - `src/components/`: IDE 核心组件 (设计器、属性面板等)。
98
- - `src/store/`: 基于 Zustand 的状态管理。
99
- - `src/types/`: TypeScript 类型定义 (Conductor 协议与 UI 状态)。
100
- - `src/demo/`: 开发阶段使用的演示应用。
101
- - `src/WorkflowIDE.tsx`: 组件库的主入口文件。
121
+
122
+ ```
123
+ src/
124
+ ├── components/ # UI 核心组件(设计器、属性面板、节点…)
125
+ │ ├── nodes/ # 各类型节点组件(TaskNode、DecisionNode、LoopNode…)
126
+ │ ├── AICopilot/ # AI 助手面板
127
+ │ └── Controls/ # 工具栏、模式切换器
128
+ ├── store/ # Zustand 状态管理(workflowStore)
129
+ ├── parser/ # Conductor JSON ↔ ReactFlow 节点/边转换
130
+ ├── layout/ # Dagre 自动布局(含蛇形布局)
131
+ ├── types/ # TypeScript 类型定义
132
+ ├── styles/ # CSS 变量和执行状态样式
133
+ ├── utils/ # 校验器、工作流生成工具
134
+ └── demo/ # 演示应用(开发调试用)
135
+ ```
136
+
137
+ ---
102
138
 
103
139
  ## 📚 文档资源
140
+
104
141
  - [集成指南 (Integration Guide)](./INTEGRATION.md)
105
142
  - [最佳实践 (Best Practices)](./docs/best-practices.md)
143
+ - [路线图 (Roadmap)](./ROADMAP.md)
106
144
 
107
145
  ## 📄 许可证
146
+
108
147
  MIT
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ThemeMode, ThemeColor, LayoutDirection, ValidationResults, ExecutionActions } from './types/workflow';
2
+ import { ThemeMode, ThemeColor, LayoutDirection, ValidationResults, ExecutionActions, ViewMode } from './types/workflow';
3
3
  import { WorkflowDef } from './types/conductor';
4
4
  import { AIServiceConfig } from './services/aiService';
5
5
  import './styles/tokens.css';
@@ -80,6 +80,14 @@ export interface WorkflowIDEProps {
80
80
  * 未传入的回调对应的按钮不会渲染。
81
81
  */
82
82
  executionActions?: ExecutionActions;
83
+ /**
84
+ * 视图模式(仅在编辑/查看态生效,运行态强制为 developer)。
85
+ * - `business`:仅展示核心业务节点
86
+ * - `standard`:业务 + 控制流节点
87
+ * - `developer`:展示所有节点(含数据转换)
88
+ * @default 'developer'
89
+ */
90
+ viewMode?: ViewMode;
83
91
  /**
84
92
  * AI 配置。通过 Props 注入优先于 localStorage 配置。
85
93
  */
@@ -0,0 +1,10 @@
1
+ import { WorkflowDiff } from '../../types/workflow';
2
+ import './DiffCard.css';
3
+ interface DiffCardProps {
4
+ diff: WorkflowDiff;
5
+ applied: boolean;
6
+ onApply: () => void;
7
+ onUndo: () => void;
8
+ }
9
+ export default function DiffCard({ diff, applied, onApply, onUndo }: DiffCardProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -1,5 +1,9 @@
1
+ import { ExecutionActions } from '../types/workflow';
2
+ interface ExecutionTaskPanelProps {
3
+ executionActions?: ExecutionActions;
4
+ }
1
5
  /**
2
- * 运行态任务详情面板
6
+ * 运行态任务详情面板 — 与定义态 TaskDetailPanel 样式对齐,全高侧边布局
3
7
  */
4
- declare const ExecutionTaskPanel: React.FC;
8
+ declare const ExecutionTaskPanel: React.FC<ExecutionTaskPanelProps>;
5
9
  export default ExecutionTaskPanel;
@@ -0,0 +1,23 @@
1
+ interface FullscreenEditorProps {
2
+ /** 标题,显示在顶部工具栏 */
3
+ title: string;
4
+ /** 当前值 */
5
+ value: string;
6
+ /** 语言提示标签(用于展示,不做真正语法高亮) */
7
+ language?: string;
8
+ /** 占位文本 */
9
+ placeholder?: string;
10
+ /** 保存回调(点击"应用"或 Ctrl+S 时触发) */
11
+ onSave: (value: string) => void;
12
+ /** 关闭(不保存)回调 */
13
+ onClose: () => void;
14
+ }
15
+ /**
16
+ * 全屏代码/表达式编辑器
17
+ *
18
+ * - 使用 createPortal 挂载到 document.body,脱离 ReactFlow transform 上下文
19
+ * - Ctrl+S 保存并关闭,Escape 取消,顶部工具栏有"应用"与"取消"按钮
20
+ * - 继承当前主题 CSS 变量
21
+ */
22
+ declare const FullscreenEditor: ({ title, value, language, placeholder, onSave, onClose, }: FullscreenEditorProps) => import("react").ReactPortal;
23
+ export default FullscreenEditor;
@@ -0,0 +1,17 @@
1
+ interface PromptDialogProps {
2
+ title: string;
3
+ /** 输入框上方的说明文字(可选) */
4
+ label?: string;
5
+ defaultValue?: string;
6
+ placeholder?: string;
7
+ confirmText?: string;
8
+ cancelText?: string;
9
+ onConfirm: (value: string) => void;
10
+ onCancel: () => void;
11
+ }
12
+ /**
13
+ * 页面内输入弹框,替代 window.prompt。
14
+ * 使用 createPortal 渲染到 document.body,继承 CSS 主题变量。
15
+ */
16
+ declare const PromptDialog: ({ title, label, defaultValue, placeholder, confirmText, cancelText, onConfirm, onCancel, }: PromptDialogProps) => import("react").ReactPortal;
17
+ export default PromptDialog;
@@ -10,6 +10,8 @@ interface NodeWrapperProps {
10
10
  isDecision?: boolean;
11
11
  isHighlighted?: boolean;
12
12
  executionStatus?: ExecutionStatus;
13
+ simRunning?: boolean;
14
+ simDone?: boolean;
13
15
  }
14
- declare const NodeWrapper: ({ children, nodeId, selected, isStartOrEnd, isError, hasWarning, isDecision, isHighlighted, executionStatus }: NodeWrapperProps) => import("react/jsx-runtime").JSX.Element;
16
+ declare const NodeWrapper: ({ children, nodeId, selected, isStartOrEnd, isError, hasWarning, isDecision, isHighlighted, executionStatus, simRunning, simDone, }: NodeWrapperProps) => import("react/jsx-runtime").JSX.Element;
15
17
  export default NodeWrapper;
@@ -1,4 +1,7 @@
1
+ import { ViewMode } from '../types/workflow';
1
2
  export type TaskCategory = 'CORE' | 'SYSTEM' | 'FLOW';
3
+ /** 视图模式下的可见分类 */
4
+ export type ViewCategory = 'business' | 'control' | 'data';
2
5
  export interface TaskTypeConfig {
3
6
  type: string;
4
7
  label: string;
@@ -6,7 +9,11 @@ export interface TaskTypeConfig {
6
9
  category: TaskCategory;
7
10
  icon: any;
8
11
  hidden?: boolean;
12
+ /** 视图模式分类:business=核心业务节点,control=控制流节点,data=数据转换节点 */
13
+ viewCategory: ViewCategory;
9
14
  }
15
+ /** 各视图模式下可见的 viewCategory 列表 */
16
+ export declare const CATEGORY_VISIBILITY: Record<ViewMode, ViewCategory[]>;
10
17
  export declare const TASK_TYPES: TaskTypeConfig[];
11
18
  export declare const TASK_CATEGORIES: {
12
19
  key: TaskCategory;
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export { WorkflowIDE } from './WorkflowIDE';
2
2
  export type { WorkflowIDEProps, WorkflowIDERef } from './WorkflowIDE';
3
3
  export type { WorkflowDef, TaskDef } from './types/conductor';
4
4
  export type { AIServiceConfig } from './services/aiService';
5
- export type { ExecutionActions } from './types/workflow';
5
+ export type { ExecutionActions, RestartOptions, ViewMode } from './types/workflow';
@@ -9,7 +9,7 @@ interface AutoLayoutOptions {
9
9
  snakeMinNodeCount?: number;
10
10
  }
11
11
  /**
12
- * 自动布局主函数
12
+ * 自动布局主函数(支持 parentId 子节点)
13
13
  */
14
14
  export declare function getLayoutedElements(nodes: WorkflowNode[], edges: Edge[], options?: AutoLayoutOptions): {
15
15
  nodes: WorkflowNode[];
@@ -1 +1 @@
1
- .react-flow{direction:ltr}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;cursor:-webkit-grab;cursor:grab}.react-flow__pane.selection{cursor:pointer}.react-flow__pane.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow .react-flow__edges{pointer-events:none;overflow:visible}.react-flow__edge-path,.react-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.react-flow__edge{pointer-events:visibleStroke;cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;-webkit-animation:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge:focus .react-flow__edge-path,.react-flow__edge:focus-visible .react-flow__edge-path{stroke:#555}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge-textbg{fill:#fff}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__connectionline{z-index:1001}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:-webkit-grab;cursor:grab}.react-flow__node.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:-webkit-grab;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background:#1a192b;border:1px solid white;border-radius:100%}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:-4px;transform:translate(-50%)}.react-flow__handle-top{left:50%;top:-4px;transform:translate(-50%)}.react-flow__handle-left{top:50%;left:-4px;transform:translateY(-50%)}.react-flow__handle-right{right:-4px;top:50%;transform:translateY(-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:#ffffff80;padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@-webkit-keyframes dashdraw{0%{stroke-dashoffset:10}}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-default,.react-flow__node-input,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:3px;width:150px;font-size:12px;color:#222;text-align:center;border-width:1px;border-style:solid;border-color:#1a192b;background-color:#fff}.react-flow__node-default.selectable:hover,.react-flow__node-input.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:0 0 0 .5px #1a192b}.react-flow__node-group{background-color:#f0f0f040}.react-flow__nodesselection-rect,.react-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls{box-shadow:0 0 2px 1px #00000014}.react-flow__controls-button{border:none;background:#fefefe;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:5px}.react-flow__controls-button:hover{background:#f4f4f4}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__minimap{background-color:#fff}.react-flow__minimap svg{display:block}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:#3367d9;transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:#3367d9;border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.empty-state-panel{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10;display:flex;flex-direction:column;align-items:center;gap:24px;padding:48px 40px;border-radius:var(--radius-xl);background:var(--glass-surface);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--glass-border);min-width:360px;max-width:420px}.empty-state-title{font-size:20px;font-weight:600;color:var(--text-primary);margin:0;text-align:center}.empty-state-subtitle{font-size:13px;color:var(--text-secondary);margin:0;text-align:center;line-height:1.5}.empty-state-name-input{width:100%;padding:10px 14px;border-radius:var(--radius-md);border:1px solid var(--border-primary);background:var(--bg-primary);color:var(--text-primary);font-size:14px;font-family:var(--font-sans);outline:none;transition:border-color .2s}.empty-state-name-input:focus{border-color:var(--color-accent)}.empty-state-name-input::placeholder{color:var(--text-tertiary)}.empty-state-actions{display:flex;flex-direction:column;gap:12px;width:100%}.empty-state-btn{display:flex;align-items:center;justify-content:center;gap:8px;padding:12px 20px;border-radius:var(--radius-md);border:1px solid var(--border-primary);background:var(--bg-tertiary);color:var(--text-primary);font-size:14px;font-weight:500;font-family:var(--font-sans);cursor:pointer;transition:background .2s,border-color .2s;width:100%}.empty-state-btn:hover{background:var(--bg-highlight);border-color:var(--text-tertiary)}.empty-state-btn.primary{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}.empty-state-btn.primary:hover{opacity:.9}.empty-state-btn-icon{font-size:16px}.parameter-suggester-popover{padding:8px;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006;animation:popIn .2s cubic-bezier(.175,.885,.32,1.275)}@keyframes popIn{0%{transform:scale(.95);opacity:0}to{transform:scale(1);opacity:1}}.suggester-overlay{position:fixed;inset:0;z-index:2999}.suggester-header{padding:8px 12px;font-size:11px;font-weight:700;color:var(--color-accent-text);text-transform:uppercase;letter-spacing:1px;display:flex;align-items:center;gap:6px;border-bottom:1px solid var(--border-primary);margin-bottom:4px}.suggester-list{max-height:250px;overflow-y:auto}.suggestion-item{padding:10px 12px;border-radius:6px;cursor:pointer;transition:background .2s}.suggestion-item:hover{background:var(--bg-highlight)}.suggestion-label{font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:2px}.suggestion-value{font-size:11px;font-family:var(--font-mono);color:var(--color-accent);margin-bottom:4px}.suggestion-desc{font-size:11px;color:var(--text-secondary);line-height:1.4}.suggester-empty{padding:20px;text-align:center;font-size:12px;color:var(--text-muted)}@keyframes suggesterShimmer{0%{background-position:-200px 0}to{background-position:200px 0}}.suggester-loading{padding:16px 12px;display:flex;flex-direction:column;gap:10px}.suggester-skeleton-line{height:14px;border-radius:6px;background:linear-gradient(90deg,var(--bg-tertiary) 25%,var(--bg-highlight) 50%,var(--bg-tertiary) 75%);background-size:400px 100%;animation:suggesterShimmer 1.5s infinite linear}.ai-chat-panel{position:fixed;bottom:20px;right:20px;width:480px;height:70vh;max-height:calc(85vh - 40px);background:var(--glass-surface);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--glass-border);border-radius:16px;display:flex;flex-direction:column;box-shadow:0 12px 40px #0006;z-index:2000;overflow:hidden;transition:all .3s cubic-bezier(.16,1,.3,1);animation:slideUpIn .4s cubic-bezier(.16,1,.3,1)}@keyframes slideUpIn{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.ai-chat-panel.collapsed{height:48px;width:200px;cursor:pointer}.ai-header{padding:12px 16px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border-primary);background:#ffffff08}.ai-title{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:600;color:var(--color-accent-text)}.ai-sparkles{font-size:16px;animation:sparkleRotate 4s infinite linear}@keyframes sparkleRotate{0%{transform:rotate(0) scale(1)}50%{transform:rotate(180deg) scale(1.2)}to{transform:rotate(360deg) scale(1)}}.ai-close{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:18px;padding:4px;display:flex;align-items:center;border-radius:4px}.ai-close:hover{background:var(--bg-highlight);color:var(--text-primary)}.ai-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:16px}.message{padding:10px 14px;border-radius:12px;max-width:95%;font-size:13px;line-height:1.5;position:relative}.message.user{align-self:flex-end;background:var(--bg-tertiary);color:var(--text-primary);border-bottom-right-radius:2px}.message.ai{align-self:flex-start;background:#3b82f61a;border:1px solid rgba(59,130,246,.2);color:var(--text-primary);border-bottom-left-radius:2px}.message pre{background:var(--bg-primary);padding:8px;border-radius:6px;font-family:var(--font-mono);font-size:12px;margin:8px 0;overflow-x:auto;max-height:400px;overflow-y:auto}.ai-input-area{padding:16px;border-top:1px solid var(--border-primary);background:#0000001a}.ai-input-container{display:flex;gap:8px;background:var(--bg-secondary);border:1px solid var(--border-primary);border-radius:8px;padding:8px;transition:border-color .2s}.ai-input-container:focus-within{border-color:var(--color-accent);box-shadow:0 0 0 2px var(--color-accent-bg)}.ai-input{flex:1;background:none;border:none;color:var(--text-primary);font-size:13px;outline:none;resize:none;min-height:44px;max-height:120px}.ai-send{background:var(--color-accent);color:#fff;border:none;border-radius:6px;padding:4px 10px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform .1s}.ai-send:hover{filter:brightness(1.1)}.ai-send:active{transform:scale(.95)}.change-card{margin-top:12px;background:var(--bg-primary);border:1px solid var(--border-secondary);border-radius:8px;overflow:hidden}.card-header{padding:8px 12px;background:var(--bg-highlight);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--text-secondary)}.card-actions{display:flex;border-top:1px solid var(--border-primary)}.card-btn{flex:1;padding:8px;background:none;border:none;color:var(--color-accent-text);font-size:12px;font-weight:500;cursor:pointer;transition:background .2s}.card-btn:hover{background:var(--bg-highlight)}.card-btn.apply{color:var(--color-success);border-left:1px solid var(--border-primary)}@keyframes blink{0%,50%{opacity:1}51%,to{opacity:0}}.streaming-cursor{animation:blink 1s infinite}.message.ai.error{border-color:#ef444466;background:#ef444414}.retry-btn{margin-top:8px;padding:4px 12px;background:#ef444426;color:#f87171;border:1px solid rgba(239,68,68,.3);border-radius:6px;font-size:12px;cursor:pointer;transition:background .2s}.retry-btn:hover{background:#ef444440}.copy-btn{position:absolute;top:6px;right:6px;background:none;border:none;cursor:pointer;font-size:12px;padding:2px 4px;border-radius:4px;opacity:0;transition:opacity .2s}.message:hover .copy-btn{opacity:.7}.copy-btn:hover{opacity:1!important;background:var(--bg-highlight)}.ai-send.stop{background:#ef4444}.message-content code{background:var(--bg-primary);padding:1px 4px;border-radius:3px;font-family:var(--font-mono);font-size:12px}.message-content strong{font-weight:700}@keyframes shimmer{0%{background-position:-200px 0}to{background-position:200px 0}}.thinking-skeleton{display:flex;flex-direction:column;gap:8px;padding:14px}.skeleton-line{height:12px;border-radius:6px;background:linear-gradient(90deg,var(--bg-tertiary) 25%,var(--bg-highlight) 50%,var(--bg-tertiary) 75%);background-size:400px 100%;animation:shimmer 1.5s infinite linear}:root{--color-brand-blue: #3b82f6;--color-brand-orange: #E75213;--color-success: #10b981;--color-warning: #f59e0b;--color-error: #ef4444;--color-info: #3b82f6;--font-sans: "Inter", system-ui, -apple-system, sans-serif;--font-mono: "JetBrains Mono", "Fira Code", monospace;--radius-sm: 4px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--z-dropdown: 1000;--z-sticky: 1100;--z-modal: 1300;--z-popover: 1500}[data-mode=dark]{--bg-primary: #0f172a;--bg-secondary: #1e293b;--bg-tertiary: #334155;--bg-highlight: rgba(255, 255, 255, .05);--glass-surface: rgba(30, 41, 59, .7);--glass-border: rgba(255, 255, 255, .1);--text-primary: #f8fafc;--text-secondary: #b0bec5;--text-muted: #8896a3;--text-inverse: #0f172a;--border-primary: rgba(255, 255, 255, .1);--border-secondary: rgba(255, 255, 255, .2)}[data-mode=light]{--bg-primary: #f8fafc;--bg-secondary: #ffffff;--bg-tertiary: #f1f5f9;--bg-highlight: rgba(0, 0, 0, .05);--glass-surface: rgba(255, 255, 255, .8);--glass-border: rgba(0, 0, 0, .1);--text-primary: #0f172a;--text-secondary: #64748b;--text-muted: #94a3b8;--text-inverse: #ffffff;--border-primary: rgba(0, 0, 0, .1);--border-secondary: rgba(0, 0, 0, .2)}[data-brand=blue]{--color-accent: #3b82f6;--color-accent-hover: #60a5fa;--color-accent-text: #93c5fd;--color-accent-bg: rgba(59, 130, 246, .15)}[data-brand=blue][data-mode=light]{--color-accent: #2563eb;--color-accent-hover: #1d4ed8;--color-accent-text: #1e40af}[data-brand=orange]{--color-accent: #E75213;--color-accent-hover: #fb923c;--color-accent-text: #fdba74;--color-accent-bg: rgba(231, 82, 19, .15)}[data-brand=orange][data-mode=light]{--color-accent: #ea580c;--color-accent-hover: #c2410c;--color-accent-text: #9a3412}.glass-panel{background:var(--glass-surface);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--glass-border)}@keyframes toastSlideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes popIn{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.wsp-input:hover:not(:disabled){border-color:var(--border-secondary)!important}.wsp-input:focus-visible{border-color:var(--color-accent)!important;outline:none;box-shadow:0 0 0 2px var(--color-accent-bg)}.wsp-tab:hover{color:var(--text-primary)!important}.wsp-close:hover{opacity:1!important}.wsp-footer-btn:hover{filter:brightness(1.1)}.detail-panel-container{transition:transform .4s cubic-bezier(.4,0,.2,1),opacity .4s ease;transform:translate(100%);opacity:0}.panel-enter-active{transform:translate(0);opacity:1}.panel-exit{transform:translate(100%);opacity:0}:root{--status-scheduled: #64748B;--status-in-progress: #3B82F6;--status-completed: #0aad76;--status-completed-with-errors: #FBBF24;--status-failed: #EF4444;--status-failed-terminal: #991B1B;--status-timed-out: #F97316;--status-skipped: #A1A1AA;--status-canceled: #6B7280}@keyframes status-pulse{0%,to{box-shadow:0 0 #3b82f666}50%{box-shadow:0 0 0 8px #3b82f600}}@keyframes status-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes status-flash-success{0%{box-shadow:0 0 #10b98199}50%{box-shadow:0 0 20px 4px #047d55cc}to{box-shadow:0 0 #10b98100}}@keyframes status-shake{0%,to{transform:translate(0)}20%,60%{transform:translate(-3px)}40%,80%{transform:translate(3px)}}.execution-node-scheduled{border-color:var(--status-scheduled)!important;background:var(--status-scheduled)!important}.execution-node-in-progress{border-color:var(--status-in-progress)!important;background:var(--status-in-progress)!important;animation:status-pulse 1.5s ease-in-out infinite}.execution-node-completed{border-color:var(--status-completed)!important;background:var(--status-completed)!important}.execution-node-completed-with-errors{border-color:var(--status-completed-with-errors)!important;background:var(--status-completed-with-errors)!important}.execution-node-failed{border-color:var(--status-failed)!important;background:var(--status-failed)!important}.execution-node-failed-terminal{border-color:var(--status-failed-terminal)!important;background:var(--status-failed-terminal)!important}.execution-node-timed-out{border-color:var(--status-timed-out)!important;background:var(--status-timed-out)!important}.execution-node-skipped{border-color:var(--status-skipped)!important;background:var(--status-skipped)!important;opacity:1}.execution-node-canceled{border-color:var(--status-canceled)!important;background:var(--status-canceled)!important;opacity:1}.execution-status-badge{position:absolute;top:-8px;right:-8px;width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:#fff;border:2px solid var(--bg-secondary);z-index:10;box-shadow:0 2px 4px #0003}.execution-status-badge.in-progress{background:var(--status-in-progress)}.execution-status-badge.in-progress .badge-icon{animation:status-spin 1.5s linear infinite}.execution-status-badge.completed{background:var(--status-completed)}.execution-status-badge.failed{background:var(--status-failed)}.execution-status-bar{background:var(--glass-surface, rgba(15, 23, 42, .8));-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border:1px solid var(--glass-border, rgba(255, 255, 255, .15));border-radius:12px;padding:12px 24px;display:flex;flex-direction:row;flex-wrap:nowrap;gap:32px;align-items:center;font-size:14px;color:var(--text-primary);box-shadow:0 12px 40px #0006;pointer-events:auto;width:max-content}.execution-status-bar .status-item{display:flex;align-items:center;gap:10px;white-space:nowrap}.execution-status-bar .status-label{color:var(--text-secondary);font-weight:500}.execution-status-bar .status-value{font-weight:700}.execution-status-bar .status-running{color:var(--status-in-progress)}.execution-status-bar .status-completed{color:var(--status-completed)}.execution-status-bar .status-failed{color:var(--status-failed)}.execution-status-bar-container{margin-bottom:20px}[data-mode=light] .execution-status-bar{background:#ffffffe6;border-color:#0000001a;box-shadow:0 4px 16px #0000001a}
1
+ .react-flow{direction:ltr}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;cursor:-webkit-grab;cursor:grab}.react-flow__pane.selection{cursor:pointer}.react-flow__pane.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow .react-flow__edges{pointer-events:none;overflow:visible}.react-flow__edge-path,.react-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.react-flow__edge{pointer-events:visibleStroke;cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;-webkit-animation:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge:focus .react-flow__edge-path,.react-flow__edge:focus-visible .react-flow__edge-path{stroke:#555}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge-textbg{fill:#fff}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__connectionline{z-index:1001}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:-webkit-grab;cursor:grab}.react-flow__node.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:-webkit-grab;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background:#1a192b;border:1px solid white;border-radius:100%}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:-4px;transform:translate(-50%)}.react-flow__handle-top{left:50%;top:-4px;transform:translate(-50%)}.react-flow__handle-left{top:50%;left:-4px;transform:translateY(-50%)}.react-flow__handle-right{right:-4px;top:50%;transform:translateY(-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:#ffffff80;padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@-webkit-keyframes dashdraw{0%{stroke-dashoffset:10}}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-default,.react-flow__node-input,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:3px;width:150px;font-size:12px;color:#222;text-align:center;border-width:1px;border-style:solid;border-color:#1a192b;background-color:#fff}.react-flow__node-default.selectable:hover,.react-flow__node-input.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:0 0 0 .5px #1a192b}.react-flow__node-group{background-color:#f0f0f040}.react-flow__nodesselection-rect,.react-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls{box-shadow:0 0 2px 1px #00000014}.react-flow__controls-button{border:none;background:#fefefe;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:5px}.react-flow__controls-button:hover{background:#f4f4f4}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__minimap{background-color:#fff}.react-flow__minimap svg{display:block}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:#3367d9;transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:#3367d9;border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.empty-state-panel{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10;display:flex;flex-direction:column;align-items:center;gap:24px;padding:48px 40px;border-radius:var(--radius-xl);background:var(--glass-surface);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--glass-border);min-width:360px;max-width:420px}.empty-state-title{font-size:20px;font-weight:600;color:var(--text-primary);margin:0;text-align:center}.empty-state-subtitle{font-size:13px;color:var(--text-secondary);margin:0;text-align:center;line-height:1.5}.empty-state-name-input{width:100%;padding:10px 14px;border-radius:var(--radius-md);border:1px solid var(--border-primary);background:var(--bg-primary);color:var(--text-primary);font-size:14px;font-family:var(--font-sans);outline:none;transition:border-color .2s}.empty-state-name-input:focus{border-color:var(--color-accent)}.empty-state-name-input::placeholder{color:var(--text-tertiary)}.empty-state-actions{display:flex;flex-direction:column;gap:12px;width:100%}.empty-state-btn{display:flex;align-items:center;justify-content:center;gap:8px;padding:12px 20px;border-radius:var(--radius-md);border:1px solid var(--border-primary);background:var(--bg-tertiary);color:var(--text-primary);font-size:14px;font-weight:500;font-family:var(--font-sans);cursor:pointer;transition:background .2s,border-color .2s;width:100%}.empty-state-btn:hover{background:var(--bg-highlight);border-color:var(--text-tertiary)}.empty-state-btn.primary{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}.empty-state-btn.primary:hover{opacity:.9}.empty-state-btn-icon{font-size:16px}.parameter-suggester-popover{padding:8px;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006;animation:popIn .2s cubic-bezier(.175,.885,.32,1.275)}@keyframes popIn{0%{transform:scale(.95);opacity:0}to{transform:scale(1);opacity:1}}.suggester-overlay{position:fixed;inset:0;z-index:2999}.suggester-header{padding:8px 12px;font-size:11px;font-weight:700;color:var(--color-accent-text);text-transform:uppercase;letter-spacing:1px;display:flex;align-items:center;gap:6px;border-bottom:1px solid var(--border-primary);margin-bottom:4px}.suggester-list{max-height:250px;overflow-y:auto}.suggestion-item{padding:10px 12px;border-radius:6px;cursor:pointer;transition:background .2s}.suggestion-item:hover{background:var(--bg-highlight)}.suggestion-label{font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:2px}.suggestion-value{font-size:11px;font-family:var(--font-mono);color:var(--color-accent);margin-bottom:4px}.suggestion-desc{font-size:11px;color:var(--text-secondary);line-height:1.4}.suggester-empty{padding:20px;text-align:center;font-size:12px;color:var(--text-muted)}@keyframes suggesterShimmer{0%{background-position:-200px 0}to{background-position:200px 0}}.suggester-loading{padding:16px 12px;display:flex;flex-direction:column;gap:10px}.suggester-skeleton-line{height:14px;border-radius:6px;background:linear-gradient(90deg,var(--bg-tertiary) 25%,var(--bg-highlight) 50%,var(--bg-tertiary) 75%);background-size:400px 100%;animation:suggesterShimmer 1.5s infinite linear}.ai-chat-panel{position:fixed;bottom:20px;right:20px;width:480px;height:70vh;max-height:calc(85vh - 40px);background:var(--glass-surface);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--glass-border);border-radius:16px;display:flex;flex-direction:column;box-shadow:0 12px 40px #0006;z-index:2000;overflow:hidden;transition:all .3s cubic-bezier(.16,1,.3,1);animation:slideUpIn .4s cubic-bezier(.16,1,.3,1)}@keyframes slideUpIn{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.ai-chat-panel.collapsed{height:48px;width:200px;cursor:pointer}.ai-header{padding:12px 16px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border-primary);background:#ffffff08}.ai-title{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:600;color:var(--color-accent-text)}.ai-sparkles{font-size:16px;animation:sparkleRotate 4s infinite linear}@keyframes sparkleRotate{0%{transform:rotate(0) scale(1)}50%{transform:rotate(180deg) scale(1.2)}to{transform:rotate(360deg) scale(1)}}.ai-close{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:18px;padding:4px;display:flex;align-items:center;border-radius:4px}.ai-close:hover{background:var(--bg-highlight);color:var(--text-primary)}.ai-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:16px}.message{padding:10px 14px;border-radius:12px;max-width:95%;font-size:13px;line-height:1.5;position:relative}.message.user{align-self:flex-end;background:var(--bg-tertiary);color:var(--text-primary);border-bottom-right-radius:2px}.message.ai{align-self:flex-start;background:#3b82f61a;border:1px solid rgba(59,130,246,.2);color:var(--text-primary);border-bottom-left-radius:2px}.message pre{background:var(--bg-primary);padding:8px;border-radius:6px;font-family:var(--font-mono);font-size:12px;margin:8px 0;overflow-x:auto;max-height:400px;overflow-y:auto}.ai-input-area{padding:16px;border-top:1px solid var(--border-primary);background:#0000001a}.ai-input-container{display:flex;gap:8px;background:var(--bg-secondary);border:1px solid var(--border-primary);border-radius:8px;padding:8px;transition:border-color .2s}.ai-input-container:focus-within{border-color:var(--color-accent);box-shadow:0 0 0 2px var(--color-accent-bg)}.ai-input{flex:1;background:none;border:none;color:var(--text-primary);font-size:13px;outline:none;resize:none;min-height:44px;max-height:120px}.ai-send{background:var(--color-accent);color:#fff;border:none;border-radius:6px;padding:4px 10px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform .1s}.ai-send:hover{filter:brightness(1.1)}.ai-send:active{transform:scale(.95)}.change-card{margin-top:12px;background:var(--bg-primary);border:1px solid var(--border-secondary);border-radius:8px;overflow:hidden}.card-header{padding:8px 12px;background:var(--bg-highlight);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--text-secondary)}.card-actions{display:flex;border-top:1px solid var(--border-primary)}.card-btn{flex:1;padding:8px;background:none;border:none;color:var(--color-accent-text);font-size:12px;font-weight:500;cursor:pointer;transition:background .2s}.card-btn:hover{background:var(--bg-highlight)}.card-btn.apply{color:var(--color-success);border-left:1px solid var(--border-primary)}@keyframes blink{0%,50%{opacity:1}51%,to{opacity:0}}.streaming-cursor{animation:blink 1s infinite}.message.ai.error{border-color:#ef444466;background:#ef444414}.retry-btn{margin-top:8px;padding:4px 12px;background:#ef444426;color:#f87171;border:1px solid rgba(239,68,68,.3);border-radius:6px;font-size:12px;cursor:pointer;transition:background .2s}.retry-btn:hover{background:#ef444440}.copy-btn{position:absolute;top:6px;right:6px;background:none;border:none;cursor:pointer;font-size:12px;padding:2px 4px;border-radius:4px;opacity:0;transition:opacity .2s}.message:hover .copy-btn{opacity:.7}.copy-btn:hover{opacity:1!important;background:var(--bg-highlight)}.ai-send.stop{background:#ef4444}.message-content code{background:var(--bg-primary);padding:1px 4px;border-radius:3px;font-family:var(--font-mono);font-size:12px}.message-content strong{font-weight:700}@keyframes shimmer{0%{background-position:-200px 0}to{background-position:200px 0}}.thinking-skeleton{display:flex;flex-direction:column;gap:8px;padding:14px}.skeleton-line{height:12px;border-radius:6px;background:linear-gradient(90deg,var(--bg-tertiary) 25%,var(--bg-highlight) 50%,var(--bg-tertiary) 75%);background-size:400px 100%;animation:shimmer 1.5s infinite linear}.diff-card{margin-top:8px;border:1px solid var(--border-strong);border-radius:8px;overflow:hidden;background:var(--bg-primary);font-size:12px}.diff-head{display:flex;align-items:center;gap:8px;padding:8px 10px;background:var(--bg-secondary);border-bottom:1px solid var(--border);color:var(--text-secondary);font-weight:500}.diff-badge{font-family:var(--font-mono, monospace);font-size:10px;text-transform:uppercase;padding:1px 6px;border-radius:3px;letter-spacing:.04em;flex-shrink:0}.diff-badge.add{background:color-mix(in srgb,var(--status-completed) 15%,transparent);color:var(--status-completed)}.diff-badge.mod{background:color-mix(in srgb,#fbbf24 15%,transparent);color:#fbbf24}.diff-badge.del{background:color-mix(in srgb,var(--status-failed) 15%,transparent);color:var(--status-failed)}.diff-summary{flex:1;font-size:11.5px;color:var(--text-secondary)}.diff-list{display:flex;flex-direction:column}.diff-row{display:flex;align-items:flex-start;gap:10px;padding:7px 10px;font-size:12px;border-top:1px solid var(--border)}.diff-row:first-child{border-top:0}.diff-marker{width:14px;height:14px;display:grid;place-items:center;font-family:monospace;font-size:11px;font-weight:700;border-radius:3px;flex-shrink:0;margin-top:1px}.diff-row.add .diff-marker{background:color-mix(in srgb,var(--status-completed) 15%,transparent);color:var(--status-completed)}.diff-row.mod .diff-marker{background:color-mix(in srgb,#fbbf24 15%,transparent);color:#fbbf24}.diff-row.del .diff-marker{background:color-mix(in srgb,var(--status-failed) 15%,transparent);color:var(--status-failed)}.diff-desc{flex:1;color:var(--text-primary);line-height:1.4}.diff-actions{display:flex;gap:6px;padding:8px 10px;border-top:1px solid var(--border);background:var(--bg-secondary)}.diff-btn{height:26px;padding:0 10px;border-radius:5px;font-size:11.5px;font-weight:500;display:inline-flex;align-items:center;gap:4px;border:1px solid var(--border-strong);background:transparent;color:var(--text-secondary);cursor:pointer;transition:background .15s,border-color .15s,color .15s;font-family:inherit}.diff-btn:hover:not(:disabled){background:var(--bg-tertiary);border-color:var(--border-strong);color:var(--text-primary)}.diff-btn.apply{background:var(--text-primary);color:var(--bg-primary);border-color:var(--text-primary)}.diff-btn.apply:hover{background:#fff}.diff-btn.applied{background:color-mix(in srgb,var(--status-completed) 15%,transparent);color:var(--status-completed);border-color:color-mix(in srgb,var(--status-completed) 30%,transparent);cursor:default}.diff-btn.undo:hover:not(:disabled){color:var(--status-failed);border-color:color-mix(in srgb,var(--status-failed) 30%,transparent)}:root{--color-brand-blue: #3b82f6;--color-brand-orange: #E75213;--color-success: #10b981;--color-warning: #f59e0b;--color-error: #ef4444;--color-info: #3b82f6;--font-sans: "Inter", system-ui, -apple-system, sans-serif;--font-mono: "JetBrains Mono", "Fira Code", monospace;--radius-sm: 4px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--z-dropdown: 1000;--z-sticky: 1100;--z-modal: 1300;--z-popover: 1500}[data-mode=dark]{--bg-primary: #0f172a;--bg-secondary: #1e293b;--bg-tertiary: #334155;--bg-highlight: rgba(255, 255, 255, .05);--glass-surface: rgba(30, 41, 59, .7);--glass-border: rgba(255, 255, 255, .18);--text-primary: #f8fafc;--text-secondary: #b0bec5;--text-muted: #8896a3;--text-inverse: #0f172a;--border-primary: rgba(255, 255, 255, .18);--border-secondary: rgba(255, 255, 255, .32);--node-shadow: 0 4px 20px rgba(0, 0, 0, .55), 0 1px 6px rgba(0, 0, 0, .35);--node-shadow-hover: 0 8px 30px rgba(0, 0, 0, .65), 0 2px 8px rgba(0, 0, 0, .4)}[data-mode=light]{--bg-primary: #f8fafc;--bg-secondary: #ffffff;--bg-tertiary: #f1f5f9;--bg-highlight: rgba(0, 0, 0, .05);--glass-surface: rgba(255, 255, 255, .8);--glass-border: rgba(0, 0, 0, .15);--text-primary: #0f172a;--text-secondary: #64748b;--text-muted: #94a3b8;--text-inverse: #ffffff;--border-primary: rgba(0, 0, 0, .15);--border-secondary: rgba(0, 0, 0, .28);--node-shadow: 0 4px 16px rgba(0, 0, 0, .18), 0 1px 4px rgba(0, 0, 0, .12);--node-shadow-hover: 0 8px 24px rgba(0, 0, 0, .22), 0 2px 6px rgba(0, 0, 0, .14)}[data-brand=blue]{--color-accent: #3b82f6;--color-accent-hover: #60a5fa;--color-accent-text: #93c5fd;--color-accent-bg: rgba(59, 130, 246, .15)}[data-brand=blue][data-mode=light]{--color-accent: #2563eb;--color-accent-hover: #1d4ed8;--color-accent-text: #1e40af}[data-brand=orange]{--color-accent: #E75213;--color-accent-hover: #fb923c;--color-accent-text: #fdba74;--color-accent-bg: rgba(231, 82, 19, .15)}[data-brand=orange][data-mode=light]{--color-accent: #ea580c;--color-accent-hover: #c2410c;--color-accent-text: #9a3412}.glass-panel{background:var(--glass-surface);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--glass-border)}@keyframes toastSlideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes popIn{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.wsp-input:hover:not(:disabled){border-color:var(--border-secondary)!important}.wsp-input:focus-visible{border-color:var(--color-accent)!important;outline:none;box-shadow:0 0 0 2px var(--color-accent-bg)}.wsp-tab:hover{color:var(--text-primary)!important}.wsp-close:hover{opacity:1!important}.wsp-footer-btn:hover{filter:brightness(1.1)}.detail-panel-container{transition:transform .4s cubic-bezier(.4,0,.2,1),opacity .4s ease;transform:translate(100%);opacity:0}.panel-enter-active{transform:translate(0);opacity:1}.panel-exit{transform:translate(100%);opacity:0}:root{--status-scheduled: #64748B;--status-in-progress: #3B82F6;--status-completed: #0aad76;--status-completed-with-errors: #FBBF24;--status-failed: #EF4444;--status-failed-terminal: #991B1B;--status-timed-out: #F97316;--status-skipped: #A1A1AA;--status-canceled: #6B7280}@keyframes status-pulse{0%,to{box-shadow:0 0 #3b82f666}50%{box-shadow:0 0 0 8px #3b82f600}}@keyframes status-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes status-flash-success{0%{box-shadow:0 0 #10b98199}50%{box-shadow:0 0 20px 4px #047d55cc}to{box-shadow:0 0 #10b98100}}@keyframes status-shake{0%,to{transform:translate(0)}20%,60%{transform:translate(-3px)}40%,80%{transform:translate(3px)}}.node-sim-running{border-color:var(--status-in-progress)!important;animation:status-pulse 1.5s ease-in-out infinite!important}.node-sim-done{border-color:var(--status-completed)!important;box-shadow:0 0 12px #0aad7680!important}.execution-node-scheduled{border-color:var(--status-scheduled)!important;background:var(--status-scheduled)!important}.execution-node-in-progress{border-color:var(--status-in-progress)!important;background:var(--status-in-progress)!important;animation:status-pulse 1.5s ease-in-out infinite}.execution-node-completed{border-color:var(--status-completed)!important;background:var(--status-completed)!important}.execution-node-completed-with-errors{border-color:var(--status-completed-with-errors)!important;background:var(--status-completed-with-errors)!important}.execution-node-failed{border-color:var(--status-failed)!important;background:var(--status-failed)!important}.execution-node-failed-terminal{border-color:var(--status-failed-terminal)!important;background:var(--status-failed-terminal)!important}.execution-node-timed-out{border-color:var(--status-timed-out)!important;background:var(--status-timed-out)!important}.execution-node-skipped{border-color:var(--status-skipped)!important;background:var(--status-skipped)!important;opacity:1}.execution-node-canceled{border-color:var(--status-canceled)!important;background:var(--status-canceled)!important;opacity:1}.execution-status-badge{position:absolute;top:-8px;right:-8px;width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:#fff;border:2px solid var(--bg-secondary);z-index:10;box-shadow:0 2px 4px #0003}.execution-status-badge.in-progress{background:var(--status-in-progress)}.execution-status-badge.in-progress .badge-icon{animation:status-spin 1.5s linear infinite}.execution-status-badge.completed{background:var(--status-completed)}.execution-status-badge.failed{background:var(--status-failed)}.execution-status-bar{background:var(--glass-surface, rgba(15, 23, 42, .8));-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border:1px solid var(--glass-border, rgba(255, 255, 255, .15));border-radius:12px;padding:12px 24px;display:flex;flex-direction:row;flex-wrap:nowrap;gap:32px;align-items:center;font-size:14px;color:var(--text-primary);box-shadow:0 12px 40px #0006;pointer-events:auto;width:max-content}.execution-status-bar .status-item{display:flex;align-items:center;gap:10px;white-space:nowrap}.execution-status-bar .status-label{color:var(--text-secondary);font-weight:500}.execution-status-bar .status-value{font-weight:700}.execution-status-bar .status-running{color:var(--status-in-progress)}.execution-status-bar .status-completed{color:var(--status-completed)}.execution-status-bar .status-failed{color:var(--status-failed)}.execution-status-bar-container{margin-bottom:20px}[data-mode=light] .execution-status-bar{background:#ffffffe6;border-color:#0000001a;box-shadow:0 4px 16px #0000001a}