brep-io-kernel 1.0.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 (403) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +144 -0
  3. package/dist-kernel/brep-kernel.js +74699 -0
  4. package/dist-kernel/help/CONTRIBUTING.html +248 -0
  5. package/dist-kernel/help/LICENSE.html +248 -0
  6. package/dist-kernel/help/MODELING.png +0 -0
  7. package/dist-kernel/help/PMI.png +0 -0
  8. package/dist-kernel/help/SKETCH.png +0 -0
  9. package/dist-kernel/help/assembly-constraints__Coincident_Constraint_dialog.png +0 -0
  10. package/dist-kernel/help/assembly-constraints___Angle_Constraint_dialog.png +0 -0
  11. package/dist-kernel/help/assembly-constraints___Distance_Constraint_dialog.png +0 -0
  12. package/dist-kernel/help/assembly-constraints___Fixed_Constraint_dialog.png +0 -0
  13. package/dist-kernel/help/assembly-constraints___Parallel_Constraint_dialog.png +0 -0
  14. package/dist-kernel/help/assembly-constraints___Touch_Align_Constraint_dialog.png +0 -0
  15. package/dist-kernel/help/assembly-constraints__angle-constraint.html +248 -0
  16. package/dist-kernel/help/assembly-constraints__coincident-constraint.html +248 -0
  17. package/dist-kernel/help/assembly-constraints__distance-constraint.html +248 -0
  18. package/dist-kernel/help/assembly-constraints__fixed-constraint.html +248 -0
  19. package/dist-kernel/help/assembly-constraints__parallel-constraint.html +248 -0
  20. package/dist-kernel/help/assembly-constraints__solver.html +248 -0
  21. package/dist-kernel/help/assembly-constraints__touch-align-constraint.html +248 -0
  22. package/dist-kernel/help/brep-api.html +263 -0
  23. package/dist-kernel/help/brep-kernel.html +258 -0
  24. package/dist-kernel/help/brep-model.html +248 -0
  25. package/dist-kernel/help/cylindrical-face-radius-embedding.html +290 -0
  26. package/dist-kernel/help/dialog-screenshots.html +248 -0
  27. package/dist-kernel/help/extruded-sketch-radius-embedding.html +336 -0
  28. package/dist-kernel/help/features__Assembly_Component_dialog.png +0 -0
  29. package/dist-kernel/help/features__Boolean_dialog.png +0 -0
  30. package/dist-kernel/help/features__Chamfer_dialog.png +0 -0
  31. package/dist-kernel/help/features__Datium_dialog.png +0 -0
  32. package/dist-kernel/help/features__Extrude_dialog.png +0 -0
  33. package/dist-kernel/help/features__Fillet_dialog.png +0 -0
  34. package/dist-kernel/help/features__Helix_dialog.png +0 -0
  35. package/dist-kernel/help/features__Hole_dialog.png +0 -0
  36. package/dist-kernel/help/features__Image_Heightmap_Solid_dialog.png +0 -0
  37. package/dist-kernel/help/features__Image_to_Face_dialog.png +0 -0
  38. package/dist-kernel/help/features__Import_3D_Model_dialog.png +0 -0
  39. package/dist-kernel/help/features__Loft_dialog.png +0 -0
  40. package/dist-kernel/help/features__Mirror_dialog.png +0 -0
  41. package/dist-kernel/help/features__Offset_Shell_dialog.png +0 -0
  42. package/dist-kernel/help/features__Overlap_Cleanup_dialog.png +0 -0
  43. package/dist-kernel/help/features__Pattern_Linear_dialog.png +0 -0
  44. package/dist-kernel/help/features__Pattern_Radial_dialog.png +0 -0
  45. package/dist-kernel/help/features__Pattern_dialog.png +0 -0
  46. package/dist-kernel/help/features__Plane_dialog.png +0 -0
  47. package/dist-kernel/help/features__Primitive_Cone_dialog.png +0 -0
  48. package/dist-kernel/help/features__Primitive_Cube_dialog.png +0 -0
  49. package/dist-kernel/help/features__Primitive_Cylinder_dialog.png +0 -0
  50. package/dist-kernel/help/features__Primitive_Pyramid_dialog.png +0 -0
  51. package/dist-kernel/help/features__Primitive_Sphere_dialog.png +0 -0
  52. package/dist-kernel/help/features__Primitive_Torus_dialog.png +0 -0
  53. package/dist-kernel/help/features__Remesh_dialog.png +0 -0
  54. package/dist-kernel/help/features__Revolve_dialog.png +0 -0
  55. package/dist-kernel/help/features__Sheet_Metal_Contour_Flange_dialog.png +0 -0
  56. package/dist-kernel/help/features__Sheet_Metal_Cutout_dialog.png +0 -0
  57. package/dist-kernel/help/features__Sheet_Metal_Flange_dialog.png +0 -0
  58. package/dist-kernel/help/features__Sheet_Metal_Tab_dialog.png +0 -0
  59. package/dist-kernel/help/features__Sketch_dialog.png +0 -0
  60. package/dist-kernel/help/features__Spline_dialog.png +0 -0
  61. package/dist-kernel/help/features__Sweep_dialog.png +0 -0
  62. package/dist-kernel/help/features__Transform_dialog.png +0 -0
  63. package/dist-kernel/help/features__Tube_dialog.png +0 -0
  64. package/dist-kernel/help/features__assembly-component.html +248 -0
  65. package/dist-kernel/help/features__boolean.html +248 -0
  66. package/dist-kernel/help/features__chamfer.html +248 -0
  67. package/dist-kernel/help/features__datium.html +248 -0
  68. package/dist-kernel/help/features__datum.html +248 -0
  69. package/dist-kernel/help/features__extrude.html +248 -0
  70. package/dist-kernel/help/features__fillet.html +248 -0
  71. package/dist-kernel/help/features__helix.html +248 -0
  72. package/dist-kernel/help/features__hole.html +248 -0
  73. package/dist-kernel/help/features__image-heightmap-solid.html +248 -0
  74. package/dist-kernel/help/features__image-to-face-2D_dialog.png +0 -0
  75. package/dist-kernel/help/features__image-to-face-3D_dialog.png +0 -0
  76. package/dist-kernel/help/features__image-to-face.html +248 -0
  77. package/dist-kernel/help/features__import-3d-model.html +248 -0
  78. package/dist-kernel/help/features__index.html +248 -0
  79. package/dist-kernel/help/features__loft.html +248 -0
  80. package/dist-kernel/help/features__mirror.html +248 -0
  81. package/dist-kernel/help/features__offset-shell.html +248 -0
  82. package/dist-kernel/help/features__pattern-linear.html +248 -0
  83. package/dist-kernel/help/features__pattern-radial.html +248 -0
  84. package/dist-kernel/help/features__pattern.html +248 -0
  85. package/dist-kernel/help/features__plane.html +248 -0
  86. package/dist-kernel/help/features__primitive-cone.html +248 -0
  87. package/dist-kernel/help/features__primitive-cube.html +248 -0
  88. package/dist-kernel/help/features__primitive-cylinder.html +248 -0
  89. package/dist-kernel/help/features__primitive-pyramid.html +248 -0
  90. package/dist-kernel/help/features__primitive-sphere.html +248 -0
  91. package/dist-kernel/help/features__primitive-torus.html +248 -0
  92. package/dist-kernel/help/features__remesh.html +248 -0
  93. package/dist-kernel/help/features__revolve.html +248 -0
  94. package/dist-kernel/help/features__sheet-metal-contour-flange.html +248 -0
  95. package/dist-kernel/help/features__sheet-metal-flange.html +248 -0
  96. package/dist-kernel/help/features__sheet-metal-tab.html +248 -0
  97. package/dist-kernel/help/features__sketch.html +248 -0
  98. package/dist-kernel/help/features__spline.html +248 -0
  99. package/dist-kernel/help/features__sweep.html +248 -0
  100. package/dist-kernel/help/features__transform.html +248 -0
  101. package/dist-kernel/help/features__tube.html +248 -0
  102. package/dist-kernel/help/file-formats.html +248 -0
  103. package/dist-kernel/help/getting-started.html +248 -0
  104. package/dist-kernel/help/highlights.html +248 -0
  105. package/dist-kernel/help/history-systems.html +248 -0
  106. package/dist-kernel/help/how-it-works.html +248 -0
  107. package/dist-kernel/help/index.html +862 -0
  108. package/dist-kernel/help/input-params-schema.html +363 -0
  109. package/dist-kernel/help/inspector-improvements.html +248 -0
  110. package/dist-kernel/help/inspector.html +248 -0
  111. package/dist-kernel/help/modes__modeling.html +248 -0
  112. package/dist-kernel/help/modes__pmi.html +248 -0
  113. package/dist-kernel/help/modes__sketch.html +248 -0
  114. package/dist-kernel/help/plugins.html +248 -0
  115. package/dist-kernel/help/pmi-annotations__Angle_Dimension_dialog.png +0 -0
  116. package/dist-kernel/help/pmi-annotations__Explode_Body_dialog.png +0 -0
  117. package/dist-kernel/help/pmi-annotations__Hole_Callout_dialog.png +0 -0
  118. package/dist-kernel/help/pmi-annotations__Leader_dialog.png +0 -0
  119. package/dist-kernel/help/pmi-annotations__Linear_Dimension_dialog.png +0 -0
  120. package/dist-kernel/help/pmi-annotations__Note_dialog.png +0 -0
  121. package/dist-kernel/help/pmi-annotations__Radial_Dimension_dialog.png +0 -0
  122. package/dist-kernel/help/pmi-annotations__angle-dimension.html +248 -0
  123. package/dist-kernel/help/pmi-annotations__explode-body.html +248 -0
  124. package/dist-kernel/help/pmi-annotations__hole-callout.html +248 -0
  125. package/dist-kernel/help/pmi-annotations__index.html +248 -0
  126. package/dist-kernel/help/pmi-annotations__leader.html +248 -0
  127. package/dist-kernel/help/pmi-annotations__linear-dimension.html +248 -0
  128. package/dist-kernel/help/pmi-annotations__note.html +248 -0
  129. package/dist-kernel/help/pmi-annotations__radial-dimension.html +248 -0
  130. package/dist-kernel/help/search-index.json +464 -0
  131. package/dist-kernel/help/simplified-radial-dimensions.html +298 -0
  132. package/dist-kernel/help/solid-methods.html +359 -0
  133. package/dist-kernel/help/table-of-contents.html +330 -0
  134. package/dist-kernel/help/ui-overview.html +248 -0
  135. package/dist-kernel/help/whats-new.html +248 -0
  136. package/package.json +54 -0
  137. package/src/BREP/AssemblyComponent.js +42 -0
  138. package/src/BREP/BREP.js +43 -0
  139. package/src/BREP/BetterSolid.js +805 -0
  140. package/src/BREP/Edge.js +103 -0
  141. package/src/BREP/Extrude.js +403 -0
  142. package/src/BREP/Face.js +187 -0
  143. package/src/BREP/MeshRepairer.js +634 -0
  144. package/src/BREP/OffsetShellSolid.js +614 -0
  145. package/src/BREP/PointCloudWrap.js +302 -0
  146. package/src/BREP/Revolve.js +345 -0
  147. package/src/BREP/SolidMethods/authoring.js +112 -0
  148. package/src/BREP/SolidMethods/booleanOps.js +230 -0
  149. package/src/BREP/SolidMethods/chamfer.js +122 -0
  150. package/src/BREP/SolidMethods/edgeResolution.js +25 -0
  151. package/src/BREP/SolidMethods/fillet.js +792 -0
  152. package/src/BREP/SolidMethods/index.js +72 -0
  153. package/src/BREP/SolidMethods/io.js +105 -0
  154. package/src/BREP/SolidMethods/lifecycle.js +103 -0
  155. package/src/BREP/SolidMethods/manifoldOps.js +375 -0
  156. package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
  157. package/src/BREP/SolidMethods/meshQueries.js +264 -0
  158. package/src/BREP/SolidMethods/metadata.js +106 -0
  159. package/src/BREP/SolidMethods/metrics.js +51 -0
  160. package/src/BREP/SolidMethods/transforms.js +361 -0
  161. package/src/BREP/SolidMethods/visualize.js +508 -0
  162. package/src/BREP/SolidShared.js +26 -0
  163. package/src/BREP/Sweep.js +1596 -0
  164. package/src/BREP/Tube.js +857 -0
  165. package/src/BREP/Vertex.js +43 -0
  166. package/src/BREP/applyBooleanOperation.js +704 -0
  167. package/src/BREP/boundsUtils.js +48 -0
  168. package/src/BREP/chamfer.js +551 -0
  169. package/src/BREP/edgePolylineUtils.js +85 -0
  170. package/src/BREP/fillets/common.js +388 -0
  171. package/src/BREP/fillets/fillet.js +1422 -0
  172. package/src/BREP/fillets/filletGeometry.js +15 -0
  173. package/src/BREP/fillets/inset.js +389 -0
  174. package/src/BREP/fillets/offsetHelper.js +143 -0
  175. package/src/BREP/fillets/outset.js +88 -0
  176. package/src/BREP/helix.js +193 -0
  177. package/src/BREP/meshToBrep.js +234 -0
  178. package/src/BREP/primitives.js +279 -0
  179. package/src/BREP/setupManifold.js +71 -0
  180. package/src/BREP/threadGeometry.js +1120 -0
  181. package/src/BREP/triangleUtils.js +8 -0
  182. package/src/BREP/triangulate.js +608 -0
  183. package/src/FeatureRegistry.js +183 -0
  184. package/src/PartHistory.js +1132 -0
  185. package/src/UI/AccordionWidget.js +292 -0
  186. package/src/UI/CADmaterials.js +850 -0
  187. package/src/UI/EnvMonacoEditor.js +522 -0
  188. package/src/UI/FloatingWindow.js +396 -0
  189. package/src/UI/HistoryWidget.js +457 -0
  190. package/src/UI/MainToolbar.js +131 -0
  191. package/src/UI/ModelLibraryView.js +194 -0
  192. package/src/UI/OrthoCameraIdle.js +206 -0
  193. package/src/UI/PluginsWidget.js +280 -0
  194. package/src/UI/SceneListing.js +606 -0
  195. package/src/UI/SelectionFilter.js +629 -0
  196. package/src/UI/ViewCube.js +389 -0
  197. package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
  198. package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
  199. package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
  200. package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
  201. package/src/UI/assembly/constraintFaceUtils.js +115 -0
  202. package/src/UI/assembly/constraintHighlightUtils.js +70 -0
  203. package/src/UI/assembly/constraintLabelUtils.js +31 -0
  204. package/src/UI/assembly/constraintPointUtils.js +64 -0
  205. package/src/UI/assembly/constraintSelectionUtils.js +185 -0
  206. package/src/UI/assembly/constraintStatusUtils.js +142 -0
  207. package/src/UI/componentSelectorModal.js +240 -0
  208. package/src/UI/controls/CombinedTransformControls.js +386 -0
  209. package/src/UI/dialogs.js +351 -0
  210. package/src/UI/expressionsManager.js +100 -0
  211. package/src/UI/featureDialogWidgets/booleanField.js +25 -0
  212. package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
  213. package/src/UI/featureDialogWidgets/buttonField.js +45 -0
  214. package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
  215. package/src/UI/featureDialogWidgets/defaultField.js +23 -0
  216. package/src/UI/featureDialogWidgets/fileField.js +66 -0
  217. package/src/UI/featureDialogWidgets/index.js +34 -0
  218. package/src/UI/featureDialogWidgets/numberField.js +165 -0
  219. package/src/UI/featureDialogWidgets/optionsField.js +33 -0
  220. package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
  221. package/src/UI/featureDialogWidgets/stringField.js +24 -0
  222. package/src/UI/featureDialogWidgets/textareaField.js +28 -0
  223. package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
  224. package/src/UI/featureDialogWidgets/transformField.js +252 -0
  225. package/src/UI/featureDialogWidgets/utils.js +43 -0
  226. package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
  227. package/src/UI/featureDialogs.js +1414 -0
  228. package/src/UI/fileManagerWidget.js +615 -0
  229. package/src/UI/history/HistoryCollectionWidget.js +1294 -0
  230. package/src/UI/history/historyCollectionWidget.css.js +257 -0
  231. package/src/UI/history/historyDisplayInfo.js +133 -0
  232. package/src/UI/mobile.js +28 -0
  233. package/src/UI/objectDump.js +442 -0
  234. package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
  235. package/src/UI/pmi/AnnotationHistory.js +353 -0
  236. package/src/UI/pmi/AnnotationRegistry.js +90 -0
  237. package/src/UI/pmi/BaseAnnotation.js +269 -0
  238. package/src/UI/pmi/LabelOverlay.css +102 -0
  239. package/src/UI/pmi/LabelOverlay.js +191 -0
  240. package/src/UI/pmi/PMIMode.js +1550 -0
  241. package/src/UI/pmi/PMIViewsWidget.js +1098 -0
  242. package/src/UI/pmi/annUtils.js +729 -0
  243. package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
  244. package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
  245. package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
  246. package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
  247. package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
  248. package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
  249. package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
  250. package/src/UI/pmi/pmiStyle.js +44 -0
  251. package/src/UI/sketcher/SketchMode3D.js +4095 -0
  252. package/src/UI/sketcher/dimensions.js +674 -0
  253. package/src/UI/sketcher/glyphs.js +236 -0
  254. package/src/UI/sketcher/highlights.js +60 -0
  255. package/src/UI/toolbarButtons/aboutButton.js +5 -0
  256. package/src/UI/toolbarButtons/exportButton.js +609 -0
  257. package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
  258. package/src/UI/toolbarButtons/importButton.js +160 -0
  259. package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
  260. package/src/UI/toolbarButtons/metadataButton.js +1063 -0
  261. package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
  262. package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
  263. package/src/UI/toolbarButtons/saveButton.js +99 -0
  264. package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
  265. package/src/UI/toolbarButtons/testsButton.js +26 -0
  266. package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
  267. package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
  268. package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
  269. package/src/UI/triangleDebuggerWindow.js +945 -0
  270. package/src/UI/viewer.js +4228 -0
  271. package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
  272. package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
  273. package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
  274. package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
  275. package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
  276. package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
  277. package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
  278. package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
  279. package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
  280. package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
  281. package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
  282. package/src/core/entities/HistoryCollectionBase.js +72 -0
  283. package/src/core/entities/ListEntityBase.js +109 -0
  284. package/src/core/entities/schemaProcesser.js +121 -0
  285. package/src/exporters/sheetMetalFlatPattern.js +659 -0
  286. package/src/exporters/sheetMetalUnfold.js +862 -0
  287. package/src/exporters/step.js +1135 -0
  288. package/src/exporters/threeMF.js +575 -0
  289. package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
  290. package/src/features/boolean/BooleanFeature.js +94 -0
  291. package/src/features/chamfer/ChamferFeature.js +116 -0
  292. package/src/features/datium/DatiumFeature.js +80 -0
  293. package/src/features/edgeFeatureUtils.js +41 -0
  294. package/src/features/extrude/ExtrudeFeature.js +143 -0
  295. package/src/features/fillet/FilletFeature.js +197 -0
  296. package/src/features/helix/HelixFeature.js +405 -0
  297. package/src/features/hole/HoleFeature.js +1050 -0
  298. package/src/features/hole/screwClearance.js +86 -0
  299. package/src/features/hole/threadDesignationCatalog.js +149 -0
  300. package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
  301. package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
  302. package/src/features/imageToFace/imageEditor.js +1270 -0
  303. package/src/features/imageToFace/traceUtils.js +971 -0
  304. package/src/features/import3dModel/Import3dModelFeature.js +151 -0
  305. package/src/features/loft/LoftFeature.js +605 -0
  306. package/src/features/mirror/MirrorFeature.js +151 -0
  307. package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
  308. package/src/features/offsetShell/OffsetShellFeature.js +89 -0
  309. package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
  310. package/src/features/pattern/PatternFeature.js +275 -0
  311. package/src/features/patternLinear/PatternLinearFeature.js +120 -0
  312. package/src/features/patternRadial/PatternRadialFeature.js +186 -0
  313. package/src/features/plane/PlaneFeature.js +154 -0
  314. package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
  315. package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
  316. package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
  317. package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
  318. package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
  319. package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
  320. package/src/features/remesh/RemeshFeature.js +97 -0
  321. package/src/features/revolve/RevolveFeature.js +111 -0
  322. package/src/features/selectionUtils.js +118 -0
  323. package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
  324. package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
  325. package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
  326. package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
  327. package/src/features/sheetMetal/SheetMetalObject.js +141 -0
  328. package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
  329. package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
  330. package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
  331. package/src/features/sheetMetal/profileUtils.js +25 -0
  332. package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
  333. package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
  334. package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
  335. package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
  336. package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
  337. package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
  338. package/src/features/sheetMetal/sheetMetalTree.js +210 -0
  339. package/src/features/sketch/SketchFeature.js +955 -0
  340. package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
  341. package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
  342. package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
  343. package/src/features/spline/SplineEditorSession.js +988 -0
  344. package/src/features/spline/SplineFeature.js +1388 -0
  345. package/src/features/spline/splineUtils.js +218 -0
  346. package/src/features/sweep/SweepFeature.js +110 -0
  347. package/src/features/transform/TransformFeature.js +152 -0
  348. package/src/features/tube/TubeFeature.js +635 -0
  349. package/src/fs.proxy.js +625 -0
  350. package/src/idbStorage.js +254 -0
  351. package/src/index.js +12 -0
  352. package/src/main.js +15 -0
  353. package/src/metadataManager.js +64 -0
  354. package/src/path.proxy.js +277 -0
  355. package/src/plugins/ghLoader.worker.js +151 -0
  356. package/src/plugins/pluginManager.js +286 -0
  357. package/src/pmi/PMIViewsManager.js +134 -0
  358. package/src/services/componentLibrary.js +198 -0
  359. package/src/tests/ConsoleCapture.js +189 -0
  360. package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
  361. package/src/tests/browserTests.js +597 -0
  362. package/src/tests/debugBoolean.js +225 -0
  363. package/src/tests/partFiles/badBoolean.json +957 -0
  364. package/src/tests/partFiles/extrudeTest.json +88 -0
  365. package/src/tests/partFiles/filletFail.json +58 -0
  366. package/src/tests/partFiles/import_TEst.part.part.json +646 -0
  367. package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
  368. package/src/tests/test_boolean_subtract.js +27 -0
  369. package/src/tests/test_chamfer.js +17 -0
  370. package/src/tests/test_extrudeFace.js +24 -0
  371. package/src/tests/test_fillet.js +17 -0
  372. package/src/tests/test_fillet_nonClosed.js +45 -0
  373. package/src/tests/test_filletsMoreDifficult.js +46 -0
  374. package/src/tests/test_history_features_basic.js +149 -0
  375. package/src/tests/test_hole.js +282 -0
  376. package/src/tests/test_mirror.js +16 -0
  377. package/src/tests/test_offsetShellGrouping.js +85 -0
  378. package/src/tests/test_plane.js +4 -0
  379. package/src/tests/test_primitiveCone.js +11 -0
  380. package/src/tests/test_primitiveCube.js +7 -0
  381. package/src/tests/test_primitiveCylinder.js +8 -0
  382. package/src/tests/test_primitivePyramid.js +9 -0
  383. package/src/tests/test_primitiveSphere.js +17 -0
  384. package/src/tests/test_primitiveTorus.js +21 -0
  385. package/src/tests/test_pushFace.js +126 -0
  386. package/src/tests/test_sheetMetalContourFlange.js +125 -0
  387. package/src/tests/test_sheetMetal_features.js +80 -0
  388. package/src/tests/test_sketch_openLoop.js +45 -0
  389. package/src/tests/test_solidMetrics.js +58 -0
  390. package/src/tests/test_stlLoader.js +1889 -0
  391. package/src/tests/test_sweepFace.js +55 -0
  392. package/src/tests/test_tube.js +45 -0
  393. package/src/tests/test_tube_closedLoop.js +67 -0
  394. package/src/tests/tests.js +493 -0
  395. package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
  396. package/src/tools/dialogCapturePageFactory.js +227 -0
  397. package/src/tools/featureDialogCapturePage.js +47 -0
  398. package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
  399. package/src/utils/axisHelpers.js +99 -0
  400. package/src/utils/deepClone.js +69 -0
  401. package/src/utils/geometryTolerance.js +37 -0
  402. package/src/utils/normalizeTypeString.js +8 -0
  403. package/src/utils/xformMath.js +51 -0
@@ -0,0 +1,1294 @@
1
+ import { SchemaForm } from '../featureDialogs.js';
2
+ import { resolveEntryId, resolveHistoryDisplayInfo } from './historyDisplayInfo.js';
3
+ import { HISTORY_COLLECTION_WIDGET_CSS } from './historyCollectionWidget.css.js';
4
+
5
+ export const HISTORY_COLLECTION_REFRESH_EVENT = 'brep:history-collections-refresh';
6
+
7
+ const noop = () => {};
8
+
9
+ /**
10
+ * Generic collection widget that renders HistoryCollectionBase-like lists using SchemaForm.
11
+ * Supports optional hooks for form customization, expansion state, and toggle notifications.
12
+ */
13
+ export class HistoryCollectionWidget {
14
+ constructor({
15
+ history = null,
16
+ viewer = null,
17
+ onEntryChange = null,
18
+ onCollectionChange = null,
19
+ formOptions = null,
20
+ determineInitialExpanded = null,
21
+ onEntryToggle = null,
22
+ onFormReady = null,
23
+ autoSyncOpenState = false,
24
+ createEntry = null,
25
+ decorateEntryHeader = null,
26
+ buildEntryControls = null,
27
+ entryToggle = null,
28
+ } = {}) {
29
+ this.history = null;
30
+ this.viewer = viewer || null;
31
+ this.onEntryChange = typeof onEntryChange === 'function' ? onEntryChange : noop;
32
+ this.onCollectionChange = typeof onCollectionChange === 'function' ? onCollectionChange : noop;
33
+ this._formOptionsProvider = typeof formOptions === 'function' ? formOptions : null;
34
+ this._determineExpanded = typeof determineInitialExpanded === 'function' ? determineInitialExpanded : null;
35
+ this._onEntryToggle = typeof onEntryToggle === 'function' ? onEntryToggle : null;
36
+ this._onFormReady = typeof onFormReady === 'function' ? onFormReady : null;
37
+ this._autoSyncOpenState = Boolean(autoSyncOpenState);
38
+ this._createEntryFunc = typeof createEntry === 'function' ? createEntry : null;
39
+ this._decorateEntryHeader = typeof decorateEntryHeader === 'function' ? decorateEntryHeader : null;
40
+ this._buildEntryControls = typeof buildEntryControls === 'function' ? buildEntryControls : null;
41
+ this._entryToggleConfig = this._normalizeEntryToggle(entryToggle);
42
+ this._addBtn = null;
43
+ this._addMenu = null;
44
+ this._onGlobalClick = null;
45
+ this._globalRefreshHandler = null;
46
+
47
+ this.uiElement = document.createElement('div');
48
+ this.uiElement.className = 'history-collection-widget-host';
49
+ this._shadow = this.uiElement.attachShadow({ mode: 'open' });
50
+ this._shadow.appendChild(this._makeStyle());
51
+
52
+ this._container = document.createElement('div');
53
+ this._container.className = 'hc-widget';
54
+ this._shadow.appendChild(this._container);
55
+
56
+ this._listEl = document.createElement('div');
57
+ this._listEl.className = 'hc-list';
58
+ this._container.appendChild(this._listEl);
59
+
60
+ this._footer = this._buildFooter();
61
+ this._container.appendChild(this._footer);
62
+
63
+ this._onGlobalClick = (ev) => {
64
+ if (!this._footer) return;
65
+ const target = ev?.target || null;
66
+ const path = typeof ev?.composedPath === 'function' ? ev.composedPath() : null;
67
+ const canCheckNode = typeof Node !== 'undefined';
68
+ const isInScope = (el) => {
69
+ if (!el) return false;
70
+ if (path && path.includes(el)) return true;
71
+ if (target && canCheckNode && target instanceof Node) {
72
+ return typeof el.contains === 'function' ? el.contains(target) : false;
73
+ }
74
+ return false;
75
+ };
76
+ if (isInScope(this._addMenu) || isInScope(this._addBtn)) {
77
+ return;
78
+ }
79
+ this._toggleAddMenu(false);
80
+ };
81
+ try {
82
+ document.addEventListener('mousedown', this._onGlobalClick, true);
83
+ } catch {
84
+ this._onGlobalClick = null;
85
+ }
86
+
87
+ this._installGlobalRefreshHandler();
88
+
89
+ this._expandedId = null;
90
+ this._titleEls = new Map();
91
+ this._metaEls = new Map();
92
+ this._itemEls = new Map();
93
+ this._forms = new Map();
94
+ this._uiFieldSignatures = new Map();
95
+ this._boundHistoryListener = null;
96
+ this._listenerUnsub = null;
97
+ this._suppressHistoryListener = false;
98
+
99
+ if (history) this.setHistory(history);
100
+ }
101
+
102
+ dispose() {
103
+ if (typeof this._listenerUnsub === 'function') {
104
+ try { this._listenerUnsub(); } catch (_) {}
105
+ }
106
+ this._listenerUnsub = null;
107
+ this._boundHistoryListener = null;
108
+ this._expandedId = null;
109
+ this._destroyAllForms();
110
+ this._titleEls.clear();
111
+ this._metaEls.clear();
112
+ this._itemEls.clear();
113
+ this._uiFieldSignatures.clear();
114
+ this._addBtn = null;
115
+ this._addMenu = null;
116
+ if (this._onGlobalClick) {
117
+ try { document.removeEventListener('mousedown', this._onGlobalClick, true); } catch (_) { /* ignore */ }
118
+ }
119
+ this._onGlobalClick = null;
120
+ if (this._globalRefreshHandler) {
121
+ try { window.removeEventListener(HISTORY_COLLECTION_REFRESH_EVENT, this._globalRefreshHandler); }
122
+ catch (_) { /* ignore */ }
123
+ }
124
+ this._globalRefreshHandler = null;
125
+ }
126
+
127
+ setHistory(history) {
128
+ if (history === this.history) {
129
+ this.render();
130
+ return;
131
+ }
132
+ this._uiFieldSignatures.clear();
133
+ if (typeof this._listenerUnsub === 'function') {
134
+ try { this._listenerUnsub(); } catch (_) {}
135
+ }
136
+ this._listenerUnsub = null;
137
+ this._boundHistoryListener = null;
138
+ this.history = history || null;
139
+ if (this.history) this._subscribeToHistory(this.history);
140
+ this._expandedId = null;
141
+ this.render();
142
+ }
143
+
144
+ render() {
145
+ this._toggleAddMenu(false);
146
+ this._refreshAddMenu();
147
+ this._titleEls.clear();
148
+ this._metaEls.clear();
149
+ this._itemEls.clear();
150
+ this._destroyAllForms();
151
+ const entries = this._getEntries();
152
+ this._listEl.textContent = '';
153
+
154
+ if (!entries.length) {
155
+ const empty = document.createElement('div');
156
+ empty.className = 'hc-empty';
157
+ empty.textContent = 'No entries yet.';
158
+ this._listEl.appendChild(empty);
159
+ return;
160
+ }
161
+
162
+ const determineExpanded = this._determineExpanded || (this._autoSyncOpenState ? this._defaultDetermineExpanded.bind(this) : null);
163
+ const entryIds = entries.map((entry, index) => this._extractEntryId(entry, index));
164
+ const validIds = new Set(entryIds);
165
+ let targetId = (this._expandedId && validIds.has(this._expandedId)) ? this._expandedId : null;
166
+
167
+ if (determineExpanded) {
168
+ let determinedId = null;
169
+ for (let i = 0; i < entries.length; i++) {
170
+ const entry = entries[i];
171
+ try {
172
+ const shouldOpen = !!determineExpanded(entry, i);
173
+ if (shouldOpen) {
174
+ determinedId = entryIds[i];
175
+ break;
176
+ }
177
+ } catch (_) { /* ignore */ }
178
+ }
179
+ if (determinedId != null) {
180
+ targetId = determinedId;
181
+ }
182
+ }
183
+
184
+ if (targetId && !validIds.has(targetId)) {
185
+ targetId = null;
186
+ }
187
+ this._expandedId = targetId;
188
+
189
+ for (let i = 0; i < entries.length; i++) {
190
+ const entry = entries[i];
191
+ const id = entryIds[i];
192
+ const itemEl = this._renderEntry(entry, id, i, targetId === id, entries.length);
193
+ this._listEl.appendChild(itemEl);
194
+ }
195
+ }
196
+
197
+ _destroyForm(id) {
198
+ if (id == null) return;
199
+ const key = String(id);
200
+ const form = this._forms.get(key);
201
+ if (!form) return;
202
+ try {
203
+ if (typeof form.destroy === 'function') form.destroy();
204
+ } catch (_) { /* ignore form destroy errors */ }
205
+ this._forms.delete(key);
206
+ }
207
+
208
+ _destroyAllForms() {
209
+ if (!this._forms || this._forms.size === 0) return;
210
+ const keys = Array.from(this._forms.keys());
211
+ for (const key of keys) {
212
+ this._destroyForm(key);
213
+ }
214
+ }
215
+
216
+ getFormForEntry(id) {
217
+ return this._forms.get(String(id)) || null;
218
+ }
219
+
220
+ _getEntries() {
221
+ if (!this.history) return [];
222
+ if (Array.isArray(this.history.entries)) return this.history.entries;
223
+ if (Array.isArray(this.history.features)) return this.history.features;
224
+ return [];
225
+ }
226
+
227
+ _findEntryInfoById(targetId) {
228
+ if (targetId == null) return null;
229
+ const id = String(targetId);
230
+ const entries = this._getEntries();
231
+ for (let i = 0; i < entries.length; i++) {
232
+ const entry = entries[i];
233
+ if (this._extractEntryId(entry, i) === id) {
234
+ return { entry, index: i };
235
+ }
236
+ }
237
+ return null;
238
+ }
239
+
240
+ _extractEntryId(entry, index) {
241
+ return resolveEntryId(entry, index);
242
+ }
243
+
244
+ _applyDisplayInfo(entry, index, entryId, elements = {}) {
245
+ const info = resolveHistoryDisplayInfo(entry, {
246
+ history: this.history,
247
+ index,
248
+ });
249
+ if (elements.titleEl) {
250
+ elements.titleEl.textContent = info.name || '';
251
+ }
252
+ if (elements.metaEl) {
253
+ const parts = [];
254
+ if (info.id) parts.push(`#${info.id}`);
255
+ if (info.statusText) parts.push(info.statusText);
256
+ elements.metaEl.textContent = parts.join(' · ');
257
+ elements.metaEl.title = info.statusTitle || '';
258
+ elements.metaEl.style.color = info.statusColor || '';
259
+ }
260
+ if (elements.item) {
261
+ elements.item.classList.toggle('has-error', Boolean(info.hasError));
262
+ }
263
+ return info;
264
+ }
265
+
266
+ _renderEntry(entry, id, index, isOpen = false, totalCount = 0) {
267
+ const entryId = id != null ? String(id) : `entry-${index}`;
268
+ const item = document.createElement('div');
269
+ item.className = 'hc-item';
270
+ item.dataset.entryId = entryId;
271
+ if (isOpen) item.classList.add('open');
272
+
273
+ const renderContext = this._createEntryRenderContext({
274
+ entry,
275
+ id: entryId,
276
+ index,
277
+ isOpen,
278
+ totalCount,
279
+ item,
280
+ });
281
+
282
+ const headerRow = document.createElement('div');
283
+ headerRow.className = 'hc-header-row';
284
+
285
+ const toggleControl = this._maybeRenderEntryToggle(renderContext);
286
+ if (toggleControl) headerRow.appendChild(toggleControl);
287
+
288
+ const toggle = document.createElement('button');
289
+ toggle.type = 'button';
290
+ toggle.className = 'hc-toggle';
291
+ toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
292
+ toggle.addEventListener('click', () => { this._toggleEntry(entryId); });
293
+
294
+ const toggleMain = document.createElement('span');
295
+ toggleMain.className = 'hc-toggle-main';
296
+ const title = document.createElement('span');
297
+ title.className = 'hc-title';
298
+ this._titleEls.set(entryId, title);
299
+ toggleMain.appendChild(title);
300
+ const subline = document.createElement('span');
301
+ subline.className = 'hc-subline';
302
+ const meta = document.createElement('span');
303
+ meta.className = 'hc-meta';
304
+ this._metaEls.set(entryId, meta);
305
+ subline.appendChild(meta);
306
+
307
+ toggleMain.appendChild(subline);
308
+
309
+ toggle.appendChild(toggleMain);
310
+ headerRow.appendChild(toggle);
311
+
312
+ const controls = document.createElement('div');
313
+ controls.className = 'hc-controls';
314
+ headerRow.appendChild(controls);
315
+ item.appendChild(headerRow);
316
+
317
+ renderContext.elements = {
318
+ item,
319
+ headerRow,
320
+ toggleControl,
321
+ toggle,
322
+ toggleMain,
323
+ titleEl: title,
324
+ metaEl: meta,
325
+ controlsEl: controls,
326
+ bodyEl: null,
327
+ };
328
+ this._itemEls.set(entryId, item);
329
+ const displayInfo = this._applyDisplayInfo(entry, index, entryId, renderContext.elements);
330
+
331
+ const controlsConfig = this._buildControlsForEntry(renderContext);
332
+ this._renderControls(controls, controlsConfig, renderContext);
333
+
334
+ const body = document.createElement('div');
335
+ body.className = 'hc-body';
336
+ body.hidden = !isOpen;
337
+
338
+ if (isOpen) {
339
+ const schema = this._resolveSchema(entry);
340
+ const filtered = this._filterSchemaForEntry(entry, schema, entryId);
341
+ const effectiveSchema = filtered.schema;
342
+ this._recordUiFieldSignature(entryId, filtered.visibleKeys);
343
+ if (!schema) {
344
+ const missing = document.createElement('div');
345
+ missing.className = 'hc-missing';
346
+ missing.textContent = `No schema available for "${displayInfo?.name || 'entity'}".`;
347
+ body.appendChild(missing);
348
+ } else {
349
+ const hasFields = effectiveSchema && Object.keys(effectiveSchema).length > 0;
350
+ if (!hasFields) {
351
+ const empty = document.createElement('div');
352
+ empty.className = 'hc-missing';
353
+ empty.textContent = 'No inputs available for this configuration.';
354
+ body.appendChild(empty);
355
+ } else {
356
+ const params = entry && entry.inputParams ? entry.inputParams : {};
357
+ const contextInfo = {
358
+ entry,
359
+ id: entryId,
360
+ index,
361
+ schema: effectiveSchema,
362
+ params,
363
+ };
364
+ let formRef = null;
365
+ const options = this._composeFormOptions(contextInfo, () => formRef);
366
+ options.excludeKeys = Array.isArray(options.excludeKeys) ? options.excludeKeys : [];
367
+ if (!options.excludeKeys.includes('id')) {
368
+ options.excludeKeys = [...options.excludeKeys, 'id'];
369
+ }
370
+
371
+ if (!Object.prototype.hasOwnProperty.call(options, 'viewer')) {
372
+ options.viewer = this.viewer || null;
373
+ }
374
+ if (!Object.prototype.hasOwnProperty.call(options, 'scene')) {
375
+ options.scene = this.viewer && this.viewer.scene ? this.viewer.scene : null;
376
+ }
377
+ if (!Object.prototype.hasOwnProperty.call(options, 'partHistory')) {
378
+ options.partHistory = this.history || null;
379
+ }
380
+
381
+ const form = new SchemaForm(effectiveSchema || {}, params, options);
382
+ formRef = form;
383
+ body.appendChild(form.uiElement);
384
+ this._forms.set(entryId, form);
385
+ if (this._onFormReady) {
386
+ try { this._onFormReady({ id: entryId, index, entry, form }); } catch (_) { /* ignore */ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ renderContext.elements.bodyEl = body;
393
+ this._applyEntryHeaderDecorators(renderContext);
394
+
395
+ item.appendChild(body);
396
+ return item;
397
+ }
398
+
399
+ _toggleEntry(id) {
400
+ if (id == null) return;
401
+ const targetId = String(id);
402
+ const currentId = this._expandedId;
403
+ const targetInfo = this._findEntryInfoById(targetId);
404
+ const targetEntry = targetInfo?.entry || null;
405
+
406
+ if (currentId === targetId) {
407
+ if (this._autoSyncOpenState && targetEntry) {
408
+ this._applyOpenState(targetEntry, false);
409
+ }
410
+ this._expandedId = null;
411
+ this.render();
412
+ this._notifyEntryToggle(targetEntry, false);
413
+ return;
414
+ }
415
+
416
+ const previousInfo = currentId ? this._findEntryInfoById(currentId) : null;
417
+ if (this._autoSyncOpenState) {
418
+ if (previousInfo?.entry) this._applyOpenState(previousInfo.entry, false);
419
+ if (targetEntry) this._applyOpenState(targetEntry, true);
420
+ }
421
+ this._expandedId = targetEntry ? targetId : null;
422
+ this.render();
423
+ if (previousInfo?.entry) this._notifyEntryToggle(previousInfo.entry, false);
424
+ if (targetEntry) this._notifyEntryToggle(targetEntry, true);
425
+ }
426
+
427
+ _createEntryRenderContext({
428
+ entry = null,
429
+ id = null,
430
+ index = 0,
431
+ isOpen = false,
432
+ totalCount = 0,
433
+ item = null,
434
+ } = {}) {
435
+ const entryId = id != null ? String(id) : null;
436
+ const baseContext = {
437
+ widget: this,
438
+ history: this.history || null,
439
+ viewer: this.viewer || null,
440
+ entry,
441
+ id: entryId,
442
+ index,
443
+ isOpen,
444
+ totalCount,
445
+ item,
446
+ getForm: () => {
447
+ if (entryId == null) return null;
448
+ return this.getFormForEntry(entryId);
449
+ },
450
+ getHelpers: () => this._createHelperContext({
451
+ entry,
452
+ id: entryId,
453
+ index,
454
+ form: entryId == null ? null : this.getFormForEntry(entryId),
455
+ }),
456
+ };
457
+ return baseContext;
458
+ }
459
+
460
+ _buildControlsForEntry(context) {
461
+ const defaults = this._defaultControlDescriptors(context);
462
+ if (typeof this._buildEntryControls === 'function') {
463
+ try {
464
+ const clone = defaults.map((descriptor) => ({ ...descriptor }));
465
+ const custom = this._buildEntryControls(context, clone);
466
+ if (Array.isArray(custom)) return custom;
467
+ if (custom === null) return [];
468
+ } catch (_) { /* ignore custom control errors */ }
469
+ }
470
+ return defaults;
471
+ }
472
+
473
+ _defaultControlDescriptors(context = {}) {
474
+ const { id = null, index = 0, totalCount = 0 } = context;
475
+ if (id == null) return [];
476
+ const descriptors = [];
477
+ descriptors.push({
478
+ key: 'move-up',
479
+ label: '△',
480
+ title: 'Move up',
481
+ disabled: index <= 0,
482
+ onClick: () => { this._moveEntry(id, -1); },
483
+ });
484
+ descriptors.push({
485
+ key: 'move-down',
486
+ label: '▽',
487
+ title: 'Move down',
488
+ disabled: index >= totalCount - 1,
489
+ onClick: () => { this._moveEntry(id, 1); },
490
+ });
491
+ descriptors.push({
492
+ key: 'delete',
493
+ label: '✕',
494
+ title: 'Delete',
495
+ className: 'hc-btn danger',
496
+ onClick: () => { this._deleteEntry(id); },
497
+ });
498
+ return descriptors;
499
+ }
500
+
501
+ _renderControls(container, descriptors, context) {
502
+ container.textContent = '';
503
+ let count = 0;
504
+ if (Array.isArray(descriptors)) {
505
+ for (const descriptor of descriptors) {
506
+ if (!descriptor) continue;
507
+ if (descriptor instanceof HTMLElement) {
508
+ container.appendChild(descriptor);
509
+ count += 1;
510
+ continue;
511
+ }
512
+ const btn = document.createElement(descriptor.tagName || 'button');
513
+ if (btn.tagName === 'BUTTON') {
514
+ btn.type = descriptor.type || 'button';
515
+ }
516
+ btn.className = descriptor.className || 'hc-btn';
517
+ if (descriptor.title) btn.title = descriptor.title;
518
+ if (descriptor.label != null) btn.textContent = descriptor.label;
519
+ if (descriptor.disabled) btn.disabled = true;
520
+ if (!descriptor.disabled && typeof descriptor.onClick === 'function') {
521
+ btn.addEventListener('click', (ev) => {
522
+ if (descriptor.stopPropagation !== false) {
523
+ ev.preventDefault();
524
+ ev.stopPropagation();
525
+ }
526
+ try { descriptor.onClick(ev, context); } catch (_) { /* ignore */ }
527
+ });
528
+ }
529
+ container.appendChild(btn);
530
+ count += 1;
531
+ }
532
+ }
533
+ container.hidden = count === 0;
534
+ }
535
+
536
+ _maybeRenderEntryToggle(context = {}) {
537
+ const config = this._entryToggleConfig;
538
+ if (!config) return null;
539
+ let isEnabled = true;
540
+ let disabled = false;
541
+ let title = 'Enable entry';
542
+ try {
543
+ isEnabled = !!config.isEnabled(context);
544
+ } catch {
545
+ isEnabled = true;
546
+ }
547
+ try {
548
+ disabled = config.isDisabled ? !!config.isDisabled(context) : false;
549
+ } catch {
550
+ disabled = false;
551
+ }
552
+ try {
553
+ if (config.getTitle) {
554
+ title = config.getTitle(context) || title;
555
+ }
556
+ } catch {
557
+ /* ignore */
558
+ }
559
+ const wrap = document.createElement('label');
560
+ wrap.className = config.className || 'hc-entry-toggle';
561
+ if (title) wrap.title = title;
562
+ const checkbox = document.createElement('input');
563
+ checkbox.type = 'checkbox';
564
+ checkbox.className = 'hc-entry-toggle-checkbox';
565
+ checkbox.checked = isEnabled;
566
+ checkbox.disabled = disabled;
567
+ if (title) checkbox.setAttribute('aria-label', title);
568
+ checkbox.addEventListener('click', (ev) => {
569
+ ev.stopPropagation();
570
+ });
571
+ checkbox.addEventListener('change', (ev) => {
572
+ ev.stopPropagation();
573
+ if (checkbox.disabled) return;
574
+ try {
575
+ config.setEnabled(context, checkbox.checked);
576
+ } catch {
577
+ // ignore
578
+ }
579
+ });
580
+ wrap.appendChild(checkbox);
581
+ return wrap;
582
+ }
583
+
584
+ _applyEntryHeaderDecorators(context) {
585
+ if (!this._decorateEntryHeader) return;
586
+ try {
587
+ this._decorateEntryHeader(context);
588
+ } catch (_) { /* ignore decorator errors */ }
589
+ }
590
+
591
+ _notifyEntryToggle(entry, isOpen) {
592
+ if (!this._onEntryToggle) return;
593
+ try {
594
+ this._onEntryToggle(entry || null, isOpen);
595
+ } catch (_) { /* ignore toggle hook errors */ }
596
+ }
597
+
598
+ async _moveEntry(id, delta) {
599
+ if (!id) return;
600
+ const entries = this._getEntries();
601
+ const idx = entries.findIndex((entry, i) => this._extractEntryId(entry, i) === id);
602
+ if (idx < 0) return;
603
+ const target = idx + delta;
604
+ if (target < 0 || target >= entries.length) return;
605
+ const [entry] = entries.splice(idx, 1);
606
+ entries.splice(target, 0, entry);
607
+ if (id != null) this._expandedId = String(id);
608
+ this.render();
609
+ this._emitCollectionChange('reorder', entry);
610
+ }
611
+
612
+ _deleteEntry(id) {
613
+ if (!id) return;
614
+ const entries = this._getEntries();
615
+ const idx = entries.findIndex((entry, i) => this._extractEntryId(entry, i) === id);
616
+ if (idx < 0) return;
617
+ const [removed] = entries.splice(idx, 1);
618
+ if (this._expandedId && String(id) === this._expandedId) {
619
+ this._expandedId = null;
620
+ }
621
+ this._destroyForm(id);
622
+ if (this._autoSyncOpenState && removed) {
623
+ this._applyOpenState(removed, false);
624
+ }
625
+ this._uiFieldSignatures.delete(String(id));
626
+ this.render();
627
+ this._emitCollectionChange('remove', removed);
628
+ }
629
+
630
+ async _handleAddEntry(typeStr) {
631
+ this._toggleAddMenu(false);
632
+ if (!typeStr) return;
633
+ let createdEntryId = null;
634
+ if (typeof this._createEntryFunc === 'function') {
635
+ let entry = null;
636
+ try {
637
+ entry = await this._createEntryFunc(typeStr);
638
+ } catch (error) {
639
+ console.warn('[HistoryCollectionWidget] Failed to create entry:', error);
640
+ return;
641
+ }
642
+ if (!entry) return;
643
+ try {
644
+ const entries = this._getEntries();
645
+ const idx = entries.indexOf(entry);
646
+ const id = this._extractEntryId(entry, idx >= 0 ? idx : entries.length - 1);
647
+ if (id != null) {
648
+ const normalizedId = String(id);
649
+ const previousId = this._expandedId;
650
+ if (this._autoSyncOpenState && previousId && previousId !== normalizedId) {
651
+ const prevInfo = this._findEntryInfoById(previousId);
652
+ if (prevInfo?.entry) this._applyOpenState(prevInfo.entry, false);
653
+ }
654
+ if (this._autoSyncOpenState) this._applyOpenState(entry, true);
655
+ this._expandedId = normalizedId;
656
+ createdEntryId = normalizedId;
657
+ }
658
+ } catch (_) { /* ignore */ }
659
+ this.render();
660
+ this._emitCollectionChange('add', entry);
661
+ this._deferScrollToEntry(createdEntryId);
662
+ return;
663
+ }
664
+ const entry = await this._instantiateEntryForType(typeStr);
665
+ if (!entry) return;
666
+ const entries = this._getEntries();
667
+ entries.push(entry);
668
+ const id = this._extractEntryId(entry, entries.length - 1);
669
+ if (id != null) {
670
+ const normalizedId = String(id);
671
+ const previousId = this._expandedId;
672
+ if (this._autoSyncOpenState && previousId && previousId !== normalizedId) {
673
+ const prevInfo = this._findEntryInfoById(previousId);
674
+ if (prevInfo?.entry) this._applyOpenState(prevInfo.entry, false);
675
+ }
676
+ if (this._autoSyncOpenState) this._applyOpenState(entry, true);
677
+ this._expandedId = normalizedId;
678
+ createdEntryId = normalizedId;
679
+ }
680
+ this.render();
681
+ this._emitCollectionChange('add', entry);
682
+ this._deferScrollToEntry(createdEntryId);
683
+ }
684
+
685
+ _handleSchemaChange(id, entry, details) {
686
+ this._updateTitleElement(id, entry);
687
+ try {
688
+ this.onEntryChange({ id, entry, details, history: this.history });
689
+ } catch (_) { /* ignore */ }
690
+ this._emitCollectionChange('update', entry);
691
+ }
692
+
693
+ _updateTitleElement(id, entry) {
694
+ const entries = this._getEntries();
695
+ const idx = entries.findIndex((it, i) => this._extractEntryId(it, i) === id);
696
+ this._applyDisplayInfo(entry, idx >= 0 ? idx : 0, id, {
697
+ titleEl: this._titleEls.get(id),
698
+ metaEl: this._metaEls.get(id),
699
+ typeEl: null,
700
+ item: this._itemEls.get(id),
701
+ });
702
+ }
703
+
704
+ _resolveSchema(entry) {
705
+ if (!entry) return null;
706
+ const type = entry.type || entry.entityType || (entry.inputParams && entry.inputParams.type);
707
+ const registry = this.history && this.history.registry ? this.history.registry : null;
708
+ if (type && registry) {
709
+ if (typeof registry.resolve === 'function') {
710
+ const resolved = registry.resolve(type);
711
+ if (resolved && resolved.inputParamsSchema) return resolved.inputParamsSchema;
712
+ }
713
+ const classes = registry.entityClasses;
714
+ if (classes instanceof Map) {
715
+ if (classes.has(type)) {
716
+ const found = classes.get(type);
717
+ if (found && found.inputParamsSchema) return found.inputParamsSchema;
718
+ }
719
+ for (const value of classes.values()) {
720
+ if (!value) continue;
721
+ if ((value.entityType && value.entityType === type) || (value.shortName && value.shortName === type)) {
722
+ if (value.inputParamsSchema) return value.inputParamsSchema;
723
+ }
724
+ }
725
+ }
726
+ }
727
+ if (entry.constructor && entry.constructor.inputParamsSchema) {
728
+ return entry.constructor.inputParamsSchema;
729
+ }
730
+ return null;
731
+ }
732
+
733
+ async _instantiateEntryForType(typeStr) {
734
+ const history = this.history;
735
+ if (!history) return null;
736
+ const EntityClass = this._resolveEntityClass(typeStr);
737
+ if (!EntityClass) return null;
738
+ let entry = null;
739
+ try {
740
+ entry = new EntityClass({ history, registry: history.registry });
741
+ } catch (error) {
742
+ console.warn('[HistoryCollectionWidget] Failed to create entity:', error);
743
+ return null;
744
+ }
745
+ const id = this._generateEntryId(EntityClass);
746
+ if (typeof entry.setId === 'function') {
747
+ entry.setId(id);
748
+ } else {
749
+ entry.id = id;
750
+ if (entry.inputParams && entry.inputParams.id == null) {
751
+ entry.inputParams.id = id;
752
+ }
753
+ }
754
+ if (!entry.inputParams) entry.inputParams = {};
755
+ entry.inputParams.type = entry.inputParams.type || entry.type || typeStr;
756
+ const defaults = this._defaultsFromSchema(entry.constructor);
757
+ entry.setParams({ ...defaults, ...entry.inputParams });
758
+ return entry;
759
+ }
760
+
761
+ _resolveEntityClass(typeStr) {
762
+ const history = this.history;
763
+ if (!history) return null;
764
+ if (history.registry && typeof history.registry.resolve === 'function') {
765
+ try {
766
+ const resolved = history.registry.resolve(typeStr);
767
+ if (resolved) return resolved;
768
+ } catch (_) { /* ignore */ }
769
+ }
770
+ if (history.registry && history.registry.entityClasses instanceof Map) {
771
+ const MapClass = history.registry.entityClasses.get(typeStr);
772
+ if (MapClass) return MapClass;
773
+ for (const value of history.registry.entityClasses.values()) {
774
+ if (!value) continue;
775
+ if (value.entityType === typeStr || value.shortName === typeStr || value.type === typeStr) return value;
776
+ }
777
+ }
778
+ return null;
779
+ }
780
+
781
+ _defaultsFromSchema(EntityClass) {
782
+ if (!EntityClass || !EntityClass.inputParamsSchema) return {};
783
+ const schema = EntityClass.inputParamsSchema;
784
+ const defaults = {};
785
+ for (const key of Object.keys(schema)) {
786
+ const def = schema[key];
787
+ if (!def || typeof def !== 'object') continue;
788
+ if (Object.prototype.hasOwnProperty.call(def, 'default_value')) {
789
+ defaults[key] = def.default_value;
790
+ }
791
+ }
792
+ return defaults;
793
+ }
794
+
795
+ _generateEntryId(EntityClass) {
796
+ const history = this.history;
797
+ if (!history || typeof history.generateId !== 'function') return `entry-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
798
+ const hint =
799
+ EntityClass?.shortName ||
800
+ EntityClass?.longName ||
801
+ EntityClass?.entityType ||
802
+ EntityClass?.name ||
803
+ 'ENTRY';
804
+ return history.generateId(hint);
805
+ }
806
+
807
+ _refreshAddMenu() {
808
+ if (!this._addMenu || !this._addBtn) return;
809
+ const menu = this._addMenu;
810
+ menu.textContent = '';
811
+ const history = this.history;
812
+ if (!history || !history.registry) {
813
+ this._addBtn.disabled = true;
814
+ const empty = document.createElement('div');
815
+ empty.className = 'hc-menu-empty';
816
+ empty.textContent = 'No entries available';
817
+ menu.appendChild(empty);
818
+ return;
819
+ }
820
+ const available = typeof history.registry.listAvailable === 'function'
821
+ ? history.registry.listAvailable()
822
+ : Array.isArray(history.registry.entityClasses)
823
+ ? history.registry.entityClasses
824
+ : (history.registry.entityClasses instanceof Map
825
+ ? Array.from(history.registry.entityClasses.values())
826
+ : []);
827
+ if (!Array.isArray(available)) {
828
+ this._addBtn.disabled = true;
829
+ const empty = document.createElement('div');
830
+ empty.className = 'hc-menu-empty';
831
+ empty.textContent = 'No entries available';
832
+ menu.appendChild(empty);
833
+ return;
834
+ }
835
+ const items = [];
836
+ for (const info of available) {
837
+ if (!info) continue;
838
+ let rawType = null;
839
+ let rawLabel = null;
840
+ let source = null;
841
+ if (typeof info === 'string') {
842
+ rawType = info;
843
+ rawLabel = info;
844
+ } else {
845
+ source = (typeof info === 'function' || typeof info === 'object') ? info : null;
846
+ if (source) {
847
+ rawType = source.type || source.entityType || source.shortName || source.name;
848
+ rawLabel = source.longName || source.shortName || source.entityType || source.type || source.name;
849
+ }
850
+ }
851
+ const item = this._composeMenuItem(rawType, rawLabel, source);
852
+ if (item) items.push(item);
853
+ }
854
+ if (!items.length) {
855
+ this._addBtn.disabled = true;
856
+ const empty = document.createElement('div');
857
+ empty.className = 'hc-menu-empty';
858
+ empty.textContent = 'No entries available';
859
+ menu.appendChild(empty);
860
+ return;
861
+ }
862
+ this._addBtn.disabled = false;
863
+ for (const { type, text } of items) {
864
+ const btn = document.createElement('button');
865
+ btn.type = 'button';
866
+ btn.className = 'hc-menu-item';
867
+ btn.setAttribute('role', 'menuitem');
868
+ btn.textContent = text;
869
+ btn.dataset.type = type;
870
+ btn.addEventListener('click', async (ev) => {
871
+ ev.stopPropagation();
872
+ const targetType = ev?.currentTarget?.dataset?.type || type;
873
+ try {
874
+ await this._handleAddEntry(targetType);
875
+ } finally {
876
+ this._toggleAddMenu(false);
877
+ }
878
+ });
879
+ menu.appendChild(btn);
880
+ }
881
+ }
882
+
883
+ _buildFooter() {
884
+ const footer = document.createElement('div');
885
+ footer.className = 'hc-footer';
886
+ const addBtn = document.createElement('button');
887
+ addBtn.type = 'button';
888
+ addBtn.className = 'hc-add-btn';
889
+ addBtn.setAttribute('aria-expanded', 'false');
890
+ addBtn.setAttribute('aria-label', 'Add entry');
891
+ addBtn.title = 'Add entry';
892
+ addBtn.textContent = '+';
893
+ footer.appendChild(addBtn);
894
+
895
+ const menu = document.createElement('div');
896
+ menu.className = 'hc-add-menu';
897
+ menu.setAttribute('role', 'menu');
898
+ menu.hidden = true;
899
+ footer.appendChild(menu);
900
+
901
+ addBtn.addEventListener('click', (ev) => {
902
+ ev.stopPropagation();
903
+ const isOpen = addBtn.getAttribute('aria-expanded') === 'true';
904
+ this._scrollListIntoView();
905
+ const nextState = !isOpen;
906
+ this._toggleAddMenu(nextState);
907
+ if (nextState) this._scrollFooterIntoView();
908
+ });
909
+
910
+ this._addBtn = addBtn;
911
+ this._addMenu = menu;
912
+ this._refreshAddMenu();
913
+ return footer;
914
+ }
915
+
916
+ _toggleAddMenu(open) {
917
+ if (!this._addBtn || !this._addMenu) return;
918
+ const willOpen = Boolean(open);
919
+ this._addBtn.setAttribute('aria-expanded', willOpen ? 'true' : 'false');
920
+ this._addMenu.hidden = !willOpen;
921
+ if (this._footer) {
922
+ this._footer.classList.toggle('menu-open', willOpen);
923
+ }
924
+ }
925
+
926
+ _subscribeToHistory(history) {
927
+ if (!history || typeof history.addListener !== 'function') return;
928
+ const handler = (payload = {}) => {
929
+ if (this._suppressHistoryListener) return;
930
+ this._handleHistoryEvent(payload);
931
+ };
932
+ this._boundHistoryListener = handler;
933
+ this._listenerUnsub = history.addListener(handler);
934
+ }
935
+
936
+ _installGlobalRefreshHandler() {
937
+ if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') return;
938
+ const handler = () => {
939
+ try { this.render(); }
940
+ catch (_) { /* ignore */ }
941
+ };
942
+ try {
943
+ window.addEventListener(HISTORY_COLLECTION_REFRESH_EVENT, handler);
944
+ this._globalRefreshHandler = handler;
945
+ } catch {
946
+ this._globalRefreshHandler = null;
947
+ }
948
+ }
949
+
950
+ _handleHistoryEvent(payload) {
951
+ const reason = payload?.reason || 'update';
952
+ const skipRender = reason === 'update' && this._hasActiveReferenceSelection();
953
+ if (!skipRender) {
954
+ this.render();
955
+ }
956
+ try {
957
+ if (payload && payload.reason) {
958
+ this._emitCollectionChange(payload.reason, payload.entry || null);
959
+ }
960
+ } catch (_) { /* ignore */ }
961
+ }
962
+
963
+ _hasActiveReferenceSelection() {
964
+ const getActive = (typeof SchemaForm?.getActiveReferenceInput === 'function')
965
+ ? SchemaForm.getActiveReferenceInput
966
+ : null;
967
+ if (!getActive) return false;
968
+
969
+ const active = getActive();
970
+ if (!active) return false;
971
+
972
+ try {
973
+ const root = (typeof active.getRootNode === 'function') ? active.getRootNode() : null;
974
+ if (root && root === this._shadow) return true;
975
+ if (root && root === this.uiElement) return true;
976
+ } catch (_) { /* ignore */ }
977
+
978
+ try {
979
+ if (this._shadow && typeof this._shadow.contains === 'function' && this._shadow.contains(active)) {
980
+ return true;
981
+ }
982
+ } catch (_) { /* ignore */ }
983
+
984
+ try {
985
+ if (this.uiElement && typeof this.uiElement.contains === 'function' && this.uiElement.contains(active)) {
986
+ return true;
987
+ }
988
+ } catch (_) { /* ignore */ }
989
+
990
+ return false;
991
+ }
992
+
993
+ _emitCollectionChange(reason, entry) {
994
+ try {
995
+ this.onCollectionChange({ reason, entry, history: this.history });
996
+ } catch (_) { /* ignore */ }
997
+ }
998
+
999
+ _defaultDetermineExpanded(entry) {
1000
+ if (!entry) return false;
1001
+ try {
1002
+ if (entry.runtimeAttributes && Object.prototype.hasOwnProperty.call(entry.runtimeAttributes, '__open')) {
1003
+ return Boolean(entry.runtimeAttributes.__open);
1004
+ }
1005
+ const params = entry.inputParams;
1006
+ if (params && Object.prototype.hasOwnProperty.call(params, '__open')) {
1007
+ return Boolean(params.__open);
1008
+ }
1009
+ } catch (_) { /* ignore */ }
1010
+ return false;
1011
+ }
1012
+
1013
+ _applyOpenState(entry, isOpen) {
1014
+ if (!entry) return;
1015
+ try {
1016
+ if (!entry.runtimeAttributes || typeof entry.runtimeAttributes !== 'object') {
1017
+ entry.runtimeAttributes = {};
1018
+ }
1019
+ entry.runtimeAttributes.__open = Boolean(isOpen);
1020
+ if (entry.inputParams && typeof entry.inputParams === 'object') {
1021
+ entry.inputParams.__open = Boolean(isOpen);
1022
+ }
1023
+ } catch (_) { /* ignore */ }
1024
+ }
1025
+
1026
+ _composeFormOptions(context, getFormRef) {
1027
+ const provider = this._formOptionsProvider;
1028
+ const providerContext = { ...(context || {}) };
1029
+ const userOptions = provider ? (provider(providerContext) || {}) : {};
1030
+ const options = { ...userOptions };
1031
+ const userOnChange = typeof options.onChange === 'function' ? options.onChange : null;
1032
+ const userOnAction = typeof options.onAction === 'function' ? options.onAction : null;
1033
+ const getForm = (typeof getFormRef === 'function') ? getFormRef : null;
1034
+
1035
+ options.onChange = (_entryId, details) => {
1036
+ const changeDetails = (details && typeof details === 'object') ? details : {};
1037
+ const helpers = this._createHelperContext({
1038
+ ...(context || {}),
1039
+ form: (getForm ? getForm() : null) || changeDetails.form || null,
1040
+ details: changeDetails,
1041
+ });
1042
+ if (helpers && typeof helpers === 'object') {
1043
+ const existing = (changeDetails.helpers && typeof changeDetails.helpers === 'object')
1044
+ ? changeDetails.helpers
1045
+ : {};
1046
+ changeDetails.helpers = { ...existing, ...helpers };
1047
+ }
1048
+ if (userOnChange) {
1049
+ try { userOnChange(_entryId, changeDetails); } catch (_) { /* ignore user handler errors */ }
1050
+ }
1051
+ const entryId = (context && context.id != null) ? String(context.id) : context?.id;
1052
+ this._handleSchemaChange(entryId, context?.entry, changeDetails);
1053
+ this._maybeRefreshUiFields(entryId, context?.entry);
1054
+ };
1055
+
1056
+ options.onAction = (featureID, actionKey) => {
1057
+ if (userOnAction) {
1058
+ try { userOnAction(featureID, actionKey); } catch (_) { /* ignore */ }
1059
+ }
1060
+ };
1061
+
1062
+ return options;
1063
+ }
1064
+
1065
+ _createHelperContext(context = {}) {
1066
+ const {
1067
+ entry = null,
1068
+ id = null,
1069
+ index = null,
1070
+ schema = null,
1071
+ params = null,
1072
+ form = null,
1073
+ } = context;
1074
+ const baseHelpers = {
1075
+ widget: this,
1076
+ history: this.history || null,
1077
+ viewer: this.viewer || null,
1078
+ entry,
1079
+ id,
1080
+ index,
1081
+ schema,
1082
+ params,
1083
+ form,
1084
+ getForm: () => {
1085
+ if (form) return form;
1086
+ if (id == null) return null;
1087
+ return this.getFormForEntry(String(id));
1088
+ },
1089
+ };
1090
+ const augmented = this._augmentHelperContext(baseHelpers, context);
1091
+ if (augmented && typeof augmented === 'object' && Object.keys(augmented).length) {
1092
+ return { ...baseHelpers, ...augmented };
1093
+ }
1094
+ return baseHelpers;
1095
+ }
1096
+
1097
+ // Subclasses can override to add extra helper utilities.
1098
+ // eslint-disable-next-line class-methods-use-this, no-unused-vars
1099
+ _augmentHelperContext(_baseHelpers, _context) {
1100
+ return {};
1101
+ }
1102
+
1103
+ _normalizeEntryToggle(config) {
1104
+ if (!config || typeof config !== 'object') return null;
1105
+ const { isEnabled, setEnabled } = config;
1106
+ if (typeof isEnabled !== 'function' || typeof setEnabled !== 'function') return null;
1107
+ return {
1108
+ isEnabled,
1109
+ setEnabled,
1110
+ isDisabled: typeof config.isDisabled === 'function' ? config.isDisabled : null,
1111
+ getTitle: typeof config.getTitle === 'function'
1112
+ ? config.getTitle
1113
+ : (typeof config.title === 'string' ? () => config.title : null),
1114
+ className: typeof config.className === 'string' && config.className.trim()
1115
+ ? config.className
1116
+ : 'hc-entry-toggle',
1117
+ };
1118
+ }
1119
+
1120
+ _composeMenuItem(rawType, rawLabel, source = null, extraNames = []) {
1121
+ if (!rawType && rawType !== 0) return null;
1122
+ const normalizedType = String(rawType);
1123
+ const nameInfo = this._extractDisplayNames(source, normalizedType, rawLabel);
1124
+ const labelText = nameInfo.longName || nameInfo.shortName || normalizedType;
1125
+ return { type: normalizedType, text: labelText };
1126
+ }
1127
+
1128
+ _extractDisplayNames(source, fallbackType = '', fallbackLabel = '') {
1129
+ const shortName = this._firstNonEmptyString([
1130
+ source?.shortName,
1131
+ fallbackType,
1132
+ fallbackLabel,
1133
+ ]);
1134
+ let longName = this._firstNonEmptyString([
1135
+ source?.longName,
1136
+ fallbackLabel,
1137
+ fallbackType,
1138
+ shortName,
1139
+ ]);
1140
+ if (!longName) longName = '';
1141
+ return { shortName: shortName || '', longName: longName || '' };
1142
+ }
1143
+
1144
+ _firstNonEmptyString(list = []) {
1145
+ if (!Array.isArray(list)) return '';
1146
+ for (const value of list) {
1147
+ if (value == null && value !== 0) continue;
1148
+ const str = String(value).trim();
1149
+ if (str.length) return str;
1150
+ }
1151
+ return '';
1152
+ }
1153
+
1154
+ _filterSchemaForEntry(entry, schema, entryId = null) {
1155
+ const fallbackKeys = schema && typeof schema === 'object' ? Object.keys(schema) : [];
1156
+ if (!schema || typeof schema !== 'object') {
1157
+ return { schema, visibleKeys: fallbackKeys };
1158
+ }
1159
+ let entityClass = this._resolveEntityClass(entry?.type || entry?.entityType || null);
1160
+ if (!entityClass && this.history && this.history.featureRegistry) {
1161
+ try {
1162
+ entityClass =
1163
+ this.history.featureRegistry.getSafe?.(entry?.type)
1164
+ || this.history.featureRegistry.get?.(entry?.type)
1165
+ || null;
1166
+ } catch {
1167
+ entityClass = null;
1168
+ }
1169
+ }
1170
+ const context = this._createHelperContext({
1171
+ entry,
1172
+ id: entryId,
1173
+ schema,
1174
+ params: entry?.inputParams || {},
1175
+ form: entryId ? this.getFormForEntry(entryId) : null,
1176
+ }) || {};
1177
+ if (entityClass) context.entityClass = entityClass;
1178
+ const runTest = (owner, fn) => {
1179
+ if (typeof fn !== 'function') return null;
1180
+ try {
1181
+ return fn.call(owner || entry || null, context);
1182
+ } catch (error) {
1183
+ console.warn('[HistoryCollectionWidget] uiFieldsTest failed; falling back to full schema.', error);
1184
+ return null;
1185
+ }
1186
+ };
1187
+ let excluded = runTest(entry, entry?.uiFieldsTest);
1188
+ if (excluded == null && entityClass) {
1189
+ if (typeof entityClass.uiFieldsTest === 'function') {
1190
+ excluded = runTest(entityClass, entityClass.uiFieldsTest);
1191
+ } else if (entityClass.prototype && entityClass.prototype.uiFieldsTest && entityClass.prototype.uiFieldsTest !== entry?.uiFieldsTest) {
1192
+ excluded = runTest(entry, entityClass.prototype.uiFieldsTest);
1193
+ }
1194
+ }
1195
+ const blockedSet = this._normalizeExcludeKeySet(excluded, fallbackKeys);
1196
+ let keys = [...fallbackKeys];
1197
+ if (blockedSet.size > 0) keys = keys.filter((key) => !blockedSet.has(key));
1198
+ if (keys.length === 0) return { schema: {}, visibleKeys: [] };
1199
+ const filtered = {};
1200
+ for (const key of keys) {
1201
+ filtered[key] = schema[key];
1202
+ }
1203
+ return { schema: filtered, visibleKeys: keys };
1204
+ }
1205
+
1206
+ _normalizeExcludeKeySet(result, fallbackKeys = []) {
1207
+ const set = new Set();
1208
+ let list = null;
1209
+ if (Array.isArray(result)) list = result;
1210
+ else if (typeof result === 'string') list = [result];
1211
+ else if (result && typeof result === 'object' && Array.isArray(result.exclude)) list = result.exclude;
1212
+ if (!Array.isArray(list)) return set;
1213
+ const allowed = new Set(fallbackKeys);
1214
+ for (const raw of list) {
1215
+ if (raw == null) continue;
1216
+ const key = String(raw);
1217
+ if (allowed.has(key)) set.add(key);
1218
+ }
1219
+ return set;
1220
+ }
1221
+
1222
+ _recordUiFieldSignature(entryId, keys = []) {
1223
+ if (entryId == null) return;
1224
+ const sig = this._uiFieldsSignatureFromKeys(keys);
1225
+ this._uiFieldSignatures.set(String(entryId), sig);
1226
+ }
1227
+
1228
+ _maybeRefreshUiFields(entryId, entry) {
1229
+ if (entryId == null) return;
1230
+ const baseSchema = this._resolveSchema(entry);
1231
+ const { visibleKeys } = this._filterSchemaForEntry(entry, baseSchema, entryId);
1232
+ const nextSig = this._uiFieldsSignatureFromKeys(visibleKeys);
1233
+ const prevSig = this._uiFieldSignatures.get(String(entryId)) || null;
1234
+ if (nextSig !== prevSig) {
1235
+ this._uiFieldSignatures.set(String(entryId), nextSig);
1236
+ this.render();
1237
+ }
1238
+ }
1239
+
1240
+ _uiFieldsSignatureFromKeys(keys = []) {
1241
+ if (!Array.isArray(keys) || !keys.length) return '';
1242
+ return keys.join('|');
1243
+ }
1244
+
1245
+ _makeStyle() {
1246
+ const style = document.createElement('style');
1247
+ style.textContent = HISTORY_COLLECTION_WIDGET_CSS;
1248
+ return style;
1249
+ }
1250
+
1251
+ _scrollListIntoView() {
1252
+ const target = this._listEl || this._container || this.uiElement;
1253
+ this._scrollElementIntoView(target);
1254
+ }
1255
+
1256
+ _scrollFooterIntoView() {
1257
+ const target = this._footer || this._addBtn || this._addMenu;
1258
+ this._scrollElementIntoView(target);
1259
+ }
1260
+
1261
+ _deferScrollToEntry(entryId) {
1262
+ if (!entryId) return;
1263
+ const exec = () => this._scrollEntryIntoView(entryId);
1264
+ if (typeof requestAnimationFrame === 'function') requestAnimationFrame(exec);
1265
+ else setTimeout(exec, 0);
1266
+ }
1267
+
1268
+ _scrollEntryIntoView(entryId) {
1269
+ if (!entryId || !this._listEl) return;
1270
+ const selector = `[data-entry-id="${this.#escapeCss(entryId)}"]`;
1271
+ const target = this._listEl.querySelector(selector);
1272
+ if (target) {
1273
+ this._scrollElementIntoView(target);
1274
+ }
1275
+ }
1276
+
1277
+ _scrollElementIntoView(target) {
1278
+ if (target && typeof target.scrollIntoView === 'function') {
1279
+ try {
1280
+ target.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
1281
+ } catch {
1282
+ target.scrollIntoView();
1283
+ }
1284
+ }
1285
+ }
1286
+
1287
+ #escapeCss(value) {
1288
+ const str = String(value);
1289
+ if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
1290
+ return CSS.escape(str);
1291
+ }
1292
+ return str.replace(/"/g, '\\"');
1293
+ }
1294
+ }