import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js" import { ComfyDialog, $el } from "../../scripts/ui.js"; import { manager_instance, rebootAPI, show_message, handle403Response, loadCss } from "./common.js"; import { buildGuiFrame } from "./comfyui-gui-builder.js"; loadCss("./snapshot.css"); async function restore_snapshot(target) { if(SnapshotManager.instance) { try { const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" }); if(response.status == 403) { await handle403Response(response); return false; } if(response.status == 400) { show_message(`Restore snapshot failed: ${target.title} / ${exception}`); } app.ui.dialog.close(); return true; } catch(exception) { show_message(`Restore snapshot failed: ${target.title} / ${exception}`); return false; } finally { await SnapshotManager.instance.invalidateControl(); SnapshotManager.instance.updateMessage("
To apply the snapshot, please ComfyUI. And refresh browser.", 'cm-reboot-button2'); } } } async function remove_snapshot(target) { if(SnapshotManager.instance) { try { const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" }); if(response.status == 403) { await handle403Response(response); return false; } if(response.status == 400) { show_message(`Remove snapshot failed: ${target.title} / ${exception}`); } app.ui.dialog.close(); return true; } catch(exception) { show_message(`Restore snapshot failed: ${target.title} / ${exception}`); return false; } finally { await SnapshotManager.instance.invalidateControl(); } } } async function save_current_snapshot() { try { const response = await api.fetchApi('/snapshot/save', { cache: "no-store" }); app.ui.dialog.close(); return true; } catch(exception) { show_message(`Backup snapshot failed: ${exception}`); return false; } finally { await SnapshotManager.instance.invalidateControl(); SnapshotManager.instance.updateMessage("
Current snapshot saved."); } } async function getSnapshotList() { const response = await api.fetchApi(`/snapshot/getlist`); const data = await response.json(); return data; } export class SnapshotManager extends ComfyDialog { static instance = null; restore_buttons = []; message_box = null; data = null; content = $el("div.snapshot-manager"); clear() { this.restore_buttons = []; this.message_box = null; this.data = null; } constructor(app, manager_dialog) { super(); // this.manager_dialog = manager_dialog; this.search_keyword = ''; const frame = buildGuiFrame( 'snapshot-manager-dialog', // dialog id 'Snapshot Manager', // title 'i.mdi.mdi-puzzle', // icon class this.content, // dialog content element this ); // send this so we can attach close functions this.element = frame; } async remove_item() { caller.disableButtons(); await caller.invalidateControl(); } createControls() { return [ $el("button.p-button.p-component", { type: "button", textContent: "Close", onclick: () => { this.close(); } }) ]; } startRestore(target) { const self = SnapshotManager.instance; self.updateMessage(`
Restore snapshot '${target.name}'`); for(let i in self.restore_buttons) { self.restore_buttons[i].disabled = true; self.restore_buttons[i].style.backgroundColor = 'gray'; } } async invalidateControl() { this.clear(); this.data = (await getSnapshotList()).items; while (this.content.children.length) { this.content.removeChild(this.content.children[0]); } await this.createGrid(); await this.createBottomControls(); } updateMessage(msg, btn_id) { this.message_box.innerHTML = msg; if(btn_id) { const rebootButton = document.getElementById(btn_id); const self = this; rebootButton.onclick = async function() { if(await rebootAPI()) { self.close(); self.manager_dialog.close(); } }; } } async createGrid(models_json) { var grid = document.createElement('table'); grid.setAttribute('id', 'snapshot-list-grid'); var thead = document.createElement('thead'); var tbody = document.createElement('tbody'); var headerRow = document.createElement('tr'); thead.style.position = "sticky"; thead.style.top = "0px"; thead.style.borderCollapse = "collapse"; thead.style.tableLayout = "fixed"; var header1 = document.createElement('th'); header1.innerHTML = '  ID  '; header1.style.width = "20px"; var header2 = document.createElement('th'); header2.innerHTML = 'Datetime'; header2.style.width = "100%"; var header_button = document.createElement('th'); header_button.innerHTML = 'Action'; header_button.style.width = "100px"; thead.appendChild(headerRow); headerRow.appendChild(header1); headerRow.appendChild(header2); headerRow.appendChild(header_button); headerRow.style.backgroundColor = "Black"; headerRow.style.color = "White"; headerRow.style.textAlign = "center"; headerRow.style.width = "100%"; headerRow.style.padding = "0"; grid.appendChild(thead); grid.appendChild(tbody); this.grid_rows = {}; if(this.data) for (var i = 0; i < this.data.length; i++) { const data = this.data[i]; var dataRow = document.createElement('tr'); var data1 = document.createElement('td'); data1.style.textAlign = "center"; data1.innerHTML = i+1; var data2 = document.createElement('td'); data2.innerHTML = ` ${data}`; var data_button = document.createElement('td'); data_button.style.textAlign = "center"; data_button.className = "data-btns"; var restoreBtn = document.createElement('button'); restoreBtn.className = "snapshot-restore-btn p-button p-component"; restoreBtn.innerHTML = 'Restore'; restoreBtn.style.width = "100px"; restoreBtn.addEventListener('click', function() { restore_snapshot(data); }); var removeBtn = document.createElement('button'); removeBtn.className = "snapshot-remove-btn p-button p-component"; removeBtn.innerHTML = 'Remove'; removeBtn.style.width = "100px"; removeBtn.addEventListener('click', function() { remove_snapshot(data); }); data_button.appendChild(restoreBtn); data_button.appendChild(removeBtn); dataRow.style.backgroundColor = "var(--bg-color)"; dataRow.style.color = "var(--fg-color)"; dataRow.style.textAlign = "left"; dataRow.appendChild(data1); dataRow.appendChild(data2); dataRow.appendChild(data_button); tbody.appendChild(dataRow); this.grid_rows[i] = {data:data, control:dataRow}; } let self = this; const panel = document.createElement('div'); panel.style.width = "100%"; panel.style.height = "100%"; panel.appendChild(grid); function handleResize() { const parentHeight = self.element.clientHeight; const gridHeight = parentHeight - 200; // grid.style.height = gridHeight + "px"; } window.addEventListener("resize", handleResize); grid.style.position = "relative"; grid.style.display = "inline-block"; grid.style.width = "100%"; grid.style.height = "100%"; grid.style.overflowY = "scroll"; this.content.appendChild(panel); handleResize(); } async createBottomControls() { var save_button = document.createElement("button"); save_button.className = "p-button p-component"; save_button.innerHTML = "Save snapshot"; save_button.onclick = () => { save_current_snapshot(); } save_button.style.horizontalAlign = "right"; save_button.style.width = "170px"; this.message_box = $el('div', {id:'custom-download-message'}, [$el('br'), '']); this.message_box.style.height = '60px'; this.message_box.style.verticalAlign = 'middle'; const footer = $el("div.snapshot-footer"); const spacer = $el("div.cn-flex-auto"); footer.appendChild(spacer); footer.appendChild(save_button); this.content.appendChild(this.message_box); this.content.appendChild(footer); } async show() { try { this.invalidateControl(); this.element.style.display = "flex"; this.element.style.zIndex = 1099; } catch(exception) { app.ui.dialog.show(`Failed to get external model list. / ${exception}`); } } }