@ifc-lite/viewer 1.17.6 → 1.19.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.
Files changed (156) hide show
  1. package/.turbo/turbo-build.log +20 -15
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +949 -0
  4. package/dist/assets/{basketViewActivator-86rgogji.js → basketViewActivator-RZy5c3Td.js} +1 -1
  5. package/dist/assets/decode-worker-Collf_X_.js +1320 -0
  6. package/dist/assets/{exporters-CcPS9MK5.js → exporters-BraHBeoi.js} +4194 -3025
  7. package/dist/assets/{geometry.worker-BFUYA08u.js → geometry.worker-DQEZB2rB.js} +1 -1
  8. package/dist/assets/ifc-lite_bg-4yUkDRD8.wasm +0 -0
  9. package/dist/assets/index-0XpVr_S5.css +1 -0
  10. package/dist/assets/{index-Bfms9I4A.js → index-BOi3BuUI.js} +46423 -31181
  11. package/dist/assets/index-XwKzDuw6.js +22 -0
  12. package/dist/assets/{native-bridge-DUyLCMZS.js → native-bridge-CpBeOPQa.js} +1 -1
  13. package/dist/assets/sandbox-Baez7n-t.js +9682 -0
  14. package/dist/assets/{server-client-BuZK7OST.js → server-client-BB6cMAXE.js} +1 -1
  15. package/dist/assets/{wasm-bridge-JsqEGDV8.js → wasm-bridge-CAYCUHbE.js} +1 -1
  16. package/dist/index.html +6 -6
  17. package/package.json +11 -10
  18. package/src/apache-arrow.d.ts +30 -0
  19. package/src/components/viewer/AddElementPanel.tsx +758 -0
  20. package/src/components/viewer/BulkPropertyEditor.tsx +7 -0
  21. package/src/components/viewer/ChatPanel.tsx +64 -2
  22. package/src/components/viewer/CommandPalette.tsx +56 -7
  23. package/src/components/viewer/EntityContextMenu.tsx +168 -4
  24. package/src/components/viewer/ExportChangesButton.tsx +25 -5
  25. package/src/components/viewer/ExportDialog.tsx +19 -1
  26. package/src/components/viewer/MainToolbar.tsx +73 -12
  27. package/src/components/viewer/PointCloudPanel.tsx +174 -0
  28. package/src/components/viewer/PropertiesPanel.tsx +222 -22
  29. package/src/components/viewer/SearchInline.tsx +669 -0
  30. package/src/components/viewer/SearchModal.filter.builder.tsx +766 -0
  31. package/src/components/viewer/SearchModal.filter.tsx +514 -0
  32. package/src/components/viewer/SearchModal.text.tsx +388 -0
  33. package/src/components/viewer/SearchModal.tsx +235 -0
  34. package/src/components/viewer/ToolOverlays.tsx +5 -0
  35. package/src/components/viewer/ViewerLayout.tsx +24 -4
  36. package/src/components/viewer/Viewport.tsx +29 -2
  37. package/src/components/viewer/ViewportContainer.tsx +45 -5
  38. package/src/components/viewer/ViewportOverlays.tsx +13 -2
  39. package/src/components/viewer/annotations/AnnotationDropInput.tsx +203 -0
  40. package/src/components/viewer/annotations/AnnotationLayer.tsx +287 -0
  41. package/src/components/viewer/annotations/AnnotationPin.tsx +90 -0
  42. package/src/components/viewer/annotations/AnnotationPopover.tsx +296 -0
  43. package/src/components/viewer/bcf/BCFTopicDetail.tsx +1 -1
  44. package/src/components/viewer/lists/ListPanel.tsx +14 -21
  45. package/src/components/viewer/properties/RawStepCard.tsx +332 -0
  46. package/src/components/viewer/properties/RawStepRow.tsx +261 -0
  47. package/src/components/viewer/properties/ScheduleCard.tsx +224 -0
  48. package/src/components/viewer/properties/TaskEditCard.tsx +510 -0
  49. package/src/components/viewer/properties/raw-step-format.ts +193 -0
  50. package/src/components/viewer/schedule/AnimationSettingsPopover.tsx +542 -0
  51. package/src/components/viewer/schedule/GanttDependencyArrows.tsx +89 -0
  52. package/src/components/viewer/schedule/GanttDragTooltip.tsx +48 -0
  53. package/src/components/viewer/schedule/GanttEmptyState.tsx +97 -0
  54. package/src/components/viewer/schedule/GanttPanel.tsx +295 -0
  55. package/src/components/viewer/schedule/GanttTaskBar.tsx +199 -0
  56. package/src/components/viewer/schedule/GanttTaskTree.tsx +250 -0
  57. package/src/components/viewer/schedule/GanttTimeline.tsx +305 -0
  58. package/src/components/viewer/schedule/GanttToolbar.tsx +406 -0
  59. package/src/components/viewer/schedule/GenerateAdvancedPanel.tsx +147 -0
  60. package/src/components/viewer/schedule/GenerateScheduleDialog.tsx +392 -0
  61. package/src/components/viewer/schedule/HeightStrategyPanel.tsx +120 -0
  62. package/src/components/viewer/schedule/generate-schedule.test.ts +439 -0
  63. package/src/components/viewer/schedule/generate-schedule.ts +648 -0
  64. package/src/components/viewer/schedule/schedule-animator.test.ts +452 -0
  65. package/src/components/viewer/schedule/schedule-animator.ts +488 -0
  66. package/src/components/viewer/schedule/schedule-selection.test.ts +148 -0
  67. package/src/components/viewer/schedule/schedule-selection.ts +163 -0
  68. package/src/components/viewer/schedule/schedule-utils.ts +223 -0
  69. package/src/components/viewer/schedule/useConstructionSequence.ts +156 -0
  70. package/src/components/viewer/schedule/useGanttBarDrag.test.ts +90 -0
  71. package/src/components/viewer/schedule/useGanttBarDrag.ts +305 -0
  72. package/src/components/viewer/schedule/useGanttSelection3DHighlight.ts +152 -0
  73. package/src/components/viewer/schedule/useOverlayCompositor.ts +108 -0
  74. package/src/components/viewer/selectionHandlers.ts +446 -0
  75. package/src/components/viewer/tools/AddElementOverlay.tsx +581 -0
  76. package/src/components/viewer/useDuplicateShortcut.ts +77 -0
  77. package/src/components/viewer/useMouseControls.ts +9 -1
  78. package/src/components/viewer/usePointCloudLifecycle.ts +64 -0
  79. package/src/components/viewer/usePointCloudSync.ts +98 -0
  80. package/src/hooks/ingest/pointCloudIngest.ts +391 -0
  81. package/src/hooks/ingest/viewerModelIngest.ts +32 -3
  82. package/src/hooks/useIfcFederation.ts +72 -3
  83. package/src/hooks/useIfcLoader.ts +89 -13
  84. package/src/hooks/useKeyboardShortcuts.ts +25 -0
  85. package/src/hooks/useSandbox.ts +1 -1
  86. package/src/hooks/useSearchIndex.ts +125 -0
  87. package/src/index.css +66 -0
  88. package/src/lib/llm/system-prompt.test.ts +14 -0
  89. package/src/lib/llm/system-prompt.ts +102 -1
  90. package/src/lib/llm/types.ts +6 -0
  91. package/src/lib/recent-files.ts +38 -4
  92. package/src/lib/scripts/templates/bim-globals.d.ts +136 -114
  93. package/src/lib/scripts/templates/construction-schedule.ts +223 -0
  94. package/src/lib/scripts/templates.ts +7 -0
  95. package/src/lib/search/common-ifc-types.ts +36 -0
  96. package/src/lib/search/filter-evaluate.test.ts +537 -0
  97. package/src/lib/search/filter-evaluate.ts +610 -0
  98. package/src/lib/search/filter-rules.test.ts +119 -0
  99. package/src/lib/search/filter-rules.ts +198 -0
  100. package/src/lib/search/filter-schema.test.ts +233 -0
  101. package/src/lib/search/filter-schema.ts +146 -0
  102. package/src/lib/search/recent-searches.test.ts +116 -0
  103. package/src/lib/search/recent-searches.ts +93 -0
  104. package/src/lib/search/result-export.test.ts +101 -0
  105. package/src/lib/search/result-export.ts +104 -0
  106. package/src/lib/search/saved-filters.test.ts +118 -0
  107. package/src/lib/search/saved-filters.ts +154 -0
  108. package/src/lib/search/tier0-scan.test.ts +196 -0
  109. package/src/lib/search/tier0-scan.ts +237 -0
  110. package/src/lib/search/tier1-index.test.ts +242 -0
  111. package/src/lib/search/tier1-index.ts +448 -0
  112. package/src/sdk/adapters/export-adapter.test.ts +434 -1
  113. package/src/sdk/adapters/export-adapter.ts +404 -1
  114. package/src/sdk/adapters/export-schedule-splice.test.ts +127 -0
  115. package/src/sdk/adapters/export-schedule-splice.ts +87 -0
  116. package/src/sdk/adapters/model-compat.ts +8 -2
  117. package/src/sdk/adapters/schedule-adapter.ts +73 -0
  118. package/src/sdk/adapters/store-adapter.ts +201 -0
  119. package/src/sdk/adapters/visibility-adapter.ts +3 -0
  120. package/src/sdk/local-backend.ts +16 -8
  121. package/src/services/desktop-export.ts +3 -1
  122. package/src/services/desktop-native-metadata.ts +41 -18
  123. package/src/services/file-dialog.ts +8 -3
  124. package/src/services/tauri-modules.d.ts +25 -0
  125. package/src/store/basketVisibleSet.ts +3 -0
  126. package/src/store/globalId.ts +4 -1
  127. package/src/store/index.ts +79 -1
  128. package/src/store/slices/addElementMeshes.ts +365 -0
  129. package/src/store/slices/addElementSlice.ts +275 -0
  130. package/src/store/slices/annotationsSlice.test.ts +133 -0
  131. package/src/store/slices/annotationsSlice.ts +251 -0
  132. package/src/store/slices/dataSlice.test.ts +23 -4
  133. package/src/store/slices/dataSlice.ts +1 -1
  134. package/src/store/slices/modelSlice.test.ts +67 -9
  135. package/src/store/slices/modelSlice.ts +39 -7
  136. package/src/store/slices/mutationSlice.ts +964 -3
  137. package/src/store/slices/overlayCompositor.test.ts +164 -0
  138. package/src/store/slices/overlaySlice.test.ts +93 -0
  139. package/src/store/slices/overlaySlice.ts +151 -0
  140. package/src/store/slices/pinboardSlice.test.ts +6 -1
  141. package/src/store/slices/playbackSlice.ts +128 -0
  142. package/src/store/slices/pointCloudSlice.ts +102 -0
  143. package/src/store/slices/schedule-edit-helpers.test.ts +97 -0
  144. package/src/store/slices/schedule-edit-helpers.ts +179 -0
  145. package/src/store/slices/scheduleSlice.test.ts +694 -0
  146. package/src/store/slices/scheduleSlice.ts +1330 -0
  147. package/src/store/slices/searchSlice.test.ts +342 -0
  148. package/src/store/slices/searchSlice.ts +341 -0
  149. package/src/store/slices/selectionSlice.test.ts +46 -0
  150. package/src/store/slices/selectionSlice.ts +20 -0
  151. package/src/store/types.ts +7 -0
  152. package/src/store.ts +14 -0
  153. package/vite.config.ts +1 -0
  154. package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
  155. package/dist/assets/index-_bfZsDCC.css +0 -1
  156. package/dist/assets/sandbox-C8575tul.js +0 -5951
@@ -100,6 +100,69 @@ function buildIntentMethodSection(intent: LlmTaskIntent): string {
100
100
  return lines.join('\n');
101
101
  }
102
102
 
103
+ function buildStoreCheatSheet(): string {
104
+ const storeNamespace = NAMESPACE_SCHEMAS.find((schema) => schema.name === 'store');
105
+ if (!storeNamespace) return '';
106
+
107
+ return [
108
+ '## BIM.STORE CHEAT SHEET',
109
+ '`bim.store.*` edits a parsed model in place — use it when the user already has',
110
+ 'a model loaded and wants raw STEP-level edits, NOT when building a new model from',
111
+ 'scratch (that\'s `bim.create`).',
112
+ '',
113
+ '- `addEntity(modelId, { type, attributes })` — inject a STEP entity. `attributes`',
114
+ ' follows EntityExtractor output: numbers → REAL/integer, `"#42"` → ref, `".AREA."` → enum,',
115
+ ' `null` → `$`, arrays → STEP list. Returns `{ modelId, expressId }`.',
116
+ '- `removeEntity(entity)` — tombstones existing source entities or forgets overlay-only ones.',
117
+ '- `setPositionalAttribute(entity, index, value)` — edit a non-IfcRoot attribute by',
118
+ ' zero-based STEP argument index. Use this for `IfcRectangleProfileDef.XDim` (index 3),',
119
+ ' `YDim` (index 4), `IfcCartesianPoint.Coordinates` (index 0), etc. Use `bim.mutate.setAttribute`',
120
+ ' for IfcRoot attributes (Name, Description, ObjectType, Tag).',
121
+ '- High-level builders anchor a new element to an existing IfcBuildingStorey:',
122
+ ' `addColumn(modelId, storeyId, { Position, Width, Depth, Height })`',
123
+ ' `addWall(modelId, storeyId, { Start, End, Thickness, Height })`',
124
+ ' `addBeam(modelId, storeyId, { Start, End, Width, Height })`',
125
+ ' `addSlab(modelId, storeyId, { Position, Width, Depth, Thickness })` // rectangle',
126
+ ' `addSlab(modelId, storeyId, { Profile: "polygon", OuterCurve: [[x,y],…], Thickness })`',
127
+ ' `addRoof(modelId, storeyId, { … same shape as addSlab — emits .FLAT_ROOF. })`',
128
+ ' `addPlate(modelId, storeyId, { … same shape as addSlab — IfcPlate, PredefinedType? })`',
129
+ ' `addSpace(modelId, storeyId, { Position, Width, Depth, Height, LongName? })` // room rectangle',
130
+ ' `addSpace(modelId, storeyId, { Profile: "polygon", OuterCurve, Height })` // room polygon',
131
+ ' `addDoor(modelId, storeyId, { Position, Width, Height, FrameThickness?, OperationType? })`',
132
+ ' `addWindow(modelId, storeyId, { Position, Width, Height, FrameThickness?, PartitioningType? })`',
133
+ ' `addMember(modelId, storeyId, { Start, End, Width, Height, PredefinedType? })` // brace/post/strut',
134
+ ' Each emits ~12 STEP entities (placement chain → profile → solid → representation +',
135
+ ' IfcRelContainedInSpatialStructure, except `addSpace` which uses IfcRelAggregates).',
136
+ ' Coords are storey-local metres. Polygon outlines need ≥3 points; the polyline is auto-closed.',
137
+ '- Edits accumulate in an overlay; they show up after `bim.export.ifc(bim.query.all())`',
138
+ ' or when the viewer next renders. Use `bim.mutate.undo(modelId)` to roll back.',
139
+ '',
140
+ 'Canonical examples:',
141
+ '```js',
142
+ '// Resize a rectangular profile from 0.3×0.4 to 0.6×0.4',
143
+ 'const profile = bim.query.byId("arch", 35);',
144
+ 'bim.store.setPositionalAttribute(profile, 3, 0.6); // XDim',
145
+ '',
146
+ '// Drop a wall on the first storey',
147
+ 'const storeyId = bim.query.byType("IfcBuildingStorey")[0].ref.expressId;',
148
+ 'bim.store.addWall("arch", storeyId, {',
149
+ ' Start: [0, 0, 0], End: [5, 0, 0],',
150
+ ' Thickness: 0.2, Height: 3, Name: "North Wall",',
151
+ '});',
152
+ '',
153
+ '// Add a custom IfcCartesianPoint, then reference it from another entity',
154
+ 'const pt = bim.store.addEntity("arch", {',
155
+ ' type: "IfcCartesianPoint",',
156
+ ' attributes: [[1.0, 2.0, 0.0]],',
157
+ '});',
158
+ 'console.log("Allocated", pt.expressId);',
159
+ '',
160
+ '// Drop an entity entirely',
161
+ 'bim.store.removeEntity(unwantedRef);',
162
+ '```',
163
+ ].join('\n');
164
+ }
165
+
103
166
  function buildCreateContractCheatSheet(): string {
104
167
  const createNamespace = NAMESPACE_SCHEMAS.find((schema) => schema.name === 'create');
105
168
  if (!createNamespace) return '## BIM.CREATE CONTRACT CHEAT SHEET';
@@ -280,6 +343,7 @@ export function buildSystemPrompt(
280
343
  const intent = inferPromptIntent(task);
281
344
  const intentSection = buildIntentMethodSection(intent);
282
345
  const createContractCheatSheet = buildCreateContractCheatSheet();
346
+ const storeCheatSheet = buildStoreCheatSheet();
283
347
  const placementSemantics = buildPlacementSemanticsSection();
284
348
  const inspectionGuidance = buildInspectionGuidance();
285
349
 
@@ -326,9 +390,11 @@ ${intentSection}
326
390
  4. Keep scripts concise — avoid unnecessary abstractions
327
391
  5. Coordinates are in meters. Z is up. Do NOT assume every create method is storey-relative — use the method-specific placement rules below.
328
392
  6. For create or explicit rewrite turns, wrap runnable code in a \`\`\`js\`\`\` fence. For repair turns, return exactly one \`\`\`ifc-script-edits\`\`\` fence containing SEARCH/REPLACE blocks and no \`\`\`js\`\`\` fence.
329
- 7. If the user asks to modify existing data, use \`bim.mutate\` or \`bim.query\` — NOT \`bim.create\`
393
+ 7. If the user asks to modify existing data, use \`bim.mutate\`, \`bim.store\`, or \`bim.query\` — NOT \`bim.create\`
330
394
  - Use \`bim.mutate.setAttribute(entity, "Description", "...")\` for root IFC attributes like \`Name\`, \`Description\`, \`ObjectType\`, or \`Tag\`
331
395
  - Use \`bim.mutate.setProperty(entity, "Pset_Name", "PropName", value)\` only for IfcPropertySet or quantity data
396
+ - Use \`bim.store.setPositionalAttribute(entity, index, value)\` for positional STEP-argument edits (profile dimensions, \`IfcCartesianPoint.Coordinates\`, and other index-addressed attributes — even when they have a symbolic name) — see BIM.STORE CHEAT SHEET
397
+ - Use \`bim.store.addEntity\` / \`bim.store.removeEntity\` to inject or drop raw STEP entities in an already-loaded model. Do NOT use \`bim.create\` for these — \`bim.create\` builds a fresh project
332
398
  - Distinguish occurrence vs type edits: occurrence/entity-specific changes belong on the occurrence; shared defaults and inherited type properties belong on the related \`Ifc...Type\` entity
333
399
  - If CURRENT MODEL STATE marks a selection as \`kind=type\`, treat it as a type object and avoid describing it as one physical placed occurrence
334
400
  - When an occurrence is selected, inspect \`bim.query.typeProperties(entity)\` before editing inherited values; mutate the type entity when the intent is to change all occurrences that share that type
@@ -353,6 +419,8 @@ ${intentSection}
353
419
 
354
420
  ${createContractCheatSheet}
355
421
 
422
+ ${storeCheatSheet}
423
+
356
424
  ${placementSemantics}
357
425
  - \`addIfcDoor\` and \`addIfcWindow\` do not infer host-wall orientation. If you place them next to angled walls, they will stay world-aligned unless you build the wall void another way.
358
426
  - For storey-relative methods, \`Z=0\` usually means floor level of that storey.
@@ -393,6 +461,39 @@ for (let i = 0; i < storeyCount; i++) {
393
461
  - If repeated elements appear only at one level, you probably reused one storey reference instead of iterating over the intended storeys.
394
462
  - If repeated world-placement elements stack at the base level, first check whether their Z coordinates include the current storey elevation.
395
463
 
464
+ ## SCHEDULING / 4D (IfcTask, IfcWorkSchedule, IfcRelSequence)
465
+ - ifc-lite ships a Gantt panel in the lower workspace that plays a construction-sequence animation driven by IfcTask dates and the products each task controls.
466
+ - Creating a schedule from scratch:
467
+ \`\`\`js
468
+ const h = bim.create.project({ Name: "Demo" });
469
+ const storey = bim.create.addIfcBuildingStorey(h, { Name: "Ground", Elevation: 0 });
470
+ const wallA = bim.create.addIfcWall(h, storey, { Start: [0,0,0], End: [5,0,0], Thickness: 0.2, Height: 3 });
471
+
472
+ const schedule = bim.create.addIfcWorkSchedule(h, {
473
+ Name: "Construction schedule",
474
+ StartTime: "2024-05-01T08:00:00",
475
+ FinishTime: "2024-06-30T17:00:00",
476
+ PredefinedType: "PLANNED",
477
+ });
478
+ const task = bim.create.addIfcTask(h, {
479
+ Name: "Install wall A",
480
+ PredefinedType: "INSTALLATION",
481
+ ScheduleStart: "2024-05-06T08:00:00",
482
+ ScheduleFinish: "2024-05-10T17:00:00",
483
+ ScheduleDuration: "P5D",
484
+ });
485
+ bim.create.assignTasksToWorkSchedule(h, schedule, [task]);
486
+ bim.create.assignProductsToTask(h, task, [wallA]); // products reveal in the 4D animation
487
+ // bim.create.addIfcRelSequence(h, prevTask, task, { SequenceType: "FINISH_START", TimeLag: "P2D" });
488
+ // bim.create.nestTasks(h, summaryTask, [task]); // WBS hierarchy
489
+ \`\`\`
490
+ - Dates are ISO 8601 (\`2024-05-01T08:00:00\`). Durations are ISO 8601 (\`P5D\`, \`PT8H\`).
491
+ - IfcTask.PredefinedType is an enum — prefer CONSTRUCTION, INSTALLATION, DEMOLITION, RENOVATION over free strings.
492
+ - For milestones (e.g. "handover"), set \`IsMilestone: true\` and omit or equate start/finish.
493
+ - \`assignProductsToTask\` is the bridge that lets the 4D Gantt animation reveal/hide elements as time advances. Always bind tasks to the elements they construct when the user wants the viewport to animate.
494
+ - Reading an existing schedule: \`bim.schedule.data()\` returns { workSchedules, tasks, sequences }. Use it to inspect or validate a construction plan.
495
+ - **CSV / Excel / PDF → schedule workflow:** when the user attaches a spreadsheet or PDF with activities and dates, parse it with \`bim.files.csv(name)\` (for CSV) or \`bim.files.text(name)\` (for text-extracted PDF/Excel content converted upstream). Map each row to an \`addIfcTask(...)\` call and resolve the \`products\` column — an IFC type like \`IfcWall\` expands to every matching entity, a globalId maps to one specific entity — into \`expressId\`s to feed \`assignProductsToTask\`. The \`Construction schedule (4D)\` script template is a ready-made starting point.
496
+
396
497
  ## API REFERENCE
397
498
  ${apiRef}
398
499
 
@@ -124,6 +124,12 @@ export interface FileAttachment {
124
124
  imageBase64?: string;
125
125
  /** Whether this is an image attachment */
126
126
  isImage?: boolean;
127
+ /** Base64-encoded PDF data (for PDF attachments — Anthropic native document blocks) */
128
+ pdfBase64?: string;
129
+ /** Whether this is a PDF attachment */
130
+ isPdf?: boolean;
131
+ /** Whether this is a binary spreadsheet (xlsx/xls/ods) that we can't parse here yet. */
132
+ isSpreadsheetBinary?: boolean;
127
133
  }
128
134
 
129
135
  export type ModelTier = 'free' | 'byok';
@@ -24,8 +24,21 @@ export interface RecentFileEntry {
24
24
  name: string;
25
25
  size: number;
26
26
  timestamp: number;
27
+ /** Native filesystem path (Tauri only) — enables direct re-open from disk. */
28
+ path?: string;
29
+ /** Last-modified epoch in ms when known (Tauri stat). */
30
+ modifiedMs?: number | null;
27
31
  }
28
32
 
33
+ // Input shape for `recordRecentFiles` — accepts the optional native fields
34
+ // so callers can persist a path / modifiedMs without lying about the type.
35
+ export type RecentFileInput = {
36
+ name: string;
37
+ size: number;
38
+ path?: string;
39
+ modifiedMs?: number | null;
40
+ };
41
+
29
42
  // ── localStorage (metadata) ─────────────────────────────────────────────
30
43
 
31
44
  export function getRecentFiles(): RecentFileEntry[] {
@@ -33,17 +46,30 @@ export function getRecentFiles(): RecentFileEntry[] {
33
46
  catch { return []; }
34
47
  }
35
48
 
36
- export function recordRecentFiles(files: { name: string; size: number }[]) {
49
+ // Path-aware dedup key: when a native filesystem path is available it
50
+ // uniquely identifies the file (so `A/model.ifc` and `B/model.ifc` are
51
+ // kept separate); otherwise fall back to the name (browser uploads
52
+ // don't expose paths).
53
+ function recentKey(f: { name: string; path?: string }): string {
54
+ return f.path ? `path:${f.path}` : `name:${f.name}`;
55
+ }
56
+
57
+ export function recordRecentFiles(files: RecentFileInput[]) {
37
58
  try {
38
- const names = new Set(files.map(f => f.name));
39
- const existing = getRecentFiles().filter(f => !names.has(f.name));
59
+ const incomingKeys = new Set(files.map(recentKey));
60
+ const existing = getRecentFiles().filter(f => !incomingKeys.has(recentKey(f)));
40
61
  const entries: RecentFileEntry[] = files.map(f => ({
41
62
  name: f.name,
42
63
  size: f.size,
43
64
  timestamp: Date.now(),
65
+ path: f.path,
66
+ modifiedMs: f.modifiedMs ?? null,
44
67
  }));
45
68
  localStorage.setItem(KEY, JSON.stringify([...entries, ...existing].slice(0, 10)));
46
- } catch { /* noop */ }
69
+ } catch (err) {
70
+ // eslint-disable-next-line no-console
71
+ console.warn('[recent-files] failed to persist recent files metadata', err);
72
+ }
47
73
  }
48
74
 
49
75
  /** Format bytes into human-readable size */
@@ -104,6 +130,14 @@ export async function cacheFileBlobs(files: File[]): Promise<void> {
104
130
 
105
131
  /** Retrieve a cached file blob and reconstruct a File object. */
106
132
  export async function getCachedFile(target: string | RecentFileEntry): Promise<File | null> {
133
+ // Path-bearing entries (Tauri filesystem) are uniquely keyed by path
134
+ // in the recents list, but the IndexedDB cache is name-keyed. A
135
+ // name-only hit could resolve `A/model.ifc` to the cached blob from
136
+ // `B/model.ifc`, opening the wrong file silently. Defer to the
137
+ // caller's native re-open path instead.
138
+ if (typeof target !== 'string' && target.path) {
139
+ return null;
140
+ }
107
141
  const name = typeof target === 'string' ? target : target.name;
108
142
  try {
109
143
  const db = await openDB();
@@ -23,18 +23,12 @@ interface BimEntity {
23
23
 
24
24
  interface BimPropertySet {
25
25
  name: string;
26
- Name?: string;
27
- globalId?: string;
28
- GlobalId?: string;
29
26
  properties: Array<{ name: string; value: string | number | boolean | null }>;
30
- Properties?: Array<{ name: string; value: string | number | boolean | null }>;
31
27
  }
32
28
 
33
29
  interface BimQuantitySet {
34
30
  name: string;
35
31
  quantities: Array<{ name: string; value: number | null }>;
36
- Name?: string;
37
- Quantities?: Array<{ name: string; value: number | null }>;
38
32
  }
39
33
 
40
34
  interface BimAttribute {
@@ -123,40 +117,6 @@ interface BimFileAttachment {
123
117
  hasTextContent: boolean;
124
118
  }
125
119
 
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
-
160
120
  // ── Namespace declarations ──────────────────────────────────────────────
161
121
 
162
122
  declare const bim: {
@@ -169,7 +129,7 @@ declare const bim: {
169
129
  /** Get active model ID */
170
130
  activeId(): string | null;
171
131
  /** Load IFC content into the 3D viewer for preview */
172
- loadIfc(content: string, filename?: string): void;
132
+ loadIfc(content: string, filename: string): void;
173
133
  };
174
134
  /** Query entities */
175
135
  query: {
@@ -241,7 +201,7 @@ declare const bim: {
241
201
  };
242
202
  /** Property editing */
243
203
  mutate: {
244
- /** Set an IfcPropertySet or quantity value */
204
+ /** Set an IfcPropertySet or quantity value (not a root IFC attribute) */
245
205
  setProperty(entity: unknown, psetName: string, propName: string, value: unknown): void;
246
206
  /** Set a root IFC attribute such as Name, Description, ObjectType, or Tag */
247
207
  setAttribute(entity: unknown, attrName: string, value: string): void;
@@ -252,6 +212,35 @@ declare const bim: {
252
212
  /** Redo undone mutation */
253
213
  redo(modelId: string): void;
254
214
  };
215
+ /** Document-level edits — add, remove, and edit positional STEP arguments on entities of a parsed model */
216
+ store: {
217
+ /** Inject a new entity into the active model. Returns an EntityRef for the freshly-allocated expressId. */
218
+ addEntity(modelId: string, def: { type: string; attributes: unknown[] }): { modelId: string; expressId: number };
219
+ /** Remove an entity. Tombstones existing entities; forgets overlay-only ones. Returns false if the id is unknown. */
220
+ removeEntity(entity: { modelId: string; expressId: number }): boolean;
221
+ /** Edit a non-IfcRoot attribute by zero-based STEP argument index (e.g. IfcRectangleProfileDef.XDim is index 3). */
222
+ setPositionalAttribute(entity: { modelId: string; expressId: number }, index: number, value: unknown): void;
223
+ /** Add an IfcColumn to a parsed model anchored to an existing IfcBuildingStorey. Returns the new column entity ref. */
224
+ addColumn(modelId: string, storeyExpressId: number, params: { Position: [number, number, number]; Width: number; Depth: number; Height: number; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
225
+ /** Add an IfcWall from Start to End anchored to an IfcBuildingStorey. Returns the new wall entity ref. */
226
+ addWall(modelId: string, storeyExpressId: number, params: { Start: [number, number, number]; End: [number, number, number]; Thickness: number; Height: number; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
227
+ /** Add an IfcSlab anchored to an IfcBuildingStorey. Two modes: rectangle (Position + Width + Depth) or polygon (OuterCurve = Array<[x, y]> with ≥3 points). */
228
+ addSlab(modelId: string, storeyExpressId: number, params: { Position: [number, number, number]; Width: number; Depth: number; Thickness: number; Profile?: "rectangle"; Name?: string; Description?: string; ObjectType?: string; Tag?: string } | { Profile: "polygon"; OuterCurve: Array<[number, number]>; Position?: [number, number, number]; Thickness: number; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
229
+ /** Add an IfcBeam from Start to End with a centred rectangular cross-section. */
230
+ addBeam(modelId: string, storeyExpressId: number, params: { Start: [number, number, number]; End: [number, number, number]; Width: number; Height: number; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
231
+ /** Add a free-standing IfcDoor anchored to an IfcBuildingStorey. */
232
+ addDoor(modelId: string, storeyExpressId: number, params: { Position: [number, number, number]; Width: number; Height: number; FrameThickness?: number; PredefinedType?: string; OperationType?: string; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
233
+ /** Add a free-standing IfcWindow anchored to an IfcBuildingStorey. */
234
+ addWindow(modelId: string, storeyExpressId: number, params: { Position: [number, number, number]; Width: number; Height: number; FrameThickness?: number; PredefinedType?: string; PartitioningType?: string; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
235
+ /** Add an IfcSpace (room) — rectangle or polygon footprint extruded by Height. */
236
+ addSpace(modelId: string, storeyExpressId: number, params: { Position: [number, number, number]; Width: number; Depth: number; Height: number; Profile?: "rectangle"; Name?: string; LongName?: string; Description?: string; ObjectType?: string } | { Profile: "polygon"; OuterCurve: Array<[number, number]>; Position?: [number, number, number]; Height: number; Name?: string; LongName?: string; Description?: string; ObjectType?: string }): { modelId: string; expressId: number };
237
+ /** Add an IfcRoof (flat-roof slab variant). Two modes: rectangle or polygon. */
238
+ addRoof(modelId: string, storeyExpressId: number, params: { Position: [number, number, number]; Width: number; Depth: number; Thickness: number; Profile?: "rectangle"; Name?: string; Description?: string; ObjectType?: string; Tag?: string } | { Profile: "polygon"; OuterCurve: Array<[number, number]>; Position?: [number, number, number]; Thickness: number; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
239
+ /** Add an IfcPlate (thin flat element). Two modes: rectangle or polygon. */
240
+ addPlate(modelId: string, storeyExpressId: number, params: { Position: [number, number, number]; Width: number; Depth: number; Thickness: number; Profile?: "rectangle"; PredefinedType?: string; Name?: string; Description?: string; ObjectType?: string; Tag?: string } | { Profile: "polygon"; OuterCurve: Array<[number, number]>; Position?: [number, number, number]; Thickness: number; PredefinedType?: string; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
241
+ /** Add an IfcMember (generic structural member — brace, post, strut) from Start to End. */
242
+ addMember(modelId: string, storeyExpressId: number, params: { Start: [number, number, number]; End: [number, number, number]; Width: number; Height: number; PredefinedType?: string; Name?: string; Description?: string; ObjectType?: string; Tag?: string }): { modelId: string; expressId: number };
243
+ };
255
244
  /** Lens visualization */
256
245
  lens: {
257
246
  /** Get built-in lens presets */
@@ -260,83 +249,105 @@ declare const bim: {
260
249
  /** IFC creation from scratch */
261
250
  create: {
262
251
  /** Create a new IFC project. Returns a creator handle (number). */
263
- project(params?: { Name?: string; Description?: string; Schema?: string; LengthUnit?: string; Author?: string; Organization?: string }): number;
252
+ project(params: { Name?: string; Description?: string; Schema?: string; LengthUnit?: string; Author?: string; Organization?: string }): number;
253
+ /** Generate the IFC STEP file content. Returns { content, entities, stats }. */
254
+ toIfc(handle: number): { content: string; entities: Array<{ expressId: number; type: string; Name?: string }>; stats: { entityCount: number; fileSize: number } };
255
+ /** Assign a named colour to an element. Call before toIfc(). */
256
+ setColor(handle: number, elementId: number, name: string, rgb: unknown): void;
257
+ /** Create an IfcWorkSchedule. Returns schedule expressId. */
258
+ addIfcWorkSchedule(handle: number, params: { Name: string; StartTime: string; FinishTime?: string; CreationDate?: string; Description?: string; Identification?: string; Purpose?: string; Duration?: string; TotalFloat?: string; PredefinedType?: 'ACTUAL' | 'BASELINE' | 'PLANNED' | 'USERDEFINED' | 'NOTDEFINED' }): number;
259
+ /** Create an IfcWorkPlan (groups multiple schedules). Returns plan expressId. */
260
+ addIfcWorkPlan(handle: number, params: { Name: string; StartTime: string; FinishTime?: string; CreationDate?: string; Description?: string; Identification?: string; Purpose?: string; Duration?: string; PredefinedType?: 'ACTUAL' | 'BASELINE' | 'PLANNED' | 'USERDEFINED' | 'NOTDEFINED' }): number;
261
+ /** Create an IfcTask. Provide ScheduleStart + ScheduleFinish (or ScheduleDuration) for time fields. Returns task expressId. */
262
+ addIfcTask(handle: number, params: { Name: string; Description?: string; Identification?: string; LongDescription?: string; Status?: string; WorkMethod?: string; IsMilestone?: boolean; Priority?: number; ObjectType?: string; ScheduleStart?: string; ScheduleFinish?: string; ScheduleDuration?: string; ActualStart?: string; ActualFinish?: string; ActualDuration?: string; EarlyStart?: string; EarlyFinish?: string; LateStart?: string; LateFinish?: string; FreeFloat?: string; TotalFloat?: string; IsCritical?: boolean; DurationType?: 'WORKTIME' | 'ELAPSEDTIME' | 'NOTDEFINED'; Completion?: number; PredefinedType?: 'ATTENDANCE' | 'CONSTRUCTION' | 'DEMOLITION' | 'DISMANTLE' | 'DISPOSAL' | 'INSTALLATION' | 'LOGISTIC' | 'MAINTENANCE' | 'MOVE' | 'OPERATION' | 'REMOVAL' | 'RENOVATION' | 'USERDEFINED' | 'NOTDEFINED' | 'ADJUSTMENT' | 'CALIBRATION' | 'EMERGENCY' | 'INSPECTION' | 'SAFETY' | 'SHUTDOWN' | 'STARTUP' | 'TESTING' | 'TROUBLESHOOTING' }): number;
263
+ /** Link predecessor → successor tasks via IfcRelSequence. Returns relationship expressId. */
264
+ addIfcRelSequence(handle: number, predecessorTaskId: number, successorTaskId: number, params: { SequenceType?: 'START_START' | 'START_FINISH' | 'FINISH_START' | 'FINISH_FINISH' | 'USERDEFINED' | 'NOTDEFINED'; TimeLag?: string; LagDurationType?: 'WORKTIME' | 'ELAPSEDTIME' | 'NOTDEFINED'; UserDefinedSequenceType?: string }): number;
265
+ /** Canonical IfcRelAssignsToControl — bind IfcObjectDefinitions (tasks or sub-schedules) to an IfcControl (IfcWorkSchedule/IfcWorkPlan). Returns relationship expressId. */
266
+ addIfcRelAssignsToControl(handle: number, relatingControlId: number, relatedObjectIds: number[]): number;
267
+ /** Canonical IfcRelAssignsToProcess — bind products to an IfcProcess (task). Drives the 4D Gantt animation. Returns relationship expressId. */
268
+ addIfcRelAssignsToProcess(handle: number, relatingProcessId: number, relatedObjectIds: number[]): number;
269
+ /** Canonical IfcRelNests — nest child objects under a parent (task WBS hierarchy). Returns relationship expressId. */
270
+ addIfcRelNests(handle: number, relatingObjectId: number, relatedObjectIds: number[]): number;
271
+ /** Ergonomic alias for addIfcRelAssignsToControl — assign tasks to a work schedule. Returns relationship expressId. */
272
+ assignTasksToWorkSchedule(handle: number, scheduleId: number, taskIds: number[]): number;
273
+ /** Ergonomic alias for addIfcRelAssignsToControl — attach work schedules to a parent IfcWorkPlan. Returns relationship expressId. */
274
+ assignSchedulesToWorkPlan(handle: number, planId: number, scheduleIds: number[]): number;
275
+ /** Ergonomic alias for addIfcRelAssignsToProcess — bind products to a task. Returns relationship expressId. */
276
+ assignProductsToTask(handle: number, taskId: number, productIds: number[]): number;
277
+ /** Ergonomic alias for addIfcRelNests — nest child tasks under a summary parent. Returns relationship expressId. */
278
+ nestTasks(handle: number, parentTaskId: number, childTaskIds: number[]): number;
279
+ /** Create ANY IFC type extruded along a Start→End axis. Returns expressId. */
280
+ addAxisElement(handle: number, storeyId: number, params: unknown): number;
281
+ /** Create ANY IFC type with a profile at a placement. Returns expressId. */
282
+ addElement(handle: number, storeyId: number, params: unknown): number;
283
+ /** Add IfcBeam. Returns expressId. */
284
+ addIfcBeam(handle: number, storeyId: number, params: unknown): number;
285
+ /** Add IfcBuildingElementProxy. Returns expressId. */
286
+ addIfcBuildingElementProxy(handle: number, storeyId: number, params: unknown): number;
264
287
  /** Add a building storey. Returns storey expressId. */
265
- addIfcBuildingStorey(handle: number, params: { Name?: string; Description?: string; Elevation: number }): number;
266
- /** Add a wall to a storey. Returns wall expressId. */
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;
268
- /** Add a slab to a storey. Returns slab expressId. */
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;
270
- /** Add a column to a storey. Returns column expressId. */
271
- addIfcColumn(handle: number, storeyId: number, params: BimElementAttrs & { Position: [number,number,number]; Width: number; Depth: number; Height: number }): number;
272
- /** Add a beam to a storey. Returns beam expressId. */
273
- addIfcBeam(handle: number, storeyId: number, params: BimElementAttrs & { Start: [number,number,number]; End: [number,number,number]; Width: number; Height: number }): number;
274
- /** Add a stair to a storey. Returns stair expressId. */
275
- addIfcStair(handle: number, storeyId: number, params: BimElementAttrs & { Position: [number,number,number]; NumberOfRisers: number; RiserHeight: number; TreadLength: number; Width: number; Direction?: number }): number;
276
- /** Add a roof to a storey. Returns roof expressId. */
277
- addIfcRoof(handle: number, storeyId: number, params: BimElementAttrs & { Position: [number,number,number]; Width: number; Depth: number; Thickness: number; Slope?: number }): number;
288
+ addIfcBuildingStorey(handle: number, params: unknown): number;
289
+ /** Add IfcCircularColumn. Returns expressId. */
290
+ addIfcCircularColumn(handle: number, storeyId: number, params: unknown): number;
291
+ /** Add IfcColumn. Returns expressId. */
292
+ addIfcColumn(handle: number, storeyId: number, params: unknown): number;
293
+ /** Add IfcCurtainWall. Returns expressId. */
294
+ addIfcCurtainWall(handle: number, storeyId: number, params: unknown): number;
295
+ /** Add IfcDoor. Returns expressId. */
296
+ addIfcDoor(handle: number, storeyId: number, params: unknown): number;
297
+ /** Add IfcElementQuantity. Returns expressId. */
298
+ addIfcElementQuantity(handle: number, elementId: number, params: unknown): number;
299
+ /** Add IfcFooting. Returns expressId. */
300
+ addIfcFooting(handle: number, storeyId: number, params: unknown): number;
301
+ /** Add IfcFurnishingElement. Returns expressId. */
302
+ addIfcFurnishingElement(handle: number, storeyId: number, params: unknown): number;
278
303
  /** 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;
304
+ addIfcGableRoof(handle: number, storeyId: number, params: unknown): number;
305
+ /** Add IfcHollowCircularColumn. Returns expressId. */
306
+ addIfcHollowCircularColumn(handle: number, storeyId: number, params: unknown): number;
307
+ /** Add IfcIShapeBeam. Returns expressId. */
308
+ addIfcIShapeBeam(handle: number, storeyId: number, params: unknown): number;
309
+ /** Add IfcLShapeMember. Returns expressId. */
310
+ addIfcLShapeMember(handle: number, storeyId: number, params: unknown): number;
311
+ /** Associate a material with an element via IfcRelAssociatesMaterial (deferred to toIfc). Returns nothing. */
312
+ addIfcMaterial(handle: number, elementId: number, params: unknown): void;
313
+ /** Add IfcMember. Returns expressId. */
314
+ addIfcMember(handle: number, storeyId: number, params: unknown): number;
315
+ /** Add IfcPile. Returns expressId. */
316
+ addIfcPile(handle: number, storeyId: number, params: unknown): number;
317
+ /** Add IfcPlate. Returns expressId. */
318
+ addIfcPlate(handle: number, storeyId: number, params: unknown): number;
319
+ /** Add IfcPropertySet. Returns expressId. */
320
+ addIfcPropertySet(handle: number, elementId: number, params: unknown): number;
321
+ /** Add IfcRailing. Returns expressId. */
322
+ addIfcRailing(handle: number, storeyId: number, params: unknown): number;
323
+ /** Add IfcRamp. Returns expressId. */
324
+ addIfcRamp(handle: number, storeyId: number, params: unknown): number;
325
+ /** Add IfcRectangleHollowBeam. Returns expressId. */
326
+ addIfcRectangleHollowBeam(handle: number, storeyId: number, params: unknown): number;
327
+ /** Add IfcRoof. Returns expressId. */
328
+ addIfcRoof(handle: number, storeyId: number, params: unknown): number;
329
+ /** Add IfcSlab. Returns expressId. */
330
+ addIfcSlab(handle: number, storeyId: number, params: unknown): number;
331
+ /** Add IfcSpace. Returns expressId. */
332
+ addIfcSpace(handle: number, storeyId: number, params: unknown): number;
333
+ /** Add IfcStair. Returns expressId. */
334
+ addIfcStair(handle: number, storeyId: number, params: unknown): number;
335
+ /** Add IfcTShapeMember. Returns expressId. */
336
+ addIfcTShapeMember(handle: number, storeyId: number, params: unknown): number;
337
+ /** Add IfcUShapeMember. Returns expressId. */
338
+ addIfcUShapeMember(handle: number, storeyId: number, params: unknown): number;
339
+ /** Add IfcWall. Returns expressId. */
340
+ addIfcWall(handle: number, storeyId: number, params: unknown): number;
280
341
  /** 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;
342
+ addIfcWallDoor(handle: number, wallId: number, params: unknown): number;
282
343
  /** 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;
344
+ addIfcWallWindow(handle: number, wallId: number, params: unknown): number;
345
+ /** Add IfcWindow. Returns expressId. */
346
+ addIfcWindow(handle: number, storeyId: number, params: unknown): number;
326
347
  /** Create a profile from a ProfileDef union. Returns profile ID. */
327
- createProfile(handle: number, profile: BimProfileDef): number;
348
+ createProfile(handle: number, profile: unknown): number;
328
349
  /** Get the world placement ID for use with addLocalPlacement. */
329
350
  getWorldPlacementId(handle: number): number;
330
- /** Assign a named colour to an element. Call before toIfc(). */
331
- setColor(handle: number, elementId: number, name: string, rgb: [number, number, number]): void;
332
- /** Assign an IFC material (simple or layered) to an element. */
333
- addIfcMaterial(handle: number, elementId: number, material: { Name: string; Category?: string; Layers?: Array<{ Name: string; Thickness: number; Category?: string; IsVentilated?: boolean }> }): void;
334
- /** Attach a property set to an element. Returns pset expressId. */
335
- addIfcPropertySet(handle: number, elementId: number, pset: { Name: string; Properties: Array<{ Name: string; NominalValue: string | number | boolean; Type?: string }> }): number;
336
- /** Attach element quantities to an element. Returns qset expressId. */
337
- addIfcElementQuantity(handle: number, elementId: number, qset: { Name: string; Quantities: Array<{ Name: string; Value: number; Kind: 'IfcQuantityLength' | 'IfcQuantityArea' | 'IfcQuantityVolume' | 'IfcQuantityCount' | 'IfcQuantityWeight' }> }): number;
338
- /** Generate the IFC STEP file content. Returns { content, entities, stats }. */
339
- toIfc(handle: number): { content: string; entities: Array<{ expressId: number; type: string; Name?: string }>; stats: { entityCount: number; fileSize: number } };
340
351
  };
341
352
  /** Uploaded file attachments */
342
353
  files: {
@@ -349,6 +360,17 @@ declare const bim: {
349
360
  /** Get parsed CSV column names for an uploaded attachment by file name */
350
361
  csvColumns(name: string): string[];
351
362
  };
363
+ /** 4D / IFC construction schedule reader (IfcTask, IfcWorkSchedule, IfcRelSequence) */
364
+ schedule: {
365
+ /** Full schedule extraction — tasks, dependencies, and work schedules. */
366
+ data(modelId?: string): { HasSchedule: boolean; WorkSchedules: Array<{ GlobalId: string; ExpressId: number; Name: string; Description?: string; Identification?: string; CreationDate?: string; StartTime?: string; FinishTime?: string; Purpose?: string; Duration?: string; PredefinedType?: string; Kind: 'WorkSchedule' | 'WorkPlan'; TaskGlobalIds: string[] }>; Tasks: Array<{ GlobalId: string; ExpressId: number; Name: string; Description?: string; ObjectType?: string; Identification?: string; LongDescription?: string; Status?: string; WorkMethod?: string; IsMilestone: boolean; Priority?: number; PredefinedType?: string; ParentTaskGlobalId?: string; ChildTaskGlobalIds: string[]; AssignedProductExpressIds: number[]; AssignedProductGlobalIds: string[]; ControllingScheduleGlobalIds: string[]; TaskTime?: { ScheduleStart?: string; ScheduleFinish?: string; ScheduleDuration?: string; ActualStart?: string; ActualFinish?: string; ActualDuration?: string; EarlyStart?: string; EarlyFinish?: string; LateStart?: string; LateFinish?: string; FreeFloat?: string; TotalFloat?: string; RemainingTime?: string; StatusTime?: string; IsCritical?: boolean; Completion?: number; DurationType?: 'WORKTIME' | 'ELAPSEDTIME' | 'NOTDEFINED' } }>; Sequences: Array<{ RelatingProcessGlobalId: string; RelatedProcessGlobalId: string; SequenceType: 'START_START' | 'START_FINISH' | 'FINISH_START' | 'FINISH_FINISH' | 'USERDEFINED' | 'NOTDEFINED'; UserDefinedSequenceType?: string; TimeLagSeconds?: number; TimeLagDuration?: string }> };
367
+ /** All IfcTask entities with their times and assigned products. */
368
+ tasks(modelId?: string): Array<{ GlobalId: string; ExpressId: number; Name: string; Description?: string; ObjectType?: string; Identification?: string; LongDescription?: string; Status?: string; WorkMethod?: string; IsMilestone: boolean; Priority?: number; PredefinedType?: string; ParentTaskGlobalId?: string; ChildTaskGlobalIds: string[]; AssignedProductExpressIds: number[]; AssignedProductGlobalIds: string[]; ControllingScheduleGlobalIds: string[]; TaskTime?: { ScheduleStart?: string; ScheduleFinish?: string; ScheduleDuration?: string; ActualStart?: string; ActualFinish?: string; ActualDuration?: string; EarlyStart?: string; EarlyFinish?: string; LateStart?: string; LateFinish?: string; FreeFloat?: string; TotalFloat?: string; RemainingTime?: string; StatusTime?: string; IsCritical?: boolean; Completion?: number; DurationType?: 'WORKTIME' | 'ELAPSEDTIME' | 'NOTDEFINED' } }>;
369
+ /** All IfcWorkSchedule and IfcWorkPlan containers. */
370
+ workSchedules(modelId?: string): Array<{ GlobalId: string; ExpressId: number; Name: string; Description?: string; Identification?: string; CreationDate?: string; StartTime?: string; FinishTime?: string; Purpose?: string; Duration?: string; PredefinedType?: string; Kind: 'WorkSchedule' | 'WorkPlan'; TaskGlobalIds: string[] }>;
371
+ /** All IfcRelSequence dependency edges (FS/SS/FF/SF, with optional IfcLagTime). */
372
+ sequences(modelId?: string): Array<{ RelatingProcessGlobalId: string; RelatedProcessGlobalId: string; SequenceType: 'START_START' | 'START_FINISH' | 'FINISH_START' | 'FINISH_FINISH' | 'USERDEFINED' | 'NOTDEFINED'; UserDefinedSequenceType?: string; TimeLagSeconds?: number; TimeLagDuration?: string }>;
373
+ };
352
374
  /** Data export */
353
375
  export: {
354
376
  /** Export entities to CSV string */
@@ -356,8 +378,8 @@ declare const bim: {
356
378
  /** Export entities to JSON array */
357
379
  json(entities: BimEntity[], columns: string[]): Record<string, unknown>[];
358
380
  /** 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;
360
- /** Trigger a browser file download with raw content */
381
+ ifc(entities: BimEntity[], options: { schema?: "IFC2X3" | "IFC4" | "IFC4X3"; filename?: string; includeMutations?: boolean; visibleOnly?: boolean }): string | Uint8Array;
382
+ /** Trigger a browser file download with the given content. mimeType defaults to text/plain. */
361
383
  download(content: string, filename: string, mimeType?: string): void;
362
384
  };
363
385
  };