// src/layout/MainAreaViewContainer.tsx import { Profiler, useContext, useEffect, useMemo, useRef } from 'react' import type { App } from '../plugin/app' import type { WorkspaceLeaf } from '../plugin/workspace' import { isReactItemView, type ReactItemView } from '../plugin/views/item-view' import { useActiveLeaf } from './hooks/useActiveLeaf' import { onRenderProfiler } from '../lib/perf-profile' import { MainAreaContext } from './MainAreaContext' import './MainAreaViewContainer.css' /** * Generic per-leaf renderer for the main editor area. Mirrors * `SidebarViewContainer.LeafMount`: * - When the active leaf hosts a `ReactItemView`, render * `view.getReactComponent()` inline (preserves React context). * - Otherwise mount `view.containerEl` into the wrapper via an effect. * * The wrapper carries `data-reams-view=""` so invariant tests and * QA can identify the currently mounted view without inspecting view * internals. * * @internal Host-internal infrastructure for content-type routing (#406). */ export function MainAreaViewContainer({ app }: { app: App }) { const activeLeaf = useActiveLeaf(app.workspace) if (!activeLeaf || !activeLeaf.view) { return (
Open a document to start editing
) } return ( ) } /** @internal Exported for tests; mounts a single leaf's view per the rules described above. */ export function LeafMount({ leaf }: { leaf: WorkspaceLeaf }) { const onInitialViewReady = useContext(MainAreaContext)?.onInitialViewReady // Mirror SidebarViewContainer.LeafMount: hooks run unconditionally and // guard inside their bodies, so a leaf whose `view` transitions to null // mid-lifecycle never changes the hook count between renders. const containerRef = useRef(null) const view = leaf.view const isReact = view != null && isReactItemView(view) const reactComponent = useMemo( () => (isReact ? (view as unknown as ReactItemView).getReactComponent() : null), [view, isReact, leaf.file], ) useEffect(() => { const el = containerRef.current if (!el || !view || reactComponent) return el.appendChild(view.containerEl) return () => { if (el.contains(view.containerEl)) { el.removeChild(view.containerEl) } } }, [view, reactComponent]) useEffect(() => { if (view && view.getViewType() !== 'markdown') onInitialViewReady?.(leaf) }, [leaf, leaf.file, view, onInitialViewReady]) if (!view) return null return (
{reactComponent}
) }