webui: implement modular demo system with TypeScript and shared fixtures

Replace hand-written HTML demo pages with TypeScript demo modules and
automated infrastructure to reduce maintenance overhead and improve
developer experience with type safety and shared code.

Problems Solved:

Demo Maintenance Overhead:
- Hand-written HTML demo pages contained extensive boilerplate duplication
- No type checking for demo setup code or component data
- Manual maintenance of demo/index.html with available demos
- Difficult to share common fake data between demo pages
- No hot module replacement for demo development

Code Quality and Consistency:
- Demo setup code written in plain JavaScript without type safety
- No validation that demo data matches component interfaces
- Inconsistent styling and structure across demo pages
- Duplicated fake data declarations in each demo file

Solution Architecture:

TypeScript Demo Module System:
- Created DemoModule interface for standardized demo structure
- Demo modules export title, description, imports, and setup functions
- Full TypeScript compilation with type checking for demo code
- Dynamic import system for on-demand demo loading with Vite integration

Shared Demo Infrastructure:
- demo-framework/ with types.ts and demo-runner.ts for core functionality
- DemoRunner class handles dynamic loading, cleanup, and error handling
- Single demo-runner.html page loads any demo module dynamically
- Supports URL hash routing for direct demo links

Centralized Fake Data:
- demo-fixtures/ directory with shared TypeScript data files
- sampleToolCalls, sampleTimelineMessages, and sampleContainerState
- Type-safe imports ensure demo data matches component interfaces
- demoUtils with helper functions for consistent demo UI creation

Auto-generated Index Page:
- generate-index.ts scans for *.demo.ts files and extracts metadata
- Creates index-generated.html with links to all available demos
- Automatically includes demo titles and descriptions
- Eliminates manual maintenance of demo listing

Implementation Details:

Demo Framework:
- DemoRunner.loadDemo() uses dynamic imports with Vite ignore comments
- Automatic component import based on demo module configuration
- Support for demo-specific CSS and cleanup functions
- Error handling with detailed error display for debugging

Demo Module Structure:
- sketch-chat-input.demo.ts: Interactive chat with message history
- sketch-container-status.demo.ts: Status variations with real-time updates
- sketch-tool-calls.demo.ts: Multiple tool call examples with progressive loading
- All use shared fixtures and utilities for consistent experience

Vite Integration:
- Hot Module Replacement works for demo modules and shared fixtures
- TypeScript compilation on-the-fly for immediate feedback
- Dynamic imports work seamlessly with Vite's module system
- @vite-ignore comments prevent import analysis warnings

Testing and Validation:
- Tested demo runner loads and displays available components
- Verified component discovery and dynamic import functionality
- Confirmed shared fixture imports work correctly
- Validated auto-generated index creation and content

Files Modified:
- demo-framework/types.ts: TypeScript interfaces for demo system
- demo-framework/demo-runner.ts: Core demo loading and execution logic
- demo-fixtures/: Shared fake data (tool-calls.ts, timeline-messages.ts, container-status.ts, index.ts)
- demo-runner.html: Interactive demo browser with sidebar navigation
- generate-index.ts: Auto-generation script for demo index
- sketch-chat-input.demo.ts: Converted chat input demo to TypeScript
- sketch-container-status.demo.ts: Container status demo with variations
- sketch-tool-calls.demo.ts: Tool calls demo with interactive examples
- readme.md: Comprehensive documentation for new demo system

Benefits:
- Developers get full TypeScript type checking for demo code
- Shared fake data ensures consistency and reduces duplication
- Hot module replacement provides instant feedback during development
- Auto-generated index eliminates manual maintenance
- Modular architecture makes it easy to add new demos
- Vite integration provides fast development iteration

The new system reduces demo maintenance overhead while providing
better developer experience through TypeScript, shared code, and
automated infrastructure.

Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s3d91894eb7c4a79fk
diff --git a/webui/src/web-components/demo/readme.md b/webui/src/web-components/demo/readme.md
index 324d077..686eb63 100644
--- a/webui/src/web-components/demo/readme.md
+++ b/webui/src/web-components/demo/readme.md
@@ -1,5 +1,257 @@
-# Stand-alone demo pages for sketch web components
+# Sketch Web Components Demo System
 
-These are handy for iterating on specific component UI issues in isolation from the rest of the sketch application, and without having to start a full backend to serve the full frontend app UI.
+This directory contains an automated demo system for Sketch web components that reduces maintenance overhead and provides a consistent development experience.
 
-See [README](../../../readme.md#development-mode) for more information on how to run the demo pages.
+## Overview
+
+The demo system consists of:
+
+- **TypeScript Demo Modules** (`*.demo.ts`) - Component-specific demo configurations
+- **Demo Framework** (`demo-framework/`) - Shared infrastructure for loading and running demos
+- **Shared Fixtures** (`demo-fixtures/`) - Common fake data and utilities
+- **Demo Runner** (`demo-runner.html`) - Interactive demo browser
+- **Auto-generated Index** - Automatically maintained list of available demos
+
+## Quick Start
+
+### Running Demos
+
+```bash
+# Start the demo server
+npm run demo
+
+# Visit the demo runner
+open http://localhost:5173/src/web-components/demo/demo-runner.html
+
+# Or view the auto-generated index
+open http://localhost:5173/src/web-components/demo/index-generated.html
+```
+
+### Creating a New Demo
+
+1. Create a new demo module file: `your-component.demo.ts`
+
+```typescript
+import { 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;
+```
+
+2. Regenerate the index:
+
+```bash
+cd src/web-components/demo
+npx tsx generate-index.ts
+```
+
+3. Your demo will automatically appear in the demo runner!
+
+## Demo Module Structure
+
+### Required Properties
+
+- `title`: Display name for the demo
+- `imports`: Array of component files to import (relative to parent directory)
+- `setup`: Function that creates the demo content
+
+### Optional Properties
+
+- `description`: Brief description of what the demo shows
+- `styles`: Additional CSS files to load
+- `customStyles`: Inline CSS styles
+- `cleanup`: Function called when demo is unloaded
+
+### Setup Function
+
+The setup function receives a container element and should populate it with demo content:
+
+```typescript
+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);
+};
+```
+
+## Shared Fixtures
+
+The `demo-fixtures/` directory contains reusable fake data and utilities:
+
+```typescript
+import {
+  sampleToolCalls,
+  sampleTimelineMessages,
+  sampleContainerState,
+  demoUtils,
+} from "./demo-fixtures/index";
+```
+
+### Available Fixtures
+
+- `sampleToolCalls` - Various tool call examples
+- `sampleTimelineMessages` - Chat/timeline message data
+- `sampleContainerState` - Container status information
+- `demoUtils` - Helper functions for creating demo UI elements
+
+### Demo Utilities
+
+- `demoUtils.createDemoSection(title, description)` - Create a styled demo section
+- `demoUtils.createButton(text, onClick)` - Create a styled button
+- `demoUtils.delay(ms)` - Promise-based delay function
+
+## Benefits of This System
+
+### For Developers
+
+- **TypeScript Support**: Full type checking for demo code and shared data
+- **Hot Module Replacement**: Instant updates when demo code changes
+- **Shared Data**: Consistent fake data across all demos
+- **Reusable Utilities**: Common demo patterns abstracted into utilities
+- **Auto-discovery**: New demos automatically appear in the index
+
+### For Maintenance
+
+- **No Boilerplate**: No need to copy HTML structure between demos
+- **Centralized Styling**: Demo appearance controlled in one place
+- **Automated Index**: Never forget to update the index page
+- **Type Safety**: Catch errors early with TypeScript compilation
+
+## Vite Integration
+
+The system is designed to work seamlessly with Vite:
+
+- **Dynamic Imports**: Demo modules are loaded on demand
+- **TypeScript Compilation**: `.demo.ts` files are compiled automatically
+- **HMR Support**: Changes to demos or fixtures trigger instant reloads
+- **Dependency Tracking**: Vite knows when to reload based on imports
+
+## Migration from HTML Demos
+
+To convert an existing HTML demo:
+
+1. Extract the component setup JavaScript into a `setup` function
+2. Move shared data to `demo-fixtures/`
+3. Replace HTML boilerplate with `demoUtils` calls
+4. Convert inline styles to `customStyles` property
+5. Test with the demo runner
+
+## File Structure
+
+```
+demo/
+├── demo-framework/
+│   ├── types.ts           # TypeScript interfaces
+│   └── demo-runner.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-runner.html       # Interactive demo browser
+├── index-generated.html   # Auto-generated index
+├── *.demo.ts             # Individual demo modules
+└── readme.md             # This file
+```
+
+## Advanced Usage
+
+### Custom Styling
+
+```typescript
+const demo: DemoModule = {
+  // ...
+  customStyles: `
+    .my-demo-container {
+      background: #f0f0f0;
+      padding: 20px;
+      border-radius: 8px;
+    }
+  `,
+  setup: async (container) => {
+    container.className = "my-demo-container";
+    // ...
+  },
+};
+```
+
+### Progressive Loading
+
+```typescript
+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];
+  }
+};
+```
+
+### Cleanup
+
+```typescript
+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.