@things-factory/figure-ui 10.1.169 → 10.1.170

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 (105) hide show
  1. package/client/modeller/affected-parts.ts +11 -0
  2. package/client/modeller/canvas.ts +15 -1
  3. package/client/modeller/editor-styles.ts +98 -6
  4. package/client/modeller/editor.ts +212 -53
  5. package/client/modeller/fasten-geometry.ts +19 -0
  6. package/client/modeller/figure-fasten-drawing.ts +4 -3
  7. package/client/modeller/figure-links.ts +80 -37
  8. package/client/modeller/figure-material-panel.ts +20 -1
  9. package/client/modeller/figure-preview.ts +117 -8
  10. package/client/modeller/figure-properties.ts +1 -2
  11. package/client/modeller/figure-section-panel.ts +5 -0
  12. package/client/modeller/figure-verdict.ts +9 -2
  13. package/client/modeller/gate-findings.ts +12 -4
  14. package/client/modeller/group-menu.ts +57 -0
  15. package/client/modeller/links.ts +22 -1
  16. package/client/modeller/modeller-keys.ts +6 -0
  17. package/client/modeller/preview-size.ts +32 -0
  18. package/client/modeller/stage-model.ts +11 -0
  19. package/client/pages/figure-modeller-page.ts +8 -2
  20. package/dist-client/modeller/affected-parts.d.ts +6 -0
  21. package/dist-client/modeller/affected-parts.js +12 -0
  22. package/dist-client/modeller/affected-parts.js.map +1 -0
  23. package/dist-client/modeller/canvas.d.ts +3 -0
  24. package/dist-client/modeller/canvas.js +12 -1
  25. package/dist-client/modeller/canvas.js.map +1 -1
  26. package/dist-client/modeller/clips.d.ts +1 -1
  27. package/dist-client/modeller/editor-styles.js +98 -6
  28. package/dist-client/modeller/editor-styles.js.map +1 -1
  29. package/dist-client/modeller/editor.d.ts +42 -1
  30. package/dist-client/modeller/editor.js +220 -51
  31. package/dist-client/modeller/editor.js.map +1 -1
  32. package/dist-client/modeller/fasten-geometry.d.ts +27 -0
  33. package/dist-client/modeller/fasten-geometry.js +14 -0
  34. package/dist-client/modeller/fasten-geometry.js.map +1 -1
  35. package/dist-client/modeller/figure-fasten-drawing.js +4 -3
  36. package/dist-client/modeller/figure-fasten-drawing.js.map +1 -1
  37. package/dist-client/modeller/figure-links.d.ts +3 -1
  38. package/dist-client/modeller/figure-links.js +84 -38
  39. package/dist-client/modeller/figure-links.js.map +1 -1
  40. package/dist-client/modeller/figure-material-panel.d.ts +8 -1
  41. package/dist-client/modeller/figure-material-panel.js +24 -1
  42. package/dist-client/modeller/figure-material-panel.js.map +1 -1
  43. package/dist-client/modeller/figure-preview.d.ts +12 -0
  44. package/dist-client/modeller/figure-preview.js +116 -5
  45. package/dist-client/modeller/figure-preview.js.map +1 -1
  46. package/dist-client/modeller/figure-properties.js +1 -2
  47. package/dist-client/modeller/figure-properties.js.map +1 -1
  48. package/dist-client/modeller/figure-section-panel.d.ts +1 -1
  49. package/dist-client/modeller/figure-section-panel.js +5 -0
  50. package/dist-client/modeller/figure-section-panel.js.map +1 -1
  51. package/dist-client/modeller/figure-verdict.d.ts +1 -0
  52. package/dist-client/modeller/figure-verdict.js +8 -2
  53. package/dist-client/modeller/figure-verdict.js.map +1 -1
  54. package/dist-client/modeller/gate-findings.d.ts +3 -1
  55. package/dist-client/modeller/gate-findings.js +8 -4
  56. package/dist-client/modeller/gate-findings.js.map +1 -1
  57. package/dist-client/modeller/group-menu.d.ts +18 -0
  58. package/dist-client/modeller/group-menu.js +52 -0
  59. package/dist-client/modeller/group-menu.js.map +1 -0
  60. package/dist-client/modeller/links.d.ts +5 -0
  61. package/dist-client/modeller/links.js +21 -1
  62. package/dist-client/modeller/links.js.map +1 -1
  63. package/dist-client/modeller/modeller-keys.js +7 -0
  64. package/dist-client/modeller/modeller-keys.js.map +1 -1
  65. package/dist-client/modeller/preview-size.d.ts +23 -0
  66. package/dist-client/modeller/preview-size.js +32 -0
  67. package/dist-client/modeller/preview-size.js.map +1 -0
  68. package/dist-client/modeller/stage-model.d.ts +11 -0
  69. package/dist-client/modeller/stage-model.js +11 -0
  70. package/dist-client/modeller/stage-model.js.map +1 -1
  71. package/dist-client/pages/figure-modeller-page.d.ts +3 -0
  72. package/dist-client/pages/figure-modeller-page.js +11 -2
  73. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  74. package/dist-client/tsconfig.tsbuildinfo +1 -1
  75. package/package.json +8 -8
  76. package/test/browser/editing-page.js +3 -1
  77. package/test/browser/editing.check.mjs +2 -1
  78. package/test/browser/links-page.js +1 -0
  79. package/test/browser/links.check.mjs +20 -5
  80. package/test/browser/modeller.check.mjs +6 -0
  81. package/test/browser/more-open.js +11 -0
  82. package/test/browser/parts.check.mjs +11 -1
  83. package/test/browser/places-mesh.check.mjs +9 -3
  84. package/test/browser/product-page.check.mjs +9 -4
  85. package/test/browser/props.check.mjs +224 -0
  86. package/test/browser/screen-page.js +17 -0
  87. package/test/browser/screen.check.mjs +171 -29
  88. package/test/browser/section-page.js +1 -0
  89. package/test/browser/section.check.mjs +6 -0
  90. package/test/command-preview.test.ts +2 -2
  91. package/test/fasten-geometry.test.ts +15 -0
  92. package/test/fields.test.ts +4 -2
  93. package/test/first-minute.test.ts +15 -2
  94. package/test/layout.test.ts +1 -1
  95. package/test/preview-size.test.ts +33 -0
  96. package/test/refusals.test.ts +1 -1
  97. package/test/resize-with-placement.test.ts +1 -1
  98. package/test/stage-model.test.ts +11 -1
  99. package/test/verdict.test.ts +7 -0
  100. package/translations/en.json +37 -27
  101. package/translations/es.json +18 -8
  102. package/translations/ja.json +19 -9
  103. package/translations/ko.json +50 -40
  104. package/translations/ms.json +19 -9
  105. package/translations/zh.json +19 -9
@@ -42,11 +42,21 @@ let FigureMaterialPanel = class FigureMaterialPanel extends localize(i18next)(Li
42
42
  /** The asset's palette, token → colour. */
43
43
  this.palette = {};
44
44
  this.locked = false;
45
+ /**
46
+ * Which half to draw. `look` is what most parts need -- the colour role and the preset; `more` is what few parts
47
+ * need -- transparency, glow and the segments of a round part -- drawn in the properties' 「더 보기」 fold.
48
+ */
49
+ this.show = 'look';
45
50
  }
46
51
  static { this.styles = [SidePanelStyles, css `
47
52
  :host {
48
53
  display: block;
49
54
  }
55
+ /* The rarer half, drawn inside the 「더 보기」 fold: the fold gives the padding and the line. */
56
+ div[more] {
57
+ display: grid;
58
+ gap: 8px;
59
+ }
50
60
  div[field] {
51
61
  display: grid;
52
62
  gap: 4px;
@@ -172,6 +182,8 @@ let FigureMaterialPanel = class FigureMaterialPanel extends localize(i18next)(Li
172
182
  const painted = entries.filter(([token]) => !token.startsWith('palette.status-'));
173
183
  const lit = entries.filter(([token]) => token.startsWith('palette.status-'));
174
184
  const glow = material.emissive;
185
+ if (this.show === 'more')
186
+ return this.renderMore(part, painted, lit);
175
187
  return html `<section>
176
188
  <h3>${i18next.t('figure.label.part-material')}</h3>
177
189
  <div field>
@@ -192,6 +204,13 @@ let FigureMaterialPanel = class FigureMaterialPanel extends localize(i18next)(Li
192
204
  ${MATERIAL_PRESETS.map(preset => html `<option value=${preset} ?selected=${preset === material.preset}>${i18next.t('figure.label.preset-' + preset)}</option>`)}
193
205
  </select>
194
206
  </div>
207
+ </section>`;
208
+ }
209
+ /** Transparency, glow and segments: few parts need them, so they wait in the 「더 보기」 fold. */
210
+ renderMore(part, painted, lit) {
211
+ const material = part.material;
212
+ const glow = material.emissive;
213
+ return html `<div more>
195
214
  <label toggle>
196
215
  <input
197
216
  type="checkbox"
@@ -224,7 +243,7 @@ let FigureMaterialPanel = class FigureMaterialPanel extends localize(i18next)(Li
224
243
  </div>`
225
244
  : nothing}
226
245
  ${this.renderSegments(part)}
227
- </section>`;
246
+ </div>`;
228
247
  }
229
248
  renderSegments(part) {
230
249
  if (!isRoundBody(part.shape))
@@ -298,6 +317,10 @@ __decorate([
298
317
  property({ type: Boolean }),
299
318
  __metadata("design:type", Object)
300
319
  ], FigureMaterialPanel.prototype, "locked", void 0);
320
+ __decorate([
321
+ property({ type: String }),
322
+ __metadata("design:type", String)
323
+ ], FigureMaterialPanel.prototype, "show", void 0);
301
324
  __decorate([
302
325
  state(),
303
326
  __metadata("design:type", Number)
@@ -1 +1 @@
1
- {"version":3,"file":"figure-material-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-material-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { MATERIAL_PRESETS, SEGMENT_PRESETS, figureTrianglesOf } from '@hatiolab/figure-model'\nimport { activePalette } from '@hatiolab/things-scene'\nimport type { FigureAssetGeometry, FigureMaterial, FigurePartEdit } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { isRoundBody } from './starting-shape.js'\nimport { glowOf, materialEditOf, offeredPaletteOf } from './material-edit.js'\n\n/** The purpose each palette role is for, by its token. */\nconst PURPOSE: Record<string, string> = {\n 'palette.primary': 'primary',\n 'palette.accent': 'accent',\n 'palette.neutral': 'neutral',\n 'palette.dark': 'dark',\n 'palette.light': 'light',\n 'palette.surface': 'surface',\n 'palette.status-danger': 'danger',\n 'palette.status-warning': 'warning',\n 'palette.status-info': 'info',\n 'palette.status-success': 'success'\n}\nexport const purposeOf = (token?: string) => i18next.t(`figure.label.palette-purpose-${PURPOSE[token ?? ''] ?? 'registered'}`)\n\n/** Bodies with a rim — the ones `set-segments` accepts. */\nconst DEFAULT_SEGMENTS = 12\n\n/**\n * 재질 — a part's material.\n *\n * An earlier editor showed the colour role as a raw token (`palette.dark`) in a select and nothing else. The material\n * holds the colour role, a preset, transparency and a glow; a round body also has how finely its rim is drawn. This\n * shows those: painted colours and lit colours as two groups of swatches, each named for what it\n * is for.\n *\n * Not here, because a material has no place for them: flat shading and casting\n * shadows. The glow's colour comes from the lit colours.\n *\n * Every change leaves as one `part-edit`; the editor runs it through the session, one step of the undo history.\n */\n@customElement('figure-material-panel')\nexport class FigureMaterialPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n div[field] {\n display: grid;\n gap: 4px;\n }\n /* A slider and its value on one line, the value at the end where the eye finishes. */\n div[slider] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 3em;\n align-items: center;\n gap: 8px;\n }\n div[slider] output {\n text-align: right;\n font-variant-numeric: tabular-nums;\n }\n div[swatches] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n button[swatch] {\n width: 22px;\n height: 22px;\n padding: 0;\n border-radius: 50%;\n border: 1px solid var(--md-sys-color-outline-variant);\n background: var(--swatch);\n cursor: pointer;\n }\n button[swatch][on] {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 1px;\n }\n select,\n input[type='range'] {\n font: inherit;\n min-width: 0;\n }\n /* The segment picker: the four common counts as their silhouettes with their triangle cost, the rest folded. */\n div[segments] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 4px;\n }\n button[seg] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1px;\n padding: 6px 2px 4px;\n font: inherit;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[seg]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n button[seg][on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n /* Small, and 12 and 16 both read as circles: large enough for the corners to show. */\n button[seg] svg {\n display: block;\n width: 32px;\n height: 32px;\n }\n button[seg] svg polygon {\n fill: var(--md-sys-color-surface-container-highest);\n stroke: currentColor;\n stroke-width: 1.1;\n stroke-linejoin: miter;\n }\n button[seg][on] svg polygon {\n fill: var(--md-sys-color-surface-container-lowest);\n }\n button[seg] b {\n font-weight: 600;\n }\n button[seg] small {\n margin: 0;\n }\n details[free] summary {\n margin-top: 4px;\n cursor: pointer;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[presets] {\n display: flex;\n gap: 4px;\n margin-top: 4px;\n }\n div[presets] button {\n flex: 1;\n padding: 3px 0;\n font: inherit;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n div[presets] button[on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n `]\n\n /** The selected body as evaluated — its material, its segments and its shape. */\n @property({ attribute: false }) body?: FigureAssetGeometry & { id: string }\n /** The asset's palette, token → colour. */\n @property({ attribute: false }) palette: Record<string, string> = {}\n @property({ type: Boolean }) locked = false\n /** The glow while its slider is dragged: shown beside it and drawn on the canvas, recorded on release. */\n @state() private glowing?: number\n\n render() {\n const part = this.body\n if (!part) return nothing\n const material = part.material\n /*\n The roles offered are the app's palette, with any the asset carries beyond it. A new asset carries\n only its primary colour; a role it lacks is sent with its colour and added in the same edit (`put`).\n */\n const entries = Object.entries(this.offered())\n const painted = entries.filter(([token]) => !token.startsWith('palette.status-'))\n const lit = entries.filter(([token]) => token.startsWith('palette.status-'))\n const glow = material.emissive\n return html`<section>\n <h3>${i18next.t('figure.label.part-material')}</h3>\n <div field>\n <label>${i18next.t('figure.label.palette-role')}</label>\n <div swatches>${this.swatches(painted, material.token, token => this.put({ token }))}</div>\n ${lit.length\n ? html`<small>${i18next.t('figure.label.status-colors')}</small>\n <div swatches>${this.swatches(lit, material.token, token => this.put({ token }))}</div>`\n : nothing}\n </div>\n <div form>\n <span>${i18next.t('figure.label.preset')}</span>\n <select\n ?disabled=${this.locked}\n @change=${(e: Event) => this.put({ preset: ((e.target as HTMLSelectElement).value || undefined) as FigureMaterial['preset'] })}\n >\n <option value=\"\" ?selected=${!material.preset}>${i18next.t('figure.label.none')}</option>\n ${MATERIAL_PRESETS.map(\n preset => html`<option value=${preset} ?selected=${preset === material.preset}>${i18next.t('figure.label.preset-' + preset)}</option>`\n )}\n </select>\n </div>\n <label toggle>\n <input\n type=\"checkbox\"\n ?disabled=${this.locked}\n .checked=${!!material.transparent}\n @change=${(e: Event) => this.put({ transparent: (e.target as HTMLInputElement).checked || undefined })}\n />\n ${i18next.t('figure.label.transparent')}\n </label>\n <div form>\n <span>${i18next.t('figure.label.emissive')}</span>\n <div slider>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n ?disabled=${this.locked}\n .value=${String(glow?.intensity ?? 0)}\n @input=${(e: Event) => this.glow(Number((e.target as HTMLInputElement).value), true)}\n @change=${(e: Event) => this.glow(Number((e.target as HTMLInputElement).value))}\n />\n <output>${(this.glowing ?? glow?.intensity ?? 0).toFixed(2)}</output>\n </div>\n </div>\n ${glow\n ? html`<div field>\n <label>${i18next.t('figure.label.emissive-color')}</label>\n <div swatches>${this.swatches(lit.length ? lit : painted, glow.token, token => this.put({ emissive: { ...glow, token } }))}</div>\n </div>`\n : nothing}\n ${this.renderSegments(part)}\n </section>`\n }\n\n private renderSegments(part: FigureAssetGeometry & { id: string }) {\n if (!isRoundBody((part as { shape?: never }).shape)) return nothing\n const current = part.segments ?? DEFAULT_SEGMENTS\n return html`\n <div field>\n <label>${i18next.t('figure.label.segments')}</label>\n <div segments>\n ${SEGMENT_PRESETS.slice(0, 4).map(\n preset => html`<button\n seg\n ?on=${preset === current}\n ?disabled=${this.locked}\n @click=${() => this.edit({ kind: 'set-segments', target: part.id, segments: preset })}\n >\n ${silhouette(preset)}\n <b>${preset}</b>\n <small>${i18next.t('figure.text.n-triangles', { count: figureTrianglesOf({ ...part, segments: preset }) })}</small>\n </button>`\n )}\n </div>\n <details free ?open=${SEGMENT_PRESETS.slice(4).includes(current as never)}>\n <summary>${i18next.t('figure.text.more-segment-options')}</summary>\n <div presets>\n ${SEGMENT_PRESETS.slice(4).map(\n preset => html`<button ?on=${preset === current} ?disabled=${this.locked} @click=${() => this.edit({ kind: 'set-segments', target: part.id, segments: preset })}>\n ${preset}\n </button>`\n )}\n </div>\n </details>\n </div>\n `\n }\n\n private swatches(entries: [string, string][], chosen: string | undefined, pick: (token: string) => void) {\n return entries.map(\n ([token, colour]) => html`<button\n swatch\n ?on=${token === chosen}\n ?disabled=${this.locked}\n title=${purposeOf(token)}\n aria-label=${purposeOf(token)}\n style=\"--swatch:${colour}\"\n @click=${() => pick(token)}\n ></button>`\n )\n }\n\n private offered(): Record<string, string> {\n return offeredPaletteOf(activePalette() as Record<string, string>, this.palette)\n }\n\n private glow(intensity: number, live = false) {\n this.glowing = live ? intensity : undefined\n this.put(glowOf(this.body!.material, intensity), live)\n }\n\n /** A material change: recorded, or -- while a slider is dragged -- only drawn (`part-live`). */\n private put(patch: Partial<FigureMaterial>, live = false) {\n const edit = materialEditOf(this.body!.id, this.body!.material, patch, this.palette, this.offered())\n if (live) this.dispatchEvent(new CustomEvent('part-live', { detail: { edit }, bubbles: true, composed: true }))\n else this.edit(edit)\n }\n\n private edit(edit: FigurePartEdit) {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }))\n }\n}\n\n/** A regular polygon of this many sides -- how round a body with this rim count looks from its end. */\nfunction silhouette(segments: number) {\n const points: string[] = []\n for (let i = 0; i < segments; i++) {\n const angle = -Math.PI / 2 + (i * 2 * Math.PI) / segments\n points.push(`${(16 + 13 * Math.cos(angle)).toFixed(2)},${(16 + 13 * Math.sin(angle)).toFixed(2)}`)\n }\n return svg`<svg viewBox=\"0 0 32 32\" aria-hidden=\"true\"><polygon points=${points.join(' ')}></polygon></svg>`\n}\n"]}
1
+ {"version":3,"file":"figure-material-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-material-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { MATERIAL_PRESETS, SEGMENT_PRESETS, figureTrianglesOf } from '@hatiolab/figure-model'\nimport { activePalette } from '@hatiolab/things-scene'\nimport type { FigureAssetGeometry, FigureMaterial, FigurePartEdit } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { isRoundBody } from './starting-shape.js'\nimport { glowOf, materialEditOf, offeredPaletteOf } from './material-edit.js'\n\n/** The purpose each palette role is for, by its token. */\nconst PURPOSE: Record<string, string> = {\n 'palette.primary': 'primary',\n 'palette.accent': 'accent',\n 'palette.neutral': 'neutral',\n 'palette.dark': 'dark',\n 'palette.light': 'light',\n 'palette.surface': 'surface',\n 'palette.status-danger': 'danger',\n 'palette.status-warning': 'warning',\n 'palette.status-info': 'info',\n 'palette.status-success': 'success'\n}\nexport const purposeOf = (token?: string) => i18next.t(`figure.label.palette-purpose-${PURPOSE[token ?? ''] ?? 'registered'}`)\n\n/** Bodies with a rim — the ones `set-segments` accepts. */\nconst DEFAULT_SEGMENTS = 12\n\n/**\n * 재질 — a part's material.\n *\n * An earlier editor showed the colour role as a raw token (`palette.dark`) in a select and nothing else. The material\n * holds the colour role, a preset, transparency and a glow; a round body also has how finely its rim is drawn. This\n * shows those: painted colours and lit colours as two groups of swatches, each named for what it\n * is for.\n *\n * Not here, because a material has no place for them: flat shading and casting\n * shadows. The glow's colour comes from the lit colours.\n *\n * Every change leaves as one `part-edit`; the editor runs it through the session, one step of the undo history.\n */\n@customElement('figure-material-panel')\nexport class FigureMaterialPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n /* The rarer half, drawn inside the 「더 보기」 fold: the fold gives the padding and the line. */\n div[more] {\n display: grid;\n gap: 8px;\n }\n div[field] {\n display: grid;\n gap: 4px;\n }\n /* A slider and its value on one line, the value at the end where the eye finishes. */\n div[slider] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 3em;\n align-items: center;\n gap: 8px;\n }\n div[slider] output {\n text-align: right;\n font-variant-numeric: tabular-nums;\n }\n div[swatches] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n button[swatch] {\n width: 22px;\n height: 22px;\n padding: 0;\n border-radius: 50%;\n border: 1px solid var(--md-sys-color-outline-variant);\n background: var(--swatch);\n cursor: pointer;\n }\n button[swatch][on] {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 1px;\n }\n select,\n input[type='range'] {\n font: inherit;\n min-width: 0;\n }\n /* The segment picker: the four common counts as their silhouettes with their triangle cost, the rest folded. */\n div[segments] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 4px;\n }\n button[seg] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1px;\n padding: 6px 2px 4px;\n font: inherit;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[seg]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n button[seg][on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n /* Small, and 12 and 16 both read as circles: large enough for the corners to show. */\n button[seg] svg {\n display: block;\n width: 32px;\n height: 32px;\n }\n button[seg] svg polygon {\n fill: var(--md-sys-color-surface-container-highest);\n stroke: currentColor;\n stroke-width: 1.1;\n stroke-linejoin: miter;\n }\n button[seg][on] svg polygon {\n fill: var(--md-sys-color-surface-container-lowest);\n }\n button[seg] b {\n font-weight: 600;\n }\n button[seg] small {\n margin: 0;\n }\n details[free] summary {\n margin-top: 4px;\n cursor: pointer;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[presets] {\n display: flex;\n gap: 4px;\n margin-top: 4px;\n }\n div[presets] button {\n flex: 1;\n padding: 3px 0;\n font: inherit;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n div[presets] button[on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n `]\n\n /** The selected body as evaluated — its material, its segments and its shape. */\n @property({ attribute: false }) body?: FigureAssetGeometry & { id: string }\n /** The asset's palette, token → colour. */\n @property({ attribute: false }) palette: Record<string, string> = {}\n @property({ type: Boolean }) locked = false\n /**\n * Which half to draw. `look` is what most parts need -- the colour role and the preset; `more` is what few parts\n * need -- transparency, glow and the segments of a round part -- drawn in the properties' 「더 보기」 fold.\n */\n @property({ type: String }) show: 'look' | 'more' = 'look'\n /** The glow while its slider is dragged: shown beside it and drawn on the canvas, recorded on release. */\n @state() private glowing?: number\n\n render() {\n const part = this.body\n if (!part) return nothing\n const material = part.material\n /*\n The roles offered are the app's palette, with any the asset carries beyond it. A new asset carries\n only its primary colour; a role it lacks is sent with its colour and added in the same edit (`put`).\n */\n const entries = Object.entries(this.offered())\n const painted = entries.filter(([token]) => !token.startsWith('palette.status-'))\n const lit = entries.filter(([token]) => token.startsWith('palette.status-'))\n const glow = material.emissive\n if (this.show === 'more') return this.renderMore(part, painted, lit)\n return html`<section>\n <h3>${i18next.t('figure.label.part-material')}</h3>\n <div field>\n <label>${i18next.t('figure.label.palette-role')}</label>\n <div swatches>${this.swatches(painted, material.token, token => this.put({ token }))}</div>\n ${lit.length\n ? html`<small>${i18next.t('figure.label.status-colors')}</small>\n <div swatches>${this.swatches(lit, material.token, token => this.put({ token }))}</div>`\n : nothing}\n </div>\n <div form>\n <span>${i18next.t('figure.label.preset')}</span>\n <select\n ?disabled=${this.locked}\n @change=${(e: Event) => this.put({ preset: ((e.target as HTMLSelectElement).value || undefined) as FigureMaterial['preset'] })}\n >\n <option value=\"\" ?selected=${!material.preset}>${i18next.t('figure.label.none')}</option>\n ${MATERIAL_PRESETS.map(\n preset => html`<option value=${preset} ?selected=${preset === material.preset}>${i18next.t('figure.label.preset-' + preset)}</option>`\n )}\n </select>\n </div>\n </section>`\n }\n\n /** Transparency, glow and segments: few parts need them, so they wait in the 「더 보기」 fold. */\n private renderMore(part: FigureAssetGeometry & { id: string }, painted: [string, string][], lit: [string, string][]) {\n const material = part.material\n const glow = material.emissive\n return html`<div more>\n <label toggle>\n <input\n type=\"checkbox\"\n ?disabled=${this.locked}\n .checked=${!!material.transparent}\n @change=${(e: Event) => this.put({ transparent: (e.target as HTMLInputElement).checked || undefined })}\n />\n ${i18next.t('figure.label.transparent')}\n </label>\n <div form>\n <span>${i18next.t('figure.label.emissive')}</span>\n <div slider>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n ?disabled=${this.locked}\n .value=${String(glow?.intensity ?? 0)}\n @input=${(e: Event) => this.glow(Number((e.target as HTMLInputElement).value), true)}\n @change=${(e: Event) => this.glow(Number((e.target as HTMLInputElement).value))}\n />\n <output>${(this.glowing ?? glow?.intensity ?? 0).toFixed(2)}</output>\n </div>\n </div>\n ${glow\n ? html`<div field>\n <label>${i18next.t('figure.label.emissive-color')}</label>\n <div swatches>${this.swatches(lit.length ? lit : painted, glow.token, token => this.put({ emissive: { ...glow, token } }))}</div>\n </div>`\n : nothing}\n ${this.renderSegments(part)}\n </div>`\n }\n\n private renderSegments(part: FigureAssetGeometry & { id: string }) {\n if (!isRoundBody((part as { shape?: never }).shape)) return nothing\n const current = part.segments ?? DEFAULT_SEGMENTS\n return html`\n <div field>\n <label>${i18next.t('figure.label.segments')}</label>\n <div segments>\n ${SEGMENT_PRESETS.slice(0, 4).map(\n preset => html`<button\n seg\n ?on=${preset === current}\n ?disabled=${this.locked}\n @click=${() => this.edit({ kind: 'set-segments', target: part.id, segments: preset })}\n >\n ${silhouette(preset)}\n <b>${preset}</b>\n <small>${i18next.t('figure.text.n-triangles', { count: figureTrianglesOf({ ...part, segments: preset }) })}</small>\n </button>`\n )}\n </div>\n <details free ?open=${SEGMENT_PRESETS.slice(4).includes(current as never)}>\n <summary>${i18next.t('figure.text.more-segment-options')}</summary>\n <div presets>\n ${SEGMENT_PRESETS.slice(4).map(\n preset => html`<button ?on=${preset === current} ?disabled=${this.locked} @click=${() => this.edit({ kind: 'set-segments', target: part.id, segments: preset })}>\n ${preset}\n </button>`\n )}\n </div>\n </details>\n </div>\n `\n }\n\n private swatches(entries: [string, string][], chosen: string | undefined, pick: (token: string) => void) {\n return entries.map(\n ([token, colour]) => html`<button\n swatch\n ?on=${token === chosen}\n ?disabled=${this.locked}\n title=${purposeOf(token)}\n aria-label=${purposeOf(token)}\n style=\"--swatch:${colour}\"\n @click=${() => pick(token)}\n ></button>`\n )\n }\n\n private offered(): Record<string, string> {\n return offeredPaletteOf(activePalette() as Record<string, string>, this.palette)\n }\n\n private glow(intensity: number, live = false) {\n this.glowing = live ? intensity : undefined\n this.put(glowOf(this.body!.material, intensity), live)\n }\n\n /** A material change: recorded, or -- while a slider is dragged -- only drawn (`part-live`). */\n private put(patch: Partial<FigureMaterial>, live = false) {\n const edit = materialEditOf(this.body!.id, this.body!.material, patch, this.palette, this.offered())\n if (live) this.dispatchEvent(new CustomEvent('part-live', { detail: { edit }, bubbles: true, composed: true }))\n else this.edit(edit)\n }\n\n private edit(edit: FigurePartEdit) {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }))\n }\n}\n\n/** A regular polygon of this many sides -- how round a body with this rim count looks from its end. */\nfunction silhouette(segments: number) {\n const points: string[] = []\n for (let i = 0; i < segments; i++) {\n const angle = -Math.PI / 2 + (i * 2 * Math.PI) / segments\n points.push(`${(16 + 13 * Math.cos(angle)).toFixed(2)},${(16 + 13 * Math.sin(angle)).toFixed(2)}`)\n }\n return svg`<svg viewBox=\"0 0 32 32\" aria-hidden=\"true\"><polygon points=${points.join(' ')}></polygon></svg>`\n}\n"]}
@@ -33,6 +33,11 @@ export declare class FigurePreview extends FigurePreview_base {
33
33
  /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */
34
34
  selected: string;
35
35
  private setupPart;
36
+ /** One figure stretched by sliders, or the eight ways of growing side by side. */
37
+ private way;
38
+ /** The size the one figure is tried at; the authored size when the figure stands up. */
39
+ private trying?;
40
+ private board;
36
41
  /** What is being tried here now -- clip speeds and control values -- for the AI dock to hear with a request; none when nothing is. */
37
42
  tried(): {
38
43
  clips?: Record<string, number>;
@@ -43,6 +48,13 @@ export declare class FigurePreview extends FigurePreview_base {
43
48
  private registered?;
44
49
  private watcher?;
45
50
  render(): import("lit-html").TemplateResult<1>;
51
+ /** The one figure's size: a slider per axis the figure lets change, and what it is at the size tried. */
52
+ private renderSizes;
53
+ private chooseWay;
54
+ /** The one figure at another size on an axis, held to what the figure declares, its centre kept on the board's. */
55
+ private tryAxis;
56
+ /** The figure as stretched, whole in view: done once the slider is let go, so the ground does not slide under a drag. */
57
+ private fitView;
46
58
  private renderDrivers;
47
59
  /** A value the data's bindings read, as the board's saved values give it: a number on a slider when it has a range, a choice when it is closed. */
48
60
  private renderDataValue;
@@ -8,7 +8,8 @@ import { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH } from '@things-f
8
8
  import { dialShape } from './angle-dial.js';
9
9
  import { domainOf, motionWrittenByData, typedValueOf, valueRowsOf } from './data-interface.js';
10
10
  import { derivedOf } from './revision.js';
11
- import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, previewTypeOf, setupPartOf } from '@things-factory/figure-viewer/dist-client/preview-model.js';
11
+ import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, previewOneInstance, previewTypeOf, setupPartOf, sizeRangeOf, sizeWithinRange } from '@things-factory/figure-viewer/dist-client/preview-model.js';
12
+ import { previewSizeWordsOf } from './preview-size.js';
12
13
  let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
13
14
  constructor() {
14
15
  super(...arguments);
@@ -35,6 +36,9 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
35
36
  /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */
36
37
  this.selected = '';
37
38
  this.setupPart = '';
39
+ /** One figure stretched by sliders, or the eight ways of growing side by side. */
40
+ this.way = 'one';
41
+ this.board = { width: 0, height: 0 };
38
42
  }
39
43
  static { this.styles = css `
40
44
  :host {
@@ -88,6 +92,55 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
88
92
  flex-wrap: wrap;
89
93
  max-width: calc(100% - 16px);
90
94
  }
95
+ /* One figure or eight: two words in the legend, the one shown pressed. */
96
+ div[legend] button[way] {
97
+ padding: 0 6px;
98
+ font: inherit;
99
+ color: inherit;
100
+ background: none;
101
+ border: none;
102
+ border-radius: 4px;
103
+ cursor: pointer;
104
+ opacity: 0.7;
105
+ }
106
+ div[legend] button[way][aria-pressed='true'] {
107
+ opacity: 1;
108
+ background-color: rgba(255, 255, 255, 0.18);
109
+ }
110
+ /* The one figure's size: under the legend, in the controls' look. */
111
+ div[sizes] {
112
+ position: absolute;
113
+ top: 40px;
114
+ left: 8px;
115
+ display: grid;
116
+ gap: 6px;
117
+ padding: 8px 10px;
118
+ max-width: calc(100% - 16px);
119
+ box-sizing: border-box;
120
+ color: rgba(255, 255, 255, 0.92);
121
+ background-color: rgba(28, 28, 30, 0.72);
122
+ border-radius: 8px;
123
+ font-size: 0.72rem;
124
+ }
125
+ div[sizes] label {
126
+ display: grid;
127
+ grid-template-columns: 32px 160px 52px;
128
+ align-items: center;
129
+ gap: 8px;
130
+ }
131
+ div[sizes] input {
132
+ width: 100%;
133
+ accent-color: var(--md-sys-color-primary);
134
+ }
135
+ div[sizes] output {
136
+ font-variant-numeric: tabular-nums;
137
+ text-align: right;
138
+ }
139
+ div[sizes] p[said] {
140
+ margin: 0;
141
+ font-variant-numeric: tabular-nums;
142
+ font-weight: 600;
143
+ }
91
144
  /* A variant drawn at another size than it asks: the figure does not declare that size. */
92
145
  div[legend] small[clamp] {
93
146
  margin-left: 4px;
@@ -209,7 +262,8 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
209
262
  ${this.cannot
210
263
  ? html `<div cannot>${this.cannot}</div>`
211
264
  : html `<div legend>
212
- ${PREVIEW_WAYS.map(way => {
265
+ ${['one', 'eight'].map(way => html `<button way=${way} aria-pressed=${String(this.way === way)} @click=${() => this.chooseWay(way)}>${i18next.t(`figure.label.preview-way-${way}`)}</button>`)}
266
+ ${this.way === 'one' ? nothing : PREVIEW_WAYS.map(way => {
213
267
  const clamps = clampsByWay[way.name] ?? [];
214
268
  return html `<span>
215
269
  ${way.axes.length ? way.name : i18next.t('figure.label.preview-base')}
@@ -219,6 +273,7 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
219
273
  </span>`;
220
274
  })}
221
275
  </div>
276
+ ${this.way === 'one' && asset && this.trying ? this.renderSizes(asset, this.trying) : nothing}
222
277
  <div foot>${asset?.appearance.length ? html `<div setup>
223
278
  <p>${i18next.t('figure.text.preview-setup')}</p>
224
279
  <select aria-label=${i18next.t('figure.label.resize-part')} @change=${(e) => this.setupPart = e.target.value}>
@@ -228,6 +283,51 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
228
283
  </div>` : nothing}
229
284
  ${controls.length + clips.length + (asset?.motionGroups?.length ?? 0) + (asset ? valueRowsOf(asset).length : 0) ? this.renderDrivers(asset, controls, clips) : nothing}</div>`}`;
230
285
  }
286
+ /** The one figure's size: a slider per axis the figure lets change, and what it is at the size tried. */
287
+ renderSizes(asset, size) {
288
+ const say = (key, params) => i18next.t(key, params);
289
+ /* A board sizes a figure by the space it takes: with none declared, its box folds and no size can be tried. */
290
+ if (!asset.occupancy)
291
+ return html `<div sizes><p said>${i18next.t('figure.text.preview-needs-space')}</p></div>`;
292
+ return html `<div sizes>
293
+ ${['x', 'y', 'z'].map(axis => {
294
+ const range = sizeRangeOf(asset, axis);
295
+ if (!range)
296
+ return nothing;
297
+ return html `<label>
298
+ <span>${i18next.t(`figure.label.measure-${axis}`)}</span>
299
+ <input type="range" aria-label=${i18next.t(`figure.label.measure-${axis}`)} min=${range.min} max=${range.max} step="any" .value=${String(size[axis])}
300
+ @input=${(e) => this.tryAxis(axis, e.target.valueAsNumber)}
301
+ @change=${() => this.fitView()} />
302
+ <output>${Math.round(size[axis]).toLocaleString()}</output>
303
+ </label>`;
304
+ })}
305
+ <p said>${previewSizeWordsOf(asset, size, say)}</p>
306
+ </div>`;
307
+ }
308
+ chooseWay(way) {
309
+ if (way === this.way)
310
+ return;
311
+ this.way = way;
312
+ this.standUp();
313
+ }
314
+ /** The one figure at another size on an axis, held to what the figure declares, its centre kept on the board's. */
315
+ tryAxis(axis, value) {
316
+ const asset = this.asset;
317
+ const used = asset ? sizeWithinRange(asset, axis, value) : undefined;
318
+ if (used === undefined || !this.trying)
319
+ return;
320
+ this.trying = { ...this.trying, [axis]: used };
321
+ const instance = this.scene?.root?.components?.[0];
322
+ instance?.set(axis === 'x' ? { width: used } : axis === 'y' ? { depth: used } : { height: used });
323
+ const { width = 0, height = 0 } = instance?.model ?? {};
324
+ instance?.set({ left: this.board.width / 2 - width / 2, top: this.board.height / 2 - height / 2 });
325
+ }
326
+ /** The figure as stretched, whole in view: done once the slider is let go, so the ground does not slide under a drag. */
327
+ fitView() {
328
+ this.scene?.fit?.('ratio');
329
+ this.scene?.root?.fitCameraToModel?.();
330
+ }
231
331
  renderDrivers(asset, controls, clips) {
232
332
  const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id]);
233
333
  return html `<div clips>
@@ -422,7 +522,7 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
422
522
  this.teardown();
423
523
  return;
424
524
  }
425
- const signature = JSON.stringify([asset, view.placement, view.ceilingHeight]);
525
+ const signature = JSON.stringify([asset, view.placement, view.ceilingHeight, this.way]);
426
526
  if (this.scene && this.standing === signature)
427
527
  return;
428
528
  try {
@@ -451,12 +551,15 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
451
551
  this.valuing = {};
452
552
  registerFigure(type, asset);
453
553
  this.registered = type;
454
- const { components, board } = previewInstances(type, size);
554
+ const { components, board } = this.way === 'one' ? previewOneInstance(type, size) : previewInstances(type, size);
555
+ this.board = board;
556
+ this.trying = this.way === 'one' ? { ...size } : undefined;
455
557
  const model = {
456
558
  type: 'model-layer',
457
559
  width: board.width,
458
560
  height: board.height,
459
- depth: previewBoardDepth({ placement: asset.placement, base: { y: size.y } }, view.ceilingHeight, PREVIEW_FACTOR),
561
+ /* As tall as the figure may grow: twice for the eight, the declared height range's top for the one. */
562
+ depth: previewBoardDepth({ placement: asset.placement, base: { y: size.y } }, view.ceilingHeight, this.way === 'one' ? Math.max(1, (sizeRangeOf(asset, 'y')?.max ?? size.y) / size.y) : PREVIEW_FACTOR),
460
563
  threed: true,
461
564
  sky: 'color',
462
565
  ...sceneLook(view),
@@ -556,6 +659,14 @@ __decorate([
556
659
  state(),
557
660
  __metadata("design:type", Object)
558
661
  ], FigurePreview.prototype, "setupPart", void 0);
662
+ __decorate([
663
+ state(),
664
+ __metadata("design:type", String)
665
+ ], FigurePreview.prototype, "way", void 0);
666
+ __decorate([
667
+ state(),
668
+ __metadata("design:type", Object)
669
+ ], FigurePreview.prototype, "trying", void 0);
559
670
  FigurePreview = __decorate([
560
671
  customElement('figure-preview')
561
672
  ], FigurePreview);
@@ -1 +1 @@
1
- {"version":3,"file":"figure-preview.js","sourceRoot":"","sources":["../../client/modeller/figure-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, unsafeCSS } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { compileFigureAsset } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureDataRow, FigureDataValue } from '@hatiolab/figure-model'\nimport { create, registerFigure, unregisterFigure } from '@hatiolab/things-scene'\n\nimport { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH, type ViewSettings } from '@things-factory/figure-viewer/dist-client/figure-view.js'\nimport { dialShape } from './angle-dial.js'\nimport { domainOf, motionWrittenByData, typedValueOf, valueRowsOf } from './data-interface.js'\nimport { derivedOf, type FigureRevision } from './revision.js'\nimport { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, previewTypeOf, setupPartOf } from '@things-factory/figure-viewer/dist-client/preview-model.js'\n\n/**\n * The figure placed on a board.\n *\n * The editing canvas shows the figure at the size it was made. Half of what a figure is lies in how it answers a\n * different size, so here the figure being authored is registered as a board registers it and placed as\n * `FigureInstance`s: the size it was made, and each of the seven ways of growing one, two or three axes twice as\n * large, side by side at one scale, four to a row. The board draws them as it will on a real board --\n * placement, lights, environment, the same renderer.\n *\n * The controls: each value that poses the figure with a slider (and a dial for an angle), each clip's\n * speed, and 「이 자세를 기본으로」 to keep the pose as the one it stands in. Nothing here is edited; the canvas edits.\n */\n\ninterface SceneHandle {\n dispose?: () => void\n resize?: () => void\n fit?: (type?: string) => void\n /** The scene's container: it can make the content again while keeping the 3D renderer and the camera (undo and redo use it). */\n rootContainer?: { rebuildContentModel?: (model: unknown, options?: { preserveThreeD?: boolean }) => void }\n root?: {\n set?: (patch: Record<string, unknown>) => void\n fitCameraToModel?: () => void\n components?: { set?: (property: string, value: unknown) => void }[]\n }\n}\n\n@customElement('figure-preview')\nexport class FigurePreview extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background-color: ${unsafeCSS(WORKBENCH)};\n font-size: 0.76rem;\n }\n div[stage] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n }\n /*\n The size help and the posing controls share the stage's bottom edge. Each stood in its own corner, so on a narrow\n stage (the 1024 width, both side panels open) the controls lay over the help's button. One row that wraps keeps both\n whole: side by side when there is room, the controls above the help when there is not.\n */\n div[foot] {\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: 8px;\n display: flex;\n flex-wrap: wrap-reverse;\n justify-content: space-between;\n align-items: flex-end;\n gap: 8px;\n pointer-events: none;\n }\n div[foot] > * { pointer-events: auto; }\n div[setup] { max-width: min(320px, 100%); padding: 12px; border-radius: 8px; background: rgba(28,28,30,.9); color: #fff; display: grid; gap: 8px; }\n div[setup] p { margin: 0; line-height: 1.5; }\n div[setup] select, div[setup] button { font: inherit; padding: 8px; min-width: 0; }\n /* Which is which: side by side means nothing without a legend. */\n div[legend] {\n position: absolute;\n top: 8px;\n left: 8px;\n display: flex;\n gap: 6px;\n padding: 4px 8px;\n color: rgba(255, 255, 255, 0.9);\n background-color: rgba(28, 28, 30, 0.6);\n border-radius: 6px;\n font-size: 0.72rem;\n flex-wrap: wrap;\n max-width: calc(100% - 16px);\n }\n /* A variant drawn at another size than it asks: the figure does not declare that size. */\n div[legend] small[clamp] {\n margin-left: 4px;\n color: #ffcc80;\n }\n div[clips] {\n margin-left: auto;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(28, 28, 30, 0.72);\n border-radius: 8px;\n font-size: 0.72rem;\n }\n div[clips] label {\n display: grid;\n grid-template-columns: auto 20px 96px 34px;\n align-items: center;\n gap: 8px;\n }\n /* A motion group's clip: as wide as the dial, slider and value together, so its name reads. */\n div[clips] select {\n grid-column: 2 / -1;\n min-width: 0;\n font: inherit;\n color: inherit;\n background-color: rgba(255, 255, 255, 0.14);\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: 5px;\n }\n div[clips] svg[dial] {\n width: 20px;\n height: 20px;\n }\n div[clips] svg[dial] path {\n fill: rgba(255, 255, 255, 0.22);\n }\n div[clips] svg[dial] line {\n stroke: var(--md-sys-color-primary, #7cc6ff);\n stroke-width: 2;\n stroke-linecap: round;\n }\n div[clips] svg[dial] circle {\n fill: none;\n stroke: rgba(255, 255, 255, 0.35);\n }\n div[clips] button[pose] {\n align-self: stretch;\n padding: 4px 8px;\n font: inherit;\n font-size: 0.7rem;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(255, 255, 255, 0.14);\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: 5px;\n cursor: pointer;\n }\n div[clips] button[pose]:hover {\n background-color: rgba(255, 255, 255, 0.24);\n }\n div[clips] span[drive] {\n opacity: 0.6;\n font-size: 0.66rem;\n }\n div[clips] input {\n width: 96px;\n accent-color: var(--md-sys-color-primary);\n }\n div[clips] output {\n font-variant-numeric: tabular-nums;\n text-align: right;\n opacity: 0.85;\n }\n div[cannot] {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n text-align: center;\n line-height: 1.6;\n color: #f4f4f4;\n background-color: ${unsafeCSS(WORKBENCH)};\n }\n `\n\n /** The figure being authored, as the editor holds it now. */\n @property({ attribute: false }) revision?: FigureRevision\n private get asset(): FigureAsset | undefined {\n return this.revision?.asset\n }\n /** The size the figure was made at, once per version: without a declared size it is measured by evaluating the figure. */\n private authoredSize(): { x: number; y: number; z: number } {\n const shown = this.revision!\n return derivedOf(shown, 'authored-size', () => authoredSizeOf(shown.asset))\n }\n /** Which figure: the type it is registered under here is this one's own, never a real type a board uses. */\n @property() context = ''\n /**\n * In front. Once made, the preview stays standing behind the edit canvas, so coming back to it is not a new 3D scene\n * (about 300 ms): out of sight it does not follow the figure, and when it is shown again it makes its content again\n * inside the scene that stands if the figure changed meanwhile.\n */\n @property({ type: Boolean }) active = true\n private stale = false\n @property({ attribute: false }) view: ViewSettings = DEFAULT_VIEW\n\n @query('div[stage]') private stage!: HTMLDivElement\n @state() private cannot = ''\n /** Clip speeds and control values given here -- the preview's, not the figure's. */\n @state() private driving: Record<string, number> = {}\n @state() private giving: Record<string, number> = {}\n /** Per motion group, the clip played here and how fast (ADR-0051 §3.2) -- the preview's, as a board's \\`motion\\`. */\n @state() private moving: Record<string, { clip: string; speed: number }> = {}\n /** Data values given here by accessor, as a board's saved `figureValues`: what moves a group whose clip or speed the data writes. */\n @state() private valuing: Record<string, FigureDataValue> = {}\n /** Data tried in the data tab: the preview's figures take it as their \\`data\\`, as a board's data source gives it; null when none is. */\n @property({ attribute: false }) sample: unknown = null\n /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */\n @property() selected = ''\n @state() private setupPart = ''\n\n /** What is being tried here now -- clip speeds and control values -- for the AI dock to hear with a request; none when nothing is. */\n tried(): { clips?: Record<string, number>; controls?: Record<string, number> } | undefined {\n const clips = Object.keys(this.driving).length ? { ...this.driving } : undefined\n const controls = Object.keys(this.giving).length ? { ...this.giving } : undefined\n return clips || controls ? { ...(clips ? { clips } : {}), ...(controls ? { controls } : {}) } : undefined\n }\n\n private scene?: SceneHandle\n private standing?: string\n private registered?: string\n private watcher?: ResizeObserver\n\n render() {\n const asset = this.asset\n const controls = asset ? asset.document.model.inputs.filter(i => i.role === 'state') : []\n /* Clips in a motion group are played by the group's choice of clip, not each by its own speed. */\n const grouped = new Set((asset?.motionGroups ?? []).flatMap(g => g.clips))\n const clips = asset ? [...new Set((asset.drivers ?? []).map(d => d.clip))].filter(c => !grouped.has(c)) : []\n let clampsByWay: ReturnType<typeof previewClampsOf> = {}\n try {\n clampsByWay = asset ? previewClampsOf(asset, this.authoredSize()) : {}\n } catch {\n clampsByWay = {}\n }\n /* The part the help speaks of: the one picked in it, else the one chosen in the editor, else the first. */\n const setupPart = setupPartOf(this.setupPart, this.selected, asset?.appearance.map(p => p.target) ?? [])\n return html`<div stage></div>\n ${this.cannot\n ? html`<div cannot>${this.cannot}</div>`\n : html`<div legend>\n ${PREVIEW_WAYS.map(way => {\n const clamps = clampsByWay[way.name] ?? []\n return html`<span>\n ${way.axes.length ? way.name : i18next.t('figure.label.preview-base')}\n ${clamps.length\n ? html`<small clamp>${clamps.map(c => i18next.t('figure.text.preview-clamped', { axis: c.axis.toUpperCase(), asked: c.asked.toLocaleString(), used: c.used.toLocaleString() })).join(' · ')}</small>`\n : nothing}\n </span>`\n })}\n </div>\n <div foot>${asset?.appearance.length ? html`<div setup>\n <p>${i18next.t('figure.text.preview-setup')}</p>\n <select aria-label=${i18next.t('figure.label.resize-part')} @change=${(e: Event) => this.setupPart = (e.target as HTMLSelectElement).value}>\n ${asset.appearance.map(p => html`<option value=${p.target} ?selected=${p.target === setupPart}>${p.target}</option>`)}\n </select>\n <button @click=${() => this.dispatchEvent(new CustomEvent('configure-size', { detail: { part: setupPart }, bubbles: true, composed: true }))}>${i18next.t('figure.button.configure-size')}</button>\n </div>` : nothing}\n ${controls.length + clips.length + (asset?.motionGroups?.length ?? 0) + (asset ? valueRowsOf(asset).length : 0) ? this.renderDrivers(asset!, controls, clips) : nothing}</div>`}`\n }\n\n private renderDrivers(asset: FigureAsset, controls: FigureAsset['document']['model']['inputs'], clips: string[]) {\n const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id])\n return html`<div clips>\n ${changed.length\n ? html`<button pose title=${i18next.t('figure.text.pose-as-default-explained')} @click=${() => this.poseAsDefault()}>\n ${i18next.t('figure.button.pose-as-default')}\n </button>`\n : nothing}\n ${controls.map(control => {\n const value = this.giving[control.id] ?? asset.stateDefaults[control.id] ?? control.min\n const step = Math.max((control.max - control.min) / 100, 0.01)\n const label = asset.stateInputs?.[control.id]?.label ?? control.id\n return html`<label>\n <span>${label} <span drive>${control.unit}</span></span>\n ${this.renderDial(control, value)}\n <input\n type=\"range\"\n min=${control.min}\n max=${control.max}\n step=${step}\n .value=${String(value)}\n @input=${(e: Event) => this.give(control.id, Number((e.target as HTMLInputElement).value))}\n />\n <output>${Math.round(value)}</output>\n </label>`\n })}\n ${clips.map(clip => {\n /*\n A clip nothing has driven stands still, here as on a board: the instances get a speed only from `drive`. The\n slider showed 1.00 over rollers that did not turn (2026-10-03). It shows what plays -- 0 until moved.\n */\n const value = this.driving[clip] ?? 0\n return html`<label>\n <span>${clip} <span drive>${i18next.t('figure.label.playback-speed')}</span></span>\n <span></span>\n <input type=\"range\" min=\"0\" max=\"3\" step=\"0.05\" .value=${String(value)} @input=${(e: Event) => this.drive(clip, Number((e.target as HTMLInputElement).value))} />\n <output>${value.toFixed(2)}</output>\n </label>`\n })}\n ${valueRowsOf(asset).map(row => this.renderDataValue(row))}\n ${(asset.motionGroups ?? []).map(group => {\n const playing = this.moving[group.id] ?? { clip: group.defaultClip, speed: 0 }\n /* A field the data writes wins over the group's own setting, here as on a board: it is moved by the data values above. */\n const byData = motionWrittenByData(asset)[group.id] ?? { clip: false, speed: false }\n return html`${byData.clip\n ? nothing\n : html`<label>\n <span>${group.label} <span drive>${i18next.t('figure.label.clip')}</span></span>\n <select aria-label=${group.label} @change=${(e: Event) => this.move(group.id, { ...playing, clip: (e.target as HTMLSelectElement).value })}>\n ${group.clips.map(c => html`<option value=${c} ?selected=${c === playing.clip}>${c}</option>`)}\n </select>\n </label>`}\n ${byData.speed\n ? nothing\n : html`<label>\n <span>${group.label} <span drive>${i18next.t('figure.label.playback-speed')}</span></span>\n <span></span>\n <input type=\"range\" min=\"0\" max=\"3\" step=\"0.05\" .value=${String(playing.speed)} @input=${(e: Event) => this.move(group.id, { ...playing, speed: Number((e.target as HTMLInputElement).value) })} />\n <output>${playing.speed.toFixed(2)}</output>\n </label>`}`\n })}\n </div>`\n }\n\n /** A value the data's bindings read, as the board's saved values give it: a number on a slider when it has a range, a choice when it is closed. */\n private renderDataValue(row: FigureDataRow) {\n const node = row.node\n const label = row.label ?? row.path\n const fallback = 'default' in node ? node.default : undefined\n const value = this.valuing[row.path] ?? fallback\n if (node.kind === 'number') {\n const ranged = node.min !== undefined && node.max !== undefined && node.max > node.min\n return html`<label>\n <span>${label} <span drive>${node.unit ?? ''}</span></span>\n <span></span>\n ${ranged\n ? html`<input type=\"range\" min=${node.min!} max=${node.max!} step=${Math.max((node.max! - node.min!) / 100, 0.01)} .value=${String(value ?? node.min)} @input=${(e: Event) => this.value(row, (e.target as HTMLInputElement).value)} />\n <output>${value === undefined ? '' : Number(value).toFixed(2)}</output>`\n : html`<input type=\"number\" step=\"any\" .value=${value === undefined ? '' : String(value)} @change=${(e: Event) => this.value(row, (e.target as HTMLInputElement).value)} />\n <span></span>`}\n </label>`\n }\n const domain = domainOf(node)\n if (!domain.length)\n return html`<label>\n <span>${label}</span>\n <input aria-label=${label} .value=${value === undefined ? '' : String(value)} @change=${(e: Event) => this.value(row, (e.target as HTMLInputElement).value)} />\n </label>`\n return html`<label>\n <span>${label}</span>\n <select aria-label=${label} @change=${(e: Event) => this.value(row, (e.target as HTMLSelectElement).value)}>\n ${domain.map(v => html`<option value=${String(v)} ?selected=${v === value}>${typeof v === 'boolean' ? i18next.t(v ? 'figure.label.data-true' : 'figure.label.data-false') : v}</option>`)}\n </select>\n </label>`\n }\n\n private value(row: FigureDataRow, typed: string) {\n const value = typedValueOf(row.node, typed)\n const next = { ...this.valuing }\n if (typeof value === 'number' && Number.isNaN(value)) delete next[row.path]\n else next[row.path] = value\n this.valuing = next\n this.tellInstances('figureValues', this.valuing)\n }\n\n private move(group: string, playing: { clip: string; speed: number }) {\n this.moving = { ...this.moving, [group]: playing }\n this.tellInstances('motion', Object.fromEntries(Object.entries(this.moving).map(([id, m]) => [id, { ...m, paused: false }])))\n }\n\n private renderDial(control: { unit: string; min: number; max: number }, value: number) {\n if (control.unit !== 'deg') return html`<span></span>`\n const dial = dialShape(control.min, control.max, value)\n const span = dial.radius * 2 + 2\n return html`<svg dial viewBox=\"0 0 ${span} ${span}\" aria-hidden=\"true\">\n <path d=${dial.sector}></path>\n <circle cx=${dial.centre.x} cy=${dial.centre.y} r=${dial.radius}></circle>\n <line x1=${dial.centre.x} y1=${dial.centre.y} x2=${dial.needle.x} y2=${dial.needle.y}></line>\n </svg>`\n }\n\n private drive(clip: string, value: number) {\n this.driving = { ...this.driving, [clip]: value }\n this.tellInstances('clips', this.driving)\n }\n\n private give(id: string, value: number) {\n this.giving = { ...this.giving, [id]: value }\n this.tellInstances('parameters', this.giving)\n }\n\n private poseAsDefault() {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit: { kind: 'set-state-defaults', values: { ...this.giving } } }, bubbles: true, composed: true }))\n this.giving = {}\n }\n\n private tellInstances(key: 'clips' | 'parameters' | 'motion' | 'figureValues', values: Record<string, unknown> | null) {\n for (const component of this.scene?.root?.components ?? []) component.set?.(key, values === null ? null : { ...values })\n }\n\n /** The tried data to the figures standing now, as their \\`data\\`: the preview's board is not saved. */\n private tellData(data: unknown) {\n for (const component of this.scene?.root?.components ?? []) component.set?.('data', data === null || data === undefined ? null : structuredClone(data))\n }\n\n\n /** The data sample to the figures standing now. */\n private tellSample() {\n if (this.sample !== null && this.sample !== undefined) this.tellData(this.sample)\n }\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('sample') && this.scene) this.tellData(this.sample)\n if (!this.hasUpdated) return\n if (!this.active) {\n this.stale = true\n return\n }\n if (this.stale || changed.has('active')) {\n this.stale = false\n this.standUp()\n return\n }\n if (changed.has('revision') || changed.has('context')) {\n this.standUp()\n return\n }\n if (!changed.has('view')) return\n const before = changed.get('view') as ViewSettings | undefined\n const now = this.view ?? DEFAULT_VIEW\n if (before?.placement !== now.placement || before?.ceilingHeight !== now.ceilingHeight) this.standUp()\n else this.scene?.root?.set?.(sceneLook(now))\n }\n\n firstUpdated() {\n this.standUp()\n this.watcher = new ResizeObserver(() => {\n if (this.active) this.scene?.resize?.()\n })\n this.watcher.observe(this)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.watcher?.disconnect()\n this.watcher = undefined\n this.teardown()\n }\n\n private standUp() {\n const asset = this.asset\n const view = this.view ?? DEFAULT_VIEW\n if (!asset || !asset.appearance.length) {\n this.cannot = i18next.t('figure.text.preview-needs-a-part')\n this.teardown()\n return\n }\n const signature = JSON.stringify([asset, view.placement, view.ceilingHeight])\n if (this.scene && this.standing === signature) return\n try {\n compileFigureAsset(asset)\n } catch {\n this.cannot = i18next.t('figure.text.preview-blocked-by-errors')\n this.teardown()\n return\n }\n try {\n /* Registered as a board registers it, under a type of this preview's own: a real type's drawings stay as they are. */\n const type = previewTypeOf(this.context)\n const size = this.authoredSize()\n /*\n The figure changed under a scene that is standing: the same type is registered again and the content made again\n inside the same 3D scene. Making a scene anew costs about a third of a second and its first drawing compiles the\n shaders again -- a value typed beside the preview held the screen for seconds. The scene is made anew only when\n there is none, for another figure, or when the content cannot be made again in place.\n */\n const again = this.scene?.rootContainer?.rebuildContentModel && this.registered === type\n if (!again) this.teardown()\n this.driving = {}\n this.moving = {}\n this.valuing = {}\n registerFigure(type, asset)\n this.registered = type\n const { components, board } = previewInstances(type, size)\n const model = {\n type: 'model-layer',\n width: board.width,\n height: board.height,\n depth: previewBoardDepth({ placement: asset.placement, base: { y: size.y } }, view.ceilingHeight, PREVIEW_FACTOR),\n threed: true,\n sky: 'color',\n ...sceneLook(view),\n placement: view.placement,\n heights: { floor: 0, ceiling: view.ceilingHeight },\n pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),\n components\n }\n if (again) {\n try {\n this.scene!.rootContainer!.rebuildContentModel!(model, { preserveThreeD: true })\n this.standing = signature\n this.cannot = ''\n this.tellSample()\n return\n } catch {\n this.teardown()\n registerFigure(type, asset)\n this.registered = type\n }\n }\n this.scene = create({\n target: this.stage,\n model,\n mode: 0\n }) as SceneHandle\n this.standing = signature\n this.scene.resize?.()\n this.scene.fit?.('ratio')\n this.scene.root?.fitCameraToModel?.()\n this.cannot = ''\n this.tellSample()\n } catch (error) {\n this.cannot = (error as Error).message\n }\n }\n\n private teardown() {\n this.scene?.dispose?.()\n this.scene = undefined\n this.standing = undefined\n if (this.registered) unregisterFigure(this.registered)\n this.registered = undefined\n }\n}\n"]}
1
+ {"version":3,"file":"figure-preview.js","sourceRoot":"","sources":["../../client/modeller/figure-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, unsafeCSS } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { compileFigureAsset } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureDataRow, FigureDataValue } from '@hatiolab/figure-model'\nimport { create, registerFigure, unregisterFigure } from '@hatiolab/things-scene'\n\nimport { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH, type ViewSettings } from '@things-factory/figure-viewer/dist-client/figure-view.js'\nimport { dialShape } from './angle-dial.js'\nimport { domainOf, motionWrittenByData, typedValueOf, valueRowsOf } from './data-interface.js'\nimport { derivedOf, type FigureRevision } from './revision.js'\nimport { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, previewOneInstance, previewTypeOf, setupPartOf, sizeRangeOf, sizeWithinRange } from '@things-factory/figure-viewer/dist-client/preview-model.js'\nimport { previewSizeWordsOf } from './preview-size.js'\n\n/**\n * The figure placed on a board.\n *\n * The editing canvas shows the figure at the size it was made. Half of what a figure is lies in how it answers a\n * different size, so here the figure being authored is registered as a board registers it and placed as a\n * `FigureInstance` the author stretches with a slider per axis the figure lets change, its size and its rows' counts\n * said beside it (first 15 minutes, 2026-10-16: eight variants side by side at one scale made a 60 mm roller on a 3 m\n * conveyor a speck). The eight -- the size it was made, and each of the seven ways of growing one, two or three axes\n * twice as large, four to a row -- are a choice in the legend. The board draws them as it will on a real board --\n * placement, lights, environment, the same renderer.\n *\n * The controls: each value that poses the figure with a slider (and a dial for an angle), each clip's\n * speed, and 「이 자세를 기본으로」 to keep the pose as the one it stands in. Nothing here is edited; the canvas edits.\n */\n\ninterface SceneHandle {\n dispose?: () => void\n resize?: () => void\n fit?: (type?: string) => void\n /** The scene's container: it can make the content again while keeping the 3D renderer and the camera (undo and redo use it). */\n rootContainer?: { rebuildContentModel?: (model: unknown, options?: { preserveThreeD?: boolean }) => void }\n root?: {\n set?: (patch: Record<string, unknown>) => void\n fitCameraToModel?: () => void\n components?: { set?: (property: string, value: unknown) => void }[]\n }\n}\n\n@customElement('figure-preview')\nexport class FigurePreview extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background-color: ${unsafeCSS(WORKBENCH)};\n font-size: 0.76rem;\n }\n div[stage] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n }\n /*\n The size help and the posing controls share the stage's bottom edge. Each stood in its own corner, so on a narrow\n stage (the 1024 width, both side panels open) the controls lay over the help's button. One row that wraps keeps both\n whole: side by side when there is room, the controls above the help when there is not.\n */\n div[foot] {\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: 8px;\n display: flex;\n flex-wrap: wrap-reverse;\n justify-content: space-between;\n align-items: flex-end;\n gap: 8px;\n pointer-events: none;\n }\n div[foot] > * { pointer-events: auto; }\n div[setup] { max-width: min(320px, 100%); padding: 12px; border-radius: 8px; background: rgba(28,28,30,.9); color: #fff; display: grid; gap: 8px; }\n div[setup] p { margin: 0; line-height: 1.5; }\n div[setup] select, div[setup] button { font: inherit; padding: 8px; min-width: 0; }\n /* Which is which: side by side means nothing without a legend. */\n div[legend] {\n position: absolute;\n top: 8px;\n left: 8px;\n display: flex;\n gap: 6px;\n padding: 4px 8px;\n color: rgba(255, 255, 255, 0.9);\n background-color: rgba(28, 28, 30, 0.6);\n border-radius: 6px;\n font-size: 0.72rem;\n flex-wrap: wrap;\n max-width: calc(100% - 16px);\n }\n /* One figure or eight: two words in the legend, the one shown pressed. */\n div[legend] button[way] {\n padding: 0 6px;\n font: inherit;\n color: inherit;\n background: none;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n opacity: 0.7;\n }\n div[legend] button[way][aria-pressed='true'] {\n opacity: 1;\n background-color: rgba(255, 255, 255, 0.18);\n }\n /* The one figure's size: under the legend, in the controls' look. */\n div[sizes] {\n position: absolute;\n top: 40px;\n left: 8px;\n display: grid;\n gap: 6px;\n padding: 8px 10px;\n max-width: calc(100% - 16px);\n box-sizing: border-box;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(28, 28, 30, 0.72);\n border-radius: 8px;\n font-size: 0.72rem;\n }\n div[sizes] label {\n display: grid;\n grid-template-columns: 32px 160px 52px;\n align-items: center;\n gap: 8px;\n }\n div[sizes] input {\n width: 100%;\n accent-color: var(--md-sys-color-primary);\n }\n div[sizes] output {\n font-variant-numeric: tabular-nums;\n text-align: right;\n }\n div[sizes] p[said] {\n margin: 0;\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n }\n /* A variant drawn at another size than it asks: the figure does not declare that size. */\n div[legend] small[clamp] {\n margin-left: 4px;\n color: #ffcc80;\n }\n div[clips] {\n margin-left: auto;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(28, 28, 30, 0.72);\n border-radius: 8px;\n font-size: 0.72rem;\n }\n div[clips] label {\n display: grid;\n grid-template-columns: auto 20px 96px 34px;\n align-items: center;\n gap: 8px;\n }\n /* A motion group's clip: as wide as the dial, slider and value together, so its name reads. */\n div[clips] select {\n grid-column: 2 / -1;\n min-width: 0;\n font: inherit;\n color: inherit;\n background-color: rgba(255, 255, 255, 0.14);\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: 5px;\n }\n div[clips] svg[dial] {\n width: 20px;\n height: 20px;\n }\n div[clips] svg[dial] path {\n fill: rgba(255, 255, 255, 0.22);\n }\n div[clips] svg[dial] line {\n stroke: var(--md-sys-color-primary, #7cc6ff);\n stroke-width: 2;\n stroke-linecap: round;\n }\n div[clips] svg[dial] circle {\n fill: none;\n stroke: rgba(255, 255, 255, 0.35);\n }\n div[clips] button[pose] {\n align-self: stretch;\n padding: 4px 8px;\n font: inherit;\n font-size: 0.7rem;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(255, 255, 255, 0.14);\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: 5px;\n cursor: pointer;\n }\n div[clips] button[pose]:hover {\n background-color: rgba(255, 255, 255, 0.24);\n }\n div[clips] span[drive] {\n opacity: 0.6;\n font-size: 0.66rem;\n }\n div[clips] input {\n width: 96px;\n accent-color: var(--md-sys-color-primary);\n }\n div[clips] output {\n font-variant-numeric: tabular-nums;\n text-align: right;\n opacity: 0.85;\n }\n div[cannot] {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n text-align: center;\n line-height: 1.6;\n color: #f4f4f4;\n background-color: ${unsafeCSS(WORKBENCH)};\n }\n `\n\n /** The figure being authored, as the editor holds it now. */\n @property({ attribute: false }) revision?: FigureRevision\n private get asset(): FigureAsset | undefined {\n return this.revision?.asset\n }\n /** The size the figure was made at, once per version: without a declared size it is measured by evaluating the figure. */\n private authoredSize(): { x: number; y: number; z: number } {\n const shown = this.revision!\n return derivedOf(shown, 'authored-size', () => authoredSizeOf(shown.asset))\n }\n /** Which figure: the type it is registered under here is this one's own, never a real type a board uses. */\n @property() context = ''\n /**\n * In front. Once made, the preview stays standing behind the edit canvas, so coming back to it is not a new 3D scene\n * (about 300 ms): out of sight it does not follow the figure, and when it is shown again it makes its content again\n * inside the scene that stands if the figure changed meanwhile.\n */\n @property({ type: Boolean }) active = true\n private stale = false\n @property({ attribute: false }) view: ViewSettings = DEFAULT_VIEW\n\n @query('div[stage]') private stage!: HTMLDivElement\n @state() private cannot = ''\n /** Clip speeds and control values given here -- the preview's, not the figure's. */\n @state() private driving: Record<string, number> = {}\n @state() private giving: Record<string, number> = {}\n /** Per motion group, the clip played here and how fast (ADR-0051 §3.2) -- the preview's, as a board's \\`motion\\`. */\n @state() private moving: Record<string, { clip: string; speed: number }> = {}\n /** Data values given here by accessor, as a board's saved `figureValues`: what moves a group whose clip or speed the data writes. */\n @state() private valuing: Record<string, FigureDataValue> = {}\n /** Data tried in the data tab: the preview's figures take it as their \\`data\\`, as a board's data source gives it; null when none is. */\n @property({ attribute: false }) sample: unknown = null\n /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */\n @property() selected = ''\n @state() private setupPart = ''\n /** One figure stretched by sliders, or the eight ways of growing side by side. */\n @state() private way: 'one' | 'eight' = 'one'\n /** The size the one figure is tried at; the authored size when the figure stands up. */\n @state() private trying?: { x: number; y: number; z: number }\n private board = { width: 0, height: 0 }\n\n /** What is being tried here now -- clip speeds and control values -- for the AI dock to hear with a request; none when nothing is. */\n tried(): { clips?: Record<string, number>; controls?: Record<string, number> } | undefined {\n const clips = Object.keys(this.driving).length ? { ...this.driving } : undefined\n const controls = Object.keys(this.giving).length ? { ...this.giving } : undefined\n return clips || controls ? { ...(clips ? { clips } : {}), ...(controls ? { controls } : {}) } : undefined\n }\n\n private scene?: SceneHandle\n private standing?: string\n private registered?: string\n private watcher?: ResizeObserver\n\n render() {\n const asset = this.asset\n const controls = asset ? asset.document.model.inputs.filter(i => i.role === 'state') : []\n /* Clips in a motion group are played by the group's choice of clip, not each by its own speed. */\n const grouped = new Set((asset?.motionGroups ?? []).flatMap(g => g.clips))\n const clips = asset ? [...new Set((asset.drivers ?? []).map(d => d.clip))].filter(c => !grouped.has(c)) : []\n let clampsByWay: ReturnType<typeof previewClampsOf> = {}\n try {\n clampsByWay = asset ? previewClampsOf(asset, this.authoredSize()) : {}\n } catch {\n clampsByWay = {}\n }\n /* The part the help speaks of: the one picked in it, else the one chosen in the editor, else the first. */\n const setupPart = setupPartOf(this.setupPart, this.selected, asset?.appearance.map(p => p.target) ?? [])\n return html`<div stage></div>\n ${this.cannot\n ? html`<div cannot>${this.cannot}</div>`\n : html`<div legend>\n ${(['one', 'eight'] as const).map(\n way => html`<button way=${way} aria-pressed=${String(this.way === way)} @click=${() => this.chooseWay(way)}>${i18next.t(`figure.label.preview-way-${way}`)}</button>`\n )}\n ${this.way === 'one' ? nothing : PREVIEW_WAYS.map(way => {\n const clamps = clampsByWay[way.name] ?? []\n return html`<span>\n ${way.axes.length ? way.name : i18next.t('figure.label.preview-base')}\n ${clamps.length\n ? html`<small clamp>${clamps.map(c => i18next.t('figure.text.preview-clamped', { axis: c.axis.toUpperCase(), asked: c.asked.toLocaleString(), used: c.used.toLocaleString() })).join(' · ')}</small>`\n : nothing}\n </span>`\n })}\n </div>\n ${this.way === 'one' && asset && this.trying ? this.renderSizes(asset, this.trying) : nothing}\n <div foot>${asset?.appearance.length ? html`<div setup>\n <p>${i18next.t('figure.text.preview-setup')}</p>\n <select aria-label=${i18next.t('figure.label.resize-part')} @change=${(e: Event) => this.setupPart = (e.target as HTMLSelectElement).value}>\n ${asset.appearance.map(p => html`<option value=${p.target} ?selected=${p.target === setupPart}>${p.target}</option>`)}\n </select>\n <button @click=${() => this.dispatchEvent(new CustomEvent('configure-size', { detail: { part: setupPart }, bubbles: true, composed: true }))}>${i18next.t('figure.button.configure-size')}</button>\n </div>` : nothing}\n ${controls.length + clips.length + (asset?.motionGroups?.length ?? 0) + (asset ? valueRowsOf(asset).length : 0) ? this.renderDrivers(asset!, controls, clips) : nothing}</div>`}`\n }\n\n /** The one figure's size: a slider per axis the figure lets change, and what it is at the size tried. */\n private renderSizes(asset: FigureAsset, size: { x: number; y: number; z: number }) {\n const say = (key: string, params?: Record<string, string>) => i18next.t(key, params)\n /* A board sizes a figure by the space it takes: with none declared, its box folds and no size can be tried. */\n if (!asset.occupancy) return html`<div sizes><p said>${i18next.t('figure.text.preview-needs-space')}</p></div>`\n return html`<div sizes>\n ${(['x', 'y', 'z'] as const).map(axis => {\n const range = sizeRangeOf(asset, axis)\n if (!range) return nothing\n return html`<label>\n <span>${i18next.t(`figure.label.measure-${axis}`)}</span>\n <input type=\"range\" aria-label=${i18next.t(`figure.label.measure-${axis}`)} min=${range.min} max=${range.max} step=\"any\" .value=${String(size[axis])}\n @input=${(e: Event) => this.tryAxis(axis, (e.target as HTMLInputElement).valueAsNumber)}\n @change=${() => this.fitView()} />\n <output>${Math.round(size[axis]).toLocaleString()}</output>\n </label>`\n })}\n <p said>${previewSizeWordsOf(asset, size, say)}</p>\n </div>`\n }\n\n private chooseWay(way: 'one' | 'eight') {\n if (way === this.way) return\n this.way = way\n this.standUp()\n }\n\n /** The one figure at another size on an axis, held to what the figure declares, its centre kept on the board's. */\n private tryAxis(axis: 'x' | 'y' | 'z', value: number) {\n const asset = this.asset\n const used = asset ? sizeWithinRange(asset, axis, value) : undefined\n if (used === undefined || !this.trying) return\n this.trying = { ...this.trying, [axis]: used }\n const instance = this.scene?.root?.components?.[0] as { model?: { width?: number; height?: number }; set(props: Record<string, unknown>): void } | undefined\n instance?.set(axis === 'x' ? { width: used } : axis === 'y' ? { depth: used } : { height: used })\n const { width = 0, height = 0 } = instance?.model ?? {}\n instance?.set({ left: this.board.width / 2 - width / 2, top: this.board.height / 2 - height / 2 })\n }\n\n /** The figure as stretched, whole in view: done once the slider is let go, so the ground does not slide under a drag. */\n private fitView() {\n this.scene?.fit?.('ratio')\n this.scene?.root?.fitCameraToModel?.()\n }\n\n private renderDrivers(asset: FigureAsset, controls: FigureAsset['document']['model']['inputs'], clips: string[]) {\n const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id])\n return html`<div clips>\n ${changed.length\n ? html`<button pose title=${i18next.t('figure.text.pose-as-default-explained')} @click=${() => this.poseAsDefault()}>\n ${i18next.t('figure.button.pose-as-default')}\n </button>`\n : nothing}\n ${controls.map(control => {\n const value = this.giving[control.id] ?? asset.stateDefaults[control.id] ?? control.min\n const step = Math.max((control.max - control.min) / 100, 0.01)\n const label = asset.stateInputs?.[control.id]?.label ?? control.id\n return html`<label>\n <span>${label} <span drive>${control.unit}</span></span>\n ${this.renderDial(control, value)}\n <input\n type=\"range\"\n min=${control.min}\n max=${control.max}\n step=${step}\n .value=${String(value)}\n @input=${(e: Event) => this.give(control.id, Number((e.target as HTMLInputElement).value))}\n />\n <output>${Math.round(value)}</output>\n </label>`\n })}\n ${clips.map(clip => {\n /*\n A clip nothing has driven stands still, here as on a board: the instances get a speed only from `drive`. The\n slider showed 1.00 over rollers that did not turn (2026-10-03). It shows what plays -- 0 until moved.\n */\n const value = this.driving[clip] ?? 0\n return html`<label>\n <span>${clip} <span drive>${i18next.t('figure.label.playback-speed')}</span></span>\n <span></span>\n <input type=\"range\" min=\"0\" max=\"3\" step=\"0.05\" .value=${String(value)} @input=${(e: Event) => this.drive(clip, Number((e.target as HTMLInputElement).value))} />\n <output>${value.toFixed(2)}</output>\n </label>`\n })}\n ${valueRowsOf(asset).map(row => this.renderDataValue(row))}\n ${(asset.motionGroups ?? []).map(group => {\n const playing = this.moving[group.id] ?? { clip: group.defaultClip, speed: 0 }\n /* A field the data writes wins over the group's own setting, here as on a board: it is moved by the data values above. */\n const byData = motionWrittenByData(asset)[group.id] ?? { clip: false, speed: false }\n return html`${byData.clip\n ? nothing\n : html`<label>\n <span>${group.label} <span drive>${i18next.t('figure.label.clip')}</span></span>\n <select aria-label=${group.label} @change=${(e: Event) => this.move(group.id, { ...playing, clip: (e.target as HTMLSelectElement).value })}>\n ${group.clips.map(c => html`<option value=${c} ?selected=${c === playing.clip}>${c}</option>`)}\n </select>\n </label>`}\n ${byData.speed\n ? nothing\n : html`<label>\n <span>${group.label} <span drive>${i18next.t('figure.label.playback-speed')}</span></span>\n <span></span>\n <input type=\"range\" min=\"0\" max=\"3\" step=\"0.05\" .value=${String(playing.speed)} @input=${(e: Event) => this.move(group.id, { ...playing, speed: Number((e.target as HTMLInputElement).value) })} />\n <output>${playing.speed.toFixed(2)}</output>\n </label>`}`\n })}\n </div>`\n }\n\n /** A value the data's bindings read, as the board's saved values give it: a number on a slider when it has a range, a choice when it is closed. */\n private renderDataValue(row: FigureDataRow) {\n const node = row.node\n const label = row.label ?? row.path\n const fallback = 'default' in node ? node.default : undefined\n const value = this.valuing[row.path] ?? fallback\n if (node.kind === 'number') {\n const ranged = node.min !== undefined && node.max !== undefined && node.max > node.min\n return html`<label>\n <span>${label} <span drive>${node.unit ?? ''}</span></span>\n <span></span>\n ${ranged\n ? html`<input type=\"range\" min=${node.min!} max=${node.max!} step=${Math.max((node.max! - node.min!) / 100, 0.01)} .value=${String(value ?? node.min)} @input=${(e: Event) => this.value(row, (e.target as HTMLInputElement).value)} />\n <output>${value === undefined ? '' : Number(value).toFixed(2)}</output>`\n : html`<input type=\"number\" step=\"any\" .value=${value === undefined ? '' : String(value)} @change=${(e: Event) => this.value(row, (e.target as HTMLInputElement).value)} />\n <span></span>`}\n </label>`\n }\n const domain = domainOf(node)\n if (!domain.length)\n return html`<label>\n <span>${label}</span>\n <input aria-label=${label} .value=${value === undefined ? '' : String(value)} @change=${(e: Event) => this.value(row, (e.target as HTMLInputElement).value)} />\n </label>`\n return html`<label>\n <span>${label}</span>\n <select aria-label=${label} @change=${(e: Event) => this.value(row, (e.target as HTMLSelectElement).value)}>\n ${domain.map(v => html`<option value=${String(v)} ?selected=${v === value}>${typeof v === 'boolean' ? i18next.t(v ? 'figure.label.data-true' : 'figure.label.data-false') : v}</option>`)}\n </select>\n </label>`\n }\n\n private value(row: FigureDataRow, typed: string) {\n const value = typedValueOf(row.node, typed)\n const next = { ...this.valuing }\n if (typeof value === 'number' && Number.isNaN(value)) delete next[row.path]\n else next[row.path] = value\n this.valuing = next\n this.tellInstances('figureValues', this.valuing)\n }\n\n private move(group: string, playing: { clip: string; speed: number }) {\n this.moving = { ...this.moving, [group]: playing }\n this.tellInstances('motion', Object.fromEntries(Object.entries(this.moving).map(([id, m]) => [id, { ...m, paused: false }])))\n }\n\n private renderDial(control: { unit: string; min: number; max: number }, value: number) {\n if (control.unit !== 'deg') return html`<span></span>`\n const dial = dialShape(control.min, control.max, value)\n const span = dial.radius * 2 + 2\n return html`<svg dial viewBox=\"0 0 ${span} ${span}\" aria-hidden=\"true\">\n <path d=${dial.sector}></path>\n <circle cx=${dial.centre.x} cy=${dial.centre.y} r=${dial.radius}></circle>\n <line x1=${dial.centre.x} y1=${dial.centre.y} x2=${dial.needle.x} y2=${dial.needle.y}></line>\n </svg>`\n }\n\n private drive(clip: string, value: number) {\n this.driving = { ...this.driving, [clip]: value }\n this.tellInstances('clips', this.driving)\n }\n\n private give(id: string, value: number) {\n this.giving = { ...this.giving, [id]: value }\n this.tellInstances('parameters', this.giving)\n }\n\n private poseAsDefault() {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit: { kind: 'set-state-defaults', values: { ...this.giving } } }, bubbles: true, composed: true }))\n this.giving = {}\n }\n\n private tellInstances(key: 'clips' | 'parameters' | 'motion' | 'figureValues', values: Record<string, unknown> | null) {\n for (const component of this.scene?.root?.components ?? []) component.set?.(key, values === null ? null : { ...values })\n }\n\n /** The tried data to the figures standing now, as their \\`data\\`: the preview's board is not saved. */\n private tellData(data: unknown) {\n for (const component of this.scene?.root?.components ?? []) component.set?.('data', data === null || data === undefined ? null : structuredClone(data))\n }\n\n\n /** The data sample to the figures standing now. */\n private tellSample() {\n if (this.sample !== null && this.sample !== undefined) this.tellData(this.sample)\n }\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('sample') && this.scene) this.tellData(this.sample)\n if (!this.hasUpdated) return\n if (!this.active) {\n this.stale = true\n return\n }\n if (this.stale || changed.has('active')) {\n this.stale = false\n this.standUp()\n return\n }\n if (changed.has('revision') || changed.has('context')) {\n this.standUp()\n return\n }\n if (!changed.has('view')) return\n const before = changed.get('view') as ViewSettings | undefined\n const now = this.view ?? DEFAULT_VIEW\n if (before?.placement !== now.placement || before?.ceilingHeight !== now.ceilingHeight) this.standUp()\n else this.scene?.root?.set?.(sceneLook(now))\n }\n\n firstUpdated() {\n this.standUp()\n this.watcher = new ResizeObserver(() => {\n if (this.active) this.scene?.resize?.()\n })\n this.watcher.observe(this)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.watcher?.disconnect()\n this.watcher = undefined\n this.teardown()\n }\n\n private standUp() {\n const asset = this.asset\n const view = this.view ?? DEFAULT_VIEW\n if (!asset || !asset.appearance.length) {\n this.cannot = i18next.t('figure.text.preview-needs-a-part')\n this.teardown()\n return\n }\n const signature = JSON.stringify([asset, view.placement, view.ceilingHeight, this.way])\n if (this.scene && this.standing === signature) return\n try {\n compileFigureAsset(asset)\n } catch {\n this.cannot = i18next.t('figure.text.preview-blocked-by-errors')\n this.teardown()\n return\n }\n try {\n /* Registered as a board registers it, under a type of this preview's own: a real type's drawings stay as they are. */\n const type = previewTypeOf(this.context)\n const size = this.authoredSize()\n /*\n The figure changed under a scene that is standing: the same type is registered again and the content made again\n inside the same 3D scene. Making a scene anew costs about a third of a second and its first drawing compiles the\n shaders again -- a value typed beside the preview held the screen for seconds. The scene is made anew only when\n there is none, for another figure, or when the content cannot be made again in place.\n */\n const again = this.scene?.rootContainer?.rebuildContentModel && this.registered === type\n if (!again) this.teardown()\n this.driving = {}\n this.moving = {}\n this.valuing = {}\n registerFigure(type, asset)\n this.registered = type\n const { components, board } = this.way === 'one' ? previewOneInstance(type, size) : previewInstances(type, size)\n this.board = board\n this.trying = this.way === 'one' ? { ...size } : undefined\n const model = {\n type: 'model-layer',\n width: board.width,\n height: board.height,\n /* As tall as the figure may grow: twice for the eight, the declared height range's top for the one. */\n depth: previewBoardDepth({ placement: asset.placement, base: { y: size.y } }, view.ceilingHeight, this.way === 'one' ? Math.max(1, (sizeRangeOf(asset, 'y')?.max ?? size.y) / size.y) : PREVIEW_FACTOR),\n threed: true,\n sky: 'color',\n ...sceneLook(view),\n placement: view.placement,\n heights: { floor: 0, ceiling: view.ceilingHeight },\n pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),\n components\n }\n if (again) {\n try {\n this.scene!.rootContainer!.rebuildContentModel!(model, { preserveThreeD: true })\n this.standing = signature\n this.cannot = ''\n this.tellSample()\n return\n } catch {\n this.teardown()\n registerFigure(type, asset)\n this.registered = type\n }\n }\n this.scene = create({\n target: this.stage,\n model,\n mode: 0\n }) as SceneHandle\n this.standing = signature\n this.scene.resize?.()\n this.scene.fit?.('ratio')\n this.scene.root?.fitCameraToModel?.()\n this.cannot = ''\n this.tellSample()\n } catch (error) {\n this.cannot = (error as Error).message\n }\n }\n\n private teardown() {\n this.scene?.dispose?.()\n this.scene = undefined\n this.standing = undefined\n if (this.registered) unregisterFigure(this.registered)\n this.registered = undefined\n }\n}\n"]}
@@ -148,7 +148,6 @@ let FigureProperties = class FigureProperties extends localize(i18next)(LitEleme
148
148
  >
149
149
  ${PLACEMENTS.map(one => html `<option value=${one} ?selected=${one === current}>${i18next.t('figure.label.placement-' + one)}</option>`)}
150
150
  </select>
151
- <small>${i18next.t('figure.text.placement-is-intent')}</small>
152
151
  </div>
153
152
  `;
154
153
  }
@@ -184,7 +183,7 @@ let FigureProperties = class FigureProperties extends localize(i18next)(LitEleme
184
183
  <button act ?disabled=${!facts.declared || !part} @click=${() => this.command({ kind: 'set-mounting-face', part: part, face })}>
185
184
  <md-icon>check</md-icon>${i18next.t('figure.button.set-mounting-face')}
186
185
  </button>
187
- <button act ?disabled=${!mounting} @click=${() => this.command({ kind: 'clear-mounting-face' })}>
186
+ <button quiet ?disabled=${!mounting} @click=${() => this.command({ kind: 'clear-mounting-face' })}>
188
187
  <md-icon>close</md-icon>${i18next.t('figure.button.clear-mounting-face')}
189
188
  </button>
190
189
  </div>