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>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user