Implement multi-scheme animation name mapper for Owen Animation System
Some checks failed
CI/CD Pipeline / Test & Lint (16.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (18.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (20.x) (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Release (push) Has been cancelled
Demo Deployment / Build Demo (push) Has been cancelled
Demo Deployment / Test Demo (push) Has been cancelled
Demo Deployment / Performance Audit (push) Has been cancelled
Demo Deployment / Deploy to Staging (push) Has been cancelled
Demo Deployment / Deploy to Production (push) Has been cancelled
Animation Processing Pipeline / Validate Animation Names (push) Has been cancelled
Animation Processing Pipeline / Process Blender Animation Assets (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (artist) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (hierarchical) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (legacy) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (semantic) (push) Has been cancelled
Multi-Scheme Testing / Test Scheme Conversions (push) Has been cancelled
Multi-Scheme Testing / Validate Demo Functionality (push) Has been cancelled
Multi-Scheme Testing / Performance Benchmarks (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, semantic) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, semantic) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, semantic) (push) Has been cancelled
Performance Testing / Memory Usage Analysis (push) Has been cancelled
Performance Testing / Demo Performance Audit (push) Has been cancelled
Animation Processing Pipeline / Update Animation Documentation (push) Has been cancelled
Animation Processing Pipeline / Deploy Animation Demo (push) Has been cancelled
Performance Testing / Generate Performance Report (push) Has been cancelled

- Added AnimationNameMapper class to handle conversion between different animation naming schemes (legacy, artist, hierarchical, semantic).
- Included methods for initialization, pattern matching, conversion, and validation of animation names.
- Developed comprehensive unit tests for the animation name converter and demo pages using Playwright.
- Created a Vite configuration for the demo application, including asset handling and optimization settings.
- Enhanced the demo with features for batch conversion, performance metrics, and responsive design.
This commit is contained in:
2025-05-24 05:18:13 +02:00
parent d513e80c07
commit b447abee00
54 changed files with 14343 additions and 989 deletions

337
demo/comparison.html Normal file
View File

@ -0,0 +1,337 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Scheme Comparison - Owen Animation System</title>
<link rel="stylesheet" href="./styles/main.css" />
<link rel="stylesheet" href="./styles/comparison.css" />
</head>
<body>
<header class="demo-header">
<div class="container">
<h1 class="logo">
<span class="logo-text">Owen</span>
<span class="logo-subtitle">Scheme Comparison</span>
</h1>
<nav class="demo-nav">
<a href="index.html" class="nav-link">Demo</a>
<a href="examples.html" class="nav-link">Examples</a>
<a href="comparison.html" class="nav-link active">Comparison</a>
<a href="interactive.html" class="nav-link">Interactive</a>
</nav>
</div>
</header>
<main class="comparison-main">
<div class="container">
<section class="comparison-hero">
<h2>Animation Naming Scheme Comparison</h2>
<p>
Compare the four supported naming schemes and understand when to use
each one.
</p>
</section>
<!-- Scheme Overview -->
<section class="schemes-overview">
<div class="scheme-cards">
<div class="scheme-card" data-scheme="legacy">
<h3>Legacy</h3>
<div class="scheme-pattern">snake_case</div>
<div class="scheme-description">
Traditional lowercase with underscores. Compatible with older
animation systems.
</div>
<div class="scheme-example">walk_forward</div>
</div>
<div class="scheme-card" data-scheme="artist">
<h3>Artist</h3>
<div class="scheme-pattern">PascalCase</div>
<div class="scheme-description">
Artist-friendly naming with clear capitalization. Intuitive for
content creators.
</div>
<div class="scheme-example">WalkForward</div>
</div>
<div class="scheme-card" data-scheme="hierarchical">
<h3>Hierarchical</h3>
<div class="scheme-pattern">dot.notation</div>
<div class="scheme-description">
Structured hierarchy with dots. Excellent for organizing complex
animation sets.
</div>
<div class="scheme-example">character.movement.walk.forward</div>
</div>
<div class="scheme-card" data-scheme="semantic">
<h3>Semantic</h3>
<div class="scheme-pattern">descriptive_names</div>
<div class="scheme-description">
Semantic meaning with underscores. Clear intent and
self-documenting.
</div>
<div class="scheme-example">character_walk_forward</div>
</div>
</div>
</section>
<!-- Interactive Comparison Table -->
<section class="comparison-table-section">
<h3>Animation Name Comparison</h3>
<div class="table-controls">
<button class="filter-btn active" data-category="all">All</button>
<button class="filter-btn" data-category="movement">
Movement
</button>
<button class="filter-btn" data-category="combat">Combat</button>
<button class="filter-btn" data-category="idle">Idle</button>
<button class="filter-btn" data-category="interaction">
Interaction
</button>
</div>
<div class="comparison-table-container">
<table class="comparison-table" id="animation-comparison-table">
<thead>
<tr>
<th>Animation Type</th>
<th>Legacy</th>
<th>Artist</th>
<th>Hierarchical</th>
<th>Semantic</th>
</tr>
</thead>
<tbody>
<!-- Table will be populated by JavaScript -->
</tbody>
</table>
</div>
</section>
<!-- Detailed Comparison -->
<section class="detailed-comparison">
<h3>Detailed Analysis</h3>
<div class="comparison-aspects">
<div class="aspect-card">
<h4>🎯 Use Cases</h4>
<div class="aspect-content">
<div class="use-case">
<strong>Legacy:</strong> Migrating from older systems,
maintaining backward compatibility
</div>
<div class="use-case">
<strong>Artist:</strong> Content creation workflows,
artist-friendly tools
</div>
<div class="use-case">
<strong>Hierarchical:</strong> Large animation libraries,
complex character systems
</div>
<div class="use-case">
<strong>Semantic:</strong> Modern development, clear
documentation needs
</div>
</div>
</div>
<div class="aspect-card">
<h4>⚡ Performance</h4>
<div class="performance-metrics">
<div class="metric">
<span class="metric-name">Lookup Speed:</span>
<div class="metric-bars">
<div class="metric-bar legacy" style="width: 95%">
Legacy
</div>
<div class="metric-bar artist" style="width: 90%">
Artist
</div>
<div class="metric-bar hierarchical" style="width: 85%">
Hierarchical
</div>
<div class="metric-bar semantic" style="width: 92%">
Semantic
</div>
</div>
</div>
<div class="metric">
<span class="metric-name">Memory Usage:</span>
<div class="metric-bars">
<div class="metric-bar legacy" style="width: 88%">
Legacy
</div>
<div class="metric-bar artist" style="width: 85%">
Artist
</div>
<div class="metric-bar hierarchical" style="width: 75%">
Hierarchical
</div>
<div class="metric-bar semantic" style="width: 82%">
Semantic
</div>
</div>
</div>
</div>
</div>
<div class="aspect-card">
<h4>🛠️ Developer Experience</h4>
<div class="aspect-content">
<div class="dx-rating">
<div class="dx-item">
<span>Readability:</span>
<div class="rating-stars">
<div class="stars legacy">★★★☆☆</div>
<div class="stars artist">★★★★☆</div>
<div class="stars hierarchical">★★★★★</div>
<div class="stars semantic">★★★★★</div>
</div>
</div>
<div class="dx-item">
<span>Autocomplete:</span>
<div class="rating-stars">
<div class="stars legacy">★★★☆☆</div>
<div class="stars artist">★★★★☆</div>
<div class="stars hierarchical">★★★★★</div>
<div class="stars semantic">★★★★☆</div>
</div>
</div>
<div class="dx-item">
<span>Maintainability:</span>
<div class="rating-stars">
<div class="stars legacy">★★☆☆☆</div>
<div class="stars artist">★★★☆☆</div>
<div class="stars hierarchical">★★★★★</div>
<div class="stars semantic">★★★★☆</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Migration Guide -->
<section class="migration-guide">
<h3>Migration Between Schemes</h3>
<div class="migration-matrix">
<div class="migration-card">
<h4>From Legacy</h4>
<ul>
<li>
<strong>To Artist:</strong> Capitalize first letter and after
underscores
</li>
<li>
<strong>To Hierarchical:</strong> Replace underscores with
dots, add category prefixes
</li>
<li>
<strong>To Semantic:</strong> Add descriptive prefixes
(character_, ui_, effect_)
</li>
</ul>
</div>
<div class="migration-card">
<h4>From Artist</h4>
<ul>
<li>
<strong>To Legacy:</strong> Convert to lowercase, add
underscores before capitals
</li>
<li>
<strong>To Hierarchical:</strong> Split on capitals, join with
dots, add categories
</li>
<li>
<strong>To Semantic:</strong> Convert to lowercase with
underscores, add prefixes
</li>
</ul>
</div>
<div class="migration-card">
<h4>Automated Tools</h4>
<ul>
<li>
<strong>CLI Converter:</strong>
<code>owen convert --from legacy --to semantic</code>
</li>
<li>
<strong>Batch Processing:</strong>
<code>owen batch-convert ./animations/</code>
</li>
<li>
<strong>Validation:</strong>
<code>owen validate --scheme semantic</code>
</li>
</ul>
</div>
</div>
</section>
<!-- Best Practices -->
<section class="best-practices">
<h3>Best Practices & Recommendations</h3>
<div class="practices-grid">
<div class="practice-card">
<h4>🏢 Enterprise Projects</h4>
<p><strong>Recommended:</strong> Hierarchical or Semantic</p>
<ul>
<li>Clear organization structure</li>
<li>Easy to maintain and scale</li>
<li>Good IDE support</li>
</ul>
</div>
<div class="practice-card">
<h4>🎨 Artist Workflows</h4>
<p><strong>Recommended:</strong> Artist or Semantic</p>
<ul>
<li>Intuitive for content creators</li>
<li>Clear visual distinction</li>
<li>Good tool integration</li>
</ul>
</div>
<div class="practice-card">
<h4>🔄 Legacy Migration</h4>
<p><strong>Recommended:</strong> Gradual transition</p>
<ul>
<li>Start with Legacy scheme</li>
<li>Use auto-conversion features</li>
<li>Migrate incrementally</li>
</ul>
</div>
<div class="practice-card">
<h4>🚀 New Projects</h4>
<p><strong>Recommended:</strong> Semantic scheme</p>
<ul>
<li>Modern best practices</li>
<li>Self-documenting code</li>
<li>Future-proof design</li>
</ul>
</div>
</div>
</section>
</div>
</main>
<footer class="demo-footer">
<div class="container">
<p>
&copy; 2024 Owen Animation System. Choose the scheme that fits your
workflow.
</p>
</div>
</footer>
<script type="module" src="./js/comparison.js"></script>
</body>
</html>

400
demo/examples.html Normal file
View File

@ -0,0 +1,400 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Examples - Owen Animation System</title>
<link rel="stylesheet" href="./styles/main.css" />
<link rel="stylesheet" href="./styles/examples.css" />
</head>
<body>
<header class="demo-header">
<div class="container">
<h1 class="logo">
<span class="logo-text">Owen</span>
<span class="logo-subtitle">Examples</span>
</h1>
<nav class="demo-nav">
<a href="index.html" class="nav-link">Demo</a>
<a href="examples.html" class="nav-link active">Examples</a>
<a href="comparison.html" class="nav-link">Comparison</a>
<a href="interactive.html" class="nav-link">Interactive</a>
</nav>
</div>
</header>
<main class="examples-main">
<div class="container">
<section class="examples-hero">
<h2>Code Examples & Integration Patterns</h2>
<p>
Explore practical examples of using the Owen Animation System in
different frameworks and scenarios.
</p>
</section>
<section class="examples-grid">
<div class="example-category">
<h3>Framework Integration</h3>
<div class="example-cards">
<div class="example-card">
<h4>React Integration</h4>
<p>Complete React component with animation state management</p>
<a href="#react-example" class="example-link">View Example</a>
</div>
<div class="example-card">
<h4>Vue Integration</h4>
<p>Vue 3 composition API with reactive animation controls</p>
<a href="#vue-example" class="example-link">View Example</a>
</div>
<div class="example-card">
<h4>Node.js Server</h4>
<p>Server-side animation processing and validation</p>
<a href="#nodejs-example" class="example-link">View Example</a>
</div>
</div>
</div>
<div class="example-category">
<h3>Multi-Scheme Usage</h3>
<div class="example-cards">
<div class="example-card">
<h4>Scheme Conversion</h4>
<p>Converting animations between different naming schemes</p>
<a href="#conversion-example" class="example-link"
>View Example</a
>
</div>
<div class="example-card">
<h4>Batch Processing</h4>
<p>Processing multiple animations with automated conversion</p>
<a href="#batch-example" class="example-link">View Example</a>
</div>
<div class="example-card">
<h4>Validation Pipeline</h4>
<p>Complete validation workflow with error handling</p>
<a href="#validation-example" class="example-link"
>View Example</a
>
</div>
</div>
</div>
<div class="example-category">
<h3>Advanced Features</h3>
<div class="example-cards">
<div class="example-card">
<h4>Custom Schemes</h4>
<p>Creating and registering custom naming schemes</p>
<a href="#custom-example" class="example-link">View Example</a>
</div>
<div class="example-card">
<h4>Performance Optimization</h4>
<p>Optimizing animation loading and caching strategies</p>
<a href="#performance-example" class="example-link"
>View Example</a
>
</div>
<div class="example-card">
<h4>Testing Integration</h4>
<p>Unit and integration testing for animation systems</p>
<a href="#testing-example" class="example-link">View Example</a>
</div>
</div>
</div>
</section>
<!-- Detailed Examples -->
<section class="detailed-examples">
<div id="react-example" class="example-detail">
<h3>React Integration Example</h3>
<pre><code class="javascript">import React, { useEffect, useRef, useState } from 'react'
import { OwenAnimationContext } from '@kjanat/owen'
export function AnimatedCharacter({ characterModel, namingScheme = 'semantic' }) {
const containerRef = useRef()
const [animationContext, setAnimationContext] = useState(null)
const [currentAnimation, setCurrentAnimation] = useState('idle')
const [isPlaying, setIsPlaying] = useState(false)
useEffect(() => {
// Initialize Owen Animation Context
const context = new OwenAnimationContext({
namingScheme,
autoConvert: true,
container: containerRef.current
})
context.loadModel(characterModel).then(() => {
setAnimationContext(context)
})
return () => context?.dispose()
}, [characterModel, namingScheme])
const playAnimation = async (animationName) => {
if (!animationContext) return
try {
await animationContext.playAnimation(animationName)
setCurrentAnimation(animationName)
setIsPlaying(true)
} catch (error) {
console.error('Failed to play animation:', error)
}
}
const stopAnimation = () => {
animationContext?.stopAnimation()
setIsPlaying(false)
}
return (
&lt;div className="animated-character"&gt;
&lt;div ref={containerRef} className="character-viewport" /&gt;
&lt;div className="animation-controls"&gt;
&lt;button onClick={() => playAnimation('walk_forward')}&gt;
Walk
&lt;/button&gt;
&lt;button onClick={() => playAnimation('character_run')}&gt;
Run
&lt;/button&gt;
&lt;button onClick={() => playAnimation('jump_high')}&gt;
Jump
&lt;/button&gt;
&lt;button onClick={stopAnimation}&gt;
Stop
&lt;/button&gt;
&lt;/div&gt;
&lt;div className="animation-info"&gt;
&lt;p&gt;Current: {currentAnimation}&lt;/p&gt;
&lt;p&gt;Status: {isPlaying ? 'Playing' : 'Stopped'}&lt;/p&gt;
&lt;p&gt;Scheme: {namingScheme}&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
)
}</code></pre>
</div>
<div id="conversion-example" class="example-detail">
<h3>Animation Name Conversion</h3>
<pre><code class="javascript">import { AnimationNameMapper } from '@kjanat/owen'
// Initialize the mapper
const mapper = new AnimationNameMapper()
// Single animation conversion
function convertAnimation(animationName, fromScheme, toScheme) {
try {
const converted = mapper.convert(animationName, toScheme, fromScheme)
console.log(`${fromScheme}: ${animationName} → ${toScheme}: ${converted}`)
return converted
} catch (error) {
console.error('Conversion failed:', error.message)
return null
}
}
// Batch conversion with validation
function convertAnimationBatch(animations, fromScheme, toScheme) {
const results = {
successful: [],
failed: [],
conflicts: []
}
animations.forEach(anim => {
try {
const converted = mapper.convert(anim, toScheme, fromScheme)
// Check for conflicts
if (results.successful.includes(converted)) {
results.conflicts.push({
original: anim,
converted,
conflict: 'Duplicate target name'
})
} else {
results.successful.push({
original: anim,
converted,
schemes: { from: fromScheme, to: toScheme }
})
}
} catch (error) {
results.failed.push({
original: anim,
error: error.message,
suggestions: mapper.suggestCorrections(anim, fromScheme)
})
}
})
return results
}
// Example usage
const legacyAnimations = [
'walk_forward', 'run_fast', 'jump_high',
'attack_sword', 'defend_shield', 'idle_breathing'
]
const conversionResults = convertAnimationBatch(
legacyAnimations,
'legacy',
'semantic'
)
console.log('Conversion Results:', conversionResults)</code></pre>
</div>
<div id="batch-example" class="example-detail">
<h3>Batch Processing Pipeline</h3>
<pre><code class="javascript">import { AnimationProcessor } from '@kjanat/owen'
import fs from 'fs/promises'
import path from 'path'
class AnimationBatchProcessor {
constructor(options = {}) {
this.processor = new AnimationProcessor(options)
this.inputDir = options.inputDir || './assets/raw'
this.outputDir = options.outputDir || './assets/processed'
this.targetScheme = options.targetScheme || 'semantic'
}
async processDirectory() {
console.log('Starting batch animation processing...')
try {
// Scan input directory
const files = await this.scanAnimationFiles()
console.log(`Found ${files.length} animation files`)
// Process each file
const results = await Promise.allSettled(
files.map(file => this.processFile(file))
)
// Generate summary report
const summary = this.generateSummary(results)
await this.saveReport(summary)
return summary
} catch (error) {
console.error('Batch processing failed:', error)
throw error
}
}
async scanAnimationFiles() {
const files = []
const entries = await fs.readdir(this.inputDir, { withFileTypes: true })
for (const entry of entries) {
if (entry.isFile() && /\.(gltf|glb|fbx)$/i.test(entry.name)) {
files.push(path.join(this.inputDir, entry.name))
}
}
return files
}
async processFile(inputFile) {
const filename = path.basename(inputFile)
console.log(`Processing: ${filename}`)
try {
// Load and analyze animation
const animation = await this.processor.loadAnimation(inputFile)
// Convert naming scheme
const convertedName = this.processor.convertName(
animation.name,
this.targetScheme
)
// Apply optimizations
const optimized = await this.processor.optimize(animation)
// Save processed animation
const outputFile = path.join(this.outputDir, `${convertedName}.gltf`)
await this.processor.saveAnimation(optimized, outputFile)
return {
status: 'success',
inputFile,
outputFile,
originalName: animation.name,
convertedName,
size: optimized.size,
duration: optimized.duration
}
} catch (error) {
return {
status: 'error',
inputFile,
error: error.message
}
}
}
generateSummary(results) {
const successful = results.filter(r => r.value?.status === 'success')
const failed = results.filter(r => r.status === 'rejected' || r.value?.status === 'error')
return {
timestamp: new Date().toISOString(),
total: results.length,
successful: successful.length,
failed: failed.length,
successRate: (successful.length / results.length * 100).toFixed(2),
details: {
successful: successful.map(r => r.value),
failed: failed.map(r => ({
file: r.value?.inputFile || 'unknown',
error: r.value?.error || r.reason
}))
}
}
}
async saveReport(summary) {
const reportPath = path.join(this.outputDir, 'processing-report.json')
await fs.mkdir(path.dirname(reportPath), { recursive: true })
await fs.writeFile(reportPath, JSON.stringify(summary, null, 2))
console.log(`Report saved: ${reportPath}`)
}
}
// Usage
const processor = new AnimationBatchProcessor({
inputDir: './assets/blender-exports',
outputDir: './assets/animations',
targetScheme: 'semantic'
})
processor.processDirectory().then(summary => {
console.log('Processing complete:', summary)
}).catch(error => {
console.error('Processing failed:', error)
process.exit(1)
})</code></pre>
</div>
</section>
</div>
</main>
<footer class="demo-footer">
<div class="container">
<p>
&copy; 2024 Owen Animation System. All examples are MIT licensed for
educational use.
</p>
</div>
</footer>
<script type="module" src="./js/examples.js"></script>
</body>
</html>

311
demo/index.html Normal file
View File

@ -0,0 +1,311 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Owen Animation System - Interactive Demo</title>
<meta
name="description"
content="Interactive demonstration of the Owen Animation System with multi-scheme naming support"
/>
<!-- Preload critical resources -->
<link rel="preload" href="./styles/main.css" as="style" />
<link rel="preload" href="./js/demo.js" as="script" />
<!-- Styles -->
<link rel="stylesheet" href="./styles/main.css" />
<link rel="stylesheet" href="./styles/demo.css" />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="./assets/favicon.svg" />
</head>
<body>
<!-- Header -->
<header class="demo-header">
<div class="container">
<h1 class="logo">
<span class="logo-text">Owen</span>
<span class="logo-subtitle">Animation System</span>
</h1>
<nav class="demo-nav">
<a href="index.html" class="nav-link active">Demo</a>
<a href="examples.html" class="nav-link">Examples</a>
<a href="comparison.html" class="nav-link">Comparison</a>
<a href="interactive.html" class="nav-link">Interactive</a>
</nav>
</div>
</header>
<!-- Main Content -->
<main class="demo-main">
<div class="container">
<!-- Hero Section -->
<section class="hero-section">
<div class="hero-content">
<h2>Multi-Scheme Animation Naming</h2>
<p>
Experience the power of flexible animation naming with support for
Legacy, Artist, Hierarchical, and Semantic schemes.
</p>
<div class="hero-actions">
<button id="start-demo" class="btn btn-primary">
Start Interactive Demo
</button>
<a href="https://github.com/kjanat/Owen" class="btn btn-secondary"
>View on GitHub</a
>
</div>
</div>
<div class="hero-visual">
<canvas id="demo-canvas" width="600" height="400"></canvas>
<div class="demo-controls">
<div class="control-group">
<label for="naming-scheme">Naming Scheme:</label>
<select id="naming-scheme">
<option value="semantic">Semantic (default)</option>
<option value="hierarchical">Hierarchical</option>
<option value="artist">Artist</option>
<option value="legacy">Legacy</option>
</select>
</div>
<div class="control-group">
<label for="animation-select">Animation:</label>
<select id="animation-select">
<option value="">Select an animation...</option>
</select>
</div>
<div class="control-group">
<button id="play-animation" class="btn btn-small" disabled>
Play
</button>
<button id="pause-animation" class="btn btn-small" disabled>
Pause
</button>
<button id="stop-animation" class="btn btn-small" disabled>
Stop
</button>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section class="features-section">
<h3>Key Features</h3>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">🎯</div>
<h4>Multi-Scheme Support</h4>
<p>
Seamlessly work with Legacy, Artist, Hierarchical, and Semantic
naming schemes in the same project.
</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔄</div>
<h4>Automatic Conversion</h4>
<p>
Convert animation names between schemes automatically with
built-in validation and error handling.
</p>
</div>
<div class="feature-card">
<div class="feature-icon"></div>
<h4>Performance Optimized</h4>
<p>
Efficient caching and lazy loading ensure smooth performance
even with large animation libraries.
</p>
</div>
<div class="feature-card">
<div class="feature-icon">🛠️</div>
<h4>Developer Tools</h4>
<p>
Comprehensive CLI tools, validation scripts, and documentation
generators for streamlined workflows.
</p>
</div>
</div>
</section>
<!-- Live Demo Section -->
<section class="live-demo-section">
<h3>Live Animation Conversion</h3>
<div class="conversion-demo">
<div class="input-section">
<label for="input-animation">Enter Animation Name:</label>
<input
type="text"
id="input-animation"
placeholder="e.g., character_walk_forward"
/>
<label for="input-scheme">Current Scheme:</label>
<select id="input-scheme">
<option value="semantic">Semantic</option>
<option value="hierarchical">Hierarchical</option>
<option value="artist">Artist</option>
<option value="legacy">Legacy</option>
</select>
</div>
<div class="conversion-results">
<h4>Converted Names:</h4>
<div class="scheme-results">
<div class="scheme-result">
<strong>Legacy:</strong> <span id="result-legacy">-</span>
</div>
<div class="scheme-result">
<strong>Artist:</strong> <span id="result-artist">-</span>
</div>
<div class="scheme-result">
<strong>Hierarchical:</strong>
<span id="result-hierarchical">-</span>
</div>
<div class="scheme-result">
<strong>Semantic:</strong> <span id="result-semantic">-</span>
</div>
</div>
</div>
</div>
</section>
<!-- Code Examples Section -->
<section class="code-examples-section">
<h3>Usage Examples</h3>
<div class="code-tabs">
<button class="tab-button active" data-tab="basic">
Basic Usage
</button>
<button class="tab-button" data-tab="conversion">
Name Conversion
</button>
<button class="tab-button" data-tab="validation">Validation</button>
</div>
<div class="tab-content">
<div id="basic-tab" class="tab-pane active">
<pre><code class="javascript">import { OwenAnimationContext } from '@kjanat/owen'
// Initialize with semantic naming scheme
const animationContext = new OwenAnimationContext({
namingScheme: 'semantic',
autoConvert: true
})
// Play an animation
await animationContext.playAnimation('character_walk_forward')
// The system automatically handles scheme conversions
await animationContext.playAnimation('CharacterWalkForward') // Artist scheme</code></pre>
</div>
<div id="conversion-tab" class="tab-pane">
<pre><code class="javascript">import { AnimationNameMapper } from '@kjanat/owen'
const mapper = new AnimationNameMapper()
// Convert between schemes
const semanticName = mapper.convert('CharacterWalkForward', 'semantic')
// Result: 'character_walk_forward'
const hierarchicalName = mapper.convert('walk_forward', 'hierarchical')
// Result: 'character.movement.walk.forward'
// Batch conversion
const animations = ['jump', 'run', 'idle']
const converted = mapper.convertBatch(animations, 'legacy', 'artist')
// Result: ['Jump', 'Run', 'Idle']</code></pre>
</div>
<div id="validation-tab" class="tab-pane">
<pre><code class="javascript">import { AnimationValidator } from '@kjanat/owen'
const validator = new AnimationValidator()
// Validate animation name for specific scheme
const isValid = validator.validate('character_walk_forward', 'semantic')
// Result: true
// Get validation details
const validation = validator.validateDetailed('InvalidName123', 'semantic')
console.log(validation.errors) // Array of validation errors
console.log(validation.suggestions) // Suggested corrections</code></pre>
</div>
</div>
</section>
</div>
</main>
<!-- Footer -->
<footer class="demo-footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h4>Owen Animation System</h4>
<p>
A comprehensive Three.js animation system with multi-scheme naming
support.
</p>
</div>
<div class="footer-section">
<h4>Documentation</h4>
<ul>
<li>
<a href="../docs/ANIMATION_SYSTEM.md">Animation System Guide</a>
</li>
<li><a href="../docs/API_REFERENCE.md">API Reference</a></li>
<li><a href="../docs/MIGRATION_GUIDE.md">Migration Guide</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Examples</h4>
<ul>
<li><a href="../examples/basic/README.md">Basic Usage</a></li>
<li>
<a href="../examples/react/README.md">React Integration</a>
</li>
<li><a href="../examples/vue/README.md">Vue Integration</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Links</h4>
<ul>
<li>
<a href="https://github.com/kjanat/Owen">GitHub Repository</a>
</li>
<li>
<a href="https://gitea.kajkowalski.nl/kjanat/Owen"
>Gitea Repository</a
>
</li>
<li><a href="https://semver.org/">Semantic Versioning</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>
&copy; 2024 Kaj "kjanat" Kowalski. Licensed under AGPL-3.0-only OR
LicenseRef-Commercial.
</p>
</div>
</div>
</footer>
<!-- Scripts -->
<script type="module" src="./js/demo.js"></script>
<script type="module" src="./js/animation-demo.js"></script>
</body>
</html>

306
demo/interactive.html Normal file
View File

@ -0,0 +1,306 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Interactive Playground - Owen Animation System</title>
<link rel="stylesheet" href="./styles/main.css" />
<link rel="stylesheet" href="./styles/interactive.css" />
</head>
<body>
<header class="demo-header">
<div class="container">
<h1 class="logo">
<span class="logo-text">Owen</span>
<span class="logo-subtitle">Interactive Playground</span>
</h1>
<nav class="demo-nav">
<a href="index.html" class="nav-link">Demo</a>
<a href="examples.html" class="nav-link">Examples</a>
<a href="comparison.html" class="nav-link">Comparison</a>
<a href="interactive.html" class="nav-link active">Interactive</a>
</nav>
</div>
</header>
<main class="interactive-main">
<div class="container">
<section class="playground-hero">
<h2>Interactive Animation Playground</h2>
<p>
Experiment with the Owen Animation System in real-time. Try
different schemes, test conversions, and see the results instantly.
</p>
</section>
<div class="playground-layout">
<!-- Sidebar Controls -->
<aside class="playground-sidebar">
<div class="control-section">
<h3>Character Model</h3>
<select id="model-select">
<option value="basic-character">Basic Character</option>
<option value="detailed-character">Detailed Character</option>
<option value="robot-character">Robot Character</option>
</select>
<button id="load-model" class="btn btn-small">Load Model</button>
</div>
<div class="control-section">
<h3>Naming Scheme</h3>
<div class="scheme-selector">
<label class="scheme-option">
<input type="radio" name="scheme" value="legacy" />
<span>Legacy (snake_case)</span>
</label>
<label class="scheme-option">
<input type="radio" name="scheme" value="artist" />
<span>Artist (PascalCase)</span>
</label>
<label class="scheme-option">
<input type="radio" name="scheme" value="hierarchical" />
<span>Hierarchical (dot.notation)</span>
</label>
<label class="scheme-option">
<input type="radio" name="scheme" value="semantic" checked />
<span>Semantic (descriptive_names)</span>
</label>
</div>
</div>
<div class="control-section">
<h3>Available Animations</h3>
<div id="animation-list" class="animation-list">
<!-- Populated by JavaScript -->
</div>
</div>
<div class="control-section">
<h3>Playback Controls</h3>
<div class="playback-controls">
<button id="play-btn" class="btn btn-primary">Play</button>
<button id="pause-btn" class="btn">Pause</button>
<button id="stop-btn" class="btn">Stop</button>
</div>
<div class="playback-options">
<label>
<input type="checkbox" id="loop-animation" checked />
Loop Animation
</label>
<label>
<input
type="range"
id="animation-speed"
min="0.1"
max="3"
step="0.1"
value="1"
/>
Speed: <span id="speed-value">1.0x</span>
</label>
</div>
</div>
<div class="control-section">
<h3>Conversion Tool</h3>
<div class="conversion-tool">
<input
type="text"
id="conversion-input"
placeholder="Enter animation name..."
/>
<select id="source-scheme">
<option value="legacy">From Legacy</option>
<option value="artist">From Artist</option>
<option value="hierarchical">From Hierarchical</option>
<option value="semantic">From Semantic</option>
</select>
<button id="convert-btn" class="btn btn-small">Convert</button>
</div>
<div id="conversion-results" class="conversion-results">
<!-- Results will be displayed here -->
</div>
</div>
</aside>
<!-- Main Viewport -->
<div class="playground-viewport">
<div class="viewport-header">
<h3>Animation Viewport</h3>
<div class="viewport-controls">
<button id="fullscreen-btn" class="btn btn-small">
Fullscreen
</button>
<button id="reset-camera" class="btn btn-small">
Reset Camera
</button>
</div>
</div>
<div class="viewport-container">
<canvas id="animation-canvas"></canvas>
<div class="viewport-overlay">
<div id="loading-indicator" class="loading-indicator">
<div class="spinner"></div>
<p>Loading model...</p>
</div>
<div id="animation-info" class="animation-info">
<div class="info-item">
<span class="label">Current Animation:</span>
<span id="current-animation">None</span>
</div>
<div class="info-item">
<span class="label">Duration:</span>
<span id="animation-duration">0s</span>
</div>
<div class="info-item">
<span class="label">Progress:</span>
<div class="progress-bar">
<div id="progress-fill" class="progress-fill"></div>
</div>
<span id="progress-time">0.0s</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Code Generator -->
<section class="code-generator">
<h3>Generated Code</h3>
<p>
See the actual code that would be used to implement your current
configuration:
</p>
<div class="code-tabs">
<button class="code-tab active" data-tab="javascript">
JavaScript
</button>
<button class="code-tab" data-tab="react">React</button>
<button class="code-tab" data-tab="vue">Vue</button>
</div>
<div class="code-content">
<div id="javascript-code" class="code-panel active">
<pre><code id="js-code-output">// Configure your naming scheme and load animations
// Code will be generated based on your selections above</code></pre>
<button class="copy-code-btn" data-target="js-code-output">
Copy Code
</button>
</div>
<div id="react-code" class="code-panel">
<pre><code id="react-code-output">// React component implementation
// Code will be generated based on your selections above</code></pre>
<button class="copy-code-btn" data-target="react-code-output">
Copy Code
</button>
</div>
<div id="vue-code" class="code-panel">
<pre><code id="vue-code-output">// Vue component implementation
// Code will be generated based on your selections above</code></pre>
<button class="copy-code-btn" data-target="vue-code-output">
Copy Code
</button>
</div>
</div>
</section>
<!-- Performance Monitor -->
<section class="performance-monitor">
<h3>Performance Monitor</h3>
<div class="performance-grid">
<div class="performance-metric">
<h4>Frame Rate</h4>
<div class="metric-value" id="fps-counter">60 FPS</div>
<div class="metric-chart" id="fps-chart"></div>
</div>
<div class="performance-metric">
<h4>Memory Usage</h4>
<div class="metric-value" id="memory-usage">0 MB</div>
<div class="metric-chart" id="memory-chart"></div>
</div>
<div class="performance-metric">
<h4>Animation Cache</h4>
<div class="metric-value" id="cache-stats">0 / 0</div>
<div class="cache-info">
<span>Cached: <span id="cached-count">0</span></span>
<span>Total: <span id="total-count">0</span></span>
</div>
</div>
<div class="performance-metric">
<h4>Conversion Time</h4>
<div class="metric-value" id="conversion-time">0ms</div>
<div class="time-breakdown">
<span>Avg: <span id="avg-time">0ms</span></span>
<span>Max: <span id="max-time">0ms</span></span>
</div>
</div>
</div>
</section>
<!-- Experiments Section -->
<section class="experiments-section">
<h3>Experiments & Tests</h3>
<div class="experiments-grid">
<div class="experiment-card">
<h4>🚀 Stress Test</h4>
<p>Load multiple animations and test performance</p>
<button id="stress-test-btn" class="btn">Run Stress Test</button>
<div id="stress-test-results" class="test-results"></div>
</div>
<div class="experiment-card">
<h4>🔄 Conversion Benchmark</h4>
<p>Benchmark animation name conversion performance</p>
<button id="conversion-benchmark-btn" class="btn">
Run Benchmark
</button>
<div id="conversion-benchmark-results" class="test-results"></div>
</div>
<div class="experiment-card">
<h4>📊 Scheme Analysis</h4>
<p>Analyze and compare naming scheme efficiency</p>
<button id="scheme-analysis-btn" class="btn">
Analyze Schemes
</button>
<div id="scheme-analysis-results" class="test-results"></div>
</div>
<div class="experiment-card">
<h4>💾 Memory Profiling</h4>
<p>Profile memory usage with different configurations</p>
<button id="memory-profile-btn" class="btn">
Profile Memory
</button>
<div id="memory-profile-results" class="test-results"></div>
</div>
</div>
</section>
</div>
</main>
<footer class="demo-footer">
<div class="container">
<p>
&copy; 2024 Owen Animation System. Experiment freely and discover the
possibilities.
</p>
</div>
</footer>
<!-- Scripts -->
<script type="module" src="./js/interactive.js"></script>
<script type="module" src="./js/playground.js"></script>
<script type="module" src="./js/performance-monitor.js"></script>
</body>
</html>

602
demo/js/demo.js Normal file
View File

@ -0,0 +1,602 @@
/**
* Owen Animation System Demo - Main JavaScript
*
* This file provides the interactive functionality for the demo pages.
* It demonstrates the core features of the Owen Animation System.
*/
// Import Owen Animation System (simulated for demo)
// In a real implementation, this would import from the actual package
const OwenDemo = {
// Mock AnimationNameMapper for demo purposes
AnimationNameMapper: class {
constructor () {
this.animations = {
legacy: [
'walk_forward', 'walk_backward', 'run_fast', 'run_slow',
'jump_high', 'jump_low', 'idle_breathing', 'idle_looking',
'attack_sword', 'attack_bow', 'defend_shield', 'defend_dodge',
'death_forward', 'death_backward', 'hurt_light', 'hurt_heavy',
'climb_up', 'climb_down', 'swim_forward', 'swim_idle'
],
artist: [
'WalkForward', 'WalkBackward', 'RunFast', 'RunSlow',
'JumpHigh', 'JumpLow', 'IdleBreathing', 'IdleLooking',
'AttackSword', 'AttackBow', 'DefendShield', 'DefendDodge',
'DeathForward', 'DeathBackward', 'HurtLight', 'HurtHeavy',
'ClimbUp', 'ClimbDown', 'SwimForward', 'SwimIdle'
],
hierarchical: [
'character.movement.walk.forward', 'character.movement.walk.backward',
'character.movement.run.fast', 'character.movement.run.slow',
'character.movement.jump.high', 'character.movement.jump.low',
'character.idle.breathing', 'character.idle.looking',
'character.combat.attack.sword', 'character.combat.attack.bow',
'character.combat.defend.shield', 'character.combat.defend.dodge',
'character.state.death.forward', 'character.state.death.backward',
'character.state.hurt.light', 'character.state.hurt.heavy',
'character.movement.climb.up', 'character.movement.climb.down',
'character.movement.swim.forward', 'character.movement.swim.idle'
],
semantic: [
'character_walk_forward', 'character_walk_backward',
'character_run_fast', 'character_run_slow',
'character_jump_high', 'character_jump_low',
'character_idle_breathing', 'character_idle_looking',
'character_attack_sword', 'character_attack_bow',
'character_defend_shield', 'character_defend_dodge',
'character_death_forward', 'character_death_backward',
'character_hurt_light', 'character_hurt_heavy',
'character_climb_up', 'character_climb_down',
'character_swim_forward', 'character_swim_idle'
]
}
// Create conversion mappings
this.conversionMap = this.createConversionMap()
}
createConversionMap () {
const map = {}
const schemes = Object.keys(this.animations)
schemes.forEach(scheme => {
map[scheme] = {}
schemes.forEach(targetScheme => {
map[scheme][targetScheme] = {}
this.animations[scheme].forEach((anim, index) => {
map[scheme][targetScheme][anim] = this.animations[targetScheme][index]
})
})
})
return map
}
getAllAnimationsByScheme (scheme) {
return this.animations[scheme] || []
}
convert (animationName, targetScheme, sourceScheme = null) {
// If no source scheme provided, try to detect it
if (!sourceScheme) {
sourceScheme = this.detectScheme(animationName)
}
if (!sourceScheme) {
throw new Error(`Unable to detect scheme for animation: ${animationName}`)
}
if (!this.conversionMap[sourceScheme] || !this.conversionMap[sourceScheme][targetScheme]) {
throw new Error(`Conversion from ${sourceScheme} to ${targetScheme} not supported`)
}
const converted = this.conversionMap[sourceScheme][targetScheme][animationName]
if (!converted) {
throw new Error(`Animation "${animationName}" not found in ${sourceScheme} scheme`)
}
return converted
}
detectScheme (animationName) {
for (const [scheme, animations] of Object.entries(this.animations)) {
if (animations.includes(animationName)) {
return scheme
}
}
return null
}
convertBatch (animations, sourceScheme, targetScheme) {
return animations.map(anim => {
try {
return {
original: anim,
converted: this.convert(anim, targetScheme, sourceScheme),
success: true
}
} catch (error) {
return {
original: anim,
error: error.message,
success: false
}
}
})
}
suggestCorrections (animationName, scheme) {
const animations = this.animations[scheme] || []
return animations.filter(anim =>
anim.toLowerCase().includes(animationName.toLowerCase()) ||
animationName.toLowerCase().includes(anim.toLowerCase())
).slice(0, 3)
}
},
// Mock OwenAnimationContext for demo purposes
OwenAnimationContext: class {
constructor (options = {}) {
this.namingScheme = options.namingScheme || 'semantic'
this.autoConvert = options.autoConvert !== false
this.container = options.container
this.currentAnimation = null
this.isPlaying = false
this.mapper = new OwenDemo.AnimationNameMapper()
}
async loadModel (modelPath) {
// Simulate model loading
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('Model loaded:', modelPath)
}
async playAnimation (animationName) {
try {
// Convert animation name if needed
let targetName = animationName
if (this.autoConvert) {
const detectedScheme = this.mapper.detectScheme(animationName)
if (detectedScheme && detectedScheme !== this.namingScheme) {
targetName = this.mapper.convert(animationName, this.namingScheme, detectedScheme)
}
}
this.currentAnimation = targetName
this.isPlaying = true
console.log(`Playing animation: ${targetName} (original: ${animationName})`)
// Simulate animation playback
return new Promise(resolve => {
setTimeout(() => {
console.log(`Animation ${targetName} completed`)
resolve()
}, 2000)
})
} catch (error) {
console.error('Failed to play animation:', error)
throw error
}
}
stopAnimation () {
this.isPlaying = false
this.currentAnimation = null
console.log('Animation stopped')
}
dispose () {
this.stopAnimation()
console.log('Animation context disposed')
}
}
}
// Demo Application State
const DemoState = {
currentScheme: 'semantic',
selectedAnimation: null,
animationContext: null,
mapper: new OwenDemo.AnimationNameMapper(),
init () {
this.setupEventListeners()
this.updateAnimationList()
this.setupConversionTool()
this.setupTabSwitching()
this.initAnimationContext()
},
setupEventListeners () {
// Naming scheme change
const schemeSelect = document.getElementById('naming-scheme')
if (schemeSelect) {
schemeSelect.addEventListener('change', (e) => {
this.currentScheme = e.target.value
this.updateAnimationList()
this.updateCodeExamples()
})
}
// Animation selection
const animationSelect = document.getElementById('animation-select')
if (animationSelect) {
animationSelect.addEventListener('change', (e) => {
this.selectedAnimation = e.target.value
this.updatePlayButtons()
})
}
// Playback controls
const playBtn = document.getElementById('play-animation')
const pauseBtn = document.getElementById('pause-animation')
const stopBtn = document.getElementById('stop-animation')
if (playBtn) {
playBtn.addEventListener('click', () => this.playSelectedAnimation())
}
if (pauseBtn) {
pauseBtn.addEventListener('click', () => this.pauseAnimation())
}
if (stopBtn) {
stopBtn.addEventListener('click', () => this.stopAnimation())
}
// Start demo button
const startBtn = document.getElementById('start-demo')
if (startBtn) {
startBtn.addEventListener('click', () => this.startInteractiveDemo())
}
},
updateAnimationList () {
const select = document.getElementById('animation-select')
if (!select) return
const animations = this.mapper.getAllAnimationsByScheme(this.currentScheme)
select.innerHTML = '<option value="">Select an animation...</option>'
animations.forEach(anim => {
const option = document.createElement('option')
option.value = anim
option.textContent = anim
select.appendChild(option)
})
this.updatePlayButtons()
},
updatePlayButtons () {
const hasSelection = !!this.selectedAnimation
const playBtn = document.getElementById('play-animation')
const pauseBtn = document.getElementById('pause-animation')
const stopBtn = document.getElementById('stop-animation')
if (playBtn) playBtn.disabled = !hasSelection
if (pauseBtn) pauseBtn.disabled = !hasSelection
if (stopBtn) stopBtn.disabled = !hasSelection
},
setupConversionTool () {
const input = document.getElementById('input-animation')
const schemeSelect = document.getElementById('input-scheme')
const convertBtn = document.getElementById('convert-btn')
if (!input || !schemeSelect || !convertBtn) return
const updateConversion = () => {
const animationName = input.value.trim()
const sourceScheme = schemeSelect.value
if (!animationName) {
this.clearConversionResults()
return
}
this.performConversion(animationName, sourceScheme)
}
input.addEventListener('input', updateConversion)
schemeSelect.addEventListener('change', updateConversion)
convertBtn.addEventListener('click', updateConversion)
},
performConversion (animationName, sourceScheme) {
const results = {
legacy: '-',
artist: '-',
hierarchical: '-',
semantic: '-'
}
const schemes = ['legacy', 'artist', 'hierarchical', 'semantic']
schemes.forEach(targetScheme => {
try {
results[targetScheme] = this.mapper.convert(animationName, targetScheme, sourceScheme)
} catch (error) {
results[targetScheme] = `Error: ${error.message}`
}
})
this.displayConversionResults(results)
},
displayConversionResults (results) {
Object.entries(results).forEach(([scheme, result]) => {
const element = document.getElementById(`result-${scheme}`)
if (element) {
element.textContent = result
element.className = result.startsWith('Error:') ? 'error' : 'success'
}
})
},
clearConversionResults () {
const schemes = ['legacy', 'artist', 'hierarchical', 'semantic']
schemes.forEach(scheme => {
const element = document.getElementById(`result-${scheme}`)
if (element) {
element.textContent = '-'
element.className = ''
}
})
},
setupTabSwitching () {
const tabButtons = document.querySelectorAll('.tab-button')
const tabPanes = document.querySelectorAll('.tab-pane')
tabButtons.forEach(button => {
button.addEventListener('click', (e) => {
const targetTab = e.target.dataset.tab
// Update button states
tabButtons.forEach(btn => btn.classList.remove('active'))
e.target.classList.add('active')
// Update pane visibility
tabPanes.forEach(pane => {
pane.classList.remove('active')
if (pane.id === `${targetTab}-tab`) {
pane.classList.add('active')
}
})
this.updateCodeExamples()
})
})
},
updateCodeExamples () {
// Update code examples based on current scheme and selection
const jsOutput = document.getElementById('js-code-output')
const reactOutput = document.getElementById('react-code-output')
const vueOutput = document.getElementById('vue-code-output')
if (jsOutput) {
jsOutput.textContent = this.generateJavaScriptExample()
}
if (reactOutput) {
reactOutput.textContent = this.generateReactExample()
}
if (vueOutput) {
vueOutput.textContent = this.generateVueExample()
}
},
generateJavaScriptExample () {
const animation = this.selectedAnimation || 'character_walk_forward'
return `import { OwenAnimationContext } from '@kjanat/owen'
// Initialize with ${this.currentScheme} naming scheme
const animationContext = new OwenAnimationContext({
namingScheme: '${this.currentScheme}',
autoConvert: true
})
// Load your character model
await animationContext.loadModel('./path/to/character.gltf')
// Play animation using ${this.currentScheme} scheme
await animationContext.playAnimation('${animation}')
// The system automatically handles conversions between schemes
// You can use any naming scheme and it will convert automatically`
},
generateReactExample () {
const animation = this.selectedAnimation || 'character_walk_forward'
return `import React, { useEffect, useRef, useState } from 'react'
import { OwenAnimationContext } from '@kjanat/owen'
export function AnimatedCharacter() {
const containerRef = useRef()
const [animationContext, setAnimationContext] = useState(null)
const [currentAnimation, setCurrentAnimation] = useState('${animation}')
useEffect(() => {
const context = new OwenAnimationContext({
namingScheme: '${this.currentScheme}',
container: containerRef.current
})
context.loadModel('./character.gltf').then(() => {
setAnimationContext(context)
})
return () => context?.dispose()
}, [])
const playAnimation = async (animationName) => {
if (animationContext) {
await animationContext.playAnimation(animationName)
setCurrentAnimation(animationName)
}
}
return (
<div className="animated-character">
<div ref={containerRef} className="viewport" />
<button onClick={() => playAnimation('${animation}')}>
Play Animation
</button>
</div>
)
}`
},
generateVueExample () {
const animation = this.selectedAnimation || 'character_walk_forward'
return `<template>
<div class="animated-character">
<div ref="viewport" class="viewport"></div>
<button @click="playAnimation('${animation}')">
Play Animation
</button>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { OwenAnimationContext } from '@kjanat/owen'
const viewport = ref(null)
const animationContext = ref(null)
const currentAnimation = ref('${animation}')
onMounted(async () => {
const context = new OwenAnimationContext({
namingScheme: '${this.currentScheme}',
container: viewport.value
})
await context.loadModel('./character.gltf')
animationContext.value = context
})
onUnmounted(() => {
animationContext.value?.dispose()
})
const playAnimation = async (animationName) => {
if (animationContext.value) {
await animationContext.value.playAnimation(animationName)
currentAnimation.value = animationName
}
}
</script>`
},
async initAnimationContext () {
const canvas = document.getElementById('demo-canvas')
if (!canvas) return
this.animationContext = new OwenDemo.OwenAnimationContext({
namingScheme: this.currentScheme,
container: canvas
})
// Simulate model loading
try {
await this.animationContext.loadModel('basic-character.gltf')
console.log('Demo character loaded successfully')
} catch (error) {
console.error('Failed to load demo character:', error)
}
},
async playSelectedAnimation () {
if (!this.selectedAnimation || !this.animationContext) return
try {
await this.animationContext.playAnimation(this.selectedAnimation)
} catch (error) {
console.error('Failed to play animation:', error)
window.alert(`Failed to play animation: ${error.message}`)
}
},
pauseAnimation () {
// In a real implementation, this would pause the current animation
console.log('Animation paused')
},
stopAnimation () {
if (this.animationContext) {
this.animationContext.stopAnimation()
}
},
startInteractiveDemo () {
// Navigate to interactive page or start guided tour
if (window.location.pathname.includes('index.html') || window.location.pathname === '/') {
window.location.href = 'interactive.html'
} else {
// Already on a page with interactive features
this.scrollToSection('.live-demo-section')
}
},
scrollToSection (selector) {
const element = document.querySelector(selector)
if (element) {
element.scrollIntoView({ behavior: 'smooth' })
}
}
}
// Copy code functionality
function setupCodeCopying () {
document.querySelectorAll('.copy-code-btn').forEach(button => {
button.addEventListener('click', async (e) => {
const targetId = e.target.dataset.target
const codeElement = document.getElementById(targetId)
if (codeElement) {
try {
await navigator.clipboard.writeText(codeElement.textContent)
// Visual feedback
const originalText = e.target.textContent
e.target.textContent = 'Copied!'
e.target.style.background = 'var(--success-color)'
setTimeout(() => {
e.target.textContent = originalText
e.target.style.background = ''
}, 2000)
} catch (error) {
console.error('Failed to copy code:', error)
window.alert('Failed to copy code to clipboard')
}
}
})
})
}
// Initialize demo when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
DemoState.init()
setupCodeCopying()
// Add some visual feedback for interactions
document.addEventListener('click', (e) => {
if (e.target.classList.contains('btn')) {
e.target.style.transform = 'scale(0.98)'
setTimeout(() => {
e.target.style.transform = ''
}, 150)
}
})
})
// Export for use in other demo files
if (typeof module !== 'undefined' && module.exports) {
module.exports = { OwenDemo, DemoState }
} else {
window.OwenDemo = OwenDemo
window.DemoState = DemoState
}

412
demo/styles/comparison.css Normal file
View File

@ -0,0 +1,412 @@
/* Comparison Page Specific Styles */
.comparison-container {
max-width: 1400px;
margin: 0 auto;
padding: 2rem;
}
.comparison-intro {
text-align: center;
margin-bottom: 3rem;
}
.comparison-intro h1 {
color: var(--text-primary);
margin-bottom: 1rem;
}
.comparison-intro p {
color: var(--text-secondary);
font-size: 1.125rem;
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
}
.scheme-overview {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}
.scheme-card {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
box-shadow: var(--shadow-sm);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.scheme-card::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: var(--scheme-accent, var(--accent-color));
}
.scheme-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.scheme-card.legacy {
--scheme-accent: #8b5cf6;
}
.scheme-card.artist {
--scheme-accent: #06b6d4;
}
.scheme-card.hierarchical {
--scheme-accent: #10b981;
}
.scheme-card.semantic {
--scheme-accent: #f59e0b;
}
.scheme-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.scheme-icon {
width: 40px;
height: 40px;
border-radius: 8px;
background: var(--scheme-accent);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 1.25rem;
}
.scheme-name {
font-size: 1.25rem;
font-weight: 600;
color: var(--text-primary);
margin: 0;
}
.scheme-description {
color: var(--text-secondary);
margin-bottom: 1rem;
line-height: 1.6;
}
.scheme-example {
background: var(--bg-code);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 0.75rem;
font-family: "Fira Code", monospace;
font-size: 0.875rem;
color: var(--text-primary);
margin-bottom: 1rem;
}
.scheme-pros {
list-style: none;
padding: 0;
margin: 0;
}
.scheme-pros li {
padding: 0.25rem 0;
padding-left: 1.5rem;
position: relative;
color: var(--text-secondary);
font-size: 0.875rem;
}
.scheme-pros li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--success-text);
font-weight: bold;
}
.comparison-table-section {
margin: 3rem 0;
}
.table-controls {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
align-items: center;
}
.table-filter {
padding: 0.5rem 1rem;
border: 1px solid var(--border-color);
background: var(--bg-secondary);
color: var(--text-secondary);
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
font-size: 0.875rem;
}
.table-filter:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.table-filter.active {
background: var(--accent-color);
color: white;
border-color: var(--accent-color);
}
.search-input {
padding: 0.5rem 1rem;
border: 1px solid var(--border-color);
background: var(--bg-secondary);
color: var(--text-primary);
border-radius: 6px;
font-size: 0.875rem;
min-width: 200px;
}
.search-input:focus {
outline: none;
border-color: var(--accent-color);
box-shadow: 0 0 0 3px var(--accent-color-alpha);
}
.comparison-table {
width: 100%;
border-collapse: collapse;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
overflow: hidden;
box-shadow: var(--shadow-sm);
}
.comparison-table th,
.comparison-table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
.comparison-table th {
background: var(--bg-tertiary);
font-weight: 600;
color: var(--text-primary);
position: sticky;
top: 0;
z-index: 10;
}
.comparison-table td {
color: var(--text-secondary);
font-family: "Fira Code", monospace;
font-size: 0.875rem;
}
.comparison-table tbody tr:hover {
background: var(--bg-hover);
}
.comparison-table tbody tr:last-child td {
border-bottom: none;
}
.scheme-label {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
color: white;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.scheme-label.legacy {
background: #8b5cf6;
}
.scheme-label.artist {
background: #06b6d4;
}
.scheme-label.hierarchical {
background: #10b981;
}
.scheme-label.semantic {
background: #f59e0b;
}
.conversion-demo {
margin: 3rem 0;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 2rem;
}
.conversion-controls {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 1rem;
align-items: center;
margin-bottom: 2rem;
}
.scheme-selector {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.scheme-selector label {
font-weight: 500;
color: var(--text-primary);
font-size: 0.875rem;
}
.scheme-select {
padding: 0.75rem;
border: 1px solid var(--border-color);
background: var(--bg-tertiary);
color: var(--text-primary);
border-radius: 6px;
font-size: 0.875rem;
}
.conversion-arrow {
display: flex;
align-items: center;
justify-content: center;
color: var(--accent-color);
font-size: 1.5rem;
font-weight: bold;
}
.animation-input {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border-color);
background: var(--bg-tertiary);
color: var(--text-primary);
border-radius: 6px;
font-family: "Fira Code", monospace;
font-size: 0.875rem;
}
.conversion-result {
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 1rem;
margin-top: 1rem;
}
.result-label {
font-size: 0.875rem;
color: var(--text-secondary);
margin-bottom: 0.5rem;
}
.result-value {
font-family: "Fira Code", monospace;
font-size: 1rem;
color: var(--text-primary);
background: var(--bg-code);
padding: 0.75rem;
border-radius: 4px;
word-break: break-all;
}
.performance-comparison {
margin: 3rem 0;
}
.performance-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.performance-card {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
text-align: center;
}
.performance-metric {
font-size: 2rem;
font-weight: bold;
color: var(--accent-color);
margin-bottom: 0.5rem;
}
.performance-label {
color: var(--text-secondary);
font-size: 0.875rem;
}
.no-results {
text-align: center;
padding: 2rem;
color: var(--text-secondary);
font-style: italic;
}
@media (max-width: 768px) {
.comparison-container {
padding: 1rem;
}
.scheme-overview {
grid-template-columns: 1fr;
gap: 1rem;
}
.table-controls {
flex-direction: column;
align-items: stretch;
}
.search-input {
min-width: auto;
}
.comparison-table {
font-size: 0.75rem;
}
.comparison-table th,
.comparison-table td {
padding: 0.75rem 0.5rem;
}
.conversion-controls {
grid-template-columns: 1fr;
text-align: center;
}
.conversion-arrow {
transform: rotate(90deg);
}
.performance-grid {
grid-template-columns: repeat(2, 1fr);
}
}

302
demo/styles/demo.css Normal file
View File

@ -0,0 +1,302 @@
/* Demo-specific styles */
/* Hero Section */
.hero-section {
padding: 4rem 0;
background: linear-gradient(
135deg,
var(--bg-secondary) 0%,
var(--bg-tertiary) 100%
);
margin-bottom: 3rem;
}
.hero-content {
text-align: center;
margin-bottom: 3rem;
}
.hero-content h2 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--text-primary);
}
.hero-content p {
font-size: 1.125rem;
color: var(--text-secondary);
margin-bottom: 2rem;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.hero-actions {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.hero-visual {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 2rem;
align-items: start;
}
#demo-canvas {
width: 100%;
height: 400px;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background: var(--bg-primary);
}
.demo-controls {
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1.5rem;
box-shadow: var(--shadow-sm);
}
.control-group {
margin-bottom: 1.5rem;
}
.control-group:last-child {
margin-bottom: 0;
}
.control-group label {
margin-bottom: 0.5rem;
font-weight: 600;
font-size: 0.875rem;
}
.control-group .btn {
margin-right: 0.5rem;
margin-bottom: 0.5rem;
}
/* Features Section */
.features-section {
padding: 3rem 0;
margin-bottom: 3rem;
}
.features-section h3 {
text-align: center;
font-size: 2rem;
margin-bottom: 2rem;
color: var(--text-primary);
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
}
.feature-card {
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 2rem;
text-align: center;
box-shadow: var(--shadow-sm);
transition:
transform 0.2s ease,
box-shadow 0.2s ease;
}
.feature-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.feature-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.feature-card h4 {
font-size: 1.25rem;
margin-bottom: 1rem;
color: var(--text-primary);
}
.feature-card p {
color: var(--text-secondary);
line-height: 1.6;
}
/* Live Demo Section */
.live-demo-section {
padding: 3rem 0;
background: var(--bg-secondary);
border-radius: var(--border-radius);
margin-bottom: 3rem;
}
.live-demo-section h3 {
text-align: center;
font-size: 1.75rem;
margin-bottom: 2rem;
color: var(--text-primary);
}
.conversion-demo {
max-width: 800px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
align-items: start;
}
.input-section {
background: var(--bg-primary);
padding: 1.5rem;
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
}
.input-section label {
margin-bottom: 0.5rem;
margin-top: 1rem;
}
.input-section label:first-child {
margin-top: 0;
}
.conversion-results {
background: var(--bg-primary);
padding: 1.5rem;
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
}
.conversion-results h4 {
margin-bottom: 1rem;
color: var(--text-primary);
}
.scheme-results {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.scheme-result {
padding: 0.75rem;
background: var(--bg-tertiary);
border-radius: var(--border-radius);
font-family: var(--font-mono);
font-size: 0.875rem;
}
.scheme-result strong {
color: var(--primary-color);
margin-right: 0.5rem;
}
/* Code Examples Section */
.code-examples-section {
padding: 3rem 0;
margin-bottom: 3rem;
}
.code-examples-section h3 {
text-align: center;
font-size: 1.75rem;
margin-bottom: 2rem;
color: var(--text-primary);
}
.code-tabs {
display: flex;
justify-content: center;
margin-bottom: 2rem;
border-bottom: 1px solid var(--border-color);
}
.tab-button {
background: none;
border: none;
padding: 1rem 2rem;
cursor: pointer;
font-weight: 500;
color: var(--text-secondary);
border-bottom: 2px solid transparent;
transition: all 0.2s ease;
}
.tab-button:hover {
color: var(--text-primary);
}
.tab-button.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
.tab-content {
max-width: 900px;
margin: 0 auto;
}
.tab-pane {
display: none;
}
.tab-pane.active {
display: block;
}
.tab-pane pre {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1.5rem;
overflow-x: auto;
font-size: 0.875rem;
line-height: 1.6;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.hero-visual {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.conversion-demo {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.hero-content h2 {
font-size: 2rem;
}
.hero-content p {
font-size: 1rem;
}
.features-grid {
grid-template-columns: 1fr;
}
.code-tabs {
flex-wrap: wrap;
gap: 0.5rem;
}
.tab-button {
padding: 0.75rem 1.5rem;
}
}

306
demo/styles/examples.css Normal file
View File

@ -0,0 +1,306 @@
/* Examples Page Specific Styles */
.examples-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.examples-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.example-card {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
box-shadow: var(--shadow-sm);
transition: all 0.3s ease;
}
.example-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.example-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-color);
}
.framework-icon {
width: 32px;
height: 32px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 0.875rem;
}
.react-icon {
background: #61dafb;
color: #000;
}
.vue-icon {
background: #4fc08d;
}
.node-icon {
background: #339933;
}
.vanilla-icon {
background: #f7df1e;
color: #000;
}
.blender-icon {
background: #e87d0d;
}
.example-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--text-primary);
margin: 0;
}
.example-description {
color: var(--text-secondary);
margin-bottom: 1.5rem;
line-height: 1.6;
}
.code-example {
background: var(--bg-code);
border: 1px solid var(--border-color);
border-radius: 8px;
overflow: hidden;
margin-bottom: 1rem;
}
.code-header {
background: var(--bg-tertiary);
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.code-language {
font-size: 0.875rem;
font-weight: 500;
color: var(--text-secondary);
}
.copy-button {
background: none;
border: 1px solid var(--border-color);
color: var(--text-secondary);
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.75rem;
cursor: pointer;
transition: all 0.2s ease;
}
.copy-button:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.copy-button.copied {
background: var(--success-bg);
color: var(--success-text);
border-color: var(--success-border);
}
.code-content {
padding: 1rem;
font-family: "Fira Code", "Monaco", "Consolas", monospace;
font-size: 0.875rem;
line-height: 1.5;
color: var(--text-primary);
overflow-x: auto;
}
.integration-steps {
margin-top: 1.5rem;
}
.step {
display: flex;
align-items: flex-start;
gap: 1rem;
margin-bottom: 1rem;
padding: 1rem;
background: var(--bg-tertiary);
border-radius: 8px;
border-left: 4px solid var(--accent-color);
}
.step-number {
background: var(--accent-color);
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: bold;
flex-shrink: 0;
margin-top: 0.125rem;
}
.step-content h4 {
margin: 0 0 0.5rem 0;
color: var(--text-primary);
font-size: 1rem;
}
.step-content p {
margin: 0;
color: var(--text-secondary);
line-height: 1.5;
}
.example-navigation {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
}
.nav-filter {
padding: 0.5rem 1rem;
border: 1px solid var(--border-color);
background: var(--bg-secondary);
color: var(--text-secondary);
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
font-size: 0.875rem;
}
.nav-filter:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.nav-filter.active {
background: var(--accent-color);
color: white;
border-color: var(--accent-color);
}
.features-list {
list-style: none;
padding: 0;
margin: 1rem 0;
}
.features-list li {
padding: 0.5rem 0;
padding-left: 1.5rem;
position: relative;
color: var(--text-secondary);
}
.features-list li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--success-text);
font-weight: bold;
}
.download-section {
margin-top: 2rem;
padding: 1.5rem;
background: var(--bg-tertiary);
border-radius: 8px;
border: 1px solid var(--border-color);
}
.download-links {
display: flex;
gap: 1rem;
margin-top: 1rem;
flex-wrap: wrap;
}
.download-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
background: var(--accent-color);
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: 500;
transition: all 0.2s ease;
}
.download-link:hover {
background: var(--accent-hover);
transform: translateY(-1px);
}
.error-boundary {
border: 2px dashed var(--error-border);
background: var(--error-bg);
color: var(--error-text);
padding: 1rem;
border-radius: 8px;
margin: 1rem 0;
text-align: center;
}
@media (max-width: 768px) {
.examples-container {
padding: 1rem;
}
.examples-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.example-card {
padding: 1rem;
}
.example-navigation {
gap: 0.5rem;
}
.nav-filter {
padding: 0.375rem 0.75rem;
font-size: 0.8rem;
}
.download-links {
flex-direction: column;
}
.step {
flex-direction: column;
gap: 0.5rem;
}
.step-number {
align-self: flex-start;
}
}

444
demo/styles/interactive.css Normal file
View File

@ -0,0 +1,444 @@
/* Interactive Playground Specific Styles */
.interactive-container {
max-width: 1600px;
margin: 0 auto;
padding: 2rem;
display: grid;
grid-template-areas:
"header header"
"controls playground"
"results results";
grid-template-columns: 300px 1fr;
grid-template-rows: auto 1fr auto;
gap: 2rem;
min-height: calc(100vh - 4rem);
}
.playground-header {
grid-area: header;
text-align: center;
margin-bottom: 1rem;
}
.playground-header h1 {
color: var(--text-primary);
margin-bottom: 1rem;
}
.playground-header p {
color: var(--text-secondary);
font-size: 1.125rem;
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
}
.playground-controls {
grid-area: controls;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
height: fit-content;
box-shadow: var(--shadow-sm);
}
.controls-section {
margin-bottom: 2rem;
}
.controls-section:last-child {
margin-bottom: 0;
}
.section-title {
font-size: 1rem;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border-color);
}
.control-group {
margin-bottom: 1rem;
}
.control-label {
display: block;
font-size: 0.875rem;
color: var(--text-secondary);
margin-bottom: 0.5rem;
font-weight: 500;
}
.control-input {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border-color);
background: var(--bg-tertiary);
color: var(--text-primary);
border-radius: 6px;
font-size: 0.875rem;
transition: all 0.2s ease;
}
.control-input:focus {
outline: none;
border-color: var(--accent-color);
box-shadow: 0 0 0 3px var(--accent-color-alpha);
}
.control-textarea {
min-height: 100px;
resize: vertical;
font-family: "Fira Code", monospace;
}
.scheme-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
margin-bottom: 1rem;
}
.scheme-button {
padding: 0.75rem;
border: 1px solid var(--border-color);
background: var(--bg-tertiary);
color: var(--text-secondary);
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
font-size: 0.75rem;
text-align: center;
font-weight: 500;
}
.scheme-button:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.scheme-button.active {
background: var(--accent-color);
color: white;
border-color: var(--accent-color);
}
.action-buttons {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.action-button {
padding: 0.75rem 1rem;
border: 1px solid var(--accent-color);
background: var(--accent-color);
color: white;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
font-size: 0.875rem;
}
.action-button:hover {
background: var(--accent-hover);
border-color: var(--accent-hover);
}
.action-button.secondary {
background: transparent;
color: var(--accent-color);
}
.action-button.secondary:hover {
background: var(--accent-color-alpha);
}
.action-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.playground-main {
grid-area: playground;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 12px;
overflow: hidden;
box-shadow: var(--shadow-sm);
display: flex;
flex-direction: column;
}
.playground-tabs {
display: flex;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
}
.playground-tab {
padding: 1rem 1.5rem;
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
border-bottom: 2px solid transparent;
}
.playground-tab:hover {
color: var(--text-primary);
background: var(--bg-hover);
}
.playground-tab.active {
color: var(--accent-color);
border-bottom-color: var(--accent-color);
background: var(--bg-secondary);
}
.playground-content {
flex: 1;
padding: 1.5rem;
overflow: auto;
}
.code-editor {
width: 100%;
height: 400px;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--bg-code);
color: var(--text-primary);
font-family: "Fira Code", monospace;
font-size: 0.875rem;
padding: 1rem;
resize: vertical;
line-height: 1.5;
}
.output-panel {
background: var(--bg-code);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 1rem;
font-family: "Fira Code", monospace;
font-size: 0.875rem;
line-height: 1.5;
color: var(--text-primary);
white-space: pre-wrap;
overflow: auto;
max-height: 400px;
}
.conversion-preview {
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 1rem;
margin-top: 1rem;
}
.preview-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0;
border-bottom: 1px solid var(--border-color);
}
.preview-item:last-child {
border-bottom: none;
}
.preview-input {
font-family: "Fira Code", monospace;
color: var(--text-secondary);
font-size: 0.875rem;
}
.preview-output {
font-family: "Fira Code", monospace;
color: var(--text-primary);
font-size: 0.875rem;
font-weight: 500;
}
.performance-monitor {
grid-area: results;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
box-shadow: var(--shadow-sm);
}
.monitor-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.monitor-card {
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
text-align: center;
}
.monitor-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--accent-color);
margin-bottom: 0.5rem;
}
.monitor-label {
color: var(--text-secondary);
font-size: 0.875rem;
}
.status-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 0.5rem;
}
.status-indicator.success {
background: var(--success-color);
}
.status-indicator.warning {
background: var(--warning-color);
}
.status-indicator.error {
background: var(--error-color);
}
.error-message {
background: var(--error-bg);
color: var(--error-text);
border: 1px solid var(--error-border);
border-radius: 6px;
padding: 1rem;
margin: 1rem 0;
}
.success-message {
background: var(--success-bg);
color: var(--success-text);
border: 1px solid var(--success-border);
border-radius: 6px;
padding: 1rem;
margin: 1rem 0;
}
.loading-spinner {
display: inline-block;
width: 16px;
height: 16px;
border: 2px solid var(--border-color);
border-top: 2px solid var(--accent-color);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.history-panel {
max-height: 300px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--bg-tertiary);
}
.history-item {
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border-color);
cursor: pointer;
transition: background 0.2s ease;
}
.history-item:hover {
background: var(--bg-hover);
}
.history-item:last-child {
border-bottom: none;
}
.history-input {
font-family: "Fira Code", monospace;
font-size: 0.875rem;
color: var(--text-primary);
margin-bottom: 0.25rem;
}
.history-meta {
font-size: 0.75rem;
color: var(--text-secondary);
}
@media (max-width: 1024px) {
.interactive-container {
grid-template-areas:
"header"
"controls"
"playground"
"results";
grid-template-columns: 1fr;
padding: 1rem;
}
.playground-controls {
height: auto;
}
.scheme-buttons {
grid-template-columns: 1fr;
}
.monitor-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 640px) {
.playground-tabs {
overflow-x: auto;
}
.playground-tab {
padding: 0.75rem 1rem;
white-space: nowrap;
}
.code-editor {
height: 300px;
}
.monitor-grid {
grid-template-columns: 1fr;
}
.action-buttons {
gap: 0.5rem;
}
}

472
demo/styles/main.css Normal file
View File

@ -0,0 +1,472 @@
/* Owen Animation System Demo - Main Styles */
/* CSS Variables for consistent theming */
:root {
--primary-color: #2563eb;
--primary-hover: #1d4ed8;
--secondary-color: #64748b;
--accent-color: #0ea5e9;
--success-color: #10b981;
--warning-color: #f59e0b;
--error-color: #ef4444;
--bg-primary: #ffffff;
--bg-secondary: #f8fafc;
--bg-tertiary: #f1f5f9;
--text-primary: #1e293b;
--text-secondary: #475569;
--text-muted: #94a3b8;
--border-color: #e2e8f0;
--border-radius: 8px;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
--font-family:
"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono:
"JetBrains Mono", "Fira Code", Consolas, "Courier New", monospace;
}
/* Dark mode variables */
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--bg-tertiary: #334155;
--text-primary: #f1f5f9;
--text-secondary: #cbd5e1;
--text-muted: #64748b;
--border-color: #334155;
}
}
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-family);
line-height: 1.6;
color: var(--text-primary);
background-color: var(--bg-primary);
transition: background-color 0.3s ease;
}
/* Container */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
/* Header */
.demo-header {
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
padding: 1rem 0;
position: sticky;
top: 0;
z-index: 100;
}
.demo-header .container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
display: flex;
flex-direction: column;
line-height: 1.2;
}
.logo-text {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary-color);
}
.logo-subtitle {
font-size: 0.875rem;
color: var(--text-secondary);
font-weight: 500;
}
.demo-nav {
display: flex;
gap: 2rem;
}
.nav-link {
text-decoration: none;
color: var(--text-secondary);
font-weight: 500;
padding: 0.5rem 1rem;
border-radius: var(--border-radius);
transition: all 0.2s ease;
}
.nav-link:hover {
color: var(--primary-color);
background-color: var(--bg-tertiary);
}
.nav-link.active {
color: var(--primary-color);
background-color: var(--primary-color);
color: white;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.5rem;
font-size: 0.875rem;
font-weight: 500;
border: none;
border-radius: var(--border-radius);
cursor: pointer;
text-decoration: none;
transition: all 0.2s ease;
gap: 0.5rem;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
background-color: var(--primary-hover);
}
.btn-secondary {
background-color: var(--bg-tertiary);
color: var(--text-primary);
border: 1px solid var(--border-color);
}
.btn-secondary:hover {
background-color: var(--bg-secondary);
}
.btn-small {
padding: 0.5rem 1rem;
font-size: 0.8rem;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Form elements */
input,
select,
textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background-color: var(--bg-primary);
color: var(--text-primary);
font-size: 0.875rem;
transition: border-color 0.2s ease;
}
input:focus,
select:focus,
textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgb(37 99 235 / 0.1);
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: var(--text-secondary);
}
/* Cards */
.card {
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1.5rem;
box-shadow: var(--shadow-sm);
}
/* Grid layouts */
.grid {
display: grid;
gap: 1.5rem;
}
.grid-2 {
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.grid-3 {
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.grid-4 {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
/* Code blocks */
pre {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1rem;
overflow-x: auto;
font-family: var(--font-mono);
font-size: 0.875rem;
line-height: 1.5;
}
code {
font-family: var(--font-mono);
font-size: 0.875rem;
background: var(--bg-tertiary);
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
}
pre code {
background: none;
padding: 0;
}
/* Utility classes */
.text-center {
text-align: center;
}
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
.mb-1 {
margin-bottom: 0.25rem;
}
.mb-2 {
margin-bottom: 0.5rem;
}
.mb-3 {
margin-bottom: 1rem;
}
.mb-4 {
margin-bottom: 1.5rem;
}
.mb-5 {
margin-bottom: 2rem;
}
.mt-1 {
margin-top: 0.25rem;
}
.mt-2 {
margin-top: 0.5rem;
}
.mt-3 {
margin-top: 1rem;
}
.mt-4 {
margin-top: 1.5rem;
}
.mt-5 {
margin-top: 2rem;
}
.p-1 {
padding: 0.25rem;
}
.p-2 {
padding: 0.5rem;
}
.p-3 {
padding: 1rem;
}
.p-4 {
padding: 1.5rem;
}
.p-5 {
padding: 2rem;
}
.hidden {
display: none;
}
.visible {
display: block;
}
/* Animation classes */
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
.slide-up {
animation: slideUp 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Loading spinner */
.spinner {
width: 24px;
height: 24px;
border: 2px solid var(--border-color);
border-top: 2px solid var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Progress bar */
.progress-bar {
width: 100%;
height: 8px;
background-color: var(--bg-tertiary);
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: var(--primary-color);
transition: width 0.3s ease;
width: 0%;
}
/* Footer */
.demo-footer {
background: var(--bg-secondary);
border-top: 1px solid var(--border-color);
padding: 2rem 0;
margin-top: 4rem;
}
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.footer-section h4 {
color: var(--text-primary);
margin-bottom: 1rem;
font-size: 1rem;
}
.footer-section ul {
list-style: none;
padding: 0;
}
.footer-section li {
margin-bottom: 0.5rem;
}
.footer-section a {
color: var(--text-secondary);
text-decoration: none;
transition: color 0.2s ease;
}
.footer-section a:hover {
color: var(--primary-color);
}
.footer-bottom {
text-align: center;
padding-top: 2rem;
border-top: 1px solid var(--border-color);
color: var(--text-muted);
font-size: 0.875rem;
}
/* Responsive design */
@media (max-width: 768px) {
.container {
padding: 0 0.75rem;
}
.demo-header .container {
flex-direction: column;
gap: 1rem;
}
.demo-nav {
gap: 1rem;
}
.grid-2,
.grid-3,
.grid-4 {
grid-template-columns: 1fr;
}
.btn {
width: 100%;
justify-content: center;
}
}
@media (max-width: 480px) {
.demo-nav {
flex-wrap: wrap;
gap: 0.5rem;
}
.nav-link {
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
}
.footer-content {
grid-template-columns: 1fr;
gap: 1.5rem;
}
}