Skip to content

Instantly share code, notes, and snippets.

@spencersmith
Last active February 20, 2026 23:50
Show Gist options
  • Select an option

  • Save spencersmith/693a60aa80d339d3cb79918c07a6e390 to your computer and use it in GitHub Desktop.

Select an option

Save spencersmith/693a60aa80d339d3cb79918c07a6e390 to your computer and use it in GitHub Desktop.
Tampermonkey - Loudly display active Medplum account at top of screen.
// ==UserScript==
// @name Medplum Environment Banner
// @namespace https://app.medplum.com/
// @version 1.0
// @description Adds a colored banner indicating the MedPlum account/environment you're currently in.
// @match https://app.medplum.com/*
// @grant GM_addStyle
// @run-at document-idle
// @author Spencer Smith
// @homepage https://pelairo.com
// @copyright None. Unlicense/CCO
// ==/UserScript==
(function () {
'use strict';
// ── Configuration ──────────────────────────────────────────────────
// Map project names to banner colors.
const PROJECT_COLORS = {
'Your Dev Env': { bg: '#16a34a', text: '#ffffff', label: '🟢🟢🟢' },
'Your Test Env': { bg: '#f59e0b', text: '#000000', label: '🟡🟡🟡' },
'Your Prod Env': { bg: '#dc2626', text: '#ffffff', label: '🔴🔴🔴' },
};
const DEFAULT_STYLE = { bg: '#6b7280', text: '#ffffff', label: '📋 UNKNOWN' };
const BANNER_HEIGHT = '15px';
// ── Banner Setup ───────────────────────────────────────────────────
GM_addStyle(`
#tm-env-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
height: ${BANNER_HEIGHT};
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.5px;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
transition: background-color 0.3s ease;
pointer-events: none;
}
`);
const banner = document.createElement('div');
banner.id = 'tm-env-banner';
document.body.prepend(banner);
// ── Navbar Width Detection ─────────────────────────────────────────
function updateBannerOffset() {
const navbar = document.querySelector(
'.mantine-AppShell-navbar, nav[class*="mantine-AppShell"]'
);
const navWidth = navbar ? navbar.getBoundingClientRect().width : 0;
banner.style.left = navWidth + 'px';
}
// Re-check on resize (navbar may collapse/expand)
window.addEventListener('resize', updateBannerOffset);
const offsetPoll = setInterval(updateBannerOffset, 1000);
setTimeout(() => clearInterval(offsetPoll), 10000);
// ── Project Detection ──────────────────────────────────────────────
function getProjectName() {
try {
const data = JSON.parse(localStorage.getItem('@medplum:activeLogin'));
return data?.project?.display || null;
} catch (_) {
return null;
}
}
function getStyle(projectName) {
return PROJECT_COLORS[projectName.trim()] || null;
}
let lastProjectName = null;
function updateBanner() {
const projectName = getProjectName();
if (!projectName) {
banner.textContent = 'No project detected';
banner.style.backgroundColor = '#6b7280';
banner.style.color = '#ffffff';
return;
}
if (projectName === lastProjectName) return;
lastProjectName = projectName;
const style = getStyle(projectName);
banner.textContent = style?.label || DEFAULT_STYLE.label;
banner.style.backgroundColor = (style || DEFAULT_STYLE).bg;
banner.style.color = (style || DEFAULT_STYLE).text;
console.log(`[Medplum Banner] Project: "${projectName}"`);
}
// ── Listen for Storage Changes & Poll ──────────────────────────────
window.addEventListener('storage', (e) => {
if (e.key === '@medplum:activeLogin') updateBanner();
});
// Initial + brief poll (localStorage may not be populated immediately)
updateBanner();
const poll = setInterval(updateBanner, 1000);
setTimeout(() => clearInterval(poll), 10000);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment