From 724474cb49292cf821a9f1ee9bd1100abd1dae84 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Sat, 16 May 2026 20:34:58 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20remove=20dead=20code=20=E2=80=94=208?= =?UTF-8?q?=20components,=205=20libs,=204=20API=20routes,=204=20npm=20pack?= =?UTF-8?q?ages,=2030+=20scripts,=20dead=20CSS,=20dead=20exports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed unused components: - brainstorm-canvas, brainstorm-create-dialog, invite-dialog, manual-idea-dialog - note-inline-editor, profile-page-header, quota-paywall, label-management-dialog Removed dead lib files: - api-auth.ts, color-harmony-recommendation.ts, label-storage.ts, modern-color-options.ts - hooks/use-card-size-mode.ts Removed dead API routes: - ai/test-chat, ai/test-embeddings, ai/test-tags, admin/randomize-labels Removed unused npm packages: - cmdk, novel, tippy.js, react-force-graph-2d Cleaned dead CSS from globals.css: - acrylic-*, win11-shadow-*, muuri-grid/item, ai-glass, ai-tab-indicator, ai-send-btn, sidebar-view-toggle, memento-sidebar-depth Removed 29 orphan scripts and 3 root orphan files Cleaned dead exports from 8 lib files: - NOTE_TYPE_CONFIG, getPublishableKey, PROVIDER_DEFAULTS, useNotes/useNote/invalidateNote, etc. --- memento-note/.win11-prompt.txt | 60 - memento-note/BRAINSTORM-CANVAS-SPEC.md | 175 -- .../app/api/admin/randomize-labels/route.ts | 36 - memento-note/app/api/ai/test-chat/route.ts | 55 - .../app/api/ai/test-embeddings/route.ts | 99 - memento-note/app/api/ai/test-tags/route.ts | 58 - memento-note/app/globals.css | 134 - .../brainstorm/brainstorm-canvas.tsx | 386 --- .../brainstorm/brainstorm-create-dialog.tsx | 84 - .../components/brainstorm/invite-dialog.tsx | 210 -- .../brainstorm/manual-idea-dialog.tsx | 98 - .../components/label-management-dialog.tsx | 229 -- .../components/note-inline-editor.tsx | 992 ------- .../components/profile-page-header.tsx | 28 - memento-note/components/quota-paywall.tsx | 49 - memento-note/hooks/use-card-size-mode.ts | 37 - memento-note/lib/agent-email-template.ts | 2 +- memento-note/lib/ai/factory.ts | 2 +- memento-note/lib/ai/fallback.ts | 27 +- memento-note/lib/ai/provider-for-user.ts | 6 +- memento-note/lib/api-auth.ts | 19 - memento-note/lib/apply-document-theme.ts | 2 +- .../lib/color-harmony-recommendation.ts | 310 -- memento-note/lib/label-storage.ts | 57 - memento-note/lib/modern-color-options.ts | 307 -- memento-note/lib/query-hooks.ts | 34 +- memento-note/lib/stripe.ts | 6 +- memento-note/lib/types.ts | 7 - memento-note/package-lock.json | 1256 -------- memento-note/package.json | 4 - memento-note/rewrite-ai-tabs.js | 71 - memento-note/run-test.ts | 8 - memento-note/scripts/add-expand-btn.js | 37 - memento-note/scripts/add-resize.js | 60 - memento-note/scripts/apply-missing-i18n.mjs | 292 -- memento-note/scripts/check-config.ts | 22 - memento-note/scripts/check-labels.js | 76 - .../scripts/check-recent-notes-state.ts | 35 - memento-note/scripts/check-users.ts | 25 - memento-note/scripts/compare-dbs.ts | 41 - memento-note/scripts/create-test-user.ts | 31 - memento-note/scripts/debug-config.ts | 41 - memento-note/scripts/debug-notes-size.ts | 36 - memento-note/scripts/debug-recent-notes.ts | 60 - .../scripts/fix-recent-notes-settings.ts | 39 - .../scripts/generate-i18n-overrides.py | 145 - memento-note/scripts/grant-all-admins.ts | 24 - .../scripts/i18n-landing-patches.json | 2615 ----------------- .../scripts/merge-missing-locale-keys.mjs | 35 - memento-note/scripts/migrate-embeddings.ts | 69 - .../scripts/migrate-sqlite-to-postgres.ts | 387 --- memento-note/scripts/promote-admin.js | 35 - memento-note/scripts/regenerate-embeddings.ts | 67 - memento-note/scripts/reset-password-auto.ts | 47 - memento-note/scripts/seed-user.ts | 22 - memento-note/scripts/setup-openai.ts | 48 - memento-note/scripts/sync_locales_from_en.py | 163 - memento-note/scripts/test-backend-logic.ts | 40 - memento-note/scripts/update-colors.ts | 41 - memento-note/scripts/verify-size.ts | 56 - memento-note/used_keys.txt | 81 - 61 files changed, 16 insertions(+), 9502 deletions(-) delete mode 100644 memento-note/.win11-prompt.txt delete mode 100644 memento-note/BRAINSTORM-CANVAS-SPEC.md delete mode 100644 memento-note/app/api/admin/randomize-labels/route.ts delete mode 100644 memento-note/app/api/ai/test-chat/route.ts delete mode 100644 memento-note/app/api/ai/test-embeddings/route.ts delete mode 100644 memento-note/app/api/ai/test-tags/route.ts delete mode 100644 memento-note/components/brainstorm/brainstorm-canvas.tsx delete mode 100644 memento-note/components/brainstorm/brainstorm-create-dialog.tsx delete mode 100644 memento-note/components/brainstorm/invite-dialog.tsx delete mode 100644 memento-note/components/brainstorm/manual-idea-dialog.tsx delete mode 100644 memento-note/components/label-management-dialog.tsx delete mode 100644 memento-note/components/note-inline-editor.tsx delete mode 100644 memento-note/components/profile-page-header.tsx delete mode 100644 memento-note/components/quota-paywall.tsx delete mode 100644 memento-note/hooks/use-card-size-mode.ts delete mode 100644 memento-note/lib/api-auth.ts delete mode 100644 memento-note/lib/color-harmony-recommendation.ts delete mode 100644 memento-note/lib/label-storage.ts delete mode 100644 memento-note/lib/modern-color-options.ts delete mode 100644 memento-note/rewrite-ai-tabs.js delete mode 100644 memento-note/run-test.ts delete mode 100644 memento-note/scripts/add-expand-btn.js delete mode 100644 memento-note/scripts/add-resize.js delete mode 100644 memento-note/scripts/apply-missing-i18n.mjs delete mode 100644 memento-note/scripts/check-config.ts delete mode 100644 memento-note/scripts/check-labels.js delete mode 100644 memento-note/scripts/check-recent-notes-state.ts delete mode 100644 memento-note/scripts/check-users.ts delete mode 100644 memento-note/scripts/compare-dbs.ts delete mode 100644 memento-note/scripts/create-test-user.ts delete mode 100644 memento-note/scripts/debug-config.ts delete mode 100644 memento-note/scripts/debug-notes-size.ts delete mode 100644 memento-note/scripts/debug-recent-notes.ts delete mode 100644 memento-note/scripts/fix-recent-notes-settings.ts delete mode 100644 memento-note/scripts/generate-i18n-overrides.py delete mode 100644 memento-note/scripts/grant-all-admins.ts delete mode 100644 memento-note/scripts/i18n-landing-patches.json delete mode 100644 memento-note/scripts/merge-missing-locale-keys.mjs delete mode 100644 memento-note/scripts/migrate-embeddings.ts delete mode 100644 memento-note/scripts/migrate-sqlite-to-postgres.ts delete mode 100644 memento-note/scripts/promote-admin.js delete mode 100644 memento-note/scripts/regenerate-embeddings.ts delete mode 100644 memento-note/scripts/reset-password-auto.ts delete mode 100644 memento-note/scripts/seed-user.ts delete mode 100644 memento-note/scripts/setup-openai.ts delete mode 100644 memento-note/scripts/sync_locales_from_en.py delete mode 100644 memento-note/scripts/test-backend-logic.ts delete mode 100644 memento-note/scripts/update-colors.ts delete mode 100644 memento-note/scripts/verify-size.ts delete mode 100644 memento-note/used_keys.txt diff --git a/memento-note/.win11-prompt.txt b/memento-note/.win11-prompt.txt deleted file mode 100644 index 2afa48e..0000000 --- a/memento-note/.win11-prompt.txt +++ /dev/null @@ -1,60 +0,0 @@ -Redesign the entire UI of this application to look like Windows 11 Fluent Design. This is a UI-only redesign - do NOT change any business logic, API routes, database schema, or functionality. Only modify visual styling (CSS classes, Tailwind utilities, color values, border-radius, shadows, etc.). - -Changes needed: - -1. globals.css - Update theme: - - Primary color: #0078D4 (Windows 11 blue) - - Add --color-win11-accent: #0078D4 and shades (#106EBE, #005A9E, #003D6B) - - Background: light #f3f3f3, dark #202020 - - Rounded corners: 8px cards, 4px small elements - - Shadows: subtle layered shadows like Win11 (0 2px 8px rgba(0,0,0,0.04), 0 8px 32px rgba(0,0,0,0.08)) - - Smooth transitions: 200-300ms ease - - Add acrylic utility: .acrylic { backdrop-filter: blur(20px) saturate(180%); background: rgba(255,255,255,0.7); } - - Add .acrylic-dark for dark mode - -2. app/(main)/layout.tsx - Main layout: - - Background: #f3f3f3 (light) / #202020 (dark) - - Sidebar: add bg-white/80 dark:bg-[#2d2d2d]/80 backdrop-blur-xl rounded-e-lg - - Content area: clean with subtle padding - -3. components/sidebar.tsx - Windows 11 navigation: - - Semi-transparent bg with backdrop-blur-xl - - Nav items: rounded-lg hover states with subtle bg-slate-100 dark:bg-slate-800 - - Active item: bg-blue-50 dark:bg-blue-900/30 text-[#0078D4] with left border-2 indicator - - Smooth collapse animation with transition-all duration-300 - -4. components/header.tsx - Windows 11 title bar: - - Clean minimal, height h-12 - - Rounded search input (rounded-full or rounded-lg) like Win11 search - - Subtle bottom border - -5. components/note-card.tsx - Win11 cards: - - rounded-lg (8px) - - border border-slate-200 dark:border-slate-700 - - hover:shadow-lg hover:-translate-y-0.5 transition-all duration-200 - - Clean white bg - -6. components/home-client.tsx - Widget layout: - - Rounded containers (rounded-xl) for each section - - Subtle hover:shadow-md transition - -7. components/note-editor.tsx & rich-text-editor.tsx - Win11 editor: - - Rounded toolbar buttons (rounded-md) - - Subtle separators between toolbar groups - - Clean focused state - -8. components/ui/button.tsx - Win11 buttons: - - rounded-md (6px) - - Primary: bg-[#0078D4] hover:bg-[#106EBE] text-white - - Secondary: bg-slate-100 hover:bg-slate-200 border border-slate-300 - - Subtle active states - -9. components/ui/card.tsx - Win11 card: - - rounded-lg border border-slate-200/60 - - hover:shadow-md transition-shadow duration-200 - -10. components/ui/input.tsx - Win11 input: - - rounded-md (6px) - - border-slate-300 focus:border-[#0078D4] focus:ring-1 focus:ring-[#0078D4]/30 - -Read each file first, understand its structure, then make surgical edits. After all changes, run npm run build to verify the build passes. Fix any build errors if any. \ No newline at end of file diff --git a/memento-note/BRAINSTORM-CANVAS-SPEC.md b/memento-note/BRAINSTORM-CANVAS-SPEC.md deleted file mode 100644 index 27e29aa..0000000 --- a/memento-note/BRAINSTORM-CANVAS-SPEC.md +++ /dev/null @@ -1,175 +0,0 @@ -# Brainstorm Canvas — Feature Spec for OpenCode - -## PROJECT CONTEXT -- **Stack**: Next.js 15 + Prisma + PostgreSQL (pgvector) -- **Location**: ~/dev/Momento/memento-note/ -- **Existing models**: User, Note, Notebook, Canvas (Excalidraw), Agent, Conversation, Workflow, etc. -- **Existing API routes**: /app/api/notes, /notebooks, /ai, /canvas, /chat, /agents, etc. -- **UI lib**: Radix UI + Tailwind CSS + shadcn components -- **State**: @tanstack/react-query -- **Auth**: NextAuth (auth.ts, auth.config.ts) - -## ⚠️ CRITICAL RULES -1. **DO NOT modify existing models or API routes** — only ADD new ones -2. **DO NOT touch prisma/schema.prisma without creating a migration** — use `npx prisma migrate dev --name add_brainstorm` -3. **Study the existing code patterns** before writing new code — match the project conventions -4. **Look at how existing API routes are structured** (e.g., /app/api/notes/route.ts) and follow the same pattern -5. **Look at how existing components use React Query** and follow the same pattern -6. **Use existing UI components** from /components/ui/ (shadcn) — don't install new UI libs -7. **The project uses TypeScript** — maintain strict typing -8. **Do NOT run npm build** — only dev server if needed for testing - -## FEATURE: Brainstorm Canvas (Wave Brainstorming) - -### Concept -A temporary workspace for brainstorming ideas using AI-generated "waves" of ideas, displayed as a radial graph. The output feeds back into the notes system. - -### Data Model — ADD these 2 models to prisma/schema.prisma: - -```prisma -model BrainstormSession { - id String @id @default(cuid()) - seedIdea String - sourceNoteId String? - contextNoteIds String? // JSON array of note IDs - exportedNoteId String? - userId String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - ideas BrainstormIdea[] - sourceNote Note? @relation(fields: [sourceNoteId], references: [id]) - exportedNote Note? @relation(fields: [exportedNoteId], references: [id]) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@index([userId]) - @@index([userId, createdAt]) -} - -model BrainstormIdea { - id String @id @default(cuid()) - sessionId String - waveNumber Int // 1, 2, or 3 - title String - description String - connectionToSeed String? - noveltyScore Int? - parentIdeaId String? // for "dig deeper" sub-brainstorms - convertedToNoteId String? - relatedNoteIds String? // JSON array - status String @default("active") // active, dismissed, converted - positionX Float? - positionY Float? - createdAt DateTime @default(now()) - session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade) - parentIdea BrainstormIdea? @relation("IdeaTree", fields: [parentIdeaId], references: [id]) - children BrainstormIdea[] @relation("IdeaTree") - convertedNote Note? @relation(fields: [convertedToNoteId], references: [id]) - - @@index([sessionId]) - @@index([waveNumber]) - @@index([status]) - @@index([parentIdeaId]) -} -``` - -Also add relations to User model: -``` -brainstormSessions BrainstormSession[] -``` - -And to Note model — add these two relations: -``` -sourceBrainstormSessions BrainstormSession[] @relation via sourceNoteId -exportedBrainstormSessions BrainstormSession[] @relation via exportedNoteId -convertedBrainstormIdeas BrainstormIdea[] @relation via convertedToNoteId -``` - -### API Routes — Create /app/api/brainstorm/ - -1. **POST /api/brainstorm/wave** — Create new brainstorm session - - Input: { seedIdea, sourceNoteId?, contextNoteIds? } - - Uses existing AI setup (check how /api/ai/ routes call LLM) - - Generates 3 waves of ~3 ideas each (9 total) - - For each idea, does an embedding search to find related notes (check how semantic search works in existing code) - - Saves session + ideas to DB - - Returns: { sessionId, ideas: [...] } - -2. **POST /api/brainstorm/[sessionId]/expand** — Dig deeper on an idea - - Input: { ideaId } - - Uses the clicked idea as new seed - - Generates 3 more waves, linked as children - - Returns new ideas with parentIdeaId set - -3. **POST /api/brainstorm/[sessionId]/dismiss** — Mark idea as not relevant - - Input: { ideaId } - - Sets status = "dismissed" - -4. **POST /api/brainstorm/[sessionId]/convert** — Convert idea to a real Note - - Input: { ideaId } - - Creates a Note with pre-filled content - - Auto-tags: "brainstorm", "idée" - - Links to source note if exists - - Sets idea.status = "converted", idea.convertedToNoteId = newNote.id - - Returns the created note - -5. **POST /api/brainstorm/[sessionId]/export** — Export session as summary note - - Generates a Markdown summary note - - Groups by waves, shows which ideas were converted - - Links to all converted notes - - Returns the created note - -6. **GET /api/brainstorm** — List user's brainstorm sessions - - Returns sessions ordered by date, with idea counts - -7. **GET /api/brainstorm/[sessionId]** — Get full session with ideas - - Returns session + all ideas + their status - -### Frontend — Canvas Component - -Use **react-force-graph-2d** (install it: `npm install react-force-graph-2d`). -It's a React wrapper around d3-force — declarative API, same physics engine. - -**Layout:** -- Center node = seed idea (large, white) -- Ring 1 (radius ~150px) = Wave 1 ideas (orange) -- Ring 2 (radius ~300px) = Wave 2 ideas (blue) -- Ring 3 (radius ~450px) = Wave 3 ideas (purple) -- Use d3.forceRadial for ring constraint -- Dismissed nodes = opacity 0.3, smaller -- Converted nodes = green border + icon -- Click node = side panel with details + 3 action buttons - -**Side Panel (when clicking a node):** -- Show: title, description, connection to seed, related notes -- 3 buttons: Dig Deeper, Create Note, Dismiss -- Uses existing shadcn Sheet or Dialog component - -**Sidebar Integration:** -- Add "Brainstorms" section in the existing sidebar -- List sessions with preview (seed idea + count) -- Click to reopen saved canvas state - -**Entry Points:** -1. From a note: Brainstorm button in note toolbar/editor -2. From sidebar: "+ New Brainstorm" button - -### AI Prompt for Wave Generation - -The LLM prompt should generate 3 waves: -- **Wave 1 — Variations**: Direct variations/expansions of the seed (sous-aspects, reformulations, variations) -- **Wave 2 — Analogies**: Cross-domain analogies (autres domaines, biologie, technologie, etc.) -- **Wave 3 — Disruptions**: Inversions, provocations, ideas that challenge assumptions - -Each idea should have: -- title (short) -- description (1-2 sentences) -- connectionToSeed (how it relates to the seed) -- noveltyScore (1-10) - -### Implementation Order -1. Prisma schema + migration -2. API routes (start with /wave and /convert) -3. Brainstorm canvas component (react-force-graph-2d) -4. Sidebar integration -5. Note toolbar button -6. Export functionality diff --git a/memento-note/app/api/admin/randomize-labels/route.ts b/memento-note/app/api/admin/randomize-labels/route.ts deleted file mode 100644 index 91566de..0000000 --- a/memento-note/app/api/admin/randomize-labels/route.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { NextResponse } from 'next/server'; -import prisma from '@/lib/prisma'; -import { LABEL_COLORS } from '@/lib/types'; -import { auth } from '@/auth'; - -export const dynamic = 'force-dynamic'; - -export async function POST() { - try { - const session = await auth() - if (!session?.user?.id || (session.user as any).role !== 'ADMIN') { - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - } - const labels = await prisma.label.findMany(); - const colors = Object.keys(LABEL_COLORS).filter(c => c !== 'gray'); // Exclude gray to force colors - - const updates = labels.map((label: any) => { - const randomColor = colors[Math.floor(Math.random() * colors.length)]; - return prisma.label.update({ - where: { id: label.id }, - data: { color: randomColor } - }); - }); - - await prisma.$transaction(updates); - - return NextResponse.json({ - success: true, - updated: updates.length, - message: "All labels have been assigned a random non-gray color." - }); - - } catch (error) { - return NextResponse.json({ error: String(error) }, { status: 500 }); - } -} diff --git a/memento-note/app/api/ai/test-chat/route.ts b/memento-note/app/api/ai/test-chat/route.ts deleted file mode 100644 index 77c2908..0000000 --- a/memento-note/app/api/ai/test-chat/route.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server' -import { getChatProvider } from '@/lib/ai/factory' -import { getSystemConfig } from '@/lib/config' -import { auth } from '@/auth' - -export async function POST(request: NextRequest) { - const session = await auth() - if (!session?.user?.id) { - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - } - if ((session.user as any).role !== 'ADMIN') { - return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) - } - - try { - const config = await getSystemConfig() - const provider = getChatProvider(config) - - const testMessage = 'Réponds en exactement 3 mots : quel est ton nom ?' - - const startTime = Date.now() - const response = await provider.generateText(testMessage) - const endTime = Date.now() - - if (!response || response.trim().length === 0) { - return NextResponse.json( - { - success: false, - error: 'No response from chat provider', - model: config.AI_MODEL_CHAT || 'granite4:latest', - }, - { status: 500 } - ) - } - - return NextResponse.json({ - success: true, - model: config.AI_MODEL_CHAT || 'granite4:latest', - chatResponse: response.trim(), - responseTime: endTime - startTime, - }) - } catch (error: any) { - const config = await getSystemConfig() - - return NextResponse.json( - { - success: false, - error: error.message || 'Unknown error', - model: config.AI_MODEL_CHAT || 'granite4:latest', - stack: process.env.NODE_ENV === 'development' ? error.stack : undefined, - }, - { status: 500 } - ) - } -} diff --git a/memento-note/app/api/ai/test-embeddings/route.ts b/memento-note/app/api/ai/test-embeddings/route.ts deleted file mode 100644 index 236a12f..0000000 --- a/memento-note/app/api/ai/test-embeddings/route.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server' -import { getEmbeddingsProvider } from '@/lib/ai/factory' -import { getSystemConfig } from '@/lib/config' -import { auth } from '@/auth' - -function getProviderDetails(config: Record, providerType: string) { - const provider = providerType.toLowerCase() - - switch (provider) { - case 'ollama': - return { - provider: 'Ollama', - baseUrl: config.OLLAMA_BASE_URL || 'http://localhost:11434', - model: config.AI_MODEL_EMBEDDING || 'embeddinggemma:latest' - } - case 'openai': - return { - provider: 'OpenAI', - baseUrl: 'https://api.openai.com/v1', - model: config.AI_MODEL_EMBEDDING || 'text-embedding-3-small' - } - case 'custom': - return { - provider: 'Custom OpenAI', - baseUrl: config.CUSTOM_OPENAI_BASE_URL || 'Not configured', - model: config.AI_MODEL_EMBEDDING || 'text-embedding-3-small' - } - default: - return { - provider: provider, - baseUrl: 'unknown', - model: config.AI_MODEL_EMBEDDING || 'unknown' - } - } -} - -export async function POST(request: NextRequest) { - const session = await auth() - if (!session?.user?.id) { - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - } - if ((session.user as any).role !== 'ADMIN') { - return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) - } - - try { - const config = await getSystemConfig() - const provider = getEmbeddingsProvider(config) - - const testText = 'test' - const startTime = Date.now() - const embeddings = await provider.getEmbeddings(testText) - const endTime = Date.now() - - if (!embeddings || embeddings.length === 0) { - const providerType = config.AI_PROVIDER_EMBEDDING || 'ollama' - const details = getProviderDetails(config, providerType) - return NextResponse.json( - { - success: false, - error: 'No embeddings returned', - provider: providerType, - model: config.AI_MODEL_EMBEDDING || 'embeddinggemma:latest', - details - }, - { status: 500 } - ) - } - - const providerType = config.AI_PROVIDER_EMBEDDING || 'ollama' - const details = getProviderDetails(config, providerType) - - return NextResponse.json({ - success: true, - provider: providerType, - model: config.AI_MODEL_EMBEDDING || 'embeddinggemma:latest', - embeddingLength: embeddings.length, - firstValues: embeddings.slice(0, 5), - responseTime: endTime - startTime, - details - }) - } catch (error: any) { - const config = await getSystemConfig() - const providerType = config.AI_PROVIDER_EMBEDDING || 'ollama' - const details = getProviderDetails(config, providerType) - - return NextResponse.json( - { - success: false, - error: error.message || 'Unknown error', - provider: providerType, - model: config.AI_MODEL_EMBEDDING || 'embeddinggemma:latest', - details, - stack: process.env.NODE_ENV === 'development' ? error.stack : undefined - }, - { status: 500 } - ) - } -} diff --git a/memento-note/app/api/ai/test-tags/route.ts b/memento-note/app/api/ai/test-tags/route.ts deleted file mode 100644 index 703a312..0000000 --- a/memento-note/app/api/ai/test-tags/route.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server' -import { getTagsProvider } from '@/lib/ai/factory' -import { getSystemConfig } from '@/lib/config' -import { auth } from '@/auth' - -export async function POST(request: NextRequest) { - const session = await auth() - if (!session?.user?.id) { - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - } - if ((session.user as any).role !== 'ADMIN') { - return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) - } - - try { - const config = await getSystemConfig() - const provider = getTagsProvider(config) - - const testContent = "This is a test note about artificial intelligence and machine learning. It contains keywords like AI, ML, neural networks, and deep learning." - - const startTime = Date.now() - const tags = await provider.generateTags(testContent) - const endTime = Date.now() - - if (!tags || tags.length === 0) { - return NextResponse.json( - { - success: false, - error: 'No tags generated', - provider: config.AI_PROVIDER_TAGS || 'ollama', - model: config.AI_MODEL_TAGS || 'granite4:latest' - }, - { status: 500 } - ) - } - - return NextResponse.json({ - success: true, - provider: config.AI_PROVIDER_TAGS || 'ollama', - model: config.AI_MODEL_TAGS || 'granite4:latest', - tags: tags, - responseTime: endTime - startTime - }) - } catch (error: any) { - const config = await getSystemConfig() - - return NextResponse.json( - { - success: false, - error: error.message || 'Unknown error', - provider: config.AI_PROVIDER_TAGS || 'ollama', - model: config.AI_MODEL_TAGS || 'granite4:latest', - stack: process.env.NODE_ENV === 'development' ? error.stack : undefined - }, - { status: 500 } - ) - } -} diff --git a/memento-note/app/globals.css b/memento-note/app/globals.css index 5f4b460..4cc6f00 100644 --- a/memento-note/app/globals.css +++ b/memento-note/app/globals.css @@ -94,36 +94,10 @@ -webkit-backdrop-filter: blur(20px) saturate(180%); } -@utility acrylic-heavy { - backdrop-filter: blur(40px) saturate(200%); - -webkit-backdrop-filter: blur(40px) saturate(200%); -} - -@utility acrylic-light { - background: rgba(255, 255, 255, 0.72); - backdrop-filter: blur(24px) saturate(1.35); - -webkit-backdrop-filter: blur(24px) saturate(1.35); -} - @utility font-memento-serif { font-family: var(--font-memento-serif), ui-serif, Georgia, "Times New Roman", serif; } -@utility acrylic-dark { - background: rgba(32, 32, 32, 0.75); - backdrop-filter: blur(20px) saturate(1.5); - -webkit-backdrop-filter: blur(20px) saturate(1.5); -} - -@utility win11-shadow { - box-shadow: var(--shadow-card-rest); - transition: box-shadow var(--transition-normal); -} - -@utility win11-shadow-hover { - box-shadow: var(--shadow-card-hover); -} - @utility editor-body { font-size: var(--editor-body-size, 16px); } @@ -137,14 +111,6 @@ html.dark .memento-paper-texture { background-color: var(--background); } -.memento-sidebar-depth { - box-shadow: 1px 0 10px rgba(0, 0, 0, 0.05); -} - -html.dark .memento-sidebar-depth { - box-shadow: 4px 0 24px -8px rgba(0, 0, 0, 0.4); -} - html:not(.dark) .memento-active-nav { background: linear-gradient(to right, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.4)); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); @@ -164,12 +130,6 @@ html:not(.dark) .memento-active-nav { background: rgba(28, 28, 28, 0.2); } -.ai-glass { - background: rgba(255, 255, 255, 0.85); - backdrop-filter: blur(20px); - border: 1px solid rgba(255, 255, 255, 0.5); -} - .sidebar-shadow { box-shadow: 1px 0 10px rgba(0, 0, 0, 0.05); } @@ -188,41 +148,6 @@ html:not(.dark) .memento-active-nav { --ai-accent: #ACB995; } -/* Sidebar toggle view button (Notebooks / Agents) */ -.sidebar-view-toggle { - display: flex; - background: rgba(255, 255, 255, 0.5); - padding: 2px; - border-radius: 999px; - border: 1px solid var(--border); -} - -.sidebar-view-toggle-btn { - padding: 6px; - border-radius: 999px; - transition: all 150ms; - color: var(--muted-foreground); -} - -.sidebar-view-toggle-btn:hover { - color: var(--foreground); -} - -.sidebar-view-toggle-btn.active { - background: var(--foreground); - color: var(--background); -} - -/* Dark mode toggle */ -html.dark .sidebar-view-toggle { - background: rgba(255, 255, 255, 0.08); -} - -html.dark .sidebar-view-toggle-btn.active { - background: var(--primary); - color: var(--primary-foreground); -} - /* Inbox section separator */ .sidebar-inbox-item { display: flex; @@ -254,16 +179,6 @@ html.dark .sidebar-inbox-item.active { border: 1px solid rgba(255, 255, 255, 0.12); } -/* Animated tab indicator for AI panel */ -.ai-tab-indicator { - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 2px; - background: var(--foreground); -} - /* Note date in editorial view */ .note-date-badge { font-size: 11px; @@ -274,31 +189,12 @@ html.dark .sidebar-inbox-item.active { opacity: 0.7; } -/* Persian/Arabic: avoid uppercase + wide tracking on mixed scripts; bidi class set in TSX */ .note-date-badge.note-date-badge--locale-rtl { text-transform: none; letter-spacing: 0.08em; unicode-bidi: isolate; } -/* AI send button accent */ -.ai-send-btn { - background: var(--ai-accent); - color: white; - border-radius: 8px; - padding: 8px; - transition: transform 100ms, opacity 100ms; -} - -.ai-send-btn:hover { - opacity: 0.9; - transform: scale(1.05); -} - -.ai-send-btn:active { - transform: scale(0.95); -} - /* Dark mode active nav */ html.dark .memento-active-nav { background: rgba(255, 255, 255, 0.09); @@ -1125,36 +1021,6 @@ html.font-system * { pointer-events: none !important; } -/* ============================================ - Muuri Grid Styles for Drag & Drop - ============================================ */ -.muuri-grid { - position: relative; -} - -/* Note: Width is controlled by Tailwind classes (w-1/2, w-1/3, w-full, etc.) */ -.muuri-item { - position: absolute; - /* width: 100%; REMOVED - Don't override Tailwind size classes */ -} - -.muuri-item.muuri-item-dragging { - z-index: 3; -} - -.muuri-item.muuri-item-releasing { - z-index: 2; -} - -.muuri-item.muuri-item-hidden { - z-index: 0; -} - -/* Ensure note cards work properly with Muuri */ -.muuri-item>* { - width: 100%; -} - /* Force URLs/links to render LTR even in RTL mode */ [dir="rtl"] .prose a { direction: ltr; diff --git a/memento-note/components/brainstorm/brainstorm-canvas.tsx b/memento-note/components/brainstorm/brainstorm-canvas.tsx deleted file mode 100644 index b2c0f3f..0000000 --- a/memento-note/components/brainstorm/brainstorm-canvas.tsx +++ /dev/null @@ -1,386 +0,0 @@ -'use client' - -import React, { useCallback, useMemo, useRef, useState } from 'react' -import dynamic from 'next/dynamic' -import { BrainstormSession, BrainstormIdea } from '@/types/brainstorm' -import { - useExpandIdea, - useDismissIdea, - useConvertIdea, - useExportBrainstorm, - brainstormQuotaMessageKey, -} from '@/hooks/use-brainstorm' -import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet' -import { Button } from '@/components/ui/button' -import { Sparkles, X, FileText, Download } from 'lucide-react' -import { toast } from 'sonner' -import { useLanguage } from '@/lib/i18n' - -const ForceGraph2D = dynamic(() => import('react-force-graph-2d'), { - ssr: false, -}) - -interface GraphNode { - id: string - name: string - val: number - color: string - borderColor: string - wave: number - status: string - idea: BrainstormIdea - x?: number - y?: number - __bckgDimensions?: [number, number] -} - -interface GraphLink { - source: string | GraphNode - target: string | GraphNode - color: string -} - -const WAVE_COLORS: Record = { - 0: '#ffffff', - 1: '#f97316', - 2: '#3b82f6', - 3: '#a855f7', -} - -const WAVE_BORDER: Record = { - 0: '#e5e5e5', - 1: '#ea580c', - 2: '#2563eb', - 3: '#9333ea', -} - -const STATUS_ALPHA: Record = { - active: 1, - dismissed: 0.25, - converted: 0.9, -} - -interface BrainstormCanvasProps { - session: BrainstormSession -} - -export function BrainstormCanvas({ session }: BrainstormCanvasProps) { - const { t } = useLanguage() - const fgRef = useRef(null) - const [selectedIdea, setSelectedIdea] = useState(null) - const [isSheetOpen, setIsSheetOpen] = useState(false) - const expandIdea = useExpandIdea(session.id) - const dismissIdea = useDismissIdea(session.id) - const convertIdea = useConvertIdea(session.id) - const exportBrainstorm = useExportBrainstorm(session.id) - - const { graphData } = useMemo(() => { - const nodes: GraphNode[] = [] - const links: GraphLink[] = [] - - nodes.push({ - id: 'seed', - name: session.seedIdea.length > 30 ? session.seedIdea.slice(0, 30) + '...' : session.seedIdea, - val: 25, - color: WAVE_COLORS[0], - borderColor: WAVE_BORDER[0], - wave: 0, - status: 'active', - idea: { - id: 'seed', - sessionId: session.id, - waveNumber: 0, - title: session.seedIdea, - description: t('brainstorm.originalSeedDescription'), - connectionToSeed: null, - noveltyScore: null, - parentIdeaId: null, - convertedToNoteId: null, - relatedNoteIds: null, - status: 'active', - positionX: null, - positionY: null, - createdAt: session.createdAt, - } as BrainstormIdea, - }) - - for (const idea of session.ideas) { - const parentNode = idea.parentIdeaId - ? idea.parentIdeaId - : 'seed' - - nodes.push({ - id: idea.id, - name: idea.title, - val: idea.status === 'dismissed' ? 8 : 15, - color: WAVE_COLORS[idea.waveNumber] || WAVE_COLORS[3], - borderColor: idea.status === 'converted' ? '#22c55e' : (WAVE_BORDER[idea.waveNumber] || WAVE_BORDER[3]), - wave: idea.waveNumber, - status: idea.status, - idea, - }) - - const linkColor = WAVE_COLORS[idea.waveNumber] || WAVE_COLORS[3] - links.push({ - source: parentNode, - target: idea.id, - color: idea.status === 'dismissed' ? '#444444' : linkColor, - }) - } - - return { graphData: { nodes, links } } - }, [session, t]) - - const handleNodeClick = useCallback((node: any) => { - setSelectedIdea(node.idea) - setIsSheetOpen(true) - }, []) - - const handleExpand = useCallback(async () => { - if (!selectedIdea || selectedIdea.id === 'seed') return - try { - await expandIdea.mutateAsync({ ideaId: selectedIdea.id }) - toast.success(t('brainstorm.toastExpandSuccess')) - } catch (err: unknown) { - const quotaKey = brainstormQuotaMessageKey(err) - toast.error(quotaKey ? t(quotaKey) : (err instanceof Error ? err.message : t('brainstorm.toastExpandFailed'))) - } - }, [selectedIdea, expandIdea, t]) - - const handleDismiss = useCallback(async () => { - if (!selectedIdea || selectedIdea.id === 'seed') return - try { - await dismissIdea.mutateAsync(selectedIdea.id) - setIsSheetOpen(false) - setSelectedIdea(null) - toast.success(t('brainstorm.toastDismissSuccess')) - } catch (err: any) { - toast.error(err.message || t('brainstorm.toastDismissFailed')) - } - }, [selectedIdea, dismissIdea, t]) - - const handleConvert = useCallback(async () => { - if (!selectedIdea || selectedIdea.id === 'seed') return - try { - await convertIdea.mutateAsync(selectedIdea.id) - toast.success(t('brainstorm.toastConvertSuccess')) - } catch (err: any) { - toast.error(err.message || t('brainstorm.toastConvertFailed')) - } - }, [selectedIdea, convertIdea, t]) - - const handleExport = useCallback(async () => { - try { - const note = await exportBrainstorm.mutateAsync() - toast.success(t('brainstorm.toastExportNoteSuccess')) - } catch (err: any) { - toast.error(err.message || t('brainstorm.toastExportFailed')) - } - }, [exportBrainstorm, t]) - - const paintNode = useCallback((node: any, ctx: CanvasRenderingContext2D, globalScale: number) => { - const label = node.name - const fontSize = Math.max(12 / globalScale, 4) - ctx.font = `${fontSize}px Sans-Serif` - const textWidth = ctx.measureText(label).width - const nodeSize = node.val - - const bgDimensions: [number, number] = [textWidth + nodeSize * 0.8, nodeSize * 1.2] - node.__bckgDimensions = bgDimensions - - const alpha = STATUS_ALPHA[node.status] || 1 - ctx.globalAlpha = alpha - - ctx.fillStyle = node.color - ctx.strokeStyle = node.borderColor - ctx.lineWidth = node.status === 'converted' ? 3 / globalScale : 1.5 / globalScale - - ctx.beginPath() - ctx.roundRect( - node.x! - bgDimensions[0] / 2, - node.y! - bgDimensions[1] / 2, - bgDimensions[0], - bgDimensions[1], - nodeSize * 0.3 - ) - ctx.fill() - ctx.stroke() - - ctx.fillStyle = node.wave === 0 ? '#000000' : '#ffffff' - ctx.textAlign = 'center' - ctx.textBaseline = 'middle' - ctx.fillText(label, node.x!, node.y!) - - if (node.status === 'converted') { - ctx.fillStyle = '#22c55e' - ctx.font = `${fontSize * 1.2}px Sans-Serif` - ctx.fillText('✓', node.x! + bgDimensions[0] / 2 - fontSize * 0.5, node.y! - bgDimensions[1] / 2 + fontSize * 0.5) - } - - ctx.globalAlpha = 1 - }, []) - - const waveLegend = useMemo( - () => [ - { label: t('brainstorm.legendSeed'), color: WAVE_COLORS[0] }, - { label: t('brainstorm.legendVariations'), color: WAVE_COLORS[1] }, - { label: t('brainstorm.legendAnalogies'), color: WAVE_COLORS[2] }, - { label: t('brainstorm.legendDisruptions'), color: WAVE_COLORS[3] }, - ], - [t] - ) - - return ( -
- { - const dims = node.__bckgDimensions - if (!dims) return - ctx.fillStyle = color - ctx.beginPath() - ctx.roundRect(node.x! - dims[0] / 2, node.y! - dims[1] / 2, dims[0], dims[1], (node.val || 10) * 0.3) - ctx.fill() - }} - onNodeClick={handleNodeClick} - linkColor={(link: any) => link.color} - linkWidth={1} - linkDirectionalArrowLength={3} - linkDirectionalArrowRelPos={1} - backgroundColor="#09090b" - nodeVal={(node: any) => node.val} - cooldownTicks={100} - enableNodeDrag={true} - enableZoomInteraction={true} - enablePanInteraction={true} - warmupTicks={50} - /> - -
- {waveLegend.map(w => ( -
-
- {w.label} -
- ))} -
- -
- -
- - - - {selectedIdea && ( - <> - - - {selectedIdea.id === 'seed' ? session.seedIdea : selectedIdea.title} - - -
- {selectedIdea.description && selectedIdea.id !== 'seed' && ( -
-

{t('brainstorm.ideaDetailDescription')}

-

{selectedIdea.description}

-
- )} - - {selectedIdea.connectionToSeed && ( -
-

{t('brainstorm.ideaDetailConnection')}

-

{selectedIdea.connectionToSeed}

-
- )} - - {(selectedIdea.noveltyScore != null && selectedIdea.noveltyScore > 0) && ( -
-

{t('brainstorm.ideaDetailNovelty')}

-
-
-
-
- {selectedIdea.noveltyScore}/10 -
-
- )} - - {selectedIdea.waveNumber > 0 && ( -
-

{t('brainstorm.ideaDetailWave')}

- - {selectedIdea.waveNumber === 1 - ? t('brainstorm.waveFlavorVariation') - : selectedIdea.waveNumber === 2 - ? t('brainstorm.waveFlavorAnalogy') - : t('brainstorm.waveFlavorDisruption')} - -
- )} - - {selectedIdea.status === 'converted' && ( -
-

- - {t('brainstorm.convertedToNoteStatus')} -

-
- )} - - {selectedIdea.id !== 'seed' && selectedIdea.status === 'active' && ( -
- - - -
- )} -
- - )} - - -
- ) -} diff --git a/memento-note/components/brainstorm/brainstorm-create-dialog.tsx b/memento-note/components/brainstorm/brainstorm-create-dialog.tsx deleted file mode 100644 index c887496..0000000 --- a/memento-note/components/brainstorm/brainstorm-create-dialog.tsx +++ /dev/null @@ -1,84 +0,0 @@ -'use client' - -import React, { useState } from 'react' -import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { Button } from '@/components/ui/button' -import { Sparkles } from 'lucide-react' -import { useLanguage } from '@/lib/i18n' - -interface BrainstormCreateDialogProps { - open: boolean - onOpenChange: (open: boolean) => void - onSubmit: (seedIdea: string) => void - isLoading?: boolean -} - -export function BrainstormCreateDialog({ - open, - onOpenChange, - onSubmit, - isLoading, -}: BrainstormCreateDialogProps) { - const { t } = useLanguage() - const [seedIdea, setSeedIdea] = useState('') - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - if (!seedIdea.trim()) return - onSubmit(seedIdea.trim()) - setSeedIdea('') - } - - return ( - - - - - - {t('brainstorm.newBrainstorm')} - - -
-
- -