Compare commits

..

1 commit

Author SHA1 Message Date
aa67a102b3 feat/remotestorage
All checks were successful
/ test (push) Successful in 11s
2025-04-25 16:17:21 +02:00
15 changed files with 60308 additions and 8444 deletions

File diff suppressed because one or more lines are too long

8
make
View file

@ -41,7 +41,7 @@ standalone(){ # build standalone xrsh.com binary
echo "document.head.innerHTML += \`<style type='text/css'>"
cat src/index.css
echo -e "</style>\`;\n"
cat src/assets/winbox* src/com/{isoterminal,data2event,dom,html-as-texture-in-xr,pastedrop,require,window}.js src/com/lib/*.js src/com/isoterminal/{ISOTerminal.js,term.js,feat/boot.js,feat/jsconsole.js,feat/term.js,feat/remotekeyboard.js}
cat src/assets/winbox* src/com/{isoterminal,data2event,dom,html-as-texture-in-xr,pastedrop,require,window}.js src/com/lib/{aframe-html,xhook.min}.js src/com/isoterminal/{ISOTerminal.js,term.js,feat/boot.js,feat/jsconsole.js,feat/term.js,feat/remotekeyboard.js}
echo "// mark above libs as pre-required in require.js, to prevent 'component already registered' errors "
echo "AFRAME.utils.require.required = {";
echo " window: true,"
@ -68,7 +68,6 @@ standalone(){ # build standalone xrsh.com binary
echo " fbterm: true,"
echo " fbtermjs: true,"
echo " remotekeyboard: true"
echo " remotestorage: true"
echo " };"
} > xrsh.js
ls -lh xrsh.js
@ -81,10 +80,9 @@ standalone(){ # build standalone xrsh.com binary
dev(){ # start dev http server
set -x
cd "$DIR"
wget "$REDBEAN_VERSION" -O /tmp/server.com
chmod +x /tmp/server.com
test -f xrsh.com || standalone
test -f /tmp/cert.pem || openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /tmp/key.pem -out /tmp/cert.pem
/tmp/server.com -c 0 -C /tmp/cert.pem -K /tmp/key.pem -D . "$@"
./xrsh.com -c 0 -C /tmp/cert.pem -K /tmp/key.pem -D . "$@"
}
shell(){ # run xrsh shell via docker

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
src/engine.glb Normal file

Binary file not shown.

View file

@ -8,7 +8,7 @@
*/
:root {
--xrsh-primary: #00AA;
--xrsh-primary: #3a51ff;
--xrsh-primary-fg: #FFF;
--xrsh-light-primary: #00a3Ff;
--xrsh-secondary: #872eff;
@ -17,11 +17,9 @@
--xrsh-dark-gray: #343334;
--xrsh-gray: #424280;
--xrsh-white: #fdfdfd;
--xrsh-black: #001c;
--xrsh-light-gray: #efefef;
--xrsh-lighter-gray: #e4e2fb96;
--xrsh-window-radius: 10px;
--xrsh-control-radius: 7px;
--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;
@ -56,7 +54,7 @@ a,a:visited,a:active{
*/
button,.btn,input[type=submit]{
border-radius: var(--xrsh-control-radius);
border-radius:7px;
background: var(--xrsh-primary);
color: var(--xrsh-primary-fg);
transition:0.3s;
@ -100,55 +98,31 @@ button,input,.btn{
*/
.winbox {
box-shadow:none !important;
background: transparent !important;
background:transparent !important;
}
.winbox .wb-body{
background: var(--xrsh-dark-gray);
/*margin-top: -6px !important;*/
margin-top: -6px !important;
z-index: 100;
border-radius:0px 0px var(--xrsh-window-radius) var(--xrsh-window-radius);
border: 1px solid #FFF3;
border-radius:7px;
}
.winbox .wb-header{
background: var(--xrsh-dark-gray);
background:transparent !important;
z-index:1000;
font-weight: bold;
border-radius: var(--xrsh-window-radius) var(--xrsh-window-radius) 0px 0px;
line-height:36px;
}
.winbox.max .wb-header{
border-radius:0px;
}
.XR .winbox .wb-header{
border-radius:0px !important;
margin-top:0px;
}
.XR .winbox .wb-control .xr {
display:block;
}
.winbox .wb-control .xr {
display:none;
background:transparent !important;
position: absolute;
top: -2px;
cursor:pointer;
}
.winbox .wb-control .xr.xr-wb-close,
.winbox .wb-control .xr.xr-wb-min{
right: 2px;
}
.winbox .wb-body{
top: 35 !important;
top:0px;
padding-top:35px;
}
.winbox .wb-control{
padding-right:5px;
height:29px;
color:#FFF;
z-index:1001;
}
.winbox .wb-drag{
padding-left:19px;
@ -186,6 +160,7 @@ button,input,.btn{
background: var(--xrsh-white);
}
/*
* icons for non-XR (please source from https://css.gg
*

View file

@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="./index.css"></link>
<script src="./assets/aframe.min.js"></script>
<script src="./assets/xrfragment.aframe.js"></script>
<script src="com/require.js"></script>
<script src="com/isoterminal.js"></script>
@ -15,6 +14,28 @@
<script src="com/launcher.js"></script>
<script src="com/helloworld-window.js"></script>
<script src="com/pressable.js"></script>
<style type="text/css">
#remotestorage-widget {
z-index: 21000000;
position: fixed;
top: 0;
right: 0;
}
.rs-widget{
border-radius: 7px !important;
}
#remotestorage-widget h1,
#remotestorage-widget h2,
#remotestorage-widget h3{
color: #55a;
}
svg.rs-main-logo{
filter: hue-rotate(240deg);
}
.rs-closed svg.rs-main-logo{
filter: grayscale(1) brightness(0.5);
}
</style>
</head>
<body style="background:linear-gradient( 45deg, #324, #8c74ff)">
@ -25,6 +46,7 @@
xr-mode-ui="XRMode: xr"
obb-collider="showColliders:false"
raycaster="objects: [html]; interval:100;"
remotestorage="dropbox: apikey_here; googledrive: apikey_here; logging: true; folder: xrsh"
>
<a-entity isoterminal="iso: ./../xrsh.iso; overlayfs: /package.overlayfs.zip; bootMenuURL: 1" position="0 1.6 -0.3">

View file

@ -1,56 +1,37 @@
isoterminal = document.querySelector("[isoterminal]")
term = document.querySelector('[isoterminal]').components.isoterminal.term
convert = ISOTerminal.prototype.convert
isoterminal.addEventListener("ready", function(){
term = isoterminal.components.isoterminal.term
convert = ISOTerminal.prototype.convert
console.test.tests = {} // empty previous tests
console.test("com/isoterminal/ISOTerminal.js", async () => {
term.worker.create_file("foo", convert.toUint8Array("hello") )
.then( (res) => {
term.worker.read_file("foo")
.then( (res) => {
try{
let data = convert.Uint8ArrayToString( res )
console.assert( data == "hello", "worker.create_file")
}catch(e){
console.assert( false, "worker.create_file")
}
})
})
.catch(console.error)
.finally(console.dir)
console.test("com/isoterminal/ISOTerminal.js", async () => {
term.worker.read_file("root/.profile")
.then( (res) => {
try{
let data = convert.Uint8ArrayToString( res )
console.assert( data.length, "worker.postMessage.promise()")
}catch(e){
console.assert( false, "worker.postMessage.promise()")
}
})
.catch(console.error)
.finally(console.dir)
console.test("com/isoterminal/ISOTerminal.js", async () => {
})
console.test("com/isoterminal/ISOTerminal.js", async () => {
term.worker.create_file("foo", convert.toUint8Array("hello") )
.then( (res) => {
term.worker.read_file("foo")
.then( (res) => {
try{
let data = convert.Uint8ArrayToString( res )
console.assert( data.length && data == "hello", "worker.postMessage.promise()")
console.assert( data == "hello", "worker.create_file")
}catch(e){
console.assert( false, "worker.postMessage.promise()")
console.assert( false, "worker.create_file")
}
})
.catch(console.error)
.finally(console.dir)
})
console.test.run()
.catch(console.error)
.finally(console.dir)
})
console.test("com/isoterminal.js", async () => {
console.assert( AFRAME.components.isoterminal != undefined, "AFRAME.components.isoterminal")
})
console.test("xrsh.iso", async () => {
console.assert( false, "create testsuite.zip package which tests iso")
})

View file

@ -1,10 +0,0 @@
const run = async function(){
await AFRAME.utils.require({
"test_util": "tests/util.js",
"test_isoterminal":"tests/ISOTerminal.js"
})
console.test.run()
}
run()

View file

@ -1,23 +1,20 @@
// minimalistc testrunner
window.console.test = async (name,cb) => {
console.test = async (name,cb) => {
let tests = console.test.tests = console.test.tests || {}
tests[name] = tests[name] || []
tests[name].push(cb)
}
window.console.test.run = async (cb) => {
const tests = window.console.test.tests
for( let name in tests ){
console.log("\x1b[34m\x1b[36;49m"+name+"\x1B[m")
for( let test in tests[name] ){
await tests[name][test]()
console.test.run = async () => {
for( let name in tests ){
console.log("\x1b[34m\x1b[36;49m"+name+"\x1B[m")
for( let test in tests[name] ){
await tests[name][test]()
}
}
}
if( cb ) cb()
}
window.console.assert = ((assert) => (a,reason,data) => {
console.assert = ((assert) => (a,reason,data) => {
console.log("\x1b[34m\x1b[36;49m♥ \x1B[m"+reason)
assert.call( console, a, {reason} )
if( !a ){

@ -1 +1 @@
Subproject commit 10bbfd27e21a1b7368c38432178309263662aef6
Subproject commit 9d549454d62671f52b007fa91098453008c37768

60139
src/xrsh.html Normal file

File diff suppressed because one or more lines are too long

BIN
xrsh.com

Binary file not shown.

View file

@ -1 +1 @@
e791dba58adf568d34648eeed71e047d60b126b3dd5a6d81306a4307c5b7c3ad xrsh.com
d25cabf0091a73f78e13c6a3135aad6178dc4708c02733446ab659b018cdfe96 xrsh.com

253
xrsh.js
View file

@ -14,7 +14,7 @@ document.head.innerHTML += `<style type='text/css'>
*/
:root {
--xrsh-primary: #00AA;
--xrsh-primary: #3a51ff;
--xrsh-primary-fg: #FFF;
--xrsh-light-primary: #00a3Ff;
--xrsh-secondary: #872eff;
@ -23,11 +23,9 @@ document.head.innerHTML += `<style type='text/css'>
--xrsh-dark-gray: #343334;
--xrsh-gray: #424280;
--xrsh-white: #fdfdfd;
--xrsh-black: #001c;
--xrsh-light-gray: #efefef;
--xrsh-lighter-gray: #e4e2fb96;
--xrsh-window-radius: 10px;
--xrsh-control-radius: 7px;
--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;
@ -62,7 +60,7 @@ a,a:visited,a:active{
*/
button,.btn,input[type=submit]{
border-radius: var(--xrsh-control-radius);
border-radius:7px;
background: var(--xrsh-primary);
color: var(--xrsh-primary-fg);
transition:0.3s;
@ -106,55 +104,31 @@ button,input,.btn{
*/
.winbox {
box-shadow:none !important;
background: transparent !important;
background:transparent !important;
}
.winbox .wb-body{
background: var(--xrsh-dark-gray);
/*margin-top: -6px !important;*/
margin-top: -6px !important;
z-index: 100;
border-radius:0px 0px var(--xrsh-window-radius) var(--xrsh-window-radius);
border: 1px solid #FFF3;
border-radius:7px;
}
.winbox .wb-header{
background: var(--xrsh-dark-gray);
background:transparent !important;
z-index:1000;
font-weight: bold;
border-radius: var(--xrsh-window-radius) var(--xrsh-window-radius) 0px 0px;
line-height:36px;
}
.winbox.max .wb-header{
border-radius:0px;
}
.XR .winbox .wb-header{
border-radius:0px !important;
margin-top:0px;
}
.XR .winbox .wb-control .xr {
display:block;
}
.winbox .wb-control .xr {
display:none;
background:transparent !important;
position: absolute;
top: -2px;
cursor:pointer;
}
.winbox .wb-control .xr.xr-wb-close,
.winbox .wb-control .xr.xr-wb-min{
right: 2px;
}
.winbox .wb-body{
top: 35 !important;
top:0px;
padding-top:35px;
}
.winbox .wb-control{
padding-right:5px;
height:29px;
color:#FFF;
z-index:1001;
}
.winbox .wb-drag{
padding-left:19px;
@ -192,6 +166,7 @@ button,input,.btn{
background: var(--xrsh-white);
}
/*
* icons for non-XR (please source from https://css.gg
*
@ -276,7 +251,6 @@ e.removeClass=function(a){this.g.classList.remove(a);return this};e.toggleClass=
*
* | property | type | default | info |
* |-------------------|-----------|------------------------|------|
* | `title` | `string` | 'xrsh.iso' | window title |
* | `iso` | `string` | https`//forgejo.isvery.ninja/assets/xrsh-buildroot/main/xrsh.iso" | |
* | `overlayfs` | `string` | '' | zip URL/file to autoextract on top of filesystem |
* | `width` | `number` | 800 ||
@ -336,7 +310,6 @@ if( typeof AFRAME != 'undefined '){
schema: {
iso: { type:"string", "default":"https://forgejo.isvery.ninja/assets/xrsh-buildroot/main/xrsh.iso" },
overlayfs: { type:"string"},
title: { type:"string", "default":"xrsh.iso"},
width: { type: 'number',"default": 800 },
height: { type: 'number',"default": 600 },
depth: { type: 'number',"default": 0.03 },
@ -407,8 +380,6 @@ if( typeof AFRAME != 'undefined '){
.isoterminal{
padding: ${me.com.data.padding}px;
margin-top:-60px;
padding-bottom:60px;
width:100%;
height:99%;
resize: both;
@ -511,19 +482,18 @@ if( typeof AFRAME != 'undefined '){
-webkit-animation:none;
}
.winbox#${me.el.uid} .wb-header{
background: var(--xrsh-black) !important;
}
.wb-body:has(> .isoterminal){
background: var(--xrsh-black);
.wb-body:has(> .isoterminal){
background: #000C;
overflow:hidden;
}
.XR .isoterminal{
background: #000 !important;
.XR .wb-body:has(> .isoterminal){
background: transparent;
}
.XR .isoterminal{
background: #000;
}
.isoterminal *{
font-size: 14px;
font-family: "Cousine",Liberation Mono,DejaVu Sans Mono,Courier New,monospace;
@ -572,20 +542,15 @@ if( typeof AFRAME != 'undefined '){
remotekeyboard: "com/isoterminal/feat/remotekeyboard.js",
indexhtml: "com/isoterminal/feat/index.html.js",
indexjs: "com/isoterminal/feat/index.js.js",
autorestore: "com/isoterminal/feat/autorestore.js",
pastedropFeat: "com/isoterminal/feat/pastedrop.js",
httpfs: "com/isoterminal/feat/httpfs.js",
autorestore: "com/isoterminal/feat/autorestore.js",
}
if( document.location.hash.match(/#test/) || this.data.debug ){
features['tests'] = "tests/index.js"
}
if( this.data.emulator == 'fbterm' ){
features['fbtermjs'] = "com/isoterminal/term.js"
features['fbterm'] = "com/isoterminal/feat/term.js"
}
await AFRAME.utils.require(features)
// this one extends autorestore.js
await AFRAME.utils.require({remotestorage: "com/isoterminal/feat/remotestorage.js"})
this.el.setAttribute("selfcontainer","")
@ -610,7 +575,7 @@ if( typeof AFRAME != 'undefined '){
this.term.emit('term_init', {instance, aEntity:this})
//instance.winbox.resize(720,380)
let size = `width: ${this.data.width}; height: ${this.data.height}`
instance.setAttribute("window", `title: ${this.data.title}; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}; ${size}; min: ${this.data.minimized}; max: ${this.data.maximized}; class: no-full, no-min, no-close, no-max, no-resize; grabbable: components.html.el.object3D.children.${this.el.children.length}`)
instance.setAttribute("window", `title: xrsh.iso; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}; ${size}; min: ${this.data.minimized}; max: ${this.data.maximized}; class: no-full, no-max, no-resize; grabbable: components.html.el.object3D.children.${this.el.children.length}`)
})
instance.addEventListener('window.oncreate', (e) => {
@ -638,6 +603,7 @@ if( typeof AFRAME != 'undefined '){
this.term.addEventListener('ready', (e) => {
instance.dom.classList.remove('blink')
this.term.emit('status',"running")
if( this.data.debug ) this.runTests()
})
this.term.addEventListener('status', function(e){
@ -657,23 +623,6 @@ if( typeof AFRAME != 'undefined '){
instance.addEventListener('window.onmaximize', resize )
const focus = (e) => {
if( event.target == document.activeElement ){
// if we're already focused, the keyboard is already triggerend
// therefore we blur() the element for correctness
// which allows the WebXR keyboard dissappear if implemented at all
// [Meta Quest 2 does not do this]
event.target.blur()
}
// calculate distance between thumb and indexfinger to detect pinch
// which should prevent focus-event (annoying to have keyboard popping up during pinch)
if( e.detail?.withEl?.components['hand-tracking-controls'] ){
const hand = e.detail.withEl.components['hand-tracking-controls']
const thumb = hand.bones.find( (b) => b.name == 'thumb-tip' )
const diff = thumb.position.distanceTo(hand.indexTipPosition)
if( diff < 0.02) return // pinching! don't trigger keyboard (focus)
}
this.el.emit('focus',e.detail)
}
@ -692,6 +641,14 @@ if( typeof AFRAME != 'undefined '){
}
},
runTests: async function(){
await AFRAME.utils.require({
"test_util": "tests/util.js",
"test_isoterminal":"tests/ISOTerminal.js"
})
console.test.run()
},
setupPasteDrop: function(){
this.el.addEventListener('pasteFile', (e) => {
e.preventDefault() // prevent bubbling up to window (which is triggering this initially)
@ -715,10 +672,9 @@ if( typeof AFRAME != 'undefined '){
this.el.addEventListener('exec', (e) => this.term.exec( e.detail ) )
this.el.addEventListener('hook', (e) => this.term.hook( e.detail[0], e.detail[1] ) )
this.el.addEventListener('send', (e) => this.term.send( e.detail[0], e.detail[1] || 0 ) )
this.el.addEventListener('create_file', async (e) => await this.term.worker.create_file( e.detail[0], e.detail[1] ) )
this.el.addEventListener('create_file_from_url', async (e) => await this.term.worker.create_file_from_url( e.detail[0], e.detail[1] ) )
this.el.addEventListener('update_file', async (e) => await this.term.worker.update_file( e.detail[0], e.detail[1] ) )
this.el.addEventListener('append_file', async (e) => await this.term.worker.append_file( e.detail[0], e.detail[1] ) )
this.el.addEventListener('create_file', async (e) => await this.term.worker.create_file( e.detail[0], this.term.convert.toUint8Array(e.detail[1]) ) )
this.el.addEventListener('update_file', async (e) => await this.term.worker.update_file( e.detail[0], this.term.convert.toUint8Array(e.detail[1]) ) )
this.el.addEventListener('append_file', async (e) => await this.term.worker.append_file( e.detail[0], this.term.convert.toUint8Array(e.detail[1]) ) )
this.el.addEventListener('read_file', async (e) => {
const buf = await this.term.worker.read_file( e.detail[0] )
const str = new TextDecoder().decode(buf)
@ -766,7 +722,6 @@ if( typeof AFRAME != 'undefined '){
"scope": "/",
"theme_color": "#3367D6",
"shortcuts": [
/*
{
"name": "What is the latest news?",
"cli":{
@ -781,9 +736,8 @@ if( typeof AFRAME != 'undefined '){
"url": "/today?source=pwa",
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
}
*/
],
"description": "Runs an .iso file",
"description": "Hello world information",
"screenshots": [
{
"src": "/images/screenshot1.png",
@ -793,7 +747,7 @@ if( typeof AFRAME != 'undefined '){
}
],
"help":`
XRSH application
Helloworld application
This is a help file which describes the application.
It will be rendered thru troika text, and will contain
@ -906,9 +860,9 @@ if( !AFRAME.components.dom ){
this
.ensureOverlay()
.addCSS()
.createReactiveDOMElement()
.assignUniqueID()
.addCSS()
.scaleDOMvsXR()
.triggerKeyboardForInputs()
@ -1441,6 +1395,16 @@ AFRAME.registerComponent('window', {
setupWindow: async function(){
await AFRAME.utils.require(this.dependencies)
if( !this.el.dom ) return console.error('window element requires dom-component as dependency')
const close = () => {
let e = {halt:false}
this.el.emit('window.onclose',e)
if( e.halt ) return true
this.data.dom.style.display = 'none';
if( this.el.parentNode ) this.el.remove() //parentElement.remove( this.el )
this.data.dom.parentElement.remove()
return false
}
this.el.addEventListener('close', () => {
close()
this.el.winbox.close()
@ -1469,12 +1433,8 @@ AFRAME.registerComponent('window', {
this.el.components['obb-collider'].data.trackedObject3D = this.data.grabbable
this.el.components['obb-collider'].update()
},1000)
this.patchButtons(e)
},
onminimize: this.onminimize,
onrestore: this.onrestore,
onclose: this.onclose.bind(this),
onclose: close
});
this.data.dom.style.display = '' // show
@ -1494,46 +1454,7 @@ AFRAME.registerComponent('window', {
show: function(state){
this.el.dom.closest('.winbox').style.display = state ? '' : 'none'
},
onminimize: function(e){
if( AFRAME.scenes[0].renderer.xr.isPresenting ){
this.window.style.display = 'none'
}
},
onrestore: function(e){
if( AFRAME.scenes[0].renderer.xr.isPresenting ){
this.window.style.display = ''
}
},
onclose: function(){
let e = {halt:false}
this.el.emit('window.onclose',e)
if( e.halt ) return true
this.data.dom.style.display = 'none';
if( this.el.parentNode ) this.el.remove() //parentElement.remove( this.el )
this.data.dom.parentElement.remove()
return false
},
// the buttons don't work in XR because HTMLMesh does not understand onclick on divs
patchButtons: function(e){
let wEl = e.mount;
let controls = [...wEl.closest(".winbox").querySelectorAll(".wb-control span")]
controls.map( (c) => {
if( c.className.match(/wb-(close|min)/) ){
let btn = document.createElement("button")
btn.className = `xr xr-${c.className}`
btn.innerText = c.className == "wb-close" ? "x" : "_"
btn.addEventListener("click", (e) => { } )// this will bubble up (to click ancestor in XR)
c.appendChild(btn)
}
})
}
})
AFRAME.utils.positionObjectNextToNeighbor = function positionObjectNextToNeighbor(object, lastNeighbor = null, margin ){
@ -2251,17 +2172,14 @@ function ISOTerminal(instance,opts){
ISOTerminal.prototype.emit = function(event,data,sender){
data = data || false
const evObj = new CustomEvent(event, {detail: data} )
// forward event to worker/instance/AFRAME element or component-function
// this feels complex, but actually keeps event- and function-names more concise in codebase
let fire = () => {
this.preventFrameDrop( () => {
// forward event to worker/instance/AFRAME element or component-function
// this feels complex, but actually keeps event- and function-names more concise in codebase
this.dispatchEvent( evObj )
if( sender != "instance" && this.instance ) this.instance.dispatchEvent(evObj)
if( sender != "worker" && this.worker ) this.worker.postMessage({event,data}, PromiseWorker.prototype.getTransferable(data) )
if( sender !== undefined && typeof this[event] == 'function' ) this[event].apply(this, data && data.push ? data : [data] )
}
if( event.match(/^serial/) ){
this.preventFrameDrop( fire )
}else fire()
})
}
ISOTerminal.addEventListener = (event,cb) => {
@ -2273,18 +2191,20 @@ ISOTerminal.addEventListener = (event,cb) => {
ISOTerminal.prototype.exec = function(opts){
const shellscript = opts[0];
const cb = opts[1];
let cmd = `printf "\n\r"; { sh<<EOF\n ${shellscript}; \nEOF\n} &> /mnt/exec;\n`
console.log(cmd)
if( cb ){
window.cb = cb
cmd += `js 'document.querySelector("[isoterminal]").emit("read_file", ["exec", window.cb ])';\n`
this.send(`printf "\n\r"; ${shellscript} &> /mnt/exec; js '
document.querySelector("[isoterminal]").emit("read_file", ["exec", window.cb ])
';
\n`,1)
}else{
this.send(`printf "\n\r"; ${shellscript} \n`,1)
}
this.send( cmd, 1 )
}
ISOTerminal.prototype.hook = function(hookname,args){
let cmd = `{ type hook || source /etc/profile.sh; }; hook ${hookname} "${args.join('" "')}"`
this.exec([cmd])
this.exec(cmd)
}
ISOTerminal.prototype.serial_input = 0; // can be set to 0,1,2,3 to define stdinput tty (xterm plugin)
@ -2375,14 +2295,8 @@ ISOTerminal.prototype.start = function(opts){
},
cmdline: "rw root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose modules=virtio_pci tsc=reliable init_on_freg|=on vga=ask", //vga=0x122",
net_device:{
//type:"ne2k",
relay_url:"fetch"
//relay_url:"wss://relay.widgetry.org/"
//local_http: true,
//type:"virtio",
//relay_url:"fetch",
//cors_proxy: "https://corsproxy.io/"
relay_url:"fetch", // or websocket proxy "wss://relay.widgetry.org/",
type:"virtio"
},
//bzimage_initrd_from_filesystem: true,
//filesystem: {
@ -2499,7 +2413,6 @@ ISOTerminal.prototype.startVM = function(opts){
this.v86opts = opts
this.addEventListener('emulator-started', async (e) => {
if( this.boot.fromImage ) return this.emit('serial-output-string', "\r[!] downloading session...please wait\n\r[!] this could take a while depending on your connection..\n\r")
let line = ''
this.ready = false
@ -2522,17 +2435,12 @@ ISOTerminal.prototype.startVM = function(opts){
}
ISOTerminal.prototype.bootISO = function(){
const getImage = (str) => decodeURIComponent(
str.match(/\&img=/) ? str.replace(/.*img=/,'').replace(/\&.*/,'') : ''
)
let msglib = this.getLoaderMsg()
this.emit('status',msglib.loadmsg)
let msg = "\n\r" + msglib.empowermsg + msglib.text_color + msglib.loadmsg + msglib.text_reset
this.emit('serial-output-string', msg)
if( getImage(this.boot.hash) ){
this.boot.fromImage = true
}
this.emit('runISO',{...this.v86opts, bufferLatency: this.opts.bufferLatency, img: getImage(this.boot.hash) })
this.emit('runISO',{...this.v86opts, bufferLatency: this.opts.bufferLatency })
}
@ -4063,7 +3971,7 @@ Term.prototype.resizePixel = function (new_width, new_height)
return true;
}
ISOTerminal.addEventListener('ready', function(e){
setTimeout( () => this.boot(), 50 ) // allow other features/plugins to settle first (autorestore.js e.g.)
setTimeout( () => this.boot(), 50 ) // because of autorestore.js
})
ISOTerminal.prototype.bootMenu = function(e){
@ -4081,13 +3989,13 @@ ISOTerminal.prototype.bootMenu = function(e){
}else{ // autoboot
if( this.term ){
this.term.handler( String(e.detail.bootMenu || e.detail.bootMenuURL).charAt(0) )
this.term.handler( e.detail.bootMenu || e.detail.bootMenuURL )
this.term.handler("\n")
}
}
}
ISOTerminal.addEventListener('bootMenu', function(e){this.bootMenu(e) })
ISOTerminal.addEventListener('bootMenu', function(e){ this.bootMenu(e) } )
ISOTerminal.prototype.boot = async function(e){
// set environment
@ -4097,17 +4005,10 @@ ISOTerminal.prototype.boot = async function(e){
'export BROWSER=1',
]
for ( let i in document.location ){
if( typeof document.location[i] == 'string' && !String(i).match(/(hash|search)/) ){
if( typeof document.location[i] == 'string' ){
env.push( 'export '+String(i).toUpperCase()+'="'+decodeURIComponent( document.location[i]+'"') )
}
}
// we export the cached hash/query (because they might be gone due to remotestorage plugin)
if( this.boot.hash.charAt(2) == '&' ){ // strip bootoption
this.boot.hashExBoot = `#` + this.boot.hash.substr(3)
}
env.push( 'export HASH="'+decodeURIComponent( this.boot.hashExBoot || this.boot.hash ) +'"' )
env.push( 'export QUERY="'+decodeURIComponent( this.boot.query ) +'"' )
await this.worker.create_file("profile.browser", this.convert.toUint8Array( env.join('\n') ) )
if( this.serial_input == 0 ){
@ -4118,12 +4019,8 @@ ISOTerminal.prototype.boot = async function(e){
}
ISOTerminal.prototype.boot.fromImage = false
ISOTerminal.prototype.boot.menu = []
ISOTerminal.prototype.boot.hash = document.location.hash
ISOTerminal.prototype.boot.query = document.location.search
// here REPL's can be defined
ISOTerminal.prototype.boot.menu = []
// REPL: iso
if( typeof window.PromiseWorker != 'undefined' ){ // if xrsh v86 is able to run in in worker
@ -4149,10 +4046,10 @@ if( typeof window.PromiseWorker != 'undefined' ){ // if xrsh v86 is able to run
}
ISOTerminal.prototype.redirectConsole = function(handler){
const log = console._log = console.log;
const dir = console._dir = console.dir;
const err = console._error = console.error;
const warn = console._warn = console.warn;
const log = console.log;
const dir = console.dir;
const err = console.error;
const warn = console.warn;
const addLineFeeds = (str) => typeof str == 'string' ? str.replace(/\n/g,"\r\n") : str
console.log = (...args)=>{
@ -4187,13 +4084,12 @@ ISOTerminal.prototype.enableConsole = function(opts){
let _str = typeof str == 'string' ? str : JSON.stringify(str)
let finalStr = "";
prefix = prefix ? prefix+' ' : ''
String(_str).trim().split("\n").map( (line) => {
_str.trim().split("\n").map( (line) => {
finalStr += `${opts.stdout ? '' : "\x1b[38;5;165m/dev/browser: \x1b[0m"}`+prefix+line+'\n'
})
if( opts.stdout ){
this.emit('serial-output-string', finalStr, "worker")
this.emit('serial-output-string', finalStr)
}else this.emit('append_file', ["/dev/browser/console",finalStr])
this.lastStr = finalStr
})
window.addEventListener('error', function(event) {
@ -4284,6 +4180,12 @@ ISOTerminal.prototype.TermInit = function(){
// patch Term-class
Term.prototype.move_textarea = function(){} /* *TODO* *FIXME* does not work in winbox */
Term.prototype.pasteHandler = function(original){
return function (ev){
original.apply(this,[ev])
}
}( Term.prototype.pasteHandler )
Term.prototype.keyDownHandler = function(original){
return function (e){
if ((e.ctrlKey || e.metaKey) && e.key === 'v') {
@ -4498,6 +4400,5 @@ AFRAME.utils.require.required = {
httpfs: true,
fbterm: true,
fbtermjs: true,
remotekeyboard: true,
remotestorage: true
remotekeyboard: true
};