Add draggable splitter to adjust width of missing model detail pane in linker UI
This commit is contained in:
+35
-2
@@ -273,7 +273,7 @@
|
||||
--ml-missing-line-strong: rgba(255,255,255,0.13);
|
||||
--ml-missing-radius: 6px;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(360px, 1.1fr) minmax(320px, 0.9fr);
|
||||
grid-template-columns: minmax(360px, 1fr) 10px var(--ml-missing-detail-track, minmax(320px, 0.9fr));
|
||||
gap: 0;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
@@ -291,9 +291,39 @@
|
||||
.ml-missing-list-pane {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto minmax(0, 1fr);
|
||||
border-right: 1px solid var(--ml-missing-line);
|
||||
border-right: 0;
|
||||
background: var(--ml-missing-bg);
|
||||
}
|
||||
.ml-missing-browser-splitter {
|
||||
position: relative;
|
||||
min-width: 10px;
|
||||
width: 10px;
|
||||
cursor: col-resize;
|
||||
user-select: none;
|
||||
touch-action: none;
|
||||
background: var(--ml-missing-bg);
|
||||
border-left: 1px solid var(--ml-missing-line);
|
||||
border-right: 1px solid var(--ml-missing-line);
|
||||
outline: none;
|
||||
}
|
||||
.ml-missing-browser-splitter::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
bottom: 16px;
|
||||
left: 50%;
|
||||
width: 2px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255,255,255,0.13);
|
||||
transform: translateX(-50%);
|
||||
transition: background 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
.ml-missing-browser-splitter:hover::before,
|
||||
.ml-missing-browser-splitter:focus-visible::before,
|
||||
.ml-missing-browser.is-resizing .ml-missing-browser-splitter::before {
|
||||
background: rgba(114,211,138,0.78);
|
||||
box-shadow: 0 0 0 3px rgba(114,211,138,0.12);
|
||||
}
|
||||
.ml-missing-list-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -651,6 +681,9 @@
|
||||
grid-template-columns: 1fr;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ml-missing-browser-splitter {
|
||||
display: none;
|
||||
}
|
||||
.ml-missing-list-pane {
|
||||
max-height: 46vh;
|
||||
border-right: 0;
|
||||
|
||||
+136
-1
@@ -39,6 +39,7 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
this.pendingDockToSidebar = false;
|
||||
this.sidebarTabId = "comfyui-model-linker";
|
||||
this.sidebarOpenModeStorageKey = "model_linker_sidebar_open_mode";
|
||||
this.missingBrowserSplitStorageKey = "model_linker_missing_browser_detail_w";
|
||||
this.dockButton = null;
|
||||
this.undockButton = null;
|
||||
this._floatingRectBeforeDock = null;
|
||||
@@ -4837,7 +4838,14 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
? `${activeCount} downloading`
|
||||
: (hasAny100Match ? 'Auto-link ready for exact matches' : 'Review matches or search online');
|
||||
const listLayout = this.getMissingModelsListLayout(missingModels);
|
||||
const listStyle = `--ml-missing-model-col:${listLayout.modelPx}px;--ml-missing-type-col:${listLayout.typePx}px;`;
|
||||
let savedDetailWidth = null;
|
||||
try {
|
||||
savedDetailWidth = parseInt(localStorage.getItem(this.missingBrowserSplitStorageKey) || '', 10);
|
||||
} catch (e) {}
|
||||
const splitStyle = Number.isFinite(savedDetailWidth) && savedDetailWidth > 0
|
||||
? `--ml-missing-detail-track:${savedDetailWidth}px;`
|
||||
: '';
|
||||
const listStyle = `${splitStyle}--ml-missing-model-col:${listLayout.modelPx}px;--ml-missing-type-col:${listLayout.typePx}px;`;
|
||||
|
||||
let html = `
|
||||
<div class="ml-missing-browser" style="${listStyle}">
|
||||
@@ -4906,6 +4914,7 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
html += `
|
||||
</div>
|
||||
</section>
|
||||
<div class="ml-missing-browser-splitter" role="separator" aria-orientation="vertical" aria-label="Resize missing model panes" tabindex="0" data-tooltip="Drag to resize panes"></div>
|
||||
<section class="ml-missing-detail-pane" aria-label="Missing model details">
|
||||
${detailMissing ? this.renderMissingModel(detailMissing, detailIndex) : this.renderStatusMessage('Select a missing model to inspect details.', 'info')}
|
||||
</section>
|
||||
@@ -4915,6 +4924,8 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
wireMissingModelsBrowser(container, data, sortedMissingModels) {
|
||||
this.wireMissingBrowserSplitter(container);
|
||||
|
||||
const selectRow = (row) => {
|
||||
const key = row.dataset.missingKey;
|
||||
if (!key || key === this.selectedMissingModelKey) return;
|
||||
@@ -4953,6 +4964,130 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
this.wireMissingModelDetail(container, selectedMissing, selectedIndex);
|
||||
}
|
||||
|
||||
wireMissingBrowserSplitter(container) {
|
||||
const browser = container.querySelector('.ml-missing-browser');
|
||||
const splitter = browser?.querySelector('.ml-missing-browser-splitter');
|
||||
if (!(browser instanceof HTMLElement) || !(splitter instanceof HTMLElement)) return;
|
||||
|
||||
this.restoreMissingBrowserSplitWidth(browser);
|
||||
|
||||
splitter.addEventListener('mousedown', (event) => {
|
||||
if (event.button !== 0) return;
|
||||
this.startMissingBrowserSplitDrag(event, browser);
|
||||
});
|
||||
|
||||
splitter.addEventListener('dblclick', () => {
|
||||
browser.style.removeProperty('--ml-missing-detail-track');
|
||||
try {
|
||||
localStorage.removeItem(this.missingBrowserSplitStorageKey);
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
splitter.addEventListener('keydown', (event) => {
|
||||
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
|
||||
event.preventDefault();
|
||||
this.resizeMissingBrowserDetailBy(browser, event.key === 'ArrowLeft' ? 32 : -32);
|
||||
});
|
||||
|
||||
this._missingBrowserResizeObserver?.disconnect?.();
|
||||
if (typeof ResizeObserver === 'function') {
|
||||
this._missingBrowserResizeObserver = new ResizeObserver(() => {
|
||||
if (!this._missingBrowserSplitDragging) {
|
||||
this.restoreMissingBrowserSplitWidth(browser);
|
||||
}
|
||||
});
|
||||
this._missingBrowserResizeObserver.observe(browser);
|
||||
}
|
||||
}
|
||||
|
||||
restoreMissingBrowserSplitWidth(browser) {
|
||||
try {
|
||||
const savedWidth = parseInt(localStorage.getItem(this.missingBrowserSplitStorageKey) || '', 10);
|
||||
if (Number.isFinite(savedWidth) && savedWidth > 0) {
|
||||
this.setMissingBrowserDetailWidth(browser, savedWidth, { persist: false });
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
startMissingBrowserSplitDrag(event, browser) {
|
||||
if (!(browser instanceof HTMLElement)) return;
|
||||
|
||||
const detailPane = browser.querySelector('.ml-missing-detail-pane');
|
||||
if (!(detailPane instanceof HTMLElement)) return;
|
||||
|
||||
event.preventDefault();
|
||||
this._missingBrowserSplitDragging = true;
|
||||
this._missingBrowserSplitStart = {
|
||||
x: event.clientX,
|
||||
width: detailPane.getBoundingClientRect().width
|
||||
};
|
||||
this._missingBrowserSplitBrowser = browser;
|
||||
this._missingBrowserPrevUserSelect = document.body.style.userSelect;
|
||||
this._missingBrowserPrevCursor = document.body.style.cursor;
|
||||
document.body.style.userSelect = 'none';
|
||||
document.body.style.cursor = 'col-resize';
|
||||
browser.classList.add('is-resizing');
|
||||
|
||||
this._onMissingBrowserSplitMove = (moveEvent) => this.onMissingBrowserSplitDrag(moveEvent);
|
||||
this._onMissingBrowserSplitUp = () => this.endMissingBrowserSplitDrag();
|
||||
document.addEventListener('mousemove', this._onMissingBrowserSplitMove);
|
||||
document.addEventListener('mouseup', this._onMissingBrowserSplitUp, { once: true });
|
||||
}
|
||||
|
||||
onMissingBrowserSplitDrag(event) {
|
||||
if (!this._missingBrowserSplitDragging || !this._missingBrowserSplitStart || !this._missingBrowserSplitBrowser) return;
|
||||
|
||||
const nextWidth = this._missingBrowserSplitStart.width - (event.clientX - this._missingBrowserSplitStart.x);
|
||||
this.setMissingBrowserDetailWidth(this._missingBrowserSplitBrowser, nextWidth);
|
||||
}
|
||||
|
||||
endMissingBrowserSplitDrag() {
|
||||
if (!this._missingBrowserSplitDragging) return;
|
||||
|
||||
this._missingBrowserSplitDragging = false;
|
||||
document.removeEventListener('mousemove', this._onMissingBrowserSplitMove);
|
||||
this._missingBrowserSplitBrowser?.classList.remove('is-resizing');
|
||||
this._missingBrowserSplitBrowser = null;
|
||||
this._missingBrowserSplitStart = null;
|
||||
try {
|
||||
document.body.style.userSelect = this._missingBrowserPrevUserSelect || '';
|
||||
document.body.style.cursor = this._missingBrowserPrevCursor || '';
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
resizeMissingBrowserDetailBy(browser, delta) {
|
||||
const detailPane = browser.querySelector('.ml-missing-detail-pane');
|
||||
if (!(detailPane instanceof HTMLElement)) return;
|
||||
|
||||
this.setMissingBrowserDetailWidth(browser, detailPane.getBoundingClientRect().width + delta);
|
||||
}
|
||||
|
||||
setMissingBrowserDetailWidth(browser, width, { persist = true } = {}) {
|
||||
const bounds = this.getMissingBrowserSplitBounds(browser);
|
||||
const nextWidth = Math.round(Math.max(bounds.min, Math.min(bounds.max, width)));
|
||||
browser.style.setProperty('--ml-missing-detail-track', `${nextWidth}px`);
|
||||
|
||||
if (persist) {
|
||||
try {
|
||||
localStorage.setItem(this.missingBrowserSplitStorageKey, String(nextWidth));
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
getMissingBrowserSplitBounds(browser) {
|
||||
const browserRect = browser.getBoundingClientRect();
|
||||
const splitter = browser.querySelector('.ml-missing-browser-splitter');
|
||||
const splitterWidth = splitter instanceof HTMLElement
|
||||
? splitter.getBoundingClientRect().width
|
||||
: 10;
|
||||
const available = Math.max(1, browserRect.width - splitterWidth);
|
||||
const minListWidth = Math.min(360, Math.max(240, Math.floor(available * 0.28)));
|
||||
const max = Math.max(1, available - minListWidth);
|
||||
const min = Math.min(max, Math.min(420, Math.max(300, Math.floor(available * 0.3))));
|
||||
|
||||
return { min, max };
|
||||
}
|
||||
|
||||
wireMissingModelDetail(container, missing, missingIndex) {
|
||||
this.wireLocalMatchButtons(container, missing, missingIndex);
|
||||
this.wireDownloadSearchPanel(container, missing);
|
||||
|
||||
Reference in New Issue
Block a user