mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
markdown-editor joke stopped being a joke...
This commit is contained in:
parent
334c07cc0c
commit
bef2e92cef
|
@ -73,7 +73,7 @@ class MpWorker(object):
|
||||||
if PY2:
|
if PY2:
|
||||||
sck = pickle.loads(sck) # nosec
|
sck = pickle.loads(sck) # nosec
|
||||||
|
|
||||||
self.log(str(addr), "-" * 4 + "C-qpop")
|
self.log("%s %s" % addr, "-" * 4 + "C-qpop")
|
||||||
self.httpsrv.accept(sck, addr)
|
self.httpsrv.accept(sck, addr)
|
||||||
|
|
||||||
with self.mutex:
|
with self.mutex:
|
||||||
|
|
|
@ -28,7 +28,7 @@ class BrokerThr(object):
|
||||||
def put(self, want_retval, dest, *args):
|
def put(self, want_retval, dest, *args):
|
||||||
if dest == "httpconn":
|
if dest == "httpconn":
|
||||||
sck, addr = args
|
sck, addr = args
|
||||||
self.log(str(addr), "-" * 4 + "C-qpop")
|
self.log("%s %s" % addr, "-" * 4 + "C-qpop")
|
||||||
self.httpsrv.accept(sck, addr)
|
self.httpsrv.accept(sck, addr)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -708,7 +708,14 @@ class HttpCli(object):
|
||||||
|
|
||||||
def tx_md(self, fs_path):
|
def tx_md(self, fs_path):
|
||||||
logmsg = "{:4} {} ".format("", self.req)
|
logmsg = "{:4} {} ".format("", self.req)
|
||||||
html_path = os.path.join(E.mod, "web/md.html")
|
if "edit" in self.uparam:
|
||||||
|
html_path = "web/mde.html"
|
||||||
|
template = self.conn.tpl_mde
|
||||||
|
else:
|
||||||
|
html_path = "web/md.html"
|
||||||
|
template = self.conn.tpl_md
|
||||||
|
|
||||||
|
html_path = os.path.join(E.mod, html_path)
|
||||||
|
|
||||||
st = os.stat(fsenc(fs_path))
|
st = os.stat(fsenc(fs_path))
|
||||||
sz_md = st.st_size
|
sz_md = st.st_size
|
||||||
|
@ -726,7 +733,7 @@ class HttpCli(object):
|
||||||
"title": html_escape(self.vpath, quote=False),
|
"title": html_escape(self.vpath, quote=False),
|
||||||
"md": "",
|
"md": "",
|
||||||
}
|
}
|
||||||
sz_html = len(self.conn.tpl_md.render(**targs).encode("utf-8"))
|
sz_html = len(template.render(**targs).encode("utf-8"))
|
||||||
self.send_headers(sz_html + sz_md, status)
|
self.send_headers(sz_html + sz_md, status)
|
||||||
|
|
||||||
logmsg += str(status)
|
logmsg += str(status)
|
||||||
|
@ -738,7 +745,7 @@ class HttpCli(object):
|
||||||
md = f.read()
|
md = f.read()
|
||||||
|
|
||||||
targs["md"] = md.decode("utf-8", "replace")
|
targs["md"] = md.decode("utf-8", "replace")
|
||||||
html = self.conn.tpl_md.render(**targs).encode("utf-8")
|
html = template.render(**targs).encode("utf-8")
|
||||||
try:
|
try:
|
||||||
self.s.sendall(html)
|
self.s.sendall(html)
|
||||||
except:
|
except:
|
||||||
|
|
|
@ -36,6 +36,7 @@ class HttpConn(object):
|
||||||
self.tpl_browser = env.get_template("browser.html")
|
self.tpl_browser = env.get_template("browser.html")
|
||||||
self.tpl_msg = env.get_template("msg.html")
|
self.tpl_msg = env.get_template("msg.html")
|
||||||
self.tpl_md = env.get_template("md.html")
|
self.tpl_md = env.get_template("md.html")
|
||||||
|
self.tpl_mde = env.get_template("mde.html")
|
||||||
|
|
||||||
def respath(self, res_name):
|
def respath(self, res_name):
|
||||||
return os.path.join(E.mod, "web", res_name)
|
return os.path.join(E.mod, "web", res_name)
|
||||||
|
|
|
@ -38,7 +38,7 @@ class HttpSrv(object):
|
||||||
|
|
||||||
def accept(self, sck, addr):
|
def accept(self, sck, addr):
|
||||||
"""takes an incoming tcp connection and creates a thread to handle it"""
|
"""takes an incoming tcp connection and creates a thread to handle it"""
|
||||||
self.log(str(addr), "-" * 5 + "C-cthr")
|
self.log("%s %s" % addr, "-" * 5 + "C-cthr")
|
||||||
thr = threading.Thread(target=self.thr_client, args=(sck, addr))
|
thr = threading.Thread(target=self.thr_client, args=(sck, addr))
|
||||||
thr.daemon = True
|
thr.daemon = True
|
||||||
thr.start()
|
thr.start()
|
||||||
|
@ -66,16 +66,18 @@ class HttpSrv(object):
|
||||||
thr.start()
|
thr.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.log(str(addr), "-" * 6 + "C-crun")
|
self.log("%s %s" % addr, "-" * 6 + "C-crun")
|
||||||
cli.run()
|
cli.run()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
self.log(str(addr), "-" * 7 + "C-done")
|
self.log("%s %s" % addr, "-" * 7 + "C-done")
|
||||||
try:
|
try:
|
||||||
sck.shutdown(socket.SHUT_RDWR)
|
sck.shutdown(socket.SHUT_RDWR)
|
||||||
sck.close()
|
sck.close()
|
||||||
except (OSError, socket.error) as ex:
|
except (OSError, socket.error) as ex:
|
||||||
self.log(str(addr), "shut_rdwr err:\n {}\n {}".format(repr(sck), ex))
|
self.log(
|
||||||
|
"%s %s" % addr, "shut_rdwr err:\n {}\n {}".format(repr(sck), ex),
|
||||||
|
)
|
||||||
if ex.errno not in [10038, 107, 57, 9]:
|
if ex.errno not in [10038, 107, 57, 9]:
|
||||||
# 10038 No longer considered a socket
|
# 10038 No longer considered a socket
|
||||||
# 107 Transport endpoint not connected
|
# 107 Transport endpoint not connected
|
||||||
|
|
|
@ -64,7 +64,7 @@ class TcpSrv(object):
|
||||||
|
|
||||||
self.log("tcpsrv", "-" * 2 + "C-acc1")
|
self.log("tcpsrv", "-" * 2 + "C-acc1")
|
||||||
sck, addr = self.srv.accept()
|
sck, addr = self.srv.accept()
|
||||||
self.log(str(addr), "-" * 3 + "C-acc2")
|
self.log("%s %s" % addr, "-" * 3 + "C-acc2")
|
||||||
self.num_clients.add()
|
self.num_clients.add()
|
||||||
self.hub.broker.put(False, "httpconn", sck, addr)
|
self.hub.broker.put(False, "httpconn", sck, addr)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<div id="mn"></div>
|
<div id="mn"></div>
|
||||||
<div id="mh">
|
<div id="mh">
|
||||||
<a id="lightswitch" href="#">dark</a>
|
<a id="lightswitch" href="#">dark</a>
|
||||||
there will soon be more buttons on this row so it looks less dumb
|
<a id="edit" href="?edit">edit</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="ml">
|
<div id="ml">
|
||||||
<div style="text-align:center;margin:5em 0">
|
<div style="text-align:center;margin:5em 0">
|
||||||
|
|
|
@ -14,15 +14,15 @@ var dom_md = document.getElementById('mt');
|
||||||
// add toolbar buttons
|
// add toolbar buttons
|
||||||
(function () {
|
(function () {
|
||||||
var n = document.location + '';
|
var n = document.location + '';
|
||||||
n = n.substr(n.indexOf('//') + 2).split('/');
|
n = n.substr(n.indexOf('//') + 2).split('?')[0].split('/');
|
||||||
n[0] = 'top';
|
n[0] = 'top';
|
||||||
|
var loc = [];
|
||||||
var nav = [];
|
var nav = [];
|
||||||
var url = '/';
|
|
||||||
for (var a = 0; a < n.length; a++) {
|
for (var a = 0; a < n.length; a++) {
|
||||||
if (a > 0)
|
if (a > 0)
|
||||||
url += n[a] + '/';
|
loc.push(n[a]);
|
||||||
|
|
||||||
nav.push('<a href="' + url + '">' + n[a] + '</a>');
|
nav.push('<a href="/' + loc.join('/') + '">' + n[a] + '</a>');
|
||||||
}
|
}
|
||||||
dom_nav.innerHTML = nav.join('');
|
dom_nav.innerHTML = nav.join('');
|
||||||
})();
|
})();
|
||||||
|
|
75
copyparty/web/mde.css
Normal file
75
copyparty/web/mde.css
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* fontawesome is 2big */
|
||||||
|
.fa { font-size: 1.5em; line-height: 1em; margin: -3em; position: relative; left: -.05em; font-style: normal }
|
||||||
|
.fa-bold:before { content: 'B'; font-weight: bold }
|
||||||
|
.fa-italic:before { content: 'I'; font-style: italic }
|
||||||
|
.fa-strikethrough:before { content: ' S '; text-decoration: line-through }
|
||||||
|
.fa-header:before { content: 'H'; font-weight: bold; font-family: serif; top: -.2em; left: .5em }
|
||||||
|
.fa-quote-left:before { content: '❝'; font-family: serif; top: -.5em }
|
||||||
|
.fa-list-ul:before { content: '•' }
|
||||||
|
.fa-list-ol:before { content: '①' }
|
||||||
|
.fa-link:before { content: '🔗'; font-size: .8em }
|
||||||
|
.fa-image:before { content: '🎨' }
|
||||||
|
.fa-eye:before { content: '👀'; font-size: .9em }
|
||||||
|
.fa-columns:before { content: '◫' }
|
||||||
|
.fa-arrows-alt:before { content: '⛶' }
|
||||||
|
.fa-undo:before { content: '⟲' }
|
||||||
|
.fa-redo:before { content: '⟳' }
|
||||||
|
.fa-question-circle:before { content: '?' }
|
||||||
|
.fa-code:before { content: '‹/›'; font-size: .8em; }
|
||||||
|
.fa-eraser:before { content: '⁋' }
|
||||||
|
.fa-table:before { content: '⊞' }
|
||||||
|
.fa-minus:before { content: '−' }
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://dev.w3.org/html5/html-author/charref
|
||||||
|
http://xahlee.info/comp/unicode_arrows.html
|
||||||
|
https://www.fileformat.info/info/unicode/block/miscellaneous_symbols_and_pictographs/list.htm
|
||||||
|
↶ ↷ 💾 🗙
|
||||||
|
*/
|
||||||
|
|
||||||
|
button { border: 1px solid #999 !important; }
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
#mn {
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 1.3em 0 .7em 1em;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
#mn a {
|
||||||
|
color: #444;
|
||||||
|
margin: 0 0 0 -.2em;
|
||||||
|
padding: 0 0 0 .4em;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
#mn a:first-child {
|
||||||
|
padding-left: .5em;
|
||||||
|
}
|
||||||
|
#mn a:last-child {
|
||||||
|
padding-right: .5em;
|
||||||
|
}
|
||||||
|
#mn a:not(:last-child):after {
|
||||||
|
content: '';
|
||||||
|
width: 1.05em;
|
||||||
|
height: 1.05em;
|
||||||
|
margin: -.2em .3em -.2em -.4em;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid rgba(0,0,0,0.3);
|
||||||
|
border-width: .05em .05em 0 0;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
#mn a:hover {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
*[data-ln]:before {
|
||||||
|
content: attr(data-ln);
|
||||||
|
font-size: .8em;
|
||||||
|
margin: 0 .4em;
|
||||||
|
color: #f0c;
|
||||||
|
}
|
29
copyparty/web/mde.html
Normal file
29
copyparty/web/mde.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html><html><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>📝🎉 {{ title }}</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=0.7">
|
||||||
|
<link href="/.cpr/mde.css" rel="stylesheet">
|
||||||
|
<link href="/.cpr/deps/easymde.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="mw">
|
||||||
|
<div id="mn"></div>
|
||||||
|
<div id="ml">
|
||||||
|
<div style="text-align:center;margin:5em 0">
|
||||||
|
<div style="font-size:2em;margin:1em 0">Loading</div>
|
||||||
|
if you're still reading this, check that javascript is allowed
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="m">
|
||||||
|
<textarea id="mt" style="display:none">{{ md }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var link_md_as_html = false; // TODO (does nothing)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="/.cpr/deps/easymde.full.js"></script>
|
||||||
|
<script src="/.cpr/mde.js"></script>
|
||||||
|
</body></html>
|
40
copyparty/web/mde.js
Normal file
40
copyparty/web/mde.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
var dom_wrap = document.getElementById('mw');
|
||||||
|
var dom_nav = document.getElementById('mn');
|
||||||
|
var dom_doc = document.getElementById('m');
|
||||||
|
var dom_md = document.getElementById('mt');
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var n = document.location + '';
|
||||||
|
n = n.substr(n.indexOf('//') + 2).split('?')[0].split('/');
|
||||||
|
n[0] = 'top';
|
||||||
|
var loc = [];
|
||||||
|
var nav = [];
|
||||||
|
for (var a = 0; a < n.length; a++) {
|
||||||
|
if (a > 0)
|
||||||
|
loc.push(n[a]);
|
||||||
|
|
||||||
|
nav.push('<a href="/' + loc.join('/') + '">' + n[a] + '</a>');
|
||||||
|
}
|
||||||
|
dom_nav.innerHTML = nav.join('');
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var mde = new EasyMDE({
|
||||||
|
autoDownloadFontAwesome: false,
|
||||||
|
autofocus: true,
|
||||||
|
insertTexts: ["[](", ")"],
|
||||||
|
renderingConfig: {
|
||||||
|
markedOptions: {
|
||||||
|
breaks: true,
|
||||||
|
gfm: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
spellChecker: false,
|
||||||
|
tabSize: 4,
|
||||||
|
showIcons: ['strikethrough', 'code', 'table', 'undo', 'redo', 'heading', 'clean-block', 'horizontal-rule']
|
||||||
|
});
|
||||||
|
var loader = document.getElementById('ml');
|
||||||
|
loader.parentNode.removeChild(loader);
|
||||||
|
})();
|
||||||
|
|
||||||
|
zsetTimeout(function () { window.location.reload(true); }, 1000);
|
|
@ -4,7 +4,8 @@ ENV ver_asmcrypto=2821dd1dedd1196c378f5854037dda5c869313f3 \
|
||||||
ver_markdownit=10.0.0 \
|
ver_markdownit=10.0.0 \
|
||||||
ver_showdown=1.9.1 \
|
ver_showdown=1.9.1 \
|
||||||
ver_marked=1.0.0 \
|
ver_marked=1.0.0 \
|
||||||
ver_ogvjs=1.6.1
|
ver_ogvjs=1.6.1 \
|
||||||
|
ver_mde=2.10.1
|
||||||
|
|
||||||
|
|
||||||
# download
|
# download
|
||||||
|
@ -12,6 +13,7 @@ RUN apk add make g++ git bash npm patch wget tar pigz brotli gzip unzip \
|
||||||
&& wget https://github.com/brion/ogv.js/releases/download/$ver_ogvjs/ogvjs-$ver_ogvjs.zip -O ogvjs.zip \
|
&& wget https://github.com/brion/ogv.js/releases/download/$ver_ogvjs/ogvjs-$ver_ogvjs.zip -O ogvjs.zip \
|
||||||
&& wget https://github.com/asmcrypto/asmcrypto.js/archive/$ver_asmcrypto.tar.gz -O asmcrypto.tgz \
|
&& wget https://github.com/asmcrypto/asmcrypto.js/archive/$ver_asmcrypto.tar.gz -O asmcrypto.tgz \
|
||||||
&& wget https://github.com/markedjs/marked/archive/v$ver_marked.tar.gz -O marked.tgz \
|
&& wget https://github.com/markedjs/marked/archive/v$ver_marked.tar.gz -O marked.tgz \
|
||||||
|
&& wget https://github.com/Ionaru/easy-markdown-editor/archive/$ver_mde.tar.gz -O mde.tgz \
|
||||||
&& unzip ogvjs.zip \
|
&& unzip ogvjs.zip \
|
||||||
&& (tar -xf asmcrypto.tgz \
|
&& (tar -xf asmcrypto.tgz \
|
||||||
&& cd asmcrypto.js-$ver_asmcrypto \
|
&& cd asmcrypto.js-$ver_asmcrypto \
|
||||||
|
@ -20,7 +22,13 @@ RUN apk add make g++ git bash npm patch wget tar pigz brotli gzip unzip \
|
||||||
&& cd marked-$ver_marked \
|
&& cd marked-$ver_marked \
|
||||||
&& npm install \
|
&& npm install \
|
||||||
&& npm i grunt uglify-js -g ) \
|
&& npm i grunt uglify-js -g ) \
|
||||||
|
&& (tar -xf mde.tgz \
|
||||||
|
&& cd easy-markdown-editor* \
|
||||||
|
&& npm install \
|
||||||
|
&& npm i gulp-cli -g ) \
|
||||||
&& mkdir /z/dist
|
&& mkdir /z/dist
|
||||||
|
# && wget https://github.com/FortAwesome/Font-Awesome/releases/download/5.0.11/fontawesome-free-5.0.11.zip -O fontawesome.zip \
|
||||||
|
# && unzip fontawesome.zip \
|
||||||
|
|
||||||
|
|
||||||
# uncomment if you wanna test the abandoned markdown converters
|
# uncomment if you wanna test the abandoned markdown converters
|
||||||
|
@ -74,10 +82,26 @@ RUN cd marked-$ver_marked \
|
||||||
&& patch -p1 < /z/marked.patch \
|
&& patch -p1 < /z/marked.patch \
|
||||||
&& npm run build \
|
&& npm run build \
|
||||||
&& cp -pv marked.min.js /z/dist/marked.js \
|
&& cp -pv marked.min.js /z/dist/marked.js \
|
||||||
&& cp -pv lib/marked.js /z/dist/marked.full.js
|
&& cp -pv lib/marked.js /z/dist/marked.full.js \
|
||||||
|
&& mkdir -p /z/nodepkgs \
|
||||||
|
&& ln -s $(pwd) /z/nodepkgs/marked
|
||||||
# && npm run test \
|
# && npm run test \
|
||||||
|
|
||||||
|
|
||||||
|
# build easymde (TODO man this thing is big)
|
||||||
|
RUN cd easy-markdown-editor-$ver_mde \
|
||||||
|
&& sed -ri 's`https://registry.npmjs.org/marked/-/marked-0.8.2.tgz`file:/z/nodepkgs/marked`' package-lock.json \
|
||||||
|
&& sed -ri 's`("marked": ")[^"]+`\1file:/z/nodepkgs/marked`' ./package.json \
|
||||||
|
&& npm install \
|
||||||
|
&& gulp \
|
||||||
|
&& cp -pv dist/easymde.min.css /z/dist/easymde.css \
|
||||||
|
&& cp -pv dist/easymde.min.js /z/dist/easymde.js \
|
||||||
|
&& sed -ri '/pipe.terser/d; /cleanCSS/d' gulpfile.js \
|
||||||
|
&& gulp \
|
||||||
|
&& cp -pv dist/easymde.min.css /z/dist/easymde.full.css \
|
||||||
|
&& cp -pv dist/easymde.min.js /z/dist/easymde.full.js
|
||||||
|
|
||||||
|
|
||||||
# build showdown (abandoned; disabled by default)
|
# build showdown (abandoned; disabled by default)
|
||||||
COPY showdown.patch /z/
|
COPY showdown.patch /z/
|
||||||
RUN [ $build_abandoned ] || exit 0; \
|
RUN [ $build_abandoned ] || exit 0; \
|
||||||
|
@ -126,6 +150,9 @@ COPY zopfli.makefile /z/dist/Makefile
|
||||||
RUN cd /z/dist \
|
RUN cd /z/dist \
|
||||||
&& make -j$(nproc) \
|
&& make -j$(nproc) \
|
||||||
&& rm Makefile
|
&& rm Makefile
|
||||||
|
# && cp -pv "$(find /z/fontawesome-fre* -name fa-regular-400.woff | head -n 1)" fontawesome.woff
|
||||||
|
# && cp -pv "$(find /z/fontawesome-fre* -name fontawesome.min.css | head -n 1)" fontawesome.css \
|
||||||
|
# && sed -ri 's`@font-face.*``' fontawesome.css \
|
||||||
|
|
||||||
|
|
||||||
# showdown: abandoned due to code-blocks in lists failing
|
# showdown: abandoned due to code-blocks in lists failing
|
||||||
|
@ -156,5 +183,5 @@ RUN cd /z/dist \
|
||||||
# f=../../copyparty/web/deps/marked.js.gz; (cd ~ed/src/ && diff -NarU1 marked-1.0.0-orig/ marked-1.0.0-edit/) >marked.patch; make && printf '%d ' $(wc -c <$f) $(gzip -d <$f | wc -c); echo
|
# f=../../copyparty/web/deps/marked.js.gz; (cd ~ed/src/ && diff -NarU1 marked-1.0.0-orig/ marked-1.0.0-edit/) >marked.patch; make && printf '%d ' $(wc -c <$f) $(gzip -d <$f | wc -c); echo
|
||||||
|
|
||||||
|
|
||||||
# d=/home/ed/dev/copyparty/scripts/deps-docker/; scp Dockerfile marked-ln.patch root@$bip:$d && ssh root@$bip "cd $d && make" && ssh root@$bip 'tar -cC /home/ed/dev/copyparty/copyparty/web deps' | (cd ../../copyparty/web/; cat > the.tgz; tar -xvf the.tgz)
|
# d=/home/ed/dev/copyparty/scripts/deps-docker/; tar -cf ../x . && ssh root@$bip "cd $d && tar -xv >&2 && make >&2 && tar -cC ../../copyparty/web deps" <../x | (cd ../../copyparty/web/; cat > the.tgz; tar -xvf the.tgz)
|
||||||
# gzip -dkf ../dev/copyparty/copyparty/web/deps/deps/marked.full.js.gz && diff -NarU2 ../dev/copyparty/copyparty/web/deps/{,deps/}marked.full.js
|
# gzip -dkf ../dev/copyparty/copyparty/web/deps/deps/marked.full.js.gz && diff -NarU2 ../dev/copyparty/copyparty/web/deps/{,deps/}marked.full.js
|
||||||
|
|
Loading…
Reference in a new issue