Files

94 lines
3.9 KiB
JavaScript

/**
* A model's file: the foot of its details (the full path, or the file name, with Copy and
* Delete), and the file manager (only on the computer ComfyUI runs on): showInFolder for the
* details' Folder button (Explorer / Finder with the file selected), openModelFolder for the
* folder column's 📂 (a type's folder or a subfolder).
*/
import { translate as t } from './locales.js';
import { anomalousAlert, anomalousConfirm } from './ui_dialog.js';
import { showWorkbenchToast } from './ui_prompt_toast.js';
/** `where`: { type, path_idx, subfolder } as the browser lists it; no `filename`: the folder itself. */
export async function openModelFolder(where, filename = '') {
try {
const response = await fetch('/anomalous/open_model_folder', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...where, filename }),
});
const data = await response.json().catch(() => ({}));
if (data.status !== 'success') throw new Error(data.message || `HTTP ${response.status}`);
} catch (error) {
await anomalousAlert(t('detailOpenFolderFailed', { error: error.message }));
}
}
export function showInFolder(model, where) {
return openModelFolder(where, model.filename);
}
export async function copyToClipboard(text, doneKey) {
try {
await navigator.clipboard.writeText(text);
showWorkbenchToast(t(doneKey));
} catch {
showWorkbenchToast(t('detailPathCopyFailed'));
}
}
/** Asks, then deletes the model's file and its side files (to the Recycle Bin) and leaves the details. */
async function deleteModel(owner, model, where) {
if (!await anomalousConfirm(`${t('delConfirm')} ${model.filename} ${t('delConfirm2')}`, t('delModel'), { okLabel: t('delModel') })) return;
try {
const response = await fetch('/anomalous/delete_model', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...where, filename: model.filename }),
});
const data = await response.json();
if (data.status !== 'success') throw new Error(data.message);
owner.currentDetailObserver?.disconnect();
owner.currentDetailObserver = null;
owner.detailPanel.style.display = 'none';
owner.stopMediaInContainer(owner.detailPanel);
owner.detailPanel.replaceChildren();
owner.grid.style.display = 'grid';
owner.loadModels();
await anomalousAlert(t('delSuccess') + data.deleted.join('\n') + t('delNote'));
} catch (error) {
await anomalousAlert(t('delFail') + error.message);
}
}
function footButton(className, label, onClick) {
const node = document.createElement('button');
node.type = 'button';
node.className = className;
node.textContent = label;
node.onclick = onClick;
return node;
}
/**
* The foot of the details, under everything else: the path (the only place the file name shows;
* the heading shows the model's name), Copy, and Delete. `where`: { type, path_idx, subfolder }.
*/
export function createModelFileFoot(owner, model, where) {
const foot = document.createElement('div');
foot.className = 'anomalous-model-file-foot';
const row = document.createElement('div');
row.className = 'anomalous-model-file';
const label = document.createElement('span');
label.className = 'anomalous-model-file-label';
label.textContent = t('detailFile');
const path = document.createElement('code');
path.textContent = model.file_path || model.filename;
path.title = path.textContent;
row.append(label, path,
footButton('anomalous-model-file-btn', t('detailCopy'), () => copyToClipboard(path.textContent, 'detailPathCopied')),
footButton('anomalous-model-file-btn is-danger', t('detailDeleteModel'), () => deleteModel(owner, model, where)));
foot.append(row);
return foot;
}