|
// ==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); |
|
})(); |