@@ -28,15 +28,24 @@ import UtilityView from '../../UtilityView';
2828import ToolView , { getToolTabParams } from '../../ToolView' ;
2929import { ApplicationStateProvider , useApplicationState } from '../../../../settings/static/ApplicationStateProvider' ;
3030import { BROWSER_PANELS , WORKSPACES } from '../../../../browser/static/js/constants' ;
31+ import pgWindow from 'sources/window' ;
3132
3233export function TabTitle ( { id, closable, defaultInternal} ) {
3334 const layoutDocker = React . useContext ( LayoutDockerContext ) ;
3435 const internal = layoutDocker ?. find ( id ) ?. internal ?? defaultInternal ;
36+ const showServerColorIndicator = usePreferences (
37+ ( state ) => state . getPreferencesForModule ( 'browser' ) ?. show_server_color_indicator ?? false
38+ ) ;
3539 const [ attrs , setAttrs ] = useState ( {
3640 icon : internal . icon ,
3741 title : internal . title ,
3842 tooltip : internal . tooltip ?? internal . title ,
43+ bgcolor : internal . bgcolor ,
44+ fgcolor : internal . fgcolor ,
3945 } ) ;
46+ // Track visibility state to trigger re-renders when tabs switch
47+ const [ isVisible , setIsVisible ] = useState ( layoutDocker ?. isTabVisible ( id ) ?? false ) ;
48+
4049 const onContextMenu = useCallback ( ( e ) => {
4150 const g = layoutDocker . find ( id ) ?. group ?? '' ;
4251 if ( ( layoutDocker . noContextGroups ?? [ ] ) . includes ( g ) ) return ;
@@ -46,25 +55,88 @@ export function TabTitle({id, closable, defaultInternal}) {
4655 } , [ ] ) ;
4756
4857 useEffect ( ( ) => {
58+ // Initialize visibility immediately once the effect runs and layoutObj is available
59+ setIsVisible ( layoutDocker ?. isTabVisible ( id ) ?? false ) ;
60+
4961 const deregister = layoutDocker . eventBus . registerListener ( LAYOUT_EVENTS . REFRESH_TITLE , ( panelId ) => {
5062 if ( panelId == id ) {
5163 const internal = layoutDocker ?. find ( id ) ?. internal ?? { } ;
5264 setAttrs ( {
5365 icon : internal . icon ,
5466 title : internal . title ,
5567 tooltip : internal . tooltip ?? internal . title ,
68+ bgcolor : internal . bgcolor ,
69+ fgcolor : internal . fgcolor ,
5670 } ) ;
5771 layoutDocker . saveLayout ( ) ;
5872 }
5973 } ) ;
6074
61- return ( ) => deregister ?. ( ) ;
75+ // Listen for tab activation to update visibility state
76+ // This ensures the color indicator appears/disappears when switching tabs
77+ const activeListener = layoutDocker . eventBus . registerListener ( LAYOUT_EVENTS . ACTIVE , ( ) => {
78+ const visible = layoutDocker ?. isTabVisible ( id ) ;
79+ setIsVisible ( visible ) ;
80+ } ) ;
81+
82+ // Listen for server color updates
83+ // This custom event is triggered specifically when server bgcolor/fgcolor changes
84+ const serverColorsUpdatedHandler = ( serverId , colorData ) => {
85+ const panelData = layoutDocker ?. find ( id ) ;
86+ if ( ! panelData ?. internal ) {
87+ return ;
88+ }
89+
90+ const tabServerId = panelData . internal . server_id ;
91+ if ( ! tabServerId || tabServerId !== serverId ) {
92+ return ;
93+ }
94+
95+ // Update internal data and attrs with new colors
96+ panelData . internal . bgcolor = colorData . bgcolor || null ;
97+ panelData . internal . fgcolor = colorData . fgcolor || null ;
98+ if ( panelData . metaData ?. tabParams ) {
99+ panelData . metaData . tabParams . bgcolor = colorData . bgcolor || null ;
100+ panelData . metaData . tabParams . fgcolor = colorData . fgcolor || null ;
101+ }
102+ setAttrs ( prev => ( {
103+ ...prev ,
104+ bgcolor : colorData . bgcolor || null ,
105+ fgcolor : colorData . fgcolor || null ,
106+ } ) ) ;
107+ // Persist the updated colors so they survive reloads
108+ layoutDocker . saveLayout ( ) ;
109+ } ;
110+
111+ // Listen to the custom server color update event
112+ pgWindow . pgAdmin ?. Browser ?. Events ?. on ( 'pgadmin:server:colors:updated' , serverColorsUpdatedHandler ) ;
113+
114+ return ( ) => {
115+ deregister ?. ( ) ;
116+ activeListener ?. ( ) ;
117+ pgWindow . pgAdmin ?. Browser ?. Events ?. off ( 'pgadmin:server:colors:updated' , serverColorsUpdatedHandler ) ;
118+ } ;
62119 } , [ ] ) ;
63120
64121 return (
65122 < Box display = "flex" alignItems = "center" title = { attrs . tooltip } onContextMenu = { onContextMenu } width = "100%" >
66123 { attrs . icon && < span className = { `dock-tab-icon ${ attrs . icon } ` } > </ span > }
67- < span style = { { textOverflow : 'ellipsis' , overflow : 'hidden' , whiteSpace : 'nowrap' } } data-visible = { layoutDocker . isTabVisible ( id ) } > { attrs . title } </ span >
124+ { showServerColorIndicator && attrs . bgcolor && ! isVisible && (
125+ < Box
126+ component = "span"
127+ sx = { {
128+ width : '12px' ,
129+ height : '12px' ,
130+ borderRadius : '50%' ,
131+ backgroundColor : attrs . bgcolor ,
132+ marginLeft : '2px' ,
133+ marginRight : '4px' ,
134+ flexShrink : 0 ,
135+ border : '1px solid rgba(0, 0, 0, 0.1)' ,
136+ } }
137+ />
138+ ) }
139+ < span style = { { textOverflow : 'ellipsis' , overflow : 'hidden' , whiteSpace : 'nowrap' } } data-visible = { isVisible } > { attrs . title } </ span >
68140 { closable && < PgIconButton title = { gettext ( 'Close' ) } icon = { < CloseIcon style = { { height : '0.7em' } } /> } size = "xs" noBorder onClick = { ( ) => {
69141 layoutDocker . close ( id ) ;
70142 } } style = { { margin : '-1px -10px -1px 0' } } /> }
@@ -368,15 +440,19 @@ export class LayoutDocker {
368440 this . saveLayout ( ) ;
369441 }
370442
371- static getPanel ( { icon, title, closable, tooltip, renamable, manualClose, ...attrs } ) {
443+ static getPanel ( { icon, title, closable, tooltip, renamable, manualClose, bgcolor , fgcolor , server_id , ...attrs } ) {
372444 const internal = {
373445 icon : icon ,
374446 title : title ,
375447 tooltip : tooltip ,
376448 closable : _ . isUndefined ( closable ) ? manualClose : closable ,
377449 renamable : renamable ,
378450 manualClose : manualClose ,
451+ bgcolor : bgcolor ,
452+ fgcolor : fgcolor ,
453+ server_id : server_id , // Store server_id to enable color updates when server properties change
379454 } ;
455+
380456 return {
381457 cached : true ,
382458 group : 'default' ,
0 commit comments