Merge branch 'dev'
This commit is contained in:
commit
826b69a587
31 files changed
+4784
-2910
No files matched your search
@@ -137,6 +137,7 @@ async function createWindow(projectPath?: string) {
|
|||||||
trafficLightPosition: { x: 10, y: 10 },
|
trafficLightPosition: { x: 10, y: 10 },
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload,
|
preload,
|
||||||
|
webSecurity: false,
|
||||||
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
||||||
// Consider using contextBridge.exposeInMainWorld
|
// Consider using contextBridge.exposeInMainWorld
|
||||||
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
<!-- <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" /> -->
|
||||||
<title>Behavior3 Editor</title>
|
<title>Behavior3 Editor</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Generated
+2472
-838
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "behavior3editor",
|
"name": "behavior3editor",
|
||||||
"version": "1.8.1",
|
"version": "1.8.2",
|
||||||
"main": "dist-electron/main/index.js",
|
"main": "dist-electron/main/index.js",
|
||||||
"description": "行为树编辑器",
|
"description": "行为树编辑器",
|
||||||
"author": "zhandouxiaojiji & codetypes",
|
"author": "zhandouxiaojiji & codetypes",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"homepage": "https://github.com/zhandouxiaojiji/behavior3editor#readme",
|
"homepage": "https://github.com/zhandouxiaojiji/behavior3editor#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^5.4.0",
|
"@ant-design/icons": "^5.4.0",
|
||||||
"@antv/g6": "^4.8.24",
|
"@antv/g6": "^5.0.45",
|
||||||
"@electron/remote": "github:electron/remote",
|
"@electron/remote": "github:electron/remote",
|
||||||
"@types/electron-localshortcut": "^3.1.3",
|
"@types/electron-localshortcut": "^3.1.3",
|
||||||
"@types/node": "^20.14.14",
|
"@types/node": "^20.14.14",
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
"tree.name": "Tree name",
|
"tree.name": "Tree name",
|
||||||
"tree.desc": "Tree desc",
|
"tree.desc": "Tree desc",
|
||||||
"tree.export": "Export",
|
"tree.export": "Export",
|
||||||
"tree.firstid": "First id",
|
"tree.prefix": "ID prefix",
|
||||||
"tree.group": "Enable Node Grouping",
|
"tree.group": "Enable Node Grouping",
|
||||||
"tree.group.placeholder": "Select node groups",
|
"tree.group.placeholder": "Select node groups",
|
||||||
"tree.vars": "Define Variables",
|
"tree.vars": "Define Variables",
|
||||||
@@ -105,9 +105,11 @@
|
|||||||
"node.children": "Num children",
|
"node.children": "Num children",
|
||||||
"node.debug": "Debug",
|
"node.debug": "Debug",
|
||||||
"node.deleteRootNodeDenied": "Unable to delete the root node",
|
"node.deleteRootNodeDenied": "Unable to delete the root node",
|
||||||
|
"node.dropDenied": "This node position is denied to drop the node",
|
||||||
"node.desc": "Node desc",
|
"node.desc": "Node desc",
|
||||||
"node.disabled": "Disabled",
|
"node.disabled": "Disabled",
|
||||||
"node.editSubtreeDenied": "Unable to edit the subtree node",
|
"node.editSubtreeDenied": "Unable to edit the subtree node",
|
||||||
|
"node.editFailed": "Node '{{name}}' edit failed",
|
||||||
"node.group": "Node group",
|
"node.group": "Node group",
|
||||||
"node.groupNotEnabled": "One of '{{group}}' node group is not enabled in current file",
|
"node.groupNotEnabled": "One of '{{group}}' node group is not enabled in current file",
|
||||||
"node.id": "Node id",
|
"node.id": "Node id",
|
||||||
@@ -126,6 +128,7 @@
|
|||||||
"node.rootNodeAsSubtree": "Root node cann't be save as subtree",
|
"node.rootNodeAsSubtree": "Root node cann't be save as subtree",
|
||||||
"node.subtree": "Subtree",
|
"node.subtree": "Subtree",
|
||||||
"node.subtreePathError": "You should save the subtree inside the current workspace",
|
"node.subtreePathError": "You should save the subtree inside the current workspace",
|
||||||
|
"node.subtreeSaveRootError": "Root node cann't be save as subtree",
|
||||||
"node.type": "Node type",
|
"node.type": "Node type",
|
||||||
"node.undefinedVariable": "Variable '{{variable}}' is not defined",
|
"node.undefinedVariable": "Variable '{{variable}}' is not defined",
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
"tree.name": "行为树",
|
"tree.name": "行为树",
|
||||||
"tree.desc": "说明",
|
"tree.desc": "说明",
|
||||||
"tree.export": "导出",
|
"tree.export": "导出",
|
||||||
"tree.firstid": "起始标识",
|
"tree.prefix": "标识前缀",
|
||||||
"tree.group": "启用节点分组",
|
"tree.group": "启用节点分组",
|
||||||
"tree.group.placeholder": "选择节点分组",
|
"tree.group.placeholder": "选择节点分组",
|
||||||
"tree.vars": "定义变量",
|
"tree.vars": "定义变量",
|
||||||
@@ -105,9 +105,11 @@
|
|||||||
"node.children": "子节点数量",
|
"node.children": "子节点数量",
|
||||||
"node.debug": "调试",
|
"node.debug": "调试",
|
||||||
"node.deleteRootNodeDenied": "不能删除根节点",
|
"node.deleteRootNodeDenied": "不能删除根节点",
|
||||||
|
"node.dropDenied": "此节点位置不允许放置拖拽的节点",
|
||||||
"node.desc": "节点说明",
|
"node.desc": "节点说明",
|
||||||
"node.disabled": "禁用",
|
"node.disabled": "禁用",
|
||||||
"node.editSubtreeDenied": "不能编辑子树节点",
|
"node.editSubtreeDenied": "不能编辑子树节点",
|
||||||
|
"node.editFailed": "节点 {{name}} 编辑失败",
|
||||||
"node.group": "节点分组",
|
"node.group": "节点分组",
|
||||||
"node.groupNotEnabled": "节点分组 “{{group}}” 任意一个未在当前文件中启用",
|
"node.groupNotEnabled": "节点分组 “{{group}}” 任意一个未在当前文件中启用",
|
||||||
"node.id": "节点标识",
|
"node.id": "节点标识",
|
||||||
@@ -126,6 +128,7 @@
|
|||||||
"node.rootNodeAsSubtree": "根节点不能保存为子树",
|
"node.rootNodeAsSubtree": "根节点不能保存为子树",
|
||||||
"node.subtree": "子树",
|
"node.subtree": "子树",
|
||||||
"node.subtreePathError": "子树必须保存在当前工作区的目录内",
|
"node.subtreePathError": "子树必须保存在当前工作区的目录内",
|
||||||
|
"node.subtreeSaveRootError": "根节点不能保存为子树",
|
||||||
"node.type": "节点类型",
|
"node.type": "节点类型",
|
||||||
"node.undefinedVariable": "变量 “{{variable}}” 未定义",
|
"node.undefinedVariable": "变量 “{{variable}}” 未定义",
|
||||||
"node.unknown.desc": "新建节点"
|
"node.unknown.desc": "新建节点"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
firstid: number;
|
firstid: number;
|
||||||
group: string[];
|
group: string[];
|
||||||
import: string[];
|
import: string[];
|
||||||
declvar: VarDef[];
|
vars: { name: string; desc: string }[];
|
||||||
root: NodeModel;
|
root: NodeModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
firstid: number;
|
firstid: number;
|
||||||
group: string[];
|
group: string[];
|
||||||
import: string[];
|
import: string[];
|
||||||
declvar: VarDef[];
|
vars: { name: string; desc: string }[];
|
||||||
root: NodeModel;
|
root: NodeModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"export": true,
|
"export": true,
|
||||||
"group": [],
|
"group": [],
|
||||||
"import": [],
|
"import": [],
|
||||||
"declvar": [
|
"vars": [
|
||||||
{
|
{
|
||||||
"name": "context",
|
"name": "context",
|
||||||
"desc": "上下文"
|
"desc": "上下文"
|
||||||
@@ -22,8 +22,6 @@
|
|||||||
"args": {
|
"args": {
|
||||||
"time": 1
|
"time": 1
|
||||||
},
|
},
|
||||||
"input": [
|
"input": ["version"]
|
||||||
"version"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
|
"version": "1.8.1",
|
||||||
"name": "declare-vars",
|
"name": "declare-vars",
|
||||||
"desc": "",
|
"desc": "",
|
||||||
"firstid": 1,
|
"firstid": 1,
|
||||||
"export": true,
|
"export": true,
|
||||||
"import": [
|
"group": [],
|
||||||
"vars/declare-core.json"
|
"import": ["vars/declare-core.json"],
|
||||||
],
|
"vars": [
|
||||||
"declvar": [
|
|
||||||
{
|
{
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"desc": "所有者"
|
"desc": "所有者"
|
||||||
@@ -17,8 +17,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": "1",
|
||||||
"name": "Sequence"
|
"name": "Sequence"
|
||||||
},
|
}
|
||||||
"group": []
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,23 @@
|
|||||||
{
|
{
|
||||||
|
"version": "1.8.1",
|
||||||
"name": "test-subtree",
|
"name": "test-subtree",
|
||||||
"desc": "",
|
"desc": "",
|
||||||
"firstid": 1,
|
"firstid": 1,
|
||||||
"export": true,
|
"export": true,
|
||||||
|
"group": [],
|
||||||
"import": ["vars/declare-core.json"],
|
"import": ["vars/declare-core.json"],
|
||||||
"declvar": [
|
"vars": [
|
||||||
{
|
{
|
||||||
"name": "test",
|
"name": "test",
|
||||||
"desc": "测试"
|
"desc": "测试"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": "1",
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": "2",
|
||||||
"name": "Log",
|
"name": "Log",
|
||||||
"args": {
|
"args": {
|
||||||
"message": "hello"
|
"message": "hello"
|
||||||
|
|||||||
+18
-16
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"name": "test-vars",
|
"name": "test-vars",
|
||||||
"desc": "",
|
"desc": "",
|
||||||
"firstid": 1,
|
"prefix": "A",
|
||||||
"export": true,
|
"export": true,
|
||||||
"group": [
|
"group": [
|
||||||
"Client",
|
"Client",
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"vars/declare-core.json",
|
"vars/declare-core.json",
|
||||||
"vars/declare-vars.json"
|
"vars/declare-vars.json"
|
||||||
],
|
],
|
||||||
"declvar": [
|
"vars": [
|
||||||
{
|
{
|
||||||
"name": "target",
|
"name": "target",
|
||||||
"desc": "目标"
|
"desc": "目标"
|
||||||
@@ -23,52 +23,52 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": "1",
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": "2",
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
"path": "vars/test-subtree.json"
|
"path": "vars/test-subtree.json"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": "4",
|
||||||
"name": "FindEnemy",
|
"name": "FindEnemy",
|
||||||
"args": {},
|
|
||||||
"output": [
|
"output": [
|
||||||
"target1"
|
"target1"
|
||||||
]
|
],
|
||||||
|
"args": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": "5",
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"input": [
|
"input": [
|
||||||
"target1"
|
"target1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 6,
|
"id": "6",
|
||||||
"name": "Check",
|
"name": "Check",
|
||||||
"args": {
|
"args": {
|
||||||
"value": "target[0].x > 0 "
|
"value": "target[0].x > 0 "
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": "7",
|
||||||
"name": "MoveToTarget",
|
"name": "MoveToTarget",
|
||||||
"input": [
|
"input": [
|
||||||
"target"
|
"target"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 8,
|
"id": "8",
|
||||||
"name": "Clear",
|
"name": "Clear",
|
||||||
"output": [
|
"output": [
|
||||||
"test"
|
"test"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 9,
|
"id": "9",
|
||||||
"name": "TestB3",
|
"name": "TestB3",
|
||||||
"args": {
|
"args": {
|
||||||
"time": 1,
|
"time": 1,
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 10,
|
"id": "10",
|
||||||
"name": "TestB3",
|
"name": "TestB3",
|
||||||
"args": {
|
"args": {
|
||||||
"time": 1,
|
"time": 1,
|
||||||
@@ -90,15 +90,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 11,
|
"id": "11",
|
||||||
"name": "Clear",
|
"name": "Clear",
|
||||||
"output": [
|
"output": [
|
||||||
"context"
|
"context"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 12,
|
"id": "12",
|
||||||
"name": "Listen",
|
"name": "Listen",
|
||||||
|
"input": [],
|
||||||
|
"output": [],
|
||||||
"args": {
|
"args": {
|
||||||
"builtin": "afterRunFailure"
|
"builtin": "afterRunFailure"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,9 @@
|
|||||||
"desc": "英雄测试AI",
|
"desc": "英雄测试AI",
|
||||||
"firstid": 1,
|
"firstid": 1,
|
||||||
"export": true,
|
"export": true,
|
||||||
"group": [
|
"group": ["Client", "Server"],
|
||||||
"Client",
|
|
||||||
"Server"
|
|
||||||
],
|
|
||||||
"import": [],
|
"import": [],
|
||||||
"declvar": [],
|
"vars": [],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Selector",
|
"name": "Selector",
|
||||||
@@ -29,16 +26,12 @@
|
|||||||
"w": 100,
|
"w": 100,
|
||||||
"h": 50
|
"h": 50
|
||||||
},
|
},
|
||||||
"output": [
|
"output": ["enemy"]
|
||||||
"enemy"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"input": [
|
"input": ["enemy"]
|
||||||
"enemy"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 5,
|
||||||
@@ -63,16 +56,12 @@
|
|||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0
|
"y": 0
|
||||||
},
|
},
|
||||||
"output": [
|
"output": ["enemy"]
|
||||||
"enemy"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 8,
|
"id": 8,
|
||||||
"name": "MoveToTarget",
|
"name": "MoveToTarget",
|
||||||
"input": [
|
"input": ["enemy"]
|
||||||
"enemy"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -84,9 +73,7 @@
|
|||||||
{
|
{
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"name": "GetHp",
|
"name": "GetHp",
|
||||||
"output": [
|
"output": ["hp"]
|
||||||
"hp"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 11,
|
"id": 11,
|
||||||
@@ -94,9 +81,7 @@
|
|||||||
"args": {
|
"args": {
|
||||||
"lt": 50
|
"lt": 50
|
||||||
},
|
},
|
||||||
"input": [
|
"input": ["hp"]
|
||||||
"hp"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 12,
|
"id": 12,
|
||||||
|
|||||||
@@ -4,11 +4,9 @@
|
|||||||
"desc": "怪物测试AI",
|
"desc": "怪物测试AI",
|
||||||
"firstid": 1,
|
"firstid": 1,
|
||||||
"export": true,
|
"export": true,
|
||||||
"group": [
|
"group": ["Test"],
|
||||||
"Test"
|
|
||||||
],
|
|
||||||
"import": [],
|
"import": [],
|
||||||
"declvar": [],
|
"vars": [],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
@@ -22,9 +20,7 @@
|
|||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "GetHp",
|
"name": "GetHp",
|
||||||
"output": [
|
"output": ["hp"],
|
||||||
"hp"
|
|
||||||
],
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
@@ -41,9 +37,7 @@
|
|||||||
"value": "hp",
|
"value": "hp",
|
||||||
"gt": 50
|
"gt": 50
|
||||||
},
|
},
|
||||||
"input": [
|
"input": ["hp"]
|
||||||
"hp"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"firstid": 1,
|
"firstid": 1,
|
||||||
"group": [],
|
"group": [],
|
||||||
"import": [],
|
"import": [],
|
||||||
"declvar": [],
|
"vars": [],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"firstid": 1,
|
"firstid": 1,
|
||||||
"group": [],
|
"group": [],
|
||||||
"import": [],
|
"import": [],
|
||||||
"declvar": [],
|
"vars": [],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
@@ -14,16 +14,12 @@
|
|||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "Cmp",
|
"name": "Cmp",
|
||||||
"input": [
|
"input": ["d"]
|
||||||
"d"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"input": [
|
"input": ["in"]
|
||||||
"in"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"firstid": 1,
|
"firstid": 1,
|
||||||
"group": [],
|
"group": [],
|
||||||
"import": [],
|
"import": [],
|
||||||
"declvar": [],
|
"vars": [],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"firstid": 1,
|
"firstid": 1,
|
||||||
"group": [],
|
"group": [],
|
||||||
"import": [],
|
"import": [],
|
||||||
"declvar": [],
|
"vars": [],
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
@@ -15,9 +15,7 @@
|
|||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "Cmp",
|
"name": "Cmp",
|
||||||
"args": {},
|
"args": {},
|
||||||
"input": [
|
"input": ["x"]
|
||||||
"x"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
Submodule src/behavior3 updated: b3e20e8578...9283002a96.
+147
-1126
File diff suppressed because it is too large.
Load diff
@@ -857,6 +857,7 @@ export const Explorer: FC = () => {
|
|||||||
<Dropdown menu={{ items: contextMenu, onClick }} trigger={["contextMenu"]}>
|
<Dropdown menu={{ items: contextMenu, onClick }} trigger={["contextMenu"]}>
|
||||||
<div>
|
<div>
|
||||||
<DirectoryTree
|
<DirectoryTree
|
||||||
|
virtual
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
treeData={workspace.fileTree ? [workspace.fileTree] : []}
|
treeData={workspace.fileTree ? [workspace.fileTree] : []}
|
||||||
fieldNames={{ key: "path", title: "path" }}
|
fieldNames={{ key: "path", title: "path" }}
|
||||||
@@ -955,6 +956,7 @@ export const Explorer: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<DirectoryTree
|
<DirectoryTree
|
||||||
|
virtual
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
fieldNames={{ key: "path", title: "path" }}
|
fieldNames={{ key: "path", title: "path" }}
|
||||||
treeData={[nodeTree]}
|
treeData={[nodeTree]}
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
+111
-71
@@ -29,7 +29,7 @@ import { ExpressionEvaluator } from "../behavior3/src/behavior3";
|
|||||||
import { EditNode, EditTree, useWorkspace } from "../contexts/workspace-context";
|
import { EditNode, EditTree, useWorkspace } from "../contexts/workspace-context";
|
||||||
import {
|
import {
|
||||||
hasArgOptions,
|
hasArgOptions,
|
||||||
ImportDef,
|
ImportDecl,
|
||||||
isBoolType,
|
isBoolType,
|
||||||
isExprType,
|
isExprType,
|
||||||
isFloatType,
|
isFloatType,
|
||||||
@@ -37,9 +37,8 @@ import {
|
|||||||
isJsonType,
|
isJsonType,
|
||||||
isStringType,
|
isStringType,
|
||||||
NodeArg,
|
NodeArg,
|
||||||
NodeModel,
|
NodeData,
|
||||||
TreeGraphData,
|
VarDecl,
|
||||||
VarDef,
|
|
||||||
} from "../misc/b3type";
|
} from "../misc/b3type";
|
||||||
import {
|
import {
|
||||||
checkNodeArgValue,
|
checkNodeArgValue,
|
||||||
@@ -47,12 +46,12 @@ import {
|
|||||||
getNodeArgRawType,
|
getNodeArgRawType,
|
||||||
isNodeArgArray,
|
isNodeArgArray,
|
||||||
isNodeArgOptional,
|
isNodeArgOptional,
|
||||||
|
isValidChildren,
|
||||||
isValidVariableName,
|
isValidVariableName,
|
||||||
isVariadic,
|
isVariadic,
|
||||||
parseExpr,
|
parseExpr,
|
||||||
usingGroups,
|
|
||||||
usingVars,
|
|
||||||
} from "../misc/b3util";
|
} from "../misc/b3util";
|
||||||
|
import { message } from "../misc/hooks";
|
||||||
import i18n from "../misc/i18n";
|
import i18n from "../misc/i18n";
|
||||||
import { Hotkey, isMacos } from "../misc/keys";
|
import { Hotkey, isMacos } from "../misc/keys";
|
||||||
import { mergeClassNames } from "../misc/util";
|
import { mergeClassNames } from "../misc/util";
|
||||||
@@ -61,7 +60,7 @@ interface OptionType extends DefaultOptionType {
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VarItem extends VarDef {
|
interface VarItem extends VarDecl {
|
||||||
count?: number;
|
count?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,11 +68,11 @@ interface VarItemProps {
|
|||||||
name: number;
|
name: number;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
value?: VarItem;
|
value?: VarItem;
|
||||||
onChange?: (vardef: VarItem) => void;
|
onChange?: (value: VarItem) => void;
|
||||||
onRemove?: (name: number | number[]) => void;
|
onRemove?: (name: number | number[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VarDefItem: FC<VarItemProps> = ({ name, onChange, onRemove, disabled, ...props }) => {
|
const VarDeclItem: FC<VarItemProps> = ({ name, onChange, onRemove, disabled, ...props }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const form = useFormInstance();
|
const form = useFormInstance();
|
||||||
const [value, setValue] = useState<VarItem>(props.value ?? { name: "", desc: "" });
|
const [value, setValue] = useState<VarItem>(props.value ?? { name: "", desc: "" });
|
||||||
@@ -152,6 +151,7 @@ const TreeInspector: FC = () => {
|
|||||||
relative: state.relative,
|
relative: state.relative,
|
||||||
open: state.open,
|
open: state.open,
|
||||||
workdir: state.workdir,
|
workdir: state.workdir,
|
||||||
|
usingVars: state.usingVars,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -160,7 +160,7 @@ const TreeInspector: FC = () => {
|
|||||||
// using count
|
// using count
|
||||||
const usingCount: Record<string, number> = useMemo(() => {
|
const usingCount: Record<string, number> = useMemo(() => {
|
||||||
const count: Record<string, number> = {};
|
const count: Record<string, number> = {};
|
||||||
const collect = (node: TreeGraphData) => {
|
const collect = (node: NodeData) => {
|
||||||
const def = workspace.nodeDefs.get(node.name);
|
const def = workspace.nodeDefs.get(node.name);
|
||||||
if (def.input) {
|
if (def.input) {
|
||||||
node.input?.forEach((v) => {
|
node.input?.forEach((v) => {
|
||||||
@@ -195,7 +195,7 @@ const TreeInspector: FC = () => {
|
|||||||
};
|
};
|
||||||
collect(workspace.editingTree.root);
|
collect(workspace.editingTree.root);
|
||||||
return count;
|
return count;
|
||||||
}, [workspace.editingTree, workspace.nodeDefs]);
|
}, [workspace.editingTree, workspace.nodeDefs, workspace.usingVars]);
|
||||||
|
|
||||||
// auto complete for subtree
|
// auto complete for subtree
|
||||||
const subtreeOptions = useMemo(() => {
|
const subtreeOptions = useMemo(() => {
|
||||||
@@ -218,11 +218,11 @@ const TreeInspector: FC = () => {
|
|||||||
form.setFieldValue("name", workspace.editingTree.name);
|
form.setFieldValue("name", workspace.editingTree.name);
|
||||||
form.setFieldValue("desc", workspace.editingTree.desc);
|
form.setFieldValue("desc", workspace.editingTree.desc);
|
||||||
form.setFieldValue("export", workspace.editingTree.export !== false);
|
form.setFieldValue("export", workspace.editingTree.export !== false);
|
||||||
form.setFieldValue("firstid", workspace.editingTree.firstid);
|
form.setFieldValue("prefix", workspace.editingTree.prefix);
|
||||||
form.setFieldValue("group", workspace.editingTree.group);
|
form.setFieldValue("group", workspace.editingTree.group);
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
"declvar",
|
"vars",
|
||||||
workspace.editingTree.declvar.map((v) => ({
|
workspace.editingTree.vars.map((v) => ({
|
||||||
name: v.name,
|
name: v.name,
|
||||||
desc: v.desc,
|
desc: v.desc,
|
||||||
count: usingCount[v.name] ?? 0,
|
count: usingCount[v.name] ?? 0,
|
||||||
@@ -258,16 +258,16 @@ const TreeInspector: FC = () => {
|
|||||||
name: values.name,
|
name: values.name,
|
||||||
desc: values.desc,
|
desc: values.desc,
|
||||||
export: values.export,
|
export: values.export,
|
||||||
firstid: Number(values.firstid),
|
prefix: values.prefix,
|
||||||
group: ((values.group ?? []) as string[]).filter((g) => g).sort((a, b) => a.localeCompare(b)),
|
group: ((values.group ?? []) as string[]).filter((g) => g).sort((a, b) => a.localeCompare(b)),
|
||||||
declvar: (values.declvar as VarDef[])
|
vars: (values.vars as VarDecl[])
|
||||||
.filter((v) => v && v.name)
|
.filter((v) => v && v.name)
|
||||||
.map((v) => ({
|
.map((v) => ({
|
||||||
name: v.name,
|
name: v.name,
|
||||||
desc: v.desc,
|
desc: v.desc,
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => a.name.localeCompare(b.name)),
|
.sort((a, b) => a.name.localeCompare(b.name)),
|
||||||
import: (values.import as ImportDef[])
|
import: (values.import as ImportDecl[])
|
||||||
.filter((v) => v && v.path)
|
.filter((v) => v && v.path)
|
||||||
.sort((a, b) => a.path.localeCompare(b.path))
|
.sort((a, b) => a.path.localeCompare(b.path))
|
||||||
.map((v) => ({
|
.map((v) => ({
|
||||||
@@ -299,8 +299,8 @@ const TreeInspector: FC = () => {
|
|||||||
<Form.Item name="desc" label={t("tree.desc")}>
|
<Form.Item name="desc" label={t("tree.desc")}>
|
||||||
<TextArea autoSize onBlur={form.submit} />
|
<TextArea autoSize onBlur={form.submit} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="firstid" label={t("tree.firstid")}>
|
<Form.Item name="prefix" label={t("tree.prefix")}>
|
||||||
<InputNumber min={1} onBlur={form.submit} />
|
<Input onBlur={form.submit} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="export" label={t("tree.export")} valuePropName="checked">
|
<Form.Item name="export" label={t("tree.export")} valuePropName="checked">
|
||||||
<Switch onChange={() => form.submit()} />
|
<Switch onChange={() => form.submit()} />
|
||||||
@@ -326,7 +326,7 @@ const TreeInspector: FC = () => {
|
|||||||
<Divider orientation="left">
|
<Divider orientation="left">
|
||||||
<h4>{t("tree.vars")}</h4>
|
<h4>{t("tree.vars")}</h4>
|
||||||
</Divider>
|
</Divider>
|
||||||
<Form.List name="declvar">
|
<Form.List name="vars">
|
||||||
{(fields, { add, remove }, { errors }) => (
|
{(fields, { add, remove }, { errors }) => (
|
||||||
<div style={{ display: "flex", flexDirection: "column", rowGap: 0 }}>
|
<div style={{ display: "flex", flexDirection: "column", rowGap: 0 }}>
|
||||||
{fields.map((item) => (
|
{fields.map((item) => (
|
||||||
@@ -351,7 +351,7 @@ const TreeInspector: FC = () => {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<VarDefItem name={item.name} onRemove={remove} />
|
<VarDeclItem name={item.name} onRemove={remove} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -421,7 +421,7 @@ const TreeInspector: FC = () => {
|
|||||||
<div style={{ display: "flex", flexDirection: "column", rowGap: 0 }}>
|
<div style={{ display: "flex", flexDirection: "column", rowGap: 0 }}>
|
||||||
{vars.map((v) => (
|
{vars.map((v) => (
|
||||||
<Form.Item key={v.key} name={v.name} style={{ marginBottom: 2 }}>
|
<Form.Item key={v.key} name={v.name} style={{ marginBottom: 2 }}>
|
||||||
<VarDefItem name={v.name} disabled={true} />
|
<VarDeclItem name={v.name} disabled={true} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -477,7 +477,7 @@ const TreeInspector: FC = () => {
|
|||||||
<div style={{ display: "flex", flexDirection: "column", rowGap: 0 }}>
|
<div style={{ display: "flex", flexDirection: "column", rowGap: 0 }}>
|
||||||
{vars.map((v) => (
|
{vars.map((v) => (
|
||||||
<Form.Item key={v.key} name={v.name} style={{ marginBottom: 2 }}>
|
<Form.Item key={v.key} name={v.name} style={{ marginBottom: 2 }}>
|
||||||
<VarDefItem name={v.name} disabled={true} />
|
<VarDeclItem name={v.name} disabled={true} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -513,7 +513,12 @@ const TreeInspector: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateArg = (node: NodeModel, arg: NodeArg, value: unknown) => {
|
const validateArg = (
|
||||||
|
node: NodeData,
|
||||||
|
arg: NodeArg,
|
||||||
|
value: unknown,
|
||||||
|
usingVars: Record<string, VarDecl> | null
|
||||||
|
) => {
|
||||||
const type = getNodeArgRawType(arg);
|
const type = getNodeArgRawType(arg);
|
||||||
const required = !isNodeArgOptional(arg);
|
const required = !isNodeArgOptional(arg);
|
||||||
if (isExprType(type) && value) {
|
if (isExprType(type) && value) {
|
||||||
@@ -559,6 +564,8 @@ const NodeInspector: FC = () => {
|
|||||||
fileTree: state.fileTree,
|
fileTree: state.fileTree,
|
||||||
groupDefs: state.groupDefs,
|
groupDefs: state.groupDefs,
|
||||||
nodeDefs: state.nodeDefs,
|
nodeDefs: state.nodeDefs,
|
||||||
|
usingGroups: state.usingGroups,
|
||||||
|
usingVars: state.usingVars,
|
||||||
onEditingNode: state.onEditingNode,
|
onEditingNode: state.onEditingNode,
|
||||||
relative: state.relative,
|
relative: state.relative,
|
||||||
}))
|
}))
|
||||||
@@ -572,12 +579,22 @@ const NodeInspector: FC = () => {
|
|||||||
100
|
100
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
if (form.getFieldsError().some((e) => e.errors.length > 0)) {
|
||||||
|
const data = workspace.editingNode.data;
|
||||||
|
const editor = workspace.editing!;
|
||||||
|
const name = `${editor.data.prefix}${data.id} ${data.name}`;
|
||||||
|
message.error(t("node.editFailed", { name }));
|
||||||
|
}
|
||||||
|
form.submit();
|
||||||
|
};
|
||||||
|
|
||||||
// set form values
|
// set form values
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const data = workspace.editingNode.data;
|
const data = workspace.editingNode.data;
|
||||||
const def = workspace.nodeDefs.get(workspace.editingNode.data.name);
|
const def = workspace.nodeDefs.get(workspace.editingNode.data.name);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
form.setFieldValue("id", data.id);
|
form.setFieldValue("id", workspace.editingNode.prefix + data.id);
|
||||||
form.setFieldValue("name", data.name);
|
form.setFieldValue("name", data.name);
|
||||||
form.setFieldValue("type", def.type);
|
form.setFieldValue("type", def.type);
|
||||||
form.setFieldValue("desc", data.desc || def.desc);
|
form.setFieldValue("desc", data.desc || def.desc);
|
||||||
@@ -647,11 +664,12 @@ const NodeInspector: FC = () => {
|
|||||||
const inoutVarOptions = useMemo(() => {
|
const inoutVarOptions = useMemo(() => {
|
||||||
const options: OptionType[] = [];
|
const options: OptionType[] = [];
|
||||||
const filter: Record<string, boolean> = {};
|
const filter: Record<string, boolean> = {};
|
||||||
const collect = (node?: TreeGraphData) => {
|
const collect = (node?: NodeData) => {
|
||||||
if (node) {
|
if (node) {
|
||||||
|
const def = workspace.nodeDefs.get(node.name);
|
||||||
node.input?.forEach((v, i) => {
|
node.input?.forEach((v, i) => {
|
||||||
let desc: string;
|
let desc: string;
|
||||||
const inputDef = node.def.input;
|
const inputDef = def.input;
|
||||||
if (inputDef && i >= inputDef.length && isVariadic(inputDef, -1)) {
|
if (inputDef && i >= inputDef.length && isVariadic(inputDef, -1)) {
|
||||||
desc = inputDef[inputDef.length - 1];
|
desc = inputDef[inputDef.length - 1];
|
||||||
} else {
|
} else {
|
||||||
@@ -664,7 +682,7 @@ const NodeInspector: FC = () => {
|
|||||||
});
|
});
|
||||||
node.output?.forEach((v, i) => {
|
node.output?.forEach((v, i) => {
|
||||||
let desc: string;
|
let desc: string;
|
||||||
const outputDef = node.def.output;
|
const outputDef = def.output;
|
||||||
if (outputDef && i >= outputDef.length && isVariadic(outputDef, -1)) {
|
if (outputDef && i >= outputDef.length && isVariadic(outputDef, -1)) {
|
||||||
desc = outputDef[outputDef.length - 1];
|
desc = outputDef[outputDef.length - 1];
|
||||||
} else {
|
} else {
|
||||||
@@ -678,18 +696,18 @@ const NodeInspector: FC = () => {
|
|||||||
node.children?.forEach((child) => collect(child));
|
node.children?.forEach((child) => collect(child));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (usingVars) {
|
if (workspace.usingVars) {
|
||||||
Object.values(usingVars ?? {}).forEach((v) => {
|
Object.values(workspace.usingVars).forEach((v) => {
|
||||||
if (!filter[v.name]) {
|
if (!filter[v.name]) {
|
||||||
options.push({ label: `${v.name}(${v.desc})`, value: v.name });
|
options.push({ label: `${v.name}(${v.desc})`, value: v.name });
|
||||||
filter[v.name] = true;
|
filter[v.name] = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
collect(workspace.editing?.root);
|
collect(workspace.editing?.data.root);
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
}, [workspace.editing, usingVars]);
|
}, [workspace.editing, workspace.usingVars]);
|
||||||
|
|
||||||
// auto complete for subtree
|
// auto complete for subtree
|
||||||
const subtreeOptions = useMemo(() => {
|
const subtreeOptions = useMemo(() => {
|
||||||
@@ -708,12 +726,12 @@ const NodeInspector: FC = () => {
|
|||||||
|
|
||||||
const editingNode = workspace.editingNode;
|
const editingNode = workspace.editingNode;
|
||||||
const def = workspace.nodeDefs.get(editingNode.data.name);
|
const def = workspace.nodeDefs.get(editingNode.data.name);
|
||||||
const disabled = !editingNode.editable;
|
const disabled = editingNode.disabled;
|
||||||
|
|
||||||
// update value
|
// update value
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const finish = (values: any) => {
|
const finish = (values: any) => {
|
||||||
const data = {} as NodeModel;
|
const data = {} as NodeData;
|
||||||
data.id = editingNode.data.id;
|
data.id = editingNode.data.id;
|
||||||
data.name = values.name;
|
data.name = values.name;
|
||||||
data.debug = values.debug || undefined;
|
data.debug = values.debug || undefined;
|
||||||
@@ -804,7 +822,8 @@ const NodeInspector: FC = () => {
|
|||||||
debug: editingNode.data.debug,
|
debug: editingNode.data.debug,
|
||||||
disabled: editingNode.data.disabled,
|
disabled: editingNode.data.disabled,
|
||||||
},
|
},
|
||||||
editable: editingNode.editable,
|
prefix: editingNode.prefix,
|
||||||
|
disabled: editingNode.disabled,
|
||||||
});
|
});
|
||||||
finish(form.getFieldsValue());
|
finish(form.getFieldsValue());
|
||||||
} else {
|
} else {
|
||||||
@@ -835,6 +854,9 @@ const NodeInspector: FC = () => {
|
|||||||
labelCol={{ span: "auto" }}
|
labelCol={{ span: "auto" }}
|
||||||
onFinish={finish}
|
onFinish={finish}
|
||||||
>
|
>
|
||||||
|
<Form.Item name="id" label={t("node.id")}>
|
||||||
|
<Input disabled={true} />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item name="type" label={t("node.type")}>
|
<Form.Item name="type" label={t("node.type")}>
|
||||||
<Input disabled={true} />
|
<Input disabled={true} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -845,7 +867,7 @@ const NodeInspector: FC = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
validator() {
|
validator() {
|
||||||
if (def.group && !def.group.some((g) => usingGroups[g])) {
|
if (def.group && !def.group.some((g) => workspace.usingGroups?.[g])) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(t("node.groupNotEnabled", { group: def.group }))
|
new Error(t("node.groupNotEnabled", { group: def.group }))
|
||||||
);
|
);
|
||||||
@@ -869,7 +891,7 @@ const NodeInspector: FC = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
validator() {
|
validator() {
|
||||||
if (editingNode.limitError) {
|
if (!isValidChildren(editingNode.data)) {
|
||||||
return Promise.reject(new Error(t("node.invalidChildren")));
|
return Promise.reject(new Error(t("node.invalidChildren")));
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@@ -908,17 +930,17 @@ const NodeInspector: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="desc" label={t("node.desc")}>
|
<Form.Item name="desc" label={t("node.desc")}>
|
||||||
<TextArea autoSize disabled={disabled} onBlur={form.submit} />
|
<TextArea autoSize disabled={disabled} onBlur={submit} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("node.debug")} name="debug" valuePropName="checked">
|
<Form.Item label={t("node.debug")} name="debug" valuePropName="checked">
|
||||||
<Switch disabled={disabled && !editingNode.data.path} onChange={form.submit} />
|
<Switch disabled={disabled && !editingNode.data.path} onChange={submit} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("node.disabled")} name="disabled" valuePropName="checked">
|
<Form.Item label={t("node.disabled")} name="disabled" valuePropName="checked">
|
||||||
<Switch disabled={disabled && !editingNode.data.path} onChange={form.submit} />
|
<Switch disabled={disabled && !editingNode.data.path} onChange={submit} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("node.subtree")} name="path">
|
<Form.Item label={t("node.subtree")} name="path">
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
disabled={disabled && !editingNode.data.path}
|
disabled={disabled && !editingNode.subtreeEditable}
|
||||||
options={subtreeOptions}
|
options={subtreeOptions}
|
||||||
onBlur={changeSubtree}
|
onBlur={changeSubtree}
|
||||||
onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()}
|
onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()}
|
||||||
@@ -958,7 +980,11 @@ const NodeInspector: FC = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
if (value && usingVars && !usingVars[value]) {
|
if (
|
||||||
|
value &&
|
||||||
|
workspace.usingVars &&
|
||||||
|
!workspace.usingVars[value]
|
||||||
|
) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(
|
new Error(
|
||||||
t("node.undefinedVariable", { variable: value })
|
t("node.undefinedVariable", { variable: value })
|
||||||
@@ -978,7 +1004,7 @@ const NodeInspector: FC = () => {
|
|||||||
<AutoComplete
|
<AutoComplete
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
options={inoutVarOptions}
|
options={inoutVarOptions}
|
||||||
onBlur={form.submit}
|
onBlur={submit}
|
||||||
onInputKeyDown={(e) =>
|
onInputKeyDown={(e) =>
|
||||||
e.code === Hotkey.Escape && e.preventDefault()
|
e.code === Hotkey.Escape && e.preventDefault()
|
||||||
}
|
}
|
||||||
@@ -994,7 +1020,7 @@ const NodeInspector: FC = () => {
|
|||||||
style={{ marginBottom: "6px" }}
|
style={{ marginBottom: "6px" }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
remove(field.name);
|
remove(field.name);
|
||||||
form.submit();
|
submit();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -1027,7 +1053,7 @@ const NodeInspector: FC = () => {
|
|||||||
{ required, message: t("fieldRequired", { field: desc }) },
|
{ required, message: t("fieldRequired", { field: desc }) },
|
||||||
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
if (value && usingVars && !usingVars[value]) {
|
if (value && workspace.usingVars && !workspace.usingVars[value]) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(t("node.undefinedVariable", { variable: value }))
|
new Error(t("node.undefinedVariable", { variable: value }))
|
||||||
);
|
);
|
||||||
@@ -1067,7 +1093,7 @@ const NodeInspector: FC = () => {
|
|||||||
<AutoComplete
|
<AutoComplete
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
options={inoutVarOptions}
|
options={inoutVarOptions}
|
||||||
onBlur={form.submit}
|
onBlur={submit}
|
||||||
onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()}
|
onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()}
|
||||||
filterOption={(inputValue: string, option?: OptionType) => {
|
filterOption={(inputValue: string, option?: OptionType) => {
|
||||||
const label = option!.label as string;
|
const label = option!.label as string;
|
||||||
@@ -1112,39 +1138,44 @@ const NodeInspector: FC = () => {
|
|||||||
},
|
},
|
||||||
() => ({
|
() => ({
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
return validateArg(editingNode.data, arg, value);
|
return validateArg(
|
||||||
|
editingNode.data,
|
||||||
|
arg,
|
||||||
|
value,
|
||||||
|
workspace.usingVars
|
||||||
|
);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{!hasArgOptions(arg) && isStringType(type) && (
|
{!hasArgOptions(arg) && isStringType(type) && (
|
||||||
<TextArea autoSize disabled={disabled} onBlur={form.submit} />
|
<TextArea autoSize disabled={disabled} onBlur={submit} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isJsonType(type) && (
|
{!hasArgOptions(arg) && isJsonType(type) && (
|
||||||
<TextArea autoSize disabled={disabled} onBlur={form.submit} />
|
<TextArea autoSize disabled={disabled} onBlur={submit} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isIntType(type) && (
|
{!hasArgOptions(arg) && isIntType(type) && (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onBlur={form.submit}
|
onBlur={submit}
|
||||||
precision={0}
|
precision={0}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isFloatType(type) && (
|
{!hasArgOptions(arg) && isFloatType(type) && (
|
||||||
<InputNumber disabled={disabled} onBlur={form.submit} />
|
<InputNumber disabled={disabled} onBlur={submit} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isBoolType(type) && (
|
{!hasArgOptions(arg) && isBoolType(type) && (
|
||||||
<Switch disabled={disabled} onChange={form.submit} />
|
<Switch disabled={disabled} onChange={submit} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isExprType(type) && (
|
{!hasArgOptions(arg) && isExprType(type) && (
|
||||||
<Input disabled={disabled} onBlur={form.submit} />
|
<Input disabled={disabled} onBlur={submit} />
|
||||||
)}
|
)}
|
||||||
{hasArgOptions(arg) && (
|
{hasArgOptions(arg) && (
|
||||||
<Select
|
<Select
|
||||||
showSearch
|
showSearch
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onBlur={form.submit}
|
onBlur={submit}
|
||||||
onChange={form.submit}
|
onChange={submit}
|
||||||
options={(arg.options ?? []).map((option) => {
|
options={(arg.options ?? []).map((option) => {
|
||||||
return {
|
return {
|
||||||
value: option.value,
|
value: option.value,
|
||||||
@@ -1164,7 +1195,7 @@ const NodeInspector: FC = () => {
|
|||||||
style={{ marginBottom: "6px" }}
|
style={{ marginBottom: "6px" }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
remove(item.name);
|
remove(item.name);
|
||||||
form.submit();
|
submit();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -1182,7 +1213,7 @@ const NodeInspector: FC = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
add(arg.default ?? isBoolType(type) ? false : "");
|
add(arg.default ?? isBoolType(type) ? false : "");
|
||||||
if (isBoolType(type)) {
|
if (isBoolType(type)) {
|
||||||
form.submit();
|
submit();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
@@ -1210,7 +1241,12 @@ const NodeInspector: FC = () => {
|
|||||||
{ required, message: t("fieldRequired", { field: arg.desc }) },
|
{ required, message: t("fieldRequired", { field: arg.desc }) },
|
||||||
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
||||||
async validator(_, value) {
|
async validator(_, value) {
|
||||||
return validateArg(editingNode.data, arg, value).then((result) => {
|
return validateArg(
|
||||||
|
editingNode.data,
|
||||||
|
arg,
|
||||||
|
value,
|
||||||
|
workspace.usingVars
|
||||||
|
).then((result) => {
|
||||||
value = result;
|
value = result;
|
||||||
if (!arg.oneof) {
|
if (!arg.oneof) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@@ -1247,29 +1283,29 @@ const NodeInspector: FC = () => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{!hasArgOptions(arg) && isStringType(type) && (
|
{!hasArgOptions(arg) && isStringType(type) && (
|
||||||
<TextArea autoSize disabled={disabled} onBlur={form.submit} />
|
<TextArea autoSize disabled={disabled} onBlur={submit} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isJsonType(type) && (
|
{!hasArgOptions(arg) && isJsonType(type) && (
|
||||||
<TextArea autoSize disabled={disabled} onBlur={form.submit} />
|
<TextArea autoSize disabled={disabled} onBlur={submit} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isIntType(type) && (
|
{!hasArgOptions(arg) && isIntType(type) && (
|
||||||
<InputNumber disabled={disabled} onBlur={form.submit} precision={0} />
|
<InputNumber disabled={disabled} onBlur={submit} precision={0} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isFloatType(type) && (
|
{!hasArgOptions(arg) && isFloatType(type) && (
|
||||||
<InputNumber disabled={disabled} onBlur={form.submit} />
|
<InputNumber disabled={disabled} onBlur={submit} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isBoolType(type) && (
|
{!hasArgOptions(arg) && isBoolType(type) && (
|
||||||
<Switch disabled={disabled} onChange={form.submit} />
|
<Switch disabled={disabled} onChange={submit} />
|
||||||
)}
|
)}
|
||||||
{!hasArgOptions(arg) && isExprType(type) && (
|
{!hasArgOptions(arg) && isExprType(type) && (
|
||||||
<Input disabled={disabled} onBlur={form.submit} />
|
<Input disabled={disabled} onBlur={submit} />
|
||||||
)}
|
)}
|
||||||
{hasArgOptions(arg) && (
|
{hasArgOptions(arg) && (
|
||||||
<Select
|
<Select
|
||||||
showSearch
|
showSearch
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onBlur={form.submit}
|
onBlur={submit}
|
||||||
onChange={form.submit}
|
onChange={submit}
|
||||||
options={(arg.options ?? []).map((option) => {
|
options={(arg.options ?? []).map((option) => {
|
||||||
return {
|
return {
|
||||||
value: option.value,
|
value: option.value,
|
||||||
@@ -1319,7 +1355,11 @@ const NodeInspector: FC = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
if (value && usingVars && !usingVars[value]) {
|
if (
|
||||||
|
value &&
|
||||||
|
workspace.usingVars &&
|
||||||
|
!workspace.usingVars[value]
|
||||||
|
) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(
|
new Error(
|
||||||
t("node.undefinedVariable", { variable: value })
|
t("node.undefinedVariable", { variable: value })
|
||||||
@@ -1339,7 +1379,7 @@ const NodeInspector: FC = () => {
|
|||||||
<AutoComplete
|
<AutoComplete
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
options={inoutVarOptions}
|
options={inoutVarOptions}
|
||||||
onBlur={form.submit}
|
onBlur={submit}
|
||||||
onInputKeyDown={(e) =>
|
onInputKeyDown={(e) =>
|
||||||
e.code === Hotkey.Escape && e.preventDefault()
|
e.code === Hotkey.Escape && e.preventDefault()
|
||||||
}
|
}
|
||||||
@@ -1355,7 +1395,7 @@ const NodeInspector: FC = () => {
|
|||||||
style={{ marginBottom: "6px" }}
|
style={{ marginBottom: "6px" }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
remove(field.name);
|
remove(field.name);
|
||||||
form.submit();
|
submit();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -1388,7 +1428,7 @@ const NodeInspector: FC = () => {
|
|||||||
{ required, message: t("fieldRequired", { field: desc }) },
|
{ required, message: t("fieldRequired", { field: desc }) },
|
||||||
{
|
{
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
if (value && usingVars && !usingVars[value]) {
|
if (value && workspace.usingVars && !workspace.usingVars[value]) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(t("node.undefinedVariable", { variable: value }))
|
new Error(t("node.undefinedVariable", { variable: value }))
|
||||||
);
|
);
|
||||||
@@ -1404,7 +1444,7 @@ const NodeInspector: FC = () => {
|
|||||||
<AutoComplete
|
<AutoComplete
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
options={inoutVarOptions}
|
options={inoutVarOptions}
|
||||||
onBlur={form.submit}
|
onBlur={submit}
|
||||||
onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()}
|
onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()}
|
||||||
filterOption={(value: string, option?: OptionType) => {
|
filterOption={(value: string, option?: OptionType) => {
|
||||||
const label = option!.label as string;
|
const label = option!.label as string;
|
||||||
|
|||||||
+659
-489
File diff suppressed because it is too large.
Load diff
@@ -64,7 +64,7 @@ export const Workspace: FC = () => {
|
|||||||
useKeyDown(Hotkey.CloseEditor, null, (event) => {
|
useKeyDown(Hotkey.CloseEditor, null, (event) => {
|
||||||
if (workspace.editing) {
|
if (workspace.editing) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (workspace.editing.unsave) {
|
if (workspace.editing.changed) {
|
||||||
showSaveDialog(workspace.editing);
|
showSaveDialog(workspace.editing);
|
||||||
} else {
|
} else {
|
||||||
workspace.close(workspace.editing.path);
|
workspace.close(workspace.editing.path);
|
||||||
@@ -266,7 +266,7 @@ export const Workspace: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.onbeforeunload = (e) => {
|
window.onbeforeunload = (e) => {
|
||||||
const unsaves = workspace.editors.filter((editor) => editor.unsave);
|
const unsaves = workspace.editors.filter((editor) => editor.changed);
|
||||||
if (unsaves.length) {
|
if (unsaves.length) {
|
||||||
showSaveAllDialog(unsaves);
|
showSaveAllDialog(unsaves);
|
||||||
return false;
|
return false;
|
||||||
@@ -441,7 +441,7 @@ export const Workspace: FC = () => {
|
|||||||
if (action === "remove") {
|
if (action === "remove") {
|
||||||
const path = activeKey as string;
|
const path = activeKey as string;
|
||||||
const editor = workspace.find(path);
|
const editor = workspace.find(path);
|
||||||
if (editor && editor.unsave) {
|
if (editor && editor.changed) {
|
||||||
showSaveDialog(editor);
|
showSaveDialog(editor);
|
||||||
} else {
|
} else {
|
||||||
workspace.close(path);
|
workspace.close(path);
|
||||||
@@ -470,11 +470,11 @@ export const Workspace: FC = () => {
|
|||||||
}}
|
}}
|
||||||
title={<div style={{ width: "max-content" }}>{v.path}</div>}
|
title={<div style={{ width: "max-content" }}>{v.path}</div>}
|
||||||
>
|
>
|
||||||
{`${Path.basename(v.path)}${v.unsave ? "*" : ""}`}
|
{`${Path.basename(v.path)}${v.changed ? "*" : ""}`}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
),
|
),
|
||||||
key: v.path,
|
key: v.path,
|
||||||
children: <Editor data={v} onUpdate={forceUpdate} />,
|
children: <Editor data={v} onChange={forceUpdate} />,
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { app } from "@electron/remote";
|
import { app } from "@electron/remote";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
import { NodeLayout } from "../misc/b3type";
|
||||||
import { readJson, writeJson } from "../misc/util";
|
import { readJson, writeJson } from "../misc/util";
|
||||||
import { useWorkspace } from "./workspace-context";
|
import { useWorkspace } from "./workspace-context";
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ const settingPath = app.getPath("userData") + "/settings.json";
|
|||||||
export type SettingModel = {
|
export type SettingModel = {
|
||||||
recent: string[];
|
recent: string[];
|
||||||
buildDir: string;
|
buildDir: string;
|
||||||
layout?: "compact" | "normal";
|
layout: NodeLayout;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SettingStore = {
|
export type SettingStore = {
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
import { Matrix, TreeGraph } from "@antv/g6";
|
|
||||||
import { BrowserWindow, dialog } from "@electron/remote";
|
import { BrowserWindow, dialog } from "@electron/remote";
|
||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { NodeDef } from "../behavior3/src/behavior3";
|
import { NodeDef } from "../behavior3/src/behavior3";
|
||||||
import {
|
import { FileVarDecl, ImportDecl, NodeData, TreeData, VarDecl } from "../misc/b3type";
|
||||||
FileVarDecl,
|
|
||||||
ImportDef,
|
|
||||||
NodeModel,
|
|
||||||
TreeGraphData,
|
|
||||||
TreeModel,
|
|
||||||
VarDef,
|
|
||||||
} from "../misc/b3type";
|
|
||||||
import * as b3util from "../misc/b3util";
|
import * as b3util from "../misc/b3util";
|
||||||
import { message } from "../misc/hooks";
|
import { message } from "../misc/hooks";
|
||||||
import i18n from "../misc/i18n";
|
import i18n from "../misc/i18n";
|
||||||
@@ -24,6 +16,7 @@ import { useSetting } from "./setting-context";
|
|||||||
let buildDir: string | undefined;
|
let buildDir: string | undefined;
|
||||||
|
|
||||||
export type EditEvent =
|
export type EditEvent =
|
||||||
|
| "close"
|
||||||
| "save"
|
| "save"
|
||||||
| "copy"
|
| "copy"
|
||||||
| "paste"
|
| "paste"
|
||||||
@@ -45,25 +38,14 @@ export type EditEvent =
|
|||||||
|
|
||||||
export class EditorStore {
|
export class EditorStore {
|
||||||
path: string;
|
path: string;
|
||||||
data: TreeModel;
|
data: TreeData;
|
||||||
|
|
||||||
root: TreeGraphData;
|
|
||||||
declare: FileVarDecl;
|
declare: FileVarDecl;
|
||||||
|
|
||||||
autoId: number = 1;
|
changed: boolean = false;
|
||||||
dragSrcId?: string;
|
mtime: number;
|
||||||
dragDstId?: string;
|
|
||||||
unsave: boolean = false;
|
|
||||||
modifiedTime: number = Date.now();
|
|
||||||
alertReload: boolean = false;
|
alertReload: boolean = false;
|
||||||
searchingText?: string;
|
focusId?: string | null;
|
||||||
|
|
||||||
historyStack: string[] = [];
|
|
||||||
historyIndex: number = 0;
|
|
||||||
selectedId: string | null = null;
|
|
||||||
|
|
||||||
graphMatrix?: Matrix;
|
|
||||||
graph!: TreeGraph;
|
|
||||||
|
|
||||||
dispatch!: (event: EditEvent, data?: unknown) => void;
|
dispatch!: (event: EditEvent, data?: unknown) => void;
|
||||||
|
|
||||||
@@ -71,25 +53,12 @@ export class EditorStore {
|
|||||||
this.path = path;
|
this.path = path;
|
||||||
this.data = readTree(path);
|
this.data = readTree(path);
|
||||||
this.data.name = Path.basenameWithoutExt(path);
|
this.data.name = Path.basenameWithoutExt(path);
|
||||||
this.root = b3util.createTreeData(this.data.root);
|
this.mtime = fs.statSync(path).mtimeMs;
|
||||||
this.modifiedTime = fs.statSync(path).mtimeMs;
|
|
||||||
this.declare = {
|
this.declare = {
|
||||||
import: this.data.import.map((v) => ({ path: v, vars: [], depends: [] })),
|
import: this.data.import.map((v) => ({ path: v, vars: [], depends: [] })),
|
||||||
subtree: [],
|
subtree: [],
|
||||||
declvar: this.data.declvar.map((v) => ({ ...v })),
|
vars: this.data.vars.map((v) => ({ ...v })),
|
||||||
};
|
};
|
||||||
this.autoId = b3util.refreshTreeDataId(this.root, this.data.firstid);
|
|
||||||
this.historyStack.push(
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
...this.data,
|
|
||||||
root: b3util.createNode(this.root),
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
);
|
|
||||||
this.historyIndex = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +74,11 @@ export type FileTreeType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type EditNode = {
|
export type EditNode = {
|
||||||
data: NodeModel;
|
data: NodeData;
|
||||||
editable: boolean;
|
error?: boolean;
|
||||||
limitError?: boolean;
|
prefix: string;
|
||||||
|
disabled: boolean;
|
||||||
|
subtreeEditable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EditNodeDef = {
|
export type EditNodeDef = {
|
||||||
@@ -119,12 +90,12 @@ export type EditTree = {
|
|||||||
name: string;
|
name: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
export?: boolean;
|
export?: boolean;
|
||||||
firstid?: number;
|
prefix?: string;
|
||||||
group: string[];
|
group: string[];
|
||||||
import: ImportDef[];
|
import: ImportDecl[];
|
||||||
declvar: VarDef[];
|
vars: VarDecl[];
|
||||||
subtree: ImportDef[];
|
subtree: ImportDecl[];
|
||||||
root: TreeGraphData;
|
root: NodeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FileMeta = {
|
export type FileMeta = {
|
||||||
@@ -170,12 +141,12 @@ export type WorkspaceStore = {
|
|||||||
isShowingSearch: boolean;
|
isShowingSearch: boolean;
|
||||||
onShowingSearch: (isShowingSearch: boolean) => void;
|
onShowingSearch: (isShowingSearch: boolean) => void;
|
||||||
|
|
||||||
open: (path: string, selectedId?: number) => void;
|
open: (path: string, focusId?: string) => void;
|
||||||
edit: (path: string, selectedId?: number) => void;
|
edit: (path: string, focusId?: string) => void;
|
||||||
close: (path: string) => void;
|
close: (path: string) => void;
|
||||||
find: (path: string) => EditorStore | undefined;
|
find: (path: string) => EditorStore | undefined;
|
||||||
relative: (path: string) => string;
|
relative: (path: string) => string;
|
||||||
refresh: (path: string) => boolean;
|
refresh: (path: string) => void;
|
||||||
|
|
||||||
save: () => void;
|
save: () => void;
|
||||||
saveAs: () => void;
|
saveAs: () => void;
|
||||||
@@ -187,6 +158,8 @@ export type WorkspaceStore = {
|
|||||||
loadNodeDefs: () => void;
|
loadNodeDefs: () => void;
|
||||||
nodeDefs: b3util.NodeDefs;
|
nodeDefs: b3util.NodeDefs;
|
||||||
groupDefs: string[];
|
groupDefs: string[];
|
||||||
|
usingGroups: typeof b3util.usingGroups;
|
||||||
|
usingVars: typeof b3util.usingVars;
|
||||||
|
|
||||||
// edit node
|
// edit node
|
||||||
editingNode?: EditNode | null;
|
editingNode?: EditNode | null;
|
||||||
@@ -238,7 +211,7 @@ const loadFileTree = (workdir: string, filename: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const saveFile = (editor?: EditorStore) => {
|
const saveFile = (editor?: EditorStore) => {
|
||||||
if (editor?.unsave) {
|
if (editor?.changed) {
|
||||||
editor.dispatch("save");
|
editor.dispatch("save");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -427,7 +400,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
nodeDefs: get().nodeDefs,
|
nodeDefs: get().nodeDefs,
|
||||||
});
|
});
|
||||||
workspace.allFiles.forEach((file) => {
|
workspace.allFiles.forEach((file) => {
|
||||||
let tree: TreeModel | null = readTree(file.path);
|
let tree: TreeData | null = readTree(file.path);
|
||||||
tree = b3util.processBatch(tree, file.path, batch);
|
tree = b3util.processBatch(tree, file.path, batch);
|
||||||
if (tree) {
|
if (tree) {
|
||||||
batch.onWriteFile?.(file.path, tree);
|
batch.onWriteFile?.(file.path, tree);
|
||||||
@@ -492,7 +465,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
set({ isShowingSearch });
|
set({ isShowingSearch });
|
||||||
},
|
},
|
||||||
|
|
||||||
open: (path, selectedId) => {
|
open: (path, focusId) => {
|
||||||
path = Path.posixPath(path);
|
path = Path.posixPath(path);
|
||||||
const workspace = get();
|
const workspace = get();
|
||||||
let editor = workspace.editors.find((v) => v.path === path);
|
let editor = workspace.editors.find((v) => v.path === path);
|
||||||
@@ -502,7 +475,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
workspace.editors.push(editor);
|
workspace.editors.push(editor);
|
||||||
set({ editors: workspace.editors });
|
set({ editors: workspace.editors });
|
||||||
workspace.updateFileMeta(editor);
|
workspace.updateFileMeta(editor);
|
||||||
workspace.edit(editor.path, selectedId);
|
workspace.edit(editor.path, focusId);
|
||||||
|
|
||||||
if (b3util.isNewVersion(editor.data.version)) {
|
if (b3util.isNewVersion(editor.data.version)) {
|
||||||
message.warning(i18n.t("alertNewVersion", { version: editor.data.version }));
|
message.warning(i18n.t("alertNewVersion", { version: editor.data.version }));
|
||||||
@@ -512,19 +485,18 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
message.error(`invalid file: ${path}`);
|
message.error(`invalid file: ${path}`);
|
||||||
}
|
}
|
||||||
} else if (workspace.editing !== editor) {
|
} else if (workspace.editing !== editor) {
|
||||||
workspace.edit(editor.path, selectedId);
|
workspace.edit(editor.path, focusId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
edit: (path, selectedId) => {
|
edit: (path, focusId) => {
|
||||||
const workspace = get();
|
const workspace = get();
|
||||||
const editor = workspace.editors.find((v) => v.path === path);
|
const editor = workspace.editors.find((v) => v.path === path);
|
||||||
if (editor && selectedId) {
|
if (editor) {
|
||||||
editor.selectedId = selectedId.toString();
|
editor.focusId = focusId;
|
||||||
}
|
}
|
||||||
set({ editing: editor, editingNode: null, editingTree: null });
|
set({ editing: editor, editingNode: null, editingTree: null });
|
||||||
if (editor) {
|
if (editor) {
|
||||||
workspace.refresh(editor.path);
|
|
||||||
workspace.onEditingTree(editor);
|
workspace.onEditingTree(editor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -533,7 +505,9 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
const workspace = get();
|
const workspace = get();
|
||||||
const idx = workspace.editors.findIndex((v) => v.path === path);
|
const idx = workspace.editors.findIndex((v) => v.path === path);
|
||||||
const editors = workspace.editors.filter((v) => v.path !== path);
|
const editors = workspace.editors.filter((v) => v.path !== path);
|
||||||
|
const editor = workspace.editors.find((v) => v.path === path);
|
||||||
let editting = workspace.editing;
|
let editting = workspace.editing;
|
||||||
|
editor?.dispatch?.("close");
|
||||||
if (editors.length && path === editting?.path) {
|
if (editors.length && path === editting?.path) {
|
||||||
editting = editors[idx === editors.length ? idx - 1 : idx];
|
editting = editors[idx === editors.length ? idx - 1 : idx];
|
||||||
workspace.onEditingTree(editting);
|
workspace.onEditingTree(editting);
|
||||||
@@ -561,7 +535,11 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
if (!editor) {
|
if (!editor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return b3util.refreshDeclare(editor.root, editor.data.group, editor.declare);
|
b3util.refreshVarDecl(editor.data.root, editor.data.group, editor.declare);
|
||||||
|
set({
|
||||||
|
usingGroups: b3util.usingGroups,
|
||||||
|
usingVars: b3util.usingVars,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
save: () => {
|
save: () => {
|
||||||
@@ -592,7 +570,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
hasEvent = true;
|
hasEvent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (event === "change" && filename) {
|
if (filename && (event === "change" || workspace.allFiles.has(filename))) {
|
||||||
if (filename === "node-config.b3-setting") {
|
if (filename === "node-config.b3-setting") {
|
||||||
workspace.loadNodeDefs();
|
workspace.loadNodeDefs();
|
||||||
} else {
|
} else {
|
||||||
@@ -600,8 +578,8 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
const editor = workspace.find(fullpath);
|
const editor = workspace.find(fullpath);
|
||||||
const modified = fs.statSync(fullpath).mtimeMs;
|
const modified = fs.statSync(fullpath).mtimeMs;
|
||||||
b3util.files[Path.posixPath(filename)] = modified;
|
b3util.files[Path.posixPath(filename)] = modified;
|
||||||
if (editor && editor.modifiedTime + 500 < modified) {
|
if (editor && editor.mtime + 500 < modified) {
|
||||||
if (editor.unsave) {
|
if (editor.changed) {
|
||||||
editor.alertReload = true;
|
editor.alertReload = true;
|
||||||
set({ modifiedTime: Date.now() });
|
set({ modifiedTime: Date.now() });
|
||||||
} else {
|
} else {
|
||||||
@@ -642,7 +620,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
}
|
}
|
||||||
fileMeta.exists = fs.existsSync(fileNode.path);
|
fileMeta.exists = fs.existsSync(fileNode.path);
|
||||||
if (fileMeta.desc === undefined) {
|
if (fileMeta.desc === undefined) {
|
||||||
const file = readJson(fileNode.path) as TreeModel;
|
const file = readJson(fileNode.path) as TreeData;
|
||||||
fileMeta.desc = file.desc ?? "";
|
fileMeta.desc = file.desc ?? "";
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
@@ -668,6 +646,8 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
|
|
||||||
nodeDefs: new b3util.NodeDefs(),
|
nodeDefs: new b3util.NodeDefs(),
|
||||||
groupDefs: [],
|
groupDefs: [],
|
||||||
|
usingGroups: null,
|
||||||
|
usingVars: null,
|
||||||
loadNodeDefs: () => {
|
loadNodeDefs: () => {
|
||||||
const workspace = get();
|
const workspace = get();
|
||||||
b3util.initWorkdir(workspace.workdir, message.error.bind(message));
|
b3util.initWorkdir(workspace.workdir, message.error.bind(message));
|
||||||
@@ -691,10 +671,10 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
|||||||
set({
|
set({
|
||||||
editingTree: {
|
editingTree: {
|
||||||
...editor.data,
|
...editor.data,
|
||||||
root: editor.root,
|
root: editor.data.root,
|
||||||
import: editor.declare.import,
|
import: editor.declare.import,
|
||||||
declvar: editor.declare.declvar,
|
|
||||||
subtree: editor.declare.subtree,
|
subtree: editor.declare.subtree,
|
||||||
|
vars: editor.declare.vars,
|
||||||
},
|
},
|
||||||
editingNodeDef: null,
|
editingNodeDef: null,
|
||||||
editingNode: null,
|
editingNode: null,
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
export {}; // Make this a module
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Array<T> {
|
||||||
|
unique(): T[];
|
||||||
|
first(): T | undefined;
|
||||||
|
last(): T | undefined;
|
||||||
|
groupBy<K>(keyFn: (item: T) => K): Map<K, T[]>;
|
||||||
|
remove(item: T): T[];
|
||||||
|
removeAt(index: number): T | undefined;
|
||||||
|
insertAt(index: number, item: T): void;
|
||||||
|
emplace(item: T, target: T, where: "before" | "after"): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.unique = function <T>(this: T[]): T[] {
|
||||||
|
return [...new Set(this)];
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.first = function <T>(this: T[]): T | undefined {
|
||||||
|
return this[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.last = function <T>(this: T[]): T | undefined {
|
||||||
|
return this[this.length - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.remove = function <T>(this: T[], item: T): T[] {
|
||||||
|
const removed: T[] = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < this.length) {
|
||||||
|
if (this[i] === item) {
|
||||||
|
removed.push(this[i]);
|
||||||
|
this.splice(i, 1);
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.removeAt = function <T>(this: T[], index: number): T | undefined {
|
||||||
|
return this.splice(index, 1)[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.insertAt = function <T>(this: T[], index: number, item: T): void {
|
||||||
|
this.splice(index, 0, item);
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.groupBy = function <T, K>(this: T[], keyFn: (item: T) => K): Map<K, T[]> {
|
||||||
|
return this.reduce((map, item) => {
|
||||||
|
const key = keyFn(item);
|
||||||
|
const group = map.get(key) || [];
|
||||||
|
group.push(item);
|
||||||
|
map.set(key, group);
|
||||||
|
return map;
|
||||||
|
}, new Map<K, T[]>());
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.emplace = function <T>(
|
||||||
|
this: T[],
|
||||||
|
item: T,
|
||||||
|
target: T,
|
||||||
|
where: "before" | "after"
|
||||||
|
): void {
|
||||||
|
const index = this.indexOf(target);
|
||||||
|
if (index !== -1) {
|
||||||
|
if (where === "before") {
|
||||||
|
this.splice(index, 0, item);
|
||||||
|
} else {
|
||||||
|
this.splice(index + 1, 0, item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.push(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
+21
-37
@@ -1,4 +1,3 @@
|
|||||||
import type { TreeGraphData as G6TreeGraphData } from "@antv/g6";
|
|
||||||
import { NodeDef } from "../behavior3/src/behavior3";
|
import { NodeDef } from "../behavior3/src/behavior3";
|
||||||
|
|
||||||
export const VERSION = "1.8.1";
|
export const VERSION = "1.8.1";
|
||||||
@@ -14,33 +13,40 @@ export const isExprType = (type: string) => type.startsWith("expr") || type.star
|
|||||||
export const isJsonType = (type: string) => type.startsWith("json");
|
export const isJsonType = (type: string) => type.startsWith("json");
|
||||||
export const hasArgOptions = (arg: NodeArg) => arg.options !== undefined;
|
export const hasArgOptions = (arg: NodeArg) => arg.options !== undefined;
|
||||||
|
|
||||||
export interface NodeModel {
|
export interface NodeData {
|
||||||
id: number;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
args?: { [key: string]: unknown };
|
args?: { [key: string]: unknown };
|
||||||
input?: string[];
|
input?: string[];
|
||||||
output?: string[];
|
output?: string[];
|
||||||
children?: NodeModel[];
|
children?: NodeData[];
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
path?: string;
|
path?: string;
|
||||||
|
|
||||||
|
// for runtime
|
||||||
|
mtime?: number;
|
||||||
|
size?: number[];
|
||||||
|
status?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VarDef {
|
export type NodeLayout = "compact" | "normal";
|
||||||
|
|
||||||
|
export interface VarDecl {
|
||||||
name: string;
|
name: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupDef {
|
export interface GroupDecl {
|
||||||
name: string;
|
name: string;
|
||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImportDef {
|
export interface ImportDecl {
|
||||||
path: string;
|
path: string;
|
||||||
modified?: number;
|
modified?: number;
|
||||||
vars: VarDef[];
|
vars: VarDecl[];
|
||||||
depends: {
|
depends: {
|
||||||
path: string;
|
path: string;
|
||||||
modified: number;
|
modified: number;
|
||||||
@@ -48,43 +54,21 @@ export interface ImportDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FileVarDecl {
|
export interface FileVarDecl {
|
||||||
import: ImportDef[];
|
import: ImportDecl[];
|
||||||
subtree: ImportDef[];
|
subtree: ImportDecl[];
|
||||||
declvar: VarDef[];
|
vars: VarDecl[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeModel {
|
export interface TreeData {
|
||||||
version: string;
|
version: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
prefix: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
export?: boolean;
|
export?: boolean;
|
||||||
firstid: number;
|
|
||||||
group: string[];
|
group: string[];
|
||||||
import: string[];
|
import: string[];
|
||||||
declvar: VarDef[];
|
vars: VarDecl[];
|
||||||
root: NodeModel;
|
root: NodeData;
|
||||||
}
|
|
||||||
|
|
||||||
export interface TreeGraphData extends G6TreeGraphData {
|
|
||||||
name: string;
|
|
||||||
desc?: string;
|
|
||||||
args?: { [key: string]: unknown };
|
|
||||||
input?: string[];
|
|
||||||
output?: string[];
|
|
||||||
children?: TreeGraphData[];
|
|
||||||
def: NodeDef;
|
|
||||||
debug?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
parent?: string;
|
|
||||||
path?: string;
|
|
||||||
lastModified?: number;
|
|
||||||
|
|
||||||
size: number[];
|
|
||||||
highlightInput?: boolean;
|
|
||||||
highlightOutput?: boolean;
|
|
||||||
highlightArgs?: boolean;
|
|
||||||
highlightGray?: boolean;
|
|
||||||
status?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getNodeType = (def: NodeDef): NodeType => {
|
export const getNodeType = (def: NodeDef): NodeType => {
|
||||||
|
|||||||
+156
-179
@@ -1,9 +1,11 @@
|
|||||||
|
import assert from "assert";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { ExpressionEvaluator, NodeDef } from "../behavior3/src/behavior3";
|
import { ExpressionEvaluator, NodeDef } from "../behavior3/src/behavior3";
|
||||||
|
import "./array";
|
||||||
import {
|
import {
|
||||||
FileVarDecl,
|
FileVarDecl,
|
||||||
hasArgOptions,
|
hasArgOptions,
|
||||||
ImportDef,
|
ImportDecl,
|
||||||
isBoolType,
|
isBoolType,
|
||||||
isExprType,
|
isExprType,
|
||||||
isFloatType,
|
isFloatType,
|
||||||
@@ -11,10 +13,9 @@ import {
|
|||||||
isJsonType,
|
isJsonType,
|
||||||
isStringType,
|
isStringType,
|
||||||
NodeArg,
|
NodeArg,
|
||||||
NodeModel,
|
NodeData,
|
||||||
TreeGraphData,
|
TreeData,
|
||||||
TreeModel,
|
VarDecl,
|
||||||
VarDef,
|
|
||||||
VERSION,
|
VERSION,
|
||||||
} from "./b3type";
|
} from "./b3type";
|
||||||
import Path from "./path";
|
import Path from "./path";
|
||||||
@@ -35,19 +36,20 @@ type Env = {
|
|||||||
|
|
||||||
export interface BatchScript {
|
export interface BatchScript {
|
||||||
onSetup?(env: Env): void;
|
onSetup?(env: Env): void;
|
||||||
onProcessTree?(tree: TreeModel, path: string): TreeModel | null;
|
onProcessTree?(tree: TreeData, path: string): TreeData | null;
|
||||||
onProcessNode?(node: NodeModel): NodeModel | null;
|
onProcessNode?(node: NodeData): NodeData | null;
|
||||||
onWriteFile?(path: string, tree: TreeModel): void;
|
onWriteFile?(path: string, tree: TreeData): void;
|
||||||
onComplete?(status: "success" | "failure"): void;
|
onComplete?(status: "success" | "failure"): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export let calcSize: (d: NodeData) => number[] = () => [0, 0];
|
||||||
export let nodeDefs: NodeDefs = new NodeDefs();
|
export let nodeDefs: NodeDefs = new NodeDefs();
|
||||||
export let groupDefs: string[] = [];
|
export let groupDefs: string[] = [];
|
||||||
export let usingGroups: Record<string, boolean> = {};
|
export let usingGroups: Record<string, boolean> | null = null;
|
||||||
export let usingVars: Record<string, VarDef> | null = null;
|
export let usingVars: Record<string, VarDecl> | null = null;
|
||||||
export const files: Record<string, number> = {};
|
export const files: Record<string, number> = {};
|
||||||
|
|
||||||
const parsedVarDefs: Record<string, ImportDef> = {};
|
const parsedVarDecl: Record<string, ImportDecl> = {};
|
||||||
const parsedExprs: Record<string, string[]> = {};
|
const parsedExprs: Record<string, string[]> = {};
|
||||||
let checkExpr: boolean = false;
|
let checkExpr: boolean = false;
|
||||||
let workdir: string = "";
|
let workdir: string = "";
|
||||||
@@ -72,15 +74,19 @@ export const initWorkdir = (path: string, handler: typeof alertError) => {
|
|||||||
groupDefs = Array.from(groups).sort();
|
groupDefs = Array.from(groups).sort();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setSizeCalculator = (calc: (d: NodeData) => number[]) => {
|
||||||
|
calcSize = calc;
|
||||||
|
};
|
||||||
|
|
||||||
export const updateUsingGroups = (group: string[]) => {
|
export const updateUsingGroups = (group: string[]) => {
|
||||||
usingGroups = {};
|
usingGroups = null;
|
||||||
for (const g of group) {
|
for (const g of group) {
|
||||||
usingGroups ??= {};
|
usingGroups ??= {};
|
||||||
usingGroups[g] = true;
|
usingGroups[g] = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateUsingVars = (vars: VarDef[]) => {
|
export const updateUsingVars = (vars: VarDecl[]) => {
|
||||||
usingVars = null;
|
usingVars = null;
|
||||||
for (const v of vars) {
|
for (const v of vars) {
|
||||||
usingVars ??= {};
|
usingVars ??= {};
|
||||||
@@ -104,6 +110,26 @@ export const parseExpr = (expr: string) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const dfs = <T extends { children?: T[] }>(
|
||||||
|
node: T,
|
||||||
|
visitor: (node: T, depth: number) => unknown,
|
||||||
|
depth: number = 0
|
||||||
|
) => {
|
||||||
|
const traverse = (n: T, d: number) => {
|
||||||
|
if (visitor(n, d) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (n.children) {
|
||||||
|
for (const child of n.children) {
|
||||||
|
if (traverse(child, d + 1) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
traverse(node, depth);
|
||||||
|
};
|
||||||
|
|
||||||
export const isNewVersion = (version: string) => {
|
export const isNewVersion = (version: string) => {
|
||||||
const [major, minor, patch] = version.split(".").map(Number);
|
const [major, minor, patch] = version.split(".").map(Number);
|
||||||
const [major2, minor2, patch2] = VERSION.split(".").map(Number);
|
const [major2, minor2, patch2] = VERSION.split(".").map(Number);
|
||||||
@@ -118,11 +144,11 @@ export const isValidVariableName = (name: string) => {
|
|||||||
return /^[a-zA-Z_]/.test(name);
|
return /^[a-zA-Z_]/.test(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isSubtreeRoot = (data: TreeGraphData) => {
|
export const isSubtreeRoot = (data: NodeData) => {
|
||||||
return data.path && data.id.toString() !== "1";
|
return data.path && data.id !== "1";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isNodeEqual = (node1: NodeModel, node2: NodeModel) => {
|
export const isNodeEqual = (node1: NodeData, node2: NodeData) => {
|
||||||
if (
|
if (
|
||||||
node1.name === node2.name &&
|
node1.name === node2.name &&
|
||||||
node1.desc === node2.desc &&
|
node1.desc === node2.desc &&
|
||||||
@@ -161,7 +187,7 @@ export const isNodeEqual = (node1: NodeModel, node2: NodeModel) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const error = (data: NodeModel | TreeGraphData, msg: string) => {
|
const error = (data: NodeData, msg: string) => {
|
||||||
console.error(`check ${data.id}|${data.name}: ${msg}`);
|
console.error(`check ${data.id}|${data.name}: ${msg}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -178,7 +204,7 @@ export const isNodeArgOptional = (arg: NodeArg) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const checkNodeArgValue = (
|
export const checkNodeArgValue = (
|
||||||
data: NodeModel | TreeGraphData,
|
data: NodeData,
|
||||||
arg: NodeArg,
|
arg: NodeArg,
|
||||||
value: unknown,
|
value: unknown,
|
||||||
verbose?: boolean
|
verbose?: boolean
|
||||||
@@ -257,21 +283,18 @@ export const checkNodeArgValue = (
|
|||||||
return !hasError;
|
return !hasError;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkNodeArg = (
|
export const checkNodeArg = (data: NodeData, conf: NodeDef, i: number, verbose?: boolean) => {
|
||||||
data: NodeModel | TreeGraphData,
|
|
||||||
conf: NodeDef,
|
|
||||||
i: number,
|
|
||||||
verbose?: boolean
|
|
||||||
) => {
|
|
||||||
let hasError = false;
|
let hasError = false;
|
||||||
const arg = conf.args![i] as NodeArg;
|
const arg = conf.args![i] as NodeArg;
|
||||||
const value = data.args?.[arg.name];
|
const value = data.args?.[arg.name];
|
||||||
if (isNodeArgArray(arg)) {
|
if (isNodeArgArray(arg)) {
|
||||||
if (!Array.isArray(value) || (!isNodeArgOptional(arg) && value.length === 0)) {
|
if (!Array.isArray(value) || value.length === 0) {
|
||||||
if (verbose) {
|
if (!isNodeArgOptional(arg)) {
|
||||||
error(data, `'${arg.name}=${JSON.stringify(value)}' is not an array or empty array`);
|
if (verbose) {
|
||||||
|
error(data, `'${arg.name}=${JSON.stringify(value)}' is not an array or empty array`);
|
||||||
|
}
|
||||||
|
hasError = true;
|
||||||
}
|
}
|
||||||
hasError = true;
|
|
||||||
} else {
|
} else {
|
||||||
for (let j = 0; j < value.length; j++) {
|
for (let j = 0; j < value.length; j++) {
|
||||||
if (!checkNodeArgValue(data, arg, value[j], verbose)) {
|
if (!checkNodeArgValue(data, arg, value[j], verbose)) {
|
||||||
@@ -304,7 +327,7 @@ export const checkOneof = (argValue: unknown, inputValue: unknown) => {
|
|||||||
return (argValue !== "" && inputValue === "") || (argValue === "" && inputValue !== "");
|
return (argValue !== "" && inputValue === "") || (argValue === "" && inputValue !== "");
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkNodeData = (data: NodeModel | null | undefined) => {
|
export const checkNodeData = (data: NodeData | null | undefined) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -317,7 +340,7 @@ export const checkNodeData = (data: NodeModel | null | undefined) => {
|
|||||||
let hasError = false;
|
let hasError = false;
|
||||||
|
|
||||||
if (conf.group) {
|
if (conf.group) {
|
||||||
if (!conf.group.some((g) => usingGroups[g])) {
|
if (!conf.group.some((g) => usingGroups?.[g])) {
|
||||||
error(data, `node group '${conf.group}' is not enabled`);
|
error(data, `node group '${conf.group}' is not enabled`);
|
||||||
hasError = true;
|
hasError = true;
|
||||||
}
|
}
|
||||||
@@ -487,22 +510,11 @@ export const checkNodeData = (data: NodeModel | null | undefined) => {
|
|||||||
return !hasError;
|
return !hasError;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const copyFromNode = (data: TreeGraphData, node: NodeModel) => {
|
|
||||||
data.name = node.name;
|
|
||||||
data.debug = node.debug;
|
|
||||||
data.disabled = node.disabled;
|
|
||||||
data.desc = node.desc;
|
|
||||||
data.path = node.path;
|
|
||||||
data.args = node.args;
|
|
||||||
data.input = node.input;
|
|
||||||
data.output = node.output;
|
|
||||||
};
|
|
||||||
|
|
||||||
const parsingStack: string[] = [];
|
const parsingStack: string[] = [];
|
||||||
|
|
||||||
export const createNode = (data: TreeGraphData, includeChildren: boolean = true) => {
|
export const createNode = (data: NodeData, includeChildren: boolean = true) => {
|
||||||
const node: NodeModel = {
|
const node: NodeData = {
|
||||||
id: Number(data.id),
|
id: data.id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
desc: data.desc,
|
desc: data.desc,
|
||||||
path: data.path,
|
path: data.path,
|
||||||
@@ -547,9 +559,10 @@ const enum StatusFlag {
|
|||||||
FAILURE_ZERO = 4,
|
FAILURE_ZERO = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
const toStatusFlag = (data: TreeGraphData) => {
|
const toStatusFlag = (data: NodeData) => {
|
||||||
let status = 0;
|
let status = 0;
|
||||||
data.def.status?.forEach((s) => {
|
const def = nodeDefs.get(data.name);
|
||||||
|
def.status?.forEach((s) => {
|
||||||
switch (s) {
|
switch (s) {
|
||||||
case "success":
|
case "success":
|
||||||
status |= 1 << StatusFlag.SUCCESS;
|
status |= 1 << StatusFlag.SUCCESS;
|
||||||
@@ -578,15 +591,16 @@ const appendStatusFlag = (status: number, childStatus: number) => {
|
|||||||
return status;
|
return status;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildStatusFlag = (data: TreeGraphData, childStatus: number) => {
|
const buildStatusFlag = (data: NodeData, childStatus: number) => {
|
||||||
let status = data.status!;
|
let status = data.status!;
|
||||||
if (data.def.status?.length) {
|
const def = nodeDefs.get(data.name);
|
||||||
|
if (def.status?.length) {
|
||||||
const childSuccess = (childStatus >> StatusFlag.SUCCESS) & 1;
|
const childSuccess = (childStatus >> StatusFlag.SUCCESS) & 1;
|
||||||
const childFailure = (childStatus >> StatusFlag.FAILURE) & 1;
|
const childFailure = (childStatus >> StatusFlag.FAILURE) & 1;
|
||||||
const childRunning = (childStatus >> StatusFlag.RUNNING) & 1;
|
const childRunning = (childStatus >> StatusFlag.RUNNING) & 1;
|
||||||
const childHasZeroSuccess = (childStatus >> StatusFlag.SUCCESS_ZERO) & 1;
|
const childHasZeroSuccess = (childStatus >> StatusFlag.SUCCESS_ZERO) & 1;
|
||||||
const childHasZeroFailure = (childStatus >> StatusFlag.FAILURE_ZERO) & 1;
|
const childHasZeroFailure = (childStatus >> StatusFlag.FAILURE_ZERO) & 1;
|
||||||
data.def.status?.forEach((s) => {
|
def.status?.forEach((s) => {
|
||||||
switch (s) {
|
switch (s) {
|
||||||
case "!success":
|
case "!success":
|
||||||
status |= childFailure << StatusFlag.SUCCESS;
|
status |= childFailure << StatusFlag.SUCCESS;
|
||||||
@@ -625,31 +639,10 @@ const buildStatusFlag = (data: TreeGraphData, childStatus: number) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const refreshTreeDataId = (data: TreeGraphData, id?: number) => {
|
export const isValidChildren = (data: NodeData) => {
|
||||||
if (!id) {
|
const def = nodeDefs.get(data.name);
|
||||||
id = 1;
|
if (def.children !== undefined && def.children !== -1) {
|
||||||
}
|
return (data.children?.length || 0) === def.children;
|
||||||
const status = toStatusFlag(data);
|
|
||||||
data.id = (id++).toString();
|
|
||||||
data.status = status;
|
|
||||||
if (data.children) {
|
|
||||||
let childStatus = 0;
|
|
||||||
data.children.forEach((child) => {
|
|
||||||
child.parent = data.id;
|
|
||||||
id = refreshTreeDataId(child, id);
|
|
||||||
if (child.status && !child.disabled) {
|
|
||||||
childStatus = appendStatusFlag(childStatus, child.status);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
buildStatusFlag(data, childStatus);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const checkChildrenLimit = (data: TreeGraphData) => {
|
|
||||||
const conf = data.def;
|
|
||||||
if (conf.children !== undefined && conf.children !== -1) {
|
|
||||||
return (data.children?.length || 0) === conf.children;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -665,28 +658,28 @@ const isValidInputOrOutput = (def: string[], data: string[] | undefined, index:
|
|||||||
return def[index].includes("?") || data?.[index] || isVariadic(def, index);
|
return def[index].includes("?") || data?.[index] || isVariadic(def, index);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkTreeData = (data: TreeGraphData) => {
|
export const checkTreeData = (data: NodeData) => {
|
||||||
const conf = data.def;
|
const def = nodeDefs.get(data.name);
|
||||||
if (conf.input) {
|
if (def.input) {
|
||||||
for (let i = 0; i < conf.input.length; i++) {
|
for (let i = 0; i < def.input.length; i++) {
|
||||||
if (!isValidInputOrOutput(conf.input, data.input, i)) {
|
if (!isValidInputOrOutput(def.input, data.input, i)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (conf.output) {
|
if (def.output) {
|
||||||
for (let i = 0; i < conf.output.length; i++) {
|
for (let i = 0; i < def.output.length; i++) {
|
||||||
if (!isValidInputOrOutput(conf.output, data.output, i)) {
|
if (!isValidInputOrOutput(def.output, data.output, i)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!checkChildrenLimit(data)) {
|
if (!isValidChildren(data)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (conf.args) {
|
if (def.args) {
|
||||||
for (let i = 0; i < conf.args.length; i++) {
|
for (let i = 0; i < def.args.length; i++) {
|
||||||
if (!checkNodeArg(data, conf, i, false)) {
|
if (!checkNodeArg(data, def, i, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -695,92 +688,80 @@ export const checkTreeData = (data: TreeGraphData) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createTreeData = (
|
export const refreshNodeData = (node: NodeData, id: number) => {
|
||||||
node: NodeModel,
|
node.id = (id++).toString();
|
||||||
parent?: string,
|
node.size = calcSize(node);
|
||||||
calcSize?: (d: TreeGraphData) => number[]
|
|
||||||
) => {
|
|
||||||
let treeData: TreeGraphData = {
|
|
||||||
id: node.id.toFixed(),
|
|
||||||
name: node.name,
|
|
||||||
desc: node.desc,
|
|
||||||
args: node.args,
|
|
||||||
input: node.input,
|
|
||||||
output: node.output,
|
|
||||||
debug: node.debug,
|
|
||||||
disabled: node.disabled,
|
|
||||||
def: nodeDefs.get(node.name),
|
|
||||||
parent: parent,
|
|
||||||
size: [0, 0],
|
|
||||||
};
|
|
||||||
|
|
||||||
treeData.def.args?.forEach((arg) => {
|
const def = nodeDefs.get(node.name);
|
||||||
treeData.args ||= {};
|
|
||||||
if (treeData.args[arg.name] === undefined && arg.default !== undefined) {
|
|
||||||
treeData.args[arg.name] = arg.default;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (calcSize) {
|
if (def.args) {
|
||||||
treeData.size = calcSize(treeData);
|
node.args ||= {};
|
||||||
}
|
def.args.forEach((arg) => {
|
||||||
|
assert(node.args);
|
||||||
if (!parent) {
|
if (node.args[arg.name] === undefined && arg.default !== undefined) {
|
||||||
parsingStack.length = 0;
|
node.args[arg.name] = arg.default;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.path) {
|
if (node.path) {
|
||||||
if (parsingStack.indexOf(node.path) >= 0) {
|
if (parsingStack.indexOf(node.path) >= 0) {
|
||||||
treeData.path = node.path;
|
|
||||||
if (calcSize) {
|
|
||||||
treeData.size = calcSize(treeData);
|
|
||||||
}
|
|
||||||
alertError(`循环引用节点:${node.path}`, 4);
|
alertError(`循环引用节点:${node.path}`, 4);
|
||||||
return treeData;
|
return id;
|
||||||
}
|
}
|
||||||
parsingStack.push(node.path);
|
parsingStack.push(node.path);
|
||||||
try {
|
try {
|
||||||
const subtreePath = workdir + "/" + node.path;
|
const subtreePath = workdir + "/" + node.path;
|
||||||
treeData = createTreeData(readTree(subtreePath).root, treeData.id, calcSize);
|
const subtree = readTree(subtreePath).root;
|
||||||
treeData.lastModified = fs.statSync(subtreePath).mtimeMs;
|
id = refreshNodeData(subtree, --id);
|
||||||
treeData.path = node.path;
|
node.name = subtree.name;
|
||||||
treeData.debug = node.debug;
|
node.desc = subtree.desc;
|
||||||
treeData.disabled = node.disabled;
|
node.args = subtree.args;
|
||||||
treeData.parent = parent;
|
node.input = subtree.input;
|
||||||
treeData.id = node.id.toFixed();
|
node.output = subtree.output;
|
||||||
if (calcSize) {
|
node.children = subtree.children;
|
||||||
treeData.size = calcSize(treeData);
|
node.mtime = fs.statSync(subtreePath).mtimeMs;
|
||||||
}
|
node.size = calcSize(node);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alertError(`解析子树失败:${node.path}`);
|
alertError(`解析子树失败:${node.path}`);
|
||||||
console.log("parse subtree:", e);
|
console.log("parse subtree:", e);
|
||||||
}
|
}
|
||||||
parsingStack.pop();
|
parsingStack.pop();
|
||||||
} else if (node.children?.length) {
|
} else if (node.children?.length) {
|
||||||
treeData.children = [];
|
for (let i = 0; i < node.children.length; i++) {
|
||||||
node.children.forEach((child) => {
|
id = refreshNodeData(node.children[i], id);
|
||||||
treeData.children!.push(createTreeData(child, treeData.id, calcSize));
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return treeData;
|
node.status = toStatusFlag(node);
|
||||||
|
if (node.children) {
|
||||||
|
let childStatus = 0;
|
||||||
|
node.children.forEach((child) => {
|
||||||
|
if (child.status && !child.disabled) {
|
||||||
|
childStatus = appendStatusFlag(childStatus, child.status);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
buildStatusFlag(node, childStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createBuildData = (path: string) => {
|
export const createBuildData = (path: string) => {
|
||||||
try {
|
try {
|
||||||
const treeModel: TreeModel = readTree(path);
|
const treeModel: TreeData = readTree(path);
|
||||||
const data = createTreeData(treeModel.root);
|
refreshNodeData(treeModel.root, 1);
|
||||||
refreshTreeDataId(data, treeModel.firstid ?? 1);
|
dfs(treeModel.root, (node) => (node.id = treeModel.prefix + node.id));
|
||||||
treeModel.name = Path.basenameWithoutExt(path);
|
treeModel.name = Path.basenameWithoutExt(path);
|
||||||
treeModel.root = createFileData(data, true);
|
treeModel.root = createFileData(treeModel.root, true);
|
||||||
return treeModel as TreeModel;
|
return treeModel as TreeData;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("build error:", path, e);
|
console.log("build error:", path, e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const processBatch = (tree: TreeModel | null, path: string, batch: BatchScript) => {
|
export const processBatch = (tree: TreeData | null, path: string, batch: BatchScript) => {
|
||||||
if (!tree) {
|
if (!tree) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -791,9 +772,9 @@ export const processBatch = (tree: TreeModel | null, path: string, batch: BatchS
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (batch.onProcessNode) {
|
if (batch.onProcessNode) {
|
||||||
const processNode = (node: NodeModel) => {
|
const processNode = (node: NodeData) => {
|
||||||
if (node.children) {
|
if (node.children) {
|
||||||
const children: NodeModel[] = [];
|
const children: NodeData[] = [];
|
||||||
node.children?.forEach((child) => {
|
node.children?.forEach((child) => {
|
||||||
const newChild = processNode(child);
|
const newChild = processNode(child);
|
||||||
if (newChild) {
|
if (newChild) {
|
||||||
@@ -804,7 +785,7 @@ export const processBatch = (tree: TreeModel | null, path: string, batch: BatchS
|
|||||||
}
|
}
|
||||||
return batch.onProcessNode?.(node);
|
return batch.onProcessNode?.(node);
|
||||||
};
|
};
|
||||||
tree.root = processNode(tree.root) ?? ({} as NodeModel);
|
tree.root = processNode(tree.root) ?? ({} as NodeData);
|
||||||
}
|
}
|
||||||
return tree;
|
return tree;
|
||||||
};
|
};
|
||||||
@@ -850,10 +831,10 @@ export const buildProject = async (project: string, buildDir: string) => {
|
|||||||
console.log("build:", buildpath);
|
console.log("build:", buildpath);
|
||||||
const declare: FileVarDecl = {
|
const declare: FileVarDecl = {
|
||||||
import: tree.import.map((v) => ({ path: v, vars: [], depends: [] })),
|
import: tree.import.map((v) => ({ path: v, vars: [], depends: [] })),
|
||||||
declvar: tree.declvar.map((v) => ({ name: v.name, desc: v.desc })),
|
vars: tree.vars.map((v) => ({ name: v.name, desc: v.desc })),
|
||||||
subtree: [],
|
subtree: [],
|
||||||
};
|
};
|
||||||
refreshDeclare(tree.root, tree.group, declare);
|
refreshVarDecl(tree.root, tree.group, declare);
|
||||||
if (!checkNodeData(tree?.root)) {
|
if (!checkNodeData(tree?.root)) {
|
||||||
hasError = true;
|
hasError = true;
|
||||||
}
|
}
|
||||||
@@ -871,16 +852,16 @@ export const loadModule = async (path: string) => {
|
|||||||
if (typeof require !== "undefined" && require.cache) {
|
if (typeof require !== "undefined" && require.cache) {
|
||||||
delete require.cache[require.resolve(path)];
|
delete require.cache[require.resolve(path)];
|
||||||
}
|
}
|
||||||
return await import(`${path}?t=${Date.now()}`);
|
return await import(/* @vite-ignore */ `${path}?t=${Date.now()}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`failed to load module: ${path}`, e);
|
console.error(`failed to load module: ${path}`, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createFileData = (data: TreeGraphData, includeSubtree?: boolean) => {
|
export const createFileData = (data: NodeData, includeSubtree?: boolean) => {
|
||||||
const nodeData: NodeModel = {
|
const nodeData: NodeData = {
|
||||||
id: Number(data.id),
|
id: data.id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
desc: data.desc || undefined,
|
desc: data.desc || undefined,
|
||||||
args: data.args || undefined,
|
args: data.args || undefined,
|
||||||
@@ -911,15 +892,15 @@ export const createFileData = (data: TreeGraphData, includeSubtree?: boolean) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createNewTree = (path: string) => {
|
export const createNewTree = (path: string) => {
|
||||||
const tree: TreeModel = {
|
const tree: TreeData = {
|
||||||
version: VERSION,
|
version: VERSION,
|
||||||
name: Path.basenameWithoutExt(path),
|
name: Path.basenameWithoutExt(path),
|
||||||
firstid: 1,
|
prefix: "",
|
||||||
group: [],
|
group: [],
|
||||||
import: [],
|
import: [],
|
||||||
declvar: [],
|
vars: [],
|
||||||
root: {
|
root: {
|
||||||
id: 1,
|
id: "1",
|
||||||
name: "Sequence",
|
name: "Sequence",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -930,7 +911,7 @@ export const isTreeFile = (path: string) => {
|
|||||||
return path.toLocaleLowerCase().endsWith(".json");
|
return path.toLocaleLowerCase().endsWith(".json");
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadVarDef = (list: ImportDef[], arr: Array<VarDef>) => {
|
const loadVarDecl = (list: ImportDecl[], arr: Array<VarDecl>) => {
|
||||||
for (const entry of list) {
|
for (const entry of list) {
|
||||||
if (!files[entry.path]) {
|
if (!files[entry.path]) {
|
||||||
console.warn(`file not found:${workdir}/${entry.path}`);
|
console.warn(`file not found:${workdir}/${entry.path}`);
|
||||||
@@ -954,14 +935,14 @@ const loadVarDef = (list: ImportDef[], arr: Array<VarDef>) => {
|
|||||||
entry.depends = [];
|
entry.depends = [];
|
||||||
entry.modified = files[entry.path];
|
entry.modified = files[entry.path];
|
||||||
|
|
||||||
const vars: Set<VarDef> = new Set();
|
const vars: Set<VarDecl> = new Set();
|
||||||
const depends: Set<string> = new Set();
|
const depends: Set<string> = new Set();
|
||||||
const load = (path: string) => {
|
const load = (path: string) => {
|
||||||
if (parsingStack.includes(path)) {
|
if (parsingStack.includes(path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedEntry: ImportDef | undefined = parsedVarDefs[path];
|
const parsedEntry: ImportDecl | undefined = parsedVarDecl[path];
|
||||||
if (parsedEntry && files[path] === parsedEntry.modified) {
|
if (parsedEntry && files[path] === parsedEntry.modified) {
|
||||||
parsedEntry.depends.forEach((v) => depends.add(v.path));
|
parsedEntry.depends.forEach((v) => depends.add(v.path));
|
||||||
parsedEntry.vars.forEach((v) => vars.add(v));
|
parsedEntry.vars.forEach((v) => vars.add(v));
|
||||||
@@ -970,8 +951,8 @@ const loadVarDef = (list: ImportDef[], arr: Array<VarDef>) => {
|
|||||||
|
|
||||||
parsingStack.push(path);
|
parsingStack.push(path);
|
||||||
try {
|
try {
|
||||||
const model: TreeModel = readTree(`${workdir}/${path}`);
|
const model: TreeData = readTree(`${workdir}/${path}`);
|
||||||
model.declvar.forEach((v) => vars.add(v));
|
model.vars.forEach((v) => vars.add(v));
|
||||||
model.import.forEach((v) => {
|
model.import.forEach((v) => {
|
||||||
load(v);
|
load(v);
|
||||||
depends.add(v);
|
depends.add(v);
|
||||||
@@ -985,7 +966,7 @@ const loadVarDef = (list: ImportDef[], arr: Array<VarDef>) => {
|
|||||||
load(entry.path);
|
load(entry.path);
|
||||||
entry.vars = Array.from(vars).sort((a, b) => a.name.localeCompare(b.name));
|
entry.vars = Array.from(vars).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
entry.depends = Array.from(depends).map((v) => ({ path: v, modified: files[v] }));
|
entry.depends = Array.from(depends).map((v) => ({ path: v, modified: files[v] }));
|
||||||
parsedVarDefs[entry.path] = {
|
parsedVarDecl[entry.path] = {
|
||||||
path: entry.path,
|
path: entry.path,
|
||||||
vars: entry.vars.map((v) => ({ name: v.name, desc: v.desc })),
|
vars: entry.vars.map((v) => ({ name: v.name, desc: v.desc })),
|
||||||
depends: entry.depends.slice(),
|
depends: entry.depends.slice(),
|
||||||
@@ -995,24 +976,20 @@ const loadVarDef = (list: ImportDef[], arr: Array<VarDef>) => {
|
|||||||
list.forEach((entry) => arr.push(...entry.vars));
|
list.forEach((entry) => arr.push(...entry.vars));
|
||||||
};
|
};
|
||||||
|
|
||||||
const collectSubtree = (data: TreeGraphData | NodeModel, list: string[] = []) => {
|
const collectSubtree = (data: NodeData) => {
|
||||||
if (data.path) {
|
const list: string[] = [];
|
||||||
list.push(data.path);
|
dfs(data, (node) => {
|
||||||
}
|
if (node.path) {
|
||||||
if (data.children) {
|
list.push(node.path);
|
||||||
data.children.forEach((child) => collectSubtree(child, list));
|
}
|
||||||
}
|
});
|
||||||
return list;
|
return list;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const refreshDeclare = (
|
export const refreshVarDecl = (root: NodeData, group: string[], declare: FileVarDecl) => {
|
||||||
root: TreeGraphData | NodeModel,
|
|
||||||
group: string[],
|
|
||||||
declare: FileVarDecl
|
|
||||||
) => {
|
|
||||||
const filter: Record<string, boolean> = {};
|
const filter: Record<string, boolean> = {};
|
||||||
const vars: Array<VarDef> = new (class extends Array {
|
const vars: Array<VarDecl> = new (class extends Array {
|
||||||
push(...items: VarDef[]): number {
|
push(...items: VarDecl[]): number {
|
||||||
for (const v of items) {
|
for (const v of items) {
|
||||||
if (filter[v.name]) {
|
if (filter[v.name]) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1023,18 +1000,18 @@ export const refreshDeclare = (
|
|||||||
return this.length;
|
return this.length;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
vars.push(...declare.declvar);
|
vars.push(...declare.vars);
|
||||||
parsingStack.length = 0;
|
parsingStack.length = 0;
|
||||||
declare.subtree = collectSubtree(root).map((v) => ({
|
declare.subtree = collectSubtree(root).map((v) => ({
|
||||||
path: v,
|
path: v,
|
||||||
vars: [],
|
vars: [],
|
||||||
depends: [],
|
depends: [],
|
||||||
}));
|
}));
|
||||||
loadVarDef(declare.import, vars);
|
loadVarDecl(declare.import, vars);
|
||||||
loadVarDef(declare.subtree, vars);
|
loadVarDecl(declare.subtree, vars);
|
||||||
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
const lastGroup = Array.from(Object.keys(usingGroups)).sort();
|
const lastGroup = Array.from(Object.keys(usingGroups ?? {})).sort();
|
||||||
group.sort();
|
group.sort();
|
||||||
if (lastGroup.length !== group.length || lastGroup.some((v, i) => v !== group[i])) {
|
if (lastGroup.length !== group.length || lastGroup.some((v, i) => v !== group[i])) {
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|||||||
+20
-14
@@ -1,12 +1,19 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { type WorkspaceModel } from "../contexts/workspace-context";
|
import { type WorkspaceModel } from "../contexts/workspace-context";
|
||||||
import { VERSION, type TreeModel } from "./b3type";
|
import { VarDecl, VERSION, type TreeData } from "./b3type";
|
||||||
|
import { dfs } from "./b3util";
|
||||||
|
import Path from "./path";
|
||||||
|
|
||||||
export const readJson = <T>(path: string): T => {
|
export const readJson = <T>(path: string): T => {
|
||||||
const str = fs.readFileSync(path, "utf-8");
|
const str = fs.readFileSync(path, "utf-8");
|
||||||
return JSON.parse(str);
|
return JSON.parse(str);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const writeJson = <T>(path: string, data: T) => {
|
||||||
|
const str = JSON.stringify(data, undefined, 2);
|
||||||
|
fs.writeFileSync(path, str, "utf-8");
|
||||||
|
};
|
||||||
|
|
||||||
export const readWorkspace = (path: string) => {
|
export const readWorkspace = (path: string) => {
|
||||||
const data = readJson(path) as WorkspaceModel;
|
const data = readJson(path) as WorkspaceModel;
|
||||||
data.settings = data.settings ?? {};
|
data.settings = data.settings ?? {};
|
||||||
@@ -14,35 +21,34 @@ export const readWorkspace = (path: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const readTree = (path: string) => {
|
export const readTree = (path: string) => {
|
||||||
const data = readJson(path) as TreeModel;
|
const data = readJson(path) as TreeData & { declvar?: VarDecl[] };
|
||||||
data.version = data.version ?? VERSION;
|
data.version = data.version ?? VERSION;
|
||||||
data.firstid = data.firstid ?? 1;
|
data.prefix = data.prefix ?? "";
|
||||||
data.group = data.group || [];
|
data.group = data.group || [];
|
||||||
data.import = data.import || [];
|
data.import = data.import || [];
|
||||||
data.declvar = data.declvar || [];
|
data.vars = data.vars || data.declvar || [];
|
||||||
data.root = data.root || {};
|
data.root = data.root || {};
|
||||||
|
|
||||||
|
// compatible with old version
|
||||||
|
dfs(data.root, (node) => (node.id = node.id.toString()));
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const writeTree = (path: string, data: TreeModel) => {
|
export const writeTree = (path: string, data: TreeData) => {
|
||||||
writeJson(path, {
|
writeJson<TreeData>(path, {
|
||||||
version: VERSION,
|
version: VERSION,
|
||||||
name: data.name,
|
name: Path.basenameWithoutExt(path),
|
||||||
desc: data.desc,
|
desc: data.desc,
|
||||||
firstid: data.firstid,
|
prefix: data.prefix,
|
||||||
export: data.export,
|
export: data.export,
|
||||||
group: data.group,
|
group: data.group,
|
||||||
import: data.import,
|
import: data.import,
|
||||||
declvar: data.declvar,
|
vars: data.vars,
|
||||||
root: data.root,
|
root: data.root,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const writeJson = (path: string, data: unknown) => {
|
|
||||||
const str = JSON.stringify(data, undefined, 2);
|
|
||||||
fs.writeFileSync(path, str, "utf-8");
|
|
||||||
};
|
|
||||||
|
|
||||||
export function mergeClassNames(...cls: (string | boolean)[]): string {
|
export function mergeClassNames(...cls: (string | boolean)[]): string {
|
||||||
return cls.filter((v) => !!v).join(" ");
|
return cls.filter((v) => !!v).join(" ");
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user