Skip to content

Commit 0733fbf

Browse files
Copilotlstein
andauthored
Fix app broken on Windows due to IE Compatibility Mode (#211)
* Initial plan * Fix IE Compatibility Mode errors breaking app on Windows - Add IECompatibilityMiddleware to FastAPI server that adds X-UA-Compatible: IE=edge header to all responses, preventing Edge/IE from entering IE Compatibility Mode which caused: * SCRIPT1028 (Swiper v11 modern JS syntax failure) * HTML1416 (-- inside HTML comments) * HTML1500 (self-closing non-void tag) - Add <meta http-equiv="X-UA-Compatible" content="IE=edge" /> to main.html as a belt-and-suspenders measure - Fix -- inside HTML comments in control-panel.html and search-panel.html (replace -- with : to avoid HTML1416 in SGML-strict parsers) - Fix self-closing <animateTransform /> in spinner.html with explicit closing tag to avoid HTML1500 - Add tests verifying X-UA-Compatible header on HTML and API responses Agent-Logs-Url: https://github.com/lstein/PhotoMapAI/sessions/907b9c95-1c90-4b09-b532-a2f69f736312 Co-authored-by: lstein <111189+lstein@users.noreply.github.com> * Show warning page on legacy Edge (EdgeHTML/Edge 44) with upgrade instructions Agent-Logs-Url: https://github.com/lstein/PhotoMapAI/sessions/b377bd0b-af3a-43f2-a1a9-b9a2da8b8ec9 Co-authored-by: lstein <111189+lstein@users.noreply.github.com> * Fix legacy Edge warning: use location.replace() instead of document.write() Agent-Logs-Url: https://github.com/lstein/PhotoMapAI/sessions/80699240-b634-4385-8889-b3ba010143ec Co-authored-by: lstein <111189+lstein@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lstein <111189+lstein@users.noreply.github.com>
1 parent 4edfd68 commit 0733fbf

File tree

8 files changed

+125
-5
lines changed

8 files changed

+125
-5
lines changed

photomap/backend/photomap_server.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from fastapi.responses import HTMLResponse
1515
from fastapi.staticfiles import StaticFiles
1616
from fastapi.templating import Jinja2Templates
17+
from starlette.middleware.base import BaseHTTPMiddleware
1718

1819
from photomap.backend.args import get_args, get_version
1920
from photomap.backend.config import get_config_manager
@@ -46,6 +47,23 @@
4647

4748
app.include_router(curation_router, prefix="/api/curation", tags=["curation"])
4849

50+
51+
class IECompatibilityMiddleware(BaseHTTPMiddleware):
52+
"""Add X-UA-Compatible header to every response.
53+
54+
This prevents Microsoft Edge and Internet Explorer from switching into
55+
IE Compatibility Mode, which breaks Swiper v11 (SCRIPT1028) and causes
56+
various HTML parsing errors (HTML1416, HTML1500).
57+
"""
58+
59+
async def dispatch(self, request: Request, call_next):
60+
response = await call_next(request)
61+
response.headers["X-UA-Compatible"] = "IE=edge"
62+
return response
63+
64+
65+
app.add_middleware(IECompatibilityMiddleware)
66+
4967
# Mount static files and templates
5068
static_path = get_package_resource_path("static")
5169
app.mount("/static", StaticFiles(directory=static_path), name="static")
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Browser not supported &mdash; PhotoMapAI</title>
7+
<style>
8+
body {
9+
font-family: Arial, sans-serif;
10+
background: #1a1a2e;
11+
color: #e0e0e0;
12+
display: flex;
13+
align-items: center;
14+
justify-content: center;
15+
min-height: 100vh;
16+
margin: 0;
17+
}
18+
.box {
19+
background: #16213e;
20+
border: 1px solid #0f3460;
21+
border-radius: 8px;
22+
padding: 40px;
23+
max-width: 540px;
24+
text-align: center;
25+
}
26+
h1 { color: #e94560; margin-top: 0; }
27+
p { line-height: 1.6; }
28+
a { color: #0f9b8e; }
29+
.steps { text-align: left; margin: 20px 0; padding-left: 20px; }
30+
.steps li { margin: 8px 0; }
31+
</style>
32+
</head>
33+
<body>
34+
<div class="box">
35+
<h1>Browser not supported</h1>
36+
<p>
37+
You are using <strong>Microsoft Edge (Legacy)</strong>, which does not
38+
support the modern JavaScript required by PhotoMapAI.
39+
</p>
40+
<p>Please upgrade to the new <strong>Microsoft Edge (Chromium)</strong>:</p>
41+
<ol class="steps">
42+
<li>
43+
Visit
44+
<a href="https://www.microsoft.com/en-us/edge" target="_blank">
45+
microsoft.com/edge
46+
</a>
47+
</li>
48+
<li>Click <strong>Download</strong> and run the installer</li>
49+
<li>After installation, open PhotoMapAI in the new Edge</li>
50+
</ol>
51+
<p>Chrome and Firefox are also fully supported.</p>
52+
</div>
53+
</body>
54+
</html>

photomap/frontend/templates/main.html

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,27 @@
33

44
<head>
55
<title id="slideshow_title">PhotoMap</title>
6-
<!-- ... (Head content remains the same) ... -->
6+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
77
<meta charset="UTF-8" />
88
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
99
<link rel="icon" type="image/x-icon" href="static/icons/favicon.ico" />
10+
11+
<!-- Detect legacy EdgeHTML (Edge ≤18 / Edge 44) before loading any ES2020 scripts.
12+
window.StyleMedia is unique to EdgeHTML and absent from Chromium-based Edge.
13+
window.location.replace() performs a full navigation so the original page
14+
never finishes loading, which avoids the document.write() race condition. -->
15+
<script>
16+
(function () {
17+
var isLegacyEdge = !!window.StyleMedia;
18+
if (!isLegacyEdge) {
19+
try { Function("x?.y"); } catch (e) { isLegacyEdge = true; }
20+
}
21+
if (isLegacyEdge) {
22+
window.location.replace("/static/unsupported-browser.html");
23+
}
24+
}());
25+
</script>
26+
1027
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
1128
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
1229
<script src="https://cdn.plot.ly/plotly-3.0.1.min.js" charset="utf-8"></script>

photomap/frontend/templates/modules/control-panel.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- HTML template for the "control panel" icons -- settings, delete, fullscreen, play/pause, etc. -->
1+
<!-- HTML template for the "control panel" icons: settings, delete, fullscreen, play/pause, etc. -->
22

33
<!-- Control Panel -->
44
<div id="controlPanel">

photomap/frontend/templates/modules/metadata-drawer.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<div id="overlayDrawer" class="overlay-drawer">
55
<div class="drawer-handle">
66
<svg class="drawer-arrow" viewBox="0 0 18 18">
7-
<path d="M7 14l5-5 5 5z" fill="currentColor" />
7+
<path d="M7 14l5-5 5 5z" fill="currentColor"></path>
88
</svg>
99
</div>
1010
</div>

photomap/frontend/templates/modules/search-panel.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- HTML template for the search icons -- show/hide semantic map, search by image similarity, search by text -->
1+
<!-- HTML template for the search icons: show/hide semantic map, search by image similarity, search by text -->
22
<!-- Search Panel -->
33
<div id="searchPanel">
44
<div class="search-panel-title">Search</div>

photomap/frontend/templates/modules/spinner.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
to="360 25 25"
2020
dur="1s"
2121
repeatCount="indefinite"
22-
/>
22+
></animateTransform>
2323
</circle>
2424
</svg>
2525
</div>

tests/backend/test_app.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,34 @@ def test_root_route(client):
1515
assert response.template.name == "main.html"
1616
assert "<title id=\"slideshow_title\">PhotoMap</title>" in response.text
1717

18+
19+
def test_ie_compatibility_header_on_html(client):
20+
"""The root page must carry X-UA-Compatible: IE=edge to prevent Edge/IE
21+
from switching into IE Compatibility Mode, which would break Swiper v11
22+
and cause SCRIPT1028 / HTML1416 / HTML1500 console errors."""
23+
response = client.get("/")
24+
assert response.headers.get("x-ua-compatible") == "IE=edge"
25+
26+
27+
def test_ie_compatibility_header_on_api(client):
28+
"""The X-UA-Compatible header should also be present on API responses."""
29+
response = client.get("/api/albums/")
30+
assert response.headers.get("x-ua-compatible") == "IE=edge"
31+
32+
33+
def test_legacy_edge_detection_script_present(client):
34+
"""The root page must include an inline script that detects legacy EdgeHTML
35+
(Edge ≤18 / Edge 44) and redirects to the upgrade-instructions page before
36+
any ES2020 library code runs."""
37+
response = client.get("/")
38+
assert "window.StyleMedia" in response.text
39+
assert "/static/unsupported-browser.html" in response.text
40+
41+
42+
def test_unsupported_browser_page_served(client):
43+
"""The static unsupported-browser page must be served and contain the
44+
upgrade link so that legacy Edge users see the instructions."""
45+
response = client.get("/static/unsupported-browser.html")
46+
assert response.status_code == 200
47+
assert "microsoft.com/edge" in response.text
48+

0 commit comments

Comments
 (0)