(obj: T): T;
+```
+
+#### 参数
+
+| 参数名 | 类型 | 描述 |
+| ------ | ---- | -------------- |
+| `obj` | T | 要深拷贝的对象 |
+
+#### 返回值
+
+| 类型 | 描述 |
+| ---- | -------------- |
+| T | 原对象的深拷贝 |
diff --git a/packages/studio-flow-editor/docs/components.md b/packages/studio-flow-editor/docs/components.md
new file mode 100644
index 000000000..964bc9390
--- /dev/null
+++ b/packages/studio-flow-editor/docs/components.md
@@ -0,0 +1,218 @@
+---
+order: 4
+title: 组件文档
+---
+# 组件文档
+
+`@graphscope/studio-flow-editor` 提供了几个预构建的组件,可以直接集成到您的应用中,简化图编辑器的操作与控制。
+
+## AddNode
+
+### 描述
+
+一个添加新节点的按钮组件。
+
+### 属性
+
+| 属性名 | 类型 | 必填 | 默认值 | 描述 |
+|-------|------|------|-------|------|
+| `style` | React.CSSProperties | 否 | - | 内联样式对象 |
+
+### 示例
+
+```bash
+import { AddNode } from '@graphscope/studio-flow-editor';
+
+// 基本用法
+
+
+// 自定义样式和文本
+
+```
+
+## ClearCanvas
+
+### 描述
+
+一个清除画布或删除选中元素的按钮组件。
+
+### 属性
+
+| 属性名 | 类型 | 必填 | 默认值 | 描述 |
+|-------|------|------|-------|------|
+| `style` | React.CSSProperties | 否 | - | 内联样式对象 |
+
+### 行为
+
+- 如果有选中的节点,会删除这些节点及相关边
+- 如果有选中的边,会删除这些边
+- 如果没有选中任何元素,会清空整个画布
+
+### 示例
+
+```bash
+import { ClearCanvas } from '@graphscope/studio-flow-editor';
+
+// 基本用法
+
+
+// 自定义样式
+
+```
+
+## ExportSvg
+
+### 描述
+
+一个导出图为SVG文件的按钮组件。
+
+### 属性
+
+| 属性名 | 类型 | 必填 | 默认值 | 描述 |
+|-------|------|------|-------|------|
+| `style` | React.CSSProperties | 否 | - | 内联样式对象 |
+| `fileName` | string | 'graph.svg' | - | 导出文件名 |
+| `parentId` | string | 否 | - | 当页面内存在多个图形实例是,可指定导出图上层dom的id |
+### 示例
+
+```bash
+import { ExportSvg } from '@graphscope/studio-flow-editor';
+
+// 基本用法
+
+
+// 自定义样式和文件名
+
+```
+
+## 组合使用
+
+这些组件通常组合在一起使用,创建一个完整的工具栏:
+
+```jsx
+import React from 'react';
+import {
+ GraphProvider,
+ GraphCanvas,
+ AddNode,
+ ClearCanvas,
+ ExportSvg
+} from '@graphscope/studio-flow-editor';
+
+const ToolbarStyle = {
+ position: 'absolute',
+ top: '10px',
+ right: '10px',
+ zIndex: 10,
+ display: 'flex',
+ gap: '8px',
+ background: 'white',
+ padding: '8px',
+ borderRadius: '4px',
+ boxShadow: '0 2px 6px rgba(0,0,0,0.1)'
+};
+
+const ButtonStyle = {
+ padding: '4px 12px',
+ borderRadius: '2px',
+ cursor: 'pointer',
+ border: '1px solid #d9d9d9',
+ backgroundColor: '#fff',
+};
+
+const App = () => {
+ return (
+
+ );
+};
+
+export default App;
+```
+
+## 自定义组件集成
+
+您也可以将这些内置组件与自定义组件结合使用,创建更丰富的控制面板:
+
+```jsx
+import React from 'react';
+import {
+ GraphProvider,
+ GraphCanvas,
+ useGraphStore,
+ AddNode,
+ ClearCanvas
+} from '@graphscope/studio-flow-editor';
+
+// 自定义信息面板
+const InfoPanel = () => {
+ const { store } = useGraphStore();
+
+ return (
+
+
节点数量: {store.nodes.length}
+
边数量: {store.edges.length}
+ {store.currentId && (
+
当前选中: {store.currentType === 'nodes' ? '节点' : '边'} {store.currentId}
+ )}
+
+ );
+};
+
+const App = () => {
+ return (
+
+ );
+};
+
+export default App;
+```
\ No newline at end of file
diff --git a/packages/studio-flow-editor/docs/demos.md b/packages/studio-flow-editor/docs/demos.md
new file mode 100644
index 000000000..3f0be00dc
--- /dev/null
+++ b/packages/studio-flow-editor/docs/demos.md
@@ -0,0 +1,1144 @@
+---
+order: 8
+title: 进阶示例
+---
+
+# 进阶示例
+
+本文档提供了一些 `@graphscope/studio-flow-editor` 的进阶使用示例,帮助您深入了解如何利用这个库构建复杂的图编辑应用。
+
+## 示例一:节点和边标签编辑器
+
+这个示例展示了如何实现自定义的节点和边标签编辑功能。
+
+```jsx
+import React, { useState } from 'react';
+import { GraphProvider, GraphCanvas, useGraphStore, useAddNode, useClearCanvas } from '@graphscope/studio-flow-editor';
+
+// 标签编辑面板组件
+const LabelEditor = () => {
+ const { store, updateStore } = useGraphStore();
+ const { currentId, currentType, nodes, edges } = store;
+ const [labelValue, setLabelValue] = useState('');
+
+ // 当选中元素变化时,更新输入框的值
+ React.useEffect(() => {
+ if (currentId && currentType) {
+ const currentItem =
+ currentType === 'nodes' ? nodes.find(item => item.id === currentId) : edges.find(item => item.id === currentId);
+
+ if (currentItem && currentItem.data) {
+ setLabelValue(currentItem.data.label || '');
+ }
+ }
+ }, [currentId, currentType, nodes, edges]);
+
+ // 没有选中任何元素时不显示编辑器
+ if (!currentId) {
+ return (
+
+ 请选择一个节点或边来编辑标签
+
+ );
+ }
+
+ // 处理标签变更
+ const handleLabelChange = e => {
+ setLabelValue(e.target.value);
+ };
+
+ // 应用标签变更
+ const applyLabelChange = () => {
+ updateStore(draft => {
+ if (currentType === 'nodes') {
+ const node = draft.nodes.find(n => n.id === currentId);
+ if (node) {
+ node.data.label = labelValue;
+ }
+ } else {
+ const edge = draft.edges.find(e => e.id === currentId);
+ if (edge) {
+ edge.data.label = labelValue;
+ }
+ }
+ });
+ };
+
+ return (
+
+
编辑 {currentType === 'nodes' ? '节点' : '边'} 标签
+
+
+
+ );
+};
+
+// 工具栏组件
+const Toolbar = () => {
+ const { handleAddVertex } = useAddNode();
+ const { handleClear } = useClearCanvas();
+
+ return (
+
+
+
+
+ );
+};
+
+// 应用组件
+const App = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default App;
+```
+
+## 示例二:生成Cypher查询语句
+
+这个示例展示了如何根据图编辑器中的节点和边生成Cypher查询语句。
+
+```jsx
+import React, { useState, useEffect } from 'react';
+import { MiniMap, Background } from 'reactflow';
+import { GraphProvider, GraphCanvas, useGraphStore, useAddNode, useClearCanvas } from '@graphscope/studio-flow-editor';
+
+// Cypher生成器组件
+const CypherGenerator = () => {
+ const { store } = useGraphStore();
+ const { nodes, edges } = store;
+ const [cypherQuery, setCypherQuery] = useState('');
+
+ // 当节点或边变化时,生成Cypher查询
+ useEffect(() => {
+ if (nodes.length === 0) {
+ setCypherQuery('// 请添加节点');
+ return;
+ }
+
+ // 生成MATCH语句
+ const matchClauses = nodes.map((node, index) => {
+ // 使用节点标签作为节点类型,默认为"Node"
+ const nodeLabel = node.data.label || 'Node';
+ // 使用"n0", "n1"等作为变量名
+ const variableName = `n${index}`;
+
+ // 生成属性对象
+ const properties = node.data.properties
+ ? Object.entries(node.data.properties)
+ .map(([key, value]) => `${key}: ${typeof value === 'string' ? `"${value}"` : value}`)
+ .join(', ')
+ : '';
+
+ const propertiesClause = properties ? `{${properties}}` : '';
+
+ // 返回完整的匹配模式
+ return `MATCH (${variableName}:${nodeLabel.replace(/\s+/g, '_')}${propertiesClause})`;
+ });
+
+ // 生成关系语句(WHERE子句)
+ const whereClauses = edges
+ .map((edge, index) => {
+ // 找到源节点和目标节点的索引
+ const sourceIndex = nodes.findIndex(node => node.id === edge.source);
+ const targetIndex = nodes.findIndex(node => node.id === edge.target);
+
+ if (sourceIndex === -1 || targetIndex === -1) return null;
+
+ // 使用边标签作为关系类型,默认为"RELATES_TO"
+ const relationshipType = (edge.data.label || 'RELATES_TO').replace(/\s+/g, '_');
+
+ // 返回关系条件
+ return `MATCH (n${sourceIndex})-[:${relationshipType}]->(n${targetIndex})`;
+ })
+ .filter(Boolean); // 过滤掉无效的关系
+
+ // 生成RETURN语句
+ const returnClause = `RETURN ${nodes.map((_, index) => `n${index}`).join(', ')}`;
+
+ // 组合完整查询
+ const query = [...matchClauses, ...whereClauses, returnClause].join('\n');
+
+ setCypherQuery(query);
+ }, [nodes, edges]);
+
+ // 复制查询到剪贴板
+ const copyToClipboard = () => {
+ navigator.clipboard
+ .writeText(cypherQuery)
+ .then(() => {
+ alert('已复制到剪贴板');
+ })
+ .catch(err => {
+ console.error('复制失败:', err);
+ });
+ };
+
+ return (
+
+
+ Cypher查询
+
+
+
+ {cypherQuery}
+
+
+ );
+};
+
+// 工具栏组件
+const Toolbar = () => {
+ const { handleAddVertex } = useAddNode();
+ const { handleClear } = useClearCanvas();
+ const { store, updateStore } = useGraphStore();
+
+ // 添加示例图
+ const addExampleGraph = () => {
+ // 先清空现有图
+ updateStore(draft => {
+ draft.nodes = [];
+ draft.edges = [];
+ });
+
+ // 添加示例节点
+ const personNode = {
+ id: 'node-1',
+ position: { x: 100, y: 100 },
+ type: 'graph-node',
+ data: {
+ label: 'Person',
+ properties: {
+ name: 'Alice',
+ age: 30,
+ },
+ },
+ };
+
+ const companyNode = {
+ id: 'node-2',
+ position: { x: 300, y: 100 },
+ type: 'graph-node',
+ data: {
+ label: 'Company',
+ properties: {
+ name: 'ACME Corp',
+ founded: 2010,
+ },
+ },
+ };
+
+ const productNode = {
+ id: 'node-3',
+ position: { x: 300, y: 250 },
+ type: 'graph-node',
+ data: {
+ label: 'Product',
+ properties: {
+ name: 'Widget X',
+ price: 99.99,
+ },
+ },
+ };
+
+ // 添加示例边
+ const worksAtEdge = {
+ id: 'edge-1',
+ source: 'node-1',
+ target: 'node-2',
+ type: 'graph-edge',
+ data: { label: 'WORKS_AT' },
+ };
+
+ const createdEdge = {
+ id: 'edge-2',
+ source: 'node-2',
+ target: 'node-3',
+ type: 'graph-edge',
+ data: { label: 'CREATED' },
+ };
+
+ const purchasedEdge = {
+ id: 'edge-3',
+ source: 'node-1',
+ target: 'node-3',
+ type: 'graph-edge',
+ data: { label: 'PURCHASED' },
+ };
+
+ // 更新图
+ updateStore(draft => {
+ draft.nodes = [personNode, companyNode, productNode];
+ draft.edges = [worksAtEdge, createdEdge, purchasedEdge];
+ });
+ };
+
+ return (
+
+
+
+
+
+ );
+};
+
+// 节点数据面板
+const NodeDataPanel = () => {
+ const { store, updateStore } = useGraphStore();
+ const { currentId, currentType, nodes } = store;
+ const [properties, setProperties] = useState({});
+ const [newKey, setNewKey] = useState('');
+ const [newValue, setNewValue] = useState('');
+
+ // 当选中节点变化时,更新属性面板
+ useEffect(() => {
+ if (currentId && currentType === 'nodes') {
+ const currentNode = nodes.find(node => node.id === currentId);
+ if (currentNode && currentNode.data) {
+ setProperties(currentNode.data.properties || {});
+ } else {
+ setProperties({});
+ }
+ } else {
+ setProperties({});
+ }
+ }, [currentId, currentType, nodes]);
+
+ // 没有选中节点时不显示面板
+ if (!currentId || currentType !== 'nodes') {
+ return null;
+ }
+
+ // 添加新属性
+ const addProperty = () => {
+ if (!newKey.trim()) return;
+
+ // 尝试转换为数字
+ let parsedValue = newValue;
+ if (!isNaN(Number(newValue))) {
+ parsedValue = Number(newValue);
+ }
+
+ const updatedProperties = {
+ ...properties,
+ [newKey]: parsedValue,
+ };
+
+ setProperties(updatedProperties);
+ updateStore(draft => {
+ const node = draft.nodes.find(n => n.id === currentId);
+ if (node) {
+ node.data.properties = updatedProperties;
+ }
+ });
+
+ // 清空输入框
+ setNewKey('');
+ setNewValue('');
+ };
+
+ // 删除属性
+ const removeProperty = key => {
+ const { [key]: _, ...rest } = properties;
+ setProperties(rest);
+
+ updateStore(draft => {
+ const node = draft.nodes.find(n => n.id === currentId);
+ if (node) {
+ node.data.properties = rest;
+ }
+ });
+ };
+
+ return (
+
+
节点属性编辑
+
+ {/* 现有属性 */}
+
+ {Object.entries(properties).length === 0 ? (
+
暂无属性
+ ) : (
+ Object.entries(properties).map(([key, value]) => (
+
+
+ {key}: {String(value)}
+
+
+
+ ))
+ )}
+
+
+ {/* 添加新属性 */}
+
+
+ );
+};
+
+// 应用组件
+const App = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default App;
+```
+
+## 示例三:结合多个功能的完整应用
+
+以下是一个结合了多项功能的完整应用示例,包括:
+
+- 节点和边标签编辑
+- 节点属性编辑
+- Cypher查询生成
+- 图数据导入/导出
+- 自定义样式
+
+```jsx
+import React, { useState, useEffect } from 'react';
+import {
+ GraphProvider,
+ GraphCanvas,
+ useGraphStore,
+ useAddNode,
+ useClearCanvas,
+ useExportSvg,
+} from '@graphscope/studio-flow-editor';
+
+// 主应用组件
+const GraphApplication = () => {
+ return (
+
+ );
+};
+
+// 主工具栏
+const MainToolbar = () => {
+ const {handleAddVertex} = useAddNode();
+ const { handleClear } = useClearCanvas();
+ const {exportSvg} = useExportSvg();
+ const { store, updateStore } = useGraphStore();
+
+ // 导出图数据为JSON
+ const exportJSON = () => {
+ const { nodes, edges } = store;
+ const dataStr = JSON.stringify({ nodes, edges }, null, 2);
+ const dataUri = `data:application/json;charset=utf-8,${encodeURIComponent(dataStr)}`;
+
+ const exportLink = document.createElement('a');
+ exportLink.setAttribute('href', dataUri);
+ exportLink.setAttribute('download', 'graph-data.json');
+ document.body.appendChild(exportLink);
+ exportLink.click();
+ document.body.removeChild(exportLink);
+ };
+
+ // 导入JSON数据
+ const importJSON = () => {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.json';
+
+ input.onchange = e => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ const reader = new FileReader();
+ reader.onload = event => {
+ try {
+ const { nodes, edges } = JSON.parse(event.target.result);
+ updateStore(draft => {
+ draft.nodes = nodes;
+ draft.edges = edges;
+ });
+ } catch (error) {
+ console.error('导入失败:', error);
+ alert('导入失败: ' + error.message);
+ }
+ };
+ reader.readAsText(file);
+ };
+
+ input.click();
+ };
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+// 属性面板
+const PropertiesPanel = () => {
+ const { store, updateStore } = useGraphStore();
+ const { currentId, currentType, nodes, edges } = store;
+ const [labelValue, setLabelValue] = useState('');
+ const [properties, setProperties] = useState({});
+ const [newKey, setNewKey] = useState('');
+ const [newValue, setNewValue] = useState('');
+
+ // 当选中元素变化时,更新面板
+ useEffect(() => {
+ if (currentId && currentType) {
+ const currentItem =
+ currentType === 'nodes' ? nodes.find(item => item.id === currentId) : edges.find(item => item.id === currentId);
+
+ if (currentItem && currentItem.data) {
+ setLabelValue(currentItem.data.label || '');
+ setProperties(currentItem.data.properties || {});
+ }
+ } else {
+ setLabelValue('');
+ setProperties({});
+ }
+ }, [currentId, currentType, nodes, edges]);
+
+ if (!currentId) return null;
+
+ // 更新标签
+ const updateLabel = () => {
+ updateStore(draft => {
+ if (currentType === 'nodes') {
+ const node = draft.nodes.find(n => n.id === currentId);
+ if (node) {
+ node.data.label = labelValue;
+ }
+ } else {
+ const edge = draft.edges.find(e => e.id === currentId);
+ if (edge) {
+ edge.data.label = labelValue;
+ }
+ }
+ });
+ };
+
+ // 添加属性
+ const addProperty = () => {
+ if (!newKey.trim()) return;
+
+ // 尝试转换为数字
+ let parsedValue = newValue;
+ if (!isNaN(Number(newValue))) {
+ parsedValue = Number(newValue);
+ }
+
+ const updatedProperties = {
+ ...properties,
+ [newKey]: parsedValue,
+ };
+
+ setProperties(updatedProperties);
+ updateStore(draft => {
+ if (currentType === 'nodes') {
+ const node = draft.nodes.find(n => n.id === currentId);
+ if (node) {
+ node.data.properties = updatedProperties;
+ }
+ } else {
+ const edge = draft.edges.find(e => e.id === currentId);
+ if (edge) {
+ edge.data.properties = updatedProperties;
+ }
+ }
+ });
+
+ setNewKey('');
+ setNewValue('');
+ };
+
+ // 删除属性
+ const removeProperty = key => {
+ const { [key]: _, ...rest } = properties;
+ setProperties(rest);
+
+ updateStore(draft => {
+ if (currentType === 'nodes') {
+ const node = draft.nodes.find(n => n.id === currentId);
+ if (node) {
+ node.data.properties = rest;
+ }
+ } else {
+ const edge = draft.edges.find(e => e.id === currentId);
+ if (edge) {
+ edge.data.properties = rest;
+ }
+ }
+ });
+ };
+
+ return (
+
+
+ {currentType === 'nodes' ? '节点属性' : '边属性'}
+
+
+ {/* 标签编辑 */}
+
+
标签:
+
+ setLabelValue(e.target.value)}
+ style={{
+ padding: '6px 8px',
+ border: '1px solid #d9d9d9',
+ borderRadius: '2px',
+ flex: 1,
+ }}
+ />
+
+
+
+
+ {/* 属性列表 */}
+
+
属性:
+ {Object.entries(properties).length === 0 ? (
+
暂无属性
+ ) : (
+
+ {Object.entries(properties).map(([key, value]) => (
+
+
+ {key}: {String(value)}
+
+
+
+ ))}
+
+ )}
+
+ {/* 添加新属性 */}
+
+
+
+ );
+};
+
+// Cypher查询面板
+const CypherPanel = () => {
+ const { store } = useGraphStore();
+ const { nodes, edges } = store;
+ const [cypherQuery, setCypherQuery] = useState('');
+
+ // 当节点或边变化时,生成Cypher查询
+ useEffect(() => {
+ if (nodes.length === 0) {
+ setCypherQuery('// 请添加节点');
+ return;
+ }
+
+ // 生成MATCH语句
+ const matchClauses = nodes.map((node, index) => {
+ const nodeLabel = node.data.label || 'Node';
+ const variableName = `n${index}`;
+
+ // 生成属性对象
+ let propertiesClause = '';
+ if (node.data.properties && Object.keys(node.data.properties).length > 0) {
+ const properties = Object.entries(node.data.properties)
+ .map(([key, value]) => `${key}: ${typeof value === 'string' ? `"${value}"` : value}`)
+ .join(', ');
+ propertiesClause = `{${properties}}`;
+ }
+
+ return `MATCH (${variableName}:${nodeLabel.replace(/\s+/g, '_')}${propertiesClause})`;
+ });
+
+ // 生成关系语句
+ const whereClauses = edges
+ .map((edge, index) => {
+ const sourceIndex = nodes.findIndex(node => node.id === edge.source);
+ const targetIndex = nodes.findIndex(node => node.id === edge.target);
+
+ if (sourceIndex === -1 || targetIndex === -1) return null;
+
+ const relationshipType = (edge.data.label || 'RELATES_TO').replace(/\s+/g, '_');
+
+ // 生成关系属性
+ let propertiesClause = '';
+ if (edge.data.properties && Object.keys(edge.data.properties).length > 0) {
+ const properties = Object.entries(edge.data.properties)
+ .map(([key, value]) => `${key}: ${typeof value === 'string' ? `"${value}"` : value}`)
+ .join(', ');
+ propertiesClause = `{${properties}}`;
+ }
+
+ return `MATCH (n${sourceIndex})-[:${relationshipType}${propertiesClause}]->(n${targetIndex})`;
+ })
+ .filter(Boolean);
+
+ // 生成RETURN语句
+ const returnClause = `RETURN ${nodes.map((_, index) => `n${index}`).join(', ')}`;
+
+ // 组合完整查询
+ const query = [...matchClauses, ...whereClauses, returnClause].join('\n');
+
+ setCypherQuery(query);
+ }, [nodes, edges]);
+
+ // 复制查询到剪贴板
+ const copyToClipboard = () => {
+ navigator.clipboard
+ .writeText(cypherQuery)
+ .then(() => {
+ alert('已复制到剪贴板');
+ })
+ .catch(err => {
+ console.error('复制失败:', err);
+ });
+ };
+
+ return (
+
+
+ Cypher查询
+
+
+
+ {cypherQuery}
+
+
+ );
+};
+
+// 状态栏
+const StatusBar = () => {
+ const { store } = useGraphStore();
+ const { nodes, edges, currentId, currentType } = store;
+
+ return (
+
+
+ 节点: {nodes.length} | 边: {edges.length}
+
+
{currentId && `当前选中: ${currentType === 'nodes' ? '节点' : '边'} ${currentId}`}
+
+ );
+};
+
+// 共用样式
+const buttonStyle = {
+ padding: '6px 12px',
+ background: '#1890ff',
+ color: 'white',
+ border: 'none',
+ borderRadius: '2px',
+ cursor: 'pointer',
+};
+
+export default GraphApplication;
+```
diff --git a/packages/studio-flow-editor/docs/hooks.md b/packages/studio-flow-editor/docs/hooks.md
new file mode 100644
index 000000000..f09c7a956
--- /dev/null
+++ b/packages/studio-flow-editor/docs/hooks.md
@@ -0,0 +1,153 @@
+---
+order: 5
+title: Hooks
+---
+
+# 钩子函数
+
+`@graphscope/studio-flow-editor` 包提供了多个自定义钩子(Hooks),帮助您与图编辑器交互并操作其状态。
+
+## 核心钩子
+
+### useGraphStore
+
+用于访问和更新图状态的主要钩子。
+
+```bash
+import { useGraphStore } from '@graphscope/studio-flow-editor';
+
+const MyComponent = () => {
+ const { store, updateStore } = useGraphStore();
+ const { nodes, edges, currentId, currentType } = store;
+
+ const handleAddNode = () => {
+ updateStore(draft => {
+ draft.nodes.push({
+ id: 'node-' + Date.now(),
+ position: { x: 100, y: 100 },
+ data: { label: '新节点' },
+ type: 'graph-node'
+ });
+ });
+ };
+
+ return (
+
+ );
+};
+```
+
+#### 存储属性
+
+| 属性 | 类型 | 说明 |
+| -------------------- | ------------------------------------------------- | ---------------------- |
+| `displayMode` | `'graph' \| 'table'` | 当前显示模式 |
+| `nodes` | `ISchemaNode[]` | 图节点数组 |
+| `edges` | `ISchemaEdge[]` | 图边数组 |
+| `nodePositionChange` | `NodePositionChange[]` | 节点位置变更记录 |
+| `hasLayouted` | `boolean` | 是否已应用自动布局 |
+| `elementOptions` | `{ isEditable: boolean, isConnectable: boolean }` | 元素交互选项 |
+| `theme` | `{ primaryColor: string }` | 主题设置 |
+| `currentId` | `string` | 当前选中的节点或边的ID |
+| `currentType` | `'nodes' \| 'edges'` | 当前选中元素的类型 |
+
+### useClearCanvas
+
+提供当存在选中节点或连线时,删除选中节点及相关连线与选中连线的功能,当不存在选中节点时,提供清空画布(删除所有节点和边)的功能。
+
+```bash
+import { useClearCanvas } from '@graphscope/studio-flow-editor';
+
+const ClearButton = () => {
+ const {handleClear} = useClearCanvas();
+
+ return (
+
+ );
+};
+```
+
+### useAddNode
+
+提供添加新节点到画布的功能。
+
+```bash
+import { useAddNode } from '@graphscope/studio-flow-editor';
+
+const AddNodeButton = () => {
+ const { handleAddVertex } = useAddNode();
+
+ return (
+
+ );
+};
+```
+
+### useExportSvg
+
+提供将图导出为SVG或图片的功能。
+
+```bash
+import { useExportSvg } from '@graphscope/studio-flow-editor';
+
+const ExportButton = () => {
+ const {exportSvg} = useExportSvg();
+
+ return (
+
+ );
+};
+```
+
+## 高级用法
+
+### 组合多个钩子
+
+```bash
+import { useGraphStore, useAddNode, useClearCanvas } from '@graphscope/studio-flow-editor';
+
+const GraphControls = () => {
+ const { store } = useGraphStore();
+ const {handleAddVertex} = useAddNode();
+ const {handleClear} = useClearCanvas();
+
+ return (
+
+
+
+
节点数量: {store.nodes.length}
+
边数量: {store.edges.length}
+
+ );
+};
+```
+
+### 自定义钩子管理节点选择
+
+```bash
+import { useGraphStore } from '@graphscope/studio-flow-editor';
+import { useCallback } from 'react';
+
+export const useNodeSelection = () => {
+ const { store, updateStore } = useGraphStore();
+
+ const selectNode = useCallback((nodeId) => {
+ updateStore(draft => {
+ draft.currentId = nodeId;
+ draft.currentType = 'nodes';
+ });
+ }, [updateStore]);
+
+ const isSelected = useCallback((nodeId) => {
+ return store.currentId === nodeId && store.currentType === 'nodes';
+ }, [store.currentId, store.currentType]);
+
+ return { selectNode, isSelected, selectedId: store.currentId };
+};
+```
diff --git a/packages/studio-flow-editor/docs/index.md b/packages/studio-flow-editor/docs/index.md
new file mode 100644
index 000000000..737373f9b
--- /dev/null
+++ b/packages/studio-flow-editor/docs/index.md
@@ -0,0 +1,103 @@
+---
+order: 0
+title: 写在前面
+---
+
+# Studio Flow Editor
+
+## 写在前面
+
+### 概述
+
+`@graphscope/studio-flow-editor` 是一个基于 React 的图编辑器组件库,提供了一套完整的图数据可视化和交互编辑解决方案。该库基于 ReactFlow 构建,支持节点与边的直观编辑、自动布局以及高度可定制的图形表示。
+
+### 主要特性
+
+- **交互式编辑**:支持拖拽式创建、移动节点和连线
+- **状态管理**:集成 Zustand 进行高效状态管理
+- **多实例支持**:允许在同一页面创建多个独立图编辑器
+- **自动布局**:内置力导向图布局算法
+- **类型安全**:完整的 TypeScript 类型定义
+- **自定义外观**:可定制节点和边的样式与行为
+- **导出功能**:支持导出为 SVG 图片
+
+### 主要组件一览
+
+#### GraphCanvas
+
+主编辑器组件,提供可视化图编辑功能。
+
+```typescript
+interface ImportorProps {
+ id?: string; // 编辑器实例ID
+ children?: React.ReactNode; // 子组件
+ nodesDraggable?: boolean; // 节点是否可拖拽
+ isPreview?: boolean; // 是否为预览模式
+ onNodesChange?: (nodes: ISchemaNode[]) => void; // 节点变化回调
+ onEdgesChange?: (edges: ISchemaEdge[]) => void; // 边变化回调
+ onSelectionChange?: (nodes: ISchemaNode[], edges: ISchemaEdge[]) => void; // 选择变化回调
+ noDefaultLabel?: boolean; // 是否禁用默认标签
+ defaultNodes?: ISchemaNode[]; // 初始节点
+ defaultEdges?: ISchemaEdge[]; // 初始边
+}
+```
+
+#### GraphProvider
+
+状态管理提供者,负责管理图编辑器的所有状态。
+
+```typescript
+interface GraphProviderProps {
+ id?: string; // 图实例ID,默认自动生成
+ children: React.ReactNode; // 子组件
+}
+```
+
+#### 状态结构
+
+```typescript
+interface GraphState {
+ displayMode: 'graph' | 'table'; // 显示模式
+ nodes: ISchemaNode[]; // 节点列表
+ edges: ISchemaEdge[]; // 边列表
+ nodePositionChange: NodePositionChange[]; // 节点位置变更
+ hasLayouted: boolean; // 是否已布局
+ elementOptions: { // 元素选项
+ isEditable: boolean; // 是否可编辑
+ isConnectable: boolean; // 是否可连接
+ };
+ theme: { // 主题
+ primaryColor: string; // 主色调
+ };
+ currentId: string; // 当前选中元素ID
+ currentType: 'nodes' | 'edges'; // 当前选中元素类型
+ selectedNodeIds: string[]; // 选中的节点ID数组
+ selectedEdgeIds: string[]; // 选中的边ID数组
+}
+```
+
+### 钩子(Hooks)一览
+
+| 钩子名称 | 返回值 | 描述 |
+|---------|-------|------|
+| `useGraphStore` | `{ store, updateStore }` | 访问和更新图状态 |
+| `useClearCanvas` | `{ handleClear }` | 清除画布或删除选中元素 |
+| `useAddNode` | `{ handleAddVertex }` | 添加新节点到画布 |
+| `useExportSvg` | `{ exportSvg }` | 导出图为SVG或图片 |
+
+### 工具组件一览
+
+| 组件名称 | 描述 |
+|---------|------|
+| `AddNode` | 添加节点按钮组件 |
+| `ClearCanvas` | 清空/删除选中元素按钮组件 |
+| `ExportSvg` | 导出SVG按钮组件 |
+
+## 文档导航
+
+- [快速开始](/floweditors/quick-start) - 安装指南和基本使用方法
+- [GraphProvider](/floweditors/provider) - 状态管理与Provider
+- [GraphCanvas](/floweditors/graph-canvas) - GraphCanvas组件
+- [API文档](/floweditors/api) - 详细的API参考
+- [组件](/floweditors/components) - 工具组件使用指南
+- [示例](/floweditors/demos) - 实用示例和高级用法
diff --git a/packages/studio-flow-editor/docs/provider.md b/packages/studio-flow-editor/docs/provider.md
new file mode 100644
index 000000000..62b531cf0
--- /dev/null
+++ b/packages/studio-flow-editor/docs/provider.md
@@ -0,0 +1,180 @@
+---
+order: 2
+title: GraphProvider
+---
+
+# 状态管理与Provider
+
+`@graphscope/studio-flow-editor` 包使用Provider组件来管理应用中的状态和上下文。这些Provider组件是图形编辑器正常运行的基础。
+
+## GraphProvider
+
+`GraphProvider` 是图形编辑器的主要状态提供者。它创建一个上下文,用于保存和管理节点、边和其他图形相关数据的状态。
+
+### 导入方式
+
+```bash
+import { GraphProvider } from '@graphscope/studio-flow-editor';
+```
+
+### 基本用法
+
+```bash
+import React from 'react';
+import { GraphProvider, GraphCanvas } from '@graphscope/studio-flow-editor';
+
+const App = () => {
+ return (
+
+
+ {/* 其他需要访问图形状态的组件 */}
+
+ );
+};
+```
+
+### 属性配置
+
+| 属性名 | 类型 | 默认值 | 说明 |
+| ---------- | --------- | -------------- | ------------------------ |
+| `id` | string | 自动生成的UUID | 该图实例的唯一标识符 |
+| `children` | ReactNode | - | 需要访问图形状态的子组件 |
+
+### 状态管理
+
+`GraphProvider` 内部使用 [Zustand](https://github.com/pmndrs/zustand)(通过 `@graphscope/use-zustand`)来管理状态。初始状态结构如下:
+
+```typescript
+interface GraphState {
+ displayMode: 'graph' | 'table';
+ nodes: ISchemaNode[];
+ edges: ISchemaEdge[];
+ nodePositionChange: NodePositionChange[];
+ hasLayouted: boolean;
+ elementOptions: {
+ isEditable: boolean;
+ isConnectable: boolean;
+ };
+ theme: {
+ primaryColor: string;
+ };
+ currentId: string;
+ currentType: 'nodes' | 'edges';
+ selectedNodeIds: string[];
+ selectedEdgeIds: string[];
+}
+```
+
+## 多实例支持
+
+可以在同一页面创建多个独立的图编辑器实例:
+
+```jsx
+import React from 'react';
+import { Toolbar } from '@graphscope/studio-components';
+import { GraphProvider, GraphCanvas, AddNode, ClearCanvas, ExportSvg } from '@graphscope/studio-flow-editor';
+import { Divider } from 'antd';
+
+const App = () => {
+ return (
+
+ );
+};
+
+export default App;
+```
+
+
+## ReactFlowProvider
+
+`ReactFlowProvider` 在内部用于为所有组件提供 ReactFlow 上下文。它已自动包含在 `GraphCanvas` 组件中,因此通常不需要直接使用它。
+
+### 访问状态
+
+要在 `GraphProvider` 的子组件中访问图状态,使用 `useGraphStore` 钩子:
+
+```bash
+import { useGraphStore } from '@graphscope/studio-flow-editor';
+
+const MyComponent = () => {
+ const { store, updateStore } = useGraphStore();
+
+ return (
+
+
节点数量: {store.nodes.length}
+
边数量: {store.edges.length}
+
+ );
+};
+```
+
+### 更新状态
+
+要更新图状态,使用 `useGraphStore` 提供的 `updateStore` 函数:
+
+```bash
+import { useGraphStore } from '@graphscope/studio-flow-editor';
+
+const AddRandomNode = () => {
+ const { updateStore } = useGraphStore();
+
+ const handleClick = () => {
+ updateStore(draft => {
+ draft.nodes.push({
+ id: `node-${Math.random().toString(36).substring(2, 9)}`,
+ position: { x: Math.random() * 500, y: Math.random() * 500 },
+ data: { label: '随机节点' },
+ type: 'graph-node',
+ });
+ });
+ };
+
+ return ;
+};
+```
+
+## 高级用法
+
+### 嵌套Provider
+
+可以嵌套Provider来创建复杂的状态层次结构:
+
+```bash
+import { GraphProvider } from '@graphscope/studio-flow-editor';
+import { ThemeProvider } from 'your-theme-provider';
+
+const App = () => {
+ return (
+
+
+
+
+
+ );
+};
+```
+
diff --git a/packages/studio-flow-editor/docs/quick-start.md b/packages/studio-flow-editor/docs/quick-start.md
new file mode 100644
index 000000000..ae7a2dd2c
--- /dev/null
+++ b/packages/studio-flow-editor/docs/quick-start.md
@@ -0,0 +1,39 @@
+---
+order: 1
+title: 快速开始
+---
+# 快速开始
+
+## 安装
+
+```bash
+# 使用npm
+npm install @graphscope/studio-flow-editor
+
+# 使用yarn
+yarn add @graphscope/studio-flow-editor
+
+# 使用pnpm
+pnpm add @graphscope/studio-flow-editor
+```
+
+## 基本用法
+
+下面是一个最简单的例子,展示了如何创建一个基本的图编辑器:
+
+```jsx
+import React from 'react';
+import { GraphProvider, GraphCanvas } from '@graphscope/studio-flow-editor';
+
+const App = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default App;
+```
diff --git a/packages/studio-flow-editor/docs/utils.md b/packages/studio-flow-editor/docs/utils.md
new file mode 100644
index 000000000..f5d0d9b85
--- /dev/null
+++ b/packages/studio-flow-editor/docs/utils.md
@@ -0,0 +1,157 @@
+---
+order: 7
+title: 工具函数
+---
+# 工具函数
+
+`@graphscope/studio-flow-editor` 包提供了多个工具函数,帮助您处理图编辑中常见的任务。
+
+## 布局工具
+
+### getBBox
+
+计算一组节点的边界框,用于确定需要可见的区域。
+
+```typescript
+function getBBox(nodes: Node[]): {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+};
+```
+
+**示例:**
+```bash
+import { getBBox } from '@graphscope/studio-flow-editor';
+
+const nodes = [
+ { id: '1', position: { x: 100, y: 100 } },
+ { id: '2', position: { x: 200, y: 300 } }
+];
+
+const bbox = getBBox(nodes);
+// 返回: { x: 100, y: 100, width: 200, height: 300 }
+```
+
+## 标签工具
+
+### createNodeLabel
+
+为新节点生成唯一标签。
+
+```typescript
+function createNodeLabel(): string;
+```
+
+**示例:**
+```bash
+import { createNodeLabel } from '@graphscope/studio-flow-editor';
+
+const newNodeLabel = createNodeLabel();
+// 返回: "Vertex_1", "Vertex_2", 等
+```
+
+### createEdgeLabel
+
+为新边生成唯一标签。
+
+```bash
+function createEdgeLabel(): string;
+```
+
+**示例:**
+```bash
+import { createEdgeLabel } from '@graphscope/studio-flow-editor';
+
+const newEdgeLabel = createEdgeLabel();
+// 返回: "Edge_1", "Edge_2", 等
+```
+
+### resetIndex
+
+重置用于生成节点和边标签的内部计数器。
+
+```bash
+function resetIndex(): void;
+```
+
+**示例:**
+```bash
+import { resetIndex } from '@graphscope/studio-flow-editor';
+
+resetIndex();
+// 下次调用 createNodeLabel() 将返回 "Vertex_1"
+```
+
+## 数据处理工具
+
+### fakeSnapshot
+
+创建对象的深拷贝。当您需要克隆图数据以避免意外修改时,这非常有用。
+
+```bash
+function fakeSnapshot(obj: T): T;
+```
+
+**示例:**
+```bash
+import { fakeSnapshot } from '@graphscope/studio-flow-editor';
+
+const originalNodes = [...];
+const nodesCopy = fakeSnapshot(originalNodes);
+// 现在可以修改 nodesCopy 而不会影响 originalNodes
+```
+
+## 类型定义
+
+该包还导出了几个在处理图数据时有用的 TypeScript 类型定义:
+
+### INodeData
+
+```typescript
+interface INodeData {
+ label: string;
+ disabled?: boolean;
+ properties?: Property[];
+ dataFields?: string[];
+ delimiter?: string;
+ datatype?: 'csv' | 'odps';
+ filelocation?: string;
+ [key: string]: any;
+}
+```
+
+### IEdgeData
+
+```typescript
+interface IEdgeData {
+ label: string;
+ disabled?: boolean;
+ saved?: boolean;
+ properties?: Property[];
+ source_vertex_fields?: Property;
+ target_vertex_fields?: Property;
+ dataFields?: string[];
+ delimiter?: string;
+ datatype?: 'csv' | 'odps';
+ filelocation?: string;
+ _extra?: {
+ type?: string;
+ offset?: string;
+ isLoop: boolean;
+ isRevert?: boolean;
+ isPoly?: boolean;
+ index?: number;
+ count?: number;
+ };
+ [key: string]: any;
+}
+```
+
+### ISchemaNode 和 ISchemaEdge
+
+```typescript
+type ISchemaNode = Node;
+type ISchemaEdge = Edge & { data: IEdgeData };
+```
diff --git a/packages/studio-flow-editor/index.html b/packages/studio-flow-editor/index.html
new file mode 100644
index 000000000..c96f69e74
--- /dev/null
+++ b/packages/studio-flow-editor/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Studio Graph Editor
+
+
+
+
+
+
+
diff --git a/packages/studio-flow-editor/index.tsx b/packages/studio-flow-editor/index.tsx
new file mode 100644
index 000000000..e786751d2
--- /dev/null
+++ b/packages/studio-flow-editor/index.tsx
@@ -0,0 +1,39 @@
+import * as React from 'react';
+import { GraphProvider, GraphCanvas, useGraphStore, useAddNode } from './src/index';
+import { createRoot } from 'react-dom/client';
+import { Button } from 'antd';
+
+interface IAppProps {}
+const Edit = () => {
+ const { store } = useGraphStore();
+ const { handleAddVertex } = useAddNode();
+ const { nodes } = store;
+ const printData = () => {
+ console.log('nodes::: ', nodes);
+ };
+ return (
+ <>
+
+
+ >
+ );
+};
+const DrawGraph: React.FunctionComponent = props => {
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+};
+
+createRoot(document.getElementById('root')!).render();
diff --git a/packages/studio-flow-editor/package.json b/packages/studio-flow-editor/package.json
new file mode 100644
index 000000000..53e79400b
--- /dev/null
+++ b/packages/studio-flow-editor/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "@graphscope/studio-flow-editor",
+ "version": "0.1.13",
+ "description": "",
+ "main": "lib/index.js",
+ "module": "es/index.js",
+ "types": "lib/index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/GraphScope/portal.git"
+ },
+ "files": [
+ "es",
+ "lib",
+ "dist"
+ ],
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org/"
+ },
+ "scripts": {
+ "start": "father dev",
+ "build": "father build",
+ "start:site": "vite dev",
+ "build:site": "vite build && tsc"
+ },
+ "peerDependencies": {
+ "react": "18.2.0",
+ "react-dom": "18.2.0"
+ },
+ "dependencies": {
+ "@graphscope/studio-components": "workspace:*",
+ "@graphscope/use-zustand": "workspace:*",
+ "antd": "^5.22.2",
+ "d3-force": "latest",
+ "dagre": "latest",
+ "html-to-image": "^1.11.11",
+ "immer": "^10.1.1",
+ "lodash": "^4.17.21",
+ "react-intl": "^6.6.1",
+ "reactflow": "latest",
+ "rxjs": "^7.8.1",
+ "uuid": "^9.0.1",
+ "valtio": "2.0.0-rc.1",
+ "zustand": "^4.5.5"
+ },
+ "devDependencies": {
+ "@types/d3-force": "latest",
+ "@types/lodash": "^4.14.202",
+ "@vitejs/plugin-react": "^4.2.1",
+ "vite": "^5.4.16"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/packages/studio-flow-editor/src/app/button-controller/add-node.tsx b/packages/studio-flow-editor/src/app/button-controller/add-node.tsx
new file mode 100644
index 000000000..67625d9ce
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/button-controller/add-node.tsx
@@ -0,0 +1,35 @@
+import * as React from 'react';
+import { Button, Tooltip } from 'antd';
+import { Icons } from '@graphscope/studio-components';
+import { FormattedMessage } from 'react-intl';
+import { useGraphStore } from '../store';
+import { useAddNode } from '../hooks/useAddNode';
+
+const AddNodeIcon = Icons.AddNode;
+
+interface IAddNodeProps {
+ style?: React.CSSProperties;
+ noDefaultLabel?: boolean;
+}
+let addNodeIndex = 0;
+
+const AddNode: React.FunctionComponent = props => {
+ const { style, noDefaultLabel } = props;
+ const { store } = useGraphStore();
+ const { elementOptions } = store;
+ const disabled = !elementOptions.isConnectable;
+ const tooltipText = disabled ? (
+
+ ) : (
+
+ );
+ const { handleAddVertex } = useAddNode({ noDefaultLabel });
+
+ return (
+
+
+
+ );
+};
+
+export default AddNode;
diff --git a/packages/studio-importor/src/app/button-controller/clear-canvas.tsx b/packages/studio-flow-editor/src/app/button-controller/clear-canvas.tsx
similarity index 63%
rename from packages/studio-importor/src/app/button-controller/clear-canvas.tsx
rename to packages/studio-flow-editor/src/app/button-controller/clear-canvas.tsx
index 8c90fd8f5..d0559a40f 100644
--- a/packages/studio-importor/src/app/button-controller/clear-canvas.tsx
+++ b/packages/studio-flow-editor/src/app/button-controller/clear-canvas.tsx
@@ -1,44 +1,36 @@
import * as React from 'react';
import { Button, Tooltip } from 'antd';
-
+import { useGraphStore } from '../store';
import { Icons, useStudioProvier } from '@graphscope/studio-components';
-import { resetIndex } from '../utils';
import { FormattedMessage } from 'react-intl';
+import { useClearCanvas } from '../hooks/useClearCanvas';
interface IAddNodeProps {
style?: React.CSSProperties;
}
-import { useContext } from '@graphscope/use-zustand';
const ClearCanvas: React.FunctionComponent = props => {
const { style } = props;
- const { updateStore, store } = useContext();
+ const { store } = useGraphStore();
const { elementOptions } = store;
- const { isLight } = useStudioProvier();
+ const { algorithm } = useStudioProvier();
+ const isLight = algorithm === 'defaultAlgorithm';
/** svg pathFill */
let pathFill = () => {
if (!isLight) {
- return elementOptions.isEditable ? '#585858' : '#fff';
+ return elementOptions.isEditable ? '#fff' : '#585858';
} else {
- return elementOptions.isEditable ? '#ddd' : '#000';
+ return elementOptions.isEditable ? '#000' : '#ddd';
}
};
- const tooltipText = elementOptions.isEditable ? (
+ const tooltipText = !elementOptions.isEditable ? (
) : (
);
-
- const handleClear = () => {
- resetIndex();
- updateStore(draft => {
- draft.nodes = [];
- draft.edges = [];
- });
- };
-
+ const { handleClear } = useClearCanvas();
return (
+ );
+};
+
+export default ExportImage;
diff --git a/packages/studio-flow-editor/src/app/button-controller/index.tsx b/packages/studio-flow-editor/src/app/button-controller/index.tsx
new file mode 100644
index 000000000..77aa0aad5
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/button-controller/index.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { Toolbar } from '@graphscope/studio-components';
+import ClearCanvas from './clear-canvas';
+import AddNode from './add-node';
+import ExportImage from './export-image';
+
+const ButtonController: React.FunctionComponent<{ noDefaultLabel?: boolean }> = props => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default ButtonController;
diff --git a/packages/studio-importor/src/app/elements/arrow-marker/index.tsx b/packages/studio-flow-editor/src/app/elements/arrow-marker/index.tsx
similarity index 100%
rename from packages/studio-importor/src/app/elements/arrow-marker/index.tsx
rename to packages/studio-flow-editor/src/app/elements/arrow-marker/index.tsx
diff --git a/packages/studio-importor/src/app/elements/connection-line/index.tsx b/packages/studio-flow-editor/src/app/elements/connection-line/index.tsx
similarity index 100%
rename from packages/studio-importor/src/app/elements/connection-line/index.tsx
rename to packages/studio-flow-editor/src/app/elements/connection-line/index.tsx
diff --git a/packages/studio-importor/src/app/elements/edge-types/graph-edge.tsx b/packages/studio-flow-editor/src/app/elements/edge-types/graph-edge.tsx
similarity index 96%
rename from packages/studio-importor/src/app/elements/edge-types/graph-edge.tsx
rename to packages/studio-flow-editor/src/app/elements/edge-types/graph-edge.tsx
index 6d7985671..6cf4786c7 100644
--- a/packages/studio-importor/src/app/elements/edge-types/graph-edge.tsx
+++ b/packages/studio-flow-editor/src/app/elements/edge-types/graph-edge.tsx
@@ -9,7 +9,7 @@ import {
getSmoothPath,
calculateDegree,
} from './utils';
-import { useContext } from '@graphscope/use-zustand';
+import { useGraphStore } from '../../store';
import LoopEdge from './loop-edge';
import Label from './label';
import { useStudioProvier } from '@graphscope/studio-components';
@@ -20,7 +20,7 @@ function GraphEdge(props: EdgeProps) {
const { offset = 0 } = _extra || {};
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
- const { store } = useContext();
+ const { store } = useGraphStore();
const { currentId, theme } = store;
const { isLight } = useStudioProvier();
if (!sourceNode || !targetNode) {
diff --git a/packages/studio-importor/src/app/elements/edge-types/index.ts b/packages/studio-flow-editor/src/app/elements/edge-types/index.ts
similarity index 100%
rename from packages/studio-importor/src/app/elements/edge-types/index.ts
rename to packages/studio-flow-editor/src/app/elements/edge-types/index.ts
diff --git a/packages/studio-importor/src/app/elements/edge-types/label.tsx b/packages/studio-flow-editor/src/app/elements/edge-types/label.tsx
similarity index 96%
rename from packages/studio-importor/src/app/elements/edge-types/label.tsx
rename to packages/studio-flow-editor/src/app/elements/edge-types/label.tsx
index 69b079434..89f4d9697 100644
--- a/packages/studio-importor/src/app/elements/edge-types/label.tsx
+++ b/packages/studio-flow-editor/src/app/elements/edge-types/label.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import { EdgeLabelRenderer } from 'reactflow';
import { EditableText, useStudioProvier, useSection } from '@graphscope/studio-components';
-import { useContext } from '@graphscope/use-zustand';
+import { useGraphStore } from '../../store';
import { CheckOutlined } from '@ant-design/icons';
import { theme } from 'antd';
const { useToken } = theme;
@@ -15,7 +15,7 @@ interface ILabelProps {
const Label: React.FunctionComponent = props => {
const { id, label, style, filelocation, disabled } = props;
- const { store, updateStore } = useContext();
+ const { store, updateStore } = useGraphStore();
const { toggleRightSide } = useSection();
const { token } = useToken();
const { currentId, theme } = store;
diff --git a/packages/studio-importor/src/app/elements/edge-types/loop-edge.tsx b/packages/studio-flow-editor/src/app/elements/edge-types/loop-edge.tsx
similarity index 97%
rename from packages/studio-importor/src/app/elements/edge-types/loop-edge.tsx
rename to packages/studio-flow-editor/src/app/elements/edge-types/loop-edge.tsx
index 4eae5a0ed..ae0dbd5f7 100644
--- a/packages/studio-importor/src/app/elements/edge-types/loop-edge.tsx
+++ b/packages/studio-flow-editor/src/app/elements/edge-types/loop-edge.tsx
@@ -1,6 +1,5 @@
import React from 'react';
import type { EdgeProps } from 'reactflow';
-import { useContext } from '@graphscope/use-zustand';
import { usePathStyle } from './useStyle';
import Label from './label';
diff --git a/packages/studio-importor/src/app/elements/edge-types/table-edge.tsx b/packages/studio-flow-editor/src/app/elements/edge-types/table-edge.tsx
similarity index 100%
rename from packages/studio-importor/src/app/elements/edge-types/table-edge.tsx
rename to packages/studio-flow-editor/src/app/elements/edge-types/table-edge.tsx
diff --git a/packages/studio-importor/src/app/elements/edge-types/useStyle.ts b/packages/studio-flow-editor/src/app/elements/edge-types/useStyle.ts
similarity index 86%
rename from packages/studio-importor/src/app/elements/edge-types/useStyle.ts
rename to packages/studio-flow-editor/src/app/elements/edge-types/useStyle.ts
index e6f9ffc2f..70b400097 100644
--- a/packages/studio-importor/src/app/elements/edge-types/useStyle.ts
+++ b/packages/studio-flow-editor/src/app/elements/edge-types/useStyle.ts
@@ -1,7 +1,7 @@
-import { useContext } from '@graphscope/use-zustand';
+import {useGraphStore} from '../../store'
import { useStudioProvier } from '@graphscope/studio-components';
export const usePathStyle = (id: string) => {
- const { store } = useContext();
+ const { store } = useGraphStore();
const { currentId, theme } = store;
const isSelected = id === currentId;
const { isLight } = useStudioProvier();
diff --git a/packages/studio-importor/src/app/elements/edge-types/utils.ts b/packages/studio-flow-editor/src/app/elements/edge-types/utils.ts
similarity index 100%
rename from packages/studio-importor/src/app/elements/edge-types/utils.ts
rename to packages/studio-flow-editor/src/app/elements/edge-types/utils.ts
diff --git a/packages/studio-importor/src/app/elements/forceLayout.tsx b/packages/studio-flow-editor/src/app/elements/forceLayout.tsx
similarity index 81%
rename from packages/studio-importor/src/app/elements/forceLayout.tsx
rename to packages/studio-flow-editor/src/app/elements/forceLayout.tsx
index e0253fff1..5376278b7 100644
--- a/packages/studio-importor/src/app/elements/forceLayout.tsx
+++ b/packages/studio-flow-editor/src/app/elements/forceLayout.tsx
@@ -4,7 +4,7 @@ export interface IParams {
iterations?: number;
center?: { x: number; y: number };
}
-export function createStaticForceLayout(nodes, edges, params: IParams = {}) {
+export function createStaticForceLayout(nodes: any[], edges: any[], params: IParams = {}) {
const { iterations = 1000, center = { x: window.innerWidth / 2, y: window.innerHeight / 2 } } = params;
const edgeDistance = 250;
const nodeStrength = -1 * (edgeDistance * (edges.length === 0 ? 0.5 : 8));
@@ -27,7 +27,7 @@ export function createStaticForceLayout(nodes, edges, params: IParams = {}) {
}
// 输出最终的节点和连线的位置
- nodes.forEach(node => {
+ nodes.forEach((node: { index: any; vx: any; vy: any; position: { x: any; y: any; }; x: any; y: any; }) => {
delete node.index;
delete node.vx;
delete node.vy;
@@ -36,7 +36,7 @@ export function createStaticForceLayout(nodes, edges, params: IParams = {}) {
delete node.y;
});
- edges.forEach(edge => {
+ edges.forEach((edge: { index: any; source: { id: any; }; target: { id: any; }; }) => {
delete edge.index;
edge.source = edge.source.id;
edge.target = edge.target.id;
diff --git a/packages/studio-flow-editor/src/app/elements/index.ts b/packages/studio-flow-editor/src/app/elements/index.ts
new file mode 100644
index 000000000..aef897fe5
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/elements/index.ts
@@ -0,0 +1,15 @@
+import processEdges from './processEdges';
+import { ISchemaEdge } from '../types';
+export function transformEdges(_edges: ISchemaEdge[], displayMode): ISchemaEdge[] {
+ const edges = processEdges(_edges);
+ return edges.map((item, index) => {
+ const { id, source, target, data } = item;
+ return {
+ id: id || `${source}-${target}-${index}`,
+ source,
+ target,
+ type: displayMode === 'table' ? 'smoothstep' : 'graph-edge',
+ data,
+ };
+ });
+}
diff --git a/packages/studio-importor/src/app/elements/node-types/arrow.tsx b/packages/studio-flow-editor/src/app/elements/node-types/arrow.tsx
similarity index 100%
rename from packages/studio-importor/src/app/elements/node-types/arrow.tsx
rename to packages/studio-flow-editor/src/app/elements/node-types/arrow.tsx
diff --git a/packages/studio-importor/src/app/elements/node-types/graph-node.tsx b/packages/studio-flow-editor/src/app/elements/node-types/graph-node.tsx
similarity index 97%
rename from packages/studio-importor/src/app/elements/node-types/graph-node.tsx
rename to packages/studio-flow-editor/src/app/elements/node-types/graph-node.tsx
index fcff6b572..8a4520e27 100644
--- a/packages/studio-importor/src/app/elements/node-types/graph-node.tsx
+++ b/packages/studio-flow-editor/src/app/elements/node-types/graph-node.tsx
@@ -4,7 +4,7 @@ import { theme } from 'antd';
import { CheckOutlined } from '@ant-design/icons';
import { EditableText, useStudioProvier, useSection } from '@graphscope/studio-components';
const { useToken } = theme;
-import { useContext } from '@graphscope/use-zustand';
+import {useGraphStore} from '../../store'
const R = 50;
const HALO_LINE_WIDTH = 16;
@@ -25,7 +25,7 @@ const styles = {
const GraphNode = (props: NodeProps) => {
const { data = {}, id } = props;
const { label, filelocation, disabled } = data;
- const { store, updateStore } = useContext();
+ const { store, updateStore } = useGraphStore();
const { currentId, theme, elementOptions } = store;
const { isLight } = useStudioProvier();
const { toggleRightSide, toggleLeftSide } = useSection();
diff --git a/packages/studio-importor/src/app/elements/node-types/index.ts b/packages/studio-flow-editor/src/app/elements/node-types/index.ts
similarity index 100%
rename from packages/studio-importor/src/app/elements/node-types/index.ts
rename to packages/studio-flow-editor/src/app/elements/node-types/index.ts
diff --git a/packages/studio-importor/src/app/elements/node-types/table-node.tsx b/packages/studio-flow-editor/src/app/elements/node-types/table-node.tsx
similarity index 100%
rename from packages/studio-importor/src/app/elements/node-types/table-node.tsx
rename to packages/studio-flow-editor/src/app/elements/node-types/table-node.tsx
diff --git a/packages/studio-flow-editor/src/app/elements/processEdges.tsx b/packages/studio-flow-editor/src/app/elements/processEdges.tsx
new file mode 100644
index 000000000..1fcacfb26
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/elements/processEdges.tsx
@@ -0,0 +1,126 @@
+import type { ISchemaEdge } from '../types';
+function isEven(number: number) {
+ return number % 2 === 0;
+}
+
+function isOdd(number: number) {
+ return !isEven(number);
+}
+
+const POLY_DEFAULT = 30;
+const LOOP_DEFAULT = 10;
+const LOOP_LABEL_POSITION_DEFAULT = 1;
+
+/**
+ *
+ * @param edges 边的集合
+ * @param {poly,loop} 设置多边和自环多边的distance
+ */
+const processEdges = (
+ edges: ISchemaEdge[],
+ {
+ poly = POLY_DEFAULT,
+ loop = LOOP_DEFAULT,
+ loopLabelPosition = LOOP_LABEL_POSITION_DEFAULT,
+ }: {
+ /** poly distance */
+ poly?: number;
+ /** loop distance */
+ loop?: number;
+ /** loop label position based on loop edge */
+ loopLabelPosition?: number;
+ } = {
+ poly: POLY_DEFAULT,
+ loop: LOOP_DEFAULT,
+ loopLabelPosition: LOOP_LABEL_POSITION_DEFAULT,
+ },
+) => {
+ const edgesMap: { [edgeId: string]: ISchemaEdge[] } = {};
+ /** 计算得到 edgesMap */
+ edges.forEach((item, index) => {
+ const edge = { ...item };
+ const { source, target } = edge;
+ const groupId = `${source}-${target}`;
+ const revertGroupId = `${target}-${source}`;
+ /** 存储edge */
+ if (edgesMap[groupId]) {
+ edgesMap[groupId].push(edge);
+ } else if (edgesMap[revertGroupId]) {
+ edgesMap[revertGroupId].push(edge);
+ } else {
+ edgesMap[groupId] = [edge];
+ }
+ });
+
+ const edgeGroups = Object.values(edgesMap);
+ const newEdges: ISchemaEdge[] = [];
+ edgeGroups.forEach(edges => {
+ const isMultipleEdge = edges.length > 1;
+ // 说明是多边的情况
+ if (isMultipleEdge) {
+ const isEvenCount = isEven(edges.length);
+ edges.forEach((edge, i: number) => {
+ const { source, target, data } = edge;
+
+ const revertGroupId = `${target}-${source}`;
+ const isLoop = source === target;
+ const isRevert = !!edgesMap[revertGroupId];
+ const index = i;
+ let distance;
+ if (isEvenCount) {
+ // 奇数
+ const idx = Math.ceil((index + 1) / 2);
+ distance = poly * idx;
+ } else {
+ // 偶数
+ const calculateIdx = isOdd(index) ? index + 1 : index;
+ const idx = Math.ceil(calculateIdx / 2);
+ distance = poly * idx;
+ }
+ let offset = isEven(index) ? distance : -distance;
+ let type = 'poly';
+ // // 反向边需要revert
+ if (isRevert) {
+ offset = -offset;
+ }
+ if (isLoop) {
+ type = 'loop';
+ offset = index * loop;
+ }
+ newEdges.push({
+ ...edge,
+ data: {
+ ...data,
+ _extra: {
+ count: edges.length,
+ index: index,
+ type: type,
+ isPoly: true,
+ isLoop: isLoop,
+ offset: offset.toString(),
+ isRevert: isRevert,
+ },
+ },
+ });
+ });
+ } else {
+ const [edge] = edges;
+ const { source, target, data } = edge;
+ newEdges.push({
+ ...edge,
+ data: {
+ ...data,
+ _extra: {
+ count: 1,
+ index: 0,
+ isLoop: source === target,
+ },
+ },
+ });
+ }
+ });
+
+ return newEdges;
+};
+
+export default processEdges;
diff --git a/packages/studio-flow-editor/src/app/hooks/index.ts b/packages/studio-flow-editor/src/app/hooks/index.ts
new file mode 100644
index 000000000..ce6de1b4d
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/hooks/index.ts
@@ -0,0 +1,3 @@
+export { useAddNode } from './useAddNode';
+export { useClearCanvas } from './useClearCanvas';
+export { useExportSvg } from './useExportSvg';
diff --git a/packages/studio-flow-editor/src/app/hooks/useAddNode.ts b/packages/studio-flow-editor/src/app/hooks/useAddNode.ts
new file mode 100644
index 000000000..280025419
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/hooks/useAddNode.ts
@@ -0,0 +1,36 @@
+import { useRef } from 'react';
+import { v4 as uuidv4 } from 'uuid';
+import { useReactFlow } from 'reactflow';
+import { useGraphStore } from '../store';
+import { createNodeLabel } from '../utils';
+
+export const useAddNode = ({ noDefaultLabel }: { noDefaultLabel?: boolean } = {}) => {
+ const addNodeIndexRef = useRef(0);
+ const { setCenter } = useReactFlow();
+ const { updateStore } = useGraphStore();
+ const handleAddVertex = (position?: { x: number; y: number }) => {
+
+ updateStore(draft => {
+ const label = noDefaultLabel ? '' : createNodeLabel();
+ const x = addNodeIndexRef.current * 200;
+ const y = addNodeIndexRef.current * 100;
+ addNodeIndexRef.current++;
+ draft.nodes = [
+ ...draft.nodes,
+ {
+ id: uuidv4(),
+ position: position || {
+ x,
+ y,
+ },
+ type: 'graph-node',
+ data: { label },
+ },
+ ];
+
+ setCenter(x + 100 / 2, y + 100 / 2, { duration: 600, zoom: 1 });
+ });
+ };
+
+ return { handleAddVertex };
+};
diff --git a/packages/studio-flow-editor/src/app/hooks/useClearCanvas.ts b/packages/studio-flow-editor/src/app/hooks/useClearCanvas.ts
new file mode 100644
index 000000000..585cd0278
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/hooks/useClearCanvas.ts
@@ -0,0 +1,54 @@
+import { useGraphStore } from '../store';
+import { resetIndex } from '../utils';
+
+export const useClearCanvas = () => {
+ const { updateStore, store } = useGraphStore();
+ const { selectedNodeIds, selectedEdgeIds } = store;
+
+ const handleClear = () => {
+ // 如果有选中的节点或边
+ if ((selectedNodeIds && selectedNodeIds.length > 0) ||
+ (selectedEdgeIds && selectedEdgeIds.length > 0)) {
+
+ updateStore(draft => {
+ // 如果有选中的节点
+ if (selectedNodeIds && selectedNodeIds.length > 0) {
+ // 创建一个选中节点的集合,用于快速查找
+ const selectedNodeIdSet = new Set(selectedNodeIds);
+
+ // 删除选中的节点
+ draft.nodes = draft.nodes.filter(node => !selectedNodeIdSet.has(node.id));
+
+ // 删除与选中节点相关的边
+ draft.edges = draft.edges.filter(
+ edge => !selectedNodeIdSet.has(edge.source) && !selectedNodeIdSet.has(edge.target)
+ );
+ }
+
+ // 如果有选中的边
+ if (selectedEdgeIds && selectedEdgeIds.length > 0) {
+ // 创建一个选中边的集合,用于快速查找
+ const selectedEdgeIdSet = new Set(selectedEdgeIds);
+
+ // 删除选中的边
+ draft.edges = draft.edges.filter(edge => !selectedEdgeIdSet.has(edge.id));
+ }
+
+ // 清除选中状态
+ if (draft.selectedNodeIds) draft.selectedNodeIds = [];
+ if (draft.selectedEdgeIds) draft.selectedEdgeIds = [];
+ });
+ } else {
+ // 如果没有选中任何节点或边,则清空所有
+ resetIndex();
+ updateStore(draft => {
+ draft.nodes = [];
+ draft.edges = [];
+ if (draft.selectedNodeIds) draft.selectedNodeIds = [];
+ if (draft.selectedEdgeIds) draft.selectedEdgeIds = [];
+ });
+ }
+ };
+
+ return { handleClear };
+};
diff --git a/packages/studio-flow-editor/src/app/hooks/useExportSvg.ts b/packages/studio-flow-editor/src/app/hooks/useExportSvg.ts
new file mode 100644
index 000000000..4f11321bb
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/hooks/useExportSvg.ts
@@ -0,0 +1,19 @@
+import { Utils } from '@graphscope/studio-components';
+import { toSvg } from 'html-to-image';
+
+export const useExportSvg = () => {
+ const exportSvg = async ({ name = 'model.svg', parentId = '' }: { name?: string; parentId?: string } = {}) => {
+ let viewBox: HTMLDivElement | null = null;
+ if(parentId){
+ viewBox = document.querySelector('#'+parentId+' .react-flow__viewport') as HTMLDivElement;
+ }else{
+ viewBox = document.querySelector('.react-flow__viewport') as HTMLDivElement;
+ }
+ if (viewBox) {
+ const dataUrl = await toSvg(viewBox, {});
+ Utils.downloadImage(dataUrl, name);
+ }
+ };
+
+ return { exportSvg };
+};
diff --git a/packages/studio-flow-editor/src/app/hooks/useInteractive.ts b/packages/studio-flow-editor/src/app/hooks/useInteractive.ts
new file mode 100644
index 000000000..e347a2f57
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/hooks/useInteractive.ts
@@ -0,0 +1,232 @@
+import { useCallback, useEffect, useRef } from 'react';
+import {
+ useReactFlow,
+ applyEdgeChanges,
+ applyNodeChanges,
+ getNodesBounds,
+ ReactFlowInstance,
+ OnConnectStartParams,
+ OnConnectEnd,
+ useOnSelectionChange,
+} from 'reactflow';
+import { v4 as uuidv4 } from 'uuid';
+import { createStaticForceLayout } from '../elements/forceLayout';
+import { transformEdges } from '../elements';
+import { fakeSnapshot } from '../utils/index';
+import type { NodeChange, EdgeChange, NodePositionChange } from 'reactflow';
+import { useGraphStore } from '../store';
+import { ISchemaEdge } from '../types';
+import { getBBox, createEdgeLabel, createNodeLabel } from '../utils';
+
+const useInteractive = props => {
+ const { store, updateStore } = useGraphStore();
+ const { handleNodesChange, handleEdgesChange, onSelectionChange, isPreview, noDefaultLabel } = props;
+ const { nodes, edges, nodePositionChange, hasLayouted, elementOptions, displayMode } = store;
+ const { screenToFlowPosition, fitBounds } = useReactFlow();
+ const connectingNodeId = useRef(null);
+ const timerRef = useRef(null);
+ const tempRef = useRef([]);
+
+ const onConnectStart = useCallback(
+ (_: any, { nodeId }: OnConnectStartParams) => {
+ connectingNodeId.current = nodeId;
+ if (!elementOptions.isConnectable) return;
+ },
+ [elementOptions.isConnectable],
+ );
+
+ const onConnectEnd: OnConnectEnd = useCallback(
+ event => {
+ if (!connectingNodeId.current) return;
+ if (!elementOptions.isConnectable) return;
+
+ const target = event.target as HTMLElement;
+ const targetIsPane = target.classList.contains('react-flow__pane');
+ if (targetIsPane) {
+ const nodeId = uuidv4();
+ const edgeId = uuidv4();
+ const position =
+ 'clientX' in event
+ ? {
+ x: event.clientX,
+ y: event.clientY,
+ }
+ : {
+ x: event.touches[0].clientX,
+ y: event.touches[0].clientY,
+ };
+ const newPosition = screenToFlowPosition(position);
+ const newNode = {
+ id: nodeId,
+ position: {
+ x: newPosition.x - 50,
+ y: newPosition.y - 50,
+ },
+ type: 'graph-node',
+ data: { label: noDefaultLabel ? '' : createNodeLabel() },
+ };
+
+ updateStore(draft => {
+ draft.nodes = [...nodes, newNode];
+ draft.edges = [
+ ...edges,
+ {
+ id: edgeId,
+ source: connectingNodeId.current || '',
+ target: nodeId,
+ type: 'graph-edge',
+ data: {
+ label: noDefaultLabel ? '' : createEdgeLabel(),
+ },
+ },
+ ];
+ });
+ } else {
+ const nodeid = target.dataset.nodeid;
+ if (!nodeid) return;
+ const edgeId = uuidv4();
+ const edgeLabel = createEdgeLabel();
+
+ updateStore(draft => {
+ draft.edges = transformEdges(
+ [
+ ...draft.edges,
+ {
+ id: edgeId,
+ data: {
+ label: noDefaultLabel ? '' : edgeLabel,
+ },
+ source: connectingNodeId.current || '',
+ target: nodeid,
+ },
+ ],
+ displayMode,
+ );
+ });
+ }
+ },
+ [screenToFlowPosition, elementOptions.isConnectable, nodes, edges, updateStore],
+ );
+
+ const onNodesChange = (changes: NodeChange[]) => {
+ if (isPreview) {
+ return;
+ }
+ const { type } = changes[0];
+
+ if (elementOptions.isConnectable && type !== 'position') {
+ updateStore(draft => {
+ const newNodes = applyNodeChanges(changes, fakeSnapshot(nodes));
+ draft.nodes = newNodes;
+ if (handleNodesChange) {
+ handleNodesChange(newNodes);
+ }
+ });
+ }
+
+ if (type === 'position') {
+ if (changes[0].dragging) {
+ tempRef.current = changes;
+ updateStore(draft => {
+ draft.nodePositionChange = changes as NodePositionChange[];
+ });
+ } else {
+ updateStore(draft => {
+ const newNodes = applyNodeChanges(tempRef.current, nodes);
+ draft.nodes = newNodes;
+ if (handleNodesChange) {
+ handleNodesChange(newNodes);
+ }
+ });
+ }
+ }
+ };
+
+ useOnSelectionChange({
+ onChange: useCallback(
+ ({ nodes, edges }) => {
+ updateStore(draft => {
+ draft.selectedNodeIds = nodes.map(node => node.id);
+ draft.selectedEdgeIds = edges.map(edge => edge.id);
+ });
+ onSelectionChange && onSelectionChange(nodes, edges as unknown as ISchemaEdge[]);
+ },
+ [onSelectionChange],
+ ),
+ });
+ const onEdgesChange = (changes: EdgeChange[]) => {
+ if (isPreview) return;
+ if (elementOptions.isConnectable) {
+ updateStore(draft => {
+ draft.edges = applyEdgeChanges(changes, fakeSnapshot(edges)) as typeof edges;
+ });
+ }
+ };
+
+ const onDoubleClick = () => {
+ const bbox = getBBox(nodes);
+ fitBounds(bbox, { duration: 600 });
+ };
+
+ useEffect(() => {
+ if (handleEdgesChange) {
+ handleEdgesChange(edges);
+ console.log('handleEdgesChange edges::: ', edges);
+ }
+ if (handleNodesChange) {
+ handleNodesChange(nodes);
+ }
+ }, [edges, nodes]);
+
+ useEffect(() => {
+ if (nodes.length > 0 && !hasLayouted) {
+ const graph = createStaticForceLayout(fakeSnapshot(nodes), fakeSnapshot(edges));
+ if (timerRef.current) {
+ clearTimeout(timerRef.current);
+ }
+ const timerId = setTimeout(() => {
+ const bbox = getBBox(graph.nodes);
+ fitBounds(bbox, { duration: 300 });
+ }, 300);
+ timerRef.current = timerId;
+ updateStore(draft => {
+ draft.hasLayouted = true;
+ draft.nodes = graph.nodes;
+ draft.edges = graph.edges;
+ });
+ }
+ }, [nodes, edges, hasLayouted, updateStore]);
+
+ const onReactFlowInit = (reactFlowInstance: ReactFlowInstance) => {
+ if (reactFlowInstance) {
+ setTimeout(() => {
+ const allNodes = reactFlowInstance.toObject().nodes;
+ const bbox = getNodesBounds(allNodes);
+ fitBounds(bbox, { duration: 600 });
+ }, 300);
+ }
+ };
+
+ useEffect(() => {
+ /** 把marker 放到 reactflow 内部,目的是为了导出的时候 dom 不丢失 */
+ const marker = document.getElementById('arrow-marker-svg');
+ const view = document.querySelector('.react-flow__viewport');
+ if (marker && view) {
+ view.appendChild(marker);
+ }
+ }, []);
+
+ return {
+ nodes,
+ edges,
+ nodePositionChange,
+ onDoubleClick,
+ onEdgesChange,
+ onNodesChange,
+ onConnectStart,
+ onConnectEnd,
+ onReactFlowInit,
+ };
+};
+
+export default useInteractive;
diff --git a/packages/studio-flow-editor/src/app/index.tsx b/packages/studio-flow-editor/src/app/index.tsx
new file mode 100644
index 000000000..fb38ad617
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/index.tsx
@@ -0,0 +1,89 @@
+import React,{useEffect} from 'react';
+import { ReactFlow, ReactFlowProvider, applyNodeChanges, Background, MiniMap, Controls } from 'reactflow';
+import useInteractive from './hooks/useInteractive';
+import { nodeTypes } from './elements/node-types';
+import { edgeTypes } from './elements/edge-types';
+import ArrowMarker from './elements/arrow-marker';
+import ConnectionLine from './elements/connection-line';
+import { theme } from 'antd';
+import type { ImportorProps } from './types';
+import cssStyles from './style';
+import locales from './locales';
+import { StudioProvier, useDynamicStyle } from '@graphscope/studio-components';
+import { useGraphStore } from './store';
+
+const GraphCanvas: React.FC = ({
+ children,
+ nodesDraggable = true,
+ isPreview = false,
+ onNodesChange: handleNodesChange,
+ onEdgesChange: handleEdgesChange,
+ onSelectionChange,
+ noDefaultLabel,
+ defaultNodes,
+ defaultEdges
+}) => {
+ const {
+ nodes,
+ edges,
+ nodePositionChange,
+ onDoubleClick,
+ onEdgesChange,
+ onNodesChange,
+ onConnectStart,
+ onConnectEnd,
+ onReactFlowInit,
+ } = useInteractive({ isPreview, handleNodesChange, handleEdgesChange, onSelectionChange, noDefaultLabel });
+ const { token } = theme.useToken();
+ const { updateStore } = useGraphStore();
+ useDynamicStyle(cssStyles, 'graphscope-flow-editor');
+ const _nodes = nodePositionChange.length === 0 ? nodes : applyNodeChanges(nodePositionChange, nodes);
+ useEffect(() => {
+ if(defaultNodes){
+ updateStore(draft => {
+ draft.nodes = defaultNodes||[];
+ });
+ }
+ if(defaultEdges){
+ updateStore(draft => {
+ draft.edges = defaultEdges||[];
+ });
+ }
+ }, []);
+ return (
+
+ );
+};
+export default (props: ImportorProps) => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/packages/studio-flow-editor/src/app/locales/en-US.ts b/packages/studio-flow-editor/src/app/locales/en-US.ts
new file mode 100644
index 000000000..2585d5a72
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/locales/en-US.ts
@@ -0,0 +1,95 @@
+export default {
+ 'Start sketching a model, a vertex label is a named grouping or categorization of nodes within the graph dataset':
+ 'Start sketching a model, a vertex label is a named grouping or categorization of nodes within the graph dataset',
+ 'Please manually input the odps file location': 'Please manually input the odps file location',
+ Upload: 'Upload',
+ /** import data */
+ 'Load data': 'Load data',
+ 'Start importing': 'Start importing',
+ Close: 'Close',
+ 'Goto Modeling': 'Goto Modeling',
+ 'Save Modeling': 'Save Modeling',
+ 'Goto Graphs': 'Goto Graphs',
+ 'Goto Importing': 'Goto Importing',
+ 'Go back to modify the graph model.': 'Go back to modify the graph model.',
+ 'View Schema': 'View Schema',
+ 'Successfully saved the graph model': 'Successfully saved the graph model',
+ 'Failed to save the graph model': 'Failed to save the graph model',
+ 'Please create the graph model first': 'Please create the graph model first',
+ 'Sorry, the system detected that there is no available graph model. Please create a graph model before importing data':
+ 'Sorry, the system detected that there is no available graph model. Please create a graph model before importing data',
+ 'Bulk Import': 'Bulk Import',
+ 'Bulk Import Data': 'Bulk Import Data',
+ 'The system has detected that you have previously uploaded CSV files. You can quickly bulk import data into graphscope':
+ 'The system has detected that you have previously uploaded CSV files. You can quickly bulk import data into graphscope',
+ 'Click or drag file to this area to upload': 'Click or drag file to this area to upload',
+ 'Clear all files': 'Clear all files',
+ 'Generate graph model': 'Generate graph model',
+ 'If you already have CSV data, feel free to upload it here, and the system will automatically infer possible graph models for you.':
+ 'If you already have CSV data, feel free to upload it here, and the system will automatically infer possible graph models for you.',
+ 'If you already have SQLDDL file, feel free to upload it here, and the system will automatically infer possible graph models for you.':
+ 'If you already have SQLDDL file, feel free to upload it here, and the system will automatically infer possible graph models for you.',
+
+ /** 创建图模型tip */
+ 'A vertex must have a primary key.': 'A vertex must have a primary key.',
+ 'A vertex must have at least one property.': 'A vertex must have at least one property.',
+ 'A edge can only have one property.': 'A edge can only have one property.',
+
+ 'The current mode is preview only, and does not support clearing the model':
+ 'The current mode is preview only, and does not support clearing the model',
+ 'Clear graph model': 'Clear graph model',
+ 'The current mode is preview only, and does not support creating new vertex':
+ 'The current mode is preview only, and does not support creating new vertex',
+ 'Create new vertex': 'Create new vertex',
+ 'The current mode is preview only, and does not support opening multi-source modeling':
+ 'The current mode is preview only, and does not support opening multi-source modeling',
+ 'Expand or collapse multi-source modeling': 'Expand or collapse multi-source modeling',
+ Configuration: 'Configuration',
+ 'You have successfully bound the data source. Please complete the configuration to start importing data.':
+ 'You have successfully bound the data source. Please complete the configuration to start importing data.',
+ 'The data loading task has been successfully created. You can view detailed logs in the job center.':
+ 'The data loading task has been successfully created. You can view detailed logs in the job center.',
+ 'Goto Jobs': 'Goto Jobs',
+ 'Please select a primate type.': 'Please select a primate type.',
+ 'Click or drag file to this area to parse it': 'Click or drag file to this area to parse it',
+ 'If you already have CSV data, feel free to parse it here, and the system will automatically infer possible graph models for you.':
+ 'If you already have CSV data, feel free to parse it here, and the system will automatically infer possible graph models for you.',
+ 'Data source binding': 'Data source binding',
+ 'Shortcut: parse files into a graph model': 'Shortcut: parse files into a graph model',
+ 'Save graph model to svg image': 'Save graph model to svg image',
+ 'Save graph model to JSON config': 'Save graph model to JSON config',
+ 'Parse files into a graph model': 'Parse files into a graph model',
+ 'For the definition and description of the schema model, please refer to the ':
+ 'For the definition and description of the schema model, please refer to the ',
+ document: 'document',
+ Vertex: 'Vertex',
+ Edges: 'Edges',
+ 'File type': 'File type',
+ 'ID field': 'ID field',
+ 'Label field': 'Label field',
+ 'Target field': 'Target field',
+ 'Source field': 'Source field',
+ 'Delete this label': 'Delete this label',
+ Label: 'Label',
+ 'Data Fields': 'Data Fields',
+ 'Add Property': 'Add Property',
+ Properties: 'Properties',
+ 'Uploading...': 'Uploading...',
+ Delimiter: 'Delimiter',
+ Type: 'Type',
+ 'Header Row': 'Header Row',
+ 'Import Option': 'Import Option',
+ Quoting: 'Quoting',
+ 'Quote char': 'Quote char',
+ Source: 'Source',
+ Target: 'Target',
+ 'Load data right now': 'Load data right now',
+ 'Click to rebind datasource': 'Click to rebind datasource',
+ 'Click to bind datasource': 'Click to bind datasource',
+ 'Preview dataloading configuration': 'Preview dataloading configuration',
+ 'Please bind datasource first': 'Please bind datasource first',
+ "Click to load all label's data": "Click to load all label's data",
+ 'Binding all datasource in batch': 'Binding all datasource in batch',
+ 'Bind all the datasource, but first, please check if the location for each label is filled in correctly.':
+ 'Bind all the datasource, but first, please check if the location for each label is filled in correctly.',
+};
diff --git a/packages/studio-flow-editor/src/app/locales/index.tsx b/packages/studio-flow-editor/src/app/locales/index.tsx
new file mode 100644
index 000000000..8bcb434d2
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/locales/index.tsx
@@ -0,0 +1,6 @@
+import enUS from './en-US';
+import zhCN from './zh-CN';
+export default {
+ 'en-US': enUS,
+ 'zh-CN': zhCN,
+};
diff --git a/packages/studio-flow-editor/src/app/locales/zh-CN.ts b/packages/studio-flow-editor/src/app/locales/zh-CN.ts
new file mode 100644
index 000000000..91e620f84
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/locales/zh-CN.ts
@@ -0,0 +1,93 @@
+export default {
+ 'Start sketching a model, a vertex label is a named grouping or categorization of nodes within the graph dataset':
+ '开始绘制模型吧,顶点标签是图数据集中节点的命名分组或分类',
+ 'Please manually input the odps file location': '请手动填写 odps 文件位置',
+ Upload: '上传',
+ /** import data */
+ 'Load data': '加载数据',
+ 'Start importing': '开始导入',
+ Close: '关闭',
+ 'Goto Modeling': '跳转到建模',
+ 'Save Modeling': '保存建模',
+ 'Goto Graphs': '跳转到图实例列表',
+ 'Goto Importing': '跳转到数据导入',
+ 'Go back to modify the graph model.': '返回修改图实例。',
+ 'View Schema': '查看 Schema',
+ 'Successfully saved the graph model': '已成功保存图模型',
+ 'Failed to save the graph model': '无法保存图模型',
+ 'Please create the graph model first': '请先创建图模型',
+ 'Sorry, the system detected that there is no available graph model. Please create a graph model before importing data':
+ '对不起,系统检测到没有可用的图模型。导入数据前请先创建图模型.',
+ 'Bulk Import': '批量导入',
+ 'Bulk Import Data': '批量导入数据',
+ 'The system has detected that you have previously uploaded CSV files. You can quickly bulk import data into graphscope':
+ '系统检测到您之前上传过 CSV 文件。您可以快速将数据批量导入 graphscope',
+ 'Click or drag file to this area to upload': '单击或拖动文件到此区域进行上传',
+ 'Clear all files': '清除所有文件',
+ 'Generate graph model': '生成图模型',
+ 'If you already have CSV data, feel free to upload it here, and the system will automatically infer possible graph models for you.':
+ '如果您已经有 CSV 数据,请随时将其上传到此处,系统将自动为您推断可能的图模型。',
+ 'If you already have SQLDDL file, feel free to upload it here, and the system will automatically infer possible graph models for you.':
+ '如果您已经有 SQLDDL 文件,请随时在此处上传,系统将自动为您推断可能的图模型。',
+
+ /** 创建图模型tip */
+ 'A vertex must have a primary key.': '节点必须具有一个主键。',
+ 'A vertex must have at least one property.': '节点必须至少具有一个属性。',
+ 'A edge can only have one property.': '一条边只能有一个属性。',
+
+ 'The current mode is preview only, and does not support clearing the model': '当前模式仅为预览模式,不支持清除模型',
+ 'Clear graph model': '清除模型',
+ 'The current mode is preview only, and does not support creating new vertex':
+ '当前模式仅为预览模式,不支持创建新顶点',
+ 'Create new vertex': '创建节点',
+ 'The current mode is preview only, and does not support opening multi-source modeling':
+ '当前模式仅为预览模式,不支持打开多源建模',
+ 'Expand or collapse multi-source modeling': '展开或折叠多源建模',
+ Configuration: '配置',
+ 'You have successfully bound the data source. Please complete the configuration to start importing data.':
+ '您已成功绑定数据源。请完成配置以开始导入数据。',
+ 'The data loading task has been successfully created. You can view detailed logs in the job center.':
+ '数据加载任务已成功创建。您可以在作业中心查看详细日志。',
+ 'Goto Jobs': '跳转到日志',
+ 'Please select a primate type.': '请补充完数据类型',
+ 'Click or drag file to this area to parse it': '单击或拖动文件到此区域进行解析',
+ 'If you already have CSV data, feel free to parse it here, and the system will automatically infer possible graph models for you.':
+ '如果您已经有 CSV 数据,请随时将其放置到此处,系统将自动为您推断可能的图模型。',
+ 'Data source binding': '数据绑定',
+ 'Shortcut: parse files into a graph model': '快捷方式:将文件解析为图模型',
+ 'Save graph model to svg image': '将图模型保存到 svg 图像',
+ 'Save graph model to JSON config': '将图模型保存到 JSON 配置',
+ 'Parse files into a graph model': '将文件解析为图模型',
+ 'For the definition and description of the schema model, please refer to the ': '关于模式模型的定义和描述,请参阅 ',
+ document: '文档',
+ Vertex: '节点',
+ Edges: '边',
+ 'File type': '文件类型',
+ 'ID field': 'ID 字段',
+ 'Label field': 'label 字段',
+ 'Target field': '终止点字段',
+ 'Source field': '起始点字段',
+ 'Delete this label': '删除该类型',
+ Label: '类型',
+ Source: '起始点',
+ Target: '目标点',
+ 'Data Fields': 'Data Fields',
+ 'Add Property': '添加属性',
+ Properties: '属性',
+ 'Uploading...': '上传中...',
+ Delimiter: '定界符',
+ Type: '类型',
+ 'Header Row': '标题行',
+ 'Import Option': '导入选项',
+ Quoting: '引用',
+ 'Quote char': '分隔符',
+ 'Load data right now': '立即载入数据',
+ 'Click to rebind datasource': '点击解绑,可重新绑定数据源',
+ 'Click to bind datasource': '点击绑定数据源',
+ 'Preview dataloading configuration': '预览周期载入配置',
+ 'Please bind datasource first': '请先绑定数据源',
+ "Click to load all label's data": '点击载入所有 label 的数据',
+ 'Binding all datasource in batch': '批量绑定所有 label 数据源',
+ 'Bind all the datasource, but first, please check if the location for each label is filled in correctly.':
+ '绑定数据源之前,请先检查每个 label 的 location 是否填写正确',
+};
diff --git a/packages/studio-flow-editor/src/app/store/index.tsx b/packages/studio-flow-editor/src/app/store/index.tsx
new file mode 100644
index 000000000..e87c7acc7
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/store/index.tsx
@@ -0,0 +1,66 @@
+import type { NodePositionChange } from 'reactflow';
+import StoreProvider, { useContext as useZustandContext } from '@graphscope/use-zustand';
+import React, { useContext, useMemo } from 'react';
+import { v4 as uuidv4 } from 'uuid';
+import { ISchemaEdge, ISchemaNode } from '../types';
+
+export interface GraphState {
+ displayMode: 'graph' | 'table';
+ nodes: ISchemaNode[];
+ edges: ISchemaEdge[];
+ nodePositionChange: NodePositionChange[];
+ hasLayouted: boolean;
+ elementOptions: {
+ isEditable: boolean;
+ isConnectable: boolean;
+ };
+ theme: {
+ primaryColor: string;
+ };
+ currentId: string;
+ currentType: 'nodes' | 'edges';
+ selectedNodeIds: string[];
+ selectedEdgeIds: string[];
+}
+
+const initialStore: GraphState = {
+ displayMode: 'graph',
+ nodes: [],
+ edges: [],
+ nodePositionChange: [],
+ hasLayouted: false,
+ elementOptions: {
+ isEditable: true,
+ isConnectable: true,
+ },
+ currentId: '',
+ theme: {
+ primaryColor: '#1978FF',
+ },
+ currentType: 'nodes',
+ selectedNodeIds: [],
+ selectedEdgeIds: []
+};
+const GraphInstanceContext = React.createContext(null);
+
+export const GraphProvider = props => {
+ const { children, id } = props;
+ const instanceId = useMemo(() => {
+ return id || `graph-${uuidv4()}`;
+ }, []);
+ return (
+
+
+ {children}
+
+
+ );
+};
+export const useGraphStore = (id?: string) => {
+ const instanceId = useContext(GraphInstanceContext);
+
+ if (!instanceId) {
+ throw new Error('请在 GraphProvider 内使用 useGraphStore');
+ }
+ return useZustandContext(id || instanceId);
+};
diff --git a/packages/studio-importor/src/app/style.tsx b/packages/studio-flow-editor/src/app/style.ts
similarity index 100%
rename from packages/studio-importor/src/app/style.tsx
rename to packages/studio-flow-editor/src/app/style.ts
diff --git a/packages/studio-flow-editor/src/app/types/index.ts b/packages/studio-flow-editor/src/app/types/index.ts
new file mode 100644
index 000000000..485a1eba0
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/types/index.ts
@@ -0,0 +1,64 @@
+import type { Property } from '@graphscope/studio-components';
+import type { Node, Edge } from 'reactflow';
+
+export interface IEdgeData {
+ label: string;
+ /** 禁用:saved / binded / xxxx */
+ disabled?: boolean;
+ /** 是否保存在服务端 */
+ saved?: boolean;
+ properties?: Property[];
+ source_vertex_fields?: Property;
+ target_vertex_fields?: Property;
+ dataFields?: string[];
+ delimiter?: string;
+ datatype?: 'csv' | 'odps';
+ filelocation?: string;
+ _extra?: {
+ type?: string;
+ offset?: string;
+ isLoop: boolean;
+ isRevert?: boolean;
+ isPoly?: boolean;
+ index?: number;
+ count?: number;
+ };
+ [key: string]: any;
+}
+
+export interface INodeData {
+ label: string;
+ disabled?: boolean;
+ properties?: Property[];
+ dataFields?: string[];
+ delimiter?: string;
+ datatype?: 'csv' | 'odps';
+ filelocation?: string;
+ [key: string]: any;
+}
+export type ISchemaNode = Node;
+
+export type ISchemaEdge = Edge & { data: IEdgeData };
+
+export interface ISchemaOptions {
+ nodes: ISchemaNode[];
+ edges: ISchemaEdge[];
+}
+
+export interface Option {
+ label: string;
+ value: string;
+}
+export interface ImportorProps {
+ /** 用于多实例管理的 ID */
+ id?: string;
+ children?: React.ReactNode;
+ nodesDraggable?: boolean;
+ isPreview?: boolean;
+ onNodesChange?: (nodes: ISchemaNode[]) => void;
+ onEdgesChange?: (edges: ISchemaEdge[]) => void;
+ onSelectionChange?: (nodes: ISchemaNode[], edges: ISchemaEdge[]) => void;
+ noDefaultLabel?: boolean;
+ defaultNodes?: ISchemaNode[];
+ defaultEdges?: ISchemaEdge[];
+}
diff --git a/packages/studio-flow-editor/src/app/utils/index.ts b/packages/studio-flow-editor/src/app/utils/index.ts
new file mode 100644
index 000000000..5d1b098df
--- /dev/null
+++ b/packages/studio-flow-editor/src/app/utils/index.ts
@@ -0,0 +1,28 @@
+import { Node } from 'reactflow';
+
+export const getBBox = (nodes: Node[]) => {
+ const xs = nodes.map(node => node.position.x);
+ const ys = nodes.map(node => node.position.y);
+ const minX = Math.min(...xs);
+ const maxX = Math.max(...xs);
+ const minY = Math.min(...ys);
+ const maxY = Math.max(...ys);
+ return {
+ x: minX,
+ y: minY,
+ width: maxX - minX + 100,
+ height: maxY - minY + 100,
+ };
+};
+
+let nodeIndex = 1;
+let edgeIndex = 1;
+export const createNodeLabel = () => `Vertex_${nodeIndex++}`;
+export const createEdgeLabel = () => `Edge_${edgeIndex++}`;
+export const resetIndex = () => {
+ nodeIndex = 1;
+ edgeIndex = 1;
+};
+export const fakeSnapshot = (obj: T): T => {
+ return JSON.parse(JSON.stringify(obj));
+};
\ No newline at end of file
diff --git a/packages/studio-flow-editor/src/index.tsx b/packages/studio-flow-editor/src/index.tsx
new file mode 100644
index 000000000..535ce86be
--- /dev/null
+++ b/packages/studio-flow-editor/src/index.tsx
@@ -0,0 +1,8 @@
+export { default as GraphCanvas } from './app';
+export * from './app/store';
+export { useClearCanvas, useAddNode, useExportSvg } from './app/hooks';
+export {default as AddNode} from './app/button-controller/add-node'
+export {default as ClearCanvas} from './app/button-controller/clear-canvas'
+export {default as ExportSvg} from './app/button-controller/export-image'
+export * from './app/utils';
+export * from './app/types';
diff --git a/packages/studio-flow-editor/tsconfig.json b/packages/studio-flow-editor/tsconfig.json
new file mode 100644
index 000000000..3b081fcbb
--- /dev/null
+++ b/packages/studio-flow-editor/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "allowSyntheticDefaultImports": true,
+ "allowJs": true,
+ "declaration": true,
+ "target": "es2018",
+ "moduleResolution": "node",
+ "emitDeclarationOnly": true, // Only emit type declarations
+ "outDir": "./dist", // Output directory for .d.ts files
+ "rootDir": "./src", // Root directory of your source files
+ "jsx": "react",
+ "resolveJsonModule": true,
+ "strict": true,
+ "noImplicitAny": false,
+ "lib": ["dom", "esnext"],
+ "module": "esnext",
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "isolatedModules": false
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/packages/studio-flow-editor/vite.config.ts b/packages/studio-flow-editor/vite.config.ts
new file mode 100644
index 000000000..af4d5e86d
--- /dev/null
+++ b/packages/studio-flow-editor/vite.config.ts
@@ -0,0 +1,37 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import path from 'path';
+
+export default defineConfig(({ mode }) => {
+ if (mode === 'production') {
+ return {
+ build: {
+ lib: {
+ entry: path.resolve(__dirname, 'src/index.tsx'),
+ name: '@graphscope/studio-graph-editor',
+ fileName: format => `studio-graph-editor.${format}.js`,
+ },
+ outDir: './dist',
+ rollupOptions: {
+ external: ['react', 'react-dom'],
+ },
+ },
+ resolve: {
+ dedupe: ['react', 'react-dom'],
+ },
+ plugins: [react()],
+ };
+ } else {
+ return {
+ root: './',
+ server: {
+ port: 8000,
+ open: '/',
+ },
+ build: {
+ outDir: './dist',
+ },
+ plugins: [react()],
+ };
+ }
+});
diff --git a/packages/studio-importor/.npmrc b/packages/studio-importor/.npmrc
new file mode 100644
index 000000000..1d06f3bac
--- /dev/null
+++ b/packages/studio-importor/.npmrc
@@ -0,0 +1 @@
+registry=http://registry.anpm.alibaba-inc.com
diff --git a/packages/studio-importor/package.json b/packages/studio-importor/package.json
index e234ee075..7a76792dc 100644
--- a/packages/studio-importor/package.json
+++ b/packages/studio-importor/package.json
@@ -27,6 +27,7 @@
"@graphscope/use-zustand": "workspace:*",
"@graphscope/studio-components": "workspace:*",
"@graphscope/studio-server": "workspace:*",
+ "@graphscope/studio-flow-editor": "workspace:*",
"d3-force": "latest",
"dagre": "latest",
"localforage": "latest",
diff --git a/packages/studio-importor/src/app/button-controller/add-node.tsx b/packages/studio-importor/src/app/button-controller/add-node.tsx
deleted file mode 100644
index fdffec58c..000000000
--- a/packages/studio-importor/src/app/button-controller/add-node.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import * as React from 'react';
-import { Button, Tooltip } from 'antd';
-import { v4 as uuidv4 } from 'uuid';
-import { useReactFlow } from 'reactflow';
-import { Icons } from '@graphscope/studio-components';
-import { FormattedMessage } from 'react-intl';
-const AddNodeIcon = Icons.AddNode;
-
-import { createNodeLabel } from '../utils';
-interface IAddNodeProps {
- style?: React.CSSProperties;
-}
-import { useContext } from '@graphscope/use-zustand';
-let addNodeIndex = 0;
-const AddNode: React.FunctionComponent = props => {
- const { style } = props;
- const { setCenter } = useReactFlow();
- const { updateStore, store } = useContext();
- const { elementOptions } = store;
- const disabled = !elementOptions.isConnectable;
- const tooltipText = disabled ? (
-
- ) : (
-
- );
-
- const handleAddVertex = () => {
- updateStore(draft => {
- const label = createNodeLabel();
- const x = addNodeIndex * 200;
- const y = addNodeIndex * 100;
- addNodeIndex++;
- draft.nodes = [
- ...draft.nodes,
- {
- id: uuidv4(),
- position: {
- x,
- y,
- },
- type: 'graph-node',
- data: { label },
- },
- ];
-
- setCenter(x + 100 / 2, y + 100 / 2, { duration: 600, zoom: 1 });
- });
- };
-
- return (
-
- }>
-
- );
-};
-
-export default AddNode;
diff --git a/packages/studio-importor/src/app/button-controller/export-image.tsx b/packages/studio-importor/src/app/button-controller/export-image.tsx
deleted file mode 100644
index e71d490ca..000000000
--- a/packages/studio-importor/src/app/button-controller/export-image.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Button, Tooltip } from 'antd';
-import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
-import { FileImageOutlined } from '@ant-design/icons';
-import { Utils } from '@graphscope/studio-components';
-
-import { toSvg } from 'html-to-image';
-interface ILeftButtonProps {}
-
-const ExportImage: React.FunctionComponent = props => {
- const onClick = async () => {
- // we calculate a transform for the nodes so that all nodes are visible
- // we then overwrite the transform of the `.react-flow__viewport` element
- // with the style option of the html-to-image library
- const viewBox = document.querySelector('.react-flow__viewport') as HTMLDivElement;
- if (viewBox) {
- const dataUrl = await toSvg(viewBox, {});
- Utils.downloadImage(dataUrl, 'model.svg');
- }
- };
-
- return (
- } placement="right">
- } onClick={onClick} />
-
- );
-};
-
-export default ExportImage;
diff --git a/packages/studio-importor/src/app/button-controller/import-and-export-config.tsx b/packages/studio-importor/src/app/button-controller/import-and-export-config.tsx
index ee2ed114c..1d5dab262 100644
--- a/packages/studio-importor/src/app/button-controller/import-and-export-config.tsx
+++ b/packages/studio-importor/src/app/button-controller/import-and-export-config.tsx
@@ -1,7 +1,7 @@
import { Button, Popover, Flex, Typography, Divider, theme, Space } from 'antd';
import * as React from 'react';
import { Utils, Icons } from '@graphscope/studio-components';
-import { useContext } from '@graphscope/use-zustand';
+import { useContext } from '../useContext';
import { transOptionsToSchema } from '../utils/modeling';
import { transformImportOptionsToSchemaMapping } from '../utils/importing';
import yaml from 'js-yaml';
diff --git a/packages/studio-importor/src/app/button-controller/index.tsx b/packages/studio-importor/src/app/button-controller/index.tsx
index c4be6d39b..1e808abbc 100644
--- a/packages/studio-importor/src/app/button-controller/index.tsx
+++ b/packages/studio-importor/src/app/button-controller/index.tsx
@@ -1,12 +1,9 @@
import React from 'react';
import { Divider } from 'antd';
-import { Toolbar, useCustomToken } from '@graphscope/studio-components';
-
+import { Toolbar } from '@graphscope/studio-components';
+import {AddNode,ClearCanvas,ExportSvg} from '@graphscope/studio-flow-editor';
import RightButton from './right-button';
-import ClearCanvas from './clear-canvas';
-import AddNode from './add-node';
-import ExportImage from './export-image';
-import { useContext } from '@graphscope/use-zustand';
+import { useContext } from '../useContext';
import ParseCSV from './parse-csv';
import ImportAndExportConfig from './import-and-export-config';
@@ -28,7 +25,7 @@ const ButtonController: React.FunctionComponent = props
-
+
>
);
diff --git a/packages/studio-importor/src/app/button-controller/left-button.tsx b/packages/studio-importor/src/app/button-controller/left-button.tsx
index 1578bc8b0..022339294 100644
--- a/packages/studio-importor/src/app/button-controller/left-button.tsx
+++ b/packages/studio-importor/src/app/button-controller/left-button.tsx
@@ -1,6 +1,6 @@
import { Button, Tooltip } from 'antd';
import * as React from 'react';
-import { useContext } from '@graphscope/use-zustand';
+import { useContext } from '../useContext';
import { Icons, useSection } from '@graphscope/studio-components';
import { FormattedMessage } from 'react-intl';
interface ILeftButtonProps {}
@@ -8,7 +8,7 @@ const LeftButton: React.FunctionComponent = props => {
const { store } = useContext();
const { elementOptions } = store;
const { toggleLeftSide } = useSection();
- const tooltipText = elementOptions.isEditable ? (
+ const tooltipText = !elementOptions.isEditable ? (
) : (
@@ -20,7 +20,7 @@ const LeftButton: React.FunctionComponent = props => {
}
// onClick={() => {
// updateStore(draft => {
diff --git a/packages/studio-importor/src/app/button-controller/parse-csv.tsx b/packages/studio-importor/src/app/button-controller/parse-csv.tsx
index b100e491a..036805a05 100644
--- a/packages/studio-importor/src/app/button-controller/parse-csv.tsx
+++ b/packages/studio-importor/src/app/button-controller/parse-csv.tsx
@@ -7,7 +7,7 @@ import { SegmentedTabs, Icons, useStudioProvier } from '@graphscope/studio-compo
import type { SegmentedTabsProps } from '@graphscope/studio-components';
import { FormattedMessage } from 'react-intl';
-import { useContext } from '@graphscope/use-zustand';
+import { useContext } from '../useContext';
interface IImportSchemaProps {
style?: React.CSSProperties;
}
@@ -25,9 +25,9 @@ const ParseCSVButton: React.FunctionComponent = props => {
/** svg pathFill */
let pathFill = () => {
if (!isLight) {
- return elementOptions.isEditable ? '#585858' : '#fff';
+ return !elementOptions.isEditable ? '#585858' : '#fff';
} else {
- return elementOptions.isEditable ? '#ddd' : '#000';
+ return !elementOptions.isEditable ? '#ddd' : '#000';
}
};
const handleClick = () => {
@@ -48,7 +48,7 @@ const ParseCSVButton: React.FunctionComponent = props => {
} placement="left">
}
diff --git a/packages/studio-importor/src/app/graph-canvas/CustomControls.tsx b/packages/studio-importor/src/app/graph-canvas/CustomControls.tsx
deleted file mode 100644
index deab55804..000000000
--- a/packages/studio-importor/src/app/graph-canvas/CustomControls.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { Button, Flex, Divider } from 'antd';
-import { PlusOutlined, MinusOutlined, ExpandOutlined } from '@ant-design/icons';
-import { useReactFlow, Panel } from 'reactflow';
-import { Icons, useStudioProvier } from '@graphscope/studio-components';
-
-interface ICustomControlsProps {
- isLocked: boolean;
- handleLocked: (val: boolean) => void;
-}
-const CustomControls: React.FunctionComponent = props => {
- const { isLocked, handleLocked } = props;
- const { zoomIn, zoomOut, fitView } = useReactFlow();
- const { isLight } = useStudioProvier();
- // svg path fill
- const color = !isLight ? '#FFF' : '#000';
- // lock or unlock
- const Icon = isLocked ? : ;
- const background = !isLight ? '#1d1d1d' : '#fff';
- return (
- <>
-
-
- }
- onClick={() => zoomIn({ duration: 100 })}
- />
-
- }
- onClick={() => zoomOut({ duration: 100 })}
- />
-
- }
- onClick={() => fitView()}
- />
-
- {
- handleLocked(!isLocked);
- }}
- />
-
-
- >
- );
-};
-
-export default CustomControls;
diff --git a/packages/studio-importor/src/app/graph-canvas/index.tsx b/packages/studio-importor/src/app/graph-canvas/index.tsx
deleted file mode 100644
index 4ae6ebe73..000000000
--- a/packages/studio-importor/src/app/graph-canvas/index.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import React, { useState } from 'react';
-import { ReactFlow, Controls, Background, MiniMap, applyNodeChanges } from 'reactflow';
-import { EmptyCanvas, useStudioProvier } from '@graphscope/studio-components';
-import { nodeTypes } from '../elements/node-types';
-import { edgeTypes } from '../elements/edge-types';
-import ConnectionLine from '../elements/connection-line';
-import ArrowMarker from '../elements/arrow-marker';
-import { PlayCircleOutlined } from '@ant-design/icons';
-import { theme } from 'antd';
-
-import useInteractive from './useInteractive';
-import { FormattedMessage } from 'react-intl';
-// import CustomControls from './CustomControls';
-
-interface IGraphEditorProps {}
-
-const fakeSnapshot = obj => {
- return JSON.parse(JSON.stringify(obj));
-};
-
-const GraphEditor: React.FunctionComponent = props => {
- const { store, onDoubleClick, onEdgesChange, onNodesChange, onConnectStart, onConnectEnd, onReactFlowInit } =
- useInteractive();
- const { nodes, edges, collapsed, appMode, nodePositionChange } = store;
-
- const [state, updateState] = useState({
- isLocked: false,
- });
-
- const { token } = theme.useToken();
-
- const isEmpty = nodes.length === 0;
-
- const description = (
- ,
- }}
- />
- );
- const IS_PURE = appMode === 'PURE';
- const _nodes = nodePositionChange.length === 0 ? nodes : applyNodeChanges(nodePositionChange, nodes);
-
- return (
-
-
-
-
- {!IS_PURE && }
- {isEmpty && }
- {!IS_PURE && }
-
-
-
- );
-};
-
-export default GraphEditor;
diff --git a/packages/studio-importor/src/app/graph-canvas/useInteractive.ts b/packages/studio-importor/src/app/graph-canvas/useInteractive.ts
deleted file mode 100644
index ab39d1b86..000000000
--- a/packages/studio-importor/src/app/graph-canvas/useInteractive.ts
+++ /dev/null
@@ -1,194 +0,0 @@
-import { useCallback, useEffect, useRef } from 'react';
-import { useReactFlow, applyEdgeChanges, applyNodeChanges, getNodesBounds } from 'reactflow';
-import { v4 as uuidv4 } from 'uuid';
-import { createStaticForceLayout } from '../elements/forceLayout';
-import { fakeSnapshot } from '../utils/index';
-const deepclone = obj => {
- return JSON.parse(JSON.stringify(obj));
-};
-
-import type { NodeChange, EdgeChange, NodePositionChange } from 'reactflow';
-import { useContext } from '@graphscope/use-zustand';
-import { transformEdges } from '../elements';
-
-import { getBBox, createEdgeLabel, createNodeLabel } from '../utils';
-
-const useInteractive: any = () => {
- const { store, updateStore } = useContext();
- const { screenToFlowPosition, fitBounds, fitView } = useReactFlow();
- const { displayMode, nodes, edges, hasLayouted, elementOptions } = store;
- const connectingNodeId = useRef(null);
- const timerRef = useRef(null);
- const tempRef = useRef([]);
-
- const onConnectStart = useCallback((_, { nodeId }) => {
- connectingNodeId.current = nodeId;
- if (!elementOptions.isConnectable) return;
- }, []);
-
- const onConnectEnd = useCallback(
- event => {
- if (!connectingNodeId.current) return;
- if (!elementOptions.isConnectable) return;
-
- const targetIsPane = event.target.classList.contains('react-flow__pane');
-
- // 空白画板需要添加节点
- if (targetIsPane) {
- // we need to remove the wrapper bounds, in order to get the correct position
- const nodeId = uuidv4();
- const edgeId = uuidv4();
- /** 这里计算的 position 是 handle 的位置,对GraphNode 而言,就是圆心的坐标 */
- const newPosition = screenToFlowPosition({
- x: event.clientX,
- y: event.clientY,
- });
- const newNode = {
- id: nodeId,
- position: {
- x: newPosition.x - 50,
- y: newPosition.y - 50,
- },
- type: 'graph-node',
- data: { label: createNodeLabel() },
- };
-
- updateStore(draft => {
- draft.nodes = [...draft.nodes, newNode];
- draft.edges = [
- ...draft.edges,
- {
- id: edgeId,
- //@ts-ignore
- source: connectingNodeId.current,
- target: nodeId,
- type: 'graph-edge',
- data: {
- label: createEdgeLabel(),
- },
- },
- ];
- });
- } else {
- const { nodeid } = event.target.dataset;
-
- const edgeId = uuidv4();
- const edgeLabel = createEdgeLabel();
-
- updateStore(draft => {
- // 可能存在多边,所以需要走一遍 transform 函数
- draft.edges = transformEdges(
- [
- ...draft.edges,
- {
- id: edgeId,
- data: {
- label: edgeLabel,
- },
- source: connectingNodeId.current || '',
- target: nodeid,
- },
- ],
- displayMode,
- );
- });
- }
- },
- [screenToFlowPosition],
- );
-
- const onNodesChange = (changes: NodeChange[]) => {
- const { type } = changes[0];
-
- if (elementOptions.isConnectable && type !== 'position') {
- updateStore(draft => {
- draft.nodes = applyNodeChanges(changes, deepclone(draft.nodes));
- });
- }
- if (type === 'position') {
- if (changes[0].dragging) {
- tempRef.current = changes;
- updateStore(draft => {
- draft.nodePositionChange = changes as NodePositionChange[];
- });
- } else {
- // 需要在拖拽结束后,更新一次位置到 nodes
- updateStore(draft => {
- draft.nodes = applyNodeChanges(tempRef.current, draft.nodes);
- });
- }
- }
- };
- const onEdgesChange = (changes: EdgeChange[]) => {
- if (elementOptions.isConnectable) {
- updateStore(draft => {
- //@ts-ignore
- draft.edges = applyEdgeChanges(changes, deepclone(draft.edges));
- });
- }
- };
-
- const onDoubleClick = () => {
- //@ts-ignore
- const bbox = getBBox(nodes);
-
- fitBounds(bbox, { duration: 600 });
- };
-
- useEffect(() => {
- if (nodes.length > 0) {
- // 交互
- // if (hasLayouted) {
- // onDoubleClick();
- // }
- // 布局
- if (!hasLayouted) {
- const graph = createStaticForceLayout(fakeSnapshot(nodes), fakeSnapshot(edges));
- if (timerRef.current) {
- clearTimeout(timerRef.current);
- }
- const timerId = setTimeout(() => {
- const bbox = getBBox(graph.nodes);
- fitBounds(bbox, { duration: 300 });
- }, 300);
- timerRef.current = timerId;
- updateStore(draft => {
- draft.hasLayouted = true;
- draft.nodes = graph.nodes;
- draft.edges = graph.edges;
- });
- }
- }
- }, [nodes, edges, hasLayouted]);
- useEffect(() => {
- /** 把marker 放到 reactflow 内部,目的是为了导出的时候 dom 不丢失 */
- const marker = document.getElementById('arrow-marker-svg');
- const view = document.querySelector('.react-flow__viewport');
- if (marker && view) {
- view.appendChild(marker);
- }
- }, []);
-
- const onReactFlowInit = reactFlowInstance => {
- if (reactFlowInstance) {
- setTimeout(()=>{
- const allNodes = reactFlowInstance.toObject().nodes;
- const bbox = getNodesBounds(allNodes);
- fitBounds(bbox, { duration: 600 });
- },300)
- }
- };
-
- return {
- store,
- updateStore,
- onDoubleClick,
- onEdgesChange,
- onNodesChange,
- onConnectStart,
- onConnectEnd,
- onReactFlowInit,
- };
-};
-
-export default useInteractive;
diff --git a/packages/studio-importor/src/app/import-schema/import-from-csv/index.tsx b/packages/studio-importor/src/app/import-schema/import-from-csv/index.tsx
index 3774dbb0c..7a85c6084 100644
--- a/packages/studio-importor/src/app/import-schema/import-from-csv/index.tsx
+++ b/packages/studio-importor/src/app/import-schema/import-from-csv/index.tsx
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../useContext'
import { Utils, ImportFiles, ParsedFile } from '@graphscope/studio-components';
import { parseSchemaByFiles } from './web-worker';
import { transform } from './transform';
diff --git a/packages/studio-importor/src/app/import-schema/import-from-sql/index.tsx b/packages/studio-importor/src/app/import-schema/import-from-sql/index.tsx
index 45c125865..0f3a37fcb 100644
--- a/packages/studio-importor/src/app/import-schema/import-from-sql/index.tsx
+++ b/packages/studio-importor/src/app/import-schema/import-from-sql/index.tsx
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { Button } from 'antd';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../useContext'
import { Utils, ImportFiles } from '@graphscope/studio-components';
diff --git a/packages/studio-importor/src/app/import-schema/import-from-yaml/index.tsx b/packages/studio-importor/src/app/import-schema/import-from-yaml/index.tsx
index 2a76df2df..319f84409 100644
--- a/packages/studio-importor/src/app/import-schema/import-from-yaml/index.tsx
+++ b/packages/studio-importor/src/app/import-schema/import-from-yaml/index.tsx
@@ -6,7 +6,7 @@ import yaml from 'js-yaml';
import { FormattedMessage } from 'react-intl';
import { transSchemaToOptions, appendData } from '../../utils/modeling';
import { transMappingSchemaToOptions } from '../../utils/importing';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../useContext'
import { transformEdges, transformGraphNodes } from '../../elements';
import { Utils, Icons } from '@graphscope/studio-components';
diff --git a/packages/studio-importor/src/app/index.tsx b/packages/studio-importor/src/app/index.tsx
index cf9d825bb..83dccb1cc 100644
--- a/packages/studio-importor/src/app/index.tsx
+++ b/packages/studio-importor/src/app/index.tsx
@@ -1,17 +1,16 @@
-import React, { useEffect, useMemo } from 'react';
-import GraphCanvas from './graph-canvas';
+import React, { useEffect } from 'react';
import PropertiesEditor from './properties-editor';
-import { ReactFlowProvider } from 'reactflow';
-import { Section, StudioProvier, GlobalSpin, useDynamicStyle } from '@graphscope/studio-components';
-import cssStyles from './style';
+import { Section, StudioProvier, GlobalSpin, EmptyCanvas } from '@graphscope/studio-components';
import { transformGraphNodes, transformEdges } from './elements/index';
-
+import { GraphCanvas, GraphProvider } from '@graphscope/studio-flow-editor';
+import { PlayCircleOutlined } from '@ant-design/icons';
import ButtonController from './button-controller';
import type { ISchemaOptions, ImportorProps } from './typing';
-import { initialStore } from './useContext';
import locales from '../locales';
-import StoreProvider, { useContext } from '@graphscope/use-zustand';
-
+import { ImportorProvider, useContext } from './useContext';
+import { FormattedMessage } from 'react-intl';
+import { Background, MiniMap } from 'reactflow';
+import { theme as antTheme } from 'antd';
const ImportApp: React.FunctionComponent = props => {
const {
appMode,
@@ -43,15 +42,14 @@ const ImportApp: React.FunctionComponent = props => {
onCreateLabel,
onDeleteLabel,
style,
- leftSide,
rightSide,
refreshIndex = 1,
} = props;
const { store, updateStore } = useContext();
- const { isReady, displayMode } = store;
-
- useDynamicStyle(cssStyles, 'graphscope-importor');
-
+ const { isReady, nodes, displayMode } = store;
+ const { token } = antTheme.useToken();
+ const IS_PURE = appMode === 'PURE';
+ const isEmpty = nodes.length === 0;
useEffect(() => {
(async () => {
let schema: ISchemaOptions = { nodes: [], edges: [] };
@@ -66,9 +64,19 @@ const ImportApp: React.FunctionComponent = props => {
// nodes: transformGraphNodes(schema.nodes, store.displayMode), //会报错!!!!
edges: transformEdges(schema.edges, displayMode),
};
-
const { nodes, edges } = schemaOptions || { nodes: [], edges: [] };
- const isEmpty = nodes.length === 0;
+ console.log('schemaOptions edges::: ', edges);
+ console.log('schemaOptions nodes::: ', nodes);
+ const schemaEmpty = nodes.length === 0;
+ const canConnect = () => {
+ if (IS_PURE) {
+ return false;
+ }
+ if (GS_ENGINE_TYPE === 'groot' && appMode === 'DATA_MODELING') {
+ return true;
+ }
+ return schemaEmpty;
+ };
updateStore(draft => {
draft.isReady = true;
@@ -76,18 +84,24 @@ const ImportApp: React.FunctionComponent = props => {
draft.edges = edges;
draft.appMode = appMode;
draft.elementOptions = {
- isEditable: !isEmpty, // 操作按钮: nodes没有值,导入,删除,添加都可以操作;接口返回有值,只能操作添加按钮。
- isConnectable: GS_ENGINE_TYPE === 'groot' && appMode === 'DATA_MODELING' ? true : isEmpty, // 初始状态,接口获取画布有 Schema 数据的时候,不可连线;groot时候一直可以连线
+ isEditable: schemaEmpty, // 操作按钮: nodes没有值,导入,删除,添加都可以操作;接口返回有值,只能操作添加按钮。
+ isConnectable: canConnect(), // 初始状态,接口获取画布有 Schema 数据的时候,不可连线;groot时候一直可以连线
};
- draft.currentId = isEmpty ? '' : nodes[0].id;
+ draft.currentId = schemaEmpty ? '' : nodes[0].id;
draft.currentType = 'nodes';
draft.isSaveFiles = isSaveFiles;
draft.hasLayouted = false;
});
})();
}, [refreshIndex]);
- const IS_PURE = appMode === 'PURE';
-
+ const description = (
+ ,
+ }}
+ />
+ );
return (
= props => {
splitBorder
>
{isReady ? (
-
+
{!IS_PURE && }
-
+ {!IS_PURE && }
+ {!IS_PURE && }
+ {isEmpty && }
{children}
-
+
) : (
)}
@@ -126,8 +142,8 @@ const ImportApp: React.FunctionComponent = props => {
export default props => {
return (
-
+
-
+
);
};
diff --git a/packages/studio-importor/src/app/mode-switch/index.tsx b/packages/studio-importor/src/app/mode-switch/index.tsx
index 23ef079f2..f7841ed47 100644
--- a/packages/studio-importor/src/app/mode-switch/index.tsx
+++ b/packages/studio-importor/src/app/mode-switch/index.tsx
@@ -1,6 +1,6 @@
import { Button, Segmented } from 'antd';
import * as React from 'react';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../useContext'
import { useReactFlow } from 'reactflow';
import { ApartmentOutlined, BranchesOutlined } from '@ant-design/icons';
diff --git a/packages/studio-importor/src/app/properties-editor/index.tsx b/packages/studio-importor/src/app/properties-editor/index.tsx
index 76fb5493f..adf706844 100644
--- a/packages/studio-importor/src/app/properties-editor/index.tsx
+++ b/packages/studio-importor/src/app/properties-editor/index.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useRef } from 'react';
import { Collapse, Space } from 'antd';
import PropertiesSchema from './properties-schema';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../useContext';
import { SegmentedTabs, Utils, EngineFeature } from '@graphscope/studio-components';
import { CaretRightOutlined } from '@ant-design/icons';
import type { ImportorProps } from '../typing';
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/bind/index.tsx b/packages/studio-importor/src/app/properties-editor/properties-schema/bind/index.tsx
index 5bfe9b392..617d62218 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/bind/index.tsx
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/bind/index.tsx
@@ -1,7 +1,6 @@
import * as React from 'react';
import { Button, Tooltip } from 'antd';
-import { Icons, Utils } from '@graphscope/studio-components';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../../useContext'
import { LinkOutlined, CheckOutlined } from '@ant-design/icons';
import type { ImportorProps, ISchemaNode, ISchemaEdge } from '../../../typing';
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/delete/index.tsx b/packages/studio-importor/src/app/properties-editor/properties-schema/delete/index.tsx
index 7a5d93605..d015f4617 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/delete/index.tsx
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/delete/index.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Button, Tooltip } from 'antd';
import { Icons } from '@graphscope/studio-components';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../../useContext'
import type { ImportorProps, ISchemaNode, ISchemaEdge } from '../../../typing';
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/load-now/index.tsx b/packages/studio-importor/src/app/properties-editor/properties-schema/load-now/index.tsx
index 42cc85260..de29bc591 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/load-now/index.tsx
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/load-now/index.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Button, Tooltip, notification } from 'antd';
import { useHistory, Utils } from '@graphscope/studio-components';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../../useContext'
import { LinkOutlined, CloudUploadOutlined } from '@ant-design/icons';
import { bindDatasourceInBatch, submitDataloadingJob } from './services';
import type { ImportorProps, ISchemaNode, ISchemaEdge } from '../../../typing';
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/load-schedule/index.tsx b/packages/studio-importor/src/app/properties-editor/properties-schema/load-schedule/index.tsx
index dea254421..cccc22710 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/load-schedule/index.tsx
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/load-schedule/index.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Button, Tooltip, Popover, Typography, Drawer } from 'antd';
import { Icons } from '@graphscope/studio-components';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../../useContext'
import { LinkOutlined, ScheduleOutlined } from '@ant-design/icons';
import type { ImportorProps, ISchemaNode, ISchemaEdge } from '../../../typing';
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/location/useChange.tsx b/packages/studio-importor/src/app/properties-editor/properties-schema/location/useChange.tsx
index 7b42f8dce..220c78cf9 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/location/useChange.tsx
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/location/useChange.tsx
@@ -1,4 +1,4 @@
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../../useContext';
import { ISchemaEdge, ISchemaNode } from '../../../typing';
import { Utils } from '@graphscope/studio-components';
export const useChange = ({ type, id }) => {
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/odps/useChange.tsx b/packages/studio-importor/src/app/properties-editor/properties-schema/odps/useChange.tsx
index 407ba745f..23eb057f1 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/odps/useChange.tsx
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/odps/useChange.tsx
@@ -1,4 +1,4 @@
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../../useContext'
import { ISchemaEdge, ISchemaNode } from '../../../typing';
import { Utils } from '@graphscope/studio-components';
export const useChange = ({ type, id }) => {
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/save/index.tsx b/packages/studio-importor/src/app/properties-editor/properties-schema/save/index.tsx
index 75aba0972..a8d982897 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/save/index.tsx
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/save/index.tsx
@@ -1,7 +1,6 @@
import * as React from 'react';
import { Button, Tooltip } from 'antd';
-
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../../useContext'
import type { ImportorProps, ISchemaEdge, ISchemaNode } from '../../../typing';
import { SaveOutlined } from '@ant-design/icons';
import { validateProperties } from '../validate-info';
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/source-target.tsx b/packages/studio-importor/src/app/properties-editor/properties-schema/source-target.tsx
index 00cc3de8b..27a2c4be4 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/source-target.tsx
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/source-target.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { Typography, Input, Flex } from 'antd';
import { MappingFields } from '@graphscope/studio-components';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../useContext'
import useModel from './useModel';
import { FormattedMessage } from 'react-intl';
diff --git a/packages/studio-importor/src/app/properties-editor/properties-schema/useModel.ts b/packages/studio-importor/src/app/properties-editor/properties-schema/useModel.ts
index 79ca1c8e0..d676a0840 100644
--- a/packages/studio-importor/src/app/properties-editor/properties-schema/useModel.ts
+++ b/packages/studio-importor/src/app/properties-editor/properties-schema/useModel.ts
@@ -1,5 +1,5 @@
import React from 'react';
-import { useContext } from '@graphscope/use-zustand';
+import {useContext} from '../../useContext'
import { Property } from '@graphscope/studio-components';
interface IuseModel {
type: string;
diff --git a/packages/studio-importor/src/app/useContext.ts b/packages/studio-importor/src/app/useContext.ts
deleted file mode 100644
index 1c16c79b7..000000000
--- a/packages/studio-importor/src/app/useContext.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import type { NodeChange, NodePositionChange } from 'reactflow';
-
-import { ISchemaNode, ISchemaEdge } from './typing';
-export type IStore = {
- /** APP类型 */
- appMode: 'DATA_MODELING' | 'DATA_IMPORTING' | 'PURE';
- /**不可编辑状态 */
- disabled: boolean;
- isReady: boolean;
- currentType: 'nodes' | 'edges';
- currentId: string;
- nodes: ISchemaNode[];
- edges: ISchemaEdge[];
- source: {
- nodes: ISchemaNode[];
- edges: ISchemaEdge[];
- };
- nodePositionChange: NodePositionChange[];
-
- displayMode: 'graph' | 'table';
- graphPosition: Record;
- tablePosition: Record;
- theme: {
- primaryColor: string;
- };
- collapsed: {
- left: boolean;
- right: boolean;
- };
- hasLayouted: boolean;
- elementOptions: {
- /** 是否可以点击,包含点和边 */
- isEditable: boolean;
- /** 是否可以编辑标签,包括节点和边 */
- isConnectable: boolean;
- };
- /** 是否保存原始上传的文件 */
- isSaveFiles?: boolean;
- csvFiles: File[];
-};
-
-export const initialStore: IStore = {
- /** APP类型 */
- appMode: 'DATA_MODELING',
- /**不可编辑状态 */
- disabled: false,
- nodes: [],
- edges: [],
- source: {
- nodes: [],
- edges: [],
- },
- nodePositionChange: [],
- isReady: false,
- displayMode: 'graph',
- graphPosition: {},
- tablePosition: {},
- currentType: 'nodes',
- currentId: '',
- theme: {
- primaryColor: '#1978FF',
- },
- collapsed: {
- left: true,
- right: true,
- },
- hasLayouted: false,
- elementOptions: {
- isEditable: true,
- isConnectable: true,
- },
- csvFiles: [],
- isSaveFiles: true,
-};
diff --git a/packages/studio-importor/src/app/useContext.tsx b/packages/studio-importor/src/app/useContext.tsx
new file mode 100644
index 000000000..03dba91c1
--- /dev/null
+++ b/packages/studio-importor/src/app/useContext.tsx
@@ -0,0 +1,115 @@
+import React, { useMemo, useContext as useReactContext } from 'react';
+import { v4 as uuidv4 } from 'uuid';
+import { ISchemaNode, ISchemaEdge } from './typing';
+import StoreProvider, { useContext as useZustandContext } from '@graphscope/use-zustand';
+import { useGraphStore, GraphProvider } from '@graphscope/studio-flow-editor';
+import type { GraphState } from '@graphscope/studio-flow-editor';
+
+export type IStore = {
+ /** APP类型 */
+ appMode: 'DATA_MODELING' | 'DATA_IMPORTING' | 'PURE';
+ /**不可编辑状态 */
+ disabled: boolean;
+ isReady: boolean;
+ source: {
+ nodes: ISchemaNode[];
+ edges: ISchemaEdge[];
+ };
+ graphPosition: Record;
+ tablePosition: Record;
+ collapsed: {
+ left: boolean;
+ right: boolean;
+ };
+ /** 是否保存原始上传的文件 */
+ isSaveFiles?: boolean;
+ csvFiles: File[];
+};
+
+export const initialStore: IStore = {
+ /** APP类型 */
+ appMode: 'DATA_MODELING',
+ /**不可编辑状态 */
+ disabled: false,
+ source: {
+ nodes: [],
+ edges: [],
+ },
+ isReady: false,
+ graphPosition: {},
+ tablePosition: {},
+ collapsed: {
+ left: true,
+ right: true,
+ },
+ csvFiles: [],
+ isSaveFiles: true,
+};
+const ImportorInstanceContext = React.createContext(null);
+
+export const ImportorProvider: React.FC<{ children: React.ReactNode; id?: string }> = props => {
+ const { children, id } = props;
+ const instanceId = useMemo(() => {
+ return id || `importor-${uuidv4()}`;
+ }, []);
+ console.log('ImportorProvider instanceId::: ', instanceId);
+ return (
+
+
+
+ {children}
+
+
+
+ );
+};
+
+export const useContext: any = (id?: string) => {
+ const instanceId = useReactContext(ImportorInstanceContext);
+ if (!instanceId) {
+ throw new Error('请在 GraphProvider 内使用 useImportorStore');
+ }
+ const { store: importorStore, updateStore: updateImportorStore } = useZustandContext(id);
+ // 获取 Graph 的 store
+ const { store: graphStore, updateStore: updateGraphStore } = useGraphStore();
+
+ // 统一的更新方法
+ const updateStore = (fn: (draft: IStore | GraphState) => void) => {
+ // 更新 Importor store
+ updateImportorStore((draft: IStore) => {
+ const importorKeys = Object.keys(initialStore);
+ fn(draft);
+ // 删除不属于 Importor store 的属性
+ Object.keys(draft).forEach(key => {
+ if (!importorKeys.includes(key) && key !== 'updateStore') {
+ delete draft[key];
+ }
+ });
+ });
+
+ // 更新 Graph store
+ updateGraphStore((draft: GraphState) => {
+ const graphKeys = Object.keys(graphStore) as (keyof GraphState)[];
+ fn(draft);
+ // 删除不属于 Graph store 的属性
+ Object.keys(draft).forEach(key => {
+ if (!graphKeys.includes(key as keyof GraphState) && key !== 'updateStore') {
+ delete draft[key];
+ }
+ });
+ });
+ };
+
+ return {
+ // 合并两个 store 的数据
+ store: {
+ ...importorStore,
+ ...graphStore,
+ },
+ // 提供统一的更新方法
+ updateStore,
+ // 也提供单独更新某个 store 的方法
+ updateImportorStore,
+ updateGraphStore,
+ };
+};
diff --git a/packages/studio-importor/src/index.tsx b/packages/studio-importor/src/index.tsx
index 526c6d493..9e5bb175e 100644
--- a/packages/studio-importor/src/index.tsx
+++ b/packages/studio-importor/src/index.tsx
@@ -1,6 +1,6 @@
import ImportApp from './app';
-export { useContext } from '@graphscope/use-zustand';
+export {useContext} from './app/useContext'
export { validateProperties } from './app/properties-editor/properties-schema/validate-info';
export type { ISchemaEdge, ISchemaNode, ISchemaOptions } from './app/typing';
export type { Property } from '@graphscope/studio-components';
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 69d17a14b..178d4a98e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -74,7 +74,7 @@ importers:
version: 2.27.10
'@types/node':
specifier: latest
- version: 22.13.14
+ version: 22.15.2
'@types/react':
specifier: 18.2.0
version: 18.2.0
@@ -89,13 +89,13 @@ importers:
version: 6.11.0(webpack@5.96.1)
dumi:
specifier: ^2.2.17
- version: 2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.13.14)(@types/react@18.2.0)(eslint@9.23.0)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
+ version: 2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.15.2)(@types/react@18.2.0)(eslint@9.25.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
dumi-theme-antd:
specifier: ^0.4.2
- version: 0.4.2(@babel/core@7.26.0)(@types/react@18.2.0)(antd@5.22.2(date-fns@2.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(date-fns@2.30.0)(dumi@2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.13.14)(@types/react@18.2.0)(eslint@9.23.0)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1))(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0)
+ version: 0.4.2(@babel/core@7.26.0)(@types/react@18.2.0)(antd@5.22.2(date-fns@2.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(date-fns@2.30.0)(dumi@2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.15.2)(@types/react@18.2.0)(eslint@9.25.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1))(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0)
father:
specifier: ^4.4.1
- version: 4.5.1(@babel/core@7.26.0)(@types/node@22.13.14)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
+ version: 4.5.1(@babel/core@7.26.0)(@types/node@22.15.2)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
file-loader:
specifier: ^6.2.0
version: 6.2.0(webpack@5.96.1)
@@ -256,7 +256,7 @@ importers:
devDependencies:
'@vitejs/plugin-react':
specifier: ^4.2.1
- version: 4.3.3(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 4.3.3(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
jszip:
specifier: ^3.10.1
version: 3.10.1
@@ -265,13 +265,13 @@ importers:
version: 2.8.1
vite:
specifier: ^5.4.16
- version: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ version: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
vite-plugin-top-level-await:
specifier: ^1.4.4
- version: 1.4.4(@swc/helpers@0.5.15)(rollup@4.38.0)(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 1.4.4(@swc/helpers@0.5.15)(rollup@4.38.0)(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
vite-plugin-wasm:
specifier: ^3.3.0
- version: 3.3.0(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 3.3.0(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
examples/mcp-portal:
dependencies:
@@ -339,6 +339,9 @@ importers:
'@graphscope/studio-components':
specifier: workspace:*
version: link:../studio-components
+ '@graphscope/studio-flow-editor':
+ specifier: workspace:*
+ version: link:../studio-flow-editor
'@graphscope/studio-graph-editor':
specifier: workspace:*
version: link:../studio-graph-editor
@@ -354,6 +357,9 @@ importers:
react-dom:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
+ reactflow:
+ specifier: ^11.11.4
+ version: 11.11.4(@types/react@18.2.0)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
rxjs:
specifier: ^7.8.1
version: 7.8.1
@@ -378,19 +384,19 @@ importers:
version: 9.0.8
'@vitejs/plugin-react':
specifier: ^4.3.1
- version: 4.3.3(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 4.3.3(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
father:
specifier: ^4.4.5
- version: 4.5.1(@babel/core@7.26.0)(@types/node@22.13.14)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
+ version: 4.5.1(@babel/core@7.26.0)(@types/node@22.15.2)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
typescript:
specifier: ^5.5.4
version: 5.6.3
vite:
specifier: ^5.4.16
- version: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ version: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
vitest:
specifier: ^2.0.5
- version: 2.1.5(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ version: 2.1.5(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
packages/studio-driver:
dependencies:
@@ -402,7 +408,7 @@ importers:
version: 0.7.0
kuzu-wasm:
specifier: latest
- version: 0.8.2
+ version: 0.9.0
neo4j-driver:
specifier: ^5.12.0
version: 5.26.0
@@ -451,16 +457,80 @@ importers:
devDependencies:
'@vitejs/plugin-react':
specifier: ^4.2.1
- version: 4.3.3(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 4.3.3(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
vite:
specifier: ^5.4.16
- version: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ version: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
vite-plugin-top-level-await:
specifier: ^1.4.4
- version: 1.4.4(@swc/helpers@0.5.15)(rollup@4.38.0)(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 1.4.4(@swc/helpers@0.5.15)(rollup@4.38.0)(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
vite-plugin-wasm:
specifier: ^3.3.0
- version: 3.3.0(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 3.3.0(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+
+ packages/studio-flow-editor:
+ dependencies:
+ '@graphscope/studio-components':
+ specifier: workspace:*
+ version: link:../studio-components
+ '@graphscope/use-zustand':
+ specifier: workspace:*
+ version: link:../use-zustand
+ antd:
+ specifier: ^5.22.2
+ version: 5.22.2(date-fns@2.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ d3-force:
+ specifier: latest
+ version: 3.0.0
+ dagre:
+ specifier: latest
+ version: 0.8.5
+ html-to-image:
+ specifier: ^1.11.11
+ version: 1.11.11
+ immer:
+ specifier: ^10.1.1
+ version: 10.1.1
+ lodash:
+ specifier: ^4.17.21
+ version: 4.17.21
+ react:
+ specifier: 18.2.0
+ version: 18.2.0
+ react-dom:
+ specifier: 18.2.0
+ version: 18.2.0(react@18.2.0)
+ react-intl:
+ specifier: ^6.6.1
+ version: 6.8.9(react@18.2.0)(typescript@5.8.2)
+ reactflow:
+ specifier: latest
+ version: 11.11.4(@types/react@18.2.0)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ rxjs:
+ specifier: ^7.8.1
+ version: 7.8.1
+ uuid:
+ specifier: ^9.0.1
+ version: 9.0.1
+ valtio:
+ specifier: 2.0.0-rc.1
+ version: 2.0.0-rc.1(@types/react@18.2.0)(react@18.2.0)
+ zustand:
+ specifier: ^4.5.5
+ version: 4.5.5(@types/react@18.2.0)(immer@10.1.1)(react@18.2.0)
+ devDependencies:
+ '@types/d3-force':
+ specifier: latest
+ version: 3.0.10
+ '@types/lodash':
+ specifier: ^4.14.202
+ version: 4.17.13
+ '@vitejs/plugin-react':
+ specifier: ^4.2.1
+ version: 4.3.4(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ vite:
+ specifier: ^5.4.16
+ version: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
packages/studio-graph:
dependencies:
@@ -536,10 +606,10 @@ importers:
version: 3.1.7
'@vitejs/plugin-react':
specifier: ^4.3.4
- version: 4.3.4(vite@6.2.4(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1))
+ version: 4.3.4(vite@6.2.4(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1))
vite:
specifier: ^6.0.3
- version: 6.2.4(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1)
+ version: 6.2.4(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1)
packages/studio-graph-editor:
dependencies:
@@ -594,16 +664,19 @@ importers:
version: 4.17.13
'@vitejs/plugin-react':
specifier: ^4.2.1
- version: 4.3.3(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 4.3.3(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
vite:
specifier: ^5.4.16
- version: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ version: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
packages/studio-importor:
dependencies:
'@graphscope/studio-components':
specifier: workspace:*
version: link:../studio-components
+ '@graphscope/studio-flow-editor':
+ specifier: workspace:*
+ version: link:../studio-flow-editor
'@graphscope/studio-server':
specifier: workspace:*
version: link:../studio-server
@@ -704,7 +777,7 @@ importers:
devDependencies:
father:
specifier: ^4.4.1
- version: 4.5.1(@babel/core@7.26.0)(@types/node@22.13.14)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
+ version: 4.5.1(@babel/core@7.26.0)(@types/node@22.15.2)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
packages/studio-server:
dependencies:
@@ -826,7 +899,7 @@ importers:
version: 9.0.8
'@umijs/lint':
specifier: latest
- version: 4.4.6(eslint@9.23.0)(stylelint@14.16.1)(typescript@5.6.3)
+ version: 4.4.10(eslint@9.25.1)(stylelint@14.16.1)(typescript@5.6.3)
'@vitejs/plugin-react':
specifier: ^4.2.1
version: 4.3.3(vite@5.4.16(@types/node@22.9.1)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
@@ -835,7 +908,7 @@ importers:
version: 16.4.5
eslint:
specifier: latest
- version: 9.23.0
+ version: 9.25.1
monaco-editor-webpack-plugin:
specifier: ^7.1.0
version: 7.1.0(monaco-editor@0.52.2)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
@@ -884,13 +957,13 @@ importers:
version: 18.2.0
'@vitejs/plugin-react':
specifier: ^4.3.3
- version: 4.3.3(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ version: 4.3.3(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
father:
specifier: ^4.4.1
- version: 4.5.1(@babel/core@7.26.0)(@types/node@22.13.14)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
+ version: 4.5.1(@babel/core@7.26.0)(@types/node@22.15.2)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
vite:
specifier: ^5.4.16
- version: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ version: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
packages:
@@ -2341,32 +2414,32 @@ packages:
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
- '@eslint/config-array@0.19.2':
- resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==}
+ '@eslint/config-array@0.20.0':
+ resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/config-helpers@0.2.0':
- resolution: {integrity: sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==}
+ '@eslint/config-helpers@0.2.1':
+ resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/core@0.12.0':
- resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
+ '@eslint/core@0.13.0':
+ resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.3.1':
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/js@9.23.0':
- resolution: {integrity: sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==}
+ '@eslint/js@9.25.1':
+ resolution: {integrity: sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.6':
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/plugin-kit@0.2.7':
- resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==}
+ '@eslint/plugin-kit@0.2.8':
+ resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@floating-ui/core@0.6.2':
@@ -3892,8 +3965,8 @@ packages:
'@types/node@22.13.10':
resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
- '@types/node@22.13.14':
- resolution: {integrity: sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==}
+ '@types/node@22.15.2':
+ resolution: {integrity: sha512-uKXqKN9beGoMdBfcaTY1ecwz6ctxuJAcUlwE55938g0ZJ8lRxwAZqRz2AJ4pzpt5dHdTPMB863UZ0ESiFUcP7A==}
'@types/node@22.8.7':
resolution: {integrity: sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==}
@@ -4075,8 +4148,8 @@ packages:
'@umijs/babel-preset-umi@4.3.34':
resolution: {integrity: sha512-DwnGVn1HJ3C2CtEWx4r+IABIwN+o6BvKAxTB2W5ChtAm2lyvOQWys/hT9cPu5ERaKfTbVjudPCktN2Z5Wp23/g==}
- '@umijs/babel-preset-umi@4.4.6':
- resolution: {integrity: sha512-SsQezW0JKAUGF9xi4LM1Kx10D4OZBkg7krqATz9Qvtvods1B3MPwRVg4tjAtZCJn5/46Wl12VgQfqxNUcVIQ3A==}
+ '@umijs/babel-preset-umi@4.4.10':
+ resolution: {integrity: sha512-nbrpQNEMsmbNVLFhEzL9vwrpFH+WPyqSQjmcCOe9tD7SBWH585juwcuuX6h8ODX6ZkdhGbShNA4MGW4c2rvGAQ==}
'@umijs/bundler-esbuild@4.3.34':
resolution: {integrity: sha512-vWF4QI/34hofwHDp0aJwiB/gcqa4zp/4jgykK9KZV+MLoM+qg/ZJoPWB5S6kvS8gnoGdNH14g9vXmT4rL0oQtA==}
@@ -4088,6 +4161,9 @@ packages:
'@umijs/bundler-utils@4.3.34':
resolution: {integrity: sha512-++fJDhZENaT3e1Y8/gPh0/UvzNIGgRm54Xhv5kRvio1wApMeLhK7KRa4Q9GUP4jHuZ8dt3KYWO/CMr23/giCzg==}
+ '@umijs/bundler-utils@4.4.10':
+ resolution: {integrity: sha512-qGT4ylTgJQHWEZVDHv3vb7EHwyanpeCs42mPb7CT/Ivm+q/A5wPWFeSY6I8Ov6SVb6v9d2of6sP5NNAd7Qmckw==}
+
'@umijs/bundler-utils@4.4.6':
resolution: {integrity: sha512-JTqva1w4OVNw9ARlHvRgWFiynGQEDXhG79dxaQ2OWw7Y/dKUk5NP67DIA6xlFFCWXLXWQYV0bsYm1ESmaSdk9g==}
@@ -4176,8 +4252,8 @@ packages:
'@umijs/lint@4.3.34':
resolution: {integrity: sha512-PPzlFEa3LH1EAk5SWUHG3Sl2wiZLBnT8Ynfkaa953X4MS70E70C2gXXZLewap5O4rW7clVFanMWoGgnV8YsURQ==}
- '@umijs/lint@4.4.6':
- resolution: {integrity: sha512-Qt/YIUlt+QMz37syPZnMp+asQm3a5rmBGzN0xe/Waz3kW8lsi2zDa/Pxo468289rRMXFAPTWxcjfJeikKNXVNg==}
+ '@umijs/lint@4.4.10':
+ resolution: {integrity: sha512-1CGe+QOrfRhwzvvs3R6o6yf5Oez7+KMwX1L9yR5ERufvElNlpckZ/eK6CF3l5OpxKhXdqdiRN3vDyAKySdr/Mg==}
'@umijs/mako-darwin-arm64@0.9.6':
resolution: {integrity: sha512-AvYzWOornkq4LUio4RSMWmWwjceyH1CTNzyia85RYYLnjT+JaJZsJVF2R9CyFSDaaM80uoQLihCB2u/Tl87BQg==}
@@ -4289,6 +4365,9 @@ packages:
'@umijs/utils@4.3.34':
resolution: {integrity: sha512-O0/6roQIstdxswvvdPXFxR+sP1yLtQP0yZaUjZbfVLvBuLPqE2hhij+tw3lOymFdumJ9nL+ZF4Uty67MMHPXKw==}
+ '@umijs/utils@4.4.10':
+ resolution: {integrity: sha512-/sgImv56UxvcrQV5l2MCnyAm+khw4Th71P52mbXO0XA+AJMa/auHs9BTyH7zZpit/uvH0Os7y6Shr1QXVj5tbw==}
+
'@umijs/utils@4.4.6':
resolution: {integrity: sha512-W273HinahK0ZmfYQ/x8GsgS9ZMCknLB9ID+6sQhPoiA1ARKmD3RYD0ZHrboz19fILD4amFqeAYaF22rLuyz7OQ==}
@@ -6318,8 +6397,8 @@ packages:
resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint@9.23.0:
- resolution: {integrity: sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==}
+ eslint@9.25.1:
+ resolution: {integrity: sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@@ -7934,8 +8013,8 @@ packages:
kolorist@1.8.0:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
- kuzu-wasm@0.8.2:
- resolution: {integrity: sha512-2XS0FCCyl+lGv1qymHq0I448k+brqyIi21VB6sxv6LJUpEo6UWolJEDjzSgYb1JE9wrV+313yWSTs4sCsbKRsA==}
+ kuzu-wasm@0.9.0:
+ resolution: {integrity: sha512-LVk6SMLWWTtwhIzkVh3XESdFCTizjSkrQpJk3UrE0RUCRj73y1tM4zAgKB1PVG/zLyLsJ87sle94+Lmcs8jALw==}
latest-version@3.1.0:
resolution: {integrity: sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==}
@@ -11600,6 +11679,9 @@ packages:
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
unfetch@5.0.0:
resolution: {integrity: sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg==}
@@ -12845,11 +12927,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/eslint-parser@7.23.3(@babel/core@7.23.6)(eslint@9.23.0)':
+ '@babel/eslint-parser@7.23.3(@babel/core@7.23.6)(eslint@9.25.1)':
dependencies:
'@babel/core': 7.23.6
'@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
- eslint: 9.23.0
+ eslint: 9.25.1
eslint-visitor-keys: 2.1.0
semver: 6.3.1
@@ -13901,24 +13983,24 @@ snapshots:
'@esbuild/win32-x64@0.25.2':
optional: true
- '@eslint-community/eslint-utils@4.4.1(eslint@9.23.0)':
+ '@eslint-community/eslint-utils@4.4.1(eslint@9.25.1)':
dependencies:
- eslint: 9.23.0
+ eslint: 9.25.1
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.1': {}
- '@eslint/config-array@0.19.2':
+ '@eslint/config-array@0.20.0':
dependencies:
'@eslint/object-schema': 2.1.6
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.4.0
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
- '@eslint/config-helpers@0.2.0': {}
+ '@eslint/config-helpers@0.2.1': {}
- '@eslint/core@0.12.0':
+ '@eslint/core@0.13.0':
dependencies:
'@types/json-schema': 7.0.15
@@ -13936,13 +14018,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/js@9.23.0': {}
+ '@eslint/js@9.25.1': {}
'@eslint/object-schema@2.1.6': {}
- '@eslint/plugin-kit@0.2.7':
+ '@eslint/plugin-kit@0.2.8':
dependencies:
- '@eslint/core': 0.12.0
+ '@eslint/core': 0.13.0
levn: 0.4.1
'@floating-ui/core@0.6.2': {}
@@ -14225,7 +14307,7 @@ snapshots:
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@types/yargs': 16.0.9
chalk: 4.1.2
@@ -14234,7 +14316,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@types/yargs': 17.0.33
chalk: 4.1.2
@@ -14284,7 +14366,7 @@ snapshots:
'@loadable/component@5.15.2(react@18.2.0)':
dependencies:
- '@babel/runtime': 7.23.6
+ '@babel/runtime': 7.27.0
hoist-non-react-statics: 3.3.2
react: 18.2.0
react-is: 16.13.1
@@ -14339,20 +14421,20 @@ snapshots:
'@types/react': 18.2.0
react: 18.2.0
- '@microsoft/api-extractor-model@7.28.4(@types/node@22.13.14)':
+ '@microsoft/api-extractor-model@7.28.4(@types/node@22.15.2)':
dependencies:
'@microsoft/tsdoc': 0.14.2
'@microsoft/tsdoc-config': 0.16.2
- '@rushstack/node-core-library': 3.63.0(@types/node@22.13.14)
+ '@rushstack/node-core-library': 3.63.0(@types/node@22.15.2)
transitivePeerDependencies:
- '@types/node'
- '@microsoft/api-extractor@7.39.1(@types/node@22.13.14)':
+ '@microsoft/api-extractor@7.39.1(@types/node@22.15.2)':
dependencies:
- '@microsoft/api-extractor-model': 7.28.4(@types/node@22.13.14)
+ '@microsoft/api-extractor-model': 7.28.4(@types/node@22.15.2)
'@microsoft/tsdoc': 0.14.2
'@microsoft/tsdoc-config': 0.16.2
- '@rushstack/node-core-library': 3.63.0(@types/node@22.13.14)
+ '@rushstack/node-core-library': 3.63.0(@types/node@22.15.2)
'@rushstack/rig-package': 0.5.1
'@rushstack/ts-command-line': 4.17.1
colors: 1.2.5
@@ -14770,7 +14852,7 @@ snapshots:
'@rc-component/trigger@1.18.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
- '@babel/runtime': 7.26.0
+ '@babel/runtime': 7.27.0
'@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
classnames: 2.5.1
rc-motion: 2.9.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -14934,7 +15016,7 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.38.0':
optional: true
- '@rushstack/node-core-library@3.63.0(@types/node@22.13.14)':
+ '@rushstack/node-core-library@3.63.0(@types/node@22.15.2)':
dependencies:
colors: 1.2.5
fs-extra: 7.0.1
@@ -14944,7 +15026,7 @@ snapshots:
semver: 7.5.4
z-schema: 5.0.5
optionalDependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@rushstack/rig-package@0.5.1':
dependencies:
@@ -15232,7 +15314,7 @@ snapshots:
'@types/body-parser@1.19.5':
dependencies:
'@types/connect': 3.4.38
- '@types/node': 22.13.10
+ '@types/node': 22.15.2
'@types/command-line-args@5.2.3': {}
@@ -15240,7 +15322,7 @@ snapshots:
'@types/connect@3.4.38':
dependencies:
- '@types/node': 22.13.10
+ '@types/node': 22.15.2
'@types/d3-array@3.2.1': {}
@@ -15385,7 +15467,7 @@ snapshots:
'@types/express-serve-static-core@5.0.6':
dependencies:
- '@types/node': 22.13.10
+ '@types/node': 22.15.2
'@types/qs': 6.9.18
'@types/range-parser': 1.2.7
'@types/send': 0.17.4
@@ -15400,13 +15482,13 @@ snapshots:
'@types/fs-extra@11.0.1':
dependencies:
'@types/jsonfile': 6.1.4
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@types/geojson@7946.0.14': {}
'@types/graceful-fs@4.1.9':
dependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@types/hapi__joi@17.1.9': {}
@@ -15448,13 +15530,13 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@types/katex@0.16.7': {}
'@types/keyv@3.1.4':
dependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@types/lodash@4.17.13': {}
@@ -15486,9 +15568,9 @@ snapshots:
dependencies:
undici-types: 6.20.0
- '@types/node@22.13.14':
+ '@types/node@22.15.2':
dependencies:
- undici-types: 6.20.0
+ undici-types: 6.21.0
'@types/node@22.8.7':
dependencies:
@@ -15535,11 +15617,11 @@ snapshots:
'@types/responselike@1.0.3':
dependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@types/sax@1.2.7':
dependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
'@types/scheduler@0.23.0': {}
@@ -15548,12 +15630,12 @@ snapshots:
'@types/send@0.17.4':
dependencies:
'@types/mime': 1.3.5
- '@types/node': 22.13.10
+ '@types/node': 22.15.2
'@types/serve-static@1.15.7':
dependencies:
'@types/http-errors': 2.0.4
- '@types/node': 22.13.10
+ '@types/node': 22.15.2
'@types/send': 0.17.4
'@types/unist@2.0.11': {}
@@ -15578,15 +15660,15 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
- '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3)':
+ '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
- '@typescript-eslint/parser': 5.62.0(eslint@9.23.0)(typescript@5.6.3)
+ '@typescript-eslint/parser': 5.62.0(eslint@9.25.1)(typescript@5.6.3)
'@typescript-eslint/scope-manager': 5.62.0
- '@typescript-eslint/type-utils': 5.62.0(eslint@9.23.0)(typescript@5.6.3)
- '@typescript-eslint/utils': 5.62.0(eslint@9.23.0)(typescript@5.6.3)
+ '@typescript-eslint/type-utils': 5.62.0(eslint@9.25.1)(typescript@5.6.3)
+ '@typescript-eslint/utils': 5.62.0(eslint@9.25.1)(typescript@5.6.3)
debug: 4.3.7(supports-color@5.5.0)
- eslint: 9.23.0
+ eslint: 9.25.1
graphemer: 1.4.0
ignore: 5.3.2
natural-compare-lite: 1.4.0
@@ -15597,13 +15679,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@5.62.0(eslint@9.23.0)(typescript@5.6.3)':
+ '@typescript-eslint/parser@5.62.0(eslint@9.25.1)(typescript@5.6.3)':
dependencies:
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3)
debug: 4.3.7(supports-color@5.5.0)
- eslint: 9.23.0
+ eslint: 9.25.1
optionalDependencies:
typescript: 5.6.3
transitivePeerDependencies:
@@ -15614,12 +15696,12 @@ snapshots:
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/visitor-keys': 5.62.0
- '@typescript-eslint/type-utils@5.62.0(eslint@9.23.0)(typescript@5.6.3)':
+ '@typescript-eslint/type-utils@5.62.0(eslint@9.25.1)(typescript@5.6.3)':
dependencies:
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3)
- '@typescript-eslint/utils': 5.62.0(eslint@9.23.0)(typescript@5.6.3)
+ '@typescript-eslint/utils': 5.62.0(eslint@9.25.1)(typescript@5.6.3)
debug: 4.3.7(supports-color@5.5.0)
- eslint: 9.23.0
+ eslint: 9.25.1
tsutils: 3.21.0(typescript@5.6.3)
optionalDependencies:
typescript: 5.6.3
@@ -15642,15 +15724,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@5.62.0(eslint@9.23.0)(typescript@5.6.3)':
+ '@typescript-eslint/utils@5.62.0(eslint@9.25.1)(typescript@5.6.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.4.1(eslint@9.23.0)
+ '@eslint-community/eslint-utils': 4.4.1(eslint@9.25.1)
'@types/json-schema': 7.0.15
'@types/semver': 7.5.8
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3)
- eslint: 9.23.0
+ eslint: 9.25.1
eslint-scope: 5.1.1
semver: 7.6.3
transitivePeerDependencies:
@@ -15711,12 +15793,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@umijs/babel-preset-umi@4.4.6':
+ '@umijs/babel-preset-umi@4.4.10':
dependencies:
'@babel/runtime': 7.23.6
'@bloomberg/record-tuple-polyfill': 0.0.4
- '@umijs/bundler-utils': 4.4.6
- '@umijs/utils': 4.4.6
+ '@umijs/bundler-utils': 4.4.10
+ '@umijs/utils': 4.4.10
core-js: 3.34.0
transitivePeerDependencies:
- supports-color
@@ -15734,7 +15816,7 @@ snapshots:
'@umijs/bundler-mako@0.9.6':
dependencies:
- '@umijs/bundler-utils': 4.3.34
+ '@umijs/bundler-utils': 4.4.6
'@umijs/mako': 0.9.6
chalk: 4.1.2
compression: 1.7.5
@@ -15759,6 +15841,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@umijs/bundler-utils@4.4.10':
+ dependencies:
+ '@umijs/utils': 4.4.10
+ esbuild: 0.21.4
+ regenerate: 1.4.2
+ regenerate-unicode-properties: 10.1.1
+ spdy: 4.0.2
+ transitivePeerDependencies:
+ - supports-color
+
'@umijs/bundler-utils@4.4.6':
dependencies:
'@umijs/utils': 4.4.6
@@ -15769,18 +15861,18 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@umijs/bundler-vite@4.3.34(@types/node@22.13.14)(lightningcss@1.22.1)(postcss@8.5.3)(rollup@3.29.5)(sass@1.81.0)(terser@5.36.0)':
+ '@umijs/bundler-vite@4.3.34(@types/node@22.15.2)(lightningcss@1.22.1)(postcss@8.5.3)(rollup@3.29.5)(sass@1.81.0)(terser@5.36.0)':
dependencies:
'@svgr/core': 6.5.1
'@umijs/bundler-utils': 4.3.34
'@umijs/utils': 4.3.34
- '@vitejs/plugin-react': 4.0.0(vite@4.5.2(@types/node@22.13.14)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ '@vitejs/plugin-react': 4.0.0(vite@4.5.2(@types/node@22.15.2)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
core-js: 3.34.0
less: 4.1.3
postcss-preset-env: 7.5.0(postcss@8.5.3)
rollup-plugin-visualizer: 5.9.0(rollup@3.29.5)
systemjs: 6.15.1
- vite: 4.5.2(@types/node@22.13.14)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite: 4.5.2(@types/node@22.15.2)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
transitivePeerDependencies:
- '@types/node'
- lightningcss
@@ -15912,20 +16004,20 @@ snapshots:
'@umijs/history@5.3.1':
dependencies:
- '@babel/runtime': 7.23.6
+ '@babel/runtime': 7.27.0
query-string: 6.14.1
- '@umijs/lint@4.3.34(eslint@9.23.0)(stylelint@14.16.1)(typescript@5.6.3)':
+ '@umijs/lint@4.3.34(eslint@9.25.1)(stylelint@14.16.1)(typescript@5.6.3)':
dependencies:
'@babel/core': 7.23.6
- '@babel/eslint-parser': 7.23.3(@babel/core@7.23.6)(eslint@9.23.0)
+ '@babel/eslint-parser': 7.23.3(@babel/core@7.23.6)(eslint@9.25.1)
'@stylelint/postcss-css-in-js': 0.38.0(postcss-syntax@0.36.2(postcss@8.5.3))(postcss@8.5.3)
- '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3)
- '@typescript-eslint/parser': 5.62.0(eslint@9.23.0)(typescript@5.6.3)
+ '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3)
+ '@typescript-eslint/parser': 5.62.0(eslint@9.25.1)(typescript@5.6.3)
'@umijs/babel-preset-umi': 4.3.34
- eslint-plugin-jest: 27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3)
- eslint-plugin-react: 7.33.2(eslint@9.23.0)
- eslint-plugin-react-hooks: 4.6.0(eslint@9.23.0)
+ eslint-plugin-jest: 27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3)
+ eslint-plugin-react: 7.33.2(eslint@9.25.1)
+ eslint-plugin-react-hooks: 4.6.0(eslint@9.25.1)
postcss: 8.5.3
postcss-syntax: 0.36.2(postcss@8.5.3)
stylelint-config-standard: 25.0.0(stylelint@14.16.1)
@@ -15941,17 +16033,17 @@ snapshots:
- supports-color
- typescript
- '@umijs/lint@4.4.6(eslint@9.23.0)(stylelint@14.16.1)(typescript@5.6.3)':
+ '@umijs/lint@4.4.10(eslint@9.25.1)(stylelint@14.16.1)(typescript@5.6.3)':
dependencies:
'@babel/core': 7.23.6
- '@babel/eslint-parser': 7.23.3(@babel/core@7.23.6)(eslint@9.23.0)
+ '@babel/eslint-parser': 7.23.3(@babel/core@7.23.6)(eslint@9.25.1)
'@stylelint/postcss-css-in-js': 0.38.0(postcss-syntax@0.36.2(postcss@8.4.49))(postcss@8.4.49)
- '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3)
- '@typescript-eslint/parser': 5.62.0(eslint@9.23.0)(typescript@5.6.3)
- '@umijs/babel-preset-umi': 4.4.6
- eslint-plugin-jest: 27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3)
- eslint-plugin-react: 7.33.2(eslint@9.23.0)
- eslint-plugin-react-hooks: 4.6.0(eslint@9.23.0)
+ '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3)
+ '@typescript-eslint/parser': 5.62.0(eslint@9.25.1)(typescript@5.6.3)
+ '@umijs/babel-preset-umi': 4.4.10
+ eslint-plugin-jest: 27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3)
+ eslint-plugin-react: 7.33.2(eslint@9.25.1)
+ eslint-plugin-react-hooks: 4.6.0(eslint@9.25.1)
postcss: 8.4.49
postcss-syntax: 0.36.2(postcss@8.4.49)
stylelint-config-standard: 25.0.0(stylelint@14.16.1)
@@ -16030,7 +16122,7 @@ snapshots:
dependencies:
tsx: 3.12.2
- '@umijs/preset-umi@4.3.34(@types/node@22.13.14)(@types/react@18.2.0)(lightningcss@1.22.1)(rollup@3.29.5)(sass@1.81.0)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)':
+ '@umijs/preset-umi@4.3.34(@types/node@22.15.2)(@types/react@18.2.0)(lightningcss@1.22.1)(rollup@3.29.5)(sass@1.81.0)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)':
dependencies:
'@iconify/utils': 2.1.1
'@svgr/core': 6.5.1
@@ -16039,7 +16131,7 @@ snapshots:
'@umijs/bundler-esbuild': 4.3.34
'@umijs/bundler-mako': 0.9.6
'@umijs/bundler-utils': 4.3.34
- '@umijs/bundler-vite': 4.3.34(@types/node@22.13.14)(lightningcss@1.22.1)(postcss@8.5.3)(rollup@3.29.5)(sass@1.81.0)(terser@5.36.0)
+ '@umijs/bundler-vite': 4.3.34(@types/node@22.15.2)(lightningcss@1.22.1)(postcss@8.5.3)(rollup@3.29.5)(sass@1.81.0)(terser@5.36.0)
'@umijs/bundler-webpack': 4.3.34(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
'@umijs/core': 4.3.34
'@umijs/did-you-know': 1.0.3
@@ -16145,6 +16237,11 @@ snapshots:
chokidar: 3.5.3
pino: 7.11.0
+ '@umijs/utils@4.4.10':
+ dependencies:
+ chokidar: 3.5.3
+ pino: 7.11.0
+
'@umijs/utils@4.4.6':
dependencies:
chokidar: 3.5.3
@@ -16156,24 +16253,24 @@ snapshots:
'@vercel/ncc@0.33.3': {}
- '@vitejs/plugin-react@4.0.0(vite@4.5.2(@types/node@22.13.14)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))':
+ '@vitejs/plugin-react@4.0.0(vite@4.5.2(@types/node@22.15.2)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))':
dependencies:
'@babel/core': 7.26.0
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
react-refresh: 0.14.2
- vite: 4.5.2(@types/node@22.13.14)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite: 4.5.2(@types/node@22.15.2)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-react@4.3.3(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))':
+ '@vitejs/plugin-react@4.3.3(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))':
dependencies:
'@babel/core': 7.26.0
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
- vite: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
transitivePeerDependencies:
- supports-color
@@ -16188,14 +16285,25 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-react@4.3.4(vite@6.2.4(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1))':
+ '@vitejs/plugin-react@4.3.4(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.14.2
+ vite: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitejs/plugin-react@4.3.4(vite@6.2.4(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1))':
dependencies:
'@babel/core': 7.26.0
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
- vite: 6.2.4(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1)
+ vite: 6.2.4(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1)
transitivePeerDependencies:
- supports-color
@@ -16206,13 +16314,13 @@ snapshots:
chai: 5.1.2
tinyrainbow: 1.2.0
- '@vitest/mocker@2.1.5(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))':
+ '@vitest/mocker@2.1.5(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))':
dependencies:
'@vitest/spy': 2.1.5
estree-walker: 3.0.3
magic-string: 0.30.13
optionalDependencies:
- vite: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
'@vitest/pretty-format@2.1.5':
dependencies:
@@ -16752,7 +16860,7 @@ snapshots:
babel-plugin-macros@3.1.0:
dependencies:
- '@babel/runtime': 7.26.0
+ '@babel/runtime': 7.27.0
cosmiconfig: 7.1.0
resolve: 1.22.8
@@ -18238,7 +18346,7 @@ snapshots:
dumi-assets-types@2.4.14: {}
- dumi-theme-antd@0.4.2(@babel/core@7.26.0)(@types/react@18.2.0)(antd@5.22.2(date-fns@2.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(date-fns@2.30.0)(dumi@2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.13.14)(@types/react@18.2.0)(eslint@9.23.0)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1))(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0):
+ dumi-theme-antd@0.4.2(@babel/core@7.26.0)(@types/react@18.2.0)(antd@5.22.2(date-fns@2.30.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(date-fns@2.30.0)(dumi@2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.15.2)(@types/react@18.2.0)(eslint@9.25.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1))(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0):
dependencies:
'@ant-design/cssinjs': 1.20.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@ant-design/icons': 5.3.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -18251,7 +18359,7 @@ snapshots:
antd-token-previewer: 2.0.0-alpha.6(@babel/core@7.26.0)(@types/react@18.2.0)(date-fns@2.30.0)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0)
classnames: 2.3.2
dayjs: 1.11.7
- dumi: 2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.13.14)(@types/react@18.2.0)(eslint@9.23.0)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
+ dumi: 2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.15.2)(@types/react@18.2.0)(eslint@9.25.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
lodash.clonedeep: 4.5.0
prism-react-renderer: 2.4.0(react@18.2.0)
rc-drawer: 6.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -18270,7 +18378,7 @@ snapshots:
- react-is
- supports-color
- dumi@2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.13.14)(@types/react@18.2.0)(eslint@9.23.0)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1):
+ dumi@2.4.14(@babel/core@7.26.0)(@swc/helpers@0.5.15)(@types/node@22.15.2)(@types/react@18.2.0)(eslint@9.25.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1):
dependencies:
'@ant-design/icons-svg': 4.4.2
'@makotot/ghostui': 2.0.0(react@18.2.0)
@@ -18335,7 +18443,7 @@ snapshots:
sass: 1.81.0
sitemap: 7.1.2
sucrase: 3.35.0
- umi: 4.3.34(@babel/core@7.26.0)(@types/node@22.13.14)(@types/react@18.2.0)(eslint@9.23.0)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(sass@1.81.0)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
+ umi: 4.3.34(@babel/core@7.26.0)(@types/node@22.15.2)(@types/react@18.2.0)(eslint@9.25.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(sass@1.81.0)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
unified: 10.1.2
unist-util-visit: 4.1.2
unist-util-visit-parents: 5.1.3
@@ -18759,28 +18867,28 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
- eslint-plugin-jest@27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3):
+ eslint-plugin-jest@27.2.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3):
dependencies:
- '@typescript-eslint/utils': 5.62.0(eslint@9.23.0)(typescript@5.6.3)
- eslint: 9.23.0
+ '@typescript-eslint/utils': 5.62.0(eslint@9.25.1)(typescript@5.6.3)
+ eslint: 9.25.1
optionalDependencies:
- '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.23.0)(typescript@5.6.3))(eslint@9.23.0)(typescript@5.6.3)
+ '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.25.1)(typescript@5.6.3))(eslint@9.25.1)(typescript@5.6.3)
transitivePeerDependencies:
- supports-color
- typescript
- eslint-plugin-react-hooks@4.6.0(eslint@9.23.0):
+ eslint-plugin-react-hooks@4.6.0(eslint@9.25.1):
dependencies:
- eslint: 9.23.0
+ eslint: 9.25.1
- eslint-plugin-react@7.33.2(eslint@9.23.0):
+ eslint-plugin-react@7.33.2(eslint@9.25.1):
dependencies:
array-includes: 3.1.8
array.prototype.flatmap: 1.3.2
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.2.0
- eslint: 9.23.0
+ eslint: 9.25.1
estraverse: 5.3.0
jsx-ast-utils: 3.3.5
minimatch: 3.1.2
@@ -18809,16 +18917,16 @@ snapshots:
eslint-visitor-keys@4.2.0: {}
- eslint@9.23.0:
+ eslint@9.25.1:
dependencies:
- '@eslint-community/eslint-utils': 4.4.1(eslint@9.23.0)
+ '@eslint-community/eslint-utils': 4.4.1(eslint@9.25.1)
'@eslint-community/regexpp': 4.12.1
- '@eslint/config-array': 0.19.2
- '@eslint/config-helpers': 0.2.0
- '@eslint/core': 0.12.0
+ '@eslint/config-array': 0.20.0
+ '@eslint/config-helpers': 0.2.1
+ '@eslint/core': 0.13.0
'@eslint/eslintrc': 3.3.1
- '@eslint/js': 9.23.0
- '@eslint/plugin-kit': 0.2.7
+ '@eslint/js': 9.25.1
+ '@eslint/plugin-kit': 0.2.8
'@humanfs/node': 0.16.6
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.2
@@ -19134,9 +19242,9 @@ snapshots:
dependencies:
reusify: 1.0.4
- father@4.5.1(@babel/core@7.26.0)(@types/node@22.13.14)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1))):
+ father@4.5.1(@babel/core@7.26.0)(@types/node@22.15.2)(styled-components@5.3.11(@babel/core@7.26.0)(react-dom@18.2.0(react@18.2.0))(react-is@18.3.1)(react@18.2.0))(type-fest@1.4.0)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1))):
dependencies:
- '@microsoft/api-extractor': 7.39.1(@types/node@22.13.14)
+ '@microsoft/api-extractor': 7.39.1(@types/node@22.15.2)
'@umijs/babel-preset-umi': 4.3.34
'@umijs/bundler-utils': 4.3.34
'@umijs/bundler-webpack': 4.3.34(type-fest@1.4.0)(typescript@5.3.3)(webpack@5.96.1(webpack-cli@5.1.4(webpack@5.96.1)))
@@ -19588,7 +19696,7 @@ snapshots:
dependencies:
basic-ftp: 5.0.5
data-uri-to-buffer: 6.0.2
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.4.0
fs-extra: 11.2.0
transitivePeerDependencies:
- supports-color
@@ -20064,7 +20172,7 @@ snapshots:
history@5.3.0:
dependencies:
- '@babel/runtime': 7.23.6
+ '@babel/runtime': 7.27.0
hmac-drbg@1.0.1:
dependencies:
@@ -20692,7 +20800,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -20709,7 +20817,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -20717,20 +20825,20 @@ snapshots:
jest-worker@27.5.1:
dependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
merge-stream: 2.0.0
supports-color: 8.1.1
jest-worker@29.4.3:
dependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
jest-worker@29.7.0:
dependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -20854,7 +20962,7 @@ snapshots:
kolorist@1.8.0: {}
- kuzu-wasm@0.8.2:
+ kuzu-wasm@0.9.0:
dependencies:
threads: 1.7.0
tiny-worker: 2.3.0
@@ -23791,7 +23899,7 @@ snapshots:
react-helmet-async@1.3.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies:
- '@babel/runtime': 7.23.6
+ '@babel/runtime': 7.27.0
invariant: 2.2.4
prop-types: 15.8.1
react: 18.2.0
@@ -24786,7 +24894,7 @@ snapshots:
spdy-transport@3.0.0:
dependencies:
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.4.0
detect-node: 2.1.0
hpack.js: 2.1.6
obuf: 1.1.2
@@ -25562,14 +25670,14 @@ snapshots:
dependencies:
'@lukeed/csprng': 1.1.0
- umi@4.3.34(@babel/core@7.26.0)(@types/node@22.13.14)(@types/react@18.2.0)(eslint@9.23.0)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(sass@1.81.0)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1):
+ umi@4.3.34(@babel/core@7.26.0)(@types/node@22.15.2)(@types/react@18.2.0)(eslint@9.25.1)(lightningcss@1.22.1)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@3.29.5)(sass@1.81.0)(stylelint@14.16.1)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1):
dependencies:
'@babel/runtime': 7.23.6
'@umijs/bundler-utils': 4.3.34
'@umijs/bundler-webpack': 4.3.34(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
'@umijs/core': 4.3.34
- '@umijs/lint': 4.3.34(eslint@9.23.0)(stylelint@14.16.1)(typescript@5.6.3)
- '@umijs/preset-umi': 4.3.34(@types/node@22.13.14)(@types/react@18.2.0)(lightningcss@1.22.1)(rollup@3.29.5)(sass@1.81.0)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
+ '@umijs/lint': 4.3.34(eslint@9.25.1)(stylelint@14.16.1)(typescript@5.6.3)
+ '@umijs/preset-umi': 4.3.34(@types/node@22.15.2)(@types/react@18.2.0)(lightningcss@1.22.1)(rollup@3.29.5)(sass@1.81.0)(terser@5.36.0)(type-fest@1.4.0)(typescript@5.6.3)(webpack@5.96.1)
'@umijs/renderer-react': 4.3.34(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@umijs/server': 4.3.34
'@umijs/test': 4.3.34(@babel/core@7.26.0)
@@ -25620,6 +25728,8 @@ snapshots:
undici-types@6.20.0: {}
+ undici-types@6.21.0: {}
+
unfetch@5.0.0: {}
unified@10.1.2:
@@ -25946,13 +26056,13 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
- vite-node@2.1.5(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0):
+ vite-node@2.1.5(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0):
dependencies:
cac: 6.7.14
debug: 4.3.7(supports-color@5.5.0)
es-module-lexer: 1.5.4
pathe: 1.1.2
- vite: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -25985,12 +26095,12 @@ snapshots:
picocolors: 1.1.1
vite: 5.4.16(@types/node@22.9.1)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
- vite-plugin-top-level-await@1.4.4(@swc/helpers@0.5.15)(rollup@4.38.0)(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)):
+ vite-plugin-top-level-await@1.4.4(@swc/helpers@0.5.15)(rollup@4.38.0)(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)):
dependencies:
'@rollup/plugin-virtual': 3.0.2(rollup@4.38.0)
'@swc/core': 1.9.2(@swc/helpers@0.5.15)
uuid: 10.0.0
- vite: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
transitivePeerDependencies:
- '@swc/helpers'
- rollup
@@ -26005,34 +26115,34 @@ snapshots:
- '@swc/helpers'
- rollup
- vite-plugin-wasm@3.3.0(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)):
+ vite-plugin-wasm@3.3.0(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)):
dependencies:
- vite: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
vite-plugin-wasm@3.3.0(vite@5.4.16(@types/node@22.9.1)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)):
dependencies:
vite: 5.4.16(@types/node@22.9.1)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
- vite@4.5.2(@types/node@22.13.14)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0):
+ vite@4.5.2(@types/node@22.15.2)(less@4.1.3)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0):
dependencies:
esbuild: 0.18.20
postcss: 8.5.3
rollup: 3.29.5
optionalDependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
fsevents: 2.3.3
less: 4.1.3
lightningcss: 1.22.1
sass: 1.81.0
terser: 5.36.0
- vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0):
+ vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0):
dependencies:
esbuild: 0.21.5
postcss: 8.5.3
rollup: 4.38.0
optionalDependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
fsevents: 2.3.3
less: 4.2.0
lightningcss: 1.22.1
@@ -26052,13 +26162,13 @@ snapshots:
sass: 1.81.0
terser: 5.36.0
- vite@6.2.4(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1):
+ vite@6.2.4(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)(yaml@2.5.1):
dependencies:
esbuild: 0.25.2
postcss: 8.5.3
rollup: 4.38.0
optionalDependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
fsevents: 2.3.3
less: 4.2.0
lightningcss: 1.22.1
@@ -26066,10 +26176,10 @@ snapshots:
terser: 5.36.0
yaml: 2.5.1
- vitest@2.1.5(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0):
+ vitest@2.1.5(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0):
dependencies:
'@vitest/expect': 2.1.5
- '@vitest/mocker': 2.1.5(vite@5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
+ '@vitest/mocker': 2.1.5(vite@5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0))
'@vitest/pretty-format': 2.1.5
'@vitest/runner': 2.1.5
'@vitest/snapshot': 2.1.5
@@ -26085,11 +26195,11 @@ snapshots:
tinyexec: 0.3.1
tinypool: 1.0.2
tinyrainbow: 1.2.0
- vite: 5.4.16(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
- vite-node: 2.1.5(@types/node@22.13.14)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite: 5.4.16(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
+ vite-node: 2.1.5(@types/node@22.15.2)(less@4.2.0)(lightningcss@1.22.1)(sass@1.81.0)(terser@5.36.0)
why-is-node-running: 2.3.0
optionalDependencies:
- '@types/node': 22.13.14
+ '@types/node': 22.15.2
transitivePeerDependencies:
- less
- lightningcss