Rename ifcviewer-autodesk connector to bonsaiviewer-autodesk

Follows the host viewer's rename to Bonsai Viewer: directory, Python
package, entry point, PyInstaller spec, keyring service, and on-disk
config/cache paths all use the bonsaiviewer-autodesk name. CI workflow
filename and path filters updated to match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-05-20 09:49:34 +10:00
parent 59e5b2b1b8
commit 084c87ea06
17 changed files with 121 additions and 114 deletions
@@ -1,15 +1,15 @@
name: Build IfcViewer Autodesk Connector
name: Build Bonsai Viewer Autodesk Connector
on:
workflow_dispatch:
push:
paths:
- 'src/ifcviewer-autodesk/**'
- '.github/workflows/build-ifcviewer-autodesk.yml'
- 'src/bonsaiviewer-autodesk/**'
- '.github/workflows/build-bonsaiviewer-autodesk.yml'
pull_request:
paths:
- 'src/ifcviewer-autodesk/**'
- '.github/workflows/build-ifcviewer-autodesk.yml'
- 'src/bonsaiviewer-autodesk/**'
- '.github/workflows/build-bonsaiviewer-autodesk.yml'
jobs:
build:
@@ -39,7 +39,7 @@ jobs:
defaults:
run:
working-directory: src/ifcviewer-autodesk
working-directory: src/bonsaiviewer-autodesk
steps:
- uses: actions/checkout@v6
@@ -64,5 +64,5 @@ jobs:
uses: actions/upload-artifact@v7
with:
name: autodesk-${{ matrix.os_label }}-${{ matrix.arch }}
path: src/ifcviewer-autodesk/dist/autodesk-${{ matrix.os_label }}-${{ matrix.arch }}.zip
path: src/bonsaiviewer-autodesk/dist/autodesk-${{ matrix.os_label }}-${{ matrix.arch }}.zip
if-no-files-found: error
+3 -3
View File
@@ -129,6 +129,6 @@ src/ifcopenshell-python/ifcopenshell/express/*.exp.cache.dat
*.py.tmp*
*.json.tmp*
# ifcviewer-autodesk connector build artifacts
/src/ifcviewer-autodesk/build/
/src/ifcviewer-autodesk/dist/
# bonsaiviewer-autodesk connector build artifacts
/src/bonsaiviewer-autodesk/build/
/src/bonsaiviewer-autodesk/dist/
@@ -1,13 +1,13 @@
# IfcViewer cloud connectors
# Bonsai Viewer cloud connectors
IfcViewer will have the capability to load and save projects and models from a
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 IfcViewer itself will depend on
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 IfcViewer.
application which will communicate to and from the Bonsai Viewer.
The following types of resources may be managed with a connector:
@@ -18,7 +18,7 @@ The following types of resources may be managed with a connector:
## Communication protocol
The IfcViewer launches one connector process per session, on first use, and
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.
@@ -28,18 +28,18 @@ 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 IfcViewer
- The connector may write arbitrary diagnostic output to stderr; the Bonsai Viewer
will not parse it.
The IfcViewer shuts a connector down by closing its stdin. The connector should
exit cleanly. If it does not exit within a few seconds, the IfcViewer will
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 IfcViewer has minimal knowledge about connectors. IfcViewer only knows how
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 IfcViewer's
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.
@@ -52,7 +52,7 @@ 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 IfcViewer.
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,
@@ -140,9 +140,9 @@ 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 IfcViewer will display all returned cloud metadata as simple text strings.
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
IfcViewer UI for convenience:
Bonsai Viewer UI for convenience:
- author
- revision
@@ -157,7 +157,7 @@ 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 IfcViewer UI that says "Open from Cloud"
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
@@ -171,8 +171,8 @@ later.
```json
{ "jsonrpc": "2.0", "id": "0", "result": { "path": "/path/to/project/file.ifcfed" } }
```
5. The IfcViewer loads the `path`. This constitutes a fresh session.
6. The IfcViewer calls `pull_models`:
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": ..., },
@@ -185,7 +185,7 @@ later.
{ "jsonrpc": "2.0", "id": "1", "result": [
{ "path": "/path/to/foo.ifc" },
{
"path": "/path/to/model.ifc", # Used to load the model in IfcViewer
"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
@@ -193,8 +193,8 @@ later.
...
] }
```
8. The IfcViewer may call another connector with more models to be downloaded.
9. The IfcViewer will load the downloaded models as regular files. Typically this will also result in the IfcViewer reading / writing a cache (e.g. .ifcview) alongside this file, but it is not expected that the connector will know or care about this.
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
@@ -205,8 +205,8 @@ the project has any cloud resources: a `.ifcfed.manifest` adjacent to 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 IfcViewer UI that says "Sync Cloud to Local"
2. IfcViewer reads the .ifcfed.manifest and invokes the relevant connector with the manifest data with the `pull_ifcfed` method:
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": ...
@@ -223,16 +223,16 @@ first requires a manifest.
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 IfcViewer continues from step 6 of the
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 IfcViewer skips step 5 as well and continues from
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 IfcViewer.
is left to the Bonsai Viewer.
## Save as to cloud
@@ -240,7 +240,7 @@ 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 IfcViewer UI that says "Save As to Cloud"
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
@@ -254,7 +254,7 @@ cloud location for a project that does not yet have a `.ifcfed.manifest`.
```json
{ "jsonrpc": "2.0", "id": "0", "result": { "path": "/path/to/project/file.ifcfed" } }
```
5. The IfcViewer "repoints" to the returned path. It is not necessary to do a full reload as no "changes" are made.
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
@@ -262,8 +262,8 @@ 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 IfcViewer UI that says "Save to Cloud"
2. IfcViewer reads the .ifcfed.manifest and invokes the relevant connector with the `push_ifcfed` method, passing both the local path and the manifest data:
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",
@@ -278,18 +278,18 @@ when there is a `.ifcfed.manifest` adjacent to the project.
```json
{ "jsonrpc": "2.0", "id": "0", "result": { "path": "/path/to/project/file.ifcfed" } }
```
4. The IfcViewer "repoints" to the returned path. It is not necessary to do a full reload as no "changes" are made.
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 IfcViewer expresses no opinion. This rule
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 IfcViewer UI that says "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
@@ -305,16 +305,16 @@ also applies to `push_model` below.
{
"display_name": "bar.ifc", # Stored in .ifcfed
"source": { "connector": "autodesk", ... }, # Stored in .ifcfed
"path": "/path/to/model.ifc", # Used to load the model in IfcViewer
"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 IfcViewer updates the .ifcfed models section with new models using the
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 IfcViewer stores the "metadata"
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
@@ -323,7 +323,7 @@ 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 IfcViewer UI that says "Save Model As to Cloud"
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
@@ -342,7 +342,7 @@ cloud `source` for a model whose current source is `local`.
"metadata": { "revision": "B", "date": "2nd Oct 2025" ... }, # Optional, used to display stats
} }
```
5. The IfcViewer updates the .ifcfed models section with the new model metadata from the provided data.
5. The Bonsai Viewer updates the .ifcfed models section with the new model metadata from the provided data.
## Save model to cloud
@@ -351,8 +351,8 @@ 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 IfcViewer UI that says "Save Model to Cloud"
2. IfcViewer 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:
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",
@@ -370,17 +370,17 @@ anything other than `local`).
"metadata": { "revision": "C", "date": "19th May 2026" ... }, # Optional, used to display stats
} }
```
4. The IfcViewer replaces the model's `source` in the .ifcfed and refreshes the stored metadata for display.
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 IfcViewer invokes
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 IfcViewer UI.
2. The IfcViewer sends `open_settings`:
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" }
```
@@ -391,7 +391,7 @@ credentials, sign-out, default folders, anything connector-specific.
```
If the connector returns a JSON-RPC `Method not found` error (code `-32601`),
the IfcViewer should treat that connector as having no settings and hide its
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.
@@ -408,22 +408,22 @@ 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 IfcViewer does not interpret or display connector
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 IfcViewer
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 IfcViewer aborts
the operation. The `error.message` may be logged by the IfcViewer for
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 IfcViewer captures for logs.
stderr, which the Bonsai Viewer captures for logs.
## Permissions
@@ -438,7 +438,7 @@ The viewer will tolerate partial failure and report skipped resources that the c
## Connector discovery
A connector is shipped as a folder containing a `connector.json` manifest and
an executable entry point. The IfcViewer discovers connectors by scanning a
an executable entry point. The Bonsai Viewer discovers connectors by scanning a
small, fixed set of locations for these folders.
### Connector bundle layout
@@ -447,7 +447,7 @@ small, fixed set of locations for these folders.
<some-connectors-dir>/
autodesk/ # folder name is arbitrary; id comes from connector.json
connector.json # required, at the folder root
ifcviewer-autodesk # the executable (or a wrapper script)
bonsaiviewer-autodesk # the executable (or a wrapper script)
... # anything else the connector ships
```
@@ -458,38 +458,38 @@ small, fixed set of locations for these folders.
"id": "autodesk",
"name": "Autodesk Forma",
"version": "0.1.0",
"exec": "./ifcviewer-autodesk"
"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 IfcViewer UI.
- `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 IfcViewer will also try `<exec>.exe` if `<exec>` does not
On Windows, the Bonsai Viewer will also try `<exec>.exe` if `<exec>` does not
exist as written.
### Search locations
The IfcViewer scans the **user connectors directory**. The platform's per-user
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 IfcViewer looks at every immediate subdirectory and treats it as a
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 IfcViewer's
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
@@ -1,6 +1,6 @@
# `ifcviewer-autodesk`
# `bonsaiviewer-autodesk`
Autodesk Forma (APS / Docs) connector for IfcViewer.
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
@@ -14,7 +14,7 @@ on Gentoo make sure `USE="tk"` is set for `dev-lang/python`).
## Install
```bash
cd src/ifcviewer-autodesk
cd src/bonsaiviewer-autodesk
python -m venv venv
source venv/bin/activate
pip install -e .
@@ -23,7 +23,7 @@ pip install -e .
## Run
```bash
ifcviewer-autodesk
bonsaiviewer-autodesk
```
The connector launches without any configuration; on first run, invoke
@@ -53,9 +53,9 @@ The connector reads the Autodesk client id from two places, in order:
The config directory is platform-specific:
- Linux: `~/.config/ifcviewer-autodesk/`
- macOS: `~/Library/Application Support/ifcviewer-autodesk/`
- Windows: `%APPDATA%\ifcviewer-autodesk\`
- 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
@@ -66,7 +66,7 @@ the client id starts a fresh session.
The connector owns its own cache. Resolved files live under (Linux):
```
~/.cache/ifcviewer-autodesk/
~/.cache/bonsaiviewer-autodesk/
ifcfeds/<hash>/<name>.ifcfed[.manifest]
models/<hash>/<filename>
```
@@ -2,9 +2,9 @@ from __future__ import annotations
import sys
from ifcviewer_autodesk.connector import AutodeskConnector
from ifcviewer_autodesk.rpc import JsonRpcHost
from ifcviewer_autodesk.ui import ensure_tk_app
from bonsaiviewer_autodesk.connector import AutodeskConnector
from bonsaiviewer_autodesk.rpc import JsonRpcHost
from bonsaiviewer_autodesk.ui import ensure_tk_app
def main() -> int:
@@ -17,7 +17,7 @@ import httpx
import keyring
import keyring.errors
from ifcviewer_autodesk.rpc import JSONRPC_INTERNAL_ERROR, RpcError
from bonsaiviewer_autodesk.rpc import JSONRPC_INTERNAL_ERROR, RpcError
Progress = Callable[[str, str, "int | None"], None]
@@ -13,12 +13,12 @@ def cache_root() -> Path:
system = platform.system()
if system == "Windows":
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
root = Path(base) / "ifcviewer-autodesk" / "Cache"
root = Path(base) / "bonsaiviewer-autodesk" / "Cache"
elif system == "Darwin":
root = Path.home() / "Library" / "Caches" / "ifcviewer-autodesk"
root = Path.home() / "Library" / "Caches" / "bonsaiviewer-autodesk"
else:
base = os.environ.get("XDG_CACHE_HOME") or os.path.expanduser("~/.cache")
root = Path(base) / "ifcviewer-autodesk"
root = Path(base) / "bonsaiviewer-autodesk"
root.mkdir(parents=True, exist_ok=True)
return root
@@ -5,10 +5,10 @@ import traceback
from pathlib import Path
from typing import Any, Callable
from ifcviewer_autodesk import cache, settings
from ifcviewer_autodesk.autodesk import ApsClient, AuthSessionService, KeyringTokenStore
from ifcviewer_autodesk.rpc import JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS, RpcError
from ifcviewer_autodesk.ui import BrowseDialog, SettingsDialog, progress_dialog, prompt_for_filename
from bonsaiviewer_autodesk import cache, settings
from bonsaiviewer_autodesk.autodesk import ApsClient, AuthSessionService, KeyringTokenStore
from bonsaiviewer_autodesk.rpc import JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS, RpcError
from bonsaiviewer_autodesk.ui import BrowseDialog, SettingsDialog, progress_dialog, prompt_for_filename
ApsProgress = Callable[[str, "int | None"], None]
@@ -34,7 +34,7 @@ def _upload_callback(report: Report) -> ApsProgress:
CONNECTOR_ID = "autodesk"
KEYRING_SERVICE = "ifcviewer-autodesk"
KEYRING_SERVICE = "bonsaiviewer-autodesk"
DEFAULT_CALLBACK_URL = "http://localhost:8080/"
DEFAULT_SCOPE = "data:read data:write data:create"
@@ -11,12 +11,12 @@ def config_root() -> Path:
system = platform.system()
if system == "Windows":
base = os.environ.get("APPDATA") or os.path.expanduser("~")
root = Path(base) / "ifcviewer-autodesk"
root = Path(base) / "bonsaiviewer-autodesk"
elif system == "Darwin":
root = Path.home() / "Library" / "Application Support" / "ifcviewer-autodesk"
root = Path.home() / "Library" / "Application Support" / "bonsaiviewer-autodesk"
else:
base = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser("~/.config")
root = Path(base) / "ifcviewer-autodesk"
root = Path(base) / "bonsaiviewer-autodesk"
root.mkdir(parents=True, exist_ok=True)
return root
@@ -6,12 +6,12 @@ from typing import TYPE_CHECKING, Any, Callable, Literal
import customtkinter as ctk
from ifcviewer_autodesk import settings
from ifcviewer_autodesk.autodesk import ApsClient, AuthSessionService, KeyringTokenStore
from ifcviewer_autodesk.rpc import JSONRPC_INTERNAL_ERROR, RpcError
from bonsaiviewer_autodesk import settings
from bonsaiviewer_autodesk.autodesk import ApsClient, AuthSessionService, KeyringTokenStore
from bonsaiviewer_autodesk.rpc import JSONRPC_INTERNAL_ERROR, RpcError
if TYPE_CHECKING:
from ifcviewer_autodesk.connector import AutodeskConnector
from bonsaiviewer_autodesk.connector import AutodeskConnector
MODEL_EXTENSIONS = (".ifc", ".ifcview", ".rdb", ".rdbview")
@@ -161,10 +161,17 @@ class _ProgressContext:
def __exit__(self, *_exc: object) -> None:
if self.dialog is not None:
try:
self.dialog.withdraw()
self.dialog.destroy()
except tk.TclError:
pass
self.dialog = None
try:
root = ensure_tk_app()
root.update_idletasks()
root.update()
except tk.TclError:
pass
def progress_dialog(message: str) -> _ProgressContext:
@@ -629,7 +636,7 @@ class SettingsDialog(_BaseDialog):
):
return
try:
KeyringTokenStore(service_name="ifcviewer-autodesk", username=client_id).delete()
KeyringTokenStore(service_name="bonsaiviewer-autodesk", username=client_id).delete()
except RpcError as exc:
show_error(title="Sign Out Failed", message=exc.message)
return
@@ -2,5 +2,5 @@
"id": "autodesk",
"name": "Autodesk Forma",
"version": "0.1.0",
"exec": "ifcviewer-autodesk"
"exec": "bonsaiviewer-autodesk"
}
@@ -1,7 +1,7 @@
# Packaging the Autodesk connector
The connector is shipped as a self-contained folder ready to drop into the
IfcViewer connectors directory. PyInstaller bundles the Python interpreter,
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 —
@@ -13,7 +13,7 @@ typically via a CI matrix.
dist/
autodesk/ # the connector folder, ready to install
connector.json
ifcviewer-autodesk[.exe]
bonsaiviewer-autodesk[.exe]
_internal/... # PyInstaller dependencies (Qt, Python, …)
autodesk-<os>-<arch>.zip # the distribution archive
```
@@ -24,7 +24,7 @@ The folder is what Bonsai Viewer expects under
## Build steps (any OS)
```bash
cd src/ifcviewer-autodesk
cd src/bonsaiviewer-autodesk
python -m venv venv
venv/bin/activate # or venv\Scripts\activate on Windows
pip install -e ".[build]"
@@ -34,7 +34,7 @@ python packaging/build.py
The build:
1. cleans `dist/` and `build/`
2. runs PyInstaller against `packaging/ifcviewer-autodesk.spec`
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`
@@ -72,7 +72,7 @@ builds do; on Gentoo make sure `USE="tk"` is set for `dev-lang/python`.
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 IfcViewer must launch the connector with
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
@@ -98,5 +98,5 @@ Bonsai Viewer picks up the connector on next launch.
- Signing / notarization (caller's responsibility per OS)
- CI matrix (project-level concern)
- Auto-update (the IfcViewer or the host installer handles this)
- 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,7 +1,7 @@
# PyInstaller spec for the IfcViewer Autodesk connector (Tk + CustomTkinter).
# PyInstaller spec for the Bonsai Viewer Autodesk connector (Tk + CustomTkinter).
#
# The connector talks JSON-RPC over stdio, so `console=True` is required to
# attach stdin/stdout on Windows. The IfcViewer is expected to spawn the
# attach stdin/stdout on Windows. The Bonsai Viewer is expected to spawn the
# connector with the OS's "hide console window" flag on Windows
# (CREATE_NO_WINDOW) so end users never see a console pop up.
@@ -22,7 +22,7 @@ HIDDEN_IMPORTS = [
a = Analysis(
[str(PROJECT_ROOT / "ifcviewer_autodesk" / "__main__.py")],
[str(PROJECT_ROOT / "bonsaiviewer_autodesk" / "__main__.py")],
pathex=[str(PROJECT_ROOT)],
binaries=[],
datas=[],
@@ -56,7 +56,7 @@ exe = EXE(
a.scripts,
[],
exclude_binaries=True,
name="ifcviewer-autodesk",
name="bonsaiviewer-autodesk",
debug=False,
bootloader_ignore_signals=False,
strip=False,
@@ -77,5 +77,5 @@ coll = COLLECT(
strip=False,
upx=True,
upx_exclude=[],
name="ifcviewer-autodesk",
name="bonsaiviewer-autodesk",
)
@@ -1,12 +1,12 @@
"""Build the Autodesk connector bundle for the current OS.
Each OS builds on itself (PyInstaller does not cross-compile). The output is a
single zip ready to drop into the IfcViewer connectors directory:
single zip ready to drop into the Bonsai Viewer connectors directory:
dist/autodesk-<os>-<arch>.zip
autodesk/
connector.json
ifcviewer-autodesk[.exe]
bonsaiviewer-autodesk[.exe]
_internal/...
Usage:
@@ -27,12 +27,12 @@ from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
PACKAGING_DIR = PROJECT_ROOT / "packaging"
SPEC_FILE = PACKAGING_DIR / "ifcviewer-autodesk.spec"
SPEC_FILE = PACKAGING_DIR / "bonsaiviewer-autodesk.spec"
DIST_DIR = PROJECT_ROOT / "dist"
BUILD_DIR = PROJECT_ROOT / "build"
CONNECTOR_FOLDER_NAME = "autodesk"
PYINSTALLER_OUTPUT_NAME = "ifcviewer-autodesk"
PYINSTALLER_OUTPUT_NAME = "bonsaiviewer-autodesk"
def _platform_tag() -> str:
@@ -92,7 +92,7 @@ def _assemble_connector_folder(pyinstaller_output: Path) -> Path:
# `pip install -e .` works for development. For the bundled folder, the
# binary lives next to connector.json, so rewrite `exec` to a relative path.
manifest = json.loads((PROJECT_ROOT / "connector.json").read_text(encoding="utf-8"))
binary_name = "ifcviewer-autodesk.exe" if platform.system() == "Windows" else "ifcviewer-autodesk"
binary_name = "bonsaiviewer-autodesk.exe" if platform.system() == "Windows" else "bonsaiviewer-autodesk"
manifest["exec"] = f"./{binary_name}"
(connector_dir / "connector.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
@@ -3,9 +3,9 @@ requires = ["setuptools>=69", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "ifcviewer-autodesk"
name = "bonsaiviewer-autodesk"
version = "0.1.0"
description = "Autodesk cloud connector for IfcViewer"
description = "Autodesk cloud connector for Bonsai Viewer"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
@@ -24,4 +24,4 @@ include-package-data = true
where = ["."]
[project.scripts]
ifcviewer-autodesk = "ifcviewer_autodesk.__main__:main"
bonsaiviewer-autodesk = "bonsaiviewer_autodesk.__main__:main"