From 38161604f9d485f3ba71a4817e0088a2cc03f621 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 9 May 2026 11:09:55 +0000 Subject: [PATCH] Viewer: query field and results table (ifcurl-2iv, ifcurl-4u3) viewer-url.js: parseIfcUrl returns query field; buildIfcUrl gains optional fifth query param, included in the QS when non-empty. viewer.html: add 'query' text field (label + input) in toolbar row 2 after the visibility selector. Add #query-panel (fixed bottom-centre) with a scrollable table showing GlobalId | value rows, a header displaying the query path, and a close button. viewer.js: populate queryInput from parsed URL on load; include query in buildIfcUrl() wrapper; wire queryInput into Enter-key and focus/blur listeners. Add applyQuery(srcUrl, queryPath) which calls GET /query, populates the table, and shows the panel. Called from main() after applySelector when both selector and query params are present. viewer-url.test.js: add tests for parseIfcUrl query field, buildIfcUrl with query, and buildIfcUrl omitting empty query. https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn --- .beads/issues.jsonl | 6 +-- forgejo/custom/public/assets/viewer-url.js | 4 +- forgejo/custom/public/assets/viewer.html | 44 ++++++++++++++++ forgejo/custom/public/assets/viewer.js | 60 ++++++++++++++++++++-- forgejo/tests/viewer-url.test.js | 16 ++++++ 5 files changed, 123 insertions(+), 7 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 5c72589..18e6968 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -50,8 +50,8 @@ {"_type":"issue","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":"Claude","metadata":"{}"},{"issue_id":"ifcurl-i1s","depends_on_id":"ifcurl-4yu","type":"blocks","created_at":"2026-04-23T07:14:11Z","created_by":"Claude","metadata":"{}"},{"issue_id":"ifcurl-i1s","depends_on_id":"ifcurl-ach","type":"blocks","created_at":"2026-04-23T06:48:10Z","created_by":"Claude","metadata":"{}"},{"issue_id":"ifcurl-i1s","depends_on_id":"ifcurl-ahd","type":"blocks","created_at":"2026-04-23T07:02:54Z","created_by":"Claude","metadata":"{}"}],"dependency_count":4,"dependent_count":1,"comment_count":0} {"_type":"issue","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":"Claude","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"_type":"issue","id":"ifcurl-982","title":"Viewer: implement clash visibility mode display","description":"Implement visibility=clash in the Bonsai viewer. Selected elements (GUIDs pre-identified as clashing, typically sourced from the server /clash endpoint) should be displayed with a solid red clash-indicator style (full opacity, distinct red material) while all other elements render normally — equivalent to highlight mode but using red rather than orange for selected elements. The viewer should be able to construct ifc://...\u0026visibility=clash URLs from a set of GUID pairs returned by /clash. When exporting to BCF, clash is treated as highlight (BCF has no equivalent distinction).","status":"closed","priority":3,"issue_type":"feature","assignee":"Claude","owner":"noreply@anthropic.com","created_at":"2026-05-09T08:08:45Z","created_by":"Claude","updated_at":"2026-05-09T09:03:00Z","started_at":"2026-05-09T09:02:27Z","closed_at":"2026-05-09T09:03:00Z","close_reason":"Closed","dependencies":[{"issue_id":"ifcurl-982","depends_on_id":"ifcurl-k0x","type":"blocks","created_at":"2026-05-09T08:28:57Z","created_by":"Claude","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} -{"_type":"issue","id":"ifcurl-4u3","title":"Viewer: query creation and editing UI","description":"Add UI controls in the Bonsai viewer for composing and modifying queries. Users need: a text field for the selector expression, a text field for the query path (dot-notation, e.g. Pset_WallCommon.FireRating), and an Apply button that updates the active ifc:// URL and triggers a re-query. The UI should also provide a copy-URL button. Changing the selector or query fields should update the URL in the address bar and the displayed 3D view/table without requiring a full page reload.","status":"open","priority":3,"issue_type":"feature","owner":"noreply@anthropic.com","created_at":"2026-05-09T08:08:42Z","created_by":"Claude","updated_at":"2026-05-09T08:08:42Z","dependency_count":0,"dependent_count":0,"comment_count":0} -{"_type":"issue","id":"ifcurl-2iv","title":"Viewer: display query results in a table panel","description":"When an ifc:// URL contains a query parameter, the viewer (Bonsai) should call the preview service /query endpoint and display the returned {GlobalId: value} mapping in a table panel alongside the 3D view. The table should have columns for element GlobalId (or Name if resolvable from the model) and the queried value. When both query and camera/visibility parameters are present, show the 3D view and property table together. The table should update whenever the URL's selector or query changes.","status":"open","priority":3,"issue_type":"feature","owner":"noreply@anthropic.com","created_at":"2026-05-09T08:08:39Z","created_by":"Claude","updated_at":"2026-05-09T08:08:39Z","dependencies":[{"issue_id":"ifcurl-2iv","depends_on_id":"ifcurl-8rf","type":"blocks","created_at":"2026-05-09T08:08:49Z","created_by":"Claude","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"ifcurl-4u3","title":"Viewer: query creation and editing UI","description":"Add UI controls in the Bonsai viewer for composing and modifying queries. Users need: a text field for the selector expression, a text field for the query path (dot-notation, e.g. Pset_WallCommon.FireRating), and an Apply button that updates the active ifc:// URL and triggers a re-query. The UI should also provide a copy-URL button. Changing the selector or query fields should update the URL in the address bar and the displayed 3D view/table without requiring a full page reload.","status":"closed","priority":3,"issue_type":"feature","assignee":"Claude","owner":"noreply@anthropic.com","created_at":"2026-05-09T08:08:42Z","created_by":"Claude","updated_at":"2026-05-09T11:09:43Z","started_at":"2026-05-09T11:06:39Z","closed_at":"2026-05-09T11:09:43Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"ifcurl-2iv","title":"Viewer: display query results in a table panel","description":"When an ifc:// URL contains a query parameter, the viewer (Bonsai) should call the preview service /query endpoint and display the returned {GlobalId: value} mapping in a table panel alongside the 3D view. The table should have columns for element GlobalId (or Name if resolvable from the model) and the queried value. When both query and camera/visibility parameters are present, show the 3D view and property table together. The table should update whenever the URL's selector or query changes.","status":"closed","priority":3,"issue_type":"feature","assignee":"Claude","owner":"noreply@anthropic.com","created_at":"2026-05-09T08:08:39Z","created_by":"Claude","updated_at":"2026-05-09T11:09:43Z","started_at":"2026-05-09T11:06:38Z","closed_at":"2026-05-09T11:09:43Z","close_reason":"Closed","dependencies":[{"issue_id":"ifcurl-2iv","depends_on_id":"ifcurl-8rf","type":"blocks","created_at":"2026-05-09T08:08:49Z","created_by":"Claude","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"ifcurl-xfi","title":"Implement OpenCDE document picker (select-documents flow)","description":"The OpenCDE Documents API defines a browser-redirect flow for document discovery. Implement POST /documents/1.0/select-documents which returns a select_documents_url. That URL serves a small HTML page: owner/repo selector then file-tree browser. When user selects an IFC file, generate the document_id and redirect to the callback_url with document_ids[] params.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-29T20:38:16Z","created_by":"Bruno Postle","updated_at":"2026-04-29T20:38:16Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"ifcurl-bil","title":"Implement OpenCDE OAuth2 proxy routes","description":"The OpenCDE Foundation API requires OAuth2 endpoints. Forgejo already has OAuth2 support — proxy through to it. Routes needed: GET /foundation/1.0/oauth2/auth_url (return Forgejo OAuth2 auth URL), POST /foundation/1.0/oauth2/token (proxy code exchange to Forgejo token endpoint), POST /foundation/1.0/oauth2/token_refresh (proxy refresh). Requires a one-time step of registering an OAuth2 app in Forgejo admin and storing client_id/client_secret as env vars.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-29T20:38:15Z","created_by":"Bruno Postle","updated_at":"2026-04-29T20:38:15Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"ifcurl-k3v","title":"Add GET /documents/1.0/document-metadata/{document_id}","description":"The OpenCDE Documents API spec includes a GET endpoint for individual file metadata. Implement it: decode the document_id, fetch the file's latest info from Forgejo (name, size, last-modified), return as a metadata response.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-29T20:38:13Z","created_by":"Bruno Postle","updated_at":"2026-04-29T21:25:07Z","started_at":"2026-04-29T21:22:34Z","closed_at":"2026-04-29T21:25:07Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} @@ -118,5 +118,5 @@ {"_type":"issue","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":"Claude","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"_type":"issue","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":"Claude","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"_type":"issue","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":"Claude","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} -{"_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/*."} +{"_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."} diff --git a/forgejo/custom/public/assets/viewer-url.js b/forgejo/custom/public/assets/viewer-url.js index 920fc4a..75a3bb2 100644 --- a/forgejo/custom/public/assets/viewer-url.js +++ b/forgejo/custom/public/assets/viewer-url.js @@ -28,17 +28,19 @@ export function parseIfcUrl(raw) { host, userAt, repoSuffix, ref, path: qs.get("path") ?? "", selector: qs.get("selector") ?? "", + query: qs.get("query") ?? "", camera: qs.get("camera") ?? "", fov: qs.get("fov") ?? "", scale: qs.get("scale") ?? "", }; } -export function buildIfcUrl(repo, ref, path, selector) { +export function buildIfcUrl(repo, ref, path, selector, query) { if (!repo || !ref) return null; const qs = new URLSearchParams(); if (path) qs.set("path", path); if (selector) qs.set("selector", selector); + if (query) qs.set("query", query); const qsStr = qs.toString().replace(/%2C/g, ",").replace(/%2B/g, "+").replace(/%24/g, "$"); return `ifc://${repo}@${ref}${qsStr ? "?" + qsStr : ""}`; } diff --git a/forgejo/custom/public/assets/viewer.html b/forgejo/custom/public/assets/viewer.html index 4d96cb9..0f4b334 100644 --- a/forgejo/custom/public/assets/viewer.html +++ b/forgejo/custom/public/assets/viewer.html @@ -111,6 +111,33 @@ } .meta-row.storey-row.active .meta-name { color: #a8c8ff; } .meta-count { font-size: 10px; color: #808088; flex-shrink: 0; } + /* Query results panel */ + #query-panel { + position: fixed; bottom: 44px; left: 50%; transform: translateX(-50%); + z-index: 10; + background: rgba(28,28,30,0.92); backdrop-filter: blur(8px); + border: 1px solid rgba(255,255,255,0.12); border-radius: 6px; + padding: 8px 10px; min-width: 320px; max-width: 640px; + max-height: 200px; overflow-y: auto; + } + #query-panel-header { + display: flex; align-items: center; justify-content: space-between; + margin-bottom: 6px; + } + #query-panel-header span { + font-size: 10px; color: #606068; + text-transform: uppercase; letter-spacing: 0.06em; + } + #query-table { width: 100%; border-collapse: collapse; font-size: 11px; } + #query-table th { + text-align: left; color: #808088; font-weight: normal; + padding: 1px 6px 4px 0; border-bottom: 1px solid rgba(255,255,255,0.08); + } + #query-table td { + padding: 2px 6px 2px 0; color: #f0f0f0; font-family: monospace; + border-bottom: 1px solid rgba(255,255,255,0.05); + max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + } /* Element properties panel */ #props-panel { position: fixed; bottom: 44px; right: 12px; z-index: 10; @@ -194,6 +221,13 @@ +
+ + +
+