@things-factory/figure-ui 10.1.170 → 10.1.172

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.
@@ -1358,7 +1358,7 @@ export class FigureEditor extends LitElement {
1358
1358
  return version > 0 ? i18next.t('figure.text.release-state-draft-over', { version: String(version) }) : i18next.t('figure.text.release-state-draft-new')
1359
1359
  }
1360
1360
  /** The findings that block release, under the bar beside 「발행」, each with the way out the verdict tab offers for it. */
1361
- private renderReleaseBlockers(asset: FigureAsset | undefined, unavailable: boolean) {
1361
+ private renderReleaseBlockers(blockers: FigureGateFinding[], asset: FigureAsset | undefined, unavailable: boolean) {
1362
1362
  const parts = (asset?.appearance ?? []).map(look => look.target)
1363
1363
  const way = (code: string) =>
1364
1364
  (code === 'occupancy-undeclared' || code === 'part-outside-occupancy') && asset && parts.length
@@ -1369,7 +1369,7 @@ export class FigureEditor extends LitElement {
1369
1369
  return html`<div release-blockers role="dialog" aria-label=${i18next.t('figure.text.must-fix-to-release')} @keydown=${(e: KeyboardEvent) => e.key === 'Escape' && (this.blockersOpen = false)}>
1370
1370
  <ul gate>
1371
1371
  <li head>${i18next.t('figure.text.must-fix-to-release')}</li>
1372
- ${this.releaseBlockers.map(finding => renderGateFinding(finding, true, way(finding.code), parts))}
1372
+ ${blockers.map(finding => renderGateFinding(finding, true, way(finding.code), parts))}
1373
1373
  </ul>
1374
1374
  </div>`
1375
1375
  }
@@ -1659,6 +1659,12 @@ export class FigureEditor extends LitElement {
1659
1659
  : view.dirty || !view.id
1660
1660
  ? 'figure.text.unsaved-changes'
1661
1661
  : 'figure.text.saved'
1662
+ /*
1663
+ A figure with no parts yet is not one that failed: what blocks its release is said once it has something to
1664
+ release (first 15 minutes, 2026-10-16 evening -- red from the first click read as a failure). The verdict tab
1665
+ still says it.
1666
+ */
1667
+ const blockers = view.geometry.length ? this.releaseBlockers : []
1662
1668
  const tab = this.tab
1663
1669
  const live = this.liveParts(shown)
1664
1670
  return html`<ox-canvas-bar
@@ -1683,15 +1689,15 @@ export class FigureEditor extends LitElement {
1683
1689
  <span slot="status" class="status" role="status" ?dirty=${status === 'figure.text.unsaved-changes'}>${i18next.t(status)}</span>
1684
1690
  <span slot="status" release-state>${this.releaseWords(view)}</span>
1685
1691
  ${this.renderOverlayButton('history', 'history', 'figure.label.history', 'commit')}
1686
- ${this.releaseBlockers.length
1692
+ ${blockers.length
1687
1693
  ? html`<button slot="commit" release-blocked aria-expanded=${String(this.blockersOpen)} @click=${() => (this.blockersOpen = !this.blockersOpen)}>
1688
- <md-icon>error</md-icon>${i18next.t('figure.button.release-blocked', { count: this.releaseBlockers.length })}
1694
+ <md-icon>error</md-icon>${i18next.t('figure.button.release-blocked', { count: blockers.length })}
1689
1695
  </button>`
1690
1696
  : nothing}
1691
1697
  <button slot="commit" publish ?disabled=${!view.id} title=${view.id ? '' : i18next.t('figure.text.save-the-figure-before-releasing')} @click=${this.goRelease}>${i18next.t('figure.button.release')}</button>
1692
1698
  <button slot="commit" save ?disabled=${unavailable || (!view.dirty && !!view.id)} @click=${this.save}>${i18next.t('figure.button.save')}</button>
1693
1699
  </ox-canvas-bar>
1694
- ${this.blockersOpen && this.releaseBlockers.length ? this.renderReleaseBlockers(asset, unavailable) : nothing}
1700
+ ${this.blockersOpen && blockers.length ? this.renderReleaseBlockers(blockers, asset, unavailable) : nothing}
1695
1701
  ${this.catalogOpen
1696
1702
  ? html`<figure-recipe-catalog
1697
1703
  .templates=${this.templates}
@@ -10,7 +10,7 @@ import { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH, type ViewSetting
10
10
  import { dialShape } from './angle-dial.js'
11
11
  import { domainOf, motionWrittenByData, typedValueOf, valueRowsOf } from './data-interface.js'
12
12
  import { derivedOf, type FigureRevision } from './revision.js'
13
- import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, previewOneInstance, previewTypeOf, setupPartOf, sizeRangeOf, sizeWithinRange } from '@things-factory/figure-viewer/dist-client/preview-model.js'
13
+ import { authoredSizeOf, occupancyAtSize, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, previewOneInstance, previewTypeOf, setupPartOf, sizeRangeOf, sizeWithinRange } from '@things-factory/figure-viewer/dist-client/preview-model.js'
14
14
  import { previewSizeWordsOf } from './preview-size.js'
15
15
 
16
16
  /**
@@ -353,6 +353,8 @@ export class FigurePreview extends localize(i18next)(LitElement) {
353
353
  if (way === this.way) return
354
354
  this.way = way
355
355
  this.standUp()
356
+ /* The content is made again inside the scene standing, which keeps the camera: from one figure to eight it stood inside them. */
357
+ requestAnimationFrame(() => this.fitView())
356
358
  }
357
359
 
358
360
  /** The one figure at another size on an axis, held to what the figure declares, its centre kept on the board's. */
@@ -362,7 +364,9 @@ export class FigurePreview extends localize(i18next)(LitElement) {
362
364
  if (used === undefined || !this.trying) return
363
365
  this.trying = { ...this.trying, [axis]: used }
364
366
  const instance = this.scene?.root?.components?.[0] as { model?: { width?: number; height?: number }; set(props: Record<string, unknown>): void } | undefined
365
- instance?.set(axis === 'x' ? { width: used } : axis === 'y' ? { depth: used } : { height: used })
367
+ /* The instance's box is the space the figure takes at the sizes tried, not the sizes (occupancyAtSize). */
368
+ const box = occupancyAtSize(asset!, this.trying) ?? this.trying
369
+ instance?.set({ width: box.x, depth: box.y, height: box.z })
366
370
  const { width = 0, height = 0 } = instance?.model ?? {}
367
371
  instance?.set({ left: this.board.width / 2 - width / 2, top: this.board.height / 2 - height / 2 })
368
372
  }
@@ -596,7 +600,9 @@ export class FigurePreview extends localize(i18next)(LitElement) {
596
600
  this.valuing = {}
597
601
  registerFigure(type, asset)
598
602
  this.registered = type
599
- const { components, board } = this.way === 'one' ? previewOneInstance(type, size) : previewInstances(type, size)
603
+ /* Each instance stands in the space the figure takes at the size it is asked (occupancyAtSize), as a board reads it. */
604
+ const boxOf = (asked: { x: number; y: number; z: number }) => occupancyAtSize(asset, asked) ?? asked
605
+ const { components, board } = this.way === 'one' ? previewOneInstance(type, boxOf(size)) : previewInstances(type, size, boxOf)
600
606
  this.board = board
601
607
  this.trying = this.way === 'one' ? { ...size } : undefined
602
608
  const model = {
@@ -1369,7 +1369,7 @@ let FigureEditor = class FigureEditor extends LitElement {
1369
1369
  return version > 0 ? i18next.t('figure.text.release-state-draft-over', { version: String(version) }) : i18next.t('figure.text.release-state-draft-new');
1370
1370
  }
1371
1371
  /** The findings that block release, under the bar beside 「발행」, each with the way out the verdict tab offers for it. */
1372
- renderReleaseBlockers(asset, unavailable) {
1372
+ renderReleaseBlockers(blockers, asset, unavailable) {
1373
1373
  const parts = (asset?.appearance ?? []).map(look => look.target);
1374
1374
  const way = (code) => (code === 'occupancy-undeclared' || code === 'part-outside-occupancy') && asset && parts.length
1375
1375
  ? html `<button act ?disabled=${unavailable} @click=${() => this.linkCommand(occupancyFromParts(asset))}>
@@ -1379,7 +1379,7 @@ let FigureEditor = class FigureEditor extends LitElement {
1379
1379
  return html `<div release-blockers role="dialog" aria-label=${i18next.t('figure.text.must-fix-to-release')} @keydown=${(e) => e.key === 'Escape' && (this.blockersOpen = false)}>
1380
1380
  <ul gate>
1381
1381
  <li head>${i18next.t('figure.text.must-fix-to-release')}</li>
1382
- ${this.releaseBlockers.map(finding => renderGateFinding(finding, true, way(finding.code), parts))}
1382
+ ${blockers.map(finding => renderGateFinding(finding, true, way(finding.code), parts))}
1383
1383
  </ul>
1384
1384
  </div>`;
1385
1385
  }
@@ -1679,6 +1679,12 @@ let FigureEditor = class FigureEditor extends LitElement {
1679
1679
  : view.dirty || !view.id
1680
1680
  ? 'figure.text.unsaved-changes'
1681
1681
  : 'figure.text.saved';
1682
+ /*
1683
+ A figure with no parts yet is not one that failed: what blocks its release is said once it has something to
1684
+ release (first 15 minutes, 2026-10-16 evening -- red from the first click read as a failure). The verdict tab
1685
+ still says it.
1686
+ */
1687
+ const blockers = view.geometry.length ? this.releaseBlockers : [];
1682
1688
  const tab = this.tab;
1683
1689
  const live = this.liveParts(shown);
1684
1690
  return html `<ox-canvas-bar
@@ -1701,15 +1707,15 @@ let FigureEditor = class FigureEditor extends LitElement {
1701
1707
  <span slot="status" class="status" role="status" ?dirty=${status === 'figure.text.unsaved-changes'}>${i18next.t(status)}</span>
1702
1708
  <span slot="status" release-state>${this.releaseWords(view)}</span>
1703
1709
  ${this.renderOverlayButton('history', 'history', 'figure.label.history', 'commit')}
1704
- ${this.releaseBlockers.length
1710
+ ${blockers.length
1705
1711
  ? html `<button slot="commit" release-blocked aria-expanded=${String(this.blockersOpen)} @click=${() => (this.blockersOpen = !this.blockersOpen)}>
1706
- <md-icon>error</md-icon>${i18next.t('figure.button.release-blocked', { count: this.releaseBlockers.length })}
1712
+ <md-icon>error</md-icon>${i18next.t('figure.button.release-blocked', { count: blockers.length })}
1707
1713
  </button>`
1708
1714
  : nothing}
1709
1715
  <button slot="commit" publish ?disabled=${!view.id} title=${view.id ? '' : i18next.t('figure.text.save-the-figure-before-releasing')} @click=${this.goRelease}>${i18next.t('figure.button.release')}</button>
1710
1716
  <button slot="commit" save ?disabled=${unavailable || (!view.dirty && !!view.id)} @click=${this.save}>${i18next.t('figure.button.save')}</button>
1711
1717
  </ox-canvas-bar>
1712
- ${this.blockersOpen && this.releaseBlockers.length ? this.renderReleaseBlockers(asset, unavailable) : nothing}
1718
+ ${this.blockersOpen && blockers.length ? this.renderReleaseBlockers(blockers, asset, unavailable) : nothing}
1713
1719
  ${this.catalogOpen
1714
1720
  ? html `<figure-recipe-catalog
1715
1721
  .templates=${this.templates}