Support multi source options of node's arg
This commit is contained in:
1 parent
2f4176857e
commit
6f9097f73d
7 files changed
+304
-213
No files matched your search
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "behavior3editor",
|
"name": "behavior3editor",
|
||||||
"version": "1.8.12",
|
"version": "1.9.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "behavior3editor",
|
"name": "behavior3editor",
|
||||||
"version": "1.8.12",
|
"version": "1.9.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "behavior3editor",
|
"name": "behavior3editor",
|
||||||
"version": "1.8.12",
|
"version": "1.9.0",
|
||||||
"main": "dist-electron/main/index.js",
|
"main": "dist-electron/main/index.js",
|
||||||
"description": "行为树编辑器",
|
"description": "行为树编辑器",
|
||||||
"author": "zhandouxiaojiji & codetypes",
|
"author": "zhandouxiaojiji & codetypes",
|
||||||
|
|||||||
+1
-1
Submodule src/behavior3 updated: 7bc20e0313...fc608994fd.
+254
-204
@@ -11,6 +11,7 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
Flex,
|
Flex,
|
||||||
Form,
|
Form,
|
||||||
|
FormInstance,
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Select,
|
Select,
|
||||||
@@ -26,7 +27,7 @@ import Markdown from "react-markdown";
|
|||||||
import { useDebounceCallback } from "usehooks-ts";
|
import { useDebounceCallback } from "usehooks-ts";
|
||||||
import { useShallow } from "zustand/react/shallow";
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import { ExpressionEvaluator } from "../behavior3/src/behavior3";
|
import { ExpressionEvaluator } from "../behavior3/src/behavior3";
|
||||||
import { EditNode, EditTree, useWorkspace } from "../contexts/workspace-context";
|
import { EditNode, EditorStore, EditTree, useWorkspace } from "../contexts/workspace-context";
|
||||||
import {
|
import {
|
||||||
hasArgOptions,
|
hasArgOptions,
|
||||||
ImportDecl,
|
ImportDecl,
|
||||||
@@ -43,12 +44,14 @@ import {
|
|||||||
import {
|
import {
|
||||||
checkNodeArgValue,
|
checkNodeArgValue,
|
||||||
checkOneof,
|
checkOneof,
|
||||||
|
getNodeArgOptions,
|
||||||
getNodeArgRawType,
|
getNodeArgRawType,
|
||||||
isNodeArgArray,
|
isNodeArgArray,
|
||||||
isNodeArgOptional,
|
isNodeArgOptional,
|
||||||
isValidChildren,
|
isValidChildren,
|
||||||
isValidVariableName,
|
isValidVariableName,
|
||||||
isVariadic,
|
isVariadic,
|
||||||
|
NodeDefs,
|
||||||
parseExpr,
|
parseExpr,
|
||||||
} from "../misc/b3util";
|
} from "../misc/b3util";
|
||||||
import { message } from "../misc/hooks";
|
import { message } from "../misc/hooks";
|
||||||
@@ -554,6 +557,223 @@ const validateArg = (
|
|||||||
return Promise.resolve(value);
|
return Promise.resolve(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateFormWithNode = (
|
||||||
|
form: FormInstance,
|
||||||
|
{
|
||||||
|
editingNode,
|
||||||
|
nodeDefs,
|
||||||
|
}: {
|
||||||
|
editingNode: EditNode;
|
||||||
|
nodeDefs: NodeDefs;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const data = editingNode.data;
|
||||||
|
const def = nodeDefs.get(data.name);
|
||||||
|
const t = i18n.t;
|
||||||
|
form.resetFields();
|
||||||
|
form.setFieldValue("id", editingNode.prefix + data.id);
|
||||||
|
form.setFieldValue("name", data.name);
|
||||||
|
form.setFieldValue("type", def.type);
|
||||||
|
form.setFieldValue("desc", data.desc || def.desc);
|
||||||
|
form.setFieldValue("debug", data.debug);
|
||||||
|
form.setFieldValue("disabled", data.disabled);
|
||||||
|
form.setFieldValue("path", data.path);
|
||||||
|
form.setFieldValue(
|
||||||
|
"group",
|
||||||
|
def.group?.map((g) => ({ label: g, value: g }))
|
||||||
|
);
|
||||||
|
if (def.children === undefined || def.children === -1) {
|
||||||
|
form.setFieldValue("children", t("node.children.unlimited"));
|
||||||
|
} else {
|
||||||
|
form.setFieldValue("children", def.children);
|
||||||
|
}
|
||||||
|
form.setFieldValue("json-data", JSON.stringify(data, null, 2));
|
||||||
|
def.args?.forEach((arg) => {
|
||||||
|
const type = getNodeArgRawType(arg);
|
||||||
|
const value = data.args?.[arg.name];
|
||||||
|
const name = `args.${arg.name}`;
|
||||||
|
if (isNodeArgArray(arg)) {
|
||||||
|
form.setFieldValue(
|
||||||
|
name,
|
||||||
|
(Array.isArray(value) ? value : []).map((item) => {
|
||||||
|
if (isJsonType(type)) {
|
||||||
|
return item === null ? "null" : JSON.stringify(item ?? arg.default, null, 2);
|
||||||
|
} else {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (isJsonType(type)) {
|
||||||
|
form.setFieldValue(
|
||||||
|
name,
|
||||||
|
value === null ? "null" : JSON.stringify(value ?? arg.default, null, 2)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
form.setFieldValue(name, value ?? arg.default);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
def.input?.forEach((_, i) => {
|
||||||
|
if (isVariadic(def.input!, i)) {
|
||||||
|
form.setFieldValue(`input.${i}`, data.input?.slice(i) ?? []);
|
||||||
|
} else {
|
||||||
|
form.setFieldValue(`input.${i}`, data.input?.[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
def.output?.forEach((_, i) => {
|
||||||
|
if (isVariadic(def.output!, i)) {
|
||||||
|
form.setFieldValue(`output.${i}`, data.output?.slice(i) ?? []);
|
||||||
|
} else {
|
||||||
|
form.setFieldValue(`output.${i}`, data.output?.[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createNodeFromForm = (
|
||||||
|
form: FormInstance,
|
||||||
|
{
|
||||||
|
editingNode,
|
||||||
|
nodeDefs,
|
||||||
|
}: {
|
||||||
|
editingNode: EditNode;
|
||||||
|
nodeDefs: NodeDefs;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const def = nodeDefs.get(editingNode.data.name);
|
||||||
|
const data = {} as NodeData;
|
||||||
|
const values = form.getFieldsValue();
|
||||||
|
data.$id = editingNode.data.$id;
|
||||||
|
data.id = editingNode.data.id;
|
||||||
|
data.name = values.name;
|
||||||
|
data.debug = values.debug || undefined;
|
||||||
|
data.disabled = values.disabled || undefined;
|
||||||
|
data.desc = values.desc && values.desc !== def.desc ? values.desc : undefined;
|
||||||
|
data.path = values.path || undefined;
|
||||||
|
|
||||||
|
if (def.args?.length) {
|
||||||
|
def.args?.forEach((arg) => {
|
||||||
|
const value = values[`args.${arg.name}`];
|
||||||
|
if (value !== null && value !== undefined && value !== "") {
|
||||||
|
data.args ||= {};
|
||||||
|
const type = getNodeArgRawType(arg);
|
||||||
|
if (isNodeArgArray(arg)) {
|
||||||
|
const arr: unknown[] = [];
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((item) => {
|
||||||
|
if (isJsonType(type)) {
|
||||||
|
try {
|
||||||
|
arr.push(item === "null" ? null : JSON.parse(item));
|
||||||
|
} catch {
|
||||||
|
/** ignore */
|
||||||
|
}
|
||||||
|
} else if (item !== null && item !== undefined) {
|
||||||
|
arr.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
data.args[arg.name] = !isNodeArgOptional(arg) ? arr : arr.length ? arr : undefined;
|
||||||
|
} else if (isJsonType(type)) {
|
||||||
|
try {
|
||||||
|
data.args[arg.name] = value === "null" ? null : JSON.parse(value);
|
||||||
|
} catch {
|
||||||
|
/** ignore */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data.args[arg.name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.args = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def.input?.length) {
|
||||||
|
def.input?.forEach((_, i) => {
|
||||||
|
data.input ||= [];
|
||||||
|
if (isVariadic(def.input!, i)) {
|
||||||
|
const arr = (values[`input.${i}`] ?? []) as string[];
|
||||||
|
data.input.push(...arr.filter((v) => typeof v === "string"));
|
||||||
|
} else {
|
||||||
|
const v = values[`input.${i}`];
|
||||||
|
data.input.push(v ?? "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.input = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def.output?.length) {
|
||||||
|
def.output?.forEach((_, i) => {
|
||||||
|
data.output ||= [];
|
||||||
|
if (isVariadic(def.output!, i)) {
|
||||||
|
const arr = (values[`output.${i}`] ?? []) as string[];
|
||||||
|
data.output.push(...arr.filter((v) => typeof v === "string"));
|
||||||
|
} else {
|
||||||
|
const v = values[`output.${i}`];
|
||||||
|
data.output.push(v ?? "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.output = [];
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createInOutOptions = ({
|
||||||
|
nodeDefs,
|
||||||
|
usingVars,
|
||||||
|
editing,
|
||||||
|
}: {
|
||||||
|
nodeDefs: NodeDefs;
|
||||||
|
usingVars: Record<string, VarDecl> | null;
|
||||||
|
editing: EditorStore | undefined;
|
||||||
|
}) => {
|
||||||
|
const options: OptionType[] = [];
|
||||||
|
const filter: Record<string, boolean> = {};
|
||||||
|
const collect = (node?: NodeData) => {
|
||||||
|
if (node) {
|
||||||
|
const def = nodeDefs.get(node.name);
|
||||||
|
node.input?.forEach((v, i) => {
|
||||||
|
let desc: string;
|
||||||
|
const inputDef = def.input;
|
||||||
|
if (inputDef?.length && i >= inputDef.length && isVariadic(inputDef, -1)) {
|
||||||
|
desc = inputDef[inputDef.length - 1];
|
||||||
|
} else {
|
||||||
|
desc = inputDef?.[i] ?? "<unknown>";
|
||||||
|
}
|
||||||
|
if (!filter[v]) {
|
||||||
|
options.push({ label: `${v}(${desc})`, value: v });
|
||||||
|
filter[v] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
node.output?.forEach((v, i) => {
|
||||||
|
let desc: string;
|
||||||
|
const outputDef = def.output;
|
||||||
|
if (outputDef?.length && i >= outputDef.length && isVariadic(outputDef, -1)) {
|
||||||
|
desc = outputDef[outputDef.length - 1];
|
||||||
|
} else {
|
||||||
|
desc = outputDef?.[i] ?? "<unknown>";
|
||||||
|
}
|
||||||
|
if (!filter[v]) {
|
||||||
|
options.push({ label: `${v}(${desc})`, value: v });
|
||||||
|
filter[v] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
node.children?.forEach((child) => collect(child));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (usingVars) {
|
||||||
|
Object.values(usingVars).forEach((v) => {
|
||||||
|
if (!filter[v.name]) {
|
||||||
|
options.push({ label: `${v.name}(${v.desc})`, value: v.name });
|
||||||
|
filter[v.name] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
collect(editing?.data.root);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
const NodeInspector: FC = () => {
|
const NodeInspector: FC = () => {
|
||||||
const workspace = useWorkspace(
|
const workspace = useWorkspace(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
@@ -578,7 +798,13 @@ const NodeInspector: FC = () => {
|
|||||||
100
|
100
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [nodeArgs, setNodeArgs] = useState<Record<string, unknown>>(
|
||||||
|
workspace.editingNode.data.args ?? {}
|
||||||
|
);
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
|
setNodeArgs(createNodeFromForm(form, workspace).args ?? {});
|
||||||
|
|
||||||
if (form.isFieldsValidating()) {
|
if (form.isFieldsValidating()) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
submit();
|
submit();
|
||||||
@@ -596,124 +822,24 @@ const NodeInspector: FC = () => {
|
|||||||
|
|
||||||
// set form values
|
// set form values
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const data = workspace.editingNode.data;
|
updateFormWithNode(form, workspace);
|
||||||
const def = workspace.nodeDefs.get(workspace.editingNode.data.name);
|
|
||||||
form.resetFields();
|
|
||||||
form.setFieldValue("id", workspace.editingNode.prefix + data.id);
|
|
||||||
form.setFieldValue("name", data.name);
|
|
||||||
form.setFieldValue("type", def.type);
|
|
||||||
form.setFieldValue("desc", data.desc || def.desc);
|
|
||||||
form.setFieldValue("debug", data.debug);
|
|
||||||
form.setFieldValue("disabled", data.disabled);
|
|
||||||
form.setFieldValue("path", data.path);
|
|
||||||
form.setFieldValue(
|
|
||||||
"group",
|
|
||||||
def.group?.map((g) => ({ label: g, value: g }))
|
|
||||||
);
|
|
||||||
if (def.children === undefined || def.children === -1) {
|
|
||||||
form.setFieldValue("children", t("node.children.unlimited"));
|
|
||||||
} else {
|
|
||||||
form.setFieldValue("children", def.children);
|
|
||||||
}
|
|
||||||
form.setFieldValue("json-data", JSON.stringify(data, null, 2));
|
|
||||||
def.args?.forEach((arg) => {
|
|
||||||
const type = getNodeArgRawType(arg);
|
|
||||||
const value = data.args?.[arg.name];
|
|
||||||
const name = `args.${arg.name}`;
|
|
||||||
if (isNodeArgArray(arg)) {
|
|
||||||
form.setFieldValue(
|
|
||||||
name,
|
|
||||||
(Array.isArray(value) ? value : []).map((item) => {
|
|
||||||
if (isJsonType(type)) {
|
|
||||||
return item === null ? "null" : JSON.stringify(item ?? arg.default, null, 2);
|
|
||||||
} else {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (isJsonType(type)) {
|
|
||||||
form.setFieldValue(
|
|
||||||
name,
|
|
||||||
value === null ? "null" : JSON.stringify(value ?? arg.default, null, 2)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setFieldValue(name, value ?? arg.default);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
def.input?.forEach((_, i) => {
|
|
||||||
if (isVariadic(def.input!, i)) {
|
|
||||||
form.setFieldValue(`input.${i}`, data.input?.slice(i) ?? []);
|
|
||||||
} else {
|
|
||||||
form.setFieldValue(`input.${i}`, data.input?.[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
def.output?.forEach((_, i) => {
|
|
||||||
if (isVariadic(def.output!, i)) {
|
|
||||||
form.setFieldValue(`output.${i}`, data.output?.slice(i) ?? []);
|
|
||||||
} else {
|
|
||||||
form.setFieldValue(`output.${i}`, data.output?.[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
validateFieldsLater();
|
validateFieldsLater();
|
||||||
}, [workspace.editingNode]);
|
}, [workspace.editingNode]);
|
||||||
|
|
||||||
// auto complete for node
|
// auto complete for node
|
||||||
const nodeOptions = useMemo(() => {
|
const nodeOptions = useMemo(
|
||||||
const options: OptionType[] = [];
|
() =>
|
||||||
workspace.nodeDefs.forEach((e) => {
|
Array.from(workspace.nodeDefs.entries()).map(([name, def]) => {
|
||||||
options.push({ label: `${e.name}(${e.desc})`, value: e.name });
|
return { label: `${name}(${def.desc})`, value: name };
|
||||||
});
|
}),
|
||||||
return options;
|
[workspace.nodeDefs]
|
||||||
}, [workspace.nodeDefs]);
|
);
|
||||||
|
|
||||||
// auto complete for input and output
|
// auto complete for input and output
|
||||||
const inoutVarOptions = useMemo(() => {
|
const inoutVarOptions = useMemo(
|
||||||
const options: OptionType[] = [];
|
() => createInOutOptions(workspace),
|
||||||
const filter: Record<string, boolean> = {};
|
[workspace.editing, workspace.usingVars]
|
||||||
const collect = (node?: NodeData) => {
|
);
|
||||||
if (node) {
|
|
||||||
const def = workspace.nodeDefs.get(node.name);
|
|
||||||
node.input?.forEach((v, i) => {
|
|
||||||
let desc: string;
|
|
||||||
const inputDef = def.input;
|
|
||||||
if (inputDef?.length && i >= inputDef.length && isVariadic(inputDef, -1)) {
|
|
||||||
desc = inputDef[inputDef.length - 1];
|
|
||||||
} else {
|
|
||||||
desc = inputDef?.[i] ?? "<unknown>";
|
|
||||||
}
|
|
||||||
if (!filter[v]) {
|
|
||||||
options.push({ label: `${v}(${desc})`, value: v });
|
|
||||||
filter[v] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
node.output?.forEach((v, i) => {
|
|
||||||
let desc: string;
|
|
||||||
const outputDef = def.output;
|
|
||||||
if (outputDef?.length && i >= outputDef.length && isVariadic(outputDef, -1)) {
|
|
||||||
desc = outputDef[outputDef.length - 1];
|
|
||||||
} else {
|
|
||||||
desc = outputDef?.[i] ?? "<unknown>";
|
|
||||||
}
|
|
||||||
if (!filter[v]) {
|
|
||||||
options.push({ label: `${v}(${desc})`, value: v });
|
|
||||||
filter[v] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
node.children?.forEach((child) => collect(child));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (workspace.usingVars) {
|
|
||||||
Object.values(workspace.usingVars).forEach((v) => {
|
|
||||||
if (!filter[v.name]) {
|
|
||||||
options.push({ label: `${v.name}(${v.desc})`, value: v.name });
|
|
||||||
filter[v.name] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
collect(workspace.editing?.data.root);
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}, [workspace.editing, workspace.usingVars]);
|
|
||||||
|
|
||||||
// auto complete for subtree
|
// auto complete for subtree
|
||||||
const subtreeOptions = useMemo(() => {
|
const subtreeOptions = useMemo(() => {
|
||||||
@@ -734,87 +860,9 @@ const NodeInspector: FC = () => {
|
|||||||
const def = workspace.nodeDefs.get(editingNode.data.name);
|
const def = workspace.nodeDefs.get(editingNode.data.name);
|
||||||
const disabled = editingNode.disabled;
|
const disabled = editingNode.disabled;
|
||||||
|
|
||||||
// update value
|
const finish = () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const finish = (values: any) => {
|
|
||||||
const data = {} as NodeData;
|
|
||||||
data.$id = editingNode.data.$id;
|
|
||||||
data.id = editingNode.data.id;
|
|
||||||
data.name = values.name;
|
|
||||||
data.debug = values.debug || undefined;
|
|
||||||
data.disabled = values.disabled || undefined;
|
|
||||||
data.desc = values.desc && values.desc !== def.desc ? values.desc : undefined;
|
|
||||||
data.path = values.path || undefined;
|
|
||||||
|
|
||||||
if (def.args?.length) {
|
|
||||||
def.args?.forEach((arg) => {
|
|
||||||
const value = values[`args.${arg.name}`];
|
|
||||||
if (value !== null && value !== undefined && value !== "") {
|
|
||||||
data.args ||= {};
|
|
||||||
const type = getNodeArgRawType(arg);
|
|
||||||
if (isNodeArgArray(arg)) {
|
|
||||||
const arr: unknown[] = [];
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
value.forEach((item) => {
|
|
||||||
if (isJsonType(type)) {
|
|
||||||
try {
|
|
||||||
arr.push(item === "null" ? null : JSON.parse(item));
|
|
||||||
} catch {
|
|
||||||
/** ignore */
|
|
||||||
}
|
|
||||||
} else if (item !== null && item !== undefined) {
|
|
||||||
arr.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
data.args[arg.name] = !isNodeArgOptional(arg) ? arr : arr.length ? arr : undefined;
|
|
||||||
} else if (isJsonType(type)) {
|
|
||||||
try {
|
|
||||||
data.args[arg.name] = value === "null" ? null : JSON.parse(value);
|
|
||||||
} catch {
|
|
||||||
/** ignore */
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data.args[arg.name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data.args = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def.input?.length) {
|
|
||||||
def.input?.forEach((_, i) => {
|
|
||||||
data.input ||= [];
|
|
||||||
if (isVariadic(def.input!, i)) {
|
|
||||||
const arr = (values[`input.${i}`] ?? []) as string[];
|
|
||||||
data.input.push(...arr.filter((v) => typeof v === "string"));
|
|
||||||
} else {
|
|
||||||
const v = values[`input.${i}`];
|
|
||||||
data.input.push(v ?? "");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data.input = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def.output?.length) {
|
|
||||||
def.output?.forEach((_, i) => {
|
|
||||||
data.output ||= [];
|
|
||||||
if (isVariadic(def.output!, i)) {
|
|
||||||
const arr = (values[`output.${i}`] ?? []) as string[];
|
|
||||||
data.output.push(...arr.filter((v) => typeof v === "string"));
|
|
||||||
} else {
|
|
||||||
const v = values[`output.${i}`];
|
|
||||||
data.output.push(v ?? "");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data.output = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
workspace.editing?.dispatch?.("updateNode", {
|
workspace.editing?.dispatch?.("updateNode", {
|
||||||
data: data,
|
data: createNodeFromForm(form, workspace),
|
||||||
} as EditNode);
|
} as EditNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -833,7 +881,7 @@ const NodeInspector: FC = () => {
|
|||||||
prefix: editingNode.prefix,
|
prefix: editingNode.prefix,
|
||||||
disabled: editingNode.disabled,
|
disabled: editingNode.disabled,
|
||||||
});
|
});
|
||||||
finish(form.getFieldsValue());
|
finish();
|
||||||
} else {
|
} else {
|
||||||
submit();
|
submit();
|
||||||
}
|
}
|
||||||
@@ -841,7 +889,7 @@ const NodeInspector: FC = () => {
|
|||||||
|
|
||||||
const changeSubtree = () => {
|
const changeSubtree = () => {
|
||||||
if (form.getFieldValue("path") !== editingNode.data.path) {
|
if (form.getFieldValue("path") !== editingNode.data.path) {
|
||||||
finish(form.getFieldsValue());
|
finish();
|
||||||
} else {
|
} else {
|
||||||
submit();
|
submit();
|
||||||
}
|
}
|
||||||
@@ -1182,7 +1230,7 @@ const NodeInspector: FC = () => {
|
|||||||
() => ({
|
() => ({
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
return validateArg(
|
return validateArg(
|
||||||
editingNode.data,
|
createNodeFromForm(form, workspace),
|
||||||
arg,
|
arg,
|
||||||
value,
|
value,
|
||||||
workspace.usingVars
|
workspace.usingVars
|
||||||
@@ -1215,12 +1263,14 @@ const NodeInspector: FC = () => {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onBlur={submit}
|
onBlur={submit}
|
||||||
onChange={submit}
|
onChange={submit}
|
||||||
options={(arg.options ?? []).map((option) => {
|
options={(getNodeArgOptions(arg, nodeArgs) ?? []).map(
|
||||||
return {
|
(option) => {
|
||||||
value: option.value,
|
return {
|
||||||
label: `${option.name}(${option.value})`,
|
value: option.value,
|
||||||
};
|
label: `${option.name}(${option.value})`,
|
||||||
})}
|
};
|
||||||
|
}
|
||||||
|
)}
|
||||||
filterOption={(value, option) => {
|
filterOption={(value, option) => {
|
||||||
value = value.toUpperCase();
|
value = value.toUpperCase();
|
||||||
return !!option?.label
|
return !!option?.label
|
||||||
@@ -1280,7 +1330,7 @@ const NodeInspector: FC = () => {
|
|||||||
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
||||||
async validator(_, value) {
|
async validator(_, value) {
|
||||||
return validateArg(
|
return validateArg(
|
||||||
editingNode.data,
|
createNodeFromForm(form, workspace),
|
||||||
arg,
|
arg,
|
||||||
value,
|
value,
|
||||||
workspace.usingVars
|
workspace.usingVars
|
||||||
@@ -1344,7 +1394,7 @@ const NodeInspector: FC = () => {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onBlur={submit}
|
onBlur={submit}
|
||||||
onChange={submit}
|
onChange={submit}
|
||||||
options={(arg.options ?? []).map((option) => {
|
options={(getNodeArgOptions(arg, nodeArgs) ?? []).map((option) => {
|
||||||
return {
|
return {
|
||||||
value: option.value,
|
value: option.value,
|
||||||
label: `${option.name}(${option.value})`,
|
label: `${option.name}(${option.value})`,
|
||||||
|
|||||||
@@ -137,6 +137,14 @@ body {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-select-dropdown .ant-select-item-option-selected {
|
||||||
|
background-color: var(--b3-color-selected) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select-dropdown .ant-select-item-option-active {
|
||||||
|
background-color: var(--b3-color-selected) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-form-item .ant-form-item-explain {
|
.ant-form-item .ant-form-item-explain {
|
||||||
font-size: small;
|
font-size: small;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import { NodeDef } from "../behavior3/src/behavior3";
|
import { NodeDef } from "../behavior3/src/behavior3";
|
||||||
|
|
||||||
export const VERSION = "1.8.10";
|
export const VERSION = "1.9.0";
|
||||||
|
|
||||||
export const keyWords = ["true", "false", "null", "undefined", "NaN", "Infinity"];
|
export const keyWords = ["true", "false", "null", "undefined", "NaN", "Infinity"];
|
||||||
|
|
||||||
|
|||||||
+37
-4
@@ -69,9 +69,28 @@ export const initWorkdir = (path: string, handler: typeof alertError) => {
|
|||||||
const nodeDefData = readJson(`${workdir}/node-config.b3-setting`) as NodeDef[];
|
const nodeDefData = readJson(`${workdir}/node-config.b3-setting`) as NodeDef[];
|
||||||
const groups: Set<string> = new Set();
|
const groups: Set<string> = new Set();
|
||||||
nodeDefs = new NodeDefs();
|
nodeDefs = new NodeDefs();
|
||||||
for (const v of nodeDefData) {
|
for (const node of nodeDefData) {
|
||||||
nodeDefs.set(v.name, v);
|
node.args?.forEach((arg) => {
|
||||||
v.group?.forEach((g) => groups.add(g));
|
if (arg.options && !arg.options[0].source) {
|
||||||
|
arg.options = [
|
||||||
|
{
|
||||||
|
source: arg.options as unknown as Array<{ name: string; value: unknown }>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
arg.options?.forEach((option) => {
|
||||||
|
Object.keys(option.match ?? {}).forEach((key) => {
|
||||||
|
if (!node.args?.find((v) => v.name === key)) {
|
||||||
|
console.error(
|
||||||
|
`match key '${key}' in arg '${arg.name}' of ` +
|
||||||
|
`node '${node.name}' is not found in args`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
nodeDefs.set(node.name, node);
|
||||||
|
node.group?.forEach((g) => groups.add(g));
|
||||||
}
|
}
|
||||||
groupDefs = Array.from(groups).sort();
|
groupDefs = Array.from(groups).sort();
|
||||||
};
|
};
|
||||||
@@ -207,6 +226,19 @@ export const isNodeArgOptional = (arg: NodeArg) => {
|
|||||||
return arg.type.includes("?");
|
return arg.type.includes("?");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getNodeArgOptions = (arg: NodeArg, args: Record<string, unknown>) => {
|
||||||
|
if (!arg.options) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const defaultMatch = arg.options.find((option) => !option.match);
|
||||||
|
if (defaultMatch) {
|
||||||
|
return defaultMatch.source;
|
||||||
|
}
|
||||||
|
return arg.options.find((entry) =>
|
||||||
|
Object.entries(entry.match!).every(([key, value]) => value.includes(args[key]))
|
||||||
|
)?.source;
|
||||||
|
};
|
||||||
|
|
||||||
export const checkNodeArgValue = (
|
export const checkNodeArgValue = (
|
||||||
data: NodeData,
|
data: NodeData,
|
||||||
arg: NodeArg,
|
arg: NodeArg,
|
||||||
@@ -264,7 +296,8 @@ export const checkNodeArgValue = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasArgOptions(arg)) {
|
if (hasArgOptions(arg)) {
|
||||||
const found = !!arg.options?.find((option) => option.value === value);
|
const options = getNodeArgOptions(arg, data.args ?? {});
|
||||||
|
const found = !!options?.find((option) => option.value === value);
|
||||||
const isOptional = value === undefined && isNodeArgOptional(arg);
|
const isOptional = value === undefined && isNodeArgOptional(arg);
|
||||||
if (!(found || isOptional)) {
|
if (!(found || isOptional)) {
|
||||||
error(`'${arg.name}=${JSON.stringify(value)}' is not a one of the option values`);
|
error(`'${arg.name}=${JSON.stringify(value)}' is not a one of the option values`);
|
||||||
|
|||||||
Reference in new issue
Block a user