From 771900051347c7efc2fb629e089d07660ab75974 Mon Sep 17 00:00:00 2001 From: Merlin04 Date: Tue, 24 Feb 2026 16:49:19 -0800 Subject: [PATCH 1/6] Migrate frontend to Bulma, remove jQuery --- pyproject.toml | 7 +- src/bepasty/bepasty_xstatic.py | 5 - src/bepasty/static/app/css/style.css | 56 +---- src/bepasty/static/app/js/fileuploader.js | 281 +++++++++++----------- src/bepasty/static/app/js/utils.js | 127 +++++----- src/bepasty/templates/_layout.html | 96 ++++---- src/bepasty/templates/_utils.html | 62 ++--- src/bepasty/templates/carousel.html | 66 +++-- src/bepasty/templates/display.html | 127 +++++----- src/bepasty/templates/error.html | 10 +- src/bepasty/templates/filelist.html | 6 +- src/bepasty/templates/index.html | 159 +++++------- src/bepasty/templates/qr.html | 82 +++++-- 13 files changed, 531 insertions(+), 553 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e950ea0b..bd3ecc85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,13 +35,10 @@ dependencies = [ "Pygments >= 2.12.0", "xstatic < 2.0.0", "XStatic-asciinema-player <= 2.6.1.1", - "xstatic-bootbox >= 5.4.0, <= 5.5.1.1", - "xstatic-bootstrap >=4.0.0.0, <= 4.5.3.1", "xstatic-font-awesome >= 6.0.0, <= 6.2.1.1", - "xstatic-jquery <= 3.5.1.1", - "xstatic-jquery-ui <= 1.13.0.1", - "xstatic-jquery-file-upload <= 10.31.0.1", "xstatic-pygments <= 2.9.0.1", + "setuptools>=82.0.0", + "pillow>=12.1.1", ] [project.scripts] diff --git a/src/bepasty/bepasty_xstatic.py b/src/bepasty/bepasty_xstatic.py index bdda38ab..05e6e167 100644 --- a/src/bepasty/bepasty_xstatic.py +++ b/src/bepasty/bepasty_xstatic.py @@ -3,12 +3,7 @@ # The names below must be package names. mod_names = [ 'asciinema_player', - 'bootbox', - 'bootstrap', 'font_awesome', - 'jquery', - 'jquery_ui', - 'jquery_file_upload', 'pygments', ] diff --git a/src/bepasty/static/app/css/style.css b/src/bepasty/static/app/css/style.css index ca07c310..034bc631 100644 --- a/src/bepasty/static/app/css/style.css +++ b/src/bepasty/static/app/css/style.css @@ -11,24 +11,15 @@ body { background-color: #fff; } -#footer { - height: 30px; - position: relative; -} - #formupload { height: 400px; width: 100%; font-family: monospace; } -#files p { +#files a { word-break: break-all; } -#files p .break-word { - word-break: break-word; - font-weight: normal !important; -} #filelist { height: auto; @@ -36,16 +27,6 @@ body { font-family: monospace; } -.fileupload-abort { - float: right; - margin-left: 10px; -} - -.dropzone { - width: 100%; - padding: 3em; -} - #wrapper { min-height: 100%; margin: 0 auto -30px; @@ -56,33 +37,11 @@ body { padding: 0 15px 60px; } -.jumbotron .fa { - font-size: 80px; -} - .jumbotron { font-size: 100%; text-align: center; } -.alert { - padding: 5px 10px; -} - -.alert-processing { - color: #464646; - background-color: #f0f0f0; - border-color: #eeeeee; -} - -.alert-processing hr { - border-top-color: #e6e6e6; -} - -.alert-processing .alert-link { - color: #959595; -} - /* If data is too wide, show scrollbars */ .data { width: 100%; @@ -132,7 +91,14 @@ table.highlighttable { width: 1px; } -/* Autocomplete for modify dialog */ -.ui-autocomplete { - z-index: 1065; +.mr-auto { + margin-right: auto; +} + +.filepond--credits { + display: none; +} + +.is-text-6-5 { + font-size: 0.85rem; } diff --git a/src/bepasty/static/app/js/fileuploader.js b/src/bepasty/static/app/js/fileuploader.js index 927e7278..755c7429 100644 --- a/src/bepasty/static/app/js/fileuploader.js +++ b/src/bepasty/static/app/js/fileuploader.js @@ -1,139 +1,148 @@ -jqXHR = {}; -$(function () { - 'use strict'; - - // Generate human-readable file size - function humansize (size) { - var suffix = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"], - tier = 0; - - while (size >= 1024) { - size = size / 1024; - tier++; - } +function humansize (size) { + const suffix = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"]; + let tier = 0; + + while (size >= 1024) { + size = size / 1024; + tier++; + } + + return Math.round(size * 10) / 10 + " " + suffix[tier]; +} + +function process(fieldName, file, metadata, load, error, progress, abort, transfer, options) { + const state = { aborted: false, xhr: null }; + let uploadUrl = null; + let displayUrl = null; + + const controller = new AbortController(); + + const doUpload = (url, name, offset) => { + if (state.aborted) return; + + const end = Math.min(offset + MAX_BODY_SIZE, file.size); + const xhr = new XMLHttpRequest(); + state.xhr = xhr; + + xhr.open("POST", url, true); + xhr.setRequestHeader("Content-Range", `bytes ${offset}-${end - 1}/${file.size}`); + + xhr.upload.onprogress = (e) => progress(true, offset + e.loaded, file.size); + + xhr.onload = () => { + state.xhr = null; + if (xhr.status < 200 || xhr.status >= 300) { + error(`Chunk upload failed: ${xhr.status}`); + return; + } + const result = JSON.parse(xhr.responseText); + if (result.files?.[0]?.url) displayUrl = result.files[0].url; + + if (end < file.size) { + doUpload(url, name, end); + } else { + load(displayUrl ?? url); + const filesDiv = document.getElementById("files"); + const alert = document.createElement("div"); + alert.className = "notification is-success is-light"; + alert.dataset.name = name; + const a = document.createElement("a"); + a.href = displayUrl ?? url; + a.target = "_blank"; + a.textContent = `${file.name} (${humansize(file.size)})`; + alert.appendChild(a); + filesDiv.appendChild(alert); + + const fileList = document.getElementById("filelist"); + fileList.textContent += name + "\n"; + const fileListForm = document.getElementById("filelist-form"); + fileListForm.style.display = ""; + } + }; + + xhr.onerror = () => { state.xhr = null; error("Network error"); }; + xhr.onabort = () => { state.xhr = null; }; + + const formData = new FormData(); + formData.append("text", ""); + formData.append("contenttype", file.type || "application/octet-stream"); + formData.append("filename", file.name); + formData.append("maxlife-value", document.querySelector("input[name=maxlife-value]").value); + formData.append("maxlife-unit", document.querySelector("select[name=maxlife-unit]").value); + formData.append("file", file.slice(offset, end), file.name); + + xhr.send(formData); + }; + + fetch(UPLOAD_NEW_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + signal: controller.signal, + body: JSON.stringify({ + filename: file.name, + size: file.size, + type: file.type, + maxlife_unit: document.querySelector("select[name=maxlife-unit]").value, + maxlife_value: document.querySelector("input[name=maxlife-value]").value + }) + }) + .then(res => { + if (!res.ok) throw new Error("Failed to initialise upload"); + return res.json(); + }) + .then(({ name, url }) => { + uploadUrl = url; + doUpload(url, name, 0); + }) + .catch(err => { + if (err.name !== "AbortError") error(err.message); + }); - return Math.round(size * 10) / 10 + " " + suffix[tier]; + return { + abort: () => { + state.aborted = true; + controller.abort(); + if (state.xhr) state.xhr.abort(); + if (uploadUrl) fetch(`${uploadUrl}/abort`).catch(() => {}); + abort(); } - - $('#fileupload') - .fileupload({ - dataType: 'json', - autoUpload: true, - singleFileUploads: true, - maxChunkSize: MAX_BODY_SIZE, - maxFileSize: MAX_ALLOWED_FILE_SIZE - }) - - .on('fileuploadadd', function (e, data) { }) - - .on('fileuploadsubmit', function (e, data) { - var $this = $(this); - var file = data.files[0] - // Create new item - $.ajax({ - type: 'POST', - url: UPLOAD_NEW_URL, - data: JSON.stringify({ - filename: file.name, - size: file.size, - type: file.type, - maxlife_unit: $("select[name=maxlife-unit] option:selected").val(), - maxlife_value: $("input[name=maxlife-value]").val() - }), - contentType: 'application/json', - success: function (result) { - data.url = result.url; - - data.context = $('
') - .appendTo('#files'); - - var abortButton = $(' - - - + +
+ @@ -86,19 +75,18 @@ - - - - - - - - - - + {% block extra_script %}{% endblock %} diff --git a/src/bepasty/templates/_utils.html b/src/bepasty/templates/_utils.html index 3e8cfbf3..71af1abf 100644 --- a/src/bepasty/templates/_utils.html +++ b/src/bepasty/templates/_utils.html @@ -1,8 +1,8 @@ {% macro filelist(files) %} - +
- {% for file in files %} -
+ Filename
Type @@ -25,7 +25,7 @@
+ {{ file['filename'] }} @@ -49,33 +49,41 @@ {% endif %} -
- - Display - - - Download - - - Inline - + +
{% if may(DELETE) %} -
-
{% endif %} {% if may(ADMIN) %} {% if not file['locked'] %} -
-
{% else %} -
-
@@ -90,14 +98,14 @@ {% endmacro %} {% macro input_filename(value) -%} - -{%- endmacro %} - -{% macro input_contenttype(value) -%} - + {%- endmacro %} -{% macro contenttype_autocomplete(selector, contenttypes) -%} - var availableTypes = ["{{ contenttypes | join('","') | safe}}"]; - {{ selector|safe }}.autocomplete({source: availableTypes}); +{% macro input_contenttype(contenttypes, value) -%} + + + {% for contenttype in contenttypes %} + + {% endfor %} + {%- endmacro %} diff --git a/src/bepasty/templates/carousel.html b/src/bepasty/templates/carousel.html index 3059cc5d..a1e56396 100644 --- a/src/bepasty/templates/carousel.html +++ b/src/bepasty/templates/carousel.html @@ -1,45 +1,39 @@ {% extends "_layout.html" %} +{% block content %} +{% for file in files %} + + {{ file['filename'] }} + +{% endfor %} +{% endblock content %} + + {% block extra_link %} + {% endblock %} -{% block content %} - -{% endblock content %} +{% block extra_script %} + + +{% endblock %} diff --git a/src/bepasty/templates/display.html b/src/bepasty/templates/display.html index a4c51b4d..d950ca97 100644 --- a/src/bepasty/templates/display.html +++ b/src/bepasty/templates/display.html @@ -6,77 +6,72 @@ {% block content %}
-
-

+
+

{{ item.meta['filename'] }}

-
+
{% if not item.meta['locked'] or may(ADMIN) %} {% if is_list_item %} - - Carousel - +

+ + + + + Carousel + +

{% endif %} - - QR - - - Download - - - Inline - +

+ + QR + +

+

+ + Download + +

+

+ + Inline + +

{% endif %} {% if may(MODIFY) %} -
- -
-
- -
- {{ utils.input_filename(item.meta['filename']) }} -
-
-
- -
- {{ utils.input_contenttype(item.meta['type']) }} -
-
- -
-
- +

+ +

{% endif %} {% if may(DELETE) %} -
+ -
{% endif %} {% if may(ADMIN) %} {% if not item.meta['locked'] %} -
-
{% else %} -
-
{% endif %} {% endif %}
-
-

+

+

Type: {{ item.meta['type'] }}, Size: {{ item.meta['size'] }} bytes, SHA256: {{ item.meta['hash'] }}. @@ -95,6 +90,36 @@

+ +{% if may(MODIFY) %} + +{% endif %} + {% endblock content %} {% block extra_link %} @@ -108,12 +133,4 @@

-{% if may(MODIFY) %} - -{% endif %} {% endblock extra_script %} diff --git a/src/bepasty/templates/error.html b/src/bepasty/templates/error.html index e2ba1f65..9c1de6b8 100644 --- a/src/bepasty/templates/error.html +++ b/src/bepasty/templates/error.html @@ -2,13 +2,13 @@ {% block content %}
-
-

+
+

{{ heading }}

-

-
-
+ +
+
{{ body }}
diff --git a/src/bepasty/templates/filelist.html b/src/bepasty/templates/filelist.html index 7d440ec1..7837d91a 100644 --- a/src/bepasty/templates/filelist.html +++ b/src/bepasty/templates/filelist.html @@ -4,9 +4,5 @@ {% set page_title = 'All items' %} {% block content %} -
-
- {{ utils.filelist(files) }} -
-
+{{ utils.filelist(files) }} {% endblock content %} diff --git a/src/bepasty/templates/index.html b/src/bepasty/templates/index.html index cace9573..a61bf5c4 100644 --- a/src/bepasty/templates/index.html +++ b/src/bepasty/templates/index.html @@ -5,143 +5,115 @@ {% macro maximum_lifetime() -%} {% set default_maxlife_value = config.DEFAULT_MAXLIFE_VALUE or 1 %} {% set default_maxlife_unit = (config.DEFAULT_MAXLIFE_UNIT or 'MONTHS')|lower %} -
-
-
-
-
- -
+
+

+
+

+

+ + + +

{% endmacro %} {% block content %} {% if may(CREATE) %} -
-
+
+
-
- +
+
-
-
- {{ utils.input_contenttype() }} -
-
+
+

+ {{ utils.input_contenttype(contenttypes) }} +

+

{{ utils.input_filename() }} -

-
- -
+

+

+ +


- - {{ maximum_lifetime() }} - - - Drag and drop files here — or click to select files - - - - +
+ +
+ {{ maximum_lifetime() }} +
+
+ +
-
+
{% else %} -
-

bepasty, the Binary File Upload Site

-
+
+

bepasty

+

the binary file upload site

+
-
-
- +
+
+

Free and Nice

A pastebin for all the stuff,
not just for text.

-
- +
+

Free and Open Source

Bepasty is free and open source software.
Bepasty project on GitHub

-
- -

Awesome Code

-

- Powered by Python and - Flask. -

-
{% endif %} {% endblock content %} {% block extra_link %} - - - - +{% if may(CREATE) %} + +{% endif %} {% endblock %} {% block extra_script %} - - - - - - - - - - - - +{% if may(CREATE) %} + - - +{% endif %} {% endblock %} diff --git a/src/bepasty/templates/qr.html b/src/bepasty/templates/qr.html index 4db5f50f..aa5da0f2 100644 --- a/src/bepasty/templates/qr.html +++ b/src/bepasty/templates/qr.html @@ -3,6 +3,16 @@ {% block extra_link %} + + {% endblock extra_script %}