mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
fix: add split pane button to tab bar for discoverability (#135)
* fix: add split pane button to tab bar for discoverability The split pane feature was only accessible via right-click context menu or Ctrl+\ keyboard shortcut. Add a subtle split icon (⧉) to the tab bar that appears at low opacity in single-pane mode. Hidden in multi-pane mode where pane headers already provide split/close controls. * fix: address review — change tab-bar from tablist to toolbar role The tab bar contains both tabs and action buttons (new workstream, split pane), which is invalid for role=tablist. Change to role=toolbar which correctly describes a container of mixed interactive controls. * fix: address design review — WCAG contrast, ARIA structure, mobile - Drop opacity approach, use border: dashed var(--border) matching #new-tab-btn pattern (fixes WCAG contrast failure at 35% opacity) - Nest tabs in #tab-list[role=tablist] inside toolbar (fixes invalid role=tab children inside role=toolbar) - Hide split button on mobile (<600px) where splits can't work - Add aria-keyshortcuts to both action buttons
This commit is contained in:
@@ -1112,6 +1112,19 @@ function updatePaneHeaders() {
|
||||
} else {
|
||||
root.classList.remove("multi-pane");
|
||||
}
|
||||
// Hide tab-bar split button when already in multi-pane mode
|
||||
var splitBtn = document.getElementById("split-btn");
|
||||
if (splitBtn) {
|
||||
if (leafCount > 1) {
|
||||
splitBtn.classList.add("hidden");
|
||||
} else {
|
||||
splitBtn.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function splitFocusedPane() {
|
||||
if (focusedPaneId) splitPane(focusedPaneId, "horizontal");
|
||||
}
|
||||
|
||||
// --- Tree helpers ---
|
||||
@@ -1860,10 +1873,11 @@ document
|
||||
// ===========================================================================
|
||||
|
||||
var tabBar = document.getElementById("tab-bar");
|
||||
var tabList = document.getElementById("tab-list");
|
||||
var newTabBtn = document.getElementById("new-tab-btn");
|
||||
|
||||
function renderTabBar() {
|
||||
tabBar.querySelectorAll(".ws-tab").forEach(function (t) {
|
||||
tabList.querySelectorAll(".ws-tab").forEach(function (t) {
|
||||
t.remove();
|
||||
});
|
||||
|
||||
@@ -1910,7 +1924,7 @@ function renderTabBar() {
|
||||
tab.appendChild(close);
|
||||
}
|
||||
|
||||
tabBar.insertBefore(tab, newTabBtn);
|
||||
tabList.appendChild(tab);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,10 @@
|
||||
<button id="logout-btn" class="header-btn" onclick="logout()" style="display:none">logout</button>
|
||||
</div>
|
||||
|
||||
<div id="tab-bar" role="tablist">
|
||||
<button id="new-tab-btn" onclick="newWorkstream()" title="New workstream (Ctrl+T)" aria-label="New workstream">+</button>
|
||||
<div id="tab-bar" role="toolbar" aria-label="Workstreams">
|
||||
<div id="tab-list" role="tablist"></div>
|
||||
<button id="new-tab-btn" onclick="newWorkstream()" title="New workstream (Ctrl+T)" aria-label="New workstream" aria-keyshortcuts="Control+t">+</button>
|
||||
<button id="split-btn" onclick="splitFocusedPane()" title="Split pane (Ctrl+\)" aria-label="Split pane" aria-keyshortcuts="Control+Backslash">⧉</button>
|
||||
</div>
|
||||
|
||||
<div id="dashboard" class="dashboard-overlay" role="dialog" aria-modal="true" aria-label="Dashboard">
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
#hamburger-btn { width: 40px; height: 40px; }
|
||||
.hmenu-item { padding: 12px 14px; }
|
||||
.ws-tab .tab-close { opacity: 1; padding: 4px 6px; font-size: 16px; }
|
||||
#split-btn { display: none; }
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
@@ -127,6 +128,11 @@
|
||||
flex-shrink: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
#tab-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
#tab-bar::-webkit-scrollbar { height: 3px; }
|
||||
#tab-bar::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 2px; }
|
||||
|
||||
@@ -190,6 +196,22 @@
|
||||
}
|
||||
#new-tab-btn:hover { background: var(--bg-highlight); color: var(--accent); border-color: var(--accent); }
|
||||
|
||||
#split-btn {
|
||||
background: none;
|
||||
border: 1px dashed var(--border);
|
||||
color: var(--fg-dim);
|
||||
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
|
||||
padding: 6px 8px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
line-height: 1;
|
||||
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
||||
margin-left: 2px;
|
||||
}
|
||||
#split-btn:hover { background: var(--bg-highlight); color: var(--accent); border-color: var(--accent); }
|
||||
#split-btn.hidden { display: none; }
|
||||
|
||||
/* ==========================================================================
|
||||
Split panes
|
||||
========================================================================== */
|
||||
@@ -1259,7 +1281,7 @@ body { position: static; }
|
||||
.tool-output-stream { animation: none; border-left-color: var(--accent); }
|
||||
.judge-spinner-dot { animation: none; opacity: 1; }
|
||||
.thinking-indicator::after { animation: none; content: '...'; }
|
||||
.ws-tab, .ws-tab .tab-close, #new-tab-btn,
|
||||
.ws-tab, .ws-tab .tab-close, #new-tab-btn, #split-btn,
|
||||
.hmenu-item, .dashboard-card,
|
||||
.approval-btn, .approval-feedback-input,
|
||||
#plan-buttons button, .pane-input-area button,
|
||||
|
||||
Reference in New Issue
Block a user