import type { useCreateBlockNote } from '@blocknote/react' import type { MutableRefObject, SetStateAction } from 'react' import type { VaultEntry } from '../types' import { hasDurableEditorBlocks } from '../utils/editorDurableMarkdown' import { serializeRichEditorDocumentToMarkdown, } from '../utils/richEditorMarkdown' interface Tab { entry: VaultEntry content: string } export interface PendingRawExitContent { path: string content: string } export interface RawModeContentTransition { pendingExitContent: PendingRawExitContent | null rawModeContentOverride: PendingRawExitContent | null } export function createRawModeContentTransition(): RawModeContentTransition { return { pendingExitContent: null, rawModeContentOverride: null, } } function resolvePendingRawExitContentAction( action: SetStateAction, current: PendingRawExitContent | null, ): PendingRawExitContent | null { return typeof action === 'function' ? action(current) : action } export function withPendingRawExitContent( transition: RawModeContentTransition, action: SetStateAction, ): RawModeContentTransition { return { ...transition, pendingExitContent: resolvePendingRawExitContentAction(action, transition.pendingExitContent), } } export function withRawModeContentOverride( transition: RawModeContentTransition, action: SetStateAction, ): RawModeContentTransition { return { ...transition, rawModeContentOverride: resolvePendingRawExitContentAction(action, transition.rawModeContentOverride), } } export function buildPendingRawExitContent( path: string | null, content: string | null, ): PendingRawExitContent | null { if (!path || content === null) return null return { path, content } } export function serializeEditorDocumentToMarkdown( editor: ReturnType, tabContent: string, vaultPath?: string, notePath?: string, ): string { return serializeRichEditorDocumentToMarkdown({ editor, notePath, tabContent, vaultPath }) } export function applyPendingRawExitContent( tabs: Tab[], pendingRawExitContent: PendingRawExitContent | null, ): Tab[] { if (!pendingRawExitContent) return tabs let changed = false const nextTabs = tabs.map((tab) => { if (tab.entry.path !== pendingRawExitContent.path || tab.content === pendingRawExitContent.content) { return tab } changed = true return { ...tab, content: pendingRawExitContent.content, } }) return changed ? nextTabs : tabs } function syncContentIfChanged({ currentContent, nextContent, path, onContentChange, }: { currentContent: string | null nextContent: string path: string onContentChange?: (path: string, content: string) => void }) { if (currentContent === nextContent) return onContentChange?.(path, nextContent) } export function syncActiveTabIntoRawBuffer(options: { editor: ReturnType activeTabPath: string | null activeTabContent: string | null rawLatestContentRef: MutableRefObject serializeRichEditorContent?: boolean vaultPath?: string }) { const { editor, activeTabPath, activeTabContent, rawLatestContentRef, serializeRichEditorContent = true, vaultPath, } = options if (!activeTabPath || activeTabContent === null) return null const shouldSerializeRichEditorContent = serializeRichEditorContent || hasDurableEditorBlocks(editor.document) const syncedContent = shouldSerializeRichEditorContent ? serializeEditorDocumentToMarkdown(editor, activeTabContent, vaultPath, activeTabPath) : activeTabContent rawLatestContentRef.current = syncedContent return syncedContent } export function rememberPendingRawExitContent(options: { activeTabPath: string | null activeTabContent: string | null rawInitialContent: string | null rawLatestContentRef: MutableRefObject onContentChange?: (path: string, content: string) => void }) { const { activeTabPath, activeTabContent, rawInitialContent, rawLatestContentRef, onContentChange, } = options const pendingRawExitContent = buildPendingRawExitContent(activeTabPath, rawLatestContentRef.current) if (!pendingRawExitContent) return null if (pendingRawExitContent.content === rawInitialContent) return null if (pendingRawExitContent.content === activeTabContent) return null syncContentIfChanged({ currentContent: activeTabContent, nextContent: pendingRawExitContent.content, path: pendingRawExitContent.path, onContentChange, }) return pendingRawExitContent } export function resolvePendingRawExitContent(options: { activeTabPath: string | null tabs: Tab[] pendingRawExitContent: PendingRawExitContent | null }) { const { activeTabPath, tabs, pendingRawExitContent } = options if (!pendingRawExitContent) return null if (activeTabPath !== pendingRawExitContent.path) { return null } const syncedTab = tabs.find((tab) => tab.entry.path === pendingRawExitContent.path) if (syncedTab?.content === pendingRawExitContent.content) { return null } return pendingRawExitContent } export function resolveRawModeContent(options: { activeTab: Tab | null rawModeContentOverride: PendingRawExitContent | null }) { const { activeTab, rawModeContentOverride } = options if (!activeTab) return null if (rawModeContentOverride?.path === activeTab.entry.path) { return rawModeContentOverride.content } return activeTab.content }