/** * Lazy chunk view wrapper: mounts a component that lives in a lazy chunk, * showing a loading placeholder while the chunk script loads and an error + * retry affordance on failure. Used by the built-in tab/viewer descriptors. * * Contract note: {@link lazyChunkComponent} returns a plain render-prop * function — the descriptor contract is `component: (props) => ReactNode`, * and the repo renders descriptors BOTH ways: Sidebar calls * `descriptor.component(props)` directly, EditorHost renders it via * `createElement`. The wrapper function body therefore contains no hooks; * all state lives in the inner {@link LazyChunkView} component. */ import { createElement, useEffect, useState, type ComponentType, type ReactNode } from 'react' import { loadChunk, type ChunkExports, type ChunkName } from './chunk-loader.ts' import { t } from './locales.ts' import css from './sidebar.module.css' interface LazyChunkViewProps
{ chunk: ChunkName /** Module-level-stable selector (an inline lambda would re-trigger the effect). */ pick: (mod: ChunkExports) => ComponentType
| undefined props: P } function LazyChunkView
({ chunk, pick, props }: LazyChunkViewProps
): ReactNode { const [attempt, setAttempt] = useState(0) const [state, setState] = useState< | { status: 'loading' } | { status: 'error'; message: string } | { status: 'ready'; Comp: ComponentType
} >({ status: 'loading' }) useEffect(() => { let cancelled = false setState({ status: 'loading' }) loadChunk(chunk).then((mod) => { if (cancelled) return const Comp = pick(mod) if (Comp === undefined) { setState({ status: 'error', message: `[dsh-better-sidebar] chunk "${chunk}" is missing its component` }) return } setState({ status: 'ready', Comp }) }).catch((error: unknown) => { if (cancelled) return setState({ status: 'error', message: error instanceof Error ? error.message : String(error) }) }) return () => { cancelled = true } }, [chunk, pick, attempt]) if (state.status === 'loading') { return
( chunk: ChunkName, pick: (mod: ChunkExports) => ComponentType
| undefined,
): (props: P) => ReactNode {
return (props: P) => createElement(
LazyChunkView as ComponentType