brep-io-kernel 1.0.0-ci.10

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 (271) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +157 -0
  3. package/dist-kernel/brep-kernel.js +74699 -0
  4. package/package.json +58 -0
  5. package/src/BREP/AssemblyComponent.js +42 -0
  6. package/src/BREP/BREP.js +43 -0
  7. package/src/BREP/BetterSolid.js +805 -0
  8. package/src/BREP/Edge.js +103 -0
  9. package/src/BREP/Extrude.js +403 -0
  10. package/src/BREP/Face.js +187 -0
  11. package/src/BREP/MeshRepairer.js +634 -0
  12. package/src/BREP/OffsetShellSolid.js +614 -0
  13. package/src/BREP/PointCloudWrap.js +302 -0
  14. package/src/BREP/Revolve.js +345 -0
  15. package/src/BREP/SolidMethods/authoring.js +112 -0
  16. package/src/BREP/SolidMethods/booleanOps.js +230 -0
  17. package/src/BREP/SolidMethods/chamfer.js +122 -0
  18. package/src/BREP/SolidMethods/edgeResolution.js +25 -0
  19. package/src/BREP/SolidMethods/fillet.js +792 -0
  20. package/src/BREP/SolidMethods/index.js +72 -0
  21. package/src/BREP/SolidMethods/io.js +105 -0
  22. package/src/BREP/SolidMethods/lifecycle.js +103 -0
  23. package/src/BREP/SolidMethods/manifoldOps.js +375 -0
  24. package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
  25. package/src/BREP/SolidMethods/meshQueries.js +264 -0
  26. package/src/BREP/SolidMethods/metadata.js +106 -0
  27. package/src/BREP/SolidMethods/metrics.js +51 -0
  28. package/src/BREP/SolidMethods/transforms.js +361 -0
  29. package/src/BREP/SolidMethods/visualize.js +508 -0
  30. package/src/BREP/SolidShared.js +26 -0
  31. package/src/BREP/Sweep.js +1596 -0
  32. package/src/BREP/Tube.js +857 -0
  33. package/src/BREP/Vertex.js +43 -0
  34. package/src/BREP/applyBooleanOperation.js +704 -0
  35. package/src/BREP/boundsUtils.js +48 -0
  36. package/src/BREP/chamfer.js +551 -0
  37. package/src/BREP/edgePolylineUtils.js +85 -0
  38. package/src/BREP/fillets/common.js +388 -0
  39. package/src/BREP/fillets/fillet.js +1422 -0
  40. package/src/BREP/fillets/filletGeometry.js +15 -0
  41. package/src/BREP/fillets/inset.js +389 -0
  42. package/src/BREP/fillets/offsetHelper.js +143 -0
  43. package/src/BREP/fillets/outset.js +88 -0
  44. package/src/BREP/helix.js +193 -0
  45. package/src/BREP/meshToBrep.js +234 -0
  46. package/src/BREP/primitives.js +279 -0
  47. package/src/BREP/setupManifold.js +71 -0
  48. package/src/BREP/threadGeometry.js +1120 -0
  49. package/src/BREP/triangleUtils.js +8 -0
  50. package/src/BREP/triangulate.js +608 -0
  51. package/src/FeatureRegistry.js +183 -0
  52. package/src/PartHistory.js +1132 -0
  53. package/src/UI/AccordionWidget.js +292 -0
  54. package/src/UI/CADmaterials.js +850 -0
  55. package/src/UI/EnvMonacoEditor.js +522 -0
  56. package/src/UI/FloatingWindow.js +396 -0
  57. package/src/UI/HistoryWidget.js +457 -0
  58. package/src/UI/MainToolbar.js +131 -0
  59. package/src/UI/ModelLibraryView.js +194 -0
  60. package/src/UI/OrthoCameraIdle.js +206 -0
  61. package/src/UI/PluginsWidget.js +280 -0
  62. package/src/UI/SceneListing.js +606 -0
  63. package/src/UI/SelectionFilter.js +629 -0
  64. package/src/UI/ViewCube.js +389 -0
  65. package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
  66. package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
  67. package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
  68. package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
  69. package/src/UI/assembly/constraintFaceUtils.js +115 -0
  70. package/src/UI/assembly/constraintHighlightUtils.js +70 -0
  71. package/src/UI/assembly/constraintLabelUtils.js +31 -0
  72. package/src/UI/assembly/constraintPointUtils.js +64 -0
  73. package/src/UI/assembly/constraintSelectionUtils.js +185 -0
  74. package/src/UI/assembly/constraintStatusUtils.js +142 -0
  75. package/src/UI/componentSelectorModal.js +240 -0
  76. package/src/UI/controls/CombinedTransformControls.js +386 -0
  77. package/src/UI/dialogs.js +351 -0
  78. package/src/UI/expressionsManager.js +100 -0
  79. package/src/UI/featureDialogWidgets/booleanField.js +25 -0
  80. package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
  81. package/src/UI/featureDialogWidgets/buttonField.js +45 -0
  82. package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
  83. package/src/UI/featureDialogWidgets/defaultField.js +23 -0
  84. package/src/UI/featureDialogWidgets/fileField.js +66 -0
  85. package/src/UI/featureDialogWidgets/index.js +34 -0
  86. package/src/UI/featureDialogWidgets/numberField.js +165 -0
  87. package/src/UI/featureDialogWidgets/optionsField.js +33 -0
  88. package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
  89. package/src/UI/featureDialogWidgets/stringField.js +24 -0
  90. package/src/UI/featureDialogWidgets/textareaField.js +28 -0
  91. package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
  92. package/src/UI/featureDialogWidgets/transformField.js +252 -0
  93. package/src/UI/featureDialogWidgets/utils.js +43 -0
  94. package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
  95. package/src/UI/featureDialogs.js +1414 -0
  96. package/src/UI/fileManagerWidget.js +615 -0
  97. package/src/UI/history/HistoryCollectionWidget.js +1294 -0
  98. package/src/UI/history/historyCollectionWidget.css.js +257 -0
  99. package/src/UI/history/historyDisplayInfo.js +133 -0
  100. package/src/UI/mobile.js +28 -0
  101. package/src/UI/objectDump.js +442 -0
  102. package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
  103. package/src/UI/pmi/AnnotationHistory.js +353 -0
  104. package/src/UI/pmi/AnnotationRegistry.js +90 -0
  105. package/src/UI/pmi/BaseAnnotation.js +269 -0
  106. package/src/UI/pmi/LabelOverlay.css +102 -0
  107. package/src/UI/pmi/LabelOverlay.js +191 -0
  108. package/src/UI/pmi/PMIMode.js +1550 -0
  109. package/src/UI/pmi/PMIViewsWidget.js +1098 -0
  110. package/src/UI/pmi/annUtils.js +729 -0
  111. package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
  112. package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
  113. package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
  114. package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
  115. package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
  116. package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
  117. package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
  118. package/src/UI/pmi/pmiStyle.js +44 -0
  119. package/src/UI/sketcher/SketchMode3D.js +4095 -0
  120. package/src/UI/sketcher/dimensions.js +674 -0
  121. package/src/UI/sketcher/glyphs.js +236 -0
  122. package/src/UI/sketcher/highlights.js +60 -0
  123. package/src/UI/toolbarButtons/aboutButton.js +5 -0
  124. package/src/UI/toolbarButtons/exportButton.js +609 -0
  125. package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
  126. package/src/UI/toolbarButtons/importButton.js +160 -0
  127. package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
  128. package/src/UI/toolbarButtons/metadataButton.js +1063 -0
  129. package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
  130. package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
  131. package/src/UI/toolbarButtons/saveButton.js +99 -0
  132. package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
  133. package/src/UI/toolbarButtons/testsButton.js +26 -0
  134. package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
  135. package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
  136. package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
  137. package/src/UI/triangleDebuggerWindow.js +945 -0
  138. package/src/UI/viewer.js +4228 -0
  139. package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
  140. package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
  141. package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
  142. package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
  143. package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
  144. package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
  145. package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
  146. package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
  147. package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
  148. package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
  149. package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
  150. package/src/core/entities/HistoryCollectionBase.js +72 -0
  151. package/src/core/entities/ListEntityBase.js +109 -0
  152. package/src/core/entities/schemaProcesser.js +121 -0
  153. package/src/exporters/sheetMetalFlatPattern.js +659 -0
  154. package/src/exporters/sheetMetalUnfold.js +862 -0
  155. package/src/exporters/step.js +1135 -0
  156. package/src/exporters/threeMF.js +575 -0
  157. package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
  158. package/src/features/boolean/BooleanFeature.js +94 -0
  159. package/src/features/chamfer/ChamferFeature.js +116 -0
  160. package/src/features/datium/DatiumFeature.js +80 -0
  161. package/src/features/edgeFeatureUtils.js +41 -0
  162. package/src/features/extrude/ExtrudeFeature.js +143 -0
  163. package/src/features/fillet/FilletFeature.js +197 -0
  164. package/src/features/helix/HelixFeature.js +405 -0
  165. package/src/features/hole/HoleFeature.js +1050 -0
  166. package/src/features/hole/screwClearance.js +86 -0
  167. package/src/features/hole/threadDesignationCatalog.js +149 -0
  168. package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
  169. package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
  170. package/src/features/imageToFace/imageEditor.js +1270 -0
  171. package/src/features/imageToFace/traceUtils.js +971 -0
  172. package/src/features/import3dModel/Import3dModelFeature.js +151 -0
  173. package/src/features/loft/LoftFeature.js +605 -0
  174. package/src/features/mirror/MirrorFeature.js +151 -0
  175. package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
  176. package/src/features/offsetShell/OffsetShellFeature.js +89 -0
  177. package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
  178. package/src/features/pattern/PatternFeature.js +275 -0
  179. package/src/features/patternLinear/PatternLinearFeature.js +120 -0
  180. package/src/features/patternRadial/PatternRadialFeature.js +186 -0
  181. package/src/features/plane/PlaneFeature.js +154 -0
  182. package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
  183. package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
  184. package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
  185. package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
  186. package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
  187. package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
  188. package/src/features/remesh/RemeshFeature.js +97 -0
  189. package/src/features/revolve/RevolveFeature.js +111 -0
  190. package/src/features/selectionUtils.js +118 -0
  191. package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
  192. package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
  193. package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
  194. package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
  195. package/src/features/sheetMetal/SheetMetalObject.js +141 -0
  196. package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
  197. package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
  198. package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
  199. package/src/features/sheetMetal/profileUtils.js +25 -0
  200. package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
  201. package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
  202. package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
  203. package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
  204. package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
  205. package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
  206. package/src/features/sheetMetal/sheetMetalTree.js +210 -0
  207. package/src/features/sketch/SketchFeature.js +955 -0
  208. package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
  209. package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
  210. package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
  211. package/src/features/spline/SplineEditorSession.js +988 -0
  212. package/src/features/spline/SplineFeature.js +1388 -0
  213. package/src/features/spline/splineUtils.js +218 -0
  214. package/src/features/sweep/SweepFeature.js +110 -0
  215. package/src/features/transform/TransformFeature.js +152 -0
  216. package/src/features/tube/TubeFeature.js +635 -0
  217. package/src/fs.proxy.js +625 -0
  218. package/src/idbStorage.js +254 -0
  219. package/src/index.js +12 -0
  220. package/src/main.js +15 -0
  221. package/src/metadataManager.js +64 -0
  222. package/src/path.proxy.js +277 -0
  223. package/src/plugins/ghLoader.worker.js +151 -0
  224. package/src/plugins/pluginManager.js +286 -0
  225. package/src/pmi/PMIViewsManager.js +134 -0
  226. package/src/services/componentLibrary.js +198 -0
  227. package/src/tests/ConsoleCapture.js +189 -0
  228. package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
  229. package/src/tests/browserTests.js +597 -0
  230. package/src/tests/debugBoolean.js +225 -0
  231. package/src/tests/partFiles/badBoolean.json +957 -0
  232. package/src/tests/partFiles/extrudeTest.json +88 -0
  233. package/src/tests/partFiles/filletFail.json +58 -0
  234. package/src/tests/partFiles/import_TEst.part.part.json +646 -0
  235. package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
  236. package/src/tests/test_boolean_subtract.js +27 -0
  237. package/src/tests/test_chamfer.js +17 -0
  238. package/src/tests/test_extrudeFace.js +24 -0
  239. package/src/tests/test_fillet.js +17 -0
  240. package/src/tests/test_fillet_nonClosed.js +45 -0
  241. package/src/tests/test_filletsMoreDifficult.js +46 -0
  242. package/src/tests/test_history_features_basic.js +149 -0
  243. package/src/tests/test_hole.js +282 -0
  244. package/src/tests/test_mirror.js +16 -0
  245. package/src/tests/test_offsetShellGrouping.js +85 -0
  246. package/src/tests/test_plane.js +4 -0
  247. package/src/tests/test_primitiveCone.js +11 -0
  248. package/src/tests/test_primitiveCube.js +7 -0
  249. package/src/tests/test_primitiveCylinder.js +8 -0
  250. package/src/tests/test_primitivePyramid.js +9 -0
  251. package/src/tests/test_primitiveSphere.js +17 -0
  252. package/src/tests/test_primitiveTorus.js +21 -0
  253. package/src/tests/test_pushFace.js +126 -0
  254. package/src/tests/test_sheetMetalContourFlange.js +125 -0
  255. package/src/tests/test_sheetMetal_features.js +80 -0
  256. package/src/tests/test_sketch_openLoop.js +45 -0
  257. package/src/tests/test_solidMetrics.js +58 -0
  258. package/src/tests/test_stlLoader.js +1889 -0
  259. package/src/tests/test_sweepFace.js +55 -0
  260. package/src/tests/test_tube.js +45 -0
  261. package/src/tests/test_tube_closedLoop.js +67 -0
  262. package/src/tests/tests.js +493 -0
  263. package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
  264. package/src/tools/dialogCapturePageFactory.js +227 -0
  265. package/src/tools/featureDialogCapturePage.js +47 -0
  266. package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
  267. package/src/utils/axisHelpers.js +99 -0
  268. package/src/utils/deepClone.js +69 -0
  269. package/src/utils/geometryTolerance.js +37 -0
  270. package/src/utils/normalizeTypeString.js +8 -0
  271. package/src/utils/xformMath.js +51 -0
@@ -0,0 +1,151 @@
1
+ // Module Worker: Fetches plugin entry from candidate URLs (preferring GitHub Raw),
2
+ // and builds a small in-memory module graph where all relative and raw.githubusercontent.com
3
+ // imports are fetched from Raw and rewritten to blob: URLs, avoiding MIME issues.
4
+ // Input: { type: 'load', urls: string[], bases: string[], ts?: number }
5
+ // Output: { ok: true, code, usedUrl, usedBase } or { ok: false, error }
6
+
7
+ const RAW_HOST = 'raw.githubusercontent.com';
8
+
9
+ function isRawUrl(u) {
10
+ try { return new URL(u).hostname === RAW_HOST; } catch { return false; }
11
+ }
12
+
13
+ function dirOf(u) {
14
+ try { return new URL('.', u).href; } catch { return u; }
15
+ }
16
+
17
+ function normalize(u) {
18
+ try {
19
+ const url = new URL(u);
20
+ // strip query/hash for graph keys
21
+ url.search = '';
22
+ url.hash = '';
23
+ return url.href;
24
+ } catch { return u; }
25
+ }
26
+
27
+ // Find string-literal specifiers in static and dynamic imports
28
+ function findSpecifiers(code) {
29
+ const out = [];
30
+ const reStatic = /\b(?:import|export)\s+(?:[^'";]*?\sfrom\s+)?(["'])([^"']+)(\1)/g;
31
+ const reDyn = /\bimport\s*\(\s*(["'])([^"']+)(\1)\s*\)/g;
32
+ let m;
33
+ while ((m = reStatic.exec(code))) {
34
+ const q = m[1];
35
+ const spec = m[2];
36
+ out.push({ kind: 'static', q, spec });
37
+ }
38
+ while ((m = reDyn.exec(code))) {
39
+ const q = m[1];
40
+ const spec = m[2];
41
+ out.push({ kind: 'dynamic', q, spec });
42
+ }
43
+ return out;
44
+ }
45
+
46
+ async function fetchText(url) {
47
+ const res = await fetch(url, { mode: 'cors', credentials: 'omit' });
48
+ if (!res || !res.ok) throw new Error(`HTTP ${res && res.status}`);
49
+ return res.text();
50
+ }
51
+
52
+ // Build module graph, rewriting target module code to import blob: URLs for its deps.
53
+ async function buildGraph(entryUrl) {
54
+ const blobBySrc = new Map(); // srcAbs -> blobUrl
55
+ const codeBySrc = new Map(); // srcAbs -> rewritten code
56
+ const loading = new Set();
57
+
58
+ async function loadModule(srcAbs) {
59
+ const key = normalize(srcAbs);
60
+ if (blobBySrc.has(key)) return blobBySrc.get(key);
61
+ if (loading.has(key)) {
62
+ // Circular import: emit minimal stub to break the cycle.
63
+ const stub = URL.createObjectURL(new Blob([`export {};`], { type: 'application/javascript' }));
64
+ blobBySrc.set(key, stub);
65
+ return stub;
66
+ }
67
+ loading.add(key);
68
+ const rawCode = await fetchText(srcAbs);
69
+ const baseDir = dirOf(srcAbs);
70
+ const specs = findSpecifiers(rawCode).filter(s => s && typeof s.spec === 'string');
71
+
72
+ // Determine which specifiers we will fetch and rewrite: relative or raw absolute.
73
+ const targets = [];
74
+ for (const s of specs) {
75
+ const spec = s.spec.trim();
76
+ if (!spec) continue;
77
+ const isRel = spec.startsWith('.') || spec.startsWith('..');
78
+ const isRawAbs = spec.startsWith('http://') || spec.startsWith('https://') ? isRawUrl(spec) : false;
79
+ if (!isRel && !isRawAbs) continue;
80
+ const abs = isRel ? new URL(spec, baseDir).href : spec;
81
+ targets.push({ lit: spec, abs });
82
+ }
83
+
84
+ // Deduplicate by absolute URL
85
+ const uniq = new Map();
86
+ for (const t of targets) { const k = normalize(t.abs); if (!uniq.has(k)) uniq.set(k, t); }
87
+
88
+ // Recursively load children first
89
+ const mapLitToBlob = new Map();
90
+ for (const [k, t] of uniq.entries()) {
91
+ const childBlob = await loadModule(t.abs);
92
+ mapLitToBlob.set(t.lit, childBlob);
93
+ }
94
+
95
+ // Rewrite code: replace each matched literal spec with its blob URL
96
+ let out = rawCode;
97
+ // Static
98
+ out = out.replace(/\b(?:import|export)\s+(?:[^'";]*?\sfrom\s+)?(["'])([^"']+)(\1)/g, (m, q, spec) => {
99
+ const repl = mapLitToBlob.get(spec);
100
+ if (repl) return m.replace(`${q}${spec}${q}`, `${q}${repl}${q}`);
101
+ // If relative import and base is raw, rewrite to absolute raw so it resolves consistently
102
+ if (spec.startsWith('.') || spec.startsWith('..')) {
103
+ try { const abs = new URL(spec, baseDir).href; return m.replace(`${q}${spec}${q}`, `${q}${abs}${q}`); } catch {}
104
+ }
105
+ return m;
106
+ });
107
+ // Dynamic
108
+ out = out.replace(/\bimport\s*\(\s*(["'])([^"']+)(\1)\s*\)/g, (m, q, spec) => {
109
+ const repl = mapLitToBlob.get(spec);
110
+ if (repl) return m.replace(`${q}${spec}${q}`, `${q}${repl}${q}`);
111
+ if (spec.startsWith('.') || spec.startsWith('..')) {
112
+ try { const abs = new URL(spec, baseDir).href; return m.replace(`${q}${spec}${q}`, `${q}${abs}${q}`); } catch {}
113
+ }
114
+ return m;
115
+ });
116
+
117
+ // Create blob for this module
118
+ const blob = URL.createObjectURL(new Blob([out], { type: 'application/javascript' }));
119
+ blobBySrc.set(key, blob);
120
+ codeBySrc.set(key, out);
121
+ loading.delete(key);
122
+ return blob;
123
+ }
124
+
125
+ const entryBlob = await loadModule(entryUrl);
126
+ // Return the rewritten code of the entry so the main thread can wrap or import it.
127
+ const entryCode = codeBySrc.get(normalize(entryUrl)) || '';
128
+ return { code: entryCode, entryBlob };
129
+ }
130
+
131
+ self.addEventListener('message', async (ev) => {
132
+ const msg = ev.data || {};
133
+ if (msg.type !== 'load') return;
134
+ const urls = Array.isArray(msg.urls) ? msg.urls : [];
135
+ const bases = Array.isArray(msg.bases) ? msg.bases : [];
136
+ let lastErr = null;
137
+ for (let i = 0; i < urls.length; i++) {
138
+ const url = urls[i];
139
+ const base = bases[i] || '';
140
+ try {
141
+ // Build a blob-based module graph starting at the chosen candidate URL.
142
+ const { code } = await buildGraph(url);
143
+ self.postMessage({ ok: true, code, usedUrl: url, usedBase: base });
144
+ return;
145
+ } catch (e) {
146
+ lastErr = e;
147
+ // try next candidate
148
+ }
149
+ }
150
+ self.postMessage({ ok: false, error: String(lastErr || 'Failed to load plugin') });
151
+ });
@@ -0,0 +1,286 @@
1
+ // Plugin loader utilities.
2
+ // - GitHub repos: try GitHub Raw first, then fall back to jsDelivr.
3
+ // - Generic URLs: load from any HTTP(S) base or a direct plugin entry URL.
4
+
5
+ // Parse common GitHub URL shapes.
6
+ // Supports:
7
+ // - https://github.com/USER/REPO
8
+ // - https://github.com/USER/REPO/tree/REF
9
+ // - https://github.com/USER/REPO/tree/REF/sub/dir
10
+ import { BREP } from "../BREP/BREP.js";
11
+ import { localStorage as LS } from "../idbStorage.js";
12
+
13
+
14
+
15
+
16
+ export function parseGithubUrl(input) {
17
+ const url = new URL(input);
18
+ const parts = url.pathname.split('/').filter(Boolean);
19
+ const user = parts[0];
20
+ const repo = parts[1];
21
+ let ref = null;
22
+ let subdir = '';
23
+ const idx = parts.indexOf('tree');
24
+ if (idx !== -1) {
25
+ ref = parts[idx + 1] || null;
26
+ const rest = parts.slice(idx + 2);
27
+ subdir = rest.length ? '/' + rest.join('/') : '';
28
+ }
29
+ if (!user || !repo) throw new Error('Invalid GitHub repo URL');
30
+ return { user, repo, ref, subdir };
31
+ }
32
+
33
+ function isGithubRepoUrl(u) {
34
+ try { return new URL(u).hostname === 'github.com'; } catch { return false; }
35
+ }
36
+
37
+ function dirOf(u) {
38
+ try { return new URL('.', u).href; } catch { return u; }
39
+ }
40
+
41
+ async function fetchAndPrepareEntryViaWorker(entryUrls, baseUrls, ts) {
42
+ return new Promise((resolve, reject) => {
43
+ const worker = new Worker(new URL('./ghLoader.worker.js', import.meta.url), { type: 'module' });
44
+ const cleanup = () => { try { worker.terminate(); } catch { } };
45
+ worker.onmessage = (ev) => {
46
+ const data = ev.data || {};
47
+ if (data.ok) {
48
+ // Do NOT terminate here; caller will keep the worker alive
49
+ // long enough for blob: dependencies to load.
50
+ resolve({ ...data, __worker: worker, __cleanup: cleanup });
51
+ } else {
52
+ cleanup();
53
+ reject(new Error(data.error || 'Worker failed'));
54
+ }
55
+ };
56
+ worker.onerror = (e) => { cleanup(); reject(new Error(String(e?.message || e))); };
57
+ worker.postMessage({ type: 'load', urls: entryUrls, bases: baseUrls, ts: ts ?? Date.now() });
58
+ });
59
+ }
60
+
61
+ export async function importGithubPlugin(repoUrl) {
62
+ const { user, repo, ref, subdir } = parseGithubUrl(repoUrl);
63
+ const t = Date.now();
64
+
65
+ // Build candidate sources in order: GitHub Raw first (ref/main/master), then jsDelivr (ref/latest)
66
+ const entryUrls = [];
67
+ const baseUrls = [];
68
+
69
+ // 1) GitHub Raw candidates
70
+ if (ref) {
71
+ const rawBase = `https://raw.githubusercontent.com/${user}/${repo}/${ref}${subdir || ''}`;
72
+ entryUrls.push(`${rawBase}/plugin.js?t=${t}`);
73
+ baseUrls.push(rawBase);
74
+ } else {
75
+ for (const branch of ['main', 'master']) {
76
+ const rawBase = `https://raw.githubusercontent.com/${user}/${repo}/${branch}${subdir || ''}`;
77
+ entryUrls.push(`${rawBase}/plugin.js?t=${t}`);
78
+ baseUrls.push(rawBase);
79
+ }
80
+ }
81
+
82
+ // 2) jsDelivr fallback (prefer specific ref if provided, else @latest)
83
+ const cdnRef = ref ? ref : 'latest';
84
+ const jsdBase = `https://cdn.jsdelivr.net/gh/${user}/${repo}@${cdnRef}${subdir || ''}`;
85
+ entryUrls.push(`${jsdBase}/plugin.js?t=${t}`);
86
+ baseUrls.push(jsdBase);
87
+
88
+ // Web worker fetch + rewrite to absolute imports for the chosen base
89
+ const { code, usedUrl, usedBase, __worker, __cleanup } = await fetchAndPrepareEntryViaWorker(entryUrls, baseUrls, t);
90
+ const blob = new Blob([code], { type: 'application/javascript' });
91
+ const url = URL.createObjectURL(blob);
92
+ try {
93
+ const mod = await import(/* webpackIgnore: true */ /* @vite-ignore */ url);
94
+ return mod;
95
+ } finally {
96
+ // Clean up the blob URL; module stays cached
97
+ setTimeout(() => {
98
+ try { URL.revokeObjectURL(url); } catch { }
99
+ // Now safe to terminate worker that created dependency blob: URLs.
100
+ try { (__cleanup || (()=>{}))(); } catch { }
101
+ try { __worker && __worker.terminate && __worker.terminate(); } catch { }
102
+ }, 0);
103
+ }
104
+ }
105
+
106
+ // Generic URL importer. Accepts either a base URL (serving plugin files with entry as plugin.js)
107
+ // or a direct entry URL ending in .js. Example: http://localhost:8080/ or https://host/path/plugin.js
108
+ export async function importUrlPlugin(rawUrl) {
109
+ const t = Date.now();
110
+ let entryUrl = '';
111
+ let baseUrl = '';
112
+ try {
113
+ const u = new URL(String(rawUrl));
114
+ // If it looks like a direct JS entry, use it as-is; otherwise, append plugin.js to the base.
115
+ const path = u.pathname || '';
116
+ const isJsEntry = /\.m?js$/i.test(path);
117
+ if (isJsEntry) {
118
+ entryUrl = u.href;
119
+ baseUrl = dirOf(u.href);
120
+ } else {
121
+ // Ensure trailing slash so URL('plugin.js', base) works consistently
122
+ if (!u.pathname.endsWith('/')) u.pathname = (u.pathname || '') + '/';
123
+ baseUrl = u.href;
124
+ entryUrl = new URL('plugin.js', baseUrl).href;
125
+ }
126
+ } catch (e) {
127
+ throw new Error('Invalid URL');
128
+ }
129
+
130
+ const entryUrls = [`${entryUrl}${entryUrl.includes('?') ? '&' : '?'}t=${t}`];
131
+ const baseUrls = [baseUrl];
132
+
133
+ const { code, __worker, __cleanup } = await fetchAndPrepareEntryViaWorker(entryUrls, baseUrls, t);
134
+ const blob = new Blob([code], { type: 'application/javascript' });
135
+ const url = URL.createObjectURL(blob);
136
+ try {
137
+ const mod = await import(/* webpackIgnore: true */ /* @vite-ignore */ url);
138
+ return mod;
139
+ } finally {
140
+ setTimeout(() => {
141
+ try { URL.revokeObjectURL(url); } catch { }
142
+ try { (__cleanup || (()=>{}))(); } catch { }
143
+ try { __worker && __worker.terminate && __worker.terminate(); } catch { }
144
+ }, 0);
145
+ }
146
+ }
147
+
148
+ // Plugin app object: exposes the full viewer and helper hooks for plugins.
149
+ function buildApp(viewer) {
150
+ const app = {
151
+ BREP,
152
+ viewer,
153
+ registerFeature(FeatureClass) {
154
+ try {
155
+ FeatureClass.fromPlugin = true;
156
+ const baseShort = FeatureClass?.shortName || FeatureClass?.name || 'Feature';
157
+ const baseLong = FeatureClass?.longName || FeatureClass?.name || baseShort;
158
+ FeatureClass.shortName = `🔌${baseShort}`;
159
+ FeatureClass.longName = `🔌 ${baseLong}`;
160
+
161
+ viewer?.partHistory?.featureRegistry?.register?.(FeatureClass);
162
+
163
+ } catch { }
164
+ },
165
+ registerAnnotation(AnnotationHandler) {
166
+ try {
167
+ // Optional: prefix plugin marker in titles if present
168
+ if (AnnotationHandler && typeof AnnotationHandler === 'object') {
169
+ if (AnnotationHandler.title) AnnotationHandler.title = `🔌 ${AnnotationHandler.title}`;
170
+ }
171
+ viewer?.annotationRegistry?.register?.(AnnotationHandler);
172
+ } catch {}
173
+ },
174
+ addToolbarButton(label, title, onClick) {
175
+ if (!viewer) return;
176
+ try { viewer.addToolbarButton(label, title, onClick); } catch { }
177
+ },
178
+ async addSidePanel(title, content) {
179
+ try {
180
+ if (typeof viewer?.addPluginSidePanel === 'function') {
181
+ return await viewer.addPluginSidePanel(title, content);
182
+ }
183
+ // Fallback: add immediately if helper is unavailable
184
+ const sec = await viewer?.accordion?.addSection?.(String(title || 'Plugin'));
185
+ if (!sec) return null;
186
+ if (typeof content === 'function') {
187
+ const el = await content();
188
+ if (el) sec.uiElement.appendChild(el);
189
+ } else if (content instanceof HTMLElement) {
190
+ sec.uiElement.appendChild(content);
191
+ } else if (content != null) {
192
+ const pre = document.createElement('pre');
193
+ pre.textContent = String(content);
194
+ sec.uiElement.appendChild(pre);
195
+ }
196
+ return sec;
197
+ } catch { return null; }
198
+ },
199
+ };
200
+ return app;
201
+ }
202
+
203
+ export async function loadPluginFromRepoUrl(viewer, repoUrl) {
204
+ // Route based on host: GitHub repo URL vs arbitrary base/entry URL
205
+ const isGh = isGithubRepoUrl(repoUrl);
206
+ const mod = isGh ? await importGithubPlugin(repoUrl) : await importUrlPlugin(repoUrl);
207
+ const app = buildApp(viewer);
208
+ if (typeof mod?.default === 'function') {
209
+ await mod.default(app);
210
+ return true;
211
+ }
212
+ if (typeof mod?.install === 'function') {
213
+ await mod.install(app);
214
+ return true;
215
+ }
216
+ console.warn('Plugin loaded but no default() or install() found:', repoUrl);
217
+ return false;
218
+ }
219
+
220
+ export async function loadPlugins(viewer, repoUrls) {
221
+ const urls = (Array.isArray(repoUrls) ? repoUrls : []).map(s => String(s || '').trim()).filter(Boolean);
222
+ const results = [];
223
+ for (const u of urls) {
224
+ try {
225
+ // eslint-disable-next-line no-await-in-loop
226
+ const ok = await loadPluginFromRepoUrl(viewer, u);
227
+ results.push({ url: u, ok, error: null });
228
+ } catch (e) {
229
+ console.error('Failed to load plugin:', u, e);
230
+ results.push({ url: u, ok: false, error: e });
231
+ }
232
+ }
233
+ return results;
234
+ }
235
+
236
+ const STORAGE_KEY = '__BREP_PLUGIN_URLS__';
237
+ const STORAGE_ENABLED_KEY = '__BREP_PLUGIN_ENABLED__';
238
+
239
+ export function getSavedPluginUrls() {
240
+ try {
241
+ const raw = LS.getItem(STORAGE_KEY);
242
+ if (!raw) return [];
243
+ const arr = JSON.parse(raw);
244
+ if (!Array.isArray(arr)) return [];
245
+ return arr.map(s => String(s || '').trim()).filter(Boolean);
246
+ } catch { return []; }
247
+ }
248
+
249
+ export function savePluginUrls(urls) {
250
+ try { LS.setItem(STORAGE_KEY, JSON.stringify((urls || []).map(s => String(s || '').trim()).filter(Boolean))); } catch { }
251
+ }
252
+
253
+ // Enabled/disabled state per plugin URL. Defaults to enabled if missing.
254
+ export function getPluginEnabledMap() {
255
+ try {
256
+ const raw = LS.getItem(STORAGE_ENABLED_KEY);
257
+ if (!raw) return {};
258
+ const obj = JSON.parse(raw);
259
+ return (obj && typeof obj === 'object') ? obj : {};
260
+ } catch { return {}; }
261
+ }
262
+
263
+ export function savePluginEnabledMap(map) {
264
+ try {
265
+ const obj = (map && typeof map === 'object') ? map : {};
266
+ LS.setItem(STORAGE_ENABLED_KEY, JSON.stringify(obj));
267
+ } catch { }
268
+ }
269
+
270
+ export function setPluginEnabled(url, enabled) {
271
+ try {
272
+ const m = getPluginEnabledMap();
273
+ if (!url) return;
274
+ m[String(url)] = Boolean(enabled);
275
+ savePluginEnabledMap(m);
276
+ } catch { }
277
+ }
278
+
279
+ export async function loadSavedPlugins(viewer) {
280
+ const urls = getSavedPluginUrls();
281
+ if (!urls.length) return [];
282
+ const enabled = getPluginEnabledMap();
283
+ // Default to enabled when state not yet saved
284
+ const urlsToLoad = urls.filter(u => enabled[u] !== false);
285
+ return loadPlugins(viewer, urlsToLoad);
286
+ }
@@ -0,0 +1,134 @@
1
+ // PMIViewsManager.js
2
+ // Encapsulates storage and lifecycle for PMI views associated with a PartHistory instance.
3
+
4
+ import { deepClone } from '../utils/deepClone.js';
5
+
6
+ export class PMIViewsManager {
7
+ constructor(partHistory) {
8
+ this.partHistory = partHistory || null;
9
+ this.views = [];
10
+ this._listeners = new Set();
11
+ }
12
+
13
+ reset() {
14
+ this.views = [];
15
+ this._emit();
16
+ }
17
+
18
+ getViews() {
19
+ this._normalizeViewsArray(this.views);
20
+ return this.views;
21
+ }
22
+
23
+ setViews(views) {
24
+ const arr = Array.isArray(views) ? Array.from(views) : [];
25
+ this.views = arr;
26
+ this._normalizeViewsArray(this.views);
27
+ this._emit();
28
+ return this.views;
29
+ }
30
+
31
+ addView(view) {
32
+ const list = this.getViews();
33
+ const normalized = this._normalizeView(view, list.length);
34
+ list.push(normalized);
35
+ this._emit();
36
+ return normalized;
37
+ }
38
+
39
+ updateView(index, updater) {
40
+ const list = this.getViews();
41
+ const idx = Number(index);
42
+ if (!Number.isInteger(idx) || idx < 0 || idx >= list.length) return null;
43
+ const current = list[idx];
44
+ let next = current;
45
+ if (typeof updater === 'function') {
46
+ try {
47
+ const result = updater(current);
48
+ if (result && typeof result === 'object') {
49
+ next = result;
50
+ }
51
+ } catch {
52
+ // ignore updater errors
53
+ }
54
+ } else if (updater && typeof updater === 'object' && updater !== current) {
55
+ next = Object.assign({}, current, updater);
56
+ }
57
+ if (next !== current) {
58
+ list[idx] = next;
59
+ }
60
+ list[idx] = this._normalizeView(list[idx], idx);
61
+ this._emit();
62
+ return list[idx];
63
+ }
64
+
65
+ removeView(index) {
66
+ const list = this.getViews();
67
+ const idx = Number(index);
68
+ if (!Number.isInteger(idx) || idx < 0 || idx >= list.length) return null;
69
+ const [removed] = list.splice(idx, 1);
70
+ for (let i = 0; i < list.length; i++) {
71
+ list[i] = this._normalizeView(list[i], i);
72
+ }
73
+ this._emit();
74
+ return removed || null;
75
+ }
76
+
77
+ addListener(listener) {
78
+ if (typeof listener !== 'function') return () => {};
79
+ this._listeners.add(listener);
80
+ return () => {
81
+ try { this._listeners.delete(listener); } catch { }
82
+ };
83
+ }
84
+
85
+ removeListener(listener) {
86
+ if (typeof listener !== 'function') return;
87
+ try { this._listeners.delete(listener); } catch { }
88
+ }
89
+
90
+ notifyChanged() {
91
+ this._emit();
92
+ }
93
+
94
+ toSerializable() {
95
+ return this.getViews().map(view => deepClone(view));
96
+ }
97
+
98
+ _normalizeViewsArray(arrayRef) {
99
+ if (!Array.isArray(arrayRef)) {
100
+ this.views = [];
101
+ return this.views;
102
+ }
103
+ for (let i = 0; i < arrayRef.length; i++) {
104
+ arrayRef[i] = this._normalizeView(arrayRef[i], i);
105
+ }
106
+ return arrayRef;
107
+ }
108
+
109
+ _normalizeView(raw, index) {
110
+ const fallbackIndex = Number.isInteger(index) ? index : 0;
111
+ const view = (raw && typeof raw === 'object') ? raw : {};
112
+ const legacyName = typeof view.name === 'string' ? view.name.trim() : '';
113
+ const currentName = typeof view.viewName === 'string' ? view.viewName.trim() : '';
114
+ const finalName = currentName || legacyName || `View ${fallbackIndex + 1}`;
115
+ view.viewName = finalName;
116
+ view.name = finalName;
117
+
118
+ if (!Array.isArray(view.annotations)) view.annotations = [];
119
+ if (!view.camera || typeof view.camera !== 'object') view.camera = {};
120
+ if (!view.viewSettings || typeof view.viewSettings !== 'object') view.viewSettings = {};
121
+ if (view.annotationHistory && typeof view.annotationHistory !== 'object') {
122
+ delete view.annotationHistory;
123
+ }
124
+ return view;
125
+ }
126
+
127
+ _emit() {
128
+ if (!this._listeners || this._listeners.size === 0) return;
129
+ const views = this.getViews();
130
+ for (const listener of Array.from(this._listeners)) {
131
+ try { listener(views, this.partHistory || null); } catch { }
132
+ }
133
+ }
134
+ }