sunpeak 0.18.6 → 0.18.7

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 (49) hide show
  1. package/bin/commands/dev.mjs +6 -2
  2. package/bin/commands/inspect.mjs +13 -1
  3. package/bin/commands/new.mjs +5 -0
  4. package/bin/lib/dev-overlay.mjs +50 -0
  5. package/bin/lib/live/live-config.d.mts +3 -0
  6. package/bin/lib/live/live-config.mjs +3 -1
  7. package/dist/chatgpt/index.cjs +2 -2
  8. package/dist/chatgpt/index.js +2 -2
  9. package/dist/claude/index.cjs +1 -1
  10. package/dist/claude/index.js +1 -1
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/inspector/index.cjs +2 -2
  14. package/dist/inspector/index.js +2 -2
  15. package/dist/inspector/inspector-url.d.ts +13 -0
  16. package/dist/inspector/use-inspector-state.d.ts +2 -0
  17. package/dist/{inspector-DRD_Q66E.cjs → inspector-CKc58UuI.cjs} +60 -19
  18. package/dist/inspector-CKc58UuI.cjs.map +1 -0
  19. package/dist/{inspector-CjSoXm6N.js → inspector-DZrN0kej.js} +60 -19
  20. package/dist/inspector-DZrN0kej.js.map +1 -0
  21. package/dist/{inspector-url-7qhtJwY6.cjs → inspector-url-C3LTKgXt.cjs} +3 -1
  22. package/dist/inspector-url-C3LTKgXt.cjs.map +1 -0
  23. package/dist/{inspector-url-DuEFmxLP.js → inspector-url-CyQcuBI9.js} +3 -1
  24. package/dist/inspector-url-CyQcuBI9.js.map +1 -0
  25. package/dist/mcp/index.cjs +81 -7
  26. package/dist/mcp/index.cjs.map +1 -1
  27. package/dist/mcp/index.js +81 -7
  28. package/dist/mcp/index.js.map +1 -1
  29. package/dist/style.css +4 -0
  30. package/package.json +1 -1
  31. package/template/dist/albums/albums.html +1 -1
  32. package/template/dist/albums/albums.json +1 -1
  33. package/template/dist/carousel/carousel.html +1 -1
  34. package/template/dist/carousel/carousel.json +1 -1
  35. package/template/dist/map/map.html +1 -1
  36. package/template/dist/map/map.json +1 -1
  37. package/template/dist/review/review.html +1 -1
  38. package/template/dist/review/review.json +1 -1
  39. package/template/tests/e2e/albums.spec.ts +1 -1
  40. package/template/tests/e2e/carousel.spec.ts +1 -1
  41. package/template/tests/e2e/dev-overlay.spec.ts +118 -0
  42. package/template/tests/e2e/helpers.ts +13 -0
  43. package/template/tests/e2e/map.spec.ts +1 -1
  44. package/template/tests/e2e/review.spec.ts +1 -1
  45. package/template/tests/live/playwright.config.ts +1 -1
  46. package/dist/inspector-CjSoXm6N.js.map +0 -1
  47. package/dist/inspector-DRD_Q66E.cjs.map +0 -1
  48. package/dist/inspector-url-7qhtJwY6.cjs.map +0 -1
  49. package/dist/inspector-url-DuEFmxLP.js.map +0 -1
@@ -38,6 +38,8 @@ function createInspectorUrl(params, basePath = "/") {
38
38
  if (params.safeAreaLeft !== void 0) searchParams.set("safeAreaLeft", String(params.safeAreaLeft));
39
39
  if (params.safeAreaRight !== void 0) searchParams.set("safeAreaRight", String(params.safeAreaRight));
40
40
  if (params.prodResources !== void 0) searchParams.set("prodResources", String(params.prodResources));
41
+ if (params.sidebar !== void 0) searchParams.set("sidebar", String(params.sidebar));
42
+ if (params.devOverlay !== void 0) searchParams.set("devOverlay", String(params.devOverlay));
41
43
  const queryString = searchParams.toString();
42
44
  return queryString ? `${basePath}?${queryString}` : basePath;
43
45
  }
@@ -49,4 +51,4 @@ Object.defineProperty(exports, "createInspectorUrl", {
49
51
  }
50
52
  });
51
53
 
52
- //# sourceMappingURL=inspector-url-7qhtJwY6.cjs.map
54
+ //# sourceMappingURL=inspector-url-C3LTKgXt.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspector-url-C3LTKgXt.cjs","names":[],"sources":["../src/inspector/inspector-url.ts"],"sourcesContent":["import type { Theme, DisplayMode, DeviceType } from '../types/runtime';\n\n/**\n * Strongly-typed URL parameters for the Inspector.\n *\n * Use with `createInspectorUrl()` to generate type-safe URL paths for e2e tests.\n *\n * The two primary selectors mirror the sidebar dropdowns:\n * - `tool` — which tool to inspect (Tool dropdown)\n * - `simulation` — which simulation fixture to load (Simulation dropdown)\n *\n * When only `tool` is specified, no mock data is loaded (\"Press Run\" state).\n * When `simulation` is specified, mock data from that fixture renders immediately.\n * When both are specified, the tool is selected and the simulation provides mock data.\n *\n * @example\n * ```ts\n * import { createInspectorUrl } from 'sunpeak/inspector';\n *\n * // Select a tool with no mock data (user must click Run):\n * await page.goto(createInspectorUrl({ tool: 'show-albums' }));\n *\n * // Select a simulation (mock data renders immediately):\n * await page.goto(createInspectorUrl({ simulation: 'show-albums' }));\n *\n * // Full options:\n * await page.goto(createInspectorUrl({\n * simulation: 'show-albums',\n * theme: 'dark',\n * host: 'claude',\n * }));\n * ```\n */\nexport interface InspectorUrlParams {\n /**\n * The simulation name to load (e.g., 'show-albums', 'review-diff').\n * Corresponds to the simulation JSON filename without the `.json` extension.\n * When specified, mock data from the simulation fixture renders immediately.\n */\n simulation?: string;\n\n /**\n * The tool name to select (e.g., 'show-albums', 'show-map').\n * When specified without `simulation`, no mock data is loaded — the user\n * must click Run to call the real handler.\n */\n tool?: string;\n\n /**\n * The host shell to use (e.g., 'chatgpt', 'claude').\n * Switches conversation chrome, theming, and reported host info/capabilities.\n * @default 'chatgpt'\n */\n host?: string;\n\n /**\n * The color theme for the inspector.\n * @default 'dark'\n */\n theme?: Theme;\n\n /**\n * The display mode for the widget.\n * - 'inline': Embedded in the conversation\n * - 'pip': Picture-in-picture mode with max height\n * - 'fullscreen': Full screen overlay\n * @default 'inline'\n */\n displayMode?: DisplayMode;\n\n /**\n * The locale for the inspector (e.g., 'en-US', 'ja-JP').\n * @default 'en-US'\n */\n locale?: string;\n\n /**\n * Maximum height in pixels for PiP mode.\n * Only applicable when displayMode is 'pip'.\n */\n maxHeight?: number;\n\n /**\n * The device type to simulate.\n * Affects default hover/touch capabilities.\n */\n deviceType?: DeviceType;\n\n /**\n * Whether the device supports hover interactions.\n * @default true for desktop, false for mobile/tablet\n */\n hover?: boolean;\n\n /**\n * Whether the device supports touch interactions.\n * @default false for desktop, true for mobile/tablet\n */\n touch?: boolean;\n\n /**\n * Safe area inset from the top of the screen (in pixels).\n * Used for devices with notches or status bars.\n */\n safeAreaTop?: number;\n\n /**\n * Safe area inset from the bottom of the screen (in pixels).\n * Used for devices with home indicators.\n */\n safeAreaBottom?: number;\n\n /**\n * Safe area inset from the left of the screen (in pixels).\n */\n safeAreaLeft?: number;\n\n /**\n * Safe area inset from the right of the screen (in pixels).\n */\n safeAreaRight?: number;\n\n /**\n * Enable Prod Resources mode (production dist/ bundles instead of HMR).\n */\n prodResources?: boolean;\n\n /**\n * Show the inspector sidebar. Useful for headless testing or embedding\n * the inspector as a pure resource viewer.\n * @default true\n */\n sidebar?: boolean;\n\n /**\n * Show the dev overlay (resource timestamp + tool timing) inside resources.\n * Set to false to hide it during e2e tests so it doesn't interfere with\n * element assertions.\n * @default true\n */\n devOverlay?: boolean;\n}\n\n/**\n * Creates a URL path with query parameters for the Inspector.\n *\n * @param params - The inspector parameters to encode\n * @param basePath - The base path for the URL (default: '/')\n * @returns A URL path string with encoded query parameters\n *\n * @example\n * ```ts\n * // Tool only (no mock data, \"Press Run\" state):\n * createInspectorUrl({ tool: 'show-albums' })\n * // Returns: '/?tool=show-albums'\n *\n * // Simulation (mock data renders immediately):\n * createInspectorUrl({ simulation: 'show-albums', theme: 'light' })\n * // Returns: '/?simulation=show-albums&theme=light'\n *\n * // Both tool and simulation:\n * createInspectorUrl({ tool: 'show-albums', simulation: 'show-albums' })\n * // Returns: '/?tool=show-albums&simulation=show-albums'\n * ```\n */\nexport function createInspectorUrl(params: InspectorUrlParams, basePath = '/'): string {\n const searchParams = new URLSearchParams();\n\n if (params.tool !== undefined) {\n searchParams.set('tool', params.tool);\n }\n if (params.simulation !== undefined) {\n searchParams.set('simulation', params.simulation);\n }\n if (params.host !== undefined) {\n searchParams.set('host', params.host);\n }\n if (params.theme !== undefined) {\n searchParams.set('theme', params.theme);\n }\n if (params.displayMode !== undefined) {\n searchParams.set('displayMode', params.displayMode);\n }\n if (params.locale !== undefined) {\n searchParams.set('locale', params.locale);\n }\n if (params.maxHeight !== undefined) {\n searchParams.set('maxHeight', String(params.maxHeight));\n }\n if (params.deviceType !== undefined) {\n searchParams.set('deviceType', params.deviceType);\n }\n if (params.hover !== undefined) {\n searchParams.set('hover', String(params.hover));\n }\n if (params.touch !== undefined) {\n searchParams.set('touch', String(params.touch));\n }\n if (params.safeAreaTop !== undefined) {\n searchParams.set('safeAreaTop', String(params.safeAreaTop));\n }\n if (params.safeAreaBottom !== undefined) {\n searchParams.set('safeAreaBottom', String(params.safeAreaBottom));\n }\n if (params.safeAreaLeft !== undefined) {\n searchParams.set('safeAreaLeft', String(params.safeAreaLeft));\n }\n if (params.safeAreaRight !== undefined) {\n searchParams.set('safeAreaRight', String(params.safeAreaRight));\n }\n if (params.prodResources !== undefined) {\n searchParams.set('prodResources', String(params.prodResources));\n }\n if (params.sidebar !== undefined) {\n searchParams.set('sidebar', String(params.sidebar));\n }\n if (params.devOverlay !== undefined) {\n searchParams.set('devOverlay', String(params.devOverlay));\n }\n\n const queryString = searchParams.toString();\n return queryString ? `${basePath}?${queryString}` : basePath;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAqKA,SAAgB,mBAAmB,QAA4B,WAAW,KAAa;CACrF,MAAM,eAAe,IAAI,iBAAiB;AAE1C,KAAI,OAAO,SAAS,KAAA,EAClB,cAAa,IAAI,QAAQ,OAAO,KAAK;AAEvC,KAAI,OAAO,eAAe,KAAA,EACxB,cAAa,IAAI,cAAc,OAAO,WAAW;AAEnD,KAAI,OAAO,SAAS,KAAA,EAClB,cAAa,IAAI,QAAQ,OAAO,KAAK;AAEvC,KAAI,OAAO,UAAU,KAAA,EACnB,cAAa,IAAI,SAAS,OAAO,MAAM;AAEzC,KAAI,OAAO,gBAAgB,KAAA,EACzB,cAAa,IAAI,eAAe,OAAO,YAAY;AAErD,KAAI,OAAO,WAAW,KAAA,EACpB,cAAa,IAAI,UAAU,OAAO,OAAO;AAE3C,KAAI,OAAO,cAAc,KAAA,EACvB,cAAa,IAAI,aAAa,OAAO,OAAO,UAAU,CAAC;AAEzD,KAAI,OAAO,eAAe,KAAA,EACxB,cAAa,IAAI,cAAc,OAAO,WAAW;AAEnD,KAAI,OAAO,UAAU,KAAA,EACnB,cAAa,IAAI,SAAS,OAAO,OAAO,MAAM,CAAC;AAEjD,KAAI,OAAO,UAAU,KAAA,EACnB,cAAa,IAAI,SAAS,OAAO,OAAO,MAAM,CAAC;AAEjD,KAAI,OAAO,gBAAgB,KAAA,EACzB,cAAa,IAAI,eAAe,OAAO,OAAO,YAAY,CAAC;AAE7D,KAAI,OAAO,mBAAmB,KAAA,EAC5B,cAAa,IAAI,kBAAkB,OAAO,OAAO,eAAe,CAAC;AAEnE,KAAI,OAAO,iBAAiB,KAAA,EAC1B,cAAa,IAAI,gBAAgB,OAAO,OAAO,aAAa,CAAC;AAE/D,KAAI,OAAO,kBAAkB,KAAA,EAC3B,cAAa,IAAI,iBAAiB,OAAO,OAAO,cAAc,CAAC;AAEjE,KAAI,OAAO,kBAAkB,KAAA,EAC3B,cAAa,IAAI,iBAAiB,OAAO,OAAO,cAAc,CAAC;AAEjE,KAAI,OAAO,YAAY,KAAA,EACrB,cAAa,IAAI,WAAW,OAAO,OAAO,QAAQ,CAAC;AAErD,KAAI,OAAO,eAAe,KAAA,EACxB,cAAa,IAAI,cAAc,OAAO,OAAO,WAAW,CAAC;CAG3D,MAAM,cAAc,aAAa,UAAU;AAC3C,QAAO,cAAc,GAAG,SAAS,GAAG,gBAAgB"}
@@ -38,10 +38,12 @@ function createInspectorUrl(params, basePath = "/") {
38
38
  if (params.safeAreaLeft !== void 0) searchParams.set("safeAreaLeft", String(params.safeAreaLeft));
39
39
  if (params.safeAreaRight !== void 0) searchParams.set("safeAreaRight", String(params.safeAreaRight));
40
40
  if (params.prodResources !== void 0) searchParams.set("prodResources", String(params.prodResources));
41
+ if (params.sidebar !== void 0) searchParams.set("sidebar", String(params.sidebar));
42
+ if (params.devOverlay !== void 0) searchParams.set("devOverlay", String(params.devOverlay));
41
43
  const queryString = searchParams.toString();
42
44
  return queryString ? `${basePath}?${queryString}` : basePath;
43
45
  }
44
46
  //#endregion
45
47
  export { createInspectorUrl as t };
46
48
 
47
- //# sourceMappingURL=inspector-url-DuEFmxLP.js.map
49
+ //# sourceMappingURL=inspector-url-CyQcuBI9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspector-url-CyQcuBI9.js","names":[],"sources":["../src/inspector/inspector-url.ts"],"sourcesContent":["import type { Theme, DisplayMode, DeviceType } from '../types/runtime';\n\n/**\n * Strongly-typed URL parameters for the Inspector.\n *\n * Use with `createInspectorUrl()` to generate type-safe URL paths for e2e tests.\n *\n * The two primary selectors mirror the sidebar dropdowns:\n * - `tool` — which tool to inspect (Tool dropdown)\n * - `simulation` — which simulation fixture to load (Simulation dropdown)\n *\n * When only `tool` is specified, no mock data is loaded (\"Press Run\" state).\n * When `simulation` is specified, mock data from that fixture renders immediately.\n * When both are specified, the tool is selected and the simulation provides mock data.\n *\n * @example\n * ```ts\n * import { createInspectorUrl } from 'sunpeak/inspector';\n *\n * // Select a tool with no mock data (user must click Run):\n * await page.goto(createInspectorUrl({ tool: 'show-albums' }));\n *\n * // Select a simulation (mock data renders immediately):\n * await page.goto(createInspectorUrl({ simulation: 'show-albums' }));\n *\n * // Full options:\n * await page.goto(createInspectorUrl({\n * simulation: 'show-albums',\n * theme: 'dark',\n * host: 'claude',\n * }));\n * ```\n */\nexport interface InspectorUrlParams {\n /**\n * The simulation name to load (e.g., 'show-albums', 'review-diff').\n * Corresponds to the simulation JSON filename without the `.json` extension.\n * When specified, mock data from the simulation fixture renders immediately.\n */\n simulation?: string;\n\n /**\n * The tool name to select (e.g., 'show-albums', 'show-map').\n * When specified without `simulation`, no mock data is loaded — the user\n * must click Run to call the real handler.\n */\n tool?: string;\n\n /**\n * The host shell to use (e.g., 'chatgpt', 'claude').\n * Switches conversation chrome, theming, and reported host info/capabilities.\n * @default 'chatgpt'\n */\n host?: string;\n\n /**\n * The color theme for the inspector.\n * @default 'dark'\n */\n theme?: Theme;\n\n /**\n * The display mode for the widget.\n * - 'inline': Embedded in the conversation\n * - 'pip': Picture-in-picture mode with max height\n * - 'fullscreen': Full screen overlay\n * @default 'inline'\n */\n displayMode?: DisplayMode;\n\n /**\n * The locale for the inspector (e.g., 'en-US', 'ja-JP').\n * @default 'en-US'\n */\n locale?: string;\n\n /**\n * Maximum height in pixels for PiP mode.\n * Only applicable when displayMode is 'pip'.\n */\n maxHeight?: number;\n\n /**\n * The device type to simulate.\n * Affects default hover/touch capabilities.\n */\n deviceType?: DeviceType;\n\n /**\n * Whether the device supports hover interactions.\n * @default true for desktop, false for mobile/tablet\n */\n hover?: boolean;\n\n /**\n * Whether the device supports touch interactions.\n * @default false for desktop, true for mobile/tablet\n */\n touch?: boolean;\n\n /**\n * Safe area inset from the top of the screen (in pixels).\n * Used for devices with notches or status bars.\n */\n safeAreaTop?: number;\n\n /**\n * Safe area inset from the bottom of the screen (in pixels).\n * Used for devices with home indicators.\n */\n safeAreaBottom?: number;\n\n /**\n * Safe area inset from the left of the screen (in pixels).\n */\n safeAreaLeft?: number;\n\n /**\n * Safe area inset from the right of the screen (in pixels).\n */\n safeAreaRight?: number;\n\n /**\n * Enable Prod Resources mode (production dist/ bundles instead of HMR).\n */\n prodResources?: boolean;\n\n /**\n * Show the inspector sidebar. Useful for headless testing or embedding\n * the inspector as a pure resource viewer.\n * @default true\n */\n sidebar?: boolean;\n\n /**\n * Show the dev overlay (resource timestamp + tool timing) inside resources.\n * Set to false to hide it during e2e tests so it doesn't interfere with\n * element assertions.\n * @default true\n */\n devOverlay?: boolean;\n}\n\n/**\n * Creates a URL path with query parameters for the Inspector.\n *\n * @param params - The inspector parameters to encode\n * @param basePath - The base path for the URL (default: '/')\n * @returns A URL path string with encoded query parameters\n *\n * @example\n * ```ts\n * // Tool only (no mock data, \"Press Run\" state):\n * createInspectorUrl({ tool: 'show-albums' })\n * // Returns: '/?tool=show-albums'\n *\n * // Simulation (mock data renders immediately):\n * createInspectorUrl({ simulation: 'show-albums', theme: 'light' })\n * // Returns: '/?simulation=show-albums&theme=light'\n *\n * // Both tool and simulation:\n * createInspectorUrl({ tool: 'show-albums', simulation: 'show-albums' })\n * // Returns: '/?tool=show-albums&simulation=show-albums'\n * ```\n */\nexport function createInspectorUrl(params: InspectorUrlParams, basePath = '/'): string {\n const searchParams = new URLSearchParams();\n\n if (params.tool !== undefined) {\n searchParams.set('tool', params.tool);\n }\n if (params.simulation !== undefined) {\n searchParams.set('simulation', params.simulation);\n }\n if (params.host !== undefined) {\n searchParams.set('host', params.host);\n }\n if (params.theme !== undefined) {\n searchParams.set('theme', params.theme);\n }\n if (params.displayMode !== undefined) {\n searchParams.set('displayMode', params.displayMode);\n }\n if (params.locale !== undefined) {\n searchParams.set('locale', params.locale);\n }\n if (params.maxHeight !== undefined) {\n searchParams.set('maxHeight', String(params.maxHeight));\n }\n if (params.deviceType !== undefined) {\n searchParams.set('deviceType', params.deviceType);\n }\n if (params.hover !== undefined) {\n searchParams.set('hover', String(params.hover));\n }\n if (params.touch !== undefined) {\n searchParams.set('touch', String(params.touch));\n }\n if (params.safeAreaTop !== undefined) {\n searchParams.set('safeAreaTop', String(params.safeAreaTop));\n }\n if (params.safeAreaBottom !== undefined) {\n searchParams.set('safeAreaBottom', String(params.safeAreaBottom));\n }\n if (params.safeAreaLeft !== undefined) {\n searchParams.set('safeAreaLeft', String(params.safeAreaLeft));\n }\n if (params.safeAreaRight !== undefined) {\n searchParams.set('safeAreaRight', String(params.safeAreaRight));\n }\n if (params.prodResources !== undefined) {\n searchParams.set('prodResources', String(params.prodResources));\n }\n if (params.sidebar !== undefined) {\n searchParams.set('sidebar', String(params.sidebar));\n }\n if (params.devOverlay !== undefined) {\n searchParams.set('devOverlay', String(params.devOverlay));\n }\n\n const queryString = searchParams.toString();\n return queryString ? `${basePath}?${queryString}` : basePath;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAqKA,SAAgB,mBAAmB,QAA4B,WAAW,KAAa;CACrF,MAAM,eAAe,IAAI,iBAAiB;AAE1C,KAAI,OAAO,SAAS,KAAA,EAClB,cAAa,IAAI,QAAQ,OAAO,KAAK;AAEvC,KAAI,OAAO,eAAe,KAAA,EACxB,cAAa,IAAI,cAAc,OAAO,WAAW;AAEnD,KAAI,OAAO,SAAS,KAAA,EAClB,cAAa,IAAI,QAAQ,OAAO,KAAK;AAEvC,KAAI,OAAO,UAAU,KAAA,EACnB,cAAa,IAAI,SAAS,OAAO,MAAM;AAEzC,KAAI,OAAO,gBAAgB,KAAA,EACzB,cAAa,IAAI,eAAe,OAAO,YAAY;AAErD,KAAI,OAAO,WAAW,KAAA,EACpB,cAAa,IAAI,UAAU,OAAO,OAAO;AAE3C,KAAI,OAAO,cAAc,KAAA,EACvB,cAAa,IAAI,aAAa,OAAO,OAAO,UAAU,CAAC;AAEzD,KAAI,OAAO,eAAe,KAAA,EACxB,cAAa,IAAI,cAAc,OAAO,WAAW;AAEnD,KAAI,OAAO,UAAU,KAAA,EACnB,cAAa,IAAI,SAAS,OAAO,OAAO,MAAM,CAAC;AAEjD,KAAI,OAAO,UAAU,KAAA,EACnB,cAAa,IAAI,SAAS,OAAO,OAAO,MAAM,CAAC;AAEjD,KAAI,OAAO,gBAAgB,KAAA,EACzB,cAAa,IAAI,eAAe,OAAO,OAAO,YAAY,CAAC;AAE7D,KAAI,OAAO,mBAAmB,KAAA,EAC5B,cAAa,IAAI,kBAAkB,OAAO,OAAO,eAAe,CAAC;AAEnE,KAAI,OAAO,iBAAiB,KAAA,EAC1B,cAAa,IAAI,gBAAgB,OAAO,OAAO,aAAa,CAAC;AAE/D,KAAI,OAAO,kBAAkB,KAAA,EAC3B,cAAa,IAAI,iBAAiB,OAAO,OAAO,cAAc,CAAC;AAEjE,KAAI,OAAO,kBAAkB,KAAA,EAC3B,cAAa,IAAI,iBAAiB,OAAO,OAAO,cAAc,CAAC;AAEjE,KAAI,OAAO,YAAY,KAAA,EACrB,cAAa,IAAI,WAAW,OAAO,OAAO,QAAQ,CAAC;AAErD,KAAI,OAAO,eAAe,KAAA,EACxB,cAAa,IAAI,cAAc,OAAO,OAAO,WAAW,CAAC;CAG3D,MAAM,cAAc,aAAa,UAAU;AAC3C,QAAO,cAAc,GAAG,SAAS,GAAG,gBAAgB"}
@@ -9109,6 +9109,10 @@ function injectDefaultDomain(meta, clientName, serverUrl) {
9109
9109
  //#region src/mcp/server.ts
9110
9110
  var localDevServerUrl = "http://localhost:8000";
9111
9111
  var localHmrWsUrl = "ws://localhost:24678";
9112
+ var lastToolTimingMs = null;
9113
+ function isDevOverlayEnabled() {
9114
+ return process.env.SUNPEAK_DEV_OVERLAY !== "false";
9115
+ }
9112
9116
  /**
9113
9117
  * Detect whether this request needs pre-built HTML (no Vite HMR).
9114
9118
  *
@@ -9134,6 +9138,57 @@ function readResourceHtmlProd(distPath) {
9134
9138
  return node_fs.default.readFileSync(htmlPath, "utf8");
9135
9139
  }
9136
9140
  /**
9141
+ * Generate an inline script that shows a dev overlay with resource served timestamp
9142
+ * and tool call request timing.
9143
+ *
9144
+ * The resource timestamp is baked into the HTML at readResource time. Tool timing
9145
+ * arrives two ways:
9146
+ * 1. Baked-in `toolMs` from readResource time (works when the tool call precedes the
9147
+ * resource read, which is the case for Claude and the inspector's initial render).
9148
+ * 2. `_meta._sunpeak.requestTimeMs` on the tool result PostMessage (handles inspector
9149
+ * Re-run and hosts that pass `_meta` through to the resource iframe).
9150
+ *
9151
+ * NOTE: Keep in sync with bin/lib/dev-overlay.mjs (used by the standalone inspector).
9152
+ *
9153
+ * @param servedAt - Unix timestamp (ms) when the resource HTML was generated/served.
9154
+ * @param toolMs - Most recent tool call duration (ms), or null if no call yet.
9155
+ */
9156
+ function getDevOverlayScript(servedAt, toolMs) {
9157
+ return `<script>
9158
+ (function(){
9159
+ var servedAt=${servedAt};
9160
+ var el=null,hidden=false,lastMs=${toolMs ?? "null"};
9161
+ function fmt(ts){var d=new Date(ts);var h=d.getHours(),m=d.getMinutes(),s=d.getSeconds();return (h<10?'0':'')+h+':'+(m<10?'0':'')+m+':'+(s<10?'0':'')+s}
9162
+ function make(){
9163
+ var existing=document.getElementById('__sunpeak-dev-timing');
9164
+ if(existing)return existing;
9165
+ var b=document.createElement('button');b.id='__sunpeak-dev-timing';
9166
+ b.style.cssText='position:fixed;bottom:8px;right:8px;z-index:2147483647;display:grid;grid-template-columns:auto auto;gap:0 6px;align-items:baseline;padding:5px 8px;border-radius:6px;border:1px solid rgba(128,128,128,0.25);background:rgba(0,0,0,0.75);backdrop-filter:blur(8px);color:#e5e5e5;font-size:11px;font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;line-height:1.4;cursor:pointer;user-select:none;opacity:0.85;transition:opacity 150ms;';
9167
+ b.onmouseenter=function(){b.style.opacity='1'};
9168
+ b.onmouseleave=function(){b.style.opacity='0.85'};
9169
+ b.onclick=function(){hidden=!hidden;upd()};
9170
+ document.body.appendChild(b);return b;
9171
+ }
9172
+ function upd(){
9173
+ if(!el)el=make();
9174
+ if(hidden){el.title='Show dev info';el.innerHTML='<span style="grid-column:1/-1;font-size:9px;text-align:center">DEV</span>';return}
9175
+ var h='';
9176
+ h+='<span style="text-align:right;color:rgba(255,255,255,0.5);white-space:nowrap">Resource:</span><span style="white-space:nowrap">'+fmt(servedAt)+'</span>';
9177
+ if(lastMs!=null)h+='<span style="text-align:right;color:rgba(255,255,255,0.5);white-space:nowrap">Tool:</span><span style="white-space:nowrap">'+(lastMs%1===0?lastMs:lastMs.toFixed(1))+'ms</span>';
9178
+ el.title='Hide dev info';el.innerHTML=h;
9179
+ }
9180
+ upd();
9181
+ window.addEventListener('message',function(e){
9182
+ var d=e.data;if(!d||typeof d!=='object')return;
9183
+ if(d.method!=='ui/notifications/tool-result')return;
9184
+ var p=d.params;if(!p)return;
9185
+ var ms=p._meta&&p._meta._sunpeak&&p._meta._sunpeak.requestTimeMs;
9186
+ if(typeof ms==='number'){lastMs=ms;upd()}
9187
+ });
9188
+ })();
9189
+ <\/script>`;
9190
+ }
9191
+ /**
9137
9192
  * Generate HTML that loads from Vite dev server with HMR.
9138
9193
  * Used for direct connections (e.g. ChatGPT connecting to localhost).
9139
9194
  */
@@ -9176,6 +9231,7 @@ function getViteResourceHtml(srcPath) {
9176
9231
  src: srcPath,
9177
9232
  component: componentName
9178
9233
  }).toString()}`}"><\/script>
9234
+ ${isDevOverlayEnabled() ? getDevOverlayScript(Date.now(), lastToolTimingMs) : ""}
9179
9235
  </body>
9180
9236
  </html>`;
9181
9237
  }
@@ -9187,7 +9243,9 @@ function getViteResourceHtml(srcPath) {
9187
9243
  */
9188
9244
  function getResourceHtml(simulation, viteMode, prodBuild) {
9189
9245
  if (viteMode && simulation.srcPath && !prodBuild) return getViteResourceHtml(simulation.srcPath);
9190
- return readResourceHtmlProd(simulation.distPath);
9246
+ let html = readResourceHtmlProd(simulation.distPath);
9247
+ if (isDevOverlayEnabled()) html = html.replace("</body>", `${getDevOverlayScript(Date.now(), lastToolTimingMs)}\n</body>`);
9248
+ return html;
9191
9249
  }
9192
9250
  /**
9193
9251
  * Inject localhost Vite dev server URLs into CSP metadata.
@@ -9308,14 +9366,29 @@ function createAppServer(config, simulations, viteMode) {
9308
9366
  const realHandler = simulation.handler;
9309
9367
  if (realHandler && (config.prodTools || !hasMockResult)) {
9310
9368
  console.log(`[MCP] CallTool: ${tool.name}${argsStr} → live handler`);
9369
+ const startTime = performance.now();
9311
9370
  try {
9312
9371
  const result = await realHandler(args, extra);
9313
- if (typeof result === "string") return { content: [{
9314
- type: "text",
9315
- text: result
9316
- }] };
9317
- return result;
9372
+ const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
9373
+ lastToolTimingMs = durationMs;
9374
+ if (typeof result === "string") return {
9375
+ content: [{
9376
+ type: "text",
9377
+ text: result
9378
+ }],
9379
+ _meta: { _sunpeak: { requestTimeMs: durationMs } }
9380
+ };
9381
+ const typed = result;
9382
+ return {
9383
+ ...typed,
9384
+ _meta: {
9385
+ ...typed._meta,
9386
+ _sunpeak: { requestTimeMs: durationMs }
9387
+ }
9388
+ };
9318
9389
  } catch (error) {
9390
+ const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
9391
+ lastToolTimingMs = durationMs;
9319
9392
  const msg = error instanceof Error ? error.message : String(error);
9320
9393
  console.error(`[MCP] CallTool error (${tool.name}): ${msg}`);
9321
9394
  return {
@@ -9323,7 +9396,8 @@ function createAppServer(config, simulations, viteMode) {
9323
9396
  type: "text",
9324
9397
  text: `Error: ${msg}`
9325
9398
  }],
9326
- isError: true
9399
+ isError: true,
9400
+ _meta: { _sunpeak: { requestTimeMs: durationMs } }
9327
9401
  };
9328
9402
  }
9329
9403
  }