Skip to content

Commit 8792a24

Browse files
authored
NextJS Template Styles (#1694)
1 parent 6a50e18 commit 8792a24

File tree

5 files changed

+171
-107
lines changed

5 files changed

+171
-107
lines changed
Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,33 @@
11
@import "tailwindcss";
22

33
:root {
4-
--background: #ffffff;
5-
--foreground: #171717;
4+
--background: #0f0f0f;
5+
--foreground: #fafafa;
6+
--muted: #71717a;
7+
--border: #27272a;
8+
--card: #18181b;
9+
--accent: #7c3aed;
10+
--accent-hover: #6d28d9;
611
}
712

813
@theme inline {
914
--color-background: var(--background);
1015
--color-foreground: var(--foreground);
16+
--color-muted: var(--muted);
17+
--color-border: var(--border);
18+
--color-card: var(--card);
19+
--color-accent: var(--accent);
20+
--color-accent-hover: var(--accent-hover);
1121
--font-sans: var(--font-geist-sans);
1222
--font-mono: var(--font-geist-mono);
1323
}
1424

15-
@media (prefers-color-scheme: dark) {
16-
:root {
17-
--background: #0a0a0a;
18-
--foreground: #ededed;
19-
}
20-
}
21-
2225
body {
2326
background: var(--background);
2427
color: var(--foreground);
25-
font-family: Arial, Helvetica, sans-serif;
28+
font-family: var(--font-sans), system-ui, sans-serif;
29+
}
30+
31+
* {
32+
border-color: var(--border);
2633
}

ts/create-smelter-app/templates/node-next-webrtc/client/app/page.tsx

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,38 @@ import Link from "next/link";
22

33
export default function Home() {
44
return (
5-
<div className="flex min-h-screen items-center justify-center bg-zinc-800 font-sans">
6-
<main className="flex flex-col gap-10 min-h-screen w-full max-w-3xl flex-col items-stretch justify-start py-32 px-16">
7-
<div>
8-
<p className="text-white">This is a simple demo application that:</p>
9-
<ul className="text-white list-disc list-inside">
10-
<li>Streams camera or screen share to the Smelter instance over WHIP.</li>
11-
<li>Apply effects, adds overlay elements, ...</li>
12-
<li>Broadcasts resulting stream over WHEP.</li>
13-
</ul>
14-
</div>
5+
<div className="min-h-screen bg-background text-foreground font-sans">
6+
<main className="max-w-2xl mx-auto px-6 py-24">
7+
<header className="mb-12">
8+
<h1 className="text-3xl font-semibold tracking-tight mb-2">Smelter Demo</h1>
9+
<p className="text-muted">WebRTC streaming with real-time video processing</p>
10+
</header>
1511

16-
<div className="flex flex-row gap-10">
17-
<Link href="/viewer" className="flex-1 bg-purple-800 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded mb-10">
18-
Open as a viewer
19-
</Link>
20-
<Link href="/streamer" className="flex-1 bg-purple-800 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded mb-10">
21-
Open as a streamer
22-
</Link>
23-
</div>
12+
<section className="mb-12">
13+
<h2 className="text-sm font-medium text-muted uppercase tracking-wide mb-4">About</h2>
14+
<div className="bg-card border border-border rounded-lg p-6">
15+
<p className="text-foreground/90 mb-4">This demo application showcases Smelter&apos;s capabilities:</p>
16+
<ul className="space-y-2 text-foreground/80">
17+
<li className="flex gap-3"><span className="text-accent"></span>Stream camera or screen share to Smelter over WHIP</li>
18+
<li className="flex gap-3"><span className="text-accent"></span>Apply effects and overlay elements in real-time</li>
19+
<li className="flex gap-3"><span className="text-accent"></span>Broadcast resulting stream over WHEP</li>
20+
</ul>
21+
</div>
22+
</section>
23+
24+
<section>
25+
<h2 className="text-sm font-medium text-muted uppercase tracking-wide mb-4">Get Started</h2>
26+
<div className="grid grid-cols-2 gap-4">
27+
<Link href="/viewer" className="bg-card border border-border rounded-lg p-6 hover:border-accent/50 transition-all group">
28+
<div className="text-lg font-medium mb-1 group-hover:text-accent transition-colors">Viewer</div>
29+
<p className="text-sm text-muted">Watch the stream</p>
30+
</Link>
31+
<Link href="/streamer" className="bg-accent hover:bg-accent-hover rounded-lg p-6 transition-all">
32+
<div className="text-lg font-medium mb-1">Streamer</div>
33+
<p className="text-sm text-white/70">Start broadcasting</p>
34+
</Link>
35+
</div>
36+
</section>
2437
</main>
2538
</div>
2639
);
Lines changed: 72 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,109 @@
1-
"use client"
1+
"use client";
22

3+
import PageHeader from "@/components/PageHeader";
34
import WhepClientVideo from "@/components/WhepClientVideo";
45
import { WhipClient } from "@/utils/whip-client";
56
import { useRef, useState } from "react";
67

78
// Base url of a WHIP/WHEP server. By default, Smelter exposes this server on
89
// port 9000, but the value can be changed via SMELTER_WHIP_WHEP_SERVER_PORT
910
// environment variable.
10-
const SMELTER_WHIP_WHEP_URL = new URL("http://127.0.0.1:9000")
11-
12-
const WHIP_AUTH_TOKEN = "example_token"
11+
const SMELTER_WHIP_WHEP_URL = new URL("http://127.0.0.1:9000");
1312

1413
// API of Node.js server from `/server` directory.
15-
const BACKEND_URL = new URL("http://127.0.0.1:3001")
14+
const BACKEND_URL = new URL("http://127.0.0.1:3001");
1615

17-
export default function Home() {
18-
return (
19-
<div className="flex min-h-screen items-center justify-center bg-zinc-800 font-sans">
20-
<main className="flex flex-row min-h-screen justify-between py-32 px-16">
21-
<WhepClientVideo
22-
url={new URL("/whep/output", SMELTER_WHIP_WHEP_URL).toString()}
23-
poster="https://placehold.co/1920x1080/000000/333333?text=Waiting+for+stream..."
24-
playsInline autoPlay controls
25-
className='min-w-0 min-h-0 w-full h-full object-cover bg-black'
26-
/>
27-
<Controls />
28-
</main>
29-
</div>
30-
);
31-
}
16+
const WHIP_AUTH_TOKEN = "example_token";
3217

33-
function Controls() {
18+
type Connection = "camera" | "screen-share" | "none";
19+
20+
export default function StreamerPage() {
3421
const clientRef = useRef<WhipClient>(new WhipClient());
35-
const [connection, setConnection] = useState<'camera' | 'screen-share' | 'none'>('none');
36-
const [showInstructions, setShowInstruction] = useState(true);
22+
const [connection, setConnection] = useState<Connection>("none");
23+
const [showInstructions, setShowInstructions] = useState(true);
3724

3825
const toggleCamera = async () => {
39-
setConnection('none')
26+
setConnection("none");
4027
await clientRef.current.close();
41-
if (connection !== 'camera') {
28+
if (connection !== "camera") {
4229
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
4330
await clientRef.current.connect(stream, new URL("/whip/input", SMELTER_WHIP_WHEP_URL), WHIP_AUTH_TOKEN);
44-
setConnection('camera')
31+
setConnection("camera");
4532
}
46-
}
33+
};
4734

4835
const toggleScreenShare = async () => {
49-
setConnection('none')
36+
setConnection("none");
5037
await clientRef.current.close();
51-
if (connection !== 'screen-share') {
38+
if (connection !== "screen-share") {
5239
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true });
5340
await clientRef.current.connect(stream, new URL("/whip/input", SMELTER_WHIP_WHEP_URL), WHIP_AUTH_TOKEN);
54-
setConnection('screen-share')
41+
setConnection("screen-share");
5542
}
56-
}
43+
};
5744

5845
const toggleInstructions = async () => {
59-
setShowInstruction(!showInstructions)
46+
setShowInstructions(!showInstructions);
6047
await fetch(new URL("/layout-update", BACKEND_URL), {
61-
method: 'POST',
62-
mode: 'cors',
63-
headers: {
64-
'content-type': 'application/json',
65-
},
48+
method: "POST",
49+
mode: "cors",
50+
headers: { "content-type": "application/json" },
6651
body: JSON.stringify({ showInstructions: !showInstructions }),
6752
});
68-
}
53+
};
6954

7055
return (
71-
<div className="w-1/3 p-10 flex flex-col items-start">
72-
<button className="bg-purple-800 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded mb-10 w-full" onClick={toggleScreenShare}>
73-
{connection === 'screen-share' ? 'Stop' : 'Start'} screen share
74-
</button>
75-
<button className="bg-purple-800 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded mb-10 w-full" onClick={toggleCamera}>
76-
{connection === 'camera' ? 'Stop' : 'Start'} camera
77-
</button>
78-
<Checkbox description="Show instructions" isChecked={showInstructions} onChange={toggleInstructions} />
79-
</div>
80-
)
81-
}
56+
<div className="min-h-screen bg-background text-foreground font-sans">
57+
<PageHeader
58+
title="Streamer"
59+
statusDot={connection === "none" ? "bg-muted" : "bg-red-500 animate-pulse"}
60+
statusText={connection === "none" ? "Not streaming" : connection === "camera" ? "Camera" : "Screen"}
61+
/>
8262

83-
function Checkbox(props: { description: string, isChecked: boolean, onChange: (update: boolean) => void }) {
84-
return (
85-
<div className="flex items-start gap-3 p-4 border border-slate-200 rounded-lg transition-colors cursor-pointer mb-10 w-full"
86-
onClick={() => props.onChange(!props.isChecked)}>
63+
<main className="max-w-6xl mx-auto px-6 py-8">
64+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
65+
<div className="lg:col-span-2">
66+
<div className="bg-card border border-border rounded-lg overflow-hidden">
67+
<div className="aspect-video bg-black">
68+
<WhepClientVideo
69+
url={new URL("/whep/output", SMELTER_WHIP_WHEP_URL).toString()}
70+
poster="https://placehold.co/1920x1080/0f0f0f/27272a?text=Preview"
71+
playsInline autoPlay controls
72+
className="w-full h-full object-contain"
73+
/>
74+
</div>
75+
</div>
76+
<p className="mt-3 text-sm text-muted text-center">Output preview</p>
77+
</div>
8778

88-
<div className="flex items-center h-5">
89-
<input
90-
type="checkbox"
91-
checked={props.isChecked}
92-
onChange={() => props.onChange(!props.isChecked)}
93-
className="w-4 h-4 text-purple-800 border-gray-300 rounded focus:ring-purple-700 cursor-pointer"
94-
/>
95-
</div>
79+
<div className="space-y-4">
80+
<div className="bg-card border border-border rounded-lg p-5">
81+
<h2 className="text-sm font-medium text-muted uppercase tracking-wide mb-4">Source</h2>
82+
<div className="space-y-3">
83+
<button onClick={toggleScreenShare} className={`w-full py-3 px-4 rounded-lg font-medium transition-all ${connection === "screen-share" ? "bg-red-500/20 text-red-400 border border-red-500/30 hover:bg-red-500/30" : "bg-accent hover:bg-accent-hover text-white"}`}>
84+
{connection === "screen-share" ? "Stop" : "Start"} Screen Share
85+
</button>
86+
<button onClick={toggleCamera} className={`w-full py-3 px-4 rounded-lg font-medium transition-all ${connection === "camera" ? "bg-red-500/20 text-red-400 border border-red-500/30 hover:bg-red-500/30" : "bg-accent hover:bg-accent-hover text-white"}`}>
87+
{connection === "camera" ? "Stop" : "Start"} Camera
88+
</button>
89+
</div>
90+
</div>
9691

97-
<div className="flex flex-col">
98-
<label
99-
className="font-medium text-slate-900 cursor-pointer text-white"
100-
>
101-
{props.description}
102-
</label>
103-
</div>
92+
<div className="bg-card border border-border rounded-lg p-5">
93+
<h2 className="text-sm font-medium text-muted uppercase tracking-wide mb-4">Overlay</h2>
94+
<label className="flex items-center justify-between cursor-pointer">
95+
<span>Show instructions</span>
96+
<div
97+
className={`relative w-11 h-6 rounded-full transition-colors ${showInstructions ? "bg-accent" : "bg-border"}`}
98+
onClick={toggleInstructions}
99+
>
100+
<div className={`absolute top-1 w-4 h-4 bg-white rounded-full transition-transform ${showInstructions ? "translate-x-6" : "translate-x-1"}`} />
101+
</div>
102+
</label>
103+
</div>
104+
</div>
105+
</div>
106+
</main>
104107
</div>
105108
);
106-
};
109+
}

ts/create-smelter-app/templates/node-next-webrtc/client/app/viewer/page.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
1+
import PageHeader from "@/components/PageHeader";
12
import WhepClientVideo from "@/components/WhepClientVideo";
23

34
// Base url of a WHIP/WHEP server. By default, Smelter exposes this server on
45
// port 9000, but the value can be changed via SMELTER_WHIP_WHEP_SERVER_PORT
56
// environment variable.
6-
const SMELTER_WHIP_WHEP_URL = new URL("http://127.0.0.1:9000")
7+
const SMELTER_WHIP_WHEP_URL = new URL("http://127.0.0.1:9000");
78

8-
export default function Home() {
9+
export default function ViewerPage() {
910
return (
10-
<div className="flex min-h-screen items-center justify-center bg-zinc-800 font-sans">
11-
<main className="flex flex-row min-h-screen justify-between py-32 px-16">
12-
<WhepClientVideo
13-
url={new URL("/whep/output", SMELTER_WHIP_WHEP_URL).toString()}
14-
poster="https://placehold.co/1920x1080/000000/333333?text=Waiting+for+stream..."
15-
playsInline autoPlay controls
16-
className='min-w-0 min-h-0 w-full h-full object-cover bg-black'
17-
/>
11+
<div className="min-h-screen bg-background text-foreground font-sans">
12+
<PageHeader
13+
title="Viewer"
14+
statusDot="bg-accent animate-pulse"
15+
statusText="Watching"
16+
/>
17+
18+
<main className="max-w-6xl mx-auto px-6 py-8">
19+
<div className="bg-card border border-border rounded-lg overflow-hidden">
20+
<div className="aspect-video bg-black">
21+
<WhepClientVideo
22+
url={new URL("/whep/output", SMELTER_WHIP_WHEP_URL).toString()}
23+
poster="https://placehold.co/1920x1080/0f0f0f/27272a?text=Waiting..."
24+
playsInline autoPlay controls
25+
className="w-full h-full object-contain"
26+
/>
27+
</div>
28+
</div>
29+
<p className="mt-6 text-sm text-muted text-center">
30+
Output stream from the Smelter server
31+
</p>
1832
</main>
1933
</div>
2034
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Link from "next/link";
2+
3+
type PageHeaderProps = {
4+
title: string;
5+
statusDot?: string;
6+
statusText?: string;
7+
};
8+
9+
export default function PageHeader({ title, statusDot, statusText }: PageHeaderProps) {
10+
return (
11+
<header className="border-b border-border py-4">
12+
<div className="max-w-6xl mx-auto px-6 flex items-center justify-between">
13+
<div className="flex items-center gap-4">
14+
<Link href="/" className="text-muted hover:text-foreground transition-colors">← Back</Link>
15+
<span className="text-border">|</span>
16+
<h1 className="font-medium">{title}</h1>
17+
</div>
18+
{statusDot && statusText && (
19+
<div className="flex items-center gap-2">
20+
<span className={`w-2 h-2 rounded-full ${statusDot}`} />
21+
<span className="text-sm text-muted">{statusText}</span>
22+
</div>
23+
)}
24+
</div>
25+
</header>
26+
);
27+
}

0 commit comments

Comments
 (0)