wip: added menu

This commit is contained in:
Leon van Kammen 2024-01-22 09:38:16 +00:00
parent 41a365200d
commit f1254d7fed
11 changed files with 708 additions and 134 deletions

View File

@ -5,6 +5,7 @@ AFRAME.registerComponent('helloworld-html', {
init: function () { init: function () {
this.el.addEventListener('ready', () => this.el.dom.style.display = 'none' )
}, },
requires:{ requires:{
@ -19,17 +20,12 @@ AFRAME.registerComponent('helloworld-html', {
</div>`, </div>`,
css: `.helloworld-html { css: `.helloworld-html {
color: var(--xrsh-dark-gray); /* see index.css */ color: var(--xrsh-light-gray); /* see index.css */
} }`,
`,
}, },
events:{ events:{
// component events
html: function( ){ console.log("html-mesh requirement mounted") },
ready: function(e){ console.log("requires are loaded") },
// combined AFRAME+DOM reactive events // combined AFRAME+DOM reactive events
keydown: function(e){ }, // keydown: function(e){ }, //
click: 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 }, 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") 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.el.dom.children[0].id = this.el.uid // important hint for html-mesh
this.data.myvalue = 1 this.data.myvalue = 1
setInterval( () => this.data.myvalue++, 100 ) 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", "name": "Hello world",
"icons": [ "icons": [
{ {
"src": "/images/icons-vector.svg", "src": "https://css.gg/browser.svg",
"type": "image/svg+xml", "type": "image/svg+xml",
"sizes": "512x512" "sizes": "512x512"
} }

View File

@ -3,15 +3,12 @@ AFRAME.registerComponent('helloworld-htmlform', {
foo: { type:"string"} foo: { type:"string"}
}, },
init: function () { init: function () {},
},
requires:{ requires:{
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME 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 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 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: { dom: {
@ -33,19 +30,15 @@ AFRAME.registerComponent('helloworld-htmlform', {
</fieldset> </fieldset>
<button>hello</button> <button>hello</button>
</div>`, </div>`,
css: `.helloworld-htmlform {
div { css: `.helloworld-htmlform > div { padding:11px; }`
padding:11px;
}
}
`,
}, },
events:{ events:{
// component events // component events
html: function( ){ console.log("html-mesh requirement mounted") }, html: function( ){ console.log("html-mesh requirement mounted") },
stylis: function( ){ console.log("stylis requirement mounted") },
// combined AFRAME+DOM reactive events // combined AFRAME+DOM reactive events
click: function(e){ }, // click: function(e){ }, //
@ -57,27 +50,26 @@ AFRAME.registerComponent('helloworld-htmlform', {
// reactive events for this.data updates // reactive events for this.data updates
myvalue: function(e){ this.el.dom.querySelector('#myvalue').innerText = this.data.myvalue }, myvalue: function(e){ this.el.dom.querySelector('#myvalue').innerText = this.data.myvalue },
// requires are loaded launcher: function(){
ready: function(e){ this.el.dom.style.display = ''
setTimeout( () => { new WinBox("Hello World",{
new WinBox("Hello World",{ width: 250,
width: 250, height: 315,
height: 315, minwidth:250,
minwidth:250, maxwidth:250,
maxwidth:250, maxheight:315,
maxheight:315, minheight:315,
minheight:315, x: 100,
x: 100, y: 100,
y: 100, id: this.el.uid, // important hint for html-mesh
id: this.el.uid, // important hint for html-mesh root: document.querySelector("#overlay"),
root: document.querySelector("#overlay"), mount: this.el.dom,
mount: this.el.dom onclose: () => { this.el.dom.style.display = 'none'; return false; }
}); });
},500) /*FIXME*/
}, },
DOMready: function( ){ ready: function( ){
this.el.dom.style.display = 'none'
console.log("this.el.dom has been added to DOM") console.log("this.el.dom has been added to DOM")
this.data.myvalue = 1 this.data.myvalue = 1
} }
@ -89,7 +81,7 @@ AFRAME.registerComponent('helloworld-htmlform', {
"name": "Hello world", "name": "Hello world",
"icons": [ "icons": [
{ {
"src": "/images/icons-vector.svg", "src": "https://css.gg/browser.svg",
"type": "image/svg+xml", "type": "image/svg+xml",
"sizes": "512x512" "sizes": "512x512"
} }

View File

@ -3,15 +3,12 @@ AFRAME.registerComponent('helloworld-window', {
foo: { type:"string"} foo: { type:"string"}
}, },
init: function () { init: function(){},
},
requires:{ requires:{
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME 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 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 winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", //
stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
}, },
dom: { dom: {
@ -20,12 +17,8 @@ AFRAME.registerComponent('helloworld-window', {
html: (me) => `<div> html: (me) => `<div>
<div class="pad"> ${me.data.foo} <b>${me.data.myvalue}</b></span> <div class="pad"> ${me.data.foo} <b>${me.data.myvalue}</b></span>
</div>`, </div>`,
css: `.helloworld-window {
div { css: `.helloworld-window div.pad { padding:11px; }`
.pad { padding:11px; }
}
}
`,
}, },
events:{ events:{
@ -41,28 +34,27 @@ AFRAME.registerComponent('helloworld-window', {
// reactive events for this.data updates // reactive events for this.data updates
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue }, myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
// requires are loaded ready: function( ){
ready: function(e){ this.el.dom.style.display = 'none'
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( ){
console.log("this.el.dom has been added to DOM") console.log("this.el.dom has been added to DOM")
this.data.myvalue = 1 this.data.myvalue = 1
setInterval( () => this.data.myvalue++, 100 ) 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", "name": "Hello world",
"icons": [ "icons": [
{ {
"src": "/images/icons-vector.svg", "src": "https://css.gg/browser.svg",
"type": "image/svg+xml", "type": "image/svg+xml",
"sizes": "512x512" "sizes": "512x512"
} }

View File

@ -3,8 +3,12 @@ AFRAME.registerComponent('helloworld', {
foo: { type:"string"} foo: { type:"string"}
}, },
init: function () { init: function () {
this.el.object3D.visible = false
this.el.setAttribute("geometry","primitive: octahedron") this.el.setAttribute("geometry","primitive: octahedron")
this.interval = setInterval( () => {
this.data.myvalue = ((this.data.myvalue||1.0) + 0.25) % 1
}, 400 )
}, },
requires:{ requires:{
@ -17,6 +21,13 @@ AFRAME.registerComponent('helloworld', {
somecomponent: function( ){ console.log("component requirement mounted") }, somecomponent: function( ){ console.log("component requirement mounted") },
ready: function(e){ console.log("requires are loaded") }, 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 manifest: { // HTML5 manifest to identify app to xrsh
@ -24,7 +35,7 @@ AFRAME.registerComponent('helloworld', {
"name": "Hello world", "name": "Hello world",
"icons": [ "icons": [
{ {
"src": "/images/icons-vector.svg", "src": "https://css.gg/shape-hexagon.svg",
"type": "image/svg+xml", "type": "image/svg+xml",
"sizes": "512x512" "sizes": "512x512"
} }

110
app/isoterminal.js Normal file
View File

@ -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) => `<div>
<div class="pad">to be implemented</span>
</div>`,
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 <type> [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.
`
}
});

185
app/launcher.js Normal file
View File

@ -0,0 +1,185 @@
/*
* ## launcher
*
* displays app (icons) for enduser to launch
*
* ```javascript
* <a-entity app="app/launcher.js"/>
* ```
*
* | property | type | example |
* |--------------|--------------------|----------------------------------------------------------------------------------------|
* | `registries` | `array` of strings | <a-entity app="app/launcher.js; registers: https://foo.com/index.json, ./index.json"/> |
*
* | 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) => `<div>
<div class="iconmenu"></div>
</div>`,
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 <type> [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.
`
}
});

View File

@ -4,14 +4,14 @@ AFRAME.registerComponent('manual', {
}, },
init: function () { 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:{ requires:{
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME 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 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 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: { dom: {
@ -35,19 +35,13 @@ AFRAME.registerComponent('manual', {
</ul> </ul>
</div>`, </div>`,
css: `.manual { css: `.manual > div { padding:11px }`
div {
padding:11px;
}
}
`,
}, },
events:{ events:{
// component events // component events
html: function( ){ console.log("html-mesh requirement mounted") }, html: function( ){ console.log("html-mesh requirement mounted") },
stylis: function( ){ console.log("stylis requirement mounted") },
// combined AFRAME+DOM reactive events // combined AFRAME+DOM reactive events
click: function(e){ }, // click: function(e){ }, //
@ -56,26 +50,24 @@ AFRAME.registerComponent('manual', {
// reactive events for this.data updates // reactive events for this.data updates
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue }, myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
// requires are loaded launcher: function(){
ready: function(e){ new WinBox( this.manifest.name, {
width: '70%',
setTimeout( () => { height: '70%',
new WinBox("XRSH manual",{ x:"center",
width:300, y:"center",
height:300, id: this.el.uid, // important hint for html-mesh
x:"center", root: document.querySelector("#overlay"),
y:"center", mount: this.el.dom,
id: this.el.uid, // important hint for html-mesh onclose: () => { this.el.dom.style.display = 'none'; return false; }
root: document.querySelector("#overlay"), });
mount: this.el.dom this.el.dom.style.display = ''
}); this.el.setAttribute("xrf", document.location.search || "https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
}, 500 )
this.el.setAttribute("xrf","https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
// navigate with: AFRAME.XRF.navigator.to("https://xrfragment.org/index.glb") // navigate with: AFRAME.XRF.navigator.to("https://xrfragment.org/index.glb")
}, },
DOMready: function( ){ DOMready: function( ){
this.el.dom.style.display = 'none'
console.log("this.el.dom has been added to DOM") console.log("this.el.dom has been added to DOM")
this.data.myvalue = 1 this.data.myvalue = 1
setInterval( () => this.data.myvalue++, 100 ) setInterval( () => this.data.myvalue++, 100 )
@ -84,11 +76,11 @@ AFRAME.registerComponent('manual', {
}, },
manifest: { // HTML5 manifest to identify app to xrsh manifest: { // HTML5 manifest to identify app to xrsh
"short_name": "Hello world", "short_name": "XRSH Manual",
"name": "Hello world", "name": "XRSH Manual",
"icons": [ "icons": [
{ {
"src": "/images/icons-vector.svg", "src": "https://css.gg/coffee.svg",
"type": "image/svg+xml", "type": "image/svg+xml",
"sizes": "512x512" "sizes": "512x512"
} }

89
app/spatialize.js Normal file
View File

@ -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 <type> [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.
`
}
});

94
app/vconsole.js Normal file
View File

@ -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 += `
<style type="text/css">
.vc-mask { display: none !important }
.vc-switch { display: none !important }
.vc-panel {
right:unset !important;
width:100%;
max-width:600px;
z-index:100 !important;
}
</style>
`
},
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 <type> [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.
`
}
});

73
app/xrfragments.js Normal file
View File

@ -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 <type> [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.
`
}
});

View File

@ -28,7 +28,18 @@ AFRAME.registerComponent('app', {
AFRAME.app[component].map( (app) => { AFRAME.app[component].map( (app) => {
if( !app.el.getAttribute(component) ) app.el.setAttribute(component,app.data) 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() { init: function() {
@ -83,7 +94,9 @@ AFRAME.registerComponent('app', {
} }
}catch(e){ console.error(`package ${package} could not be retrieved..aborting :(`); throw e; } }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(){ return function(){
updateProperties.apply(this,arguments) updateProperties.apply(this,arguments)
if( !this.data || !this.data.uri ) return // only deal with apps
const reactify = (el,aframe) => new Proxy(this.data,{ // ensure overlay
get(me,k,v) { return me[k] let overlay = document.querySelector('#overlay')
}, if( !overlay ){
set(me,k,v){ overlay = document.createElement('div')
me[k] = v overlay.id = "overlay"
aframe.emit(k,{el,k,v}) 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 = `<img src="${this.manifest.icons[0].src}"/>`
}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 // reactify components with dom-definition
if( this.data.uri && this.dom && !this.el.dom ){ if( this.data.uri && this.dom && !this.el.dom ){
tasks = { 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: () => { 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 = document.createElement('div')
this.el.dom.className = this.parseAppURI(this.data.uri).component this.el.dom.className = this.parseAppURI(this.data.uri).component
this.el.dom.innerHTML = this.dom.html(this) this.el.dom.innerHTML = this.dom.html(this)
this.el.dom.style.display = 'none'
this.data = reactify( this.dom.el, this.el ) this.data = reactify( this.dom.el, this.el )
this.dom.events.map( (e) => this.el.dom.addEventListener(e, (ev) => this.el.emit(e,ev) ) ) this.dom.events.map( (e) => this.el.dom.addEventListener(e, (ev) => this.el.emit(e,ev) ) )
return tasks return tasks
@ -159,37 +183,46 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
return tasks return tasks
}, },
requireDependencies: () => {
this.require( this.requires )
return tasks
},
setupListeners: () => { setupListeners: () => {
this.scene.addEventListener('apps:2D', () => this.el.setAttribute('visible', false) ) this.scene.addEventListener('apps:2D', () => this.el.setAttribute('visible', false) )
this.scene.addEventListener('apps:XR', () => { this.scene.addEventListener('apps:XR', () => {
console.log("JAXR")
this.el.setAttribute('visible', true) this.el.setAttribute('visible', true)
this.el.setAttribute("html",`html:#${this.el.uid}; cursor:#cursor`) this.el.setAttribute("html",`html:#${this.el.uid}; cursor:#cursor`)
}) })
return tasks 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 tasks
.ensureOverlay()
.addCSS() .addCSS()
.createReactiveDOMElement() .createReactiveDOMElement()
.scaleDOMvsXR() .scaleDOMvsXR()
.requireDependencies() .triggerKeyboardForInputs()
.setupListeners() .setupListeners()
tasks.overlay.appendChild(this.el.dom) document.querySelector('#overlay').appendChild(this.el.dom)
this.el.emit('DOMready',{el: this.el.dom}) this.el.emit('DOMready',{el: this.el.dom})
} }
// assign unique app id // assign unique app id
if( !this.el.uid ) this.el.uid = '_'+String(Math.random()).substr(10) 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) }( AFRAME.AComponent.prototype.updateProperties)
@ -218,6 +251,7 @@ document.head.innerHTML += `
} }
#toggle_overlay{ #toggle_overlay{
display:none;
position: fixed; position: fixed;
right: 20px; right: 20px;
bottom: 73px; bottom: 73px;