Files
DemonGatanjieu-Anomalous_Mo…/web/modules/model_detail_head.js
T
DemonGatanjieuandClaude Opus 5.5 1dadd5b0f0 Hand to AI: a model, an image or a combo starts a task that carries it
- ui_ai_handoff.js: "Hand to AI" on a model's page, the image inspector (✦) and combo cards.
  The user says what they want (prefilled), Anomalous starts a waiting task with `about` (the
  model_id, the output image, the combo's name; checked in ai_tasks.start_task) and shows the
  sentence to send the AI; begin_task hands `about` to the AI so it needs no search.
- The AI windows sit above the image inspector they can open from; going to the AI page from
  there closes the inspector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:05:35 +08:00

88 lines
4.2 KiB
JavaScript

/**
* The top of a model's details, beside its cover: back (a crumb, "Models / LoRA", or back to
* Model Check, the assistant or the model before; Esc presses it), the name, the size / base /
* source line with the source links, and one row of actions: Add node (the one blue button),
* Edit, Folder (its tooltip shows the path), Outputs and Hand to AI (ui_ai_handoff.js). The path, Copy and Delete are at the
* foot of the details (model_location.js). Where the page is too narrow for the cover column,
* a small cover sits beside the name instead.
*/
import { translate as t } from './locales.js';
import { renderDetailLinks, renderSourceLine } from './model_source.js';
import { showInFolder } from './model_location.js';
import { cardPosterUrl, cardPreviewUrl } from './ui_grid.js';
import { typeLabel } from './ui_model_types.js';
import { handModelToAi } from './ui_ai_handoff.js';
function el(tag, className, text) {
const node = document.createElement(tag);
if (className) node.className = className;
if (text !== undefined) node.textContent = text;
return node;
}
function button(className, label, onClick, title = '') {
const node = el('button', className, label);
node.type = 'button';
if (title) node.title = title;
node.onclick = onClick;
return node;
}
/** Where back leads, as the crumb says it; true second when it leads off the models page. */
function backLabel(owner) {
const last = owner.historyStack.at(-1);
if (last?.type === 'doctor') return [t('detailBackDoctor'), true];
if (last?.type === 'assistant') return [t('detailBackAssistant'), true];
if (owner.historyStack.length) return [t('detailBackPrevModel'), false];
if (typeof owner.recipeModelReturn === 'function') return [t('detailBack'), true];
const group = (owner.foldersData || []).find(item => item.type === owner.currentType && item.path_idx === owner.currentPathIdx);
return [t('detailBackModels', { type: group ? typeLabel(group) : owner.currentType }), false];
}
function smallCover(owner, model) {
if (!model.preview_url) return null;
const cover = el('img', 'anomalous-detail-head-cover');
cover.alt = '';
cover.src = /\.(mp4|webm)(?:&|$)/i.test(model.preview_url)
? cardPosterUrl(model.preview_url) : cardPreviewUrl(model.preview_url, owner.cardThumbnailMode);
return cover;
}
/** `onBack()` leads back (ui_detail.js owns where to). */
export function createDetailHead(owner, model, onBack) {
const meta = model.metadata || {};
const where = { type: owner.currentType, path_idx: owner.currentPathIdx, subfolder: owner.currentSubfolder };
const head = el('div', 'anomalous-detail-head');
const [crumbText, leavesPage] = backLabel(owner);
const crumb = button('anomalous-detail-back', crumbText, onBack, t('detailBackHint'));
crumb.classList.toggle('is-leaving', leavesPage);
const named = el('div', 'anomalous-detail-named');
const cover = smallCover(owner, model);
if (cover) named.append(cover);
const title = el('div', 'anomalous-detail-title');
const name = el('h2', 'anomalous-detail-name', meta.custom_name || meta.name || model.filename);
name.title = model.filename;
const facts = el('div', 'anomalous-detail-facts');
const line = el('span', 'anomalous-detail-source');
renderSourceLine(line, model, meta);
facts.append(line, renderDetailLinks(meta));
title.append(name, facts);
named.append(title);
const actions = el('div', 'anomalous-detail-actions');
actions.append(
button('anomalous-detail-action is-primary', t('applyToCanvas'), () => owner.applyModelToCanvas(owner.currentType, owner.currentSubfolder, model)),
button('anomalous-detail-action', t('detailEdit'), () => owner.showEditModal(model)),
button('anomalous-detail-action', t('detailFolder'), () => showInFolder(model, where),
`${t('detailOpenFolder')}\n${model.file_path || model.filename}`),
button('anomalous-detail-action', t('detailOutputs'), () => owner.showGeneratedGallery(model), t('detailGeneratedGallery')),
button('anomalous-detail-action', `✦ ${t('aiHand')}`, () => handModelToAi(owner, model, where), t('aiHandHint')),
);
head.append(crumb, named, actions);
return head;
}