support search file desc
This commit is contained in:
1 parent
6d00be9d3b
commit
b693855012
13 files changed
+154
-40
No files matched your search
@@ -171,3 +171,8 @@ ipcMain.handle("trashItem", (_, arg: string) => {
|
||||
arg = arg.replace(/\//g, path.sep);
|
||||
shell.trashItem(arg).catch((e) => console.error(e));
|
||||
});
|
||||
|
||||
ipcMain.handle("showItemInFolder", (_, arg: string) => {
|
||||
arg = arg.replace(/\//g, path.sep);
|
||||
shell.showItemInFolder(arg);
|
||||
});
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "behavior3editor",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "behavior3editor",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "behavior3editor",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0",
|
||||
"main": "dist-electron/main/index.js",
|
||||
"description": "行为树编辑器",
|
||||
"author": "zhandouxiaojiji & codetypes",
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
"regnode.subtree": "Subtree: ",
|
||||
|
||||
"node.args": "Const arguments",
|
||||
"node.codePlaceholder": "a.b * 3 > 2",
|
||||
"node.debug": "Debug",
|
||||
"node.disabled": "Disabled",
|
||||
"node.deleteRootNodeDenied": "Unable to delete the root node",
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
"regnode.subtree": "子树:",
|
||||
|
||||
"node.args": "常量参数",
|
||||
"node.codePlaceholder": "a.b * 3 > 2",
|
||||
"node.debug": "调试",
|
||||
"node.disabled": "禁用",
|
||||
"node.deleteRootNodeDenied": "不能删除根节点",
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
{
|
||||
"isRelative": true,
|
||||
"nodeConfPath": "node-config.json",
|
||||
"workdir": "workdir"
|
||||
"files": [
|
||||
{
|
||||
"path": "workdir/hero.json",
|
||||
"desc": "英雄测试AI"
|
||||
},
|
||||
{
|
||||
"path": "workdir/monster.json",
|
||||
"desc": "怪物测试AI"
|
||||
},
|
||||
{
|
||||
"path": "workdir/sub/subtree1.json",
|
||||
"desc": "子树1"
|
||||
},
|
||||
{
|
||||
"path": "workdir/sub/subtree2.json",
|
||||
"desc": "子树2"
|
||||
},
|
||||
{
|
||||
"path": "workdir/subtree1.json",
|
||||
"desc": "子树1"
|
||||
},
|
||||
{
|
||||
"path": "workdir/subtree2.json",
|
||||
"desc": "子树2"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -131,6 +131,8 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
onEditingTree: useWorkspace((state) => state.onEditingTree),
|
||||
open: useWorkspace((state) => state.open),
|
||||
workdir: useWorkspace((state) => state.workdir),
|
||||
relative: useWorkspace((state) => state.relative),
|
||||
updateFileMeta: useWorkspace((state) => state.updateFileMeta),
|
||||
};
|
||||
|
||||
const searchInputRef = useRef<InputRef>(null);
|
||||
@@ -605,8 +607,8 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
desc: editor.desc,
|
||||
} as TreeModel;
|
||||
fs.writeFileSync(path, JSON.stringify(treeModel, null, 2));
|
||||
workspace.updateFileMeta(editor);
|
||||
editor.unsave = false;
|
||||
|
||||
editor.data = b3util.createTreeData(root);
|
||||
editor.autoId = b3util.refreshTreeDataId(editor.data);
|
||||
editor.graph.changeData(editor.data);
|
||||
@@ -664,7 +666,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
desc: data.desc,
|
||||
} as TreeModel;
|
||||
fs.writeFileSync(subpath, JSON.stringify(subtreeModel, null, 2));
|
||||
data.path = Path.relative(workspace.workdir, subpath).replaceAll(Path.sep, "/");
|
||||
data.path = workspace.relative(subpath);
|
||||
reload();
|
||||
pushHistory();
|
||||
onChange();
|
||||
@@ -858,7 +860,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
{
|
||||
id: editor.autoId++,
|
||||
name: "unknow",
|
||||
path: Path.relative(workspace.workdir, exploreFile).replaceAll(Path.sep, "/"),
|
||||
path: workspace.relative(exploreFile),
|
||||
},
|
||||
dstData.id
|
||||
);
|
||||
|
||||
@@ -551,7 +551,7 @@ export const Explorer: FC = () => {
|
||||
break;
|
||||
}
|
||||
case "revealFile":
|
||||
shell.showItemInFolder(node.path.replaceAll("/", Path.sep));
|
||||
ipcRenderer.invoke("showItemInFolder", node.path);
|
||||
break;
|
||||
case "rename": {
|
||||
node.editing = true;
|
||||
|
||||
@@ -9,7 +9,9 @@ import { FC, useEffect, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Markdown from "react-markdown";
|
||||
|
||||
interface OptionType extends DefaultOptionType {}
|
||||
interface OptionType extends DefaultOptionType {
|
||||
value: string;
|
||||
}
|
||||
|
||||
const TreeInspector: FC = () => {
|
||||
const workspace = {
|
||||
@@ -70,6 +72,7 @@ const NodeInspector: FC = () => {
|
||||
onEditingNode: useWorkspace((state) => state.onEditingNode),
|
||||
allFiles: useWorkspace((state) => state.allFiles),
|
||||
fileTree: useWorkspace((state) => state.fileTree),
|
||||
relative: useWorkspace((state) => state.relative),
|
||||
};
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
@@ -133,14 +136,15 @@ const NodeInspector: FC = () => {
|
||||
// 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, "/");
|
||||
workspace.allFiles.forEach((file) => {
|
||||
const value = workspace.relative(file.path);
|
||||
const desc = ""; //fileNode.desc ? `(${fileNode.desc})` : "";
|
||||
options.push({
|
||||
label: `${value}${desc}`,
|
||||
value: value,
|
||||
});
|
||||
});
|
||||
options.sort((a, b) => a.value.localeCompare(b.value));
|
||||
return options;
|
||||
}, [workspace.allFiles, workspace.fileTree]);
|
||||
|
||||
@@ -328,9 +332,7 @@ const NodeInspector: FC = () => {
|
||||
)}
|
||||
{type === "float" && <InputNumber disabled={disabled} onBlur={form.submit} />}
|
||||
{type === "boolean" && <Switch disabled={disabled} onChange={form.submit} />}
|
||||
{type === "code" && (
|
||||
<Input onBlur={form.submit} placeholder={t("node.codePlaceholder")} />
|
||||
)}
|
||||
{type === "code" && <Input disabled={disabled} onBlur={form.submit} />}
|
||||
{type === "enum" && (
|
||||
<Select disabled={disabled} onBlur={form.submit} onChange={form.submit}>
|
||||
{v.options?.map((value) => {
|
||||
@@ -475,7 +477,6 @@ const NodeDefInspector: FC = () => {
|
||||
</Divider>
|
||||
{data.args.map((v, i) => {
|
||||
const required = v.type.indexOf("?") == -1;
|
||||
const type = v.type.replace("?", "") as NodeArgType;
|
||||
return (
|
||||
<Form.Item
|
||||
name={`args.${i}.type`}
|
||||
|
||||
@@ -41,12 +41,10 @@ export const Menu: FC<LayoutProps> = () => {
|
||||
const { t } = useTranslation();
|
||||
const [trigger, setTrigger] = useState<DropDownProps["trigger"]>(["click"]);
|
||||
const workspace = {
|
||||
allFiles: useWorkspace((state) => state.allFiles),
|
||||
batchProject: useWorkspace((state) => state.batchProject),
|
||||
buildProject: useWorkspace((state) => state.buildProject),
|
||||
createProject: useWorkspace((state) => state.createProject),
|
||||
editing: useWorkspace((state) => state.editing),
|
||||
editors: useWorkspace((state) => state.editors),
|
||||
init: useWorkspace((state) => state.init),
|
||||
open: useWorkspace((state) => state.open),
|
||||
openProject: useWorkspace((state) => state.openProject),
|
||||
|
||||
@@ -20,23 +20,28 @@ export const TitleBar: FC<LayoutProps> = () => {
|
||||
onShowingSearch: useWorkspace((state) => state.onShowingSearch),
|
||||
open: useWorkspace((state) => state.open),
|
||||
name: useWorkspace((state) => state.path),
|
||||
relative: useWorkspace((state) => state.relative),
|
||||
};
|
||||
const searchOptions = useMemo(() => {
|
||||
const options: OptionType[] = [];
|
||||
workspace.allFiles.forEach((path) => {
|
||||
const value = Path.relative(workspace.fileTree!.path, path).replaceAll(Path.sep, "/");
|
||||
workspace.allFiles.forEach((file) => {
|
||||
const value = workspace.relative(file.path);
|
||||
const desc = file.desc ?? "";
|
||||
options.push({
|
||||
label: (
|
||||
<div>
|
||||
{Path.basename(value)}
|
||||
<span> </span>
|
||||
<span style={{ color: "gray", fontSize: "12px" }}>{Path.dirname(value)}</span>
|
||||
<span style={{ color: "gray", fontSize: "12px" }}>
|
||||
{Path.dirname(value)} {desc}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
value: value,
|
||||
path: path,
|
||||
value: `${value.toLocaleLowerCase()} ${desc.toLocaleLowerCase()}`,
|
||||
path: file.path,
|
||||
});
|
||||
});
|
||||
options.sort((a, b) => a.path.localeCompare(b.path));
|
||||
return options;
|
||||
}, [workspace.allFiles, workspace.fileTree]);
|
||||
return (
|
||||
@@ -101,7 +106,9 @@ export const TitleBar: FC<LayoutProps> = () => {
|
||||
workspace.open(option.path);
|
||||
}
|
||||
}}
|
||||
filterOption={(input, option) => (option?.value ?? "").includes(input)}
|
||||
filterOption={(input, option) =>
|
||||
(option?.value ?? "").includes(input.toLocaleLowerCase())
|
||||
}
|
||||
filterSort={(optionA, optionB) =>
|
||||
(optionA?.value ?? "")
|
||||
.toLowerCase()
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NodeDef, NodeModel, TreeGraphData, TreeModel, unknownNodeDef } from "@/
|
||||
import * as b3util from "@/misc/b3util";
|
||||
import { message } from "@/misc/hooks";
|
||||
import i18n from "@/misc/i18n";
|
||||
import { readJson } from "@/misc/util";
|
||||
import { readJson, writeJson } from "@/misc/util";
|
||||
import Path from "@/misc/path";
|
||||
import { Matrix, TreeGraph } from "@antv/g6";
|
||||
import { BrowserWindow, dialog } from "@electron/remote";
|
||||
@@ -61,9 +61,9 @@ export class EditorStore {
|
||||
constructor(path: string) {
|
||||
this.path = path;
|
||||
|
||||
const file = readJson(path);
|
||||
const file = readJson(path) as TreeModel;
|
||||
this.data = b3util.createTreeData(file.root);
|
||||
this.desc = file.desc;
|
||||
this.desc = file.desc ?? "";
|
||||
this.name = file.name || path.slice(0, -5);
|
||||
this.autoId = b3util.refreshTreeDataId(this.data);
|
||||
this.historyStack.push(b3util.createNode(this.data));
|
||||
@@ -95,6 +95,16 @@ export type EditTree = {
|
||||
data: TreeModel;
|
||||
};
|
||||
|
||||
export type FileMeta = {
|
||||
path: string;
|
||||
desc?: string;
|
||||
exists?: boolean;
|
||||
};
|
||||
|
||||
interface WorkspaceModel {
|
||||
files?: { path: string; desc: string }[];
|
||||
}
|
||||
|
||||
export type WorkspaceStore = {
|
||||
init: (project: string) => void;
|
||||
createProject: () => void;
|
||||
@@ -105,7 +115,11 @@ export type WorkspaceStore = {
|
||||
workdir: string;
|
||||
path: string;
|
||||
|
||||
allFiles: string[];
|
||||
loadWorkspace: () => void;
|
||||
saveWorkspace: () => void;
|
||||
updateFileMeta: (editor: EditorStore) => void;
|
||||
|
||||
allFiles: Map<string, FileMeta>;
|
||||
fileTree?: FileTreeType;
|
||||
editors: EditorStore[];
|
||||
editing?: EditorStore;
|
||||
@@ -117,6 +131,7 @@ export type WorkspaceStore = {
|
||||
edit: (path: string) => void;
|
||||
close: (path: string) => void;
|
||||
find: (path: string) => EditorStore | undefined;
|
||||
relative: (path: string) => string;
|
||||
|
||||
save: () => void;
|
||||
saveAs: () => void;
|
||||
@@ -189,7 +204,7 @@ const saveFile = (editor?: EditorStore) => {
|
||||
};
|
||||
|
||||
export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
allFiles: [],
|
||||
allFiles: new Map(),
|
||||
fileTree: undefined,
|
||||
editors: [],
|
||||
workdir: "",
|
||||
@@ -201,6 +216,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
try {
|
||||
workspace.workdir = Path.dirname(path).replaceAll(Path.sep, "/");
|
||||
workspace.path = path;
|
||||
workspace.loadWorkspace();
|
||||
workspace.loadTrees();
|
||||
workspace.loadNodeDefs();
|
||||
workspace.watch();
|
||||
@@ -257,8 +273,8 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
}
|
||||
try {
|
||||
let hasError = false;
|
||||
for (const path of workspace.allFiles) {
|
||||
const buildpath = buildDir + "/" + Path.relative(workspace.workdir, path);
|
||||
for (const [path] of workspace.allFiles) {
|
||||
const buildpath = buildDir + "/" + workspace.relative(path);
|
||||
console.log("build:", buildpath);
|
||||
const treeModel = b3util.createBuildData(path);
|
||||
if (!b3util.checkNodeData(treeModel?.root)) {
|
||||
@@ -290,8 +306,8 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
console.log("run script", scriptPath);
|
||||
const str = fs.readFileSync(scriptPath, "utf8");
|
||||
const batch = eval(str) as BatchScript;
|
||||
workspace.allFiles.forEach((path) => {
|
||||
const treeStr = fs.readFileSync(path, "utf8");
|
||||
workspace.allFiles.forEach((file) => {
|
||||
const treeStr = fs.readFileSync(file.path, "utf8");
|
||||
let tree: TreeModel | undefined = JSON.parse(treeStr);
|
||||
if (batch.processTree && tree) {
|
||||
tree = batch.processTree(tree);
|
||||
@@ -304,7 +320,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
processNode(tree.root);
|
||||
}
|
||||
if (tree) {
|
||||
fs.writeFileSync(path, JSON.stringify(tree));
|
||||
fs.writeFileSync(file.path, JSON.stringify(tree));
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -313,6 +329,40 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
loadWorkspace: () => {
|
||||
const workspace = get();
|
||||
const data = readJson(workspace.path) as WorkspaceModel;
|
||||
data.files?.forEach((file) => {
|
||||
workspace.allFiles.set(file.path, { path: file.path, desc: file.desc, exists: false });
|
||||
});
|
||||
},
|
||||
|
||||
saveWorkspace: () => {
|
||||
const workspace = get();
|
||||
const data: WorkspaceModel = {
|
||||
files: [],
|
||||
};
|
||||
workspace.allFiles.forEach((file) => {
|
||||
data.files?.push({
|
||||
path: workspace.relative(file.path),
|
||||
desc: file.desc ?? "",
|
||||
});
|
||||
});
|
||||
data.files?.sort((a, b) => a.path.localeCompare(b.path));
|
||||
writeJson(workspace.path, data);
|
||||
},
|
||||
|
||||
updateFileMeta: (editor) => {
|
||||
const workspace = get();
|
||||
const path = workspace.relative(editor.path);
|
||||
const file = workspace.allFiles.get(path);
|
||||
if (file && file.desc !== editor.desc) {
|
||||
file.desc = editor.desc;
|
||||
set({ allFiles: new Map(workspace.allFiles) });
|
||||
workspace.saveWorkspace();
|
||||
}
|
||||
},
|
||||
|
||||
isShowingSearch: false,
|
||||
onShowingSearch: (isShowingSearch) => {
|
||||
set({ isShowingSearch });
|
||||
@@ -326,6 +376,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
editor = new EditorStore(path);
|
||||
workspace.editors.push(editor);
|
||||
set({ editors: workspace.editors });
|
||||
workspace.updateFileMeta(editor);
|
||||
workspace.edit(editor.path);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -384,6 +435,11 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
return workspace.editors.find((v) => v.path === path);
|
||||
},
|
||||
|
||||
relative: (path: string) => {
|
||||
const workspace = get();
|
||||
return Path.relative(workspace.workdir, path).replaceAll(Path.sep, "/");
|
||||
},
|
||||
|
||||
save: () => {
|
||||
const workspace = get();
|
||||
saveFile(workspace.editing);
|
||||
@@ -431,15 +487,39 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
|
||||
};
|
||||
set({ fileTree: data });
|
||||
|
||||
const allFiles: string[] = [];
|
||||
const allFiles = workspace.allFiles;
|
||||
let updated = false;
|
||||
allFiles.forEach((file) => (file.exists = false));
|
||||
const collect = (fileNode?: FileTreeType) => {
|
||||
if (fileNode?.isLeaf) {
|
||||
allFiles.push(fileNode.path);
|
||||
const path = workspace.relative(fileNode.path);
|
||||
let fileMeta = allFiles.get(path);
|
||||
if (!fileMeta) {
|
||||
fileMeta = { path: fileNode.path };
|
||||
allFiles.set(fileNode.path, fileMeta);
|
||||
} else {
|
||||
fileMeta.path = fileNode.path;
|
||||
}
|
||||
fileMeta.exists = true;
|
||||
if (fileMeta.desc === undefined) {
|
||||
const file = readJson(fileNode.path) as TreeModel;
|
||||
fileMeta.desc = file.desc ?? "";
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
fileNode?.children?.forEach((child) => collect(child));
|
||||
};
|
||||
collect(data);
|
||||
allFiles.forEach((file, key) => {
|
||||
if (!file.exists) {
|
||||
allFiles.delete(key);
|
||||
updated = true;
|
||||
}
|
||||
});
|
||||
set({ allFiles });
|
||||
if (updated) {
|
||||
workspace.saveWorkspace();
|
||||
}
|
||||
},
|
||||
|
||||
loadNodeDefs: () => {
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export interface NodeArg {
|
||||
name: string;
|
||||
type: string;
|
||||
desc: string;
|
||||
default?: string;
|
||||
default?: unknown;
|
||||
options?: NodeArgOption[];
|
||||
}
|
||||
export interface NodeDef {
|
||||
|
||||
Reference in new issue
Block a user