@@ -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,84 @@ 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+ setAttrs ( prev => ( {
99+ ...prev ,
100+ bgcolor : colorData . bgcolor || null ,
101+ fgcolor : colorData . fgcolor || null ,
102+ } ) ) ;
103+ // Persist the updated colors so they survive reloads
104+ layoutDocker . saveLayout ( ) ;
105+ } ;
106+
107+ // Listen to the custom server color update event
108+ pgWindow . pgAdmin ?. Browser ?. Events ?. on ( 'pgadmin:server:colors:updated' , serverColorsUpdatedHandler ) ;
109+
110+ return ( ) => {
111+ deregister ?. ( ) ;
112+ activeListener ?. ( ) ;
113+ pgWindow . pgAdmin ?. Browser ?. Events ?. off ( 'pgadmin:server:colors:updated' , serverColorsUpdatedHandler ) ;
114+ } ;
62115 } , [ ] ) ;
63116
64117 return (
65118 < Box display = "flex" alignItems = "center" title = { attrs . tooltip } onContextMenu = { onContextMenu } width = "100%" >
66119 { 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 >
120+ { showServerColorIndicator && attrs . bgcolor && ! isVisible && (
121+ < Box
122+ component = "span"
123+ sx = { {
124+ width : '12px' ,
125+ height : '12px' ,
126+ borderRadius : '50%' ,
127+ backgroundColor : attrs . bgcolor ,
128+ marginLeft : '2px' ,
129+ marginRight : '4px' ,
130+ flexShrink : 0 ,
131+ border : '1px solid rgba(0, 0, 0, 0.1)' ,
132+ } }
133+ />
134+ ) }
135+ < span style = { { textOverflow : 'ellipsis' , overflow : 'hidden' , whiteSpace : 'nowrap' } } data-visible = { isVisible } > { attrs . title } </ span >
68136 { closable && < PgIconButton title = { gettext ( 'Close' ) } icon = { < CloseIcon style = { { height : '0.7em' } } /> } size = "xs" noBorder onClick = { ( ) => {
69137 layoutDocker . close ( id ) ;
70138 } } style = { { margin : '-1px -10px -1px 0' } } /> }
@@ -368,15 +436,19 @@ export class LayoutDocker {
368436 this . saveLayout ( ) ;
369437 }
370438
371- static getPanel ( { icon, title, closable, tooltip, renamable, manualClose, ...attrs } ) {
439+ static getPanel ( { icon, title, closable, tooltip, renamable, manualClose, bgcolor , fgcolor , server_id , ...attrs } ) {
372440 const internal = {
373441 icon : icon ,
374442 title : title ,
375443 tooltip : tooltip ,
376444 closable : _ . isUndefined ( closable ) ? manualClose : closable ,
377445 renamable : renamable ,
378446 manualClose : manualClose ,
447+ bgcolor : bgcolor ,
448+ fgcolor : fgcolor ,
449+ server_id : server_id , // Store server_id to enable color updates when server properties change
379450 } ;
451+
380452 return {
381453 cached : true ,
382454 group : 'default' ,
0 commit comments