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,133 @@
1
+ <!doctype html>
2
+ <!-- reference: samples/paint -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Paint | XR Blocks</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
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
21
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
22
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
23
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
24
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
25
+ "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
26
+ "lit/": "https://esm.run/lit@3/",
27
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
28
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/"
29
+ }
30
+ }
31
+ </script>
32
+ </head>
33
+
34
+ <body>
35
+ <script type="module">
36
+ import 'xrblocks/addons/simulator/SimulatorAddons.js';
37
+
38
+ import * as THREE from 'three';
39
+ import {TubePainter} from 'three/addons/misc/TubePainter.js';
40
+ import * as xb from 'xrblocks';
41
+
42
+ /**
43
+ * A remixed version of three.js's examples/webxr_xr_paint.html in XR Blocks.
44
+ * PaintDemo is an example script for using pinch to paint in Android XR and
45
+ * using clicks to draw in desktop simulated environments.
46
+ */
47
+ class PaintDemo extends xb.Script {
48
+ init() {
49
+ this.add(new THREE.HemisphereLight(0xffffff, 0x666666, /*intensity=*/ 3));
50
+
51
+ // Painting setup.
52
+ this.painters = [];
53
+ this.user = xb.core.user;
54
+
55
+ for (let i = 0; i < this.user.controllers.length; ++i) {
56
+ const painter = new TubePainter();
57
+ this.painters.push(painter);
58
+ this.add(painter.mesh);
59
+ }
60
+
61
+ // Adds pivotal points to indicate user's intents.
62
+ this.user.enablePivots();
63
+ }
64
+
65
+ /**
66
+ * Moves the painter to the pivot position when select starts.
67
+ * @param {XRInputSourceEvent} event
68
+ */
69
+ onSelectStart(event) {
70
+ const id = event.target.userData.id;
71
+ const painter = this.painters[id];
72
+ const cursor = this.user.getPivotPosition(id);
73
+ painter.moveTo(cursor);
74
+ }
75
+
76
+ /**
77
+ * Updates the painter's line to the current pivot position during selection.
78
+ * @param {XRInputSourceEvent} event
79
+ */
80
+ onSelecting(event) {
81
+ const id = event.target.userData.id;
82
+ const painter = this.painters[id];
83
+ const cursor = this.user.getPivotPosition(id);
84
+ painter.lineTo(cursor);
85
+ painter.update();
86
+ }
87
+
88
+ /**
89
+ * Stores the initial position and scale of the controller when squeeze
90
+ * starts.
91
+ * @param {XRInputSourceEvent} event
92
+ */
93
+ onSqueezeStart(event) {
94
+ const controller = event.target;
95
+ const id = controller.userData.id;
96
+ const data = this.user.data[id].squeeze;
97
+
98
+ data.positionOnStart = controller.position.y;
99
+ data.scaleOnStart = controller.position.y;
100
+ }
101
+
102
+ /**
103
+ * Updates the scale of the controller's pivot based on the squeeze amount.
104
+ * @param {XRInputSourceEvent} event
105
+ */
106
+ onSqueezing(event) {
107
+ const controller = event.target;
108
+ const id = controller.userData.id;
109
+ const pivot = this.user.getPivot(id);
110
+ const data = this.user.data[id].squeeze;
111
+
112
+ const delta = (controller.position.y - data.positionOnStart) * 5;
113
+ const scale = Math.max(0.1, data.scaleOnStart + delta);
114
+ pivot.scale.setScalar(scale);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Entry point for the application.
120
+ */
121
+ function start() {
122
+ const options = new xb.Options();
123
+ options.simulator.instructions.enabled = false;
124
+ options.setAppTitle('XR Paint');
125
+ xb.add(new PaintDemo());
126
+ xb.init(options);
127
+ }
128
+
129
+ document.addEventListener('DOMContentLoaded', start);
130
+
131
+ </script>
132
+ </body>
133
+ </html>
@@ -0,0 +1,45 @@
1
+ <!doctype html>
2
+ <!-- reference: samples/planar-vst -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Planar VST | XR Blocks</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 type="importmap">
13
+ {
14
+ "imports": {
15
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
16
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
17
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
18
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/"
19
+ }
20
+ }
21
+ </script>
22
+ </head>
23
+
24
+ <body>
25
+ <script type="module">
26
+ import * as THREE from 'three';
27
+ import * as xb from 'xrblocks';
28
+ import {PlanarVST} from 'xrblocks/addons/camera/PlanarVST.js';
29
+
30
+ document.addEventListener('DOMContentLoaded', function () {
31
+ const options = new xb.Options();
32
+ options.simulator.instructions.enabled = false;
33
+ options.enableCamera();
34
+ options.deviceCamera.videoConstraints = {
35
+ width: {ideal: 1280},
36
+ height: {ideal: 720},
37
+ facingMode: 'environment',
38
+ };
39
+ options.enableDepth();
40
+ xb.add(new PlanarVST());
41
+ xb.init(options);
42
+ });
43
+ </script>
44
+ </body>
45
+ </html>
@@ -0,0 +1,95 @@
1
+ <!doctype html>
2
+ <!-- reference: samples/reticle -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Reticle | XR Blocks</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+
12
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
13
+ <script type="importmap">
14
+ {
15
+ "imports": {
16
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
17
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
18
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
19
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
20
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
21
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
22
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
23
+ "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
24
+ "lit/": "https://esm.run/lit@3/",
25
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
26
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/"
27
+ }
28
+ }
29
+ </script>
30
+ </head>
31
+
32
+ <body>
33
+ <script type="module">
34
+ import * as xb from 'xrblocks';
35
+ import {TextBillboard} from 'xrblocks/addons/ui/TextBillboard.js';
36
+
37
+ class ReticleVisualizer extends xb.Script {
38
+ activeControllerToBillboardMap = new Map();
39
+
40
+ init() {
41
+ xb.showReticleOnDepthMesh(true);
42
+ }
43
+
44
+ onSelectStart(event) {
45
+ const controller = event.target;
46
+ const intersection = xb.core.user.select(
47
+ xb.core.depth.depthMesh,
48
+ controller
49
+ );
50
+ if (!intersection) return;
51
+ const billboard = new TextBillboard();
52
+ this.add(billboard);
53
+ this.activeControllerToBillboardMap.set(controller, billboard);
54
+ this.updateBillboard(controller, billboard);
55
+ }
56
+
57
+ onSelectEnd(event) {
58
+ this.activeControllerToBillboardMap.delete(event.target);
59
+ }
60
+
61
+ update() {
62
+ this.activeControllerToBillboardMap.forEach((billboard, controller) => {
63
+ this.updateBillboard(controller, billboard);
64
+ });
65
+ }
66
+
67
+ updateBillboard(controller, billboard) {
68
+ const intersection = xb.core.user.select(
69
+ xb.core.depth.depthMesh,
70
+ controller
71
+ );
72
+ if (intersection) {
73
+ const reticleHeight = intersection.point.y;
74
+ billboard.position.copy(intersection.point);
75
+ billboard.lookAt(xb.core.camera.position);
76
+ billboard.updateText(
77
+ `Distance: ${intersection.distance.toFixed(2)} m\n` +
78
+ `Height: ${reticleHeight.toFixed(2)} m`
79
+ );
80
+ }
81
+ }
82
+ }
83
+
84
+ document.addEventListener('DOMContentLoaded', function () {
85
+ const options = new xb.Options();
86
+ options.simulator.instructions.enabled = false;
87
+ options.depth = new xb.DepthOptions(xb.xrDepthMeshOptions);
88
+ options.setAppTitle('XR Reticle');
89
+ xb.add(new ReticleVisualizer());
90
+ xb.init(options);
91
+ });
92
+
93
+ </script>
94
+ </body>
95
+ </html>
@@ -0,0 +1,384 @@
1
+ <!doctype html>
2
+ <!-- reference: samples/skybox_agent -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Gemini Live Skybox Agent | XR Blocks</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+
12
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
13
+ <script>
14
+ window.litDisableBundleWarning = true;
15
+ </script>
16
+ <script type="importmap">
17
+ {
18
+ "imports": {
19
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
20
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
21
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
22
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
23
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
24
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
25
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
26
+ "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
27
+ "lit/": "https://esm.run/lit@3/",
28
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
29
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/",
30
+ "@google/genai": "https://cdn.jsdelivr.net/npm/@google/genai/+esm"
31
+ }
32
+ }
33
+ </script>
34
+ </head>
35
+
36
+ <body>
37
+ <script type="module">
38
+ /* eslint-env browser */
39
+ import 'xrblocks/addons/simulator/SimulatorAddons.js';
40
+
41
+ import * as THREE from 'three';
42
+ import * as xb from 'xrblocks';
43
+
44
+ // --- Inlined: TranscriptionManager.js ---
45
+ class TranscriptionManager {
46
+ constructor(responseDisplay) {
47
+ this.responseDisplay = responseDisplay;
48
+ this.currentInputText = '';
49
+ this.currentOutputText = '';
50
+ this.conversationHistory = [];
51
+ }
52
+
53
+ handleInputTranscription(text) {
54
+ if (!text) return;
55
+ this.currentInputText += text;
56
+ this.updateLiveDisplay();
57
+ }
58
+
59
+ handleOutputTranscription(text) {
60
+ if (!text) return;
61
+ this.currentOutputText += text;
62
+ this.updateLiveDisplay();
63
+ }
64
+
65
+ finalizeTurn() {
66
+ if (this.currentInputText.trim()) {
67
+ this.conversationHistory.push({
68
+ speaker: 'You',
69
+ text: this.currentInputText.trim(),
70
+ });
71
+ }
72
+ if (this.currentOutputText.trim()) {
73
+ this.conversationHistory.push({
74
+ speaker: 'AI',
75
+ text: this.currentOutputText.trim(),
76
+ });
77
+ }
78
+ this.currentInputText = '';
79
+ this.currentOutputText = '';
80
+ this.updateFinalDisplay();
81
+ }
82
+
83
+ updateLiveDisplay() {
84
+ let displayText = '';
85
+ for (const entry of this.conversationHistory.slice(-2)) {
86
+ displayText += `${entry.speaker}: ${entry.text}\n\n`;
87
+ }
88
+ if (this.currentInputText.trim()) {
89
+ displayText += `You: ${this.currentInputText}`;
90
+ }
91
+ if (this.currentOutputText.trim()) {
92
+ if (this.currentInputText.trim()) displayText += '\n\n';
93
+ displayText += `AI: ${this.currentOutputText}`;
94
+ }
95
+ this.responseDisplay?.setText(displayText);
96
+ }
97
+
98
+ updateFinalDisplay() {
99
+ let displayText = '';
100
+ for (const entry of this.conversationHistory) {
101
+ displayText += `${entry.speaker}: ${entry.text}\n\n`;
102
+ }
103
+ this.responseDisplay?.setText(displayText);
104
+ }
105
+
106
+ clear() {
107
+ this.currentInputText = '';
108
+ this.currentOutputText = '';
109
+ this.conversationHistory = [];
110
+ }
111
+
112
+ addText(text) {
113
+ this.responseDisplay?.addText(text + '\n\n');
114
+ }
115
+
116
+ setText(text) {
117
+ this.responseDisplay?.setText(text);
118
+ }
119
+ }
120
+
121
+ // --- Inlined: GeminiSkyboxGenerator.js ---
122
+ class GeminiSkyboxGenerator extends xb.Script {
123
+ constructor() {
124
+ super();
125
+ this.transcription = null;
126
+ this.liveAgent = null;
127
+ this.statusText = null;
128
+ this.defaultText =
129
+ "I am a skybox designer agent. Describe the background you want, and I'll render it for you!";
130
+ }
131
+
132
+ init() {
133
+ this.createTextDisplay();
134
+ this.createAgent();
135
+
136
+ this.add(new THREE.HemisphereLight(0x888877, 0x777788, 3));
137
+ const light = new THREE.DirectionalLight(0xffffff, 5.0);
138
+ light.position.set(-0.5, 4, 1.0);
139
+ this.add(light);
140
+ }
141
+
142
+ createAgent() {
143
+ this.liveAgent = new xb.SkyboxAgent(
144
+ xb.core.ai,
145
+ xb.core.sound,
146
+ xb.core.scene,
147
+ {
148
+ onSessionStart: () => {
149
+ this.updateButtonState();
150
+ this.updateStatus('Session started - Ready to listen');
151
+ },
152
+ onSessionEnd: () => {
153
+ this.updateButtonState();
154
+ this.transcription?.clear();
155
+ this.transcription?.setText(this.defaultText);
156
+ this.updateStatus('Session ended');
157
+ },
158
+ onError: (error) => {
159
+ this.updateStatus(`Error: ${error.message}`);
160
+ this.transcription?.addText(`✗ Error: ${error.message}`);
161
+ },
162
+ }
163
+ );
164
+ }
165
+
166
+ async toggleGeminiLive() {
167
+ const isActive = this.liveAgent?.getSessionState().isActive;
168
+ return isActive ? this.stopGeminiLive() : this.startGeminiLive();
169
+ }
170
+
171
+ async startGeminiLive() {
172
+ if (this.liveAgent?.getSessionState().isActive) return;
173
+
174
+ try {
175
+ this.updateStatus('Starting session...');
176
+
177
+ // Enable audio BEFORE starting the session
178
+ await xb.core.sound.enableAudio();
179
+
180
+ // Start live session with callbacks
181
+ await this.liveAgent.startLiveSession({
182
+ onopen: () => {
183
+ this.updateStatus('Connected - Listening...');
184
+ },
185
+ onmessage: (message) => this.handleAIMessage(message),
186
+ onclose: (closeEvent) => {
187
+ this.handleSessionClose(closeEvent);
188
+ },
189
+ });
190
+ } catch (error) {
191
+ this.updateStatus(`Failed to start: ${error.message}`);
192
+ this.transcription?.addText(
193
+ `Error: Failed to start AI session - ${error.message}`
194
+ );
195
+ await this.cleanup();
196
+ }
197
+ }
198
+
199
+ async stopGeminiLive() {
200
+ if (!this.liveAgent?.getSessionState().isActive) return;
201
+ await this.cleanup();
202
+ }
203
+
204
+ handleSessionClose(closeEvent) {
205
+ if (closeEvent.reason) {
206
+ this.transcription?.addText(closeEvent.reason);
207
+ }
208
+ xb.core.sound?.disableAudio();
209
+ xb.core.sound?.stopAIAudio();
210
+ }
211
+
212
+ createTextDisplay() {
213
+ this.textPanel = new xb.SpatialPanel({
214
+ width: 3,
215
+ height: 1.8,
216
+ backgroundColor: '#1a1a1abb',
217
+ });
218
+ const grid = this.textPanel.addGrid();
219
+
220
+ const statusRow = grid.addRow({weight: 0.1});
221
+ this.statusText = statusRow.addText({
222
+ text: 'Click Start to begin',
223
+ fontSize: 0.04,
224
+ fontColor: '#4ecdc4',
225
+ textAlign: 'center',
226
+ });
227
+
228
+ const responseDisplay = new xb.ScrollingTroikaTextView({
229
+ text: this.defaultText,
230
+ fontSize: 0.03,
231
+ textAlign: 'left',
232
+ });
233
+ grid.addRow({weight: 0.65}).add(responseDisplay);
234
+ this.transcription = new TranscriptionManager(responseDisplay);
235
+
236
+ this.toggleButton = grid.addRow({weight: 0.25}).addTextButton({
237
+ text: '▶ Start',
238
+ fontColor: '#ffffff',
239
+ backgroundColor: '#006644',
240
+ fontSize: 0.2,
241
+ });
242
+ this.toggleButton.onTriggered = () => this.toggleGeminiLive();
243
+
244
+ this.textPanel.position.set(0, 1.2, -2);
245
+ this.add(this.textPanel);
246
+ }
247
+
248
+ async handleAIMessage(message) {
249
+ if (message.data) {
250
+ xb.core.sound.playAIAudio(message.data);
251
+ }
252
+
253
+ const content = message.serverContent;
254
+ if (content) {
255
+ if (content.inputTranscription?.text) {
256
+ this.transcription.handleInputTranscription(
257
+ content.inputTranscription.text
258
+ );
259
+ }
260
+ if (content.outputTranscription?.text) {
261
+ this.transcription.handleOutputTranscription(
262
+ content.outputTranscription.text
263
+ );
264
+ }
265
+ if (content.turnComplete) {
266
+ this.transcription.finalizeTurn();
267
+ }
268
+ }
269
+
270
+ if (message.toolCall) {
271
+ this.updateStatus('AI is calling a tool...');
272
+ const functionResponses = [];
273
+
274
+ for (const fc of message.toolCall.functionCalls) {
275
+ const tool = this.liveAgent.findTool(fc.name);
276
+
277
+ if (tool) {
278
+ const promptText = fc.args?.prompt || 'custom scene';
279
+ this.updateStatus(`Generating skybox: ${promptText}...`);
280
+
281
+ // Small delay to ensure status is visible before long operation
282
+ await new Promise((resolve) => setTimeout(resolve, 100));
283
+
284
+ const result = await tool.execute(fc.args);
285
+ const response = xb.SkyboxAgent.createToolResponse(
286
+ fc.id,
287
+ fc.name,
288
+ result
289
+ );
290
+ functionResponses.push(response);
291
+ if (result.success) {
292
+ this.updateStatus('Skybox generated successfully!');
293
+ this.transcription.addText(`✓ ${result.data || 'Task completed'}`);
294
+ } else {
295
+ this.updateStatus(`Generation failed: ${result.error}`);
296
+ this.transcription.addText(`✗ Error: ${result.error}`);
297
+ }
298
+ } else {
299
+ this.updateStatus(`Tool not found: ${fc.name}`);
300
+ functionResponses.push({
301
+ id: fc.id,
302
+ name: fc.name,
303
+ response: {error: `Tool ${fc.name} not found`},
304
+ });
305
+ this.transcription.addText(`✗ Tool not found: ${fc.name}`);
306
+ }
307
+ }
308
+
309
+ this.liveAgent.sendToolResponse({functionResponses});
310
+ }
311
+ }
312
+
313
+ updateButtonState() {
314
+ const isActive = this.liveAgent?.getSessionState().isActive;
315
+ this.toggleButton?.setText(isActive ? '⏹ Stop' : '▶ Start');
316
+ }
317
+
318
+ updateStatus(message) {
319
+ if (this.statusText) {
320
+ this.statusText.text = message;
321
+ }
322
+ }
323
+
324
+ async cleanup() {
325
+ if (this.liveAgent?.getSessionState().isActive) {
326
+ try {
327
+ await this.liveAgent.stopLiveSession();
328
+ } catch (e) {
329
+ this.updateStatus(`Error stopping session: ${e.message}`);
330
+ }
331
+ }
332
+ xb.core.sound?.disableAudio();
333
+ xb.core.sound?.stopAIAudio();
334
+ }
335
+
336
+ async dispose() {
337
+ await this.cleanup();
338
+ super.dispose();
339
+ }
340
+ }
341
+
342
+ async function requestAudioPermission() {
343
+ try {
344
+ const stream = await navigator.mediaDevices.getUserMedia({
345
+ audio: {
346
+ sampleRate: 16000,
347
+ channelCount: 1,
348
+ echoCancellation: true,
349
+ noiseSuppression: true,
350
+ },
351
+ });
352
+ stream.getTracks().forEach((track) => track.stop());
353
+ return stream;
354
+ } catch (error) {
355
+ console.error(`Error requesting audio permission: ${error.message}`);
356
+ return null;
357
+ }
358
+ }
359
+
360
+ async function start() {
361
+ try {
362
+ await requestAudioPermission();
363
+
364
+ const options = new xb.Options();
365
+ options.simulator.instructions.enabled = false;
366
+ options.enableUI();
367
+ options.enableHands();
368
+ options.enableAI();
369
+ options.setAppTitle('Generating Skybox with Gemini');
370
+
371
+ xb.init(options);
372
+ xb.add(new GeminiSkyboxGenerator());
373
+ } catch (error) {
374
+ console.error(`Error initializing: ${error.message}`);
375
+ }
376
+ }
377
+
378
+ document.addEventListener('DOMContentLoaded', function () {
379
+ start();
380
+ });
381
+
382
+ </script>
383
+ </body>
384
+ </html>