File size: 6,601 Bytes
5ae6924
bdced15
 
 
 
 
 
 
e497585
 
 
 
 
 
2e4b63b
 
 
bdced15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d884d4c
7b64182
 
 
 
 
 
 
 
 
 
bdced15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b64182
 
68dec37
 
bdced15
 
 
 
 
 
 
 
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    <title>Howdy Friend</title>
    <link rel="icon" href="/public/icon.svg" type="image/svg+xml" />

    <!-- Node global shim: the SDK host bundle references process.env.NODE_ENV and we load it unbundled from a CDN. -->
    <script>
      window.process = window.process || { env: { NODE_ENV: "production" } };
      window.global = window.global || window;
    </script>

    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link href="https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=swap" rel="stylesheet" />

    <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/@pollen-robotics/reachy-mini-sdk@1.8.0/+esm" />
    <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/@pollen-robotics/reachy-mini-sdk@1.8.0/host/dist/entry/auto.js" />
    <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/@pollen-robotics/reachy-mini-sdk@1.8.0/host/dist/entry/embed.js" />

    <!-- Theme bootstrap (host passes ?theme=dark|light when iframing us) -->
    <script>
      (function () {
        try {
          var params = new URLSearchParams(window.location.search);
          var raw = params.get("theme");
          var mode;
          if (raw === "dark" || raw === "light") mode = raw;
          else if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches === false) mode = "light";
          else mode = "dark";
          document.documentElement.setAttribute("data-theme", mode);
        } catch (_) {
          document.documentElement.setAttribute("data-theme", "dark");
        }
      })();
    </script>

    <!-- HF Spaces OAuth placeholders. Substituted by HF at serve time because hf_oauth: true. -->
    <script>
      (function () {
        var clientId = "__OAUTH_CLIENT_ID__";
        var scopes = "__OAUTH_SCOPES__";
        var spaceHost = "__SPACE_HOST__";
        var spaceId = "__SPACE_ID__";
        var looksSubstituted = clientId && clientId.indexOf("__") !== 0;
        if (looksSubstituted) {
          window.huggingface = window.huggingface || {};
          window.huggingface.variables = {
            OAUTH_CLIENT_ID: clientId,
            OAUTH_SCOPES: scopes && scopes.indexOf("__") !== 0 ? scopes : "openid profile",
            SPACE_HOST: spaceHost && spaceHost.indexOf("__") !== 0 ? spaceHost : "",
            SPACE_ID: spaceId && spaceId.indexOf("__") !== 0 ? spaceId : "",
          };
        }
      })();
    </script>

    <style>
      :root {
        --bg: #101214;
        --fg: #f3f1ea;
        --muted: #9aa0a6;
        --accent: #f2c94c;
        --ok: #2bb673;
        --panel: #1a1d20;
      }
      [data-theme="light"] {
        --bg: #f7f5ef;
        --fg: #16181a;
        --muted: #5d6368;
        --panel: #ffffff;
      }
      * { box-sizing: border-box; }
      html, body { margin: 0; height: 100%; background: var(--bg); color: var(--fg);
        font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
      #root { display: none; }
      #app { display: none; min-height: 100dvh; flex-direction: column; padding: 0.75rem;
        padding-bottom: calc(0.75rem + env(safe-area-inset-bottom)); gap: 0.75rem; }
      body.embed #app { display: flex; }
      body.host #root { display: block; }

      .stage { position: relative; width: 100%; aspect-ratio: 4 / 3; background: #000;
        border-radius: 12px; overflow: hidden; }
      .stage video, .stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
      .stage canvas { pointer-events: none; }

      .status { display: flex; align-items: center; gap: 0.6rem; font-size: 1.05rem; min-height: 1.6rem; }
      .status .dot { width: 0.7rem; height: 0.7rem; border-radius: 50%; background: var(--muted); flex: none;
        transition: background 0.2s; }
      .status.tracking .dot { background: var(--accent); }
      .status.reacting .dot { background: var(--ok); }
      .status small { color: var(--muted); }

      .controls { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
      button { min-height: 48px; border: 1px solid transparent; border-radius: 10px; background: var(--panel);
        color: var(--fg); font: inherit; font-size: 1rem; padding: 0.6rem 0.9rem; cursor: pointer; }
      button[aria-pressed="true"] { border-color: var(--accent); }
      button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
      button.wide { grid-column: 1 / -1; }

      .log { font-size: 0.8rem; color: var(--muted); white-space: pre-wrap; max-height: 14rem; overflow: auto; }

      /* Big-screen mode: camera + overlay fill the page, everything else hidden */
      body.big #app { padding: 0; gap: 0; }
      body.big .stage { position: fixed; inset: 0; width: 100vw; height: 100vh; aspect-ratio: auto; border-radius: 0; z-index: 10; }
      body.big .controls, body.big .log { display: none; }
      body.big .status { position: fixed; left: 0; right: 0; bottom: 0; z-index: 11; padding: 0.5rem 1rem; font-size: 1.2rem;
        background: linear-gradient(transparent, rgba(0,0,0,0.55)); color: #fff; }
      body.big .status small { color: #ddd; }
      .stage:fullscreen { border-radius: 0; }
      .stage:fullscreen video, .stage:fullscreen canvas { object-fit: contain; }
      @media (prefers-reduced-motion: reduce) { .status .dot { transition: none; } }
    </style>
  </head>
  <body>
    <div id="root"></div>

    <main id="app">
      <div class="stage">
        <video id="video" autoplay playsinline muted></video>
        <canvas id="overlay"></canvas>
      </div>

      <div id="status" class="status">
        <span class="dot"></span>
        <span id="statusText">Loading vision models</span>
        <small id="statusSub"></small>
      </div>

      <div class="controls">
        <button id="btnTrack" aria-pressed="true">Tracking on</button>
        <button id="btnBody" aria-pressed="true">Body turns</button>
        <button id="btnMute" aria-pressed="false">Sound on</button>
        <button id="btnWave">Test wave reaction</button>
        <button id="btnWake">Wake Reachy</button>
        <button id="btnBig">Big screen</button>
        <button id="btnLook" aria-pressed="true">Look: up</button>
        <button id="btnVoice" aria-pressed="false">Voice: off</button>
      </div>

      <div id="log" class="log"></div>
    </main>

    <script type="module" src="/app.js"></script>
  </body>
</html>