@things-factory/figure-ui 10.1.147 → 10.1.150

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 (210) hide show
  1. package/client/bootstrap.ts +10 -2
  2. package/client/graphql/index.ts +20 -0
  3. package/client/modeller/canvas.ts +6 -2
  4. package/client/modeller/carrier-declaration.ts +63 -0
  5. package/client/modeller/editor.ts +62 -27
  6. package/client/modeller/figure-carrier.ts +117 -0
  7. package/client/modeller/figure-cavity-drawing.ts +3 -9
  8. package/client/modeller/figure-first-save.ts +4 -7
  9. package/client/modeller/figure-history-panel.ts +2 -3
  10. package/client/modeller/figure-links.ts +46 -60
  11. package/client/modeller/figure-material-panel.ts +4 -14
  12. package/client/modeller/figure-mesh-choice.ts +68 -0
  13. package/client/modeller/figure-parts.ts +23 -1
  14. package/client/modeller/figure-preview.ts +2 -2
  15. package/client/modeller/figure-properties.ts +5 -3
  16. package/client/modeller/figure-section-editor.ts +12 -33
  17. package/client/modeller/figure-section-panel.ts +6 -7
  18. package/client/modeller/figure-slot-layout-panel.ts +208 -0
  19. package/client/modeller/figure-verdict.ts +21 -31
  20. package/client/modeller/first-save.ts +23 -0
  21. package/client/modeller/links.ts +52 -1
  22. package/client/modeller/material-edit.ts +38 -0
  23. package/client/modeller/maturity.ts +28 -0
  24. package/client/modeller/mesh-import.ts +77 -0
  25. package/client/modeller/modeller-route.ts +9 -0
  26. package/client/modeller/mounting-part.ts +15 -0
  27. package/client/modeller/newest-answer.ts +21 -0
  28. package/client/modeller/preview-model.ts +5 -0
  29. package/client/modeller/refusals.ts +168 -82
  30. package/client/modeller/section-controls.ts +25 -1
  31. package/client/modeller/section-drawing.ts +43 -1
  32. package/client/modeller/slot-layout-host.ts +140 -0
  33. package/client/pages/figure-modeller-page.ts +6 -5
  34. package/client/viewparts/detail-route.ts +7 -0
  35. package/client/viewparts/figure-detail-dock.ts +1 -3
  36. package/dist-client/bootstrap.d.ts +1 -1
  37. package/dist-client/bootstrap.js +9 -2
  38. package/dist-client/bootstrap.js.map +1 -1
  39. package/dist-client/graphql/index.d.ts +5 -0
  40. package/dist-client/graphql/index.js +19 -0
  41. package/dist-client/graphql/index.js.map +1 -1
  42. package/dist-client/modeller/canvas.d.ts +2 -0
  43. package/dist-client/modeller/canvas.js +10 -2
  44. package/dist-client/modeller/canvas.js.map +1 -1
  45. package/dist-client/modeller/carrier-declaration.d.ts +31 -0
  46. package/dist-client/modeller/carrier-declaration.js +53 -0
  47. package/dist-client/modeller/carrier-declaration.js.map +1 -0
  48. package/dist-client/modeller/editor.d.ts +13 -0
  49. package/dist-client/modeller/editor.js +62 -27
  50. package/dist-client/modeller/editor.js.map +1 -1
  51. package/dist-client/modeller/figure-carrier.d.ts +26 -0
  52. package/dist-client/modeller/figure-carrier.js +123 -0
  53. package/dist-client/modeller/figure-carrier.js.map +1 -0
  54. package/dist-client/modeller/figure-cavity-drawing.js +3 -11
  55. package/dist-client/modeller/figure-cavity-drawing.js.map +1 -1
  56. package/dist-client/modeller/figure-first-save.d.ts +4 -0
  57. package/dist-client/modeller/figure-first-save.js +5 -8
  58. package/dist-client/modeller/figure-first-save.js.map +1 -1
  59. package/dist-client/modeller/figure-history-panel.js +2 -4
  60. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  61. package/dist-client/modeller/figure-links.d.ts +4 -1
  62. package/dist-client/modeller/figure-links.js +47 -57
  63. package/dist-client/modeller/figure-links.js.map +1 -1
  64. package/dist-client/modeller/figure-material-panel.d.ts +0 -1
  65. package/dist-client/modeller/figure-material-panel.js +4 -14
  66. package/dist-client/modeller/figure-material-panel.js.map +1 -1
  67. package/dist-client/modeller/figure-mesh-choice.d.ts +16 -0
  68. package/dist-client/modeller/figure-mesh-choice.js +75 -0
  69. package/dist-client/modeller/figure-mesh-choice.js.map +1 -0
  70. package/dist-client/modeller/figure-parts.d.ts +1 -1
  71. package/dist-client/modeller/figure-parts.js +22 -0
  72. package/dist-client/modeller/figure-parts.js.map +1 -1
  73. package/dist-client/modeller/figure-preview.js +2 -2
  74. package/dist-client/modeller/figure-preview.js.map +1 -1
  75. package/dist-client/modeller/figure-properties.d.ts +2 -1
  76. package/dist-client/modeller/figure-properties.js +5 -3
  77. package/dist-client/modeller/figure-properties.js.map +1 -1
  78. package/dist-client/modeller/figure-section-editor.d.ts +2 -2
  79. package/dist-client/modeller/figure-section-editor.js +12 -33
  80. package/dist-client/modeller/figure-section-editor.js.map +1 -1
  81. package/dist-client/modeller/figure-section-panel.js +6 -7
  82. package/dist-client/modeller/figure-section-panel.js.map +1 -1
  83. package/dist-client/modeller/figure-slot-layout-panel.d.ts +36 -0
  84. package/dist-client/modeller/figure-slot-layout-panel.js +198 -0
  85. package/dist-client/modeller/figure-slot-layout-panel.js.map +1 -0
  86. package/dist-client/modeller/figure-verdict.d.ts +1 -1
  87. package/dist-client/modeller/figure-verdict.js +19 -37
  88. package/dist-client/modeller/figure-verdict.js.map +1 -1
  89. package/dist-client/modeller/first-save.d.ts +10 -0
  90. package/dist-client/modeller/first-save.js +16 -0
  91. package/dist-client/modeller/first-save.js.map +1 -1
  92. package/dist-client/modeller/links.d.ts +26 -1
  93. package/dist-client/modeller/links.js +43 -0
  94. package/dist-client/modeller/links.js.map +1 -1
  95. package/dist-client/modeller/material-edit.d.ts +11 -0
  96. package/dist-client/modeller/material-edit.js +28 -0
  97. package/dist-client/modeller/material-edit.js.map +1 -0
  98. package/dist-client/modeller/maturity.d.ts +15 -0
  99. package/dist-client/modeller/maturity.js +23 -0
  100. package/dist-client/modeller/maturity.js.map +1 -1
  101. package/dist-client/modeller/mesh-import.d.ts +47 -0
  102. package/dist-client/modeller/mesh-import.js +47 -0
  103. package/dist-client/modeller/mesh-import.js.map +1 -0
  104. package/dist-client/modeller/modeller-route.d.ts +5 -0
  105. package/dist-client/modeller/modeller-route.js +9 -0
  106. package/dist-client/modeller/modeller-route.js.map +1 -1
  107. package/dist-client/modeller/mounting-part.d.ts +14 -0
  108. package/dist-client/modeller/mounting-part.js +10 -0
  109. package/dist-client/modeller/mounting-part.js.map +1 -0
  110. package/dist-client/modeller/newest-answer.d.ts +12 -0
  111. package/dist-client/modeller/newest-answer.js +21 -0
  112. package/dist-client/modeller/newest-answer.js.map +1 -0
  113. package/dist-client/modeller/preview-model.d.ts +2 -0
  114. package/dist-client/modeller/preview-model.js +4 -0
  115. package/dist-client/modeller/preview-model.js.map +1 -1
  116. package/dist-client/modeller/refusals.d.ts +26 -5
  117. package/dist-client/modeller/refusals.js +184 -86
  118. package/dist-client/modeller/refusals.js.map +1 -1
  119. package/dist-client/modeller/section-controls.d.ts +17 -1
  120. package/dist-client/modeller/section-controls.js +18 -0
  121. package/dist-client/modeller/section-controls.js.map +1 -1
  122. package/dist-client/modeller/section-drawing.d.ts +24 -1
  123. package/dist-client/modeller/section-drawing.js +42 -0
  124. package/dist-client/modeller/section-drawing.js.map +1 -1
  125. package/dist-client/modeller/slot-layout-host.d.ts +81 -0
  126. package/dist-client/modeller/slot-layout-host.js +105 -0
  127. package/dist-client/modeller/slot-layout-host.js.map +1 -0
  128. package/dist-client/pages/figure-modeller-page.js +5 -4
  129. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  130. package/dist-client/tsconfig.tsbuildinfo +1 -1
  131. package/dist-client/viewparts/detail-route.d.ts +5 -0
  132. package/dist-client/viewparts/detail-route.js +8 -0
  133. package/dist-client/viewparts/detail-route.js.map +1 -0
  134. package/dist-client/viewparts/figure-detail-dock.d.ts +0 -1
  135. package/dist-client/viewparts/figure-detail-dock.js +1 -3
  136. package/dist-client/viewparts/figure-detail-dock.js.map +1 -1
  137. package/package.json +9 -8
  138. package/test/align.test.ts +0 -9
  139. package/test/browser/assistant-stand-in.js +32 -0
  140. package/test/browser/build.check.mjs +77 -0
  141. package/test/browser/build.mjs +31 -6
  142. package/test/browser/editing-page.js +224 -0
  143. package/test/browser/editing.check.mjs +550 -0
  144. package/test/browser/links-page.js +113 -0
  145. package/test/browser/links.check.mjs +568 -0
  146. package/test/browser/modeller.check.mjs +21 -0
  147. package/test/browser/parts-page.js +120 -0
  148. package/test/browser/parts.check.mjs +328 -0
  149. package/test/browser/places-mesh-page.js +36 -0
  150. package/test/browser/places-mesh.check.mjs +239 -0
  151. package/test/browser/product-page.check.mjs +682 -0
  152. package/test/browser/product-page.js +276 -0
  153. package/test/browser/screen-page.js +112 -0
  154. package/test/browser/screen-product-page.js +62 -0
  155. package/test/browser/screen.check.mjs +1003 -0
  156. package/test/browser/section-page.js +256 -0
  157. package/test/browser/section.check.mjs +719 -0
  158. package/test/browser/serve.mjs +20 -12
  159. package/test/carrier-declaration.test.ts +69 -0
  160. package/test/cavity-drawing.test.ts +2 -18
  161. package/test/clip-ai-proposal.test.ts +1 -5
  162. package/test/clip-consent.test.ts +0 -5
  163. package/test/clips.test.ts +0 -7
  164. package/test/command-preview.test.ts +0 -68
  165. package/test/controls.test.ts +0 -9
  166. package/test/detail-dock.test.ts +6 -25
  167. package/test/face-mapping.test.ts +1 -20
  168. package/test/fasten-drawing.test.ts +0 -23
  169. package/test/fields.test.ts +6 -33
  170. package/test/figure-assistant-surface.test.ts +32 -38
  171. package/test/first-minute.test.ts +3 -55
  172. package/test/first-save-suggest.test.ts +11 -8
  173. package/test/first-save.test.ts +4 -26
  174. package/test/gizmo.test.ts +0 -25
  175. package/test/joint-fan.test.ts +0 -10
  176. package/test/keys.test.ts +0 -7
  177. package/test/layout.test.ts +4 -73
  178. package/test/links.test.ts +48 -67
  179. package/test/live.test.ts +0 -34
  180. package/test/material.test.ts +17 -26
  181. package/test/maturity.test.ts +13 -16
  182. package/test/mesh-import.test.ts +76 -0
  183. package/test/modeller-route.test.ts +3 -19
  184. package/test/package-download.test.ts +6 -7
  185. package/test/parts-wiring.test.ts +9 -51
  186. package/test/preview.test.ts +5 -46
  187. package/test/properties.test.ts +11 -26
  188. package/test/proposal-record.test.ts +2 -19
  189. package/test/recipes.test.ts +0 -15
  190. package/test/refusals.test.ts +257 -151
  191. package/test/release-entry.test.ts +1 -9
  192. package/test/release-panel.test.ts +1 -22
  193. package/test/resize-with-placement.test.ts +0 -32
  194. package/test/revert-choice.test.ts +1 -22
  195. package/test/revert-keeping.test.ts +1 -12
  196. package/test/save-landed.test.ts +16 -14
  197. package/test/scene-canvas.test.ts +2 -13
  198. package/test/section-editor.test.ts +30 -83
  199. package/test/section-panel.test.ts +22 -17
  200. package/test/slot-layout-host.test.ts +117 -0
  201. package/test/theme.test.ts +5 -9
  202. package/test/verdict.test.ts +44 -41
  203. package/test/view.test.ts +5 -12
  204. package/translations/en.json +61 -1
  205. package/translations/es.json +61 -1
  206. package/translations/ja.json +61 -1
  207. package/translations/ko.json +61 -1
  208. package/translations/ms.json +61 -1
  209. package/translations/zh.json +61 -1
  210. package/test/thumbnail.test.ts +0 -29
@@ -5,10 +5,10 @@ import { customElement, property, state } from 'lit/decorators.js';
5
5
  import { live } from 'lit/directives/live.js';
6
6
  import { i18next, localize } from '@operato/i18n';
7
7
  import { SidePanelStyles } from './side-panel-styles.js';
8
- import { drawnOf } from './section-drawing.js';
8
+ import { drawnOf, sectionPointsWordsOf } from './section-drawing.js';
9
9
  import './figure-cavity-drawing.js';
10
10
  import './figure-face-mapping.js';
11
- import { cavityDepthOf, cavityRequestOf, cavityResultOf, cornerLimitOf, cornerRefusalOf, mm, sameOf } from './section-controls.js';
11
+ import { cavityDepthOf, cavityRequestOf, cavityResultOf, cornerActionOf, cornerLimitOf, cornerRefusalOf, hollowableOf, mm, sameOf } from './section-controls.js';
12
12
  /**
13
13
  * 단면 — a pushed part's rounded corners and its inside (ADR-0100 §3.3).
14
14
  *
@@ -79,6 +79,7 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
79
79
  if (!body || !e)
80
80
  return nothing;
81
81
  const kind = e.section.kind;
82
+ const points = sectionPointsWordsOf(e);
82
83
  return html `<section>
83
84
  <h3>${i18next.t('figure.label.group-section')}</h3>
84
85
  ${body.shape.dimensions.jamb === undefined
@@ -86,13 +87,11 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
86
87
  <button act ?disabled=${this.locked} @click=${() => this.dispatchEvent(new CustomEvent('section-editor-open', { bubbles: true, composed: true }))}>
87
88
  <md-icon>edit</md-icon>${i18next.t('figure.button.section-editor-open')}
88
89
  </button>
89
- ${e.section.kind === 'polygon'
90
- ? html `<small>${i18next.t(e.apex ? 'figure.text.section-points-apex' : 'figure.text.section-points', { points: String(e.section.points.length) })}</small>`
91
- : nothing}
90
+ ${points ? html `<small>${i18next.t(points.key, points.params)}</small>` : nothing}
92
91
  </div>`
93
92
  : nothing}
94
93
  ${kind === 'rectangle' ? this.renderCorner(body) : nothing}
95
- ${ /* No inside for a drawn section: the control is not there, rather than a line saying so. */kind === 'rectangle' || kind === 'ellipse' ? this.renderCavity(body) : nothing}
94
+ ${ /* No inside for a drawn section: the control is not there, rather than a line saying so. */hollowableOf(body.shape) ? this.renderCavity(body) : nothing}
96
95
  ${this.faceMapping ? html `<figure-face-mapping .need=${this.faceMapping}></figure-face-mapping>` : nothing}
97
96
  </section>`;
98
97
  }
@@ -112,7 +111,7 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
112
111
  const r = cornerRefusalOf(body.shape, value);
113
112
  return r ? i18next.t('figure.text.section-corner-over', { max: String(mm(r.max)) }) : '';
114
113
  },
115
- action: value => ({ kind: 'set-section-corner', part: body.id, corner: value > 0 ? value : null }),
114
+ action: value => cornerActionOf(body.id, value),
116
115
  placeholder: now === undefined ? i18next.t('figure.text.section-corner-differs') : ''
117
116
  })}
118
117
  ${this.refusals.corner ? nothing : max !== undefined ? html `<small>${i18next.t('figure.text.section-at-most', { max: String(mm(max)) })}</small>` : nothing}
@@ -1 +1 @@
1
- {"version":3,"file":"figure-section-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-section-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing, type PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport type { FigureAuthoringAction, FigureEvaluatedShape, FigureFaceMappingNeed } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from './side-panel-styles.js'\nimport { drawnOf } from './section-drawing.js'\nimport './figure-cavity-drawing.js'\nimport './figure-face-mapping.js'\nimport { cavityDepthOf, cavityRequestOf, cavityResultOf, cornerLimitOf, cornerRefusalOf, mm, sameOf } from './section-controls.js'\n\ntype Body = { id: string; shape: FigureEvaluatedShape }\n\n/**\n * 단면 — a pushed part's rounded corners and its inside (ADR-0100 §3.3).\n *\n * Corners for a rectangle; an inside for a rectangle or an ellipse, with its walls (one thickness, or one per side when\n * asked for), its two ends open or closed, and what that makes of the part and how deep. A drawn section says beside\n * the inside why it has none. A turned body shows nothing here.\n *\n * A value being typed is drawn at once and recorded nowhere (`command-live`); a value the kernel would refuse is said\n * under its field with the most it can be, and is not sent. Enter or leaving the field sends the one `command`, one step\n * of the undo history; Esc puts the value back.\n */\n@customElement('figure-section-panel')\nexport class FigureSectionPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n div[row] {\n display: flex;\n gap: 6px;\n align-items: center;\n }\n div[row] > * {\n flex: 1;\n min-width: 0;\n }\n div[sides] {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 6px;\n /* A side's refusal under its field lengthens its cell only: the field beside it stays level. */\n align-items: start;\n }\n div[sides] > label {\n display: grid;\n gap: 2px;\n }\n small[refusal] {\n color: var(--md-sys-color-error);\n }\n input[aria-invalid='true'] {\n outline: 2px solid var(--md-sys-color-error);\n }\n `]\n\n @property({ attribute: false }) body?: Body\n @property({ type: Boolean }) locked = false\n /** What stands on the faces the last edit here takes away (ADR-0100 A8): asked here, where the edit was made. */\n @property({ attribute: false }) faceMapping?: FigureFaceMappingNeed\n /** What each field would not take, by field, said under it. */\n @state() private refusals: Record<string, string> = {}\n /** What is typed in a field and not yet sent, by field: the figure drawn with it must not type over it. */\n @state() private typed: Record<string, string> = {}\n /** Walls one per side, asked for or because they already differ. */\n @state() private perSide = false\n\n protected willUpdate(changed: PropertyValues<this>) {\n const before = changed.get('body') as Body | undefined\n if (changed.has('body') && before?.id !== this.body?.id) {\n this.refusals = {}\n this.typed = {}\n /* A part whose walls already differ opens with them one per side. */\n const walls = this.body?.shape.cavity?.wall\n this.perSide = !!walls && sameOf(Object.values(walls)) === undefined\n }\n }\n\n render() {\n const body = this.body\n /* A push, or a pyramid: a drawn section drawn to one point, which has no corners and no inside. */\n const e = body?.shape ? drawnOf(body.shape) : undefined\n if (!body || !e) return nothing\n const kind = e.section.kind\n return html`<section>\n <h3>${i18next.t('figure.label.group-section')}</h3>\n ${body.shape.dimensions.jamb === undefined\n ? html`<div field>\n <button act ?disabled=${this.locked} @click=${() => this.dispatchEvent(new CustomEvent('section-editor-open', { bubbles: true, composed: true }))}>\n <md-icon>edit</md-icon>${i18next.t('figure.button.section-editor-open')}\n </button>\n ${e.section.kind === 'polygon'\n ? html`<small>${i18next.t(e.apex ? 'figure.text.section-points-apex' : 'figure.text.section-points', { points: String(e.section.points.length) })}</small>`\n : nothing}\n </div>`\n : nothing}\n ${kind === 'rectangle' ? this.renderCorner(body) : nothing}\n ${/* No inside for a drawn section: the control is not there, rather than a line saying so. */ kind === 'rectangle' || kind === 'ellipse' ? this.renderCavity(body) : nothing}\n ${this.faceMapping ? html`<figure-face-mapping .need=${this.faceMapping}></figure-face-mapping>` : nothing}\n </section>`\n }\n\n private renderCorner(body: Body) {\n const d = body.shape.dimensions\n const reaches = Object.keys(d)\n .filter(k => k.startsWith('corner.'))\n .map(k => d[k])\n const now = reaches.length ? sameOf(reaches) : 0\n const max = cornerLimitOf(body.shape)\n return html`<div form>\n <label for=\"corner\">${i18next.t('figure.label.section-corner')}</label>\n <div>\n ${this.number('corner', now, {\n min: 0,\n ask: value => {\n const r = cornerRefusalOf(body.shape, value)\n return r ? i18next.t('figure.text.section-corner-over', { max: String(mm(r.max)) }) : ''\n },\n action: value => ({ kind: 'set-section-corner', part: body.id, corner: value > 0 ? value : null }),\n placeholder: now === undefined ? i18next.t('figure.text.section-corner-differs') : ''\n })}\n ${this.refusals.corner ? nothing : max !== undefined ? html`<small>${i18next.t('figure.text.section-at-most', { max: String(mm(max)) })}</small>` : nothing}\n </div>\n </div>`\n }\n\n /*\n * The inside: made or taken away here, and drawn -- walls dragged or their number pressed and typed, ends pressed\n * open or closed (figure-cavity-drawing). The number fields under the drawing went (user 2026-10-01): they said the\n * drawing again.\n */\n private renderCavity(body: Body) {\n const cavity = body.shape.cavity\n return html`<div field>\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${live(!!cavity)}\n ?disabled=${this.locked}\n @change=${(ev: Event) => {\n const on = (ev.target as HTMLInputElement).checked\n ;(ev.target as HTMLInputElement).checked = !!cavity\n this.command({ kind: 'set-cavity', part: body.id, cavity: on ? cavityRequestOf(body.shape, {}) : null })\n }}\n />\n ${i18next.t('figure.label.section-cavity')}\n </label>\n ${cavity\n ? html`<small result>\n ${i18next.t('figure.text.section-depth', {\n result: i18next.t(`figure.label.section-result-${cavityResultOf(cavity)}`),\n depth: String(cavityDepthOf(body.shape))\n })}\n </small>`\n : nothing}\n </div>\n ${cavity\n ? html`<figure-cavity-drawing .body=${body} .perSide=${this.perSide} ?locked=${this.locked}></figure-cavity-drawing>\n <label toggle>\n <input type=\"checkbox\" .checked=${live(this.perSide)} ?disabled=${this.locked} @change=${(ev: Event) => (this.perSide = (ev.target as HTMLInputElement).checked)} />\n ${i18next.t('figure.label.section-wall-per-side')}\n </label>`\n : nothing}`\n }\n\n /**\n * A millimetre field. Typing draws the value (or, if the kernel would refuse it, says why under the field and draws the\n * figure as it stands); Enter or leaving sends it once; Esc puts the value back.\n */\n private number(\n key: string,\n now: number | undefined,\n o: { min: number; ask: (value: number) => string; action: (value: number) => FigureAuthoringAction; placeholder?: string }\n ) {\n const shown = now === undefined ? '' : String(mm(now))\n const done = () => {\n const { [key]: _, ...rest } = this.typed\n this.typed = rest\n }\n const read = (input: HTMLInputElement) => (input.value === '' ? NaN : Number(input.value))\n const judge = (input: HTMLInputElement) => {\n const value = read(input)\n const refusal = !Number.isFinite(value) || value < o.min ? '' : o.ask(value)\n this.refusals = { ...this.refusals, [key]: refusal }\n return Number.isFinite(value) && value >= o.min && !refusal ? value : undefined\n }\n return html`<input\n id=${key}\n type=\"number\"\n min=${o.min}\n step=\"any\"\n inputmode=\"decimal\"\n .value=${live(this.typed[key] ?? shown)}\n placeholder=${o.placeholder ?? ''}\n aria-invalid=${this.refusals[key] ? 'true' : 'false'}\n aria-describedby=${`${key}-refusal`}\n ?disabled=${this.locked}\n @input=${(ev: Event) => {\n this.typed = { ...this.typed, [key]: (ev.target as HTMLInputElement).value }\n const value = judge(ev.target as HTMLInputElement)\n this.preview(value === undefined ? null : o.action(value))\n }}\n @change=${(ev: Event) => {\n const input = ev.target as HTMLInputElement\n if (input.value === shown) return done(), this.preview(null)\n const value = judge(input)\n /* A refused value stays typed with its reason under it, to be corrected; nothing is sent. */\n if (value === undefined) return this.preview(null)\n done()\n this.command(o.action(value))\n }}\n @keydown=${(ev: KeyboardEvent) => {\n if (ev.key !== 'Escape') return\n const input = ev.target as HTMLInputElement\n input.value = shown\n done()\n this.refusals = { ...this.refusals, [key]: '' }\n this.preview(null)\n }}\n />\n ${this.refusals[key] ? html`<small refusal id=${`${key}-refusal`} role=\"alert\">${this.refusals[key]}</small>` : nothing}`\n }\n\n private preview(action: FigureAuthoringAction | null) {\n this.dispatchEvent(new CustomEvent('command-live', { detail: { action }, bubbles: true, composed: true }))\n }\n\n private command(action: FigureAuthoringAction) {\n this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }))\n }\n}\n"]}
1
+ {"version":3,"file":"figure-section-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-section-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing, type PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport type { FigureAuthoringAction, FigureEvaluatedShape, FigureFaceMappingNeed } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from './side-panel-styles.js'\nimport { drawnOf, sectionPointsWordsOf } from './section-drawing.js'\nimport './figure-cavity-drawing.js'\nimport './figure-face-mapping.js'\nimport { cavityDepthOf, cavityRequestOf, cavityResultOf, cornerActionOf, cornerLimitOf, cornerRefusalOf, hollowableOf, mm, sameOf } from './section-controls.js'\n\ntype Body = { id: string; shape: FigureEvaluatedShape }\n\n/**\n * 단면 — a pushed part's rounded corners and its inside (ADR-0100 §3.3).\n *\n * Corners for a rectangle; an inside for a rectangle or an ellipse, with its walls (one thickness, or one per side when\n * asked for), its two ends open or closed, and what that makes of the part and how deep. A drawn section says beside\n * the inside why it has none. A turned body shows nothing here.\n *\n * A value being typed is drawn at once and recorded nowhere (`command-live`); a value the kernel would refuse is said\n * under its field with the most it can be, and is not sent. Enter or leaving the field sends the one `command`, one step\n * of the undo history; Esc puts the value back.\n */\n@customElement('figure-section-panel')\nexport class FigureSectionPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n div[row] {\n display: flex;\n gap: 6px;\n align-items: center;\n }\n div[row] > * {\n flex: 1;\n min-width: 0;\n }\n div[sides] {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 6px;\n /* A side's refusal under its field lengthens its cell only: the field beside it stays level. */\n align-items: start;\n }\n div[sides] > label {\n display: grid;\n gap: 2px;\n }\n small[refusal] {\n color: var(--md-sys-color-error);\n }\n input[aria-invalid='true'] {\n outline: 2px solid var(--md-sys-color-error);\n }\n `]\n\n @property({ attribute: false }) body?: Body\n @property({ type: Boolean }) locked = false\n /** What stands on the faces the last edit here takes away (ADR-0100 A8): asked here, where the edit was made. */\n @property({ attribute: false }) faceMapping?: FigureFaceMappingNeed\n /** What each field would not take, by field, said under it. */\n @state() private refusals: Record<string, string> = {}\n /** What is typed in a field and not yet sent, by field: the figure drawn with it must not type over it. */\n @state() private typed: Record<string, string> = {}\n /** Walls one per side, asked for or because they already differ. */\n @state() private perSide = false\n\n protected willUpdate(changed: PropertyValues<this>) {\n const before = changed.get('body') as Body | undefined\n if (changed.has('body') && before?.id !== this.body?.id) {\n this.refusals = {}\n this.typed = {}\n /* A part whose walls already differ opens with them one per side. */\n const walls = this.body?.shape.cavity?.wall\n this.perSide = !!walls && sameOf(Object.values(walls)) === undefined\n }\n }\n\n render() {\n const body = this.body\n /* A push, or a pyramid: a drawn section drawn to one point, which has no corners and no inside. */\n const e = body?.shape ? drawnOf(body.shape) : undefined\n if (!body || !e) return nothing\n const kind = e.section.kind\n const points = sectionPointsWordsOf(e)\n return html`<section>\n <h3>${i18next.t('figure.label.group-section')}</h3>\n ${body.shape.dimensions.jamb === undefined\n ? html`<div field>\n <button act ?disabled=${this.locked} @click=${() => this.dispatchEvent(new CustomEvent('section-editor-open', { bubbles: true, composed: true }))}>\n <md-icon>edit</md-icon>${i18next.t('figure.button.section-editor-open')}\n </button>\n ${points ? html`<small>${i18next.t(points.key, points.params)}</small>` : nothing}\n </div>`\n : nothing}\n ${kind === 'rectangle' ? this.renderCorner(body) : nothing}\n ${/* No inside for a drawn section: the control is not there, rather than a line saying so. */ hollowableOf(body.shape) ? this.renderCavity(body) : nothing}\n ${this.faceMapping ? html`<figure-face-mapping .need=${this.faceMapping}></figure-face-mapping>` : nothing}\n </section>`\n }\n\n private renderCorner(body: Body) {\n const d = body.shape.dimensions\n const reaches = Object.keys(d)\n .filter(k => k.startsWith('corner.'))\n .map(k => d[k])\n const now = reaches.length ? sameOf(reaches) : 0\n const max = cornerLimitOf(body.shape)\n return html`<div form>\n <label for=\"corner\">${i18next.t('figure.label.section-corner')}</label>\n <div>\n ${this.number('corner', now, {\n min: 0,\n ask: value => {\n const r = cornerRefusalOf(body.shape, value)\n return r ? i18next.t('figure.text.section-corner-over', { max: String(mm(r.max)) }) : ''\n },\n action: value => cornerActionOf(body.id, value),\n placeholder: now === undefined ? i18next.t('figure.text.section-corner-differs') : ''\n })}\n ${this.refusals.corner ? nothing : max !== undefined ? html`<small>${i18next.t('figure.text.section-at-most', { max: String(mm(max)) })}</small>` : nothing}\n </div>\n </div>`\n }\n\n /*\n * The inside: made or taken away here, and drawn -- walls dragged or their number pressed and typed, ends pressed\n * open or closed (figure-cavity-drawing). The number fields under the drawing went (user 2026-10-01): they said the\n * drawing again.\n */\n private renderCavity(body: Body) {\n const cavity = body.shape.cavity\n return html`<div field>\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${live(!!cavity)}\n ?disabled=${this.locked}\n @change=${(ev: Event) => {\n const on = (ev.target as HTMLInputElement).checked\n ;(ev.target as HTMLInputElement).checked = !!cavity\n this.command({ kind: 'set-cavity', part: body.id, cavity: on ? cavityRequestOf(body.shape, {}) : null })\n }}\n />\n ${i18next.t('figure.label.section-cavity')}\n </label>\n ${cavity\n ? html`<small result>\n ${i18next.t('figure.text.section-depth', {\n result: i18next.t(`figure.label.section-result-${cavityResultOf(cavity)}`),\n depth: String(cavityDepthOf(body.shape))\n })}\n </small>`\n : nothing}\n </div>\n ${cavity\n ? html`<figure-cavity-drawing .body=${body} .perSide=${this.perSide} ?locked=${this.locked}></figure-cavity-drawing>\n <label toggle>\n <input type=\"checkbox\" .checked=${live(this.perSide)} ?disabled=${this.locked} @change=${(ev: Event) => (this.perSide = (ev.target as HTMLInputElement).checked)} />\n ${i18next.t('figure.label.section-wall-per-side')}\n </label>`\n : nothing}`\n }\n\n /**\n * A millimetre field. Typing draws the value (or, if the kernel would refuse it, says why under the field and draws the\n * figure as it stands); Enter or leaving sends it once; Esc puts the value back.\n */\n private number(\n key: string,\n now: number | undefined,\n o: { min: number; ask: (value: number) => string; action: (value: number) => FigureAuthoringAction; placeholder?: string }\n ) {\n const shown = now === undefined ? '' : String(mm(now))\n const done = () => {\n const { [key]: _, ...rest } = this.typed\n this.typed = rest\n }\n const read = (input: HTMLInputElement) => (input.value === '' ? NaN : Number(input.value))\n const judge = (input: HTMLInputElement) => {\n const value = read(input)\n const refusal = !Number.isFinite(value) || value < o.min ? '' : o.ask(value)\n this.refusals = { ...this.refusals, [key]: refusal }\n return Number.isFinite(value) && value >= o.min && !refusal ? value : undefined\n }\n return html`<input\n id=${key}\n type=\"number\"\n min=${o.min}\n step=\"any\"\n inputmode=\"decimal\"\n .value=${live(this.typed[key] ?? shown)}\n placeholder=${o.placeholder ?? ''}\n aria-invalid=${this.refusals[key] ? 'true' : 'false'}\n aria-describedby=${`${key}-refusal`}\n ?disabled=${this.locked}\n @input=${(ev: Event) => {\n this.typed = { ...this.typed, [key]: (ev.target as HTMLInputElement).value }\n const value = judge(ev.target as HTMLInputElement)\n this.preview(value === undefined ? null : o.action(value))\n }}\n @change=${(ev: Event) => {\n const input = ev.target as HTMLInputElement\n if (input.value === shown) return done(), this.preview(null)\n const value = judge(input)\n /* A refused value stays typed with its reason under it, to be corrected; nothing is sent. */\n if (value === undefined) return this.preview(null)\n done()\n this.command(o.action(value))\n }}\n @keydown=${(ev: KeyboardEvent) => {\n if (ev.key !== 'Escape') return\n const input = ev.target as HTMLInputElement\n input.value = shown\n done()\n this.refusals = { ...this.refusals, [key]: '' }\n this.preview(null)\n }}\n />\n ${this.refusals[key] ? html`<small refusal id=${`${key}-refusal`} role=\"alert\">${this.refusals[key]}</small>` : nothing}`\n }\n\n private preview(action: FigureAuthoringAction | null) {\n this.dispatchEvent(new CustomEvent('command-live', { detail: { action }, bubbles: true, composed: true }))\n }\n\n private command(action: FigureAuthoringAction) {\n this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }))\n }\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import { LitElement, nothing } from 'lit';
2
+ import '@material/web/icon/icon.js';
3
+ import '@operato/slot-layout-editor';
4
+ import { type FigureAsset } from '@hatiolab/figure-model';
5
+ declare const FigureSlotLayoutPanel_base: (new (...args: any[]) => LitElement) & typeof LitElement;
6
+ /**
7
+ * A figure's slot layout (ADR-0118): places made by a rule -- anchors on parts' tops or bottoms, tiers on them, slots in
8
+ * the tiers. The tiers and slots are edited in the shared editor (`ox-slot-layout-editor`, the one fmsim hosts); this
9
+ * panel owns what only the modeller knows: which part's face each anchor is, the layout's name and what its slots are.
10
+ * Every change goes out as one `set-slot-layout` command -- one step of the history, refused by figure-model in its words.
11
+ *
12
+ * The layout here is the only one: a scene or a run never overrides it (ADR-0118 decision 8), so there is no "default".
13
+ */
14
+ export declare class FigureSlotLayoutPanel extends FigureSlotLayoutPanel_base {
15
+ static styles: import("lit").CSSResult;
16
+ asset?: FigureAsset;
17
+ /** The part selected on the canvas: what a new anchor stands on. */
18
+ partId: string;
19
+ locked: boolean;
20
+ /** The name, role and anchors being edited; started again from the figure whenever the figure changes. */
21
+ private form?;
22
+ private formFor?;
23
+ private options?;
24
+ private stored?;
25
+ willUpdate(): void;
26
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
27
+ private editorOptions;
28
+ private edit;
29
+ private anchor;
30
+ /** A new anchor on the selected part's face: what an author picks on the canvas comes here. */
31
+ private addAnchor;
32
+ /** The whole layout with the anchors and name as they stand: one command, one step. */
33
+ private apply;
34
+ private command;
35
+ }
36
+ export {};
@@ -0,0 +1,198 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { css, html, LitElement, nothing } from 'lit';
3
+ import { customElement, property, state } from 'lit/decorators.js';
4
+ import { i18next, localize } from '@operato/i18n';
5
+ import '@material/web/icon/icon.js';
6
+ import '@operato/slot-layout-editor';
7
+ import { compileFigureAsset } from '@hatiolab/figure-model';
8
+ import { anchorFacesOf, anchorIdFor, figureSlotIssues, slotLayoutCommandOf, slotLayoutFormOf, slotPresetsOf } from './slot-layout-host.js';
9
+ /**
10
+ * A figure's slot layout (ADR-0118): places made by a rule -- anchors on parts' tops or bottoms, tiers on them, slots in
11
+ * the tiers. The tiers and slots are edited in the shared editor (`ox-slot-layout-editor`, the one fmsim hosts); this
12
+ * panel owns what only the modeller knows: which part's face each anchor is, the layout's name and what its slots are.
13
+ * Every change goes out as one `set-slot-layout` command -- one step of the history, refused by figure-model in its words.
14
+ *
15
+ * The layout here is the only one: a scene or a run never overrides it (ADR-0118 decision 8), so there is no "default".
16
+ */
17
+ let FigureSlotLayoutPanel = class FigureSlotLayoutPanel extends localize(i18next)(LitElement) {
18
+ constructor() {
19
+ super(...arguments);
20
+ /** The part selected on the canvas: what a new anchor stands on. */
21
+ this.partId = '';
22
+ this.locked = false;
23
+ }
24
+ static { this.styles = css `
25
+ :host {
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: 6px;
29
+ }
30
+ div[form] {
31
+ display: grid;
32
+ grid-template-columns: auto 1fr;
33
+ gap: 4px 8px;
34
+ align-items: center;
35
+ }
36
+ ul {
37
+ list-style: none;
38
+ margin: 0;
39
+ padding: 0;
40
+ display: flex;
41
+ flex-direction: column;
42
+ gap: 4px;
43
+ }
44
+ li {
45
+ display: grid;
46
+ grid-template-columns: 1fr 1fr auto auto;
47
+ gap: 4px;
48
+ align-items: center;
49
+ }
50
+ input,
51
+ select {
52
+ min-width: 0;
53
+ font: inherit;
54
+ }
55
+ div[actions] {
56
+ display: flex;
57
+ flex-wrap: wrap;
58
+ gap: 4px;
59
+ }
60
+ button {
61
+ display: inline-flex;
62
+ align-items: center;
63
+ gap: 4px;
64
+ font: inherit;
65
+ }
66
+ md-icon {
67
+ --md-icon-size: 16px;
68
+ }
69
+ small[hint] {
70
+ color: var(--md-sys-color-on-surface-variant);
71
+ }
72
+ `; }
73
+ willUpdate() {
74
+ /* A figure changed under the panel (an undo, another edit): what is shown is the figure's, not an old draft. */
75
+ if (this.asset !== this.formFor) {
76
+ this.formFor = this.asset;
77
+ this.stored = this.asset ? slotLayoutFormOf(this.asset, this.partId) : undefined;
78
+ this.form = this.stored?.form;
79
+ }
80
+ }
81
+ render() {
82
+ const asset = this.asset;
83
+ const stored = this.stored;
84
+ const form = this.form;
85
+ if (!asset || !stored || !form)
86
+ return nothing;
87
+ const parts = asset.document.model.nodes.filter(n => n.op === 'place@1').map(n => n.id);
88
+ const changed = JSON.stringify(form) !== JSON.stringify(stored.form);
89
+ return html `
90
+ <div form>
91
+ <span>${i18next.t('figure.label.slot-layout-name')}</span>
92
+ <input .value=${form.label} placeholder=${i18next.t('figure.label.slot-layout')} ?disabled=${this.locked} @change=${(e) => this.edit({ label: e.target.value })} />
93
+ <span>${i18next.t('figure.label.slot-layout-role')}</span>
94
+ <select ?disabled=${this.locked} @change=${(e) => this.edit({ role: e.target.value })}>
95
+ <option value="seat" ?selected=${form.role === 'seat'}>${i18next.t('figure.label.place-seat')}</option>
96
+ <option value="grip" ?selected=${form.role === 'grip'}>${i18next.t('figure.label.place-grip')}</option>
97
+ </select>
98
+ </div>
99
+
100
+ <h4>${i18next.t('figure.label.slot-anchors')}</h4>
101
+ <ul>
102
+ ${form.anchors.map((a, i) => html `<li>
103
+ <input .value=${a.id} aria-label=${i18next.t('figure.label.slot-anchor-id')} ?disabled=${this.locked} @change=${(e) => this.anchor(i, { id: e.target.value.trim() })} />
104
+ <select aria-label=${i18next.t('figure.label.part')} ?disabled=${this.locked} @change=${(e) => this.anchor(i, { part: e.target.value })}>
105
+ ${parts.map(p => html `<option value=${p} ?selected=${p === a.part}>${p}</option>`)}
106
+ </select>
107
+ <select aria-label=${i18next.t('figure.label.face')} ?disabled=${this.locked} @change=${(e) => this.anchor(i, { face: e.target.value })}>
108
+ ${['top', 'bottom'].map(f => html `<option value=${f} ?selected=${f === a.face}>${i18next.t('figure.label.face-' + f)}</option>`)}
109
+ </select>
110
+ <button ?disabled=${this.locked || form.anchors.length <= 1} title=${i18next.t('figure.button.remove-slot-anchor')} @click=${() => this.edit({ anchors: form.anchors.filter((_, k) => k !== i) })}>
111
+ <md-icon>close</md-icon>
112
+ </button>
113
+ </li>`)}
114
+ </ul>
115
+ <div actions>
116
+ ${['top', 'bottom'].map(face => html `<button ?disabled=${this.locked || !this.partId} @click=${() => this.addAnchor(face)}>
117
+ <md-icon>add</md-icon>${i18next.t(face === 'top' ? 'figure.button.add-slot-anchor-top' : 'figure.button.add-slot-anchor-bottom', { part: this.partId })}
118
+ </button>`)}
119
+ </div>
120
+ <small hint>${i18next.t('figure.text.slot-anchor-pick')}</small>
121
+
122
+ <ox-slot-layout-editor .value=${stored.layout} .options=${this.editorOptions(asset, form, stored.layout)} @change=${(e) => this.apply(e.target.value)}></ox-slot-layout-editor>
123
+
124
+ <div actions>
125
+ ${stored.layout && changed
126
+ ? html `<button ?disabled=${this.locked} @click=${() => this.apply(stored.layout)}><md-icon>check</md-icon>${i18next.t('figure.button.apply-slot-anchors')}</button>`
127
+ : nothing}
128
+ ${stored.layout
129
+ ? html `<button ?disabled=${this.locked} @click=${() => this.command({ kind: 'remove-slot-layout' })}><md-icon>delete</md-icon>${i18next.t('figure.button.remove-slot-layout')}</button>`
130
+ : nothing}
131
+ </div>
132
+ `;
133
+ }
134
+ /*
135
+ The editor's options, made again only when what they are made from changes: the editor starts its draft again when its
136
+ starting layout changes, so a new object every render would throw the author's edits away.
137
+ */
138
+ editorOptions(asset, form, layout) {
139
+ const key = JSON.stringify([form.anchors, !!layout]);
140
+ if (this.options?.key === key && this.options.asset === asset)
141
+ return this.options.value;
142
+ const geometry = compileFigureAsset(asset).evaluate().geometry;
143
+ const anchorFaces = anchorFacesOf(form.anchors, geometry);
144
+ const value = {
145
+ canUseDefault: false,
146
+ presets: slotPresetsOf(form.anchors, key => i18next.t(key)),
147
+ /* The first anchor's face is the size a preset is laid out over; the slot height is the author's. */
148
+ presetSize: anchorFaces[form.anchors[0]?.id ?? ''] ?? {},
149
+ anchors: form.anchors.map(a => ({ id: a.id, label: `${a.id} · ${a.part} ${i18next.t('figure.label.face-' + a.face)}` })),
150
+ anchorFaces,
151
+ validate: (layout) => figureSlotIssues(layout, form.anchors.map(a => a.id)),
152
+ issueText: (issue) => issue.code === 'ACCEPTS_REQUIRED' ? `${issue.path}: ${i18next.t('figure.text.slot-accepts-required')}` : undefined
153
+ };
154
+ this.options = { key, asset, value };
155
+ return value;
156
+ }
157
+ edit(patch) {
158
+ this.form = { ...this.form, ...patch };
159
+ }
160
+ anchor(i, patch) {
161
+ this.edit({ anchors: this.form.anchors.map((a, k) => (k === i ? { ...a, ...patch } : a)) });
162
+ }
163
+ /** A new anchor on the selected part's face: what an author picks on the canvas comes here. */
164
+ addAnchor(face) {
165
+ const anchors = this.form.anchors;
166
+ this.edit({ anchors: [...anchors, { id: anchorIdFor(this.partId, anchors.map(a => a.id)), part: this.partId, face }] });
167
+ }
168
+ /** The whole layout with the anchors and name as they stand: one command, one step. */
169
+ apply(layout) {
170
+ if (!layout || !this.form)
171
+ return;
172
+ this.command(slotLayoutCommandOf(this.form, layout, i18next.t('figure.label.slot-layout')));
173
+ }
174
+ command(action) {
175
+ this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }));
176
+ }
177
+ };
178
+ __decorate([
179
+ property({ attribute: false }),
180
+ __metadata("design:type", Object)
181
+ ], FigureSlotLayoutPanel.prototype, "asset", void 0);
182
+ __decorate([
183
+ property(),
184
+ __metadata("design:type", Object)
185
+ ], FigureSlotLayoutPanel.prototype, "partId", void 0);
186
+ __decorate([
187
+ property({ type: Boolean }),
188
+ __metadata("design:type", Object)
189
+ ], FigureSlotLayoutPanel.prototype, "locked", void 0);
190
+ __decorate([
191
+ state(),
192
+ __metadata("design:type", Object)
193
+ ], FigureSlotLayoutPanel.prototype, "form", void 0);
194
+ FigureSlotLayoutPanel = __decorate([
195
+ customElement('figure-slot-layout-panel')
196
+ ], FigureSlotLayoutPanel);
197
+ export { FigureSlotLayoutPanel };
198
+ //# sourceMappingURL=figure-slot-layout-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-slot-layout-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-slot-layout-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\nimport '@material/web/icon/icon.js'\nimport '@operato/slot-layout-editor'\nimport { compileFigureAsset, type FigureAsset, type SlotLayout } from '@hatiolab/figure-model'\n\nimport {\n anchorFacesOf,\n anchorIdFor,\n figureSlotIssues,\n slotLayoutCommandOf,\n slotLayoutFormOf,\n slotPresetsOf,\n type SlotAnchor,\n type SlotAnchorFace,\n type SlotLayoutForm\n} from './slot-layout-host.js'\n\n/**\n * A figure's slot layout (ADR-0118): places made by a rule -- anchors on parts' tops or bottoms, tiers on them, slots in\n * the tiers. The tiers and slots are edited in the shared editor (`ox-slot-layout-editor`, the one fmsim hosts); this\n * panel owns what only the modeller knows: which part's face each anchor is, the layout's name and what its slots are.\n * Every change goes out as one `set-slot-layout` command -- one step of the history, refused by figure-model in its words.\n *\n * The layout here is the only one: a scene or a run never overrides it (ADR-0118 decision 8), so there is no \"default\".\n */\n@customElement('figure-slot-layout-panel')\nexport class FigureSlotLayoutPanel extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n div[form] {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 4px 8px;\n align-items: center;\n }\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n li {\n display: grid;\n grid-template-columns: 1fr 1fr auto auto;\n gap: 4px;\n align-items: center;\n }\n input,\n select {\n min-width: 0;\n font: inherit;\n }\n div[actions] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n button {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font: inherit;\n }\n md-icon {\n --md-icon-size: 16px;\n }\n small[hint] {\n color: var(--md-sys-color-on-surface-variant);\n }\n `\n\n @property({ attribute: false }) asset?: FigureAsset\n /** The part selected on the canvas: what a new anchor stands on. */\n @property() partId = ''\n @property({ type: Boolean }) locked = false\n\n /** The name, role and anchors being edited; started again from the figure whenever the figure changes. */\n @state() private form?: SlotLayoutForm\n private formFor?: FigureAsset\n private options?: { key: string; asset: FigureAsset; value: object }\n /*\n The figure's layout, read once per figure: the editor starts its draft again whenever its value changes, so a value\n read again every render would throw the author's edits away.\n */\n private stored?: ReturnType<typeof slotLayoutFormOf>\n\n willUpdate() {\n /* A figure changed under the panel (an undo, another edit): what is shown is the figure's, not an old draft. */\n if (this.asset !== this.formFor) {\n this.formFor = this.asset\n this.stored = this.asset ? slotLayoutFormOf(this.asset, this.partId) : undefined\n this.form = this.stored?.form\n }\n }\n\n render() {\n const asset = this.asset\n const stored = this.stored\n const form = this.form\n if (!asset || !stored || !form) return nothing\n const parts = asset.document.model.nodes.filter(n => n.op === 'place@1').map(n => n.id)\n const changed = JSON.stringify(form) !== JSON.stringify(stored.form)\n return html`\n <div form>\n <span>${i18next.t('figure.label.slot-layout-name')}</span>\n <input .value=${form.label} placeholder=${i18next.t('figure.label.slot-layout')} ?disabled=${this.locked} @change=${(e: Event) => this.edit({ label: (e.target as HTMLInputElement).value })} />\n <span>${i18next.t('figure.label.slot-layout-role')}</span>\n <select ?disabled=${this.locked} @change=${(e: Event) => this.edit({ role: (e.target as HTMLSelectElement).value as 'seat' | 'grip' })}>\n <option value=\"seat\" ?selected=${form.role === 'seat'}>${i18next.t('figure.label.place-seat')}</option>\n <option value=\"grip\" ?selected=${form.role === 'grip'}>${i18next.t('figure.label.place-grip')}</option>\n </select>\n </div>\n\n <h4>${i18next.t('figure.label.slot-anchors')}</h4>\n <ul>\n ${form.anchors.map(\n (a, i) => html`<li>\n <input .value=${a.id} aria-label=${i18next.t('figure.label.slot-anchor-id')} ?disabled=${this.locked} @change=${(e: Event) => this.anchor(i, { id: (e.target as HTMLInputElement).value.trim() })} />\n <select aria-label=${i18next.t('figure.label.part')} ?disabled=${this.locked} @change=${(e: Event) => this.anchor(i, { part: (e.target as HTMLSelectElement).value })}>\n ${parts.map(p => html`<option value=${p} ?selected=${p === a.part}>${p}</option>`)}\n </select>\n <select aria-label=${i18next.t('figure.label.face')} ?disabled=${this.locked} @change=${(e: Event) => this.anchor(i, { face: (e.target as HTMLSelectElement).value as SlotAnchorFace })}>\n ${(['top', 'bottom'] as const).map(f => html`<option value=${f} ?selected=${f === a.face}>${i18next.t('figure.label.face-' + f)}</option>`)}\n </select>\n <button ?disabled=${this.locked || form.anchors.length <= 1} title=${i18next.t('figure.button.remove-slot-anchor')} @click=${() => this.edit({ anchors: form.anchors.filter((_, k) => k !== i) })}>\n <md-icon>close</md-icon>\n </button>\n </li>`\n )}\n </ul>\n <div actions>\n ${(['top', 'bottom'] as const).map(\n face => html`<button ?disabled=${this.locked || !this.partId} @click=${() => this.addAnchor(face)}>\n <md-icon>add</md-icon>${i18next.t(face === 'top' ? 'figure.button.add-slot-anchor-top' : 'figure.button.add-slot-anchor-bottom', { part: this.partId })}\n </button>`\n )}\n </div>\n <small hint>${i18next.t('figure.text.slot-anchor-pick')}</small>\n\n <ox-slot-layout-editor .value=${stored.layout} .options=${this.editorOptions(asset, form, stored.layout)} @change=${(e: Event) => this.apply((e.target as { value?: SlotLayout }).value)}></ox-slot-layout-editor>\n\n <div actions>\n ${stored.layout && changed\n ? html`<button ?disabled=${this.locked} @click=${() => this.apply(stored.layout)}><md-icon>check</md-icon>${i18next.t('figure.button.apply-slot-anchors')}</button>`\n : nothing}\n ${stored.layout\n ? html`<button ?disabled=${this.locked} @click=${() => this.command({ kind: 'remove-slot-layout' })}><md-icon>delete</md-icon>${i18next.t('figure.button.remove-slot-layout')}</button>`\n : nothing}\n </div>\n `\n }\n\n /*\n The editor's options, made again only when what they are made from changes: the editor starts its draft again when its\n starting layout changes, so a new object every render would throw the author's edits away.\n */\n private editorOptions(asset: FigureAsset, form: SlotLayoutForm, layout: SlotLayout | undefined) {\n const key = JSON.stringify([form.anchors, !!layout])\n if (this.options?.key === key && this.options.asset === asset) return this.options.value\n const geometry = compileFigureAsset(asset).evaluate().geometry as never\n const anchorFaces = anchorFacesOf(form.anchors, geometry)\n const value = {\n canUseDefault: false,\n presets: slotPresetsOf(form.anchors, key => i18next.t(key)),\n /* The first anchor's face is the size a preset is laid out over; the slot height is the author's. */\n presetSize: anchorFaces[form.anchors[0]?.id ?? ''] ?? {},\n anchors: form.anchors.map(a => ({ id: a.id, label: `${a.id} · ${a.part} ${i18next.t('figure.label.face-' + a.face)}` })),\n anchorFaces,\n validate: (layout: SlotLayout) => figureSlotIssues(layout, form.anchors.map(a => a.id)),\n issueText: (issue: { code: string; path: string }) =>\n issue.code === 'ACCEPTS_REQUIRED' ? `${issue.path}: ${i18next.t('figure.text.slot-accepts-required')}` : undefined\n }\n this.options = { key, asset, value }\n return value\n }\n\n private edit(patch: Partial<SlotLayoutForm>) {\n this.form = { ...this.form!, ...patch }\n }\n\n private anchor(i: number, patch: Partial<SlotAnchor>) {\n this.edit({ anchors: this.form!.anchors.map((a, k) => (k === i ? { ...a, ...patch } : a)) })\n }\n\n /** A new anchor on the selected part's face: what an author picks on the canvas comes here. */\n private addAnchor(face: SlotAnchorFace) {\n const anchors = this.form!.anchors\n this.edit({ anchors: [...anchors, { id: anchorIdFor(this.partId, anchors.map(a => a.id)), part: this.partId, face }] })\n }\n\n /** The whole layout with the anchors and name as they stand: one command, one step. */\n private apply(layout: SlotLayout | undefined) {\n if (!layout || !this.form) return\n this.command(slotLayoutCommandOf(this.form, layout, i18next.t('figure.label.slot-layout')))\n }\n\n private command(action: object) {\n this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }))\n }\n}\n"]}
@@ -24,7 +24,7 @@ export declare class FigureVerdict extends FigureVerdict_base {
24
24
  private asking;
25
25
  private timer?;
26
26
  /** Answers can come back out of order; only the newest question's answer is shown. */
27
- private asked;
27
+ private answers;
28
28
  disconnectedCallback(): void;
29
29
  /** The report is measured once the figure has settled: it is read beside the work, and each measure compiles the figure several times. */
30
30
  private measureTimer?;
@@ -4,12 +4,13 @@ import { css, html, LitElement, nothing, svg } from 'lit';
4
4
  import { customElement, property, state } from 'lit/decorators.js';
5
5
  import { i18next, localize } from '@operato/i18n';
6
6
  import { ScrollbarStyles } from '@operato/styles';
7
- import { LIMITS, FIGURE_PER_INSTANCE, figureCostOf } from '@hatiolab/figure-model';
7
+ import { LIMITS, FIGURE_PER_INSTANCE } from '@hatiolab/figure-model';
8
8
  import { inspectFigureDraft } from '../graphql/index.js';
9
9
  import { gateFindingStyles, renderGateFinding } from './gate-findings.js';
10
10
  import { MEASURED } from './scene-budget.js';
11
11
  import { SidePanelStyles } from './side-panel-styles.js';
12
- import { budgetOf, CURVE_SIZE, curveLayout, GRADE_SCALE, shortNumber, figureReportOf } from './maturity.js';
12
+ import { CURVE_SIZE, curveLayout, gradeBoundsOf, shortNumber, verdictMeasureOf } from './maturity.js';
13
+ import { newestAnswer } from './newest-answer.js';
13
14
  /** How long an edit settles before the server is asked again. One question per pause, not one per keystroke. */
14
15
  export const VERDICT_SETTLE_MS = 400;
15
16
  /** How long the figure rests before its report is measured again. */
@@ -51,7 +52,7 @@ let FigureVerdict = class FigureVerdict extends localize(i18next)(LitElement) {
51
52
  this.failure = '';
52
53
  this.asking = false;
53
54
  /** Answers can come back out of order; only the newest question's answer is shown. */
54
- this.asked = 0;
55
+ this.answers = newestAnswer();
55
56
  this.fit = () => {
56
57
  this.dispatchEvent(new CustomEvent('fit-base-to-parts', { bubbles: true, composed: true }));
57
58
  };
@@ -403,23 +404,12 @@ let FigureVerdict = class FigureVerdict extends localize(i18next)(LitElement) {
403
404
  this.schedule();
404
405
  }
405
406
  measure() {
406
- let report;
407
- let bodies = 0;
408
- try {
409
- const asset = this.revision?.asset;
410
- const cost = asset ? figureCostOf(asset) : undefined;
411
- bodies = cost ? cost.groups + cost.hidden : 0;
412
- report = asset ? figureReportOf(asset) : undefined;
413
- }
414
- catch {
415
- /* A source the format refuses has no report; the server's finding below says why. */
416
- report = undefined;
417
- }
407
+ const { report, bodies, told } = verdictMeasureOf(this.revision?.asset);
418
408
  this.report = report;
419
409
  this.bodies = bodies;
420
410
  if (!report)
421
411
  this.expanded = false;
422
- this.dispatchEvent(new CustomEvent('capacity-changed', { detail: { instances: report?.capacity.instances, budget: budgetOf(report) } }));
412
+ this.dispatchEvent(new CustomEvent('capacity-changed', { detail: told }));
423
413
  }
424
414
  schedule() {
425
415
  clearTimeout(this.timer);
@@ -430,27 +420,20 @@ let FigureVerdict = class FigureVerdict extends localize(i18next)(LitElement) {
430
420
  this.timer = setTimeout(() => this.ask(), VERDICT_SETTLE_MS);
431
421
  }
432
422
  async ask() {
433
- const question = ++this.asked;
434
423
  const source = this.revision.text;
435
424
  this.asking = true;
436
- try {
437
- const inspection = await inspectFigureDraft(source, this.figureId);
438
- if (question !== this.asked)
439
- return;
440
- this.inspection = inspection;
441
- this.failure = '';
442
- }
443
- catch (error) {
444
- if (question !== this.asked)
445
- return;
446
- /* The server's reason as it gave it — the screen does not make one up. The last answer is dropped with it. */
447
- this.inspection = undefined;
448
- this.failure = error instanceof Error ? error.message : String(error);
449
- }
450
- finally {
451
- if (question === this.asked)
452
- this.asking = false;
453
- }
425
+ await this.answers(() => inspectFigureDraft(source, this.figureId), outcome => {
426
+ this.asking = false;
427
+ if ('answer' in outcome) {
428
+ this.inspection = outcome.answer;
429
+ this.failure = '';
430
+ }
431
+ else {
432
+ /* The server's reason as it gave it — the screen does not make one up. The last answer is dropped with it. */
433
+ this.inspection = undefined;
434
+ this.failure = outcome.failure instanceof Error ? outcome.failure.message : String(outcome.failure);
435
+ }
436
+ });
454
437
  }
455
438
  render() {
456
439
  /*
@@ -527,11 +510,10 @@ let FigureVerdict = class FigureVerdict extends localize(i18next)(LitElement) {
527
510
  }
528
511
  /** A · B · C · D and where this figure stands, drawn from the bounds the grade is computed with. */
529
512
  renderGradeScale(now) {
530
- const bounds = [...GRADE_SCALE.map(([grade, bound]) => [grade, `≤ ${bound * 100}%`]), ['D', '> 100%']];
531
513
  return html `
532
514
  <p scale>
533
515
  <span>${i18next.t('figure.text.grade-scale-lead')}</span>
534
- ${bounds.map(([grade, bound]) => html `<span ?now=${grade === now}><b>${grade}</b> ${bound}</span>`)}
516
+ ${gradeBoundsOf().map(([grade, bound]) => html `<span ?now=${grade === now}><b>${grade}</b> ${bound}</span>`)}
535
517
  </p>
536
518
  `;
537
519
  }