Skip to content

Commit 750e94a

Browse files
jerelvelardeclaude
andcommitted
feat: fullscreen toggle on playgrounds, reorder chapters, add Agent Skills
- Add fullscreen button (expand icon) to all playground cells with Escape key to exit; body scroll locked while fullscreen - Rewrite MCP Skills chapter as "Agent Skills" — focuses on how skill documents (SKILL.md) teach the agent to write code: decision tree, response format selection, design system tokens - Reorder chapters: Introduction → Deep Agent → Agent Skills → CopilotKit → Widget Renderer → Putting It Together Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 57f8894 commit 750e94a

3 files changed

Lines changed: 356 additions & 255 deletions

File tree

apps/notebook/src/components/playground-cell.tsx

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
SandpackLayout,
88
useSandpack,
99
} from "@codesandbox/sandpack-react";
10-
import { useEffect, useState } from "react";
10+
import { useEffect, useState, useCallback } from "react";
1111

1212
function ResetButton() {
1313
const { sandpack } = useSandpack();
@@ -36,6 +36,7 @@ export function PlaygroundCell({
3636
title?: string;
3737
}) {
3838
const [isDark, setIsDark] = useState(false);
39+
const [isFullscreen, setIsFullscreen] = useState(false);
3940

4041
useEffect(() => {
4142
const check = () =>
@@ -49,6 +50,23 @@ export function PlaygroundCell({
4950
return () => observer.disconnect();
5051
}, []);
5152

53+
// Close fullscreen on Escape
54+
useEffect(() => {
55+
if (!isFullscreen) return;
56+
const handler = (e: KeyboardEvent) => {
57+
if (e.key === "Escape") setIsFullscreen(false);
58+
};
59+
document.addEventListener("keydown", handler);
60+
// Prevent body scroll while fullscreen
61+
document.body.style.overflow = "hidden";
62+
return () => {
63+
document.removeEventListener("keydown", handler);
64+
document.body.style.overflow = "";
65+
};
66+
}, [isFullscreen]);
67+
68+
const toggleFullscreen = useCallback(() => setIsFullscreen((v) => !v), []);
69+
5270
const sandpackTheme = {
5371
colors: {
5472
surface1: isDark ? "#1a1a2e" : "#ffffff",
@@ -70,26 +88,89 @@ export function PlaygroundCell({
7088
},
7189
};
7290

91+
const wrapperClass = isFullscreen
92+
? "fixed inset-0 z-50 flex flex-col"
93+
: "notebook-cell overflow-hidden";
94+
95+
const wrapperStyle = isFullscreen
96+
? {
97+
background: isDark ? "#0a0a0a" : "#ffffff",
98+
borderColor: "var(--color-mint)",
99+
}
100+
: { borderColor: "var(--color-mint)" };
101+
73102
return (
74-
<div className="notebook-cell overflow-hidden" style={{ borderColor: "var(--color-mint)" }}>
103+
<div className={wrapperClass} style={wrapperStyle}>
104+
{/* Header bar */}
75105
{title && (
76106
<div
77-
className="px-4 py-2.5 text-sm font-semibold flex items-center gap-2 border-b"
107+
className="px-4 py-2.5 text-sm font-semibold flex items-center gap-2 border-b shrink-0"
78108
style={{
79109
borderColor: "var(--color-border-glass)",
80-
background: "var(--color-glass-subtle)",
110+
background: isFullscreen
111+
? isDark
112+
? "#1a1a2e"
113+
: "#f7f7f9"
114+
: "var(--color-glass-subtle)",
81115
}}
82116
>
83117
<span
84118
className="inline-flex items-center justify-center w-5 h-5 rounded text-xs"
85119
style={{
86-
background: "linear-gradient(135deg, var(--color-lilac), var(--color-mint))",
120+
background:
121+
"linear-gradient(135deg, var(--color-lilac), var(--color-mint))",
87122
color: "#fff",
88123
}}
89124
>
90125
&#9654;
91126
</span>
92-
<span style={{ color: "var(--text-primary)" }}>{title}</span>
127+
<span className="flex-1" style={{ color: "var(--text-primary)" }}>
128+
{title}
129+
</span>
130+
<button
131+
onClick={toggleFullscreen}
132+
className="flex items-center justify-center w-7 h-7 rounded-md transition-colors cursor-pointer"
133+
style={{
134+
background: "var(--color-glass-subtle)",
135+
border: "1px solid var(--color-border-glass)",
136+
color: "var(--text-secondary)",
137+
}}
138+
title={isFullscreen ? "Exit fullscreen (Esc)" : "Fullscreen"}
139+
>
140+
{isFullscreen ? (
141+
<svg
142+
width="14"
143+
height="14"
144+
viewBox="0 0 24 24"
145+
fill="none"
146+
stroke="currentColor"
147+
strokeWidth="2"
148+
strokeLinecap="round"
149+
strokeLinejoin="round"
150+
>
151+
<polyline points="4 14 10 14 10 20" />
152+
<polyline points="20 10 14 10 14 4" />
153+
<line x1="14" y1="10" x2="21" y2="3" />
154+
<line x1="3" y1="21" x2="10" y2="14" />
155+
</svg>
156+
) : (
157+
<svg
158+
width="14"
159+
height="14"
160+
viewBox="0 0 24 24"
161+
fill="none"
162+
stroke="currentColor"
163+
strokeWidth="2"
164+
strokeLinecap="round"
165+
strokeLinejoin="round"
166+
>
167+
<polyline points="15 3 21 3 21 9" />
168+
<polyline points="9 21 3 21 3 15" />
169+
<line x1="21" y1="3" x2="14" y2="10" />
170+
<line x1="3" y1="21" x2="10" y2="14" />
171+
</svg>
172+
)}
173+
</button>
93174
</div>
94175
)}
95176

@@ -111,25 +192,27 @@ export function PlaygroundCell({
111192
}}
112193
>
113194
<SandpackLayout
195+
className={isFullscreen ? "flex-1" : ""}
114196
style={{
115197
border: "none",
116198
borderRadius: 0,
117199
background: "transparent",
200+
...(isFullscreen ? { height: "100%" } : {}),
118201
}}
119202
>
120203
<SandpackCodeEditor
121204
showLineNumbers
122205
showTabs
123-
style={{ minHeight: 280 }}
206+
style={{ minHeight: isFullscreen ? undefined : 280 }}
124207
/>
125208
<SandpackPreview
126209
showOpenInCodeSandbox={false}
127210
showRefreshButton
128-
style={{ minHeight: 280 }}
211+
style={{ minHeight: isFullscreen ? undefined : 280 }}
129212
/>
130213
</SandpackLayout>
131214
<div
132-
className="flex items-center gap-2 px-4 py-2 border-t"
215+
className="flex items-center gap-2 px-4 py-2 border-t shrink-0"
133216
style={{ borderColor: "var(--color-border-glass)" }}
134217
>
135218
<ResetButton />

0 commit comments

Comments
 (0)