diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue
index 8a91dca..cbf2555 100644
--- a/src/components/ControlPanel.vue
+++ b/src/components/ControlPanel.vue
@@ -14,7 +14,9 @@
Control Panel
-
+
+
+
@@ -22,6 +24,14 @@
diff --git a/src/components/SpeckleViewer.vue b/src/components/SpeckleViewer.vue
index 4b63ac1..f000f13 100644
--- a/src/components/SpeckleViewer.vue
+++ b/src/components/SpeckleViewer.vue
@@ -6,16 +6,16 @@
diff --git a/src/composables/viewer.ts b/src/composables/viewer.ts
deleted file mode 100644
index ceaf8a7..0000000
--- a/src/composables/viewer.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { ref } from 'vue'
-import {
- CameraController,
- DefaultViewerParams,
- SelectionExtension,
- SpeckleLoader,
- UrlHelper,
- Viewer,
-} from '@speckle/viewer'
-
-let viewer: Viewer | null = null
-
-export default function useViewer() {
- /**
- * Initialize the viewer
- * @param element - HTMLDivElement to initialize the viewer on
- */
- async function init(element: HTMLDivElement) {
- // Set the default viewer parameters
- const params = DefaultViewerParams
- params.showStats = false
- params.verbose = true
-
- // Create the viewer instance on the element
- viewer = new Viewer(element, params)
-
- await viewer.init()
-
- // Add the stock camera controller and selection extensions
- viewer.createExtension(CameraController)
- viewer.createExtension(SelectionExtension)
- }
-
- /**
- * Load a model from a Speckle URL
- * @param url - The URL of the Speckle model
- */
- const loadModelFromUrl = async (url: string) => {
- if (!viewer) return
-
- const urls = await UrlHelper.getResourceUrls(url)
- urls.forEach(async url => {
- const loader = new SpeckleLoader(viewer.getWorldTree(), url, '')
- await viewer.loadObject(loader, true)
- })
- }
-
- return {
- init,
- viewer,
- loadModelFromUrl,
- }
-}
diff --git a/src/composables/viewer/actions.ts b/src/composables/viewer/actions.ts
new file mode 100644
index 0000000..45dd8eb
--- /dev/null
+++ b/src/composables/viewer/actions.ts
@@ -0,0 +1,14 @@
+import useViewer from '@/composables/viewer/viewer'
+
+export default function useViewerActions() {
+ const { viewer } = useViewer()
+
+ const getObjectProperties = async () => {
+ if (!viewer) return
+ return await viewer.getObjectProperties()
+ }
+
+ return {
+ getObjectProperties
+ }
+}
diff --git a/src/composables/viewer/viewer.ts b/src/composables/viewer/viewer.ts
new file mode 100644
index 0000000..d9ce943
--- /dev/null
+++ b/src/composables/viewer/viewer.ts
@@ -0,0 +1,63 @@
+import {
+ CameraController,
+ DefaultViewerParams,
+ SelectionExtension,
+ FilteringExtension,
+ SpeckleLoader,
+ UrlHelper,
+ Viewer
+} from '@speckle/viewer'
+import { Catalogue } from '@/extensions/Catalogue'
+
+let viewer: Viewer | undefined = undefined
+
+export default function useViewer() {
+ /**
+ * Initialize the viewer
+ * @param element - DOM element to initialize the viewer on
+ */
+ async function init(element: HTMLDivElement) {
+ const params = {
+ ...DefaultViewerParams,
+ showStats: false,
+ verbose: true
+ }
+
+ viewer = new Viewer(element, params)
+ await viewer.init()
+ }
+
+ /**
+ * The viewer can be extended with additional functionality by adding extensions
+ * You can use extensions provided by the viewer, or create your own
+ */
+ function addExtensions() {
+ if (!viewer) return
+ viewer.createExtension(CameraController)
+ viewer.createExtension(SelectionExtension)
+ viewer.createExtension(FilteringExtension)
+ // Example of a custom extension
+ viewer.createExtension(Catalogue)
+ }
+
+ /**
+ * Load a model from a Speckle URL
+ * @param url - The URL of the Speckle model
+ * @param authToken - This is required if the model is private
+ */
+ const loadModelFromUrl = async (url: string, authToken?: string) => {
+ const urls = await UrlHelper.getResourceUrls(url, authToken)
+ urls.forEach(async (url) => {
+ if (!viewer) return
+ const loader = new SpeckleLoader(viewer.getWorldTree(), url, '')
+ await viewer.loadObject(loader, true)
+ })
+ }
+
+ return {
+ init,
+ viewer,
+ loadModelFromUrl,
+ addExtensions
+ }
+}