feat: add brightness slider with auto theme adaptation
This commit is contained in:
@@ -11,6 +11,16 @@
|
||||
<div id="terminal-container">
|
||||
<div class="terminal-header">
|
||||
<div class="terminal-title">XCTerminal</div>
|
||||
<div class="terminal-controls">
|
||||
<button id="theme-toggle" title="Adjust Brightness">☀</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="brightness-popup" class="brightness-popup hidden">
|
||||
<div class="brightness-slider-container">
|
||||
<span class="brightness-icon">🌙</span>
|
||||
<input type="range" id="brightness-slider" min="0" max="100" value="0">
|
||||
<span class="brightness-icon">☀</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="terminal"></div>
|
||||
<div class="terminal-status">
|
||||
@@ -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();
|
||||
</script>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user