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
|
||||
console.log("updating "+file)
|
||||
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:{
|
||||
|
|
|
@ -55,6 +55,7 @@ if( typeof AFRAME != 'undefined '){
|
|||
this.calculateDimension()
|
||||
this.initHud()
|
||||
this.setupBox()
|
||||
this.setupPasteDrop()
|
||||
|
||||
fetch(this.data.iso,{method: 'HEAD'})
|
||||
.then( (res) => {
|
||||
|
@ -71,10 +72,11 @@ if( typeof AFRAME != 'undefined '){
|
|||
requires:{
|
||||
com: "com/dom.js",
|
||||
window: "com/window.js",
|
||||
pastedrop: "com/pastedrop.js",
|
||||
v86: "com/isoterminal/libv86.js",
|
||||
vt100: "com/isoterminal/VT100.js",
|
||||
// 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",
|
||||
// html to texture
|
||||
htmlinxr: "com/html-as-texture-in-xr.js",
|
||||
|
@ -202,6 +204,7 @@ if( typeof AFRAME != 'undefined '){
|
|||
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",
|
||||
})
|
||||
|
||||
this.el.setAttribute("selfcontainer","")
|
||||
|
@ -245,6 +248,7 @@ if( typeof AFRAME != 'undefined '){
|
|||
})
|
||||
|
||||
instance.setAttribute("dom", "")
|
||||
instance.setAttribute("pastedrop", "")
|
||||
|
||||
this.term.addEventListener('ready', (e) => {
|
||||
instance.dom.classList.remove('blink')
|
||||
|
@ -302,7 +306,7 @@ if( typeof AFRAME != 'undefined '){
|
|||
|
||||
setupVT100: function(instance){
|
||||
const el = this.el.dom.querySelector('#term')
|
||||
const opts = {
|
||||
this.term.opts.vt100 = {
|
||||
cols: this.cols,
|
||||
rows: this.rows,
|
||||
el_or_id: el,
|
||||
|
@ -311,7 +315,8 @@ if( typeof AFRAME != 'undefined '){
|
|||
rainbow: [VT100.COLOR_MAGENTA, VT100.COLOR_CYAN ],
|
||||
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.curs_set( 1, true)
|
||||
this.vt100.focus()
|
||||
|
@ -328,6 +333,24 @@ if( typeof AFRAME != 'undefined '){
|
|||
this.el.addEventListener('serial-output-string', (e) => {
|
||||
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(){
|
||||
|
|
|
@ -17,14 +17,14 @@ function ISOTerminal(instance,opts){
|
|||
ISOTerminal.prototype.emit = function(event,data,sender){
|
||||
data = data || false
|
||||
const evObj = new CustomEvent(event, {detail: data} )
|
||||
//this.preventFrameDrop( () => {
|
||||
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] )
|
||||
//})
|
||||
})
|
||||
}
|
||||
|
||||
ISOTerminal.addEventListener = (event,cb) => {
|
||||
|
@ -37,6 +37,10 @@ ISOTerminal.prototype.exec = function(shellscript){
|
|||
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.send = function(str, ttyNr){
|
||||
|
@ -48,7 +52,9 @@ ISOTerminal.prototype.send = function(str, ttyNr){
|
|||
}else{
|
||||
this.convert.toUint8Array( str ).map( (c) => {
|
||||
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",
|
||||
"FOSS empowers users to customize and improve their tools",
|
||||
"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",
|
||||
"Linux can revive old computers, extending their life and reducing e-waste",
|
||||
"Many lightweight Linux distributions run smoothly on older hardware",
|
||||
|
|
|
@ -121,7 +121,7 @@ function VT100(opts)
|
|||
}
|
||||
this.scr_ = scr;
|
||||
this.scr_.style.display = 'inline'
|
||||
this.setupTouchInputFallback() // smartphone
|
||||
this.setupTouchInputFallback() // smartphone/android
|
||||
this.cursor_vis_ = true;
|
||||
this.cursor_key_mode_ = VT100.CK_CURSOR;
|
||||
this.grab_events_ = false;
|
||||
|
@ -218,7 +218,7 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
|
|||
ch = '\n';
|
||||
}
|
||||
} else {
|
||||
switch (event.key) {
|
||||
switch (event.code) {
|
||||
case "Backspace":
|
||||
ch = '\b';
|
||||
break;
|
||||
|
@ -267,13 +267,17 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
|
|||
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);
|
||||
|
||||
if( cb ){
|
||||
cb(vt.key_buf_)
|
||||
vt.key_buf_ = []
|
||||
}else setTimeout(VT100.go_getch_, 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -287,6 +291,7 @@ VT100.handle_onkeydown_ = function VT100_handle_onkeydown()
|
|||
default:
|
||||
return true;
|
||||
}
|
||||
event.preventDefault()
|
||||
vt.key_buf_.push(ch);
|
||||
setTimeout(VT100.go_getch_, 0);
|
||||
return false;
|
||||
|
@ -1079,7 +1084,7 @@ VT100.prototype.write = function VT100_write(stuff)
|
|||
x = this.csi_parms_[j];
|
||||
if( x > 89 && x < 98 && 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) {
|
||||
case 0:
|
||||
|
@ -1325,18 +1330,28 @@ VT100.prototype.throttleSmart = function throttleSmart(fn, wait) {
|
|||
|
||||
VT100.prototype.setupTouchInputFallback = function(){
|
||||
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.setAttribute("type", "text")
|
||||
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 = document.createElement("form")
|
||||
this.form.addEventListener("submit", (e) => {
|
||||
e.preventDefault()
|
||||
this.key_buf_.push('\n')
|
||||
setTimeout(VT100.go_getch_, 0);
|
||||
return false
|
||||
})
|
||||
this.form.appendChild(this.upload)
|
||||
this.form.appendChild(this.input)
|
||||
this.scr_.parentElement.appendChild(this.form)
|
||||
|
||||
|
@ -1353,8 +1368,8 @@ VT100.prototype.setupTouchInputFallback = function(){
|
|||
|
||||
this.input.handler = (e) => {
|
||||
let ch
|
||||
let isEnter = String(e?.code).toLowerCase() == "enter" || e?.code == 13
|
||||
let isBackspace = String(e?.code).toLowerCase() == "backspace" || e?.code == 8
|
||||
let isEnter = String(e?.key).toLowerCase() == "enter" || e?.code == 13
|
||||
let isBackspace = String(e?.key).toLowerCase() == "backspace" || e?.code == 8
|
||||
if( isEnter ){
|
||||
ch = '\n'
|
||||
}else if( isBackspace ){
|
||||
|
@ -1372,6 +1387,7 @@ VT100.prototype.setupTouchInputFallback = function(){
|
|||
|
||||
this.scr_.addEventListener('touchend', (e) => this.focus() )
|
||||
this.scr_.addEventListener('click', (e) => this.focus() )
|
||||
|
||||
}
|
||||
this.useFallbackInput = true
|
||||
this.focus()
|
||||
|
@ -1381,7 +1397,6 @@ VT100.prototype.focus = function(){
|
|||
setTimeout( () => {
|
||||
const el = this[ this.useFallbackInput ? 'input' : 'scr_' ]
|
||||
el.focus()
|
||||
console.dir(el)
|
||||
}, 10 )
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ emulator.fs9p.update_file = async function(file,data){
|
|||
inode.size = buf.length
|
||||
const now = Math.round(Date.now() / 1000);
|
||||
inode.atime = inode.mtime = now;
|
||||
me.postMessage({event:'exec',data:[`touch /mnt/${file}`]}) // update inode
|
||||
return new Promise( (resolve,reject) => resolve(buf) )
|
||||
}catch(e){
|
||||
console.error({file,data})
|
||||
|
|
|
@ -14,7 +14,7 @@ ISOTerminal.prototype.boot = async function(e){
|
|||
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.noboot ){
|
||||
|
|
|
@ -6,7 +6,7 @@ if( typeof emulator != 'undefined' ){
|
|||
const convert = ISOTerminal.prototype.convert
|
||||
const buf = await this.emulator.read_file("dev/browser/js")
|
||||
const script = convert.Uint8ArrayToString(buf)
|
||||
let PID="?"
|
||||
let PID=null
|
||||
try{
|
||||
if( script.match(/^PID/) ){
|
||||
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)
|
||||
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