File size: 4,090 Bytes
c8a88af
61eecf2
5c85958
 
 
279ae3b
5c85958
 
 
 
 
 
 
 
 
800eea1
 
 
 
 
 
 
 
 
 
 
 
 
61eecf2
 
 
 
 
 
800eea1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c8a88af
 
 
800eea1
 
 
 
 
 
 
 
 
 
 
 
 
e409b8e
 
800eea1
e409b8e
9eab2cc
 
e409b8e
3a6d7d7
 
9eab2cc
 
3a6d7d7
9eab2cc
 
3a6d7d7
 
 
e409b8e
c8a88af
 
 
 
5c85958
 
 
 
279ae3b
 
800eea1
 
 
279ae3b
 
 
 
 
 
 
 
61eecf2
 
279ae3b
800eea1
279ae3b
 
5c85958
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { useEffect } from 'react';
import { BrowserRouter, Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom';
import { ThemeProvider, CssBaseline } from '@mui/material';
import theme from './theme/theme';
import { AppsProvider } from './context/AppsContext';
import { AuthProvider } from './context/AuthContext';

import Home from './pages/Home';
import Download from './pages/Download';
import FAQ from './pages/FAQ';
import Apps from './pages/Apps';
import Buy from './pages/Buy';
import GettingStarted from './pages/GettingStarted';
import Build from './pages/Build';

/**
 * Handle hash-to-path redirect for HuggingFace Spaces iframe embedding.
 *
 * HF propagates the parent page's hash to the iframe on initial load.
 * For example, visiting huggingface.co/reachy-mini#/apps will load the
 * iframe at *.hf.space/#/apps. This component reads that hash and
 * converts it to a BrowserRouter path (e.g. /apps).
 */
function HashRedirect() {
  const navigate = useNavigate();

  useEffect(() => {
    const hash = window.location.hash;
    // Match hash routes like #/apps, #/download, #apps, #download, etc.
    if (hash && hash.length > 1) {
      // Support both #/apps and #apps formats
      const path = hash.startsWith('#/') ? hash.slice(1) : `/${hash.slice(1)}`;
      // Use replaceState to cleanly remove hash without triggering navigation
      window.history.replaceState(null, '', window.location.pathname);
      navigate(path, { replace: true });
    }
  }, [navigate]);

  return null;
}

/**
 * Sync the current route back to the HF parent page via postMessage.
 * This updates the URL in the browser address bar so users can
 * copy/share deep links (e.g. huggingface.co/reachy-mini#/apps).
 *
 * Also handles scrollTo query parameter for anchor-like behavior.
 */
function RouteSync() {
  const location = useLocation();

  useEffect(() => {
    // Sync current path to parent frame hash (for HF Spaces embedding)
    const isInIframe = window.parent !== window;
    if (isInIframe && location.pathname !== '/') {
      window.parent.postMessage(
        { hash: `#${location.pathname}` },
        'https://huggingface.co'
      );
    } else if (isInIframe && location.pathname === '/') {
      // Clear hash when on home page
      window.parent.postMessage({ hash: '' }, 'https://huggingface.co');
    }

    // Handle scrollTo query parameter
    const params = new URLSearchParams(location.search);
    const scrollTo = params.get('scrollTo');

    if (scrollTo) {
      // Retry mechanism to wait for element to be rendered
      const scrollToElement = (retries = 0) => {
        const element = document.getElementById(scrollTo);
        if (element) {
          element.scrollIntoView({ behavior: 'smooth', block: 'center' });
        } else if (retries < 10) {
          setTimeout(() => scrollToElement(retries + 1), 100);
        }
      };
      setTimeout(() => scrollToElement(), 300);
    } else {
      window.scrollTo({ top: 0, behavior: 'smooth' });
    }
  }, [location.pathname, location.search]);

  return null;
}

export default function App() {
  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <AuthProvider>
        <AppsProvider>
          <BrowserRouter>
            <HashRedirect />
            <RouteSync />
            <Routes>
              <Route path="/" element={<Home />} />
              <Route path="/getting-started" element={<GettingStarted />} />
              <Route path="https://huggingface.co/docs/reachy_mini/" element={<Build />} />
              <Route path="/download" element={<Download />} />
              <Route path="https://huggingface.co/docs/reachy_mini/troubleshooting" element={<FAQ />} />
              <Route path="/apps" element={<Apps />} />
              <Route path="/buy" element={<Buy />} />
              {/* Catch-all: redirect unknown routes to home */}
              <Route path="*" element={<Navigate to="/" replace />} />
            </Routes>
          </BrowserRouter>
        </AppsProvider>
      </AuthProvider>
    </ThemeProvider>
  );
}