brep-io-kernel 1.0.20 → 1.0.22

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 (43) hide show
  1. package/README.md +4 -1
  2. package/dist-kernel/brep-kernel.js +10858 -9938
  3. package/package.json +3 -2
  4. package/src/BREP/Edge.js +2 -0
  5. package/src/BREP/Face.js +2 -0
  6. package/src/BREP/SolidMethods/visualize.js +372 -365
  7. package/src/BREP/Vertex.js +2 -17
  8. package/src/PartHistory.js +4 -25
  9. package/src/SketchSolver2D.js +3 -0
  10. package/src/UI/AccordionWidget.js +1 -1
  11. package/src/UI/EnvMonacoEditor.js +0 -3
  12. package/src/UI/HistoryWidget.js +12 -4
  13. package/src/UI/SceneListing.js +45 -7
  14. package/src/UI/SelectionFilter.js +903 -438
  15. package/src/UI/SelectionState.js +464 -0
  16. package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +41 -1
  17. package/src/UI/assembly/AssemblyConstraintsWidget.js +21 -3
  18. package/src/UI/assembly/constraintSelectionUtils.js +3 -182
  19. package/src/UI/{assembly/constraintFaceUtils.js → faceUtils.js} +30 -5
  20. package/src/UI/featureDialogs.js +154 -69
  21. package/src/UI/history/HistoryCollectionWidget.js +65 -0
  22. package/src/UI/pmi/AnnotationCollectionWidget.js +1 -0
  23. package/src/UI/pmi/BaseAnnotation.js +37 -0
  24. package/src/UI/pmi/LabelOverlay.js +32 -0
  25. package/src/UI/pmi/PMIMode.js +27 -0
  26. package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +5 -0
  27. package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +5 -0
  28. package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +57 -0
  29. package/src/UI/pmi/dimensions/LeaderAnnotation.js +5 -0
  30. package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +22 -16
  31. package/src/UI/pmi/dimensions/NoteAnnotation.js +9 -0
  32. package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +81 -16
  33. package/src/UI/toolbarButtons/orientToFaceButton.js +3 -36
  34. package/src/UI/toolbarButtons/registerDefaultButtons.js +2 -0
  35. package/src/UI/toolbarButtons/selectionStateButton.js +206 -0
  36. package/src/UI/viewer.js +34 -13
  37. package/src/assemblyConstraints/AssemblyConstraintHistory.js +18 -42
  38. package/src/assemblyConstraints/constraints/AngleConstraint.js +1 -0
  39. package/src/assemblyConstraints/constraints/DistanceConstraint.js +1 -0
  40. package/src/features/selectionUtils.js +21 -5
  41. package/src/features/sketch/SketchFeature.js +2 -2
  42. package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +3 -2
  43. package/src/utils/selectionResolver.js +258 -0
@@ -0,0 +1,464 @@
1
+ import { CADmaterials } from "./CADmaterials.js";
2
+
3
+ const debugMode = false;
4
+
5
+ export class SelectionState {
6
+ static hoverColor = '#fbff00';
7
+
8
+ static attach(obj, { deep = false } = {}) {
9
+ if (!obj || typeof obj !== 'object') return false;
10
+ let changed = false;
11
+
12
+ const attachOne = (target) => {
13
+ if (!target || typeof target !== 'object') return;
14
+ const state = SelectionState._ensureState(target);
15
+ if (!state) return;
16
+ const wasAttached = !!state._attached;
17
+
18
+ const selectedDesc = Object.getOwnPropertyDescriptor(target, 'selected');
19
+ if (!selectedDesc || !selectedDesc.get || !selectedDesc.get.__selectionState) {
20
+ const getter = function () { return state.selected; };
21
+ getter.__selectionState = true;
22
+ const setter = function (v) {
23
+ const nv = !!v;
24
+ if (state.selected === nv) return;
25
+ if (debugMode) {
26
+ try {
27
+ console.log('[SelectionState] selected changed', {
28
+ name: target?.name,
29
+ type: target?.type,
30
+ prev: state.selected,
31
+ next: nv,
32
+ target,
33
+ });
34
+ console.trace('[SelectionState] selected stack');
35
+ } catch { }
36
+ }
37
+ state.selected = nv;
38
+ SelectionState.apply(target);
39
+ };
40
+ setter.__selectionState = true;
41
+ Object.defineProperty(target, 'selected', {
42
+ get: getter,
43
+ set: setter,
44
+ configurable: true,
45
+ enumerable: true,
46
+ });
47
+ changed = true;
48
+ }
49
+
50
+ const hoveredDesc = Object.getOwnPropertyDescriptor(target, 'hovered');
51
+ if (!hoveredDesc || !hoveredDesc.get || !hoveredDesc.get.__selectionState) {
52
+ const getter = function () { return state.hovered; };
53
+ getter.__selectionState = true;
54
+ const setter = function (v) {
55
+ const nv = !!v;
56
+ if (state.hovered === nv) return;
57
+ if (debugMode) {
58
+ try {
59
+ console.log('[SelectionState] hovered changed', {
60
+ name: target?.name,
61
+ type: target?.type,
62
+ prev: state.hovered,
63
+ next: nv,
64
+ target,
65
+ });
66
+ console.trace('[SelectionState] hovered stack');
67
+ } catch { }
68
+ }
69
+ state.hovered = nv;
70
+ SelectionState.apply(target);
71
+ };
72
+ setter.__selectionState = true;
73
+ Object.defineProperty(target, 'hovered', {
74
+ get: getter,
75
+ set: setter,
76
+ configurable: true,
77
+ enumerable: true,
78
+ });
79
+ changed = true;
80
+ }
81
+
82
+ SelectionState._seedBaseMaterials(target);
83
+ if (!wasAttached) state._attached = true;
84
+
85
+ if (!wasAttached && (state.selected || state.hovered)) {
86
+ SelectionState.apply(target);
87
+ }
88
+ };
89
+
90
+ if (!deep) {
91
+ attachOne(obj);
92
+ return changed;
93
+ }
94
+
95
+ const stack = [obj];
96
+ while (stack.length) {
97
+ const current = stack.pop();
98
+ attachOne(current);
99
+ const kids = Array.isArray(current?.children) ? current.children : [];
100
+ for (const child of kids) {
101
+ if (child) stack.push(child);
102
+ }
103
+ }
104
+ return changed;
105
+ }
106
+
107
+ static setHoverColor(color) {
108
+ if (!color) return;
109
+ try { SelectionState.hoverColor = String(color); } catch { }
110
+ }
111
+
112
+ static apply(obj, { force = false } = {}) {
113
+ if (!obj || typeof obj !== 'object') return;
114
+ SelectionState.attach(obj);
115
+ const state = SelectionState._getState(obj);
116
+ if (!state) return;
117
+
118
+ const type = obj.type || '';
119
+ if (type === 'SOLID' || type === 'COMPONENT') {
120
+ SelectionState._applyToSolid(obj, state, { force });
121
+ return;
122
+ }
123
+
124
+ SelectionState._applyForObject(obj, {
125
+ selected: state.selected,
126
+ hovered: state.hovered,
127
+ force,
128
+ });
129
+ }
130
+
131
+ static setBaseMaterial(obj, material, { force = true } = {}) {
132
+ if (!obj || !material) return;
133
+ SelectionState.attach(obj);
134
+ const targets = SelectionState._getDrawableTargets(obj);
135
+ for (const target of targets) {
136
+ if (!target) continue;
137
+ target.userData = target.userData || {};
138
+ target.userData.__baseMaterial = material;
139
+ }
140
+ SelectionState.apply(obj, { force: !!force });
141
+ }
142
+
143
+ static getBaseMaterial(obj, rootType = obj?.type) {
144
+ if (!obj) return null;
145
+ const ud = obj.userData || {};
146
+ if (ud.__baseMaterial) return ud.__baseMaterial;
147
+ if (ud.__defaultMaterial) return ud.__defaultMaterial;
148
+
149
+ const type = rootType || obj.type || '';
150
+ if (type === 'FACE') return CADmaterials.FACE?.BASE ?? obj.material;
151
+ if (type === 'PLANE') return CADmaterials.PLANE?.BASE ?? CADmaterials.FACE?.BASE ?? obj.material;
152
+ if (type === 'EDGE') return CADmaterials.EDGE?.BASE ?? obj.material;
153
+ if (type === 'VERTEX') return CADmaterials.VERTEX?.BASE ?? obj.material;
154
+ return obj.material ?? null;
155
+ }
156
+
157
+ static getHoverTargets(target) {
158
+ if (!target) return [];
159
+ const type = target.type || '';
160
+ if (type === 'SOLID' || type === 'COMPONENT') {
161
+ const out = [];
162
+ const kids = Array.isArray(target.children) ? target.children : [];
163
+ for (const child of kids) {
164
+ if (!child) continue;
165
+ if (child.type === 'SOLID' || child.type === 'COMPONENT') {
166
+ const nested = Array.isArray(child.children) ? child.children : [];
167
+ for (const nestedChild of nested) {
168
+ if (nestedChild && (nestedChild.type === 'FACE' || nestedChild.type === 'EDGE')) out.push(nestedChild);
169
+ }
170
+ } else if (child.type === 'FACE' || child.type === 'EDGE') {
171
+ out.push(child);
172
+ }
173
+ }
174
+ return out;
175
+ }
176
+ return [target];
177
+ }
178
+
179
+ static _ensureState(obj) {
180
+ if (!obj || typeof obj !== 'object') return null;
181
+ let state = obj.__selectionState;
182
+ if (!state) {
183
+ const existingSelected = !!obj.selected;
184
+ const existingHovered = !!obj.hovered;
185
+ state = { selected: existingSelected, hovered: existingHovered };
186
+ try {
187
+ Object.defineProperty(obj, '__selectionState', {
188
+ value: state,
189
+ writable: true,
190
+ configurable: true,
191
+ enumerable: false,
192
+ });
193
+ } catch {
194
+ obj.__selectionState = state;
195
+ }
196
+ }
197
+ return state;
198
+ }
199
+
200
+ static _getState(obj) {
201
+ return obj?.__selectionState || null;
202
+ }
203
+
204
+ static _seedBaseMaterials(obj) {
205
+ const targets = SelectionState._getDrawableTargets(obj);
206
+ for (const target of targets) {
207
+ if (!target) continue;
208
+ target.userData = target.userData || {};
209
+ if (!target.userData.__baseMaterial) {
210
+ if (target.userData.__defaultMaterial) {
211
+ target.userData.__baseMaterial = target.userData.__defaultMaterial;
212
+ } else if (target.material) {
213
+ target.userData.__baseMaterial = target.material;
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ static _getDrawableTargets(obj) {
220
+ if (!obj) return [];
221
+ if (obj.type === 'VERTEX') {
222
+ const targets = [];
223
+ if (obj._point && obj._point.material) targets.push(obj._point);
224
+ const kids = Array.isArray(obj.children) ? obj.children : [];
225
+ for (const child of kids) {
226
+ if (child && child.material && !targets.includes(child)) targets.push(child);
227
+ }
228
+ return targets.length ? targets : [obj];
229
+ }
230
+ if (obj.material) return [obj];
231
+ return [obj];
232
+ }
233
+
234
+ static _applyToSolid(obj, state, { force = false } = {}) {
235
+ const children = Array.isArray(obj.children) ? obj.children : [];
236
+ if (state.hovered) {
237
+ for (const child of children) {
238
+ if (!child) continue;
239
+ if (child.type === 'SOLID' || child.type === 'COMPONENT') {
240
+ const nested = Array.isArray(child.children) ? child.children : [];
241
+ for (const nestedChild of nested) {
242
+ if (nestedChild && (nestedChild.type === 'FACE' || nestedChild.type === 'EDGE')) {
243
+ SelectionState._applyForObject(nestedChild, { hovered: true, force });
244
+ }
245
+ }
246
+ } else if (child.type === 'FACE' || child.type === 'EDGE') {
247
+ SelectionState._applyForObject(child, { hovered: true, force });
248
+ }
249
+ }
250
+ return;
251
+ }
252
+
253
+ if (state.selected) {
254
+ for (const child of children) {
255
+ if (!child) continue;
256
+ if (child.type === 'FACE' || child.type === 'EDGE' || child.type === 'PLANE') {
257
+ SelectionState._applyForObject(child, { selected: true, force });
258
+ }
259
+ }
260
+ return;
261
+ }
262
+
263
+ for (const child of children) {
264
+ if (!child) continue;
265
+ if (child.type === 'FACE' || child.type === 'EDGE' || child.type === 'PLANE') {
266
+ SelectionState.attach(child);
267
+ const childState = SelectionState._getState(child);
268
+ SelectionState._applyForObject(child, {
269
+ selected: !!childState?.selected,
270
+ hovered: !!childState?.hovered,
271
+ force,
272
+ });
273
+ }
274
+ }
275
+ }
276
+
277
+ static _applyForObject(obj, { selected = false, hovered = false, force = false } = {}) {
278
+ if (!obj) return;
279
+ SelectionState._seedBaseMaterials(obj);
280
+ const rootType = obj.type || '';
281
+ const targets = SelectionState._getDrawableTargets(obj);
282
+ for (const target of targets) {
283
+ if (!target) continue;
284
+ if (hovered) {
285
+ SelectionState._applyHover(target, rootType, { force });
286
+ continue;
287
+ }
288
+ SelectionState._clearHover(target);
289
+ if (selected) {
290
+ SelectionState._applySelected(target, rootType);
291
+ } else {
292
+ SelectionState._applyBase(target, rootType);
293
+ }
294
+ }
295
+ }
296
+
297
+ static _applyBase(target, rootType) {
298
+ const base = SelectionState.getBaseMaterial(target, rootType);
299
+ try {
300
+ const ud = target.userData || {};
301
+ if (ud.__selectedMat && ud.__selectedMat !== base) {
302
+ if (Array.isArray(ud.__selectedMat)) {
303
+ for (const m of ud.__selectedMat) {
304
+ try { if (m && typeof m.dispose === 'function') m.dispose(); } catch { }
305
+ }
306
+ } else if (typeof ud.__selectedMat.dispose === 'function') {
307
+ ud.__selectedMat.dispose();
308
+ }
309
+ }
310
+ try { delete ud.__selectedMat; } catch { }
311
+ try { delete ud.__selectedMatBase; } catch { }
312
+ try { delete ud.__selectedColor; } catch { }
313
+ } catch { }
314
+ if (base) SelectionState._assignMaterial(target, base);
315
+ }
316
+
317
+ static _applySelected(target, rootType) {
318
+ const base = SelectionState.getBaseMaterial(target, rootType);
319
+ let mat = base;
320
+ if (rootType === 'FACE') {
321
+ mat = CADmaterials.FACE?.SELECTED ?? base;
322
+ } else if (rootType === 'PLANE') {
323
+ mat = CADmaterials.PLANE?.SELECTED ?? CADmaterials.FACE?.SELECTED ?? base;
324
+ } else if (rootType === 'EDGE') {
325
+ const selMat = CADmaterials.EDGE?.SELECTED ?? CADmaterials.EDGE?.BASE ?? base;
326
+ const selColor = SelectionState._resolveColor(selMat?.color ?? selMat?.color?.getHexString?.());
327
+ if (selColor && base) {
328
+ const ud = target.userData || (target.userData = {});
329
+ const prevMat = ud.__selectedMat;
330
+ const sameBase = ud.__selectedMatBase === base;
331
+ const sameColor = ud.__selectedColor === selColor;
332
+ if (prevMat && sameBase && sameColor) {
333
+ mat = prevMat;
334
+ } else {
335
+ if (prevMat && prevMat !== base) {
336
+ try {
337
+ if (Array.isArray(prevMat)) {
338
+ for (const m of prevMat) {
339
+ try { if (m && typeof m.dispose === 'function') m.dispose(); } catch { }
340
+ }
341
+ } else if (typeof prevMat.dispose === 'function') {
342
+ prevMat.dispose();
343
+ }
344
+ } catch { }
345
+ }
346
+ mat = SelectionState._cloneMaterialWithColor(base, selColor);
347
+ ud.__selectedMat = mat;
348
+ ud.__selectedMatBase = base;
349
+ ud.__selectedColor = selColor;
350
+ }
351
+ } else {
352
+ mat = selMat ?? base;
353
+ }
354
+ } else if (rootType === 'VERTEX') {
355
+ mat = CADmaterials.VERTEX?.SELECTED ?? base;
356
+ }
357
+ if (mat) SelectionState._assignMaterial(target, mat);
358
+ }
359
+
360
+ static _applyHover(target, rootType, { force = false } = {}) {
361
+ const ud = target.userData || (target.userData = {});
362
+ if (ud.__hoverMatApplied) {
363
+ if (!force) return;
364
+ SelectionState._clearHover(target);
365
+ }
366
+ const base = SelectionState.getBaseMaterial(target, rootType);
367
+ if (!base) return;
368
+ const hoverColor = SelectionState.hoverColor;
369
+ const hoverMat = SelectionState._cloneMaterialWithColor(base, hoverColor);
370
+ ud.__hoverOrigMat = base;
371
+ ud.__hoverMatApplied = true;
372
+ ud.__hoverMat = hoverMat;
373
+ if (hoverMat) SelectionState._assignMaterial(target, hoverMat);
374
+ }
375
+
376
+ static _clearHover(target) {
377
+ const ud = target.userData || {};
378
+ if (!ud.__hoverMatApplied) return;
379
+ try {
380
+ if (ud.__hoverMat && ud.__hoverMat !== ud.__hoverOrigMat) {
381
+ if (Array.isArray(ud.__hoverMat)) {
382
+ for (const m of ud.__hoverMat) {
383
+ try { if (m && typeof m.dispose === 'function') m.dispose(); } catch { }
384
+ }
385
+ } else if (typeof ud.__hoverMat.dispose === 'function') {
386
+ ud.__hoverMat.dispose();
387
+ }
388
+ }
389
+ } catch { }
390
+ try { delete ud.__hoverMatApplied; } catch { }
391
+ try { delete ud.__hoverOrigMat; } catch { }
392
+ try { delete ud.__hoverMat; } catch { }
393
+ }
394
+
395
+ static _assignMaterial(target, material) {
396
+ try {
397
+ const prev = target?.material;
398
+ if (prev !== material && debugMode) {
399
+ console.log('[SelectionState] material changed', {
400
+ name: target?.name,
401
+ type: target?.type,
402
+ prev,
403
+ next: material,
404
+ target,
405
+ });
406
+ console.trace('[SelectionState] material stack');
407
+ }
408
+ } catch { }
409
+ try { target.material = material; } catch { }
410
+ }
411
+
412
+ static _cloneMaterialWithColor(material, color) {
413
+ if (!material) return material;
414
+ if (Array.isArray(material)) {
415
+ return material.map((m) => SelectionState._cloneMaterialWithColor(m, color));
416
+ }
417
+ let clone = material;
418
+ try {
419
+ if (material && typeof material.clone === 'function') clone = material.clone();
420
+ } catch {
421
+ clone = material;
422
+ }
423
+ try {
424
+ if (clone && clone.color && typeof clone.color.set === 'function' && color) {
425
+ clone.color.set(color);
426
+ }
427
+ } catch { }
428
+ try {
429
+ if (material && clone && material.resolution && clone.resolution && typeof clone.resolution.copy === 'function') {
430
+ clone.resolution.copy(material.resolution);
431
+ }
432
+ } catch { }
433
+ try {
434
+ if (material && clone && typeof material.dashed !== 'undefined' && typeof clone.dashed !== 'undefined') {
435
+ clone.dashed = material.dashed;
436
+ }
437
+ if (material && clone && typeof material.dashSize !== 'undefined' && typeof clone.dashSize !== 'undefined') {
438
+ clone.dashSize = material.dashSize;
439
+ }
440
+ if (material && clone && typeof material.gapSize !== 'undefined' && typeof clone.gapSize !== 'undefined') {
441
+ clone.gapSize = material.gapSize;
442
+ }
443
+ if (material && clone && typeof material.dashScale !== 'undefined' && typeof clone.dashScale !== 'undefined') {
444
+ clone.dashScale = material.dashScale;
445
+ }
446
+ } catch { }
447
+ return clone;
448
+ }
449
+
450
+ static _resolveColor(value) {
451
+ if (!value) return null;
452
+ if (value?.isColor) return value;
453
+ if (typeof value === 'string') {
454
+ const v = value.trim();
455
+ if (/^[0-9a-fA-F]{6}$/.test(v)) return `#${v}`;
456
+ return v;
457
+ }
458
+ if (typeof value === 'number') return value;
459
+ if (typeof value?.getHexString === 'function') {
460
+ try { return `#${value.getHexString()}`; } catch { }
461
+ }
462
+ return null;
463
+ }
464
+ }
@@ -164,6 +164,8 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
164
164
 
165
165
  this.partHistory = partHistory || null;
166
166
  this.viewer = viewer || null;
167
+ this._pendingFocusField = null;
168
+ this._autoFocusOnExpand = true;
167
169
  this._highlightCallback = typeof onHighlightRequest === 'function' ? onHighlightRequest : null;
168
170
  this._clearHighlightCallback = typeof onClearHighlight === 'function' ? onClearHighlight : null;
169
171
  this._beforeConstraintChangeHandler = callBeforeChange;
@@ -174,11 +176,15 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
174
176
  this.partHistory = partHistory || null;
175
177
  }
176
178
 
177
- focusEntryById(targetId, { behavior = 'smooth' } = {}) {
179
+ focusEntryById(targetId, { behavior = 'smooth', focusField = null } = {}) {
178
180
  if (targetId == null) return;
179
181
  const id = String(targetId);
180
182
  if (!id) return;
181
183
  this._expandedId = id;
184
+ if (this._autoFocusOnExpand) {
185
+ this._pendingFocusEntryId = id;
186
+ }
187
+ this._pendingFocusField = focusField != null ? String(focusField) : null;
182
188
  this.render();
183
189
  const root = this._shadow || null;
184
190
  if (!root) return;
@@ -189,6 +195,40 @@ export class AssemblyConstraintCollectionWidget extends HistoryCollectionWidget
189
195
  }
190
196
  }
191
197
 
198
+ _applyPendingFocus() {
199
+ if (!this._autoFocusOnExpand) return;
200
+ const targetId = this._pendingFocusEntryId;
201
+ if (!targetId) return;
202
+ if (!this._expandedId || String(this._expandedId) !== String(targetId)) {
203
+ this._pendingFocusEntryId = null;
204
+ this._pendingFocusField = null;
205
+ return;
206
+ }
207
+ const form = this.getFormForEntry(targetId);
208
+ if (!form) {
209
+ this._pendingFocusEntryId = null;
210
+ this._pendingFocusField = null;
211
+ return;
212
+ }
213
+ const focusField = this._pendingFocusField;
214
+ const focus = () => {
215
+ let handled = false;
216
+ if (focusField && typeof form.activateField === 'function') {
217
+ try { handled = form.activateField(focusField) === true; } catch { /* ignore */ }
218
+ }
219
+ if (!handled) {
220
+ try {
221
+ if (typeof form.focusFirstField === 'function') form.focusFirstField();
222
+ else if (typeof form.activateFirstReferenceSelection === 'function') form.activateFirstReferenceSelection();
223
+ } catch { /* ignore */ }
224
+ }
225
+ };
226
+ if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => focus());
227
+ else setTimeout(focus, 0);
228
+ this._pendingFocusEntryId = null;
229
+ this._pendingFocusField = null;
230
+ }
231
+
192
232
  #decorateEntry(context = {}) {
193
233
  const { entry = null, elements = {}, index = 0 } = context;
194
234
  if (!elements || !elements.item) return;
@@ -7,7 +7,7 @@ import {
7
7
  computeFaceOrigin,
8
8
  computeFaceNormal,
9
9
  estimateArrowLength,
10
- } from './constraintFaceUtils.js';
10
+ } from '../faceUtils.js';
11
11
  import { applyHighlightMaterial, restoreHighlightRecords } from './constraintHighlightUtils.js';
12
12
  import { extractWorldPoint } from './constraintPointUtils.js';
13
13
  import { constraintStatusInfo } from './constraintStatusUtils.js';
@@ -209,6 +209,10 @@ export class AssemblyConstraintsWidget {
209
209
  this._scheduleSync();
210
210
  }
211
211
 
212
+ collapseExpandedDialogs() {
213
+ try { this._constraintList?.collapseExpandedEntries?.({ clearOpenState: true }); } catch { /* ignore */ }
214
+ }
215
+
212
216
  #handleHistoryChange() {
213
217
  this._scheduleSync();
214
218
  if (this._ignoreFullSolveChangeCount > 0) {
@@ -1247,6 +1251,17 @@ export class AssemblyConstraintsWidget {
1247
1251
  return null;
1248
1252
  }
1249
1253
 
1254
+ #resolveFocusFieldForConstraint(entry) {
1255
+ const constraintClass = this._resolveConstraintClass(entry);
1256
+ const field = constraintClass?.focusField;
1257
+ if (field) return field;
1258
+ const type = constraintClass?.constraintType || entry?.type || entry?.inputParams?.type;
1259
+ const normalized = typeof type === 'string' ? type.toLowerCase() : '';
1260
+ if (normalized === 'distance') return 'distance';
1261
+ if (normalized === 'angle') return 'angle';
1262
+ return null;
1263
+ }
1264
+
1250
1265
  #handleLabelClick(idx, _ann, ev) {
1251
1266
  if (idx == null) return;
1252
1267
  const id = String(idx);
@@ -1255,12 +1270,14 @@ export class AssemblyConstraintsWidget {
1255
1270
  try { ev.preventDefault(); } catch { }
1256
1271
  try { ev.stopPropagation(); } catch { }
1257
1272
  }
1273
+ const entries = this.history?.list?.() || [];
1274
+ const targetEntry = entries.find((entry) => resolveConstraintId(entry) === id) || null;
1275
+
1258
1276
  let changed = false;
1259
1277
  if (typeof this.history?.setExclusiveOpen === 'function') {
1260
1278
  changed = this.history.setExclusiveOpen(id);
1261
1279
  }
1262
1280
  if (!changed) {
1263
- const entries = this.history?.list?.() || [];
1264
1281
  for (const entry of entries) {
1265
1282
  const entryId = resolveConstraintId(entry);
1266
1283
  const shouldOpen = entryId === id;
@@ -1272,7 +1289,8 @@ export class AssemblyConstraintsWidget {
1272
1289
  this.history?.setOpenState?.(id, true);
1273
1290
  }
1274
1291
 
1275
- this._constraintList?.focusEntryById?.(id);
1292
+ const focusField = this.#resolveFocusFieldForConstraint(targetEntry);
1293
+ this._constraintList?.focusEntryById?.(id, { focusField });
1276
1294
  }
1277
1295
 
1278
1296
  _createNormalArrow(object, color, label) {