@gigabuddy/gadgets 0.1.12 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +26 -0
- package/index.js.map +3 -3
- package/package.json +1 -1
- package/react.js +41 -34
- package/react.js.map +2 -2
- package/src/index.d.ts +2 -0
- package/src/lib/capabilities.d.ts +17 -0
- package/src/lib/createGadgetRenderer.d.ts +2 -0
package/index.js
CHANGED
|
@@ -6,8 +6,10 @@ function createGadgetRenderer(componentCode, data = {}, options) {
|
|
|
6
6
|
const chatEnabled = options?.chatEnabled ?? false;
|
|
7
7
|
const contextEnabled = options?.contextEnabled ?? false;
|
|
8
8
|
const roomEnabled = options?.roomEnabled ?? false;
|
|
9
|
+
const libraries = options?.libraries ?? [];
|
|
9
10
|
const escapedCode = componentCode.replace(/<\/script>/g, "<\\/script>");
|
|
10
11
|
const serializedAssets = JSON.stringify(options?.assets ?? {});
|
|
12
|
+
const libraryScripts = libraries.map((url) => ` <script src="${url}"><\/script>`).join("\n");
|
|
11
13
|
const stateBridgeScript = stateEnabled ? `
|
|
12
14
|
window.gadget.state = {
|
|
13
15
|
shared: {},
|
|
@@ -337,6 +339,7 @@ function createGadgetRenderer(componentCode, data = {}, options) {
|
|
|
337
339
|
<script src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"><\/script>
|
|
338
340
|
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"><\/script>
|
|
339
341
|
<script src="https://cdn.tailwindcss.com"><\/script>
|
|
342
|
+
${libraryScripts}
|
|
340
343
|
<style>
|
|
341
344
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
342
345
|
html, body { background: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-height: 100%; }
|
|
@@ -519,6 +522,28 @@ ${roomBridgeScript}
|
|
|
519
522
|
</html>`;
|
|
520
523
|
}
|
|
521
524
|
|
|
525
|
+
// libs/gadgets/src/lib/capabilities.ts
|
|
526
|
+
var CAPABILITY_LIBS = {
|
|
527
|
+
"3d": ["https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"],
|
|
528
|
+
// Rapier loads via dynamic import() inside gadget code — no static script needed
|
|
529
|
+
physics: [],
|
|
530
|
+
audio: ["https://cdn.jsdelivr.net/npm/tone@14/build/Tone.min.js"],
|
|
531
|
+
ml: ["https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4/dist/tf.min.js"]
|
|
532
|
+
};
|
|
533
|
+
function resolveCapabilities(capabilities) {
|
|
534
|
+
const libs = [];
|
|
535
|
+
for (const cap of capabilities) {
|
|
536
|
+
const capLibs = CAPABILITY_LIBS[cap];
|
|
537
|
+
if (!capLibs)
|
|
538
|
+
continue;
|
|
539
|
+
for (const url of capLibs) {
|
|
540
|
+
if (!libs.includes(url))
|
|
541
|
+
libs.push(url);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return libs;
|
|
545
|
+
}
|
|
546
|
+
|
|
522
547
|
// libs/gadgets/src/lib/setupGadgetBreakout.ts
|
|
523
548
|
function setupGadgetBreakout(iframe, options) {
|
|
524
549
|
const savedStyles = {};
|
|
@@ -960,6 +985,7 @@ var SceneGraph = class {
|
|
|
960
985
|
export {
|
|
961
986
|
SceneGraph,
|
|
962
987
|
createGadgetRenderer,
|
|
988
|
+
resolveCapabilities,
|
|
963
989
|
setupGadgetAwareness,
|
|
964
990
|
setupGadgetBreakout
|
|
965
991
|
};
|
package/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../libs/gadgets/src/lib/createGadgetRenderer.ts", "../../../libs/gadgets/src/lib/setupGadgetBreakout.ts", "../../../libs/gadgets/src/lib/setupGadgetAwareness.ts", "../../../libs/gadgets/src/lib/sceneGraph.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Creates a self-contained HTML string for rendering a gadget component in an iframe.\n *\n * Loads React 18, ReactDOM, Babel (for JSX transpilation), and Tailwind CSS from CDNs.\n * Sets up the full gadget bridge: state, breakout, actions, chat, context, and hot-swap.\n *\n * Usage:\n * import { createGadgetRenderer, setupGadgetBreakout } from '@gigabuddy/chat';\n *\n * const html = createGadgetRenderer(componentCode, data, { viewport: 'compact', stateEnabled: true });\n * iframe.srcdoc = html;\n * setupGadgetBreakout(iframe);\n *\n * The gadget component receives these props:\n * function Gadget({ data, viewport, state, userId, breakout, chat, context })\n *\n * Bridge protocol (postMessage):\n * - Host \u2192 iframe: `gadget-set-data` \u2014 push new props\n * - Host \u2192 iframe: `gadget-state-init` / `gadget-state-update` \u2014 state changes\n * - Host \u2192 iframe: `gadget-update-component` \u2014 hot-swap component code\n * - Host \u2192 iframe: `gadget-breakout-started` \u2014 breakout activated with originalRect\n * - iframe \u2192 Host: `gadget-interaction` \u2014 user dispatched an action\n * - iframe \u2192 Host: `gadget-resize` \u2014 auto-height\n * - iframe \u2192 Host: `gadget-request-breakout` / `gadget-exit-breakout` \u2014 breakout lifecycle\n * - iframe \u2192 Host: `gadget-action-request` / `gadget-chat-request` / `gadget-context-request` \u2014 API calls\n */\nexport function createGadgetRenderer(\n componentCode: string,\n data: unknown = {},\n options?: {\n viewport?: 'compact' | 'full' | 'mobile';\n stateEnabled?: boolean;\n chatEnabled?: boolean;\n contextEnabled?: boolean;\n roomEnabled?: boolean;\n assets?: Record<string, { url: string; name: string }>;\n },\n): string {\n const serializedData = JSON.stringify(data);\n const viewport = options?.viewport ?? 'full';\n const stateEnabled = options?.stateEnabled ?? false;\n const chatEnabled = options?.chatEnabled ?? false;\n const contextEnabled = options?.contextEnabled ?? false;\n const roomEnabled = options?.roomEnabled ?? false;\n const escapedCode = componentCode.replace(/<\\/script>/g, '<\\\\/script>');\n const serializedAssets = JSON.stringify(options?.assets ?? {});\n\n const stateBridgeScript = stateEnabled\n ? `\n window.gadget.state = {\n shared: {},\n user: {},\n userId: null,\n _sharedCallbacks: [],\n _userCallbacks: [],\n\n dispatch: function(action, data) {\n window.parent.postMessage({ type: 'gadget-interaction', action: action, data: data || {} }, '*');\n },\n\n onSharedChange: function(callback) {\n window.gadget.state._sharedCallbacks.push(callback);\n callback(window.gadget.state.shared);\n return function() {\n var i = window.gadget.state._sharedCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.state._sharedCallbacks.splice(i, 1);\n };\n },\n\n onUserChange: function(callback) {\n window.gadget.state._userCallbacks.push(callback);\n callback(window.gadget.state.user);\n return function() {\n var i = window.gadget.state._userCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.state._userCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && (d.type === 'gadget-state-init' || d.type === 'gadget-state-update')) {\n if (d.userId !== undefined) {\n window.gadget.state.userId = d.userId;\n }\n if (d.shared !== undefined) {\n window.gadget.state.shared = d.shared;\n window.gadget.state._sharedCallbacks.forEach(function(cb) { try { cb(d.shared); } catch(e) {} });\n }\n if (d.user !== undefined) {\n window.gadget.state.user = d.user;\n window.gadget.state._userCallbacks.forEach(function(cb) { try { cb(d.user); } catch(e) {} });\n }\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const chatBridgeScript = chatEnabled\n ? `\n window.gadget.chat = {\n _request: function(method, params) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-chat-response' && d.requestId === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() { window.removeEventListener('message', handler); reject(new Error('Timed out')); }, 30000);\n window.parent.postMessage({ type: 'gadget-chat-request', requestId: requestId, method: method, params: params || {} }, '*');\n });\n },\n\n _eventCallbacks: [],\n\n createChannel: function(opts) { return window.gadget.chat._request('createChannel', opts); },\n findOrCreateChannel: function(name, opts) { return window.gadget.chat._request('findOrCreateChannel', { name: name, description: opts && opts.description }); },\n listChannels: function() { return window.gadget.chat._request('listChannels'); },\n joinChannel: function(conversationId) { return window.gadget.chat._request('joinChannel', { conversationId: conversationId }); },\n sendMessage: function(conversationId, text) { return window.gadget.chat._request('sendMessage', { conversationId: conversationId, text: text }); },\n getMessages: function(conversationId, opts) { return window.gadget.chat._request('getMessages', { conversationId: conversationId, limit: opts && opts.limit, before: opts && opts.before }); },\n updateChannel: function(conversationId, updates) { return window.gadget.chat._request('updateChannel', { conversationId: conversationId, description: updates && updates.description }); },\n createInvite: function(conversationId) { return window.gadget.chat._request('createInvite', { conversationId: conversationId }); },\n redeemInvite: function(token) { return window.gadget.chat._request('redeemInvite', { token: token }); },\n\n onEvent: function(callback) {\n window.gadget.chat._eventCallbacks.push(callback);\n return function() {\n var i = window.gadget.chat._eventCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.chat._eventCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-chat-event') {\n window.gadget.chat._eventCallbacks.forEach(function(cb) { try { cb(d.event); } catch(e) {} });\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const contextBridgeScript = contextEnabled\n ? `\n window.gadget.context = {\n _request: function(method, params) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-context-response' && d.id === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() { window.removeEventListener('message', handler); reject(new Error('Timed out')); }, 30000);\n window.parent.postMessage({ type: 'gadget-context-request', id: requestId, method: method, params: params || {} }, '*');\n });\n },\n\n _eventCallbacks: [],\n\n listTypes: function() { return window.gadget.context._request('listTypes', {}); },\n getType: function(contextType) { return window.gadget.context._request('getType', { contextType: contextType }); },\n listInstances: function(params) { return window.gadget.context._request('listInstances', params || {}); },\n getInstance: function(id) { return window.gadget.context._request('getInstance', { id: id }); },\n createInstance: function(params) { return window.gadget.context._request('createInstance', params); },\n updateInstance: function(id, data) { return window.gadget.context._request('updateInstance', { id: id, data: data }); },\n deleteInstance: function(id) { return window.gadget.context._request('deleteInstance', { id: id }); },\n searchKnowledge: function(params) { return window.gadget.context._request('searchKnowledge', params); },\n getEnriched: function(id) { return window.gadget.context._request('getEnriched', { id: id }); },\n getRelated: function(id) { return window.gadget.context._request('getRelated', { id: id }); },\n\n onEvent: function(callback) {\n window.gadget.context._eventCallbacks.push(callback);\n return function() {\n var i = window.gadget.context._eventCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.context._eventCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-context-event') {\n window.gadget.context._eventCallbacks.forEach(function(cb) { try { cb(d.event); } catch(e) {} });\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const roomBridgeScript = roomEnabled\n ? `\n // \u2500\u2500 Room presence \u2500\u2500\n window.gadget.room = {\n peers: [],\n userId: null,\n displayName: null,\n _peersCallbacks: [],\n _playerJoinedCallbacks: [],\n _playerLeftCallbacks: [],\n\n setCursor: function(pos) {\n window.parent.postMessage({ type: 'gadget-presence', cursor: pos }, '*');\n },\n setSelection: function(sel) {\n window.parent.postMessage({ type: 'gadget-presence', selection: sel }, '*');\n },\n onPeersChange: function(cb) {\n window.gadget.room._peersCallbacks.push(cb);\n cb(window.gadget.room.peers);\n return function() {\n var i = window.gadget.room._peersCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._peersCallbacks.splice(i, 1);\n };\n },\n onPlayerJoined: function(cb) {\n window.gadget.room._playerJoinedCallbacks.push(cb);\n return function() {\n var i = window.gadget.room._playerJoinedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._playerJoinedCallbacks.splice(i, 1);\n };\n },\n onPlayerLeft: function(cb) {\n window.gadget.room._playerLeftCallbacks.push(cb);\n return function() {\n var i = window.gadget.room._playerLeftCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._playerLeftCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Room chat \u2500\u2500\n window.gadget.roomChat = {\n messages: [],\n _messageCallbacks: [],\n\n send: function(text) {\n window.parent.postMessage({ type: 'gadget-chat-send', text: text }, '*');\n },\n onMessage: function(cb) {\n window.gadget.roomChat._messageCallbacks.push(cb);\n return function() {\n var i = window.gadget.roomChat._messageCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.roomChat._messageCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Gestures \u2500\u2500\n window.gadget.gestures = {\n active: [],\n _spawnedCallbacks: [],\n _dismissedCallbacks: [],\n\n spawn: function(gadgetId, anchor, opts) {\n opts = opts || {};\n window.parent.postMessage({\n type: 'gadget-gesture-send',\n gadgetId: gadgetId,\n anchor: anchor,\n ttl: opts.ttl,\n size: opts.size,\n rotation: opts.rotation,\n }, '*');\n },\n dismiss: function(gestureId) {\n window.parent.postMessage({ type: 'gadget-gesture-dismiss', gestureId: gestureId }, '*');\n },\n reportAnchorRect: function(selector, rect) {\n window.parent.postMessage({ type: 'gadget-anchor-rect', selector: selector, rect: rect }, '*');\n },\n onSpawned: function(cb) {\n window.gadget.gestures._spawnedCallbacks.push(cb);\n return function() {\n var i = window.gadget.gestures._spawnedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.gestures._spawnedCallbacks.splice(i, 1);\n };\n },\n onDismissed: function(cb) {\n window.gadget.gestures._dismissedCallbacks.push(cb);\n return function() {\n var i = window.gadget.gestures._dismissedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.gestures._dismissedCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Buddy attraction \u2500\u2500\n window.gadget.buddy = {\n attract: function(anchor, interest) {\n window.parent.postMessage({\n type: 'gadget-buddy-attract',\n anchor: anchor,\n interest: interest || 'medium',\n }, '*');\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (!d) return;\n\n // Presence updates\n if (d.type === 'gadget-presence-update') {\n window.gadget.room.peers = d.peers || [];\n window.gadget.room._peersCallbacks.forEach(function(cb) { try { cb(d.peers || []); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Player join/leave\n if (d.type === 'gadget-player-joined') {\n window.gadget.room._playerJoinedCallbacks.forEach(function(cb) { try { cb(d); } catch(e) {} });\n }\n if (d.type === 'gadget-player-left') {\n window.gadget.room.peers = window.gadget.room.peers.filter(function(p) { return p.userId !== d.userId; });\n window.gadget.room._playerLeftCallbacks.forEach(function(cb) { try { cb(d.userId); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Chat\n if (d.type === 'gadget-chat-message') {\n var msg = d.message || d;\n window.gadget.roomChat.messages.push(msg);\n if (window.gadget.roomChat.messages.length > 100) window.gadget.roomChat.messages.shift();\n window.gadget.roomChat._messageCallbacks.forEach(function(cb) { try { cb(msg); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-chat-history') {\n window.gadget.roomChat.messages = d.messages || [];\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Gestures\n if (d.type === 'gadget-gesture-spawned') {\n window.gadget.gestures.active.push(d.gesture);\n window.gadget.gestures._spawnedCallbacks.forEach(function(cb) { try { cb(d.gesture); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-gesture-dismissed') {\n window.gadget.gestures.active = window.gadget.gestures.active.filter(function(g) { return g.id !== d.gestureId; });\n window.gadget.gestures._dismissedCallbacks.forEach(function(cb) { try { cb(d.gestureId); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-gesture-sync') {\n window.gadget.gestures.active = d.gestures || [];\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Anchor rect request from host\n if (d.type === 'gadget-request-anchor-rect' && d.selector) {\n try {\n var el = document.querySelector(d.selector);\n if (el) {\n var rect = el.getBoundingClientRect();\n window.parent.postMessage({\n type: 'gadget-anchor-rect',\n selector: d.selector,\n rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n }, '*');\n }\n } catch(e) {}\n }\n });`\n : '';\n\n // When state is enabled, pass state prop alongside data and viewport\n const chatPropFragment = chatEnabled ? ', chat: window.gadget.chat' : '';\n const contextPropFragment = contextEnabled ? ', context: window.gadget.context' : '';\n const roomPropFragment = roomEnabled\n ? ', room: window.gadget.room, roomChat: window.gadget.roomChat, gestures: window.gadget.gestures'\n : '';\n const breakoutPropFragment =\n ', breakout: { active: window.gadget.breakout._active, originalRect: window.gadget.breakout._originalRect, request: window.gadget.breakout.request, exit: window.gadget.breakout.exit }';\n const componentProps = stateEnabled\n ? `{ data: window.__GADGET_DATA__, viewport: window.__GADGET_VIEWPORT__, state: window.gadget.state ? { shared: window.gadget.state.shared, user: window.gadget.state.user } : undefined, userId: window.gadget.state ? window.gadget.state.userId : undefined${chatPropFragment}${contextPropFragment}${breakoutPropFragment}${roomPropFragment} }`\n : `{ data: window.__GADGET_DATA__, viewport: window.__GADGET_VIEWPORT__${chatPropFragment}${contextPropFragment}${breakoutPropFragment}${roomPropFragment} }`;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <script src=\"https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js\"></script>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n html, body { background: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-height: 100%; }\n #root { min-height: 100vh; }\n .gadget-error {\n padding: 16px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n color: #991b1b;\n font-family: monospace;\n font-size: 13px;\n white-space: pre-wrap;\n word-break: break-word;\n }\n .gadget-error-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n </style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script>\n window.__GADGET_DATA__ = ${serializedData};\n window.__GADGET_VIEWPORT__ = '${viewport}';\n\n window.gadget = {\n data: window.__GADGET_DATA__,\n assets: ${serializedAssets},\n\n breakout: {\n _active: false,\n _originalRect: null,\n request: function() {\n if (window.gadget.breakout._active) return;\n window.gadget.breakout._active = true;\n window.parent.postMessage({ type: 'gadget-request-breakout' }, '*');\n window.__rerenderGadget && window.__rerenderGadget();\n },\n exit: function() {\n if (!window.gadget.breakout._active) return;\n window.gadget.breakout._active = false;\n window.gadget.breakout._originalRect = null;\n window.parent.postMessage({ type: 'gadget-exit-breakout' }, '*');\n window.__rerenderGadget();\n },\n },\n\n callAction: function(input) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-action-response' && d.requestId === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() {\n window.removeEventListener('message', handler);\n reject(new Error('Action timed out'));\n }, 30000);\n window.parent.postMessage({ type: 'gadget-action-request', requestId: requestId, input: input }, '*');\n });\n },\n };\n${stateBridgeScript}\n${chatBridgeScript}\n${contextBridgeScript}\n${roomBridgeScript}\n\n window.__gadgetRoot = null;\n window.__gadgetComponent = null;\n\n window.__rerenderGadget = function() {\n if (window.__gadgetRoot && window.__gadgetComponent) {\n var C = window.__gadgetComponent;\n var EB = window.__gadgetErrorBoundary;\n window.__gadgetRoot.render(\n React.createElement(EB, null, React.createElement(C, ${componentProps}))\n );\n }\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-set-data') {\n window.__GADGET_DATA__ = d.data;\n window.gadget.data = d.data;\n window.__rerenderGadget();\n }\n if (d && d.type === 'gadget-breakout-started') {\n var wasActive = window.gadget.breakout._active;\n window.gadget.breakout._active = true;\n window.gadget.breakout._originalRect = d.originalRect || null;\n if (!wasActive) window.__rerenderGadget();\n }\n if (d && d.type === 'gadget-update-component' && d.code) {\n try {\n var compiled = Babel.transform(d.code, { presets: ['react'] }).code;\n var fn = new Function('React', 'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef',\n compiled + '\\\\nreturn typeof Gadget !== \"undefined\" ? Gadget : null;');\n var NewComponent = fn(React, React.useState, React.useEffect, React.useCallback, React.useMemo, React.useRef);\n if (NewComponent) {\n window.__gadgetComponent = NewComponent;\n if (window.__gadgetErrorBoundaryInstance) {\n window.__gadgetErrorBoundaryInstance.setState({ error: null });\n }\n window.__rerenderGadget();\n }\n } catch (err) {\n console.error('[gadget-hmr] Component update failed:', err);\n }\n }\n });\n </script>\n <script type=\"text/babel\" data-type=\"module\">\n const { useState, useEffect, useCallback, useMemo, useRef } = React;\n\n class ErrorBoundary extends React.Component {\n constructor(props) {\n super(props);\n this.state = { error: null };\n window.__gadgetErrorBoundaryInstance = this;\n }\n static getDerivedStateFromError(error) {\n return { error };\n }\n render() {\n if (this.state.error) {\n return React.createElement('div', { className: 'gadget-error' },\n React.createElement('div', { className: 'gadget-error-title' }, 'Runtime Error'),\n this.state.error.message\n );\n }\n return this.props.children;\n }\n }\n window.__gadgetErrorBoundary = ErrorBoundary;\n\n try {\n ${escapedCode}\n\n const ComponentToRender = typeof Gadget !== 'undefined' ? Gadget : null;\n\n if (ComponentToRender) {\n window.__gadgetComponent = ComponentToRender;\n const root = ReactDOM.createRoot(document.getElementById('root'));\n window.__gadgetRoot = root;\n root.render(\n <ErrorBoundary>\n <ComponentToRender data={window.__GADGET_DATA__} viewport={window.__GADGET_VIEWPORT__}${stateEnabled ? ' state={window.gadget.state ? { shared: window.gadget.state.shared, user: window.gadget.state.user } : undefined} userId={window.gadget.state ? window.gadget.state.userId : undefined}' : ''}${chatEnabled ? ' chat={window.gadget.chat}' : ''}${contextEnabled ? ' context={window.gadget.context}' : ''}${roomEnabled ? ' room={window.gadget.room} roomChat={window.gadget.roomChat} gestures={window.gadget.gestures}' : ''} breakout={{ active: window.gadget.breakout._active, originalRect: window.gadget.breakout._originalRect, request: window.gadget.breakout.request, exit: window.gadget.breakout.exit }} />\n </ErrorBoundary>\n );\n } else {\n document.getElementById('root').innerHTML =\n '<div class=\"gadget-error\"><div class=\"gadget-error-title\">No component found</div>Define a function called Gadget.</div>';\n }\n } catch (err) {\n document.getElementById('root').innerHTML =\n '<div class=\"gadget-error\"><div class=\"gadget-error-title\">Error</div>' +\n err.message.replace(/</g, '<').replace(/>/g, '>') +\n '</div>';\n }\n\n const rootEl = document.getElementById('root');\n const observer = new ResizeObserver(() => {\n window.parent.postMessage({\n type: 'gadget-resize',\n height: rootEl.scrollHeight,\n }, '*');\n });\n observer.observe(rootEl);\n </script>\n</body>\n</html>`;\n}\n", "/**\n * Enables the gadget bridge protocols on an iframe.\n *\n * **Breakout protocol:** When a gadget calls `breakout.request()`, this handler\n * promotes the iframe to a fullscreen transparent overlay so the gadget can\n * render across the entire viewport (e.g. dice rolls, confetti). When the\n * gadget calls `breakout.exit()`, the iframe is restored.\n *\n * **File URL resolution:** When `resolveFileUrl` is provided, gadgets can request\n * fresh signed URLs for internal file references via `gadget-file-url-request`.\n * This solves the expired signed URL problem for images and files.\n *\n * Usage:\n * import { setupGadgetBreakout } from '@gigabuddy/gadgets';\n * const cleanup = setupGadgetBreakout(iframeElement);\n *\n * // With file URL resolution:\n * const cleanup = setupGadgetBreakout(iframeElement, {\n * resolveFileUrl: async (fileId) => {\n * const res = await fetch(`/api/get-file-url`, { ... });\n * return (await res.json()).url;\n * },\n * });\n */\nexport interface GadgetBridgeOptions {\n resolveFileUrl: (fileId: string) => Promise<string>;\n}\n\nexport function setupGadgetBreakout(iframe: HTMLIFrameElement, options?: GadgetBridgeOptions): () => void {\n const savedStyles: Record<string, string> = {};\n const STYLE_KEYS = [\n 'position',\n 'top',\n 'left',\n 'width',\n 'height',\n 'zIndex',\n 'background',\n 'border',\n 'borderRadius',\n 'maxWidth',\n 'maxHeight',\n 'margin',\n 'transform',\n 'pointerEvents',\n 'overflow',\n 'visibility',\n ] as const;\n\n // Track ancestor overrides so we can restore them\n const ancestorOverrides: { el: HTMLElement; key: string; original: string }[] = [];\n let placeholderDiv: HTMLDivElement | null = null;\n\n function clearAncestorClipping(): void {\n let el = iframe.parentElement;\n while (el && el !== document.body && el !== document.documentElement) {\n const computed = getComputedStyle(el);\n const overflow = computed.overflow + computed.overflowX + computed.overflowY;\n const hasClipping = /hidden|auto|scroll|clip/.test(overflow);\n const hasContainingBlock =\n computed.transform !== 'none' ||\n computed.willChange === 'transform' ||\n computed.contain !== 'none' ||\n computed.filter !== 'none';\n\n if (hasClipping || hasContainingBlock) {\n if (hasClipping) {\n ancestorOverrides.push({ el, key: 'overflow', original: el.style.overflow });\n ancestorOverrides.push({ el, key: 'overflowX', original: el.style.overflowX });\n ancestorOverrides.push({ el, key: 'overflowY', original: el.style.overflowY });\n el.style.overflow = 'visible';\n el.style.overflowX = 'visible';\n el.style.overflowY = 'visible';\n }\n if (hasContainingBlock && computed.transform !== 'none') {\n ancestorOverrides.push({ el, key: 'transform', original: el.style.transform });\n el.style.transform = 'none';\n }\n if (hasContainingBlock && computed.contain !== 'none') {\n ancestorOverrides.push({ el, key: 'contain', original: el.style.contain });\n el.style.contain = 'none';\n }\n }\n el = el.parentElement;\n }\n }\n\n function restoreAncestorClipping(): void {\n for (const { el, key, original } of ancestorOverrides) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (el.style as any)[key] = original;\n }\n ancestorOverrides.length = 0;\n }\n\n function handler(event: MessageEvent): void {\n if (event.source !== iframe.contentWindow) return;\n const d = event.data;\n if (!d || typeof d !== 'object') return;\n\n if (d.type === 'gadget-request-breakout') {\n const rect = iframe.getBoundingClientRect();\n\n // Save current inline styles and attributes\n for (const key of STYLE_KEYS) {\n savedStyles[key] = iframe.style[key as keyof CSSStyleDeclaration] as string;\n }\n savedStyles['_scrolling'] = iframe.getAttribute('scrolling') ?? '';\n\n // Insert a placeholder to preserve layout space while iframe is position:fixed\n placeholderDiv = document.createElement('div');\n placeholderDiv.style.width = `${rect.width}px`;\n placeholderDiv.style.height = `${rect.height}px`;\n placeholderDiv.style.flexShrink = '0';\n iframe.parentElement?.insertBefore(placeholderDiv, iframe);\n\n // Remove scrolling restriction during breakout\n iframe.removeAttribute('scrolling');\n\n // Neutralize ancestor clipping so position:fixed works viewport-wide\n clearAncestorClipping();\n\n // Promote to fullscreen overlay (hidden initially to avoid single-frame flicker\n // while gadget repositions its content based on originalRect)\n Object.assign(iframe.style, {\n position: 'fixed',\n top: '0',\n left: '0',\n width: '100vw',\n height: '100vh',\n zIndex: '99999',\n background: 'transparent',\n border: 'none',\n borderRadius: '0',\n maxWidth: 'none',\n maxHeight: 'none',\n margin: '0',\n transform: 'none',\n pointerEvents: 'none',\n overflow: 'visible',\n visibility: 'hidden',\n });\n\n // Tell the gadget its original position for seamless visual continuity\n iframe.contentWindow?.postMessage(\n {\n type: 'gadget-breakout-started',\n originalRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n },\n '*',\n );\n\n // Reveal after gadget has repositioned (double-rAF ensures both the\n // style application and the gadget's postMessage handler have run)\n requestAnimationFrame(() =>\n requestAnimationFrame(() => {\n iframe.style.visibility = 'visible';\n }),\n );\n }\n\n if (d.type === 'gadget-file-url-request' && options?.resolveFileUrl) {\n const requestId = d.requestId as string;\n const fileId = d.fileId as string;\n if (!requestId || !fileId) return;\n\n void (async () => {\n try {\n const url = await options.resolveFileUrl!(fileId);\n iframe.contentWindow?.postMessage({ type: 'gadget-file-url-response', requestId, url }, '*');\n } catch (err) {\n iframe.contentWindow?.postMessage(\n {\n type: 'gadget-file-url-response',\n requestId,\n error: err instanceof Error ? err.message : 'Failed to resolve file URL',\n },\n '*',\n );\n }\n })();\n return;\n }\n\n if (d.type === 'gadget-exit-breakout') {\n // Remove placeholder\n if (placeholderDiv) {\n placeholderDiv.remove();\n placeholderDiv = null;\n }\n\n // Restore iframe styles\n for (const key of STYLE_KEYS) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (iframe.style as any)[key] = savedStyles[key] ?? '';\n }\n if (savedStyles['_scrolling']) {\n iframe.setAttribute('scrolling', savedStyles['_scrolling']);\n }\n\n // Restore ancestor clipping\n restoreAncestorClipping();\n }\n }\n\n window.addEventListener('message', handler);\n return () => {\n if (placeholderDiv) {\n placeholderDiv.remove();\n placeholderDiv = null;\n }\n restoreAncestorClipping();\n window.removeEventListener('message', handler);\n };\n}\n", "/**\n * Host-side awareness bridge for gadget iframes.\n *\n * Listens for awareness messages from gadget iframes (anchor registration,\n * awareness publish, subscription config) and forwards filtered room\n * awareness state back to the gadget.\n *\n * Usage:\n * const cleanup = setupGadgetAwareness(iframe, {\n * gadgetId: 'poker',\n * sceneGraph,\n * getAwarenessState: () => roomAwareness.states,\n * onAwarenessPublish: (gadgetId, state) => { ... },\n * throttleMs: 100,\n * });\n */\n\nimport type { SceneGraph } from './sceneGraph.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type AwarenessFilter = 'focused' | 'room' | 'all';\n\nexport interface AwarenessParticipant {\n actorId: string;\n displayName?: string;\n cursor?: { x: number; y: number };\n focus?: string;\n intent?: string;\n [key: string]: unknown;\n}\n\nexport interface GadgetAwarenessState {\n /** Participants matching this gadget's subscription filter */\n participants: AwarenessParticipant[];\n /** Anchors registered by this gadget */\n anchors: Array<{\n id: string;\n bounds: { x: number; y: number; w: number; h: number };\n occupant?: string;\n attention: string[];\n }>;\n /** Participants whose focus target is this gadget */\n focused: AwarenessParticipant[];\n}\n\nexport interface SetupGadgetAwarenessOptions {\n /** Unique ID for this gadget instance */\n gadgetId: string;\n /** Shared scene graph for the room */\n sceneGraph: SceneGraph;\n /** Returns current room awareness states (Map<clientId, state>) */\n getAwarenessStates: () => Map<number, { state: Record<string, unknown> }>;\n /** Called when a gadget publishes its own awareness state */\n onAwarenessPublish?: (gadgetId: string, state: Record<string, unknown>) => void;\n /** Called when the scene graph changes (anchors registered/updated) */\n onSceneGraphChange?: () => void;\n /** Throttle interval for awareness forwarding (default: 100ms) */\n throttleMs?: number;\n}\n\n// =============================================================================\n// Setup\n// =============================================================================\n\nexport function setupGadgetAwareness(iframe: HTMLIFrameElement, options: SetupGadgetAwarenessOptions): () => void {\n const {\n gadgetId,\n sceneGraph,\n getAwarenessStates,\n onAwarenessPublish,\n onSceneGraphChange,\n throttleMs = 100,\n } = options;\n\n let filter: AwarenessFilter = 'focused';\n let throttleTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingUpdate = false;\n\n // \u2500\u2500 Coordinate translation \u2500\u2500\n\n function getIframeBounds(): DOMRect {\n return iframe.getBoundingClientRect();\n }\n\n function roomToGadget(rx: number, ry: number, bounds: DOMRect): { x: number; y: number } | null {\n const gx = (rx - bounds.left) / bounds.width;\n const gy = (ry - bounds.top) / bounds.height;\n // Filter out cursors outside the gadget bounds\n if (gx < 0 || gx > 1 || gy < 0 || gy > 1) return null;\n return { x: gx, y: gy };\n }\n\n function gadgetToRoom(gx: number, gy: number, bounds: DOMRect): { x: number; y: number } {\n return {\n x: gx * bounds.width + bounds.left,\n y: gy * bounds.height + bounds.top,\n };\n }\n\n // \u2500\u2500 Build filtered awareness for this gadget \u2500\u2500\n\n function buildAwarenessState(): GadgetAwarenessState {\n const states = getAwarenessStates();\n const bounds = getIframeBounds();\n const allParticipants: AwarenessParticipant[] = [];\n const focused: AwarenessParticipant[] = [];\n\n for (const [, entry] of states) {\n const s = entry.state;\n if (!s || typeof s !== 'object') continue;\n\n const participant: AwarenessParticipant = {\n actorId: (s['actorId'] as string) ?? (s['displayName'] as string) ?? 'unknown',\n displayName: s['displayName'] as string | undefined,\n intent: s['intent'] as string | undefined,\n focus: s['focus'] as string | undefined,\n };\n\n // Translate cursor from room space to gadget space\n const cursorVal = s['cursor'];\n if (cursorVal && typeof cursorVal === 'object') {\n const rc = cursorVal as { x: number; y: number };\n const gc = roomToGadget(rc.x, rc.y, bounds);\n if (gc) {\n participant.cursor = gc;\n }\n }\n\n allParticipants.push(participant);\n\n // Check if this participant is focused on this gadget\n const focusVal = s['focus'];\n if (focusVal === gadgetId || focusVal === `gadget:${gadgetId}`) {\n focused.push(participant);\n }\n }\n\n // Get this gadget's anchors\n const gadgetAnchors = sceneGraph.getGadgetAnchors(gadgetId).map((a) => ({\n id: a.anchorId,\n bounds: a.bounds,\n occupant: a.occupant,\n attention: a.attention,\n }));\n\n return {\n participants: filter === 'focused' ? focused : allParticipants,\n anchors: gadgetAnchors,\n focused,\n };\n }\n\n // \u2500\u2500 Throttled forwarding \u2500\u2500\n\n function scheduleForward(): void {\n if (throttleTimer) {\n pendingUpdate = true;\n return;\n }\n\n forwardAwareness();\n\n throttleTimer = setTimeout(() => {\n throttleTimer = null;\n if (pendingUpdate) {\n pendingUpdate = false;\n forwardAwareness();\n }\n }, throttleMs);\n }\n\n function forwardAwareness(): void {\n if (!iframe.contentWindow) return;\n const state = buildAwarenessState();\n iframe.contentWindow.postMessage({ type: 'gadget-awareness-state', ...state }, '*');\n }\n\n // \u2500\u2500 Handle messages from gadget iframe \u2500\u2500\n\n function handleMessage(event: MessageEvent): void {\n // Only accept messages from our iframe\n if (event.source !== iframe.contentWindow) return;\n const msg = event.data;\n if (!msg || typeof msg.type !== 'string') return;\n\n switch (msg.type) {\n case 'gadget-anchor-register': {\n const anchors = msg.anchors as Array<{\n id: string;\n bounds: { x: number; y: number; w: number; h: number };\n parent?: string;\n }>;\n if (Array.isArray(anchors)) {\n sceneGraph.registerAnchors(gadgetId, anchors);\n onSceneGraphChange?.();\n }\n break;\n }\n\n case 'gadget-anchor-update': {\n const { id, occupant, attention } = msg as {\n id: string;\n occupant?: string | null;\n attention?: string[];\n };\n if (typeof id === 'string') {\n sceneGraph.updateAnchor(gadgetId, id, { occupant, attention });\n onSceneGraphChange?.();\n }\n break;\n }\n\n case 'gadget-awareness-publish': {\n const state = msg.state as Record<string, unknown>;\n if (state && typeof state === 'object') {\n // Translate any cursor from gadget space to room space\n if (state['cursor'] && typeof state['cursor'] === 'object') {\n const gc = state['cursor'] as { x: number; y: number };\n const bounds = getIframeBounds();\n state['cursor'] = gadgetToRoom(gc.x, gc.y, bounds);\n }\n onAwarenessPublish?.(gadgetId, state);\n }\n break;\n }\n\n case 'gadget-awareness-subscribe': {\n const newFilter = msg.filter as AwarenessFilter;\n if (newFilter === 'focused' || newFilter === 'room' || newFilter === 'all') {\n filter = newFilter;\n // Send immediate update with new filter\n forwardAwareness();\n }\n break;\n }\n }\n }\n\n // \u2500\u2500 Setup \u2500\u2500\n\n window.addEventListener('message', handleMessage);\n\n // Send initial awareness state once iframe is loaded\n const onLoad = () => forwardAwareness();\n iframe.addEventListener('load', onLoad);\n\n // \u2500\u2500 Cleanup \u2500\u2500\n\n return () => {\n window.removeEventListener('message', handleMessage);\n iframe.removeEventListener('load', onLoad);\n if (throttleTimer) clearTimeout(throttleTimer);\n sceneGraph.removeGadget(gadgetId);\n onSceneGraphChange?.();\n };\n}\n", "/**\n * Scene Graph \u2014 spatial anchor registry for gadgets in a room.\n *\n * Tracks named anchors registered by gadgets, their parent/child\n * relationships, occupancy, and attention. Queried by the host to\n * build per-gadget awareness state.\n *\n * Anchors are namespaced by gadget: `{gadgetId}:{anchorId}`.\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AnchorBounds {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface Anchor {\n /** Fully qualified ID: `{gadgetId}:{anchorId}` */\n qualifiedId: string;\n /** Raw anchor ID as registered by the gadget */\n anchorId: string;\n /** Gadget that owns this anchor */\n gadgetId: string;\n /** Bounds relative to the owning gadget's coordinate space */\n bounds: AnchorBounds;\n /** Parent anchor qualified ID (for nesting) */\n parent?: string;\n /** Who's currently \"at\" this anchor (e.g., seated at a poker table) */\n occupant?: string;\n /** Who's looking at this anchor */\n attention: string[];\n}\n\nexport interface AnchorNode extends Anchor {\n children: AnchorNode[];\n}\n\n// =============================================================================\n// Scene Graph\n// =============================================================================\n\nexport class SceneGraph {\n private anchors = new Map<string, Anchor>();\n private onChange: (() => void) | null = null;\n\n /**\n * Set a callback for when the scene graph changes.\n */\n setOnChange(cb: (() => void) | null): void {\n this.onChange = cb;\n }\n\n /**\n * Register anchors for a gadget. Replaces any existing anchors\n * from the same gadget with the same IDs.\n */\n registerAnchors(gadgetId: string, anchors: Array<{ id: string; bounds: AnchorBounds; parent?: string }>): void {\n for (const a of anchors) {\n const qualifiedId = `${gadgetId}:${a.id}`;\n const parentQualified = a.parent ? `${gadgetId}:${a.parent}` : undefined;\n\n this.anchors.set(qualifiedId, {\n qualifiedId,\n anchorId: a.id,\n gadgetId,\n bounds: a.bounds,\n parent: parentQualified,\n occupant: this.anchors.get(qualifiedId)?.occupant,\n attention: this.anchors.get(qualifiedId)?.attention ?? [],\n });\n }\n this.onChange?.();\n }\n\n /**\n * Update an anchor's occupancy or attention.\n */\n updateAnchor(gadgetId: string, anchorId: string, update: { occupant?: string | null; attention?: string[] }): void {\n const qualifiedId = `${gadgetId}:${anchorId}`;\n const anchor = this.anchors.get(qualifiedId);\n if (!anchor) return;\n\n if (update.occupant !== undefined) {\n anchor.occupant = update.occupant ?? undefined;\n }\n if (update.attention !== undefined) {\n anchor.attention = update.attention;\n }\n this.onChange?.();\n }\n\n /**\n * Remove all anchors for a gadget (cleanup on iframe removal).\n */\n removeGadget(gadgetId: string): void {\n const toRemove: string[] = [];\n for (const [id, anchor] of this.anchors) {\n if (anchor.gadgetId === gadgetId) {\n toRemove.push(id);\n }\n }\n for (const id of toRemove) {\n this.anchors.delete(id);\n }\n if (toRemove.length > 0) this.onChange?.();\n }\n\n /**\n * Get all anchors as a flat map.\n */\n getAnchors(): Map<string, Anchor> {\n return this.anchors;\n }\n\n /**\n * Get anchors for a specific gadget.\n */\n getGadgetAnchors(gadgetId: string): Anchor[] {\n const result: Anchor[] = [];\n for (const anchor of this.anchors.values()) {\n if (anchor.gadgetId === gadgetId) {\n result.push(anchor);\n }\n }\n return result;\n }\n\n /**\n * Get the anchor tree, optionally rooted at a specific anchor.\n */\n getAnchorTree(rootId?: string): AnchorNode[] {\n const childMap = new Map<string | undefined, Anchor[]>();\n\n for (const anchor of this.anchors.values()) {\n const parentKey = anchor.parent ?? undefined;\n if (!childMap.has(parentKey)) childMap.set(parentKey, []);\n childMap.get(parentKey)!.push(anchor);\n }\n\n const buildNode = (anchor: Anchor): AnchorNode => {\n const children = (childMap.get(anchor.qualifiedId) ?? []).map(buildNode);\n return { ...anchor, children };\n };\n\n if (rootId) {\n const root = this.anchors.get(rootId);\n if (!root) return [];\n return [buildNode(root)];\n }\n\n // Return all root-level anchors (no parent)\n return (childMap.get(undefined) ?? []).map(buildNode);\n }\n\n /**\n * Find the anchor at a given point (in room coordinates).\n * Returns the deepest (most specific) anchor containing the point.\n */\n findAnchorAt(x: number, y: number): Anchor | null {\n let best: Anchor | null = null;\n let bestArea = Infinity;\n\n for (const anchor of this.anchors.values()) {\n const b = anchor.bounds;\n if (x >= b.x && x <= b.x + b.w && y >= b.y && y <= b.y + b.h) {\n const area = b.w * b.h;\n if (area < bestArea) {\n best = anchor;\n bestArea = area;\n }\n }\n }\n return best;\n }\n\n /**\n * Get a serializable snapshot of all anchors.\n */\n toJSON(): Array<{\n id: string;\n gadgetId: string;\n bounds: AnchorBounds;\n parent?: string;\n occupant?: string;\n attention: string[];\n }> {\n return Array.from(this.anchors.values()).map((a) => ({\n id: a.qualifiedId,\n gadgetId: a.gadgetId,\n bounds: a.bounds,\n parent: a.parent,\n occupant: a.occupant,\n attention: a.attention,\n }));\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
3
|
+
"sources": ["../../../libs/gadgets/src/lib/createGadgetRenderer.ts", "../../../libs/gadgets/src/lib/capabilities.ts", "../../../libs/gadgets/src/lib/setupGadgetBreakout.ts", "../../../libs/gadgets/src/lib/setupGadgetAwareness.ts", "../../../libs/gadgets/src/lib/sceneGraph.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Creates a self-contained HTML string for rendering a gadget component in an iframe.\n *\n * Loads React 18, ReactDOM, Babel (for JSX transpilation), and Tailwind CSS from CDNs.\n * Sets up the full gadget bridge: state, breakout, actions, chat, context, and hot-swap.\n *\n * Usage:\n * import { createGadgetRenderer, setupGadgetBreakout } from '@gigabuddy/chat';\n *\n * const html = createGadgetRenderer(componentCode, data, { viewport: 'compact', stateEnabled: true });\n * iframe.srcdoc = html;\n * setupGadgetBreakout(iframe);\n *\n * The gadget component receives these props:\n * function Gadget({ data, viewport, state, userId, breakout, chat, context })\n *\n * Bridge protocol (postMessage):\n * - Host \u2192 iframe: `gadget-set-data` \u2014 push new props\n * - Host \u2192 iframe: `gadget-state-init` / `gadget-state-update` \u2014 state changes\n * - Host \u2192 iframe: `gadget-update-component` \u2014 hot-swap component code\n * - Host \u2192 iframe: `gadget-breakout-started` \u2014 breakout activated with originalRect\n * - iframe \u2192 Host: `gadget-interaction` \u2014 user dispatched an action\n * - iframe \u2192 Host: `gadget-resize` \u2014 auto-height\n * - iframe \u2192 Host: `gadget-request-breakout` / `gadget-exit-breakout` \u2014 breakout lifecycle\n * - iframe \u2192 Host: `gadget-action-request` / `gadget-chat-request` / `gadget-context-request` \u2014 API calls\n */\nexport function createGadgetRenderer(\n componentCode: string,\n data: unknown = {},\n options?: {\n viewport?: 'compact' | 'full' | 'mobile';\n stateEnabled?: boolean;\n chatEnabled?: boolean;\n contextEnabled?: boolean;\n roomEnabled?: boolean;\n assets?: Record<string, { url: string; name: string }>;\n /** Additional script URLs to inject before gadget code (e.g. GadgetKit, Three.js) */\n libraries?: string[];\n },\n): string {\n const serializedData = JSON.stringify(data);\n const viewport = options?.viewport ?? 'full';\n const stateEnabled = options?.stateEnabled ?? false;\n const chatEnabled = options?.chatEnabled ?? false;\n const contextEnabled = options?.contextEnabled ?? false;\n const roomEnabled = options?.roomEnabled ?? false;\n const libraries = options?.libraries ?? [];\n const escapedCode = componentCode.replace(/<\\/script>/g, '<\\\\/script>');\n const serializedAssets = JSON.stringify(options?.assets ?? {});\n const libraryScripts = libraries.map((url) => ` <script src=\"${url}\"></script>`).join('\\n');\n\n const stateBridgeScript = stateEnabled\n ? `\n window.gadget.state = {\n shared: {},\n user: {},\n userId: null,\n _sharedCallbacks: [],\n _userCallbacks: [],\n\n dispatch: function(action, data) {\n window.parent.postMessage({ type: 'gadget-interaction', action: action, data: data || {} }, '*');\n },\n\n onSharedChange: function(callback) {\n window.gadget.state._sharedCallbacks.push(callback);\n callback(window.gadget.state.shared);\n return function() {\n var i = window.gadget.state._sharedCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.state._sharedCallbacks.splice(i, 1);\n };\n },\n\n onUserChange: function(callback) {\n window.gadget.state._userCallbacks.push(callback);\n callback(window.gadget.state.user);\n return function() {\n var i = window.gadget.state._userCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.state._userCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && (d.type === 'gadget-state-init' || d.type === 'gadget-state-update')) {\n if (d.userId !== undefined) {\n window.gadget.state.userId = d.userId;\n }\n if (d.shared !== undefined) {\n window.gadget.state.shared = d.shared;\n window.gadget.state._sharedCallbacks.forEach(function(cb) { try { cb(d.shared); } catch(e) {} });\n }\n if (d.user !== undefined) {\n window.gadget.state.user = d.user;\n window.gadget.state._userCallbacks.forEach(function(cb) { try { cb(d.user); } catch(e) {} });\n }\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const chatBridgeScript = chatEnabled\n ? `\n window.gadget.chat = {\n _request: function(method, params) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-chat-response' && d.requestId === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() { window.removeEventListener('message', handler); reject(new Error('Timed out')); }, 30000);\n window.parent.postMessage({ type: 'gadget-chat-request', requestId: requestId, method: method, params: params || {} }, '*');\n });\n },\n\n _eventCallbacks: [],\n\n createChannel: function(opts) { return window.gadget.chat._request('createChannel', opts); },\n findOrCreateChannel: function(name, opts) { return window.gadget.chat._request('findOrCreateChannel', { name: name, description: opts && opts.description }); },\n listChannels: function() { return window.gadget.chat._request('listChannels'); },\n joinChannel: function(conversationId) { return window.gadget.chat._request('joinChannel', { conversationId: conversationId }); },\n sendMessage: function(conversationId, text) { return window.gadget.chat._request('sendMessage', { conversationId: conversationId, text: text }); },\n getMessages: function(conversationId, opts) { return window.gadget.chat._request('getMessages', { conversationId: conversationId, limit: opts && opts.limit, before: opts && opts.before }); },\n updateChannel: function(conversationId, updates) { return window.gadget.chat._request('updateChannel', { conversationId: conversationId, description: updates && updates.description }); },\n createInvite: function(conversationId) { return window.gadget.chat._request('createInvite', { conversationId: conversationId }); },\n redeemInvite: function(token) { return window.gadget.chat._request('redeemInvite', { token: token }); },\n\n onEvent: function(callback) {\n window.gadget.chat._eventCallbacks.push(callback);\n return function() {\n var i = window.gadget.chat._eventCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.chat._eventCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-chat-event') {\n window.gadget.chat._eventCallbacks.forEach(function(cb) { try { cb(d.event); } catch(e) {} });\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const contextBridgeScript = contextEnabled\n ? `\n window.gadget.context = {\n _request: function(method, params) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-context-response' && d.id === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() { window.removeEventListener('message', handler); reject(new Error('Timed out')); }, 30000);\n window.parent.postMessage({ type: 'gadget-context-request', id: requestId, method: method, params: params || {} }, '*');\n });\n },\n\n _eventCallbacks: [],\n\n listTypes: function() { return window.gadget.context._request('listTypes', {}); },\n getType: function(contextType) { return window.gadget.context._request('getType', { contextType: contextType }); },\n listInstances: function(params) { return window.gadget.context._request('listInstances', params || {}); },\n getInstance: function(id) { return window.gadget.context._request('getInstance', { id: id }); },\n createInstance: function(params) { return window.gadget.context._request('createInstance', params); },\n updateInstance: function(id, data) { return window.gadget.context._request('updateInstance', { id: id, data: data }); },\n deleteInstance: function(id) { return window.gadget.context._request('deleteInstance', { id: id }); },\n searchKnowledge: function(params) { return window.gadget.context._request('searchKnowledge', params); },\n getEnriched: function(id) { return window.gadget.context._request('getEnriched', { id: id }); },\n getRelated: function(id) { return window.gadget.context._request('getRelated', { id: id }); },\n\n onEvent: function(callback) {\n window.gadget.context._eventCallbacks.push(callback);\n return function() {\n var i = window.gadget.context._eventCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.context._eventCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-context-event') {\n window.gadget.context._eventCallbacks.forEach(function(cb) { try { cb(d.event); } catch(e) {} });\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const roomBridgeScript = roomEnabled\n ? `\n // \u2500\u2500 Room presence \u2500\u2500\n window.gadget.room = {\n peers: [],\n userId: null,\n displayName: null,\n _peersCallbacks: [],\n _playerJoinedCallbacks: [],\n _playerLeftCallbacks: [],\n\n setCursor: function(pos) {\n window.parent.postMessage({ type: 'gadget-presence', cursor: pos }, '*');\n },\n setSelection: function(sel) {\n window.parent.postMessage({ type: 'gadget-presence', selection: sel }, '*');\n },\n onPeersChange: function(cb) {\n window.gadget.room._peersCallbacks.push(cb);\n cb(window.gadget.room.peers);\n return function() {\n var i = window.gadget.room._peersCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._peersCallbacks.splice(i, 1);\n };\n },\n onPlayerJoined: function(cb) {\n window.gadget.room._playerJoinedCallbacks.push(cb);\n return function() {\n var i = window.gadget.room._playerJoinedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._playerJoinedCallbacks.splice(i, 1);\n };\n },\n onPlayerLeft: function(cb) {\n window.gadget.room._playerLeftCallbacks.push(cb);\n return function() {\n var i = window.gadget.room._playerLeftCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._playerLeftCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Room chat \u2500\u2500\n window.gadget.roomChat = {\n messages: [],\n _messageCallbacks: [],\n\n send: function(text) {\n window.parent.postMessage({ type: 'gadget-chat-send', text: text }, '*');\n },\n onMessage: function(cb) {\n window.gadget.roomChat._messageCallbacks.push(cb);\n return function() {\n var i = window.gadget.roomChat._messageCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.roomChat._messageCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Gestures \u2500\u2500\n window.gadget.gestures = {\n active: [],\n _spawnedCallbacks: [],\n _dismissedCallbacks: [],\n\n spawn: function(gadgetId, anchor, opts) {\n opts = opts || {};\n window.parent.postMessage({\n type: 'gadget-gesture-send',\n gadgetId: gadgetId,\n anchor: anchor,\n ttl: opts.ttl,\n size: opts.size,\n rotation: opts.rotation,\n }, '*');\n },\n dismiss: function(gestureId) {\n window.parent.postMessage({ type: 'gadget-gesture-dismiss', gestureId: gestureId }, '*');\n },\n reportAnchorRect: function(selector, rect) {\n window.parent.postMessage({ type: 'gadget-anchor-rect', selector: selector, rect: rect }, '*');\n },\n onSpawned: function(cb) {\n window.gadget.gestures._spawnedCallbacks.push(cb);\n return function() {\n var i = window.gadget.gestures._spawnedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.gestures._spawnedCallbacks.splice(i, 1);\n };\n },\n onDismissed: function(cb) {\n window.gadget.gestures._dismissedCallbacks.push(cb);\n return function() {\n var i = window.gadget.gestures._dismissedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.gestures._dismissedCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Buddy attraction \u2500\u2500\n window.gadget.buddy = {\n attract: function(anchor, interest) {\n window.parent.postMessage({\n type: 'gadget-buddy-attract',\n anchor: anchor,\n interest: interest || 'medium',\n }, '*');\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (!d) return;\n\n // Presence updates\n if (d.type === 'gadget-presence-update') {\n window.gadget.room.peers = d.peers || [];\n window.gadget.room._peersCallbacks.forEach(function(cb) { try { cb(d.peers || []); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Player join/leave\n if (d.type === 'gadget-player-joined') {\n window.gadget.room._playerJoinedCallbacks.forEach(function(cb) { try { cb(d); } catch(e) {} });\n }\n if (d.type === 'gadget-player-left') {\n window.gadget.room.peers = window.gadget.room.peers.filter(function(p) { return p.userId !== d.userId; });\n window.gadget.room._playerLeftCallbacks.forEach(function(cb) { try { cb(d.userId); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Chat\n if (d.type === 'gadget-chat-message') {\n var msg = d.message || d;\n window.gadget.roomChat.messages.push(msg);\n if (window.gadget.roomChat.messages.length > 100) window.gadget.roomChat.messages.shift();\n window.gadget.roomChat._messageCallbacks.forEach(function(cb) { try { cb(msg); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-chat-history') {\n window.gadget.roomChat.messages = d.messages || [];\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Gestures\n if (d.type === 'gadget-gesture-spawned') {\n window.gadget.gestures.active.push(d.gesture);\n window.gadget.gestures._spawnedCallbacks.forEach(function(cb) { try { cb(d.gesture); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-gesture-dismissed') {\n window.gadget.gestures.active = window.gadget.gestures.active.filter(function(g) { return g.id !== d.gestureId; });\n window.gadget.gestures._dismissedCallbacks.forEach(function(cb) { try { cb(d.gestureId); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-gesture-sync') {\n window.gadget.gestures.active = d.gestures || [];\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Anchor rect request from host\n if (d.type === 'gadget-request-anchor-rect' && d.selector) {\n try {\n var el = document.querySelector(d.selector);\n if (el) {\n var rect = el.getBoundingClientRect();\n window.parent.postMessage({\n type: 'gadget-anchor-rect',\n selector: d.selector,\n rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n }, '*');\n }\n } catch(e) {}\n }\n });`\n : '';\n\n // When state is enabled, pass state prop alongside data and viewport\n const chatPropFragment = chatEnabled ? ', chat: window.gadget.chat' : '';\n const contextPropFragment = contextEnabled ? ', context: window.gadget.context' : '';\n const roomPropFragment = roomEnabled\n ? ', room: window.gadget.room, roomChat: window.gadget.roomChat, gestures: window.gadget.gestures'\n : '';\n const breakoutPropFragment =\n ', breakout: { active: window.gadget.breakout._active, originalRect: window.gadget.breakout._originalRect, request: window.gadget.breakout.request, exit: window.gadget.breakout.exit }';\n const componentProps = stateEnabled\n ? `{ data: window.__GADGET_DATA__, viewport: window.__GADGET_VIEWPORT__, state: window.gadget.state ? { shared: window.gadget.state.shared, user: window.gadget.state.user } : undefined, userId: window.gadget.state ? window.gadget.state.userId : undefined${chatPropFragment}${contextPropFragment}${breakoutPropFragment}${roomPropFragment} }`\n : `{ data: window.__GADGET_DATA__, viewport: window.__GADGET_VIEWPORT__${chatPropFragment}${contextPropFragment}${breakoutPropFragment}${roomPropFragment} }`;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <script src=\"https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js\"></script>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n${libraryScripts}\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n html, body { background: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-height: 100%; }\n #root { min-height: 100vh; }\n .gadget-error {\n padding: 16px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n color: #991b1b;\n font-family: monospace;\n font-size: 13px;\n white-space: pre-wrap;\n word-break: break-word;\n }\n .gadget-error-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n </style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script>\n window.__GADGET_DATA__ = ${serializedData};\n window.__GADGET_VIEWPORT__ = '${viewport}';\n\n window.gadget = {\n data: window.__GADGET_DATA__,\n assets: ${serializedAssets},\n\n breakout: {\n _active: false,\n _originalRect: null,\n request: function() {\n if (window.gadget.breakout._active) return;\n window.gadget.breakout._active = true;\n window.parent.postMessage({ type: 'gadget-request-breakout' }, '*');\n window.__rerenderGadget && window.__rerenderGadget();\n },\n exit: function() {\n if (!window.gadget.breakout._active) return;\n window.gadget.breakout._active = false;\n window.gadget.breakout._originalRect = null;\n window.parent.postMessage({ type: 'gadget-exit-breakout' }, '*');\n window.__rerenderGadget();\n },\n },\n\n callAction: function(input) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-action-response' && d.requestId === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() {\n window.removeEventListener('message', handler);\n reject(new Error('Action timed out'));\n }, 30000);\n window.parent.postMessage({ type: 'gadget-action-request', requestId: requestId, input: input }, '*');\n });\n },\n };\n${stateBridgeScript}\n${chatBridgeScript}\n${contextBridgeScript}\n${roomBridgeScript}\n\n window.__gadgetRoot = null;\n window.__gadgetComponent = null;\n\n window.__rerenderGadget = function() {\n if (window.__gadgetRoot && window.__gadgetComponent) {\n var C = window.__gadgetComponent;\n var EB = window.__gadgetErrorBoundary;\n window.__gadgetRoot.render(\n React.createElement(EB, null, React.createElement(C, ${componentProps}))\n );\n }\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-set-data') {\n window.__GADGET_DATA__ = d.data;\n window.gadget.data = d.data;\n window.__rerenderGadget();\n }\n if (d && d.type === 'gadget-breakout-started') {\n var wasActive = window.gadget.breakout._active;\n window.gadget.breakout._active = true;\n window.gadget.breakout._originalRect = d.originalRect || null;\n if (!wasActive) window.__rerenderGadget();\n }\n if (d && d.type === 'gadget-update-component' && d.code) {\n try {\n var compiled = Babel.transform(d.code, { presets: ['react'] }).code;\n var fn = new Function('React', 'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef',\n compiled + '\\\\nreturn typeof Gadget !== \"undefined\" ? Gadget : null;');\n var NewComponent = fn(React, React.useState, React.useEffect, React.useCallback, React.useMemo, React.useRef);\n if (NewComponent) {\n window.__gadgetComponent = NewComponent;\n if (window.__gadgetErrorBoundaryInstance) {\n window.__gadgetErrorBoundaryInstance.setState({ error: null });\n }\n window.__rerenderGadget();\n }\n } catch (err) {\n console.error('[gadget-hmr] Component update failed:', err);\n }\n }\n });\n </script>\n <script type=\"text/babel\" data-type=\"module\">\n const { useState, useEffect, useCallback, useMemo, useRef } = React;\n\n class ErrorBoundary extends React.Component {\n constructor(props) {\n super(props);\n this.state = { error: null };\n window.__gadgetErrorBoundaryInstance = this;\n }\n static getDerivedStateFromError(error) {\n return { error };\n }\n render() {\n if (this.state.error) {\n return React.createElement('div', { className: 'gadget-error' },\n React.createElement('div', { className: 'gadget-error-title' }, 'Runtime Error'),\n this.state.error.message\n );\n }\n return this.props.children;\n }\n }\n window.__gadgetErrorBoundary = ErrorBoundary;\n\n try {\n ${escapedCode}\n\n const ComponentToRender = typeof Gadget !== 'undefined' ? Gadget : null;\n\n if (ComponentToRender) {\n window.__gadgetComponent = ComponentToRender;\n const root = ReactDOM.createRoot(document.getElementById('root'));\n window.__gadgetRoot = root;\n root.render(\n <ErrorBoundary>\n <ComponentToRender data={window.__GADGET_DATA__} viewport={window.__GADGET_VIEWPORT__}${stateEnabled ? ' state={window.gadget.state ? { shared: window.gadget.state.shared, user: window.gadget.state.user } : undefined} userId={window.gadget.state ? window.gadget.state.userId : undefined}' : ''}${chatEnabled ? ' chat={window.gadget.chat}' : ''}${contextEnabled ? ' context={window.gadget.context}' : ''}${roomEnabled ? ' room={window.gadget.room} roomChat={window.gadget.roomChat} gestures={window.gadget.gestures}' : ''} breakout={{ active: window.gadget.breakout._active, originalRect: window.gadget.breakout._originalRect, request: window.gadget.breakout.request, exit: window.gadget.breakout.exit }} />\n </ErrorBoundary>\n );\n } else {\n document.getElementById('root').innerHTML =\n '<div class=\"gadget-error\"><div class=\"gadget-error-title\">No component found</div>Define a function called Gadget.</div>';\n }\n } catch (err) {\n document.getElementById('root').innerHTML =\n '<div class=\"gadget-error\"><div class=\"gadget-error-title\">Error</div>' +\n err.message.replace(/</g, '<').replace(/>/g, '>') +\n '</div>';\n }\n\n const rootEl = document.getElementById('root');\n const observer = new ResizeObserver(() => {\n window.parent.postMessage({\n type: 'gadget-resize',\n height: rootEl.scrollHeight,\n }, '*');\n });\n observer.observe(rootEl);\n </script>\n</body>\n</html>`;\n}\n", "/**\n * Gadget capability resolution.\n *\n * Maps declared capabilities (e.g. '3d', 'audio') to the library URLs\n * that must be loaded in the iframe before gadget componentCode runs.\n */\n\nexport type GadgetCapability = '3d' | 'physics' | 'audio' | 'ml';\n\nconst CAPABILITY_LIBS: Record<GadgetCapability, string[]> = {\n '3d': ['https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js'],\n // Rapier loads via dynamic import() inside gadget code \u2014 no static script needed\n physics: [],\n audio: ['https://cdn.jsdelivr.net/npm/tone@14/build/Tone.min.js'],\n ml: ['https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4/dist/tf.min.js'],\n};\n\n/**\n * Resolves an array of capability names to deduplicated library URLs.\n *\n * ```ts\n * resolveCapabilities(['3d', 'physics'])\n * // \u2192 ['https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js',\n * // 'https://g.gigabuddy.com/gadget-3d.js']\n * ```\n */\nexport function resolveCapabilities(capabilities: GadgetCapability[]): string[] {\n const libs: string[] = [];\n for (const cap of capabilities) {\n const capLibs = CAPABILITY_LIBS[cap];\n if (!capLibs) continue;\n for (const url of capLibs) {\n if (!libs.includes(url)) libs.push(url);\n }\n }\n return libs;\n}\n", "/**\n * Enables the gadget bridge protocols on an iframe.\n *\n * **Breakout protocol:** When a gadget calls `breakout.request()`, this handler\n * promotes the iframe to a fullscreen transparent overlay so the gadget can\n * render across the entire viewport (e.g. dice rolls, confetti). When the\n * gadget calls `breakout.exit()`, the iframe is restored.\n *\n * **File URL resolution:** When `resolveFileUrl` is provided, gadgets can request\n * fresh signed URLs for internal file references via `gadget-file-url-request`.\n * This solves the expired signed URL problem for images and files.\n *\n * Usage:\n * import { setupGadgetBreakout } from '@gigabuddy/gadgets';\n * const cleanup = setupGadgetBreakout(iframeElement);\n *\n * // With file URL resolution:\n * const cleanup = setupGadgetBreakout(iframeElement, {\n * resolveFileUrl: async (fileId) => {\n * const res = await fetch(`/api/get-file-url`, { ... });\n * return (await res.json()).url;\n * },\n * });\n */\nexport interface GadgetBridgeOptions {\n resolveFileUrl: (fileId: string) => Promise<string>;\n}\n\nexport function setupGadgetBreakout(iframe: HTMLIFrameElement, options?: GadgetBridgeOptions): () => void {\n const savedStyles: Record<string, string> = {};\n const STYLE_KEYS = [\n 'position',\n 'top',\n 'left',\n 'width',\n 'height',\n 'zIndex',\n 'background',\n 'border',\n 'borderRadius',\n 'maxWidth',\n 'maxHeight',\n 'margin',\n 'transform',\n 'pointerEvents',\n 'overflow',\n 'visibility',\n ] as const;\n\n // Track ancestor overrides so we can restore them\n const ancestorOverrides: { el: HTMLElement; key: string; original: string }[] = [];\n let placeholderDiv: HTMLDivElement | null = null;\n\n function clearAncestorClipping(): void {\n let el = iframe.parentElement;\n while (el && el !== document.body && el !== document.documentElement) {\n const computed = getComputedStyle(el);\n const overflow = computed.overflow + computed.overflowX + computed.overflowY;\n const hasClipping = /hidden|auto|scroll|clip/.test(overflow);\n const hasContainingBlock =\n computed.transform !== 'none' ||\n computed.willChange === 'transform' ||\n computed.contain !== 'none' ||\n computed.filter !== 'none';\n\n if (hasClipping || hasContainingBlock) {\n if (hasClipping) {\n ancestorOverrides.push({ el, key: 'overflow', original: el.style.overflow });\n ancestorOverrides.push({ el, key: 'overflowX', original: el.style.overflowX });\n ancestorOverrides.push({ el, key: 'overflowY', original: el.style.overflowY });\n el.style.overflow = 'visible';\n el.style.overflowX = 'visible';\n el.style.overflowY = 'visible';\n }\n if (hasContainingBlock && computed.transform !== 'none') {\n ancestorOverrides.push({ el, key: 'transform', original: el.style.transform });\n el.style.transform = 'none';\n }\n if (hasContainingBlock && computed.contain !== 'none') {\n ancestorOverrides.push({ el, key: 'contain', original: el.style.contain });\n el.style.contain = 'none';\n }\n }\n el = el.parentElement;\n }\n }\n\n function restoreAncestorClipping(): void {\n for (const { el, key, original } of ancestorOverrides) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (el.style as any)[key] = original;\n }\n ancestorOverrides.length = 0;\n }\n\n function handler(event: MessageEvent): void {\n if (event.source !== iframe.contentWindow) return;\n const d = event.data;\n if (!d || typeof d !== 'object') return;\n\n if (d.type === 'gadget-request-breakout') {\n const rect = iframe.getBoundingClientRect();\n\n // Save current inline styles and attributes\n for (const key of STYLE_KEYS) {\n savedStyles[key] = iframe.style[key as keyof CSSStyleDeclaration] as string;\n }\n savedStyles['_scrolling'] = iframe.getAttribute('scrolling') ?? '';\n\n // Insert a placeholder to preserve layout space while iframe is position:fixed\n placeholderDiv = document.createElement('div');\n placeholderDiv.style.width = `${rect.width}px`;\n placeholderDiv.style.height = `${rect.height}px`;\n placeholderDiv.style.flexShrink = '0';\n iframe.parentElement?.insertBefore(placeholderDiv, iframe);\n\n // Remove scrolling restriction during breakout\n iframe.removeAttribute('scrolling');\n\n // Neutralize ancestor clipping so position:fixed works viewport-wide\n clearAncestorClipping();\n\n // Promote to fullscreen overlay (hidden initially to avoid single-frame flicker\n // while gadget repositions its content based on originalRect)\n Object.assign(iframe.style, {\n position: 'fixed',\n top: '0',\n left: '0',\n width: '100vw',\n height: '100vh',\n zIndex: '99999',\n background: 'transparent',\n border: 'none',\n borderRadius: '0',\n maxWidth: 'none',\n maxHeight: 'none',\n margin: '0',\n transform: 'none',\n pointerEvents: 'none',\n overflow: 'visible',\n visibility: 'hidden',\n });\n\n // Tell the gadget its original position for seamless visual continuity\n iframe.contentWindow?.postMessage(\n {\n type: 'gadget-breakout-started',\n originalRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n },\n '*',\n );\n\n // Reveal after gadget has repositioned (double-rAF ensures both the\n // style application and the gadget's postMessage handler have run)\n requestAnimationFrame(() =>\n requestAnimationFrame(() => {\n iframe.style.visibility = 'visible';\n }),\n );\n }\n\n if (d.type === 'gadget-file-url-request' && options?.resolveFileUrl) {\n const requestId = d.requestId as string;\n const fileId = d.fileId as string;\n if (!requestId || !fileId) return;\n\n void (async () => {\n try {\n const url = await options.resolveFileUrl!(fileId);\n iframe.contentWindow?.postMessage({ type: 'gadget-file-url-response', requestId, url }, '*');\n } catch (err) {\n iframe.contentWindow?.postMessage(\n {\n type: 'gadget-file-url-response',\n requestId,\n error: err instanceof Error ? err.message : 'Failed to resolve file URL',\n },\n '*',\n );\n }\n })();\n return;\n }\n\n if (d.type === 'gadget-exit-breakout') {\n // Remove placeholder\n if (placeholderDiv) {\n placeholderDiv.remove();\n placeholderDiv = null;\n }\n\n // Restore iframe styles\n for (const key of STYLE_KEYS) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (iframe.style as any)[key] = savedStyles[key] ?? '';\n }\n if (savedStyles['_scrolling']) {\n iframe.setAttribute('scrolling', savedStyles['_scrolling']);\n }\n\n // Restore ancestor clipping\n restoreAncestorClipping();\n }\n }\n\n window.addEventListener('message', handler);\n return () => {\n if (placeholderDiv) {\n placeholderDiv.remove();\n placeholderDiv = null;\n }\n restoreAncestorClipping();\n window.removeEventListener('message', handler);\n };\n}\n", "/**\n * Host-side awareness bridge for gadget iframes.\n *\n * Listens for awareness messages from gadget iframes (anchor registration,\n * awareness publish, subscription config) and forwards filtered room\n * awareness state back to the gadget.\n *\n * Usage:\n * const cleanup = setupGadgetAwareness(iframe, {\n * gadgetId: 'poker',\n * sceneGraph,\n * getAwarenessState: () => roomAwareness.states,\n * onAwarenessPublish: (gadgetId, state) => { ... },\n * throttleMs: 100,\n * });\n */\n\nimport type { SceneGraph } from './sceneGraph.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type AwarenessFilter = 'focused' | 'room' | 'all';\n\nexport interface AwarenessParticipant {\n actorId: string;\n displayName?: string;\n cursor?: { x: number; y: number };\n focus?: string;\n intent?: string;\n [key: string]: unknown;\n}\n\nexport interface GadgetAwarenessState {\n /** Participants matching this gadget's subscription filter */\n participants: AwarenessParticipant[];\n /** Anchors registered by this gadget */\n anchors: Array<{\n id: string;\n bounds: { x: number; y: number; w: number; h: number };\n occupant?: string;\n attention: string[];\n }>;\n /** Participants whose focus target is this gadget */\n focused: AwarenessParticipant[];\n}\n\nexport interface SetupGadgetAwarenessOptions {\n /** Unique ID for this gadget instance */\n gadgetId: string;\n /** Shared scene graph for the room */\n sceneGraph: SceneGraph;\n /** Returns current room awareness states (Map<clientId, state>) */\n getAwarenessStates: () => Map<number, { state: Record<string, unknown> }>;\n /** Called when a gadget publishes its own awareness state */\n onAwarenessPublish?: (gadgetId: string, state: Record<string, unknown>) => void;\n /** Called when the scene graph changes (anchors registered/updated) */\n onSceneGraphChange?: () => void;\n /** Throttle interval for awareness forwarding (default: 100ms) */\n throttleMs?: number;\n}\n\n// =============================================================================\n// Setup\n// =============================================================================\n\nexport function setupGadgetAwareness(iframe: HTMLIFrameElement, options: SetupGadgetAwarenessOptions): () => void {\n const {\n gadgetId,\n sceneGraph,\n getAwarenessStates,\n onAwarenessPublish,\n onSceneGraphChange,\n throttleMs = 100,\n } = options;\n\n let filter: AwarenessFilter = 'focused';\n let throttleTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingUpdate = false;\n\n // \u2500\u2500 Coordinate translation \u2500\u2500\n\n function getIframeBounds(): DOMRect {\n return iframe.getBoundingClientRect();\n }\n\n function roomToGadget(rx: number, ry: number, bounds: DOMRect): { x: number; y: number } | null {\n const gx = (rx - bounds.left) / bounds.width;\n const gy = (ry - bounds.top) / bounds.height;\n // Filter out cursors outside the gadget bounds\n if (gx < 0 || gx > 1 || gy < 0 || gy > 1) return null;\n return { x: gx, y: gy };\n }\n\n function gadgetToRoom(gx: number, gy: number, bounds: DOMRect): { x: number; y: number } {\n return {\n x: gx * bounds.width + bounds.left,\n y: gy * bounds.height + bounds.top,\n };\n }\n\n // \u2500\u2500 Build filtered awareness for this gadget \u2500\u2500\n\n function buildAwarenessState(): GadgetAwarenessState {\n const states = getAwarenessStates();\n const bounds = getIframeBounds();\n const allParticipants: AwarenessParticipant[] = [];\n const focused: AwarenessParticipant[] = [];\n\n for (const [, entry] of states) {\n const s = entry.state;\n if (!s || typeof s !== 'object') continue;\n\n const participant: AwarenessParticipant = {\n actorId: (s['actorId'] as string) ?? (s['displayName'] as string) ?? 'unknown',\n displayName: s['displayName'] as string | undefined,\n intent: s['intent'] as string | undefined,\n focus: s['focus'] as string | undefined,\n };\n\n // Translate cursor from room space to gadget space\n const cursorVal = s['cursor'];\n if (cursorVal && typeof cursorVal === 'object') {\n const rc = cursorVal as { x: number; y: number };\n const gc = roomToGadget(rc.x, rc.y, bounds);\n if (gc) {\n participant.cursor = gc;\n }\n }\n\n allParticipants.push(participant);\n\n // Check if this participant is focused on this gadget\n const focusVal = s['focus'];\n if (focusVal === gadgetId || focusVal === `gadget:${gadgetId}`) {\n focused.push(participant);\n }\n }\n\n // Get this gadget's anchors\n const gadgetAnchors = sceneGraph.getGadgetAnchors(gadgetId).map((a) => ({\n id: a.anchorId,\n bounds: a.bounds,\n occupant: a.occupant,\n attention: a.attention,\n }));\n\n return {\n participants: filter === 'focused' ? focused : allParticipants,\n anchors: gadgetAnchors,\n focused,\n };\n }\n\n // \u2500\u2500 Throttled forwarding \u2500\u2500\n\n function scheduleForward(): void {\n if (throttleTimer) {\n pendingUpdate = true;\n return;\n }\n\n forwardAwareness();\n\n throttleTimer = setTimeout(() => {\n throttleTimer = null;\n if (pendingUpdate) {\n pendingUpdate = false;\n forwardAwareness();\n }\n }, throttleMs);\n }\n\n function forwardAwareness(): void {\n if (!iframe.contentWindow) return;\n const state = buildAwarenessState();\n iframe.contentWindow.postMessage({ type: 'gadget-awareness-state', ...state }, '*');\n }\n\n // \u2500\u2500 Handle messages from gadget iframe \u2500\u2500\n\n function handleMessage(event: MessageEvent): void {\n // Only accept messages from our iframe\n if (event.source !== iframe.contentWindow) return;\n const msg = event.data;\n if (!msg || typeof msg.type !== 'string') return;\n\n switch (msg.type) {\n case 'gadget-anchor-register': {\n const anchors = msg.anchors as Array<{\n id: string;\n bounds: { x: number; y: number; w: number; h: number };\n parent?: string;\n }>;\n if (Array.isArray(anchors)) {\n sceneGraph.registerAnchors(gadgetId, anchors);\n onSceneGraphChange?.();\n }\n break;\n }\n\n case 'gadget-anchor-update': {\n const { id, occupant, attention } = msg as {\n id: string;\n occupant?: string | null;\n attention?: string[];\n };\n if (typeof id === 'string') {\n sceneGraph.updateAnchor(gadgetId, id, { occupant, attention });\n onSceneGraphChange?.();\n }\n break;\n }\n\n case 'gadget-awareness-publish': {\n const state = msg.state as Record<string, unknown>;\n if (state && typeof state === 'object') {\n // Translate any cursor from gadget space to room space\n if (state['cursor'] && typeof state['cursor'] === 'object') {\n const gc = state['cursor'] as { x: number; y: number };\n const bounds = getIframeBounds();\n state['cursor'] = gadgetToRoom(gc.x, gc.y, bounds);\n }\n onAwarenessPublish?.(gadgetId, state);\n }\n break;\n }\n\n case 'gadget-awareness-subscribe': {\n const newFilter = msg.filter as AwarenessFilter;\n if (newFilter === 'focused' || newFilter === 'room' || newFilter === 'all') {\n filter = newFilter;\n // Send immediate update with new filter\n forwardAwareness();\n }\n break;\n }\n }\n }\n\n // \u2500\u2500 Setup \u2500\u2500\n\n window.addEventListener('message', handleMessage);\n\n // Send initial awareness state once iframe is loaded\n const onLoad = () => forwardAwareness();\n iframe.addEventListener('load', onLoad);\n\n // \u2500\u2500 Cleanup \u2500\u2500\n\n return () => {\n window.removeEventListener('message', handleMessage);\n iframe.removeEventListener('load', onLoad);\n if (throttleTimer) clearTimeout(throttleTimer);\n sceneGraph.removeGadget(gadgetId);\n onSceneGraphChange?.();\n };\n}\n", "/**\n * Scene Graph \u2014 spatial anchor registry for gadgets in a room.\n *\n * Tracks named anchors registered by gadgets, their parent/child\n * relationships, occupancy, and attention. Queried by the host to\n * build per-gadget awareness state.\n *\n * Anchors are namespaced by gadget: `{gadgetId}:{anchorId}`.\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AnchorBounds {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface Anchor {\n /** Fully qualified ID: `{gadgetId}:{anchorId}` */\n qualifiedId: string;\n /** Raw anchor ID as registered by the gadget */\n anchorId: string;\n /** Gadget that owns this anchor */\n gadgetId: string;\n /** Bounds relative to the owning gadget's coordinate space */\n bounds: AnchorBounds;\n /** Parent anchor qualified ID (for nesting) */\n parent?: string;\n /** Who's currently \"at\" this anchor (e.g., seated at a poker table) */\n occupant?: string;\n /** Who's looking at this anchor */\n attention: string[];\n}\n\nexport interface AnchorNode extends Anchor {\n children: AnchorNode[];\n}\n\n// =============================================================================\n// Scene Graph\n// =============================================================================\n\nexport class SceneGraph {\n private anchors = new Map<string, Anchor>();\n private onChange: (() => void) | null = null;\n\n /**\n * Set a callback for when the scene graph changes.\n */\n setOnChange(cb: (() => void) | null): void {\n this.onChange = cb;\n }\n\n /**\n * Register anchors for a gadget. Replaces any existing anchors\n * from the same gadget with the same IDs.\n */\n registerAnchors(gadgetId: string, anchors: Array<{ id: string; bounds: AnchorBounds; parent?: string }>): void {\n for (const a of anchors) {\n const qualifiedId = `${gadgetId}:${a.id}`;\n const parentQualified = a.parent ? `${gadgetId}:${a.parent}` : undefined;\n\n this.anchors.set(qualifiedId, {\n qualifiedId,\n anchorId: a.id,\n gadgetId,\n bounds: a.bounds,\n parent: parentQualified,\n occupant: this.anchors.get(qualifiedId)?.occupant,\n attention: this.anchors.get(qualifiedId)?.attention ?? [],\n });\n }\n this.onChange?.();\n }\n\n /**\n * Update an anchor's occupancy or attention.\n */\n updateAnchor(gadgetId: string, anchorId: string, update: { occupant?: string | null; attention?: string[] }): void {\n const qualifiedId = `${gadgetId}:${anchorId}`;\n const anchor = this.anchors.get(qualifiedId);\n if (!anchor) return;\n\n if (update.occupant !== undefined) {\n anchor.occupant = update.occupant ?? undefined;\n }\n if (update.attention !== undefined) {\n anchor.attention = update.attention;\n }\n this.onChange?.();\n }\n\n /**\n * Remove all anchors for a gadget (cleanup on iframe removal).\n */\n removeGadget(gadgetId: string): void {\n const toRemove: string[] = [];\n for (const [id, anchor] of this.anchors) {\n if (anchor.gadgetId === gadgetId) {\n toRemove.push(id);\n }\n }\n for (const id of toRemove) {\n this.anchors.delete(id);\n }\n if (toRemove.length > 0) this.onChange?.();\n }\n\n /**\n * Get all anchors as a flat map.\n */\n getAnchors(): Map<string, Anchor> {\n return this.anchors;\n }\n\n /**\n * Get anchors for a specific gadget.\n */\n getGadgetAnchors(gadgetId: string): Anchor[] {\n const result: Anchor[] = [];\n for (const anchor of this.anchors.values()) {\n if (anchor.gadgetId === gadgetId) {\n result.push(anchor);\n }\n }\n return result;\n }\n\n /**\n * Get the anchor tree, optionally rooted at a specific anchor.\n */\n getAnchorTree(rootId?: string): AnchorNode[] {\n const childMap = new Map<string | undefined, Anchor[]>();\n\n for (const anchor of this.anchors.values()) {\n const parentKey = anchor.parent ?? undefined;\n if (!childMap.has(parentKey)) childMap.set(parentKey, []);\n childMap.get(parentKey)!.push(anchor);\n }\n\n const buildNode = (anchor: Anchor): AnchorNode => {\n const children = (childMap.get(anchor.qualifiedId) ?? []).map(buildNode);\n return { ...anchor, children };\n };\n\n if (rootId) {\n const root = this.anchors.get(rootId);\n if (!root) return [];\n return [buildNode(root)];\n }\n\n // Return all root-level anchors (no parent)\n return (childMap.get(undefined) ?? []).map(buildNode);\n }\n\n /**\n * Find the anchor at a given point (in room coordinates).\n * Returns the deepest (most specific) anchor containing the point.\n */\n findAnchorAt(x: number, y: number): Anchor | null {\n let best: Anchor | null = null;\n let bestArea = Infinity;\n\n for (const anchor of this.anchors.values()) {\n const b = anchor.bounds;\n if (x >= b.x && x <= b.x + b.w && y >= b.y && y <= b.y + b.h) {\n const area = b.w * b.h;\n if (area < bestArea) {\n best = anchor;\n bestArea = area;\n }\n }\n }\n return best;\n }\n\n /**\n * Get a serializable snapshot of all anchors.\n */\n toJSON(): Array<{\n id: string;\n gadgetId: string;\n bounds: AnchorBounds;\n parent?: string;\n occupant?: string;\n attention: string[];\n }> {\n return Array.from(this.anchors.values()).map((a) => ({\n id: a.qualifiedId,\n gadgetId: a.gadgetId,\n bounds: a.bounds,\n parent: a.parent,\n occupant: a.occupant,\n attention: a.attention,\n }));\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
package/react.js
CHANGED
|
@@ -41,8 +41,10 @@ function createGadgetRenderer(componentCode, data = {}, options) {
|
|
|
41
41
|
const chatEnabled = options?.chatEnabled ?? false;
|
|
42
42
|
const contextEnabled = options?.contextEnabled ?? false;
|
|
43
43
|
const roomEnabled = options?.roomEnabled ?? false;
|
|
44
|
+
const libraries = options?.libraries ?? [];
|
|
44
45
|
const escapedCode = componentCode.replace(/<\/script>/g, "<\\/script>");
|
|
45
46
|
const serializedAssets = JSON.stringify(options?.assets ?? {});
|
|
47
|
+
const libraryScripts = libraries.map((url) => ` <script src="${url}"><\/script>`).join("\n");
|
|
46
48
|
const stateBridgeScript = stateEnabled ? `
|
|
47
49
|
window.gadget.state = {
|
|
48
50
|
shared: {},
|
|
@@ -372,6 +374,7 @@ function createGadgetRenderer(componentCode, data = {}, options) {
|
|
|
372
374
|
<script src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"><\/script>
|
|
373
375
|
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"><\/script>
|
|
374
376
|
<script src="https://cdn.tailwindcss.com"><\/script>
|
|
377
|
+
${libraryScripts}
|
|
375
378
|
<style>
|
|
376
379
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
377
380
|
html, body { background: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-height: 100%; }
|
|
@@ -573,37 +576,7 @@ function GadgetFrame({
|
|
|
573
576
|
const { config, resolveFileUrl } = useGadgetConfig();
|
|
574
577
|
const base = `${config.apiUrl}/${config.orgId}/${config.projectId}`;
|
|
575
578
|
const lazyEnabled = lazy ?? mode === "compact";
|
|
576
|
-
const sentinelRef = useRef(null);
|
|
577
579
|
const [visible, setVisible] = useState(!lazyEnabled);
|
|
578
|
-
useEffect(() => {
|
|
579
|
-
if (!lazyEnabled || visible)
|
|
580
|
-
return;
|
|
581
|
-
const el = sentinelRef.current;
|
|
582
|
-
if (!el)
|
|
583
|
-
return;
|
|
584
|
-
const observer = new IntersectionObserver(
|
|
585
|
-
([entry]) => {
|
|
586
|
-
if (entry.isIntersecting) {
|
|
587
|
-
setVisible(true);
|
|
588
|
-
observer.disconnect();
|
|
589
|
-
}
|
|
590
|
-
},
|
|
591
|
-
{ rootMargin: "200px" }
|
|
592
|
-
// start loading 200px before entering viewport
|
|
593
|
-
);
|
|
594
|
-
observer.observe(el);
|
|
595
|
-
return () => observer.disconnect();
|
|
596
|
-
}, [lazyEnabled, visible]);
|
|
597
|
-
if (!visible) {
|
|
598
|
-
return /* @__PURE__ */ jsx2(
|
|
599
|
-
"div",
|
|
600
|
-
{
|
|
601
|
-
ref: sentinelRef,
|
|
602
|
-
className,
|
|
603
|
-
style: { width: "100%", height: maxHeight, minHeight: 60 }
|
|
604
|
-
}
|
|
605
|
-
);
|
|
606
|
-
}
|
|
607
580
|
const [html, setHtml] = useState(null);
|
|
608
581
|
const [loading, setLoading] = useState(true);
|
|
609
582
|
const [error, setError] = useState(null);
|
|
@@ -611,6 +584,7 @@ function GadgetFrame({
|
|
|
611
584
|
const [iframeLoaded, setIframeLoaded] = useState(false);
|
|
612
585
|
const [breakoutActive, setBreakoutActive] = useState(false);
|
|
613
586
|
const [breakoutRevealed, setBreakoutRevealed] = useState(false);
|
|
587
|
+
const sentinelRef = useRef(null);
|
|
614
588
|
const iframeRef = useRef(null);
|
|
615
589
|
const entityRef = useRef(null);
|
|
616
590
|
const initialPropsRef = useRef(props);
|
|
@@ -626,6 +600,27 @@ function GadgetFrame({
|
|
|
626
600
|
const isCapped = isCompact && naturalHeight > maxHeight;
|
|
627
601
|
const iframeHeight = isCapped ? maxHeight : naturalHeight;
|
|
628
602
|
useEffect(() => {
|
|
603
|
+
if (!lazyEnabled || visible)
|
|
604
|
+
return;
|
|
605
|
+
const el = sentinelRef.current;
|
|
606
|
+
if (!el)
|
|
607
|
+
return;
|
|
608
|
+
const observer = new IntersectionObserver(
|
|
609
|
+
([entry]) => {
|
|
610
|
+
if (entry.isIntersecting) {
|
|
611
|
+
setVisible(true);
|
|
612
|
+
observer.disconnect();
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
{ rootMargin: "200px" }
|
|
616
|
+
// start loading 200px before entering viewport
|
|
617
|
+
);
|
|
618
|
+
observer.observe(el);
|
|
619
|
+
return () => observer.disconnect();
|
|
620
|
+
}, [lazyEnabled, visible]);
|
|
621
|
+
useEffect(() => {
|
|
622
|
+
if (!visible)
|
|
623
|
+
return;
|
|
629
624
|
let cancelled = false;
|
|
630
625
|
(async () => {
|
|
631
626
|
try {
|
|
@@ -708,7 +703,7 @@ function GadgetFrame({
|
|
|
708
703
|
sseRef.current = null;
|
|
709
704
|
}
|
|
710
705
|
};
|
|
711
|
-
}, [gadgetId, base, config.getToken, config.streamBaseUrl, config.orgId, config.projectId, mode]);
|
|
706
|
+
}, [visible, gadgetId, base, config.getToken, config.streamBaseUrl, config.orgId, config.projectId, mode]);
|
|
712
707
|
useEffect(() => {
|
|
713
708
|
if (!iframeLoaded || !latestStateRef.current)
|
|
714
709
|
return;
|
|
@@ -728,7 +723,7 @@ function GadgetFrame({
|
|
|
728
723
|
iframeRef.current?.contentWindow?.postMessage({ type: "gadget-set-data", data: props }, "*");
|
|
729
724
|
}, [props, iframeLoaded]);
|
|
730
725
|
useEffect(() => {
|
|
731
|
-
if (!iframeRef.current || !html || !breakoutEnabled)
|
|
726
|
+
if (!visible || !iframeRef.current || !html || !breakoutEnabled)
|
|
732
727
|
return;
|
|
733
728
|
const iframe = iframeRef.current;
|
|
734
729
|
function clearAncestorClipping() {
|
|
@@ -814,7 +809,7 @@ function GadgetFrame({
|
|
|
814
809
|
setBreakoutActive(false);
|
|
815
810
|
setBreakoutRevealed(false);
|
|
816
811
|
};
|
|
817
|
-
}, [html, breakoutEnabled, onBreakoutChange]);
|
|
812
|
+
}, [visible, html, breakoutEnabled, onBreakoutChange]);
|
|
818
813
|
const handleMessage = useCallback(
|
|
819
814
|
async (event) => {
|
|
820
815
|
const data = event.data;
|
|
@@ -917,9 +912,21 @@ function GadgetFrame({
|
|
|
917
912
|
[base, config.getToken, resolveFileUrl, gadgetId, isCompact]
|
|
918
913
|
);
|
|
919
914
|
useEffect(() => {
|
|
915
|
+
if (!visible)
|
|
916
|
+
return;
|
|
920
917
|
window.addEventListener("message", handleMessage);
|
|
921
918
|
return () => window.removeEventListener("message", handleMessage);
|
|
922
|
-
}, [handleMessage]);
|
|
919
|
+
}, [visible, handleMessage]);
|
|
920
|
+
if (!visible) {
|
|
921
|
+
return /* @__PURE__ */ jsx2(
|
|
922
|
+
"div",
|
|
923
|
+
{
|
|
924
|
+
ref: sentinelRef,
|
|
925
|
+
className,
|
|
926
|
+
style: { width: "100%", height: maxHeight, minHeight: 60 }
|
|
927
|
+
}
|
|
928
|
+
);
|
|
929
|
+
}
|
|
923
930
|
if (error) {
|
|
924
931
|
return /* @__PURE__ */ jsx2(
|
|
925
932
|
"div",
|
package/react.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../libs/gadgets/src/lib/react/GadgetContext.tsx", "../../../libs/gadgets/src/lib/react/GadgetFrame.tsx", "../../../libs/gadgets/src/lib/createGadgetRenderer.ts"],
|
|
4
|
-
"sourcesContent": ["import { createContext, useContext, useMemo, type ReactNode } from 'react';\n\nexport interface GadgetConfig {\n /** Base API URL (e.g. \"https://api.gigabuddy.com\") */\n apiUrl: string;\n /** Organization ID */\n orgId: string;\n /** Project ID */\n projectId: string;\n /** Returns a fresh auth token */\n getToken: () => Promise<string>;\n /** Optional SSE stream base URL (falls back to apiUrl) */\n streamBaseUrl?: string;\n}\n\ninterface GadgetContextValue {\n config: GadgetConfig;\n /** Resolves a file ID to a fresh signed URL */\n resolveFileUrl: (fileId: string) => Promise<string>;\n}\n\nconst GadgetCtx = createContext<GadgetContextValue | null>(null);\n\nexport function GadgetProvider({ config, children }: { config: GadgetConfig; children: ReactNode }) {\n const value = useMemo<GadgetContextValue>(() => {\n const base = `${config.apiUrl}/${config.orgId}/${config.projectId}`;\n\n return {\n config,\n resolveFileUrl: async (fileId: string) => {\n const token = await config.getToken();\n const res = await fetch(`${base}/context/get-file-url`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ fileId }),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as { url: string };\n return data.url;\n },\n };\n }, [config.apiUrl, config.orgId, config.projectId, config.getToken, config.streamBaseUrl]);\n\n return <GadgetCtx.Provider value={value}>{children}</GadgetCtx.Provider>;\n}\n\nexport function useGadgetConfig(): GadgetContextValue {\n const ctx = useContext(GadgetCtx);\n if (!ctx) throw new Error('GadgetFrame must be wrapped in a <GadgetProvider>');\n return ctx;\n}\n", "import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { createGadgetRenderer } from '../createGadgetRenderer.js';\nimport { useGadgetConfig } from './GadgetContext.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface GadgetFrameProps {\n /** The gadget entity ID to load */\n gadgetId: string;\n /** Data props to pass to the gadget */\n props?: Record<string, unknown>;\n /** Display mode \u2014 compact (inline, resizable) or full (fills container) */\n mode?: 'compact' | 'full';\n /** Whether breakout is enabled (default: true in compact mode) */\n breakout?: boolean;\n /** Max height in compact mode before scrolling (default: 300) */\n maxHeight?: number;\n /** Deployment ID for mission-backed gadgets (auto-generated if not provided) */\n deploymentId?: string;\n /** Conversation ID \u2014 used when updating message content after action */\n conversationId?: string;\n /** Message ID \u2014 used when updating message content after action */\n messageId?: string;\n /** Additional className for the wrapper div */\n className?: string;\n /** Called when breakout state changes */\n onBreakoutChange?: (active: boolean) => void;\n /** Called on load error */\n onError?: (error: string) => void;\n /** Whether to lazy-load (only render when near viewport). Default: true in compact mode. */\n lazy?: boolean;\n}\n\ninterface GadgetEntity {\n data: {\n type: string;\n componentCode: string;\n actionRef?: { type: string; id: string };\n missionRef?: { type: string; id: string };\n stateBindings?: {\n shared?: { contextFields: string[] };\n user?: { contextFields: string[] };\n };\n assets?: Record<string, { fileId: string; url: string; name: string; contentType: string }>;\n };\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function GadgetFrame({\n gadgetId,\n props,\n mode = 'compact',\n breakout: breakoutEnabled = true,\n maxHeight = 300,\n deploymentId: externalDeploymentId,\n conversationId,\n messageId,\n className,\n onBreakoutChange,\n onError,\n lazy,\n}: GadgetFrameProps) {\n const { config, resolveFileUrl } = useGadgetConfig();\n const base = `${config.apiUrl}/${config.orgId}/${config.projectId}`;\n\n // \u2500\u2500 Lazy loading: defer everything until the gadget is near the viewport \u2500\u2500\n const lazyEnabled = lazy ?? mode === 'compact';\n const sentinelRef = useRef<HTMLDivElement>(null);\n const [visible, setVisible] = useState(!lazyEnabled);\n\n useEffect(() => {\n if (!lazyEnabled || visible) return;\n const el = sentinelRef.current;\n if (!el) return;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setVisible(true);\n observer.disconnect();\n }\n },\n { rootMargin: '200px' }, // start loading 200px before entering viewport\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [lazyEnabled, visible]);\n\n if (!visible) {\n return (\n <div\n ref={sentinelRef}\n className={className}\n style={{ width: '100%', height: maxHeight, minHeight: 60 }}\n />\n );\n }\n\n // \u2500\u2500 State \u2500\u2500\n const [html, setHtml] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [naturalHeight, setNaturalHeight] = useState(60);\n const [iframeLoaded, setIframeLoaded] = useState(false);\n const [breakoutActive, setBreakoutActive] = useState(false);\n const [breakoutRevealed, setBreakoutRevealed] = useState(false);\n\n // \u2500\u2500 Refs \u2500\u2500\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const entityRef = useRef<GadgetEntity | null>(null);\n const initialPropsRef = useRef(props);\n const deploymentIdRef = useRef<string | null>(externalDeploymentId ?? null);\n const sseRef = useRef<EventSource | null>(null);\n const userIdRef = useRef<string | null>(null);\n const latestStateRef = useRef<{ shared: Record<string, unknown>; user: Record<string, unknown> } | null>(null);\n const placeholderRef = useRef<HTMLDivElement | null>(null);\n const ancestorOverridesRef = useRef<{ el: HTMLElement; key: string; original: string }[]>([]);\n const breakoutActiveRef = useRef(false);\n breakoutActiveRef.current = breakoutActive;\n\n // \u2500\u2500 Derived \u2500\u2500\n const isCompact = mode === 'compact';\n const isCapped = isCompact && naturalHeight > maxHeight;\n const iframeHeight = isCapped ? maxHeight : naturalHeight;\n\n // \u2500\u2500 Fetch entity + build HTML + deploy \u2500\u2500\n useEffect(() => {\n let cancelled = false;\n\n (async () => {\n try {\n const token = await config.getToken();\n const res = await fetch(`${base}/chat/get-gadget`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ gadgetId }),\n });\n if (!res.ok) throw new Error(`Failed to load gadget: ${res.status}`);\n\n const entity = (await res.json()) as GadgetEntity;\n if (cancelled) return;\n\n entityRef.current = entity;\n const hasMission = entity.data.missionRef?.id != null;\n\n const assetMap = entity.data.assets\n ? Object.fromEntries(Object.entries(entity.data.assets).map(([k, v]) => [k, { url: v.url, name: v.name }]))\n : undefined;\n\n const builtHtml = createGadgetRenderer(entity.data.componentCode, initialPropsRef.current ?? {}, {\n viewport: mode,\n stateEnabled: hasMission,\n assets: assetMap,\n });\n setHtml(builtHtml);\n\n // Deploy if mission-backed\n if (hasMission) {\n const depId = externalDeploymentId ?? messageId ?? `gadget_${gadgetId}_${Date.now()}`;\n deploymentIdRef.current = depId;\n\n const deployRes = await fetch(`${base}/gadget-deploy`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n gadgetId,\n deploymentId: depId,\n initialData: initialPropsRef.current,\n conversationId,\n }),\n });\n if (!deployRes.ok) throw new Error(`Failed to deploy gadget: ${deployRes.status}`);\n\n const deployData = (await deployRes.json()) as {\n deploymentId: string;\n missionInstanceId: string;\n initialState: { shared: Record<string, unknown>; user: Record<string, unknown> };\n userId?: string;\n };\n if (cancelled) return;\n\n if (deployData.userId) userIdRef.current = deployData.userId;\n latestStateRef.current = deployData.initialState;\n\n // Connect SSE\n const streamBase = config.streamBaseUrl ?? config.apiUrl;\n const sseUrl = `${streamBase}/${config.orgId}/${config.projectId}/gadget-state/${depId}/stream?token=${encodeURIComponent(token)}`;\n const sse = new EventSource(sseUrl);\n sseRef.current = sse;\n\n sse.addEventListener('state-update', (event) => {\n if (cancelled) return;\n try {\n const state = JSON.parse(event.data) as {\n shared: Record<string, unknown>;\n user: Record<string, unknown>;\n userId?: string;\n };\n if (state.userId && !userIdRef.current) userIdRef.current = state.userId;\n latestStateRef.current = state;\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-state-update', shared: state.shared, user: state.user, userId: userIdRef.current },\n '*',\n );\n } catch {\n // Ignore malformed SSE\n }\n });\n }\n } catch (err) {\n if (!cancelled) {\n const msg = err instanceof Error ? err.message : 'Failed to load gadget';\n setError(msg);\n onError?.(msg);\n }\n } finally {\n if (!cancelled) setLoading(false);\n }\n })();\n\n return () => {\n cancelled = true;\n if (sseRef.current) {\n sseRef.current.close();\n sseRef.current = null;\n }\n };\n }, [gadgetId, base, config.getToken, config.streamBaseUrl, config.orgId, config.projectId, mode]);\n\n // \u2500\u2500 Push initial state on iframe load \u2500\u2500\n useEffect(() => {\n if (!iframeLoaded || !latestStateRef.current) return;\n iframeRef.current?.contentWindow?.postMessage(\n {\n type: 'gadget-state-init',\n shared: latestStateRef.current.shared,\n user: latestStateRef.current.user,\n userId: userIdRef.current,\n },\n '*',\n );\n }, [iframeLoaded]);\n\n // \u2500\u2500 Push props changes \u2500\u2500\n useEffect(() => {\n if (!iframeLoaded || !props) return;\n iframeRef.current?.contentWindow?.postMessage({ type: 'gadget-set-data', data: props }, '*');\n }, [props, iframeLoaded]);\n\n // \u2500\u2500 Breakout handler \u2500\u2500\n useEffect(() => {\n if (!iframeRef.current || !html || !breakoutEnabled) return;\n const iframe = iframeRef.current;\n\n function clearAncestorClipping() {\n let el = iframe.parentElement;\n while (el && el !== document.body && el !== document.documentElement) {\n const cs = getComputedStyle(el);\n const overflow = cs.overflow + cs.overflowX + cs.overflowY;\n const hasClip = /hidden|auto|scroll|clip/.test(overflow);\n const hasContain =\n cs.transform !== 'none' || cs.willChange === 'transform' || cs.contain !== 'none' || cs.filter !== 'none';\n\n if (hasClip || hasContain) {\n if (hasClip) {\n ancestorOverridesRef.current.push({ el, key: 'overflow', original: el.style.overflow });\n ancestorOverridesRef.current.push({ el, key: 'overflowX', original: el.style.overflowX });\n ancestorOverridesRef.current.push({ el, key: 'overflowY', original: el.style.overflowY });\n el.style.overflow = 'visible';\n el.style.overflowX = 'visible';\n el.style.overflowY = 'visible';\n }\n if (hasContain && cs.transform !== 'none') {\n ancestorOverridesRef.current.push({ el, key: 'transform', original: el.style.transform });\n el.style.transform = 'none';\n }\n if (hasContain && cs.contain !== 'none') {\n ancestorOverridesRef.current.push({ el, key: 'contain', original: el.style.contain });\n el.style.contain = 'none';\n }\n }\n el = el.parentElement;\n }\n }\n\n function restoreAncestorClipping() {\n for (const { el, key, original } of ancestorOverridesRef.current) {\n (el.style as unknown as Record<string, string>)[key] = original;\n }\n ancestorOverridesRef.current.length = 0;\n }\n\n function onMessage(event: MessageEvent) {\n if (event.source !== iframe.contentWindow) return;\n const d = event.data;\n if (!d || typeof d !== 'object') return;\n\n if (d.type === 'gadget-request-breakout') {\n const rect = iframe.getBoundingClientRect();\n\n // Placeholder to preserve layout\n const ph = document.createElement('div');\n ph.style.width = `${rect.width}px`;\n ph.style.height = `${rect.height}px`;\n ph.style.flexShrink = '0';\n iframe.parentElement?.insertBefore(ph, iframe);\n placeholderRef.current = ph;\n\n clearAncestorClipping();\n setBreakoutActive(true);\n setBreakoutRevealed(false);\n onBreakoutChange?.(true);\n\n iframe.contentWindow?.postMessage(\n {\n type: 'gadget-breakout-started',\n originalRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n },\n '*',\n );\n\n // Reveal after repositioning\n requestAnimationFrame(() => requestAnimationFrame(() => setBreakoutRevealed(true)));\n }\n\n if (d.type === 'gadget-exit-breakout') {\n if (placeholderRef.current) {\n placeholderRef.current.remove();\n placeholderRef.current = null;\n }\n restoreAncestorClipping();\n setBreakoutActive(false);\n setBreakoutRevealed(false);\n onBreakoutChange?.(false);\n }\n }\n\n window.addEventListener('message', onMessage);\n return () => {\n window.removeEventListener('message', onMessage);\n if (placeholderRef.current) {\n placeholderRef.current.remove();\n placeholderRef.current = null;\n }\n restoreAncestorClipping();\n setBreakoutActive(false);\n setBreakoutRevealed(false);\n };\n }, [html, breakoutEnabled, onBreakoutChange]);\n\n // \u2500\u2500 Bridge handler (actions, interactions, file URL, resize) \u2500\u2500\n const handleMessage = useCallback(\n async (event: MessageEvent<unknown>) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const data = event.data as any;\n if (!data || typeof data !== 'object') return;\n if (event.source !== iframeRef.current?.contentWindow) return;\n\n const type = data.type as string;\n\n // Resize (compact only)\n if (type === 'gadget-resize') {\n if (breakoutActiveRef.current || !isCompact) return;\n const h = data.height as number;\n if (h > 0) setNaturalHeight(h);\n return;\n }\n\n // Interaction dispatch\n if (type === 'gadget-interaction' && deploymentIdRef.current) {\n void (async () => {\n try {\n const token = await config.getToken();\n await fetch(`${base}/gadget-interact`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n deploymentId: deploymentIdRef.current,\n action: data.action as string,\n data: (data.data as Record<string, unknown>) ?? {},\n }),\n });\n } catch {\n // Fire-and-forget\n }\n })();\n return;\n }\n\n // File URL resolution\n if (type === 'gadget-file-url-request') {\n const requestId = data.requestId as string;\n const fileId = data.fileId as string;\n if (!requestId || !fileId) return;\n void (async () => {\n try {\n const url = await resolveFileUrl(fileId);\n iframeRef.current?.contentWindow?.postMessage({ type: 'gadget-file-url-response', requestId, url }, '*');\n } catch (err) {\n iframeRef.current?.contentWindow?.postMessage(\n {\n type: 'gadget-file-url-response',\n requestId,\n error: err instanceof Error ? err.message : 'Failed to resolve file URL',\n },\n '*',\n );\n }\n })();\n return;\n }\n\n // Action execution\n if (type === 'gadget-action-request') {\n const requestId = data.requestId as string;\n const input = (data.input as Record<string, unknown>) || {};\n const entity = entityRef.current;\n\n if (!entity?.data.actionRef?.id) {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, error: 'No action configured' },\n '*',\n );\n return;\n }\n\n try {\n const token = await config.getToken();\n const res = await fetch(`${base}/chat/execute-gadget`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ gadgetId, input }),\n });\n\n const result = (await res.json()) as {\n status?: string;\n error?: string;\n errorMessage?: string;\n output?: unknown;\n };\n\n if (!res.ok) {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, error: result.error || `HTTP ${res.status}` },\n '*',\n );\n } else if (result.status === 'failure') {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, error: result.errorMessage || 'Action failed' },\n '*',\n );\n } else {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, result: result.output },\n '*',\n );\n }\n } catch (err) {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, error: err instanceof Error ? err.message : 'Unknown error' },\n '*',\n );\n }\n }\n },\n [base, config.getToken, resolveFileUrl, gadgetId, isCompact],\n );\n\n useEffect(() => {\n window.addEventListener('message', handleMessage);\n return () => window.removeEventListener('message', handleMessage);\n }, [handleMessage]);\n\n // \u2500\u2500 Render \u2500\u2500\n\n if (error) {\n return (\n <div\n className={className}\n style={{\n padding: '8px 12px',\n fontSize: 12,\n color: '#b91c1c',\n background: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: 8,\n }}\n >\n {error}\n </div>\n );\n }\n\n if (loading) {\n return (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '12px', fontSize: 12, color: '#6b7280' }}\n >\n <span\n style={{\n display: 'inline-block',\n width: 16,\n height: 16,\n border: '2px solid #d1d5db',\n borderTopColor: '#6b7280',\n borderRadius: '50%',\n animation: 'gadget-spin 0.6s linear infinite',\n }}\n />\n Loading gadget...\n <style>{`@keyframes gadget-spin { to { transform: rotate(360deg) } }`}</style>\n </div>\n );\n }\n\n if (!html) return null;\n\n const compactStyle = breakoutActive\n ? {\n position: 'fixed' as const,\n top: 0,\n left: 0,\n width: '100vw',\n height: '100vh',\n zIndex: 99999,\n background: 'transparent',\n border: 'none',\n borderRadius: 0,\n maxWidth: 'none',\n maxHeight: 'none',\n margin: 0,\n transform: 'none',\n pointerEvents: 'none' as const,\n overflow: 'visible',\n visibility: breakoutRevealed ? ('visible' as const) : ('hidden' as const),\n }\n : {\n width: '100%',\n height: iframeHeight,\n border: 'none',\n borderRadius: 8,\n overflow: isCapped ? undefined : ('hidden' as const),\n };\n\n const fullStyle = {\n width: '100%',\n height: '100%',\n border: 'none',\n };\n\n return (\n <div className={className} style={isCompact ? { position: 'relative' } : { width: '100%', height: '100%' }}>\n <iframe\n ref={iframeRef}\n srcDoc={html}\n style={isCompact ? compactStyle : fullStyle}\n scrolling={isCompact && !breakoutActive && !isCapped ? 'no' : undefined}\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox\"\n title=\"Gadget\"\n onLoad={() => setIframeLoaded(true)}\n />\n </div>\n );\n}\n", "/**\n * Creates a self-contained HTML string for rendering a gadget component in an iframe.\n *\n * Loads React 18, ReactDOM, Babel (for JSX transpilation), and Tailwind CSS from CDNs.\n * Sets up the full gadget bridge: state, breakout, actions, chat, context, and hot-swap.\n *\n * Usage:\n * import { createGadgetRenderer, setupGadgetBreakout } from '@gigabuddy/chat';\n *\n * const html = createGadgetRenderer(componentCode, data, { viewport: 'compact', stateEnabled: true });\n * iframe.srcdoc = html;\n * setupGadgetBreakout(iframe);\n *\n * The gadget component receives these props:\n * function Gadget({ data, viewport, state, userId, breakout, chat, context })\n *\n * Bridge protocol (postMessage):\n * - Host \u2192 iframe: `gadget-set-data` \u2014 push new props\n * - Host \u2192 iframe: `gadget-state-init` / `gadget-state-update` \u2014 state changes\n * - Host \u2192 iframe: `gadget-update-component` \u2014 hot-swap component code\n * - Host \u2192 iframe: `gadget-breakout-started` \u2014 breakout activated with originalRect\n * - iframe \u2192 Host: `gadget-interaction` \u2014 user dispatched an action\n * - iframe \u2192 Host: `gadget-resize` \u2014 auto-height\n * - iframe \u2192 Host: `gadget-request-breakout` / `gadget-exit-breakout` \u2014 breakout lifecycle\n * - iframe \u2192 Host: `gadget-action-request` / `gadget-chat-request` / `gadget-context-request` \u2014 API calls\n */\nexport function createGadgetRenderer(\n componentCode: string,\n data: unknown = {},\n options?: {\n viewport?: 'compact' | 'full' | 'mobile';\n stateEnabled?: boolean;\n chatEnabled?: boolean;\n contextEnabled?: boolean;\n roomEnabled?: boolean;\n assets?: Record<string, { url: string; name: string }>;\n },\n): string {\n const serializedData = JSON.stringify(data);\n const viewport = options?.viewport ?? 'full';\n const stateEnabled = options?.stateEnabled ?? false;\n const chatEnabled = options?.chatEnabled ?? false;\n const contextEnabled = options?.contextEnabled ?? false;\n const roomEnabled = options?.roomEnabled ?? false;\n const escapedCode = componentCode.replace(/<\\/script>/g, '<\\\\/script>');\n const serializedAssets = JSON.stringify(options?.assets ?? {});\n\n const stateBridgeScript = stateEnabled\n ? `\n window.gadget.state = {\n shared: {},\n user: {},\n userId: null,\n _sharedCallbacks: [],\n _userCallbacks: [],\n\n dispatch: function(action, data) {\n window.parent.postMessage({ type: 'gadget-interaction', action: action, data: data || {} }, '*');\n },\n\n onSharedChange: function(callback) {\n window.gadget.state._sharedCallbacks.push(callback);\n callback(window.gadget.state.shared);\n return function() {\n var i = window.gadget.state._sharedCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.state._sharedCallbacks.splice(i, 1);\n };\n },\n\n onUserChange: function(callback) {\n window.gadget.state._userCallbacks.push(callback);\n callback(window.gadget.state.user);\n return function() {\n var i = window.gadget.state._userCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.state._userCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && (d.type === 'gadget-state-init' || d.type === 'gadget-state-update')) {\n if (d.userId !== undefined) {\n window.gadget.state.userId = d.userId;\n }\n if (d.shared !== undefined) {\n window.gadget.state.shared = d.shared;\n window.gadget.state._sharedCallbacks.forEach(function(cb) { try { cb(d.shared); } catch(e) {} });\n }\n if (d.user !== undefined) {\n window.gadget.state.user = d.user;\n window.gadget.state._userCallbacks.forEach(function(cb) { try { cb(d.user); } catch(e) {} });\n }\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const chatBridgeScript = chatEnabled\n ? `\n window.gadget.chat = {\n _request: function(method, params) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-chat-response' && d.requestId === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() { window.removeEventListener('message', handler); reject(new Error('Timed out')); }, 30000);\n window.parent.postMessage({ type: 'gadget-chat-request', requestId: requestId, method: method, params: params || {} }, '*');\n });\n },\n\n _eventCallbacks: [],\n\n createChannel: function(opts) { return window.gadget.chat._request('createChannel', opts); },\n findOrCreateChannel: function(name, opts) { return window.gadget.chat._request('findOrCreateChannel', { name: name, description: opts && opts.description }); },\n listChannels: function() { return window.gadget.chat._request('listChannels'); },\n joinChannel: function(conversationId) { return window.gadget.chat._request('joinChannel', { conversationId: conversationId }); },\n sendMessage: function(conversationId, text) { return window.gadget.chat._request('sendMessage', { conversationId: conversationId, text: text }); },\n getMessages: function(conversationId, opts) { return window.gadget.chat._request('getMessages', { conversationId: conversationId, limit: opts && opts.limit, before: opts && opts.before }); },\n updateChannel: function(conversationId, updates) { return window.gadget.chat._request('updateChannel', { conversationId: conversationId, description: updates && updates.description }); },\n createInvite: function(conversationId) { return window.gadget.chat._request('createInvite', { conversationId: conversationId }); },\n redeemInvite: function(token) { return window.gadget.chat._request('redeemInvite', { token: token }); },\n\n onEvent: function(callback) {\n window.gadget.chat._eventCallbacks.push(callback);\n return function() {\n var i = window.gadget.chat._eventCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.chat._eventCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-chat-event') {\n window.gadget.chat._eventCallbacks.forEach(function(cb) { try { cb(d.event); } catch(e) {} });\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const contextBridgeScript = contextEnabled\n ? `\n window.gadget.context = {\n _request: function(method, params) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-context-response' && d.id === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() { window.removeEventListener('message', handler); reject(new Error('Timed out')); }, 30000);\n window.parent.postMessage({ type: 'gadget-context-request', id: requestId, method: method, params: params || {} }, '*');\n });\n },\n\n _eventCallbacks: [],\n\n listTypes: function() { return window.gadget.context._request('listTypes', {}); },\n getType: function(contextType) { return window.gadget.context._request('getType', { contextType: contextType }); },\n listInstances: function(params) { return window.gadget.context._request('listInstances', params || {}); },\n getInstance: function(id) { return window.gadget.context._request('getInstance', { id: id }); },\n createInstance: function(params) { return window.gadget.context._request('createInstance', params); },\n updateInstance: function(id, data) { return window.gadget.context._request('updateInstance', { id: id, data: data }); },\n deleteInstance: function(id) { return window.gadget.context._request('deleteInstance', { id: id }); },\n searchKnowledge: function(params) { return window.gadget.context._request('searchKnowledge', params); },\n getEnriched: function(id) { return window.gadget.context._request('getEnriched', { id: id }); },\n getRelated: function(id) { return window.gadget.context._request('getRelated', { id: id }); },\n\n onEvent: function(callback) {\n window.gadget.context._eventCallbacks.push(callback);\n return function() {\n var i = window.gadget.context._eventCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.context._eventCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-context-event') {\n window.gadget.context._eventCallbacks.forEach(function(cb) { try { cb(d.event); } catch(e) {} });\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const roomBridgeScript = roomEnabled\n ? `\n // \u2500\u2500 Room presence \u2500\u2500\n window.gadget.room = {\n peers: [],\n userId: null,\n displayName: null,\n _peersCallbacks: [],\n _playerJoinedCallbacks: [],\n _playerLeftCallbacks: [],\n\n setCursor: function(pos) {\n window.parent.postMessage({ type: 'gadget-presence', cursor: pos }, '*');\n },\n setSelection: function(sel) {\n window.parent.postMessage({ type: 'gadget-presence', selection: sel }, '*');\n },\n onPeersChange: function(cb) {\n window.gadget.room._peersCallbacks.push(cb);\n cb(window.gadget.room.peers);\n return function() {\n var i = window.gadget.room._peersCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._peersCallbacks.splice(i, 1);\n };\n },\n onPlayerJoined: function(cb) {\n window.gadget.room._playerJoinedCallbacks.push(cb);\n return function() {\n var i = window.gadget.room._playerJoinedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._playerJoinedCallbacks.splice(i, 1);\n };\n },\n onPlayerLeft: function(cb) {\n window.gadget.room._playerLeftCallbacks.push(cb);\n return function() {\n var i = window.gadget.room._playerLeftCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._playerLeftCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Room chat \u2500\u2500\n window.gadget.roomChat = {\n messages: [],\n _messageCallbacks: [],\n\n send: function(text) {\n window.parent.postMessage({ type: 'gadget-chat-send', text: text }, '*');\n },\n onMessage: function(cb) {\n window.gadget.roomChat._messageCallbacks.push(cb);\n return function() {\n var i = window.gadget.roomChat._messageCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.roomChat._messageCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Gestures \u2500\u2500\n window.gadget.gestures = {\n active: [],\n _spawnedCallbacks: [],\n _dismissedCallbacks: [],\n\n spawn: function(gadgetId, anchor, opts) {\n opts = opts || {};\n window.parent.postMessage({\n type: 'gadget-gesture-send',\n gadgetId: gadgetId,\n anchor: anchor,\n ttl: opts.ttl,\n size: opts.size,\n rotation: opts.rotation,\n }, '*');\n },\n dismiss: function(gestureId) {\n window.parent.postMessage({ type: 'gadget-gesture-dismiss', gestureId: gestureId }, '*');\n },\n reportAnchorRect: function(selector, rect) {\n window.parent.postMessage({ type: 'gadget-anchor-rect', selector: selector, rect: rect }, '*');\n },\n onSpawned: function(cb) {\n window.gadget.gestures._spawnedCallbacks.push(cb);\n return function() {\n var i = window.gadget.gestures._spawnedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.gestures._spawnedCallbacks.splice(i, 1);\n };\n },\n onDismissed: function(cb) {\n window.gadget.gestures._dismissedCallbacks.push(cb);\n return function() {\n var i = window.gadget.gestures._dismissedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.gestures._dismissedCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Buddy attraction \u2500\u2500\n window.gadget.buddy = {\n attract: function(anchor, interest) {\n window.parent.postMessage({\n type: 'gadget-buddy-attract',\n anchor: anchor,\n interest: interest || 'medium',\n }, '*');\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (!d) return;\n\n // Presence updates\n if (d.type === 'gadget-presence-update') {\n window.gadget.room.peers = d.peers || [];\n window.gadget.room._peersCallbacks.forEach(function(cb) { try { cb(d.peers || []); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Player join/leave\n if (d.type === 'gadget-player-joined') {\n window.gadget.room._playerJoinedCallbacks.forEach(function(cb) { try { cb(d); } catch(e) {} });\n }\n if (d.type === 'gadget-player-left') {\n window.gadget.room.peers = window.gadget.room.peers.filter(function(p) { return p.userId !== d.userId; });\n window.gadget.room._playerLeftCallbacks.forEach(function(cb) { try { cb(d.userId); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Chat\n if (d.type === 'gadget-chat-message') {\n var msg = d.message || d;\n window.gadget.roomChat.messages.push(msg);\n if (window.gadget.roomChat.messages.length > 100) window.gadget.roomChat.messages.shift();\n window.gadget.roomChat._messageCallbacks.forEach(function(cb) { try { cb(msg); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-chat-history') {\n window.gadget.roomChat.messages = d.messages || [];\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Gestures\n if (d.type === 'gadget-gesture-spawned') {\n window.gadget.gestures.active.push(d.gesture);\n window.gadget.gestures._spawnedCallbacks.forEach(function(cb) { try { cb(d.gesture); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-gesture-dismissed') {\n window.gadget.gestures.active = window.gadget.gestures.active.filter(function(g) { return g.id !== d.gestureId; });\n window.gadget.gestures._dismissedCallbacks.forEach(function(cb) { try { cb(d.gestureId); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-gesture-sync') {\n window.gadget.gestures.active = d.gestures || [];\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Anchor rect request from host\n if (d.type === 'gadget-request-anchor-rect' && d.selector) {\n try {\n var el = document.querySelector(d.selector);\n if (el) {\n var rect = el.getBoundingClientRect();\n window.parent.postMessage({\n type: 'gadget-anchor-rect',\n selector: d.selector,\n rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n }, '*');\n }\n } catch(e) {}\n }\n });`\n : '';\n\n // When state is enabled, pass state prop alongside data and viewport\n const chatPropFragment = chatEnabled ? ', chat: window.gadget.chat' : '';\n const contextPropFragment = contextEnabled ? ', context: window.gadget.context' : '';\n const roomPropFragment = roomEnabled\n ? ', room: window.gadget.room, roomChat: window.gadget.roomChat, gestures: window.gadget.gestures'\n : '';\n const breakoutPropFragment =\n ', breakout: { active: window.gadget.breakout._active, originalRect: window.gadget.breakout._originalRect, request: window.gadget.breakout.request, exit: window.gadget.breakout.exit }';\n const componentProps = stateEnabled\n ? `{ data: window.__GADGET_DATA__, viewport: window.__GADGET_VIEWPORT__, state: window.gadget.state ? { shared: window.gadget.state.shared, user: window.gadget.state.user } : undefined, userId: window.gadget.state ? window.gadget.state.userId : undefined${chatPropFragment}${contextPropFragment}${breakoutPropFragment}${roomPropFragment} }`\n : `{ data: window.__GADGET_DATA__, viewport: window.__GADGET_VIEWPORT__${chatPropFragment}${contextPropFragment}${breakoutPropFragment}${roomPropFragment} }`;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <script src=\"https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js\"></script>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n html, body { background: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-height: 100%; }\n #root { min-height: 100vh; }\n .gadget-error {\n padding: 16px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n color: #991b1b;\n font-family: monospace;\n font-size: 13px;\n white-space: pre-wrap;\n word-break: break-word;\n }\n .gadget-error-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n </style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script>\n window.__GADGET_DATA__ = ${serializedData};\n window.__GADGET_VIEWPORT__ = '${viewport}';\n\n window.gadget = {\n data: window.__GADGET_DATA__,\n assets: ${serializedAssets},\n\n breakout: {\n _active: false,\n _originalRect: null,\n request: function() {\n if (window.gadget.breakout._active) return;\n window.gadget.breakout._active = true;\n window.parent.postMessage({ type: 'gadget-request-breakout' }, '*');\n window.__rerenderGadget && window.__rerenderGadget();\n },\n exit: function() {\n if (!window.gadget.breakout._active) return;\n window.gadget.breakout._active = false;\n window.gadget.breakout._originalRect = null;\n window.parent.postMessage({ type: 'gadget-exit-breakout' }, '*');\n window.__rerenderGadget();\n },\n },\n\n callAction: function(input) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-action-response' && d.requestId === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() {\n window.removeEventListener('message', handler);\n reject(new Error('Action timed out'));\n }, 30000);\n window.parent.postMessage({ type: 'gadget-action-request', requestId: requestId, input: input }, '*');\n });\n },\n };\n${stateBridgeScript}\n${chatBridgeScript}\n${contextBridgeScript}\n${roomBridgeScript}\n\n window.__gadgetRoot = null;\n window.__gadgetComponent = null;\n\n window.__rerenderGadget = function() {\n if (window.__gadgetRoot && window.__gadgetComponent) {\n var C = window.__gadgetComponent;\n var EB = window.__gadgetErrorBoundary;\n window.__gadgetRoot.render(\n React.createElement(EB, null, React.createElement(C, ${componentProps}))\n );\n }\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-set-data') {\n window.__GADGET_DATA__ = d.data;\n window.gadget.data = d.data;\n window.__rerenderGadget();\n }\n if (d && d.type === 'gadget-breakout-started') {\n var wasActive = window.gadget.breakout._active;\n window.gadget.breakout._active = true;\n window.gadget.breakout._originalRect = d.originalRect || null;\n if (!wasActive) window.__rerenderGadget();\n }\n if (d && d.type === 'gadget-update-component' && d.code) {\n try {\n var compiled = Babel.transform(d.code, { presets: ['react'] }).code;\n var fn = new Function('React', 'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef',\n compiled + '\\\\nreturn typeof Gadget !== \"undefined\" ? Gadget : null;');\n var NewComponent = fn(React, React.useState, React.useEffect, React.useCallback, React.useMemo, React.useRef);\n if (NewComponent) {\n window.__gadgetComponent = NewComponent;\n if (window.__gadgetErrorBoundaryInstance) {\n window.__gadgetErrorBoundaryInstance.setState({ error: null });\n }\n window.__rerenderGadget();\n }\n } catch (err) {\n console.error('[gadget-hmr] Component update failed:', err);\n }\n }\n });\n </script>\n <script type=\"text/babel\" data-type=\"module\">\n const { useState, useEffect, useCallback, useMemo, useRef } = React;\n\n class ErrorBoundary extends React.Component {\n constructor(props) {\n super(props);\n this.state = { error: null };\n window.__gadgetErrorBoundaryInstance = this;\n }\n static getDerivedStateFromError(error) {\n return { error };\n }\n render() {\n if (this.state.error) {\n return React.createElement('div', { className: 'gadget-error' },\n React.createElement('div', { className: 'gadget-error-title' }, 'Runtime Error'),\n this.state.error.message\n );\n }\n return this.props.children;\n }\n }\n window.__gadgetErrorBoundary = ErrorBoundary;\n\n try {\n ${escapedCode}\n\n const ComponentToRender = typeof Gadget !== 'undefined' ? Gadget : null;\n\n if (ComponentToRender) {\n window.__gadgetComponent = ComponentToRender;\n const root = ReactDOM.createRoot(document.getElementById('root'));\n window.__gadgetRoot = root;\n root.render(\n <ErrorBoundary>\n <ComponentToRender data={window.__GADGET_DATA__} viewport={window.__GADGET_VIEWPORT__}${stateEnabled ? ' state={window.gadget.state ? { shared: window.gadget.state.shared, user: window.gadget.state.user } : undefined} userId={window.gadget.state ? window.gadget.state.userId : undefined}' : ''}${chatEnabled ? ' chat={window.gadget.chat}' : ''}${contextEnabled ? ' context={window.gadget.context}' : ''}${roomEnabled ? ' room={window.gadget.room} roomChat={window.gadget.roomChat} gestures={window.gadget.gestures}' : ''} breakout={{ active: window.gadget.breakout._active, originalRect: window.gadget.breakout._originalRect, request: window.gadget.breakout.request, exit: window.gadget.breakout.exit }} />\n </ErrorBoundary>\n );\n } else {\n document.getElementById('root').innerHTML =\n '<div class=\"gadget-error\"><div class=\"gadget-error-title\">No component found</div>Define a function called Gadget.</div>';\n }\n } catch (err) {\n document.getElementById('root').innerHTML =\n '<div class=\"gadget-error\"><div class=\"gadget-error-title\">Error</div>' +\n err.message.replace(/</g, '<').replace(/>/g, '>') +\n '</div>';\n }\n\n const rootEl = document.getElementById('root');\n const observer = new ResizeObserver(() => {\n window.parent.postMessage({\n type: 'gadget-resize',\n height: rootEl.scrollHeight,\n }, '*');\n });\n observer.observe(rootEl);\n </script>\n</body>\n</html>`;\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,eAAe,YAAY,eAA+B;AA2C1D;AAtBT,IAAM,YAAY,cAAyC,IAAI;AAExD,SAAS,eAAe,EAAE,QAAQ,SAAS,GAAkD;AAClG,QAAM,QAAQ,QAA4B,MAAM;AAC9C,UAAM,OAAO,GAAG,OAAO,MAAM,IAAI,OAAO,KAAK,IAAI,OAAO,SAAS;AAEjE,WAAO;AAAA,MACL;AAAA,MACA,gBAAgB,OAAO,WAAmB;AACxC,cAAM,QAAQ,MAAM,OAAO,SAAS;AACpC,cAAM,MAAM,MAAM,MAAM,GAAG,IAAI,yBAAyB;AAAA,UACtD,QAAQ;AAAA,UACR,SAAS,EAAE,eAAe,UAAU,KAAK,IAAI,gBAAgB,mBAAmB;AAAA,UAChF,MAAM,KAAK,UAAU,EAAE,OAAO,CAAC;AAAA,QACjC,CAAC;AACD,YAAI,CAAC,IAAI;AAAI,gBAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,EAAE;AACjD,cAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU,OAAO,aAAa,CAAC;AAEzF,SAAO,oBAAC,UAAU,UAAV,EAAmB,OAAe,UAAS;AACrD;AAEO,SAAS,kBAAsC;AACpD,QAAM,MAAM,WAAW,SAAS;AAChC,MAAI,CAAC;AAAK,UAAM,IAAI,MAAM,mDAAmD;AAC7E,SAAO;AACT;;;AClDA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;;;AC0BlD,SAAS,qBACd,eACA,OAAgB,CAAC,GACjB,SAQQ;AACR,QAAM,iBAAiB,KAAK,UAAU,IAAI;AAC1C,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,eAAe,SAAS,gBAAgB;AAC9C,QAAM,cAAc,SAAS,eAAe;AAC5C,QAAM,iBAAiB,SAAS,kBAAkB;AAClD,QAAM,cAAc,SAAS,eAAe;AAC5C,QAAM,cAAc,cAAc,QAAQ,eAAe,aAAa;AACtE,QAAM,mBAAmB,KAAK,UAAU,SAAS,UAAU,CAAC,CAAC;AAE7D,QAAM,oBAAoB,eACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAgDA;AAEJ,QAAM,mBAAmB,cACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WA+CA;AAEJ,QAAM,sBAAsB,iBACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAgDA;AAEJ,QAAM,mBAAmB,cACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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",
|
|
4
|
+
"sourcesContent": ["import { createContext, useContext, useMemo, type ReactNode } from 'react';\n\nexport interface GadgetConfig {\n /** Base API URL (e.g. \"https://api.gigabuddy.com\") */\n apiUrl: string;\n /** Organization ID */\n orgId: string;\n /** Project ID */\n projectId: string;\n /** Returns a fresh auth token */\n getToken: () => Promise<string>;\n /** Optional SSE stream base URL (falls back to apiUrl) */\n streamBaseUrl?: string;\n}\n\ninterface GadgetContextValue {\n config: GadgetConfig;\n /** Resolves a file ID to a fresh signed URL */\n resolveFileUrl: (fileId: string) => Promise<string>;\n}\n\nconst GadgetCtx = createContext<GadgetContextValue | null>(null);\n\nexport function GadgetProvider({ config, children }: { config: GadgetConfig; children: ReactNode }) {\n const value = useMemo<GadgetContextValue>(() => {\n const base = `${config.apiUrl}/${config.orgId}/${config.projectId}`;\n\n return {\n config,\n resolveFileUrl: async (fileId: string) => {\n const token = await config.getToken();\n const res = await fetch(`${base}/context/get-file-url`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ fileId }),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as { url: string };\n return data.url;\n },\n };\n }, [config.apiUrl, config.orgId, config.projectId, config.getToken, config.streamBaseUrl]);\n\n return <GadgetCtx.Provider value={value}>{children}</GadgetCtx.Provider>;\n}\n\nexport function useGadgetConfig(): GadgetContextValue {\n const ctx = useContext(GadgetCtx);\n if (!ctx) throw new Error('GadgetFrame must be wrapped in a <GadgetProvider>');\n return ctx;\n}\n", "import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { createGadgetRenderer } from '../createGadgetRenderer.js';\nimport { useGadgetConfig } from './GadgetContext.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface GadgetFrameProps {\n /** The gadget entity ID to load */\n gadgetId: string;\n /** Data props to pass to the gadget */\n props?: Record<string, unknown>;\n /** Display mode \u2014 compact (inline, resizable) or full (fills container) */\n mode?: 'compact' | 'full';\n /** Whether breakout is enabled (default: true in compact mode) */\n breakout?: boolean;\n /** Max height in compact mode before scrolling (default: 300) */\n maxHeight?: number;\n /** Deployment ID for mission-backed gadgets (auto-generated if not provided) */\n deploymentId?: string;\n /** Conversation ID \u2014 used when updating message content after action */\n conversationId?: string;\n /** Message ID \u2014 used when updating message content after action */\n messageId?: string;\n /** Additional className for the wrapper div */\n className?: string;\n /** Called when breakout state changes */\n onBreakoutChange?: (active: boolean) => void;\n /** Called on load error */\n onError?: (error: string) => void;\n /** Whether to lazy-load (only render when near viewport). Default: true in compact mode. */\n lazy?: boolean;\n}\n\ninterface GadgetEntity {\n data: {\n type: string;\n componentCode: string;\n actionRef?: { type: string; id: string };\n missionRef?: { type: string; id: string };\n stateBindings?: {\n shared?: { contextFields: string[] };\n user?: { contextFields: string[] };\n };\n assets?: Record<string, { fileId: string; url: string; name: string; contentType: string }>;\n };\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function GadgetFrame({\n gadgetId,\n props,\n mode = 'compact',\n breakout: breakoutEnabled = true,\n maxHeight = 300,\n deploymentId: externalDeploymentId,\n conversationId,\n messageId,\n className,\n onBreakoutChange,\n onError,\n lazy,\n}: GadgetFrameProps) {\n const { config, resolveFileUrl } = useGadgetConfig();\n const base = `${config.apiUrl}/${config.orgId}/${config.projectId}`;\n\n // \u2500\u2500 State (all hooks must be called unconditionally \u2014 no early returns before hooks) \u2500\u2500\n const lazyEnabled = lazy ?? mode === 'compact';\n const [visible, setVisible] = useState(!lazyEnabled);\n const [html, setHtml] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [naturalHeight, setNaturalHeight] = useState(60);\n const [iframeLoaded, setIframeLoaded] = useState(false);\n const [breakoutActive, setBreakoutActive] = useState(false);\n const [breakoutRevealed, setBreakoutRevealed] = useState(false);\n\n // \u2500\u2500 Refs \u2500\u2500\n const sentinelRef = useRef<HTMLDivElement>(null);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const entityRef = useRef<GadgetEntity | null>(null);\n const initialPropsRef = useRef(props);\n const deploymentIdRef = useRef<string | null>(externalDeploymentId ?? null);\n const sseRef = useRef<EventSource | null>(null);\n const userIdRef = useRef<string | null>(null);\n const latestStateRef = useRef<{ shared: Record<string, unknown>; user: Record<string, unknown> } | null>(null);\n const placeholderRef = useRef<HTMLDivElement | null>(null);\n const ancestorOverridesRef = useRef<{ el: HTMLElement; key: string; original: string }[]>([]);\n const breakoutActiveRef = useRef(false);\n breakoutActiveRef.current = breakoutActive;\n\n // \u2500\u2500 Derived \u2500\u2500\n const isCompact = mode === 'compact';\n const isCapped = isCompact && naturalHeight > maxHeight;\n const iframeHeight = isCapped ? maxHeight : naturalHeight;\n\n // \u2500\u2500 Lazy loading: defer everything until the gadget is near the viewport \u2500\u2500\n useEffect(() => {\n if (!lazyEnabled || visible) return;\n const el = sentinelRef.current;\n if (!el) return;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setVisible(true);\n observer.disconnect();\n }\n },\n { rootMargin: '200px' }, // start loading 200px before entering viewport\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [lazyEnabled, visible]);\n\n // \u2500\u2500 Fetch entity + build HTML + deploy \u2500\u2500\n useEffect(() => {\n if (!visible) return;\n let cancelled = false;\n\n (async () => {\n try {\n const token = await config.getToken();\n const res = await fetch(`${base}/chat/get-gadget`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ gadgetId }),\n });\n if (!res.ok) throw new Error(`Failed to load gadget: ${res.status}`);\n\n const entity = (await res.json()) as GadgetEntity;\n if (cancelled) return;\n\n entityRef.current = entity;\n const hasMission = entity.data.missionRef?.id != null;\n\n const assetMap = entity.data.assets\n ? Object.fromEntries(Object.entries(entity.data.assets).map(([k, v]) => [k, { url: v.url, name: v.name }]))\n : undefined;\n\n const builtHtml = createGadgetRenderer(entity.data.componentCode, initialPropsRef.current ?? {}, {\n viewport: mode,\n stateEnabled: hasMission,\n assets: assetMap,\n });\n setHtml(builtHtml);\n\n // Deploy if mission-backed\n if (hasMission) {\n const depId = externalDeploymentId ?? messageId ?? `gadget_${gadgetId}_${Date.now()}`;\n deploymentIdRef.current = depId;\n\n const deployRes = await fetch(`${base}/gadget-deploy`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n gadgetId,\n deploymentId: depId,\n initialData: initialPropsRef.current,\n conversationId,\n }),\n });\n if (!deployRes.ok) throw new Error(`Failed to deploy gadget: ${deployRes.status}`);\n\n const deployData = (await deployRes.json()) as {\n deploymentId: string;\n missionInstanceId: string;\n initialState: { shared: Record<string, unknown>; user: Record<string, unknown> };\n userId?: string;\n };\n if (cancelled) return;\n\n if (deployData.userId) userIdRef.current = deployData.userId;\n latestStateRef.current = deployData.initialState;\n\n // Connect SSE\n const streamBase = config.streamBaseUrl ?? config.apiUrl;\n const sseUrl = `${streamBase}/${config.orgId}/${config.projectId}/gadget-state/${depId}/stream?token=${encodeURIComponent(token)}`;\n const sse = new EventSource(sseUrl);\n sseRef.current = sse;\n\n sse.addEventListener('state-update', (event) => {\n if (cancelled) return;\n try {\n const state = JSON.parse(event.data) as {\n shared: Record<string, unknown>;\n user: Record<string, unknown>;\n userId?: string;\n };\n if (state.userId && !userIdRef.current) userIdRef.current = state.userId;\n latestStateRef.current = state;\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-state-update', shared: state.shared, user: state.user, userId: userIdRef.current },\n '*',\n );\n } catch {\n // Ignore malformed SSE\n }\n });\n }\n } catch (err) {\n if (!cancelled) {\n const msg = err instanceof Error ? err.message : 'Failed to load gadget';\n setError(msg);\n onError?.(msg);\n }\n } finally {\n if (!cancelled) setLoading(false);\n }\n })();\n\n return () => {\n cancelled = true;\n if (sseRef.current) {\n sseRef.current.close();\n sseRef.current = null;\n }\n };\n }, [visible, gadgetId, base, config.getToken, config.streamBaseUrl, config.orgId, config.projectId, mode]);\n\n // \u2500\u2500 Push initial state on iframe load \u2500\u2500\n useEffect(() => {\n if (!iframeLoaded || !latestStateRef.current) return;\n iframeRef.current?.contentWindow?.postMessage(\n {\n type: 'gadget-state-init',\n shared: latestStateRef.current.shared,\n user: latestStateRef.current.user,\n userId: userIdRef.current,\n },\n '*',\n );\n }, [iframeLoaded]);\n\n // \u2500\u2500 Push props changes \u2500\u2500\n useEffect(() => {\n if (!iframeLoaded || !props) return;\n iframeRef.current?.contentWindow?.postMessage({ type: 'gadget-set-data', data: props }, '*');\n }, [props, iframeLoaded]);\n\n // \u2500\u2500 Breakout handler \u2500\u2500\n useEffect(() => {\n if (!visible || !iframeRef.current || !html || !breakoutEnabled) return;\n const iframe = iframeRef.current;\n\n function clearAncestorClipping() {\n let el = iframe.parentElement;\n while (el && el !== document.body && el !== document.documentElement) {\n const cs = getComputedStyle(el);\n const overflow = cs.overflow + cs.overflowX + cs.overflowY;\n const hasClip = /hidden|auto|scroll|clip/.test(overflow);\n const hasContain =\n cs.transform !== 'none' || cs.willChange === 'transform' || cs.contain !== 'none' || cs.filter !== 'none';\n\n if (hasClip || hasContain) {\n if (hasClip) {\n ancestorOverridesRef.current.push({ el, key: 'overflow', original: el.style.overflow });\n ancestorOverridesRef.current.push({ el, key: 'overflowX', original: el.style.overflowX });\n ancestorOverridesRef.current.push({ el, key: 'overflowY', original: el.style.overflowY });\n el.style.overflow = 'visible';\n el.style.overflowX = 'visible';\n el.style.overflowY = 'visible';\n }\n if (hasContain && cs.transform !== 'none') {\n ancestorOverridesRef.current.push({ el, key: 'transform', original: el.style.transform });\n el.style.transform = 'none';\n }\n if (hasContain && cs.contain !== 'none') {\n ancestorOverridesRef.current.push({ el, key: 'contain', original: el.style.contain });\n el.style.contain = 'none';\n }\n }\n el = el.parentElement;\n }\n }\n\n function restoreAncestorClipping() {\n for (const { el, key, original } of ancestorOverridesRef.current) {\n (el.style as unknown as Record<string, string>)[key] = original;\n }\n ancestorOverridesRef.current.length = 0;\n }\n\n function onMessage(event: MessageEvent) {\n if (event.source !== iframe.contentWindow) return;\n const d = event.data;\n if (!d || typeof d !== 'object') return;\n\n if (d.type === 'gadget-request-breakout') {\n const rect = iframe.getBoundingClientRect();\n\n // Placeholder to preserve layout\n const ph = document.createElement('div');\n ph.style.width = `${rect.width}px`;\n ph.style.height = `${rect.height}px`;\n ph.style.flexShrink = '0';\n iframe.parentElement?.insertBefore(ph, iframe);\n placeholderRef.current = ph;\n\n clearAncestorClipping();\n setBreakoutActive(true);\n setBreakoutRevealed(false);\n onBreakoutChange?.(true);\n\n iframe.contentWindow?.postMessage(\n {\n type: 'gadget-breakout-started',\n originalRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n },\n '*',\n );\n\n // Reveal after repositioning\n requestAnimationFrame(() => requestAnimationFrame(() => setBreakoutRevealed(true)));\n }\n\n if (d.type === 'gadget-exit-breakout') {\n if (placeholderRef.current) {\n placeholderRef.current.remove();\n placeholderRef.current = null;\n }\n restoreAncestorClipping();\n setBreakoutActive(false);\n setBreakoutRevealed(false);\n onBreakoutChange?.(false);\n }\n }\n\n window.addEventListener('message', onMessage);\n return () => {\n window.removeEventListener('message', onMessage);\n if (placeholderRef.current) {\n placeholderRef.current.remove();\n placeholderRef.current = null;\n }\n restoreAncestorClipping();\n setBreakoutActive(false);\n setBreakoutRevealed(false);\n };\n }, [visible, html, breakoutEnabled, onBreakoutChange]);\n\n // \u2500\u2500 Bridge handler (actions, interactions, file URL, resize) \u2500\u2500\n const handleMessage = useCallback(\n async (event: MessageEvent<unknown>) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const data = event.data as any;\n if (!data || typeof data !== 'object') return;\n if (event.source !== iframeRef.current?.contentWindow) return;\n\n const type = data.type as string;\n\n // Resize (compact only)\n if (type === 'gadget-resize') {\n if (breakoutActiveRef.current || !isCompact) return;\n const h = data.height as number;\n if (h > 0) setNaturalHeight(h);\n return;\n }\n\n // Interaction dispatch\n if (type === 'gadget-interaction' && deploymentIdRef.current) {\n void (async () => {\n try {\n const token = await config.getToken();\n await fetch(`${base}/gadget-interact`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n deploymentId: deploymentIdRef.current,\n action: data.action as string,\n data: (data.data as Record<string, unknown>) ?? {},\n }),\n });\n } catch {\n // Fire-and-forget\n }\n })();\n return;\n }\n\n // File URL resolution\n if (type === 'gadget-file-url-request') {\n const requestId = data.requestId as string;\n const fileId = data.fileId as string;\n if (!requestId || !fileId) return;\n void (async () => {\n try {\n const url = await resolveFileUrl(fileId);\n iframeRef.current?.contentWindow?.postMessage({ type: 'gadget-file-url-response', requestId, url }, '*');\n } catch (err) {\n iframeRef.current?.contentWindow?.postMessage(\n {\n type: 'gadget-file-url-response',\n requestId,\n error: err instanceof Error ? err.message : 'Failed to resolve file URL',\n },\n '*',\n );\n }\n })();\n return;\n }\n\n // Action execution\n if (type === 'gadget-action-request') {\n const requestId = data.requestId as string;\n const input = (data.input as Record<string, unknown>) || {};\n const entity = entityRef.current;\n\n if (!entity?.data.actionRef?.id) {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, error: 'No action configured' },\n '*',\n );\n return;\n }\n\n try {\n const token = await config.getToken();\n const res = await fetch(`${base}/chat/execute-gadget`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ gadgetId, input }),\n });\n\n const result = (await res.json()) as {\n status?: string;\n error?: string;\n errorMessage?: string;\n output?: unknown;\n };\n\n if (!res.ok) {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, error: result.error || `HTTP ${res.status}` },\n '*',\n );\n } else if (result.status === 'failure') {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, error: result.errorMessage || 'Action failed' },\n '*',\n );\n } else {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, result: result.output },\n '*',\n );\n }\n } catch (err) {\n iframeRef.current?.contentWindow?.postMessage(\n { type: 'gadget-action-response', requestId, error: err instanceof Error ? err.message : 'Unknown error' },\n '*',\n );\n }\n }\n },\n [base, config.getToken, resolveFileUrl, gadgetId, isCompact],\n );\n\n useEffect(() => {\n if (!visible) return;\n window.addEventListener('message', handleMessage);\n return () => window.removeEventListener('message', handleMessage);\n }, [visible, handleMessage]);\n\n // \u2500\u2500 Lazy loading: show placeholder until visible \u2500\u2500\n if (!visible) {\n return (\n <div\n ref={sentinelRef}\n className={className}\n style={{ width: '100%', height: maxHeight, minHeight: 60 }}\n />\n );\n }\n\n // \u2500\u2500 Render \u2500\u2500\n\n if (error) {\n return (\n <div\n className={className}\n style={{\n padding: '8px 12px',\n fontSize: 12,\n color: '#b91c1c',\n background: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: 8,\n }}\n >\n {error}\n </div>\n );\n }\n\n if (loading) {\n return (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '12px', fontSize: 12, color: '#6b7280' }}\n >\n <span\n style={{\n display: 'inline-block',\n width: 16,\n height: 16,\n border: '2px solid #d1d5db',\n borderTopColor: '#6b7280',\n borderRadius: '50%',\n animation: 'gadget-spin 0.6s linear infinite',\n }}\n />\n Loading gadget...\n <style>{`@keyframes gadget-spin { to { transform: rotate(360deg) } }`}</style>\n </div>\n );\n }\n\n if (!html) return null;\n\n const compactStyle = breakoutActive\n ? {\n position: 'fixed' as const,\n top: 0,\n left: 0,\n width: '100vw',\n height: '100vh',\n zIndex: 99999,\n background: 'transparent',\n border: 'none',\n borderRadius: 0,\n maxWidth: 'none',\n maxHeight: 'none',\n margin: 0,\n transform: 'none',\n pointerEvents: 'none' as const,\n overflow: 'visible',\n visibility: breakoutRevealed ? ('visible' as const) : ('hidden' as const),\n }\n : {\n width: '100%',\n height: iframeHeight,\n border: 'none',\n borderRadius: 8,\n overflow: isCapped ? undefined : ('hidden' as const),\n };\n\n const fullStyle = {\n width: '100%',\n height: '100%',\n border: 'none',\n };\n\n return (\n <div className={className} style={isCompact ? { position: 'relative' } : { width: '100%', height: '100%' }}>\n <iframe\n ref={iframeRef}\n srcDoc={html}\n style={isCompact ? compactStyle : fullStyle}\n scrolling={isCompact && !breakoutActive && !isCapped ? 'no' : undefined}\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox\"\n title=\"Gadget\"\n onLoad={() => setIframeLoaded(true)}\n />\n </div>\n );\n}\n", "/**\n * Creates a self-contained HTML string for rendering a gadget component in an iframe.\n *\n * Loads React 18, ReactDOM, Babel (for JSX transpilation), and Tailwind CSS from CDNs.\n * Sets up the full gadget bridge: state, breakout, actions, chat, context, and hot-swap.\n *\n * Usage:\n * import { createGadgetRenderer, setupGadgetBreakout } from '@gigabuddy/chat';\n *\n * const html = createGadgetRenderer(componentCode, data, { viewport: 'compact', stateEnabled: true });\n * iframe.srcdoc = html;\n * setupGadgetBreakout(iframe);\n *\n * The gadget component receives these props:\n * function Gadget({ data, viewport, state, userId, breakout, chat, context })\n *\n * Bridge protocol (postMessage):\n * - Host \u2192 iframe: `gadget-set-data` \u2014 push new props\n * - Host \u2192 iframe: `gadget-state-init` / `gadget-state-update` \u2014 state changes\n * - Host \u2192 iframe: `gadget-update-component` \u2014 hot-swap component code\n * - Host \u2192 iframe: `gadget-breakout-started` \u2014 breakout activated with originalRect\n * - iframe \u2192 Host: `gadget-interaction` \u2014 user dispatched an action\n * - iframe \u2192 Host: `gadget-resize` \u2014 auto-height\n * - iframe \u2192 Host: `gadget-request-breakout` / `gadget-exit-breakout` \u2014 breakout lifecycle\n * - iframe \u2192 Host: `gadget-action-request` / `gadget-chat-request` / `gadget-context-request` \u2014 API calls\n */\nexport function createGadgetRenderer(\n componentCode: string,\n data: unknown = {},\n options?: {\n viewport?: 'compact' | 'full' | 'mobile';\n stateEnabled?: boolean;\n chatEnabled?: boolean;\n contextEnabled?: boolean;\n roomEnabled?: boolean;\n assets?: Record<string, { url: string; name: string }>;\n /** Additional script URLs to inject before gadget code (e.g. GadgetKit, Three.js) */\n libraries?: string[];\n },\n): string {\n const serializedData = JSON.stringify(data);\n const viewport = options?.viewport ?? 'full';\n const stateEnabled = options?.stateEnabled ?? false;\n const chatEnabled = options?.chatEnabled ?? false;\n const contextEnabled = options?.contextEnabled ?? false;\n const roomEnabled = options?.roomEnabled ?? false;\n const libraries = options?.libraries ?? [];\n const escapedCode = componentCode.replace(/<\\/script>/g, '<\\\\/script>');\n const serializedAssets = JSON.stringify(options?.assets ?? {});\n const libraryScripts = libraries.map((url) => ` <script src=\"${url}\"></script>`).join('\\n');\n\n const stateBridgeScript = stateEnabled\n ? `\n window.gadget.state = {\n shared: {},\n user: {},\n userId: null,\n _sharedCallbacks: [],\n _userCallbacks: [],\n\n dispatch: function(action, data) {\n window.parent.postMessage({ type: 'gadget-interaction', action: action, data: data || {} }, '*');\n },\n\n onSharedChange: function(callback) {\n window.gadget.state._sharedCallbacks.push(callback);\n callback(window.gadget.state.shared);\n return function() {\n var i = window.gadget.state._sharedCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.state._sharedCallbacks.splice(i, 1);\n };\n },\n\n onUserChange: function(callback) {\n window.gadget.state._userCallbacks.push(callback);\n callback(window.gadget.state.user);\n return function() {\n var i = window.gadget.state._userCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.state._userCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && (d.type === 'gadget-state-init' || d.type === 'gadget-state-update')) {\n if (d.userId !== undefined) {\n window.gadget.state.userId = d.userId;\n }\n if (d.shared !== undefined) {\n window.gadget.state.shared = d.shared;\n window.gadget.state._sharedCallbacks.forEach(function(cb) { try { cb(d.shared); } catch(e) {} });\n }\n if (d.user !== undefined) {\n window.gadget.state.user = d.user;\n window.gadget.state._userCallbacks.forEach(function(cb) { try { cb(d.user); } catch(e) {} });\n }\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const chatBridgeScript = chatEnabled\n ? `\n window.gadget.chat = {\n _request: function(method, params) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-chat-response' && d.requestId === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() { window.removeEventListener('message', handler); reject(new Error('Timed out')); }, 30000);\n window.parent.postMessage({ type: 'gadget-chat-request', requestId: requestId, method: method, params: params || {} }, '*');\n });\n },\n\n _eventCallbacks: [],\n\n createChannel: function(opts) { return window.gadget.chat._request('createChannel', opts); },\n findOrCreateChannel: function(name, opts) { return window.gadget.chat._request('findOrCreateChannel', { name: name, description: opts && opts.description }); },\n listChannels: function() { return window.gadget.chat._request('listChannels'); },\n joinChannel: function(conversationId) { return window.gadget.chat._request('joinChannel', { conversationId: conversationId }); },\n sendMessage: function(conversationId, text) { return window.gadget.chat._request('sendMessage', { conversationId: conversationId, text: text }); },\n getMessages: function(conversationId, opts) { return window.gadget.chat._request('getMessages', { conversationId: conversationId, limit: opts && opts.limit, before: opts && opts.before }); },\n updateChannel: function(conversationId, updates) { return window.gadget.chat._request('updateChannel', { conversationId: conversationId, description: updates && updates.description }); },\n createInvite: function(conversationId) { return window.gadget.chat._request('createInvite', { conversationId: conversationId }); },\n redeemInvite: function(token) { return window.gadget.chat._request('redeemInvite', { token: token }); },\n\n onEvent: function(callback) {\n window.gadget.chat._eventCallbacks.push(callback);\n return function() {\n var i = window.gadget.chat._eventCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.chat._eventCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-chat-event') {\n window.gadget.chat._eventCallbacks.forEach(function(cb) { try { cb(d.event); } catch(e) {} });\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const contextBridgeScript = contextEnabled\n ? `\n window.gadget.context = {\n _request: function(method, params) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-context-response' && d.id === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() { window.removeEventListener('message', handler); reject(new Error('Timed out')); }, 30000);\n window.parent.postMessage({ type: 'gadget-context-request', id: requestId, method: method, params: params || {} }, '*');\n });\n },\n\n _eventCallbacks: [],\n\n listTypes: function() { return window.gadget.context._request('listTypes', {}); },\n getType: function(contextType) { return window.gadget.context._request('getType', { contextType: contextType }); },\n listInstances: function(params) { return window.gadget.context._request('listInstances', params || {}); },\n getInstance: function(id) { return window.gadget.context._request('getInstance', { id: id }); },\n createInstance: function(params) { return window.gadget.context._request('createInstance', params); },\n updateInstance: function(id, data) { return window.gadget.context._request('updateInstance', { id: id, data: data }); },\n deleteInstance: function(id) { return window.gadget.context._request('deleteInstance', { id: id }); },\n searchKnowledge: function(params) { return window.gadget.context._request('searchKnowledge', params); },\n getEnriched: function(id) { return window.gadget.context._request('getEnriched', { id: id }); },\n getRelated: function(id) { return window.gadget.context._request('getRelated', { id: id }); },\n\n onEvent: function(callback) {\n window.gadget.context._eventCallbacks.push(callback);\n return function() {\n var i = window.gadget.context._eventCallbacks.indexOf(callback);\n if (i !== -1) window.gadget.context._eventCallbacks.splice(i, 1);\n };\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-context-event') {\n window.gadget.context._eventCallbacks.forEach(function(cb) { try { cb(d.event); } catch(e) {} });\n window.__rerenderGadget();\n }\n });`\n : '';\n\n const roomBridgeScript = roomEnabled\n ? `\n // \u2500\u2500 Room presence \u2500\u2500\n window.gadget.room = {\n peers: [],\n userId: null,\n displayName: null,\n _peersCallbacks: [],\n _playerJoinedCallbacks: [],\n _playerLeftCallbacks: [],\n\n setCursor: function(pos) {\n window.parent.postMessage({ type: 'gadget-presence', cursor: pos }, '*');\n },\n setSelection: function(sel) {\n window.parent.postMessage({ type: 'gadget-presence', selection: sel }, '*');\n },\n onPeersChange: function(cb) {\n window.gadget.room._peersCallbacks.push(cb);\n cb(window.gadget.room.peers);\n return function() {\n var i = window.gadget.room._peersCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._peersCallbacks.splice(i, 1);\n };\n },\n onPlayerJoined: function(cb) {\n window.gadget.room._playerJoinedCallbacks.push(cb);\n return function() {\n var i = window.gadget.room._playerJoinedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._playerJoinedCallbacks.splice(i, 1);\n };\n },\n onPlayerLeft: function(cb) {\n window.gadget.room._playerLeftCallbacks.push(cb);\n return function() {\n var i = window.gadget.room._playerLeftCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.room._playerLeftCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Room chat \u2500\u2500\n window.gadget.roomChat = {\n messages: [],\n _messageCallbacks: [],\n\n send: function(text) {\n window.parent.postMessage({ type: 'gadget-chat-send', text: text }, '*');\n },\n onMessage: function(cb) {\n window.gadget.roomChat._messageCallbacks.push(cb);\n return function() {\n var i = window.gadget.roomChat._messageCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.roomChat._messageCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Gestures \u2500\u2500\n window.gadget.gestures = {\n active: [],\n _spawnedCallbacks: [],\n _dismissedCallbacks: [],\n\n spawn: function(gadgetId, anchor, opts) {\n opts = opts || {};\n window.parent.postMessage({\n type: 'gadget-gesture-send',\n gadgetId: gadgetId,\n anchor: anchor,\n ttl: opts.ttl,\n size: opts.size,\n rotation: opts.rotation,\n }, '*');\n },\n dismiss: function(gestureId) {\n window.parent.postMessage({ type: 'gadget-gesture-dismiss', gestureId: gestureId }, '*');\n },\n reportAnchorRect: function(selector, rect) {\n window.parent.postMessage({ type: 'gadget-anchor-rect', selector: selector, rect: rect }, '*');\n },\n onSpawned: function(cb) {\n window.gadget.gestures._spawnedCallbacks.push(cb);\n return function() {\n var i = window.gadget.gestures._spawnedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.gestures._spawnedCallbacks.splice(i, 1);\n };\n },\n onDismissed: function(cb) {\n window.gadget.gestures._dismissedCallbacks.push(cb);\n return function() {\n var i = window.gadget.gestures._dismissedCallbacks.indexOf(cb);\n if (i !== -1) window.gadget.gestures._dismissedCallbacks.splice(i, 1);\n };\n },\n };\n\n // \u2500\u2500 Buddy attraction \u2500\u2500\n window.gadget.buddy = {\n attract: function(anchor, interest) {\n window.parent.postMessage({\n type: 'gadget-buddy-attract',\n anchor: anchor,\n interest: interest || 'medium',\n }, '*');\n },\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (!d) return;\n\n // Presence updates\n if (d.type === 'gadget-presence-update') {\n window.gadget.room.peers = d.peers || [];\n window.gadget.room._peersCallbacks.forEach(function(cb) { try { cb(d.peers || []); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Player join/leave\n if (d.type === 'gadget-player-joined') {\n window.gadget.room._playerJoinedCallbacks.forEach(function(cb) { try { cb(d); } catch(e) {} });\n }\n if (d.type === 'gadget-player-left') {\n window.gadget.room.peers = window.gadget.room.peers.filter(function(p) { return p.userId !== d.userId; });\n window.gadget.room._playerLeftCallbacks.forEach(function(cb) { try { cb(d.userId); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Chat\n if (d.type === 'gadget-chat-message') {\n var msg = d.message || d;\n window.gadget.roomChat.messages.push(msg);\n if (window.gadget.roomChat.messages.length > 100) window.gadget.roomChat.messages.shift();\n window.gadget.roomChat._messageCallbacks.forEach(function(cb) { try { cb(msg); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-chat-history') {\n window.gadget.roomChat.messages = d.messages || [];\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Gestures\n if (d.type === 'gadget-gesture-spawned') {\n window.gadget.gestures.active.push(d.gesture);\n window.gadget.gestures._spawnedCallbacks.forEach(function(cb) { try { cb(d.gesture); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-gesture-dismissed') {\n window.gadget.gestures.active = window.gadget.gestures.active.filter(function(g) { return g.id !== d.gestureId; });\n window.gadget.gestures._dismissedCallbacks.forEach(function(cb) { try { cb(d.gestureId); } catch(e) {} });\n window.__rerenderGadget && window.__rerenderGadget();\n }\n if (d.type === 'gadget-gesture-sync') {\n window.gadget.gestures.active = d.gestures || [];\n window.__rerenderGadget && window.__rerenderGadget();\n }\n\n // Anchor rect request from host\n if (d.type === 'gadget-request-anchor-rect' && d.selector) {\n try {\n var el = document.querySelector(d.selector);\n if (el) {\n var rect = el.getBoundingClientRect();\n window.parent.postMessage({\n type: 'gadget-anchor-rect',\n selector: d.selector,\n rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n }, '*');\n }\n } catch(e) {}\n }\n });`\n : '';\n\n // When state is enabled, pass state prop alongside data and viewport\n const chatPropFragment = chatEnabled ? ', chat: window.gadget.chat' : '';\n const contextPropFragment = contextEnabled ? ', context: window.gadget.context' : '';\n const roomPropFragment = roomEnabled\n ? ', room: window.gadget.room, roomChat: window.gadget.roomChat, gestures: window.gadget.gestures'\n : '';\n const breakoutPropFragment =\n ', breakout: { active: window.gadget.breakout._active, originalRect: window.gadget.breakout._originalRect, request: window.gadget.breakout.request, exit: window.gadget.breakout.exit }';\n const componentProps = stateEnabled\n ? `{ data: window.__GADGET_DATA__, viewport: window.__GADGET_VIEWPORT__, state: window.gadget.state ? { shared: window.gadget.state.shared, user: window.gadget.state.user } : undefined, userId: window.gadget.state ? window.gadget.state.userId : undefined${chatPropFragment}${contextPropFragment}${breakoutPropFragment}${roomPropFragment} }`\n : `{ data: window.__GADGET_DATA__, viewport: window.__GADGET_VIEWPORT__${chatPropFragment}${contextPropFragment}${breakoutPropFragment}${roomPropFragment} }`;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <script src=\"https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js\"></script>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n${libraryScripts}\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n html, body { background: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-height: 100%; }\n #root { min-height: 100vh; }\n .gadget-error {\n padding: 16px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n color: #991b1b;\n font-family: monospace;\n font-size: 13px;\n white-space: pre-wrap;\n word-break: break-word;\n }\n .gadget-error-title {\n font-weight: 600;\n margin-bottom: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n </style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script>\n window.__GADGET_DATA__ = ${serializedData};\n window.__GADGET_VIEWPORT__ = '${viewport}';\n\n window.gadget = {\n data: window.__GADGET_DATA__,\n assets: ${serializedAssets},\n\n breakout: {\n _active: false,\n _originalRect: null,\n request: function() {\n if (window.gadget.breakout._active) return;\n window.gadget.breakout._active = true;\n window.parent.postMessage({ type: 'gadget-request-breakout' }, '*');\n window.__rerenderGadget && window.__rerenderGadget();\n },\n exit: function() {\n if (!window.gadget.breakout._active) return;\n window.gadget.breakout._active = false;\n window.gadget.breakout._originalRect = null;\n window.parent.postMessage({ type: 'gadget-exit-breakout' }, '*');\n window.__rerenderGadget();\n },\n },\n\n callAction: function(input) {\n var requestId = Math.random().toString(36).slice(2) + Date.now().toString(36);\n return new Promise(function(resolve, reject) {\n var handler = function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-action-response' && d.requestId === requestId) {\n window.removeEventListener('message', handler);\n if (d.error) reject(new Error(d.error));\n else resolve(d.result);\n }\n };\n window.addEventListener('message', handler);\n setTimeout(function() {\n window.removeEventListener('message', handler);\n reject(new Error('Action timed out'));\n }, 30000);\n window.parent.postMessage({ type: 'gadget-action-request', requestId: requestId, input: input }, '*');\n });\n },\n };\n${stateBridgeScript}\n${chatBridgeScript}\n${contextBridgeScript}\n${roomBridgeScript}\n\n window.__gadgetRoot = null;\n window.__gadgetComponent = null;\n\n window.__rerenderGadget = function() {\n if (window.__gadgetRoot && window.__gadgetComponent) {\n var C = window.__gadgetComponent;\n var EB = window.__gadgetErrorBoundary;\n window.__gadgetRoot.render(\n React.createElement(EB, null, React.createElement(C, ${componentProps}))\n );\n }\n };\n\n window.addEventListener('message', function(event) {\n var d = event.data;\n if (d && d.type === 'gadget-set-data') {\n window.__GADGET_DATA__ = d.data;\n window.gadget.data = d.data;\n window.__rerenderGadget();\n }\n if (d && d.type === 'gadget-breakout-started') {\n var wasActive = window.gadget.breakout._active;\n window.gadget.breakout._active = true;\n window.gadget.breakout._originalRect = d.originalRect || null;\n if (!wasActive) window.__rerenderGadget();\n }\n if (d && d.type === 'gadget-update-component' && d.code) {\n try {\n var compiled = Babel.transform(d.code, { presets: ['react'] }).code;\n var fn = new Function('React', 'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef',\n compiled + '\\\\nreturn typeof Gadget !== \"undefined\" ? Gadget : null;');\n var NewComponent = fn(React, React.useState, React.useEffect, React.useCallback, React.useMemo, React.useRef);\n if (NewComponent) {\n window.__gadgetComponent = NewComponent;\n if (window.__gadgetErrorBoundaryInstance) {\n window.__gadgetErrorBoundaryInstance.setState({ error: null });\n }\n window.__rerenderGadget();\n }\n } catch (err) {\n console.error('[gadget-hmr] Component update failed:', err);\n }\n }\n });\n </script>\n <script type=\"text/babel\" data-type=\"module\">\n const { useState, useEffect, useCallback, useMemo, useRef } = React;\n\n class ErrorBoundary extends React.Component {\n constructor(props) {\n super(props);\n this.state = { error: null };\n window.__gadgetErrorBoundaryInstance = this;\n }\n static getDerivedStateFromError(error) {\n return { error };\n }\n render() {\n if (this.state.error) {\n return React.createElement('div', { className: 'gadget-error' },\n React.createElement('div', { className: 'gadget-error-title' }, 'Runtime Error'),\n this.state.error.message\n );\n }\n return this.props.children;\n }\n }\n window.__gadgetErrorBoundary = ErrorBoundary;\n\n try {\n ${escapedCode}\n\n const ComponentToRender = typeof Gadget !== 'undefined' ? Gadget : null;\n\n if (ComponentToRender) {\n window.__gadgetComponent = ComponentToRender;\n const root = ReactDOM.createRoot(document.getElementById('root'));\n window.__gadgetRoot = root;\n root.render(\n <ErrorBoundary>\n <ComponentToRender data={window.__GADGET_DATA__} viewport={window.__GADGET_VIEWPORT__}${stateEnabled ? ' state={window.gadget.state ? { shared: window.gadget.state.shared, user: window.gadget.state.user } : undefined} userId={window.gadget.state ? window.gadget.state.userId : undefined}' : ''}${chatEnabled ? ' chat={window.gadget.chat}' : ''}${contextEnabled ? ' context={window.gadget.context}' : ''}${roomEnabled ? ' room={window.gadget.room} roomChat={window.gadget.roomChat} gestures={window.gadget.gestures}' : ''} breakout={{ active: window.gadget.breakout._active, originalRect: window.gadget.breakout._originalRect, request: window.gadget.breakout.request, exit: window.gadget.breakout.exit }} />\n </ErrorBoundary>\n );\n } else {\n document.getElementById('root').innerHTML =\n '<div class=\"gadget-error\"><div class=\"gadget-error-title\">No component found</div>Define a function called Gadget.</div>';\n }\n } catch (err) {\n document.getElementById('root').innerHTML =\n '<div class=\"gadget-error\"><div class=\"gadget-error-title\">Error</div>' +\n err.message.replace(/</g, '<').replace(/>/g, '>') +\n '</div>';\n }\n\n const rootEl = document.getElementById('root');\n const observer = new ResizeObserver(() => {\n window.parent.postMessage({\n type: 'gadget-resize',\n height: rootEl.scrollHeight,\n }, '*');\n });\n observer.observe(rootEl);\n </script>\n</body>\n</html>`;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["jsx"]
|
|
7
7
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { createGadgetRenderer } from './lib/createGadgetRenderer.js';
|
|
2
|
+
export { resolveCapabilities } from './lib/capabilities.js';
|
|
3
|
+
export type { GadgetCapability } from './lib/capabilities.js';
|
|
2
4
|
export { setupGadgetBreakout } from './lib/setupGadgetBreakout.js';
|
|
3
5
|
export type { GadgetBridgeOptions } from './lib/setupGadgetBreakout.js';
|
|
4
6
|
export { setupGadgetAwareness } from './lib/setupGadgetAwareness.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gadget capability resolution.
|
|
3
|
+
*
|
|
4
|
+
* Maps declared capabilities (e.g. '3d', 'audio') to the library URLs
|
|
5
|
+
* that must be loaded in the iframe before gadget componentCode runs.
|
|
6
|
+
*/
|
|
7
|
+
export type GadgetCapability = '3d' | 'physics' | 'audio' | 'ml';
|
|
8
|
+
/**
|
|
9
|
+
* Resolves an array of capability names to deduplicated library URLs.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* resolveCapabilities(['3d', 'physics'])
|
|
13
|
+
* // → ['https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js',
|
|
14
|
+
* // 'https://g.gigabuddy.com/gadget-3d.js']
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveCapabilities(capabilities: GadgetCapability[]): string[];
|