ext-appss / src /react /useHostStyles.ts
AbdulElahGwaith's picture
Upload folder using huggingface_hub
e1cc3bc verified
import { useEffect, useRef } from "react";
import { App } from "../app";
import {
applyDocumentTheme,
applyHostFonts,
applyHostStyleVariables,
} from "../styles";
import { McpUiHostContext } from "../types";
/**
* React hook that applies host style variables and theme as CSS custom properties.
*
* This hook listens to host context changes and automatically applies:
* - `styles.variables` CSS variables to `document.documentElement` (e.g., `--color-background-primary`)
* - `theme` via `color-scheme` CSS property, enabling `light-dark()` CSS function support
*
* The hook also applies styles and theme from the initial host context when
* the app first connects.
*
* **Note:** If the host provides style values using CSS `light-dark()` function,
* this hook ensures they work correctly by setting the `color-scheme` property
* based on the host's theme preference.
*
* @param app - The connected {@link App} instance, or null during initialization
* @param initialContext - Initial host context from the connection (optional).
* If provided, styles and theme will be applied immediately on mount.
*
* @example
* ```tsx
* import { useApp, useHostStyleVariables } from '@modelcontextprotocol/ext-apps/react';
*
* function MyApp() {
* const { app, isConnected } = useApp({
* appInfo: { name: "MyApp", version: "1.0.0" },
* capabilities: {},
* });
*
* // Apply host styles - pass initial context to apply styles from connect() immediately
* useHostStyleVariables(app, app?.getHostContext());
*
* return (
* <div style={{ background: 'var(--color-background-primary)' }}>
* Hello!
* </div>
* );
* }
* ```
*
* @see {@link applyHostStyleVariables} for the underlying styles function
* @see {@link applyDocumentTheme} for the underlying theme function
* @see {@link useHostFonts} for applying host fonts
* @see {@link McpUiStyles} for available CSS variables
*/
export function useHostStyleVariables(
app: App | null,
initialContext?: McpUiHostContext | null,
): void {
const initialApplied = useRef(false);
// Apply initial styles and theme once on mount
useEffect(() => {
if (initialApplied.current) {
return;
}
if (initialContext?.theme) {
applyDocumentTheme(initialContext.theme);
}
if (initialContext?.styles?.variables) {
applyHostStyleVariables(initialContext.styles.variables);
}
if (initialContext?.theme || initialContext?.styles?.variables) {
initialApplied.current = true;
}
}, [initialContext]);
// Listen for host context changes and apply updated styles/theme
useEffect(() => {
if (!app) {
return;
}
app.onhostcontextchanged = (params) => {
if (params.theme) {
applyDocumentTheme(params.theme);
}
if (params.styles?.variables) {
applyHostStyleVariables(params.styles.variables);
}
};
}, [app]);
}
/**
* React hook that applies host fonts from CSS.
*
* This hook listens to host context changes and automatically applies:
* - `styles.css.fonts` as a `<style>` tag for custom fonts
*
* The CSS can contain `@font-face` rules for self-hosted fonts,
* `@import` statements for Google Fonts or other font services, or both.
*
* The hook also applies fonts from the initial host context when
* the app first connects.
*
* @param app - The connected {@link App} instance, or null during initialization
* @param initialContext - Initial host context from the connection (optional).
* If provided, fonts will be applied immediately on mount.
*
* @example Basic usage with useApp
* ```tsx
* import { useApp } from '@modelcontextprotocol/ext-apps/react';
* import { useHostFonts } from '@modelcontextprotocol/ext-apps/react';
*
* function MyApp() {
* const { app, isConnected } = useApp({
* appInfo: { name: "MyApp", version: "1.0.0" },
* capabilities: {},
* });
*
* // Automatically apply host fonts
* useHostFonts(app);
*
* return (
* <div style={{ fontFamily: 'var(--font-sans)' }}>
* Hello!
* </div>
* );
* }
* ```
*
* @example With initial context
* ```tsx
* const [hostContext, setHostContext] = useState<McpUiHostContext | null>(null);
*
* // ... get initial context from app.connect() result
*
* useHostFonts(app, hostContext);
* ```
*
* @see {@link applyHostFonts} for the underlying fonts function
* @see {@link useHostStyleVariables} for applying style variables and theme
*/
export function useHostFonts(
app: App | null,
initialContext?: McpUiHostContext | null,
): void {
const initialApplied = useRef(false);
// Apply initial fonts once on mount
useEffect(() => {
if (initialApplied.current) {
return;
}
if (initialContext?.styles?.css?.fonts) {
applyHostFonts(initialContext.styles.css.fonts);
initialApplied.current = true;
}
}, [initialContext]);
// Listen for host context changes and apply updated fonts
useEffect(() => {
if (!app) {
return;
}
app.onhostcontextchanged = (params) => {
if (params.styles?.css?.fonts) {
applyHostFonts(params.styles.css.fonts);
}
};
}, [app]);
}
/**
* Applies all host styling (CSS variables, theme, and fonts) to match the host application.
*
* This is a convenience hook that combines {@link useHostStyleVariables} and
* {@link useHostFonts}. Use the individual hooks if you need more control.
*
* @param app - The connected {@link App} instance, or null during initialization
* @param initialContext - Initial host context from the connection (optional).
* Pass `app?.getHostContext()` to apply styles immediately on mount.
*
* @example
* ```tsx
* function MyApp() {
* const { app } = useApp({ appInfo, capabilities: {} });
* useHostStyles(app, app?.getHostContext());
*
* return <div style={{ background: 'var(--color-background-primary)' }}>...</div>;
* }
* ```
*
* @see {@link useHostStyleVariables} for style variables and theme only
* @see {@link useHostFonts} for fonts only
*/
export function useHostStyles(
app: App | null,
initialContext?: McpUiHostContext | null,
): void {
useHostStyleVariables(app, initialContext);
useHostFonts(app, initialContext);
}