Add Dreamer section to settings — Dream Now button, mode selector, lucid task input, last dream status

This commit is contained in:
2026-04-26 21:20:58 -04:00
parent bff439b1e9
commit 6cd91b2f4b
2 changed files with 68 additions and 1 deletions
+56 -1
View File
@@ -3,7 +3,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useStore } from '@/lib/store'; import { useStore } from '@/lib/store';
import { api, auth } from '@/lib/api'; import { api, auth } from '@/lib/api';
import type { Status } from '@/lib/api'; import type { Status, DreamerStatus } from '@/lib/api';
export default function SettingsPanel() { export default function SettingsPanel() {
const { settingsOpen, setSettingsOpen, settings, setSettings } = useStore(); const { settingsOpen, setSettingsOpen, settings, setSettings } = useStore();
@@ -11,10 +11,16 @@ export default function SettingsPanel() {
const [memory, setMemory] = useState(''); const [memory, setMemory] = useState('');
const [editingMemory, setEditingMemory] = useState(false); const [editingMemory, setEditingMemory] = useState(false);
const [reindexing, setReindexing] = useState(false); const [reindexing, setReindexing] = useState(false);
const [dreamerStatus, setDreamerStatus] = useState<DreamerStatus | null>(null);
const [dreamMode, setDreamMode] = useState('nrem');
const [dreamTask, setDreamTask] = useState('');
const [dreaming, setDreaming] = useState(false);
const [dreamStarted, setDreamStarted] = useState(false);
useEffect(() => { useEffect(() => {
if (!settingsOpen) return; if (!settingsOpen) return;
api.getStatus().then(setStatus).catch(console.error); api.getStatus().then(setStatus).catch(console.error);
api.getDreamerStatus().then(setDreamerStatus).catch(console.error);
api.getMemory().then(d => setMemory(d.content)).catch(console.error); api.getMemory().then(d => setMemory(d.content)).catch(console.error);
}, [settingsOpen]); }, [settingsOpen]);
@@ -29,6 +35,18 @@ export default function SettingsPanel() {
setEditingMemory(false); setEditingMemory(false);
} }
async function triggerDream() {
setDreaming(true);
setDreamStarted(false);
const result = await api.runDreamer(dreamMode, dreamTask || undefined);
setDreaming(false);
if (result.started) {
setDreamStarted(true);
setTimeout(() => setDreamStarted(false), 4000);
setTimeout(() => api.getDreamerStatus().then(setDreamerStatus), 5000);
}
}
async function triggerReindex() { async function triggerReindex() {
setReindexing(true); setReindexing(true);
await api.reindex(); await api.reindex();
@@ -188,6 +206,43 @@ export default function SettingsPanel() {
</Row> </Row>
</Section> </Section>
{/* Dreamer */}
<Section title="Dreamer">
<div className="grid grid-cols-2 gap-2 mb-3">
<StatCard number={dreamerStatus?.last_mode?.toUpperCase() || '—'} label="last mode" />
<StatCard number={dreamerStatus?.last_dream || '—'} label="last dream" />
</div>
<Row label="Mode">
<select
value={dreamMode}
onChange={e => setDreamMode(e.target.value)}
className="rounded-md px-2 py-1 text-sm"
style={{ background: 'var(--bg3)', border: '1px solid var(--border2)', color: 'var(--text)', fontFamily: 'var(--font-sans)' }}
>
<option value="nrem">NREM consolidation</option>
<option value="early-rem">Early REM personal</option>
<option value="late-rem">Late REM associative</option>
<option value="lucid">Lucid targeted</option>
</select>
</Row>
{dreamMode === 'lucid' && (
<div className="py-2" style={{ borderBottom: '1px solid var(--border)' }}>
<input
value={dreamTask}
onChange={e => setDreamTask(e.target.value)}
placeholder="What question should the dreamer pursue?"
className="w-full rounded-md px-3 py-2 text-xs"
style={{ background: 'var(--bg3)', border: '1px solid var(--border2)', color: 'var(--text)', fontFamily: 'var(--font-sans)' }}
/>
</div>
)}
<Row label="Dream now" desc={dreamStarted ? 'Dream started — check Journal/Dreams/' : 'Run the dreamer immediately'}>
<SBtn primary onClick={triggerDream} disabled={dreaming || (dreamMode === 'lucid' && !dreamTask.trim())}>
{dreaming ? 'Starting...' : dreamStarted ? 'Started ✓' : 'Dream'}
</SBtn>
</Row>
</Section>
{/* System */} {/* System */}
<Section title="System"> <Section title="System">
<StatusRow label="Aaron AI service" value={status?.aaron_ai || 'unknown'} ok={status?.aaron_ai === 'running'} /> <StatusRow label="Aaron AI service" value={status?.aaron_ai || 'unknown'} ok={status?.aaron_ai === 'running'} />
+12
View File
@@ -32,6 +32,12 @@ export const api = {
request<{ saved: boolean }>('/memory', { method: 'POST', body: JSON.stringify({ content }) }), request<{ saved: boolean }>('/memory', { method: 'POST', body: JSON.stringify({ content }) }),
getStatus: () => request<Status>('/status'), getStatus: () => request<Status>('/status'),
reindex: () => request<{ started: boolean }>('/reindex', { method: 'POST' }), reindex: () => request<{ started: boolean }>('/reindex', { method: 'POST' }),
getDreamerStatus: () => request<DreamerStatus>('/dreamer/status'),
runDreamer: (mode: string, task?: string) =>
request<{ started: boolean; mode: string }>('/dreamer/run', {
method: 'POST',
body: JSON.stringify({ mode, task }),
}),
transcribe: async (audio: Blob): Promise<{ text: string }> => { transcribe: async (audio: Blob): Promise<{ text: string }> => {
const form = new FormData(); const form = new FormData();
form.append('audio', audio, 'recording.webm'); form.append('audio', audio, 'recording.webm');
@@ -86,6 +92,12 @@ export interface ChatResponse {
sources: string[]; sources: string[];
conversation_id: string; conversation_id: string;
} }
export interface DreamerStatus {
last_dream: string;
last_mode: string;
last_file: string;
}
export interface Status { export interface Status {
aaron_ai: string; aaron_ai: string;
watcher: string; watcher: string;