This repository has no description
1'use client';
2
3import { createContext, use, useEffect, useRef, useState } from 'react';
4import { newMessagePortRpcSession, type RpcStub } from 'capnweb';
5
6type EmbedBlockData =
7 | { type: 'text'; content: string }
8 | {
9 type: 'embed';
10 url: string;
11 height?: number;
12 aspectRatio?: string;
13 };
14
15// Define the interface the *host* exposes (methods you can call)
16export type HostMethods = {
17 open(url: string): void;
18 replaceWith(block: EmbedBlockData): void;
19 addBelow(block: EmbedBlockData): void;
20};
21
22export type HostSession = RpcStub<HostMethods>;
23
24const RpcSessionContext = createContext<HostSession | null>(null);
25
26function connectToHost(): Promise<HostSession> {
27 return new Promise((resolve) => {
28 // Listen for the host to send us a MessagePort
29 function handleMessage(event: MessageEvent) {
30 if (event.data?.type !== 'parts.page.channel') return;
31
32 const port = event.ports[0];
33 if (!port) return;
34
35 // Start the RPC session over the transferred port
36 const session = newMessagePortRpcSession(port, null) as HostSession;
37 resolve(session);
38
39 // Remove listener once connected
40 window.removeEventListener('message', handleMessage);
41 }
42
43 window.addEventListener('message', handleMessage);
44
45 // Signal to the host that we want to connect
46 window.parent.postMessage({ type: 'parts.page.connect' }, '*');
47 });
48}
49
50export function RpcSessionProvider({
51 children,
52}: {
53 children: React.ReactNode;
54}) {
55 const [session, setSession] = useState<HostSession | null>(null);
56 const connectingRef = useRef(false);
57
58 useEffect(() => {
59 // De-dupe: only connect once even in StrictMode double-mount
60 if (connectingRef.current) return;
61 connectingRef.current = true;
62
63 connectToHost().then((session) => setSession(() => session));
64
65 return () => {
66 // Cleanup on unmount
67 session?.[Symbol.dispose]();
68 };
69 }, [session]);
70
71 return <RpcSessionContext value={session}>{children}</RpcSessionContext>;
72}
73
74export function useRpcSession() {
75 return use(RpcSessionContext);
76}