From afae2af85b1fefaa8204184f22d82a23f3bb253e Mon Sep 17 00:00:00 2001 From: Storme-bit Date: Fri, 17 Apr 2026 23:18:48 -0700 Subject: [PATCH] memory settings implementation --- packages/chat-client/src/api/orchestration.js | 16 +++ .../src/components/SettingsView.jsx | 120 +++++++++++------- packages/chat-client/src/hooks/useSettings.js | 25 ++++ 3 files changed, 116 insertions(+), 45 deletions(-) create mode 100644 packages/chat-client/src/hooks/useSettings.js diff --git a/packages/chat-client/src/api/orchestration.js b/packages/chat-client/src/api/orchestration.js index eba2ade..d571da0 100644 --- a/packages/chat-client/src/api/orchestration.js +++ b/packages/chat-client/src/api/orchestration.js @@ -172,4 +172,20 @@ export async function getEpisodes({ limit = API_DEFAULTS.EPISODE_LIMIT, offset = export async function deleteEpisode(id) { const res = await fetch(`${BASE_URL}/episodes/${id}`, { method: 'DELETE' }); if (!res.ok) throw new Error(`Failed to delete episode: ${res.status}`); +} + +export async function getSettings() { + const res = await fetch(`${BASE_URL}/settings`); + if (!res.ok) throw new Error(`Failed to fetch settings: ${res.status}`); + return res.json(); +} + +export async function updateSettings(updates) { + const res = await fetch(`${BASE_URL}/settings`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(updates), + }); + if (!res.ok) throw new Error(`Failed to update settings: ${res.status}`); + return res.json(); } \ No newline at end of file diff --git a/packages/chat-client/src/components/SettingsView.jsx b/packages/chat-client/src/components/SettingsView.jsx index b2fdc03..c07a935 100644 --- a/packages/chat-client/src/components/SettingsView.jsx +++ b/packages/chat-client/src/components/SettingsView.jsx @@ -1,17 +1,17 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; +import { useSettings } from '../hooks/useSettings'; export default function SettingsView({ onNavigate }) { + const { settings, saveSetting, saving } = useSettings(); + return (
- - {/* Header */}
Settings
- {/* Memory */} onNavigate('memory')}>Open →} /> - } - /> - } + value={settings?.recentEpisodeLimit} + min={1} max={20} + onSave={val => saveSetting('recentEpisodeLimit', val)} + saving={saving} + /> + saveSetting('semanticLimit', val)} + saving={saving} + /> + saveSetting('scoreThreshold', val)} + saving={saving} /> - {/* Models */} - } - /> - } - /> - } - /> + } /> + } /> + } /> - {/* About */} - } - /> - v0.1.0} - /> + } /> + v0.1.0} /> - {/* Appearance */} - } - /> + } />
@@ -96,6 +83,49 @@ function SettingsSection({ title, children }) { ); } +function NumberSetting({ label, description, value, min, max, step = 1, onSave, saving }) { + const [local, setLocal] = useState(value ?? ''); + const isDirty = local !== '' && Number(local) !== value; + + // Sync when settings load from API + useEffect(() => { + if (value !== undefined) setLocal(value); + }, [value]); + + return ( + + setLocal(e.target.value)} + style={{ + width: '64px', padding: '5px 8px', textAlign: 'center', + background: 'var(--bg-elevated)', border: '1px solid var(--border)', + borderRadius: 'var(--radius-md)', color: 'var(--text-primary)', + fontSize: '13px', outline: 'none', + }} + /> + {isDirty && ( + + )} +
+ } + /> + ); +} + function SettingsRow({ label, description, action }) { return (
{ + getSettings().then(setSettings).catch(console.error); + }, []); + + async function saveSetting(key, value) { + setSaving(true); + try { + const updated = await updateSettings({ [key]: value }); + setSettings(updated); + } catch (err) { + console.error('[useSettings] Save failed:', err.message); + } finally { + setSaving(false); + } + } + + return { settings, saveSetting, saving }; +} \ No newline at end of file