Loose .md/.txt/.log files rendered as forced-download links in the gallery
and downloaded (or showed raw) when opened. Now they open in a readable
in-booth page via the existing /b/<name>/view route:
- .md -> rendered HTML (Python-Markdown: fenced code, tables, sane lists),
styled in an Australis .markdown-body with the viewer chrome;
- .txt/.log -> preformatted <pre> text view.
The gallery links docs to the viewer (📄) instead of a download; the view
page keeps a ⬇ (?dl=1) for saving. Files over 2 MB hand back raw. New
markdown dep (optional-import: degrades .md to text view if absent).
booth_image_view -> booth_view_file (now handles image + doc + raw-fallback).
9 new tests, suite 44 passing; deployed + verified live on nh3-dev :8090.
87 lines
3.4 KiB
HTML
87 lines
3.4 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}{{ name }} · The Booth{% endblock %}
|
||
{% block content %}
|
||
<div class="boothhead">
|
||
<a class="back" href="/">‹ all booths</a>
|
||
<h1>{{ name }}</h1>
|
||
<span class="sub">{% if uploaded %}<span class="badge">⬆ pickup</span> {% endif %}{{ items|length }} item{{ '' if items|length == 1 else 's' }} · expires in {{ expires_in|dur }}</span>
|
||
{% if items %}<a class="dl-link" href="/b/{{ name_url }}/?download=1" title="download this booth as a zip">⬇ zip</a>{% endif %}
|
||
<form class="wipe wipe-lg" method="post" action="/b/{{ name_url }}/delete"
|
||
onsubmit="return confirm('Wipe this booth now?')">
|
||
<button>Wipe now</button>
|
||
</form>
|
||
</div>
|
||
|
||
{% if uploaded %}
|
||
<div class="pickup-note">
|
||
📦 Pickup <code>{{ name }}</code>
|
||
<button type="button" class="copy-btn" data-copy="{{ name }}" title="copy id to clipboard">⧉ copy</button>
|
||
— download files below, or on nh3-dev grab <code>~/booth-data/{{ name }}/</code>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if not items %}
|
||
<div class="empty">This booth is empty.</div>
|
||
{% else %}
|
||
<div class="gallery">
|
||
{% for it in items %}
|
||
<figure class="item item-{{ it.kind }}">
|
||
{% if it.kind == 'image' %}
|
||
<a href="view?f={{ it.url }}"><img loading="lazy" src="{{ it.url }}" alt="{{ it.name }}"></a>
|
||
{% elif it.kind == 'video' %}
|
||
<video controls preload="metadata" src="{{ it.url }}"></video>
|
||
{% elif it.kind == 'audio' %}
|
||
<audio controls preload="metadata" src="{{ it.url }}"></audio>
|
||
{% elif it.doc %}
|
||
<a class="dl doc" href="view?f={{ it.url }}" title="view {{ it.name }}">📄 {{ it.name }}</a>
|
||
{% else %}
|
||
<a class="dl" href="{{ it.url }}" download>⬇ {{ it.name }}</a>
|
||
{% endif %}
|
||
{% if it.kind == 'other' %}
|
||
{% if it.caption %}<figcaption><span class="cap-text">{{ it.caption }}</span></figcaption>{% endif %}
|
||
{% else %}
|
||
<figcaption>
|
||
<a class="dl-link" href="{{ it.url }}" download title="download {{ it.name }}">⬇</a>
|
||
<span class="cap-text">{{ it.caption or it.name }}</span>
|
||
</figcaption>
|
||
{% endif %}
|
||
</figure>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<script>
|
||
/* Copy-to-clipboard for any .copy-btn[data-copy]. The Booth serves over plain
|
||
HTTP on a LAN IP, where navigator.clipboard is undefined (secure-context
|
||
only) — so fall back to a hidden-textarea execCommand('copy'). */
|
||
(function () {
|
||
function copyText(t) {
|
||
if (navigator.clipboard && window.isSecureContext) {
|
||
return navigator.clipboard.writeText(t);
|
||
}
|
||
var ta = document.createElement('textarea');
|
||
ta.value = t;
|
||
ta.setAttribute('readonly', '');
|
||
ta.style.position = 'fixed';
|
||
ta.style.top = '-1000px';
|
||
ta.style.opacity = '0';
|
||
document.body.appendChild(ta);
|
||
ta.select();
|
||
try { document.execCommand('copy'); } catch (e) {}
|
||
document.body.removeChild(ta);
|
||
return Promise.resolve();
|
||
}
|
||
document.querySelectorAll('.copy-btn').forEach(function (btn) {
|
||
var label = btn.textContent;
|
||
btn.addEventListener('click', function () {
|
||
copyText(btn.getAttribute('data-copy')).then(function () {
|
||
btn.classList.add('copied');
|
||
btn.textContent = '✓ copied';
|
||
setTimeout(function () { btn.classList.remove('copied'); btn.textContent = label; }, 1300);
|
||
});
|
||
});
|
||
});
|
||
})();
|
||
</script>
|
||
{% endblock %}
|