@mcp-b/aec-components 0.2.22 → 0.3.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.
@@ -52,9 +52,16 @@ declare class SigveloIfcViewer extends SigveloElement {
52
52
  private _components;
53
53
  private _world;
54
54
  private _resizeObserver;
55
- firstUpdated(): Promise<void>;
55
+ private _connection;
56
+ private _initializing;
57
+ private _ready;
58
+ private _workerObjectUrl;
59
+ connectedCallback(): void;
60
+ firstUpdated(): void;
61
+ private _startViewer;
56
62
  updated(changed: PropertyValues<this>): Promise<void>;
57
63
  disconnectedCallback(): void;
64
+ private _disposeViewer;
58
65
  /** Load an IFC model from a URL. */
59
66
  loadUrl(url: string): Promise<void>;
60
67
  /** Load an IFC model from a File object. */
@@ -1,3 +1,3 @@
1
1
  import "./ifc-viewer.events.js";
2
- import { t as SigveloIfcViewer } from "../../ifc-viewer-Co1EAZcX.js";
2
+ import { t as SigveloIfcViewer } from "../../ifc-viewer-Ctu-bAs_.js";
3
3
  export { SigveloIfcViewer };
@@ -113,16 +113,62 @@
113
113
  "text": "null | { onDisposed: { enabled: boolean, add: { }, remove: { }, trigger: { }, reset: { }, handlers: any }, list: { onBeforeSet: { enabled: boolean, add: { }, remove: { }, trigger: { }, reset: { }, handlers: any }, onItemSet: { enabled: boolean, add: { }, remove: { }, trigger: { }, reset: { }, handlers: any }, onItemUpdated: { enabled: boolean, add: { }, remove: { }, trigger: { }, reset: { }, handlers: any }, onBeforeDelete: { enabled: boolean, add: { }, remove: { }, trigger: { }, reset: { }, handlers: any }, onItemDeleted: { enabled: boolean, add: { }, remove: { }, trigger: { }, reset: { }, handlers: any }, onCleared: { enabled: boolean, add: { }, remove: { }, trigger: { }, reset: { }, handlers: any }, eventsEnabled: boolean, clear: { }, set: { }, guard: { }, deleteGuard: { }, updateGuard: { }, delete: { }, getKey: { }, add: { }, update: { }, deleteIf: { }, replaceKey: { }, dispose: { }, forEach: { }, get: { }, has: { }, size: number, entries: { }, keys: { }, values: { }, __@iterator@165: { }, __@toStringTag@470: string }, enabled: boolean, _timer: any, onInit: { enabled: boolean, add: { }, remove: { }, trigger: { }, reset: { }, handlers: any }, add: { }, get: { }, init: { }, dispose: { }, update: any }"
114
114
  }
115
115
  },
116
+ {
117
+ "kind": "field",
118
+ "name": "_connection",
119
+ "type": {
120
+ "text": "AbortController | null"
121
+ },
122
+ "privacy": "private",
123
+ "default": "null",
124
+ "parsedType": {
125
+ "text": "null | { signal: { aborted: boolean, onabort: null | { }, reason: any, throwIfAborted: { }, addEventListener: { }, removeEventListener: { }, dispatchEvent: { } }, abort: { } }"
126
+ }
127
+ },
116
128
  {
117
129
  "kind": "method",
118
- "name": "_initViewer",
130
+ "name": "_disposeViewer",
119
131
  "privacy": "private"
120
132
  },
133
+ {
134
+ "kind": "field",
135
+ "name": "_initializing",
136
+ "type": {
137
+ "text": "Promise<void> | null"
138
+ },
139
+ "privacy": "private",
140
+ "default": "null",
141
+ "parsedType": {
142
+ "text": "null | { then: { }, catch: { }, finally: { }, __@toStringTag@470: string }"
143
+ }
144
+ },
145
+ {
146
+ "kind": "method",
147
+ "name": "_initViewer",
148
+ "privacy": "private",
149
+ "parameters": [
150
+ {
151
+ "name": "signal",
152
+ "type": {
153
+ "text": "AbortSignal"
154
+ }
155
+ }
156
+ ]
157
+ },
121
158
  {
122
159
  "kind": "method",
123
160
  "name": "_loadSrc",
124
161
  "privacy": "private"
125
162
  },
163
+ {
164
+ "kind": "field",
165
+ "name": "_ready",
166
+ "type": {
167
+ "text": "boolean"
168
+ },
169
+ "privacy": "private",
170
+ "default": "false"
171
+ },
126
172
  {
127
173
  "kind": "field",
128
174
  "name": "_resizeObserver",
@@ -135,11 +181,28 @@
135
181
  "text": "null | { disconnect: { }, observe: { }, unobserve: { } }"
136
182
  }
137
183
  },
184
+ {
185
+ "kind": "method",
186
+ "name": "_startViewer",
187
+ "privacy": "private"
188
+ },
138
189
  {
139
190
  "kind": "method",
140
191
  "name": "_updateBackgroundColor",
141
192
  "privacy": "private"
142
193
  },
194
+ {
195
+ "kind": "field",
196
+ "name": "_workerObjectUrl",
197
+ "type": {
198
+ "text": "string | null"
199
+ },
200
+ "privacy": "private",
201
+ "default": "null",
202
+ "parsedType": {
203
+ "text": "null | string"
204
+ }
205
+ },
143
206
  {
144
207
  "kind": "field",
145
208
  "name": "_world",
@@ -373,7 +436,7 @@
373
436
  "package": {
374
437
  "name": "@mcp-b/aec-components",
375
438
  "description": "AEC (Architecture, Engineering, Construction) web components for Sigvelo, built with Lit.",
376
- "version": "0.2.22",
439
+ "version": "0.3.0",
377
440
  "license": "MIT"
378
441
  }
379
442
  }
@@ -44,28 +44,53 @@ let SigveloIfcViewer = class SigveloIfcViewer extends SigveloElement {
44
44
  this._components = null;
45
45
  this._world = null;
46
46
  this._resizeObserver = null;
47
+ this._connection = null;
48
+ this._initializing = null;
49
+ this._ready = false;
50
+ this._workerObjectUrl = null;
47
51
  }
48
52
  static {
49
53
  this.styles = [hostStyles, ifc_viewer_styles_default];
50
54
  }
51
- async firstUpdated() {
52
- await this._initViewer();
55
+ connectedCallback() {
56
+ super.connectedCallback();
57
+ if (this.hasUpdated) this._startViewer();
58
+ }
59
+ firstUpdated() {
60
+ this._startViewer();
61
+ }
62
+ _startViewer() {
63
+ if (!this.isConnected || this._connection) return;
64
+ this._connection = new AbortController();
65
+ this._initializing = this._initViewer(this._connection.signal);
53
66
  }
54
67
  async updated(changed) {
55
- if (changed.has("src") && this._components && changed.get("src") !== void 0) await this._loadSrc();
68
+ if (changed.has("src") && this._ready && changed.get("src") !== void 0) await this._loadSrc();
69
+ if (changed.has("cameraControls") && this._world) this._world.camera.controls.enabled = this.cameraControls;
56
70
  if (changed.has("backgroundColor") && this._world) this._updateBackgroundColor();
57
71
  }
58
72
  disconnectedCallback() {
59
73
  super.disconnectedCallback();
74
+ this._disposeViewer();
75
+ }
76
+ _disposeViewer() {
77
+ this._connection?.abort();
78
+ this._connection = null;
79
+ this._ready = false;
60
80
  this._resizeObserver?.disconnect();
61
81
  this._resizeObserver = null;
62
82
  this._components?.dispose();
63
83
  this._components = null;
64
84
  this._world = null;
85
+ if (this._workerObjectUrl) URL.revokeObjectURL(this._workerObjectUrl);
86
+ this._workerObjectUrl = null;
65
87
  }
66
88
  /** Load an IFC model from a URL. */
67
89
  async loadUrl(url) {
68
- if (!this._components) return;
90
+ const signal = this._connection?.signal;
91
+ if (!signal) return;
92
+ await this._initializing;
93
+ if (signal.aborted || !this._components) return;
69
94
  try {
70
95
  const ifcLoader = this._components.get(OBC.IfcLoader);
71
96
  await ifcLoader.setup({
@@ -75,20 +100,27 @@ let SigveloIfcViewer = class SigveloIfcViewer extends SigveloElement {
75
100
  absolute: true
76
101
  }
77
102
  });
78
- const response = await fetch(url);
103
+ if (signal.aborted) return;
104
+ const response = await fetch(url, { signal });
79
105
  if (!response.ok) throw new Error(`Failed to fetch IFC file: ${response.status} ${response.statusText}`);
80
106
  const buffer = new Uint8Array(await response.arrayBuffer());
81
107
  const name = url.split("/").pop()?.replace(".ifc", "") ?? "model";
108
+ if (signal.aborted) return;
82
109
  await ifcLoader.load(buffer, true, name);
110
+ if (signal.aborted) return;
83
111
  this.dispatchEvent(new SigveloIfcLoadEvent());
84
112
  } catch (err) {
113
+ if (signal.aborted) return;
85
114
  const error = err instanceof Error ? err : new Error(String(err));
86
115
  this.dispatchEvent(new SigveloIfcErrorEvent(error));
87
116
  }
88
117
  }
89
118
  /** Load an IFC model from a File object. */
90
119
  async loadFile(file) {
91
- if (!this._components) return;
120
+ const signal = this._connection?.signal;
121
+ if (!signal) return;
122
+ await this._initializing;
123
+ if (signal.aborted || !this._components) return;
92
124
  try {
93
125
  const ifcLoader = this._components.get(OBC.IfcLoader);
94
126
  await ifcLoader.setup({
@@ -98,48 +130,62 @@ let SigveloIfcViewer = class SigveloIfcViewer extends SigveloElement {
98
130
  absolute: true
99
131
  }
100
132
  });
133
+ if (signal.aborted) return;
101
134
  const buffer = new Uint8Array(await file.arrayBuffer());
102
135
  const name = file.name.replace(".ifc", "");
136
+ if (signal.aborted) return;
103
137
  await ifcLoader.load(buffer, true, name);
138
+ if (signal.aborted) return;
104
139
  this.dispatchEvent(new SigveloIfcLoadEvent());
105
140
  } catch (err) {
141
+ if (signal.aborted) return;
106
142
  const error = err instanceof Error ? err : new Error(String(err));
107
143
  this.dispatchEvent(new SigveloIfcErrorEvent(error));
108
144
  }
109
145
  }
110
- async _initViewer() {
146
+ async _initViewer(signal) {
111
147
  const container = this.renderRoot.querySelector(".viewer-container");
112
148
  if (!container) return;
113
- this._components = new OBC.Components();
114
- const worlds = this._components.get(OBC.Worlds);
115
- this._world = worlds.create();
116
- this._world.scene = new OBC.SimpleScene(this._components);
117
- this._world.renderer = new OBC.SimpleRenderer(this._components, container);
118
- this._world.camera = new OBC.SimpleCamera(this._components);
119
- this._components.init();
120
- this._world.scene.setup();
121
- this._updateBackgroundColor();
122
- if (this.grid) this._components.get(OBC.Grids).create(this._world);
123
- const fragments = this._components.get(OBC.FragmentsManager);
124
- const workerBlob = await (await fetch(this.workerUrl)).blob();
125
- const workerFile = new File([workerBlob], "worker.mjs", { type: "text/javascript" });
126
- const workerObjectUrl = URL.createObjectURL(workerFile);
127
- fragments.init(workerObjectUrl);
128
- fragments.list.onItemSet.add(({ value: model }) => {
129
- if (!this._world) return;
130
- model.useCamera(this._world.camera.three);
131
- this._world.scene.three.add(model.object);
132
- fragments.core.update(true);
133
- });
134
- this._world.camera.controls.addEventListener("update", () => {
135
- fragments.core.update();
136
- });
137
- await this._world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);
138
- this._resizeObserver = new ResizeObserver(() => {
139
- this._world?.renderer?.resize();
140
- });
141
- this._resizeObserver.observe(container);
142
- if (this.src) await this._loadSrc();
149
+ try {
150
+ const components = new OBC.Components();
151
+ this._components = components;
152
+ const world = components.get(OBC.Worlds).create();
153
+ this._world = world;
154
+ world.scene = new OBC.SimpleScene(components);
155
+ world.renderer = new OBC.SimpleRenderer(components, container);
156
+ world.camera = new OBC.SimpleCamera(components);
157
+ world.camera.controls.enabled = this.cameraControls;
158
+ components.init();
159
+ world.scene.setup();
160
+ this._updateBackgroundColor();
161
+ if (this.grid) components.get(OBC.Grids).create(world);
162
+ const fragments = components.get(OBC.FragmentsManager);
163
+ const response = await fetch(this.workerUrl, { signal });
164
+ if (!response.ok) throw new Error(`Failed to fetch IFC worker: ${response.status} ${response.statusText}`);
165
+ const blob = await response.blob();
166
+ if (signal.aborted) return;
167
+ this._workerObjectUrl = URL.createObjectURL(new Blob([blob], { type: "text/javascript" }));
168
+ fragments.init(this._workerObjectUrl);
169
+ fragments.list.onItemSet.add(({ value: model }) => {
170
+ if (signal.aborted) return;
171
+ model.useCamera(world.camera.three);
172
+ world.scene.three.add(model.object);
173
+ fragments.core.update(true);
174
+ });
175
+ world.camera.controls.addEventListener("update", () => {
176
+ if (!signal.aborted) fragments.core.update();
177
+ });
178
+ await world.camera.controls.setLookAt(12, 6, 8, 0, 0, -10);
179
+ if (signal.aborted) return;
180
+ this._resizeObserver = new ResizeObserver(() => world.renderer?.resize());
181
+ this._resizeObserver.observe(container);
182
+ this._ready = true;
183
+ if (this.src) this.loadUrl(this.src);
184
+ } catch (err) {
185
+ if (signal.aborted) return;
186
+ this._disposeViewer();
187
+ this.dispatchEvent(new SigveloIfcErrorEvent(err instanceof Error ? err : new Error(String(err))));
188
+ }
143
189
  }
144
190
  async _loadSrc() {
145
191
  if (this.src) await this.loadUrl(this.src);
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { SigveloIfcErrorEvent, SigveloIfcLoadEvent, SigveloIfcProgressEvent } from "./components/ifc-viewer/ifc-viewer.events.js";
2
- import { t as SigveloIfcViewer } from "./ifc-viewer-Co1EAZcX.js";
2
+ import { t as SigveloIfcViewer } from "./ifc-viewer-Ctu-bAs_.js";
3
3
  export { SigveloIfcErrorEvent, SigveloIfcLoadEvent, SigveloIfcProgressEvent, SigveloIfcViewer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/aec-components",
3
- "version": "0.2.22",
3
+ "version": "0.3.0",
4
4
  "description": "AEC (Architecture, Engineering, Construction) web components for Sigvelo, built with Lit.",
5
5
  "keywords": [
6
6
  "aec",
@@ -34,7 +34,7 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@mcp-b/wc-support": "0.2.19"
37
+ "@mcp-b/wc-support": "0.3.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@custom-elements-manifest/analyzer": "^0.10.10",