diff --git a/docs/patterns.md b/docs/patterns.md index f55d807..4bc4b0c 100644 --- a/docs/patterns.md +++ b/docs/patterns.md @@ -158,7 +158,7 @@ loadDataInChunks(resourceId, (loaded, total) => { _See [`examples/pdf-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/pdf-server) for a full implementation of this pattern._ -## Giving errors back to model +## Giving errors back to the model **Server-side**: Tool handler validates inputs and returns `{ isError: true, content: [...] }`. The model receives this error through the normal tool call response. @@ -182,18 +182,19 @@ try { } ``` -## Matching host styling (CSS variables, theme, and fonts) +## Adapting to host context (theme, styling, fonts, and safe areas) -Use the SDK's style helpers to apply host styling, then reference them in your CSS: +The host provides context about its environment via {@link types!McpUiHostContext `McpUiHostContext`}. Use this to adapt your app's appearance and layout: -- **CSS variables** — Use `var(--color-background-primary)`, etc. in your CSS - **Theme** — Use `[data-theme="dark"]` selectors or `light-dark()` function for theme-aware styles +- **CSS variables** — Use `var(--color-background-primary)`, etc. in your CSS (see {@link types!McpUiStyleVariableKey `McpUiStyleVariableKey`} for a full list) - **Fonts** — Use `var(--font-sans)` or `var(--font-mono)` with fallbacks (e.g., `font-family: var(--font-sans, system-ui, sans-serif)`) +- **Safe area insets** — Apply padding to avoid device notches, rounded corners, or system UI overlays **Vanilla JS:** -```tsx source="./patterns.tsx#hostStylingVanillaJs" +```tsx source="./patterns.tsx#hostContextVanillaJs" function applyHostContext(ctx: McpUiHostContext) { if (ctx.theme) { applyDocumentTheme(ctx.theme); @@ -204,12 +205,18 @@ function applyHostContext(ctx: McpUiHostContext) { if (ctx.styles?.css?.fonts) { applyHostFonts(ctx.styles.css.fonts); } + if (ctx.safeAreaInsets) { + mainEl.style.paddingTop = `${ctx.safeAreaInsets.top}px`; + mainEl.style.paddingRight = `${ctx.safeAreaInsets.right}px`; + mainEl.style.paddingBottom = `${ctx.safeAreaInsets.bottom}px`; + mainEl.style.paddingLeft = `${ctx.safeAreaInsets.left}px`; + } } // Apply when host context changes app.onhostcontextchanged = applyHostContext; -// Apply initial styles after connecting +// Apply initial context after connecting app.connect().then(() => { const ctx = app.getHostContext(); if (ctx) { @@ -221,25 +228,52 @@ app.connect().then(() => { **React:** -```tsx source="./patterns.tsx#hostStylingReact" +```tsx source="./patterns.tsx#hostContextReact" function MyApp() { + const [hostContext, setHostContext] = useState(); + const { app } = useApp({ appInfo: { name: "MyApp", version: "1.0.0" }, capabilities: {}, + onAppCreated: (app) => { + app.onhostcontextchanged = (ctx) => { + setHostContext((prev) => ({ ...prev, ...ctx })); + }; + }, }); - // Apply all host styles (variables, theme, fonts) - useHostStyles(app, app?.getHostContext()); + // Set initial host context after connection + useEffect(() => { + if (app) { + setHostContext(app.getHostContext()); + } + }, [app]); + + // Apply styles when host context changes + useEffect(() => { + if (hostContext?.theme) { + applyDocumentTheme(hostContext.theme); + } + if (hostContext?.styles?.variables) { + applyHostStyleVariables(hostContext.styles.variables); + } + if (hostContext?.styles?.css?.fonts) { + applyHostFonts(hostContext.styles.css.fonts); + } + }, [hostContext]); return (
-

Styled with host CSS variables and fonts

-

Uses [data-theme] selectors

+ Styled with host CSS variables, fonts, and safe area insets
); } @@ -247,17 +281,18 @@ function MyApp() { _See [`examples/basic-server-vanillajs/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) and [`examples/basic-server-react/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) for full implementations of this pattern._ -## Entering / Exiting fullscreen +## Entering / exiting fullscreen Toggle fullscreen mode by calling {@link app!App.requestDisplayMode `requestDisplayMode`}: ```ts source="../src/app.examples.ts#App_requestDisplayMode_toggle" +const container = document.getElementById("main")!; const ctx = app.getHostContext(); -if (ctx?.availableDisplayModes?.includes("fullscreen")) { - const target = ctx.displayMode === "fullscreen" ? "inline" : "fullscreen"; - const result = await app.requestDisplayMode({ mode: target }); - console.log("Now in:", result.mode); +const newMode = ctx?.displayMode === "inline" ? "fullscreen" : "inline"; +if (ctx?.availableDisplayModes?.includes(newMode)) { + const result = await app.requestDisplayMode({ mode: newMode }); + container.classList.toggle("fullscreen", result.mode === "fullscreen"); } ``` @@ -265,17 +300,38 @@ Listen for display mode changes via {@link app!App.onhostcontextchanged `onhostc ```ts source="../src/app.examples.ts#App_onhostcontextchanged_respondToDisplayMode" -app.onhostcontextchanged = (params) => { - if (params.displayMode) { - const isFullscreen = params.displayMode === "fullscreen"; - document.body.classList.toggle("fullscreen", isFullscreen); +app.onhostcontextchanged = (ctx) => { + // Adjust to current display mode + if (ctx.displayMode) { + const container = document.getElementById("main")!; + const isFullscreen = ctx.displayMode === "fullscreen"; + container.classList.toggle("fullscreen", isFullscreen); + } + + // Adjust display mode controls + if (ctx.availableDisplayModes) { + const fullscreenBtn = document.getElementById("fullscreen-btn")!; + const canFullscreen = ctx.availableDisplayModes.includes("fullscreen"); + fullscreenBtn.style.display = canFullscreen ? "block" : "none"; } }; ``` +In fullscreen mode, remove the container's border radius so content extends to the viewport edges: + +```css +#main { + border-radius: var(--border-radius-lg); + + &.fullscreen { + border-radius: 0; + } +} +``` + _See [`examples/shadertoy-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/shadertoy-server) for a full implementation of this pattern._ -## Passing contextual information from the App to the Model +## Passing contextual information from the App to the model Use {@link app!App.updateModelContext `updateModelContext`} to keep the model informed about what the user is viewing or interacting with. Structure the content with YAML frontmatter for easy parsing: @@ -389,9 +445,9 @@ app.ontoolresult = (result) => { _See [`examples/map-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/map-server) for a full implementation of this pattern._ -## Pausing computation-heavy views when out of view +## Pausing computation-heavy views when offscreen -Views with animations, WebGL rendering, or polling can consume significant CPU/GPU even when scrolled out of view. Use [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to pause expensive operations when the view isn't visible: +Views with animations, WebGL rendering, or polling can consume significant CPU/GPU even when scrolled offscreen. Use [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to pause expensive operations when the view isn't visible: ```tsx source="./patterns.tsx#visibilityBasedPause" @@ -399,9 +455,9 @@ Views with animations, WebGL rendering, or polling can consume significant CPU/G const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { - animation.play(); + animation.play(); // or startPolling(), etc } else { - animation.pause(); + animation.pause(); // or stopPolling(), etc } }); }); @@ -419,31 +475,24 @@ _See [`examples/shadertoy-server/`](https://github.com/modelcontextprotocol/ext- ## Lowering perceived latency -Use {@link app!App.ontoolinputpartial `ontoolinputpartial`} to receive streaming tool arguments as they arrive, allowing you to show a loading preview before the complete input is available. +Use {@link app!App.ontoolinputpartial `ontoolinputpartial`} to receive streaming tool arguments as they arrive. This lets you show a loading preview before the complete input is available, such as streaming code into a `
` tag before executing it, partially rendering a table as data arrives, or incrementally populating a chart.
 
 
 ```ts source="../src/app.examples.ts#App_ontoolinputpartial_progressiveRendering"
-let toolInputs: Record | null = null;
-let toolInputsPartial: Record | null = null;
+const codePreview = document.querySelector("#code-preview")!;
+const canvas = document.querySelector("#canvas")!;
 
 app.ontoolinputpartial = (params) => {
-  toolInputsPartial = params.arguments as Record;
-  render();
+  codePreview.textContent = (params.arguments?.code as string) ?? "";
+  codePreview.style.display = "block";
+  canvas.style.display = "none";
 };
 
 app.ontoolinput = (params) => {
-  toolInputs = params.arguments as Record;
-  toolInputsPartial = null;
-  render();
+  codePreview.style.display = "none";
+  canvas.style.display = "block";
+  render(params.arguments?.code as string);
 };
-
-function render() {
-  if (toolInputs) {
-    renderFinalUI(toolInputs);
-  } else {
-    renderLoadingUI(toolInputsPartial); // e.g., shimmer with partial preview
-  }
-}
 ```
 
 > [!IMPORTANT]
diff --git a/docs/patterns.tsx b/docs/patterns.tsx
index f13268f..cae3529 100644
--- a/docs/patterns.tsx
+++ b/docs/patterns.tsx
@@ -16,7 +16,8 @@ import {
 import { randomUUID } from "node:crypto";
 import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
 import type { McpUiHostContext } from "../src/types.js";
-import { useApp, useHostStyles } from "../src/react/index.js";
+import { useEffect, useState } from "react";
+import { useApp } from "../src/react/index.js";
 import { registerAppTool } from "../src/server/index.js";
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import { z } from "zod";
@@ -147,10 +148,10 @@ function chunkedDataClient(app: App, resourceId: string) {
 }
 
 /**
- * Example: Unified host styling (theme, CSS variables, fonts)
+ * Example: Adapting to host context (theme, CSS variables, fonts, safe areas)
  */
-function hostStylingVanillaJs(app: App) {
-  //#region hostStylingVanillaJs
+function hostContextVanillaJs(app: App, mainEl: HTMLElement) {
+  //#region hostContextVanillaJs
   function applyHostContext(ctx: McpUiHostContext) {
     if (ctx.theme) {
       applyDocumentTheme(ctx.theme);
@@ -161,48 +162,81 @@ function hostStylingVanillaJs(app: App) {
     if (ctx.styles?.css?.fonts) {
       applyHostFonts(ctx.styles.css.fonts);
     }
+    if (ctx.safeAreaInsets) {
+      mainEl.style.paddingTop = `${ctx.safeAreaInsets.top}px`;
+      mainEl.style.paddingRight = `${ctx.safeAreaInsets.right}px`;
+      mainEl.style.paddingBottom = `${ctx.safeAreaInsets.bottom}px`;
+      mainEl.style.paddingLeft = `${ctx.safeAreaInsets.left}px`;
+    }
   }
 
   // Apply when host context changes
   app.onhostcontextchanged = applyHostContext;
 
-  // Apply initial styles after connecting
+  // Apply initial context after connecting
   app.connect().then(() => {
     const ctx = app.getHostContext();
     if (ctx) {
       applyHostContext(ctx);
     }
   });
-  //#endregion hostStylingVanillaJs
+  //#endregion hostContextVanillaJs
 }
 
 /**
- * Example: Host styling with React (CSS variables, theme, fonts)
+ * Example: Adapting to host context with React (CSS variables, theme, fonts, safe areas)
  */
-function hostStylingReact() {
-  //#region hostStylingReact
+function hostContextReact() {
+  //#region hostContextReact
   function MyApp() {
+    const [hostContext, setHostContext] = useState();
+
     const { app } = useApp({
       appInfo: { name: "MyApp", version: "1.0.0" },
       capabilities: {},
+      onAppCreated: (app) => {
+        app.onhostcontextchanged = (ctx) => {
+          setHostContext((prev) => ({ ...prev, ...ctx }));
+        };
+      },
     });
 
-    // Apply all host styles (variables, theme, fonts)
-    useHostStyles(app, app?.getHostContext());
+    // Set initial host context after connection
+    useEffect(() => {
+      if (app) {
+        setHostContext(app.getHostContext());
+      }
+    }, [app]);
+
+    // Apply styles when host context changes
+    useEffect(() => {
+      if (hostContext?.theme) {
+        applyDocumentTheme(hostContext.theme);
+      }
+      if (hostContext?.styles?.variables) {
+        applyHostStyleVariables(hostContext.styles.variables);
+      }
+      if (hostContext?.styles?.css?.fonts) {
+        applyHostFonts(hostContext.styles.css.fonts);
+      }
+    }, [hostContext]);
 
     return (
       
-

Styled with host CSS variables and fonts

-

Uses [data-theme] selectors

+ Styled with host CSS variables, fonts, and safe area insets
); } - //#endregion hostStylingReact + //#endregion hostContextReact } /** @@ -284,9 +318,9 @@ function visibilityBasedPause( const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { - animation.play(); + animation.play(); // or startPolling(), etc } else { - animation.pause(); + animation.pause(); // or stopPolling(), etc } }); }); @@ -304,8 +338,8 @@ function visibilityBasedPause( // Suppress unused variable warnings void chunkedDataServer; void chunkedDataClient; -void hostStylingVanillaJs; -void hostStylingReact; +void hostContextVanillaJs; +void hostContextReact; void persistViewStateServer; void persistViewState; void visibilityBasedPause; diff --git a/src/app.examples.ts b/src/app.examples.ts index 97aa5a0..4705071 100644 --- a/src/app.examples.ts +++ b/src/app.examples.ts @@ -145,33 +145,25 @@ async function App_ontoolinput_setter(app: App) { */ function App_ontoolinputpartial_progressiveRendering(app: App) { //#region App_ontoolinputpartial_progressiveRendering - let toolInputs: Record | null = null; - let toolInputsPartial: Record | null = null; + const codePreview = document.querySelector("#code-preview")!; + const canvas = document.querySelector("#canvas")!; app.ontoolinputpartial = (params) => { - toolInputsPartial = params.arguments as Record; - render(); + codePreview.textContent = (params.arguments?.code as string) ?? ""; + codePreview.style.display = "block"; + canvas.style.display = "none"; }; app.ontoolinput = (params) => { - toolInputs = params.arguments as Record; - toolInputsPartial = null; - render(); + codePreview.style.display = "none"; + canvas.style.display = "block"; + render(params.arguments?.code as string); }; - - function render() { - if (toolInputs) { - renderFinalUI(toolInputs); - } else { - renderLoadingUI(toolInputsPartial); // e.g., shimmer with partial preview - } - } //#endregion App_ontoolinputpartial_progressiveRendering } -// Stubs for App_ontoolinputpartial_progressiveRendering example -declare function renderLoadingUI(data: Record | null): void; -declare function renderFinalUI(data: Record): void; +// Stub for App_ontoolinputpartial_progressiveRendering example +declare function render(code: string): void; /** * Example: Display tool execution results using ontoolresult. @@ -205,8 +197,8 @@ function App_ontoolcancelled_handleCancellation(app: App) { */ function App_onhostcontextchanged_respondToTheme(app: App) { //#region App_onhostcontextchanged_respondToTheme - app.onhostcontextchanged = (params) => { - if (params.theme === "dark") { + app.onhostcontextchanged = (ctx) => { + if (ctx.theme === "dark") { document.body.classList.add("dark-theme"); } else { document.body.classList.remove("dark-theme"); @@ -220,10 +212,19 @@ function App_onhostcontextchanged_respondToTheme(app: App) { */ function App_onhostcontextchanged_respondToDisplayMode(app: App) { //#region App_onhostcontextchanged_respondToDisplayMode - app.onhostcontextchanged = (params) => { - if (params.displayMode) { - const isFullscreen = params.displayMode === "fullscreen"; - document.body.classList.toggle("fullscreen", isFullscreen); + app.onhostcontextchanged = (ctx) => { + // Adjust to current display mode + if (ctx.displayMode) { + const container = document.getElementById("main")!; + const isFullscreen = ctx.displayMode === "fullscreen"; + container.classList.toggle("fullscreen", isFullscreen); + } + + // Adjust display mode controls + if (ctx.availableDisplayModes) { + const fullscreenBtn = document.getElementById("fullscreen-btn")!; + const canFullscreen = ctx.availableDisplayModes.includes("fullscreen"); + fullscreenBtn.style.display = canFullscreen ? "block" : "none"; } }; //#endregion App_onhostcontextchanged_respondToDisplayMode @@ -424,11 +425,12 @@ async function App_openLink_documentation(app: App) { */ async function App_requestDisplayMode_toggle(app: App) { //#region App_requestDisplayMode_toggle + const container = document.getElementById("main")!; const ctx = app.getHostContext(); - if (ctx?.availableDisplayModes?.includes("fullscreen")) { - const target = ctx.displayMode === "fullscreen" ? "inline" : "fullscreen"; - const result = await app.requestDisplayMode({ mode: target }); - console.log("Now in:", result.mode); + const newMode = ctx?.displayMode === "inline" ? "fullscreen" : "inline"; + if (ctx?.availableDisplayModes?.includes(newMode)) { + const result = await app.requestDisplayMode({ mode: newMode }); + container.classList.toggle("fullscreen", result.mode === "fullscreen"); } //#endregion App_requestDisplayMode_toggle } diff --git a/src/app.ts b/src/app.ts index 0678800..f813f44 100644 --- a/src/app.ts +++ b/src/app.ts @@ -358,27 +358,20 @@ export class App extends Protocol { * * @example Progressive rendering of tool arguments * ```ts source="./app.examples.ts#App_ontoolinputpartial_progressiveRendering" - * let toolInputs: Record | null = null; - * let toolInputsPartial: Record | null = null; + * const codePreview = document.querySelector("#code-preview")!; + * const canvas = document.querySelector("#canvas")!; * * app.ontoolinputpartial = (params) => { - * toolInputsPartial = params.arguments as Record; - * render(); + * codePreview.textContent = (params.arguments?.code as string) ?? ""; + * codePreview.style.display = "block"; + * canvas.style.display = "none"; * }; * * app.ontoolinput = (params) => { - * toolInputs = params.arguments as Record; - * toolInputsPartial = null; - * render(); + * codePreview.style.display = "none"; + * canvas.style.display = "block"; + * render(params.arguments?.code as string); * }; - * - * function render() { - * if (toolInputs) { - * renderFinalUI(toolInputs); - * } else { - * renderLoadingUI(toolInputsPartial); // e.g., shimmer with partial preview - * } - * } * ``` * * @see {@link setNotificationHandler `setNotificationHandler`} for the underlying method @@ -486,8 +479,8 @@ export class App extends Protocol { * * @example Respond to theme changes * ```ts source="./app.examples.ts#App_onhostcontextchanged_respondToTheme" - * app.onhostcontextchanged = (params) => { - * if (params.theme === "dark") { + * app.onhostcontextchanged = (ctx) => { + * if (ctx.theme === "dark") { * document.body.classList.add("dark-theme"); * } else { * document.body.classList.remove("dark-theme"); @@ -945,11 +938,12 @@ export class App extends Protocol { * * @example Toggle display mode * ```ts source="./app.examples.ts#App_requestDisplayMode_toggle" + * const container = document.getElementById("main")!; * const ctx = app.getHostContext(); - * if (ctx?.availableDisplayModes?.includes("fullscreen")) { - * const target = ctx.displayMode === "fullscreen" ? "inline" : "fullscreen"; - * const result = await app.requestDisplayMode({ mode: target }); - * console.log("Now in:", result.mode); + * const newMode = ctx?.displayMode === "inline" ? "fullscreen" : "inline"; + * if (ctx?.availableDisplayModes?.includes(newMode)) { + * const result = await app.requestDisplayMode({ mode: newMode }); + * container.classList.toggle("fullscreen", result.mode === "fullscreen"); * } * ``` * diff --git a/src/react/useApp.examples.tsx b/src/react/useApp.examples.tsx index eb25fe7..afa54c9 100644 --- a/src/react/useApp.examples.tsx +++ b/src/react/useApp.examples.tsx @@ -51,8 +51,8 @@ function useApp_basicUsage() { app.onerror = (error) => { console.log("Error:", error); }; - app.onhostcontextchanged = (params) => { - setHostContext((prev) => ({ ...prev, ...params })); + app.onhostcontextchanged = (ctx) => { + setHostContext((prev) => ({ ...prev, ...ctx })); }; }, }); diff --git a/src/react/useApp.tsx b/src/react/useApp.tsx index be1fb03..d6dcfbb 100644 --- a/src/react/useApp.tsx +++ b/src/react/useApp.tsx @@ -102,8 +102,8 @@ export interface AppState { * app.onerror = (error) => { * console.log("Error:", error); * }; - * app.onhostcontextchanged = (params) => { - * setHostContext((prev) => ({ ...prev, ...params })); + * app.onhostcontextchanged = (ctx) => { + * setHostContext((prev) => ({ ...prev, ...ctx })); * }; * }, * }); diff --git a/src/styles.examples.ts b/src/styles.examples.ts index 3c3f4ca..8f367e6 100644 --- a/src/styles.examples.ts +++ b/src/styles.examples.ts @@ -31,9 +31,9 @@ function getDocumentTheme_checkCurrent() { function applyDocumentTheme_fromHostContext(app: App) { //#region applyDocumentTheme_fromHostContext // Apply when host context changes - app.onhostcontextchanged = (params) => { - if (params.theme) { - applyDocumentTheme(params.theme); + app.onhostcontextchanged = (ctx) => { + if (ctx.theme) { + applyDocumentTheme(ctx.theme); } }; @@ -56,9 +56,9 @@ function applyHostStyleVariables_fromHostContext(app: App) { document.body.style.background = "var(--color-background-primary)"; // Apply when host context changes - app.onhostcontextchanged = (params) => { - if (params.styles?.variables) { - applyHostStyleVariables(params.styles.variables); + app.onhostcontextchanged = (ctx) => { + if (ctx.styles?.variables) { + applyHostStyleVariables(ctx.styles.variables); } }; @@ -77,10 +77,10 @@ function applyHostStyleVariables_fromHostContext(app: App) { */ function applyHostStyleVariables_toElement(app: App) { //#region applyHostStyleVariables_toElement - app.onhostcontextchanged = (params) => { + app.onhostcontextchanged = (ctx) => { const container = document.getElementById("app-root"); - if (container && params.styles?.variables) { - applyHostStyleVariables(params.styles.variables, container); + if (container && ctx.styles?.variables) { + applyHostStyleVariables(ctx.styles.variables, container); } }; //#endregion applyHostStyleVariables_toElement @@ -92,9 +92,9 @@ function applyHostStyleVariables_toElement(app: App) { function applyHostFonts_fromHostContext(app: App) { //#region applyHostFonts_fromHostContext // Apply when host context changes - app.onhostcontextchanged = (params) => { - if (params.styles?.css?.fonts) { - applyHostFonts(params.styles.css.fonts); + app.onhostcontextchanged = (ctx) => { + if (ctx.styles?.css?.fonts) { + applyHostFonts(ctx.styles.css.fonts); } }; diff --git a/src/styles.ts b/src/styles.ts index 0fcfdd3..ad900e2 100644 --- a/src/styles.ts +++ b/src/styles.ts @@ -44,9 +44,9 @@ export function getDocumentTheme(): McpUiTheme { * @example Apply theme from host context * ```ts source="./styles.examples.ts#applyDocumentTheme_fromHostContext" * // Apply when host context changes - * app.onhostcontextchanged = (params) => { - * if (params.theme) { - * applyDocumentTheme(params.theme); + * app.onhostcontextchanged = (ctx) => { + * if (ctx.theme) { + * applyDocumentTheme(ctx.theme); * } * }; * @@ -95,9 +95,9 @@ export function applyDocumentTheme(theme: McpUiTheme): void { * document.body.style.background = "var(--color-background-primary)"; * * // Apply when host context changes - * app.onhostcontextchanged = (params) => { - * if (params.styles?.variables) { - * applyHostStyleVariables(params.styles.variables); + * app.onhostcontextchanged = (ctx) => { + * if (ctx.styles?.variables) { + * applyHostStyleVariables(ctx.styles.variables); * } * }; * @@ -112,10 +112,10 @@ export function applyDocumentTheme(theme: McpUiTheme): void { * * @example Apply to a specific element * ```ts source="./styles.examples.ts#applyHostStyleVariables_toElement" - * app.onhostcontextchanged = (params) => { + * app.onhostcontextchanged = (ctx) => { * const container = document.getElementById("app-root"); - * if (container && params.styles?.variables) { - * applyHostStyleVariables(params.styles.variables, container); + * if (container && ctx.styles?.variables) { + * applyHostStyleVariables(ctx.styles.variables, container); * } * }; * ``` @@ -163,9 +163,9 @@ export function applyHostStyleVariables( * @example Apply fonts from host context * ```ts source="./styles.examples.ts#applyHostFonts_fromHostContext" * // Apply when host context changes - * app.onhostcontextchanged = (params) => { - * if (params.styles?.css?.fonts) { - * applyHostFonts(params.styles.css.fonts); + * app.onhostcontextchanged = (ctx) => { + * if (ctx.styles?.css?.fonts) { + * applyHostFonts(ctx.styles.css.fonts); * } * }; *