Compare commits
3 commits
ed9fc6ca8b
...
1b5cf3febb
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b5cf3febb | |||
| 938d1bc229 | |||
| dcef641217 |
9 changed files with 49 additions and 1656 deletions
|
|
@ -351,6 +351,12 @@ if( typeof AFRAME != 'undefined '){
|
||||||
instance.setAttribute("dom", "")
|
instance.setAttribute("dom", "")
|
||||||
instance.setAttribute("pastedrop", "")
|
instance.setAttribute("pastedrop", "")
|
||||||
|
|
||||||
|
|
||||||
|
// *REMOVE* make a boot-plugin mechanism in feat/term.js
|
||||||
|
this.term.addEventListener('enable-console', () => {
|
||||||
|
instance.dom.classList.remove('blink')
|
||||||
|
})
|
||||||
|
|
||||||
this.term.addEventListener('ready', (e) => {
|
this.term.addEventListener('ready', (e) => {
|
||||||
instance.dom.classList.remove('blink')
|
instance.dom.classList.remove('blink')
|
||||||
this.term.emit('status',"running")
|
this.term.emit('status',"running")
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -4,26 +4,52 @@ if( typeof emulator != 'undefined' ){
|
||||||
|
|
||||||
ISOTerminal.addEventListener('ready', function(e){
|
ISOTerminal.addEventListener('ready', function(e){
|
||||||
|
|
||||||
|
function getMimeType(file) {
|
||||||
|
const mimeTypes = {
|
||||||
|
jpg: 'image/jpeg',
|
||||||
|
jpeg: 'image/jpeg',
|
||||||
|
png: 'image/png',
|
||||||
|
mp4: 'video/mp4',
|
||||||
|
gif: 'image/gif',
|
||||||
|
};
|
||||||
|
|
||||||
|
const extension = file.split('.').pop().toLowerCase();
|
||||||
|
return mimeTypes[extension] || 'application/octet-stream'; // Fallback
|
||||||
|
}
|
||||||
|
|
||||||
// listen for http request to the filesystem ( file://host/path )
|
// listen for http request to the filesystem ( file://host/path )
|
||||||
xhook.before( (request,callback) => {
|
xhook.before( (request,callback) => {
|
||||||
|
console.log(request.url)
|
||||||
if (request.url.match(/^file:\/\/xrsh\/mnt\/.*/) ){
|
if (request.url.match(/^\/mnt\/.*/) ){
|
||||||
let response
|
let response
|
||||||
let file = request.url.replace(/^file:\/\/xrsh\/mnt\//,'')
|
let file = request.url.replace(/^\/mnt\//,'')
|
||||||
|
let mimetype = getMimeType(file)
|
||||||
this.worker.read_file_world(file)
|
this.worker.read_file_world(file)
|
||||||
.then( (data) => {
|
.then( (data) => {
|
||||||
response = new Response( new Blob( [data] ) ) // wrap Uint8Array into array
|
if( data == null ) throw `/mnt/${file} does not exist in ISO filesystem`"
|
||||||
response.status = 200
|
let blob = new Blob( [data], {type: getMimeType(file) }) // wrap Uint8Array into array
|
||||||
|
response = {
|
||||||
|
headers: new Headers({ 'Content-Type': getMimeType(file) }),
|
||||||
|
data,
|
||||||
|
url: file,
|
||||||
|
status: 200,
|
||||||
|
blob: () => new Promise( (resolve,reject) => resolve(blob) ),
|
||||||
|
arrayBuffer: blob.arrayBuffer
|
||||||
|
}
|
||||||
|
console.log("serving from iso filesystem: "+file)
|
||||||
|
console.log("*TODO* large files being served partially")
|
||||||
callback(response)
|
callback(response)
|
||||||
})
|
})
|
||||||
.catch( (e) => {
|
.catch( (e) => {
|
||||||
|
console.error(e)
|
||||||
response = new Response()
|
response = new Response()
|
||||||
response.status = 404
|
response.status = 404
|
||||||
callback(response)
|
callback(response)
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}else{
|
||||||
callback()
|
callback()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ ISOTerminal.prototype.redirectConsole = function(handler){
|
||||||
const dir = console.dir;
|
const dir = console.dir;
|
||||||
const err = console.error;
|
const err = console.error;
|
||||||
const warn = console.warn;
|
const warn = console.warn;
|
||||||
const addLineFeeds = (str) => str.replace(/\n/g,"\n\r")
|
const addLineFeeds = (str) => typeof str == 'string' ? str.replace(/\n/g,"\n\r") : str
|
||||||
|
|
||||||
console.log = (...args)=>{
|
console.log = (...args)=>{
|
||||||
const textArg = args[0];
|
const textArg = args[0];
|
||||||
|
|
@ -34,9 +34,10 @@ ISOTerminal.prototype.enableConsole = function(opts){
|
||||||
opts = opts || {stdout:false}
|
opts = opts || {stdout:false}
|
||||||
|
|
||||||
this.redirectConsole( (str,prefix) => {
|
this.redirectConsole( (str,prefix) => {
|
||||||
|
let _str = typeof str == 'string' ? str : JSON.stringify(str)
|
||||||
let finalStr = "";
|
let finalStr = "";
|
||||||
prefix = prefix ? prefix+' ' : ''
|
prefix = prefix ? prefix+' ' : ''
|
||||||
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'
|
finalStr += `${opts.stdout ? '' : "\x1b[38;5;165m/dev/browser: \x1b[0m"}`+prefix+line+'\n'
|
||||||
})
|
})
|
||||||
if( opts.stdout ){
|
if( opts.stdout ){
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ if( typeof emulator != 'undefined' ){
|
||||||
ISOTerminal.prototype.pasteFile = async function(data){
|
ISOTerminal.prototype.pasteFile = async function(data){
|
||||||
const {type,item,pastedText} = data
|
const {type,item,pastedText} = data
|
||||||
if( pastedText){
|
if( pastedText){
|
||||||
this.pasteWriteFile( this.convert.toUint8Array(pastedText) ,type, null, true)
|
// the terminal handles this (pastes text)
|
||||||
|
// this.pasteWriteFile( this.convert.toUint8Array(pastedText) ,type, null, true)
|
||||||
}else{
|
}else{
|
||||||
const file = item.getAsFile();
|
const file = item.getAsFile();
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
@ -35,8 +36,9 @@ if( typeof emulator != 'undefined' ){
|
||||||
const el = aEntity.el.dom.querySelector('#pastedrop') // upload input
|
const el = aEntity.el.dom.querySelector('#pastedrop') // upload input
|
||||||
el.addEventListener('change', (e) => {
|
el.addEventListener('change', (e) => {
|
||||||
const file = el.files[0];
|
const file = el.files[0];
|
||||||
const item = {...file, getAsFile: () => file }
|
const item = {...file, getAsFile: () => file } // pasteFile-event works with File objets
|
||||||
this.el.emit('pasteFile', { item, type: file.type });
|
const data = { item, type: file.type }
|
||||||
|
this.emit( 'pasteFile', data, "worker" ) // impersonate as worker (as worker cannot handle File objet)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
ISOTerminal.addEventListener('init', function(){
|
|
||||||
this.VT100Init()
|
|
||||||
})
|
|
||||||
|
|
||||||
ISOTerminal.prototype.VT100Init = function(){
|
|
||||||
|
|
||||||
const setupVT100 = (opts) => {
|
|
||||||
if( !opts ) return
|
|
||||||
const {instance, aEntity} = opts
|
|
||||||
const el = aEntity.el.dom.querySelector('#term')
|
|
||||||
opts.vt100 = {
|
|
||||||
cols: aEntity.cols,
|
|
||||||
rows: aEntity.rows,
|
|
||||||
el_or_id: el,
|
|
||||||
max_scroll_lines: aEntity.rows*2,
|
|
||||||
nodim: true,
|
|
||||||
rainbow: [VT100.COLOR_MAGENTA, VT100.COLOR_CYAN ],
|
|
||||||
xr: AFRAME.scenes[0].renderer.xr,
|
|
||||||
map: {
|
|
||||||
'ArrowRight': { ch: false, ctrl: '\x1b\x66' }, // this triggers ash-shell forward-word
|
|
||||||
'ArrowLeft': { ch: false, ctrl: '\x1b\x62' } // backward-word
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.vt100 = new VT100( opts.vt100 )
|
|
||||||
this.vt100.el = el
|
|
||||||
this.vt100.curs_set( 1, true)
|
|
||||||
this.vt100.focus()
|
|
||||||
this.vt100.getch( (ch,t) => {
|
|
||||||
this.send( ch )
|
|
||||||
})
|
|
||||||
aEntity.term.emit('initVT100',this)
|
|
||||||
aEntity.el.addEventListener('focus', () => this.vt100.focus() )
|
|
||||||
|
|
||||||
//aEntity.el.addEventListener('serial-output-byte', (e) => {
|
|
||||||
// const byte = e.detail
|
|
||||||
// var chr = String.fromCharCode(byte);
|
|
||||||
// this.vt100.addch(chr)
|
|
||||||
//})
|
|
||||||
aEntity.el.addEventListener('serial-output-string', (e) => {
|
|
||||||
this.vt100.write(e.detail)
|
|
||||||
})
|
|
||||||
|
|
||||||
//aEntity.el.addEventListener('serial-output-string', (e) => {
|
|
||||||
// const str = e.detail;
|
|
||||||
// let currentString = '';
|
|
||||||
// let inEscapeSequence = false;
|
|
||||||
// for (let i = 0; i < str.length; i++) {
|
|
||||||
// const chr = str[i];
|
|
||||||
// if (chr === '\r' || chr === '\n' || chr === '\b' || chr.charCodeAt(0) < 32 || chr.charCodeAt(0) === 127) {
|
|
||||||
// if (currentString) {
|
|
||||||
// this.vt100.write(currentString);
|
|
||||||
// currentString = '';
|
|
||||||
// }
|
|
||||||
// this.vt100.addch(chr);
|
|
||||||
// } else if (chr === '\x1b') {
|
|
||||||
// if (currentString) {
|
|
||||||
// this.vt100.write(currentString);
|
|
||||||
// currentString = '';
|
|
||||||
// }
|
|
||||||
// inEscapeSequence = true;
|
|
||||||
// this.vt100.addch(chr);
|
|
||||||
// } else if (inEscapeSequence) {
|
|
||||||
// if (chr ==='m') {
|
|
||||||
// inEscapeSequence = false;
|
|
||||||
// }
|
|
||||||
// this.vt100.addch(chr);
|
|
||||||
// } else {
|
|
||||||
// currentString += chr;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (currentString) {
|
|
||||||
// this.vt100.write(currentString);
|
|
||||||
// }
|
|
||||||
//});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// translate file upload into pasteFile
|
|
||||||
this.vt100.upload.addEventListener('change', (e) => {
|
|
||||||
const file = this.vt100.upload.files[0];
|
|
||||||
const item = {...file, getAsFile: () => file }
|
|
||||||
this.el.emit('pasteFile', { item, type: file.type });
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addEventListener('term_init', (opts) => setupVT100(opts.detail) )
|
|
||||||
}
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
ISOTerminal.addEventListener('init', function(){
|
|
||||||
if( typeof Terminal != 'undefined' ) this.xtermInit()
|
|
||||||
})
|
|
||||||
|
|
||||||
ISOTerminal.addEventListener('runISO', function(e){
|
|
||||||
let opts = e.detail
|
|
||||||
opts.serial_container_xtermjs = opts.screen_container
|
|
||||||
delete opts.screen_container
|
|
||||||
})
|
|
||||||
|
|
||||||
ISOTerminal.prototype.xtermInit = function(){
|
|
||||||
this.serial_input = 0 // set input to serial line 0
|
|
||||||
let isoterm = this
|
|
||||||
// monkeypatch Xterm (which V86 initializes) so we can add our own constructor args
|
|
||||||
window._Terminal = window.Terminal
|
|
||||||
window.Terminal = function(opts){
|
|
||||||
const term = new window._Terminal({ ...opts,
|
|
||||||
cursorBlink:true,
|
|
||||||
onSelectionChange: function(e){ console.log("selectchange") },
|
|
||||||
letterSpacing: 0
|
|
||||||
})
|
|
||||||
|
|
||||||
term.onSelectionChange( () => {
|
|
||||||
document.execCommand('copy')
|
|
||||||
term.select(0, 0, 0)
|
|
||||||
isoterm.emit('status','copied to clipboard')
|
|
||||||
})
|
|
||||||
|
|
||||||
term.onRender( () => {
|
|
||||||
// xterm relies on requestAnimationFrame (which does not called in immersive mode)
|
|
||||||
let _window = term._core._coreBrowserService._window
|
|
||||||
if( !_window._XRSH_proxied ){ // patch the planet!
|
|
||||||
|
|
||||||
//_window.requestAnimationFrameAFRAME = function(cb){
|
|
||||||
// if( term.tid != null ) clearTimeout(term.tid)
|
|
||||||
// term.tid = setTimeout( function(){
|
|
||||||
// console.log("render")
|
|
||||||
// cb()
|
|
||||||
// term.tid = null
|
|
||||||
// },100)
|
|
||||||
//}
|
|
||||||
this.i = 0
|
|
||||||
const requestAnimationFrameAFRAME = AFRAME.utils.throttleLeadingAndTrailing(
|
|
||||||
function(cb){ cb() }
|
|
||||||
,150
|
|
||||||
)
|
|
||||||
|
|
||||||
// we proxy the _window object of xterm, and reroute
|
|
||||||
// requestAnimationFrame to requestAnimationFrameAFRAME
|
|
||||||
_window_new = new Proxy(_window,{
|
|
||||||
get(me,k){
|
|
||||||
if( k == '_XRSH_proxied' ) return true
|
|
||||||
if( k == 'requestAnimationFrame' ){
|
|
||||||
return requestAnimationFrameAFRAME.bind(me)
|
|
||||||
}
|
|
||||||
return typeof me[k] == 'function' ? me[k].bind(me) : me[k]
|
|
||||||
},
|
|
||||||
set(me,k,v){
|
|
||||||
me[k] = v
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
term._core._coreBrowserService._window = _window_new
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return term
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.addEventListener('emulator-started', function(){
|
|
||||||
this.emulator.serial_adapter.term.element.querySelector('.xterm-viewport').style.background = 'transparent'
|
|
||||||
// toggle immersive with ESCAPE
|
|
||||||
//document.body.addEventListener('keydown', (e) => e.key == 'Escape' && this.emulator.serial_adapter.term.blur() )
|
|
||||||
})
|
|
||||||
|
|
||||||
const resize = (w,h) => {
|
|
||||||
setTimeout( () => {
|
|
||||||
if( isoterm?.emulator?.serial_adapter?.term ){
|
|
||||||
isoterm.xtermAutoResize(isoterm.emulator.serial_adapter.term, isoterm.instance,-3)
|
|
||||||
}
|
|
||||||
},800) // wait for resize anim
|
|
||||||
}
|
|
||||||
isoterm.instance.addEventListener('window.onresize', resize )
|
|
||||||
isoterm.instance.addEventListener('window.onmaximize', resize )
|
|
||||||
}
|
|
||||||
|
|
||||||
ISOTerminal.prototype.xtermAutoResize = function(term,instance,rowoffset){
|
|
||||||
if( !term.element ) return
|
|
||||||
|
|
||||||
const defaultScrollWidth = 24;
|
|
||||||
const MINIMUM_COLS = 2;
|
|
||||||
const MINIMUM_ROWS = 2;
|
|
||||||
|
|
||||||
const dims = term._core._renderService.dimensions;
|
|
||||||
const scrollbarWidth = (term.options.scrollback === 0
|
|
||||||
? 0
|
|
||||||
: (term.options.overviewRuler?.width || defaultScrollWidth ));
|
|
||||||
|
|
||||||
const parentElementStyle = window.getComputedStyle(instance.dom);
|
|
||||||
const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
|
|
||||||
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
|
|
||||||
const elementStyle = window.getComputedStyle(term.element);
|
|
||||||
const elementPadding = {
|
|
||||||
top: parseInt(elementStyle.getPropertyValue('padding-top')),
|
|
||||||
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
|
|
||||||
right: parseInt(elementStyle.getPropertyValue('padding-right')),
|
|
||||||
left: parseInt(elementStyle.getPropertyValue('padding-left'))
|
|
||||||
};
|
|
||||||
const elementPaddingVer = elementPadding.top + elementPadding.bottom;
|
|
||||||
const elementPaddingHor = elementPadding.right + elementPadding.left;
|
|
||||||
const availableHeight = parentElementHeight - elementPaddingVer;
|
|
||||||
const availableWidth = parentElementWidth - elementPaddingHor - scrollbarWidth;
|
|
||||||
const geometry = {
|
|
||||||
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / dims.css.cell.width)),
|
|
||||||
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / dims.css.cell.height))
|
|
||||||
};
|
|
||||||
term.resize(geometry.cols, geometry.rows + (rowoffset||0) );
|
|
||||||
}
|
|
||||||
|
|
@ -47,7 +47,8 @@ AFRAME.registerComponent('selfcontainer', {
|
||||||
cb(res)
|
cb(res)
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
if( request.url.match(/(^file:\/\/xrsh)/) ) return cb(response)
|
// never cache requests to filesystem
|
||||||
|
if( request.url.match(/(^\/mnt\/)/) ) return cb(response)
|
||||||
|
|
||||||
console.log("selfcontainer.js: caching "+request.url)
|
console.log("selfcontainer.js: caching "+request.url)
|
||||||
if( response.text ){
|
if( response.text ){
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue