From a Bilibili comment (buttons small and in five places, the file name twice, a title bar of little use), layout B chosen by the user from three mockups: - The title bar goes. The head (new model_detail_head.js) tops the right column: a crumb back (Models / LoRA, or where history leads; still .anomalous-detail-back, so Esc presses it), the name, the source line with the source links, and one action row: Add node (the one accent button), Edit, Folder, Outputs, and ... with Copy path, Copy file name, Delete. - Delete asks in AMB's dialog instead of window.confirm, and is no longer beside Add node. - model_location.js: one path line with Copy; Show in folder is exported for Folder. - action_menu.js: the small menu the gallery card's right-click had, shared with the ... - Narrow (no cover column), a 64 px cover sits beside the name. - Gone: the jump-to-bottom button, the full-width Outputs button, the location card's file name row, and their locale keys (back, backToPrev, detailJumpToBottom, detailPhysicalFilename, detailFullPath). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
3.2 KiB
JavaScript
69 lines
3.2 KiB
JavaScript
/**
|
|
* A gallery card's right-click menu: put the picture on the canvas as a Load Image node, or
|
|
* open its parameters. The picture is uploaded into the input folder the way a dropped file is
|
|
* (ComfyUI keeps one copy of the same picture): a "name.png [output]" value would be flagged
|
|
* as a missing file by ComfyUI's checks for any picture not made since ComfyUI started.
|
|
*/
|
|
|
|
import { app } from '../../../scripts/app.js';
|
|
import { translate as t } from './locales.js';
|
|
import { recordCanvasStep } from './canvas_history.js';
|
|
import { showWorkbenchToast } from './ui_prompt_toast.js';
|
|
import { canvasPoint, followPointer } from './notebook_canvas.js';
|
|
import { openActionMenu } from './action_menu.js';
|
|
|
|
/** The output picture uploaded into the input folder; its name there for a Load Image node. */
|
|
async function copyToInput(image) {
|
|
const query = new URLSearchParams({ filename: image.filename, subfolder: image.subfolder || '', type: 'output' });
|
|
const picture = await fetch(`/view?${query}`);
|
|
if (!picture.ok) throw new Error(`view ${picture.status}`);
|
|
const blob = await picture.blob();
|
|
const form = new FormData();
|
|
form.append('image', new File([blob], image.filename, { type: blob.type }));
|
|
form.append('type', 'input');
|
|
const response = await fetch('/upload/image', { method: 'POST', body: form });
|
|
if (!response.ok) throw new Error(`upload ${response.status}`);
|
|
const data = await response.json();
|
|
return data.subfolder ? `${data.subfolder}/${data.name}` : data.name;
|
|
}
|
|
|
|
/**
|
|
* A Load Image node showing `image`: the browser closes and the node follows the pointer until a
|
|
* click puts it down (Esc takes it off again), like a combo's Put on canvas. `at` is the click
|
|
* that chose it, where the node starts.
|
|
*/
|
|
export async function insertLoadImageNode(owner, image, at) {
|
|
const node = globalThis.LiteGraph?.createNode('LoadImage');
|
|
const widget = node?.widgets?.find(item => item.name === 'image');
|
|
let file = null;
|
|
try {
|
|
if (node && widget && app.graph) file = await copyToInput(image);
|
|
} catch (error) {
|
|
console.error('[AMB] Could not copy the picture into the input folder:', error);
|
|
}
|
|
if (!file) {
|
|
showWorkbenchToast(t('galleryInsertNodeFailed'));
|
|
return;
|
|
}
|
|
const [x, y] = canvasPoint(at);
|
|
node.pos = [x - node.size[0] / 2, y - 20];
|
|
app.graph.add(node);
|
|
const choices = widget.options?.values;
|
|
if (Array.isArray(choices) && !choices.includes(file)) choices.push(file);
|
|
widget.value = file;
|
|
widget.callback?.(file); // shows the picture in the node
|
|
app.graph.setDirtyCanvas(true, true);
|
|
owner.close();
|
|
followPointer(owner, [{ node, relX: 0, relY: 0 }], () => recordCanvasStep(app));
|
|
showWorkbenchToast(t('galleryInsertNodeFollow'));
|
|
}
|
|
|
|
/** Opens the menu at the pointer; `openDetails()` opens the picture's parameters. */
|
|
export function openGalleryCardMenu(event, image, { owner, openDetails }) {
|
|
event.preventDefault();
|
|
openActionMenu(event, [
|
|
{ icon: '🖼️', label: t('galleryInsertAsNode'), run: (click) => insertLoadImageNode(owner, image, click) },
|
|
{ icon: '🔍', label: t('materialViewParameters'), run: openDetails },
|
|
]);
|
|
}
|