@things-factory/figure-ui 10.1.205 → 10.1.207
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/modeller/editor-styles.ts +16 -0
- package/client/modeller/editor.ts +53 -137
- package/client/modeller/fasten-form.ts +50 -11
- package/client/modeller/figure-facts.ts +1 -1
- package/client/modeller/figure-fasten-dialog.ts +3 -2
- package/client/modeller/figure-history-panel.ts +1 -29
- package/client/modeller/figure-links.ts +2 -2
- package/client/modeller/figure-preview.ts +10 -12
- package/client/modeller/figure-verdict.ts +2 -17
- package/client/modeller/gizmo.ts +10 -0
- package/client/modeller/keys.ts +10 -1
- package/client/modeller/links.ts +32 -41
- package/client/modeller/maturity.ts +11 -27
- package/client/modeller/palette.ts +1 -1
- package/client/modeller/product-session.ts +16 -4
- package/client/modeller/refusal-teller.ts +3 -25
- package/client/modeller/refusals.ts +20 -33
- package/client/pages/figure-modeller-page.ts +7 -22
- package/dist-client/modeller/editor-styles.js +16 -0
- package/dist-client/modeller/editor-styles.js.map +1 -1
- package/dist-client/modeller/editor.d.ts +8 -11
- package/dist-client/modeller/editor.js +57 -138
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/fasten-form.d.ts +16 -7
- package/dist-client/modeller/fasten-form.js +45 -11
- package/dist-client/modeller/fasten-form.js.map +1 -1
- package/dist-client/modeller/figure-facts.js +1 -1
- package/dist-client/modeller/figure-facts.js.map +1 -1
- package/dist-client/modeller/figure-fasten-dialog.js +3 -3
- package/dist-client/modeller/figure-fasten-dialog.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +0 -16
- package/dist-client/modeller/figure-history-panel.js +1 -27
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-links.js +2 -2
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/figure-preview.js +8 -11
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-verdict.d.ts +0 -5
- package/dist-client/modeller/figure-verdict.js +2 -21
- package/dist-client/modeller/figure-verdict.js.map +1 -1
- package/dist-client/modeller/gizmo.d.ts +6 -0
- package/dist-client/modeller/gizmo.js +11 -0
- package/dist-client/modeller/gizmo.js.map +1 -1
- package/dist-client/modeller/keys.d.ts +1 -1
- package/dist-client/modeller/keys.js +6 -0
- package/dist-client/modeller/keys.js.map +1 -1
- package/dist-client/modeller/links.d.ts +16 -22
- package/dist-client/modeller/links.js +26 -39
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/modeller/maturity.d.ts +2 -4
- package/dist-client/modeller/maturity.js +12 -26
- package/dist-client/modeller/maturity.js.map +1 -1
- package/dist-client/modeller/palette.js +1 -1
- package/dist-client/modeller/palette.js.map +1 -1
- package/dist-client/modeller/product-session.d.ts +15 -13
- package/dist-client/modeller/product-session.js +15 -5
- package/dist-client/modeller/product-session.js.map +1 -1
- package/dist-client/modeller/refusal-teller.d.ts +1 -12
- package/dist-client/modeller/refusal-teller.js +3 -22
- package/dist-client/modeller/refusal-teller.js.map +1 -1
- package/dist-client/modeller/refusals.d.ts +7 -17
- package/dist-client/modeller/refusals.js +17 -34
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +1 -3
- package/dist-client/pages/figure-modeller-page.js +7 -23
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/test/browser/acceptance-page.js +4 -4
- package/test/browser/detail-page.js +1 -3
- package/test/browser/detail-tabs-page.js +1 -2
- package/test/browser/links.check.mjs +32 -50
- package/test/browser/modeller.check.mjs +46 -19
- package/test/browser/parts-page.js +2 -2
- package/test/browser/parts.check.mjs +51 -12
- package/test/browser/product-page.check.mjs +6 -9
- package/test/browser/product-page.js +1 -2
- package/test/browser/screen-page.js +2 -6
- package/test/browser/screen.check.mjs +8 -65
- package/test/browser/section.check.mjs +27 -0
- package/test/fasten-form.test.ts +42 -2
- package/test/first-minute.test.ts +2 -3
- package/test/fixtures/joint-column.json +1 -66
- package/test/fixtures/joint-roller.json +1 -66
- package/test/fixtures/robot-arm-6dof.json +2 -67
- package/test/gizmo.test.ts +12 -1
- package/test/keys.test.ts +11 -0
- package/test/links.test.ts +39 -34
- package/test/parts-wiring.test.ts +1 -1
- package/test/properties.test.ts +9 -12
- package/test/refusals.test.ts +20 -42
- package/test/resize-with-placement.test.ts +13 -15
- package/test/revision.test.ts +1 -2
- package/test/verdict.test.ts +1 -15
- package/translations/en.json +8 -32
- package/translations/es.json +8 -32
- package/translations/ja.json +8 -32
- package/translations/ko.json +8 -32
- package/translations/ms.json +8 -32
- package/translations/zh.json +8 -32
- package/client/modeller/occupancy.ts +0 -51
- package/dist-client/modeller/occupancy.d.ts +0 -31
- package/dist-client/modeller/occupancy.js +0 -36
- package/dist-client/modeller/occupancy.js.map +0 -1
- package/test/release-panel.test.ts +0 -53
|
@@ -3,14 +3,14 @@ import { customElement, property, query, state } from 'lit/decorators.js'
|
|
|
3
3
|
|
|
4
4
|
import { i18next, localize } from '@operato/i18n'
|
|
5
5
|
import { compileFigureAsset } from '@hatiolab/figure-model'
|
|
6
|
-
import type { FigureAsset, FigureDataRow, FigureDataValue } from '@hatiolab/figure-model'
|
|
6
|
+
import type { FigureAsset, FigureDataRow, FigureDataValue, FigureGraphStateInput } from '@hatiolab/figure-model'
|
|
7
7
|
import { create, registerFigure, unregisterFigure } from '@hatiolab/things-scene'
|
|
8
8
|
|
|
9
9
|
import { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH, type ViewSettings } from '@things-factory/figure-viewer/dist-client/figure-view.js'
|
|
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,
|
|
13
|
+
import { authoredSizeOf, bodyAtSize, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, previewOneInstance, previewTypeOf, setupPartOf, sizeSpanOf, sizeWithinRange } from '@things-factory/figure-viewer/dist-client/preview-model.js'
|
|
14
14
|
import { previewSizeWordsOf } from './preview-size.js'
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -331,11 +331,9 @@ export class FigurePreview extends localize(i18next)(LitElement) {
|
|
|
331
331
|
/** The one figure's size: a slider per axis the figure lets change, and what it is at the size tried. */
|
|
332
332
|
private renderSizes(asset: FigureAsset, size: { x: number; y: number; z: number }) {
|
|
333
333
|
const say = (key: string, params?: Record<string, string>) => i18next.t(key, params)
|
|
334
|
-
/* A board sizes a figure by the space it takes: with none declared, its box folds and no size can be tried. */
|
|
335
|
-
if (!asset.occupancy) return html`<div sizes><p said>${i18next.t('figure.text.preview-needs-space')}</p></div>`
|
|
336
334
|
return html`<div sizes>
|
|
337
335
|
${(['x', 'y', 'z'] as const).map(axis => {
|
|
338
|
-
const range =
|
|
336
|
+
const range = sizeSpanOf(asset, axis)
|
|
339
337
|
if (!range) return nothing
|
|
340
338
|
return html`<label>
|
|
341
339
|
<span>${i18next.t(`figure.label.measure-${axis}`)}</span>
|
|
@@ -364,8 +362,8 @@ export class FigurePreview extends localize(i18next)(LitElement) {
|
|
|
364
362
|
if (used === undefined || !this.trying) return
|
|
365
363
|
this.trying = { ...this.trying, [axis]: used }
|
|
366
364
|
const instance = this.scene?.root?.components?.[0] as { model?: { width?: number; height?: number }; set(props: Record<string, unknown>): void } | undefined
|
|
367
|
-
/* The instance's box is the
|
|
368
|
-
const box =
|
|
365
|
+
/* The instance's box is the body at the sizes tried, not the sizes (bodyAtSize). */
|
|
366
|
+
const box = bodyAtSize(asset!, this.trying) ?? this.trying
|
|
369
367
|
instance?.set({ width: box.x, depth: box.y, height: box.z })
|
|
370
368
|
const { width = 0, height = 0 } = instance?.model ?? {}
|
|
371
369
|
instance?.set({ left: this.board.width / 2 - width / 2, top: this.board.height / 2 - height / 2 })
|
|
@@ -377,7 +375,7 @@ export class FigurePreview extends localize(i18next)(LitElement) {
|
|
|
377
375
|
this.scene?.root?.fitCameraToModel?.()
|
|
378
376
|
}
|
|
379
377
|
|
|
380
|
-
private renderDrivers(asset: FigureAsset, controls:
|
|
378
|
+
private renderDrivers(asset: FigureAsset, controls: FigureGraphStateInput[], clips: string[]) {
|
|
381
379
|
const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id])
|
|
382
380
|
return html`<div clips>
|
|
383
381
|
${changed.length
|
|
@@ -600,8 +598,8 @@ export class FigurePreview extends localize(i18next)(LitElement) {
|
|
|
600
598
|
this.valuing = {}
|
|
601
599
|
registerFigure(type, asset)
|
|
602
600
|
this.registered = type
|
|
603
|
-
/* Each instance stands in
|
|
604
|
-
const boxOf = (asked: { x: number; y: number; z: number }) =>
|
|
601
|
+
/* Each instance stands in its body at the size it is asked (bodyAtSize), as a board reads it. */
|
|
602
|
+
const boxOf = (asked: { x: number; y: number; z: number }) => bodyAtSize(asset, asked) ?? asked
|
|
605
603
|
const { components, board } = this.way === 'one' ? previewOneInstance(type, boxOf(size)) : previewInstances(type, size, boxOf)
|
|
606
604
|
this.board = board
|
|
607
605
|
this.trying = this.way === 'one' ? { ...size } : undefined
|
|
@@ -609,8 +607,8 @@ export class FigurePreview extends localize(i18next)(LitElement) {
|
|
|
609
607
|
type: 'model-layer',
|
|
610
608
|
width: board.width,
|
|
611
609
|
height: board.height,
|
|
612
|
-
/* As tall as the figure may grow: twice for the eight, the
|
|
613
|
-
depth: previewBoardDepth({ placement: asset.placement, base: { y: size.y } }, view.ceilingHeight, this.way === 'one' ? Math.max(1, (
|
|
610
|
+
/* As tall as the figure may grow: twice for the eight, the top of the height slider for the one. */
|
|
611
|
+
depth: previewBoardDepth({ placement: asset.placement, base: { y: size.y } }, view.ceilingHeight, this.way === 'one' ? Math.max(1, (sizeSpanOf(asset, 'y')?.max ?? size.y) / size.y) : PREVIEW_FACTOR),
|
|
614
612
|
threed: true,
|
|
615
613
|
sky: 'color',
|
|
616
614
|
...sceneLook(view),
|
|
@@ -392,8 +392,6 @@ export class FigureVerdict extends localize(i18next)(LitElement) {
|
|
|
392
392
|
|
|
393
393
|
/** The report over the source being edited. Absent when nothing is drawn, or the format refuses the source. */
|
|
394
394
|
@state() private report?: FigureReport
|
|
395
|
-
/** Bodies in the asset, drawn or hidden. With none there is nothing to fit the space to, so the button is not offered. */
|
|
396
|
-
@state() private bodies = 0
|
|
397
395
|
/** Whether the trend chart is open at its own size. */
|
|
398
396
|
@state() private expanded = false
|
|
399
397
|
@state() private inspection?: FigureInspection
|
|
@@ -428,9 +426,8 @@ export class FigureVerdict extends localize(i18next)(LitElement) {
|
|
|
428
426
|
}
|
|
429
427
|
|
|
430
428
|
private measure() {
|
|
431
|
-
const { report,
|
|
429
|
+
const { report, told } = verdictMeasureOf(this.revision?.asset)
|
|
432
430
|
this.report = report
|
|
433
|
-
this.bodies = bodies
|
|
434
431
|
if (!report) this.expanded = false
|
|
435
432
|
this.dispatchEvent(new CustomEvent('capacity-changed', { detail: told }))
|
|
436
433
|
}
|
|
@@ -766,7 +763,7 @@ export class FigureVerdict extends localize(i18next)(LitElement) {
|
|
|
766
763
|
${blocking.length
|
|
767
764
|
? html`<ul gate>
|
|
768
765
|
<li head>${i18next.t('figure.text.must-fix-to-release')}</li>
|
|
769
|
-
${blocking.map(finding => renderGateFinding(finding, true,
|
|
766
|
+
${blocking.map(finding => renderGateFinding(finding, true, nothing, this.partNames()))}
|
|
770
767
|
</ul>`
|
|
771
768
|
: nothing}
|
|
772
769
|
${advisory.length
|
|
@@ -782,16 +779,4 @@ export class FigureVerdict extends localize(i18next)(LitElement) {
|
|
|
782
779
|
private partNames(): string[] {
|
|
783
780
|
return (this.revision?.asset.appearance ?? []).map(look => look.target)
|
|
784
781
|
}
|
|
785
|
-
|
|
786
|
-
/** The same way out the release panel offers for the same two findings — declare the space from the parts. */
|
|
787
|
-
private actionsFor(code: string) {
|
|
788
|
-
if (code !== 'part-outside-occupancy' && code !== 'occupancy-undeclared') return nothing
|
|
789
|
-
/* A new figure with no parts is refused as undeclared too, but fitting needs parts to measure (seen 2026-09-24). */
|
|
790
|
-
if (!this.bodies) return nothing
|
|
791
|
-
return html`<button fit @click=${this.fit}>${i18next.t('figure.button.fit-base-to-parts')}</button>`
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
private fit = () => {
|
|
795
|
-
this.dispatchEvent(new CustomEvent('fit-base-to-parts', { bubbles: true, composed: true }))
|
|
796
|
-
}
|
|
797
782
|
}
|
package/client/modeller/gizmo.ts
CHANGED
|
@@ -111,6 +111,16 @@ export function gizmoReachOf(asset: AssetLike, part: string): GizmoReach {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** Where a drag left the part, in the asset frame: millimetres, degrees and per-axis scale. */
|
|
114
|
+
/**
|
|
115
|
+
* The size handles that change one measure (ADR-0140 4.1): a round part's radius across is one value, so its X and Z
|
|
116
|
+
* handles are the same diameter, and a sphere's three are one. Said on the canvas while the size tool is on, to see how
|
|
117
|
+
* it handles before deciding to show fewer. Nothing for a part whose handles each change their own measure.
|
|
118
|
+
*/
|
|
119
|
+
export function sameMeasureAxesOf(reach: GizmoReach): Array<'x' | 'y' | 'z'> | undefined {
|
|
120
|
+
if (!reach.scale || !reach.round) return undefined
|
|
121
|
+
return reach.round.along.length ? ['x', 'z'] : ['x', 'y', 'z']
|
|
122
|
+
}
|
|
123
|
+
|
|
114
124
|
export interface GizmoResult {
|
|
115
125
|
position?: [number, number, number]
|
|
116
126
|
rotation?: [number, number, number]
|
package/client/modeller/keys.ts
CHANGED
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
* Typing is never taken: a key pressed in a field belongs to the field -- Backspace in a name box deletes a letter,
|
|
6
6
|
* not the part.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
/*
|
|
9
|
+
* `copy` · `paste` are the modeller's own: a part copied and pasted is the kernel's duplicate, with a name of its own.
|
|
10
|
+
* Left to the scene, Ctrl+C / Ctrl+V copied the canvas object with the part's name -- two objects of one name, the copy
|
|
11
|
+
* moving the original (user 2026-10-21). `hold` is a key kept from the scene that the modeller has no use for: the
|
|
12
|
+
* scene's cut and bulk paste act on canvas objects alone and leave the figure out of step with them.
|
|
13
|
+
*/
|
|
14
|
+
export type KeyAction = 'undo' | 'redo' | 'duplicate' | 'remove' | 'deselect' | 'copy' | 'paste' | 'hold' | GizmoKey
|
|
9
15
|
|
|
10
16
|
/** The gizmo's modes on W, E and R, as the scene has them. */
|
|
11
17
|
export type GizmoKey = 'translate' | 'rotate' | 'scale'
|
|
@@ -36,6 +42,9 @@ export function keyActionOf(e: KeyLike, selected: string): KeyAction | null {
|
|
|
36
42
|
const key = e.key.toLowerCase()
|
|
37
43
|
if (mod && key === 'z') return e.shiftKey ? 'redo' : 'undo'
|
|
38
44
|
if (mod && key === 'y') return 'redo'
|
|
45
|
+
if (mod && key === 'c') return selected ? 'copy' : 'hold'
|
|
46
|
+
if (mod && key === 'v') return e.shiftKey ? 'hold' : 'paste'
|
|
47
|
+
if (mod && key === 'x') return 'hold'
|
|
39
48
|
if (!selected) return null
|
|
40
49
|
if (mod && key === 'd') return 'duplicate'
|
|
41
50
|
if (!mod && (key === 'delete' || key === 'backspace')) return 'remove'
|
package/client/modeller/links.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FIGURE_FACES, figureShapeFaces, figurePartsExtentOf, compileFigureAsset, figureFollowOf, proposeFigureProportion,
|
|
2
|
-
import type { FigureFollow,
|
|
1
|
+
import { FIGURE_FACES, figureShapeFaces, figurePartsExtentOf, compileFigureAsset, figureFollowOf, proposeFigureProportion, figureAttachmentsOf, figureBindingsOf, figureInstanceSizeRangeOf, figureFollowableAxesOf, figureShapeProviderOf, figureMimicTermsOf, figureFastenKeptPlaceOf, figureFastenAxesOf, figureJointAsFixed, figureJointWithMotion, figureSurfaceOf } from '@hatiolab/figure-model'
|
|
2
|
+
import type { FigureFollow, FigureInstanceSizeRange } from '@hatiolab/figure-model'
|
|
3
3
|
import type { FigureAsset, FigureAuthoringAction, FigureBindingPlace, FigureDimensionSource, FigureFace, FigureFacing, FigureJoint, FigureSurfacePlace } from '@hatiolab/figure-model'
|
|
4
4
|
import { startingShapeKindOf } from './starting-shape.js'
|
|
5
5
|
import { boxOf } from './align.js'
|
|
@@ -733,53 +733,48 @@ export const holdsThings = (asset: FigureAsset) => (asset.document.capabilities.
|
|
|
733
733
|
export const acceptsOf = (typed: string) => [...new Set(typed.split(',').map(one => one.trim()).filter(Boolean))]
|
|
734
734
|
|
|
735
735
|
/**
|
|
736
|
-
* The sizes a board may give this figure: the
|
|
737
|
-
*
|
|
738
|
-
*
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
const declared = {} as FigureSizeRange
|
|
744
|
-
const authored = {} as Record<Axis, number>
|
|
745
|
-
for (const axis of AXES) {
|
|
746
|
-
const input = inputs.find(i => i.id === `size.${axis}` && i.role === 'design')
|
|
747
|
-
const at = asset.designInputs[`size.${axis}`]
|
|
748
|
-
if (!input || typeof at !== 'number') return undefined
|
|
749
|
-
declared[axis] = { min: input.min ?? 0, max: input.max ?? Infinity }
|
|
750
|
-
authored[axis] = at
|
|
751
|
-
}
|
|
752
|
-
const start = defaultInstanceSizeRange(authored)
|
|
753
|
-
const measured = foldInstanceSizeRange(start, figureInstanceSizeLimitsOf(asset, start)).range
|
|
754
|
-
return { declared, measured }
|
|
736
|
+
* The sizes a board may give this figure, per axis: the ends its author narrowed, and the rest as far as its parts
|
|
737
|
+
* allow, computed by figure-model (`figureInstanceSizeRangeOf`, ADR-0087 decision 6) -- the screen does not measure
|
|
738
|
+
* on its own. Nothing when no size is declared.
|
|
739
|
+
*/
|
|
740
|
+
export function instanceRangeOf(asset: FigureAsset): Record<Axis, FigureInstanceSizeRange> | undefined {
|
|
741
|
+
const range = figureInstanceSizeRangeOf(asset)
|
|
742
|
+
return AXES.every(axis => range[axis]) ? (range as Record<Axis, FigureInstanceSizeRange>) : undefined
|
|
755
743
|
}
|
|
756
744
|
|
|
757
745
|
/**
|
|
758
|
-
* The size
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
* to follow its size -- every figure made in the modeller was left there (user, 2026-09-25).
|
|
746
|
+
* The size to declare for a figure that has none yet: its parts' extent now, the size it is authored at. Undefined when
|
|
747
|
+
* the figure already has a size or has nothing to measure. Without a declared size a board cannot resize the figure,
|
|
748
|
+
* and no part is offered to follow its size -- every figure made in the modeller was left there (user, 2026-09-25).
|
|
762
749
|
*/
|
|
763
|
-
export function
|
|
750
|
+
export function instanceSizeToDeclare(asset: FigureAsset): { at: Record<Axis, number> } | undefined {
|
|
764
751
|
if (instanceRangeOf(asset) || !asset.appearance.length) return undefined
|
|
765
752
|
/* The parts as they rest -- not the room their joints swing them through, which declared a door 8208 mm every way. */
|
|
766
753
|
const at = figurePartsExtentOf(asset) as Record<Axis, number>
|
|
767
754
|
if (AXES.some(axis => !(at[axis] > 0))) return undefined
|
|
768
|
-
|
|
769
|
-
return { range: foldInstanceSizeRange(start, figureInstanceSizeLimitsOf(asset, start)).range, at }
|
|
755
|
+
return { at }
|
|
770
756
|
}
|
|
771
757
|
|
|
772
|
-
/** Whether measuring again would change the declared range (to a micrometre). */
|
|
773
758
|
/**
|
|
774
|
-
* The
|
|
775
|
-
*
|
|
776
|
-
* value is not a number; whether the figure holds there is the command's to say.
|
|
759
|
+
* The ends the author narrowed, with one end typed or cleared: a number narrows that end, an empty field opens it
|
|
760
|
+
* again to what the parts allow. Whether the figure holds there is the command's to say.
|
|
777
761
|
*/
|
|
778
|
-
export function rangeWith(
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
762
|
+
export function rangeWith(
|
|
763
|
+
range: Record<Axis, FigureInstanceSizeRange>,
|
|
764
|
+
axis: Axis,
|
|
765
|
+
bound: 'min' | 'max',
|
|
766
|
+
value: number
|
|
767
|
+
): Partial<Record<Axis, { min?: number; max?: number }>> {
|
|
768
|
+
const out: Partial<Record<Axis, { min?: number; max?: number }>> = {}
|
|
769
|
+
for (const a of AXES) {
|
|
770
|
+
const ends: { min?: number; max?: number } = {}
|
|
771
|
+
for (const b of ['min', 'max'] as const) {
|
|
772
|
+
const typed = a === axis && b === bound
|
|
773
|
+
if (typed ? Number.isFinite(value) : range[a].narrowed[b]) ends[b] = typed ? value : range[a][b]
|
|
774
|
+
}
|
|
775
|
+
if (Object.keys(ends).length) out[a] = ends
|
|
776
|
+
}
|
|
777
|
+
return out
|
|
783
778
|
}
|
|
784
779
|
|
|
785
780
|
/**
|
|
@@ -807,10 +802,6 @@ export function valueNow(revision: FigureRevision, ref: string | undefined): num
|
|
|
807
802
|
}
|
|
808
803
|
}
|
|
809
804
|
|
|
810
|
-
export function rangeDiffers(a: FigureSizeRange, b: FigureSizeRange): boolean {
|
|
811
|
-
return AXES.some(axis => Math.abs(a[axis].min - b[axis].min) > 1e-3 || Math.abs(a[axis].max - b[axis].max) > 1e-3)
|
|
812
|
-
}
|
|
813
|
-
|
|
814
805
|
/**
|
|
815
806
|
* Whether the figure is a mechanism -- a part turns about a joint. Such a figure should grow by one ratio (chief
|
|
816
807
|
* architect's ruling 2026-09-24); the modeller proposes it, the author declares it.
|
|
@@ -137,7 +137,7 @@ export function figureReportOf(asset: FigureAsset): FigureReport | undefined {
|
|
|
137
137
|
},
|
|
138
138
|
{ key: 'detail', blocked: 'figure.text.maturity-blocked-no-detail-level' },
|
|
139
139
|
{ key: 'reactive', blocked: 'figure.text.maturity-blocked-no-binding' },
|
|
140
|
-
readable(
|
|
140
|
+
readable(drawn, writers)
|
|
141
141
|
]
|
|
142
142
|
|
|
143
143
|
return {
|
|
@@ -229,13 +229,10 @@ const roundFootprint = (shape: { extrusion?: { axis: string; section: { kind: st
|
|
|
229
229
|
* finer segment count does not raise it: H = −Σ p·log p over each material's share of the footprint plus the
|
|
230
230
|
* background, divided by log(`LIMITS.distinctMaterials` + 1), the most this measure can reach.
|
|
231
231
|
*
|
|
232
|
-
* The footprint is the
|
|
233
|
-
* occupancy), otherwise the drawn bodies' own extent. Bodies are their axis-aligned world boxes
|
|
232
|
+
* The footprint is the drawn bodies' own extent, the box a board gives it (ADR-0145). Bodies are their axis-aligned world boxes
|
|
234
233
|
* (`figureWorldBoxesOf`, the gate's reading); the higher one covers the lower.
|
|
235
234
|
*/
|
|
236
235
|
function readable(
|
|
237
|
-
asset: FigureAsset,
|
|
238
|
-
plan: ReturnType<typeof compileFigureDocument>,
|
|
239
236
|
drawn: FigureAssetGeometry[],
|
|
240
237
|
writers: Map<string, string>
|
|
241
238
|
): FigureMaturityAxis {
|
|
@@ -245,20 +242,12 @@ function readable(
|
|
|
245
242
|
.map(body => ({ body, box: figureWorldBoxesOf([body], writers)[0]! }))
|
|
246
243
|
if (placed.length === 0) return { key: 'readable', blocked: 'figure.text.maturity-blocked-no-parts' }
|
|
247
244
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
frame =
|
|
254
|
-
} else {
|
|
255
|
-
frame = { x0: Infinity, x1: -Infinity, z0: Infinity, z1: -Infinity }
|
|
256
|
-
for (const { box } of placed) {
|
|
257
|
-
frame.x0 = Math.min(frame.x0, box.centre.x - box.extent.x / 2)
|
|
258
|
-
frame.x1 = Math.max(frame.x1, box.centre.x + box.extent.x / 2)
|
|
259
|
-
frame.z0 = Math.min(frame.z0, box.centre.z - box.extent.z / 2)
|
|
260
|
-
frame.z1 = Math.max(frame.z1, box.centre.z + box.extent.z / 2)
|
|
261
|
-
}
|
|
245
|
+
const frame = { x0: Infinity, x1: -Infinity, z0: Infinity, z1: -Infinity }
|
|
246
|
+
for (const { box } of placed) {
|
|
247
|
+
frame.x0 = Math.min(frame.x0, box.centre.x - box.extent.x / 2)
|
|
248
|
+
frame.x1 = Math.max(frame.x1, box.centre.x + box.extent.x / 2)
|
|
249
|
+
frame.z0 = Math.min(frame.z0, box.centre.z - box.extent.z / 2)
|
|
250
|
+
frame.z1 = Math.max(frame.z1, box.centre.z + box.extent.z / 2)
|
|
262
251
|
}
|
|
263
252
|
|
|
264
253
|
const regions = topViewRegions(placed, frame)
|
|
@@ -417,26 +406,21 @@ export interface FigureBudgetLine {
|
|
|
417
406
|
}
|
|
418
407
|
|
|
419
408
|
/**
|
|
420
|
-
* What the verdict tab measures of the figure on screen once it settles: the report,
|
|
421
|
-
*
|
|
422
|
-
* tab and the parts list's line, from the same report. A source the format refuses has no report.
|
|
409
|
+
* What the verdict tab measures of the figure on screen once it settles: the report, and what it tells the page: the
|
|
410
|
+
* count for its tab and the parts list's line, from the same report. A source the format refuses has no report.
|
|
423
411
|
*/
|
|
424
412
|
export function verdictMeasureOf(asset: FigureAsset | undefined): {
|
|
425
413
|
report?: FigureReport
|
|
426
|
-
bodies: number
|
|
427
414
|
told: { instances?: number; budget?: FigureBudgetLine }
|
|
428
415
|
} {
|
|
429
416
|
let report: FigureReport | undefined
|
|
430
|
-
let bodies = 0
|
|
431
417
|
try {
|
|
432
|
-
const cost = asset ? figureCostOf(asset) : undefined
|
|
433
|
-
bodies = cost ? cost.groups + cost.hidden : 0
|
|
434
418
|
report = asset ? figureReportOf(asset) : undefined
|
|
435
419
|
} catch {
|
|
436
420
|
/* A source the format refuses has no report; the server's finding says why. */
|
|
437
421
|
report = undefined
|
|
438
422
|
}
|
|
439
|
-
return { report,
|
|
423
|
+
return { report, told: { instances: report?.capacity.instances, budget: budgetOf(report) } }
|
|
440
424
|
}
|
|
441
425
|
|
|
442
426
|
export function budgetOf(report: FigureReport | undefined): FigureBudgetLine | undefined {
|
|
@@ -16,7 +16,7 @@ export function paletteRecipe(kind: FigurePaletteKind, id: string): FigurePrimit
|
|
|
16
16
|
case 'rounded-box':
|
|
17
17
|
return { ...standing(100), kind, dimensions: { width: 100, height: 100, depth: 100, round: 10 } }
|
|
18
18
|
case 'sphere':
|
|
19
|
-
return { ...standing(100), kind, dimensions: {
|
|
19
|
+
return { ...standing(100), kind, dimensions: { radius: 50, radiusY: 50 } }
|
|
20
20
|
case 'frustum':
|
|
21
21
|
return { ...standing(100), kind, dimensions: { radiusTop: 0, radiusBottom: 50, height: 100 } }
|
|
22
22
|
case 'polygon':
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
createFigureEditorWorkspace,
|
|
6
6
|
serializeFigureAsset,
|
|
7
7
|
parseFigureAsset,
|
|
8
|
+
figureSizeRangeFitsOf,
|
|
9
|
+
type FigureSizeRangeFit,
|
|
8
10
|
type FigureInputChange,
|
|
9
11
|
type FigurePrimitiveCreation,
|
|
10
12
|
type FigurePartEdit,
|
|
@@ -71,10 +73,14 @@ export function createFigureProductSession(store: FigureProductStore) {
|
|
|
71
73
|
*/
|
|
72
74
|
let done: Array<string | undefined> = [],
|
|
73
75
|
undone: Array<string | undefined> = []
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
/* The author's size ends the last step moved without being asked (ADR-0087 decision 6), told once. */
|
|
77
|
+
let fitted: FigureSizeRangeFit[] = []
|
|
78
|
+
function tracked<T>(kind: 'do' | 'undo' | 'redo', run: () => T, tag?: string, setsRange = false): T {
|
|
79
|
+
const was = workspace.read()
|
|
76
80
|
const result = run()
|
|
77
|
-
|
|
81
|
+
const now = workspace.read()
|
|
82
|
+
if (now.revision === was.revision) return result
|
|
83
|
+
fitted = kind === 'do' && !setsRange && was.asset && now.asset ? figureSizeRangeFitsOf(was.asset, now.asset) : []
|
|
78
84
|
if (kind === 'do') {
|
|
79
85
|
done.push(tag)
|
|
80
86
|
undone = []
|
|
@@ -250,7 +256,13 @@ export function createFigureProductSession(store: FigureProductStore) {
|
|
|
250
256
|
},
|
|
251
257
|
/** One authoring command — what a person means, not a graph edit — as one step of the same history. */
|
|
252
258
|
command(action: FigureAuthoringAction) {
|
|
253
|
-
return tracked('do', () => workspace.command({ ...writable(), action, ...consented() }))
|
|
259
|
+
return tracked('do', () => workspace.command({ ...writable(), action, ...consented() }), undefined, action.kind === 'declare-instance-size')
|
|
260
|
+
},
|
|
261
|
+
/** The ends of the size on a drawing the last step moved in to what the parts allow, once: then nothing until the next. */
|
|
262
|
+
takeSizeRangeFits(): FigureSizeRangeFit[] {
|
|
263
|
+
const out = fitted
|
|
264
|
+
fitted = []
|
|
265
|
+
return out
|
|
254
266
|
},
|
|
255
267
|
/**
|
|
256
268
|
* The first edit made inside `run` may let go of exactly these clips -- the ones a CLIPS_DROPPED refusal named and
|
|
@@ -2,29 +2,23 @@ import type { PropertyValues } from 'lit'
|
|
|
2
2
|
import { i18next } from '@operato/i18n'
|
|
3
3
|
import { notify } from '@operato/layout'
|
|
4
4
|
import { OxPrompt } from '@operato/popup'
|
|
5
|
-
import type {
|
|
5
|
+
import type { FigureClipLoss } from '@hatiolab/figure-model'
|
|
6
6
|
import type { FigureProductSession } from './product-session.js'
|
|
7
7
|
import { clipLossOf } from './refusals.js'
|
|
8
8
|
|
|
9
|
-
/** The same link offered again with the size range narrowed, after a link the declared range could not hold. */
|
|
10
|
-
export type Retry = { label: string; action?: FigureAuthoringAction; run?: () => void }
|
|
11
|
-
|
|
12
9
|
/** What the teller reads of the editor, and what it asks of it. */
|
|
13
10
|
export interface RefusalHost {
|
|
14
11
|
session(): FigureProductSession | undefined
|
|
15
12
|
error(): string
|
|
16
13
|
notice(): string
|
|
17
|
-
retry(): Retry | undefined
|
|
18
14
|
/** Said as the editor's refusal. */
|
|
19
15
|
refuse(message: string): void
|
|
20
|
-
/** A narrowed link, confirmed: run as the editor runs a command. */
|
|
21
|
-
command(action: FigureAuthoringAction): void
|
|
22
16
|
}
|
|
23
17
|
|
|
24
18
|
/**
|
|
25
19
|
* Refusals and notices go to the app's shared surfaces, as every screen's do (user, 2026-09-27: 「왜 모두가 사용하는
|
|
26
20
|
* notification 오류 표현 방법을 사용하지 않고」 · 「혹은 oxprompt 에서 확인하고 진행하던지」). A refusal with a way on --
|
|
27
|
-
* delete and fit the space again,
|
|
21
|
+
* delete and fit the space again, let a clip go -- asks in the app's prompt and goes on when confirmed; any
|
|
28
22
|
* other refusal is the app's notification, which stays until it is closed.
|
|
29
23
|
*/
|
|
30
24
|
export class RefusalTeller {
|
|
@@ -35,11 +29,7 @@ export class RefusalTeller {
|
|
|
35
29
|
/** After the editor's update: what changed in its refusal and notice, and the session's own error, said once. */
|
|
36
30
|
tell(changed: PropertyValues) {
|
|
37
31
|
const error = this.of.error()
|
|
38
|
-
if (changed.has('error') && error) {
|
|
39
|
-
const retry = this.of.retry()
|
|
40
|
-
if (retry) this.ask(error, retry)
|
|
41
|
-
else notify({ level: 'error', message: error })
|
|
42
|
-
}
|
|
32
|
+
if (changed.has('error') && error) notify({ level: 'error', message: error })
|
|
43
33
|
const notice = this.of.notice()
|
|
44
34
|
if (changed.has('notice') && notice) notify({ level: 'info', message: notice })
|
|
45
35
|
const viewError = this.of.session()?.read()?.error ?? ''
|
|
@@ -91,16 +81,4 @@ export class RefusalTeller {
|
|
|
91
81
|
}
|
|
92
82
|
asked.session.withConsent({ dropClips: clips.map(c => c.id), context: asked.context, revision: asked.revision }, again)
|
|
93
83
|
}
|
|
94
|
-
|
|
95
|
-
private async ask(message: string, retry: Retry) {
|
|
96
|
-
const agreed = await OxPrompt.open({
|
|
97
|
-
type: 'question',
|
|
98
|
-
text: message,
|
|
99
|
-
confirmButton: { text: retry.label },
|
|
100
|
-
cancelButton: { text: i18next.t('figure.button.keep-editing') }
|
|
101
|
-
})
|
|
102
|
-
if (!agreed) return
|
|
103
|
-
if (retry.run) retry.run()
|
|
104
|
-
else this.of.command(retry.action!)
|
|
105
|
-
}
|
|
106
84
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* English is for logs and readers of the kernel, and a screen that read it went quiet whenever a sentence changed or a
|
|
8
8
|
* new one came under the same code (ADR-0054). A refusal with no details the screen knows is said with its code.
|
|
9
9
|
*/
|
|
10
|
-
import { FIGURE_INSTANCE_SIZE_INPUTS, type FigureClipLoss, type FigureRefusalDetails } from '@hatiolab/figure-model'
|
|
10
|
+
import { FIGURE_INSTANCE_SIZE_INPUTS, type FigureClipLoss, type FigureRefusalDetails, type FigureSizeRangeFit } from '@hatiolab/figure-model'
|
|
11
11
|
|
|
12
12
|
type Say = (key: string, params: Record<string, string>) => string
|
|
13
13
|
type Reason = FigureRefusalDetails['reason']
|
|
@@ -23,36 +23,29 @@ const numberOf = (value: number) => value.toLocaleString(undefined, { maximumFra
|
|
|
23
23
|
/** Bytes in KiB, rounded up: a figure a few bytes over its limit is not said to be at it. */
|
|
24
24
|
const kibOf = (bytes: number) => Math.ceil(bytes / 1024).toLocaleString()
|
|
25
25
|
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The ends an author typed for the size on a drawing, refused: they leave out the size the figure is drawn at, or start
|
|
28
|
+
* at zero. The only refusals the size range has left (ADR-0087 decision 6); nothing for any other refusal.
|
|
29
|
+
*/
|
|
27
30
|
export function sizeRangeRefusalOf(error: unknown, say: Say): string | null {
|
|
28
31
|
if (codeOf(error) !== 'INSTANCE_SIZE_RANGE') return null
|
|
29
|
-
const reach = detailsOf(error, 'size-cannot-reach')
|
|
30
|
-
if (reach)
|
|
31
|
-
return say(reach.bound === 'min' ? 'figure.text.size-range-too-small' : 'figure.text.size-range-too-large', { axis: reach.axis.toUpperCase(), mm: mmOf(reach.need), part: reach.part ?? '' })
|
|
32
32
|
const outside = detailsOf(error, 'size-outside-range')
|
|
33
|
-
if (outside)
|
|
33
|
+
if (outside)
|
|
34
|
+
return say('figure.text.size-range-leaves-out-size', {
|
|
35
|
+
axis: say(`figure.label.resize-${outside.axis}`, {}),
|
|
36
|
+
range: `${mmOf(outside.min ?? 0)} – ${outside.max === undefined ? '∞' : mmOf(outside.max)}`,
|
|
37
|
+
at: mmOf(outside.at)
|
|
38
|
+
})
|
|
34
39
|
const zero = detailsOf(error, 'size-min-not-positive')
|
|
35
|
-
if (zero) return say('figure.text.size-range-from-zero', { axis: zero.axis
|
|
36
|
-
const gone = detailsOf(error, 'size-already-gone')
|
|
37
|
-
if (gone) return say('figure.text.size-range-part-gone', { axis: gone.axis.toUpperCase(), at: mmOf(gone.at) })
|
|
40
|
+
if (zero) return say('figure.text.size-range-from-zero', { axis: say(`figure.label.resize-${zero.axis}`, {}) })
|
|
38
41
|
return null
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (action.kind !== 'link-dimension' || codeOf(error) !== 'INSTANCE_SIZE_RANGE') return null
|
|
47
|
-
const reach = detailsOf(error, 'size-cannot-reach')
|
|
48
|
-
if (!reach) return null
|
|
49
|
-
const words = { axis: reach.axis.toUpperCase(), mm: mmOf(reach.need), part: reach.part ?? '' }
|
|
50
|
-
const below = reach.bound === 'min'
|
|
51
|
-
return {
|
|
52
|
-
message: say(below ? 'figure.text.link-needs-smaller-range' : 'figure.text.link-needs-larger-range', words),
|
|
53
|
-
label: say(below ? 'figure.button.narrow-range-from' : 'figure.button.narrow-range-to', words),
|
|
54
|
-
retry: { ...action, narrowRange: true }
|
|
55
|
-
}
|
|
44
|
+
/** What a command did to the ends the author typed, in one line each: what moved, from what to what. */
|
|
45
|
+
export function sizeRangeFitsOf(fits: readonly FigureSizeRangeFit[], say: Say): string {
|
|
46
|
+
return fits
|
|
47
|
+
.map(f => say(`figure.text.size-range-moved-${f.bound}`, { axis: say(`figure.label.resize-${f.axis}`, {}), from: mmOf(f.from), to: mmOf(f.to) }))
|
|
48
|
+
.join(' ')
|
|
56
49
|
}
|
|
57
50
|
|
|
58
51
|
/**
|
|
@@ -83,20 +76,14 @@ export function clipLossOf(error: unknown): FigureClipLoss[] | null {
|
|
|
83
76
|
return codeOf(error) === 'CLIPS_DROPPED' ? (detailsOf(error, 'clips-dropped')?.clips ?? []) : null
|
|
84
77
|
}
|
|
85
78
|
|
|
86
|
-
/** A part refused because the declared room reads it -- the one refusal deleting it with the room refitted answers. */
|
|
87
|
-
export function inDeclaredRoom(error: unknown): boolean {
|
|
88
|
-
return codeOf(error) === 'PART_REFERENCED' && !!detailsOf(error, 'in-declared-space')
|
|
89
|
-
}
|
|
90
|
-
|
|
91
79
|
/**
|
|
92
80
|
* Why a part cannot be deleted, in the author's words. Since the chief architect's ruling of 2026-09-27 a part that
|
|
93
81
|
* holds others, leads a motion or is followed goes, and what depended on it is let go of (`removalNoticeOf`); what is
|
|
94
|
-
* left is
|
|
82
|
+
* left is a row -- one laid along the part, or hanging in its row.
|
|
95
83
|
*/
|
|
96
84
|
export function partRefusalOf(error: unknown, part: string, say: Say): string | null {
|
|
97
85
|
const code = codeOf(error)
|
|
98
86
|
if (code !== 'PART_REFERENCED' && code !== 'PART_ROW') return null
|
|
99
|
-
if (detailsOf(error, 'in-declared-space')) return say('figure.text.part-in-declared-space', { part })
|
|
100
87
|
const along = detailsOf(error, 'row-along')
|
|
101
88
|
if (along) return say('figure.text.part-row-along', { part, rows: along.rows.join(', ') })
|
|
102
89
|
const carried = detailsOf(error, 'hangs-in-row')
|
|
@@ -297,7 +284,7 @@ export function dataMotionRefusalOf(error: unknown, say: Say): string | null {
|
|
|
297
284
|
const setting = detailsOf(error, 'motion-setting')
|
|
298
285
|
if (setting) return say('figure.text.motion-setting-' + setting.why, { group: setting.group, clip: setting.clip ?? '' })
|
|
299
286
|
const key = codeOf(error) === 'MOTION_GROUP' ? detailsOf(error, 'value-outside-range') : undefined
|
|
300
|
-
if (key) return say('figure.text.motion-key-outside-range', { input: key.input, value: numberOf(key.value), min: numberOf(key.min), max: numberOf(key.max) })
|
|
287
|
+
if (key) return say('figure.text.motion-key-outside-range', { input: key.input, value: numberOf(key.value), min: numberOf(key.min!), max: numberOf(key.max!) })
|
|
301
288
|
return null
|
|
302
289
|
}
|
|
303
290
|
|
|
@@ -310,7 +297,7 @@ export function inputRangeRefusalOf(error: unknown, say: Say): string | null {
|
|
|
310
297
|
const range = detailsOf(error, 'value-outside-range')
|
|
311
298
|
if (!range) return null
|
|
312
299
|
const axis = (Object.entries(FIGURE_INSTANCE_SIZE_INPUTS) as Array<[string, string]>).find(([, id]) => id === range.input)?.[0]
|
|
313
|
-
const params = { value: numberOf(range.value), min: numberOf(range.min), max: numberOf(range.max) }
|
|
300
|
+
const params = { value: numberOf(range.value), min: range.min === undefined ? '−∞' : numberOf(range.min), max: range.max === undefined ? '∞' : numberOf(range.max) }
|
|
314
301
|
return axis ? say('figure.text.input-range-size', { ...params, axis: axis.toUpperCase() }) : say('figure.text.input-range', params)
|
|
315
302
|
}
|
|
316
303
|
|
|
@@ -10,7 +10,6 @@ import { fetchFigure, fetchFigureCategories, fetchFigureNamespaces, fetchFigureT
|
|
|
10
10
|
import { figureProposalOutcome } from '../figure-assistant.js'
|
|
11
11
|
import { availableRecipes, recipeGuide } from '../modeller/recipe-source.js'
|
|
12
12
|
import type { RecipeTemplate } from '../modeller/recipes.js'
|
|
13
|
-
import { occupancyFromParts } from '../modeller/occupancy.js'
|
|
14
13
|
import type { FigureAuthoringAction } from '@hatiolab/figure-model'
|
|
15
14
|
import type { FigureGateFinding } from '../types.js'
|
|
16
15
|
import '../modeller/editor.js'
|
|
@@ -290,13 +289,7 @@ export class FigureModellerPage extends PageView {
|
|
|
290
289
|
input.value = this.row.type ?? ''
|
|
291
290
|
}
|
|
292
291
|
}
|
|
293
|
-
/**
|
|
294
|
-
private fitToParts = () => {
|
|
295
|
-
const shown = this.session.revisionNow()
|
|
296
|
-
/* Nothing open, nothing to fit: the button stands only beside an open figure. */
|
|
297
|
-
if (shown) this.runCommand(() => occupancyFromParts(shown.asset))
|
|
298
|
-
}
|
|
299
|
-
/** A command from the properties tab — rename, declare the room, the mounting face. */
|
|
292
|
+
/** A command from the properties tab — rename, the mounting face. */
|
|
300
293
|
private onCommand = (event: Event) => {
|
|
301
294
|
const action = (event as CustomEvent).detail?.action as FigureAuthoringAction | undefined
|
|
302
295
|
if (action) this.runCommand(() => action)
|
|
@@ -311,18 +304,12 @@ export class FigureModellerPage extends PageView {
|
|
|
311
304
|
this.commandError = ''
|
|
312
305
|
this.changed()
|
|
313
306
|
} catch (error) {
|
|
314
|
-
/*
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
? i18next.t('figure.text.fit-needs-parts')
|
|
321
|
-
: (error as { code?: string })?.code
|
|
322
|
-
? editRefusalOf(error, (key, params) => i18next.t(key, params))
|
|
323
|
-
: error instanceof Error
|
|
324
|
-
? error.message
|
|
325
|
-
: String(error)
|
|
307
|
+
/* Said in the author's words beside the side panel, not as the page error that replaces the whole editor. */
|
|
308
|
+
this.commandError = (error as { code?: string })?.code
|
|
309
|
+
? editRefusalOf(error, (key, params) => i18next.t(key, params))
|
|
310
|
+
: error instanceof Error
|
|
311
|
+
? error.message
|
|
312
|
+
: String(error)
|
|
326
313
|
}
|
|
327
314
|
}
|
|
328
315
|
/**
|
|
@@ -562,7 +549,6 @@ export class FigureModellerPage extends PageView {
|
|
|
562
549
|
@capacity-changed=${(event: CustomEvent) => {
|
|
563
550
|
this.budget = event.detail.budget
|
|
564
551
|
}}
|
|
565
|
-
@fit-base-to-parts=${this.fitToParts}
|
|
566
552
|
@release-verdict=${(event: CustomEvent<{ blocking: FigureGateFinding[] }>) => (this.releaseBlockers = event.detail.blocking)}
|
|
567
553
|
></figure-verdict>
|
|
568
554
|
<figure-history-panel
|
|
@@ -575,7 +561,6 @@ export class FigureModellerPage extends PageView {
|
|
|
575
561
|
.dirty=${!!view.dirty}
|
|
576
562
|
.saveFirst=${this.saveBeforeRevert}
|
|
577
563
|
.serverSource=${() => this.session.confirmedSource()}
|
|
578
|
-
@fit-base-to-parts=${this.fitToParts}
|
|
579
564
|
@figure-released=${this.released}
|
|
580
565
|
@figure-reverted=${this.reverted}
|
|
581
566
|
></figure-history-panel>
|