Phase 3c: 3D diff view for IFC PR merges

After a PR merge completes, surface a 'View merge in 3D' button using the Phase 3b viewer. Use @thatopen/components highlighter to colour added, removed, and modified elements differently, driven by comparing the merged IFC against either parent. Requires Phase 3b viewer (ifcurl-i1s) to be working.

Rendered diff already implemented in footer.tmpl Case 3; static PNG via /render_diff endpoint, not interactive 3D


Forgejo build and deploy procedure: go build -C /home/bruno/src/forgejo -tags 'sqlite sqlite_unlock_notify' -ldflags "-X 'forgejo.org/modules/setting.StaticRootPath=/usr/share/forgejo'" -o /home/bruno/src/forgejo/forgejo . — then sudo cp forgejo /usr/bin/forgejo. StaticRootPath=/usr/share/forgejo. CustomPath=/var/lib/forgejo/custom. Custom templates go to /var/lib/forgejo/custom/templates/custom/ (e.g. footer.tmpl → /var/lib/forgejo/custom/templates/custom/footer.tmpl). Custom static files go to /var/lib/forgejo/custom/public/assets/ (NOT /etc/forgejo/public/assets/) and are served at /assets/*.


toRawUrl in viewer-url.js must support GitHub, GitLab, Forgejo/Gitea (codeberg.org, self-hosted), and localhost. Tests in forgejo/tests/viewer-url.test.js cover all four. The localhost check (http vs https) is based on host.startsWith('localhost'), so subdomain.localhost gets https.


ifcurl service names are ifcurl-api.service and ifcurl-render.service (not ifcurl.service). Install path for Python package: /opt/ifcurl/lib/python3.14/site-packages/ifcurl/. Python binary: /opt/ifcurl/bin/python3.