2025-08-22 23:42:34 +02:00
|
|
|
import React, { useState, useEffect } from 'react';
|
|
|
|
|
|
2026-03-20 08:15:59 +01:00
|
|
|
const BACKEND_API_URL_KEY = 'backendApiUrl';
|
|
|
|
|
const OLLAMA_API_URL_KEY = 'ollamaApiUrl';
|
2025-08-22 23:42:34 +02:00
|
|
|
const MODEL_KEY = 'chatModel';
|
2025-08-23 16:45:46 +02:00
|
|
|
const STREAM_KEY = 'streamOutput';
|
2026-03-20 08:15:59 +01:00
|
|
|
const DEFAULT_BACKEND_API_URL = 'http://127.0.0.1:8000';
|
|
|
|
|
const DEFAULT_OLLAMA_API_URL = 'http://127.0.0.1:11434';
|
|
|
|
|
|
|
|
|
|
function resolveBackendApiUrl(settings) {
|
|
|
|
|
return settings.backendApiUrl || settings.ollamaApiUrl || DEFAULT_BACKEND_API_URL;
|
|
|
|
|
}
|
2025-08-22 23:42:34 +02:00
|
|
|
|
2025-08-23 16:45:46 +02:00
|
|
|
export default function GeneralSettings({ onModelChange, onStreamOutputChange }) {
|
2026-03-20 08:15:59 +01:00
|
|
|
const [backendApiUrl, setBackendApiUrl] = useState('');
|
2025-08-22 23:42:34 +02:00
|
|
|
const [ollamaApiUrl, setOllamaApiUrl] = useState('');
|
|
|
|
|
const [models, setModels] = useState([]);
|
|
|
|
|
const [selectedModel, setSelectedModel] = useState('');
|
2025-08-23 16:45:46 +02:00
|
|
|
const [streamOutput, setStreamOutput] = useState(false);
|
2025-08-22 23:42:34 +02:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
window.electronAPI.getSettings().then(settings => {
|
2026-03-20 08:15:59 +01:00
|
|
|
setBackendApiUrl(resolveBackendApiUrl(settings));
|
2025-08-22 23:42:34 +02:00
|
|
|
setOllamaApiUrl(settings.ollamaApiUrl);
|
|
|
|
|
setSelectedModel(settings.chatModel || '');
|
2025-08-23 16:45:46 +02:00
|
|
|
setStreamOutput(settings.streamOutput || false);
|
2025-08-22 23:42:34 +02:00
|
|
|
});
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-03-20 08:15:59 +01:00
|
|
|
if (backendApiUrl) {
|
|
|
|
|
fetch(backendApiUrl + '/models')
|
2025-08-22 23:42:34 +02:00
|
|
|
.then(r => r.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
const names = data.models?.map(m => m.name) || [];
|
|
|
|
|
setModels(names);
|
|
|
|
|
// If no model is selected or the selected model is no longer available, select the first one
|
|
|
|
|
if (!selectedModel || !names.includes(selectedModel)) {
|
|
|
|
|
const defaultModel = names[0] || '';
|
|
|
|
|
setSelectedModel(defaultModel);
|
|
|
|
|
window.electronAPI.setSetting(MODEL_KEY, defaultModel);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(err => console.error('Failed to load models', err));
|
|
|
|
|
}
|
2026-03-20 08:15:59 +01:00
|
|
|
}, [backendApiUrl, selectedModel]); // Depend on selectedModel to re-evaluate default selection
|
|
|
|
|
|
|
|
|
|
const handleBackendUrlChange = (e) => {
|
|
|
|
|
const newUrl = e.target.value;
|
|
|
|
|
setBackendApiUrl(newUrl);
|
|
|
|
|
window.electronAPI.setSetting(BACKEND_API_URL_KEY, newUrl);
|
|
|
|
|
};
|
2025-08-22 23:42:34 +02:00
|
|
|
|
2026-03-20 08:15:59 +01:00
|
|
|
const handleOllamaUrlChange = (e) => {
|
2025-08-22 23:42:34 +02:00
|
|
|
const newUrl = e.target.value;
|
|
|
|
|
setOllamaApiUrl(newUrl);
|
2026-03-20 08:15:59 +01:00
|
|
|
window.electronAPI.setSetting(OLLAMA_API_URL_KEY, newUrl);
|
2025-08-22 23:42:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleModelChange = (e) => {
|
|
|
|
|
const newModel = e.target.value;
|
|
|
|
|
setSelectedModel(newModel);
|
|
|
|
|
window.electronAPI.setSetting(MODEL_KEY, newModel);
|
2025-08-23 16:45:46 +02:00
|
|
|
if (onModelChange) {
|
|
|
|
|
onModelChange(newModel);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleStreamToggle = () => {
|
|
|
|
|
const newStreamValue = !streamOutput;
|
|
|
|
|
setStreamOutput(newStreamValue);
|
|
|
|
|
window.electronAPI.setSetting(STREAM_KEY, newStreamValue);
|
|
|
|
|
if (onStreamOutputChange) {
|
|
|
|
|
onStreamOutputChange(newStreamValue);
|
|
|
|
|
}
|
2025-08-22 23:42:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="settings-content-panel">
|
|
|
|
|
<div className="setting-section">
|
2026-03-20 08:15:59 +01:00
|
|
|
<h3>Heimgeist Backend URL</h3>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
className="input"
|
|
|
|
|
value={backendApiUrl}
|
|
|
|
|
onChange={handleBackendUrlChange}
|
|
|
|
|
placeholder={`e.g., ${DEFAULT_BACKEND_API_URL}`}
|
|
|
|
|
/>
|
|
|
|
|
<p className="setting-description">Internal UI requests like chats, sessions, and databases go to this URL.</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="setting-section">
|
|
|
|
|
<h3>Ollama URL</h3>
|
2025-08-22 23:42:34 +02:00
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
className="input"
|
|
|
|
|
value={ollamaApiUrl}
|
2026-03-20 08:15:59 +01:00
|
|
|
onChange={handleOllamaUrlChange}
|
|
|
|
|
placeholder={`e.g., ${DEFAULT_OLLAMA_API_URL}`}
|
2025-08-22 23:42:34 +02:00
|
|
|
/>
|
2026-03-20 08:15:59 +01:00
|
|
|
<p className="setting-description">Heimgeist uses this URL to talk to Ollama for models and chat generation.</p>
|
2025-08-22 23:42:34 +02:00
|
|
|
</div>
|
|
|
|
|
<div className="setting-section">
|
|
|
|
|
<h3>Chat Model</h3>
|
|
|
|
|
<select
|
|
|
|
|
className="select"
|
|
|
|
|
value={selectedModel}
|
|
|
|
|
onChange={handleModelChange}
|
|
|
|
|
>
|
|
|
|
|
{models.length === 0 && <option>— No models available —</option>}
|
|
|
|
|
{models.map(m => <option key={m} value={m}>{m}</option>)}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
2025-08-23 16:45:46 +02:00
|
|
|
<div className="setting-section">
|
|
|
|
|
<h3>Stream Output</h3>
|
|
|
|
|
<label className="toggle-switch">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={streamOutput}
|
|
|
|
|
onChange={handleStreamToggle}
|
|
|
|
|
/>
|
|
|
|
|
<span className="slider"></span>
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
2025-08-22 23:42:34 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|