Skip to content

Commit 735028c

Browse files
committed
fix theme and fullscreen
1 parent 1372c4a commit 735028c

9 files changed

Lines changed: 78 additions & 34 deletions

File tree

apps/web/src/App.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export default function App() {
167167

168168
{!isClosed && (
169169
<div
170-
className={appStyles.window}
170+
className={isMaximized ? appStyles.windowFullscreen : appStyles.window}
171171
ref={frameRef}
172172
onClick={handleWindowAction}
173173
>
@@ -182,7 +182,7 @@ export default function App() {
182182
onSyntaxThemeChange={setSyntaxThemeId}
183183
/>
184184

185-
<div className="relative">
185+
<div className={appStyles.windowBody}>
186186
<div className={appStyles.editorColumn}>
187187
<div className={appStyles.editorInner}>
188188
<EditorSurface
@@ -224,6 +224,7 @@ export default function App() {
224224
wpmDisplay={wpmDisplay}
225225
elapsedLabel={elapsedLabel}
226226
completionPercent={completionPercent}
227+
isFullscreen={isMaximized}
227228
/>
228229
</div>
229230
</div>

apps/web/src/app/App.styles.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ export const appStyles = {
66
main: "relative w-full max-w-5xl",
77
glow: "pointer-events-none absolute inset-0 -z-10 rounded-[32px] bg-gradient-radial from-blue-500/5 via-transparent to-transparent blur-3xl",
88
window:
9-
`relative z-10 overflow-hidden rounded-2xl ${uiTokens.glassSurface} shadow-[0_40px_120px_rgba(0,0,0,0.45)] will-change-transform`,
10-
editorColumn: "flex",
11-
editorInner: "flex-1",
9+
`relative z-10 flex w-full min-h-0 flex-col overflow-hidden rounded-2xl ${uiTokens.glassSurface} shadow-[0_40px_120px_rgba(0,0,0,0.45)] will-change-transform`,
10+
windowFullscreen:
11+
`fixed inset-0 z-30 m-0 flex h-screen w-screen min-h-0 flex-col overflow-hidden rounded-none shadow-none ${uiTokens.glassSurface} text-white`,
12+
windowBody: "relative flex min-h-0 flex-1 flex-col",
13+
editorColumn: "flex flex-1 min-h-0",
14+
editorInner: "flex-1 min-h-0",
1215
explorerPane: (open: boolean) =>
1316
`absolute inset-y-0 right-0 transform transition-transform duration-300 ease-out will-change-transform backdrop-blur-2xl backdrop-saturate-150 ${
1417
open ? "translate-x-0 pointer-events-auto" : "translate-x-full pointer-events-none"

apps/web/src/features/explorer/components/ExplorerSidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function ExplorerSidebar({ prompts, selectedLanguage, selectedPromptId, o
3333

3434
return (
3535
<aside className={explorerStyles.root}>
36-
<div className={explorerStyles.header}>Explorer</div>
36+
<div className={explorerStyles.header}>Main</div>
3737
<ul className={explorerStyles.list}>
3838
{grouped.map(([lang, items]) => {
3939
const isOpen = expanded[lang] ?? lang === selectedLanguage;

apps/web/src/features/stats/components/BottomStatsPanel.styles.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { uiTokens } from "../../../shared/styles/tokens";
22

33
export const bottomStatsStyles = {
4-
root: "mt-2",
4+
root: (fullscreen: boolean) =>
5+
`${fullscreen ? "mt-auto" : "mt-2"} w-full`,
56
container:
6-
"overflow-hidden border-t border-white/10 bg-[#161616]/90 backdrop-blur-xl shadow-[0_-8px_30px_rgba(0,0,0,0.45)]",
7+
"overflow-hidden border-t border-white/10 bg-[#161616]/90 backdrop-blur-xl shadow-[0_-8px_30px_rgba(0,0,0,0.45)] shrink-0",
78
collapsedButton:
89
"flex w-full items-center justify-between px-5 py-2.5 text-left transition-colors hover:bg-white/5",
910
collapsedMetrics: "flex items-center gap-6",

apps/web/src/features/stats/components/BottomStatsPanel.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface BottomStatsPanelProps {
1414
wpmDisplay: string;
1515
elapsedLabel: string;
1616
completionPercent: string;
17+
isFullscreen?: boolean;
1718
}
1819

1920
function CollapsedMetric({ icon, value, label }: { icon: string; value: string; label: string }) {
@@ -36,6 +37,7 @@ export function BottomStatsPanel({
3637
wpmDisplay,
3738
elapsedLabel,
3839
completionPercent,
40+
isFullscreen = false,
3941
}: BottomStatsPanelProps) {
4042
const [open, setOpen] = useState(false);
4143
const [tab, setTab] = useState<"stats" | "history">("stats");
@@ -54,7 +56,7 @@ export function BottomStatsPanel({
5456
}), [wpmDisplay, elapsedLabel, completionPercent]);
5557

5658
return (
57-
<div className={bottomStatsStyles.root}>
59+
<div className={bottomStatsStyles.root(isFullscreen)}>
5860
<div className={bottomStatsStyles.container}>
5961
{/* Collapsed bar (metrics + toggle). Hidden when open */}
6062
{!open && (

apps/web/src/shared/components/MenuBar.styles.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { uiTokens } from "../styles/tokens";
22

3+
const toggleIconLineBase =
4+
"absolute left-1/2 -translate-x-1/2 h-[2px] rounded-full bg-current transition-all duration-300 ease-out";
5+
36
export const menuBarStyles = {
4-
root: "grid select-none grid-cols-3 items-center border-b border-white/[0.08] bg-gradient-to-b from-white/[0.02] to-transparent pl-3 pr-6 py-3",
7+
root:
8+
"grid select-none grid-cols-[auto_minmax(0,1fr)_auto] items-center border-b border-white/[0.08] bg-gradient-to-b from-white/[0.02] to-transparent pl-3 pr-2 py-3",
59
trafficCluster: "flex items-center gap-2",
610
closeButton: "group relative h-3.5 w-3.5 rounded-full bg-[#ff5f57] ring-1 ring-black/30",
711
closeGlyph:
@@ -34,16 +38,35 @@ export const menuBarStyles = {
3438
themeSwitcherMenuDot: "h-2.5 w-2.5 rounded-full",
3539
themeSwitcherMenuLabel: "font-medium tracking-[0.04em]",
3640
toggleButton: (isExplorerOpen?: boolean) =>
37-
`rounded-md p-1.5 transition ${
41+
`flex items-center justify-center rounded-md p-1.5 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 ${
42+
isExplorerOpen ? "text-white" : "text-white/80 hover:text-white"
43+
}`,
44+
toggleIcon: "relative h-4 w-4",
45+
toggleIconLineTop: (isExplorerOpen?: boolean) =>
46+
`${toggleIconLineBase} ${
3847
isExplorerOpen
39-
? "bg-white/[0.12] text-white"
40-
: "bg-white/[0.05] text-white/80 hover:bg-white/10 hover:text-white"
48+
? "top-1/2 -translate-y-1/2 origin-center rotate-45 w-3.5"
49+
: "top-0.5 translate-y-0 rotate-0 origin-center w-full"
50+
}`,
51+
toggleIconLineMiddle: (isExplorerOpen?: boolean) =>
52+
`${toggleIconLineBase} top-1/2 -translate-y-1/2 ${
53+
isExplorerOpen
54+
? "origin-center w-3.5 opacity-0"
55+
: "origin-center w-full opacity-100"
56+
}`,
57+
toggleIconLineBottom: (isExplorerOpen?: boolean) =>
58+
`${toggleIconLineBase} ${
59+
isExplorerOpen
60+
? "top-1/2 -translate-y-1/2 origin-center -rotate-45 w-3.5"
61+
: "bottom-0.5 translate-y-0 rotate-0 origin-center w-full"
4162
}`,
42-
toggleIcon: "h-4 w-4",
4363
};
4464

4565
export const menuBarIconStyles = {
46-
zoomButton: "group relative h-3.5 w-3.5 rounded-full bg-[#28c840] ring-1 ring-black/30",
66+
zoomButton: (isMaximized: boolean) =>
67+
`group relative h-3.5 w-3.5 rounded-full ring-1 ring-black/30 ${
68+
isMaximized ? "bg-[#fdbc40]" : "bg-[#28c840]"
69+
}`,
4770
zoomGlyph:
4871
"pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-[11px] leading-none text-black/70 opacity-0 group-hover:opacity-100",
4972
};

apps/web/src/shared/components/MenuBar.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,12 @@ export function MenuBar({
6666
title={isExplorerOpen ? "Hide explorer" : "Show explorer"}
6767
data-no-drag
6868
>
69-
{/* Hamburger icon */}
70-
<svg className={menuBarStyles.toggleIcon} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
71-
<path d="M4 7h16M4 12h16M4 17h16" strokeLinecap="round" />
72-
</svg>
69+
{/* Animated hamburger icon */}
70+
<span aria-hidden="true" className={menuBarStyles.toggleIcon}>
71+
<span className={menuBarStyles.toggleIconLineTop(isExplorerOpen)} />
72+
<span className={menuBarStyles.toggleIconLineMiddle(isExplorerOpen)} />
73+
<span className={menuBarStyles.toggleIconLineBottom(isExplorerOpen)} />
74+
</span>
7375
<span className="sr-only">Toggle explorer</span>
7476
</button>
7577
</div>
@@ -81,8 +83,8 @@ function ZoomButton({ isMaximized }: { isMaximized: boolean }) {
8183
return (
8284
<button
8385
type="button"
84-
aria-label="Zoom"
85-
className={menuBarIconStyles.zoomButton}
86+
aria-label={isMaximized ? "Restore window" : "Enter fullscreen"}
87+
className={menuBarIconStyles.zoomButton(isMaximized)}
8688
data-action="toggle-maximize"
8789
>
8890
{/* Icon appears only on hover like macOS; content reflects maximize state */}

apps/web/src/shared/hooks/useWindowFrame.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export function useWindowFrame() {
1212
const containerInsetsRef = useRef<{ left: number; right: number; top: number; bottom: number }>({ left: 0, right: 0, top: 0, bottom: 0 });
1313

1414
const framePosRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
15+
const previousFramePosRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
1516
const rafRef = useRef<number | null>(null);
1617
const isMaximizedRef = useRef(false);
1718

@@ -26,8 +27,11 @@ export function useWindowFrame() {
2627
const applyTransform = useCallback((p: { x: number; y: number }) => {
2728
const el = frameRef.current;
2829
if (!el) return;
29-
const scale = isMaximizedRef.current ? 1.08 : 1.0;
30-
el.style.transform = `translate(${p.x}px, ${p.y}px) scale(${scale})`;
30+
if (isMaximizedRef.current) {
31+
el.style.transform = "translate(0px, 0px)";
32+
return;
33+
}
34+
el.style.transform = `translate(${p.x}px, ${p.y}px)`;
3135
}, []);
3236

3337
const getBounds = useCallback(() => {
@@ -36,9 +40,8 @@ export function useWindowFrame() {
3640
// Respect actual container padding as the safe space, fallback to 16px
3741
const padX = Math.max(16, Math.min(containerInsetsRef.current.left, containerInsetsRef.current.right));
3842
const padY = Math.max(16, Math.min(containerInsetsRef.current.top, containerInsetsRef.current.bottom));
39-
const scale = isMaximizedRef.current ? 1.08 : 1.0;
40-
const width = (frameSizeRef.current.width || Math.min(cw, 800)) * scale;
41-
const height = (frameSizeRef.current.height || Math.min(ch, 500)) * scale;
43+
const width = frameSizeRef.current.width || Math.min(cw, 800);
44+
const height = frameSizeRef.current.height || Math.min(ch, 500);
4245
const halfW = Math.min(width, cw) / 2;
4346
const halfH = Math.min(height, ch) / 2;
4447
const maxX = Math.max(0, cw / 2 - halfW - padX);
@@ -114,6 +117,9 @@ export function useWindowFrame() {
114117
const onDragPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
115118
if (e.button !== 0) return;
116119
const target = e.target as HTMLElement | null;
120+
if (isMaximizedRef.current) {
121+
return;
122+
}
117123
if (target && target.closest("button, a, [data-action], [data-no-drag]")) {
118124
return;
119125
}
@@ -192,11 +198,19 @@ export function useWindowFrame() {
192198
applyTransform({ x: 0, y: 0 });
193199
setIsClosed(true);
194200
} else if (action === "toggle-maximize") {
195-
isMaximizedRef.current = !isMaximizedRef.current;
196-
setIsMaximized(isMaximizedRef.current);
197-
const clamped = clampToViewport(framePosRef.current.x, framePosRef.current.y);
198-
framePosRef.current = clamped;
199-
applyTransform(clamped);
201+
if (!isMaximizedRef.current) {
202+
previousFramePosRef.current = { ...framePosRef.current };
203+
isMaximizedRef.current = true;
204+
setIsMaximized(true);
205+
framePosRef.current = { x: 0, y: 0 };
206+
applyTransform({ x: 0, y: 0 });
207+
} else {
208+
isMaximizedRef.current = false;
209+
setIsMaximized(false);
210+
const restored = clampToViewport(previousFramePosRef.current.x, previousFramePosRef.current.y);
211+
framePosRef.current = restored;
212+
applyTransform(restored);
213+
}
200214
}
201215
},
202216
[applyTransform, clampToViewport]
@@ -224,4 +238,3 @@ export function useWindowFrame() {
224238
} as const;
225239
}
226240

227-
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
export const uiTokens = {
22
monoFont:
33
"[font-family:'JetBrains_Mono',_'Fira_Code',_'Consolas',_'Monaco',_monospace]",
4-
glassSurface: "border border-white/10 bg-[#1e1e1e]/95 backdrop-blur-xl",
4+
glassSurface: "border border-white/20 bg-[#111111]/95 backdrop-blur-xl",
55
glassCard: "border border-white/10 bg-white/5",
66
pillButton:
77
"inline-flex items-center gap-2 rounded-xl border border-white/15 bg-white/5 px-3 py-1.5 transition hover:border-white/30 hover:text-white",
88
};
9-

0 commit comments

Comments
 (0)