/
GreyCat
/
openhuman
Обзор
Документация
Войти
/
GreyCat
/
openhuman
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
app/src/components/intelligence/MemoryWorkspace.tsx
158 строк
7 KB
Steven Enamakel
feat(theme): runtime theming — semantic tokens, Theme Studio, presets (#4130)
26 июн 2026, 00:33
Не верифицирован
26 июн 2026, 00:33
a49f2ce
Код
Авторство
О чём код?
/** * Obsidian-style graph view for the memory tree, plus controls to drive * the ingestion pipeline manually. * * ┌───────────────────────────────────────────────────────┐ * │ MemoryTreeStatusPanel (chunk counts + freshness) │ * └───────────────────────────────────────────────────────┘ * ┌───────────────────────────────────────────────────────┐ * │ MemorySourcesRegistry — unified source list │ * │ (Composio + folder + GitHub + RSS + web · per-row │ * │ Sync button, status chip, chunk count, freshness) │ * └───────────────────────────────────────────────────────┘ * ┌───────────────────────────────────────────────────────┐ * │ WhatsAppMemorySection │ * └───────────────────────────────────────────────────────┘ * ┌───────────────────────────────────────────────────────┐ * │ ModeToggle · Reset Memory · Reset Tree · Build Trees │ * │ [ View vault in Obsidian ] (shown when vault set) │ * └───────────────────────────────────────────────────────┘ * ┌───────────────────────────────────────────────────────┐ * │ Force-directed summary graph (SVG) │ * └───────────────────────────────────────────────────────┘ * * `MemorySourcesRegistry` replaces the old Composio-only `MemorySources` * panel. It auto-seeds active Composio connections as sources and lets * users add folder, GitHub repo, RSS, and web-page sources via the * Add Source dialog. * * `Build summary trees` calls `memory_tree.flush_now` which enqueues a * `flush_stale` job with `max_age_secs=0` so every L0 buffer * force-seals immediately. The seal worker runs each through the * configured cloud or local LLM and the new summary nodes appear in * the graph after the worker drains. */ import { useEffect, useState } from 'react'; import { useT } from '../../lib/i18n/I18nContext'; import type { ToastNotification } from '../../types/intelligence'; import { type GraphExportResponse, type GraphMode, memoryTreeGraphExport, } from '../../utils/tauriCommands'; import { MemoryControls } from './MemoryControls'; import { MemoryGraph } from './MemoryGraph'; import { MemorySourcesRegistry } from './MemorySourcesRegistry'; import { MemoryTreeStatusPanel } from './MemoryTreeStatusPanel'; import { SyncAuditPanel } from './SyncAuditPanel'; import { WhatsAppMemorySection } from './WhatsAppMemorySection'; interface MemoryWorkspaceProps { onToast?: (toast: Omit<ToastNotification, 'id'>) => void; } export function MemoryWorkspace({ onToast }: MemoryWorkspaceProps) { const { t } = useT(); const [graph, setGraph] = useState<GraphExportResponse | null>(null); const [error, setError] = useState<string | null>(null); const [mode, setMode] = useState<GraphMode>('tree'); const [graphVersion, setGraphVersion] = useState(0); // (Re)load the graph whenever the mode toggle flips or tree events arrive. useEffect(() => { console.debug('[ui-flow][memory-workspace] graph load: entry mode=%s v=%d', mode, graphVersion); let cancelled = false; setError(null); void (async () => { try { const resp = await memoryTreeGraphExport(mode); if (cancelled) return; console.debug( '[ui-flow][memory-workspace] graph load: exit mode=%s n=%d edges=%d', mode, resp.nodes.length, resp.edges.length ); setGraph(resp); } catch (err) { if (cancelled) return; console.error('[ui-flow][memory-workspace] graph load failed', err); setError(err instanceof Error ? err.message : String(err)); } })(); return () => { cancelled = true; }; }, [mode, graphVersion]); useEffect(() => { const onTreeDone = () => { setTimeout(() => setGraphVersion(v => v + 1), 2000); }; const onSyncDone = (e: Event) => { const data = (e as CustomEvent).detail as { stage?: string } | null; if (data?.stage === 'completed') { setTimeout(() => setGraphVersion(v => v + 1), 3000); } }; window.addEventListener('openhuman:memory-tree-completed', onTreeDone); window.addEventListener('openhuman:memory-sync-stage', onSyncDone); return () => { window.removeEventListener('openhuman:memory-tree-completed', onTreeDone); window.removeEventListener('openhuman:memory-sync-stage', onSyncDone); }; }, []); // Live refresh: re-pull the graph every 30s while this tab is mounted so it // reflects background tree growth (e.g. seal_document jobs draining as // Notion syncs) without a manual refresh. The Memory tab unmounts this // component when inactive, which clears the interval — so the poll only runs // while the tab is actually open. Ticks are skipped while the window is // backgrounded to avoid needless RPC churn; the next visible tick catches up. useEffect(() => { const GRAPH_POLL_MS = 30_000; const id = setInterval(() => { if (typeof document !== 'undefined' && document.hidden) return; console.debug('[ui-flow][memory-workspace] graph poll tick → bump version'); setGraphVersion(v => v + 1); }, GRAPH_POLL_MS); return () => clearInterval(id); }, []); return ( <div className="space-y-4" data-testid="memory-workspace"> <MemoryTreeStatusPanel onToast={onToast} /> <MemorySourcesRegistry onToast={onToast} /> <WhatsAppMemorySection /> <MemoryControls mode={mode} onModeChange={setMode} onRefresh={() => setGraphVersion(v => v + 1)} onToast={onToast} contentRootAbs={graph?.content_root_abs} /> {error ? ( <div className="rounded-lg border border-coral-200 dark:border-coral-500/30 bg-coral-50 dark:bg-coral-500/10 px-4 py-3 text-sm text-coral-800"> {t('workspace.graphLoadFailed')}: {error} </div> ) : !graph ? ( <div className="flex h-[640px] items-center justify-center rounded-lg border border-line-subtle bg-surface-muted/40 text-sm text-content-muted"> {t('workspace.loadingGraph')} </div> ) : ( <MemoryGraph nodes={graph.nodes} edges={graph.edges} mode={mode} /> )} <div className="rounded-lg border border-line-subtle bg-surface p-4"> <h3 className="mb-2 text-sm font-medium text-content-secondary"> {t('sync.auditTitle', 'Sync History')} </h3> <SyncAuditPanel /> </div> </div> ); }