fileman ui

This commit is contained in:
ed 2021-07-25 01:09:14 +02:00
parent fec0c620d4
commit 27cce086c6
4 changed files with 219 additions and 22 deletions

View file

@ -40,6 +40,7 @@ this is the readme for v0.12 (unreleased) which has a different expression for v
* [zip downloads](#zip-downloads) * [zip downloads](#zip-downloads)
* [uploading](#uploading) * [uploading](#uploading)
* [file-search](#file-search) * [file-search](#file-search)
* [file manager](#file-manager)
* [markdown viewer](#markdown-viewer) * [markdown viewer](#markdown-viewer)
* [other tricks](#other-tricks) * [other tricks](#other-tricks)
* [searching](#searching) * [searching](#searching)
@ -230,6 +231,9 @@ the browser has the following hotkeys (assumes qwerty, ignores actual layout)
* `M` parent folder (or unexpand current) * `M` parent folder (or unexpand current)
* `G` toggle list / grid view * `G` toggle list / grid view
* `T` toggle thumbnails / icons * `T` toggle thumbnails / icons
* `ctrl-X` cut selected files/folders
* `ctrl-V` paste
* `F2` rename selected file/folder
* when playing audio: * when playing audio:
* `J/L` prev/next song * `J/L` prev/next song
* `U/O` skip 10sec back/forward * `U/O` skip 10sec back/forward
@ -352,6 +356,13 @@ note that since up2k has to read the file twice, `[🎈 bup]` can be up to 2x fa
up2k has saved a few uploads from becoming corrupted in-transfer already; caught an android phone on wifi redhanded in wireshark with a bitflip, however bup with https would *probably* have noticed as well (thanks to tls also functioning as an integrity check) up2k has saved a few uploads from becoming corrupted in-transfer already; caught an android phone on wifi redhanded in wireshark with a bitflip, however bup with https would *probably* have noticed as well (thanks to tls also functioning as an integrity check)
## file manager
if you have the required permissions, you can cut/paste, rename, and delete files/folders
you can move files across browser tabs (cut in one tab, paste in another)
## markdown viewer ## markdown viewer
![copyparty-md-read-fs8](https://user-images.githubusercontent.com/241032/115978057-66419080-a57d-11eb-8539-d2be843991aa.png) ![copyparty-md-read-fs8](https://user-images.githubusercontent.com/241032/115978057-66419080-a57d-11eb-8539-d2be843991aa.png)

View file

@ -1357,14 +1357,21 @@ class Up2k(object):
self._forget_file(svn.realpath, srem, c1, w) self._forget_file(svn.realpath, srem, c1, w)
return "k" return "k"
# not found in dst vol; copy info # not found in dst db; copy info
self.log("mv: plain move") self.log("mv: plain move")
st = bos.stat(sabs)
if c1 and c2:
self._copy_tags(c1, c2, w) self._copy_tags(c1, c2, w)
if c1:
self._forget_file(svn.realpath, srem, c1, w) self._forget_file(svn.realpath, srem, c1, w)
st = bos.stat(sabs)
if c2:
self.db_add(c2, w, drd, dfn, st.st_mtime, st.st_size) self.db_add(c2, w, drd, dfn, st.st_mtime, st.st_size)
bos.rename(sabs, dabs)
return "k" bos.rename(sabs, dabs)
return "k"
def _copy_tags(self, csrc, cdst, wark): def _copy_tags(self, csrc, cdst, wark):
"""copy all tags for wark from src-db to dst-db""" """copy all tags for wark from src-db to dst-db"""

View file

@ -432,6 +432,27 @@ html.light #ggrid a.sel {
font-size: .6em; font-size: .6em;
display: block; display: block;
} }
#wfm a:not(.en) {
opacity: .3;
color: #f6c;
}
html.light #wfm a:not(.en) {
color: #c4a;
}
#files tbody td.c1 {
animation: fcut1 .5s ease-out;
}
#files tbody td.c2 {
animation: fcut2 .5s ease-out;
}
@keyframes fcut1 {
0% {opacity:0}
100% {opacity:1}
}
@keyframes fcut2 {
0% {opacity:0}
100% {opacity:1}
}
#wzip a { #wzip a {
font-size: .4em; font-size: .4em;
margin: -.3em .3em; margin: -.3em .3em;

View file

@ -31,10 +31,9 @@ ebi('widget').innerHTML = (
'<div id="wtoggle">' + '<div id="wtoggle">' +
'<span id="wfm"><a' + '<span id="wfm"><a' +
' href="#" id="fren" tt="rename selected item$NHotkey: F2">✎<span>name</span></a><a' + ' href="#" id="fren" tt="rename selected item$NHotkey: F2">✎<span>name</span></a><a' +
' href="#" id="fdel" tt="delete selected items">⌫<span>delete</span></a><a' + ' href="#" id="fdel" tt="delete selected items$NHotkey: ctrl-K">⌫<span>delete</span></a><a' +
' href="#" id="fcut" tt="cut selected items &lt;small&gt;(then paste somewhere else)&lt;/small&gt;$NHotkey: ctrl-shift-X">✂<span>cut</span></a><a' + ' href="#" id="fcut" tt="cut selected items &lt;small&gt;(then paste somewhere else)&lt;/small&gt;$NHotkey: ctrl-X">✂<span>cut</span></a><a' +
' href="#" id="fcpy" tt="copy selected items &lt;small&gt;(then paste somewhere else)&lt;/small&gt;$NHotkey: ctrl-shift-C">⧉<span>copy</span></a><a' + ' href="#" id="fpst" tt="paste a previously cut/copied selection$NHotkey: ctrl-V">📋<span>paste</span></a>' +
' href="#" id="fpst" tt="paste a previously cut/copied selection$NHotkey: ctrl-shift-V">📋<span>paste</span></a>' +
'</span><span id="wzip"><a' + '</span><span id="wzip"><a' +
' href="#" id="selall" tt="select all files">sel.<br />all</a><a' + ' href="#" id="selall" tt="select all files">sel.<br />all</a><a' +
' href="#" id="selinv" tt="invert selection">sel.<br />inv.</a><a' + ' href="#" id="selinv" tt="invert selection">sel.<br />inv.</a><a' +
@ -1465,6 +1464,132 @@ function play_linked() {
})(); })();
var fileman = (function () {
var bren = ebi('fren'),
bdel = ebi('fdel'),
bcut = ebi('fcut'),
bpst = ebi('fpst'),
r = {};
r.clip = null;
r.bus = new BroadcastChannel("fileman_bus");
r.render = function () {
if (r.clip === null)
r.clip = jread('fman_clip', []);
var sel = msel.getsel();
clmod(bren, 'en', sel.length == 1);
clmod(bdel, 'en', sel.length);
clmod(bcut, 'en', sel.length);
clmod(bpst, 'en', r.clip && r.clip.length);
bren.style.display = has(perms, 'write') && has(perms, 'move') ? '' : 'none';
bdel.style.display = has(perms, 'delete') ? '' : 'none';
bcut.style.display = has(perms, 'move') ? '' : 'none';
bpst.style.display = has(perms, 'write') ? '' : 'none';
bpst.setAttribute('tt', 'paste ' + r.clip.length + ' items$NHotkey: ctrl-V');
};
r.rename = function (e) {
ev(e);
var sel = msel.getsel();
if (sel.length !== 1)
return alert('select exactly 1 item to rename');
var fn = prompt('new filename:', sel[0]);
if (!fn || fn == sel[0])
return alert('aborted');
};
r.delete = function (e) {
ev(e);
var sel = msel.getsel(),
req = msel.selu;
if (!sel.length)
return alert('select at least 1 item to delete');
if (!confirm('===== DANGER =====\nDELETE these ' + req.length + ' items?\n\n' + req.join('\n')))
return;
if (!confirm('Last chance! Delete?'))
return;
};
r.cut = function (e) {
ev(e);
var sel = msel.getsel(),
vsel = msel.selu;
if (!sel.length)
return alert('select at least 1 item to cut');
var tds = QSA('#files tbody tr.sel td');
for (var a = 0; a < tds.length; a++) {
var cl = tds[a].classList, inv = cl.contains('c1');
cl.remove(inv ? 'c1' : 'c2');
cl.add(inv ? 'c2' : 'c1');
}
jwrite('fman_clip', vsel);
r.tx();
};
r.paste = function (e) {
ev(e);
if (!r.clip.length)
return alert('first cut some items to paste\n\nnote: you can cut/paste across different tabs');
var req = [],
exists = [],
indir = [],
links = QSA('#files tbody td:nth-child(2) a');
for (var a = 0, aa = links.length; a < aa; a++)
indir.push(links[a].getAttribute('name'));
for (var a = 0; a < r.clip.length; a++) {
var found = false;
for (var b = 0; b < indir.length; b++) {
if (r.clip[a].endsWith('/' + indir[b])) {
exists.push(r.clip[a]);
found = true;
}
}
if (!found)
req.push(r.clip[a]);
}
if (exists.length)
alert('these ' + exists.length + ' items cannot be pasted here (names already exist):\n\n' + exists.join('\n'));
if (!confirm('paste these ' + req.length + ' items here?\n\n' + req.join('\n')))
return;
jwrite('fman_clip', []);
r.tx();
};
r.bus.onmessage = function () {
console.log('fman onmsg');
r.clip = null;
r.render();
};
r.tx = function () {
r.bus.postMessage(1);
r.bus.onmessage();
};
bren.onclick = r.rename;
bdel.onclick = r.delete;
bcut.onclick = r.cut;
bpst.onclick = r.paste;
return r;
})();
var thegrid = (function () { var thegrid = (function () {
var lfiles = ebi('files'), var lfiles = ebi('files'),
gfiles = mknod('div'); gfiles = mknod('div');
@ -1794,7 +1919,7 @@ document.onkeydown = function (e) {
if (!document.activeElement || document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a') if (!document.activeElement || document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a')
return; return;
if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing) if (e.altKey || e.isComposing)
return; return;
if (QS('#bbox-overlay.visible')) if (QS('#bbox-overlay.visible'))
@ -1802,6 +1927,19 @@ document.onkeydown = function (e) {
var k = e.code + '', pos = -1, n; var k = e.code + '', pos = -1, n;
if (ctrl(e)) {
if (k == 'KeyX')
return fileman.cut();
if (k == 'KeyV')
return fileman.paste();
if (k == 'KeyK')
return fileman.delete();
return;
}
if (e.shiftKey && k != 'KeyA' && k != 'KeyD') if (e.shiftKey && k != 'KeyA' && k != 'KeyD')
return; return;
@ -1840,6 +1978,9 @@ document.onkeydown = function (e) {
if (k == 'KeyT') if (k == 'KeyT')
return ebi('thumbs').click(); return ebi('thumbs').click();
if (k == 'F2')
return fileman.rename();
if (!treectl.hidden && (!e.shiftKey || !thegrid.en)) { if (!treectl.hidden && (!e.shiftKey || !thegrid.en)) {
if (k == 'KeyA') if (k == 'KeyA')
return QS('#twig').click(); return QS('#twig').click();
@ -3026,18 +3167,35 @@ var arcfmt = (function () {
var msel = (function () { var msel = (function () {
function getsel() { var r = {};
var names = [], r.selu = null;
links = QSA('#files tbody tr.sel td:nth-child(2) a'); r.seln = null;
for (var a = 0, aa = links.length; a < aa; a++) r.getsel = function () {
names.push(links[a].getAttribute('href').replace(/\/$/, "").split('/').slice(-1)); if (r.seln !== null)
return r.seln;
return names; r.selu = [];
r.seln = [];
var links = QSA('#files tbody tr.sel td:nth-child(2) a'),
vbase = get_evpath();
for (var a = 0, aa = links.length; a < aa; a++) {
var url = links[a].getAttribute('href').replace(/\/$/, ""),
name = url.split('/').slice(-1);
r.selu.push(url.indexOf('/') !== -1 ? url : vbase + url);
r.seln.push(name);
links[a].setAttribute('name', name);
}
return r.seln;
} }
function selui() { function selui() {
clmod(ebi('wtoggle'), 'sel', getsel().length); r.selu = r.seln = null;
clmod(ebi('wtoggle'), 'sel', r.getsel().length);
thegrid.loadsel(); thegrid.loadsel();
fileman.render();
} }
function seltgl(e) { function seltgl(e) {
ev(e); ev(e);
@ -3060,7 +3218,7 @@ var msel = (function () {
}; };
ebi('selzip').onclick = function (e) { ebi('selzip').onclick = function (e) {
ev(e); ev(e);
var names = getsel(), var names = r.getsel(),
arg = ebi('selzip').getAttribute('fmt'), arg = ebi('selzip').getAttribute('fmt'),
txt = names.join('\n'), txt = names.join('\n'),
frm = mknod('form'); frm = mknod('form');
@ -3083,16 +3241,16 @@ var msel = (function () {
console.log(txt); console.log(txt);
frm.submit(); frm.submit();
}; };
function render() { r.render = function () {
var tds = QSA('#files tbody td+td+td'); var tds = QSA('#files tbody td+td+td');
for (var a = 0, aa = tds.length; a < aa; a++) { for (var a = 0, aa = tds.length; a < aa; a++) {
tds[a].onclick = seltgl; tds[a].onclick = seltgl;
} }
r.selu = r.seln = null;
arcfmt.render(); arcfmt.render();
fileman.render();
} }
return { return r;
"render": render
};
})(); })();