@@ -1151,23 +1151,6 @@ document.addEventListener("DOMContentLoaded", function () {
11511151 if ( tabId ) switchTab ( tabId ) ;
11521152 } ;
11531153
1154- // "+ Create" button at end of tab list (placed outside tabList to prevent ARIA child violation)
1155- let newBtn = tabList . parentElement . querySelector ( '.tab-new-btn' ) ;
1156- if ( ! newBtn ) {
1157- newBtn = document . createElement ( 'button' ) ;
1158- newBtn . className = 'tab-new-btn' ;
1159- newBtn . title = 'New Tab (Ctrl+T)' ;
1160- newBtn . setAttribute ( 'aria-label' , 'Open new tab' ) ;
1161- newBtn . innerHTML = '<i class="bi bi-plus-lg"></i>' ;
1162- newBtn . addEventListener ( 'click' , function ( ) { newTab ( ) ; } ) ;
1163-
1164- const resetBtn = document . getElementById ( 'tab-reset-btn' ) ;
1165- if ( resetBtn ) {
1166- tabList . parentElement . insertBefore ( newBtn , resetBtn ) ;
1167- } else {
1168- tabList . parentElement . appendChild ( newBtn ) ;
1169- }
1170- }
11711154
11721155 // Auto-scroll active tab into view (paint-aligned to prevent forced reflows)
11731156 const activeItem = tabList . querySelector ( '.tab-item.active' ) ;
@@ -1224,6 +1207,91 @@ document.addEventListener("DOMContentLoaded", function () {
12241207 } ;
12251208
12261209 renderMobileTabList ( tabsArr , currentActiveTabId ) ;
1210+ if ( typeof tabList . dispatchEvent === 'function' ) {
1211+ tabList . dispatchEvent ( new Event ( 'scroll' ) ) ;
1212+ }
1213+ }
1214+
1215+ // ========================================
1216+ // TAB OVERFLOW — Scroll Buttons, Wheel, Indicators
1217+ // ========================================
1218+
1219+ var _tabOverflowInitialized = false ;
1220+
1221+ function setupTabOverflow ( ) {
1222+ if ( _tabOverflowInitialized ) return ;
1223+ _tabOverflowInitialized = true ;
1224+
1225+ var tabBar = document . getElementById ( 'tab-bar' ) ;
1226+ var tabList = document . getElementById ( 'tab-list' ) ;
1227+ if ( ! tabBar || ! tabList ) return ;
1228+
1229+ // --- Create scroll arrow buttons ---
1230+ var scrollLeftBtn = document . createElement ( 'button' ) ;
1231+ scrollLeftBtn . className = 'tab-scroll-btn tab-scroll-left' ;
1232+ scrollLeftBtn . setAttribute ( 'aria-label' , 'Scroll tabs left' ) ;
1233+ scrollLeftBtn . title = 'Scroll left' ;
1234+ scrollLeftBtn . innerHTML = '<i class="bi bi-chevron-left"></i>' ;
1235+ scrollLeftBtn . addEventListener ( 'click' , function ( ) {
1236+ tabList . scrollBy ( { left : - 200 , behavior : 'smooth' } ) ;
1237+ } ) ;
1238+
1239+ var scrollRightBtn = document . createElement ( 'button' ) ;
1240+ scrollRightBtn . className = 'tab-scroll-btn tab-scroll-right' ;
1241+ scrollRightBtn . setAttribute ( 'aria-label' , 'Scroll tabs right' ) ;
1242+ scrollRightBtn . title = 'Scroll right' ;
1243+ scrollRightBtn . innerHTML = '<i class="bi bi-chevron-right"></i>' ;
1244+ scrollRightBtn . addEventListener ( 'click' , function ( ) {
1245+ tabList . scrollBy ( { left : 200 , behavior : 'smooth' } ) ;
1246+ } ) ;
1247+
1248+ // Insert scroll buttons flanking the tab-list
1249+ tabBar . insertBefore ( scrollLeftBtn , tabList ) ;
1250+ var newBtn = document . getElementById ( 'tab-new-btn' ) ;
1251+ if ( newBtn ) {
1252+ tabBar . insertBefore ( scrollRightBtn , newBtn ) ;
1253+ } else {
1254+ var resetBtn = document . getElementById ( 'tab-reset-btn' ) ;
1255+ if ( resetBtn ) {
1256+ tabBar . insertBefore ( scrollRightBtn , resetBtn ) ;
1257+ } else {
1258+ tabBar . appendChild ( scrollRightBtn ) ;
1259+ }
1260+ }
1261+
1262+ // --- Overflow detection ---
1263+ var _overflowRafId = null ;
1264+ function updateOverflowState ( ) {
1265+ if ( _overflowRafId ) return ;
1266+ _overflowRafId = requestAnimationFrame ( function ( ) {
1267+ _overflowRafId = null ;
1268+ var hasLeft = tabList . scrollLeft > 1 ;
1269+ var hasRight = tabList . scrollLeft < ( tabList . scrollWidth - tabList . clientWidth - 1 ) ;
1270+ tabBar . classList . toggle ( 'has-overflow-left' , hasLeft ) ;
1271+ tabBar . classList . toggle ( 'has-overflow-right' , hasRight ) ;
1272+ } ) ;
1273+ }
1274+
1275+ tabList . addEventListener ( 'scroll' , updateOverflowState ) ;
1276+
1277+ // Use ResizeObserver to detect when overflow state changes due to window resize
1278+ if ( typeof ResizeObserver !== 'undefined' ) {
1279+ var resizeObs = new ResizeObserver ( updateOverflowState ) ;
1280+ resizeObs . observe ( tabList ) ;
1281+ }
1282+
1283+ // Initial check
1284+ updateOverflowState ( ) ;
1285+
1286+ // --- Mouse wheel scroll: vertical wheel → horizontal scroll ---
1287+ tabList . addEventListener ( 'wheel' , function ( e ) {
1288+ // Only intercept vertical wheel (don't fight native horizontal wheel/trackpad)
1289+ if ( Math . abs ( e . deltaY ) > Math . abs ( e . deltaX ) ) {
1290+ e . preventDefault ( ) ;
1291+ tabList . scrollLeft += e . deltaY ;
1292+ updateOverflowState ( ) ;
1293+ }
1294+ } , { passive : false } ) ;
12271295 }
12281296
12291297 function renderMobileTabList ( tabsArr , currentActiveTabId ) {
@@ -1510,6 +1578,14 @@ document.addEventListener("DOMContentLoaded", function () {
15101578 markdownEditor . scrollTop = activeTab . scrollPos || 0 ;
15111579 } ) ;
15121580 renderTabBar ( tabs , activeTabId ) ;
1581+ setupTabOverflow ( ) ;
1582+
1583+ const staticNewBtn = document . getElementById ( 'tab-new-btn' ) ;
1584+ if ( staticNewBtn ) {
1585+ staticNewBtn . onclick = function ( ) {
1586+ newTab ( ) ;
1587+ } ;
1588+ }
15131589 }
15141590
15151591 // Late-load callback hook for Neutralino command-line files
0 commit comments