Refactor frontend
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Storage reads and writes the browser's local cache and the saved
|
||||
* client settings. It is the only module that touches localStorage.
|
||||
* It does no rendering and sends no requests: main.js decides what
|
||||
* to cache and when, and Storage only stores and returns it.
|
||||
*
|
||||
* Keys and their contents:
|
||||
* - odidere_conversation: the selected conversation ID and its saved
|
||||
* message rows, for offline viewing and a fast start. The server
|
||||
* owns the history; this entry is a copy, replaced by every sync.
|
||||
* - odidere_system_message: the system message used when the next
|
||||
* conversation is created. It does not change a conversation that
|
||||
* already exists.
|
||||
* - odidere_provider, odidere_model, odidere_voice: selections.
|
||||
* - odidere_max_turns, odidere_reasoning_budget: compose settings.
|
||||
* - odidere_mute: whether speech playback is muted.
|
||||
*
|
||||
* The browser-only history that older versions kept under
|
||||
* odidere_history is not read and is left untouched.
|
||||
*/
|
||||
|
||||
const CONVERSATION_KEY = 'odidere_conversation';
|
||||
const SYSTEM_MESSAGE_KEY = 'odidere_system_message';
|
||||
const PROVIDER_KEY = 'odidere_provider';
|
||||
const MODEL_KEY = 'odidere_model';
|
||||
const VOICE_KEY = 'odidere_voice';
|
||||
const MAX_TURNS_KEY = 'odidere_max_turns';
|
||||
const REASONING_BUDGET_KEY = 'odidere_reasoning_budget';
|
||||
const MUTE_KEY = 'odidere_mute';
|
||||
|
||||
export class Storage {
|
||||
/**
|
||||
* loadConversation returns the cached selection: the conversation
|
||||
* ID and its message rows in saved order. Rows are stored under
|
||||
* their message IDs; the array order is the saved order.
|
||||
* @returns {{id: string, rows: Array<Object>} | null}
|
||||
*/
|
||||
loadConversation() {
|
||||
try {
|
||||
const cached = JSON.parse(localStorage.getItem(CONVERSATION_KEY));
|
||||
if (!cached?.id || !Array.isArray(cached.rows)) return null;
|
||||
return { id: cached.id, rows: cached.rows };
|
||||
} catch (error) {
|
||||
console.error('failed to load conversation cache:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* saveConversation caches the selected conversation and its rows.
|
||||
* The server owns the history; this is a local copy for offline
|
||||
* viewing. Streaming drafts are never passed to this method.
|
||||
* @param {string} id
|
||||
* @param {Array<Object>} rows
|
||||
*/
|
||||
saveConversation(id, rows) {
|
||||
try {
|
||||
localStorage.setItem(CONVERSATION_KEY, JSON.stringify({ id, rows }));
|
||||
} catch (error) {
|
||||
console.error('failed to cache conversation:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clearConversation drops the cached selection. It does not
|
||||
* change or delete the conversation on the server.
|
||||
*/
|
||||
clearConversation() {
|
||||
try {
|
||||
localStorage.removeItem(CONVERSATION_KEY);
|
||||
} catch (error) {
|
||||
console.error('failed to clear conversation cache:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// --- System message ---
|
||||
|
||||
/** @returns {string} the saved default system message, or '' */
|
||||
getSystemMessage() {
|
||||
return localStorage.getItem(SYSTEM_MESSAGE_KEY) || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* setSystemMessage stores the default system message for the next
|
||||
* conversation. It does not change any existing conversation.
|
||||
* @param {string} value
|
||||
*/
|
||||
setSystemMessage(value) {
|
||||
localStorage.setItem(SYSTEM_MESSAGE_KEY, value);
|
||||
}
|
||||
|
||||
// --- Model and voice ---
|
||||
|
||||
/** @returns {{provider: string | null, model: string | null}} */
|
||||
getModelSelection() {
|
||||
return {
|
||||
provider: localStorage.getItem(PROVIDER_KEY),
|
||||
model: localStorage.getItem(MODEL_KEY),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setModelSelection stores the selected provider and model.
|
||||
* @param {{provider: string, model: string}} selection
|
||||
*/
|
||||
setModelSelection({ provider, model }) {
|
||||
localStorage.setItem(PROVIDER_KEY, provider);
|
||||
localStorage.setItem(MODEL_KEY, model);
|
||||
}
|
||||
|
||||
/** @returns {string} the selected voice, or '' for automatic */
|
||||
getVoice() {
|
||||
return localStorage.getItem(VOICE_KEY) || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* setVoice stores the selected voice. '' selects the default.
|
||||
* @param {string} voice
|
||||
*/
|
||||
setVoice(voice) {
|
||||
localStorage.setItem(VOICE_KEY, voice);
|
||||
}
|
||||
|
||||
// --- Compose settings ---
|
||||
|
||||
/**
|
||||
* getMaxIterations returns the agent loop limit. Zero means no
|
||||
* limit: the model replies without tool calls or the run fails.
|
||||
* @returns {number}
|
||||
*/
|
||||
getMaxIterations() {
|
||||
const stored = Number(localStorage.getItem(MAX_TURNS_KEY));
|
||||
return Number.isFinite(stored) && stored > 0 ? stored : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* setMaxIterations stores the agent loop limit. Zero means no
|
||||
* limit.
|
||||
* @param {number} value
|
||||
*/
|
||||
setMaxIterations(value) {
|
||||
localStorage.setItem(MAX_TURNS_KEY, String(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* getReasoningBudget returns the llama.cpp reasoning token budget,
|
||||
* or null when unset. -1 means unlimited and 0 means off.
|
||||
* @returns {number | null}
|
||||
*/
|
||||
getReasoningBudget() {
|
||||
const stored = localStorage.getItem(REASONING_BUDGET_KEY);
|
||||
if (stored === null) return null;
|
||||
const value = Number(stored);
|
||||
return Number.isFinite(value) ? value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setReasoningBudget stores the reasoning token budget. null
|
||||
* removes the setting and leaves the choice to the server.
|
||||
* @param {number | null} value
|
||||
*/
|
||||
setReasoningBudget(value) {
|
||||
if (value === null) {
|
||||
localStorage.removeItem(REASONING_BUDGET_KEY);
|
||||
} else {
|
||||
localStorage.setItem(REASONING_BUDGET_KEY, String(value));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Mute ---
|
||||
|
||||
/** @returns {boolean} whether speech playback is muted */
|
||||
getMuted() {
|
||||
return localStorage.getItem(MUTE_KEY) === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* setMuted stores the speech mute state.
|
||||
* @param {boolean} muted
|
||||
*/
|
||||
setMuted(muted) {
|
||||
localStorage.setItem(MUTE_KEY, String(muted));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user