ifcurl-open dispatches ifc:// URLs to a configured viewer (bonsai or
ifcviewer preset, or a custom command list). ifcurl-register installs
it as the OS-level handler: .desktop + xdg-mime on Linux, app bundle
+ lsregister on macOS, HKCU registry keys on Windows (no admin needed).
Data templates in ifcurl/data/. Both support --unregister.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements ifcurl-egi. find_local_repos(genesis) scans directories
from [repo_search] paths in ~/.config/ifcurl/config.toml, computes the
genesis commit of each git repo found, and returns paths whose genesis
matches. Results are cached in ~/.cache/ifcurl/genesis_repos/<genesis>.json
for IFCURL_REPO_CACHE_TTL seconds (default 3600). Recursion stops at repo
boundaries and hidden directories; depth capped at 4 levels. Adds tomli
as a conditional dep for Python <3.11. Unblocks origin remapping (ifcurl-oef)
and ambiguity resolution (ifcurl-77u).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- auth.py: catch OSError (not just FileNotFoundError) so ProtectHome=yes
doesn't crash token lookup — returns None and falls back to anonymous
- git.py: wipe partial bare-clone dir before HTTP fallback so git can retry
after HTTPS fails (port 443 not available on local HTTP-only Forgejo)
- pyproject.toml: add python-multipart to service deps (FastAPI form data)
- ifcurl-api.service: fix ExecStart path (/opt/ifcurl), set allowed-hosts
to localhost, add MPLCONFIGDIR to silence matplotlib cache warning
- ifcurl-render.service: fix ExecStart path, add CacheDirectory + MPLCONFIGDIR
- nginx-ifcurl.conf: new file tracking the nginx reverse-proxy config;
listen on [::]:80 so IPv6 loopback requests reach the Forgejo proxy block
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
render_service.py: new FastAPI app with /render, /select, /render_diff
endpoints that wrap run_sandboxed — listens on a Unix domain socket.
service.py: imports the three pipeline functions from render_service.py;
delegates to render service via httpx when IFCURL_RENDER_SOCKET is set,
falls back to direct run_sandboxed when unset (single-service mode).
__main__.py: add 'render-service' subcommand (--socket PATH).
pyproject.toml: add httpx to service optional deps.
ifcurl-api.service: renamed from ifcurl-preview.service; sets
IFCURL_RENDER_SOCKET, adds AF_UNIX to RestrictAddressFamilies.
ifcurl-render.service: new hardened unit — AF_UNIX only, ~execve
~execveat blocked, no credentials, separate ifcurl-render user.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the ifcgit/Bonsai diff algorithm for the preview service:
diff.py: step_ids_from_diff() parses git diff text (regex on +#NNN= / -#NNN=
lines) to classify step IDs as added/modified/removed. expand_step_ids()
walks IfcShapeRepresentation, IfcObjectPlacement, IfcPropertySet, and
IfcTypeProduct relationships to promote changed sub-entities to the parent
IfcProduct that is visually affected.
render.py: render_diff() does two passes over the same camera. Pass 1 renders
the head model with green=added, blue=modified, ghost=unchanged. Pass 2
renders only the removed elements from the base model in red, using the camera
auto-fitted to pass 1. PIL composites the passes: wherever pass 2 is not
white background it stamps onto pass 1. Moved elements (modified) appear once
in blue with no doubling artefact.
git.py: diff_text() runs git diff against the cached bare repo clone and
returns the raw text for diff.py to parse.
service.py: GET+POST /render_diff endpoint. Fetches both commits, gets
diff text, runs the full pipeline sandboxed. Caches on disk when both refs
are immutable. Pillow added to render/service extras.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pyproject.toml: add bcf to [service] dependencies (ifcurl-9rk)
- README.md: replace bogus GET /viewer row with POST /bcf (ifcurl-ttb)
- viewer-url.js: fix GitLab host detection (includes → startsWith) (ifcurl-3n4)
- git.py: call _evict_if_needed() after fetch, not only after clone (ifcurl-t1k)
- bcf.py: add description param to build_bcf(); markup.bcf now includes
<Description> when provided (ifcurl-7wg)
- service.py: pass request URL as description to build_bcf() (ifcurl-7wg)
- viewer.html: BCF export reads visibility= from URL and sets
DefaultVisibility accordingly (ifcurl-sto)
- viewer.html: applyCameraParam now switches to orthographic projection
and applies scale when scale= is present in URL (ifcurl-4l7)
- tests: 3 new tests for description field; update JS tests pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ifcurl/service.py: FastAPI POST /preview → image/png with three
caching tiers (bytes LRU, GUID-set LRU, PNG filesystem)
- ifcurl/git.py: add fetch_ifc() returning (hexsha, bytes); refactor
around _get_repo() and _read_commit_blob() helpers
- ifcurl/__main__.py: add 'ifcurl serve' subcommand; no-args prints
full help; all subcommands have descriptions and examples; URL format
and parameter reference in every help page
Generated with the assistance of an AI coding tool.