From be1395649c8b173d4c70b5f899a2455aa1e47a13 Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Sun, 26 Apr 2026 08:17:34 +0100 Subject: [PATCH] viewer: fix visibility modes, add dropdown, zoom-all, and load transition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - visibility=highlight: apply orange colour overlay to selected elements (all others visible normally) using FragmentsManager.highlight() directly — OBC.Highlighter is in @thatopen/components-front which is not installed - visibility=ghost: dim non-selected elements to 15% grey opacity - visibility=isolate: unchanged (hider.isolate) - Add 'show' dropdown (highlight/ghost/isolate) that appears when a selector is set; changing it reloads with the new visibility= param - Add 'fit' button: calls cameraControls.fitToBox(model.box, true) - Smooth load transition: capture canvas snapshot (preserveDrawingBuffer) before navigation, display it as an overlay on the new page, fade it out once the model has rendered Co-Authored-By: Claude Sonnet 4.6 --- .beads/issues.jsonl | 9 +- forgejo/custom/public/assets/viewer.html | 141 ++++++++++++++++++++--- 2 files changed, 135 insertions(+), 15 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 97224b1..83a2e65 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -43,9 +43,14 @@ {"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-21x","title":"viewer: smooth transition when URL settings change","description":"Changing the visibility dropdown, selector, or other settings triggers a full page reload, causing the screen to go blank for several seconds while the model re-fetches and re-parses. Options: (a) CSS fade-in on load only — minimal change; (b) capture canvas snapshot before navigate and show as placeholder during reload; (c) in-place model reload without page navigation — best UX but requires refactoring main() to support hot-reload. Option (b) requires preserveDrawingBuffer:true in the WebGL renderer.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-26T07:13:24Z","created_by":"Bruno Postle","updated_at":"2026-04-26T07:17:15Z","started_at":"2026-04-26T07:15:10Z","closed_at":"2026-04-26T07:17:15Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} +{"id":"ifcurl-npy","title":"viewer: add zoom-all / reset-view button","description":"When the camera has panned or zoomed away from the building, there is no way to get back to a full view of the model. Add a 'zoom all' or 'reset view' button in the toolbar that calls cameraControls.fitToBox(model.box, true) to fit the entire model back in view.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-26T07:12:59Z","created_by":"Bruno Postle","updated_at":"2026-04-26T07:17:14Z","started_at":"2026-04-26T07:15:07Z","closed_at":"2026-04-26T07:17:14Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} +{"id":"ifcurl-8yw","title":"viewer: add visibility dropdown when selector is active","description":"When a selector is set in the viewer toolbar, show a drop-down next to the selector input to choose between highlight (ghost non-selected), ghost (alias for highlight), and isolate (hide non-selected). Currently visibility can only be set by editing the raw URL.","status":"closed","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-26T06:57:52Z","created_by":"Bruno Postle","updated_at":"2026-04-26T07:17:13Z","closed_at":"2026-04-26T07:17:13Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} +{"id":"ifcurl-olo","title":"viewer: 'View in 3D' link should open in a new tab","description":"The link to the viewer should open in a new tab (target=_blank) so the user stays on the Forgejo page. It previously opened in a new window but now opens in the same window, causing the user to navigate away from Forgejo.","status":"open","priority":3,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-26T06:24:44Z","created_by":"Bruno Postle","updated_at":"2026-04-26T06:24:44Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"id":"ifcurl-mts","title":"viewer: selector text box too narrow","description":"The selector input field in the viewer UI is not wide enough, making it awkward to type or read selector expressions. It should be wider to accommodate typical selector strings like 'IfcWall, Name=\"Core Wall\"'.","status":"open","priority":3,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-26T06:22:33Z","created_by":"Bruno Postle","updated_at":"2026-04-26T06:22:33Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-irp","title":"viewer: no render_diff preview shown on Forgejo commit pages","description":"When viewing a commit in Forgejo that contains .ifc file changes (e.g. /bruno/simple-ifc/commit/61e64e05d783e621d77869feadfc218a0858c499), no 3D diff preview image is injected. The footer.tmpl is supposed to detect .ifc diffs on commit pages and inject an img tag pointing to /render_diff. Either the detection is not firing or the render_diff request is failing.","status":"closed","priority":3,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-25T22:17:59Z","created_by":"Bruno Postle","updated_at":"2026-04-25T22:57:57Z","closed_at":"2026-04-25T22:57:57Z","close_reason":"Fixed by render_diff fixes in commit 837a9d1","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-3sz","title":"preview: visibility=highlight renders non-selected elements as opaque grey instead of transparent grey","description":"In the rendered PNG, visibility=highlight shows selected elements normally but non-selected elements appear as opaque grey rather than transparent grey (semi-transparent). The highlight mode should make non-selected elements translucent so the selected elements are visually emphasised against a faded context.","status":"closed","priority":3,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-25T22:15:42Z","created_by":"Bruno Postle","updated_at":"2026-04-25T23:03:49Z","started_at":"2026-04-25T22:59:19Z","closed_at":"2026-04-25T23:03:49Z","close_reason":"Not a bug — opaque grey is the model's actual IFC material colors displayed normally in highlight mode, which is correct per spec","dependency_count":0,"dependent_count":0,"comment_count":0} -{"id":"ifcurl-d7m","title":"viewer: visibility=highlight renders as isolate (non-selected elements should be transparent grey)","description":"In the viewer, visibility=highlight and visibility=isolate produce identical results — only selected elements are shown. The correct behaviour for highlight is to show selected elements normally and render all other elements as transparent grey. Isolate should hide non-selected elements entirely. The preview service already handles both modes correctly.","notes":"First fix attempt (commit 7aa4ef8) used highlightByID('select', ...) but selected elements still hidden — highlightByID may not prevent hider from hiding them, or the Highlighter 'select' style still isolates. Needs further investigation into OBC Highlighter vs Hider interaction.","status":"open","priority":3,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-25T22:15:41Z","created_by":"Bruno Postle","updated_at":"2026-04-25T23:25:07Z","started_at":"2026-04-25T22:59:21Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"id":"ifcurl-d7m","title":"viewer: visibility=highlight renders as isolate (non-selected elements should be transparent grey)","description":"In the viewer, visibility=highlight and visibility=isolate produce identical results — only selected elements are shown. The correct behaviour for highlight is to show selected elements normally and render all other elements as transparent grey. Isolate should hide non-selected elements entirely. The preview service already handles both modes correctly.","notes":"First fix attempt (commit 7aa4ef8) used highlightByID('select', ...) but selected elements still hidden — highlightByID may not prevent hider from hiding them, or the Highlighter 'select' style still isolates. Needs further investigation into OBC Highlighter vs Hider interaction.","status":"closed","priority":3,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-25T22:15:41Z","created_by":"Bruno Postle","updated_at":"2026-04-26T07:17:13Z","started_at":"2026-04-25T22:59:21Z","closed_at":"2026-04-26T07:17:13Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-xy9","title":"viewer: orthographic projection not implemented (works in preview)","description":"The viewer ignores the 'scale' parameter that selects orthographic projection. The preview service renders orthographic views correctly, but the 3D viewer in the browser always shows perspective. The viewer needs to switch to an orthographic camera when the IFC URL contains 'scale=' instead of 'fov='.","status":"open","priority":3,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-25T22:15:38Z","created_by":"Bruno Postle","updated_at":"2026-04-25T22:15:38Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-a9x","title":"viewer: typing in selector box does not filter or apply selector","description":"Typing an IfcOpenShell selector expression (e.g. 'IfcWall') into the selector input box has no effect. The selector should be applied to the model, updating the view to show only matching elements.","status":"closed","priority":3,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-25T21:37:17Z","created_by":"Bruno Postle","updated_at":"2026-04-25T22:36:10Z","closed_at":"2026-04-25T22:36:10Z","close_reason":"selector box now working in viewer","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"ifcurl-66h","title":"viewer: browse branches/tags button does not navigate to selected ref","description":"The browse branches and tags button does not appear to do anything when clicked. The ref input box has a datalist populated with heads/main but selecting a branch/tag and clicking browse does not navigate to that ref.","status":"open","priority":3,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-25T21:37:16Z","created_by":"Bruno Postle","updated_at":"2026-04-25T21:37:16Z","dependency_count":0,"dependent_count":0,"comment_count":0} @@ -92,4 +97,4 @@ {"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":"open","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-23T05:48:30Z","created_by":"Bruno Postle","updated_at":"2026-04-23T05:48:30Z","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 /etc/forgejo/public/assets/ and are served at /assets/*. /var/lib/forgejo is root/forgejo-only. /etc/forgejo has conf/ public/ templates/ but only public/assets/ is used for static files."} +{"_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.html b/forgejo/custom/public/assets/viewer.html index 13c2850..bfbeff1 100644 --- a/forgejo/custom/public/assets/viewer.html +++ b/forgejo/custom/public/assets/viewer.html @@ -69,6 +69,13 @@ pointer-events: none; } #status:empty { display: none; } + @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } + body { animation: fadeIn 0.2s ease; } + #snapshot-overlay { + position: fixed; inset: 0; top: var(--toolbar-h, 72px); z-index: 4; + background: #1c1c1e center/contain no-repeat; + transition: opacity 0.5s ease; + } body.drag-over::after { content: "Drop ifc:// URL or .bcf file"; position: fixed; inset: 0; z-index: 100; @@ -142,6 +149,7 @@ spellcheck="false"> + @@ -179,6 +187,14 @@ title="Type-name union: IfcWall, IfcWall+IfcSlab. Property/attribute filters are applied server-side only." spellcheck="false"> +