@thatopen/services 0.6.0 → 0.8.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 (73) hide show
  1. package/README.md +62 -62
  2. package/dist/built-in/index.d.ts +485 -100
  3. package/dist/cli/lib/revit-addin.d.ts +8 -0
  4. package/dist/cli.js +135 -24
  5. package/dist/index.cjs.js +2 -2
  6. package/dist/index.es.js +41 -50
  7. package/dist/vite-env.d.ts +1 -1
  8. package/docs/ai-quickstart.md +138 -4
  9. package/docs/cli/create.md +29 -29
  10. package/docs/cli/local-server.md +38 -38
  11. package/docs/cli/login.md +32 -32
  12. package/docs/cli/publish.md +31 -31
  13. package/docs/cli/run.md +26 -26
  14. package/docs/cli/serve.md +43 -43
  15. package/docs/cli/swap.md +30 -30
  16. package/docs/flow-architecture.md +1 -1
  17. package/docs/flow-plugin-guide.md +27 -1
  18. package/docs/platform-token.md +81 -0
  19. package/docs/revit-collab-quickstart.md +115 -35
  20. package/docs/rhino-interop-quickstart.md +458 -0
  21. package/package.json +1 -1
  22. package/resources/AGENTS.md +1 -1
  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/UIManager/src/app/example.ts +97 -97
  33. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
  34. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
  35. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
  36. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
  37. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
  38. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
  39. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
  40. package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
  41. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
  42. package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
  43. package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -70
  44. package/src/built-in/UIManager/src/models-list/example.ts +122 -122
  45. package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
  46. package/src/built-in/index.ts +485 -100
  47. package/src/cli/templates/app/index.html +35 -35
  48. package/src/cli/templates/app/package.json +32 -32
  49. package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
  50. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  51. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  52. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  53. package/src/cli/templates/app/src/globals.ts +1 -1
  54. package/src/cli/templates/app/src/main.history.ts +191 -0
  55. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
  56. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
  57. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
  58. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
  59. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
  60. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
  61. package/src/cli/templates/app/src/ui-components/index.ts +2 -2
  62. package/src/cli/templates/app/tsconfig.json +16 -16
  63. package/src/cli/templates/app/vite.config.js +57 -57
  64. package/src/cli/templates/cloud-component/README.md +205 -205
  65. package/src/cli/templates/cloud-component/_thatopen +5 -5
  66. package/src/cli/templates/cloud-component/declarations.json +4 -4
  67. package/src/cli/templates/cloud-component/package.json +21 -21
  68. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  69. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  70. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  71. package/src/cli/templates/shared/AGENTS.md +8 -8
  72. package/src/cli/templates/shared/CLAUDE.md +1 -1
  73. package/src/cli/templates/shared/_gitignore +5 -5
@@ -1,123 +1,123 @@
1
- /* MD
2
- ## Consuming platform contexts in child components
3
- ---
4
- Developers building custom BIM panels need access to the 3D engine, the platform
5
- API, and the current project's metadata — but threading these objects down through
6
- props across multiple component layers forces every intermediate element to know
7
- about data it doesn't use, making trees brittle and hard to refactor.
8
-
9
- top-app publishes the engine instance, the platform client, and the current
10
- project data as named reactive contexts, so any descendant component can subscribe
11
- to exactly what it needs without any prop drilling from the root.
12
-
13
- This tutorial covers defining a custom panel that subscribes to the engine
14
- context, the platform client context, and the project data context; handling the
15
- intermediate loading state before contexts become available after boot; and
16
- mounting the panel inside a top-app layout.
17
-
18
- By the end, you'll have a custom info panel that reads the project name and engine
19
- state directly from context, with no objects passed down as props from the app root.
20
- */
21
-
22
- import { html, LitElement } from "lit";
23
- import { customElement, state } from "lit/decorators.js";
24
- import { consume } from "@lit/context";
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, ProjectData, UIManager } from "@thatopen/services";
31
- import {
32
- componentsContext,
33
- clientContext,
34
- projectDataContext,
35
- appStateContext,
36
- type App,
37
- type AppState,
38
- } from "../../index";
39
-
40
- // appStateContext is provided by top-app immediately — before setup completes.
41
- // componentsContext, clientContext, and projectDataContext become available only
42
- // after app.setup returns and all waitUntil promises resolve.
43
- @customElement("project-info-panel")
44
- class ProjectInfoPanel extends LitElement {
45
- @consume({ context: appStateContext, subscribe: true })
46
- @state()
47
- private _appState?: AppState;
48
-
49
- @consume({ context: componentsContext, subscribe: true })
50
- @state()
51
- private _components?: OBC.Components;
52
-
53
- @consume({ context: clientContext, subscribe: true })
54
- @state()
55
- private _client?: PlatformClient;
56
-
57
- @consume({ context: projectDataContext, subscribe: true })
58
- @state()
59
- private _projectData?: ProjectData;
60
-
61
- render() {
62
- if (!this._components || !this._client || !this._projectData) {
63
- return html`<bim-label>Loading…</bim-label>`;
64
- }
65
- return html`
66
- <bim-panel label="Project Info">
67
- <bim-panel-section label="Project" icon="solar:buildings-bold">
68
- <bim-label .value=${this._projectData.name}></bim-label>
69
- </bim-panel-section>
70
- <bim-panel-section label="Engine" icon="solar:cpu-bold">
71
- <bim-label>Components ready</bim-label>
72
- </bim-panel-section>
73
- </bim-panel>
74
- `;
75
- }
76
- }
77
-
78
- // ---- app wiring ----
79
-
80
- const client = PlatformClient.fromPlatformContext();
81
- const { components } = (await client.setup(
82
- { OBC, OBF, BUI, THREE, FRAGS },
83
- { uuid: UIManager.uuid },
84
- )) as { components: OBC.Components };
85
- components.get(UIManager).init();
86
-
87
- const app = document.createElement("top-app") as unknown as App;
88
-
89
- app.setup = (waitUntil) => {
90
- waitUntil(
91
- (async () => {
92
- const fragments = components.get(OBC.FragmentsManager);
93
- const workerUrl = await FRAGS.FragmentsModels.getWorker();
94
- fragments.init(await FRAGS.toClassicWorker(workerUrl), {
95
- classicWorker: true,
96
- });
97
- })(),
98
- "Fragments Core",
99
- );
100
- return { components, client };
101
- };
102
-
103
- app.elements = {
104
- viewer: () =>
105
- html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
106
-
107
- // Any element rendered inside top-app can consume the contexts above.
108
- info: () => html`<project-info-panel></project-info-panel>`,
109
- };
110
-
111
- app.base = "viewer";
112
- app.layouts = {
113
- main: {
114
- label: "Main",
115
- icon: "solar:home-bold",
116
- template: `"info" 1fr / 22rem`,
117
- },
118
- };
119
- app.layout = "main";
120
-
121
- const container = document.getElementById("that-open-app") ?? document.body;
122
- container.appendChild(app);
123
- document.body.style.margin = "0";
1
+ /* MD
2
+ ## Consuming platform contexts in child components
3
+ ---
4
+ Developers building custom BIM panels need access to the 3D engine, the platform
5
+ API, and the current project's metadata — but threading these objects down through
6
+ props across multiple component layers forces every intermediate element to know
7
+ about data it doesn't use, making trees brittle and hard to refactor.
8
+
9
+ top-app publishes the engine instance, the platform client, and the current
10
+ project data as named reactive contexts, so any descendant component can subscribe
11
+ to exactly what it needs without any prop drilling from the root.
12
+
13
+ This tutorial covers defining a custom panel that subscribes to the engine
14
+ context, the platform client context, and the project data context; handling the
15
+ intermediate loading state before contexts become available after boot; and
16
+ mounting the panel inside a top-app layout.
17
+
18
+ By the end, you'll have a custom info panel that reads the project name and engine
19
+ state directly from context, with no objects passed down as props from the app root.
20
+ */
21
+
22
+ import { html, LitElement } from "lit";
23
+ import { customElement, state } from "lit/decorators.js";
24
+ import { consume } from "@lit/context";
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, ProjectData, UIManager } from "@thatopen/services";
31
+ import {
32
+ componentsContext,
33
+ clientContext,
34
+ projectDataContext,
35
+ appStateContext,
36
+ type App,
37
+ type AppState,
38
+ } from "../../index";
39
+
40
+ // appStateContext is provided by top-app immediately — before setup completes.
41
+ // componentsContext, clientContext, and projectDataContext become available only
42
+ // after app.setup returns and all waitUntil promises resolve.
43
+ @customElement("project-info-panel")
44
+ class ProjectInfoPanel extends LitElement {
45
+ @consume({ context: appStateContext, subscribe: true })
46
+ @state()
47
+ private _appState?: AppState;
48
+
49
+ @consume({ context: componentsContext, subscribe: true })
50
+ @state()
51
+ private _components?: OBC.Components;
52
+
53
+ @consume({ context: clientContext, subscribe: true })
54
+ @state()
55
+ private _client?: PlatformClient;
56
+
57
+ @consume({ context: projectDataContext, subscribe: true })
58
+ @state()
59
+ private _projectData?: ProjectData;
60
+
61
+ render() {
62
+ if (!this._components || !this._client || !this._projectData) {
63
+ return html`<bim-label>Loading…</bim-label>`;
64
+ }
65
+ return html`
66
+ <bim-panel label="Project Info">
67
+ <bim-panel-section label="Project" icon="solar:buildings-bold">
68
+ <bim-label .value=${this._projectData.name}></bim-label>
69
+ </bim-panel-section>
70
+ <bim-panel-section label="Engine" icon="solar:cpu-bold">
71
+ <bim-label>Components ready</bim-label>
72
+ </bim-panel-section>
73
+ </bim-panel>
74
+ `;
75
+ }
76
+ }
77
+
78
+ // ---- app wiring ----
79
+
80
+ const client = PlatformClient.fromPlatformContext();
81
+ const { components } = (await client.setup(
82
+ { OBC, OBF, BUI, THREE, FRAGS },
83
+ { uuid: UIManager.uuid },
84
+ )) as { components: OBC.Components };
85
+ components.get(UIManager).init();
86
+
87
+ const app = document.createElement("top-app") as unknown as App;
88
+
89
+ app.setup = (waitUntil) => {
90
+ waitUntil(
91
+ (async () => {
92
+ const fragments = components.get(OBC.FragmentsManager);
93
+ const workerUrl = await FRAGS.FragmentsModels.getWorker();
94
+ fragments.init(await FRAGS.toClassicWorker(workerUrl), {
95
+ classicWorker: true,
96
+ });
97
+ })(),
98
+ "Fragments Core",
99
+ );
100
+ return { components, client };
101
+ };
102
+
103
+ app.elements = {
104
+ viewer: () =>
105
+ html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
106
+
107
+ // Any element rendered inside top-app can consume the contexts above.
108
+ info: () => html`<project-info-panel></project-info-panel>`,
109
+ };
110
+
111
+ app.base = "viewer";
112
+ app.layouts = {
113
+ main: {
114
+ label: "Main",
115
+ icon: "solar:home-bold",
116
+ template: `"info" 1fr / 22rem`,
117
+ },
118
+ };
119
+ app.layout = "main";
120
+
121
+ const container = document.getElementById("that-open-app") ?? document.body;
122
+ container.appendChild(app);
123
+ document.body.style.margin = "0";
@@ -1,124 +1,124 @@
1
- /* MD
2
- ## File drag-and-drop with top:files-dropped
3
- ---
4
- Desktop BIM workflows often rely on dragging files straight from the OS file
5
- manager onto the browser window — but wiring a document-level dragenter,
6
- dragover, dragleave, and drop handler that correctly tracks nested enter/leave
7
- counts, shows a visual overlay, and forwards the files to the right component
8
- is tedious boilerplate to repeat for every app.
9
-
10
- top-app handles all of that automatically. It listens for file drag events on
11
- the document, shows a configurable overlay while files are in flight, and fires
12
- a single top:files-dropped event when the user releases the files. The drop
13
- message can be a static string or a function evaluated at render time, so it
14
- can reflect live application state.
15
-
16
- This tutorial covers receiving dropped files via top:files-dropped; filtering
17
- them by extension and surfacing a warning when none are supported; setting
18
- dropMessage as a function that reads a live model counter so the hint text
19
- updates as models are loaded; and showing a toast on success using the
20
- top:notification event from the same handler.
21
-
22
- By the end, you'll have an app with a drag-and-drop zone whose overlay message
23
- adapts to how many models are already loaded, and that reports unsupported
24
- files with a warning toast.
25
- */
26
-
27
- import { html } from "lit";
28
- import * as THREE from "three";
29
- import * as OBC from "@thatopen/components";
30
- import * as OBF from "@thatopen/components-front";
31
- import * as FRAGS from "@thatopen/fragments";
32
- import * as BUI from "@thatopen/ui";
33
- import { PlatformClient, UIManager } from "@thatopen/services";
34
- import type { App, AppReadyEvent } from "../../index";
35
-
36
- const client = PlatformClient.fromPlatformContext();
37
- const { components } = (await client.setup(
38
- { OBC, OBF, BUI, THREE, FRAGS },
39
- { uuid: UIManager.uuid },
40
- )) as { components: OBC.Components };
41
- components.get(UIManager).init();
42
-
43
- const app = document.createElement("top-app") as unknown as App;
44
-
45
- app.setup = (waitUntil) => {
46
- waitUntil(
47
- (async () => {
48
- const fragments = components.get(OBC.FragmentsManager);
49
- const workerUrl = await FRAGS.FragmentsModels.getWorker();
50
- fragments.init(await FRAGS.toClassicWorker(workerUrl), {
51
- classicWorker: true,
52
- });
53
- })(),
54
- "Fragments Core",
55
- );
56
- return { components, client };
57
- };
58
-
59
- app.elements = {
60
- viewer: () =>
61
- html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
62
- };
63
-
64
- app.base = "viewer";
65
- app.layouts = {
66
- main: {
67
- label: "Main",
68
- icon: "solar:home-bold",
69
- template: `"_" 1fr / 1fr`,
70
- },
71
- };
72
- app.layout = "main";
73
-
74
- // dropMessage as a function is evaluated at render time so it can read live state.
75
- // Here it reads how many models are already loaded and adjusts the hint text.
76
- let loadedCount = 0;
77
- app.dropMessage = () =>
78
- loadedCount === 0
79
- ? "Drop an IFC or FRAG file to load your first model"
80
- : `Drop a file to add to the ${loadedCount} loaded model${loadedCount === 1 ? "" : "s"}`;
81
-
82
- // top:files-dropped fires when the user drops files onto the window.
83
- // e.detail.files is a File[]. Handle upload, load, or validation here.
84
- app.addEventListener("top:files-dropped", (e) => {
85
- const { files } = (e as CustomEvent<{ files: File[] }>).detail;
86
-
87
- const supported = files.filter(
88
- (f) => f.name.endsWith(".ifc") || f.name.endsWith(".frag"),
89
- );
90
-
91
- // Dispatch top:notification to surface feedback without a reference to top-app.
92
- if (!supported.length) {
93
- app.dispatchEvent(
94
- new CustomEvent("top:notification", {
95
- bubbles: true,
96
- composed: true,
97
- detail: { message: "No supported files dropped", type: "warning" },
98
- }),
99
- );
100
- return;
101
- }
102
-
103
- loadedCount += supported.length;
104
- app.dispatchEvent(
105
- new CustomEvent("top:notification", {
106
- bubbles: true,
107
- composed: true,
108
- detail: {
109
- message: `${supported.length} file${supported.length === 1 ? "" : "s"} received`,
110
- type: "success",
111
- description: supported.map((f) => f.name).join(", "),
112
- },
113
- }),
114
- );
115
- });
116
-
117
- app.addEventListener("top:app-ready", (e) => {
118
- const { app: readyApp } = (e as AppReadyEvent).detail;
119
- readyApp.showToast("App ready — try dropping an IFC or FRAG file", "info");
120
- });
121
-
122
- const container = document.getElementById("that-open-app") ?? document.body;
123
- container.appendChild(app);
124
- document.body.style.margin = "0";
1
+ /* MD
2
+ ## File drag-and-drop with top:files-dropped
3
+ ---
4
+ Desktop BIM workflows often rely on dragging files straight from the OS file
5
+ manager onto the browser window — but wiring a document-level dragenter,
6
+ dragover, dragleave, and drop handler that correctly tracks nested enter/leave
7
+ counts, shows a visual overlay, and forwards the files to the right component
8
+ is tedious boilerplate to repeat for every app.
9
+
10
+ top-app handles all of that automatically. It listens for file drag events on
11
+ the document, shows a configurable overlay while files are in flight, and fires
12
+ a single top:files-dropped event when the user releases the files. The drop
13
+ message can be a static string or a function evaluated at render time, so it
14
+ can reflect live application state.
15
+
16
+ This tutorial covers receiving dropped files via top:files-dropped; filtering
17
+ them by extension and surfacing a warning when none are supported; setting
18
+ dropMessage as a function that reads a live model counter so the hint text
19
+ updates as models are loaded; and showing a toast on success using the
20
+ top:notification event from the same handler.
21
+
22
+ By the end, you'll have an app with a drag-and-drop zone whose overlay message
23
+ adapts to how many models are already loaded, and that reports unsupported
24
+ files with a warning toast.
25
+ */
26
+
27
+ import { html } from "lit";
28
+ import * as THREE from "three";
29
+ import * as OBC from "@thatopen/components";
30
+ import * as OBF from "@thatopen/components-front";
31
+ import * as FRAGS from "@thatopen/fragments";
32
+ import * as BUI from "@thatopen/ui";
33
+ import { PlatformClient, UIManager } from "@thatopen/services";
34
+ import type { App, AppReadyEvent } from "../../index";
35
+
36
+ const client = PlatformClient.fromPlatformContext();
37
+ const { components } = (await client.setup(
38
+ { OBC, OBF, BUI, THREE, FRAGS },
39
+ { uuid: UIManager.uuid },
40
+ )) as { components: OBC.Components };
41
+ components.get(UIManager).init();
42
+
43
+ const app = document.createElement("top-app") as unknown as App;
44
+
45
+ app.setup = (waitUntil) => {
46
+ waitUntil(
47
+ (async () => {
48
+ const fragments = components.get(OBC.FragmentsManager);
49
+ const workerUrl = await FRAGS.FragmentsModels.getWorker();
50
+ fragments.init(await FRAGS.toClassicWorker(workerUrl), {
51
+ classicWorker: true,
52
+ });
53
+ })(),
54
+ "Fragments Core",
55
+ );
56
+ return { components, client };
57
+ };
58
+
59
+ app.elements = {
60
+ viewer: () =>
61
+ html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
62
+ };
63
+
64
+ app.base = "viewer";
65
+ app.layouts = {
66
+ main: {
67
+ label: "Main",
68
+ icon: "solar:home-bold",
69
+ template: `"_" 1fr / 1fr`,
70
+ },
71
+ };
72
+ app.layout = "main";
73
+
74
+ // dropMessage as a function is evaluated at render time so it can read live state.
75
+ // Here it reads how many models are already loaded and adjusts the hint text.
76
+ let loadedCount = 0;
77
+ app.dropMessage = () =>
78
+ loadedCount === 0
79
+ ? "Drop an IFC or FRAG file to load your first model"
80
+ : `Drop a file to add to the ${loadedCount} loaded model${loadedCount === 1 ? "" : "s"}`;
81
+
82
+ // top:files-dropped fires when the user drops files onto the window.
83
+ // e.detail.files is a File[]. Handle upload, load, or validation here.
84
+ app.addEventListener("top:files-dropped", (e) => {
85
+ const { files } = (e as CustomEvent<{ files: File[] }>).detail;
86
+
87
+ const supported = files.filter(
88
+ (f) => f.name.endsWith(".ifc") || f.name.endsWith(".frag"),
89
+ );
90
+
91
+ // Dispatch top:notification to surface feedback without a reference to top-app.
92
+ if (!supported.length) {
93
+ app.dispatchEvent(
94
+ new CustomEvent("top:notification", {
95
+ bubbles: true,
96
+ composed: true,
97
+ detail: { message: "No supported files dropped", type: "warning" },
98
+ }),
99
+ );
100
+ return;
101
+ }
102
+
103
+ loadedCount += supported.length;
104
+ app.dispatchEvent(
105
+ new CustomEvent("top:notification", {
106
+ bubbles: true,
107
+ composed: true,
108
+ detail: {
109
+ message: `${supported.length} file${supported.length === 1 ? "" : "s"} received`,
110
+ type: "success",
111
+ description: supported.map((f) => f.name).join(", "),
112
+ },
113
+ }),
114
+ );
115
+ });
116
+
117
+ app.addEventListener("top:app-ready", (e) => {
118
+ const { app: readyApp } = (e as AppReadyEvent).detail;
119
+ readyApp.showToast("App ready — try dropping an IFC or FRAG file", "info");
120
+ });
121
+
122
+ const container = document.getElementById("that-open-app") ?? document.body;
123
+ container.appendChild(app);
124
+ document.body.style.margin = "0";