mml bugfix + lovr-packager

This commit is contained in:
Leon van Kammen 2026-02-26 22:40:23 +01:00
parent f446266a4b
commit f82fcb24f9
15 changed files with 263 additions and 33 deletions

View file

@ -1,5 +1,8 @@
# XRForge # XRForge
XRForge is a pre-configured [Manyfold](https://manyfold.app) container (reproducably via [nix](https://nixos.org) dockertools).
It also contains some extra's, to better fit an XR audience & enable community libraries.
> Self-sovereign **XR Experiences** for teams & organisations, powered by FOSS ♥ > Self-sovereign **XR Experiences** for teams & organisations, powered by FOSS ♥
<img src="manyfold/usr/src/app/public/assets/xrh-full.svg" height="35" style="height:35px;display:inline-block"/> <img src="manyfold/usr/src/app/public/assets/xrh-full.svg" height="35" style="height:35px;display:inline-block"/>

View file

@ -9,13 +9,13 @@ test -n "$GODOT_VERSION" || export GODOT_VERSION=4.4.1-stable
test -n "$IMPORT_INSTANCES" || export IMPORT_INSTANCES=1 test -n "$IMPORT_INSTANCES" || export IMPORT_INSTANCES=1
test -n "$SERVER_CORS" || export SERVER_CORS=http://localhost:5577 test -n "$SERVER_CORS" || export SERVER_CORS=http://localhost:5577
test -n "$SERVER_JANUS" || export SERVER_JANUS=http://localhost:5566 test -n "$SERVER_JANUS" || export SERVER_JANUS=http://localhost:5566
test -n "$HTTPS_ONLY" || unsafe=1 && export HTTPS_ONLY=disabled test -n "$HTTPS_ONLY" || unsafe=1
test -n "$SECRET_KEY_BASE" || unsafe=1 && export SECRET_KEY_BASE=j1gf2cj3gfcjhf2j34298kjk2j3h4k test -n "$SECRET_KEY_BASE" || unsafe=1 && export SECRET_KEY_BASE=j1gf2cj3gfcjhf2j34298kjk2j3h4k
test -n "$SUDO_RUN_UNSAFELY" || unsafe=1 && export SUDO_RUN_UNSAFELY=enabled test -n "$SUDO_RUN_UNSAFELY" || unsafe=1 && export SUDO_RUN_UNSAFELY=enabled
test -n "$DATABASE_ADAPTER" || export DATABASE_ADAPTER=sqlite3 test -n "$DATABASE_ADAPTER" || export DATABASE_ADAPTER=sqlite3
test -n "$MULTIUSER" || export MULTIUSER=enabled test -n "$MULTIUSER" || export MULTIUSER=enabled
test -n "$FEDERATE_SERVERS" || export FEDERATE_SERVERS='"*"' test -n "$FEDERATE_SERVERS" || export FEDERATE_SERVERS='"*"'
test -n "$PUBLIC_HOSTNAME" || export PUBLIC_HOSTNAME=localhost test -n "$PUBLIC_HOSTNAME" #|| export PUBLIC_HOSTNAME=localhost
test -n "$CADDY" && { test -n "$CADDY" && {
export HTTPS_ONLY=enabled export HTTPS_ONLY=enabled
@ -179,9 +179,11 @@ set_upload_path(){
} }
mount_dir(){ mount_dir(){
mkdir /usr/src/app/public/lib
find /mnt -type d -mindepth 1 -maxdepth 1 | grep -vE '(janusweb|view)' | while read dir; do find /mnt -type d -mindepth 1 -maxdepth 1 | grep -vE '(janusweb|view)' | while read dir; do
echocolor "[$APPNAME]" "mounting $dir as library" echocolor "[$APPNAME]" "mounting $dir as library"
add_lib_to_db "$dir" add_lib_to_db "$dir"
ln -s "$dir" /usr/src/app/public/lib/.
done done
} }
@ -243,7 +245,8 @@ set_homepage(){
rename_app(){ rename_app(){
echocolor "[$APPNAME]" "renaming manyfold to $APPNAME" echocolor "[$APPNAME]" "renaming manyfold to $APPNAME"
sed -i 's/title: Manyfold/title: '$APPNAME'/g' /usr/src/app/config/locales/*.yml sed -i 's/title: Manyfold/title: '$APPNAME'/g' /usr/src/app/config/locales/*.yml
sed -i 's|powered_by_html:.*|powered_by_html: Radically opensource-powered by <a href="https://manifold.app" target="_blank">Manyfold</a>, <a href="https://xrfragment.org">XR Fragments</a>, <a href="https://github.com/jbaicoianu/janusweb" target="_blank">JanusWeb</a> and <a href="https://nixos.org" target="_blank">NIX</a>|g' /usr/src/app/config/locales/*.yml sed -i 's|powered_by_html:.*|powered_by_html: \|\n <small>Opensource-powered by <a href="https://manifold.app" target="_blank">Manyfold</a>, <a href="https://xrfragment.org">XR Fragments</a>, <a href="https://xrhf.isvery.ninja">XR Hypermedia Federation</a>, <a href="https://github.com/jbaicoianu/janusweb" target="_blank">JanusWeb</a>, <a href="https://nixos.org" target="_blank">NIX</a> and <a href="https://nlnet.nl" target="_blank">NLnet</a></small>|g' /usr/src/app/config/locales/*.yml
sed -i 's| by_html:.*| by_html: \|\n XRForge is a <u tabindex="0">radically <span>openource platform. A sysadmin can selfhost this platform right now!<br><br><img src="assets/xrforge_term.svg" style="border-radius:7px; border-radius 7px; width 100%; max-width 450px; margin-bottom 40px;"/><br><br>This </span></u> opensource platform allows organisations to run on intranets or federate with others.|g' /usr/src/app/config/locales/*.yml
echocolor "[$APPNAME]" "renaming 'model' to 'experience'" echocolor "[$APPNAME]" "renaming 'model' to 'experience'"
for dir in /usr/src/app/config/locales/*.yml /usr/src/app/config/locales/*/*.yml; do for dir in /usr/src/app/config/locales/*.yml /usr/src/app/config/locales/*/*.yml; do
sed -i 's|Models|Experiences|g' "$dir" sed -i 's|Models|Experiences|g' "$dir"
@ -253,6 +256,9 @@ rename_app(){
done done
sed -i 's|File(s) uploaded succesfully|File(s) uploaded succesfully. The experience is now being (re)generated, please be patient and check back later|g' /usr/src/app/config/locales/models/en.yml sed -i 's|File(s) uploaded succesfully|File(s) uploaded succesfully. The experience is now being (re)generated, please be patient and check back later|g' /usr/src/app/config/locales/models/en.yml
sed -i 's|File(s) uploaded succesfully|File(s) uploaded succesfully. The experience is now being (re)generated, please be patient and check back later|g' /usr/src/app/config/locales/model_files/en.yml sed -i 's|File(s) uploaded succesfully|File(s) uploaded succesfully. The experience is now being (re)generated, please be patient and check back later|g' /usr/src/app/config/locales/model_files/en.yml
# link instead of duplicate models/collections immersive overviews
ln -s /usr/src/app/app/views/models/_janusroom.html.erb /usr/src/app/app/views/collections/.
} }
federate_servers(){ federate_servers(){
@ -387,6 +393,6 @@ usage(){
exit 0 exit 0
} }
test "$unsafe" = 1 && echocolor "[WARNING]" "default env-vars SECRET_KEY_BASE or SUDO_RUN_UNSAFELY are used. Please check: https://codeberg.org/coderofsalvation/xrforge/src/branch/master/manyfold" && echo test "$unsafe" = 1 && echocolor "[WARNING]" "default env-vars SECRET_KEY_BASE or SUDO_RUN_UNSAFELY are used. Please check: https://codeberg.org/coderofsalvation/xrforge/src/branch/master/manyfold" 1>&2 && echo
test -n "$1" && "$@" test -n "$1" && "$@"
test -n "$1" || usage test -n "$1" || usage

View file

@ -49,13 +49,14 @@ begin
# Iterate through the images array # Iterate through the images array
gltf['images'].each_with_index do |img, i| gltf['images'].each_with_index do |img, i|
# move file to modeldirectory (but dont overwrite if user overwrite it) # use matching texture in original modeldirectory (if generated via extract-textures shellscript)
imgExts = ["jpg","png"] imgExts = ["jpg","png"]
imgExts.each do |imgExt| imgExts.each do |imgExt|
new_filename = "#{dir}/#{filenameWithoutExt}_img#{i}.#{imgExt}" new_filename = "#{dir}/#{filenameWithoutExt}_img#{i}.#{imgExt}"
if File.exist?(new_filename) if File.exist?(new_filename)
XRForge.log("🤔 detected #{File.basename(new_filename)}",logfile) XRForge.log("🤔 detected #{File.basename(new_filename)}",logfile)
XRForge.log("✅ importing #{File.basename(new_filename)} -> #{resource['path']}",logfile) XRForge.log("✅ importing #{File.basename(new_filename)} -> #{resource['path']}",logfile)
# here we update the glTF JSON with the matching texture
img['uri'] = new_filename # NOTE: editing uri will cause assimp to drop image['name'] when exporting :/ img['uri'] = new_filename # NOTE: editing uri will cause assimp to drop image['name'] when exporting :/
update = true update = true
end end
@ -64,6 +65,7 @@ begin
if update if update
File.write( gltf_path, JSON.pretty_generate(gltf) ) File.write( gltf_path, JSON.pretty_generate(gltf) )
XRForge.log("✅ writing #{resource['path']}",logfile) XRForge.log("✅ writing #{resource['path']}",logfile)
# convert scratch-model (with updated textures) to actual model
system("assimp export #{gltf_path} #{resource['path']} --embed-textures") system("assimp export #{gltf_path} #{resource['path']} --embed-textures")
end end
end end

View file

@ -6,5 +6,7 @@ echo "[package_lovr_zip.sh] zipping lovr.zip"
# overwrite empty lovr template project-zip with given URL # overwrite empty lovr template project-zip with given URL
test -n "$LOVR_TEMPLATE_ZIP" && timeout 50 wget "$LOVR_TEMPLATE_ZIP" -O ~/templates/template_lovr.zip test -n "$LOVR_TEMPLATE_ZIP" && timeout 50 wget "$LOVR_TEMPLATE_ZIP" -O ~/templates/template_lovr.zip
ln -s $(echo *.glb *.obj *.dae | awk '{print $1}') scene.glb
cp ~/templates/template_lovr.zip .xrforge/lovr.zip cp ~/templates/template_lovr.zip .xrforge/lovr.zip
zip .xrforge/lovr.zip $(find . -maxdepth 1 -type f) zip .xrforge/lovr.zip $(find -L . -maxdepth 1 -type f)
test -f scene.glb && rm scene.glb

View file

@ -43,7 +43,7 @@ begin
# https://viewer.mml.io/main/v1/?url=https%3A%2F%2Ffoo.org%2Fbar.mml # https://viewer.mml.io/main/v1/?url=https%3A%2F%2Ffoo.org%2Fbar.mml
mml = <<~MML mml = <<~MML
<m-model src="#{federate_drive_host}/#{model_file.gsub("#","%23")}" anim-loop="true" anim-enabled="true"></m-model> <m-model src="../#{model_file.gsub("#","%23")}" anim-loop="true" anim-enabled="true"></m-model>
MML MML
File.write('.xrforge/scene.mml', mml) File.write('.xrforge/scene.mml', mml)

View file

@ -108,12 +108,6 @@ class Components::ModelCard < Components::Base
div class: "col" do div class: "col" do
#open_button #open_button
#whitespace #whitespace
if ! @model.tags.where(name: "singleuser" ).any?
a alt: "start a meeting at this location", href: "/view/index.html?profile=default#janus.url="+model_url(@model), target: "_blank", class: "btn btn-secondary btn-sm" do
i class: "bi bi-telephone"
img src: "/assets/janusxr.svg", style: "width: 16px; margin-left: 7px; transform: translate(0px,-2px);"
end
end
whitespace whitespace
status_badges @model status_badges @model
end end

View file

@ -16,7 +16,7 @@ class Components::PreviewFrame < Components::Base
def view_template def view_template
if @file if @file
a href: "/view/index.html?profile=preview#janus.url="+model_url(@file.model), target: "_blank" do a href: "/view/index.html?profile=default#janus.url="+model_url(@file.model), target: "_blank" do
local local
end end
elsif @object.remote? elsif @object.remote?

View file

@ -0,0 +1,42 @@
<footer class="container-fluid mt-5 py-2 border-top" id="footer">
<div class="row">
<div class="col-lg-3 me-auto">
<span class="d-inline-flex align-items-center mb-2">
<%= image_tag "roundel.svg", width: 48, height: 48, class: "me-2", alt: translate("application.title") %>
<span class="fs-5"><%= t(".powered_by_html", name: t("application.title")) %></span>
</span>
<ul class="list-unstyled small text-muted">
<li class="mb-2"><%= t ".by_html" %></li>
<li class="mb-2"><a href="https://codeberg.org/coderofsalvation/xrforge" target="_blank">Sourcecode</a></li>
<!--
<%= t ".open_source_html" %><
-->
<% if current_user&.is_administrator? %>
<li class="mb-2"><%= t ".version" %>:
<%= link_to "#{Rails.application.config.app_version} (#{Rails.application.config.git_sha.first(8)})",
"#{Rails.application.config.upstream_repo}/tree/#{Rails.application.config.git_sha}",
target: "_blank", rel: "noreferrer" %>
</li>
<% end %>
</ul>
</div>
<div class="col-4 col-lg-2 mb-3">
<h5><%= site_name(default: t(".instance_heading")) %></h5>
<ul class="list-unstyled">
<li class="mb-2"><%= link_to t(".about"), about_path %></li>
<% if SiteSettings.support_link.presence %>
<li class="mb-2"><a href="<%= SiteSettings.support_link %>" target="_blank" rel="noreferrer"><%= t ".support" %></a></li>
<% end %>
<li class="mb-2"><%= link_to t(".api"), api_url, rel: "noopener", target: "_blank" %></li>
</ul>
</div>
<div class="col-4 col-lg-2 mb-3">
<h5><%= t "application.title" %></h5>
<ul class="list-unstyled">
<li class="mb-2"><a href="https://codeberg.org/coderofsalvation/xrforge/issues" target="_blank" rel="noreferrer"><%= t ".issues" %></a></li>
<li class="mb-2"><a href="https://chat.isvery.ninja" target="_blank" rel="noreferrer"><%= t ".community" %></a></li>
<li class="mb-2"><a href="https://leondustar.gumroad.com/l/hGYGh" target="_blank" rel="noreferrer"><%= t ".sponsor" %></a></li>
</ul>
</div>
</div>
</footer>

View file

@ -40,5 +40,21 @@
</div> </div>
</main> </main>
<%= render "application/footer" %> <%= render "application/footer" %>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="notification" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto"></strong>
<small></small>
</div>
<div class="toast-body">
</div>
</div>
</div>
<!-- remember telescopic text e.g. -->
<%= "<script src='assets/xrforge.js'></script>".html_safe %>
</body> </body>
</html> </html>

View file

@ -3,7 +3,7 @@
<div class="row row-cols-md-2 mt-2"> <div class="row row-cols-md-2 mt-2">
<div class="col-md-9" id="item_list"> <div class="col-md-9" id="item_list">
<iframe allowfullscreen allow="xr-spatial-tracking; fullscreen" src="/view/index.html?overlay=true&profile=preview" style="width:100%; height:360px; border:0; border-radius:10px;"></iframe> <iframe allowfullscreen allow="xr-spatial-tracking; fullscreen" src="/view/index.html?overlay=true&profile=default" style="width:100%; height:50vh; border:0; border-radius:10px;"></iframe>
<%= yield :items %> <%= yield :items %>
</div> </div>

View file

@ -19,6 +19,7 @@
</assets> </assets>
<room pos="0 0 0" skybox="true" showavatar="false" use_local_asset="room_plane"> <room pos="0 0 0" skybox="true" showavatar="false" use_local_asset="room_plane">
<light js_id="dan9-janus:light/light_cone_angle=0-6" pos="0 3 2" collision_trigger="false" /> <light js_id="dan9-janus:light/light_cone_angle=0-6" pos="0 3 2" collision_trigger="false" />
<text js_id="location" pos="0 2.6 2" billboard="y" col="#BBFFFF" scale="5 5 1" text="<%= request.original_url %>" rotation="-180 0 -180" emissive="0 0 0" roughness="0.3" metalness="0" />
<CircularLayout pos="0 0 0" radius="4.5" scale="1 1 1" > <CircularLayout pos="0 0 0" radius="4.5" scale="1 1 1" >
<% @models.each_with_index do |model, i| %> <% @models.each_with_index do |model, i| %>
<object id="o_<%= i %>"> <object id="o_<%= i %>">

View file

@ -60,12 +60,12 @@ end
<div class="shimmer"></div> <div class="shimmer"></div>
<img src="/assets/roundel-1d688b1e.svg" /> <img src="/assets/roundel-1d688b1e.svg" />
<br><br> <br><br>
🚀 (Re)generating the experience..<br><br> 🚀 (Re)generating the experience..<br>
☕ Please be patient or check back later.<br> ☕ Please be patient or check back later.<br>
</article> </article>
</div> </div>
<% else %> <% else %>
<iframe allowfullscreen allow="xr-spatial-tracking; fullscreen" src="<%=janusURLPreview%>?networking=false" style="width:100%; height:<%=viewer_width%>px; border:0; border-radius:10px;"></iframe> <iframe allowfullscreen allow="xr-spatial-tracking; fullscreen" src="<%=janusURLPreview%>?networking=false" style="width:100%; height: 50vh; border:0; border-radius:10px;"></iframe>
<% end %> <% end %>
<div style="height:15px"></div> <div style="height:15px"></div>
<% else %> <% else %>
@ -134,7 +134,7 @@ end
<%== jml %> <%== jml %>
--> -->
<% if File.exist?( filePath+".xrforge/scene.gltf" ) %> <% if File.exist?( filePath+".xrforge/scene.gltf" ) && ENV['FEDERATE_DRIVE_HOST'] %>
<tr> <tr>
<td>⁂</td> <td>⁂</td>
<td> <td>
@ -177,7 +177,7 @@ end
</tr> </tr>
<% end %> <% end %>
<% if File.exist?( filePath+".xrforge/scene.mml" ) %> <% if File.exist?( filePath+".xrforge/scene.mml" ) && ENV['FEDERATE_DRIVE_HOST'] %>
<tr> <tr>
<td>⁂</td> <td>⁂</td>
<td> <td>
@ -210,6 +210,8 @@ end
<td><%= link_to @model.creator.name, @model.creator, itemprop: "author" %></td> <td><%= link_to @model.creator.name, @model.creator, itemprop: "author" %></td>
</tr> </tr>
<% end %> <% end %>
<% if ENV['FEDERATE_DRIVE_HOST'] %>
<tr> <tr>
<td> <td>
<i class="bi bi-journal-check" role="img"></i> <i class="bi bi-journal-check" role="img"></i>
@ -232,6 +234,7 @@ end
</div> </div>
</td> </td>
</tr> </tr>
<% end %>
<% if @model.collection %> <% if @model.collection %>
<tr> <tr>
@ -308,6 +311,7 @@ end
</div> </div>
<%= card :secondary, "Packages" do %> <%= card :secondary, "Packages" do %>
<% if ENV['FEDERATE_DRIVE_HOST'] %>
<table class="table table-borderless table-sm"> <table class="table table-borderless table-sm">
<tr> <tr>
<td> <td>
@ -331,6 +335,32 @@ end
</td> </td>
</tr> </tr>
</table> </table>
<% end %>
<% if ENV['FEDERATE_DRIVE_HOST'] %>
<table class="table table-borderless table-sm">
<tr>
<td>
<i class="bi bi-controller" role="img"></i>
</td>
<td>
<%= link_to "loVR project", ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/lovr.zip" %>
<label for="toggle_loVR"><i class="bi bi-info-circle"></i></label>
<div class="toggle-box">
<input type="checkbox" id="toggle_loVR" hidden>
<div class="hidden-tooltip">
<i class="bi bi-arrow-90deg-up"></i>&nbsp;
<small>
This is a LoVR app (startingpoint) which wraps your (3D file) experience.<br>
<a href="https://loVR.org" target="_blank">Godot</a> is a Free and Opensource Game engine.<br>
You can modify and run the app on these platforms:
<b>WARNING</b>: use <a href="https://en.wikipedia.org/wiki/Progressive_enhancement" target="_blank">progressive enhancement</a> so your 3D file experience will always run in other <a href="https://xrfragment.org" target="_blank">XR Fragment</a> viewers.
</small>
</div>
</div>
</td>
</tr>
</table>
<% end %>
<% end %> <% end %>
<%= card :secondary, t("layouts.card_list_page.actions_heading") do %> <%= card :secondary, t("layouts.card_list_page.actions_heading") do %>

View file

@ -12,6 +12,7 @@
<link rel="stylesheet" href="/assets/themes/slate-9cc3cc7d.css" nonce="1de89072246b25ca36376d8f1cf5e051" /> <link rel="stylesheet" href="/assets/themes/slate-9cc3cc7d.css" nonce="1de89072246b25ca36376d8f1cf5e051" />
<link rel="stylesheet" href="/assets/xrforge.css" /> <link rel="stylesheet" href="/assets/xrforge.css" />
<script src="assets/xrforge.js"></script>
</head> </head>
<body> <body>
@ -62,13 +63,13 @@
<h2>Turn files into AR/VR 🥽 experiences</h2> <h2>Turn files into AR/VR 🥽 experiences</h2>
<br> <br>
<center> <center>
<a href="https://codeberg.org/coderofsalvation/xrforge" class="btn btn-secondary"> <a href="https://codeberg.org/coderofsalvation/xrforge" class="btn btn-secondary" style="text-align:left">
<img src="assets/codeberg.svg" style="width:20px"/> <img src="assets/codeberg.svg" style="width:20px"/>
Check the sourcecode<br> Check the sourcecode<br>
<small>irc.isvery.ninja port 443 via ObsidianIRC</small> <small>docker run codeforge.org/coderofsalvation/xrforge:latest</small>
</a> </a>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<a href="https://isvery.ninja/chat/index.html" class="btn btn-secondary" style="text-align:left"> <a href="https://chat.isvery.ninja" class="btn btn-secondary" style="text-align:left">
<img src="assets/obsidian.png" style="width:20px"/> <img src="assets/obsidian.png" style="width:20px"/>
chat with community <br> chat with community <br>
<small>irc.isvery.ninja port 443 via ObsidianIRC</small> <small>irc.isvery.ninja port 443 via ObsidianIRC</small>
@ -105,7 +106,7 @@
<h3>Why people want XRForge</h3> <h3>Why people want XRForge</h3>
<div> <div>
The metaverse-hype has shown: people <b>like 3D</b> but <b>existing</b> 2D ecosystems are king.<br> The metaverse has shown: people <b>like 3D</b> but certain <b>existing</b> 2D ecosystems are king.<br>
They're just more cost-efficient to use.<br> They're just more cost-efficient to use.<br>
Hence, XRForge promotes <b>projecting these ecosystems</b> as virtual <b>hyperlinked</b> worlds.<br> Hence, XRForge promotes <b>projecting these ecosystems</b> as virtual <b>hyperlinked</b> worlds.<br>
XRForge can seed itself via local or remote XRForge can seed itself via local or remote
@ -213,14 +214,132 @@
<div></div> <div></div>
</div> </div>
<center>
<h3>Comparison</h3>
<table class="table table-dark table-striped" id="comparison">
<thead>
<tr>
<td></td>
<td><b>XRForge</b></td>
<td><b>Other solutions</b><br>(Meta Horizon e.g.)</td>
</tr>
</thead>
<tbody>
<tr>
<td>No data-collection or login requirement</td>
<td></td>
<td></td>
</tr>
<tr>
<td>users can host content themselves</td>
<td><small>HTTP/DAT/IPFS Open Protocols</small></td>
<td><small>we own your data</small></td>
</tr>
<tr>
<td>User operated content and viewers</td>
<td></td>
<td><small>we control both</small></td>
</tr>
<tr>
<td>portals to remote content</td>
<td><small>JanusXR or <a href="https://xrfragment.org" target="_blank">XR Fragments</a> protocol</small></td>
<td></td>
</tr>
<tr>
<td>Platform does not control network</td>
<td><small>user-operated viewers and content</small></td>
<td><small>stakeholders can block anything</small></td>
</tr>
<tr>
<td>Platform to Platform portals</td>
<td><small>JanusXR or <a href="https://xrfragment.org" target="_blank">XR Fragments</a> protocol</small></td>
<td></td>
</tr>
<tr>
<td>2D web to 3D translators</td>
<td><small>JanusWeb translators</small></td>
<td></td>
</tr>
<tr>
<td>Embed 3D scene in regular file (pdf e.g.)</td>
<td><small>supported via JanusWeb</small></td>
<td></td>
</tr>
<tr>
<td>Portals from/to Internet/Intranet</td>
<td></td>
<td></td>
</tr>
<tr>
<td>User can make content private</td>
<td><small>XRForge permissions or HTTP password</small></td>
<td></td>
</tr>
<tr>
<td>Spatial anchors / URL addressibility</td>
<td><small>JanusWeb and <a href="https://xrfragment.org" target="_blank">XR Fragments</a> protocol</small></td>
<td></td>
</tr>
<tr>
<td>Direct portals to 3D file</td>
<td><small>Janusweb and <a href="https://xrfragment.org" target="_blank">XR Fragments</a> protocol</small></td>
<td></td>
</tr>
<tr>
<td>Detect 3D scene in webpage URL</td>
<td><small>Janusweb and <a href="https://xrfragment.org" target="_blank">XR Fragments</a> protocol</small></td>
<td></td>
</tr>
<tr>
<td>Export data + Credible exit</td>
<td><small>server-export runs on opensource <a href="https://github.com/jbaicoianu/janusweb" target="_blank">janusweb</a> viewer</small></td>
<td></td>
</tr>
<tr>
<td>Lightweight (runs on Raspberry PI e.g.)</td>
<td></td>
<td></td>
</tr>
<tr>
<td>XR worlds at rest</td>
<td></td>
<td><small>compute required for nonvisited spaces</small></td>
</tr>
<tr>
<td>Integrates with Fediverse</td>
<td><small>via ActivityPub and RSS/HTML translators</small></td>
<td></td>
</tr>
<tr>
<td>Integrate with (own) dataclouds</td>
<td style="width:33%"><small>scroll down below</small>
<div style="max-height: 200px; overflow-y: scroll; background: var(--bs-body-bg); padding: 10px; border-radius: 9px;">
<small>
Via rclone: Google Drive, Amazon S3, Microsoft OneDrive, Dropbox, Backblaze B2, SFTP, Google Cloud Storage (GCS), Microsoft Azure Blob Storage, MinIO, FTP, WebDAV, Wasabi, Cloudflare R2, Local Filesystem, MEGA, pCloud, Nextcloud, DigitalOcean Spaces, Hetzner Storage Box, Box, Google Photos, iCloud Drive, SMB/CIFS, Oracle Object Storage, Proton Drive, IDrive e2, Alibaba Cloud (Aliyun) OSS, Yandex Disk, Hetzner Object Storage, OVHcloud, ownCloud, Scaleway, Koofr, Put.io, Seafile, Mail.ru, 1Fichier, Akamai NetStorage, Internet Archive, Jottacloud, Storj, Tencent COS, Huawei OBS, Qiniu Kodo, OpenStack Swift, Arvan Cloud AOS, Bizfly Cloud, Ceph, China Mobile Ecloud, Citrix ShareFile, Cloudinary, Cubbit, Digi Storage, Dreamhost, Drime, Enterprise File Fabric, Exaba, Fastmail Files, FileLu Cloud, Filen, Files.com, Gofile, HDFS, HiDrive, HTTP, ImageKit, Internxt, IONOS Cloud, Leviia, Liara, Linkbox, Magalu, Memory, OpenDrive, Oracle Cloud Storage, Outscale, Petabox, PikPak, Pixeldrain, Premiumize.me, QingStor, Quatrix, Rabata, RackCorp, Rackspace Cloud Files, Rsync.net, Scaleway, Seafile, Seagate Lyve Cloud, SeaweedFS, Selectel, Servercore, Spectra Logic, Storj, SugarSync, Uloz.to, Zoho WorkDrive</small>
</small>
</td>
<td>
</td>
</body>
</table>
</center>
<div class="spectrum">
<div></div>
<div></div>
</div>
<center> <center>
<h3>Supporter of Open XR Hypermedia stacks</h3> <h3>Supporter of Open XR Hypermedia stacks</h3>
<div style="max-width:945px;"> <div style="max-width:945px;">
<a href="https://coderofsalvation.github.io/janus-guide/" target="_blank"> <a href="https://coderofsalvation.github.io/janus-guide/" target="_blank">
<img src="/assets/janusxr-xrf.png"/> <img src="/assets/janusxr-xrf.png" style="width:100%"/>
</a> </a>
<br> <br>
</div> </div>
<br>
</center> </center>
<div class="spectrum"> <div class="spectrum">
@ -398,14 +517,5 @@
</main> </main>
<script>
// telescopic text:
// a JS cheat whicht allows persisting unfolds
// uncomment this if you really want this
([...document.querySelectorAll('u')]).map( (u) => {
u.addEventListener('click', e => e.target.className = 'show' )
});
</script>
</body> </body>
</html> </html>

View file

@ -493,3 +493,12 @@ input[type="radio"][id="browser"]:checked ~ .openlearning {
transform: scale(0.8); transform: scale(0.8);
} }
} }
.form-control::-moz-placeholder {
color: #888;
opacity: 1;
}
.form-control::placeholder {
color: #888;
opacity: 1;
}

View file

@ -0,0 +1,15 @@
window.notify = function(opts){
let { html,title, subtitle, domid, timeout } = opts
let el = document.querySelector( domid || '#notification')
if( title ) el.querySelector(".toast-header").innerHTML = `<strong class="me-auto">${title}</strong><small>${subtitle||''}</small>`
if( html ) el.querySelector(".toast-body").innerHTML = html
el.style.display = 'block'
setTimeout( () => el.style.display = 'none', timeout || 5000 )
};
// telescopic text:
// a JS cheat whicht allows persisting unfolds
// uncomment this if you really want this
([...document.querySelectorAll('u')]).map( (u) => {
u.addEventListener('click', e => e.target.className = 'show' )
});