diff --git a/scripts/build_html.py b/scripts/build_html.py
index f2cfb7e9..32847760 100644
--- a/scripts/build_html.py
+++ b/scripts/build_html.py
@@ -4,27 +4,138 @@ import gzip
SRC_DIR = "src"
+
+def strip_js_comments(js: str) -> str:
+ """Remove JS comments while preserving string literals and URLs."""
+ result = []
+ i = 0
+ length = len(js)
+ while i < length:
+ # String literals — pass through unchanged
+ if js[i] in ('"', "'", "`"):
+ quote = js[i]
+ result.append(js[i])
+ i += 1
+ while i < length:
+ if js[i] == "\\" and i + 1 < length:
+ result.append(js[i : i + 2])
+ i += 2
+ elif js[i] == quote:
+ result.append(js[i])
+ i += 1
+ break
+ else:
+ result.append(js[i])
+ i += 1
+ # Block comment /* ... */
+ elif js[i] == "/" and i + 1 < length and js[i + 1] == "*":
+ end = js.find("*/", i + 2)
+ i = end + 2 if end != -1 else length
+ # Line comment // ...
+ elif js[i] == "/" and i + 1 < length and js[i + 1] == "/":
+ end = js.find("\n", i)
+ if end == -1:
+ i = length
+ else:
+ # Keep the newline to preserve line structure
+ result.append("\n")
+ i = end + 1
+ # Regex literal — pass through unchanged
+ # Heuristic: / after = ( , ; ! & | ? : [ { } ~ ^ or line start
+ elif js[i] == "/" and i > 0:
+ # Look back for operator context (skip whitespace)
+ j = i - 1
+ while j >= 0 and js[j] in " \t":
+ j -= 1
+ if j >= 0 and js[j] in "=(!,;:&|?[{}>~^+-*%":
+ result.append(js[i])
+ i += 1
+ while i < length:
+ if js[i] == "\\" and i + 1 < length:
+ result.append(js[i : i + 2])
+ i += 2
+ elif js[i] == "/":
+ result.append(js[i])
+ i += 1
+ # Regex flags
+ while i < length and js[i].isalpha():
+ result.append(js[i])
+ i += 1
+ break
+ elif js[i] == "[":
+ # Character class — / doesn't end regex inside []
+ result.append(js[i])
+ i += 1
+ while i < length and js[i] != "]":
+ if js[i] == "\\" and i + 1 < length:
+ result.append(js[i : i + 2])
+ i += 2
+ else:
+ result.append(js[i])
+ i += 1
+ else:
+ result.append(js[i])
+ i += 1
+ else:
+ result.append(js[i])
+ i += 1
+ else:
+ result.append(js[i])
+ i += 1
+ return "".join(result)
+
+
def minify_html(html: str) -> str:
# Tags where whitespace should be preserved
- preserve_tags = ['pre', 'code', 'textarea', 'script', 'style']
- preserve_regex = '|'.join(preserve_tags)
+ preserve_tags = ["pre", "code", "textarea"]
+ script_style_tags = ["script", "style"]
+ preserve_regex = "|".join(preserve_tags)
+ script_style_regex = "|".join(script_style_tags)
- # Protect preserve blocks with placeholders
+ # Protect preserve blocks (pre/code/textarea) with placeholders
preserve_blocks = []
+
def preserve(match):
preserve_blocks.append(match.group(0))
- return f"__PRESERVE_BLOCK_{len(preserve_blocks)-1}__"
+ return f"__PRESERVE_BLOCK_{len(preserve_blocks) - 1}__"
- html = re.sub(rf'<({preserve_regex})[\s\S]*?\1>', preserve, html, flags=re.IGNORECASE)
+ html = re.sub(
+ rf"<({preserve_regex})[\s\S]*?\1>", preserve, html, flags=re.IGNORECASE
+ )
+
+ # Strip JS/CSS comments inside
+ const xhr = new XMLHttpRequest();
+ xhr.open('POST', '/move', true);
+
+ xhr.onload = function () {
+ if (xhr.status === 200) {
+ window.location.reload();
+ } else {
+ alert('Failed to move: ' + xhr.responseText);
+ }
+ closeMoveModal();
+ };
+
+ xhr.onerror = function () {
+ alert('Failed to move - network error');
+ closeMoveModal();
+ };
+
+ xhr.send(formData);
+ }
+ hydrate();
+