Showing
36 changed files
with
5713 additions
and
0 deletions
.gitignore
0 → 100644
AGENTS.md
0 → 100644
| 1 | +# 仓库协作规则 | ||
| 2 | + | ||
| 3 | +## 文档规则 | ||
| 4 | + | ||
| 5 | +- 本仓库中的需求文档、设计文档、计划文档、进度文档、说明文档统一使用中文编写。 | ||
| 6 | +- 遇到路径、命令、库名、API 名称、类型名等技术标识时,可保留原文,不强制翻译。 | ||
| 7 | +- 新增文档时,标题、章节说明、结论、风险、验收标准等面向人的内容必须使用中文。 | ||
| 8 | + | ||
| 9 | +## 代码注释规则 | ||
| 10 | + | ||
| 11 | +- 页面主要逻辑上的注释统一使用中文。 | ||
| 12 | +- 组件页面中与业务流程、交互状态、核心分支、数据转换相关的关键注释必须使用中文。 | ||
| 13 | +- 注释应简洁明确,优先解释“为什么这样做”或“这一段主要负责什么”,避免无意义的逐行翻译式注释。 | ||
| 14 | + | ||
| 15 | +## 执行要求 | ||
| 16 | + | ||
| 17 | +- 修改已有文档时,优先检查是否存在英文标题或英文说明,若有则同步转成中文。 | ||
| 18 | +- 新增页面或重构页面逻辑时,需要检查主要逻辑处是否补充了必要的中文注释。 |
apps/demo/index.html
0 → 100644
| 1 | +<!doctype html> | ||
| 2 | +<html lang="zh-CN"> | ||
| 3 | + <head> | ||
| 4 | + <meta charset="UTF-8" /> | ||
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| 6 | + <title>流程编辑器迁移基线页</title> | ||
| 7 | + </head> | ||
| 8 | + <body> | ||
| 9 | + <div id="app"></div> | ||
| 10 | + <script type="module" src="/src/main.ts"></script> | ||
| 11 | + </body> | ||
| 12 | +</html> | ||
| 13 | + |
apps/demo/src/App.vue
0 → 100644
| 1 | +<script setup lang="ts"> | ||
| 2 | +import { computed, onMounted, ref } from 'vue' | ||
| 3 | +import { ElMessage } from 'element-plus' | ||
| 4 | +import { FlowEditor, normalizeGraphData } from '@flow-editor' | ||
| 5 | +import type { | ||
| 6 | + FlowEditorRef, | ||
| 7 | + FlowEditorInputGraphData, | ||
| 8 | + ToolbarButtonDefinition, | ||
| 9 | +} from '@flow-editor' | ||
| 10 | + | ||
| 11 | +const editorRef = ref<FlowEditorRef | null>(null) | ||
| 12 | +const currentVersionLabel = ref('基线版本') | ||
| 13 | +const latestAction = ref('等待初始化') | ||
| 14 | +const flowData = ref<FlowEditorInputGraphData>({ | ||
| 15 | + nodes: [], | ||
| 16 | + edges: [], | ||
| 17 | +}) | ||
| 18 | + | ||
| 19 | +const toolbarButtonHandler = (buttons: ToolbarButtonDefinition[]) => { | ||
| 20 | + const hiddenKeys = new Set(['grid', 'miniMap', 'delete', 'undo', 'redo']) | ||
| 21 | + return buttons.filter((button) => !hiddenKeys.has(button.key)) | ||
| 22 | +} | ||
| 23 | + | ||
| 24 | +const hasLoadedRemoteBootstrap = computed(() => { | ||
| 25 | + const normalizedGraph = normalizeGraphData(flowData.value) | ||
| 26 | + return normalizedGraph.nodes.length > 0 || normalizedGraph.edges.length > 0 | ||
| 27 | +}) | ||
| 28 | + | ||
| 29 | +function onRef(instance: FlowEditorRef) { | ||
| 30 | + editorRef.value = instance | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +function centerCanvas() { | ||
| 34 | + editorRef.value?.commander.fitView() | ||
| 35 | + latestAction.value = '已执行居中/适应画布' | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +function saveFlowDraft() { | ||
| 39 | + latestAction.value = '当前为迁移阶段演示版,已模拟保存成功' | ||
| 40 | + ElMessage.success('当前为迁移阶段演示版,已模拟保存成功') | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +function openPreviewPanel() { | ||
| 44 | + editorRef.value?.openPreview() | ||
| 45 | + latestAction.value = '已打开流程预览' | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +async function loadBootstrapData() { | ||
| 49 | + // 这里保留旧项目的接口语义,方便把 Playwright 拦截能力平移到新项目。 | ||
| 50 | + const requestJson = async <T,>(url: string, fallback: T): Promise<T> => { | ||
| 51 | + try { | ||
| 52 | + const response = await fetch(url) | ||
| 53 | + if (!response.ok) { | ||
| 54 | + return fallback | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + const payload = (await response.json()) as { data?: T } | ||
| 58 | + return payload.data ?? fallback | ||
| 59 | + } catch { | ||
| 60 | + return fallback | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + const [versionList, graph] = await Promise.all([ | ||
| 65 | + requestJson<Array<{ note?: string }>>('/admin/index.php?m=mod&a=flow_version', [ | ||
| 66 | + { note: '基线版本' }, | ||
| 67 | + ]), | ||
| 68 | + requestJson<FlowEditorInputGraphData>('/admin/index.php?m=srv&a=flow_nodes', { | ||
| 69 | + nodes: [], | ||
| 70 | + edges: [], | ||
| 71 | + }), | ||
| 72 | + ]) | ||
| 73 | + | ||
| 74 | + currentVersionLabel.value = versionList[0]?.note || '基线版本' | ||
| 75 | + flowData.value = graph | ||
| 76 | + editorRef.value?.read(graph) | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +function bindTestContract() { | ||
| 80 | + window.__FLOW_EDITOR_TEST_API__ = { | ||
| 81 | + getEditorContract: () => { | ||
| 82 | + const instance = editorRef.value | ||
| 83 | + | ||
| 84 | + return { | ||
| 85 | + hasEditor: Boolean(instance), | ||
| 86 | + hasCommander: Boolean(instance?.commander), | ||
| 87 | + hasDelete: typeof instance?.commander?.delete === 'function', | ||
| 88 | + hasUndo: typeof instance?.commander?.undo === 'function', | ||
| 89 | + hasRedo: typeof instance?.commander?.redo === 'function', | ||
| 90 | + hasOpenModel: typeof instance?.openModel === 'function', | ||
| 91 | + hasCloseModel: typeof instance?.closeModel === 'function', | ||
| 92 | + hasAddNode: typeof instance?.addNode === 'function', | ||
| 93 | + hasUpdateModel: typeof instance?.updateModel === 'function', | ||
| 94 | + hasGraphRead: typeof instance?.read === 'function', | ||
| 95 | + } | ||
| 96 | + }, | ||
| 97 | + } | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | +onMounted(async () => { | ||
| 101 | + bindTestContract() | ||
| 102 | + await loadBootstrapData() | ||
| 103 | + bindTestContract() | ||
| 104 | +}) | ||
| 105 | +</script> | ||
| 106 | + | ||
| 107 | +<template> | ||
| 108 | + <div class="app"> | ||
| 109 | + <header class="page-header"> | ||
| 110 | + <div> | ||
| 111 | + <p class="page-eyebrow">流程编辑器重构迁移</p> | ||
| 112 | + <h1>新项目基线页</h1> | ||
| 113 | + </div> | ||
| 114 | + <div class="version-card"> | ||
| 115 | + <span>当前启用版本</span> | ||
| 116 | + <strong>{{ currentVersionLabel }}</strong> | ||
| 117 | + </div> | ||
| 118 | + </header> | ||
| 119 | + | ||
| 120 | + <main class="page-main"> | ||
| 121 | + <FlowEditor | ||
| 122 | + :data="flowData" | ||
| 123 | + :grid="false" | ||
| 124 | + :mini-map="false" | ||
| 125 | + :on-ref="onRef" | ||
| 126 | + :toolbar-button-handler="toolbarButtonHandler" | ||
| 127 | + > | ||
| 128 | + <template #toolbar-extra> | ||
| 129 | + <button | ||
| 130 | + type="button" | ||
| 131 | + class="toolbar-button toolbar-button--accent" | ||
| 132 | + @click="centerCanvas" | ||
| 133 | + > | ||
| 134 | + 居中 | ||
| 135 | + </button> | ||
| 136 | + <button | ||
| 137 | + type="button" | ||
| 138 | + class="toolbar-button toolbar-button--accent" | ||
| 139 | + @click="saveFlowDraft" | ||
| 140 | + > | ||
| 141 | + 保存 | ||
| 142 | + </button> | ||
| 143 | + <button | ||
| 144 | + type="button" | ||
| 145 | + class="toolbar-button toolbar-button--accent" | ||
| 146 | + data-testid="open-preview-panel" | ||
| 147 | + @click="openPreviewPanel" | ||
| 148 | + > | ||
| 149 | + 预览测试 | ||
| 150 | + </button> | ||
| 151 | + </template> | ||
| 152 | + </FlowEditor> | ||
| 153 | + | ||
| 154 | + <aside class="status-panel"> | ||
| 155 | + <h2>迁移状态</h2> | ||
| 156 | + <p>当前目标:先建立新项目最小壳层与测试护栏,再逐步补齐 LogicFlow 实现。</p> | ||
| 157 | + <p>接口基线:{{ hasLoadedRemoteBootstrap ? '已接入初始化 mock 语义' : '使用本地兜底空图数据' }}</p> | ||
| 158 | + <p>最近动作:{{ latestAction }}</p> | ||
| 159 | + </aside> | ||
| 160 | + </main> | ||
| 161 | + </div> | ||
| 162 | +</template> | ||
| 163 | + | ||
| 164 | +<style scoped> | ||
| 165 | +.app { | ||
| 166 | + min-height: 100vh; | ||
| 167 | + padding: 24px; | ||
| 168 | + background: | ||
| 169 | + radial-gradient(circle at top left, rgba(43, 104, 168, 0.14), transparent 32%), | ||
| 170 | + linear-gradient(180deg, #f5f7fb 0%, #eef2f8 100%); | ||
| 171 | + color: #18314f; | ||
| 172 | + font-family: "PingFang SC", "Microsoft YaHei", sans-serif; | ||
| 173 | +} | ||
| 174 | + | ||
| 175 | +.page-header { | ||
| 176 | + display: flex; | ||
| 177 | + align-items: flex-start; | ||
| 178 | + justify-content: space-between; | ||
| 179 | + gap: 16px; | ||
| 180 | + margin-bottom: 20px; | ||
| 181 | +} | ||
| 182 | + | ||
| 183 | +.page-header h1 { | ||
| 184 | + margin: 6px 0 0; | ||
| 185 | + font-size: 28px; | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +.page-eyebrow { | ||
| 189 | + margin: 0; | ||
| 190 | + font-size: 12px; | ||
| 191 | + letter-spacing: 0.14em; | ||
| 192 | + color: #52749b; | ||
| 193 | +} | ||
| 194 | + | ||
| 195 | +.version-card { | ||
| 196 | + min-width: 180px; | ||
| 197 | + padding: 14px 16px; | ||
| 198 | + border: 1px solid rgba(24, 49, 79, 0.1); | ||
| 199 | + border-radius: 16px; | ||
| 200 | + background: rgba(255, 255, 255, 0.9); | ||
| 201 | + box-shadow: 0 18px 40px rgba(34, 61, 96, 0.08); | ||
| 202 | +} | ||
| 203 | + | ||
| 204 | +.version-card span { | ||
| 205 | + display: block; | ||
| 206 | + margin-bottom: 6px; | ||
| 207 | + font-size: 12px; | ||
| 208 | + color: #52749b; | ||
| 209 | +} | ||
| 210 | + | ||
| 211 | +.version-card strong { | ||
| 212 | + font-size: 18px; | ||
| 213 | +} | ||
| 214 | + | ||
| 215 | +.page-main { | ||
| 216 | + display: grid; | ||
| 217 | + grid-template-columns: minmax(0, 1fr) 320px; | ||
| 218 | + gap: 20px; | ||
| 219 | +} | ||
| 220 | + | ||
| 221 | +.status-panel { | ||
| 222 | + padding: 18px; | ||
| 223 | + border: 1px solid rgba(24, 49, 79, 0.08); | ||
| 224 | + border-radius: 20px; | ||
| 225 | + background: rgba(255, 255, 255, 0.88); | ||
| 226 | + box-shadow: 0 18px 32px rgba(34, 61, 96, 0.08); | ||
| 227 | +} | ||
| 228 | + | ||
| 229 | +.status-panel h2 { | ||
| 230 | + margin: 0 0 12px; | ||
| 231 | + font-size: 18px; | ||
| 232 | +} | ||
| 233 | + | ||
| 234 | +.status-panel p { | ||
| 235 | + margin: 0 0 10px; | ||
| 236 | + line-height: 1.6; | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | +.toolbar-button { | ||
| 240 | + border: none; | ||
| 241 | + border-radius: 999px; | ||
| 242 | + background: #ffffff; | ||
| 243 | + color: #18314f; | ||
| 244 | +} | ||
| 245 | + | ||
| 246 | +.toolbar-button--accent { | ||
| 247 | + background: linear-gradient(135deg, #2b68a8 0%, #3f8ec6 100%); | ||
| 248 | + color: #ffffff; | ||
| 249 | +} | ||
| 250 | + | ||
| 251 | +@media (max-width: 960px) { | ||
| 252 | + .page-main { | ||
| 253 | + grid-template-columns: 1fr; | ||
| 254 | + } | ||
| 255 | + | ||
| 256 | + .page-header { | ||
| 257 | + flex-direction: column; | ||
| 258 | + } | ||
| 259 | +} | ||
| 260 | +</style> |
apps/demo/src/main.ts
0 → 100644
| 1 | +import { createApp } from 'vue' | ||
| 2 | +import ElementPlus from 'element-plus' | ||
| 3 | +import 'element-plus/dist/index.css' | ||
| 4 | +import '@logicflow/core/lib/style/index.css' | ||
| 5 | +import '@logicflow/extension/lib/style/index.css' | ||
| 6 | +import App from './App.vue' | ||
| 7 | + | ||
| 8 | +createApp(App).use(ElementPlus).mount('#app') |
| 1 | +# 流程编辑器迁移实施计划 | ||
| 2 | + | ||
| 3 | +> **执行说明:** 后续真正进入实施阶段时,建议逐任务执行,并在每个任务完成后立即补充验证结果与文档记录。 | ||
| 4 | + | ||
| 5 | +**目标:** 先在旧项目建立自动化行为基线,再在新项目中以 Vue 3、Element Plus、LogicFlow 2.0、Vite 逐步重建流程编辑器,并在每一阶段保持可验证、可回退。 | ||
| 6 | + | ||
| 7 | +**架构方式:** 采用“双仓协同 + 测试先行”的迁移策略。旧仓库负责定义真实业务行为基线,新仓库负责按兼容契约逐层实现替代能力,测试、夹具、操作脚本尽量复用同一套语义。 | ||
| 8 | + | ||
| 9 | +**技术栈:** Vue 3、TypeScript、Vite、Element Plus、LogicFlow 2.0、Vitest、Vue Test Utils、Playwright | ||
| 10 | + | ||
| 11 | +--- | ||
| 12 | + | ||
| 13 | +## 执行原则 | ||
| 14 | + | ||
| 15 | +- 先迁测试,再迁功能。 | ||
| 16 | +- 先保护旧行为,再替换底层引擎。 | ||
| 17 | +- 每个阶段只处理一个主要风险面。 | ||
| 18 | +- 每个任务都必须带验证动作。 | ||
| 19 | +- 每次提交只包含一个清晰目标,避免大批量混合改动。 | ||
| 20 | + | ||
| 21 | +## 目录规划 | ||
| 22 | + | ||
| 23 | +### 旧项目建议新增目录 | ||
| 24 | + | ||
| 25 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e` | ||
| 26 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/fixtures/graphs` | ||
| 27 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/fixtures/operations` | ||
| 28 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/helpers` | ||
| 29 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor/playwright.config.ts` | ||
| 30 | + | ||
| 31 | +### 新项目建议初始目录 | ||
| 32 | + | ||
| 33 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor2/apps/demo` | ||
| 34 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor` | ||
| 35 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/e2e` | ||
| 36 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/fixtures/graphs` | ||
| 37 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/fixtures/operations` | ||
| 38 | +- `/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/unit` | ||
| 39 | + | ||
| 40 | +## 任务 1:冻结旧项目基线样板页 | ||
| 41 | + | ||
| 42 | +**目标:** 把旧项目 `doc/index.vue` 固定为首个官方回归入口,避免后续基线页面持续漂移。 | ||
| 43 | + | ||
| 44 | +**涉及文件:** | ||
| 45 | +- 修改:`/Users/huyirui/program/itomix/git/vue-flow-editor/doc/index.vue` | ||
| 46 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/doc/README-基线回归页.md` | ||
| 47 | + | ||
| 48 | +**步骤:** | ||
| 49 | +1. 检查 `doc/index.vue` 当前依赖的数据、事件、插槽和演示能力。 | ||
| 50 | +2. 标记哪些区域属于回归必测区,哪些属于展示性区块。 | ||
| 51 | +3. 在基线说明文档中写清楚启动方式、演示入口、核心交互列表。 | ||
| 52 | +4. 尽量减少基线页中的随机性数据和不稳定异步行为。 | ||
| 53 | +5. 记录该页面暴露的 `onRef` 方法、事件和业务钩子。 | ||
| 54 | + | ||
| 55 | +**验证:** | ||
| 56 | +- 能稳定打开旧项目示例页。 | ||
| 57 | +- 页面至少包含节点渲染、工具栏、右侧面板、节点编辑、删除、预览能力。 | ||
| 58 | + | ||
| 59 | +**完成标准:** | ||
| 60 | +- 任何人都能根据文档找到旧项目基线回归入口。 | ||
| 61 | + | ||
| 62 | +## 任务 2:在旧项目接入 Playwright 基础设施 | ||
| 63 | + | ||
| 64 | +**目标:** 让旧项目具备最小可运行的端到端测试能力。 | ||
| 65 | + | ||
| 66 | +**涉及文件:** | ||
| 67 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/package.json` 中测试脚本 | ||
| 68 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/playwright.config.ts` | ||
| 69 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/smoke.spec.ts` | ||
| 70 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/helpers/editor-page.ts` | ||
| 71 | + | ||
| 72 | +**步骤:** | ||
| 73 | +1. 安装并配置 Playwright。 | ||
| 74 | +2. 配置旧项目本地启动命令与 `baseURL`。 | ||
| 75 | +3. 编写最小冒烟测试,只验证页面能正常加载、画布存在、工具栏存在。 | ||
| 76 | +4. 抽一层页面对象,避免测试直接依赖过深 DOM 结构。 | ||
| 77 | +5. 先确保测试可以本地稳定运行。 | ||
| 78 | + | ||
| 79 | +**验证:** | ||
| 80 | +- 执行 Playwright 冒烟用例能够通过。 | ||
| 81 | +- 启动失败时能够明确定位是服务问题还是测试问题。 | ||
| 82 | + | ||
| 83 | +**完成标准:** | ||
| 84 | +- 旧项目具备第一条可重复执行的自动化用例。 | ||
| 85 | + | ||
| 86 | +## 任务 3:沉淀第一批图数据夹具 | ||
| 87 | + | ||
| 88 | +**目标:** 把旧项目中的典型流程图数据整理成稳定的测试夹具。 | ||
| 89 | + | ||
| 90 | +**涉及文件:** | ||
| 91 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/fixtures/graphs/base-single-node.json` | ||
| 92 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/fixtures/graphs/linear-approval-flow.json` | ||
| 93 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/fixtures/graphs/branching-flow.json` | ||
| 94 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/fixtures/README.md` | ||
| 95 | + | ||
| 96 | +**步骤:** | ||
| 97 | +1. 从旧项目示例页提取最小单节点数据。 | ||
| 98 | +2. 提取线性审批流数据。 | ||
| 99 | +3. 提取包含分支的流程图数据。 | ||
| 100 | +4. 去掉无关噪音字段,保留渲染与行为必需字段。 | ||
| 101 | +5. 为每份夹具补充用途说明。 | ||
| 102 | + | ||
| 103 | +**验证:** | ||
| 104 | +- 每份夹具都能在旧项目中正常读入。 | ||
| 105 | +- 节点数、边数、关键标签与预期一致。 | ||
| 106 | + | ||
| 107 | +**完成标准:** | ||
| 108 | +- 至少三份高价值夹具可在自动化测试中复用。 | ||
| 109 | + | ||
| 110 | +## 任务 4:补齐旧项目核心行为回归脚本 | ||
| 111 | + | ||
| 112 | +**目标:** 先把最关键、最容易回归出问题的交互变成自动化脚本。 | ||
| 113 | + | ||
| 114 | +**涉及文件:** | ||
| 115 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/toolbar.spec.ts` | ||
| 116 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/selection.spec.ts` | ||
| 117 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/delete-undo-redo.spec.ts` | ||
| 118 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/node-edge-create.spec.ts` | ||
| 119 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/panel-preview.spec.ts` | ||
| 120 | + | ||
| 121 | +**步骤:** | ||
| 122 | +1. 先覆盖工具栏:网格、缩略图、缩放、适应画布、实际尺寸。 | ||
| 123 | +2. 覆盖选择行为:单选、多选、框选、全选。 | ||
| 124 | +3. 覆盖删除、撤销、重做。 | ||
| 125 | +4. 覆盖新增节点、拖拽连线。 | ||
| 126 | +5. 覆盖双击节点打开右侧面板、预览弹窗打开。 | ||
| 127 | + | ||
| 128 | +**验证:** | ||
| 129 | +- 每条用例都能输出清晰断言结果。 | ||
| 130 | +- 测试失败时能快速定位到是工具栏、选择、命令还是新增链路。 | ||
| 131 | + | ||
| 132 | +**完成标准:** | ||
| 133 | +- 旧项目主路径至少有 8 到 12 条自动化场景。 | ||
| 134 | + | ||
| 135 | +## 任务 5:补齐旧项目对外契约断言 | ||
| 136 | + | ||
| 137 | +**目标:** 把 `onRef`、事件和 hooks 这些宿主契约也纳入自动化保护。 | ||
| 138 | + | ||
| 139 | +**涉及文件:** | ||
| 140 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/contract.spec.ts` | ||
| 141 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor/tests/helpers/contract-probe.ts` | ||
| 142 | + | ||
| 143 | +**步骤:** | ||
| 144 | +1. 在基线页中增加仅供测试读取的探针输出。 | ||
| 145 | +2. 记录 `onRef` 是否暴露出预期方法。 | ||
| 146 | +3. 记录 `click-node`、`select-change` 等事件是否触发。 | ||
| 147 | +4. 记录 `beforeAdd/afterAdd`、`beforeDelete/afterDelete` 的触发顺序。 | ||
| 148 | +5. 对外契约断言不要依赖具体引擎内部对象结构。 | ||
| 149 | + | ||
| 150 | +**验证:** | ||
| 151 | +- 自动化用例能稳定拿到对外契约状态。 | ||
| 152 | +- hook 的触发次数和顺序可被断言。 | ||
| 153 | + | ||
| 154 | +**完成标准:** | ||
| 155 | +- “宿主接入不变”被转成测试可验证事实。 | ||
| 156 | + | ||
| 157 | +## 任务 6:初始化新项目工程骨架 | ||
| 158 | + | ||
| 159 | +**目标:** 在新仓库建立标准工程结构,但先不急于实现完整编辑能力。 | ||
| 160 | + | ||
| 161 | +**涉及文件:** | ||
| 162 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/package.json` | ||
| 163 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/pnpm-workspace.yaml` | ||
| 164 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tsconfig.json` | ||
| 165 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/vite.config.ts` | ||
| 166 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/apps/demo/*` | ||
| 167 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/*` | ||
| 168 | + | ||
| 169 | +**步骤:** | ||
| 170 | +1. 初始化 Vite + Vue 3 + TypeScript 工程。 | ||
| 171 | +2. 接入 Element Plus。 | ||
| 172 | +3. 接入 LogicFlow 2.0。 | ||
| 173 | +4. 建立 `apps/demo` 和 `packages/flow-editor` 双层结构。 | ||
| 174 | +5. 让示例页能成功启动并展示一个最小占位编辑器。 | ||
| 175 | + | ||
| 176 | +**验证:** | ||
| 177 | +- `dev` 启动成功。 | ||
| 178 | +- 页面能正常渲染占位编辑器壳层。 | ||
| 179 | + | ||
| 180 | +**完成标准:** | ||
| 181 | +- 新项目具备最小可运行骨架。 | ||
| 182 | + | ||
| 183 | +## 任务 7:把旧项目测试资产迁入新项目 | ||
| 184 | + | ||
| 185 | +**目标:** 让新项目从一开始就共享旧项目的测试语义与夹具。 | ||
| 186 | + | ||
| 187 | +**涉及文件:** | ||
| 188 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/fixtures/graphs/*` | ||
| 189 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/fixtures/operations/*` | ||
| 190 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/playwright.config.ts` | ||
| 191 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/e2e/smoke.spec.ts` | ||
| 192 | + | ||
| 193 | +**步骤:** | ||
| 194 | +1. 迁入旧项目图数据夹具。 | ||
| 195 | +2. 保持文件命名和语义一致。 | ||
| 196 | +3. 在新项目创建对应的 Playwright 配置。 | ||
| 197 | +4. 先落一条冒烟测试,验证新项目示例页可打开。 | ||
| 198 | +5. 为后续复用行为脚本预留页面对象封装。 | ||
| 199 | + | ||
| 200 | +**验证:** | ||
| 201 | +- 新项目能运行第一条端到端测试。 | ||
| 202 | +- 夹具目录结构与旧项目保持一致。 | ||
| 203 | + | ||
| 204 | +**完成标准:** | ||
| 205 | +- 新项目测试底座与旧项目开始对齐。 | ||
| 206 | + | ||
| 207 | +## 任务 8:实现数据适配层 | ||
| 208 | + | ||
| 209 | +**目标:** 先让旧数据可以在新项目中被读入,而不是一开始就实现全部交互。 | ||
| 210 | + | ||
| 211 | +**涉及文件:** | ||
| 212 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/core/adapter/g6-to-logicflow.ts` | ||
| 213 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/core/schema/types.ts` | ||
| 214 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/unit/g6-to-logicflow.spec.ts` | ||
| 215 | + | ||
| 216 | +**步骤:** | ||
| 217 | +1. 定义旧图数据和新图数据的目标类型。 | ||
| 218 | +2. 先完成节点映射。 | ||
| 219 | +3. 再完成边映射。 | ||
| 220 | +4. 保留关键业务字段和节点文案。 | ||
| 221 | +5. 为异常输入写保护逻辑。 | ||
| 222 | + | ||
| 223 | +**验证:** | ||
| 224 | +- 单元测试覆盖单节点、线性流、分支流三种夹具。 | ||
| 225 | +- 新项目示例页可以静态渲染夹具。 | ||
| 226 | + | ||
| 227 | +**完成标准:** | ||
| 228 | +- 旧图数据在新项目可读、可画、可断言。 | ||
| 229 | + | ||
| 230 | +## 任务 9:实现兼容层与编辑器壳层 | ||
| 231 | + | ||
| 232 | +**目标:** 先恢复宿主侧最容易感知的对外接口,而不是马上深入复杂交互。 | ||
| 233 | + | ||
| 234 | +**涉及文件:** | ||
| 235 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/components/FlowEditor.vue` | ||
| 236 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/compat/props.ts` | ||
| 237 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/compat/events.ts` | ||
| 238 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/compat/ref-api.ts` | ||
| 239 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/unit/compat-contract.spec.ts` | ||
| 240 | + | ||
| 241 | +**步骤:** | ||
| 242 | +1. 先定义兼容 props。 | ||
| 243 | +2. 再定义兼容事件映射。 | ||
| 244 | +3. 实现 `onRef` 暴露对象的基础结构。 | ||
| 245 | +4. 先接通只读渲染链路。 | ||
| 246 | +5. 在示例页中以旧项目调用方式接入新组件。 | ||
| 247 | + | ||
| 248 | +**验证:** | ||
| 249 | +- 单元测试可验证 props 和 `onRef` 基本结构。 | ||
| 250 | +- 示例页能用旧项目风格参数启动新编辑器。 | ||
| 251 | + | ||
| 252 | +**完成标准:** | ||
| 253 | +- 宿主接入层具备最小兼容能力。 | ||
| 254 | + | ||
| 255 | +## 任务 10:实现命令系统第一批能力 | ||
| 256 | + | ||
| 257 | +**目标:** 先恢复最基础但最高频的命令语义。 | ||
| 258 | + | ||
| 259 | +**涉及文件:** | ||
| 260 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/core/commands/commander.ts` | ||
| 261 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/core/commands/use-editor-commands.ts` | ||
| 262 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/unit/commander.spec.ts` | ||
| 263 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/e2e/toolbar.spec.ts` | ||
| 264 | + | ||
| 265 | +**步骤:** | ||
| 266 | +1. 先实现命令注册、执行、撤销、重做骨架。 | ||
| 267 | +2. 实现缩放、适应画布、实际尺寸。 | ||
| 268 | +3. 接入工具栏按钮。 | ||
| 269 | +4. 把命令状态映射到按钮禁用态。 | ||
| 270 | +5. 补充最小 E2E 验证。 | ||
| 271 | + | ||
| 272 | +**验证:** | ||
| 273 | +- 单元测试覆盖命令队列行为。 | ||
| 274 | +- 工具栏端到端测试至少覆盖缩放和视图控制。 | ||
| 275 | + | ||
| 276 | +**完成标准:** | ||
| 277 | +- 新项目已恢复第一批可感知命令能力。 | ||
| 278 | + | ||
| 279 | +## 任务 11:实现选择、删除、撤销/重做链路 | ||
| 280 | + | ||
| 281 | +**目标:** 打通最关键的编辑闭环。 | ||
| 282 | + | ||
| 283 | +**涉及文件:** | ||
| 284 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/features/selection/*` | ||
| 285 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/features/node/delete-node.ts` | ||
| 286 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/unit/delete-command.spec.ts` | ||
| 287 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/e2e/delete-undo-redo.spec.ts` | ||
| 288 | + | ||
| 289 | +**步骤:** | ||
| 290 | +1. 先实现单选。 | ||
| 291 | +2. 再实现多选和框选。 | ||
| 292 | +3. 实现删除逻辑与 hooks 接入。 | ||
| 293 | +4. 接入撤销/重做回放。 | ||
| 294 | +5. 补齐端到端断言。 | ||
| 295 | + | ||
| 296 | +**验证:** | ||
| 297 | +- 能删除节点或边。 | ||
| 298 | +- 删除前后 hooks 正常触发。 | ||
| 299 | +- 撤销/重做可恢复前一状态。 | ||
| 300 | + | ||
| 301 | +**完成标准:** | ||
| 302 | +- 新项目具备“选中 -> 删除 -> 撤销 -> 重做”完整闭环。 | ||
| 303 | + | ||
| 304 | +## 任务 12:实现新增节点、连线与节点编辑 | ||
| 305 | + | ||
| 306 | +**目标:** 恢复完整编辑链路。 | ||
| 307 | + | ||
| 308 | +**涉及文件:** | ||
| 309 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/features/node/add-node.ts` | ||
| 310 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/features/edge/add-edge.ts` | ||
| 311 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/features/node/update-node.ts` | ||
| 312 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/e2e/node-edge-create.spec.ts` | ||
| 313 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/e2e/panel-preview.spec.ts` | ||
| 314 | + | ||
| 315 | +**步骤:** | ||
| 316 | +1. 接入左侧菜单创建节点。 | ||
| 317 | +2. 实现锚点或等价交互的新建连线。 | ||
| 318 | +3. 接通 `beforeAdd/afterAdd`。 | ||
| 319 | +4. 接通双击节点打开右侧面板。 | ||
| 320 | +5. 接通 `updateModel`、`read`、`clearStates`。 | ||
| 321 | + | ||
| 322 | +**验证:** | ||
| 323 | +- 节点可新增、连线可新增、节点内容可更新。 | ||
| 324 | +- 预览弹窗可以打开。 | ||
| 325 | +- 基础编辑链路端到端通过。 | ||
| 326 | + | ||
| 327 | +**完成标准:** | ||
| 328 | +- 新项目已恢复旧编辑器的核心业务闭环。 | ||
| 329 | + | ||
| 330 | +## 任务 13:迁移旧示例页到新项目 | ||
| 331 | + | ||
| 332 | +**目标:** 用真实业务接入方式验证新编辑器,而不是只在简化 demo 里通过。 | ||
| 333 | + | ||
| 334 | +**涉及文件:** | ||
| 335 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/apps/demo/src/pages/legacy-compatible-demo.vue` | ||
| 336 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/e2e/legacy-compatible-demo.spec.ts` | ||
| 337 | + | ||
| 338 | +**步骤:** | ||
| 339 | +1. 把旧项目接入方式按兼容层迁到新示例页。 | ||
| 340 | +2. 先保留高频业务面板和工具栏交互。 | ||
| 341 | +3. 对差异点逐条记录。 | ||
| 342 | +4. 补充真实接入回归脚本。 | ||
| 343 | +5. 确认宿主使用方式无需大改或只需极小改动。 | ||
| 344 | + | ||
| 345 | +**验证:** | ||
| 346 | +- 新示例页能跑主要旧用法。 | ||
| 347 | +- 差异点有文档可追踪。 | ||
| 348 | + | ||
| 349 | +**完成标准:** | ||
| 350 | +- 新项目具备可替代旧项目示例页的能力。 | ||
| 351 | + | ||
| 352 | +## 任务 14:收口文档、差异和发布策略 | ||
| 353 | + | ||
| 354 | +**目标:** 确保迁移不是“能跑就算完”,而是有明确交付口径。 | ||
| 355 | + | ||
| 356 | +**涉及文件:** | ||
| 357 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/docs/migration/compatibility-matrix.md` | ||
| 358 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/docs/migration/open-issues.md` | ||
| 359 | +- 新增:`/Users/huyirui/program/itomix/git/vue-flow-editor2/docs/migration/release-checklist.md` | ||
| 360 | + | ||
| 361 | +**步骤:** | ||
| 362 | +1. 列出已兼容能力与未兼容能力矩阵。 | ||
| 363 | +2. 记录已知风险和残留问题。 | ||
| 364 | +3. 写清楚发布前检查项。 | ||
| 365 | +4. 写清楚回滚方式。 | ||
| 366 | +5. 与自动化结果一起形成交付材料。 | ||
| 367 | + | ||
| 368 | +**验证:** | ||
| 369 | +- 文档能够支撑最终切换决策。 | ||
| 370 | +- 任何已知差异都有记录。 | ||
| 371 | + | ||
| 372 | +**完成标准:** | ||
| 373 | +- 项目进入可灰度替换旧编辑器的状态。 | ||
| 374 | + | ||
| 375 | +## 推荐提交节奏 | ||
| 376 | + | ||
| 377 | +- `docs: add migration implementation plan` | ||
| 378 | +- `test: add legacy editor playwright smoke tests` | ||
| 379 | +- `test: add legacy editor regression fixtures` | ||
| 380 | +- `chore: scaffold new flow editor workspace` | ||
| 381 | +- `feat: add logicflow data adapter` | ||
| 382 | +- `feat: add compatibility shell` | ||
| 383 | +- `feat: add commander foundation` | ||
| 384 | +- `feat: add selection and delete flow` | ||
| 385 | +- `feat: add node and edge creation flow` | ||
| 386 | + | ||
| 387 | +## 每阶段强制检查项 | ||
| 388 | + | ||
| 389 | +- 是否新增或更新了自动化测试 | ||
| 390 | +- 是否更新了中文文档 | ||
| 391 | +- 页面主要逻辑是否补充了中文注释 | ||
| 392 | +- 是否记录了兼容差异 | ||
| 393 | +- 是否能够明确回退 | ||
| 394 | + | ||
| 395 | +## 下一步建议 | ||
| 396 | + | ||
| 397 | +最稳妥的下一步不是直接创建新项目代码,而是先执行任务 1 到任务 5,把旧项目行为基线完全立起来。这样一旦进入新项目实现阶段,我们就不会失去“到底哪里变坏了”的判断依据。 |
| 1 | +# Flow Editor 重构需求与安全迁移方案 | ||
| 2 | + | ||
| 3 | +## 1. 背景 | ||
| 4 | + | ||
| 5 | +现有项目 [`vue-flow-editor`](/Users/huyirui/program/itomix/git/vue-flow-editor) 已经长期稳定承载业务,但底层技术栈混杂、构建链老旧,继续在原基座上升级的风险越来越高。当前代码同时存在以下问题: | ||
| 6 | + | ||
| 7 | +- 图编辑内核基于 G6 3.x 时代的能力与自定义 behavior/shape 体系。 | ||
| 8 | +- 工程基座仍是 Vue CLI 风格,难以承接后续现代化构建与测试。 | ||
| 9 | +- UI 侧存在 `element-ui` 与 `element-plus` 混用痕迹。 | ||
| 10 | +- 编辑器能力并不只是“画图”,还包含命令系统、插槽扩展、右侧属性面板、预览、业务节点配置等完整交互闭环。 | ||
| 11 | + | ||
| 12 | +因此本项目不是“升级依赖”,而是一次有严格兼容目标的重构:在新仓库 [`vue-flow-editor2`](/Users/huyirui/program/itomix/git/vue-flow-editor2) 中,以 Vue 3 + Element Plus + LogicFlow 2.0 + Vite 重建流程编辑器,并通过自动化测试最大限度保住现有业务行为。 | ||
| 13 | + | ||
| 14 | +## 2. 项目目标 | ||
| 15 | + | ||
| 16 | +### 2.1 核心目标 | ||
| 17 | + | ||
| 18 | +- 新建一个可持续维护的流程图编辑器项目,技术栈统一为 Vue 3、Element Plus、LogicFlow 2.0、Vite。 | ||
| 19 | +- 对老项目关键业务行为保持一致,优先保证“用户怎么操作、系统怎么响应”不变。 | ||
| 20 | +- 在迁移前期先建立自动化回归护栏,再开始大规模功能迁移。 | ||
| 21 | +- 新项目在任一阶段都应可运行、可验证,而不是等到全部完成后再一起联调。 | ||
| 22 | + | ||
| 23 | +### 2.2 非目标 | ||
| 24 | + | ||
| 25 | +- 不追求源码结构 1:1 复刻。 | ||
| 26 | +- 不追求一次性把所有业务定制表单都重写完再测试。 | ||
| 27 | +- 不把这次重构变成 UI 风格大改版项目。 | ||
| 28 | +- 不在没有测试护栏的情况下直接全量替换老编辑器。 | ||
| 29 | + | ||
| 30 | +## 3. 兼容范围定义 | ||
| 31 | + | ||
| 32 | +本次迁移应以“行为兼容”作为验收标准,至少覆盖以下维度。 | ||
| 33 | + | ||
| 34 | +### 3.1 对外 Props 能力 | ||
| 35 | + | ||
| 36 | +老项目当前已暴露的重要输入包括: | ||
| 37 | + | ||
| 38 | +- `data` | ||
| 39 | +- `grid` | ||
| 40 | +- `miniMap` | ||
| 41 | +- `disabledDragEdge` | ||
| 42 | +- `disabledUndo` | ||
| 43 | +- `height` | ||
| 44 | +- `toolbarHeight` | ||
| 45 | +- `menuWidth` | ||
| 46 | +- `modelWidth` | ||
| 47 | +- `onRef` | ||
| 48 | +- `toolbarButtonHandler` | ||
| 49 | +- `loading` | ||
| 50 | +- `multipleSelect` | ||
| 51 | +- `beforeDelete` | ||
| 52 | +- `afterDelete` | ||
| 53 | +- `beforeAdd` | ||
| 54 | +- `afterAdd` | ||
| 55 | +- `activityConfig` | ||
| 56 | +- `controlConfig` | ||
| 57 | + | ||
| 58 | +### 3.2 对外事件 | ||
| 59 | + | ||
| 60 | +老项目已对宿主页面透出的关键事件包括: | ||
| 61 | + | ||
| 62 | +- `click-canvas` | ||
| 63 | +- `drag-canvas` | ||
| 64 | +- `click-node-mousedown` | ||
| 65 | +- `click-node` | ||
| 66 | +- `dblclick-node` | ||
| 67 | +- `dragend-node` | ||
| 68 | +- `click-edge` | ||
| 69 | +- `dblclick-edge` | ||
| 70 | +- `select-change` | ||
| 71 | + | ||
| 72 | +### 3.3 对外实例 API | ||
| 73 | + | ||
| 74 | +通过 `onRef` 暴露的能力至少包括: | ||
| 75 | + | ||
| 76 | +- `editorState` | ||
| 77 | +- `commander` | ||
| 78 | +- `openModel` | ||
| 79 | +- `closeModel` | ||
| 80 | +- `addNode` | ||
| 81 | +- `updateModel` | ||
| 82 | +- `openPreview` | ||
| 83 | +- `read` | ||
| 84 | +- `clearStates` | ||
| 85 | + | ||
| 86 | +### 3.4 用户操作能力 | ||
| 87 | + | ||
| 88 | +根据 README 与现有实现,必须优先保留的用户操作有: | ||
| 89 | + | ||
| 90 | +- 工具栏控制:网格、缩略图、适应画布、实际尺寸、放大、缩小、删除、预览、撤销、重做 | ||
| 91 | +- 画布选择:单选、多选、框选、全选 | ||
| 92 | +- 节点操作:拖拽添加、拖拽移动、双击编辑、删除 | ||
| 93 | +- 连线操作:拖拽创建边、边选择、边删除 | ||
| 94 | +- 右侧编辑面板:宿主自定义表单内容展示与关闭/打开 | ||
| 95 | +- 预览能力:按当前图数据输出预览 | ||
| 96 | + | ||
| 97 | +## 4. 对旧项目的审计结论 | ||
| 98 | + | ||
| 99 | +目前可以明确的结论如下: | ||
| 100 | + | ||
| 101 | +- 老项目是“组件库 + demo 文档页”的形式,不是单页应用直接 hardcode。 | ||
| 102 | +- 编辑器稳定性的核心不在样式,而在 `commander + graph event + before/after hook` 这一套行为层。 | ||
| 103 | +- `src/editor/editor.ts` 已经将关键操作抽成命令:`undo`、`redo`、`switchGrid`、`switchMiniMap`、`fitView`、`actualView`、`zoomIn`、`zoomOut`、`delete`、`drag`、`selectAll`、`addEdge`、`addNode`、`update`。 | ||
| 104 | +- `src/behavior` 负责交互行为编排,包括拖拽节点、点击选择、锚点悬浮、拖拽连线、框选、拖动画布。 | ||
| 105 | +- `doc/index.vue` 是一个高价值基线样板页,已经串起了 `onRef`、事件监听、增删前后钩子、菜单插槽、复杂右侧表单等真实用法,适合作为自动化基线回归页面。 | ||
| 106 | + | ||
| 107 | +结论:新项目最应该保护的是“命令语义 + 事件语义 + 宿主接入语义”,而不是老实现里的具体 G6 细节。 | ||
| 108 | + | ||
| 109 | +## 5. 可选迁移路径对比 | ||
| 110 | + | ||
| 111 | +### 方案 A:直接从老代码重写到新栈 | ||
| 112 | + | ||
| 113 | +做法: | ||
| 114 | + | ||
| 115 | +- 先搭新仓库。 | ||
| 116 | +- 直接按老功能清单一口气实现 LogicFlow 版本。 | ||
| 117 | +- 最后统一联调和回归。 | ||
| 118 | + | ||
| 119 | +优点: | ||
| 120 | + | ||
| 121 | +- 前期推进快,文档负担最轻。 | ||
| 122 | + | ||
| 123 | +缺点: | ||
| 124 | + | ||
| 125 | +- 风险最高。 | ||
| 126 | +- 等到最后才发现偏差时,返工成本极大。 | ||
| 127 | +- 无法证明“老功能没有丢”。 | ||
| 128 | + | ||
| 129 | +结论:不推荐。 | ||
| 130 | + | ||
| 131 | +### 方案 B:先搭新壳子,再边迁边手工点 | ||
| 132 | + | ||
| 133 | +做法: | ||
| 134 | + | ||
| 135 | +- 先搭 Vite + Vue3 + Element Plus + LogicFlow。 | ||
| 136 | +- 每迁一个能力,人工自己点一遍。 | ||
| 137 | + | ||
| 138 | +优点: | ||
| 139 | + | ||
| 140 | +- 进度感较强。 | ||
| 141 | + | ||
| 142 | +缺点: | ||
| 143 | + | ||
| 144 | +- 依赖人工回归,且用户明确没有办法做大量点击验证。 | ||
| 145 | +- 随着功能变多,测试深度必然衰减。 | ||
| 146 | + | ||
| 147 | +结论:不推荐。 | ||
| 148 | + | ||
| 149 | +### 方案 C:先抽行为基线测试,再分阶段替换内核 | ||
| 150 | + | ||
| 151 | +做法: | ||
| 152 | + | ||
| 153 | +- 先对老项目建立可复用的基线测试和 fixture。 | ||
| 154 | +- 再在新项目中按“数据层、命令层、交互层、业务层”分批迁移。 | ||
| 155 | +- 每迁完一层,就跑同一套或共享大部分逻辑的测试。 | ||
| 156 | + | ||
| 157 | +优点: | ||
| 158 | + | ||
| 159 | +- 风险最低。 | ||
| 160 | +- 可以做到“先迁测试,再迁功能”。 | ||
| 161 | +- 每一阶段都有明确的退出条件。 | ||
| 162 | + | ||
| 163 | +缺点: | ||
| 164 | + | ||
| 165 | +- 前期准备时间更多。 | ||
| 166 | + | ||
| 167 | +结论:推荐,且这是本项目最安全的路线。 | ||
| 168 | + | ||
| 169 | +## 6. 推荐总体策略 | ||
| 170 | + | ||
| 171 | +推荐采用“基线优先 + 分层迁移 + 双轨验证”的方式推进。 | ||
| 172 | + | ||
| 173 | +### 核心原则 | ||
| 174 | + | ||
| 175 | +- 先定义兼容契约,再写新实现。 | ||
| 176 | +- 先建立自动化回归样板,再迁移核心行为。 | ||
| 177 | +- 每一阶段只替换一个主要风险面。 | ||
| 178 | +- 数据结构适配优先于交互细节重写。 | ||
| 179 | +- 优先保护宿主可感知行为,而不是内部实现完全一致。 | ||
| 180 | + | ||
| 181 | +### 双轨验证 | ||
| 182 | + | ||
| 183 | +双轨验证指: | ||
| 184 | + | ||
| 185 | +- 轨道 1:老项目基线测试,负责定义“过去正确是什么”。 | ||
| 186 | +- 轨道 2:新项目迁移测试,负责验证“现在是否仍然正确”。 | ||
| 187 | + | ||
| 188 | +理想状态下,同一份 fixture、同一份操作脚本、同一份断言语义,分别跑在旧实现和新实现上,只允许底层引擎不同。 | ||
| 189 | + | ||
| 190 | +## 7. 目标架构建议 | ||
| 191 | + | ||
| 192 | +新项目建议仍然保留“编辑器组件库 + 示例宿主页”的结构,而不是只做一个业务页面。推荐分层如下: | ||
| 193 | + | ||
| 194 | +### 7.1 工程层 | ||
| 195 | + | ||
| 196 | +- `apps/demo` | ||
| 197 | + - 迁移自旧项目 `doc/index.vue` 的演示/基线页面 | ||
| 198 | +- `packages/flow-editor` | ||
| 199 | + - 真正的编辑器组件库 | ||
| 200 | +- `tests` | ||
| 201 | + - 跨项目共享的 fixture、回归脚本、视觉快照、契约测试 | ||
| 202 | + | ||
| 203 | +### 7.2 编辑器内部层次 | ||
| 204 | + | ||
| 205 | +- `core/adapter` | ||
| 206 | + - 旧数据结构与 LogicFlow 数据结构适配 | ||
| 207 | +- `core/commands` | ||
| 208 | + - 命令总线、撤销重做、行为封装 | ||
| 209 | +- `core/events` | ||
| 210 | + - 统一对外事件派发与兼容转换 | ||
| 211 | +- `core/schema` | ||
| 212 | + - 节点/边/锚点/属性 schema | ||
| 213 | +- `features/canvas` | ||
| 214 | + - 画布初始化、缩放、视图控制、grid、miniMap | ||
| 215 | +- `features/selection` | ||
| 216 | + - 单选、多选、框选、全选 | ||
| 217 | +- `features/edge` | ||
| 218 | + - 连线创建、校验、edge hook | ||
| 219 | +- `features/node` | ||
| 220 | + - 节点创建、更新、拖拽、删除 | ||
| 221 | +- `features/panel` | ||
| 222 | + - 右侧属性面板、预览面板、插槽封装 | ||
| 223 | +- `compat` | ||
| 224 | + - 老 props、老事件名、老 `onRef` API 兼容层 | ||
| 225 | + | ||
| 226 | +### 7.3 最重要的设计要求 | ||
| 227 | + | ||
| 228 | +新项目不要让宿主业务代码直接依赖 LogicFlow 实例。宿主继续依赖兼容层暴露出来的编辑器 API,这样后续底层再演进时成本最低。 | ||
| 229 | + | ||
| 230 | +## 8. 分阶段迁移计划 | ||
| 231 | + | ||
| 232 | +### 阶段 0:建立旧项目行为基线 | ||
| 233 | + | ||
| 234 | +目标: | ||
| 235 | + | ||
| 236 | +- 把老项目的“正确行为”变成自动化脚本和 fixture。 | ||
| 237 | + | ||
| 238 | +产出: | ||
| 239 | + | ||
| 240 | +- 老项目示例页可稳定启动 | ||
| 241 | +- 回归样例数据集 | ||
| 242 | +- 第一批 Playwright 场景 | ||
| 243 | +- 第一批截图快照 | ||
| 244 | +- 第一批命令层契约说明 | ||
| 245 | + | ||
| 246 | +优先测试场景: | ||
| 247 | + | ||
| 248 | +- 初始图渲染 | ||
| 249 | +- 工具栏按钮可见性与禁用状态 | ||
| 250 | +- 网格开关 | ||
| 251 | +- 缩略图开关 | ||
| 252 | +- 缩放、适应画布、实际尺寸 | ||
| 253 | +- 选择节点/边 | ||
| 254 | +- 多选与框选 | ||
| 255 | +- 删除节点/边 | ||
| 256 | +- 撤销/重做 | ||
| 257 | +- 双击节点打开编辑面板 | ||
| 258 | +- 拖拽添加节点 | ||
| 259 | +- 拖拽创建连线 | ||
| 260 | +- `beforeAdd/afterAdd` 与 `beforeDelete/afterDelete` 是否触发 | ||
| 261 | +- `onRef` 暴露方法是否可调用 | ||
| 262 | + | ||
| 263 | +退出条件: | ||
| 264 | + | ||
| 265 | +- 至少 70% 的核心主路径操作已有自动化覆盖 | ||
| 266 | +- 至少 3 套真实 fixture 图数据可重复运行 | ||
| 267 | +- 老项目测试结果可稳定复现 | ||
| 268 | + | ||
| 269 | +### 阶段 1:新项目基础架构与测试底座 | ||
| 270 | + | ||
| 271 | +目标: | ||
| 272 | + | ||
| 273 | +- 在新仓库搭出标准工程骨架与测试设施,但暂不急于追求全部功能。 | ||
| 274 | + | ||
| 275 | +产出: | ||
| 276 | + | ||
| 277 | +- Vite + Vue3 + TypeScript + Element Plus 基础工程 | ||
| 278 | +- LogicFlow 2.0 集成验证页 | ||
| 279 | +- Vitest + Vue Test Utils | ||
| 280 | +- Playwright E2E | ||
| 281 | +- 统一 fixture 目录 | ||
| 282 | +- 统一截图基线目录 | ||
| 283 | +- CI 执行脚本 | ||
| 284 | + | ||
| 285 | +退出条件: | ||
| 286 | + | ||
| 287 | +- 新项目示例页可启动 | ||
| 288 | +- 测试命令可在本地一键执行 | ||
| 289 | +- 至少有 1 个冒烟场景打通 | ||
| 290 | + | ||
| 291 | +### 阶段 2:数据兼容与静态渲染 | ||
| 292 | + | ||
| 293 | +目标: | ||
| 294 | + | ||
| 295 | +- 先让新项目能正确读出旧数据并画出来。 | ||
| 296 | + | ||
| 297 | +产出: | ||
| 298 | + | ||
| 299 | +- 旧 G6 数据到 LogicFlow 数据的适配器 | ||
| 300 | +- 节点类型、边类型、文本属性映射 | ||
| 301 | +- 静态渲染一致性检查 | ||
| 302 | + | ||
| 303 | +优先验证: | ||
| 304 | + | ||
| 305 | +- 读入老 fixture 不报错 | ||
| 306 | +- 节点数量、边数量一致 | ||
| 307 | +- 关键 label、坐标、类型、业务字段保留 | ||
| 308 | +- 预览数据导出格式可控 | ||
| 309 | + | ||
| 310 | +退出条件: | ||
| 311 | + | ||
| 312 | +- 旧 fixture 在新项目中可稳定渲染 | ||
| 313 | +- 核心数据往返读写可验证 | ||
| 314 | + | ||
| 315 | +### 阶段 3:命令系统与基础交互迁移 | ||
| 316 | + | ||
| 317 | +目标: | ||
| 318 | + | ||
| 319 | +- 优先重建最核心的命令语义和用户主操作。 | ||
| 320 | + | ||
| 321 | +产出: | ||
| 322 | + | ||
| 323 | +- 新版 `commander` | ||
| 324 | +- `undo/redo` | ||
| 325 | +- `zoomIn/zoomOut` | ||
| 326 | +- `fitView/actualView` | ||
| 327 | +- `delete` | ||
| 328 | +- `selectAll` | ||
| 329 | +- 节点拖拽移动 | ||
| 330 | +- 单选/多选/框选 | ||
| 331 | + | ||
| 332 | +验证重点: | ||
| 333 | + | ||
| 334 | +- 命令是否能回放和撤销 | ||
| 335 | +- 选择状态变化是否正确发出 `select-change` | ||
| 336 | +- 删除前后钩子是否触发且支持阻断 | ||
| 337 | + | ||
| 338 | +退出条件: | ||
| 339 | + | ||
| 340 | +- 主工具栏行为全部通过自动化测试 | ||
| 341 | +- 命令栈回退行为稳定 | ||
| 342 | + | ||
| 343 | +### 阶段 4:新增节点、连线与 hook 迁移 | ||
| 344 | + | ||
| 345 | +目标: | ||
| 346 | + | ||
| 347 | +- 打通完整编辑链路。 | ||
| 348 | + | ||
| 349 | +产出: | ||
| 350 | + | ||
| 351 | +- 左侧菜单拖入创建节点 | ||
| 352 | +- 锚点拖拽创建边 | ||
| 353 | +- `beforeAdd/afterAdd` | ||
| 354 | +- `updateModel` | ||
| 355 | +- `read` | ||
| 356 | +- `clearStates` | ||
| 357 | + | ||
| 358 | +验证重点: | ||
| 359 | + | ||
| 360 | +- 节点新增后数据正确 | ||
| 361 | +- 边新增后数据正确 | ||
| 362 | +- 新增失败或校验失败时状态可恢复 | ||
| 363 | +- 撤销/重做对新增和连线同样有效 | ||
| 364 | + | ||
| 365 | +退出条件: | ||
| 366 | + | ||
| 367 | +- “新建节点 -> 连线 -> 编辑 -> 删除 -> 撤销/重做”全链路通过 | ||
| 368 | + | ||
| 369 | +### 阶段 5:右侧表单、预览和宿主集成迁移 | ||
| 370 | + | ||
| 371 | +目标: | ||
| 372 | + | ||
| 373 | +- 迁移复杂业务接入能力,而不是只保留基础画图。 | ||
| 374 | + | ||
| 375 | +产出: | ||
| 376 | + | ||
| 377 | +- `model` 插槽 | ||
| 378 | +- `menu` 插槽 | ||
| 379 | +- `foot` 插槽 | ||
| 380 | +- 预览弹窗 | ||
| 381 | +- `onRef` 实例 API 兼容 | ||
| 382 | +- 示例页迁移版 | ||
| 383 | + | ||
| 384 | +验证重点: | ||
| 385 | + | ||
| 386 | +- 双击节点可打开对应业务表单 | ||
| 387 | +- 宿主可通过 `onRef` 调用实例方法 | ||
| 388 | +- 工具栏按钮可通过 `toolbarButtonHandler` 定制 | ||
| 389 | + | ||
| 390 | +退出条件: | ||
| 391 | + | ||
| 392 | +- 示例页具备替代旧文档页的主要能力 | ||
| 393 | + | ||
| 394 | +### 阶段 6:切换与收尾 | ||
| 395 | + | ||
| 396 | +目标: | ||
| 397 | + | ||
| 398 | +- 从“功能可用”进入“可替换旧项目”的状态。 | ||
| 399 | + | ||
| 400 | +产出: | ||
| 401 | + | ||
| 402 | +- 迁移文档 | ||
| 403 | +- 差异说明 | ||
| 404 | +- 已知不兼容点清单 | ||
| 405 | +- 发布与回滚方案 | ||
| 406 | + | ||
| 407 | +退出条件: | ||
| 408 | + | ||
| 409 | +- 核心回归用例通过 | ||
| 410 | +- 关键业务场景通过 | ||
| 411 | +- 已知差异可被接受或已补齐 | ||
| 412 | + | ||
| 413 | +## 9. 测试迁移策略 | ||
| 414 | + | ||
| 415 | +这是本项目最关键的部分。推荐将测试分成四层,而且必须先做前两层再做大规模迁移。 | ||
| 416 | + | ||
| 417 | +### 9.1 第一层:行为基线端到端测试 | ||
| 418 | + | ||
| 419 | +工具建议: | ||
| 420 | + | ||
| 421 | +- Playwright | ||
| 422 | + | ||
| 423 | +用途: | ||
| 424 | + | ||
| 425 | +- 在旧项目 demo 页上执行真实点击、拖拽、键盘操作 | ||
| 426 | +- 记录主路径行为 | ||
| 427 | +- 截图关键状态 | ||
| 428 | + | ||
| 429 | +为什么必须先做: | ||
| 430 | + | ||
| 431 | +- 用户无法手工重复大量点击验证 | ||
| 432 | +- 这是最接近真实使用方式的回归定义 | ||
| 433 | + | ||
| 434 | +### 9.2 第二层:共享 fixture + 结果断言 | ||
| 435 | + | ||
| 436 | +内容: | ||
| 437 | + | ||
| 438 | +- 建立 `fixtures/graphs/*.json` | ||
| 439 | +- 建立 `fixtures/operations/*.json` | ||
| 440 | +- 把“图数据 + 操作序列 + 预期结果”沉淀成通用规格 | ||
| 441 | + | ||
| 442 | +示例: | ||
| 443 | + | ||
| 444 | +- `base-approval-flow.json` | ||
| 445 | +- `multi-branch-flow.json` | ||
| 446 | +- `edge-validation-flow.json` | ||
| 447 | + | ||
| 448 | +预期断言: | ||
| 449 | + | ||
| 450 | +- 节点/边数量 | ||
| 451 | +- 当前选中项 | ||
| 452 | +- 缩放等级 | ||
| 453 | +- 钩子触发次数 | ||
| 454 | +- 导出图数据结构 | ||
| 455 | + | ||
| 456 | +意义: | ||
| 457 | + | ||
| 458 | +- 未来旧项目和新项目都能吃同一批数据 | ||
| 459 | + | ||
| 460 | +### 9.3 第三层:命令与适配器单元测试 | ||
| 461 | + | ||
| 462 | +工具建议: | ||
| 463 | + | ||
| 464 | +- Vitest | ||
| 465 | + | ||
| 466 | +范围: | ||
| 467 | + | ||
| 468 | +- `commander` | ||
| 469 | +- 数据适配器 | ||
| 470 | +- hook 包装器 | ||
| 471 | +- 事件兼容转换器 | ||
| 472 | + | ||
| 473 | +重点: | ||
| 474 | + | ||
| 475 | +- `undo/redo` 的队列行为 | ||
| 476 | +- `delete/add/update` 的前后状态恢复 | ||
| 477 | +- 旧数据结构到新结构的兼容转换 | ||
| 478 | + | ||
| 479 | +### 9.4 第四层:视觉与布局快照 | ||
| 480 | + | ||
| 481 | +工具建议: | ||
| 482 | + | ||
| 483 | +- Playwright screenshot | ||
| 484 | +- 必要时加像素容差比对 | ||
| 485 | + | ||
| 486 | +范围: | ||
| 487 | + | ||
| 488 | +- 默认画布 | ||
| 489 | +- 节点选中态 | ||
| 490 | +- 多选框选态 | ||
| 491 | +- 预览弹窗 | ||
| 492 | + | ||
| 493 | +注意: | ||
| 494 | + | ||
| 495 | +- 视觉快照只做辅助,不作为唯一正确性判断 | ||
| 496 | + | ||
| 497 | +## 10. “先迁测试再迁功能”的具体执行法 | ||
| 498 | + | ||
| 499 | +推荐按下面的顺序: | ||
| 500 | + | ||
| 501 | +1. 在老项目上补一个最小可运行的自动化测试工程。 | ||
| 502 | +2. 先把 `doc/index.vue` 作为官方基线页固定下来。 | ||
| 503 | +3. 先写 8 到 12 条核心操作回归脚本。 | ||
| 504 | +4. 把核心图数据样本沉淀成 fixture。 | ||
| 505 | +5. 新项目初始化后,先把这些 fixture 和脚本目录直接复制或共享过来。 | ||
| 506 | +6. 先让新项目通过静态渲染和简单命令测试,再逐条恢复复杂交互。 | ||
| 507 | +7. 每迁完一个命令簇,就同时补对应单元测试和 E2E。 | ||
| 508 | + | ||
| 509 | +这里的关键不是“测试写得多”,而是“测试写得可迁移”。测试必须尽量面向: | ||
| 510 | + | ||
| 511 | +- 用户操作 | ||
| 512 | +- 对外 API | ||
| 513 | +- 输出数据 | ||
| 514 | +- 关键视觉状态 | ||
| 515 | + | ||
| 516 | +不要让测试过度绑定旧 DOM 结构,否则到了 LogicFlow 后会全部失效。 | ||
| 517 | + | ||
| 518 | +## 11. 风险清单与规避方案 | ||
| 519 | + | ||
| 520 | +### 风险 1:LogicFlow 与 G6 交互语义不完全一致 | ||
| 521 | + | ||
| 522 | +规避: | ||
| 523 | + | ||
| 524 | +- 通过适配层屏蔽底层事件差异 | ||
| 525 | +- 将兼容目标定为用户可感知行为一致,而非引擎内部事件完全一致 | ||
| 526 | + | ||
| 527 | +### 风险 2:拖拽、框选、连线是最容易出现细微偏差的区域 | ||
| 528 | + | ||
| 529 | +规避: | ||
| 530 | + | ||
| 531 | +- 优先对这些能力建立 Playwright 操作脚本 | ||
| 532 | +- 每次迁移只处理一类交互 | ||
| 533 | + | ||
| 534 | +### 风险 3:复杂业务表单耦合在 demo 页面中,容易阻塞底层迁移 | ||
| 535 | + | ||
| 536 | +规避: | ||
| 537 | + | ||
| 538 | +- 将“业务表单展示能力”与“编辑器内核能力”拆开迁移 | ||
| 539 | +- 先迁插槽和实例 API,再迁具体表单逻辑 | ||
| 540 | + | ||
| 541 | +### 风险 4:撤销/重做在重构后容易悄悄失真 | ||
| 542 | + | ||
| 543 | +规避: | ||
| 544 | + | ||
| 545 | +- 将命令栈行为做独立单元测试 | ||
| 546 | +- 针对 add/delete/update/drag 各自建立回放测试 | ||
| 547 | + | ||
| 548 | +### 风险 5:想一次做完所有节点类型和业务配置 | ||
| 549 | + | ||
| 550 | +规避: | ||
| 551 | + | ||
| 552 | +- 先做最小节点集 | ||
| 553 | +- 用 fixture 优先覆盖真实高频业务节点 | ||
| 554 | +- 按业务价值排序,而不是按代码文件排序 | ||
| 555 | + | ||
| 556 | +## 12. 第一阶段建议交付物 | ||
| 557 | + | ||
| 558 | +建议我们下一步先做这几件事,而不是直接开写新编辑器: | ||
| 559 | + | ||
| 560 | +1. 初始化新项目文档和计划文件。 | ||
| 561 | +2. 在老项目建立 Playwright 基线测试。 | ||
| 562 | +3. 提取第一批 fixture: | ||
| 563 | + - 单节点流程 | ||
| 564 | + - 线性审批流程 | ||
| 565 | + - 带分支流程 | ||
| 566 | +4. 梳理旧项目对外契约并固化成 checklist。 | ||
| 567 | +5. 再开始新项目工程初始化。 | ||
| 568 | + | ||
| 569 | +## 13. 初始验收标准 | ||
| 570 | + | ||
| 571 | +只要以下条件满足,就说明这次升级是在“安全轨道”上: | ||
| 572 | + | ||
| 573 | +- 老项目已有可重复执行的自动化回归脚本 | ||
| 574 | +- 新项目已完成基础工程和测试底座 | ||
| 575 | +- 新项目已能渲染老 fixture | ||
| 576 | +- 新项目已恢复核心命令链:选择、缩放、删除、撤销、重做 | ||
| 577 | +- 新项目已恢复核心新增链:新增节点、新增边、更新节点 | ||
| 578 | +- 新项目已恢复宿主接入链:插槽、事件、`onRef` | ||
| 579 | + | ||
| 580 | +## 14. 结论 | ||
| 581 | + | ||
| 582 | +这次最安全的升级方式不是“直接用新栈重写”,而是: | ||
| 583 | + | ||
| 584 | +- 先把老项目变成可测试的行为基线 | ||
| 585 | +- 再用新栈分阶段替换底层 | ||
| 586 | +- 每一阶段都以自动化测试作为继续推进的前提 | ||
| 587 | + | ||
| 588 | +如果按这个方案执行,虽然前期会比“直接开写”慢一点,但整体成功率和可控性会高很多,尤其适合当前“业务不能出问题、人工回归能力又有限”的情况。 |
env.d.ts
0 → 100644
| 1 | +/// <reference types="vite/client" /> | ||
| 2 | + | ||
| 3 | +declare module '*.vue' { | ||
| 4 | + import type { DefineComponent } from 'vue' | ||
| 5 | + | ||
| 6 | + const component: DefineComponent<Record<string, never>, Record<string, never>, unknown> | ||
| 7 | + export default component | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | +declare global { | ||
| 11 | + interface Window { | ||
| 12 | + __FLOW_EDITOR_TEST_API__?: { | ||
| 13 | + getEditorContract: () => { | ||
| 14 | + hasEditor: boolean | ||
| 15 | + hasCommander: boolean | ||
| 16 | + hasDelete: boolean | ||
| 17 | + hasUndo: boolean | ||
| 18 | + hasRedo: boolean | ||
| 19 | + hasOpenModel: boolean | ||
| 20 | + hasCloseModel: boolean | ||
| 21 | + hasAddNode: boolean | ||
| 22 | + hasUpdateModel: boolean | ||
| 23 | + hasGraphRead: boolean | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + } | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +export {} | ||
| 30 | + |
findings.md
0 → 100644
| 1 | +# 调研结论与决策 | ||
| 2 | + | ||
| 3 | +## 需求摘要 | ||
| 4 | +- 在 `/Users/huyirui/program/itomix/git/vue-flow-editor2` 中重建现有流程编辑器项目 | ||
| 5 | +- 保持 `/Users/huyirui/program/itomix/git/vue-flow-editor` 中既有业务逻辑和核心操作行为的一致性 | ||
| 6 | +- 将技术基座升级为最新的 Vue 3、Element Plus、LogicFlow 2.0 和 Vite | ||
| 7 | +- 输出一份安全、可分阶段落地的迁移方案 | ||
| 8 | +- 在功能迁移前或迁移过程中优先补齐自动化测试护栏 | ||
| 9 | +- 尽量降低对人工点击回归和人工细查的依赖 | ||
| 10 | + | ||
| 11 | +## 测试环境信息 | ||
| 12 | +- 如后续调试页面登录态或接口联调时遇到登录问题,可优先使用测试账号: | ||
| 13 | +- 用户名:`jack` | ||
| 14 | +- 密码:`000000` | ||
| 15 | +- 用途:测试环境排查登录与页面接入问题时使用 | ||
| 16 | + | ||
| 17 | +## 调研发现 | ||
| 18 | +- 新仓库当前是空仓库,尚未初始化工程 | ||
| 19 | +- 旧项目是“组件库式编辑器”,不是单纯的页面应用 | ||
| 20 | +- 旧项目的主要结构层次包括: | ||
| 21 | +- `src/editor`:编辑器壳层、画布、工具栏、菜单、预览、表单/弹框组合 | ||
| 22 | +- `src/shape`:自定义节点、边、锚点、控制点 | ||
| 23 | +- `src/behavior`:拖拽、选择、连线、悬浮、框选等交互行为 | ||
| 24 | +- `src/plugins/Command.ts`:命令管理层,负责撤销/重做等命令式能力 | ||
| 25 | +- 旧项目使用 Vue CLI 风格构建链,并建立在 G6 3.x 时代的编辑器能力之上 | ||
| 26 | +- 旧项目同时存在 `element-ui` 与 `element-plus` 的混用痕迹,说明技术底座已经老化 | ||
| 27 | +- 旧项目对外契约不只是“画布渲染”,还包含 props、事件、插槽以及 `onRef` 暴露出的命令式 API | ||
| 28 | +- `src/editor/editor.ts` 是迁移价值最高的行为核心,因为它集中承载了撤销、重做、删除、缩放、全选、新增边、新增节点、拖拽、更新等语义 | ||
| 29 | +- 旧项目画布通过行为注册组合了点击选择、节点拖拽、边拖拽、画布拖拽、悬浮态和框选能力 | ||
| 30 | +- 旧项目的 `doc/index.vue` 是非常接近真实业务接入的样板页,适合作为第一份自动化基线回归页面 | ||
| 31 | +- 旧项目基线页并不是纯静态示例页,启动后会请求流程版本和流程图数据接口;如果没有后端登录态,会触发 `doc/axios.js` 中的“登录失效”弹框逻辑 | ||
| 32 | +- 旧项目实际运行依赖 `.nvmrc` 指定的 Node `v16.17.0`,使用当前默认 Node 18 启动会触发 Vue CLI / webpack 运行异常 | ||
| 33 | +- 旧项目首页会通过 `toolbarButtonHandler` 隐藏默认工具栏中的 `grid`、`miniMap`,并在“当前启用版本”等于“当前选中版本”时继续隐藏 `delete`、`undo`、`redo` | ||
| 34 | +- 旧项目基线测试使用统一的 `/admin/**` 接口拦截更稳,已知 `flow_version`、`flow_nodes` 返回固定数据,其他后台接口先兜底返回成功空对象,能够避免“登录失效”弹框打断测试 | ||
| 35 | +- 保存按钮链路会依赖 `checkAllFlowNodePropertyAPI`,只要该接口返回空数组,就能稳定进入“是否确定保存流程?”确认框 | ||
| 36 | +- 旧项目已经可以通过测试探针稳定验证 `onRef` 暴露出来的命令能力、节点选中、右侧面板打开、删除/撤销/重做等核心行为,无需在 E2E 中硬依赖 canvas 坐标点击 | ||
| 37 | +- 旧项目当前已经有 13 条稳定通过的基线回归,覆盖页面骨架、工具栏、版本区、预览、保存确认、实例契约、单选、多选、全选、节点面板、删除撤销、新增节点、新增连线 | ||
| 38 | +- 新项目已经完成第一轮工程初始化,当前具备 `apps/demo + packages/flow-editor + tests` 的最小双层结构 | ||
| 39 | +- 新项目首批平移的测试语义已经落地为 3 条基线用例:冒烟、工具栏、实例契约 | ||
| 40 | +- 新项目当前采用“兼容壳层 + 最小 LogicFlow 2.x 实例”的方式承接迁移起步,不再只是纯占位页面 | ||
| 41 | +- 在当前 npm 生态里,真正的 LogicFlow 2.x 包应使用 `@logicflow/core` 与 `@logicflow/extension`,而不是名为 `logicflow` 的同名旧包 | ||
| 42 | +- LogicFlow 2.x 的样式路径需要使用 `@logicflow/core/lib/style/index.css` 与 `@logicflow/extension/lib/style/index.css` | ||
| 43 | +- LogicFlow 2.x 的 `pluginsOptions` 需要按插件名拆分;结合本地安装源码可确认 MiniMap 的 `pluginName` 实际为 `miniMap` | ||
| 44 | +- 当前运行环境下,Playwright 自带 `webServer` 预检查会被本机代理或旧服务干扰,导致错误复用旧页面或误判端口占用;改为仓库脚本自管 Vite 生命周期后已稳定 | ||
| 45 | +- 直接对 Vue props 代理对象使用 `structuredClone` 会在浏览器中抛错;兼容层需要显式做图数据浅拷贝/结构化转换 | ||
| 46 | +- 旧项目保存时导出的边结构是 `source/target/sourceAnchor/targetAnchor`,而不是当前新仓夹具里的 `sourceNodeId/targetNodeId` | ||
| 47 | +- 旧项目节点展示字段常见为 `text/shape/activity/control`,宿主语义字段再由业务补到 `label/type` | ||
| 48 | +- 新项目已经实现统一适配流程:`旧数据/新数据 -> normalizeGraphData -> toLogicFlowGraphData` | ||
| 49 | +- 适配层当前已覆盖三类高价值夹具:单节点、线性流、分支流 | ||
| 50 | +- 新项目当前已经具备基础可操作能力:工具栏缩放/适应画布、节点新增、节点选中、节点删除、预览打开 | ||
| 51 | +- 当前选中态已经和 LogicFlow 的 `node:click`、`blank:click` 事件同步,不再只是页面侧假状态 | ||
| 52 | +- 为了让页面尽快进入“可演示”状态,当前新增/删除节点仍以兼容层状态为主驱动,再同步给 LogicFlow 画布;后续再继续下沉到更完整的命令系统 | ||
| 53 | +- 当前页面已经移除了遮挡画布的过渡提示层,用户可以直接看到画布与侧边操作区 | ||
| 54 | +- 新项目当前已具备命令闭环的第一版能力:更新节点名称、创建新连线、撤销、重做 | ||
| 55 | +- 当前撤销/重做基于兼容层图数据快照实现,适用于现阶段的节点增删改和连线新增 | ||
| 56 | + | ||
| 57 | +## 技术决策 | ||
| 58 | +| 决策 | 原因 | | ||
| 59 | +|------|------| | ||
| 60 | +| 兼容性以“用户可感知行为一致”为准,而不是追求源码级一致 | LogicFlow 2.0 和 Vite 的引入会带来内部架构变化 | | ||
| 61 | +| 在迁移核心能力前先补旧项目基线回归测试 | 这能把“过去正确的行为”沉淀成可执行规格 | | ||
| 62 | +| 将迁移拆成壳层、数据适配、交互模型、业务集成等阶段 | 能缩小每次改动的影响面,并便于逐阶段验收 | | ||
| 63 | +| 将 `doc/index.vue` 作为第一份官方回归样板 | 它比孤立组件测试更接近真实接入场景 | | ||
| 64 | +| 优先建设兼容层,而不是让宿主直接依赖 LogicFlow | 这样更容易在新旧实现之间保持稳定契约 | | ||
| 65 | +| 在设计文档之外单独输出实施计划 | 设计文档负责方向,实施计划负责逐任务落地 | | ||
| 66 | +| 旧项目基线测试优先通过 Playwright 拦截初始化接口稳定运行 | 这样可以先摆脱真实后端登录态依赖,尽快把基线回归跑起来 | | ||
| 67 | +| 工具栏回归测试应以基线页真实展示结果为准 | 旧项目示例页会二次过滤默认工具栏,不能直接拿组件默认配置做断言 | | ||
| 68 | +| 基线接口 mock 优先做成统一 helper | 后续新增预览、版本区、保存、删除等测试时可以直接复用同一套后台兜底逻辑 | | ||
| 69 | +| 保存链路的第一批回归先验证“能弹确认框” | 这比直接覆盖真实保存提交更稳,更适合作为基线起步 | | ||
| 70 | +| 对 canvas 型交互优先引入测试探针而不是纯坐标点击 | 这样更稳定,也更适合作为后续 LogicFlow 迁移时的行为契约 | | ||
| 71 | +| 旧项目阶段优先补“高价值行为基线” | 先把最容易在迁移中回归的核心行为保护住,而不是追求面面俱到的 UI 覆盖 | | ||
| 72 | +| 新项目初始化阶段先平移高价值测试语义,再逐步接入真实 LogicFlow 行为 | 这样能在每补一层实现时立刻看到与旧项目的偏差 | | ||
| 73 | +| 新项目暂时只接入 MiniMap 作为 2.x 插件配置示例 | 先验证 2.x 包、样式、插件配置和运行方式,再继续补命令/交互迁移 | | ||
| 74 | +| 新项目数据层优先支持“旧结构 + 新结构”双读 | 这样 demo、mock、后续 fixture 和真实保存链路可以逐步迁,不必一次改干净 | | ||
| 75 | +| 新项目交互层先保证用户能完成“新增-选中-删除-预览”最小闭环 | 这是第一个真正值得人工打开页面查看的里程碑 | | ||
| 76 | +| 命令系统先实现“对用户可见”的撤销/重做和连线创建 | 先优先满足页面体验,再考虑更深的引擎级命令抽象 | | ||
| 77 | + | ||
| 78 | +## 遇到的问题 | ||
| 79 | +| 问题 | 处理方式 | | ||
| 80 | +|------|----------| | ||
| 81 | +| 新仓库当前没有任何现成工程结构可供检查 | 在设计文档中补充了初始化与搭建建议 | | ||
| 82 | +| LogicFlow 2.x 的 npm 包名与用户常说的 `logicflow` 不一致 | 通过 `npm search logicflow --json` 与已安装包类型定义确认,应改用 `@logicflow/core` / `@logicflow/extension` | | ||
| 83 | +| Playwright `webServer` 在当前机器上会把代理返回的 `502` 当成端口已占用 | 改成 `scripts/run-e2e.mjs` 手动拉起 Vite、等待 ready、执行测试并清理进程 | | ||
| 84 | + | ||
| 85 | +## 参考资料 | ||
| 86 | +- 旧项目仓库:`/Users/huyirui/program/itomix/git/vue-flow-editor` | ||
| 87 | +- 新项目仓库:`/Users/huyirui/program/itomix/git/vue-flow-editor2` | ||
| 88 | +- 旧项目入口文件:`/Users/huyirui/program/itomix/git/vue-flow-editor/src/editor/index.ts` | ||
| 89 | +- 旧项目依赖清单:`/Users/huyirui/program/itomix/git/vue-flow-editor/package.json` | ||
| 90 | +- 旧项目 README:`/Users/huyirui/program/itomix/git/vue-flow-editor/README.md` | ||
| 91 | +- 旧项目命令编排:`/Users/huyirui/program/itomix/git/vue-flow-editor/src/editor/editor.ts` | ||
| 92 | +- 旧项目命令管理器:`/Users/huyirui/program/itomix/git/vue-flow-editor/src/plugins/Command.ts` | ||
| 93 | +- 旧项目示例接入页:`/Users/huyirui/program/itomix/git/vue-flow-editor/doc/index.vue` | ||
| 94 | +- 当前重构设计文档:`/Users/huyirui/program/itomix/git/vue-flow-editor2/docs/plans/2026-03-31-flow-editor-rebuild-design.md` | ||
| 95 | +- 当前迁移实施计划:`/Users/huyirui/program/itomix/git/vue-flow-editor2/docs/plans/2026-03-31-flow-editor-migration-implementation-plan.md` | ||
| 96 | +- 旧项目基线页说明:`/Users/huyirui/program/itomix/git/vue-flow-editor/doc/README-基线回归页.md` | ||
| 97 | +- 新项目 LogicFlow 2.x 类型定义:`/Users/huyirui/program/itomix/git/vue-flow-editor2/node_modules/@logicflow/core/lib/index.d.ts` | ||
| 98 | +- 新项目 LogicFlow 扩展类型定义:`/Users/huyirui/program/itomix/git/vue-flow-editor2/node_modules/@logicflow/extension/lib/index.d.ts` | ||
| 99 | +- 新项目数据适配层:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/core/adapter/g6-to-logicflow.ts` | ||
| 100 | +- 新项目适配类型定义:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/core/schema/types.ts` | ||
| 101 | +- 新项目交互核心实现:`/Users/huyirui/program/itomix/git/vue-flow-editor2/packages/flow-editor/src/components/FlowEditor.vue` | ||
| 102 | +- 新项目交互回归测试:`/Users/huyirui/program/itomix/git/vue-flow-editor2/tests/e2e/interaction.spec.ts` | ||
| 103 | + | ||
| 104 | +## 可视化/浏览器结论 | ||
| 105 | +- 新项目基线页已可在浏览器中稳定渲染 `.app`、`.vue-flow-editor`、`.vue-flow-editor-toolbar`、`.vue-flow-editor-canvas-target` | ||
| 106 | +- 新项目当前已跑通 3 条 Playwright 基线用例,验证页面骨架、工具栏语义和 `onRef` 实例契约 | ||
| 107 | +- 新项目基线页当前已经能直接消费旧口径 mock 数据,并通过适配层渲染到 LogicFlow 2.x | ||
| 108 | +- 新项目基线页当前已经具备基础编辑能力,用户可以直接在页面上新增节点、选中节点、删除节点并打开预览 | ||
| 109 | +- 新项目基线页当前已经具备更完整的编辑演示能力,用户可以直接改节点名、创建连线并进行撤销/重做 |
package.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "vue-flow-editor2", | ||
| 3 | + "version": "0.1.0", | ||
| 4 | + "private": true, | ||
| 5 | + "type": "module", | ||
| 6 | + "packageManager": "pnpm@10.9.0", | ||
| 7 | + "scripts": { | ||
| 8 | + "dev": "vite --host 127.0.0.1 --port 4173", | ||
| 9 | + "build": "vite build", | ||
| 10 | + "preview": "vite preview --host 127.0.0.1 --port 4173", | ||
| 11 | + "test:unit": "vitest run", | ||
| 12 | + "test:unit:watch": "vitest", | ||
| 13 | + "test:e2e": "node ./scripts/run-e2e.mjs" | ||
| 14 | + }, | ||
| 15 | + "dependencies": { | ||
| 16 | + "@logicflow/core": "^2.1.11", | ||
| 17 | + "@logicflow/extension": "^2.1.15", | ||
| 18 | + "element-plus": "^2.13.6", | ||
| 19 | + "vue": "^3.5.31" | ||
| 20 | + }, | ||
| 21 | + "devDependencies": { | ||
| 22 | + "@playwright/test": "^1.58.2", | ||
| 23 | + "@types/node": "^25.5.0", | ||
| 24 | + "@vitejs/plugin-vue": "^6.0.5", | ||
| 25 | + "@vue/test-utils": "^2.4.6", | ||
| 26 | + "jsdom": "^29.0.1", | ||
| 27 | + "typescript": "^6.0.2", | ||
| 28 | + "vite": "^8.0.3", | ||
| 29 | + "vitest": "^4.1.2", | ||
| 30 | + "vue-tsc": "^3.2.6" | ||
| 31 | + } | ||
| 32 | +} |
| 1 | +<script setup lang="ts"> | ||
| 2 | +import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue' | ||
| 3 | +import LogicFlow from '@logicflow/core' | ||
| 4 | +import { MiniMap } from '@logicflow/extension' | ||
| 5 | +import { | ||
| 6 | + cloneNormalizedGraphData, | ||
| 7 | + normalizeGraphData, | ||
| 8 | + toLogicFlowGraphData, | ||
| 9 | +} from '../core/adapter/g6-to-logicflow' | ||
| 10 | +import type { | ||
| 11 | + FlowEditorCommander, | ||
| 12 | + FlowEditorRef, | ||
| 13 | + FlowEditorState, | ||
| 14 | + FlowNodeData, | ||
| 15 | + ToolbarButtonDefinition, | ||
| 16 | +} from '../types' | ||
| 17 | +import type { FlowEditorInputGraphData } from '../core/schema/types' | ||
| 18 | + | ||
| 19 | +interface FlowEditorProps { | ||
| 20 | + data?: FlowEditorInputGraphData | ||
| 21 | + grid?: boolean | ||
| 22 | + miniMap?: boolean | ||
| 23 | + disabledDragEdge?: boolean | ||
| 24 | + disabledUndo?: boolean | ||
| 25 | + height?: number | string | ||
| 26 | + toolbarHeight?: number | string | ||
| 27 | + menuWidth?: number | string | ||
| 28 | + modelWidth?: number | string | ||
| 29 | + onRef?: (instance: FlowEditorRef) => void | ||
| 30 | + toolbarButtonHandler?: (buttons: ToolbarButtonDefinition[]) => ToolbarButtonDefinition[] | ||
| 31 | + loading?: boolean | ||
| 32 | + multipleSelect?: boolean | ||
| 33 | + enableEngine?: boolean | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +const props = withDefaults(defineProps<FlowEditorProps>(), { | ||
| 37 | + data: () => ({ | ||
| 38 | + nodes: [], | ||
| 39 | + edges: [], | ||
| 40 | + }), | ||
| 41 | + grid: true, | ||
| 42 | + miniMap: true, | ||
| 43 | + disabledDragEdge: false, | ||
| 44 | + disabledUndo: false, | ||
| 45 | + height: 560, | ||
| 46 | + toolbarHeight: 56, | ||
| 47 | + menuWidth: 240, | ||
| 48 | + modelWidth: 360, | ||
| 49 | + loading: false, | ||
| 50 | + multipleSelect: true, | ||
| 51 | + enableEngine: true, | ||
| 52 | +}) | ||
| 53 | + | ||
| 54 | +const canvasTargetRef = ref<HTMLDivElement | null>(null) | ||
| 55 | +let logicFlowInstance: LogicFlow | null = null | ||
| 56 | +const historyPast = ref<FlowEditorState['graphData'][]>([]) | ||
| 57 | +const historyFuture = ref<FlowEditorState['graphData'][]>([]) | ||
| 58 | +const selectedNodeNameDraft = ref('') | ||
| 59 | + | ||
| 60 | +const state = reactive<FlowEditorState>({ | ||
| 61 | + canvasProps: { | ||
| 62 | + grid: props.grid, | ||
| 63 | + miniMap: props.miniMap, | ||
| 64 | + zoom: 1, | ||
| 65 | + }, | ||
| 66 | + graphData: cloneNormalizedGraphData(normalizeGraphData(props.data)), | ||
| 67 | + selectedNodeIds: [], | ||
| 68 | + isModelOpen: false, | ||
| 69 | + isPreviewOpen: false, | ||
| 70 | +}) | ||
| 71 | + | ||
| 72 | +const DEFAULT_TOOLBAR_BUTTONS: ToolbarButtonDefinition[] = [ | ||
| 73 | + { key: 'grid', label: '网格' }, | ||
| 74 | + { key: 'miniMap', label: '缩略图' }, | ||
| 75 | + { key: 'fitView', label: '适应画布' }, | ||
| 76 | + { key: 'actualView', label: '实际尺寸' }, | ||
| 77 | + { key: 'zoomIn', label: '放大' }, | ||
| 78 | + { key: 'zoomOut', label: '缩小' }, | ||
| 79 | + { key: 'delete', label: '删除' }, | ||
| 80 | + { key: 'preview', label: '预览' }, | ||
| 81 | + { key: 'undo', label: '撤销' }, | ||
| 82 | + { key: 'redo', label: '重做' }, | ||
| 83 | +] | ||
| 84 | + | ||
| 85 | +const toolbarButtons = computed(() => { | ||
| 86 | + const buttons = DEFAULT_TOOLBAR_BUTTONS.map((button) => ({ ...button })) | ||
| 87 | + return props.toolbarButtonHandler ? props.toolbarButtonHandler(buttons) : buttons | ||
| 88 | +}) | ||
| 89 | + | ||
| 90 | +const heightStyle = computed(() => { | ||
| 91 | + return typeof props.height === 'number' ? `${props.height}px` : props.height | ||
| 92 | +}) | ||
| 93 | + | ||
| 94 | +const selectedNode = computed(() => { | ||
| 95 | + return state.graphData.nodes.find((node) => node.id === state.selectedNodeIds[0]) ?? null | ||
| 96 | +}) | ||
| 97 | + | ||
| 98 | +const previewGraphJson = computed(() => { | ||
| 99 | + return JSON.stringify(state.graphData, null, 2) | ||
| 100 | +}) | ||
| 101 | + | ||
| 102 | +const canUndo = computed(() => historyPast.value.length > 0) | ||
| 103 | +const canRedo = computed(() => historyFuture.value.length > 0) | ||
| 104 | + | ||
| 105 | +function syncSelection(nodeIds: string[]) { | ||
| 106 | + state.selectedNodeIds = [...nodeIds] | ||
| 107 | + selectedNodeNameDraft.value = selectedNode.value?.label ?? '' | ||
| 108 | + | ||
| 109 | + if (!logicFlowInstance) { | ||
| 110 | + return | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + logicFlowInstance.clearSelectElements() | ||
| 114 | + nodeIds.forEach((nodeId) => { | ||
| 115 | + logicFlowInstance?.selectElementById(nodeId, true) | ||
| 116 | + }) | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +function focusNode(nodeId: string) { | ||
| 120 | + if (!logicFlowInstance) { | ||
| 121 | + return | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + syncSelection([nodeId]) | ||
| 125 | + logicFlowInstance.focusOn(nodeId) | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +function queueNodeFocus(nodeId: string) { | ||
| 129 | + void nextTick(() => { | ||
| 130 | + focusNode(nodeId) | ||
| 131 | + }) | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +function snapshotGraphData() { | ||
| 135 | + return cloneNormalizedGraphData(state.graphData) | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +function applyGraphData(nextGraphData: FlowEditorState['graphData'], recordHistory = true) { | ||
| 139 | + if (recordHistory) { | ||
| 140 | + historyPast.value.push(snapshotGraphData()) | ||
| 141 | + historyFuture.value = [] | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + state.graphData = cloneNormalizedGraphData(nextGraphData) | ||
| 145 | + syncSelection([]) | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +function runToolbarAction(key: string) { | ||
| 149 | + switch (key) { | ||
| 150 | + case 'grid': | ||
| 151 | + commander.switchGrid() | ||
| 152 | + break | ||
| 153 | + case 'miniMap': | ||
| 154 | + commander.switchMiniMap() | ||
| 155 | + break | ||
| 156 | + case 'fitView': | ||
| 157 | + commander.fitView() | ||
| 158 | + break | ||
| 159 | + case 'actualView': | ||
| 160 | + commander.actualView() | ||
| 161 | + break | ||
| 162 | + case 'zoomIn': | ||
| 163 | + commander.zoomIn() | ||
| 164 | + break | ||
| 165 | + case 'zoomOut': | ||
| 166 | + commander.zoomOut() | ||
| 167 | + break | ||
| 168 | + case 'delete': | ||
| 169 | + commander.delete() | ||
| 170 | + break | ||
| 171 | + case 'preview': | ||
| 172 | + api.openPreview() | ||
| 173 | + break | ||
| 174 | + case 'undo': | ||
| 175 | + commander.undo() | ||
| 176 | + break | ||
| 177 | + case 'redo': | ||
| 178 | + commander.redo() | ||
| 179 | + break | ||
| 180 | + default: | ||
| 181 | + break | ||
| 182 | + } | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +function toggleNodeSelection(nodeId: string) { | ||
| 186 | + if (!props.multipleSelect) { | ||
| 187 | + focusNode(nodeId) | ||
| 188 | + return | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + const nextSelectedNodeIds = state.selectedNodeIds.includes(nodeId) | ||
| 192 | + ? state.selectedNodeIds.filter((currentNodeId) => currentNodeId !== nodeId) | ||
| 193 | + : [...state.selectedNodeIds, nodeId] | ||
| 194 | + | ||
| 195 | + syncSelection(nextSelectedNodeIds) | ||
| 196 | +} | ||
| 197 | + | ||
| 198 | +function createEdgeFromSelection() { | ||
| 199 | + if (state.selectedNodeIds.length < 2) { | ||
| 200 | + return | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + const [sourceNodeId, targetNodeId] = state.selectedNodeIds | ||
| 204 | + const existingEdge = state.graphData.edges.find((edge) => { | ||
| 205 | + return edge.sourceNodeId === sourceNodeId && edge.targetNodeId === targetNodeId | ||
| 206 | + }) | ||
| 207 | + | ||
| 208 | + if (existingEdge) { | ||
| 209 | + return | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + const nextEdges = [ | ||
| 213 | + ...state.graphData.edges, | ||
| 214 | + { | ||
| 215 | + id: `edge-${sourceNodeId}-${targetNodeId}-${state.graphData.edges.length + 1}`, | ||
| 216 | + sourceNodeId, | ||
| 217 | + targetNodeId, | ||
| 218 | + label: '新建连线', | ||
| 219 | + }, | ||
| 220 | + ] | ||
| 221 | + | ||
| 222 | + applyGraphData({ | ||
| 223 | + nodes: state.graphData.nodes, | ||
| 224 | + edges: nextEdges, | ||
| 225 | + }) | ||
| 226 | + syncSelection([sourceNodeId, targetNodeId]) | ||
| 227 | +} | ||
| 228 | + | ||
| 229 | +function saveSelectedNodeName() { | ||
| 230 | + const currentNodeId = state.selectedNodeIds[0] | ||
| 231 | + if (!currentNodeId || !selectedNodeNameDraft.value.trim()) { | ||
| 232 | + return | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + applyGraphData({ | ||
| 236 | + nodes: state.graphData.nodes.map((node) => { | ||
| 237 | + return node.id === currentNodeId | ||
| 238 | + ? { ...node, label: selectedNodeNameDraft.value.trim() } | ||
| 239 | + : node | ||
| 240 | + }), | ||
| 241 | + edges: state.graphData.edges, | ||
| 242 | + }) | ||
| 243 | + syncSelection([currentNodeId]) | ||
| 244 | +} | ||
| 245 | + | ||
| 246 | +function renderGraphToEngine() { | ||
| 247 | + if (!logicFlowInstance) { | ||
| 248 | + return | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + logicFlowInstance.render(toLogicFlowGraphData(state.graphData)) | ||
| 252 | +} | ||
| 253 | + | ||
| 254 | +function initLogicFlow() { | ||
| 255 | + if (!props.enableEngine || !canvasTargetRef.value) { | ||
| 256 | + return | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + logicFlowInstance?.destroy() | ||
| 260 | + logicFlowInstance = new LogicFlow({ | ||
| 261 | + container: canvasTargetRef.value, | ||
| 262 | + grid: props.grid, | ||
| 263 | + history: true, | ||
| 264 | + plugins: [MiniMap], | ||
| 265 | + disabledPlugins: props.miniMap ? [] : ['miniMap'], | ||
| 266 | + // LogicFlow 2.x 需要按插件名拆分配置,这里直接使用 miniMap 的 pluginName。 | ||
| 267 | + pluginsOptions: { | ||
| 268 | + miniMap: { | ||
| 269 | + showEdge: true, | ||
| 270 | + }, | ||
| 271 | + }, | ||
| 272 | + }) | ||
| 273 | + | ||
| 274 | + // 这里先把页面的“选中态”同步到 LogicFlow 事件,后续事件兼容层会继续扩展。 | ||
| 275 | + logicFlowInstance.on('node:click', ({ data }) => { | ||
| 276 | + syncSelection([data.id]) | ||
| 277 | + }) | ||
| 278 | + logicFlowInstance.on('blank:click', () => { | ||
| 279 | + syncSelection([]) | ||
| 280 | + }) | ||
| 281 | + | ||
| 282 | + renderGraphToEngine() | ||
| 283 | + logicFlowInstance.fitView(40, 20) | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | +watch( | ||
| 287 | + () => props.data, | ||
| 288 | + (value) => { | ||
| 289 | + historyPast.value = [] | ||
| 290 | + historyFuture.value = [] | ||
| 291 | + state.graphData = cloneNormalizedGraphData(normalizeGraphData(value)) | ||
| 292 | + }, | ||
| 293 | + { deep: true }, | ||
| 294 | +) | ||
| 295 | + | ||
| 296 | +watch( | ||
| 297 | + () => props.grid, | ||
| 298 | + (value) => { | ||
| 299 | + state.canvasProps.grid = value | ||
| 300 | + if (logicFlowInstance) { | ||
| 301 | + logicFlowInstance.updateEditConfig({ | ||
| 302 | + stopMoveGraph: false, | ||
| 303 | + }) | ||
| 304 | + } | ||
| 305 | + }, | ||
| 306 | +) | ||
| 307 | + | ||
| 308 | +watch( | ||
| 309 | + () => props.miniMap, | ||
| 310 | + (value) => { | ||
| 311 | + state.canvasProps.miniMap = value | ||
| 312 | + initLogicFlow() | ||
| 313 | + }, | ||
| 314 | +) | ||
| 315 | + | ||
| 316 | +watch( | ||
| 317 | + () => state.graphData, | ||
| 318 | + () => { | ||
| 319 | + renderGraphToEngine() | ||
| 320 | + }, | ||
| 321 | + { deep: true }, | ||
| 322 | +) | ||
| 323 | + | ||
| 324 | +function createNode(node?: Partial<FlowNodeData>) { | ||
| 325 | + return { | ||
| 326 | + id: node?.id ?? `node-${state.graphData.nodes.length + 1}`, | ||
| 327 | + label: node?.label ?? `新节点 ${state.graphData.nodes.length + 1}`, | ||
| 328 | + type: node?.type ?? 'custom', | ||
| 329 | + x: node?.x ?? 120 + state.graphData.nodes.length * 40, | ||
| 330 | + y: node?.y ?? 120, | ||
| 331 | + ...node, | ||
| 332 | + } | ||
| 333 | +} | ||
| 334 | + | ||
| 335 | +function addNodeByType(type: FlowNodeData['type']) { | ||
| 336 | + const nextNode = createNode({ | ||
| 337 | + type, | ||
| 338 | + label: type === 'approval' ? '审批节点' : type === 'cc' ? '抄送节点' : '新节点', | ||
| 339 | + x: 200 + state.graphData.nodes.length * 70, | ||
| 340 | + y: 180 + ((state.graphData.nodes.length % 2) * 100), | ||
| 341 | + }) | ||
| 342 | + | ||
| 343 | + applyGraphData({ | ||
| 344 | + nodes: [...state.graphData.nodes, nextNode], | ||
| 345 | + edges: state.graphData.edges, | ||
| 346 | + }) | ||
| 347 | + queueNodeFocus(nextNode.id) | ||
| 348 | + return nextNode | ||
| 349 | +} | ||
| 350 | + | ||
| 351 | +const commander: FlowEditorCommander = { | ||
| 352 | + switchGrid() { | ||
| 353 | + state.canvasProps.grid = !state.canvasProps.grid | ||
| 354 | + }, | ||
| 355 | + switchMiniMap() { | ||
| 356 | + state.canvasProps.miniMap = !state.canvasProps.miniMap | ||
| 357 | + initLogicFlow() | ||
| 358 | + }, | ||
| 359 | + fitView() { | ||
| 360 | + state.canvasProps.zoom = 0.9 | ||
| 361 | + logicFlowInstance?.fitView(40, 20) | ||
| 362 | + }, | ||
| 363 | + actualView() { | ||
| 364 | + state.canvasProps.zoom = 1 | ||
| 365 | + logicFlowInstance?.resetZoom() | ||
| 366 | + }, | ||
| 367 | + zoomIn() { | ||
| 368 | + state.canvasProps.zoom = Number((state.canvasProps.zoom + 0.1).toFixed(2)) | ||
| 369 | + logicFlowInstance?.zoom(true) | ||
| 370 | + }, | ||
| 371 | + zoomOut() { | ||
| 372 | + state.canvasProps.zoom = Number(Math.max(0.2, state.canvasProps.zoom - 0.1).toFixed(2)) | ||
| 373 | + logicFlowInstance?.zoom(false) | ||
| 374 | + }, | ||
| 375 | + delete() { | ||
| 376 | + // 删除语义优先和画布当前选区保持一致,后续再接 before/after hook。 | ||
| 377 | + const engineSelection = logicFlowInstance?.getSelectElements() | ||
| 378 | + const selectedIds = new Set([ | ||
| 379 | + ...state.selectedNodeIds, | ||
| 380 | + ...(engineSelection?.nodes?.map((node) => node.id) ?? []), | ||
| 381 | + ]) | ||
| 382 | + | ||
| 383 | + applyGraphData({ | ||
| 384 | + nodes: state.graphData.nodes.filter((node) => !selectedIds.has(node.id)), | ||
| 385 | + edges: state.graphData.edges.filter((edge) => { | ||
| 386 | + return !selectedIds.has(edge.sourceNodeId) && !selectedIds.has(edge.targetNodeId) | ||
| 387 | + }), | ||
| 388 | + }) | ||
| 389 | + }, | ||
| 390 | + undo() { | ||
| 391 | + const previousGraph = historyPast.value.pop() | ||
| 392 | + if (!previousGraph) { | ||
| 393 | + return | ||
| 394 | + } | ||
| 395 | + | ||
| 396 | + historyFuture.value.unshift(snapshotGraphData()) | ||
| 397 | + state.isPreviewOpen = false | ||
| 398 | + state.graphData = cloneNormalizedGraphData(previousGraph) | ||
| 399 | + syncSelection([]) | ||
| 400 | + }, | ||
| 401 | + redo() { | ||
| 402 | + const nextGraph = historyFuture.value.shift() | ||
| 403 | + if (!nextGraph) { | ||
| 404 | + return | ||
| 405 | + } | ||
| 406 | + | ||
| 407 | + historyPast.value.push(snapshotGraphData()) | ||
| 408 | + state.graphData = cloneNormalizedGraphData(nextGraph) | ||
| 409 | + syncSelection([]) | ||
| 410 | + }, | ||
| 411 | + selectAll() { | ||
| 412 | + syncSelection(state.graphData.nodes.map((node) => node.id)) | ||
| 413 | + }, | ||
| 414 | +} | ||
| 415 | + | ||
| 416 | +const api: FlowEditorRef = { | ||
| 417 | + editorState: state, | ||
| 418 | + commander, | ||
| 419 | + openModel() { | ||
| 420 | + state.isModelOpen = true | ||
| 421 | + }, | ||
| 422 | + closeModel() { | ||
| 423 | + state.isModelOpen = false | ||
| 424 | + }, | ||
| 425 | + addNode(node) { | ||
| 426 | + return addNodeByType(node?.type ?? 'custom') | ||
| 427 | + }, | ||
| 428 | + updateModel(patch = {}) { | ||
| 429 | + const currentNodeId = state.selectedNodeIds[0] | ||
| 430 | + if (!currentNodeId) { | ||
| 431 | + return | ||
| 432 | + } | ||
| 433 | + | ||
| 434 | + applyGraphData({ | ||
| 435 | + nodes: state.graphData.nodes.map((node) => { | ||
| 436 | + return node.id === currentNodeId ? { ...node, ...patch } : node | ||
| 437 | + }), | ||
| 438 | + edges: state.graphData.edges, | ||
| 439 | + }) | ||
| 440 | + syncSelection([currentNodeId]) | ||
| 441 | + }, | ||
| 442 | + openPreview() { | ||
| 443 | + state.isPreviewOpen = true | ||
| 444 | + }, | ||
| 445 | + read(graphData) { | ||
| 446 | + historyPast.value = [] | ||
| 447 | + historyFuture.value = [] | ||
| 448 | + state.graphData = cloneNormalizedGraphData(normalizeGraphData(graphData)) | ||
| 449 | + state.selectedNodeIds = [] | ||
| 450 | + renderGraphToEngine() | ||
| 451 | + }, | ||
| 452 | + clearStates() { | ||
| 453 | + syncSelection([]) | ||
| 454 | + state.isModelOpen = false | ||
| 455 | + state.isPreviewOpen = false | ||
| 456 | + }, | ||
| 457 | +} | ||
| 458 | + | ||
| 459 | +props.onRef?.(api) | ||
| 460 | + | ||
| 461 | +onMounted(() => { | ||
| 462 | + initLogicFlow() | ||
| 463 | +}) | ||
| 464 | + | ||
| 465 | +onBeforeUnmount(() => { | ||
| 466 | + logicFlowInstance?.destroy() | ||
| 467 | + logicFlowInstance = null | ||
| 468 | +}) | ||
| 469 | +</script> | ||
| 470 | + | ||
| 471 | +<template> | ||
| 472 | + <section class="vue-flow-editor"> | ||
| 473 | + <div class="vue-flow-editor-toolbar"> | ||
| 474 | + <button | ||
| 475 | + v-for="button in toolbarButtons" | ||
| 476 | + :key="button.key" | ||
| 477 | + type="button" | ||
| 478 | + class="toolbar-button" | ||
| 479 | + :data-testid="`toolbar-${button.key}`" | ||
| 480 | + @click="runToolbarAction(button.key)" | ||
| 481 | + > | ||
| 482 | + {{ button.label }} | ||
| 483 | + </button> | ||
| 484 | + <slot name="toolbar-extra" /> | ||
| 485 | + </div> | ||
| 486 | + | ||
| 487 | + <div class="vue-flow-editor-body"> | ||
| 488 | + <div | ||
| 489 | + class="vue-flow-editor-canvas-target" | ||
| 490 | + :style="{ minHeight: heightStyle }" | ||
| 491 | + > | ||
| 492 | + <div class="canvas-badge-list"> | ||
| 493 | + <span class="canvas-badge"> | ||
| 494 | + 网格:{{ state.canvasProps.grid ? '开' : '关' }} | ||
| 495 | + </span> | ||
| 496 | + <span class="canvas-badge"> | ||
| 497 | + 缩略图:{{ state.canvasProps.miniMap ? '开' : '关' }} | ||
| 498 | + </span> | ||
| 499 | + <span class="canvas-badge"> | ||
| 500 | + 缩放:{{ state.canvasProps.zoom.toFixed(1) }} | ||
| 501 | + <strong data-testid="zoom-value">{{ state.canvasProps.zoom.toFixed(1) }}</strong> | ||
| 502 | + </span> | ||
| 503 | + </div> | ||
| 504 | + | ||
| 505 | + <div ref="canvasTargetRef" class="logicflow-stage" /> | ||
| 506 | + | ||
| 507 | + <div class="graph-summary"> | ||
| 508 | + <div> | ||
| 509 | + <span>节点数</span> | ||
| 510 | + <strong data-testid="node-count-value">{{ state.graphData.nodes.length }}</strong> | ||
| 511 | + </div> | ||
| 512 | + <div> | ||
| 513 | + <span>连线数</span> | ||
| 514 | + <strong data-testid="edge-count-value">{{ state.graphData.edges.length }}</strong> | ||
| 515 | + </div> | ||
| 516 | + <div> | ||
| 517 | + <span>当前选中</span> | ||
| 518 | + <strong data-testid="selected-count-value">{{ state.selectedNodeIds.length }}</strong> | ||
| 519 | + </div> | ||
| 520 | + </div> | ||
| 521 | + </div> | ||
| 522 | + | ||
| 523 | + <aside class="flow-editor-side-panel"> | ||
| 524 | + <section class="side-section"> | ||
| 525 | + <h2>节点操作</h2> | ||
| 526 | + <div class="action-grid"> | ||
| 527 | + <button | ||
| 528 | + type="button" | ||
| 529 | + class="side-action-button" | ||
| 530 | + data-testid="add-approval-node" | ||
| 531 | + @click="addNodeByType('approval')" | ||
| 532 | + > | ||
| 533 | + 新增审批节点 | ||
| 534 | + </button> | ||
| 535 | + <button | ||
| 536 | + type="button" | ||
| 537 | + class="side-action-button" | ||
| 538 | + data-testid="add-copy-node" | ||
| 539 | + @click="addNodeByType('cc')" | ||
| 540 | + > | ||
| 541 | + 新增抄送节点 | ||
| 542 | + </button> | ||
| 543 | + <button | ||
| 544 | + type="button" | ||
| 545 | + class="side-action-button" | ||
| 546 | + @click="commander.selectAll()" | ||
| 547 | + > | ||
| 548 | + 全选节点 | ||
| 549 | + </button> | ||
| 550 | + <button | ||
| 551 | + type="button" | ||
| 552 | + class="side-action-button" | ||
| 553 | + data-testid="create-edge-from-selection" | ||
| 554 | + :disabled="state.selectedNodeIds.length < 2" | ||
| 555 | + @click="createEdgeFromSelection" | ||
| 556 | + > | ||
| 557 | + 基于选中创建连线 | ||
| 558 | + </button> | ||
| 559 | + <button | ||
| 560 | + type="button" | ||
| 561 | + class="side-action-button side-action-button--danger" | ||
| 562 | + data-testid="delete-selected-node" | ||
| 563 | + :disabled="state.selectedNodeIds.length === 0" | ||
| 564 | + @click="commander.delete()" | ||
| 565 | + > | ||
| 566 | + 删除当前节点 | ||
| 567 | + </button> | ||
| 568 | + </div> | ||
| 569 | + </section> | ||
| 570 | + | ||
| 571 | + <section class="side-section"> | ||
| 572 | + <h2>当前选中</h2> | ||
| 573 | + <p data-testid="selected-node-label"> | ||
| 574 | + {{ selectedNode ? `${selectedNode.label}(${selectedNode.id})` : '未选中节点' }} | ||
| 575 | + </p> | ||
| 576 | + <div class="editor-row"> | ||
| 577 | + <input | ||
| 578 | + v-model="selectedNodeNameDraft" | ||
| 579 | + class="node-name-input" | ||
| 580 | + data-testid="selected-node-name-input" | ||
| 581 | + type="text" | ||
| 582 | + placeholder="输入节点名称" | ||
| 583 | + :disabled="!selectedNode" | ||
| 584 | + > | ||
| 585 | + <button | ||
| 586 | + type="button" | ||
| 587 | + class="side-action-button" | ||
| 588 | + data-testid="save-node-name" | ||
| 589 | + :disabled="!selectedNode" | ||
| 590 | + @click="saveSelectedNodeName" | ||
| 591 | + > | ||
| 592 | + 更新节点名 | ||
| 593 | + </button> | ||
| 594 | + </div> | ||
| 595 | + </section> | ||
| 596 | + | ||
| 597 | + <section class="side-section"> | ||
| 598 | + <h2>节点列表</h2> | ||
| 599 | + <div class="node-list"> | ||
| 600 | + <button | ||
| 601 | + v-for="node in state.graphData.nodes" | ||
| 602 | + :key="node.id" | ||
| 603 | + type="button" | ||
| 604 | + class="node-chip" | ||
| 605 | + :data-testid="`node-chip-${node.id}`" | ||
| 606 | + :class="{ 'node-chip--active': state.selectedNodeIds.includes(node.id) }" | ||
| 607 | + @click="toggleNodeSelection(node.id)" | ||
| 608 | + > | ||
| 609 | + {{ node.label }} | ||
| 610 | + </button> | ||
| 611 | + </div> | ||
| 612 | + </section> | ||
| 613 | + | ||
| 614 | + <section class="side-section"> | ||
| 615 | + <h2>命令操作</h2> | ||
| 616 | + <div class="action-grid"> | ||
| 617 | + <button | ||
| 618 | + type="button" | ||
| 619 | + class="side-action-button" | ||
| 620 | + data-testid="command-undo" | ||
| 621 | + :disabled="!canUndo" | ||
| 622 | + @click="commander.undo()" | ||
| 623 | + > | ||
| 624 | + 撤销 | ||
| 625 | + </button> | ||
| 626 | + <button | ||
| 627 | + type="button" | ||
| 628 | + class="side-action-button" | ||
| 629 | + data-testid="command-redo" | ||
| 630 | + :disabled="!canRedo" | ||
| 631 | + @click="commander.redo()" | ||
| 632 | + > | ||
| 633 | + 重做 | ||
| 634 | + </button> | ||
| 635 | + </div> | ||
| 636 | + </section> | ||
| 637 | + | ||
| 638 | + <section class="side-section"> | ||
| 639 | + <h2>兼容层状态</h2> | ||
| 640 | + <p>当前已接通基础工具栏、节点新增、节点选中、删除与预览能力。</p> | ||
| 641 | + <p>下一阶段会继续把命令系统和更多交互迁移到真实 LogicFlow 行为上。</p> | ||
| 642 | + </section> | ||
| 643 | + </aside> | ||
| 644 | + </div> | ||
| 645 | + | ||
| 646 | + <section | ||
| 647 | + v-if="state.isPreviewOpen" | ||
| 648 | + class="flow-editor-preview-panel" | ||
| 649 | + data-testid="preview-panel" | ||
| 650 | + > | ||
| 651 | + <div class="preview-panel-header"> | ||
| 652 | + <h2>流程预览</h2> | ||
| 653 | + <button | ||
| 654 | + type="button" | ||
| 655 | + class="preview-close-button" | ||
| 656 | + @click="state.isPreviewOpen = false" | ||
| 657 | + > | ||
| 658 | + 关闭 | ||
| 659 | + </button> | ||
| 660 | + </div> | ||
| 661 | + <pre class="preview-content">{{ previewGraphJson }}</pre> | ||
| 662 | + </section> | ||
| 663 | + </section> | ||
| 664 | +</template> | ||
| 665 | + | ||
| 666 | +<style scoped> | ||
| 667 | +.vue-flow-editor { | ||
| 668 | + position: relative; | ||
| 669 | + border: 1px solid rgba(24, 49, 79, 0.08); | ||
| 670 | + border-radius: 24px; | ||
| 671 | + background: rgba(255, 255, 255, 0.84); | ||
| 672 | + box-shadow: 0 18px 40px rgba(34, 61, 96, 0.08); | ||
| 673 | + overflow: hidden; | ||
| 674 | +} | ||
| 675 | + | ||
| 676 | +.vue-flow-editor-toolbar { | ||
| 677 | + display: flex; | ||
| 678 | + flex-wrap: wrap; | ||
| 679 | + gap: 10px; | ||
| 680 | + align-items: center; | ||
| 681 | + padding: 18px; | ||
| 682 | + border-bottom: 1px solid rgba(24, 49, 79, 0.08); | ||
| 683 | + background: linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(247, 250, 255, 0.92)); | ||
| 684 | +} | ||
| 685 | + | ||
| 686 | +.toolbar-button { | ||
| 687 | + padding: 9px 14px; | ||
| 688 | + border: 1px solid rgba(24, 49, 79, 0.12); | ||
| 689 | + border-radius: 999px; | ||
| 690 | + background: #ffffff; | ||
| 691 | + color: #18314f; | ||
| 692 | + font-size: 13px; | ||
| 693 | + cursor: pointer; | ||
| 694 | +} | ||
| 695 | + | ||
| 696 | +.vue-flow-editor-body { | ||
| 697 | + display: grid; | ||
| 698 | + grid-template-columns: minmax(0, 1fr) 280px; | ||
| 699 | + gap: 0; | ||
| 700 | +} | ||
| 701 | + | ||
| 702 | +.vue-flow-editor-canvas-target { | ||
| 703 | + position: relative; | ||
| 704 | + padding: 24px; | ||
| 705 | + background: | ||
| 706 | + linear-gradient(90deg, rgba(64, 126, 201, 0.06) 1px, transparent 1px), | ||
| 707 | + linear-gradient(rgba(64, 126, 201, 0.06) 1px, transparent 1px), | ||
| 708 | + linear-gradient(180deg, #fdfefe 0%, #f4f7fb 100%); | ||
| 709 | + background-size: 24px 24px, 24px 24px, auto; | ||
| 710 | +} | ||
| 711 | + | ||
| 712 | +.logicflow-stage { | ||
| 713 | + position: absolute; | ||
| 714 | + inset: 72px 24px 96px; | ||
| 715 | + border-radius: 20px; | ||
| 716 | + overflow: hidden; | ||
| 717 | +} | ||
| 718 | + | ||
| 719 | +.canvas-badge-list { | ||
| 720 | + position: relative; | ||
| 721 | + z-index: 2; | ||
| 722 | + display: flex; | ||
| 723 | + flex-wrap: wrap; | ||
| 724 | + gap: 10px; | ||
| 725 | + margin-bottom: 28px; | ||
| 726 | +} | ||
| 727 | + | ||
| 728 | +.canvas-badge { | ||
| 729 | + padding: 6px 10px; | ||
| 730 | + border-radius: 999px; | ||
| 731 | + background: rgba(43, 104, 168, 0.1); | ||
| 732 | + color: #2b68a8; | ||
| 733 | + font-size: 12px; | ||
| 734 | +} | ||
| 735 | + | ||
| 736 | +.canvas-badge strong { | ||
| 737 | + margin-left: 6px; | ||
| 738 | +} | ||
| 739 | + | ||
| 740 | +.graph-summary { | ||
| 741 | + position: relative; | ||
| 742 | + z-index: 2; | ||
| 743 | + display: grid; | ||
| 744 | + grid-template-columns: repeat(3, minmax(0, 1fr)); | ||
| 745 | + gap: 12px; | ||
| 746 | + margin-top: 24px; | ||
| 747 | +} | ||
| 748 | + | ||
| 749 | +.graph-summary div { | ||
| 750 | + padding: 14px; | ||
| 751 | + border-radius: 16px; | ||
| 752 | + background: rgba(255, 255, 255, 0.92); | ||
| 753 | +} | ||
| 754 | + | ||
| 755 | +.graph-summary span { | ||
| 756 | + display: block; | ||
| 757 | + margin-bottom: 6px; | ||
| 758 | + font-size: 12px; | ||
| 759 | + color: #52749b; | ||
| 760 | +} | ||
| 761 | + | ||
| 762 | +.graph-summary strong { | ||
| 763 | + font-size: 20px; | ||
| 764 | +} | ||
| 765 | + | ||
| 766 | +.flow-editor-side-panel { | ||
| 767 | + padding: 24px 20px; | ||
| 768 | + border-left: 1px solid rgba(24, 49, 79, 0.08); | ||
| 769 | + background: rgba(249, 251, 255, 0.9); | ||
| 770 | +} | ||
| 771 | + | ||
| 772 | +.flow-editor-side-panel h2 { | ||
| 773 | + margin: 0 0 12px; | ||
| 774 | + font-size: 18px; | ||
| 775 | +} | ||
| 776 | + | ||
| 777 | +.flow-editor-side-panel p { | ||
| 778 | + margin: 0 0 10px; | ||
| 779 | + line-height: 1.6; | ||
| 780 | +} | ||
| 781 | + | ||
| 782 | +.side-section + .side-section { | ||
| 783 | + margin-top: 20px; | ||
| 784 | +} | ||
| 785 | + | ||
| 786 | +.action-grid { | ||
| 787 | + display: grid; | ||
| 788 | + grid-template-columns: 1fr 1fr; | ||
| 789 | + gap: 10px; | ||
| 790 | +} | ||
| 791 | + | ||
| 792 | +.side-action-button, | ||
| 793 | +.node-chip, | ||
| 794 | +.preview-close-button { | ||
| 795 | + padding: 10px 12px; | ||
| 796 | + border: 1px solid rgba(24, 49, 79, 0.12); | ||
| 797 | + border-radius: 14px; | ||
| 798 | + background: #ffffff; | ||
| 799 | + color: #18314f; | ||
| 800 | + cursor: pointer; | ||
| 801 | +} | ||
| 802 | + | ||
| 803 | +.side-action-button:disabled { | ||
| 804 | + cursor: not-allowed; | ||
| 805 | + opacity: 0.45; | ||
| 806 | +} | ||
| 807 | + | ||
| 808 | +.editor-row { | ||
| 809 | + display: flex; | ||
| 810 | + gap: 8px; | ||
| 811 | +} | ||
| 812 | + | ||
| 813 | +.node-name-input { | ||
| 814 | + flex: 1; | ||
| 815 | + min-width: 0; | ||
| 816 | + padding: 10px 12px; | ||
| 817 | + border: 1px solid rgba(24, 49, 79, 0.12); | ||
| 818 | + border-radius: 14px; | ||
| 819 | + background: #ffffff; | ||
| 820 | + color: #18314f; | ||
| 821 | +} | ||
| 822 | + | ||
| 823 | +.side-action-button--danger { | ||
| 824 | + color: #b42318; | ||
| 825 | +} | ||
| 826 | + | ||
| 827 | +.node-list { | ||
| 828 | + display: flex; | ||
| 829 | + flex-wrap: wrap; | ||
| 830 | + gap: 8px; | ||
| 831 | +} | ||
| 832 | + | ||
| 833 | +.node-chip--active { | ||
| 834 | + border-color: #2b68a8; | ||
| 835 | + background: rgba(43, 104, 168, 0.1); | ||
| 836 | + color: #2b68a8; | ||
| 837 | +} | ||
| 838 | + | ||
| 839 | +.flow-editor-preview-panel { | ||
| 840 | + position: absolute; | ||
| 841 | + right: 28px; | ||
| 842 | + bottom: 28px; | ||
| 843 | + width: min(520px, calc(100vw - 56px)); | ||
| 844 | + max-height: 60vh; | ||
| 845 | + padding: 18px; | ||
| 846 | + border: 1px solid rgba(24, 49, 79, 0.1); | ||
| 847 | + border-radius: 20px; | ||
| 848 | + background: rgba(255, 255, 255, 0.96); | ||
| 849 | + box-shadow: 0 24px 40px rgba(34, 61, 96, 0.16); | ||
| 850 | + overflow: hidden; | ||
| 851 | +} | ||
| 852 | + | ||
| 853 | +.preview-panel-header { | ||
| 854 | + display: flex; | ||
| 855 | + align-items: center; | ||
| 856 | + justify-content: space-between; | ||
| 857 | + gap: 12px; | ||
| 858 | + margin-bottom: 12px; | ||
| 859 | +} | ||
| 860 | + | ||
| 861 | +.preview-panel-header h2 { | ||
| 862 | + margin: 0; | ||
| 863 | + font-size: 18px; | ||
| 864 | +} | ||
| 865 | + | ||
| 866 | +.preview-content { | ||
| 867 | + margin: 0; | ||
| 868 | + max-height: calc(60vh - 80px); | ||
| 869 | + overflow: auto; | ||
| 870 | + padding: 14px; | ||
| 871 | + border-radius: 16px; | ||
| 872 | + background: #f4f7fb; | ||
| 873 | + color: #18314f; | ||
| 874 | + font-size: 12px; | ||
| 875 | + line-height: 1.6; | ||
| 876 | +} | ||
| 877 | + | ||
| 878 | +@media (max-width: 960px) { | ||
| 879 | + .vue-flow-editor-body { | ||
| 880 | + grid-template-columns: 1fr; | ||
| 881 | + } | ||
| 882 | + | ||
| 883 | + .flow-editor-side-panel { | ||
| 884 | + border-left: none; | ||
| 885 | + border-top: 1px solid rgba(24, 49, 79, 0.08); | ||
| 886 | + } | ||
| 887 | + | ||
| 888 | + .graph-summary { | ||
| 889 | + grid-template-columns: 1fr; | ||
| 890 | + } | ||
| 891 | + | ||
| 892 | + .action-grid { | ||
| 893 | + grid-template-columns: 1fr; | ||
| 894 | + } | ||
| 895 | + | ||
| 896 | + .flow-editor-preview-panel { | ||
| 897 | + left: 16px; | ||
| 898 | + right: 16px; | ||
| 899 | + width: auto; | ||
| 900 | + } | ||
| 901 | +} | ||
| 902 | +</style> |
| 1 | +import type { FlowEdgeData, FlowGraphData, FlowNodeData } from '../../types' | ||
| 2 | +import type { | ||
| 3 | + FlowEditorInputEdge, | ||
| 4 | + FlowEditorInputGraphData, | ||
| 5 | + FlowEditorInputNode, | ||
| 6 | + LogicFlowGraphData, | ||
| 7 | +} from '../schema/types' | ||
| 8 | + | ||
| 9 | +function isLegacyEdge(edge: FlowEditorInputEdge): edge is { | ||
| 10 | + source?: string | ||
| 11 | + target?: string | ||
| 12 | + sourceAnchor?: number | ||
| 13 | + targetAnchor?: number | ||
| 14 | + [key: string]: unknown | ||
| 15 | +} { | ||
| 16 | + return 'source' in edge || 'target' in edge | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +function isLegacyNode(node: FlowEditorInputNode): node is { | ||
| 20 | + text?: string | ||
| 21 | + shape?: string | ||
| 22 | + activity?: string | ||
| 23 | + control?: string | ||
| 24 | + [key: string]: unknown | ||
| 25 | +} { | ||
| 26 | + return 'shape' in node || 'text' in node || 'activity' in node || 'control' in node | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +function inferNodeType(node: FlowEditorInputNode): string { | ||
| 30 | + if ('type' in node && typeof node.type === 'string' && node.type) { | ||
| 31 | + return node.type | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + if ('id' in node && node.id === 'start-node') { | ||
| 35 | + return 'start' | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + if ('id' in node && node.id === 'end-node') { | ||
| 39 | + return 'end' | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + if (isLegacyNode(node)) { | ||
| 43 | + if (node.activity) { | ||
| 44 | + return 'activity' | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + if (node.control) { | ||
| 48 | + return 'control' | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + if (node.shape === 'start' || node.shape === 'start-node') { | ||
| 52 | + return 'start' | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + if (node.shape === 'end' || node.shape === 'end-node') { | ||
| 56 | + return 'end' | ||
| 57 | + } | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + return 'custom' | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +function inferLogicFlowNodeType(node: FlowNodeData): string { | ||
| 64 | + if (node.type === 'start' || node.type === 'end') { | ||
| 65 | + return 'circle' | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + return 'rect' | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +function normalizeNode(node: FlowEditorInputNode): FlowNodeData { | ||
| 72 | + const label = 'label' in node && typeof node.label === 'string' && node.label | ||
| 73 | + ? node.label | ||
| 74 | + : isLegacyNode(node) && typeof node.text === 'string' && node.text | ||
| 75 | + ? node.text | ||
| 76 | + : node.id | ||
| 77 | + | ||
| 78 | + return { | ||
| 79 | + ...node, | ||
| 80 | + id: node.id, | ||
| 81 | + label, | ||
| 82 | + type: inferNodeType(node), | ||
| 83 | + x: typeof node.x === 'number' ? node.x : 120, | ||
| 84 | + y: typeof node.y === 'number' ? node.y : 120, | ||
| 85 | + } | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +function normalizeEdge(edge: FlowEditorInputEdge, index: number): FlowEdgeData { | ||
| 89 | + if (!isLegacyEdge(edge)) { | ||
| 90 | + return { | ||
| 91 | + ...edge, | ||
| 92 | + id: edge.id || `edge-${edge.sourceNodeId}-${edge.targetNodeId}-${index}`, | ||
| 93 | + label: edge.label ?? '', | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + const sourceNodeId = typeof edge.source === 'string' ? edge.source : '' | ||
| 98 | + const targetNodeId = typeof edge.target === 'string' ? edge.target : '' | ||
| 99 | + const label = typeof edge.label === 'string' && edge.label | ||
| 100 | + ? edge.label | ||
| 101 | + : typeof edge.text === 'string' | ||
| 102 | + ? edge.text | ||
| 103 | + : '' | ||
| 104 | + | ||
| 105 | + return { | ||
| 106 | + ...edge, | ||
| 107 | + id: typeof edge.id === 'string' && edge.id ? edge.id : `edge-${sourceNodeId}-${targetNodeId}-${index}`, | ||
| 108 | + sourceNodeId, | ||
| 109 | + targetNodeId, | ||
| 110 | + label, | ||
| 111 | + } | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +export function normalizeGraphData(graphData: FlowEditorInputGraphData): FlowGraphData { | ||
| 115 | + return { | ||
| 116 | + nodes: graphData.nodes.map((node) => normalizeNode(node)), | ||
| 117 | + edges: graphData.edges.map((edge, index) => normalizeEdge(edge, index)), | ||
| 118 | + } | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +export function cloneNormalizedGraphData(graphData: FlowGraphData): FlowGraphData { | ||
| 122 | + return { | ||
| 123 | + nodes: graphData.nodes.map((node) => ({ ...node })), | ||
| 124 | + edges: graphData.edges.map((edge) => ({ ...edge })), | ||
| 125 | + } | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +export function toLogicFlowGraphData(graphData: FlowEditorInputGraphData): LogicFlowGraphData { | ||
| 129 | + const normalizedGraph = normalizeGraphData(graphData) | ||
| 130 | + | ||
| 131 | + return { | ||
| 132 | + nodes: normalizedGraph.nodes.map((node) => ({ | ||
| 133 | + id: node.id, | ||
| 134 | + type: inferLogicFlowNodeType(node), | ||
| 135 | + x: node.x ?? 120, | ||
| 136 | + y: node.y ?? 120, | ||
| 137 | + text: node.label, | ||
| 138 | + properties: { | ||
| 139 | + ...node, | ||
| 140 | + }, | ||
| 141 | + })), | ||
| 142 | + edges: normalizedGraph.edges.map((edge) => ({ | ||
| 143 | + id: edge.id, | ||
| 144 | + type: 'polyline', | ||
| 145 | + sourceNodeId: edge.sourceNodeId, | ||
| 146 | + targetNodeId: edge.targetNodeId, | ||
| 147 | + text: edge.label, | ||
| 148 | + properties: { | ||
| 149 | + ...edge, | ||
| 150 | + }, | ||
| 151 | + })), | ||
| 152 | + } | ||
| 153 | +} | ||
| 154 | + |
| 1 | +import type { FlowEdgeData, FlowGraphData, FlowNodeData } from '../../types' | ||
| 2 | + | ||
| 3 | +export interface LegacyFlowNode { | ||
| 4 | + id: string | ||
| 5 | + x?: number | ||
| 6 | + y?: number | ||
| 7 | + text?: string | ||
| 8 | + label?: string | ||
| 9 | + shape?: string | ||
| 10 | + type?: string | ||
| 11 | + desc?: string | ||
| 12 | + activity?: string | ||
| 13 | + control?: string | ||
| 14 | + data?: Record<string, unknown> | ||
| 15 | + [key: string]: unknown | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +export interface LegacyFlowEdge { | ||
| 19 | + id?: string | ||
| 20 | + shape?: string | ||
| 21 | + source?: string | ||
| 22 | + target?: string | ||
| 23 | + sourceAnchor?: number | ||
| 24 | + targetAnchor?: number | ||
| 25 | + text?: string | ||
| 26 | + label?: string | ||
| 27 | + [key: string]: unknown | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +export interface LegacyFlowGraphData { | ||
| 31 | + nodes: LegacyFlowNode[] | ||
| 32 | + edges: LegacyFlowEdge[] | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +export interface LogicFlowNodeData { | ||
| 36 | + id: string | ||
| 37 | + type: string | ||
| 38 | + x: number | ||
| 39 | + y: number | ||
| 40 | + text: string | ||
| 41 | + properties: Record<string, unknown> | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +export interface LogicFlowEdgeData { | ||
| 45 | + id: string | ||
| 46 | + type: string | ||
| 47 | + sourceNodeId: string | ||
| 48 | + targetNodeId: string | ||
| 49 | + text?: string | ||
| 50 | + properties: Record<string, unknown> | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +export interface LogicFlowGraphData { | ||
| 54 | + nodes: LogicFlowNodeData[] | ||
| 55 | + edges: LogicFlowEdgeData[] | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +export type FlowEditorInputNode = FlowNodeData | LegacyFlowNode | ||
| 59 | +export type FlowEditorInputEdge = FlowEdgeData | LegacyFlowEdge | ||
| 60 | +export type FlowEditorInputGraphData = FlowGraphData | LegacyFlowGraphData | ||
| 61 | + |
packages/flow-editor/src/index.ts
0 → 100644
| 1 | +export { default as FlowEditor } from './components/FlowEditor.vue' | ||
| 2 | +export { | ||
| 3 | + cloneNormalizedGraphData, | ||
| 4 | + normalizeGraphData, | ||
| 5 | + toLogicFlowGraphData, | ||
| 6 | +} from './core/adapter/g6-to-logicflow' | ||
| 7 | +export type { | ||
| 8 | + FlowEditorCommander, | ||
| 9 | + FlowEditorRef, | ||
| 10 | + FlowEditorState, | ||
| 11 | + FlowEdgeData, | ||
| 12 | + FlowGraphData, | ||
| 13 | + FlowNodeData, | ||
| 14 | + ToolbarButtonDefinition, | ||
| 15 | +} from './types' | ||
| 16 | +export type { | ||
| 17 | + FlowEditorInputGraphData, | ||
| 18 | + LegacyFlowEdge, | ||
| 19 | + LegacyFlowGraphData, | ||
| 20 | + LegacyFlowNode, | ||
| 21 | + LogicFlowEdgeData, | ||
| 22 | + LogicFlowGraphData, | ||
| 23 | + LogicFlowNodeData, | ||
| 24 | +} from './core/schema/types' |
packages/flow-editor/src/types.ts
0 → 100644
| 1 | +export interface FlowNodeData { | ||
| 2 | + id: string | ||
| 3 | + label: string | ||
| 4 | + type?: string | ||
| 5 | + x?: number | ||
| 6 | + y?: number | ||
| 7 | + [key: string]: unknown | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | +export interface FlowEdgeData { | ||
| 11 | + id: string | ||
| 12 | + sourceNodeId: string | ||
| 13 | + targetNodeId: string | ||
| 14 | + label?: string | ||
| 15 | + [key: string]: unknown | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +export interface FlowGraphData { | ||
| 19 | + nodes: FlowNodeData[] | ||
| 20 | + edges: FlowEdgeData[] | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +export interface ToolbarButtonDefinition { | ||
| 24 | + key: string | ||
| 25 | + label: string | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +export interface FlowEditorState { | ||
| 29 | + canvasProps: { | ||
| 30 | + grid: boolean | ||
| 31 | + miniMap: boolean | ||
| 32 | + zoom: number | ||
| 33 | + } | ||
| 34 | + graphData: FlowGraphData | ||
| 35 | + selectedNodeIds: string[] | ||
| 36 | + isModelOpen: boolean | ||
| 37 | + isPreviewOpen: boolean | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +export interface FlowEditorCommander { | ||
| 41 | + switchGrid: () => void | ||
| 42 | + switchMiniMap: () => void | ||
| 43 | + fitView: () => void | ||
| 44 | + actualView: () => void | ||
| 45 | + zoomIn: () => void | ||
| 46 | + zoomOut: () => void | ||
| 47 | + delete: () => void | ||
| 48 | + undo: () => void | ||
| 49 | + redo: () => void | ||
| 50 | + selectAll: () => void | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +export interface FlowEditorRef { | ||
| 54 | + editorState: FlowEditorState | ||
| 55 | + commander: FlowEditorCommander | ||
| 56 | + openModel: () => void | ||
| 57 | + closeModel: () => void | ||
| 58 | + addNode: (node?: Partial<FlowNodeData>) => FlowNodeData | ||
| 59 | + updateModel: (patch?: Record<string, unknown>) => void | ||
| 60 | + openPreview: () => void | ||
| 61 | + read: (graphData: FlowGraphData) => void | ||
| 62 | + clearStates: () => void | ||
| 63 | +} | ||
| 64 | + |
playwright.config.ts
0 → 100644
pnpm-lock.yaml
0 → 100644
| 1 | +lockfileVersion: '9.0' | ||
| 2 | + | ||
| 3 | +settings: | ||
| 4 | + autoInstallPeers: true | ||
| 5 | + excludeLinksFromLockfile: false | ||
| 6 | + | ||
| 7 | +importers: | ||
| 8 | + | ||
| 9 | + .: | ||
| 10 | + dependencies: | ||
| 11 | + '@logicflow/core': | ||
| 12 | + specifier: ^2.1.11 | ||
| 13 | + version: 2.1.11 | ||
| 14 | + '@logicflow/extension': | ||
| 15 | + specifier: ^2.1.15 | ||
| 16 | + version: 2.1.15(@logicflow/core@2.1.11)(@logicflow/vue-node-registry@1.1.13(@logicflow/core@2.1.11)(vue@3.5.31(typescript@6.0.2))) | ||
| 17 | + element-plus: | ||
| 18 | + specifier: ^2.13.6 | ||
| 19 | + version: 2.13.6(typescript@6.0.2)(vue@3.5.31(typescript@6.0.2)) | ||
| 20 | + vue: | ||
| 21 | + specifier: ^3.5.31 | ||
| 22 | + version: 3.5.31(typescript@6.0.2) | ||
| 23 | + devDependencies: | ||
| 24 | + '@playwright/test': | ||
| 25 | + specifier: ^1.58.2 | ||
| 26 | + version: 1.58.2 | ||
| 27 | + '@types/node': | ||
| 28 | + specifier: ^25.5.0 | ||
| 29 | + version: 25.5.0 | ||
| 30 | + '@vitejs/plugin-vue': | ||
| 31 | + specifier: ^6.0.5 | ||
| 32 | + version: 6.0.5(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0))(vue@3.5.31(typescript@6.0.2)) | ||
| 33 | + '@vue/test-utils': | ||
| 34 | + specifier: ^2.4.6 | ||
| 35 | + version: 2.4.6 | ||
| 36 | + jsdom: | ||
| 37 | + specifier: ^29.0.1 | ||
| 38 | + version: 29.0.1 | ||
| 39 | + typescript: | ||
| 40 | + specifier: ^6.0.2 | ||
| 41 | + version: 6.0.2 | ||
| 42 | + vite: | ||
| 43 | + specifier: ^8.0.3 | ||
| 44 | + version: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0) | ||
| 45 | + vitest: | ||
| 46 | + specifier: ^4.1.2 | ||
| 47 | + version: 4.1.2(@types/node@25.5.0)(jsdom@29.0.1)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)) | ||
| 48 | + vue-tsc: | ||
| 49 | + specifier: ^3.2.6 | ||
| 50 | + version: 3.2.6(typescript@6.0.2) | ||
| 51 | + | ||
| 52 | +packages: | ||
| 53 | + | ||
| 54 | + '@antv/hierarchy@0.6.14': | ||
| 55 | + resolution: {integrity: sha512-V3uknf7bhynOqQDw2sg+9r9DwZ9pc6k/EcqyTFdfXB1+ydr7urisP0MipIuimucvQKN+Qkd+d6w601r1UIroqQ==} | ||
| 56 | + | ||
| 57 | + '@asamuzakjp/css-color@5.1.1': | ||
| 58 | + resolution: {integrity: sha512-iGWN8E45Ws0XWx3D44Q1t6vX2LqhCKcwfmwBYCDsFrYFS6m4q/Ks61L2veETaLv+ckDC6+dTETJoaAAb7VjLiw==} | ||
| 59 | + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} | ||
| 60 | + | ||
| 61 | + '@asamuzakjp/dom-selector@7.0.4': | ||
| 62 | + resolution: {integrity: sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==} | ||
| 63 | + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} | ||
| 64 | + | ||
| 65 | + '@asamuzakjp/nwsapi@2.3.9': | ||
| 66 | + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} | ||
| 67 | + | ||
| 68 | + '@babel/helper-string-parser@7.27.1': | ||
| 69 | + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} | ||
| 70 | + engines: {node: '>=6.9.0'} | ||
| 71 | + | ||
| 72 | + '@babel/helper-validator-identifier@7.28.5': | ||
| 73 | + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} | ||
| 74 | + engines: {node: '>=6.9.0'} | ||
| 75 | + | ||
| 76 | + '@babel/parser@7.29.2': | ||
| 77 | + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} | ||
| 78 | + engines: {node: '>=6.0.0'} | ||
| 79 | + hasBin: true | ||
| 80 | + | ||
| 81 | + '@babel/types@7.29.0': | ||
| 82 | + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} | ||
| 83 | + engines: {node: '>=6.9.0'} | ||
| 84 | + | ||
| 85 | + '@bramus/specificity@2.4.2': | ||
| 86 | + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} | ||
| 87 | + hasBin: true | ||
| 88 | + | ||
| 89 | + '@csstools/color-helpers@6.0.2': | ||
| 90 | + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} | ||
| 91 | + engines: {node: '>=20.19.0'} | ||
| 92 | + | ||
| 93 | + '@csstools/css-calc@3.1.1': | ||
| 94 | + resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} | ||
| 95 | + engines: {node: '>=20.19.0'} | ||
| 96 | + peerDependencies: | ||
| 97 | + '@csstools/css-parser-algorithms': ^4.0.0 | ||
| 98 | + '@csstools/css-tokenizer': ^4.0.0 | ||
| 99 | + | ||
| 100 | + '@csstools/css-color-parser@4.0.2': | ||
| 101 | + resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} | ||
| 102 | + engines: {node: '>=20.19.0'} | ||
| 103 | + peerDependencies: | ||
| 104 | + '@csstools/css-parser-algorithms': ^4.0.0 | ||
| 105 | + '@csstools/css-tokenizer': ^4.0.0 | ||
| 106 | + | ||
| 107 | + '@csstools/css-parser-algorithms@4.0.0': | ||
| 108 | + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} | ||
| 109 | + engines: {node: '>=20.19.0'} | ||
| 110 | + peerDependencies: | ||
| 111 | + '@csstools/css-tokenizer': ^4.0.0 | ||
| 112 | + | ||
| 113 | + '@csstools/css-syntax-patches-for-csstree@1.1.2': | ||
| 114 | + resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==} | ||
| 115 | + peerDependencies: | ||
| 116 | + css-tree: ^3.2.1 | ||
| 117 | + peerDependenciesMeta: | ||
| 118 | + css-tree: | ||
| 119 | + optional: true | ||
| 120 | + | ||
| 121 | + '@csstools/css-tokenizer@4.0.0': | ||
| 122 | + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} | ||
| 123 | + engines: {node: '>=20.19.0'} | ||
| 124 | + | ||
| 125 | + '@ctrl/tinycolor@4.2.0': | ||
| 126 | + resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} | ||
| 127 | + engines: {node: '>=14'} | ||
| 128 | + | ||
| 129 | + '@element-plus/icons-vue@2.3.2': | ||
| 130 | + resolution: {integrity: sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==} | ||
| 131 | + peerDependencies: | ||
| 132 | + vue: ^3.2.0 | ||
| 133 | + | ||
| 134 | + '@emnapi/core@1.9.1': | ||
| 135 | + resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} | ||
| 136 | + | ||
| 137 | + '@emnapi/runtime@1.9.1': | ||
| 138 | + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} | ||
| 139 | + | ||
| 140 | + '@emnapi/wasi-threads@1.2.0': | ||
| 141 | + resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} | ||
| 142 | + | ||
| 143 | + '@exodus/bytes@1.15.0': | ||
| 144 | + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} | ||
| 145 | + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} | ||
| 146 | + peerDependencies: | ||
| 147 | + '@noble/hashes': ^1.8.0 || ^2.0.0 | ||
| 148 | + peerDependenciesMeta: | ||
| 149 | + '@noble/hashes': | ||
| 150 | + optional: true | ||
| 151 | + | ||
| 152 | + '@floating-ui/core@1.7.5': | ||
| 153 | + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} | ||
| 154 | + | ||
| 155 | + '@floating-ui/dom@1.7.6': | ||
| 156 | + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} | ||
| 157 | + | ||
| 158 | + '@floating-ui/utils@0.2.11': | ||
| 159 | + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} | ||
| 160 | + | ||
| 161 | + '@isaacs/cliui@8.0.2': | ||
| 162 | + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} | ||
| 163 | + engines: {node: '>=12'} | ||
| 164 | + | ||
| 165 | + '@jridgewell/sourcemap-codec@1.5.5': | ||
| 166 | + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} | ||
| 167 | + | ||
| 168 | + '@logicflow/core@2.1.11': | ||
| 169 | + resolution: {integrity: sha512-PXpuJ4UdNcLYcPq6rWdwAOz4A1GfHC3OAWOWc9/sTLE7we3MC3+97ERoBD7ICf6mFHDDKYvNHoQXU/MgzhQa0Q==} | ||
| 170 | + | ||
| 171 | + '@logicflow/extension@2.1.15': | ||
| 172 | + resolution: {integrity: sha512-Hl9lM1GfOwxahC1zb3bDz3xTwVpnABlFHHKn2hKT8YycexAr2dcpQ1IJB329ZhwWAgtpovrF5NDyGmeHn/3cEA==} | ||
| 173 | + peerDependencies: | ||
| 174 | + '@logicflow/core': 2.1.11 | ||
| 175 | + '@logicflow/vue-node-registry': 1.1.13 | ||
| 176 | + | ||
| 177 | + '@logicflow/vue-node-registry@1.1.13': | ||
| 178 | + resolution: {integrity: sha512-CqLOSidTOXWbDF2JOzCDnY0kLr8oTAHEZNfYhmFcRuiCFwdDhR91+havDQQXIrerpQgz0zLaQBGbwviaFt1eCQ==} | ||
| 179 | + peerDependencies: | ||
| 180 | + '@logicflow/core': 2.1.11 | ||
| 181 | + '@vue/composition-api': ^1.0.0-rc.10 | ||
| 182 | + vue: ^2.0.0 || >=3.0.0 | ||
| 183 | + peerDependenciesMeta: | ||
| 184 | + '@vue/composition-api': | ||
| 185 | + optional: true | ||
| 186 | + | ||
| 187 | + '@napi-rs/wasm-runtime@1.1.2': | ||
| 188 | + resolution: {integrity: sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==} | ||
| 189 | + peerDependencies: | ||
| 190 | + '@emnapi/core': ^1.7.1 | ||
| 191 | + '@emnapi/runtime': ^1.7.1 | ||
| 192 | + | ||
| 193 | + '@one-ini/wasm@0.1.1': | ||
| 194 | + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} | ||
| 195 | + | ||
| 196 | + '@oxc-project/types@0.122.0': | ||
| 197 | + resolution: {integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==} | ||
| 198 | + | ||
| 199 | + '@pkgjs/parseargs@0.11.0': | ||
| 200 | + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} | ||
| 201 | + engines: {node: '>=14'} | ||
| 202 | + | ||
| 203 | + '@playwright/test@1.58.2': | ||
| 204 | + resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==} | ||
| 205 | + engines: {node: '>=18'} | ||
| 206 | + hasBin: true | ||
| 207 | + | ||
| 208 | + '@rolldown/binding-android-arm64@1.0.0-rc.12': | ||
| 209 | + resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==} | ||
| 210 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 211 | + cpu: [arm64] | ||
| 212 | + os: [android] | ||
| 213 | + | ||
| 214 | + '@rolldown/binding-darwin-arm64@1.0.0-rc.12': | ||
| 215 | + resolution: {integrity: sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==} | ||
| 216 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 217 | + cpu: [arm64] | ||
| 218 | + os: [darwin] | ||
| 219 | + | ||
| 220 | + '@rolldown/binding-darwin-x64@1.0.0-rc.12': | ||
| 221 | + resolution: {integrity: sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==} | ||
| 222 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 223 | + cpu: [x64] | ||
| 224 | + os: [darwin] | ||
| 225 | + | ||
| 226 | + '@rolldown/binding-freebsd-x64@1.0.0-rc.12': | ||
| 227 | + resolution: {integrity: sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==} | ||
| 228 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 229 | + cpu: [x64] | ||
| 230 | + os: [freebsd] | ||
| 231 | + | ||
| 232 | + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': | ||
| 233 | + resolution: {integrity: sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==} | ||
| 234 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 235 | + cpu: [arm] | ||
| 236 | + os: [linux] | ||
| 237 | + | ||
| 238 | + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': | ||
| 239 | + resolution: {integrity: sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==} | ||
| 240 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 241 | + cpu: [arm64] | ||
| 242 | + os: [linux] | ||
| 243 | + | ||
| 244 | + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': | ||
| 245 | + resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==} | ||
| 246 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 247 | + cpu: [arm64] | ||
| 248 | + os: [linux] | ||
| 249 | + | ||
| 250 | + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': | ||
| 251 | + resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==} | ||
| 252 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 253 | + cpu: [ppc64] | ||
| 254 | + os: [linux] | ||
| 255 | + | ||
| 256 | + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': | ||
| 257 | + resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==} | ||
| 258 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 259 | + cpu: [s390x] | ||
| 260 | + os: [linux] | ||
| 261 | + | ||
| 262 | + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': | ||
| 263 | + resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==} | ||
| 264 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 265 | + cpu: [x64] | ||
| 266 | + os: [linux] | ||
| 267 | + | ||
| 268 | + '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': | ||
| 269 | + resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==} | ||
| 270 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 271 | + cpu: [x64] | ||
| 272 | + os: [linux] | ||
| 273 | + | ||
| 274 | + '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': | ||
| 275 | + resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==} | ||
| 276 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 277 | + cpu: [arm64] | ||
| 278 | + os: [openharmony] | ||
| 279 | + | ||
| 280 | + '@rolldown/binding-wasm32-wasi@1.0.0-rc.12': | ||
| 281 | + resolution: {integrity: sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==} | ||
| 282 | + engines: {node: '>=14.0.0'} | ||
| 283 | + cpu: [wasm32] | ||
| 284 | + | ||
| 285 | + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': | ||
| 286 | + resolution: {integrity: sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==} | ||
| 287 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 288 | + cpu: [arm64] | ||
| 289 | + os: [win32] | ||
| 290 | + | ||
| 291 | + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': | ||
| 292 | + resolution: {integrity: sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==} | ||
| 293 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 294 | + cpu: [x64] | ||
| 295 | + os: [win32] | ||
| 296 | + | ||
| 297 | + '@rolldown/pluginutils@1.0.0-rc.12': | ||
| 298 | + resolution: {integrity: sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==} | ||
| 299 | + | ||
| 300 | + '@rolldown/pluginutils@1.0.0-rc.2': | ||
| 301 | + resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} | ||
| 302 | + | ||
| 303 | + '@sphinxxxx/color-conversion@2.2.2': | ||
| 304 | + resolution: {integrity: sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==} | ||
| 305 | + | ||
| 306 | + '@standard-schema/spec@1.1.0': | ||
| 307 | + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} | ||
| 308 | + | ||
| 309 | + '@sxzz/popperjs-es@2.11.8': | ||
| 310 | + resolution: {integrity: sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==} | ||
| 311 | + | ||
| 312 | + '@tybys/wasm-util@0.10.1': | ||
| 313 | + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} | ||
| 314 | + | ||
| 315 | + '@types/chai@5.2.3': | ||
| 316 | + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} | ||
| 317 | + | ||
| 318 | + '@types/deep-eql@4.0.2': | ||
| 319 | + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} | ||
| 320 | + | ||
| 321 | + '@types/estree@1.0.8': | ||
| 322 | + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} | ||
| 323 | + | ||
| 324 | + '@types/lodash-es@4.17.12': | ||
| 325 | + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} | ||
| 326 | + | ||
| 327 | + '@types/lodash@4.17.24': | ||
| 328 | + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} | ||
| 329 | + | ||
| 330 | + '@types/node@25.5.0': | ||
| 331 | + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} | ||
| 332 | + | ||
| 333 | + '@types/web-bluetooth@0.0.20': | ||
| 334 | + resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} | ||
| 335 | + | ||
| 336 | + '@vitejs/plugin-vue@6.0.5': | ||
| 337 | + resolution: {integrity: sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==} | ||
| 338 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 339 | + peerDependencies: | ||
| 340 | + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 | ||
| 341 | + vue: ^3.2.25 | ||
| 342 | + | ||
| 343 | + '@vitest/expect@4.1.2': | ||
| 344 | + resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} | ||
| 345 | + | ||
| 346 | + '@vitest/mocker@4.1.2': | ||
| 347 | + resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} | ||
| 348 | + peerDependencies: | ||
| 349 | + msw: ^2.4.9 | ||
| 350 | + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 | ||
| 351 | + peerDependenciesMeta: | ||
| 352 | + msw: | ||
| 353 | + optional: true | ||
| 354 | + vite: | ||
| 355 | + optional: true | ||
| 356 | + | ||
| 357 | + '@vitest/pretty-format@4.1.2': | ||
| 358 | + resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} | ||
| 359 | + | ||
| 360 | + '@vitest/runner@4.1.2': | ||
| 361 | + resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} | ||
| 362 | + | ||
| 363 | + '@vitest/snapshot@4.1.2': | ||
| 364 | + resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} | ||
| 365 | + | ||
| 366 | + '@vitest/spy@4.1.2': | ||
| 367 | + resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} | ||
| 368 | + | ||
| 369 | + '@vitest/utils@4.1.2': | ||
| 370 | + resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} | ||
| 371 | + | ||
| 372 | + '@volar/language-core@2.4.28': | ||
| 373 | + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} | ||
| 374 | + | ||
| 375 | + '@volar/source-map@2.4.28': | ||
| 376 | + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} | ||
| 377 | + | ||
| 378 | + '@volar/typescript@2.4.28': | ||
| 379 | + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} | ||
| 380 | + | ||
| 381 | + '@vue/compiler-core@3.5.31': | ||
| 382 | + resolution: {integrity: sha512-k/ueL14aNIEy5Onf0OVzR8kiqF/WThgLdFhxwa4e/KF/0qe38IwIdofoSWBTvvxQOesaz6riAFAUaYjoF9fLLQ==} | ||
| 383 | + | ||
| 384 | + '@vue/compiler-dom@3.5.31': | ||
| 385 | + resolution: {integrity: sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw==} | ||
| 386 | + | ||
| 387 | + '@vue/compiler-sfc@3.5.31': | ||
| 388 | + resolution: {integrity: sha512-M8wpPgR9UJ8MiRGjppvx9uWJfLV7A/T+/rL8s/y3QG3u0c2/YZgff3d6SuimKRIhcYnWg5fTfDMlz2E6seUW8Q==} | ||
| 389 | + | ||
| 390 | + '@vue/compiler-ssr@3.5.31': | ||
| 391 | + resolution: {integrity: sha512-h0xIMxrt/LHOvJKMri+vdYT92BrK3HFLtDqq9Pr/lVVfE4IyKZKvWf0vJFW10Yr6nX02OR4MkJwI0c1HDa1hog==} | ||
| 392 | + | ||
| 393 | + '@vue/language-core@3.2.6': | ||
| 394 | + resolution: {integrity: sha512-xYYYX3/aVup576tP/23sEUpgiEnujrENaoNRbaozC1/MA9I6EGFQRJb4xrt/MmUCAGlxTKL2RmT8JLTPqagCkg==} | ||
| 395 | + | ||
| 396 | + '@vue/reactivity@3.5.31': | ||
| 397 | + resolution: {integrity: sha512-DtKXxk9E/KuVvt8VxWu+6Luc9I9ETNcqR1T1oW1gf02nXaZ1kuAx58oVu7uX9XxJR0iJCro6fqBLw9oSBELo5g==} | ||
| 398 | + | ||
| 399 | + '@vue/runtime-core@3.5.31': | ||
| 400 | + resolution: {integrity: sha512-AZPmIHXEAyhpkmN7aWlqjSfYynmkWlluDNPHMCZKFHH+lLtxP/30UJmoVhXmbDoP1Ng0jG0fyY2zCj1PnSSA6Q==} | ||
| 401 | + | ||
| 402 | + '@vue/runtime-dom@3.5.31': | ||
| 403 | + resolution: {integrity: sha512-xQJsNRmGPeDCJq/u813tyonNgWBFjzfVkBwDREdEWndBnGdHLHgkwNBQxLtg4zDrzKTEcnikUy1UUNecb3lJ6g==} | ||
| 404 | + | ||
| 405 | + '@vue/server-renderer@3.5.31': | ||
| 406 | + resolution: {integrity: sha512-GJuwRvMcdZX/CriUnyIIOGkx3rMV3H6sOu0JhdKbduaeCji6zb60iOGMY7tFoN24NfsUYoFBhshZtGxGpxO4iA==} | ||
| 407 | + peerDependencies: | ||
| 408 | + vue: 3.5.31 | ||
| 409 | + | ||
| 410 | + '@vue/shared@3.5.31': | ||
| 411 | + resolution: {integrity: sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==} | ||
| 412 | + | ||
| 413 | + '@vue/test-utils@2.4.6': | ||
| 414 | + resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} | ||
| 415 | + | ||
| 416 | + '@vueuse/core@12.0.0': | ||
| 417 | + resolution: {integrity: sha512-C12RukhXiJCbx4MGhjmd/gH52TjJsc3G0E0kQj/kb19H3Nt6n1CA4DRWuTdWWcaFRdlTe0npWDS942mvacvNBw==} | ||
| 418 | + | ||
| 419 | + '@vueuse/metadata@12.0.0': | ||
| 420 | + resolution: {integrity: sha512-Yzimd1D3sjxTDOlF05HekU5aSGdKjxhuhRFHA7gDWLn57PRbBIh+SF5NmjhJ0WRgF3my7T8LBucyxdFJjIfRJQ==} | ||
| 421 | + | ||
| 422 | + '@vueuse/shared@12.0.0': | ||
| 423 | + resolution: {integrity: sha512-3i6qtcq2PIio5i/vVYidkkcgvmTjCqrf26u+Fd4LhnbBmIT6FN8y6q/GJERp8lfcB9zVEfjdV0Br0443qZuJpw==} | ||
| 424 | + | ||
| 425 | + abbrev@2.0.0: | ||
| 426 | + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} | ||
| 427 | + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} | ||
| 428 | + | ||
| 429 | + alien-signals@3.1.2: | ||
| 430 | + resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==} | ||
| 431 | + | ||
| 432 | + ansi-regex@5.0.1: | ||
| 433 | + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} | ||
| 434 | + engines: {node: '>=8'} | ||
| 435 | + | ||
| 436 | + ansi-regex@6.2.2: | ||
| 437 | + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} | ||
| 438 | + engines: {node: '>=12'} | ||
| 439 | + | ||
| 440 | + ansi-styles@4.3.0: | ||
| 441 | + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} | ||
| 442 | + engines: {node: '>=8'} | ||
| 443 | + | ||
| 444 | + ansi-styles@6.2.3: | ||
| 445 | + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} | ||
| 446 | + engines: {node: '>=12'} | ||
| 447 | + | ||
| 448 | + assertion-error@2.0.1: | ||
| 449 | + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} | ||
| 450 | + engines: {node: '>=12'} | ||
| 451 | + | ||
| 452 | + async-validator@4.2.5: | ||
| 453 | + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} | ||
| 454 | + | ||
| 455 | + balanced-match@1.0.2: | ||
| 456 | + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} | ||
| 457 | + | ||
| 458 | + bidi-js@1.0.3: | ||
| 459 | + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} | ||
| 460 | + | ||
| 461 | + brace-expansion@2.0.3: | ||
| 462 | + resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} | ||
| 463 | + | ||
| 464 | + chai@6.2.2: | ||
| 465 | + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} | ||
| 466 | + engines: {node: '>=18'} | ||
| 467 | + | ||
| 468 | + classnames@2.5.1: | ||
| 469 | + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} | ||
| 470 | + | ||
| 471 | + color-convert@2.0.1: | ||
| 472 | + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} | ||
| 473 | + engines: {node: '>=7.0.0'} | ||
| 474 | + | ||
| 475 | + color-name@1.1.4: | ||
| 476 | + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} | ||
| 477 | + | ||
| 478 | + commander@10.0.1: | ||
| 479 | + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} | ||
| 480 | + engines: {node: '>=14'} | ||
| 481 | + | ||
| 482 | + config-chain@1.1.13: | ||
| 483 | + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} | ||
| 484 | + | ||
| 485 | + convert-source-map@2.0.0: | ||
| 486 | + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} | ||
| 487 | + | ||
| 488 | + cross-spawn@7.0.6: | ||
| 489 | + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} | ||
| 490 | + engines: {node: '>= 8'} | ||
| 491 | + | ||
| 492 | + css-tree@3.2.1: | ||
| 493 | + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} | ||
| 494 | + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} | ||
| 495 | + | ||
| 496 | + csstype@3.2.3: | ||
| 497 | + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} | ||
| 498 | + | ||
| 499 | + data-urls@7.0.0: | ||
| 500 | + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} | ||
| 501 | + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} | ||
| 502 | + | ||
| 503 | + dayjs@1.11.20: | ||
| 504 | + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} | ||
| 505 | + | ||
| 506 | + decimal.js@10.6.0: | ||
| 507 | + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} | ||
| 508 | + | ||
| 509 | + detect-libc@2.1.2: | ||
| 510 | + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} | ||
| 511 | + engines: {node: '>=8'} | ||
| 512 | + | ||
| 513 | + eastasianwidth@0.2.0: | ||
| 514 | + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} | ||
| 515 | + | ||
| 516 | + editorconfig@1.0.7: | ||
| 517 | + resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} | ||
| 518 | + engines: {node: '>=14'} | ||
| 519 | + hasBin: true | ||
| 520 | + | ||
| 521 | + element-plus@2.13.6: | ||
| 522 | + resolution: {integrity: sha512-XHgwXr8Fjz6i+6BaqFhAbae/dJbG7bBAAlHrY3pWL7dpj+JcqcOyKYt4Oy5KP86FQwS1k4uIZDjCx2FyUR5lDg==} | ||
| 523 | + peerDependencies: | ||
| 524 | + vue: ^3.3.0 | ||
| 525 | + | ||
| 526 | + emoji-regex@8.0.0: | ||
| 527 | + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} | ||
| 528 | + | ||
| 529 | + emoji-regex@9.2.2: | ||
| 530 | + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} | ||
| 531 | + | ||
| 532 | + entities@6.0.1: | ||
| 533 | + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} | ||
| 534 | + engines: {node: '>=0.12'} | ||
| 535 | + | ||
| 536 | + entities@7.0.1: | ||
| 537 | + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} | ||
| 538 | + engines: {node: '>=0.12'} | ||
| 539 | + | ||
| 540 | + es-module-lexer@2.0.0: | ||
| 541 | + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} | ||
| 542 | + | ||
| 543 | + estree-walker@2.0.2: | ||
| 544 | + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} | ||
| 545 | + | ||
| 546 | + estree-walker@3.0.3: | ||
| 547 | + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} | ||
| 548 | + | ||
| 549 | + expect-type@1.3.0: | ||
| 550 | + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} | ||
| 551 | + engines: {node: '>=12.0.0'} | ||
| 552 | + | ||
| 553 | + fdir@6.5.0: | ||
| 554 | + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} | ||
| 555 | + engines: {node: '>=12.0.0'} | ||
| 556 | + peerDependencies: | ||
| 557 | + picomatch: ^3 || ^4 | ||
| 558 | + peerDependenciesMeta: | ||
| 559 | + picomatch: | ||
| 560 | + optional: true | ||
| 561 | + | ||
| 562 | + foreground-child@3.3.1: | ||
| 563 | + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} | ||
| 564 | + engines: {node: '>=14'} | ||
| 565 | + | ||
| 566 | + fsevents@2.3.2: | ||
| 567 | + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} | ||
| 568 | + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} | ||
| 569 | + os: [darwin] | ||
| 570 | + | ||
| 571 | + fsevents@2.3.3: | ||
| 572 | + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} | ||
| 573 | + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} | ||
| 574 | + os: [darwin] | ||
| 575 | + | ||
| 576 | + glob@10.5.0: | ||
| 577 | + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} | ||
| 578 | + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me | ||
| 579 | + hasBin: true | ||
| 580 | + | ||
| 581 | + hoist-non-react-statics@2.5.5: | ||
| 582 | + resolution: {integrity: sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==} | ||
| 583 | + | ||
| 584 | + html-encoding-sniffer@6.0.0: | ||
| 585 | + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} | ||
| 586 | + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} | ||
| 587 | + | ||
| 588 | + ini@1.3.8: | ||
| 589 | + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} | ||
| 590 | + | ||
| 591 | + is-fullwidth-code-point@3.0.0: | ||
| 592 | + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} | ||
| 593 | + engines: {node: '>=8'} | ||
| 594 | + | ||
| 595 | + is-potential-custom-element-name@1.0.1: | ||
| 596 | + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} | ||
| 597 | + | ||
| 598 | + isexe@2.0.0: | ||
| 599 | + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} | ||
| 600 | + | ||
| 601 | + jackspeak@3.4.3: | ||
| 602 | + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} | ||
| 603 | + | ||
| 604 | + js-beautify@1.15.4: | ||
| 605 | + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} | ||
| 606 | + engines: {node: '>=14'} | ||
| 607 | + hasBin: true | ||
| 608 | + | ||
| 609 | + js-cookie@3.0.5: | ||
| 610 | + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} | ||
| 611 | + engines: {node: '>=14'} | ||
| 612 | + | ||
| 613 | + jsdom@29.0.1: | ||
| 614 | + resolution: {integrity: sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==} | ||
| 615 | + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} | ||
| 616 | + peerDependencies: | ||
| 617 | + canvas: ^3.0.0 | ||
| 618 | + peerDependenciesMeta: | ||
| 619 | + canvas: | ||
| 620 | + optional: true | ||
| 621 | + | ||
| 622 | + lightningcss-android-arm64@1.32.0: | ||
| 623 | + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} | ||
| 624 | + engines: {node: '>= 12.0.0'} | ||
| 625 | + cpu: [arm64] | ||
| 626 | + os: [android] | ||
| 627 | + | ||
| 628 | + lightningcss-darwin-arm64@1.32.0: | ||
| 629 | + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} | ||
| 630 | + engines: {node: '>= 12.0.0'} | ||
| 631 | + cpu: [arm64] | ||
| 632 | + os: [darwin] | ||
| 633 | + | ||
| 634 | + lightningcss-darwin-x64@1.32.0: | ||
| 635 | + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} | ||
| 636 | + engines: {node: '>= 12.0.0'} | ||
| 637 | + cpu: [x64] | ||
| 638 | + os: [darwin] | ||
| 639 | + | ||
| 640 | + lightningcss-freebsd-x64@1.32.0: | ||
| 641 | + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} | ||
| 642 | + engines: {node: '>= 12.0.0'} | ||
| 643 | + cpu: [x64] | ||
| 644 | + os: [freebsd] | ||
| 645 | + | ||
| 646 | + lightningcss-linux-arm-gnueabihf@1.32.0: | ||
| 647 | + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} | ||
| 648 | + engines: {node: '>= 12.0.0'} | ||
| 649 | + cpu: [arm] | ||
| 650 | + os: [linux] | ||
| 651 | + | ||
| 652 | + lightningcss-linux-arm64-gnu@1.32.0: | ||
| 653 | + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} | ||
| 654 | + engines: {node: '>= 12.0.0'} | ||
| 655 | + cpu: [arm64] | ||
| 656 | + os: [linux] | ||
| 657 | + | ||
| 658 | + lightningcss-linux-arm64-musl@1.32.0: | ||
| 659 | + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} | ||
| 660 | + engines: {node: '>= 12.0.0'} | ||
| 661 | + cpu: [arm64] | ||
| 662 | + os: [linux] | ||
| 663 | + | ||
| 664 | + lightningcss-linux-x64-gnu@1.32.0: | ||
| 665 | + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} | ||
| 666 | + engines: {node: '>= 12.0.0'} | ||
| 667 | + cpu: [x64] | ||
| 668 | + os: [linux] | ||
| 669 | + | ||
| 670 | + lightningcss-linux-x64-musl@1.32.0: | ||
| 671 | + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} | ||
| 672 | + engines: {node: '>= 12.0.0'} | ||
| 673 | + cpu: [x64] | ||
| 674 | + os: [linux] | ||
| 675 | + | ||
| 676 | + lightningcss-win32-arm64-msvc@1.32.0: | ||
| 677 | + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} | ||
| 678 | + engines: {node: '>= 12.0.0'} | ||
| 679 | + cpu: [arm64] | ||
| 680 | + os: [win32] | ||
| 681 | + | ||
| 682 | + lightningcss-win32-x64-msvc@1.32.0: | ||
| 683 | + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} | ||
| 684 | + engines: {node: '>= 12.0.0'} | ||
| 685 | + cpu: [x64] | ||
| 686 | + os: [win32] | ||
| 687 | + | ||
| 688 | + lightningcss@1.32.0: | ||
| 689 | + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} | ||
| 690 | + engines: {node: '>= 12.0.0'} | ||
| 691 | + | ||
| 692 | + lodash-es@4.17.23: | ||
| 693 | + resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} | ||
| 694 | + | ||
| 695 | + lodash-unified@1.0.3: | ||
| 696 | + resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==} | ||
| 697 | + peerDependencies: | ||
| 698 | + '@types/lodash-es': '*' | ||
| 699 | + lodash: '*' | ||
| 700 | + lodash-es: '*' | ||
| 701 | + | ||
| 702 | + lodash@4.17.23: | ||
| 703 | + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} | ||
| 704 | + | ||
| 705 | + lru-cache@10.4.3: | ||
| 706 | + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} | ||
| 707 | + | ||
| 708 | + lru-cache@11.2.7: | ||
| 709 | + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} | ||
| 710 | + engines: {node: 20 || >=22} | ||
| 711 | + | ||
| 712 | + magic-string@0.30.21: | ||
| 713 | + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} | ||
| 714 | + | ||
| 715 | + mdn-data@2.27.1: | ||
| 716 | + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} | ||
| 717 | + | ||
| 718 | + medium-editor@5.23.3: | ||
| 719 | + resolution: {integrity: sha512-he9/TdjX8f8MGdXGfCs8AllrYnqXJJvjNkDKmPg3aPW/uoIrlRqtkFthrwvmd+u4QyzEiadhCCM0EwTiRdUCJw==} | ||
| 720 | + | ||
| 721 | + memoize-one@6.0.0: | ||
| 722 | + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} | ||
| 723 | + | ||
| 724 | + minimatch@9.0.9: | ||
| 725 | + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} | ||
| 726 | + engines: {node: '>=16 || 14 >=14.17'} | ||
| 727 | + | ||
| 728 | + minipass@7.1.3: | ||
| 729 | + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} | ||
| 730 | + engines: {node: '>=16 || 14 >=14.17'} | ||
| 731 | + | ||
| 732 | + mobx-preact@3.0.0: | ||
| 733 | + resolution: {integrity: sha512-ijan/cBs3WmRye87E5+3JmoFBB00KDAwNA3pm7bMwYLPHBAXlN86aC3gdrXw8aKzM5RI8V3a993PphzPv6P4FA==} | ||
| 734 | + peerDependencies: | ||
| 735 | + mobx: 5.x | ||
| 736 | + preact: '>=8' | ||
| 737 | + | ||
| 738 | + mobx-utils@5.6.2: | ||
| 739 | + resolution: {integrity: sha512-a/WlXyGkp6F12b01sTarENpxbmlRgPHFyR1Xv2bsSjQBm5dcOtd16ONb40/vOqck8L99NHpI+C9MXQ+SZ8f+yw==} | ||
| 740 | + peerDependencies: | ||
| 741 | + mobx: ^4.13.1 || ^5.13.1 | ||
| 742 | + | ||
| 743 | + mobx@5.15.7: | ||
| 744 | + resolution: {integrity: sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw==} | ||
| 745 | + | ||
| 746 | + mousetrap@1.6.5: | ||
| 747 | + resolution: {integrity: sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==} | ||
| 748 | + | ||
| 749 | + muggle-string@0.4.1: | ||
| 750 | + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} | ||
| 751 | + | ||
| 752 | + nanoid@3.3.11: | ||
| 753 | + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} | ||
| 754 | + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} | ||
| 755 | + hasBin: true | ||
| 756 | + | ||
| 757 | + nopt@7.2.1: | ||
| 758 | + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} | ||
| 759 | + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} | ||
| 760 | + hasBin: true | ||
| 761 | + | ||
| 762 | + normalize-wheel-es@1.2.0: | ||
| 763 | + resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} | ||
| 764 | + | ||
| 765 | + obug@2.1.1: | ||
| 766 | + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} | ||
| 767 | + | ||
| 768 | + package-json-from-dist@1.0.1: | ||
| 769 | + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} | ||
| 770 | + | ||
| 771 | + parse5@8.0.0: | ||
| 772 | + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} | ||
| 773 | + | ||
| 774 | + path-browserify@1.0.1: | ||
| 775 | + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} | ||
| 776 | + | ||
| 777 | + path-key@3.1.1: | ||
| 778 | + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} | ||
| 779 | + engines: {node: '>=8'} | ||
| 780 | + | ||
| 781 | + path-scurry@1.11.1: | ||
| 782 | + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} | ||
| 783 | + engines: {node: '>=16 || 14 >=14.18'} | ||
| 784 | + | ||
| 785 | + pathe@2.0.3: | ||
| 786 | + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} | ||
| 787 | + | ||
| 788 | + picocolors@1.1.1: | ||
| 789 | + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} | ||
| 790 | + | ||
| 791 | + picomatch@4.0.4: | ||
| 792 | + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} | ||
| 793 | + engines: {node: '>=12'} | ||
| 794 | + | ||
| 795 | + playwright-core@1.58.2: | ||
| 796 | + resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} | ||
| 797 | + engines: {node: '>=18'} | ||
| 798 | + hasBin: true | ||
| 799 | + | ||
| 800 | + playwright@1.58.2: | ||
| 801 | + resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} | ||
| 802 | + engines: {node: '>=18'} | ||
| 803 | + hasBin: true | ||
| 804 | + | ||
| 805 | + postcss@8.5.8: | ||
| 806 | + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} | ||
| 807 | + engines: {node: ^10 || ^12 || >=14} | ||
| 808 | + | ||
| 809 | + preact@10.29.0: | ||
| 810 | + resolution: {integrity: sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg==} | ||
| 811 | + | ||
| 812 | + proto-list@1.2.4: | ||
| 813 | + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} | ||
| 814 | + | ||
| 815 | + punycode@2.3.1: | ||
| 816 | + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} | ||
| 817 | + engines: {node: '>=6'} | ||
| 818 | + | ||
| 819 | + rangy@1.3.2: | ||
| 820 | + resolution: {integrity: sha512-fS1C4MOyk8T+ZJZdLcgrukPWxkyDXa+Hd2Kj+Zg4wIK71yrWgmjzHubzPMY1G+WD9EgGxMp3fIL0zQ1ickmSWA==} | ||
| 821 | + | ||
| 822 | + require-from-string@2.0.2: | ||
| 823 | + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} | ||
| 824 | + engines: {node: '>=0.10.0'} | ||
| 825 | + | ||
| 826 | + rolldown@1.0.0-rc.12: | ||
| 827 | + resolution: {integrity: sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==} | ||
| 828 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 829 | + hasBin: true | ||
| 830 | + | ||
| 831 | + saxes@6.0.0: | ||
| 832 | + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} | ||
| 833 | + engines: {node: '>=v12.22.7'} | ||
| 834 | + | ||
| 835 | + semver@7.7.4: | ||
| 836 | + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} | ||
| 837 | + engines: {node: '>=10'} | ||
| 838 | + hasBin: true | ||
| 839 | + | ||
| 840 | + shebang-command@2.0.0: | ||
| 841 | + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} | ||
| 842 | + engines: {node: '>=8'} | ||
| 843 | + | ||
| 844 | + shebang-regex@3.0.0: | ||
| 845 | + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} | ||
| 846 | + engines: {node: '>=8'} | ||
| 847 | + | ||
| 848 | + siginfo@2.0.0: | ||
| 849 | + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} | ||
| 850 | + | ||
| 851 | + signal-exit@4.1.0: | ||
| 852 | + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} | ||
| 853 | + engines: {node: '>=14'} | ||
| 854 | + | ||
| 855 | + source-map-js@1.2.1: | ||
| 856 | + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} | ||
| 857 | + engines: {node: '>=0.10.0'} | ||
| 858 | + | ||
| 859 | + stackback@0.0.2: | ||
| 860 | + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} | ||
| 861 | + | ||
| 862 | + std-env@4.0.0: | ||
| 863 | + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} | ||
| 864 | + | ||
| 865 | + string-width@4.2.3: | ||
| 866 | + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} | ||
| 867 | + engines: {node: '>=8'} | ||
| 868 | + | ||
| 869 | + string-width@5.1.2: | ||
| 870 | + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} | ||
| 871 | + engines: {node: '>=12'} | ||
| 872 | + | ||
| 873 | + strip-ansi@6.0.1: | ||
| 874 | + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} | ||
| 875 | + engines: {node: '>=8'} | ||
| 876 | + | ||
| 877 | + strip-ansi@7.2.0: | ||
| 878 | + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} | ||
| 879 | + engines: {node: '>=12'} | ||
| 880 | + | ||
| 881 | + symbol-tree@3.2.4: | ||
| 882 | + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} | ||
| 883 | + | ||
| 884 | + tinybench@2.9.0: | ||
| 885 | + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} | ||
| 886 | + | ||
| 887 | + tinyexec@1.0.4: | ||
| 888 | + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} | ||
| 889 | + engines: {node: '>=18'} | ||
| 890 | + | ||
| 891 | + tinyglobby@0.2.15: | ||
| 892 | + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} | ||
| 893 | + engines: {node: '>=12.0.0'} | ||
| 894 | + | ||
| 895 | + tinyrainbow@3.1.0: | ||
| 896 | + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} | ||
| 897 | + engines: {node: '>=14.0.0'} | ||
| 898 | + | ||
| 899 | + tldts-core@7.0.27: | ||
| 900 | + resolution: {integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==} | ||
| 901 | + | ||
| 902 | + tldts@7.0.27: | ||
| 903 | + resolution: {integrity: sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==} | ||
| 904 | + hasBin: true | ||
| 905 | + | ||
| 906 | + tough-cookie@6.0.1: | ||
| 907 | + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} | ||
| 908 | + engines: {node: '>=16'} | ||
| 909 | + | ||
| 910 | + tr46@6.0.0: | ||
| 911 | + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} | ||
| 912 | + engines: {node: '>=20'} | ||
| 913 | + | ||
| 914 | + tslib@2.8.1: | ||
| 915 | + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} | ||
| 916 | + | ||
| 917 | + typescript@6.0.2: | ||
| 918 | + resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==} | ||
| 919 | + engines: {node: '>=14.17'} | ||
| 920 | + hasBin: true | ||
| 921 | + | ||
| 922 | + undici-types@7.18.2: | ||
| 923 | + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} | ||
| 924 | + | ||
| 925 | + undici@7.24.6: | ||
| 926 | + resolution: {integrity: sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==} | ||
| 927 | + engines: {node: '>=20.18.1'} | ||
| 928 | + | ||
| 929 | + uuid@9.0.1: | ||
| 930 | + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} | ||
| 931 | + hasBin: true | ||
| 932 | + | ||
| 933 | + vanilla-picker@2.12.3: | ||
| 934 | + resolution: {integrity: sha512-qVkT1E7yMbUsB2mmJNFmaXMWE2hF8ffqzMMwe9zdAikd8u2VfnsVY2HQcOUi2F38bgbxzlJBEdS1UUhOXdF9GQ==} | ||
| 935 | + | ||
| 936 | + vite@8.0.3: | ||
| 937 | + resolution: {integrity: sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==} | ||
| 938 | + engines: {node: ^20.19.0 || >=22.12.0} | ||
| 939 | + hasBin: true | ||
| 940 | + peerDependencies: | ||
| 941 | + '@types/node': ^20.19.0 || >=22.12.0 | ||
| 942 | + '@vitejs/devtools': ^0.1.0 | ||
| 943 | + esbuild: ^0.27.0 | ||
| 944 | + jiti: '>=1.21.0' | ||
| 945 | + less: ^4.0.0 | ||
| 946 | + sass: ^1.70.0 | ||
| 947 | + sass-embedded: ^1.70.0 | ||
| 948 | + stylus: '>=0.54.8' | ||
| 949 | + sugarss: ^5.0.0 | ||
| 950 | + terser: ^5.16.0 | ||
| 951 | + tsx: ^4.8.1 | ||
| 952 | + yaml: ^2.4.2 | ||
| 953 | + peerDependenciesMeta: | ||
| 954 | + '@types/node': | ||
| 955 | + optional: true | ||
| 956 | + '@vitejs/devtools': | ||
| 957 | + optional: true | ||
| 958 | + esbuild: | ||
| 959 | + optional: true | ||
| 960 | + jiti: | ||
| 961 | + optional: true | ||
| 962 | + less: | ||
| 963 | + optional: true | ||
| 964 | + sass: | ||
| 965 | + optional: true | ||
| 966 | + sass-embedded: | ||
| 967 | + optional: true | ||
| 968 | + stylus: | ||
| 969 | + optional: true | ||
| 970 | + sugarss: | ||
| 971 | + optional: true | ||
| 972 | + terser: | ||
| 973 | + optional: true | ||
| 974 | + tsx: | ||
| 975 | + optional: true | ||
| 976 | + yaml: | ||
| 977 | + optional: true | ||
| 978 | + | ||
| 979 | + vitest@4.1.2: | ||
| 980 | + resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} | ||
| 981 | + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} | ||
| 982 | + hasBin: true | ||
| 983 | + peerDependencies: | ||
| 984 | + '@edge-runtime/vm': '*' | ||
| 985 | + '@opentelemetry/api': ^1.9.0 | ||
| 986 | + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 | ||
| 987 | + '@vitest/browser-playwright': 4.1.2 | ||
| 988 | + '@vitest/browser-preview': 4.1.2 | ||
| 989 | + '@vitest/browser-webdriverio': 4.1.2 | ||
| 990 | + '@vitest/ui': 4.1.2 | ||
| 991 | + happy-dom: '*' | ||
| 992 | + jsdom: '*' | ||
| 993 | + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 | ||
| 994 | + peerDependenciesMeta: | ||
| 995 | + '@edge-runtime/vm': | ||
| 996 | + optional: true | ||
| 997 | + '@opentelemetry/api': | ||
| 998 | + optional: true | ||
| 999 | + '@types/node': | ||
| 1000 | + optional: true | ||
| 1001 | + '@vitest/browser-playwright': | ||
| 1002 | + optional: true | ||
| 1003 | + '@vitest/browser-preview': | ||
| 1004 | + optional: true | ||
| 1005 | + '@vitest/browser-webdriverio': | ||
| 1006 | + optional: true | ||
| 1007 | + '@vitest/ui': | ||
| 1008 | + optional: true | ||
| 1009 | + happy-dom: | ||
| 1010 | + optional: true | ||
| 1011 | + jsdom: | ||
| 1012 | + optional: true | ||
| 1013 | + | ||
| 1014 | + vscode-uri@3.1.0: | ||
| 1015 | + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} | ||
| 1016 | + | ||
| 1017 | + vue-component-type-helpers@2.2.12: | ||
| 1018 | + resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} | ||
| 1019 | + | ||
| 1020 | + vue-component-type-helpers@3.2.6: | ||
| 1021 | + resolution: {integrity: sha512-O02tnvIfOQVmnvoWwuSydwRoHjZVt8UEBR+2p4rT35p8GAy5VTlWP8o5qXfJR/GWCN0nVZoYWsVUvx2jwgdBmQ==} | ||
| 1022 | + | ||
| 1023 | + vue-demi@0.14.10: | ||
| 1024 | + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} | ||
| 1025 | + engines: {node: '>=12'} | ||
| 1026 | + hasBin: true | ||
| 1027 | + peerDependencies: | ||
| 1028 | + '@vue/composition-api': ^1.0.0-rc.1 | ||
| 1029 | + vue: ^3.0.0-0 || ^2.6.0 | ||
| 1030 | + peerDependenciesMeta: | ||
| 1031 | + '@vue/composition-api': | ||
| 1032 | + optional: true | ||
| 1033 | + | ||
| 1034 | + vue-tsc@3.2.6: | ||
| 1035 | + resolution: {integrity: sha512-gYW/kWI0XrwGzd0PKc7tVB/qpdeAkIZLNZb10/InizkQjHjnT8weZ/vBarZoj4kHKbUTZT/bAVgoOr8x4NsQ/Q==} | ||
| 1036 | + hasBin: true | ||
| 1037 | + peerDependencies: | ||
| 1038 | + typescript: '>=5.0.0' | ||
| 1039 | + | ||
| 1040 | + vue@3.5.31: | ||
| 1041 | + resolution: {integrity: sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==} | ||
| 1042 | + peerDependencies: | ||
| 1043 | + typescript: '*' | ||
| 1044 | + peerDependenciesMeta: | ||
| 1045 | + typescript: | ||
| 1046 | + optional: true | ||
| 1047 | + | ||
| 1048 | + w3c-xmlserializer@5.0.0: | ||
| 1049 | + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} | ||
| 1050 | + engines: {node: '>=18'} | ||
| 1051 | + | ||
| 1052 | + webidl-conversions@8.0.1: | ||
| 1053 | + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} | ||
| 1054 | + engines: {node: '>=20'} | ||
| 1055 | + | ||
| 1056 | + whatwg-mimetype@5.0.0: | ||
| 1057 | + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} | ||
| 1058 | + engines: {node: '>=20'} | ||
| 1059 | + | ||
| 1060 | + whatwg-url@16.0.1: | ||
| 1061 | + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} | ||
| 1062 | + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} | ||
| 1063 | + | ||
| 1064 | + which@2.0.2: | ||
| 1065 | + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} | ||
| 1066 | + engines: {node: '>= 8'} | ||
| 1067 | + hasBin: true | ||
| 1068 | + | ||
| 1069 | + why-is-node-running@2.3.0: | ||
| 1070 | + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} | ||
| 1071 | + engines: {node: '>=8'} | ||
| 1072 | + hasBin: true | ||
| 1073 | + | ||
| 1074 | + wrap-ansi@7.0.0: | ||
| 1075 | + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} | ||
| 1076 | + engines: {node: '>=10'} | ||
| 1077 | + | ||
| 1078 | + wrap-ansi@8.1.0: | ||
| 1079 | + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} | ||
| 1080 | + engines: {node: '>=12'} | ||
| 1081 | + | ||
| 1082 | + xml-name-validator@5.0.0: | ||
| 1083 | + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} | ||
| 1084 | + engines: {node: '>=18'} | ||
| 1085 | + | ||
| 1086 | + xmlchars@2.2.0: | ||
| 1087 | + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} | ||
| 1088 | + | ||
| 1089 | +snapshots: | ||
| 1090 | + | ||
| 1091 | + '@antv/hierarchy@0.6.14': {} | ||
| 1092 | + | ||
| 1093 | + '@asamuzakjp/css-color@5.1.1': | ||
| 1094 | + dependencies: | ||
| 1095 | + '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) | ||
| 1096 | + '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) | ||
| 1097 | + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) | ||
| 1098 | + '@csstools/css-tokenizer': 4.0.0 | ||
| 1099 | + lru-cache: 11.2.7 | ||
| 1100 | + | ||
| 1101 | + '@asamuzakjp/dom-selector@7.0.4': | ||
| 1102 | + dependencies: | ||
| 1103 | + '@asamuzakjp/nwsapi': 2.3.9 | ||
| 1104 | + bidi-js: 1.0.3 | ||
| 1105 | + css-tree: 3.2.1 | ||
| 1106 | + is-potential-custom-element-name: 1.0.1 | ||
| 1107 | + lru-cache: 11.2.7 | ||
| 1108 | + | ||
| 1109 | + '@asamuzakjp/nwsapi@2.3.9': {} | ||
| 1110 | + | ||
| 1111 | + '@babel/helper-string-parser@7.27.1': {} | ||
| 1112 | + | ||
| 1113 | + '@babel/helper-validator-identifier@7.28.5': {} | ||
| 1114 | + | ||
| 1115 | + '@babel/parser@7.29.2': | ||
| 1116 | + dependencies: | ||
| 1117 | + '@babel/types': 7.29.0 | ||
| 1118 | + | ||
| 1119 | + '@babel/types@7.29.0': | ||
| 1120 | + dependencies: | ||
| 1121 | + '@babel/helper-string-parser': 7.27.1 | ||
| 1122 | + '@babel/helper-validator-identifier': 7.28.5 | ||
| 1123 | + | ||
| 1124 | + '@bramus/specificity@2.4.2': | ||
| 1125 | + dependencies: | ||
| 1126 | + css-tree: 3.2.1 | ||
| 1127 | + | ||
| 1128 | + '@csstools/color-helpers@6.0.2': {} | ||
| 1129 | + | ||
| 1130 | + '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': | ||
| 1131 | + dependencies: | ||
| 1132 | + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) | ||
| 1133 | + '@csstools/css-tokenizer': 4.0.0 | ||
| 1134 | + | ||
| 1135 | + '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': | ||
| 1136 | + dependencies: | ||
| 1137 | + '@csstools/color-helpers': 6.0.2 | ||
| 1138 | + '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) | ||
| 1139 | + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) | ||
| 1140 | + '@csstools/css-tokenizer': 4.0.0 | ||
| 1141 | + | ||
| 1142 | + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': | ||
| 1143 | + dependencies: | ||
| 1144 | + '@csstools/css-tokenizer': 4.0.0 | ||
| 1145 | + | ||
| 1146 | + '@csstools/css-syntax-patches-for-csstree@1.1.2(css-tree@3.2.1)': | ||
| 1147 | + optionalDependencies: | ||
| 1148 | + css-tree: 3.2.1 | ||
| 1149 | + | ||
| 1150 | + '@csstools/css-tokenizer@4.0.0': {} | ||
| 1151 | + | ||
| 1152 | + '@ctrl/tinycolor@4.2.0': {} | ||
| 1153 | + | ||
| 1154 | + '@element-plus/icons-vue@2.3.2(vue@3.5.31(typescript@6.0.2))': | ||
| 1155 | + dependencies: | ||
| 1156 | + vue: 3.5.31(typescript@6.0.2) | ||
| 1157 | + | ||
| 1158 | + '@emnapi/core@1.9.1': | ||
| 1159 | + dependencies: | ||
| 1160 | + '@emnapi/wasi-threads': 1.2.0 | ||
| 1161 | + tslib: 2.8.1 | ||
| 1162 | + optional: true | ||
| 1163 | + | ||
| 1164 | + '@emnapi/runtime@1.9.1': | ||
| 1165 | + dependencies: | ||
| 1166 | + tslib: 2.8.1 | ||
| 1167 | + optional: true | ||
| 1168 | + | ||
| 1169 | + '@emnapi/wasi-threads@1.2.0': | ||
| 1170 | + dependencies: | ||
| 1171 | + tslib: 2.8.1 | ||
| 1172 | + optional: true | ||
| 1173 | + | ||
| 1174 | + '@exodus/bytes@1.15.0': {} | ||
| 1175 | + | ||
| 1176 | + '@floating-ui/core@1.7.5': | ||
| 1177 | + dependencies: | ||
| 1178 | + '@floating-ui/utils': 0.2.11 | ||
| 1179 | + | ||
| 1180 | + '@floating-ui/dom@1.7.6': | ||
| 1181 | + dependencies: | ||
| 1182 | + '@floating-ui/core': 1.7.5 | ||
| 1183 | + '@floating-ui/utils': 0.2.11 | ||
| 1184 | + | ||
| 1185 | + '@floating-ui/utils@0.2.11': {} | ||
| 1186 | + | ||
| 1187 | + '@isaacs/cliui@8.0.2': | ||
| 1188 | + dependencies: | ||
| 1189 | + string-width: 5.1.2 | ||
| 1190 | + string-width-cjs: string-width@4.2.3 | ||
| 1191 | + strip-ansi: 7.2.0 | ||
| 1192 | + strip-ansi-cjs: strip-ansi@6.0.1 | ||
| 1193 | + wrap-ansi: 8.1.0 | ||
| 1194 | + wrap-ansi-cjs: wrap-ansi@7.0.0 | ||
| 1195 | + | ||
| 1196 | + '@jridgewell/sourcemap-codec@1.5.5': {} | ||
| 1197 | + | ||
| 1198 | + '@logicflow/core@2.1.11': | ||
| 1199 | + dependencies: | ||
| 1200 | + classnames: 2.5.1 | ||
| 1201 | + lodash-es: 4.17.23 | ||
| 1202 | + mobx: 5.15.7 | ||
| 1203 | + mobx-preact: 3.0.0(mobx@5.15.7)(preact@10.29.0) | ||
| 1204 | + mobx-utils: 5.6.2(mobx@5.15.7) | ||
| 1205 | + mousetrap: 1.6.5 | ||
| 1206 | + preact: 10.29.0 | ||
| 1207 | + uuid: 9.0.1 | ||
| 1208 | + | ||
| 1209 | + '@logicflow/extension@2.1.15(@logicflow/core@2.1.11)(@logicflow/vue-node-registry@1.1.13(@logicflow/core@2.1.11)(vue@3.5.31(typescript@6.0.2)))': | ||
| 1210 | + dependencies: | ||
| 1211 | + '@antv/hierarchy': 0.6.14 | ||
| 1212 | + '@logicflow/core': 2.1.11 | ||
| 1213 | + '@logicflow/vue-node-registry': 1.1.13(@logicflow/core@2.1.11)(vue@3.5.31(typescript@6.0.2)) | ||
| 1214 | + classnames: 2.5.1 | ||
| 1215 | + lodash-es: 4.17.23 | ||
| 1216 | + medium-editor: 5.23.3 | ||
| 1217 | + mobx: 5.15.7 | ||
| 1218 | + preact: 10.29.0 | ||
| 1219 | + rangy: 1.3.2 | ||
| 1220 | + vanilla-picker: 2.12.3 | ||
| 1221 | + | ||
| 1222 | + '@logicflow/vue-node-registry@1.1.13(@logicflow/core@2.1.11)(vue@3.5.31(typescript@6.0.2))': | ||
| 1223 | + dependencies: | ||
| 1224 | + '@logicflow/core': 2.1.11 | ||
| 1225 | + lodash-es: 4.17.23 | ||
| 1226 | + vue: 3.5.31(typescript@6.0.2) | ||
| 1227 | + vue-demi: 0.14.10(vue@3.5.31(typescript@6.0.2)) | ||
| 1228 | + | ||
| 1229 | + '@napi-rs/wasm-runtime@1.1.2(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': | ||
| 1230 | + dependencies: | ||
| 1231 | + '@emnapi/core': 1.9.1 | ||
| 1232 | + '@emnapi/runtime': 1.9.1 | ||
| 1233 | + '@tybys/wasm-util': 0.10.1 | ||
| 1234 | + optional: true | ||
| 1235 | + | ||
| 1236 | + '@one-ini/wasm@0.1.1': {} | ||
| 1237 | + | ||
| 1238 | + '@oxc-project/types@0.122.0': {} | ||
| 1239 | + | ||
| 1240 | + '@pkgjs/parseargs@0.11.0': | ||
| 1241 | + optional: true | ||
| 1242 | + | ||
| 1243 | + '@playwright/test@1.58.2': | ||
| 1244 | + dependencies: | ||
| 1245 | + playwright: 1.58.2 | ||
| 1246 | + | ||
| 1247 | + '@rolldown/binding-android-arm64@1.0.0-rc.12': | ||
| 1248 | + optional: true | ||
| 1249 | + | ||
| 1250 | + '@rolldown/binding-darwin-arm64@1.0.0-rc.12': | ||
| 1251 | + optional: true | ||
| 1252 | + | ||
| 1253 | + '@rolldown/binding-darwin-x64@1.0.0-rc.12': | ||
| 1254 | + optional: true | ||
| 1255 | + | ||
| 1256 | + '@rolldown/binding-freebsd-x64@1.0.0-rc.12': | ||
| 1257 | + optional: true | ||
| 1258 | + | ||
| 1259 | + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': | ||
| 1260 | + optional: true | ||
| 1261 | + | ||
| 1262 | + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': | ||
| 1263 | + optional: true | ||
| 1264 | + | ||
| 1265 | + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': | ||
| 1266 | + optional: true | ||
| 1267 | + | ||
| 1268 | + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': | ||
| 1269 | + optional: true | ||
| 1270 | + | ||
| 1271 | + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': | ||
| 1272 | + optional: true | ||
| 1273 | + | ||
| 1274 | + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': | ||
| 1275 | + optional: true | ||
| 1276 | + | ||
| 1277 | + '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': | ||
| 1278 | + optional: true | ||
| 1279 | + | ||
| 1280 | + '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': | ||
| 1281 | + optional: true | ||
| 1282 | + | ||
| 1283 | + '@rolldown/binding-wasm32-wasi@1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': | ||
| 1284 | + dependencies: | ||
| 1285 | + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) | ||
| 1286 | + transitivePeerDependencies: | ||
| 1287 | + - '@emnapi/core' | ||
| 1288 | + - '@emnapi/runtime' | ||
| 1289 | + optional: true | ||
| 1290 | + | ||
| 1291 | + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': | ||
| 1292 | + optional: true | ||
| 1293 | + | ||
| 1294 | + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': | ||
| 1295 | + optional: true | ||
| 1296 | + | ||
| 1297 | + '@rolldown/pluginutils@1.0.0-rc.12': {} | ||
| 1298 | + | ||
| 1299 | + '@rolldown/pluginutils@1.0.0-rc.2': {} | ||
| 1300 | + | ||
| 1301 | + '@sphinxxxx/color-conversion@2.2.2': {} | ||
| 1302 | + | ||
| 1303 | + '@standard-schema/spec@1.1.0': {} | ||
| 1304 | + | ||
| 1305 | + '@sxzz/popperjs-es@2.11.8': {} | ||
| 1306 | + | ||
| 1307 | + '@tybys/wasm-util@0.10.1': | ||
| 1308 | + dependencies: | ||
| 1309 | + tslib: 2.8.1 | ||
| 1310 | + optional: true | ||
| 1311 | + | ||
| 1312 | + '@types/chai@5.2.3': | ||
| 1313 | + dependencies: | ||
| 1314 | + '@types/deep-eql': 4.0.2 | ||
| 1315 | + assertion-error: 2.0.1 | ||
| 1316 | + | ||
| 1317 | + '@types/deep-eql@4.0.2': {} | ||
| 1318 | + | ||
| 1319 | + '@types/estree@1.0.8': {} | ||
| 1320 | + | ||
| 1321 | + '@types/lodash-es@4.17.12': | ||
| 1322 | + dependencies: | ||
| 1323 | + '@types/lodash': 4.17.24 | ||
| 1324 | + | ||
| 1325 | + '@types/lodash@4.17.24': {} | ||
| 1326 | + | ||
| 1327 | + '@types/node@25.5.0': | ||
| 1328 | + dependencies: | ||
| 1329 | + undici-types: 7.18.2 | ||
| 1330 | + | ||
| 1331 | + '@types/web-bluetooth@0.0.20': {} | ||
| 1332 | + | ||
| 1333 | + '@vitejs/plugin-vue@6.0.5(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0))(vue@3.5.31(typescript@6.0.2))': | ||
| 1334 | + dependencies: | ||
| 1335 | + '@rolldown/pluginutils': 1.0.0-rc.2 | ||
| 1336 | + vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0) | ||
| 1337 | + vue: 3.5.31(typescript@6.0.2) | ||
| 1338 | + | ||
| 1339 | + '@vitest/expect@4.1.2': | ||
| 1340 | + dependencies: | ||
| 1341 | + '@standard-schema/spec': 1.1.0 | ||
| 1342 | + '@types/chai': 5.2.3 | ||
| 1343 | + '@vitest/spy': 4.1.2 | ||
| 1344 | + '@vitest/utils': 4.1.2 | ||
| 1345 | + chai: 6.2.2 | ||
| 1346 | + tinyrainbow: 3.1.0 | ||
| 1347 | + | ||
| 1348 | + '@vitest/mocker@4.1.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0))': | ||
| 1349 | + dependencies: | ||
| 1350 | + '@vitest/spy': 4.1.2 | ||
| 1351 | + estree-walker: 3.0.3 | ||
| 1352 | + magic-string: 0.30.21 | ||
| 1353 | + optionalDependencies: | ||
| 1354 | + vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0) | ||
| 1355 | + | ||
| 1356 | + '@vitest/pretty-format@4.1.2': | ||
| 1357 | + dependencies: | ||
| 1358 | + tinyrainbow: 3.1.0 | ||
| 1359 | + | ||
| 1360 | + '@vitest/runner@4.1.2': | ||
| 1361 | + dependencies: | ||
| 1362 | + '@vitest/utils': 4.1.2 | ||
| 1363 | + pathe: 2.0.3 | ||
| 1364 | + | ||
| 1365 | + '@vitest/snapshot@4.1.2': | ||
| 1366 | + dependencies: | ||
| 1367 | + '@vitest/pretty-format': 4.1.2 | ||
| 1368 | + '@vitest/utils': 4.1.2 | ||
| 1369 | + magic-string: 0.30.21 | ||
| 1370 | + pathe: 2.0.3 | ||
| 1371 | + | ||
| 1372 | + '@vitest/spy@4.1.2': {} | ||
| 1373 | + | ||
| 1374 | + '@vitest/utils@4.1.2': | ||
| 1375 | + dependencies: | ||
| 1376 | + '@vitest/pretty-format': 4.1.2 | ||
| 1377 | + convert-source-map: 2.0.0 | ||
| 1378 | + tinyrainbow: 3.1.0 | ||
| 1379 | + | ||
| 1380 | + '@volar/language-core@2.4.28': | ||
| 1381 | + dependencies: | ||
| 1382 | + '@volar/source-map': 2.4.28 | ||
| 1383 | + | ||
| 1384 | + '@volar/source-map@2.4.28': {} | ||
| 1385 | + | ||
| 1386 | + '@volar/typescript@2.4.28': | ||
| 1387 | + dependencies: | ||
| 1388 | + '@volar/language-core': 2.4.28 | ||
| 1389 | + path-browserify: 1.0.1 | ||
| 1390 | + vscode-uri: 3.1.0 | ||
| 1391 | + | ||
| 1392 | + '@vue/compiler-core@3.5.31': | ||
| 1393 | + dependencies: | ||
| 1394 | + '@babel/parser': 7.29.2 | ||
| 1395 | + '@vue/shared': 3.5.31 | ||
| 1396 | + entities: 7.0.1 | ||
| 1397 | + estree-walker: 2.0.2 | ||
| 1398 | + source-map-js: 1.2.1 | ||
| 1399 | + | ||
| 1400 | + '@vue/compiler-dom@3.5.31': | ||
| 1401 | + dependencies: | ||
| 1402 | + '@vue/compiler-core': 3.5.31 | ||
| 1403 | + '@vue/shared': 3.5.31 | ||
| 1404 | + | ||
| 1405 | + '@vue/compiler-sfc@3.5.31': | ||
| 1406 | + dependencies: | ||
| 1407 | + '@babel/parser': 7.29.2 | ||
| 1408 | + '@vue/compiler-core': 3.5.31 | ||
| 1409 | + '@vue/compiler-dom': 3.5.31 | ||
| 1410 | + '@vue/compiler-ssr': 3.5.31 | ||
| 1411 | + '@vue/shared': 3.5.31 | ||
| 1412 | + estree-walker: 2.0.2 | ||
| 1413 | + magic-string: 0.30.21 | ||
| 1414 | + postcss: 8.5.8 | ||
| 1415 | + source-map-js: 1.2.1 | ||
| 1416 | + | ||
| 1417 | + '@vue/compiler-ssr@3.5.31': | ||
| 1418 | + dependencies: | ||
| 1419 | + '@vue/compiler-dom': 3.5.31 | ||
| 1420 | + '@vue/shared': 3.5.31 | ||
| 1421 | + | ||
| 1422 | + '@vue/language-core@3.2.6': | ||
| 1423 | + dependencies: | ||
| 1424 | + '@volar/language-core': 2.4.28 | ||
| 1425 | + '@vue/compiler-dom': 3.5.31 | ||
| 1426 | + '@vue/shared': 3.5.31 | ||
| 1427 | + alien-signals: 3.1.2 | ||
| 1428 | + muggle-string: 0.4.1 | ||
| 1429 | + path-browserify: 1.0.1 | ||
| 1430 | + picomatch: 4.0.4 | ||
| 1431 | + | ||
| 1432 | + '@vue/reactivity@3.5.31': | ||
| 1433 | + dependencies: | ||
| 1434 | + '@vue/shared': 3.5.31 | ||
| 1435 | + | ||
| 1436 | + '@vue/runtime-core@3.5.31': | ||
| 1437 | + dependencies: | ||
| 1438 | + '@vue/reactivity': 3.5.31 | ||
| 1439 | + '@vue/shared': 3.5.31 | ||
| 1440 | + | ||
| 1441 | + '@vue/runtime-dom@3.5.31': | ||
| 1442 | + dependencies: | ||
| 1443 | + '@vue/reactivity': 3.5.31 | ||
| 1444 | + '@vue/runtime-core': 3.5.31 | ||
| 1445 | + '@vue/shared': 3.5.31 | ||
| 1446 | + csstype: 3.2.3 | ||
| 1447 | + | ||
| 1448 | + '@vue/server-renderer@3.5.31(vue@3.5.31(typescript@6.0.2))': | ||
| 1449 | + dependencies: | ||
| 1450 | + '@vue/compiler-ssr': 3.5.31 | ||
| 1451 | + '@vue/shared': 3.5.31 | ||
| 1452 | + vue: 3.5.31(typescript@6.0.2) | ||
| 1453 | + | ||
| 1454 | + '@vue/shared@3.5.31': {} | ||
| 1455 | + | ||
| 1456 | + '@vue/test-utils@2.4.6': | ||
| 1457 | + dependencies: | ||
| 1458 | + js-beautify: 1.15.4 | ||
| 1459 | + vue-component-type-helpers: 2.2.12 | ||
| 1460 | + | ||
| 1461 | + '@vueuse/core@12.0.0(typescript@6.0.2)': | ||
| 1462 | + dependencies: | ||
| 1463 | + '@types/web-bluetooth': 0.0.20 | ||
| 1464 | + '@vueuse/metadata': 12.0.0 | ||
| 1465 | + '@vueuse/shared': 12.0.0(typescript@6.0.2) | ||
| 1466 | + vue: 3.5.31(typescript@6.0.2) | ||
| 1467 | + transitivePeerDependencies: | ||
| 1468 | + - typescript | ||
| 1469 | + | ||
| 1470 | + '@vueuse/metadata@12.0.0': {} | ||
| 1471 | + | ||
| 1472 | + '@vueuse/shared@12.0.0(typescript@6.0.2)': | ||
| 1473 | + dependencies: | ||
| 1474 | + vue: 3.5.31(typescript@6.0.2) | ||
| 1475 | + transitivePeerDependencies: | ||
| 1476 | + - typescript | ||
| 1477 | + | ||
| 1478 | + abbrev@2.0.0: {} | ||
| 1479 | + | ||
| 1480 | + alien-signals@3.1.2: {} | ||
| 1481 | + | ||
| 1482 | + ansi-regex@5.0.1: {} | ||
| 1483 | + | ||
| 1484 | + ansi-regex@6.2.2: {} | ||
| 1485 | + | ||
| 1486 | + ansi-styles@4.3.0: | ||
| 1487 | + dependencies: | ||
| 1488 | + color-convert: 2.0.1 | ||
| 1489 | + | ||
| 1490 | + ansi-styles@6.2.3: {} | ||
| 1491 | + | ||
| 1492 | + assertion-error@2.0.1: {} | ||
| 1493 | + | ||
| 1494 | + async-validator@4.2.5: {} | ||
| 1495 | + | ||
| 1496 | + balanced-match@1.0.2: {} | ||
| 1497 | + | ||
| 1498 | + bidi-js@1.0.3: | ||
| 1499 | + dependencies: | ||
| 1500 | + require-from-string: 2.0.2 | ||
| 1501 | + | ||
| 1502 | + brace-expansion@2.0.3: | ||
| 1503 | + dependencies: | ||
| 1504 | + balanced-match: 1.0.2 | ||
| 1505 | + | ||
| 1506 | + chai@6.2.2: {} | ||
| 1507 | + | ||
| 1508 | + classnames@2.5.1: {} | ||
| 1509 | + | ||
| 1510 | + color-convert@2.0.1: | ||
| 1511 | + dependencies: | ||
| 1512 | + color-name: 1.1.4 | ||
| 1513 | + | ||
| 1514 | + color-name@1.1.4: {} | ||
| 1515 | + | ||
| 1516 | + commander@10.0.1: {} | ||
| 1517 | + | ||
| 1518 | + config-chain@1.1.13: | ||
| 1519 | + dependencies: | ||
| 1520 | + ini: 1.3.8 | ||
| 1521 | + proto-list: 1.2.4 | ||
| 1522 | + | ||
| 1523 | + convert-source-map@2.0.0: {} | ||
| 1524 | + | ||
| 1525 | + cross-spawn@7.0.6: | ||
| 1526 | + dependencies: | ||
| 1527 | + path-key: 3.1.1 | ||
| 1528 | + shebang-command: 2.0.0 | ||
| 1529 | + which: 2.0.2 | ||
| 1530 | + | ||
| 1531 | + css-tree@3.2.1: | ||
| 1532 | + dependencies: | ||
| 1533 | + mdn-data: 2.27.1 | ||
| 1534 | + source-map-js: 1.2.1 | ||
| 1535 | + | ||
| 1536 | + csstype@3.2.3: {} | ||
| 1537 | + | ||
| 1538 | + data-urls@7.0.0: | ||
| 1539 | + dependencies: | ||
| 1540 | + whatwg-mimetype: 5.0.0 | ||
| 1541 | + whatwg-url: 16.0.1 | ||
| 1542 | + transitivePeerDependencies: | ||
| 1543 | + - '@noble/hashes' | ||
| 1544 | + | ||
| 1545 | + dayjs@1.11.20: {} | ||
| 1546 | + | ||
| 1547 | + decimal.js@10.6.0: {} | ||
| 1548 | + | ||
| 1549 | + detect-libc@2.1.2: {} | ||
| 1550 | + | ||
| 1551 | + eastasianwidth@0.2.0: {} | ||
| 1552 | + | ||
| 1553 | + editorconfig@1.0.7: | ||
| 1554 | + dependencies: | ||
| 1555 | + '@one-ini/wasm': 0.1.1 | ||
| 1556 | + commander: 10.0.1 | ||
| 1557 | + minimatch: 9.0.9 | ||
| 1558 | + semver: 7.7.4 | ||
| 1559 | + | ||
| 1560 | + element-plus@2.13.6(typescript@6.0.2)(vue@3.5.31(typescript@6.0.2)): | ||
| 1561 | + dependencies: | ||
| 1562 | + '@ctrl/tinycolor': 4.2.0 | ||
| 1563 | + '@element-plus/icons-vue': 2.3.2(vue@3.5.31(typescript@6.0.2)) | ||
| 1564 | + '@floating-ui/dom': 1.7.6 | ||
| 1565 | + '@popperjs/core': '@sxzz/popperjs-es@2.11.8' | ||
| 1566 | + '@types/lodash': 4.17.24 | ||
| 1567 | + '@types/lodash-es': 4.17.12 | ||
| 1568 | + '@vueuse/core': 12.0.0(typescript@6.0.2) | ||
| 1569 | + async-validator: 4.2.5 | ||
| 1570 | + dayjs: 1.11.20 | ||
| 1571 | + lodash: 4.17.23 | ||
| 1572 | + lodash-es: 4.17.23 | ||
| 1573 | + lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.23)(lodash@4.17.23) | ||
| 1574 | + memoize-one: 6.0.0 | ||
| 1575 | + normalize-wheel-es: 1.2.0 | ||
| 1576 | + vue: 3.5.31(typescript@6.0.2) | ||
| 1577 | + vue-component-type-helpers: 3.2.6 | ||
| 1578 | + transitivePeerDependencies: | ||
| 1579 | + - typescript | ||
| 1580 | + | ||
| 1581 | + emoji-regex@8.0.0: {} | ||
| 1582 | + | ||
| 1583 | + emoji-regex@9.2.2: {} | ||
| 1584 | + | ||
| 1585 | + entities@6.0.1: {} | ||
| 1586 | + | ||
| 1587 | + entities@7.0.1: {} | ||
| 1588 | + | ||
| 1589 | + es-module-lexer@2.0.0: {} | ||
| 1590 | + | ||
| 1591 | + estree-walker@2.0.2: {} | ||
| 1592 | + | ||
| 1593 | + estree-walker@3.0.3: | ||
| 1594 | + dependencies: | ||
| 1595 | + '@types/estree': 1.0.8 | ||
| 1596 | + | ||
| 1597 | + expect-type@1.3.0: {} | ||
| 1598 | + | ||
| 1599 | + fdir@6.5.0(picomatch@4.0.4): | ||
| 1600 | + optionalDependencies: | ||
| 1601 | + picomatch: 4.0.4 | ||
| 1602 | + | ||
| 1603 | + foreground-child@3.3.1: | ||
| 1604 | + dependencies: | ||
| 1605 | + cross-spawn: 7.0.6 | ||
| 1606 | + signal-exit: 4.1.0 | ||
| 1607 | + | ||
| 1608 | + fsevents@2.3.2: | ||
| 1609 | + optional: true | ||
| 1610 | + | ||
| 1611 | + fsevents@2.3.3: | ||
| 1612 | + optional: true | ||
| 1613 | + | ||
| 1614 | + glob@10.5.0: | ||
| 1615 | + dependencies: | ||
| 1616 | + foreground-child: 3.3.1 | ||
| 1617 | + jackspeak: 3.4.3 | ||
| 1618 | + minimatch: 9.0.9 | ||
| 1619 | + minipass: 7.1.3 | ||
| 1620 | + package-json-from-dist: 1.0.1 | ||
| 1621 | + path-scurry: 1.11.1 | ||
| 1622 | + | ||
| 1623 | + hoist-non-react-statics@2.5.5: {} | ||
| 1624 | + | ||
| 1625 | + html-encoding-sniffer@6.0.0: | ||
| 1626 | + dependencies: | ||
| 1627 | + '@exodus/bytes': 1.15.0 | ||
| 1628 | + transitivePeerDependencies: | ||
| 1629 | + - '@noble/hashes' | ||
| 1630 | + | ||
| 1631 | + ini@1.3.8: {} | ||
| 1632 | + | ||
| 1633 | + is-fullwidth-code-point@3.0.0: {} | ||
| 1634 | + | ||
| 1635 | + is-potential-custom-element-name@1.0.1: {} | ||
| 1636 | + | ||
| 1637 | + isexe@2.0.0: {} | ||
| 1638 | + | ||
| 1639 | + jackspeak@3.4.3: | ||
| 1640 | + dependencies: | ||
| 1641 | + '@isaacs/cliui': 8.0.2 | ||
| 1642 | + optionalDependencies: | ||
| 1643 | + '@pkgjs/parseargs': 0.11.0 | ||
| 1644 | + | ||
| 1645 | + js-beautify@1.15.4: | ||
| 1646 | + dependencies: | ||
| 1647 | + config-chain: 1.1.13 | ||
| 1648 | + editorconfig: 1.0.7 | ||
| 1649 | + glob: 10.5.0 | ||
| 1650 | + js-cookie: 3.0.5 | ||
| 1651 | + nopt: 7.2.1 | ||
| 1652 | + | ||
| 1653 | + js-cookie@3.0.5: {} | ||
| 1654 | + | ||
| 1655 | + jsdom@29.0.1: | ||
| 1656 | + dependencies: | ||
| 1657 | + '@asamuzakjp/css-color': 5.1.1 | ||
| 1658 | + '@asamuzakjp/dom-selector': 7.0.4 | ||
| 1659 | + '@bramus/specificity': 2.4.2 | ||
| 1660 | + '@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1) | ||
| 1661 | + '@exodus/bytes': 1.15.0 | ||
| 1662 | + css-tree: 3.2.1 | ||
| 1663 | + data-urls: 7.0.0 | ||
| 1664 | + decimal.js: 10.6.0 | ||
| 1665 | + html-encoding-sniffer: 6.0.0 | ||
| 1666 | + is-potential-custom-element-name: 1.0.1 | ||
| 1667 | + lru-cache: 11.2.7 | ||
| 1668 | + parse5: 8.0.0 | ||
| 1669 | + saxes: 6.0.0 | ||
| 1670 | + symbol-tree: 3.2.4 | ||
| 1671 | + tough-cookie: 6.0.1 | ||
| 1672 | + undici: 7.24.6 | ||
| 1673 | + w3c-xmlserializer: 5.0.0 | ||
| 1674 | + webidl-conversions: 8.0.1 | ||
| 1675 | + whatwg-mimetype: 5.0.0 | ||
| 1676 | + whatwg-url: 16.0.1 | ||
| 1677 | + xml-name-validator: 5.0.0 | ||
| 1678 | + transitivePeerDependencies: | ||
| 1679 | + - '@noble/hashes' | ||
| 1680 | + | ||
| 1681 | + lightningcss-android-arm64@1.32.0: | ||
| 1682 | + optional: true | ||
| 1683 | + | ||
| 1684 | + lightningcss-darwin-arm64@1.32.0: | ||
| 1685 | + optional: true | ||
| 1686 | + | ||
| 1687 | + lightningcss-darwin-x64@1.32.0: | ||
| 1688 | + optional: true | ||
| 1689 | + | ||
| 1690 | + lightningcss-freebsd-x64@1.32.0: | ||
| 1691 | + optional: true | ||
| 1692 | + | ||
| 1693 | + lightningcss-linux-arm-gnueabihf@1.32.0: | ||
| 1694 | + optional: true | ||
| 1695 | + | ||
| 1696 | + lightningcss-linux-arm64-gnu@1.32.0: | ||
| 1697 | + optional: true | ||
| 1698 | + | ||
| 1699 | + lightningcss-linux-arm64-musl@1.32.0: | ||
| 1700 | + optional: true | ||
| 1701 | + | ||
| 1702 | + lightningcss-linux-x64-gnu@1.32.0: | ||
| 1703 | + optional: true | ||
| 1704 | + | ||
| 1705 | + lightningcss-linux-x64-musl@1.32.0: | ||
| 1706 | + optional: true | ||
| 1707 | + | ||
| 1708 | + lightningcss-win32-arm64-msvc@1.32.0: | ||
| 1709 | + optional: true | ||
| 1710 | + | ||
| 1711 | + lightningcss-win32-x64-msvc@1.32.0: | ||
| 1712 | + optional: true | ||
| 1713 | + | ||
| 1714 | + lightningcss@1.32.0: | ||
| 1715 | + dependencies: | ||
| 1716 | + detect-libc: 2.1.2 | ||
| 1717 | + optionalDependencies: | ||
| 1718 | + lightningcss-android-arm64: 1.32.0 | ||
| 1719 | + lightningcss-darwin-arm64: 1.32.0 | ||
| 1720 | + lightningcss-darwin-x64: 1.32.0 | ||
| 1721 | + lightningcss-freebsd-x64: 1.32.0 | ||
| 1722 | + lightningcss-linux-arm-gnueabihf: 1.32.0 | ||
| 1723 | + lightningcss-linux-arm64-gnu: 1.32.0 | ||
| 1724 | + lightningcss-linux-arm64-musl: 1.32.0 | ||
| 1725 | + lightningcss-linux-x64-gnu: 1.32.0 | ||
| 1726 | + lightningcss-linux-x64-musl: 1.32.0 | ||
| 1727 | + lightningcss-win32-arm64-msvc: 1.32.0 | ||
| 1728 | + lightningcss-win32-x64-msvc: 1.32.0 | ||
| 1729 | + | ||
| 1730 | + lodash-es@4.17.23: {} | ||
| 1731 | + | ||
| 1732 | + lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.23)(lodash@4.17.23): | ||
| 1733 | + dependencies: | ||
| 1734 | + '@types/lodash-es': 4.17.12 | ||
| 1735 | + lodash: 4.17.23 | ||
| 1736 | + lodash-es: 4.17.23 | ||
| 1737 | + | ||
| 1738 | + lodash@4.17.23: {} | ||
| 1739 | + | ||
| 1740 | + lru-cache@10.4.3: {} | ||
| 1741 | + | ||
| 1742 | + lru-cache@11.2.7: {} | ||
| 1743 | + | ||
| 1744 | + magic-string@0.30.21: | ||
| 1745 | + dependencies: | ||
| 1746 | + '@jridgewell/sourcemap-codec': 1.5.5 | ||
| 1747 | + | ||
| 1748 | + mdn-data@2.27.1: {} | ||
| 1749 | + | ||
| 1750 | + medium-editor@5.23.3: {} | ||
| 1751 | + | ||
| 1752 | + memoize-one@6.0.0: {} | ||
| 1753 | + | ||
| 1754 | + minimatch@9.0.9: | ||
| 1755 | + dependencies: | ||
| 1756 | + brace-expansion: 2.0.3 | ||
| 1757 | + | ||
| 1758 | + minipass@7.1.3: {} | ||
| 1759 | + | ||
| 1760 | + mobx-preact@3.0.0(mobx@5.15.7)(preact@10.29.0): | ||
| 1761 | + dependencies: | ||
| 1762 | + hoist-non-react-statics: 2.5.5 | ||
| 1763 | + mobx: 5.15.7 | ||
| 1764 | + preact: 10.29.0 | ||
| 1765 | + | ||
| 1766 | + mobx-utils@5.6.2(mobx@5.15.7): | ||
| 1767 | + dependencies: | ||
| 1768 | + mobx: 5.15.7 | ||
| 1769 | + | ||
| 1770 | + mobx@5.15.7: {} | ||
| 1771 | + | ||
| 1772 | + mousetrap@1.6.5: {} | ||
| 1773 | + | ||
| 1774 | + muggle-string@0.4.1: {} | ||
| 1775 | + | ||
| 1776 | + nanoid@3.3.11: {} | ||
| 1777 | + | ||
| 1778 | + nopt@7.2.1: | ||
| 1779 | + dependencies: | ||
| 1780 | + abbrev: 2.0.0 | ||
| 1781 | + | ||
| 1782 | + normalize-wheel-es@1.2.0: {} | ||
| 1783 | + | ||
| 1784 | + obug@2.1.1: {} | ||
| 1785 | + | ||
| 1786 | + package-json-from-dist@1.0.1: {} | ||
| 1787 | + | ||
| 1788 | + parse5@8.0.0: | ||
| 1789 | + dependencies: | ||
| 1790 | + entities: 6.0.1 | ||
| 1791 | + | ||
| 1792 | + path-browserify@1.0.1: {} | ||
| 1793 | + | ||
| 1794 | + path-key@3.1.1: {} | ||
| 1795 | + | ||
| 1796 | + path-scurry@1.11.1: | ||
| 1797 | + dependencies: | ||
| 1798 | + lru-cache: 10.4.3 | ||
| 1799 | + minipass: 7.1.3 | ||
| 1800 | + | ||
| 1801 | + pathe@2.0.3: {} | ||
| 1802 | + | ||
| 1803 | + picocolors@1.1.1: {} | ||
| 1804 | + | ||
| 1805 | + picomatch@4.0.4: {} | ||
| 1806 | + | ||
| 1807 | + playwright-core@1.58.2: {} | ||
| 1808 | + | ||
| 1809 | + playwright@1.58.2: | ||
| 1810 | + dependencies: | ||
| 1811 | + playwright-core: 1.58.2 | ||
| 1812 | + optionalDependencies: | ||
| 1813 | + fsevents: 2.3.2 | ||
| 1814 | + | ||
| 1815 | + postcss@8.5.8: | ||
| 1816 | + dependencies: | ||
| 1817 | + nanoid: 3.3.11 | ||
| 1818 | + picocolors: 1.1.1 | ||
| 1819 | + source-map-js: 1.2.1 | ||
| 1820 | + | ||
| 1821 | + preact@10.29.0: {} | ||
| 1822 | + | ||
| 1823 | + proto-list@1.2.4: {} | ||
| 1824 | + | ||
| 1825 | + punycode@2.3.1: {} | ||
| 1826 | + | ||
| 1827 | + rangy@1.3.2: {} | ||
| 1828 | + | ||
| 1829 | + require-from-string@2.0.2: {} | ||
| 1830 | + | ||
| 1831 | + rolldown@1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1): | ||
| 1832 | + dependencies: | ||
| 1833 | + '@oxc-project/types': 0.122.0 | ||
| 1834 | + '@rolldown/pluginutils': 1.0.0-rc.12 | ||
| 1835 | + optionalDependencies: | ||
| 1836 | + '@rolldown/binding-android-arm64': 1.0.0-rc.12 | ||
| 1837 | + '@rolldown/binding-darwin-arm64': 1.0.0-rc.12 | ||
| 1838 | + '@rolldown/binding-darwin-x64': 1.0.0-rc.12 | ||
| 1839 | + '@rolldown/binding-freebsd-x64': 1.0.0-rc.12 | ||
| 1840 | + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.12 | ||
| 1841 | + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.12 | ||
| 1842 | + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.12 | ||
| 1843 | + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.12 | ||
| 1844 | + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.12 | ||
| 1845 | + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.12 | ||
| 1846 | + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.12 | ||
| 1847 | + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.12 | ||
| 1848 | + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) | ||
| 1849 | + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.12 | ||
| 1850 | + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.12 | ||
| 1851 | + transitivePeerDependencies: | ||
| 1852 | + - '@emnapi/core' | ||
| 1853 | + - '@emnapi/runtime' | ||
| 1854 | + | ||
| 1855 | + saxes@6.0.0: | ||
| 1856 | + dependencies: | ||
| 1857 | + xmlchars: 2.2.0 | ||
| 1858 | + | ||
| 1859 | + semver@7.7.4: {} | ||
| 1860 | + | ||
| 1861 | + shebang-command@2.0.0: | ||
| 1862 | + dependencies: | ||
| 1863 | + shebang-regex: 3.0.0 | ||
| 1864 | + | ||
| 1865 | + shebang-regex@3.0.0: {} | ||
| 1866 | + | ||
| 1867 | + siginfo@2.0.0: {} | ||
| 1868 | + | ||
| 1869 | + signal-exit@4.1.0: {} | ||
| 1870 | + | ||
| 1871 | + source-map-js@1.2.1: {} | ||
| 1872 | + | ||
| 1873 | + stackback@0.0.2: {} | ||
| 1874 | + | ||
| 1875 | + std-env@4.0.0: {} | ||
| 1876 | + | ||
| 1877 | + string-width@4.2.3: | ||
| 1878 | + dependencies: | ||
| 1879 | + emoji-regex: 8.0.0 | ||
| 1880 | + is-fullwidth-code-point: 3.0.0 | ||
| 1881 | + strip-ansi: 6.0.1 | ||
| 1882 | + | ||
| 1883 | + string-width@5.1.2: | ||
| 1884 | + dependencies: | ||
| 1885 | + eastasianwidth: 0.2.0 | ||
| 1886 | + emoji-regex: 9.2.2 | ||
| 1887 | + strip-ansi: 7.2.0 | ||
| 1888 | + | ||
| 1889 | + strip-ansi@6.0.1: | ||
| 1890 | + dependencies: | ||
| 1891 | + ansi-regex: 5.0.1 | ||
| 1892 | + | ||
| 1893 | + strip-ansi@7.2.0: | ||
| 1894 | + dependencies: | ||
| 1895 | + ansi-regex: 6.2.2 | ||
| 1896 | + | ||
| 1897 | + symbol-tree@3.2.4: {} | ||
| 1898 | + | ||
| 1899 | + tinybench@2.9.0: {} | ||
| 1900 | + | ||
| 1901 | + tinyexec@1.0.4: {} | ||
| 1902 | + | ||
| 1903 | + tinyglobby@0.2.15: | ||
| 1904 | + dependencies: | ||
| 1905 | + fdir: 6.5.0(picomatch@4.0.4) | ||
| 1906 | + picomatch: 4.0.4 | ||
| 1907 | + | ||
| 1908 | + tinyrainbow@3.1.0: {} | ||
| 1909 | + | ||
| 1910 | + tldts-core@7.0.27: {} | ||
| 1911 | + | ||
| 1912 | + tldts@7.0.27: | ||
| 1913 | + dependencies: | ||
| 1914 | + tldts-core: 7.0.27 | ||
| 1915 | + | ||
| 1916 | + tough-cookie@6.0.1: | ||
| 1917 | + dependencies: | ||
| 1918 | + tldts: 7.0.27 | ||
| 1919 | + | ||
| 1920 | + tr46@6.0.0: | ||
| 1921 | + dependencies: | ||
| 1922 | + punycode: 2.3.1 | ||
| 1923 | + | ||
| 1924 | + tslib@2.8.1: | ||
| 1925 | + optional: true | ||
| 1926 | + | ||
| 1927 | + typescript@6.0.2: {} | ||
| 1928 | + | ||
| 1929 | + undici-types@7.18.2: {} | ||
| 1930 | + | ||
| 1931 | + undici@7.24.6: {} | ||
| 1932 | + | ||
| 1933 | + uuid@9.0.1: {} | ||
| 1934 | + | ||
| 1935 | + vanilla-picker@2.12.3: | ||
| 1936 | + dependencies: | ||
| 1937 | + '@sphinxxxx/color-conversion': 2.2.2 | ||
| 1938 | + | ||
| 1939 | + vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0): | ||
| 1940 | + dependencies: | ||
| 1941 | + lightningcss: 1.32.0 | ||
| 1942 | + picomatch: 4.0.4 | ||
| 1943 | + postcss: 8.5.8 | ||
| 1944 | + rolldown: 1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) | ||
| 1945 | + tinyglobby: 0.2.15 | ||
| 1946 | + optionalDependencies: | ||
| 1947 | + '@types/node': 25.5.0 | ||
| 1948 | + fsevents: 2.3.3 | ||
| 1949 | + transitivePeerDependencies: | ||
| 1950 | + - '@emnapi/core' | ||
| 1951 | + - '@emnapi/runtime' | ||
| 1952 | + | ||
| 1953 | + vitest@4.1.2(@types/node@25.5.0)(jsdom@29.0.1)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)): | ||
| 1954 | + dependencies: | ||
| 1955 | + '@vitest/expect': 4.1.2 | ||
| 1956 | + '@vitest/mocker': 4.1.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)) | ||
| 1957 | + '@vitest/pretty-format': 4.1.2 | ||
| 1958 | + '@vitest/runner': 4.1.2 | ||
| 1959 | + '@vitest/snapshot': 4.1.2 | ||
| 1960 | + '@vitest/spy': 4.1.2 | ||
| 1961 | + '@vitest/utils': 4.1.2 | ||
| 1962 | + es-module-lexer: 2.0.0 | ||
| 1963 | + expect-type: 1.3.0 | ||
| 1964 | + magic-string: 0.30.21 | ||
| 1965 | + obug: 2.1.1 | ||
| 1966 | + pathe: 2.0.3 | ||
| 1967 | + picomatch: 4.0.4 | ||
| 1968 | + std-env: 4.0.0 | ||
| 1969 | + tinybench: 2.9.0 | ||
| 1970 | + tinyexec: 1.0.4 | ||
| 1971 | + tinyglobby: 0.2.15 | ||
| 1972 | + tinyrainbow: 3.1.0 | ||
| 1973 | + vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0) | ||
| 1974 | + why-is-node-running: 2.3.0 | ||
| 1975 | + optionalDependencies: | ||
| 1976 | + '@types/node': 25.5.0 | ||
| 1977 | + jsdom: 29.0.1 | ||
| 1978 | + transitivePeerDependencies: | ||
| 1979 | + - msw | ||
| 1980 | + | ||
| 1981 | + vscode-uri@3.1.0: {} | ||
| 1982 | + | ||
| 1983 | + vue-component-type-helpers@2.2.12: {} | ||
| 1984 | + | ||
| 1985 | + vue-component-type-helpers@3.2.6: {} | ||
| 1986 | + | ||
| 1987 | + vue-demi@0.14.10(vue@3.5.31(typescript@6.0.2)): | ||
| 1988 | + dependencies: | ||
| 1989 | + vue: 3.5.31(typescript@6.0.2) | ||
| 1990 | + | ||
| 1991 | + vue-tsc@3.2.6(typescript@6.0.2): | ||
| 1992 | + dependencies: | ||
| 1993 | + '@volar/typescript': 2.4.28 | ||
| 1994 | + '@vue/language-core': 3.2.6 | ||
| 1995 | + typescript: 6.0.2 | ||
| 1996 | + | ||
| 1997 | + vue@3.5.31(typescript@6.0.2): | ||
| 1998 | + dependencies: | ||
| 1999 | + '@vue/compiler-dom': 3.5.31 | ||
| 2000 | + '@vue/compiler-sfc': 3.5.31 | ||
| 2001 | + '@vue/runtime-dom': 3.5.31 | ||
| 2002 | + '@vue/server-renderer': 3.5.31(vue@3.5.31(typescript@6.0.2)) | ||
| 2003 | + '@vue/shared': 3.5.31 | ||
| 2004 | + optionalDependencies: | ||
| 2005 | + typescript: 6.0.2 | ||
| 2006 | + | ||
| 2007 | + w3c-xmlserializer@5.0.0: | ||
| 2008 | + dependencies: | ||
| 2009 | + xml-name-validator: 5.0.0 | ||
| 2010 | + | ||
| 2011 | + webidl-conversions@8.0.1: {} | ||
| 2012 | + | ||
| 2013 | + whatwg-mimetype@5.0.0: {} | ||
| 2014 | + | ||
| 2015 | + whatwg-url@16.0.1: | ||
| 2016 | + dependencies: | ||
| 2017 | + '@exodus/bytes': 1.15.0 | ||
| 2018 | + tr46: 6.0.0 | ||
| 2019 | + webidl-conversions: 8.0.1 | ||
| 2020 | + transitivePeerDependencies: | ||
| 2021 | + - '@noble/hashes' | ||
| 2022 | + | ||
| 2023 | + which@2.0.2: | ||
| 2024 | + dependencies: | ||
| 2025 | + isexe: 2.0.0 | ||
| 2026 | + | ||
| 2027 | + why-is-node-running@2.3.0: | ||
| 2028 | + dependencies: | ||
| 2029 | + siginfo: 2.0.0 | ||
| 2030 | + stackback: 0.0.2 | ||
| 2031 | + | ||
| 2032 | + wrap-ansi@7.0.0: | ||
| 2033 | + dependencies: | ||
| 2034 | + ansi-styles: 4.3.0 | ||
| 2035 | + string-width: 4.2.3 | ||
| 2036 | + strip-ansi: 6.0.1 | ||
| 2037 | + | ||
| 2038 | + wrap-ansi@8.1.0: | ||
| 2039 | + dependencies: | ||
| 2040 | + ansi-styles: 6.2.3 | ||
| 2041 | + string-width: 5.1.2 | ||
| 2042 | + strip-ansi: 7.2.0 | ||
| 2043 | + | ||
| 2044 | + xml-name-validator@5.0.0: {} | ||
| 2045 | + | ||
| 2046 | + xmlchars@2.2.0: {} |
pnpm-workspace.yaml
0 → 100644
progress.md
0 → 100644
| 1 | +# 进度记录 | ||
| 2 | + | ||
| 3 | +## 会话:2026-03-31 | ||
| 4 | + | ||
| 5 | +### 阶段 1:需求与旧项目调研 | ||
| 6 | +- **状态:** 已完成 | ||
| 7 | +- **开始时间:** 2026-03-31 | ||
| 8 | +- 已执行动作: | ||
| 9 | + - 阅读了 brainstorming、doc-coauthoring、planning-with-files 的工作流说明 | ||
| 10 | + - 确认新项目目录当前为空 | ||
| 11 | + - 检查旧项目仓库结构 | ||
| 12 | + - 阅读旧项目的 `package.json`、`README.md` 和入口文件 | ||
| 13 | + - 阅读旧项目命令系统、工具栏、画布初始化、行为注册和示例接入页 | ||
| 14 | + - 明确本次迁移应采用“行为优先 + 自动化保护”的重构路线 | ||
| 15 | +- 已创建/修改文件: | ||
| 16 | + - `task_plan.md`(新建) | ||
| 17 | + - `findings.md`(新建) | ||
| 18 | + - `progress.md`(新建) | ||
| 19 | + - `docs/plans/2026-03-31-flow-editor-rebuild-design.md`(新建) | ||
| 20 | + | ||
| 21 | +### 阶段 2:迁移策略与安全方案设计 | ||
| 22 | +- **状态:** 已完成 | ||
| 23 | +- 已执行动作: | ||
| 24 | + - 对比了“直接重写”和“先建基线再迁移”两种路线 | ||
| 25 | + - 明确了推荐的分阶段迁移策略 | ||
| 26 | + - 明确了测试优先的推进方式和每阶段的退出条件 | ||
| 27 | +- 已创建/修改文件: | ||
| 28 | + - `docs/plans/2026-03-31-flow-editor-rebuild-design.md` | ||
| 29 | + | ||
| 30 | +### 阶段 3:文档编写 | ||
| 31 | +- **状态:** 已完成 | ||
| 32 | +- 已执行动作: | ||
| 33 | + - 将需求范围、兼容契约、迁移阶段、测试策略和风险控制写入设计文档 | ||
| 34 | +- 已创建/修改文件: | ||
| 35 | + - `docs/plans/2026-03-31-flow-editor-rebuild-design.md` | ||
| 36 | + | ||
| 37 | +### 阶段 4:实施计划细化 | ||
| 38 | +- **状态:** 已完成 | ||
| 39 | +- 已执行动作: | ||
| 40 | + - 将迁移方案拆解成可以直接执行的任务清单 | ||
| 41 | + - 补充了旧仓库和新仓库的建议目录结构 | ||
| 42 | + - 明确了先旧项目基线、后新项目实现的执行顺序 | ||
| 43 | +- 已创建/修改文件: | ||
| 44 | + - `docs/plans/2026-03-31-flow-editor-migration-implementation-plan.md` | ||
| 45 | + - `task_plan.md` | ||
| 46 | + - `findings.md` | ||
| 47 | + - `progress.md` | ||
| 48 | + | ||
| 49 | +### 阶段 5:旧项目基线测试启动 | ||
| 50 | +- **状态:** 已完成 | ||
| 51 | +- 已执行动作: | ||
| 52 | + - 在旧项目新增基线回归页说明文档 | ||
| 53 | + - 新增 Playwright 配置、冒烟测试和页面对象辅助文件 | ||
| 54 | + - 确认旧项目需要使用 `.nvmrc` 中的 Node `v16.17.0` 启动 | ||
| 55 | + - 通过 Playwright 拦截初始化接口,绕过真实后端登录态依赖 | ||
| 56 | + - 为旧项目基线页补充测试探针,覆盖实例契约、节点面板、选中状态、删除撤销链路 | ||
| 57 | + - 继续补齐多选、全选、新增节点、新增连线回归 | ||
| 58 | + - 跑通旧项目 13 条基线回归测试 | ||
| 59 | +- 已创建/修改文件: | ||
| 60 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/doc/README-基线回归页.md` | ||
| 61 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/playwright.config.ts` | ||
| 62 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/smoke.spec.ts` | ||
| 63 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/toolbar.spec.ts` | ||
| 64 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/preview.spec.ts` | ||
| 65 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/version.spec.ts` | ||
| 66 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/save.spec.ts` | ||
| 67 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/panel.spec.ts` | ||
| 68 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/multi-select.spec.ts` | ||
| 69 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/select-all.spec.ts` | ||
| 70 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/selection.spec.ts` | ||
| 71 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/delete-undo-redo.spec.ts` | ||
| 72 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/contract.spec.ts` | ||
| 73 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/add-node.spec.ts` | ||
| 74 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/e2e/add-edge.spec.ts` | ||
| 75 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/helpers/editor-page.ts` | ||
| 76 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/tests/helpers/mock-flow-api.ts` | ||
| 77 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/package.json` | ||
| 78 | + - `/Users/huyirui/program/itomix/git/vue-flow-editor/doc/index.vue` | ||
| 79 | + - `findings.md` | ||
| 80 | + - `progress.md` | ||
| 81 | + | ||
| 82 | +### 阶段 6:新项目工程骨架与测试底座初始化 | ||
| 83 | +- **状态:** 已完成 | ||
| 84 | +- 已执行动作: | ||
| 85 | + - 初始化了新项目 `package.json`、`pnpm-workspace.yaml`、`tsconfig.json`、`vite.config.ts`、`vitest.config.ts` | ||
| 86 | + - 建立了 `apps/demo`、`packages/flow-editor`、`tests` 的目录结构 | ||
| 87 | + - 接入了 Vue 3、Element Plus、LogicFlow 2.x、Vitest、Playwright | ||
| 88 | + - 按 LogicFlow 2.x 规则接入了样式路径 `lib/style/index.css` | ||
| 89 | + - 在 `packages/flow-editor` 中实现了最小兼容壳组件,恢复旧项目 DOM 结构、工具栏语义和 `onRef` 基础契约 | ||
| 90 | + - 在 demo 页接入旧项目初始化接口语义与统一 mock 方式,方便继续复用旧项目测试思路 | ||
| 91 | + - 确认 LogicFlow 2.x 的 `pluginsOptions` 需要按插件名拆分,并按 `miniMap` 配置 MiniMap 插件 | ||
| 92 | + - 新增了新项目首批 3 条 Playwright 基线用例:冒烟、工具栏、实例契约 | ||
| 93 | + - 补充了 1 条单元测试,验证兼容层 `onRef` 最小契约 | ||
| 94 | + - 将 E2E 启动方式改为仓库脚本 `scripts/run-e2e.mjs` 自管 Vite 生命周期,避免当前环境下 `webServer` 误判端口 | ||
| 95 | +- 已创建/修改文件: | ||
| 96 | + - `package.json` | ||
| 97 | + - `pnpm-workspace.yaml` | ||
| 98 | + - `tsconfig.json` | ||
| 99 | + - `env.d.ts` | ||
| 100 | + - `vite.config.ts` | ||
| 101 | + - `vitest.config.ts` | ||
| 102 | + - `playwright.config.ts` | ||
| 103 | + - `scripts/run-e2e.mjs` | ||
| 104 | + - `apps/demo/index.html` | ||
| 105 | + - `apps/demo/src/main.ts` | ||
| 106 | + - `apps/demo/src/App.vue` | ||
| 107 | + - `packages/flow-editor/src/index.ts` | ||
| 108 | + - `packages/flow-editor/src/types.ts` | ||
| 109 | + - `packages/flow-editor/src/components/FlowEditor.vue` | ||
| 110 | + - `tests/helpers/editor-page.ts` | ||
| 111 | + - `tests/helpers/mock-flow-api.ts` | ||
| 112 | + - `tests/e2e/smoke.spec.ts` | ||
| 113 | + - `tests/e2e/toolbar.spec.ts` | ||
| 114 | + - `tests/e2e/contract.spec.ts` | ||
| 115 | + - `tests/unit/compat-contract.spec.ts` | ||
| 116 | + - `tests/fixtures/graphs/linear-approval-flow.json` | ||
| 117 | + - `tests/fixtures/operations/README.md` | ||
| 118 | + - `findings.md` | ||
| 119 | + - `progress.md` | ||
| 120 | + - `task_plan.md` | ||
| 121 | + | ||
| 122 | +### 阶段 7:新项目数据适配层 | ||
| 123 | +- **状态:** 已完成 | ||
| 124 | +- 已执行动作: | ||
| 125 | + - 梳理了旧项目节点与边的真实数据口径,确认旧结构中常见 `text/shape/activity/control`、`source/target` | ||
| 126 | + - 新增 `core/schema/types.ts` 与 `core/adapter/g6-to-logicflow.ts` | ||
| 127 | + - 实现了 `normalizeGraphData`、`cloneNormalizedGraphData`、`toLogicFlowGraphData` | ||
| 128 | + - 让 `FlowEditor` 内部统一改为走适配器,再映射到 LogicFlow 2.x | ||
| 129 | + - 让 demo 页面与 E2E mock 直接喂旧口径数据,以验证双读能力 | ||
| 130 | + - 新增单节点、线性流、分支流三类图数据夹具 | ||
| 131 | + - 新增适配层单元测试,验证旧结构归一化和 LogicFlow 映射结果 | ||
| 132 | +- 已创建/修改文件: | ||
| 133 | + - `packages/flow-editor/src/core/schema/types.ts` | ||
| 134 | + - `packages/flow-editor/src/core/adapter/g6-to-logicflow.ts` | ||
| 135 | + - `packages/flow-editor/src/components/FlowEditor.vue` | ||
| 136 | + - `packages/flow-editor/src/index.ts` | ||
| 137 | + - `apps/demo/src/App.vue` | ||
| 138 | + - `tests/helpers/mock-flow-api.ts` | ||
| 139 | + - `tests/fixtures/graphs/base-single-node.json` | ||
| 140 | + - `tests/fixtures/graphs/branching-flow.json` | ||
| 141 | + - `tests/unit/g6-to-logicflow.spec.ts` | ||
| 142 | + - `task_plan.md` | ||
| 143 | + - `findings.md` | ||
| 144 | + - `progress.md` | ||
| 145 | + | ||
| 146 | +### 阶段 8:新项目基础编辑闭环 | ||
| 147 | +- **状态:** 已完成 | ||
| 148 | +- 已执行动作: | ||
| 149 | + - 接通了 `FlowEditor` 工具栏按钮点击逻辑,支持 `fitView`、`actualView`、`zoomIn`、`zoomOut`、`preview` | ||
| 150 | + - 通过 LogicFlow 事件同步节点选中态,支持画布节点点击选中与空白区取消选中 | ||
| 151 | + - 在侧边区域补充了基础节点操作面板,支持新增审批节点、新增抄送节点、全选、删除当前节点 | ||
| 152 | + - 补充了节点列表与当前选中展示,便于页面直接演示和调试 | ||
| 153 | + - 补充了预览面板,并在 demo 页把“居中”“保存”“预览测试”三个额外按钮接上真实动作 | ||
| 154 | + - 新增交互 E2E 用例,验证新增节点、删除节点、缩放操作和打开预览 | ||
| 155 | + - 调整 `scripts/run-e2e.mjs`,支持向 Playwright 透传单文件参数 | ||
| 156 | +- 已创建/修改文件: | ||
| 157 | + - `packages/flow-editor/src/components/FlowEditor.vue` | ||
| 158 | + - `apps/demo/src/App.vue` | ||
| 159 | + - `tests/e2e/interaction.spec.ts` | ||
| 160 | + - `scripts/run-e2e.mjs` | ||
| 161 | + - `task_plan.md` | ||
| 162 | + - `findings.md` | ||
| 163 | + - `progress.md` | ||
| 164 | + | ||
| 165 | +### 阶段 9:新项目命令闭环增强 | ||
| 166 | +- **状态:** 已完成 | ||
| 167 | +- 已执行动作: | ||
| 168 | + - 移除了覆盖在画布上的迁移提示层,恢复更直接的可视化编辑视图 | ||
| 169 | + - 为组件新增图数据历史快照,支持撤销与重做 | ||
| 170 | + - 支持更新当前选中节点名称,并在侧边面板实时展示 | ||
| 171 | + - 支持基于当前多选节点创建新连线,并对已有同路径连线做去重保护 | ||
| 172 | + - 为节点列表补充稳定测试标识,方便交互回归稳定定位 | ||
| 173 | + - 扩展交互 E2E 用例,覆盖节点名更新、创建连线、撤销/重做 | ||
| 174 | + - 将测试环境账号信息写入 `findings.md` | ||
| 175 | +- 已创建/修改文件: | ||
| 176 | + - `packages/flow-editor/src/components/FlowEditor.vue` | ||
| 177 | + - `tests/e2e/interaction.spec.ts` | ||
| 178 | + - `findings.md` | ||
| 179 | + - `task_plan.md` | ||
| 180 | + - `progress.md` | ||
| 181 | + | ||
| 182 | +## 检查结果 | ||
| 183 | +| 检查项 | 输入 | 预期 | 实际 | 状态 | | ||
| 184 | +|--------|------|------|------|------| | ||
| 185 | +| 新仓库检查 | 在新仓库执行 `ls -la` | 确认起始状态 | 仓库为空 | 通过 | | ||
| 186 | +| 旧项目依赖检查 | 读取旧项目关键文件 | 确认技术栈与功能范围 | 确认存在混合技术栈与编辑器完整能力 | 通过 | | ||
| 187 | +| 旧项目命令层检查 | 读取 `src/editor/editor.ts` 和 `src/plugins/Command.ts` | 找到稳定行为核心 | 确认命令式架构适合作为迁移契约 | 通过 | | ||
| 188 | +| 旧项目接入页检查 | 读取 `doc/index.vue` | 找到真实回归样板 | 确认该页面适合作为自动化基线页 | 通过 | | ||
| 189 | +| 实施计划检查 | 阅读设计文档并拆分任务 | 形成可执行迁移顺序 | 已输出逐任务迁移实施计划 | 通过 | | ||
| 190 | +| 旧项目冒烟测试 | `npx playwright test tests/e2e/smoke.spec.ts` | 基线页可在自动化环境中打开并渲染编辑器骨架 | 1 条用例通过 | 通过 | | ||
| 191 | +| 旧项目工具栏测试 | `npx playwright test tests/e2e/toolbar.spec.ts` | 基线页显示的核心工具栏按钮符合当前业务配置 | 1 条用例通过 | 通过 | | ||
| 192 | +| 旧项目预览测试 | `npx playwright test tests/e2e/preview.spec.ts` | 点击“预览测试”后可打开预览抽屉 | 1 条用例通过 | 通过 | | ||
| 193 | +| 旧项目版本区测试 | `npx playwright test tests/e2e/version.spec.ts` | 页面正确展示当前版本和启用状态 | 1 条用例通过 | 通过 | | ||
| 194 | +| 旧项目保存确认测试 | `npx playwright test tests/e2e/save.spec.ts` | 点击保存后弹出确认框 | 1 条用例通过 | 通过 | | ||
| 195 | +| 旧项目节点面板测试 | `npx playwright test tests/e2e/panel.spec.ts` | 通过测试探针打开流程节点右侧面板 | 1 条用例通过 | 通过 | | ||
| 196 | +| 旧项目选中状态测试 | `npx playwright test tests/e2e/selection.spec.ts` | 通过测试探针选中流程节点 | 1 条用例通过 | 通过 | | ||
| 197 | +| 旧项目删除撤销测试 | `npx playwright test tests/e2e/delete-undo-redo.spec.ts` | 删除抄送节点后可撤销、可重做 | 1 条用例通过 | 通过 | | ||
| 198 | +| 旧项目实例契约测试 | `npx playwright test tests/e2e/contract.spec.ts` | `onRef` 核心实例能力存在 | 1 条用例通过 | 通过 | | ||
| 199 | +| 旧项目多选测试 | `npx playwright test tests/e2e/multi-select.spec.ts` | 通过测试探针多选节点 | 1 条用例通过 | 通过 | | ||
| 200 | +| 旧项目全选测试 | `npx playwright test tests/e2e/select-all.spec.ts` | 通过命令链全选流程图元素 | 1 条用例通过 | 通过 | | ||
| 201 | +| 旧项目新增节点测试 | `npx playwright test tests/e2e/add-node.spec.ts` | 通过测试探针新增流程节点 | 1 条用例通过 | 通过 | | ||
| 202 | +| 旧项目新增连线测试 | `npx playwright test tests/e2e/add-edge.spec.ts` | 通过测试探针新增连线 | 1 条用例通过 | 通过 | | ||
| 203 | +| 旧项目基线回归套件 | `npx playwright test tests/e2e/*.spec.ts` | 所有基线用例可一起通过 | 13 条用例全部通过 | 通过 | | ||
| 204 | +| 新项目单元测试 | `npm run test:unit` | 兼容层最小实例契约可验证 | 1 条用例通过 | 通过 | | ||
| 205 | +| 新项目构建 | `npm run build` | 新项目骨架可正常构建 | 构建通过 | 通过 | | ||
| 206 | +| 新项目 E2E 基线套件 | `npm run test:e2e` | 新项目冒烟、工具栏、实例契约用例可通过 | 3 条用例全部通过 | 通过 | | ||
| 207 | +| 新项目适配层单元测试 | `npm run test:unit` | 旧结构、新结构与 LogicFlow 映射可验证 | 4 条用例全部通过 | 通过 | | ||
| 208 | +| 新项目适配层浏览器回归 | `npm run test:e2e` | demo 可消费旧口径 mock 并保持首批基线语义 | 3 条用例全部通过 | 通过 | | ||
| 209 | +| 新项目基础交互单测 | `npm run test:unit` | 兼容层与适配层未被交互改动破坏 | 4 条用例全部通过 | 通过 | | ||
| 210 | +| 新项目基础交互构建 | `npm run build` | 页面交互增强后仍可正常构建 | 构建通过 | 通过 | | ||
| 211 | +| 新项目基础交互 E2E | `npm run test:e2e` | 冒烟、工具栏、实例契约、基础交互全量通过 | 5 条用例全部通过 | 通过 | | ||
| 212 | +| 新项目命令闭环单测 | `npm run test:unit` | 适配层与兼容层未被命令增强破坏 | 4 条用例全部通过 | 通过 | | ||
| 213 | +| 新项目命令闭环构建 | `npm run build` | 命令增强后页面仍可正常构建 | 构建通过 | 通过 | | ||
| 214 | +| 新项目命令闭环 E2E | `npm run test:e2e` | 冒烟、契约、基础交互、命令闭环全量通过 | 7 条用例全部通过 | 通过 | | ||
| 215 | + | ||
| 216 | +## 错误记录 | ||
| 217 | +| 时间 | 错误 | 次数 | 处理方式 | | ||
| 218 | +|------|------|------|----------| | ||
| 219 | +| 2026-03-31 | `rg --files` 在新仓库没有返回文件 | 1 | 改为确认目录状态,验证新仓库确实为空 | | ||
| 220 | +| 2026-03-31 | 旧项目 `npm install` 出现 peer 依赖冲突 | 1 | 改用更小范围的测试基础设施接入,并避免将基线测试绑定到本地 `@playwright/test` 安装 | | ||
| 221 | +| 2026-03-31 | 旧项目默认 Node 18 启动 Vue CLI 失败 | 1 | 改为使用 `.nvmrc` 指定的 Node `v16.17.0` 启动基线页 | | ||
| 222 | +| 2026-03-31 | 冒烟测试首页被“登录失效”弹框拦截 | 1 | 在测试中拦截 `flow_version` 和 `flow_nodes` 初始化接口,提供稳定假数据 | | ||
| 223 | +| 2026-03-31 | 工具栏测试错误假设了组件默认按钮都可见 | 1 | 改为按 `toolbarButtonHandler` 过滤后的真实展示结果做断言 | | ||
| 224 | +| 2026-03-31 | 预览测试再次被“登录失效”弹框拦截 | 1 | 将接口 mock 升级为统一的 `/admin/**` 兜底拦截,避免遗漏后台接口 | | ||
| 225 | +| 2026-03-31 | 版本区测试因文本定位过宽导致严格模式冲突 | 1 | 改为用角色按钮定位版本选择入口 | | ||
| 226 | +| 2026-03-31 | 保存链路依赖节点完整性检查接口 | 1 | 在统一 mock 中补 `check_all_node_property` 空数组返回,先稳定验证确认框链路 | | ||
| 227 | +| 2026-03-31 | 节点面板测试因节点属性接口返回结构不稳定失败 | 1 | 将 `/admin/**` 兜底 mock 补充为页面可消费的最小业务结构 | | ||
| 228 | +| 2026-03-31 | 删除流程节点触发了开始/结束边保护逻辑 | 1 | 改为删除 `cc` 节点,保留一条完整流程路径并验证删除/撤销/重做闭环 | | ||
| 229 | +| 2026-03-31 | 旧项目 canvas 交互不适合长期依赖坐标点击 | 1 | 通过测试探针补齐多选、全选、新增节点、新增连线等稳定验证入口 | | ||
| 230 | +| 2026-03-31 | `corepack pnpm install` 出现签名 `keyid` 校验失败 | 1 | 改为 `npx pnpm@10.9.0 install` 完成依赖安装 | | ||
| 231 | +| 2026-03-31 | 新项目初次渲染时对 Vue props 使用 `structuredClone` 导致浏览器报错 | 1 | 改为显式 `cloneGraphData` 数据拷贝函数 | | ||
| 232 | +| 2026-03-31 | Playwright `webServer` 在当前环境中会误用旧服务或误判端口占用 | 1 | 改为 `scripts/run-e2e.mjs` 手动启动 Vite 后执行测试 | | ||
| 233 | +| 2026-03-31 | 旧项目与新项目图数据字段名不一致 | 1 | 新增 `g6-to-logicflow` 适配层,统一归一化后再渲染 | | ||
| 234 | +| 2026-03-31 | `test:e2e` 脚本最初不能透传单文件参数 | 1 | 在 `scripts/run-e2e.mjs` 中补充 `process.argv.slice(2)` 转发 | | ||
| 235 | +| 2026-03-31 | 创建连线的交互用例一开始选中了已存在连线路径 | 1 | 调整为“新增节点后再创建新连线”的真实新增路径 | | ||
| 236 | + | ||
| 237 | +## 五问自检 | ||
| 238 | +| 问题 | 回答 | | ||
| 239 | +|------|------| | ||
| 240 | +| 我现在在哪个阶段? | 新项目已经完成第二个可演示里程碑,页面具备基础命令闭环 | | ||
| 241 | +| 下一步要去哪里? | 继续迁移更完整的边交互、属性面板和业务事件兼容层 | | ||
| 242 | +| 当前总目标是什么? | 在新仓中逐步重建流程编辑器,同时让旧项目与新项目都处于可验证状态 | | ||
| 243 | +| 目前学到了什么? | 通过兼容层状态驱动加上 LogicFlow 2.x 渲染,可以较快搭出稳定的用户可见命令闭环,再逐步下沉到更深层能力 | | ||
| 244 | +| 已经完成了什么? | 已建立计划文件,完成旧项目基线测试,也完成了新项目工程骨架、LogicFlow 2.x 最小接入、数据适配层、基础编辑闭环与命令闭环增强 | |
scripts/run-e2e.mjs
0 → 100644
| 1 | +import { spawn } from 'node:child_process' | ||
| 2 | + | ||
| 3 | +const VITE_READY_PATTERN = /127\.0\.0\.1:4174/ | ||
| 4 | +const START_TIMEOUT = 20_000 | ||
| 5 | + | ||
| 6 | +function waitForServer(serverProcess) { | ||
| 7 | + return new Promise((resolve, reject) => { | ||
| 8 | + const timeout = setTimeout(() => { | ||
| 9 | + reject(new Error('等待 Vite 测试服务启动超时')) | ||
| 10 | + }, START_TIMEOUT) | ||
| 11 | + | ||
| 12 | + const handleOutput = (chunk) => { | ||
| 13 | + const text = chunk.toString() | ||
| 14 | + process.stdout.write(text) | ||
| 15 | + | ||
| 16 | + if (VITE_READY_PATTERN.test(text)) { | ||
| 17 | + clearTimeout(timeout) | ||
| 18 | + serverProcess.stdout.off('data', handleOutput) | ||
| 19 | + serverProcess.stderr.off('data', handleOutput) | ||
| 20 | + resolve() | ||
| 21 | + } | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + serverProcess.stdout.on('data', handleOutput) | ||
| 25 | + serverProcess.stderr.on('data', handleOutput) | ||
| 26 | + serverProcess.once('exit', (code) => { | ||
| 27 | + clearTimeout(timeout) | ||
| 28 | + reject(new Error(`Vite 测试服务启动失败,退出码:${code ?? 'unknown'}`)) | ||
| 29 | + }) | ||
| 30 | + }) | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +function killProcessTree(serverProcess) { | ||
| 34 | + return new Promise((resolve) => { | ||
| 35 | + if (serverProcess.killed) { | ||
| 36 | + resolve() | ||
| 37 | + return | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + serverProcess.once('exit', () => resolve()) | ||
| 41 | + serverProcess.kill('SIGTERM') | ||
| 42 | + | ||
| 43 | + setTimeout(() => { | ||
| 44 | + if (!serverProcess.killed) { | ||
| 45 | + serverProcess.kill('SIGKILL') | ||
| 46 | + } | ||
| 47 | + }, 3_000) | ||
| 48 | + }) | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +async function main() { | ||
| 52 | + const playwrightArgs = process.argv.slice(2) | ||
| 53 | + const serverProcess = spawn( | ||
| 54 | + 'npx', | ||
| 55 | + ['vite', '--host', '127.0.0.1', '--port', '4174', '--strictPort'], | ||
| 56 | + { | ||
| 57 | + stdio: ['ignore', 'pipe', 'pipe'], | ||
| 58 | + env: process.env, | ||
| 59 | + }, | ||
| 60 | + ) | ||
| 61 | + | ||
| 62 | + try { | ||
| 63 | + await waitForServer(serverProcess) | ||
| 64 | + | ||
| 65 | + const testProcess = spawn('npx', ['playwright', 'test', ...playwrightArgs], { | ||
| 66 | + stdio: 'inherit', | ||
| 67 | + env: process.env, | ||
| 68 | + }) | ||
| 69 | + | ||
| 70 | + const exitCode = await new Promise((resolve, reject) => { | ||
| 71 | + testProcess.once('exit', (code) => resolve(code ?? 1)) | ||
| 72 | + testProcess.once('error', reject) | ||
| 73 | + }) | ||
| 74 | + | ||
| 75 | + process.exitCode = exitCode | ||
| 76 | + } finally { | ||
| 77 | + await killProcessTree(serverProcess) | ||
| 78 | + } | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +main().catch((error) => { | ||
| 82 | + console.error(error) | ||
| 83 | + process.exitCode = 1 | ||
| 84 | +}) |
task_plan.md
0 → 100644
| 1 | +# 任务计划:流程编辑器重构调研 | ||
| 2 | + | ||
| 3 | +## 目标 | ||
| 4 | + | ||
| 5 | +输出一份完整的需求与迁移设计方案,用于将 `vue-flow-editor` 重构为基于 Vue 3、Element Plus、LogicFlow 2.0、Vite 的新项目,并采用“测试优先、分阶段迁移”的安全升级策略。 | ||
| 6 | + | ||
| 7 | +## 当前阶段 | ||
| 8 | + | ||
| 9 | +阶段 10 | ||
| 10 | + | ||
| 11 | +## 阶段拆分 | ||
| 12 | + | ||
| 13 | +### 阶段 1:需求与旧项目调研 | ||
| 14 | +- [x] 理解用户诉求 | ||
| 15 | +- [x] 确认新项目当前起点 | ||
| 16 | +- [x] 审计旧项目架构、行为能力和对外 API | ||
| 17 | +- [x] 记录迁移约束与测试风险 | ||
| 18 | +- **状态:** 已完成 | ||
| 19 | + | ||
| 20 | +### 阶段 2:迁移策略与安全方案设计 | ||
| 21 | +- [x] 定义分阶段迁移路径 | ||
| 22 | +- [x] 定义重构前的基线保护策略 | ||
| 23 | +- [x] 定义兼容口径与验收清单 | ||
| 24 | +- **状态:** 已完成 | ||
| 25 | + | ||
| 26 | +### 阶段 3:文档编写 | ||
| 27 | +- [x] 编写需求与设计文档 | ||
| 28 | +- [x] 编写分阶段迁移方案 | ||
| 29 | +- [x] 编写测试迁移与验证方案 | ||
| 30 | +- **状态:** 已完成 | ||
| 31 | + | ||
| 32 | +### 阶段 4:复核与收敛 | ||
| 33 | +- [x] 基于旧项目再次核对关键假设 | ||
| 34 | +- [x] 收紧风险、开放问题和优先级 | ||
| 35 | +- [x] 形成可直接开工的初始方案 | ||
| 36 | +- **状态:** 已完成 | ||
| 37 | + | ||
| 38 | +### 阶段 5:实施计划细化 | ||
| 39 | +- [x] 将迁移方案细化成可执行任务清单 | ||
| 40 | +- [x] 补充双仓目录规划和阶段性产物 | ||
| 41 | +- [x] 输出下一步的明确实施顺序 | ||
| 42 | +- **状态:** 已完成 | ||
| 43 | + | ||
| 44 | +### 阶段 6:旧项目基线测试落地 | ||
| 45 | +- [x] 固定旧项目基线回归页说明 | ||
| 46 | +- [x] 接入 Playwright 最小测试基础设施 | ||
| 47 | +- [x] 跑通第一条旧项目冒烟测试 | ||
| 48 | +- **状态:** 已完成 | ||
| 49 | + | ||
| 50 | +### 阶段 7:新项目骨架与测试底座初始化 | ||
| 51 | +- [x] 初始化 Vue 3 + Vite + TypeScript + pnpm workspace 工程 | ||
| 52 | +- [x] 接入 Element Plus 与 LogicFlow 2.x 基础依赖 | ||
| 53 | +- [x] 建立 `apps/demo`、`packages/flow-editor`、`tests` 目录结构 | ||
| 54 | +- [x] 迁入首批基线测试语义(冒烟、工具栏、实例契约) | ||
| 55 | +- [x] 跑通新项目 `build`、`test:unit`、`test:e2e` | ||
| 56 | +- **状态:** 已完成 | ||
| 57 | + | ||
| 58 | +### 阶段 8:新项目数据适配层 | ||
| 59 | +- [x] 梳理旧项目图数据与当前规范数据的差异 | ||
| 60 | +- [x] 实现 `g6-to-logicflow` 兼容适配层 | ||
| 61 | +- [x] 让 demo 与编辑器内部统一走适配器 | ||
| 62 | +- [x] 补齐单节点、线性流、分支流三类夹具与单元测试 | ||
| 63 | +- [x] 跑通适配层相关验证 | ||
| 64 | +- **状态:** 已完成 | ||
| 65 | + | ||
| 66 | +### 阶段 9:新项目基础编辑闭环 | ||
| 67 | +- [x] 接通工具栏主要按钮的真实点击行为 | ||
| 68 | +- [x] 支持新增节点、选中节点、删除节点 | ||
| 69 | +- [x] 支持打开预览面板 | ||
| 70 | +- [x] 为基础交互补齐 E2E 回归 | ||
| 71 | +- **状态:** 已完成 | ||
| 72 | + | ||
| 73 | +### 阶段 10:新项目命令闭环增强 | ||
| 74 | +- [x] 移除遮挡画布的过渡提示层 | ||
| 75 | +- [x] 支持节点名称更新 | ||
| 76 | +- [x] 支持基于当前选中节点创建连线 | ||
| 77 | +- [x] 支持图数据撤销/重做 | ||
| 78 | +- [x] 为命令闭环补齐 E2E 回归 | ||
| 79 | +- **状态:** 已完成 | ||
| 80 | + | ||
| 81 | +## 关键问题 | ||
| 82 | +1. 旧项目里哪些行为必须被视为严格兼容项,哪些属于可接受的现代化调整? | ||
| 83 | +2. 如何在迁移编辑器内核之前,先建立可靠的自动化回归护栏? | ||
| 84 | +3. 应该优先迁移哪些能力,才能保证每一阶段都可验证、可回退? | ||
| 85 | + | ||
| 86 | +## 已做决策 | ||
| 87 | +| 决策 | 原因 | | ||
| 88 | +|------|------| | ||
| 89 | +| 先写文档和迁移设计,再启动代码搭建 | 新仓库当前为空,且用户明确希望优先保证升级安全性 | | ||
| 90 | +| 采用“行为优先”的迁移方式,而不是“依赖升级优先” | 旧项目存在混合技术栈,直接升级依赖风险高 | | ||
| 91 | +| 将测试视为本次迁移的一等产物 | 用户无法在重构后进行大量人工点击回归 | | ||
| 92 | +| 先把旧项目 `doc/index.vue` 作为第一份基线回归样板 | 该页面已覆盖 hooks、事件、插槽和复杂业务接入 | | ||
| 93 | +| 在新项目大规模开发前先迁移基线测试 | 这是保住现有业务行为最稳妥的方式 | | ||
| 94 | +| 继续补一份“实施级迁移计划”文档 | 这样后续可以按任务直接开工,而不是重复讨论阶段划分 | | ||
| 95 | +| 旧项目基线测试先采用“Node 16 + 接口拦截”的运行方式 | 这是当前成本最低、稳定性最高的自动化起步方案 | | ||
| 96 | +| 新项目先用“兼容壳层 + 最小 LogicFlow 2.x 实例”承接测试语义 | 这样可以在不提前实现全部交互的前提下,尽快建立新仓回归护栏 | | ||
| 97 | +| 新项目 E2E 改为脚本自管 Vite 生命周期 | 当前环境中 Playwright `webServer` 预检查会被本机代理/旧服务误导,脚本启动更稳 | | ||
| 98 | +| 新项目内部图数据统一先归一化,再映射到 LogicFlow 2.x | 这样后续命令系统、事件兼容层和保存链路都只面对一套稳定结构 | | ||
| 99 | +| 先做“可操作的基础编辑闭环”,再补复杂命令和业务面板 | 这样用户可以尽早看到真实可交互页面,而不是一直停留在只读状态 | | ||
| 100 | +| 连线创建和撤销/重做先基于兼容层图数据历史实现 | 先把用户操作闭环跑通,再逐步下沉到更完整的 LogicFlow 命令栈 | | ||
| 101 | + | ||
| 102 | +## 遇到的问题 | ||
| 103 | +| 问题 | 次数 | 处理方式 | | ||
| 104 | +|------|------|----------| | ||
| 105 | +| `rg --files` 在新仓库中没有返回文件 | 1 | 通过 `ls -la` 确认新仓库确实为空仓库,因此先从规划与文档开始 | | ||
| 106 | +| `corepack pnpm install` 出现签名 `keyid` 校验失败 | 1 | 改为使用 `npx pnpm@10.9.0 install` 绕过本机 corepack 问题 | | ||
| 107 | +| Playwright `webServer` 会误判本地端口已被占用 | 1 | 移除 `webServer` 配置,改为仓库脚本显式启动 Vite 并回收进程 | | ||
| 108 | + | ||
| 109 | +## 备注 | ||
| 110 | +- 在做重大决策前,重新回看本计划文件 | ||
| 111 | +- 继续将旧项目调研结论记录到 `findings.md` | ||
| 112 | +- 最终设计文档路径为 `docs/plans/2026-03-31-flow-editor-rebuild-design.md` | ||
| 113 | +- 实施计划文档路径为 `docs/plans/2026-03-31-flow-editor-migration-implementation-plan.md` |
tests/e2e/contract.spec.ts
0 → 100644
| 1 | +import { expect, test } from '@playwright/test' | ||
| 2 | +import { mockFlowBootstrap } from '../helpers/mock-flow-api' | ||
| 3 | + | ||
| 4 | +test('新项目基线页应暴露稳定的编辑器实例契约', async ({ page }) => { | ||
| 5 | + await mockFlowBootstrap(page) | ||
| 6 | + | ||
| 7 | + await page.goto('/') | ||
| 8 | + await expect(page.locator('.vue-flow-editor')).toBeVisible() | ||
| 9 | + | ||
| 10 | + const contract = await page.evaluate(() => { | ||
| 11 | + return window.__FLOW_EDITOR_TEST_API__?.getEditorContract() | ||
| 12 | + }) | ||
| 13 | + | ||
| 14 | + expect(contract?.hasEditor).toBe(true) | ||
| 15 | + expect(contract?.hasCommander).toBe(true) | ||
| 16 | + expect(contract?.hasDelete).toBe(true) | ||
| 17 | + expect(contract?.hasUndo).toBe(true) | ||
| 18 | + expect(contract?.hasRedo).toBe(true) | ||
| 19 | + expect(contract?.hasOpenModel).toBe(true) | ||
| 20 | + expect(contract?.hasCloseModel).toBe(true) | ||
| 21 | + expect(contract?.hasAddNode).toBe(true) | ||
| 22 | + expect(contract?.hasUpdateModel).toBe(true) | ||
| 23 | + expect(contract?.hasGraphRead).toBe(true) | ||
| 24 | +}) | ||
| 25 | + |
tests/e2e/interaction.spec.ts
0 → 100644
| 1 | +import { expect, test } from '@playwright/test' | ||
| 2 | +import { mockFlowBootstrap } from '../helpers/mock-flow-api' | ||
| 3 | + | ||
| 4 | +test('新项目页面应支持新增节点并删除当前选中节点', async ({ page }) => { | ||
| 5 | + await mockFlowBootstrap(page) | ||
| 6 | + | ||
| 7 | + await page.goto('/') | ||
| 8 | + | ||
| 9 | + await expect(page.getByTestId('node-count-value')).toHaveText('2') | ||
| 10 | + | ||
| 11 | + await page.getByTestId('add-approval-node').click() | ||
| 12 | + | ||
| 13 | + await expect(page.getByTestId('node-count-value')).toHaveText('3') | ||
| 14 | + await expect(page.getByTestId('selected-node-label')).toContainText('审批节点') | ||
| 15 | + | ||
| 16 | + await page.getByTestId('delete-selected-node').click() | ||
| 17 | + | ||
| 18 | + await expect(page.getByTestId('node-count-value')).toHaveText('2') | ||
| 19 | + await expect(page.getByTestId('selected-node-label')).toHaveText('未选中节点') | ||
| 20 | +}) | ||
| 21 | + | ||
| 22 | +test('新项目页面应支持缩放操作并打开预览面板', async ({ page }) => { | ||
| 23 | + await mockFlowBootstrap(page) | ||
| 24 | + | ||
| 25 | + await page.goto('/') | ||
| 26 | + | ||
| 27 | + await expect(page.getByTestId('zoom-value')).toHaveText('1.0') | ||
| 28 | + | ||
| 29 | + await page.getByTestId('toolbar-zoomIn').click() | ||
| 30 | + await expect(page.getByTestId('zoom-value')).toHaveText('1.1') | ||
| 31 | + | ||
| 32 | + await page.getByTestId('toolbar-actualView').click() | ||
| 33 | + await expect(page.getByTestId('zoom-value')).toHaveText('1.0') | ||
| 34 | + | ||
| 35 | + await page.getByTestId('open-preview-panel').click() | ||
| 36 | + await expect(page.getByTestId('preview-panel')).toBeVisible() | ||
| 37 | + await expect(page.getByTestId('preview-panel')).toContainText('审批节点') | ||
| 38 | +}) | ||
| 39 | + | ||
| 40 | +test('新项目页面应支持更新节点名称并创建连线', async ({ page }) => { | ||
| 41 | + await mockFlowBootstrap(page) | ||
| 42 | + | ||
| 43 | + await page.goto('/') | ||
| 44 | + | ||
| 45 | + await page.getByTestId('node-chip-start').click() | ||
| 46 | + await expect(page.getByTestId('selected-node-label')).toContainText('开始节点') | ||
| 47 | + | ||
| 48 | + await page.getByTestId('selected-node-name-input').fill('发起流程') | ||
| 49 | + await page.getByTestId('save-node-name').click() | ||
| 50 | + await expect(page.getByTestId('selected-node-label')).toContainText('发起流程') | ||
| 51 | + | ||
| 52 | + await page.getByTestId('add-copy-node').click() | ||
| 53 | + await expect(page.getByTestId('node-count-value')).toHaveText('3') | ||
| 54 | + | ||
| 55 | + await page.getByTestId('node-chip-approve').click() | ||
| 56 | + await page.getByTestId('create-edge-from-selection').click() | ||
| 57 | + | ||
| 58 | + await expect(page.getByTestId('edge-count-value')).toHaveText('2') | ||
| 59 | + await expect(page.getByTestId('selected-count-value')).toHaveText('2') | ||
| 60 | +}) | ||
| 61 | + | ||
| 62 | +test('新项目页面应支持撤销与重做图数据改动', async ({ page }) => { | ||
| 63 | + await mockFlowBootstrap(page) | ||
| 64 | + | ||
| 65 | + await page.goto('/') | ||
| 66 | + | ||
| 67 | + await expect(page.getByTestId('node-count-value')).toHaveText('2') | ||
| 68 | + | ||
| 69 | + await page.getByTestId('add-copy-node').click() | ||
| 70 | + await expect(page.getByTestId('node-count-value')).toHaveText('3') | ||
| 71 | + | ||
| 72 | + await page.getByTestId('command-undo').click() | ||
| 73 | + await expect(page.getByTestId('node-count-value')).toHaveText('2') | ||
| 74 | + | ||
| 75 | + await page.getByTestId('command-redo').click() | ||
| 76 | + await expect(page.getByTestId('node-count-value')).toHaveText('3') | ||
| 77 | +}) |
tests/e2e/smoke.spec.ts
0 → 100644
| 1 | +import { expect, test } from '@playwright/test' | ||
| 2 | +import { EditorPage } from '../helpers/editor-page' | ||
| 3 | +import { mockFlowBootstrap } from '../helpers/mock-flow-api' | ||
| 4 | + | ||
| 5 | +test('新项目基线页可以正常打开并渲染编辑器骨架', async ({ page }) => { | ||
| 6 | + await mockFlowBootstrap(page) | ||
| 7 | + | ||
| 8 | + const editorPage = new EditorPage(page) | ||
| 9 | + await editorPage.gotoHome() | ||
| 10 | + | ||
| 11 | + await expect(editorPage.app).toBeVisible() | ||
| 12 | + await expect(editorPage.editor).toBeVisible() | ||
| 13 | + await expect(editorPage.toolbar).toBeVisible() | ||
| 14 | + await expect(editorPage.canvas).toBeVisible() | ||
| 15 | +}) | ||
| 16 | + |
tests/e2e/toolbar.spec.ts
0 → 100644
| 1 | +import { expect, test } from '@playwright/test' | ||
| 2 | +import { mockFlowBootstrap } from '../helpers/mock-flow-api' | ||
| 3 | + | ||
| 4 | +test('新项目基线页应先对齐核心工具栏展示语义', async ({ page }) => { | ||
| 5 | + await mockFlowBootstrap(page) | ||
| 6 | + | ||
| 7 | + await page.goto('/') | ||
| 8 | + | ||
| 9 | + const toolbar = page.locator('.vue-flow-editor-toolbar') | ||
| 10 | + | ||
| 11 | + await expect(toolbar).toBeVisible() | ||
| 12 | + await expect(toolbar.getByText('适应画布', { exact: true })).toBeVisible() | ||
| 13 | + await expect(toolbar.getByText('实际尺寸', { exact: true })).toBeVisible() | ||
| 14 | + await expect(toolbar.getByText('放大', { exact: true })).toBeVisible() | ||
| 15 | + await expect(toolbar.getByText('缩小', { exact: true })).toBeVisible() | ||
| 16 | + await expect(toolbar.getByText('预览', { exact: true })).toBeVisible() | ||
| 17 | + await expect(toolbar.getByText('居中', { exact: true })).toBeVisible() | ||
| 18 | + await expect(toolbar.getByText('保存', { exact: true })).toBeVisible() | ||
| 19 | + await expect(toolbar.getByText('预览测试', { exact: true })).toBeVisible() | ||
| 20 | + await expect(toolbar.getByText('网格')).toHaveCount(0) | ||
| 21 | + await expect(toolbar.getByText('缩略图')).toHaveCount(0) | ||
| 22 | + await expect(toolbar.getByText('删除')).toHaveCount(0) | ||
| 23 | + await expect(toolbar.getByText('撤销')).toHaveCount(0) | ||
| 24 | + await expect(toolbar.getByText('重做')).toHaveCount(0) | ||
| 25 | +}) | ||
| 26 | + |
tests/fixtures/graphs/base-single-node.json
0 → 100644
tests/fixtures/graphs/branching-flow.json
0 → 100644
| 1 | +{ | ||
| 2 | + "nodes": [ | ||
| 3 | + { | ||
| 4 | + "id": "start-node", | ||
| 5 | + "text": "开始节点", | ||
| 6 | + "shape": "start", | ||
| 7 | + "x": 120, | ||
| 8 | + "y": 200 | ||
| 9 | + }, | ||
| 10 | + { | ||
| 11 | + "id": "review-node", | ||
| 12 | + "text": "审批节点", | ||
| 13 | + "activity": "approval", | ||
| 14 | + "shape": "activity", | ||
| 15 | + "x": 320, | ||
| 16 | + "y": 200 | ||
| 17 | + }, | ||
| 18 | + { | ||
| 19 | + "id": "approve-node", | ||
| 20 | + "text": "通过分支", | ||
| 21 | + "control": "branch-yes", | ||
| 22 | + "shape": "control", | ||
| 23 | + "x": 520, | ||
| 24 | + "y": 140 | ||
| 25 | + }, | ||
| 26 | + { | ||
| 27 | + "id": "reject-node", | ||
| 28 | + "text": "驳回分支", | ||
| 29 | + "control": "branch-no", | ||
| 30 | + "shape": "control", | ||
| 31 | + "x": 520, | ||
| 32 | + "y": 260 | ||
| 33 | + }, | ||
| 34 | + { | ||
| 35 | + "id": "end-node", | ||
| 36 | + "text": "结束节点", | ||
| 37 | + "shape": "end", | ||
| 38 | + "x": 740, | ||
| 39 | + "y": 200 | ||
| 40 | + } | ||
| 41 | + ], | ||
| 42 | + "edges": [ | ||
| 43 | + { | ||
| 44 | + "source": "start-node", | ||
| 45 | + "target": "review-node", | ||
| 46 | + "shape": "flow-polyline-round", | ||
| 47 | + "text": "提交审批" | ||
| 48 | + }, | ||
| 49 | + { | ||
| 50 | + "source": "review-node", | ||
| 51 | + "target": "approve-node", | ||
| 52 | + "shape": "flow-polyline-round", | ||
| 53 | + "text": "同意" | ||
| 54 | + }, | ||
| 55 | + { | ||
| 56 | + "source": "review-node", | ||
| 57 | + "target": "reject-node", | ||
| 58 | + "shape": "flow-polyline-round", | ||
| 59 | + "text": "驳回" | ||
| 60 | + }, | ||
| 61 | + { | ||
| 62 | + "source": "approve-node", | ||
| 63 | + "target": "end-node", | ||
| 64 | + "shape": "flow-polyline-round", | ||
| 65 | + "text": "完成" | ||
| 66 | + }, | ||
| 67 | + { | ||
| 68 | + "source": "reject-node", | ||
| 69 | + "target": "end-node", | ||
| 70 | + "shape": "flow-polyline-round", | ||
| 71 | + "text": "结束" | ||
| 72 | + } | ||
| 73 | + ] | ||
| 74 | +} | ||
| 75 | + |
| 1 | +{ | ||
| 2 | + "nodes": [ | ||
| 3 | + { | ||
| 4 | + "id": "start", | ||
| 5 | + "label": "开始节点", | ||
| 6 | + "type": "start", | ||
| 7 | + "x": 120, | ||
| 8 | + "y": 180 | ||
| 9 | + }, | ||
| 10 | + { | ||
| 11 | + "id": "approve", | ||
| 12 | + "label": "审批节点", | ||
| 13 | + "type": "approval", | ||
| 14 | + "x": 320, | ||
| 15 | + "y": 180 | ||
| 16 | + }, | ||
| 17 | + { | ||
| 18 | + "id": "finish", | ||
| 19 | + "label": "结束节点", | ||
| 20 | + "type": "end", | ||
| 21 | + "x": 520, | ||
| 22 | + "y": 180 | ||
| 23 | + } | ||
| 24 | + ], | ||
| 25 | + "edges": [ | ||
| 26 | + { | ||
| 27 | + "id": "edge-start-approve", | ||
| 28 | + "sourceNodeId": "start", | ||
| 29 | + "targetNodeId": "approve", | ||
| 30 | + "label": "提交审批" | ||
| 31 | + }, | ||
| 32 | + { | ||
| 33 | + "id": "edge-approve-finish", | ||
| 34 | + "sourceNodeId": "approve", | ||
| 35 | + "targetNodeId": "finish", | ||
| 36 | + "label": "审批通过" | ||
| 37 | + } | ||
| 38 | + ] | ||
| 39 | +} | ||
| 40 | + |
tests/fixtures/operations/README.md
0 → 100644
tests/helpers/editor-page.ts
0 → 100644
| 1 | +import type { Locator, Page } from '@playwright/test' | ||
| 2 | + | ||
| 3 | +export class EditorPage { | ||
| 4 | + readonly page: Page | ||
| 5 | + readonly app: Locator | ||
| 6 | + readonly editor: Locator | ||
| 7 | + readonly toolbar: Locator | ||
| 8 | + readonly canvas: Locator | ||
| 9 | + | ||
| 10 | + constructor(page: Page) { | ||
| 11 | + this.page = page | ||
| 12 | + this.app = page.locator('.app') | ||
| 13 | + this.editor = page.locator('.vue-flow-editor') | ||
| 14 | + this.toolbar = page.locator('.vue-flow-editor-toolbar') | ||
| 15 | + this.canvas = page.locator('.vue-flow-editor-canvas-target') | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + async gotoHome() { | ||
| 19 | + await this.page.goto('/') | ||
| 20 | + } | ||
| 21 | +} | ||
| 22 | + |
tests/helpers/mock-flow-api.ts
0 → 100644
| 1 | +import type { Page } from '@playwright/test' | ||
| 2 | + | ||
| 3 | +export async function mockFlowBootstrap(page: Page) { | ||
| 4 | + await page.route('**/admin/**', async (route) => { | ||
| 5 | + const url = route.request().url() | ||
| 6 | + | ||
| 7 | + if (url.includes('a=flow_version') && url.includes('m=mod')) { | ||
| 8 | + await route.fulfill({ | ||
| 9 | + status: 200, | ||
| 10 | + contentType: 'application/json', | ||
| 11 | + body: JSON.stringify({ | ||
| 12 | + code: 1, | ||
| 13 | + data: [ | ||
| 14 | + { | ||
| 15 | + id: 1, | ||
| 16 | + code: 1, | ||
| 17 | + status: '1', | ||
| 18 | + note: '基线版本', | ||
| 19 | + }, | ||
| 20 | + ], | ||
| 21 | + }), | ||
| 22 | + }) | ||
| 23 | + return | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + if (url.includes('a=flow_nodes') && url.includes('m=srv')) { | ||
| 27 | + await route.fulfill({ | ||
| 28 | + status: 200, | ||
| 29 | + contentType: 'application/json', | ||
| 30 | + body: JSON.stringify({ | ||
| 31 | + code: 1, | ||
| 32 | + data: { | ||
| 33 | + nodes: [ | ||
| 34 | + { | ||
| 35 | + id: 'start', | ||
| 36 | + text: '开始节点', | ||
| 37 | + shape: 'start', | ||
| 38 | + x: 120, | ||
| 39 | + y: 180, | ||
| 40 | + }, | ||
| 41 | + { | ||
| 42 | + id: 'approve', | ||
| 43 | + text: '审批节点', | ||
| 44 | + activity: 'approval', | ||
| 45 | + shape: 'activity', | ||
| 46 | + x: 320, | ||
| 47 | + y: 180, | ||
| 48 | + }, | ||
| 49 | + ], | ||
| 50 | + edges: [ | ||
| 51 | + { | ||
| 52 | + source: 'start', | ||
| 53 | + target: 'approve', | ||
| 54 | + shape: 'flow-polyline-round', | ||
| 55 | + text: '提交审批', | ||
| 56 | + }, | ||
| 57 | + ], | ||
| 58 | + }, | ||
| 59 | + }), | ||
| 60 | + }) | ||
| 61 | + return | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + await route.fulfill({ | ||
| 65 | + status: 200, | ||
| 66 | + contentType: 'application/json', | ||
| 67 | + body: JSON.stringify({ | ||
| 68 | + code: 1, | ||
| 69 | + data: {}, | ||
| 70 | + }), | ||
| 71 | + }) | ||
| 72 | + }) | ||
| 73 | +} |
tests/unit/compat-contract.spec.ts
0 → 100644
| 1 | +import { mount } from '@vue/test-utils' | ||
| 2 | +import { describe, expect, it } from 'vitest' | ||
| 3 | +import type { FlowEditorRef } from '@flow-editor' | ||
| 4 | +import FlowEditor from '../../packages/flow-editor/src/components/FlowEditor.vue' | ||
| 5 | + | ||
| 6 | +describe('FlowEditor 兼容层', () => { | ||
| 7 | + it('应通过 onRef 暴露最小实例契约', () => { | ||
| 8 | + let editorInstance: FlowEditorRef | null = null | ||
| 9 | + | ||
| 10 | + mount(FlowEditor, { | ||
| 11 | + props: { | ||
| 12 | + enableEngine: false, | ||
| 13 | + onRef: (instance: FlowEditorRef) => { | ||
| 14 | + editorInstance = instance | ||
| 15 | + }, | ||
| 16 | + }, | ||
| 17 | + }) | ||
| 18 | + | ||
| 19 | + expect(editorInstance).not.toBeNull() | ||
| 20 | + expect(typeof editorInstance?.commander.delete).toBe('function') | ||
| 21 | + expect(typeof editorInstance?.commander.undo).toBe('function') | ||
| 22 | + expect(typeof editorInstance?.commander.redo).toBe('function') | ||
| 23 | + expect(typeof editorInstance?.openModel).toBe('function') | ||
| 24 | + expect(typeof editorInstance?.closeModel).toBe('function') | ||
| 25 | + expect(typeof editorInstance?.addNode).toBe('function') | ||
| 26 | + expect(typeof editorInstance?.updateModel).toBe('function') | ||
| 27 | + expect(typeof editorInstance?.read).toBe('function') | ||
| 28 | + }) | ||
| 29 | +}) |
tests/unit/g6-to-logicflow.spec.ts
0 → 100644
| 1 | +import { describe, expect, it } from 'vitest' | ||
| 2 | +import { | ||
| 3 | + normalizeGraphData, | ||
| 4 | + toLogicFlowGraphData, | ||
| 5 | +} from '../../packages/flow-editor/src/core/adapter/g6-to-logicflow' | ||
| 6 | +import baseSingleNode from '../fixtures/graphs/base-single-node.json' | ||
| 7 | +import branchingFlow from '../fixtures/graphs/branching-flow.json' | ||
| 8 | +import linearApprovalFlow from '../fixtures/graphs/linear-approval-flow.json' | ||
| 9 | + | ||
| 10 | +describe('g6-to-logicflow 适配器', () => { | ||
| 11 | + it('应能把旧项目单节点数据归一化为兼容结构', () => { | ||
| 12 | + const normalizedGraph = normalizeGraphData(baseSingleNode) | ||
| 13 | + | ||
| 14 | + expect(normalizedGraph.nodes).toHaveLength(1) | ||
| 15 | + expect(normalizedGraph.nodes[0]).toMatchObject({ | ||
| 16 | + id: 'start-node', | ||
| 17 | + label: '开始节点', | ||
| 18 | + type: 'start', | ||
| 19 | + x: 160, | ||
| 20 | + y: 180, | ||
| 21 | + }) | ||
| 22 | + expect(normalizedGraph.edges).toHaveLength(0) | ||
| 23 | + }) | ||
| 24 | + | ||
| 25 | + it('应兼容已经整理过的新结构图数据', () => { | ||
| 26 | + const normalizedGraph = normalizeGraphData(linearApprovalFlow) | ||
| 27 | + | ||
| 28 | + expect(normalizedGraph.nodes).toHaveLength(3) | ||
| 29 | + expect(normalizedGraph.edges).toHaveLength(2) | ||
| 30 | + expect(normalizedGraph.nodes[1]).toMatchObject({ | ||
| 31 | + id: 'approve', | ||
| 32 | + label: '审批节点', | ||
| 33 | + type: 'approval', | ||
| 34 | + }) | ||
| 35 | + expect(normalizedGraph.edges[0]).toMatchObject({ | ||
| 36 | + sourceNodeId: 'start', | ||
| 37 | + targetNodeId: 'approve', | ||
| 38 | + label: '提交审批', | ||
| 39 | + }) | ||
| 40 | + }) | ||
| 41 | + | ||
| 42 | + it('应能把旧项目分支流程映射为 LogicFlow 2.x 可消费结构', () => { | ||
| 43 | + const logicFlowGraph = toLogicFlowGraphData(branchingFlow) | ||
| 44 | + | ||
| 45 | + expect(logicFlowGraph.nodes).toHaveLength(5) | ||
| 46 | + expect(logicFlowGraph.edges).toHaveLength(5) | ||
| 47 | + expect(logicFlowGraph.nodes[0]).toMatchObject({ | ||
| 48 | + id: 'start-node', | ||
| 49 | + type: 'circle', | ||
| 50 | + text: '开始节点', | ||
| 51 | + }) | ||
| 52 | + expect(logicFlowGraph.nodes[2]).toMatchObject({ | ||
| 53 | + id: 'approve-node', | ||
| 54 | + type: 'rect', | ||
| 55 | + text: '通过分支', | ||
| 56 | + }) | ||
| 57 | + expect(logicFlowGraph.edges[0]).toMatchObject({ | ||
| 58 | + sourceNodeId: 'start-node', | ||
| 59 | + targetNodeId: 'review-node', | ||
| 60 | + type: 'polyline', | ||
| 61 | + text: '提交审批', | ||
| 62 | + }) | ||
| 63 | + expect(logicFlowGraph.edges[0].id).toContain('edge-start-node-review-node') | ||
| 64 | + }) | ||
| 65 | +}) |
tsconfig.json
0 → 100644
| 1 | +{ | ||
| 2 | + "compilerOptions": { | ||
| 3 | + "target": "ES2022", | ||
| 4 | + "useDefineForClassFields": true, | ||
| 5 | + "module": "ESNext", | ||
| 6 | + "moduleResolution": "Bundler", | ||
| 7 | + "strict": true, | ||
| 8 | + "jsx": "preserve", | ||
| 9 | + "resolveJsonModule": true, | ||
| 10 | + "isolatedModules": true, | ||
| 11 | + "esModuleInterop": true, | ||
| 12 | + "lib": ["ES2022", "DOM", "DOM.Iterable"], | ||
| 13 | + "skipLibCheck": true, | ||
| 14 | + "types": ["node", "vitest/globals"], | ||
| 15 | + "baseUrl": ".", | ||
| 16 | + "paths": { | ||
| 17 | + "@flow-editor": ["packages/flow-editor/src/index.ts"], | ||
| 18 | + "@flow-editor/*": ["packages/flow-editor/src/*"] | ||
| 19 | + } | ||
| 20 | + }, | ||
| 21 | + "include": [ | ||
| 22 | + "env.d.ts", | ||
| 23 | + "vite.config.ts", | ||
| 24 | + "vitest.config.ts", | ||
| 25 | + "playwright.config.ts", | ||
| 26 | + "apps/**/*.ts", | ||
| 27 | + "apps/**/*.vue", | ||
| 28 | + "packages/**/*.ts", | ||
| 29 | + "packages/**/*.vue", | ||
| 30 | + "tests/**/*.ts" | ||
| 31 | + ] | ||
| 32 | +} | ||
| 33 | + |
vite.config.ts
0 → 100644
| 1 | +import { resolve } from 'node:path' | ||
| 2 | +import { defineConfig } from 'vite' | ||
| 3 | +import vue from '@vitejs/plugin-vue' | ||
| 4 | + | ||
| 5 | +export default defineConfig({ | ||
| 6 | + root: resolve(__dirname, 'apps/demo'), | ||
| 7 | + plugins: [vue()], | ||
| 8 | + resolve: { | ||
| 9 | + alias: { | ||
| 10 | + '@flow-editor': resolve(__dirname, 'packages/flow-editor/src/index.ts'), | ||
| 11 | + '@flow-editor/components': resolve(__dirname, 'packages/flow-editor/src/components'), | ||
| 12 | + '@flow-editor-src': resolve(__dirname, 'packages/flow-editor/src'), | ||
| 13 | + }, | ||
| 14 | + }, | ||
| 15 | + server: { | ||
| 16 | + host: '127.0.0.1', | ||
| 17 | + port: 4173, | ||
| 18 | + }, | ||
| 19 | +}) | ||
| 20 | + |
vitest.config.ts
0 → 100644
| 1 | +import { resolve } from 'node:path' | ||
| 2 | +import { defineConfig } from 'vitest/config' | ||
| 3 | +import vue from '@vitejs/plugin-vue' | ||
| 4 | + | ||
| 5 | +export default defineConfig({ | ||
| 6 | + plugins: [vue()], | ||
| 7 | + resolve: { | ||
| 8 | + alias: { | ||
| 9 | + '@flow-editor': resolve(__dirname, 'packages/flow-editor/src/index.ts'), | ||
| 10 | + '@flow-editor/components': resolve(__dirname, 'packages/flow-editor/src/components'), | ||
| 11 | + '@flow-editor-src': resolve(__dirname, 'packages/flow-editor/src'), | ||
| 12 | + }, | ||
| 13 | + }, | ||
| 14 | + test: { | ||
| 15 | + environment: 'jsdom', | ||
| 16 | + include: ['tests/unit/**/*.spec.ts'], | ||
| 17 | + }, | ||
| 18 | +}) | ||
| 19 | + |
-
Please register or login to post a comment