@things-factory/figure-ui 10.1.265 → 10.1.266

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 (35) hide show
  1. package/client/modeller/align.ts +13 -1
  2. package/client/modeller/clips.ts +90 -1
  3. package/client/modeller/editor-styles.ts +36 -0
  4. package/client/modeller/editor.ts +53 -6
  5. package/client/modeller/figure-clips.ts +82 -34
  6. package/client/modeller/joint-drag.ts +32 -2
  7. package/dist-client/modeller/align.d.ts +5 -0
  8. package/dist-client/modeller/align.js +10 -1
  9. package/dist-client/modeller/align.js.map +1 -1
  10. package/dist-client/modeller/clips.d.ts +56 -4
  11. package/dist-client/modeller/clips.js +89 -0
  12. package/dist-client/modeller/clips.js.map +1 -1
  13. package/dist-client/modeller/editor-styles.js +36 -0
  14. package/dist-client/modeller/editor-styles.js.map +1 -1
  15. package/dist-client/modeller/editor.d.ts +7 -0
  16. package/dist-client/modeller/editor.js +58 -5
  17. package/dist-client/modeller/editor.js.map +1 -1
  18. package/dist-client/modeller/figure-clips.d.ts +13 -1
  19. package/dist-client/modeller/figure-clips.js +87 -34
  20. package/dist-client/modeller/figure-clips.js.map +1 -1
  21. package/dist-client/modeller/joint-drag.d.ts +21 -2
  22. package/dist-client/modeller/joint-drag.js +20 -1
  23. package/dist-client/modeller/joint-drag.js.map +1 -1
  24. package/dist-client/tsconfig.tsbuildinfo +1 -1
  25. package/package.json +6 -6
  26. package/test/align.test.ts +18 -0
  27. package/test/clips.test.ts +83 -0
  28. package/test/joint-drag.test.ts +49 -0
  29. package/test/joint-frames.test.ts +3 -2
  30. package/translations/en.json +22 -2
  31. package/translations/es.json +22 -2
  32. package/translations/ja.json +22 -2
  33. package/translations/ko.json +22 -2
  34. package/translations/ms.json +22 -2
  35. package/translations/zh.json +22 -2
@@ -43,7 +43,7 @@ import { groupFields, ownerOf, partLabelOf, repeatOf } from './fields.js';
43
43
  import { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js';
44
44
  import { EditorStyles } from './editor-styles.js';
45
45
  import { ScrollbarStyles } from '@operato/styles';
46
- import { alignChanges, canMirror, mirrorEditOf, mirrorPlanOf } from './align.js';
46
+ import { alignChanges, canMirror, mirrorEditOf, mirroredClipsOf, mirrorPlanOf } from './align.js';
47
47
  import { paletteRecipe } from './palette.js';
48
48
  import { meshChoicesOf, meshPartOf } from './mesh-import.js';
49
49
  import './figure-mesh-choice.js';
@@ -60,7 +60,7 @@ import { sizeInputOf } from './size-response.js';
60
60
  import { canFollow, followableAxes, endPlaceKeyOf, followOf, sizeFollowersOf, measureNow, partSummaryOf, instanceRangeOf, instanceSizeToDeclare, proportionToFollow, rangeWith, turnsAboutAJoint, valueNow } from './links.js';
61
61
  import { jointFanOf } from './joint-fan.js';
62
62
  import { derivedOf } from './revision.js';
63
- import { jointDragOf } from './joint-drag.js';
63
+ import { jointDragOf, jointHandleOf } from './joint-drag.js';
64
64
  import { liveOverSession, NOTHING_LIVE, scrubLabel } from './live.js';
65
65
  import { startingShapeKindOfBody } from './starting-shape.js';
66
66
  import { GroupMenuStyles } from './group-menu.js';
@@ -108,6 +108,8 @@ let FigureEditor = class FigureEditor extends LitElement {
108
108
  this.moreOpen = FigureEditor_1.storedMoreOpen();
109
109
  /** What the middle shows: the parts being edited, or the figure placed on a board at eight sizes. */
110
110
  this.middle = 'edit';
111
+ /** 「관절 위치 편집」: while on, a drag on a part that hangs from a joint that moves moves where the joint sits, not its pose. Off on each new selection. */
112
+ this.jointPlacing = false;
111
113
  /** A line saying what a command did besides what was asked; gone at the next command. */
112
114
  this.notice = '';
113
115
  /**
@@ -296,8 +298,10 @@ let FigureEditor = class FigureEditor extends LitElement {
296
298
  }
297
299
  if (view && !view.geometry.some(g => ownerOf(g.id) === this.selected))
298
300
  this.selected = '';
299
- if (_changed.has('selected'))
301
+ if (_changed.has('selected')) {
300
302
  this.gizmoMode = undefined;
303
+ this.jointPlacing = false;
304
+ }
301
305
  /* The window is about the part chosen on the properties tab, being edited: it does not follow anywhere else. */
302
306
  if (this.fastening && (_changed.has('selected') || !this.selected || this.tab !== 'properties' || this.middle !== 'edit'))
303
307
  this.closeFastening();
@@ -328,6 +332,30 @@ let FigureEditor = class FigureEditor extends LitElement {
328
332
  this.selected = next;
329
333
  });
330
334
  }
335
+ /**
336
+ * On a part that hangs from a joint that moves, what a drag does, before it is dragged (review 2026-11-09): the handle
337
+ * that poses it, or why none does and where it is moved from -- and 「관절 위치 편집」 to move where the joint sits.
338
+ */
339
+ renderJointHandle(unavailable) {
340
+ const asset = this.asset();
341
+ if (unavailable || this.gizmoMode === 'scale' || !this.selected || !asset)
342
+ return nothing;
343
+ const handle = jointHandleOf(asset, this.selected);
344
+ if (!handle)
345
+ return nothing;
346
+ if (this.jointPlacing)
347
+ return html `<p joint-handle role="status">${i18next.t('figure.text.joint-placing', { joint: handle.joint })}
348
+ <button act @click=${() => (this.jointPlacing = false)}>${i18next.t('figure.button.joint-place-off')}</button></p>`;
349
+ const said = handle.kind === 'mimic'
350
+ ? i18next.t('figure.text.gizmo-joint-mimic', { joint: handle.joint, leader: handle.leader })
351
+ : handle.kind === 'several-axes'
352
+ ? i18next.t('figure.text.gizmo-joint-several-axes', { joint: handle.joint })
353
+ : i18next.t(`figure.text.joint-handle-${handle.kind}`, { joint: handle.joint });
354
+ const leaderPart = handle.kind === 'mimic' ? handle.leaderPart : undefined;
355
+ return html `<p joint-handle role="status">${said}
356
+ ${leaderPart ? html `<button act @click=${() => this.choose(leaderPart)}>${i18next.t('figure.button.select-leader', { leader: handle.leader })}</button>` : nothing}
357
+ <button act @click=${() => (this.jointPlacing = true)}>${i18next.t('figure.button.joint-place-on')}</button></p>`;
358
+ }
331
359
  /** While the size tool is on a round part: which of its handles change the one diameter (ADR-0140 4.1). */
332
360
  renderSameMeasure(unavailable) {
333
361
  if (unavailable || this.gizmoMode !== 'scale' || !this.selected)
@@ -697,12 +725,31 @@ let FigureEditor = class FigureEditor extends LitElement {
697
725
  const shown = this.shown();
698
726
  const asset = shown.asset;
699
727
  const drawn = view.geometry.find(g => g.id === id);
700
- const drag = drawn ? jointDragOf(asset, ownerOf(id), drawn, result, shown.inputs) : null;
728
+ const drag = drawn ? jointDragOf(asset, ownerOf(id), drawn, result, shown.inputs, { place: this.jointPlacing }) : null;
701
729
  if (drag) {
702
730
  if (drag.kind === 'value')
703
731
  this.run(() => this.session.apply([{ kind: 'set-state', input: drag.input, unit: drag.unit, value: drag.value }]));
704
732
  else if (drag.kind === 'joint')
705
733
  this.run(() => this.session.command({ kind: 'set-joint', joint: drag.joint }));
734
+ else if (drag.reason === 'place-off') {
735
+ /* A pose drag that would have moved the joint: nothing is written, and the way to move it is offered. */
736
+ this.refusals.point(i18next.t('figure.text.gizmo-joint-place-off', { joint: drag.joint }), {
737
+ label: i18next.t('figure.button.joint-place-on'),
738
+ run: () => (this.jointPlacing = true)
739
+ });
740
+ this.requestUpdate();
741
+ }
742
+ else if (drag.reason === 'mimic') {
743
+ /* A joint that follows another: the one it follows is a click away. */
744
+ const leader = jointHandleOf(asset, ownerOf(id));
745
+ const leaderPart = leader?.kind === 'mimic' ? leader.leaderPart : undefined;
746
+ const message = i18next.t('figure.text.gizmo-joint-mimic', { joint: drag.joint, leader: drag.leader ?? '' });
747
+ if (leaderPart)
748
+ this.refusals.point(message, { label: i18next.t('figure.button.select-leader', { leader: drag.leader ?? '' }), run: () => this.choose(leaderPart) });
749
+ else
750
+ this.refusals.say(message);
751
+ this.requestUpdate();
752
+ }
706
753
  else {
707
754
  this.error = i18next.t(`figure.text.gizmo-joint-${drag.reason}`, { joint: drag.joint, leader: drag.leader ?? '', axis: drag.axis ?? '' });
708
755
  this.requestUpdate();
@@ -825,6 +872,7 @@ let FigureEditor = class FigureEditor extends LitElement {
825
872
  ${i18next.t('figure.label.mirror-keep-motion')}
826
873
  </label>`
827
874
  : nothing}
875
+ ${(clips => (clips.length ? html `<p hint mirror-clips>${i18next.t('figure.text.mirror-copies-clips', { clips: clips.join(', ') })}</p>` : nothing))(mirroredClipsOf(this.shown().asset, m.part, m.form.chain))}
828
876
  <div pair>
829
877
  <button act ?disabled=${unavailable} @click=${() => this.mirrorWith(m.part, m.form)}>${i18next.t('figure.button.mirror-' + m.form.axis)}</button>
830
878
  <button act @click=${() => (this.mirroring = undefined)}>${i18next.t('figure.button.cancel')}</button>
@@ -1122,6 +1170,7 @@ let FigureEditor = class FigureEditor extends LitElement {
1122
1170
  @states=${(e) => this.run(() => this.session.apply(e.detail.changes))}
1123
1171
  @states-live=${(e) => this.hold(e.detail.values)}
1124
1172
  @part-edit=${(e) => this.run(() => this.session.edit(e.detail.edit))}
1173
+ @part-edits=${(e) => this.run(() => this.session.applyRecipe(e.detail.edits))}
1125
1174
  ></figure-clips>`;
1126
1175
  }
1127
1176
  /** One command, one step of the history. A refusal a person can meet here is said in their words. */
@@ -2096,7 +2145,7 @@ let FigureEditor = class FigureEditor extends LitElement {
2096
2145
  @render-error=${(e) => {
2097
2146
  this.error = e.detail.message;
2098
2147
  }}
2099
- ></figure-canvas>${this.renderSameMeasure(unavailable)}</div>
2148
+ ></figure-canvas>${this.renderSameMeasure(unavailable)}${this.renderJointHandle(unavailable)}</div>
2100
2149
  <aside slot="end">
2101
2150
  <div tabs>
2102
2151
  ${this.renderTab('properties', 'tune', i18next.t('figure.label.properties'))}
@@ -2399,6 +2448,10 @@ __decorate([
2399
2448
  state(),
2400
2449
  __metadata("design:type", String)
2401
2450
  ], FigureEditor.prototype, "gizmoMode", void 0);
2451
+ __decorate([
2452
+ state(),
2453
+ __metadata("design:type", Object)
2454
+ ], FigureEditor.prototype, "jointPlacing", void 0);
2402
2455
  __decorate([
2403
2456
  state(),
2404
2457
  __metadata("design:type", Object)