@things-factory/figure-ui 10.1.206 → 10.1.207

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 (68) hide show
  1. package/client/modeller/editor.ts +11 -97
  2. package/client/modeller/figure-facts.ts +1 -1
  3. package/client/modeller/figure-history-panel.ts +1 -29
  4. package/client/modeller/figure-preview.ts +5 -7
  5. package/client/modeller/figure-verdict.ts +2 -17
  6. package/client/modeller/maturity.ts +11 -27
  7. package/client/modeller/refusals.ts +1 -7
  8. package/client/pages/figure-modeller-page.ts +7 -22
  9. package/dist-client/modeller/editor.d.ts +1 -8
  10. package/dist-client/modeller/editor.js +12 -98
  11. package/dist-client/modeller/editor.js.map +1 -1
  12. package/dist-client/modeller/figure-facts.js +1 -1
  13. package/dist-client/modeller/figure-facts.js.map +1 -1
  14. package/dist-client/modeller/figure-fasten-dialog.d.ts +1 -1
  15. package/dist-client/modeller/figure-history-panel.d.ts +0 -16
  16. package/dist-client/modeller/figure-history-panel.js +1 -27
  17. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  18. package/dist-client/modeller/figure-links.d.ts +1 -1
  19. package/dist-client/modeller/figure-preview.js +5 -8
  20. package/dist-client/modeller/figure-preview.js.map +1 -1
  21. package/dist-client/modeller/figure-verdict.d.ts +0 -5
  22. package/dist-client/modeller/figure-verdict.js +2 -21
  23. package/dist-client/modeller/figure-verdict.js.map +1 -1
  24. package/dist-client/modeller/maturity.d.ts +2 -4
  25. package/dist-client/modeller/maturity.js +12 -26
  26. package/dist-client/modeller/maturity.js.map +1 -1
  27. package/dist-client/modeller/refusals.d.ts +1 -3
  28. package/dist-client/modeller/refusals.js +1 -7
  29. package/dist-client/modeller/refusals.js.map +1 -1
  30. package/dist-client/pages/figure-modeller-page.d.ts +1 -3
  31. package/dist-client/pages/figure-modeller-page.js +7 -23
  32. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  33. package/dist-client/route.d.ts +1 -1
  34. package/dist-client/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +6 -6
  36. package/test/browser/acceptance-page.js +3 -3
  37. package/test/browser/detail-page.js +1 -3
  38. package/test/browser/detail-tabs-page.js +1 -2
  39. package/test/browser/links.check.mjs +2 -37
  40. package/test/browser/modeller.check.mjs +1 -3
  41. package/test/browser/parts-page.js +2 -2
  42. package/test/browser/parts.check.mjs +2 -12
  43. package/test/browser/product-page.check.mjs +6 -9
  44. package/test/browser/product-page.js +1 -2
  45. package/test/browser/screen-page.js +2 -6
  46. package/test/browser/screen.check.mjs +8 -65
  47. package/test/first-minute.test.ts +2 -3
  48. package/test/fixtures/joint-column.json +1 -66
  49. package/test/fixtures/joint-roller.json +1 -66
  50. package/test/fixtures/robot-arm-6dof.json +2 -67
  51. package/test/links.test.ts +1 -1
  52. package/test/parts-wiring.test.ts +1 -1
  53. package/test/properties.test.ts +9 -12
  54. package/test/refusals.test.ts +3 -21
  55. package/test/resize-with-placement.test.ts +13 -15
  56. package/test/revision.test.ts +1 -2
  57. package/test/verdict.test.ts +1 -15
  58. package/translations/en.json +0 -19
  59. package/translations/es.json +0 -19
  60. package/translations/ja.json +0 -19
  61. package/translations/ko.json +0 -19
  62. package/translations/ms.json +0 -19
  63. package/translations/zh.json +0 -19
  64. package/client/modeller/occupancy.ts +0 -51
  65. package/dist-client/modeller/occupancy.d.ts +0 -31
  66. package/dist-client/modeller/occupancy.js +0 -36
  67. package/dist-client/modeller/occupancy.js.map +0 -1
  68. package/test/release-panel.test.ts +0 -53
@@ -4,10 +4,9 @@ import { LitElement, html, nothing } from 'lit';
4
4
  import { i18next } from '@operato/i18n';
5
5
  import '@operato/layout';
6
6
  import { openPopup } from '@operato/layout';
7
- import { OxPrompt } from '@operato/popup';
8
7
  import { customElement, property, state } from 'lit/decorators.js';
9
8
  import { live } from 'lit/directives/live.js';
10
- import { figurePartFaceOf, PART_LIMIT, parseFigureRecipe, resolveFigureRecipe, figurePartCountOf, figurePartIdFor, figureRecipeIdFor, figureSizeEditOf, figureSizeEditorOf, figureSectionMeasuresOf, figurePlaneAlignChanges, figureRowOf, figureFaceOverhangOf, figureRoomOnlyInputsOf, figureRoomRefitOf } from '@hatiolab/figure-model';
9
+ import { figurePartFaceOf, PART_LIMIT, parseFigureRecipe, resolveFigureRecipe, figurePartCountOf, figurePartIdFor, figureRecipeIdFor, figureSizeEditOf, figureSizeEditorOf, figureSectionMeasuresOf, figurePlaneAlignChanges, figureRowOf, figureFaceOverhangOf } from '@hatiolab/figure-model';
11
10
  import './figure-recipe-catalog.js';
12
11
  import { captureFigureThumbnail } from './thumbnail.js';
13
12
  import { drawableFigureParts } from './renderer.js';
@@ -48,11 +47,10 @@ const NO_MARKS = [];
48
47
  import { apexOver, drawnOf } from './section-drawing.js';
49
48
  import { END_FACE } from './section-controls.js';
50
49
  import { nudgeOf, placementOf } from './stage-model.js';
51
- import { occupancyFromParts, roomRefitRowsOf } from './occupancy.js';
52
50
  import { gateFindingStyles, renderGateFinding } from './gate-findings.js';
53
- import { editRefusalOf, inDeclaredRoom, partNameRefusalOf, sizeRangeFitsOf, partRefusalOf, placedRefusalOf, recipeRefusalOf, removalNoticeOf } from './refusals.js';
51
+ import { editRefusalOf, partNameRefusalOf, sizeRangeFitsOf, partRefusalOf, placedRefusalOf, recipeRefusalOf, removalNoticeOf } from './refusals.js';
54
52
  import { HelpToggles } from './help.js';
55
- import { endPlaceKeyOf, fieldWordsOf, followOf, sizeFollowersOf, measureNow, partSummaryOf, instanceRangeOf, instanceSizeToDeclare, proportionToFollow, rangeWith, turnsAboutAJoint, valueNow } from './links.js';
53
+ import { endPlaceKeyOf, followOf, sizeFollowersOf, measureNow, partSummaryOf, instanceRangeOf, instanceSizeToDeclare, proportionToFollow, rangeWith, turnsAboutAJoint, valueNow } from './links.js';
56
54
  import { jointFanOf } from './joint-fan.js';
57
55
  import { derivedOf } from './revision.js';
58
56
  import { jointDragOf } from './joint-drag.js';
@@ -508,9 +506,9 @@ let FigureEditor = class FigureEditor extends LitElement {
508
506
  movePart(id, to) {
509
507
  this.run(() => this.session.edit({ kind: 'move-part', target: id, to }));
510
508
  }
511
- removePart(id, refitRoom = false) {
509
+ removePart(id) {
512
510
  try {
513
- const view = this.session.edit({ kind: 'remove-part', target: id, ...(refitRoom ? { refitRoom: true } : {}) });
511
+ const view = this.session.edit({ kind: 'remove-part', target: id });
514
512
  this.error = '';
515
513
  /* What else the removal let go of, named (chief architect, 2026-09-27); undo brings it back. */
516
514
  this.notice = removalNoticeOf(view.removal, (key, params) => i18next.t(key, params)) ?? '';
@@ -519,19 +517,8 @@ let FigureEditor = class FigureEditor extends LitElement {
519
517
  this.changed();
520
518
  }
521
519
  catch (error) {
522
- if (this.refusals.askClipLoss(error, () => this.removePart(id, refitRoom)))
520
+ if (this.refusals.askClipLoss(error, () => this.removePart(id)))
523
521
  return;
524
- /*
525
- A part in the declared space goes, and the space is fitted to the parts left, in the same click: every space
526
- this product declares is fitted to the parts (「부품에 맞추기」, the AI's fit-room -- no padding), so fitting it
527
- again keeps what it says, and there is nothing to ask (user, 2026-09-27: 「이 확인이 꼭 필요한가 ?」).
528
- */
529
- if (!refitRoom && inDeclaredRoom(error)) {
530
- this.removePart(id, true);
531
- if (!this.error)
532
- this.notice = [this.notice, i18next.t('figure.text.removal-space-refitted')].filter(Boolean).join(' ');
533
- return;
534
- }
535
522
  this.error = partRefusalOf(error, id, (key, params) => i18next.t(key, params)) ?? this.said(error);
536
523
  }
537
524
  }
@@ -989,7 +976,6 @@ let FigureEditor = class FigureEditor extends LitElement {
989
976
  this.holdAction(null);
990
977
  this.notice = '';
991
978
  const hadSize = this.hasSize();
992
- const hadSpace = !!this.shown()?.asset.occupancy;
993
979
  try {
994
980
  this.session.command(action);
995
981
  this.error = '';
@@ -997,9 +983,7 @@ let FigureEditor = class FigureEditor extends LitElement {
997
983
  /* What the command did besides what was asked, in one line: the figure's size declared to follow (ADR ruling Q1). */
998
984
  if (action.kind === 'follow-size' && !hadSize) {
999
985
  const size = this.designValues();
1000
- /* The same step may have fitted the space the figure takes to its parts (figure-model, ruling 2026-10-16 Q1 A): one line says both. */
1001
- const spaceToo = !hadSpace && !!this.shown()?.asset.occupancy;
1002
- this.notice = i18next.t(spaceToo ? 'figure.text.size-and-space-declared-to-follow' : 'figure.text.size-declared-to-follow', {
986
+ this.notice = i18next.t('figure.text.size-declared-to-follow', {
1003
987
  x: Math.round(size['size.x'] ?? 0).toLocaleString(),
1004
988
  y: Math.round(size['size.y'] ?? 0).toLocaleString(),
1005
989
  z: Math.round(size['size.z'] ?? 0).toLocaleString()
@@ -1440,18 +1424,13 @@ let FigureEditor = class FigureEditor extends LitElement {
1440
1424
  return i18next.t('figure.text.release-state-released', { version: String(version) });
1441
1425
  return version > 0 ? i18next.t('figure.text.release-state-draft-over', { version: String(version) }) : i18next.t('figure.text.release-state-draft-new');
1442
1426
  }
1443
- /** The findings that block release, under the bar beside 「발행」, each with the way out the verdict tab offers for it. */
1444
- renderReleaseBlockers(blockers, asset, unavailable) {
1427
+ /** The findings that block release, under the bar beside 「발행」. */
1428
+ renderReleaseBlockers(blockers, asset) {
1445
1429
  const parts = (asset?.appearance ?? []).map(look => look.target);
1446
- const way = (code) => (code === 'occupancy-undeclared' || code === 'part-outside-occupancy') && asset && parts.length
1447
- ? html `<button act ?disabled=${unavailable} @click=${() => this.linkCommand(occupancyFromParts(asset))}>
1448
- <md-icon>fit_screen</md-icon>${i18next.t('figure.button.fit-base-to-parts')}
1449
- </button>`
1450
- : nothing;
1451
1430
  return html `<div release-blockers role="dialog" aria-label=${i18next.t('figure.text.must-fix-to-release')} @keydown=${(e) => e.key === 'Escape' && (this.blockersOpen = false)}>
1452
1431
  <ul gate>
1453
1432
  <li head>${i18next.t('figure.text.must-fix-to-release')}</li>
1454
- ${blockers.map(finding => renderGateFinding(finding, true, way(finding.code), parts))}
1433
+ ${blockers.map(finding => renderGateFinding(finding, true, nothing, parts))}
1455
1434
  </ul>
1456
1435
  </div>`;
1457
1436
  }
@@ -1786,20 +1765,9 @@ let FigureEditor = class FigureEditor extends LitElement {
1786
1765
  /* A repeat's own row values (its pitch) have their own section, not the shape's. */
1787
1766
  const own = design.filter(i => this.selected && i.id.startsWith(this.selected + '.') && !i.id.startsWith(this.selected + '.repeat.'));
1788
1767
  const common = design.filter(i => !view.geometry.some(g => i.id.startsWith(ownerOf(g.id) + '.')));
1789
- /*
1790
- A part's field only the declared room still reads -- its old height position once the floor holds it -- moves
1791
- nothing; it is shown apart as what it is, not as a position (ADR-0097, designer 2026-09-27).
1792
- */
1793
1768
  const shown = this.shown();
1794
1769
  const asset = shown?.asset;
1795
- let roomOnly = [];
1796
- try {
1797
- roomOnly = this.selected && asset ? figureRoomOnlyInputsOf(asset, this.selected) : [];
1798
- }
1799
- catch {
1800
- roomOnly = [];
1801
- }
1802
- const partFields = own.filter(i => !roomOnly.includes(i.id));
1770
+ const partFields = own;
1803
1771
  /* The numbers a rule gives the axes that have no field of their own -- shown read-only, never as a blank. */
1804
1772
  let held;
1805
1773
  try {
@@ -1817,7 +1785,6 @@ let FigureEditor = class FigureEditor extends LitElement {
1817
1785
  catch {
1818
1786
  held = undefined;
1819
1787
  }
1820
- const roomFields = own.filter(i => roomOnly.includes(i.id));
1821
1788
  /*
1822
1789
  Only what is under way is said. Saved or not is the save button's: filled and pressable when there is something
1823
1790
  to save, dimmed when not -- the saved and unsaved words beside it said the same thing again in the primary
@@ -1868,7 +1835,7 @@ let FigureEditor = class FigureEditor extends LitElement {
1868
1835
  </button>
1869
1836
  <button slot="commit" save ?disabled=${unavailable || (!view.dirty && !!view.id)} @click=${this.save}><md-icon>save</md-icon>${i18next.t('figure.button.save')}</button>
1870
1837
  </ox-canvas-bar>
1871
- ${this.blockersOpen && blockers.length ? this.renderReleaseBlockers(blockers, asset, unavailable) : nothing}
1838
+ ${this.blockersOpen && blockers.length ? this.renderReleaseBlockers(blockers, asset) : nothing}
1872
1839
  ${this.catalogOpen
1873
1840
  ? html `<figure-recipe-catalog
1874
1841
  .templates=${this.templates}
@@ -1976,7 +1943,6 @@ let FigureEditor = class FigureEditor extends LitElement {
1976
1943
  ${this.renderRepeatNote(view.geometry)}
1977
1944
  </section>
1978
1945
  ${this.renderPartGroups(shown, view.geometry, partFields, unavailable, held)}
1979
- ${roomFields.length ? this.renderRoomOnly(roomFields, unavailable) : nothing}
1980
1946
  <figure-material-panel
1981
1947
  .body=${{ ...part, id: this.selected }}
1982
1948
  .palette=${view.palette ?? {}}
@@ -2118,58 +2084,6 @@ let FigureEditor = class FigureEditor extends LitElement {
2118
2084
  return html `<p hint held-axes>${i18next.t('figure.text.held-by-fastening')}</p>`;
2119
2085
  return html `<p hint held-axes>${i18next.t('figure.text.held-worked-out', { group: held.map(g => i18next.t('figure.label.group-' + g.name)).join(' · ') })}</p>`;
2120
2086
  }
2121
- /**
2122
- * A part's fields only the declared room still reads (ADR-0097, designer 2026-09-27): shown as what they are and not
2123
- * typed into -- they move nothing. Declaring the room again is offered; nothing is deleted or widened on the way.
2124
- */
2125
- renderRoomOnly(fields, unavailable) {
2126
- return html `<section room-only>
2127
- <h3>${i18next.t('figure.label.room-only-inputs')}</h3>
2128
- <p hint>${i18next.t('figure.text.room-only-inputs')}</p>
2129
- <div form>
2130
- ${fields.map(f => html `<span>${fieldWordsOf(f.id, key => i18next.t(key))} (${f.unit})</span><input aria-label=${f.id} type="number" .value=${String(f.value)} disabled />`)}
2131
- </div>
2132
- <button act room-refit ?disabled=${unavailable} @click=${() => this.askRoomRefit()}><md-icon>fit_screen</md-icon>${i18next.t('figure.button.room-refit')}</button>
2133
- </section>`;
2134
- }
2135
- /** The room as declared and as proposed, and what lies outside each as measured, before the author confirms. */
2136
- async askRoomRefit() {
2137
- let refit;
2138
- try {
2139
- refit = figureRoomRefitOf(this.shown().asset);
2140
- }
2141
- catch (error) {
2142
- this.error = this.said(error);
2143
- return;
2144
- }
2145
- if (!refit)
2146
- return;
2147
- const rows = roomRefitRowsOf(refit);
2148
- const outside = (list) => list.length
2149
- ? list.map(o => i18next.t('figure.text.room-part-outside', { part: partLabelOf(o.part), axis: o.axis.toUpperCase(), mm: String(o.mm) })).join(' · ')
2150
- : i18next.t('figure.text.room-nothing-outside');
2151
- const agreed = await OxPrompt.open({
2152
- type: 'question',
2153
- title: i18next.t('figure.label.room-refit'),
2154
- template: html `<table room-refit>
2155
- <tr><th></th><th>${i18next.t('figure.label.room-declared')}</th><th>${i18next.t('figure.label.room-proposed')}</th></tr>
2156
- ${rows.map(r => html `<tr>
2157
- <td>${i18next.t(`figure.label.room-at-${r.at}`)} ${r.axis.toUpperCase()}</td>
2158
- <td>${r.declared}</td>
2159
- <td>${r.same ? i18next.t('figure.text.room-same') : r.proposed}</td>
2160
- </tr>`)}
2161
- </table>
2162
- <p>${i18next.t('figure.label.room-declared')}: ${outside(refit.outside.declared)}</p>
2163
- <p>${i18next.t('figure.label.room-proposed')}: ${outside(refit.outside.proposed)}</p>
2164
- ${refit.drops.length
2165
- ? html `<p>${i18next.t('figure.text.room-drops', { fields: [...new Set(refit.drops.map(id => `${partLabelOf(id.split('.')[0])} ${fieldWordsOf(id, key => i18next.t(key))}`))].join(' · ') })}</p>`
2166
- : nothing}`,
2167
- confirmButton: { text: i18next.t('figure.button.room-refit-confirm') },
2168
- cancelButton: { text: i18next.t('figure.button.keep-editing') }
2169
- });
2170
- if (agreed)
2171
- this.linkCommand(refit.step);
2172
- }
2173
2087
  renderSingle(name, field, unavailable) {
2174
2088
  const words = name.split('.').map(part => i18next.t('figure.label.field-' + part, { defaultValue: part }));
2175
2089
  return html `<span ?scrub=${!unavailable} @pointerdown=${(e) => this.scrub(field, e, unavailable)}>${words.join(' · ')} (${field.unit})</span