diff options
| -rw-r--r-- | makima/frontend/src/components/directives/DirectiveDetail.tsx | 53 | ||||
| -rw-r--r-- | makima/frontend/src/hooks/useDirectiveMemories.ts | 122 | ||||
| -rw-r--r-- | makima/frontend/tsconfig.tsbuildinfo | 2 |
3 files changed, 69 insertions, 108 deletions
diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx index ab6ddbb..e66325a 100644 --- a/makima/frontend/src/components/directives/DirectiveDetail.tsx +++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx @@ -18,10 +18,12 @@ const CATEGORY_COLORS: Record<MemoryCategory, { text: string; border: string; bg context: { text: "text-cyan-400", border: "border-cyan-800", bg: "bg-cyan-900/20", label: "Context" }, preference: { text: "text-violet-400", border: "border-violet-800", bg: "bg-violet-900/20", label: "Preference" }, learning: { text: "text-emerald-400", border: "border-emerald-800", bg: "bg-emerald-900/20", label: "Learning" }, + issue: { text: "text-red-400", border: "border-red-800", bg: "bg-red-900/20", label: "Issue" }, + progress: { text: "text-blue-400", border: "border-blue-800", bg: "bg-blue-900/20", label: "Progress" }, other: { text: "text-[#7788aa]", border: "border-[#2a3a5a]", bg: "bg-[#1a2540]", label: "Other" }, }; -const ALL_CATEGORIES: MemoryCategory[] = ["decision", "context", "preference", "learning", "other"]; +const ALL_CATEGORIES: MemoryCategory[] = ["decision", "context", "preference", "learning", "issue", "progress", "other"]; interface DirectiveDetailProps { directive: DirectiveWithSteps; @@ -65,23 +67,22 @@ export function DirectiveDetail({ const [addingMemory, setAddingMemory] = useState(false); const [newCategory, setNewCategory] = useState<MemoryCategory>("context"); const [newContent, setNewContent] = useState(""); - const [newSource, setNewSource] = useState(""); const [confirmClear, setConfirmClear] = useState(false); const { grouped, - config: memoryConfig, loading: memoryLoading, error: memoryError, - toggleEnabled, add: addMemory, remove: removeMemory, clearAll: clearMemories, refresh: refreshMemories, } = useDirectiveMemories(directive.id); - const memoryEnabled = memoryConfig?.enabled ?? false; - const totalMemories = Object.values(grouped).reduce((sum, arr) => sum + arr.length, 0); + const totalMemories = Object.values(grouped).reduce( + (sum: number, arr: unknown[]) => sum + arr.length, + 0 + ); // Build task map from directive steps and orchestrator const taskMap = useMemo(() => { @@ -126,10 +127,8 @@ export function DirectiveDetail({ await addMemory({ category: newCategory, content: newContent.trim(), - source: newSource.trim() || undefined, }); setNewContent(""); - setNewSource(""); setAddingMemory(false); }; @@ -358,21 +357,6 @@ export function DirectiveDetail({ </span> )} </button> - <div className="flex items-center gap-2"> - {/* Enable/disable toggle */} - <button - type="button" - onClick={() => toggleEnabled(!memoryEnabled)} - className={`text-[9px] font-mono border rounded px-1.5 py-0.5 transition-colors ${ - memoryEnabled - ? "text-emerald-400 border-emerald-800 hover:text-emerald-300" - : "text-[#556677] border-[#2a3a5a] hover:text-[#7788aa]" - }`} - title={memoryEnabled ? "Disable memory" : "Enable memory"} - > - {memoryEnabled ? "ON" : "OFF"} - </button> - </div> </div> {/* Collapsible content */} @@ -389,14 +373,13 @@ export function DirectiveDetail({ ) : totalMemories === 0 ? ( <div className="text-[10px] font-mono text-[#556677] py-2"> No memory entries yet. - {!memoryEnabled && " Enable memory to start capturing entries."} </div> ) : ( /* Grouped entries */ <div className="flex flex-col gap-2"> {ALL_CATEGORIES.map((cat) => { - const entries = grouped[cat]; - if (entries.length === 0) return null; + const catEntries = grouped[cat]; + if (catEntries.length === 0) return null; const style = CATEGORY_COLORS[cat]; return ( <div key={cat}> @@ -405,11 +388,11 @@ export function DirectiveDetail({ {style.label} </span> <span className="text-[9px] font-mono text-[#556677]"> - ({entries.length}) + ({catEntries.length}) </span> </div> <div className="flex flex-col gap-1"> - {entries.map((entry) => ( + {catEntries.map((entry) => ( <div key={entry.id} className={`flex items-start gap-2 px-2 py-1.5 rounded border ${style.border} ${style.bg}`} @@ -418,11 +401,6 @@ export function DirectiveDetail({ <p className="text-[10px] font-mono text-[#c0d0e0] whitespace-pre-wrap break-words"> {entry.content} </p> - {entry.source && ( - <span className="text-[9px] font-mono text-[#556677] mt-0.5 block"> - src: {entry.source} - </span> - )} </div> <button type="button" @@ -515,13 +493,6 @@ export function DirectiveDetail({ className="w-full bg-[#1a2540] border border-[rgba(117,170,252,0.2)] rounded px-2 py-1.5 text-[10px] font-mono text-white resize-y min-h-[40px] placeholder:text-[#556677]" rows={2} /> - <input - type="text" - value={newSource} - onChange={(e) => setNewSource(e.target.value)} - placeholder="Source (optional)" - className="w-full bg-[#1a2540] border border-[rgba(117,170,252,0.2)] rounded px-2 py-1 text-[10px] font-mono text-white placeholder:text-[#556677]" - /> <div className="flex gap-1.5"> <button type="button" @@ -533,7 +504,7 @@ export function DirectiveDetail({ </button> <button type="button" - onClick={() => { setAddingMemory(false); setNewContent(""); setNewSource(""); }} + onClick={() => { setAddingMemory(false); setNewContent(""); }} className="text-[10px] font-mono text-[#7788aa] hover:text-white border border-[#2a3a5a] rounded px-2 py-0.5" > Cancel diff --git a/makima/frontend/src/hooks/useDirectiveMemories.ts b/makima/frontend/src/hooks/useDirectiveMemories.ts index 3844c44..db1f3fa 100644 --- a/makima/frontend/src/hooks/useDirectiveMemories.ts +++ b/makima/frontend/src/hooks/useDirectiveMemories.ts @@ -1,41 +1,35 @@ import { useState, useEffect, useCallback } from "react"; import { - type DirectiveMemoryEntry, - type DirectiveMemoryConfig, + type DirectiveMemory, type MemoryCategory, type CreateDirectiveMemoryRequest, - getDirectiveMemoryConfig, - setDirectiveMemoryEnabled, listDirectiveMemories, - addDirectiveMemory, + createDirectiveMemory, deleteDirectiveMemory, - clearDirectiveMemories, } from "../lib/api"; +const ALL_CATEGORIES: MemoryCategory[] = [ + "decision", + "learning", + "context", + "preference", + "issue", + "progress", + "other", +]; + export function useDirectiveMemories(directiveId: string | undefined) { - const [memories, setMemories] = useState<DirectiveMemoryEntry[]>([]); - const [config, setConfig] = useState<DirectiveMemoryConfig | null>(null); + const [memories, setMemories] = useState<DirectiveMemory[]>([]); const [loading, setLoading] = useState(false); const [error, setError] = useState<string | null>(null); - const refreshConfig = useCallback(async () => { - if (!directiveId) return; - try { - const c = await getDirectiveMemoryConfig(directiveId); - setConfig(c); - } catch (e) { - // Config may not exist yet — treat as disabled - setConfig({ directiveId, enabled: false, updatedAt: new Date().toISOString() }); - } - }, [directiveId]); - const refreshMemories = useCallback(async () => { if (!directiveId) return; try { setLoading(true); setError(null); - const entries = await listDirectiveMemories(directiveId); - setMemories(entries); + const response = await listDirectiveMemories(directiveId); + setMemories(response.memories); } catch (e) { setError(e instanceof Error ? e.message : "Failed to load memories"); } finally { @@ -43,75 +37,71 @@ export function useDirectiveMemories(directiveId: string | undefined) { } }, [directiveId]); - const refresh = useCallback(async () => { - await Promise.all([refreshConfig(), refreshMemories()]); - }, [refreshConfig, refreshMemories]); - useEffect(() => { - refresh(); - }, [refresh]); - - const toggleEnabled = useCallback(async (enabled: boolean) => { - if (!directiveId) return; - try { - setError(null); - const c = await setDirectiveMemoryEnabled(directiveId, enabled); - setConfig(c); - } catch (e) { - setError(e instanceof Error ? e.message : "Failed to toggle memory"); - } - }, [directiveId]); + refreshMemories(); + }, [refreshMemories]); - const add = useCallback(async (req: CreateDirectiveMemoryRequest) => { - if (!directiveId) return; - try { - setError(null); - await addDirectiveMemory(directiveId, req); - await refreshMemories(); - } catch (e) { - setError(e instanceof Error ? e.message : "Failed to add memory"); - } - }, [directiveId, refreshMemories]); + const add = useCallback( + async (req: CreateDirectiveMemoryRequest) => { + if (!directiveId) return; + try { + setError(null); + await createDirectiveMemory(directiveId, req); + await refreshMemories(); + } catch (e) { + setError(e instanceof Error ? e.message : "Failed to add memory"); + } + }, + [directiveId, refreshMemories] + ); - const remove = useCallback(async (memoryId: string) => { - if (!directiveId) return; - try { - setError(null); - await deleteDirectiveMemory(directiveId, memoryId); - await refreshMemories(); - } catch (e) { - setError(e instanceof Error ? e.message : "Failed to delete memory"); - } - }, [directiveId, refreshMemories]); + const remove = useCallback( + async (memoryId: string) => { + if (!directiveId) return; + try { + setError(null); + await deleteDirectiveMemory(directiveId, memoryId); + await refreshMemories(); + } catch (e) { + setError(e instanceof Error ? e.message : "Failed to delete memory"); + } + }, + [directiveId, refreshMemories] + ); const clearAll = useCallback(async () => { if (!directiveId) return; try { setError(null); - await clearDirectiveMemories(directiveId); + // Delete all memories one by one (no bulk clear endpoint) + for (const m of memories) { + await deleteDirectiveMemory(directiveId, m.id); + } setMemories([]); } catch (e) { setError(e instanceof Error ? e.message : "Failed to clear memories"); } - }, [directiveId]); + }, [directiveId, memories]); /** Group entries by category */ - const grouped = memories.reduce<Record<MemoryCategory, DirectiveMemoryEntry[]>>( + const grouped = memories.reduce<Record<MemoryCategory, DirectiveMemory[]>>( (acc, entry) => { - acc[entry.category].push(entry); + const cat = entry.category || "other"; + acc[cat].push(entry); return acc; }, - { decision: [], context: [], preference: [], learning: [], other: [] }, + ALL_CATEGORIES.reduce( + (init, cat) => ({ ...init, [cat]: [] }), + {} as Record<MemoryCategory, DirectiveMemory[]> + ) ); return { memories, grouped, - config, loading, error, - refresh, - toggleEnabled, + refresh: refreshMemories, add, remove, clearAll, diff --git a/makima/frontend/tsconfig.tsbuildinfo b/makima/frontend/tsconfig.tsbuildinfo index f36c337..210212c 100644 --- a/makima/frontend/tsconfig.tsbuildinfo +++ b/makima/frontend/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/commandmodepanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/directives/directivedag.tsx","./src/components/directives/directivedetail.tsx","./src/components/directives/directivelist.tsx","./src/components/directives/stepnode.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/discusscontractmodal.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/workflow/phasecolumn.tsx","./src/components/workflow/workflowboard.tsx","./src/components/workflow/workflowcontractcard.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usedirectives.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usespeakwebsocket.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/directives.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/settings.tsx","./src/routes/speak.tsx","./src/routes/workflow.tsx","./src/types/messages.ts"],"version":"5.9.3"}
\ No newline at end of file +{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/commandmodepanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/directives/directivedag.tsx","./src/components/directives/directivedetail.tsx","./src/components/directives/directivelist.tsx","./src/components/directives/directivelogstream.tsx","./src/components/directives/stepnode.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/discusscontractmodal.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/workflow/phasecolumn.tsx","./src/components/workflow/workflowboard.tsx","./src/components/workflow/workflowcontractcard.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usedirectivememories.ts","./src/hooks/usedirectives.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usemultitasksubscription.ts","./src/hooks/usespeakwebsocket.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/directives.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/settings.tsx","./src/routes/speak.tsx","./src/routes/workflow.tsx","./src/types/messages.ts"],"version":"5.9.3"}
\ No newline at end of file |
