sunpeak 0.18.2 → 0.18.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 (57) hide show
  1. package/bin/commands/inspect.mjs +392 -4
  2. package/bin/lib/sandbox-server.mjs +11 -1
  3. package/dist/chatgpt/index.cjs +1 -1
  4. package/dist/chatgpt/index.js +1 -1
  5. package/dist/claude/index.cjs +1 -1
  6. package/dist/claude/index.js +1 -1
  7. package/dist/hooks/index.d.ts +1 -0
  8. package/dist/hooks/use-request-teardown.d.ts +21 -0
  9. package/dist/host/chatgpt/index.cjs +1 -1
  10. package/dist/host/chatgpt/index.js +1 -1
  11. package/dist/index.cjs +110 -70
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.js +68 -31
  15. package/dist/index.js.map +1 -1
  16. package/dist/inspector/hosts.d.ts +12 -0
  17. package/dist/inspector/index.cjs +1 -1
  18. package/dist/inspector/index.js +1 -1
  19. package/dist/inspector/mcp-app-host.d.ts +3 -0
  20. package/dist/inspector/simple-sidebar.d.ts +1 -1
  21. package/dist/inspector/use-mcp-connection.d.ts +9 -1
  22. package/dist/{inspector-ClhpqKLi.js → inspector-CjSoXm6N.js} +497 -117
  23. package/dist/inspector-CjSoXm6N.js.map +1 -0
  24. package/dist/{inspector-CByJjmPD.cjs → inspector-DRD_Q66E.cjs} +498 -118
  25. package/dist/inspector-DRD_Q66E.cjs.map +1 -0
  26. package/dist/mcp/index.cjs +38 -33
  27. package/dist/mcp/index.cjs.map +1 -1
  28. package/dist/mcp/index.js +35 -30
  29. package/dist/mcp/index.js.map +1 -1
  30. package/dist/style.css +8 -0
  31. package/dist/{use-app-X7JbGskk.js → use-app-BNbz1uzj.js} +51 -42
  32. package/dist/use-app-BNbz1uzj.js.map +1 -0
  33. package/dist/{use-app-D2h-aiyr.cjs → use-app-Dqh20JPP.cjs} +93 -72
  34. package/dist/use-app-Dqh20JPP.cjs.map +1 -0
  35. package/package.json +2 -2
  36. package/template/dist/albums/albums.html +3 -3
  37. package/template/dist/albums/albums.json +1 -1
  38. package/template/dist/carousel/carousel.html +3 -3
  39. package/template/dist/carousel/carousel.json +1 -1
  40. package/template/dist/map/map.html +3 -3
  41. package/template/dist/map/map.json +1 -1
  42. package/template/dist/review/review.html +3 -3
  43. package/template/dist/review/review.json +1 -1
  44. package/template/node_modules/.vite/deps/_metadata.json +3 -3
  45. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js +51 -42
  46. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js.map +1 -1
  47. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js +56 -50
  48. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js.map +1 -1
  49. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js +52 -43
  50. package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js.map +1 -1
  51. package/template/node_modules/.vite-mcp/deps/_metadata.json +22 -22
  52. package/template/tests/e2e/albums.spec.ts +19 -15
  53. package/template/tests/live/albums.spec.ts +10 -0
  54. package/dist/inspector-CByJjmPD.cjs.map +0 -1
  55. package/dist/inspector-ClhpqKLi.js.map +0 -1
  56. package/dist/use-app-D2h-aiyr.cjs.map +0 -1
  57. package/dist/use-app-X7JbGskk.js.map +0 -1
@@ -486,7 +486,9 @@ function ClaudeConversation({ children, screenWidth, displayMode, platform, onRe
486
486
  style: {
487
487
  transform: "translate(0)",
488
488
  backgroundColor: "var(--sim-bg-conversation, var(--color-background-primary))",
489
- color: "var(--color-text-primary)"
489
+ color: "var(--color-text-primary)",
490
+ fontFamily: "var(--font-sans)",
491
+ WebkitFontSmoothing: "antialiased"
490
492
  },
491
493
  children: [
492
494
  isFullscreen && /* @__PURE__ */ jsxs("div", {
@@ -520,17 +522,20 @@ function ClaudeConversation({ children, screenWidth, displayMode, platform, onRe
520
522
  style: { backgroundColor: "var(--sim-bg-conversation, var(--color-background-primary))" },
521
523
  children: /* @__PURE__ */ jsx("div", {
522
524
  className: "max-w-[48rem] mx-auto",
523
- children: /* @__PURE__ */ jsx("input", {
524
- type: "text",
525
- name: "userInput",
526
- disabled: true,
527
- placeholder: "Reply to sunpeak...",
528
- className: "w-full rounded-xl px-4 py-2.5 text-sm",
525
+ children: /* @__PURE__ */ jsx("div", {
526
+ className: "relative rounded-[20px] px-4 py-2.5",
529
527
  style: {
530
528
  backgroundColor: "var(--sim-bg-reply-input, var(--color-background-secondary))",
531
- border: "1px solid var(--color-border-primary)",
532
- color: "var(--color-text-primary)"
533
- }
529
+ boxShadow: "0 4px 20px rgba(0, 0, 0, 0.035), 0 0 0 0.5px var(--color-border-tertiary)"
530
+ },
531
+ children: /* @__PURE__ */ jsx("div", {
532
+ className: "w-full text-base outline-none opacity-50",
533
+ style: {
534
+ lineHeight: "1.4",
535
+ color: "var(--color-text-tertiary)"
536
+ },
537
+ children: "Reply to sunpeak..."
538
+ })
534
539
  })
535
540
  })
536
541
  })
@@ -558,8 +563,14 @@ function ClaudeConversation({ children, screenWidth, displayMode, platform, onRe
558
563
  children: /* @__PURE__ */ jsx("div", {
559
564
  className: "max-w-[48rem] mx-auto flex justify-end",
560
565
  children: /* @__PURE__ */ jsx("div", {
561
- className: "inline-block rounded-2xl px-4 py-2.5 text-sm max-w-[85%]",
562
- style: { backgroundColor: "var(--sim-bg-user-bubble, var(--color-background-tertiary))" },
566
+ className: "inline-flex rounded-xl max-w-[85%] break-words",
567
+ style: {
568
+ padding: "10px 16px",
569
+ lineHeight: "22.4px",
570
+ fontSize: "16px",
571
+ fontWeight: 430,
572
+ backgroundColor: "var(--sim-bg-user-bubble, var(--color-background-tertiary))"
573
+ },
563
574
  children: userMessage
564
575
  })
565
576
  })
@@ -629,17 +640,18 @@ function ClaudeConversation({ children, screenWidth, displayMode, platform, onRe
629
640
  children: /* @__PURE__ */ jsx("div", {
630
641
  className: "max-w-[48rem] mx-auto px-4 py-4",
631
642
  children: /* @__PURE__ */ jsx("div", {
632
- className: "relative",
633
- children: /* @__PURE__ */ jsx("input", {
634
- type: "text",
635
- name: "userInput",
636
- disabled: true,
637
- placeholder: "Reply to sunpeak...",
638
- className: "w-full rounded-xl px-4 py-2.5 text-sm",
643
+ className: "relative rounded-[20px] px-4 py-2.5",
644
+ style: {
645
+ backgroundColor: "var(--sim-bg-reply-input, var(--color-background-secondary))",
646
+ boxShadow: "0 4px 20px rgba(0, 0, 0, 0.035), 0 0 0 0.5px var(--color-border-tertiary)"
647
+ },
648
+ children: /* @__PURE__ */ jsx("div", {
649
+ className: "w-full text-base outline-none opacity-50",
639
650
  style: {
640
- backgroundColor: "var(--sim-bg-reply-input, var(--color-background-secondary))",
641
- border: "1px solid var(--color-border-primary)"
642
- }
651
+ lineHeight: "1.4",
652
+ color: "var(--color-text-tertiary)"
653
+ },
654
+ children: "Reply to sunpeak..."
643
655
  })
644
656
  })
645
657
  })
@@ -650,17 +662,32 @@ function ClaudeConversation({ children, screenWidth, displayMode, platform, onRe
650
662
  }
651
663
  //#endregion
652
664
  //#region src/claude/claude-host.ts
665
+ /**
666
+ * Claude host version info — matches what Claude reports via the MCP protocol.
667
+ * Verified against production Claude on 2026-03-25.
668
+ */
653
669
  var CLAUDE_HOST_INFO = {
654
670
  name: "Claude",
655
671
  version: "1.0.0"
656
672
  };
673
+ /**
674
+ * Claude host capabilities — matches what Claude reports via the MCP protocol.
675
+ * Verified against production Claude on 2026-03-25.
676
+ *
677
+ * Notable: Claude supports downloadFile, updateModelContext.image, and
678
+ * message.text. serverTools and serverResources both report listChanged.
679
+ * No sandbox.permissions (no microphone etc.). No PiP display mode.
680
+ */
657
681
  var CLAUDE_HOST_CAPABILITIES = {
658
682
  openLinks: {},
659
- serverTools: {},
660
- serverResources: {},
661
683
  downloadFile: {},
684
+ serverTools: { listChanged: true },
685
+ serverResources: { listChanged: true },
662
686
  logging: {},
663
- updateModelContext: { text: {} },
687
+ updateModelContext: {
688
+ text: {},
689
+ image: {}
690
+ },
664
691
  message: { text: {} },
665
692
  sandbox: {}
666
693
  };
@@ -679,35 +706,100 @@ registerHostShell({
679
706
  applyTheme: applyClaudeTheme,
680
707
  hostInfo: CLAUDE_HOST_INFO,
681
708
  hostCapabilities: CLAUDE_HOST_CAPABILITIES,
682
- userAgent: "claude",
683
709
  styleVariables: {
684
710
  ...DEFAULT_STYLE_VARIABLES,
685
- "--color-background-primary": "light-dark(#faf9f5, #262624)",
686
- "--color-background-secondary": "light-dark(#ffffff, #3a3935)",
687
- "--color-background-tertiary": "light-dark(#e8e4dc, #4a4843)",
688
- "--color-background-inverse": "light-dark(#2b2a27, #f3f0e8)",
689
- "--color-text-primary": "light-dark(#2d2b27, #e8e4dc)",
690
- "--color-text-secondary": "light-dark(#6b6560, #9b9690)",
691
- "--color-text-tertiary": "light-dark(#9b9690, #6b6560)",
692
- "--color-text-inverse": "light-dark(#e8e4dc, #2d2b27)",
693
- "--color-border-primary": "light-dark(#e0ddd5, #4a4843)",
694
- "--color-border-secondary": "light-dark(#d5d1c8, #5a5753)",
695
- "--color-border-tertiary": "light-dark(#f0ede5, #3a3935)"
711
+ "--color-background-primary": "light-dark(rgba(255, 255, 255, 1), rgba(48, 48, 46, 1))",
712
+ "--color-background-secondary": "light-dark(rgba(245, 244, 237, 1), rgba(38, 38, 36, 1))",
713
+ "--color-background-tertiary": "light-dark(rgba(250, 249, 245, 1), rgba(20, 20, 19, 1))",
714
+ "--color-background-inverse": "light-dark(rgba(20, 20, 19, 1), rgba(250, 249, 245, 1))",
715
+ "--color-background-ghost": "light-dark(rgba(255, 255, 255, 0), rgba(48, 48, 46, 0))",
716
+ "--color-background-info": "light-dark(rgba(214, 228, 246, 1), rgba(37, 62, 95, 1))",
717
+ "--color-background-danger": "light-dark(rgba(247, 236, 236, 1), rgba(96, 42, 40, 1))",
718
+ "--color-background-success": "light-dark(rgba(233, 241, 220, 1), rgba(27, 70, 20, 1))",
719
+ "--color-background-warning": "light-dark(rgba(246, 238, 223, 1), rgba(72, 58, 15, 1))",
720
+ "--color-background-disabled": "light-dark(rgba(255, 255, 255, 0.5), rgba(48, 48, 46, 0.5))",
721
+ "--color-text-primary": "light-dark(rgba(20, 20, 19, 1), rgba(250, 249, 245, 1))",
722
+ "--color-text-secondary": "light-dark(rgba(61, 61, 58, 1), rgba(194, 192, 182, 1))",
723
+ "--color-text-tertiary": "light-dark(rgba(115, 114, 108, 1), rgba(156, 154, 146, 1))",
724
+ "--color-text-inverse": "light-dark(rgba(255, 255, 255, 1), rgba(20, 20, 19, 1))",
725
+ "--color-text-ghost": "light-dark(rgba(115, 114, 108, 0.5), rgba(156, 154, 146, 0.5))",
726
+ "--color-text-info": "light-dark(rgba(50, 102, 173, 1), rgba(128, 170, 221, 1))",
727
+ "--color-text-danger": "light-dark(rgba(127, 44, 40, 1), rgba(238, 136, 132, 1))",
728
+ "--color-text-success": "light-dark(rgba(38, 91, 25, 1), rgba(122, 185, 72, 1))",
729
+ "--color-text-warning": "light-dark(rgba(90, 72, 21, 1), rgba(209, 160, 65, 1))",
730
+ "--color-text-disabled": "light-dark(rgba(20, 20, 19, 0.5), rgba(250, 249, 245, 0.5))",
731
+ "--color-border-primary": "light-dark(rgba(31, 30, 29, 0.4), rgba(222, 220, 209, 0.4))",
732
+ "--color-border-secondary": "light-dark(rgba(31, 30, 29, 0.3), rgba(222, 220, 209, 0.3))",
733
+ "--color-border-tertiary": "light-dark(rgba(31, 30, 29, 0.15), rgba(222, 220, 209, 0.15))",
734
+ "--color-border-inverse": "light-dark(rgba(255, 255, 255, 0.3), rgba(20, 20, 19, 0.15))",
735
+ "--color-border-ghost": "light-dark(rgba(31, 30, 29, 0), rgba(222, 220, 209, 0))",
736
+ "--color-border-info": "light-dark(rgba(70, 130, 213, 1), rgba(70, 130, 213, 1))",
737
+ "--color-border-danger": "light-dark(rgba(167, 61, 57, 1), rgba(205, 92, 88, 1))",
738
+ "--color-border-success": "light-dark(rgba(67, 116, 38, 1), rgba(89, 145, 48, 1))",
739
+ "--color-border-warning": "light-dark(rgba(128, 92, 31, 1), rgba(168, 120, 41, 1))",
740
+ "--color-border-disabled": "light-dark(rgba(31, 30, 29, 0.1), rgba(222, 220, 209, 0.1))",
741
+ "--color-ring-primary": "light-dark(rgba(20, 20, 19, 0.7), rgba(250, 249, 245, 0.7))",
742
+ "--color-ring-secondary": "light-dark(rgba(61, 61, 58, 0.7), rgba(194, 192, 182, 0.7))",
743
+ "--color-ring-inverse": "light-dark(rgba(255, 255, 255, 0.7), rgba(20, 20, 19, 0.7))",
744
+ "--color-ring-info": "light-dark(rgba(50, 102, 173, 0.5), rgba(128, 170, 221, 0.5))",
745
+ "--color-ring-danger": "light-dark(rgba(167, 61, 57, 0.5), rgba(205, 92, 88, 0.5))",
746
+ "--color-ring-success": "light-dark(rgba(67, 116, 38, 0.5), rgba(89, 145, 48, 0.5))",
747
+ "--color-ring-warning": "light-dark(rgba(128, 92, 31, 0.5), rgba(168, 120, 41, 0.5))",
748
+ "--font-sans": "\"Anthropic Sans\", system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif",
749
+ "--font-mono": "ui-monospace, monospace",
750
+ "--font-text-xs-size": "12px",
751
+ "--font-text-sm-size": "14px",
752
+ "--font-text-md-size": "16px",
753
+ "--font-text-lg-size": "20px",
754
+ "--font-heading-xs-size": "12px",
755
+ "--font-heading-sm-size": "14px",
756
+ "--font-heading-md-size": "16px",
757
+ "--font-heading-lg-size": "20px",
758
+ "--font-heading-xl-size": "24px",
759
+ "--font-heading-2xl-size": "28px",
760
+ "--font-heading-3xl-size": "36px",
761
+ "--font-text-md-line-height": "1.4",
762
+ "--font-text-lg-line-height": "1.25",
763
+ "--font-heading-lg-line-height": "1.25",
764
+ "--font-heading-2xl-line-height": "1.1",
765
+ "--font-heading-3xl-line-height": "1",
766
+ "--border-radius-xs": "4px",
767
+ "--border-radius-sm": "6px",
768
+ "--border-radius-md": "8px",
769
+ "--border-radius-lg": "10px",
770
+ "--border-width-regular": "0.5px"
696
771
  },
697
772
  pageStyles: {
698
- "--sim-bg-sidebar": "light-dark(#f9f8f3, #252523)",
699
- "--sim-bg-conversation": "light-dark(#faf9f5, #262624)",
700
- "--sim-bg-user-bubble": "light-dark(#f1eee6, #141413)",
701
- "--sim-bg-reply-input": "light-dark(#ffffff, #30302e)"
702
- }
773
+ "--sim-bg-sidebar": "light-dark(rgb(250, 249, 245), rgb(38, 38, 36))",
774
+ "--sim-bg-conversation": "light-dark(rgb(250, 249, 245), rgb(38, 38, 36))",
775
+ "--sim-bg-user-bubble": "light-dark(rgb(240, 238, 230), rgb(20, 20, 19))",
776
+ "--sim-bg-reply-input": "light-dark(rgb(255, 255, 255), rgb(48, 48, 46))"
777
+ },
778
+ availableDisplayModes: ["inline", "fullscreen"],
779
+ fontCss: `@font-face {
780
+ font-family: "Anthropic Sans";
781
+ src: url("https://assets-proxy.anthropic.com/claude-ai/v2/assets/v1/cc27851ad-CFxw3nG7.woff2") format("woff2");
782
+ font-weight: 300 800;
783
+ font-style: normal;
784
+ font-display: swap;
785
+ font-feature-settings: "dlig" 0;
786
+ }
787
+ @font-face {
788
+ font-family: "Anthropic Sans";
789
+ src: url("https://assets-proxy.anthropic.com/claude-ai/v2/assets/v1/c9d3a3a49-BI1hrwN4.woff2") format("woff2");
790
+ font-weight: 300 800;
791
+ font-style: italic;
792
+ font-display: swap;
793
+ font-feature-settings: "dlig" 0;
794
+ }`
703
795
  });
704
796
  //#endregion
705
- //#region ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.2.2_@modelcontextprotocol+sdk@1.27.1_zod@4.3.6__react-_e9bf7657371391a829fe8b4e289b253c/node_modules/@modelcontextprotocol/ext-apps/dist/src/app-bridge.js
706
- var G = "2026-01-26", m = union([literal("light"), literal("dark")]).describe("Color theme preference for the host environment."), D = union([
797
+ //#region ../../node_modules/.pnpm/@modelcontextprotocol+ext-apps@1.3.1_@modelcontextprotocol+sdk@1.27.1_zod@4.3.6__react-_cd1fb28fd87e0bddd0a29eba0721855d/node_modules/@modelcontextprotocol/ext-apps/dist/src/app-bridge.js
798
+ var G = "2026-01-26", p = union([literal("light"), literal("dark")]).describe("Color theme preference for the host environment."), D = union([
707
799
  literal("inline"),
708
800
  literal("fullscreen"),
709
801
  literal("pip")
710
- ]).describe("Display mode for UI presentation."), VQ = record(union([
802
+ ]).describe("Display mode for UI presentation."), AQ = record(union([
711
803
  literal("--color-background-primary"),
712
804
  literal("--color-background-secondary"),
713
805
  literal("--color-background-tertiary"),
@@ -809,7 +901,7 @@ for compatibility with Zod schema generation. Both are functionally equivalent f
809
901
  object({ isError: boolean().optional().describe("True if the host failed to open the URL (e.g., due to security policy).") }).passthrough();
810
902
  object({ isError: boolean().optional().describe("True if the download failed (e.g., user cancelled or host denied).") }).passthrough();
811
903
  object({ isError: boolean().optional().describe("True if the host rejected or failed to deliver the message.") }).passthrough();
812
- var F = object({
904
+ var L = object({
813
905
  method: literal("ui/notifications/sandbox-proxy-ready"),
814
906
  params: object({})
815
907
  }), j = object({
@@ -844,22 +936,25 @@ object({
844
936
  method: literal("ui/notifications/tool-cancelled"),
845
937
  params: object({ reason: string().optional().describe("Optional reason for the cancellation (e.g., \"user action\", \"timeout\").") })
846
938
  });
847
- var p = object({ fonts: string().optional() }), c = object({
848
- variables: VQ.optional().describe("CSS variables for theming the app."),
849
- css: p.optional().describe("CSS blocks that apps can inject.")
939
+ var c = object({ fonts: string().optional() }), n = object({
940
+ variables: AQ.optional().describe("CSS variables for theming the app."),
941
+ css: c.optional().describe("CSS blocks that apps can inject.")
850
942
  });
851
943
  object({
852
944
  method: literal("ui/resource-teardown"),
853
945
  params: object({})
854
946
  });
855
- var R = record(string(), unknown()), O = object({
947
+ var H = record(string(), unknown()), O = object({
856
948
  text: object({}).optional().describe("Host supports text content blocks."),
857
949
  image: object({}).optional().describe("Host supports image content blocks."),
858
950
  audio: object({}).optional().describe("Host supports audio content blocks."),
859
951
  resource: object({}).optional().describe("Host supports resource content blocks."),
860
952
  resourceLink: object({}).optional().describe("Host supports resource link content blocks."),
861
953
  structuredContent: object({}).optional().describe("Host supports structured content.")
862
- }), n = object({
954
+ }), M = object({
955
+ method: literal("ui/notifications/request-teardown"),
956
+ params: object({}).optional()
957
+ }), i = object({
863
958
  experimental: object({}).optional().describe("Experimental features (structure TBD)."),
864
959
  openLinks: object({}).optional().describe("Host supports opening external URLs."),
865
960
  downloadFile: object({}).optional().describe("Host supports file downloads via ui/download-file."),
@@ -872,11 +967,11 @@ var R = record(string(), unknown()), O = object({
872
967
  }).optional().describe("Sandbox configuration applied by the host."),
873
968
  updateModelContext: O.optional().describe("Host accepts context updates (ui/update-model-context) to be included in the model's context for future turns."),
874
969
  message: O.optional().describe("Host supports receiving content messages (ui/message) from the view.")
875
- }), i = object({
970
+ }), l = object({
876
971
  experimental: object({}).optional().describe("Experimental features (structure TBD)."),
877
972
  tools: object({ listChanged: boolean().optional().describe("App supports tools/list_changed notifications.") }).optional().describe("App exposes MCP-style tools that the host can call."),
878
973
  availableDisplayModes: array(D).optional().describe("Display modes the app supports.")
879
- }), M = object({
974
+ }), v = object({
880
975
  method: literal("ui/notifications/initialized"),
881
976
  params: object({}).optional()
882
977
  });
@@ -905,18 +1000,18 @@ var W = object({
905
1000
  params: object({ mode: D.describe("The display mode being requested.") })
906
1001
  });
907
1002
  object({ mode: D.describe("The display mode that was actually set. May differ from requested if not supported.") }).passthrough();
908
- var l = union([literal("model"), literal("app")]).describe("Tool visibility scope - who can access the tool.");
1003
+ var r = union([literal("model"), literal("app")]).describe("Tool visibility scope - who can access the tool.");
909
1004
  object({
910
1005
  resourceUri: string().optional(),
911
- visibility: array(l).optional().describe(`Who can access this tool. Default: ["model", "app"]
1006
+ visibility: array(r).optional().describe(`Who can access this tool. Default: ["model", "app"]
912
1007
  - "model": Tool visible to and callable by the agent
913
1008
  - "app": Tool callable by the app from this server only`)
914
1009
  });
915
1010
  object({ mimeTypes: array(string()).optional().describe("Array of supported MIME types for UI resources.\nMust include `\"text/html;profile=mcp-app\"` for MCP Apps support.") });
916
- var S = object({
1011
+ var C = object({
917
1012
  method: literal("ui/download-file"),
918
1013
  params: object({ contents: array(union([EmbeddedResourceSchema, ResourceLinkSchema])).describe("Resource contents to download — embedded (inline data) or linked (host fetches). Uses standard MCP resource types.") })
919
- }), C = object({
1014
+ }), S = object({
920
1015
  method: literal("ui/message"),
921
1016
  params: object({
922
1017
  role: literal("user").describe("Message role, currently only \"user\" is supported."),
@@ -936,13 +1031,13 @@ object({
936
1031
  method: literal("ui/notifications/tool-result"),
937
1032
  params: CallToolResultSchema.describe("Standard MCP tool execution result.")
938
1033
  });
939
- var q = object({
1034
+ var y = object({
940
1035
  toolInfo: object({
941
1036
  id: RequestIdSchema.optional().describe("JSON-RPC id of the tools/call request."),
942
1037
  tool: ToolSchema.describe("Tool definition including name, inputSchema, etc.")
943
1038
  }).optional().describe("Metadata of the tool call that instantiated this App."),
944
- theme: m.optional().describe("Current color theme preference."),
945
- styles: c.optional().describe("Style configuration for theming the app."),
1039
+ theme: p.optional().describe("Current color theme preference."),
1040
+ styles: n.optional().describe("Style configuration for theming the app."),
946
1041
  displayMode: D.optional().describe("How the UI is currently displayed."),
947
1042
  availableDisplayModes: array(D).optional().describe("Display modes the host supports."),
948
1043
  containerDimensions: union([object({ height: number().describe("Fixed container height in pixels.") }), object({ maxHeight: union([number(), _undefined()]).optional().describe("Maximum container height in pixels.") })]).and(union([object({ width: number().describe("Fixed container width in pixels.") }), object({ maxWidth: union([number(), _undefined()]).optional().describe("Maximum container width in pixels.") })])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
@@ -968,27 +1063,27 @@ container holding the app. Specify either width or maxWidth, and either height o
968
1063
  }).passthrough();
969
1064
  object({
970
1065
  method: literal("ui/notifications/host-context-changed"),
971
- params: q.describe("Partial context update containing only changed fields.")
1066
+ params: y.describe("Partial context update containing only changed fields.")
972
1067
  });
973
- var y = object({
1068
+ var f = object({
974
1069
  method: literal("ui/update-model-context"),
975
1070
  params: object({
976
1071
  content: array(ContentBlockSchema).optional().describe("Context content blocks (text, image, etc.)."),
977
1072
  structuredContent: record(string(), unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")
978
1073
  })
979
- }), f = object({
1074
+ }), d = object({
980
1075
  method: literal("ui/initialize"),
981
1076
  params: object({
982
1077
  appInfo: ImplementationSchema.describe("App identification (name and version)."),
983
- appCapabilities: i.describe("Features and capabilities this app provides."),
1078
+ appCapabilities: l.describe("Features and capabilities this app provides."),
984
1079
  protocolVersion: string().describe("Protocol version this app supports.")
985
1080
  })
986
1081
  });
987
1082
  object({
988
1083
  protocolVersion: string().describe("Negotiated protocol version string (e.g., \"2025-11-21\")."),
989
1084
  hostInfo: ImplementationSchema.describe("Host application identification and version."),
990
- hostCapabilities: n.describe("Features and capabilities provided by the host."),
991
- hostContext: q.describe("Rich context about the host environment.")
1085
+ hostCapabilities: i.describe("Features and capabilities provided by the host."),
1086
+ hostContext: y.describe("Rich context about the host environment.")
992
1087
  }).passthrough();
993
1088
  var N = class {
994
1089
  eventTarget;
@@ -1024,8 +1119,8 @@ var N = class {
1024
1119
  sessionId;
1025
1120
  setProtocolVersion;
1026
1121
  };
1027
- var rQ = [G];
1028
- var oQ = class extends Protocol {
1122
+ var aQ = [G];
1123
+ var tQ = class extends Protocol {
1029
1124
  _client;
1030
1125
  _hostInfo;
1031
1126
  _capabilities;
@@ -1037,7 +1132,7 @@ var oQ = class extends Protocol {
1037
1132
  this._client = X;
1038
1133
  this._hostInfo = Y;
1039
1134
  this._capabilities = Z;
1040
- this._hostContext = $?.hostContext || {}, this.setRequestHandler(f, (K) => this._oninitialize(K)), this.setRequestHandler(PingRequestSchema, (K, J) => {
1135
+ this._hostContext = $?.hostContext || {}, this.setRequestHandler(d, (K) => this._oninitialize(K)), this.setRequestHandler(PingRequestSchema, (K, J) => {
1041
1136
  return this.onping?.(K.params, J), {};
1042
1137
  }), this.setRequestHandler(W, (K) => {
1043
1138
  return { mode: this._hostContext.displayMode ?? "inline" };
@@ -1054,13 +1149,13 @@ var oQ = class extends Protocol {
1054
1149
  this.setNotificationHandler(P, (Y) => X(Y.params));
1055
1150
  }
1056
1151
  set onsandboxready(X) {
1057
- this.setNotificationHandler(F, (Y) => X(Y.params));
1152
+ this.setNotificationHandler(L, (Y) => X(Y.params));
1058
1153
  }
1059
1154
  set oninitialized(X) {
1060
- this.setNotificationHandler(M, (Y) => X(Y.params));
1155
+ this.setNotificationHandler(v, (Y) => X(Y.params));
1061
1156
  }
1062
1157
  set onmessage(X) {
1063
- this.setRequestHandler(C, async (Y, Z) => {
1158
+ this.setRequestHandler(S, async (Y, Z) => {
1064
1159
  return X(Y.params, Z);
1065
1160
  });
1066
1161
  }
@@ -1070,10 +1165,13 @@ var oQ = class extends Protocol {
1070
1165
  });
1071
1166
  }
1072
1167
  set ondownloadfile(X) {
1073
- this.setRequestHandler(S, async (Y, Z) => {
1168
+ this.setRequestHandler(C, async (Y, Z) => {
1074
1169
  return X(Y.params, Z);
1075
1170
  });
1076
1171
  }
1172
+ set onrequestteardown(X) {
1173
+ this.setNotificationHandler(M, (Y) => X(Y.params));
1174
+ }
1077
1175
  set onrequestdisplaymode(X) {
1078
1176
  this.setRequestHandler(W, async (Y, Z) => {
1079
1177
  return X(Y.params, Z);
@@ -1085,7 +1183,7 @@ var oQ = class extends Protocol {
1085
1183
  });
1086
1184
  }
1087
1185
  set onupdatemodelcontext(X) {
1088
- this.setRequestHandler(y, async (Y, Z) => {
1186
+ this.setRequestHandler(f, async (Y, Z) => {
1089
1187
  return X(Y.params, Z);
1090
1188
  });
1091
1189
  }
@@ -1147,7 +1245,7 @@ var oQ = class extends Protocol {
1147
1245
  async _oninitialize(X) {
1148
1246
  let Y = X.params.protocolVersion;
1149
1247
  return this._appCapabilities = X.params.appCapabilities, this._appInfo = X.params.appInfo, {
1150
- protocolVersion: rQ.includes(Y) ? Y : G,
1248
+ protocolVersion: aQ.includes(Y) ? Y : G,
1151
1249
  hostCapabilities: this.getCapabilities(),
1152
1250
  hostInfo: this._hostInfo,
1153
1251
  hostContext: this._hostContext
@@ -1157,7 +1255,7 @@ var oQ = class extends Protocol {
1157
1255
  let Y = {}, Z = !1;
1158
1256
  for (let $ of Object.keys(X)) {
1159
1257
  let K = this._hostContext[$], J = X[$];
1160
- if (aQ(K, J)) continue;
1258
+ if (sQ(K, J)) continue;
1161
1259
  Y[$] = J, Z = !0;
1162
1260
  }
1163
1261
  if (Z) this._hostContext = X, this.sendHostContextChange(Y);
@@ -1202,7 +1300,7 @@ var oQ = class extends Protocol {
1202
1300
  return this.request({
1203
1301
  method: "ui/resource-teardown",
1204
1302
  params: X
1205
- }, R, Y);
1303
+ }, H, Y);
1206
1304
  }
1207
1305
  sendResourceTeardown = this.teardownResource;
1208
1306
  async connect(X) {
@@ -1248,7 +1346,7 @@ var oQ = class extends Protocol {
1248
1346
  return super.connect(X);
1249
1347
  }
1250
1348
  };
1251
- function aQ(X, Y) {
1349
+ function sQ(X, Y) {
1252
1350
  return JSON.stringify(X) === JSON.stringify(Y);
1253
1351
  }
1254
1352
  //#endregion
@@ -1282,10 +1380,11 @@ var McpAppHost = class {
1282
1380
  _prevDisplayMode;
1283
1381
  _pendingToolInput = null;
1284
1382
  _pendingToolResult = null;
1383
+ _messageListener = null;
1285
1384
  constructor(options = {}) {
1286
1385
  this.options = options;
1287
1386
  this._prevDisplayMode = options.hostContext?.displayMode;
1288
- this.bridge = new oQ(null, options.hostInfo ?? DEFAULT_HOST_INFO, options.hostCapabilities ?? DEFAULT_HOST_CAPABILITIES, { hostContext: options.hostContext });
1387
+ this.bridge = new tQ(null, options.hostInfo ?? DEFAULT_HOST_INFO, options.hostCapabilities ?? DEFAULT_HOST_CAPABILITIES, { hostContext: options.hostContext });
1289
1388
  this.bridge.oninitialized = () => {
1290
1389
  this._initialized = true;
1291
1390
  if (this._pendingToolInput) {
@@ -1298,38 +1397,35 @@ var McpAppHost = class {
1298
1397
  }
1299
1398
  };
1300
1399
  this.bridge.onopenlink = async ({ url }) => {
1301
- console.log("[MCP App] openLink:", url);
1302
1400
  if (this.options.onOpenLink) this.options.onOpenLink(url);
1303
- else {
1304
- try {
1305
- const parsed = new URL(url);
1306
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
1307
- console.warn("[MCP App] openLink blocked non-http(s) URL:", url);
1308
- return {};
1309
- }
1310
- } catch {
1311
- console.warn("[MCP App] openLink blocked invalid URL:", url);
1312
- return {};
1313
- }
1314
- window.open(url, "_blank");
1401
+ else try {
1402
+ const parsed = new URL(url);
1403
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") console.warn("[MCP App] openLink blocked non-http(s) URL:", url);
1404
+ else window.open(url, "_blank");
1405
+ } catch {
1406
+ console.warn("[MCP App] openLink blocked invalid URL:", url);
1315
1407
  }
1316
- return {};
1408
+ const ack = {};
1409
+ console.log(`%c[MCP ↓]%c host → app: %copenLink ack`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", ack);
1410
+ return ack;
1317
1411
  };
1318
1412
  this.bridge.onmessage = async ({ role, content }) => {
1319
1413
  if (this.options.onMessage) this.options.onMessage(role, content);
1320
- else console.log("[MCP App] sendMessage:", {
1321
- role,
1322
- content
1323
- });
1324
- return {};
1414
+ const ack = {};
1415
+ console.log(`%c[MCP ↓]%c host → app: %csendMessage ack`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", ack);
1416
+ return ack;
1325
1417
  };
1326
1418
  this.bridge.onrequestdisplaymode = async ({ mode }) => {
1327
1419
  this.options.onDisplayModeChange?.(mode);
1328
- return { mode };
1420
+ const result = { mode };
1421
+ console.log(`%c[MCP ↓]%c host → app: %crequestDisplayMode result`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", result);
1422
+ return result;
1329
1423
  };
1330
1424
  this.bridge.onupdatemodelcontext = async ({ content, structuredContent }) => {
1331
1425
  this.options.onUpdateModelContext?.(content ?? [], structuredContent);
1332
- return {};
1426
+ const ack = {};
1427
+ console.log(`%c[MCP ↓]%c host → app: %cupdateModelContext ack`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", ack);
1428
+ return ack;
1333
1429
  };
1334
1430
  this.bridge.onsizechange = (params) => {
1335
1431
  this.options.onSizeChanged?.(params);
@@ -1346,17 +1442,24 @@ var McpAppHost = class {
1346
1442
  }
1347
1443
  };
1348
1444
  this.bridge.oncalltool = async (params) => {
1349
- if (this.options.onCallTool) return this.options.onCallTool(params);
1350
- console.log("[MCP App] callServerTool:", params.name, params.arguments);
1351
- return { content: [{
1445
+ let result;
1446
+ if (this.options.onCallTool) result = await this.options.onCallTool(params);
1447
+ else result = { content: [{
1352
1448
  type: "text",
1353
1449
  text: `[Inspector] Tool "${params.name}" called (no handler configured)`
1354
1450
  }] };
1451
+ console.log(`%c[MCP ↓]%c host → app: %ccallServerTool result(${params.name})`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", result);
1452
+ return result;
1355
1453
  };
1356
1454
  this.bridge.ondownloadfile = async ({ contents }) => {
1357
1455
  if (this.options.onDownloadFile) this.options.onDownloadFile(contents);
1358
- else console.log("[MCP App] downloadFile:", contents.length, "item(s)");
1359
- return {};
1456
+ const ack = {};
1457
+ console.log(`%c[MCP ↓]%c host → app: %cdownloadFile ack`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", ack);
1458
+ return ack;
1459
+ };
1460
+ this.bridge.onrequestteardown = () => {
1461
+ if (this.options.onRequestTeardown) this.options.onRequestTeardown();
1462
+ else console.log("[MCP App] requestTeardown (app requested close)");
1360
1463
  };
1361
1464
  this.bridge.onsandboxready = () => {
1362
1465
  this.options.onSandboxReady?.();
@@ -1366,7 +1469,18 @@ var McpAppHost = class {
1366
1469
  * Connect to an iframe's contentWindow.
1367
1470
  */
1368
1471
  async connectToIframe(contentWindow) {
1472
+ if (this._messageListener) window.removeEventListener("message", this._messageListener);
1369
1473
  this._contentWindow = contentWindow;
1474
+ this._messageListener = (event) => {
1475
+ if (event.source !== contentWindow) return;
1476
+ const data = event.data;
1477
+ if (!data || typeof data !== "object") return;
1478
+ const method = data.method;
1479
+ if (method?.startsWith("sunpeak/") || method === "ui/notifications/sandbox-proxy-ready") return;
1480
+ const label = method ?? (data.id != null ? `response #${data.id}` : "unknown");
1481
+ console.log(`%c[MCP ↑]%c app → host: %c${label}`, "color:#6ee7b7", "color:inherit", "color:#93c5fd", data);
1482
+ };
1483
+ window.addEventListener("message", this._messageListener);
1370
1484
  const transport = new N(contentWindow, contentWindow);
1371
1485
  await this.bridge.connect(transport);
1372
1486
  }
@@ -1401,11 +1515,12 @@ var McpAppHost = class {
1401
1515
  this._fenceCleanup = cleanup;
1402
1516
  window.addEventListener("message", handler);
1403
1517
  try {
1404
- win.postMessage({
1518
+ const fenceMsg = {
1405
1519
  jsonrpc: "2.0",
1406
1520
  method: "sunpeak/fence",
1407
1521
  params: { fenceId: id }
1408
- }, "*");
1522
+ };
1523
+ win.postMessage(fenceMsg, "*");
1409
1524
  } catch {
1410
1525
  cleanup();
1411
1526
  resolve();
@@ -1418,6 +1533,7 @@ var McpAppHost = class {
1418
1533
  * to commit its DOM before firing onDisplayModeReady.
1419
1534
  */
1420
1535
  setHostContext(context) {
1536
+ console.log(`%c[MCP ↓]%c host → app: %csetHostContext`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", context);
1421
1537
  this.bridge.setHostContext(context);
1422
1538
  const currentMode = context.displayMode;
1423
1539
  if (currentMode && currentMode !== this._prevDisplayMode) {
@@ -1434,6 +1550,7 @@ var McpAppHost = class {
1434
1550
  */
1435
1551
  sendToolInput(args) {
1436
1552
  const params = { arguments: args };
1553
+ console.log(`%c[MCP ↓]%c host → app: %csendToolInput`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", params);
1437
1554
  if (this._initialized) this.bridge.sendToolInput(params);
1438
1555
  else this._pendingToolInput = params;
1439
1556
  }
@@ -1442,6 +1559,7 @@ var McpAppHost = class {
1442
1559
  * If the app hasn't initialized yet, the result is queued.
1443
1560
  */
1444
1561
  sendToolResult(result) {
1562
+ console.log(`%c[MCP ↓]%c host → app: %csendToolResult`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", result);
1445
1563
  if (this._initialized) this.bridge.sendToolResult(result);
1446
1564
  else this._pendingToolResult = result;
1447
1565
  }
@@ -1451,6 +1569,7 @@ var McpAppHost = class {
1451
1569
  */
1452
1570
  sendToolInputPartial(args) {
1453
1571
  const params = { arguments: args };
1572
+ console.log(`%c[MCP ↓]%c host → app: %csendToolInputPartial`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", params);
1454
1573
  if (this._initialized) this.bridge.sendToolInputPartial(params);
1455
1574
  }
1456
1575
  /**
@@ -1459,6 +1578,7 @@ var McpAppHost = class {
1459
1578
  */
1460
1579
  sendToolCancelled(reason) {
1461
1580
  const params = reason ? { reason } : {};
1581
+ console.log(`%c[MCP ↓]%c host → app: %csendToolCancelled`, "color:#f9a8d4", "color:inherit", "color:#93c5fd", params);
1462
1582
  if (this._initialized) this.bridge.sendToolCancelled(params);
1463
1583
  }
1464
1584
  /**
@@ -1482,6 +1602,10 @@ var McpAppHost = class {
1482
1602
  * Close the connection.
1483
1603
  */
1484
1604
  async close() {
1605
+ if (this._messageListener) {
1606
+ window.removeEventListener("message", this._messageListener);
1607
+ this._messageListener = null;
1608
+ }
1485
1609
  this._fenceCleanup?.();
1486
1610
  this._fenceCleanup = null;
1487
1611
  try {
@@ -1592,7 +1716,7 @@ function generateSandboxProxyHtml(platformScript) {
1592
1716
  <head>
1593
1717
  <meta name="color-scheme" content="${colorScheme}" />
1594
1718
  <style>
1595
- html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; }
1719
+ html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: transparent; }
1596
1720
  iframe { border: none; width: 100%; height: 100%; display: block; }
1597
1721
  </style>
1598
1722
  </head>
@@ -1655,6 +1779,16 @@ iframe { border: none; width: 100%; height: 100%; display: block; }
1655
1779
  return;
1656
1780
  }
1657
1781
 
1782
+ // Sync color-scheme on the inner iframe element when theme changes.
1783
+ // This ensures prefers-color-scheme resolves correctly inside the app.
1784
+ // Important: do NOT set color-scheme on the proxy's own document —
1785
+ // changing it from the initial 'dark' causes Chrome to re-evaluate
1786
+ // the CSS Canvas as opaque white, blocking the host's conversation
1787
+ // background from showing through the transparent proxy.
1788
+ if (data.method === 'ui/notifications/host-context-changed' && data.params && data.params.theme) {
1789
+ if (innerFrame) innerFrame.style.colorScheme = data.params.theme;
1790
+ }
1791
+
1658
1792
  // Forward all other messages to the inner iframe
1659
1793
  if (innerWindow) {
1660
1794
  try { innerWindow.postMessage(data, '*'); } catch(e) { /* detached */ }
@@ -2507,15 +2641,17 @@ function useMcpConnection(initialServerUrl) {
2507
2641
  const [error, setError] = useState();
2508
2642
  const [simulations, setSimulations] = useState();
2509
2643
  const [hasReconnected, setHasReconnected] = useState(false);
2510
- const reconnect = useCallback(async (url) => {
2644
+ const reconnect = useCallback(async (url, auth) => {
2511
2645
  setHasReconnected(true);
2512
2646
  setStatus("connecting");
2513
2647
  setError(void 0);
2514
2648
  try {
2649
+ const body = { url };
2650
+ if (auth && auth.type !== "none") body.auth = auth;
2515
2651
  const res = await fetch("/__sunpeak/connect", {
2516
2652
  method: "POST",
2517
2653
  headers: { "Content-Type": "application/json" },
2518
- body: JSON.stringify({ url })
2654
+ body: JSON.stringify(body)
2519
2655
  });
2520
2656
  if (!res.ok) {
2521
2657
  let message = `Connection failed (${res.status})`;
@@ -2534,6 +2670,12 @@ function useMcpConnection(initialServerUrl) {
2534
2670
  setSimulations(void 0);
2535
2671
  }
2536
2672
  }, []);
2673
+ const setConnected = useCallback((sims) => {
2674
+ setHasReconnected(true);
2675
+ setStatus("connected");
2676
+ setError(void 0);
2677
+ setSimulations(sims);
2678
+ }, []);
2537
2679
  useEffect(() => {
2538
2680
  if (!initialServerUrl) return;
2539
2681
  let cancelled = false;
@@ -2559,7 +2701,8 @@ function useMcpConnection(initialServerUrl) {
2559
2701
  error,
2560
2702
  simulations,
2561
2703
  hasReconnected,
2562
- reconnect
2704
+ reconnect,
2705
+ setConnected
2563
2706
  };
2564
2707
  }
2565
2708
  //#endregion
@@ -3043,22 +3186,144 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3043
3186
  defaultHost
3044
3187
  });
3045
3188
  const [serverUrl, setServerUrl] = React.useState(mcpServerUrl ?? "");
3189
+ const [authType, setAuthType] = React.useState("none");
3190
+ const [bearerToken, setBearerToken] = React.useState("");
3191
+ const [oauthScopes, setOauthScopes] = React.useState("");
3192
+ const [oauthClientId, setOauthClientId] = React.useState("");
3193
+ const [oauthClientSecret, setOauthClientSecret] = React.useState("");
3194
+ const [oauthStatus, setOauthStatus] = React.useState("none");
3195
+ const [oauthError, setOauthError] = React.useState();
3046
3196
  const connection = useMcpConnection(mcpServerUrl || void 0);
3047
3197
  const [prodResources, setProdResources] = React.useState(state.urlProdResources ?? defaultProdResources);
3048
3198
  const [isRunning, setIsRunning] = React.useState(false);
3049
3199
  const [hasRun, setHasRun] = React.useState(false);
3050
3200
  const [showCheck, setShowCheck] = React.useState(false);
3051
3201
  const checkTimerRef = React.useRef(void 0);
3202
+ const oauthCleanupRef = React.useRef(void 0);
3052
3203
  React.useEffect(() => {
3053
3204
  state.setSelectedSimulationName(effectiveSimulationName);
3054
3205
  }, [effectiveSimulationName]);
3206
+ const currentAuthConfig = React.useMemo(() => {
3207
+ if (authType === "bearer" && bearerToken) return {
3208
+ type: "bearer",
3209
+ bearerToken
3210
+ };
3211
+ if (authType === "oauth") return { type: "oauth" };
3212
+ }, [authType, bearerToken]);
3055
3213
  const prevServerUrlRef = React.useRef(serverUrl);
3056
3214
  React.useEffect(() => {
3057
3215
  const urlChanged = serverUrl !== prevServerUrlRef.current;
3058
3216
  prevServerUrlRef.current = serverUrl;
3059
3217
  if (!urlChanged) return;
3060
- if (serverUrl) connection.reconnect(serverUrl);
3061
- }, [serverUrl, connection.reconnect]);
3218
+ if (serverUrl) {
3219
+ setOauthStatus("none");
3220
+ setOauthError(void 0);
3221
+ if (authType === "oauth") return;
3222
+ connection.reconnect(serverUrl, currentAuthConfig);
3223
+ }
3224
+ }, [
3225
+ serverUrl,
3226
+ connection.reconnect,
3227
+ authType,
3228
+ currentAuthConfig
3229
+ ]);
3230
+ const handleStartOAuth = React.useCallback(async () => {
3231
+ if (!serverUrl || demoMode) return;
3232
+ setOauthStatus("authorizing");
3233
+ setOauthError(void 0);
3234
+ const popup = window.open("about:blank", `sunpeak-oauth-${Date.now()}`, "width=600,height=700,popup=yes");
3235
+ try {
3236
+ const res = await fetch("/__sunpeak/oauth/start", {
3237
+ method: "POST",
3238
+ headers: { "Content-Type": "application/json" },
3239
+ body: JSON.stringify({
3240
+ url: serverUrl,
3241
+ scope: oauthScopes || void 0,
3242
+ clientId: oauthClientId || void 0,
3243
+ clientSecret: oauthClientSecret || void 0
3244
+ })
3245
+ });
3246
+ if (!res.ok) {
3247
+ let message = `OAuth start failed (${res.status})`;
3248
+ try {
3249
+ const json = await res.json();
3250
+ if (json.error) message = json.error;
3251
+ } catch {}
3252
+ throw new Error(message);
3253
+ }
3254
+ const data = await res.json();
3255
+ if (data.error) {
3256
+ popup?.close();
3257
+ setOauthError(data.error);
3258
+ setOauthStatus("error");
3259
+ return;
3260
+ }
3261
+ if (data.status === "authorized") {
3262
+ popup?.close();
3263
+ setOauthStatus("authorized");
3264
+ connection.setConnected(data.simulations);
3265
+ return;
3266
+ }
3267
+ if (data.status === "redirect" && data.authUrl) {
3268
+ if (!popup || popup.closed) {
3269
+ setOauthError("Popup was blocked. Allow popups for this site and try again.");
3270
+ setOauthStatus("error");
3271
+ return;
3272
+ }
3273
+ popup.location.href = data.authUrl;
3274
+ let checkClosed;
3275
+ let bc;
3276
+ const cleanup = () => {
3277
+ clearInterval(checkClosed);
3278
+ window.removeEventListener("message", handleMessage);
3279
+ bc?.close();
3280
+ oauthCleanupRef.current = void 0;
3281
+ };
3282
+ oauthCleanupRef.current?.();
3283
+ oauthCleanupRef.current = cleanup;
3284
+ const handleOAuthResult = (result) => {
3285
+ cleanup();
3286
+ if (result.error) {
3287
+ setOauthError(result.errorDescription || result.error);
3288
+ setOauthStatus("error");
3289
+ } else if (result.success) {
3290
+ setOauthStatus("authorized");
3291
+ connection.setConnected(result.simulations);
3292
+ }
3293
+ };
3294
+ const handleMessage = (event) => {
3295
+ if (event.origin !== window.location.origin) return;
3296
+ if (event.data?.type !== "sunpeak-oauth-callback") return;
3297
+ handleOAuthResult(event.data);
3298
+ };
3299
+ window.addEventListener("message", handleMessage);
3300
+ if (typeof BroadcastChannel !== "undefined") {
3301
+ bc = new BroadcastChannel("sunpeak-oauth");
3302
+ bc.onmessage = (event) => {
3303
+ if (event.data?.type !== "sunpeak-oauth-callback") return;
3304
+ handleOAuthResult(event.data);
3305
+ };
3306
+ }
3307
+ checkClosed = setInterval(() => {
3308
+ if (popup?.closed) {
3309
+ cleanup();
3310
+ setOauthStatus((prev) => prev === "authorizing" ? "none" : prev);
3311
+ }
3312
+ }, 500);
3313
+ }
3314
+ } catch (err) {
3315
+ popup?.close();
3316
+ setOauthError(err instanceof Error ? err.message : String(err));
3317
+ setOauthStatus("error");
3318
+ }
3319
+ }, [
3320
+ serverUrl,
3321
+ oauthScopes,
3322
+ oauthClientId,
3323
+ oauthClientSecret,
3324
+ demoMode,
3325
+ connection
3326
+ ]);
3062
3327
  React.useEffect(() => {
3063
3328
  if (connection.simulations) setSimulations(connection.simulations);
3064
3329
  else if (connection.status === "error" && connection.hasReconnected) setSimulations({});
@@ -3090,7 +3355,10 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3090
3355
  React.useEffect(() => {
3091
3356
  setHasRun(false);
3092
3357
  }, [effectiveSimulationName]);
3093
- React.useEffect(() => () => clearTimeout(checkTimerRef.current), []);
3358
+ React.useEffect(() => () => {
3359
+ clearTimeout(checkTimerRef.current);
3360
+ oauthCleanupRef.current?.();
3361
+ }, []);
3094
3362
  const handleRun = React.useCallback(async () => {
3095
3363
  const caller = onCallToolDirect ?? onCallTool;
3096
3364
  const sim = simulations[effectiveSimulationName];
@@ -3145,16 +3413,26 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3145
3413
  const ctx = { ...state.hostContext };
3146
3414
  if (styleVars) ctx.styles = { variables: styleVars };
3147
3415
  if (userAgent) ctx.userAgent = userAgent;
3416
+ if (activeShell?.availableDisplayModes) ctx.availableDisplayModes = activeShell.availableDisplayModes;
3148
3417
  return ctx;
3149
3418
  }, [state.hostContext, activeShell]);
3419
+ const { displayMode, setDisplayMode } = state;
3150
3420
  React.useEffect(() => {
3421
+ const modes = activeShell?.availableDisplayModes;
3422
+ if (modes && !modes.includes(displayMode)) setDisplayMode("inline");
3423
+ }, [
3424
+ activeShell,
3425
+ displayMode,
3426
+ setDisplayMode
3427
+ ]);
3428
+ React.useLayoutEffect(() => {
3151
3429
  const vars = activeShell?.styleVariables;
3152
3430
  if (!vars) return;
3153
3431
  const root = document.documentElement;
3154
3432
  for (const [key, value] of Object.entries(vars)) if (value) root.style.setProperty(key, value);
3155
3433
  }, [activeShell]);
3156
3434
  const prevPageStyleKeysRef = React.useRef([]);
3157
- React.useEffect(() => {
3435
+ React.useLayoutEffect(() => {
3158
3436
  const root = document.documentElement;
3159
3437
  for (const key of prevPageStyleKeysRef.current) root.style.removeProperty(key);
3160
3438
  const pageStyles = activeShell?.pageStyles;
@@ -3167,6 +3445,22 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3167
3445
  prevPageStyleKeysRef.current = keys;
3168
3446
  } else prevPageStyleKeysRef.current = [];
3169
3447
  }, [activeShell]);
3448
+ React.useLayoutEffect(() => {
3449
+ const fontCss = activeShell?.fontCss;
3450
+ const id = "sunpeak-host-fonts";
3451
+ const existing = document.getElementById(id);
3452
+ if (!fontCss) {
3453
+ existing?.remove();
3454
+ return;
3455
+ }
3456
+ if (existing) existing.textContent = fontCss;
3457
+ else {
3458
+ const style = document.createElement("style");
3459
+ style.id = id;
3460
+ style.textContent = fontCss;
3461
+ document.head.appendChild(style);
3462
+ }
3463
+ }, [activeShell]);
3170
3464
  const handleCallTool = React.useCallback((params) => {
3171
3465
  if (activeSimulationName) {
3172
3466
  const mock = simulations[activeSimulationName]?.serverTools?.[params.name];
@@ -3370,6 +3664,92 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3370
3664
  disabled: demoMode
3371
3665
  })
3372
3666
  }),
3667
+ !demoMode && /* @__PURE__ */ jsx(SidebarCollapsibleControl, {
3668
+ label: "Authentication",
3669
+ defaultCollapsed: authType === "none",
3670
+ children: /* @__PURE__ */ jsxs("div", {
3671
+ className: "space-y-1",
3672
+ children: [
3673
+ /* @__PURE__ */ jsx(SidebarSelect, {
3674
+ value: authType,
3675
+ onChange: (value) => {
3676
+ const newType = value;
3677
+ setAuthType(newType);
3678
+ setOauthStatus("none");
3679
+ setOauthError(void 0);
3680
+ if (newType === "none" && serverUrl) connection.reconnect(serverUrl);
3681
+ },
3682
+ options: [
3683
+ {
3684
+ value: "none",
3685
+ label: "None"
3686
+ },
3687
+ {
3688
+ value: "bearer",
3689
+ label: "Bearer Token"
3690
+ },
3691
+ {
3692
+ value: "oauth",
3693
+ label: "OAuth"
3694
+ }
3695
+ ]
3696
+ }),
3697
+ authType === "bearer" && /* @__PURE__ */ jsx(SidebarInput, {
3698
+ type: "password",
3699
+ value: bearerToken,
3700
+ onChange: (value) => {
3701
+ setBearerToken(value);
3702
+ if (serverUrl && value) connection.reconnect(serverUrl, {
3703
+ type: "bearer",
3704
+ bearerToken: value
3705
+ });
3706
+ },
3707
+ applyOnBlur: true,
3708
+ placeholder: "Paste your token"
3709
+ }),
3710
+ authType === "oauth" && /* @__PURE__ */ jsxs("div", {
3711
+ className: "space-y-1",
3712
+ children: [
3713
+ /* @__PURE__ */ jsx(SidebarInput, {
3714
+ value: oauthClientId,
3715
+ onChange: setOauthClientId,
3716
+ applyOnBlur: true,
3717
+ placeholder: "Client ID (optional)"
3718
+ }),
3719
+ oauthClientId && /* @__PURE__ */ jsx(SidebarInput, {
3720
+ type: "password",
3721
+ value: oauthClientSecret,
3722
+ onChange: setOauthClientSecret,
3723
+ applyOnBlur: true,
3724
+ placeholder: "Client Secret (optional)"
3725
+ }),
3726
+ /* @__PURE__ */ jsx(SidebarInput, {
3727
+ value: oauthScopes,
3728
+ onChange: setOauthScopes,
3729
+ applyOnBlur: true,
3730
+ placeholder: "Scopes (optional)"
3731
+ }),
3732
+ /* @__PURE__ */ jsx("button", {
3733
+ type: "button",
3734
+ onClick: handleStartOAuth,
3735
+ disabled: !serverUrl || oauthStatus === "authorizing",
3736
+ className: "w-full h-7 text-xs rounded-md px-2 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed",
3737
+ style: {
3738
+ backgroundColor: oauthStatus === "authorized" ? "#22c55e" : "var(--color-text-primary)",
3739
+ color: "var(--color-background-primary)"
3740
+ },
3741
+ children: oauthStatus === "authorizing" ? "Authorizing…" : oauthStatus === "authorized" ? "Authorized" : "Authorize"
3742
+ }),
3743
+ oauthError && /* @__PURE__ */ jsx("div", {
3744
+ className: "text-[9px]",
3745
+ style: { color: "var(--color-text-danger, #dc2626)" },
3746
+ children: oauthError
3747
+ })
3748
+ ]
3749
+ })
3750
+ ]
3751
+ })
3752
+ }, `auth-${authType === "none" ? "none" : "active"}`),
3373
3753
  !hideInspectorModes && !demoMode && /* @__PURE__ */ jsx(SidebarCheckbox, {
3374
3754
  checked: prodResources,
3375
3755
  onChange: setProdResources,
@@ -3525,7 +3905,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3525
3905
  value: "fullscreen",
3526
3906
  label: "Full"
3527
3907
  }
3528
- ]
3908
+ ].filter((opt) => !activeShell?.availableDisplayModes || activeShell.availableDisplayModes.includes(opt.value))
3529
3909
  })
3530
3910
  }),
3531
3911
  /* @__PURE__ */ jsxs("div", {
@@ -3807,4 +4187,4 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3807
4187
  //#endregion
3808
4188
  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, useInspectorState as m, resolveServerToolResult as n, SidebarInput as o, useMcpConnection as p, SidebarCheckbox as r, SidebarSelect as s, Inspector as t, SimpleSidebar as u, SCREEN_WIDTHS as v, registerHostShell as x, getHostShell as y };
3809
4189
 
3810
- //# sourceMappingURL=inspector-ClhpqKLi.js.map
4190
+ //# sourceMappingURL=inspector-CjSoXm6N.js.map