Lintottq commited on
Commit
cae2aeb
·
verified ·
1 Parent(s): 3b8c389

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +556 -419
index.html CHANGED
@@ -1,451 +1,588 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
- <title>Fitbit x Claude Voice Bridge</title>
7
- <!-- FontAwesome for Icons -->
8
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
-
10
- <style>
11
- :root {
12
- --ios-bg: #F2F2F7;
13
- --ios-card: #FFFFFF;
14
- --ios-text: #1C1C1E;
15
- --ios-gray: #8E8E93;
16
- --fitbit-green: #00B0B9;
17
- --claude-purple: #D97757; /* Anthropic orange-ish */
18
- --claude-bg: #F9F7F3;
19
- --accent-blue: #007AFF;
20
- --shadow: 0 4px 20px rgba(0,0,0,0.08);
21
- --font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
22
- }
23
-
24
- * {
25
- box-sizing: box-sizing;
26
- margin: 0;
27
- padding: 0;
28
- -webkit-tap-highlight-color: transparent;
29
- }
30
-
31
- body {
32
- background-color: #e0e0e0;
33
- font-family: var(--font-stack);
34
- display: flex;
35
- justify-content: center;
36
- align-items: center;
37
- min-height: 100vh;
38
- overflow: hidden;
39
- }
40
-
41
- /* --- Phone Container --- */
42
- .iphone-frame {
43
- width: 375px;
44
- height: 812px;
45
- background: var(--ios-bg);
46
- border-radius: 40px;
47
- box-shadow: 0 20px 50px rgba(0,0,0,0.2);
48
- position: relative;
49
- overflow: hidden;
50
- display: flex;
51
- flex-direction: column;
52
- border: 8px solid #333;
53
- }
54
-
55
- /* --- Header / Status Bar --- */
56
- .status-bar {
57
- height: 44px;
58
- display: flex;
59
- justify-content: space-between;
60
- align-items: center;
61
- padding: 0 20px;
62
- font-size: 14px;
63
- font-weight: 600;
64
- color: var(--ios-text);
65
- background: rgba(255, 255, 255, 0.8);
66
- backdrop-filter: blur(10px);
67
- z-index: 10;
68
- }
69
-
70
- .app-header {
71
- padding: 10px 20px;
72
- background: rgba(255, 255, 255, 0.9);
73
- backdrop-filter: blur(20px);
74
- border-bottom: 1px solid rgba(0,0,0,0.05);
75
- display: flex;
76
- justify-content: space-between;
77
- align-items: center;
78
- z-index: 9;
79
- }
80
-
81
- .app-title {
82
- font-weight: 700;
83
- font-size: 18px;
84
- color: var(--ios-text);
85
- }
86
-
87
- .connection-status {
88
- display: flex;
89
- gap: 8px;
90
- font-size: 12px;
91
- color: var(--ios-gray);
92
- }
93
-
94
- .status-dot {
95
- width: 8px;
96
- height: 8px;
97
- border-radius: 50%;
98
- background-color: var(--fitbit-green);
99
- box-shadow: 0 0 4px var(--fitbit-green);
100
- animation: pulse 2s infinite;
101
- }
102
-
103
- /* --- Main Content Area --- */
104
- .content-area {
105
- flex: 1;
106
- padding: 20px;
107
- overflow-y: auto;
108
- display: flex;
109
- flex-direction: column;
110
- gap: 15px;
111
- }
112
-
113
- /* Fitbit Data Card */
114
- .fitbit-context {
115
- background: linear-gradient(135deg, #00B0B9, #008a91);
116
- color: white;
117
- padding: 15px;
118
- border-radius: 16px;
119
- box-shadow: var(--shadow);
120
- display: flex;
121
- justify-content: space-between;
122
- align-items: center;
123
- }
124
-
125
- .fitbit-label {
126
- font-size: 12px;
127
- opacity: 0.8;
128
- text-transform: uppercase;
129
- letter-spacing: 1px;
130
- }
131
-
132
- .fitbit-data {
133
- font-size: 20px;
134
- font-weight: 700;
135
- }
136
-
137
- .heart-icon {
138
- font-size: 16px;
139
- animation: heartbeat 1.2s infinite;
140
- }
141
-
142
- /* Claude Response Card */
143
- .claude-card {
144
- background: var(--claude-bg);
145
- border: 1px solid rgba(0,0,0,0.05);
146
- padding: 15px;
147
- border-radius: 16px;
148
- font-size: 14px;
149
- line-height: 1.5;
150
- color: #333;
151
- box-shadow: var(--shadow);
152
- position: relative;
153
- display: none; /* Hidden by default */
154
- }
155
-
156
- .claude-card.active {
157
- display: block;
158
- animation: slideUp 0.4s ease-out;
159
- }
160
-
161
- .claude-header {
162
- font-size: 10px;
163
- color: var(--claude-purple);
164
- font-weight: 700;
165
- text-transform: uppercase;
166
- margin-bottom: 5px;
167
- display: flex;
168
- align-items: center;
169
- gap: 5px;
170
- }
171
-
172
- /* --- Voice Interaction Zone --- */
173
- .voice-zone {
174
- margin-top: auto;
175
- margin-bottom: 20px;
176
- display: flex;
177
- flex-direction: column;
178
- align-items: center;
179
- justify-content: center;
180
- height: 180px;
181
- }
182
-
183
- .mic-button {
184
- width: 80px;
185
- height: 80px;
186
- background: white;
187
- border-radius: 50%;
188
- display: flex;
189
- justify-content: center;
190
- align-items: center;
191
- box-shadow: 0 10px 30px rgba(0,0,0,0.1);
192
- cursor: pointer;
193
- transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
194
- position: relative;
195
- z-index: 2;
196
- }
197
 
198
- .mic-button i {
199
- font-size: 24px;
200
- color: var(--ios-text);
201
- transition: color 0.3s;
202
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
- .mic-button.listening {
205
- background: var(--accent-blue);
206
- transform: scale(1.1);
207
- }
208
 
209
- .mic-button.listening i {
210
- color: white;
211
- }
 
 
 
 
 
 
 
212
 
213
- /* Ripple Effect */
214
- .ripple {
215
- position: absolute;
216
- width: 80px;
217
- height: 80px;
218
- border-radius: 50%;
219
- border: 2px solid var(--accent-blue);
220
- opacity: 0;
221
- z-index: 1;
222
- }
 
 
 
 
223
 
224
- .mic-button.listening .ripple {
225
- animation: rippleAnim 1.5s infinite;
226
- }
227
 
228
- .voice-status-text {
229
- margin-top: 15px;
230
- font-size: 14px;
231
- color: var(--ios-gray);
232
- font-weight: 500;
233
- height: 20px;
234
- }
 
235
 
236
- /* --- Footer --- */
237
- .footer-link {
238
- position: absolute;
239
- bottom: 10px;
240
- width: 100%;
241
- text-align: center;
242
- font-size: 10px;
243
- color: var(--ios-gray);
244
- opacity: 0.6;
245
- }
246
-
247
- .footer-link a {
248
- color: var(--ios-text);
249
- text-decoration: none;
250
- font-weight: 600;
251
- }
252
 
253
- /* --- Animations --- */
254
- @keyframes pulse {
255
- 0% { opacity: 0.5; }
256
- 50% { opacity: 1; }
257
- 100% { opacity: 0.5; }
258
- }
259
 
260
- @keyframes heartbeat {
261
- 0% { transform: scale(1); }
262
- 14% { transform: scale(1.3); }
263
- 28% { transform: scale(1); }
264
- 42% { transform: scale(1.3); }
265
- 70% { transform: scale(1); }
266
- }
267
 
268
- @keyframes rippleAnim {
269
- 0% { width: 80px; height: 80px; opacity: 1; }
270
- 100% { width: 200px; height: 200px; opacity: 0; }
271
- }
272
 
273
- @keyframes slideUp {
274
- from { transform: translateY(20px); opacity: 0; }
275
- to { transform: translateY(0); opacity: 1; }
276
- }
 
277
 
278
- /* --- Loading Bar --- */
279
- .loading-bar {
280
- height: 2px;
281
- width: 100%;
282
- background: #eee;
283
- position: relative;
284
- overflow: hidden;
285
- display: none;
286
- }
287
 
288
- .loading-bar.active {
289
- display: block;
290
- }
291
-
292
- .loading-progress {
293
- height: 100%;
294
- width: 50%;
295
- background: var(--fitbit-green);
296
- position: absolute;
297
- animation: load 1.5s infinite ease-in-out;
298
- }
299
-
300
- @keyframes load {
301
- 0% { left: -50%; }
302
- 100% { left: 100%; }
303
- }
304
-
305
- </style>
306
- </head>
307
- <body>
308
-
309
- <div class="iphone-frame">
310
- <!-- iOS Status Bar Simulation -->
311
- <div class="status-bar">
312
- <span>9:41</span>
313
- <div style="display: flex; gap: 5px;">
314
- <i class="fas fa-signal"></i>
315
- <i class="fas fa-wifi"></i>
316
- <i class="fas fa-battery-three-quarters"></i>
317
- </div>
318
- </div>
319
-
320
- <!-- App Header -->
321
- <div class="app-header">
322
- <div class="app-title">Fitbit<span style="color:var(--fitbit-green)">Voice</span></div>
323
- <div class="connection-status">
324
- <span>Fitbit</span>
325
- <div class="status-dot"></div>
326
- <span>Claude</span>
327
- <i class="fas fa-check-circle" style="color: var(--claude-purple)"></i>
328
- </div>
329
- </div>
330
-
331
- <!-- Main Scrollable Content -->
332
- <div class="content-area" id="chat-container">
333
-
334
- <!-- Context Card: Simulates Fitbit Data being sent -->
335
- <div class="fitbit-context">
336
- <div>
337
- <div class="fitbit-label">Current Activity</div>
338
- <div class="fitbit-data">Walking <span style="font-size: 14px; opacity: 0.8">• 102 bpm</span></div>
339
- </div>
340
- <i class="fas fa-heartbeat heart-icon"></i>
341
- </div>
342
-
343
- <!-- Claude Response Placeholder -->
344
- <div class="claude-card" id="claude-response">
345
- <div class="claude-header">
346
- <i class="fas fa-sparkles"></i> Claude AI
347
- </div>
348
- <div id="claude-text">
349
- Waiting for command...
350
- </div>
351
- </div>
352
-
353
  </div>
354
 
355
- <!-- Loading Bar -->
356
- <div class="loading-bar" id="loader">
357
- <div class="loading-progress"></div>
358
  </div>
359
 
360
- <!-- Voice Control Area -->
361
- <div class="voice-zone">
362
- <div class="voice-status-text" id="status-text">Tap to speak</div>
363
-
364
- <div class="mic-button" id="mic-btn" onclick="toggleRecording()">
365
- <i class="fas fa-microphone"></i>
366
- <div class="ripple"></div>
367
- </div>
368
  </div>
369
 
370
- <!-- Footer Link -->
371
- <div class="footer-link">
372
- Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
373
- </div>
374
  </div>
375
 
376
- <script>
377
- const micBtn = document.getElementById('mic-btn');
378
- const statusText = document.getElementById('status-text');
379
- const claudeCard = document.getElementById('claude-response');
380
- const claudeText = document.getElementById('claude-text');
381
- const loader = document.getElementById('loader');
382
- const chatContainer = document.getElementById('chat-container');
383
-
384
- let isRecording = false;
385
-
386
- // Mock responses for demonstration
387
- const mockResponses = [
388
- "I've logged your 'Walking' activity. Your heart rate is slightly elevated at 102 bpm. Is this a new workout session?",
389
- "Based on your Fitbit data, you've taken 4,200 steps today. You are 60% towards your daily goal. Keep moving!",
390
- "I noticed a spike in your heart rate at 9:30 AM. Did you just finish a sprint? I can add that to your exercise log."
391
- ];
392
-
393
- function toggleRecording() {
394
- if (isRecording) {
395
- stopRecording();
396
- } else {
397
- startRecording();
398
- }
 
 
 
 
 
 
399
  }
 
400
 
401
- function startRecording() {
402
- isRecording = true;
403
- micBtn.classList.add('listening');
404
- statusText.innerText = "Listening...";
405
- statusText.style.color = "#007AFF";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
 
407
- // Simulate listening duration (3 seconds)
408
- setTimeout(() => {
409
- processCommand();
410
- }, 3000);
411
- }
 
 
 
 
 
 
412
 
413
- function stopRecording() {
414
- isRecording = false;
415
- micBtn.classList.remove('listening');
416
- statusText.innerText = "Tap to speak";
417
- statusText.style.color = "#8E8E93";
418
- }
419
 
420
- function processCommand() {
421
- stopRecording();
422
-
423
- // 1. Show Loading State (Sending to Claude)
424
- loader.classList.add('active');
425
- statusText.innerText = "Processing with Claude...";
426
- statusText.style.color = "#D97757"; // Claude color
427
-
428
- // 2. Simulate Network Delay
429
- setTimeout(() => {
430
- loader.classList.remove('active');
431
-
432
- // 3. Generate Response
433
- const randomResponse = mockResponses[Math.floor(Math.random() * mockResponses.length)];
434
-
435
- // 4. Update UI with Response
436
- claudeText.innerText = randomResponse;
437
- claudeCard.classList.add('active');
438
-
439
- statusText.innerText = "Command Sent";
440
-
441
- // Scroll to bottom
442
- chatContainer.scrollTop = chatContainer.scrollHeight;
443
-
444
- }, 2000);
445
  }
 
446
 
447
- // Initialize
448
- console.log("Fitbit-Claude Bridge Initialized");
449
- </script>
450
  </body>
 
451
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
7
+ <title>Fitbit x Claude Voice Bridge</title>
8
+ <!-- FontAwesome for Icons -->
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
+
11
+ <style>
12
+ :root {
13
+ --ios-bg: #F2F2F7;
14
+ --ios-card: #FFFFFF;
15
+ --ios-text: #1C1C1E;
16
+ --ios-gray: #8E8E93;
17
+ --fitbit-green: #00B0B9;
18
+ --claude-purple: #D97757;
19
+ --claude-bg: #F9F7F3;
20
+ --accent-blue: #007AFF;
21
+ --shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
22
+ --font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
23
+ }
24
+
25
+ * {
26
+ box-sizing: box-sizing;
27
+ margin: 0;
28
+ padding: 0;
29
+ -webkit-tap-highlight-color: transparent;
30
+ }
31
+
32
+ body {
33
+ background-color: #e0e0e0;
34
+ font-family: var(--font-stack);
35
+ display: flex;
36
+ justify-content: center;
37
+ align-items: center;
38
+ min-height: 100vh;
39
+ overflow: hidden;
40
+ }
41
+
42
+ /* --- Phone Container --- */
43
+ .iphone-frame {
44
+ width: 375px;
45
+ height: 812px;
46
+ background: var(--ios-bg);
47
+ border-radius: 40px;
48
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2);
49
+ position: relative;
50
+ overflow: hidden;
51
+ display: flex;
52
+ flex-direction: column;
53
+ border: 8px solid #333;
54
+ }
55
+
56
+ /* --- Header / Status Bar --- */
57
+ .status-bar {
58
+ height: 44px;
59
+ display: flex;
60
+ justify-content: space-between;
61
+ align-items: center;
62
+ padding: 0 20px;
63
+ font-size: 14px;
64
+ font-weight: 600;
65
+ color: var(--ios-text);
66
+ background: rgba(255, 255, 255, 0.8);
67
+ backdrop-filter: blur(10px);
68
+ z-index: 10;
69
+ }
70
+
71
+ .app-header {
72
+ padding: 10px 20px;
73
+ background: rgba(255, 255, 255, 0.9);
74
+ backdrop-filter: blur(20px);
75
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
76
+ display: flex;
77
+ justify-content: space-between;
78
+ align-items: center;
79
+ z-index: 9;
80
+ }
81
+
82
+ .app-title {
83
+ font-weight: 700;
84
+ font-size: 18px;
85
+ color: var(--ios-text);
86
+ }
87
+
88
+ .connection-status {
89
+ display: flex;
90
+ gap: 8px;
91
+ font-size: 12px;
92
+ color: var(--ios-gray);
93
+ }
94
+
95
+ .status-dot {
96
+ width: 8px;
97
+ height: 8px;
98
+ border-radius: 50%;
99
+ background-color: var(--fitbit-green);
100
+ box-shadow: 0 0 4px var(--fitbit-green);
101
+ animation: pulse 2s infinite;
102
+ }
103
+
104
+ .help-btn {
105
+ cursor: pointer;
106
+ color: var(--accent-blue);
107
+ font-size: 16px;
108
+ transition: transform 0.2s;
109
+ }
110
+
111
+ .help-btn:active {
112
+ transform: scale(0.9);
113
+ }
114
+
115
+ /* --- Main Content Area --- */
116
+ .content-area {
117
+ flex: 1;
118
+ padding: 20px;
119
+ overflow-y: auto;
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: 15px;
123
+ }
124
+
125
+ /* Fitbit Data Card */
126
+ .fitbit-context {
127
+ background: linear-gradient(135deg, #00B0B9, #008a91);
128
+ color: white;
129
+ padding: 15px;
130
+ border-radius: 16px;
131
+ box-shadow: var(--shadow);
132
+ display: flex;
133
+ justify-content: space-between;
134
+ align-items: center;
135
+ }
136
+
137
+ .fitbit-label {
138
+ font-size: 12px;
139
+ opacity: 0.8;
140
+ text-transform: uppercase;
141
+ letter-spacing: 1px;
142
+ }
143
+
144
+ .fitbit-data {
145
+ font-size: 20px;
146
+ font-weight: 700;
147
+ }
148
+
149
+ .heart-icon {
150
+ font-size: 16px;
151
+ animation: heartbeat 1.2s infinite;
152
+ }
153
+
154
+ /* Claude Response Card */
155
+ .claude-card {
156
+ background: var(--claude-bg);
157
+ border: 1px solid rgba(0, 0, 0, 0.05);
158
+ padding: 15px;
159
+ border-radius: 16px;
160
+ font-size: 14px;
161
+ line-height: 1.5;
162
+ color: #333;
163
+ box-shadow: var(--shadow);
164
+ position: relative;
165
+ display: none;
166
+ /* Hidden by default */
167
+ }
168
+
169
+ .claude-card.active {
170
+ display: block;
171
+ animation: slideUp 0.4s ease-out;
172
+ }
173
+
174
+ .claude-header {
175
+ font-size: 10px;
176
+ color: var(--claude-purple);
177
+ font-weight: 700;
178
+ text-transform: uppercase;
179
+ margin-bottom: 5px;
180
+ display: flex;
181
+ align-items: center;
182
+ gap: 5px;
183
+ }
184
+
185
+ /* --- Voice Interaction Zone --- */
186
+ .voice-zone {
187
+ margin-top: auto;
188
+ margin-bottom: 20px;
189
+ display: flex;
190
+ flex-direction: column;
191
+ align-items: center;
192
+ justify-content: center;
193
+ height: 180px;
194
+ }
195
+
196
+ .mic-button {
197
+ width: 80px;
198
+ height: 80px;
199
+ background: white;
200
+ border-radius: 50%;
201
+ display: flex;
202
+ justify-content: center;
203
+ align-items: center;
204
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
205
+ cursor: pointer;
206
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
207
+ position: relative;
208
+ z-index: 2;
209
+ }
210
+
211
+ .mic-button i {
212
+ font-size: 24px;
213
+ color: var(--ios-text);
214
+ transition: color 0.3s;
215
+ }
216
+
217
+ .mic-button.listening {
218
+ background: var(--accent-blue);
219
+ transform: scale(1.1);
220
+ }
221
+
222
+ .mic-button.listening i {
223
+ color: white;
224
+ }
225
+
226
+ /* Ripple Effect */
227
+ .ripple {
228
+ position: absolute;
229
+ width: 80px;
230
+ height: 80px;
231
+ border-radius: 50%;
232
+ border: 2px solid var(--accent-blue);
233
+ opacity: 0;
234
+ z-index: 1;
235
+ }
236
+
237
+ .mic-button.listening .ripple {
238
+ animation: rippleAnim 1.5s infinite;
239
+ }
240
+
241
+ .voice-status-text {
242
+ margin-top: 15px;
243
+ font-size: 14px;
244
+ color: var(--ios-gray);
245
+ font-weight: 500;
246
+ height: 20px;
247
+ }
248
+
249
+ /* --- Footer --- */
250
+ .footer-link {
251
+ position: absolute;
252
+ bottom: 10px;
253
+ width: 100%;
254
+ text-align: center;
255
+ font-size: 10px;
256
+ color: var(--ios-gray);
257
+ opacity: 0.6;
258
+ }
259
+
260
+ .footer-link a {
261
+ color: var(--ios-text);
262
+ text-decoration: none;
263
+ font-weight: 600;
264
+ }
265
+
266
+ /* --- Animations --- */
267
+ @keyframes pulse {
268
+ 0% { opacity: 0.5; }
269
+ 50% { opacity: 1; }
270
+ 100% { opacity: 0.5; }
271
+ }
272
+
273
+ @keyframes heartbeat {
274
+ 0% { transform: scale(1); }
275
+ 14% { transform: scale(1.3); }
276
+ 28% { transform: scale(1); }
277
+ 42% { transform: scale(1.3); }
278
+ 70% { transform: scale(1); }
279
+ }
280
+
281
+ @keyframes rippleAnim {
282
+ 0% { width: 80px; height: 80px; opacity: 1; }
283
+ 100% { width: 200px; height: 200px; opacity: 0; }
284
+ }
285
+
286
+ @keyframes slideUp {
287
+ from { transform: translateY(20px); opacity: 0; }
288
+ to { transform: translateY(0); opacity: 1; }
289
+ }
290
+
291
+ /* --- Loading Bar --- */
292
+ .loading-bar {
293
+ height: 2px;
294
+ width: 100%;
295
+ background: #eee;
296
+ position: relative;
297
+ overflow: hidden;
298
+ display: none;
299
+ }
300
+
301
+ .loading-bar.active {
302
+ display: block;
303
+ }
304
+
305
+ .loading-progress {
306
+ height: 100%;
307
+ width: 50%;
308
+ background: var(--fitbit-green);
309
+ position: absolute;
310
+ animation: load 1.5s infinite ease-in-out;
311
+ }
312
+
313
+ @keyframes load {
314
+ 0% { left: -50%; }
315
+ 100% { left: 100%; }
316
+ }
317
+
318
+ /* --- Installation Guide Modal --- */
319
+ .modal-overlay {
320
+ position: absolute;
321
+ top: 0;
322
+ left: 0;
323
+ width: 100%;
324
+ height: 100%;
325
+ background: rgba(0, 0, 0, 0.4);
326
+ backdrop-filter: blur(5px);
327
+ z-index: 100;
328
+ display: none; /* Hidden by default */
329
+ justify-content: center;
330
+ align-items: center;
331
+ padding: 20px;
332
+ opacity: 0;
333
+ transition: opacity 0.3s ease;
334
+ }
335
+
336
+ .modal-overlay.open {
337
+ display: flex;
338
+ opacity: 1;
339
+ }
340
+
341
+ .modal-content {
342
+ background: rgba(255, 255, 255, 0.95);
343
+ width: 100%;
344
+ max-width: 300px;
345
+ border-radius: 20px;
346
+ padding: 25px;
347
+ box-shadow: 0 10px 40px rgba(0,0,0,0.2);
348
+ text-align: center;
349
+ transform: scale(0.9);
350
+ transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
351
+ }
352
+
353
+ .modal-overlay.open .modal-content {
354
+ transform: scale(1);
355
+ }
356
+
357
+ .modal-title {
358
+ font-size: 18px;
359
+ font-weight: 700;
360
+ margin-bottom: 15px;
361
+ color: var(--ios-text);
362
+ }
363
+
364
+ .modal-step {
365
+ display: flex;
366
+ align-items: center;
367
+ gap: 15px;
368
+ margin-bottom: 15px;
369
+ text-align: left;
370
+ }
371
+
372
+ .step-icon {
373
+ width: 40px;
374
+ height: 40px;
375
+ background: #eee;
376
+ border-radius: 10px;
377
+ display: flex;
378
+ justify-content: center;
379
+ align-items: center;
380
+ font-size: 18px;
381
+ color: var(--ios-text);
382
+ }
383
+
384
+ .step-text {
385
+ font-size: 13px;
386
+ line-height: 1.4;
387
+ color: #555;
388
+ }
389
+
390
+ .modal-close {
391
+ margin-top: 10px;
392
+ background: var(--ios-text);
393
+ color: white;
394
+ border: none;
395
+ padding: 12px 20px;
396
+ border-radius: 12px;
397
+ font-weight: 600;
398
+ font-size: 14px;
399
+ cursor: pointer;
400
+ width: 100%;
401
+ }
402
+
403
+ </style>
404
+ </head>
405
 
406
+ <body>
 
 
 
407
 
408
+ <div class="iphone-frame">
409
+ <!-- iOS Status Bar Simulation -->
410
+ <div class="status-bar">
411
+ <span>9:41</span>
412
+ <div style="display: flex; gap: 5px;">
413
+ <i class="fas fa-signal"></i>
414
+ <i class="fas fa-wifi"></i>
415
+ <i class="fas fa-battery-three-quarters"></i>
416
+ </div>
417
+ </div>
418
 
419
+ <!-- App Header -->
420
+ <div class="app-header">
421
+ <div class="app-title">Fitbit<span style="color:var(--fitbit-green)">Voice</span></div>
422
+ <div class="connection-status">
423
+ <span>Fitbit</span>
424
+ <div class="status-dot"></div>
425
+ <span>Claude</span>
426
+ <i class="fas fa-check-circle" style="color: var(--claude-purple)"></i>
427
+ </div>
428
+ <!-- Added Help Button -->
429
+ <div class="help-btn" onclick="toggleModal()">
430
+ <i class="fas fa-info-circle"></i>
431
+ </div>
432
+ </div>
433
 
434
+ <!-- Main Scrollable Content -->
435
+ <div class="content-area" id="chat-container">
 
436
 
437
+ <!-- Context Card: Simulates Fitbit Data being sent -->
438
+ <div class="fitbit-context">
439
+ <div>
440
+ <div class="fitbit-label">Current Activity</div>
441
+ <div class="fitbit-data">Walking <span style="font-size: 14px; opacity: 0.8">• 102 bpm</span></div>
442
+ </div>
443
+ <i class="fas fa-heartbeat heart-icon"></i>
444
+ </div>
445
 
446
+ <!-- Claude Response Placeholder -->
447
+ <div class="claude-card" id="claude-response">
448
+ <div class="claude-header">
449
+ <i class="fas fa-sparkles"></i> Claude AI
450
+ </div>
451
+ <div id="claude-text">
452
+ Waiting for command...
453
+ </div>
454
+ </div>
 
 
 
 
 
 
 
455
 
456
+ </div>
 
 
 
 
 
457
 
458
+ <!-- Loading Bar -->
459
+ <div class="loading-bar" id="loader">
460
+ <div class="loading-progress"></div>
461
+ </div>
 
 
 
462
 
463
+ <!-- Voice Control Area -->
464
+ <div class="voice-zone">
465
+ <div class="voice-status-text" id="status-text">Tap to speak</div>
 
466
 
467
+ <div class="mic-button" id="mic-btn" onclick="toggleRecording()">
468
+ <i class="fas fa-microphone"></i>
469
+ <div class="ripple"></div>
470
+ </div>
471
+ </div>
472
 
473
+ <!-- Installation Guide Modal -->
474
+ <div class="modal-overlay" id="install-modal">
475
+ <div class="modal-content">
476
+ <div class="modal-title">Install on iPhone</div>
 
 
 
 
 
477
 
478
+ <div class="modal-step">
479
+ <div class="step-icon"><i class="fas fa-share"></i></div>
480
+ <div class="step-text">Tap the <b>Share</b> button in Safari.</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
  </div>
482
 
483
+ <div class="modal-step">
484
+ <div class="step-icon"><i class="fas fa-plus-square"></i></div>
485
+ <div class="step-text">Scroll down and tap <b>"Add to Home Screen"</b>.</div>
486
  </div>
487
 
488
+ <div class="modal-step">
489
+ <div class="step-icon"><i class="fas fa-check"></i></div>
490
+ <div class="step-text">Tap <b>"Add"</b> to finish.</div>
 
 
 
 
 
491
  </div>
492
 
493
+ <button class="modal-close" onclick="toggleModal()">Got it</button>
494
+ </div>
 
 
495
  </div>
496
 
497
+ <!-- Footer Link -->
498
+ <div class="footer-link">
499
+ Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
500
+ </div>
501
+ </div>
502
+
503
+ <script>
504
+ const micBtn = document.getElementById('mic-btn');
505
+ const statusText = document.getElementById('status-text');
506
+ const claudeCard = document.getElementById('claude-response');
507
+ const claudeText = document.getElementById('claude-text');
508
+ const loader = document.getElementById('loader');
509
+ const chatContainer = document.getElementById('chat-container');
510
+ const modal = document.getElementById('install-modal');
511
+
512
+ let isRecording = false;
513
+
514
+ // Mock responses for demonstration
515
+ const mockResponses = [
516
+ "I've logged your 'Walking' activity. Your heart rate is slightly elevated at 102 bpm. Is this a new workout session?",
517
+ "Based on your Fitbit data, you've taken 4,200 steps today. You are 60% towards your daily goal. Keep moving!",
518
+ "I noticed a spike in your heart rate at 9:30 AM. Did you just finish a sprint? I can add that to your exercise log."
519
+ ];
520
+
521
+ function toggleRecording() {
522
+ if (isRecording) {
523
+ stopRecording();
524
+ } else {
525
+ startRecording();
526
  }
527
+ }
528
 
529
+ function startRecording() {
530
+ isRecording = true;
531
+ micBtn.classList.add('listening');
532
+ statusText.innerText = "Listening...";
533
+ statusText.style.color = "#007AFF";
534
+
535
+ // Simulate listening duration (3 seconds)
536
+ setTimeout(() => {
537
+ processCommand();
538
+ }, 3000);
539
+ }
540
+
541
+ function stopRecording() {
542
+ isRecording = false;
543
+ micBtn.classList.remove('listening');
544
+ statusText.innerText = "Tap to speak";
545
+ statusText.style.color = "#8E8E93";
546
+ }
547
+
548
+ function processCommand() {
549
+ stopRecording();
550
+
551
+ // 1. Show Loading State (Sending to Claude)
552
+ loader.classList.add('active');
553
+ statusText.innerText = "Processing with Claude...";
554
+ statusText.style.color = "#D97757"; // Claude color
555
+
556
+ // 2. Simulate Network Delay
557
+ setTimeout(() => {
558
+ loader.classList.remove('active');
559
 
560
+ // 3. Generate Response
561
+ const randomResponse = mockResponses[Math.floor(Math.random() * mockResponses.length)];
562
+
563
+ // 4. Update UI with Response
564
+ claudeText.innerText = randomResponse;
565
+ claudeCard.classList.add('active');
566
+
567
+ statusText.innerText = "Command Sent";
568
+
569
+ // Scroll to bottom
570
+ chatContainer.scrollTop = chatContainer.scrollHeight;
571
 
572
+ }, 2000);
573
+ }
 
 
 
 
574
 
575
+ function toggleModal() {
576
+ if (modal.classList.contains('open')) {
577
+ modal.classList.remove('open');
578
+ } else {
579
+ modal.classList.add('open');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
  }
581
+ }
582
 
583
+ // Initialize
584
+ console.log("Fitbit-Claude Bridge Initialized");
585
+ </script>
586
  </body>
587
+
588
  </html>