This directory contains an automated demo system for Sketch web components that reduces maintenance overhead and provides a consistent development experience.
The demo system consists of:
*.demo.ts) - Component-specific demo configurationsdemo-framework/) - Shared infrastructure for loading and running demosdemo-fixtures/) - Common fake data and utilitiesdemo.html) - Interactive demo browser# Start the demo server npm run demo # Visit the demo runner open http://localhost:5173/src/web-components/demo/demo.html # Or view the auto-generated index open http://localhost:5173/src/web-components/demo/index-generated.html
your-component.demo.tsimport { DemoModule } from "./demo-framework/types"; import { demoUtils, sampleData } from "./demo-fixtures/index"; const demo: DemoModule = { title: "Your Component Demo", description: "Interactive demo showing component functionality", imports: ["your-component.ts"], // Component files to import setup: async (container: HTMLElement) => { // Create demo sections const section = demoUtils.createDemoSection( "Basic Usage", "Description of what this demo shows", ); // Create your component const component = document.createElement("your-component") as any; component.data = sampleData.yourData; // Add to container section.appendChild(component); container.appendChild(section); }, cleanup: async () => { // Optional cleanup when demo is unloaded }, }; export default demo;
cd src/web-components/demo npx tsx generate-index.ts
title: Display name for the demoimports: Array of component files to import (relative to parent directory)setup: Function that creates the demo contentdescription: Brief description of what the demo showsstyles: Additional CSS files to loadcustomStyles: Inline CSS stylescleanup: Function called when demo is unloadedThe setup function receives a container element and should populate it with demo content:
setup: async (container: HTMLElement) => { // Use demo utilities for consistent styling const section = demoUtils.createDemoSection("Title", "Description"); // Create and configure your component const component = document.createElement("my-component"); component.setAttribute("data", JSON.stringify(sampleData)); // Add interactive controls const button = demoUtils.createButton("Reset", () => { component.reset(); }); // Assemble the demo section.appendChild(component); section.appendChild(button); container.appendChild(section); };
The demo-fixtures/ directory contains reusable fake data and utilities:
import { sampleToolCalls, sampleTimelineMessages, sampleContainerState, demoUtils, } from "./demo-fixtures/index";
sampleToolCalls - Various tool call examplessampleTimelineMessages - Chat/timeline message datasampleContainerState - Container status informationdemoUtils - Helper functions for creating demo UI elementsdemoUtils.createDemoSection(title, description) - Create a styled demo sectiondemoUtils.createButton(text, onClick) - Create a styled buttondemoUtils.delay(ms) - Promise-based delay functionThe system is designed to work seamlessly with Vite:
.demo.ts files are compiled automaticallyTo convert an existing HTML demo:
setup functiondemo-fixtures/demoUtils callscustomStyles propertydemo/ ├── demo-framework/ │ ├── types.ts # TypeScript interfaces │ └── demo.ts # Demo loading and execution ├── demo-fixtures/ │ ├── tool-calls.ts # Tool call sample data │ ├── timeline-messages.ts # Message sample data │ ├── container-status.ts # Status sample data │ └── index.ts # Centralized exports ├── generate-index.ts # Index generation script ├── demo.html # Interactive demo browser ├── index-generated.html # Auto-generated index ├── *.demo.ts # Individual demo modules └── readme.md # This file
const demo: DemoModule = { // ... customStyles: ` .my-demo-container { background: #f0f0f0; padding: 20px; border-radius: 8px; } `, setup: async (container) => { container.className = "my-demo-container"; // ... }, };
setup: async (container) => { const messages = []; const timeline = document.createElement("sketch-timeline"); // Add messages progressively for (let i = 0; i < sampleMessages.length; i++) { await demoUtils.delay(500); messages.push(sampleMessages[i]); timeline.messages = [...messages]; } };
let intervalId: number; const demo: DemoModule = { // ... setup: async (container) => { // Set up interval for updates intervalId = setInterval(() => { updateComponent(); }, 1000); }, cleanup: async () => { // Clean up interval if (intervalId) { clearInterval(intervalId); } }, };
For more examples, see the existing demo modules in this directory.