@thatopen/services 0.3.11 → 0.3.13

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 (38) hide show
  1. package/dist/cli/commands/revit.d.ts +9 -0
  2. package/dist/cli/lib/revit-addin.d.ts +20 -0
  3. package/dist/cli.js +128 -0
  4. package/dist/cloud/LasToPotree.d.ts +59 -0
  5. package/dist/cloud/SplatToFq.d.ts +38 -0
  6. package/dist/cloud/index.d.ts +2 -0
  7. package/docs/builtin/paths.json +86 -86
  8. package/docs/cloud/GaussianSplatConverter.md +43 -0
  9. package/docs/cloud/PointCloudConverter.md +53 -0
  10. package/docs/revit-collab-quickstart.md +205 -0
  11. package/package.json +95 -95
  12. package/src/built-in/ClashesManager/example.ts +283 -283
  13. package/src/built-in/UIManager/src/clashes-panel/example.ts +160 -160
  14. package/src/built-in/UIManager/src/models-list/example.ts +122 -118
  15. package/src/cli/templates/app/package.json +33 -33
  16. package/src/cli/templates/app/src/setups/camera-tools.ts +0 -63
  17. package/src/cli/templates/app/src/setups/camera-views.ts +0 -201
  18. package/src/cli/templates/app/src/setups/card-header.ts +0 -35
  19. package/src/cli/templates/app/src/setups/clipper-tool.ts +0 -773
  20. package/src/cli/templates/app/src/setups/clipper.ts +0 -75
  21. package/src/cli/templates/app/src/setups/cloud-runner.ts +0 -13
  22. package/src/cli/templates/app/src/setups/exploded-view.ts +0 -246
  23. package/src/cli/templates/app/src/setups/helper-panel.ts +0 -88
  24. package/src/cli/templates/app/src/setups/hider.ts +0 -98
  25. package/src/cli/templates/app/src/setups/index.ts +0 -23
  26. package/src/cli/templates/app/src/setups/inspection.ts +0 -136
  27. package/src/cli/templates/app/src/setups/measurement-panel.ts +0 -188
  28. package/src/cli/templates/app/src/setups/measurement-tool.ts +0 -588
  29. package/src/cli/templates/app/src/setups/measurements.ts +0 -133
  30. package/src/cli/templates/app/src/setups/navigation-gizmo.ts +0 -316
  31. package/src/cli/templates/app/src/setups/plans-panel.ts +0 -619
  32. package/src/cli/templates/app/src/setups/right-sidebar.ts +0 -178
  33. package/src/cli/templates/app/src/setups/styles-panel.ts +0 -110
  34. package/src/cli/templates/app/src/setups/styles.ts +0 -375
  35. package/src/cli/templates/app/src/setups/tool-mode-manager.ts +0 -156
  36. package/src/cli/templates/app/src/setups/tool-mode.ts +0 -23
  37. package/src/cli/templates/app/src/setups/toolbar.ts +0 -229
  38. package/src/cli/templates/app/src/setups/ui-manager.ts +0 -27
@@ -1,156 +0,0 @@
1
- import * as OBC from "@thatopen/components";
2
- import * as OBF from "@thatopen/components-front";
3
-
4
- /**
5
- * Central single-active-tool manager (explicit-state discipline per the grimoire
6
- * interaction-editing/modal-tools/state-machine-discipline): exactly ONE modal
7
- * tool (Section / Measure / future walkthrough …) is active at a time, and while
8
- * ANY tool is active the viewer's passive interactions — the Hoverer overlay and
9
- * the Highlighter (hover + click-select) — are suppressed so they don't fight the
10
- * tool's own picking. Both are restored when the last tool exits.
11
- *
12
- * A tool registers a {@link ManagedTool} and calls {@link setActive} on enter /
13
- * {@link clearActive} on exit. When another tool takes over, the manager calls
14
- * the previous tool's {@link ManagedTool.onDeactivate} (LOCAL teardown only — it
15
- * must not call back into the manager, to avoid re-entrancy).
16
- *
17
- * Reachable as a per-Components singleton via {@link toolModeManager} so panels
18
- * (and later walkthrough/others) can route through the same instance. (Flagging:
19
- * a plain memoised singleton, not an OBC.Component — say if you'd rather it be
20
- * components.get()-able and I'll wrap it.)
21
- */
22
- export interface ManagedTool {
23
- readonly id: string;
24
- /**
25
- * Human-readable description of what the user is doing while this tool is
26
- * active (e.g. "Drawing clipping plane", "Measuring length"). Surfaced by the
27
- * active-tool HUD — which only reads this, so any future tool that registers a
28
- * label shows up there with no HUD changes. May be a function for tools whose
29
- * label varies while active (call {@link ToolModeManager.refresh} to update).
30
- */
31
- readonly label: string | (() => string);
32
- /** Optional mdi icon name (e.g. "mdi:scissors-cutting") shown next to the label. */
33
- readonly icon?: string;
34
- /** Called when another tool takes over; do LOCAL teardown only (no manager calls). */
35
- onDeactivate(): void;
36
- }
37
-
38
- export class ToolModeManager {
39
- // Always a real tool — the resting default is `selectTool` (never null), so
40
- // SELECT is a first-class state: reflected by the HUD, and entered via the same
41
- // setActive() exclusivity path as every other tool (so clicking Select
42
- // deactivates whatever modal tool was active, just like clip↔measure).
43
- private _active: ManagedTool;
44
- private _suppressed = false;
45
- private _prevHovererEnabled = true;
46
- private _prevHighlighterEnabled = true;
47
-
48
- /** Fires whenever the active tool changes (carries the new active tool, which
49
- * is `selectTool` in the default mode). `| null` kept for HUD back-compat. */
50
- readonly onActiveChanged = new OBC.Event<ManagedTool | null>();
51
-
52
- /**
53
- * The default SELECT tool — plain object hover + click-select. It is the
54
- * resting active tool (on load and after any modal tool exits) and is the ONE
55
- * tool that does NOT suppress viewer interaction (Select IS that mode).
56
- */
57
- private readonly selectTool: ManagedTool = {
58
- id: ToolModeManager.SELECT_ID,
59
- label: "Select",
60
- icon: "mdi:cursor-default",
61
- onDeactivate: () => {},
62
- };
63
-
64
- constructor(private readonly components: OBC.Components) {
65
- this._active = this.selectTool;
66
- }
67
-
68
- /**
69
- * Id of the implicit default mode: idle / plain object-selection. Active on load
70
- * and whenever no modal tool is engaged. The Select toolbar button maps to it,
71
- * and the HUD reflects it as the resting state.
72
- */
73
- static readonly SELECT_ID = "select";
74
-
75
- get active(): ManagedTool {
76
- return this._active;
77
- }
78
-
79
- /** The active tool's id ({@link ToolModeManager.SELECT_ID} in the default mode). */
80
- getActiveId(): string {
81
- return this._active.id;
82
- }
83
-
84
- /** True in the default object-selection mode (no modal tool active). */
85
- get isSelectMode(): boolean {
86
- return this._active === this.selectTool;
87
- }
88
-
89
- /**
90
- * Return to SELECT (the default): exit whatever modal tool is active so the
91
- * viewer is back to idle hover + click-select. No-op if already in select. The
92
- * Select toolbar button calls this; individual tools still exit via clearActive.
93
- */
94
- selectMode() {
95
- this.setActive(this.selectTool);
96
- }
97
-
98
- /** Make `tool` the single active tool, deactivating the previous one. */
99
- setActive(tool: ManagedTool) {
100
- if (this._active === tool) return;
101
- this._active.onDeactivate(); // tear down the previous tool (selectTool: no-op)
102
- this._active = tool;
103
- // Suppress passive hover/select for MODAL tools only — selectTool IS that mode.
104
- this._applySuppression(tool !== this.selectTool);
105
- this.onActiveChanged.trigger(this._active);
106
- }
107
-
108
- /** Exit `tool` back to the default SELECT tool (no-op unless it's the active one). */
109
- clearActive(tool: ManagedTool) {
110
- if (this._active !== tool) return;
111
- this.setActive(this.selectTool);
112
- }
113
-
114
- /**
115
- * Re-emit the current active tool without changing it — for a tool whose
116
- * dynamic {@link ManagedTool.label} changed while it stayed active (e.g. the
117
- * measurement tool switching length → area), so the HUD re-reads the label.
118
- */
119
- refresh() {
120
- this.onActiveChanged.trigger(this._active);
121
- }
122
-
123
- private _applySuppression(suppress: boolean) {
124
- if (suppress === this._suppressed) return; // only act on a real transition
125
- const hoverer = this.components.get(OBF.Hoverer);
126
- const highlighter = this.components.get(OBF.Highlighter);
127
- if (suppress) {
128
- this._prevHovererEnabled = hoverer.enabled;
129
- this._prevHighlighterEnabled = highlighter.enabled;
130
- hoverer.enabled = false;
131
- highlighter.enabled = false;
132
- } else {
133
- hoverer.enabled = this._prevHovererEnabled;
134
- highlighter.enabled = this._prevHighlighterEnabled;
135
- }
136
- this._suppressed = suppress;
137
- }
138
- }
139
-
140
- // Real OBC.Component wrapper → per-`components` singleton resolved by UUID, so
141
- // the coordinator is shared across bundle boundaries (toolbar built-in + tools +
142
- // the app's panels all drive the SAME manager).
143
- export class ToolModeManagerComponent extends OBC.Component {
144
- static readonly uuid = "9b2c7e10-3a4f-4c21-8b6d-1f0a2c3d4e53" as const;
145
- enabled = true;
146
- readonly manager: ToolModeManager;
147
- constructor(components: OBC.Components) {
148
- super(components);
149
- components.add(ToolModeManagerComponent.uuid, this);
150
- this.manager = new ToolModeManager(components);
151
- }
152
- }
153
-
154
- /** Per-Components singleton accessor. */
155
- export const toolModeManager = (components: OBC.Components): ToolModeManager =>
156
- components.get(ToolModeManagerComponent).manager;
@@ -1,23 +0,0 @@
1
- /**
2
- * Controller returned by each toolbar mode-tool setup (`clipper`,
3
- * `lengthMeasurement`, `areaMeasurement`, …). The toolbar — owned by Worker 2 —
4
- * calls {@link activate} to enter the tool's mode and {@link deactivate} to exit
5
- * it, and guarantees only one mode is active at a time (mutual exclusion).
6
- *
7
- * Contract:
8
- * - `activate()` — enable the tool's pointer/keyboard listeners + cursor.
9
- * - `deactivate()` — remove those listeners and drop any transient in-progress
10
- * state. It must NOT discard finished results (e.g. existing
11
- * section planes / measurements persist across mode switches).
12
- * Both are idempotent.
13
- */
14
- export interface ModeTool {
15
- /** Human label, used for the toolbar button tooltip. */
16
- label: string;
17
- /** Iconify icon name, e.g. "mdi:scissors-cutting". */
18
- icon: string;
19
- /** Enter the tool's mode. */
20
- activate(): void;
21
- /** Exit the tool's mode and clean up transient state. */
22
- deactivate(): void;
23
- }
@@ -1,229 +0,0 @@
1
- import * as OBC from "@thatopen/components";
2
- import * as BUI from "@thatopen/ui";
3
- import { cameraTools } from "./camera-tools";
4
- import { hider } from "./hider";
5
- import { clipper } from "./clipper";
6
- import { lengthMeasurement, areaMeasurement } from "./measurements";
7
- import type { HelperPanelController } from "./helper-panel";
8
- import { stylesTool } from "./styles-panel";
9
-
10
- /**
11
- * A floating tool palette centered at the BOTTOM-CENTER of the viewport. Built
12
- * on BUI's native `bim-toolbar` (icon-only `bim-button`s with hover tooltips),
13
- * placed in a `bim-grid floating` overlay so the bar is interactive while empty
14
- * areas click through to the 3D scene. Styled to match the cards (dark
15
- * `bg-base`, rounded, subtle shadow).
16
- *
17
- * Tool kinds (the modules' integration contract):
18
- * - ACTION `{ label, icon, run() }` — fire-and-forget.
19
- * - MODE `{ label, icon, activate(), deactivate() }` — mutually exclusive:
20
- * activating one deactivates the current; clicking the active one
21
- * turns it off.
22
- * - TOGGLE `{ label, icon, activate(), deactivate(), active?() }` — independent
23
- * on/off (e.g. ortho).
24
- * - PANEL `{ label, icon, render(refresh) }` — toggles the bottom-left helper
25
- * panel showing that tool's content (e.g. Styles). Mutually exclusive
26
- * among panel tools; INDEPENDENT of viewport modes (opening Styles
27
- * doesn't cancel an active section/measure mode, and vice versa).
28
- *
29
- * Only this file assembles the toolbar; other workers deliver tool controllers
30
- * (camera here; hider from hider.ts; clipper/measure to be added when ready).
31
- *
32
- * @param components engine components
33
- * @param container the viewport element to overlay
34
- */
35
- type ActionTool = {
36
- kind: "action";
37
- label: string;
38
- icon: string;
39
- run: () => void | Promise<void>;
40
- };
41
- type ModeTool = {
42
- kind: "mode";
43
- label: string;
44
- icon: string;
45
- activate: () => void | Promise<void>;
46
- deactivate: () => void | Promise<void>;
47
- };
48
- type ToggleTool = {
49
- kind: "toggle";
50
- label: string;
51
- icon: string;
52
- activate: () => void | Promise<void>;
53
- deactivate: () => void | Promise<void>;
54
- active?: () => boolean;
55
- };
56
- type PanelTool = {
57
- kind: "panel";
58
- label: string;
59
- icon: string;
60
- render: (refresh: () => void) => unknown;
61
- };
62
- type Tool = ActionTool | ModeTool | ToggleTool | PanelTool;
63
-
64
- export const toolbar = (
65
- components: OBC.Components,
66
- container: HTMLElement,
67
- helper: HelperPanelController, // the left-stack helper card (panel tools drive it)
68
- ) => {
69
- const cam = cameraTools(components);
70
- const vis = hider(components);
71
- // World for the MODE tools (clipper/measurements need a world + its canvas).
72
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
- const world = [...components.get(OBC.Worlds).list.values()][0] as any;
74
- // MODE controllers ({ label, icon, activate, deactivate }) — only built when a
75
- // world exists (it does: the viewport mounts before the toolbar).
76
- const modeTools: Tool[] = world
77
- ? [
78
- { kind: "mode", ...clipper(components, world) },
79
- { kind: "mode", ...lengthMeasurement(components, world) },
80
- { kind: "mode", ...areaMeasurement(components, world) },
81
- ]
82
- : [];
83
-
84
- // Groups → rendered as separate toolbar sections (auto-divider between them).
85
- // Order: [view: fit, ortho] | [tools: section/measure — pending] | [visibility].
86
- const groups: Tool[][] = [
87
- [
88
- { kind: "action", label: "Zoom to fit", icon: cam.fitAll.icon, run: cam.fitAll.run },
89
- {
90
- kind: "toggle",
91
- label: "Orthographic",
92
- icon: cam.orthoToggle.icon,
93
- activate: cam.orthoToggle.activate,
94
- deactivate: cam.orthoToggle.deactivate,
95
- active: cam.orthoToggle.active,
96
- },
97
- ] as Tool[],
98
- // Mode tools (section / measure) — mutually exclusive.
99
- modeTools,
100
- [
101
- { kind: "action", label: "Hide", icon: "mdi:eye-off-outline", run: vis.hideSelected },
102
- { kind: "action", label: "Isolate", icon: "mdi:select-search", run: vis.isolateSelected },
103
- { kind: "action", label: "Show all", icon: "mdi:eye-outline", run: vis.showAll },
104
- ] as Tool[],
105
- // Panel tools (open the bottom-left helper). Styles needs a world.
106
- world
107
- ? ([{ kind: "panel", ...stylesTool(components, world) }] as Tool[])
108
- : [],
109
- ].filter((g) => g.length > 0);
110
-
111
- // ── Active-state bookkeeping ───────────────────────────────────
112
- let activeMode: ModeTool | null = null; // mutual exclusion across all modes
113
- let activePanel: PanelTool | null = null; // exclusive among panel tools
114
- const onToggle = new Set<ToggleTool>(); // independent toggles that are on
115
-
116
- const isActive = (t: Tool) =>
117
- t.kind === "mode"
118
- ? t === activeMode
119
- : t.kind === "panel"
120
- ? t === activePanel
121
- : t.kind === "toggle"
122
- ? (t.active ? t.active() : onToggle.has(t))
123
- : false;
124
-
125
- const onClick = async (t: Tool) => {
126
- if (t.kind === "action") {
127
- await t.run();
128
- } else if (t.kind === "mode") {
129
- if (activeMode === t) {
130
- await t.deactivate();
131
- activeMode = null;
132
- } else {
133
- if (activeMode) await activeMode.deactivate();
134
- await t.activate();
135
- activeMode = t;
136
- }
137
- } else if (t.kind === "panel") {
138
- // Toggle the helper panel; exclusive among panel tools, independent of modes.
139
- if (activePanel === t) {
140
- activePanel = null;
141
- helper.clear();
142
- } else {
143
- activePanel = t;
144
- helper.show({
145
- title: t.label,
146
- icon: t.icon,
147
- render: () => t.render(() => helper.refresh()),
148
- });
149
- }
150
- } else {
151
- const on = isActive(t);
152
- if (on) {
153
- await t.deactivate();
154
- onToggle.delete(t);
155
- } else {
156
- await t.activate();
157
- onToggle.add(t);
158
- }
159
- }
160
- refresh();
161
- };
162
-
163
- let tick = 0;
164
- const refresh = () => barUpdate({ tick: (tick += 1) });
165
-
166
- const [bar, barUpdate] = BUI.Component.create<HTMLElement, { tick: number }>(
167
- // Param required: BUI.Component.create returns a single element (not the
168
- // [element, update] tuple) when the template has arity 0.
169
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
170
- (_state) => BUI.html`
171
- <bim-toolbar
172
- style="
173
- pointer-events: auto;
174
- padding: 0.15rem;
175
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
176
- /* Uniform rhythm: inter-button gap = G; section padding = G/2 so the
177
- space across a divider (padA + padB) equals the inter-button gap and
178
- the divider sits centered with equal margins. */
179
- "
180
- >
181
- ${groups.map(
182
- (group) => BUI.html`
183
- <bim-toolbar-section label-hidden>
184
- ${group.map(
185
- (t) => BUI.html`
186
- <bim-button
187
- icon=${t.icon}
188
- ?active=${isActive(t)}
189
- @click=${() => onClick(t)}
190
- style="width: 1.9rem; min-width: 1.9rem; height: 1.9rem;"
191
- ><bim-tooltip placement="top">${t.label}</bim-tooltip></bim-button>
192
- `,
193
- )}
194
- </bim-toolbar-section>
195
- `,
196
- )}
197
- </bim-toolbar>
198
- `,
199
- { tick: 0 },
200
- );
201
-
202
- // Floating grid: bar docked bottom-center. Row 1 (1fr) empty filler above;
203
- // row 2 (auto) holds the bar in the center column, flanked by 1fr columns so
204
- // it stays horizontally centered. Empty areas click through.
205
- const grid = BUI.Component.create(() => {
206
- const onCreated = (element?: Element) => {
207
- if (!element) return;
208
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
- const g = element as any;
210
- g.elements = { bar };
211
- g.layouts = {
212
- main: {
213
- template: `
214
- "fillL fillC fillR" 1fr
215
- "restL bar restR" auto
216
- / 1fr auto 1fr
217
- `,
218
- },
219
- };
220
- g.layout = "main";
221
- };
222
- return BUI.html`
223
- <bim-grid style="padding: 1rem;" ${BUI.ref(onCreated)} floating></bim-grid>
224
- `;
225
- });
226
- container.append(grid);
227
-
228
- return bar;
229
- };
@@ -1,27 +0,0 @@
1
- import * as OBC from "@thatopen/components";
2
- import * as BUI from "@thatopen/ui";
3
- import { UIManager } from "@thatopen/services";
4
- import {
5
- appInfoSectionTemplate,
6
- AppInfoSectionState,
7
- cloudRunnerSectionTemplate,
8
- CloudRunnerSectionState,
9
- } from "../ui-components";
10
-
11
- export type CustomUIs = {
12
- appInfoSection: { type: BUI.PanelSection; state: AppInfoSectionState };
13
- cloudRunnerSection: { type: BUI.PanelSection; state: CloudRunnerSectionState };
14
- };
15
-
16
- export const getUIManager = (components: OBC.Components) =>
17
- components.get(UIManager<CustomUIs>);
18
-
19
- export const uiManager = (components: OBC.Components) => {
20
- const uis = getUIManager(components);
21
- uis.registerTemplate("appInfoSection", {
22
- template: appInfoSectionTemplate,
23
- });
24
- uis.registerTemplate("cloudRunnerSection", {
25
- template: cloudRunnerSectionTemplate,
26
- });
27
- };