diff --git a/src/frontend/index.html b/src/frontend/index.html
index 0ade680..4469aae 100644
--- a/src/frontend/index.html
+++ b/src/frontend/index.html
@@ -11,6 +11,16 @@
+
@@ -36,6 +46,112 @@
let ws;
let term;
+ let currentBrightness = 0;
+
+ function getThemeForBrightness(brightness) {
+ const isDark = brightness < 50;
+ const bgValue = Math.round((brightness / 100) * 255);
+ const bgColor = `rgb(${bgValue}, ${bgValue}, ${bgValue})`;
+
+ const headerBg = isDark ? 'rgb(10, 10, 10)' : 'rgb(245, 245, 245)';
+ const statusBg = isDark ? 'rgb(10, 10, 10)' : 'rgb(240, 240, 240)';
+ const textColor = isDark ? 'rgb(136, 136, 136)' : 'rgb(80, 80, 80)';
+ const borderColor = isDark ? 'rgb(26, 26, 26)' : 'rgb(220, 220, 220)';
+
+ document.documentElement.style.setProperty('--bg-primary', bgColor);
+ document.documentElement.style.setProperty('--bg-secondary', isDark ? 'rgb(10, 10, 10)' : 'rgb(245, 245, 245)');
+ document.documentElement.style.setProperty('--text-secondary', textColor);
+ document.documentElement.style.setProperty('--border', borderColor);
+ document.documentElement.style.setProperty('--bg-tertiary', isDark ? 'rgb(30, 30, 30)' : 'rgb(230, 230, 230)');
+
+ const terminalTheme = isDark ? {
+ background: bgColor,
+ foreground: '#cccccc',
+ cursor: '#ff6b6b',
+ cursorAccent: bgColor,
+ selection: 'rgba(100, 100, 100, 0.3)',
+ black: '#000000',
+ red: '#d35252',
+ green: '#859a5f',
+ yellow: '#d64c4c',
+ blue: '#7278b2',
+ magenta: '#9c6cb3',
+ cyan: '#6fb3b8',
+ white: '#cccccc',
+ brightBlack: '#666666',
+ brightRed: '#d35252',
+ brightGreen: '#859a5f',
+ brightYellow: '#d64c4c',
+ brightBlue: '#7278b2',
+ brightMagenta: '#9c6cb3',
+ brightCyan: '#6fb3b8',
+ brightWhite: '#ffffff'
+ } : {
+ background: bgColor,
+ foreground: '#1a1a1a',
+ cursor: '#d63031',
+ cursorAccent: bgColor,
+ selection: 'rgba(0, 0, 0, 0.2)',
+ black: '#000000',
+ red: '#c0392b',
+ green: '#27ae60',
+ yellow: '#d63031',
+ blue: '#2980b9',
+ magenta: '#8e44ad',
+ cyan: '#16a085',
+ white: '#1a1a1a',
+ brightBlack: '#555555',
+ brightRed: '#c0392b',
+ brightGreen: '#27ae60',
+ brightYellow: '#d63031',
+ brightBlue: '#2980b9',
+ brightMagenta: '#8e44ad',
+ brightCyan: '#16a085',
+ brightWhite: '#ffffff'
+ };
+
+ if (term) {
+ term.options.theme = terminalTheme;
+ }
+
+ return { bgColor, headerBg, statusBg, textColor, borderColor };
+ }
+
+ function updateBrightness(brightness) {
+ currentBrightness = brightness;
+ const { bgColor, headerBg, statusBg, borderColor } = getThemeForBrightness(brightness);
+ document.body.style.background = bgColor;
+ document.querySelector('.terminal-header').style.background = headerBg;
+ document.querySelector('.terminal-header').style.borderBottomColor = borderColor;
+ document.querySelector('.terminal-status').style.background = statusBg;
+ document.querySelector('.terminal-status').style.borderTopColor = borderColor;
+
+ const sliderThumb = document.querySelector('#brightness-slider::-webkit-slider-thumb');
+ if (sliderThumb) {
+ sliderThumb.style.background = brightness > 50 ? '#1a1a1a' : '#ffffff';
+ }
+ }
+
+ function setupBrightnessControl() {
+ const themeToggle = document.getElementById('theme-toggle');
+ const brightnessPopup = document.getElementById('brightness-popup');
+ const brightnessSlider = document.getElementById('brightness-slider');
+
+ themeToggle.addEventListener('click', (e) => {
+ e.stopPropagation();
+ brightnessPopup.classList.toggle('hidden');
+ });
+
+ brightnessSlider.addEventListener('input', (e) => {
+ updateBrightness(parseInt(e.target.value));
+ });
+
+ document.addEventListener('click', (e) => {
+ if (!brightnessPopup.contains(e.target) && e.target !== themeToggle) {
+ brightnessPopup.classList.add('hidden');
+ }
+ });
+ }
async function initGhosttyVT() {
const wasmUrl = urlParams.get('wasm') || 'ghostty-vt.wasm';
@@ -168,6 +284,7 @@
term.focus();
}
+ setupBrightnessControl();
connectTerminal();
initGhosttyVT();
diff --git a/src/frontend/styles.css b/src/frontend/styles.css
index 2fd52e5..4bc27e6 100644
--- a/src/frontend/styles.css
+++ b/src/frontend/styles.css
@@ -158,4 +158,82 @@ html, body {
#terminal {
padding: 4px;
}
+}
+
+#theme-toggle {
+ width: 28px;
+ height: 28px;
+ border: none;
+ border-radius: 6px;
+ background: transparent;
+ color: var(--text-secondary);
+ cursor: pointer;
+ font-size: 16px;
+ transition: color 0.2s, background 0.2s;
+ -webkit-app-region: no-drag;
+}
+
+#theme-toggle:hover {
+ color: var(--text-primary);
+ background: var(--bg-tertiary);
+}
+
+.brightness-popup {
+ position: absolute;
+ top: 44px;
+ right: 16px;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ padding: 12px 16px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+ z-index: 1000;
+}
+
+.brightness-popup.hidden {
+ display: none;
+}
+
+.brightness-slider-container {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.brightness-icon {
+ font-size: 14px;
+ color: var(--text-secondary);
+}
+
+#brightness-slider {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 150px;
+ height: 6px;
+ border-radius: 3px;
+ background: linear-gradient(to right, #000000, #ffffff);
+ outline: none;
+ cursor: pointer;
+}
+
+#brightness-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: #ffffff;
+ border: 2px solid #333;
+ cursor: pointer;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
+}
+
+#brightness-slider::-moz-range-thumb {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: #ffffff;
+ border: 2px solid #333;
+ cursor: pointer;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
\ No newline at end of file