diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index a6835ca..dcf8c4b 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -4,7 +4,7 @@ {"id":"ifcurl-4l7","title":"viewer.html: scale= URLs don't load in orthographic mode","description":"applyCameraParam() reads the camera= and fov= parameters from the URL and applies them, but never reads scale= or switches the camera to orthographic (parallel) projection. URLs like ?camera=...\u0026scale=50 will load in perspective mode instead of orthographic. The OBC OrthoPerspectiveCamera can switch modes, but the code path to set parallel projection and apply the scale value is missing from applyCameraParam.","status":"closed","priority":1,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T09:04:51Z","created_by":"Bruno Postle","updated_at":"2026-04-24T10:56:29Z","started_at":"2026-04-24T10:50:19Z","closed_at":"2026-04-24T10:56:29Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-362","title":"Add missing server-config files: gitconfig-ifcmerge and gitattributes","description":"forgejo/README.md documents deploying two files that don't exist in the repo: forgejo/server-config/gitconfig-ifcmerge and forgejo/server-config/gitattributes. Both are required for the ifcmerge git merge driver to work in bare Forgejo repos. The README even includes 'sudo cp forgejo/server-config/gitattributes /etc/gitattributes' and 'sudo git config --system include.path .../gitconfig-ifcmerge', but the files themselves are absent. Anyone following the deployment instructions will hit a 'file not found' error. Need to create both files with the content described in the README.","status":"closed","priority":1,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T09:04:31Z","created_by":"Bruno Postle","updated_at":"2026-04-24T10:51:47Z","started_at":"2026-04-24T10:50:15Z","closed_at":"2026-04-24T10:51:47Z","close_reason":"Files already exist: forgejo/server-config/gitconfig-ifcmerge and gitattributes are both present and correct. Missed in initial review.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-ntm","title":"Avoid full model reload when only view params change (camera, clip, visibility)","description":"When editing the ifc:// URL field in the viewer, any parameter change triggers a full fetch, parse, and 3D rebuild. This is unnecessary for parameters that only affect the rendered view — camera, clipping planes, visibility mode, selector highlighting — all of which can be applied to the already-loaded model without re-fetching the IFC file from the server. A full reload should only happen when the source changes: host, repo, ref, or path.","status":"closed","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-05-17T05:43:36Z","created_by":"Bruno Postle","updated_at":"2026-05-17T08:12:58Z","started_at":"2026-05-17T05:44:26Z","closed_at":"2026-05-17T08:12:58Z","close_reason":"Implemented in-place view updates via applyViewChanges(); toRawUrl comparison for source identity; cameraControls.update(0) flush before render","dependency_count":0,"dependent_count":0,"comment_count":0} -{"id":"ifcurl-c8t","title":"Black screen flash before screenshot transition on model reload","description":"When the model is re-fetched, a screenshot of the current view is taken and faded into the new model. However, immediately before the screenshot appears there is a brief black screen, breaking the seamless transition. The screenshot (or the last rendered frame) should be displayed immediately when a reload begins — before the WebGL context is torn down or the canvas is cleared — so that the old view stays visible without interruption until the fade-in completes.","status":"in_progress","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-05-17T05:43:06Z","created_by":"Bruno Postle","updated_at":"2026-05-17T08:16:30Z","started_at":"2026-05-17T08:16:30Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"id":"ifcurl-c8t","title":"Black screen flash before screenshot transition on model reload","description":"When the model is re-fetched, a screenshot of the current view is taken and faded into the new model. However, immediately before the screenshot appears there is a brief black screen, breaking the seamless transition. The screenshot (or the last rendered frame) should be displayed immediately when a reload begins — before the WebGL context is torn down or the canvas is cleared — so that the old view stays visible without interruption until the fade-in completes.","status":"closed","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-05-17T05:43:06Z","created_by":"Bruno Postle","updated_at":"2026-05-17T09:23:44Z","started_at":"2026-05-17T08:16:30Z","closed_at":"2026-05-17T09:23:44Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-eaf","title":"Preview image camera is offset ~5m laterally vs JS viewer","description":"When opening a Forgejo markdown preview image (rendered by Python service via /preview), the camera position appears ~5m to the left compared to the same URL opened in the JS viewer.\n\nConfirmed: the model geometry IS shifted, not just a display scaling issue.\n\nWhat's known:\n- render.py uses IfcOpenShell with use-world-coords=True (world-space vertices)\n- _apply_camera() sets PyVista camera directly in IFC coordinates (no axis conversion)\n- JS viewer uses toThree() to convert IFC (Z-up) → Three.js (Y-up) coords\n- Python test from source correctly fetches the file (fetch works, rendering is the issue)\n\nHypothesis to investigate:\n- Does IfcOpenShell's geom iterator output vertices in IFC world coords (Z-up) or does it apply an axis swap?\n- Does PyVista/VTK interpret the camera position in the same axis convention as the IFC geometry?\n- Is plotter.show() triggering a reset_camera() that overrides the explicitly set camera?\n\nTest URL: ifc://localhost/bruno/simple-ifc@6af61b5454ac10ee2651da2d7363f54faa4f5b70?path=_test_simple.ifc\u0026camera=13.4983,-3.5129,4.4906,-0.8673,-0.4901,-0.0869,0,0,1\u0026fov=60\n\nRelevant code: ifcurl/render.py _apply_camera() line 88, _render_iterator() line 269.","notes":"Investigation so far (2026-05-16):\n\nRoot cause hypothesis: web-ifc uses COORDINATE_TO_ORIGIN=True when loading IFC for the JS viewer. This moves the model's IFC WorldCoordinateSystem to Three.js origin. IfcOpenShell with use-world-coords=True gives raw IFC world coordinates. If COORDINATE_TO_ORIGIN applies a non-identity transform to this specific model, that explains the offset.\n\nModel coordinate facts:\n- WorldCoordinateSystem: at origin (0,0,0) with standard axes\n- Site/Building/Storey placements: all at origin (identity matrix)\n- Model Y range (IFC): -15.9 to 4.1m, bbox center Y=-5.9m\n- This ~5.9m Y-offset matches the reported ~5m lateral offset\n\nKey findings:\n- COORDINATE_TO_ORIGIN=True is set in IfcImporter.webIfcSettings (engine_fragment)\n- The autoCoordinate=true in FragmentsModels does NOT center a single model — only aligns subsequent models to the first one's coordinate system\n- MCP tool .mcp.json configured: ifcmcp installed at /usr/bin/ifcmcp\n\nNext step: use ifcmcp to inspect what web-ifc actually outputs for this model's bounding box vs IfcOpenShell. Need to determine: does web-ifc COORDINATE_TO_ORIGIN shift the model's geometry even when WorldCoordinateSystem is at origin?","status":"closed","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-05-16T00:13:00Z","created_by":"Bruno Postle","updated_at":"2026-05-16T11:30:35Z","started_at":"2026-05-16T11:03:44Z","closed_at":"2026-05-16T11:30:35Z","close_reason":"Fixed by adding webIfc: { COORDINATE_TO_ORIGIN: false } to ifcLoader.setup() in viewer.js. Root cause: web-ifc COORDINATE_TO_ORIGIN shifts all model geometry (by ~12m for this model's first-element position) but GetCoordinationMatrix returns identity, hiding the shift. Camera URL stores IFC world coords but model was in shifted space. Fix keeps model in IFC world coords (per spec §5) which matches IfcOpenShell's unshifted output in the Python renderer.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-b1c","title":"Implement OpenCDE BCF API on Forgejo","description":"Expose a BCF 3.0 REST API as a Python route group in the preview service, proxied at /bcf/3.0/ on the Forgejo hostname via nginx/Caddy.\n\n## Architecture\n\nPython routes added to service.py alongside the existing /preview and /bcf endpoints. No new Forgejo Go code — the service forwards the user's Authorization: Bearer header verbatim to Forgejo's REST API on localhost, so Forgejo's own auth middleware enforces per-user permissions. No service account or machine token required.\n\nBCF \u003c-\u003e ifc:// conversion stays entirely in Python: bcf.py (ifc:// -\u003e BCF, already exists) and the reverse direction implemented in ifcurl-l50.\n\n## Design\n\nThe BCF API is a stateless translation layer over Forgejo's REST API. No Forgejo recompilation, no schema changes, no new Go code.\n\n**Storage:** ifc:// URLs in issue and comment bodies are the viewpoint format. This is already what the viewer Issue button produces. The BCF\u003c-\u003eifc:// conversion is specified in SPECIFICATION.md §6.\n\n**Mapping:**\n\n| BCF resource | Forgejo |\n|---|---|\n| Project | Repository (project_id = owner/repo) |\n| Topic | Issue |\n| Topic guid | Derived from issue ID |\n| Comment | Issue comment |\n| Viewpoint | ifc:// URL found in issue/comment body |\n| Viewpoint guid | Derived from comment ID |\n| Snapshot | ifc:// URL proxied through preview service |\n\n**Auth:** BCF client sends Authorization: Bearer \u003cforgejo-token\u003e. Service forwards it to Forgejo REST API. No credential storage, no service token.\n\n**Read viewpoints:** scan issue/comment bodies for ifc:// URLs, convert each to a BCF viewpoint object on the fly via the §6 mapping.\n\n**Write viewpoints:** convert incoming BCF viewpoint to an ifc:// URL (ifcurl-l50), post as a new Forgejo comment.\n\n**Snapshot:** GET .../viewpoints/{guid}/snapshot proxies the stored ifc:// URL through the existing /preview endpoint.\n\n## Minimum viable endpoints\n\n- GET /bcf/3.0/projects\n- GET /bcf/3.0/projects/{id}\n- GET/POST /bcf/3.0/projects/{id}/topics\n- GET/PUT /bcf/3.0/projects/{id}/topics/{guid}\n- GET/POST /bcf/3.0/projects/{id}/topics/{guid}/comments\n- GET/POST /bcf/3.0/projects/{id}/topics/{guid}/viewpoints\n- GET /bcf/3.0/projects/{id}/topics/{guid}/viewpoints/{guid}\n- GET /bcf/3.0/projects/{id}/topics/{guid}/viewpoints/{guid}/snapshot\n- GET /bcf/3.0/projects/{id}/extensions (static minimal response)\n\n## Prerequisites\n\n- ifcurl-lwl: Foundation API discovery so BCF clients can locate this endpoint via OpenCDE auto-discovery\n- ifcurl-l50: BCF -\u003e ifc:// URL conversion needed for POST /viewpoints","status":"closed","priority":2,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-28T22:55:11Z","created_by":"Bruno Postle","updated_at":"2026-04-29T18:39:11Z","closed_at":"2026-04-29T18:39:11Z","close_reason":"Closed","dependencies":[{"issue_id":"ifcurl-b1c","depends_on_id":"ifcurl-l50","type":"blocks","created_at":"2026-04-29T18:44:06Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"ifcurl-b1c","depends_on_id":"ifcurl-lwl","type":"blocks","created_at":"2026-04-29T18:44:05Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":2,"dependent_count":0,"comment_count":0} {"id":"ifcurl-962","title":"forgejo: replace Go markdown extension with JS, eliminate required Go compile","description":"ifc_url.go is a Goldmark AST extension that requires patching and recompiling Forgejo to work. This is painful when dev environment (Fedora) differs from deployment (Docker) — compiler flags, static linking, build tags must all match. The goal is to make the ifc:// preview feature work with zero Go compilation, via pure JS asset deployment.\n\nPlan:\n1. Add querySelectorAll('a[href^=\"ifc://\"]') scanning to ifcurl.js — replace matched anchors with figure elements (preview image + viewer link + ifc:// URL). Goldmark already renders [title](ifc://...) as \u003ca href=\"ifc://...\"\u003etitle\u003c/a\u003e without any extension, so this covers all markdown link syntax.\n2. Change the Issue button in viewer.js to emit [view](ifc://...) markdown link syntax instead of a bare URL — so that user-generated issue bodies produce linkable anchors Goldmark will render, not plain text.\n3. The existing Go extension (ifc_url.go) and its markdown.go registration can be removed on the next Forgejo upgrade; the current patched binary continues to work in the meantime.\n\nAcceptance: after this change, deploying new ifcurl features only requires copying JS assets — no Forgejo rebuild needed.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-27T06:58:15Z","created_by":"Bruno Postle","updated_at":"2026-04-27T07:25:09Z","started_at":"2026-04-27T06:58:48Z","closed_at":"2026-04-27T07:25:09Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} @@ -50,7 +50,7 @@ {"id":"ifcurl-ei8","title":"Fix syncCameraUrl: convert WebGL coords to IFC world space","description":"syncCameraUrl in viewer.py currently records camera.position/direction/up directly from Three.js, which are in WebGL/Three.js coordinate space (Y-up, different scale/orientation than IFC). The ifc:// URL spec expects IFC world coordinates (Z-up, matching BCF viewpoint conventions). Need to find the IFC→Three.js transform that @thatopen/components applies when loading a model, then apply its inverse in syncCameraUrl before serialising the camera params. Repos cloned for reference: ~/src/engine_components and ~/src/engine_fragment.","status":"closed","priority":2,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-23T05:53:06Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:14:36Z","closed_at":"2026-04-23T06:14:36Z","close_reason":"Split into atomic tasks: ifcurl-ldk, ifcurl-j2s, ifcurl-cmd","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-i1s","title":"Test Phase 3b web viewer end-to-end","description":"After deploying the footer template (ifcurl-ach), test the full flow: open an .ifc file in Forgejo at localhost:3000, click 'View in 3D', verify the viewer loads the model. CDN versions (@thatopen/components 2.4.0, three 0.160.0, web-ifc 0.0.66) may need adjustment if the API has changed.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-23T05:48:01Z","created_by":"Bruno Postle","updated_at":"2026-04-23T20:05:04Z","started_at":"2026-04-23T19:54:56Z","closed_at":"2026-04-23T20:05:04Z","close_reason":"Closed","dependencies":[{"issue_id":"ifcurl-i1s","depends_on_id":"ifcurl-49o","type":"blocks","created_at":"2026-04-23T07:14:12Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"ifcurl-i1s","depends_on_id":"ifcurl-4yu","type":"blocks","created_at":"2026-04-23T07:14:11Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"ifcurl-i1s","depends_on_id":"ifcurl-ach","type":"blocks","created_at":"2026-04-23T06:48:10Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"ifcurl-i1s","depends_on_id":"ifcurl-ahd","type":"blocks","created_at":"2026-04-23T07:02:54Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":4,"dependent_count":1,"comment_count":0} {"id":"ifcurl-ach","title":"Deploy Phase 3b footer template to Forgejo","description":"The footer.tmpl file that injects a 'View in 3D' button on .ifc file pages has been written but not yet deployed. Deploy it to /etc/forgejo/templates/custom/footer.tmpl and verify the button appears when viewing an .ifc file on localhost:3000.","status":"closed","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-23T05:47:53Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:14:35Z","closed_at":"2026-04-23T06:14:35Z","close_reason":"Split into atomic tasks: ifcurl-ohq, ifcurl-49o","dependencies":[{"issue_id":"ifcurl-ach","depends_on_id":"ifcurl-ahd","type":"blocks","created_at":"2026-04-23T07:02:40Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} -{"id":"ifcurl-2dj","title":"Clearance representations visible as backfaces despite hide logic","description":"There is code to hide Clearance representations of elements, but they remain visible as backfaces — the back face of the clearance geometry is rendered through the model. The hide logic is not suppressing backface rendering, so the representation bleeds through.","status":"open","priority":3,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-05-17T08:45:41Z","created_by":"Bruno Postle","updated_at":"2026-05-17T08:45:41Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"id":"ifcurl-2dj","title":"Clearance representations visible as backfaces despite hide logic","description":"There is code to hide Clearance representations of elements, but they remain visible as backfaces — the back face of the clearance geometry is rendered through the model. The hide logic is not suppressing backface rendering, so the representation bleeds through.","status":"closed","priority":3,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-05-17T08:45:41Z","created_by":"Bruno Postle","updated_at":"2026-05-17T09:55:26Z","started_at":"2026-05-17T09:36:23Z","closed_at":"2026-05-17T09:55:26Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-yhp","title":"Query panel: clicking an element in the results list should highlight it in the 3D view","description":"When a selector query is active, the query panel lists matched elements. Clicking a row in this list should highlight (or isolate) that specific element in the 3D canvas, so the user can locate it spatially. This mirrors the click-to-inspect flow but driven from the panel rather than the canvas.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-05-17T08:45:31Z","created_by":"Bruno Postle","updated_at":"2026-05-17T08:45:31Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-6xq","title":"Query panel does not appear after selector resolves","description":"When a selector is set in the viewer and a query= parameter is also present, the query results table (showing GUID → value pairs) should appear in a side panel. This panel never appears. The applyQuery() function in viewer.js fetches /query from the ifcurl service and populates queryTbody, but the panel remains hidden. Needs investigation: check whether the query param is being passed correctly, whether the /query endpoint returns data, and whether queryPanel.style.display is being set.","status":"closed","priority":3,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-05-16T13:24:59Z","created_by":"Bruno Postle","updated_at":"2026-05-16T20:02:21Z","started_at":"2026-05-16T14:14:52Z","closed_at":"2026-05-16T20:02:21Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-hwk","title":"Meta panel: missing type counts, icon is hamburger not (i)","description":"The metadata panel works for storey isolation but has two issues: (1) The toolbar button is a hamburger icon, not an (i) icon as documented in CLAUDE.md check #17. Update the checklist text to match reality. (2) The type counts breakdown (IfcWall: N, IfcSlab: N, etc.) does not appear in the panel — only the storey list is shown. Investigate whether populateMetaPanel() is failing silently or the type classifier isn't running correctly.","status":"closed","priority":3,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-05-16T13:24:45Z","created_by":"Bruno Postle","updated_at":"2026-05-16T20:02:21Z","started_at":"2026-05-16T14:15:06Z","closed_at":"2026-05-16T20:02:21Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} @@ -120,6 +120,6 @@ {"id":"ifcurl-vam","title":"Phase 6: Bonsai offer ifc:// URL when saving linked model reference","description":"When saving an IFCDOCUMENTREFERENCE in Bonsai, offer the option to write the location as an ifc:// URL rather than a relative path. When a relative path would traverse above the repository root, prompt the user to convert it to an ifc:// URL. Requires Phase 4 federation (ifcurl-bqt).","status":"deferred","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-23T05:49:16Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:15:13Z","defer_until":"2026-12-01T00:00:00Z","dependencies":[{"issue_id":"ifcurl-vam","depends_on_id":"ifcurl-bqt","type":"blocks","created_at":"2026-04-23T06:49:23Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"ifcurl-9k2","title":"Phase 5: IFC Viewer integration (equivalent of Phase 4)","description":"Add ifc:// OS protocol handler and 'Copy view URL' button to the IFC Viewer desktop application, equivalent to the Bonsai Phase 4 integration. Depends on Phase 4 being complete as a reference implementation.","status":"deferred","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-23T05:49:08Z","created_by":"Bruno Postle","updated_at":"2026-04-23T06:15:12Z","defer_until":"2026-12-01T00:00:00Z","dependencies":[{"issue_id":"ifcurl-9k2","depends_on_id":"ifcurl-0oj","type":"blocks","created_at":"2026-04-23T06:49:14Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"ifcurl-prc","title":"Phase 3c: 3D diff view for IFC PR merges","description":"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.","status":"closed","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-23T05:48:30Z","created_by":"Bruno Postle","updated_at":"2026-04-26T17:15:07Z","closed_at":"2026-04-26T17:15:07Z","close_reason":"Rendered diff already implemented in footer.tmpl Case 3; static PNG via /render_diff endpoint, not interactive 3D","dependencies":[{"issue_id":"ifcurl-prc","depends_on_id":"ifcurl-i1s","type":"blocks","created_at":"2026-04-23T06:48:36Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} +{"_type":"memory","key":"forgejo-build-and-deploy-procedure-go-build-c","value":"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/*."} {"_type":"memory","key":"ifcurl-service-names-are-ifcurl-api-service-and","value":"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."} {"_type":"memory","key":"torawurl-in-viewer-url-js-must-support-github","value":"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."} -{"_type":"memory","key":"forgejo-build-and-deploy-procedure-go-build-c","value":"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/*."} diff --git a/forgejo/custom/public/assets/viewer.js b/forgejo/custom/public/assets/viewer.js index 9b8f57f..56f1a99 100644 --- a/forgejo/custom/public/assets/viewer.js +++ b/forgejo/custom/public/assets/viewer.js @@ -582,6 +582,51 @@ function setupClipper(components, world, clips) { clipper.onAfterDrag.add(() => syncCameraUrl(threeCamera)); } +// ----------------------------------------------------------------------- +// Strip Clearance representation geometry from an IFC STEP buffer so that +// door-swing and equipment-clearance volumes are never rendered. +// +// web-ifc disposes its IfcAPI after load, so context filtering must happen +// before the buffer reaches ifcLoader.load(). IFCSHAPEREPRESENTATION items +// are always bare entity refs (#NNN) with no nested parens, so the items +// list (…) is safe to match with a simple non-nested regex. +// ----------------------------------------------------------------------- +function removeClearanceGeometry(buffer) { + // Only process IFC-SPF (STEP Physical Format) files. + const sig = [73, 83, 79, 45, 49, 48]; // 'ISO-10' + if (buffer.length < 6 || sig.some((b, i) => buffer[i] !== b)) return buffer; + + const text = new TextDecoder().decode(buffer); + + // 1. Find subcontext IDs where ContextIdentifier = 'Clearance'. + const ctxIds = new Set(); + for (const m of text.matchAll( + /#(\d+)\s*=\s*IFCGEOMETRICREPRESENTATIONSUBCONTEXT\s*\(\s*'Clearance'/gi + )) ctxIds.add(m[1]); + if (!ctxIds.size) return buffer; + + // 2. Find IFCSHAPEREPRESENTATION IDs that reference those contexts. + const repIds = new Set(); + for (const ctxId of ctxIds) + for (const m of text.matchAll( + new RegExp(`#(\\d+)\\s*=\\s*IFCSHAPEREPRESENTATION\\s*\\(\\s*#${ctxId}\\s*,`, 'gi') + )) repIds.add(m[1]); + if (!repIds.size) return buffer; + + // 3. Empty the items list of each clearance shape representation. + let modified = text; + for (const repId of repIds) + modified = modified.replace( + new RegExp( + `(#${repId}\\s*=\\s*IFCSHAPEREPRESENTATION\\s*\\([^,]+,[^,]+,[^,]+,)\\([^)]*\\)`, + 'gi' + ), + '$1()' + ); + + return new TextEncoder().encode(modified); +} + // ----------------------------------------------------------------------- // Apply selector= by isolating matching IFC elements. // @@ -1243,6 +1288,8 @@ async function main() { return; } + buffer = removeClearanceGeometry(buffer); + statusEl.textContent = "Parsing IFC…"; try { const model = await ifcLoader.load(buffer, true, "model");