| import type { HostEvent } from "./types"; |
|
|
| const READY = "streamlit:componentReady"; |
| const RENDER = "streamlit:render"; |
| const SET_VALUE = "streamlit:setComponentValue"; |
| const SET_HEIGHT = "streamlit:setFrameHeight"; |
| const HOST_RENDER_EVENT = "aios:grid-host-render"; |
|
|
| type HostWindow = Window & { |
| __AIOS_STREAMLIT_ARGS__?: Record<string, unknown>; |
| }; |
|
|
| function post(type: string, extra: Record<string, unknown>): void { |
| window.parent.postMessage( |
| { isStreamlitMessage: true, type, ...extra }, |
| "*" |
| ); |
| } |
|
|
| export function isStreamlitComponent(): boolean { |
| if (typeof window === "undefined" || window.parent === window) return false; |
| return new URLSearchParams(window.location.search).has("streamlitUrl"); |
| } |
|
|
| |
| |
| |
| |
| export function initializeHostBridge(): void { |
| if (!isStreamlitComponent()) return; |
| window.addEventListener("message", (event: MessageEvent) => { |
| const data = event.data as |
| | { type?: string; args?: Record<string, unknown> } |
| | undefined; |
| if (!data || data.type !== RENDER) return; |
| (window as HostWindow).__AIOS_STREAMLIT_ARGS__ = data.args ?? {}; |
| window.dispatchEvent(new CustomEvent(HOST_RENDER_EVENT)); |
| const height = Number(data.args?.height ?? 760); |
| post(SET_HEIGHT, { height: Number.isFinite(height) ? height : 760 }); |
| }); |
| post(READY, { apiVersion: 1 }); |
| } |
|
|
| export function readHostArgs(): Record<string, unknown> | null { |
| return (window as HostWindow).__AIOS_STREAMLIT_ARGS__ ?? null; |
| } |
|
|
| export function subscribeHostRender(listener: () => void): () => void { |
| window.addEventListener(HOST_RENDER_EVENT, listener); |
| return () => window.removeEventListener(HOST_RENDER_EVENT, listener); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const PENDING: HostEvent[] = []; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| type EventSink = (event: HostEvent) => boolean; |
| let standaloneSink: EventSink | null = null; |
|
|
| export function setStandaloneSink(sink: EventSink | null): void { |
| standaloneSink = sink; |
| } |
|
|
| export function emitHostEvent(event: HostEvent): boolean { |
| if (!isStreamlitComponent()) return standaloneSink ? standaloneSink(event) : false; |
| PENDING.push(event); |
| if (PENDING.length > 24) PENDING.shift(); |
| post(SET_VALUE, { value: { events: [...PENDING] }, dataType: "json" }); |
| return true; |
| } |
|
|
| export function eventId(prefix: string): string { |
| const cryptoId = |
| typeof crypto !== "undefined" && "randomUUID" in crypto |
| ? crypto.randomUUID() |
| : `${Date.now()}-${Math.random().toString(36).slice(2)}`; |
| return `${prefix}:${cryptoId}`; |
| } |
|
|