janusweb 1.7.0 → 1.7.1

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 (32) hide show
  1. package/.github/workflows/deploy.yml +71 -0
  2. package/Dockerfile +14 -0
  3. package/README.md +210 -7
  4. package/janusxr.com/.args +2 -0
  5. package/janusxr.com/.init.lua +30 -0
  6. package/media/assets/webui/apps/comms/voip.js +27 -25
  7. package/media/assets/webui/apps/editor/editor.js +3 -0
  8. package/media/assets/webui/apps/navigation/navigation.js +1 -1
  9. package/media/assets/webui/apps/xrfragment/level0-sidecarfile.js +308 -0
  10. package/media/assets/webui/apps/xrfragment/level2-hyperlink.js +265 -0
  11. package/media/assets/webui/apps/xrfragment/level7-engine-prefixes.export.js +93 -0
  12. package/media/assets/webui/apps/xrfragment/level7-engine-prefixes.import.js +229 -0
  13. package/media/assets/webui/apps/xrfragment/patch/metaquest-fix-flickering.js +15 -0
  14. package/media/assets/webui/apps/xrfragment/xrfragment.json +14 -0
  15. package/media/assets/webui/default.json +1 -0
  16. package/media/images/portal.svg +130 -0
  17. package/media/index.html +48 -1
  18. package/package.json +1 -1
  19. package/scripts/client.js +24 -0
  20. package/scripts/config.js +12 -1
  21. package/scripts/janusbase.js +1 -0
  22. package/scripts/janusparagraph.js +169 -22
  23. package/scripts/janusxrplayer.js +4 -6
  24. package/scripts/room.js +59 -36
  25. package/scripts/translators/paragraph/html-xml-rss.js +47 -0
  26. package/scripts/translators/peertube.js +89 -0
  27. package/scripts/translators/xrfragments.js +5 -5
  28. package/tests/room/paragraph.xml +47 -0
  29. package/utils/build.sh +1 -0
  30. package/utils/dev-link.sh +75 -0
  31. package/utils/release.binary.sh +36 -0
  32. package/utils/release.docker.sh +11 -0
@@ -0,0 +1,71 @@
1
+ name: Build and Deploy to Pages
2
+
3
+ on:
4
+ push:
5
+ # Monitor all branches so we can catch '#deploy' in commit messages
6
+ branches: ["**"]
7
+ tags:
8
+ - "v*.*.*"
9
+
10
+ permissions: write-all
11
+
12
+ # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
13
+ concurrency:
14
+ group: "pages"
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ build-and-deploy:
19
+ # Run only on the master branch OR if the commit message contains '#deploy'
20
+ runs-on: ubuntu-latest
21
+
22
+ steps:
23
+ - name: Checkout code
24
+ uses: actions/checkout@v4
25
+
26
+ - name: Setup Node.js
27
+ uses: actions/setup-node@v4
28
+ with:
29
+ node-version: '20' # Change to your preferred version
30
+ cache: 'npm' # Only works if you have a package-lock.json
31
+
32
+ - name: Install dependencies
33
+ # Only runs if package.json exists; avoids errors if it doesn't
34
+ run: |
35
+ if [ -f package.json ]; then
36
+ npm install
37
+ fi
38
+
39
+ - name: Run build script
40
+ run: |
41
+ chmod +x ./utils/build.sh
42
+ ./utils/build.sh
43
+ ./utils/release.binary.sh
44
+
45
+ - name: Create Release and Upload Assets
46
+ uses: softprops/action-gh-release@v3
47
+ with:
48
+ files: |
49
+ build/janusweb-*.tar.gz
50
+ build/*/janusxr.com
51
+ #env: # only needed when creating a release in other repo
52
+ # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53
+
54
+ - name: Organize build artifacts
55
+ # Creates a clean 'www' folder and copies your build/sub/folders into it
56
+ run: |
57
+ mkdir -p www
58
+ cp -r build/*/* www/ 2>/dev/null || cp -r build/* www/
59
+
60
+ - name: Setup Pages
61
+ uses: actions/configure-pages@v4
62
+
63
+ - name: Upload artifact
64
+ uses: actions/upload-pages-artifact@v3
65
+ with:
66
+ path: 'www'
67
+
68
+ - name: Deploy to GitHub Pages
69
+ id: deployment
70
+ uses: actions/deploy-pages@v4
71
+
package/Dockerfile ADDED
@@ -0,0 +1,14 @@
1
+ FROM busybox:latest
2
+
3
+ ARG JANUSXR_VERSION="1.5.56"
4
+
5
+ WORKDIR /www
6
+ COPY build/$JANUSXR_VERSION/janusxr.com .
7
+
8
+ # assimilate the crossplatform binary into host architecture
9
+ # otherwise docker will complain with 'Exec format error'
10
+ RUN wget https://cosmo.zip/pub/cosmos/bin/assimilate && \
11
+ chmod +x assimilate && \
12
+ ./assimilate ./janusxr.com
13
+
14
+ CMD ["/www/janusxr.com"]
package/README.md CHANGED
@@ -70,10 +70,188 @@ You can even put your mark-up onto sites like PasteBin or PiratePad. Then just
70
70
  in our viewer by entering the URL into the navigation bar, and you can link directly to it, share
71
71
  on social media, or embed our viewer directly into other webpages, blog posts, or articles.
72
72
 
73
- See also **Using a specific version of JanusWeb** below.
73
+ ### Single-binary server+client
74
+
75
+ Use our all-in-one **janusxr.com** multi-platform binary file:
76
+ Run a **local-first** viewer locally on your desktop, raspberry pi (ARM64) or server.<br>
77
+
78
+ | executable | platforms |
79
+ |-|-|
80
+ | [janusxr.com](https://jbaicoianu.github.io/janusweb/janusxr.com) | <img src="https://i.imgur.com/v7cYVq1.png"/> |
81
+
82
+ Run `janusxr.com` from anywhere, or put it into a folder with your 3D models or JanusXR rooms:
83
+
84
+ ```
85
+ $ chmod +x janusxr.com # only for non-windows users
86
+
87
+ $ ./janusxr.com
88
+ I2026-01-19T15:41:10 (srvr) listen http://127.0.0.1:8080
89
+ I2026-01-19T15:41:10 (srvr) listen http://192.168.1.168:8080
90
+ Launching browser..
91
+ ```
92
+
93
+ A browser will popup, and connect to `https://janusxr.org` via Janusweb.<br>
94
+ Webbrowsers on the same network can access your node `https://192.168.1.168:8080`
95
+
96
+ > Optionally, you can expose this janusweb-node to the internet. Check [here](https://redbean.dev) for more commandline options.
97
+ </details>
98
+
99
+ <details>
100
+ <summary><b>Usecase:</b> 3D file viewer</summary>
101
+ <br>
102
+ Make sure `janusxr.com` is in a folder with your 3D models
103
+
104
+ ```
105
+ $ ls
106
+ janusxr.com
107
+ world1.glb
108
+ world2.glb
109
+ ```
110
+
111
+ Then make sure you rename `janusxr.com` to whatever you want to set as homepage:
112
+
113
+ ```
114
+ $ mv janusxr.com world1.com
115
+ $ ./world1.com
116
+ I2026-01-19T15:41:10 (srvr) listen http://127.0.0.1:8080
117
+ I2026-01-19T15:41:10 (srvr) listen http://192.168.1.168:8080
118
+ ✅ detected /world1.glb (setting as spatial home)
119
+ Launching browser..
120
+ ```
121
+ </details>
122
+
123
+ <details>
124
+ <summary><b>Usecase:</b> all-in-one JanusXR runtime</summary>
125
+ <br>
126
+ You can bundle all your XR experiences with the server/client binary.<br>
127
+ Just Make sure `janusxr.com` is in a folder with your JanusXR rooms or 3D models
128
+
129
+ ```
130
+ $ ls
131
+ janusxr.com
132
+ world1.html
133
+ world2.html
134
+ foo.glb
135
+ ```
136
+
137
+ Then make sure you rename `janusxr.com` to whatever you want to set as homepage:
138
+
139
+ ```
140
+ $ zip -r janusxr.com * # add janus files to janusxr.com
141
+ $ mv janusxr.com world1.com # hint homepage room (world1.html) sidecarfile
142
+ $ ./world1.com
143
+ ✅ detected /world1.html (setting as spatial home)
144
+ I2026-01-19T15:41:10 (srvr) listen http://127.0.0.1:8080
145
+ I2026-01-19T15:41:10 (srvr) listen http://192.168.1.168:8080
146
+ Launching browser..
147
+ ```
148
+
149
+ Profit! You can now send `world1.com` to a friend, and his browser will launch straight into world1.html
150
+
151
+ > NOTE: you can also point directly to a 3D model (`foo.com` would load `foo.glb`) or PDF-file etc!
152
+ </details>
153
+
154
+
155
+ <details>
156
+ <summary><b>Usecase:</b> run as a docker</summary>
157
+ <br>
158
+ You can run our docker-image as following:
159
+
160
+ ```
161
+ $ docker run -p 8080:8080 codeberg.org/coderofsalvation/janusxr:1.5.56
162
+ ```
163
+
164
+ Profit! You can now point your browser to `http://localhost:8080` or configure your reverseproxy for SSL certs/domain etc.<br>
165
+ Or optionally, serve your own XR experiences with it:
166
+
167
+ ```
168
+ $ ls
169
+ myworld1.xml myworld2.html myworld3.glb
170
+ $
171
+ $ docker run -p 8081:8080 -v $(pwd):/data codeberg.org/coderofsalvation/janusxr:1.5.56 /www/janusxr.com -D /data
172
+ ```
173
+
174
+ Profit! You can now use URLs like `http://localhost:8080/#janus.url=https://localhost:8080/myworld1.xml` e.g.<br>
175
+ Btw. you can build the docker image yourself too:
176
+
177
+ ```
178
+ $ utils/release.binary.sh # build the janusxr.com binary
179
+ $ docker build -t janusxr.com .
180
+ $ docker run -p 8080:8080 janusxr
181
+ ```
182
+
183
+ </summary>
184
+ </details>
185
+
186
+ <details>
187
+ <summary><b>Usecase:</b> serve all-in-one-binary in different docker/OCI container</summary>
188
+ <br>
189
+
190
+ Make sure to run you're in a folder with your JanusXR rooms or 3D models, and `janusxr.com`
191
+
192
+ ```
193
+ $ ls
194
+ janusxr.com
195
+
196
+ $ unzip janusxr.com # extract the viewer files
197
+
198
+ $ ls
199
+ janusxr.com
200
+ world1.html
201
+ world2.html
202
+ foo.glb
203
+
204
+ $ docker run -d -v ./:/www -p 8080:8080 --name janusweb busybox:latest /bin/httpd -f -h /www -p 8080
205
+ e7928798379e872983b7ec9b89237ebc
206
+
207
+ $ docker ps
208
+ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
209
+ 6711ed9739f3 docker.io/library/busybox:latest /bin/httpd -f -h ... 16 seconds ago Up 16 seconds 0.0.0.0:8080->8080/tcp janusweb
210
+ ```
211
+
212
+ Profit! You can now point your browser to `http://127.0.0.1:8080` or configure your reverseproxy for SSL certs/domain etc.
213
+
214
+ </details>
215
+
216
+ <details>
217
+ <summary><b>Usecase:</b> deploy janusweb as github/gitlab/codeforge webpage</summary>
218
+
219
+ <br>
220
+ *TODO* provide action-files
221
+ </details>
222
+
223
+ <details>
224
+ <summary><b>Usecase:</b> get the latest build files</summary>
225
+
226
+ <br>
227
+ Sometimes, developers just want the latest janusweb **files**.<br>
228
+ Good news, the executable is also a zip-file.<br>
229
+
230
+ ```
231
+ $ ls
232
+ janusxr.com
233
+ $ unzip janusxr.com
234
+ $ ls
235
+ index.html
236
+ janusweb.js
237
+ (...)
238
+
239
+ $ janusxr.com
240
+ I2026-01-19T15:41:10 (srvr) listen http://127.0.0.1:8080
241
+ I2026-01-19T15:41:10 (srvr) listen http://192.168.1.168:8080
242
+ Launching browser..
243
+
244
+ ( the webserver will prioritize the unzipped files )
245
+
246
+ $ zip janusxr.com index.html # you can even update the binary
247
+ ```
248
+
249
+ > Profit!
250
+
251
+ </details>
74
252
 
75
253
  ### Pull our scripts into your page
76
- Using the above method, all of your links would go through our servers. If you'd prefer to
254
+ Using the above method, all of your links would go through community CORS-proxies. If you'd prefer to
77
255
  link to your own servers, you can pull our JS into your page and use JanusWeb as a scriptable
78
256
  client via its API. This looks something like this:
79
257
 
@@ -97,13 +275,38 @@ client via its API. This looks something like this:
97
275
  ```
98
276
 
99
277
  The `elation.janusweb.init()` function can take a number of arguments, and returns a promise which
100
- receives an instance of the client. This client reference can be controlled via its API. See the
101
- sections on **Arguments** and **Scripting** below.
278
+ receives an instance of the client. This client reference can be controlled via its API:
279
+
280
+ ```html
281
+ <html>
282
+ <head>
283
+ <title>My JanusVR Room</title>
284
+ </head>
285
+ <body>
286
+ <script src="https://web.janusvr.com/janusweb.js"></script>
287
+ <script>
288
+ elation.janusweb.init({url: document.location.href })
289
+ .then( (client)=> console.log("rendering this page's virtual twin") )
290
+ </script>
291
+
292
+ <!--
293
+ <fireboxroom>
294
+ <room use_local_asset="room1">
295
+ <object id="cube" pos="0 1 5" />
296
+ <text col="1 0 0" pos="0 2 4">my first room</text>
297
+ </room>
298
+ </fireboxroom>
299
+ -->
300
+
301
+ </body>
302
+ </html>
303
+ ```
304
+
305
+ > **NOTE**: you can also update the `janusxr.com` binary to your likings: (`unzip janusxr.com index.html && vi index.html && zip janusxr.com index.html` see 'get the latest build files' above)
102
306
 
103
- See also **Using a specific version of JanusWeb** below.
307
+ See the sections on **Arguments** and **Scripting** below.
104
308
 
105
- ### Install from ZIPs
106
- (TODO - we will start shipping zip builds of JanusWeb once we release v1.0)
309
+ > See also **Using a specific version of JanusWeb** below.
107
310
 
108
311
  ### Install from NPM
109
312
  (TODO - we will start shipping official NPM packages of JanusWeb once we release v1.0)
@@ -0,0 +1,2 @@
1
+ -D
2
+ .
@@ -0,0 +1,30 @@
1
+ HidePath("/usr/share/zoneinfo/")
2
+ HidePath("/usr/share/ssl/")
3
+ homepage = false
4
+ homepath = false
5
+ home = arg[-1]:gsub(".*/","")
6
+ :gsub(".com","")
7
+ ext = { "txt", "xml", "html", "pdf", "mp3", "yaml", "json", "xlsx", "png", "jpg", "glb", "gltf", "dae", "obj"}
8
+ for k,v in pairs(ext) do
9
+ file = string.format("%s.%s", home, v)
10
+ if( path.exists( file ) ) then homepath = "/" .. file; end
11
+ end
12
+
13
+ if( homepath ) then
14
+ print("✅ detected " .. homepath .. " (setting as spatial home)")
15
+ homepage = Slurp("/zip/index.html"):gsub("<janus%-viewer .-</janus%-viewer>","<janus-viewer src=\"" .. homepath .. "\"></janus-viewer>")
16
+
17
+ function OnHttpRequest()
18
+ path = GetPath()
19
+ if( homepage and path == "/" ) then
20
+ SetStatus(200)
21
+ SetHeader("Content-Type", "text/html; charset=utf-8")
22
+ SetHeader("Access-Control-Allow-Origin", "*")
23
+ Write(homepage)
24
+ else
25
+ Route()
26
+ end
27
+ end
28
+ end
29
+ LaunchBrowser()
30
+
@@ -681,35 +681,37 @@ console.log('[voip-remoteuser] got a remote voip user', data);
681
681
  this.audio = audio;
682
682
  this.appendChild(audio);
683
683
 
684
- audio.play()
685
- .then(() => {
686
- console.log('[voip-remoteuser] remote user audio playing', audio, this);
687
- this.hasaudio = true;
688
- });
684
+ room.getAudioNodes().then(() => {
685
+ audio.play()
686
+ .then(() => {
687
+ console.log('[voip-remoteuser] remote user audio playing', audio, this);
688
+ this.hasaudio = true;
689
+ });
689
690
 
690
- let listener = player.engine.systems.sound.getRealListener();
691
- let context = listener.context;
692
- let analyser = context.createAnalyser();
693
- let source = context.createMediaStreamSource(audio.srcObject);
694
- source.connect(analyser);
691
+ let listener = player.engine.systems.sound.getRealListener();
692
+ let context = listener.context;
693
+ let analyser = context.createAnalyser();
694
+ let source = context.createMediaStreamSource(audio.srcObject);
695
+ source.connect(analyser);
695
696
 
696
- analyser.fftSize = 32;
697
- this.analyser = analyser;
698
- this.audiobuffer = new Uint8Array(analyser.frequencyBinCount);
697
+ analyser.fftSize = 32;
698
+ this.analyser = analyser;
699
+ this.audiobuffer = new Uint8Array(analyser.frequencyBinCount);
699
700
 
700
- if (this.updateVolumeInterval) {
701
- clearInterval(this.updateVolumeInterval);
702
- this.updateVolumeInterval = false;
703
- }
704
- this.updateVolumeInterval = setInterval(() => {
705
- analyser.getByteFrequencyData(this.audiobuffer);
706
- let sum = 0;
707
- for (let i = 0; i < this.audiobuffer.length; i++) {
708
- sum += this.audiobuffer[i];
701
+ if (this.updateVolumeInterval) {
702
+ clearInterval(this.updateVolumeInterval);
703
+ this.updateVolumeInterval = false;
709
704
  }
710
- let avg = sum / this.audiobuffer.length;
711
- this.updateVolume(avg / 100);
712
- }, 20);
705
+ this.updateVolumeInterval = setInterval(() => {
706
+ analyser.getByteFrequencyData(this.audiobuffer);
707
+ let sum = 0;
708
+ for (let i = 0; i < this.audiobuffer.length; i++) {
709
+ sum += this.audiobuffer[i];
710
+ }
711
+ let avg = sum / this.audiobuffer.length;
712
+ this.updateVolume(avg / 100);
713
+ }, 20);
714
+ });
713
715
  }
714
716
 
715
717
  let remoteuser = janus.network.remoteplayers[this.id];
@@ -325,7 +325,10 @@ console.log('set translation snap', ev.data, ev);
325
325
  room._target.objects['3d'].traverse(n => {
326
326
  userDatas.set(n, n.userData);
327
327
  n.userData = {};
328
+ elation.events.fire({element: this, type: 'webui_editor_export_node', data:{n,userData: userDatas.get(n)} })
328
329
  });
330
+ elation.events.fire({element: this, type: 'webui_editor_export', data:{ scene: room._target.objects['3d']} })
331
+
329
332
  exporter.parse(room._target.objects['3d'], (data) => {
330
333
  // Restore userData objects
331
334
  room._target.objects['3d'].traverse(n => {
@@ -391,7 +391,7 @@ elation.elements.define('janus.ui.urlbar', class extends elation.elements.ui.pan
391
391
  handleAccept(ev) {
392
392
  var url = this.input.value;
393
393
  if (url.length > 0) {
394
- if (url.indexOf(':') == -1) {
394
+ if (url.indexOf(':') == -1 && !url.match(/^\.\//) ) {
395
395
  url = 'http://' + url;
396
396
  }
397
397
  if (ev.altKey) {