import type {ReadonlySignal, Signal} from '@preact/signals'; import {computed, signal, useSignalEffect} from '@preact/signals'; import type {Scene2D} from '@revideo/2d'; import {SceneRenderEvent} from '@revideo/core'; import {useApplication, useCurrentScene} from '@revideo/ui'; import type {ComponentChildren} from 'preact'; import {createContext} from 'preact'; import {useContext, useMemo} from 'preact/hooks'; export interface PluginState { selectedKey: Signal; hoveredKey: Signal; openNodes: Map; scene: ReadonlySignal; selectedChain: ReadonlySignal>; afterRender: ReadonlySignal; } const PluginContext = createContext(null); export const NodeInspectorKey = '@revideo/2d/node-inspector'; export function usePluginState() { return useContext(PluginContext)!; } export function Provider({children}: {children?: ComponentChildren}) { const {inspection} = useApplication(); const currentScene = useCurrentScene(); const state = useMemo(() => { const scene = signal(currentScene as Scene2D); const selectedKey = signal(null); const afterRender = signal(0); const hoveredKey = signal(null); const openNodes = new Map(); const selectedChain = computed(() => { const chain = new Set(); const key = selectedKey.value; const selectedNode = scene.value?.getNode(key); if (selectedNode) { let node = selectedNode.parent() ?? null; while (node) { chain.add(node.key); node = node.parent(); } } return chain; }); return { selectedKey, hoveredKey, afterRender, openNodes, selectedChain, scene, } satisfies PluginState; }, []); state.scene.value = currentScene as Scene2D; useSignalEffect(() => state.scene.value?.onRenderLifecycle.subscribe(([event]) => { if (event === SceneRenderEvent.AfterRender) { state.afterRender.value++; } }), ); useSignalEffect(() => { const {key, payload} = inspection.value; if (key === NodeInspectorKey) { state.selectedKey.value = payload as string; } }); useSignalEffect(() => { const nodeKey = state.selectedKey.value; const {key, payload} = inspection.peek(); if (key === NodeInspectorKey && !nodeKey) { inspection.value = {key: '', payload: null}; } else if (payload !== nodeKey) { inspection.value = {key: NodeInspectorKey, payload: nodeKey}; } }); return ( {children} ); }