diff --git a/com/codemirror.js b/com/codemirror.js
index 11dc5cf..84ca833 100644
--- a/com/codemirror.js
+++ b/com/codemirror.js
@@ -36,7 +36,7 @@ AFRAME.registerComponent('codemirror', {
css: (me) => `.CodeMirror{
width: ${me.com.data.width}px !important;
- height: ${me.com.data.height}px !important;
+ height: ${me.com.data.height-30}px !important;
}
.codemirror *{
font-size: 14px;
@@ -45,7 +45,10 @@ AFRAME.registerComponent('codemirror', {
letter-spacing: 0 !important;
text-shadow: 0px 0px 10px #F075;
}
- #${me.dom.id} .wb-body { overflow:hidden; }
+
+ .wb-body:has(> .codemirror){
+ overflow:hidden;
+ }
.CodeMirror {
margin-top:18px;
@@ -78,11 +81,30 @@ AFRAME.registerComponent('codemirror', {
this.editor.updateFile( this.data.file, instance.getValue() )
})
+ this
+ .handleFocus()
+
setTimeout( () => {
this.el.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}`) // only show aframe-html in xr
},1500)
},
+ handleFocus: function(){
+ const focus = (showdom) => (e) => {
+ if( this.editor ){
+ this.editor.focus()
+ }
+ if( this.el.components.window && this.data.renderer == 'canvas'){
+ this.el.components.window.show( showdom )
+ }
+ }
+ this.el.addEventListener('obbcollisionstarted', focus(false) )
+ this.el.sceneEl.addEventListener('enter-vr', focus(false) )
+ this.el.sceneEl.addEventListener('enter-ar', focus(false) )
+ this.el.sceneEl.addEventListener('exit-vr', focus(true) )
+ this.el.sceneEl.addEventListener('exit-ar', focus(true) )
+ },
+
updateFile: async function(file,str){
// we don't do via shellcmd: isoterminal.exec(`echo '${str}' > ${file}`,1)
// as it would require all kindof ugly stringescaping
diff --git a/com/html-as-texture-in-xr.js b/com/html-as-texture-in-xr.js
index 34b96fc..abf0766 100644
--- a/com/html-as-texture-in-xr.js
+++ b/com/html-as-texture-in-xr.js
@@ -21,7 +21,7 @@ if( !AFRAME.components['html-as-texture-in-xr'] ){
this.el.setAttribute("html",`html: ${this.data.domid}; cursor:#cursor; xrlayer: true`)
this.el.setAttribute("visible", AFRAME.utils.XD() == '3D' ? 'true' : 'false' )
if( this.data.faceuser ){
- this.el.setAttribute("position", AFRAME.utils.XD.getPositionInFrontOfCamera(0.5) )
+ this.el.setAttribute("position", AFRAME.utils.XD.getPositionInFrontOfCamera(0.8) )
}
},
diff --git a/com/isoterminal.js b/com/isoterminal.js
index 552cde1..2e4ca41 100644
--- a/com/isoterminal.js
+++ b/com/isoterminal.js
@@ -37,6 +37,7 @@ if( typeof AFRAME != 'undefined '){
overlayfs: { type:"string"},
width: { type: 'number',"default": -1 },
height: { type: 'number',"default": -1 },
+ depth: { type: 'number',"default": 0.03 },
lineHeight: { type: 'number',"default": 18 },
padding: { type: 'number',"default": 18 },
minimized: { type: 'boolean',"default":false},
@@ -44,19 +45,18 @@ if( typeof AFRAME != 'undefined '){
muteUntilPrompt:{ type: 'boolean',"default":true}, // mute stdout until a prompt is detected in ISO
HUD: { type: 'boolean',"default":false}, // link to camera movement
transparent: { type:'boolean', "default":false }, // need good gpu
- memory: { type: 'number', "default":64 }, // VM memory (in MB)
- bufferLatency: { type: 'number', "default":30 }, // in ms: bufferlatency from webworker to xterm (batch-update every char to texture)
+ memory: { type: 'number', "default":40 }, // VM memory (in MB) [NOTE: quest or smartphone might crash > 40mb ]
+ bufferLatency: { type: 'number', "default":1 }, // in ms: bufferlatency from webworker to xterm (batch-update every char to texture)
debug: { type: 'boolean', "default":false }
},
init: function(){
this.el.object3D.visible = false
- if( this.data.width == -1 ) this.data.width = document.body.offsetWidth
- if( this.data.height == -1 ) this.data.height = document.body.offsetHeight
- this.data.width -= this.data.padding*2
- this.data.height -= this.data.padding*2
+ this.calculateDimension()
this.initHud()
+ this.setupBox()
+
fetch(this.data.iso,{method: 'HEAD'})
.then( (res) => {
if( res.status != 200 ) throw 'not found'
@@ -79,17 +79,17 @@ if( typeof AFRAME != 'undefined '){
selfcontain: "com/selfcontainer.js",
// html to texture
htmlinxr: "com/html-as-texture-in-xr.js",
- // isoterminal features
+ // isoterminal global features
PromiseWorker: "com/isoterminal/PromiseWorker.js",
ISOTerminal: "com/isoterminal/ISOTerminal.js",
- localforage: "https://cdn.rawgit.com/mozilla/localForage/master/dist/localforage.js"
+ localforage: "com/isoterminal/localforage.js",
},
dom: {
- scale: 1.0,
+ scale: 0.66,
events: ['click','keydown'],
html: (me) => `
-
`,
@@ -105,7 +105,7 @@ if( typeof AFRAME != 'undefined '){
position:relative;
line-height: ${me.com.data.lineHeight}px;
}
- #vt100 {
+ #term {
outline: none !important;
}
@font-face {
@@ -127,10 +127,6 @@ if( typeof AFRAME != 'undefined '){
border:none;
padding:none;
}
- span blink:last-of-type{
- border-right: 8px solid #F07;
- padding-right: 3px;
- }
#overlay .winbox:has(> .isoterminal){
background:transparent;
@@ -218,8 +214,10 @@ if( typeof AFRAME != 'undefined '){
this.term = new ISOTerminal(instance,this.data)
instance.addEventListener('DOMready', () => {
- instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}; faceuser: true`)
- setTimeout( () => this.setupVT100(instance),100)
+ this.setupVT100(instance)
+ setTimeout( () => {
+ instance.setAttribute("html-as-texture-in-xr", `domid: #term; faceuser: true`)
+ },100)
//instance.winbox.resize(720,380)
let size = `width: ${this.data.width}; height: ${this.data.height}`
instance.setAttribute("window", `title: xrsh.iso; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}; ${size}; min: ${this.data.minimized}; max: ${this.data.maximized}`)
@@ -231,6 +229,8 @@ if( typeof AFRAME != 'undefined '){
// run iso
let opts = {dom:instance.dom}
for( let i in this.data ) opts[i] = this.data[i]
+ opts.cols = this.cols
+ opts.rows = this.rows
this.term.start(opts)
})
@@ -262,6 +262,7 @@ if( typeof AFRAME != 'undefined '){
if( this.el.components.window && this.data.renderer == 'canvas'){
this.el.components.window.show( showdom )
}
+ this.el.emit('focus',e.detail)
}
this.el.addEventListener('obbcollisionstarted', focus(false) )
@@ -290,17 +291,20 @@ if( typeof AFRAME != 'undefined '){
},
setupVT100: function(instance){
- const el = this.el.dom.querySelector('#vt100')
- this.vt100 = new VT100(
- Math.floor(this.data.width/this.data.lineHeight),
- Math.floor(this.data.height*0.8/this.data.lineHeight),
- el,
- 100
- )
+ const el = this.el.dom.querySelector('#term')
+ const opts = {
+ cols: this.cols,
+ rows: this.rows,
+ el_or_id: el,
+ max_scroll_lines: 100,
+ nodim: true
+ }
+ this.vt100 = new VT100( opts )
+ this.vt100.el = el
this.vt100.curs_set( 1, true)
el.focus()
+ this.el.addEventListener('focus', () => el.focus())
this.vt100.getch( (ch,t) => {
- console.log(ch)
this.term.send( ch )
this.vt100.curs_set( 0, true)
})
@@ -323,6 +327,27 @@ if( typeof AFRAME != 'undefined '){
//})
},
+ setupBox: function(){
+ // setup slightly bigger black backdrop (this.el.getObject3D("mesh"))
+ const w = this.data.width/950;
+ const h = this.data.height/950;
+ this.el.box = document.createElement('a-entity')
+ this.el.box.setAttribute("geometry",`primitive: box; width:${w}; height:${h}; depth: -${this.data.depth}`)
+ this.el.box.setAttribute("material","shader:flat; color:black; opacity:0.9; transparent:true; ")
+ this.el.box.setAttribute("position",`0 0 ${(this.data.depth/2)-0.001}`)
+ this.el.appendChild(this.el.box)
+ },
+
+ calculateDimension: function(){
+ if( this.data.width == -1 ) this.data.width = document.body.offsetWidth
+ if( this.data.height == -1 ) this.data.height = document.body.offsetHeight
+ if( this.data.height > this.data.width ) this.data.height = this.data.width // mobile smartphone fix
+ this.data.width -= this.data.padding*2
+ this.data.height -= this.data.padding*2
+ this.cols = Math.floor(this.data.width/this.data.lineHeight*1.9)
+ this.rows = Math.floor(this.data.height*0.5/this.data.lineHeight*1.7) // keep extra height for mobile browser bottom-bar (android)
+ },
+
events:{
// combined AFRAME+DOM reactive events
diff --git a/com/isoterminal/ISOTerminal.js b/com/isoterminal/ISOTerminal.js
index 3d65a03..8fc822f 100644
--- a/com/isoterminal/ISOTerminal.js
+++ b/com/isoterminal/ISOTerminal.js
@@ -61,11 +61,11 @@ ISOTerminal.prototype.convert = {
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
- return window.btoa(binary);
+ return btoa(binary);
},
base64ToArrayBuffer: function(base64) {
- const binaryString = window.atob(base64);
+ const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
@@ -211,11 +211,14 @@ ISOTerminal.prototype.startVM = function(opts){
\r[38;5;165m ▬▬▬▬▬▬▬▬ https://xrsh.isvery.ninja ▬▬▬▬▬▬▬▬▬▬▬▬
\r[38;5;165m local-first, polyglot, unixy WebXR IDE & runtime
\r
-\r credits: NLnet | @nlnet@nlnet.nl
-\r Leon van Kammen | @lvk@mastodon.online
-\r Fabien Benetou | @utopiah@mastodon.pirateparty.be
-\r Mr Doob | THREE.js
-\r Diego Marcos | AFRAME.js
+\r credits
+\r -------
+\r @nlnet@nlnet.nl
+\r @lvk@mastodon.online
+\r @utopiah@mastodon.pirateparty.be
+\r https://www.w3.org/TR/webxr
+\r https://three.org
+\r https://aframe.org
`
const text_color = "\r[38;5;129m"
diff --git a/com/isoterminal/VT100.js b/com/isoterminal/VT100.js
index 4d51ba1..e3bdd0e 100644
--- a/com/isoterminal/VT100.js
+++ b/com/isoterminal/VT100.js
@@ -1,8 +1,15 @@
-// https://raw.githubusercontent.com/vsinitsyn/vt100/refs/heads/coffeescript/public/javascripts/VT100.js
+// https://raw.githubusercontent.com/vetupinitsyn/vt100/refs/heads/coffeescript/public/javascripts/VT100.js
//
// VT100.js -- a text terminal emulator in JavaScript with a ncurses-like
// interface and a POSIX-like interface. (The POSIX-like calls are
// implemented on top of the ncurses-like calls, not the other way round.)
+//
+// required markup:
+//
+//
+
//
// Released under the GNU LGPL v2.1, by Frank Bi
//
@@ -77,8 +84,10 @@
// interpreted and acted on.
// constructor
-function VT100(wd, ht, el_or_id, max_scroll_lines, fg, bg)
+function VT100(opts)
{
+ this.opts = opts
+ let {cols, rows, el_or_id, max_scroll_lines, fg, bg, nodim} = opts
if (!max_scroll_lines) {
max_scroll_lines = 1000;
}
@@ -92,20 +101,20 @@ function VT100(wd, ht, el_or_id, max_scroll_lines, fg, bg)
var r;
var c;
var scr = typeof el_or_id == 'string' ? document.getElementById(el_or_id) : el_or_id
- this.wd_ = wd;
- this.ht_ = ht;
+ this.wd_ = cols;
+ this.ht_ = rows;
// Keep up to max_scroll_lines of scrollback history.
this.max_ht_ = max_scroll_lines;
this._set_colors(fg, bg);
- this.text_ = new Array(ht);
- this.attr_ = new Array(ht);
- this.redraw_ = new Array(ht);
- this.scroll_region_ = [0, ht-1];
+ this.text_ = new Array(rows);
+ this.attr_ = new Array(rows);
+ this.redraw_ = new Array(rows);
+ this.scroll_region_ = [0, rows-1];
this.start_row_id = 0;
- this.num_rows_ = ht;
- for (r = 0; r < ht; ++r) {
- this.text_[r] = new Array(wd);
- this.attr_[r] = new Array(wd);
+ this.num_rows_ = rows;
+ for (r = 0; r < rows; ++r) {
+ this.text_[r] = new Array(cols);
+ this.attr_[r] = new Array(cols);
this.redraw_[r] = 1;
}
this.scr_ = scr;
@@ -116,10 +125,14 @@ function VT100(wd, ht, el_or_id, max_scroll_lines, fg, bg)
this.key_buf_ = [];
this.echo_ = false;
this.esc_state_ = 0;
- this.log_level_ = VT100.DEBUG //WARN;
+ this.log_level_ = VT100.WARN
this.clear_all();
- this.refresh();
+
+ // rate limit this.refresh
+ this.refresh = this.throttleSmart( VT100.prototype.refresh.bind(this), 100)
+
+ this.setupTouchInputFallback() // smartphone
}
// public constants -- colours and colour pairs
@@ -172,6 +185,7 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
var vt = VT100.the_vt_, ch;
if (vt === undefined)
return true;
+
//if ( event.keyCode != undefined || !event.charCode){
// ch = event.keyCode;
// if (ch == 13)
@@ -180,11 +194,11 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
// return true;
// ch = String.fromCharCode(ch);
//} else {
- ch = event.charCode;
//dump("ch: " + ch + "\n");
//dump("ctrl?: " + event.ctrlKey + "\n");
- vt.debug("onkeypress:: keyCode: " + event.keyCode + ", ch: " + event.charCode);
- if (ch) {
+ vt.debug("onkeypress:: ch: " + event.code + " ,key: "+event.key);
+ if (event.key.length == 1) {
+ ch = event.key.charCodeAt(0)
if (ch > 255)
return true;
if (event.ctrlKey && event.shiftKey) {
@@ -203,51 +217,52 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
}
} else {
switch (event.key) {
- case "Backspace":
- ch = '\b';
- break;
- case "Tab":
- ch = '\t';
- break;
- case event.DOM_VK_RETURN:
- case event.DOM_VK_ENTER:
- ch = '\r';
- break;
- case event.DOM_VK_UP:
- if (this.cursor_key_mode_ == VT100.CK_CURSOR)
+ case "Backspace":
+ ch = '\b';
+ break;
+ case "Tab":
+ ch = '\t';
+ break;
+ case "Enter":
+ ch = '\n';
+ break;
+ case "ArrowUp":
+ if (this.cursor_key_mode_ == VT100.CK_CURSOR)
ch = '\x1b[A';
- else
+ else
ch = '\x1bOA';
- break;
- case event.DOM_VK_DOWN:
- if (this.cursor_key_mode_ == VT100.CK_CURSOR)
+ break;
+ case "ArrowDown":
+ if (this.cursor_key_mode_ == VT100.CK_CURSOR)
ch = '\x1b[B';
- else
+ else
ch = '\x1bOB';
- break;
- case event.DOM_VK_RIGHT:
- if (this.cursor_key_mode_ == VT100.CK_CURSOR)
+ break;
+ case "ArrowRight":
+ if (this.cursor_key_mode_ == VT100.CK_CURSOR)
ch = '\x1b[C';
- else
+ else
ch = '\x1bOC';
- break;
- case event.DOM_VK_LEFT:
- if (this.cursor_key_mode_ == VT100.CK_CURSOR)
+ break;
+ case "ArrowLeft":
+ if (this.cursor_key_mode_ == VT100.CK_CURSOR)
ch = '\x1b[D';
- else
+ else
ch = '\x1bOD';
- break;
- case event.DOM_VK_DELETE:
- ch = '\x1b[3~';
- break;
- case event.DOM_VK_HOME:
- ch = '\x1b[H';
- break;
- case event.DOM_VK_ESCAPE:
- ch = '\x1b';
- break;
- default:
- return true;
+ break;
+ case "Delete":
+ ch = '\x1b[3~';
+ break;
+ case "Home":
+ ch = '\x1b[H';
+ break;
+ case "Escape":
+ ch = '\x1b';
+ case "Control":
+ break;
+ default:
+ return true
+ break;
}
}
// Stop the event from doing anything else.
@@ -311,39 +326,39 @@ VT100.prototype.html_colours_ = function VT100_html_colours_(attr)
var fg, bg, co0, co1;
fg = attr.fg;
bg = attr.bg;
- switch (attr.mode & (VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD)) {
- case 0:
- case VT100.A_DIM | VT100.A_BOLD:
- co0 = '00';
- if (bg == VT100.COLOR_WHITE)
- co1 = 'ff';
- else
- co1 = 'c0';
- break;
- case VT100.A_BOLD:
- co0 = '00'; co1 = 'ff';
- break;
- case VT100.A_DIM:
- if (fg == VT100.COLOR_BLACK)
- co0 = '40';
- else
- co0 = '00';
- co1 = '40';
- break;
- case VT100.A_REVERSE:
- case VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD:
- co0 = 'c0'; co1 = '40';
- break;
- case VT100.A_REVERSE | VT100.A_BOLD:
- co0 = 'c0'; co1 = '00';
- break;
- default:
- if (fg == VT100.COLOR_BLACK)
- co0 = '80';
- else
- co0 = 'c0';
- co1 = 'c0';
- }
+ switch (attr.mode & (VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD)) {
+ case 0:
+ case VT100.A_DIM | VT100.A_BOLD:
+ co0 = '00';
+ if (bg == VT100.COLOR_WHITE)
+ co1 = 'ff';
+ else
+ co1 = 'c0';
+ break;
+ case VT100.A_BOLD:
+ co0 = '00'; co1 = 'ff';
+ break;
+ case VT100.A_DIM:
+ if (fg == VT100.COLOR_BLACK)
+ co0 = '40';
+ else
+ co0 = '00';
+ co1 = '40';
+ break;
+ case VT100.A_REVERSE:
+ case VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD:
+ co0 = 'c0'; co1 = 'ff';
+ break;
+ case VT100.A_REVERSE | VT100.A_BOLD:
+ co0 = 'c0'; co1 = '00';
+ break;
+ default:
+ if (fg == VT100.COLOR_BLACK)
+ co0 = '80';
+ else
+ co0 = 'c0';
+ co1 = 'c0';
+ }
return {
f: '#' + (fg & 4 ? co1 : co0) +
(fg & 2 ? co1 : co0) +
@@ -1283,6 +1298,52 @@ VT100.prototype.warn = function VT100_warn(message) {
}
}
+VT100.prototype.throttleSmart = function throttleSmart(fn, wait) {
+ let timeout, lastArgs;
+ return (...args) => {
+ lastArgs = lastArgs || []
+ if (!timeout) {
+ fn(...args); timeout = setTimeout(() => { fn(...lastArgs); timeout = null; }, wait);
+ } else lastArgs = args;
+ };
+}
+
+VT100.prototype.setupTouchInputFallback = function(){
+ this.scr_.addEventListener('touchend', () => {
+ if( !this.input ){
+ this.form = document.createElement("form")
+ this.form.addEventListener("submit", (e) => {
+ e.preventDefault()
+ this.key_buf_.push('\n')
+ setTimeout(VT100.go_getch_, 0);
+ })
+ this.input = document.createElement("input")
+ this.input.setAttribute("cols", this.opts.cols )
+ this.input.setAttribute("rows", this.opts.rows )
+ this.input.style.opacity = '0'
+ this.input.style.position = 'absolute'
+ this.input.style.left = '-9999px'
+ this.form.appendChild(this.input)
+ this.scr_.parentElement.appendChild(this.form)
+
+ this.input.handler = () => {
+ let ch = this.input.value
+ // detect backspace
+ //if( e.inputType == 'deleteContentBackward' ) ch = '\b'
+ this.input.value = ''
+ if( !ch ) return
+ this.key_buf_.push(ch);
+ setTimeout(VT100.go_getch_, 0);
+ this.input.valueLast = this.input.value
+ }
+ this.input.addEventListener('input', this.input.handler )
+
+ }
+ setTimeout( () => this.input.focus(), 10 )
+ })
+}
+
function dump(x) {
// Do nothing
+ console.log(x)
}
diff --git a/com/isoterminal/feat/autorestore.js b/com/isoterminal/feat/autorestore.js
index 83d4c9f..0213ec3 100644
--- a/com/isoterminal/feat/autorestore.js
+++ b/com/isoterminal/feat/autorestore.js
@@ -1,15 +1,31 @@
if( typeof emulator != 'undefined' ){
// inside worker-thread
+ importScripts("localforage.js") // we don't instance it again here (just use its functions)
this['emulator.restore_state'] = async function(data){
- await emulator.restore_state(data)
- console.log("restored state")
- this.postMessage({event:"state_restored",data:false})
+ return new Promise( (resolve,reject) => {
+ localforage.getItem("state", async (err,stateBase64) => {
+ if( stateBase64 && !err ){
+ state = ISOTerminal.prototype.convert.base64ToArrayBuffer( stateBase64 )
+ await emulator.restore_state(state)
+ console.log("restored state")
+ }else return reject("worker.js: emulator.restore_state (could not get state from localforage)")
+ resolve()
+ })
+ })
}
this['emulator.save_state'] = async function(){
console.log("saving session")
let state = await emulator.save_state()
- this.postMessage({event:"state_saved",data:state},[state])
+ localforage.setDriver([
+ localforage.INDEXEDDB,
+ localforage.WEBSQL,
+ localforage.LOCALSTORAGE
+ ]).then( () => {
+ localforage.setItem("state", ISOTerminal.prototype.convert.arrayBufferToBase64(state) )
+ console.log("state saved")
+ })
+ console.dir(state)
}
@@ -30,30 +46,21 @@ if( typeof emulator != 'undefined' ){
localforage.getItem("state", async (err,stateBase64) => {
if( stateBase64 && !err && confirm('continue last session?') ){
this.noboot = true // see feat/boot.js
- state = this.convert.base64ToArrayBuffer( stateBase64 )
-
- this.addEventListener('state_restored', function(){
+ try{
+ await this.worker['emulator.restore_state']()
// simulate / fastforward boot events
this.postBoot( () => {
this.send("l\n")
this.send("hook wakeup\n")
})
- })
-
- this.worker.postMessage({event:'emulator.restore_state',data:state})
+ }catch(e){ console.error(e) }
}
})
this.save = async () => {
- const state = await this.worker.postMessage({event:"emulator.save_state",data:false})
+ await this.worker['emulator.save_state']()
}
- this.addEventListener('state_saved', function(e){
- const state = e.detail
- localforage.setItem("state", this.convert.arrayBufferToBase64(state) )
- console.log("state saved")
- })
-
window.addEventListener("beforeunload", function (e) {
var confirmationMessage = "Sure you want to leave?\nTIP: enter 'save' to continue this session later";
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
diff --git a/com/isoterminal/feat/boot.js b/com/isoterminal/feat/boot.js
index fe20b6b..1d1024f 100644
--- a/com/isoterminal/feat/boot.js
+++ b/com/isoterminal/feat/boot.js
@@ -4,7 +4,11 @@ ISOTerminal.addEventListener('ready', function(e){
ISOTerminal.prototype.boot = async function(e){
// set environment
- let env = ['export BROWSER=1']
+ let env = [
+ `export LINES=${this.opts.rows}`,
+ `export COLUMNS=${this.opts.cols}`,
+ 'export BROWSER=1',
+ ]
for ( let i in document.location ){
if( typeof document.location[i] == 'string' ){
env.push( 'export '+String(i).toUpperCase()+'="'+decodeURIComponent( document.location[i]+'"') )