import { EditNode, EditTree, useWorkspace } from "@/contexts/workspace-context"; import { NodeArgType, NodeModel, TreeGraphData, TreeModel } from "@/misc/b3type"; import { Hotkey, isMacos } from "@/misc/keys"; import Path from "@/misc/path"; import { AutoComplete, Divider, Flex, Form, Input, InputNumber, Select, Switch } from "antd"; import { DefaultOptionType } from "antd/es/select"; import { FC, useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import Markdown from "react-markdown"; interface OptionType extends DefaultOptionType {} const TreeInspector: FC = () => { const workspace = { editing: useWorkspace((state) => state.editing), editingTree: useWorkspace((state) => state.editingTree)!, }; const { t } = useTranslation(); const [form] = Form.useForm(); // set form values useEffect(() => { const data = workspace.editingTree.data; form.resetFields(); form.setFieldValue("name", data.name); form.setFieldValue("desc", data.desc); }, [workspace.editingTree]); const finish = (values: any) => { const data = {} as TreeModel; data.name = values.name; data.desc = values.desc || undefined; workspace.editing?.dispatch("updateTree", { data: { name: values.name, desc: values.desc || undefined, }, } as EditTree); }; return ( <>
{t("tree.overview")}
); }; const NodeInspector: FC = () => { const workspace = { editing: useWorkspace((state) => state.editing), editingNode: useWorkspace((state) => state.editingNode)!, getNodeDef: useWorkspace((state) => state.getNodeDef), nodeDefs: useWorkspace((state) => state.nodeDefs), onEditingNode: useWorkspace((state) => state.onEditingNode), allFiles: useWorkspace((state) => state.allFiles), fileTree: useWorkspace((state) => state.fileTree), }; const { t } = useTranslation(); const [form] = Form.useForm(); // set form values useEffect(() => { const data = workspace.editingNode.data; const def = workspace.getNodeDef(workspace.editingNode.data.name); form.resetFields(); form.setFieldValue("id", data.id); form.setFieldValue("name", data.name); form.setFieldValue("type", def.type); form.setFieldValue("desc", data.desc); form.setFieldValue("debug", data.debug); form.setFieldValue("path", data.path); def.args?.forEach((v) => { form.setFieldValue(`args.${v.name}`, data.args?.[v.name]); }); def.input?.forEach((_, i) => { form.setFieldValue(`input.${i}`, data.input?.[i]); }); def.output?.forEach((_, i) => { form.setFieldValue(`output.${i}`, data.output?.[i]); }); }, [workspace.editingNode]); // auto complete for node const nodeOptions = useMemo(() => { const options: OptionType[] = []; workspace.nodeDefs.forEach((e) => { options.push({ label: `${e.name}(${e.desc})`, value: e.name }); }); return options; }, [workspace.nodeDefs]); // auto complete for input and output const inoutVarOptions = useMemo(() => { const options: OptionType[] = []; const collect = (node?: TreeGraphData) => { if (node) { node.input?.forEach((v, i) => { const desc = node.def.input?.[i] ?? ""; if (v && !options.find((option) => option.value === v)) { options.push({ label: `${v}(${desc})`, value: v }); } }); node.output?.forEach((v, i) => { const desc = node.def.output?.[i] ?? ""; if (v && !options.find((option) => option.value === v)) { options.push({ label: `${v}(${desc})`, value: v }); } }); node.children?.forEach((child) => collect(child)); } }; collect(workspace.editing?.data); return options; }, [workspace.editing]); // auto complete for subtree const subtreeOptions = useMemo(() => { const options: OptionType[] = []; workspace.allFiles.forEach((path) => { const value = Path.relative(workspace.fileTree!.path, path).replaceAll(Path.sep, "/"); const desc = ""; //fileNode.desc ? `(${fileNode.desc})` : ""; options.push({ label: `${value}${desc}`, value: value, }); }); return options; }, [workspace.allFiles, workspace.fileTree]); const editingNode = workspace.editingNode; const def = workspace.getNodeDef(editingNode.data.name); const disabled = !editingNode.editable; // update value const finish = (values: any) => { const data = {} as NodeModel; data.id = editingNode.data.id; data.name = values.name; data.debug = values.debug || undefined; data.desc = values.desc || undefined; data.path = values.path || undefined; def.args?.forEach((arg) => { const v = values[`args.${arg.name}`]; if (v !== null && v !== undefined && v !== "") { data.args ||= {}; data.args[arg.name] = v; } }); def.input?.forEach((_, i) => { const v = values[`input.${i}`]; data.input ||= []; data.input.push(v ?? ""); }); def.output?.forEach((_, i) => { const v = values[`output.${i}`]; data.output ||= []; data.output.push(v ?? ""); }); workspace.editing?.dispatch("updateNode", { data: data, } as EditNode); }; // change node def const changeNodeDef = (newname: string) => { if (editingNode.data.name !== newname) { workspace.onEditingNode({ data: { id: editingNode.data.id, name: workspace.nodeDefs.get(newname)?.name ?? newname, desc: editingNode.data.desc, debug: editingNode.data.debug, }, editable: editingNode.editable, }); finish(form.getFieldsValue()); } else { form.submit(); } }; const changeSubtree = () => { if (form.getFieldValue("path") !== editingNode.data.path) { finish(form.getFieldsValue()); } else { form.submit(); } }; return ( <>
{def.desc}
{t("node.id")}
} > {t("node.type")} } > changeNodeDef(form.getFieldValue("name"))} onSelect={changeNodeDef} onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()} filterOption={(inputValue: string, option?: OptionType) => { const label = option!.label as string; return label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1; }} /> e.code === Hotkey.Escape && e.preventDefault()} filterOption={(inputValue: string, option?: OptionType) => { const label = option!.label as string; return label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1; }} /> {def.doc} {def.input && def.input.length > 0 && ( <>

{t("node.inputVariable")}

{def.input.map((v, i) => { const required = v.indexOf("?") == -1; const desc = v.replace("?", ""); return ( e.code === Hotkey.Escape && e.preventDefault()} filterOption={(inputValue: string, option?: OptionType) => { const label = option!.label as string; return label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1; }} /> ); })} )} {def.args && def.args.length > 0 && ( <>

{t("node.args")}

{def.args.map((v) => { const required = v.type.indexOf("?") == -1; const type = v.type.replace("?", "") as NodeArgType; return ( {type === "string" && } {type === "int" && ( )} {type === "float" && } {type === "boolean" && } {type === "code" && ( )} {type === "enum" && ( )} ); })} )} {def.output && def.output.length > 0 && ( <>

{t("node.outputVariable")}

{def.output.map((v, i) => { const required = v.indexOf("?") == -1; const desc = v.replace("?", ""); return ( e.code === Hotkey.Escape && e.preventDefault()} filterOption={(inputValue: string, option?: OptionType) => { const label = option!.label as string; return label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1; }} /> ); })} )} ); }; const NodeDefInspector: FC = () => { const workspace = { editingNodeDef: useWorkspace((state) => state.editingNodeDef)!, }; const { t } = useTranslation(); const [form] = Form.useForm(); const data = workspace.editingNodeDef.data; // set form values useEffect(() => { form.resetFields(); form.setFieldValue("name", data.name); form.setFieldValue("type", data.type); form.setFieldValue("desc", data.desc); form.setFieldValue("doc", data.doc); data.input?.forEach((v, i) => { form.setFieldValue(`input.${i}.name`, v.replaceAll("?", "")); }); data.output?.forEach((v, i) => { form.setFieldValue(`output.${i}.name`, v.replaceAll("?", "")); }); data.args?.forEach((v, i) => { form.setFieldValue(`args.${i}.type`, v.type.replaceAll("?", "")); }); }, [workspace.editingNodeDef]); return ( <>
{t("nodeDefinition")}
{t("node.name")}
} > {t("node.type")} } > {t("node.desc")} } > {data.doc} {data.input && data.input.length > 0 && ( <>

{t("node.inputVariable")}

{data.input.map((v, i) => { const required = v.indexOf("?") == -1; return ( ); })} )} {data.args && data.args.length > 0 && ( <>

{t("node.args")}

{data.args.map((v, i) => { const required = v.type.indexOf("?") == -1; const type = v.type.replace("?", "") as NodeArgType; return ( ); })} )} {data.output && data.output.length > 0 && ( <>

{t("node.outputVariable")}

{data.output.map((v, i) => { const required = v.indexOf("?") == -1; return ( ); })} )} ); }; export const Inspector: FC = () => { const workspace = { editingNode: useWorkspace((state) => state.editingNode), editingTree: useWorkspace((state) => state.editingTree), editingNodeDef: useWorkspace((state) => state.editingNodeDef), }; let isEditingNode = false; let isEditingTree = false; let isEditingNodeDef = false; if (workspace.editingNodeDef) { isEditingNodeDef = true; } else if (workspace.editingTree) { isEditingTree = true; } else if (workspace.editingNode) { isEditingNode = true; } return ( {isEditingNodeDef && } {isEditingTree && } {isEditingNode && } ); };