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 @@
XCTerminal
+
+ +
+
+
@@ -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