@thatopen/services 0.0.1 → 0.0.3

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 (79) hide show
  1. package/AGENTS.md +86 -0
  2. package/CONTEXT.md +4 -4
  3. package/README.md +4 -4
  4. package/dist/cli.js +17 -6
  5. package/dist/core/client.d.ts +1 -1
  6. package/docs/access-backend-data.md +19 -0
  7. package/docs/app-architecture.md +94 -0
  8. package/docs/app-layout.md +139 -0
  9. package/docs/app-wiring.md +123 -0
  10. package/docs/bim-components/coding-conventions.md +128 -0
  11. package/docs/bim-components/element-collections.md +69 -0
  12. package/docs/bim-components/expose-events.md +84 -0
  13. package/docs/bim-components/library-examples.md +28 -0
  14. package/docs/bim-components/observable-collections.md +83 -0
  15. package/docs/bim-components/overview.md +160 -0
  16. package/docs/bim-components/per-frame-updates.md +20 -0
  17. package/docs/bim-components/save-and-restore-state.md +21 -0
  18. package/docs/bim-components/setup-and-cleanup.md +64 -0
  19. package/docs/bim-components/type-conventions.md +49 -0
  20. package/docs/bim-components/user-driven-object-creation.md +56 -0
  21. package/docs/cli-setup.md +54 -0
  22. package/docs/cloud-components.md +74 -0
  23. package/docs/connect-logic-to-ui.md +113 -0
  24. package/docs/previewing.md +45 -0
  25. package/docs/publishing.md +35 -0
  26. package/docs/scaffolding.md +71 -0
  27. package/docs/ui-components/async-actions.md +18 -0
  28. package/docs/ui-components/confirmation-dialog.md +39 -0
  29. package/docs/ui-components/data-table.md +185 -0
  30. package/docs/ui-components/display-text.md +39 -0
  31. package/docs/ui-components/inline-form.md +44 -0
  32. package/docs/ui-components/library-examples.md +24 -0
  33. package/docs/ui-components/overview.md +194 -0
  34. package/docs/ui-components/rendering-patterns.md +129 -0
  35. package/docs/ui-components/sections-layout.md +123 -0
  36. package/docs/update-grid-elements.md +46 -0
  37. package/docs/using-colors.md +32 -0
  38. package/docs/using-icons.md +37 -0
  39. package/package.json +4 -2
  40. package/src/cli/templates/bim/CONTEXT.md +4 -7
  41. package/src/cli/templates/bim/package.json +1 -2
  42. package/src/cli/templates/bim/src/app.ts +1 -1
  43. package/src/cli/templates/bim/src/bim-components/CloudRunner/index.ts +1 -1
  44. package/src/cli/templates/bim/src/main.ts +2 -3
  45. package/src/cli/templates/bim/src/setups/ui-manager.ts +1 -1
  46. package/src/cli/templates/bim/src/setups/viewports-manager.ts +1 -1
  47. package/src/cli/templates/bim-beta/CONTEXT.md +241 -0
  48. package/src/cli/templates/bim-beta/package.json +25 -0
  49. package/src/cli/templates/bim-beta/src/app.ts +16 -0
  50. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/index.ts +91 -0
  51. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/src/index.ts +1 -0
  52. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/src/types.ts +5 -0
  53. package/src/cli/templates/bim-beta/src/bim-components/index.ts +1 -0
  54. package/src/cli/templates/bim-beta/src/globals.ts +1 -0
  55. package/src/cli/templates/bim-beta/src/main.ts +89 -0
  56. package/src/cli/templates/bim-beta/src/setups/cloud-runner.ts +13 -0
  57. package/src/cli/templates/bim-beta/src/setups/index.ts +3 -0
  58. package/src/cli/templates/bim-beta/src/setups/ui-manager.ts +27 -0
  59. package/src/cli/templates/bim-beta/src/setups/viewports-manager.ts +22 -0
  60. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/index.ts +26 -0
  61. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/src/index.ts +1 -0
  62. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/src/types.ts +15 -0
  63. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/index.ts +37 -0
  64. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/src/index.ts +1 -0
  65. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/src/types.ts +14 -0
  66. package/src/cli/templates/bim-beta/src/ui-components/index.ts +2 -0
  67. package/src/cli/templates/cloud/CONTEXT.md +205 -205
  68. package/src/cli/templates/cloud/package.json +1 -1
  69. package/src/cli/templates/cloud/src/main.ts +70 -70
  70. package/src/cli/templates/cloud-test/package.json +1 -1
  71. package/src/cli/templates/cloud-test/src/main.ts +1 -1
  72. package/src/cli/templates/default/CONTEXT.md +4 -4
  73. package/src/cli/templates/default/package.json +3 -0
  74. package/src/cli/templates/default/src/main.ts +3 -3
  75. package/src/cli/templates/shared/AGENTS.md +30 -0
  76. package/src/cli/templates/shared/CLAUDE.md +1 -0
  77. package/src/cli/templates/shared/cloud/vite.config.js +3 -3
  78. package/src/cli/templates/test/package.json +1 -1
  79. package/src/cli/templates/test/src/main.ts +2 -2
@@ -0,0 +1,123 @@
1
+ # Panel Section Layout
2
+
3
+ ## Composition mental model
4
+
5
+ A panel section has zones that follow a natural sequence reflecting how the user processes the panel: first they act on the context (controls), then understand the current state (messages), then operate on the selected item (toolbar), then see the data (main content), and finally execute high-impact actions (bottom).
6
+
7
+ ```
8
+ <bim-panel-section label="..." icon="..." fixed?>
9
+ [controls bar] ← search or selector + action buttons
10
+ [status messages] ← conditional bim-labels (warn, success, info)
11
+ [actions toolbar] ← bim-toolbar when there are many actions on the active item
12
+ [main content] ← table, list, or sub-component
13
+ [primary actions] ← 1-2 high-impact buttons at the bottom
14
+ </bim-panel-section>
15
+ ```
16
+
17
+ Not all zones are always present. This sequence is a guide, not a mandatory template — if the panel's content breaks that flow in a way that improves the experience, propose the layout that makes the most sense and explain the reasoning.
18
+
19
+ ### Forbidden nesting
20
+
21
+ Inside a `<bim-panel-section>` it is **never** valid to place:
22
+
23
+ - Another `<bim-panel-section>`
24
+ - A `<bim-panel>`
25
+
26
+ This mistake is most tempting when the user asks to "group" sections. The correct answer is **not** nesting — it is creating independent templates. A template does not decide how it is grouped with others; that is the consumer's responsibility. If the content truly belongs in one section, use the existing internal zones (`controls bar`, `toolbar`, `main content`). If it needs to be split into multiple sections, each section is its own independent template.
27
+
28
+ ---
29
+
30
+ ## Controls bar
31
+
32
+ Always a `<div style="display: flex; gap: 0.5rem;">`. There are two main variants:
33
+
34
+ **Search** — when the user filters over a list or table:
35
+ ```ts
36
+ return BUI.html`
37
+ <bim-panel-section label="Elements">
38
+ <div style="display: flex; gap: 0.5rem;">
39
+ <bim-text-input @input=${onSearch} vertical placeholder="Search..."></bim-text-input>
40
+ <bim-button icon=${icons.ADD} @click=${onCreate}></bim-button>
41
+ </div>
42
+ ${table}
43
+ </bim-panel-section>
44
+ `
45
+ ```
46
+
47
+ **Selector** — when the user picks the active item from a dropdown. The gap is tighter (`0.25rem`) because the dropdown already takes most of the width:
48
+ ```ts
49
+ return BUI.html`
50
+ <bim-panel-section label="Proposals">
51
+ <div style="display: flex; gap: 0.25rem;">
52
+ ${proposalsDropdown}
53
+ <bim-button icon=${icons.ADD} @click=${onCreate}></bim-button>
54
+ </div>
55
+ ${content}
56
+ </bim-panel-section>
57
+ `
58
+ ```
59
+
60
+ Action buttons in the controls bar are always icon-only (`icon=` without `label=`). The search `<bim-text-input>` always has `vertical` and `debounce="200"`.
61
+
62
+ ---
63
+
64
+ ## Status messages
65
+
66
+ Conditional `<bim-label>` elements that appear between the controls and the content to communicate the state of the active item — warnings, confirmations, relevant info. Built as optional variables:
67
+
68
+ ```ts
69
+ let statusMessage: BUI.TemplateResult | undefined
70
+ if (item?.status === "sent") {
71
+ statusMessage = BUI.html`<bim-label>This item has been sent and can't be modified.</bim-label>`
72
+ }
73
+
74
+ return BUI.html`
75
+ <bim-panel-section label="...">
76
+ ${controls}
77
+ ${statusMessage}
78
+ ${content}
79
+ </bim-panel-section>
80
+ `
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Actions toolbar
86
+
87
+ When there are many actions on the active item (more than 3-4 buttons), use `<bim-toolbar>` with `<bim-toolbar-section>` instead of a plain flex div. The toolbar goes after the status messages and before the main content:
88
+
89
+ ```ts
90
+ const toolbar = BUI.html`
91
+ <bim-toolbar style="border: 1px solid var(--bim-ui_bg-contrast-20);">
92
+ <bim-toolbar-section>
93
+ ${addItemBtn}
94
+ <bim-button style="flex: 0" icon=${icons.SELECT} @click=${onSelect}></bim-button>
95
+ <bim-button style="flex: 0" icon=${icons.DELETE} @click=${onDelete}></bim-button>
96
+ </bim-toolbar-section>
97
+ </bim-toolbar>
98
+ `
99
+ ```
100
+
101
+ Buttons inside the toolbar always have `style="flex: 0"` to prevent them from stretching.
102
+
103
+ ---
104
+
105
+ ## Conditional content
106
+
107
+ When the panel content changes completely based on state (e.g. "select something first" vs. showing data), assign to a variable before the `return`:
108
+
109
+ ```ts
110
+ let content: BUI.TemplateResult
111
+ if (hasSelection) {
112
+ content = BUI.html`<bim-table ...></bim-table>`
113
+ } else {
114
+ content = BUI.html`<bim-label>Select an element to see its data.</bim-label>`
115
+ }
116
+
117
+ return BUI.html`
118
+ <bim-panel-section label="...">
119
+ ${controls}
120
+ ${content}
121
+ </bim-panel-section>
122
+ `
123
+ ```
@@ -0,0 +1,46 @@
1
+ # Update Grid Elements
2
+
3
+ ## When to use
4
+
5
+ `app.grid.updateComponent` pushes a state update to a specific named grid element. Use it when a BIM component event needs to refresh the state of a template-based section mounted in the grid.
6
+
7
+ This is different from `uis.custom.get(...).updateInstances()` — that re-renders all instances of a UIManager template regardless of where they're mounted. `updateComponent` targets a **specific grid slot by name**, which makes it the right choice when the element lives in the grid and you want precise, typed control over what state it receives.
8
+
9
+ ---
10
+
11
+ ## API
12
+
13
+ ```ts
14
+ app.grid.updateComponent[componentName](updatedState)
15
+ ```
16
+
17
+ - **`componentName`** — the element name as declared in the `App` type (e.g. `"qtosSection"`)
18
+ - **`updatedState`** — partial state; only the provided fields are merged
19
+
20
+ The call is fully typed: the accepted state shape is inferred from the `{ComponentName}GridElement` type in `app.ts`.
21
+
22
+ ---
23
+
24
+ ## Where to call it
25
+
26
+ In a setup file (`setups/{custom}.ts`), in response to a BIM component event:
27
+
28
+ ```ts
29
+ // setups/qto-manager.ts
30
+ export const qtoManager = (components: OBC.Components) => {
31
+ const app = getAppManager(components)
32
+ const qtoManager = components.get(QtoManager)
33
+
34
+ qtoManager.onResultsChanged.add((results) => {
35
+ app.grid.updateComponent["qtosSection"]({ results })
36
+ })
37
+ }
38
+ ```
39
+
40
+ Never call `updateComponent` from inside a template function — templates receive state passively and should not trigger updates themselves.
41
+
42
+ ---
43
+
44
+ ## Type safety
45
+
46
+ Because `app.grid` is typed via the `App` type in `app.ts`, TypeScript knows which names are valid and what state shape each one accepts. Passing an unknown name or a mismatched state shape produces a type error at the call site.
@@ -0,0 +1,32 @@
1
+ # Using Colors
2
+
3
+ ## `globals.ts` shape
4
+
5
+ ```ts
6
+ export const colors = {
7
+ GREEN: "#00FF00",
8
+ SOFT_BLUE: "#5B8CFF",
9
+ }
10
+ ```
11
+
12
+ **Colors are always stored as full hex codes, including the `#`.** This is required because some systems (like the highlighter) use the string itself as a style identifier in addition to a color value.
13
+
14
+ ## Rules
15
+
16
+ - No hardcoded inline colors — not in the highlighter, not in palettes, not in any component. Always imported from `globals.ts`.
17
+ - Before adding a new color, check whether an existing one already works.
18
+
19
+ ## Example: highlighter
20
+
21
+ ```ts
22
+ const highlighter = components.get(OBF.Highlighter)
23
+ if (!highlighter.styles.has(colors.GREEN)) {
24
+ highlighter.styles.set(colors.GREEN, {
25
+ color: new THREE.Color(colors.GREEN),
26
+ renderedFaces: 1,
27
+ opacity: 1,
28
+ transparent: false,
29
+ })
30
+ }
31
+ highlighter.highlightByID(colors.GREEN, modelIdMap)
32
+ ```
@@ -0,0 +1,37 @@
1
+ # Using Icons
2
+
3
+ ## Icon registry rules
4
+
5
+ **All icons in a platform app must be declared in `globals.ts` and accessed through `AppManager`.** This is not optional.
6
+
7
+ The flow is always:
8
+ 1. Declare the icon in `globals.ts` with a `UPPER_SNAKE_CASE` key
9
+ 2. `app.ts` — the `App` type's `icons` field is typed as `(keyof typeof icons)[]`, which means any new key added to the registry is automatically reflected in the type — no manual update needed per icon, but this field must be present in the `App` type
10
+ 3. Pass the registry to `app.init()` as `icons: appIcons`
11
+ 4. Access icons elsewhere via `app.icons.KEY_NAME` (always through `getAppManager(components)`)
12
+
13
+ ```ts
14
+ // ✗ Forbidden — inline icon string anywhere in the app
15
+ <bim-button icon="ic:round-warning"></bim-button>
16
+ grid.layouts = { Viewer: { icon: "ic:round-warning", template: `...` } }
17
+
18
+ // ✓ Correct — always via the registry
19
+ const app = getAppManager(components)
20
+ <bim-button icon=${app.icons.WARNING}></bim-button>
21
+ grid.layouts = { Viewer: { icon: app.icons.WARNING, template: `...` } }
22
+ ```
23
+
24
+ ## Before adding a new icon
25
+
26
+ Before declaring a new icon in `globals.ts`, check if an existing one in the registry already serves the purpose. Reuse an existing icon when it communicates the same intent — avoid registry bloat.
27
+
28
+ If no existing icon fits, add it to `globals.ts` first — then use it through `app.icons`.
29
+
30
+ ## `globals.ts` shape
31
+
32
+ ```ts
33
+ export const icons = {
34
+ WARNING: "ic:round-warning",
35
+ COLLISION: "fa7-solid:explosion",
36
+ }
37
+ ```
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "access": "public"
16
16
  },
17
17
  "private": false,
18
- "version": "0.0.1",
18
+ "version": "0.0.3",
19
19
  "main": "dist/index.cjs.js",
20
20
  "module": "dist/index.es.js",
21
21
  "types": "dist/index.d.ts",
@@ -26,6 +26,8 @@
26
26
  "dist",
27
27
  "src/cli/templates",
28
28
  "src/built-in",
29
+ "docs",
30
+ "AGENTS.md",
29
31
  "CONTEXT.md"
30
32
  ],
31
33
  "scripts": {
@@ -35,7 +37,7 @@
35
37
  "build:lib": "tsc && vite build",
36
38
  "test": "vitest run",
37
39
  "test:watch": "vitest",
38
- "build:cli": "vite build --config vite.config.cli.mts && node scripts/copy-templates.mjs",
40
+ "build:cli": "vite build --config vite.config.cli.mts",
39
41
  "preview": "vite preview",
40
42
  "test:ui": "vite --config test/vite.config.mts",
41
43
  "test:cli-build-app": "npm run build:cli && node test/setup-test-app.mjs",
@@ -44,9 +44,8 @@ Always use `npm run dev` which runs `thatopen serve` under the hood.
44
44
  | `@thatopen/components-front` | `OBF` | Front-end BIM components (Highlighter, measurements, etc.) |
45
45
  | `@thatopen/fragments` | `FRAGS` | Fragment geometry format |
46
46
  | `@thatopen/ui` | `BUI` | UI web components (`<bim-panel>`, `<bim-grid>`, etc.) |
47
- | `@thatopen/ui-obc` | `CUI` | Pre-built OBC UI tables (used by ModelsPanel) |
48
47
  | `three` | `THREE` | 3D rendering engine |
49
- | `thatopen-services` | `EngineServicesClient` | Platform API client + built-in components |
48
+ | `@thatopen/services` | `EngineServicesClient` | Platform API client + built-in components |
50
49
 
51
50
  ## Architecture pattern
52
51
 
@@ -85,7 +84,7 @@ They are fetched, evaluated, and registered with the OBC component system.
85
84
  | **ScreenshotAnnotator** | Modal for annotating screenshots (arrows, text, freehand) via MarkerJS |
86
85
 
87
86
  **Full API reference**: Each component has detailed JSDoc with `@example` blocks in the
88
- `thatopen-services` package source (`src/built-in/index.ts`). Read that file for config
87
+ `@thatopen/services` package source (`src/built-in/index.ts`). Read that file for config
89
88
  interfaces, method signatures, and code examples.
90
89
 
91
90
  ### Loading pattern
@@ -93,13 +92,13 @@ interfaces, method signatures, and code examples.
93
92
  Use `setup` to create the component system and load built-in components in one call:
94
93
 
95
94
  ```ts
96
- import { PlatformClient, AppManager, ViewportsManager } from "thatopen-services";
95
+ import { PlatformClient, AppManager, ViewportsManager } from "@thatopen/services";
97
96
 
98
97
  const client = PlatformClient.fromPlatformContext();
99
98
 
100
99
  // Creates OBC.Components, inits BUI, loads built-ins, calls components.init()
101
100
  const { components } = await client.setup(
102
- { OBC, OBF, BUI, CUI, THREE, FRAGS },
101
+ { OBC, OBF, BUI, THREE, FRAGS },
103
102
  AppManager, ViewportsManager,
104
103
  );
105
104
 
@@ -125,7 +124,6 @@ await client.initBuiltInComponent(AppManager, components);
125
124
  | ViewportsManager | `{ OBC, BUI, THREE, FRAGS }` | — |
126
125
  | LoadModelButton | `{ OBC, BUI }` | — |
127
126
  | ModelsDropdown | `{ OBC, BUI }` | — |
128
- | ModelsPanel | `{ OBC, BUI, CUI }` | `@thatopen/ui-obc` |
129
127
  | ViewerToolbar | `{ OBC, OBF, BUI, THREE }` | `@thatopen/components-front` |
130
128
  | ColorsPalette | `{ OBC, OBF, BUI }` | `@thatopen/components-front` |
131
129
  | ClashesList | `{ OBC, OBF, BUI, THREE }` | `@thatopen/components-front` |
@@ -145,7 +143,6 @@ await client.initBuiltInComponent(AppManager, components);
145
143
  import * as OBC from "@thatopen/components";
146
144
  import * as OBF from "@thatopen/components-front"; // needed for Toolbar, Highlighters, Clashes, etc.
147
145
  import * as BUI from "@thatopen/ui";
148
- import * as CUI from "@thatopen/ui-obc"; // needed for ModelsPanel
149
146
  import * as THREE from "three";
150
147
  import * as FRAGS from "@thatopen/fragments";
151
148
  import * as MARKERJS from "@markerjs/markerjs3"; // needed for ScreenshotAnnotator
@@ -14,8 +14,7 @@
14
14
  "@thatopen/components-front": "~3.4.0",
15
15
  "@thatopen/fragments": "~3.4.0",
16
16
  "@thatopen/ui": "~3.4.0",
17
- "@thatopen/ui-obc": "~3.4.0",
18
- "thatopen-services": "file:../../../..",
17
+ "@thatopen/services": "file:../../../..",
19
18
  "three": "^0.182.0"
20
19
  },
21
20
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  import * as OBC from "@thatopen/components";
2
2
  import * as BUI from "@thatopen/ui";
3
- import { AppManager } from "thatopen-services";
3
+ import { AppManager } from "@thatopen/services";
4
4
  import { icons } from "./globals";
5
5
  import { AppInfoSectionGridElement, CloudRunnerSectionGridElement } from "./ui-components";
6
6
 
@@ -1,5 +1,5 @@
1
1
  import * as OBC from "@thatopen/components";
2
- import { AppManager } from "thatopen-services";
2
+ import { AppManager } from "@thatopen/services";
3
3
  import { CloudRunnerStatus } from "./src";
4
4
 
5
5
  export class CloudRunner extends OBC.Component {
@@ -3,14 +3,13 @@ import * as OBC from "@thatopen/components";
3
3
  import * as OBF from "@thatopen/components-front";
4
4
  import * as FRAGS from "@thatopen/fragments";
5
5
  import * as BUI from "@thatopen/ui";
6
- import * as CUI from "@thatopen/ui-obc";
7
6
  import * as MARKERJS from "@markerjs/markerjs3"
8
7
  import {
9
8
  PlatformClient,
10
9
  AppManager,
11
10
  ViewportsManager,
12
11
  UIManager,
13
- } from "thatopen-services";
12
+ } from "@thatopen/services";
14
13
 
15
14
  import { getAppManager } from "./app";
16
15
  import { uiManager, cloudRunner, viewportsManager, getUIManager } from "./setups";
@@ -26,7 +25,7 @@ async function main() {
26
25
  // First argument: library globals used by the engine.
27
26
  // Subsequent arguments: platform built-in components to initialise.
28
27
  const { components } = await client.setup<OBC.Components>(
29
- { OBC, OBF, BUI, CUI, THREE, FRAGS, MARKERJS },
28
+ { OBC, OBF, BUI, THREE, FRAGS, MARKERJS },
30
29
  { uuid: ViewportsManager.uuid },
31
30
  { uuid: AppManager.uuid },
32
31
  { uuid: UIManager.uuid },
@@ -1,6 +1,6 @@
1
1
  import * as OBC from "@thatopen/components";
2
2
  import * as BUI from "@thatopen/ui";
3
- import { UIManager } from "thatopen-services";
3
+ import { UIManager } from "@thatopen/services";
4
4
  import {
5
5
  appInfoSectionTemplate,
6
6
  AppInfoSectionState,
@@ -1,5 +1,5 @@
1
1
  import * as OBC from "@thatopen/components";
2
- import { ViewportsManager } from "thatopen-services";
2
+ import { ViewportsManager } from "@thatopen/services";
3
3
 
4
4
  export const viewportsManager = async (components: OBC.Components) => {
5
5
  const viewports = components.get(ViewportsManager);
@@ -0,0 +1,241 @@
1
+ # ThatOpen BIM App
2
+
3
+ This is a BIM (Building Information Modeling) app built for the That Open Platform.
4
+ It runs in the browser inside the platform's iframe and has access to a 3D viewer,
5
+ UI components, and the platform API.
6
+
7
+ ## How this app works
8
+
9
+ - **Entry point**: `src/main.ts` — runs as an IIFE when the platform loads the app.
10
+ - **Build output**: `dist/bundle.js` — a single IIFE file built by Vite.
11
+ - **Platform context**: The platform injects `window.__THATOPEN_CONTEXT__` with:
12
+ - `appId` — this app's unique ID
13
+ - `projectId` — the project this app belongs to
14
+ - `accessToken` — Auth0 JWT for API calls
15
+ - `apiUrl` — base URL for the That Open API
16
+
17
+ ## Commands
18
+
19
+ ```bash
20
+ npm run dev # Start dev server (esbuild watch + serve on :4000)
21
+ npm run build # Build dist/bundle.js (Vite/Rollup production build)
22
+ npm run login # Authenticate with the platform (saves token locally)
23
+ npm run publish # Publish to the platform
24
+ ```
25
+
26
+ ### Local development
27
+
28
+ Apps run inside the That Open Platform (platform.thatopen.com) within a project —
29
+ not as standalone websites. To develop locally:
30
+
31
+ 1. Run `npm run dev` — this watches source files with esbuild and serves the bundle on port 4000.
32
+ 2. Open your project on the platform and click the debug button.
33
+ 3. Live reload is enabled — save a file to rebuild automatically.
34
+
35
+ The dev server (`thatopen serve`) uses esbuild for near-instant incremental rebuilds.
36
+ **Important**: Do NOT run `vite`, `vite build --watch`, or `npx vite` directly for development.
37
+ Always use `npm run dev` which runs `thatopen serve` under the hood.
38
+
39
+ ## Key libraries
40
+
41
+ | Package | Import | Purpose |
42
+ |---------|--------|---------|
43
+ | `@thatopen-platform/components-beta` | `OBC` | BIM engine — components, fragments, worlds |
44
+ | `@thatopen-platform/components-front-beta` | `OBF` | Front-end BIM components (Highlighter, measurements, etc.) |
45
+ | `@thatopen-platform/fragments-beta` | `FRAGS` | Fragment geometry format |
46
+ | `@thatopen/ui` | `BUI` | UI web components (`<bim-panel>`, `<bim-grid>`, etc.) |
47
+ | `three` | `THREE` | 3D rendering engine |
48
+ | `@thatopen/services` | `EngineServicesClient` | Platform API client + built-in components |
49
+
50
+ ## Architecture pattern
51
+
52
+ ```
53
+ 1. Create EngineServicesClient from platform context
54
+ 2. Call client.setup(globals, ...builtIns) — creates OBC.Components,
55
+ inits BUI, loads built-in components, calls components.init()
56
+ 3. Create viewport(s) and UI elements
57
+ 4. Configure AppManager with elements + layouts
58
+ 5. Call app.init()
59
+ ```
60
+
61
+ ## Built-in components
62
+
63
+ Built-in components are platform-hosted UI modules loaded at runtime via the API client.
64
+ They are fetched, evaluated, and registered with the OBC component system.
65
+
66
+ | Component | Purpose |
67
+ |-----------|---------|
68
+ | **AppManager** | App shell — CSS grid layout system with sidebar for switching layouts |
69
+ | **ViewportsManager** | Factory for 3D viewports with pre-configured world (scene, camera, renderer) |
70
+ | **LoadModelButton** | Button + dropdown for loading IFC and Fragments files |
71
+ | **ViewerToolbar** | Toolbar with Show/Hide/Focus/Isolate actions and color palette |
72
+ | **ModelsPanel** | Panel listing loaded models with search bar and load button |
73
+ | **ModelsDropdown** | Dropdown selector listing loaded models |
74
+ | **ClassificationsList** | Hierarchical table of IFC classification data |
75
+ | **ClashesList** | Interactive clash detection results with click-to-highlight |
76
+ | **ClippingsList** | Panel listing clipping planes with enable/delete controls |
77
+ | **LengthMeasuringsList** | Panel listing length measurements with cumulative total |
78
+ | **AreaMeasuringsList** | Panel listing area measurements with area/perimeter totals |
79
+ | **ColorsPalette** | Color picker grid with custom input and Highlighter styles |
80
+ | **HighlightersList** | Panel listing Highlighter styles with manage/apply actions |
81
+ | **QtoComparisonList** | Side-by-side quantity comparison for two selected elements |
82
+ | **QueriesHierarchy** | Recursive multi-level query browser for IFC data |
83
+ | **CustomViewLegend** | Color legend overlay with colored circles and labels |
84
+ | **ScreenshotAnnotator** | Modal for annotating screenshots (arrows, text, freehand) via MarkerJS |
85
+
86
+ **Full API reference**: Each component has detailed JSDoc with `@example` blocks in the
87
+ `@thatopen/services` package source (`src/built-in/index.ts`). Read that file for config
88
+ interfaces, method signatures, and code examples.
89
+
90
+ ### Loading pattern
91
+
92
+ Use `setup` to create the component system and load built-in components in one call:
93
+
94
+ ```ts
95
+ import { PlatformClient, AppManager, ViewportsManager } from "@thatopen/services";
96
+
97
+ const client = PlatformClient.fromPlatformContext();
98
+
99
+ // Creates OBC.Components, inits BUI, loads built-ins, calls components.init()
100
+ const { components } = await client.setup(
101
+ { OBC, OBF, BUI, THREE, FRAGS },
102
+ AppManager, ViewportsManager,
103
+ );
104
+
105
+ const app = components.get(AppManager);
106
+ const viewports = components.get(ViewportsManager);
107
+ ```
108
+
109
+ You can also load components individually if needed:
110
+
111
+ ```ts
112
+ // Batch load (parallel)
113
+ await client.initBuiltInComponents(components, AppManager, ViewportsManager);
114
+
115
+ // Or one at a time
116
+ await client.initBuiltInComponent(AppManager, components);
117
+ ```
118
+
119
+ ### Required globals per component
120
+
121
+ | Component | Globals to pass | Extra npm packages needed |
122
+ |-----------|----------------|--------------------------|
123
+ | AppManager | `{ OBC, BUI }` | — |
124
+ | ViewportsManager | `{ OBC, BUI, THREE, FRAGS }` | — |
125
+ | LoadModelButton | `{ OBC, BUI }` | — |
126
+ | ModelsDropdown | `{ OBC, BUI }` | — |
127
+ | ViewerToolbar | `{ OBC, OBF, BUI, THREE }` | `@thatopen-platform/components-front-beta` |
128
+ | ColorsPalette | `{ OBC, OBF, BUI }` | `@thatopen-platform/components-front-beta` |
129
+ | ClashesList | `{ OBC, OBF, BUI, THREE }` | `@thatopen-platform/components-front-beta` |
130
+ | ClassificationsList | `{ OBC, OBF, BUI }` | `@thatopen-platform/components-front-beta` |
131
+ | ClippingsList | `{ OBC, BUI }` | — |
132
+ | HighlightersList | `{ OBC, OBF, BUI }` | `@thatopen-platform/components-front-beta` |
133
+ | LengthMeasuringsList | `{ OBC, OBF, BUI, THREE }` | `@thatopen-platform/components-front-beta` |
134
+ | AreaMeasuringsList | `{ OBC, OBF, BUI, THREE }` | `@thatopen-platform/components-front-beta` |
135
+ | QtoComparisonList | `{ OBC, OBF, BUI }` | `@thatopen-platform/components-front-beta` |
136
+ | QueriesHierarchy | `{ OBC, OBF, BUI }` | `@thatopen-platform/components-front-beta` |
137
+ | CustomViewLegend | `{ OBC, BUI }` | — |
138
+ | ScreenshotAnnotator | `{ OBC, BUI, MARKERJS }` | `@markerjs/markerjs3` |
139
+
140
+ ### Global abbreviations
141
+
142
+ ```ts
143
+ import * as OBC from "@thatopen-platform/components-beta";
144
+ import * as OBF from "@thatopen-platform/components-front-beta"; // needed for Toolbar, Highlighters, Clashes, etc.
145
+ import * as BUI from "@thatopen/ui";
146
+ import * as THREE from "three";
147
+ import * as FRAGS from "@thatopen-platform/fragments-beta";
148
+ import * as MARKERJS from "@markerjs/markerjs3"; // needed for ScreenshotAnnotator
149
+ ```
150
+
151
+ ### AppManager — app shell with CSS grid layouts
152
+
153
+ Creates a grid-based layout system. Define named element slots and named layouts.
154
+ A sidebar for switching layouts appears automatically when multiple layouts exist.
155
+
156
+ ```ts
157
+ const Layouts = ["Viewer", "Split"];
158
+ const Elements = ["viewer", "panel"];
159
+
160
+ await app.init({
161
+ client,
162
+ icons: undefined, // pass Record<K, string> when using typed App interface with icon keys
163
+ grid: (grid: BUI.Grid<Layouts, Elements>) => {
164
+ grid.elements = {
165
+ viewer: viewportElement,
166
+ panel: panelFunction, // Can be HTMLElement, () => BUI.TemplateResult, or { template, initialState }
167
+ };
168
+ grid.layouts = {
169
+ Viewer: { template: `"viewer" 1fr / 1fr` },
170
+ Split: {
171
+ template: `"panel viewer" 1fr / 20rem 1fr`,
172
+ icon: "solar:settings-bold",
173
+ },
174
+ };
175
+ },
176
+ });
177
+ ```
178
+
179
+ The `template` string uses CSS `grid-template` shorthand: `"areas" rows / columns`.
180
+
181
+ ### ViewportsManager — 3D viewport factory
182
+
183
+ Creates viewports with pre-configured world (scene, camera, renderer) and auto-initialized fragments.
184
+
185
+ ```ts
186
+ const viewports = components.get(ViewportsManager);
187
+ const { element, world } = await viewports.create();
188
+ // element is an HTMLElement to place in a layout slot
189
+ // world has world.scene, world.camera, world.renderer
190
+ ```
191
+
192
+ ## Loading a BIM model
193
+
194
+ ```ts
195
+ const fragments = components.get(OBC.FragmentsManager);
196
+
197
+ // From URL
198
+ const response = await fetch("https://example.com/model.frag");
199
+ const buffer = await response.arrayBuffer();
200
+ await fragments.core.load(buffer, { modelId: "my-model" });
201
+
202
+ // From platform storage
203
+ const fileResponse = await client.downloadFile(fileId);
204
+ const fileBuffer = await fileResponse.arrayBuffer();
205
+ await fragments.core.load(fileBuffer, { modelId: "my-model" });
206
+ ```
207
+
208
+ ## EngineServicesClient API (commonly used in apps)
209
+
210
+ ```ts
211
+ // Recommended: auto-reads window.__THATOPEN_CONTEXT__ and sets useBearer: true
212
+ const client = PlatformClient.fromPlatformContext();
213
+ console.log(client.context.projectId); // access the platform context
214
+
215
+ // Files
216
+ const files = await client.listFiles();
217
+ const file = await client.getFile(fileId);
218
+ const response = await client.downloadFile(fileId);
219
+ await client.createFile({ file: blob, name: "model.ifc", versionTag: "v1" });
220
+
221
+ // Folders
222
+ const folders = await client.listFolders();
223
+ await client.createFolder("My Folder");
224
+
225
+ // Execute cloud components
226
+ const { executionId } = await client.executeComponent(componentId, { param: "value" });
227
+ client.onExecutionProgress(executionId, (data) => {
228
+ // data.progressUpdate — progress percentage
229
+ // data.messageUpdate — status messages
230
+ });
231
+
232
+ // Test against a local cloud component (requires thatopen local-server running in the component project)
233
+ client.localServerUrl = "http://localhost:4001";
234
+ const local = await client.executeComponent("any-id", { param: "value" });
235
+ client.localServerUrl = null; // reset to use the cloud API
236
+ ```
237
+
238
+ ## Configuration
239
+
240
+ - `.thatopen` — local config (gitignored). Created by `npm run login`. Contains `accessToken`, `apiUrl`, and `appId` after first publish.
241
+ - `vite.config.js` — builds to IIFE format as `dist/bundle.js`. All dependencies are bundled.
@@ -0,0 +1,25 @@
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
+ "@markerjs/markerjs3": "^3.9.0",
13
+ "@thatopen-platform/components-beta": "latest",
14
+ "@thatopen-platform/components-front-beta": "latest",
15
+ "@thatopen-platform/fragments-beta": "latest",
16
+ "@thatopen/ui": "~3.4.0",
17
+ "@thatopen/services": "file:../../../..",
18
+ "three": "^0.182.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/three": "^0.182.0",
22
+ "typescript": "^5.2.0",
23
+ "vite": "^5.2.0"
24
+ }
25
+ }
@@ -0,0 +1,16 @@
1
+ import * as OBC from "@thatopen-platform/components-beta";
2
+ import * as BUI from "@thatopen/ui";
3
+ import { AppManager } from "@thatopen/services";
4
+ import { icons } from "./globals";
5
+ import { AppInfoSectionGridElement, CloudRunnerSectionGridElement } from "./ui-components";
6
+
7
+ export type App = {
8
+ icons: (keyof typeof icons)[];
9
+ grid: BUI.Grid<
10
+ ["Viewer", "Split"],
11
+ ["viewer", AppInfoSectionGridElement, CloudRunnerSectionGridElement]
12
+ >;
13
+ };
14
+
15
+ export const getAppManager = (components: OBC.Components) =>
16
+ components.get(AppManager<App>);