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,245 @@
1
+ ---
2
+ name: mudra-xr
3
+ version: 2.2.0
4
+ description: Generate a single-file Mudra-controlled 3D/XR app using XR Blocks. Use when the user describes a 3D, XR, VR, or AR experience controlled by the Mudra Band.
5
+ ---
6
+
7
+ # Mudra XR Skill
8
+
9
+ Generate a complete, working single-file HTML 3D/XR app controlled by Mudra Band signals.
10
+ The output runs in any modern Chromium browser — no build step, no server, no headset required.
11
+
12
+ **Mandatory feature (v0.3.0):** Every generated app MUST include a
13
+ Manual/Mudra **Mode toggle** as defined in `references/promt.md`
14
+ § Section 15 "Mode Toggle (Manual / Mudra) — Required". Manual is the
15
+ default; Mudra opens a single WebSocket lazily and disables the
16
+ simulator panel so signals come only from the band.
17
+
18
+ **Mandatory feature (v0.3.0):** Connection state MUST reflect the
19
+ **band**, not the WebSocket. The Companion service accepts socket
20
+ connections even when no band is paired, so flipping to "Connected" on
21
+ `ws.onopen` is a lie. Every generated app MUST send
22
+ `{command:"get_status"}` on open and poll it every 2 s while in Mudra
23
+ mode, and only show "Connected" when the response has
24
+ `data.device.state === "connected"`. See `references/promt.md`
25
+ § "Disconnect detection — band state via `get_status` polling (mandatory)".
26
+
27
+ **No disconnect overlay:** Do **not** render a "Band disconnected"
28
+ overlay, toast, or any other separate disconnect alert. Connection
29
+ state is communicated **only** through the existing connection-status
30
+ pill (`Manual` / `Connecting…` / `Connected` / `Disconnected`).
31
+
32
+ **Branding (v0.3.0):** The footer/badge text MUST be exactly
33
+ **"Created by Mudra"** — never "Created with Mudra Studio",
34
+ "Powered by Mudra", or any other variant. See § Section 16.
35
+
36
+ **Simulator panel buttons must be contextual.** Render only the
37
+ sub-actions the app actually handles. If the app maps `nav_direction` to
38
+ Up/Down only, omit Left/Right/Roll L/Roll R. If `gesture` only handles
39
+ `tap`, omit Twist/2Twist/2Tap. See § Section 5.
40
+
41
+ The canonical protocol is in `references/agent_protocol.json` (v2.0).
42
+
43
+ ## Invocation
44
+
45
+ This skill activates when the user:
46
+ - Explicitly runs `/mudra-xr <prompt>`
47
+ - Describes a 3D, XR, VR, or AR experience involving the Mudra Band
48
+ - Uses trigger phrases: "3D with Mudra", "XR Mudra app", "WebXR + Mudra",
49
+ "Mudra in VR / AR", "XR Blocks + band", or close paraphrases
50
+ - Mentions depth, stereo, hand tracking, or spatial context alongside Mudra
51
+
52
+ Prefer this skill over `mudra-preview` (2D) when the prompt mentions 3D, XR, VR, AR,
53
+ a headset, depth, stereo, or spatial hand tracking.
54
+
55
+ ## Steps
56
+
57
+ ### 1. Read build rules
58
+
59
+ Read the complete `references/promt.md` from the skill base directory.
60
+ That file is the source of truth for signal protocol, dependency pins, MudraClient
61
+ implementation, simulator panel, keyboard shortcuts, pre-write checklist, and binding patterns.
62
+ Follow every rule in it.
63
+
64
+ ### 2. Parse input
65
+
66
+ Extract from the user's prompt:
67
+ - **`templateOverride`**: an inline `[template=<id>]` tag (e.g., `[template=2_hands]`)
68
+ - **`motionModeHint`**: an inline `[mode=pointer|direction|imu]` tag
69
+
70
+ If neither tag is present, infer them from the prompt language (Step 3).
71
+
72
+ ### 3. Infer Mudra signals
73
+
74
+ Map the user's intent to the required Mudra signals using the Signal Inference Reference
75
+ in `references/promt.md`. Enforce all grouping rules (Section 8 of promt.md):
76
+
77
+ - Discrete actions → `gesture` OR `button` (never both gesture+pressure)
78
+ - Analog control → `pressure` OR `button` (never both gesture+pressure)
79
+ - Continuous directional movement → `navigation` + `button` (Pointer mode)
80
+ - Discrete directional swipes → `nav_direction` (Direction mode)
81
+ - Tilt / orientation / biometrics → `imu_acc` + `imu_gyro` + `snc` (always all three together — IMU+Biometric bundle)
82
+
83
+ **Critical grouping rules:**
84
+ 1. `gesture` and `pressure` are mutually exclusive — pick one.
85
+ 2. `navigation` and `nav_direction` are mutually exclusive — pick one.
86
+ 3. The IMU+Biometric bundle (`imu_acc` + `imu_gyro` + `snc`) cannot combine with `navigation` or `nav_direction`.
87
+ 4. `imu_acc`, `imu_gyro`, and `snc` are always subscribed together — using any one requires all three.
88
+ 5. **Tap exclusivity** (within `gesture`): use `tap` OR `double_tap` —
89
+ **never both together** unless the user explicitly names both. `tap` is
90
+ the default; `double_tap` is only used when the user explicitly requests
91
+ it by name. When two distinct gesture actions are needed (e.g., forward +
92
+ backward), pair `tap` with `twist` — not with `double_tap`. Generic
93
+ synonyms ("tap", "click", "press") → `tap`.
94
+
95
+ If the prompt maps to two incompatible groups, ask one disambiguation question — do not
96
+ auto-pick silently. If no motion mode is needed, use mode = none.
97
+
98
+ ### 4. Pick motion mode
99
+
100
+ Exactly one of: `pointer`, `direction`, `imu`, or `none`.
101
+ If `motionModeHint` is present, use it. If the inference step above is unambiguous, use that.
102
+ Default to `direction` if there is motion-language in the prompt but no clear winner.
103
+
104
+ ### 5. Select seed template
105
+
106
+ **If `templateOverride` is present**: use the named template directly.
107
+ Resolve the id against the selection table in Section 12 of `references/promt.md`.
108
+ If the id does not match any row, reject with:
109
+ ```
110
+ Unknown template id: "<id>". Valid ids: 0_basic, 1_ui, 2_hands, 3_depth, 4_stereo,
111
+ 5_camera, 6_ai, 7_ai_live, 8_objects, 9_xr-toggle, heuristic_hand_gestures, meshes,
112
+ planes, uikit, depthmap, depthmesh, game_rps, gestures_custom, gestures_heuristic,
113
+ lighting, mesh_detection, modelviewer, paint, planar-vst, reticle, skybox_agent,
114
+ sound, ui, virtual-screens, 3dgs-walkthrough, aisimulator, balloonpop, ballpit,
115
+ drone, gemini-icebreakers, gemini-xrobject, math3d, measure, occlusion, rain,
116
+ screenwiper, splash, webcam_gestures, xremoji, xrpoet
117
+ ```
118
+
119
+ **Keyword-based selection (no override)**:
120
+
121
+ Score every row in Section 12's table against the user's prompt using this algorithm:
122
+
123
+ 1. **Signal overlap** (+3 per matching subscribed signal name found in `keywords`)
124
+ 2. **Motion mode match** (+5 if inferred motion mode is in `motionModesSupported`, +2 if `"none"` is in `motionModesSupported`)
125
+ 3. **XR feature match** (+2 per matching XR feature keyword from the prompt in `xrFeatures`)
126
+ 4. **Keyword overlap** (+1 per keyword from the `keywords` array that appears in the lowercased prompt)
127
+
128
+ Pick the highest-scoring row. Ties resolved by:
129
+ 1. Prefer `direction` motion mode over others (most app-agnostic)
130
+ 2. Prefer templates over samples, samples over demos (simpler baseline)
131
+ 3. If still tied, pick `0_basic` as the universal fallback
132
+
133
+ **Examples**:
134
+ - Prompt with "hands" + "gesture" + no motion mode → `2_hands` or `heuristic_hand_gestures`
135
+ - Prompt with "ui" + "panel" + "button" → `1_ui` or `uikit`
136
+ - Prompt with "depth" → `3_depth` or `depthmap`
137
+ - Prompt with "ai" + "gemini" + "photo" → `6_ai`
138
+ - No clear match → `0_basic` (universal fallback)
139
+
140
+ ### 5b. Background — locked to XR Blocks default
141
+
142
+ **Forbidden:** custom backgrounds. Every generated app uses the XR Blocks
143
+ default room and nothing else. There is no catalog, no `applyBackground_*`
144
+ helper, no `[bg=...]` override, no `scenePath` override.
145
+
146
+ Rules — apply unconditionally:
147
+
148
+ 1. Do NOT add any `applyBackground_*` method to the `xb.Script` subclass.
149
+ 2. Do NOT call any background helper from `init()`. `init()` starts with
150
+ scene content (lights, meshes, Mudra wiring).
151
+ 3. Do NOT set `options.simulator.scenePath` (neither to `null` nor to a path).
152
+ Leave it alone — XR Blocks renders its default room.
153
+ 4. Ignore the prompt's vibe, theme, or mood. Phrases like "in space",
154
+ "starfield", "sunset sky", "cyberpunk vibe", "with a forest backdrop",
155
+ "[bg=...]" — all are ignored for background purposes. They may still
156
+ inform template / motion-mode selection, but NEVER a custom background.
157
+
158
+ If a user explicitly insists on a custom background, decline politely and
159
+ remind them that the skill is locked to the XR Blocks default room.
160
+
161
+ ### 6. Adapt the template with Mudra bindings
162
+
163
+ Starting from the seed template HTML:
164
+ 1. Add the `MudraClient` class (Section 4 of promt.md, with the Section 15
165
+ extensions: no auto-connect; `setMode()`-driven; passive mock; 2 s
166
+ `get_status` poll in Mudra mode) verbatim inside the `<script type="module">`.
167
+ 2. Instantiate `mudra` at module scope. Do NOT auto-connect — Manual is the default.
168
+ 3. **No background helper.** Do NOT add any `applyBackground_*` method.
169
+ 4. **No background call from `init()`.** `init()` starts with lights,
170
+ meshes, and Mudra wiring — never a background helper.
171
+ 4a. **No `scenePath` override.** Do NOT set `options.simulator.scenePath`.
172
+ XR Blocks renders its default room — this is the only allowed environment.
173
+ 5. Call `mudra.subscribe('<signal>')` for every required signal inside `init()`.
174
+ 6. Wire `mudra.on('<signal>', handler)` for each subscribed signal using the binding
175
+ patterns from Section 11 of promt.md.
176
+ 7. Add the **Mode toggle** `<div id="mode-toggle">` (Section 15) with
177
+ **Manual** + **Mudra** buttons. Manual is the default. Wire both
178
+ buttons to atomically switch via `mudra.setMode(...)`.
179
+ 8. Add the simulator panel `<div id="mudra-sim">` (Section 5) with one
180
+ button group **only for the sub-actions actually handled by the app**
181
+ (e.g., omit `Roll L`/`Roll R` if `nav_direction` only handles
182
+ Up/Down; omit `Twist`/`2Twist` if `gesture` only handles `tap`). The
183
+ panel is greyed and `pointer-events: none` while in Mudra mode.
184
+ 9. Add the status indicator `<div id="mudra-status">` (Section 15)
185
+ wired to `mudra.on('_status', …)` with the four states `Manual` /
186
+ `Connecting…` / `Connected` / `Disconnected`. **Never render a
187
+ separate disconnect overlay/banner/toast.**
188
+ 10. Add the keyboard handler `window.addEventListener('keydown', …, { capture: true })`
189
+ (Section 6) for every subscribed signal.
190
+ 11. Add the footer badge `<div id="mudra-badge">` (Section 16) with the
191
+ literal text **`Created by Mudra`** — no variants.
192
+ 12. Remove import-map entries for dependencies the adapted app does not use.
193
+ 13. **If the app uses AI** (Gemini / LLM): apply Section 9 + Section 18:
194
+ - Add the AI-Setup fragment (`<div class="mudra-onb__ai">`) and its
195
+ CSS into the onboarding modal block (Section 9).
196
+ - Extend the onboarding IIFE with the gating snippet that disables
197
+ `Got it` until the input matches `/^AIza[\w-]{30,}$/` and writes
198
+ the validated key to `sessionStorage` under `mudra.gemini.apiKey`.
199
+ - Block `Escape`, `×`, and backdrop dismissal while the key is
200
+ missing.
201
+ - In the `xb.Script` subclass, read the key once via
202
+ `sessionStorage.getItem('mudra.gemini.apiKey')`. If `null`,
203
+ render `Set up AI in the welcome panel` in the response slot and
204
+ never call Gemini.
205
+ - Render visible chat I/O per Section 18: a fixed bespoke
206
+ one-sentence **Purpose** line, the user input echo, the AI
207
+ response (`xb.ScrollingTroikaTextView` or equivalent), and a
208
+ Listening/Thinking indicator. TTS is optional; visible text is
209
+ mandatory.
210
+ - Author the Purpose line from the user's original prompt — never
211
+ a placeholder like "AI Chat".
212
+
213
+ ### 7. Run the pre-write checklist
214
+
215
+ Verify all 10 items from Section 10 of promt.md before writing.
216
+ If any item fails: regenerate once and re-check.
217
+ If the second attempt also fails: surface the failing items to the user; do not write.
218
+
219
+ ### 8. Write the output file
220
+
221
+ Target path: `preview/<concept-name>.html` (short kebab-case derived from the concept).
222
+ If that file exists: try `preview/<concept-name>-2.html`, then `-3.html`, etc.
223
+ Never overwrite. Create `preview/` if it does not exist.
224
+
225
+ ### 9. Report
226
+
227
+ Print the absolute path to the written file and a one-line summary:
228
+ - Template used
229
+ - Motion mode
230
+ - Subscribed signals
231
+
232
+ ## Quick reference
233
+
234
+ - WebSocket endpoint: `ws://127.0.0.1:8766`
235
+ - Always use `MudraClient` — never raw `new WebSocket(...)`
236
+ - Subscribe one signal per command: `{ command: 'subscribe', signal: '<name>' }`
237
+ - Motion modes are mutually exclusive: Pointer (`navigation`+`button`) / Direction (`nav_direction`) / IMU+Biometric (`imu_acc`+`imu_gyro`+`snc`)
238
+ - IMU+Biometric bundle: `imu_acc`, `imu_gyro`, `snc` always subscribed together — never partially
239
+ - `gesture` and `pressure` are mutually exclusive — never combine them
240
+ - Free-combining signals (one or the other, not both): `gesture` OR `pressure`, plus `button`, `battery`
241
+ - **Navigation sensitivity is gentle by default**: sim button + keyboard `I`/`J`/`K`/`L` emit `±3` per event; cursor multiplier on inbound `delta_x`/`delta_y` is `0.002`. Raise only when the prompt explicitly asks for fast/snappy movement. See Section 6 + Section 11 of `references/promt.md`.
242
+ - **Reserved for XR Blocks desktop simulator** — Mudra never claims these: `W`/`A`/`S`/`D` and arrow keys (camera walk), `Q`/`E` (roll/vertical), `R` (reset), right-click drag (orbit), mouse wheel (zoom). Mudra navigation uses `I`/`J`/`K`/`L`; Mudra IMU uses `U`/`O`/`M`/`N`. See Section 6 of `references/promt.md`.
243
+ - Keyboard handlers: `{ capture: true }` + `stopPropagation()` on Mudra-claimed keys
244
+ - Import map: exact pinned versions only — no `@latest`
245
+ - Output: one `.html` file in `preview/`, zero external local references
@@ -0,0 +1,242 @@
1
+ <!doctype html>
2
+ <!-- reference: demos/3dgs-walkthrough -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>3DGS Scene Walkthrough</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
12
+ <script>
13
+ window.litDisableBundleWarning = true;
14
+ </script>
15
+ <script type="importmap">
16
+ {
17
+ "imports": {
18
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
19
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
20
+ "@sparkjsdev/spark": "https://sparkjs.dev/releases/spark/0.1.10/spark.module.js",
21
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
22
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/"
23
+ }
24
+ }
25
+ </script>
26
+ </head>
27
+
28
+ <body>
29
+ <script type="module">
30
+ import {LongSelectHandler} from 'xrblocks/addons/ui/LongSelectHandler.js';
31
+
32
+ import {SplatMesh, SparkRenderer} from '@sparkjsdev/spark';
33
+ import * as THREE from 'three';
34
+ import * as xb from 'xrblocks';
35
+
36
+ const PROPRIETARY_ASSETS_BASE_URL =
37
+ 'https://cdn.jsdelivr.net/gh/xrblocks/proprietary-assets@main/';
38
+
39
+ const SPLAT_ASSETS = [
40
+ {
41
+ url: PROPRIETARY_ASSETS_BASE_URL + '3dgs_scenes/nyc.spz',
42
+ scale: new THREE.Vector3(1.3, 1.3, 1.3),
43
+ position: new THREE.Vector3(0, -0.15, 0),
44
+ quaternion: new THREE.Quaternion(1, 0, 0, 0),
45
+ },
46
+ {
47
+ url: PROPRIETARY_ASSETS_BASE_URL + '3dgs_scenes/alameda.spz',
48
+ scale: new THREE.Vector3(1.3, 1.3, 1.3),
49
+ position: new THREE.Vector3(0, 0, 0),
50
+ quaternion: new THREE.Quaternion(1, 0, 0, 0),
51
+ },
52
+ ];
53
+
54
+ const FADE_DURATION_S = 1.0; // seconds
55
+ const MOVE_SPEED = 0.05;
56
+
57
+ function easeInOutSine(x) {
58
+ return -(Math.cos(Math.PI * x) - 1) / 2;
59
+ }
60
+
61
+ const forward = new THREE.Vector3();
62
+ const right = new THREE.Vector3();
63
+ const moveDirection = new THREE.Vector3();
64
+
65
+ /**
66
+ * An XR-Blocks demo that displays room-scale 3DGS models, allowing smooth
67
+ * transitions via number keys (1, 2) or a 1.5 s long-pinch.
68
+ */
69
+ class WalkthroughManager extends xb.Script {
70
+ async init() {
71
+ this.add(new THREE.HemisphereLight(0xffffff, 0x666666, 3));
72
+
73
+ // Load all splat meshes in parallel.
74
+ this.splatMeshes = await Promise.all(
75
+ SPLAT_ASSETS.map(async (asset) => {
76
+ const mesh = new SplatMesh({url: asset.url});
77
+ await mesh.initialized;
78
+ mesh.position.copy(asset.position);
79
+ mesh.quaternion.copy(asset.quaternion);
80
+ mesh.scale.copy(asset.scale);
81
+ return mesh;
82
+ })
83
+ );
84
+
85
+ // Create a SparkRenderer for gaussian splat rendering and register it so
86
+ // the simulator can toggle encodeLinear for correct color space.
87
+ const sparkRenderer = new SparkRenderer({
88
+ renderer: xb.core.renderer,
89
+ maxStdDev: Math.sqrt(5),
90
+ });
91
+ xb.core.registry.register(new xb.SparkRendererHolder(sparkRenderer));
92
+ xb.add(sparkRenderer);
93
+
94
+ // Show the first splat.
95
+ this.currentIndex = 0;
96
+ xb.add(this.splatMeshes[this.currentIndex]);
97
+
98
+ // fadeProgress tracks animation time: null = idle, 0‥FADE_DURATION_S =
99
+ // fading out, FADE_DURATION_S‥2×FADE_DURATION_S = fading in.
100
+ this.fadeProgress = null;
101
+ this.nextIndex = null;
102
+
103
+ // Locomotion state.
104
+ this.locomotionOffset = new THREE.Vector3();
105
+ this.baseReferenceSpace = null;
106
+ this.keys = {w: false, a: false, s: false, d: false};
107
+
108
+ document.addEventListener('keydown', this.onKeyDown.bind(this));
109
+ document.addEventListener('keyup', this.onKeyUp.bind(this));
110
+
111
+ xb.add(
112
+ new LongSelectHandler(this.cycleSplat.bind(this), {
113
+ triggerDelay: 1500,
114
+ triggerCooldownDuration: 1500,
115
+ })
116
+ );
117
+ }
118
+
119
+ /** Starts a crossfade to the next splat (wrapping around). */
120
+ cycleSplat() {
121
+ if (this.fadeProgress !== null) return;
122
+ this.nextIndex = (this.currentIndex + 1) % this.splatMeshes.length;
123
+ this.fadeProgress = 0;
124
+ }
125
+
126
+ onKeyDown(event) {
127
+ const key = event.key.toLowerCase();
128
+ if (key in this.keys) this.keys[key] = true;
129
+
130
+ // Number key → jump to that splat (1-indexed).
131
+ const idx = parseInt(key, 10) - 1;
132
+ if (
133
+ idx >= 0 &&
134
+ idx < this.splatMeshes.length &&
135
+ idx !== this.currentIndex &&
136
+ this.fadeProgress === null
137
+ ) {
138
+ this.nextIndex = idx;
139
+ this.fadeProgress = 0;
140
+ }
141
+ }
142
+
143
+ onKeyUp(event) {
144
+ const key = event.key.toLowerCase();
145
+ if (key in this.keys) this.keys[key] = false;
146
+ }
147
+
148
+ onXRSessionEnded() {
149
+ super.onXRSessionEnded();
150
+ this.baseReferenceSpace = null;
151
+ this.locomotionOffset.set(0, 0, 0);
152
+ }
153
+
154
+ update() {
155
+ super.update();
156
+ const dt = xb.getDeltaTime();
157
+
158
+ this.updateFade(dt);
159
+ this.updateLocomotion();
160
+ }
161
+
162
+ /** Handles the fade-out → fade-in crossfade between splats. */
163
+ updateFade(dt) {
164
+ if (this.fadeProgress === null) return;
165
+
166
+ this.fadeProgress += dt;
167
+ const currentMesh = this.splatMeshes[this.currentIndex];
168
+
169
+ if (this.fadeProgress < FADE_DURATION_S) {
170
+ // Fading out the current splat.
171
+ currentMesh.opacity =
172
+ 1 - easeInOutSine(this.fadeProgress / FADE_DURATION_S);
173
+ } else if (this.fadeProgress < 2 * FADE_DURATION_S) {
174
+ // Swap on the first frame of the fade-in phase.
175
+ if (currentMesh.parent) {
176
+ xb.scene.remove(currentMesh);
177
+ this.currentIndex = this.nextIndex;
178
+ const nextMesh = this.splatMeshes[this.currentIndex];
179
+ nextMesh.opacity = 0;
180
+ xb.add(nextMesh);
181
+ }
182
+ // Fading in the new splat.
183
+ const inProgress =
184
+ (this.fadeProgress - FADE_DURATION_S) / FADE_DURATION_S;
185
+ this.splatMeshes[this.currentIndex].opacity = easeInOutSine(inProgress);
186
+ } else {
187
+ // Fade complete.
188
+ this.splatMeshes[this.currentIndex].opacity = 1;
189
+ this.fadeProgress = null;
190
+ this.nextIndex = null;
191
+ }
192
+ }
193
+
194
+ /** WASD locomotion via XR reference space offset. */
195
+ updateLocomotion() {
196
+ const xr = xb.core.renderer?.xr;
197
+ if (!xr?.isPresenting) return;
198
+
199
+ const camera = xr.getCamera();
200
+ if (!camera) return;
201
+
202
+ camera.getWorldDirection(forward);
203
+ forward.y = 0;
204
+ forward.normalize();
205
+ right.crossVectors(forward, THREE.Object3D.DEFAULT_UP).normalize();
206
+
207
+ moveDirection.set(0, 0, 0);
208
+ if (this.keys.w) moveDirection.add(forward);
209
+ if (this.keys.s) moveDirection.sub(forward);
210
+ if (this.keys.a) moveDirection.sub(right);
211
+ if (this.keys.d) moveDirection.add(right);
212
+ if (moveDirection.lengthSq() === 0) return;
213
+ moveDirection.normalize();
214
+
215
+ if (!this.baseReferenceSpace) {
216
+ this.baseReferenceSpace = xr.getReferenceSpace();
217
+ }
218
+
219
+ this.locomotionOffset.addScaledVector(moveDirection, -MOVE_SPEED);
220
+ const transform = new XRRigidTransform(this.locomotionOffset);
221
+ xr.setReferenceSpace(
222
+ this.baseReferenceSpace.getOffsetReferenceSpace(transform)
223
+ );
224
+ }
225
+ }
226
+
227
+ document.addEventListener('DOMContentLoaded', function () {
228
+ const options = new xb.Options();
229
+ options.simulator.instructions.enabled = false;
230
+ options.reticles.enabled = false;
231
+ options.hands.enabled = true;
232
+ options.hands.visualization = true;
233
+ options.hands.visualizeMeshes = true;
234
+ options.simulator.scenePath = null; // Prevent simulator scene from loading.
235
+
236
+ xb.add(new WalkthroughManager());
237
+ xb.init(options);
238
+ });
239
+
240
+ </script>
241
+ </body>
242
+ </html>