Add user defined system message

This commit is contained in:
dwrz
2026-05-28 00:53:08 +00:00
parent a06833bb8b
commit 9ea4005e96
26 changed files with 652 additions and 222 deletions

View File

@@ -3,6 +3,7 @@ const ICONS_URL = '/static/icons.svg';
const MODELS_ENDPOINT = '/v1/models';
const MODEL_KEY = 'odidere_model';
const STORAGE_KEY = 'odidere_history';
const SYSTEM_MESSAGE_KEY = 'odidere_system_message';
const VOICES_ENDPOINT = '/v1/voices';
const VOICE_KEY = 'odidere_voice';
@@ -42,6 +43,15 @@ class Odidere {
this.$voice = document.getElementById('voice');
this.$mute = document.getElementById('mute');
// Settings modal
this.$settings = document.getElementById('settings');
this.$settingsOverlay = document.getElementById('settings-overlay');
this.$settingsClose = document.getElementById('settings-close');
this.$settingsTabs = document.querySelectorAll('.settings-tab');
this.$settingsPanels = document.querySelectorAll('.settings-tab-panel');
this.$systemMessageInput = document.getElementById('system-message-input');
this.$saveSystemMessage = document.getElementById('save-system-message');
// Templates
this.$tplAssistantMessage = document.getElementById(
'tpl-assistant-message',
@@ -151,6 +161,28 @@ class Odidere {
});
// Mute button
this.$mute.addEventListener('click', () => this.#toggleMute());
// Settings modal
this.$settings.addEventListener('click', () => this.openSettings());
this.$settingsClose.addEventListener('click', () => this.closeSettings());
this.$settingsOverlay.addEventListener('click', (e) => {
if (e.target === this.$settingsOverlay) this.closeSettings();
});
this.document.addEventListener('keydown', (e) => {
if (
e.key === 'Escape' &&
this.$settingsOverlay.classList.contains('open')
) {
e.preventDefault();
this.closeSettings();
}
});
this.$settingsTabs.forEach(($tab) => {
$tab.addEventListener('click', () => this.#switchTab($tab.dataset.tab));
});
this.$saveSystemMessage.addEventListener('click', () =>
this.#saveSystemMessage(),
);
}
/**
@@ -557,6 +589,10 @@ class Odidere {
voice: this.$voice.value,
model: this.$model.value,
};
const systemMessage = localStorage.getItem(SYSTEM_MESSAGE_KEY);
if (systemMessage) {
payload.system_message = systemMessage;
}
if (audio) {
payload.audio = await this.#toBase64(audio);
}
@@ -674,7 +710,11 @@ class Odidere {
}
// Collect tool results and render once all have arrived.
if (message.role === 'tool' && pendingTools && pendingTools.assistant) {
if (
message.role === 'tool' &&
pendingTools &&
pendingTools.assistant
) {
pendingTools.results.push(message);
// Add to history (server needs it) but don't render yet.
this.#appendHistory([message]);
@@ -849,6 +889,67 @@ class Odidere {
}
}
/**
* openSettings opens the settings modal and loads current values.
*/
openSettings() {
this.$settingsOverlay.classList.add('open');
this.#loadSystemMessage();
this.#switchTab('model-voice');
// Focus the close button for accessibility.
this.$settingsClose.focus();
}
/**
* closeSettings closes the settings modal and restores focus.
*/
closeSettings() {
this.$settingsOverlay.classList.remove('open');
this.$settings.focus();
}
/**
* #switchTab switches the active tab in the settings modal.
* @param {string} tabName
*/
#switchTab(tabName) {
this.$settingsTabs.forEach(($tab) => {
const isActive = $tab.dataset.tab === tabName;
$tab.classList.toggle('active', isActive);
$tab.setAttribute('aria-selected', String(isActive));
});
this.$settingsPanels.forEach(($panel) => {
const isActive = $panel.dataset.tabPanel === tabName;
$panel.classList.toggle('active', isActive);
$panel.hidden = !isActive;
});
}
/**
* #saveSystemMessage saves the textarea value to localStorage.
*/
#saveSystemMessage() {
const value = this.$systemMessageInput.value;
localStorage.setItem(SYSTEM_MESSAGE_KEY, value);
// Brief visual feedback on the save button.
this.$saveSystemMessage.replaceChildren(this.#icon('check'));
this.$saveSystemMessage.classList.add('settings-save-btn--success');
setTimeout(() => {
this.$saveSystemMessage.textContent = 'Save';
this.$saveSystemMessage.classList.remove('settings-save-btn--success');
}, 1500);
}
/**
* #loadSystemMessage reads from localStorage and populates the textarea.
*/
#loadSystemMessage() {
const stored = localStorage.getItem(SYSTEM_MESSAGE_KEY);
this.$systemMessageInput.value = stored || '';
}
// ====================
// RENDER: SELECTS
// ====================