diff --git a/.gitmodules b/.gitmodules
index 2dbe80c..5ca14c4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "godot"]
- path = godot
+ path = clients/godot
url = ../xrforge-godot.git
diff --git a/godot b/clients/godot
similarity index 100%
rename from godot
rename to clients/godot
diff --git a/clients/webxr/viewer/index.html b/clients/webxr/viewer/index.html
new file mode 100644
index 0000000..57f499a
--- /dev/null
+++ b/clients/webxr/viewer/index.html
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/clients/webxr/viewer/play.svg b/clients/webxr/viewer/play.svg
new file mode 100644
index 0000000..72c813a
--- /dev/null
+++ b/clients/webxr/viewer/play.svg
@@ -0,0 +1,43 @@
+
+
+
diff --git a/clients/webxr/viewer/plugin.xrforge.js b/clients/webxr/viewer/plugin.xrforge.js
new file mode 100644
index 0000000..77c1dac
--- /dev/null
+++ b/clients/webxr/viewer/plugin.xrforge.js
@@ -0,0 +1,44 @@
+
+function init(widget){
+ inferSource(widget.src)
+}
+
+async function inferSource(src){
+ response = await fetch(src, {
+ method: 'HEAD'
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ // 1. Get the Content-Disposition header value
+ contentDisposition = response.headers.get('Content-Disposition');
+
+ // 2. Process the value to extract the filename
+ console.log(widget.src)
+ widget.src = extractFilename(contentDisposition)
+ console.log("detected: "+widget.src)
+}
+
+function extractFilename(contentDispositionHeader) {
+ // Check for the preferred 'filename*' (UTF-8 encoded)
+ // This looks for 'filename*=' followed by the encoding and the filename
+ const utf8Match = contentDispositionHeader.match(/filename\*=(?:utf-8|UTF-8)''(.+?)(?:;|$)/i);
+ if (utf8Match && utf8Match[1]) {
+ // The filename might be URL-encoded, so decode it
+ // The value in your example is 'website.jpg' which is not encoded,
+ // but a proper implementation should decode.
+ return decodeURIComponent(utf8Match[1].trim());
+ }
+
+ // Fallback to the simpler 'filename'
+ const basicMatch = contentDispositionHeader.match(/filename="(.+?)"/i);
+ if (basicMatch && basicMatch[1]) {
+ return basicMatch[1].trim();
+ }
+
+ return null;
+}
+
+export { init };
diff --git a/clients/webxr/viewer/shell.nix b/clients/webxr/viewer/shell.nix
new file mode 100644
index 0000000..bdb1a75
--- /dev/null
+++ b/clients/webxr/viewer/shell.nix
@@ -0,0 +1,26 @@
+#let
+# pkgs = import (builtins.fetchGit {
+# name = "nixos-23.05";
+# url = "https://github.com/nixos/nixpkgs/";
+# ref = "refs/heads/nixos-unstable";
+# rev = "ef99fa5c5ed624460217c31ac4271cfb5cb2502c";
+# }) {};
+{ pkgs ? import {} }:
+
+ pkgs.mkShell {
+ # nativeBuildInputs is usually what you want -- tools you need to run
+ nativeBuildInputs = with pkgs.buildPackages; [
+
+ nodejs_20
+
+ ];
+
+ shellHook = ''
+
+ # install bun
+ alias bun="steam-run ~/.bun/bin/bun"
+ test -f ~/.bun/bin/bun || curl -fsSL https://bun.com/install | bash -s "bun-v1.2.22"
+
+ export NIX_SHELL_DEV=1
+ '';
+}