import type React from 'react' import { useEffect, useRef } from 'react' import type { useCreateBlockNote } from '@blocknote/react' import type { NoteStatus, VaultEntry } from '../../types' import { useEditorTheme } from '../../hooks/useTheme' import { resolveNoteIcon } from '../../utils/noteIcon' import { deriveEditorContentState } from './editorContentState' export interface Tab { entry: VaultEntry content: string } export interface EditorContentProps { activeTab: Tab | null isLoadingNewTab: boolean entries: VaultEntry[] editor: ReturnType diffMode: boolean diffContent: string | null diffLoading: boolean onToggleDiff: () => void rawMode: boolean onToggleRaw: () => void onRawContentChange?: (path: string, content: string) => void onSave?: () => void activeStatus: NoteStatus showDiffToggle: boolean showAIChat?: boolean onToggleAIChat?: () => void inspectorCollapsed: boolean onToggleInspector: () => void onNavigateWikilink: (target: string) => void onEditorChange?: () => void onToggleFavorite?: (path: string) => void onToggleOrganized?: (path: string) => void onDeleteNote?: (path: string) => void onArchiveNote?: (path: string) => void onUnarchiveNote?: (path: string) => void vaultPath?: string rawLatestContentRef?: React.MutableRefObject onTitleChange?: (path: string, newTitle: string) => void onRenameFilename?: (path: string, newFilenameStem: string) => void isConflicted?: boolean onKeepMine?: (path: string) => void onKeepTheirs?: (path: string) => void } function useBreadcrumbTitleVisibility({ showEditor, showTitleSection, path, breadcrumbBarRef, titleSectionRef, }: { showEditor: boolean showTitleSection: boolean path: string breadcrumbBarRef: React.RefObject titleSectionRef: React.RefObject }) { useEffect(() => { if (!showEditor) return const bar = breadcrumbBarRef.current const titleSection = titleSectionRef.current if (!bar || !titleSection) return if (!showTitleSection) { bar.setAttribute('data-title-hidden', '') return () => { bar.removeAttribute('data-title-hidden') } } const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) bar.removeAttribute('data-title-hidden') else bar.setAttribute('data-title-hidden', '') }, { threshold: 0 }, ) observer.observe(titleSection) return () => { observer.disconnect() bar.removeAttribute('data-title-hidden') } }, [path, showEditor, showTitleSection, breadcrumbBarRef, titleSectionRef]) } export function useEditorContentModel(props: EditorContentProps) { const { activeTab, entries, rawMode, activeStatus, diffMode, } = props const { cssVars } = useEditorTheme() const { isArchived, isDeletedPreview, isNonMarkdownText, effectiveRawMode, showEditor: showContentEditor, path, showTitleSection, wordCount, } = deriveEditorContentState({ activeTab, entries, rawMode, activeStatus, }) const showEditor = !diffMode && showContentEditor const entryIcon = activeTab?.entry.icon ?? null const hasDisplayIcon = resolveNoteIcon(entryIcon).kind !== 'none' const titleSectionRef = useRef(null) const breadcrumbBarRef = useRef(null) useBreadcrumbTitleVisibility({ showEditor, showTitleSection, path, breadcrumbBarRef, titleSectionRef, }) return { ...props, cssVars, isArchived, isDeletedPreview, effectiveRawMode, forceRawMode: isNonMarkdownText || isDeletedPreview, showEditor, entryIcon, hasDisplayIcon, path, showTitleSection, titleSectionRef, breadcrumbBarRef, wordCount, } }