Skip to content

Commit 7e3e6df

Browse files
committed
Feat: Added a option to colorize panel/tab header based on server color #2431
1 parent aa3dc38 commit 7e3e6df

File tree

7 files changed

+101
-7
lines changed

7 files changed

+101
-7
lines changed

web/pgadmin/browser/register_browser_preferences.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ def register_browser_preferences(self):
101101
)
102102
)
103103

104+
self.preference.register(
105+
'display', 'show_server_color_indicator',
106+
gettext("Show server color indicator in panel tabs?"), 'boolean', False,
107+
category_label=PREF_LABEL_DISPLAY,
108+
help_str=gettext(
109+
'If enabled, a colored circle indicator will be shown in panel tabs '
110+
'(Query Tool, ERD Tool, etc.) matching the server\'s custom background color. '
111+
'This helps quickly identify which server a panel is connected to.'
112+
)
113+
)
114+
104115
self.table_row_count_threshold = self.preference.register(
105116
'properties', 'table_row_count_threshold',
106117
gettext("Count rows if estimated less than"), 'integer', 2000,

web/pgadmin/static/js/helpers/Layout/index.jsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,15 @@ import { BROWSER_PANELS, WORKSPACES } from '../../../../browser/static/js/consta
3232
export function TabTitle({id, closable, defaultInternal}) {
3333
const layoutDocker = React.useContext(LayoutDockerContext);
3434
const internal = layoutDocker?.find(id)?.internal ?? defaultInternal;
35+
const showServerColorIndicator = usePreferences(
36+
React.useCallback((state) => state.getPreferencesForModule('browser')?.show_server_color_indicator ?? false, [])
37+
);
3538
const [attrs, setAttrs] = useState({
3639
icon: internal.icon,
3740
title: internal.title,
3841
tooltip: internal.tooltip ?? internal.title,
42+
bgcolor: internal.bgcolor,
43+
fgcolor: internal.fgcolor,
3944
});
4045
const onContextMenu = useCallback((e)=>{
4146
const g = layoutDocker.find(id)?.group??'';
@@ -53,6 +58,8 @@ export function TabTitle({id, closable, defaultInternal}) {
5358
icon: internal.icon,
5459
title: internal.title,
5560
tooltip: internal.tooltip ?? internal.title,
61+
bgcolor: internal.bgcolor,
62+
fgcolor: internal.fgcolor,
5663
});
5764
layoutDocker.saveLayout();
5865
}
@@ -64,6 +71,21 @@ export function TabTitle({id, closable, defaultInternal}) {
6471
return (
6572
<Box display="flex" alignItems="center" title={attrs.tooltip} onContextMenu={onContextMenu} width="100%">
6673
{attrs.icon && <span className={`dock-tab-icon ${attrs.icon}`}></span>}
74+
{showServerColorIndicator && attrs.bgcolor && !layoutDocker.isTabVisible(id) && (
75+
<Box
76+
component="span"
77+
sx={{
78+
width: '12px',
79+
height: '12px',
80+
borderRadius: '50%',
81+
backgroundColor: attrs.bgcolor,
82+
marginLeft: '2px',
83+
marginRight: '4px',
84+
flexShrink: 0,
85+
border: '1px solid rgba(0, 0, 0, 0.1)',
86+
}}
87+
/>
88+
)}
6789
<span style={{textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap'}} data-visible={layoutDocker.isTabVisible(id)}>{attrs.title}</span>
6890
{closable && <PgIconButton title={gettext('Close')} icon={<CloseIcon style={{height: '0.7em'}} />} size="xs" noBorder onClick={()=>{
6991
layoutDocker.close(id);
@@ -368,14 +390,16 @@ export class LayoutDocker {
368390
this.saveLayout();
369391
}
370392

371-
static getPanel({icon, title, closable, tooltip, renamable, manualClose, ...attrs}) {
393+
static getPanel({icon, title, closable, tooltip, renamable, manualClose, bgcolor, fgcolor, ...attrs}) {
372394
const internal = {
373395
icon: icon,
374396
title: title,
375397
tooltip: tooltip,
376398
closable: _.isUndefined(closable) ? manualClose : closable,
377399
renamable: renamable,
378400
manualClose: manualClose,
401+
bgcolor: bgcolor,
402+
fgcolor: fgcolor,
379403
};
380404
return {
381405
cached: true,

web/pgadmin/tools/debugger/static/js/DebuggerModule.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,13 +378,23 @@ export default class DebuggerModule {
378378
let open_new_tab = browser_preferences.new_browser_tab_open;
379379
const db_label = self.checkDbNameChange(data, dbNode, newTreeInfo);
380380
let label = getAppropriateLabel(newTreeInfo);
381+
382+
// Extract bgcolor and fgcolor from server icon if available
383+
let bgcolor = null;
384+
let fgcolor = null;
385+
if (newTreeInfo?.server?.icon) {
386+
const iconParts = newTreeInfo.server.icon.split(' ');
387+
bgcolor = iconParts[1] || null;
388+
fgcolor = iconParts[2] || null;
389+
}
390+
381391
pgAdmin.Browser.Events.trigger(
382392
'pgadmin:tool:show',
383393
`${BROWSER_PANELS.DEBUGGER_TOOL}_${trans_id}`,
384394
url,
385395
null,
386396
{title: getDebuggerTitle(browser_preferences, label, newTreeInfo.schema.label, db_label, null, self.pgBrowser),
387-
icon: 'fa fa-bug', manualClose: false, renamable: true},
397+
icon: 'fa fa-bug', manualClose: false, renamable: true, bgcolor: bgcolor, fgcolor: fgcolor},
388398
Boolean(open_new_tab?.includes('debugger'))
389399
);
390400
})
@@ -527,13 +537,22 @@ export default class DebuggerModule {
527537

528538
let label = getAppropriateLabel(treeInfo);
529539

540+
// Extract bgcolor and fgcolor from server icon if available
541+
let bgcolor = null;
542+
let fgcolor = null;
543+
if (treeInfo?.server?.icon) {
544+
const iconParts = treeInfo.server.icon.split(' ');
545+
bgcolor = iconParts[1] || null;
546+
fgcolor = iconParts[2] || null;
547+
}
548+
530549
pgAdmin.Browser.Events.trigger(
531550
'pgadmin:tool:show',
532551
`${BROWSER_PANELS.DEBUGGER_TOOL}_${res.data.data.debuggerTransId}`,
533552
url,
534553
null,
535554
{title: getDebuggerTitle(browser_preferences, label, db_label, db_label, null, self.pgBrowser),
536-
icon: 'fa fa-bug', manualClose: false, renamable: true},
555+
icon: 'fa fa-bug', manualClose: false, renamable: true, bgcolor: bgcolor, fgcolor: fgcolor},
537556
Boolean(open_new_tab?.includes('debugger'))
538557
);
539558
})

web/pgadmin/tools/erd/static/js/ERDModule.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,21 @@ export default class ERDModule {
142142
const panelUrl = this.getPanelUrl(transId, parentData, gen);
143143
const open_new_tab = usePreferences.getState().getPreferencesForModule('browser').new_browser_tab_open;
144144

145+
// Extract bgcolor and fgcolor from server icon if available
146+
let bgcolor = null;
147+
let fgcolor = null;
148+
if (parentData?.server?.icon) {
149+
const iconParts = parentData.server.icon.split(' ');
150+
bgcolor = iconParts[1] || null;
151+
fgcolor = iconParts[2] || null;
152+
}
153+
145154
pgAdmin.Browser.Events.trigger(
146155
'pgadmin:tool:show',
147156
`${BROWSER_PANELS.ERD_TOOL}_${transId}`,
148157
panelUrl,
149158
{sql_id: toolDataId, connectionTitle: _.escape(panelTitle), db_name:parentData.database.label, server_name: parentData.server.label, user: parentData.server.user.name, server_type: parentData.server.server_type},
150-
{title: 'Untitled', icon: 'fa fa-sitemap'},
159+
{title: 'Untitled', icon: 'fa fa-sitemap', bgcolor: bgcolor, fgcolor: fgcolor},
151160
Boolean(open_new_tab?.includes('erd_tool'))
152161
);
153162

web/pgadmin/tools/psql/static/js/PsqlModule.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,21 @@ export default class Psql {
173173

174174
const open_new_tab = usePreferences.getState().getPreferencesForModule('browser').new_browser_tab_open;
175175

176+
// Extract bgcolor and fgcolor from server icon if available
177+
let bgcolor = null;
178+
let fgcolor = null;
179+
if (parentData?.server?.icon) {
180+
const iconParts = parentData.server.icon.split(' ');
181+
bgcolor = iconParts[1] || null;
182+
fgcolor = iconParts[2] || null;
183+
}
184+
176185
pgAdmin.Browser.Events.trigger(
177186
'pgadmin:tool:show',
178187
`${BROWSER_PANELS.PSQL_TOOL}_${transId}`,
179188
panelUrl,
180189
{title: panelTitle, db: db_label, server_name: parentData.server.label, 'user': parentData.server.user.name },
181-
{title: panelTitle, icon: 'pg-font-icon icon-terminal', manualClose: false, renamable: true},
190+
{title: panelTitle, icon: 'pg-font-icon icon-terminal', manualClose: false, renamable: true, bgcolor: bgcolor, fgcolor: fgcolor},
182191
Boolean(open_new_tab?.includes('psql_tool'))
183192
);
184193

web/pgadmin/tools/schema_diff/static/js/SchemaDiffModule.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,25 @@ export default class SchemaDiff {
7575
let browserPreferences = usePreferences.getState().getPreferencesForModule('browser');
7676
let openInNewTab = browserPreferences.new_browser_tab_open;
7777

78+
// Extract bgcolor and fgcolor from selected server if available
79+
let bgcolor = null;
80+
let fgcolor = null;
81+
const selectedItem = pgAdmin.Browser.tree?.selected();
82+
if (selectedItem) {
83+
const selectedNodeInfo = pgAdmin.Browser.tree?.getTreeNodeHierarchy(selectedItem);
84+
if (selectedNodeInfo?.server?.icon) {
85+
const iconParts = selectedNodeInfo.server.icon.split(' ');
86+
bgcolor = iconParts[1] || null;
87+
fgcolor = iconParts[2] || null;
88+
}
89+
}
90+
7891
pgAdmin.Browser.Events.trigger(
7992
'pgadmin:tool:show',
8093
`${BROWSER_PANELS.SCHEMA_DIFF_TOOL}_${trans_id}`,
8194
baseUrl,
8295
{...params},
83-
{title: panelTitle, icon: 'pg-font-icon icon-compare', manualClose: false, renamable: true},
96+
{title: panelTitle, icon: 'pg-font-icon icon-compare', manualClose: false, renamable: true, bgcolor: bgcolor, fgcolor: fgcolor},
8497
Boolean(openInNewTab?.includes('schema_diff'))
8598
);
8699
return true;

web/pgadmin/tools/sqleditor/static/js/SQLEditorModule.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,21 @@ export default class SQLEditor {
223223
const [icon, tooltip] = panelTitleFunc.getQueryToolIcon(panel_title, is_query_tool);
224224
let selectedNodeInfo = pgAdmin.Browser.tree?.selected() ? pgAdmin.Browser.tree?.getTreeNodeHierarchy(pgAdmin.Browser.tree.selected()) : null;
225225

226+
// Extract bgcolor and fgcolor from selectedNodeInfo if available
227+
let bgcolor = null;
228+
let fgcolor = null;
229+
if (selectedNodeInfo?.server?.icon) {
230+
const iconParts = selectedNodeInfo.server.icon.split(' ');
231+
bgcolor = iconParts[1] || null;
232+
fgcolor = iconParts[2] || null;
233+
}
234+
226235
pgAdmin.Browser.Events.trigger(
227236
'pgadmin:tool:show',
228237
`${BROWSER_PANELS.QUERY_TOOL}_${trans_id}`,
229238
panel_url,
230239
{...params, title: panel_title, selectedNodeInfo: JSON.stringify(selectedNodeInfo)},
231-
{title: panel_title, icon: icon, tooltip: tooltip, renamable: true},
240+
{title: panel_title, icon: icon, tooltip: tooltip, renamable: true, bgcolor: bgcolor, fgcolor: fgcolor},
232241
Boolean(open_new_tab?.includes('qt'))
233242
);
234243
return true;

0 commit comments

Comments
 (0)