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.
- package/.claude-plugin/plugin.json +19 -0
- package/CLAUDE.md +35 -0
- package/bin/install.js +25 -0
- package/package.json +28 -0
- package/skills/mudra-master/SKILL.md +202 -0
- package/skills/mudra-master/mudra-preview/SKILL.md +151 -0
- package/skills/mudra-master/mudra-preview/assets/ar-menu.html +382 -0
- package/skills/mudra-master/mudra-preview/assets/document-scroller.html +468 -0
- package/skills/mudra-master/mudra-preview/assets/drum-machine.html +788 -0
- package/skills/mudra-master/mudra-preview/assets/emg-visualizer.html +390 -0
- package/skills/mudra-master/mudra-preview/assets/generative-art.html +408 -0
- package/skills/mudra-master/mudra-preview/assets/gesture-assistant.html +342 -0
- package/skills/mudra-master/mudra-preview/assets/gesture-speech.html +444 -0
- package/skills/mudra-master/mudra-preview/assets/hands-free-desktop.html +376 -0
- package/skills/mudra-master/mudra-preview/assets/model-rotator.html +365 -0
- package/skills/mudra-master/mudra-preview/assets/mudra-duel.html +3200 -0
- package/skills/mudra-master/mudra-preview/assets/mudra-monitor.html +1451 -0
- package/skills/mudra-master/mudra-preview/assets/mudra-ultimate-template.html +891 -0
- package/skills/mudra-master/mudra-preview/assets/music-sequencer.html +455 -0
- package/skills/mudra-master/mudra-preview/assets/neural-pong.html +380 -0
- package/skills/mudra-master/mudra-preview/assets/neural-snake.html +364 -0
- package/skills/mudra-master/mudra-preview/assets/presentation-controller.html +345 -0
- package/skills/mudra-master/mudra-preview/assets/pressure-painter.html +285 -0
- package/skills/mudra-master/mudra-preview/assets/runner.html +1812 -0
- package/skills/mudra-master/mudra-preview/assets/smart-home.html +461 -0
- package/skills/mudra-master/mudra-preview/assets/space-invaders.html +1154 -0
- package/skills/mudra-master/mudra-preview/assets/waterful-ring-toss.html +1624 -0
- package/skills/mudra-master/mudra-preview/references/agent_protocol.json +646 -0
- package/skills/mudra-master/mudra-preview/references/promt.md +16829 -0
- package/skills/mudra-master/mudra-xr/SKILL.md +245 -0
- package/skills/mudra-master/mudra-xr/assets/demos/3dgs-walkthrough.html +242 -0
- package/skills/mudra-master/mudra-xr/assets/demos/aisimulator.html +792 -0
- package/skills/mudra-master/mudra-xr/assets/demos/balloonpop.html +422 -0
- package/skills/mudra-master/mudra-xr/assets/demos/ballpit.html +284 -0
- package/skills/mudra-master/mudra-xr/assets/demos/drone.html +41 -0
- package/skills/mudra-master/mudra-xr/assets/demos/gemini-icebreakers.html +338 -0
- package/skills/mudra-master/mudra-xr/assets/demos/gemini-xrobject.html +302 -0
- package/skills/mudra-master/mudra-xr/assets/demos/math3d.html +188 -0
- package/skills/mudra-master/mudra-xr/assets/demos/measure.html +247 -0
- package/skills/mudra-master/mudra-xr/assets/demos/occlusion.html +251 -0
- package/skills/mudra-master/mudra-xr/assets/demos/rain.html +498 -0
- package/skills/mudra-master/mudra-xr/assets/demos/screenwiper.html +432 -0
- package/skills/mudra-master/mudra-xr/assets/demos/splash.html +519 -0
- package/skills/mudra-master/mudra-xr/assets/demos/webcam_gestures.html +253 -0
- package/skills/mudra-master/mudra-xr/assets/demos/xremoji.html +833 -0
- package/skills/mudra-master/mudra-xr/assets/demos/xrpoet.html +151 -0
- package/skills/mudra-master/mudra-xr/assets/samples/depthmap.html +266 -0
- package/skills/mudra-master/mudra-xr/assets/samples/depthmesh.html +100 -0
- package/skills/mudra-master/mudra-xr/assets/samples/game_rps.html +1099 -0
- package/skills/mudra-master/mudra-xr/assets/samples/gestures_custom.html +467 -0
- package/skills/mudra-master/mudra-xr/assets/samples/gestures_heuristic.html +256 -0
- package/skills/mudra-master/mudra-xr/assets/samples/lighting.html +163 -0
- package/skills/mudra-master/mudra-xr/assets/samples/mesh_detection.html +46 -0
- package/skills/mudra-master/mudra-xr/assets/samples/modelviewer.html +183 -0
- package/skills/mudra-master/mudra-xr/assets/samples/paint.html +133 -0
- package/skills/mudra-master/mudra-xr/assets/samples/planar-vst.html +45 -0
- package/skills/mudra-master/mudra-xr/assets/samples/reticle.html +95 -0
- package/skills/mudra-master/mudra-xr/assets/samples/skybox_agent.html +384 -0
- package/skills/mudra-master/mudra-xr/assets/samples/sound.html +390 -0
- package/skills/mudra-master/mudra-xr/assets/samples/ui.html +496 -0
- package/skills/mudra-master/mudra-xr/assets/samples/virtual-screens.html +808 -0
- package/skills/mudra-master/mudra-xr/assets/templates/0_basic.html +64 -0
- package/skills/mudra-master/mudra-xr/assets/templates/1_ui.html +107 -0
- package/skills/mudra-master/mudra-xr/assets/templates/2_hands.html +183 -0
- package/skills/mudra-master/mudra-xr/assets/templates/3_depth.html +89 -0
- package/skills/mudra-master/mudra-xr/assets/templates/4_stereo.html +84 -0
- package/skills/mudra-master/mudra-xr/assets/templates/5_camera.html +117 -0
- package/skills/mudra-master/mudra-xr/assets/templates/6_ai.html +154 -0
- package/skills/mudra-master/mudra-xr/assets/templates/7_ai_live.html +253 -0
- package/skills/mudra-master/mudra-xr/assets/templates/8_objects.html +95 -0
- package/skills/mudra-master/mudra-xr/assets/templates/9_xr-toggle.html +90 -0
- package/skills/mudra-master/mudra-xr/assets/templates/heuristic_hand_gestures.html +102 -0
- package/skills/mudra-master/mudra-xr/assets/templates/meshes.html +47 -0
- package/skills/mudra-master/mudra-xr/assets/templates/planes.html +47 -0
- package/skills/mudra-master/mudra-xr/assets/templates/uikit.html +187 -0
- package/skills/mudra-master/mudra-xr/references/agent_protocol.json +878 -0
- package/skills/mudra-master/mudra-xr/references/promt.md +1516 -0
- package/skills/mudra-preview/SKILL.md +151 -0
- package/skills/mudra-preview/assets/ar-menu.html +382 -0
- package/skills/mudra-preview/assets/document-scroller.html +468 -0
- package/skills/mudra-preview/assets/drum-machine.html +788 -0
- package/skills/mudra-preview/assets/emg-visualizer.html +390 -0
- package/skills/mudra-preview/assets/generative-art.html +408 -0
- package/skills/mudra-preview/assets/gesture-assistant.html +342 -0
- package/skills/mudra-preview/assets/gesture-speech.html +444 -0
- package/skills/mudra-preview/assets/hands-free-desktop.html +376 -0
- package/skills/mudra-preview/assets/model-rotator.html +365 -0
- package/skills/mudra-preview/assets/mudra-duel.html +3200 -0
- package/skills/mudra-preview/assets/mudra-monitor.html +1451 -0
- package/skills/mudra-preview/assets/mudra-ultimate-template.html +891 -0
- package/skills/mudra-preview/assets/music-sequencer.html +455 -0
- package/skills/mudra-preview/assets/neural-pong.html +380 -0
- package/skills/mudra-preview/assets/neural-snake.html +364 -0
- package/skills/mudra-preview/assets/presentation-controller.html +345 -0
- package/skills/mudra-preview/assets/pressure-painter.html +285 -0
- package/skills/mudra-preview/assets/runner.html +1812 -0
- package/skills/mudra-preview/assets/smart-home.html +461 -0
- package/skills/mudra-preview/assets/space-invaders.html +1154 -0
- package/skills/mudra-preview/assets/waterful-ring-toss.html +1624 -0
- package/skills/mudra-preview/references/agent_protocol.json +646 -0
- package/skills/mudra-preview/references/promt.md +16829 -0
- package/skills/mudra-xr/SKILL.md +245 -0
- package/skills/mudra-xr/assets/demos/3dgs-walkthrough.html +242 -0
- package/skills/mudra-xr/assets/demos/aisimulator.html +792 -0
- package/skills/mudra-xr/assets/demos/balloonpop.html +422 -0
- package/skills/mudra-xr/assets/demos/ballpit.html +284 -0
- package/skills/mudra-xr/assets/demos/drone.html +41 -0
- package/skills/mudra-xr/assets/demos/gemini-icebreakers.html +338 -0
- package/skills/mudra-xr/assets/demos/gemini-xrobject.html +302 -0
- package/skills/mudra-xr/assets/demos/math3d.html +188 -0
- package/skills/mudra-xr/assets/demos/measure.html +247 -0
- package/skills/mudra-xr/assets/demos/occlusion.html +251 -0
- package/skills/mudra-xr/assets/demos/rain.html +498 -0
- package/skills/mudra-xr/assets/demos/screenwiper.html +432 -0
- package/skills/mudra-xr/assets/demos/splash.html +519 -0
- package/skills/mudra-xr/assets/demos/webcam_gestures.html +253 -0
- package/skills/mudra-xr/assets/demos/xremoji.html +833 -0
- package/skills/mudra-xr/assets/demos/xrpoet.html +151 -0
- package/skills/mudra-xr/assets/samples/depthmap.html +266 -0
- package/skills/mudra-xr/assets/samples/depthmesh.html +100 -0
- package/skills/mudra-xr/assets/samples/game_rps.html +1099 -0
- package/skills/mudra-xr/assets/samples/gestures_custom.html +467 -0
- package/skills/mudra-xr/assets/samples/gestures_heuristic.html +256 -0
- package/skills/mudra-xr/assets/samples/lighting.html +163 -0
- package/skills/mudra-xr/assets/samples/mesh_detection.html +46 -0
- package/skills/mudra-xr/assets/samples/modelviewer.html +183 -0
- package/skills/mudra-xr/assets/samples/paint.html +133 -0
- package/skills/mudra-xr/assets/samples/planar-vst.html +45 -0
- package/skills/mudra-xr/assets/samples/reticle.html +95 -0
- package/skills/mudra-xr/assets/samples/skybox_agent.html +384 -0
- package/skills/mudra-xr/assets/samples/sound.html +390 -0
- package/skills/mudra-xr/assets/samples/ui.html +496 -0
- package/skills/mudra-xr/assets/samples/virtual-screens.html +808 -0
- package/skills/mudra-xr/assets/templates/0_basic.html +64 -0
- package/skills/mudra-xr/assets/templates/1_ui.html +107 -0
- package/skills/mudra-xr/assets/templates/2_hands.html +183 -0
- package/skills/mudra-xr/assets/templates/3_depth.html +89 -0
- package/skills/mudra-xr/assets/templates/4_stereo.html +84 -0
- package/skills/mudra-xr/assets/templates/5_camera.html +117 -0
- package/skills/mudra-xr/assets/templates/6_ai.html +154 -0
- package/skills/mudra-xr/assets/templates/7_ai_live.html +253 -0
- package/skills/mudra-xr/assets/templates/8_objects.html +95 -0
- package/skills/mudra-xr/assets/templates/9_xr-toggle.html +90 -0
- package/skills/mudra-xr/assets/templates/heuristic_hand_gestures.html +102 -0
- package/skills/mudra-xr/assets/templates/meshes.html +47 -0
- package/skills/mudra-xr/assets/templates/planes.html +47 -0
- package/skills/mudra-xr/assets/templates/uikit.html +187 -0
- package/skills/mudra-xr/references/agent_protocol.json +878 -0
- package/skills/mudra-xr/references/promt.md +1516 -0
|
@@ -0,0 +1,468 @@
|
|
|
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>Document Scroller — Mudra Studio</title>
|
|
7
|
+
<style>
|
|
8
|
+
@media (prefers-reduced-motion: reduce) {
|
|
9
|
+
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; scroll-behavior: auto !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
|
+
}
|
|
18
|
+
|
|
19
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
20
|
+
body { background: var(--bg); color: var(--text); font-family: 'Poppins', system-ui, sans-serif; overflow: hidden; height: 100vh; display: flex; flex-direction: column; }
|
|
21
|
+
|
|
22
|
+
.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; }
|
|
23
|
+
.header-left, .header-right { display: flex; align-items: center; gap: 10px; }
|
|
24
|
+
.brand { font-weight: 700; font-size: 14px; } .brand span { color: var(--primary); }
|
|
25
|
+
.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; }
|
|
26
|
+
.dot.live { background: var(--success); box-shadow: 0 0 0 3px rgba(34,197,94,0.18); }
|
|
27
|
+
.conn-label { font-size: 12px; color: var(--text-secondary); }
|
|
28
|
+
|
|
29
|
+
.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; }
|
|
30
|
+
.btn:hover { border-color: var(--primary); color: var(--primary); }
|
|
31
|
+
.btn.primary-btn { background: var(--primary); color: #072f33; border-color: transparent; font-weight: 600; }
|
|
32
|
+
.btn.primary-btn:hover { opacity: 0.9; }
|
|
33
|
+
|
|
34
|
+
/* Layout */
|
|
35
|
+
.layout { flex: 1; display: flex; overflow: hidden; position: relative; }
|
|
36
|
+
|
|
37
|
+
/* Table of contents sidebar */
|
|
38
|
+
.toc { width: 220px; background: var(--card); border-right: 1px solid var(--border); overflow-y: auto; padding: 16px 12px; flex-shrink: 0; }
|
|
39
|
+
.toc-title { font-size: 11px; text-transform: uppercase; letter-spacing: 1.5px; color: var(--text-secondary); margin-bottom: 12px; font-weight: 600; }
|
|
40
|
+
.toc-item { display: block; padding: 7px 10px; font-size: 12px; color: var(--text-secondary); text-decoration: none; border-radius: 6px; margin-bottom: 2px; transition: all 150ms; cursor: pointer; border: 1px solid transparent; line-height: 1.4; }
|
|
41
|
+
.toc-item:hover { color: var(--text); background: var(--surface); }
|
|
42
|
+
.toc-item.active { color: var(--primary); background: rgba(119,234,233,0.06); border-color: rgba(119,234,233,0.2); }
|
|
43
|
+
|
|
44
|
+
/* Document area */
|
|
45
|
+
.doc-area { flex: 1; position: relative; overflow: hidden; display: flex; flex-direction: column; }
|
|
46
|
+
.doc-scroll { flex: 1; overflow-y: auto; scroll-behavior: smooth; padding: 32px 40px; }
|
|
47
|
+
|
|
48
|
+
/* Scan mode */
|
|
49
|
+
.doc-scroll.scan-mode .section h2 { font-size: 17px; }
|
|
50
|
+
.doc-scroll.scan-mode .section p { font-size: 12px; line-height: 1.5; color: var(--text-secondary); }
|
|
51
|
+
.doc-scroll.scan-mode .section .callout { border-color: var(--primary); background: rgba(119,234,233,0.1); }
|
|
52
|
+
.doc-scroll.scan-mode .section { margin-bottom: 24px; }
|
|
53
|
+
|
|
54
|
+
/* Content sections */
|
|
55
|
+
.section { margin-bottom: 44px; }
|
|
56
|
+
.section h2 { font-size: 22px; font-weight: 700; margin-bottom: 14px; color: var(--text); scroll-margin-top: 20px; }
|
|
57
|
+
.section h2 .num { color: var(--primary); margin-right: 8px; font-variant-numeric: tabular-nums; }
|
|
58
|
+
.section p { font-size: 15px; line-height: 1.8; color: var(--text-secondary); margin-bottom: 12px; }
|
|
59
|
+
.section .callout { background: var(--surface); border-left: 3px solid var(--accent); padding: 14px 18px; border-radius: 0 10px 10px 0; margin: 14px 0; font-size: 14px; color: var(--text-secondary); line-height: 1.7; }
|
|
60
|
+
|
|
61
|
+
/* Custom scrollbar track */
|
|
62
|
+
.scrollbar-track { position: absolute; right: 0; top: 0; bottom: 0; width: 14px; background: var(--card); border-left: 1px solid var(--border); z-index: 10; }
|
|
63
|
+
.scroll-thumb { position: absolute; right: 2px; width: 10px; background: var(--text-secondary); opacity: 0.3; border-radius: 5px; min-height: 30px; transition: background 150ms, opacity 150ms; cursor: pointer; }
|
|
64
|
+
.scroll-thumb:hover { opacity: 0.6; background: var(--primary); }
|
|
65
|
+
.bookmark-marker { position: absolute; right: 0; width: 14px; height: 4px; background: var(--primary); border-radius: 2px; pointer-events: none; z-index: 2; }
|
|
66
|
+
|
|
67
|
+
/* Bottom toolbar */
|
|
68
|
+
.toolbar { display: flex; align-items: center; gap: 14px; padding: 10px 16px; background: var(--card); border-top: 1px solid var(--border); z-index: 100; flex-shrink: 0; flex-wrap: wrap; }
|
|
69
|
+
.section-name { font-size: 13px; color: var(--primary); font-weight: 600; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
70
|
+
.meter { display: flex; align-items: center; gap: 8px; }
|
|
71
|
+
.meter-label { font-size: 12px; color: var(--text-secondary); }
|
|
72
|
+
.meter-bar { width: 60px; height: 8px; background: var(--surface); border-radius: 999px; overflow: hidden; border: 1px solid var(--border); }
|
|
73
|
+
.meter-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--primary)); border-radius: 999px; transition: width 75ms linear; width: 0%; }
|
|
74
|
+
.bm-count { font-size: 12px; color: var(--text-secondary); white-space: nowrap; }
|
|
75
|
+
.mode-badge { font-size: 11px; padding: 4px 10px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); color: var(--text-secondary); cursor: pointer; transition: all 150ms; white-space: nowrap; }
|
|
76
|
+
.mode-badge.scan { border-color: var(--primary); color: var(--primary); }
|
|
77
|
+
.hint { font-size: 11px; color: var(--text-secondary); opacity: 0.6; }
|
|
78
|
+
|
|
79
|
+
.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; }
|
|
80
|
+
|
|
81
|
+
@media (max-width: 640px) {
|
|
82
|
+
.header { padding: 8px 10px; }
|
|
83
|
+
.toc { display: none; }
|
|
84
|
+
.doc-scroll { padding: 20px 16px; }
|
|
85
|
+
.toolbar { gap: 8px; padding: 8px 10px; }
|
|
86
|
+
.hint { display: none; }
|
|
87
|
+
.meter-bar { width: 40px; }
|
|
88
|
+
}
|
|
89
|
+
</style>
|
|
90
|
+
</head>
|
|
91
|
+
<body>
|
|
92
|
+
|
|
93
|
+
<div class="header">
|
|
94
|
+
<div class="header-left">
|
|
95
|
+
<div class="brand"><span>Mudra</span> Document Scroller</div>
|
|
96
|
+
<span class="dot" id="dot"></span>
|
|
97
|
+
<span class="conn-label" id="connLabel">Connecting…</span>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="header-right">
|
|
100
|
+
<a href="/gallery" class="btn" style="text-decoration:none;">← Gallery</a>
|
|
101
|
+
<button class="btn" id="simTap">Bookmark</button>
|
|
102
|
+
<button class="btn" id="simScan">Toggle Scan</button>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div class="layout">
|
|
107
|
+
<div class="toc" id="toc">
|
|
108
|
+
<div class="toc-title">Contents</div>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="doc-area">
|
|
111
|
+
<div class="doc-scroll" id="docScroll"></div>
|
|
112
|
+
<div class="scrollbar-track" id="scrollbarTrack">
|
|
113
|
+
<div class="scroll-thumb" id="scrollThumb"></div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div class="toolbar">
|
|
119
|
+
<span class="section-name" id="sectionName">Introduction</span>
|
|
120
|
+
<div class="meter">
|
|
121
|
+
<span class="meter-label">Speed</span>
|
|
122
|
+
<div class="meter-bar"><div class="meter-fill" id="speedFill"></div></div>
|
|
123
|
+
</div>
|
|
124
|
+
<span class="bm-count" id="bmCount">0 bookmarks</span>
|
|
125
|
+
<span class="mode-badge" id="modeBadge">Reading</span>
|
|
126
|
+
<span class="hint">↑↓ scroll · B bookmark · N next · S scan · [ ] speed</span>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div class="mudra-badge">Created by Mudra</div>
|
|
130
|
+
|
|
131
|
+
<script>
|
|
132
|
+
/* ── Mudra Protocol Connection (v2) ────────────────────────── */
|
|
133
|
+
const WS_URL = 'ws://127.0.0.1:8766';
|
|
134
|
+
const SIGNALS = ['navigation', 'gesture', 'pressure'];
|
|
135
|
+
|
|
136
|
+
let ws, reconnectTimer, simTimer;
|
|
137
|
+
let wsOpen = false, deviceReady = false, simulating = true;
|
|
138
|
+
const bus = {};
|
|
139
|
+
|
|
140
|
+
function on(ev, fn) { (bus[ev] ??= []).push(fn); }
|
|
141
|
+
function emit(ev, d) { (bus[ev] || []).forEach(fn => fn(d)); }
|
|
142
|
+
function send(p) { if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(p)); }
|
|
143
|
+
|
|
144
|
+
function connect() {
|
|
145
|
+
try {
|
|
146
|
+
ws = new WebSocket(WS_URL);
|
|
147
|
+
ws.onopen = () => {
|
|
148
|
+
wsOpen = true; stopSim(); updateStatus();
|
|
149
|
+
SIGNALS.forEach(s => send({ command: 'subscribe', signal: s }));
|
|
150
|
+
};
|
|
151
|
+
ws.onmessage = (e) => {
|
|
152
|
+
try {
|
|
153
|
+
const msg = JSON.parse(e.data);
|
|
154
|
+
if (msg.type === 'connection_status') {
|
|
155
|
+
deviceReady = msg.data?.status === 'connected';
|
|
156
|
+
updateStatus(); return;
|
|
157
|
+
}
|
|
158
|
+
emit(msg.type, msg.data);
|
|
159
|
+
} catch {}
|
|
160
|
+
};
|
|
161
|
+
ws.onclose = () => {
|
|
162
|
+
wsOpen = false; deviceReady = false; updateStatus();
|
|
163
|
+
startSim();
|
|
164
|
+
clearTimeout(reconnectTimer);
|
|
165
|
+
reconnectTimer = setTimeout(connect, 1500);
|
|
166
|
+
};
|
|
167
|
+
ws.onerror = () => ws?.close();
|
|
168
|
+
} catch {
|
|
169
|
+
wsOpen = false; startSim();
|
|
170
|
+
clearTimeout(reconnectTimer);
|
|
171
|
+
reconnectTimer = setTimeout(connect, 1500);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function triggerGesture(type) {
|
|
176
|
+
if (wsOpen) send({ command: 'trigger_gesture', data: { type } });
|
|
177
|
+
else emit('gesture', { type, confidence: 0.95, timestamp: Date.now() });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ── Simulation (when Companion not reachable) ─────────────── */
|
|
181
|
+
function startSim() {
|
|
182
|
+
if (simTimer) return;
|
|
183
|
+
simulating = true;
|
|
184
|
+
let t = 0;
|
|
185
|
+
simTimer = setInterval(() => {
|
|
186
|
+
t += 0.05;
|
|
187
|
+
/* gentle auto-scroll via navigation */
|
|
188
|
+
const dy = Math.round(1.5 + Math.sin(t * 0.2) * 1);
|
|
189
|
+
emit('navigation', { delta_x: 0, delta_y: dy, timestamp: Date.now() });
|
|
190
|
+
/* pressure for scroll speed */
|
|
191
|
+
const n = Math.max(0, Math.min(1, 0.3 + 0.15 * Math.sin(t * 0.4) + (Math.random() - 0.5) * 0.05));
|
|
192
|
+
emit('pressure', { value: Math.round(n * 100), normalized: n, timestamp: Date.now() });
|
|
193
|
+
/* occasional bookmark */
|
|
194
|
+
if (Math.random() < 0.002) {
|
|
195
|
+
emit('gesture', { type: 'tap', confidence: 0.9, timestamp: Date.now() });
|
|
196
|
+
}
|
|
197
|
+
}, 60);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function stopSim() { clearInterval(simTimer); simTimer = null; simulating = false; }
|
|
201
|
+
|
|
202
|
+
/* ── Status UI ─────────────────────────────────────────────── */
|
|
203
|
+
const dotEl = document.getElementById('dot');
|
|
204
|
+
const connEl = document.getElementById('connLabel');
|
|
205
|
+
|
|
206
|
+
function updateStatus() {
|
|
207
|
+
if (wsOpen && deviceReady) { dotEl.className = 'dot live'; connEl.textContent = 'Connected'; }
|
|
208
|
+
else if (wsOpen) { dotEl.className = 'dot'; connEl.textContent = 'Waiting for Mudra Band\u2026'; }
|
|
209
|
+
else { dotEl.className = 'dot'; connEl.textContent = simulating ? 'Simulated' : 'Disconnected'; }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* ── Document Content ──────────────────────────────────────── */
|
|
213
|
+
const sections = [
|
|
214
|
+
{
|
|
215
|
+
title: 'Introduction to Neural Computing',
|
|
216
|
+
paragraphs: [
|
|
217
|
+
'Neural computing represents one of the most profound shifts in human-computer interaction. By capturing the faintest electrical signals from the nervous system, devices like the Mudra wristband translate intention into action, bypassing traditional input methods entirely.',
|
|
218
|
+
'The fundamental premise is elegant: when you think about moving your fingers, your brain sends electrical signals through motor neurons to the muscles in your forearm. These signals, known as electromyographic (EMG) signals, can be detected and decoded before the movement is even completed.'
|
|
219
|
+
],
|
|
220
|
+
callout: 'Neural input does not require actual physical movement. The signals can be captured from the intention alone, opening up entirely new paradigms for accessibility and hands-free interaction.'
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
title: 'Electromyography Fundamentals',
|
|
224
|
+
paragraphs: [
|
|
225
|
+
'Electromyography (EMG) is the recording and analysis of electrical activity produced by skeletal muscles. When a motor neuron fires, it generates an action potential that propagates along the muscle fiber, creating a measurable electrical field.',
|
|
226
|
+
'Surface EMG sensors, like those in the Mudra wristband, detect these signals non-invasively through the skin. The raw signal is a complex interference pattern of many motor unit action potentials overlapping in time and space.',
|
|
227
|
+
'Signal processing pipelines typically include bandpass filtering (20-500 Hz), rectification, and envelope detection. Advanced systems use machine learning classifiers trained on labeled gesture data to recognize specific hand poses and movements.'
|
|
228
|
+
],
|
|
229
|
+
callout: 'The Mudra Protocol v2 WebSocket interface streams processed neural data including pressure, navigation deltas, IMU readings, and discrete gesture events in real-time JSON packets at configurable frequencies.'
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
title: 'Gesture Recognition Pipeline',
|
|
233
|
+
paragraphs: [
|
|
234
|
+
'Converting raw EMG signals into discrete gestures involves several processing stages. The first stage is signal acquisition and preprocessing, where noise from power lines, motion artifacts, and other sources is filtered out.',
|
|
235
|
+
'Feature extraction follows, where time-domain features (mean absolute value, zero crossings, waveform length) and frequency-domain features (power spectral density, median frequency) are computed over sliding windows of typically 150 to 300 milliseconds.',
|
|
236
|
+
'Classification algorithms then map feature vectors to gesture labels. The Mudra system recognizes gestures including tap, double tap, twist, and double twist with associated confidence scores.',
|
|
237
|
+
'Latency is critical for real-time interaction. End-to-end latency from muscle activation to gesture output should remain below 100 milliseconds for the interaction to feel instantaneous.'
|
|
238
|
+
],
|
|
239
|
+
callout: null
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
title: 'Accelerometer and IMU Fusion',
|
|
243
|
+
paragraphs: [
|
|
244
|
+
'Beyond EMG, the Mudra wristband incorporates a tri-axis accelerometer and gyroscope forming an inertial measurement unit. These sensors track the orientation and movement of the wrist in three-dimensional space.',
|
|
245
|
+
'Sensor fusion algorithms combine accelerometer, gyroscope, and EMG data to provide a richer understanding of user intent. The navigation signal provides continuous delta values for smooth pointing and scrolling.',
|
|
246
|
+
'Calibration is important for navigation-based control. The neutral wrist position defines the zero point, and sensitivity can be adjusted based on the range of comfortable motion for each user.'
|
|
247
|
+
],
|
|
248
|
+
callout: 'The navigation signal provides continuous analog control like a joystick, while EMG gestures provide discrete events like button presses. This combination enables rich, expressive interaction without touching any surface.'
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
title: 'WebSocket Protocol v2',
|
|
252
|
+
paragraphs: [
|
|
253
|
+
'The Mudra Companion application exposes a WebSocket server on localhost port 8766. Client applications connect and subscribe to specific signals they need: gesture, pressure, navigation, imu_acc, imu_gyro, snc, button, and battery.',
|
|
254
|
+
'Each signal is subscribed individually using the command format. Data arrives as typed JSON messages with the signal type and a data payload containing the relevant values and a timestamp.',
|
|
255
|
+
'Connection management follows standard WebSocket patterns with automatic reconnection. Applications should implement simulation mode to generate realistic synthetic data for development without a physical device.',
|
|
256
|
+
'The protocol supports bidirectional communication. The trigger_gesture command allows applications to programmatically trigger gesture events, useful for testing and accessibility features.'
|
|
257
|
+
],
|
|
258
|
+
callout: null
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
title: 'Accessibility Applications',
|
|
262
|
+
paragraphs: [
|
|
263
|
+
'Neural input technology has transformative potential for accessibility. People with limited mobility, speech difficulties, or conditions like ALS can use subtle muscle signals to communicate and control devices.',
|
|
264
|
+
'The gesture-to-speech paradigm maps neural gestures to spoken phrases, enabling communication through minimal physical effort. As the technology matures, the vocabulary and expressiveness of such systems will expand dramatically.',
|
|
265
|
+
'Environmental control systems allow users to operate smart home devices, navigate wheelchair controls, and interact with computers entirely through neural input. The low-latency, always-available nature of wrist-worn neural sensors makes them ideal for continuous assistive use.'
|
|
266
|
+
],
|
|
267
|
+
callout: 'Unlike eye-tracking or head-tracking systems, neural input works regardless of lighting conditions, head position, or whether the user is looking at a screen. It is truly eyes-free and hands-free interaction.'
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
title: 'Creative and Musical Applications',
|
|
271
|
+
paragraphs: [
|
|
272
|
+
'Artists and musicians are discovering novel expressive possibilities with neural input. The continuous nature of pressure and navigation signals maps naturally to musical parameters like volume, pitch bend, and filter cutoff.',
|
|
273
|
+
'Step sequencers controlled by neural input allow musicians to compose beats using gestures for step selection and navigation deltas for parameter movement. The physical subtlety of the interaction opens up performance possibilities impossible with traditional controllers.',
|
|
274
|
+
'Visual artists can control generative art parameters in real-time, creating a feedback loop between neural intention and visual output. Pressure controls brush size, navigation drives spatial transformations, and gestures trigger discrete creative actions.'
|
|
275
|
+
],
|
|
276
|
+
callout: null
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
title: 'Future Directions',
|
|
280
|
+
paragraphs: [
|
|
281
|
+
'The field of neural computing is advancing rapidly. Next-generation sensors will offer higher spatial resolution, capturing signals from individual motor units rather than aggregate muscle activity.',
|
|
282
|
+
'Machine learning models are becoming more personalized, adapting to individual users over time and recognizing an ever-growing vocabulary of gestures. Transfer learning techniques reduce the calibration burden for new users.',
|
|
283
|
+
'Integration with augmented and virtual reality systems represents a compelling frontier. Neural input provides the missing interaction modality for spatial computing, where traditional keyboards and mice are impractical.',
|
|
284
|
+
'As developers, we have the opportunity to shape this emerging interaction paradigm. The Mudra SDK and WebSocket protocol provide the foundation. What you build with it is limited only by imagination.'
|
|
285
|
+
],
|
|
286
|
+
callout: 'The ultimate vision is a seamless neural interface where the boundary between thought and digital action dissolves entirely. The technology in your hands today is the first step toward that future.'
|
|
287
|
+
}
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
/* ── Build Document ────────────────────────────────────────── */
|
|
291
|
+
const docScroll = document.getElementById('docScroll');
|
|
292
|
+
const tocEl = document.getElementById('toc');
|
|
293
|
+
const sectionEls = [];
|
|
294
|
+
const tocItems = [];
|
|
295
|
+
|
|
296
|
+
sections.forEach((sec, i) => {
|
|
297
|
+
/* Document section */
|
|
298
|
+
const div = document.createElement('div');
|
|
299
|
+
div.className = 'section';
|
|
300
|
+
div.id = 'sec-' + i;
|
|
301
|
+
|
|
302
|
+
const h2 = document.createElement('h2');
|
|
303
|
+
const numSpan = document.createElement('span');
|
|
304
|
+
numSpan.className = 'num';
|
|
305
|
+
numSpan.textContent = String(i + 1).padStart(2, '0');
|
|
306
|
+
h2.appendChild(numSpan);
|
|
307
|
+
h2.appendChild(document.createTextNode(sec.title));
|
|
308
|
+
div.appendChild(h2);
|
|
309
|
+
|
|
310
|
+
sec.paragraphs.forEach(text => {
|
|
311
|
+
const p = document.createElement('p');
|
|
312
|
+
p.textContent = text;
|
|
313
|
+
div.appendChild(p);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
if (sec.callout) {
|
|
317
|
+
const co = document.createElement('div');
|
|
318
|
+
co.className = 'callout';
|
|
319
|
+
co.textContent = sec.callout;
|
|
320
|
+
div.appendChild(co);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
docScroll.appendChild(div);
|
|
324
|
+
sectionEls.push(div);
|
|
325
|
+
|
|
326
|
+
/* TOC entry */
|
|
327
|
+
const tocItem = document.createElement('div');
|
|
328
|
+
tocItem.className = 'toc-item' + (i === 0 ? ' active' : '');
|
|
329
|
+
tocItem.textContent = sec.title;
|
|
330
|
+
tocItem.addEventListener('click', () => {
|
|
331
|
+
div.scrollIntoView({ behavior: 'smooth' });
|
|
332
|
+
});
|
|
333
|
+
tocEl.appendChild(tocItem);
|
|
334
|
+
tocItems.push(tocItem);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
/* ── Scrollbar ─────────────────────────────────────────────── */
|
|
338
|
+
const scrollbarTrack = document.getElementById('scrollbarTrack');
|
|
339
|
+
const scrollThumb = document.getElementById('scrollThumb');
|
|
340
|
+
let bookmarks = [];
|
|
341
|
+
let scanMode = false;
|
|
342
|
+
let scrollSpeedMult = 0.3;
|
|
343
|
+
|
|
344
|
+
function updateScrollbar() {
|
|
345
|
+
const el = docScroll;
|
|
346
|
+
const scrollH = el.scrollHeight;
|
|
347
|
+
const clientH = el.clientHeight;
|
|
348
|
+
if (scrollH <= clientH) { scrollThumb.style.display = 'none'; return; }
|
|
349
|
+
scrollThumb.style.display = 'block';
|
|
350
|
+
const ratio = clientH / scrollH;
|
|
351
|
+
const thumbH = Math.max(30, ratio * clientH);
|
|
352
|
+
scrollThumb.style.height = thumbH + 'px';
|
|
353
|
+
const scrollRatio = el.scrollTop / (scrollH - clientH);
|
|
354
|
+
scrollThumb.style.top = scrollRatio * (clientH - thumbH) + 'px';
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function updateCurrentSection() {
|
|
358
|
+
const scrollTop = docScroll.scrollTop + 60;
|
|
359
|
+
let current = 0;
|
|
360
|
+
sectionEls.forEach((el, i) => {
|
|
361
|
+
if (el.offsetTop <= scrollTop) current = i;
|
|
362
|
+
});
|
|
363
|
+
tocItems.forEach((item, i) => item.classList.toggle('active', i === current));
|
|
364
|
+
document.getElementById('sectionName').textContent = sections[current].title;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
docScroll.addEventListener('scroll', () => { updateScrollbar(); updateCurrentSection(); });
|
|
368
|
+
window.addEventListener('resize', updateScrollbar);
|
|
369
|
+
setTimeout(updateScrollbar, 100);
|
|
370
|
+
|
|
371
|
+
function addBookmark() {
|
|
372
|
+
const scrollH = docScroll.scrollHeight;
|
|
373
|
+
const clientH = docScroll.clientHeight;
|
|
374
|
+
if (scrollH <= clientH) return;
|
|
375
|
+
const pos = docScroll.scrollTop / (scrollH - clientH);
|
|
376
|
+
bookmarks.push({ pos, scrollTop: docScroll.scrollTop });
|
|
377
|
+
bookmarks.sort((a, b) => a.scrollTop - b.scrollTop);
|
|
378
|
+
/* render marker */
|
|
379
|
+
const marker = document.createElement('div');
|
|
380
|
+
marker.className = 'bookmark-marker';
|
|
381
|
+
marker.style.top = (pos * clientH) + 'px';
|
|
382
|
+
scrollbarTrack.appendChild(marker);
|
|
383
|
+
document.getElementById('bmCount').textContent = bookmarks.length + ' bookmark' + (bookmarks.length !== 1 ? 's' : '');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function jumpToNextBookmark() {
|
|
387
|
+
if (bookmarks.length === 0) return;
|
|
388
|
+
const current = docScroll.scrollTop;
|
|
389
|
+
const next = bookmarks.find(b => b.scrollTop > current + 10);
|
|
390
|
+
if (next) docScroll.scrollTo({ top: next.scrollTop, behavior: 'smooth' });
|
|
391
|
+
else docScroll.scrollTo({ top: bookmarks[0].scrollTop, behavior: 'smooth' });
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function clearBookmarks() {
|
|
395
|
+
bookmarks = [];
|
|
396
|
+
scrollbarTrack.querySelectorAll('.bookmark-marker').forEach(m => m.remove());
|
|
397
|
+
document.getElementById('bmCount').textContent = '0 bookmarks';
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function toggleScanMode() {
|
|
401
|
+
scanMode = !scanMode;
|
|
402
|
+
docScroll.classList.toggle('scan-mode', scanMode);
|
|
403
|
+
const badge = document.getElementById('modeBadge');
|
|
404
|
+
badge.textContent = scanMode ? 'Scan' : 'Reading';
|
|
405
|
+
badge.classList.toggle('scan', scanMode);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/* ── Signal Handlers ───────────────────────────────────────── */
|
|
409
|
+
let scrollAccY = 0;
|
|
410
|
+
|
|
411
|
+
on('navigation', (d) => {
|
|
412
|
+
const dy = d.delta_y || 0;
|
|
413
|
+
/* smooth scroll: accumulate and apply */
|
|
414
|
+
const speed = 2 + scrollSpeedMult * 8;
|
|
415
|
+
docScroll.scrollTop += dy * speed;
|
|
416
|
+
updateScrollbar();
|
|
417
|
+
updateCurrentSection();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
on('gesture', (d) => {
|
|
421
|
+
const type = d.type || 'unknown';
|
|
422
|
+
if (type === 'tap') addBookmark();
|
|
423
|
+
if (type === 'double_tap') toggleScanMode();
|
|
424
|
+
if (type === 'twist') jumpToNextBookmark();
|
|
425
|
+
if (type === 'double_twist') clearBookmarks();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
on('pressure', (d) => {
|
|
429
|
+
scrollSpeedMult = Math.max(0, Math.min(1, d.normalized ?? d.value / 100));
|
|
430
|
+
document.getElementById('speedFill').style.width = Math.round(scrollSpeedMult * 100) + '%';
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
/* ── Keyboard Fallback ─────────────────────────────────────── */
|
|
434
|
+
let kbSpeed = 0.3;
|
|
435
|
+
document.addEventListener('keydown', (e) => {
|
|
436
|
+
if (e.code === 'ArrowDown') {
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
docScroll.scrollTop += 40 + scrollSpeedMult * 60;
|
|
439
|
+
updateScrollbar(); updateCurrentSection();
|
|
440
|
+
}
|
|
441
|
+
if (e.code === 'ArrowUp') {
|
|
442
|
+
e.preventDefault();
|
|
443
|
+
docScroll.scrollTop -= 40 + scrollSpeedMult * 60;
|
|
444
|
+
updateScrollbar(); updateCurrentSection();
|
|
445
|
+
}
|
|
446
|
+
if (e.code === 'KeyB') addBookmark();
|
|
447
|
+
if (e.code === 'KeyN') jumpToNextBookmark();
|
|
448
|
+
if (e.code === 'KeyS') toggleScanMode();
|
|
449
|
+
if (e.key === ']') {
|
|
450
|
+
kbSpeed = Math.min(1, kbSpeed + 0.1);
|
|
451
|
+
emit('pressure', { value: Math.round(kbSpeed * 100), normalized: kbSpeed, timestamp: Date.now() });
|
|
452
|
+
}
|
|
453
|
+
if (e.key === '[') {
|
|
454
|
+
kbSpeed = Math.max(0, kbSpeed - 0.1);
|
|
455
|
+
emit('pressure', { value: Math.round(kbSpeed * 100), normalized: kbSpeed, timestamp: Date.now() });
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
/* ── Buttons ───────────────────────────────────────────────── */
|
|
460
|
+
document.getElementById('simTap').addEventListener('click', () => triggerGesture('tap'));
|
|
461
|
+
document.getElementById('simScan').addEventListener('click', () => triggerGesture('double_tap'));
|
|
462
|
+
document.getElementById('modeBadge').addEventListener('click', toggleScanMode);
|
|
463
|
+
|
|
464
|
+
/* ── Start ─────────────────────────────────────────────────── */
|
|
465
|
+
connect();
|
|
466
|
+
</script>
|
|
467
|
+
</body>
|
|
468
|
+
</html>
|