ui: editor-like QoL — selection highlight, drag feedback, keyboard nav

Selection / highlight:
- .vv-row-selected: blue tint + left accent bar (was barely-visible gray)
- .vv-sb-selected: same treatment, consistent across both panels
- .vv-df-active (containers): match new selection style
- .vv-job-row + .vv-sb-row: smooth transition on state changes
- .vv-orch-row: hover feedback + pointer cursor (was no visual response)

Drag and drop:
- .vv-drag-ghost: adds dashed ring so source item stays visible while dragging
- .vv-drop-line: blue glow to match selection accent (was flat green)
- All drop targets: unified blue theme (.vv-children, .vv-folder-children)
- .vv-library-zone drop target: improved red with inset glow
- .vv-drag-handle:active: grabbing cursor state
- document.body.vv-is-dragging: cursor:grabbing on entire page during drag
  (added to all three drag start handlers + failsafe document dragend)

Keyboard navigation (scheduler):
- ↑/↓: navigate between top-level orchestrator cards
- Home/End: jump to first/last card
- Escape: close right panel, return to suggestions
- /: focus log search when log panel is open
- All shortcuts suppressed when focus is in any input/textarea/select

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gmer4Lfe
2026-05-31 20:03:27 -04:00
co-authored by Claude Sonnet 4.6
parent a943a5c294
commit 753f99ac0c
2 changed files with 79 additions and 13 deletions
+48
View File
@@ -3051,6 +3051,7 @@ function _vvOrchScriptDragStart(e) {
vvDragFromArray = this.dataset.confArray || null;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', vvDragScript);
document.body.classList.add('vv-is-dragging');
setTimeout(() => this.classList.add('vv-drag-ghost'), 0);
}
@@ -3060,6 +3061,7 @@ function _vvCustomDragStart(e) {
vvDragFromArray = null;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', vvDragScript);
document.body.classList.add('vv-is-dragging');
setTimeout(() => this.classList.add('vv-drag-ghost'), 0);
}
@@ -3069,13 +3071,17 @@ function vvLibDragStart(e, el) {
vvDragFromArray = null;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', vvDragScript);
document.body.classList.add('vv-is-dragging');
setTimeout(() => el.classList.add('vv-drag-ghost'), 0);
}
function vvDragEnd(e) {
if (vvDragEl) vvDragEl.classList.remove('vv-drag-ghost');
document.body.classList.remove('vv-is-dragging');
vvDragEl = null;
}
// Failsafe: clear dragging class if drop lands outside the window
document.addEventListener('dragend', () => document.body.classList.remove('vv-is-dragging'));
// Return the .vv-script element that the cursor is above the midpoint of,
// or null if cursor is below all items (meaning: append).
@@ -3470,4 +3476,46 @@ requestAnimationFrame(function() {
vvStartStatusPoll();
vvStartSnapPoll();
});
// ── Keyboard navigation ───────────────────────────────────────────────────────
// ↑/↓ — navigate top-level orchestrator cards
// Home/End — jump to first/last card
// Escape — close right panel
// / — focus log search when visible
document.addEventListener('keydown', function(e) {
// Don't intercept when typing in any input/textarea/select
const t = document.activeElement;
if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' ||
t.tagName === 'SELECT' || t.isContentEditable)) return;
// Escape — back to suggestions
if (e.key === 'Escape' && vvActiveId) {
e.preventDefault();
vvBackToSuggestions();
return;
}
// / — focus log search when the log panel is open
if (e.key === '/' && document.getElementById('vv-log-search').style.display !== 'none') {
e.preventDefault();
document.getElementById('vv-log-search').focus();
return;
}
// ↑/↓/Home/End — navigate top-level orchestrator cards
if (!['ArrowUp','ArrowDown','Home','End'].includes(e.key)) return;
const cards = [...document.querySelectorAll('#vv-sched-cards > .vv-sched-card')];
if (!cards.length) return;
const cur = vvActiveId ? cards.findIndex(c => c.dataset.id === vvActiveId) : -1;
let next;
if (e.key === 'ArrowDown') next = cur < cards.length - 1 ? cur + 1 : cur;
else if (e.key === 'ArrowUp') next = cur > 0 ? cur - 1 : 0;
else if (e.key === 'Home') next = 0;
else next = cards.length - 1;
if (next === cur && cur !== -1) return;
if (next < 0) next = 0;
e.preventDefault();
cards[next].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
vvOpenRight(cards[next].dataset.id);
});
</script>