@seed-ship/mcp-ui-solid 1.0.10 → 1.0.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.
Files changed (55) hide show
  1. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  2. package/dist/components.cjs +3 -3
  3. package/dist/components.js +3 -3
  4. package/dist/hooks.cjs +1 -1
  5. package/dist/hooks.js +1 -1
  6. package/dist/index.cjs +6 -6
  7. package/dist/index.js +6 -6
  8. package/dist/{components → mcp-ui-solid/src/components}/GenerativeUIErrorBoundary.cjs +2 -1
  9. package/dist/mcp-ui-solid/src/components/GenerativeUIErrorBoundary.cjs.map +1 -0
  10. package/dist/{components → mcp-ui-solid/src/components}/GenerativeUIErrorBoundary.js +2 -1
  11. package/dist/mcp-ui-solid/src/components/GenerativeUIErrorBoundary.js.map +1 -0
  12. package/dist/mcp-ui-solid/src/components/StreamingUIRenderer.cjs.map +1 -0
  13. package/dist/mcp-ui-solid/src/components/StreamingUIRenderer.js.map +1 -0
  14. package/dist/{components → mcp-ui-solid/src/components}/UIResourceRenderer.cjs +15 -2
  15. package/dist/mcp-ui-solid/src/components/UIResourceRenderer.cjs.map +1 -0
  16. package/dist/{components → mcp-ui-solid/src/components}/UIResourceRenderer.js +16 -3
  17. package/dist/mcp-ui-solid/src/components/UIResourceRenderer.js.map +1 -0
  18. package/dist/mcp-ui-solid/src/hooks/useStreamingUI.cjs.map +1 -0
  19. package/dist/mcp-ui-solid/src/hooks/useStreamingUI.js.map +1 -0
  20. package/dist/mcp-ui-solid/src/services/component-registry.cjs.map +1 -0
  21. package/dist/mcp-ui-solid/src/services/component-registry.js.map +1 -0
  22. package/dist/{services → mcp-ui-solid/src/services}/validation.cjs +12 -0
  23. package/dist/mcp-ui-solid/src/services/validation.cjs.map +1 -0
  24. package/dist/{services → mcp-ui-solid/src/services}/validation.js +12 -0
  25. package/dist/mcp-ui-solid/src/services/validation.js.map +1 -0
  26. package/dist/mcp-ui-solid/src/utils/logger.cjs.map +1 -0
  27. package/dist/mcp-ui-solid/src/utils/logger.js.map +1 -0
  28. package/dist/node_modules/.pnpm/marked@16.4.2/node_modules/marked/lib/marked.esm.cjs +1118 -0
  29. package/dist/node_modules/.pnpm/marked@16.4.2/node_modules/marked/lib/marked.esm.cjs.map +1 -0
  30. package/dist/node_modules/.pnpm/marked@16.4.2/node_modules/marked/lib/marked.esm.js +1119 -0
  31. package/dist/node_modules/.pnpm/marked@16.4.2/node_modules/marked/lib/marked.esm.js.map +1 -0
  32. package/dist/services/validation.d.ts.map +1 -1
  33. package/package.json +2 -1
  34. package/dist/components/GenerativeUIErrorBoundary.cjs.map +0 -1
  35. package/dist/components/GenerativeUIErrorBoundary.js.map +0 -1
  36. package/dist/components/StreamingUIRenderer.cjs.map +0 -1
  37. package/dist/components/StreamingUIRenderer.js.map +0 -1
  38. package/dist/components/UIResourceRenderer.cjs.map +0 -1
  39. package/dist/components/UIResourceRenderer.js.map +0 -1
  40. package/dist/hooks/useStreamingUI.cjs.map +0 -1
  41. package/dist/hooks/useStreamingUI.js.map +0 -1
  42. package/dist/services/component-registry.cjs.map +0 -1
  43. package/dist/services/component-registry.js.map +0 -1
  44. package/dist/services/validation.cjs.map +0 -1
  45. package/dist/services/validation.js.map +0 -1
  46. package/dist/utils/logger.cjs.map +0 -1
  47. package/dist/utils/logger.js.map +0 -1
  48. /package/dist/{components → mcp-ui-solid/src/components}/StreamingUIRenderer.cjs +0 -0
  49. /package/dist/{components → mcp-ui-solid/src/components}/StreamingUIRenderer.js +0 -0
  50. /package/dist/{hooks → mcp-ui-solid/src/hooks}/useStreamingUI.cjs +0 -0
  51. /package/dist/{hooks → mcp-ui-solid/src/hooks}/useStreamingUI.js +0 -0
  52. /package/dist/{services → mcp-ui-solid/src/services}/component-registry.cjs +0 -0
  53. /package/dist/{services → mcp-ui-solid/src/services}/component-registry.js +0 -0
  54. /package/dist/{utils → mcp-ui-solid/src/utils}/logger.cjs +0 -0
  55. /package/dist/{utils → mcp-ui-solid/src/utils}/logger.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"UIResourceRenderer.d.ts","sourceRoot":"","sources":["../../src/components/UIResourceRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,SAAS,EAAoC,MAAM,UAAU,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAA;AAInF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,EAAE,WAAW,GAAG,QAAQ,CAAA;IAE/B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAExC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AA8QD;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,CAAC,uBAAuB,CAiDjE,CAAA"}
1
+ {"version":3,"file":"UIResourceRenderer.d.ts","sourceRoot":"","sources":["../../src/components/UIResourceRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,SAAS,EAAgD,MAAM,UAAU,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAA;AAKnF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,EAAE,WAAW,GAAG,QAAQ,CAAA;IAE/B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAExC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAsRD;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,CAAC,uBAAuB,CAiEjE,CAAA"}
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const UIResourceRenderer = require("./components/UIResourceRenderer.cjs");
4
- const StreamingUIRenderer = require("./components/StreamingUIRenderer.cjs");
5
- const GenerativeUIErrorBoundary = require("./components/GenerativeUIErrorBoundary.cjs");
3
+ const UIResourceRenderer = require("./mcp-ui-solid/src/components/UIResourceRenderer.cjs");
4
+ const StreamingUIRenderer = require("./mcp-ui-solid/src/components/StreamingUIRenderer.cjs");
5
+ const GenerativeUIErrorBoundary = require("./mcp-ui-solid/src/components/GenerativeUIErrorBoundary.cjs");
6
6
  exports.UIResourceRenderer = UIResourceRenderer.UIResourceRenderer;
7
7
  exports.StreamingUIRenderer = StreamingUIRenderer.StreamingUIRenderer;
8
8
  exports.GenerativeUIErrorBoundary = GenerativeUIErrorBoundary.GenerativeUIErrorBoundary;
@@ -1,6 +1,6 @@
1
- import { UIResourceRenderer } from "./components/UIResourceRenderer.js";
2
- import { StreamingUIRenderer } from "./components/StreamingUIRenderer.js";
3
- import { GenerativeUIErrorBoundary } from "./components/GenerativeUIErrorBoundary.js";
1
+ import { UIResourceRenderer } from "./mcp-ui-solid/src/components/UIResourceRenderer.js";
2
+ import { StreamingUIRenderer } from "./mcp-ui-solid/src/components/StreamingUIRenderer.js";
3
+ import { GenerativeUIErrorBoundary } from "./mcp-ui-solid/src/components/GenerativeUIErrorBoundary.js";
4
4
  export {
5
5
  GenerativeUIErrorBoundary,
6
6
  StreamingUIRenderer,
package/dist/hooks.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const useStreamingUI = require("./hooks/useStreamingUI.cjs");
3
+ const useStreamingUI = require("./mcp-ui-solid/src/hooks/useStreamingUI.cjs");
4
4
  exports.useStreamingUI = useStreamingUI.useStreamingUI;
5
5
  //# sourceMappingURL=hooks.cjs.map
package/dist/hooks.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useStreamingUI } from "./hooks/useStreamingUI.js";
1
+ import { useStreamingUI } from "./mcp-ui-solid/src/hooks/useStreamingUI.js";
2
2
  export {
3
3
  useStreamingUI
4
4
  };
package/dist/index.cjs CHANGED
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const UIResourceRenderer = require("./components/UIResourceRenderer.cjs");
4
- const StreamingUIRenderer = require("./components/StreamingUIRenderer.cjs");
5
- const GenerativeUIErrorBoundary = require("./components/GenerativeUIErrorBoundary.cjs");
6
- const useStreamingUI = require("./hooks/useStreamingUI.cjs");
7
- const validation = require("./services/validation.cjs");
8
- const componentRegistry = require("./services/component-registry.cjs");
3
+ const UIResourceRenderer = require("./mcp-ui-solid/src/components/UIResourceRenderer.cjs");
4
+ const StreamingUIRenderer = require("./mcp-ui-solid/src/components/StreamingUIRenderer.cjs");
5
+ const GenerativeUIErrorBoundary = require("./mcp-ui-solid/src/components/GenerativeUIErrorBoundary.cjs");
6
+ const useStreamingUI = require("./mcp-ui-solid/src/hooks/useStreamingUI.cjs");
7
+ const validation = require("./mcp-ui-solid/src/services/validation.cjs");
8
+ const componentRegistry = require("./mcp-ui-solid/src/services/component-registry.cjs");
9
9
  exports.UIResourceRenderer = UIResourceRenderer.UIResourceRenderer;
10
10
  exports.StreamingUIRenderer = StreamingUIRenderer.StreamingUIRenderer;
11
11
  exports.GenerativeUIErrorBoundary = GenerativeUIErrorBoundary.GenerativeUIErrorBoundary;
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { UIResourceRenderer } from "./components/UIResourceRenderer.js";
2
- import { StreamingUIRenderer } from "./components/StreamingUIRenderer.js";
3
- import { GenerativeUIErrorBoundary } from "./components/GenerativeUIErrorBoundary.js";
4
- import { useStreamingUI } from "./hooks/useStreamingUI.js";
5
- import { DEFAULT_RESOURCE_LIMITS, validateComponent, validateLayout } from "./services/validation.js";
6
- import { ComponentRegistry } from "./services/component-registry.js";
1
+ import { UIResourceRenderer } from "./mcp-ui-solid/src/components/UIResourceRenderer.js";
2
+ import { StreamingUIRenderer } from "./mcp-ui-solid/src/components/StreamingUIRenderer.js";
3
+ import { GenerativeUIErrorBoundary } from "./mcp-ui-solid/src/components/GenerativeUIErrorBoundary.js";
4
+ import { useStreamingUI } from "./mcp-ui-solid/src/hooks/useStreamingUI.js";
5
+ import { DEFAULT_RESOURCE_LIMITS, validateComponent, validateLayout } from "./mcp-ui-solid/src/services/validation.js";
6
+ import { ComponentRegistry } from "./mcp-ui-solid/src/services/component-registry.js";
7
7
  export {
8
8
  ComponentRegistry,
9
9
  DEFAULT_RESOURCE_LIMITS,
@@ -6,7 +6,8 @@ const logger$1 = require("../utils/logger.cjs");
6
6
  var _tmpl$ = ["<p", ' class="text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono">', "</p>"], _tmpl$2 = ["<button", ' class="mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline">Retry Rendering</button>'], _tmpl$3 = ["<div", ' class="w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4"><div class="flex items-start gap-3"><div class="flex-shrink-0"><svg class="w-5 h-5 text-yellow-600 dark:text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg></div><div class="flex-1 min-w-0"><p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">Component Failed to Render</p><p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">Type: <!--$-->', "<!--/--> | ID: <!--$-->", "<!--/-->...</p><!--$-->", "<!--/--><!--$-->", "<!--/--></div></div></div>"];
7
7
  const logger = logger$1.createLogger("generative-ui");
8
8
  function DefaultErrorFallback(props) {
9
- return web.ssr(_tmpl$3, web.ssrHydrationKey(), web.escape(props.componentType), web.escape(props.componentId.slice(0, 8)), web.escape(web.createComponent(solidJs.Show, {
9
+ var _a;
10
+ return web.ssr(_tmpl$3, web.ssrHydrationKey(), web.escape(props.componentType) || "unknown", web.escape((_a = props.componentId) == null ? void 0 : _a.slice(0, 8)) || "unknown", web.escape(web.createComponent(solidJs.Show, {
10
11
  get when() {
11
12
  return false;
12
13
  },
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerativeUIErrorBoundary.cjs","sources":["../../../../src/components/GenerativeUIErrorBoundary.tsx"],"sourcesContent":["/**\n * Generative UI Error Boundary with Telemetry\n * Phase 0: Error isolation + structured logging\n *\n * Features:\n * - Component-level error isolation\n * - Structured logging with context\n * - Performance timing\n * - Retry mechanism\n * - User-friendly fallback UI\n */\n\nimport { Component, ErrorBoundary, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport { createLogger } from '../utils/logger'\nimport type { RendererError } from '../types'\n\nconst logger = createLogger('generative-ui')\n\n/**\n * Props for GenerativeUIErrorBoundary\n */\nexport interface GenerativeUIErrorBoundaryProps {\n /**\n * Component identifier for telemetry\n */\n componentId: string\n\n /**\n * Component type for context\n */\n componentType: string\n\n /**\n * Error callback\n */\n onError?: (error: RendererError) => void\n\n /**\n * Allow retry on error\n */\n allowRetry?: boolean\n\n /**\n * Child components to wrap\n */\n children: any\n\n /**\n * Custom fallback UI (optional)\n */\n fallback?: (error: Error, retry?: () => void) => any\n}\n\n/**\n * Default fallback UI for errors\n */\nfunction DefaultErrorFallback(props: {\n error: Error\n componentId: string\n componentType: string\n allowRetry?: boolean\n onRetry?: () => void\n}) {\n return (\n <div class=\"w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4\">\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-shrink-0\">\n <svg\n class=\"w-5 h-5 text-yellow-600 dark:text-yellow-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n Component Failed to Render\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Type: {props.componentType || 'unknown'} | ID: {props.componentId?.slice(0, 8) || 'unknown'}...\n </p>\n <Show when={import.meta.env.DEV}>\n <p class=\"text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono\">\n {props.error.message}\n </p>\n </Show>\n <Show when={props.allowRetry}>\n <button\n onClick={props.onRetry}\n class=\"mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline\"\n >\n Retry Rendering\n </button>\n </Show>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Generative UI Error Boundary Component\n */\nexport const GenerativeUIErrorBoundary: Component<GenerativeUIErrorBoundaryProps> = (props) => {\n const [retryKey, setRetryKey] = createSignal(0)\n const [renderStartTime] = createSignal(isServer ? 0 : performance.now())\n\n // Handle error with telemetry\n const handleError = (error: Error) => {\n const renderEndTime = isServer ? 0 : performance.now()\n const renderDuration = renderEndTime - renderStartTime()\n\n // Structure error context\n const errorContext = {\n componentId: props.componentId,\n componentType: props.componentType,\n errorMessage: error.message,\n errorStack: error.stack,\n renderDuration,\n retryCount: retryKey(),\n timestamp: new Date().toISOString(),\n userAgent: isServer ? 'server' : navigator.userAgent,\n viewport: isServer\n ? { width: 0, height: 0 }\n : { width: window.innerWidth, height: window.innerHeight },\n }\n\n // Log to structured logger\n logger.error(`Component render failed: ${props.componentType}`, errorContext)\n\n // Call error callback\n props.onError?.({\n type: 'render',\n message: error.message,\n componentId: props.componentId,\n details: errorContext,\n })\n\n // In production, send to monitoring service\n if (import.meta.env.PROD) {\n // Future: Send to Sentry or other APM\n // Sentry.captureException(error, { contexts: { component: errorContext } })\n }\n }\n\n // Retry mechanism\n const handleRetry = () => {\n const newRetryCount = retryKey() + 1\n logger.info(`Retrying component render: ${props.componentType}`, {\n componentId: props.componentId,\n retryCount: newRetryCount,\n })\n setRetryKey(newRetryCount)\n }\n\n return (\n <ErrorBoundary\n fallback={(error) => {\n handleError(error)\n\n // Use custom fallback if provided\n if (props.fallback) {\n return props.fallback(error, props.allowRetry ? handleRetry : undefined)\n }\n\n // Default fallback\n return (\n <DefaultErrorFallback\n error={error}\n componentId={props.componentId}\n componentType={props.componentType}\n allowRetry={props.allowRetry}\n onRetry={handleRetry}\n />\n )\n }}\n >\n {/* Key prop for forcing remount on retry */}\n {(() => {\n const _ = retryKey() // Access signal to track changes\n return <>{props.children}</>\n })()}\n </ErrorBoundary>\n )\n}\n\n/**\n * Performance monitoring wrapper\n * Logs render times for performance analysis\n */\nexport function withPerformanceMonitoring<P extends { componentId: string; componentType: string }>(\n WrappedComponent: Component<P>\n) {\n return (props: P) => {\n const renderStart = isServer ? 0 : performance.now()\n\n // Log render start\n logger.debug(`Component render start: ${props.componentType}`, {\n componentId: props.componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Measure on mount completion (client-side only)\n if (!isServer && typeof window !== 'undefined') {\n requestAnimationFrame(() => {\n const renderEnd = performance.now()\n const duration = renderEnd - renderStart\n\n logger.info(`Component rendered: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n timestamp: new Date().toISOString(),\n })\n\n // Warn if render is slow (>50ms target)\n if (duration > 50) {\n logger.warn(`Slow component render: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n threshold: 50,\n })\n }\n })\n }\n\n return <WrappedComponent {...props} />\n }\n}\n\n/**\n * Hook to track component lifecycle events\n */\nexport function useComponentTelemetry(componentId: string, componentType: string) {\n const mountTime = isServer ? 0 : performance.now()\n\n // Log mount\n logger.debug(`Component mounted: ${componentType}`, {\n componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Return cleanup function for unmount\n return () => {\n const lifetime = isServer ? 0 : performance.now() - mountTime\n logger.debug(`Component unmounted: ${componentType}`, {\n componentId,\n lifetime,\n timestamp: new Date().toISOString(),\n })\n }\n}\n"],"names":["logger","createLogger","DefaultErrorFallback","props","_$ssr","_tmpl$3","_$ssrHydrationKey","_$escape","componentType","componentId","slice","_$createComponent","Show","when","import","children","_tmpl$","error","message","allowRetry","_tmpl$2","GenerativeUIErrorBoundary","retryKey","setRetryKey","createSignal","renderStartTime","isServer","performance","now","handleError","renderEndTime","renderDuration","errorContext","errorMessage","errorStack","stack","retryCount","timestamp","Date","toISOString","userAgent","navigator","viewport","width","height","window","innerWidth","innerHeight","onError","type","details","handleRetry","newRetryCount","info","ErrorBoundary","fallback","undefined","onRetry"],"mappings":"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"}
@@ -4,7 +4,8 @@ import { createLogger } from "../utils/logger.js";
4
4
  var _tmpl$ = ["<p", ' class="text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono">', "</p>"], _tmpl$2 = ["<button", ' class="mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline">Retry Rendering</button>'], _tmpl$3 = ["<div", ' class="w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4"><div class="flex items-start gap-3"><div class="flex-shrink-0"><svg class="w-5 h-5 text-yellow-600 dark:text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg></div><div class="flex-1 min-w-0"><p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">Component Failed to Render</p><p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">Type: <!--$-->', "<!--/--> | ID: <!--$-->", "<!--/-->...</p><!--$-->", "<!--/--><!--$-->", "<!--/--></div></div></div>"];
5
5
  const logger = createLogger("generative-ui");
6
6
  function DefaultErrorFallback(props) {
7
- return ssr(_tmpl$3, ssrHydrationKey(), escape(props.componentType), escape(props.componentId.slice(0, 8)), escape(createComponent(Show, {
7
+ var _a;
8
+ return ssr(_tmpl$3, ssrHydrationKey(), escape(props.componentType) || "unknown", escape((_a = props.componentId) == null ? void 0 : _a.slice(0, 8)) || "unknown", escape(createComponent(Show, {
8
9
  get when() {
9
10
  return false;
10
11
  },
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerativeUIErrorBoundary.js","sources":["../../../../src/components/GenerativeUIErrorBoundary.tsx"],"sourcesContent":["/**\n * Generative UI Error Boundary with Telemetry\n * Phase 0: Error isolation + structured logging\n *\n * Features:\n * - Component-level error isolation\n * - Structured logging with context\n * - Performance timing\n * - Retry mechanism\n * - User-friendly fallback UI\n */\n\nimport { Component, ErrorBoundary, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport { createLogger } from '../utils/logger'\nimport type { RendererError } from '../types'\n\nconst logger = createLogger('generative-ui')\n\n/**\n * Props for GenerativeUIErrorBoundary\n */\nexport interface GenerativeUIErrorBoundaryProps {\n /**\n * Component identifier for telemetry\n */\n componentId: string\n\n /**\n * Component type for context\n */\n componentType: string\n\n /**\n * Error callback\n */\n onError?: (error: RendererError) => void\n\n /**\n * Allow retry on error\n */\n allowRetry?: boolean\n\n /**\n * Child components to wrap\n */\n children: any\n\n /**\n * Custom fallback UI (optional)\n */\n fallback?: (error: Error, retry?: () => void) => any\n}\n\n/**\n * Default fallback UI for errors\n */\nfunction DefaultErrorFallback(props: {\n error: Error\n componentId: string\n componentType: string\n allowRetry?: boolean\n onRetry?: () => void\n}) {\n return (\n <div class=\"w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4\">\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-shrink-0\">\n <svg\n class=\"w-5 h-5 text-yellow-600 dark:text-yellow-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n Component Failed to Render\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Type: {props.componentType || 'unknown'} | ID: {props.componentId?.slice(0, 8) || 'unknown'}...\n </p>\n <Show when={import.meta.env.DEV}>\n <p class=\"text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono\">\n {props.error.message}\n </p>\n </Show>\n <Show when={props.allowRetry}>\n <button\n onClick={props.onRetry}\n class=\"mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline\"\n >\n Retry Rendering\n </button>\n </Show>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Generative UI Error Boundary Component\n */\nexport const GenerativeUIErrorBoundary: Component<GenerativeUIErrorBoundaryProps> = (props) => {\n const [retryKey, setRetryKey] = createSignal(0)\n const [renderStartTime] = createSignal(isServer ? 0 : performance.now())\n\n // Handle error with telemetry\n const handleError = (error: Error) => {\n const renderEndTime = isServer ? 0 : performance.now()\n const renderDuration = renderEndTime - renderStartTime()\n\n // Structure error context\n const errorContext = {\n componentId: props.componentId,\n componentType: props.componentType,\n errorMessage: error.message,\n errorStack: error.stack,\n renderDuration,\n retryCount: retryKey(),\n timestamp: new Date().toISOString(),\n userAgent: isServer ? 'server' : navigator.userAgent,\n viewport: isServer\n ? { width: 0, height: 0 }\n : { width: window.innerWidth, height: window.innerHeight },\n }\n\n // Log to structured logger\n logger.error(`Component render failed: ${props.componentType}`, errorContext)\n\n // Call error callback\n props.onError?.({\n type: 'render',\n message: error.message,\n componentId: props.componentId,\n details: errorContext,\n })\n\n // In production, send to monitoring service\n if (import.meta.env.PROD) {\n // Future: Send to Sentry or other APM\n // Sentry.captureException(error, { contexts: { component: errorContext } })\n }\n }\n\n // Retry mechanism\n const handleRetry = () => {\n const newRetryCount = retryKey() + 1\n logger.info(`Retrying component render: ${props.componentType}`, {\n componentId: props.componentId,\n retryCount: newRetryCount,\n })\n setRetryKey(newRetryCount)\n }\n\n return (\n <ErrorBoundary\n fallback={(error) => {\n handleError(error)\n\n // Use custom fallback if provided\n if (props.fallback) {\n return props.fallback(error, props.allowRetry ? handleRetry : undefined)\n }\n\n // Default fallback\n return (\n <DefaultErrorFallback\n error={error}\n componentId={props.componentId}\n componentType={props.componentType}\n allowRetry={props.allowRetry}\n onRetry={handleRetry}\n />\n )\n }}\n >\n {/* Key prop for forcing remount on retry */}\n {(() => {\n const _ = retryKey() // Access signal to track changes\n return <>{props.children}</>\n })()}\n </ErrorBoundary>\n )\n}\n\n/**\n * Performance monitoring wrapper\n * Logs render times for performance analysis\n */\nexport function withPerformanceMonitoring<P extends { componentId: string; componentType: string }>(\n WrappedComponent: Component<P>\n) {\n return (props: P) => {\n const renderStart = isServer ? 0 : performance.now()\n\n // Log render start\n logger.debug(`Component render start: ${props.componentType}`, {\n componentId: props.componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Measure on mount completion (client-side only)\n if (!isServer && typeof window !== 'undefined') {\n requestAnimationFrame(() => {\n const renderEnd = performance.now()\n const duration = renderEnd - renderStart\n\n logger.info(`Component rendered: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n timestamp: new Date().toISOString(),\n })\n\n // Warn if render is slow (>50ms target)\n if (duration > 50) {\n logger.warn(`Slow component render: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n threshold: 50,\n })\n }\n })\n }\n\n return <WrappedComponent {...props} />\n }\n}\n\n/**\n * Hook to track component lifecycle events\n */\nexport function useComponentTelemetry(componentId: string, componentType: string) {\n const mountTime = isServer ? 0 : performance.now()\n\n // Log mount\n logger.debug(`Component mounted: ${componentType}`, {\n componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Return cleanup function for unmount\n return () => {\n const lifetime = isServer ? 0 : performance.now() - mountTime\n logger.debug(`Component unmounted: ${componentType}`, {\n componentId,\n lifetime,\n timestamp: new Date().toISOString(),\n })\n }\n}\n"],"names":["logger","createLogger","DefaultErrorFallback","props","_$ssr","_tmpl$3","_$ssrHydrationKey","_$escape","componentType","componentId","slice","_$createComponent","Show","when","import","children","_tmpl$","error","message","allowRetry","_tmpl$2","GenerativeUIErrorBoundary","retryKey","setRetryKey","createSignal","renderStartTime","isServer","performance","now","handleError","renderEndTime","renderDuration","errorContext","errorMessage","errorStack","stack","retryCount","timestamp","Date","toISOString","userAgent","navigator","viewport","width","height","window","innerWidth","innerHeight","onError","type","details","handleRetry","newRetryCount","info","ErrorBoundary","fallback","undefined","onRetry"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamingUIRenderer.cjs","sources":["../../../../src/components/StreamingUIRenderer.tsx"],"sourcesContent":["/**\n * StreamingUIRenderer Component - Phase 2\n *\n * Renders streaming dashboard components with skeleton states and progress indicators.\n * Uses the useStreamingUI hook for SSE connection and state management.\n *\n * Features:\n * - Skeleton loading states while components stream\n * - Progress bar and status messages\n * - Smooth component animations on arrival\n * - Error handling with retry capability\n * - Responsive 12-column grid layout\n *\n * Usage:\n * ```tsx\n * <StreamingUIRenderer\n * query=\"Show me revenue trends\"\n * spaceIds={['uuid1', 'uuid2']}\n * onComplete={(metadata) => console.log('Done!', metadata)}\n * />\n * ```\n */\n\nimport { Show, For, createSignal, onMount } from 'solid-js'\nimport { useStreamingUI, type UseStreamingUIOptions } from '../hooks/useStreamingUI'\nimport type { UIComponent, RendererError } from '../types'\nimport { validateComponent } from '../services/validation'\nimport { GenerativeUIErrorBoundary } from './GenerativeUIErrorBoundary'\n\nexport interface StreamingUIRendererProps extends UseStreamingUIOptions {\n class?: string\n showProgress?: boolean\n showMetadata?: boolean\n onRenderError?: (error: RendererError) => void\n}\n\n/**\n * Component Renderer - Inline lightweight version\n * (Full implementation in UIResourceRenderer)\n */\nfunction StreamingComponentRenderer(props: {\n component: UIComponent\n onError?: (error: RendererError) => void\n}) {\n // Validate component before rendering\n const validation = validateComponent(props.component)\n if (!validation.valid) {\n props.onError?.({\n type: 'validation',\n message: 'Component validation failed',\n componentId: props.component.id,\n details: validation.errors,\n })\n\n return (\n <div class=\"w-full bg-error-subtle border border-border-error rounded-lg p-4\">\n <p class=\"text-sm font-medium text-error-primary\">Validation Error</p>\n <p class=\"text-xs text-text-secondary mt-1\">\n {validation.errors?.[0]?.message || 'Unknown validation error'}\n </p>\n </div>\n )\n }\n\n // Simplified renderer - just show component type and title\n // Full rendering logic in UIResourceRenderer\n const params = props.component.params as any\n\n return (\n <GenerativeUIErrorBoundary\n componentId={props.component.id}\n componentType={props.component.type}\n onError={props.onError}\n allowRetry={false}\n >\n <div class=\"w-full bg-surface-secondary border border-border-subtle rounded-lg p-4\">\n <div class=\"flex items-center gap-2 mb-2\">\n <span class=\"text-xs font-medium text-text-tertiary uppercase\">\n {props.component.type}\n </span>\n </div>\n <Show when={params?.title}>\n <h3 class=\"text-sm font-semibold text-text-primary\">{params.title}</h3>\n </Show>\n <Show when={props.component.type === 'metric' && params?.value}>\n <div class=\"mt-2\">\n <p class=\"text-2xl font-semibold text-text-primary\">{params.value}</p>\n <Show when={params.unit}>\n <span class=\"text-sm text-text-secondary\">{params.unit}</span>\n </Show>\n </div>\n </Show>\n <div class=\"mt-3 text-xs text-text-tertiary\">\n Component ID: {props.component.id.slice(0, 8)}...\n </div>\n </div>\n </GenerativeUIErrorBoundary>\n )\n}\n\nexport function StreamingUIRenderer(props: StreamingUIRendererProps) {\n const { components, isLoading, isStreaming, error, progress, metadata, startStreaming } =\n useStreamingUI({\n query: props.query,\n spaceIds: props.spaceIds,\n sessionId: props.sessionId,\n options: props.options,\n onComplete: props.onComplete,\n onError: props.onError,\n onComponentReceived: props.onComponentReceived,\n })\n\n const [animatingComponents, setAnimatingComponents] = createSignal<Set<string>>(new Set())\n\n // Track new components for animation\n const handleComponentRender = (componentId: string) => {\n setAnimatingComponents((prev) => new Set([...prev, componentId]))\n\n // Remove from animating set after animation completes\n setTimeout(() => {\n setAnimatingComponents((prev) => {\n const next = new Set(prev)\n next.delete(componentId)\n return next\n })\n }, 500)\n }\n\n return (\n <div class={`streaming-ui-renderer ${props.class || ''}`}>\n {/* Progress Bar */}\n <Show when={props.showProgress !== false && (isLoading() || isStreaming())}>\n <div class=\"mb-4 rounded-lg border border-border-subtle bg-surface-secondary p-4\">\n {/* Status Message */}\n <div class=\"mb-2 flex items-center justify-between\">\n <span class=\"text-sm font-medium text-text-primary\">{progress().message}</span>\n <Show when={progress().totalCount !== null}>\n <span class=\"text-sm text-text-secondary\">\n {progress().receivedCount} / {progress().totalCount}\n </span>\n </Show>\n </div>\n\n {/* Progress Bar */}\n <div class=\"h-2 w-full overflow-hidden rounded-full bg-surface-tertiary\">\n <div\n class=\"h-full bg-brand-primary transition-all duration-300 ease-out\"\n style={\n progress().totalCount !== null\n ? `width: ${(progress().receivedCount / progress().totalCount!) * 100}%`\n : 'width: 0%'\n }\n />\n </div>\n\n {/* Indeterminate Progress (when totalCount unknown) */}\n <Show when={progress().totalCount === null && isStreaming()}>\n <div class=\"mt-2\">\n <div class=\"h-1 w-full overflow-hidden rounded-full bg-surface-tertiary\">\n <div class=\"animate-progress-indeterminate h-full w-1/3 bg-brand-primary\" />\n </div>\n </div>\n </Show>\n </div>\n </Show>\n\n {/* Error State */}\n <Show when={error()}>\n <div class=\"mb-4 rounded-lg border border-border-error bg-error-subtle p-4\">\n <div class=\"mb-2 flex items-center gap-2\">\n <svg\n class=\"h-5 w-5 text-error-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <span class=\"font-medium text-error-primary\">{error()?.error}</span>\n </div>\n <p class=\"text-sm text-text-secondary\">{error()?.message}</p>\n\n {/* Retry Button (if recoverable) */}\n <Show when={error()?.recoverable}>\n <button\n type=\"button\"\n class=\"mt-3 rounded-md bg-error-primary px-3 py-1.5 text-sm font-medium text-white hover:bg-error-hover\"\n onClick={() => startStreaming()}\n >\n Retry\n </button>\n </Show>\n </div>\n </Show>\n\n {/* Components Grid */}\n <div class=\"grid grid-cols-12 gap-4\">\n {/* Render received components */}\n <For each={components()}>\n {(component) => {\n // Trigger animation on mount (SSR-safe, no 'use' directive needed)\n onMount(() => handleComponentRender(component.id))\n\n return (\n <div\n class={`\n col-span-${component.position.colSpan}\n ${animatingComponents().has(component.id) ? 'animate-fade-in-up' : ''}\n `}\n style={`grid-column-start: ${component.position.colStart}; grid-column-end: ${component.position.colStart + component.position.colSpan}`}\n >\n <StreamingComponentRenderer component={component} onError={props.onRenderError} />\n </div>\n )\n }}\n </For>\n\n {/* Skeleton placeholders (if streaming and expecting more) */}\n <Show when={isStreaming() && progress().totalCount !== null}>\n <For\n each={Array.from({\n length: progress().totalCount! - progress().receivedCount,\n })}\n >\n {() => <SkeletonComponent />}\n </For>\n </Show>\n </div>\n\n {/* Metadata Display */}\n <Show when={props.showMetadata !== false && metadata()}>\n <div class=\"mt-6 rounded-lg border border-border-subtle bg-surface-secondary p-4 text-sm text-text-secondary\">\n <div class=\"grid grid-cols-2 gap-4 md:grid-cols-4\">\n <div>\n <div class=\"font-medium text-text-primary\">Provider</div>\n <div>{metadata()?.provider}</div>\n </div>\n <div>\n <div class=\"font-medium text-text-primary\">Model</div>\n <div>{metadata()?.model}</div>\n </div>\n <div>\n <div class=\"font-medium text-text-primary\">Execution Time</div>\n <div>{metadata()?.executionTimeMs}ms</div>\n </div>\n <Show when={metadata()?.costUSD !== undefined}>\n <div>\n <div class=\"font-medium text-text-primary\">Cost</div>\n <div>${metadata()?.costUSD?.toFixed(4)}</div>\n </div>\n </Show>\n <div>\n <div class=\"font-medium text-text-primary\">TTFB</div>\n <div>{metadata()?.firstTokenMs}ms</div>\n </div>\n <Show when={metadata()?.cached}>\n <div>\n <div class=\"font-medium text-text-primary\">Cached</div>\n <div class=\"text-success-primary\">Yes</div>\n </div>\n </Show>\n </div>\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * Skeleton Component - Placeholder while components load\n */\nfunction SkeletonComponent() {\n return (\n <div class=\"col-span-12 md:col-span-6 lg:col-span-4\">\n <div class=\"animate-pulse rounded-lg border border-border-subtle bg-surface-secondary p-4\">\n {/* Header skeleton */}\n <div class=\"mb-4 h-6 w-1/2 rounded bg-surface-tertiary\" />\n\n {/* Content skeleton */}\n <div class=\"space-y-3\">\n <div class=\"h-4 rounded bg-surface-tertiary\" />\n <div class=\"h-4 w-5/6 rounded bg-surface-tertiary\" />\n <div class=\"h-4 w-4/6 rounded bg-surface-tertiary\" />\n </div>\n\n {/* Chart/visual skeleton */}\n <div class=\"mt-4 h-32 rounded bg-surface-tertiary\" />\n </div>\n </div>\n )\n}\n\n// CSS Animations (add to global styles or Tailwind config)\n/*\n@keyframes fade-in-up {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes progress-indeterminate {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n\n.animate-fade-in-up {\n animation: fade-in-up 0.5s ease-out;\n}\n\n.animate-progress-indeterminate {\n animation: progress-indeterminate 1.5s infinite ease-in-out;\n}\n*/\n"],"names":["StreamingComponentRenderer","props","validation","validateComponent","component","valid","onError","type","message","componentId","id","details","errors","_$ssr","_tmpl$","_$ssrHydrationKey","_$escape","params","_$createComponent","GenerativeUIErrorBoundary","componentType","allowRetry","children","_tmpl$5","Show","when","title","_tmpl$2","value","_tmpl$4","unit","_tmpl$3","slice","StreamingUIRenderer","components","isLoading","isStreaming","error","progress","metadata","startStreaming","useStreamingUI","query","spaceIds","sessionId","options","onComplete","onComponentReceived","animatingComponents","setAnimatingComponents","createSignal","Set","handleComponentRender","prev","setTimeout","next","delete","_tmpl$12","class","showProgress","_tmpl$8","totalCount","_tmpl$6","receivedCount","_$ssrStyle","_tmpl$7","_tmpl$0","recoverable","_tmpl$9","For","each","onMount","_tmpl$13","position","colSpan","has","colStart","onRenderError","Array","from","length","SkeletonComponent","showMetadata","_tmpl$11","provider","model","executionTimeMs","costUSD","undefined","_tmpl$1","toFixed","firstTokenMs","cached","_tmpl$10","_tmpl$14"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamingUIRenderer.js","sources":["../../../../src/components/StreamingUIRenderer.tsx"],"sourcesContent":["/**\n * StreamingUIRenderer Component - Phase 2\n *\n * Renders streaming dashboard components with skeleton states and progress indicators.\n * Uses the useStreamingUI hook for SSE connection and state management.\n *\n * Features:\n * - Skeleton loading states while components stream\n * - Progress bar and status messages\n * - Smooth component animations on arrival\n * - Error handling with retry capability\n * - Responsive 12-column grid layout\n *\n * Usage:\n * ```tsx\n * <StreamingUIRenderer\n * query=\"Show me revenue trends\"\n * spaceIds={['uuid1', 'uuid2']}\n * onComplete={(metadata) => console.log('Done!', metadata)}\n * />\n * ```\n */\n\nimport { Show, For, createSignal, onMount } from 'solid-js'\nimport { useStreamingUI, type UseStreamingUIOptions } from '../hooks/useStreamingUI'\nimport type { UIComponent, RendererError } from '../types'\nimport { validateComponent } from '../services/validation'\nimport { GenerativeUIErrorBoundary } from './GenerativeUIErrorBoundary'\n\nexport interface StreamingUIRendererProps extends UseStreamingUIOptions {\n class?: string\n showProgress?: boolean\n showMetadata?: boolean\n onRenderError?: (error: RendererError) => void\n}\n\n/**\n * Component Renderer - Inline lightweight version\n * (Full implementation in UIResourceRenderer)\n */\nfunction StreamingComponentRenderer(props: {\n component: UIComponent\n onError?: (error: RendererError) => void\n}) {\n // Validate component before rendering\n const validation = validateComponent(props.component)\n if (!validation.valid) {\n props.onError?.({\n type: 'validation',\n message: 'Component validation failed',\n componentId: props.component.id,\n details: validation.errors,\n })\n\n return (\n <div class=\"w-full bg-error-subtle border border-border-error rounded-lg p-4\">\n <p class=\"text-sm font-medium text-error-primary\">Validation Error</p>\n <p class=\"text-xs text-text-secondary mt-1\">\n {validation.errors?.[0]?.message || 'Unknown validation error'}\n </p>\n </div>\n )\n }\n\n // Simplified renderer - just show component type and title\n // Full rendering logic in UIResourceRenderer\n const params = props.component.params as any\n\n return (\n <GenerativeUIErrorBoundary\n componentId={props.component.id}\n componentType={props.component.type}\n onError={props.onError}\n allowRetry={false}\n >\n <div class=\"w-full bg-surface-secondary border border-border-subtle rounded-lg p-4\">\n <div class=\"flex items-center gap-2 mb-2\">\n <span class=\"text-xs font-medium text-text-tertiary uppercase\">\n {props.component.type}\n </span>\n </div>\n <Show when={params?.title}>\n <h3 class=\"text-sm font-semibold text-text-primary\">{params.title}</h3>\n </Show>\n <Show when={props.component.type === 'metric' && params?.value}>\n <div class=\"mt-2\">\n <p class=\"text-2xl font-semibold text-text-primary\">{params.value}</p>\n <Show when={params.unit}>\n <span class=\"text-sm text-text-secondary\">{params.unit}</span>\n </Show>\n </div>\n </Show>\n <div class=\"mt-3 text-xs text-text-tertiary\">\n Component ID: {props.component.id.slice(0, 8)}...\n </div>\n </div>\n </GenerativeUIErrorBoundary>\n )\n}\n\nexport function StreamingUIRenderer(props: StreamingUIRendererProps) {\n const { components, isLoading, isStreaming, error, progress, metadata, startStreaming } =\n useStreamingUI({\n query: props.query,\n spaceIds: props.spaceIds,\n sessionId: props.sessionId,\n options: props.options,\n onComplete: props.onComplete,\n onError: props.onError,\n onComponentReceived: props.onComponentReceived,\n })\n\n const [animatingComponents, setAnimatingComponents] = createSignal<Set<string>>(new Set())\n\n // Track new components for animation\n const handleComponentRender = (componentId: string) => {\n setAnimatingComponents((prev) => new Set([...prev, componentId]))\n\n // Remove from animating set after animation completes\n setTimeout(() => {\n setAnimatingComponents((prev) => {\n const next = new Set(prev)\n next.delete(componentId)\n return next\n })\n }, 500)\n }\n\n return (\n <div class={`streaming-ui-renderer ${props.class || ''}`}>\n {/* Progress Bar */}\n <Show when={props.showProgress !== false && (isLoading() || isStreaming())}>\n <div class=\"mb-4 rounded-lg border border-border-subtle bg-surface-secondary p-4\">\n {/* Status Message */}\n <div class=\"mb-2 flex items-center justify-between\">\n <span class=\"text-sm font-medium text-text-primary\">{progress().message}</span>\n <Show when={progress().totalCount !== null}>\n <span class=\"text-sm text-text-secondary\">\n {progress().receivedCount} / {progress().totalCount}\n </span>\n </Show>\n </div>\n\n {/* Progress Bar */}\n <div class=\"h-2 w-full overflow-hidden rounded-full bg-surface-tertiary\">\n <div\n class=\"h-full bg-brand-primary transition-all duration-300 ease-out\"\n style={\n progress().totalCount !== null\n ? `width: ${(progress().receivedCount / progress().totalCount!) * 100}%`\n : 'width: 0%'\n }\n />\n </div>\n\n {/* Indeterminate Progress (when totalCount unknown) */}\n <Show when={progress().totalCount === null && isStreaming()}>\n <div class=\"mt-2\">\n <div class=\"h-1 w-full overflow-hidden rounded-full bg-surface-tertiary\">\n <div class=\"animate-progress-indeterminate h-full w-1/3 bg-brand-primary\" />\n </div>\n </div>\n </Show>\n </div>\n </Show>\n\n {/* Error State */}\n <Show when={error()}>\n <div class=\"mb-4 rounded-lg border border-border-error bg-error-subtle p-4\">\n <div class=\"mb-2 flex items-center gap-2\">\n <svg\n class=\"h-5 w-5 text-error-primary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <span class=\"font-medium text-error-primary\">{error()?.error}</span>\n </div>\n <p class=\"text-sm text-text-secondary\">{error()?.message}</p>\n\n {/* Retry Button (if recoverable) */}\n <Show when={error()?.recoverable}>\n <button\n type=\"button\"\n class=\"mt-3 rounded-md bg-error-primary px-3 py-1.5 text-sm font-medium text-white hover:bg-error-hover\"\n onClick={() => startStreaming()}\n >\n Retry\n </button>\n </Show>\n </div>\n </Show>\n\n {/* Components Grid */}\n <div class=\"grid grid-cols-12 gap-4\">\n {/* Render received components */}\n <For each={components()}>\n {(component) => {\n // Trigger animation on mount (SSR-safe, no 'use' directive needed)\n onMount(() => handleComponentRender(component.id))\n\n return (\n <div\n class={`\n col-span-${component.position.colSpan}\n ${animatingComponents().has(component.id) ? 'animate-fade-in-up' : ''}\n `}\n style={`grid-column-start: ${component.position.colStart}; grid-column-end: ${component.position.colStart + component.position.colSpan}`}\n >\n <StreamingComponentRenderer component={component} onError={props.onRenderError} />\n </div>\n )\n }}\n </For>\n\n {/* Skeleton placeholders (if streaming and expecting more) */}\n <Show when={isStreaming() && progress().totalCount !== null}>\n <For\n each={Array.from({\n length: progress().totalCount! - progress().receivedCount,\n })}\n >\n {() => <SkeletonComponent />}\n </For>\n </Show>\n </div>\n\n {/* Metadata Display */}\n <Show when={props.showMetadata !== false && metadata()}>\n <div class=\"mt-6 rounded-lg border border-border-subtle bg-surface-secondary p-4 text-sm text-text-secondary\">\n <div class=\"grid grid-cols-2 gap-4 md:grid-cols-4\">\n <div>\n <div class=\"font-medium text-text-primary\">Provider</div>\n <div>{metadata()?.provider}</div>\n </div>\n <div>\n <div class=\"font-medium text-text-primary\">Model</div>\n <div>{metadata()?.model}</div>\n </div>\n <div>\n <div class=\"font-medium text-text-primary\">Execution Time</div>\n <div>{metadata()?.executionTimeMs}ms</div>\n </div>\n <Show when={metadata()?.costUSD !== undefined}>\n <div>\n <div class=\"font-medium text-text-primary\">Cost</div>\n <div>${metadata()?.costUSD?.toFixed(4)}</div>\n </div>\n </Show>\n <div>\n <div class=\"font-medium text-text-primary\">TTFB</div>\n <div>{metadata()?.firstTokenMs}ms</div>\n </div>\n <Show when={metadata()?.cached}>\n <div>\n <div class=\"font-medium text-text-primary\">Cached</div>\n <div class=\"text-success-primary\">Yes</div>\n </div>\n </Show>\n </div>\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * Skeleton Component - Placeholder while components load\n */\nfunction SkeletonComponent() {\n return (\n <div class=\"col-span-12 md:col-span-6 lg:col-span-4\">\n <div class=\"animate-pulse rounded-lg border border-border-subtle bg-surface-secondary p-4\">\n {/* Header skeleton */}\n <div class=\"mb-4 h-6 w-1/2 rounded bg-surface-tertiary\" />\n\n {/* Content skeleton */}\n <div class=\"space-y-3\">\n <div class=\"h-4 rounded bg-surface-tertiary\" />\n <div class=\"h-4 w-5/6 rounded bg-surface-tertiary\" />\n <div class=\"h-4 w-4/6 rounded bg-surface-tertiary\" />\n </div>\n\n {/* Chart/visual skeleton */}\n <div class=\"mt-4 h-32 rounded bg-surface-tertiary\" />\n </div>\n </div>\n )\n}\n\n// CSS Animations (add to global styles or Tailwind config)\n/*\n@keyframes fade-in-up {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes progress-indeterminate {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n\n.animate-fade-in-up {\n animation: fade-in-up 0.5s ease-out;\n}\n\n.animate-progress-indeterminate {\n animation: progress-indeterminate 1.5s infinite ease-in-out;\n}\n*/\n"],"names":["StreamingComponentRenderer","props","validation","validateComponent","component","valid","onError","type","message","componentId","id","details","errors","_$ssr","_tmpl$","_$ssrHydrationKey","_$escape","params","_$createComponent","GenerativeUIErrorBoundary","componentType","allowRetry","children","_tmpl$5","Show","when","title","_tmpl$2","value","_tmpl$4","unit","_tmpl$3","slice","StreamingUIRenderer","components","isLoading","isStreaming","error","progress","metadata","startStreaming","useStreamingUI","query","spaceIds","sessionId","options","onComplete","onComponentReceived","animatingComponents","setAnimatingComponents","createSignal","Set","handleComponentRender","prev","setTimeout","next","delete","_tmpl$12","class","showProgress","_tmpl$8","totalCount","_tmpl$6","receivedCount","_$ssrStyle","_tmpl$7","_tmpl$0","recoverable","_tmpl$9","For","each","onMount","_tmpl$13","position","colSpan","has","colStart","onRenderError","Array","from","length","SkeletonComponent","showMetadata","_tmpl$11","provider","model","executionTimeMs","costUSD","undefined","_tmpl$1","toFixed","firstTokenMs","cached","_tmpl$10","_tmpl$14"],"mappings":"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"}
@@ -4,6 +4,7 @@ const web = require("solid-js/web");
4
4
  const solidJs = require("solid-js");
5
5
  const validation = require("../services/validation.cjs");
6
6
  const GenerativeUIErrorBoundary = require("./GenerativeUIErrorBoundary.cjs");
7
+ const marked_esm = require("../../../node_modules/.pnpm/marked@16.4.2/node_modules/marked/lib/marked.esm.cjs");
7
8
  var _tmpl$ = ["<div", ' class="absolute inset-0 flex items-center justify-center"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div></div>'], _tmpl$2 = ["<div", ' class="absolute inset-0 flex items-center justify-center p-4"><div class="text-center"><p class="text-red-600 dark:text-red-400 text-sm font-medium">Chart Error</p><p class="text-gray-600 dark:text-gray-400 text-xs mt-1">', "</p></div></div>"], _tmpl$3 = ["<h3", ' class="text-sm font-semibold text-gray-900 dark:text-white mb-3">', "</h3>"], _tmpl$4 = ["<div", ' class="w-full h-full p-4"><!--$-->', '<!--/--><div class="w-full h-full"><img', ' alt="Chart visualization" class="w-full h-auto max-h-[300px] object-contain"></div></div>'], _tmpl$5 = ["<div", ' class="relative w-full h-full min-h-[300px] bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!--$-->', "<!--/--><!--$-->", "<!--/--><!--$-->", "<!--/--></div>"], _tmpl$6 = ["<div", ' class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400"><span>Showing <!--$-->', "<!--/--> - <!--$-->", "<!--/--> of <!--$-->", "<!--/--></span></div>"], _tmpl$7 = ["<div", ' class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><div class="p-4"><!--$-->', '<!--/--><div class="overflow-x-auto"><table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"><thead class="bg-gray-50 dark:bg-gray-900"><tr>', '</tr></thead><tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">', "</tbody></table></div><!--$-->", "<!--/--></div></div>"], _tmpl$8 = ["<th", ' scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider" style="', '">', "</th>"], _tmpl$9 = ["<tr", ' class="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">', "</tr>"], _tmpl$0 = ["<td", ' class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100 whitespace-nowrap">', "</td>"], _tmpl$1 = ["<span", ' class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400">', "</span>"], _tmpl$10 = ["<div", ' class="mt-3 flex items-center"><span class="', '"><!--$-->', "<!--/--> <!--$-->", "<!--/-->%</span></div>"], _tmpl$11 = ["<p", ' class="mt-2 text-xs text-gray-500 dark:text-gray-400">', "</p>"], _tmpl$12 = ["<div", ' class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4"><div class="flex flex-col h-full justify-between"><div><p class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">', '</p><div class="mt-2 flex items-baseline"><p class="text-2xl font-semibold text-gray-900 dark:text-white">', "</p><!--$-->", "<!--/--></div></div><!--$-->", "<!--/--><!--$-->", "<!--/--></div></div>"], _tmpl$13 = ["<div", ' class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4"><div class="', '">', "</div></div>"], _tmpl$14 = ["<div", ' class="w-full h-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4"><p class="text-sm font-medium text-red-900 dark:text-red-100">Validation Error</p><p class="text-xs text-red-700 dark:text-red-300 mt-1">', "</p></div>"], _tmpl$15 = ["<div", ' class="', '"><div class="grid gap-4" style="', '">', "</div></div>"], _tmpl$16 = ["<div", ' style="', '">', "</div>"];
8
9
  function ChartRenderer(props) {
9
10
  const [iframeUrl, setIframeUrl] = solidJs.createSignal();
@@ -117,7 +118,15 @@ function MetricRenderer(props) {
117
118
  }
118
119
  function TextRenderer(props) {
119
120
  const textParams = props.component.params;
120
- return web.ssr(_tmpl$13, web.ssrHydrationKey(), `prose prose-sm dark:prose-invert max-w-none ${web.escape(textParams.className, true) || ""}`, textParams.content);
121
+ const htmlContent = solidJs.createMemo(() => {
122
+ if (textParams.markdown) {
123
+ return marked_esm.marked.parse(textParams.content, {
124
+ async: false
125
+ });
126
+ }
127
+ return textParams.content;
128
+ });
129
+ return web.ssr(_tmpl$13, web.ssrHydrationKey(), `prose prose-sm dark:prose-invert max-w-none ${web.escape(textParams.className, true) || ""}`, htmlContent());
121
130
  }
122
131
  function ComponentRenderer(props) {
123
132
  var _a, _b, _c;
@@ -199,7 +208,7 @@ function ComponentRenderer(props) {
199
208
  }
200
209
  const UIResourceRenderer = (props) => {
201
210
  const layout = () => {
202
- if ("type" in props.content) {
211
+ if ("type" in props.content && props.content.type !== "composite") {
203
212
  return {
204
213
  id: "single-component",
205
214
  components: [props.content],
@@ -213,6 +222,10 @@ const UIResourceRenderer = (props) => {
213
222
  };
214
223
  const gridContainerStyle = () => `grid-template-columns: repeat(${layout().grid.columns}, 1fr); gap: ${layout().grid.gap}`;
215
224
  const getGridStyleString = (component) => {
225
+ if (!component.position) {
226
+ console.error("[UIResourceRenderer] Component missing position field:", component);
227
+ return "grid-column: 1 / span 12; grid-row: auto";
228
+ }
216
229
  const {
217
230
  colStart,
218
231
  colSpan,
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UIResourceRenderer.cjs","sources":["../../../../src/components/UIResourceRenderer.tsx"],"sourcesContent":["/**\n * UI Resource Renderer Component\n * Phase 0: Foundation with iframe sandbox and composite grid support\n *\n * Security features:\n * - Sandboxed iframes for untrusted content\n * - CSP enforcement via middleware\n * - XSS prevention with DOMPurify\n * - Domain whitelist validation\n *\n * Performance:\n * - Lazy loading with Intersection Observer\n * - Render timeout enforcement\n * - Error boundaries for isolation\n */\n\nimport { Component, createSignal, onMount, Show, For, createMemo } from 'solid-js'\nimport type { UIComponent, UILayout, RendererError, ComponentType } from '../types'\nimport { validateComponent, DEFAULT_RESOURCE_LIMITS } from '../services/validation'\nimport { GenerativeUIErrorBoundary } from './GenerativeUIErrorBoundary'\nimport { marked } from 'marked'\n\n/**\n * Props for UIResourceRenderer\n */\nexport interface UIResourceRendererProps {\n /**\n * Single component or full layout to render\n */\n content: UIComponent | UILayout\n\n /**\n * Lazy loading (default: true)\n */\n lazyLoad?: boolean\n\n /**\n * Error callback\n */\n onError?: (error: RendererError) => void\n\n /**\n * Custom CSS class\n */\n class?: string\n}\n\n/**\n * Render a single chart component in a sandboxed iframe\n */\nfunction ChartRenderer(props: {\n component: UIComponent\n onError?: (error: RendererError) => void\n}) {\n const [iframeUrl, setIframeUrl] = createSignal<string>()\n const [isLoading, setIsLoading] = createSignal(true)\n const [error, setError] = createSignal<string>()\n\n onMount(() => {\n const chartParams = props.component.params as any\n\n // Build Quickchart URL\n const chartConfig = {\n type: chartParams.type,\n data: chartParams.data,\n options: {\n ...chartParams.options,\n responsive: true,\n maintainAspectRatio: false,\n },\n }\n\n // Encode chart configuration for Quickchart API\n const configStr = encodeURIComponent(JSON.stringify(chartConfig))\n const url = `https://quickchart.io/chart?c=${configStr}&width=500&height=300&devicePixelRatio=2`\n\n // Validate domain (should always pass for quickchart.io)\n setIframeUrl(url)\n setIsLoading(false)\n })\n\n return (\n <div class=\"relative w-full h-full min-h-[300px] bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden\">\n <Show when={isLoading()}>\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <div class=\"animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600\" />\n </div>\n </Show>\n\n <Show when={error()}>\n <div class=\"absolute inset-0 flex items-center justify-center p-4\">\n <div class=\"text-center\">\n <p class=\"text-red-600 dark:text-red-400 text-sm font-medium\">Chart Error</p>\n <p class=\"text-gray-600 dark:text-gray-400 text-xs mt-1\">{error()}</p>\n </div>\n </div>\n </Show>\n\n <Show when={iframeUrl() && !error()}>\n <div class=\"w-full h-full p-4\">\n <Show when={(props.component.params as any).title}>\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white mb-3\">\n {(props.component.params as any).title}\n </h3>\n </Show>\n <div class=\"w-full h-full\">\n <img\n src={iframeUrl()}\n alt=\"Chart visualization\"\n class=\"w-full h-auto max-h-[300px] object-contain\"\n onError={() => {\n setError('Failed to load chart')\n props.onError?.({\n type: 'render',\n message: 'Chart rendering failed',\n componentId: props.component.id,\n })\n }}\n />\n </div>\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * Render a table component\n */\nfunction TableRenderer(props: {\n component: UIComponent\n onError?: (error: RendererError) => void\n}) {\n const tableParams = props.component.params as any\n\n return (\n <div class=\"w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden\">\n <div class=\"p-4\">\n <Show when={tableParams.title}>\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white mb-3\">\n {tableParams.title}\n </h3>\n </Show>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full divide-y divide-gray-200 dark:divide-gray-700\">\n <thead class=\"bg-gray-50 dark:bg-gray-900\">\n <tr>\n <For each={tableParams.columns}>\n {(column: any) => (\n <th\n scope=\"col\"\n class=\"px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider\"\n style={column.width ? { width: column.width } : {}}\n >\n {column.label}\n </th>\n )}\n </For>\n </tr>\n </thead>\n <tbody class=\"bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700\">\n <For each={tableParams.rows.slice(0, DEFAULT_RESOURCE_LIMITS.maxTableRows)}>\n {(row: any) => (\n <tr class=\"hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\">\n <For each={tableParams.columns}>\n {(column: any) => (\n <td class=\"px-4 py-3 text-sm text-gray-900 dark:text-gray-100 whitespace-nowrap\">\n {row[column.key] || '-'}\n </td>\n )}\n </For>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n </div>\n\n <Show when={tableParams.pagination}>\n <div class=\"mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400\">\n <span>\n Showing {tableParams.pagination.currentPage * tableParams.pagination.pageSize + 1} -{' '}\n {Math.min(\n (tableParams.pagination.currentPage + 1) * tableParams.pagination.pageSize,\n tableParams.pagination.totalRows\n )}{' '}\n of {tableParams.pagination.totalRows}\n </span>\n </div>\n </Show>\n </div>\n </div>\n )\n}\n\n/**\n * Render a metric card component\n */\nfunction MetricRenderer(props: { component: UIComponent }) {\n const metricParams = props.component.params as any\n\n return (\n <div class=\"w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4\">\n <div class=\"flex flex-col h-full justify-between\">\n <div>\n <p class=\"text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide\">\n {metricParams.title}\n </p>\n <div class=\"mt-2 flex items-baseline\">\n <p class=\"text-2xl font-semibold text-gray-900 dark:text-white\">{metricParams.value}</p>\n <Show when={metricParams.unit}>\n <span class=\"ml-2 text-sm font-medium text-gray-500 dark:text-gray-400\">\n {metricParams.unit}\n </span>\n </Show>\n </div>\n </div>\n\n <Show when={metricParams.trend}>\n <div class=\"mt-3 flex items-center\">\n <span\n class={`text-sm font-medium ${\n metricParams.trend.direction === 'up'\n ? 'text-green-600 dark:text-green-400'\n : metricParams.trend.direction === 'down'\n ? 'text-red-600 dark:text-red-400'\n : 'text-gray-600 dark:text-gray-400'\n }`}\n >\n {metricParams.trend.direction === 'up'\n ? '�'\n : metricParams.trend.direction === 'down'\n ? '�'\n : '�'}{' '}\n {Math.abs(metricParams.trend.value)}%\n </span>\n </div>\n </Show>\n\n <Show when={metricParams.subtitle}>\n <p class=\"mt-2 text-xs text-gray-500 dark:text-gray-400\">{metricParams.subtitle}</p>\n </Show>\n </div>\n </div>\n )\n}\n\n/**\n * Render a text component (with optional markdown)\n */\nfunction TextRenderer(props: { component: UIComponent }) {\n const textParams = props.component.params as any\n\n // Convert markdown to HTML if markdown flag is true\n const htmlContent = createMemo(() => {\n if (textParams.markdown) {\n return marked.parse(textParams.content, { async: false }) as string\n }\n return textParams.content\n })\n\n return (\n <div class=\"w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4\">\n <div\n class={`prose prose-sm dark:prose-invert max-w-none ${textParams.className || ''}`}\n innerHTML={htmlContent()}\n />\n </div>\n )\n}\n\n/**\n * Render a single component with error boundary\n */\nfunction ComponentRenderer(props: {\n component: UIComponent\n onError?: (error: RendererError) => void\n}) {\n // Validate component before rendering\n const validation = validateComponent(props.component)\n if (!validation.valid) {\n props.onError?.({\n type: 'validation',\n message: 'Component validation failed',\n componentId: props.component.id,\n details: validation.errors,\n })\n\n return (\n <div class=\"w-full h-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4\">\n <p class=\"text-sm font-medium text-red-900 dark:text-red-100\">Validation Error</p>\n <p class=\"text-xs text-red-700 dark:text-red-300 mt-1\">\n {validation.errors?.[0]?.message || 'Unknown validation error'}\n </p>\n </div>\n )\n }\n\n // Render based on component type with enhanced error boundary\n return (\n <GenerativeUIErrorBoundary\n componentId={props.component.id}\n componentType={props.component.type}\n onError={props.onError}\n allowRetry={true}\n >\n <Show when={props.component.type === 'chart'}>\n <ChartRenderer component={props.component} onError={props.onError} />\n </Show>\n <Show when={props.component.type === 'table'}>\n <TableRenderer component={props.component} onError={props.onError} />\n </Show>\n <Show when={props.component.type === 'metric'}>\n <MetricRenderer component={props.component} />\n </Show>\n <Show when={props.component.type === 'text'}>\n <TextRenderer component={props.component} />\n </Show>\n </GenerativeUIErrorBoundary>\n )\n}\n\n/**\n * Main UIResourceRenderer component\n */\nexport const UIResourceRenderer: Component<UIResourceRendererProps> = (props) => {\n const layout = () => {\n // ✅ PHASE 3.3 FIX: Check if content is a UIComponent (non-composite) vs UILayout (composite)\n // UILayout has type='composite', UIComponent has type='chart'|'table'|'metric'|'text'\n if ('type' in props.content && (props.content as any).type !== 'composite') {\n return {\n id: 'single-component',\n components: [props.content as UIComponent],\n grid: {\n columns: 12,\n gap: '1rem',\n },\n } as UILayout\n }\n return props.content as UILayout\n }\n\n // Grid position to CSS Grid styles\n const getGridStyles = (component: UIComponent) => {\n // ✅ PHASE 3 FIX: Defensive check for position field\n if (!component.position) {\n console.error('[UIResourceRenderer] Component missing position field:', component)\n return {\n 'grid-column': '1 / span 12',\n 'grid-row': 'auto',\n }\n }\n\n const { colStart, colSpan, rowStart, rowSpan = 1 } = component.position\n\n return {\n 'grid-column': `${colStart} / span ${colSpan}`,\n 'grid-row': rowStart ? `${rowStart} / span ${rowSpan}` : 'auto',\n }\n }\n\n // Convert grid styles to CSS string to avoid setStyleProperty\n const gridContainerStyle = () =>\n `grid-template-columns: repeat(${layout().grid.columns}, 1fr); gap: ${layout().grid.gap}`\n\n // Convert component grid styles to CSS string\n const getGridStyleString = (component: UIComponent) => {\n // ✅ PHASE 3 FIX: Defensive check for position field\n if (!component.position) {\n console.error('[UIResourceRenderer] Component missing position field:', component)\n return 'grid-column: 1 / span 12; grid-row: auto' // Default to full width\n }\n\n const { colStart, colSpan, rowStart, rowSpan = 1 } = component.position\n return `grid-column: ${colStart} / span ${colSpan}; grid-row: ${rowStart ? `${rowStart} / span ${rowSpan}` : 'auto'}`\n }\n\n return (\n <div class={`w-full ${props.class || ''}`}>\n <div class=\"grid gap-4\" style={gridContainerStyle()}>\n <For each={layout().components}>\n {(component) => (\n <div style={getGridStyleString(component)}>\n <ComponentRenderer component={component} onError={props.onError} />\n </div>\n )}\n </For>\n </div>\n </div>\n )\n}\n"],"names":["ChartRenderer","props","iframeUrl","setIframeUrl","createSignal","isLoading","setIsLoading","error","setError","onMount","chartParams","component","params","chartConfig","type","data","options","responsive","maintainAspectRatio","configStr","encodeURIComponent","JSON","stringify","url","_$ssr","_tmpl$5","_$ssrHydrationKey","_$escape","_$createComponent","Show","when","children","_tmpl$","_tmpl$2","_tmpl$4","title","_tmpl$3","_$ssrAttribute","TableRenderer","tableParams","_tmpl$7","For","each","columns","column","_tmpl$8","_$ssrStyle","width","label","rows","slice","DEFAULT_RESOURCE_LIMITS","maxTableRows","row","_tmpl$9","_tmpl$0","key","pagination","_tmpl$6","currentPage","pageSize","Math","min","totalRows","MetricRenderer","metricParams","_tmpl$12","value","unit","_tmpl$1","trend","_tmpl$10","direction","abs","subtitle","_tmpl$11","TextRenderer","textParams","htmlContent","createMemo","markdown","marked","parse","content","async","_tmpl$13","className","ComponentRenderer","validation","validateComponent","valid","onError","message","componentId","id","details","errors","_tmpl$14","GenerativeUIErrorBoundary","componentType","allowRetry","UIResourceRenderer","layout","components","grid","gap","gridContainerStyle","getGridStyleString","position","console","colStart","colSpan","rowStart","rowSpan","_tmpl$15","class","_tmpl$16"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { ssr, ssrHydrationKey, escape, ssrStyle, createComponent, ssrAttribute } from "solid-js/web";
2
- import { For, Show, createSignal, onMount } from "solid-js";
2
+ import { For, Show, createSignal, onMount, createMemo } from "solid-js";
3
3
  import { validateComponent, DEFAULT_RESOURCE_LIMITS } from "../services/validation.js";
4
4
  import { GenerativeUIErrorBoundary } from "./GenerativeUIErrorBoundary.js";
5
+ import { marked as k } from "../../../node_modules/.pnpm/marked@16.4.2/node_modules/marked/lib/marked.esm.js";
5
6
  var _tmpl$ = ["<div", ' class="absolute inset-0 flex items-center justify-center"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div></div>'], _tmpl$2 = ["<div", ' class="absolute inset-0 flex items-center justify-center p-4"><div class="text-center"><p class="text-red-600 dark:text-red-400 text-sm font-medium">Chart Error</p><p class="text-gray-600 dark:text-gray-400 text-xs mt-1">', "</p></div></div>"], _tmpl$3 = ["<h3", ' class="text-sm font-semibold text-gray-900 dark:text-white mb-3">', "</h3>"], _tmpl$4 = ["<div", ' class="w-full h-full p-4"><!--$-->', '<!--/--><div class="w-full h-full"><img', ' alt="Chart visualization" class="w-full h-auto max-h-[300px] object-contain"></div></div>'], _tmpl$5 = ["<div", ' class="relative w-full h-full min-h-[300px] bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!--$-->', "<!--/--><!--$-->", "<!--/--><!--$-->", "<!--/--></div>"], _tmpl$6 = ["<div", ' class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400"><span>Showing <!--$-->', "<!--/--> - <!--$-->", "<!--/--> of <!--$-->", "<!--/--></span></div>"], _tmpl$7 = ["<div", ' class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><div class="p-4"><!--$-->', '<!--/--><div class="overflow-x-auto"><table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"><thead class="bg-gray-50 dark:bg-gray-900"><tr>', '</tr></thead><tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">', "</tbody></table></div><!--$-->", "<!--/--></div></div>"], _tmpl$8 = ["<th", ' scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider" style="', '">', "</th>"], _tmpl$9 = ["<tr", ' class="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">', "</tr>"], _tmpl$0 = ["<td", ' class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100 whitespace-nowrap">', "</td>"], _tmpl$1 = ["<span", ' class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400">', "</span>"], _tmpl$10 = ["<div", ' class="mt-3 flex items-center"><span class="', '"><!--$-->', "<!--/--> <!--$-->", "<!--/-->%</span></div>"], _tmpl$11 = ["<p", ' class="mt-2 text-xs text-gray-500 dark:text-gray-400">', "</p>"], _tmpl$12 = ["<div", ' class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4"><div class="flex flex-col h-full justify-between"><div><p class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">', '</p><div class="mt-2 flex items-baseline"><p class="text-2xl font-semibold text-gray-900 dark:text-white">', "</p><!--$-->", "<!--/--></div></div><!--$-->", "<!--/--><!--$-->", "<!--/--></div></div>"], _tmpl$13 = ["<div", ' class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4"><div class="', '">', "</div></div>"], _tmpl$14 = ["<div", ' class="w-full h-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4"><p class="text-sm font-medium text-red-900 dark:text-red-100">Validation Error</p><p class="text-xs text-red-700 dark:text-red-300 mt-1">', "</p></div>"], _tmpl$15 = ["<div", ' class="', '"><div class="grid gap-4" style="', '">', "</div></div>"], _tmpl$16 = ["<div", ' style="', '">', "</div>"];
6
7
  function ChartRenderer(props) {
7
8
  const [iframeUrl, setIframeUrl] = createSignal();
@@ -115,7 +116,15 @@ function MetricRenderer(props) {
115
116
  }
116
117
  function TextRenderer(props) {
117
118
  const textParams = props.component.params;
118
- return ssr(_tmpl$13, ssrHydrationKey(), `prose prose-sm dark:prose-invert max-w-none ${escape(textParams.className, true) || ""}`, textParams.content);
119
+ const htmlContent = createMemo(() => {
120
+ if (textParams.markdown) {
121
+ return k.parse(textParams.content, {
122
+ async: false
123
+ });
124
+ }
125
+ return textParams.content;
126
+ });
127
+ return ssr(_tmpl$13, ssrHydrationKey(), `prose prose-sm dark:prose-invert max-w-none ${escape(textParams.className, true) || ""}`, htmlContent());
119
128
  }
120
129
  function ComponentRenderer(props) {
121
130
  var _a, _b, _c;
@@ -197,7 +206,7 @@ function ComponentRenderer(props) {
197
206
  }
198
207
  const UIResourceRenderer = (props) => {
199
208
  const layout = () => {
200
- if ("type" in props.content) {
209
+ if ("type" in props.content && props.content.type !== "composite") {
201
210
  return {
202
211
  id: "single-component",
203
212
  components: [props.content],
@@ -211,6 +220,10 @@ const UIResourceRenderer = (props) => {
211
220
  };
212
221
  const gridContainerStyle = () => `grid-template-columns: repeat(${layout().grid.columns}, 1fr); gap: ${layout().grid.gap}`;
213
222
  const getGridStyleString = (component) => {
223
+ if (!component.position) {
224
+ console.error("[UIResourceRenderer] Component missing position field:", component);
225
+ return "grid-column: 1 / span 12; grid-row: auto";
226
+ }
214
227
  const {
215
228
  colStart,
216
229
  colSpan,
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UIResourceRenderer.js","sources":["../../../../src/components/UIResourceRenderer.tsx"],"sourcesContent":["/**\n * UI Resource Renderer Component\n * Phase 0: Foundation with iframe sandbox and composite grid support\n *\n * Security features:\n * - Sandboxed iframes for untrusted content\n * - CSP enforcement via middleware\n * - XSS prevention with DOMPurify\n * - Domain whitelist validation\n *\n * Performance:\n * - Lazy loading with Intersection Observer\n * - Render timeout enforcement\n * - Error boundaries for isolation\n */\n\nimport { Component, createSignal, onMount, Show, For, createMemo } from 'solid-js'\nimport type { UIComponent, UILayout, RendererError, ComponentType } from '../types'\nimport { validateComponent, DEFAULT_RESOURCE_LIMITS } from '../services/validation'\nimport { GenerativeUIErrorBoundary } from './GenerativeUIErrorBoundary'\nimport { marked } from 'marked'\n\n/**\n * Props for UIResourceRenderer\n */\nexport interface UIResourceRendererProps {\n /**\n * Single component or full layout to render\n */\n content: UIComponent | UILayout\n\n /**\n * Lazy loading (default: true)\n */\n lazyLoad?: boolean\n\n /**\n * Error callback\n */\n onError?: (error: RendererError) => void\n\n /**\n * Custom CSS class\n */\n class?: string\n}\n\n/**\n * Render a single chart component in a sandboxed iframe\n */\nfunction ChartRenderer(props: {\n component: UIComponent\n onError?: (error: RendererError) => void\n}) {\n const [iframeUrl, setIframeUrl] = createSignal<string>()\n const [isLoading, setIsLoading] = createSignal(true)\n const [error, setError] = createSignal<string>()\n\n onMount(() => {\n const chartParams = props.component.params as any\n\n // Build Quickchart URL\n const chartConfig = {\n type: chartParams.type,\n data: chartParams.data,\n options: {\n ...chartParams.options,\n responsive: true,\n maintainAspectRatio: false,\n },\n }\n\n // Encode chart configuration for Quickchart API\n const configStr = encodeURIComponent(JSON.stringify(chartConfig))\n const url = `https://quickchart.io/chart?c=${configStr}&width=500&height=300&devicePixelRatio=2`\n\n // Validate domain (should always pass for quickchart.io)\n setIframeUrl(url)\n setIsLoading(false)\n })\n\n return (\n <div class=\"relative w-full h-full min-h-[300px] bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden\">\n <Show when={isLoading()}>\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <div class=\"animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600\" />\n </div>\n </Show>\n\n <Show when={error()}>\n <div class=\"absolute inset-0 flex items-center justify-center p-4\">\n <div class=\"text-center\">\n <p class=\"text-red-600 dark:text-red-400 text-sm font-medium\">Chart Error</p>\n <p class=\"text-gray-600 dark:text-gray-400 text-xs mt-1\">{error()}</p>\n </div>\n </div>\n </Show>\n\n <Show when={iframeUrl() && !error()}>\n <div class=\"w-full h-full p-4\">\n <Show when={(props.component.params as any).title}>\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white mb-3\">\n {(props.component.params as any).title}\n </h3>\n </Show>\n <div class=\"w-full h-full\">\n <img\n src={iframeUrl()}\n alt=\"Chart visualization\"\n class=\"w-full h-auto max-h-[300px] object-contain\"\n onError={() => {\n setError('Failed to load chart')\n props.onError?.({\n type: 'render',\n message: 'Chart rendering failed',\n componentId: props.component.id,\n })\n }}\n />\n </div>\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * Render a table component\n */\nfunction TableRenderer(props: {\n component: UIComponent\n onError?: (error: RendererError) => void\n}) {\n const tableParams = props.component.params as any\n\n return (\n <div class=\"w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden\">\n <div class=\"p-4\">\n <Show when={tableParams.title}>\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white mb-3\">\n {tableParams.title}\n </h3>\n </Show>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full divide-y divide-gray-200 dark:divide-gray-700\">\n <thead class=\"bg-gray-50 dark:bg-gray-900\">\n <tr>\n <For each={tableParams.columns}>\n {(column: any) => (\n <th\n scope=\"col\"\n class=\"px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider\"\n style={column.width ? { width: column.width } : {}}\n >\n {column.label}\n </th>\n )}\n </For>\n </tr>\n </thead>\n <tbody class=\"bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700\">\n <For each={tableParams.rows.slice(0, DEFAULT_RESOURCE_LIMITS.maxTableRows)}>\n {(row: any) => (\n <tr class=\"hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\">\n <For each={tableParams.columns}>\n {(column: any) => (\n <td class=\"px-4 py-3 text-sm text-gray-900 dark:text-gray-100 whitespace-nowrap\">\n {row[column.key] || '-'}\n </td>\n )}\n </For>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n </div>\n\n <Show when={tableParams.pagination}>\n <div class=\"mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400\">\n <span>\n Showing {tableParams.pagination.currentPage * tableParams.pagination.pageSize + 1} -{' '}\n {Math.min(\n (tableParams.pagination.currentPage + 1) * tableParams.pagination.pageSize,\n tableParams.pagination.totalRows\n )}{' '}\n of {tableParams.pagination.totalRows}\n </span>\n </div>\n </Show>\n </div>\n </div>\n )\n}\n\n/**\n * Render a metric card component\n */\nfunction MetricRenderer(props: { component: UIComponent }) {\n const metricParams = props.component.params as any\n\n return (\n <div class=\"w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4\">\n <div class=\"flex flex-col h-full justify-between\">\n <div>\n <p class=\"text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide\">\n {metricParams.title}\n </p>\n <div class=\"mt-2 flex items-baseline\">\n <p class=\"text-2xl font-semibold text-gray-900 dark:text-white\">{metricParams.value}</p>\n <Show when={metricParams.unit}>\n <span class=\"ml-2 text-sm font-medium text-gray-500 dark:text-gray-400\">\n {metricParams.unit}\n </span>\n </Show>\n </div>\n </div>\n\n <Show when={metricParams.trend}>\n <div class=\"mt-3 flex items-center\">\n <span\n class={`text-sm font-medium ${\n metricParams.trend.direction === 'up'\n ? 'text-green-600 dark:text-green-400'\n : metricParams.trend.direction === 'down'\n ? 'text-red-600 dark:text-red-400'\n : 'text-gray-600 dark:text-gray-400'\n }`}\n >\n {metricParams.trend.direction === 'up'\n ? '�'\n : metricParams.trend.direction === 'down'\n ? '�'\n : '�'}{' '}\n {Math.abs(metricParams.trend.value)}%\n </span>\n </div>\n </Show>\n\n <Show when={metricParams.subtitle}>\n <p class=\"mt-2 text-xs text-gray-500 dark:text-gray-400\">{metricParams.subtitle}</p>\n </Show>\n </div>\n </div>\n )\n}\n\n/**\n * Render a text component (with optional markdown)\n */\nfunction TextRenderer(props: { component: UIComponent }) {\n const textParams = props.component.params as any\n\n // Convert markdown to HTML if markdown flag is true\n const htmlContent = createMemo(() => {\n if (textParams.markdown) {\n return marked.parse(textParams.content, { async: false }) as string\n }\n return textParams.content\n })\n\n return (\n <div class=\"w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4\">\n <div\n class={`prose prose-sm dark:prose-invert max-w-none ${textParams.className || ''}`}\n innerHTML={htmlContent()}\n />\n </div>\n )\n}\n\n/**\n * Render a single component with error boundary\n */\nfunction ComponentRenderer(props: {\n component: UIComponent\n onError?: (error: RendererError) => void\n}) {\n // Validate component before rendering\n const validation = validateComponent(props.component)\n if (!validation.valid) {\n props.onError?.({\n type: 'validation',\n message: 'Component validation failed',\n componentId: props.component.id,\n details: validation.errors,\n })\n\n return (\n <div class=\"w-full h-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4\">\n <p class=\"text-sm font-medium text-red-900 dark:text-red-100\">Validation Error</p>\n <p class=\"text-xs text-red-700 dark:text-red-300 mt-1\">\n {validation.errors?.[0]?.message || 'Unknown validation error'}\n </p>\n </div>\n )\n }\n\n // Render based on component type with enhanced error boundary\n return (\n <GenerativeUIErrorBoundary\n componentId={props.component.id}\n componentType={props.component.type}\n onError={props.onError}\n allowRetry={true}\n >\n <Show when={props.component.type === 'chart'}>\n <ChartRenderer component={props.component} onError={props.onError} />\n </Show>\n <Show when={props.component.type === 'table'}>\n <TableRenderer component={props.component} onError={props.onError} />\n </Show>\n <Show when={props.component.type === 'metric'}>\n <MetricRenderer component={props.component} />\n </Show>\n <Show when={props.component.type === 'text'}>\n <TextRenderer component={props.component} />\n </Show>\n </GenerativeUIErrorBoundary>\n )\n}\n\n/**\n * Main UIResourceRenderer component\n */\nexport const UIResourceRenderer: Component<UIResourceRendererProps> = (props) => {\n const layout = () => {\n // ✅ PHASE 3.3 FIX: Check if content is a UIComponent (non-composite) vs UILayout (composite)\n // UILayout has type='composite', UIComponent has type='chart'|'table'|'metric'|'text'\n if ('type' in props.content && (props.content as any).type !== 'composite') {\n return {\n id: 'single-component',\n components: [props.content as UIComponent],\n grid: {\n columns: 12,\n gap: '1rem',\n },\n } as UILayout\n }\n return props.content as UILayout\n }\n\n // Grid position to CSS Grid styles\n const getGridStyles = (component: UIComponent) => {\n // ✅ PHASE 3 FIX: Defensive check for position field\n if (!component.position) {\n console.error('[UIResourceRenderer] Component missing position field:', component)\n return {\n 'grid-column': '1 / span 12',\n 'grid-row': 'auto',\n }\n }\n\n const { colStart, colSpan, rowStart, rowSpan = 1 } = component.position\n\n return {\n 'grid-column': `${colStart} / span ${colSpan}`,\n 'grid-row': rowStart ? `${rowStart} / span ${rowSpan}` : 'auto',\n }\n }\n\n // Convert grid styles to CSS string to avoid setStyleProperty\n const gridContainerStyle = () =>\n `grid-template-columns: repeat(${layout().grid.columns}, 1fr); gap: ${layout().grid.gap}`\n\n // Convert component grid styles to CSS string\n const getGridStyleString = (component: UIComponent) => {\n // ✅ PHASE 3 FIX: Defensive check for position field\n if (!component.position) {\n console.error('[UIResourceRenderer] Component missing position field:', component)\n return 'grid-column: 1 / span 12; grid-row: auto' // Default to full width\n }\n\n const { colStart, colSpan, rowStart, rowSpan = 1 } = component.position\n return `grid-column: ${colStart} / span ${colSpan}; grid-row: ${rowStart ? `${rowStart} / span ${rowSpan}` : 'auto'}`\n }\n\n return (\n <div class={`w-full ${props.class || ''}`}>\n <div class=\"grid gap-4\" style={gridContainerStyle()}>\n <For each={layout().components}>\n {(component) => (\n <div style={getGridStyleString(component)}>\n <ComponentRenderer component={component} onError={props.onError} />\n </div>\n )}\n </For>\n </div>\n </div>\n )\n}\n"],"names":["ChartRenderer","props","iframeUrl","setIframeUrl","createSignal","isLoading","setIsLoading","error","setError","onMount","chartParams","component","params","chartConfig","type","data","options","responsive","maintainAspectRatio","configStr","encodeURIComponent","JSON","stringify","url","_$ssr","_tmpl$5","_$ssrHydrationKey","_$escape","_$createComponent","Show","when","children","_tmpl$","_tmpl$2","_tmpl$4","title","_tmpl$3","_$ssrAttribute","TableRenderer","tableParams","_tmpl$7","For","each","columns","column","_tmpl$8","_$ssrStyle","width","label","rows","slice","DEFAULT_RESOURCE_LIMITS","maxTableRows","row","_tmpl$9","_tmpl$0","key","pagination","_tmpl$6","currentPage","pageSize","Math","min","totalRows","MetricRenderer","metricParams","_tmpl$12","value","unit","_tmpl$1","trend","_tmpl$10","direction","abs","subtitle","_tmpl$11","TextRenderer","textParams","htmlContent","createMemo","markdown","marked","parse","content","async","_tmpl$13","className","ComponentRenderer","validation","validateComponent","valid","onError","message","componentId","id","details","errors","_tmpl$14","GenerativeUIErrorBoundary","componentType","allowRetry","UIResourceRenderer","layout","components","grid","gap","gridContainerStyle","getGridStyleString","position","console","colStart","colSpan","rowStart","rowSpan","_tmpl$15","class","_tmpl$16"],"mappings":";;;;;;AAkDA,SAASA,cAAcC,OAGpB;AACD,QAAM,CAACC,WAAWC,YAAY,IAAIC,aAAAA;AAClC,QAAM,CAACC,WAAWC,YAAY,IAAIF,aAAa,IAAI;AACnD,QAAM,CAACG,OAAOC,QAAQ,IAAIJ,aAAAA;AAE1BK,UAAQ,MAAM;AACZ,UAAMC,cAAcT,MAAMU,UAAUC;AAGpC,UAAMC,cAAc;AAAA,MAClBC,MAAMJ,YAAYI;AAAAA,MAClBC,MAAML,YAAYK;AAAAA,MAClBC,SAAS;AAAA,QACP,GAAGN,YAAYM;AAAAA,QACfC,YAAY;AAAA,QACZC,qBAAqB;AAAA,MAAA;AAAA,IACvB;AAIF,UAAMC,YAAYC,mBAAmBC,KAAKC,UAAUT,WAAW,CAAC;AAChE,UAAMU,MAAM,iCAAiCJ,SAAS;AAGtDhB,iBAAaoB,GAAG;AAChBjB,iBAAa,KAAK;AAAA,EACpB,CAAC;AAED,SAAAkB,IAAAC,SAAAC,gBAAAA,GAAAC,OAAAC,gBAEKC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEzB,UAAAA;AAAAA,IAAW;AAAA,IAAA,IAAA0B,WAAA;AAAA,aAAAP,IAAAQ,QAAAN,iBAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA,GAAAC,OAAAC,gBAMtBC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEvB,MAAAA;AAAAA,IAAO;AAAA,IAAA,IAAAwB,WAAA;AAAA,aAAAP,IAAAS,SAAAP,gBAAAA,GAAAC,OAI6CpB,MAAAA,CAAO,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA,GAAAoB,OAAAC,gBAKtEC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAE5B,UAAAA,KAAe,CAACK,MAAAA;AAAAA,IAAO;AAAA,IAAA,IAAAwB,WAAA;AAAA,aAAAP,IAAAU,SAAAR,gBAAAA,GAAAC,OAAAC,gBAE9BC,MAAI;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAG7B,MAAMU,UAAUC,OAAeuB;AAAAA,QAAK;AAAA,QAAA,IAAAJ,WAAA;AAAA,iBAAAP,IAAAY,SAAAV,mBAAAC,OAE3C1B,MAAMU,UAAUC,OAAeuB,KAAK,CAAA;AAAA,QAAA;AAAA,MAAA,CAAA,CAAA,GAAAE,aAAA,OAAAV,OAKjCzB,aAAW,IAAA,GAAA,KAAA,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA,CAAA;AAiB9B;AAKA,SAASoC,cAAcrC,OAGpB;AACD,QAAMsC,cAActC,MAAMU,UAAUC;AAEpC,SAAAY,IAAAgB,SAAAd,gBAAAA,GAAAC,OAAAC,gBAGOC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAES,YAAYJ;AAAAA,IAAK;AAAA,IAAA,IAAAJ,WAAA;AAAA,aAAAP,IAAAY,SAAAV,gBAAAA,GAAAC,OAExBY,YAAYJ,KAAK,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA,GAAAR,OAAAC,gBAQba,KAAG;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEH,YAAYI;AAAAA,IAAO;AAAA,IAAAZ,UAC3BA,CAACa,WAAWpB,IAAAqB,SAAAnB,mBAAAoB,SAIFF,OAAOG,QAAQ;AAAA,MAAEA,OAAOH,OAAOG;AAAAA,IAAAA,IAAU,EAAE,GAAApB,OAEjDiB,OAAOI,KAAK,CAAA;AAAA,EAAA,CAEhB,CAAA,GAAArB,OAAAC,gBAKJa,KAAG;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEH,YAAYU,KAAKC,MAAM,GAAGC,wBAAwBC,YAAY;AAAA,IAAC;AAAA,IAAArB,UACvEA,CAACsB,QAAQ7B,IAAA8B,SAAA5B,mBAAAC,OAAAC,gBAELa,KAAG;AAAA,MAAA,IAACC,OAAI;AAAA,eAAEH,YAAYI;AAAAA,MAAO;AAAA,MAAAZ,UAC3BA,CAACa,WAAWpB,IAAA+B,SAAA7B,gBAAAA,GAERC,OAAA0B,IAAIT,OAAOY,GAAG,CAAC,KAAI,GAAG;AAAA,IAAA,CAE1B,CAAA,CAAA;AAAA,EAAA,CAGN,CAAA,GAAA7B,OAAAC,gBAMRC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAES,YAAYkB;AAAAA,IAAU;AAAA,IAAA,IAAA1B,WAAA;AAAA,aAAAP,IAAAkC,SAAAhC,gBAAAA,GAGnBC,OAAAY,YAAYkB,WAAWE,WAAW,IAAAhC,OAAGY,YAAYkB,WAAWG,QAAQ,IAAG,GAACjC,OAChFkC,KAAKC,KACHvB,YAAYkB,WAAWE,cAAc,KAAKpB,YAAYkB,WAAWG,UAClErB,YAAYkB,WAAWM,SACzB,CAAC,GAAApC,OACGY,YAAYkB,WAAWM,SAAS,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA,CAAA;AAOlD;AAKA,SAASC,eAAe/D,OAAmC;AACzD,QAAMgE,eAAehE,MAAMU,UAAUC;AAErC,SAAAY,IAAA0C,UAAAxC,gBAAAA,GAAAC,OAKWsC,aAAa9B,KAAK,GAAAR,OAG8CsC,aAAaE,KAAK,GAAAxC,OAAAC,gBAClFC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEmC,aAAaG;AAAAA,IAAI;AAAA,IAAA,IAAArC,WAAA;AAAA,aAAAP,IAAA6C,SAAA3C,gBAAAA,GAAAC,OAExBsC,aAAaG,IAAI,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA,GAAAzC,OAAAC,gBAMzBC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEmC,aAAaK;AAAAA,IAAK;AAAA,IAAA,IAAAvC,WAAA;AAAA,aAAAP,IAAA+C,UAAA7C,gBAAAA,GAGjB,uBACLuC,aAAaK,MAAME,cAAc,OAC7B,uCACAP,aAAaK,MAAME,cAAc,SAC/B,mCACA,kCAAkC,IAGzCP,aAAaK,MAAME,cAAc,OAC9B,MACAP,aAAaK,MAAME,cAAc,SAC/B,MACA,KAAG7C,OACRkC,KAAKY,IAAIR,aAAaK,MAAMH,KAAK,CAAC,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA,GAAAxC,OAAAC,gBAKxCC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEmC,aAAaS;AAAAA,IAAQ;AAAA,IAAA,IAAA3C,WAAA;AAAA,aAAAP,IAAAmD,UAAAjD,gBAAAA,GAAAC,OAC2BsC,aAAaS,QAAQ,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA,CAAA;AAKzF;AAKA,SAASE,aAAa3E,OAAmC;AACvD,QAAM4E,aAAa5E,MAAMU,UAAUC;AAGnC,QAAMkE,cAAcC,WAAW,MAAM;AACnC,QAAIF,WAAWG,UAAU;AACvB,aAAOC,EAAOC,MAAML,WAAWM,SAAS;AAAA,QAAEC,OAAO;AAAA,MAAA,CAAO;AAAA,IAC1D;AACA,WAAOP,WAAWM;AAAAA,EACpB,CAAC;AAED,SAAA3D,IAAA6D,UAAA3D,gBAAAA,GAGa,+CAA+CC,OAAAkD,WAAWS,WAAS,IAAA,KAAI,EAAE,IACrER,aAAa;AAIhC;AAKA,SAASS,kBAAkBtF,OAGxB;;AAED,QAAMuF,aAAaC,kBAAkBxF,MAAMU,SAAS;AACpD,MAAI,CAAC6E,WAAWE,OAAO;AACrBzF,gBAAM0F,YAAN1F,+BAAgB;AAAA,MACda,MAAM;AAAA,MACN8E,SAAS;AAAA,MACTC,aAAa5F,MAAMU,UAAUmF;AAAAA,MAC7BC,SAASP,WAAWQ;AAAAA,IAAAA;AAGtB,WAAAxE,IAAAyE,UAAAvE,gBAAAA,GAIOC,QAAA6D,sBAAWQ,WAAXR,mBAAoB,OAApBA,mBAAwBI,OAAO,KAAI,0BAA0B;AAAA,EAItE;AAGA,SAAAhE,gBACGsE,2BAAyB;AAAA,IAAA,IACxBL,cAAW;AAAA,aAAE5F,MAAMU,UAAUmF;AAAAA,IAAE;AAAA,IAAA,IAC/BK,gBAAa;AAAA,aAAElG,MAAMU,UAAUG;AAAAA,IAAI;AAAA,IAAA,IACnC6E,UAAO;AAAA,aAAE1F,MAAM0F;AAAAA,IAAO;AAAA,IACtBS,YAAY;AAAA,IAAI,IAAArE,WAAA;AAAA,aAAA,CAAAH,gBAEfC,MAAI;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE7B,MAAMU,UAAUG,SAAS;AAAA,QAAO;AAAA,QAAA,IAAAiB,WAAA;AAAA,iBAAAH,gBACzC5B,eAAa;AAAA,YAAA,IAACW,YAAS;AAAA,qBAAEV,MAAMU;AAAAA,YAAS;AAAA,YAAA,IAAEgF,UAAO;AAAA,qBAAE1F,MAAM0F;AAAAA,YAAO;AAAA,UAAA,CAAA;AAAA,QAAA;AAAA,MAAA,CAAA,GAAA/D,gBAElEC,MAAI;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE7B,MAAMU,UAAUG,SAAS;AAAA,QAAO;AAAA,QAAA,IAAAiB,WAAA;AAAA,iBAAAH,gBACzCU,eAAa;AAAA,YAAA,IAAC3B,YAAS;AAAA,qBAAEV,MAAMU;AAAAA,YAAS;AAAA,YAAA,IAAEgF,UAAO;AAAA,qBAAE1F,MAAM0F;AAAAA,YAAO;AAAA,UAAA,CAAA;AAAA,QAAA;AAAA,MAAA,CAAA,GAAA/D,gBAElEC,MAAI;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE7B,MAAMU,UAAUG,SAAS;AAAA,QAAQ;AAAA,QAAA,IAAAiB,WAAA;AAAA,iBAAAH,gBAC1CoC,gBAAc;AAAA,YAAA,IAACrD,YAAS;AAAA,qBAAEV,MAAMU;AAAAA,YAAS;AAAA,UAAA,CAAA;AAAA,QAAA;AAAA,MAAA,CAAA,GAAAiB,gBAE3CC,MAAI;AAAA,QAAA,IAACC,OAAI;AAAA,iBAAE7B,MAAMU,UAAUG,SAAS;AAAA,QAAM;AAAA,QAAA,IAAAiB,WAAA;AAAA,iBAAAH,gBACxCgD,cAAY;AAAA,YAAA,IAACjE,YAAS;AAAA,qBAAEV,MAAMU;AAAAA,YAAS;AAAA,UAAA,CAAA;AAAA,QAAA;AAAA,MAAA,CAAA,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA;AAIhD;AAKO,MAAM0F,qBAA0DpG,CAAAA,UAAU;AAC/E,QAAMqG,SAASA,MAAM;AAGnB,QAAI,UAAUrG,MAAMkF,WAAYlF,MAAMkF,QAAgBrE,SAAS,aAAa;AAC1E,aAAO;AAAA,QACLgF,IAAI;AAAA,QACJS,YAAY,CAACtG,MAAMkF,OAAsB;AAAA,QACzCqB,MAAM;AAAA,UACJ7D,SAAS;AAAA,UACT8D,KAAK;AAAA,QAAA;AAAA,MACP;AAAA,IAEJ;AACA,WAAOxG,MAAMkF;AAAAA,EACf;AAsBA,QAAMuB,qBAAqBA,MACzB,iCAAiCJ,OAAAA,EAASE,KAAK7D,OAAO,gBAAgB2D,OAAAA,EAASE,KAAKC,GAAG;AAGzF,QAAME,qBAAqBA,CAAChG,cAA2B;AAErD,QAAI,CAACA,UAAUiG,UAAU;AACvBC,cAAQtG,MAAM,0DAA0DI,SAAS;AACjF,aAAO;AAAA,IACT;AAEA,UAAM;AAAA,MAAEmG;AAAAA,MAAUC;AAAAA,MAASC;AAAAA,MAAUC,UAAU;AAAA,IAAA,IAAMtG,UAAUiG;AAC/D,WAAO,gBAAgBE,QAAQ,WAAWC,OAAO,eAAeC,WAAW,GAAGA,QAAQ,WAAWC,OAAO,KAAK,MAAM;AAAA,EACrH;AAEA,SAAAzF,IAAA0F,UAAAxF,gBAAAA,GACc,UAAUC,OAAA1B,MAAMkH,OAAK,IAAA,KAAI,EAAE,IAAErE,SACR4D,mBAAAA,CAAoB,GAAA/E,OAAAC,gBAChDa,KAAG;AAAA,IAAA,IAACC,OAAI;AAAA,aAAE4D,SAASC;AAAAA,IAAU;AAAA,IAAAxE,UAC1BpB,CAAAA,cAASa,IAAA4F,UAAA1F,gBAAAA,GAAAoB,SACG6D,mBAAmBhG,SAAS,CAAC,GAAAgB,OAAAC,gBACtC2D,mBAAiB;AAAA,MAAC5E;AAAAA,MAAoB,IAAEgF,UAAO;AAAA,eAAE1F,MAAM0F;AAAAA,MAAO;AAAA,IAAA,CAAA,CAAA,CAAA;AAAA,EAAA,CAElE,CAAA,CAAA;AAKX;"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStreamingUI.cjs","sources":["../../../../src/hooks/useStreamingUI.ts"],"sourcesContent":["/**\n * useStreamingUI Hook - Phase 2\n *\n * Client-side hook for consuming the streaming generative UI endpoint.\n * Handles SSE connection, component buffering, reordering, and error handling.\n *\n * Features:\n * - SSE connection with automatic reconnection\n * - Component buffering and reordering by sequenceId\n * - Progress tracking and loading states\n * - Error handling with recovery attempts\n * - Cleanup on unmount\n *\n * Usage:\n * ```tsx\n * const { components, isLoading, error, progress } = useStreamingUI({\n * query: 'Show me revenue trends',\n * spaceIds: ['uuid1', 'uuid2'],\n * onComplete: (metadata) => console.log('Done!', metadata),\n * })\n * ```\n */\n\nimport { createSignal, onCleanup } from 'solid-js'\nimport type { UIComponent } from '../types'\nimport { createLogger } from '../utils/logger'\n\nconst logger = createLogger('useStreamingUI')\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseStreamingUIOptions {\n query: string\n spaceIds?: string[]\n sessionId?: string\n options?: {\n useCache?: boolean\n useLLM?: boolean\n maxComponents?: number\n preferredComponents?: Array<'chart' | 'table' | 'metric' | 'text'>\n }\n onComplete?: (metadata: CompleteMetadata) => void\n onError?: (error: StreamError) => void\n onComponentReceived?: (component: UIComponent) => void\n}\n\nexport interface StreamingUIState {\n components: UIComponent[]\n isLoading: boolean\n isStreaming: boolean\n error: StreamError | null\n progress: StreamProgress\n metadata: CompleteMetadata | null\n}\n\nexport interface StreamProgress {\n receivedCount: number\n totalCount: number | null\n message: string\n timestamp: string\n}\n\nexport interface CompleteMetadata {\n layoutId: string\n componentsCount: number\n executionTimeMs: number\n firstTokenMs: number\n provider: 'groq' | 'mock'\n model: string\n tokensUsed?: number\n costUSD?: number\n cached: boolean\n}\n\nexport interface StreamError {\n error: string\n message: string\n componentId?: string\n recoverable: boolean\n}\n\ninterface ComponentBuffer {\n [sequenceId: number]: {\n component: UIComponent\n position: { colStart: number; colSpan: number; rowStart?: number; rowSpan?: number }\n }\n}\n\n// ============================================================================\n// SSE Event Types (must match server)\n// ============================================================================\n\ntype SSEEventType = 'status' | 'component-start' | 'component' | 'complete' | 'error'\n\ninterface StatusEvent {\n message: string\n timestamp: string\n totalComponents?: number\n}\n\ninterface ComponentStartEvent {\n componentId: string\n type: 'chart' | 'table' | 'metric' | 'text'\n sequenceId: number\n position: { colStart: number; colSpan: number }\n}\n\ninterface ComponentEvent {\n componentId: string\n sequenceId: number\n component: UIComponent\n position: { colStart: number; colSpan: number; rowStart?: number; rowSpan?: number }\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\nexport function useStreamingUI(options: UseStreamingUIOptions) {\n // State\n const [components, setComponents] = createSignal<UIComponent[]>([])\n const [isLoading, setIsLoading] = createSignal(false)\n const [isStreaming, setIsStreaming] = createSignal(false)\n const [error, setError] = createSignal<StreamError | null>(null)\n const [progress, setProgress] = createSignal<StreamProgress>({\n receivedCount: 0,\n totalCount: null,\n message: 'Initializing...',\n timestamp: new Date().toISOString(),\n })\n const [metadata, setMetadata] = createSignal<CompleteMetadata | null>(null)\n\n // Component buffer for reordering\n let componentBuffer: ComponentBuffer = {}\n let nextSequenceId = 0\n let eventSource: EventSource | null = null\n let reconnectAttempts = 0\n const maxReconnectAttempts = 3\n\n /**\n * Flush components from buffer in sequence order\n */\n const flushBuffer = () => {\n const flushed: UIComponent[] = []\n\n while (componentBuffer[nextSequenceId]) {\n const { component } = componentBuffer[nextSequenceId]\n flushed.push(component)\n delete componentBuffer[nextSequenceId]\n nextSequenceId++\n }\n\n if (flushed.length > 0) {\n setComponents((prev) => [...prev, ...flushed])\n\n setProgress((prev) => ({\n ...prev,\n receivedCount: prev.receivedCount + flushed.length,\n }))\n\n logger.debug('Flushed components from buffer', {\n count: flushed.length,\n nextSequenceId,\n })\n }\n }\n\n /**\n * Handle SSE status event\n */\n const handleStatusEvent = (data: StatusEvent) => {\n logger.debug('Status event received', data as unknown as Record<string, unknown>)\n\n setProgress({\n receivedCount: progress().receivedCount,\n totalCount: data.totalComponents ?? progress().totalCount,\n message: data.message,\n timestamp: data.timestamp,\n })\n }\n\n /**\n * Handle SSE component-start event\n */\n const handleComponentStartEvent = (data: ComponentStartEvent) => {\n logger.debug('Component-start event received', data as unknown as Record<string, unknown>)\n\n setProgress((prev) => ({\n ...prev,\n message: `Loading ${data.type} component...`,\n timestamp: new Date().toISOString(),\n }))\n }\n\n /**\n * Handle SSE component event\n */\n const handleComponentEvent = (data: ComponentEvent) => {\n logger.debug('Component event received', {\n componentId: data.componentId,\n sequenceId: data.sequenceId,\n })\n\n // Add to buffer\n componentBuffer[data.sequenceId] = {\n component: data.component,\n position: data.position,\n }\n\n // Flush buffer in sequence\n flushBuffer()\n\n // Notify callback\n if (options.onComponentReceived) {\n options.onComponentReceived(data.component)\n }\n }\n\n /**\n * Handle SSE complete event\n */\n const handleCompleteEvent = (data: CompleteMetadata) => {\n logger.info('Stream completed', data as unknown as Record<string, unknown>)\n\n setIsStreaming(false)\n setIsLoading(false)\n setMetadata(data)\n\n // Flush any remaining buffered components\n flushBuffer()\n\n setProgress((prev) => ({\n ...prev,\n message: 'Dashboard loaded',\n timestamp: new Date().toISOString(),\n }))\n\n // Notify callback\n if (options.onComplete) {\n options.onComplete(data)\n }\n }\n\n /**\n * Handle SSE error event\n */\n const handleErrorEvent = (data: StreamError) => {\n logger.error('Stream error received', data as unknown as Record<string, unknown>)\n\n setError(data)\n setIsStreaming(false)\n setIsLoading(false)\n\n setProgress((prev) => ({\n ...prev,\n message: `Error: ${data.message}`,\n timestamp: new Date().toISOString(),\n }))\n\n // Notify callback\n if (options.onError) {\n options.onError(data)\n }\n\n // Try to reconnect if recoverable\n if (data.recoverable && reconnectAttempts < maxReconnectAttempts) {\n reconnectAttempts++\n logger.warn('Attempting to reconnect', { attempt: reconnectAttempts })\n setTimeout(() => startStreaming(), 1000 * reconnectAttempts)\n }\n }\n\n /**\n * Parse SSE event\n */\n const parseSSEEvent = (event: MessageEvent, eventType: SSEEventType) => {\n try {\n const data = JSON.parse(event.data)\n\n switch (eventType) {\n case 'status':\n handleStatusEvent(data as StatusEvent)\n break\n case 'component-start':\n handleComponentStartEvent(data as ComponentStartEvent)\n break\n case 'component':\n handleComponentEvent(data as ComponentEvent)\n break\n case 'complete':\n handleCompleteEvent(data as CompleteMetadata)\n break\n case 'error':\n handleErrorEvent(data as StreamError)\n break\n default:\n logger.warn('Unknown SSE event type', { eventType })\n }\n } catch (error) {\n logger.error('Failed to parse SSE event', {\n error: error instanceof Error ? error.message : String(error),\n eventType,\n })\n }\n }\n\n /**\n * Start SSE streaming\n */\n const startStreaming = () => {\n // Reset state\n setComponents([])\n setError(null)\n setIsLoading(true)\n setIsStreaming(true)\n componentBuffer = {}\n nextSequenceId = 0\n\n setProgress({\n receivedCount: 0,\n totalCount: null,\n message: 'Connecting to server...',\n timestamp: new Date().toISOString(),\n })\n\n logger.info('Starting SSE stream', {\n query: options.query,\n spaceIds: options.spaceIds,\n })\n\n // Build request body\n const requestBody = {\n query: options.query,\n spaceIds: options.spaceIds,\n sessionId: options.sessionId,\n options: options.options,\n }\n\n // Create EventSource (SSE connection)\n // Note: EventSource doesn't support POST, so we need to use fetch + ReadableStream\n fetch('/api/mcp/generative-ui-stream', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n })\n .then(async (response) => {\n if (!response.ok) {\n const errorData = await response.json()\n throw new Error(errorData.message || 'Stream request failed')\n }\n\n if (!response.body) {\n throw new Error('Response body is null')\n }\n\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n\n let buffer = ''\n\n // Read stream\n const readChunk = async (): Promise<void> => {\n const { done, value } = await reader.read()\n\n if (done) {\n logger.info('Stream ended')\n return\n }\n\n buffer += decoder.decode(value, { stream: true })\n\n // Process SSE messages\n const lines = buffer.split('\\n')\n buffer = lines.pop() || '' // Keep incomplete line in buffer\n\n let currentEvent: SSEEventType | null = null\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7) as SSEEventType\n } else if (line.startsWith('data: ') && currentEvent) {\n const data = line.slice(6)\n parseSSEEvent({ data } as MessageEvent, currentEvent)\n currentEvent = null\n }\n }\n\n // Continue reading\n return readChunk()\n }\n\n await readChunk()\n })\n .catch((err) => {\n logger.error('Stream fetch failed', {\n error: err instanceof Error ? err.message : String(err),\n })\n\n handleErrorEvent({\n error: 'Stream connection failed',\n message: err instanceof Error ? err.message : 'Unknown error',\n recoverable: true,\n })\n })\n }\n\n /**\n * Stop streaming\n */\n const stopStreaming = () => {\n if (eventSource) {\n eventSource.close()\n eventSource = null\n }\n\n setIsStreaming(false)\n setIsLoading(false)\n\n logger.info('Streaming stopped')\n }\n\n /**\n * Cleanup on unmount\n */\n onCleanup(() => {\n stopStreaming()\n })\n\n // Auto-start streaming\n startStreaming()\n\n // Return state accessors and controls\n return {\n components,\n isLoading,\n isStreaming,\n error,\n progress,\n metadata,\n startStreaming,\n stopStreaming,\n }\n}\n"],"names":["createLogger","createSignal","error","onCleanup"],"mappings":"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"}