@@ -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 ;
@@ -53,18 +62,72 @@ export function TabTitle({id, closable, defaultInternal}) {
5362 icon : internal . icon ,
5463 title : internal . title ,
5564 tooltip : internal . tooltip ?? internal . title ,
65+ bgcolor : internal . bgcolor ,
66+ fgcolor : internal . fgcolor ,
5667 } ) ;
5768 layoutDocker . saveLayout ( ) ;
5869 }
5970 } ) ;
6071
61- return ( ) => deregister ?. ( ) ;
72+ // Listen for tab activation to update visibility state
73+ // This ensures the color indicator appears/disappears when switching tabs
74+ const activeListener = layoutDocker . eventBus . registerListener ( LAYOUT_EVENTS . ACTIVE , ( ) => {
75+ const visible = layoutDocker ?. isTabVisible ( id ) ;
76+ setIsVisible ( visible ) ;
77+ } ) ;
78+
79+ // Listen for server color updates
80+ // This custom event is triggered specifically when server bgcolor/fgcolor changes
81+ const serverColorsUpdatedHandler = ( serverId , colorData ) => {
82+ const panelData = layoutDocker ?. find ( id ) ;
83+ if ( ! panelData ?. internal ) {
84+ return ;
85+ }
86+
87+ const tabServerId = panelData . internal . server_id ;
88+ if ( ! tabServerId || tabServerId !== serverId ) {
89+ return ;
90+ }
91+
92+ // Update internal data and attrs with new colors
93+ panelData . internal . bgcolor = colorData . bgcolor || null ;
94+ panelData . internal . fgcolor = colorData . fgcolor || null ;
95+ setAttrs ( prev => ( {
96+ ...prev ,
97+ bgcolor : colorData . bgcolor || null ,
98+ fgcolor : colorData . fgcolor || null ,
99+ } ) ) ;
100+ } ;
101+
102+ // Listen to the custom server color update event
103+ pgWindow . pgAdmin ?. Browser ?. Events ?. on ( 'pgadmin:server:colors:updated' , serverColorsUpdatedHandler ) ;
104+
105+ return ( ) => {
106+ deregister ?. ( ) ;
107+ activeListener ?. ( ) ;
108+ pgWindow . pgAdmin ?. Browser ?. Events ?. off ( 'pgadmin:server:colors:updated' , serverColorsUpdatedHandler ) ;
109+ } ;
62110 } , [ ] ) ;
63111
64112 return (
65113 < Box display = "flex" alignItems = "center" title = { attrs . tooltip } onContextMenu = { onContextMenu } width = "100%" >
66114 { 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 >
115+ { showServerColorIndicator && attrs . bgcolor && ! isVisible && (
116+ < Box
117+ component = "span"
118+ sx = { {
119+ width : '12px' ,
120+ height : '12px' ,
121+ borderRadius : '50%' ,
122+ backgroundColor : attrs . bgcolor ,
123+ marginLeft : '2px' ,
124+ marginRight : '4px' ,
125+ flexShrink : 0 ,
126+ border : '1px solid rgba(0, 0, 0, 0.1)' ,
127+ } }
128+ />
129+ ) }
130+ < span style = { { textOverflow : 'ellipsis' , overflow : 'hidden' , whiteSpace : 'nowrap' } } data-visible = { isVisible } > { attrs . title } </ span >
68131 { closable && < PgIconButton title = { gettext ( 'Close' ) } icon = { < CloseIcon style = { { height : '0.7em' } } /> } size = "xs" noBorder onClick = { ( ) => {
69132 layoutDocker . close ( id ) ;
70133 } } style = { { margin : '-1px -10px -1px 0' } } /> }
@@ -368,15 +431,19 @@ export class LayoutDocker {
368431 this . saveLayout ( ) ;
369432 }
370433
371- static getPanel ( { icon, title, closable, tooltip, renamable, manualClose, ...attrs } ) {
434+ static getPanel ( { icon, title, closable, tooltip, renamable, manualClose, bgcolor , fgcolor , server_id , ...attrs } ) {
372435 const internal = {
373436 icon : icon ,
374437 title : title ,
375438 tooltip : tooltip ,
376439 closable : _ . isUndefined ( closable ) ? manualClose : closable ,
377440 renamable : renamable ,
378441 manualClose : manualClose ,
442+ bgcolor : bgcolor ,
443+ fgcolor : fgcolor ,
444+ server_id : server_id , // Store server_id to enable color updates when server properties change
379445 } ;
446+
380447 return {
381448 cached : true ,
382449 group : 'default' ,
0 commit comments