Add utility to set + read theme
This commit is contained in:
parent
aa8968808a
commit
a8b3bf0ccd
4 changed files with 150 additions and 2 deletions
|
|
@ -47,7 +47,11 @@ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|||
|
||||
export { PostMessageTransport } from "./message-transport";
|
||||
export * from "./types";
|
||||
export { applyHostStyles } from "./styles";
|
||||
export {
|
||||
applyHostStyles,
|
||||
getDocumentTheme,
|
||||
applyDocumentTheme,
|
||||
} from "./styles";
|
||||
|
||||
/**
|
||||
* Metadata key for associating a resource URI with a tool call.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
*
|
||||
* - {@link useApp} - React hook to create and connect an MCP App
|
||||
* - {@link useHostStyles} - React hook to apply host styles as CSS variables
|
||||
* - {@link useDocumentTheme} - React hook for reactive document theme
|
||||
* - {@link useAutoResize} - React hook for manual auto-resize control (rarely needed)
|
||||
*
|
||||
* @module @modelcontextprotocol/ext-apps/react
|
||||
|
|
@ -33,4 +34,5 @@
|
|||
*/
|
||||
export * from "./useApp";
|
||||
export * from "./useAutoResize";
|
||||
export * from "./useDocumentTheme";
|
||||
export * from "./useHostStyles";
|
||||
|
|
|
|||
69
src/react/useDocumentTheme.ts
Normal file
69
src/react/useDocumentTheme.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { getDocumentTheme } from "../styles";
|
||||
import { McpUiTheme } from "../types";
|
||||
|
||||
/**
|
||||
* React hook that provides the current document theme reactively.
|
||||
*
|
||||
* Uses a MutationObserver to watch for changes to the `data-theme` attribute
|
||||
* or `class` on `document.documentElement`. When the theme changes (e.g., from
|
||||
* host context updates), the hook automatically re-renders your component with
|
||||
* the new theme value.
|
||||
*
|
||||
* @returns The current theme ("light" or "dark")
|
||||
*
|
||||
* @example Conditionally render based on theme
|
||||
* ```tsx
|
||||
* import { useDocumentTheme } from '@modelcontextprotocol/ext-apps/react';
|
||||
*
|
||||
* function MyApp() {
|
||||
* const theme = useDocumentTheme();
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* {theme === 'dark' ? <DarkIcon /> : <LightIcon />}
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example Use with theme-aware styling
|
||||
* ```tsx
|
||||
* function ThemedButton() {
|
||||
* const theme = useDocumentTheme();
|
||||
*
|
||||
* return (
|
||||
* <button style={{
|
||||
* background: theme === 'dark' ? '#333' : '#fff',
|
||||
* color: theme === 'dark' ? '#fff' : '#333',
|
||||
* }}>
|
||||
* Click me
|
||||
* </button>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see {@link getDocumentTheme} for the underlying function
|
||||
* @see {@link applyDocumentTheme} to set the theme
|
||||
*/
|
||||
export function useDocumentTheme(): McpUiTheme {
|
||||
const [theme, setTheme] = useState<McpUiTheme>(getDocumentTheme);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new MutationObserver(() => {
|
||||
setTheme(getDocumentTheme());
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme", "class"],
|
||||
characterData: false,
|
||||
childList: false,
|
||||
subtree: false,
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
|
@ -1,4 +1,77 @@
|
|||
import { McpUiStyles } from "./types";
|
||||
import { McpUiStyles, McpUiTheme } from "./types";
|
||||
|
||||
/**
|
||||
* Get the current document theme from the root HTML element.
|
||||
*
|
||||
* Reads the theme from the `data-theme` attribute on `document.documentElement`.
|
||||
* Falls back to checking for a `dark` class for compatibility with Tailwind CSS
|
||||
* dark mode conventions.
|
||||
*
|
||||
* @returns The current theme ("light" or "dark")
|
||||
*
|
||||
* @example Check current theme
|
||||
* ```typescript
|
||||
* import { getDocumentTheme } from '@modelcontextprotocol/ext-apps';
|
||||
*
|
||||
* const theme = getDocumentTheme();
|
||||
* console.log(`Current theme: ${theme}`);
|
||||
* ```
|
||||
*
|
||||
* @see {@link applyDocumentTheme} to set the theme
|
||||
* @see {@link McpUiTheme} for the theme type
|
||||
*/
|
||||
export function getDocumentTheme(): McpUiTheme {
|
||||
const theme = document.documentElement.getAttribute("data-theme");
|
||||
|
||||
if (theme === "dark" || theme === "light") {
|
||||
return theme;
|
||||
}
|
||||
|
||||
// Fallback: check for "dark" class (Tailwind CSS convention)
|
||||
const darkMode = document.documentElement.classList.contains("dark");
|
||||
|
||||
return darkMode ? "dark" : "light";
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a theme to the document root element.
|
||||
*
|
||||
* Sets the `data-theme` attribute and CSS `color-scheme` property on
|
||||
* `document.documentElement`. This enables CSS selectors like
|
||||
* `[data-theme="dark"]` and ensures native elements (scrollbars, form controls)
|
||||
* respect the theme.
|
||||
*
|
||||
* @param theme - The theme to apply ("light" or "dark")
|
||||
*
|
||||
* @example Apply theme from host context
|
||||
* ```typescript
|
||||
* import { applyDocumentTheme } from '@modelcontextprotocol/ext-apps';
|
||||
*
|
||||
* app.onhostcontextchanged = (params) => {
|
||||
* if (params.theme) {
|
||||
* applyDocumentTheme(params.theme);
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @example Use with CSS selectors
|
||||
* ```css
|
||||
* [data-theme="dark"] {
|
||||
* --bg-color: #1a1a1a;
|
||||
* }
|
||||
* [data-theme="light"] {
|
||||
* --bg-color: #ffffff;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see {@link getDocumentTheme} to read the current theme
|
||||
* @see {@link McpUiTheme} for the theme type
|
||||
*/
|
||||
export function applyDocumentTheme(theme: McpUiTheme): void {
|
||||
const root = document.documentElement;
|
||||
root.setAttribute("data-theme", theme);
|
||||
root.style.colorScheme = theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply host styles as CSS custom properties on an element.
|
||||
|
|
|
|||
Loading…
Reference in a new issue