102 lines
3.0 KiB
TypeScript
102 lines
3.0 KiB
TypeScript
import { useEffect, useRef, type RefObject } from 'react';
|
|
|
|
/**
|
|
* Send a prompt to the parent window
|
|
*/
|
|
export function sendPrompt(message: string) {
|
|
window.parent.postMessage({
|
|
type: 'message',
|
|
payload: {
|
|
message: message
|
|
}
|
|
}, '*');
|
|
}
|
|
|
|
/**
|
|
* Call a tool
|
|
*/
|
|
export function callTool(toolName: string, params: Record<string, unknown> = {}) {
|
|
window.parent.postMessage({
|
|
type: 'tool',
|
|
payload: {
|
|
toolName: toolName,
|
|
params: params
|
|
}
|
|
}, '*');
|
|
}
|
|
|
|
/**
|
|
* Request the parent to resize the iframe to fit content
|
|
* Call this after your content has rendered
|
|
*/
|
|
export function resizeToContent(element?: HTMLElement) {
|
|
const target = element || document.body;
|
|
|
|
// Use scrollHeight/scrollWidth for full content size (not just visible area)
|
|
// Also account for any margin on the element
|
|
const style = window.getComputedStyle(target);
|
|
const marginTop = parseFloat(style.marginTop) || 0;
|
|
const marginBottom = parseFloat(style.marginBottom) || 0;
|
|
const marginLeft = parseFloat(style.marginLeft) || 0;
|
|
const marginRight = parseFloat(style.marginRight) || 0;
|
|
|
|
// For body, also account for body padding/margin
|
|
let extraHeight = 0;
|
|
let extraWidth = 0;
|
|
if (target !== document.body) {
|
|
const bodyStyle = window.getComputedStyle(document.body);
|
|
extraHeight = (parseFloat(bodyStyle.paddingTop) || 0) + (parseFloat(bodyStyle.paddingBottom) || 0);
|
|
extraWidth = (parseFloat(bodyStyle.paddingLeft) || 0) + (parseFloat(bodyStyle.paddingRight) || 0);
|
|
}
|
|
|
|
const width = Math.ceil(target.scrollWidth + marginLeft + marginRight + extraWidth);
|
|
const height = Math.ceil(target.scrollHeight + marginTop + marginBottom + extraHeight);
|
|
|
|
window.parent.postMessage({
|
|
type: 'resize',
|
|
payload: { width, height }
|
|
}, '*');
|
|
}
|
|
|
|
/**
|
|
* Hook that automatically notifies parent when content size changes
|
|
* Returns a ref to attach to your container element
|
|
*/
|
|
export function useResizeToContent<T extends HTMLElement = HTMLDivElement>(): RefObject<T | null> {
|
|
const ref = useRef<T | null>(null);
|
|
|
|
useEffect(() => {
|
|
const element = ref.current;
|
|
if (!element) return;
|
|
|
|
const observer = new ResizeObserver(() => {
|
|
resizeToContent(element);
|
|
});
|
|
|
|
observer.observe(element);
|
|
// Initial resize
|
|
resizeToContent(element);
|
|
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
return ref;
|
|
}
|
|
|
|
/**
|
|
* Use MCP props
|
|
*/
|
|
export function useProps<T>(defaults: T): T {
|
|
// In a real implementation, this would read from:
|
|
// window.__MCP_INITIAL_DATA__ or similar injection point
|
|
const scriptTag = document.getElementById('mcp-initial-data');
|
|
if (scriptTag?.textContent) {
|
|
try {
|
|
console.log('scriptTag.textContent', scriptTag.textContent);
|
|
return { ...defaults, ...JSON.parse(scriptTag.textContent) };
|
|
} catch {
|
|
return defaults;
|
|
}
|
|
}
|
|
return defaults;
|
|
} |