Improve patterns guide with concrete examples and safe area insets
- Rename "host styling" section to "host context" and add safe area insets handling - Replace abstract `renderLoadingUI`/`renderFinalUI` example with concrete code preview pattern for `ontoolinputpartial` - Rename callback parameter `params` → `ctx` for clarity across all `onhostcontextchanged` handlers - Add CSS snippet for fullscreen border-radius handling - Expand display mode example to show `availableDisplayModes` check - Fix minor typos and wording consistency Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6c8379ff3e
commit
c6c2428e0b
6 changed files with 215 additions and 136 deletions
|
|
@ -145,33 +145,25 @@ async function App_ontoolinput_setter(app: App) {
|
|||
*/
|
||||
function App_ontoolinputpartial_progressiveRendering(app: App) {
|
||||
//#region App_ontoolinputpartial_progressiveRendering
|
||||
let toolInputs: Record<string, unknown> | null = null;
|
||||
let toolInputsPartial: Record<string, unknown> | null = null;
|
||||
const codePreview = document.querySelector<HTMLPreElement>("#code-preview")!;
|
||||
const canvas = document.querySelector<HTMLCanvasElement>("#canvas")!;
|
||||
|
||||
app.ontoolinputpartial = (params) => {
|
||||
toolInputsPartial = params.arguments as Record<string, unknown>;
|
||||
render();
|
||||
codePreview.textContent = (params.arguments?.code as string) ?? "";
|
||||
codePreview.style.display = "block";
|
||||
canvas.style.display = "none";
|
||||
};
|
||||
|
||||
app.ontoolinput = (params) => {
|
||||
toolInputs = params.arguments as Record<string, unknown>;
|
||||
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<string, unknown> | null): void;
|
||||
declare function renderFinalUI(data: Record<string, unknown>): 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
|
||||
}
|
||||
|
|
|
|||
36
src/app.ts
36
src/app.ts
|
|
@ -358,27 +358,20 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
|||
*
|
||||
* @example Progressive rendering of tool arguments
|
||||
* ```ts source="./app.examples.ts#App_ontoolinputpartial_progressiveRendering"
|
||||
* let toolInputs: Record<string, unknown> | null = null;
|
||||
* let toolInputsPartial: Record<string, unknown> | null = null;
|
||||
* const codePreview = document.querySelector<HTMLPreElement>("#code-preview")!;
|
||||
* const canvas = document.querySelector<HTMLCanvasElement>("#canvas")!;
|
||||
*
|
||||
* app.ontoolinputpartial = (params) => {
|
||||
* toolInputsPartial = params.arguments as Record<string, unknown>;
|
||||
* render();
|
||||
* codePreview.textContent = (params.arguments?.code as string) ?? "";
|
||||
* codePreview.style.display = "block";
|
||||
* canvas.style.display = "none";
|
||||
* };
|
||||
*
|
||||
* app.ontoolinput = (params) => {
|
||||
* toolInputs = params.arguments as Record<string, unknown>;
|
||||
* 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<AppRequest, AppNotification, AppResult> {
|
|||
*
|
||||
* @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<AppRequest, AppNotification, AppResult> {
|
|||
*
|
||||
* @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");
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 }));
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 }));
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
* }
|
||||
* };
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue