Skip to content

Commit c2e2c83

Browse files
feat: add demo mode feature flag using Redacted Script font
1 parent b25a683 commit c2e2c83

5 files changed

Lines changed: 95 additions & 18 deletions

File tree

packages/web/src/components/layout/Sidebar.tsx

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
import { Link, useMatchRoute } from "@tanstack/react-router";
22
import { motion } from "framer-motion";
3-
import { Boxes, Brain, ChevronRight, LayoutDashboard, Moon, Settings, Sun } from "lucide-react";
3+
import {
4+
Boxes,
5+
Brain,
6+
ChevronRight,
7+
Eye,
8+
EyeOff,
9+
LayoutDashboard,
10+
Moon,
11+
Settings,
12+
Sun,
13+
} from "lucide-react";
14+
import { useDemo } from "@/hooks/useDemo";
415
import { useTheme } from "@/hooks/useTheme";
516
import { loadConfig } from "@/lib/config";
617
import { COLOR } from "@/lib/constants";
@@ -15,6 +26,7 @@ export function Sidebar() {
1526
const matchRoute = useMatchRoute();
1627
const config = loadConfig();
1728
const { theme, toggle } = useTheme();
29+
const { demo, toggle: toggleDemo } = useDemo();
1830

1931
return (
2032
<motion.aside
@@ -110,23 +122,42 @@ export function Sidebar() {
110122
<p className="text-xs font-mono hidden sm:block" style={{ color: "var(--text-4)" }}>
111123
API v3
112124
</p>
113-
<button
114-
type="button"
115-
onClick={toggle}
116-
className="w-7 h-7 rounded-md flex items-center justify-center transition-colors mx-auto sm:mx-0"
117-
style={{
118-
background: "var(--surface)",
119-
border: "1px solid var(--border)",
120-
color: "var(--text-3)",
121-
}}
122-
title={theme === "dark" ? "Switch to light mode" : "Switch to dark mode"}
123-
>
124-
{theme === "dark" ? (
125-
<Sun className="w-3.5 h-3.5" strokeWidth={1.5} />
126-
) : (
127-
<Moon className="w-3.5 h-3.5" strokeWidth={1.5} />
128-
)}
129-
</button>
125+
<div className="flex items-center gap-1.5 mx-auto sm:mx-0">
126+
<button
127+
type="button"
128+
onClick={toggleDemo}
129+
className="w-7 h-7 rounded-md flex items-center justify-center transition-colors"
130+
style={{
131+
background: demo ? "var(--accent-dim)" : "var(--surface)",
132+
border: `1px solid ${demo ? "var(--accent-border)" : "var(--border)"}`,
133+
color: demo ? "var(--accent-text)" : "var(--text-3)",
134+
}}
135+
title={demo ? "Disable demo mode" : "Enable demo mode"}
136+
>
137+
{demo ? (
138+
<EyeOff className="w-3.5 h-3.5" strokeWidth={1.5} />
139+
) : (
140+
<Eye className="w-3.5 h-3.5" strokeWidth={1.5} />
141+
)}
142+
</button>
143+
<button
144+
type="button"
145+
onClick={toggle}
146+
className="w-7 h-7 rounded-md flex items-center justify-center transition-colors"
147+
style={{
148+
background: "var(--surface)",
149+
border: "1px solid var(--border)",
150+
color: "var(--text-3)",
151+
}}
152+
title={theme === "dark" ? "Switch to light mode" : "Switch to dark mode"}
153+
>
154+
{theme === "dark" ? (
155+
<Sun className="w-3.5 h-3.5" strokeWidth={1.5} />
156+
) : (
157+
<Moon className="w-3.5 h-3.5" strokeWidth={1.5} />
158+
)}
159+
</button>
160+
</div>
130161
</div>
131162
</motion.aside>
132163
);

packages/web/src/hooks/useDemo.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useEffect, useState } from "react";
2+
import { applyDemoMode, getDemoMode } from "@/lib/demo";
3+
4+
export function useDemo() {
5+
const [demo, setDemo] = useState<boolean>(() => getDemoMode());
6+
7+
useEffect(() => {
8+
applyDemoMode(demo);
9+
}, [demo]);
10+
11+
function toggle() {
12+
setDemo((d) => !d);
13+
}
14+
15+
return { demo, toggle };
16+
}

packages/web/src/index.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@
55
@import "@fontsource/dm-sans/500.css";
66
@import "@fontsource/dm-sans/600.css";
77

8+
/* Redacted font — loaded on demand for demo mode */
9+
@font-face {
10+
font-family: "Redacted Script";
11+
font-style: normal;
12+
font-weight: 400;
13+
font-display: swap;
14+
src:
15+
url("https://cdn.jsdelivr.net/gh/christiannaths/redacted-font@0.0.2/fonts/web/redacted-script-regular.woff2")
16+
format("woff2"),
17+
url("https://cdn.jsdelivr.net/gh/christiannaths/redacted-font@0.0.2/fonts/web/redacted-script-regular.woff")
18+
format("woff");
19+
}
20+
21+
[data-demo="true"] *:not(svg):not(path):not(circle):not(line):not(polyline):not(rect):not(polygon) {
22+
font-family: "Redacted Script", cursive !important;
23+
}
24+
825
/* ─── Tailwind v4 theme bridge ─── */
926
@theme inline {
1027
--color-background: var(--bg);

packages/web/src/lib/demo.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const DEMO_KEY = "openconcho:demo";
2+
3+
export function getDemoMode(): boolean {
4+
return localStorage.getItem(DEMO_KEY) === "true";
5+
}
6+
7+
export function applyDemoMode(enabled: boolean): void {
8+
document.documentElement.setAttribute("data-demo", String(enabled));
9+
localStorage.setItem(DEMO_KEY, String(enabled));
10+
}

packages/web/src/main.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
22
import { createRouter, RouterProvider } from "@tanstack/react-router";
33
import { StrictMode } from "react";
44
import { createRoot } from "react-dom/client";
5+
import { applyDemoMode, getDemoMode } from "@/lib/demo";
56
import { routeTree } from "./routeTree.gen";
67
import "./index.css";
78

9+
applyDemoMode(getDemoMode());
10+
811
const queryClient = new QueryClient({
912
defaultOptions: {
1013
queries: {

0 commit comments

Comments
 (0)