@thatopen/services 0.3.15 → 0.4.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 (68) hide show
  1. package/README.md +62 -62
  2. package/dist/built-in/index.d.ts +165 -2
  3. package/dist/cli.js +27 -13
  4. package/dist/core/client.d.ts +1 -0
  5. package/dist/core/platform-client.d.ts +7 -0
  6. package/dist/index.cjs.js +2 -2
  7. package/dist/index.es.js +396 -374
  8. package/dist/vite-env.d.ts +1 -1
  9. package/docs/builtin/paths.json +94 -86
  10. package/docs/cli/create.md +29 -29
  11. package/docs/cli/local-server.md +38 -38
  12. package/docs/cli/login.md +32 -32
  13. package/docs/cli/publish.md +31 -31
  14. package/docs/cli/run.md +26 -26
  15. package/docs/cli/serve.md +43 -43
  16. package/docs/cli/swap.md +30 -30
  17. package/package.json +4 -4
  18. package/resources/AGENTS.md +40 -40
  19. package/src/built-in/CDEManager/example.ts +214 -214
  20. package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
  21. package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
  22. package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
  23. package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
  24. package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
  25. package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
  26. package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
  27. package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
  28. package/src/built-in/ClashesManager/example.ts +283 -283
  29. package/src/built-in/UIManager/src/app/example.ts +97 -97
  30. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
  31. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
  32. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
  33. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
  34. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
  35. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
  36. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
  37. package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -0
  38. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
  39. package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -160
  40. package/src/built-in/UIManager/src/models-list/example.ts +122 -122
  41. package/src/built-in/UIManager/src/topics-panel/example.ts +170 -0
  42. package/src/built-in/index.ts +1048 -881
  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/bim-components/CloudRunner/index.ts +95 -95
  46. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  47. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  48. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  49. package/src/cli/templates/app/src/globals.ts +1 -1
  50. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
  51. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
  52. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
  53. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
  54. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
  55. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
  56. package/src/cli/templates/app/src/ui-components/index.ts +2 -2
  57. package/src/cli/templates/app/tsconfig.json +16 -16
  58. package/src/cli/templates/app/vite.config.js +57 -57
  59. package/src/cli/templates/cloud-component/README.md +205 -205
  60. package/src/cli/templates/cloud-component/_thatopen +5 -5
  61. package/src/cli/templates/cloud-component/declarations.json +4 -4
  62. package/src/cli/templates/cloud-component/package.json +21 -21
  63. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  64. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  65. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  66. package/src/cli/templates/shared/AGENTS.md +8 -8
  67. package/src/cli/templates/shared/CLAUDE.md +1 -1
  68. 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";