import type React from 'react' import { useCallback, useEffect, useRef } from 'react' import { cn } from '@/lib/utils' import { translate, type AppLocale } from '../../lib/i18n' import type { VaultEntry } from '../../types' import { dispatchEditorFindAvailability } from '../../utils/editorFindEvents' import { DiffView } from '../DiffView' import { BreadcrumbBar } from '../BreadcrumbBar' import { ArchivedNoteBanner } from '../ArchivedNoteBanner' import { ConflictNoteBanner } from '../ConflictNoteBanner' import { RawEditorView } from '../RawEditorView' import { SingleEditorView } from '../SingleEditorView' import type { useEditorContentModel } from './useEditorContentModel' type EditorContentModel = ReturnType type BreadcrumbActions = Pick< EditorContentModel, | 'diffMode' | 'diffLoading' | 'onToggleDiff' | 'effectiveRawMode' | 'onToggleRaw' | 'forceRawMode' | 'showAIChat' | 'onToggleAIChat' | 'showTableOfContents' | 'onToggleTableOfContents' | 'inspectorCollapsed' | 'onToggleInspector' | 'showDiffToggle' | 'onToggleFavorite' | 'onToggleOrganized' | 'onEnterNeighborhood' | 'onRevealFile' | 'onCopyFilePath' | 'onDeleteNote' | 'onArchiveNote' | 'onUnarchiveNote' | 'onRenameFilename' | 'noteWidth' | 'onToggleNoteWidth' > const LOADING_BREADCRUMB_ENTRY: VaultEntry = { path: '', filename: 'loading.md', title: '', isA: 'Note', aliases: [], belongsTo: [], relatedTo: [], status: null, archived: false, modifiedAt: null, createdAt: null, fileSize: 0, snippet: '', wordCount: 0, relationships: {}, icon: null, color: null, order: null, sidebarLabel: null, template: null, sort: null, view: null, visible: true, organized: false, favorite: false, favoriteIndex: null, listPropertiesDisplay: [], outgoingLinks: [], properties: {}, hasH1: false, fileKind: 'markdown', } function EditorLoadingSkeleton() { return (
) } function EditorLoadingCanvas({ cssVars }: Pick) { return (
) } function DiffModeView({ diffContent, locale = 'en', onToggleDiff }: { diffContent: string | null; locale?: AppLocale; onToggleDiff: () => void }) { const label = translate(locale, 'editor.toolbar.rawReturn') return (
) } function RawModeEditorSection({ activeTab, entries, findRequest, rawMode, rawModeContent, onRawContentChange, onSave, rawLatestContentRef, vaultPath, locale, }: Pick< EditorContentModel, 'activeTab' | 'entries' | 'findRequest' | 'onRawContentChange' | 'onSave' | 'rawLatestContentRef' | 'rawModeContent' | 'vaultPath' > & { rawMode: boolean locale?: AppLocale }) { if (!rawMode || !activeTab) return null return ( {})} onSave={onSave ?? (() => {})} latestContentRef={rawLatestContentRef} vaultPath={vaultPath} locale={locale} /> ) } function bindPath(cb: ((path: string) => void) | undefined, path: string) { return cb ? () => cb(path) : undefined } function ActiveTabBreadcrumb({ activeTab, barRef, wordCount, path, actions, locale, loadingTitle, }: { activeTab: NonNullable barRef: React.RefObject wordCount: number path: string actions: BreadcrumbActions locale?: AppLocale loadingTitle?: boolean }) { return ( ) } function EditorLoadingBreadcrumb({ actions, barRef, locale, }: { actions: BreadcrumbActions barRef: React.RefObject locale?: AppLocale }) { return ( ) } function buildBreadcrumbActions(model: EditorContentModel): BreadcrumbActions { return { diffMode: model.diffMode, diffLoading: model.diffLoading, onToggleDiff: model.onToggleDiff, effectiveRawMode: model.effectiveRawMode, onToggleRaw: model.onToggleRaw, forceRawMode: model.forceRawMode, showAIChat: model.showAIChat, onToggleAIChat: model.onToggleAIChat, showTableOfContents: model.showTableOfContents, onToggleTableOfContents: model.onToggleTableOfContents, inspectorCollapsed: model.inspectorCollapsed, onToggleInspector: model.onToggleInspector, showDiffToggle: model.showDiffToggle, onToggleFavorite: model.onToggleFavorite, onToggleOrganized: model.onToggleOrganized, onEnterNeighborhood: model.onEnterNeighborhood, onRevealFile: model.onRevealFile, onCopyFilePath: model.onCopyFilePath, onDeleteNote: model.onDeleteNote, onArchiveNote: model.onArchiveNote, onUnarchiveNote: model.onUnarchiveNote, onRenameFilename: model.onRenameFilename, noteWidth: model.noteWidth, onToggleNoteWidth: model.onToggleNoteWidth, } } function EditorBreadcrumbArea({ actions, barRef, chromePath, chromeTab, chromeWordCount, isVaultLoading, locale, }: { actions: BreadcrumbActions barRef: React.RefObject chromePath: string chromeTab: EditorContentModel['activeTab'] | EditorContentModel['loadingTab'] chromeWordCount: number isVaultLoading?: boolean locale?: AppLocale }) { if (chromeTab) { return ( ) } if (!isVaultLoading) return null return ( ) } function EditorChrome({ isArchived, onUnarchiveNote, path, isConflicted, onKeepMine, onKeepTheirs, diffMode, diffContent, onToggleDiff, locale, }: Pick< EditorContentModel, 'isArchived' | 'onUnarchiveNote' | 'path' | 'isConflicted' | 'onKeepMine' | 'onKeepTheirs' | 'diffMode' | 'diffContent' | 'onToggleDiff' | 'locale' >) { return ( <> {isArchived && onUnarchiveNote && ( onUnarchiveNote(path)} locale={locale} /> )} {isConflicted && ( onKeepMine?.(path)} onKeepTheirs={() => onKeepTheirs?.(path)} locale={locale} /> )} {diffMode && } ) } function EditorCanvas({ showEditor, cssVars, editor, entries, onNavigateWikilink, onEditorChange, isDeletedPreview, vaultPath, locale, }: Pick< EditorContentModel, | 'showEditor' | 'cssVars' | 'editor' | 'entries' | 'onNavigateWikilink' | 'onEditorChange' | 'isDeletedPreview' | 'vaultPath' | 'locale' >) { if (!showEditor) return null return (
) } function EditorFindScope({ children, className, style, }: { children: React.ReactNode className?: string style?: React.CSSProperties }) { const scopeRef = useRef(null) const syncAvailability = useCallback(() => { const activeElement = document.activeElement const enabled = activeElement instanceof Node && scopeRef.current?.contains(activeElement) === true dispatchEditorFindAvailability(enabled) }, []) useEffect(() => () => dispatchEditorFindAvailability(false), []) return (
dispatchEditorFindAvailability(true)} onBlurCapture={() => requestAnimationFrame(syncAvailability)} style={style} > {children}
) } export function EditorContentLayout(model: EditorContentModel) { const { activeTab, loadingTab, isLoadingNewTab, entries, editor, diffMode, diffContent, onToggleDiff, effectiveRawMode, onRawContentChange, onSave, showEditor, isArchived, onUnarchiveNote, path, isConflicted, onKeepMine, onKeepTheirs, breadcrumbBarRef, wordCount, vaultPath, cssVars, onNavigateWikilink, onEditorChange, isDeletedPreview, rawLatestContentRef, rawModeContent, noteWidth, findRequest, locale, isVaultLoading, } = model const rootClassName = cn( 'flex flex-1 flex-col min-w-0 min-h-0', noteWidth === 'wide' ? 'editor-content-width--wide' : 'editor-content-width--normal', ) const chromeTab = activeTab ?? loadingTab const chromePath = chromeTab?.entry.path ?? path const chromeWordCount = activeTab ? wordCount : 0 const showActiveContent = activeTab && !isVaultLoading const showLoadingContent = isVaultLoading || (isLoadingNewTab && showEditor) const breadcrumbActions = buildBreadcrumbActions(model) return (
{showActiveContent && ( <> )} {showLoadingContent && }
) }