@@ -139,6 +139,34 @@ define(function (require, exports, module) {
139139 * Call this when tabs are added, removed, or renamed.
140140 * @private
141141 */
142+ /**
143+ * Build a tab element for a panel.
144+ * @param {Panel } panel
145+ * @param {boolean } isActive
146+ * @return {jQueryObject }
147+ * @private
148+ */
149+ function _buildTab ( panel , isActive ) {
150+ let title = panel . _tabTitle || _getPanelTitle ( panel . panelID , panel . $panel ) ;
151+ let $tab = $ ( '<div class="bottom-panel-tab"></div>' )
152+ . toggleClass ( 'active' , isActive )
153+ . attr ( 'data-panel-id' , panel . panelID ) ;
154+ const opts = panel . _options ;
155+ if ( opts . iconClass ) {
156+ $tab . append ( $ ( '<i class="bottom-panel-tab-icon panel-titlebar-icon"></i>' )
157+ . addClass ( opts . iconClass ) ) ;
158+ } else if ( opts . iconSvg ) {
159+ $tab . append ( $ ( '<img class="bottom-panel-tab-icon panel-titlebar-icon">' )
160+ . attr ( "src" , opts . iconSvg ) ) ;
161+ } else {
162+ // Fallback generic icon for panels without a custom icon
163+ $tab . append ( $ ( '<i class="bottom-panel-tab-icon panel-titlebar-icon fa-solid fa-window-maximize"></i>' ) ) ;
164+ }
165+ $tab . append ( $ ( '<span class="bottom-panel-tab-title"></span>' ) . text ( title ) ) ;
166+ $tab . append ( $ ( '<span class="bottom-panel-tab-close-btn">×</span>' ) . attr ( 'title' , Strings . CLOSE ) ) ;
167+ return $tab ;
168+ }
169+
142170 function _updateBottomPanelTabBar ( ) {
143171 if ( ! _$tabsOverflow ) {
144172 return ;
@@ -150,21 +178,15 @@ define(function (require, exports, module) {
150178 if ( ! panel ) {
151179 return ;
152180 }
153- let title = panel . _tabTitle || _getPanelTitle ( panelId , panel . $panel ) ;
154- let isActive = ( panelId === _activeId ) ;
155- let $tab = $ ( '<div class="bottom-panel-tab"></div>' )
156- . toggleClass ( 'active' , isActive )
157- . attr ( 'data-panel-id' , panelId ) ;
158- $tab . append ( $ ( '<span class="bottom-panel-tab-title"></span>' ) . text ( title ) ) ;
159- $tab . append ( $ ( '<span class="bottom-panel-tab-close-btn">×</span>' ) . attr ( 'title' , Strings . CLOSE ) ) ;
160- _$tabsOverflow . append ( $tab ) ;
181+ _$tabsOverflow . append ( _buildTab ( panel , panelId === _activeId ) ) ;
161182 } ) ;
162183
163184 // Re-append the "+" button at the end (after all tabs)
164185 if ( _$addBtn ) {
165186 _$tabsOverflow . append ( _$addBtn ) ;
166187 _updateAddButtonVisibility ( ) ;
167188 }
189+ _checkTabOverflow ( ) ;
168190 }
169191
170192 /**
@@ -199,13 +221,7 @@ define(function (require, exports, module) {
199221 if ( ! panel ) {
200222 return ;
201223 }
202- let title = panel . _tabTitle || _getPanelTitle ( panelId , panel . $panel ) ;
203- let isActive = ( panelId === _activeId ) ;
204- let $tab = $ ( '<div class="bottom-panel-tab"></div>' )
205- . toggleClass ( 'active' , isActive )
206- . attr ( 'data-panel-id' , panelId ) ;
207- $tab . append ( $ ( '<span class="bottom-panel-tab-title"></span>' ) . text ( title ) ) ;
208- $tab . append ( $ ( '<span class="bottom-panel-tab-close-btn">×</span>' ) . attr ( 'title' , Strings . CLOSE ) ) ;
224+ let $tab = _buildTab ( panel , panelId === _activeId ) ;
209225
210226 // Insert before the "+" button so it stays at the end
211227 if ( _$addBtn && _$addBtn . parent ( ) . length ) {
@@ -214,6 +230,7 @@ define(function (require, exports, module) {
214230 _$tabsOverflow . append ( $tab ) ;
215231 }
216232 _updateAddButtonVisibility ( ) ;
233+ _checkTabOverflow ( ) ;
217234 }
218235
219236 /**
@@ -228,6 +245,22 @@ define(function (require, exports, module) {
228245 }
229246 _$tabsOverflow . find ( '.bottom-panel-tab[data-panel-id="' + panelId + '"]' ) . remove ( ) ;
230247 _updateAddButtonVisibility ( ) ;
248+ _checkTabOverflow ( ) ;
249+ }
250+
251+ /**
252+ * Check if the tab bar is overflowing and collapse tabs to icons if so.
253+ * Only collapses tabs that have an icon available.
254+ * @private
255+ */
256+ function _checkTabOverflow ( ) {
257+ if ( ! _$tabBar ) {
258+ return ;
259+ }
260+ // Remove collapsed state first to measure true width
261+ _$tabBar . removeClass ( "bottom-panel-tabs-collapsed" ) ;
262+ const isOverflowing = _$tabsOverflow [ 0 ] . scrollWidth > _$tabsOverflow [ 0 ] . clientWidth ;
263+ _$tabBar . toggleClass ( "bottom-panel-tabs-collapsed" , isOverflowing ) ;
231264 }
232265
233266 /**
@@ -281,10 +314,19 @@ define(function (require, exports, module) {
281314 * @param {string } id Unique panel identifier.
282315 * @param {string= } title Optional display title for the tab bar.
283316 */
284- function Panel ( $panel , id , title ) {
317+ /**
318+ * @param {jQueryObject } $panel
319+ * @param {string } id
320+ * @param {string= } title
321+ * @param {Object= } options
322+ * @param {string= } options.iconClass FontAwesome class string (e.g. "fa-solid fa-terminal").
323+ * @param {string= } options.iconSvg Path to an SVG icon (e.g. "styles/images/icon.svg").
324+ */
325+ function Panel ( $panel , id , title , options ) {
285326 this . $panel = $panel ;
286327 this . panelID = id ;
287328 this . _tabTitle = _getPanelTitle ( id , $panel , title ) ;
329+ this . _options = options || { } ;
288330 _panelMap [ id ] = this ;
289331 }
290332
@@ -573,6 +615,10 @@ define(function (require, exports, module) {
573615 _toggleMaximize ( ) ;
574616 } ) ;
575617
618+ // Re-check tab overflow when the tab bar resizes (e.g. window resize)
619+ const tabBarResizeObserver = new ResizeObserver ( _checkTabOverflow ) ;
620+ tabBarResizeObserver . observe ( _$tabsOverflow [ 0 ] ) ;
621+
576622 // Restore maximize state from preferences (survives reload).
577623 _isMaximized = PreferencesManager . getViewState ( PREF_BOTTOM_PANEL_MAXIMIZED ) === true ;
578624
0 commit comments