diff --git a/ui/src/components/settings/MemoryLearningForm.tsx b/ui/src/components/settings/MemoryLearningForm.tsx index 911df46..1a8e754 100644 --- a/ui/src/components/settings/MemoryLearningForm.tsx +++ b/ui/src/components/settings/MemoryLearningForm.tsx @@ -1,34 +1,15 @@ /** - * MemoryLearningForm.tsx — "Memory & Learning" settings section + * MemoryLearningForm.tsx — "Reflection 履歴" settings section * - * Two stacked panels: - * 1. MemoryEntriesPanel — list / inline-edit / delete user memory entries - * 2. ReflectionTimelinePanel — paged snapshot history + revert + 30-day metrics + * ReflectionTimelinePanel — paged snapshot history + revert + 30-day metrics. + * (Memory entry editing moved to User Folder → memory/ — MemoryPanel.tsx.) */ -import { useState, useEffect } from 'react'; +import { useState } from 'react'; import { useQuery, useMutation, useQueryClient, useInfiniteQuery } from '@tanstack/react-query'; -import { HelpText } from './HelpText'; // ── API types ───────────────────────────────────────────────────────────────── -type MemoryType = 'user' | 'feedback' | 'project' | 'reference'; - -// Mirrors the server's flat shape from `listMemoryEntries` in -// src/user-folder/memory.ts and `GET /api/local/memory/entries` in -// src/bridge/memory-api.ts. If you change this shape, update both. -interface MemoryEntry { - name: string; - description: string; - type: MemoryType; - body: string; -} - -interface MemoryListResponse { - entries: MemoryEntry[]; - index: string; -} - interface SnapshotIndexEntry { ts: string; snapshotId: string; @@ -80,35 +61,6 @@ interface ReflectionMetrics { // ── API helpers ─────────────────────────────────────────────────────────────── -async function fetchMemoryEntries(): Promise { - const res = await fetch('/api/local/memory/entries'); - if (!res.ok) throw new Error(`メモリエントリの読み込みに失敗しました (${res.status})`); - return res.json(); -} - -async function upsertMemoryEntry( - name: string, - payload: { description: string; type: MemoryType; body: string }, -): Promise { - const res = await fetch(`/api/local/memory/entries/${encodeURIComponent(name)}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload), - }); - const data = await res.json().catch(() => ({ error: res.statusText })); - if (!res.ok) throw new Error(data.error ?? res.statusText); -} - -async function deleteMemoryEntry(name: string): Promise { - const res = await fetch(`/api/local/memory/entries/${encodeURIComponent(name)}`, { - method: 'DELETE', - }); - if (!res.ok) { - const data = await res.json().catch(() => ({ error: res.statusText })); - throw new Error(data.error ?? res.statusText); - } -} - async function fetchHistoryPage(cursor?: string): Promise { const params = new URLSearchParams({ limit: '20' }); if (cursor) params.set('before', cursor); @@ -171,313 +123,6 @@ function formatTs(ts: string): string { } } -// ── Validator rejection code messages ───────────────────────────────────────── - -const REJECTION_MESSAGES: Record = { - rejected_bad_name: '名前が無効です(英数字・ハイフン・アンダースコア、1〜64文字)', - rejected_bad_description: '概要は必須で、1行以内で入力してください', - rejected_unknown_type: 'タイプは user / feedback / project / reference のいずれかを指定してください', - rejected_bad_body: '本文は文字列で指定してください', - rejected_body_too_large: '本文が許容サイズを超えています', - rejected_bad_request: 'リクエストの形式が正しくありません', -}; - -function rejectionMessage(code: string): string { - return REJECTION_MESSAGES[code] ?? code; -} - -// ── MemoryEntryModal ────────────────────────────────────────────────────────── - -interface EntryFormState { - name: string; - description: string; - type: MemoryType; - body: string; -} - -const MEMORY_TYPES: MemoryType[] = ['user', 'feedback', 'project', 'reference']; - -function MemoryEntryModal({ - initial, - isNew, - onClose, - onSaved, -}: { - initial: EntryFormState; - isNew: boolean; - onClose: () => void; - onSaved: () => void; -}) { - const [form, setForm] = useState(initial); - const [error, setError] = useState(null); - const [saving, setSaving] = useState(false); - - const set = (k: K, v: EntryFormState[K]) => - setForm(prev => ({ ...prev, [k]: v })); - - const handleSave = async () => { - setSaving(true); - setError(null); - try { - await upsertMemoryEntry(form.name, { - description: form.description, - type: form.type, - body: form.body, - }); - onSaved(); - onClose(); - } catch (e: any) { - setError(rejectionMessage(e.message)); - } finally { - setSaving(false); - } - }; - - return ( -
-
-
-

- {isNew ? '新しいメモリエントリ' : `編集 — ${initial.name}`} -

- -
- -
- {/* Name — only editable when creating */} -
- - set('name', e.target.value)} - disabled={!isNew} - placeholder="my-fact" - className={`w-full h-8 px-2.5 text-[13px] font-mono border border-hairline rounded-md focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none ${ - !isNew ? 'bg-slate-50 text-slate-500 cursor-not-allowed' : 'bg-canvas' - }`} - /> -
- -
- - set('description', e.target.value)} - placeholder="メモリ一覧に表示される1行の説明" - className="w-full h-8 px-2.5 text-[13px] border border-hairline rounded-md focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none" - /> -
- -
- - - - user: あなた固有の好み・役割 / feedback: 過去のフィードバック・教訓 / project: プロジェクト別の文脈 / reference: 参照資料・外部情報 - -
- -
- -