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,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>
|