@thatopen/services 0.7.0 → 0.9.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.
- package/README.md +62 -62
- package/dist/built-in/index.d.ts +66 -41
- package/dist/cli/lib/revit-addin.d.ts +8 -0
- package/dist/cli.js +154 -13
- package/dist/core/client.d.ts +6 -0
- package/dist/core/platform-client.d.ts +139 -0
- package/dist/core/platform-client.live.test.d.ts +1 -0
- package/dist/core/platform-client.notifications.test.d.ts +1 -0
- package/dist/index.cjs.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +635 -445
- package/dist/types/notifications.d.ts +54 -0
- package/dist/vite-env.d.ts +1 -1
- package/docs/ai-quickstart.md +120 -1
- package/docs/cli/create.md +29 -29
- package/docs/cli/local-server.md +38 -38
- package/docs/cli/login.md +32 -32
- package/docs/cli/publish.md +31 -31
- package/docs/cli/run.md +26 -26
- package/docs/cli/serve.md +43 -43
- package/docs/cli/swap.md +30 -30
- package/docs/flow-plugin-guide.md +26 -0
- package/docs/revit-collab-quickstart.md +26 -17
- package/docs/rhino-interop-quickstart.md +74 -7
- package/package.json +1 -1
- package/resources/AGENTS.md +40 -40
- package/src/built-in/CDEManager/example.ts +214 -214
- package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
- package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
- package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
- package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
- package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
- package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
- package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
- package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
- package/src/built-in/UIManager/src/app/example.ts +97 -97
- package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
- package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
- package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
- package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
- package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
- package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
- package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
- package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
- package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
- package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
- package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -70
- package/src/built-in/UIManager/src/models-list/example.ts +122 -122
- package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
- package/src/built-in/index.ts +66 -41
- package/src/cli/templates/app/index.html +35 -35
- package/src/cli/templates/app/package.json +32 -32
- package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
- package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
- package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
- package/src/cli/templates/app/src/bim-components/index.ts +1 -1
- package/src/cli/templates/app/src/globals.ts +1 -1
- package/src/cli/templates/app/src/main.history.ts +191 -0
- package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
- package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
- package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
- package/src/cli/templates/app/src/ui-components/index.ts +2 -2
- package/src/cli/templates/app/tsconfig.json +16 -16
- package/src/cli/templates/app/vite.config.js +57 -57
- package/src/cli/templates/cloud-component/README.md +205 -205
- package/src/cli/templates/cloud-component/_thatopen +5 -5
- package/src/cli/templates/cloud-component/declarations.json +4 -4
- package/src/cli/templates/cloud-component/package.json +21 -21
- package/src/cli/templates/cloud-component/src/main.ts +70 -70
- package/src/cli/templates/cloud-component/tsconfig.json +16 -16
- package/src/cli/templates/cloud-component/vite.config.js +46 -46
- package/src/cli/templates/shared/AGENTS.md +8 -8
- package/src/cli/templates/shared/CLAUDE.md +1 -1
- 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
|
+
};
|