Close a workflow's tabs when you delete it from the file list
This commit is contained in:
@@ -128,3 +128,37 @@ test("a file renamed by someone else re-points the open tab and rejoins its room
|
||||
sync.tick();
|
||||
assert.equal(sent.at(-1).room, null, "moved out of reach: the tab leaves the room");
|
||||
});
|
||||
|
||||
test("deleting a folder closes its tabs, the active one through ComfyUI's close", async () => {
|
||||
const a = { path: "workflows/team/a.json", isTemporary: false, isModified: true };
|
||||
const b = { path: "workflows/team/sub/b.json", isTemporary: false };
|
||||
const other = { path: "workflows/other.json", isTemporary: false };
|
||||
const store = {
|
||||
activeWorkflow: a, openWorkflows: [a, b, other],
|
||||
closeWorkflow(wf) { this.openWorkflows = this.openWorkflows.filter((w) => w !== wf); },
|
||||
};
|
||||
const commands = [];
|
||||
const client = Object.assign(new EventTarget(), {
|
||||
online: true, perms: { edit: true }, rooms: [], users: [],
|
||||
on(type, fn) { this.addEventListener(type, (e) => fn(e.detail)); },
|
||||
emit(type, detail) { this.dispatchEvent(new CustomEvent(type, { detail })); },
|
||||
send() { return true; },
|
||||
});
|
||||
const app = {
|
||||
extensionManager: { workflow: store, command: { async execute(id) {
|
||||
commands.push([id, store.activeWorkflow.isModified]);
|
||||
store.closeWorkflow(store.activeWorkflow);
|
||||
store.activeWorkflow = store.openWorkflows[0];
|
||||
} } },
|
||||
canvas: null, loadGraphData: async () => {},
|
||||
rootGraph: { serialize: () => ({ nodes: [], links: [], extra: {} }), extra: {}, getNodeById: () => null },
|
||||
};
|
||||
const sync = new RoomSync(app, new EventTarget(), client);
|
||||
const toasts = [];
|
||||
client.on("toast", (t) => toasts.push(t));
|
||||
await sync.closeTabsUnder("workflows/team");
|
||||
assert.deepEqual(store.openWorkflows, [other]);
|
||||
assert.deepEqual(commands, [["Workspace.CloseWorkflow", false]], "no unsaved-changes prompt");
|
||||
client.emit("room_closed", { room: "file:workflows/team/a.json", name: "a" });
|
||||
assert.equal(toasts.length, 0, "our own delete: no private-copy notice");
|
||||
});
|
||||
|
||||
@@ -310,10 +310,15 @@ export class FilesBrowser {
|
||||
async remove(e) {
|
||||
const what = e.type === "folder" ? `the folder "${e.name}"` : `"${e.name}"`;
|
||||
if (!(await this.confirm("Delete", `Delete ${what}? This cannot be undone.`))) return;
|
||||
// Its open tabs close first (ComfyUI would otherwise keep them as orphans).
|
||||
const path = `workflows/${e.path}`;
|
||||
const closing = this.sync.tabsUnder(path).map((wf) => `file:${wf.path}`);
|
||||
await this.sync.closeTabsUnder(path);
|
||||
let done;
|
||||
if (e.type === "folder") {
|
||||
await this.run(() => this.client.request("POST", "/rigshare/api/workspace/delete-folder", { path: e.path }), "Deleted");
|
||||
done = await this.run(() => this.client.request("POST", "/rigshare/api/workspace/delete-folder", { path: e.path }), "Deleted");
|
||||
} else {
|
||||
await this.run(async () => {
|
||||
done = await this.run(async () => {
|
||||
const store = this.app.extensionManager.workflow;
|
||||
await store.syncWorkflows?.();
|
||||
const wf = store.getWorkflowByPath?.(`workflows/${e.path}`);
|
||||
@@ -325,6 +330,7 @@ export class FilesBrowser {
|
||||
}
|
||||
}, "Deleted");
|
||||
}
|
||||
if (!done) for (const key of closing) this.sync.denied.delete(key); // still there: reopening it rejoins
|
||||
await this.refreshComfy();
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ export class RoomSync extends EventTarget {
|
||||
});
|
||||
client.on("room_closed", (msg) => {
|
||||
// The file was deleted: the tab stays open as a private copy.
|
||||
if (this.denied.has(msg.room) && !this.findTab({ key: msg.room })) return; // we deleted it and closed the tab
|
||||
this.denied.add(msg.room);
|
||||
if (msg.room === this.targetKey) {
|
||||
this.ready = false;
|
||||
@@ -524,6 +525,27 @@ export class RoomSync extends EventTarget {
|
||||
!wf.isTemporary && (wf.path === path || wf.path?.startsWith(path + "/")));
|
||||
}
|
||||
|
||||
/**
|
||||
* We are deleting a file (or folder): close its tabs first, without the
|
||||
* "unsaved changes" prompt (the user already confirmed the delete). The
|
||||
* active tab goes through ComfyUI's own close, which opens the next tab.
|
||||
*/
|
||||
async closeTabsUnder(path) {
|
||||
const store = this.store;
|
||||
const tabs = this.tabsUnder(path);
|
||||
if (!tabs.length) return;
|
||||
for (const wf of tabs) {
|
||||
this.denied.add(`file:${wf.path}`); // our own delete: no "private copy" notice
|
||||
if (wf === store.activeWorkflow) continue;
|
||||
store.closeWorkflow?.(wf);
|
||||
}
|
||||
const active = store.activeWorkflow;
|
||||
if (active && tabs.includes(active)) {
|
||||
active.isModified = false;
|
||||
await this.app.extensionManager?.command?.execute("Workspace.CloseWorkflow");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Someone renamed or moved a file (or folder) we have open: point our tabs
|
||||
* at the new path. The file already moved on the server, so ComfyUI's own
|
||||
|
||||
Reference in New Issue
Block a user