Skip to content

Commit 9a8d8d9

Browse files
committed
feat: add interactive Desk component with MacBook and Xiaomi monitor featuring desktop applications and a YouTube music player.
1 parent a15de5e commit 9a8d8d9

1 file changed

Lines changed: 294 additions & 55 deletions

File tree

src/components/canvas/Desk.tsx

Lines changed: 294 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { useGLTF, Text, Image } from '@react-three/drei'
1+
import { useGLTF, Text, Image, Html } from '@react-three/drei'
22
import { useLoader, useFrame } from '@react-three/fiber'
33
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
44
import * as THREE from 'three'
55
import { useMemo, useState } from 'react'
66
import { useOverlay } from '../../context/OverlayContext'
7+
import { useMusic } from '../../context/MusicContext'
78
import { DEFAULT_FONT } from '../../constants/fonts'
89

910
// MacBook component with Gemini app
@@ -96,36 +97,155 @@ const XiaomiDesktopIcon = ({
9697
function XiaomiMonitor({ position, rotation, scale }: { position: [number, number, number], rotation: [number, number, number], scale: number }) {
9798
const { scene } = useGLTF('/models/xiaomi_4k_27_monitor/scene.gltf')
9899
const { setOverlay } = useOverlay()
100+
const { musicActive, setMusicActive } = useMusic()
99101

100102
return (
101103
<group position={position} rotation={rotation} scale={scale}>
102104
<primitive object={scene.clone()} scale={3.015} position={[0, 0.3, 0]} />
103105
{/* Screen Content - Desktop Icons */}
104106
<group position={[0, 0.3, 0.08]}>
105-
<XiaomiDesktopIcon
106-
position={[-0.5, 0.15, 0]}
107-
iconUrl="/textures/logos/githublogo.png"
108-
label="GitHub"
109-
onClick={() => window.open('https://github.com/sametuca', '_blank')}
110-
/>
111-
<XiaomiDesktopIcon
112-
position={[-0.15, 0.15, 0]}
113-
iconUrl="/textures/logos/mediumlogo.png"
114-
label="Medium"
115-
onClick={() => window.open('https://medium.com/@sametuca', '_blank')}
116-
/>
117-
<XiaomiDesktopIcon
118-
position={[0.2, 0.15, 0]}
119-
iconUrl="/textures/logos/certificatelogo.png"
120-
label="Certificate"
121-
onClick={() => setOverlay('certificates')}
122-
/>
123-
<XiaomiDesktopIcon
124-
position={[0.55, 0.15, 0]}
125-
iconUrl="/textures/logos/contactlogo.png"
126-
label="About"
127-
onClick={() => setOverlay('contact')}
128-
/>
107+
{/* Black Background */}
108+
<mesh position={[0, 0, -0.01]}>
109+
<planeGeometry args={[1.4, 0.8]} />
110+
<meshBasicMaterial color="#000000" />
111+
</mesh>
112+
113+
{/* VMware Window Title Bar */}
114+
<mesh position={[0, 0.37, 0]}>
115+
<planeGeometry args={[1.4, 0.05]} />
116+
<meshBasicMaterial color="#2d2d2d" />
117+
</mesh>
118+
119+
{/* VMware Icon */}
120+
<mesh position={[-0.65, 0.37, 0.001]}>
121+
<planeGeometry args={[0.025, 0.025]} />
122+
<meshBasicMaterial color="#00a8e1" />
123+
</mesh>
124+
125+
{/* VMware Title Text */}
126+
<Text
127+
position={[-0.35, 0.37, 0.001]}
128+
fontSize={0.02}
129+
color="#ffffff"
130+
anchorX="center"
131+
font={DEFAULT_FONT}
132+
>
133+
VMware Workstation Pro
134+
</Text>
135+
136+
{/* Window Control Buttons */}
137+
<group position={[0.62, 0.37, 0.001]}>
138+
{/* Minimize */}
139+
<mesh position={[-0.06, 0, 0]}>
140+
<planeGeometry args={[0.025, 0.025]} />
141+
<meshBasicMaterial color="#404040" />
142+
</mesh>
143+
<Text position={[-0.06, -0.003, 0.001]} fontSize={0.015} color="#ffffff">_</Text>
144+
145+
{/* Maximize */}
146+
<mesh position={[-0.03, 0, 0]}>
147+
<planeGeometry args={[0.025, 0.025]} />
148+
<meshBasicMaterial color="#404040" />
149+
</mesh>
150+
<Text position={[-0.03, 0, 0.001]} fontSize={0.015} color="#ffffff"></Text>
151+
152+
{/* Close */}
153+
<mesh position={[0, 0, 0]}>
154+
<planeGeometry args={[0.025, 0.025]} />
155+
<meshBasicMaterial color="#e81123" />
156+
</mesh>
157+
<Text position={[0, 0, 0.001]} fontSize={0.015} color="#ffffff"></Text>
158+
</group>
159+
160+
{/* YouTube Music Player - Show when music is active */}
161+
{musicActive && (
162+
<>
163+
<Html position={[0, 0, 0.01]} transform occlude distanceFactor={0.7}>
164+
<div style={{ width: '560px', height: '314px', background: '#000', overflow: 'hidden', pointerEvents: 'auto' }}>
165+
<iframe
166+
width="560"
167+
height="314"
168+
src="https://www.youtube.com/embed/YVowLNuV4Zk?autoplay=1&start=214"
169+
title="YouTube Music Player"
170+
frameBorder="0"
171+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
172+
allowFullScreen
173+
/>
174+
</div>
175+
</Html>
176+
{/* Close Button */}
177+
<mesh position={[0.65, 0.32, 0.02]} onClick={(e) => { e.stopPropagation(); setMusicActive(false) }}>
178+
<planeGeometry args={[0.06, 0.06]} />
179+
<meshBasicMaterial color="#ff0000" />
180+
</mesh>
181+
<Text position={[0.65, 0.32, 0.021]} fontSize={0.04} color="white" anchorX="center" font={DEFAULT_FONT}>
182+
183+
</Text>
184+
</>
185+
)}
186+
187+
{/* Desktop Icons - Only show when music is not active */}
188+
{!musicActive && (
189+
<>
190+
<XiaomiDesktopIcon
191+
position={[-0.5, 0.15, 0]}
192+
iconUrl="/textures/logos/githublogo.png"
193+
label="GitHub"
194+
onClick={() => window.open('https://github.com/sametuca', '_blank')}
195+
/>
196+
<XiaomiDesktopIcon
197+
position={[-0.15, 0.15, 0]}
198+
iconUrl="/textures/logos/mediumlogo.png"
199+
label="Medium"
200+
onClick={() => window.open('https://medium.com/@sametuca', '_blank')}
201+
/>
202+
<XiaomiDesktopIcon
203+
position={[0.2, 0.15, 0]}
204+
iconUrl="/textures/logos/certificatelogo.png"
205+
label="Certificate"
206+
onClick={() => setOverlay('certificates')}
207+
/>
208+
<XiaomiDesktopIcon
209+
position={[0.55, 0.15, 0]}
210+
iconUrl="/textures/logos/contactlogo.png"
211+
label="About"
212+
onClick={() => setOverlay('contact')}
213+
/>
214+
</>
215+
)}
216+
217+
{/* Taskbar */}
218+
<mesh position={[0, -0.36, 0]}>
219+
<planeGeometry args={[1.4, 0.06]} />
220+
<meshBasicMaterial color="#1a1a1a" />
221+
</mesh>
222+
223+
{/* Start Button - Opens YouTube Music */}
224+
<group position={[-0.63, -0.36, 0.001]} onClick={(e) => { e.stopPropagation(); setMusicActive(true) }}>
225+
<mesh>
226+
<planeGeometry args={[0.08, 0.04]} />
227+
<meshBasicMaterial color="#0078d4" />
228+
</mesh>
229+
<Text
230+
fontSize={0.025}
231+
color="#ffffff"
232+
anchorX="center"
233+
font={DEFAULT_FONT}
234+
>
235+
236+
</Text>
237+
</group>
238+
239+
{/* Clock */}
240+
<Text
241+
position={[0.6, -0.36, 0.001]}
242+
fontSize={0.018}
243+
color="#ffffff"
244+
anchorX="center"
245+
font={DEFAULT_FONT}
246+
>
247+
{new Date().toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' })}
248+
</Text>
129249
</group>
130250
</group>
131251
)
@@ -134,42 +254,161 @@ function XiaomiMonitor({ position, rotation, scale }: { position: [number, numbe
134254
// Xiaomi Code Monitor component - for tech stack icons
135255
function XiaomiCodeMonitor({ position, rotation, scale }: { position: [number, number, number], rotation: [number, number, number], scale: number }) {
136256
const { scene } = useGLTF('/models/xiaomi_4k_27_monitor/scene.gltf')
257+
const { musicActive, setMusicActive } = useMusic()
137258

138259
return (
139260
<group position={position} rotation={rotation} scale={scale}>
140261
<primitive object={scene.clone()} scale={3.015} position={[0, 0.3, 0]} />
141262
{/* Screen Content - Tech Stack Icons */}
142263
<group position={[0, 0.3, 0.08]}>
143-
<XiaomiDesktopIcon
144-
position={[-0.6, 0.2, 0]}
145-
iconUrl="/textures/logos/reactlogo.webp"
146-
label="React"
147-
onClick={() => window.open('https://react.dev', '_blank')}
148-
/>
149-
<XiaomiDesktopIcon
150-
position={[-0.25, 0.2, 0]}
151-
iconUrl="/textures/logos/typescriptlogo.png"
152-
label="TypeScript"
153-
onClick={() => window.open('https://typescriptlang.org', '_blank')}
154-
/>
155-
<XiaomiDesktopIcon
156-
position={[0.1, 0.2, 0]}
157-
iconUrl="/textures/logos/nodejslogo.png"
158-
label="Node.js"
159-
onClick={() => window.open('https://nodejs.org', '_blank')}
160-
/>
161-
<XiaomiDesktopIcon
162-
position={[0.45, 0.2, 0]}
163-
iconUrl="/textures/logos/threejslogo.png"
164-
label="Three.js"
165-
onClick={() => window.open('https://threejs.org', '_blank')}
166-
/>
167-
<XiaomiDesktopIcon
168-
position={[-0.6, -0.1, 0]}
169-
iconUrl="/textures/logos/nextjslogo.webp"
170-
label="Next.js"
171-
onClick={() => window.open('https://nextjs.org', '_blank')}
172-
/>
264+
{/* Black Background */}
265+
<mesh position={[0, 0, -0.01]}>
266+
<planeGeometry args={[1.4, 0.8]} />
267+
<meshBasicMaterial color="#000000" />
268+
</mesh>
269+
270+
{/* VMware Window Title Bar */}
271+
<mesh position={[0, 0.37, 0]}>
272+
<planeGeometry args={[1.4, 0.05]} />
273+
<meshBasicMaterial color="#2d2d2d" />
274+
</mesh>
275+
276+
{/* VMware Icon */}
277+
<mesh position={[-0.65, 0.37, 0.001]}>
278+
<planeGeometry args={[0.025, 0.025]} />
279+
<meshBasicMaterial color="#00a8e1" />
280+
</mesh>
281+
282+
{/* VMware Title Text */}
283+
<Text
284+
position={[-0.35, 0.37, 0.001]}
285+
fontSize={0.02}
286+
color="#ffffff"
287+
anchorX="center"
288+
font={DEFAULT_FONT}
289+
>
290+
VMware Workstation Pro
291+
</Text>
292+
293+
{/* Window Control Buttons */}
294+
<group position={[0.62, 0.37, 0.001]}>
295+
{/* Minimize */}
296+
<mesh position={[-0.06, 0, 0]}>
297+
<planeGeometry args={[0.025, 0.025]} />
298+
<meshBasicMaterial color="#404040" />
299+
</mesh>
300+
<Text position={[-0.06, -0.003, 0.001]} fontSize={0.015} color="#ffffff">_</Text>
301+
302+
{/* Maximize */}
303+
<mesh position={[-0.03, 0, 0]}>
304+
<planeGeometry args={[0.025, 0.025]} />
305+
<meshBasicMaterial color="#404040" />
306+
</mesh>
307+
<Text position={[-0.03, 0, 0.001]} fontSize={0.015} color="#ffffff"></Text>
308+
309+
{/* Close */}
310+
<mesh position={[0, 0, 0]}>
311+
<planeGeometry args={[0.025, 0.025]} />
312+
<meshBasicMaterial color="#e81123" />
313+
</mesh>
314+
<Text position={[0, 0, 0.001]} fontSize={0.015} color="#ffffff"></Text>
315+
</group>
316+
317+
{/* YouTube Music Player - Show when music is active */}
318+
{musicActive && (
319+
<>
320+
<Html position={[0, 0, 0.01]} transform occlude distanceFactor={0.7}>
321+
<div style={{ width: '560px', height: '314px', background: '#000', overflow: 'hidden', pointerEvents: 'auto' }}>
322+
<iframe
323+
width="560"
324+
height="314"
325+
src="https://www.youtube.com/embed/YVowLNuV4Zk?autoplay=1&start=214"
326+
title="YouTube Music Player"
327+
frameBorder="0"
328+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
329+
allowFullScreen
330+
/>
331+
</div>
332+
</Html>
333+
{/* Close Button */}
334+
<mesh position={[0.65, 0.32, 0.02]} onClick={(e) => { e.stopPropagation(); setMusicActive(false) }}>
335+
<planeGeometry args={[0.06, 0.06]} />
336+
<meshBasicMaterial color="#ff0000" />
337+
</mesh>
338+
<Text position={[0.65, 0.32, 0.021]} fontSize={0.04} color="white" anchorX="center" font={DEFAULT_FONT}>
339+
340+
</Text>
341+
</>
342+
)}
343+
344+
{/* Desktop Icons - Only show when music is not active */}
345+
{!musicActive && (
346+
<>
347+
<XiaomiDesktopIcon
348+
position={[-0.6, 0.2, 0]}
349+
iconUrl="/textures/logos/reactlogo.webp"
350+
label="React"
351+
onClick={() => window.open('https://react.dev', '_blank')}
352+
/>
353+
<XiaomiDesktopIcon
354+
position={[-0.25, 0.2, 0]}
355+
iconUrl="/textures/logos/typescriptlogo.png"
356+
label="TypeScript"
357+
onClick={() => window.open('https://typescriptlang.org', '_blank')}
358+
/>
359+
<XiaomiDesktopIcon
360+
position={[0.1, 0.2, 0]}
361+
iconUrl="/textures/logos/nodejslogo.png"
362+
label="Node.js"
363+
onClick={() => window.open('https://nodejs.org', '_blank')}
364+
/>
365+
<XiaomiDesktopIcon
366+
position={[0.45, 0.2, 0]}
367+
iconUrl="/textures/logos/threejslogo.png"
368+
label="Three.js"
369+
onClick={() => window.open('https://threejs.org', '_blank')}
370+
/>
371+
<XiaomiDesktopIcon
372+
position={[-0.6, -0.1, 0]}
373+
iconUrl="/textures/logos/nextjslogo.webp"
374+
label="Next.js"
375+
onClick={() => window.open('https://nextjs.org', '_blank')}
376+
/>
377+
</>
378+
)}
379+
380+
{/* Taskbar */}
381+
<mesh position={[0, -0.36, 0]}>
382+
<planeGeometry args={[1.4, 0.06]} />
383+
<meshBasicMaterial color="#1a1a1a" />
384+
</mesh>
385+
386+
{/* Start Button - Opens YouTube Music */}
387+
<group position={[-0.63, -0.36, 0.001]} onClick={(e) => { e.stopPropagation(); setMusicActive(true) }}>
388+
<mesh>
389+
<planeGeometry args={[0.08, 0.04]} />
390+
<meshBasicMaterial color="#0078d4" />
391+
</mesh>
392+
<Text
393+
fontSize={0.025}
394+
color="#ffffff"
395+
anchorX="center"
396+
font={DEFAULT_FONT}
397+
>
398+
399+
</Text>
400+
</group>
401+
402+
{/* Clock */}
403+
<Text
404+
position={[0.6, -0.36, 0.001]}
405+
fontSize={0.018}
406+
color="#ffffff"
407+
anchorX="center"
408+
font={DEFAULT_FONT}
409+
>
410+
{new Date().toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' })}
411+
</Text>
173412
</group>
174413
</group>
175414
)

0 commit comments

Comments
 (0)