store open files in history

This commit is contained in:
codetypes committed 2025-04-15 10:36:08 +08:00
1 parent 5fb19c5c08
commit 3b1091cf87
8 files changed
+119 -62

No files matched your search

+1 -22
View File
@@ -109,7 +109,6 @@ if (!app.requestSingleInstanceLock()) {
interface Workspace { interface Workspace {
projectPath?: string; projectPath?: string;
files: { path: string; active: boolean }[];
window: BrowserWindow; window: BrowserWindow;
} }
@@ -244,27 +243,7 @@ ipcMain.handle("open-win", (e, arg) => {
ipcMain.handle("ready-to-show", (e) => { ipcMain.handle("ready-to-show", (e) => {
const workspace = windows.find((v) => v.window.webContents.id === e.sender.id); const workspace = windows.find((v) => v.window.webContents.id === e.sender.id);
if (workspace && workspace.projectPath) { if (workspace && workspace.projectPath) {
workspace.window.webContents.send("open-project", workspace.projectPath, workspace.files); workspace.window.webContents.send("open-project", workspace.projectPath);
}
});
ipcMain.handle("open-file", (e, path: string) => {
const workspace = windows.find((v) => v.window.webContents.id === e.sender.id);
if (workspace) {
const entry = workspace.files.find((v) => v.path === path);
workspace.files.forEach((v) => (v.active = false));
if (entry) {
entry.active = true;
} else {
workspace.files.push({ path, active: true });
}
}
});
ipcMain.handle("close-file", (e, path: string) => {
const workspace = windows.find((v) => v.window.webContents.id === e.sender.id);
if (workspace) {
workspace.files = workspace.files.filter((v) => v.path !== path);
} }
}); });
+3 -3
View File
@@ -144,7 +144,7 @@ export const Editor: FC<EditorProps> = ({ onChange, data: editor, ...props }) =>
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
e.stopPropagation(); e.stopPropagation();
editor.dispatch(hotkeyMap[key]); editor.dispatch?.(hotkeyMap[key]);
} }
); );
@@ -153,7 +153,7 @@ export const Editor: FC<EditorProps> = ({ onChange, data: editor, ...props }) =>
return; return;
} }
e.stopPropagation(); e.stopPropagation();
editor.dispatch(hotkeyMap[key]); editor.dispatch?.(hotkeyMap[key]);
}); });
const searchInputRef = useRef<InputRef>(null); const searchInputRef = useRef<InputRef>(null);
@@ -493,7 +493,7 @@ export const Editor: FC<EditorProps> = ({ onChange, data: editor, ...props }) =>
)} )}
<Dropdown <Dropdown
menu={{ items: menuItems, onClick: (info) => editor.dispatch(info.key as EditEvent) }} menu={{ items: menuItems, onClick: (info) => editor.dispatch?.(info.key as EditEvent) }}
trigger={["contextMenu"]} trigger={["contextMenu"]}
> >
<div tabIndex={-1} style={{ width: "100%", height: "100%" }} ref={graphRef} /> <div tabIndex={-1} style={{ width: "100%", height: "100%" }} ref={graphRef} />
+4 -4
View File
@@ -105,11 +105,11 @@ const renameFile = (oldPath: string, newPath: string) => {
for (const editor of workspace.editors) { for (const editor of workspace.editors) {
if (isDirectory) { if (isDirectory) {
if (editor.path.startsWith(oldPath)) { if (editor.path.startsWith(oldPath)) {
editor.dispatch("rename", editor.path.replace(oldPath, newPath)); editor.dispatch?.("rename", editor.path.replace(oldPath, newPath));
} }
} else { } else {
if (editor.path === oldPath) { if (editor.path === oldPath) {
editor.dispatch("rename", newPath); editor.dispatch?.("rename", newPath);
} }
} }
} }
@@ -406,12 +406,12 @@ const doMoveFile = (path: string, newPath: string) => {
fs.renameSync(path, newPath); fs.renameSync(path, newPath);
for (const editor of workspace.editors) { for (const editor of workspace.editors) {
if (editor.path.startsWith(path)) { if (editor.path.startsWith(path)) {
editor.dispatch("rename", destDir + "/" + Path.basename(editor.path)); editor.dispatch?.("rename", destDir + "/" + Path.basename(editor.path));
} }
console.log("editor move", editor.path === newPath, editor.path, newPath); console.log("editor move", editor.path === newPath, editor.path, newPath);
if (editor.path.startsWith(newPath)) { if (editor.path.startsWith(newPath)) {
console.log("editor reload", editor.path === newPath, editor.path, newPath); console.log("editor reload", editor.path === newPath, editor.path, newPath);
editor.dispatch("refresh"); editor.dispatch?.("refresh");
} }
} }
}; };
+4 -4
View File
@@ -105,7 +105,7 @@ const VarDeclItem: FC<VarItemProps> = ({ name, onChange, onRemove, disabled, ...
borderTop: "1px solid #3d506c", borderTop: "1px solid #3d506c",
borderBottom: "1px solid #3d506c", borderBottom: "1px solid #3d506c",
}} }}
onClick={() => value.name && editing?.dispatch("clickVar", value.name)} onClick={() => value.name && editing?.dispatch?.("clickVar", value.name)}
> >
<AimOutlined /> <AimOutlined />
<span style={{ marginLeft: 4 }}>{value?.count ?? 0}</span> <span style={{ marginLeft: 4 }}>{value?.count ?? 0}</span>
@@ -254,7 +254,7 @@ const TreeInspector: FC = () => {
// 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) => {
workspace.editing?.dispatch("updateTree", { workspace.editing?.dispatch?.("updateTree", {
name: values.name, name: values.name,
desc: values.desc, desc: values.desc,
export: values.export, export: values.export,
@@ -812,7 +812,7 @@ const NodeInspector: FC = () => {
data.output = []; data.output = [];
} }
workspace.editing?.dispatch("updateNode", { workspace.editing?.dispatch?.("updateNode", {
data: data, data: data,
} as EditNode); } as EditNode);
}; };
@@ -1500,7 +1500,7 @@ const NodeInspector: FC = () => {
type="primary" type="primary"
style={{ width: "100%" }} style={{ width: "100%" }}
icon={<EditOutlined />} icon={<EditOutlined />}
onClick={() => workspace.editing?.dispatch("editSubtree")} onClick={() => workspace.editing?.dispatch?.("editSubtree")}
> >
{t("editSubtree")} {t("editSubtree")}
</Button> </Button>
+2 -2
View File
@@ -29,8 +29,8 @@ const getFocusedWebContents = () => {
return BrowserWindow.getFocusedWindow()?.webContents; return BrowserWindow.getFocusedWindow()?.webContents;
}; };
ipcRenderer.on("open-project", (_, dir, files) => { ipcRenderer.on("open-project", (_, dir) => {
useWorkspace.getState().init(dir, files); useWorkspace.getState().init(dir);
}); });
ipcRenderer.on("refresh-app-men", () => { ipcRenderer.on("refresh-app-men", () => {
+5 -5
View File
@@ -80,12 +80,12 @@ export const Workspace: FC = () => {
useKeyDown(Hotkey.SearchNode, keysRef, (event) => { useKeyDown(Hotkey.SearchNode, keysRef, (event) => {
event.preventDefault(); event.preventDefault();
workspace.editing?.dispatch("searchNode"); workspace.editing?.dispatch?.("searchNode");
}); });
useKeyDown(Hotkey.JumpNode, keysRef, (event) => { useKeyDown(Hotkey.JumpNode, keysRef, (event) => {
event.preventDefault(); event.preventDefault();
workspace.editing?.dispatch("jumpNode"); workspace.editing?.dispatch?.("jumpNode");
}); });
useEffect(() => { useEffect(() => {
@@ -118,7 +118,7 @@ export const Workspace: FC = () => {
type="primary" type="primary"
onClick={() => { onClick={() => {
editor.alertReload = false; editor.alertReload = false;
editor.dispatch("reload"); editor.dispatch?.("reload");
alert.destroy(); alert.destroy();
keysRef.current?.focus(); keysRef.current?.focus();
setShowingAlert(false); setShowingAlert(false);
@@ -164,7 +164,7 @@ export const Workspace: FC = () => {
<Button <Button
type="primary" type="primary"
onClick={() => { onClick={() => {
editor.dispatch("save"); editor.dispatch?.("save");
workspace.close(editor.path); workspace.close(editor.path);
alert.destroy(); alert.destroy();
keysRef.current?.focus(); keysRef.current?.focus();
@@ -232,7 +232,7 @@ export const Workspace: FC = () => {
<Button <Button
type="primary" type="primary"
onClick={() => { onClick={() => {
unsaves.forEach((editor) => editor.dispatch("save")); unsaves.forEach((editor) => editor.dispatch?.("save"));
alert.destroy(); alert.destroy();
window.close(); window.close();
setShowingAlert(false); setShowingAlert(false);
+81 -9
View File
@@ -7,10 +7,21 @@ import { useWorkspace } from "./workspace-context";
const settingPath = app.getPath("userData") + "/settings.json"; const settingPath = app.getPath("userData") + "/settings.json";
export type OpenEditor = {
path: string;
active: boolean;
};
export type ProjectSetting = {
path: string;
buildDir: string;
editors: OpenEditor[];
};
export type SettingModel = { export type SettingModel = {
recent: string[]; recent: string[];
buildDir: string;
layout: NodeLayout; layout: NodeLayout;
projects: ProjectSetting[];
}; };
export type SettingStore = { export type SettingStore = {
@@ -20,8 +31,12 @@ export type SettingStore = {
save: () => void; save: () => void;
appendRecent: (path: string) => void; appendRecent: (path: string) => void;
removeRecent: (path: string) => void; removeRecent: (path: string) => void;
setBuildDir: (dir: string) => void;
setLayout: (layout: "compact" | "normal") => void; setLayout: (layout: "compact" | "normal") => void;
setBuildDir: (project: string, dir: string) => void;
getBuildDir: (project: string) => string;
openEditor: (project: string, path: string) => void;
closeEditor: (project: string, path: string) => void;
getEditors: (project: string) => OpenEditor[];
}; };
export const useSetting = create<SettingStore>((set, get) => ({ export const useSetting = create<SettingStore>((set, get) => ({
@@ -29,22 +44,25 @@ export const useSetting = create<SettingStore>((set, get) => ({
recent: [], recent: [],
buildDir: "", buildDir: "",
layout: "compact", layout: "compact",
checkExpr: true, projects: [],
}, },
load: () => { load: () => {
try { try {
if (fs.existsSync(settingPath)) { if (fs.existsSync(settingPath)) {
const settings = readJson(settingPath) as SettingModel; const settings = readJson(settingPath) as SettingModel;
settings.layout = settings.layout || "compact"; settings.layout = settings.layout || "compact";
settings.projects = settings.projects || [];
set({ data: settings }); set({ data: settings });
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
}, },
save: () => { save: () => {
writeJson(settingPath, get().data); writeJson(settingPath, get().data);
}, },
appendRecent: (path: string) => { appendRecent: (path: string) => {
const { data, save } = get(); const { data, save } = get();
const recent = data.recent.filter((v) => v !== path); const recent = data.recent.filter((v) => v !== path);
@@ -52,21 +70,75 @@ export const useSetting = create<SettingStore>((set, get) => ({
set({ data: { ...data, recent } }); set({ data: { ...data, recent } });
save(); save();
}, },
removeRecent: (path: string) => { removeRecent: (path: string) => {
const { data, save } = get(); const { data, save } = get();
const recent = data.recent.filter((v) => v !== path); const recent = data.recent.filter((v) => v !== path);
set({ data: { ...data, recent } }); set({ data: { ...data, recent } });
save(); save();
}, },
setBuildDir: (dir: string) => {
const { data, save } = get();
set({ data: { ...data, buildDir: dir } });
save();
},
setLayout: (layout: "compact" | "normal") => { setLayout: (layout: "compact" | "normal") => {
const { data, save } = get(); const { data, save } = get();
set({ data: { ...data, layout } }); set({ data: { ...data, layout } });
save(); save();
useWorkspace.getState().editing?.dispatch("refresh"); useWorkspace.getState().editing?.dispatch?.("refresh");
},
setBuildDir: (projectPath: string, dir: string) => {
const { data, save } = get();
let project = data.projects.find((v) => v.path === projectPath);
if (!project) {
project = {
path: projectPath,
buildDir: "",
editors: [],
};
}
project.buildDir = dir;
set({ data: { ...data, projects: data.projects } });
save();
},
getBuildDir: (project: string) => {
const { data } = get();
return data.projects.find((v) => v.path === project)?.buildDir ?? "";
},
openEditor: (projectPath: string, filePath: string) => {
const { data, save } = get();
let project = data.projects.find((v) => v.path === projectPath);
if (!project) {
project = {
path: projectPath,
buildDir: "",
editors: [],
};
data.projects.push(project);
}
const editor = project.editors.find((v) => v.path === filePath);
project.editors.forEach((v) => (v.active = false));
if (editor) {
editor.active = true;
} else {
project.editors.push({ path: filePath, active: true });
}
set({ data: { ...data, projects: data.projects } });
save();
},
closeEditor: (project: string, path: string) => {
const { data, save } = get();
const projectSetting = data.projects.find((v) => v.path === project);
if (projectSetting) {
projectSetting.editors = projectSetting.editors.filter((v) => v.path !== path);
}
set({ data: { ...data, projects: data.projects } });
save();
},
getEditors: (project: string) => {
const { data } = get();
return data.projects.find((v) => v.path === project)?.editors ?? [];
}, },
})); }));
+19 -13
View File
@@ -47,7 +47,7 @@ export class EditorStore {
alertReload: boolean = false; alertReload: boolean = false;
focusId?: string | null; focusId?: string | null;
dispatch!: (event: EditEvent, data?: unknown) => void; dispatch?: (event: EditEvent, data?: unknown) => void;
constructor(path: string) { constructor(path: string) {
this.path = path; this.path = path;
@@ -113,7 +113,7 @@ export interface WorkspaceModel {
} }
export type WorkspaceStore = { export type WorkspaceStore = {
init: (project: string, files?: { path: string; active: boolean }[]) => void; init: (project: string) => void;
createProject: () => void; createProject: () => void;
openProject: (project?: string) => void; openProject: (project?: string) => void;
batchProject: () => void; batchProject: () => void;
@@ -212,7 +212,7 @@ const loadFileTree = (workdir: string, filename: string) => {
const saveFile = (editor?: EditorStore) => { const saveFile = (editor?: EditorStore) => {
if (editor?.changed) { if (editor?.changed) {
editor.dispatch("save"); editor.dispatch?.("save");
} }
}; };
@@ -225,8 +225,9 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
path: "", path: "",
settings: {}, settings: {},
init: (path, files) => { init: (path) => {
const workspace = get(); const workspace = get();
const files = useSetting.getState().getEditors(path);
if (!workspace.workdir) { if (!workspace.workdir) {
try { try {
workspace.workdir = Path.dirname(path).replaceAll(Path.sep, "/"); workspace.workdir = Path.dirname(path).replaceAll(Path.sep, "/");
@@ -236,9 +237,10 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
workspace.loadNodeDefs(); workspace.loadNodeDefs();
workspace.watch(); workspace.watch();
useSetting.getState().appendRecent(path); useSetting.getState().appendRecent(path);
if (files?.length) { if (files.length) {
for (const entry of files) { for (const entry of files) {
try { try {
entry.path = Path.posixPath(entry.path);
const editor = new EditorStore(entry.path); const editor = new EditorStore(entry.path);
workspace.editors.push(editor); workspace.editors.push(editor);
if (entry.active) { if (entry.active) {
@@ -271,7 +273,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
}); });
b3util.setCheckExpr(checkExpr); b3util.setCheckExpr(checkExpr);
saveWorkspace(); saveWorkspace();
useWorkspace.getState().editing?.dispatch("refresh"); get().editing?.dispatch?.("refresh");
}, },
setupBuildScript: () => { setupBuildScript: () => {
@@ -365,17 +367,18 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
buildProject: async () => { buildProject: async () => {
const workspace = get(); const workspace = get();
const setting = useSetting.getState();
if (workspace.path) { if (workspace.path) {
if (!buildDir) { if (!buildDir) {
buildDir = dialog.showOpenDialogSync({ buildDir = dialog.showOpenDialogSync({
properties: ["openDirectory", "createDirectory"], properties: ["openDirectory", "createDirectory"],
defaultPath: useSetting.getState().data.buildDir, defaultPath: setting.getBuildDir(workspace.path),
})?.[0]; })?.[0];
} }
} }
if (buildDir) { if (buildDir) {
for (const editor of workspace.editors) { for (const editor of workspace.editors) {
editor.dispatch("save"); editor.dispatch?.("save");
} }
const debug = console.debug; const debug = console.debug;
console.debug = () => {}; console.debug = () => {};
@@ -386,7 +389,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
} else { } else {
message.success(i18n.t("buildCompleted")); message.success(i18n.t("buildCompleted"));
} }
useSetting.getState().setBuildDir(buildDir); setting.setBuildDir(workspace.path, buildDir);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
message.error(i18n.t("buildFailed")); message.error(i18n.t("buildFailed"));
@@ -511,6 +514,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
edit: (path, focusId) => { edit: (path, focusId) => {
const workspace = get(); const workspace = get();
const setting = useSetting.getState();
const editor = workspace.editors.find((v) => v.path === path); const editor = workspace.editors.find((v) => v.path === path);
if (editor) { if (editor) {
editor.focusId = focusId; editor.focusId = focusId;
@@ -519,11 +523,11 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
if (editor) { if (editor) {
workspace.onEditingTree(editor); workspace.onEditingTree(editor);
} }
ipcRenderer.invoke("open-file", path);
}, },
close: (path) => { close: (path) => {
const workspace = get(); const workspace = get();
const setting = useSetting.getState();
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); const editor = workspace.editors.find((v) => v.path === path);
@@ -538,7 +542,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
set({ editingNode: undefined, editingTree: undefined }); set({ editingNode: undefined, editingTree: undefined });
} }
set({ editing: editting, editors: editors }); set({ editing: editting, editors: editors });
ipcRenderer.invoke("close-file", path); setting.closeEditor(workspace.path, path);
}, },
find: (path) => { find: (path) => {
@@ -605,7 +609,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
editor.alertReload = true; editor.alertReload = true;
set({ modifiedTime: Date.now() }); set({ modifiedTime: Date.now() });
} else { } else {
editor.dispatch("reload"); editor.dispatch?.("reload");
} }
} }
} }
@@ -674,7 +678,7 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
const workspace = get(); const workspace = get();
b3util.initWorkdir(workspace.workdir, message.error.bind(message)); b3util.initWorkdir(workspace.workdir, message.error.bind(message));
set({ nodeDefs: b3util.nodeDefs, groupDefs: b3util.groupDefs }); set({ nodeDefs: b3util.nodeDefs, groupDefs: b3util.groupDefs });
workspace.editing?.dispatch("refresh"); workspace.editing?.dispatch?.("refresh");
}, },
// node edit // node edit
@@ -689,7 +693,9 @@ export const useWorkspace = create<WorkspaceStore>((set, get) => ({
// tree edit // tree edit
onEditingTree: (editor) => { onEditingTree: (editor) => {
const workspace = get(); const workspace = get();
const setting = useSetting.getState();
workspace.refresh(editor.path); workspace.refresh(editor.path);
setting.openEditor(workspace.path, editor.path);
set({ set({
editingTree: { editingTree: {
...editor.data, ...editor.data,