ソースを参照

编写条件判断

lj1559651600@163.com 3 週間 前
コミット
bddc925968

+ 40 - 49
.idea/workspace.xml

@@ -5,44 +5,22 @@
   </component>
   <component name="ChangeListManager">
     <list default="true" id="41cd2400-3735-4e1a-9a77-7ea2e4a1c041" name="更改" comment="合并冲突">
-      <change afterPath="$PROJECT_DIR$/apps/web/src/assets/icons/sparkle.svg" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/components/Chart/ExecutionChart.vue" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/composables/useI18n.ts" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/features/editorFooter/index.vue" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/features/selectTableModal/index.vue" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/i18n/index.ts" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/i18n/locales/zh-cn.ts" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/store/modules/chat.store.ts" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/stores/dashboard.ts" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/views/Chat.vue" afterDir="false" />
-      <change afterPath="$PROJECT_DIR$/apps/web/src/views/Statistics.vue" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/condition/BranchCard.vue" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/condition/ConditionBuilder.vue" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/condition/VariablePicker.vue" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/apps/web/components.d.ts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/components.d.ts" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/apps/web/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/package.json" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/apps/web/src/components/SearchDialog/index.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/SearchDialog/index.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/apps/web/src/components/Sidebar/index.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/Sidebar/index.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/apps/web/src/components/setter/DatabaseSetter.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/setter/DatabaseSetter.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/apps/web/src/components/setter/index.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/setter/index.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/apps/web/src/layouts/MainLayout.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/layouts/MainLayout.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/CodeEditor.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/CodeEditor.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/InputVariables.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/InputVariables.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/OutputVariables.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/OutputVariables.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/TabGroup.vue" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/TestConfig.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/SetterCommon/Code/TestConfig.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/apps/web/src/components/setter/CodeSetter.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/setter/CodeSetter.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/apps/web/src/components/setter/ConditionSetter.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/components/setter/ConditionSetter.vue" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/apps/web/src/main.ts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/main.ts" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/apps/web/src/router/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/router/index.ts" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/apps/web/src/views/Dashboard.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/views/Dashboard.vue" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/apps/web/src/views/Editor.vue" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/views/Editor.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/nodes/materials/start.ts" beforeDir="false" afterPath="$PROJECT_DIR$/packages/nodes/materials/start.ts" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/nodes/materials/toolbar.ts" beforeDir="false" afterPath="$PROJECT_DIR$/packages/nodes/materials/toolbar.ts" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/ui/components/icon-button/IconButton.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/ui/components/icon-button/IconButton.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/ui/components/sticky-note/StickyNote.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/ui/components/sticky-note/StickyNote.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/Interface.ts" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/Interface.ts" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/Canvas.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/Canvas.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/CanvasControlBar.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/CanvasControlBar.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/CodeNode.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/CodeNode.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/ConditionNode.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/ConditionNode.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/DataBaseNode.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/DataBaseNode.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/EndNode.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/EndNode.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/HttpNode1.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/HttpNode1.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/StartNode.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/node-temp/StartNode.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/nodes/CanvasNode.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/nodes/CanvasNode.vue" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/packages/workflow/src/components/elements/nodes/render-types/NodeStickyNote.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packages/workflow/src/components/elements/nodes/render-types/NodeStickyNote.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/apps/web/vite.config.ts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/vite.config.ts" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/pnpm-lock.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pnpm-lock.yaml" afterDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
@@ -61,6 +39,9 @@
     </option>
     <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
   </component>
+  <component name="ProblemsViewState">
+    <option name="selectedTabId" value="CurrentFile" />
+  </component>
   <component name="ProjectColorInfo">{
   &quot;associatedIndex&quot;: 7
 }</component>
@@ -72,21 +53,21 @@
     <option name="hideEmptyMiddlePackages" value="true" />
     <option name="showLibraryContents" value="true" />
   </component>
-  <component name="PropertiesComponent"><![CDATA[{
-  "keyToString": {
-    "RunOnceActivity.ShowReadmeOnStart": "true",
-    "git-widget-placeholder": "正在合并 feature-0123-Ai-workFlow",
-    "last_opened_file_path": "/Users/liujie/Desktop/shalu/shalu-agent-workflow",
-    "node.js.detected.package.eslint": "true",
-    "node.js.detected.package.tslint": "true",
-    "node.js.selected.package.eslint": "(autodetect)",
-    "node.js.selected.package.tslint": "(autodetect)",
-    "nodejs_package_manager_path": "pnpm",
-    "settings.editor.selected.configurable": "preferences.keymap",
-    "ts.external.directory.path": "/Users/liujie/Desktop/shalu/shalu-agent-workflow/node_modules/typescript/lib",
-    "vue.rearranger.settings.migration": "true"
+  <component name="PropertiesComponent">{
+  &quot;keyToString&quot;: {
+    &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
+    &quot;git-widget-placeholder&quot;: &quot;feature-0123-Ai-workFlow&quot;,
+    &quot;last_opened_file_path&quot;: &quot;/Users/liujie/Desktop/shalu/shalu-agent-workflow&quot;,
+    &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
+    &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
+    &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
+    &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
+    &quot;nodejs_package_manager_path&quot;: &quot;pnpm&quot;,
+    &quot;settings.editor.selected.configurable&quot;: &quot;preferences.keymap&quot;,
+    &quot;ts.external.directory.path&quot;: &quot;/Users/liujie/Desktop/shalu/shalu-agent-workflow/node_modules/typescript/lib&quot;,
+    &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
   }
-}]]></component>
+}</component>
   <component name="RecentsManager">
     <key name="MoveFile.RECENT_KEYS">
       <recent name="$PROJECT_DIR$/apps/web/src/components/SetterCommonComponents/Code" />
@@ -113,7 +94,9 @@
       <updated>1769391572649</updated>
       <workItem from="1769391573741" duration="223000" />
       <workItem from="1769391800519" duration="10090000" />
-      <workItem from="1769518660683" duration="3027000" />
+      <workItem from="1769518660683" duration="5115000" />
+      <workItem from="1769525730430" duration="3654000" />
+      <workItem from="1769529933165" duration="6257000" />
     </task>
     <task id="LOCAL-00001" summary="合并冲突">
       <option name="closed" value="true" />
@@ -147,7 +130,15 @@
       <option name="project" value="LOCAL" />
       <updated>1769393225277</updated>
     </task>
-    <option name="localTasksCounter" value="5" />
+    <task id="LOCAL-00005" summary="合并冲突">
+      <option name="closed" value="true" />
+      <created>1769523432476</created>
+      <option name="number" value="00005" />
+      <option name="presentableId" value="LOCAL-00005" />
+      <option name="project" value="LOCAL" />
+      <updated>1769523432476</updated>
+    </task>
+    <option name="localTasksCounter" value="6" />
     <servers />
   </component>
   <component name="TypeScriptGeneratedFilesManager">

+ 3 - 0
apps/web/components.d.ts

@@ -11,8 +11,10 @@ export {}
 /* prettier-ignore */
 declare module 'vue' {
   export interface GlobalComponents {
+    BranchCard: typeof import('./src/components/SetterCommon/condition/BranchCard.vue')['default']
     CodeEditor: typeof import('./src/components/SetterCommon/Code/CodeEditor.vue')['default']
     CodeSetter: typeof import('./src/components/setter/CodeSetter.vue')['default']
+    ConditionBuilder: typeof import('./src/components/SetterCommon/condition/ConditionBuilder.vue')['default']
     ConditionSetter: typeof import('./src/components/setter/ConditionSetter.vue')['default']
     CustomDropdown: typeof import('./src/components/CustomDropdown/index.vue')['default']
     DatabaseSetter: typeof import('./src/components/setter/DatabaseSetter.vue')['default']
@@ -76,6 +78,7 @@ declare module 'vue' {
     SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
     TabGroup: typeof import('./src/components/SetterCommon/Code/TabGroup.vue')['default']
     TestConfig: typeof import('./src/components/SetterCommon/Code/TestConfig.vue')['default']
+    VariablePicker: typeof import('./src/components/SetterCommon/condition/VariablePicker.vue')['default']
   }
   export interface GlobalDirectives {
     vLoading: typeof import('element-plus/es')['ElLoadingDirective']

+ 3 - 0
apps/web/package.json

@@ -13,6 +13,7 @@
     "@repo/nodes": "workspace:^",
     "echarts": "^6.0.0",
     "element-plus": "^2.13.1",
+    "monaco-editor": "^0.55.1",
     "normalize.css": "^8.0.1",
     "pinia": "^3.0.4",
     "uuid": "^13.0.0",
@@ -26,6 +27,7 @@
     "@repo/workflow": "workspace:*",
     "@vitejs/plugin-vue": "^6.0.1",
     "@vue/tsconfig": "^0.8.1",
+    "esbuild": "^0.27.2",
     "less": "^4.5.1",
     "typescript": "~5.9.3",
     "unocss": "^66.6.0",
@@ -33,6 +35,7 @@
     "unplugin-icons": "^23.0.1",
     "unplugin-vue-components": "^31.0.0",
     "vite": "npm:rolldown-vite@7.2.5",
+    "vite-plugin-monaco-editor": "^1.1.0",
     "vite-plugin-svg-icons": "^2.0.1",
     "vue-tsc": "^3.1.4"
   },

+ 60 - 117
apps/web/src/components/SetterCommon/Code/CodeEditor.vue

@@ -1,126 +1,69 @@
 <template>
-    <div class="space-y-2">
-        <div class="flex items-center justify-between">
-            <div class="flex items-center gap-2">
-                <select v-model="selectedLanguage" @change="handleLanguageChange"
-                    class="px-3 py-1.5 text-sm font-medium border border-gray-200 rounded-lg focus:outline-none bg-white">
-                    <option value="python">Python</option>
-                    <option value="javascript">JavaScript</option>
-                    <option value="typescript">TypeScript</option>
-                </select>
-            </div>
-
-            <div class="flex items-center gap-1">
-                <button @click="formatCode" class="p-1.5 hover:bg-gray-100 rounded transition-colors" title="格式化代码">
-                    <Icon icon="lucide:align-left" :height="16" :width="16" class="text-gray-600" />
-                </button>
-                <button @click="copyCode" class="p-1.5 hover:bg-gray-100 rounded transition-colors" title="复制代码">
-                    <Icon icon="lucide:copy" :height="16" :width="16" class="text-gray-600" />
-                </button>
-            </div>
-        </div>
-
-        <div class="relative">
-            <div
-                class="absolute left-0 top-0 bottom-[8px] w-12 bg-gray-200 border-r border-gray-200 flex flex-col text-xs text-gray-500 select-none rounded-l-lg overflow-hidden">
-                <div v-for="line in lineNumbers" :key="line" class="h-6 flex items-center justify-end px-2">
-                    {{ line }}
-                </div>
-            </div>
-            <textarea v-model="code" @input="handleCodeChange"
-                class="w-[80%] h-64 pl-12 font-mono text-sm border border-gray-100 rounded-lg focus:outline-none resize-none bg-gray-200"
-                :placeholder="placeholder" spellcheck="false"></textarea>
-        </div>
-
-        <div v-if="showCopySuccess" class="flex items-center gap-1 text-xs text-green-600">
-            <Icon icon="lucide:check" :height="14" :width="14" />
-            代码已复制到剪贴板
-        </div>
+    <div class="space-y-4">
+<!--			<div class="flex items-center justify-between text-gray-800">-->
+<!--				<span class="w-[60px] text-sm">语法</span>-->
+<!--				<ElSelect v-model="selectedLanguageVal" @change="changeLanguage">-->
+<!--					<el-option v-for="li in language" :key="li" :value="li">{{li}}</el-option>-->
+<!--				</ElSelect>-->
+<!--			</div>-->
+			<div ref="editorContainer" style="height: 240px;"></div>
     </div>
 </template>
 
 <script setup lang="ts">
-import { ref, computed, watch } from 'vue'
-import { Icon } from '@iconify/vue'
-
-interface Props {
-    modelValue: string
-    language?: string
-}
-
-interface Emits {
-    (e: 'update:modelValue', value: string): void
-    (e: 'update:language', value: string): void
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    language: 'python'
-})
-
-const emit = defineEmits<Emits>()
-
-const code = ref(props.modelValue || '')
-const selectedLanguage = ref(props.language)
-const showCopySuccess = ref(false)
-
-const placeholder = computed(() => {
-    const placeholders: Record<string, string> = {
-        python: 'def main(arg1: str, arg2: str):\n    return {\n        "result": arg1 + arg2,\n    }',
-        javascript: 'function main(arg1, arg2) {\n    return {\n        result: arg1 + arg2\n    };\n}',
-        typescript: 'function main(arg1: string, arg2: string): object {\n    return {\n        result: arg1 + arg2\n    };\n}'
-    }
-    return placeholders[selectedLanguage.value] || placeholders.python
-})
-
-const lineNumbers = computed(() => {
-    const lines = code.value.split('\n').length
-    return Array.from({ length: Math.max(lines, 10) }, (_, i) => i + 1)
-})
-
-watch(
-    () => props.modelValue,
-    (newVal) => {
-        code.value = newVal || ''
-    }
-)
-
-watch(
-    () => props.language,
-    (newVal) => {
-        selectedLanguage.value = newVal
-    }
-)
-
-const handleCodeChange = () => {
-    emit('update:modelValue', code.value)
-}
-
-const handleLanguageChange = () => {
-    emit('update:language', selectedLanguage.value)
-}
 
-const formatCode = () => {
-    // 简单的代码格式化逻辑
-    const lines = code.value.split('\n')
-    const formatted = lines.map(line => line.trim()).join('\n')
-    code.value = formatted
-    handleCodeChange()
-}
+import { ref, onMounted, onUnmounted } from 'vue'
+import * as monaco from 'monaco-editor'
+// const language = ref(['javascript','python']);
+// let selectedLanguageVal = ref('javascript')
+
+const editorContainer = ref<HTMLElement | null>(null)
+let editor = ref(null) as any;
+
+onMounted(() => {
+		try {
+				if(!editorContainer.value){
+					console.log('编辑器挂载节点没获取到')
+					return
+				}
+				editor.value = monaco.editor.create(editorContainer.value, {
+					value: [
+						'function x() {',
+						'    console.log("Hello world!");',
+						'}'
+					].join('\n'),
+					language: 'javascript',
+					theme: 'vs-dark',
+					automaticLayout: true,
+					fixedOverflowWidgets: true,
+				});
+				// 防止冒泡
+				editor.value.onKeyDown((e:Event) => {
+					e.stopPropagation();
+				});
+
+		}catch (err){
+				console.error("Monaco 创建失败:", err);
+		}
+
+});
+
+onUnmounted(() => {
+	if (editor.value) {
+		editor.dispose();
+	}
+});
+
+// // 动态设置模型语言
+// const changeLanguage = ()=>{
+// 		console.log('changeLanguage', selectedLanguageVal.value,editor.value );
+// 		if(editor.value){
+// 			const model = editor.value.getModel()
+// 			if (model) {
+// 				monaco.editor.setModelLanguage(model, selectedLanguageVal.value)
+// 			}
+// 		}
+// }
 
-const copyCode = async () => {
-    try {
-        await navigator.clipboard.writeText(code.value)
-        showCopySuccess.value = true
-        setTimeout(() => {
-            showCopySuccess.value = false
-        }, 2000)
-    } catch (err) {
-        console.error('Failed to copy code:', err)
-    }
-}
 
-const toggleFullscreen = () => {
-    // 全屏功能需要父组件支持
-    console.log('Toggle fullscreen')
-}
 </script>

+ 15 - 19
apps/web/src/components/SetterCommon/Code/InputVariables.vue

@@ -2,39 +2,35 @@
 	<div class="space-y-3">
 		<div class="flex items-center justify-between">
 			<label class="text-sm font-medium text-gray-700">输入变量</label>
-			<button
-				@click="addVariable"
-				class="p-1 hover:bg-gray-100 rounded transition-colors"
-				title="添加变量"
-			>
+			<ElButton
+					@click="addVariable"
+					class="p-1  rounded bg-gray-100 transition-colors"
+					title="添加变量">
 				<Icon icon="lucide:plus" :height="16" :width="16" class="text-gray-600" />
-			</button>
+			</ElButton>
 		</div>
 
 		<div class="space-y-2">
 			<div
 				v-for="(variable, index) in variables"
-				:key="variable.id"
+				:key="variable.name"
 				class="flex items-center gap-2 group"
 			>
-				<input
-					v-model="variable.name"
+				<ElInput
+					v-model="variable.id"
 					type="text"
-					:placeholder="`arg${index + 1}`"
-					class="flex-1 px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+					placeholder="变量名"
+					class="w-1/3 px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
 					@input="handleVariableChange"
 				/>
-				<span class="text-xs text-gray-400 flex items-center gap-1">
-          <Icon icon="lucide:x" :height="12" :width="12" />
-          设置变量值
-        </span>
-				<button
+				<ElInput  class="w-1/3" v-model="variable.name"  placeholder="设置变量值" />
+				<ElButton
 					@click="removeVariable(index)"
-					class="p-1 opacity-0 group-hover:opacity-100 hover:bg-red-50 rounded transition-all"
+					class="w-1/3 p-1 opacity-0 group-hover:opacity-100 hover:bg-red-50 rounded transition-all"
 					title="删除变量"
 				>
 					<Icon icon="lucide:trash-2" :height="16" :width="16" class="text-red-500" />
-				</button>
+				</ElButton>
 			</div>
 
 			<div v-if="variables.length === 0" class="text-sm text-gray-400 text-center py-4">
@@ -49,8 +45,8 @@ import { ref, watch } from 'vue'
 import { Icon } from '@iconify/vue'
 
 interface Variable {
-	id: string
 	name: string
+	id: string
 }
 
 interface Props {

+ 15 - 15
apps/web/src/components/SetterCommon/Code/OutputVariables.vue

@@ -12,28 +12,28 @@
                 输出变量
                 <span class="text-red-500">*</span>
             </label>
-            <button @click="addOutput" class="p-1 hover:bg-gray-100 rounded transition-colors" title="添加输出">
+            <ElButton @click="addOutput" class="p-1 hover:bg-gray-100 rounded transition-colors" title="添加输出">
                 <Icon icon="lucide:plus" :height="16" :width="16" class="text-gray-600" />
-            </button>
+            </ElButton>
         </div>
 
         <div class="space-y-2">
             <div v-for="(output, index) in outputs" :key="output.id" class="flex items-center gap-2 group">
-                <input v-model="output.name" type="text" placeholder="result"
-                    class="flex-1 px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+                <ElInput v-model="output.name" type="text" placeholder="result"
+                    class="w-1/3 px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                     @input="handleOutputChange" />
-                <select v-model="output.type" @change="handleOutputChange"
-                    class="px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white min-w-[100px]">
-                    <option value="String">String</option>
-                    <option value="Number">Number</option>
-                    <option value="Boolean">Boolean</option>
-                    <option value="Object">Object</option>
-                    <option value="Array">Array</option>
-                </select>
-                <button @click="removeOutput(index)"
-                    class="p-1 opacity-0 group-hover:opacity-100 hover:bg-red-50 rounded transition-all" title="删除输出">
+                <ElSelect v-model="output.type" @change="handleOutputChange"
+                    class="w-1/3 px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white min-w-[100px]">
+                    <el-option value="String">String</el-option>
+                    <el-option value="Number">Number</el-option>
+                    <el-option value="Boolean">Boolean</el-option>
+                    <el-option value="Object">Object</el-option>
+                    <el-option value="Array">Array</el-option>
+                </ElSelect>
+                <ElButton @click="removeOutput(index)"
+                    class="w-1/3 p-1 opacity-0 group-hover:opacity-100 hover:bg-red-50 rounded transition-all" title="删除输出">
                     <Icon icon="lucide:trash-2" :height="16" :width="16" class="text-red-500" />
-                </button>
+                </ElButton>
             </div>
 
             <div v-if="outputs.length === 0" class="text-sm text-gray-400 text-center py-4">

+ 0 - 46
apps/web/src/components/SetterCommon/Code/TabGroup.vue

@@ -1,46 +0,0 @@
-<template>
-	<div class="border-b border-gray-200">
-		<div class="flex gap-4">
-			<div
-				v-for="tab in tabs"
-				:key="tab.value"
-				@click="selectTab(tab.value)"
-				class="px-4 py-2 text-sm font-medium transition-colors relative"
-				:class="[
-          modelValue === tab.value
-            ? 'text-blue-600'
-            : 'text-gray-600 hover:text-gray-900'
-        ]"
-			>
-				{{ tab.label }}
-				<div
-					v-if="modelValue === tab.value"
-					class="absolute bottom-0 left-0 right-0 h-0.5 bg-blue-600"
-				></div>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script setup lang="ts">
-interface Tab {
-	label: string
-	value: string
-}
-
-interface Props {
-	modelValue: string
-	tabs: Tab[]
-}
-
-interface Emits {
-	(e: 'update:modelValue', value: string): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
-
-const selectTab = (value: string) => {
-	emit('update:modelValue', value)
-}
-</script>

+ 8 - 15
apps/web/src/components/SetterCommon/Code/TestConfig.vue

@@ -1,10 +1,3 @@
-<!--
- * @Author: liuJie
- * @Date: 2026-01-27 14:45:03
- * @LastEditors: liuJie
- * @LastEditTime: 2026-01-27 18:19:49
- * @Describe: file describe
--->
 <template>
     <div class="space-y-4">
         <!-- 失败时重试 -->
@@ -15,11 +8,11 @@
             </div>
             <div class="space-y-2" v-if="config.retryEnabled">
                 <div class="pl-[22px]">
-                    <p class="m-0 text-sm">最大重试次数</p>
+                    <p class="m-0 text-sm text-gray-600">最大重试次数</p>
                     <el-slider v-model="config.maxRetries" show-input />
                 </div>
                 <div class="pl-[22px]">
-                    <p class="m-0 text-sm">重试间隔</p>
+                    <p class="m-0 text-sm text-gray-600">重试间隔</p>
                     <el-slider v-model="config.retryInterval" show-input />
                 </div>
             </div>
@@ -31,13 +24,13 @@
                 异常处理
                 <Icon icon="lucide:info" :height="14" :width="14" class="text-gray-400" />
             </label>
-            <select v-model="config.errorHandling" @change="handleConfigChange"
+            <ElSelect v-model="config.errorHandling" @change="handleConfigChange"
                 class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white">
-                <option value="none">无</option>
-                <option value="stop">停止执行</option>
-                <option value="continue">继续执行</option>
-                <option value="retry">重试</option>
-            </select>
+                <el-option value="none">无</el-option>
+                <el-option value="stop">停止执行</el-option>
+                <el-option value="continue">继续执行</el-option>
+                <el-option value="retry">重试</el-option>
+            </ElSelect>
         </div>
     </div>
 </template>

+ 125 - 0
apps/web/src/components/SetterCommon/condition/BranchCard.vue

@@ -0,0 +1,125 @@
+<template>
+	<div
+		class="border rounded-lg transition-all"
+		:class="[
+      isActive ? 'border-blue-400 bg-blue-50/30' : 'border-gray-200 bg-white',
+      isDragging ? 'opacity-50' : ''
+    ]"
+	>
+		<!-- 分支头部 -->
+		<div
+			class="flex items-center justify-between px-4 py-3 cursor-pointer"
+			@click="toggleExpanded"
+		>
+			<div class="flex items-center gap-3">
+				<!-- 分支类型标签 -->
+				<div
+					class="px-2.5 py-1 rounded text-xs font-medium"
+					:class="branchTypeClass"
+				>
+					{{ branchTypeLabel }}
+				</div>
+
+				<!-- 分支标题 -->
+				<div class="flex flex-col">
+					<span class="text-sm font-medium text-gray-900">{{ branch.label }}</span>
+					<span v-if="branch.description" class="text-xs text-gray-500">
+            {{ branch.description }}
+          </span>
+				</div>
+			</div>
+
+			<div class="flex items-center gap-2">
+				<!-- 删除按钮 (只有 ELIF 可以删除) -->
+				<ElButton
+					v-if="branch.type === 'elif'"
+					@click.stop="handleRemove"
+					class="p-1.5 hover:bg-red-50 rounded transition-colors"
+					title="删除分支"
+				>
+					<Icon icon="lucide:trash-2" :height="16" :width="16" class="text-red-500" />
+				</ElButton>
+
+				<!-- 展开/收起图标 -->
+				<Icon
+					:icon="isExpanded ? 'lucide:chevron-up' : 'lucide:chevron-down'"
+					:height="18"
+					:width="18"
+					class="text-gray-400"
+				/>
+			</div>
+		</div>
+
+		<!-- 分支内容 -->
+		<div
+			v-show="isExpanded"
+			class="px-4 pb-4 space-y-3 border-t border-gray-100"
+		>
+			<!-- IF 和 ELIF 的条件配置 -->
+			<div v-if="branch.type !== 'else'" class="pt-3">
+				<slot name="condition"></slot>
+			</div>
+
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed } from 'vue'
+import { Icon } from '@iconify/vue'
+
+interface Branch {
+	id: string
+	type: 'if' | 'elif' | 'else'
+	label: string
+	description?: string
+	conditions?: any[]
+}
+
+interface Props {
+	branch: Branch
+	isActive?: boolean
+	isDragging?: boolean
+}
+
+interface Emits {
+	(e: 'remove'): void
+	(e: 'toggle'): void
+}
+
+const props = withDefaults(defineProps<Props>(), {
+	isActive: false,
+	isDragging: false
+})
+
+const emit = defineEmits<Emits>()
+
+const isExpanded = ref(true)
+
+const branchTypeLabel = computed(() => {
+	const labels: Record<string, string> = {
+		if: 'IF',
+		elif: 'ELIF',
+		else: 'ELSE'
+	}
+	return labels[props.branch.type] || 'IF'
+})
+
+const branchTypeClass = computed(() => {
+	const classes: Record<string, string> = {
+		if: 'bg-blue-100 text-blue-700',
+		elif: 'bg-purple-100 text-purple-700',
+		else: 'bg-gray-100 text-gray-700'
+	}
+	return classes[props.branch.type] || 'bg-blue-100 text-blue-700'
+})
+
+const toggleExpanded = () => {
+	isExpanded.value = !isExpanded.value
+	emit('toggle')
+}
+
+const handleRemove = () => {
+	emit('remove')
+}
+</script>

+ 151 - 0
apps/web/src/components/SetterCommon/condition/ConditionBuilder.vue

@@ -0,0 +1,151 @@
+<template>
+	<div class="space-y-2">
+		<!-- 条件行 -->
+		<div
+			v-for="(condition, index) in conditions"
+			:key="condition.id"
+			class="flex items-center"
+		>
+			<!-- 左侧变量选择 -->
+			<div class="relative w-3/4">
+					<ElSelect placeholder="选择变量" v-model="condition.leftValue">
+						<el-option></el-option>
+					</ElSelect>
+<!--				<ElButton-->
+<!--					@click="openVariablePicker(index, 'left')"-->
+<!--					class="w-full px-3 py-2 text-sm text-left border border-gray-200 rounded-lg bg-white flex items-center justify-between group"-->
+<!--				>-->
+<!--          <span v-if="condition.leftValue" class="flex items-center gap-1">-->
+<!--            <Icon icon="lucide:at-sign" :height="14" :width="14" class="text-red-500" />-->
+<!--            <span class="text-red-600">{{ condition.leftValue }}</span>-->
+<!--          </span>-->
+<!--					<span v-else class="text-gray-400">选择变量</span>-->
+<!--					<Icon icon="lucide:chevron-down" :height="14" :width="14" class="text-gray-400" />-->
+<!--				</ElButton>-->
+			</div>
+
+			<!-- 运算符选择 -->
+			<ElSelect
+				v-model="condition.operator"
+				@change="handleConditionChange"
+				class="w-[60px] px-3 py-2 text-sm border border-gray-200 rounded-lg  bg-white ">
+				<el-option value="equals">等于</el-option>
+				<el-option value="notEquals">不等于</el-option>
+				<el-option value="contains">包含</el-option>
+				<el-option value="notContains">不包含</el-option>
+				<el-option value="greaterThan">大于</el-option>
+				<el-option value="lessThan">小于</el-option>
+				<el-option value="greaterOrEqual">大于等于</el-option>
+				<el-option value="lessOrEqual">小于等于</el-option>
+			</ElSelect>
+
+			<!-- 右侧值输入 -->
+			<div class="relative w-3/4">
+				<ElInput
+					v-model="condition.rightValue"
+					type="text"
+					placeholder="输入值"
+					@input="handleConditionChange"
+					class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
+				/>
+			</div>
+
+			<!-- 删除按钮 -->
+			<ElButton
+				v-if="conditions.length > 1"
+				@click="removeCondition(index)"
+				class="p-2 w-1/4 opacity-0 hover:opacity-100 hover:bg-red-50 rounded transition-all"
+				title="删除条件">
+					<Icon icon="lucide:trash-2" :height="16" :width="16" class="text-red-500" />
+			</ElButton>
+		</div>
+
+		<!-- AND 连接符 -->
+<!--		<div v-if="showAndButton" class="flex items-center gap-2 pl-3">-->
+<!--			<div class="flex items-center gap-2 px-3 py-1.5 bg-blue-50 text-blue-600 text-xs font-medium rounded">-->
+<!--				<Icon icon="lucide:link" :height="12" :width="12" />-->
+<!--				AND-->
+<!--			</div>-->
+<!--		</div>-->
+
+		<!-- 添加条件按钮 -->
+		<div class="text-center">
+			<ElButton
+				@click="addCondition"
+				class="flex items-center gap-2 px-3 py-2 text-sm text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
+			>
+				<Icon icon="lucide:plus" :height="16" :width="16" />
+				添加条件
+			</ElButton>
+		</div>
+
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, watch } from 'vue'
+import { Icon } from '@iconify/vue'
+
+interface Condition {
+	id: string
+	leftValue: string
+	operator: string
+	rightValue: string
+}
+
+interface Props {
+	modelValue: Condition[]
+}
+
+interface Emits {
+	(e: 'update:modelValue', value: Condition[]): void
+	(e: 'openVariablePicker', index: number, side: 'left' | 'right'): void
+}
+
+const props = defineProps<Props>()
+const emit = defineEmits<Emits>()
+
+const conditions = ref<Condition[]>(props.modelValue || [
+	{
+		id: `condition_${Date.now()}`,
+		leftValue: '',
+		operator: 'equals',
+		rightValue: ''
+	}
+])
+
+const showAndButton = computed(() => conditions.value.length > 1)
+
+watch(
+	() => props.modelValue,
+	(newVal) => {
+		if (newVal && newVal.length > 0) {
+			conditions.value = newVal
+		}
+	},
+	{ deep: true }
+)
+
+const addCondition = () => {
+	conditions.value.push({
+		id: `condition_${Date.now()}`,
+		leftValue: '',
+		operator: 'equals',
+		rightValue: ''
+	})
+	handleConditionChange()
+}
+
+const removeCondition = (index: number) => {
+	conditions.value.splice(index, 1)
+	handleConditionChange()
+}
+
+const handleConditionChange = () => {
+	emit('update:modelValue', conditions.value)
+}
+
+const openVariablePicker = (index: number, side: 'left' | 'right') => {
+	emit('openVariablePicker', index, side)
+}
+</script>

+ 159 - 0
apps/web/src/components/SetterCommon/condition/VariablePicker.vue

@@ -0,0 +1,159 @@
+<template>
+	<div
+		v-if="isOpen"
+		class="fixed inset-0 bg-black bg-opacity-30 flex items-center justify-center z-50"
+		@click.self="handleClose"
+	>
+		<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
+			<!-- 头部 -->
+			<div class="flex items-center justify-between px-4 py-3 border-b border-gray-200">
+				<h3 class="text-sm font-semibold text-gray-900">选择变量</h3>
+				<ElButton
+					@click="handleClose"
+					class="p-1 hover:bg-gray-100 rounded transition-colors"
+				>
+					<Icon icon="lucide:x" :height="18" :width="18" class="text-gray-500" />
+				</ElButton>
+			</div>
+
+			<!-- 搜索框 -->
+			<div class="p-4 border-b border-gray-100">
+				<div class="relative">
+					<Icon
+						icon="lucide:search"
+						:height="16"
+						:width="16"
+						class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
+					/>
+					<ElInput
+						v-model="searchQuery"
+						type="text"
+						placeholder="搜索变量..."
+						class="w-full pl-10 pr-4 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
+					/>
+				</div>
+			</div>
+
+			<!-- 变量列表 -->
+			<div class="max-h-96 overflow-y-auto">
+				<div v-if="filteredVariables.length === 0" class="px-4 py-8 text-center">
+					<Icon icon="lucide:inbox" :height="48" :width="48" class="mx-auto mb-2 text-gray-300" />
+					<p class="text-sm text-gray-500">没有找到变量</p>
+				</div>
+
+				<div v-else class="py-2">
+					<ElButton
+						v-for="variable in filteredVariables"
+						:key="variable.id"
+						@click="selectVariable(variable)"
+						class="w-full px-4 py-2.5 text-left hover:bg-gray-50 transition-colors flex items-center gap-3 group"
+					>
+						<div
+							class="w-8 h-8 rounded-lg flex items-center justify-center"
+							:class="getVariableIconBg(variable.source)"
+						>
+							<Icon
+								:icon="getVariableIcon(variable.source)"
+								:height="16"
+								:width="16"
+								:class="getVariableIconColor(variable.source)"
+							/>
+						</div>
+						<div class="flex-1">
+							<div class="flex items-center gap-2">
+								<span class="text-sm font-medium text-gray-900">{{ variable.name }}</span>
+								<span class="text-xs px-2 py-0.5 bg-gray-100 text-gray-600 rounded">
+                  {{ variable.type }}
+                </span>
+							</div>
+							<p class="text-xs text-gray-500 mt-0.5">{{ variable.source }}</p>
+						</div>
+						<Icon
+							icon="lucide:chevron-right"
+							:height="16"
+							:width="16"
+							class="text-gray-400 opacity-0 group-hover:opacity-100 transition-opacity"
+						/>
+					</ElButton>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed } from 'vue'
+import { Icon } from '@iconify/vue'
+
+interface Variable {
+	id: string
+	name: string
+	type: string
+	source: string
+}
+
+interface Props {
+	isOpen: boolean
+	variables: Variable[]
+}
+
+interface Emits {
+	(e: 'close'): void
+	(e: 'select', variable: Variable): void
+}
+
+const props = defineProps<Props>()
+const emit = defineEmits<Emits>()
+
+const searchQuery = ref('')
+
+const filteredVariables = computed(() => {
+	if (!searchQuery.value) return props.variables
+
+	const query = searchQuery.value.toLowerCase()
+	return props.variables.filter(v =>
+		v.name.toLowerCase().includes(query) ||
+		v.source.toLowerCase().includes(query)
+	)
+})
+
+const getVariableIcon = (source: string) => {
+	const icons: Record<string, string> = {
+		'HTTP 请求': 'lucide:globe',
+		'代码执行': 'lucide:code-2',
+		'SQL查询': 'lucide:database',
+		'条件分支': 'lucide:git-branch'
+	}
+	return icons[source] || 'lucide:box'
+}
+
+const getVariableIconBg = (source: string) => {
+	const colors: Record<string, string> = {
+		'HTTP 请求': 'bg-purple-100',
+		'代码执行': 'bg-blue-100',
+		'SQL查询': 'bg-green-100',
+		'条件分支': 'bg-orange-100'
+	}
+	return colors[source] || 'bg-gray-100'
+}
+
+const getVariableIconColor = (source: string) => {
+	const colors: Record<string, string> = {
+		'HTTP 请求': 'text-purple-600',
+		'代码执行': 'text-blue-600',
+		'SQL查询': 'text-green-600',
+		'条件分支': 'text-orange-600'
+	}
+	return colors[source] || 'text-gray-600'
+}
+
+const selectVariable = (variable: Variable) => {
+	emit('select', variable)
+	handleClose()
+}
+
+const handleClose = () => {
+	searchQuery.value = ''
+	emit('close')
+}
+</script>

+ 15 - 77
apps/web/src/components/setter/CodeSetter.vue

@@ -1,43 +1,9 @@
 <template>
-    <div class=" bg-black bg-opacity-50 flex items-center justify-center">
-        <div class="bg-white  w-full max-w-4xl max-h-[90vh] flex flex-col">
-            <!-- Header -->
-            <div class="flex items-start justify-between border-b border-gray-200">
-                <div class="flex items-center gap-3">
-                    <div class="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center">
-                        <Icon icon="lucide:code-2" :height="20" :width="20" class="text-blue-600" />
-                    </div>
-                    <div>
-                        <h2 class="text-lg font-semibold text-gray-900 m-0">代码执行</h2>
-                    </div>
-
-                </div>
-                <div class="flex items-center gap-2">
-                    <button @click="emit('update:visible', false)"
-                        class="px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 focus:outline-none rounded-lg flex items-center gap-2">
-                        <Icon icon="lucide:play" :height="16" :width="16" />
-                    </button>
-                    <button class="p-2 hover:bg-gray-100 rounded-lg focus:outline-none">
-                        <Icon icon="lucide:more-horizontal" :height="18" :width="18" class="text-gray-600" />
-                    </button>
-                    <button @click="handleClose" class="p-2 hover:bg-gray-100 rounded-lg focus:outline-none">
-                        <Icon icon="lucide:x" :height="18" :width="18" class="text-gray-600" />
-                    </button>
-                </div>
-
-            </div>
-            <div>
-                <input v-model="nodeName" type="text" placeholder="添加描述..."
-                    class="text-sm text-gray-500 border-none outline-none focus:ring-0 p-0 mt-0.5 w-full" />
-            </div>
-            <!--			 Tab 导航-->
-            <div class="mt-2">
-                <TabGroup v-model="activeTab" :tabs="tabs" />
-            </div>
-
+    <div class="bg-white w-full">
+        <div class="w-full">
             <!-- Content -->
-            <div class=" overflow-auto pt-4 pb-20">
-                <div v-show="activeTab === 'settings'" class="space-y-6">
+            <div class="p-4">
+                <div class="space-y-6">
                     <!-- 输入变量 -->
                     <InputVariables v-model="formData.inputVariables" />
 
@@ -50,26 +16,18 @@
                     <!-- 测试配置 -->
                     <TestConfig v-model="formData.testConfig" />
                 </div>
-
-                <div v-show="activeTab === 'lastRun'" class="space-y-4">
-                    <div class="text-center py-12 text-gray-400">
-                        <Icon icon="lucide:clock" :height="48" :width="48" class="mx-auto mb-3 text-gray-300" />
-                        <p class="text-sm">暂无运行记录</p>
-                    </div>
-                </div>
             </div>
         </div>
     </div>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive } from 'vue'
-import { Icon } from '@iconify/vue'
-import TabGroup from '@/components/SetterCommon/Code/TabGroup.vue'
+import { reactive } from 'vue'
 import InputVariables from '@/components/SetterCommon/Code/InputVariables.vue'
 import CodeEditor from '@/components/SetterCommon/Code/CodeEditor.vue'
 import OutputVariables from '@/components/SetterCommon/Code/OutputVariables.vue'
 import TestConfig from '@/components/SetterCommon/Code/TestConfig.vue'
+
 interface Variable {
     id: string
     name: string
@@ -86,29 +44,17 @@ interface TestConfigData {
     errorHandling: string
 }
 
-const props = withDefaults(defineProps<{
-    visible: boolean
-}>(), {
-    visible: false
-})
-
-const emit = defineEmits<{
-    'update:visible': [value: boolean]
-}>()
-
-const nodeName = ref('代码执行')
-const activeTab = ref('settings')
-
-const tabs = [
-    { label: '设置', value: 'settings' },
-    { label: '上次运行', value: 'lastRun' }
-]
+// const props = withDefaults(defineProps<{
+// }>(), {
+// })
+//
+// const emit = defineEmits<{
+// }>()
 
 const formData = reactive({
-    // 输入变量
+    // 输入变量, 变量默认值
     inputVariables: [
-        { id: 'var_1', name: 'arg1' },
-        { id: 'var_2', name: 'arg2' }
+        // { id: 'var_1', name: 'arg1' },
     ] as Variable[],
     // 代码内容
     code: `function main(arg1, arg2) {
@@ -119,7 +65,7 @@ const formData = reactive({
     language: 'JavaScript',
     // 输出变量
     outputVariables: [
-        { id: 'output_1', name: 'result', type: 'String' }
+        // { id: 'output_1', name: 'result', type: 'String' }
     ] as OutputVariable[],
     // 测试配置
     testConfig: {
@@ -128,12 +74,4 @@ const formData = reactive({
     } as TestConfigData
 })
 
-const handleClose = () => {
-    emit('update:visible', false)
-}
-
-const handleTest = () => {
-    console.log('Testing code execution...', formData)
-}
-
 </script>

+ 212 - 40
apps/web/src/components/setter/ConditionSetter.vue

@@ -1,46 +1,218 @@
-<!--
- * @Author: liuJie
- * @Date: 2026-01-25 22:08:04
- * @LastEditors: liuJie
- * @LastEditTime: 2026-01-25 23:04:45
- * @Describe: 条件设置器
--->
-<script lang="ts" setup>
-import { ElDrawer, ElButton } from 'element-plus';
-import { Icon } from '@iconify/vue';
-const props = withDefaults(
-    defineProps<{
-        data: any,
-        visible: boolean,
-    }>(),
-    {
-        visible: false,
-        data: {}
-    }
-);
-const emit = defineEmits<{
-    'update:visible': [value: boolean]
-}>()
-</script>
 <template>
-    <div class='content'>
-        <ElDrawer :model-value="visible" :show-close="false" size="25%" @close="emit('update:visible', false)">
+	<div class="bg-white w-full">
+		<div class="w-full">
+			<!-- Content -->
+				<!-- 设置标签页 -->
+				<div class="space-y-4">
+					<!-- IF 分支 -->
+					<BranchCard
+						:branch="branches.if"
+						:is-active="activeBranchId === branches.if.id"
+					>
+						<template #condition>
+							<ConditionBuilder
+								v-model="branches.if.conditions"
+								@open-variable-picker="openVariablePicker"
+							/>
+						</template>
+					</BranchCard>
+
+					<!-- ELIF 分支列表 -->
+					<BranchCard
+						v-for="(elifBranch, index) in branches.elifs"
+						:key="elifBranch.id"
+						:branch="elifBranch"
+						:is-active="activeBranchId === elifBranch.id"
+						@remove="removeElifBranch(index)"
+					>
+						<template #condition>
+							<ConditionBuilder
+								v-model="elifBranch.conditions"
+								@open-variable-picker="openVariablePicker"
+							/>
+						</template>
+					</BranchCard>
+
+					<!-- 添加 ELIF 按钮 -->
+					<div class="text-center">
+						<ElButton @click="addElifBranch" class="gap-2 px-4 py-3">
+							<Icon icon="lucide:plus" :height="16" :width="16" />
+							添加 ELIF
+						</ElButton>
+					</div>
 
-            <template #header>
-                <h4>条件</h4>
-                <Icon icon="lucide:x" height="24" width="24"></Icon>
-            </template>
 
-            <!-- Drawer content -->
-            This is drawer content.
+					<!-- ELSE 分支 -->
+					<BranchCard
+						:branch="branches.else"
+						:is-active="activeBranchId === branches.else.id"
+					>
+						<template #condition>
+							<!-- ELSE 没有条件配置 -->
+						</template>
+					</BranchCard>
+				</div>
 
-            <!-- <template #footer>
-                <ElButton type="success" size="large" class="w-full" @click="emit('update:visible', false)">
-                    运行
-                </ElButton>
-            </template> -->
+		</div>
 
-        </ElDrawer>
-    </div>
+		<!-- 变量选择器弹窗 -->
+<!--		<VariablePicker-->
+<!--			:is-open="showVariablePicker"-->
+<!--			:variables="availableVariables"-->
+<!--			@close="closeVariablePicker"-->
+<!--			@select="handleVariableSelect"-->
+<!--		/>-->
+	</div>
 </template>
-<style lang="scss" scoped></style>
+
+<script setup lang="ts">
+import { ref, reactive } from 'vue'
+import { Icon } from '@iconify/vue'
+import BranchCard from '@/components/SetterCommon/condition/BranchCard.vue'
+import ConditionBuilder from '@/components/SetterCommon/condition/ConditionBuilder.vue'
+import VariablePicker from '@/components/SetterCommon/condition/VariablePicker.vue'
+
+interface Condition {
+	id: string
+	leftValue: string
+	operator: string
+	rightValue: string
+}
+
+interface Branch {
+	id: string
+	type: 'if' | 'elif' | 'else'
+	label: string
+	description?: string
+	conditions: Condition[]
+}
+
+interface Variable {
+	id: string
+	name: string
+	type: string
+	source: string
+}
+
+interface Emits {
+	(e: 'close'): void
+	(e: 'save', data: any): void
+}
+
+const emit = defineEmits<Emits>()
+
+const nodeName = ref('条件分支')
+const activeTab = ref('settings')
+const activeBranchId = ref<string | null>(null)
+const showVariablePicker = ref(false)
+const currentPickerContext = ref<{ branchId?: string; conditionIndex?: number; side?: 'left' | 'right' }>({})
+
+const tabs = [
+	{ label: '设置', value: 'settings' },
+	{ label: '上次运行', value: 'lastRun' }
+]
+
+// 分支数据
+const branches = reactive({
+	if: {
+		id: 'if_1',
+		type: 'if' as const,
+		label: 'CASE 1',
+		description: '条件未设置',
+		conditions: [
+			{
+				id: 'condition_1',
+				leftValue: 'sys.user_id',
+				operator: 'equals',
+				rightValue: '1323'
+			}
+		]
+	},
+	elifs: [] as Branch[],
+	else: {
+		id: 'else_1',
+		type: 'else' as const,
+		label: 'ELSE',
+		description: '用于定义当 if 条件不满足且没有匹配的逻辑时。',
+		conditions: []
+	}
+})
+
+// 可用变量列表
+const availableVariables: Variable[] = [
+	{ id: 'var_1', name: 'sys.user_id', type: 'String', source: 'HTTP 请求' },
+	{ id: 'var_2', name: 'sys.app_id', type: 'Number', source: 'HTTP 请求' },
+	{ id: 'var_3', name: 'result', type: 'String', source: '代码执行' },
+	{ id: 'var_4', name: 'output_format', type: 'String', source: 'SQL查询' },
+	{ id: 'var_5', name: 'user_name', type: 'String', source: 'HTTP 请求' },
+	{ id: 'var_6', name: 'user_email', type: 'String', source: 'HTTP 请求' },
+]
+
+const addElifBranch = () => {
+	const newElif: Branch = {
+		id: `elif_${Date.now()}`,
+		type: 'elif',
+		label: `CASE ${branches.elifs.length + 2}`,
+		description: '条件未设置',
+		conditions: [
+			{
+				id: `condition_${Date.now()}`,
+				leftValue: '',
+				operator: 'equals',
+				rightValue: ''
+			}
+		]
+	}
+	branches.elifs.push(newElif)
+}
+
+const removeElifBranch = (index: number) => {
+	branches.elifs.splice(index, 1)
+	// 重新编号 CASE
+	branches.elifs.forEach((branch, idx) => {
+		branch.label = `CASE ${idx + 2}`
+	})
+}
+
+const openVariablePicker = (conditionIndex: number, side: 'left' | 'right') => {
+	currentPickerContext.value = { conditionIndex, side }
+	showVariablePicker.value = true
+}
+
+const closeVariablePicker = () => {
+	showVariablePicker.value = false
+	currentPickerContext.value = {}
+}
+
+const handleVariableSelect = (variable: Variable) => {
+	const { conditionIndex, side } = currentPickerContext.value
+
+	if (conditionIndex !== undefined && side) {
+		// 更新对应条件的值
+		if (side === 'left') {
+			branches.if.conditions[conditionIndex].leftValue = variable.name
+		}
+	}
+}
+
+const handleTest = () => {
+	console.log('Testing condition branch...', branches)
+}
+
+const handleClose = () => {
+	emit('close')
+}
+
+const handleSave = () => {
+	const data = {
+		name: nodeName.value,
+		branches: {
+			if: branches.if,
+			elifs: branches.elifs,
+			else: branches.else
+		}
+	}
+	emit('save', data)
+	console.log('Saved:', data)
+}
+</script>

+ 1 - 0
apps/web/src/main.ts

@@ -8,6 +8,7 @@ import 'element-plus/dist/index.css'
 import zhCn from 'element-plus/es/locale/lang/zh-cn'
 import i18n from './i18n'
 import 'virtual:svg-icons-register'
+// import 'monaco-editor/esm/vs/editor/editor.main.css';
 
 import 'normalize.css'
 import 'virtual:uno.css'

+ 0 - 1
apps/web/src/views/Editor.vue

@@ -144,7 +144,6 @@ const handleNodeCreate = (value: SourceType | string) => {
 	console.log(workflow.value.nodes, 'workflow.nodes')
 }
 const handleNodeClick = (id: string, position: XYPosition) => {
-	console.log('click node', id, position)
 	nodeID.value = id
 	setterVisible.value = true
 }

+ 5 - 3
apps/web/vite.config.ts

@@ -8,12 +8,10 @@ import AutoImport from 'unplugin-auto-import/vite'
 import Components from 'unplugin-vue-components/vite'
 import IconsResolver from 'unplugin-icons/resolver'
 import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import monacoEditorPlugin from 'vite-plugin-monaco-editor'
 
 // https://vite.dev/config/
 export default defineConfig({
-    server: {
-            hmr: true
-        },
 	plugins: [
 		vue(),
 		UnoCss(),
@@ -22,6 +20,10 @@ export default defineConfig({
 			iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
 			symbolId: 'svg-icon-[dir]-[name]'
 		}),
+		// 代码编辑器
+		(monacoEditorPlugin as any).default({
+			languageWorkers: ['editorWorkerService', 'typescript', 'json', 'html', 'css']
+		}),
 		// 按需求加载(模板)
 		AutoImport({
 			imports: ['vue'],

+ 387 - 52
pnpm-lock.yaml

@@ -148,6 +148,9 @@ importers:
       element-plus:
         specifier: ^2.13.1
         version: 2.13.1(vue@3.5.27(typescript@5.9.3))
+      monaco-editor:
+        specifier: ^0.55.1
+        version: 0.55.1
       normalize.css:
         specifier: ^8.0.1
         version: 8.0.1
@@ -175,10 +178,13 @@ importers:
         version: link:../../packages/workflow
       '@vitejs/plugin-vue':
         specifier: ^6.0.1
-        version: 6.0.3(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))(vue@3.5.27(typescript@5.9.3))
+        version: 6.0.3(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))(vue@3.5.27(typescript@5.9.3))
       '@vue/tsconfig':
         specifier: ^0.8.1
         version: 0.8.1(typescript@5.9.3)(vue@3.5.27(typescript@5.9.3))
+      esbuild:
+        specifier: ^0.27.2
+        version: 0.27.2
       less:
         specifier: ^4.5.1
         version: 4.5.1
@@ -187,7 +193,7 @@ importers:
         version: 5.9.3
       unocss:
         specifier: ^66.6.0
-        version: 66.6.0(postcss@5.2.18)(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))
+        version: 66.6.0(postcss@5.2.18)(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))
       unplugin-auto-import:
         specifier: ^21.0.0
         version: 21.0.0(@vueuse/core@13.9.0(vue@3.5.27(typescript@5.9.3)))
@@ -199,10 +205,13 @@ importers:
         version: 31.0.0(vue@3.5.27(typescript@5.9.3))
       vite:
         specifier: npm:rolldown-vite@7.2.5
-        version: rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)
+        version: rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)
+      vite-plugin-monaco-editor:
+        specifier: ^1.1.0
+        version: 1.1.0(monaco-editor@0.55.1)
       vite-plugin-svg-icons:
         specifier: ^2.0.1
-        version: 2.0.1(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))
+        version: 2.0.1(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))
       vue-tsc:
         specifier: ^3.1.4
         version: 3.2.2(typescript@5.9.3)
@@ -218,7 +227,7 @@ importers:
         version: link:../typescript-config
       '@umijs/openapi':
         specifier: ^1.14.1
-        version: 1.14.1(typescript@5.9.3)
+        version: 1.14.1(chokidar@5.0.0)(typescript@5.9.3)
 
   packages/api-service:
     devDependencies:
@@ -230,7 +239,7 @@ importers:
         version: link:../typescript-config
       '@umijs/openapi':
         specifier: ^1.14.1
-        version: 1.14.1(typescript@5.9.3)
+        version: 1.14.1(chokidar@5.0.0)(typescript@5.9.3)
 
   packages/eslint-config:
     devDependencies:
@@ -275,7 +284,7 @@ importers:
         version: 4.5.1
       less-loader:
         specifier: ^12.3.0
-        version: 12.3.0(@rspack/core@1.7.3)(less@4.5.1)
+        version: 12.3.0(@rspack/core@1.7.3(@swc/helpers@0.5.18))(less@4.5.1)
     devDependencies:
       '@repo/nodes':
         specifier: workspace:*
@@ -344,13 +353,13 @@ importers:
         version: 4.5.1
       less-loader:
         specifier: ^12.3.0
-        version: 12.3.0(@rspack/core@1.7.3)(less@4.5.1)
+        version: 12.3.0(@rspack/core@1.7.3(@swc/helpers@0.5.18))(less@4.5.1)
       normalize.css:
         specifier: ^8.0.1
         version: 8.0.1
       unocss:
         specifier: ^66.6.0
-        version: 66.6.0(postcss@8.5.6)(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))
+        version: 66.6.0(postcss@8.5.6)(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))
       vue:
         specifier: ^3.5.24
         version: 3.5.27(typescript@5.9.3)
@@ -369,7 +378,7 @@ importers:
         version: 24.10.9
       '@vitejs/plugin-vue':
         specifier: ^6.0.1
-        version: 6.0.3(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))(vue@3.5.27(typescript@5.9.3))
+        version: 6.0.3(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))(vue@3.5.27(typescript@5.9.3))
       '@vue/tsconfig':
         specifier: ^0.8.1
         version: 0.8.1(typescript@5.9.3)(vue@3.5.27(typescript@5.9.3))
@@ -378,7 +387,7 @@ importers:
         version: 5.9.3
       vite:
         specifier: npm:rolldown-vite@7.2.5
-        version: rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)
+        version: rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)
       vue-tsc:
         specifier: ^3.1.4
         version: 3.2.2(typescript@5.9.3)
@@ -1005,24 +1014,28 @@ packages:
     engines: {node: '>=14.21.3'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@biomejs/cli-linux-arm64@2.3.11':
     resolution: {integrity: sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g==}
     engines: {node: '>=14.21.3'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@biomejs/cli-linux-x64-musl@2.3.11':
     resolution: {integrity: sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw==}
     engines: {node: '>=14.21.3'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@biomejs/cli-linux-x64@2.3.11':
     resolution: {integrity: sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg==}
     engines: {node: '>=14.21.3'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@biomejs/cli-win32-arm64@2.3.11':
     resolution: {integrity: sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw==}
@@ -1436,6 +1449,162 @@ packages:
   '@emotion/unitless@0.7.5':
     resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
 
+  '@esbuild/aix-ppc64@0.27.2':
+    resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/android-arm64@0.27.2':
+    resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.27.2':
+    resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.27.2':
+    resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.27.2':
+    resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.27.2':
+    resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.27.2':
+    resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.27.2':
+    resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.27.2':
+    resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.27.2':
+    resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.27.2':
+    resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.27.2':
+    resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==}
+    engines: {node: '>=18'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.27.2':
+    resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==}
+    engines: {node: '>=18'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.27.2':
+    resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.27.2':
+    resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==}
+    engines: {node: '>=18'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.27.2':
+    resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==}
+    engines: {node: '>=18'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.27.2':
+    resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-arm64@0.27.2':
+    resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.27.2':
+    resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-arm64@0.27.2':
+    resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.27.2':
+    resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/openharmony-arm64@0.27.2':
+    resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openharmony]
+
+  '@esbuild/sunos-x64@0.27.2':
+    resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.27.2':
+    resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.27.2':
+    resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.27.2':
+    resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [win32]
+
   '@eslint-community/eslint-utils@4.9.0':
     resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -1983,24 +2152,28 @@ packages:
     engines: {node: ^20.19.0 || >=22.12.0}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50':
     resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==}
     engines: {node: ^20.19.0 || >=22.12.0}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50':
     resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==}
     engines: {node: ^20.19.0 || >=22.12.0}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@rolldown/binding-linux-x64-musl@1.0.0-beta.50':
     resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==}
     engines: {node: ^20.19.0 || >=22.12.0}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@rolldown/binding-openharmony-arm64@1.0.0-beta.50':
     resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==}
@@ -2066,21 +2239,25 @@ packages:
     resolution: {integrity: sha512-SodEX3+1/GLz0LobX9cY1QdjJ1NftSEh4C2vGpr71iA3MS9HyXuw4giqSeRQ4DpCybqpdS/3RLjVqFQEfGpcnw==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@rspack/binding-linux-arm64-musl@1.7.3':
     resolution: {integrity: sha512-ydD2fNdEy+G7EYJ/a3FfdFZPfrLj/UnZocCNlZTTSHEhu+jURdQk0hwV11CvL+sjnKU5e/8IVMGUzhu3Gu8Ghg==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@rspack/binding-linux-x64-gnu@1.7.3':
     resolution: {integrity: sha512-adnDbUqafSAI6/N6vZ+iONSo1W3yUpnNtJqP3rVp7+YdABhUpbOhtaY37qpIJ3uFajXctYFyISPrb4MWl1M9Yg==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@rspack/binding-linux-x64-musl@1.7.3':
     resolution: {integrity: sha512-5jnjdODk5HCUFPN6rTaFukynDU4Fn9eCL+4TSp6mqo6YAnfnJEuzDjfetA8t3aQFcAs7WriQfNwvdcA4HvYtbA==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@rspack/binding-wasm32-wasi@1.7.3':
     resolution: {integrity: sha512-WLQK0ksUzMkVeGoHAMIxenmeEU5tMvFDK36Aip7VRj7T6vZTcAwvbMwc38QrIAvlG7dqWoxgPQi35ba1igNNDw==}
@@ -2651,41 +2828,49 @@ packages:
     resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
     resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
     resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
     cpu: [ppc64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
     resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
     resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
     cpu: [riscv64]
     os: [linux]
+    libc: [musl]
 
   '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
     resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
     resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@unrs/resolver-binding-linux-x64-musl@1.11.1':
     resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@unrs/resolver-binding-wasm32-wasi@1.11.1':
     resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -3646,6 +3831,9 @@ packages:
     resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
     engines: {node: '>= 4'}
 
+  dompurify@3.2.7:
+    resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
+
   dompurify@3.3.1:
     resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==}
 
@@ -3777,6 +3965,11 @@ packages:
   esast-util-from-js@2.0.1:
     resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==}
 
+  esbuild@0.27.2:
+    resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
+    engines: {node: '>=18'}
+    hasBin: true
+
   escalade@3.2.0:
     resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
     engines: {node: '>=6'}
@@ -4839,24 +5032,28 @@ packages:
     engines: {node: '>= 12.0.0'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   lightningcss-linux-arm64-musl@1.31.1:
     resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==}
     engines: {node: '>= 12.0.0'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   lightningcss-linux-x64-gnu@1.31.1:
     resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==}
     engines: {node: '>= 12.0.0'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   lightningcss-linux-x64-musl@1.31.1:
     resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==}
     engines: {node: '>= 12.0.0'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   lightningcss-win32-arm64-msvc@1.31.1:
     resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==}
@@ -4981,6 +5178,11 @@ packages:
       marked: '>=7.0.0'
       shiki: '>=1.0.0'
 
+  marked@14.0.0:
+    resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==}
+    engines: {node: '>= 18'}
+    hasBin: true
+
   marked@15.0.12:
     resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==}
     engines: {node: '>= 18'}
@@ -5238,6 +5440,9 @@ packages:
   modern-screenshot@4.6.7:
     resolution: {integrity: sha512-0GhgI6i6le4AhKzCvLYjwEmsP47kTsX45iT5yuAzsLTi/7i3Rjxe8fbH2VjGJLuyOThwsa0CdQAPd4auoEtsZg==}
 
+  monaco-editor@0.55.1:
+    resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
+
   mousetrap@1.6.5:
     resolution: {integrity: sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==}
 
@@ -6682,6 +6887,11 @@ packages:
   vfile@6.0.3:
     resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
 
+  vite-plugin-monaco-editor@1.1.0:
+    resolution: {integrity: sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==}
+    peerDependencies:
+      monaco-editor: '>=0.33.0'
+
   vite-plugin-svg-icons@2.0.1:
     resolution: {integrity: sha512-6ktD+DhV6Rz3VtedYvBKKVA2eXF+sAQVaKkKLDSqGUfnhqXl3bj5PPkVTl3VexfTuZy66PmINi8Q6eFnVfRUmA==}
     peerDependencies:
@@ -6904,17 +7114,17 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/eslint-parser@7.19.1(@babel/core@7.28.6)(eslint@9.39.1(jiti@2.6.1))':
+  '@babel/eslint-parser@7.19.1(@babel/core@7.28.6)(eslint@8.57.1)':
     dependencies:
       '@babel/core': 7.28.6
       '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
-      eslint: 9.39.1(jiti@2.6.1)
+      eslint: 8.57.1
       eslint-visitor-keys: 2.1.0
       semver: 6.3.1
 
   '@babel/eslint-plugin@7.27.1(@babel/eslint-parser@7.19.1(@babel/core@7.28.6)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))':
     dependencies:
-      '@babel/eslint-parser': 7.19.1(@babel/core@7.28.6)(eslint@9.39.1(jiti@2.6.1))
+      '@babel/eslint-parser': 7.19.1(@babel/core@7.28.6)(eslint@8.57.1)
       eslint: 9.39.1(jiti@2.6.1)
       eslint-rule-composer: 0.3.0
 
@@ -8363,6 +8573,84 @@ snapshots:
 
   '@emotion/unitless@0.7.5': {}
 
+  '@esbuild/aix-ppc64@0.27.2':
+    optional: true
+
+  '@esbuild/android-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/android-arm@0.27.2':
+    optional: true
+
+  '@esbuild/android-x64@0.27.2':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/darwin-x64@0.27.2':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-arm@0.27.2':
+    optional: true
+
+  '@esbuild/linux-ia32@0.27.2':
+    optional: true
+
+  '@esbuild/linux-loong64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.27.2':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-s390x@0.27.2':
+    optional: true
+
+  '@esbuild/linux-x64@0.27.2':
+    optional: true
+
+  '@esbuild/netbsd-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.27.2':
+    optional: true
+
+  '@esbuild/openbsd-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.27.2':
+    optional: true
+
+  '@esbuild/openharmony-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/sunos-x64@0.27.2':
+    optional: true
+
+  '@esbuild/win32-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/win32-ia32@0.27.2':
+    optional: true
+
+  '@esbuild/win32-x64@0.27.2':
+    optional: true
+
   '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)':
     dependencies:
       eslint: 8.57.1
@@ -8549,13 +8837,13 @@ snapshots:
   '@flowgram.ai/eslint-config@1.0.7(@types/node@18.19.130)(jiti@2.6.1)(typescript@5.9.3)':
     dependencies:
       '@babel/core': 7.28.6
-      '@babel/eslint-parser': 7.19.1(@babel/core@7.28.6)(eslint@9.39.1(jiti@2.6.1))
+      '@babel/eslint-parser': 7.19.1(@babel/core@7.28.6)(eslint@8.57.1)
       '@babel/eslint-plugin': 7.27.1(@babel/eslint-parser@7.19.1(@babel/core@7.28.6)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))
       '@babel/preset-env': 7.20.2(@babel/core@7.28.6)
       '@babel/preset-react': 7.13.13(@babel/core@7.28.6)
       '@eslint/eslintrc': 3.3.3
       '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
-      '@typescript-eslint/parser': 8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+      '@typescript-eslint/parser': 8.50.0(eslint@8.57.1)(typescript@5.9.3)
       eslint: 9.39.1(jiti@2.6.1)
       eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@2.6.1))
       eslint-define-config: 1.12.0
@@ -9868,7 +10156,7 @@ snapshots:
   '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
     dependencies:
       '@eslint-community/regexpp': 4.12.2
-      '@typescript-eslint/parser': 8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+      '@typescript-eslint/parser': 8.50.0(eslint@8.57.1)(typescript@5.9.3)
       '@typescript-eslint/scope-manager': 8.50.0
       '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
       '@typescript-eslint/utils': 8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
@@ -9881,27 +10169,27 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.2)':
+  '@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 8.50.0
       '@typescript-eslint/types': 8.50.0
-      '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.2)
+      '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3)
       '@typescript-eslint/visitor-keys': 8.50.0
       debug: 4.4.3(supports-color@5.5.0)
-      eslint: 9.39.1(jiti@2.6.1)
-      typescript: 5.9.2
+      eslint: 8.57.1
+      typescript: 5.9.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
+  '@typescript-eslint/parser@8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.2)':
     dependencies:
       '@typescript-eslint/scope-manager': 8.50.0
       '@typescript-eslint/types': 8.50.0
-      '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3)
+      '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.2)
       '@typescript-eslint/visitor-keys': 8.50.0
       debug: 4.4.3(supports-color@5.5.0)
       eslint: 9.39.1(jiti@2.6.1)
-      typescript: 5.9.3
+      typescript: 5.9.2
     transitivePeerDependencies:
       - supports-color
 
@@ -10026,7 +10314,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@umijs/openapi@1.14.1(typescript@5.9.3)':
+  '@umijs/openapi@1.14.1(chokidar@5.0.0)(typescript@5.9.3)':
     dependencies:
       chalk: 4.1.2
       cosmiconfig: 9.0.0(typescript@5.9.3)
@@ -10038,7 +10326,7 @@ snapshots:
       mockjs: 1.1.0
       node-fetch: 2.7.0
       number-to-words: 1.2.4
-      nunjucks: 3.2.4
+      nunjucks: 3.2.4(chokidar@5.0.0)
       openapi3-ts: 2.0.2
       prettier: 2.8.8
       reserved-words: 0.1.2
@@ -10052,13 +10340,13 @@ snapshots:
 
   '@ungap/structured-clone@1.3.0': {}
 
-  '@unocss/astro@66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))':
+  '@unocss/astro@66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))':
     dependencies:
       '@unocss/core': 66.6.0
       '@unocss/reset': 66.6.0
-      '@unocss/vite': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))
+      '@unocss/vite': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))
     optionalDependencies:
-      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)
+      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)
 
   '@unocss/cli@66.6.0':
     dependencies:
@@ -10197,7 +10485,7 @@ snapshots:
     dependencies:
       '@unocss/core': 66.6.0
 
-  '@unocss/vite@66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))':
+  '@unocss/vite@66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))':
     dependencies:
       '@jridgewell/remapping': 2.3.5
       '@unocss/config': 66.6.0
@@ -10208,7 +10496,7 @@ snapshots:
       pathe: 2.0.3
       tinyglobby: 0.2.15
       unplugin-utils: 0.3.1
-      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)
+      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)
 
   '@unrs/resolver-binding-android-arm-eabi@1.11.1':
     optional: true
@@ -10269,10 +10557,10 @@ snapshots:
   '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
     optional: true
 
-  '@vitejs/plugin-vue@6.0.3(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))(vue@3.5.27(typescript@5.9.3))':
+  '@vitejs/plugin-vue@6.0.3(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))(vue@3.5.27(typescript@5.9.3))':
     dependencies:
       '@rolldown/pluginutils': 1.0.0-beta.53
-      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)
+      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)
       vue: 3.5.27(typescript@5.9.3)
 
   '@volar/language-core@2.4.27':
@@ -11306,6 +11594,10 @@ snapshots:
     dependencies:
       domelementtype: 2.3.0
 
+  dompurify@3.2.7:
+    optionalDependencies:
+      '@types/trusted-types': 2.0.7
+
   dompurify@3.3.1:
     optionalDependencies:
       '@types/trusted-types': 2.0.7
@@ -11553,6 +11845,35 @@ snapshots:
       esast-util-from-estree: 2.0.0
       vfile-message: 4.0.3
 
+  esbuild@0.27.2:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.27.2
+      '@esbuild/android-arm': 0.27.2
+      '@esbuild/android-arm64': 0.27.2
+      '@esbuild/android-x64': 0.27.2
+      '@esbuild/darwin-arm64': 0.27.2
+      '@esbuild/darwin-x64': 0.27.2
+      '@esbuild/freebsd-arm64': 0.27.2
+      '@esbuild/freebsd-x64': 0.27.2
+      '@esbuild/linux-arm': 0.27.2
+      '@esbuild/linux-arm64': 0.27.2
+      '@esbuild/linux-ia32': 0.27.2
+      '@esbuild/linux-loong64': 0.27.2
+      '@esbuild/linux-mips64el': 0.27.2
+      '@esbuild/linux-ppc64': 0.27.2
+      '@esbuild/linux-riscv64': 0.27.2
+      '@esbuild/linux-s390x': 0.27.2
+      '@esbuild/linux-x64': 0.27.2
+      '@esbuild/netbsd-arm64': 0.27.2
+      '@esbuild/netbsd-x64': 0.27.2
+      '@esbuild/openbsd-arm64': 0.27.2
+      '@esbuild/openbsd-x64': 0.27.2
+      '@esbuild/openharmony-arm64': 0.27.2
+      '@esbuild/sunos-x64': 0.27.2
+      '@esbuild/win32-arm64': 0.27.2
+      '@esbuild/win32-ia32': 0.27.2
+      '@esbuild/win32-x64': 0.27.2
+
   escalade@3.2.0: {}
 
   escape-string-regexp@1.0.5: {}
@@ -11594,14 +11915,13 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)):
+  eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
-      '@typescript-eslint/parser': 8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
-      eslint: 9.39.1(jiti@2.6.1)
+      '@typescript-eslint/parser': 8.50.0(eslint@8.57.1)(typescript@5.9.3)
+      eslint: 8.57.1
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1))
     transitivePeerDependencies:
       - supports-color
 
@@ -11621,7 +11941,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 8.57.1
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))
+      eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1)
       hasown: 2.0.2
       is-core-module: 2.16.1
       is-glob: 4.0.3
@@ -11633,7 +11953,7 @@ snapshots:
       string.prototype.trimend: 1.0.9
       tsconfig-paths: 3.15.0
     optionalDependencies:
-      '@typescript-eslint/parser': 8.50.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+      '@typescript-eslint/parser': 8.50.0(eslint@8.57.1)(typescript@5.9.3)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
@@ -12711,7 +13031,7 @@ snapshots:
 
   layout-base@2.0.1: {}
 
-  less-loader@12.3.0(@rspack/core@1.7.3)(less@4.5.1):
+  less-loader@12.3.0(@rspack/core@1.7.3(@swc/helpers@0.5.18))(less@4.5.1):
     dependencies:
       less: 4.5.1
     optionalDependencies:
@@ -12885,6 +13205,8 @@ snapshots:
       marked: 15.0.12
       shiki: 3.21.0
 
+  marked@14.0.0: {}
+
   marked@15.0.12: {}
 
   marked@16.4.2: {}
@@ -13459,6 +13781,11 @@ snapshots:
 
   modern-screenshot@4.6.7: {}
 
+  monaco-editor@0.55.1:
+    dependencies:
+      dompurify: 3.2.7
+      marked: 14.0.0
+
   mousetrap@1.6.5: {}
 
   mrmime@2.0.1: {}
@@ -13529,11 +13856,13 @@ snapshots:
 
   number-to-words@1.2.4: {}
 
-  nunjucks@3.2.4:
+  nunjucks@3.2.4(chokidar@5.0.0):
     dependencies:
       a-sync-waterfall: 1.0.1
       asap: 2.0.6
       commander: 5.1.0
+    optionalDependencies:
+      chokidar: 5.0.0
 
   oas-kit-common@1.0.8:
     dependencies:
@@ -14246,7 +14575,7 @@ snapshots:
 
   robust-predicates@3.0.2: {}
 
-  rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1):
+  rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2):
     dependencies:
       '@oxc-project/runtime': 0.97.0
       fdir: 6.5.0(picomatch@4.0.3)
@@ -14257,9 +14586,11 @@ snapshots:
       tinyglobby: 0.2.15
     optionalDependencies:
       '@types/node': 24.10.9
+      esbuild: 0.27.2
       fsevents: 2.3.3
       jiti: 2.6.1
       less: 4.5.1
+      yaml: 1.10.2
 
   rolldown@1.0.0-beta.50:
     dependencies:
@@ -15023,9 +15354,9 @@ snapshots:
 
   universalify@2.0.1: {}
 
-  unocss@66.6.0(postcss@5.2.18)(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)):
+  unocss@66.6.0(postcss@5.2.18)(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)):
     dependencies:
-      '@unocss/astro': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))
+      '@unocss/astro': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))
       '@unocss/cli': 66.6.0
       '@unocss/core': 66.6.0
       '@unocss/postcss': 66.6.0(postcss@5.2.18)
@@ -15043,16 +15374,16 @@ snapshots:
       '@unocss/transformer-compile-class': 66.6.0
       '@unocss/transformer-directives': 66.6.0
       '@unocss/transformer-variant-group': 66.6.0
-      '@unocss/vite': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))
+      '@unocss/vite': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))
     optionalDependencies:
-      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)
+      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)
     transitivePeerDependencies:
       - postcss
       - supports-color
 
-  unocss@66.6.0(postcss@8.5.6)(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)):
+  unocss@66.6.0(postcss@8.5.6)(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)):
     dependencies:
-      '@unocss/astro': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))
+      '@unocss/astro': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))
       '@unocss/cli': 66.6.0
       '@unocss/core': 66.6.0
       '@unocss/postcss': 66.6.0(postcss@8.5.6)
@@ -15070,9 +15401,9 @@ snapshots:
       '@unocss/transformer-compile-class': 66.6.0
       '@unocss/transformer-directives': 66.6.0
       '@unocss/transformer-variant-group': 66.6.0
-      '@unocss/vite': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1))
+      '@unocss/vite': 66.6.0(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2))
     optionalDependencies:
-      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)
+      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)
     transitivePeerDependencies:
       - postcss
       - supports-color
@@ -15201,7 +15532,11 @@ snapshots:
       '@types/unist': 3.0.3
       vfile-message: 4.0.3
 
-  vite-plugin-svg-icons@2.0.1(rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)):
+  vite-plugin-monaco-editor@1.1.0(monaco-editor@0.55.1):
+    dependencies:
+      monaco-editor: 0.55.1
+
+  vite-plugin-svg-icons@2.0.1(rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)):
     dependencies:
       '@types/svgo': 2.6.4
       cors: 2.8.6
@@ -15211,7 +15546,7 @@ snapshots:
       pathe: 0.2.0
       svg-baker: 1.7.0
       svgo: 2.8.0
-      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)
+      vite: rolldown-vite@7.2.5(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(yaml@1.10.2)
     transitivePeerDependencies:
       - supports-color