Make metadata history filterable and full-length, and stabilize build progress UI
This commit is contained in:
+18
@@ -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",
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user