Boopster commited on
Commit
05ca173
Β·
1 Parent(s): a17e77e

feat: replace app installation UI with detailed feature descriptions and add code preview styling.

Browse files
Files changed (3) hide show
  1. index.html +66 -198
  2. reachy_mini_danceml/sequence_planner.py +4 -4
  3. style.css +12 -0
index.html CHANGED
@@ -4,16 +4,16 @@
4
  <head>
5
  <meta charset="utf-8" />
6
  <meta name="viewport" content="width=device-width" />
7
- <title> Reachy Mini Danceml </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 Danceml </h1>
16
- <p class="tagline">Voice Assisted Realtime Dance Composition with Reachy Mini</p>
17
  </div>
18
  </div>
19
 
@@ -21,215 +21,83 @@
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 Danceml 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 Danceml β€’
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 DanceML</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 DanceML</h1>
16
+ <p class="tagline">Voice-controlled movement SDK for Reachy Mini robot</p>
17
  </div>
18
  </div>
19
 
 
21
  <div class="main-card">
22
  <div class="app-preview">
23
  <div class="preview-image">
24
+ <div class="camera-feed">πŸŽ™οΈ</div>
25
+ <pre class="code-preview">"Hey Reachy!"
26
+ "Do a happy dance"
27
+ "Now look surprised"
28
+ "Wave hello to everyone"</pre>
29
  </div>
30
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ <div class="app-details">
33
+ <h2>About DanceML</h2>
34
+ <div class="template-info">
35
+ <div class="info-box">
36
+ <h3>🎀 Voice Control</h3>
37
+ <p>Natural language commands via OpenAI Realtime API. Just speak to your Reachy Mini and watch
38
+ it respond!</p>
39
+ </div>
40
+ <div class="info-box">
41
+ <h3>🧠 AI Agent</h3>
42
+ <p>11 function-calling tools for precise movement control, from simple poses to complex
43
+ choreography.</p>
44
+ </div>
45
+ </div>
46
 
47
+ <div class="how-to-use">
48
+ <h3>Features</h3>
49
+ <div class="steps">
50
+ <div class="step">
51
+ <div class="step-number">1</div>
52
+ <div>
53
+ <h4>πŸ“š Move Library</h4>
54
+ <p>101 pre-recorded dances and emotional expressions ready to use</p>
55
+ </div>
56
+ </div>
57
+ <div class="step">
58
+ <div class="step-number">2</div>
59
+ <div>
60
+ <h4>🌊 Procedural Motion</h4>
61
+ <p>Generate continuous animations with waveforms like spirals, circles, and
62
+ figure-eights</p>
63
+ </div>
64
+ </div>
65
+ <div class="step">
66
+ <div class="step-number">3</div>
67
+ <div>
68
+ <h4>🎬 Sequence Planning</h4>
69
+ <p>Multi-step choreography via GPT-4.1 planner with smooth keyframe interpolation</p>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
 
75
+ <div class="template-info" style="margin-top: 2rem;">
76
+ <div class="info-box">
77
+ <h3>🎯 Available Tools</h3>
78
+ <p><code>goto_pose</code> β€’ <code>look_at</code> β€’ <code>play_move</code> β€’
79
+ <code>search_moves</code> β€’ <code>execute_sequence</code> β€’ <code>generate_motion</code>
80
+ </p>
81
+ </div>
82
+ <div class="info-box">
83
+ <h3>πŸ“– Move Dataset</h3>
84
+ <p>Access 101 pre-recorded moves via the <a
85
+ href="https://huggingface.co/datasets/pollen-robotics/reachy-mini-dances-library"
86
+ target="_blank">HuggingFace dataset</a>.</p>
87
+ </div>
88
+ </div>
89
+ </div>
90
  </div>
91
  </div>
92
 
93
  <div class="footer">
94
  <p>
95
+ πŸ€– Reachy Mini DanceML β€’
96
  <a href="https://github.com/pollen-robotics" target="_blank">Pollen Robotics</a> β€’
97
  <a href="https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Apps" target="_blank">Browse More
98
  Apps</a>
99
  </p>
100
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  </body>
102
 
103
  </html>
reachy_mini_danceml/sequence_planner.py CHANGED
@@ -1,4 +1,4 @@
1
- """Sequence planner - uses GPT-4 to plan movement sequences.
2
 
3
  Takes natural language requests and outputs structured SequencePlans
4
  that the executor can run step by step.
@@ -13,7 +13,7 @@ from openai import OpenAI
13
  from .sequence_types import SequencePlan, SequenceStep, StepType, PEEKABOO_SEQUENCE
14
 
15
 
16
- # JSON schema for GPT-4 structured output
17
  SEQUENCE_PLAN_SCHEMA = {
18
  "type": "object",
19
  "properties": {
@@ -90,7 +90,7 @@ Output a complete, well-timed sequence plan."""
90
 
91
 
92
  class SequencePlanner:
93
- """Plans movement sequences using GPT-4."""
94
 
95
  def __init__(self, api_key: Optional[str] = None):
96
  """Initialize with OpenAI API key.
@@ -123,7 +123,7 @@ class SequencePlanner:
123
  print(f" πŸ“¦ Using cached sequence: {plan.name}")
124
  return plan
125
 
126
- # Use GPT-4 to plan custom sequences
127
  print(f" 🧠 Planning sequence for: {request}")
128
 
129
  try:
 
1
+ """Sequence planner - uses GPT-4.1 to plan movement sequences.
2
 
3
  Takes natural language requests and outputs structured SequencePlans
4
  that the executor can run step by step.
 
13
  from .sequence_types import SequencePlan, SequenceStep, StepType, PEEKABOO_SEQUENCE
14
 
15
 
16
+ # JSON schema for GPT-4.1 structured output
17
  SEQUENCE_PLAN_SCHEMA = {
18
  "type": "object",
19
  "properties": {
 
90
 
91
 
92
  class SequencePlanner:
93
+ """Plans movement sequences using GPT-4.1."""
94
 
95
  def __init__(self, api_key: Optional[str] = None):
96
  """Initialize with OpenAI API key.
 
123
  print(f" πŸ“¦ Using cached sequence: {plan.name}")
124
  return plan
125
 
126
+ # Use GPT-4.1 to plan custom sequences
127
  print(f" 🧠 Planning sequence for: {request}")
128
 
129
  try:
style.css CHANGED
@@ -88,6 +88,18 @@ body {
88
  opacity: 0.7;
89
  }
90
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  .detection-overlay {
92
  position: absolute;
93
  top: 50%;
 
88
  opacity: 0.7;
89
  }
90
 
91
+ .code-preview {
92
+ background: #1f2937;
93
+ color: #10b981;
94
+ padding: 1rem;
95
+ border-radius: 8px;
96
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
97
+ font-size: 0.9rem;
98
+ text-align: left;
99
+ white-space: pre-line;
100
+ line-height: 1.8;
101
+ }
102
+
103
  .detection-overlay {
104
  position: absolute;
105
  top: 50%;