The Trimble Connect Workspace API allows the client applications to interact with the Trimble Connect Web. It utilizes window.postMessage() to facilitate the communication. There are two methods to utilize the Trimble Connect Workspace API:
For Typescript use, add the Trimble Connect Workspace API component as a dependency to your project. The component bundles TypeScript v2 definition files for use in TypeScript projects and to support tools that can read .d.ts files. For ES6-module-friendly consumers, you can also import the modules directly, or import them from the root module.
install trimble-connect-workspace-api npm package.
npm i trimble-connect-workspace-api --save
Alternatively, it is also possible to directly import the IIFE module from the below URL.
<script src="https://components.connect.trimble.com/trimble-connect-workspace-api/index.js" />
Put together the 3D Viewer and the client application:
iframe
HTML
<iframe
src="https://web.connect.trimble.com/?isEmbedded=true"
id="viewer"
sandbox="allow-scripts allow-modals allow-forms allow-same-origin"
width="100%"
height="700px" />
Javascript
const API = await TrimbleConnectWorkspace.connect(document.getElementById("viewer"),
(event, args) => {
console.log(event, args);
}
);
await API.embed.setTokens({accessToken: "accessToken_here"});
// (or) await API.embed.setTokens({shareToken: "shareToken_here"});
await API.embed.init3DViewer({projectId: "projectId_here"});
// (or) await API.embed.init3DViewer({}); // if you want to select a project from the list
HTML
<iframe
src="https://web.connect.trimble.com/projects/:projectId/viewer/3d"
id="viewer"
sandbox="allow-scripts allow-modals allow-forms allow-same-origin"
width="100%"
height="700px" />
projectId
can be undefined if you want to select a project from the projects list.Javascript
const API = await TrimbleConnectWorkspace.connect(document.getElementById("viewer"),
(event, args) => {
console.log(event, args);
}
);
await API.embed.setTokens({accessToken: "accessToken_here"});
// (or) await API.embed.setTokens({shareToken: "shareToken_here"});
Configuring the extension in 3D Viewer:
It is possible to configure extensions that are used for each Connect Project via 3D Viewer Extensions Settings. Basically, it is possible to define a new extension manually or through an extension definition (a manifest):
Manually: Specify all extension parameters manually
Extension URL: The URL to the extension manifest file, which is a JSON file that specifies all extension parameters (e.g. Extension title, Extension URL...). The data structure follows the ExtensionSetting
interface. For example:
{
"url": "https://my.awesome.app/extension",
"title": "This is my extension",
"infoUrl": "https://my.awesome.app/extension/help.html"
}
All the APIs will be exposed through the return value of connect
method.
Authentication:
accessToken
should be passed to the 3D Viewer using embed.setTokens
. It will also accept refreshToken
optionally.If the refreshToken
passed then the accessToken
refresh responsibility is passed to the 3D Viewer. Keep in mind that the accessToken
refresh responsibility cannot be shared between the client application and 3D Viewer, only one application should perform the refresh. If the accessToken
is refreshed by 3D Viewer, 3D Viewer will emit an extension.accessToken
event with the new accessToken
.
If the refreshToken
is not passed to the 3D Viewer, it is the client application's responsibility to handle the accessToken
refresh. 3D Viewer will help in the process by emitting the extension.sessionInvalid
event when the accessToken
expired or was invalidated. After the accessToken
is refreshed by the client application, it can be passed back to the 3D Viewer by calling the same embed.setTokens
API with the new accessToken
.
When the client application is embedded inside the 3D Viewer as an extension, the accessToken
should be retrieved from 3D Viewer by calling the extension.requestPermission
API. This call will initially display a message requesting for the user consent for the client application to use the accessToken
. If the user consents, the call will return the accessToken
which can then be used by the client application.
In this case, the 3D Viewer application will handle the tokens, including the tokens refresh. When the accessToken
is refreshed, the 3D Viewer will emit an extension.accessToken
event with the new accessToken
.
Query parameters:
When using embed.init3DViewer
, you can pass all the possible query params as a config
argument to init3DViewer
. No query params are needed.
Note: The following query params kept only for legacy support, we highly recommand the use of embed.init3DViewer
.
embed
: https://web.connect.trimble.com/projects/:projectId/viewer/3d/?embed=false
Specifies if the application is embedded in a sandboxed environment. If true
, the application will emulate the local storage.
projectId
: https://web.connect.trimble.com/projects/:projectId/viewer/3d/?projectId=projectId
The id of a project that will be automatically loaded by 3D Viewer application, if it exists.
modelId
: https://web.connect.trimble.com/projects/:projectId/viewer/3d/?projectId=projectId&modelId=my_model_id1,my_model_id2
The id of a model that will be automatically loaded by 3D Viewer application, if it exists in the specified project. Used in conjuction with projectId
. Accepts an array of values, separated by comma.
versionId
: https://web.connect.trimble.com/projects/:projectId/viewer/3d/?projectId=project_id&modelId=model_id1,model_id2&versionId=model_version_id1,model_version_id2
The version id of a model that will be automatically loaded by 3D Viewer application, if it exists in the specified project. Used in conjuction with modelId
and projectId
. Accepts an array of values, separated by comma.
viewId
: https://web.connect.trimble.com/projects/:projectId/viewer/3d/?projectId=projectId&viewId=my_view_id
The id of a view that will be automatically loaded by 3D Viewer application, if it exists. Used in conjuction with projectId
.
todoId
: https://web.connect.trimble.com/projects/:projectId/viewer/3d/?projectId=projectId&todoId=my_todo_id
The id of a to do that will be automatically loaded by 3D Viewer application, if it exists. Used in conjuction with projectId
.
clashSetId
: https://web.connect.trimble.com/projects/:projectId/viewer/3d/?projectId=projectId&clashSetId=my_clash_set_id
The id of a clash set that will be automatically loaded by 3D Viewer application, if it exists. Used in conjuction with projectId
.
Examples: Initiate the connection between the client application and 3D Viewer
Embed the 3D Viewer inside the application using an iframe
: This should be done after the iframe
has been loaded. The connection initiation should be done in an async function as the connection is only established once the user has selected a Trimble Connect project in the 3D Viewer.
<script src="https://components.connect.trimble.com/trimble-connect-workspace-api/index.js"></script>
<div>
<button onclick="doConnect()">connect</button>
<iframe hidden id="viewer" src="https://web.connect.trimble.com/?isEmbedded=true" width="100%" height="750px"/>
<script>
function doConnect(){
var viewer = document.getElementById("viewer");
viewer.hidden = false;
var API = await TrimbleConnectWorkspace.connect(viewer, (event, data) => {
console.log("Event: ", event, data);
});
API.embed.setTokens({accessToken: "accessToken_here"}); // or {shareToken: "shareToken_here"}
API.embed.init3DViewer({projectId: "projectId_here"});
}
</script>
</div>
Embed the application inside the 3D Viewer as an extension: Directly issue the connect
request to the parent window (3D Viewer) that is hosting the application:
<script src="https://components.connect.trimble.com/trimble-connect-workspace-api/index.js"></script>
<script>
var API = await TrimbleConnectWorkspace.connect(window.parent, (event, data) => {
console.log("Event: ", event, data);
});
API.project.getProject().then(project => {
console.log(project); // Trimble Connect project details
});
</script>
Check out the example client application for more details!
Trimble Connect Web extension framework to enable extension owners (or integrator applications) to expose additional capabilities / functionalities through Trimble Connect Web application within the Trimble Connect project context.
This framework allows creation of new extensions through manifest files and displays the menu provided by the extension in TCWEB left panel navigation. It also allows additional configuration for the extensions if needed (through configCommand property).
Example Implementation
import * as WorkspaceAPI from "trimble-connect-workspace-api";
....
....
/** Instantiate extension .
* @param window - Parent window object.
* @param callback - Listen the events with args from the parent.
* @param timeout - Connect timeout in milliseconds.
* @returns TCExtensionAPI - Object with the interaction methods.
*/
this.API = await WorkspaceAPI.connect(
window.parent,
(event, args) => {
switch (event) {
case "extension.command":
//"Command executed by the user: args.data"
break;
case "extension.accessToken":
//"Accestoken or status: args.data"
break;
case "extension.userSettingsChanged":
//"User settings changed!"
break;
default:
}
},
30000
);
....
....
// Updating the menu object.
this.API.ui.setMenu("<mainMenuObject>:ExtensionMainMenu")
// Where <mainMenuObject> is the menu object and ExtensionMainMenu is the object type which you can find it in the documentation.
// Updating the active submenu.
this.API.ui.setActiveMenuItem("<submenuCommand>:string");
// Get the current project info
this.API.project.getCurrentProject().then((projectInfo: ConnectProject) => {
//Current project info: projectInfo.
});
//Get the current user language info.
this.API.user.getUserSettings().then((userSettings: UserSettings) => {
//Current user language: userSettings.language
});
//Updating the status message.
this.API.extension.setStatusMessage("<statusMessage>:string");
//Request for the access token.
this.API.extension.getPermission("accesstoken").then((accessToken: string) => {
//Current user access token or status: accessToken
});
Example manifest JSON
{
"icon": "http://example.com/app_icon.png",
"title": "React example",
"url": "http://example.com/index.html",
"description": "Test extension",
"configCommand": "do_config",
"enabled": false
}
Example manifest JSON link.
While adding new custom extensions, please ensure your manifest file url is CORS enabled to avoid any issues in the extension creation. As the TCWEB application will try to download the extension manifest file to read it's contents before creating the extension, it is important that the manifest file url (example: https://foo.example.manifestfile.com/manifest.json
) is CORS enabled to allow access from Trimble Connect Web. For example, when Trimble Connect Web tries to fetch the file, the server should respond with proper CORS response headers (Example: Access-Control-Allow-Origin, Access-Control-Allow-Methods etc.,). Please refer to Cross-Origin Resource Sharing (CORS) - HTTP | MDN for details.
Example menu object
const mainMenuObject = {
title: "Test extension app",
icon: "http://example.com/main_menu_icon.png",
command: "main_nav_menu_cliked",
subMenus: [
{
title: "Sub menu 1",
icon: "http://example.com/sub_menu_icon.png",
command: "submenu_1_clicked",
},
{
title: "Sub menu 2",
icon: "http://example.com/sub_menu_icon.png",
command: "submenu_2_clicked",
},
],
};
Example
How to create an extension in TCWEB?
How to enable / disable extension?
Who can configure the extensions?
Who decides on the main navigation menu for the extension? What is the structure of the menu?
The navigation menu object is passed by the extension application (refers to the url mentioned in the extension manifest) to TCWEB and TCWEB renders it in it’s left panel navigation.
The menu object should conform to the below structure. As of now the menu object can contain just two levels - one parent menu item and one/more submenu items. Please note that the values in the sample below are given for illustration purposes only and hence they need to be updated to reflect your extension’s need.
Dynamic Menu Structure: (received from extension while loading the extension. extension creator should be aware of this):
{
"title": "Quadri shared Model",
"icon": "https://abcd.com/images/q.svg",
"command": "QUADRI_TOP_MENU", /*This will be passed to the extension when the menu is clicked*/
"subMenus": [
{
"title": "Quadri shared Modelsubmenu1",
"icon": "https://abcd.com/images/q1.svg",
"command": "QUADRI_SUB_MENU1", /*This will be passed to the extension when the menu is clicked*/
},
{
"title": "Quadri shared Model submenu2",
"icon": "https://abcd.com/images/q2.svg",
"command": "QUADRI_SUB_MENU2", /*This will be passed to the extension when the menu is clicked*/
},
]
}
Where the extensions will be displayed?
What is the mechanism used to enable interaction between the extension application and the TCWEB application?
Are extension manifest url and the extension url the same?
Can I pass query params along with submenu command while calling setActiveMenuItem?
task
. If the setActiveMenu
API is called with the command task?id=7
, TCWEB UI will make the 'task' submenu active and the extension will receive what (command with query parameters - task?id=7) it has passed to the TCWEB application.After an access token has been expired for a few hours, may API.embed.setTokens still be used to wake up the connection?
What is the difference between the error messages received during the integrations with the trimble-connect-workspace-api - “Please refresh the user session. The user session is invalid” and “Access Denied. You don’t have access to the trimble connect web modules"
Will the same event "embed.session.invalid" be sent for both cases mentioned above?