diff --git a/server/mapcontrol_server/main.py b/server/mapcontrol_server/main.py
index 0a450b3..1397f6f 100644
--- a/server/mapcontrol_server/main.py
+++ b/server/mapcontrol_server/main.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+import hashlib
import json
import logging
import os
@@ -11,6 +12,19 @@
_SERVER_START_TIME = time.time()
+STATIC_DIR = Path(__file__).parent / "static"
+
+
+def _ui_asset_url(root_path: str, filename: str) -> str:
+ """Keep a browser's UI scripts/styles in sync with the served map shell.
+
+ Old unversioned URLs can remain fresh in a browser cache across deployments.
+ A content hash changes the URL when the file changes; unchanged builds keep
+ their cache hits. These small files are read when serving the HTML, so local
+ static edits also receive a new version without requiring a process restart.
+ """
+ digest = hashlib.sha256((STATIC_DIR / filename).read_bytes()).hexdigest()[:16]
+ return f"{root_path}/static/{filename}?v={digest}"
import uvicorn
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request
@@ -439,15 +453,15 @@ def _thumbnail_url(entry) -> str:
# and publishes window.ESIPMap for bespoke UIs. The default outfit
# (esip-embed.css/js) is loaded only when ui == "default".
ui_head = (
- f''
+ f''
if ui == "default" else ""
)
- ui_body = f''
+ ui_body = f''
if inspect_enabled:
- ui_head += f''
- ui_body += f''
+ ui_head += f''
+ ui_body += f''
if ui == "default":
- ui_body += f''
+ ui_body += f''
# Initial camera resolution (server-side). The page used to ALWAYS boot at
@@ -3371,7 +3385,9 @@ class DrawToolbarControl {{