mudra-skills 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/CLAUDE.md +35 -0
  3. package/bin/install.js +25 -0
  4. package/package.json +28 -0
  5. package/skills/mudra-master/SKILL.md +202 -0
  6. package/skills/mudra-master/mudra-preview/SKILL.md +151 -0
  7. package/skills/mudra-master/mudra-preview/assets/ar-menu.html +382 -0
  8. package/skills/mudra-master/mudra-preview/assets/document-scroller.html +468 -0
  9. package/skills/mudra-master/mudra-preview/assets/drum-machine.html +788 -0
  10. package/skills/mudra-master/mudra-preview/assets/emg-visualizer.html +390 -0
  11. package/skills/mudra-master/mudra-preview/assets/generative-art.html +408 -0
  12. package/skills/mudra-master/mudra-preview/assets/gesture-assistant.html +342 -0
  13. package/skills/mudra-master/mudra-preview/assets/gesture-speech.html +444 -0
  14. package/skills/mudra-master/mudra-preview/assets/hands-free-desktop.html +376 -0
  15. package/skills/mudra-master/mudra-preview/assets/model-rotator.html +365 -0
  16. package/skills/mudra-master/mudra-preview/assets/mudra-duel.html +3200 -0
  17. package/skills/mudra-master/mudra-preview/assets/mudra-monitor.html +1451 -0
  18. package/skills/mudra-master/mudra-preview/assets/mudra-ultimate-template.html +891 -0
  19. package/skills/mudra-master/mudra-preview/assets/music-sequencer.html +455 -0
  20. package/skills/mudra-master/mudra-preview/assets/neural-pong.html +380 -0
  21. package/skills/mudra-master/mudra-preview/assets/neural-snake.html +364 -0
  22. package/skills/mudra-master/mudra-preview/assets/presentation-controller.html +345 -0
  23. package/skills/mudra-master/mudra-preview/assets/pressure-painter.html +285 -0
  24. package/skills/mudra-master/mudra-preview/assets/runner.html +1812 -0
  25. package/skills/mudra-master/mudra-preview/assets/smart-home.html +461 -0
  26. package/skills/mudra-master/mudra-preview/assets/space-invaders.html +1154 -0
  27. package/skills/mudra-master/mudra-preview/assets/waterful-ring-toss.html +1624 -0
  28. package/skills/mudra-master/mudra-preview/references/agent_protocol.json +646 -0
  29. package/skills/mudra-master/mudra-preview/references/promt.md +16829 -0
  30. package/skills/mudra-master/mudra-xr/SKILL.md +245 -0
  31. package/skills/mudra-master/mudra-xr/assets/demos/3dgs-walkthrough.html +242 -0
  32. package/skills/mudra-master/mudra-xr/assets/demos/aisimulator.html +792 -0
  33. package/skills/mudra-master/mudra-xr/assets/demos/balloonpop.html +422 -0
  34. package/skills/mudra-master/mudra-xr/assets/demos/ballpit.html +284 -0
  35. package/skills/mudra-master/mudra-xr/assets/demos/drone.html +41 -0
  36. package/skills/mudra-master/mudra-xr/assets/demos/gemini-icebreakers.html +338 -0
  37. package/skills/mudra-master/mudra-xr/assets/demos/gemini-xrobject.html +302 -0
  38. package/skills/mudra-master/mudra-xr/assets/demos/math3d.html +188 -0
  39. package/skills/mudra-master/mudra-xr/assets/demos/measure.html +247 -0
  40. package/skills/mudra-master/mudra-xr/assets/demos/occlusion.html +251 -0
  41. package/skills/mudra-master/mudra-xr/assets/demos/rain.html +498 -0
  42. package/skills/mudra-master/mudra-xr/assets/demos/screenwiper.html +432 -0
  43. package/skills/mudra-master/mudra-xr/assets/demos/splash.html +519 -0
  44. package/skills/mudra-master/mudra-xr/assets/demos/webcam_gestures.html +253 -0
  45. package/skills/mudra-master/mudra-xr/assets/demos/xremoji.html +833 -0
  46. package/skills/mudra-master/mudra-xr/assets/demos/xrpoet.html +151 -0
  47. package/skills/mudra-master/mudra-xr/assets/samples/depthmap.html +266 -0
  48. package/skills/mudra-master/mudra-xr/assets/samples/depthmesh.html +100 -0
  49. package/skills/mudra-master/mudra-xr/assets/samples/game_rps.html +1099 -0
  50. package/skills/mudra-master/mudra-xr/assets/samples/gestures_custom.html +467 -0
  51. package/skills/mudra-master/mudra-xr/assets/samples/gestures_heuristic.html +256 -0
  52. package/skills/mudra-master/mudra-xr/assets/samples/lighting.html +163 -0
  53. package/skills/mudra-master/mudra-xr/assets/samples/mesh_detection.html +46 -0
  54. package/skills/mudra-master/mudra-xr/assets/samples/modelviewer.html +183 -0
  55. package/skills/mudra-master/mudra-xr/assets/samples/paint.html +133 -0
  56. package/skills/mudra-master/mudra-xr/assets/samples/planar-vst.html +45 -0
  57. package/skills/mudra-master/mudra-xr/assets/samples/reticle.html +95 -0
  58. package/skills/mudra-master/mudra-xr/assets/samples/skybox_agent.html +384 -0
  59. package/skills/mudra-master/mudra-xr/assets/samples/sound.html +390 -0
  60. package/skills/mudra-master/mudra-xr/assets/samples/ui.html +496 -0
  61. package/skills/mudra-master/mudra-xr/assets/samples/virtual-screens.html +808 -0
  62. package/skills/mudra-master/mudra-xr/assets/templates/0_basic.html +64 -0
  63. package/skills/mudra-master/mudra-xr/assets/templates/1_ui.html +107 -0
  64. package/skills/mudra-master/mudra-xr/assets/templates/2_hands.html +183 -0
  65. package/skills/mudra-master/mudra-xr/assets/templates/3_depth.html +89 -0
  66. package/skills/mudra-master/mudra-xr/assets/templates/4_stereo.html +84 -0
  67. package/skills/mudra-master/mudra-xr/assets/templates/5_camera.html +117 -0
  68. package/skills/mudra-master/mudra-xr/assets/templates/6_ai.html +154 -0
  69. package/skills/mudra-master/mudra-xr/assets/templates/7_ai_live.html +253 -0
  70. package/skills/mudra-master/mudra-xr/assets/templates/8_objects.html +95 -0
  71. package/skills/mudra-master/mudra-xr/assets/templates/9_xr-toggle.html +90 -0
  72. package/skills/mudra-master/mudra-xr/assets/templates/heuristic_hand_gestures.html +102 -0
  73. package/skills/mudra-master/mudra-xr/assets/templates/meshes.html +47 -0
  74. package/skills/mudra-master/mudra-xr/assets/templates/planes.html +47 -0
  75. package/skills/mudra-master/mudra-xr/assets/templates/uikit.html +187 -0
  76. package/skills/mudra-master/mudra-xr/references/agent_protocol.json +878 -0
  77. package/skills/mudra-master/mudra-xr/references/promt.md +1516 -0
  78. package/skills/mudra-preview/SKILL.md +151 -0
  79. package/skills/mudra-preview/assets/ar-menu.html +382 -0
  80. package/skills/mudra-preview/assets/document-scroller.html +468 -0
  81. package/skills/mudra-preview/assets/drum-machine.html +788 -0
  82. package/skills/mudra-preview/assets/emg-visualizer.html +390 -0
  83. package/skills/mudra-preview/assets/generative-art.html +408 -0
  84. package/skills/mudra-preview/assets/gesture-assistant.html +342 -0
  85. package/skills/mudra-preview/assets/gesture-speech.html +444 -0
  86. package/skills/mudra-preview/assets/hands-free-desktop.html +376 -0
  87. package/skills/mudra-preview/assets/model-rotator.html +365 -0
  88. package/skills/mudra-preview/assets/mudra-duel.html +3200 -0
  89. package/skills/mudra-preview/assets/mudra-monitor.html +1451 -0
  90. package/skills/mudra-preview/assets/mudra-ultimate-template.html +891 -0
  91. package/skills/mudra-preview/assets/music-sequencer.html +455 -0
  92. package/skills/mudra-preview/assets/neural-pong.html +380 -0
  93. package/skills/mudra-preview/assets/neural-snake.html +364 -0
  94. package/skills/mudra-preview/assets/presentation-controller.html +345 -0
  95. package/skills/mudra-preview/assets/pressure-painter.html +285 -0
  96. package/skills/mudra-preview/assets/runner.html +1812 -0
  97. package/skills/mudra-preview/assets/smart-home.html +461 -0
  98. package/skills/mudra-preview/assets/space-invaders.html +1154 -0
  99. package/skills/mudra-preview/assets/waterful-ring-toss.html +1624 -0
  100. package/skills/mudra-preview/references/agent_protocol.json +646 -0
  101. package/skills/mudra-preview/references/promt.md +16829 -0
  102. package/skills/mudra-xr/SKILL.md +245 -0
  103. package/skills/mudra-xr/assets/demos/3dgs-walkthrough.html +242 -0
  104. package/skills/mudra-xr/assets/demos/aisimulator.html +792 -0
  105. package/skills/mudra-xr/assets/demos/balloonpop.html +422 -0
  106. package/skills/mudra-xr/assets/demos/ballpit.html +284 -0
  107. package/skills/mudra-xr/assets/demos/drone.html +41 -0
  108. package/skills/mudra-xr/assets/demos/gemini-icebreakers.html +338 -0
  109. package/skills/mudra-xr/assets/demos/gemini-xrobject.html +302 -0
  110. package/skills/mudra-xr/assets/demos/math3d.html +188 -0
  111. package/skills/mudra-xr/assets/demos/measure.html +247 -0
  112. package/skills/mudra-xr/assets/demos/occlusion.html +251 -0
  113. package/skills/mudra-xr/assets/demos/rain.html +498 -0
  114. package/skills/mudra-xr/assets/demos/screenwiper.html +432 -0
  115. package/skills/mudra-xr/assets/demos/splash.html +519 -0
  116. package/skills/mudra-xr/assets/demos/webcam_gestures.html +253 -0
  117. package/skills/mudra-xr/assets/demos/xremoji.html +833 -0
  118. package/skills/mudra-xr/assets/demos/xrpoet.html +151 -0
  119. package/skills/mudra-xr/assets/samples/depthmap.html +266 -0
  120. package/skills/mudra-xr/assets/samples/depthmesh.html +100 -0
  121. package/skills/mudra-xr/assets/samples/game_rps.html +1099 -0
  122. package/skills/mudra-xr/assets/samples/gestures_custom.html +467 -0
  123. package/skills/mudra-xr/assets/samples/gestures_heuristic.html +256 -0
  124. package/skills/mudra-xr/assets/samples/lighting.html +163 -0
  125. package/skills/mudra-xr/assets/samples/mesh_detection.html +46 -0
  126. package/skills/mudra-xr/assets/samples/modelviewer.html +183 -0
  127. package/skills/mudra-xr/assets/samples/paint.html +133 -0
  128. package/skills/mudra-xr/assets/samples/planar-vst.html +45 -0
  129. package/skills/mudra-xr/assets/samples/reticle.html +95 -0
  130. package/skills/mudra-xr/assets/samples/skybox_agent.html +384 -0
  131. package/skills/mudra-xr/assets/samples/sound.html +390 -0
  132. package/skills/mudra-xr/assets/samples/ui.html +496 -0
  133. package/skills/mudra-xr/assets/samples/virtual-screens.html +808 -0
  134. package/skills/mudra-xr/assets/templates/0_basic.html +64 -0
  135. package/skills/mudra-xr/assets/templates/1_ui.html +107 -0
  136. package/skills/mudra-xr/assets/templates/2_hands.html +183 -0
  137. package/skills/mudra-xr/assets/templates/3_depth.html +89 -0
  138. package/skills/mudra-xr/assets/templates/4_stereo.html +84 -0
  139. package/skills/mudra-xr/assets/templates/5_camera.html +117 -0
  140. package/skills/mudra-xr/assets/templates/6_ai.html +154 -0
  141. package/skills/mudra-xr/assets/templates/7_ai_live.html +253 -0
  142. package/skills/mudra-xr/assets/templates/8_objects.html +95 -0
  143. package/skills/mudra-xr/assets/templates/9_xr-toggle.html +90 -0
  144. package/skills/mudra-xr/assets/templates/heuristic_hand_gestures.html +102 -0
  145. package/skills/mudra-xr/assets/templates/meshes.html +47 -0
  146. package/skills/mudra-xr/assets/templates/planes.html +47 -0
  147. package/skills/mudra-xr/assets/templates/uikit.html +187 -0
  148. package/skills/mudra-xr/references/agent_protocol.json +878 -0
  149. package/skills/mudra-xr/references/promt.md +1516 -0
@@ -0,0 +1,382 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>AR Menu Navigator — Mudra Studio</title>
7
+ <style>
8
+ @media (prefers-reduced-motion: reduce) {
9
+ *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
10
+ }
11
+
12
+ :root {
13
+ --bg: #ffffff; --card: #f1f5f9; --primary: #0d9488; --accent: #14b8a6;
14
+ --text: #0f172a; --text-secondary: #475569; --success: #16a34a;
15
+ --warning: #ca8a04; --error: #dc2626;
16
+ --border: rgba(15,23,42,0.12); --surface: rgba(0,0,0,0.04);
17
+ --glass: rgba(241,245,249,0.75); --glass-border: rgba(13,148,136,0.2);
18
+ }
19
+
20
+ * { margin: 0; padding: 0; box-sizing: border-box; }
21
+ body { background: var(--bg); color: var(--text); font-family: 'Poppins', system-ui, sans-serif; overflow: hidden; height: 100vh; display: flex; flex-direction: column; }
22
+
23
+ .header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; background: var(--card); border-bottom: 1px solid var(--border); z-index: 200; flex-shrink: 0; gap: 8px; flex-wrap: wrap; }
24
+ .header-left, .header-right { display: flex; align-items: center; gap: 10px; }
25
+ .brand { font-weight: 700; font-size: 14px; } .brand span { color: var(--primary); }
26
+ .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--warning); box-shadow: 0 0 0 3px rgba(234,179,8,0.15); transition: all 150ms; }
27
+ .dot.live { background: var(--success); box-shadow: 0 0 0 3px rgba(34,197,94,0.18); }
28
+ .conn-label { font-size: 12px; color: var(--text-secondary); }
29
+
30
+ .btn { background: var(--surface); border: 1px solid var(--border); color: var(--text-secondary); padding: 5px 12px; border-radius: 8px; font-size: 12px; cursor: pointer; font-family: inherit; transition: border-color 150ms, color 150ms; white-space: nowrap; }
31
+ .btn:hover { border-color: var(--primary); color: var(--primary); }
32
+
33
+ .ar-viewport { flex: 1; position: relative; overflow: hidden; display: flex; align-items: center; justify-content: center; background: var(--bg);
34
+ background-image:
35
+ linear-gradient(rgba(119,234,233,0.03) 1px, transparent 1px),
36
+ linear-gradient(90deg, rgba(119,234,233,0.03) 1px, transparent 1px);
37
+ background-size: 40px 40px;
38
+ }
39
+
40
+ .breadcrumb { position: absolute; top: 16px; left: 16px; display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-secondary); z-index: 10; }
41
+ .bc-item { cursor: pointer; } .bc-item:hover { color: var(--primary); }
42
+ .bc-sep { opacity: 0.4; }
43
+ .bc-current { color: var(--primary); font-weight: 600; }
44
+
45
+ .pressure-meter { position: absolute; top: 16px; right: 16px; z-index: 10; display: flex; align-items: center; gap: 8px; }
46
+ .pm-label { font-size: 10px; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.06em; }
47
+ .pm-bar { width: 70px; height: 5px; background: var(--surface); border-radius: 999px; overflow: hidden; border: 1px solid var(--border); }
48
+ .pm-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--primary)); border-radius: 999px; transition: width 80ms; width: 0%; }
49
+ .pm-val { font-size: 11px; color: var(--text-secondary); font-variant-numeric: tabular-nums; min-width: 28px; text-align: right; }
50
+
51
+ .menu-container { transition: transform 300ms cubic-bezier(0.25,0.46,0.45,0.94); }
52
+ .menu-panel { display: flex; flex-direction: column; gap: 6px; min-width: 220px; }
53
+ .menu-item { padding: 14px 18px; border-radius: 12px; background: var(--glass); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border: 1px solid var(--glass-border); font-size: 14px; font-weight: 500; color: var(--text); cursor: default; transition: all 200ms; display: flex; align-items: center; gap: 10px; }
54
+ .mi-emoji { font-size: 18px; }
55
+ .mi-label { flex: 1; }
56
+ .mi-arrow { font-size: 12px; color: var(--text-secondary); opacity: 0.5; }
57
+ .menu-item.highlighted { border-color: var(--primary); background: rgba(119,234,233,0.08); box-shadow: 0 0 20px rgba(119,234,233,0.1); }
58
+ .menu-item.highlighted .mi-label { color: var(--primary); }
59
+
60
+ .toolbar { display: flex; align-items: center; justify-content: center; gap: 14px; padding: 10px 16px; background: var(--card); border-top: 1px solid var(--border); z-index: 200; flex-shrink: 0; flex-wrap: wrap; }
61
+ .hint { font-size: 11px; color: var(--text-secondary); opacity: 0.6; }
62
+
63
+ .mudra-badge { position: fixed; bottom: 12px; right: 12px; font-size: 11px; color: var(--text-secondary); opacity: 0.7; letter-spacing: 0.02em; pointer-events: none; z-index: 300; }
64
+
65
+ @media (max-width: 640px) {
66
+ .header { padding: 8px 10px; } .toolbar { gap: 8px; padding: 8px 10px; }
67
+ .menu-panel { min-width: 170px; }
68
+ .menu-item { padding: 10px 14px; font-size: 13px; }
69
+ .hint { display: none; }
70
+ .pressure-meter { display: none; }
71
+ }
72
+ </style>
73
+ </head>
74
+ <body>
75
+
76
+ <div class="header">
77
+ <div class="header-left">
78
+ <div class="brand"><span>Mudra</span> AR Menu Navigator</div>
79
+ <span class="dot" id="dot"></span>
80
+ <span class="conn-label" id="connLabel">Connecting&hellip;</span>
81
+ </div>
82
+ <div class="header-right">
83
+ <a href="/gallery" class="btn" style="text-decoration:none;">&#8592; Gallery</a>
84
+ <button class="btn" id="simTap">Simulate Tap</button>
85
+ <button class="btn" id="simTwist">Simulate Twist</button>
86
+ </div>
87
+ </div>
88
+
89
+ <div class="ar-viewport" id="viewport">
90
+ <div class="breadcrumb" id="breadcrumb"></div>
91
+ <div class="pressure-meter">
92
+ <span class="pm-label">Scale</span>
93
+ <div class="pm-bar"><div class="pm-fill" id="pressFill"></div></div>
94
+ <span class="pm-val" id="pressVal">0%</span>
95
+ </div>
96
+ <div class="menu-container" id="menuContainer"></div>
97
+ </div>
98
+
99
+ <div class="toolbar">
100
+ <button class="btn" id="selectBtn">Select</button>
101
+ <button class="btn" id="backBtn">Back</button>
102
+ <button class="btn" id="homeBtn">Home</button>
103
+ <span class="hint">Arrows navigate &middot; Space select &middot; Backspace back &middot; [ ] pressure</span>
104
+ </div>
105
+
106
+ <div class="mudra-badge">Created by Mudra</div>
107
+
108
+ <script>
109
+ /* ── Mudra Protocol Connection (v2) ────────────────────────── */
110
+ const WS_URL = 'ws://127.0.0.1:8766';
111
+ const SIGNALS = ['navigation', 'gesture', 'pressure'];
112
+
113
+ let ws, reconnectTimer, simTimer;
114
+ let wsOpen = false, deviceReady = false, simulating = true;
115
+ const bus = {};
116
+
117
+ function on(ev, fn) { (bus[ev] ??= []).push(fn); }
118
+ function emit(ev, d) { (bus[ev] || []).forEach(fn => fn(d)); }
119
+ function send(p) { if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(p)); }
120
+
121
+ function connect() {
122
+ try {
123
+ ws = new WebSocket(WS_URL);
124
+ ws.onopen = () => {
125
+ wsOpen = true; stopSim(); updateStatus();
126
+ SIGNALS.forEach(s => send({ command: 'subscribe', signal: s }));
127
+ };
128
+ ws.onmessage = (e) => {
129
+ try {
130
+ const msg = JSON.parse(e.data);
131
+ if (msg.type === 'connection_status') {
132
+ deviceReady = msg.data?.status === 'connected';
133
+ updateStatus(); return;
134
+ }
135
+ emit(msg.type, msg.data);
136
+ } catch {}
137
+ };
138
+ ws.onclose = () => {
139
+ wsOpen = false; deviceReady = false; updateStatus();
140
+ startSim();
141
+ clearTimeout(reconnectTimer);
142
+ reconnectTimer = setTimeout(connect, 1500);
143
+ };
144
+ ws.onerror = () => ws?.close();
145
+ } catch {
146
+ wsOpen = false; startSim();
147
+ clearTimeout(reconnectTimer);
148
+ reconnectTimer = setTimeout(connect, 1500);
149
+ }
150
+ }
151
+
152
+ function triggerGesture(type) {
153
+ if (wsOpen) send({ command: 'trigger_gesture', data: { type } });
154
+ else emit('gesture', { type, confidence: 0.95, timestamp: Date.now() });
155
+ }
156
+
157
+ /* ── Simulation (when Companion not reachable) ─────────────── */
158
+ function startSim() {
159
+ if (simTimer) return;
160
+ simulating = true;
161
+ let t = 0, navAcc = 0;
162
+ simTimer = setInterval(() => {
163
+ t += 0.05;
164
+ navAcc += 0.02;
165
+ if (navAcc > 1) {
166
+ navAcc = 0;
167
+ emit('navigation', { delta_x: 0, delta_y: 6, timestamp: Date.now() });
168
+ }
169
+ const n = 0.3 + 0.25 * Math.sin(t * 0.4) + (Math.random() - 0.5) * 0.05;
170
+ emit('pressure', { value: Math.round(n * 100), normalized: Math.max(0, Math.min(1, n)), timestamp: Date.now() });
171
+ if (Math.random() < 0.004) {
172
+ const g = ['tap', 'double_tap', 'twist', 'double_twist'];
173
+ emit('gesture', { type: g[Math.floor(Math.random() * g.length)], confidence: 0.7 + Math.random() * 0.3, timestamp: Date.now() });
174
+ }
175
+ }, 60);
176
+ }
177
+
178
+ function stopSim() { clearInterval(simTimer); simTimer = null; simulating = false; }
179
+
180
+ /* ── Status UI ─────────────────────────────────────────────── */
181
+ const dotEl = document.getElementById('dot');
182
+ const connEl = document.getElementById('connLabel');
183
+
184
+ function updateStatus() {
185
+ if (wsOpen && deviceReady) { dotEl.className = 'dot live'; connEl.textContent = 'Connected'; }
186
+ else if (wsOpen) { dotEl.className = 'dot'; connEl.textContent = 'Waiting for Mudra Band\u2026'; }
187
+ else { dotEl.className = 'dot'; connEl.textContent = simulating ? 'Simulated' : 'Disconnected'; }
188
+ }
189
+
190
+ /* ── Menu Data ─────────────────────────────────────────────── */
191
+ const menuTree = {
192
+ label: 'Home',
193
+ items: [
194
+ { label: 'Maps', emoji: '\u{1F5FA}\uFE0F', children: [
195
+ { label: 'Navigate' }, { label: 'Search Places' }, { label: 'Saved Locations' }, { label: 'Traffic' }
196
+ ]},
197
+ { label: 'Music', emoji: '\u{1F3B5}', children: [
198
+ { label: 'Now Playing' }, { label: 'Playlists' }, { label: 'Search' }
199
+ ]},
200
+ { label: 'Messages', emoji: '\u{1F4AC}', children: [
201
+ { label: 'Inbox' }, { label: 'Compose' }, { label: 'Starred' }, { label: 'Archive' }
202
+ ]},
203
+ { label: 'Camera', emoji: '\u{1F4F7}', children: [
204
+ { label: 'Photo' }, { label: 'Video' }, { label: 'Gallery' }
205
+ ]},
206
+ { label: 'Settings', emoji: '\u2699\uFE0F', children: [
207
+ { label: 'Display' }, { label: 'Gestures' }, { label: 'Connectivity' }, { label: 'About' }
208
+ ]},
209
+ ]
210
+ };
211
+
212
+ /* ── Menu State ────────────────────────────────────────────── */
213
+ let menuPath = [];
214
+ let currentItems = menuTree.items;
215
+ let highlightIdx = 0;
216
+ let pressure = 0.3;
217
+ let navAccY = 0;
218
+
219
+ const containerEl = document.getElementById('menuContainer');
220
+ const breadcrumbEl = document.getElementById('breadcrumb');
221
+ const pressFillEl = document.getElementById('pressFill');
222
+ const pressValEl = document.getElementById('pressVal');
223
+
224
+ function renderMenu() {
225
+ /* Clear old panel */
226
+ while (containerEl.firstChild) containerEl.removeChild(containerEl.firstChild);
227
+
228
+ const panel = document.createElement('div');
229
+ panel.className = 'menu-panel';
230
+
231
+ currentItems.forEach((item, i) => {
232
+ const el = document.createElement('div');
233
+ el.className = 'menu-item' + (i === highlightIdx ? ' highlighted' : '');
234
+
235
+ const emo = document.createElement('span');
236
+ emo.className = 'mi-emoji';
237
+ emo.textContent = item.emoji || '\u2022';
238
+
239
+ const lab = document.createElement('span');
240
+ lab.className = 'mi-label';
241
+ lab.textContent = item.label;
242
+
243
+ el.appendChild(emo);
244
+ el.appendChild(lab);
245
+
246
+ if (item.children) {
247
+ const arr = document.createElement('span');
248
+ arr.className = 'mi-arrow';
249
+ arr.textContent = '\u25B6';
250
+ el.appendChild(arr);
251
+ }
252
+
253
+ el.addEventListener('click', () => { highlightIdx = i; selectItem(); });
254
+ panel.appendChild(el);
255
+ });
256
+
257
+ containerEl.appendChild(panel);
258
+ applyScale();
259
+ renderBreadcrumb();
260
+ }
261
+
262
+ function renderBreadcrumb() {
263
+ while (breadcrumbEl.firstChild) breadcrumbEl.removeChild(breadcrumbEl.firstChild);
264
+
265
+ const home = document.createElement('span');
266
+ home.className = 'bc-item';
267
+ home.textContent = 'Home';
268
+ home.addEventListener('click', goHome);
269
+ breadcrumbEl.appendChild(home);
270
+
271
+ menuPath.forEach((entry, i) => {
272
+ const sep = document.createElement('span');
273
+ sep.className = 'bc-sep';
274
+ sep.textContent = ' \u203A ';
275
+ breadcrumbEl.appendChild(sep);
276
+
277
+ const crumb = document.createElement('span');
278
+ crumb.textContent = entry.node.label;
279
+ if (i === menuPath.length - 1) {
280
+ crumb.className = 'bc-current';
281
+ } else {
282
+ crumb.className = 'bc-item';
283
+ crumb.addEventListener('click', () => {
284
+ menuPath = menuPath.slice(0, i + 1);
285
+ currentItems = menuPath[menuPath.length - 1].node.children;
286
+ highlightIdx = 0;
287
+ renderMenu();
288
+ });
289
+ }
290
+ breadcrumbEl.appendChild(crumb);
291
+ });
292
+ }
293
+
294
+ function applyScale() {
295
+ const scale = 0.75 + pressure * 0.5;
296
+ containerEl.style.transform = 'scale(' + scale + ')';
297
+ }
298
+
299
+ function moveHighlight(dir) {
300
+ highlightIdx = (highlightIdx + dir + currentItems.length) % currentItems.length;
301
+ const items = containerEl.querySelectorAll('.menu-item');
302
+ items.forEach((el, i) => el.classList.toggle('highlighted', i === highlightIdx));
303
+ }
304
+
305
+ function selectItem() {
306
+ const item = currentItems[highlightIdx];
307
+ if (item && item.children) {
308
+ menuPath.push({ node: item, selectedIdx: highlightIdx });
309
+ currentItems = item.children;
310
+ highlightIdx = 0;
311
+ renderMenu();
312
+ }
313
+ }
314
+
315
+ function goBack() {
316
+ if (menuPath.length === 0) return;
317
+ const prev = menuPath.pop();
318
+ currentItems = menuPath.length === 0 ? menuTree.items : menuPath[menuPath.length - 1].node.children;
319
+ highlightIdx = prev.selectedIdx;
320
+ renderMenu();
321
+ }
322
+
323
+ function goHome() {
324
+ menuPath = [];
325
+ currentItems = menuTree.items;
326
+ highlightIdx = 0;
327
+ renderMenu();
328
+ }
329
+
330
+ /* ── Signal Handlers ───────────────────────────────────────── */
331
+ on('navigation', (d) => {
332
+ navAccY += (d.delta_y || 0);
333
+ if (Math.abs(navAccY) > 12) {
334
+ moveHighlight(navAccY > 0 ? 1 : -1);
335
+ navAccY = 0;
336
+ }
337
+ });
338
+
339
+ on('gesture', (d) => {
340
+ const t = d.type || '';
341
+ if (t === 'tap') selectItem();
342
+ if (t === 'twist') goBack();
343
+ if (t === 'double_tap') goHome();
344
+ });
345
+
346
+ on('pressure', (d) => {
347
+ pressure = Math.max(0, Math.min(1, d.normalized ?? d.value / 100));
348
+ pressFillEl.style.width = Math.round(pressure * 100) + '%';
349
+ pressValEl.textContent = Math.round(pressure * 100) + '%';
350
+ applyScale();
351
+ });
352
+
353
+ /* ── Keyboard Fallback ─────────────────────────────────────── */
354
+ let kbPressure = 0.3;
355
+ document.addEventListener('keydown', (e) => {
356
+ if (e.code === 'ArrowDown') moveHighlight(1);
357
+ if (e.code === 'ArrowUp') moveHighlight(-1);
358
+ if (e.code === 'ArrowRight' || e.code === 'Space') { e.preventDefault(); selectItem(); }
359
+ if (e.code === 'ArrowLeft' || e.code === 'Backspace') { e.preventDefault(); goBack(); }
360
+ if (e.key === ']') {
361
+ kbPressure = Math.min(1, kbPressure + 0.08);
362
+ emit('pressure', { value: Math.round(kbPressure * 100), normalized: kbPressure, timestamp: Date.now() });
363
+ }
364
+ if (e.key === '[') {
365
+ kbPressure = Math.max(0, kbPressure - 0.08);
366
+ emit('pressure', { value: Math.round(kbPressure * 100), normalized: kbPressure, timestamp: Date.now() });
367
+ }
368
+ });
369
+
370
+ /* ── Buttons ───────────────────────────────────────────────── */
371
+ document.getElementById('simTap').addEventListener('click', () => triggerGesture('tap'));
372
+ document.getElementById('simTwist').addEventListener('click', () => triggerGesture('twist'));
373
+ document.getElementById('selectBtn').addEventListener('click', selectItem);
374
+ document.getElementById('backBtn').addEventListener('click', goBack);
375
+ document.getElementById('homeBtn').addEventListener('click', goHome);
376
+
377
+ /* ── Init ──────────────────────────────────────────────────── */
378
+ renderMenu();
379
+ connect();
380
+ </script>
381
+ </body>
382
+ </html>