'use client'
import { useState, useRef } from 'react'
import { useNoteEditorContext } from './note-editor-context'
import { LabelManager } from '@/components/label-manager'
import { LabelBadge } from '@/components/label-badge'
import { GhostTags } from '@/components/ghost-tags'
import { EditorImages } from '@/components/editor-images'
import { TitleSuggestions } from '@/components/title-suggestions'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import {
X, Plus, Palette, Image as ImageIcon, Bell, Eye, Link as LinkIcon, Sparkles,
Maximize2, Copy, ArrowLeft, ChevronRight, PanelRight, Check, Loader2, Save, MoreHorizontal,
Trash2, LogOut, Wand2, Share2, Wind, Paperclip, GraduationCap
} from 'lucide-react'
import { FlashcardGenerateDialog } from '@/components/flashcards/flashcard-generate-dialog'
import { NoteShareDialog } from './note-share-dialog'
import { deleteNote, leaveSharedNote } from '@/app/actions/notes'
import { emitNoteChange } from '@/lib/note-change-sync'
import { useLanguage } from '@/lib/i18n'
import { NOTE_COLORS, NoteColor, Note } from '@/lib/types'
import { cn } from '@/lib/utils'
import { toast } from 'sonner'
import { format } from 'date-fns'
interface NoteEditorToolbarProps {
mode: 'fullPage' | 'dialog'
onClose: () => void
onToggleAttachments?: () => void
attachmentsCount?: number
}
export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachmentsCount }: NoteEditorToolbarProps) {
const { state, actions, note, readOnly, fullPage, notebooks, fileInputRef } = useNoteEditorContext()
const { t } = useLanguage()
const [isConverting, setIsConverting] = useState(false)
const [shareOpen, setShareOpen] = useState(false)
const [flashcardsOpen, setFlashcardsOpen] = useState(false)
const notebookName = notebooks.find(nb => nb.id === note.notebookId)?.name || null
const undoSnapshotRef = useRef<{ content: string; isMarkdown: boolean } | null>(null)
const handleConvertToRichtext = async () => {
if (isConverting || !state.content.trim()) return
setIsConverting(true)
const snapshot = { content: state.content, isMarkdown: state.isMarkdown }
undoSnapshotRef.current = snapshot
try {
let html: string
if (state.isMarkdown) {
const { marked } = await import('marked')
html = await marked(state.content, { async: false }) as string
} else {
html = state.content
.split(/\n{2,}/)
.map(para => `
${para.trim().replace(/\n/g, '
')}
`)
.join('')
}
actions.setContent(html)
actions.setIsMarkdown(false)
toast.success(t('notes.convertedToRichText') || 'Converted to rich text', {
duration: 8000,
action: {
label: t('notes.undo') || '↩ Undo',
onClick: () => {
const snap = undoSnapshotRef.current
if (!snap) return
actions.setContent(snap.content)
if (snap.isMarkdown) actions.setIsMarkdown(true)
undoSnapshotRef.current = null
toast.info(t('ai.undoApplied') || 'Conversion undone')
},
},
})
} catch {
toast.error(t('notes.transformFailed') || 'Conversion failed')
} finally {
setIsConverting(false)
}
}
if (mode === 'fullPage') {
const handleCloseWithSave = async () => {
if (state.isDirty && !state.isSaving) {
await actions.handleSaveInPlace()
}
onClose()
}
return (
{state.isSaving
? <>{t('notes.saving')}>
: state.isDirty
? <>{t('notes.dirtyStatus')}>
: <>{t('notes.savedStatus')}>}
{state.isMarkdown && !readOnly && (
)}
{state.isMarkdown && !readOnly && (
)}
{!readOnly && (
)}
{!readOnly && onToggleAttachments && (
)}
{!readOnly && (
)}
{!readOnly && (
)}
{!readOnly && (
{
try {
await deleteNote(note.id, { skipRevalidation: true })
emitNoteChange({ type: 'deleted', noteId: note.id, notebookId: note.notebookId })
toast.success(t('notes.noteDeletedToast'))
onClose()
} catch { toast.error(t('notes.deleteNoteFailedToast')) }
}}
className="text-red-600 dark:text-red-400 focus:text-red-600"
>
{t('notes.deleteNoteConfirmItem')}
)}
{shareOpen && (
setShareOpen(false)}
/>
)}
setFlashcardsOpen(false)}
noteId={note.id}
noteTitle={state.title || note.title || 'Untitled'}
onSaved={(deckId) => {
toast.success(t('flashcards.savedCount', { count: '' }).replace('{count}', ''), {
description: t('flashcards.reviewNow') || 'Review now',
action: {
label: t('flashcards.reviewNow') || 'Review now →',
onClick: () => {
window.open(`/revision?deckId=${encodeURIComponent(deckId)}`, '_self')
},
},
duration: 8000,
})
}}
/>
)
}
return (
{!readOnly && (
<>
{state.isMarkdown && (
)}
{(['small', 'medium', 'large'] as const).map((s) => (
))}
{Object.entries(NOTE_COLORS).map(([colorName, classes]) => (
>
)}
{readOnly && (
{t('notes.sharedReadOnly')}
)}
{readOnly ? (
<>
>
) : (
<>
>
)}
)
}