work in progress [might break]
This commit is contained in:
parent
513127a92b
commit
4a441bd9b2
|
@ -1,43 +1,37 @@
|
||||||
AFRAME.registerComponent('helloworld', {
|
AFRAME.registerComponent('helloworld', {
|
||||||
schema: {
|
schema: {
|
||||||
|
foo: { type:"string"}
|
||||||
},
|
},
|
||||||
|
|
||||||
dependencies:{
|
dependencies:{
|
||||||
"iso": "https://rawgit.com/Utopiah/reponame/master/dist/file.min.js", // fallback URL for xrsh in case component
|
"html": "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
"xterm": "https://rawgit.com/Utopiah/reponame/master/dist/file.min.js", // was not loaded (in AFRAME.components)
|
"stylis": "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js" // modern CSS (https://stylis.js.org)
|
||||||
"content-menu": "https://rawgit.com/Utopiah/reponame/master/dist/file.min.js", // TIP: include branch/commit in URL to lock specify version
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dom: {
|
||||||
|
scale: 3.5,
|
||||||
|
events: ['click'],
|
||||||
|
html: `<div id="hello"><button>hello world</button></div>`,
|
||||||
|
css: `div{ #hello {position:relative;top:0;width:300px} }`
|
||||||
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
"html": function( ){ console.log("htmlmesh component mounted!") }, // html-component was added to this AFRAME entity
|
||||||
|
"title": function(e){ this.dom.el.querySelector("button").innerHTML = e.detail.v }, // this.data.title was changed
|
||||||
|
"click": function(e){ // a click was detected on this.dom.el or AFRAME entity
|
||||||
|
this.data.title = 'hello world '+(new Date().getTime())
|
||||||
|
console.dir(e.detail.target || e.target)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"iso": function(tty){// act when component gets mounted
|
init: function () {
|
||||||
// 'term' is basically AFRAME.components.ISOterminal
|
this.require( this.dependencies )
|
||||||
tty.write('hello to tty ISO-os from AFRAME')
|
.then( () => {
|
||||||
tty.on('stdout', (data) => {
|
document.body.appendChild(this.dom.el)
|
||||||
// react to data being spoken/typed into the terminal
|
this.el.setAttribute("html",'html:#hello; cursor:#cursor')
|
||||||
// (spatial prompting like 'open foo.gltf', 'component helloworld' e.g.)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
"xterm": function(xterm){// act when component gets mounted
|
|
||||||
// 'term' is basically AFRAME.components.ISOterminal
|
|
||||||
},
|
|
||||||
|
|
||||||
"content-menu": function(menu){
|
|
||||||
menu.add({
|
|
||||||
name: 'edit', // "everything must have an edit-button" ~ Fabien Benetou
|
|
||||||
icon: 'gear', // see https://jsonforms.io to see json-2-html forms
|
|
||||||
type: 'object', // json-2-webxr has nothing like it (yet) but offers uniform interfaces across components
|
|
||||||
properties:{
|
|
||||||
enabled: { type: 'boolean', default: true, format: 'checkbox' },
|
|
||||||
edit_terminal: { type: 'function', cb: () => AFRAME.components.ISOterminal.exec('pico /com/helloworld.js') },
|
|
||||||
edit_spatial: { type: 'function', cb: () => this.require({"spatial-edit":{required:true}}) }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function () { },
|
|
||||||
|
|
||||||
manifest: { // HTML5 manifest to identify app to xrsh
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
"short_name": "Hello world",
|
"short_name": "Hello world",
|
||||||
"name": "Hello world",
|
"name": "Hello world",
|
||||||
|
@ -56,18 +50,18 @@ AFRAME.registerComponent('helloworld', {
|
||||||
"theme_color": "#3367D6",
|
"theme_color": "#3367D6",
|
||||||
"shortcuts": [
|
"shortcuts": [
|
||||||
{
|
{
|
||||||
"name": "How are you today?",
|
"name": "What is the latest news?",
|
||||||
|
"cli":{
|
||||||
|
"usage": "helloworld <type> [options]",
|
||||||
|
"example": "helloworld news",
|
||||||
|
"args":{
|
||||||
|
"--latest": {type:"string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
"short_name": "Today",
|
"short_name": "Today",
|
||||||
"description": "View weather information for today",
|
"description": "View weather information for today",
|
||||||
"url": "/today?source=pwa",
|
"url": "/today?source=pwa",
|
||||||
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "How's weather tomorrow?",
|
|
||||||
"short_name": "Tomorrow",
|
|
||||||
"description": "View weather information for tomorrow",
|
|
||||||
"url": "/tomorrow?source=pwa",
|
|
||||||
"icons": [{ "src": "/images/tomorrow.png", "sizes": "192x192" }]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Hello world information",
|
"description": "Hello world information",
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
AFRAME.registerComponent('helloworld', {
|
||||||
|
schema: {
|
||||||
|
foo: { type:"string"}
|
||||||
|
},
|
||||||
|
dependencies:{
|
||||||
|
"html": "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
"stylis": "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js" // modern CSS (https://stylis.js.org)
|
||||||
|
},
|
||||||
|
dom: {
|
||||||
|
scale: 3.5,
|
||||||
|
events: ['click'],
|
||||||
|
html: `<div id="hello"><button>hello</button></div>`,
|
||||||
|
css: `div{ #hello {position:absolute;top:0;width:300px} }`
|
||||||
|
},
|
||||||
|
events:{
|
||||||
|
"html": function( ){ console.log("htmlmesh component mounted!") }, // html-component was added to this AFRAME entity
|
||||||
|
"title": function(e){ this.dom.el.querySelector("button").innerHTML = e.detail.v }, // this.data.title was changed
|
||||||
|
"click": function(e){ alert("clicked "+ (e.detail.target || e.target).tagName ) }, // a click was detected on this.dom.el or AFRAME entity
|
||||||
|
// "aframe-html": function(){
|
||||||
|
// this.el.setAttribute()
|
||||||
|
// alert("aframe loaded")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// "iso": function(tty){// act when component gets mounted
|
||||||
|
// // 'term' is basically AFRAME.components.ISOterminal
|
||||||
|
// tty.write('hello to tty ISO-os from AFRAME')
|
||||||
|
// tty.on('stdout', (data) => {
|
||||||
|
// // react to data being spoken/typed into the terminal
|
||||||
|
// // (spatial prompting like 'open foo.gltf', 'component helloworld' e.g.)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// "xterm": function(xterm){// act when component gets mounted
|
||||||
|
// // 'term' is basically AFRAME.components.ISOterminal
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// "content-menu": function(menu){
|
||||||
|
// menu.add({
|
||||||
|
// name: 'edit', // "everything must have an edit-button" ~ Fabien Benetou
|
||||||
|
// icon: 'gear', // see https://jsonforms.io to see json-2-html forms
|
||||||
|
// type: 'object', // json-2-webxr has nothing like it (yet) but offers uniform interfaces across components
|
||||||
|
// properties:{
|
||||||
|
// enabled: { type: 'boolean', default: true, format: 'checkbox' },
|
||||||
|
// edit_terminal: { type: 'function', cb: () => AFRAME.components.ISOterminal.exec('pico /com/helloworld.js') },
|
||||||
|
// edit_spatial: { type: 'function', cb: () => this.require({"spatial-edit":{required:true}}) }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
this.require( this.dependencies )
|
||||||
|
.then( () => {
|
||||||
|
document.body.appendChild(this.dom.el)
|
||||||
|
|
||||||
|
setInterval( () => this.data.title = String(Math.random()), 500 )
|
||||||
|
this.el.setAttribute("html",'html:#hello; cursor:#cursor')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
"short_name": "Hello world",
|
||||||
|
"name": "Hello world",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/images/icons-vector.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": "How are you today?",
|
||||||
|
"short_name": "Today",
|
||||||
|
"description": "View weather information for today",
|
||||||
|
"url": "/today?source=pwa",
|
||||||
|
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "How's weather tomorrow?",
|
||||||
|
"short_name": "Tomorrow",
|
||||||
|
"description": "View weather information for tomorrow",
|
||||||
|
"url": "/tomorrow?source=pwa",
|
||||||
|
"icons": [{ "src": "/images/tomorrow.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.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
// this is a highlevel way of loading buildless 'apps' (a collection of js components)
|
||||||
|
|
||||||
|
AFRAME.registerComponent('app', {
|
||||||
|
schema:{
|
||||||
|
"uri":{ type:"string"}
|
||||||
|
},
|
||||||
|
init: function() {
|
||||||
|
this.require([ this.data.uri ])
|
||||||
|
.then( () => {
|
||||||
|
let id = this.data.uri.split("/").pop() // 'a/b/c/mycom.js' => 'mycom.js'
|
||||||
|
let component = id.split(".js").shift() // 'mycom.js' => 'mycom'
|
||||||
|
let entity = document.createElement("a-entity")
|
||||||
|
this.el.setAttribute(component,this.data)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// usage: require(["./app/foo.js"])
|
||||||
|
// require({foo: "https://foo.com/foo.js"})
|
||||||
|
require: AFRAME.AComponent.prototype.require = function(packages){
|
||||||
|
let deps = []
|
||||||
|
if( !packages.map ) packages = Object.values(packages)
|
||||||
|
packages.map( (package) => {
|
||||||
|
let id = package.split("/").pop()
|
||||||
|
if( !document.head.querySelector(`script#${id}`) ){
|
||||||
|
let p = new Promise( (resolve,reject) => {
|
||||||
|
let script = document.createElement("script")
|
||||||
|
script.id = id
|
||||||
|
script.src = package
|
||||||
|
script.onload = () => resolve()
|
||||||
|
script.onerror = (e) => reject(e)
|
||||||
|
document.head.appendChild(script)
|
||||||
|
})
|
||||||
|
deps.push(p)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return Promise.all(deps)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
// monkeypatching initComponent will trigger events when components
|
||||||
|
// are initialized (that way apps can react to attached components)
|
||||||
|
// basically, in both situations:
|
||||||
|
// <a-entity foo="a:1"/>
|
||||||
|
// <a-entity app="uri: myapp.js"/> <!-- myapp.js calls this.require(['foo.js']) -->
|
||||||
|
//
|
||||||
|
// event 'foo' will be triggered as both entities (in)directly require component 'foo'
|
||||||
|
|
||||||
|
AFRAME.AComponent.prototype.initComponent = function(initComponent){
|
||||||
|
return function(){
|
||||||
|
this.el.emit( this.attrName, this)
|
||||||
|
return initComponent.apply(this,arguments)
|
||||||
|
}
|
||||||
|
}( AFRAME.AComponent.prototype.initComponent)
|
||||||
|
|
||||||
|
|
||||||
|
// monkeypatching updateProperties will detect a component config like:
|
||||||
|
// dom: {
|
||||||
|
// html: `<h1>hello</h1>`,
|
||||||
|
// css: `#hello {color:red}`,
|
||||||
|
// events: ['click']
|
||||||
|
// }
|
||||||
|
// and use it to create a reactive DOM-component (using native javascript Proxy)
|
||||||
|
// which delegates all related DOM-events AND data-changes back to the AFRAME component
|
||||||
|
|
||||||
|
AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
|
return function(){
|
||||||
|
updateProperties.apply(this,arguments)
|
||||||
|
if( this.dom && this.data){
|
||||||
|
let 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})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let el = document.createElement('div')
|
||||||
|
el.innerHTML = this.dom.html
|
||||||
|
this.data = reactify( el, this.el )
|
||||||
|
this.dom.events.map( (e) => el.addEventListener(e, (ev) => this.el.emit(e,ev) ) )
|
||||||
|
this.dom.el = el
|
||||||
|
// add css if any
|
||||||
|
if( this.dom.css && !document.head.querySelector(`style#${this.attrName}`) ){
|
||||||
|
document.head.innerHTML += `<style id="${this.attrName}">${this.dom.css}</style>`
|
||||||
|
}
|
||||||
|
if( this.dom.scale ) this.el.setAttribute('scale',`${this.dom.scale} ${this.dom.scale} ${this.dom.scale}`)
|
||||||
|
//('[helloworld]').object3D.children[0].material.map.magFilter = THREE.NearestFilter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}( AFRAME.AComponent.prototype.updateProperties)
|
||||||
|
|
||||||
|
//
|
||||||
|
// base CSS for XRSH apps
|
||||||
|
//
|
||||||
|
document.head.innerHTML += `
|
||||||
|
<style type="text/css">
|
||||||
|
/* CSS reset */
|
||||||
|
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace, monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace, monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--xrsh-primary: #6839dc;
|
||||||
|
--xrsh-primary-fg: #FFF;
|
||||||
|
--xrsh-light-primary: #00a3Ff;
|
||||||
|
--xrsh-secondary: #872eff;
|
||||||
|
--xrsh-third: #ce7df2;
|
||||||
|
--xrsh-box-shadow: #0005;
|
||||||
|
--xrsh-dark-gray: #343334;
|
||||||
|
--xrsh-gray: #424280;
|
||||||
|
--xrsh-white: #fdfdfd;
|
||||||
|
--xrsh-light-gray: #efefef;
|
||||||
|
--xrsh-lighter-gray: #e4e2fb96;
|
||||||
|
--xrsh-font-sans-serif: system-ui, -apple-system, segoe ui, roboto, ubuntu, helvetica, cantarell, noto sans, sans-serif;
|
||||||
|
--xrsh-font-monospace: menlo, monaco, lucida console, liberation mono, dejavu sans mono, bitstream vera sans mono, courier new, monospace, serif;
|
||||||
|
--xrsh-font-size-0: 12px;
|
||||||
|
--xrsh-font-size-1: 14px;
|
||||||
|
--xrsh-font-size-2: 17px;
|
||||||
|
--xrsh-font-size-3: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a-scene{
|
||||||
|
position:fixed;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
bottom:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
html.a-fullscreen body{
|
||||||
|
color: var(--xrsh-dark-gray);
|
||||||
|
font-size: var(--xrsh-font-size-1);
|
||||||
|
font-family: var(--xrsh-font-sans-serif);
|
||||||
|
padding:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,h2,h3,h4,h5{
|
||||||
|
color: var(--xrsh-gray);
|
||||||
|
}
|
||||||
|
h1 { font-size: var(--xrsh-font-size-3); }
|
||||||
|
h2,h3,h4{ font-size: var(--xrsh-font-size-2); }
|
||||||
|
|
||||||
|
button,.btn,input[type=submit]{
|
||||||
|
border-radius:7px;
|
||||||
|
background: var(--xrsh-primary);
|
||||||
|
color: var(--xrsh-primary-fg);
|
||||||
|
transition:1s;
|
||||||
|
padding: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-block: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
button:hover,.btn:hover,input[type=submit]:hover{
|
||||||
|
filter: brightness(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
`
|
|
@ -1,14 +0,0 @@
|
||||||
AFRAME.registerComponent('require', {
|
|
||||||
init: function() {
|
|
||||||
},
|
|
||||||
|
|
||||||
})
|
|
||||||
// work in progress
|
|
||||||
//
|
|
||||||
// //<script src="https://coderofsalvation.github.io/xrsh-apps/helloworld.js"></script>
|
|
||||||
//const updateComponents = AFRAME.AEntity.prototype.updateComponents
|
|
||||||
//AFRAME.AEntity.prototype.updateComponents = function(updateComponents){
|
|
||||||
// return function(){
|
|
||||||
// return updateComponents.apply(this,args)
|
|
||||||
// }
|
|
||||||
//}(updateComponents)
|
|
Loading…
Reference in New Issue