sunpeak 0.20.35 → 0.20.42

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 (44) hide show
  1. package/README.md +4 -2
  2. package/bin/commands/inspect.mjs +241 -48
  3. package/bin/commands/test-init.mjs +61 -44
  4. package/bin/lib/eval/eval-runner.mjs +49 -1
  5. package/bin/lib/eval/eval-types.d.mts +27 -0
  6. package/bin/lib/eval/model-registry.mjs +6 -3
  7. package/bin/lib/test/test-fixtures.d.mts +9 -1
  8. package/bin/lib/test/test-fixtures.mjs +25 -7
  9. package/dist/chatgpt/index.cjs +1 -1
  10. package/dist/chatgpt/index.js +1 -1
  11. package/dist/claude/index.cjs +1 -1
  12. package/dist/claude/index.js +1 -1
  13. package/dist/embed.css +1 -1
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/inspector/app-types.d.ts +6 -4
  17. package/dist/inspector/index.cjs +1 -1
  18. package/dist/inspector/index.js +1 -1
  19. package/dist/inspector/inspector.d.ts +12 -4
  20. package/dist/inspector/simple-sidebar.d.ts +20 -6
  21. package/dist/inspector/use-inspector-state.d.ts +32 -0
  22. package/dist/{inspector-D0TWNx_T.js → inspector-C6n8zap3.js} +432 -171
  23. package/dist/{inspector-D0TWNx_T.js.map → inspector-C6n8zap3.js.map} +1 -1
  24. package/dist/{inspector-DQ_vv1wj.cjs → inspector-DOmiG64-.cjs} +432 -171
  25. package/dist/{inspector-DQ_vv1wj.cjs.map → inspector-DOmiG64-.cjs.map} +1 -1
  26. package/dist/style.css +18 -14
  27. package/package.json +1 -1
  28. package/template/dist/albums/albums.html +1 -1
  29. package/template/dist/albums/albums.json +1 -1
  30. package/template/dist/carousel/carousel.html +1 -1
  31. package/template/dist/carousel/carousel.json +1 -1
  32. package/template/dist/map/map.html +1 -1
  33. package/template/dist/map/map.json +1 -1
  34. package/template/dist/review/review.html +1 -1
  35. package/template/dist/review/review.json +1 -1
  36. package/template/tests/e2e/visual.spec.ts +0 -8
  37. package/template/tests/e2e/visual.spec.ts-snapshots/albums-fullscreen-chatgpt-darwin.png +0 -0
  38. package/template/tests/e2e/visual.spec.ts-snapshots/albums-fullscreen-chatgpt-linux.png +0 -0
  39. package/template/tests/e2e/visual.spec.ts-snapshots/albums-fullscreen-claude-darwin.png +0 -0
  40. package/template/tests/e2e/visual.spec.ts-snapshots/albums-fullscreen-claude-linux.png +0 -0
  41. package/template/tests/e2e/visual.spec.ts-snapshots/albums-page-light-chatgpt-darwin.png +0 -0
  42. package/template/tests/e2e/visual.spec.ts-snapshots/albums-page-light-chatgpt-linux.png +0 -0
  43. package/template/tests/e2e/visual.spec.ts-snapshots/albums-page-light-claude-darwin.png +0 -0
  44. package/template/tests/e2e/visual.spec.ts-snapshots/albums-page-light-claude-linux.png +0 -0
@@ -6434,6 +6434,7 @@ function IframeResource({ src, scriptSrc, html, hostContext, toolInput, toolInpu
6434
6434
  var DEFAULT_THEME = "dark";
6435
6435
  var DEFAULT_DISPLAY_MODE = "inline";
6436
6436
  var DEFAULT_PLATFORM = "desktop";
6437
+ var DEFAULT_SIDEBAR_WIDTH$1 = 260;
6437
6438
  /**
6438
6439
  * Parse URL params for initial inspector values.
6439
6440
  * Supported params:
@@ -6537,6 +6538,9 @@ var VALID_SCREEN_WIDTHS = new Set([
6537
6538
  "tablet",
6538
6539
  "full"
6539
6540
  ]);
6541
+ function isSafeStoredString(value) {
6542
+ return typeof value === "string" && value.length <= 200 && !/[\u0000-\u001f\u007f]/.test(value);
6543
+ }
6540
6544
  function sanitizeStoredPrefs(raw) {
6541
6545
  if (!raw || typeof raw !== "object") return {};
6542
6546
  const obj = raw;
@@ -6544,6 +6548,8 @@ function sanitizeStoredPrefs(raw) {
6544
6548
  if (typeof obj.theme === "string" && VALID_THEMES.has(obj.theme)) prefs.theme = obj.theme;
6545
6549
  if (typeof obj.locale === "string") prefs.locale = obj.locale;
6546
6550
  if (typeof obj.displayMode === "string" && VALID_DISPLAY_MODES.has(obj.displayMode)) prefs.displayMode = obj.displayMode;
6551
+ if (typeof obj.containerHeight === "number" && Number.isFinite(obj.containerHeight)) prefs.containerHeight = obj.containerHeight;
6552
+ if (typeof obj.containerWidth === "number" && Number.isFinite(obj.containerWidth)) prefs.containerWidth = obj.containerWidth;
6547
6553
  if (typeof obj.containerMaxHeight === "number" && Number.isFinite(obj.containerMaxHeight)) prefs.containerMaxHeight = obj.containerMaxHeight;
6548
6554
  if (typeof obj.containerMaxWidth === "number" && Number.isFinite(obj.containerMaxWidth)) prefs.containerMaxWidth = obj.containerMaxWidth;
6549
6555
  if (obj.safeAreaInsets && typeof obj.safeAreaInsets === "object") {
@@ -6560,6 +6566,12 @@ function sanitizeStoredPrefs(raw) {
6560
6566
  if (typeof obj.hover === "boolean") prefs.hover = obj.hover;
6561
6567
  if (typeof obj.touch === "boolean") prefs.touch = obj.touch;
6562
6568
  if (typeof obj.screenWidth === "string" && VALID_SCREEN_WIDTHS.has(obj.screenWidth)) prefs.screenWidth = obj.screenWidth;
6569
+ if (typeof obj.sidebarWidth === "number" && Number.isFinite(obj.sidebarWidth)) prefs.sidebarWidth = Math.max(DEFAULT_SIDEBAR_WIDTH$1, Math.round(obj.sidebarWidth));
6570
+ if (typeof obj.rightSidebarWidth === "number" && Number.isFinite(obj.rightSidebarWidth)) prefs.rightSidebarWidth = Math.max(DEFAULT_SIDEBAR_WIDTH$1, Math.round(obj.rightSidebarWidth));
6571
+ if (typeof obj.timeZone === "string") prefs.timeZone = obj.timeZone;
6572
+ if (typeof obj.prodResources === "boolean") prefs.prodResources = obj.prodResources;
6573
+ if (isSafeStoredString(obj.modelProvider)) prefs.modelProvider = obj.modelProvider;
6574
+ if (isSafeStoredString(obj.modelId)) prefs.modelId = obj.modelId;
6563
6575
  return prefs;
6564
6576
  }
6565
6577
  function readStoredPrefs() {
@@ -6572,6 +6584,12 @@ function readStoredPrefs() {
6572
6584
  return {};
6573
6585
  }
6574
6586
  }
6587
+ function writeStoredPrefs(prefs) {
6588
+ if (typeof window === "undefined") return;
6589
+ try {
6590
+ localStorage.setItem(PREFS_KEY, JSON.stringify(prefs));
6591
+ } catch {}
6592
+ }
6575
6593
  function deriveContainerDimensions({ displayMode, containerHeight, containerWidth, containerMaxHeight, containerMaxWidth, measuredContentWidth, viewportHeight = 800, viewportWidth = 1280 }) {
6576
6594
  if (containerHeight != null || containerWidth != null || containerMaxHeight != null || containerMaxWidth != null) return {
6577
6595
  ...containerHeight != null ? { height: containerHeight } : {},
@@ -6590,36 +6608,38 @@ function deriveContainerDimensions({ displayMode, containerHeight, containerWidt
6590
6608
  if (measuredContentWidth != null) return { maxWidth: measuredContentWidth };
6591
6609
  }
6592
6610
  function useInspectorState({ simulations, defaultHost = "chatgpt", preserveToolDataOnSimulationChange = false }) {
6593
- const simulationNames = Object.keys(simulations).filter((name) => simulations[name].resource).sort((a, b) => {
6611
+ const simulationNames = Object.keys(simulations).sort((a, b) => {
6594
6612
  const simA = simulations[a];
6595
6613
  const simB = simulations[b];
6596
- const resourceLabelA = simA.resource.title || simA.resource.name;
6597
- const resourceLabelB = simB.resource.title || simB.resource.name;
6598
- const labelA = `${resourceLabelA}: ${simA.tool.title || simA.tool.name}`;
6599
- const labelB = `${resourceLabelB}: ${simB.tool.title || simB.tool.name}`;
6614
+ const resourceLabelA = simA.resource ? `${simA.resource.title || simA.resource.name}: ` : "";
6615
+ const resourceLabelB = simB.resource ? `${simB.resource.title || simB.resource.name}: ` : "";
6616
+ const labelA = `${resourceLabelA}${simA.tool.title || simA.tool.name}`;
6617
+ const labelB = `${resourceLabelB}${simB.tool.title || simB.tool.name}`;
6600
6618
  return labelA.localeCompare(labelB);
6601
6619
  });
6620
+ const defaultSimulationName = simulationNames.find((name) => !!simulations[name]?.resource) ?? simulationNames[0] ?? "";
6602
6621
  const urlParams = useMemo(() => parseUrlParams(), []);
6603
6622
  const autoRun = urlParams.autoRun === true;
6604
6623
  const storedPrefs = useMemo(() => autoRun ? {} : readStoredPrefs(), [autoRun]);
6605
6624
  const [screenWidth, setScreenWidth] = useState(storedPrefs.screenWidth ?? "full");
6625
+ const [sidebarWidth, setSidebarWidth] = useState(storedPrefs.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH$1);
6626
+ const [rightSidebarWidth, setRightSidebarWidth] = useState(storedPrefs.rightSidebarWidth ?? DEFAULT_SIDEBAR_WIDTH$1);
6606
6627
  const isMobileWidth = (width) => width === "mobile-s" || width === "mobile-l";
6607
6628
  const [activeHost, setActiveHost] = useState(urlParams.host ?? storedPrefs.activeHost ?? defaultHost);
6608
6629
  const [selectedSimulationName, setSelectedSimulationName] = useState(useMemo(() => {
6609
- const defaultName = simulationNames[0] ?? "";
6610
- if (!urlParams.simulation) return defaultName;
6611
- return urlParams.simulation in simulations ? urlParams.simulation : defaultName;
6630
+ if (!urlParams.simulation) return defaultSimulationName;
6631
+ return urlParams.simulation in simulations ? urlParams.simulation : defaultSimulationName;
6612
6632
  }, [
6613
6633
  urlParams.simulation,
6614
6634
  simulations,
6615
- simulationNames
6635
+ defaultSimulationName
6616
6636
  ]));
6617
6637
  const selectedSim = simulations[selectedSimulationName];
6618
6638
  const [theme, setTheme] = useState(urlParams.theme ?? storedPrefs.theme ?? DEFAULT_THEME);
6619
6639
  const [displayMode, _setDisplayMode] = useState(urlParams.displayMode ?? storedPrefs.displayMode ?? DEFAULT_DISPLAY_MODE);
6620
6640
  const [locale, setLocale] = useState(urlParams.locale ?? storedPrefs.locale ?? "en-US");
6621
- const [containerHeight, setContainerHeight] = useState(void 0);
6622
- const [containerWidth, setContainerWidth] = useState(void 0);
6641
+ const [containerHeight, setContainerHeight] = useState(storedPrefs.containerHeight);
6642
+ const [containerWidth, setContainerWidth] = useState(storedPrefs.containerWidth);
6623
6643
  const [containerMaxHeight, setContainerMaxHeight] = useState(urlParams.containerMaxHeight ?? storedPrefs.containerMaxHeight);
6624
6644
  const [containerMaxWidth, setContainerMaxWidth] = useState(urlParams.containerMaxWidth ?? storedPrefs.containerMaxWidth);
6625
6645
  const [platform, setPlatform] = useState(urlParams.platform ?? storedPrefs.platform ?? DEFAULT_PLATFORM);
@@ -6631,7 +6651,7 @@ function useInspectorState({ simulations, defaultHost = "chatgpt", preserveToolD
6631
6651
  left: 0,
6632
6652
  right: 0
6633
6653
  });
6634
- const [timeZone, setTimeZone] = useState(() => Intl.DateTimeFormat().resolvedOptions().timeZone);
6654
+ const [timeZone, setTimeZone] = useState(() => storedPrefs.timeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone);
6635
6655
  const isFirstRender = useRef(true);
6636
6656
  useEffect(() => {
6637
6657
  if (isFirstRender.current) {
@@ -6639,27 +6659,32 @@ function useInspectorState({ simulations, defaultHost = "chatgpt", preserveToolD
6639
6659
  return;
6640
6660
  }
6641
6661
  if (autoRun) return;
6642
- try {
6643
- const prefs = {
6644
- theme,
6645
- locale,
6646
- displayMode,
6647
- containerMaxHeight,
6648
- containerMaxWidth,
6649
- safeAreaInsets,
6650
- activeHost,
6651
- platform,
6652
- hover,
6653
- touch,
6654
- screenWidth
6655
- };
6656
- localStorage.setItem(PREFS_KEY, JSON.stringify(prefs));
6657
- } catch {}
6662
+ writeStoredPrefs({
6663
+ ...readStoredPrefs(),
6664
+ theme,
6665
+ locale,
6666
+ displayMode,
6667
+ containerHeight,
6668
+ containerWidth,
6669
+ containerMaxHeight,
6670
+ containerMaxWidth,
6671
+ safeAreaInsets,
6672
+ activeHost,
6673
+ platform,
6674
+ hover,
6675
+ touch,
6676
+ screenWidth,
6677
+ sidebarWidth,
6678
+ rightSidebarWidth,
6679
+ timeZone
6680
+ });
6658
6681
  }, [
6659
6682
  autoRun,
6660
6683
  theme,
6661
6684
  locale,
6662
6685
  displayMode,
6686
+ containerHeight,
6687
+ containerWidth,
6663
6688
  containerMaxHeight,
6664
6689
  containerMaxWidth,
6665
6690
  safeAreaInsets,
@@ -6667,7 +6692,10 @@ function useInspectorState({ simulations, defaultHost = "chatgpt", preserveToolD
6667
6692
  platform,
6668
6693
  hover,
6669
6694
  touch,
6670
- screenWidth
6695
+ screenWidth,
6696
+ sidebarWidth,
6697
+ rightSidebarWidth,
6698
+ timeZone
6671
6699
  ]);
6672
6700
  const [measuredContentWidth, setMeasuredContentWidth] = useState(void 0);
6673
6701
  const handleContentWidthChange = useCallback((width) => {
@@ -6752,6 +6780,8 @@ function useInspectorState({ simulations, defaultHost = "chatgpt", preserveToolD
6752
6780
  }
6753
6781
  if (editingField !== "modelContext") {
6754
6782
  setModelContext(null);
6783
+ setModelAppContext(null);
6784
+ setModelContextJson("null");
6755
6785
  setModelContextError("");
6756
6786
  }
6757
6787
  }, [
@@ -6828,6 +6858,10 @@ function useInspectorState({ simulations, defaultHost = "chatgpt", preserveToolD
6828
6858
  setActiveHost,
6829
6859
  screenWidth,
6830
6860
  setScreenWidth,
6861
+ sidebarWidth,
6862
+ setSidebarWidth,
6863
+ rightSidebarWidth,
6864
+ setRightSidebarWidth,
6831
6865
  theme,
6832
6866
  setTheme,
6833
6867
  displayMode,
@@ -7058,9 +7092,10 @@ var useThemeContext = () => {
7058
7092
  if (context === void 0) throw new Error("useThemeContext must be used within a ThemeProvider");
7059
7093
  return context;
7060
7094
  };
7061
- //#endregion
7062
- //#region src/inspector/simple-sidebar.tsx
7063
- var DEFAULT_SIDEBAR_WIDTH = 260;
7095
+ function clampSidebarWidth(rawWidth, viewportWidth) {
7096
+ const maxWidth = Math.floor(viewportWidth / 3);
7097
+ return Math.max(260, Math.min(maxWidth, rawWidth));
7098
+ }
7064
7099
  function ChevronRightIcon() {
7065
7100
  return /* @__PURE__ */ jsx("svg", {
7066
7101
  width: "1em",
@@ -7074,19 +7109,34 @@ function ChevronRightIcon() {
7074
7109
  })
7075
7110
  });
7076
7111
  }
7077
- function SimpleSidebar({ children, controls, headerRight, rootRef, fillParent = false }) {
7112
+ function SimpleSidebar({ children, controls, rightControls, sidebarWidth, rightSidebarWidth, onSidebarWidthChange, onRightSidebarWidthChange, headerRight, rootRef, fillParent = false }) {
7078
7113
  const [isDrawerOpen, setIsDrawerOpen] = React.useState(false);
7079
- const [sidebarWidth, setSidebarWidth] = React.useState(DEFAULT_SIDEBAR_WIDTH);
7114
+ const [internalSidebarWidth, setInternalSidebarWidth] = React.useState(260);
7115
+ const [internalRightSidebarWidth, setInternalRightSidebarWidth] = React.useState(260);
7080
7116
  const [isResizing, setIsResizing] = React.useState(false);
7117
+ const [isResizingRight, setIsResizingRight] = React.useState(false);
7118
+ const effectiveSidebarWidth = sidebarWidth ?? internalSidebarWidth;
7119
+ const effectiveRightSidebarWidth = rightSidebarWidth ?? internalRightSidebarWidth;
7120
+ const setSidebarWidth = React.useCallback((width) => {
7121
+ setInternalSidebarWidth(width);
7122
+ onSidebarWidthChange?.(width);
7123
+ }, [onSidebarWidthChange]);
7124
+ const setRightSidebarWidth = React.useCallback((width) => {
7125
+ setInternalRightSidebarWidth(width);
7126
+ onRightSidebarWidthChange?.(width);
7127
+ }, [onRightSidebarWidthChange]);
7081
7128
  const handleMouseDown = React.useCallback((e) => {
7082
7129
  e.preventDefault();
7083
7130
  setIsResizing(true);
7084
7131
  }, []);
7132
+ const handleRightMouseDown = React.useCallback((e) => {
7133
+ e.preventDefault();
7134
+ setIsResizingRight(true);
7135
+ }, []);
7085
7136
  React.useEffect(() => {
7086
7137
  if (!isResizing) return;
7087
7138
  const handleMouseMove = (e) => {
7088
- const maxWidth = Math.floor(window.innerWidth / 3);
7089
- setSidebarWidth(Math.min(maxWidth, Math.max(DEFAULT_SIDEBAR_WIDTH, e.clientX)));
7139
+ setSidebarWidth(clampSidebarWidth(e.clientX, window.innerWidth));
7090
7140
  };
7091
7141
  const handleMouseUp = () => {
7092
7142
  setIsResizing(false);
@@ -7097,12 +7147,27 @@ function SimpleSidebar({ children, controls, headerRight, rootRef, fillParent =
7097
7147
  document.removeEventListener("mousemove", handleMouseMove);
7098
7148
  document.removeEventListener("mouseup", handleMouseUp);
7099
7149
  };
7100
- }, [isResizing]);
7150
+ }, [isResizing, setSidebarWidth]);
7151
+ React.useEffect(() => {
7152
+ if (!isResizingRight) return;
7153
+ const handleMouseMove = (e) => {
7154
+ setRightSidebarWidth(clampSidebarWidth(window.innerWidth - e.clientX, window.innerWidth));
7155
+ };
7156
+ const handleMouseUp = () => {
7157
+ setIsResizingRight(false);
7158
+ };
7159
+ document.addEventListener("mousemove", handleMouseMove);
7160
+ document.addEventListener("mouseup", handleMouseUp);
7161
+ return () => {
7162
+ document.removeEventListener("mousemove", handleMouseMove);
7163
+ document.removeEventListener("mouseup", handleMouseUp);
7164
+ };
7165
+ }, [isResizingRight, setRightSidebarWidth]);
7101
7166
  return /* @__PURE__ */ jsxs("div", {
7102
7167
  ref: rootRef,
7103
7168
  className: `sunpeak-inspector-root flex ${fillParent ? "h-full w-full" : "h-screen w-full"} overflow-hidden relative`,
7104
7169
  children: [
7105
- isResizing && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 cursor-col-resize" }),
7170
+ (isResizing || isResizingRight) && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 cursor-col-resize" }),
7106
7171
  isDrawerOpen && /* @__PURE__ */ jsx("div", {
7107
7172
  className: "md:hidden fixed inset-0 bg-black/50 z-40 pointer-events-auto",
7108
7173
  onClick: (e) => {
@@ -7118,7 +7183,7 @@ function SimpleSidebar({ children, controls, headerRight, rootRef, fillParent =
7118
7183
  ${isDrawerOpen ? "max-md:translate-x-0" : "max-md:-translate-x-full"}
7119
7184
  `,
7120
7185
  style: {
7121
- width: sidebarWidth,
7186
+ width: effectiveSidebarWidth,
7122
7187
  borderRight: "1px solid var(--color-border-primary)"
7123
7188
  },
7124
7189
  children: [/* @__PURE__ */ jsx("div", {
@@ -7171,19 +7236,69 @@ function SimpleSidebar({ children, controls, headerRight, rootRef, fillParent =
7171
7236
  "aria-label": "Open sidebar",
7172
7237
  children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
7173
7238
  }), children]
7239
+ }),
7240
+ rightControls && /* @__PURE__ */ jsxs("aside", {
7241
+ className: "relative hidden md:flex flex-col bg-sidebar",
7242
+ style: {
7243
+ width: effectiveRightSidebarWidth,
7244
+ borderLeft: "1px solid var(--color-border-primary)"
7245
+ },
7246
+ children: [/* @__PURE__ */ jsx("div", {
7247
+ className: "flex-1 min-h-0 overflow-y-auto px-3 pb-3 pt-2",
7248
+ children: rightControls
7249
+ }), /* @__PURE__ */ jsx("div", {
7250
+ onMouseDown: handleRightMouseDown,
7251
+ className: "hidden md:block absolute top-0 left-0 w-1 h-full cursor-col-resize hover:bg-black/10 dark:hover:bg-white/10 active:bg-black/20 dark:active:bg-white/20 transition-colors"
7252
+ })]
7174
7253
  })
7175
7254
  ]
7176
7255
  });
7177
7256
  }
7178
7257
  var DOCS_BASE_URL$1 = "https://sunpeak.ai/docs";
7179
- function HelpIcon({ tooltip, docsPath }) {
7258
+ function HelpIcon({ tooltip, docsPath, placement = "right" }) {
7259
+ const linkRef = React.useRef(null);
7260
+ const [isTooltipVisible, setIsTooltipVisible] = React.useState(false);
7261
+ const [tooltipPosition, setTooltipPosition] = React.useState({
7262
+ left: 0,
7263
+ top: 0
7264
+ });
7265
+ const tooltipOffset = 8;
7266
+ const tooltipTransform = placement === "left" ? "translate(-100%, -50%)" : "translateY(-50%)";
7267
+ const setTooltipFromPoint = React.useCallback((clientX, clientY) => {
7268
+ setTooltipPosition({
7269
+ left: placement === "left" ? clientX - tooltipOffset : clientX + tooltipOffset,
7270
+ top: clientY
7271
+ });
7272
+ }, [placement]);
7273
+ const setTooltipFromIcon = React.useCallback(() => {
7274
+ const rect = linkRef.current?.getBoundingClientRect();
7275
+ if (!rect) return;
7276
+ setTooltipPosition({
7277
+ left: placement === "left" ? rect.left - tooltipOffset : rect.right + tooltipOffset,
7278
+ top: rect.top + rect.height / 2
7279
+ });
7280
+ }, [placement]);
7180
7281
  return /* @__PURE__ */ jsxs("a", {
7282
+ ref: linkRef,
7181
7283
  href: `${DOCS_BASE_URL$1}/${docsPath}`,
7182
7284
  target: "_blank",
7183
7285
  rel: "noopener noreferrer",
7184
7286
  "aria-label": tooltip,
7185
7287
  className: "group relative inline-flex items-center justify-center no-underline flex-shrink-0 transition-colors",
7186
7288
  style: { color: "var(--color-text-tertiary, var(--color-text-secondary))" },
7289
+ onMouseEnter: (e) => {
7290
+ setIsTooltipVisible(true);
7291
+ setTooltipFromPoint(e.clientX, e.clientY);
7292
+ },
7293
+ onMouseMove: (e) => {
7294
+ setTooltipFromPoint(e.clientX, e.clientY);
7295
+ },
7296
+ onMouseLeave: () => setIsTooltipVisible(false),
7297
+ onFocus: () => {
7298
+ setIsTooltipVisible(true);
7299
+ setTooltipFromIcon();
7300
+ },
7301
+ onBlur: () => setIsTooltipVisible(false),
7187
7302
  onClick: (e) => e.stopPropagation(),
7188
7303
  children: [/* @__PURE__ */ jsxs("svg", {
7189
7304
  width: "12",
@@ -7209,8 +7324,11 @@ function HelpIcon({ tooltip, docsPath }) {
7209
7324
  })]
7210
7325
  }), /* @__PURE__ */ jsx("span", {
7211
7326
  "aria-hidden": "true",
7212
- className: "pointer-events-none absolute left-full top-1/2 z-[200] ml-1.5 hidden -translate-y-1/2 whitespace-nowrap rounded px-2 py-1 text-[11px] font-normal leading-tight group-hover:block group-focus-within:block",
7327
+ className: `pointer-events-none fixed z-[1000] whitespace-nowrap rounded px-2 py-1 text-[11px] font-normal leading-tight ${isTooltipVisible ? "block" : "hidden"}`,
7213
7328
  style: {
7329
+ left: tooltipPosition.left,
7330
+ top: tooltipPosition.top,
7331
+ transform: tooltipTransform,
7214
7332
  backgroundColor: "var(--color-text-primary)",
7215
7333
  color: "var(--color-background-primary)"
7216
7334
  },
@@ -7218,7 +7336,7 @@ function HelpIcon({ tooltip, docsPath }) {
7218
7336
  })]
7219
7337
  });
7220
7338
  }
7221
- function SidebarControl({ label, children, tooltip, docsPath, "data-testid": testId }) {
7339
+ function SidebarControl({ label, children, tooltip, tooltipPlacement, docsPath, "data-testid": testId }) {
7222
7340
  return /* @__PURE__ */ jsxs("div", {
7223
7341
  className: "space-y-1",
7224
7342
  "data-testid": testId,
@@ -7227,15 +7345,17 @@ function SidebarControl({ label, children, tooltip, docsPath, "data-testid": tes
7227
7345
  style: { color: "var(--color-text-secondary)" },
7228
7346
  children: [label, tooltip && docsPath && /* @__PURE__ */ jsx(HelpIcon, {
7229
7347
  tooltip,
7230
- docsPath
7348
+ docsPath,
7349
+ placement: tooltipPlacement
7231
7350
  })]
7232
7351
  }), children]
7233
7352
  });
7234
7353
  }
7235
- function SidebarCollapsibleControl({ label, children, defaultCollapsed = true, tooltip, docsPath, "data-testid": testId }) {
7354
+ function SidebarCollapsibleControl({ label, children, defaultCollapsed = true, className, contentClassName, style, tooltip, tooltipPlacement, docsPath, "data-testid": testId }) {
7236
7355
  const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed);
7237
7356
  return /* @__PURE__ */ jsxs("div", {
7238
- className: "space-y-1",
7357
+ className: className ? `space-y-1 ${className}` : "space-y-1",
7358
+ style: isCollapsed ? void 0 : style,
7239
7359
  "data-testid": testId,
7240
7360
  children: [/* @__PURE__ */ jsxs("button", {
7241
7361
  onClick: () => setIsCollapsed(!isCollapsed),
@@ -7246,13 +7366,17 @@ function SidebarCollapsibleControl({ label, children, defaultCollapsed = true, t
7246
7366
  className: "inline-flex items-center gap-1",
7247
7367
  children: [label, tooltip && docsPath && /* @__PURE__ */ jsx(HelpIcon, {
7248
7368
  tooltip,
7249
- docsPath
7369
+ docsPath,
7370
+ placement: tooltipPlacement
7250
7371
  })]
7251
7372
  }), /* @__PURE__ */ jsx("span", {
7252
7373
  className: "text-[8px]",
7253
7374
  children: isCollapsed ? "▶" : "▼"
7254
7375
  })]
7255
- }), !isCollapsed && children]
7376
+ }), !isCollapsed && /* @__PURE__ */ jsx("div", {
7377
+ className: contentClassName,
7378
+ children
7379
+ })]
7256
7380
  });
7257
7381
  }
7258
7382
  var formElementStyle = {
@@ -7327,7 +7451,7 @@ function SidebarInput({ value, onChange, applyOnBlur = false, autoComplete, plac
7327
7451
  }
7328
7452
  });
7329
7453
  }
7330
- function SidebarCheckbox({ checked, onChange, label, tooltip, docsPath }) {
7454
+ function SidebarCheckbox({ checked, onChange, label, tooltip, tooltipPlacement, docsPath }) {
7331
7455
  const id = React.useId();
7332
7456
  return /* @__PURE__ */ jsxs("div", {
7333
7457
  className: "flex items-center gap-1.5",
@@ -7347,24 +7471,25 @@ function SidebarCheckbox({ checked, onChange, label, tooltip, docsPath }) {
7347
7471
  }),
7348
7472
  tooltip && docsPath && /* @__PURE__ */ jsx(HelpIcon, {
7349
7473
  tooltip,
7350
- docsPath
7474
+ docsPath,
7475
+ placement: tooltipPlacement
7351
7476
  })
7352
7477
  ]
7353
7478
  });
7354
7479
  }
7355
- function SidebarTextarea({ value, onChange, onFocus, onBlur, placeholder, maxRows = 8, error, "data-testid": testId }) {
7480
+ function SidebarTextarea({ value, onChange, onFocus, onBlur, placeholder, maxRows = 8, fill = false, error, "data-testid": testId }) {
7356
7481
  const contentRows = value?.split("\n").length ?? 1;
7357
7482
  return /* @__PURE__ */ jsxs("div", {
7358
- className: "space-y-0.5",
7483
+ className: fill ? "flex h-full min-h-0 flex-col gap-0.5" : "space-y-0.5",
7359
7484
  children: [/* @__PURE__ */ jsx("textarea", {
7360
7485
  value,
7361
7486
  onChange: (e) => onChange(e.target.value),
7362
7487
  onFocus,
7363
7488
  onBlur,
7364
7489
  placeholder,
7365
- rows: Math.min(contentRows, maxRows),
7490
+ rows: fill ? void 0 : Math.min(contentRows, maxRows),
7366
7491
  "data-testid": testId,
7367
- className: "w-full text-[10px] font-mono rounded-md px-2 py-1.5 outline-none resize-y",
7492
+ className: `w-full text-[10px] font-mono rounded-md px-2 py-1.5 outline-none ${fill ? "h-full min-h-0 flex-1 resize-none" : "resize-y"}`,
7368
7493
  style: {
7369
7494
  ...formElementStyle,
7370
7495
  cursor: "text",
@@ -7453,10 +7578,9 @@ function flattenAppToSimulations(app) {
7453
7578
  }
7454
7579
  for (const appTool of app.tools) {
7455
7580
  const uri = getOutputTemplate(appTool.tool._meta);
7456
- if (!uri) continue;
7457
- const resource = resourcesByUri.get(uri);
7458
- if (!resource) continue;
7459
- const mcpResource = toMcpResource(resource);
7581
+ const resource = uri ? resourcesByUri.get(uri) : void 0;
7582
+ if (uri && !resource) console.warn(`[Inspector] Tool '${appTool.tool.name}' references unknown resource URI '${uri}'. The tool remains callable but has no UI to render.`);
7583
+ const mcpResource = resource ? toMcpResource(resource) : void 0;
7460
7584
  const sims = appTool.simulations && appTool.simulations.length > 0 ? appTool.simulations : [{ name: appTool.tool.name }];
7461
7585
  for (const sim of sims) {
7462
7586
  const key = `${appTool.tool.name}__${sim.name}`;
@@ -7464,10 +7588,12 @@ function flattenAppToSimulations(app) {
7464
7588
  result[key] = {
7465
7589
  name: key,
7466
7590
  displayName: sim.name,
7467
- resourceHtml: resource.html,
7591
+ ...resource ? {
7592
+ resourceHtml: resource.html,
7593
+ resource: mcpResource
7594
+ } : {},
7468
7595
  userMessage: sim.userMessage,
7469
7596
  tool: appTool.tool,
7470
- resource: mcpResource,
7471
7597
  toolInput: sim.toolInput,
7472
7598
  toolResult: sim.toolResult,
7473
7599
  serverTools: sim.serverTools
@@ -7482,12 +7608,16 @@ var DOCS_BASE_URL = "https://sunpeak.ai/docs";
7482
7608
  var DEFAULT_MODEL_PROVIDERS = [{
7483
7609
  id: "openai",
7484
7610
  label: "OpenAI",
7485
- defaultModel: "gpt-4o"
7611
+ defaultModel: "gpt-5.5"
7486
7612
  }, {
7487
7613
  id: "anthropic",
7488
7614
  label: "Anthropic",
7489
- defaultModel: "claude-3-5-sonnet-20241022"
7615
+ defaultModel: "claude-sonnet-4-20250514"
7490
7616
  }];
7617
+ function createModelConversationId() {
7618
+ const random = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
7619
+ return `model-chat-${Date.now()}-${random}`;
7620
+ }
7491
7621
  function splitCssArgs(value) {
7492
7622
  const args = [];
7493
7623
  let depth = 0;
@@ -7543,7 +7673,6 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7543
7673
  const toolMap = React.useMemo(() => {
7544
7674
  const map = /* @__PURE__ */ new Map();
7545
7675
  for (const [simName, sim] of Object.entries(simulations)) {
7546
- if (!sim.resource) continue;
7547
7676
  const toolName = sim.tool.name;
7548
7677
  if (!map.has(toolName)) map.set(toolName, {
7549
7678
  tool: sim.tool,
@@ -7552,6 +7681,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7552
7681
  fixtureSimNames: []
7553
7682
  });
7554
7683
  const info = map.get(toolName);
7684
+ if (!info.resource && sim.resource) info.resource = sim.resource;
7555
7685
  info.simNames.push(simName);
7556
7686
  if (hasFixtureData(sim)) info.fixtureSimNames.push(simName);
7557
7687
  }
@@ -7564,6 +7694,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7564
7694
  const labelB = infoB.tool.title || b;
7565
7695
  return labelA.localeCompare(labelB);
7566
7696
  }), [toolMap]);
7697
+ const defaultToolName = React.useMemo(() => toolNames.find((name) => !!toolMap.get(name)?.resource) ?? toolNames[0] ?? "", [toolMap, toolNames]);
7567
7698
  const initUrlParams = React.useMemo(() => {
7568
7699
  if (typeof window === "undefined") return {
7569
7700
  tool: null,
@@ -7579,17 +7710,18 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7579
7710
  autoRun: params.get("autoRun") === "true"
7580
7711
  };
7581
7712
  }, []);
7713
+ const storedPrefs = React.useMemo(() => initUrlParams.autoRun ? {} : readStoredPrefs(), [initUrlParams.autoRun]);
7582
7714
  const [selectedToolName, setSelectedToolName] = React.useState(() => {
7583
7715
  if (initUrlParams.tool && toolMap.has(initUrlParams.tool)) return initUrlParams.tool;
7584
7716
  if (initUrlParams.simulation) {
7585
7717
  for (const [toolName, info] of toolMap) if (info.simNames.includes(initUrlParams.simulation)) return toolName;
7586
7718
  }
7587
- return toolNames[0] ?? "";
7719
+ return defaultToolName;
7588
7720
  });
7589
7721
  const prevToolNamesRef = React.useRef(toolNames);
7590
7722
  if (prevToolNamesRef.current !== toolNames) {
7591
7723
  prevToolNamesRef.current = toolNames;
7592
- if (toolNames.length > 0 && !toolMap.has(selectedToolName)) setSelectedToolName(toolNames[0]);
7724
+ if (toolNames.length > 0 && !toolMap.has(selectedToolName)) setSelectedToolName(defaultToolName);
7593
7725
  }
7594
7726
  const selectedToolInfo = toolMap.get(selectedToolName);
7595
7727
  const [activeSimulationName, setActiveSimulationName] = React.useState(() => {
@@ -7613,6 +7745,12 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7613
7745
  defaultHost,
7614
7746
  preserveToolDataOnSimulationChange: isLiveMcpRender
7615
7747
  });
7748
+ const resetAppContextForSelectionChange = () => {
7749
+ state.setModelContext(null);
7750
+ state.setModelAppContext(null);
7751
+ state.setModelContextJson("null");
7752
+ state.setModelContextError("");
7753
+ };
7616
7754
  const [serverUrl, setServerUrl] = React.useState(mcpServerUrl ?? "");
7617
7755
  const [authType, setAuthType] = React.useState("none");
7618
7756
  const [bearerToken, setBearerToken] = React.useState("");
@@ -7622,13 +7760,12 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7622
7760
  const [oauthStatus, setOauthStatus] = React.useState("none");
7623
7761
  const [oauthError, setOauthError] = React.useState();
7624
7762
  const connection = useMcpConnection(isEmbedded ? void 0 : mcpServerUrl || void 0, inspectorApiBaseUrl);
7625
- const [prodResources, setProdResources] = React.useState(state.urlProdResources ?? defaultProdResources);
7763
+ const [prodResources, setProdResources] = React.useState(state.urlProdResources ?? storedPrefs.prodResources ?? defaultProdResources);
7626
7764
  const showSidebar = state.urlSidebar !== false;
7627
7765
  const showDevOverlay = state.urlDevOverlay !== false;
7628
7766
  const [isRunning, setIsRunning] = React.useState(false);
7629
7767
  const [hasRun, setHasRun] = React.useState(false);
7630
7768
  const [showCheck, setShowCheck] = React.useState(false);
7631
- const prodResourcesId = React.useId();
7632
7769
  const [serverPreviewGeneration, setServerPreviewGeneration] = React.useState(0);
7633
7770
  const checkTimerRef = React.useRef(void 0);
7634
7771
  const oauthCleanupRef = React.useRef(void 0);
@@ -7645,6 +7782,15 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7645
7782
  if (requested && modelProviderOptions.some((provider) => provider.id === requested)) return requested;
7646
7783
  return modelProviderOptions[0]?.id ?? "openai";
7647
7784
  }, [modelChat?.defaultProvider, modelProviderOptions]);
7785
+ const initialModelProvider = React.useMemo(() => {
7786
+ const stored = storedPrefs.modelProvider;
7787
+ if (stored && modelProviderOptions.some((provider) => provider.id === stored)) return stored;
7788
+ return defaultModelProvider;
7789
+ }, [
7790
+ defaultModelProvider,
7791
+ modelProviderOptions,
7792
+ storedPrefs.modelProvider
7793
+ ]);
7648
7794
  const getDefaultModelId = React.useCallback((provider) => {
7649
7795
  const option = modelProviderOptions.find((item) => item.id === provider);
7650
7796
  const candidates = [
@@ -7656,8 +7802,20 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7656
7802
  if (providerModels.length === 0) return candidates.find(Boolean) ?? "";
7657
7803
  return candidates.find((model) => model && providerModels.includes(model)) ?? providerModels[0];
7658
7804
  }, [modelChat?.defaultModel, modelProviderOptions]);
7659
- const [modelProvider, setModelProvider] = React.useState(defaultModelProvider);
7660
- const [modelId, setModelId] = React.useState(() => getDefaultModelId(defaultModelProvider));
7805
+ const getInitialModelId = React.useCallback((provider) => {
7806
+ const stored = storedPrefs.modelId;
7807
+ if (storedPrefs.modelProvider && storedPrefs.modelProvider !== provider) return getDefaultModelId(provider);
7808
+ const providerModels = modelProviderOptions.find((item) => item.id === provider)?.models ?? [];
7809
+ if (stored && (providerModels.length === 0 || providerModels.includes(stored))) return stored;
7810
+ return getDefaultModelId(provider);
7811
+ }, [
7812
+ getDefaultModelId,
7813
+ modelProviderOptions,
7814
+ storedPrefs.modelId,
7815
+ storedPrefs.modelProvider
7816
+ ]);
7817
+ const [modelProvider, setModelProvider] = React.useState(initialModelProvider);
7818
+ const [modelId, setModelId] = React.useState(() => getInitialModelId(initialModelProvider));
7661
7819
  const [apiKeyDraft, setApiKeyDraft] = React.useState("");
7662
7820
  const [keyStatus, setKeyStatus] = React.useState({ hasKey: false });
7663
7821
  const [isKeyStatusLoading, setIsKeyStatusLoading] = React.useState(usesApiKeyUi);
@@ -7666,9 +7824,36 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7666
7824
  const [chatInput, setChatInput] = React.useState("");
7667
7825
  const [isChatting, setIsChatting] = React.useState(false);
7668
7826
  const [chatStatus, setChatStatus] = React.useState("");
7827
+ const modelConversationIdRef = React.useRef(createModelConversationId());
7669
7828
  const currentModelProvider = React.useMemo(() => modelProviderOptions.find((provider) => provider.id === modelProvider), [modelProvider, modelProviderOptions]);
7670
7829
  const selectedProviderModelOptions = React.useMemo(() => currentModelProvider?.models ?? [], [currentModelProvider?.models]);
7671
- const modelCallableTools = React.useMemo(() => Array.from(toolMap.values()).filter((info) => !!info.resource && isToolVisibleToModel(info.tool)).map((info) => info.tool), [toolMap]);
7830
+ const modelCallableTools = React.useMemo(() => {
7831
+ const map = /* @__PURE__ */ new Map();
7832
+ for (const sim of Object.values(simulations)) {
7833
+ if (!isToolVisibleToModel(sim.tool) || map.has(sim.tool.name)) continue;
7834
+ map.set(sim.tool.name, sim.tool);
7835
+ }
7836
+ return Array.from(map.values());
7837
+ }, [simulations]);
7838
+ const isFirstInspectorPrefsRender = React.useRef(true);
7839
+ React.useEffect(() => {
7840
+ if (isFirstInspectorPrefsRender.current) {
7841
+ isFirstInspectorPrefsRender.current = false;
7842
+ return;
7843
+ }
7844
+ if (initUrlParams.autoRun) return;
7845
+ writeStoredPrefs({
7846
+ ...readStoredPrefs(),
7847
+ prodResources,
7848
+ modelProvider,
7849
+ modelId
7850
+ });
7851
+ }, [
7852
+ initUrlParams.autoRun,
7853
+ modelId,
7854
+ modelProvider,
7855
+ prodResources
7856
+ ]);
7672
7857
  React.useEffect(() => {
7673
7858
  setServerUrl(mcpServerUrl ?? "");
7674
7859
  }, [mcpServerUrl]);
@@ -7778,6 +7963,13 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
7778
7963
  usesApiKeyUi,
7779
7964
  usesLocalModelEndpoints
7780
7965
  ]);
7966
+ const handleResetModelConversation = React.useCallback(() => {
7967
+ modelConversationIdRef.current = createModelConversationId();
7968
+ setChatMessages([]);
7969
+ setChatInput("");
7970
+ setChatStatus("");
7971
+ setIsChatting(false);
7972
+ }, []);
7781
7973
  React.useEffect(() => {
7782
7974
  state.setSelectedSimulationName(effectiveSimulationName);
7783
7975
  }, [effectiveSimulationName]);
@@ -8038,15 +8230,18 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8038
8230
  state.setToolResultJson("");
8039
8231
  state.setToolResultError("");
8040
8232
  setHasRun(false);
8233
+ const requestConversationId = modelConversationIdRef.current;
8041
8234
  try {
8042
8235
  const messages = nextMessages.map((message) => ({
8043
8236
  role: message.role,
8044
- content: message.content
8045
- }));
8237
+ content: message.content.trim()
8238
+ })).filter((message) => message.content.length > 0);
8046
8239
  let data;
8047
8240
  if (modelChatHandler) data = await modelChatHandler({
8241
+ conversationId: requestConversationId,
8048
8242
  provider: modelProvider,
8049
8243
  modelId,
8244
+ host: state.activeHost,
8050
8245
  messages,
8051
8246
  tools: modelCallableTools,
8052
8247
  appContext: state.modelAppContext ?? void 0
@@ -8057,8 +8252,10 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8057
8252
  method: "POST",
8058
8253
  headers: { "Content-Type": "application/json" },
8059
8254
  body: JSON.stringify({
8255
+ conversationId: requestConversationId,
8060
8256
  provider: modelProvider,
8061
8257
  modelId,
8258
+ host: state.activeHost,
8062
8259
  messages,
8063
8260
  appContext: state.modelAppContext ?? void 0
8064
8261
  })
@@ -8067,6 +8264,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8067
8264
  if (!res.ok || data.error) throw new Error(data.error ?? `Model request failed (${res.status})`);
8068
8265
  }
8069
8266
  if (data.error) throw new Error(data.error);
8267
+ if (requestConversationId !== modelConversationIdRef.current) return;
8070
8268
  let rendersApp = false;
8071
8269
  const toolCalls = data.toolCalls ?? [];
8072
8270
  for (let index = toolCalls.length - 1; index >= 0; index--) {
@@ -8088,7 +8286,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8088
8286
  const assistantMessage = {
8089
8287
  id: `assistant-${Date.now()}`,
8090
8288
  role: "assistant",
8091
- content: data.text ?? (toolCalls.length > 0 ? "I called the MCP tool and rendered the app below." : "The model returned an empty response."),
8289
+ content: data.text ?? (rendersApp ? "I called the MCP tool and rendered the app below." : toolCalls.length > 0 ? "I called the MCP tool." : "The model returned an empty response."),
8092
8290
  toolCalls: toolCalls.map((call) => ({
8093
8291
  name: call.name,
8094
8292
  arguments: call.arguments,
@@ -8104,6 +8302,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8104
8302
  });
8105
8303
  setChatStatus("");
8106
8304
  } catch (err) {
8305
+ if (requestConversationId !== modelConversationIdRef.current) return;
8107
8306
  const message = err instanceof Error ? err.message : String(err);
8108
8307
  setChatMessages((messages) => [...messages, {
8109
8308
  id: `assistant-error-${Date.now()}`,
@@ -8112,7 +8311,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8112
8311
  }]);
8113
8312
  setChatStatus(message);
8114
8313
  } finally {
8115
- setIsChatting(false);
8314
+ if (requestConversationId === modelConversationIdRef.current) setIsChatting(false);
8116
8315
  }
8117
8316
  }, [
8118
8317
  canUseModelChat,
@@ -8308,10 +8507,19 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8308
8507
  children: /* @__PURE__ */ jsx("span", {
8309
8508
  className: "text-sm text-center max-w-xs",
8310
8509
  style: { color: "var(--color-text-secondary)" },
8311
- children: isEmbedded ? "No tools with UI resources in this app" : 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"
8510
+ children: isEmbedded ? "No tools in this app" : isError ? "Could not connect to MCP server" : isConnected ? "No tools found on this server" : serverUrl ? "Connecting…" : "Enter an MCP server URL to get started"
8312
8511
  })
8313
8512
  });
8314
- } else if (showEmptyState) content = /* @__PURE__ */ jsx("div", {
8513
+ } else if (!selectedToolInfo?.resource) content = /* @__PURE__ */ jsx("div", {
8514
+ className: "h-full w-full flex items-center justify-center",
8515
+ style: { background: iframeBg },
8516
+ children: /* @__PURE__ */ jsx("span", {
8517
+ className: "text-sm text-center max-w-xs",
8518
+ style: { color: "var(--color-text-secondary)" },
8519
+ children: "Tool does not render a UI"
8520
+ })
8521
+ });
8522
+ else if (showEmptyState) content = /* @__PURE__ */ jsx("div", {
8315
8523
  className: "h-full w-full flex items-center justify-center",
8316
8524
  style: { background: iframeBg },
8317
8525
  children: /* @__PURE__ */ jsxs("span", {
@@ -8435,31 +8643,9 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8435
8643
  children: /* @__PURE__ */ jsx("path", { d: "M0 0L10 6L0 12V0Z" })
8436
8644
  }), "Run"]
8437
8645
  }) : void 0;
8438
- const headerProdResourcesControl = !hideInspectorModes && !demoMode && !isEmbedded ? /* @__PURE__ */ jsxs("div", {
8439
- className: "flex shrink-0 items-center gap-1.5 text-[11px] font-medium",
8440
- style: { color: "var(--color-text-secondary)" },
8441
- children: [
8442
- /* @__PURE__ */ jsx("input", {
8443
- id: prodResourcesId,
8444
- type: "checkbox",
8445
- checked: prodResources,
8446
- onChange: (event) => setProdResources(event.currentTarget.checked),
8447
- className: "h-3.5 w-3.5 accent-[var(--color-text-primary)]"
8448
- }),
8449
- /* @__PURE__ */ jsx("label", {
8450
- htmlFor: prodResourcesId,
8451
- className: "cursor-pointer select-none",
8452
- children: "Prod Resources"
8453
- }),
8454
- /* @__PURE__ */ jsx(HelpIcon, {
8455
- tooltip: "Load resources from dist/ builds instead of HMR",
8456
- docsPath: "app-framework/cli/dev#prod-tools-and-prod-resources-flags"
8457
- })
8458
- ]
8459
- }) : null;
8460
- const headerAction = runButton || headerProdResourcesControl ? /* @__PURE__ */ jsxs("div", {
8461
- className: "flex min-w-0 items-center gap-2",
8462
- children: [runButton, headerProdResourcesControl]
8646
+ const headerAction = runButton ? /* @__PURE__ */ jsx("div", {
8647
+ className: "flex min-w-0 items-center",
8648
+ children: runButton
8463
8649
  }) : void 0;
8464
8650
  const conversationContent = ShellConversation ? /* @__PURE__ */ jsx(ShellConversation, {
8465
8651
  screenWidth: state.screenWidth,
@@ -8482,6 +8668,11 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8482
8668
  children: content
8483
8669
  }) : content;
8484
8670
  const rootSizing = isEmbedded ? "h-full w-full" : "h-screen w-screen";
8671
+ const getJsonPanelFlexGrow = (value) => {
8672
+ const text = value || "";
8673
+ const lineCount = text.split("\n").length;
8674
+ return Math.max(1, Math.min(8, lineCount + Math.floor(text.length / 500)));
8675
+ };
8485
8676
  if (!showSidebar) return /* @__PURE__ */ jsx(ThemeProvider, {
8486
8677
  theme: state.theme,
8487
8678
  applyTheme,
@@ -8497,6 +8688,10 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8497
8688
  children: [/* @__PURE__ */ jsx(SimpleSidebar, {
8498
8689
  rootRef,
8499
8690
  fillParent: isEmbedded,
8691
+ sidebarWidth: state.sidebarWidth,
8692
+ rightSidebarWidth: state.rightSidebarWidth,
8693
+ onSidebarWidthChange: state.setSidebarWidth,
8694
+ onRightSidebarWidthChange: state.setRightSidebarWidth,
8500
8695
  controls: /* @__PURE__ */ jsxs("div", {
8501
8696
  className: "space-y-1",
8502
8697
  children: [
@@ -8521,7 +8716,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8521
8716
  })
8522
8717
  }), !demoMode && /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
8523
8718
  label: "Authentication",
8524
- defaultCollapsed: authType === "none",
8719
+ defaultCollapsed: false,
8525
8720
  children: /* @__PURE__ */ jsxs("div", {
8526
8721
  className: "space-y-1",
8527
8722
  children: [
@@ -8605,70 +8800,6 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8605
8800
  ]
8606
8801
  })
8607
8802
  }, `auth-${authType === "none" ? "none" : "active"}`)] }),
8608
- canUseModelChat && /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
8609
- label: "Model Chat",
8610
- defaultCollapsed: true,
8611
- tooltip: "Talk to this MCP server through a model",
8612
- docsPath: "testing/evals",
8613
- children: /* @__PURE__ */ jsxs("div", {
8614
- className: "space-y-2",
8615
- children: [/* @__PURE__ */ jsxs("div", {
8616
- className: "grid grid-cols-2 gap-2",
8617
- children: [/* @__PURE__ */ jsx(SidebarControl, {
8618
- label: "Provider",
8619
- children: /* @__PURE__ */ jsx(SidebarSelect, {
8620
- value: modelProvider,
8621
- onChange: handleModelProviderChange,
8622
- options: modelProviderOptions.map((provider) => ({
8623
- value: provider.id,
8624
- label: provider.label ?? provider.id
8625
- }))
8626
- })
8627
- }), /* @__PURE__ */ jsx(SidebarControl, {
8628
- label: "Model",
8629
- children: selectedProviderModelOptions.length > 0 ? /* @__PURE__ */ jsx(SidebarSelect, {
8630
- value: modelId,
8631
- onChange: setModelId,
8632
- options: selectedProviderModelOptions.map((model) => ({
8633
- value: model,
8634
- label: model
8635
- }))
8636
- }) : /* @__PURE__ */ jsx(SidebarInput, {
8637
- value: modelId,
8638
- onChange: setModelId,
8639
- applyOnBlur: true,
8640
- placeholder: getDefaultModelId(modelProvider)
8641
- })
8642
- })]
8643
- }), usesApiKeyUi && /* @__PURE__ */ jsxs(SidebarControl, {
8644
- label: "API Key",
8645
- children: [/* @__PURE__ */ jsxs("div", {
8646
- className: "flex gap-1",
8647
- children: [/* @__PURE__ */ jsx(SidebarInput, {
8648
- type: "password",
8649
- autoComplete: "new-password",
8650
- value: apiKeyDraft,
8651
- onChange: setApiKeyDraft,
8652
- placeholder: keyStatus.hasKey ? "Saved locally" : `Paste ${modelProvider} key`
8653
- }), /* @__PURE__ */ jsx("button", {
8654
- type: "button",
8655
- onClick: handleSaveApiKey,
8656
- disabled: isKeyStatusLoading || !apiKeyDraft && !keyStatus.hasKey,
8657
- className: "h-7 rounded-md px-2 text-xs font-medium transition-opacity disabled:opacity-40",
8658
- style: {
8659
- backgroundColor: "var(--color-text-primary)",
8660
- color: "var(--color-background-primary)"
8661
- },
8662
- children: apiKeyDraft ? "Save" : "Clear"
8663
- })]
8664
- }), /* @__PURE__ */ jsx("div", {
8665
- className: "mt-1 text-[9px]",
8666
- style: { color: "var(--color-text-secondary)" },
8667
- children: keyMessage || (isKeyStatusLoading ? "Checking saved key..." : keyStatus.hasKey ? `Key saved ${keyStatus.storage ?? "locally"}` : "Paste a key or use one already saved on this machine")
8668
- })]
8669
- })]
8670
- })
8671
- }),
8672
8803
  hasTools && /* @__PURE__ */ jsxs("div", {
8673
8804
  className: "grid grid-cols-2 gap-2",
8674
8805
  "data-testid": "tool-simulation-row",
@@ -8681,6 +8812,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8681
8812
  value: selectedToolName,
8682
8813
  onChange: (value) => {
8683
8814
  setIsLiveMcpRender(false);
8815
+ resetAppContextForSelectionChange();
8684
8816
  setSelectedToolName(value);
8685
8817
  },
8686
8818
  options: toolNames.map((name) => {
@@ -8713,6 +8845,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8713
8845
  onChange: (value) => {
8714
8846
  if (value === "__live__") return;
8715
8847
  setIsLiveMcpRender(false);
8848
+ resetAppContextForSelectionChange();
8716
8849
  setActiveSimulationName(value === "__none__" ? null : value);
8717
8850
  },
8718
8851
  options: [
@@ -8774,6 +8907,116 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
8774
8907
  })
8775
8908
  })]
8776
8909
  }),
8910
+ !hideInspectorModes && !demoMode && !isEmbedded && /* @__PURE__ */ jsx("div", {
8911
+ className: "py-1",
8912
+ children: /* @__PURE__ */ jsx(SidebarCheckbox, {
8913
+ checked: prodResources,
8914
+ onChange: setProdResources,
8915
+ label: "Prod Resources",
8916
+ tooltip: "Load resources from dist/ builds instead of HMR",
8917
+ docsPath: "app-framework/cli/dev#prod-tools-and-prod-resources-flags"
8918
+ })
8919
+ }),
8920
+ canUseModelChat && /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
8921
+ label: "Model Chat",
8922
+ defaultCollapsed: false,
8923
+ tooltip: "Talk to this MCP server through a model",
8924
+ docsPath: "testing/evals",
8925
+ children: /* @__PURE__ */ jsxs("div", {
8926
+ className: "space-y-1",
8927
+ children: [/* @__PURE__ */ jsxs("div", {
8928
+ className: "grid grid-cols-[0.7fr_minmax(0,1fr)_1.75rem] items-end gap-2",
8929
+ children: [
8930
+ /* @__PURE__ */ jsx(SidebarControl, {
8931
+ label: "Provider",
8932
+ children: /* @__PURE__ */ jsx(SidebarSelect, {
8933
+ value: modelProvider,
8934
+ onChange: handleModelProviderChange,
8935
+ options: modelProviderOptions.map((provider) => ({
8936
+ value: provider.id,
8937
+ label: provider.label ?? provider.id
8938
+ }))
8939
+ })
8940
+ }),
8941
+ /* @__PURE__ */ jsx(SidebarControl, {
8942
+ label: "Model",
8943
+ children: selectedProviderModelOptions.length > 0 ? /* @__PURE__ */ jsx(SidebarSelect, {
8944
+ value: modelId,
8945
+ onChange: setModelId,
8946
+ options: selectedProviderModelOptions.map((model) => ({
8947
+ value: model,
8948
+ label: model
8949
+ }))
8950
+ }) : /* @__PURE__ */ jsx(SidebarInput, {
8951
+ value: modelId,
8952
+ onChange: setModelId,
8953
+ applyOnBlur: true,
8954
+ placeholder: getDefaultModelId(modelProvider)
8955
+ })
8956
+ }),
8957
+ /* @__PURE__ */ jsxs("div", {
8958
+ className: "group relative flex h-7 items-center self-end",
8959
+ children: [/* @__PURE__ */ jsx("button", {
8960
+ type: "button",
8961
+ onClick: handleResetModelConversation,
8962
+ disabled: chatMessages.length === 0 && !isChatting && !chatStatus,
8963
+ "aria-label": "Reset model conversation",
8964
+ "aria-describedby": "reset-model-conversation-tooltip",
8965
+ title: "Reset conversation",
8966
+ className: "flex h-7 w-7 cursor-pointer items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-40",
8967
+ style: {
8968
+ backgroundColor: "var(--color-background-primary)",
8969
+ color: "var(--color-text-primary)"
8970
+ },
8971
+ children: /* @__PURE__ */ jsx("svg", {
8972
+ width: "18",
8973
+ height: "18",
8974
+ viewBox: "0 0 24 24",
8975
+ fill: "currentColor",
8976
+ "aria-hidden": "true",
8977
+ children: /* @__PURE__ */ jsx("path", { d: "M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-8 3.58-8 8s3.58 8 8 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h8V3z" })
8978
+ })
8979
+ }), /* @__PURE__ */ jsx("span", {
8980
+ id: "reset-model-conversation-tooltip",
8981
+ role: "tooltip",
8982
+ className: "pointer-events-none absolute right-0 top-full z-[1000] mt-1 hidden whitespace-nowrap rounded px-2 py-1 text-[11px] font-normal leading-tight group-focus-within:block group-hover:block",
8983
+ style: {
8984
+ backgroundColor: "var(--color-text-primary)",
8985
+ color: "var(--color-background-primary)"
8986
+ },
8987
+ children: "Reset conversation"
8988
+ })]
8989
+ })
8990
+ ]
8991
+ }), usesApiKeyUi && /* @__PURE__ */ jsxs(SidebarControl, {
8992
+ label: "API Key",
8993
+ children: [/* @__PURE__ */ jsxs("div", {
8994
+ className: "flex gap-1",
8995
+ children: [/* @__PURE__ */ jsx(SidebarInput, {
8996
+ type: "password",
8997
+ autoComplete: "new-password",
8998
+ value: apiKeyDraft,
8999
+ onChange: setApiKeyDraft,
9000
+ placeholder: keyStatus.hasKey ? "Saved locally" : `Paste ${modelProvider} key`
9001
+ }), /* @__PURE__ */ jsx("button", {
9002
+ type: "button",
9003
+ onClick: handleSaveApiKey,
9004
+ disabled: isKeyStatusLoading || !apiKeyDraft && !keyStatus.hasKey,
9005
+ className: "h-7 rounded-md px-2 text-xs font-medium transition-opacity disabled:opacity-40",
9006
+ style: {
9007
+ backgroundColor: "var(--color-text-primary)",
9008
+ color: "var(--color-background-primary)"
9009
+ },
9010
+ children: apiKeyDraft ? "Save" : "Clear"
9011
+ })]
9012
+ }), /* @__PURE__ */ jsx("div", {
9013
+ className: "mt-1 text-[9px]",
9014
+ style: { color: "var(--color-text-secondary)" },
9015
+ children: keyMessage || (isKeyStatusLoading ? "Checking saved key..." : keyStatus.hasKey ? `Key saved ${keyStatus.storage ?? "locally"}` : "Paste a key or use one already saved on this machine")
9016
+ })]
9017
+ })]
9018
+ })
9019
+ }),
8777
9020
  /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
8778
9021
  label: "Host Context",
8779
9022
  defaultCollapsed: false,
@@ -9036,14 +9279,24 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
9036
9279
  })
9037
9280
  ]
9038
9281
  })
9039
- }),
9282
+ })
9283
+ ]
9284
+ }),
9285
+ rightControls: /* @__PURE__ */ jsxs("div", {
9286
+ className: "flex h-full min-h-0 flex-col gap-3",
9287
+ children: [
9040
9288
  /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
9041
9289
  label: "App Context",
9042
- defaultCollapsed: true,
9290
+ defaultCollapsed: false,
9043
9291
  tooltip: "App-provided context shared with the model",
9044
9292
  docsPath: "app-framework/hooks/use-app-state",
9293
+ className: "flex min-h-0 flex-col",
9294
+ contentClassName: "min-h-0 flex-1",
9295
+ style: { flex: `${getJsonPanelFlexGrow(state.modelContextJson)} 1 0` },
9296
+ tooltipPlacement: "left",
9045
9297
  children: /* @__PURE__ */ jsx(SidebarTextarea, {
9046
9298
  value: state.modelContextJson,
9299
+ "data-testid": "app-context-textarea",
9047
9300
  onChange: (json) => state.validateJSON(json, state.setModelContextJson, state.setModelContextError),
9048
9301
  onFocus: () => state.setEditingField("modelContext"),
9049
9302
  onBlur: () => state.commitJSON(state.modelContextJson, state.setModelContextError, (parsed) => {
@@ -9054,7 +9307,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
9054
9307
  });
9055
9308
  }),
9056
9309
  error: state.modelContextError,
9057
- maxRows: 8
9310
+ fill: true
9058
9311
  })
9059
9312
  }),
9060
9313
  /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
@@ -9062,6 +9315,10 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
9062
9315
  defaultCollapsed: false,
9063
9316
  tooltip: "Arguments passed to the tool",
9064
9317
  docsPath: "app-framework/hooks/use-tool-data",
9318
+ className: "flex min-h-0 flex-col",
9319
+ contentClassName: "min-h-0 flex-1",
9320
+ style: { flex: `${getJsonPanelFlexGrow(state.toolInputJson)} 1 0` },
9321
+ tooltipPlacement: "left",
9065
9322
  children: /* @__PURE__ */ jsx(SidebarTextarea, {
9066
9323
  value: state.toolInputJson,
9067
9324
  "data-testid": "tool-input-textarea",
@@ -9069,7 +9326,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
9069
9326
  onFocus: () => state.setEditingField("toolInput"),
9070
9327
  onBlur: () => state.commitJSON(state.toolInputJson, state.setToolInputError, (parsed) => state.setToolInput(parsed ?? {})),
9071
9328
  error: state.toolInputError,
9072
- maxRows: 8
9329
+ fill: true
9073
9330
  })
9074
9331
  }),
9075
9332
  /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
@@ -9078,6 +9335,10 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
9078
9335
  tooltip: "Structured content returned by the tool",
9079
9336
  docsPath: "app-framework/hooks/use-tool-data",
9080
9337
  "data-testid": "tool-result-section",
9338
+ className: "flex min-h-0 flex-col",
9339
+ contentClassName: "min-h-0 flex-1",
9340
+ style: { flex: `${getJsonPanelFlexGrow(state.toolResultJson)} 1 0` },
9341
+ tooltipPlacement: "left",
9081
9342
  children: /* @__PURE__ */ jsx(SidebarTextarea, {
9082
9343
  value: state.toolResultJson,
9083
9344
  "data-testid": "tool-result-textarea",
@@ -9095,7 +9356,7 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
9095
9356
  }
9096
9357
  }),
9097
9358
  error: state.toolResultError,
9098
- maxRows: 8
9359
+ fill: true
9099
9360
  })
9100
9361
  })
9101
9362
  ]
@@ -9114,4 +9375,4 @@ function Inspector({ children, app, simulations: initialSimulationsProp = EMPTY_
9114
9375
  //#endregion
9115
9376
  export { cn as C, registerHostShell as S, extractResourceCSP as _, SidebarCollapsibleControl as a, getHostShell as b, SidebarSelect as c, SimpleSidebar as d, ThemeProvider as f, IframeResource as g, useInspectorState as h, SidebarCheckbox as i, SidebarTextarea as l, useMcpConnection as m, flattenAppToSimulations as n, SidebarControl as o, useThemeContext as p, resolveServerToolResult as r, SidebarInput as s, Inspector as t, SidebarToggle as u, McpAppHost as v, DEFAULT_STYLE_VARIABLES as w, getRegisteredHosts as x, SCREEN_WIDTHS as y };
9116
9377
 
9117
- //# sourceMappingURL=inspector-D0TWNx_T.js.map
9378
+ //# sourceMappingURL=inspector-C6n8zap3.js.map