mirror of
https://github.com/9001/copyparty.git
synced 2025-08-18 09:22:31 -06:00
fileman ui
This commit is contained in:
parent
fec0c620d4
commit
27cce086c6
11
README.md
11
README.md
|
@ -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
|
||||||
|
|
||||||

|

|
||||||
|
|
|
@ -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"""
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 <small>(then paste somewhere else)</small>$NHotkey: ctrl-shift-X">✂<span>cut</span></a><a' +
|
' href="#" id="fcut" tt="cut selected items <small>(then paste somewhere else)</small>$NHotkey: ctrl-X">✂<span>cut</span></a><a' +
|
||||||
' href="#" id="fcpy" tt="copy selected items <small>(then paste somewhere else)</small>$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
|
|
||||||
};
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue