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,102 @@
1
+ import { openComponentSelectorModal } from '../componentSelectorModal.js';
2
+
3
+ export function renderComponentSelectorField({ ui, key, def, id, controlWrap }) {
4
+ const wrap = document.createElement('div');
5
+ wrap.className = 'component-selector-wrap';
6
+
7
+ const inputEl = document.createElement('input');
8
+ inputEl.type = 'text';
9
+ inputEl.id = id;
10
+ inputEl.className = 'input component-selector-input';
11
+ inputEl.readOnly = true;
12
+ inputEl.value = String(ui._pickInitialValue(key, def) || '');
13
+
14
+ const btn = document.createElement('button');
15
+ btn.type = 'button';
16
+ btn.className = 'btn component-selector-btn';
17
+ btn.textContent = String(def.buttonLabel || 'Choose…');
18
+
19
+ const clearBtn = document.createElement('button');
20
+ clearBtn.type = 'button';
21
+ clearBtn.className = 'btn btn-slim component-selector-clear';
22
+ clearBtn.textContent = 'Clear';
23
+
24
+ const applyValue = (value, record) => {
25
+ inputEl.value = value || '';
26
+ ui.params[key] = value || '';
27
+ ui._emitParamsChange(key, ui.params[key]);
28
+ if (typeof def.onSelect === 'function') {
29
+ try {
30
+ const ctx = {
31
+ featureID: ui.params?.featureID ?? ui.params?.id ?? null,
32
+ key,
33
+ viewer: ui.options?.viewer || null,
34
+ partHistory: ui.options?.partHistory || null,
35
+ feature: ui.options?.featureRef || null,
36
+ form: ui,
37
+ };
38
+ def.onSelect(ctx, record || null);
39
+ } catch (_) { /* ignore handler errors */ }
40
+ }
41
+ };
42
+
43
+ btn.addEventListener('click', async (ev) => {
44
+ ev.preventDefault();
45
+ ui._stopActiveReferenceSelection();
46
+ const record = await openComponentSelectorModal({ title: def.dialogTitle || 'Select Component' });
47
+ if (!record || !record.data3mf) return;
48
+ applyValue(record.name || '', record);
49
+ });
50
+
51
+ clearBtn.addEventListener('click', () => {
52
+ applyValue('', null);
53
+ });
54
+
55
+ wrap.appendChild(inputEl);
56
+ const controls = document.createElement('div');
57
+ controls.className = 'component-selector-controls';
58
+ controls.appendChild(btn);
59
+ controls.appendChild(clearBtn);
60
+ wrap.appendChild(controls);
61
+
62
+ if (controlWrap instanceof HTMLElement) {
63
+ controlWrap.appendChild(wrap);
64
+ }
65
+
66
+ return {
67
+ inputEl,
68
+ activate() {
69
+ btn.focus();
70
+ },
71
+ readValue() {
72
+ return inputEl.value;
73
+ }
74
+ };
75
+ }
76
+
77
+ (function ensureComponentSelectorStyles() {
78
+ if (typeof document === 'undefined') return;
79
+ if (document.getElementById('component-selector-field-styles')) return;
80
+ const style = document.createElement('style');
81
+ style.id = 'component-selector-field-styles';
82
+ style.textContent = `
83
+ .component-selector-wrap {
84
+ display: flex;
85
+ gap: 8px;
86
+ align-items: center;
87
+ }
88
+ .component-selector-wrap .component-selector-input {
89
+ flex: 1 1 auto;
90
+ min-width: 0;
91
+ }
92
+ .component-selector-controls {
93
+ display: flex;
94
+ gap: 6px;
95
+ flex: 0 0 auto;
96
+ }
97
+ .component-selector-clear {
98
+ padding: 4px 8px;
99
+ }
100
+ `;
101
+ document.head.appendChild(style);
102
+ })();
@@ -0,0 +1,23 @@
1
+ export function renderDefaultField({ ui, key, def, id }) {
2
+ const inputEl = document.createElement('input');
3
+ inputEl.type = 'text';
4
+ inputEl.id = id;
5
+ inputEl.className = 'input';
6
+
7
+ ui._setInputValue(inputEl, 'string', ui._pickInitialValue(key, def));
8
+
9
+ inputEl.addEventListener('change', () => {
10
+ ui.params[key] = inputEl.value;
11
+ ui._emitParamsChange(key, inputEl.value);
12
+ });
13
+
14
+ return {
15
+ inputEl,
16
+ activate() {
17
+ inputEl.focus();
18
+ },
19
+ readValue() {
20
+ return inputEl.value;
21
+ },
22
+ };
23
+ }
@@ -0,0 +1,66 @@
1
+ export function renderFileField({ ui, key, def, id, controlWrap }) {
2
+ const inputEl = document.createElement('button');
3
+ inputEl.type = 'button';
4
+ inputEl.id = id;
5
+ inputEl.className = 'btn';
6
+ inputEl.textContent = String(def.label || 'Choose File…');
7
+
8
+ const info = document.createElement('div');
9
+ info.className = 'file-info';
10
+ const initial = ui._pickInitialValue(key, def);
11
+ if (typeof initial === 'string' && initial.startsWith('data:') && initial.includes(';base64,')) {
12
+ const b64 = initial.split(',')[1] || '';
13
+ const size = Math.floor((b64.length * 3) / 4);
14
+ info.textContent = `Loaded (${size} bytes)`;
15
+ } else if (initial && String(initial).length) {
16
+ info.textContent = `Loaded (${String(initial).length} chars)`;
17
+ } else {
18
+ info.textContent = 'No file selected';
19
+ }
20
+
21
+ const fileInput = document.createElement('input');
22
+ fileInput.type = 'file';
23
+ fileInput.style.display = 'none';
24
+ if (def && def.accept) fileInput.setAttribute('accept', String(def.accept));
25
+
26
+ inputEl.addEventListener('click', (ev) => {
27
+ ev.preventDefault();
28
+ fileInput.click();
29
+ });
30
+
31
+ fileInput.addEventListener('change', async () => {
32
+ const f = fileInput.files && fileInput.files[0];
33
+ if (!f) return;
34
+ try {
35
+ const ab = await f.arrayBuffer();
36
+ const bytes = new Uint8Array(ab);
37
+ let binary = '';
38
+ const chunk = 0x8000;
39
+ for (let i = 0; i < bytes.length; i += chunk) {
40
+ const sub = bytes.subarray(i, i + chunk);
41
+ binary += String.fromCharCode.apply(null, sub);
42
+ }
43
+ const b64 = (typeof btoa === 'function') ? btoa(binary) : (typeof Buffer !== 'undefined' ? Buffer.from(bytes).toString('base64') : '');
44
+ const mime = (f.type && f.type.length) ? f.type : 'application/octet-stream';
45
+ const dataUrl = `data:${mime};base64,${b64}`;
46
+ ui.params[key] = dataUrl;
47
+ info.textContent = `${f.name} (${bytes.length} bytes)`;
48
+ ui._emitParamsChange(key, dataUrl);
49
+ } catch (e) {
50
+ info.textContent = `Failed to read file: ${e?.message || e}`;
51
+ }
52
+ });
53
+
54
+ controlWrap.appendChild(info);
55
+ controlWrap.appendChild(fileInput);
56
+
57
+ return {
58
+ inputEl,
59
+ activate() {
60
+ fileInput.click();
61
+ },
62
+ readValue() {
63
+ return ui.params[key] ?? null;
64
+ },
65
+ };
66
+ }
@@ -0,0 +1,34 @@
1
+ import { renderNumberField } from './numberField.js';
2
+ import { renderTextareaField } from './textareaField.js';
3
+ import { renderReferenceSelectionField } from './referenceSelectionField.js';
4
+ import { renderTransformField } from './transformField.js';
5
+ import { renderBooleanOperationField } from './booleanOperationField.js';
6
+ import { renderStringField } from './stringField.js';
7
+ import { renderBooleanField } from './booleanField.js';
8
+ import { renderOptionsField } from './optionsField.js';
9
+ import { renderVec3Field } from './vec3Field.js';
10
+ import { renderFileField } from './fileField.js';
11
+ import { renderButtonField } from './buttonField.js';
12
+ import { renderDefaultField } from './defaultField.js';
13
+ import { renderComponentSelectorField } from './componentSelectorField.js';
14
+ import { renderThreadDesignationField } from './threadDesignationField.js';
15
+
16
+ const RENDERERS = {
17
+ number: renderNumberField,
18
+ textarea: renderTextareaField,
19
+ reference_selection: renderReferenceSelectionField,
20
+ transform: renderTransformField,
21
+ boolean_operation: renderBooleanOperationField,
22
+ string: renderStringField,
23
+ boolean: renderBooleanField,
24
+ options: renderOptionsField,
25
+ vec3: renderVec3Field,
26
+ file: renderFileField,
27
+ button: renderButtonField,
28
+ component_selector: renderComponentSelectorField,
29
+ thread_designation: renderThreadDesignationField,
30
+ };
31
+
32
+ export function getWidgetRenderer(type) {
33
+ return RENDERERS[type] || renderDefaultField;
34
+ }
@@ -0,0 +1,165 @@
1
+ export function renderNumberField({ ui, key, def, id, controlWrap }) {
2
+ const wrap = document.createElement('div');
3
+ wrap.className = 'number-input-wrap';
4
+
5
+ const inputEl = document.createElement('input');
6
+ inputEl.type = 'text';
7
+ inputEl.id = id;
8
+ inputEl.className = 'input number-input';
9
+ inputEl.dataset.forceText = 'true';
10
+
11
+ try {
12
+ if (def && (typeof def.step === 'number' || (typeof def.step === 'string' && def.step.trim() !== ''))) {
13
+ inputEl.dataset.step = String(def.step);
14
+ }
15
+ if (def && (typeof def.min === 'number' || (typeof def.min === 'string' && def.min !== ''))) {
16
+ inputEl.dataset.min = String(def.min);
17
+ }
18
+ if (def && (typeof def.max === 'number' || (typeof def.max === 'string' && def.max !== ''))) {
19
+ inputEl.dataset.max = String(def.max);
20
+ }
21
+ } catch (_) { }
22
+
23
+ const stepper = document.createElement('div');
24
+ stepper.className = 'number-stepper';
25
+ const stepUp = document.createElement('button');
26
+ stepUp.type = 'button';
27
+ stepUp.className = 'number-stepper-btn number-stepper-up';
28
+ stepUp.setAttribute('aria-label', 'Increment');
29
+ const stepDown = document.createElement('button');
30
+ stepDown.type = 'button';
31
+ stepDown.className = 'number-stepper-btn number-stepper-down';
32
+ stepDown.setAttribute('aria-label', 'Decrement');
33
+ stepper.appendChild(stepUp);
34
+ stepper.appendChild(stepDown);
35
+
36
+ wrap.appendChild(inputEl);
37
+ wrap.appendChild(stepper);
38
+ if (controlWrap) controlWrap.appendChild(wrap);
39
+
40
+ ui._setInputValue(inputEl, def.type, ui._pickInitialValue(key, def));
41
+
42
+ const commitValue = () => {
43
+ ui.params[key] = inputEl.value;
44
+ ui._emitParamsChange(key, inputEl.value);
45
+ };
46
+
47
+ inputEl.addEventListener('change', commitValue);
48
+
49
+ inputEl.addEventListener('focus', () => {
50
+ inputEl.select();
51
+ ui._stopActiveReferenceSelection();
52
+ });
53
+
54
+ const numericLike = /^\s*[-+]?((\d+(?:\.\d*)?)|(\.\d+))(?:[eE][-+]?\d+)?\s*$/;
55
+ const readLimit = (name) => {
56
+ const raw = inputEl.dataset ? inputEl.dataset[name] : null;
57
+ if (raw == null || raw === '') return null;
58
+ const num = Number(raw);
59
+ return Number.isFinite(num) ? num : null;
60
+ };
61
+ const readStep = () => {
62
+ const raw = inputEl.dataset ? inputEl.dataset.step : null;
63
+ const num = Number(raw);
64
+ if (!Number.isFinite(num) || num === 0) return 1;
65
+ return Math.abs(num);
66
+ };
67
+ const readBase = () => {
68
+ const raw = String(inputEl.value || '').trim();
69
+ if (!raw) {
70
+ const minVal = readLimit('min');
71
+ return Number.isFinite(minVal) ? minVal : 0;
72
+ }
73
+ if (!numericLike.test(raw)) return null;
74
+ const num = Number(raw);
75
+ return Number.isFinite(num) ? num : null;
76
+ };
77
+ const applyStep = (dir, options = {}) => {
78
+ const { commit = true, focus = true } = options;
79
+ const base = readBase();
80
+ if (base == null) return false;
81
+ const step = readStep();
82
+ let next = base + dir * step;
83
+ const minVal = readLimit('min');
84
+ const maxVal = readLimit('max');
85
+ if (Number.isFinite(minVal)) next = Math.max(minVal, next);
86
+ if (Number.isFinite(maxVal)) next = Math.min(maxVal, next);
87
+ ui._setInputValue(inputEl, 'number', next);
88
+ if (commit) commitValue();
89
+ if (focus) {
90
+ inputEl.focus();
91
+ inputEl.select();
92
+ }
93
+ return true;
94
+ };
95
+ const preventBlur = (ev) => {
96
+ ev.preventDefault();
97
+ };
98
+ const repeat = { delay: 350, interval: 80 };
99
+ let repeatTimeout = null;
100
+ let repeatInterval = null;
101
+ let clearClickTimeout = null;
102
+ let didRepeatStep = false;
103
+ let suppressClick = false;
104
+ const stopRepeat = () => {
105
+ if (repeatTimeout) clearTimeout(repeatTimeout);
106
+ if (repeatInterval) clearInterval(repeatInterval);
107
+ repeatTimeout = null;
108
+ repeatInterval = null;
109
+ if (didRepeatStep) {
110
+ commitValue();
111
+ didRepeatStep = false;
112
+ }
113
+ if (suppressClick) {
114
+ if (clearClickTimeout) clearTimeout(clearClickTimeout);
115
+ clearClickTimeout = setTimeout(() => { suppressClick = false; }, 200);
116
+ }
117
+ };
118
+ const startRepeat = (dir) => {
119
+ stopRepeat();
120
+ didRepeatStep = applyStep(dir, { commit: false, focus: true });
121
+ if (!didRepeatStep) return;
122
+ repeatTimeout = setTimeout(() => {
123
+ repeatInterval = setInterval(() => {
124
+ if (applyStep(dir, { commit: false, focus: false })) didRepeatStep = true;
125
+ }, repeat.interval);
126
+ }, repeat.delay);
127
+ window.addEventListener('pointerup', stopRepeat, { once: true });
128
+ window.addEventListener('pointercancel', stopRepeat, { once: true });
129
+ return true;
130
+ };
131
+ stepUp.addEventListener('pointerdown', (ev) => {
132
+ preventBlur(ev);
133
+ suppressClick = Boolean(startRepeat(1));
134
+ });
135
+ stepDown.addEventListener('pointerdown', (ev) => {
136
+ preventBlur(ev);
137
+ suppressClick = Boolean(startRepeat(-1));
138
+ });
139
+ stepUp.addEventListener('click', () => {
140
+ if (suppressClick) {
141
+ suppressClick = false;
142
+ if (clearClickTimeout) clearTimeout(clearClickTimeout);
143
+ return;
144
+ }
145
+ applyStep(1);
146
+ });
147
+ stepDown.addEventListener('click', () => {
148
+ if (suppressClick) {
149
+ suppressClick = false;
150
+ if (clearClickTimeout) clearTimeout(clearClickTimeout);
151
+ return;
152
+ }
153
+ applyStep(-1);
154
+ });
155
+
156
+ return {
157
+ inputEl,
158
+ activate() {
159
+ inputEl.focus();
160
+ },
161
+ readValue() {
162
+ return inputEl.value;
163
+ },
164
+ };
165
+ }
@@ -0,0 +1,33 @@
1
+ export function renderOptionsField({ ui, key, def, id }) {
2
+ const inputEl = document.createElement('select');
3
+ inputEl.id = id;
4
+ inputEl.className = 'select';
5
+
6
+ const opts = Array.isArray(def.options) ? def.options : [];
7
+ for (let i = 0; i < opts.length; i++) {
8
+ const opt = opts[i];
9
+ const o = document.createElement('option');
10
+ o.value = String(opt);
11
+ o.textContent = String(opt);
12
+ inputEl.appendChild(o);
13
+ }
14
+
15
+ ui._setInputValue(inputEl, 'options', ui._pickInitialValue(key, def));
16
+
17
+ inputEl.addEventListener('change', () => {
18
+ const v = inputEl.value;
19
+ ui.params[key] = v;
20
+ ui._emitParamsChange(key, v);
21
+ ui._stopActiveReferenceSelection();
22
+ });
23
+
24
+ return {
25
+ inputEl,
26
+ activate() {
27
+ inputEl.focus();
28
+ },
29
+ readValue() {
30
+ return inputEl.value;
31
+ },
32
+ };
33
+ }
@@ -0,0 +1,208 @@
1
+ import { normalizeReferenceList, normalizeReferenceName } from './utils.js';
2
+
3
+ export function renderReferenceSelectionField({ ui, key, def, id, controlWrap, valueAdapter = null }) {
4
+ const inputEl = document.createElement('input');
5
+ inputEl.type = 'hidden';
6
+ inputEl.id = id;
7
+
8
+ const isMulti = !!def.multiple;
9
+ if (isMulti) inputEl.dataset.multiple = 'true';
10
+
11
+ const parseBound = (value) => {
12
+ const num = Number(value);
13
+ if (!Number.isFinite(num)) return null;
14
+ const coerced = Math.max(0, Math.floor(num));
15
+ return coerced >= 0 ? coerced : null;
16
+ };
17
+
18
+ const minSelections = isMulti ? parseBound(def.minSelections) : null;
19
+ let maxSelections = isMulti ? parseBound(def.maxSelections) : null;
20
+ if (isMulti && maxSelections !== null && minSelections !== null && maxSelections < minSelections) {
21
+ maxSelections = minSelections;
22
+ }
23
+
24
+ if (isMulti && minSelections !== null) inputEl.dataset.minSelections = String(minSelections);
25
+ if (isMulti && maxSelections !== null) inputEl.dataset.maxSelections = String(maxSelections);
26
+
27
+ const placeholderText = (typeof def.placeholder === 'string' && def.placeholder.trim())
28
+ ? def.placeholder.trim()
29
+ : 'Click then select in scene…';
30
+
31
+ const adapter = (valueAdapter && typeof valueAdapter === 'object') ? valueAdapter : null;
32
+ const readRawValue = () => {
33
+ if (adapter && typeof adapter.read === 'function') {
34
+ try { return adapter.read(); } catch (_) { return null; }
35
+ }
36
+ return ui._pickInitialValue(key, def);
37
+ };
38
+ const writeRawValue = (next) => {
39
+ if (adapter && typeof adapter.write === 'function') {
40
+ try { adapter.write(next); return; } catch (_) { return; }
41
+ }
42
+ ui.params[key] = next;
43
+ };
44
+ const emitChange = (value) => {
45
+ if (adapter && typeof adapter.emit === 'function') {
46
+ try { adapter.emit(value); return; } catch (_) { return; }
47
+ }
48
+ ui._emitParamsChange(key, value);
49
+ };
50
+
51
+ const refWrap = document.createElement('div');
52
+ refWrap.className = isMulti ? 'ref-multi-wrap' : 'ref-single-wrap';
53
+
54
+ let chipsWrap = null;
55
+ const updateSelectionMetadata = (list) => {
56
+ if (!isMulti) return;
57
+ const normalized = normalizeReferenceList(Array.isArray(list) ? list : []);
58
+ try { inputEl.dataset.selectedCount = String(normalized.length); } catch (_) { }
59
+ try { inputEl.dataset.selectedValues = JSON.stringify(normalized); } catch (_) { }
60
+ };
61
+
62
+ if (isMulti) {
63
+ inputEl.__getSelectionList = () => {
64
+ try {
65
+ const raw = readRawValue();
66
+ if (!Array.isArray(raw)) return [];
67
+ return normalizeReferenceList(raw);
68
+ } catch (_) {
69
+ return [];
70
+ }
71
+ };
72
+ inputEl.__updateSelectionMetadata = updateSelectionMetadata;
73
+ }
74
+ if (isMulti) {
75
+ chipsWrap = document.createElement('div');
76
+ chipsWrap.className = 'ref-chips';
77
+ chipsWrap.addEventListener('click', () => ui._activateReferenceSelection(inputEl, def));
78
+ refWrap.appendChild(chipsWrap);
79
+ try {
80
+ const initial = readRawValue();
81
+ const current = normalizeReferenceList(Array.isArray(initial) ? initial : []);
82
+ writeRawValue(current);
83
+ ui._renderChips(chipsWrap, key, current);
84
+ updateSelectionMetadata(current);
85
+ } catch (_) { }
86
+ } else {
87
+ const valueWrap = document.createElement('button');
88
+ valueWrap.type = 'button';
89
+ valueWrap.className = 'ref-single-display';
90
+ valueWrap.title = placeholderText;
91
+ valueWrap.dataset.placeholder = placeholderText;
92
+
93
+ const label = document.createElement('span');
94
+ label.className = 'ref-single-label';
95
+ valueWrap.appendChild(label);
96
+
97
+ const clearBtn = document.createElement('span');
98
+ clearBtn.className = 'ref-chip-remove';
99
+ clearBtn.role = 'button';
100
+ clearBtn.tabIndex = 0;
101
+ clearBtn.title = 'Clear selection';
102
+ clearBtn.textContent = '✕';
103
+
104
+ const clearSelection = (ev) => {
105
+ ev.stopPropagation();
106
+ ev.preventDefault?.();
107
+ writeRawValue(null);
108
+ inputEl.value = '';
109
+ updateSingleDisplay(null);
110
+ emitChange(null);
111
+ };
112
+
113
+ clearBtn.addEventListener('click', clearSelection);
114
+ clearBtn.addEventListener('keydown', (ev) => {
115
+ if (ev.key === 'Enter' || ev.key === ' ') {
116
+ clearSelection(ev);
117
+ }
118
+ });
119
+ valueWrap.appendChild(clearBtn);
120
+
121
+ const updateSingleDisplay = (val) => {
122
+ const normalized = normalizeReferenceName(val);
123
+ label.textContent = normalized || placeholderText;
124
+ clearBtn.style.visibility = normalized ? 'visible' : 'hidden';
125
+ };
126
+
127
+ const initial = normalizeReferenceName(readRawValue());
128
+ writeRawValue(initial);
129
+ updateSingleDisplay(initial);
130
+ inputEl.value = initial ?? '';
131
+
132
+ valueWrap.addEventListener('click', () => ui._activateReferenceSelection(inputEl, def));
133
+ refWrap.appendChild(valueWrap);
134
+
135
+ inputEl.addEventListener('change', () => {
136
+ updateSingleDisplay(inputEl.value);
137
+ const normalized = normalizeReferenceName(inputEl.value);
138
+ writeRawValue(normalized);
139
+ emitChange(normalized);
140
+ });
141
+ }
142
+
143
+ ui._setInputValue(inputEl, def.type, readRawValue());
144
+
145
+ inputEl.addEventListener('change', () => {
146
+ const raw = inputEl.value;
147
+ if (isMulti) {
148
+ if (inputEl.dataset && inputEl.dataset.forceClear === 'true') {
149
+ writeRawValue([]);
150
+ if (chipsWrap) {
151
+ ui._renderChips(chipsWrap, key, []);
152
+ updateSelectionMetadata([]);
153
+ }
154
+ inputEl.value = '';
155
+ delete inputEl.dataset.forceClear;
156
+ emitChange(adapter ? readRawValue() : []);
157
+ return;
158
+ }
159
+ let incoming = [];
160
+ let parsedArray = false;
161
+ try {
162
+ const parsed = JSON.parse(raw);
163
+ if (Array.isArray(parsed)) { incoming = parsed; parsedArray = true; }
164
+ } catch (_) { }
165
+ if (!parsedArray && raw != null && String(raw).trim() !== '') incoming = [String(raw).trim()];
166
+
167
+ const existingRaw = readRawValue();
168
+ const current = Array.isArray(existingRaw) ? existingRaw : [];
169
+ const next = current.slice();
170
+ for (const name of incoming) {
171
+ const normalized = normalizeReferenceName(name);
172
+ if (!normalized) continue;
173
+ if (!next.includes(normalized)) next.push(normalized);
174
+ }
175
+ const normalizedList = normalizeReferenceList(next);
176
+ if (isMulti && maxSelections !== null && normalizedList.length > maxSelections) {
177
+ normalizedList.length = maxSelections;
178
+ }
179
+ writeRawValue(normalizedList);
180
+ if (chipsWrap) {
181
+ ui._renderChips(chipsWrap, key, normalizedList);
182
+ updateSelectionMetadata(normalizedList);
183
+ }
184
+ inputEl.value = '';
185
+ emitChange(adapter ? readRawValue() : normalizedList);
186
+ } else {
187
+ const normalized = normalizeReferenceName(raw);
188
+ inputEl.value = normalized ?? '';
189
+ writeRawValue(normalized);
190
+ emitChange(normalized);
191
+ }
192
+ });
193
+
194
+ refWrap.appendChild(inputEl);
195
+ controlWrap.appendChild(refWrap);
196
+
197
+ const activate = () => ui._activateReferenceSelection(inputEl, def);
198
+
199
+ return {
200
+ inputEl,
201
+ activate,
202
+ readValue() {
203
+ const value = readRawValue();
204
+ if (Array.isArray(value)) return normalizeReferenceList(value);
205
+ return normalizeReferenceName(value);
206
+ },
207
+ };
208
+ }
@@ -0,0 +1,24 @@
1
+ export function renderStringField({ ui, key, def, id }) {
2
+ const inputEl = document.createElement('input');
3
+ inputEl.type = 'text';
4
+ inputEl.id = id;
5
+ inputEl.className = 'input';
6
+
7
+ ui._setInputValue(inputEl, def.type, ui._pickInitialValue(key, def));
8
+
9
+ inputEl.addEventListener('change', () => {
10
+ ui.params[key] = inputEl.value;
11
+ ui._emitParamsChange(key, inputEl.value);
12
+ ui._stopActiveReferenceSelection();
13
+ });
14
+
15
+ return {
16
+ inputEl,
17
+ activate() {
18
+ inputEl.focus();
19
+ },
20
+ readValue() {
21
+ return inputEl.value;
22
+ },
23
+ };
24
+ }
@@ -0,0 +1,28 @@
1
+ export function renderTextareaField({ ui, key, def, id }) {
2
+ const inputEl = document.createElement('textarea');
3
+ inputEl.id = id;
4
+ inputEl.className = 'input textarea';
5
+
6
+ if (def && def.rows != null) {
7
+ const rows = parseInt(def.rows, 10);
8
+ if (Number.isFinite(rows) && rows > 0) inputEl.rows = rows;
9
+ }
10
+ if (def && typeof def.placeholder === 'string') inputEl.placeholder = def.placeholder;
11
+
12
+ ui._setInputValue(inputEl, 'string', ui._pickInitialValue(key, def));
13
+
14
+ inputEl.addEventListener('change', () => {
15
+ ui.params[key] = inputEl.value;
16
+ ui._emitParamsChange(key, inputEl.value);
17
+ });
18
+
19
+ return {
20
+ inputEl,
21
+ activate() {
22
+ inputEl.focus();
23
+ },
24
+ readValue() {
25
+ return inputEl.value;
26
+ },
27
+ };
28
+ }