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/demo-fixtures/container-status.ts b/webui/src/web-components/demo/demo-fixtures/container-status.ts
new file mode 100644
index 0000000..14c4964
--- /dev/null
+++ b/webui/src/web-components/demo/demo-fixtures/container-status.ts
@@ -0,0 +1,99 @@
+/**
+ * Shared fake container status data for demos
+ */
+
+import { State, CumulativeUsage } from "../../../types";
+
+export const sampleUsage: CumulativeUsage = {
+  start_time: "2024-01-15T10:00:00Z",
+  messages: 1337,
+  input_tokens: 25432,
+  output_tokens: 18765,
+  cache_read_input_tokens: 8234,
+  cache_creation_input_tokens: 12354,
+  total_cost_usd: 2.03,
+  tool_uses: {
+    bash: 45,
+    patch: 23,
+    think: 12,
+    "multiple-choice": 8,
+    keyword_search: 6,
+  },
+};
+
+export const sampleContainerState: State = {
+  state_version: 1,
+  message_count: 27,
+  total_usage: sampleUsage,
+  initial_commit: "decafbad42abc123",
+  slug: "file-upload-component",
+  branch_name: "sketch-wip",
+  branch_prefix: "sketch",
+  hostname: "example.hostname",
+  working_dir: "/app",
+  os: "linux",
+  git_origin: "https://github.com/user/repo.git",
+  outstanding_llm_calls: 0,
+  outstanding_tool_calls: null,
+  session_id: "session-abc123",
+  ssh_available: true,
+  in_container: true,
+  first_message_index: 0,
+  agent_state: "ready",
+  outside_hostname: "host.example.com",
+  inside_hostname: "container.local",
+  outside_os: "macOS",
+  inside_os: "linux",
+  outside_working_dir: "/Users/dev/project",
+  inside_working_dir: "/app",
+  todo_content:
+    "- Implement file upload component\n- Add drag and drop support\n- Write tests",
+  skaband_addr: "localhost:8080",
+  link_to_github: true,
+  ssh_connection_string: "ssh user@example.com",
+  diff_lines_added: 245,
+  diff_lines_removed: 67,
+};
+
+export const lightUsageState: State = {
+  ...sampleContainerState,
+  message_count: 5,
+  total_usage: {
+    ...sampleUsage,
+    messages: 5,
+    input_tokens: 1234,
+    output_tokens: 890,
+    total_cost_usd: 0.15,
+    tool_uses: {
+      bash: 2,
+      patch: 1,
+    },
+  },
+  diff_lines_added: 45,
+  diff_lines_removed: 12,
+};
+
+export const heavyUsageState: State = {
+  ...sampleContainerState,
+  message_count: 156,
+  total_usage: {
+    ...sampleUsage,
+    messages: 156,
+    input_tokens: 89234,
+    output_tokens: 67890,
+    cache_read_input_tokens: 23456,
+    cache_creation_input_tokens: 45678,
+    total_cost_usd: 12.45,
+    tool_uses: {
+      bash: 234,
+      patch: 89,
+      think: 67,
+      "multiple-choice": 23,
+      keyword_search: 45,
+      browser_navigate: 12,
+      codereview: 8,
+    },
+  },
+  diff_lines_added: 2847,
+  diff_lines_removed: 1456,
+};