feat(shadertoy): add streaming partial input, visibility optimization, host styling
Shadertoy server improvements: - Add ontoolinputpartial handler to show code preview during generation - Add IntersectionObserver to pause/play shader based on visibility - Apply host style variables (theme, colors, fonts) - Add border-radius with fullscreen mode support - Use gradient background and ghost text color for code preview Skill documentation updates: - Add streaming partial input section with use cases - Add visibility-based resource management pattern - Add fullscreen mode API and CSS patterns - Expand host styling with CSS variable usage examples
This commit is contained in:
parent
8c3b1daf7e
commit
f1a00f9e15
2 changed files with 187 additions and 3 deletions
|
|
@ -11,6 +11,12 @@ html, body {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
border-radius: var(--border-radius-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main.fullscreen {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
|
|
@ -19,6 +25,34 @@ html, body {
|
|||
display: block;
|
||||
}
|
||||
|
||||
#canvas.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#code-preview {
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
var(--color-background-secondary) 0%,
|
||||
var(--color-background-tertiary) 100%
|
||||
);
|
||||
color: var(--color-text-ghost);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--font-text-xs-size);
|
||||
line-height: var(--font-text-xs-line-height);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#code-preview.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Fullscreen button */
|
||||
.fullscreen-btn {
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
/**
|
||||
* ShaderToy renderer MCP App using ShaderToyLite.js
|
||||
*/
|
||||
import { App, type McpUiHostContext } from "@modelcontextprotocol/ext-apps";
|
||||
import {
|
||||
App,
|
||||
type McpUiHostContext,
|
||||
applyHostStyleVariables,
|
||||
applyDocumentTheme,
|
||||
} from "@modelcontextprotocol/ext-apps";
|
||||
import "./global.css";
|
||||
import "./mcp-app.css";
|
||||
import ShaderToyLite, {
|
||||
|
|
@ -34,6 +39,7 @@ const log = {
|
|||
// Get element references
|
||||
const mainEl = document.querySelector(".main") as HTMLElement;
|
||||
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
|
||||
const codePreview = document.getElementById("code-preview") as HTMLPreElement;
|
||||
const fullscreenBtn = document.getElementById(
|
||||
"fullscreen-btn",
|
||||
) as HTMLButtonElement;
|
||||
|
|
@ -49,8 +55,12 @@ function resizeCanvas() {
|
|||
resizeCanvas();
|
||||
window.addEventListener("resize", resizeCanvas);
|
||||
|
||||
// Handle host context changes (display mode)
|
||||
// Handle host context changes (display mode, styling)
|
||||
function handleHostContextChanged(ctx: McpUiHostContext) {
|
||||
// Apply host styling
|
||||
if (ctx.theme) applyDocumentTheme(ctx.theme);
|
||||
if (ctx.styles?.variables) applyHostStyleVariables(ctx.styles.variables);
|
||||
|
||||
// Note: We ignore safeAreaInsets to maximize shader display area
|
||||
|
||||
// Show fullscreen button if available (only update if field is present)
|
||||
|
|
@ -112,9 +122,22 @@ app.onteardown = async () => {
|
|||
return {};
|
||||
};
|
||||
|
||||
app.ontoolinputpartial = (params) => {
|
||||
// Show code preview, hide canvas
|
||||
codePreview.classList.add("visible");
|
||||
canvas.classList.add("hidden");
|
||||
const code = params.arguments?.fragmentShader;
|
||||
codePreview.textContent = typeof code === "string" ? code : "";
|
||||
codePreview.scrollTop = codePreview.scrollHeight;
|
||||
};
|
||||
|
||||
app.ontoolinput = (params) => {
|
||||
log.info("Received shader input");
|
||||
|
||||
// Hide code preview, show canvas
|
||||
codePreview.classList.remove("visible");
|
||||
canvas.classList.remove("hidden");
|
||||
|
||||
if (!isShaderInput(params.arguments)) {
|
||||
log.error("Invalid tool input");
|
||||
return;
|
||||
|
|
@ -162,6 +185,18 @@ app.onerror = log.error;
|
|||
|
||||
app.onhostcontextchanged = handleHostContextChanged;
|
||||
|
||||
// Pause/resume shader based on visibility
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
shaderToy?.play();
|
||||
} else {
|
||||
shaderToy?.pause();
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe(mainEl);
|
||||
|
||||
// Connect to host
|
||||
app.connect().then(() => {
|
||||
log.info("Connected to host");
|
||||
|
|
|
|||
Loading…
Reference in a new issue