Files
Momento/memento-note/components/note-editor/note-editor-split-peek.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

105 lines
4.2 KiB
TypeScript

'use client'
import { useEffect, useRef } from 'react'
import { motion } from 'framer-motion'
import { X, Maximize2 } from 'lucide-react'
import type { Note } from '@/lib/types'
import { useLanguage } from '@/lib/i18n'
import { NoteEditorProvider, useNoteEditorContext } from './note-editor-context'
import { NoteTitleBlock } from './note-title-block'
import { NoteContentArea } from './note-content-area'
import { formatAbsoluteDateLocalized } from '@/lib/utils/format-localized-date'
import { fr } from 'date-fns/locale/fr'
import { enUS } from 'date-fns/locale/en-US'
interface NoteEditorSplitPeekProps {
note: Note
blockId?: string
onClose: () => void
onOpenFully: () => void
}
function PeekEditorBody({ blockId }: { blockId?: string }) {
const { note } = useNoteEditorContext()
const { t, language } = useLanguage()
const dateLocale = language === 'fr' ? fr : enUS
const scrollRootRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!blockId) return
const timer = window.setTimeout(() => {
const escaped = typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(blockId) : blockId
const el = scrollRootRef.current?.querySelector(`[data-id="${escaped}"]`)
el?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}, 450)
return () => window.clearTimeout(timer)
}, [blockId, note.id])
return (
<div ref={scrollRootRef} className="flex-1 min-h-0 overflow-y-auto">
<div className="max-w-2xl mx-auto w-full px-6 sm:px-8 py-10 space-y-8 pb-24">
<p
className="text-[10px] uppercase tracking-[.25em] font-bold text-[var(--color-concrete)]"
suppressHydrationWarning
>
{formatAbsoluteDateLocalized(new Date(note.contentUpdatedAt), language, 'MMM d, yyyy', dateLocale)}
</p>
<NoteTitleBlock />
<div className="max-w-xl mx-auto w-full">
<NoteContentArea />
</div>
<p className="text-[11px] text-[var(--color-concrete)] italic">{t('notePeek.readOnlyHint')}</p>
</div>
</div>
)
}
export function NoteEditorSplitPeek({ note, blockId, onClose, onOpenFully }: NoteEditorSplitPeekProps) {
const { t, language } = useLanguage()
const isRtl = language === 'fa' || language === 'ar'
return (
<motion.aside
initial={{ width: 0, opacity: 0 }}
animate={{ width: 'min(50vw, 720px)', opacity: 1 }}
exit={{ width: 0, opacity: 0 }}
transition={{ type: 'spring', stiffness: 340, damping: 34 }}
className={`shrink-0 h-full min-h-0 bg-[#fafaf9] dark:bg-zinc-950 flex flex-col overflow-hidden z-40 ${
isRtl
? 'border-r border-black/10 dark:border-white/10 shadow-[4px_0_24px_-12px_rgba(0,0,0,0.12)]'
: 'border-l border-black/10 dark:border-white/10 shadow-[-4px_0_24px_-12px_rgba(0,0,0,0.12)]'
}`}
aria-label={t('notePeek.panelLabel')}
>
<NoteEditorProvider note={note} readOnly fullPage>
<div className="shrink-0 px-4 py-2.5 flex items-center justify-between gap-3 border-b border-black/[0.06] dark:border-white/[0.06] bg-white/80 dark:bg-zinc-900/80 backdrop-blur-sm">
<span className="text-[10px] font-bold uppercase tracking-[0.2em] text-[var(--color-concrete)] truncate">
{t('notePeek.label')}
</span>
<div className="flex items-center gap-1 shrink-0">
<button
type="button"
onClick={onOpenFully}
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-[10px] font-bold uppercase tracking-wide text-blue-600 dark:text-blue-400 hover:bg-blue-500/10 transition-colors"
title={t('notePeek.openFullyHelp')}
>
<Maximize2 size={12} />
{t('notePeek.openFully')}
</button>
<button
type="button"
onClick={onClose}
className="p-1.5 rounded-lg text-[var(--color-concrete)] hover:text-[var(--color-ink)] hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
title={t('notePeek.close')}
aria-label={t('notePeek.close')}
>
<X size={16} />
</button>
</div>
</div>
<PeekEditorBody blockId={blockId} />
</NoteEditorProvider>
</motion.aside>
)
}