8bitkick commited on
Commit
cf91c98
·
1 Parent(s): 691ba96

Try hosting direcly on spaces

Browse files
Files changed (2) hide show
  1. index.html +136 -276
  2. index_old.html +280 -0
index.html CHANGED
@@ -1,280 +1,140 @@
1
- <!doctype html>
2
- <html>
3
-
4
  <head>
5
- <meta charset="utf-8" />
6
- <meta name="viewport" content="width=device-width" />
7
- <title>Example App - Reachy Mini Template</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>Example Reachy Mini App</h1>
16
- <p class="tagline">Template for creating your own Reachy Mini applications</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
-
29
- <div class="app-details">
30
- <h2>Example Template App</h2>
31
- <div class="template-info">
32
- <div class="info-box">
33
- <h3>🎨 Template Purpose</h3>
34
- <p>This is an example landing page for Reachy Mini apps. Feel free to duplicate this template and
35
- customize it for your own applications!</p>
36
- </div>
37
- <div class="info-box">
38
- <h3>🚀 Getting Started</h3>
39
- <p>Use this template to showcase your Reachy Mini app with a landing page. Simply modify the
40
- content, add your app's repository URL, and deploy!</p>
41
- </div>
42
- </div>
43
-
44
-
45
- <div class="how-to-use">
46
- <h3>How to Use This Template</h3>
47
- <div class="steps">
48
- <div class="step">
49
- <span class="step-number">1</span>
50
- <div>
51
- <h4>Duplicate & Customize</h4>
52
- <p>Copy this template and modify the content for your app</p>
53
- </div>
54
- </div>
55
- <div class="step">
56
- <span class="step-number">2</span>
57
- <div>
58
- <h4>Update Repository URL</h4>
59
- <p>Change the JavaScript to point to your app's Git repository</p>
60
- </div>
61
- </div>
62
- <div class="step">
63
- <span class="step-number">3</span>
64
- <div>
65
- <h4>Deploy to HF Spaces</h4>
66
- <p>Upload your customized version to Hugging Face Spaces</p>
67
- </div>
68
- </div>
69
- </div>
70
- </div>
71
- </div>
72
- </div>
73
-
74
- <div class="download-section">
75
- <div class="download-card">
76
- <h2>Install This Example App</h2>
77
- <p>Try out the installation process with this template app</p>
78
-
79
- <div class="dashboard-config">
80
- <label for="dashboardUrl">Your Reachy Dashboard URL:</label>
81
- <input type="url" id="dashboardUrl" value="http://localhost:8000"
82
- placeholder="http://your-reachy-ip:8000" />
83
- </div>
84
-
85
- <button id="installBtn" class="install-btn primary">
86
- <span class="btn-icon">📥</span>
87
- Install Example App to Reachy
88
- </button>
89
-
90
- <div id="installStatus" class="install-status"></div>
91
-
92
- </div>
93
- </div>
94
-
95
- <div class="footer">
96
- <p>
97
- 🤖 Template for Reachy Mini Apps •
98
- <a href="https://github.com/pollen-robotics" target="_blank">Pollen Robotics</a> •
99
- <a href="https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Apps" target="_blank">Browse More
100
- Apps</a>
101
- </p>
102
- </div>
103
- </div>
104
-
105
- <script>
106
- // Get the current Hugging Face Space URL as the repository URL
107
- function getCurrentSpaceUrl() {
108
- // Get current page URL and convert to repository format
109
- const currentUrl = window.location.href;
110
-
111
- // Remove any trailing slashes and query parameters
112
- const cleanUrl = currentUrl.split('?')[0].replace(/\/$/, '');
113
-
114
- return cleanUrl;
115
- }
116
-
117
- // Parse TOML content to extract project name
118
- function parseTomlProjectName(tomlContent) {
119
- try {
120
- const lines = tomlContent.split('\n');
121
- let inProjectSection = false;
122
-
123
- for (const line of lines) {
124
- const trimmedLine = line.trim();
125
-
126
- // Check if we're entering the [project] section
127
- if (trimmedLine === '[project]') {
128
- inProjectSection = true;
129
- continue;
130
- }
131
-
132
- // Check if we're entering a different section
133
- if (trimmedLine.startsWith('[') && trimmedLine !== '[project]') {
134
- inProjectSection = false;
135
- continue;
136
- }
137
-
138
- // If we're in the project section, look for the name field
139
- if (inProjectSection && trimmedLine.startsWith('name')) {
140
- const match = trimmedLine.match(/name\s*=\s*["']([^"']+)["']/);
141
- if (match) {
142
- // Convert to lowercase and replace invalid characters for app naming
143
- return match[1].toLowerCase().replace(/[^a-z0-9-_]/g, '-');
144
- }
145
- }
146
- }
147
-
148
- throw new Error('Project name not found in pyproject.toml');
149
- } catch (error) {
150
- console.error('Error parsing pyproject.toml:', error);
151
- return 'unknown-app';
152
- }
153
- }
154
-
155
- // Fetch and parse pyproject.toml from the current space
156
- async function getAppNameFromCurrentSpace() {
157
- try {
158
- // Fetch pyproject.toml from the current space
159
- const response = await fetch('./pyproject.toml');
160
- if (!response.ok) {
161
- throw new Error(`Failed to fetch pyproject.toml: ${response.status}`);
162
- }
163
-
164
- const tomlContent = await response.text();
165
- return parseTomlProjectName(tomlContent);
166
- } catch (error) {
167
- console.error('Error fetching app name from current space:', error);
168
- // Fallback to extracting from URL if pyproject.toml is not accessible
169
- const url = getCurrentSpaceUrl();
170
- const parts = url.split('/');
171
- const spaceName = parts[parts.length - 1];
172
- return spaceName.toLowerCase().replace(/[^a-z0-9-_]/g, '-');
173
- }
174
- }
175
-
176
- async function installToReachy() {
177
- const dashboardUrl = document.getElementById('dashboardUrl').value.trim();
178
- const statusDiv = document.getElementById('installStatus');
179
- const installBtn = document.getElementById('installBtn');
180
-
181
- if (!dashboardUrl) {
182
- showStatus('error', 'Please enter your Reachy dashboard URL');
183
- return;
184
- }
185
-
186
- try {
187
- installBtn.disabled = true;
188
- installBtn.innerHTML = '<span class="btn-icon">⏳</span>Installing...';
189
- showStatus('loading', 'Connecting to your Reachy dashboard...');
190
-
191
- // Test connection
192
- const testResponse = await fetch(`${dashboardUrl}/api/status`, {
193
- method: 'GET',
194
- mode: 'cors',
195
- });
196
-
197
- if (!testResponse.ok) {
198
- throw new Error('Cannot connect to dashboard. Make sure the URL is correct and the dashboard is running.');
199
- }
200
-
201
- showStatus('loading', 'Reading app configuration...');
202
-
203
- // Get app name from pyproject.toml in current space
204
- const appName = await getAppNameFromCurrentSpace();
205
-
206
- // Get current space URL as repository URL
207
- const repoUrl = getCurrentSpaceUrl();
208
-
209
- showStatus('loading', `Starting installation of "${appName}"...`);
210
-
211
- // Start installation
212
- const installResponse = await fetch(`${dashboardUrl}/api/install`, {
213
- method: 'POST',
214
- mode: 'cors',
215
- headers: {
216
- 'Content-Type': 'application/json',
217
- },
218
- body: JSON.stringify({
219
- url: repoUrl,
220
- name: appName
221
- })
222
- });
223
-
224
- const result = await installResponse.json();
225
-
226
- if (installResponse.ok) {
227
- showStatus('success', `✅ Installation started for "${appName}"! Check your dashboard for progress.`);
228
- setTimeout(() => {
229
- showStatus('info', `Open your dashboard at ${dashboardUrl} to see the installed app.`);
230
- }, 3000);
231
- } else {
232
- throw new Error(result.detail || 'Installation failed');
233
- }
234
-
235
- } catch (error) {
236
- console.error('Installation error:', error);
237
- showStatus('error', `❌ ${error.message}`);
238
- } finally {
239
- installBtn.disabled = false;
240
- installBtn.innerHTML = '<span class="btn-icon">📥</span>Install App to Reachy';
241
- }
242
- }
243
-
244
- function showStatus(type, message) {
245
- const statusDiv = document.getElementById('installStatus');
246
- statusDiv.className = `install-status ${type}`;
247
- statusDiv.textContent = message;
248
- statusDiv.style.display = 'block';
249
- }
250
-
251
- function copyToClipboard() {
252
- const repoUrl = document.getElementById('repoUrl').textContent;
253
- navigator.clipboard.writeText(repoUrl).then(() => {
254
- showStatus('success', '📋 Repository URL copied to clipboard!');
255
- }).catch(() => {
256
- showStatus('error', 'Failed to copy URL. Please copy manually.');
257
- });
258
- }
259
-
260
- // Update the displayed repository URL on page load
261
- document.addEventListener('DOMContentLoaded', () => {
262
- // Auto-detect local dashboard
263
- const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
264
- if (isLocalhost) {
265
- document.getElementById('dashboardUrl').value = 'http://localhost:8000';
266
- }
267
-
268
- // Update the repository URL display if element exists
269
- const repoUrlElement = document.getElementById('repoUrl');
270
- if (repoUrlElement) {
271
- repoUrlElement.textContent = getCurrentSpaceUrl();
272
- }
273
- });
274
-
275
- // Event listeners
276
- document.getElementById('installBtn').addEventListener('click', installToReachy);
277
- </script>
278
  </body>
279
-
280
- </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
 
3
  <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Reachy Mini 3D Visualization</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
11
+ overflow: hidden;
12
+ }
13
+ #container {
14
+ width: 100vw;
15
+ height: 100vh;
16
+ }
17
+ #status {
18
+ background: rgba(0, 0, 0, 0.7);
19
+ padding: 15px;
20
+ border-radius: 5px;
21
+ font-size: 16px;
22
+ color: white;
23
+ width: 270px;
24
+ box-sizing: border-box;
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 8px;
28
+ font-family: 'VT323', monospace;
29
+ }
30
+ .connection-led {
31
+ width: 8px;
32
+ height: 8px;
33
+ border-radius: 50%;
34
+ display: inline-block;
35
+ }
36
+ .ok .connection-led { background-color: #00ff00; box-shadow: 0 0 8px #00ff00; }
37
+ .err .connection-led { background-color: #ff0000; box-shadow: 0 0 8px #ff0000; }
38
+ #data-panel {
39
+ position: absolute;
40
+ top: 10px;
41
+ left: 10px;
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: 10px;
45
+ z-index: 100;
46
+ }
47
+ #joints {
48
+ background: rgba(0, 0, 0, 0.7);
49
+ padding: 15px;
50
+ border-radius: 5px;
51
+ font-size: 16px;
52
+ width: 270px;
53
+ height: 200px;
54
+ display: flex;
55
+ flex-direction: column;
56
+ font-family: 'VT323', monospace;
57
+ box-sizing: border-box;
58
+ }
59
+ #joints h3 {
60
+ margin: 0 0 10px 0;
61
+ font-size: 11px;
62
+ font-family: 'VT323', monospace;
63
+ color: #ccc;
64
+ font-weight: normal;
65
+ }
66
+ .joint-item {
67
+ margin: 3px 0;
68
+ display: flex;
69
+ justify-content: space-between;
70
+ align-items: center;
71
+ gap: 15px;
72
+ }
73
+ .joint-name {
74
+ color: #999;
75
+ flex: 0 0 140px;
76
+ text-align: left;
77
+ white-space: nowrap;
78
+ display: flex;
79
+ align-items: center;
80
+ gap: 8px;
81
+ }
82
+ .joint-color-dot {
83
+ width: 8px;
84
+ height: 8px;
85
+ border-radius: 50%;
86
+ display: inline-block;
87
+ }
88
+ .joint-value {
89
+ font-weight: bold;
90
+ color: #ccc;
91
+ flex: 0 0 80px;
92
+ text-align: right;
93
+ }
94
+ #chart-container {
95
+ background: rgba(0, 0, 0, 0.7);
96
+ padding: 15px;
97
+ border-radius: 5px;
98
+ width: 270px;
99
+ height: 200px;
100
+ box-sizing: border-box;
101
+ }
102
+ #chart {
103
+ width: 100%;
104
+ height: 100%;
105
+ }
106
+ </style>
107
  </head>
 
108
  <body>
109
+ <div id="data-panel">
110
+ <div id="status">Loading...</div>
111
+ <div id="joints">
112
+ <div id="joints-list">Waiting for data...</div>
113
+ </div>
114
+ <div id="chart-container">
115
+ <canvas id="chart"></canvas>
116
+ </div>
117
+ </div>
118
+ <div id="container"></div>
119
+
120
+ <!-- Chart.js -->
121
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
122
+
123
+ <!-- Three.js and URDFLoader -->
124
+ <script type="importmap">
125
+ {
126
+ "imports": {
127
+ "three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
128
+ "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
129
+ }
130
+ }
131
+ </script>
132
+
133
+ <script type="module">
134
+ import { App } from '.assets/src/App.js';
135
+
136
+ // Initialize the application
137
+ new App();
138
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  </body>
140
+ </html>
 
index_old.html ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width" />
7
+ <title>Example App - Reachy Mini Template</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>Example Reachy Mini App</h1>
16
+ <p class="tagline">Template for creating your own Reachy Mini applications</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
+
29
+ <div class="app-details">
30
+ <h2>Example Template App</h2>
31
+ <div class="template-info">
32
+ <div class="info-box">
33
+ <h3>🎨 Template Purpose</h3>
34
+ <p>This is an example landing page for Reachy Mini apps. Feel free to duplicate this template and
35
+ customize it for your own applications!</p>
36
+ </div>
37
+ <div class="info-box">
38
+ <h3>🚀 Getting Started</h3>
39
+ <p>Use this template to showcase your Reachy Mini app with a landing page. Simply modify the
40
+ content, add your app's repository URL, and deploy!</p>
41
+ </div>
42
+ </div>
43
+
44
+
45
+ <div class="how-to-use">
46
+ <h3>How to Use This Template</h3>
47
+ <div class="steps">
48
+ <div class="step">
49
+ <span class="step-number">1</span>
50
+ <div>
51
+ <h4>Duplicate & Customize</h4>
52
+ <p>Copy this template and modify the content for your app</p>
53
+ </div>
54
+ </div>
55
+ <div class="step">
56
+ <span class="step-number">2</span>
57
+ <div>
58
+ <h4>Update Repository URL</h4>
59
+ <p>Change the JavaScript to point to your app's Git repository</p>
60
+ </div>
61
+ </div>
62
+ <div class="step">
63
+ <span class="step-number">3</span>
64
+ <div>
65
+ <h4>Deploy to HF Spaces</h4>
66
+ <p>Upload your customized version to Hugging Face Spaces</p>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </div>
73
+
74
+ <div class="download-section">
75
+ <div class="download-card">
76
+ <h2>Install This Example App</h2>
77
+ <p>Try out the installation process with this template app</p>
78
+
79
+ <div class="dashboard-config">
80
+ <label for="dashboardUrl">Your Reachy Dashboard URL:</label>
81
+ <input type="url" id="dashboardUrl" value="http://localhost:8000"
82
+ placeholder="http://your-reachy-ip:8000" />
83
+ </div>
84
+
85
+ <button id="installBtn" class="install-btn primary">
86
+ <span class="btn-icon">📥</span>
87
+ Install Example App to Reachy
88
+ </button>
89
+
90
+ <div id="installStatus" class="install-status"></div>
91
+
92
+ </div>
93
+ </div>
94
+
95
+ <div class="footer">
96
+ <p>
97
+ 🤖 Template for Reachy Mini Apps •
98
+ <a href="https://github.com/pollen-robotics" target="_blank">Pollen Robotics</a> •
99
+ <a href="https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Apps" target="_blank">Browse More
100
+ Apps</a>
101
+ </p>
102
+ </div>
103
+ </div>
104
+
105
+ <script>
106
+ // Get the current Hugging Face Space URL as the repository URL
107
+ function getCurrentSpaceUrl() {
108
+ // Get current page URL and convert to repository format
109
+ const currentUrl = window.location.href;
110
+
111
+ // Remove any trailing slashes and query parameters
112
+ const cleanUrl = currentUrl.split('?')[0].replace(/\/$/, '');
113
+
114
+ return cleanUrl;
115
+ }
116
+
117
+ // Parse TOML content to extract project name
118
+ function parseTomlProjectName(tomlContent) {
119
+ try {
120
+ const lines = tomlContent.split('\n');
121
+ let inProjectSection = false;
122
+
123
+ for (const line of lines) {
124
+ const trimmedLine = line.trim();
125
+
126
+ // Check if we're entering the [project] section
127
+ if (trimmedLine === '[project]') {
128
+ inProjectSection = true;
129
+ continue;
130
+ }
131
+
132
+ // Check if we're entering a different section
133
+ if (trimmedLine.startsWith('[') && trimmedLine !== '[project]') {
134
+ inProjectSection = false;
135
+ continue;
136
+ }
137
+
138
+ // If we're in the project section, look for the name field
139
+ if (inProjectSection && trimmedLine.startsWith('name')) {
140
+ const match = trimmedLine.match(/name\s*=\s*["']([^"']+)["']/);
141
+ if (match) {
142
+ // Convert to lowercase and replace invalid characters for app naming
143
+ return match[1].toLowerCase().replace(/[^a-z0-9-_]/g, '-');
144
+ }
145
+ }
146
+ }
147
+
148
+ throw new Error('Project name not found in pyproject.toml');
149
+ } catch (error) {
150
+ console.error('Error parsing pyproject.toml:', error);
151
+ return 'unknown-app';
152
+ }
153
+ }
154
+
155
+ // Fetch and parse pyproject.toml from the current space
156
+ async function getAppNameFromCurrentSpace() {
157
+ try {
158
+ // Fetch pyproject.toml from the current space
159
+ const response = await fetch('./pyproject.toml');
160
+ if (!response.ok) {
161
+ throw new Error(`Failed to fetch pyproject.toml: ${response.status}`);
162
+ }
163
+
164
+ const tomlContent = await response.text();
165
+ return parseTomlProjectName(tomlContent);
166
+ } catch (error) {
167
+ console.error('Error fetching app name from current space:', error);
168
+ // Fallback to extracting from URL if pyproject.toml is not accessible
169
+ const url = getCurrentSpaceUrl();
170
+ const parts = url.split('/');
171
+ const spaceName = parts[parts.length - 1];
172
+ return spaceName.toLowerCase().replace(/[^a-z0-9-_]/g, '-');
173
+ }
174
+ }
175
+
176
+ async function installToReachy() {
177
+ const dashboardUrl = document.getElementById('dashboardUrl').value.trim();
178
+ const statusDiv = document.getElementById('installStatus');
179
+ const installBtn = document.getElementById('installBtn');
180
+
181
+ if (!dashboardUrl) {
182
+ showStatus('error', 'Please enter your Reachy dashboard URL');
183
+ return;
184
+ }
185
+
186
+ try {
187
+ installBtn.disabled = true;
188
+ installBtn.innerHTML = '<span class="btn-icon">⏳</span>Installing...';
189
+ showStatus('loading', 'Connecting to your Reachy dashboard...');
190
+
191
+ // Test connection
192
+ const testResponse = await fetch(`${dashboardUrl}/api/status`, {
193
+ method: 'GET',
194
+ mode: 'cors',
195
+ });
196
+
197
+ if (!testResponse.ok) {
198
+ throw new Error('Cannot connect to dashboard. Make sure the URL is correct and the dashboard is running.');
199
+ }
200
+
201
+ showStatus('loading', 'Reading app configuration...');
202
+
203
+ // Get app name from pyproject.toml in current space
204
+ const appName = await getAppNameFromCurrentSpace();
205
+
206
+ // Get current space URL as repository URL
207
+ const repoUrl = getCurrentSpaceUrl();
208
+
209
+ showStatus('loading', `Starting installation of "${appName}"...`);
210
+
211
+ // Start installation
212
+ const installResponse = await fetch(`${dashboardUrl}/api/install`, {
213
+ method: 'POST',
214
+ mode: 'cors',
215
+ headers: {
216
+ 'Content-Type': 'application/json',
217
+ },
218
+ body: JSON.stringify({
219
+ url: repoUrl,
220
+ name: appName
221
+ })
222
+ });
223
+
224
+ const result = await installResponse.json();
225
+
226
+ if (installResponse.ok) {
227
+ showStatus('success', `✅ Installation started for "${appName}"! Check your dashboard for progress.`);
228
+ setTimeout(() => {
229
+ showStatus('info', `Open your dashboard at ${dashboardUrl} to see the installed app.`);
230
+ }, 3000);
231
+ } else {
232
+ throw new Error(result.detail || 'Installation failed');
233
+ }
234
+
235
+ } catch (error) {
236
+ console.error('Installation error:', error);
237
+ showStatus('error', `❌ ${error.message}`);
238
+ } finally {
239
+ installBtn.disabled = false;
240
+ installBtn.innerHTML = '<span class="btn-icon">📥</span>Install App to Reachy';
241
+ }
242
+ }
243
+
244
+ function showStatus(type, message) {
245
+ const statusDiv = document.getElementById('installStatus');
246
+ statusDiv.className = `install-status ${type}`;
247
+ statusDiv.textContent = message;
248
+ statusDiv.style.display = 'block';
249
+ }
250
+
251
+ function copyToClipboard() {
252
+ const repoUrl = document.getElementById('repoUrl').textContent;
253
+ navigator.clipboard.writeText(repoUrl).then(() => {
254
+ showStatus('success', '📋 Repository URL copied to clipboard!');
255
+ }).catch(() => {
256
+ showStatus('error', 'Failed to copy URL. Please copy manually.');
257
+ });
258
+ }
259
+
260
+ // Update the displayed repository URL on page load
261
+ document.addEventListener('DOMContentLoaded', () => {
262
+ // Auto-detect local dashboard
263
+ const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
264
+ if (isLocalhost) {
265
+ document.getElementById('dashboardUrl').value = 'http://localhost:8000';
266
+ }
267
+
268
+ // Update the repository URL display if element exists
269
+ const repoUrlElement = document.getElementById('repoUrl');
270
+ if (repoUrlElement) {
271
+ repoUrlElement.textContent = getCurrentSpaceUrl();
272
+ }
273
+ });
274
+
275
+ // Event listeners
276
+ document.getElementById('installBtn').addEventListener('click', installToReachy);
277
+ </script>
278
+ </body>
279
+
280
+ </html>