diff --git a/app/helloworld-html.js b/app/helloworld-html.js
index f9cfb0b..87217ea 100644
--- a/app/helloworld-html.js
+++ b/app/helloworld-html.js
@@ -5,6 +5,7 @@ AFRAME.registerComponent('helloworld-html', {
init: function () {
+ this.el.addEventListener('ready', () => this.el.dom.style.display = 'none' )
},
requires:{
@@ -19,17 +20,12 @@ AFRAME.registerComponent('helloworld-html', {
`,
css: `.helloworld-html {
- color: var(--xrsh-dark-gray); /* see index.css */
- }
- `,
+ color: var(--xrsh-light-gray); /* see index.css */
+ }`,
},
events:{
- // component events
- html: function( ){ console.log("html-mesh requirement mounted") },
- ready: function(e){ console.log("requires are loaded") },
-
// combined AFRAME+DOM reactive events
keydown: function(e){ }, //
click: function(e){
@@ -40,12 +36,18 @@ AFRAME.registerComponent('helloworld-html', {
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
- DOMready: function( ){
+ ready: function( ){
+ this.el.dom.style.display = 'none'
console.log("this.el.dom has been added to DOM")
this.el.dom.children[0].id = this.el.uid // important hint for html-mesh
this.data.myvalue = 1
setInterval( () => this.data.myvalue++, 100 )
- }
+ },
+
+ launcher: function(){
+ this.el.dom.style.display = ''
+ console.log("launcher")
+ },
},
@@ -54,7 +56,7 @@ AFRAME.registerComponent('helloworld-html', {
"name": "Hello world",
"icons": [
{
- "src": "/images/icons-vector.svg",
+ "src": "https://css.gg/browser.svg",
"type": "image/svg+xml",
"sizes": "512x512"
}
diff --git a/app/helloworld-htmlform.js b/app/helloworld-htmlform.js
index de86fdd..762ad6a 100644
--- a/app/helloworld-htmlform.js
+++ b/app/helloworld-htmlform.js
@@ -3,15 +3,12 @@ AFRAME.registerComponent('helloworld-htmlform', {
foo: { type:"string"}
},
- init: function () {
-
- },
+ init: function () {},
requires:{
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", // deadsimple windows: https://nextapps-de.github.io/winbox
- stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
},
dom: {
@@ -33,19 +30,15 @@ AFRAME.registerComponent('helloworld-htmlform', {
`,
- css: `.helloworld-htmlform {
- div {
- padding:11px;
- }
- }
- `,
+
+ css: `.helloworld-htmlform > div { padding:11px; }`
+
},
events:{
// component events
html: function( ){ console.log("html-mesh requirement mounted") },
- stylis: function( ){ console.log("stylis requirement mounted") },
// combined AFRAME+DOM reactive events
click: function(e){ }, //
@@ -57,27 +50,26 @@ AFRAME.registerComponent('helloworld-htmlform', {
// reactive events for this.data updates
myvalue: function(e){ this.el.dom.querySelector('#myvalue').innerText = this.data.myvalue },
- // requires are loaded
- ready: function(e){
- setTimeout( () => {
- new WinBox("Hello World",{
- width: 250,
- height: 315,
- minwidth:250,
- maxwidth:250,
- maxheight:315,
- minheight:315,
- x: 100,
- y: 100,
- id: this.el.uid, // important hint for html-mesh
- root: document.querySelector("#overlay"),
- mount: this.el.dom
- });
- },500) /*FIXME*/
-
+ launcher: function(){
+ this.el.dom.style.display = ''
+ new WinBox("Hello World",{
+ width: 250,
+ height: 315,
+ minwidth:250,
+ maxwidth:250,
+ maxheight:315,
+ minheight:315,
+ x: 100,
+ y: 100,
+ id: this.el.uid, // important hint for html-mesh
+ root: document.querySelector("#overlay"),
+ mount: this.el.dom,
+ onclose: () => { this.el.dom.style.display = 'none'; return false; }
+ });
},
- DOMready: function( ){
+ ready: function( ){
+ this.el.dom.style.display = 'none'
console.log("this.el.dom has been added to DOM")
this.data.myvalue = 1
}
@@ -89,7 +81,7 @@ AFRAME.registerComponent('helloworld-htmlform', {
"name": "Hello world",
"icons": [
{
- "src": "/images/icons-vector.svg",
+ "src": "https://css.gg/browser.svg",
"type": "image/svg+xml",
"sizes": "512x512"
}
diff --git a/app/helloworld-window.js b/app/helloworld-window.js
index 2ffda16..190653f 100644
--- a/app/helloworld-window.js
+++ b/app/helloworld-window.js
@@ -3,15 +3,12 @@ AFRAME.registerComponent('helloworld-window', {
foo: { type:"string"}
},
- init: function () {
-
- },
+ init: function(){},
requires:{
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
- winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", // deadsimple windows: https://nextapps-de.github.io/winbox
- stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
+ winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", //
},
dom: {
@@ -20,12 +17,8 @@ AFRAME.registerComponent('helloworld-window', {
html: (me) => `
${me.data.foo} ${me.data.myvalue}
`,
- css: `.helloworld-window {
- div {
- .pad { padding:11px; }
- }
- }
- `,
+
+ css: `.helloworld-window div.pad { padding:11px; }`
},
events:{
@@ -41,28 +34,27 @@ AFRAME.registerComponent('helloworld-window', {
// reactive events for this.data updates
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
- // requires are loaded
- ready: function(e){
-
- setTimeout( () => {
- new WinBox("Hello World",{
- width: 250,
- height: 150,
- x:"center",
- y:"center",
- id: this.el.uid, // important hint for html-mesh
- root: document.querySelector("#overlay"),
- mount: this.el.dom
- });
- }, 500 )
-
- },
-
- DOMready: function( ){
+ ready: function( ){
+ this.el.dom.style.display = 'none'
console.log("this.el.dom has been added to DOM")
this.data.myvalue = 1
setInterval( () => this.data.myvalue++, 100 )
- }
+ },
+
+ launcher: function(){
+ console.log("this.el.dom has been added to DOM")
+ new WinBox("Hello World",{
+ width: 250,
+ height: 150,
+ x:"center",
+ y:"center",
+ id: this.el.uid, // important hint for html-mesh
+ root: document.querySelector("#overlay"),
+ mount: this.el.dom,
+ onclose: () => { this.el.dom.style.display = 'none'; return false; }
+ });
+ this.el.dom.style.display = ''
+ },
},
@@ -71,7 +63,7 @@ AFRAME.registerComponent('helloworld-window', {
"name": "Hello world",
"icons": [
{
- "src": "/images/icons-vector.svg",
+ "src": "https://css.gg/browser.svg",
"type": "image/svg+xml",
"sizes": "512x512"
}
diff --git a/app/helloworld.js b/app/helloworld.js
index ec96a47..bbd7863 100644
--- a/app/helloworld.js
+++ b/app/helloworld.js
@@ -3,8 +3,12 @@ AFRAME.registerComponent('helloworld', {
foo: { type:"string"}
},
- init: function () {
+ init: function () {
+ this.el.object3D.visible = false
this.el.setAttribute("geometry","primitive: octahedron")
+ this.interval = setInterval( () => {
+ this.data.myvalue = ((this.data.myvalue||1.0) + 0.25) % 1
+ }, 400 )
},
requires:{
@@ -17,6 +21,13 @@ AFRAME.registerComponent('helloworld', {
somecomponent: function( ){ console.log("component requirement mounted") },
ready: function(e){ console.log("requires are loaded") },
+ launcher: function(e){
+ this.el.object3D.visible = !this.el.object3D.visible
+ },
+
+ // reactive this.data value demo
+ myvalue:function( ){ this.el.object3D.children[0].scale.y = this.data.myvalue }
+
},
manifest: { // HTML5 manifest to identify app to xrsh
@@ -24,7 +35,7 @@ AFRAME.registerComponent('helloworld', {
"name": "Hello world",
"icons": [
{
- "src": "/images/icons-vector.svg",
+ "src": "https://css.gg/shape-hexagon.svg",
"type": "image/svg+xml",
"sizes": "512x512"
}
diff --git a/app/isoterminal.js b/app/isoterminal.js
new file mode 100644
index 0000000..f2132aa
--- /dev/null
+++ b/app/isoterminal.js
@@ -0,0 +1,110 @@
+AFRAME.registerComponent('isoterminal', {
+ schema: {
+ foo: { type:"string"}
+ },
+
+ init: function(){},
+
+ requires:{
+ html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
+ winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
+ winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", //
+ },
+
+ dom: {
+ scale: 3,
+ events: ['click','keydown'],
+ html: (me) => `
+
to be implemented
+
`,
+
+ css: `.helloworld-window div.pad { padding:11px; }`
+ },
+
+ events:{
+
+ // combined AFRAME+DOM reactive events
+ click: function(e){ }, //
+ keydown: function(e){ }, //
+
+ // reactive events for this.data updates
+ myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
+
+ ready: function( ){
+ this.el.dom.style.display = 'none'
+ },
+
+ launcher: function(){
+ new WinBox( this.manifest.iso + ' ' + this.manifest.name, {
+ width: '70%',
+ height: '80%',
+ x:"center",
+ y:"center",
+ id: this.el.uid, // important hint for html-mesh
+ root: document.querySelector("#overlay"),
+ mount: this.el.dom,
+ onclose: () => {
+ if( !confirm('do you want to kill this virtual machine and all its processes?') ) return true
+ this.el.dom.style.display = 'none'
+ return false
+ }
+ });
+ this.el.dom.style.display = ''
+ },
+
+ },
+
+ manifest: { // HTML5 manifest to identify app to xrsh
+ "iso": "linux-x64-4.15.iso",
+ "short_name": "ISOTerm",
+ "name": "terminal",
+ "icons": [
+ {
+ "src": "https://css.gg/terminal.svg",
+ "type": "image/svg+xml",
+ "sizes": "512x512"
+ }
+ ],
+ "id": "/?source=pwa",
+ "start_url": "/?source=pwa",
+ "background_color": "#3367D6",
+ "display": "standalone",
+ "scope": "/",
+ "theme_color": "#3367D6",
+ "shortcuts": [
+ {
+ "name": "What is the latest news?",
+ "cli":{
+ "usage": "helloworld
[options]",
+ "example": "helloworld news",
+ "args":{
+ "--latest": {type:"string"}
+ }
+ },
+ "short_name": "Today",
+ "description": "View weather information for today",
+ "url": "/today?source=pwa",
+ "icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
+ }
+ ],
+ "description": "Hello world information",
+ "screenshots": [
+ {
+ "src": "/images/screenshot1.png",
+ "type": "image/png",
+ "sizes": "540x720",
+ "form_factor": "narrow"
+ }
+ ],
+ "help":`
+Helloworld application
+
+This is a help file which describes the application.
+It will be rendered thru troika text, and will contain
+headers based on non-punctualized lines separated by linebreaks,
+in above's case "\nHelloworld application\n" will qualify as header.
+ `
+ }
+
+});
+
diff --git a/app/launcher.js b/app/launcher.js
new file mode 100644
index 0000000..dcae6d2
--- /dev/null
+++ b/app/launcher.js
@@ -0,0 +1,185 @@
+/*
+ * ## launcher
+ *
+ * displays app (icons) for enduser to launch
+ *
+ * ```javascript
+ *
+ * ```
+ *
+ * | property | type | example |
+ * |--------------|--------------------|----------------------------------------------------------------------------------------|
+ * | `registries` | `array` of strings | |
+ *
+ * | event | target | info |
+ * |--------------|-------------------------------------------------------------------------------------------------------------|
+ * | `launcher` | an app | when pressing an app icon, `launcher` event will be send to the respective app |
+ */
+
+AFRAME.registerComponent('launcher', {
+ schema: {
+ foo: { type:"string"}
+ },
+
+ init: function () {
+ this.data.apps = []
+
+ AFRAME.scenes.map( (scene) => {
+ scene.addEventListener('app:ready', (e) => this.render(e.detail) )
+ })
+ },
+
+ requires:{
+ html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
+ },
+
+ dom: {
+ scale: 3,
+ events: ['click'],
+ html: (me) => `
+
+
`,
+
+ css: `#iconmenu {
+ z-index: 1000;
+ display: flex;
+ flex-direction: column-reverse;
+ align-items: flex-end;
+ height: 100%;
+ position: fixed;
+ top: 0px;
+ right: 20px;
+ bottom: 0;
+ padding-bottom: 72px;
+ box-sizing: border-box;
+ pointer-events: none;
+ }
+ #iconmenu > button {
+ pointer-events:all;
+ width: 58px;
+ height: 39px;
+ padding: 12px 0px 20px 0px;
+ border-radius: 0px;
+ color: var(--xrsh-primary);
+ background: #FFF;
+ border-left: 5px solid var(--xrsh-primary);
+ border-right: 5px solid var(--xrsh-primary);
+ }
+
+ #iconmenu > button:first-child {
+ border-radius:0px 0px 30px 30px;
+ border-bottom: 5px solid var(--xrsh-primary);
+ padding-bottom:35px;
+ }
+
+ #iconmenu > button:last-child {
+ border-radius:30px 30px 0px 0px;
+ border-top: 5px solid var(--xrsh-primary);
+ padding-top:13px;
+ }
+
+ #iconmenu > button > img {
+ transform: translate(0px,-5px);
+ opacity:0.5;
+ padding: 5px;
+ border-radius: 0px;
+ }
+ #iconmenu > button > img:hover,
+ #iconmenu > button.enable > img {
+ border:2px solid #444;
+ transition:0.gg3s;
+ border-radius: 50%;
+ }`
+ },
+
+ events:{
+
+ // combined AFRAME+DOM reactive events
+ click: function(e){
+ console.dir(e)
+ }, //
+
+
+ ready: function( ){
+ this.el.dom.children[0].id = this.el.uid // important hint for html-mesh
+ },
+
+ },
+
+ render: function(app){
+ clearTimeout(this.timeout)
+ this.timeout = setTimeout( () => {
+ AFRAME.app.foreach( (app) => {
+ console.dir(app)
+ if( !app.manifest ) return
+
+ console.log("--rendering button")
+ return
+ let btn = app.btn = document.createElement('button')
+ if( app.manifest.icons?.length > 0){
+ let img = document.createElement('img')
+ img.src = app.manifest.icons[0].src
+ img.alt = app.manifest.name
+ btn.appendChild(img)
+ }else btn.innerText = app.manifest.short_name
+ btn.addEventListener('click', () => {
+ app.el.emit('launcher',app)
+ })
+ this.el.dom.querySelector('#iconmenu').appendChild(btn)
+ })
+ },200)
+ },
+
+ manifest: { // HTML5 manifest to identify app to xrsh
+ "short_name": "Hello world",
+ "name": "Hello world",
+ "icons": [
+ {
+ "src": "https://css.gg/browser.svg",
+ "type": "image/svg+xml",
+ "sizes": "512x512"
+ }
+ ],
+ "id": "/?source=pwa",
+ "start_url": "/?source=pwa",
+ "background_color": "#3367D6",
+ "display": "standalone",
+ "scope": "/",
+ "theme_color": "#3367D6",
+ "shortcuts": [
+ {
+ "name": "What is the latest news?",
+ "cli":{
+ "usage": "helloworld [options]",
+ "example": "helloworld news",
+ "args":{
+ "--latest": {type:"string"}
+ }
+ },
+ "short_name": "Today",
+ "description": "View weather information for today",
+ "url": "/today?source=pwa",
+ "icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
+ }
+ ],
+ "description": "Hello world information",
+ "screenshots": [
+ {
+ "src": "/images/screenshot1.png",
+ "type": "image/png",
+ "sizes": "540x720",
+ "form_factor": "narrow"
+ }
+ ],
+ "help":`
+Helloworld application
+
+This is a help file which describes the application.
+It will be rendered thru troika text, and will contain
+headers based on non-punctualized lines separated by linebreaks,
+in above's case "\nHelloworld application\n" will qualify as header.
+ `
+ }
+
+});
+
diff --git a/app/manual.js b/app/manual.js
index 46f7ef9..38f6ed5 100644
--- a/app/manual.js
+++ b/app/manual.js
@@ -4,14 +4,14 @@ AFRAME.registerComponent('manual', {
},
init: function () {
- //AFRAME.XRF.navigator.to("https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
+ // requires are loaded
+ this.el.addEventListener('ready', () => this.el.dom.style.display = 'none' )
},
requires:{
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", // deadsimple windows: https://nextapps-de.github.io/winbox
- xrfragments: "https://xrfragment.org/dist/xrfragment.aframe.js",
},
dom: {
@@ -35,19 +35,13 @@ AFRAME.registerComponent('manual', {
`,
- css: `.manual {
- div {
- padding:11px;
- }
- }
- `,
+ css: `.manual > div { padding:11px }`
},
events:{
// component events
html: function( ){ console.log("html-mesh requirement mounted") },
- stylis: function( ){ console.log("stylis requirement mounted") },
// combined AFRAME+DOM reactive events
click: function(e){ }, //
@@ -56,26 +50,24 @@ AFRAME.registerComponent('manual', {
// reactive events for this.data updates
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
- // requires are loaded
- ready: function(e){
-
- setTimeout( () => {
- new WinBox("XRSH manual",{
- width:300,
- height:300,
- x:"center",
- y:"center",
- id: this.el.uid, // important hint for html-mesh
- root: document.querySelector("#overlay"),
- mount: this.el.dom
- });
- }, 500 )
-
- this.el.setAttribute("xrf","https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
+ launcher: function(){
+ new WinBox( this.manifest.name, {
+ width: '70%',
+ height: '70%',
+ x:"center",
+ y:"center",
+ id: this.el.uid, // important hint for html-mesh
+ root: document.querySelector("#overlay"),
+ mount: this.el.dom,
+ onclose: () => { this.el.dom.style.display = 'none'; return false; }
+ });
+ this.el.dom.style.display = ''
+ this.el.setAttribute("xrf", document.location.search || "https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
// navigate with: AFRAME.XRF.navigator.to("https://xrfragment.org/index.glb")
},
DOMready: function( ){
+ this.el.dom.style.display = 'none'
console.log("this.el.dom has been added to DOM")
this.data.myvalue = 1
setInterval( () => this.data.myvalue++, 100 )
@@ -84,11 +76,11 @@ AFRAME.registerComponent('manual', {
},
manifest: { // HTML5 manifest to identify app to xrsh
- "short_name": "Hello world",
- "name": "Hello world",
+ "short_name": "XRSH Manual",
+ "name": "XRSH Manual",
"icons": [
{
- "src": "/images/icons-vector.svg",
+ "src": "https://css.gg/coffee.svg",
"type": "image/svg+xml",
"sizes": "512x512"
}
diff --git a/app/spatialize.js b/app/spatialize.js
new file mode 100644
index 0000000..a574eae
--- /dev/null
+++ b/app/spatialize.js
@@ -0,0 +1,89 @@
+AFRAME.registerComponent('spatialize', {
+ schema: {
+ foo: { type:"string"}
+ },
+
+ init: function () {
+
+ document.querySelector('a-scene').addEventListener('enter-vr',() => this.toggle(true) )
+ document.querySelector('a-scene').addEventListener('exit-vr', () => this.toggle(false) )
+ // toggle immersive with ESCAPE
+ document.body.addEventListener('keydown', (e) => e.key == 'Escape' && this.toggle() )
+ },
+
+ requires:{
+ // somecomponent: "https://unpkg.com/some-aframe-component/mycom.min.js"
+ },
+
+ events:{
+
+ // component events
+ ready: function(e){
+ this.btn.style.background = 'var(--xrsh-primary)'
+ },
+
+ launcher: function(e){ this.toggle() },
+
+ },
+
+ // draw a button so we can toggle apps between 2D / XR
+ toggle: function(state){
+ state = state || !document.body.className.match(/XR/)
+ document.body.classList[ state ? 'add' : 'remove'](['XR'])
+ AFRAME.scenes[0].emit( state ? 'apps:XR' : 'apps:2D')
+ this.btn.classList.toggle('enable')
+ },
+
+ manifest: { // HTML5 manifest to identify app to xrsh
+ "short_name": "spatialize",
+ "name": "spatialize",
+ "icons": [
+ {
+ "src": "https://css.gg/stack.svg",
+ "type": "image/svg+xml",
+ "sizes": "512x512"
+ }
+ ],
+ "id": "/?source=pwa",
+ "start_url": "/?source=pwa",
+ "background_color": "#3367D6",
+ "display": "standalone",
+ "scope": "/",
+ "theme_color": "#3367D6",
+ "shortcuts": [
+ {
+ "name": "What is the latest news?",
+ "cli":{
+ "usage": "helloworld
[options]",
+ "example": "helloworld news",
+ "args":{
+ "--latest": {type:"string"}
+ }
+ },
+ "short_name": "Today",
+ "description": "View weather information for today",
+ "url": "/today?source=pwa",
+ "icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
+ }
+ ],
+ "description": "Hello world information",
+ "screenshots": [
+ {
+ "src": "/images/screenshot1.png",
+ "type": "image/png",
+ "sizes": "540x720",
+ "form_factor": "narrow"
+ }
+ ],
+ "help":`
+Helloworld application
+
+This is a help file which describes the application.
+It will be rendered thru troika text, and will contain
+headers based on non-punctualized lines separated by linebreaks,
+in above's case "\nHelloworld application\n" will qualify as header.
+ `
+ }
+
+});
+
diff --git a/app/vconsole.js b/app/vconsole.js
new file mode 100644
index 0000000..99b96c0
--- /dev/null
+++ b/app/vconsole.js
@@ -0,0 +1,94 @@
+AFRAME.registerComponent('vconsole', {
+ schema: {
+ foo: { type:"string"}
+ },
+
+ init: function () {
+ //AFRAME.XRF.navigator.to("https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
+ document.head.innerHTML += `
+
+ `
+ },
+
+ requires:{
+ vconsole: "https://unpkg.com/vconsole@latest/dist/vconsole.min.js"
+ },
+
+ events:{
+
+ // requires are loaded
+ ready: function(e){
+ this.vConsole = new window.VConsole()
+ document.querySelector('.vc-mask').remove()
+ document.querySelector('.vc-switch').remove()
+ },
+
+ launcher: function(){
+ let panel = document.querySelector('.vc-panel')
+ if( panel.style.display == 'none' ) this.vConsole.show()
+ else this.vConsole.hide()
+ },
+ },
+
+ manifest: { // HTML5 manifest to identify app to xrsh
+ "short_name": "Hello world",
+ "name": "Hello world",
+ "icons": [
+ {
+ "src": "https://css.gg/border-bottom.svg",
+ "type": "image/svg+xml",
+ "sizes": "512x512"
+ }
+ ],
+ "id": "/?source=pwa",
+ "start_url": "/?source=pwa",
+ "background_color": "#3367D6",
+ "display": "standalone",
+ "scope": "/",
+ "theme_color": "#3367D6",
+ "shortcuts": [
+ {
+ "name": "What is the latest news?",
+ "cli":{
+ "usage": "helloworld [options]",
+ "example": "helloworld news",
+ "args":{
+ "--latest": {type:"string"}
+ }
+ },
+ "short_name": "Today",
+ "description": "View weather information for today",
+ "url": "/today?source=pwa",
+ "icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
+ }
+ ],
+ "description": "Hello world information",
+ "screenshots": [
+ {
+ "src": "/images/screenshot1.png",
+ "type": "image/png",
+ "sizes": "540x720",
+ "form_factor": "narrow"
+ }
+ ],
+ "help":`
+Helloworld application
+
+This is a help file which describes the application.
+It will be rendered thru troika text, and will contain
+headers based on non-punctualized lines separated by linebreaks,
+in above's case "\nHelloworld application\n" will qualify as header.
+ `
+ }
+
+});
+
diff --git a/app/xrfragments.js b/app/xrfragments.js
new file mode 100644
index 0000000..be56025
--- /dev/null
+++ b/app/xrfragments.js
@@ -0,0 +1,73 @@
+AFRAME.registerComponent('xrfragments', {
+ schema: {
+ url: { type:"string"}
+ },
+
+ init: function () {
+ },
+
+ requires:{
+ xrfragments: "https://xrfragment.org/dist/xrfragment.aframe.js",
+ },
+
+ events:{
+
+ // requires are loaded
+ ready: function(e){
+ this.el.setAttribute("xrf","https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
+ },
+
+ launcher: function(){
+ let url = prompt('enter URL to glb/fbx/json/obj/usdz asset', 'https://xrfragment.org/index.glb')
+ if( url ) AFRAME.XRF.navigator.to(url)
+ }
+
+ },
+
+ manifest: { // HTML5 manifest to identify app to xrsh
+ "short_name": "XRF",
+ "name": "XR Fragment URL",
+ "icons": [ ],
+ "id": "/?source=pwa",
+ "start_url": "/?source=pwa",
+ "background_color": "#3367D6",
+ "display": "standalone",
+ "scope": "/",
+ "theme_color": "#3367D6",
+ "shortcuts": [
+ {
+ "name": "What is the latest news?",
+ "cli":{
+ "usage": "helloworld [options]",
+ "example": "helloworld news",
+ "args":{
+ "--latest": {type:"string"}
+ }
+ },
+ "short_name": "Today",
+ "description": "View weather information for today",
+ "url": "/today?source=pwa",
+ "icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
+ }
+ ],
+ "description": "Hello world information",
+ "screenshots": [
+ {
+ "src": "/images/screenshot1.png",
+ "type": "image/png",
+ "sizes": "540x720",
+ "form_factor": "narrow"
+ }
+ ],
+ "help":`
+Helloworld application
+
+This is a help file which describes the application.
+It will be rendered thru troika text, and will contain
+headers based on non-punctualized lines separated by linebreaks,
+in above's case "\nHelloworld application\n" will qualify as header.
+ `
+ }
+
+});
+
diff --git a/com/app.js b/com/app.js
index f6c1a70..d15952d 100644
--- a/com/app.js
+++ b/com/app.js
@@ -28,7 +28,18 @@ AFRAME.registerComponent('app', {
AFRAME.app[component].map( (app) => {
if( !app.el.getAttribute(component) ) app.el.setAttribute(component,app.data)
})
- }
+ },
+ "requires:ready": function(){
+ let {id,component,type} = this.parseAppURI(this.data.uri)
+ AFRAME.app[component].map( (app) => {
+ if( app.readyFired ) return
+ setTimeout( () => {
+ if( this.el.dom ) this.el.dom.style.display = '' // finally show dom elements
+ app.el.emit('ready')
+ app.readyFired = true
+ },400) // big js scripts need some parsing time
+ })
+ },
},
init: function() {
@@ -83,7 +94,9 @@ AFRAME.registerComponent('app', {
}
}catch(e){ console.error(`package ${package} could not be retrieved..aborting :(`); throw e; }
})
- Promise.all(deps).then( () => this.el.emit( readyEvent||'ready', packages) )
+ Promise.all(deps).then( () => {
+ this.el.emit( readyEvent || 'requireReady', packages)
+ })
}
})
@@ -109,39 +122,50 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
return function(){
updateProperties.apply(this,arguments)
+ if( !this.data || !this.data.uri ) return // only deal with apps
- const reactify = (el,aframe) => new Proxy(this.data,{
- get(me,k,v) { return me[k]
- },
- set(me,k,v){
- me[k] = v
- aframe.emit(k,{el,k,v})
- }
- })
+ // ensure overlay
+ let overlay = document.querySelector('#overlay')
+ if( !overlay ){
+ overlay = document.createElement('div')
+ overlay.id = "overlay"
+ document.body.appendChild(overlay)
+ document.querySelector("a-scene").setAttribute("webxr","overlayElement:#overlay")
+ let menu = document.createElement('div')
+ menu.id = 'iconmenu'
+ document.body.appendChild(menu)
+ }
- if( !this.data ) return
+ // add menu button
+ if( this.manifest && this.manifest.icons ){
+ let btn = document.createElement('button')
+ btn.app = this
+ if( this.manifest.icons.length ){
+ btn.innerHTML = ``
+ }else btn.innerText = this.manifest.short_name
+ btn.setAttribute("alt", this.manifest.name )
+ btn.addEventListener('click', (e) => this.el.emit('launcher',{}) )
+ document.querySelector("#iconmenu").appendChild(btn)
+ }
// reactify components with dom-definition
if( this.data.uri && this.dom && !this.el.dom ){
tasks = {
- ensureOverlay: () => {
- let overlay = document.querySelector('#overlay')
- if( !overlay ){
- overlay = document.createElement('div')
- overlay.id = "overlay"
- document.body.appendChild(overlay)
- document.querySelector("a-scene").setAttribute("webxr","overlayElement:#overlay")
- }
- tasks.overlay = overlay
- return tasks
- },
-
createReactiveDOMElement: () => {
+ const reactify = (el,aframe) => new Proxy(this.data,{
+ get(me,k,v) { return me[k]
+ },
+ set(me,k,v){
+ me[k] = v
+ aframe.emit(k,{el,k,v})
+ }
+ })
this.el.dom = document.createElement('div')
this.el.dom.className = this.parseAppURI(this.data.uri).component
this.el.dom.innerHTML = this.dom.html(this)
+ this.el.dom.style.display = 'none'
this.data = reactify( this.dom.el, this.el )
this.dom.events.map( (e) => this.el.dom.addEventListener(e, (ev) => this.el.emit(e,ev) ) )
return tasks
@@ -159,37 +183,46 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
return tasks
},
- requireDependencies: () => {
- this.require( this.requires )
- return tasks
- },
-
setupListeners: () => {
this.scene.addEventListener('apps:2D', () => this.el.setAttribute('visible', false) )
this.scene.addEventListener('apps:XR', () => {
- console.log("JAXR")
this.el.setAttribute('visible', true)
this.el.setAttribute("html",`html:#${this.el.uid}; cursor:#cursor`)
})
return tasks
+ },
+
+ triggerKeyboardForInputs: () => {
+ // https://developer.oculus.com/documentation/web/webxr-keyboard ;
+ [...this.el.dom.querySelectorAll('[type=text]')].map( (input) => {
+ let triggerKeyboard = function(){
+ this.focus()
+ console.log("focus")
+ }
+ input.addEventListener('click', triggerKeyboard )
+ })
+ return tasks
}
}
tasks
- .ensureOverlay()
.addCSS()
.createReactiveDOMElement()
.scaleDOMvsXR()
- .requireDependencies()
+ .triggerKeyboardForInputs()
.setupListeners()
- tasks.overlay.appendChild(this.el.dom)
+ document.querySelector('#overlay').appendChild(this.el.dom)
this.el.emit('DOMready',{el: this.el.dom})
}
// assign unique app id
if( !this.el.uid ) this.el.uid = '_'+String(Math.random()).substr(10)
+
+ // fetch requires
+ if( this.requires ) this.require( this.requires, 'requires:ready' )
+ else this.el.emit('requires:ready')
}
}( AFRAME.AComponent.prototype.updateProperties)
@@ -218,6 +251,7 @@ document.head.innerHTML += `
}
#toggle_overlay{
+ display:none;
position: fixed;
right: 20px;
bottom: 73px;