/* Copyright 2026 Marimo. All rights reserved. */ /* oxlint-disable typescript/no-explicit-any */ import { useCallback, useEffect, useRef, useState } from "react"; import { z } from "zod"; import { MarimoIncomingMessageEvent } from "@/core/dom/events"; import { type HTMLElementNotDerivedFromRef, useEventListener, } from "@/hooks/useEventListener"; import { createPlugin } from "@/plugins/core/builder"; import { rpc } from "@/plugins/core/rpc"; import { isTrustedVirtualFileUrl } from "@/plugins/core/trusted-url"; import type { IPluginProps } from "@/plugins/types"; import { Logger } from "@/utils/Logger"; import { EventBuffer, extractBuffers, MessageSchema } from "./utils"; interface BokehDocument { create_json_patch: (events: unknown[]) => unknown; apply_json_patch: (content: unknown, buffers: ArrayBuffer[]) => void; on_change: (callback: (event: unknown) => void) => void; _all_models: Map; } interface RenderItems { roots: Record; [key: string]: unknown; } declare global { interface Window { Bokeh: { embed: { embed_items_notebook: ( docs_json: Record, render_items: unknown[], ) => Promise; }; index: { get_by_id: (id: string) => { model: { document: BokehDocument } }; }; protocol: { Message: { create: ( type: string, metadata: Record, content: unknown, ) => Record; }; Receiver: new () => { consume: (data: ArrayBuffer | string) => void; message: { content: { events?: { model?: { id?: string }; }[]; [key: string]: unknown; }; buffers: ArrayBuffer[]; } | null; }; }; }; } } interface PanelData { extensionUrl: string | null; docs_json: Record; render_json: { roots: Record; [key: string]: unknown; }; } type T = Record; // oxlint-disable-next-line typescript/consistent-type-definitions type PluginFunctions = { send_to_widget: (req: { message?: unknown; buffers?: unknown; }) => Promise; }; export const PanelPlugin = createPlugin("marimo-panel") .withData( z.object({ extensionUrl: z.string().nullable(), docs_json: z.record(z.string(), z.unknown()), render_json: z .object({ roots: z.record(z.string(), z.string()), }) .catchall(z.unknown()), }), ) .withFunctions({ send_to_widget: rpc .input( z.object({ message: z.unknown(), buffers: z.array(z.string()), }), ) .output(z.null().optional()), }) .renderer((props) => ); function isBokehLoaded() { return window.Bokeh != null; } /** * Append a `