Boopster commited on
Commit
df20c79
·
1 Parent(s): 5c78d2c

feat: Redesign the landing page to showcase app features and usage instructions, removing the direct installation UI.

Browse files
Files changed (2) hide show
  1. index.html +58 -200
  2. style.css +127 -0
index.html CHANGED
@@ -4,232 +4,90 @@
4
  <head>
5
  <meta charset="utf-8" />
6
  <meta name="viewport" content="width=device-width" />
7
- <title> Reachy Mini Metronome </title>
 
 
8
  <link rel="stylesheet" href="style.css" />
9
  </head>
10
 
11
  <body>
12
  <div class="hero">
13
  <div class="hero-content">
14
- <div class="app-icon">🤖</div>
15
- <h1> Reachy Mini Metronome </h1>
16
- <p class="tagline">Enter your tagline here</p>
17
  </div>
18
  </div>
19
 
20
  <div class="container">
21
  <div class="main-card">
22
- <div class="app-preview">
23
- <div class="preview-image">
24
- <div class="camera-feed">🛠️</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  </div>
26
  </div>
27
  </div>
28
- </div>
29
-
30
- <div class="download-section">
31
- <div class="download-card">
32
- <h2>Install This App</h2>
33
-
34
- <div class="dashboard-config">
35
- <label for="dashboardUrl">Your Reachy Dashboard URL:</label>
36
- <input type="url" id="dashboardUrl" value="http://localhost:8000"
37
- placeholder="http://your-reachy-ip:8000" />
38
- </div>
39
-
40
- <button id="installBtn" class="install-btn primary">
41
- <span class="btn-icon">📥</span>
42
- Install Reachy Mini Metronome to Reachy Mini
43
- </button>
44
 
45
- <div id="installStatus" class="install-status"></div>
 
 
 
 
 
 
 
 
 
 
 
46
 
 
 
 
 
 
 
 
 
 
47
  </div>
48
  </div>
49
 
50
  <div class="footer">
51
  <p>
52
- 🤖 Reachy Mini Metronome •
53
  <a href="https://github.com/pollen-robotics" target="_blank">Pollen Robotics</a> •
54
  <a href="https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Apps" target="_blank">Browse More
55
  Apps</a>
56
  </p>
57
  </div>
58
- </div>
59
-
60
- <script>
61
- // Get the current Hugging Face Space URL as the repository URL
62
- function getCurrentSpaceUrl() {
63
- // Get current page URL and convert to repository format
64
- const currentUrl = window.location.href;
65
-
66
- // Remove any trailing slashes and query parameters
67
- const cleanUrl = currentUrl.split('?')[0].replace(/\/$/, '');
68
-
69
- return cleanUrl;
70
- }
71
-
72
- // Parse TOML content to extract project name
73
- function parseTomlProjectName(tomlContent) {
74
- try {
75
- const lines = tomlContent.split('\n');
76
- let inProjectSection = false;
77
-
78
- for (const line of lines) {
79
- const trimmedLine = line.trim();
80
-
81
- // Check if we're entering the [project] section
82
- if (trimmedLine === '[project]') {
83
- inProjectSection = true;
84
- continue;
85
- }
86
-
87
- // Check if we're entering a different section
88
- if (trimmedLine.startsWith('[') && trimmedLine !== '[project]') {
89
- inProjectSection = false;
90
- continue;
91
- }
92
-
93
- // If we're in the project section, look for the name field
94
- if (inProjectSection && trimmedLine.startsWith('name')) {
95
- const match = trimmedLine.match(/name\s*=\s*["']([^"']+)["']/);
96
- if (match) {
97
- // Convert to lowercase and replace invalid characters for app naming
98
- return match[1].toLowerCase().replace(/[^a-z0-9-_]/g, '-');
99
- }
100
- }
101
- }
102
-
103
- throw new Error('Project name not found in pyproject.toml');
104
- } catch (error) {
105
- console.error('Error parsing pyproject.toml:', error);
106
- return 'unknown-app';
107
- }
108
- }
109
-
110
- // Fetch and parse pyproject.toml from the current space
111
- async function getAppNameFromCurrentSpace() {
112
- try {
113
- // Fetch pyproject.toml from the current space
114
- const response = await fetch('./pyproject.toml');
115
- if (!response.ok) {
116
- throw new Error(`Failed to fetch pyproject.toml: ${response.status}`);
117
- }
118
-
119
- const tomlContent = await response.text();
120
- return parseTomlProjectName(tomlContent);
121
- } catch (error) {
122
- console.error('Error fetching app name from current space:', error);
123
- // Fallback to extracting from URL if pyproject.toml is not accessible
124
- const url = getCurrentSpaceUrl();
125
- const parts = url.split('/');
126
- const spaceName = parts[parts.length - 1];
127
- return spaceName.toLowerCase().replace(/[^a-z0-9-_]/g, '-');
128
- }
129
- }
130
-
131
- async function installToReachy() {
132
- const dashboardUrl = document.getElementById('dashboardUrl').value.trim();
133
- const statusDiv = document.getElementById('installStatus');
134
- const installBtn = document.getElementById('installBtn');
135
-
136
- if (!dashboardUrl) {
137
- showStatus('error', 'Please enter your Reachy dashboard URL');
138
- return;
139
- }
140
-
141
- try {
142
- installBtn.disabled = true;
143
- installBtn.innerHTML = '<span class="btn-icon">⏳</span>Installing...';
144
- showStatus('loading', 'Connecting to your Reachy dashboard...');
145
-
146
- // Test connection
147
- const testResponse = await fetch(`${dashboardUrl}/api/status`, {
148
- method: 'GET',
149
- mode: 'cors',
150
- });
151
-
152
- if (!testResponse.ok) {
153
- throw new Error('Cannot connect to dashboard. Make sure the URL is correct and the dashboard is running.');
154
- }
155
-
156
- showStatus('loading', 'Reading app configuration...');
157
-
158
- // Get app name from pyproject.toml in current space
159
- const appName = await getAppNameFromCurrentSpace();
160
-
161
- // Get current space URL as repository URL
162
- const repoUrl = getCurrentSpaceUrl();
163
-
164
- showStatus('loading', `Starting installation of "${appName}"...`);
165
-
166
- // Start installation
167
- const installResponse = await fetch(`${dashboardUrl}/api/install`, {
168
- method: 'POST',
169
- mode: 'cors',
170
- headers: {
171
- 'Content-Type': 'application/json',
172
- },
173
- body: JSON.stringify({
174
- url: repoUrl,
175
- name: appName
176
- })
177
- });
178
-
179
- const result = await installResponse.json();
180
-
181
- if (installResponse.ok) {
182
- showStatus('success', `✅ Installation started for "${appName}"! Check your dashboard for progress.`);
183
- setTimeout(() => {
184
- showStatus('info', `Open your dashboard at ${dashboardUrl} to see the installed app.`);
185
- }, 3000);
186
- } else {
187
- throw new Error(result.detail || 'Installation failed');
188
- }
189
-
190
- } catch (error) {
191
- console.error('Installation error:', error);
192
- showStatus('error', `❌ ${error.message}`);
193
- } finally {
194
- installBtn.disabled = false;
195
- installBtn.innerHTML = '<span class="btn-icon">📥</span>Install App to Reachy';
196
- }
197
- }
198
-
199
- function showStatus(type, message) {
200
- const statusDiv = document.getElementById('installStatus');
201
- statusDiv.className = `install-status ${type}`;
202
- statusDiv.textContent = message;
203
- statusDiv.style.display = 'block';
204
- }
205
-
206
- function copyToClipboard() {
207
- const repoUrl = document.getElementById('repoUrl').textContent;
208
- navigator.clipboard.writeText(repoUrl).then(() => {
209
- showStatus('success', '📋 Repository URL copied to clipboard!');
210
- }).catch(() => {
211
- showStatus('error', 'Failed to copy URL. Please copy manually.');
212
- });
213
- }
214
-
215
- // Update the displayed repository URL on page load
216
- document.addEventListener('DOMContentLoaded', () => {
217
- // Auto-detect local dashboard
218
- const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
219
- if (isLocalhost) {
220
- document.getElementById('dashboardUrl').value = 'http://localhost:8000';
221
- }
222
-
223
- // Update the repository URL display if element exists
224
- const repoUrlElement = document.getElementById('repoUrl');
225
- if (repoUrlElement) {
226
- repoUrlElement.textContent = getCurrentSpaceUrl();
227
- }
228
- });
229
-
230
- // Event listeners
231
- document.getElementById('installBtn').addEventListener('click', installToReachy);
232
- </script>
233
  </body>
234
 
235
  </html>
 
4
  <head>
5
  <meta charset="utf-8" />
6
  <meta name="viewport" content="width=device-width" />
7
+ <title>Reachy Mini Metronome</title>
8
+ <meta name="description"
9
+ content="A visual and audible metronome for Reachy Mini that uses both antennas as a pendulum while producing synthesized click sounds." />
10
  <link rel="stylesheet" href="style.css" />
11
  </head>
12
 
13
  <body>
14
  <div class="hero">
15
  <div class="hero-content">
16
+ <div class="app-icon">🎵🤖</div>
17
+ <h1>Reachy Mini Metronome</h1>
18
+ <p class="tagline">Turn your Reachy Mini into a playful, animated metronome</p>
19
  </div>
20
  </div>
21
 
22
  <div class="container">
23
  <div class="main-card">
24
+ <div class="features-section">
25
+ <h2>✨ Features</h2>
26
+
27
+ <div class="feature-grid">
28
+ <div class="feature-item">
29
+ <div class="feature-icon">🎵</div>
30
+ <h3>BPM Control</h3>
31
+ <p>Adjustable tempo from 40 to 208 BPM with preset options for common tempos like Largo,
32
+ Moderato, and Presto.</p>
33
+ </div>
34
+
35
+ <div class="feature-item">
36
+ <div class="feature-icon">🎼</div>
37
+ <h3>Time Signatures</h3>
38
+ <p>Support for multiple time signatures including 2/4, 3/4, 4/4, 5/4, and 6/8 to match your
39
+ music.</p>
40
+ </div>
41
+
42
+ <div class="feature-item">
43
+ <div class="feature-icon">🔊</div>
44
+ <h3>Accented Downbeats</h3>
45
+ <p>Clear audio feedback with a higher-pitched click on beat 1 to help you keep track of
46
+ measures.</p>
47
+ </div>
48
+
49
+ <div class="feature-item">
50
+ <div class="feature-icon">🤖</div>
51
+ <h3>Animated Antennas</h3>
52
+ <p>Both antennas swing in mirror motion like a pendulum, giving you a visual beat indicator.</p>
53
+ </div>
54
  </div>
55
  </div>
56
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ <div class="info-card">
59
+ <h2>🎯 How It Works</h2>
60
+ <p>
61
+ When running, Reachy Mini's antennas swing back and forth in sync with the tempo you've set.
62
+ Each beat is accompanied by a synthesized click sound played through the robot's speaker,
63
+ with a distinctive accent on the first beat of each measure.
64
+ </p>
65
+ <p>
66
+ Use the web interface at <code>http://localhost:8042</code> to control the metronome in real-time —
67
+ adjust the BPM, change time signatures, and start or stop the beat.
68
+ </p>
69
+ </div>
70
 
71
+ <div class="info-card">
72
+ <h2>🚀 Getting Started</h2>
73
+ <ol class="steps-list">
74
+ <li>Start the Reachy Mini daemon on your robot or simulator</li>
75
+ <li>Go to the dashboard at <code>http://localhost:8000</code></li>
76
+ <li>Find <strong>Reachy Mini Metronome</strong> in the Applications section and toggle it on</li>
77
+ <li>Click the ⚙️ icon on the app tile, or go directly to <code>http://localhost:8042</code> to open the
78
+ metronome controls</li>
79
+ </ol>
80
  </div>
81
  </div>
82
 
83
  <div class="footer">
84
  <p>
85
+ 🎵 Reachy Mini Metronome •
86
  <a href="https://github.com/pollen-robotics" target="_blank">Pollen Robotics</a> •
87
  <a href="https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Apps" target="_blank">Browse More
88
  Apps</a>
89
  </p>
90
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  </body>
92
 
93
  </html>
style.css CHANGED
@@ -365,6 +365,133 @@ body {
365
  line-height: 1.8;
366
  }
367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  .footer {
369
  text-align: center;
370
  padding: 2rem;
 
365
  line-height: 1.8;
366
  }
367
 
368
+ /* Features Section */
369
+ .features-section {
370
+ padding: 2rem;
371
+ }
372
+
373
+ .features-section h2 {
374
+ font-size: 1.5rem;
375
+ color: #1e293b;
376
+ margin-bottom: 1.5rem;
377
+ text-align: center;
378
+ }
379
+
380
+ .feature-grid {
381
+ display: grid;
382
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
383
+ gap: 1rem;
384
+ }
385
+
386
+ .feature-item {
387
+ background: linear-gradient(135deg, #f8fafc, #f1f5f9);
388
+ border-radius: 12px;
389
+ padding: 1.25rem;
390
+ text-align: center;
391
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
392
+ border: 1px solid #e2e8f0;
393
+ }
394
+
395
+ .feature-item:hover {
396
+ transform: translateY(-5px);
397
+ box-shadow: 0 10px 30px rgba(102, 126, 234, 0.15);
398
+ }
399
+
400
+ .feature-icon {
401
+ font-size: 2rem;
402
+ margin-bottom: 0.5rem;
403
+ display: block;
404
+ }
405
+
406
+ .feature-item h3 {
407
+ color: #1e293b;
408
+ font-size: 1rem;
409
+ margin-bottom: 0.25rem;
410
+ }
411
+
412
+ .feature-item p {
413
+ color: #64748b;
414
+ font-size: 0.85rem;
415
+ line-height: 1.5;
416
+ }
417
+
418
+ /* Info Cards */
419
+ .info-card {
420
+ background: white;
421
+ border-radius: 20px;
422
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
423
+ padding: 2.5rem;
424
+ margin-bottom: 2rem;
425
+ }
426
+
427
+ .info-card h2 {
428
+ font-size: 1.5rem;
429
+ color: #1e293b;
430
+ margin-bottom: 1.5rem;
431
+ }
432
+
433
+ .info-card p {
434
+ color: #475569;
435
+ font-size: 1.05rem;
436
+ line-height: 1.8;
437
+ margin-bottom: 1rem;
438
+ }
439
+
440
+ .info-card p:last-child {
441
+ margin-bottom: 0;
442
+ }
443
+
444
+ .info-card code {
445
+ background: #f1f5f9;
446
+ color: #7c3aed;
447
+ padding: 0.2rem 0.5rem;
448
+ border-radius: 6px;
449
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
450
+ font-size: 0.9rem;
451
+ }
452
+
453
+ /* Steps List */
454
+ .steps-list {
455
+ counter-reset: step-counter;
456
+ list-style: none;
457
+ padding: 0;
458
+ }
459
+
460
+ .steps-list li {
461
+ position: relative;
462
+ padding-left: 3rem;
463
+ margin-bottom: 1.25rem;
464
+ color: #475569;
465
+ font-size: 1.05rem;
466
+ line-height: 1.6;
467
+ }
468
+
469
+ .steps-list li:last-child {
470
+ margin-bottom: 0;
471
+ }
472
+
473
+ .steps-list li::before {
474
+ counter-increment: step-counter;
475
+ content: counter(step-counter);
476
+ position: absolute;
477
+ left: 0;
478
+ top: 0;
479
+ width: 2rem;
480
+ height: 2rem;
481
+ background: linear-gradient(135deg, #667eea, #764ba2);
482
+ color: white;
483
+ border-radius: 50%;
484
+ display: flex;
485
+ align-items: center;
486
+ justify-content: center;
487
+ font-weight: 700;
488
+ font-size: 0.9rem;
489
+ }
490
+
491
+ .steps-list li strong {
492
+ color: #1e293b;
493
+ }
494
+
495
  .footer {
496
  text-align: center;
497
  padding: 2rem;