diff --git a/src/frontend/styles.css b/src/frontend/styles.css
index ca847ad..1603e83 100644
--- a/src/frontend/styles.css
+++ b/src/frontend/styles.css
@@ -695,7 +695,7 @@ body[data-view="overview"] .toolbar { display: none; }
box-shadow: 0 0 0 1px var(--accent-blue);
}
-.card.focused {
+.card.focused, .card:focus-visible {
outline: 2px solid var(--accent-blue);
outline-offset: 2px;
}
@@ -949,6 +949,7 @@ body[data-view="overview"] .toolbar { display: none; }
z-index: 50;
}
.calendar-popup.open { display: block; }
+.calendar-popup:focus { outline: none; } /* dialog container itself isn't the visible target — the first day/nav control is */
.cal-header {
display: flex;
@@ -987,8 +988,10 @@ body[data-view="overview"] .toolbar { display: none; }
cursor: pointer;
color: var(--text-secondary);
transition: all 0.1s;
+ background: none; border: none; font-family: inherit; width: 100%;
}
.cal-day:hover { background: rgba(255,255,255,0.08); color: var(--text-primary); }
+.cal-day:focus-visible { outline: 2px solid var(--accent-blue); outline-offset: -1px; }
.cal-day.other-month { color: var(--text-muted); opacity: 0.3; }
.cal-day.today { font-weight: 700; color: var(--accent-blue); }
.cal-day.in-range { background: rgba(96,165,250,0.1); }
@@ -2225,6 +2228,7 @@ body[data-view="overview"] .toolbar { display: none; }
.qa-item:last-child { border-bottom: none; }
.qa-item:hover { background: var(--bg-card-hover); }
.qa-item.selected { background: rgba(96, 165, 250, 0.08); }
+.qa-item:focus-visible { outline: 2px solid var(--accent-blue); outline-offset: -2px; }
.qa-question {
flex: 1;
@@ -3177,8 +3181,10 @@ body[data-view="overview"] .toolbar { display: none; }
display: flex; flex-direction: column; gap: 6px; text-align: left;
padding: 14px; background: var(--bg-card); border: 1px solid var(--border);
border-radius: 12px; cursor: pointer; transition: border-color 0.15s;
+ font: inherit; width: 100%;
}
.ov-card:hover { border-color: var(--accent-blue); }
+.ov-card:focus-visible { outline: 2px solid var(--accent-blue); outline-offset: 2px; }
.ov-card.limit { border-color: var(--accent-red); }
.ov-card-top { display: flex; align-items: center; gap: 8px; }
.ov-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-muted); }
@@ -3297,6 +3303,8 @@ body[data-view="overview"] .toolbar { display: none; }
.ws-resizer-h::before { left: 0; right: 0; top: 50%; height: 2px; transform: translateY(-50%); }
.ws-resizer:hover::before, .ws-resizer.dragging::before { background: var(--accent-blue); }
.ws-resizer.dragging::before { box-shadow: 0 0 8px color-mix(in srgb, var(--accent-blue) 60%, transparent); }
+.ws-resizer:focus-visible::before { background: var(--accent-blue); box-shadow: 0 0 8px color-mix(in srgb, var(--accent-blue) 60%, transparent); }
+.ws-resizer:focus-visible { outline: none; } /* the ::before line itself is the focus indicator */
.ws-pane {
display: flex;
@@ -4417,7 +4425,7 @@ body[data-view="overview"] .toolbar { display: none; }
border-color: var(--accent-blue);
}
-.list-row.focused {
+.list-row.focused, .list-row:focus-visible {
outline: 2px solid var(--accent-blue);
outline-offset: -2px;
}
diff --git a/src/frontend/workspace.js b/src/frontend/workspace.js
index 3c2b525..75c3948 100644
--- a/src/frontend/workspace.js
+++ b/src/frontend/workspace.js
@@ -750,10 +750,10 @@ function _wsPaneMarkup(pane) {
'ondblclick="renameWorkspacePane(\'' + escHtml(pane.id) + '\')">connecting…' +
'✎ ' +
- '' + _wsLaunchOptionsHtml() + ' ' +
'☆ ' +
- '× ' +
+ '× ' +
'
' +
'
' +
'
' +
@@ -1252,7 +1252,7 @@ function _wsTabMarkup(tab) {
'title="Drag to reorder · double-click to rename">' +
'
' + escHtml(tab.name) + ' ' +
'
✎ ' +
- '
× ' +
+ '
× ' +
'
';
}
@@ -1448,6 +1448,47 @@ function _wsMakeResizer(grid, tab, dir, pos, gr, idx) {
h.className = 'ws-resizer ws-resizer-' + dir;
if (dir === 'v') { h.style.left = pos + 'px'; }
else { h.style.top = pos + 'px'; }
+ // Pointer-only by default (like a native
splitter with
+ // no keyboard equivalent) — make it a real ARIA separator so arrow keys
+ // can resize too, not just drag.
+ h.tabIndex = 0;
+ h.setAttribute('role', 'separator');
+ h.setAttribute('aria-orientation', dir === 'v' ? 'vertical' : 'horizontal');
+ h.setAttribute('aria-label', dir === 'v' ? 'Resize panes (left/right arrow keys)' : 'Resize panes (up/down arrow keys)');
+ h.addEventListener('keydown', function (e) {
+ var forward = (dir === 'v') ? e.key === 'ArrowRight' : e.key === 'ArrowDown';
+ var backward = (dir === 'v') ? e.key === 'ArrowLeft' : e.key === 'ArrowUp';
+ if (!forward && !backward) return;
+ e.preventDefault();
+ var els = tab.panes.map(function (p) { return grid.querySelector('.ws-pane[data-pane-id="' + p.id + '"]'); });
+ var gr2 = grid.getBoundingClientRect();
+ var arr = (dir === 'v') ? tab.cols : tab.rows;
+ var a = idx, b = idx + 1;
+ var sum = arr[a] + arr[b];
+ var startPx, endPx;
+ if (dir === 'v') {
+ startPx = els[a].getBoundingClientRect().left;
+ endPx = els[b].getBoundingClientRect().right;
+ } else {
+ startPx = els[0].getBoundingClientRect().top;
+ endPx = els[2].getBoundingClientRect().bottom;
+ }
+ var S = endPx - startPx;
+ if (S < 2 * _WS_MIN_PANE_PX) return;
+ var curRel = (arr[a] / sum) * S;
+ var stepPx = Math.max(8, S * 0.05); // ~5% of the two tracks' span per keypress
+ var nextRel = Math.min(S - _WS_MIN_PANE_PX, Math.max(_WS_MIN_PANE_PX, curRel + (forward ? stepPx : -stepPx)));
+ arr[a] = sum * (nextRel / S);
+ arr[b] = sum - arr[a];
+ _wsApplyGrid(tab);
+ // Reposition this handle in place (like a live drag) rather than calling
+ // _wsLayoutResizers — that rebuilds every handle from scratch and would
+ // destroy (and unfocus) the one the user is actively adjusting.
+ if (dir === 'v') h.style.left = (startPx + nextRel - gr2.left) + 'px';
+ else h.style.top = (startPx + nextRel - gr2.top) + 'px';
+ _wsThrottleRefit(tab);
+ _wsSaveSession();
+ });
grid.appendChild(h);
h.addEventListener('pointerdown', function (e) {
e.preventDefault();
@@ -2294,7 +2335,7 @@ async function renderWorkspace(container) {
'