diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index a3322d9..dcf8c4b 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -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":"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":"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."} diff --git a/README.md b/README.md index 212def5..6b53565 100644 --- a/README.md +++ b/README.md @@ -59,17 +59,12 @@ A self-contained WebGL IFC viewer (`viewer.html`) served as a Forgejo asset at ` ## Forgejo integration -A set of JS assets and an optional Go patch for Forgejo. Most features work with -asset deployment only — no Forgejo rebuild required. See -[`forgejo/README.md`](forgejo/README.md) for full details, or -[`forgejo/SETUP_LOCAL.md`](forgejo/SETUP_LOCAL.md) for a self-contained local -setup on Windows using Docker. +A set of JS assets for Forgejo — all features work with asset deployment only, +no Forgejo rebuild required. See [`forgejo/README.md`](forgejo/README.md) for +full details, or [`forgejo/SETUP_LOCAL.md`](forgejo/SETUP_LOCAL.md) for a +self-contained local setup on Windows using Docker. -### Quick setup (no rebuild) - -Deploy the assets and the preview service. All features work except bare -`ifc://...` text in markdown (use `[title](ifc://...)` link syntax instead, which -the viewer's Issue button produces automatically). +### Quick setup ```bash # Deploy assets @@ -85,33 +80,15 @@ sudo cp forgejo/server-config/ifcurl-preview.service /etc/systemd/system/ sudo systemctl enable --now ifcurl-preview ``` -### Optional: Go patch for bare URL rendering +### Bare `ifc://` URLs in markdown — underscore caveat -To also render bare `ifc://...` text in markdown (without `[title](...)` syntax), -apply the Go patch and rebuild Forgejo: - -```bash -cp forgejo/modules/markup/markdown/ifc_url{,_test}.go /path/to/forgejo/modules/markup/markdown/ -cd /path/to/forgejo && git apply /path/to/ifcurl/forgejo/go.patch -go build -tags 'sqlite sqlite_unlock_notify' \ - -ldflags "-X 'forgejo.org/modules/setting.StaticRootPath=/usr/share/forgejo'" \ - -o forgejo . && sudo cp forgejo /usr/bin/forgejo -``` - -Add to `/etc/forgejo/conf/app.ini`: - -```ini -[ifcurl] -PREVIEW_SERVICE_URL = http://localhost:8000 -; Optional: Forgejo API token for a read-only machine user. -; The preview service will use this token to fetch IFC files from private -; repositories on this Forgejo instance. The token is appended as a query -; parameter in the URL generated by the markdown extension, so it -; is visible in page source. Only set this on trusted private instances. -; SERVICE_TOKEN = -``` - -**Private repositories:** create a machine user with read access and set `SERVICE_TOKEN` to its API token. Alternatively, configure credentials in `~/.config/ifcurl/tokens.json` under the service user account. +Bare `ifc://` URLs in markdown (pasted without `[title](...)` syntax) are +linkified client-side by `ifcurl.js`. This works for most paths, but if the +file path or query parameters contain matched underscores (e.g. `_model_v2.ifc`), +Goldmark renders them as `` tags and splits the URL — the bare form will +fail silently. Use the explicit link form `[label](ifc://...)` for any URL where +the path or parameters contain paired underscores. The viewer's **Issue** button +always generates this safe form automatically. ### Authentication in the viewer @@ -303,10 +280,6 @@ python -m pytest tests/ # JavaScript tests (Node 18+) node --test tests/test_viewer_url.mjs - -# Go tests (requires Forgejo source tree with patch applied) -cd /path/to/forgejo -go test ./modules/markup/markdown/ -run TestIfcURL -v ``` ### Roadmap @@ -315,7 +288,7 @@ go test ./modules/markup/markdown/ -run TestIfcURL -v |---|---|---| | 1 — Python core | ✓ done | URL parsing, git fetch, render | | 2 — Preview service | ✓ done | HTTP service with caching | -| 3 — Forgejo integration | ✓ done | Go patch, viewer, markdown extension | +| 3 — Forgejo integration | ✓ done | Viewer, JS markdown extension | | 4 — Bonsai integration | planned | Protocol handler + "Copy view URL" | | 5 — IFC Viewer integration | planned | Plugin for the open-source IFC Viewer | | 6 — Federation | planned | `IFCDOCUMENTREFERENCE` cross-repo links | diff --git a/forgejo/README.md b/forgejo/README.md index ae0f16d..5a6e8b9 100644 --- a/forgejo/README.md +++ b/forgejo/README.md @@ -11,18 +11,18 @@ split into two tiers by what infrastructure they require. | Browser IFC viewer | `viewer.html` static asset | No | | PR diff 3D render | `ifcurl.js` + ifcurl service + **Nginx proxy** | No | | `[title](ifc://...)` links in markdown → inline preview | `ifcurl.js` + ifcurl service + **Nginx proxy** | No | -| Bare `ifc://...` text in markdown → inline preview | Go patch + `PREVIEW_SERVICE_URL` in app.ini | **Yes** | +| Bare `ifc://...` text in markdown → inline preview | `ifcurl.js` (JS text-node linkification) | No | -All features except bare-URL markdown rendering work with static asset deployment -only — no Forgejo rebuild required. `ifcurl.js` detects `` links -produced by Goldmark's standard link parser and replaces them with preview figures -at page load. +All features work with static asset deployment only — no Forgejo rebuild required. +`ifcurl.js` handles both `` links produced by Goldmark's +standard link parser and bare `ifc://...` text nodes, replacing both with preview +figures at page load. -The Go patch is needed **only** if you want bare `ifc://...` text in markdown -(without `[title](...)` syntax) to render as a preview. The Issue button in the -viewer emits `[title](ifc://...)` link syntax, so normal issue/PR workflows work -without the patch. Minimising the patch surface is deliberate — every line of Go -code must be re-verified against each Forgejo release. +**Bare URL caveat:** bare `ifc://` URLs where the path or query parameters +contain matched underscores (e.g. `_model_v2.ifc`) may be split by Goldmark's +emphasis parser before `ifcurl.js` can linkify them. Use the explicit +`[label](ifc://...)` form in those cases — the viewer's **Issue** button always +generates this safe form. The PR diff feature requires a **reverse proxy** (e.g. Nginx) to expose the ifcurl service at the same origin as Forgejo. This is because the diff image is @@ -37,10 +37,6 @@ deployments already sit behind Nginx for TLS termination; adding two ``` forgejo/ - go.patch ← diff against Forgejo source (apply once) - modules/markup/markdown/ - ifc_url.go ← new Go source file (copy into source tree) - ifc_url_test.go ← Go tests (copy into source tree) custom/public/assets/ viewer.html ← browser IFC viewer (no rebuild needed) viewer-url.js ← ifc:// URL parse/build/resolve logic (ES module) @@ -67,64 +63,9 @@ For a self-contained local deployment of Forgejo + ifcurl on Windows using Docke - ifcurl API service + render service running (see [Running the preview service](#running-the-preview-service)) - Nginx (or equivalent) in front of Forgejo if PR diff images are wanted -- Forgejo source tree cloned and building cleanly (`go` 1.21+) — **only if** - markdown inline preview is wanted -The patch was written against the `v13.0` branch of Forgejo. Check which -Forgejo commit the patch was authored against with: - -```bash -cd /path/to/forgejo -git log --oneline modules/markup/markdown/markdown.go | head -3 -``` - ---- - -## Applying the patch - -### 1. Copy new Go source files - -```bash -cp forgejo/modules/markup/markdown/ifc_url.go /path/to/forgejo/modules/markup/markdown/ -cp forgejo/modules/markup/markdown/ifc_url_test.go /path/to/forgejo/modules/markup/markdown/ -``` - -### 2. Apply the diff to existing files - -```bash -cd /path/to/forgejo -git apply /path/to/ifcurl/forgejo/go.patch -``` - -This adds one line to `modules/markup/markdown/markdown.go` and six lines to -`modules/setting/markup.go`. If the patch does not apply cleanly (e.g. after -a Forgejo upstream upgrade), the changes are small enough to apply by hand — -see `go.patch` for the exact hunks. - -### 3. Run the Go tests - -```bash -cd /path/to/forgejo -go test ./modules/markup/markdown/ -run TestIfcURL -v -``` - -All seven tests should pass before proceeding. - -### 4. Build and deploy Forgejo - -```bash -cd /path/to/forgejo -go build \ - -tags 'sqlite sqlite_unlock_notify' \ - -ldflags "-X 'forgejo.org/modules/setting.StaticRootPath=/usr/share/forgejo'" \ - -o forgejo . - -sudo cp forgejo /usr/bin/forgejo -sudo systemctl restart forgejo -``` - -Adjust build tags and `-ldflags` to match your existing Forgejo build -configuration. +No Forgejo source tree or Go toolchain is required — all features work with +static asset deployment. --- @@ -285,25 +226,6 @@ ifcurl serve --allowed-hosts localhost:3000 non-private remote hosts, which is unsafe if the service is reachable from untrusted clients. -## Configuration - -Add to `/etc/forgejo/conf/app.ini`: - -```ini -[ifcurl] -PREVIEW_SERVICE_URL = http://localhost:8000 -``` - -`PREVIEW_SERVICE_URL` is used **server-side** by Forgejo's markdown renderer to -fetch preview images when rendering ifc:// links in issue descriptions and -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 `` @@ -427,13 +349,6 @@ identify `.ifc` files correctly. ## Upgrading Forgejo -After upgrading Forgejo upstream: - -1. Re-apply `go.patch` (or apply the two hunks by hand if context has shifted). -2. Copy `ifc_url.go` and `ifc_url_test.go` back in — they are not modified by - upstream. -3. Run the Go tests to verify compatibility. -4. Rebuild and redeploy. - -Custom assets and `footer.tmpl` do not require a rebuild and are unaffected by -Forgejo upgrades. +Custom assets (`viewer.html`, `ifcurl.js`, etc.) and `footer.tmpl` are +unaffected by Forgejo upstream upgrades — redeploy them after each asset +change and restart Forgejo once after updating `footer.tmpl`. diff --git a/forgejo/SETUP_LOCAL.md b/forgejo/SETUP_LOCAL.md index e5cf4a8..8f87d7d 100644 --- a/forgejo/SETUP_LOCAL.md +++ b/forgejo/SETUP_LOCAL.md @@ -195,18 +195,6 @@ Open `http://localhost:3000` and complete the Forgejo setup wizard. --- -## Step 6 — Add ifcurl config to Forgejo - -The setup wizard writes its own `app.ini` inside the container. After -completing the wizard, append the ifcurl setting and restart: - -```bash -MSYS_NO_PATHCONV=1 docker exec forgejo sh -c 'printf "\n[ifcurl]\nPREVIEW_SERVICE_URL = http://ifcurl:8000\n" >> /data/gitea/conf/app.ini' -docker compose restart forgejo -``` - ---- - ## Verify the setup Check that Forgejo is serving the ifcurl assets: @@ -228,7 +216,7 @@ You should see JavaScript, not a 404. | PR diff 3D renders (green=added, red=removed) | Yes | | `[label](ifc://...)` links in markdown → inline preview | Yes | | CLI rendering (`ifcurl render "ifc://..."`) | Yes | -| Bare `ifc://...` text in markdown | No — needs Go patch + Forgejo rebuild | +| Bare `ifc://...` text in markdown | Yes (avoid paired underscores in paths — use `[label](ifc://...)` form) | For normal workflows, use `[label](ifc://...)` link syntax — the viewer's **Issue** button generates this format automatically. @@ -278,6 +266,5 @@ docker compose up -d - The ifcurl service clones remote git repos as bare repos into the container's cache on first use. Mutable refs (`@heads/`, `@HEAD`) trigger a `git fetch` on each request; immutable refs (commit hashes, tags) use the cache as-is. -- The Go patch in the ifcurl repo was written against Forgejo v13.0. As of - this writing the latest release is v15.0.0. The patch is optional and only - needed for bare `ifc://` URL rendering in markdown. +- No Go patch or Forgejo rebuild is needed — all features including bare + `ifc://` URL rendering are handled client-side by `ifcurl.js`. diff --git a/forgejo/go.patch b/forgejo/go.patch deleted file mode 100644 index 4f39e98..0000000 --- a/forgejo/go.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go -index 2b19e0f1c9..12be3e1c24 100644 ---- a/modules/markup/markdown/markdown.go -+++ b/modules/markup/markdown/markdown.go -@@ -116,6 +116,7 @@ func SpecializedMarkdown() goldmark.Markdown { - math.NewExtension( - math.Enabled(setting.Markdown.EnableMath), - ), -+ NewIfcURLExtension(), - ), - goldmark.WithParserOptions( - parser.WithAttribute(), -diff --git a/modules/setting/markup.go b/modules/setting/markup.go -index 4ab9e7b2d1..862852381e 100644 ---- a/modules/setting/markup.go -+++ b/modules/setting/markup.go -@@ -59,8 +59,15 @@ type MarkupSanitizerRule struct { - AllowDataURIImages bool - } - -+// IfcURL holds configuration for the ifcurl preview service integration. -+var IfcURL = struct { -+ PreviewServiceURL string `ini:"PREVIEW_SERVICE_URL"` -+ ServiceToken string `ini:"SERVICE_TOKEN"` -+}{} -+ - func loadMarkupFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "markdown", &Markdown) -+ mustMapSetting(rootCfg, "ifcurl", &IfcURL) - - MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(50000) - FilePreviewMaxLines = rootCfg.Section("markup").Key("FILEPREVIEW_MAX_LINES").MustInt(50)