@thatopen/services 0.3.16 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +62 -62
  2. package/dist/cli/commands/rhino.d.ts +11 -0
  3. package/dist/cli/lib/plugin-install.d.ts +24 -0
  4. package/dist/cli.js +124 -1
  5. package/dist/core/client.d.ts +1 -0
  6. package/dist/core/platform-client.d.ts +7 -0
  7. package/dist/index.cjs.js +2 -2
  8. package/dist/index.es.js +382 -370
  9. package/dist/vite-env.d.ts +1 -1
  10. package/docs/ai-quickstart.md +130 -130
  11. package/docs/cli/create.md +29 -29
  12. package/docs/cli/local-server.md +38 -38
  13. package/docs/cli/login.md +32 -32
  14. package/docs/cli/publish.md +31 -31
  15. package/docs/cli/run.md +26 -26
  16. package/docs/cli/serve.md +43 -43
  17. package/docs/cli/swap.md +30 -30
  18. package/docs/cloud/GaussianSplatConverter.md +43 -43
  19. package/docs/cloud/PointCloudConverter.md +53 -53
  20. package/docs/revit-collab-quickstart.md +256 -256
  21. package/package.json +95 -95
  22. package/resources/AGENTS.md +40 -40
  23. package/src/built-in/CDEManager/example.ts +214 -214
  24. package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
  25. package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
  26. package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
  27. package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
  28. package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
  29. package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
  30. package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
  31. package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
  32. package/src/built-in/ClashesManager/example.ts +283 -283
  33. package/src/built-in/UIManager/src/app/example.ts +97 -97
  34. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
  35. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
  36. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
  37. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
  38. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
  39. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
  40. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
  41. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
  42. package/src/cli/templates/app/README.md +217 -217
  43. package/src/cli/templates/app/index.html +35 -35
  44. package/src/cli/templates/app/package.json +32 -32
  45. package/src/cli/templates/app/src/app.ts +37 -37
  46. package/src/cli/templates/app/src/assets/tool-placeholder.ts +50 -50
  47. package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
  48. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  49. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  50. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  51. package/src/cli/templates/app/src/globals.ts +1 -1
  52. package/src/cli/templates/app/src/main.ts +199 -199
  53. package/src/cli/templates/app/src/setups/reality-capture/lib/hidden-tiles-plugin.ts +88 -88
  54. package/src/cli/templates/app/src/setups/reality-capture/lib/point-tile-plugin.ts +166 -166
  55. package/src/cli/templates/app/src/setups/reality-capture/lib/render-frame.ts +31 -31
  56. package/src/cli/templates/app/src/setups/reality-capture/lib/splat-tile-plugin.ts +51 -51
  57. package/src/cli/templates/app/src/setups/reality-capture-viewer.ts +1383 -1383
  58. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
  59. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
  60. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
  61. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
  62. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
  63. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
  64. package/src/cli/templates/app/src/ui-components/index.ts +2 -2
  65. package/src/cli/templates/app/tsconfig.json +16 -16
  66. package/src/cli/templates/app/vite.config.js +57 -57
  67. package/src/cli/templates/cloud-component/README.md +205 -205
  68. package/src/cli/templates/cloud-component/_thatopen +5 -5
  69. package/src/cli/templates/cloud-component/declarations.json +4 -4
  70. package/src/cli/templates/cloud-component/package.json +21 -21
  71. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  72. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  73. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  74. package/src/cli/templates/shared/AGENTS.md +8 -8
  75. package/src/cli/templates/shared/CLAUDE.md +1 -1
  76. package/src/cli/templates/shared/_gitignore +5 -5
@@ -1,163 +1,163 @@
1
- /* MD
2
- ## Confirmation modals with top:modal
3
- ---
4
- Destructive or irreversible actions — deleting records, overwriting files,
5
- leaving unsaved changes — should ask the user to confirm before proceeding.
6
- Opening a custom dialog element from a deeply nested component means threading
7
- a reference to a controller up through every layer of the tree.
8
-
9
- top-app provides a modal slot that any descendant can trigger by dispatching
10
- a single DOM event. The modal blocks the UI, shows a configurable message and
11
- action labels, and calls back into the originating component when the user
12
- confirms or cancels — no reference to the root element required.
13
-
14
- This tutorial covers showing a modal from a child component by dispatching
15
- top:modal; setting the label, icon, and button labels; running an async
16
- confirm handler while the modal shows a loading spinner; and handling the
17
- cancel path without side effects.
18
-
19
- By the end, you'll have an app where a panel can trigger confirmation dialogs
20
- for actions of varying severity, each with its own copy and async behaviour.
21
- */
22
-
23
- import { html, LitElement } from "lit";
24
- import { customElement, state } from "lit/decorators.js";
25
- import * as THREE from "three";
26
- import * as OBC from "@thatopen/components";
27
- import * as OBF from "@thatopen/components-front";
28
- import * as FRAGS from "@thatopen/fragments";
29
- import * as BUI from "@thatopen/ui";
30
- import { PlatformClient, UIManager } from "@thatopen/services";
31
- import type { App } from "../../index";
32
-
33
- // A child component that triggers modals without holding a reference to top-app.
34
- // It dispatches top:modal and receives the result via onConfirm / onCancel callbacks.
35
- @customElement("modal-actions")
36
- class ModalActions extends LitElement {
37
- @state() private _lastAction = "—";
38
-
39
- private _confirmDelete() {
40
- // top:modal opens a blocking dialog on the nearest top-app ancestor.
41
- // The modal stays open (with a loading spinner) while onConfirm is awaited.
42
- this.dispatchEvent(
43
- new CustomEvent("top:modal", {
44
- bubbles: true,
45
- composed: true,
46
- detail: {
47
- label: "Delete model",
48
- icon: "tabler:trash",
49
- confirmLabel: "Delete",
50
- cancelLabel: "Keep",
51
- content: html`
52
- <bim-label style="white-space:normal;">
53
- This will permanently remove the model and all its versions.
54
- This action cannot be undone.
55
- </bim-label>
56
- `,
57
- onConfirm: async () => {
58
- // Simulate an async delete operation.
59
- // The modal shows a loading spinner until this promise resolves.
60
- await new Promise<void>((resolve) => setTimeout(resolve, 1500));
61
- this._lastAction = "Model deleted";
62
- },
63
- onCancel: () => {
64
- this._lastAction = "Delete cancelled";
65
- },
66
- },
67
- }),
68
- );
69
- }
70
-
71
- private _confirmOverwrite() {
72
- this.dispatchEvent(
73
- new CustomEvent("top:modal", {
74
- bubbles: true,
75
- composed: true,
76
- detail: {
77
- label: "File already exists",
78
- icon: "tabler:alert-triangle",
79
- confirmLabel: "Overwrite",
80
- cancelLabel: "Cancel",
81
- content: html`
82
- <bim-label style="white-space:normal;">
83
- A file named <strong>structure.ifc</strong> already exists in this
84
- folder. Uploading will create a new version alongside it.
85
- </bim-label>
86
- `,
87
- // onConfirm can be synchronous too — the modal closes immediately.
88
- onConfirm: () => {
89
- this._lastAction = "Overwrite confirmed";
90
- },
91
- onCancel: () => {
92
- this._lastAction = "Overwrite cancelled";
93
- },
94
- },
95
- }),
96
- );
97
- }
98
-
99
- render() {
100
- return html`
101
- <bim-panel label="Modal Actions">
102
- <bim-panel-section label="Actions" icon="tabler:click">
103
- <bim-button
104
- label="Delete model (async confirm)"
105
- icon="tabler:trash"
106
- @click=${this._confirmDelete}
107
- ></bim-button>
108
- <bim-button
109
- label="Overwrite file (sync confirm)"
110
- icon="tabler:file-upload"
111
- @click=${this._confirmOverwrite}
112
- ></bim-button>
113
- <bim-label>Last action: ${this._lastAction}</bim-label>
114
- </bim-panel-section>
115
- </bim-panel>
116
- `;
117
- }
118
- }
119
-
120
- // ---- app wiring ----
121
-
122
- const client = PlatformClient.fromPlatformContext();
123
- const { components } = (await client.setup(
124
- { OBC, OBF, BUI, THREE, FRAGS },
125
- { uuid: UIManager.uuid },
126
- )) as { components: OBC.Components };
127
- components.get(UIManager).init();
128
-
129
- const app = document.createElement("top-app") as unknown as App;
130
-
131
- app.setup = (waitUntil) => {
132
- waitUntil(
133
- (async () => {
134
- const fragments = components.get(OBC.FragmentsManager);
135
- const workerUrl = await FRAGS.FragmentsModels.getWorker();
136
- fragments.init(await FRAGS.toClassicWorker(workerUrl), {
137
- classicWorker: true,
138
- });
139
- })(),
140
- "Fragments Core",
141
- );
142
- return { components, client };
143
- };
144
-
145
- app.elements = {
146
- viewer: () =>
147
- html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
148
- panel: () => html`<modal-actions></modal-actions>`,
149
- };
150
-
151
- app.base = "viewer";
152
- app.layouts = {
153
- main: {
154
- label: "Main",
155
- icon: "solar:home-bold",
156
- template: `"panel" 1fr / 22rem`,
157
- },
158
- };
159
- app.layout = "main";
160
-
161
- const container = document.getElementById("that-open-app") ?? document.body;
162
- container.appendChild(app);
163
- document.body.style.margin = "0";
1
+ /* MD
2
+ ## Confirmation modals with top:modal
3
+ ---
4
+ Destructive or irreversible actions — deleting records, overwriting files,
5
+ leaving unsaved changes — should ask the user to confirm before proceeding.
6
+ Opening a custom dialog element from a deeply nested component means threading
7
+ a reference to a controller up through every layer of the tree.
8
+
9
+ top-app provides a modal slot that any descendant can trigger by dispatching
10
+ a single DOM event. The modal blocks the UI, shows a configurable message and
11
+ action labels, and calls back into the originating component when the user
12
+ confirms or cancels — no reference to the root element required.
13
+
14
+ This tutorial covers showing a modal from a child component by dispatching
15
+ top:modal; setting the label, icon, and button labels; running an async
16
+ confirm handler while the modal shows a loading spinner; and handling the
17
+ cancel path without side effects.
18
+
19
+ By the end, you'll have an app where a panel can trigger confirmation dialogs
20
+ for actions of varying severity, each with its own copy and async behaviour.
21
+ */
22
+
23
+ import { html, LitElement } from "lit";
24
+ import { customElement, state } from "lit/decorators.js";
25
+ import * as THREE from "three";
26
+ import * as OBC from "@thatopen/components";
27
+ import * as OBF from "@thatopen/components-front";
28
+ import * as FRAGS from "@thatopen/fragments";
29
+ import * as BUI from "@thatopen/ui";
30
+ import { PlatformClient, UIManager } from "@thatopen/services";
31
+ import type { App } from "../../index";
32
+
33
+ // A child component that triggers modals without holding a reference to top-app.
34
+ // It dispatches top:modal and receives the result via onConfirm / onCancel callbacks.
35
+ @customElement("modal-actions")
36
+ class ModalActions extends LitElement {
37
+ @state() private _lastAction = "—";
38
+
39
+ private _confirmDelete() {
40
+ // top:modal opens a blocking dialog on the nearest top-app ancestor.
41
+ // The modal stays open (with a loading spinner) while onConfirm is awaited.
42
+ this.dispatchEvent(
43
+ new CustomEvent("top:modal", {
44
+ bubbles: true,
45
+ composed: true,
46
+ detail: {
47
+ label: "Delete model",
48
+ icon: "tabler:trash",
49
+ confirmLabel: "Delete",
50
+ cancelLabel: "Keep",
51
+ content: html`
52
+ <bim-label style="white-space:normal;">
53
+ This will permanently remove the model and all its versions.
54
+ This action cannot be undone.
55
+ </bim-label>
56
+ `,
57
+ onConfirm: async () => {
58
+ // Simulate an async delete operation.
59
+ // The modal shows a loading spinner until this promise resolves.
60
+ await new Promise<void>((resolve) => setTimeout(resolve, 1500));
61
+ this._lastAction = "Model deleted";
62
+ },
63
+ onCancel: () => {
64
+ this._lastAction = "Delete cancelled";
65
+ },
66
+ },
67
+ }),
68
+ );
69
+ }
70
+
71
+ private _confirmOverwrite() {
72
+ this.dispatchEvent(
73
+ new CustomEvent("top:modal", {
74
+ bubbles: true,
75
+ composed: true,
76
+ detail: {
77
+ label: "File already exists",
78
+ icon: "tabler:alert-triangle",
79
+ confirmLabel: "Overwrite",
80
+ cancelLabel: "Cancel",
81
+ content: html`
82
+ <bim-label style="white-space:normal;">
83
+ A file named <strong>structure.ifc</strong> already exists in this
84
+ folder. Uploading will create a new version alongside it.
85
+ </bim-label>
86
+ `,
87
+ // onConfirm can be synchronous too — the modal closes immediately.
88
+ onConfirm: () => {
89
+ this._lastAction = "Overwrite confirmed";
90
+ },
91
+ onCancel: () => {
92
+ this._lastAction = "Overwrite cancelled";
93
+ },
94
+ },
95
+ }),
96
+ );
97
+ }
98
+
99
+ render() {
100
+ return html`
101
+ <bim-panel label="Modal Actions">
102
+ <bim-panel-section label="Actions" icon="tabler:click">
103
+ <bim-button
104
+ label="Delete model (async confirm)"
105
+ icon="tabler:trash"
106
+ @click=${this._confirmDelete}
107
+ ></bim-button>
108
+ <bim-button
109
+ label="Overwrite file (sync confirm)"
110
+ icon="tabler:file-upload"
111
+ @click=${this._confirmOverwrite}
112
+ ></bim-button>
113
+ <bim-label>Last action: ${this._lastAction}</bim-label>
114
+ </bim-panel-section>
115
+ </bim-panel>
116
+ `;
117
+ }
118
+ }
119
+
120
+ // ---- app wiring ----
121
+
122
+ const client = PlatformClient.fromPlatformContext();
123
+ const { components } = (await client.setup(
124
+ { OBC, OBF, BUI, THREE, FRAGS },
125
+ { uuid: UIManager.uuid },
126
+ )) as { components: OBC.Components };
127
+ components.get(UIManager).init();
128
+
129
+ const app = document.createElement("top-app") as unknown as App;
130
+
131
+ app.setup = (waitUntil) => {
132
+ waitUntil(
133
+ (async () => {
134
+ const fragments = components.get(OBC.FragmentsManager);
135
+ const workerUrl = await FRAGS.FragmentsModels.getWorker();
136
+ fragments.init(await FRAGS.toClassicWorker(workerUrl), {
137
+ classicWorker: true,
138
+ });
139
+ })(),
140
+ "Fragments Core",
141
+ );
142
+ return { components, client };
143
+ };
144
+
145
+ app.elements = {
146
+ viewer: () =>
147
+ html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
148
+ panel: () => html`<modal-actions></modal-actions>`,
149
+ };
150
+
151
+ app.base = "viewer";
152
+ app.layouts = {
153
+ main: {
154
+ label: "Main",
155
+ icon: "solar:home-bold",
156
+ template: `"panel" 1fr / 22rem`,
157
+ },
158
+ };
159
+ app.layout = "main";
160
+
161
+ const container = document.getElementById("that-open-app") ?? document.body;
162
+ container.appendChild(app);
163
+ document.body.style.margin = "0";
@@ -1,133 +1,133 @@
1
- /* MD
2
- ## Toasts and cross-component notifications
3
- ---
4
- Developers building BIM processing UIs need to surface status updates, errors,
5
- and async feedback to users from different points in the component tree —
6
- but reaching back to the app root to trigger a notification from a deeply
7
- nested component requires threading a reference through every layer in between.
8
-
9
- top-app provides a built-in toast system that any descendant can trigger by
10
- dispatching a standard DOM event. Toasts are auto-dismissed after a type-based
11
- duration and stack at the bottom-left of the screen.
12
-
13
- This tutorial covers showing a toast on app boot via showToast; building a
14
- component that dispatches top:notification to report async progress and
15
- completion without a reference to the root; and using the four toast types
16
- (success, error, warning, info) with their respective durations.
17
-
18
- By the end, you'll have an app with a boot confirmation toast and a panel that
19
- reports async work status without holding any reference to top-app.
20
- */
21
-
22
- import { html, LitElement } from "lit";
23
- import { customElement, state } from "lit/decorators.js";
24
- import * as THREE from "three";
25
- import * as OBC from "@thatopen/components";
26
- import * as OBF from "@thatopen/components-front";
27
- import * as FRAGS from "@thatopen/fragments";
28
- import * as BUI from "@thatopen/ui";
29
- import { PlatformClient, UIManager } from "@thatopen/services";
30
- import type { App, AppReadyEvent } from "../../index";
31
-
32
- // A child component that dispatches top:notification to surface messages
33
- // to the nearest top-app ancestor — no reference to the app element needed.
34
- @customElement("status-reporter")
35
- class StatusReporter extends LitElement {
36
- @state() private _busy = false;
37
-
38
- private _simulateWork() {
39
- this._busy = true;
40
-
41
- // Dispatching top:notification from any descendant of top-app shows a toast.
42
- // bubbles:true + composed:true lets it cross shadow-DOM boundaries.
43
- this.dispatchEvent(
44
- new CustomEvent("top:notification", {
45
- bubbles: true,
46
- composed: true,
47
- detail: { message: "Analysis started", type: "info" },
48
- }),
49
- );
50
-
51
- setTimeout(() => {
52
- this._busy = false;
53
- this.dispatchEvent(
54
- new CustomEvent("top:notification", {
55
- bubbles: true,
56
- composed: true,
57
- detail: {
58
- message: "Analysis complete",
59
- type: "success",
60
- description: "Found 342 elements across 5 categories.",
61
- },
62
- }),
63
- );
64
- }, 2000);
65
- }
66
-
67
- render() {
68
- return html`
69
- <bim-panel label="Reporter">
70
- <bim-panel-section label="Actions" icon="solar:play-bold">
71
- <bim-button
72
- label="Run Analysis"
73
- icon="solar:play-bold"
74
- .loading=${this._busy}
75
- @click=${this._simulateWork}
76
- ></bim-button>
77
- </bim-panel-section>
78
- </bim-panel>
79
- `;
80
- }
81
- }
82
-
83
- // ---- app wiring ----
84
-
85
- const client = PlatformClient.fromPlatformContext();
86
- const { components } = (await client.setup(
87
- { OBC, OBF, BUI, THREE, FRAGS },
88
- { uuid: UIManager.uuid },
89
- )) as { components: OBC.Components };
90
- components.get(UIManager).init();
91
-
92
- const app = document.createElement("top-app") as unknown as App;
93
-
94
- app.setup = (waitUntil) => {
95
- waitUntil(
96
- (async () => {
97
- const fragments = components.get(OBC.FragmentsManager);
98
- const workerUrl = await FRAGS.FragmentsModels.getWorker();
99
- fragments.init(await FRAGS.toClassicWorker(workerUrl), {
100
- classicWorker: true,
101
- });
102
- })(),
103
- "Fragments Core",
104
- );
105
- return { components, client };
106
- };
107
-
108
- app.elements = {
109
- viewer: () =>
110
- html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
111
- panel: () => html`<status-reporter></status-reporter>`,
112
- };
113
-
114
- app.base = "viewer";
115
- app.layouts = {
116
- main: {
117
- label: "Main",
118
- icon: "solar:home-bold",
119
- template: `"panel" 1fr / 22rem`,
120
- },
121
- };
122
- app.layout = "main";
123
-
124
- // top:app-ready fires after all waitUntil promises resolve.
125
- // e.detail.app is a reference to the top-app element itself.
126
- app.addEventListener("top:app-ready", (e) => {
127
- const { app: readyApp } = (e as AppReadyEvent).detail;
128
- readyApp.showToast("App ready", "success");
129
- });
130
-
131
- const container = document.getElementById("that-open-app") ?? document.body;
132
- container.appendChild(app);
133
- document.body.style.margin = "0";
1
+ /* MD
2
+ ## Toasts and cross-component notifications
3
+ ---
4
+ Developers building BIM processing UIs need to surface status updates, errors,
5
+ and async feedback to users from different points in the component tree —
6
+ but reaching back to the app root to trigger a notification from a deeply
7
+ nested component requires threading a reference through every layer in between.
8
+
9
+ top-app provides a built-in toast system that any descendant can trigger by
10
+ dispatching a standard DOM event. Toasts are auto-dismissed after a type-based
11
+ duration and stack at the bottom-left of the screen.
12
+
13
+ This tutorial covers showing a toast on app boot via showToast; building a
14
+ component that dispatches top:notification to report async progress and
15
+ completion without a reference to the root; and using the four toast types
16
+ (success, error, warning, info) with their respective durations.
17
+
18
+ By the end, you'll have an app with a boot confirmation toast and a panel that
19
+ reports async work status without holding any reference to top-app.
20
+ */
21
+
22
+ import { html, LitElement } from "lit";
23
+ import { customElement, state } from "lit/decorators.js";
24
+ import * as THREE from "three";
25
+ import * as OBC from "@thatopen/components";
26
+ import * as OBF from "@thatopen/components-front";
27
+ import * as FRAGS from "@thatopen/fragments";
28
+ import * as BUI from "@thatopen/ui";
29
+ import { PlatformClient, UIManager } from "@thatopen/services";
30
+ import type { App, AppReadyEvent } from "../../index";
31
+
32
+ // A child component that dispatches top:notification to surface messages
33
+ // to the nearest top-app ancestor — no reference to the app element needed.
34
+ @customElement("status-reporter")
35
+ class StatusReporter extends LitElement {
36
+ @state() private _busy = false;
37
+
38
+ private _simulateWork() {
39
+ this._busy = true;
40
+
41
+ // Dispatching top:notification from any descendant of top-app shows a toast.
42
+ // bubbles:true + composed:true lets it cross shadow-DOM boundaries.
43
+ this.dispatchEvent(
44
+ new CustomEvent("top:notification", {
45
+ bubbles: true,
46
+ composed: true,
47
+ detail: { message: "Analysis started", type: "info" },
48
+ }),
49
+ );
50
+
51
+ setTimeout(() => {
52
+ this._busy = false;
53
+ this.dispatchEvent(
54
+ new CustomEvent("top:notification", {
55
+ bubbles: true,
56
+ composed: true,
57
+ detail: {
58
+ message: "Analysis complete",
59
+ type: "success",
60
+ description: "Found 342 elements across 5 categories.",
61
+ },
62
+ }),
63
+ );
64
+ }, 2000);
65
+ }
66
+
67
+ render() {
68
+ return html`
69
+ <bim-panel label="Reporter">
70
+ <bim-panel-section label="Actions" icon="solar:play-bold">
71
+ <bim-button
72
+ label="Run Analysis"
73
+ icon="solar:play-bold"
74
+ .loading=${this._busy}
75
+ @click=${this._simulateWork}
76
+ ></bim-button>
77
+ </bim-panel-section>
78
+ </bim-panel>
79
+ `;
80
+ }
81
+ }
82
+
83
+ // ---- app wiring ----
84
+
85
+ const client = PlatformClient.fromPlatformContext();
86
+ const { components } = (await client.setup(
87
+ { OBC, OBF, BUI, THREE, FRAGS },
88
+ { uuid: UIManager.uuid },
89
+ )) as { components: OBC.Components };
90
+ components.get(UIManager).init();
91
+
92
+ const app = document.createElement("top-app") as unknown as App;
93
+
94
+ app.setup = (waitUntil) => {
95
+ waitUntil(
96
+ (async () => {
97
+ const fragments = components.get(OBC.FragmentsManager);
98
+ const workerUrl = await FRAGS.FragmentsModels.getWorker();
99
+ fragments.init(await FRAGS.toClassicWorker(workerUrl), {
100
+ classicWorker: true,
101
+ });
102
+ })(),
103
+ "Fragments Core",
104
+ );
105
+ return { components, client };
106
+ };
107
+
108
+ app.elements = {
109
+ viewer: () =>
110
+ html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
111
+ panel: () => html`<status-reporter></status-reporter>`,
112
+ };
113
+
114
+ app.base = "viewer";
115
+ app.layouts = {
116
+ main: {
117
+ label: "Main",
118
+ icon: "solar:home-bold",
119
+ template: `"panel" 1fr / 22rem`,
120
+ },
121
+ };
122
+ app.layout = "main";
123
+
124
+ // top:app-ready fires after all waitUntil promises resolve.
125
+ // e.detail.app is a reference to the top-app element itself.
126
+ app.addEventListener("top:app-ready", (e) => {
127
+ const { app: readyApp } = (e as AppReadyEvent).detail;
128
+ readyApp.showToast("App ready", "success");
129
+ });
130
+
131
+ const container = document.getElementById("that-open-app") ?? document.body;
132
+ container.appendChild(app);
133
+ document.body.style.margin = "0";