From a Bilibili comment (2026-10-13):
- Esc closes the top layer: a dialog or menu (the plugin's alert and confirm
now close on Esc, as OK / Cancel), else the Workflows / Combos workspace,
else the browser. One capture listener decides before the layers act, so a
dialog closing on the same Esc still counts as above. The shortcut and the
Extensions entry toggle the browser.
- A model's file card has Show in folder: POST /anomalous/open_model_folder
(api/model_folder.py) takes type, folder index, subfolder and file name,
never a path, and opens Explorer / Finder with the file selected; a POST,
so local_guard keeps it to this computer. The card moves out of
ui_detail.js into model_location.js.
- A gallery card's right-click menu (gallery_card_menu.js): Insert as a Load
Image node reads the output file in place ("name.png [output]"), and View
Parameters.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
2.7 KiB
JavaScript
71 lines
2.7 KiB
JavaScript
/**
|
|
* The model details' location card: the file name and full path, Copy path, and Show in
|
|
* folder (Explorer / Finder opens with the file selected; only on the computer ComfyUI runs on).
|
|
*/
|
|
|
|
import { translate as t } from './locales.js';
|
|
import { anomalousAlert } from './ui_dialog.js';
|
|
|
|
function actionButton(label) {
|
|
const button = document.createElement('button');
|
|
button.type = 'button';
|
|
button.className = 'anomalous-model-location-copy';
|
|
button.textContent = label;
|
|
return button;
|
|
}
|
|
|
|
async function showInFolder(model, where) {
|
|
try {
|
|
const response = await fetch('/anomalous/open_model_folder', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ ...where, filename: model.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 }));
|
|
}
|
|
}
|
|
|
|
/** `where`: the model's { type, path_idx, subfolder } in the browser. */
|
|
export function createModelLocationCard(model, where) {
|
|
const card = document.createElement('div');
|
|
card.className = 'anomalous-model-location';
|
|
|
|
const appendValue = (labelKey, value) => {
|
|
const row = document.createElement('div');
|
|
row.className = 'anomalous-model-location-row';
|
|
const label = document.createElement('span');
|
|
label.className = 'anomalous-model-location-label';
|
|
label.textContent = t(labelKey);
|
|
const content = document.createElement('code');
|
|
content.textContent = value;
|
|
content.title = value;
|
|
row.append(label, content);
|
|
card.appendChild(row);
|
|
};
|
|
|
|
appendValue('detailPhysicalFilename', model.filename);
|
|
if (model.file_path) {
|
|
appendValue('detailFullPath', model.file_path);
|
|
const actions = document.createElement('div');
|
|
actions.className = 'anomalous-model-location-actions';
|
|
const copyButton = actionButton(t('detailCopyPath'));
|
|
copyButton.onclick = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(model.file_path);
|
|
copyButton.textContent = t('detailPathCopied');
|
|
} catch (error) {
|
|
copyButton.textContent = t('detailPathCopyFailed');
|
|
}
|
|
setTimeout(() => { copyButton.textContent = t('detailCopyPath'); }, 1400);
|
|
};
|
|
const folderButton = actionButton(t('detailOpenFolder'));
|
|
folderButton.onclick = () => showInFolder(model, where);
|
|
actions.append(copyButton, folderButton);
|
|
card.appendChild(actions);
|
|
}
|
|
return card;
|
|
}
|