Files
DemonGatanjieuandClaude Opus 5.5 983e9b1166 Model details, layout B: name and actions beside the cover
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>
2026-10-13 15:45:44 +08:00

71 lines
2.7 KiB
JavaScript

/**
* A small menu of actions, looking like the keep menu: at the pointer (a right-click) or under
* a button. A pick, a click elsewhere, the wheel or Esc closes it; its Esc goes no further, so
* the page behind stays open. One at a time.
*/
let closeOpenMenu = null;
function row({ icon, label, run, danger }, close) {
const item = document.createElement('button');
item.type = 'button';
item.className = `anomalous-keep-row${danger ? ' is-danger' : ''}`;
item.setAttribute('role', 'menuitem');
const mark = document.createElement('span');
mark.className = 'anomalous-keep-row-icon';
mark.textContent = icon;
const text = document.createElement('strong');
text.textContent = label;
item.append(mark, text);
item.onclick = (event) => {
close();
run(event);
};
return item;
}
/**
* `at`: a pointer event (the menu opens there) or a button (the menu opens under it,
* right-aligned; a click on that button is left to it, so it can close the menu again).
* `onClose()` runs however it closes. Returns the close function.
*/
export function openActionMenu(at, items, { className = '', onClose } = {}) {
closeOpenMenu?.();
const menu = document.createElement('div');
menu.className = `anomalous-keep-menu anomalous-action-menu ${className}`.trim();
menu.setAttribute('role', 'menu');
const onPointer = (pointer) => {
if (!menu.contains(pointer.target) && !(at instanceof Element && at.contains(pointer.target))) close();
};
const onKey = (key) => {
if (key.key !== 'Escape') return;
key.stopPropagation();
close();
};
const close = () => {
menu.remove();
window.removeEventListener('pointerdown', onPointer, true);
window.removeEventListener('keydown', onKey, true);
window.removeEventListener('wheel', close, true);
if (closeOpenMenu === close) closeOpenMenu = null;
onClose?.();
};
menu.append(...items.map(item => row(item, close)));
document.body.append(menu);
const { width, height } = menu.getBoundingClientRect();
let x = at.clientX;
let y = at.clientY;
if (at instanceof Element) {
const anchor = at.getBoundingClientRect();
x = anchor.right - width;
y = anchor.bottom + 6;
}
menu.style.left = `${Math.max(8, Math.min(x, window.innerWidth - width - 8))}px`;
menu.style.top = `${Math.max(8, Math.min(y, window.innerHeight - height - 8))}px`;
window.addEventListener('pointerdown', onPointer, true);
window.addEventListener('keydown', onKey, true);
window.addEventListener('wheel', close, true);
closeOpenMenu = close;
return close;
}