@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,35 +1,35 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>ThatOpen App</title>
7
- <style>
8
- *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
9
- html, body { width: 100%; height: 100%; overflow: hidden; font-family: system-ui, -apple-system, sans-serif; }
10
- #that-open-app { width: 100%; height: 100%; }
11
- /* Scrollbar rail = the panel surface (#262629). Every panel (app .xxx-vp
12
- tracks) and every bim-* shadow-DOM scrollbar reads var(--bim-scrollbar--bgc,
13
- …), so defining it once here makes all rails match the surface instead of
14
- falling back to the lighter bg-contrast-40 (#5F5F64).
15
- NOTE: must use --bim-ui_gray-1 (defined UNCONDITIONALLY at :root = #262629),
16
- not --bim-ui_bg-contrast-* (only defined under @media/theme-class scopes, so
17
- it doesn't resolve at <html>:root and left --bim-scrollbar--bgc unset). */
18
- :root { --bim-scrollbar--bgc: var(--bim-ui_gray-1, #262629); }
19
- </style>
20
- </head>
21
- <body>
22
- <div id="that-open-app"></div>
23
- <script>
24
- // Simulates the context that the platform provides in production.
25
- // When running inside the platform iframe, these values come from query params.
26
- window.__THATOPEN_CONTEXT__ = {
27
- appId: 'local-dev',
28
- projectId: 'local-project',
29
- accessToken: '',
30
- apiUrl: 'http://localhost:3000',
31
- };
32
- </script>
33
- <script type="module" src="/src/main.ts"></script>
34
- </body>
35
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>ThatOpen App</title>
7
+ <style>
8
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
9
+ html, body { width: 100%; height: 100%; overflow: hidden; font-family: system-ui, -apple-system, sans-serif; }
10
+ #that-open-app { width: 100%; height: 100%; }
11
+ /* Scrollbar rail = the panel surface (#262629). Every panel (app .xxx-vp
12
+ tracks) and every bim-* shadow-DOM scrollbar reads var(--bim-scrollbar--bgc,
13
+ …), so defining it once here makes all rails match the surface instead of
14
+ falling back to the lighter bg-contrast-40 (#5F5F64).
15
+ NOTE: must use --bim-ui_gray-1 (defined UNCONDITIONALLY at :root = #262629),
16
+ not --bim-ui_bg-contrast-* (only defined under @media/theme-class scopes, so
17
+ it doesn't resolve at <html>:root and left --bim-scrollbar--bgc unset). */
18
+ :root { --bim-scrollbar--bgc: var(--bim-ui_gray-1, #262629); }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <div id="that-open-app"></div>
23
+ <script>
24
+ // Simulates the context that the platform provides in production.
25
+ // When running inside the platform iframe, these values come from query params.
26
+ window.__THATOPEN_CONTEXT__ = {
27
+ appId: 'local-dev',
28
+ projectId: 'local-project',
29
+ accessToken: '',
30
+ apiUrl: 'http://localhost:3000',
31
+ };
32
+ </script>
33
+ <script type="module" src="/src/main.ts"></script>
34
+ </body>
35
+ </html>
@@ -1,33 +1,33 @@
1
- {
2
- "name": "{{PROJECT_NAME}}",
3
- "version": "1.0.0",
4
- "private": true,
5
- "scripts": {
6
- "dev": "thatopen serve",
7
- "build": "vite build",
8
- "login": "thatopen login --local",
9
- "publish": "thatopen publish"
10
- },
11
- "dependencies": {
12
- "@lit/context": "^1.1.6",
13
- "lit": "^3.0.0",
14
- "@markerjs/markerjs3": "^3.9.0",
15
- "@thatopen/components": "~3.4.0",
16
- "@thatopen/components-front": "~3.4.0",
17
- "@thatopen/fragments": "~3.4.0",
18
- "@thatopen/services": "file:../../../..",
19
- "@thatopen/ui": "~3.4.0",
20
- "three": "0.185.0",
21
- "@sparkjsdev/spark": "^0.1.10",
22
- "3d-tiles-renderer": "^0.4.28",
23
- "camera-controls": "^3.1.2",
24
- "opentype.js": "1.3.4",
25
- "potree-core": "^2.0.15",
26
- "web-ifc": "^0.0.77"
27
- },
28
- "devDependencies": {
29
- "@types/three": "^0.182.0",
30
- "typescript": "^5.2.0",
31
- "vite": "^5.2.0"
32
- }
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "thatopen serve",
7
+ "build": "vite build",
8
+ "login": "thatopen login --local",
9
+ "publish": "thatopen publish"
10
+ },
11
+ "dependencies": {
12
+ "@lit/context": "^1.1.6",
13
+ "lit": "^3.0.0",
14
+ "@markerjs/markerjs3": "^3.9.0",
15
+ "@thatopen/components": "~3.4.0",
16
+ "@thatopen/components-front": "~3.4.0",
17
+ "@thatopen/fragments": "~3.4.0",
18
+ "@thatopen/services": "file:../../../..",
19
+ "@thatopen/ui": "~3.4.0",
20
+ "three": "0.185.0",
21
+ "@sparkjsdev/spark": "^0.1.10",
22
+ "3d-tiles-renderer": "^0.4.28",
23
+ "camera-controls": "^3.1.2",
24
+ "opentype.js": "1.3.4",
25
+ "potree-core": "^2.0.15",
26
+ "web-ifc": "^0.0.77"
27
+ },
28
+ "devDependencies": {
29
+ "@types/three": "^0.182.0",
30
+ "typescript": "^5.2.0",
31
+ "vite": "^5.2.0"
32
+ }
33
33
  }
@@ -1,95 +1,95 @@
1
- import * as OBC from "@thatopen/components";
2
- import { getAppManager } from "../../app";
3
- import { CloudRunnerStatus } from "./src";
4
-
5
- export class CloudRunner extends OBC.Component {
6
- static readonly uuid = "7c4e5d3b-2a1f-4e8d-9b6c-0a3f7e2c5d1b" as const;
7
-
8
- enabled = true;
9
-
10
- readonly onExecutionUpdated = new OBC.Event<CloudRunnerStatus>();
11
-
12
- // TODO: Replace with your actual component ID after publishing.
13
- componentId = "your-component-id";
14
- localServerUrl = "http://localhost:4001";
15
-
16
- // Reactive state — read by the UI template on each render.
17
- status = "Idle";
18
- progress = 0;
19
- messages: string[] = [];
20
-
21
- constructor(components: OBC.Components) {
22
- super(components);
23
- components.add(CloudRunner.uuid, this);
24
- }
25
-
26
- async run(useLocal: boolean) {
27
- // Resolve client at call time via the app shim — never store it as a field.
28
- const client = getAppManager(this.components).client;
29
- if (!client) {
30
- this.status = "No platform client available.";
31
- this._trigger();
32
- return;
33
- }
34
-
35
- client.localServerUrl = useLocal ? this.localServerUrl : null;
36
-
37
- this.status = useLocal ? "Starting (local)..." : "Starting (deployed)...";
38
- this.progress = 0;
39
- this.messages = [];
40
- this._trigger();
41
-
42
- try {
43
- const { executionId } = await client.executeComponent(this.componentId, {
44
- greeting: "Hello from the BIM app!",
45
- });
46
-
47
- this.status = `Running (${executionId.slice(0, 8)}...)`;
48
- this._trigger();
49
-
50
- // Subscribe to real-time progress updates via WebSocket.
51
- client.onExecutionProgress(executionId, (data) => {
52
- if (data.progressUpdate) {
53
- this.progress = data.progressUpdate.progress;
54
- if (data.progressUpdate.result) {
55
- this.status = `${data.progressUpdate.result}: ${data.progressUpdate.resultMessage ?? "Done"}`;
56
- }
57
- }
58
- if (data.messageUpdate) {
59
- this.messages.push(data.messageUpdate.content);
60
- }
61
- this._trigger();
62
- });
63
-
64
- // Poll once after a short delay to catch fast executions that complete
65
- // before the WebSocket subscription is established.
66
- setTimeout(async () => {
67
- try {
68
- const exec = await client.getExecution(executionId);
69
- if (exec.result) {
70
- this.progress = exec.progress;
71
- this.status = `${exec.result}: ${exec.resultMessage ?? "Done"}`;
72
- this._trigger();
73
- }
74
- } catch {
75
- /* WebSocket handles it */
76
- }
77
- }, 2000);
78
- } catch (err) {
79
- this.status = `Error: ${err}`;
80
- this._trigger();
81
- } finally {
82
- client.localServerUrl = null;
83
- }
84
- }
85
-
86
- private _trigger() {
87
- this.onExecutionUpdated.trigger({
88
- status: this.status,
89
- progress: this.progress,
90
- messages: [...this.messages],
91
- });
92
- }
93
- }
94
-
95
- export * from "./src";
1
+ import * as OBC from "@thatopen/components";
2
+ import { getAppManager } from "../../app";
3
+ import { CloudRunnerStatus } from "./src";
4
+
5
+ export class CloudRunner extends OBC.Component {
6
+ static readonly uuid = "7c4e5d3b-2a1f-4e8d-9b6c-0a3f7e2c5d1b" as const;
7
+
8
+ enabled = true;
9
+
10
+ readonly onExecutionUpdated = new OBC.Event<CloudRunnerStatus>();
11
+
12
+ // TODO: Replace with your actual component ID after publishing.
13
+ componentId = "your-component-id";
14
+ localServerUrl = "http://localhost:4001";
15
+
16
+ // Reactive state — read by the UI template on each render.
17
+ status = "Idle";
18
+ progress = 0;
19
+ messages: string[] = [];
20
+
21
+ constructor(components: OBC.Components) {
22
+ super(components);
23
+ components.add(CloudRunner.uuid, this);
24
+ }
25
+
26
+ async run(useLocal: boolean) {
27
+ // Resolve client at call time via the app shim — never store it as a field.
28
+ const client = getAppManager(this.components).client;
29
+ if (!client) {
30
+ this.status = "No platform client available.";
31
+ this._trigger();
32
+ return;
33
+ }
34
+
35
+ client.localServerUrl = useLocal ? this.localServerUrl : null;
36
+
37
+ this.status = useLocal ? "Starting (local)..." : "Starting (deployed)...";
38
+ this.progress = 0;
39
+ this.messages = [];
40
+ this._trigger();
41
+
42
+ try {
43
+ const { executionId } = await client.executeComponent(this.componentId, {
44
+ greeting: "Hello from the BIM app!",
45
+ });
46
+
47
+ this.status = `Running (${executionId.slice(0, 8)}...)`;
48
+ this._trigger();
49
+
50
+ // Subscribe to real-time progress updates via WebSocket.
51
+ client.onExecutionProgress(executionId, (data) => {
52
+ if (data.progressUpdate) {
53
+ this.progress = data.progressUpdate.progress;
54
+ if (data.progressUpdate.result) {
55
+ this.status = `${data.progressUpdate.result}: ${data.progressUpdate.resultMessage ?? "Done"}`;
56
+ }
57
+ }
58
+ if (data.messageUpdate) {
59
+ this.messages.push(data.messageUpdate.content);
60
+ }
61
+ this._trigger();
62
+ });
63
+
64
+ // Poll once after a short delay to catch fast executions that complete
65
+ // before the WebSocket subscription is established.
66
+ setTimeout(async () => {
67
+ try {
68
+ const exec = await client.getExecution(executionId);
69
+ if (exec.result) {
70
+ this.progress = exec.progress;
71
+ this.status = `${exec.result}: ${exec.resultMessage ?? "Done"}`;
72
+ this._trigger();
73
+ }
74
+ } catch {
75
+ /* WebSocket handles it */
76
+ }
77
+ }, 2000);
78
+ } catch (err) {
79
+ this.status = `Error: ${err}`;
80
+ this._trigger();
81
+ } finally {
82
+ client.localServerUrl = null;
83
+ }
84
+ }
85
+
86
+ private _trigger() {
87
+ this.onExecutionUpdated.trigger({
88
+ status: this.status,
89
+ progress: this.progress,
90
+ messages: [...this.messages],
91
+ });
92
+ }
93
+ }
94
+
95
+ export * from "./src";
@@ -1 +1 @@
1
- export * from "./types";
1
+ export * from "./types";
@@ -1,5 +1,5 @@
1
- export interface CloudRunnerStatus {
2
- status: string;
3
- progress: number;
4
- messages: string[];
5
- }
1
+ export interface CloudRunnerStatus {
2
+ status: string;
3
+ progress: number;
4
+ messages: string[];
5
+ }
@@ -1 +1 @@
1
- export * from "./CloudRunner";
1
+ export * from "./CloudRunner";
@@ -1 +1 @@
1
- export const icons = {};
1
+ export const icons = {};
@@ -0,0 +1,191 @@
1
+ import * as THREE from "three";
2
+ import * as OBC from "@thatopen/components";
3
+ import * as OBF from "@thatopen/components-front";
4
+ import * as FRAGS from "@thatopen/fragments";
5
+ // Inlines the fragments worker so it runs inside the platform's sandboxed iframe.
6
+ import "@thatopen/fragments/inline";
7
+ import * as BUI from "@thatopen/ui";
8
+ import * as MARKERJS from "@markerjs/markerjs3";
9
+ import {
10
+ PlatformClient,
11
+ UIManager,
12
+ GitHistoryManager,
13
+ } from "@thatopen/services";
14
+ import { setAppContext } from "./app";
15
+
16
+ // The standard app, plus the revit-flow commit history: a History layout with the
17
+ // timeline beside the viewer. Scaffolded by `thatopen create <name> --beta --history`.
18
+ //
19
+ // THE BOOT ORDER IS THE WHOLE TRICK, and it is why this ships as a file rather than
20
+ // as instructions. GitHistoryManager reads the commit .frag files and colours the
21
+ // elements of a commit, so it reaches for FragmentsManager and for the 3D world the
22
+ // moment it starts. FragmentsManager is initialised inside top-app's own `setup`
23
+ // below, and the world does not exist until <top-app> is in the DOM and top-viewer
24
+ // has built it. Call `init(client)` where it reads naturally, right after
25
+ // `client.setup`, and you get "FragmentsManager not initialized" plus a pair of
26
+ // undefined `onHighlight` / `onClear` errors, none of which say "too early".
27
+ async function main() {
28
+ const client = PlatformClient.fromPlatformContext();
29
+
30
+ // Brand accent (purple) — drives layout-selector active state, dividers, etc.
31
+ document.documentElement.style.setProperty("--bim-ui_accent-base", "#6528d7");
32
+
33
+ // The dev `thatopen serve` wrapper HTML doesn't zero the UA body margin (8px),
34
+ // which insets the whole app inside the platform iframe. Kill it here so it's
35
+ // fixed in both dev and production regardless of the host page.
36
+ document.body.style.margin = "0";
37
+
38
+ // 1 ── UIManager registers the platform web components (top-app, top-viewer, …)
39
+ // before the DOM renders. GitHistoryManager is the headless half of the history
40
+ // panel: <top-git-history> is intent-only and asks the registry for it by uuid,
41
+ // so without this registration the panel mounts and stays inert.
42
+ const { components } = await client.setup<OBC.Components>(
43
+ { OBC, OBF, BUI, THREE, FRAGS, MARKERJS },
44
+ { uuid: UIManager.uuid },
45
+ { uuid: GitHistoryManager.uuid },
46
+ );
47
+ components.get(UIManager).init();
48
+
49
+ // One STABLE top-viewer node, returned by reference so re-rendering top-app
50
+ // reuses it instead of disposing and recreating its world.
51
+ const viewerEl = document.createElement("top-viewer");
52
+ viewerEl.style.border = "1px solid var(--bim-ui_bg-contrast-20)";
53
+ viewerEl.style.borderRadius = "0.75rem";
54
+ viewerEl.style.boxSizing = "border-box";
55
+
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ const app = document.createElement("top-app") as any;
58
+
59
+ // 2 ── this callback is what initialises FragmentsManager. Nothing that needs
60
+ // fragments may run before it has resolved.
61
+ app.setup = (waitUntil: (p: Promise<void>, label?: string) => void) => {
62
+ waitUntil(
63
+ (async () => {
64
+ const fragments = components.get(OBC.FragmentsManager);
65
+ const workerUrl = await FRAGS.FragmentsModels.getWorker();
66
+ fragments.init(await FRAGS.toClassicWorker(workerUrl), {
67
+ classicWorker: true,
68
+ });
69
+ })(),
70
+ "Fragments Core",
71
+ );
72
+ return { components, client };
73
+ };
74
+
75
+ // Mount minimally first (viewer only) so top-viewer creates the world.
76
+ app.elements = { viewer: () => BUI.html`${viewerEl}` };
77
+ app.layouts = {
78
+ Main: {
79
+ label: "Main",
80
+ icon: "solar:3d-square-bold",
81
+ template: `"viewer" 1fr / 1fr`,
82
+ },
83
+ };
84
+ app.layout = "Main";
85
+
86
+ const container = document.getElementById("that-open-app") ?? document.body;
87
+ container.appendChild(app);
88
+
89
+ // 3 ── the world is created asynchronously by top-viewer. Wait for it.
90
+ await firstWorld(components.get(OBC.Worlds));
91
+
92
+ // Platform client + project data for the AppManager-shim consumers.
93
+ const projectId: string | undefined = client?.context?.projectId;
94
+ let projectData;
95
+ try {
96
+ if (projectId) projectData = await client.getProjectData(projectId);
97
+ } catch {
98
+ /* dev/no-project → consumers degrade gracefully */
99
+ }
100
+ setAppContext(client, projectData);
101
+
102
+ // 4 ── ONLY NOW. Everything above had to have happened first. Read the comment
103
+ // at the top of this file before moving this line.
104
+ await components.get(GitHistoryManager).init(client);
105
+
106
+ // Pluggable loaders for <top-models-list>. Heavy, app-specific loaders are
107
+ // registered here so they stay OUT of the built-in's bundle.
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ let rcViewer: any = null;
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ const modelLoaders: Record<string, any> = {
112
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
+ "3tz": async (fileId: string, ctx: any) => {
114
+ if (!rcViewer) {
115
+ const { realityCaptureViewer } = await import(
116
+ "./setups/reality-capture-viewer"
117
+ );
118
+ rcViewer = realityCaptureViewer(ctx.components, client);
119
+ }
120
+ const saved = ctx.getAlignment(fileId) as number[] | undefined;
121
+ await rcViewer.loadIntoWorld(fileId, {
122
+ keepPostproduction: true,
123
+ transform: saved ? new THREE.Matrix4().fromArray(saved) : undefined,
124
+ onTransformChange: (m: THREE.Matrix4) =>
125
+ ctx.setAlignment(fileId, m.toArray()),
126
+ });
127
+ },
128
+ };
129
+
130
+ // The stable viewer + the panels, under named layouts with the activity-bar
131
+ // sidebar. All panels are built-ins that self-wire from top-app's contexts.
132
+ app.elements = {
133
+ viewer: () => BUI.html`${viewerEl}`,
134
+ tree: () => BUI.html`<top-model-tree></top-model-tree>`,
135
+ properties: () => BUI.html`<top-properties-panel></top-properties-panel>`,
136
+ files: () =>
137
+ BUI.html`<top-models-list .loaders=${modelLoaders}></top-models-list>`,
138
+ dataTable: () => BUI.html`<top-data-table-panel></top-data-table-panel>`,
139
+ objects: () => BUI.html`<top-objects-panel></top-objects-panel>`,
140
+ settings: () => BUI.html`<top-settings-panel></top-settings-panel>`,
141
+ history: () => BUI.html`<top-git-history></top-git-history>`,
142
+ };
143
+ app.layouts = {
144
+ Explorer: {
145
+ icon: "mdi:file-tree",
146
+ template: `"tree viewer" 1fr "properties viewer" 1fr / 22rem 1fr`,
147
+ },
148
+ Assets: {
149
+ icon: "mdi:folder-multiple-outline",
150
+ template: `"files viewer" 1fr "objects viewer" 1fr / 22rem 1fr`,
151
+ },
152
+ Data: {
153
+ icon: "mdi:table",
154
+ template: `"dataTable viewer" 1fr / 22rem 1fr`,
155
+ },
156
+ Settings: {
157
+ icon: "mdi:cog",
158
+ template: `"settings viewer" 1fr / 22rem 1fr`,
159
+ },
160
+ // Same side, same width and same shape as the others: this only swaps the panel.
161
+ History: {
162
+ icon: "at-icons:git-branch",
163
+ template: `"history viewer" 1fr / 22rem 1fr`,
164
+ },
165
+ };
166
+ // Opens on History, because that is what this app was scaffolded for.
167
+ app.layout = "History";
168
+ app.sidebar = true;
169
+
170
+ viewerEl.appendChild(document.createElement("top-viewer-toolbar"));
171
+
172
+ // The history panel lights up once a revit-flow model is loaded. Load it from the
173
+ // Assets panel, or auto-load it here if this app only ever shows one model.
174
+ }
175
+
176
+ /** Resolves with the first world once it exists (top-viewer creates it async). */
177
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
178
+ function firstWorld(worlds: any): Promise<any> {
179
+ const existing = [...worlds.list.values()][0];
180
+ if (existing) return Promise.resolve(existing);
181
+ return new Promise((resolve) => {
182
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
183
+ const handler = ({ value }: any) => {
184
+ worlds.list.onItemSet.remove(handler);
185
+ resolve(value);
186
+ };
187
+ worlds.list.onItemSet.add(handler);
188
+ });
189
+ }
190
+
191
+ main().catch(console.error);
@@ -1,26 +1,26 @@
1
- import * as BUI from "@thatopen/ui";
2
- import { AppInfoSectionComponent } from "./src";
3
- import { getAppManager } from "../../app";
4
-
5
- export const appInfoSectionTemplate: AppInfoSectionComponent = ({
6
- components,
7
- }) => {
8
- const app = getAppManager(components);
9
-
10
- const fileItems =
11
- app.projectData?.files.map(
12
- (f: { name: string }) => BUI.html`<bim-label>${f.name}</bim-label>`,
13
- ) ?? [];
14
-
15
- return BUI.html`
16
- <bim-panel-section label="App Info" icon="solar:info-circle-bold">
17
- <bim-label>App ID: ${app.client?.context.appId}</bim-label>
18
- <bim-label>Project ID: ${app.client?.context.projectId}</bim-label>
19
- <bim-label>API URL: ${app.client?.context.apiUrl}</bim-label>
20
- <bim-label>Files: ${fileItems.length}</bim-label>
21
- ${fileItems}
22
- </bim-panel-section>
23
- `;
24
- };
25
-
26
- export * from "./src";
1
+ import * as BUI from "@thatopen/ui";
2
+ import { AppInfoSectionComponent } from "./src";
3
+ import { getAppManager } from "../../app";
4
+
5
+ export const appInfoSectionTemplate: AppInfoSectionComponent = ({
6
+ components,
7
+ }) => {
8
+ const app = getAppManager(components);
9
+
10
+ const fileItems =
11
+ app.projectData?.files.map(
12
+ (f: { name: string }) => BUI.html`<bim-label>${f.name}</bim-label>`,
13
+ ) ?? [];
14
+
15
+ return BUI.html`
16
+ <bim-panel-section label="App Info" icon="solar:info-circle-bold">
17
+ <bim-label>App ID: ${app.client?.context.appId}</bim-label>
18
+ <bim-label>Project ID: ${app.client?.context.projectId}</bim-label>
19
+ <bim-label>API URL: ${app.client?.context.apiUrl}</bim-label>
20
+ <bim-label>Files: ${fileItems.length}</bim-label>
21
+ ${fileItems}
22
+ </bim-panel-section>
23
+ `;
24
+ };
25
+
26
+ export * from "./src";
@@ -1 +1 @@
1
- export * from "./types";
1
+ export * from "./types";
@@ -1,15 +1,15 @@
1
- import * as BUI from "@thatopen/ui";
2
- import * as OBC from "@thatopen/components";
3
-
4
- export interface AppInfoSectionState {
5
- components: OBC.Components;
6
- }
7
-
8
- export type AppInfoSectionComponent =
9
- BUI.StatefullComponent<AppInfoSectionState>;
10
-
11
- // Used in App type (app.ts) to type this element slot in the grid.
12
- export type AppInfoSectionGridElement = {
13
- name: "appInfoSection";
14
- state: AppInfoSectionState;
15
- };
1
+ import * as BUI from "@thatopen/ui";
2
+ import * as OBC from "@thatopen/components";
3
+
4
+ export interface AppInfoSectionState {
5
+ components: OBC.Components;
6
+ }
7
+
8
+ export type AppInfoSectionComponent =
9
+ BUI.StatefullComponent<AppInfoSectionState>;
10
+
11
+ // Used in App type (app.ts) to type this element slot in the grid.
12
+ export type AppInfoSectionGridElement = {
13
+ name: "appInfoSection";
14
+ state: AppInfoSectionState;
15
+ };