Files
Momento/memento-note/components/note-editor/note-editor-peek-host.tsx
Antigravity f46654f574 feat: editor improvements and architectural grid prototype
Multiple feature additions and improvements across the application:

- NextGen Editor: drag handles, smart paste, block actions
- Structured views: Kanban and table layouts for notes
- Architectural Grid: new brainstorming/agent interface prototype
- Flashcards: SM-2 revision algorithm with AI generation
- MCP server: robustness improvements
- Graph/PDF chat: fix click propagation and copy behavior
- Various UI/UX enhancements and bug fixes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 19:45:15 +00:00

91 lines
3.1 KiB
TypeScript

'use client'
import { useState, useEffect, useCallback, type ReactNode } from 'react'
import { AnimatePresence } from 'framer-motion'
import { useRouter, useSearchParams } from 'next/navigation'
import { toast } from 'sonner'
import type { Note } from '@/lib/types'
import { getNoteById } from '@/app/actions/notes'
import { NOTE_REQUEST_SAVE_EVENT } from '@/lib/note-change-sync'
import {
NOTE_PEEK_OPEN_EVENT,
NOTE_PEEK_CLOSE_EVENT,
type NotePeekOpenDetail,
} from '@/lib/note-peek-sync'
import { NoteEditorSplitPeek } from './note-editor-split-peek'
import { useLanguage } from '@/lib/i18n'
interface NoteEditorPeekHostProps {
noteId: string
fullPage?: boolean
children: ReactNode
}
export function NoteEditorPeekHost({ noteId, fullPage, children }: NoteEditorPeekHostProps) {
const router = useRouter()
const searchParams = useSearchParams()
const { t, language } = useLanguage()
const isRtl = language === 'fa' || language === 'ar'
const [peekState, setPeekState] = useState<{ note: Note; blockId?: string } | null>(null)
useEffect(() => {
const onOpenPeek = (event: Event) => {
const detail = (event as CustomEvent<NotePeekOpenDetail>).detail
if (!detail?.noteId) return
if (detail.noteId === noteId) return
void getNoteById(detail.noteId).then((fetched) => {
if (fetched) {
setPeekState({ note: fetched, blockId: detail.blockId })
} else {
toast.error(t('notePeek.loadFailed'))
}
})
}
const onClosePeek = () => setPeekState(null)
window.addEventListener(NOTE_PEEK_OPEN_EVENT, onOpenPeek)
window.addEventListener(NOTE_PEEK_CLOSE_EVENT, onClosePeek)
return () => {
window.removeEventListener(NOTE_PEEK_OPEN_EVENT, onOpenPeek)
window.removeEventListener(NOTE_PEEK_CLOSE_EVENT, onClosePeek)
}
}, [noteId, t])
const handleClosePeek = useCallback(() => {
setPeekState(null)
}, [])
const handleOpenPeekFully = useCallback(() => {
if (!peekState) return
window.dispatchEvent(new CustomEvent(NOTE_REQUEST_SAVE_EVENT, {
detail: { noteId, reason: 'before-peek-full-open' },
}))
const params = new URLSearchParams(searchParams.toString())
params.set('openNote', peekState.note.id)
router.replace(params.toString() ? `/home?${params.toString()}` : '/home', { scroll: false })
setPeekState(null)
}, [noteId, peekState, router, searchParams])
const shellClass = fullPage
? 'flex flex-1 min-h-0 h-full w-full items-stretch overflow-hidden'
: 'relative flex min-h-0 flex-1 flex-col overflow-hidden'
return (
<div className={`${shellClass}${peekState ? (isRtl ? ' flex-row-reverse' : ' flex-row') : ''}`}>
<div className="flex-1 min-w-0 flex flex-col overflow-hidden">{children}</div>
<AnimatePresence initial={false}>
{peekState && (
<NoteEditorSplitPeek
key={peekState.note.id}
note={peekState.note}
blockId={peekState.blockId}
onClose={handleClosePeek}
onOpenFully={handleOpenPeekFully}
/>
)}
</AnimatePresence>
</div>
)
}