Add Bonsai Viewer docs

Create a standalone Sphinx docs tree for Bonsai Viewer and migrate the Autodesk connector Markdown documentation into RST.\n\nGenerated with the assistance of an AI coding tool.
This commit is contained in:
Dion Moult
2026-05-20 16:35:33 +10:00
parent bb17cfbc40
commit 66f3593de1
16 changed files with 960 additions and 703 deletions
@@ -1,498 +0,0 @@
# Bonsai Viewer cloud connectors
Bonsai Viewer will have the capability to load and save projects and models from a
cloud platform. Later on, there will be other resources stored on cloud
platforms too, such as issues, clash results, and so on, but this behaviour is
not currently designed.
Due to the variety of cloud platforms, the Bonsai Viewer itself will depend on
a "connector" to integrate with each platform. The connector is a separate
application which will communicate to and from the Bonsai Viewer.
The following types of resources may be managed with a connector:
- Projects (.ifcfed)
- Models (.ifc, .rdb, .ifcview, .rdbview)
- Issues (.bcf, not yet supported nor defined)
- Specifications (.ids, not yet supported nor defined)
## Communication protocol
The Bonsai Viewer launches one connector process per session, on first use, and
keeps it alive for the duration of the session. This allows the connector to
maintain authentication tokens, browse state, in-flight downloads, and caches
in memory across calls without re-authenticating on every request.
Communication is over stdio using newline-delimited JSON-RPC 2.0:
- Requests and responses are single-line JSON objects on the connector's
stdin/stdout. Each message is terminated by a single `\n`.
- The connector must not emit literal newlines inside a JSON message.
- The connector may write arbitrary diagnostic output to stderr; the Bonsai Viewer
will not parse it.
The Bonsai Viewer shuts a connector down by closing its stdin. The connector should
exit cleanly. If it does not exit within a few seconds, the Bonsai Viewer will
terminate it.
## Connector scope
The Bonsai Viewer has minimal knowledge about connectors. Bonsai Viewer only knows how
to work with local files. If it detects that a project or model is not local,
it will invoke a connector. The connector's job is to resolve the Bonsai Viewer's
request back into a local file and cloud metadata. The connector must not
modify the file in any way.
A connector will handle anything necessary for the cloud platform (or arbitrary
data source). This includes authentication, browsing files, filters and
searches, selecting or pinning revisions, progress bars, cache,
platform-specific requirements, etc. The connector may or may not display a UI.
This makes connectors very flexible.
A single project may have different resources coming from different connectors.
For example, some models might be on one platform, and some projects hosted on
another platform. The permissions regarding model access can be quite granular
and therefore managed by the platform, and not Bonsai Viewer.
When a connector returns a local file, that file is required to be the sole
child in its directory. This is because there may be adjacent temporary files,
viewer-generated sidecar files, database locks or helpers (e.g. SQLite WAL), or
where filenames are significant (and cannot be renamed to prevent collisions),
or are actually directories containing other files.
The connector is expected to persist this cache until explicitly cleared by a
user, because it will be used directly as a local path by the viewer. If a
connector invalidates a cache, it can simply delete the entire directory. If a
connector resolves to a new version of the file, it can create a fresh
directory (thus all sidecar artefacts will be regenerated if needed). It is not
prescribed how a connector manages cache.
## Resource: Projects
A project is defined using an `.ifcfed` file. The file stores settings (such as
units, home view coordinates, saved searches, etc) and models.
For example:
```json
# project.ifcfed
{
"created": "2026-04-29T21:22:36Z",
"modified": "2026-04-29T21:22:36Z",
"home_view": null,
"models": ... # see Resources: Models,
...
}
```
A project may have a manifest file (with `.manifest` as a suffix), which may
store metadata that a ifcfed was retrieved from a cloud source.
```json
# project.ifcfed.manifest
{
"connector": "mycompany",
# Arbitrary connector-specific data
"version": "2",
"url": "http://example.com/project.ifcfed",
}
```
## Resource: Models
The list of models is defined in the .ifcfed. Each model may either point to a
local file (via the special "local" connector), or to a cloud file.
Cloud connections may store arbitrary source data as keys. For example, they
might store a revision policy that determines whether the model is pinned to a
particular revision or must always be the latest. This is completely up to the
connector.
Here is an example of how models might be stored in an .ifcfed:
```json
[
{
"display_name": "foo.ifc",
"id": "0503642e-e2f6-4700-87fd-16479542e801",
"source": {
"connector": "local",
"path": "path/to/foo.ifc" # Only for local
},
},
{
"display_name": "bar.ifc",
"id": "5fc69e6a-1ff0-4d8a-82c6-2215df53d2ed",
"source": {
"connector": "autodesk",
# Below is arbitrary data depending on the connector
"version": "1",
"hub_id": "b.hub123",
"project_id": "b.project456",
"item_id": "urn:adsk.wipprod:dm.lineage:abc",
"version_id": "urn:adsk.wipprod:fs.file:vf.xyz?version=3"
},
},
]
```
Note that cloud metadata (filename, cloud ID, revision, date modified, etc) is
not specified nor stored in the .ifcfed. This is to be returned by the
connector when requested.
The Bonsai Viewer will display all returned cloud metadata as simple text strings.
However some keys are treated specially and shown in more places in the
Bonsai Viewer UI for convenience:
- author
- revision
- date
## Open from cloud workflow
This opens a .ifcfed from a cloud platform and constitutes a fresh session.
Note that downloading the latest versions of all models immediately upon open
is not required. At a minimum, only the .ifcfed needs to be opened. It is
perfectly acceptable to give the user choice on whether to download all or some
models, or use cache (even if outdated). The user can always reopen the project
later.
1. The user presses a button in the Bonsai Viewer UI that says "Open from Cloud"
2. The user chooses a connector.
3. The `pull_ifcfed_interactive` method is sent to the connector.
```json
{ "jsonrpc": "2.0", "id": "0", "method": "pull_ifcfed_interactive" }
```
4. The connector:
- (Does optional workflow) authenticates, browses projects, filters files, etc
- The user selects an .ifcfed file from the connector's UI
- Downloads (or retrieves from cache) the cloud .ifcfed into a connector managed directory
- The connector returns a path to the .ifcfed. The connector must also create an adjacent .ifcfed.manifest file:
```json
{ "jsonrpc": "2.0", "id": "0", "result": { "path": "/path/to/project/file.ifcfed" } }
```
5. The Bonsai Viewer loads the `path`. This constitutes a fresh session.
6. The Bonsai Viewer calls `pull_models`:
```json
{ "jsonrpc": "2.0", "id": "1", "method": "pull_models", "params": [
{ "display_name": ..., "id": ..., "source": ..., },
{ "display_name": ..., "id": ..., "source": ..., },
...
] }
```
7. The connector handles downloading files. It may always check and download the latest version of the file, or be designed to pin to a particular revision, or give the user the option of not downloading a file, etc. or retrieves from its own cache, and returns a path.
```json
{ "jsonrpc": "2.0", "id": "1", "result": [
{ "path": "/path/to/foo.ifc" },
{
"path": "/path/to/model.ifc", # Used to load the model in Bonsai Viewer
"metadata": { "revision": "B", "date": "2nd Oct 2025" ... }, # Optional, used to display stats
},
null, # If skipped, error, etc
{ "path": "/path/to/bar.ifc" },
...
] }
```
8. The Bonsai Viewer may call another connector with more models to be downloaded.
9. The Bonsai Viewer will load the downloaded models as regular files. Typically this will also result in the Bonsai Viewer reading / writing a cache (e.g. .ifcview) alongside this file, but it is not expected that the connector will know or care about this.
## Sync cloud to local
This refreshes cloud-sourced resources in the currently-open project to their
latest cloud revisions, without prompting the user. It is available whenever
the project has any cloud resources: a `.ifcfed.manifest` adjacent to the
.ifcfed, or one or more models whose `source.connector` is not `local`. The
.ifcfed-refresh phase and the model-refresh phase are independent — only the
first requires a manifest.
1. The user presses a button in the Bonsai Viewer UI that says "Sync Cloud to Local"
2. Bonsai Viewer reads the .ifcfed.manifest and invokes the relevant connector with the manifest data with the `pull_ifcfed` method:
```json
{ "jsonrpc": "2.0", "id": "0", "method": "pull_ifcfed", "params": {
"connector": "mycompany", "version": "2", "url": ...
} }
```
3. The connector does what it needs:
- Authenticates (optional)
- (Typically without user interaction) finds the .ifcfed on the cloud platform using the ifcfed manifest
- Downloads (or retrieves from cache) the cloud .ifcfed into a connector managed directory
- The connector returns a path to the .ifcfed. The connector must also create an adjacent .ifcfed.manifest file:
```json
{ "jsonrpc": "2.0", "id": "0", "result": { "path": "/path/to/project/file.ifcfed" } }
```
4. Continue with step 5 of the "Open from cloud" workflow.
If no `.ifcfed.manifest` is present, steps 24 are skipped. The .ifcfed on
disk is used as-is, and the Bonsai Viewer continues from step 6 of the
"Open from cloud" workflow (calling `pull_models` for any cloud-sourced models
referenced in the .ifcfed).
Additionally, if the .ifcfed returned in step 3 is unchanged from the one
already loaded (e.g. the connector served a cached copy because the cloud
revision matched), the Bonsai Viewer skips step 5 as well and continues from
step 6, preserving the current session rather than forcing an unnecessary
fresh one. How "unchanged" is determined (byte equality, hash, mtime, etc.)
is left to the Bonsai Viewer.
## Save as to cloud
This pushes a .ifcfed to a fresh location on a cloud platform, chosen by the
user. It is the "Save As" equivalent and is the only way to first establish a
cloud location for a project that does not yet have a `.ifcfed.manifest`.
1. The user presses a button in the Bonsai Viewer UI that says "Save As to Cloud"
2. The user chooses a connector.
3. The `push_ifcfed_interactive` method is called with the path to the .ifcfed. The connector should treat this as a temporary .ifcfed file, as the real project may or may not be actually saved on disk.
```json
{ "jsonrpc": "2.0", "id": "0", "method": "push_ifcfed_interactive", "params": { "path": "/tmp/path/to/project.ifcfed" } }
```
4. The connector does what it needs:
- (Does optional workflow) authenticates, browses projects, filters files, etc
- Selects existing or writes a new name for an .ifcfed file
- Uploads .ifcfed file to the cloud platform
- The connector returns a path to the .ifcfed. The connector must also create an adjacent .ifcfed.manifest file:
```json
{ "jsonrpc": "2.0", "id": "0", "result": { "path": "/path/to/project/file.ifcfed" } }
```
5. The Bonsai Viewer "repoints" to the returned path. It is not necessary to do a full reload as no "changes" are made.
## Save to cloud
This pushes a .ifcfed back to the cloud location it originally came from,
without prompting the user. It is the "Save" equivalent and is only available
when there is a `.ifcfed.manifest` adjacent to the project.
1. The user presses a button in the Bonsai Viewer UI that says "Save to Cloud"
2. Bonsai Viewer reads the .ifcfed.manifest and invokes the relevant connector with the `push_ifcfed` method, passing both the local path and the manifest data:
```json
{ "jsonrpc": "2.0", "id": "0", "method": "push_ifcfed", "params": {
"path": "/tmp/path/to/project.ifcfed",
"manifest": { "connector": "mycompany", "version": "2", "url": "..." }
} }
```
3. The connector does what it needs:
- Authenticates (optional)
- (Typically without user interaction) locates the existing .ifcfed on the cloud platform using the manifest data
- Uploads the .ifcfed, overwriting or creating a new revision as the platform dictates
- The connector returns a path to the .ifcfed and rewrites the adjacent .ifcfed.manifest if any of its fields have changed (e.g. a new version number):
```json
{ "jsonrpc": "2.0", "id": "0", "result": { "path": "/path/to/project/file.ifcfed" } }
```
4. The Bonsai Viewer "repoints" to the returned path. It is not necessary to do a full reload as no "changes" are made.
Conflict resolution for non-interactive push methods (the cloud copy moved on
since the manifest or source was captured, the user lacks write permission,
revision-pinning policies, etc.) is entirely the connector's responsibility.
The connector may silently overwrite, prompt the user, refuse with a JSON-RPC
error, or anything in between. The Bonsai Viewer expresses no opinion. This rule
also applies to `push_model` below.
## Add model from cloud
1. The user presses a button in the Bonsai Viewer UI that says "Add model from cloud"
2. The user chooses a connector.
3. The `pull_models_interactive` method is sent to the connector.
```json
{ "jsonrpc": "2.0", "id": "0", "method": "pull_models_interactive" }
```
4. The connector does what it needs:
- (Does optional workflow) authenticates, browses projects, filters files, etc
- Selects a model (.ifc, .ifcview, .rdbview, .rdb, etc)
- Downloads (or retrieves from cache) the model into a connector managed directory
- The connector returns a successful result:
```json
{ "jsonrpc": "2.0", "id": "0", "result": [
{
"display_name": "bar.ifc", # Stored in .ifcfed
"source": { "connector": "autodesk", ... }, # Stored in .ifcfed
"path": "/path/to/model.ifc", # Used to load the model in Bonsai Viewer
"metadata": { "revision": "B", "date": "2nd Oct 2025" ... }, # Optional, used to display stats
},
{ ... },
...
] }
```
5. The Bonsai Viewer updates the .ifcfed models section with new models using the
"source" and "display\_name" from the provided data. The models are
immediately loaded from the "path", and the Bonsai Viewer stores the "metadata"
for display. The path and metadata is never stored in the .ifcfed.
## Save model as to cloud
This pushes a model to a fresh location on a cloud platform, chosen by the
user. It is the "Save As" equivalent and is the only way to first establish a
cloud `source` for a model whose current source is `local`.
1. The user presses a button in the Bonsai Viewer UI that says "Save Model As to Cloud"
2. The user chooses a connector.
3. The `push_model_interactive` method is sent to the connector with a path to the model to be uploaded (typically a file, but RocksDB databases can be a folder).
```json
{ "jsonrpc": "2.0", "id": "0", "method": "push_model_interactive", "params": { "path": "/tmp/path/to/model.ifc" } }
```
4. The connector does what it needs:
- (Does optional workflow) authenticates, browses projects, filters files, etc
- Selects existing or types a new name for the model
- Uploads the model (only the file in params, though the connector is free to do optional additional work) to the cloud platform
- The connector returns a successful result:
```json
{ "jsonrpc": "2.0", "id": "0", "result": {
"display_name": "bar.ifc", # Stored in .ifcfed
"path": "/path/to/model.ifc",
"source": { "connector": "autodesk", ... }, # Stored in .ifcfed
"metadata": { "revision": "B", "date": "2nd Oct 2025" ... }, # Optional, used to display stats
} }
```
5. The Bonsai Viewer updates the .ifcfed models section with the new model metadata from the provided data.
## Save model to cloud
This pushes a model back to the cloud location it originally came from,
without prompting the user. It is the "Save" equivalent and is only available
for models whose .ifcfed `source` already points at a cloud connector (i.e.
anything other than `local`).
1. The user presses a button in the Bonsai Viewer UI that says "Save Model to Cloud"
2. Bonsai Viewer invokes the connector named in the model's `source` with the `push_model` method, passing both the local path and the existing `source` object verbatim:
```json
{ "jsonrpc": "2.0", "id": "0", "method": "push_model", "params": {
"path": "/tmp/path/to/model.ifc",
"source": { "connector": "autodesk", "hub_id": "b.hub123", "item_id": "...", ... }
} }
```
3. The connector does what it needs:
- Authenticates (optional)
- (Typically without user interaction) locates the existing model on the cloud platform using the `source` data
- Uploads the model, overwriting or creating a new revision as the platform dictates
- The connector returns a successful result. The returned `source` reflects the just-uploaded revision (e.g. a new `version_id`) and replaces the existing one in the .ifcfed; `display_name` is omitted (the existing one is retained):
```json
{ "jsonrpc": "2.0", "id": "0", "result": {
"source": { "connector": "autodesk", ... }, # Replaces existing source in .ifcfed
"metadata": { "revision": "C", "date": "19th May 2026" ... }, # Optional, used to display stats
} }
```
4. The Bonsai Viewer replaces the model's `source` in the .ifcfed and refreshes the stored metadata for display.
## Connector settings (optional)
A connector MAY implement an `open_settings` method that the Bonsai Viewer invokes
when the user clicks the connector's settings entry (e.g. a gear icon next to
the connector name). The connector is responsible for the entire settings UI:
credentials, sign-out, default folders, anything connector-specific.
1. The user clicks the connector's settings entry in the Bonsai Viewer UI.
2. The Bonsai Viewer sends `open_settings`:
```json
{ "jsonrpc": "2.0", "id": "0", "method": "open_settings" }
```
3. The connector shows its own settings dialog. When the user closes it, the
connector returns:
```json
{ "jsonrpc": "2.0", "id": "0", "result": {} }
```
If the connector returns a JSON-RPC `Method not found` error (code `-32601`),
the Bonsai Viewer should treat that connector as having no settings and hide its
settings entry. There is no other discovery mechanism — the viewer probes by
calling the method when needed.
The connector is free to use this method for things like:
- Signing in / signing out
- Setting API keys, client ids, or other credentials
- Choosing default upload folders or revision policies
- Clearing the connector's cache
The result object is currently always empty (`{}`); future revisions may add
optional fields (for example a fresh display label for the connector).
### Error Response
The connector owns all user-facing error handling: dialogs, retry prompts,
re-auth flows, logs. The Bonsai Viewer does not interpret or display connector
errors directly.
The protocol expresses only two outcomes:
- **Per-item soft failure** (one model in a batch failed, others succeeded):
the connector returns `null` in that slot of the result array. The Bonsai Viewer
skips it and continues.
- **Whole-call hard failure** (the connector cannot service the request at
all): the connector returns a JSON-RPC error object. The Bonsai Viewer aborts
the operation. The `error.message` may be logged by the Bonsai Viewer for
diagnostics, but is not shown to the user — the connector is expected to
have already surfaced the problem in its own UI.
Diagnostic detail (stack traces, codes, retry context) should be written to
stderr, which the Bonsai Viewer captures for logs.
## Permissions
Permissions are completely managed by the connector. For example:
- one Autodesk model resolves successfully
- one Aconex model fails with access denied
- one Dropbox model resolves successfully
The viewer will tolerate partial failure and report skipped resources that the connector cannot resolve. A federation does not need to become all-or-nothing just because some remote models are permission-restricted.
## Connector discovery
A connector is shipped as a folder containing a `connector.json` manifest and
an executable entry point. The Bonsai Viewer discovers connectors by scanning a
small, fixed set of locations for these folders.
### Connector bundle layout
```
<some-connectors-dir>/
autodesk/ # folder name is arbitrary; id comes from connector.json
connector.json # required, at the folder root
bonsaiviewer-autodesk # the executable (or a wrapper script)
... # anything else the connector ships
```
### `connector.json`
```json
{
"id": "autodesk",
"name": "Autodesk Forma",
"version": "0.1.0",
"exec": "./bonsaiviewer-autodesk"
}
```
- `id` — stable identifier used in `.ifcfed` `source.connector` fields and in
`.ifcfed.manifest`. Must be unique across all discovered connectors.
- `name` — human-readable label shown in the Bonsai Viewer UI.
- `version` — connector version string; informational only.
- `exec` — path to the connector executable. Relative paths are resolved
against the connector folder; absolute paths are used as-is. Bundled
connectors should use a relative path so the bundle is self-contained.
On Windows, the Bonsai Viewer will also try `<exec>.exe` if `<exec>` does not
exist as written.
### Search locations
The Bonsai Viewer scans the **user connectors directory**. The platform's per-user
application data location:
- Linux: `~/.local/share/IfcOpenShell/BonsaiViewer/connectors/`
- macOS: `~/Library/Application Support/IfcOpenShell/BonsaiViewer/connectors/`
- Windows: `%APPDATA%\IfcOpenShell\BonsaiViewer\connectors\`
The Bonsai Viewer looks at every immediate subdirectory and treats it as a
connector iff it contains a `connector.json`. Connectors are launched on
demand when the user invokes a cloud workflow, not at startup.
### Conflicts and errors
- If two folders declare the same `id`, the one found earlier in directory
order wins; the loser is skipped and a warning is written to the Bonsai Viewer's
log.
- A `connector.json` that is missing, unreadable, malformed, or missing
required fields causes that folder to be skipped (with a log entry); other
connectors are unaffected.
- A connector whose `exec` cannot be resolved or launched is reported to the
user only when the user actually tries to invoke it.
-100
View File
@@ -1,100 +0,0 @@
# `bonsaiviewer-autodesk`
Autodesk Forma (APS / Docs) connector for Bonsai Viewer.
Implements the JSON-RPC connector contract defined in
[`CLOUD_SYNC_PROTOCOL.md`](CLOUD_SYNC_PROTOCOL.md). The connector is a separate
process the viewer launches and speaks to over stdio.
UI is built on **CustomTkinter** (Tcl/Tk under the hood), keeping the
packaged connector around 50 MB unpacked / 21 MB zipped on Linux. The Python
running this code must include `tkinter` (most distribution Python builds do;
on Gentoo make sure `USE="tk"` is set for `dev-lang/python`).
## Install
```bash
cd src/bonsaiviewer-autodesk
python -m venv venv
source venv/bin/activate
pip install -e .
```
## Run
```bash
bonsaiviewer-autodesk
```
The connector launches without any configuration; on first run, invoke
`open_settings` (or, equivalently, set the `APS_CLIENT_ID` env var) to
configure the Autodesk client id and OAuth callback port.
Then send newline-delimited JSON-RPC 2.0 requests on `stdin`. Examples:
```json
{"jsonrpc":"2.0","id":"0","method":"open_settings"}
{"jsonrpc":"2.0","id":"1","method":"pull_ifcfed_interactive"}
{"jsonrpc":"2.0","id":"2","method":"pull_models","params":[{"display_name":"foo.ifc","id":"abc","source":{"connector":"autodesk","hub_id":"b.hub","project_id":"b.proj","item_id":"urn:adsk...","version_id":"latest"}}]}
{"jsonrpc":"2.0","id":"3","method":"push_ifcfed_interactive","params":{"path":"/tmp/project.ifcfed"}}
{"jsonrpc":"2.0","id":"4","method":"push_ifcfed","params":{"path":"/tmp/project.ifcfed","manifest":{"connector":"autodesk","hub_id":"b.hub","project_id":"b.proj","item_id":"urn:adsk..."}}}
```
The viewer is expected to launch this binary once per session and keep it alive
until shutdown; closing the connector's `stdin` triggers a clean exit.
### Configuration
The connector reads the Autodesk client id from two places, in order:
1. The `APS_CLIENT_ID` environment variable (takes precedence — useful for dev
overrides).
2. `<config dir>/settings.json` (persisted via the settings dialog).
The OAuth callback host is always `localhost`. The callback port defaults to
`8080` and can be changed in the settings dialog.
The config directory is platform-specific:
- Linux: `~/.config/bonsaiviewer-autodesk/`
- macOS: `~/Library/Application Support/bonsaiviewer-autodesk/`
- Windows: `%APPDATA%\bonsaiviewer-autodesk\`
OAuth tokens are stored in the OS keychain (Secret Service on Linux, Keychain
on macOS, Credential Manager on Windows), keyed by the client id, so changing
the client id starts a fresh session.
## Cache
The connector owns its own cache. Resolved files live under (Linux):
```
~/.cache/bonsaiviewer-autodesk/
ifcfeds/<hash>/<name>.ifcfed[.manifest]
models/<hash>/<filename>
```
Each file is the sole child in its directory so the viewer can write sidecar
files (e.g. `.ifcview`) next to it without colliding. A new resolved version of
a model lands in a fresh `models/<hash>/` directory; the old directory may be
removed manually to clear space.
## Status
What is implemented:
- Strict JSON-RPC 2.0 host over stdio
- APS PKCE sign-in with keyring-backed token store
- Hub / project / folder browsing (Qt UI)
- `pull_ifcfed_interactive`, `pull_ifcfed`, `pull_models`, `pull_models_interactive`
- `push_ifcfed_interactive`, `push_ifcfed`, `push_model_interactive`, `push_model`
- `open_settings` — edit the client id and callback port, sign out
- Connector-managed cache with sole-child invariant
- Adjacent `.ifcfed.manifest` written/read alongside `.ifcfed` files
What is intentionally not implemented:
- JSON-RPC notifications for progress streaming (the connector shows its own
progress dialog instead, per spec)
- Cancellation of in-flight downloads
- Subdirectory upload layouts inside push destinations (one flat file at a time)
@@ -1,102 +0,0 @@
# Packaging the Autodesk connector
The connector is shipped as a self-contained folder ready to drop into the
Bonsai Viewer connectors directory. PyInstaller bundles the Python interpreter,
Qt, and all dependencies so end users do not need Python installed.
PyInstaller does **not** cross-compile. Each OS must build on itself —
typically via a CI matrix.
## Output
```
dist/
autodesk/ # the connector folder, ready to install
connector.json
bonsaiviewer-autodesk[.exe]
_internal/... # PyInstaller dependencies (Qt, Python, …)
autodesk-<os>-<arch>.zip # the distribution archive
```
The folder is what Bonsai Viewer expects under
`~/.local/share/IfcOpenShell/BonsaiViewer/connectors/` (or the OS equivalent).
## Build steps (any OS)
```bash
cd src/bonsaiviewer-autodesk
python -m venv venv
venv/bin/activate # or venv\Scripts\activate on Windows
pip install -e ".[build]"
python packaging/build.py
```
The build:
1. cleans `dist/` and `build/`
2. runs PyInstaller against `packaging/bonsaiviewer-autodesk.spec`
3. renames the produced folder to `autodesk/` and copies `connector.json` into it
4. zips the folder as `autodesk-<os>-<arch>.zip`
The UI is Tcl/Tk via CustomTkinter, which keeps the bundle small. Expect
~50 MB unpacked / ~21 MB zipped per OS. The Python used to run the build
must include `tkinter` — most distribution and python-build-standalone
builds do; on Gentoo make sure `USE="tk"` is set for `dev-lang/python`.
## Per-OS notes
### Linux
- Build on the **oldest glibc** you intend to support. Binaries built on a
newer glibc will not run on older distributions. Ubuntu 22.04 LTS
(glibc 2.35) is a reasonable lowest common denominator in 2026.
- The keyring backend used at runtime is `SecretService` (gnome-keyring or
KWallet); end users need a Secret Service provider running.
- Output: `autodesk-linux-x86_64.zip` (and/or `arm64`).
### macOS
- Each architecture builds separately. To support both Apple Silicon and
Intel, build on each and ship two zips, or post-process with `lipo` to
produce universal binaries.
- The keyring backend is the system Keychain.
- For distribution outside the developer's machine you will need to
**codesign** the executable and Tcl/Tk dylibs, and notarize the bundle.
Unsigned binaries trigger Gatekeeper warnings. Codesigning is left to the
caller; the spec's `codesign_identity` field can be wired up.
- Output: `autodesk-macos-arm64.zip` and/or `autodesk-macos-x86_64.zip`.
### Windows
- Build with the Microsoft Visual C++ runtime available (usually present in
any modern Python distribution).
- The keyring backend is Credential Manager.
- The `.exe` is built with `console=True` because the connector speaks
JSON-RPC over stdio. The Bonsai Viewer must launch the connector with
`CREATE_NO_WINDOW` (Qt: `QProcess::setCreateProcessArgumentsModifier`) so
end users never see a console window flicker.
- For distribution: sign the `.exe` with an Authenticode certificate to
avoid SmartScreen warnings. Signing is left to the caller.
- Output: `autodesk-windows-x86_64.zip`.
## Installing a built connector
```bash
# Linux
unzip dist/autodesk-linux-x86_64.zip -d ~/.local/share/IfcOpenShell/BonsaiViewer/connectors/
# macOS
unzip dist/autodesk-macos-arm64.zip -d "~/Library/Application Support/IfcOpenShell/BonsaiViewer/connectors/"
# Windows (PowerShell)
Expand-Archive dist\autodesk-windows-x86_64.zip -DestinationPath "$env:APPDATA\IfcOpenShell\BonsaiViewer\connectors\"
```
Bonsai Viewer picks up the connector on next launch.
## Out of scope here
- Signing / notarization (caller's responsibility per OS)
- CI matrix (project-level concern)
- Auto-update (the Bonsai Viewer or the host installer handles this)
- Universal macOS binaries via `lipo` (post-process step, not part of `build.py`)
+1 -1
View File
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
name = "bonsaiviewer-autodesk"
version = "0.1.0"
description = "Autodesk cloud connector for Bonsai Viewer"
readme = "README.md"
readme = { text = "Autodesk cloud connector for Bonsai Viewer. See src/bonsaiviewer/docs/connectors/autodesk.rst in the IfcOpenShell repository.", content-type = "text/x-rst" }
requires-python = ">=3.11"
dependencies = [
"customtkinter>=5.2",
+20
View File
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@echo ""
@echo "You can also do 'make server' to launch html server for previously built html docs."
.PHONY: help Makefile
server:
python -m http.server 8080 --directory $(BUILDDIR)/html
.PHONY: server Makefile
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+126
View File
@@ -0,0 +1,126 @@
@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@200;300;400;500;600;700;800&display=swap");
@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@200;300;400;500;600;700&display=swap');
h1, h2, h3, h4 {
font-weight: normal;
color: #7cbf33;
background: -webkit-linear-gradient(0deg, #7cbf33, #049344);
background-clip: border-box;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
a {
text-decoration: none;
}
.toc-tree .reference:hover {
color: #d98014;
}
.sidebar-brand-text {
font-size: 1rem;
text-align: center;
}
.blockbutton {
max-width: 500px;
margin-left: auto;
margin-right: auto;
text-align: center;
}
.blockbutton a {
display: block;
padding-top: 15px;
padding-bottom: 15px;
border-radius: 5px;
background: #40b74c;
width: 100%;
height: 100%;
color: #fff;
text-decoration: none;
}
.blockbutton a:hover {
background: #70ba35;
}
.blockbutton a.download code {
background: none !important;
border: none !important;
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: bold;
padding: 0 !important;
}
.highlight .hll {
background-color: #ffc2;
}
section img {
display: block;
margin-left: auto;
margin-right: auto;
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
border-radius: 5px;
}
.location-scene img {
display: inline;
border-radius: 0px;
box-shadow: none;
vertical-align: middle;
margin-top: -3px;
}
.location-scene p {
margin: 0.75rem;
}
.location-scene {
border: 1px solid var(--color-admonition-title);
background-color: var(--color-admonition-title-background);
border-radius: 5px;
font-style: italic;
color: var(--color-admonition-text);
}
img.transparent {
width: auto;
height: auto;
box-shadow: none;
}
img.icon {
width: auto;
height: auto;
border-radius: 0px;
box-shadow: none;
vertical-align: middle;
}
.content figure figcaption>p {
font-style: italic;
font-size: small;
color: #808080;
}
span.menuselection {
border: 1px solid var(--color-admonition-title);
background-color: var(--color-admonition-title-background);
color: var(--color-admonition-text);
border-radius: 5px;
padding-left: 3px;
padding-right: 3px;
}
.toc-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
grid-gap: 20px;
list-style-type: none;
margin-bottom: 24px;
}
+89
View File
@@ -0,0 +1,89 @@
# Configuration file for the Sphinx documentation builder.
import os
from datetime import datetime
project = "Bonsai Viewer"
copyright = f"2020-{datetime.now().year} IfcOpenShell Contributors"
author = "IfcOpenShell Contributors"
cwd = os.path.dirname(os.path.realpath(__file__))
with open(os.path.join(cwd, "..", "..", "..", "VERSION"), "r") as f:
release = f.read().strip()
extensions = ["sphinx.ext.autodoc", "sphinx.ext.autosectionlabel", "sphinx_copybutton"]
autosectionlabel_prefix_document = True
autosectionlabel_maxdepth = 2
templates_path = []
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".venv"]
html_theme = "furo"
html_static_path = ["_static"]
html_css_files = ["custom.css"]
pygments_style = "one-dark"
pygments_dark_style = "one-dark"
html_favicon = "https://ifcopenshell.org/assets/images/logo.png"
html_logo = "https://ifcopenshell.org/assets/images/logo.png"
html_theme_options = {
"source_repository": "https://github.com/IfcOpenShell/IfcOpenShell/",
"source_branch": "v0.8.0",
"source_directory": "src/bonsaiviewer/docs/",
"light_css_variables": {
"color-brand-primary": "#39b54a",
"color-brand-content": "#39b54a",
"color-brand-visited": "#d9e021",
"color-background-primary": "#f7f7f6",
"color-background-secondary": "#eeeeec",
"color-background-border": "#cfd0cb",
"color-foreground-primary": "#2e3436",
"color-sidebar-item-background--hover": "#f7f7f6",
"color-link": "#39b54a",
"color-link--visited": "#39b54a",
"color-link--hover": "#d98014",
"color-link--visited--hover": "#d98014",
"color-admonition-text": "#651fff",
"font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji",
},
"dark_css_variables": {
"color-brand-primary": "#39b54a",
"color-brand-content": "#39b54a",
"color-brand-visited": "#d9e021",
"color-background-primary": "#2e3436",
"color-background-border": "#2e3436",
"color-foreground-primary": "#eeeeec",
"color-sidebar-item-background--hover": "#2e3436",
"color-link": "#39b54a",
"color-link--visited": "#39b54a",
"color-link--hover": "#d98014",
"color-link--visited--hover": "#d98014",
"color-admonition-text": "#EEEEEC",
"font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji",
},
"footer_icons": [
{
"name": "IfcOpenShell",
"url": "https://ifcopenshell.org",
"html": """
<img src="https://ifcopenshell.org/assets/images/logo.png" style="width: auto;" />
""",
"class": "",
},
{
"name": "GitHub",
"url": "https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.8.0/src/bonsaiviewer/docs",
"html": """
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>
</svg>
""",
"class": "",
},
],
}
@@ -0,0 +1,111 @@
Autodesk Connector
==================
The Autodesk connector integrates Bonsai Viewer with Autodesk Forma, APS, and
Docs. It implements the cloud connector protocol and runs as a separate process
that Bonsai Viewer launches and communicates with over standard input and
standard output.
The connector UI is built with CustomTkinter. The Python runtime used for
development or packaging must include ``tkinter``. On Gentoo, make sure
``dev-lang/python`` is built with ``USE="tk"``.
Install
-------
From the repository root:
.. code-block:: bash
cd src/bonsaiviewer-autodesk
python -m venv venv
source venv/bin/activate
pip install -e .
Run
---
.. code-block:: bash
bonsaiviewer-autodesk
The connector launches without configuration. On first run, open the settings
dialog, or set the ``APS_CLIENT_ID`` environment variable, to configure the
Autodesk client ID and OAuth callback port.
For direct protocol testing, send newline-delimited JSON-RPC 2.0 requests on
standard input:
.. code-block:: json
{"jsonrpc":"2.0","id":"0","method":"open_settings"}
{"jsonrpc":"2.0","id":"1","method":"pull_ifcfed_interactive"}
{"jsonrpc":"2.0","id":"2","method":"pull_models","params":[{"display_name":"foo.ifc","id":"abc","source":{"connector":"autodesk","hub_id":"b.hub","project_id":"b.proj","item_id":"urn:adsk...","version_id":"latest"}}]}
{"jsonrpc":"2.0","id":"3","method":"push_ifcfed_interactive","params":{"path":"/tmp/project.ifcfed"}}
{"jsonrpc":"2.0","id":"4","method":"push_ifcfed","params":{"path":"/tmp/project.ifcfed","manifest":{"connector":"autodesk","hub_id":"b.hub","project_id":"b.proj","item_id":"urn:adsk..."}}}
Bonsai Viewer is expected to launch this binary once per session and keep it
alive until shutdown. Closing the connector's standard input triggers a clean
exit.
Configuration
-------------
The connector reads the Autodesk client ID from these places, in order:
1. ``APS_CLIENT_ID`` environment variable.
2. ``settings.json`` in the connector config directory, written by the settings
dialog.
The OAuth callback host is always ``localhost``. The callback port defaults to
``8080`` and can be changed in the settings dialog.
The config directory is platform-specific:
- Linux: ``~/.config/bonsaiviewer-autodesk/``
- macOS: ``~/Library/Application Support/bonsaiviewer-autodesk/``
- Windows: ``%APPDATA%\bonsaiviewer-autodesk\``
OAuth tokens are stored in the OS keychain, keyed by the client ID. Changing
the client ID starts a fresh sign-in session. The keychain backend is Secret
Service on Linux, Keychain on macOS, and Credential Manager on Windows.
Cache
-----
The connector owns its own cache. On Linux, resolved files live under:
.. code-block:: text
~/.cache/bonsaiviewer-autodesk/
ifcfeds/<hash>/<name>.ifcfed[.manifest]
models/<hash>/<filename>
Each resolved file is the sole child in its directory so Bonsai Viewer can
write sidecar files, such as ``.ifcview``, next to it without colliding. A new
resolved model version lands in a fresh ``models/<hash>/`` directory. Old cache
directories may be removed manually to clear space.
Status
------
Implemented:
- strict JSON-RPC 2.0 host over stdio
- APS PKCE sign-in with keyring-backed token storage
- hub, project, and folder browsing
- ``pull_ifcfed_interactive``, ``pull_ifcfed``, ``pull_models``, and
``pull_models_interactive``
- ``push_ifcfed_interactive``, ``push_ifcfed``,
``push_model_interactive``, and ``push_model``
- ``open_settings`` for client ID, callback port, and sign-out
- connector-managed cache with one resolved file per directory
- adjacent ``.ifcfed.manifest`` files written and read alongside
``.ifcfed`` files
Not implemented:
- JSON-RPC notifications for progress streaming; the connector shows its own
progress dialog
- cancellation of in-flight downloads
- subdirectory upload layouts inside push destinations
@@ -0,0 +1,130 @@
Packaging the Autodesk Connector
================================
The Autodesk connector is shipped as a self-contained folder that can be
dropped into the Bonsai Viewer connectors directory. PyInstaller bundles the
Python interpreter, Qt, and dependencies so end users do not need Python
installed.
PyInstaller does not cross-compile. Each operating system must build its own
package, typically through a CI matrix.
Output
------
.. code-block:: text
dist/
autodesk/
connector.json
bonsaiviewer-autodesk[.exe]
_internal/...
autodesk-<os>-<arch>.zip
The ``autodesk/`` folder is what Bonsai Viewer expects under the
``connectors`` directory bundled next to the viewer executable
(``<application-dir>/connectors/``).
Build
-----
.. code-block:: bash
cd src/bonsaiviewer-autodesk
python -m venv venv
source venv/bin/activate
pip install -e ".[build]"
python packaging/build.py
On Windows, activate the virtual environment with
``venv\Scripts\activate``.
The build script:
1. cleans ``dist/`` and ``build/``
2. runs PyInstaller against ``packaging/bonsaiviewer-autodesk.spec``
3. renames the produced folder to ``autodesk/``
4. copies ``connector.json`` into it
5. zips the folder as ``autodesk-<os>-<arch>.zip``
The UI uses Tcl/Tk through CustomTkinter, which keeps the bundle small. Expect
roughly 50 MB unpacked or 21 MB zipped per OS. The Python used to build must
include ``tkinter``.
Linux
-----
Build on the oldest glibc you intend to support. Binaries built on a newer
glibc will not run on older distributions. Ubuntu 22.04 LTS, with glibc 2.35,
is a reasonable lowest common denominator in 2026.
The runtime keyring backend is Secret Service, such as gnome-keyring or
KWallet. End users need a Secret Service provider running.
Expected output: ``autodesk-linux-x86_64.zip`` and/or an ARM64 archive.
macOS
-----
Each architecture builds separately. To support Apple Silicon and Intel, build
on each architecture and ship two zips, or post-process with ``lipo`` to
produce universal binaries.
The runtime keyring backend is the system Keychain. For distribution outside
the developer's machine, codesign the executable and Tcl/Tk dylibs and notarize
the bundle. Unsigned binaries trigger Gatekeeper warnings. Codesigning is left
to the caller; the spec's ``codesign_identity`` field can be wired up.
Expected output: ``autodesk-macos-arm64.zip`` and/or
``autodesk-macos-x86_64.zip``.
Windows
-------
Build with the Microsoft Visual C++ runtime available, which is usually true
for modern Python distributions. The runtime keyring backend is Credential
Manager.
The executable is built with ``console=True`` because the connector speaks
JSON-RPC over stdio. Bonsai Viewer must launch it without showing a console
window, for example with Qt's ``QProcess::setCreateProcessArgumentsModifier``
and ``CREATE_NO_WINDOW``.
For distribution, sign the executable with an Authenticode certificate to avoid
SmartScreen warnings. Signing is left to the caller.
Expected output: ``autodesk-windows-x86_64.zip``.
Install a Built Connector
-------------------------
Extract the zip into the ``connectors`` directory next to the Bonsai Viewer
executable.
Linux:
.. code-block:: bash
unzip dist/autodesk-linux-x86_64.zip -d <application-dir>/connectors/
macOS:
.. code-block:: bash
unzip dist/autodesk-macos-arm64.zip -d "<application-dir>/connectors/"
Windows PowerShell:
.. code-block:: powershell
Expand-Archive dist\autodesk-windows-x86_64.zip -DestinationPath "<application-dir>\connectors\"
Bonsai Viewer discovers the connector on next launch.
Out of Scope
------------
- signing and notarization
- CI matrix setup
- auto-update
- universal macOS binaries via ``lipo``
@@ -0,0 +1,384 @@
Cloud Connector Protocol
========================
Bonsai Viewer can load and save projects and models from cloud platforms
through connectors. A connector is a separate application launched by Bonsai
Viewer. It handles authentication, browsing, downloads, uploads, caching, and
platform-specific behaviour.
The protocol currently covers:
- projects: ``.ifcfed``
- models: ``.ifc``, ``.rdb``, ``.ifcview``, ``.rdbview``
Issues, clash results, specifications, and other resources are not yet defined.
Communication
-------------
Bonsai Viewer launches one connector process per session, on first use, and
keeps it alive for the duration of the session. Communication uses
newline-delimited JSON-RPC 2.0 over standard input and standard output:
- requests and responses are single-line JSON objects
- every message is terminated by a single newline
- the connector must not emit literal newlines inside a JSON message
- diagnostics may be written to standard error
Bonsai Viewer shuts down a connector by closing its standard input. The
connector should exit cleanly. If it does not exit within a few seconds, Bonsai
Viewer may terminate it.
Connector Scope
---------------
Bonsai Viewer only works with local files. If a project or model is not local,
the viewer asks a connector to resolve it. The connector returns a local path
and optional cloud metadata. It must not modify the model file itself.
The connector handles anything specific to the cloud platform or data source:
authentication, browsing, filtering, searches, revision pinning, progress UI,
caches, and platform-specific rules. A connector may show its own UI or run
without UI.
Returned files must be the sole child in their directory. This allows Bonsai
Viewer to write adjacent sidecars, temporary files, database locks, or helper
files without filename collisions. A connector may invalidate cached files by
deleting the whole resolved directory. A new cloud revision should resolve to a
fresh directory.
Projects
--------
A project is an ``.ifcfed`` file. It stores project settings and model entries.
Example project:
.. code-block:: json
{
"created": "2026-04-29T21:22:36Z",
"modified": "2026-04-29T21:22:36Z",
"home_view": null,
"models": []
}
A cloud-sourced project may have an adjacent ``.ifcfed.manifest`` file storing
connector-specific source metadata.
.. code-block:: json
{
"connector": "mycompany",
"version": "2",
"url": "https://example.com/project.ifcfed"
}
Models
------
Models are listed in the ``.ifcfed`` file. A model may point to a local path or
to a cloud connector source.
Cloud connector source data is intentionally connector-specific. For example,
a connector may store whether a model is pinned to one revision or should
always resolve to the latest revision.
.. code-block:: json
[
{
"display_name": "foo.ifc",
"id": "0503642e-e2f6-4700-87fd-16479542e801",
"source": {
"connector": "local",
"path": "path/to/foo.ifc"
}
},
{
"display_name": "bar.ifc",
"id": "5fc69e6a-1ff0-4d8a-82c6-2215df53d2ed",
"source": {
"connector": "autodesk",
"version": "1",
"hub_id": "b.hub123",
"project_id": "b.project456",
"item_id": "urn:adsk.wipprod:dm.lineage:abc",
"version_id": "urn:adsk.wipprod:fs.file:vf.xyz?version=3"
}
}
]
Cloud metadata such as filename, cloud ID, revision, and date modified is not
specified or stored in the ``.ifcfed``. It is returned by the connector when
requested. Bonsai Viewer displays returned cloud metadata as text. The keys
``author``, ``revision``, and ``date`` may be shown in more prominent UI
locations.
Open from Cloud
---------------
``pull_ifcfed_interactive`` opens an ``.ifcfed`` from a cloud platform and
starts a fresh session.
.. code-block:: json
{ "jsonrpc": "2.0", "id": "0", "method": "pull_ifcfed_interactive" }
The connector authenticates and lets the user browse or choose a project. It
downloads or resolves the ``.ifcfed`` into a connector-managed directory,
creates an adjacent ``.ifcfed.manifest``, and returns the local path:
.. code-block:: json
{ "jsonrpc": "2.0", "id": "0", "result": { "path": "/path/to/project/file.ifcfed" } }
Bonsai Viewer loads the project, then calls ``pull_models`` for cloud-sourced
models referenced by the ``.ifcfed``:
.. code-block:: json
{
"jsonrpc": "2.0",
"id": "1",
"method": "pull_models",
"params": [
{ "display_name": "bar.ifc", "id": "...", "source": { "connector": "autodesk" } }
]
}
The connector returns one result per requested model. A result may include
optional metadata. ``null`` means that item was skipped or failed without
aborting the whole batch.
.. code-block:: json
{
"jsonrpc": "2.0",
"id": "1",
"result": [
{
"path": "/path/to/model.ifc",
"metadata": { "revision": "B", "date": "2nd Oct 2025" }
},
null
]
}
Downloading the latest version of every model immediately is not required. A
connector may download the latest file, use a pinned revision, offer the user a
choice, skip a model, or return a cached file. The user can reopen or sync the
project later.
Sync Cloud to Local
-------------------
``pull_ifcfed`` refreshes the cloud-sourced project without prompting the user.
It is available when the project has cloud resources: an adjacent
``.ifcfed.manifest`` for the project itself, one or more cloud-sourced models,
or both.
.. code-block:: json
{
"jsonrpc": "2.0",
"id": "0",
"method": "pull_ifcfed",
"params": {
"connector": "mycompany",
"version": "2",
"url": "https://example.com/project.ifcfed"
}
}
The connector returns a local ``.ifcfed`` path and writes a fresh adjacent
manifest. Bonsai Viewer then continues by calling ``pull_models`` for
cloud-sourced models. The project-refresh phase and model-refresh phase are
independent. If no manifest exists, the project refresh phase is skipped and
Bonsai Viewer refreshes only cloud-sourced models.
If the returned ``.ifcfed`` is unchanged from the currently loaded file, Bonsai
Viewer may skip the fresh-session reload and continue to model refresh. How
"unchanged" is determined, such as byte equality, hash, or modified time, is
left to Bonsai Viewer.
Save Projects to Cloud
----------------------
``push_ifcfed_interactive`` is the cloud equivalent of Save As. It pushes an
``.ifcfed`` to a new user-selected cloud location.
.. code-block:: json
{
"jsonrpc": "2.0",
"id": "0",
"method": "push_ifcfed_interactive",
"params": { "path": "/tmp/path/to/project.ifcfed" }
}
The connector uploads the file, writes an adjacent manifest, and returns the
resolved local path.
``push_ifcfed`` is the cloud equivalent of Save. It pushes back to the existing
cloud location described by the manifest.
.. code-block:: json
{
"jsonrpc": "2.0",
"id": "0",
"method": "push_ifcfed",
"params": {
"path": "/tmp/path/to/project.ifcfed",
"manifest": {
"connector": "mycompany",
"version": "2",
"url": "https://example.com/project.ifcfed"
}
}
}
The connector may update the manifest if the cloud platform returns a new
revision or version identifier.
Add and Save Models
-------------------
``pull_models_interactive`` lets the user choose one or more cloud models to
add to the current project.
.. code-block:: json
{ "jsonrpc": "2.0", "id": "0", "method": "pull_models_interactive" }
The connector returns model entries with ``display_name``, ``source``, a local
``path``, and optional metadata:
.. code-block:: json
{
"jsonrpc": "2.0",
"id": "0",
"result": [
{
"display_name": "bar.ifc",
"source": { "connector": "autodesk" },
"path": "/path/to/model.ifc",
"metadata": { "revision": "B", "date": "2nd Oct 2025" }
}
]
}
Bonsai Viewer stores ``display_name`` and ``source`` in the ``.ifcfed`` and
loads the model from ``path``. The path and metadata are not stored in the
``.ifcfed``.
``push_model_interactive`` is Save Model As to Cloud:
.. code-block:: json
{
"jsonrpc": "2.0",
"id": "0",
"method": "push_model_interactive",
"params": { "path": "/tmp/path/to/model.ifc" }
}
``push_model`` saves a model back to the existing cloud location described by
the model's ``source``:
.. code-block:: json
{
"jsonrpc": "2.0",
"id": "0",
"method": "push_model",
"params": {
"path": "/tmp/path/to/model.ifc",
"source": { "connector": "autodesk", "hub_id": "b.hub123" }
}
}
The connector returns an updated ``source`` and optional metadata. Bonsai
Viewer replaces the model's ``source`` in the ``.ifcfed``.
Conflict handling for non-interactive push methods is the connector's
responsibility. The connector may overwrite, prompt, reject with a JSON-RPC
error, or apply a platform-specific policy.
Settings and Errors
-------------------
A connector may implement ``open_settings`` for credentials, sign-out, default
folders, cache management, or other connector-specific settings.
.. code-block:: json
{ "jsonrpc": "2.0", "id": "0", "method": "open_settings" }
If a connector returns JSON-RPC ``Method not found`` with code ``-32601``,
Bonsai Viewer treats it as having no settings UI.
The settings result is currently always an empty object. Future revisions may
add optional fields, such as a refreshed display label for the connector.
The connector owns user-facing error handling. Bonsai Viewer understands only:
- per-item soft failure: ``null`` in a result array
- whole-call hard failure: a JSON-RPC error object
Diagnostic details should be written to standard error for logs.
Permissions
-----------
Permissions are entirely connector-managed. A federation may contain models
from multiple platforms, and some resources may fail while others succeed.
Bonsai Viewer tolerates partial failure and does not require all remote models
to resolve successfully.
Discovery
---------
A connector is shipped as a folder containing a ``connector.json`` manifest and
an executable entry point.
.. code-block:: text
<connectors-dir>/
autodesk/
connector.json
bonsaiviewer-autodesk
...
Example ``connector.json``:
.. code-block:: json
{
"id": "autodesk",
"name": "Autodesk Forma",
"version": "0.1.0",
"exec": "./bonsaiviewer-autodesk"
}
Fields:
- ``id`` is the stable identifier used in ``.ifcfed`` ``source.connector``
fields and in ``.ifcfed.manifest`` files.
- ``name`` is the human-readable UI label.
- ``version`` is informational.
- ``exec`` is the connector executable. Relative paths are resolved against the
connector folder. On Windows, Bonsai Viewer also tries ``<exec>.exe`` if the
path does not exist as written.
Bonsai Viewer scans the ``connectors`` directory bundled alongside the
executable (``<application-dir>/connectors/``).
Every immediate subdirectory containing ``connector.json`` is treated as a
connector. Connectors are launched on demand, not at startup.
If two folders declare the same ``id``, the first one wins and the other is
skipped with a log warning. Malformed manifests or missing executables are
reported when relevant without preventing other connectors from loading.
@@ -0,0 +1,14 @@
Connectors
==========
Bonsai Viewer connectors are separate applications that let the viewer open
and save projects or models from cloud platforms. The viewer itself works with
local files; a connector resolves cloud resources into local cached files and
returns the metadata needed to keep them connected to the cloud source.
.. toctree::
:maxdepth: 2
cloud_sync_protocol
autodesk
autodesk_packaging
+45
View File
@@ -0,0 +1,45 @@
Learn Bonsai Viewer
===================
Hello world. Bonsai Viewer is a desktop application for opening, federating,
and reviewing IFC models.
It is focused on fast model viewing rather than authoring. It works with IFC
files and viewer-oriented formats such as ``.ifcview`` and ``.rdbview``.
.. only:: builder_html and (not singlehtml)
.. container:: toc-cards
.. container:: card
Getting started
Open a model, navigate the scene, and inspect project data.
.. container:: card
Large models
Use viewer caches and packaged formats for faster loading.
.. container:: card
Federation
Combine multiple model files into one review session.
.. container:: card
:doc:`/connectors/index`
Load models from external systems and cloud workflows.
.. container:: global-index-toc
.. toctree::
:hidden:
:caption: Contents
:maxdepth: 2
connectors/index
Need more help? Join the `live chat <https://osarch.org/chat/>`__ or `community
forums <https://community.osarch.org>`__. Something strange happening? Please
`report a bug <https://github.com/IfcOpenShell/IfcOpenShell/issues>`__.
+35
View File
@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
+3
View File
@@ -0,0 +1,3 @@
sphinx
furo
sphinx-copybutton
@@ -83,7 +83,7 @@ QString userConnectorsDir() {
// Linux -> ~/.local/share
// macOS -> ~/Library/Application Support
// Win -> %APPDATA% (Roaming)
// matching CLOUD_SYNC_PROTOCOL.md's listed locations.
// matching the Bonsai Viewer connector protocol docs.
const QString base = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
return QDir(base).filePath("IfcOpenShell/BonsaiViewer/connectors");
}
@@ -35,7 +35,7 @@ struct ConnectorManifest {
// otherwise a bare name for PATH lookup at launch
};
// Scans the per-user connectors directory per CLOUD_SYNC_PROTOCOL.md.
// Scans the per-user connectors directory per the connector protocol docs.
// First match wins for any given id; duplicates and malformed manifests
// are skipped with a qWarning. Returned in discovery order so first-wins
// is observable to callers.