diff --git a/copyparty/__main__.py b/copyparty/__main__.py
index c44ac711..2e91822e 100644
--- a/copyparty/__main__.py
+++ b/copyparty/__main__.py
@@ -447,6 +447,7 @@ def run_argparse(argv, formatter):
ap2 = ap.add_argument_group('ui options')
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
+ ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
ap2 = ap.add_argument_group('debug options')
ap2.add_argument("--no-sendfile", action="store_true", help="disable sendfile")
diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py
index cb3aaf53..0a3bc2d2 100644
--- a/copyparty/httpcli.py
+++ b/copyparty/httpcli.py
@@ -2268,6 +2268,19 @@ class HttpCli(object):
ls_ret["taglist"] = taglist
return self.tx_ls(ls_ret)
+ doc = self.uparam.get("doc") if self.can_read else None
+ if doc:
+ doc = unquotep(doc.replace("+", " "))
+ j2a["docname"] = doc
+ if next((x for x in files if x["name"] == doc), None):
+ with open(os.path.join(abspath, doc), "rb") as f:
+ doc = f.read().decode("utf-8", "replace")
+ else:
+ self.log("doc 404: [{}]".format(doc), c=6)
+ doc = "( textfile not found )"
+
+ j2a["doc"] = doc
+
for d in dirs:
d["name"] += "/"
@@ -2276,6 +2289,7 @@ class HttpCli(object):
j2a["files"] = dirs + files
j2a["logues"] = logues
j2a["taglist"] = taglist
+ j2a["txt_ext"] = self.args.textfiles.replace(',', ' ')
if "mth" in vn.flags:
j2a["def_hcols"] = vn.flags["mth"].split(",")
diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css
index 2dd4c853..88a138eb 100644
--- a/copyparty/web/browser.css
+++ b/copyparty/web/browser.css
@@ -23,7 +23,7 @@ html, body {
margin: 0;
padding: 0;
}
-pre, code, tt {
+pre, code, tt, #doc, #doc code {
font-family: 'scp', monospace, monospace;
}
#path,
@@ -31,9 +31,8 @@ pre, code, tt {
font-size: 1em;
}
#path {
- color: #aca;
+ color: #ccc;
text-shadow: 1px 1px 0 #000;
- font-variant: small-caps;
font-weight: normal;
display: inline-block;
padding: .35em .5em .2em .5em;
@@ -732,12 +731,12 @@ input.eq_gain {
#tree li:last-child {
border-bottom: none;
}
-#treeul a.hl {
+#tree ul a.hl {
color: #400;
background: #fc4;
text-shadow: none;
}
-#treeul a {
+#tree ul a {
border-radius: .3em;
display: inline-block;
}
@@ -745,7 +744,7 @@ input.eq_gain {
width: calc(100% - 2em);
line-height: 1em;
}
-#tree.nowrap #treeul li {
+#tree.nowrap #tree li {
min-height: 1.4em;
white-space: nowrap;
}
@@ -758,6 +757,7 @@ html.light #tree.nowrap #treeul a+a:hover {
background: rgba(255, 255, 255, 0.67);
color: #000;
}
+#docul a:hover,
#treeul a+a:hover {
background: #181818;
color: #fff;
@@ -857,30 +857,31 @@ html.light #tree.nowrap #treeul a+a:hover {
#wraptree.on+#hovertree {
display: none;
}
-#ghead {
+.ghead {
border-radius: .3em;
padding: .2em .5em;
line-height: 2.3em;
- margin-bottom: 1em;
+ margin-bottom: 1.5em;
+}
+#ghead {
position: sticky;
top: -.3em;
z-index: 1;
}
-html.light #ghead {
+html.light .ghead {
background: #f7f7f7;
border-color: #ddd;
}
-#ghead .btn {
+.ghead .btn {
position: relative;
top: 0;
}
-#ghead>span {
+.ghead>span {
white-space: pre;
padding-left: .3em;
}
#ggrid {
- padding-top: .5em;
- margin: 0 -.5em;
+ margin: -.2em -.5em;
}
#ggrid>a>span {
overflow: hidden;
@@ -989,6 +990,45 @@ html.light #rui {
padding: 0;
font-size: 1.5em;
}
+#doc {
+ background: none;
+ overflow: visible;
+ margin: -1em 0 .5em 0;
+ padding: 1em 0 1em 0;
+}
+#docul li.bn {
+ text-align: center;
+ padding: .5em 0;
+}
+#doc.prism {
+ padding-left: 3em;
+}
+#doc code {
+ background: none;
+ box-shadow: none;
+ z-index: 1;
+}
+#doc.mdo {
+ white-space: normal;
+}
+#doc.prism * {
+ line-height: 1.5em;
+}
+#doc .line-highlight {
+ border-radius: .3em;
+ box-shadow: 0 0 .5em #333;
+ background: linear-gradient(90deg, #111, #222);
+}
+html.light #doc .line-highlight {
+ box-shadow: 0 0 .5em #ccc;
+ background: linear-gradient(90deg, #fff, #eee);
+}
+#docul li {
+ margin: 0;
+}
+#tree #docul a {
+ display: block;
+}
#pvol,
#barbuf,
#barpos,
@@ -1043,7 +1083,7 @@ html,
.opbox,
#path,
#srch_form,
-#ghead {
+.ghead {
background: #2b2b2b;
border: 1px solid #333;
box-shadow: 0 0 .3em #111;
@@ -1110,12 +1150,15 @@ html.light #ops,
html.light .opbox,
html.light #path,
html.light #srch_form,
-html.light #ghead,
+html.light .ghead,
html.light #u2etas {
background: #f7f7f7;
box-shadow: 0 0 .3em #ccc;
border-color: #f7f7f7;
}
+html.light #wrap.doc {
+ background: #f7f7f7;
+}
html.light #ops a.act {
box-shadow: 0 .2em .2em #ccc;
background: #fff;
@@ -1158,17 +1201,17 @@ html.light #treeul a+a {
background: inherit;
color: #06a;
}
-html.light #treeul a.hl {
+html.light #tree ul a.hl {
background: #07a;
color: #fff;
}
-html.light #treeul a.hl:hover {
+html.light #tree ul a.hl:hover {
background: #059;
}
html.light #tree li {
border-color: #f7f7f7 #fff #ddd #fff;
}
-html.light #treeul a:hover {
+html.light #tree ul a:hover {
background: #fff;
}
html.light #tree ul {
@@ -1283,6 +1326,7 @@ html.light #files td div span {
color: #000;
}
html.light #path {
+ color: #777;
background: #f7f7f7;
text-shadow: none;
box-shadow: 0 0 .3em #bbb;
@@ -1300,6 +1344,7 @@ html.light #path a:hover {
html.light #files tbody div a {
color: #d38;
}
+html.light #docul a:hover,
html.light #files a:hover,
html.light #files tr.sel a:hover {
color: #000;
diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html
index bf46ef8f..4b27f836 100644
--- a/copyparty/web/browser.html
+++ b/copyparty/web/browser.html
@@ -76,6 +76,12 @@
+ {%- if doc %}
+
+ {%- else %}
+
+ {%- endif %}
+
{{ logues[0] }}
@@ -135,6 +141,7 @@
have_del = {{ have_del|tojson }},
have_unpost = {{ have_unpost|tojson }},
have_zip = {{ have_zip|tojson }},
+ txt_ext = "{{ txt_ext }}",
readme = {{ readme|tojson }};
document.documentElement.setAttribute("class", localStorage.lightmode == 1 ? "light" : "dark");
diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js
index 6f6d025c..f4119e1c 100644
--- a/copyparty/web/browser.js
+++ b/copyparty/web/browser.js
@@ -2295,6 +2295,204 @@ var fileman = (function () {
})();
+var showfile = (function () {
+ var r = {};
+ r.map = {
+ '.ahk': 'autohotkey',
+ '.bas': 'basic',
+ '.bat': 'batch',
+ '.cxx': 'cpp',
+ '.h': 'c',
+ '.hpp': 'cpp',
+ '.htm': 'html',
+ '.hxx': 'cpp',
+ '.ps1': 'powershell',
+ '.psm1': 'powershell',
+ '.pl': 'perl',
+ '.rs': 'rust',
+ '.sh': 'bash',
+ '.service': 'systemd',
+ '.vb': 'vbnet',
+ '.v': 'verilog',
+ '.vh': 'verilog',
+ '.yml': 'yaml'
+ };
+ r.nmap = {
+ 'cmakelists.txt': 'cmake',
+ 'dockerfile': 'docker'
+ };
+ var x = txt_ext + ' c cpp cs css diff go html ini java js json jsx kt kts less latex lisp lua makefile md py r rss rb ruby sass scss sql svg swift tex toml ts vhdl xml yaml';
+ x = x.split(/ +/g);
+ for (var a = 0; a < x.length; a++)
+ r.map["." + x[a]] = x[a];
+
+ window.Prism = { 'manual': true };
+ var em = QS('#bdoc>pre');
+ if (em)
+ em = [window.location.search.split(/[?&]doc=/)[1].split('&')[0], window.location.hash, em.textContent];
+
+ r.setstyle = function () {
+ qsr('#prism_css');
+ var el = mknod('link');
+ el.rel = 'stylesheet';
+ el.href = '/.cpr/deps/prism' + (light ? '' : 'd') + '.css';
+ el.setAttribute('id', 'prism_css');
+ document.head.appendChild(el);
+ };
+
+ r.active = function () {
+ return document.location.search.indexOf('doc=') + 1;
+ };
+
+ function getlang(fn) {
+ fn = fn.toLowerCase();
+ var ext = fn.slice(fn.lastIndexOf('.'));
+ return r.map[ext] || r.nmap[fn];
+ }
+
+ r.addlinks = function () {
+ r.files = [];
+ var links = msel.getall();
+ for (var a = 0; a < links.length; a++) {
+ var link = links[a],
+ fn = link.vp.split('/').slice(-1)[0],
+ lang = getlang(fn);
+
+ if (!lang)
+ continue;
+
+ r.files.push({ 'id': link.id, 'name': fn });
+
+ if (lang == 'md')
+ continue;
+
+ ebi(link.id).closest('tr').getElementsByTagName('td')[0].innerHTML =
+ '-txt-';
+ }
+ if (em) {
+ render(em);
+ em = null;
+ }
+ };
+
+ r.show = function (url) {
+ var xhr = new XMLHttpRequest();
+ xhr.url = url;
+ xhr.ts = Date.now();
+ xhr.open('GET', url.split('?')[0] + '?raw', true);
+ xhr.onreadystatechange = load_cb;
+ xhr.send();
+ };
+
+ function load_cb() {
+ if (this.readyState != XMLHttpRequest.DONE)
+ return;
+
+ if (this.status !== 200) {
+ toast.err(0, "recvtree, http " + this.status + ": " + this.responseText);
+ return;
+ }
+
+ render([this.url, '', this.responseText]);
+ }
+
+ function render(doc) {
+ r.q = null;
+ var url = doc[0],
+ lnh = doc[1],
+ txt = doc[2],
+ name = url.split('/').slice(-1)[0],
+ lang = getlang(name),
+ is_md = lang == 'md';
+
+ ebi('files').style.display = ebi('gfiles').style.display = ebi('pro').style.display = ebi('epi').style.display = 'none';
+ ebi('dldoc').setAttribute('href', url);
+
+ var wr = ebi('bdoc'),
+ nav = ebi('treeul'),
+ defer = !Prism.highlightElement;
+
+ var fun = function (el) {
+ console.log('fun fun fun fun');
+ try {
+ if (lnh.slice(0, 5) == '#doc.')
+ sethash(lnh.slice(1));
+
+ Prism.highlightElement(el || QS('#doc>code'));
+ }
+ catch (ex) { }
+ }
+
+ qsr('#doc');
+ var el = mknod('pre');
+ el.setAttribute('id', 'doc');
+ clmod(ebi('wrap'), 'doc', !is_md);
+ if (is_md) {
+ show_md(txt, name, el);
+ }
+ else {
+ el.textContent = txt;
+ el.innerHTML = '' + el.innerHTML + '
';
+ el.setAttribute('class', 'prism linkable-line-numbers line-numbers language-' + lang);
+ if (!defer)
+ fun(el.firstChild);
+ else
+ import_js('/.cpr/deps/prism.js', function () { fun(); });
+ }
+
+ wr.appendChild(el);
+ wr.style.display = '';
+
+ document.documentElement.scrollTop = 0;
+ hist_push('?doc=' + url.split('/').slice(-1)[0]);
+
+ qsr('#docul');
+ qsr('#docname');
+ el = mknod('span');
+ el.textContent = name;
+ el.setAttribute('id', 'docname');
+ ebi('path').appendChild(el);
+
+ if (!nav)
+ return;
+
+ var html = ['list of textfiles in
' + esc(get_vpath()) + ''];
+ for (var a = 0; a < r.files.length; a++) {
+ var file = r.files[a];
+ html.push('' + esc(uricom_dec(file.name)[0]) + '');
+ }
+
+ el = mknod('ul');
+ el.innerHTML = html.join('\n');
+ el.setAttribute('id', 'docul');
+ nav.style.display = 'none';
+ nav.parentNode.insertBefore(el, nav);
+ el.onclick = ebi('files').onclick;
+ }
+
+ var bdoc = ebi('bdoc');
+ bdoc.setAttribute('class', 'line-numbers');
+ bdoc.innerHTML = (
+ ''
+ );
+ ebi('xdoc').onclick = function () {
+ thegrid.setvis(true);
+ }
+ ebi('dldoc').setAttribute('download', '');
+ ebi('prevdoc').onclick = function () { tree_neigh(-1); };
+ ebi('nextdoc').onclick = function () { tree_neigh(1); };
+
+ return r;
+})();
+
+
var thegrid = (function () {
var lfiles = ebi('files'),
gfiles = mknod('div');
@@ -2302,7 +2500,7 @@ var thegrid = (function () {
gfiles.setAttribute('id', 'gfiles');
gfiles.style.display = 'none';
gfiles.innerHTML = (
- '' +
+ '
' +
'
multiselect zoom: ' +
'– ' +
'+ chop: ' +
@@ -2350,8 +2548,22 @@ var thegrid = (function () {
for (var a = 0; a < links.length; a++)
links[a].onclick = btnclick;
- r.setvis = function (vis) {
- (r.en ? gfiles : lfiles).style.display = vis ? '' : 'none';
+ r.setvis = function (force) {
+ if (showfile.active()) {
+ if (!force)
+ return;
+
+ hist_push(get_evpath());
+ }
+
+ var vis = has(perms, "read");
+ gfiles.style.display = vis && r.en ? '' : 'none';
+ lfiles.style.display = vis && !r.en ? '' : 'none';
+ ebi('pro').style.display = ebi('epi').style.display = ebi('treeul').style.display = '';
+ ebi('bdoc').style.display = 'none';
+ clmod(ebi('wrap'), 'doc');
+ qsr('#docname');
+ qsr('#docul');
};
r.setdirty = function () {
@@ -2359,6 +2571,7 @@ var thegrid = (function () {
if (r.en) {
loadgrid();
}
+ r.setvis();
};
function setln(v) {
@@ -2477,18 +2690,11 @@ var thegrid = (function () {
tt.att(ebi('ggrid'));
};
- function ungrid() {
- lfiles.style.display = '';
- gfiles.style.display = 'none';
- }
-
function loadgrid() {
if (have_webp === null)
return setTimeout(loadgrid, 50);
- lfiles.style.display = 'none';
- gfiles.style.display = 'block';
-
+ r.setvis();
if (!r.dirty)
return r.loadsel();
@@ -2575,7 +2781,7 @@ var thegrid = (function () {
bcfg_bind(r, 'thumbs', 'thumbs', true, r.setdirty);
bcfg_bind(r, 'sel', 'gridsel', false, r.loadsel);
bcfg_bind(r, 'en', 'griden', false, function (v) {
- v ? loadgrid() : ungrid();
+ v ? loadgrid() : r.setvis(true);
pbar.onresize();
vbar.onresize();
});
@@ -2600,7 +2806,7 @@ function th_onload(el) {
function tree_scrollto(e) {
ev(e);
- var act = QS('#treeul a.hl'),
+ var act = QS('#tree a.hl'),
ul = act ? act.offsetParent : null;
if (!ul)
@@ -2620,7 +2826,7 @@ function tree_scrollto(e) {
function tree_neigh(n) {
- var links = QSA('#treeul li>a+a');
+ var links = QSA(showfile.active() ? '#docul li>a' : '#treeul li>a+a');
if (!links.length) {
treectl.dir_cb = function () {
tree_neigh(n);
@@ -2650,6 +2856,9 @@ function tree_neigh(n) {
function tree_up() {
+ if (showfile.active())
+ return thegrid.setvis(true);
+
var act = QS('#treeul a.hl');
if (!act) {
treectl.dir_cb = tree_up;
@@ -3426,7 +3635,7 @@ var treectl = (function () {
html = html.join('\n');
set_files_html(html);
- if (this.hpush)
+ if (this.hpush && !showfile.active())
hist_push(this.top);
acct = res.acct;
@@ -3597,7 +3806,7 @@ function apply_perms(newperms) {
up2k.set_fsearch();
ebi('widget').style.display = have_read ? '' : 'none';
- thegrid.setvis(have_read);
+ thegrid.setvis();
if (!have_read && have_write)
goto('up2k');
}
@@ -3930,6 +4139,7 @@ var light;
pbar.drawbuf();
pbar.drawpos();
vbar.draw();
+ showfile.setstyle();
}
bcfg_bind(window, 'light', 'lightmode', false, freshen);
@@ -4232,13 +4442,9 @@ var msel = (function () {
})();
-function show_readme(md, url, depth) {
- if (!treectl.ireadme)
- return;
-
- var div = ebi('epi'),
- errmsg = 'cannot show README.md:\n\n',
- now = window.location.href.replace(/\/?[?#].*/, "");
+function show_md(md, name, div, url, depth) {
+ var errmsg = 'cannot show ' + name + ':\n\n',
+ now = get_evpath();
url = url || now;
if (url != now)
@@ -4249,7 +4455,7 @@ function show_readme(md, url, depth) {
return toast.warn(10, errmsg + 'failed to load marked.js')
return import_js('/.cpr/deps/marked.js', function () {
- show_readme(md, url, 1);
+ show_md(md, name, div, url, 1);
});
}
@@ -4273,6 +4479,14 @@ function show_readme(md, url, depth) {
toast.warn(10, errmsg + ex);
}
}
+
+
+function show_readme(md) {
+ if (!treectl.ireadme)
+ return;
+
+ show_md(md, 'README.md', ebi('epi'));
+}
if (readme)
show_readme(readme);
@@ -4463,15 +4677,20 @@ function goto_unpost(e) {
ebi('files').onclick = function (e) {
var tgt = e.target.closest('a[id]');
- if (!tgt || tgt.getAttribute('id').indexOf('f-') !== 0 || !tgt.textContent.endsWith('/'))
- return;
+ if (tgt && tgt.getAttribute('id').indexOf('f-') === 0 && tgt.textContent.endsWith('/')) {
+ var el = treectl.find(tgt.textContent.slice(0, -1));
+ if (!el)
+ return;
- var el = treectl.find(tgt.textContent.slice(0, -1));
- if (!el)
- return;
+ el.click();
+ return ev(e);
+ }
- ev(e);
- el.click();
+ tgt = e.target.closest('a[hl]');
+ if (tgt) {
+ showfile.show(noq_href(ebi(tgt.getAttribute('hl'))), tgt.getAttribute('lang'));
+ return ev(e);
+ }
}
@@ -4531,6 +4750,7 @@ function reload_browser(not_mp) {
thegrid.setdirty();
msel.render();
+ showfile.addlinks();
}
reload_browser(true);
mukey.render();
diff --git a/copyparty/web/util.js b/copyparty/web/util.js
index d783121c..097b8bfd 100644
--- a/copyparty/web/util.js
+++ b/copyparty/web/util.js
@@ -736,7 +736,7 @@ function hist_replace(url) {
function sethash(hv) {
if (window.history && history.replaceState) {
- hist_replace(document.location.pathname + '#' + hv);
+ hist_replace(document.location.pathname + document.location.search + '#' + hv);
}
else {
document.location.hash = hv;
diff --git a/scripts/deps-docker/Dockerfile b/scripts/deps-docker/Dockerfile
index b5211498..c13323cd 100644
--- a/scripts/deps-docker/Dockerfile
+++ b/scripts/deps-docker/Dockerfile
@@ -45,6 +45,12 @@ RUN mkdir -p /z/dist/no-pk \
&& tar -xf zopfli.tgz
+# todo
+# https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js
+# https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/default.min.css
+# https://prismjs.com/download.html#themes=prism-funky&languages=markup+css+clike+javascript+autohotkey+bash+basic+batch+c+csharp+cpp+cmake+diff+docker+go+ini+java+json+kotlin+latex+less+lisp+lua+makefile+objectivec+perl+powershell+python+r+jsx+ruby+rust+sass+scss+sql+swift+systemd+toml+typescript+vbnet+verilog+vhdl+yaml&plugins=line-highlight+line-numbers+autolinker
+
+
# build fonttools (which needs zopfli)
RUN tar -xf zopfli.tgz \
&& cd zopfli* \