Tina Tarighian commited on
Commit
bb6831d
·
1 Parent(s): 0a001bd
components/CameraSetup.js CHANGED
@@ -35,13 +35,12 @@ const CameraSetup = ({
35
  tracks.forEach(track => track.stop());
36
  }
37
 
38
- // Get camera stream with better constraints
39
  const stream = await navigator.mediaDevices.getUserMedia({
40
  video: {
41
  facingMode: facingMode,
42
- width: { ideal: 1280 },
43
- height: { ideal: 720 },
44
- aspectRatio: { ideal: 4/3 } // Add ideal aspect ratio
45
  },
46
  audio: false
47
  });
@@ -142,22 +141,6 @@ const CameraSetup = ({
142
 
143
  try {
144
  await videoRef.current.play();
145
-
146
- // Explicitly handle metadata loading after camera switch
147
- videoRef.current.onloadedmetadata = () => {
148
- if (!isMounted.current) return;
149
-
150
- const videoWidth = videoRef.current.videoWidth;
151
- const videoHeight = videoRef.current.videoHeight;
152
- const aspectRatio = videoWidth / videoHeight;
153
-
154
- // Update aspect ratio and canvas size
155
- setVideoAspectRatio(aspectRatio);
156
- updateCanvasSize(aspectRatio);
157
-
158
- console.log(`Camera switched with aspect ratio: ${aspectRatio}`);
159
- };
160
-
161
  } catch (playError) {
162
  console.log("Play interrupted during camera switch:", playError);
163
  // Don't treat play interruptions as fatal errors
 
35
  tracks.forEach(track => track.stop());
36
  }
37
 
38
+ // Get camera stream
39
  const stream = await navigator.mediaDevices.getUserMedia({
40
  video: {
41
  facingMode: facingMode,
42
+ width: { ideal: 1920 },
43
+ height: { ideal: 1080 }
 
44
  },
45
  audio: false
46
  });
 
141
 
142
  try {
143
  await videoRef.current.play();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  } catch (playError) {
145
  console.log("Play interrupted during camera switch:", playError);
146
  // Don't treat play interruptions as fatal errors
components/MainContent.js CHANGED
@@ -45,7 +45,7 @@ const MainContent = ({
45
  />
46
  )}
47
 
48
- <div ref={containerRef} className={`relative w-full max-w-4xl canvas-container ${isMobile ? 'mt-0' : 'mt-4'}`}>
49
  {/* Camera Setup */}
50
  <CameraSetup
51
  videoRef={videoRef}
@@ -63,12 +63,7 @@ const MainContent = ({
63
  <canvas
64
  ref={canvasRef}
65
  className="rounded-lg shadow-lg w-full"
66
- style={{
67
- height: `${canvasHeight}px`,
68
- maxHeight: isMobile ? '70vh' : 'none',
69
- objectFit: 'cover',
70
- objectPosition: 'center'
71
- }}
72
  width={canvasWidth}
73
  height={canvasHeight}
74
  />
 
45
  />
46
  )}
47
 
48
+ <div ref={containerRef} className="relative w-full max-w-4xl canvas-container">
49
  {/* Camera Setup */}
50
  <CameraSetup
51
  videoRef={videoRef}
 
63
  <canvas
64
  ref={canvasRef}
65
  className="rounded-lg shadow-lg w-full"
66
+ style={{ height: `${canvasHeight}px` }}
 
 
 
 
 
67
  width={canvasWidth}
68
  height={canvasHeight}
69
  />
components/ThoughtBubble.js CHANGED
@@ -127,14 +127,13 @@ const ThoughtBubble = ({
127
  };
128
  }
129
 
130
- // Reduce the offset to position bubble closer to hand
131
- const offset = isMobile ? 8 : 12; // Reduced from 12/20
132
 
133
  if (isLeftHand) {
134
  // For left hand, position to the right of thumb
135
  return {
136
  position: 'absolute',
137
- top: `${thumbPosition.y - (isMobile ? 10 : 15)}px`, // Reduced vertical offset
138
  left: `${thumbPosition.x + offset}px`,
139
  width: getBubbleWidth(),
140
  maxWidth: isThinking ? 'none' : `${canvasWidth - thumbPosition.x - (offset * 2)}px` // Prevent overflow
@@ -143,7 +142,7 @@ const ThoughtBubble = ({
143
  // For right hand, position to the left of thumb
144
  return {
145
  position: 'absolute',
146
- top: `${thumbPosition.y - (isMobile ? 10 : 15)}px`, // Reduced vertical offset
147
  right: `${canvasWidth - thumbPosition.x + offset}px`,
148
  width: getBubbleWidth(),
149
  maxWidth: isThinking ? 'none' : `${thumbPosition.x - (offset * 2)}px` // Prevent overflow
 
127
  };
128
  }
129
 
130
+ const offset = isMobile ? 12 : 20; // Space between thumb and bubble
 
131
 
132
  if (isLeftHand) {
133
  // For left hand, position to the right of thumb
134
  return {
135
  position: 'absolute',
136
+ top: `${thumbPosition.y - (isMobile ? 20 : 30)}px`,
137
  left: `${thumbPosition.x + offset}px`,
138
  width: getBubbleWidth(),
139
  maxWidth: isThinking ? 'none' : `${canvasWidth - thumbPosition.x - (offset * 2)}px` // Prevent overflow
 
142
  // For right hand, position to the left of thumb
143
  return {
144
  position: 'absolute',
145
+ top: `${thumbPosition.y - (isMobile ? 20 : 30)}px`,
146
  right: `${canvasWidth - thumbPosition.x + offset}px`,
147
  width: getBubbleWidth(),
148
  maxWidth: isThinking ? 'none' : `${thumbPosition.x - (offset * 2)}px` // Prevent overflow
hooks/useDeviceAndCanvas.js CHANGED
@@ -45,14 +45,7 @@ const useDeviceAndCanvas = () => {
45
  const containerWidth = document.querySelector('.canvas-container')?.clientWidth || window.innerWidth;
46
  // Set maximum width for the canvas - increased for desktop
47
  const maxWidth = Math.min(containerWidth, isMobile ? 640 : 960);
48
-
49
- // Calculate height based on aspect ratio, but reduce height on mobile
50
- let height = maxWidth / aspectRatio;
51
-
52
- // For mobile, reduce the height by 25% to make it more compact
53
- if (isMobile) {
54
- height = height * 0.75;
55
- }
56
 
57
  setCanvasWidth(maxWidth);
58
  setCanvasHeight(height);
 
45
  const containerWidth = document.querySelector('.canvas-container')?.clientWidth || window.innerWidth;
46
  // Set maximum width for the canvas - increased for desktop
47
  const maxWidth = Math.min(containerWidth, isMobile ? 640 : 960);
48
+ const height = maxWidth / aspectRatio;
 
 
 
 
 
 
 
49
 
50
  setCanvasWidth(maxWidth);
51
  setCanvasHeight(height);
pages/index.js CHANGED
@@ -11,9 +11,9 @@ const inter = Inter({ subsets: ['latin'] });
11
 
12
  const Header = () => {
13
  return (
14
- <div className="sticky top-0 left-0 right-0 w-full bg-white p-2 z-50 shadow-sm">
15
  <div className="w-full flex justify-between items-center text-base max-w-7xl mx-auto">
16
- <div className="text-gray-500 text-sm md:text-base">
17
  <span className="text-black font-bold text-lg mr-2">HandSpew</span>
18
  Built with <a
19
  href="https://ai.google.dev"
@@ -42,12 +42,12 @@ export default function Home() {
42
  <Head>
43
  <title>HandSpew</title>
44
  <meta name="description" content="Generate thoughts based on hand gestures using MediaPipe and Gemini" />
45
- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
46
  <link rel="icon" href="/favicon.ico" />
47
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" />
48
  </Head>
49
  <Header />
50
- <main className="flex flex-col items-center p-2 md:p-4 bg-white font-['Google_Sans',sans-serif] pt-2 md:pt-4 overflow-y-auto">
51
  <HandDetector />
52
  </main>
53
  </>
 
11
 
12
  const Header = () => {
13
  return (
14
+ <div className="fixed top-0 left-0 right-0 w-full bg-white p-4 z-50 shadow-sm">
15
  <div className="w-full flex justify-between items-center text-base max-w-7xl mx-auto">
16
+ <div className="text-gray-500">
17
  <span className="text-black font-bold text-lg mr-2">HandSpew</span>
18
  Built with <a
19
  href="https://ai.google.dev"
 
42
  <Head>
43
  <title>HandSpew</title>
44
  <meta name="description" content="Generate thoughts based on hand gestures using MediaPipe and Gemini" />
45
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
46
  <link rel="icon" href="/favicon.ico" />
47
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" />
48
  </Head>
49
  <Header />
50
+ <main className="flex min-h-screen flex-col items-center justify-center p-4 bg-white font-['Google_Sans',sans-serif] pt-20">
51
  <HandDetector />
52
  </main>
53
  </>
styles/globals.css CHANGED
@@ -16,24 +16,10 @@
16
  }
17
  }
18
 
19
- html, body {
20
- height: 100%;
21
- overflow-x: hidden;
22
- position: relative;
23
- }
24
-
25
  body {
26
  color: var(--foreground);
27
  background: var(--background);
28
  font-family: 'Google Sans', Arial, Helvetica, sans-serif;
29
- -webkit-overflow-scrolling: touch; /* Enables momentum scrolling on iOS */
30
- }
31
-
32
- /* Fix for iOS Safari 100vh issue */
33
- @supports (-webkit-touch-callout: none) {
34
- .min-h-screen {
35
- height: -webkit-fill-available;
36
- }
37
  }
38
 
39
  /* Minimal thought bubble styling */
@@ -70,23 +56,6 @@ body {
70
  }
71
  }
72
 
73
- /* Canvas container styling */
74
- .canvas-container {
75
- position: relative;
76
- width: 100%;
77
- max-width: 100%;
78
- overflow: hidden;
79
- border-radius: 12px; /* Add rounded corners */
80
- }
81
-
82
- .canvas-container canvas {
83
- display: block;
84
- width: 100%;
85
- height: auto;
86
- object-fit: cover; /* Changed from contain to cover */
87
- border-radius: 12px; /* Match container */
88
- }
89
-
90
  /* Mobile-specific styles */
91
  @media (max-width: 767px) {
92
  .thought-bubble {
@@ -97,20 +66,4 @@ body {
97
  font-size: 12px;
98
  line-height: 1.3;
99
  }
100
-
101
- /* Compact layout for mobile */
102
- .canvas-container {
103
- margin-top: 0;
104
- }
105
-
106
- /* Reduce spacing around elements on mobile */
107
- .w-full {
108
- margin-bottom: 0.5rem;
109
- }
110
-
111
- /* Make header more compact on mobile */
112
- .sticky {
113
- padding-top: 0.5rem;
114
- padding-bottom: 0.5rem;
115
- }
116
  }
 
16
  }
17
  }
18
 
 
 
 
 
 
 
19
  body {
20
  color: var(--foreground);
21
  background: var(--background);
22
  font-family: 'Google Sans', Arial, Helvetica, sans-serif;
 
 
 
 
 
 
 
 
23
  }
24
 
25
  /* Minimal thought bubble styling */
 
56
  }
57
  }
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  /* Mobile-specific styles */
60
  @media (max-width: 767px) {
61
  .thought-bubble {
 
66
  font-size: 12px;
67
  line-height: 1.3;
68
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  }