From ac3a15bfaa067cd72e0d8b4b603f682178e6ab4a Mon Sep 17 00:00:00 2001 From: Ron Pierce Date: Mon, 1 Jun 2026 08:00:40 -0700 Subject: [PATCH] fix(web): repair array-table.js syntax error and version static assets (#357) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues left the v3 web UI's Overview (and other Alpine-driven tabs) blank: 1. array-table.js had two safeSetHTML(target, `...`) calls that closed the template-literal argument with `; instead of `); — a SyntaxError that aborts the script and halts widget registration / Alpine initialization. 2. Static assets are served `Cache-Control: public, max-age=31536000, immutable` but were referenced without a cache-busting version (the header comment assumed "versioning via query params", which was only ever applied by hand to app.css). So edited JS/CSS never reached browsers — including fix #1. Add a Flask url_defaults hook that appends each static file's mtime as a ?v= param to every url_for('static', ...), so changed files get a new URL and are refetched while unchanged files keep the long immutable cache. Drop the now redundant manual ?v= on app.css. Co-authored-by: Claude Opus 4.8 --- web_interface/app.py | 16 ++++++++++++++++ .../static/v3/js/widgets/array-table.js | 4 ++-- web_interface/templates/v3/base.html | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/web_interface/app.py b/web_interface/app.py index f8ffcbd3..d4d4c20f 100644 --- a/web_interface/app.py +++ b/web_interface/app.py @@ -391,6 +391,22 @@ def captive_portal_redirect(): # Redirect to lightweight captive portal setup page (not the full UI) return redirect(url_for('pages_v3.captive_setup'), code=302) +# Append a content-version query param (file mtime) to every static URL so the +# long-lived `immutable` cache (see add_security_headers below) is actually safe: +# when a static file changes its URL changes, so browsers refetch it. Without +# this, edited JS/CSS were served immutable under an unchanging URL and never +# reached clients until a manual cache clear. +@app.url_defaults +def add_static_version(endpoint, values): + if endpoint == 'static' and values.get('filename'): + try: + file_path = os.path.join(app.static_folder, values['filename']) + values['v'] = int(os.path.getmtime(file_path)) + except OSError: + # File missing (e.g. plugin asset not yet installed) — skip versioning. + pass + + # Add security headers and caching to all responses @app.after_request def add_security_headers(response): diff --git a/web_interface/static/v3/js/widgets/array-table.js b/web_interface/static/v3/js/widgets/array-table.js index 20078fa2..77e82edf 100644 --- a/web_interface/static/v3/js/widgets/array-table.js +++ b/web_interface/static/v3/js/widgets/array-table.js @@ -440,7 +440,7 @@

Advanced Properties

- `; + `); const body = document.createElement('div'); body.className = 'px-5 py-4 space-y-4'; @@ -512,7 +512,7 @@ `; + class="px-4 py-2 text-sm bg-blue-600 hover:bg-blue-700 text-white rounded-md">Save`); // Save handler footer.querySelector('#array-row-editor-save').onclick = function() { diff --git a/web_interface/templates/v3/base.html b/web_interface/templates/v3/base.html index 922dab42..841ed4c6 100644 --- a/web_interface/templates/v3/base.html +++ b/web_interface/templates/v3/base.html @@ -867,7 +867,7 @@ - +