import { useEffect, useMemo, useState } from 'react' import type { App } from '../plugin/app' import type { TemplatePlugin } from '../plugins/templates' interface UseReactiveTemplatePluginOptions { active: boolean app: App } /** * Duck-typed shape check for TemplatePlugin. The bare `as` cast in getPlugin() * would let any plugin registered under 'reams-templates' through, including a * partial mock from a malformed plugin manifest. Guard with a duck-typed shape * check so consumers don't get TypeError when calling the required methods. * * Checks all methods used by FileTree and AppShell consumers: * - createFileFromTemplate (file creation) * - expandFilenamePattern (filename preview) * - getTemplates (template list) * - getDefaultTemplate (default template path) * - getFilenamePattern (pattern for filename preview) */ export function isValidTemplatePlugin(candidate: unknown): candidate is TemplatePlugin { if (candidate === null || typeof candidate !== 'object') return false const obj = candidate as Record return ( typeof obj.createFileFromTemplate === 'function' && typeof obj.expandFilenamePattern === 'function' && typeof obj.getTemplates === 'function' && typeof obj.getDefaultTemplate === 'function' && typeof obj.getFilenamePattern === 'function' ) } /** * React hook for reactively accessing the template plugin. * * Uses app.plugins.getPlugin('reams-templates') directly and subscribes to * plugin state changes via app.plugins.on('plugin-state-change', ...). * * @param options.active When true, subscribes to plugin state changes * @param options.app The App instance to get the plugin from * @returns { plugin, version } where plugin is the TemplatePlugin or null, * and version increments on state changes for useMemo dependency */ export function useReactiveTemplatePlugin({ active, app, }: UseReactiveTemplatePluginOptions): { plugin: TemplatePlugin | null; version: number } { const [version, setVersion] = useState(0) useEffect(() => { if (!active) return const ref = app.plugins.on('plugin-state-change', (pluginId) => { if (pluginId === 'reams-templates') { setVersion(current => current + 1) } }) return () => { app.plugins.offref(ref) } }, [active, app]) const plugin = useMemo(() => { if (!active) return null const candidate = app.plugins.getPlugin('reams-templates') return isValidTemplatePlugin(candidate) ? candidate : null }, [active, app, version]) return { plugin, version } }