Skip to content

Commit b306f0f

Browse files
committed
feat(demo): add tree-shakeable demo modes for App Store screenshots
Add demo mode support via VITE_DEMO_MODE environment variable with 5 modes: empty, withChanges, withComments, staged, darkMode. Key implementation details: - Dynamic imports ensure demo code is excluded from production bundles - Stores accept data as parameters instead of importing demo modules - import.meta.env.DEV guard enables dead code elimination
1 parent d21487f commit b306f0f

14 files changed

Lines changed: 714 additions & 22 deletions

docs/icon.png

-229 KB
Loading

docs/screenshot-dark-split.png

468 KB
Loading

docs/screenshot-dark.png

258 KB
Loading

docs/screenshot-light-split.png

465 KB
Loading

docs/screenshot-light.png

262 KB
Loading

scripts/demo.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/bash
2+
# Demo mode launcher for Revu (Tauri/React)
3+
# Runs the dev server with selected demo mode
4+
5+
set -e
6+
7+
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
8+
9+
# Demo modes
10+
MODES=(
11+
"empty|No repo loaded"
12+
"withChanges|Files with changes, one selected"
13+
"withComments|Changes + review comments panel"
14+
"staged|Files staged, commit panel ready"
15+
"darkMode|withComments in dark theme"
16+
)
17+
18+
echo "=== Revu Demo Mode Launcher ==="
19+
echo ""
20+
echo "Select a demo mode:"
21+
echo ""
22+
23+
for i in "${!MODES[@]}"; do
24+
mode="${MODES[$i]%%|*}"
25+
desc="${MODES[$i]#*|}"
26+
printf " %d) %-15s - %s\n" $((i+1)) "$mode" "$desc"
27+
done
28+
29+
echo ""
30+
read -p "Enter choice [1-${#MODES[@]}]: " choice
31+
32+
if [[ ! "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt ${#MODES[@]} ]; then
33+
echo "Invalid choice"
34+
exit 1
35+
fi
36+
37+
SELECTED="${MODES[$((choice-1))]}"
38+
SELECTED_MODE="${SELECTED%%|*}"
39+
echo ""
40+
echo "Selected: $SELECTED_MODE"
41+
echo ""
42+
43+
cd "$PROJECT_DIR"
44+
45+
# Launch with demo mode environment variable
46+
echo "Starting dev server with VITE_DEMO_MODE=$SELECTED_MODE..."
47+
echo "(Press Ctrl+C to stop)"
48+
echo ""
49+
50+
VITE_DEMO_MODE="$SELECTED_MODE" bun run tauri dev

site/logo.png

-229 KB
Loading

site/screenshot-dark.png

258 KB
Loading

src/App.tsx

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,65 @@ import { CommitPanel } from "@/features/commit";
1212
import { Button } from "@/components/ui";
1313

1414
export default function App() {
15-
const { repoPath, status, setRepoPath, refreshStatus } = useGitStore();
15+
const { repoPath, status, setRepoPath, refreshStatus, initDemoMode, isDemo } =
16+
useGitStore();
1617
const {
1718
draft,
1819
exportToMarkdown,
1920
setRepoPath: setCommentRepoPath,
2021
getAllComments,
22+
initDemoComments,
2123
} = useCommentStore();
2224
const {
2325
showCommentsPanel,
2426
toggleCommentsPanel,
2527
setShowCommentsPanel,
28+
setTheme,
2629
sidebarWidth,
2730
} = useUiStore();
28-
29-
// Sync comment store's repo path with git store
31+
32+
// Initialize demo mode on mount (only in development)
33+
useEffect(() => {
34+
// Early exit in production - this branch is removed by dead code elimination
35+
if (!import.meta.env.DEV) return;
36+
37+
// Check for demo mode env var
38+
const demoModeEnv = import.meta.env.VITE_DEMO_MODE as string | undefined;
39+
if (!demoModeEnv || demoModeEnv === "empty") return;
40+
41+
// Dynamic import - entire demo module tree excluded from production bundle
42+
import("@/lib/demo").then(
43+
({ getDemoMode, demoHasComments, demoUsesDarkTheme, buildDemoGitState, createDemoComments }) => {
44+
const demoMode = getDemoMode();
45+
if (!demoMode || demoMode === "empty") return;
46+
47+
// Initialize git store with demo data
48+
const demoGitState = buildDemoGitState();
49+
initDemoMode(demoGitState);
50+
51+
// Initialize comments if needed
52+
if (demoHasComments(demoMode)) {
53+
const demoComments = createDemoComments();
54+
initDemoComments(demoGitState.status.path, demoComments);
55+
setShowCommentsPanel(true);
56+
}
57+
58+
// Set dark theme if needed
59+
if (demoUsesDarkTheme(demoMode)) {
60+
setTheme("dark");
61+
}
62+
},
63+
);
64+
// Only run on mount
65+
// eslint-disable-next-line react-hooks/exhaustive-deps
66+
}, []);
67+
68+
// Sync comment store's repo path with git store (skip in demo mode)
3069
useEffect(() => {
31-
if (repoPath) {
70+
if (repoPath && !isDemo) {
3271
setCommentRepoPath(repoPath);
3372
}
34-
}, [repoPath, setCommentRepoPath]);
73+
}, [repoPath, setCommentRepoPath, isDemo]);
3574

3675
// Listen for CLI-provided repo path from backend
3776
useEffect(() => {

0 commit comments

Comments
 (0)