fix(split-pane): style and dismiss the split-picker menu

.split-picker-menu/-item/-empty (created in openSplitPicker()) had zero CSS
and could not be dismissed except by picking an item — a default-path defect
since the Split button ships enabled to anyone who flips showSplitButton on.
Add CSS matching the sibling .run-mode-menu popover's look (floating-bg
backdrop blur, border, shadow, z-index 1000 above the header's 100), and
dismiss on outside click or Escape via the same one-shot listener pattern
session-ui.js already uses for its other transient popovers
(toggleCaseSettings(), toggleRunModeMenu()). Picking an item now routes
through the same _dismissSplitPicker() method as the outside-click/Escape
handlers, so the listeners never outlive the menu.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
timkjr
2026-09-20 13:10:27 -05:00
co-authored by Claude Sonnet 5
parent 57406f6c14
commit 2724c922ce
2 changed files with 66 additions and 1 deletions
+40
View File
@@ -18635,3 +18635,43 @@ html[data-session-list="sidebar"][data-sidebar="collapsed"] .btn-sidebar-toggle
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-height: 0;
} }
/* Split-picker menu: a small popover listing sessions to split with, appended
to document.body and positioned `fixed` by openSplitPicker() (JS sets
top/right against the Split header button's own rect). z-index above the
header (100) with headroom to spare, matching the sibling .run-mode-menu
dropdown's 1000. Dismissed by openSplitPane()'s own inline onclick, or by
the click-outside/Escape listeners installed alongside it. */
.split-picker-menu {
position: fixed;
z-index: 1000;
min-width: 200px;
max-width: 320px;
padding: 4px;
background: var(--floating-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--control-border);
border-radius: 10px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
}
.split-picker-item {
padding: 8px 10px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.split-picker-item:hover {
background: var(--control-bg-hover, rgba(255, 255, 255, 0.08));
}
.split-picker-empty {
padding: 8px 10px;
font-size: 13px;
color: var(--text-muted);
}
+26 -1
View File
@@ -146,7 +146,7 @@ Object.assign(CodemanApp.prototype, {
menu.innerHTML = candidates menu.innerHTML = candidates
.map( .map(
(c) => (c) =>
`<div class="split-picker-item" data-session-id="${escapeHtml(c.id)}" onclick="app.openSplitPane(${escapeHtml(JSON.stringify(c.id))}); document.getElementById('splitPickerMenu')?.remove();">${escapeHtml(c.label)}</div>` `<div class="split-picker-item" data-session-id="${escapeHtml(c.id)}" onclick="app.openSplitPane(${escapeHtml(JSON.stringify(c.id))}); app._dismissSplitPicker();">${escapeHtml(c.label)}</div>`
) )
.join(''); .join('');
} }
@@ -158,6 +158,31 @@ Object.assign(CodemanApp.prototype, {
menu.style.top = `${rect.bottom + 4}px`; menu.style.top = `${rect.bottom + 4}px`;
menu.style.right = `${window.innerWidth - rect.right}px`; menu.style.right = `${window.innerWidth - rect.right}px`;
} }
// Dismiss on outside click or Escape — same one-shot listener pattern as
// session-ui.js's other transient popovers (toggleCaseSettings(),
// toggleRunModeMenu()). Deferred by a tick so the click that OPENED the
// menu (still bubbling) doesn't immediately close it. Picking an item
// (above) calls the SAME dismiss method, so these listeners never
// outlive the menu either way.
const closeOnOutsideClick = (e) => {
if (!menu.contains(e.target) && e.target !== splitBtn) this._dismissSplitPicker();
};
const closeOnEscape = (e) => {
if (e.key === 'Escape') this._dismissSplitPicker();
};
this._splitPickerDismissHandlers = { closeOnOutsideClick, closeOnEscape };
setTimeout(() => document.addEventListener('click', closeOnOutsideClick), 0);
document.addEventListener('keydown', closeOnEscape);
},
_dismissSplitPicker() {
document.getElementById('splitPickerMenu')?.remove();
if (this._splitPickerDismissHandlers) {
document.removeEventListener('click', this._splitPickerDismissHandlers.closeOnOutsideClick);
document.removeEventListener('keydown', this._splitPickerDismissHandlers.closeOnEscape);
this._splitPickerDismissHandlers = null;
}
}, },
openSplitPane(sessionId) { openSplitPane(sessionId) {