stable copy-paste
This commit is contained in:
parent
de8883673c
commit
c5ed500a93
|
@ -110,6 +110,7 @@ AFRAME.registerComponent('codemirror', {
|
||||||
// as it would require all kindof ugly stringescaping
|
// as it would require all kindof ugly stringescaping
|
||||||
console.log("updating "+file)
|
console.log("updating "+file)
|
||||||
await this.isoterminal.worker.update_file(file, this.isoterminal.convert.toUint8Array(str) )
|
await this.isoterminal.worker.update_file(file, this.isoterminal.convert.toUint8Array(str) )
|
||||||
|
this.isoterminal.exec("touch "+file) // *FIXME* notify filesystem (why does inotifyd need this? v86's 9pfees is cached?)
|
||||||
},
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
|
@ -55,6 +55,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
this.calculateDimension()
|
this.calculateDimension()
|
||||||
this.initHud()
|
this.initHud()
|
||||||
this.setupBox()
|
this.setupBox()
|
||||||
|
this.setupPasteDrop()
|
||||||
|
|
||||||
fetch(this.data.iso,{method: 'HEAD'})
|
fetch(this.data.iso,{method: 'HEAD'})
|
||||||
.then( (res) => {
|
.then( (res) => {
|
||||||
|
@ -71,10 +72,11 @@ if( typeof AFRAME != 'undefined '){
|
||||||
requires:{
|
requires:{
|
||||||
com: "com/dom.js",
|
com: "com/dom.js",
|
||||||
window: "com/window.js",
|
window: "com/window.js",
|
||||||
|
pastedrop: "com/pastedrop.js",
|
||||||
v86: "com/isoterminal/libv86.js",
|
v86: "com/isoterminal/libv86.js",
|
||||||
vt100: "com/isoterminal/VT100.js",
|
vt100: "com/isoterminal/VT100.js",
|
||||||
// allow xrsh to selfcontain scene + itself
|
// allow xrsh to selfcontain scene + itself
|
||||||
xhook: "https://jpillora.com/xhook/dist/xhook.min.js",
|
xhook: "com/lib/xhook.min.js",
|
||||||
selfcontain: "com/selfcontainer.js",
|
selfcontain: "com/selfcontainer.js",
|
||||||
// html to texture
|
// html to texture
|
||||||
htmlinxr: "com/html-as-texture-in-xr.js",
|
htmlinxr: "com/html-as-texture-in-xr.js",
|
||||||
|
@ -202,6 +204,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
indexhtml: "com/isoterminal/feat/index.html.js",
|
indexhtml: "com/isoterminal/feat/index.html.js",
|
||||||
indexjs: "com/isoterminal/feat/index.js.js",
|
indexjs: "com/isoterminal/feat/index.js.js",
|
||||||
autorestore: "com/isoterminal/feat/autorestore.js",
|
autorestore: "com/isoterminal/feat/autorestore.js",
|
||||||
|
pastedropFeat: "com/isoterminal/feat/pastedrop.js",
|
||||||
})
|
})
|
||||||
|
|
||||||
this.el.setAttribute("selfcontainer","")
|
this.el.setAttribute("selfcontainer","")
|
||||||
|
@ -245,6 +248,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
})
|
})
|
||||||
|
|
||||||
instance.setAttribute("dom", "")
|
instance.setAttribute("dom", "")
|
||||||
|
instance.setAttribute("pastedrop", "")
|
||||||
|
|
||||||
this.term.addEventListener('ready', (e) => {
|
this.term.addEventListener('ready', (e) => {
|
||||||
instance.dom.classList.remove('blink')
|
instance.dom.classList.remove('blink')
|
||||||
|
@ -302,7 +306,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
|
|
||||||
setupVT100: function(instance){
|
setupVT100: function(instance){
|
||||||
const el = this.el.dom.querySelector('#term')
|
const el = this.el.dom.querySelector('#term')
|
||||||
const opts = {
|
this.term.opts.vt100 = {
|
||||||
cols: this.cols,
|
cols: this.cols,
|
||||||
rows: this.rows,
|
rows: this.rows,
|
||||||
el_or_id: el,
|
el_or_id: el,
|
||||||
|
@ -311,7 +315,8 @@ if( typeof AFRAME != 'undefined '){
|
||||||
rainbow: [VT100.COLOR_MAGENTA, VT100.COLOR_CYAN ],
|
rainbow: [VT100.COLOR_MAGENTA, VT100.COLOR_CYAN ],
|
||||||
xr: AFRAME.scenes[0].renderer.xr
|
xr: AFRAME.scenes[0].renderer.xr
|
||||||
}
|
}
|
||||||
this.vt100 = new VT100( opts )
|
this.term.emit('initVT100',this)
|
||||||
|
this.vt100 = new VT100( this.term.opts.vt100 )
|
||||||
this.vt100.el = el
|
this.vt100.el = el
|
||||||
this.vt100.curs_set( 1, true)
|
this.vt100.curs_set( 1, true)
|
||||||
this.vt100.focus()
|
this.vt100.focus()
|
||||||
|
@ -328,6 +333,24 @@ if( typeof AFRAME != 'undefined '){
|
||||||
this.el.addEventListener('serial-output-string', (e) => {
|
this.el.addEventListener('serial-output-string', (e) => {
|
||||||
this.vt100.write(e.detail)
|
this.vt100.write(e.detail)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 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 });
|
||||||
|
})
|
||||||
|
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
setupPasteDrop: function(){
|
||||||
|
this.el.addEventListener('pasteFile', (e) => {
|
||||||
|
e.preventDefault() // prevent bubbling up to window (which is triggering this initially)
|
||||||
|
if( !this.term.pasteFile ) return // skip if feat/pastedrop.js is not loaded
|
||||||
|
this.term.pasteFile(e.detail)
|
||||||
|
})
|
||||||
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setupBox: function(){
|
setupBox: function(){
|
||||||
|
|
|
@ -17,14 +17,14 @@ function ISOTerminal(instance,opts){
|
||||||
ISOTerminal.prototype.emit = function(event,data,sender){
|
ISOTerminal.prototype.emit = function(event,data,sender){
|
||||||
data = data || false
|
data = data || false
|
||||||
const evObj = new CustomEvent(event, {detail: data} )
|
const evObj = new CustomEvent(event, {detail: data} )
|
||||||
//this.preventFrameDrop( () => {
|
this.preventFrameDrop( () => {
|
||||||
// forward event to worker/instance/AFRAME element or component-function
|
// 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 feels complex, but actually keeps event- and function-names more concise in codebase
|
||||||
this.dispatchEvent( evObj )
|
this.dispatchEvent( evObj )
|
||||||
if( sender != "instance" && this.instance ) this.instance.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 != "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( sender !== undefined && typeof this[event] == 'function' ) this[event].apply(this, data && data.push ? data : [data] )
|
||||||
//})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ISOTerminal.addEventListener = (event,cb) => {
|
ISOTerminal.addEventListener = (event,cb) => {
|
||||||
|
@ -37,6 +37,10 @@ ISOTerminal.prototype.exec = function(shellscript){
|
||||||
this.send(shellscript+"\n",1)
|
this.send(shellscript+"\n",1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISOTerminal.prototype.hook = function(hookname,args){
|
||||||
|
this.exec(`{ type hook || source /etc/profile.sh; }; hook ${hookname} "${args.join('" "')}"`)
|
||||||
|
}
|
||||||
|
|
||||||
ISOTerminal.prototype.serial_input = 0; // can be set to 0,1,2,3 to define stdinput tty (xterm plugin)
|
ISOTerminal.prototype.serial_input = 0; // can be set to 0,1,2,3 to define stdinput tty (xterm plugin)
|
||||||
|
|
||||||
ISOTerminal.prototype.send = function(str, ttyNr){
|
ISOTerminal.prototype.send = function(str, ttyNr){
|
||||||
|
@ -48,7 +52,9 @@ ISOTerminal.prototype.send = function(str, ttyNr){
|
||||||
}else{
|
}else{
|
||||||
this.convert.toUint8Array( str ).map( (c) => {
|
this.convert.toUint8Array( str ).map( (c) => {
|
||||||
this.preventFrameDrop(
|
this.preventFrameDrop(
|
||||||
() => this.worker.postMessage({event:`serial${ttyNr}-input`,data:c})
|
() => {
|
||||||
|
this.worker.postMessage({event:`serial${ttyNr}-input`,data:c})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -187,7 +193,6 @@ ISOTerminal.prototype.startVM = function(opts){
|
||||||
"Learned helplessness fades when we realize tech isn’t too complex to understand",
|
"Learned helplessness fades when we realize tech isn’t too complex to understand",
|
||||||
"FOSS empowers users to customize and improve their tools",
|
"FOSS empowers users to customize and improve their tools",
|
||||||
"Engaging with FOSS helps build confidence and self-reliance in tech",
|
"Engaging with FOSS helps build confidence and self-reliance in tech",
|
||||||
"FOSS tools are accessible and often better than closed alternatives",
|
|
||||||
"FOSS shows that anyone can shape the digital world with curiosity and effort",
|
"FOSS shows that anyone can shape the digital world with curiosity and effort",
|
||||||
"Linux can revive old computers, extending their life and reducing e-waste",
|
"Linux can revive old computers, extending their life and reducing e-waste",
|
||||||
"Many lightweight Linux distributions run smoothly on older hardware",
|
"Many lightweight Linux distributions run smoothly on older hardware",
|
||||||
|
|
|
@ -121,7 +121,7 @@ function VT100(opts)
|
||||||
}
|
}
|
||||||
this.scr_ = scr;
|
this.scr_ = scr;
|
||||||
this.scr_.style.display = 'inline'
|
this.scr_.style.display = 'inline'
|
||||||
this.setupTouchInputFallback() // smartphone
|
this.setupTouchInputFallback() // smartphone/android
|
||||||
this.cursor_vis_ = true;
|
this.cursor_vis_ = true;
|
||||||
this.cursor_key_mode_ = VT100.CK_CURSOR;
|
this.cursor_key_mode_ = VT100.CK_CURSOR;
|
||||||
this.grab_events_ = false;
|
this.grab_events_ = false;
|
||||||
|
@ -218,7 +218,7 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
|
||||||
ch = '\n';
|
ch = '\n';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (event.key) {
|
switch (event.code) {
|
||||||
case "Backspace":
|
case "Backspace":
|
||||||
ch = '\b';
|
ch = '\b';
|
||||||
break;
|
break;
|
||||||
|
@ -267,13 +267,17 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Stop the event from doing anything else.
|
|
||||||
event.preventDefault();
|
// Workaround: top the event from doing anything else.
|
||||||
|
// (prevent input from adding characters instead of via VM)
|
||||||
|
event.preventDefault()
|
||||||
vt.key_buf_.push(ch);
|
vt.key_buf_.push(ch);
|
||||||
|
|
||||||
if( cb ){
|
if( cb ){
|
||||||
cb(vt.key_buf_)
|
cb(vt.key_buf_)
|
||||||
vt.key_buf_ = []
|
vt.key_buf_ = []
|
||||||
}else setTimeout(VT100.go_getch_, 0);
|
}else setTimeout(VT100.go_getch_, 0);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,6 +291,7 @@ VT100.handle_onkeydown_ = function VT100_handle_onkeydown()
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
event.preventDefault()
|
||||||
vt.key_buf_.push(ch);
|
vt.key_buf_.push(ch);
|
||||||
setTimeout(VT100.go_getch_, 0);
|
setTimeout(VT100.go_getch_, 0);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1079,7 +1084,7 @@ VT100.prototype.write = function VT100_write(stuff)
|
||||||
x = this.csi_parms_[j];
|
x = this.csi_parms_[j];
|
||||||
if( x > 89 && x < 98 && this.opts.rainbow ){
|
if( x > 89 && x < 98 && this.opts.rainbow ){
|
||||||
const rainbow = this.opts.rainbow
|
const rainbow = this.opts.rainbow
|
||||||
this.fgset( rainbow[ Math.floor( Math.random() * 1000 ) % rainbow.length ] )
|
this.fgset( rainbow[ x % rainbow.length ] )
|
||||||
}
|
}
|
||||||
switch (x) {
|
switch (x) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -1325,18 +1330,28 @@ VT100.prototype.throttleSmart = function throttleSmart(fn, wait) {
|
||||||
|
|
||||||
VT100.prototype.setupTouchInputFallback = function(){
|
VT100.prototype.setupTouchInputFallback = function(){
|
||||||
if( !this.input ){
|
if( !this.input ){
|
||||||
|
this.upload = document.createElement("input")
|
||||||
|
this.upload.setAttribute("type", "file")
|
||||||
|
this.upload.style.opacity = '0'
|
||||||
|
this.upload.style.position = 'absolute'
|
||||||
|
this.upload.style.left = '-9999px'
|
||||||
|
|
||||||
this.input = document.createElement("input")
|
this.input = document.createElement("input")
|
||||||
|
this.input.setAttribute("type", "text")
|
||||||
this.input.setAttribute("cols", this.opts.cols )
|
this.input.setAttribute("cols", this.opts.cols )
|
||||||
this.input.setAttribute("rows", this.opts.rows )
|
this.input.setAttribute("rows", this.opts.rows )
|
||||||
this.input.style.opacity = '0'
|
this.input.style.opacity = '0'
|
||||||
this.input.style.position = 'absolute'
|
this.input.style.position = 'absolute'
|
||||||
this.input.style.left = '-9999px'
|
this.input.style.left = '-9999px'
|
||||||
|
|
||||||
this.form = document.createElement("form")
|
this.form = document.createElement("form")
|
||||||
this.form.addEventListener("submit", (e) => {
|
this.form.addEventListener("submit", (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.key_buf_.push('\n')
|
this.key_buf_.push('\n')
|
||||||
setTimeout(VT100.go_getch_, 0);
|
setTimeout(VT100.go_getch_, 0);
|
||||||
|
return false
|
||||||
})
|
})
|
||||||
|
this.form.appendChild(this.upload)
|
||||||
this.form.appendChild(this.input)
|
this.form.appendChild(this.input)
|
||||||
this.scr_.parentElement.appendChild(this.form)
|
this.scr_.parentElement.appendChild(this.form)
|
||||||
|
|
||||||
|
@ -1353,8 +1368,8 @@ VT100.prototype.setupTouchInputFallback = function(){
|
||||||
|
|
||||||
this.input.handler = (e) => {
|
this.input.handler = (e) => {
|
||||||
let ch
|
let ch
|
||||||
let isEnter = String(e?.code).toLowerCase() == "enter" || e?.code == 13
|
let isEnter = String(e?.key).toLowerCase() == "enter" || e?.code == 13
|
||||||
let isBackspace = String(e?.code).toLowerCase() == "backspace" || e?.code == 8
|
let isBackspace = String(e?.key).toLowerCase() == "backspace" || e?.code == 8
|
||||||
if( isEnter ){
|
if( isEnter ){
|
||||||
ch = '\n'
|
ch = '\n'
|
||||||
}else if( isBackspace ){
|
}else if( isBackspace ){
|
||||||
|
@ -1372,6 +1387,7 @@ VT100.prototype.setupTouchInputFallback = function(){
|
||||||
|
|
||||||
this.scr_.addEventListener('touchend', (e) => this.focus() )
|
this.scr_.addEventListener('touchend', (e) => this.focus() )
|
||||||
this.scr_.addEventListener('click', (e) => this.focus() )
|
this.scr_.addEventListener('click', (e) => this.focus() )
|
||||||
|
|
||||||
}
|
}
|
||||||
this.useFallbackInput = true
|
this.useFallbackInput = true
|
||||||
this.focus()
|
this.focus()
|
||||||
|
@ -1381,7 +1397,6 @@ VT100.prototype.focus = function(){
|
||||||
setTimeout( () => {
|
setTimeout( () => {
|
||||||
const el = this[ this.useFallbackInput ? 'input' : 'scr_' ]
|
const el = this[ this.useFallbackInput ? 'input' : 'scr_' ]
|
||||||
el.focus()
|
el.focus()
|
||||||
console.dir(el)
|
|
||||||
}, 10 )
|
}, 10 )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ emulator.fs9p.update_file = async function(file,data){
|
||||||
inode.size = buf.length
|
inode.size = buf.length
|
||||||
const now = Math.round(Date.now() / 1000);
|
const now = Math.round(Date.now() / 1000);
|
||||||
inode.atime = inode.mtime = now;
|
inode.atime = inode.mtime = now;
|
||||||
me.postMessage({event:'exec',data:[`touch /mnt/${file}`]}) // update inode
|
|
||||||
return new Promise( (resolve,reject) => resolve(buf) )
|
return new Promise( (resolve,reject) => resolve(buf) )
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error({file,data})
|
console.error({file,data})
|
||||||
|
|
|
@ -14,7 +14,7 @@ ISOTerminal.prototype.boot = async function(e){
|
||||||
env.push( 'export '+String(i).toUpperCase()+'="'+decodeURIComponent( document.location[i]+'"') )
|
env.push( 'export '+String(i).toUpperCase()+'="'+decodeURIComponent( document.location[i]+'"') )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.emit("emulator.create_file", ["profile.browser", this.convert.toUint8Array( env.join('\n') ) ] )
|
this.worker.create_file("profile.browser", this.convert.toUint8Array( env.join('\n') ) )
|
||||||
|
|
||||||
if( this.serial_input == 0 ){
|
if( this.serial_input == 0 ){
|
||||||
if( !this.noboot ){
|
if( !this.noboot ){
|
||||||
|
|
|
@ -6,7 +6,7 @@ if( typeof emulator != 'undefined' ){
|
||||||
const convert = ISOTerminal.prototype.convert
|
const convert = ISOTerminal.prototype.convert
|
||||||
const buf = await this.emulator.read_file("dev/browser/js")
|
const buf = await this.emulator.read_file("dev/browser/js")
|
||||||
const script = convert.Uint8ArrayToString(buf)
|
const script = convert.Uint8ArrayToString(buf)
|
||||||
let PID="?"
|
let PID=null
|
||||||
try{
|
try{
|
||||||
if( script.match(/^PID/) ){
|
if( script.match(/^PID/) ){
|
||||||
PID = script.match(/^PID=([0-9]+);/)[1]
|
PID = script.match(/^PID=([0-9]+);/)[1]
|
||||||
|
@ -35,7 +35,9 @@ if( typeof emulator != 'undefined' ){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// update output to 9p with PID as filename (in /mnt/run)
|
// update output to 9p with PID as filename (in /mnt/run)
|
||||||
this.emit('fs9p.update_file', [`run/${PID}`, this.convert.toUint8Array(res)] )
|
if( PID ){
|
||||||
|
this.worker.update_file(`run/${PID}`, this.convert.toUint8Array(res) )
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
if( typeof emulator != 'undefined' ){
|
||||||
|
// inside worker-thread
|
||||||
|
|
||||||
|
}else{
|
||||||
|
// inside browser-thread
|
||||||
|
//
|
||||||
|
ISOTerminal.prototype.pasteWriteFile = async function(data,type,filename){
|
||||||
|
this.pasteWriteFile.fileCount = this.pasteWriteFile.fileCount || 0
|
||||||
|
const file = `clipboard/`+ ( filename || `user-paste-${this.pasteWriteFile.fileCount}`)
|
||||||
|
await this.worker.create_file(file, data )
|
||||||
|
// run the xrsh hook
|
||||||
|
this.hook("clipboard", [ `/mnt/${file}`, type ] )
|
||||||
|
console.log("clipboard paste: /mnt/"+file)
|
||||||
|
this.pasteWriteFile.fileCount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ISOTerminal.prototype.pasteFile = async function(data){
|
||||||
|
const {type,item,pastedText} = data
|
||||||
|
if( pastedText){
|
||||||
|
this.pasteWriteFile( this.convert.toUint8Array(pastedText) ,type)
|
||||||
|
}else{
|
||||||
|
const file = item.getAsFile();
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
const arr = new Uint8Array(e.target.result)
|
||||||
|
this.pasteWriteFile( arr, type, file.name ); // or use readAsDataURL for images
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
179
com/paste.js
179
com/paste.js
|
@ -1,179 +0,0 @@
|
||||||
AFRAME.registerComponent('paste', {
|
|
||||||
schema: {
|
|
||||||
foo: { type:"string"}
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
|
||||||
this.el.object3D.visible = false
|
|
||||||
//this.el.innerHTML = ` `
|
|
||||||
},
|
|
||||||
|
|
||||||
requires:{
|
|
||||||
osbutton: "com/osbutton.js"
|
|
||||||
},
|
|
||||||
|
|
||||||
events:{
|
|
||||||
|
|
||||||
// component events
|
|
||||||
somecomponent: function( ){ console.log("component requirement mounted") },
|
|
||||||
ready: function(e){ console.log("requires are loaded") },
|
|
||||||
|
|
||||||
launcher: function(e){
|
|
||||||
const paste = () => {
|
|
||||||
navigator.clipboard.readText()
|
|
||||||
.then( (base64) => {
|
|
||||||
let mimetype = base64.replace(/;base64,.*/,'')
|
|
||||||
let data = base64.replace(/.*;base64,/,'')
|
|
||||||
let type = this.textHeuristic(data)
|
|
||||||
console.log("type="+type)
|
|
||||||
switch( this.textHeuristic(data) ){
|
|
||||||
case "aframe": this.insertAFRAME(data); break;
|
|
||||||
default: this.insertText(data); break;
|
|
||||||
}
|
|
||||||
this.count += 1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
navigator.permissions.query({ name: 'clipboard-read' })
|
|
||||||
.then( (permission) => {
|
|
||||||
if( permission.state != 'granted' ){
|
|
||||||
this.el.sceneEl.exitVR()
|
|
||||||
setTimeout( () => paste(), 500 )
|
|
||||||
return
|
|
||||||
}else paste()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
textHeuristic: function(text){
|
|
||||||
// Script type identification clues
|
|
||||||
const bashClues = ["|", "if ", "fi", "cat"];
|
|
||||||
const htmlClues = ["/>", "href=", "src="];
|
|
||||||
const aframeClues = ["<a-entity", "/>", "position="];
|
|
||||||
const jsClues = ["var ", "let ", "function ", "setTimeout","console."];
|
|
||||||
// Count occurrences of clues for each script type
|
|
||||||
const bashCount = bashClues.reduce((acc, clue) => acc + (text.includes(clue) ? 1 : 0), 0);
|
|
||||||
const htmlCount = htmlClues.reduce((acc, clue) => acc + (text.includes(clue) ? 1 : 0), 0);
|
|
||||||
const aframeCount = aframeClues.reduce((acc, clue) => acc + (text.includes(clue) ? 1 : 0), 0);
|
|
||||||
const jsCount = jsClues.reduce((acc, clue) => acc + (text.includes(clue) ? 1 : 0), 0);
|
|
||||||
|
|
||||||
// Identify the script with the most clues or return unknown if inconclusive
|
|
||||||
const maxCount = Math.max(bashCount, htmlCount, jsCount, aframeCount);
|
|
||||||
if (maxCount === 0) {
|
|
||||||
return "unknown";
|
|
||||||
} else if (bashCount === maxCount) {
|
|
||||||
return "bash";
|
|
||||||
} else if (htmlCount === maxCount) {
|
|
||||||
return "html";
|
|
||||||
} else if (jsCount === maxCount) {
|
|
||||||
return "javascript";
|
|
||||||
} else {
|
|
||||||
return "aframe";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
insertAFRAME: function(data){
|
|
||||||
let scene = document.createElement('a-entity')
|
|
||||||
scene.id = "embedAframe"
|
|
||||||
scene.innerHTML = data
|
|
||||||
let el = document.createElement('a-text')
|
|
||||||
el.setAttribute("value",data)
|
|
||||||
el.setAttribute("color","white")
|
|
||||||
el.setAttribute("align","center")
|
|
||||||
el.setAttribute("anchor","align")
|
|
||||||
let osbutton = this.wrapOSButton(el,"aframe",data)
|
|
||||||
AFRAME.scenes[0].appendChild(osbutton)
|
|
||||||
console.log(data)
|
|
||||||
},
|
|
||||||
|
|
||||||
insertText: function(data){
|
|
||||||
let el = document.createElement('a-text')
|
|
||||||
el.setAttribute("value",data)
|
|
||||||
el.setAttribute("color","white")
|
|
||||||
el.setAttribute("align","center")
|
|
||||||
el.setAttribute("anchor","align")
|
|
||||||
let osbutton = this.wrapOSButton(el,"text",data)
|
|
||||||
AFRAME.scenes[0].appendChild(osbutton)
|
|
||||||
console.log(data)
|
|
||||||
},
|
|
||||||
|
|
||||||
wrapOSButton: function(el,type,data){
|
|
||||||
let osbutton = document.createElement('a-entity')
|
|
||||||
let height = type == 'aframe' ? 0.3 : 0.1
|
|
||||||
let depth = type == 'aframe' ? 0.3 : 0.05
|
|
||||||
osbutton.setAttribute("osbutton",`width:0.3; height: ${height}; depth: ${depth}; color:blue `)
|
|
||||||
osbutton.appendChild(el)
|
|
||||||
osbutton.object3D.position.copy( this.getPositionInFrontOfCamera() )
|
|
||||||
return osbutton
|
|
||||||
},
|
|
||||||
|
|
||||||
getPositionInFrontOfCamera: function(distance){
|
|
||||||
const camera = this.el.sceneEl.camera;
|
|
||||||
let pos = new THREE.Vector3()
|
|
||||||
let direction = new THREE.Vector3();
|
|
||||||
// Get camera's forward direction (without rotation)
|
|
||||||
camera.getWorldDirection(direction);
|
|
||||||
camera.getWorldPosition(pos)
|
|
||||||
direction.normalize();
|
|
||||||
// Scale the direction by 1 meter
|
|
||||||
if( !distance ) distance = 1.5
|
|
||||||
direction.multiplyScalar(distance);
|
|
||||||
// Add the camera's position to the scaled direction to get the target point
|
|
||||||
pos.add(direction);
|
|
||||||
return pos
|
|
||||||
},
|
|
||||||
|
|
||||||
manifest: { // HTML5 manifest to identify app to xrsh
|
|
||||||
"short_name": "Paste",
|
|
||||||
"name": "Paste",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "https://css.gg/clipboard.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": "Paste the clipboard",
|
|
||||||
"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,115 @@
|
||||||
|
AFRAME.registerComponent('pastedrop', {
|
||||||
|
schema: {
|
||||||
|
foo: { type:"string"}
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
|
||||||
|
window.addEventListener('paste', this.onPaste.bind(this) )
|
||||||
|
|
||||||
|
document.body.addEventListener('dragover',(e) => e.preventDefault() )
|
||||||
|
document.body.addEventListener('drop', this.onDrop.bind(this) )
|
||||||
|
},
|
||||||
|
|
||||||
|
initClipboard: function(){
|
||||||
|
navigator.permissions.query({ name: 'clipboard-read' })
|
||||||
|
.then( (permission) => {
|
||||||
|
if( permission.state != 'granted' ){
|
||||||
|
this.el.sceneEl.exitVR()
|
||||||
|
setTimeout( () => this.paste(), 500 )
|
||||||
|
return
|
||||||
|
}else this.paste()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
//getClipboard: function(){
|
||||||
|
// navigator.clipboard.readText()
|
||||||
|
// .then( async (base64) => {
|
||||||
|
// let mimetype = base64.replace(/;base64,.*/,'')
|
||||||
|
// let data = base64.replace(/.*;base64,/,'')
|
||||||
|
// let type = this.textHeuristic(data)
|
||||||
|
// const term = document.querySelector('[isoterminal]').components.isoterminal.term
|
||||||
|
// this.el.emit('pasteFile',{}) /*TODO* data incompatible */
|
||||||
|
// })
|
||||||
|
//},
|
||||||
|
|
||||||
|
onDrop: function(e){
|
||||||
|
e.preventDefault()
|
||||||
|
this.onPaste({...e, type: "paste", clipboardData: e.dataTransfer})
|
||||||
|
},
|
||||||
|
|
||||||
|
onPaste: function(e){
|
||||||
|
if( e.type != "paste" ) return
|
||||||
|
|
||||||
|
const clipboardData = e.clipboardData || navigator.clipboard;
|
||||||
|
const items = clipboardData.items;
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i];
|
||||||
|
const type = item.type;
|
||||||
|
|
||||||
|
// Check if the item is a file
|
||||||
|
if (item.kind === "file") {
|
||||||
|
this.el.emit('pasteFile',{item,type})
|
||||||
|
} else if (type === "text/plain") {
|
||||||
|
const pastedText = clipboardData.getData("text/plain");
|
||||||
|
const newType = "text" // let /root/hook.d/mimetype/text further decide whether this is text/plain (or something else)
|
||||||
|
this.el.emit('pasteFile',{item,type:newType,pastedText})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
"short_name": "Paste",
|
||||||
|
"name": "Paste",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "https://css.gg/clipboard.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": "Paste the clipboard",
|
||||||
|
"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.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue