= () => {
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: (
{Path.basename(value)}
- {Path.dirname(value)}
+
+ {Path.dirname(value)} {desc}
+
),
- 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 = () => {
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()
diff --git a/src/contexts/workspace-context.ts b/src/contexts/workspace-context.ts
index dab73fa..2611d65 100644
--- a/src/contexts/workspace-context.ts
+++ b/src/contexts/workspace-context.ts
@@ -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;
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((set, get) => ({
- allFiles: [],
+ allFiles: new Map(),
fileTree: undefined,
editors: [],
workdir: "",
@@ -201,6 +216,7 @@ export const useWorkspace = create((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((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((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((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((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((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((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((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: () => {
diff --git a/src/index.scss b/src/index.scss
index b26a781..f126461 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -86,8 +86,13 @@ body {
cursor: pointer !important;
}
-.ant-tree .ant-tree-node-content-wrapper {
+.ant-tree-node-content-wrapper {
padding-left: 0 !important;
+ display: inline-flex;
+}
+
+.ant-tree-title {
+ display: contents;
}
.ant-tree-switcher.ant-tree-switcher-noop,
@@ -179,11 +184,11 @@ body {
}
// editor
-.b3-editor-button-filter {
+.b3-editor-filter {
width: 20px !important;
height: 20px !important;
}
-.b3-editor-button-filter-selected {
+.b3-editor-filter-selected {
background-color: var(--b3-color-selected) !important;
}
diff --git a/src/misc/b3type.ts b/src/misc/b3type.ts
index 8d52416..0e9e579 100644
--- a/src/misc/b3type.ts
+++ b/src/misc/b3type.ts
@@ -12,7 +12,7 @@ export interface NodeArg {
name: string;
type: string;
desc: string;
- default?: string;
+ default?: unknown;
options?: NodeArgOption[];
}
export interface NodeDef {
@@ -36,6 +36,7 @@ export interface NodeModel {
output?: string[];
children?: NodeModel[];
debug?: boolean;
+ disabled?: boolean;
path?: string;
}
@@ -54,6 +55,7 @@ export interface TreeGraphData extends G6TreeGraphData {
children?: TreeGraphData[];
def: NodeDef;
debug?: boolean;
+ disabled?: boolean;
parent?: string;
path?: string;
lastModified?: number;
diff --git a/src/misc/b3util.ts b/src/misc/b3util.ts
index 4fdfc83..e84668b 100644
--- a/src/misc/b3util.ts
+++ b/src/misc/b3util.ts
@@ -35,7 +35,8 @@ export const isNodeEqual = (node1: NodeModel, node2: NodeModel) => {
node1.name === node2.name &&
node1.desc === node2.desc &&
node1.path === node2.path &&
- node1.debug === node2.debug
+ node1.debug === node2.debug &&
+ node1.disabled === node2.disabled
) {
const def = useWorkspace.getState().getNodeDef(node1.name);
@@ -142,6 +143,7 @@ export const checkNodeData = (data: NodeModel | null | undefined) => {
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;
@@ -158,6 +160,7 @@ export const createNode = (data: TreeGraphData, includeChildren: boolean = true)
desc: data.desc,
path: data.path,
debug: data.debug,
+ disabled: data.disabled,
};
if (data.input) {
node.input = [];
@@ -280,6 +283,7 @@ export const createTreeData = (node: NodeModel, parent?: string) => {
input: node.input,
output: node.output,
debug: node.debug,
+ disabled: node.disabled,
def: workspace.getNodeDef(node.name),
parent: parent,
};
@@ -310,6 +314,7 @@ export const createTreeData = (node: NodeModel, parent?: string) => {
treeData.lastModified = fs.statSync(subtreePath).mtimeMs;
treeData.path = node.path;
treeData.debug = node.debug;
+ treeData.disabled = node.disabled;
treeData.parent = parent;
treeData.id = node.id.toFixed();
treeData.size = calcTreeDataSize(treeData);
@@ -346,6 +351,7 @@ export const createFileData = (data: TreeGraphData, includeSubtree?: boolean) =>
input: data.input || undefined,
output: data.output || undefined,
debug: data.debug || undefined,
+ disabled: data.disabled || undefined,
path: data.path || undefined,
};
if (data.children?.length && (includeSubtree || !isSubtreeRoot(data))) {