feat(info-dialog): improve model info dialog layout and UX
- Add Escape key handler to close the info dialog, with proper cleanup - Extract reusable renderInfoFieldLabel() helper to reduce code duplication - Add dedicated SVG icons (file, fileText, hash, box, hardDrive, bookType) for field labels - Adjust dialog sizing to calc(100vh - 16px) with 92vw max width - Refactor CSS into .mr-info-field-label / .mr-info-label-icon / .mr-info-label-text - Improve trained-words row layout with flex styling
This commit is contained in:
+26
-26
@@ -3238,6 +3238,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.mr-info-dialog {
|
||||
background: var(--comfy-menu-bg, #202020);
|
||||
@@ -3245,7 +3247,7 @@
|
||||
border-radius: 8px;
|
||||
width: 640px;
|
||||
max-width: 92vw;
|
||||
max-height: 80vh;
|
||||
max-height: calc(100vh - 16px);
|
||||
min-width: min(520px, 92vw);
|
||||
min-height: 320px;
|
||||
overflow: hidden;
|
||||
@@ -3524,8 +3526,7 @@
|
||||
font-weight: 650;
|
||||
word-break: break-word;
|
||||
}
|
||||
.mr-info-table td:first-child > span,
|
||||
.mr-info-trained-words-label {
|
||||
.mr-info-field-label {
|
||||
display: grid;
|
||||
grid-template-columns: 14px minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
@@ -3536,22 +3537,19 @@
|
||||
line-height: 1.25;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.mr-info-table td:first-child > span {
|
||||
overflow: visible;
|
||||
}
|
||||
.mr-info-table td:first-child .mr-tooltip-badge,
|
||||
.mr-info-trained-words-label .mr-tooltip-badge {
|
||||
justify-self: end;
|
||||
}
|
||||
.mr-info-table td:first-child > span::before,
|
||||
.mr-info-trained-words-label::before {
|
||||
content: "";
|
||||
.mr-info-label-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1px solid rgba(255,255,255,0.22);
|
||||
border-radius: 4px;
|
||||
background: rgba(255,255,255,0.035);
|
||||
box-shadow: 0 1px 0 rgba(255,255,255,0.05) inset;
|
||||
color: var(--mr-text-muted, #888);
|
||||
opacity: 0.82;
|
||||
}
|
||||
.mr-info-label-text {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
}
|
||||
.mr-info-field-label .mr-tooltip-badge {
|
||||
justify-self: end;
|
||||
}
|
||||
.mr-info-location {
|
||||
font-family: "Cascadia Mono", "Consolas", monospace;
|
||||
@@ -3594,11 +3592,20 @@
|
||||
opacity: 0.78;
|
||||
}
|
||||
.mr-info-trained-words-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
.mr-info-trained-words-meta {
|
||||
grid-column: 1 / -1;
|
||||
justify-self: start;
|
||||
align-self: flex-start;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 11px;
|
||||
color: var(--mr-text-muted, #888);
|
||||
}
|
||||
.mr-info-trainedwords-row td {
|
||||
align-items: flex-start;
|
||||
@@ -3612,13 +3619,6 @@
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.mr-info-trained-words-meta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 11px;
|
||||
color: var(--mr-text-muted, #888);
|
||||
}
|
||||
.mr-info-trained-words-hint {
|
||||
margin-bottom: 8px;
|
||||
font-size: 11px;
|
||||
|
||||
@@ -253,6 +253,13 @@ export const modelInfoMethods = {
|
||||
document.body.appendChild(dialog);
|
||||
this.bindTooltips(dialog);
|
||||
this.bindInfoDialogResizePersistence(dialog);
|
||||
dialog._onInfoDialogKeyDown = (event) => {
|
||||
if (event.key !== 'Escape') return;
|
||||
if (document.querySelector('.mr-image-preview-backdrop')) return;
|
||||
event.preventDefault();
|
||||
this.closeInfoDialog(dialog);
|
||||
};
|
||||
document.addEventListener('keydown', dialog._onInfoDialogKeyDown);
|
||||
|
||||
// Add close handlers
|
||||
const closeBtn = dialog.querySelector('.mr-info-dialog-close');
|
||||
@@ -281,6 +288,16 @@ export const modelInfoMethods = {
|
||||
/**
|
||||
* Create the info dialog element
|
||||
*/
|
||||
renderInfoFieldLabel(iconName, label, tooltip) {
|
||||
return `
|
||||
<span class="mr-info-field-label">
|
||||
${getSvgIcon(iconName, 'currentColor', 'mr-info-label-icon')}
|
||||
<span class="mr-info-label-text">${this.escapeHtml(label)}</span>
|
||||
<span class="mr-tooltip-badge" data-tooltip="${this.escapeHtml(tooltip)}">?</span>
|
||||
</span>
|
||||
`;
|
||||
},
|
||||
|
||||
createInfoDialog(loraName, modelData) {
|
||||
const loraDisplayName = loraName.replace(/\.(safetensors|ckpt|pt|pth|bin|pkl|sft|onnx|gguf)$/i, '');
|
||||
|
||||
@@ -310,29 +327,29 @@ export const modelInfoMethods = {
|
||||
<table class="mr-info-table">
|
||||
<tbody>
|
||||
<tr class="mr-info-file-row">
|
||||
<td><span>File <span class="mr-tooltip-badge" data-tooltip="The model file name found locally or returned by CivitAI.">?</span></span></td>
|
||||
<td>${this.renderInfoFieldLabel('file', 'File', 'The model file name found locally or returned by CivitAI.')}</td>
|
||||
<td><span class="mr-info-file"></span></td>
|
||||
</tr>
|
||||
<tr class="mr-info-basemodel-row">
|
||||
<td><span>Base Model <span class="mr-tooltip-badge" data-tooltip="Base model this resource was made for, for example SD1.5, SDXL or Flux.">?</span></span></td>
|
||||
<td>${this.renderInfoFieldLabel('box', 'Base Model', 'Base model this resource was made for, for example SD1.5, SDXL or Flux.')}</td>
|
||||
<td><span class="mr-info-base-model"></span></td>
|
||||
</tr>
|
||||
<tr class="mr-info-hash-row">
|
||||
<td><span>Hash (sha256) <span class="mr-tooltip-badge" data-tooltip="Unique fingerprint of the local file. Model Resolver uses it to confirm the exact CivitAI version.">?</span></span></td>
|
||||
<td>${this.renderInfoFieldLabel('hash', 'Hash (sha256)', 'Unique fingerprint of the local file. Model Resolver uses it to confirm the exact CivitAI version.')}</td>
|
||||
<td><span class="mr-info-hash"></span></td>
|
||||
</tr>
|
||||
<tr class="mr-info-size-row mr-hidden-initial">
|
||||
<td><span>Size <span class="mr-tooltip-badge" data-tooltip="The local model file size, read from metadata or from the file on disk.">?</span></span></td>
|
||||
<td>${this.renderInfoFieldLabel('hardDrive', 'Size', 'The local model file size, read from metadata or from the file on disk.')}</td>
|
||||
<td><span class="mr-info-size"></span></td>
|
||||
</tr>
|
||||
<tr class="mr-info-location-row mr-info-row-wide mr-hidden-initial">
|
||||
<td><span>Location <span class="mr-tooltip-badge" data-tooltip="Folder where this local model file is stored.">?</span></span></td>
|
||||
<td>${this.renderInfoFieldLabel('locate', 'Location', 'Folder where this local model file is stored.')}</td>
|
||||
<td><span class="mr-info-location"></span></td>
|
||||
</tr>
|
||||
<tr class="mr-info-trainedwords-row mr-hidden-initial">
|
||||
<td>
|
||||
<div class="mr-info-trained-words-label">
|
||||
Trained Words <span class="mr-tooltip-badge" data-tooltip="Trigger words recommended by the model author. Click the words you want, then copy them into your prompt.">?</span>
|
||||
${this.renderInfoFieldLabel('bookType', 'Trained Words', 'Trigger words recommended by the model author. Click the words you want, then copy them into your prompt.')}
|
||||
<small class="mr-info-trained-words-meta">
|
||||
<span class="mr-info-trained-words-count">0 selected</span>
|
||||
<button type="button" class="mr-info-copy-trained-words" disabled>Copy</button>
|
||||
@@ -345,11 +362,11 @@ export const modelInfoMethods = {
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="mr-info-clipskip-row mr-hidden-initial">
|
||||
<td><span>Clip Skip <span class="mr-tooltip-badge" data-tooltip="Recommended Clip Skip value from the model author, if one is provided.">?</span></span></td>
|
||||
<td>${this.renderInfoFieldLabel('wrench', 'Clip Skip', 'Recommended Clip Skip value from the model author, if one is provided.')}</td>
|
||||
<td><span class="mr-info-clip-skip"></span></td>
|
||||
</tr>
|
||||
<tr class="mr-info-description-row mr-hidden-initial">
|
||||
<td><span>Description <span class="mr-tooltip-badge" data-tooltip="Model description from CivitAI or local metadata. Long descriptions are shortened until you click Show more.">?</span></span></td>
|
||||
<td>${this.renderInfoFieldLabel('fileText', 'Description', 'Model description from CivitAI or local metadata. Long descriptions are shortened until you click Show more.')}</td>
|
||||
<td>
|
||||
<div class="mr-info-description-wrap">
|
||||
<div class="mr-info-description"></div>
|
||||
@@ -775,8 +792,8 @@ export const modelInfoMethods = {
|
||||
const height = Number(saved.h);
|
||||
if (!Number.isFinite(width) || !Number.isFinite(height)) return;
|
||||
|
||||
const maxWidth = Math.floor(window.innerWidth * 0.9);
|
||||
const maxHeight = Math.floor(window.innerHeight * 0.8);
|
||||
const maxWidth = Math.floor(window.innerWidth * 0.92);
|
||||
const maxHeight = Math.max(320, window.innerHeight - 16);
|
||||
const clampedWidth = Math.max(420, Math.min(width, maxWidth));
|
||||
const clampedHeight = Math.max(320, Math.min(height, maxHeight));
|
||||
|
||||
@@ -2057,6 +2074,10 @@ export const modelInfoMethods = {
|
||||
clearTimeout(dialog._infoDialogResizeSaveTimer);
|
||||
dialog._infoDialogResizeSaveTimer = null;
|
||||
}
|
||||
if (dialog?._onInfoDialogKeyDown) {
|
||||
document.removeEventListener('keydown', dialog._onInfoDialogKeyDown);
|
||||
dialog._onInfoDialogKeyDown = null;
|
||||
}
|
||||
this.saveInfoDialogSize(dialog);
|
||||
this.closeInfoImagePreview();
|
||||
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
const SVG_ICONS = {
|
||||
file: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"></path><path d="M14 2v5a1 1 0 0 0 1 1h5"></path></svg>`,
|
||||
fileText: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"></path><path d="M14 2v5a1 1 0 0 0 1 1h5"></path><path d="M10 9H8"></path><path d="M16 13H8"></path><path d="M16 17H8"></path></svg>`,
|
||||
hash: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="4" x2="20" y1="9" y2="9"></line><line x1="4" x2="20" y1="15" y2="15"></line><line x1="10" x2="8" y1="3" y2="21"></line><line x1="16" x2="14" y1="3" y2="21"></line></svg>`,
|
||||
box: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"></path><path d="m3.3 7 8.7 5 8.7-5"></path><path d="M12 22V12"></path></svg>`,
|
||||
hardDrive: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 16h.01"></path><path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path><path d="M21.946 12.013H2.054"></path><path d="M6 16h.01"></path></svg>`,
|
||||
bookType: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13h4"></path><path d="M12 6v7"></path><path d="M16 8V6H8v2"></path><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"></path></svg>`,
|
||||
search: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="6.5"></circle><path d="M16 16l5 5"></path></svg>`,
|
||||
refreshCw: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-2.64-6.36"></path><path d="M21 3v6h-6"></path></svg>`,
|
||||
locate: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12h3"></path><path d="M19 12h3"></path><path d="M12 2v3"></path><path d="M12 19v3"></path><circle cx="12" cy="12" r="7"></circle><circle cx="12" cy="12" r="2.5"></circle></svg>`,
|
||||
|
||||
Reference in New Issue
Block a user