mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
feat: add light mode to dev portal (#2072)
## Summary Adds a light / dark mode toggle to the dev portal. <img width="1549" height="922" alt="Screenshot 2026-04-08 at 18 57 29" src="https://github.com/user-attachments/assets/e118d21b-6840-4db3-8309-a8af43ea698b" /> <img width="1549" height="922" alt="Screenshot 2026-04-08 at 18 57 24" src="https://github.com/user-attachments/assets/ff08c270-2ba8-4514-b46d-4b671957e8bf" />
This commit is contained in:
parent
73b746cbcf
commit
337ebff054
2 changed files with 130 additions and 12 deletions
|
|
@ -66,9 +66,25 @@
|
|||
</svg>
|
||||
HyperDX Dev Portal
|
||||
</h1>
|
||||
<div class="status">
|
||||
<div class="dot"></div>
|
||||
<span id="refresh-status">Auto-refreshing every 3s</span>
|
||||
|
||||
<div class="header-right">
|
||||
<div class="status">
|
||||
<div class="dot"></div>
|
||||
<span id="refresh-status">Auto-refreshing every 3s</span>
|
||||
</div>
|
||||
<button
|
||||
class="theme-toggle"
|
||||
id="theme-toggle"
|
||||
onclick="toggleTheme()"
|
||||
title="Toggle light/dark mode"
|
||||
>
|
||||
<svg id="theme-icon-sun" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1m0 16v1m8.66-13.66l-.71.71M4.05 19.95l-.71.71M21 12h-1M4 12H3m16.66 7.66l-.71-.71M4.05 4.05l-.71-.71M16 12a4 4 0 11-8 0 4 4 0 018 0z"/>
|
||||
</svg>
|
||||
<svg id="theme-icon-moon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="display:none">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12.79A9 9 0 1111.21 3a7 7 0 009.79 9.79z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -120,6 +136,46 @@
|
|||
</div>
|
||||
|
||||
<script>
|
||||
// --- Theme ---
|
||||
function getPreferredTheme() {
|
||||
const stored = localStorage.getItem('hdx-dev-portal-theme');
|
||||
if (stored === 'light' || stored === 'dark') return stored;
|
||||
return window.matchMedia('(prefers-color-scheme: light)').matches
|
||||
? 'light'
|
||||
: 'dark';
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
const sunIcon = document.getElementById('theme-icon-sun');
|
||||
const moonIcon = document.getElementById('theme-icon-moon');
|
||||
if (sunIcon && moonIcon) {
|
||||
// Sun icon shown in dark mode (click to go light), moon in light mode
|
||||
sunIcon.style.display = theme === 'dark' ? '' : 'none';
|
||||
moonIcon.style.display = theme === 'light' ? '' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const current =
|
||||
document.documentElement.getAttribute('data-theme') || 'dark';
|
||||
const next = current === 'dark' ? 'light' : 'dark';
|
||||
localStorage.setItem('hdx-dev-portal-theme', next);
|
||||
applyTheme(next);
|
||||
}
|
||||
|
||||
// Apply immediately to avoid flash
|
||||
applyTheme(getPreferredTheme());
|
||||
|
||||
// Listen for OS theme changes (only when no explicit preference saved)
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: light)')
|
||||
.addEventListener('change', () => {
|
||||
if (!localStorage.getItem('hdx-dev-portal-theme')) {
|
||||
applyTheme(getPreferredTheme());
|
||||
}
|
||||
});
|
||||
|
||||
const contentEl = document.getElementById('content');
|
||||
const historyEl = document.getElementById('history-content');
|
||||
const errorEl = document.getElementById('error-banner');
|
||||
|
|
|
|||
|
|
@ -15,6 +15,36 @@
|
|||
--orange: #db6d28;
|
||||
--log-bg: #141517; /* dark-8 */
|
||||
--hover: #25262b; /* dark-6: color-bg-hover */
|
||||
--log-text: #c9d1d9;
|
||||
--row-hover: rgba(88, 166, 255, 0.05);
|
||||
--row-active: rgba(88, 166, 255, 0.1);
|
||||
--table-border: rgba(48, 54, 61, 0.5);
|
||||
--modal-backdrop: rgba(0, 0, 0, 0.6);
|
||||
--hover-overlay: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
:root[data-theme='light'] {
|
||||
--bg: #ffffff;
|
||||
--card-bg: #f8f9fa;
|
||||
--card-surface: #f1f3f5;
|
||||
--border: #dee2e6;
|
||||
--border-emphasis: #ced4da;
|
||||
--text: #212529;
|
||||
--text-muted: #868e96;
|
||||
--accent: #12b886;
|
||||
--accent-hover: #0ca678;
|
||||
--green: #12b886;
|
||||
--red: #e03131;
|
||||
--yellow: #f08c00;
|
||||
--orange: #e8590c;
|
||||
--log-bg: #f8f9fa;
|
||||
--hover: #e9ecef;
|
||||
--log-text: #212529;
|
||||
--row-hover: rgba(0, 0, 0, 0.03);
|
||||
--row-active: rgba(0, 0, 0, 0.06);
|
||||
--table-border: rgba(0, 0, 0, 0.08);
|
||||
--modal-backdrop: rgba(0, 0, 0, 0.3);
|
||||
--hover-overlay: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
@ -120,7 +150,7 @@ body {
|
|||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
background: var(--bg);
|
||||
color: #c9d1d9;
|
||||
color: var(--log-text);
|
||||
}
|
||||
|
||||
.log-content .log-line {
|
||||
|
|
@ -166,6 +196,12 @@ body {
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header .header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.header .status {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
|
|
@ -329,7 +365,7 @@ body {
|
|||
.services-table td {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid rgba(48, 54, 61, 0.5);
|
||||
border-bottom: 1px solid var(--table-border);
|
||||
}
|
||||
|
||||
.services-table tr:last-child td {
|
||||
|
|
@ -342,11 +378,11 @@ body {
|
|||
}
|
||||
|
||||
.services-table tr.clickable:hover {
|
||||
background: rgba(88, 166, 255, 0.05);
|
||||
background: var(--row-hover);
|
||||
}
|
||||
|
||||
.services-table tr.active {
|
||||
background: rgba(88, 166, 255, 0.1);
|
||||
background: var(--row-active);
|
||||
}
|
||||
|
||||
.service-name {
|
||||
|
|
@ -517,7 +553,7 @@ body {
|
|||
|
||||
.history-toggle-btn:hover {
|
||||
color: var(--text);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
background: var(--hover-overlay);
|
||||
}
|
||||
|
||||
.history-card-body {
|
||||
|
|
@ -543,7 +579,7 @@ body {
|
|||
justify-content: space-between;
|
||||
padding: 12px 20px;
|
||||
background: var(--card-surface);
|
||||
border-bottom: 1px solid rgba(48, 54, 61, 0.5);
|
||||
border-bottom: 1px solid var(--table-border);
|
||||
}
|
||||
|
||||
.history-entry-header .history-meta {
|
||||
|
|
@ -595,11 +631,11 @@ body {
|
|||
}
|
||||
|
||||
.file-item:hover {
|
||||
background: rgba(88, 166, 255, 0.05);
|
||||
background: var(--row-hover);
|
||||
}
|
||||
|
||||
.file-item.active {
|
||||
background: rgba(88, 166, 255, 0.1);
|
||||
background: var(--row-active);
|
||||
}
|
||||
|
||||
.file-name {
|
||||
|
|
@ -660,7 +696,7 @@ body {
|
|||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
background: var(--modal-backdrop);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -735,3 +771,29 @@ body {
|
|||
.modal-danger:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* --- Theme toggle --- */
|
||||
.theme-toggle {
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
color: var(--text);
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.theme-toggle svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue