Improve queue panel resize UX with edge-drag collapse preview and collapse-to-edge toggle

This commit is contained in:
Dariusz L
2026-06-03 16:43:20 +02:00
parent 5f3262e266
commit 1cf5b448cb
3 changed files with 171 additions and 29 deletions
+9 -9
View File
@@ -4038,19 +4038,19 @@
max-width: var(--ml-missing-icon-button-size, 28px);
}
#model-linker-modal #queue-toggle-icon {
right: 8px;
min-width: 28px;
height: 32px;
padding: 0 7px;
right: 0;
min-width: 12px;
width: 12px;
height: 76px;
padding: 0;
color: #cfcfcf;
background: rgba(255,255,255,0.05);
border-color: rgba(255,255,255,0.12);
border-radius: 7px;
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
}
#model-linker-modal #queue-toggle-icon:hover {
background: rgba(255,255,255,0.075);
border-color: rgba(255,255,255,0.18);
background: transparent;
}
#model-linker-modal #model-linker-content,
#model-linker-modal .ml-missing-detail-pane,
+39 -8
View File
@@ -237,7 +237,8 @@
#model-linker-splitter:hover::before,
#model-linker-splitter:focus-visible::before,
#model-linker-body.ml-is-resizing-queue #model-linker-splitter::before {
#model-linker-body.ml-is-resizing-queue #model-linker-splitter::before,
#model-linker-body.ml-queue-preview-collapsed #model-linker-splitter::before {
background: rgba(114, 211, 138, 0.78);
box-shadow: 0 0 0 3px rgba(114, 211, 138, 0.12);
}
@@ -245,15 +246,45 @@
#queue-toggle-icon {
position: absolute;
top: 50%;
right: var(--ml-spacing-6);
right: 0;
z-index: 1000;
padding: var(--ml-spacing-2) var(--ml-spacing-6);
width: 12px;
min-width: 12px;
height: 76px;
padding: 0;
transform: translateY(-50%);
opacity: 0.9;
cursor: pointer;
background: var(--comfy-input-bg, #2f2f2f);
border: 1px solid var(--border-color);
border-radius: var(--ml-radius-md);
opacity: 0.72;
cursor: col-resize;
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
}
#queue-toggle-icon::before {
content: "";
position: absolute;
top: 8px;
bottom: 8px;
left: 50%;
width: 2px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.34);
transform: translateX(-50%);
transition: background 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
}
#queue-toggle-icon:hover,
#queue-toggle-icon:focus-visible {
opacity: 1;
outline: none;
}
#queue-toggle-icon:hover::before,
#queue-toggle-icon:focus-visible::before,
#queue-toggle-icon.is-collapsed::before {
background: rgba(114, 211, 138, 0.82);
box-shadow: 0 0 0 3px rgba(114, 211, 138, 0.12);
}
.ml-queue-panel,
+123 -12
View File
@@ -4085,8 +4085,11 @@ class LinkerManagerDialog extends ComfyDialog {
try {
this.queueToggleIcon = $el("button", {
id: "queue-toggle-icon",
onclick: () => this.toggleQueueCollapsed()
}, [document.createTextNode('⮜')]);
type: "button",
"aria-label": "Toggle queued selections",
onclick: (event) => this.onQueueEdgeClick(event),
onmousedown: (event) => this.startQueueEdgeDrag(event)
});
body.appendChild(this.queueToggleIcon);
this.updateQueueToggleIcon();
} catch (e) { }
@@ -4311,18 +4314,50 @@ class LinkerManagerDialog extends ComfyDialog {
updateQueueToggleIcon() {
if (!this.queueToggleIcon) return;
this.queueToggleIcon.style.display = this.activeTab === 'missing' ? '' : 'none';
this.queueToggleIcon.textContent = '';
this.queueToggleIcon.removeAttribute('data-tooltip');
this.queueToggleIcon.removeAttribute('title');
if (this.queueCollapsed) {
this.queueToggleIcon.textContent = '⮞';
this.setTooltip(this.queueToggleIcon, 'Show queued selections');
this.queueToggleIcon.classList.add('is-collapsed');
this.queueToggleIcon.setAttribute('aria-label', 'Show queued selections');
} else {
this.queueToggleIcon.textContent = '⮜';
this.setTooltip(this.queueToggleIcon, 'Hide queued selections');
this.queueToggleIcon.classList.remove('is-collapsed');
this.queueToggleIcon.setAttribute('aria-label', 'Hide queued selections');
}
}
onQueueEdgeClick(event) {
if (this._suppressQueueEdgeClick) {
event?.preventDefault?.();
event?.stopPropagation?.();
this._suppressQueueEdgeClick = false;
return;
}
this.toggleQueueCollapsed();
}
startQueueEdgeDrag(event) {
if (event.button !== 0 || !this.queueCollapsed || !this.queueElement) return;
const startX = event.clientX;
const previousCursor = document.body.style.cursor;
event.preventDefault();
document.body.style.cursor = 'col-resize';
this._suppressQueueEdgeClick = true;
this.splitterElement.style.display = '';
document.getElementById('model-linker-body')?.classList.add('ml-queue-preview-collapsed');
this.queueElement.style.display = 'none';
this.queueElement.style.width = '56px';
this.startSplitDrag(
{ clientX: startX, preventDefault: () => {} },
{ startWidth: 56, edgeOpen: true, previousCursor, startCollapsedPreview: true }
);
}
// Begin split drag for resizable panels
startSplitDrag(e) {
startSplitDrag(e, { startWidth = null, edgeOpen = false, previousCursor = null, startCollapsedPreview = false } = {}) {
try {
e?.preventDefault?.();
if (!this.queueElement) return;
const rect = this.queueElement.getBoundingClientRect();
const body = document.getElementById('model-linker-body');
@@ -4331,10 +4366,14 @@ class LinkerManagerDialog extends ComfyDialog {
body?.classList.add('ml-is-resizing-queue');
this._splitStart = {
x: e.clientX,
startWidth: rect.width,
startWidth: Number.isFinite(startWidth) ? startWidth : rect.width,
containerWidth: bodyRect.width
};
this._lastSplitDragApply = 0;
this._splitPreviewCollapsed = !!startCollapsedPreview;
this._splitEdgeOpen = !!edgeOpen;
this._splitEdgeOpenedPastThreshold = false;
this._splitPreviousCursor = previousCursor;
this._prevUserSelect = document.body.style.userSelect;
document.body.style.userSelect = 'none';
this._onSplitMove = (ev) => this.onSplitDrag(ev);
@@ -4348,18 +4387,36 @@ class LinkerManagerDialog extends ComfyDialog {
if (!this._splitDragging || !this._splitStart || !this.queueElement) return;
const dx = e.clientX - this._splitStart.x;
let newW = this._splitStart.startWidth - dx;
const minW = 240;
const minW = 56;
const maxW = Math.max(minW, Math.floor(this._splitStart.containerWidth - 360));
if (newW < minW) newW = minW;
if (newW > maxW) newW = maxW;
this._pendingSplitWidth = Math.round(newW);
if (this._splitEdgeOpen && this._pendingSplitWidth > 140) {
this._splitEdgeOpenedPastThreshold = true;
if (this.queueCollapsed) {
this.queueCollapsed = false;
try { localStorage.setItem('model_linker_queue_collapsed', '0'); } catch (error) { }
this.updateQueueToggleIcon();
this.updateQueuePanel();
}
}
const previewCollapsed = this._splitEdgeOpen
? (!this._splitEdgeOpenedPastThreshold || this._pendingSplitWidth <= 140)
: this._pendingSplitWidth <= 140;
if (previewCollapsed !== this._splitPreviewCollapsed) {
this._splitPreviewCollapsed = previewCollapsed;
const body = document.getElementById('model-linker-body');
body?.classList.toggle('ml-queue-preview-collapsed', previewCollapsed);
this.queueElement.style.display = previewCollapsed ? 'none' : '';
}
const now = performance.now();
if (this._lastSplitDragApply && now - this._lastSplitDragApply < 33) return;
this._lastSplitDragApply = now;
if (this._splitDragFrame) return;
this._splitDragFrame = requestAnimationFrame(() => {
this._splitDragFrame = null;
if (!this._splitDragging || !this.queueElement || !this._pendingSplitWidth) return;
if (!this._splitDragging || !this.queueElement || !this._pendingSplitWidth || this._splitPreviewCollapsed) return;
this.queueElement.style.width = `${this._pendingSplitWidth}px`;
});
}
@@ -4371,18 +4428,72 @@ class LinkerManagerDialog extends ComfyDialog {
cancelAnimationFrame(this._splitDragFrame);
this._splitDragFrame = null;
}
if (this.queueElement && this._pendingSplitWidth) {
if (this.queueElement && this._pendingSplitWidth && !this._splitPreviewCollapsed) {
this.queueElement.style.width = `${this._pendingSplitWidth}px`;
}
document.getElementById('model-linker-body')?.classList.remove('ml-is-resizing-queue');
const body = document.getElementById('model-linker-body');
body?.classList.remove('ml-is-resizing-queue');
body?.classList.remove('ml-queue-preview-collapsed');
document.removeEventListener('mousemove', this._onSplitMove);
if (this._splitEdgeOpen && !this._pendingSplitWidth) {
try {
const restoreWidth = Math.max(240, Math.round(this._splitStart?.startWidth || 320));
this.queueElement.style.width = `${restoreWidth}px`;
this.queueElement.style.display = '';
if (this.splitterElement) this.splitterElement.style.display = '';
localStorage.setItem('model_linker_split_w', String(restoreWidth));
} catch (e) { }
this.queueCollapsed = false;
try { localStorage.setItem('model_linker_queue_collapsed', '0'); } catch (e) { }
this.updateQueueVisibility();
this.updateQueuePanel();
this._splitEdgeOpen = false;
this._pendingSplitWidth = null;
this._lastSplitDragApply = 0;
this._splitPreviewCollapsed = false;
try { document.body.style.userSelect = this._prevUserSelect || ''; } catch (e) { }
try { document.body.style.cursor = this._splitPreviousCursor || ''; } catch (e) { }
this._splitPreviousCursor = null;
this._splitEdgeOpenedPastThreshold = false;
setTimeout(() => { this._suppressQueueEdgeClick = false; }, 0);
return;
}
const shouldCollapse = !!this._splitPreviewCollapsed;
if (shouldCollapse) {
try {
const restoreWidth = Math.max(240, Math.round(this._splitStart?.startWidth || 320));
this.queueElement.style.width = `${restoreWidth}px`;
this.queueElement.style.display = '';
localStorage.setItem('model_linker_split_w', String(restoreWidth));
} catch (e) { }
this._pendingSplitWidth = null;
this._lastSplitDragApply = 0;
this._splitPreviewCollapsed = false;
this._splitEdgeOpen = false;
this._splitEdgeOpenedPastThreshold = false;
try { document.body.style.userSelect = this._prevUserSelect || ''; } catch (e) { }
try { document.body.style.cursor = this._splitPreviousCursor || ''; } catch (e) { }
this._splitPreviousCursor = null;
this.setQueueCollapsed(true);
setTimeout(() => { this._suppressQueueEdgeClick = false; }, 0);
return;
}
if (this.queueElement) this.queueElement.style.display = '';
try {
const rect = this.queueElement.getBoundingClientRect();
localStorage.setItem('model_linker_split_w', String(Math.round(rect.width)));
} catch (e) { }
this._pendingSplitWidth = null;
this._lastSplitDragApply = 0;
this._splitPreviewCollapsed = false;
this._splitEdgeOpen = false;
this._splitEdgeOpenedPastThreshold = false;
try { document.body.style.userSelect = this._prevUserSelect || ''; } catch (e) { }
if (this._splitPreviousCursor !== null) {
try { document.body.style.cursor = this._splitPreviousCursor || ''; } catch (e) { }
this._splitPreviousCursor = null;
}
setTimeout(() => { this._suppressQueueEdgeClick = false; }, 0);
}
/**