Files
Heimgeist/src/InterfaceSettings.jsx

151 lines
4.7 KiB
React
Raw Normal View History

import React, { useEffect, useState } from 'react'
import { colorSchemes, applyColorScheme } from './colorSchemes'
2025-08-22 23:42:34 +02:00
const COLOR_SCHEME_KEY = 'colorScheme'
const STREAM_KEY = 'streamOutput'
const UI_SCALE_KEY = 'uiScale'
const OPEN_DEVTOOLS_ON_STARTUP_KEY = 'openDevToolsOnStartup'
const DEFAULT_UI_SCALE = 1
const MIN_UI_SCALE = 0.7
const MAX_UI_SCALE = 1.3
const UI_SCALE_STEP = 0.05
function normalizeUiScale(value) {
const numericValue = Number(value)
if (!Number.isFinite(numericValue)) {
return DEFAULT_UI_SCALE
}
return Math.min(MAX_UI_SCALE, Math.max(MIN_UI_SCALE, Math.round(numericValue * 100) / 100))
}
2025-08-22 23:42:34 +02:00
export default function InterfaceSettings({
streamOutput = false,
onStreamOutputChange,
}) {
const [selectedColorScheme, setSelectedColorScheme] = useState('Default')
const [uiScale, setUiScale] = useState(DEFAULT_UI_SCALE)
const [openDevToolsOnStartup, setOpenDevToolsOnStartup] = useState(false)
2025-08-22 23:42:34 +02:00
useEffect(() => {
window.electronAPI.getSettings().then(settings => {
const schemeName = settings.colorScheme || 'Default'
setSelectedColorScheme(schemeName)
setUiScale(normalizeUiScale(settings.uiScale))
setOpenDevToolsOnStartup(settings.openDevToolsOnStartup === true)
applyColorScheme(schemeName)
})
}, [])
2025-08-22 23:42:34 +02:00
useEffect(() => {
applyColorScheme(selectedColorScheme)
}, [selectedColorScheme])
const handleColorSchemeChange = (event) => {
const newScheme = event.target.value
setSelectedColorScheme(newScheme)
window.electronAPI.setSetting(COLOR_SCHEME_KEY, newScheme)
}
2025-08-22 23:42:34 +02:00
const persistUiScale = (value) => {
const nextScale = normalizeUiScale(value)
setUiScale(nextScale)
window.electronAPI.setSetting(UI_SCALE_KEY, nextScale)
}
const handleUiScaleChange = (event) => {
persistUiScale(event.target.value)
}
const handleUiScaleReset = () => {
persistUiScale(DEFAULT_UI_SCALE)
}
2025-08-22 23:42:34 +02:00
const handleOpenDevToolsOnStartupToggle = () => {
const nextValue = !openDevToolsOnStartup
setOpenDevToolsOnStartup(nextValue)
window.electronAPI.setSetting(OPEN_DEVTOOLS_ON_STARTUP_KEY, nextValue)
}
const handleStreamOutputToggle = () => {
const nextValue = !streamOutput
window.electronAPI.setSetting(STREAM_KEY, nextValue)
if (onStreamOutputChange) {
onStreamOutputChange(nextValue)
}
}
2025-08-22 23:42:34 +02:00
return (
<div className="settings-content-panel">
<div className="setting-section">
<h3>Color Scheme</h3>
<select
className="select"
value={selectedColorScheme}
onChange={handleColorSchemeChange}
>
{Object.keys(colorSchemes).map((schemeName) => (
<option key={schemeName} value={schemeName}>
{schemeName}
</option>
))}
</select>
</div>
<div className="setting-section">
<h3>UI Scale</h3>
<div className="setting-control-row">
<input
type="range"
className="range-input"
min={MIN_UI_SCALE}
max={MAX_UI_SCALE}
step={UI_SCALE_STEP}
value={uiScale}
onChange={handleUiScaleChange}
/>
<span className="setting-value">{Math.round(uiScale * 100)}%</span>
<button
type="button"
className="button"
onClick={handleUiScaleReset}
disabled={uiScale === DEFAULT_UI_SCALE}
>
Reset
</button>
</div>
<p className="setting-description">
Scales the whole interface, including fonts, spacing, and controls. 100% is the default size.
</p>
</div>
<div className="setting-section">
<h3>Open DevTools on Startup</h3>
<label className="toggle-switch">
<input
type="checkbox"
checked={openDevToolsOnStartup}
onChange={handleOpenDevToolsOnStartupToggle}
/>
<span className="slider"></span>
</label>
<p className="setting-description">
Only applies in Electron development mode. When enabled, Heimgeist opens detached DevTools for new windows and updates currently open windows right away.
</p>
</div>
<div className="setting-section">
<h3>Stream Output</h3>
<label className="toggle-switch">
<input
type="checkbox"
checked={streamOutput}
onChange={handleStreamOutputToggle}
/>
<span className="slider"></span>
</label>
<p className="setting-description">
Shows assistant replies token by token while they are generating instead of waiting for the full message to finish first.
</p>
</div>
2025-08-22 23:42:34 +02:00
</div>
)
2025-08-22 23:42:34 +02:00
}