sunpeak 0.17.4 → 0.17.6

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 (48) hide show
  1. package/bin/commands/dev.mjs +4 -3
  2. package/bin/commands/inspect.mjs +58 -33
  3. package/dist/chatgpt/globals.css +5 -0
  4. package/dist/chatgpt/index.cjs +2 -2
  5. package/dist/chatgpt/index.js +2 -2
  6. package/dist/claude/index.cjs +1 -1
  7. package/dist/claude/index.js +1 -1
  8. package/dist/index.cjs +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/mcp/index.cjs +1 -1
  11. package/dist/mcp/index.cjs.map +1 -1
  12. package/dist/mcp/index.js +1 -1
  13. package/dist/mcp/index.js.map +1 -1
  14. package/dist/mcp/types.d.ts +7 -0
  15. package/dist/simulator/index.cjs +2 -2
  16. package/dist/simulator/index.cjs.map +1 -1
  17. package/dist/simulator/index.js +2 -2
  18. package/dist/simulator/index.js.map +1 -1
  19. package/dist/simulator/simulator-url.d.ts +32 -41
  20. package/dist/simulator/simulator.d.ts +14 -10
  21. package/dist/simulator/use-mcp-connection.d.ts +12 -7
  22. package/dist/simulator/use-simulator-state.d.ts +1 -1
  23. package/dist/{simulator-Dl8B-Ljb.js → simulator-BijjlOXb.js} +278 -143
  24. package/dist/simulator-BijjlOXb.js.map +1 -0
  25. package/dist/{simulator-CH9hs0N6.cjs → simulator-DqWETA_1.cjs} +278 -143
  26. package/dist/simulator-DqWETA_1.cjs.map +1 -0
  27. package/dist/{simulator-url-CozKF1jf.cjs → simulator-url-3ATCsPOT.cjs} +11 -30
  28. package/dist/simulator-url-3ATCsPOT.cjs.map +1 -0
  29. package/dist/{simulator-url-KoS_ToP6.js → simulator-url-BbuuWa7S.js} +11 -30
  30. package/dist/simulator-url-BbuuWa7S.js.map +1 -0
  31. package/dist/style.css +5 -0
  32. package/package.json +1 -1
  33. package/template/dist/albums/albums.html +1 -1
  34. package/template/dist/albums/albums.json +1 -1
  35. package/template/dist/carousel/carousel.html +1 -1
  36. package/template/dist/carousel/carousel.json +1 -1
  37. package/template/dist/map/map.html +1 -1
  38. package/template/dist/map/map.json +1 -1
  39. package/template/dist/review/review.html +1 -1
  40. package/template/dist/review/review.json +1 -1
  41. package/template/tests/e2e/albums.spec.ts +3 -9
  42. package/template/tests/e2e/carousel.spec.ts +3 -9
  43. package/template/tests/e2e/map.spec.ts +3 -9
  44. package/template/tests/e2e/review.spec.ts +3 -9
  45. package/dist/simulator-CH9hs0N6.cjs.map +0 -1
  46. package/dist/simulator-Dl8B-Ljb.js.map +0 -1
  47. package/dist/simulator-url-CozKF1jf.cjs.map +0 -1
  48. package/dist/simulator-url-KoS_ToP6.js.map +0 -1
@@ -2185,13 +2185,14 @@ var DEFAULT_PLATFORM = "desktop";
2185
2185
  * - touch: 'true' | 'false'
2186
2186
  * - safeAreaTop, safeAreaBottom, safeAreaLeft, safeAreaRight: number
2187
2187
  * - host: 'chatgpt' | 'claude'
2188
- * - prodTools: 'true' | 'false'
2188
+ * - tool: tool name (e.g., 'show-albums') selects tool without mock data
2189
2189
  * - prodResources: 'true' | 'false'
2190
2190
  */
2191
2191
  function parseUrlParams() {
2192
2192
  if (typeof window === "undefined") return {};
2193
2193
  const params = new URLSearchParams(window.location.search);
2194
2194
  const simulation = params.get("simulation") ?? void 0;
2195
+ const tool = params.get("tool") ?? void 0;
2195
2196
  const theme = params.get("theme");
2196
2197
  const displayMode = params.get("displayMode");
2197
2198
  const locale = params.get("locale");
@@ -2200,8 +2201,6 @@ function parseUrlParams() {
2200
2201
  const maxWidthParam = params.get("maxWidth");
2201
2202
  const containerMaxWidth = maxWidthParam ? Number(maxWidthParam) : void 0;
2202
2203
  const host = params.get("host") ?? void 0;
2203
- const prodToolsParam = params.get("prodTools");
2204
- const prodTools = prodToolsParam === "true" ? true : prodToolsParam === "false" ? false : void 0;
2205
2204
  const prodResourcesParam = params.get("prodResources");
2206
2205
  const prodResources = prodResourcesParam === "true" ? true : prodResourcesParam === "false" ? false : void 0;
2207
2206
  const deviceType = params.get("deviceType");
@@ -2226,6 +2225,7 @@ function parseUrlParams() {
2226
2225
  } : void 0;
2227
2226
  return {
2228
2227
  simulation,
2228
+ tool,
2229
2229
  theme: theme ?? void 0,
2230
2230
  displayMode: displayMode ?? void 0,
2231
2231
  locale: locale ?? void 0,
@@ -2235,7 +2235,6 @@ function parseUrlParams() {
2235
2235
  deviceCapabilities,
2236
2236
  safeAreaInsets,
2237
2237
  host: host ?? void 0,
2238
- prodTools,
2239
2238
  prodResources
2240
2239
  };
2241
2240
  }
@@ -2486,7 +2485,7 @@ function useSimulatorState({ simulations, defaultHost = "chatgpt" }) {
2486
2485
  csp,
2487
2486
  permissions: resourceMeta?.permissions,
2488
2487
  prefersBorder: resourceMeta?.prefersBorder ?? false,
2489
- urlProdTools: urlParams.prodTools,
2488
+ urlTool: urlParams.tool,
2490
2489
  urlProdResources: urlParams.prodResources
2491
2490
  };
2492
2491
  }
@@ -2495,17 +2494,21 @@ function useSimulatorState({ simulations, defaultHost = "chatgpt" }) {
2495
2494
  /**
2496
2495
  * Hook for managing MCP server connection status via the dev server proxy.
2497
2496
  *
2498
- * On mount (when `serverUrl` is provided), verifies the connection is alive
2499
- * by fetching `/__sunpeak/list-tools`. Tracks connection status for display
2500
- * in the sidebar (colored dot indicator).
2497
+ * On mount (when `initialServerUrl` is provided), verifies the connection is alive
2498
+ * by fetching `/__sunpeak/list-tools`. URL changes are handled by the caller
2499
+ * via `reconnect()`, which posts to `/__sunpeak/connect`.
2501
2500
  *
2502
- * Tool calling is handled separately via the `onCallTool` prop this
2503
- * hook only manages the connection lifecycle and status display.
2501
+ * This split avoids React StrictMode issues: the mount-only health check runs
2502
+ * once (or safely twice with cancellation), while explicit `reconnect()` calls
2503
+ * are triggered by the Simulator's URL-change effect.
2504
2504
  */
2505
- function useMcpConnection(serverUrl) {
2506
- const [status, setStatus] = useState(serverUrl ? "connecting" : "disconnected");
2505
+ function useMcpConnection(initialServerUrl) {
2506
+ const [status, setStatus] = useState(initialServerUrl ? "connecting" : "disconnected");
2507
2507
  const [error, setError] = useState();
2508
+ const [simulations, setSimulations] = useState();
2509
+ const [hasReconnected, setHasReconnected] = useState(false);
2508
2510
  const reconnect = useCallback(async (url) => {
2511
+ setHasReconnected(true);
2509
2512
  setStatus("connecting");
2510
2513
  setError(void 0);
2511
2514
  try {
@@ -2515,23 +2518,27 @@ function useMcpConnection(serverUrl) {
2515
2518
  body: JSON.stringify({ url })
2516
2519
  });
2517
2520
  if (!res.ok) {
2518
- const text = await res.text();
2519
- throw new Error(text || `Connection failed (${res.status})`);
2521
+ let message = `Connection failed (${res.status})`;
2522
+ try {
2523
+ const json = await res.json();
2524
+ if (json.error) message = json.error;
2525
+ } catch {}
2526
+ throw new Error(message);
2520
2527
  }
2528
+ const data = await res.json();
2521
2529
  setStatus("connected");
2530
+ setSimulations(data.simulations ?? void 0);
2522
2531
  } catch (err) {
2523
2532
  setError(err instanceof Error ? err.message : String(err));
2524
2533
  setStatus("error");
2534
+ setSimulations(void 0);
2525
2535
  }
2526
2536
  }, []);
2527
2537
  useEffect(() => {
2528
- if (!serverUrl) {
2529
- setStatus("disconnected");
2530
- return;
2531
- }
2538
+ if (!initialServerUrl) return;
2532
2539
  let cancelled = false;
2540
+ setStatus("connecting");
2533
2541
  (async () => {
2534
- setStatus("connecting");
2535
2542
  try {
2536
2543
  const res = await fetch("/__sunpeak/list-tools");
2537
2544
  if (cancelled) return;
@@ -2546,10 +2553,12 @@ function useMcpConnection(serverUrl) {
2546
2553
  return () => {
2547
2554
  cancelled = true;
2548
2555
  };
2549
- }, [serverUrl]);
2556
+ }, []);
2550
2557
  return {
2551
2558
  status,
2552
2559
  error,
2560
+ simulations,
2561
+ hasReconnected,
2553
2562
  reconnect
2554
2563
  };
2555
2564
  }
@@ -2703,7 +2712,7 @@ function SimpleSidebar({ children, controls, headerRight }) {
2703
2712
  ]
2704
2713
  });
2705
2714
  }
2706
- var DOCS_BASE_URL = "https://sunpeak.ai/docs";
2715
+ var DOCS_BASE_URL$1 = "https://sunpeak.ai/docs";
2707
2716
  function HelpIcon({ tooltip, docsPath }) {
2708
2717
  const [pos, setPos] = React.useState(null);
2709
2718
  const ref = React.useRef(null);
@@ -2716,7 +2725,7 @@ function HelpIcon({ tooltip, docsPath }) {
2716
2725
  };
2717
2726
  return /* @__PURE__ */ jsxs("a", {
2718
2727
  ref,
2719
- href: `${DOCS_BASE_URL}/${docsPath}`,
2728
+ href: `${DOCS_BASE_URL$1}/${docsPath}`,
2720
2729
  target: "_blank",
2721
2730
  rel: "noopener noreferrer",
2722
2731
  className: "inline-flex items-center justify-center no-underline flex-shrink-0 transition-colors",
@@ -2838,6 +2847,13 @@ function SidebarInput({ value, onChange, applyOnBlur = false, placeholder, type
2838
2847
  setIsEditing(false);
2839
2848
  onChange(draft);
2840
2849
  },
2850
+ onKeyDown: (e) => {
2851
+ if (e.key === "Enter") {
2852
+ setIsEditing(false);
2853
+ onChange(draft);
2854
+ e.target.blur();
2855
+ }
2856
+ },
2841
2857
  placeholder,
2842
2858
  disabled,
2843
2859
  className: "w-full h-7 text-xs rounded-md px-2 outline-none disabled:opacity-50 disabled:cursor-not-allowed",
@@ -2948,54 +2964,142 @@ function resolveServerToolResult(mock, args) {
2948
2964
  }
2949
2965
  //#endregion
2950
2966
  //#region src/simulator/simulator.tsx
2951
- function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, defaultHost = "chatgpt", onCallTool, onCallToolDirect, defaultProdTools = false, defaultProdResources = false, hideSimulatorModes = false, sandboxUrl, mcpServerUrl }) {
2952
- const isInspectMode = mcpServerUrl != null;
2967
+ var DOCS_BASE_URL = "https://sunpeak.ai/docs";
2968
+ /** Check whether a simulation has user-authored fixture data. */
2969
+ function hasFixtureData(sim) {
2970
+ return sim.toolResult != null || sim.toolInput != null || sim.serverTools != null;
2971
+ }
2972
+ function Simulator({ children, simulations: initialSimulations = {}, appName = "Sunpeak", appIcon, defaultHost = "chatgpt", onCallTool, onCallToolDirect, defaultProdResources = false, hideSimulatorModes = false, demoMode = false, sandboxUrl, mcpServerUrl }) {
2973
+ const [simulations, setSimulations] = React.useState(initialSimulations);
2974
+ React.useEffect(() => {
2975
+ setSimulations(initialSimulations);
2976
+ }, [initialSimulations]);
2977
+ const toolMap = React.useMemo(() => {
2978
+ const map = /* @__PURE__ */ new Map();
2979
+ for (const [simName, sim] of Object.entries(simulations)) {
2980
+ if (!sim.resource) continue;
2981
+ const toolName = sim.tool.name;
2982
+ if (!map.has(toolName)) map.set(toolName, {
2983
+ tool: sim.tool,
2984
+ resource: sim.resource,
2985
+ simNames: [],
2986
+ fixtureSimNames: []
2987
+ });
2988
+ const info = map.get(toolName);
2989
+ info.simNames.push(simName);
2990
+ if (hasFixtureData(sim)) info.fixtureSimNames.push(simName);
2991
+ }
2992
+ return map;
2993
+ }, [simulations]);
2994
+ const toolNames = React.useMemo(() => Array.from(toolMap.keys()).sort((a, b) => {
2995
+ const infoA = toolMap.get(a);
2996
+ const infoB = toolMap.get(b);
2997
+ const labelA = infoA.tool.title || a;
2998
+ const labelB = infoB.tool.title || b;
2999
+ return labelA.localeCompare(labelB);
3000
+ }), [toolMap]);
3001
+ const initUrlParams = React.useMemo(() => {
3002
+ if (typeof window === "undefined") return {
3003
+ tool: null,
3004
+ simulation: null,
3005
+ noMockData: false
3006
+ };
3007
+ const params = new URLSearchParams(window.location.search);
3008
+ const prodTools = params.get("prodTools") === "true";
3009
+ return {
3010
+ tool: params.get("tool"),
3011
+ simulation: params.get("simulation"),
3012
+ noMockData: prodTools
3013
+ };
3014
+ }, []);
3015
+ const [selectedToolName, setSelectedToolName] = React.useState(() => {
3016
+ if (initUrlParams.tool && toolMap.has(initUrlParams.tool)) return initUrlParams.tool;
3017
+ if (initUrlParams.simulation) {
3018
+ for (const [toolName, info] of toolMap) if (info.simNames.includes(initUrlParams.simulation)) return toolName;
3019
+ }
3020
+ return toolNames[0] ?? "";
3021
+ });
3022
+ const prevToolNamesRef = React.useRef(toolNames);
3023
+ if (prevToolNamesRef.current !== toolNames) {
3024
+ prevToolNamesRef.current = toolNames;
3025
+ if (toolNames.length > 0 && !toolMap.has(selectedToolName)) setSelectedToolName(toolNames[0]);
3026
+ }
3027
+ const selectedToolInfo = toolMap.get(selectedToolName);
3028
+ const [activeSimulationName, setActiveSimulationName] = React.useState(() => {
3029
+ if (!selectedToolInfo) return null;
3030
+ if (initUrlParams.noMockData) return null;
3031
+ if (initUrlParams.tool && !initUrlParams.simulation) return null;
3032
+ if (initUrlParams.simulation && selectedToolInfo.fixtureSimNames.includes(initUrlParams.simulation)) return initUrlParams.simulation;
3033
+ return selectedToolInfo.fixtureSimNames[0] ?? null;
3034
+ });
3035
+ const prevToolNameRef = React.useRef(selectedToolName);
3036
+ if (prevToolNameRef.current !== selectedToolName) {
3037
+ prevToolNameRef.current = selectedToolName;
3038
+ setActiveSimulationName(toolMap.get(selectedToolName)?.fixtureSimNames[0] ?? null);
3039
+ }
3040
+ const effectiveSimulationName = activeSimulationName ?? selectedToolInfo?.simNames[0] ?? "";
3041
+ const currentSim = simulations[effectiveSimulationName];
2953
3042
  const state = useSimulatorState({
2954
3043
  simulations,
2955
3044
  defaultHost
2956
3045
  });
2957
- const connection = useMcpConnection(mcpServerUrl);
2958
- const [prodTools, setProdTools] = React.useState(isInspectMode ? true : state.urlProdTools ?? defaultProdTools);
3046
+ const [serverUrl, setServerUrl] = React.useState(mcpServerUrl ?? "");
3047
+ const connection = useMcpConnection(mcpServerUrl || void 0);
2959
3048
  const [prodResources, setProdResources] = React.useState(state.urlProdResources ?? defaultProdResources);
2960
3049
  const [isRunning, setIsRunning] = React.useState(false);
2961
3050
  const [hasRun, setHasRun] = React.useState(false);
2962
3051
  const [showCheck, setShowCheck] = React.useState(false);
2963
3052
  const checkTimerRef = React.useRef(void 0);
2964
3053
  React.useEffect(() => {
2965
- if (prodTools) {
2966
- setHasRun(false);
2967
- state.setToolResult(void 0);
2968
- state.setToolResultJson("");
2969
- state.setToolResultError("");
3054
+ state.setSelectedSimulationName(effectiveSimulationName);
3055
+ }, [effectiveSimulationName]);
3056
+ const prevServerUrlRef = React.useRef(serverUrl);
3057
+ React.useEffect(() => {
3058
+ const urlChanged = serverUrl !== prevServerUrlRef.current;
3059
+ prevServerUrlRef.current = serverUrl;
3060
+ if (!urlChanged) return;
3061
+ if (serverUrl) connection.reconnect(serverUrl);
3062
+ }, [serverUrl, connection.reconnect]);
3063
+ React.useEffect(() => {
3064
+ if (connection.simulations) setSimulations(connection.simulations);
3065
+ else if (connection.status === "error" && connection.hasReconnected) setSimulations({});
3066
+ }, [
3067
+ connection.simulations,
3068
+ connection.status,
3069
+ connection.hasReconnected
3070
+ ]);
3071
+ const { setToolResult, setToolResultJson, setToolResultError } = state;
3072
+ React.useEffect(() => {
3073
+ if (activeSimulationName === null) {
3074
+ setToolResult(void 0);
3075
+ setToolResultJson("");
3076
+ setToolResultError("");
2970
3077
  } else {
2971
- const simResult = state.selectedSim?.toolResult ?? void 0;
2972
- state.setToolResult(simResult);
2973
- }
2974
- }, [prodTools, state.selectedSimulationName]);
2975
- React.useEffect(() => () => clearTimeout(checkTimerRef.current), []);
2976
- const toolOptions = React.useMemo(() => {
2977
- if (!prodTools) return [];
2978
- const seen = /* @__PURE__ */ new Map();
2979
- for (const simName of state.simulationNames) {
2980
- const toolName = simulations[simName].tool.name;
2981
- if (!seen.has(toolName)) seen.set(toolName, simName);
3078
+ const result = simulations[activeSimulationName]?.toolResult ?? void 0;
3079
+ setToolResult(result);
3080
+ setToolResultJson(result ? JSON.stringify(result, null, 2) : "");
3081
+ setToolResultError("");
2982
3082
  }
2983
- return Array.from(seen.entries()).map(([toolName, simName]) => ({
2984
- value: simName,
2985
- label: simulations[simName].tool.title || toolName
2986
- }));
2987
3083
  }, [
2988
- prodTools,
2989
- state.simulationNames,
2990
- simulations
3084
+ activeSimulationName,
3085
+ effectiveSimulationName,
3086
+ simulations,
3087
+ setToolResult,
3088
+ setToolResultJson,
3089
+ setToolResultError
2991
3090
  ]);
3091
+ React.useEffect(() => {
3092
+ setHasRun(false);
3093
+ }, [effectiveSimulationName]);
3094
+ React.useEffect(() => () => clearTimeout(checkTimerRef.current), []);
2992
3095
  const handleRun = React.useCallback(async () => {
2993
3096
  const caller = onCallToolDirect ?? onCallTool;
2994
- if (!caller || !state.selectedSim) return;
2995
- const toolName = state.selectedSim.tool.name;
3097
+ const sim = simulations[effectiveSimulationName];
3098
+ if (!caller || !sim) return;
3099
+ const toolName = sim.tool.name;
2996
3100
  setIsRunning(true);
2997
3101
  try {
2998
- const result = (isInspectMode ? state.selectedSim.toolResult : void 0) ?? await caller({
3102
+ const result = await caller({
2999
3103
  name: toolName,
3000
3104
  arguments: state.toolInput
3001
3105
  });
@@ -3022,14 +3126,16 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3022
3126
  }],
3023
3127
  isError: true
3024
3128
  }, null, 2));
3129
+ setHasRun(true);
3025
3130
  } finally {
3026
3131
  setIsRunning(false);
3027
3132
  }
3028
3133
  }, [
3029
3134
  onCallTool,
3030
3135
  onCallToolDirect,
3031
- state,
3032
- isInspectMode
3136
+ simulations,
3137
+ effectiveSimulationName,
3138
+ state
3033
3139
  ]);
3034
3140
  const activeShell = getHostShell(state.activeHost);
3035
3141
  const registeredHosts = getRegisteredHosts();
@@ -3063,8 +3169,8 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3063
3169
  } else prevPageStyleKeysRef.current = [];
3064
3170
  }, [activeShell]);
3065
3171
  const handleCallTool = React.useCallback((params) => {
3066
- if (!prodTools) {
3067
- const mock = state.selectedSim?.serverTools?.[params.name];
3172
+ if (activeSimulationName) {
3173
+ const mock = simulations[activeSimulationName]?.serverTools?.[params.name];
3068
3174
  if (mock) {
3069
3175
  const result = resolveServerToolResult(mock, params.arguments);
3070
3176
  if (result) return result;
@@ -3073,15 +3179,15 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3073
3179
  if (onCallTool) return onCallTool(params);
3074
3180
  return { content: [{
3075
3181
  type: "text",
3076
- text: `[Simulator] Tool "${params.name}" called — no serverTools mock found in simulation "${state.selectedSimulationName}".`
3182
+ text: `[Simulator] Tool "${params.name}" called — no serverTools mock found in simulation "${effectiveSimulationName}".`
3077
3183
  }] };
3078
3184
  }, [
3079
3185
  onCallTool,
3080
- prodTools,
3081
- state.selectedSim,
3082
- state.selectedSimulationName
3186
+ activeSimulationName,
3187
+ simulations,
3188
+ effectiveSimulationName
3083
3189
  ]);
3084
- const prodToolsUserMessage = prodTools && state.selectedSim ? `Call my ${state.selectedSim.tool.title || state.selectedSim.tool.name} tool` : void 0;
3190
+ const userMessage = currentSim ? currentSim.userMessage ?? `Call my ${currentSim.tool.title || currentSim.tool.name} tool` : void 0;
3085
3191
  const prodResourcesPath = React.useMemo(() => {
3086
3192
  if (!prodResources || !state.selectedSim?.resource) return void 0;
3087
3193
  const name = state.selectedSim.resource.name;
@@ -3122,10 +3228,23 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3122
3228
  }, [prodResourcesPath]);
3123
3229
  const effectiveResourceUrl = (prodResourcesPath && prodResourcesReady ? prodResourcesPath : void 0) ?? state.resourceUrl;
3124
3230
  const prodResourcesLoading = !!prodResourcesPath && !prodResourcesReady;
3125
- const showEmptyState = prodTools && !hasRun;
3231
+ const hasTools = toolNames.length > 0;
3232
+ const showEmptyState = !(activeSimulationName !== null && currentSim?.toolResult != null) && !hasRun;
3126
3233
  let content;
3127
3234
  const iframeBg = "var(--sim-bg-conversation, var(--color-background-primary, transparent))";
3128
- if (showEmptyState) content = /* @__PURE__ */ jsx("div", {
3235
+ if (!hasTools) {
3236
+ const isConnected = connection.status === "connected";
3237
+ const isError = connection.status === "error";
3238
+ content = /* @__PURE__ */ jsx("div", {
3239
+ className: "h-full w-full flex items-center justify-center",
3240
+ style: { background: iframeBg },
3241
+ children: /* @__PURE__ */ jsx("span", {
3242
+ className: "text-sm text-center max-w-xs",
3243
+ style: { color: "var(--color-text-secondary)" },
3244
+ children: isError ? "Could not connect to MCP server" : isConnected ? "No tools with UI resources found on this server" : serverUrl ? "Connecting…" : "Enter an MCP server URL to get started"
3245
+ })
3246
+ });
3247
+ } else if (showEmptyState) content = /* @__PURE__ */ jsx("div", {
3129
3248
  className: "h-full w-full flex items-center justify-center",
3130
3249
  style: { background: iframeBg },
3131
3250
  children: /* @__PURE__ */ jsxs("span", {
@@ -3169,7 +3288,7 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3169
3288
  injectOpenAIRuntime: state.activeHost === "chatgpt",
3170
3289
  sandboxUrl,
3171
3290
  className: "h-full w-full"
3172
- }, `${state.activeHost}-${state.selectedSimulationName}-${prodResources}-${prodResourcesGeneration}`)
3291
+ }, `${state.activeHost}-${effectiveResourceUrl}-${prodResources}-${prodResourcesGeneration}`)
3173
3292
  });
3174
3293
  else if (!prodResources && state.resourceScript) content = /* @__PURE__ */ jsx("div", {
3175
3294
  className: "h-full w-full",
@@ -3194,10 +3313,37 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3194
3313
  injectOpenAIRuntime: state.activeHost === "chatgpt",
3195
3314
  sandboxUrl,
3196
3315
  className: "h-full w-full"
3197
- }, `${state.activeHost}-${state.selectedSimulationName}`)
3316
+ }, `${state.activeHost}-${state.resourceScript}`)
3198
3317
  });
3199
3318
  else content = children;
3200
3319
  const applyTheme = activeShell?.applyTheme;
3320
+ const runButton = !demoMode && onCallTool && currentSim && activeSimulationName === null ? /* @__PURE__ */ jsxs("button", {
3321
+ type: "button",
3322
+ onClick: handleRun,
3323
+ disabled: isRunning,
3324
+ className: "rounded-full px-3 py-1 text-sm font-medium transition-opacity disabled:opacity-40 flex items-center gap-1.5 cursor-pointer",
3325
+ style: {
3326
+ backgroundColor: "var(--color-text-primary)",
3327
+ color: "var(--color-background-primary)"
3328
+ },
3329
+ children: [showCheck ? /* @__PURE__ */ jsx("svg", {
3330
+ width: "12",
3331
+ height: "12",
3332
+ viewBox: "0 0 12 12",
3333
+ fill: "none",
3334
+ stroke: "currentColor",
3335
+ strokeWidth: "2",
3336
+ strokeLinecap: "round",
3337
+ strokeLinejoin: "round",
3338
+ children: /* @__PURE__ */ jsx("path", { d: "M2 6L5 9L10 3" })
3339
+ }) : /* @__PURE__ */ jsx("svg", {
3340
+ width: "10",
3341
+ height: "12",
3342
+ viewBox: "0 0 10 12",
3343
+ fill: "currentColor",
3344
+ children: /* @__PURE__ */ jsx("path", { d: "M0 0L10 6L0 12V0Z" })
3345
+ }), "Run"]
3346
+ }) : void 0;
3201
3347
  return /* @__PURE__ */ jsx(ThemeProvider, {
3202
3348
  theme: state.theme,
3203
3349
  applyTheme,
@@ -3205,39 +3351,82 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3205
3351
  controls: /* @__PURE__ */ jsxs("div", {
3206
3352
  className: "space-y-1",
3207
3353
  children: [
3208
- isInspectMode && /* @__PURE__ */ jsx(SidebarControl, {
3354
+ /* @__PURE__ */ jsx(SidebarControl, {
3209
3355
  label: /* @__PURE__ */ jsxs("span", {
3210
3356
  className: "flex items-center gap-1.5",
3211
- children: ["MCP Server", /* @__PURE__ */ jsx("span", {
3357
+ children: ["MCP Server", serverUrl && !demoMode && /* @__PURE__ */ jsx("span", {
3212
3358
  className: "inline-block w-2 h-2 rounded-full",
3213
- "data-testid": "inspect-connection-status",
3359
+ "data-testid": "connection-status",
3214
3360
  style: { backgroundColor: connection.status === "connected" ? "#22c55e" : connection.status === "connecting" ? "#eab308" : connection.status === "error" ? "#ef4444" : "#6b7280" },
3215
3361
  title: connection.error ?? connection.status
3216
3362
  })]
3217
3363
  }),
3218
- tooltip: "MCP server URL (set via --server flag)",
3219
- "data-testid": "inspect-server-url",
3364
+ tooltip: "MCP server URL",
3365
+ "data-testid": "server-url",
3220
3366
  children: /* @__PURE__ */ jsx(SidebarInput, {
3221
- value: mcpServerUrl ?? "",
3222
- onChange: () => {},
3223
- disabled: true,
3224
- placeholder: "http://localhost:8000/mcp"
3367
+ value: demoMode ? "http://localhost:8000/mcp" : serverUrl,
3368
+ onChange: demoMode ? () => {} : setServerUrl,
3369
+ applyOnBlur: true,
3370
+ placeholder: "http://localhost:8000/mcp",
3371
+ disabled: demoMode
3225
3372
  })
3226
3373
  }),
3227
- !isInspectMode && !hideSimulatorModes && onCallTool && /* @__PURE__ */ jsx(SidebarCheckbox, {
3228
- checked: prodTools,
3229
- onChange: setProdTools,
3230
- label: "Prod Tools",
3231
- tooltip: "Use real tool handlers instead of simulations",
3232
- docsPath: "api-reference/cli/dev#prod-tools-and-prod-resources-flags"
3233
- }),
3234
- !isInspectMode && !hideSimulatorModes && /* @__PURE__ */ jsx(SidebarCheckbox, {
3374
+ !hideSimulatorModes && !demoMode && /* @__PURE__ */ jsx(SidebarCheckbox, {
3235
3375
  checked: prodResources,
3236
3376
  onChange: setProdResources,
3237
3377
  label: "Prod Resources",
3238
3378
  tooltip: "Load resources from dist/ builds instead of HMR",
3239
3379
  docsPath: "api-reference/cli/dev#prod-tools-and-prod-resources-flags"
3240
3380
  }),
3381
+ hasTools && /* @__PURE__ */ jsxs("div", {
3382
+ className: "grid grid-cols-2 gap-2",
3383
+ "data-testid": "tool-simulation-row",
3384
+ children: [/* @__PURE__ */ jsx(SidebarControl, {
3385
+ label: "Tool",
3386
+ tooltip: "Tool to inspect",
3387
+ docsPath: "api-reference/cli/dev",
3388
+ "data-testid": "tool-selector",
3389
+ children: /* @__PURE__ */ jsx(SidebarSelect, {
3390
+ value: selectedToolName,
3391
+ onChange: (value) => setSelectedToolName(value),
3392
+ options: toolNames.map((name) => {
3393
+ return {
3394
+ value: name,
3395
+ label: toolMap.get(name).tool.title || name
3396
+ };
3397
+ })
3398
+ })
3399
+ }), /* @__PURE__ */ jsx(SidebarControl, {
3400
+ label: selectedToolInfo && selectedToolInfo.fixtureSimNames.length > 0 ? "Simulation" : /* @__PURE__ */ jsx("a", {
3401
+ href: `${DOCS_BASE_URL}/api-reference/simulations/simulation`,
3402
+ target: "_blank",
3403
+ rel: "noopener noreferrer",
3404
+ className: "no-underline transition-colors",
3405
+ style: { color: "var(--color-text-secondary)" },
3406
+ onMouseEnter: (e) => {
3407
+ e.target.style.color = "var(--color-text-primary)";
3408
+ },
3409
+ onMouseLeave: (e) => {
3410
+ e.target.style.color = "var(--color-text-secondary)";
3411
+ },
3412
+ children: "Simulation"
3413
+ }),
3414
+ tooltip: selectedToolInfo && selectedToolInfo.fixtureSimNames.length > 0 ? "Test fixture with mock data" : "Create simulations for faster testing",
3415
+ docsPath: "api-reference/simulations/simulation",
3416
+ "data-testid": "simulation-selector",
3417
+ children: /* @__PURE__ */ jsx(SidebarSelect, {
3418
+ value: activeSimulationName ?? "__none__",
3419
+ onChange: (value) => setActiveSimulationName(value === "__none__" ? null : value),
3420
+ options: [...demoMode ? [] : [{
3421
+ value: "__none__",
3422
+ label: selectedToolInfo && selectedToolInfo.fixtureSimNames.length > 0 ? "None (call server)" : "None"
3423
+ }], ...(selectedToolInfo?.fixtureSimNames ?? []).map((simName) => ({
3424
+ value: simName,
3425
+ label: simName
3426
+ }))]
3427
+ })
3428
+ })]
3429
+ }),
3241
3430
  /* @__PURE__ */ jsxs("div", {
3242
3431
  className: "grid grid-cols-2 gap-2",
3243
3432
  children: [registeredHosts.length > 1 && /* @__PURE__ */ jsx(SidebarControl, {
@@ -3280,34 +3469,6 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3280
3469
  })
3281
3470
  })]
3282
3471
  }),
3283
- prodTools && toolOptions.length > 1 && /* @__PURE__ */ jsx(SidebarControl, {
3284
- label: "Tool",
3285
- tooltip: "Tool to call with prod handler",
3286
- docsPath: "api-reference/cli/dev",
3287
- children: /* @__PURE__ */ jsx(SidebarSelect, {
3288
- value: state.selectedSimulationName,
3289
- onChange: (value) => state.setSelectedSimulationName(value),
3290
- options: toolOptions
3291
- })
3292
- }),
3293
- !prodTools && state.simulationNames.length > 1 && /* @__PURE__ */ jsx(SidebarControl, {
3294
- label: "Simulation",
3295
- tooltip: "Test fixture to render",
3296
- docsPath: "api-reference/simulations/simulation",
3297
- children: /* @__PURE__ */ jsx(SidebarSelect, {
3298
- value: state.selectedSimulationName,
3299
- onChange: (value) => state.setSelectedSimulationName(value),
3300
- options: state.simulationNames.map((name) => {
3301
- const sim = simulations[name];
3302
- const resourceTitle = sim.resource ? sim.resource.title || sim.resource.name : void 0;
3303
- const toolTitle = sim.tool.title || sim.tool.name;
3304
- return {
3305
- value: name,
3306
- label: resourceTitle ? `${resourceTitle}: ${toolTitle}` : toolTitle
3307
- };
3308
- })
3309
- })
3310
- }),
3311
3472
  /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
3312
3473
  label: "Host Context",
3313
3474
  defaultCollapsed: false,
@@ -3589,7 +3750,7 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3589
3750
  }),
3590
3751
  /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
3591
3752
  label: "Tool Input (JSON)",
3592
- defaultCollapsed: !prodTools,
3753
+ defaultCollapsed: false,
3593
3754
  tooltip: "Arguments passed to the tool",
3594
3755
  docsPath: "api-reference/hooks/use-tool-data",
3595
3756
  children: /* @__PURE__ */ jsx(SidebarTextarea, {
@@ -3600,10 +3761,10 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3600
3761
  error: state.toolInputError,
3601
3762
  maxRows: 8
3602
3763
  })
3603
- }, `tool-input-${prodTools}`),
3764
+ }),
3604
3765
  /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
3605
3766
  label: "Tool Result (JSON)",
3606
- defaultCollapsed: prodTools,
3767
+ defaultCollapsed: false,
3607
3768
  tooltip: "Structured content returned by the tool",
3608
3769
  docsPath: "api-reference/hooks/use-tool-data",
3609
3770
  "data-testid": "tool-result-section",
@@ -3626,7 +3787,7 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3626
3787
  error: state.toolResultError,
3627
3788
  maxRows: 8
3628
3789
  })
3629
- }, `tool-result-${prodTools}`)
3790
+ })
3630
3791
  ]
3631
3792
  }),
3632
3793
  children: ShellConversation ? /* @__PURE__ */ jsx(ShellConversation, {
@@ -3636,35 +3797,9 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3636
3797
  onRequestDisplayMode: state.handleDisplayModeChange,
3637
3798
  appName,
3638
3799
  appIcon,
3639
- userMessage: prodToolsUserMessage ?? state.selectedSim?.userMessage,
3800
+ userMessage,
3640
3801
  onContentWidthChange: state.handleContentWidthChange,
3641
- headerAction: prodTools && onCallTool ? /* @__PURE__ */ jsxs("button", {
3642
- type: "button",
3643
- onClick: handleRun,
3644
- disabled: isRunning,
3645
- className: "rounded-full px-3 py-1 text-sm font-medium transition-opacity disabled:opacity-40 flex items-center gap-1.5 cursor-pointer",
3646
- style: {
3647
- backgroundColor: "var(--color-text-primary)",
3648
- color: "var(--color-background-primary)"
3649
- },
3650
- children: [showCheck ? /* @__PURE__ */ jsx("svg", {
3651
- width: "12",
3652
- height: "12",
3653
- viewBox: "0 0 12 12",
3654
- fill: "none",
3655
- stroke: "currentColor",
3656
- strokeWidth: "2",
3657
- strokeLinecap: "round",
3658
- strokeLinejoin: "round",
3659
- children: /* @__PURE__ */ jsx("path", { d: "M2 6L5 9L10 3" })
3660
- }) : /* @__PURE__ */ jsx("svg", {
3661
- width: "10",
3662
- height: "12",
3663
- viewBox: "0 0 10 12",
3664
- fill: "currentColor",
3665
- children: /* @__PURE__ */ jsx("path", { d: "M0 0L10 6L0 12V0Z" })
3666
- }), "Run"]
3667
- }) : void 0,
3802
+ headerAction: runButton,
3668
3803
  children: content
3669
3804
  }) : content
3670
3805
  })
@@ -3673,4 +3808,4 @@ function Simulator({ children, simulations = {}, appName = "Sunpeak", appIcon, d
3673
3808
  //#endregion
3674
3809
  export { DEFAULT_STYLE_VARIABLES as S, McpAppHost as _, SidebarControl as a, getRegisteredHosts as b, SidebarTextarea as c, ThemeProvider as d, useThemeContext as f, extractResourceCSP as g, IframeResource as h, SidebarCollapsibleControl as i, SidebarToggle as l, useSimulatorState as m, resolveServerToolResult as n, SidebarInput as o, useMcpConnection as p, SidebarCheckbox as r, SidebarSelect as s, Simulator as t, SimpleSidebar as u, SCREEN_WIDTHS as v, registerHostShell as x, getHostShell as y };
3675
3810
 
3676
- //# sourceMappingURL=simulator-Dl8B-Ljb.js.map
3811
+ //# sourceMappingURL=simulator-BijjlOXb.js.map