Commit graph

128 commits

Author SHA1 Message Date
a3d4994c4f Sync beads: correct remote Python version in deploy note
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:59:15 +01:00
040af6ee47 Sync beads: close ifcurl-i5c
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:58:03 +01:00
fee3008b60 Document picker: forward OAuth2 access token to Forgejo API
The select-documents picker queried the Forgejo API with only the
browser's session cookie (credentials: 'include'), so an OpenCDE client
that authenticated via OAuth2 saw only public repositories.

Thread the access token presented on the select-documents request
through to the picker UI (query param) and have its JS send it as
Authorization: Bearer on every Forgejo API call, falling back to cookie
auth when no token is supplied. Add a no-referrer policy so the token in
the page URL does not leak via Referer on API fetches or the callback
redirect.

Closes ifcurl-i5c.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:56:30 +01:00
3a395cca41 Ignore CLOUD_SYNC_PROTOCOL.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 23:46:11 +01:00
e442631a5f Update nginx proxy config; document IFCURL_FORGEJO_URL requirement
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 23:42:26 +01:00
fe0025ebd6 Ambiguity resolution: prompt when multiple local repos match genesis
select_local_repo() replaces direct find_local_repos() calls in
_remap_origin.  It reads/writes [project_origins] in config.toml:
a cached path is returned immediately; false marks a project read-only;
a single match is auto-cached without prompting; multiple matches call
a prompt_fn (default: interactive stdin) and cache the choice.  Stale
config paths bust the discovery cache so the next call rescans.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 21:03:13 +01:00
4d4a76ea34 Remap checkout origin to local repo when discovered
After creating a working checkout, if find_local_repos() finds a local
clone or fork of the same project, rename 'origin' (bare cache) to
'upstream' and add a new 'origin' pointing at the local repo.  Mirrors
the standard fork workflow: fetch from upstream, push to origin (local
repo), then push to the shared remote and open a PR.  If no local repo
is found, origin stays as the bare cache (read-only).  Idempotent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 18:11:47 +01:00
403aba2e28 Document OpenCDE OAuth2 proxy, document picker, protocol handler, and roadmap status
- README: add OAuth2 proxy routes and select-documents endpoints to service
  table; add IFCURL_OAUTH2_CLIENT_ID/SECRET to config table; add
  ifcurl-register/ifcurl-open CLI section; update roadmap to mark phases
  4/7/8/9 as done
- SPECIFICATION.md: add note to §9 step 2 pointing to OAuth2 proxy routes
  in README
- CLAUDE.md: add test item 19 for OpenCDE select-documents picker flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 16:23:52 +01:00
8101e1afcf Fix four security vulnerabilities found in security review
1. Stored XSS in populateMetaPanel (viewer.js): IFC type and storey
   names from model data were interpolated unescaped into innerHTML.
   Added hesc() helper and applied it to all IFC-derived values.

2. sessionStorage CSS injection (viewer.html): viewerSnapshot value was
   used unsanitised in style.backgroundImage. Now validated to start
   with 'data:image/jpeg;base64,' before use.

3. Hostname-based SSRF bypass (service.py): _is_private_ip() only
   blocked literal IP addresses; hostnames resolving to private
   addresses bypassed the check. Now resolves all addresses via
   socket.getaddrinfo() and rejects if any resolve to a private range.
   Unresolvable hostnames are treated as private (fail-safe).

4. SSRF + bearer token exfiltration via BCF snapshot (bcf_api.py):
   get_snapshot() forwarded the caller's Authorization token to the
   preview service for an ifc:// URL stored in a Forgejo comment body
   (attacker-controlled). Now parses and validates the stored URL
   through _ssrf_check(), and uses the server-side token for the
   ifc:// host instead of the caller's credential.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 14:49:45 +01:00
15ae66b5f7 Fix DOM XSS in document picker breadcrumb
renderBreadcrumb was building HTML via string concatenation and writing
it to innerHTML. The path segments from Forgejo's API were inserted raw
into single-quote-delimited onclick attributes, and escHtml did not
escape single quotes, so a directory name containing ' could inject
arbitrary JS executed on click.

Replace innerHTML construction with DOM API calls (textContent +
element.onclick) matching the safe pattern already used in addListItem,
and remove the now-dead escHtml helper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 12:48:24 +01:00
e2d3d12bc1 Add OpenCDE select-documents picker (POST + UI)
POST /documents/1.0/select-documents accepts a callback URL and returns a
select_documents_url pointing to a self-contained HTML picker that browses
Forgejo repos and IFC files, encodes the selection as a document_id, then
redirects back to the callback with document_ids[] appended.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 12:19:01 +01:00
601bbacacd Update beads index after closing ifcurl-bil 2026-06-07 10:09:31 +01:00
48169ff94b Add OpenCDE Foundation API 1.1 OAuth2 proxy routes
Implements GET /foundation/1.1/oauth2/auth_url, POST /oauth2/token, and
POST /oauth2/token_refresh, proxying to Forgejo's OAuth2 endpoints using
IFCURL_OAUTH2_CLIENT_ID and IFCURL_OAUTH2_CLIENT_SECRET env vars.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 10:08:13 +01:00
d975658515 Add ifcurl-open and ifcurl-register: ifc:// OS protocol handler
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>
2026-06-07 09:36:20 +01:00
5ea60da943 Fix BCF export missing snapshot.png when selector is active
Capture the WebGL canvas client-side before POSTing to /bcf, send the
base64 PNG as a new `snapshot` field, and embed it as snapshot.png in
the zip alongside viewpoint.bcfv.  Also wires up the <Snapshot> element
in markup.bcf when a snapshot is present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 06:56:11 +01:00
caf303d3ed Add checkout.py: writable working-tree checkouts from bare cache
Implements ifcurl-5oo. get_checkout() ensures the bare cache is current via
fetch_ifc(), then clones it locally to <cache_dir>/checkout/ (no network),
and places it at the resolved commit as a detached HEAD — forcing branch
creation before any commit. Viewer artefact patterns (*.ifcview, *.rdbview,
etc.) are appended to .git/info/exclude so they are locally ignored without
touching the repo's own .gitignore. When a newer commit is requested, the
checkout fetches from origin (the local bare cache) using the default refspec
to avoid git's "refusing to fetch into checked-out branch" error. Unblocks
the JSON-RPC connector (ifcurl-242).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 23:38:13 +01:00
988eed5a93 Add discover.py: find local repos matching a project by genesis commit
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>
2026-06-05 23:05:33 +01:00
8625528514 Add get_genesis_commit: root commit hash as project identity key
Implements ifcurl-4d4. _compute_genesis() runs git rev-list --max-parents=0
HEAD to find the root commit(s); get_genesis_commit() wraps it with a
per-remote disk cache (~/.cache/ifcurl/<hash>/genesis_commit) so the git
command only runs once per repo. For multi-root repos the lexicographically
smallest hexsha is chosen for a stable result. Local repos compute fresh
on each call. Exported from __init__. Unblocks local repo discovery (ifcurl-egi).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 18:32:54 +01:00
ae143b74b7 Add fetch_ifc_path: materialise IFC blob to stable per-commit cache path
Implements ifcurl-i07. fetch_ifc_path() fetches the IFC file and writes
it to ~/.cache/ifcurl/<url-hash>/<hexsha>/<basename>, returning the path
alongside the is_stale flag. Content-addressed by commit hexsha, so the
file is never rewritten once present. Works for both local and remote
URLs. Underpins the connector and checkout layers (ifcurl-5oo).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 18:26:11 +01:00
7aaee37ac8 Fix git-wrapper: preserve argv[0] for multi-call dispatch, tighten security
- exec -a "$0" so git.real sees the correct argv[0] (e.g. git-upload-pack)
  and dispatches correctly; without this SSH clone was broken
- Check only $1 for "merge-file" instead of scanning all args, preventing
  accidental ifcmerge invocation from commit messages or other arg positions
- Use absolute path /usr/local/bin/ifcmerge to prevent PATH hijacking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:17:55 +01:00
6850a8d4f9 Close ifcurl-lal in beads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:17:52 +01:00
df11dcc9d3 Fix: exclude IfcSpace and IfcAnnotation from default render
These element types produced large grey boxes obscuring the building in
renders of models that contain space volumes or annotation geometry
(e.g. 2D floor-plan drawings stored as Body CSG). Other IFC viewers
(Bonsai, web viewer) suppress these types in 3D views by default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:13:51 +01:00
ea1484abf7 Fix: scope ifc:// link processing to .markup elements only
Forgejo editor previews (non-contenteditable) were being modified by
linkifyBareIfcUrls and replaceIfcAnchors, corrupting the editor state.
Restrict both functions to .markup containers (rendered issue/wiki/README
content) so editor UI is never touched.
2026-05-17 23:56:45 +01:00
57180bc178 Fix: skip contenteditable editor areas in linkifyBareIfcUrls and replaceIfcAnchors
Forgejo's WYSIWYG editor uses a contenteditable div. Walking text nodes
inside it caused linkifyBareIfcUrls to split [label](ifc://...) by wrapping
just the URL in an anchor, which the editor serialised back as [label]().
2026-05-17 23:44:19 +01:00
0e855d8413 Revert BCF snapshot debug logging 2026-05-17 22:59:20 +01:00
10017f01e4 Debug BCF snapshot: log rendererCanvas and toDataURL result 2026-05-17 22:50:02 +01:00
8facdaca34 Docs: remove Go patch requirement; all features work with asset-only deploy
Commit 4b53dfe replaced the Go goldmark extension with JS text-node
linkification, making bare ifc:// URL rendering work entirely client-side.
No Forgejo rebuild is needed for any feature.

- Remove "Optional: Go patch" section from README.md
- Remove Go tests from Development section
- Update forgejo/README.md feature table, prerequisites, and deploying sections
- Remove "Applying the patch", "Configuration" (PREVIEW_SERVICE_URL), and
  "Upgrading Forgejo" (Go-specific steps) sections from forgejo/README.md
- Update forgejo/SETUP_LOCAL.md: bare URLs now work; remove Step 6 (app.ini)
- Delete forgejo/go.patch (dead code — ifc_url.go was removed in 4b53dfe)
- Add underscore caveat note wherever bare URL support is described

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:28:38 +01:00
8cbbf95333 Document bare ifc:// URL limitation with matched underscores
Bare URLs with paired underscores in the path/query (e.g. _model_v2.ifc)
are split by Goldmark's emphasis parser into <em> tags, breaking the JS
text-node linkifier.  Workaround: use the [label](ifc://...) link form.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:11:14 +01:00
97ae246aa2 Strip Clearance representation geometry before loading IFC in viewer
web-ifc disposes its IfcAPI after load so context filtering must happen
before the buffer reaches ifcLoader.load(). Pre-process the IFC-SPF buffer
to find IfcGeometricRepresentationSubContext entities with ContextIdentifier
'Clearance', then empty the items list of any IFCSHAPEREPRESENTATION that
references those contexts, so door-swing and equipment-clearance volumes
produce no geometry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 10:56:13 +01:00
369b7f98e3 Fix model reload transition: snapshot overlay + bfcache handling
- Inline script at top of <body> creates the snapshot overlay immediately
  on page load, before the deferred module script runs, eliminating the
  black flash between navigation and overlay appearance
- Save a JPEG snapshot to sessionStorage before navigating to a new ref;
  the overlay covers the blank WebGL canvas while the model reloads
- Fade the overlay out after two animation frames post-load so the canvas
  has painted before the transition begins
- Reload on pageshow with persisted=true: bfcache restore left the WebGL
  canvas blank and skipped the inline script; a fresh reload lets the
  snapshot overlay work correctly
- Replace native <datalist> ref picker with a custom dropdown; the native
  datalist won't open programmatically via .focus() in most browsers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 10:23:29 +01:00
afc966fdf6 Avoid full model reload for view-only URL changes (camera, clip, selector)
When the ifc:// URL is edited and Enter pressed, capture toRawUrl() of
the loaded model at load time. On any subsequent loadUrl() call, compare
against that snapshot: if the raw file URL is unchanged, call the new
applyViewChanges() function in-place instead of navigating to a new page.

applyViewChanges() diffs old vs new URL query params and applies only what
changed: camera position (with cameraControls.update(0) flush before
render), FOV/scale, clipping planes, selector/visibility, and query. A
full page reload now only happens when host, repo, ref, or path changes.

Fixes ifcurl-ntm.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 09:12:40 +01:00
b3e37c512a Fix query panel visibility and meta panel type counts
- Add /query proxy rule to nginx config (was missing, caused 404)
- Fix populateMetaPanel: call groupData.get() instead of reading empty
  groupData.map, and use ids.size (Set) not ids.length
- Update CLAUDE.md check #17 to reflect type counts now working

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:02:54 +01:00
4a92afb6b5 add ifcmcp config 2026-05-16 14:31:09 +01:00
7b17c8aafa Add Phase 3 browser test checklist to CLAUDE.md; file two bugs
- Document Forgejo/JS deployment paths, deploy commands, and services
- Add 18-item browser test checklist with known issues noted inline
- Correct check #17: hamburger button, type counts not shown (ifcurl-hwk)
- Create ifcurl-hwk: meta panel missing type counts
- Create ifcurl-6xq: query panel does not appear after selector resolves

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 14:29:45 +01:00
c05ed370aa Fix viewer camera offset: disable COORDINATE_TO_ORIGIN in web-ifc
web-ifc's COORDINATE_TO_ORIGIN=true shifts all model geometry so the first
element lands at origin (≈12m shift for simple-ifc). GetCoordinationMatrix
returns identity despite the shift, so engine_fragment can't compensate.
Camera URLs are in IFC world coords (per spec §5), but the model was in
shifted coords, causing the JS viewer and Python renderer to disagree.

Set COORDINATE_TO_ORIGIN: false in ifcLoader.setup() so model geometry
stays in IFC world coords, matching what IfcOpenShell outputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:40:05 +01:00
03782ec9f7 Fix fetch-on-miss for PR commits: catch ValueError from GitPython directly
GitPython raises a plain ValueError from _parse_object_header when a commit
hash is missing, not BadObject, so the previous message-based retry guard
("not found in repository") never matched. Broaden the except clause to catch
(ValueError, BadObject, BadName) and drop the fragile string check. Extract
the fetch logic into _fetch_all_refs helper and apply the same retry to
diff_text. Add tests covering _fetch_all_refs, the retry-on-BadObject path,
and the no-retry-for-local-transport invariant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 08:56:07 +01:00
3937de2974 Fix PR diff early return and fetch branch commits on miss
ifcurl.js: URL-pattern cases (PR diff, commit page) now run before
the DOM-based file-view case, which also matches on Forgejo diff pages
and was causing an early return that skipped PR diff rendering.

git.py: fetch_ifc now retries with a full ref fetch when an immutable
ref (commit hash) is not found in the cached clone — PR branch commits
are absent from the initial default-branch clone and need a fetch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 01:12:15 +01:00
Claude
38161604f9 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
2026-05-09 11:09:55 +00:00
Claude
ea0ad56bda Update beads state
https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn
2026-05-09 09:26:34 +00:00
Claude
8d13757c4d Viewer: add clash visibility mode (ifcurl-982)
viewer.html: add 'clash' option to the visibility <select> dropdown.

viewer.js: add clash branch in applySelector for both the simple
type-selector path and the complex server-side GUID path. Selected
elements are coloured 0xdc3232 (red, matching render.py's _CLASH_COLOR);
non-selected elements remain at their normal material colours, consistent
with highlight mode behaviour.

https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn
2026-05-09 09:03:11 +00:00
Claude
ccf692b168 Add /clash endpoint for geometry intersection detection (ifcurl-k0x)
render_service.py: _sandboxed_clash(ifc_bytes, selector_a, selector_b) uses
ifcopenshell.geom.tree.clash_collision_many for hard-clash detection (mesh-
level interpenetration, touching excluded). When selector_b is None, tests
set A against all other IfcProduct elements. Deduplicates pairs via frozenset.

render_service.py: /clash endpoint wraps _sandboxed_clash in the sandbox.

service.py: ClashRequest model, _clash_via_socket() for socket delegation,
and GET/POST /clash endpoints. URL selector= is set A; optional selector_b
body/query param is set B. Returns {"pairs": [["guidA", "guidB"], ...]}.
Reuses Tier 2 byte cache.

https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn
2026-05-09 08:53:51 +00:00
Claude
39243e343a Close ifcurl-8rf in beads
https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn
2026-05-09 08:46:40 +00:00
Claude
b560551f3c Implement clash visibility mode and /query endpoint (ifcurl-19h, ifcurl-8rf)
render.py: add _CLASH_COLOR and clash branch in _add_shape — selected
elements rendered in red, non-selected in normal material colours.

render_service.py: add _sandboxed_query(ifc_bytes, selector, query_path)
and /query endpoint. Dot-notation query_path splits into pset/property
(Pset_WallCommon.FireRating) or falls back to direct IFC attribute (Name).
Returns {GlobalId: str(value)}, excluding None and entity references.

service.py: add QueryRequest model, _query_via_socket() for socket
delegation, and GET/POST /query endpoints reusing Tier 2 byte cache.

https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn
2026-05-09 08:46:26 +00:00
Claude
83a8e087ed Sync beads after main merge
https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn
2026-05-09 08:41:33 +00:00
Claude
91b57ef37b Update clash beads: server-side detection via /clash endpoint
- Add ifcurl-k0x (P2): server /clash endpoint using IfcOpenShell geometry
  intersection (bbox pre-filter + mesh-level test), returns {pairs: [[gA,gB]]}
- Update ifcurl-982: viewer clash display now explicitly depends on ifcurl-k0x;
  GUIDs sourced from /clash rather than computed in JS
- Add dependency: ifcurl-982 blocked by ifcurl-k0x

https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn
2026-05-09 08:29:04 +00:00
Claude
56c36b51e9 Add beads for query and clash features
New issues:
- ifcurl-8rf (P2): Server /query endpoint + _sandboxed_query
- ifcurl-19h (P2): Renderer clash visibility mode
- ifcurl-2iv (P3): Viewer query results table (blocked on ifcurl-8rf)
- ifcurl-4u3 (P3): Viewer query creation/editing UI
- ifcurl-982 (P3): Viewer clash visibility mode display

https://claude.ai/code/session_01NBsytCd6L7UQPiKbcLn4kn
2026-05-09 08:09:10 +00:00
Claude
b01c72b3ef .claude: add SessionStart hook to install bd and bootstrap beads
Installs bd (beads issue tracker) via go install and bootstraps the
embedded Dolt database from the git-tracked issues.jsonl on each fresh
web session, where the embeddeddolt/ directory is absent.

Bootstrap sequence:
  1. go install github.com/steveyegge/beads/cmd/bd@latest (if absent)
  2. Temporarily point sync.remote to the HTTP git origin so bootstrap
     can create the embedded Dolt schema (Dolt wire-protocol clone fails
     over HTTP/1.1 but that is expected and handled)
  3. Restore .beads/config.yaml and .beads/issues.jsonl from git before
     any bd auto-export can overwrite them
  4. bd config set issue_prefix ifcurl triggers auto-import of the full
     issues.jsonl into the initialised but empty database

Also adds bd to PATH via CLAUDE_ENV_FILE and sets beads.role=contributor
to suppress the role-not-configured warning.

https://claude.ai/code/session_01CKpkhWhzkzR5K5PDSR8N5F
2026-05-09 07:55:30 +00:00
4b96ee3206 viewer: show status hint when clip mode is active
Displays "Double-click model surface to place clip plane" in the status
bar when clip mode is toggled on, clears it when toggled off.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 22:30:24 +01:00
9afeae5d44 bcf_api: add topic filtering; documents_api: add GET /document-metadata
BCF list_topics now maps topic_status, assigned_to, label, modified_after,
modified_before query params to Forgejo issue API params.

documents_api adds GET /document-metadata/{document_id} which decodes the
document_id and proxies Forgejo's contents endpoint for file metadata.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 22:24:52 +01:00
95428eee25 beads: add OpenCDE completion issues (topic filtering, doc metadata, OAuth2, picker) 2026-04-29 21:39:54 +01:00