Skip to content

Fix unstyled UI on Windows: pin MIME types for web assets - #256

Open
zwanski-start wants to merge 1 commit into
AutoForgeAI:masterfrom
zwanski-start:swarm/issue-37-barbara
Open

zwanski-start wants to merge 1 commit into
AutoForgeAI:masterfrom
zwanski-start:swarm/issue-37-barbara

Conversation

@zwanski-start

Copy link
Copy Markdown

Closes #37

Problem

On Windows, Python's mimetypes module seeds its table from the registry (HKEY_CLASSES_ROOT\.css → Content Type). Editors and other installed software often rewrite these entries (e.g. .css → text/plain). Starlette's StaticFiles/FileResponse use mimetypes.guess_type(), so the built stylesheet comes back with the wrong Content-Type. The browser refuses to apply it, and the UI renders with no colors, as in the issue screenshot. The JS bundle still runs because .js was already overridden, only for that one extension, in server/routers/settings.py.

Change

  • New server/utils/mime_types.py with register_web_mime_types(). It sets the correct type for every asset extension Vite emits (css, js, mjs, json, map, html, svg, png, ico, webp, woff, woff2, wasm).
  • server/main.py calls it before mounting the static files.
  • Removed the old .js-only override from the settings router, where it had no reason to live.

Verification

  • New test_mime_types.py maps .css/.js to text/plain, the way a broken registry does, then checks that register_web_mime_types() restores the right types and that a StaticFiles mount serves text/css / text/javascript.
  • ruff check ., python test_security.py, pytest test_mime_types.py test_ws_security.py, npm run build and npm run lint all pass. Lint gives 6 warnings and 0 errors; I left the warnings alone.
  • Manual check: built the UI and ran the real server.main:app on port 5443 with .css/.js mapped to text/plain before startup. curl -I showed text/css and text/javascript. In Playwright the stylesheet loaded (322 rules) and the page was fully styled.

Assumptions

  • I can't reproduce on a real Windows machine here. The diagnosis fits the reported symptoms: JS runs but CSS is missing, on Windows only. This is also the known Windows-registry mimetypes problem that the existing .js fix was written for.
  • The branch is pushed from the fork zwanski-start/autoforge because the account has only read access to the upstream repo.

🤖 Generated with Claude Code

Python's mimetypes reads the Windows registry, which third-party software
often rewrites so .css (or .js) maps to text/plain. Starlette then serves
the built stylesheet with the wrong Content-Type and browsers refuse it,
leaving the UI unstyled. Only .js was previously overridden (in the
settings router); register all web asset types centrally in main.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zwanski-start

Copy link
Copy Markdown
Author

🔍 QA report: ✅ Passed

Tester: Ada (cubefarm agent, in a fresh session) · Round: 1 · Author: Barbara

I reproduced issue #37 on master by simulating a Windows registry that maps .css/.js to text/plain: the stylesheet was served as text/plain, Chromium loaded it with 0 rules and the UI rendered unstyled with no console errors, exactly as reported. On this PR the same setup serves text/css and text/javascript, the stylesheet loads (322 rules) and the UI is fully styled with a clean console. ruff, the security suites, the UI build and lint pass. The mypy errors (25) and pytest failures (21, all in test_devserver_security.py) are identical on master, and both new tests fail when the fix is disabled.

Check Details
✅ Issue repro on master (simulated Windows registry) Patched mimetypes so the registry read maps .css/.js to text/plain and knownfiles is empty, as on Windows. On master, curl showed index-*.css served as text/plain and JS as text/javascript (because of the old .js-only override). In Chromium the stylesheet had 0 cssRules, body background was transparent, the page was unstyled, and there were 0 console errors, matching the issue report.
✅ Fix verified on PR (same simulation) The real server.main:app on port 5571 served index-*.css as text/css, all JS bundles as text/javascript, and svg/png correctly. In the browser the stylesheet loaded with 322 rules, the body had its theme background and font, the page was fully styled, and the console was clean.
✅ New unit tests are meaningful test_mime_types.py: 2 passed. With register_web_mime_types turned into a no-op, both tests fail, so they catch a regression.
✅ Removal of .js override in settings router The .js mapping is now covered by register_web_mime_types(), which is called at module level in server/main.py before StaticFiles is mounted. No other mimetypes usage exists in the repo.
✅ Python lint/types/tests vs master baseline ruff: clean. mypy: 25 errors on both master and the PR, none in the changed files. pytest: 21 failures, all in test_devserver_security.py, the identical set on master; the PR adds 2 passing tests.
✅ Backend boots; REST + WebSocket /api/health, /api/settings, /api/settings/models, /api/projects and /api/setup/status return 200. An unknown project returns 404 and an invalid settings PATCH returns 422. /ws/projects/{name} connects and returns a JSON error for an unregistered project. No errors in the server log.
✅ Frontend build/lint tsc -b && vite build succeeded; ESLint reports 0 errors and the 6 existing warnings.
✅ Responsive layout (390x844) Styled at phone width, no console errors. Navigating to #/docs did not open the docs page (the screenshot shows the home page); this is unrelated to the PR.
⏭️ Kanban / DAG / agent stream rendering No registered projects in this environment and the change is backend-only (MIME registration), so I didn't exercise these views.
⏭️ UI Playwright e2e The PR does not touch UI code.
✅ Security / cross-platform security.py allowlist and localhost-only middleware untouched, no DB schema change, no new hardcoded OS paths. The fix is platform-independent: it overrides registry-derived types after mimetypes.init().
🧪 Commands run
Command Result
ruff check . All checks passed
mypy . 25 errors in 10 files, identical count on origin/master; none in changed files
python test_security.py ALL TESTS PASSED
python test_security_integration.py ALL INTEGRATION TESTS PASSED
python -m pytest -q 146 passed, 21 failed (master: 144 passed, same 21 failed in test_devserver_security.py)
python -m pytest -q test_mime_types.py 2 passed; 2 failed when register_web_mime_types is a no-op
npm --prefix ui run build success
npm --prefix ui run lint 0 errors, 6 warnings (existing)
python winsim.py (uvicorn server.main:app with simulated Windows registry) on master:5572 and PR:5571 + curl -w content_type master: CSS text/plain; PR: CSS text/css, JS text/javascript
curl REST routes + websockets client to /ws/projects/nonexistent 200/404/422 as expected; WS connected and returned a JSON error

No browser screenshots were taken in this round.

Posted by cubefarm · merges automatically once GitHub's checks pass

cabana8471-arch pushed a commit to cabana8471-arch/autoforge that referenced this pull request Nov 8, 2026
Adaptation: AutoForgeAI#317 (issue AutoForgeAI#49) is covered by this change: its .css/.mjs/.svg/.json/.wasm lines are all in WEB_MIME_TYPES, and its importlib.reload test is dropped.

Integrates AutoForgeAI#256

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeH26pcsqoxPb3CDs8g1VN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Layout terrible on Windows 11

1 participant