@things-factory/figure-ui 10.1.244 → 10.1.246

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 (135) hide show
  1. package/client/graphql/index.ts +15 -0
  2. package/client/modeller/align.ts +51 -13
  3. package/client/modeller/clips.ts +51 -1
  4. package/client/modeller/data-interface.ts +16 -0
  5. package/client/modeller/editor-styles.ts +6 -0
  6. package/client/modeller/editor.ts +112 -25
  7. package/client/modeller/fasten-form.ts +8 -1
  8. package/client/modeller/fasten-geometry.ts +24 -2
  9. package/client/modeller/figure-clips.ts +137 -4
  10. package/client/modeller/figure-fasten-dialog.ts +1 -0
  11. package/client/modeller/figure-fasten-drawing.ts +11 -5
  12. package/client/modeller/figure-links.ts +25 -0
  13. package/client/modeller/figure-material-panel.ts +54 -4
  14. package/client/modeller/figure-parts.ts +7 -2
  15. package/client/modeller/figure-pipe-panel.ts +215 -0
  16. package/client/modeller/figure-preview.ts +125 -4
  17. package/client/modeller/figure-section-editor.ts +36 -3
  18. package/client/modeller/links.ts +24 -2
  19. package/client/modeller/material-edit.ts +9 -0
  20. package/client/modeller/maturity.ts +15 -4
  21. package/client/modeller/palette.ts +9 -0
  22. package/client/modeller/pipe-path.ts +21 -0
  23. package/client/modeller/refusals.ts +66 -1
  24. package/client/modeller/section-controls.ts +2 -1
  25. package/client/modeller/section-drawing.ts +20 -1
  26. package/client/modeller/starting-shape.ts +8 -5
  27. package/client/pages/figure-list-page.ts +15 -5
  28. package/dist-client/graphql/index.d.ts +2 -0
  29. package/dist-client/graphql/index.js +15 -0
  30. package/dist-client/graphql/index.js.map +1 -1
  31. package/dist-client/modeller/align.d.ts +28 -7
  32. package/dist-client/modeller/align.js +37 -12
  33. package/dist-client/modeller/align.js.map +1 -1
  34. package/dist-client/modeller/clips.d.ts +24 -0
  35. package/dist-client/modeller/clips.js +53 -1
  36. package/dist-client/modeller/clips.js.map +1 -1
  37. package/dist-client/modeller/data-interface.d.ts +6 -0
  38. package/dist-client/modeller/data-interface.js +17 -0
  39. package/dist-client/modeller/data-interface.js.map +1 -1
  40. package/dist-client/modeller/editor-styles.js +6 -0
  41. package/dist-client/modeller/editor-styles.js.map +1 -1
  42. package/dist-client/modeller/editor.d.ts +10 -1
  43. package/dist-client/modeller/editor.js +117 -26
  44. package/dist-client/modeller/editor.js.map +1 -1
  45. package/dist-client/modeller/fasten-form.d.ts +2 -0
  46. package/dist-client/modeller/fasten-form.js +5 -1
  47. package/dist-client/modeller/fasten-form.js.map +1 -1
  48. package/dist-client/modeller/fasten-geometry.d.ts +8 -1
  49. package/dist-client/modeller/fasten-geometry.js +22 -1
  50. package/dist-client/modeller/fasten-geometry.js.map +1 -1
  51. package/dist-client/modeller/figure-clips.d.ts +9 -0
  52. package/dist-client/modeller/figure-clips.js +137 -4
  53. package/dist-client/modeller/figure-clips.js.map +1 -1
  54. package/dist-client/modeller/figure-fasten-dialog.js +1 -0
  55. package/dist-client/modeller/figure-fasten-dialog.js.map +1 -1
  56. package/dist-client/modeller/figure-fasten-drawing.d.ts +3 -1
  57. package/dist-client/modeller/figure-fasten-drawing.js +13 -5
  58. package/dist-client/modeller/figure-fasten-drawing.js.map +1 -1
  59. package/dist-client/modeller/figure-links.d.ts +5 -0
  60. package/dist-client/modeller/figure-links.js +22 -0
  61. package/dist-client/modeller/figure-links.js.map +1 -1
  62. package/dist-client/modeller/figure-material-panel.d.ts +2 -0
  63. package/dist-client/modeller/figure-material-panel.js +53 -4
  64. package/dist-client/modeller/figure-material-panel.js.map +1 -1
  65. package/dist-client/modeller/figure-parts.d.ts +1 -1
  66. package/dist-client/modeller/figure-parts.js +5 -1
  67. package/dist-client/modeller/figure-parts.js.map +1 -1
  68. package/dist-client/modeller/figure-pipe-panel.d.ts +29 -0
  69. package/dist-client/modeller/figure-pipe-panel.js +221 -0
  70. package/dist-client/modeller/figure-pipe-panel.js.map +1 -0
  71. package/dist-client/modeller/figure-preview.d.ts +8 -0
  72. package/dist-client/modeller/figure-preview.js +128 -4
  73. package/dist-client/modeller/figure-preview.js.map +1 -1
  74. package/dist-client/modeller/figure-section-editor.d.ts +5 -0
  75. package/dist-client/modeller/figure-section-editor.js +33 -3
  76. package/dist-client/modeller/figure-section-editor.js.map +1 -1
  77. package/dist-client/modeller/links.d.ts +6 -0
  78. package/dist-client/modeller/links.js +17 -2
  79. package/dist-client/modeller/links.js.map +1 -1
  80. package/dist-client/modeller/material-edit.d.ts +8 -0
  81. package/dist-client/modeller/material-edit.js +8 -0
  82. package/dist-client/modeller/material-edit.js.map +1 -1
  83. package/dist-client/modeller/maturity.js +16 -5
  84. package/dist-client/modeller/maturity.js.map +1 -1
  85. package/dist-client/modeller/palette.js +9 -0
  86. package/dist-client/modeller/palette.js.map +1 -1
  87. package/dist-client/modeller/pipe-path.d.ts +19 -0
  88. package/dist-client/modeller/pipe-path.js +14 -0
  89. package/dist-client/modeller/pipe-path.js.map +1 -0
  90. package/dist-client/modeller/refusals.d.ts +12 -1
  91. package/dist-client/modeller/refusals.js +67 -0
  92. package/dist-client/modeller/refusals.js.map +1 -1
  93. package/dist-client/modeller/section-controls.js +2 -1
  94. package/dist-client/modeller/section-controls.js.map +1 -1
  95. package/dist-client/modeller/section-drawing.d.ts +5 -0
  96. package/dist-client/modeller/section-drawing.js +19 -1
  97. package/dist-client/modeller/section-drawing.js.map +1 -1
  98. package/dist-client/modeller/starting-shape.d.ts +9 -2
  99. package/dist-client/modeller/starting-shape.js +8 -3
  100. package/dist-client/modeller/starting-shape.js.map +1 -1
  101. package/dist-client/pages/figure-list-page.d.ts +1 -0
  102. package/dist-client/pages/figure-list-page.js +12 -3
  103. package/dist-client/pages/figure-list-page.js.map +1 -1
  104. package/dist-client/tsconfig.tsbuildinfo +1 -1
  105. package/package.json +8 -8
  106. package/test/align.test.ts +28 -6
  107. package/test/browser/editing-page.js +21 -1
  108. package/test/browser/editing.check.mjs +124 -0
  109. package/test/browser/links.check.mjs +49 -8
  110. package/test/browser/modeller.check.mjs +27 -1
  111. package/test/browser/part-ids.check.mjs +60 -3
  112. package/test/browser/parts.check.mjs +2 -2
  113. package/test/browser/places-mesh.check.mjs +14 -8
  114. package/test/browser/screen.check.mjs +54 -1
  115. package/test/browser/section-page.js +7 -1
  116. package/test/browser/section.check.mjs +81 -0
  117. package/test/browser/side-panel-sweep.check.mjs +13 -0
  118. package/test/clips.test.ts +30 -1
  119. package/test/data-motion.test.ts +14 -1
  120. package/test/fasten-geometry.test.ts +13 -2
  121. package/test/joint-frames.test.ts +12 -2
  122. package/test/links.test.ts +24 -0
  123. package/test/material.test.ts +15 -1
  124. package/test/maturity.test.ts +19 -1
  125. package/test/palette-icons.test.ts +2 -1
  126. package/test/pipe-panel.test.ts +33 -0
  127. package/test/refusals.test.ts +29 -0
  128. package/test/revision.test.ts +1 -1
  129. package/test/section-editor.test.ts +17 -1
  130. package/translations/en.json +86 -3
  131. package/translations/es.json +86 -3
  132. package/translations/ja.json +86 -3
  133. package/translations/ko.json +86 -3
  134. package/translations/ms.json +86 -3
  135. package/translations/zh.json +86 -3
@@ -9,7 +9,7 @@ import { openPopup } from '@operato/layout';
9
9
  import { OxPrompt } from '@operato/popup';
10
10
  import { customElement, property, state } from 'lit/decorators.js';
11
11
  import { live } from 'lit/directives/live.js';
12
- import { parseFigureAsset, figureFastenAxesOf, figurePartFaceOf, PART_LIMIT, parseFigureRecipe, resolveFigureRecipe, figurePartCountOf, figurePartIdFor, figureRecipeIdFor, figureSizeEditOf, figureSizeEditorOf, figureSectionMeasuresOf, figurePlaneAlignChanges, figureRowOf, figureFaceOverhangOf } from '@hatiolab/figure-model';
12
+ import { parseFigureAsset, figureFastenAxesOf, figurePartFaceOf, PART_LIMIT, parseFigureRecipe, resolveFigureRecipe, figurePartCountOf, figurePartIdFor, figureRecipeIdFor, figureSizeEditOf, figureSizeEditorOf, figureSectionMeasuresOf, figurePlaneAlignChanges, figureLayDownOf, figureHangingChainOf, figureRowOf, figureFaceOverhangOf } from '@hatiolab/figure-model';
13
13
  import './figure-recipe-catalog.js';
14
14
  import { captureFigureThumbnail } from './thumbnail.js';
15
15
  import { drawableFigureParts } from './renderer.js';
@@ -17,6 +17,7 @@ import './canvas.js';
17
17
  import './figure-parts.js';
18
18
  import './figure-material-panel.js';
19
19
  import './figure-section-panel.js';
20
+ import './figure-pipe-panel.js';
20
21
  import './figure-section-editor.js';
21
22
  import { faceHighlightOf, rowWordsOf } from './command-preview.js';
22
23
  import { CommandPreview, NO_WORDS } from './command-preview-controller.js';
@@ -42,7 +43,7 @@ import { groupFields, ownerOf, partLabelOf, repeatOf } from './fields.js';
42
43
  import { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js';
43
44
  import { EditorStyles } from './editor-styles.js';
44
45
  import { ScrollbarStyles } from '@operato/styles';
45
- import { alignChanges, canMirror, mirrorChanges } from './align.js';
46
+ import { alignChanges, canMirror, mirrorEditOf, mirrorPlanOf } from './align.js';
46
47
  import { paletteRecipe } from './palette.js';
47
48
  import { meshChoicesOf, meshPartOf } from './mesh-import.js';
48
49
  import './figure-mesh-choice.js';
@@ -53,7 +54,7 @@ import { apexOver, drawnOf } from './section-drawing.js';
53
54
  import { END_FACE } from './section-controls.js';
54
55
  import { nudgeOf, placementOf } from './stage-model.js';
55
56
  import { gateFindingStyles, renderGateFinding } from './gate-findings.js';
56
- import { editRefusalOf, partNameRefusalOf, sizeRangeFitsOf, partRefusalOf, placedRefusalOf, recipeRefusalOf, removalNoticeOf } from './refusals.js';
57
+ import { defaultsMovedOf, editRefusalOf, partNameRefusalOf, sizeRangeFitsOf, partRefusalOf, placedRefusalOf, recipeRefusalOf, removalNoticeOf } from './refusals.js';
57
58
  import { HelpToggles } from './help.js';
58
59
  import { sizeInputOf } from './size-response.js';
59
60
  import { canFollow, followableAxes, endPlaceKeyOf, followOf, sizeFollowersOf, measureNow, partSummaryOf, instanceRangeOf, instanceSizeToDeclare, proportionToFollow, rangeWith, turnsAboutAJoint, valueNow } from './links.js';
@@ -363,11 +364,12 @@ let FigureEditor = class FigureEditor extends LitElement {
363
364
  return code ? editRefusalOf(error, (key, params) => i18next.t(key, params)) : error instanceof Error ? error.message : String(error);
364
365
  }
365
366
  run(action) {
367
+ const before = this.shown()?.asset;
366
368
  try {
367
369
  action();
368
370
  this.error = '';
369
- /* The next step stands on its own: what the last removal let go of is no longer the news. */
370
- this.notice = '';
371
+ /* The next step stands on its own: what the last removal let go of is no longer the news -- what this one moved is. */
372
+ this.notice = defaultsMovedOf(before, this.shown()?.asset, (key, params) => i18next.t(key, params));
371
373
  this.changed();
372
374
  }
373
375
  catch (error) {
@@ -777,17 +779,58 @@ let FigureEditor = class FigureEditor extends LitElement {
777
779
  this.session.apply(changes);
778
780
  });
779
781
  }
780
- /** A mirrored copy: duplicate, then place the copy on the other side — two steps. */
782
+ /**
783
+ * A mirrored copy, one step: figure-model's mirror-part, a fastened part's record reflected with it. A part with parts
784
+ * hanging from it, or moving, is asked about first -- what comes with it, its names, how it turns (robot dog 2026-11-03).
785
+ */
781
786
  mirror(axis) {
787
+ const plan = mirrorPlanOf(this.shown().asset, this.selected, axis);
788
+ if (plan.asks)
789
+ this.mirroring = { part: this.selected, form: plan.form, hanging: plan.hanging, moves: plan.moves };
790
+ else
791
+ this.mirrorWith(this.selected, plan.form);
792
+ }
793
+ mirrorWith(part, form) {
782
794
  this.run(() => {
783
- const id = this.selected;
784
- const copy = this.session.edit({ kind: 'duplicate-part', target: id }).made?.[0];
785
- if (!copy)
786
- return;
787
- this.session.apply(mirrorChanges(this.shown(), id, copy, axis));
788
- this.selected = copy;
795
+ /* The copy of the part itself, the first of what the edit made. */
796
+ const copy = this.session.edit(mirrorEditOf(part, form)).made?.[0];
797
+ this.mirroring = undefined;
798
+ if (copy)
799
+ this.selected = copy;
789
800
  });
790
801
  }
802
+ renderMirrorForm(unavailable) {
803
+ const m = this.mirroring;
804
+ if (!m || m.part !== this.selected)
805
+ return nothing;
806
+ const set = (patch) => (this.mirroring = { ...m, form: { ...m.form, ...patch } });
807
+ const text = (key) => (e) => set({ [key]: e.target.value.trim() });
808
+ return html `<div mirror-form>
809
+ ${m.hanging.length
810
+ ? html `<label toggle>
811
+ <input type="checkbox" .checked=${m.form.chain} @change=${(e) => set({ chain: e.target.checked })} />
812
+ ${i18next.t('figure.label.mirror-with-hanging', { count: m.hanging.length })}
813
+ </label>`
814
+ : nothing}
815
+ <div form>
816
+ <span>${i18next.t('figure.label.mirror-names')}</span>
817
+ <div pair>
818
+ <input aria-label=${i18next.t('figure.label.mirror-names-from')} placeholder=${i18next.t('figure.label.mirror-names-from')} .value=${m.form.from} @change=${text('from')} />
819
+ <input aria-label=${i18next.t('figure.label.mirror-names-to')} placeholder=${i18next.t('figure.label.mirror-names-to')} .value=${m.form.to} @change=${text('to')} />
820
+ </div>
821
+ </div>
822
+ ${m.moves
823
+ ? html `<label toggle title=${i18next.t('figure.text.mirror-keep-motion-explained')}>
824
+ <input type="checkbox" .checked=${m.form.keepMotion} @change=${(e) => set({ keepMotion: e.target.checked })} />
825
+ ${i18next.t('figure.label.mirror-keep-motion')}
826
+ </label>`
827
+ : nothing}
828
+ <div pair>
829
+ <button act ?disabled=${unavailable} @click=${() => this.mirrorWith(m.part, m.form)}>${i18next.t('figure.button.mirror-' + m.form.axis)}</button>
830
+ <button act @click=${() => (this.mirroring = undefined)}>${i18next.t('figure.button.cancel')}</button>
831
+ </div>
832
+ </div>`;
833
+ }
791
834
  /**
792
835
  * The one-off "put it on the plane", by the figure's placement (designer 2026-09-27): figure-model's rule
793
836
  * (`figurePlaneAlignChanges`) -- a floor figure's part to the floor by its bottom, a ceiling one's to the ceiling by
@@ -806,16 +849,31 @@ let FigureEditor = class FigureEditor extends LitElement {
806
849
  }
807
850
  });
808
851
  }
852
+ /*
853
+ A part with parts hanging from it, or one on the mounting plane, is put down as a whole: the lowest point of all of it,
854
+ as it stands, reaches the plane -- a body by its feet (robot dog 2026-11-03).
855
+ */
856
+ laysDownWhole(asset, part) {
857
+ return figureHangingChainOf(asset, part).length > 1 || !!asset.joints?.some(j => 'plane' in j.body0 && 'part' in j.body1 && j.body1.part === part);
858
+ }
809
859
  renderPlaneAlign(shown, unavailable) {
810
860
  const placement = shown.asset.placement ?? 'floor';
811
861
  /* A tilted part touches the plane at a point or an edge, a ball at its lowest; the tooltip says so rather than promise a face (designer 2026-09-27). */
812
- const contact = this.planeContactOf(shown, this.selected);
813
- const told = contact === 'tilted' || contact === 'extreme' ? `-${contact}` : '';
862
+ const contact = this.laysDownWhole(shown.asset, this.selected) ? 'hanging' : this.planeContactOf(shown, this.selected);
863
+ const told = contact === 'tilted' || contact === 'extreme' || contact === 'hanging' ? `-${contact}` : '';
814
864
  return html `<button plane-align=${placement} ?disabled=${unavailable} title=${i18next.t(`figure.text.align-to-${placement}${told}`)}
815
865
  @click=${() => this.alignToPlane()}>${i18next.t(`figure.button.align-to-${placement}`)}</button>`;
816
866
  }
817
867
  alignToPlane() {
818
868
  const asset = this.shown().asset;
869
+ if (this.laysDownWhole(asset, this.selected)) {
870
+ const { command, refusal } = figureLayDownOf(asset, this.selected);
871
+ if (refusal)
872
+ this.error = i18next.t(`figure.text.align-refused-${refusal.toLowerCase().replace(/_/g, '-')}`, { part: partLabelOf(this.selected) });
873
+ else if (command)
874
+ this.run(() => this.session.command(command));
875
+ return;
876
+ }
819
877
  const { changes, contact, refusal } = figurePlaneAlignChanges(asset, this.selected);
820
878
  if (refusal) {
821
879
  this.error = i18next.t(`figure.text.align-refused-${refusal.toLowerCase().replace(/_/g, '-')}`, { part: partLabelOf(this.selected) });
@@ -829,23 +887,33 @@ let FigureEditor = class FigureEditor extends LitElement {
829
887
  renderTools(unavailable) {
830
888
  const reach = this.reachOf(this.selected);
831
889
  const shown = this.shown();
832
- if (!this.selected || !reach.translate || !shown)
890
+ if (!this.selected || !shown)
833
891
  return null;
834
892
  /*
835
893
  * In words, two rows by what they do (user 2026-10-01: 「무엇을 하는 것인지 모르겠다」): moving the part to the figure's
836
894
  * centre lines, onto its plane or onto the grid; and making a mirrored copy, which makes a part and moves nothing.
837
895
  */
838
896
  const tool = (name, label, title) => html `<button ?disabled=${unavailable} title=${i18next.t(title)} @click=${() => this.align(name)}>${i18next.t(label)}</button>`;
839
- const mirrors = ['x', 'z'].map(axis => ({ axis, able: canMirror(shown, this.selected, axis, reach) }));
840
- return html `<div vector-row>
841
- <label>${i18next.t('figure.label.tools')}</label>
842
- <div tools>
843
- ${tool('centre-x', 'figure.button.center-x', 'figure.text.center-on-x')}
844
- ${tool('centre-z', 'figure.button.center-z', 'figure.text.center-on-z')}
845
- ${this.renderPlaneAlign(shown, unavailable)}
846
- ${tool('snap', 'figure.button.snap', 'figure.text.snap-explained')}
847
- </div>
848
- </div>
897
+ /* A fastened part is not moved by these tools, but it is mirrored: the copy's fastening is reflected with it. */
898
+ const mirrors = ['x', 'z'].map(axis => ({ axis, able: canMirror(shown, this.selected, axis) }));
899
+ /* On the mounting plane nothing else here moves it; putting it down writes its gap. */
900
+ const onPlane = !reach.translate && !!shown.asset.joints?.some(j => 'plane' in j.body0 && 'part' in j.body1 && j.body1.part === this.selected);
901
+ return html `${onPlane
902
+ ? html `<div vector-row>
903
+ <label>${i18next.t('figure.label.tools')}</label>
904
+ <div tools>${this.renderPlaneAlign(shown, unavailable)}</div>
905
+ </div>`
906
+ : nothing}${reach.translate
907
+ ? html `<div vector-row>
908
+ <label>${i18next.t('figure.label.tools')}</label>
909
+ <div tools>
910
+ ${tool('centre-x', 'figure.button.center-x', 'figure.text.center-on-x')}
911
+ ${tool('centre-z', 'figure.button.center-z', 'figure.text.center-on-z')}
912
+ ${this.renderPlaneAlign(shown, unavailable)}
913
+ ${tool('snap', 'figure.button.snap', 'figure.text.snap-explained')}
914
+ </div>
915
+ </div>`
916
+ : null}
849
917
  <div vector-row>
850
918
  <label>${i18next.t('figure.label.mirror-copy')}</label>
851
919
  <div tools>
@@ -854,6 +922,7 @@ let FigureEditor = class FigureEditor extends LitElement {
854
922
  </button>`)}
855
923
  </div>
856
924
  </div>
925
+ ${this.renderMirrorForm(unavailable)}
857
926
  `;
858
927
  }
859
928
  /**
@@ -930,6 +999,17 @@ let FigureEditor = class FigureEditor extends LitElement {
930
999
  const place = groups.vectors.filter(g => g.kind === 'vector' && g.name !== 'size');
931
1000
  const fastened = !!this.asset()?.joints?.some(j => j.body1.part === ownerOf(this.selected));
932
1001
  const placeLocked = fastened && place.every(g => g.kind === 'vector' && ['x', 'y', 'z'].every(axis => !g.cells[axis]));
1002
+ /* A pipe's measures are its section, wall and path, in its own panel (ADR-0100 §16.2): not raw fields of the part. */
1003
+ const body = geometry.find(g => g.id === this.selected);
1004
+ if (body?.shape?.sweep)
1005
+ return this.renderLinks(unavailable, 'shape', html `<section slot="size" group-fields>
1006
+ <h3>${i18next.t('figure.label.group-pipe')}</h3>
1007
+ <!-- Its command reaches the links' own handler: it is slotted in them. -->
1008
+ <figure-pipe-panel .body=${body} ?locked=${unavailable}></figure-pipe-panel>
1009
+ </section>
1010
+ ${place.length && !placeLocked
1011
+ ? html `<div slot="place" group-fields>${place.map(g => this.renderVector(g, unavailable, held))}${this.renderHeldAxes(place)}</div>`
1012
+ : nothing}`);
933
1013
  return this.renderLinks(unavailable, 'shape', html `${size.length || singles.length
934
1014
  ? html `<section slot="size" group-fields>
935
1015
  <h3>${i18next.t(integrated ? 'figure.label.group-shape' : 'figure.label.group-size')}</h3>
@@ -1079,13 +1159,20 @@ let FigureEditor = class FigureEditor extends LitElement {
1079
1159
  this.holdAction(null);
1080
1160
  this.notice = '';
1081
1161
  const hadSize = this.hasSize();
1162
+ const before = this.shown()?.asset;
1082
1163
  try {
1083
1164
  this.session.command(action);
1084
1165
  this.error = '';
1085
1166
  this.faceMapping = undefined;
1086
- /* What the command did besides what was asked, in one line: the figure's size declared to follow (ADR ruling Q1). */
1167
+ /*
1168
+ What the command did besides what was asked, in one line: the figure's size declared to follow (ADR ruling Q1), a
1169
+ default pose its new range moved.
1170
+ */
1087
1171
  if (action.kind === 'follow-size' && !hadSize)
1088
1172
  this.notice = this.sizeDeclaredWords();
1173
+ const moved = defaultsMovedOf(before, this.shown()?.asset, (key, params) => i18next.t(key, params));
1174
+ if (moved)
1175
+ this.notice = [this.notice, moved].filter(Boolean).join(' ');
1089
1176
  this.changed();
1090
1177
  return true;
1091
1178
  }
@@ -2284,6 +2371,10 @@ __decorate([
2284
2371
  state(),
2285
2372
  __metadata("design:type", Object)
2286
2373
  ], FigureEditor.prototype, "fastening", void 0);
2374
+ __decorate([
2375
+ state(),
2376
+ __metadata("design:type", Object)
2377
+ ], FigureEditor.prototype, "mirroring", void 0);
2287
2378
  __decorate([
2288
2379
  state(),
2289
2380
  __metadata("design:type", Object)