mirror of
https://github.com/brunopostle/ifcurl.git
synced 2026-07-12 02:08:13 +00:00
forgejo: PR diff page renders IFC diff images via footer.tmpl + Nginx proxy
Case 3 in footer.tmpl detects /{owner}/{repo}/pulls/{N}/files, fetches base/
head SHAs from the Forgejo API, then injects <img src="/render_diff?…"> below
the file header of each .ifc file in the diff. Returns early so Case 2 (history
page) does not also run on the PR diff page.
README documents the required Nginx proxy for /preview and /render_diff, and
clarifies that PREVIEW_SERVICE_URL is server-side-only (localhost:8000 is
correct for it); the browser-side diff images need the proxy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
96aa1a2377
commit
ec461239f4
3 changed files with 82 additions and 4 deletions
|
|
@ -2,11 +2,13 @@
|
||||||
{"id":"ifcurl-p60","title":"Viewer: no element click-to-identify — can't get GlobalId or properties","description":"There is no way to click on a mesh in the viewer and see which IFC element it represents. Reviewers can see geometry but cannot reference specific elements — they can't discover a GlobalId to put in a selector URL, can't see the element name/type/Psets, and can't say 'this specific wall' in a BCF or Forgejo comment. This breaks the collaboration workflow at its foundation: every useful feedback comment needs to identify specific elements. Need: click or hover on a surface to highlight it and show a properties panel (name, type, GlobalId, key Psets). The GlobalId should be copyable into the selector field or URL. The ThatOpen components library provides the tools for this (OBC.IfcRelationsIndexer, element property queries).","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:02:33Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:23Z","started_at":"2026-04-24T11:14:57Z","closed_at":"2026-04-24T11:47:23Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-p60","title":"Viewer: no element click-to-identify — can't get GlobalId or properties","description":"There is no way to click on a mesh in the viewer and see which IFC element it represents. Reviewers can see geometry but cannot reference specific elements — they can't discover a GlobalId to put in a selector URL, can't see the element name/type/Psets, and can't say 'this specific wall' in a BCF or Forgejo comment. This breaks the collaboration workflow at its foundation: every useful feedback comment needs to identify specific elements. Need: click or hover on a surface to highlight it and show a properties panel (name, type, GlobalId, key Psets). The GlobalId should be copyable into the selector field or URL. The ThatOpen components library provides the tools for this (OBC.IfcRelationsIndexer, element property queries).","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:02:33Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:23Z","started_at":"2026-04-24T11:14:57Z","closed_at":"2026-04-24T11:47:23Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"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-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-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-ndf","title":"gitconfig-ifcmerge: update to use ifcmerge --prioritise-local instead of swapping LOCAL/REMOTE","description":"The current gitconfig-ifcmerge uses the old technique of swapping the order of %A and %B arguments to preserve the base branch's STEP ID space:\n\n driver = ifcmerge %O %A %B %L (standard)\n driver = ifcmerge %O %B %A %L (ours variant)\n\nBonsai's current ifcgit code (tool/ifcgit.py, config_ifcmerge) uses the newer --prioritise-local flag instead:\n\n ifcmerge --prioritise-local $BASE $LOCAL $REMOTE $MERGED\n\nThe --prioritise-local flag tells ifcmerge to preserve LOCAL's STEP ID space without needing to swap argument order. This is cleaner, more readable, and follows the canonical ifcmerge interface.\n\nThe gitconfig-ifcmerge needs updating to:\n driver = ifcmerge --prioritise-local %O %A %B %A\n\n(where %A is both LOCAL and the output file — git merge drivers write their result to %A)\n\nThe ifcmerge_ours variant (for rebase direction where base branch arrives as %B) would become:\n driver = ifcmerge --prioritise-local %O %B %A %B\n\nVerify that the --prioritise-local flag exists in the version of ifcmerge being used before merging.","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-25T06:06:59Z","created_by":"Bruno Postle","updated_at":"2026-04-25T06:06:59Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
|
{"id":"ifcurl-qjd","title":"render_diff: auto-fit camera to changed elements, not whole model","description":"When rendering a diff of a large building model, the auto-fit camera fits to the entire head model. A wall added to one corner of a multi-storey building would appear as a tiny green speck. The camera should instead zoom to the bounding box of just the added + modified + removed elements so the changed area fills the viewport.\n\nImplementation sketch:\n- In render_diff(), after adding all shapes to plotter1, call plotter1.reset_camera() as now, but also compute the bounds of only the diff-coloured meshes (added + modified) and re-fit to those bounds.\n- For pass 2 (removed elements), the elements are already isolated in the iterator, so reset_camera() naturally fits to them. The pass 1 camera capture then needs to consider that pass 2 might be out of that frustum.\n- A combined approach: compute the bounding box union of added + modified + removed element meshes, then set the camera to fit that box with a small margin. This is the camera used for both passes.\n- pyvista Plotter has plotter.reset_camera(bounds=(xmin,xmax,ymin,ymax,zmin,zmax)) which can fit to an explicit box.\n- The bounds can be accumulated during the _add_shape loop for diff-coloured elements.\n\nAcceptance: a single modified wall in a large building renders with the wall clearly visible and filling most of the image.","status":"open","priority":2,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-04-25T06:06:39Z","created_by":"Bruno Postle","updated_at":"2026-04-25T06:06:39Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-zxs","title":"sandbox.py: add seccomp filter to block execve and socket in render subprocess","description":"The subprocess isolation in sandbox.py (ifcurl-vm5) prevents DoS but not RCE: if a crafted IFC file achieves code execution inside the render child, that child still has full access to execve and the network. A seccomp BPF filter applied at the start of _worker() would block the two most useful post-exploitation steps even under active code execution.\n\nThe filter should:\n- Block execve/execveat (cannot exec a shell or any binary)\n- Block socket() for AF_INET/AF_INET6/AF_UNIX (cannot open a reverse shell or connect to the Forgejo Unix socket)\n- Allow clone/fork (ifcopenshell's geom.iterator spawns its own worker processes)\n- Allow everything else needed by Python + ifcopenshell + pyvista\n\nThe python-seccomp library (pip install seccomp, wraps libseccomp) is the cleanest way to do this. The filter would be installed via resource.setrlimit companion code already present in sandbox._worker().\n\nThe main risk is over-filtering: if the seccomp policy blocks a syscall that ifcopenshell or pyvista needs, renders fail with EPERM. The policy needs to be validated against the full render path (including the geometry iterator subprocesses, which inherit the filter across fork).\n\nAcceptance criteria:\n- A render subprocess cannot exec /bin/sh even with arbitrary code execution (verified by replacing fn with a ctypes execve call — should get EPERM/killed, not a shell)\n- Normal renders continue to produce correct PNG output\n- The seccomp library is optional (graceful fallback if libseccomp is absent, e.g. on macOS or minimal containers)","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-24T20:47:00Z","created_by":"Bruno Postle","updated_at":"2026-04-24T20:47:00Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-zxs","title":"sandbox.py: add seccomp filter to block execve and socket in render subprocess","description":"The subprocess isolation in sandbox.py (ifcurl-vm5) prevents DoS but not RCE: if a crafted IFC file achieves code execution inside the render child, that child still has full access to execve and the network. A seccomp BPF filter applied at the start of _worker() would block the two most useful post-exploitation steps even under active code execution.\n\nThe filter should:\n- Block execve/execveat (cannot exec a shell or any binary)\n- Block socket() for AF_INET/AF_INET6/AF_UNIX (cannot open a reverse shell or connect to the Forgejo Unix socket)\n- Allow clone/fork (ifcopenshell's geom.iterator spawns its own worker processes)\n- Allow everything else needed by Python + ifcopenshell + pyvista\n\nThe python-seccomp library (pip install seccomp, wraps libseccomp) is the cleanest way to do this. The filter would be installed via resource.setrlimit companion code already present in sandbox._worker().\n\nThe main risk is over-filtering: if the seccomp policy blocks a syscall that ifcopenshell or pyvista needs, renders fail with EPERM. The policy needs to be validated against the full render path (including the geometry iterator subprocesses, which inherit the filter across fork).\n\nAcceptance criteria:\n- A render subprocess cannot exec /bin/sh even with arbitrary code execution (verified by replacing fn with a ctypes execve call — should get EPERM/killed, not a shell)\n- Normal renders continue to produce correct PNG output\n- The seccomp library is optional (graceful fallback if libseccomp is absent, e.g. on macOS or minimal containers)","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-04-24T20:47:00Z","created_by":"Bruno Postle","updated_at":"2026-04-24T20:47:00Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-9cr","title":"ifc:// URLs in Forgejo issues and PR comments not documented as a collaboration tool","description":"Forgejo's markdown extension renders ifc:// URLs as clickable 3D preview images in file README views, but it works equally in issue bodies, PR descriptions, and comment threads — which is where the actual collaboration happens. This is potentially the most useful feature: a reviewer can paste an ifc:// URL in a PR comment and everyone in the thread sees the 3D view inline. This is not mentioned anywhere in README.md, forgejo/README.md, or SPECIFICATION.md. Add a 'Collaboration workflow' section to the README showing: (1) navigate to view in viewer, (2) copy URL from address bar, (3) paste as [label](ifc://...) in a Forgejo comment/issue, (4) collaborators click the preview image to open the view.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:44Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:24Z","started_at":"2026-04-24T11:14:59Z","closed_at":"2026-04-24T11:47:24Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-9cr","title":"ifc:// URLs in Forgejo issues and PR comments not documented as a collaboration tool","description":"Forgejo's markdown extension renders ifc:// URLs as clickable 3D preview images in file README views, but it works equally in issue bodies, PR descriptions, and comment threads — which is where the actual collaboration happens. This is potentially the most useful feature: a reviewer can paste an ifc:// URL in a PR comment and everyone in the thread sees the 3D view inline. This is not mentioned anywhere in README.md, forgejo/README.md, or SPECIFICATION.md. Add a 'Collaboration workflow' section to the README showing: (1) navigate to view in viewer, (2) copy URL from address bar, (3) paste as [label](ifc://...) in a Forgejo comment/issue, (4) collaborators click the preview image to open the view.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:44Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:24Z","started_at":"2026-04-24T11:14:59Z","closed_at":"2026-04-24T11:47:24Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-n00","title":"Forgejo markdown previews fail silently for private repos — no auth path from preview service","description":"The Forgejo markdown extension generates \u003cimg src='http://localhost:8000/preview?url=ifc://...'\u003e tags. The preview service fetches the IFC file server-side using GET /preview with no authentication. For private repos, this returns HTTP 404 or 403 and the preview image shows as broken. There is no mechanism for the Forgejo markdown renderer to pass the user's session token to the preview service. Options to investigate: (1) the Forgejo integration could pass a signed request token; (2) the preview service could run as the Forgejo git user (which has read access to all repos); (3) SSH-based fetch from the service using the Forgejo server's own key. The current state is a silent failure that will confuse users.","status":"closed","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:39Z","created_by":"Bruno Postle","updated_at":"2026-04-24T12:39:46Z","started_at":"2026-04-24T11:56:30Z","closed_at":"2026-04-24T12:39:46Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-n00","title":"Forgejo markdown previews fail silently for private repos — no auth path from preview service","description":"The Forgejo markdown extension generates \u003cimg src='http://localhost:8000/preview?url=ifc://...'\u003e tags. The preview service fetches the IFC file server-side using GET /preview with no authentication. For private repos, this returns HTTP 404 or 403 and the preview image shows as broken. There is no mechanism for the Forgejo markdown renderer to pass the user's session token to the preview service. Options to investigate: (1) the Forgejo integration could pass a signed request token; (2) the preview service could run as the Forgejo git user (which has read access to all repos); (3) SSH-based fetch from the service using the Forgejo server's own key. The current state is a silent failure that will confuse users.","status":"closed","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:39Z","created_by":"Bruno Postle","updated_at":"2026-04-24T12:39:46Z","started_at":"2026-04-24T11:56:30Z","closed_at":"2026-04-24T12:39:46Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-2y6","title":"Private Forgejo repos work in viewer via session cookie (same-origin) but this is undocumented and untested","description":"When the viewer is served at https://forgejo.example.com/assets/viewer.html, toRawUrl() constructs a same-origin URL like https://forgejo.example.com/org/repo/raw/branch/main/model.ifc. The browser automatically attaches the Forgejo session cookie to this fetch, so private repos on the same Forgejo instance should be accessible to logged-in users without any extra configuration. This is a significant capability that is not documented or tested. Needs: (1) a test or note confirming this works, (2) documentation in forgejo/README.md explaining that private repos work automatically for logged-in users when the viewer is served from the same Forgejo instance, (3) clear note that external repos (other Forgejo, GitHub, GitLab) have no auth path from the browser viewer.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:26Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:25Z","started_at":"2026-04-24T11:15:00Z","closed_at":"2026-04-24T11:47:25Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-2y6","title":"Private Forgejo repos work in viewer via session cookie (same-origin) but this is undocumented and untested","description":"When the viewer is served at https://forgejo.example.com/assets/viewer.html, toRawUrl() constructs a same-origin URL like https://forgejo.example.com/org/repo/raw/branch/main/model.ifc. The browser automatically attaches the Forgejo session cookie to this fetch, so private repos on the same Forgejo instance should be accessible to logged-in users without any extra configuration. This is a significant capability that is not documented or tested. Needs: (1) a test or note confirming this works, (2) documentation in forgejo/README.md explaining that private repos work automatically for logged-in users when the viewer is served from the same Forgejo instance, (3) clear note that external repos (other Forgejo, GitHub, GitLab) have no auth path from the browser viewer.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:26Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:25Z","started_at":"2026-04-24T11:15:00Z","closed_at":"2026-04-24T11:47:25Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-u8a","title":"PR review: no before/after 3D render when an IFC file is modified","description":"When a Forgejo PR modifies a .ifc file, the Files Changed tab shows raw STEP text diff — completely unreadable. Reviewers cannot see what geometry changed. The system should automatically add before/after rendered PNG images to the PR description or as a PR check comment when an .ifc file is modified. The service can render both the base-branch version and the PR-branch version using the commit hexshas already available in the PR context. This is the single most important workflow gap for IFC code review — currently 100% of the visual review burden falls on the reviewer manually opening the viewer and navigating. This supersedes and expands the existing ifcurl-prc issue.","status":"open","priority":2,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:21Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:03:21Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-u8a","title":"PR review: no before/after 3D render when an IFC file is modified","description":"When a Forgejo PR modifies a .ifc file, the Files Changed tab shows raw STEP text diff — completely unreadable. Reviewers cannot see what geometry changed. The system should automatically add before/after rendered PNG images to the PR description or as a PR check comment when an .ifc file is modified. The service can render both the base-branch version and the PR-branch version using the commit hexshas already available in the PR context. This is the single most important workflow gap for IFC code review — currently 100% of the visual review burden falls on the reviewer manually opening the viewer and navigating. This supersedes and expands the existing ifcurl-prc issue.","status":"in_progress","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:21Z","created_by":"Bruno Postle","updated_at":"2026-04-25T05:52:14Z","started_at":"2026-04-25T05:52:14Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-sgv","title":"BCF import in viewer: reconstruct ifc:// URL from a BCF viewpoint file","description":"BCF is a standard exchange format for BIM issue tracking. Reviewers using other tools (Solibri, Navisworks, BIMcollab) will generate BCF files. There is currently no way to open a BCF file in the viewer to see the referenced view. Implement BCF import: accept a .bcf zip via drag-and-drop or a file picker, parse the first viewpoint's camera/clips, construct an ifc:// URL from the Components/Selection GUIDs (as a GUID-based selector), and load the view. This also completes SPECIFICATION.md §6 direction 2. The ifc:// URL base (repo, ref, path) cannot be recovered from BCF alone — the viewer should keep the current model context and only apply the viewpoint.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:07Z","created_by":"Bruno Postle","updated_at":"2026-04-24T12:39:47Z","started_at":"2026-04-24T11:56:31Z","closed_at":"2026-04-24T12:39:47Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-sgv","title":"BCF import in viewer: reconstruct ifc:// URL from a BCF viewpoint file","description":"BCF is a standard exchange format for BIM issue tracking. Reviewers using other tools (Solibri, Navisworks, BIMcollab) will generate BCF files. There is currently no way to open a BCF file in the viewer to see the referenced view. Implement BCF import: accept a .bcf zip via drag-and-drop or a file picker, parse the first viewpoint's camera/clips, construct an ifc:// URL from the Components/Selection GUIDs (as a GUID-based selector), and load the view. This also completes SPECIFICATION.md §6 direction 2. The ifc:// URL base (repo, ref, path) cannot be recovered from BCF alone — the viewer should keep the current model context and only apply the viewpoint.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:07Z","created_by":"Bruno Postle","updated_at":"2026-04-24T12:39:47Z","started_at":"2026-04-24T11:56:31Z","closed_at":"2026-04-24T12:39:47Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-qcw","title":"Viewer: no 'Create Forgejo issue' button — BCF export and Forgejo issues are disconnected","description":"The BCF export downloads a file that the reviewer must then manually attach to a Forgejo issue. There is no direct path from 'I found a problem in the viewer' to 'this is now a Forgejo issue'. The feedback loop — reviewer flags, designer fixes, reviewer verifies — requires the issue to live in Forgejo where the code/model review happens. A 'Report issue' button in the viewer should call the Forgejo API (POST /api/v1/repos/{owner}/{repo}/issues) with the current ifc:// URL pre-filled in the issue description, so the issue body automatically renders a clickable 3D preview. The viewer already knows the repo path (from the URL) and when served by Forgejo the user is already authenticated.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:03Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:24Z","started_at":"2026-04-24T11:14:58Z","closed_at":"2026-04-24T11:47:24Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-qcw","title":"Viewer: no 'Create Forgejo issue' button — BCF export and Forgejo issues are disconnected","description":"The BCF export downloads a file that the reviewer must then manually attach to a Forgejo issue. There is no direct path from 'I found a problem in the viewer' to 'this is now a Forgejo issue'. The feedback loop — reviewer flags, designer fixes, reviewer verifies — requires the issue to live in Forgejo where the code/model review happens. A 'Report issue' button in the viewer should call the Forgejo API (POST /api/v1/repos/{owner}/{repo}/issues) with the current ifc:// URL pre-filled in the issue description, so the issue body automatically renders a clickable 3D preview. The viewer already knows the repo path (from the URL) and when served by Forgejo the user is already authenticated.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:03:03Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:24Z","started_at":"2026-04-24T11:14:58Z","closed_at":"2026-04-24T11:47:24Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"id":"ifcurl-unu","title":"Viewer: no 'Copy URL' / share button — relies on users knowing to use the address bar","description":"The viewer continuously updates the browser address bar via history.replaceState so the current view is always shareable. But there is no visible 'Copy link to this view' button. Non-technical users (clients, project managers) will not know to copy from the address bar, especially when the URL is long and truncated. A clipboard button alongside the BCF button — or a dedicated Share toolbar item — would make the core sharing workflow discoverable. Should copy the ifc:// URL (from urlInput, which reflects current camera state) to the clipboard and briefly confirm with a visual indicator.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:02:49Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:23Z","started_at":"2026-04-24T11:14:38Z","closed_at":"2026-04-24T11:47:23Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"id":"ifcurl-unu","title":"Viewer: no 'Copy URL' / share button — relies on users knowing to use the address bar","description":"The viewer continuously updates the browser address bar via history.replaceState so the current view is always shareable. But there is no visible 'Copy link to this view' button. Non-technical users (clients, project managers) will not know to copy from the address bar, especially when the URL is long and truncated. A clipboard button alongside the BCF button — or a dedicated Share toolbar item — would make the core sharing workflow discoverable. Should copy the ifc:// URL (from urlInput, which reflects current camera state) to the clipboard and briefly confirm with a visual indicator.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-04-24T11:02:49Z","created_by":"Bruno Postle","updated_at":"2026-04-24T11:47:23Z","started_at":"2026-04-24T11:14:38Z","closed_at":"2026-04-24T11:47:23Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
|
|
|
||||||
|
|
@ -178,9 +178,42 @@ Add to `/etc/forgejo/conf/app.ini`:
|
||||||
PREVIEW_SERVICE_URL = http://localhost:8000
|
PREVIEW_SERVICE_URL = http://localhost:8000
|
||||||
```
|
```
|
||||||
|
|
||||||
Set `PREVIEW_SERVICE_URL` to the base URL of the running ifcurl preview
|
`PREVIEW_SERVICE_URL` is used **server-side** by Forgejo's markdown renderer to
|
||||||
service. If left empty, ifc:// links in markdown render as plain links with
|
fetch preview images when rendering ifc:// links in issue descriptions and
|
||||||
no preview image.
|
comments. It runs on the Forgejo server itself, so `localhost:8000` is the
|
||||||
|
right value.
|
||||||
|
|
||||||
|
If left empty, ifc:// links in markdown render as plain links with no preview
|
||||||
|
image.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nginx reverse-proxy for PR diff images
|
||||||
|
|
||||||
|
The PR diff viewer (Case 3 in `footer.tmpl`) injects `<img src="/render_diff?…">`
|
||||||
|
tags that the **browser** must fetch. The browser resolves `/render_diff` against
|
||||||
|
the Forgejo origin, so the ifcurl service must be reachable at the public URL.
|
||||||
|
|
||||||
|
Add these proxy locations to the Nginx virtual host that fronts Forgejo:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# ifcurl preview service — served under the same origin as Forgejo
|
||||||
|
location /preview {
|
||||||
|
proxy_pass http://127.0.0.1:8000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /render_diff {
|
||||||
|
proxy_pass http://127.0.0.1:8000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Without this proxy the "View in 3D" button (Case 1/2) still works — it opens
|
||||||
|
the viewer HTML which fetches the IFC file directly. The PR diff images
|
||||||
|
(Case 3) will silently fail to load until the proxy is in place.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,49 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Case 3: PR diff page — inject a render_diff image below the header of
|
||||||
|
// each .ifc file in the diff. Requires Nginx to proxy /render_diff to
|
||||||
|
// the ifcurl preview service (see forgejo/README.md).
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
var prMatch = window.location.pathname.match(/^\/([^/]+\/[^/]+)\/pulls\/(\d+)\/files$/);
|
||||||
|
if (prMatch) {
|
||||||
|
var prRepoPath = prMatch[1];
|
||||||
|
var prNum = prMatch[2];
|
||||||
|
fetch("/api/v1/repos/" + prRepoPath + "/pulls/" + prNum)
|
||||||
|
.then(function(r) { return r.ok ? r.json() : Promise.reject(r.status); })
|
||||||
|
.then(function(pr) {
|
||||||
|
var baseSha = pr.base && pr.base.sha;
|
||||||
|
var headSha = pr.head && pr.head.sha;
|
||||||
|
if (!baseSha || !headSha) return;
|
||||||
|
var origin = window.location.origin;
|
||||||
|
document.querySelectorAll('a[href*="/src/commit/"]').forEach(function(a) {
|
||||||
|
var info = parseCommitHref(a.getAttribute("href"));
|
||||||
|
if (!info || !info.treePath.toLowerCase().endsWith(".ifc")) return;
|
||||||
|
if (info.hash !== headSha) return;
|
||||||
|
var baseIfc = "ifc://" + host + "/" + prRepoPath + "@" + baseSha
|
||||||
|
+ "?path=" + encodeURIComponent(info.treePath);
|
||||||
|
var headIfc = "ifc://" + host + "/" + prRepoPath + "@" + headSha
|
||||||
|
+ "?path=" + encodeURIComponent(info.treePath);
|
||||||
|
var img = document.createElement("img");
|
||||||
|
img.src = origin + "/render_diff"
|
||||||
|
+ "?base=" + encodeURIComponent(baseIfc)
|
||||||
|
+ "&head=" + encodeURIComponent(headIfc);
|
||||||
|
img.style.cssText = "max-width:100%;display:block;margin:8px 0";
|
||||||
|
img.alt = info.treePath;
|
||||||
|
var fileBox = a.closest(".diff-file-box");
|
||||||
|
var header = fileBox && fileBox.querySelector(".file-header");
|
||||||
|
if (header) {
|
||||||
|
header.insertAdjacentElement("afterend", img);
|
||||||
|
} else {
|
||||||
|
a.insertAdjacentElement("afterend", img);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function() {}); // silently skip on API error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Case 2: file history page — inject a "3D" link next to each commit's
|
// Case 2: file history page — inject a "3D" link next to each commit's
|
||||||
// browse-file link (/src/commit/{hash}/{treepath}).
|
// browse-file link (/src/commit/{hash}/{treepath}).
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue