@ifc-lite/viewer 1.14.2 → 1.14.4

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 (80) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/assets/{Arrow.dom-CSgnLhN4.js → Arrow.dom-_vGzMMKs.js} +1 -1
  3. package/dist/assets/basketViewActivator-BZcoCL3V.js +1 -0
  4. package/dist/assets/{browser-qSKWrKQW.js → browser-Czmf34bo.js} +1 -1
  5. package/dist/assets/ifc-lite_bg-DyBKoGgk.wasm +0 -0
  6. package/dist/assets/index-CMQ_Dgkr.css +1 -0
  7. package/dist/assets/index-D7nEDctQ.js +229 -0
  8. package/dist/assets/{index-4Y4XaV8N.js → index-DX-Qf5fA.js} +72669 -61673
  9. package/dist/assets/{native-bridge-CSFDsEkg.js → native-bridge-DAOWftxE.js} +1 -1
  10. package/dist/assets/{wasm-bridge-Zf90ysEm.js → wasm-bridge-D7jYpn8a.js} +1 -1
  11. package/dist/index.html +2 -2
  12. package/package.json +21 -20
  13. package/src/App.tsx +17 -1
  14. package/src/components/viewer/BasketPresentationDock.tsx +8 -4
  15. package/src/components/viewer/ChatPanel.tsx +1402 -0
  16. package/src/components/viewer/CodeEditor.tsx +70 -4
  17. package/src/components/viewer/CommandPalette.tsx +1 -0
  18. package/src/components/viewer/HierarchyPanel.tsx +28 -13
  19. package/src/components/viewer/MainToolbar.tsx +113 -95
  20. package/src/components/viewer/ScriptPanel.tsx +351 -184
  21. package/src/components/viewer/UpgradePage.tsx +69 -0
  22. package/src/components/viewer/Viewport.tsx +23 -0
  23. package/src/components/viewer/chat/ChatMessage.tsx +144 -0
  24. package/src/components/viewer/chat/ExecutableCodeBlock.tsx +416 -0
  25. package/src/components/viewer/chat/ModelSelector.tsx +102 -0
  26. package/src/components/viewer/chat/renderTextContent.test.ts +23 -0
  27. package/src/components/viewer/chat/renderTextContent.ts +19 -0
  28. package/src/components/viewer/hierarchy/HierarchyNode.tsx +10 -3
  29. package/src/components/viewer/hierarchy/treeDataBuilder.test.ts +126 -0
  30. package/src/components/viewer/hierarchy/treeDataBuilder.ts +139 -38
  31. package/src/components/viewer/hierarchy/types.ts +6 -1
  32. package/src/components/viewer/hierarchy/useHierarchyTree.ts +27 -12
  33. package/src/hooks/useIfcCache.ts +1 -2
  34. package/src/hooks/useSandbox.ts +122 -6
  35. package/src/index.css +10 -0
  36. package/src/lib/attachments.ts +46 -0
  37. package/src/lib/llm/ClerkChatSync.tsx +74 -0
  38. package/src/lib/llm/clerk-auth.ts +62 -0
  39. package/src/lib/llm/code-extractor.ts +50 -0
  40. package/src/lib/llm/context-builder.test.ts +18 -0
  41. package/src/lib/llm/context-builder.ts +305 -0
  42. package/src/lib/llm/free-models.test.ts +118 -0
  43. package/src/lib/llm/message-capabilities.test.ts +131 -0
  44. package/src/lib/llm/message-capabilities.ts +94 -0
  45. package/src/lib/llm/models.ts +197 -0
  46. package/src/lib/llm/repair-loop.test.ts +91 -0
  47. package/src/lib/llm/repair-loop.ts +76 -0
  48. package/src/lib/llm/script-diagnostics.ts +445 -0
  49. package/src/lib/llm/script-edit-ops.test.ts +399 -0
  50. package/src/lib/llm/script-edit-ops.ts +954 -0
  51. package/src/lib/llm/script-preflight.test.ts +513 -0
  52. package/src/lib/llm/script-preflight.ts +990 -0
  53. package/src/lib/llm/script-preservation.test.ts +128 -0
  54. package/src/lib/llm/script-preservation.ts +152 -0
  55. package/src/lib/llm/stream-client.test.ts +97 -0
  56. package/src/lib/llm/stream-client.ts +410 -0
  57. package/src/lib/llm/system-prompt.test.ts +181 -0
  58. package/src/lib/llm/system-prompt.ts +665 -0
  59. package/src/lib/llm/types.ts +150 -0
  60. package/src/lib/scripts/templates/bim-globals.d.ts +226 -7
  61. package/src/lib/scripts/templates/create-building.ts +12 -12
  62. package/src/main.tsx +10 -1
  63. package/src/sdk/adapters/export-adapter.test.ts +24 -0
  64. package/src/sdk/adapters/export-adapter.ts +40 -16
  65. package/src/sdk/adapters/files-adapter.ts +39 -0
  66. package/src/sdk/adapters/model-compat.ts +1 -1
  67. package/src/sdk/adapters/mutate-adapter.ts +20 -6
  68. package/src/sdk/adapters/mutation-view.ts +112 -0
  69. package/src/sdk/adapters/query-adapter.ts +100 -4
  70. package/src/sdk/local-backend.ts +4 -0
  71. package/src/store/index.ts +15 -1
  72. package/src/store/slices/chatSlice.test.ts +325 -0
  73. package/src/store/slices/chatSlice.ts +468 -0
  74. package/src/store/slices/scriptSlice.test.ts +75 -0
  75. package/src/store/slices/scriptSlice.ts +256 -9
  76. package/src/vite-env.d.ts +10 -0
  77. package/vite.config.ts +21 -2
  78. package/dist/assets/ifc-lite_bg-BOvNXJA_.wasm +0 -0
  79. package/dist/assets/index-ByrFvN5A.css +0 -1
  80. package/dist/assets/index-CN7qDq7G.js +0 -216
@@ -0,0 +1,150 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+
5
+ /**
6
+ * Types for the LLM chat integration.
7
+ */
8
+
9
+ import type { RepairScope, ScriptDiagnostic } from './script-diagnostics.js';
10
+
11
+ export type MessageRole = 'user' | 'assistant' | 'system';
12
+
13
+ export interface ChatMessage {
14
+ id: string;
15
+ role: MessageRole;
16
+ content: string;
17
+ /** Timestamp in ms */
18
+ createdAt: number;
19
+ /** Parsed code blocks from assistant messages */
20
+ codeBlocks?: CodeBlock[];
21
+ /** Execution results for code blocks in this message */
22
+ execResults?: Map<number, CodeExecResult>;
23
+ /** Attached files (user messages only) */
24
+ attachments?: FileAttachment[];
25
+ }
26
+
27
+ export interface CodeBlock {
28
+ /** Index within the message */
29
+ index: number;
30
+ /** Language hint from the code fence (e.g. 'js', 'typescript') */
31
+ language: string;
32
+ /** The code content */
33
+ code: string;
34
+ }
35
+
36
+ export interface CodeExecResult {
37
+ status: 'running' | 'success' | 'error';
38
+ logs?: Array<{ level: string; args: unknown[] }>;
39
+ value?: unknown;
40
+ error?: string;
41
+ durationMs?: number;
42
+ }
43
+
44
+ export interface ScriptEditorSelection {
45
+ from: number;
46
+ to: number;
47
+ }
48
+
49
+ export interface ScriptEditorTextChange {
50
+ from: number;
51
+ to: number;
52
+ insert: string;
53
+ }
54
+
55
+ interface ScriptEditBase {
56
+ opId: string;
57
+ baseRevision: number;
58
+ groupId?: string;
59
+ scope?: RepairScope;
60
+ atomic?: boolean;
61
+ targetRootCause?: string;
62
+ }
63
+
64
+ export interface ScriptEditInsertOp extends ScriptEditBase {
65
+ type: 'insert';
66
+ at: number;
67
+ text: string;
68
+ }
69
+
70
+ export interface ScriptEditReplaceRangeOp extends ScriptEditBase {
71
+ type: 'replaceRange';
72
+ from: number;
73
+ to: number;
74
+ text: string;
75
+ expectedText?: string;
76
+ }
77
+
78
+ export interface ScriptEditReplaceSelectionOp extends ScriptEditBase {
79
+ type: 'replaceSelection';
80
+ text: string;
81
+ }
82
+
83
+ export interface ScriptEditAppendOp extends ScriptEditBase {
84
+ type: 'append';
85
+ text: string;
86
+ }
87
+
88
+ export interface ScriptEditReplaceAllOp extends ScriptEditBase {
89
+ type: 'replaceAll';
90
+ text: string;
91
+ }
92
+
93
+ export type ScriptEditOperation =
94
+ | ScriptEditInsertOp
95
+ | ScriptEditReplaceRangeOp
96
+ | ScriptEditReplaceSelectionOp
97
+ | ScriptEditAppendOp
98
+ | ScriptEditReplaceAllOp;
99
+
100
+ export type ChatRepairReason = 'runtime' | 'preflight' | 'patch-conflict' | 'patch-apply';
101
+
102
+ export interface ChatRepairRequest {
103
+ error: string;
104
+ reason: ChatRepairReason;
105
+ diagnostics?: ScriptDiagnostic[];
106
+ staleCodeBlock?: string;
107
+ includeSelection?: boolean;
108
+ requestedRepairScope?: RepairScope;
109
+ rootCauseKey?: string;
110
+ }
111
+
112
+ export interface FileAttachment {
113
+ id: string;
114
+ name: string;
115
+ type: string;
116
+ size: number;
117
+ /** Parsed CSV rows (if CSV file) */
118
+ csvData?: Record<string, string>[];
119
+ /** Column names (if CSV file) */
120
+ csvColumns?: string[];
121
+ /** Raw text content */
122
+ textContent?: string;
123
+ /** Base64-encoded image data (for image attachments) */
124
+ imageBase64?: string;
125
+ /** Whether this is an image attachment */
126
+ isImage?: boolean;
127
+ }
128
+
129
+ export type ModelTier = 'free' | 'pro';
130
+
131
+ /** Relative cost indicator for paid models */
132
+ export type ModelCost = '$' | '$$' | '$$$';
133
+
134
+ export interface LLMModel {
135
+ id: string;
136
+ name: string;
137
+ provider: string;
138
+ tier: ModelTier;
139
+ contextWindow: number;
140
+ /** Whether this model accepts image inputs in chat content */
141
+ supportsImages: boolean;
142
+ /** Whether this model should receive uploaded file context */
143
+ supportsFileAttachments: boolean;
144
+ /** Notes shown in model selector */
145
+ notes?: string;
146
+ /** Relative cost indicator (pro models only) */
147
+ cost?: ModelCost;
148
+ }
149
+
150
+ export type ChatStatus = 'idle' | 'sending' | 'streaming' | 'error';
@@ -23,12 +23,87 @@ interface BimEntity {
23
23
 
24
24
  interface BimPropertySet {
25
25
  name: string;
26
+ Name?: string;
27
+ globalId?: string;
28
+ GlobalId?: string;
26
29
  properties: Array<{ name: string; value: string | number | boolean | null }>;
30
+ Properties?: Array<{ name: string; value: string | number | boolean | null }>;
27
31
  }
28
32
 
29
33
  interface BimQuantitySet {
30
34
  name: string;
31
35
  quantities: Array<{ name: string; value: number | null }>;
36
+ Name?: string;
37
+ Quantities?: Array<{ name: string; value: number | null }>;
38
+ }
39
+
40
+ interface BimAttribute {
41
+ name: string;
42
+ value: string;
43
+ }
44
+
45
+ interface BimClassification {
46
+ system?: string;
47
+ identification?: string;
48
+ name?: string;
49
+ location?: string;
50
+ description?: string;
51
+ path?: string[];
52
+ }
53
+
54
+ interface BimMaterialLayer {
55
+ materialName?: string;
56
+ thickness?: number;
57
+ isVentilated?: boolean;
58
+ name?: string;
59
+ category?: string;
60
+ }
61
+
62
+ interface BimMaterialProfile {
63
+ materialName?: string;
64
+ name?: string;
65
+ category?: string;
66
+ }
67
+
68
+ interface BimMaterialConstituent {
69
+ materialName?: string;
70
+ name?: string;
71
+ fraction?: number;
72
+ category?: string;
73
+ }
74
+
75
+ interface BimMaterial {
76
+ type: 'Material' | 'MaterialLayerSet' | 'MaterialProfileSet' | 'MaterialConstituentSet' | 'MaterialList';
77
+ name?: string;
78
+ description?: string;
79
+ layers?: BimMaterialLayer[];
80
+ profiles?: BimMaterialProfile[];
81
+ constituents?: BimMaterialConstituent[];
82
+ materials?: string[];
83
+ }
84
+
85
+ interface BimTypeProperties {
86
+ typeName: string;
87
+ typeId: number;
88
+ properties: BimPropertySet[];
89
+ }
90
+
91
+ interface BimDocument {
92
+ name?: string;
93
+ description?: string;
94
+ location?: string;
95
+ identification?: string;
96
+ purpose?: string;
97
+ intendedUse?: string;
98
+ revision?: string;
99
+ confidentiality?: string;
100
+ }
101
+
102
+ interface BimRelationships {
103
+ voids: Array<{ id: number; name?: string; type: string }>;
104
+ fills: Array<{ id: number; name?: string; type: string }>;
105
+ groups: Array<{ id: number; name?: string }>;
106
+ connections: Array<{ id: number; name?: string; type: string }>;
32
107
  }
33
108
 
34
109
  interface BimModelInfo {
@@ -39,6 +114,49 @@ interface BimModelInfo {
39
114
  fileSize: number;
40
115
  }
41
116
 
117
+ interface BimFileAttachment {
118
+ name: string;
119
+ type: string;
120
+ size: number;
121
+ rowCount?: number;
122
+ columns?: string[];
123
+ hasTextContent: boolean;
124
+ }
125
+
126
+ type BimPoint3D = [number, number, number];
127
+ type BimPoint2D = [number, number];
128
+
129
+ interface BimPlacement3D {
130
+ Location: BimPoint3D;
131
+ Axis?: BimPoint3D;
132
+ RefDirection?: BimPoint3D;
133
+ }
134
+
135
+ type BimProfileDef =
136
+ | { ProfileType: 'AREA'; XDim: number; YDim: number }
137
+ | { ProfileType: 'AREA'; Radius: number; WallThickness?: number }
138
+ | { ProfileType: 'AREA'; OuterCurve: BimPoint2D[] }
139
+ | { ProfileType: 'AREA'; Shape: 'IfcTShapeProfileDef'; FlangeWidth: number; Depth: number; WebThickness: number; FlangeThickness: number; FilletRadius?: number }
140
+ | { ProfileType: 'AREA'; Shape: 'IfcUShapeProfileDef'; Depth: number; FlangeWidth: number; WebThickness: number; FlangeThickness: number; FilletRadius?: number }
141
+ | { ProfileType: 'AREA'; OverallWidth: number; OverallDepth: number; WebThickness: number; FlangeThickness: number; FilletRadius?: number }
142
+ | { ProfileType: 'AREA'; Depth: number; Width: number; Thickness: number; FilletRadius?: number }
143
+ | { ProfileType: 'AREA'; Depth: number; Width: number; WallThickness: number; Girth: number }
144
+ | { ProfileType: 'AREA'; XDim: number; YDim: number; WallThickness: number; InnerFilletRadius?: number; OuterFilletRadius?: number };
145
+
146
+ type BimElementAttrs = {
147
+ Name?: string;
148
+ Description?: string;
149
+ ObjectType?: string;
150
+ Tag?: string;
151
+ };
152
+
153
+ type BimWallHostedOpening = BimElementAttrs & {
154
+ Position: BimPoint3D;
155
+ Width: number;
156
+ Height: number;
157
+ Thickness?: number;
158
+ };
159
+
42
160
  // ── Namespace declarations ──────────────────────────────────────────────
43
161
 
44
162
  declare const bim: {
@@ -61,10 +179,44 @@ declare const bim: {
61
179
  byType(...types: string[]): BimEntity[];
62
180
  /** Get entity by model ID and express ID */
63
181
  entity(modelId: string, expressId: number): BimEntity | null;
182
+ /** Get all named string/enum attributes for an entity */
183
+ attributes(entity: BimEntity): BimAttribute[];
64
184
  /** Get all IfcPropertySet data for an entity */
65
185
  properties(entity: BimEntity): BimPropertySet[];
66
186
  /** Get all IfcElementQuantity data for an entity */
67
187
  quantities(entity: BimEntity): BimQuantitySet[];
188
+ /** Get a single property value from an entity */
189
+ property(entity: BimEntity, psetName: string, propName: string): string | number | boolean | null;
190
+ /** Get classification references for an entity */
191
+ classifications(entity: BimEntity): BimClassification[];
192
+ /** Get material assignment for an entity */
193
+ materials(entity: BimEntity): BimMaterial | null;
194
+ /** Get type-level property sets for an entity */
195
+ typeProperties(entity: BimEntity): BimTypeProperties | null;
196
+ /** Get linked document references for an entity */
197
+ documents(entity: BimEntity): BimDocument[];
198
+ /** Get structural relationship summary for an entity */
199
+ relationships(entity: BimEntity): BimRelationships;
200
+ /** Get a single quantity value from an entity */
201
+ quantity(entity: BimEntity, qsetName: string, quantityName: string): number | null;
202
+ /** Get related entities by IFC relationship type */
203
+ related(entity: BimEntity, relType: string, direction: 'forward' | 'inverse'): BimEntity[];
204
+ /** Get the spatial container of an entity */
205
+ containedIn(entity: BimEntity): BimEntity | null;
206
+ /** Get entities contained in a spatial container */
207
+ contains(entity: BimEntity): BimEntity[];
208
+ /** Get the parent aggregate of an entity */
209
+ decomposedBy(entity: BimEntity): BimEntity | null;
210
+ /** Get aggregated children of an entity */
211
+ decomposes(entity: BimEntity): BimEntity[];
212
+ /** Get the containing building storey of an entity */
213
+ storey(entity: BimEntity): BimEntity | null;
214
+ /** Get the spatial/aggregation path from project to entity */
215
+ path(entity: BimEntity): BimEntity[];
216
+ /** List all building storeys */
217
+ storeys(): BimEntity[];
218
+ /** Get the current viewer selection as entities */
219
+ selection(): BimEntity[];
68
220
  };
69
221
  /** Viewer control */
70
222
  viewer: {
@@ -89,8 +241,10 @@ declare const bim: {
89
241
  };
90
242
  /** Property editing */
91
243
  mutate: {
92
- /** Set a property value */
244
+ /** Set an IfcPropertySet or quantity value */
93
245
  setProperty(entity: unknown, psetName: string, propName: string, value: unknown): void;
246
+ /** Set a root IFC attribute such as Name, Description, ObjectType, or Tag */
247
+ setAttribute(entity: unknown, attrName: string, value: string): void;
94
248
  /** Delete a property */
95
249
  deleteProperty(entity: unknown, psetName: string, propName: string): void;
96
250
  /** Undo last mutation */
@@ -110,17 +264,69 @@ declare const bim: {
110
264
  /** Add a building storey. Returns storey expressId. */
111
265
  addIfcBuildingStorey(handle: number, params: { Name?: string; Description?: string; Elevation: number }): number;
112
266
  /** Add a wall to a storey. Returns wall expressId. */
113
- addIfcWall(handle: number, storeyId: number, params: { Start: [number,number,number]; End: [number,number,number]; Thickness: number; Height: number; Name?: string; Openings?: Array<{ Width: number; Height: number; Position: [number,number,number]; Name?: string }> }): number;
267
+ addIfcWall(handle: number, storeyId: number, params: BimElementAttrs & { Start: [number,number,number]; End: [number,number,number]; Thickness: number; Height: number; Openings?: Array<{ Width: number; Height: number; Position: [number,number,number]; Name?: string }> }): number;
114
268
  /** Add a slab to a storey. Returns slab expressId. */
115
- addIfcSlab(handle: number, storeyId: number, params: { Position: [number,number,number]; Thickness: number; Width?: number; Depth?: number; Profile?: [number,number][]; Name?: string; Openings?: Array<{ Width: number; Height: number; Position: [number,number,number]; Name?: string }> }): number;
269
+ addIfcSlab(handle: number, storeyId: number, params: BimElementAttrs & { Position: [number,number,number]; Thickness: number; Width?: number; Depth?: number; Profile?: [number,number][]; Openings?: Array<{ Width: number; Height: number; Position: [number,number,number]; Name?: string }> }): number;
116
270
  /** Add a column to a storey. Returns column expressId. */
117
- addIfcColumn(handle: number, storeyId: number, params: { Position: [number,number,number]; Width: number; Depth: number; Height: number; Name?: string }): number;
271
+ addIfcColumn(handle: number, storeyId: number, params: BimElementAttrs & { Position: [number,number,number]; Width: number; Depth: number; Height: number }): number;
118
272
  /** Add a beam to a storey. Returns beam expressId. */
119
- addIfcBeam(handle: number, storeyId: number, params: { Start: [number,number,number]; End: [number,number,number]; Width: number; Height: number; Name?: string }): number;
273
+ addIfcBeam(handle: number, storeyId: number, params: BimElementAttrs & { Start: [number,number,number]; End: [number,number,number]; Width: number; Height: number }): number;
120
274
  /** Add a stair to a storey. Returns stair expressId. */
121
- addIfcStair(handle: number, storeyId: number, params: { Position: [number,number,number]; NumberOfRisers: number; RiserHeight: number; TreadLength: number; Width: number; Direction?: number; Name?: string }): number;
275
+ addIfcStair(handle: number, storeyId: number, params: BimElementAttrs & { Position: [number,number,number]; NumberOfRisers: number; RiserHeight: number; TreadLength: number; Width: number; Direction?: number }): number;
122
276
  /** Add a roof to a storey. Returns roof expressId. */
123
- addIfcRoof(handle: number, storeyId: number, params: { Position: [number,number,number]; Width: number; Depth: number; Thickness: number; Slope?: number; Name?: string }): number;
277
+ addIfcRoof(handle: number, storeyId: number, params: BimElementAttrs & { Position: [number,number,number]; Width: number; Depth: number; Thickness: number; Slope?: number }): number;
278
+ /** Add a dual-pitch gable roof. `Slope` is in radians. Returns roof expressId. */
279
+ addIfcGableRoof(handle: number, storeyId: number, params: BimElementAttrs & { Position: [number,number,number]; Width: number; Depth: number; Thickness: number; Slope: number; Overhang?: number }): number;
280
+ /** Add a door hosted in a wall opening. Position is wall-local [alongWall, 0, baseHeight]. Returns door expressId. */
281
+ addIfcWallDoor(handle: number, wallId: number, params: BimWallHostedOpening & { PredefinedType?: string; OperationType?: string }): number;
282
+ /** Add a window hosted in a wall opening. Position is wall-local [alongWall, 0, sillHeight]. Returns window expressId. */
283
+ addIfcWallWindow(handle: number, wallId: number, params: BimWallHostedOpening & { PartitioningType?: string }): number;
284
+ /** Add a door to a storey. Returns door expressId. */
285
+ addIfcDoor(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Width: number; Height: number; Thickness?: number; PredefinedType?: string; OperationType?: string }): number;
286
+ /** Add a window to a storey. Returns window expressId. */
287
+ addIfcWindow(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Width: number; Height: number; Thickness?: number; PartitioningType?: string }): number;
288
+ /** Add a ramp to a storey. Returns ramp expressId. */
289
+ addIfcRamp(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Width: number; Length: number; Thickness: number; Rise?: number }): number;
290
+ /** Add a railing to a storey. Returns railing expressId. */
291
+ addIfcRailing(handle: number, storeyId: number, params: BimElementAttrs & { Start: BimPoint3D; End: BimPoint3D; Height: number; Width?: number }): number;
292
+ /** Add a plate to a storey. Returns plate expressId. */
293
+ addIfcPlate(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Width: number; Depth: number; Thickness: number; Profile?: BimPoint2D[] }): number;
294
+ /** Add a member to a storey. Returns member expressId. */
295
+ addIfcMember(handle: number, storeyId: number, params: BimElementAttrs & { Start: BimPoint3D; End: BimPoint3D; Width: number; Height: number }): number;
296
+ /** Add a footing to a storey. Returns footing expressId. */
297
+ addIfcFooting(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Width: number; Depth: number; Height: number; PredefinedType?: string }): number;
298
+ /** Add a pile to a storey. Returns pile expressId. */
299
+ addIfcPile(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Length: number; Diameter: number; IsRectangular?: boolean; RectangularDepth?: number }): number;
300
+ /** Add a space to a storey. Returns space expressId. */
301
+ addIfcSpace(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Width: number; Depth: number; Height: number; LongName?: string; Profile?: BimPoint2D[] }): number;
302
+ /** Add a curtain wall to a storey. Returns curtain wall expressId. */
303
+ addIfcCurtainWall(handle: number, storeyId: number, params: BimElementAttrs & { Start: BimPoint3D; End: BimPoint3D; Height: number; Thickness?: number }): number;
304
+ /** Add a furnishing element to a storey. Returns expressId. */
305
+ addIfcFurnishingElement(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Width: number; Depth: number; Height: number; Direction?: number }): number;
306
+ /** Add a generic building element proxy to a storey. Returns expressId. */
307
+ addIfcBuildingElementProxy(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Width: number; Depth: number; Height: number; Profile?: BimPoint2D[]; ProxyType?: string }): number;
308
+ /** Add a circular column to a storey. Returns expressId. */
309
+ addIfcCircularColumn(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Radius: number; Height: number }): number;
310
+ /** Add an I-shape beam to a storey. Returns expressId. */
311
+ addIfcIShapeBeam(handle: number, storeyId: number, params: BimElementAttrs & { Start: BimPoint3D; End: BimPoint3D; OverallWidth: number; OverallDepth: number; WebThickness: number; FlangeThickness: number; FilletRadius?: number }): number;
312
+ /** Add an L-shape member to a storey. Returns expressId. */
313
+ addIfcLShapeMember(handle: number, storeyId: number, params: BimElementAttrs & { Start: BimPoint3D; End: BimPoint3D; Width: number; Depth: number; Thickness: number; FilletRadius?: number }): number;
314
+ /** Add a T-shape member to a storey. Returns expressId. */
315
+ addIfcTShapeMember(handle: number, storeyId: number, params: BimElementAttrs & { Start: BimPoint3D; End: BimPoint3D; FlangeWidth: number; Depth: number; WebThickness: number; FlangeThickness: number; FilletRadius?: number }): number;
316
+ /** Add a U-shape member to a storey. Returns expressId. */
317
+ addIfcUShapeMember(handle: number, storeyId: number, params: BimElementAttrs & { Start: BimPoint3D; End: BimPoint3D; Depth: number; FlangeWidth: number; WebThickness: number; FlangeThickness: number; FilletRadius?: number }): number;
318
+ /** Add a hollow circular column to a storey. Returns expressId. */
319
+ addIfcHollowCircularColumn(handle: number, storeyId: number, params: BimElementAttrs & { Position: BimPoint3D; Radius: number; WallThickness: number; Height: number }): number;
320
+ /** Add a rectangular hollow beam to a storey. Returns expressId. */
321
+ addIfcRectangleHollowBeam(handle: number, storeyId: number, params: BimElementAttrs & { Start: BimPoint3D; End: BimPoint3D; XDim: number; YDim: number; WallThickness: number; InnerFilletRadius?: number; OuterFilletRadius?: number }): number;
322
+ /** Create any IFC type with a profile at a placement. Returns expressId. */
323
+ addElement(handle: number, storeyId: number, params: { IfcType: string; Placement: BimPlacement3D; Profile: BimProfileDef; Depth: number; ExtrusionDirection?: BimPoint3D; PredefinedType?: string; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): number;
324
+ /** Create any IFC type extruded along a Start→End axis. Returns expressId. */
325
+ addAxisElement(handle: number, storeyId: number, params: { IfcType: string; Start: BimPoint3D; End: BimPoint3D; Profile: BimProfileDef; PredefinedType?: string; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): number;
326
+ /** Create a profile from a ProfileDef union. Returns profile ID. */
327
+ createProfile(handle: number, profile: BimProfileDef): number;
328
+ /** Get the world placement ID for use with addLocalPlacement. */
329
+ getWorldPlacementId(handle: number): number;
124
330
  /** Assign a named colour to an element. Call before toIfc(). */
125
331
  setColor(handle: number, elementId: number, name: string, rgb: [number, number, number]): void;
126
332
  /** Assign an IFC material (simple or layered) to an element. */
@@ -132,12 +338,25 @@ declare const bim: {
132
338
  /** Generate the IFC STEP file content. Returns { content, entities, stats }. */
133
339
  toIfc(handle: number): { content: string; entities: Array<{ expressId: number; type: string; Name?: string }>; stats: { entityCount: number; fileSize: number } };
134
340
  };
341
+ /** Uploaded file attachments */
342
+ files: {
343
+ /** List uploaded file attachments available to scripts */
344
+ list(): BimFileAttachment[];
345
+ /** Get raw text content for an uploaded attachment by file name */
346
+ text(name: string): string | null;
347
+ /** Get parsed CSV/TSV rows for an uploaded attachment by file name */
348
+ csv(name: string): Record<string, string>[] | null;
349
+ /** Get parsed CSV column names for an uploaded attachment by file name */
350
+ csvColumns(name: string): string[];
351
+ };
135
352
  /** Data export */
136
353
  export: {
137
354
  /** Export entities to CSV string */
138
355
  csv(entities: BimEntity[], options: { columns: string[]; filename?: string; separator?: string }): string;
139
356
  /** Export entities to JSON array */
140
357
  json(entities: BimEntity[], columns: string[]): Record<string, unknown>[];
358
+ /** Export entities to IFC STEP text. Pass filename to auto-download a valid .ifc file */
359
+ ifc(entities: BimEntity[], options?: { schema?: "IFC2X3" | "IFC4" | "IFC4X3"; filename?: string; includeMutations?: boolean; visibleOnly?: boolean }): string;
141
360
  /** Trigger a browser file download with raw content */
142
361
  download(content: string, filename: string, mimeType?: string): void;
143
362
  };
@@ -249,7 +249,7 @@ bim.create.addIfcElementQuantity(h, stairId, {
249
249
 
250
250
  const ff = bim.create.addIfcBuildingStorey(h, { Name: 'First Floor', Elevation: 3 });
251
251
 
252
- // Floor slab at z = 3 with stair opening
252
+ // Floor slab position is relative to storey elevation (3m)
253
253
  // Opening must be long enough for headroom (≥ 2.1 m) over the ascending stair.
254
254
  // At the slab level the stair is still rising, so the opening needs to extend
255
255
  // back far enough that a person standing on a lower step has full clearance.
@@ -258,7 +258,7 @@ const stairOpenLen = 4.2; // ~1.5 × run ensures headro
258
258
  const stairOpenY = stairRunEnd - stairOpenLen / 2; // center near arrival end
259
259
  const ffSlab = bim.create.addIfcSlab(h, ff, {
260
260
  Name: 'First Floor Slab', Description: 'Reinforced concrete floor slab', ObjectType: 'Floor:Concrete - 300mm',
261
- Position: [0, 0, 2.7], Thickness: 0.3, Width: 5, Depth: 8,
261
+ Position: [0, 0, -0.3], Thickness: 0.3, Width: 5, Depth: 8,
262
262
  Openings: [
263
263
  { Name: 'Stair Opening', Width: stairW + 0.2, Height: stairOpenLen, Position: [stairW / 2, stairOpenY, 0] },
264
264
  ],
@@ -282,31 +282,31 @@ bim.create.addIfcPropertySet(h, ffSlab, {
282
282
  ],
283
283
  });
284
284
 
285
- // Second floor walls — same footprint, window on east instead of door
285
+ // Second floor walls — same footprint, Z=0 relative to storey elevation
286
286
  const ffSouthWall = bim.create.addIfcWall(h, ff, {
287
287
  Name: 'South Wall', Description: 'Exterior south façade', ObjectType: 'Basic Wall:Exterior - 200mm',
288
- Start: [0, 0, 3], End: [5, 0, 3], Thickness: 0.2, Height: 3,
288
+ Start: [0, 0, 0], End: [5, 0, 0], Thickness: 0.2, Height: 3,
289
289
  Openings: [
290
290
  { Name: 'W-03 Window', Width: 1.2, Height: 1.5, Position: [1.5, 0, 0.9] },
291
291
  ],
292
292
  });
293
293
  const ffEastWall = bim.create.addIfcWall(h, ff, {
294
294
  Name: 'East Wall', Description: 'Exterior east façade', ObjectType: 'Basic Wall:Exterior - 200mm',
295
- Start: [5, 0, 3], End: [5, 8, 3], Thickness: 0.2, Height: 3,
295
+ Start: [5, 0, 0], End: [5, 8, 0], Thickness: 0.2, Height: 3,
296
296
  Openings: [
297
297
  { Name: 'W-04 Window', Width: 1.4, Height: 1.5, Position: [3, 0, 0.9] },
298
298
  ],
299
299
  });
300
300
  const ffNorthWall = bim.create.addIfcWall(h, ff, {
301
301
  Name: 'North Wall', Description: 'Exterior north façade', ObjectType: 'Basic Wall:Exterior - 200mm',
302
- Start: [5, 8, 3], End: [0, 8, 3], Thickness: 0.2, Height: 3,
302
+ Start: [5, 8, 0], End: [0, 8, 0], Thickness: 0.2, Height: 3,
303
303
  Openings: [
304
304
  { Name: 'W-05 Window', Width: 1.4, Height: 1.5, Position: [1.8, 0, 0.9] },
305
305
  ],
306
306
  });
307
307
  const ffWestWall = bim.create.addIfcWall(h, ff, {
308
308
  Name: 'West Wall', Description: 'Exterior west façade', ObjectType: 'Basic Wall:Exterior - 200mm',
309
- Start: [0, 8, 3], End: [0, 0, 3], Thickness: 0.2, Height: 3,
309
+ Start: [0, 8, 0], End: [0, 0, 0], Thickness: 0.2, Height: 3,
310
310
  });
311
311
 
312
312
  for (const wId of [ffSouthWall, ffEastWall, ffNorthWall, ffWestWall]) {
@@ -350,21 +350,21 @@ for (const [wId, wName, wLen] of [
350
350
  });
351
351
  }
352
352
 
353
- // Second floor columns
353
+ // Second floor columns — Z=0 relative to storey elevation
354
354
  for (const [cName, cx, cy] of columnPositions) {
355
355
  const colId = bim.create.addIfcColumn(h, ff, {
356
356
  Name: cName, Description: 'Reinforced concrete column', ObjectType: 'Column:Concrete 300x300',
357
- Position: [cx, cy, 3], Width: 0.3, Depth: 0.3, Height: 3,
357
+ Position: [cx, cy, 0], Width: 0.3, Depth: 0.3, Height: 3,
358
358
  });
359
359
  bim.create.setColor(h, colId, 'Concrete - Light', [0.72, 0.72, 0.74]);
360
360
  bim.create.addIfcMaterial(h, colId, { Name: 'Reinforced Concrete C30/37', Category: 'Concrete' });
361
361
  }
362
362
 
363
- // Second floor beams
363
+ // Second floor beams — Z=3 relative to storey elevation (→ world Z=6)
364
364
  for (const [bName, bStart, bEnd] of beamDefs) {
365
365
  const beamId = bim.create.addIfcBeam(h, ff, {
366
366
  Name: bName, Description: 'Steel I-beam', ObjectType: 'Beam:IPE 200',
367
- Start: [bStart[0], bStart[1], 6], End: [bEnd[0], bEnd[1], 6], Width: 0.2, Height: 0.4,
367
+ Start: [bStart[0], bStart[1], 3], End: [bEnd[0], bEnd[1], 3], Width: 0.2, Height: 0.4,
368
368
  });
369
369
  bim.create.setColor(h, beamId, 'Steel - Grey', [0.55, 0.55, 0.58]);
370
370
  bim.create.addIfcMaterial(h, beamId, { Name: 'Structural Steel S235', Category: 'Steel' });
@@ -378,7 +378,7 @@ for (const [bName, bStart, bEnd] of beamDefs) {
378
378
 
379
379
  const ROOF_W = 5; // building width (X)
380
380
  const ROOF_D = 8; // building depth (Y)
381
- const WALL_H = 6; // wall-top elevation (2 storeys × 3 m)
381
+ const WALL_H = 3; // wall-top relative to first floor storey (elevation 3)
382
382
  const CROWN = 1.8; // rise above walls
383
383
  const NU = 10; // grid divisions along X
384
384
  const NV = 16; // grid divisions along Y
package/src/main.tsx CHANGED
@@ -8,11 +8,20 @@
8
8
 
9
9
  import React from 'react';
10
10
  import ReactDOM from 'react-dom/client';
11
+ import { ClerkProvider } from '@clerk/clerk-react';
11
12
  import { App } from './App';
12
13
  import './index.css';
13
14
 
15
+ const clerkPublishableKey = (import.meta.env.VITE_CLERK_PUBLISHABLE_KEY as string | undefined)?.trim();
16
+
14
17
  ReactDOM.createRoot(document.getElementById('root')!).render(
15
18
  <React.StrictMode>
16
- <App />
19
+ {clerkPublishableKey ? (
20
+ <ClerkProvider publishableKey={clerkPublishableKey}>
21
+ <App />
22
+ </ClerkProvider>
23
+ ) : (
24
+ <App />
25
+ )}
17
26
  </React.StrictMode>
18
27
  );
@@ -0,0 +1,24 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+
5
+ import test from 'node:test';
6
+ import assert from 'node:assert/strict';
7
+ import { resolveVisibilityFilterSets } from './export-adapter.js';
8
+ import { LEGACY_MODEL_ID } from './model-compat.js';
9
+
10
+ test('resolveVisibilityFilterSets honors legacy single-model hidden and isolated state', () => {
11
+ const state = {
12
+ models: new Map(),
13
+ hiddenEntities: new Set([11, 12]),
14
+ isolatedEntities: new Set([21, 22]),
15
+ hiddenEntitiesByModel: new Map(),
16
+ isolatedEntitiesByModel: new Map(),
17
+ };
18
+
19
+ const result = resolveVisibilityFilterSets(state as never, LEGACY_MODEL_ID, new Set([1, 2, 3]), 3);
20
+
21
+ assert.equal(result.visibleOnly, false);
22
+ assert.deepEqual([...result.hiddenEntityIds], [11, 12]);
23
+ assert.deepEqual(result.isolatedEntityIds ? [...result.isolatedEntityIds] : null, [21, 22]);
24
+ });