Make metadata history filterable and full-length, and stabilize build progress UI

This commit is contained in:
Dariusz L
2026-07-05 22:25:01 +02:00
parent b9f364e3e4
commit 39943d5e17
4 changed files with 227 additions and 22 deletions
+18
View File
@@ -1220,6 +1220,12 @@ class ModelResolverExtension:
stage="cancelled",
message="Metadata build cancelled",
percent=100,
active_models=[],
active_worker_count=0,
current_model="",
current_path="",
bytes_read=0,
total_bytes=0,
result=result,
**result,
)
@@ -1231,6 +1237,12 @@ class ModelResolverExtension:
stage="done",
message="Local metadata build completed.",
percent=100,
active_models=[],
active_worker_count=0,
current_model="",
current_path="",
bytes_read=0,
total_bytes=0,
result=result,
**result,
)
@@ -1242,6 +1254,12 @@ class ModelResolverExtension:
stage="error",
message=str(exc) or "Metadata build failed",
percent=100,
active_models=[],
active_worker_count=0,
current_model="",
current_path="",
bytes_read=0,
total_bytes=0,
error=str(exc) or "Metadata build failed",
)
+16 -4
View File
@@ -470,7 +470,7 @@ def _build_result_payload(
"errors": errors[:50],
"updated": updated[:200],
"updated_count": len(updated),
"history": history[:500],
"history": history,
"history_count": len(history),
}
@@ -696,6 +696,12 @@ def _build_missing_local_metadata_parallel(
"current": completed,
"total": total,
"percent": 100,
"active_models": [],
"active_worker_count": 0,
"current_model": "",
"current_path": "",
"bytes_read": 0,
"total_bytes": 0,
**result,
},
)
@@ -792,7 +798,7 @@ def build_missing_local_metadata(
"error_count": len(errors),
"errors": errors[:50],
"updated": updated[:200],
"history": history[:500],
"history": history,
"history_count": len(history),
}
@@ -1056,7 +1062,7 @@ def build_missing_local_metadata(
"error_count": len(errors),
"errors": errors[:50],
"updated": updated[:200],
"history": history[:500],
"history": history,
"history_count": len(history),
}
except Exception as exc:
@@ -1110,7 +1116,7 @@ def build_missing_local_metadata(
"errors": errors[:50],
"updated": updated[:200],
"updated_count": len(updated),
"history": history[:500],
"history": history,
"history_count": len(history),
}
_emit(
@@ -1122,6 +1128,12 @@ def build_missing_local_metadata(
"current": total,
"total": total,
"percent": 100,
"active_models": [],
"active_worker_count": 0,
"current_model": "",
"current_path": "",
"bytes_read": 0,
"total_bytes": 0,
**result,
},
)
+94 -7
View File
@@ -6348,6 +6348,11 @@ a.mr-image-preview-resource-name:hover {
grid-template-rows: auto auto auto auto auto minmax(0, 1fr);
}
.mr-options-build-stack {
--mr-options-build-surface: #121212;
--mr-tab-active-bg: var(--mr-options-build-surface);
}
.mr-options-panel-title {
margin: 0 0 6px 0;
font-size: 16px;
@@ -6467,8 +6472,9 @@ a.mr-image-preview-resource-name:hover {
height: 36px;
min-height: 36px;
margin-top: 12px;
margin-left: 14px;
overflow: visible;
--mr-tab-active-bg: rgba(20, 20, 24, 0.96);
--mr-tab-active-bg: var(--mr-options-build-surface);
}
.mr-options-build-tabs .mr-options-build-tab {
@@ -6510,21 +6516,51 @@ a.mr-image-preview-resource-name:hover {
.mr-options-build-tab-panel {
min-width: 0;
min-height: 0;
height: 100%;
}
.mr-options-build-tab-panel.is-hidden {
display: none;
}
#mr-options-metadata-build-progress-panel,
#mr-options-metadata-build-history-panel {
min-height: 0;
padding: 10px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 0 6px 6px 6px;
background: var(--mr-tab-active-bg, var(--mr-options-build-surface));
}
#mr-options-metadata-build-progress-panel {
display: grid;
grid-template-rows: minmax(0, 1fr);
}
#mr-options-metadata-build-history-panel {
display: flex;
flex-direction: column;
}
#mr-options-metadata-build-progress-panel.is-hidden,
#mr-options-metadata-build-history-panel.is-hidden {
display: none;
}
.mr-options-build-progress {
display: grid;
gap: 8px;
margin-top: 10px;
grid-template-rows: auto auto auto minmax(0, 1fr);
height: 100%;
margin-top: 0;
min-width: 0;
padding: 10px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 6px;
background: rgba(0, 0, 0, 0.14);
min-height: 0;
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
}
.mr-options-build-progress[hidden] {
@@ -6584,7 +6620,9 @@ a.mr-image-preview-resource-name:hover {
.mr-options-build-worker-list {
display: grid;
gap: 8px;
max-height: 260px;
align-content: start;
height: 100%;
max-height: none;
min-height: 0;
overflow: auto;
padding-right: 2px;
@@ -6656,6 +6694,36 @@ a.mr-image-preview-resource-name:hover {
transition: width 0.2s ease;
}
.mr-options-history-toolbar {
display: flex;
align-items: center;
flex: 0 0 auto;
gap: 8px;
min-height: 32px;
margin-bottom: 10px;
}
.mr-options-history-filter-label {
color: var(--mr-text-secondary);
font-size: 12px;
font-weight: 700;
}
#model-resolver-modal .mr-options-history-filter-select {
width: auto;
min-width: 160px;
min-height: 32px;
padding: 6px 10px;
border-radius: 6px;
font-size: 12px;
}
.mr-options-history-filter-info {
margin-left: auto;
color: var(--mr-text-muted);
font-size: 12px;
}
.mr-options-history-action {
display: inline-flex;
align-items: center;
@@ -6693,6 +6761,25 @@ a.mr-image-preview-resource-name:hover {
background: rgba(255, 255, 255, 0.06);
}
.mr-options-build-tab-panel .mr-options-audit-results:not([hidden]) {
display: flex;
flex: 1 1 auto;
flex-direction: column;
height: 100%;
min-height: 0;
margin-top: 0;
}
.mr-options-build-tab-panel .mr-options-audit-results[hidden] {
display: none;
}
.mr-options-build-tab-panel .mr-options-audit-table-wrap,
.mr-options-build-tab-panel .mr-options-audit-empty {
border-top-left-radius: 0;
background: transparent;
}
.mr-options-audit-results {
margin-top: 12px;
min-width: 0;
+99 -11
View File
@@ -860,6 +860,20 @@ export const optionsMethods = {
</div>
</div>
<div id="mr-options-metadata-build-history-panel" class="mr-options-build-tab-panel is-hidden">
<div class="mr-options-history-toolbar">
<label class="mr-options-history-filter-label" for="mr-options-metadata-build-history-filter">Show</label>
<select id="mr-options-metadata-build-history-filter" class="mr-options-input mr-options-history-filter-select">
<option value="all">All</option>
<option value="done">Done</option>
<option value="created">Created</option>
<option value="updated">Updated</option>
<option value="skipped">Skipped</option>
<option value="checked">Checked</option>
<option value="error">Errors</option>
<option value="cancelled">Cancelled</option>
</select>
<span id="mr-options-metadata-build-history-filter-info" class="mr-options-history-filter-info">0 shown</span>
</div>
<div id="mr-options-metadata-build-results" class="mr-options-audit-results" hidden></div>
</div>
</div>
@@ -970,6 +984,8 @@ export const optionsMethods = {
const metadataBuildProgressPanel = this.contentElement.querySelector('#mr-options-metadata-build-progress-panel');
const metadataBuildHistoryPanel = this.contentElement.querySelector('#mr-options-metadata-build-history-panel');
const metadataBuildHistoryCountEl = this.contentElement.querySelector('#mr-options-metadata-build-history-count');
const metadataBuildHistoryFilterInput = this.contentElement.querySelector('#mr-options-metadata-build-history-filter');
const metadataBuildHistoryFilterInfo = this.contentElement.querySelector('#mr-options-metadata-build-history-filter-info');
const metadataBuildCpuEl = this.contentElement.querySelector('#mr-options-metadata-build-cpu');
const metadataBuildWorkersEl = this.contentElement.querySelector('#mr-options-metadata-build-workers');
const metadataBuildWorkerInput = this.contentElement.querySelector('#mr-options-metadata-build-worker-count');
@@ -983,6 +999,13 @@ export const optionsMethods = {
const optionsMain = this.contentElement.querySelector('.mr-options-main');
const navButtons = Array.from(this.contentElement.querySelectorAll('.mr-options-nav-btn'));
const optionSections = Array.from(this.contentElement.querySelectorAll('.mr-options-section'));
const metadataBuildHistoryFilterValues = ['all', 'done', 'created', 'updated', 'skipped', 'checked', 'error', 'cancelled'];
if (!metadataBuildHistoryFilterValues.includes(this.metadataBuildHistoryFilter)) {
this.metadataBuildHistoryFilter = 'all';
}
if (metadataBuildHistoryFilterInput) {
metadataBuildHistoryFilterInput.value = this.metadataBuildHistoryFilter;
}
if (this.metadataBuildPollTimer) {
window.clearTimeout(this.metadataBuildPollTimer);
this.metadataBuildPollTimer = null;
@@ -1356,10 +1379,6 @@ export const optionsMethods = {
this.metadataBuildHistoryKeys.add(key);
this.metadataBuildHistory.unshift(item);
});
if (this.metadataBuildHistory.length > 1000) {
this.metadataBuildHistory = this.metadataBuildHistory.slice(0, 1000);
this.metadataBuildHistoryKeys = new Set(this.metadataBuildHistory.map(getMetadataBuildHistoryKey));
}
if (metadataBuildHistoryCountEl) {
metadataBuildHistoryCountEl.textContent = this.metadataBuildHistory.length.toLocaleString();
}
@@ -1467,6 +1486,12 @@ export const optionsMethods = {
const renderMetadataBuildWorkers = (data = {}, activeModels = []) => {
if (!metadataBuildWorkerListEl) return;
const statusValue = String(data.status || data.stage || '').toLowerCase();
if (['done', 'cancelled', 'error'].includes(statusValue)) {
metadataBuildWorkerListEl.innerHTML = '';
metadataBuildWorkerListEl.hidden = true;
return;
}
const workerItems = activeModels.length
? activeModels
: (data.current_model ? [{
@@ -1525,7 +1550,8 @@ export const optionsMethods = {
const current = Number(data.current || 0);
const total = Number(data.total || data.total_models || 0);
const activeModels = Array.isArray(data.active_models) ? data.active_models : [];
const terminalStatus = ['done', 'cancelled', 'error'].includes(String(data.status || stage || '').toLowerCase());
const activeModels = !terminalStatus && Array.isArray(data.active_models) ? data.active_models : [];
const currentModel = data.current_model || data.filename || '';
const suffix = total > 0 ? ` (${current.toLocaleString()} / ${total.toLocaleString()})` : '';
if (metadataBuildCurrentModelEl) {
@@ -1557,13 +1583,64 @@ export const optionsMethods = {
renderMetadataBuildWorkers(data, activeModels);
};
const renderMetadataBuildResults = (data = null) => {
const metadataBuildHistoryFilterLabels = {
all: 'All',
done: 'Done',
created: 'Created',
updated: 'Updated',
skipped: 'Skipped',
checked: 'Checked',
error: 'Errors',
cancelled: 'Cancelled'
};
const metadataBuildDoneActions = new Set(['created', 'updated', 'skipped', 'checked']);
const getMetadataBuildHistoryAction = (item = {}) => String(item.action || 'checked').toLowerCase();
const getMetadataBuildHistoryFilter = () => {
const value = metadataBuildHistoryFilterInput?.value || this.metadataBuildHistoryFilter || 'all';
return metadataBuildHistoryFilterValues.includes(value) ? value : 'all';
};
const metadataBuildHistoryMatchesFilter = (item, filter) => {
const action = getMetadataBuildHistoryAction(item);
if (filter === 'all') return true;
if (filter === 'done') return metadataBuildDoneActions.has(action);
return action === filter;
};
const getMetadataBuildHistoryFilterCounts = (items = []) => {
const counts = Object.fromEntries(metadataBuildHistoryFilterValues.map(value => [value, 0]));
items.forEach((item) => {
const action = getMetadataBuildHistoryAction(item);
counts.all += 1;
if (metadataBuildDoneActions.has(action)) counts.done += 1;
if (Object.prototype.hasOwnProperty.call(counts, action)) counts[action] += 1;
});
return counts;
};
const updateMetadataBuildHistoryFilterUi = (allItems = [], filteredItems = [], filter = 'all') => {
const counts = getMetadataBuildHistoryFilterCounts(allItems);
if (metadataBuildHistoryFilterInput) {
Array.from(metadataBuildHistoryFilterInput.options).forEach((option) => {
const label = metadataBuildHistoryFilterLabels[option.value] || option.value;
option.textContent = `${label} (${Number(counts[option.value] || 0).toLocaleString()})`;
});
metadataBuildHistoryFilterInput.value = filter;
}
if (metadataBuildHistoryFilterInfo) {
metadataBuildHistoryFilterInfo.textContent = `${filteredItems.length.toLocaleString()} shown of ${allItems.length.toLocaleString()}`;
}
};
const renderMetadataBuildResults = (data = undefined) => {
if (!metadataBuildResults) return;
const source = data?.result && typeof data.result === 'object' ? data.result : data;
if (data !== undefined) {
this.metadataBuildLastResultsData = data;
}
const renderData = data === undefined ? this.metadataBuildLastResultsData : data;
const source = renderData?.result && typeof renderData.result === 'object' ? renderData.result : renderData;
const historyItems = Array.isArray(this.metadataBuildHistory)
? this.metadataBuildHistory
: [];
if (!source && !historyItems.length) {
updateMetadataBuildHistoryFilterUi([], [], getMetadataBuildHistoryFilter());
metadataBuildResults.hidden = true;
metadataBuildResults.innerHTML = '';
return;
@@ -1574,7 +1651,11 @@ export const optionsMethods = {
: (source && Array.isArray(source.updated) ? source.updated : []);
const displayItems = historyItems.length ? historyItems : fallbackItems;
const errors = Array.isArray(source?.errors) ? source.errors : [];
const visibleItems = displayItems.slice(0, 500);
const activeHistoryFilter = getMetadataBuildHistoryFilter();
this.metadataBuildHistoryFilter = activeHistoryFilter;
const filteredItems = displayItems.filter(item => metadataBuildHistoryMatchesFilter(item, activeHistoryFilter));
updateMetadataBuildHistoryFilterUi(displayItems, filteredItems, activeHistoryFilter);
const visibleItems = filteredItems;
let html = '';
if (visibleItems.length) {
@@ -1654,9 +1735,9 @@ export const optionsMethods = {
</table>
</div>
`;
if (displayItems.length > visibleItems.length) {
html += `<div class="mr-options-db-message">Showing latest ${visibleItems.length.toLocaleString()} of ${displayItems.length.toLocaleString()} history items.</div>`;
}
} else if (displayItems.length) {
const label = metadataBuildHistoryFilterLabels[activeHistoryFilter] || activeHistoryFilter;
html += `<div class="mr-options-audit-empty">No history items match the ${this.escapeHtml(label)} filter.</div>`;
} else if (source?.cancelled) {
html += '<div class="mr-options-audit-empty">Metadata build was cancelled before any history item was captured.</div>';
} else {
@@ -2896,6 +2977,13 @@ export const optionsMethods = {
});
});
if (metadataBuildHistoryFilterInput) {
metadataBuildHistoryFilterInput.addEventListener('change', () => {
this.metadataBuildHistoryFilter = getMetadataBuildHistoryFilter();
renderMetadataBuildResults();
});
}
if (metadataBuildWorkerInput) {
metadataBuildWorkerInput.addEventListener('change', () => {
metadataBuildWorkerInput.dataset.userEdited = 'true';