mudra-skills 3.0.2 → 3.0.4

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 (146) hide show
  1. package/package.json +1 -1
  2. package/skills/mudra-master/SKILL.md +0 -196
  3. package/skills/mudra-master/mudra-preview/SKILL.md +0 -150
  4. package/skills/mudra-master/mudra-preview/assets/ar-menu.html +0 -382
  5. package/skills/mudra-master/mudra-preview/assets/document-scroller.html +0 -468
  6. package/skills/mudra-master/mudra-preview/assets/drum-machine.html +0 -788
  7. package/skills/mudra-master/mudra-preview/assets/emg-visualizer.html +0 -390
  8. package/skills/mudra-master/mudra-preview/assets/generative-art.html +0 -408
  9. package/skills/mudra-master/mudra-preview/assets/gesture-assistant.html +0 -342
  10. package/skills/mudra-master/mudra-preview/assets/gesture-speech.html +0 -444
  11. package/skills/mudra-master/mudra-preview/assets/hands-free-desktop.html +0 -376
  12. package/skills/mudra-master/mudra-preview/assets/model-rotator.html +0 -365
  13. package/skills/mudra-master/mudra-preview/assets/mudra-duel.html +0 -3200
  14. package/skills/mudra-master/mudra-preview/assets/mudra-monitor.html +0 -1451
  15. package/skills/mudra-master/mudra-preview/assets/mudra-ultimate-template.html +0 -891
  16. package/skills/mudra-master/mudra-preview/assets/music-sequencer.html +0 -455
  17. package/skills/mudra-master/mudra-preview/assets/neural-pong.html +0 -380
  18. package/skills/mudra-master/mudra-preview/assets/neural-snake.html +0 -364
  19. package/skills/mudra-master/mudra-preview/assets/presentation-controller.html +0 -345
  20. package/skills/mudra-master/mudra-preview/assets/pressure-painter.html +0 -285
  21. package/skills/mudra-master/mudra-preview/assets/runner.html +0 -1812
  22. package/skills/mudra-master/mudra-preview/assets/smart-home.html +0 -461
  23. package/skills/mudra-master/mudra-preview/assets/space-invaders.html +0 -1154
  24. package/skills/mudra-master/mudra-preview/assets/waterful-ring-toss.html +0 -1624
  25. package/skills/mudra-master/mudra-preview/references/agent_protocol.json +0 -634
  26. package/skills/mudra-master/mudra-preview/references/prompt.md +0 -16746
  27. package/skills/mudra-master/mudra-xr/SKILL.md +0 -245
  28. package/skills/mudra-master/mudra-xr/assets/demos/3dgs-walkthrough.html +0 -242
  29. package/skills/mudra-master/mudra-xr/assets/demos/aisimulator.html +0 -792
  30. package/skills/mudra-master/mudra-xr/assets/demos/balloonpop.html +0 -422
  31. package/skills/mudra-master/mudra-xr/assets/demos/ballpit.html +0 -284
  32. package/skills/mudra-master/mudra-xr/assets/demos/drone.html +0 -41
  33. package/skills/mudra-master/mudra-xr/assets/demos/gemini-icebreakers.html +0 -338
  34. package/skills/mudra-master/mudra-xr/assets/demos/gemini-xrobject.html +0 -302
  35. package/skills/mudra-master/mudra-xr/assets/demos/math3d.html +0 -188
  36. package/skills/mudra-master/mudra-xr/assets/demos/measure.html +0 -247
  37. package/skills/mudra-master/mudra-xr/assets/demos/occlusion.html +0 -251
  38. package/skills/mudra-master/mudra-xr/assets/demos/rain.html +0 -498
  39. package/skills/mudra-master/mudra-xr/assets/demos/screenwiper.html +0 -432
  40. package/skills/mudra-master/mudra-xr/assets/demos/splash.html +0 -519
  41. package/skills/mudra-master/mudra-xr/assets/demos/webcam_gestures.html +0 -253
  42. package/skills/mudra-master/mudra-xr/assets/demos/xremoji.html +0 -833
  43. package/skills/mudra-master/mudra-xr/assets/demos/xrpoet.html +0 -151
  44. package/skills/mudra-master/mudra-xr/assets/samples/depthmap.html +0 -266
  45. package/skills/mudra-master/mudra-xr/assets/samples/depthmesh.html +0 -100
  46. package/skills/mudra-master/mudra-xr/assets/samples/game_rps.html +0 -1099
  47. package/skills/mudra-master/mudra-xr/assets/samples/gestures_custom.html +0 -467
  48. package/skills/mudra-master/mudra-xr/assets/samples/gestures_heuristic.html +0 -256
  49. package/skills/mudra-master/mudra-xr/assets/samples/lighting.html +0 -163
  50. package/skills/mudra-master/mudra-xr/assets/samples/mesh_detection.html +0 -46
  51. package/skills/mudra-master/mudra-xr/assets/samples/modelviewer.html +0 -183
  52. package/skills/mudra-master/mudra-xr/assets/samples/paint.html +0 -133
  53. package/skills/mudra-master/mudra-xr/assets/samples/planar-vst.html +0 -45
  54. package/skills/mudra-master/mudra-xr/assets/samples/reticle.html +0 -95
  55. package/skills/mudra-master/mudra-xr/assets/samples/skybox_agent.html +0 -384
  56. package/skills/mudra-master/mudra-xr/assets/samples/sound.html +0 -390
  57. package/skills/mudra-master/mudra-xr/assets/samples/ui.html +0 -496
  58. package/skills/mudra-master/mudra-xr/assets/samples/virtual-screens.html +0 -808
  59. package/skills/mudra-master/mudra-xr/assets/templates/0_basic.html +0 -64
  60. package/skills/mudra-master/mudra-xr/assets/templates/1_ui.html +0 -107
  61. package/skills/mudra-master/mudra-xr/assets/templates/2_hands.html +0 -183
  62. package/skills/mudra-master/mudra-xr/assets/templates/3_depth.html +0 -89
  63. package/skills/mudra-master/mudra-xr/assets/templates/4_stereo.html +0 -84
  64. package/skills/mudra-master/mudra-xr/assets/templates/5_camera.html +0 -117
  65. package/skills/mudra-master/mudra-xr/assets/templates/6_ai.html +0 -154
  66. package/skills/mudra-master/mudra-xr/assets/templates/7_ai_live.html +0 -253
  67. package/skills/mudra-master/mudra-xr/assets/templates/8_objects.html +0 -95
  68. package/skills/mudra-master/mudra-xr/assets/templates/9_xr-toggle.html +0 -90
  69. package/skills/mudra-master/mudra-xr/assets/templates/heuristic_hand_gestures.html +0 -102
  70. package/skills/mudra-master/mudra-xr/assets/templates/meshes.html +0 -47
  71. package/skills/mudra-master/mudra-xr/assets/templates/planes.html +0 -47
  72. package/skills/mudra-master/mudra-xr/assets/templates/uikit.html +0 -187
  73. package/skills/mudra-master/mudra-xr/references/agent_protocol.json +0 -865
  74. package/skills/mudra-master/mudra-xr/references/prompt.md +0 -1605
  75. package/skills/mudra-preview/SKILL.md +0 -159
  76. package/skills/mudra-preview/assets/ar-menu.html +0 -382
  77. package/skills/mudra-preview/assets/document-scroller.html +0 -468
  78. package/skills/mudra-preview/assets/drum-machine.html +0 -788
  79. package/skills/mudra-preview/assets/emg-visualizer.html +0 -390
  80. package/skills/mudra-preview/assets/generative-art.html +0 -408
  81. package/skills/mudra-preview/assets/gesture-assistant.html +0 -342
  82. package/skills/mudra-preview/assets/gesture-speech.html +0 -444
  83. package/skills/mudra-preview/assets/hands-free-desktop.html +0 -376
  84. package/skills/mudra-preview/assets/model-rotator.html +0 -365
  85. package/skills/mudra-preview/assets/mudra-duel.html +0 -3200
  86. package/skills/mudra-preview/assets/mudra-monitor.html +0 -1451
  87. package/skills/mudra-preview/assets/mudra-ultimate-template.html +0 -997
  88. package/skills/mudra-preview/assets/music-sequencer.html +0 -455
  89. package/skills/mudra-preview/assets/neural-pong.html +0 -380
  90. package/skills/mudra-preview/assets/neural-snake.html +0 -364
  91. package/skills/mudra-preview/assets/presentation-controller.html +0 -345
  92. package/skills/mudra-preview/assets/pressure-painter.html +0 -285
  93. package/skills/mudra-preview/assets/runner.html +0 -1812
  94. package/skills/mudra-preview/assets/smart-home.html +0 -461
  95. package/skills/mudra-preview/assets/space-invaders.html +0 -1154
  96. package/skills/mudra-preview/assets/waterful-ring-toss.html +0 -1624
  97. package/skills/mudra-preview/references/agent_protocol.json +0 -701
  98. package/skills/mudra-preview/references/prompt.md +0 -16731
  99. package/skills/mudra-xr/SKILL.md +0 -245
  100. package/skills/mudra-xr/assets/demos/3dgs-walkthrough.html +0 -242
  101. package/skills/mudra-xr/assets/demos/aisimulator.html +0 -792
  102. package/skills/mudra-xr/assets/demos/balloonpop.html +0 -422
  103. package/skills/mudra-xr/assets/demos/ballpit.html +0 -284
  104. package/skills/mudra-xr/assets/demos/drone.html +0 -41
  105. package/skills/mudra-xr/assets/demos/gemini-icebreakers.html +0 -338
  106. package/skills/mudra-xr/assets/demos/gemini-xrobject.html +0 -302
  107. package/skills/mudra-xr/assets/demos/math3d.html +0 -188
  108. package/skills/mudra-xr/assets/demos/measure.html +0 -247
  109. package/skills/mudra-xr/assets/demos/occlusion.html +0 -251
  110. package/skills/mudra-xr/assets/demos/rain.html +0 -498
  111. package/skills/mudra-xr/assets/demos/screenwiper.html +0 -432
  112. package/skills/mudra-xr/assets/demos/splash.html +0 -519
  113. package/skills/mudra-xr/assets/demos/webcam_gestures.html +0 -253
  114. package/skills/mudra-xr/assets/demos/xremoji.html +0 -833
  115. package/skills/mudra-xr/assets/demos/xrpoet.html +0 -151
  116. package/skills/mudra-xr/assets/samples/depthmap.html +0 -266
  117. package/skills/mudra-xr/assets/samples/depthmesh.html +0 -100
  118. package/skills/mudra-xr/assets/samples/game_rps.html +0 -1099
  119. package/skills/mudra-xr/assets/samples/gestures_custom.html +0 -467
  120. package/skills/mudra-xr/assets/samples/gestures_heuristic.html +0 -256
  121. package/skills/mudra-xr/assets/samples/lighting.html +0 -163
  122. package/skills/mudra-xr/assets/samples/mesh_detection.html +0 -46
  123. package/skills/mudra-xr/assets/samples/modelviewer.html +0 -183
  124. package/skills/mudra-xr/assets/samples/paint.html +0 -133
  125. package/skills/mudra-xr/assets/samples/planar-vst.html +0 -45
  126. package/skills/mudra-xr/assets/samples/reticle.html +0 -95
  127. package/skills/mudra-xr/assets/samples/skybox_agent.html +0 -384
  128. package/skills/mudra-xr/assets/samples/sound.html +0 -390
  129. package/skills/mudra-xr/assets/samples/ui.html +0 -496
  130. package/skills/mudra-xr/assets/samples/virtual-screens.html +0 -808
  131. package/skills/mudra-xr/assets/templates/0_basic.html +0 -64
  132. package/skills/mudra-xr/assets/templates/1_ui.html +0 -107
  133. package/skills/mudra-xr/assets/templates/2_hands.html +0 -183
  134. package/skills/mudra-xr/assets/templates/3_depth.html +0 -89
  135. package/skills/mudra-xr/assets/templates/4_stereo.html +0 -84
  136. package/skills/mudra-xr/assets/templates/5_camera.html +0 -117
  137. package/skills/mudra-xr/assets/templates/6_ai.html +0 -154
  138. package/skills/mudra-xr/assets/templates/7_ai_live.html +0 -253
  139. package/skills/mudra-xr/assets/templates/8_objects.html +0 -95
  140. package/skills/mudra-xr/assets/templates/9_xr-toggle.html +0 -90
  141. package/skills/mudra-xr/assets/templates/heuristic_hand_gestures.html +0 -102
  142. package/skills/mudra-xr/assets/templates/meshes.html +0 -47
  143. package/skills/mudra-xr/assets/templates/planes.html +0 -47
  144. package/skills/mudra-xr/assets/templates/uikit.html +0 -187
  145. package/skills/mudra-xr/references/agent_protocol.json +0 -865
  146. package/skills/mudra-xr/references/prompt.md +0 -1605
@@ -1,345 +0,0 @@
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>Presentation Controller — 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
- --slide-bg: #f8fafc;
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: 100; 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
- .slide-container { flex: 1; position: relative; overflow: hidden; background: var(--slide-bg); }
34
- .slide { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 40px 60px; transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.5s; text-align: center; }
35
- .slide.active { transform: translateX(0); opacity: 1; }
36
- .slide.left { transform: translateX(-100%); opacity: 0; }
37
- .slide.right { transform: translateX(100%); opacity: 0; }
38
- .slide h1 { font-size: clamp(28px, 5vw, 52px); font-weight: 800; margin-bottom: 16px; line-height: 1.15; }
39
- .slide h1 em { font-style: normal; color: var(--primary); }
40
- .slide p { font-size: clamp(14px, 2vw, 18px); color: var(--text-secondary); max-width: 600px; line-height: 1.7; }
41
- .slide .tag { display: inline-block; background: rgba(119,234,233,0.1); color: var(--primary); padding: 4px 14px; border-radius: 20px; font-size: 13px; margin-bottom: 14px; font-weight: 600; border: 1px solid rgba(119,234,233,0.15); }
42
- .slide ul { list-style: none; display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 20px; }
43
- .slide ul li { background: var(--surface); border: 1px solid var(--border); padding: 10px 18px; border-radius: 10px; font-size: 14px; color: var(--text-secondary); }
44
-
45
- .laser { position: fixed; border-radius: 50%; background: var(--error); box-shadow: 0 0 16px var(--error), 0 0 40px rgba(239,68,68,0.35); pointer-events: none; z-index: 90; opacity: 0; transition: opacity 0.15s; transform: translate(-50%, -50%); }
46
-
47
- .toolbar { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 10px 16px; background: var(--card); border-top: 1px solid var(--border); z-index: 100; flex-shrink: 0; flex-wrap: wrap; }
48
- .toolbar-left, .toolbar-right { display: flex; align-items: center; gap: 12px; }
49
- .nav-dots { display: flex; gap: 6px; }
50
- .nav-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--surface); border: 1px solid var(--border); cursor: pointer; transition: background 150ms, border-color 150ms; }
51
- .nav-dot.active { background: var(--primary); border-color: var(--primary); }
52
- .slide-counter { font-size: 13px; color: var(--text-secondary); font-variant-numeric: tabular-nums; min-width: 48px; text-align: center; }
53
- .meter { display: flex; align-items: center; gap: 8px; }
54
- .meter-bar { width: 80px; height: 8px; background: var(--surface); border-radius: 999px; overflow: hidden; border: 1px solid var(--border); }
55
- .meter-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--primary)); border-radius: 999px; transition: width 75ms linear; width: 0%; }
56
- .meter-val { font-size: 12px; color: var(--text-secondary); min-width: 32px; text-align: right; font-variant-numeric: tabular-nums; }
57
- .laser-badge { font-size: 11px; color: var(--error); font-weight: 600; opacity: 0; transition: opacity 150ms; }
58
- .laser-badge.active { opacity: 1; }
59
- .hint { font-size: 11px; color: var(--text-secondary); opacity: 0.6; }
60
-
61
- .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: 200; }
62
-
63
- @media (max-width: 640px) {
64
- .header { padding: 8px 10px; } .toolbar { gap: 8px; padding: 8px 10px; }
65
- .slide { padding: 24px 20px; } .meter-bar { width: 50px; } .hint { display: none; }
66
- }
67
- </style>
68
- </head>
69
- <body>
70
-
71
- <div class="header">
72
- <div class="header-left">
73
- <div class="brand"><span>Mudra</span> Presentation Controller</div>
74
- <span class="dot" id="dot"></span>
75
- <span class="conn-label" id="connLabel">Connecting&hellip;</span>
76
- </div>
77
- <div class="header-right">
78
- <a href="/gallery" class="btn" style="text-decoration:none;">&#8592; Gallery</a>
79
- <button class="btn" id="simTap">Simulate Tap</button>
80
- <button class="btn" id="simTwist">Simulate Twist</button>
81
- </div>
82
- </div>
83
-
84
- <div class="slide-container" id="slideContainer">
85
- <div class="slide active" id="slide0">
86
- <div class="tag">Welcome</div>
87
- <h1>Present with <em>Neural Input</em></h1>
88
- <p>Control your presentations using the Mudra wristband. No clicker, no remote — just natural gestures from your hand.</p>
89
- </div>
90
- <div class="slide right" id="slide1">
91
- <div class="tag">The Problem</div>
92
- <h1>Clickers Are <em>Outdated</em></h1>
93
- <p>Physical remotes get lost, run out of batteries, and limit your movement. Neural input frees you from hardware constraints entirely.</p>
94
- </div>
95
- <div class="slide right" id="slide2">
96
- <div class="tag">Technology</div>
97
- <h1>How <em>Mudra</em> Works</h1>
98
- <p>The Mudra wristband reads surface nerve signals (sEMG) from your wrist, detecting micro-gestures with millisecond latency and zero physical movement required.</p>
99
- </div>
100
- <div class="slide right" id="slide3">
101
- <div class="tag">Gestures</div>
102
- <h1>Gesture <em>Controls</em></h1>
103
- <p>Intuitive mapping of neural gestures to presentation actions. All customizable via the API.</p>
104
- <ul>
105
- <li>Tap: Next Slide</li>
106
- <li>Twist: Previous</li>
107
- <li>Double Tap: Laser Pointer</li>
108
- <li>Double Twist: Reset</li>
109
- </ul>
110
- </div>
111
- <div class="slide right" id="slide4">
112
- <div class="tag">Get Started</div>
113
- <h1>Join the <em>Waitlist</em></h1>
114
- <p>Be among the first developers to build with neural input APIs. Visit mudrastudio.dev to register for early access and start building the future of hands-free interaction.</p>
115
- </div>
116
- </div>
117
-
118
- <div class="laser" id="laser"></div>
119
-
120
- <div class="toolbar">
121
- <div class="toolbar-left">
122
- <div class="nav-dots" id="navDots"></div>
123
- <span class="slide-counter" id="slideCounter">1 / 5</span>
124
- <span class="laser-badge" id="laserBadge">LASER</span>
125
- </div>
126
- <div class="toolbar-right">
127
- <div class="meter">
128
- <span class="meter-val" id="pressVal">0%</span>
129
- <div class="meter-bar"><div class="meter-fill" id="pressFill"></div></div>
130
- </div>
131
- <span class="hint">Arrow keys &middot; Space tap &middot; L laser &middot; [ ] size</span>
132
- </div>
133
- </div>
134
-
135
- <div class="mudra-badge">Created by Mudra</div>
136
-
137
- <script>
138
- /* ── Mudra Protocol Connection (v2) ────────────────────────── */
139
- const WS_URL = 'ws://127.0.0.1:8766';
140
- const SIGNALS = ['gesture', 'navigation', 'pressure'];
141
-
142
- let ws, reconnectTimer, simTimer;
143
- let wsOpen = false, deviceReady = false, simulating = true;
144
- const bus = {};
145
-
146
- function on(ev, fn) { (bus[ev] ??= []).push(fn); }
147
- function emit(ev, d) { (bus[ev] || []).forEach(fn => fn(d)); }
148
- function send(p) { if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(p)); }
149
-
150
- function connect() {
151
- try {
152
- ws = new WebSocket(WS_URL);
153
- ws.onopen = () => {
154
- wsOpen = true; stopSim(); updateStatus();
155
- SIGNALS.forEach(s => send({ command: 'subscribe', signal: s }));
156
- };
157
- ws.onmessage = (e) => {
158
- try {
159
- const msg = JSON.parse(e.data);
160
- if (msg.type === 'connection_status') {
161
- deviceReady = msg.data?.status === 'connected';
162
- updateStatus(); return;
163
- }
164
- emit(msg.type, msg.data);
165
- } catch {}
166
- };
167
- ws.onclose = () => {
168
- wsOpen = false; deviceReady = false; updateStatus();
169
- startSim();
170
- clearTimeout(reconnectTimer);
171
- reconnectTimer = setTimeout(connect, 1500);
172
- };
173
- ws.onerror = () => ws?.close();
174
- } catch {
175
- wsOpen = false; startSim();
176
- clearTimeout(reconnectTimer);
177
- reconnectTimer = setTimeout(connect, 1500);
178
- }
179
- }
180
-
181
- function triggerGesture(type) {
182
- if (wsOpen) send({ command: 'trigger_gesture', data: { type } });
183
- else emit('gesture', { type, confidence: 0.95, timestamp: Date.now() });
184
- }
185
-
186
- /* ── Simulation (when Companion not reachable) ─────────────── */
187
- function startSim() {
188
- if (simTimer) return;
189
- simulating = true;
190
- let t = 0, autoSlideTimer = 0;
191
- simTimer = setInterval(() => {
192
- t += 0.05;
193
- // Simulate pressure
194
- const n = Math.max(0, Math.min(1, 0.3 + 0.25 * Math.sin(t * 0.5) + (Math.random() - 0.5) * 0.08));
195
- emit('pressure', { value: Math.round(n * 100), normalized: n, timestamp: Date.now() });
196
- // Simulate navigation (move laser in circles when laser mode active)
197
- if (laserMode) {
198
- emit('navigation', { delta_x: Math.cos(t * 1.2) * 3, delta_y: Math.sin(t * 1.2) * 3, timestamp: Date.now() });
199
- }
200
- // Auto-advance slides every ~5s
201
- autoSlideTimer += 60;
202
- if (autoSlideTimer >= 5000) {
203
- autoSlideTimer = 0;
204
- emit('gesture', { type: 'tap', confidence: 0.85, timestamp: Date.now() });
205
- }
206
- // Occasional random gestures
207
- if (Math.random() < 0.002) {
208
- const g = ['tap', 'double_tap', 'twist', 'double_twist'];
209
- emit('gesture', { type: g[Math.floor(Math.random() * g.length)], confidence: 0.7 + Math.random() * 0.3, timestamp: Date.now() });
210
- }
211
- }, 60);
212
- }
213
-
214
- function stopSim() { clearInterval(simTimer); simTimer = null; simulating = false; }
215
-
216
- /* ── Status UI ─────────────────────────────────────────────── */
217
- const dotEl = document.getElementById('dot');
218
- const connEl = document.getElementById('connLabel');
219
-
220
- function updateStatus() {
221
- if (wsOpen && deviceReady) { dotEl.className = 'dot live'; connEl.textContent = 'Connected'; }
222
- else if (wsOpen) { dotEl.className = 'dot'; connEl.textContent = 'Waiting for Mudra Band\u2026'; }
223
- else { dotEl.className = 'dot'; connEl.textContent = simulating ? 'Simulated' : 'Disconnected'; }
224
- }
225
-
226
- /* ── Slide Logic ───────────────────────────────────────────── */
227
- const TOTAL = 5;
228
- let current = 0;
229
- let laserMode = false;
230
- let laserX = 50, laserY = 50;
231
- let pressure = 0;
232
-
233
- const laserEl = document.getElementById('laser');
234
- const laserBadge = document.getElementById('laserBadge');
235
- const pressVal = document.getElementById('pressVal');
236
- const pressFill = document.getElementById('pressFill');
237
- const slideCounter = document.getElementById('slideCounter');
238
- const navDotsEl = document.getElementById('navDots');
239
-
240
- // Build nav dots
241
- for (let i = 0; i < TOTAL; i++) {
242
- const d = document.createElement('div');
243
- d.className = 'nav-dot' + (i === 0 ? ' active' : '');
244
- d.id = 'ndot' + i;
245
- d.addEventListener('click', () => goTo(i));
246
- navDotsEl.appendChild(d);
247
- }
248
-
249
- function goTo(idx) {
250
- if (idx < 0 || idx >= TOTAL || idx === current) return;
251
- const dir = idx > current ? 1 : -1;
252
- document.getElementById('slide' + current).className = 'slide ' + (dir > 0 ? 'left' : 'right');
253
- document.getElementById('slide' + idx).className = 'slide active';
254
- document.getElementById('ndot' + current).classList.remove('active');
255
- document.getElementById('ndot' + idx).classList.add('active');
256
- current = idx;
257
- slideCounter.textContent = (current + 1) + ' / ' + TOTAL;
258
- }
259
-
260
- function nextSlide() { goTo(current + 1 < TOTAL ? current + 1 : current); }
261
- function prevSlide() { goTo(current - 1 >= 0 ? current - 1 : current); }
262
-
263
- /* ── Signal Handlers ───────────────────────────────────────── */
264
- on('gesture', (d) => {
265
- const type = d.type || 'unknown';
266
- if (type === 'tap') nextSlide();
267
- if (type === 'twist') prevSlide();
268
- if (type === 'double_tap') {
269
- laserMode = !laserMode;
270
- laserEl.style.opacity = laserMode ? '1' : '0';
271
- laserBadge.classList.toggle('active', laserMode);
272
- }
273
- if (type === 'double_twist') goTo(0);
274
- });
275
-
276
- on('navigation', (d) => {
277
- if (!laserMode) return;
278
- laserX = Math.max(2, Math.min(98, laserX + (d.delta_x || 0) * 0.5));
279
- laserY = Math.max(2, Math.min(98, laserY + (d.delta_y || 0) * 0.5));
280
- laserEl.style.left = laserX + '%';
281
- laserEl.style.top = laserY + '%';
282
- });
283
-
284
- on('pressure', (d) => {
285
- pressure = Math.max(0, Math.min(1, d.normalized ?? d.value / 100));
286
- pressVal.textContent = Math.round(pressure * 100) + '%';
287
- pressFill.style.width = Math.round(pressure * 100) + '%';
288
- // Laser pointer size scales with pressure
289
- const size = 8 + pressure * 24;
290
- laserEl.style.width = size + 'px';
291
- laserEl.style.height = size + 'px';
292
- });
293
-
294
- /* ── Keyboard Fallback ─────────────────────────────────────── */
295
- let kbPressure = 0.3;
296
- document.addEventListener('keydown', (e) => {
297
- if (e.code === 'ArrowRight') { e.preventDefault(); triggerGesture('tap'); }
298
- if (e.code === 'ArrowLeft') { e.preventDefault(); triggerGesture('twist'); }
299
- if (e.code === 'Space') { e.preventDefault(); triggerGesture('tap'); }
300
- if (e.code === 'KeyL') {
301
- triggerGesture('double_tap');
302
- }
303
- if (e.key === ']') {
304
- kbPressure = Math.min(1, kbPressure + 0.08);
305
- emit('pressure', { value: Math.round(kbPressure * 100), normalized: kbPressure, timestamp: Date.now() });
306
- }
307
- if (e.key === '[') {
308
- kbPressure = Math.max(0, kbPressure - 0.08);
309
- emit('pressure', { value: Math.round(kbPressure * 100), normalized: kbPressure, timestamp: Date.now() });
310
- }
311
- });
312
-
313
- /* ── Buttons ───────────────────────────────────────────────── */
314
- document.getElementById('simTap').addEventListener('click', () => triggerGesture('tap'));
315
- document.getElementById('simTwist').addEventListener('click', () => triggerGesture('twist'));
316
-
317
- /* ── Mouse/Touch for Laser ─────────────────────────────────── */
318
- const slideContainer = document.getElementById('slideContainer');
319
- slideContainer.addEventListener('mousemove', (e) => {
320
- if (!laserMode) return;
321
- const r = slideContainer.getBoundingClientRect();
322
- laserX = ((e.clientX - r.left) / r.width) * 100;
323
- laserY = ((e.clientY - r.top) / r.height) * 100;
324
- laserEl.style.left = laserX + '%';
325
- laserEl.style.top = laserY + '%';
326
- });
327
- slideContainer.addEventListener('touchmove', (e) => {
328
- if (!laserMode) return;
329
- e.preventDefault();
330
- const r = slideContainer.getBoundingClientRect();
331
- laserX = ((e.touches[0].clientX - r.left) / r.width) * 100;
332
- laserY = ((e.touches[0].clientY - r.top) / r.height) * 100;
333
- laserEl.style.left = laserX + '%';
334
- laserEl.style.top = laserY + '%';
335
- }, { passive: false });
336
-
337
- /* ── Init ──────────────────────────────────────────────────── */
338
- laserEl.style.left = '50%';
339
- laserEl.style.top = '50%';
340
- laserEl.style.width = '12px';
341
- laserEl.style.height = '12px';
342
- connect();
343
- </script>
344
- </body>
345
- </html>
@@ -1,285 +0,0 @@
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>Pressure Painter — 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
- --canvas-bg: #f8fafc;
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: 100; 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
- .btn.primary-btn { background: var(--primary); color: #072f33; border-color: transparent; font-weight: 600; }
33
- .btn.primary-btn:hover { opacity: 0.9; }
34
-
35
- canvas { flex: 1; cursor: crosshair; display: block; background: var(--canvas-bg); }
36
-
37
- .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: 100; flex-shrink: 0; flex-wrap: wrap; }
38
- .color-swatch { width: 28px; height: 28px; border-radius: 8px; border: 2px solid var(--border); flex-shrink: 0; }
39
- .meter { display: flex; align-items: center; gap: 8px; }
40
- .meter-bar { width: 80px; height: 8px; background: var(--surface); border-radius: 999px; overflow: hidden; border: 1px solid var(--border); }
41
- .meter-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--primary)); border-radius: 999px; transition: width 75ms linear; width: 0%; }
42
- .meter-val { font-size: 12px; color: var(--text-secondary); min-width: 32px; text-align: right; font-variant-numeric: tabular-nums; }
43
- .hint { font-size: 11px; color: var(--text-secondary); opacity: 0.6; }
44
-
45
- .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: 200; }
46
-
47
- @media (max-width: 640px) {
48
- .header { padding: 8px 10px; } .toolbar { gap: 8px; padding: 8px 10px; }
49
- .meter-bar { width: 50px; } .hint { display: none; }
50
- }
51
- </style>
52
- </head>
53
- <body>
54
-
55
- <div class="header">
56
- <div class="header-left">
57
- <div class="brand"><span>Mudra</span> Pressure Painter</div>
58
- <span class="dot" id="dot"></span>
59
- <span class="conn-label" id="connLabel">Connecting&hellip;</span>
60
- </div>
61
- <div class="header-right">
62
- <a href="/gallery" class="btn" style="text-decoration:none;">&#8592; Gallery</a>
63
- <button class="btn" id="simTap">Simulate Tap</button>
64
- </div>
65
- </div>
66
-
67
- <canvas id="canvas"></canvas>
68
-
69
- <div class="toolbar">
70
- <button class="btn" id="clearBtn">Clear</button>
71
- <div class="color-swatch" id="swatch"></div>
72
- <div class="meter">
73
- <span class="meter-val" id="pressVal">0%</span>
74
- <div class="meter-bar"><div class="meter-fill" id="pressFill"></div></div>
75
- </div>
76
- <span class="hint">[ ] pressure &middot; Space tap &middot; D clear</span>
77
- </div>
78
-
79
- <div class="mudra-badge">Created by Mudra</div>
80
-
81
- <script>
82
- /* ── Mudra Protocol Connection (v2) ────────────────────────── */
83
- const WS_URL = 'ws://127.0.0.1:8766';
84
- const SIGNALS = ['pressure', 'gesture', 'snc'];
85
-
86
- let ws, reconnectTimer, simTimer;
87
- let wsOpen = false, deviceReady = false, simulating = true;
88
- const bus = {};
89
-
90
- function on(ev, fn) { (bus[ev] ??= []).push(fn); }
91
- function emit(ev, d) { (bus[ev] || []).forEach(fn => fn(d)); }
92
- function send(p) { if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(p)); }
93
-
94
- function connect() {
95
- try {
96
- ws = new WebSocket(WS_URL);
97
- ws.onopen = () => {
98
- wsOpen = true; stopSim(); updateStatus();
99
- SIGNALS.forEach(s => send({ command: 'subscribe', signal: s }));
100
- };
101
- ws.onmessage = (e) => {
102
- try {
103
- const msg = JSON.parse(e.data);
104
- if (msg.type === 'connection_status') {
105
- deviceReady = msg.data?.status === 'connected';
106
- updateStatus(); return;
107
- }
108
- emit(msg.type, msg.data);
109
- } catch {}
110
- };
111
- ws.onclose = () => {
112
- wsOpen = false; deviceReady = false; updateStatus();
113
- startSim();
114
- clearTimeout(reconnectTimer);
115
- reconnectTimer = setTimeout(connect, 1500);
116
- };
117
- ws.onerror = () => ws?.close();
118
- } catch {
119
- wsOpen = false; startSim();
120
- clearTimeout(reconnectTimer);
121
- reconnectTimer = setTimeout(connect, 1500);
122
- }
123
- }
124
-
125
- function triggerGesture(type) {
126
- if (wsOpen) send({ command: 'trigger_gesture', data: { type } });
127
- else emit('gesture', { type, confidence: 0.95, timestamp: Date.now() });
128
- }
129
-
130
- /* ── Simulation (when Companion not reachable) ─────────────── */
131
- function startSim() {
132
- if (simTimer) return;
133
- simulating = true;
134
- let t = 0;
135
- simTimer = setInterval(() => {
136
- t += 0.05;
137
- const n = Math.max(0, Math.min(1, 0.3 + 0.25 * Math.sin(t * 0.7) + (Math.random() - 0.5) * 0.1));
138
- emit('pressure', { value: Math.round(n * 100), normalized: n, timestamp: Date.now() });
139
- emit('snc', {
140
- values: [
141
- 0.3 * Math.sin(t * 1.3) + (Math.random() - 0.5) * 0.1,
142
- 0.2 * Math.cos(t * 1.7) + (Math.random() - 0.5) * 0.1,
143
- 0.15 * Math.sin(t * 2.1) + (Math.random() - 0.5) * 0.08
144
- ],
145
- frequency: 1000, timestamp: Date.now()
146
- });
147
- if (Math.random() < 0.004) {
148
- const g = ['tap', 'double_tap', 'twist', 'double_twist'];
149
- emit('gesture', { type: g[Math.floor(Math.random() * g.length)], confidence: 0.7 + Math.random() * 0.3, timestamp: Date.now() });
150
- }
151
- }, 60);
152
- }
153
-
154
- function stopSim() { clearInterval(simTimer); simTimer = null; simulating = false; }
155
-
156
- /* ── Status UI ─────────────────────────────────────────────── */
157
- const dotEl = document.getElementById('dot');
158
- const connEl = document.getElementById('connLabel');
159
-
160
- function updateStatus() {
161
- if (wsOpen && deviceReady) { dotEl.className = 'dot live'; connEl.textContent = 'Connected'; }
162
- else if (wsOpen) { dotEl.className = 'dot'; connEl.textContent = 'Waiting for Mudra Band\u2026'; }
163
- else { dotEl.className = 'dot'; connEl.textContent = simulating ? 'Simulated' : 'Disconnected'; }
164
- }
165
-
166
- /* ── Canvas Setup ──────────────────────────────────────────── */
167
- const canvas = document.getElementById('canvas');
168
- const ctx = canvas.getContext('2d');
169
- const swatch = document.getElementById('swatch');
170
- const pressVal = document.getElementById('pressVal');
171
- const pressFill = document.getElementById('pressFill');
172
-
173
- let hue = 179, pressure = 0;
174
- let cursorX = 0, cursorY = 0, mouseActive = false;
175
- let simTime = 0;
176
- const points = [];
177
-
178
- function resize() {
179
- const saved = ctx.getImageData(0, 0, canvas.width, canvas.height);
180
- const r = canvas.getBoundingClientRect();
181
- canvas.width = r.width * devicePixelRatio;
182
- canvas.height = r.height * devicePixelRatio;
183
- ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0);
184
- ctx.putImageData(saved, 0, 0);
185
- }
186
- resize();
187
- window.addEventListener('resize', resize);
188
-
189
- canvas.addEventListener('mousemove', (e) => {
190
- const r = canvas.getBoundingClientRect();
191
- cursorX = e.clientX - r.left; cursorY = e.clientY - r.top;
192
- mouseActive = true;
193
- });
194
- canvas.addEventListener('mouseleave', () => { mouseActive = false; });
195
- canvas.addEventListener('touchmove', (e) => {
196
- e.preventDefault();
197
- const r = canvas.getBoundingClientRect();
198
- cursorX = e.touches[0].clientX - r.left; cursorY = e.touches[0].clientY - r.top;
199
- mouseActive = true;
200
- }, { passive: false });
201
-
202
- /* ── Signal Handlers ───────────────────────────────────────── */
203
- on('pressure', (d) => {
204
- pressure = Math.max(0, Math.min(1, d.normalized ?? d.value / 100));
205
- pressVal.textContent = Math.round(pressure * 100) + '%';
206
- pressFill.style.width = Math.round(pressure * 100) + '%';
207
- });
208
-
209
- on('snc', (d) => {
210
- const vals = d.values || [0, 0, 0];
211
- const energy = (Math.abs(vals[0]) + Math.abs(vals[1]) + Math.abs(vals[2])) / 3;
212
- hue = Math.round((energy * 720) % 360);
213
- swatch.style.background = `hsl(${hue}, 80%, 55%)`;
214
- });
215
-
216
- on('gesture', (d) => {
217
- const type = d.type || 'unknown';
218
- if (type === 'tap') hue = Math.floor(Math.random() * 360);
219
- if (type === 'double_tap') {
220
- points.length = 0;
221
- ctx.clearRect(0, 0, canvas.width / devicePixelRatio, canvas.height / devicePixelRatio);
222
- }
223
- swatch.style.background = `hsl(${hue}, 80%, 55%)`;
224
- });
225
-
226
- /* ── Keyboard Fallback ─────────────────────────────────────── */
227
- let kbPressure = 0.3;
228
- document.addEventListener('keydown', (e) => {
229
- if (e.code === 'Space') { e.preventDefault(); triggerGesture('tap'); }
230
- if (e.code === 'KeyD') triggerGesture('double_tap');
231
- if (e.key === ']') {
232
- kbPressure = Math.min(1, kbPressure + 0.08);
233
- emit('pressure', { value: Math.round(kbPressure * 100), normalized: kbPressure, timestamp: Date.now() });
234
- }
235
- if (e.key === '[') {
236
- kbPressure = Math.max(0, kbPressure - 0.08);
237
- emit('pressure', { value: Math.round(kbPressure * 100), normalized: kbPressure, timestamp: Date.now() });
238
- }
239
- });
240
-
241
- /* ── Buttons ───────────────────────────────────────────────── */
242
- document.getElementById('simTap').addEventListener('click', () => triggerGesture('tap'));
243
- document.getElementById('clearBtn').addEventListener('click', () => {
244
- points.length = 0;
245
- ctx.clearRect(0, 0, canvas.width / devicePixelRatio, canvas.height / devicePixelRatio);
246
- });
247
-
248
- /* ── Paint Loop ────────────────────────────────────────────── */
249
- function paintLoop() {
250
- simTime += 0.02;
251
- const r = canvas.getBoundingClientRect();
252
- const w = r.width, h = r.height;
253
-
254
- let x = cursorX, y = cursorY;
255
- if (!mouseActive) {
256
- x = w / 2 + Math.sin(simTime * 0.5) * w * 0.3 + Math.cos(simTime * 0.8) * w * 0.1;
257
- y = h / 2 + Math.cos(simTime * 0.4) * h * 0.25 + Math.sin(simTime * 0.7) * h * 0.1;
258
- }
259
-
260
- const size = 2 + pressure * 38;
261
- const alpha = 0.2 + pressure * 0.8;
262
- points.push({ x, y, size, hue, alpha });
263
- if (points.length > 8000) points.splice(0, 100);
264
-
265
- if (points.length >= 2) {
266
- const p0 = points[points.length - 2], p1 = points[points.length - 1];
267
- ctx.beginPath();
268
- ctx.moveTo(p0.x, p0.y);
269
- ctx.lineTo(p1.x, p1.y);
270
- ctx.strokeStyle = `hsla(${p1.hue}, 80%, 55%, ${p1.alpha})`;
271
- ctx.lineWidth = p1.size;
272
- ctx.lineCap = 'round';
273
- ctx.lineJoin = 'round';
274
- ctx.stroke();
275
- }
276
-
277
- requestAnimationFrame(paintLoop);
278
- }
279
-
280
- swatch.style.background = `hsl(${hue}, 80%, 55%)`;
281
- connect();
282
- paintLoop();
283
- </script>
284
- </body>
285
- </html>