Implement two-line status card layout for manual model selection in downloads tab

- queue_methods.js: refactor updateSelectedBarForMissing to use a fixed two-line HTML structure with icon, label, code, and action buttons
- resolver-main.css: add .mr-selected-summary/.mr-selected-status-icon/.mr-selected-actions styling and adjust .mr-missing-detail-pane button/link treatments for the new layout
- test_downloads_tab_workflow_route.mjs: assert the new markup, class structure, and CSS selectors for the selected status card
This commit is contained in:
Dariusz L
2026-07-27 19:06:51 +02:00
parent aa2d08d995
commit c599a26f55
3 changed files with 126 additions and 29 deletions
@@ -91,6 +91,40 @@ test('Missing Models rows use fixed-size virtualization for smooth stable scroll
assert.match(rowRule[1], /contain\s*:\s*layout paint style/);
});
test('manual model selection uses the two-line status card layout', () => {
const updateSelectedBarForMissing = extractMethod(
queueMethodsSource,
'updateSelectedBarForMissing'
);
assert.match(updateSelectedBarForMissing, /mr-selected-summary/);
assert.match(updateSelectedBarForMissing, /mr-selected-status-icon/);
assert.match(updateSelectedBarForMissing, /getSvgIcon\('circleCheckBig'\)/);
assert.match(updateSelectedBarForMissing, /<span>Selected<\/span>/);
assert.doesNotMatch(updateSelectedBarForMissing, /Selected:/);
assert.match(updateSelectedBarForMissing, /mr-selected-apply/);
assert.match(updateSelectedBarForMissing, /mr-selected-remove/);
assert.match(
updateSelectedBarForMissing,
/selectedBar\.innerHTML\s*=\s*`[\s\S]*?<div class="mr-selected-bar-inner"[\s\S]*?<\/div>\s*`;/
);
assert.doesNotMatch(updateSelectedBarForMissing, /selectedBar\.innerHTML\s*\+=/);
assert.match(resolverMainCssSource, /\.mr-selected-summary\s*\{[^}]*flex-direction:\s*column/s);
assert.match(
resolverMainCssSource,
/\.mr-missing-detail-pane \.model-resolver-selected\s*\{[^}]*height:\s*74px/s
);
assert.match(
resolverMainCssSource,
/\.mr-missing-detail-pane \.mr-selected-bar-inner\s*\{[^}]*display:\s*grid[^}]*grid-template-columns:\s*minmax\(0,\s*1fr\)\s+auto/s
);
assert.doesNotMatch(
resolverMainCssSource,
/\.mr-missing-detail-pane \.mr-selected-bar-inner\s*\{[^}]*flex-direction:\s*column/s
);
assert.match(resolverMainCssSource, /\.mr-btn-danger\s*\{[^}]*rgba\(239,\s*68,\s*68/s);
});
test('local and loaded model tooltips include preview image routes above full names', () => {
const getModelPreviewTooltipAttrs = eval(`(${extractMethod(renderFormatMethodsSource, 'getModelPreviewTooltipAttrs')})`);
const previousApi = globalThis.api;
+77 -21
View File
@@ -9006,13 +9006,23 @@ a.mr-image-preview-resource-name:hover {
#model-resolver-modal .mr-missing-detail-pane .model-resolver-selected {
margin: 0 0 12px;
padding: 9px 11px;
background: linear-gradient(90deg, rgba(114, 211, 138, 0.11) 0%, rgba(114, 211, 138, 0.045) 100%);
border: 1px solid rgba(114, 211, 138, 0.20);
border-radius: 7px;
padding: 0;
width: 100%;
height: 74px;
min-height: 74px;
max-height: 74px;
box-sizing: border-box;
overflow: hidden;
background:
linear-gradient(90deg, rgba(45, 185, 112, 0.065) 0%, rgba(20, 24, 29, 0.96) 38%, rgba(17, 20, 25, 0.98) 100%);
border: 1px solid rgba(255, 255, 255, 0.085);
border-left: 3px solid #43d184;
border-radius: 6px;
color: var(--mr-text-soft);
font-size: 12px;
box-shadow: none;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.025),
0 8px 24px rgba(0, 0, 0, 0.10);
}
/* Flatten right-side sections */
@@ -9071,34 +9081,77 @@ a.mr-image-preview-resource-name:hover {
}
#model-resolver-modal .mr-missing-detail-pane .mr-selected-bar-inner {
gap: 8px;
flex-wrap: nowrap;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
width: 100%;
height: 100%;
min-height: 0;
box-sizing: border-box;
padding: 11px 13px 11px 15px;
gap: 16px;
}
#model-resolver-modal .mr-missing-detail-pane .mr-selected-summary {
display: flex;
flex-direction: column;
align-items: flex-start;
align-self: center;
min-width: 0;
gap: 4px;
}
#model-resolver-modal .mr-missing-detail-pane .mr-selected-label {
display: inline-flex;
align-items: center;
gap: 7px;
color: #72d38a;
font-size: 11px;
font-weight: 750;
font-size: 12px;
font-weight: 650;
line-height: 1;
white-space: nowrap;
}
#model-resolver-modal .mr-missing-detail-pane .mr-selected-status-icon {
display: inline-flex;
width: 17px;
height: 17px;
color: #43d184;
}
#model-resolver-modal .mr-missing-detail-pane .mr-selected-status-icon svg {
width: 100%;
height: 100%;
}
#model-resolver-modal .mr-missing-detail-pane .mr-selected-code {
display: block;
width: 100%;
min-width: 0;
color: #d9d9d9;
padding: 0;
overflow: hidden;
background: transparent;
color: #ededed;
font-family: "Segoe UI Variable", "Segoe UI", system-ui, sans-serif;
font-size: 11px;
font-size: 14px;
font-weight: 700;
line-height: 1.25;
text-overflow: ellipsis;
white-space: nowrap;
}
#model-resolver-modal .mr-missing-detail-pane .mr-selected-actions {
gap: 8px;
margin-left: auto;
display: inline-flex;
align-items: center;
align-self: center;
gap: 10px;
margin-left: 0;
}
#model-resolver-modal .mr-missing-detail-pane .model-resolver-selected .mr-btn {
min-height: var(--mr-missing-search-button-height, 34px);
height: var(--mr-missing-search-button-height, 34px);
padding: 0 12px;
min-height: 40px;
height: 40px;
padding: 0 17px;
margin-left: 0;
background: var(--mr-missing-control-bg, rgba(255, 255, 255, 0.035));
border-color: var(--mr-missing-control-border, rgba(255, 255, 255, 0.105));
@@ -9116,14 +9169,17 @@ a.mr-image-preview-resource-name:hover {
}
#model-resolver-modal .mr-missing-detail-pane .model-resolver-selected .mr-btn-primary {
background: rgba(47, 128, 237, 0.18);
border-color: rgba(47, 128, 237, 0.34);
color: #dbeafe;
background: linear-gradient(180deg, rgba(37, 99, 190, 0.88) 0%, rgba(25, 72, 144, 0.92) 100%);
border-color: rgba(88, 151, 255, 0.46);
color: #edf5ff;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.11),
0 4px 12px rgba(16, 72, 156, 0.18) !important;
}
#model-resolver-modal .mr-missing-detail-pane .model-resolver-selected .mr-btn-primary:hover:not(:disabled) {
background: rgba(47, 128, 237, 0.26);
border-color: rgba(47, 128, 237, 0.48);
background: linear-gradient(180deg, rgba(47, 117, 218, 0.96) 0%, rgba(30, 85, 166, 0.98) 100%);
border-color: rgba(111, 170, 255, 0.62);
color: #ffffff;
}
+15 -8
View File
@@ -1570,14 +1570,21 @@ export const queueMethods = {
const applyBtnId = `selected-apply-${domKey}`;
const removeBtnId = `selected-remove-${domKey}`;
selectedBar.innerHTML = `<div class="mr-selected-bar-inner"${selectedContextAttrs}>`;
selectedBar.innerHTML += `<span class="mr-selected-label">✓ Selected:</span>`;
selectedBar.innerHTML += `<code class="mr-selected-code">${this.escapeHtml(label)}</code>`;
selectedBar.innerHTML += `<span class="mr-selected-actions">`;
selectedBar.innerHTML += `<button id="${applyBtnId}" class="mr-btn mr-btn-primary mr-btn-sm" data-tooltip="Apply this selected local match to the workflow.">Apply</button>`;
selectedBar.innerHTML += `<button id="${removeBtnId}" class="mr-btn mr-btn-danger mr-btn-sm">Remove</button>`;
selectedBar.innerHTML += `</span>`;
selectedBar.innerHTML += `</div>`;
selectedBar.innerHTML = `
<div class="mr-selected-bar-inner"${selectedContextAttrs}>
<span class="mr-selected-summary">
<span class="mr-selected-label">
<span class="mr-selected-status-icon" aria-hidden="true">${getSvgIcon('circleCheckBig')}</span>
<span>Selected</span>
</span>
<code class="mr-selected-code" data-tooltip="${this.escapeHtml(label)}">${this.escapeHtml(label)}</code>
</span>
<span class="mr-selected-actions">
<button type="button" id="${applyBtnId}" class="mr-btn mr-btn-primary mr-btn-sm mr-selected-apply" data-tooltip="Apply this selected local match to the workflow.">Apply</button>
<button type="button" id="${removeBtnId}" class="mr-btn mr-btn-danger mr-btn-sm mr-selected-remove" data-tooltip="Remove this selection.">Remove</button>
</span>
</div>
`;
selectedBar.style.display = 'block';
const applyBtn = selectedBar.querySelector(`#${applyBtnId}`);