Skip to content

Commit 2edb9ef

Browse files
author
Michael Gosha
committed
Add theme system with presets and light mode improvements
- Add configurable color themes via YAML (preset or custom colors) - Theme presets: purple (default), blue, green, teal, orange, rose - Improve light mode with subtle gradient background - Add CSS custom properties for dynamic color injection - Create build.sh script for proper UI/CLI build and sync - Update components to use theme variables - Document theme system in DESIGN.md and CLAUDE.md
1 parent e8effa7 commit 2edb9ef

File tree

18 files changed

+995
-35
lines changed

18 files changed

+995
-35
lines changed

CLAUDE.md

Lines changed: 408 additions & 0 deletions
Large diffs are not rendered by default.

DESIGN.md

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# DemoScript Design Document
22

3-
> **Note:** This document describes the full DemoScript architecture. The open source version includes the `serve` command for live presentations. Additional features like `record`, `build`, `deploy`, and video export are available in [DemoScript Studio](https://demoscript.app).
4-
53
## Overview
64

75
DemoScript is a framework for creating scripted, shareable product demonstrations. Users write simple YAML files describing demo steps, and the framework provides:
@@ -145,8 +143,11 @@ tags: ["api", "tutorial"]
145143
settings:
146144
base_url: "http://localhost:8000" # Default base URL for REST calls
147145
theme:
148-
logo: "./assets/logo.png"
149-
primary_color: "#3B82F6"
146+
logo: "./assets/logo.png" # Optional logo image
147+
preset: "purple" # Color preset: purple|blue|green|teal|orange|rose
148+
primary: "#8b5cf6" # Custom primary color (overrides preset)
149+
accent: "#06b6d4" # Custom accent color (overrides preset)
150+
mode: "auto" # Force light/dark mode: auto|light|dark
150151
polling:
151152
interval: 2000 # Default polling interval (ms)
152153
max_attempts: 30 # Default max polling attempts
@@ -535,6 +536,69 @@ Key files:
535536
- `packages/ui/src/components/effects/` - Effect components (Confetti, SoundEffects, etc.)
536537
- `packages/ui/src/types/schema.ts` - EffectsSettings interface
537538

539+
### Theme System
540+
541+
DemoScript supports customizable color themes via YAML configuration.
542+
543+
#### Theme Configuration
544+
545+
```yaml
546+
settings:
547+
theme:
548+
# Option 1: Use a preset theme
549+
preset: "purple" # purple (default) | blue | green | teal | orange | rose
550+
551+
# Option 2: Custom colors (overrides preset)
552+
primary: "#8b5cf6" # Main brand color (buttons, gradients, accents)
553+
accent: "#06b6d4" # Secondary color (gradient endpoints, highlights)
554+
555+
# Option 3: Force light/dark mode
556+
mode: "auto" # auto | light | dark
557+
```
558+
559+
#### Color Presets
560+
561+
| Preset | Primary | Accent | Use Case |
562+
|--------|---------|--------|----------|
563+
| `purple` | #8b5cf6 | #06b6d4 | Default DemoScript look |
564+
| `blue` | #3b82f6 | #8b5cf6 | Corporate, professional |
565+
| `green` | #10b981 | #3b82f6 | Fintech, nature |
566+
| `teal` | #14b8a6 | #f59e0b | Modern, fresh |
567+
| `orange` | #f97316 | #8b5cf6 | Energetic, warm |
568+
| `rose` | #f43f5e | #8b5cf6 | Bold, vibrant |
569+
570+
#### Architecture
571+
572+
The theme system uses CSS custom properties for dynamic color injection:
573+
574+
```
575+
┌──────────────────────────────────────────────────────────────┐
576+
│ Theme System Flow │
577+
├──────────────────────────────────────────────────────────────┤
578+
│ │
579+
│ 1. YAML config loads ──► theme settings extracted │
580+
│ │
581+
│ 2. DemoRunner ──► getThemeColors() ──► resolves colors │
582+
│ (preset lookup or custom colors) │
583+
│ │
584+
│ 3. applyThemeColors() ──► injects CSS variables on <html> │
585+
│ --color-primary: #8b5cf6 │
586+
│ --color-accent: #06b6d4 │
587+
│ --color-primary-rgb: 139, 92, 246 │
588+
│ --color-accent-rgb: 6, 182, 212 │
589+
│ │
590+
│ 4. Components use CSS variables via Tailwind classes │
591+
│ from-theme-primary, text-theme-accent, etc. │
592+
│ │
593+
└──────────────────────────────────────────────────────────────┘
594+
```
595+
596+
Key files:
597+
- `packages/ui/src/lib/theme-colors.ts` - Presets and color utilities
598+
- `packages/ui/src/context/ThemeContext.tsx` - Theme state and mode management
599+
- `packages/ui/src/index.css` - CSS variables and utility classes
600+
- `packages/ui/tailwind.config.js` - Tailwind color configuration
601+
538602
### Branching and Navigation
539603

540604
Steps can define unique identifiers and navigation targets for non-linear demo flows.

build.sh

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
#!/bin/bash
2+
# DemoScript Build Script
3+
#
4+
# Builds all project components and optionally creates distribution packages.
5+
#
6+
# Usage:
7+
# ./build.sh # Build UI and CLI
8+
# ./build.sh --serve [serve-args] # Build and start dev server
9+
# ./build.sh --rpm # Build everything + RPM package
10+
# ./build.sh --deb # Build everything + DEB package
11+
# ./build.sh --packages # Build everything + all packages
12+
# ./build.sh --clean # Clean build artifacts first
13+
#
14+
# Options:
15+
# --clean Clean build artifacts before building
16+
# --serve Start dev server after building (args after this are passed to serve)
17+
# --rpm Build RPM package after building
18+
# --deb Build DEB package after building
19+
# --packages Build all packages (RPM + DEB) after building
20+
# --skip-ui Skip UI build (use existing)
21+
# --skip-cli Skip CLI build (use existing)
22+
# -h, --help Show this help message
23+
#
24+
# Serve examples:
25+
# ./build.sh --serve # Default demo, port 3001, all interfaces
26+
# ./build.sh --serve ~/my-demo # Custom demo path
27+
# ./build.sh --serve ~/my-demo --port 8080 # Custom port
28+
# ./build.sh --serve --tunnel # With ngrok tunnel
29+
30+
set -e
31+
32+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
33+
cd "$SCRIPT_DIR"
34+
35+
# Colors for output
36+
RED='\033[0;31m'
37+
GREEN='\033[0;32m'
38+
YELLOW='\033[1;33m'
39+
CYAN='\033[0;36m'
40+
NC='\033[0m' # No Color
41+
42+
# Parse arguments
43+
CLEAN=false
44+
SERVE=false
45+
BUILD_RPM=false
46+
BUILD_DEB=false
47+
SKIP_UI=false
48+
SKIP_CLI=false
49+
SERVE_ARGS=()
50+
51+
while [[ $# -gt 0 ]]; do
52+
case $1 in
53+
--clean)
54+
CLEAN=true
55+
shift
56+
;;
57+
--serve)
58+
SERVE=true
59+
shift
60+
# Collect remaining args for serve command
61+
SERVE_ARGS=("$@")
62+
break
63+
;;
64+
--rpm)
65+
BUILD_RPM=true
66+
shift
67+
;;
68+
--deb)
69+
BUILD_DEB=true
70+
shift
71+
;;
72+
--packages)
73+
BUILD_RPM=true
74+
BUILD_DEB=true
75+
shift
76+
;;
77+
--skip-ui)
78+
SKIP_UI=true
79+
shift
80+
;;
81+
--skip-cli)
82+
SKIP_CLI=true
83+
shift
84+
;;
85+
-h|--help)
86+
head -28 "$0" | tail -27
87+
exit 0
88+
;;
89+
*)
90+
echo -e "${RED}Unknown option: $1${NC}"
91+
echo "Use --help for usage information"
92+
exit 1
93+
;;
94+
esac
95+
done
96+
97+
echo -e "${CYAN}╔══════════════════════════════════════╗${NC}"
98+
echo -e "${CYAN}║ DemoScript Build Script ║${NC}"
99+
echo -e "${CYAN}╚══════════════════════════════════════╝${NC}"
100+
echo
101+
102+
# Step 1: Clean if requested
103+
if [ "$CLEAN" = true ]; then
104+
echo -e "${YELLOW}[1/4] Cleaning build artifacts...${NC}"
105+
rm -rf packages/ui/dist
106+
rm -rf packages/cli/dist
107+
rm -rf dist
108+
echo -e "${GREEN} Cleaned!${NC}"
109+
echo
110+
fi
111+
112+
# Step 2: Build UI
113+
if [ "$SKIP_UI" = false ]; then
114+
echo -e "${YELLOW}[2/4] Building UI package...${NC}"
115+
cd packages/ui
116+
npm run build
117+
cd "$SCRIPT_DIR"
118+
echo -e "${GREEN} UI build complete!${NC}"
119+
echo
120+
else
121+
echo -e "${YELLOW}[2/4] Skipping UI build${NC}"
122+
echo
123+
fi
124+
125+
# Step 3: Build CLI
126+
if [ "$SKIP_CLI" = false ]; then
127+
echo -e "${YELLOW}[3/4] Building CLI package...${NC}"
128+
cd packages/cli
129+
npm run build
130+
cd "$SCRIPT_DIR"
131+
echo -e "${GREEN} CLI build complete!${NC}"
132+
echo
133+
else
134+
echo -e "${YELLOW}[3/4] Skipping CLI build${NC}"
135+
echo
136+
fi
137+
138+
# Step 4: Copy UI dist to CLI's ui-dist folder
139+
# This is CRITICAL - the CLI serve command looks for ui-dist in its dist folder
140+
echo -e "${YELLOW}[4/4] Syncing UI assets to CLI...${NC}"
141+
if [ -d "packages/ui/dist" ]; then
142+
mkdir -p packages/cli/dist/ui-dist
143+
rm -rf packages/cli/dist/ui-dist/*
144+
cp -r packages/ui/dist/* packages/cli/dist/ui-dist/
145+
echo -e "${GREEN} UI assets synced to CLI!${NC}"
146+
else
147+
echo -e "${RED} Error: UI dist not found. Run without --skip-ui first.${NC}"
148+
exit 1
149+
fi
150+
echo
151+
152+
# Verify build
153+
echo -e "${CYAN}Build verification:${NC}"
154+
if [ -f "packages/cli/dist/ui-dist/index.html" ]; then
155+
JS_FILE=$(grep -o 'index-[A-Za-z0-9]*\.js' packages/cli/dist/ui-dist/index.html | head -1)
156+
CSS_FILE=$(grep -o 'index-[A-Za-z0-9]*\.css' packages/cli/dist/ui-dist/index.html | head -1)
157+
echo -e " ${GREEN}${NC} UI HTML: packages/cli/dist/ui-dist/index.html"
158+
echo -e " ${GREEN}${NC} JS: packages/cli/dist/ui-dist/assets/${JS_FILE}"
159+
echo -e " ${GREEN}${NC} CSS: packages/cli/dist/ui-dist/assets/${CSS_FILE}"
160+
else
161+
echo -e " ${RED}${NC} UI dist not found!"
162+
exit 1
163+
fi
164+
165+
if [ -f "packages/cli/dist/bundle.cjs" ]; then
166+
echo -e " ${GREEN}${NC} CLI: packages/cli/dist/bundle.cjs"
167+
else
168+
echo -e " ${RED}${NC} CLI bundle not found!"
169+
exit 1
170+
fi
171+
echo
172+
173+
# Build packages if requested
174+
if [ "$BUILD_RPM" = true ] || [ "$BUILD_DEB" = true ]; then
175+
echo -e "${CYAN}Building distribution packages...${NC}"
176+
echo
177+
178+
if [ "$BUILD_RPM" = true ] && [ "$BUILD_DEB" = true ]; then
179+
./scripts/build-packages.sh all
180+
elif [ "$BUILD_RPM" = true ]; then
181+
./scripts/build-packages.sh rpm
182+
elif [ "$BUILD_DEB" = true ]; then
183+
./scripts/build-packages.sh deb
184+
fi
185+
echo
186+
fi
187+
188+
echo -e "${GREEN}╔══════════════════════════════════════╗${NC}"
189+
echo -e "${GREEN}║ Build Complete! ║${NC}"
190+
echo -e "${GREEN}╚══════════════════════════════════════╝${NC}"
191+
echo
192+
193+
# Start server if requested
194+
if [ "$SERVE" = true ]; then
195+
echo -e "${CYAN}Starting dev server...${NC}"
196+
echo "Use Ctrl+C to stop"
197+
echo
198+
199+
# Parse serve args - first non-flag arg is the demo path
200+
DEMO_PATH=""
201+
EXTRA_ARGS=()
202+
HAS_PORT=false
203+
HAS_HOST=false
204+
205+
for arg in "${SERVE_ARGS[@]}"; do
206+
if [[ "$arg" == --port* ]] || [[ "$arg" == -p ]]; then
207+
HAS_PORT=true
208+
EXTRA_ARGS+=("$arg")
209+
elif [[ "$arg" == -H ]] || [[ "$arg" == --host ]]; then
210+
HAS_HOST=true
211+
EXTRA_ARGS+=("$arg")
212+
elif [[ "$arg" == -* ]]; then
213+
EXTRA_ARGS+=("$arg")
214+
elif [ -z "$DEMO_PATH" ]; then
215+
DEMO_PATH="$arg"
216+
else
217+
EXTRA_ARGS+=("$arg")
218+
fi
219+
done
220+
221+
# Default demo path
222+
DEMO_PATH="${DEMO_PATH:-examples/feature-showcase}"
223+
224+
# Add default port and host if not specified
225+
if [ "$HAS_PORT" = false ]; then
226+
EXTRA_ARGS+=(--port 3001)
227+
fi
228+
if [ "$HAS_HOST" = false ]; then
229+
EXTRA_ARGS+=(-H 0.0.0.0)
230+
fi
231+
232+
if [ -d "$DEMO_PATH" ] || [ -f "$DEMO_PATH" ]; then
233+
echo -e " Demo: ${CYAN}$DEMO_PATH${NC}"
234+
echo -e " Args: ${CYAN}${EXTRA_ARGS[*]}${NC}"
235+
echo
236+
node packages/cli/dist/index.js serve "$DEMO_PATH" "${EXTRA_ARGS[@]}"
237+
else
238+
echo -e "${RED}Demo not found: $DEMO_PATH${NC}"
239+
echo
240+
echo "Usage: ./build.sh --serve [demo-path] [serve-options]"
241+
echo "Examples:"
242+
echo " ./build.sh --serve"
243+
echo " ./build.sh --serve ~/my-demo"
244+
echo " ./build.sh --serve ~/my-demo --port 8080"
245+
echo " ./build.sh --serve --tunnel"
246+
exit 1
247+
fi
248+
fi

packages/ui/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function App() {
88
<ThemeProvider defaultTheme="dark">
99
<SoundProvider>
1010
<DemoProvider>
11-
<div className="min-h-screen relative bg-white dark:bg-transparent transition-colors duration-300">
11+
<div className="min-h-screen relative light-mode-gradient dark:bg-transparent transition-colors duration-300">
1212
<DemoRunner />
1313
</div>
1414
</DemoProvider>

packages/ui/src/components/Dashboard.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ export function Dashboard({ config, onStart }: DashboardProps) {
3333
return (
3434
<div className="min-h-screen flex items-center justify-center p-4 relative z-10">
3535
<div className="w-full max-w-2xl">
36-
<div className="bg-white/90 dark:bg-slate-800/90 backdrop-blur-xl rounded-2xl shadow-2xl dark:shadow-purple-500/10 p-8 border border-slate-200 dark:border-purple-500/20">
36+
<div className="bg-white/90 dark:bg-slate-800/90 backdrop-blur-xl rounded-2xl shadow-2xl dark:shadow-[rgba(var(--color-primary-rgb),0.1)] p-8 border border-slate-200 dark:border-[rgba(var(--color-primary-rgb),0.2)]">
3737
{/* Logo/Icon */}
3838
<div className="flex justify-center mb-6">
39-
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-purple-500 to-cyan-500 flex items-center justify-center shadow-lg shadow-purple-500/30">
39+
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-theme-primary to-theme-accent flex items-center justify-center shadow-lg shadow-[rgba(var(--color-primary-rgb),0.3)]">
4040
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
4141
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
4242
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
@@ -134,7 +134,7 @@ export function Dashboard({ config, onStart }: DashboardProps) {
134134
{config.tags.map(tag => (
135135
<span
136136
key={tag}
137-
className="px-3 py-1 text-xs font-medium rounded-full bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300"
137+
className="px-3 py-1 text-xs font-medium rounded-full bg-[rgba(var(--color-primary-rgb),0.1)] dark:bg-[rgba(var(--color-primary-rgb),0.2)] text-theme-primary"
138138
>
139139
{tag}
140140
</span>
@@ -146,10 +146,10 @@ export function Dashboard({ config, onStart }: DashboardProps) {
146146
<button
147147
onClick={onStart}
148148
className="w-full py-4 px-6 rounded-lg font-medium text-lg transition-all duration-200
149-
bg-gradient-to-r from-purple-600 to-cyan-600
150-
hover:from-purple-500 hover:to-cyan-500
151-
text-white shadow-lg shadow-purple-500/30
152-
focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2
149+
bg-gradient-to-r from-theme-primary to-theme-accent
150+
hover:opacity-90
151+
text-white shadow-lg shadow-[rgba(var(--color-primary-rgb),0.3)]
152+
focus:outline-none focus:ring-2 focus:ring-[rgba(var(--color-primary-rgb),0.5)] focus:ring-offset-2
153153
dark:focus:ring-offset-slate-800"
154154
>
155155
Start Demo

0 commit comments

Comments
 (0)