@things-factory/figure-ui 10.1.74 → 10.1.76
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/figure-assistant.ts +3 -3
- package/client/graphql/{v3-store.ts → store.ts} +6 -6
- package/client/modeller/{v3-ai-proposal.ts → ai-proposal.ts} +8 -8
- package/client/modeller/{v3-align.ts → align.ts} +14 -14
- package/client/modeller/{v3-canvas.ts → canvas.ts} +13 -13
- package/client/modeller/{v3-clips.ts → clips.ts} +5 -5
- package/client/modeller/{v3-controls.ts → controls.ts} +2 -2
- package/client/modeller/{v3-editor.ts → editor.ts} +137 -126
- package/client/modeller/{figure-v3-capability.ts → figure-capability-panel.ts} +7 -7
- package/client/modeller/{figure-v3-clips.ts → figure-clips.ts} +10 -10
- package/client/modeller/{figure-v3-controls.ts → figure-controls.ts} +9 -9
- package/client/modeller/{v3-figure-facts.ts → figure-facts.ts} +11 -11
- package/client/modeller/{figure-v3-first-save.ts → figure-first-save.ts} +3 -3
- package/client/modeller/figure-history-panel.ts +1 -1
- package/client/modeller/{figure-v3-links.ts → figure-links.ts} +35 -35
- package/client/modeller/{figure-v3-material.ts → figure-material-panel.ts} +20 -20
- package/client/modeller/{figure-v3-parts.ts → figure-parts.ts} +15 -15
- package/client/modeller/{figure-v3-preview.ts → figure-preview.ts} +14 -14
- package/client/modeller/{figure-v3-properties.ts → figure-properties.ts} +25 -25
- package/client/modeller/{figure-v3-recipe-catalog.ts → figure-recipe-catalog.ts} +10 -10
- package/client/modeller/{figure-v3-verdict.ts → figure-verdict.ts} +24 -24
- package/client/modeller/gate-findings.ts +2 -2
- package/client/modeller/{v3-gizmo.ts → gizmo.ts} +11 -11
- package/client/modeller/{v3-joint-drag.ts → joint-drag.ts} +11 -11
- package/client/modeller/{v3-joint-fan.ts → joint-fan.ts} +13 -13
- package/client/modeller/{v3-links.ts → links.ts} +59 -59
- package/client/modeller/{v3-live.ts → live.ts} +9 -9
- package/client/modeller/{v3-maturity.ts → maturity.ts} +65 -65
- package/client/modeller/modeller-route.ts +1 -1
- package/client/modeller/{v3-occupancy.ts → occupancy.ts} +6 -6
- package/client/modeller/{v3-palette.ts → palette.ts} +3 -3
- package/client/modeller/{v3-preview-model.ts → preview-model.ts} +8 -8
- package/client/modeller/{v3-product-session.ts → product-session.ts} +25 -25
- package/client/modeller/{v3-recipe-source.ts → recipe-source.ts} +2 -2
- package/client/modeller/{v3-recipes.ts → recipes.ts} +9 -9
- package/client/modeller/{v3-refusals.ts → refusals.ts} +62 -13
- package/client/modeller/{v3-renderer.ts → renderer.ts} +16 -16
- package/client/modeller/{v3-stage-model.ts → stage-model.ts} +15 -15
- package/client/modeller/{v3-thumbnail.ts → thumbnail.ts} +8 -8
- package/client/pages/figure-list-page.ts +4 -4
- package/client/pages/{figure-v3-modeller-page.ts → figure-modeller-page.ts} +37 -37
- package/client/route.ts +1 -5
- package/dist-client/figure-assistant.js +3 -3
- package/dist-client/figure-assistant.js.map +1 -1
- package/dist-client/graphql/store.d.ts +3 -0
- package/dist-client/graphql/store.js +39 -0
- package/dist-client/graphql/store.js.map +1 -0
- package/dist-client/modeller/ai-proposal.d.ts +51 -0
- package/dist-client/modeller/ai-proposal.js +64 -0
- package/dist-client/modeller/ai-proposal.js.map +1 -0
- package/dist-client/modeller/align.d.ts +30 -0
- package/dist-client/modeller/align.js +75 -0
- package/dist-client/modeller/align.js.map +1 -0
- package/dist-client/modeller/canvas.d.ts +72 -0
- package/dist-client/modeller/canvas.js +379 -0
- package/dist-client/modeller/canvas.js.map +1 -0
- package/dist-client/modeller/clips.d.ts +39 -0
- package/dist-client/modeller/clips.js +58 -0
- package/dist-client/modeller/clips.js.map +1 -0
- package/dist-client/modeller/controls.d.ts +28 -0
- package/dist-client/modeller/controls.js +28 -0
- package/dist-client/modeller/controls.js.map +1 -0
- package/dist-client/modeller/editor.d.ts +203 -0
- package/dist-client/modeller/editor.js +1526 -0
- package/dist-client/modeller/editor.js.map +1 -0
- package/dist-client/modeller/fields.d.ts +45 -0
- package/dist-client/modeller/fields.js +83 -0
- package/dist-client/modeller/fields.js.map +1 -0
- package/dist-client/modeller/figure-capability-panel.d.ts +22 -0
- package/dist-client/modeller/figure-capability-panel.js +141 -0
- package/dist-client/modeller/figure-capability-panel.js.map +1 -0
- package/dist-client/modeller/figure-clips.d.ts +31 -0
- package/dist-client/modeller/figure-clips.js +268 -0
- package/dist-client/modeller/figure-clips.js.map +1 -0
- package/dist-client/modeller/figure-controls.d.ts +36 -0
- package/dist-client/modeller/figure-controls.js +226 -0
- package/dist-client/modeller/figure-controls.js.map +1 -0
- package/dist-client/modeller/figure-facts.d.ts +35 -0
- package/dist-client/modeller/figure-facts.js +55 -0
- package/dist-client/modeller/figure-facts.js.map +1 -0
- package/dist-client/modeller/figure-first-save.d.ts +20 -0
- package/dist-client/modeller/figure-first-save.js +118 -0
- package/dist-client/modeller/figure-first-save.js.map +1 -0
- package/dist-client/modeller/figure-history-panel.d.ts +1 -1
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-links.d.ts +77 -0
- package/dist-client/modeller/figure-links.js +649 -0
- package/dist-client/modeller/figure-links.js.map +1 -0
- package/dist-client/modeller/figure-material-panel.d.ts +39 -0
- package/dist-client/modeller/figure-material-panel.js +323 -0
- package/dist-client/modeller/figure-material-panel.js.map +1 -0
- package/dist-client/modeller/figure-parts.d.ts +69 -0
- package/dist-client/modeller/figure-parts.js +559 -0
- package/dist-client/modeller/figure-parts.js.map +1 -0
- package/dist-client/modeller/figure-preview.d.ts +34 -0
- package/dist-client/modeller/figure-preview.js +346 -0
- package/dist-client/modeller/figure-preview.js.map +1 -0
- package/dist-client/modeller/figure-properties.d.ts +45 -0
- package/dist-client/modeller/figure-properties.js +239 -0
- package/dist-client/modeller/figure-properties.js.map +1 -0
- package/dist-client/modeller/figure-recipe-catalog.d.ts +38 -0
- package/dist-client/modeller/figure-recipe-catalog.js +399 -0
- package/dist-client/modeller/figure-recipe-catalog.js.map +1 -0
- package/dist-client/modeller/figure-verdict.d.ts +64 -0
- package/dist-client/modeller/figure-verdict.js +787 -0
- package/dist-client/modeller/figure-verdict.js.map +1 -0
- package/dist-client/modeller/first-save.d.ts +17 -0
- package/dist-client/modeller/first-save.js +26 -0
- package/dist-client/modeller/first-save.js.map +1 -0
- package/dist-client/modeller/gate-findings.d.ts +2 -2
- package/dist-client/modeller/gate-findings.js +2 -2
- package/dist-client/modeller/gate-findings.js.map +1 -1
- package/dist-client/modeller/gizmo.d.ts +72 -0
- package/dist-client/modeller/gizmo.js +111 -0
- package/dist-client/modeller/gizmo.js.map +1 -0
- package/dist-client/modeller/help.d.ts +18 -0
- package/dist-client/modeller/help.js +37 -0
- package/dist-client/modeller/help.js.map +1 -0
- package/dist-client/modeller/joint-drag.d.ts +19 -0
- package/dist-client/modeller/joint-drag.js +97 -0
- package/dist-client/modeller/joint-drag.js.map +1 -0
- package/dist-client/modeller/joint-fan.d.ts +9 -0
- package/dist-client/modeller/joint-fan.js +276 -0
- package/dist-client/modeller/joint-fan.js.map +1 -0
- package/dist-client/modeller/keys.d.ts +22 -0
- package/dist-client/modeller/keys.js +26 -0
- package/dist-client/modeller/keys.js.map +1 -0
- package/dist-client/modeller/links.d.ts +267 -0
- package/dist-client/modeller/links.js +480 -0
- package/dist-client/modeller/links.js.map +1 -0
- package/dist-client/modeller/live.d.ts +37 -0
- package/dist-client/modeller/live.js +68 -0
- package/dist-client/modeller/live.js.map +1 -0
- package/dist-client/modeller/maturity.d.ts +120 -0
- package/dist-client/modeller/maturity.js +313 -0
- package/dist-client/modeller/maturity.js.map +1 -0
- package/dist-client/modeller/modeller-route.d.ts +1 -1
- package/dist-client/modeller/modeller-route.js +1 -1
- package/dist-client/modeller/modeller-route.js.map +1 -1
- package/dist-client/modeller/occupancy.d.ts +18 -0
- package/dist-client/modeller/occupancy.js +24 -0
- package/dist-client/modeller/occupancy.js.map +1 -0
- package/dist-client/modeller/palette.d.ts +7 -0
- package/dist-client/modeller/palette.js +46 -0
- package/dist-client/modeller/palette.js.map +1 -0
- package/dist-client/modeller/preview-model.d.ts +47 -0
- package/dist-client/modeller/preview-model.js +68 -0
- package/dist-client/modeller/preview-model.js.map +1 -0
- package/dist-client/modeller/product-session.d.ts +510 -0
- package/dist-client/modeller/product-session.js +157 -0
- package/dist-client/modeller/product-session.js.map +1 -0
- package/dist-client/modeller/recipe-source.d.ts +8 -0
- package/dist-client/modeller/recipe-source.js +14 -0
- package/dist-client/modeller/recipe-source.js.map +1 -0
- package/dist-client/modeller/recipes.d.ts +37 -0
- package/dist-client/modeller/recipes.js +103 -0
- package/dist-client/modeller/recipes.js.map +1 -0
- package/dist-client/modeller/refusals.d.ts +68 -0
- package/dist-client/modeller/refusals.js +142 -0
- package/dist-client/modeller/refusals.js.map +1 -0
- package/dist-client/modeller/renderer.d.ts +40 -0
- package/dist-client/modeller/renderer.js +73 -0
- package/dist-client/modeller/renderer.js.map +1 -0
- package/dist-client/modeller/stage-model.d.ts +39 -0
- package/dist-client/modeller/stage-model.js +147 -0
- package/dist-client/modeller/stage-model.js.map +1 -0
- package/dist-client/modeller/thumbnail.d.ts +3 -0
- package/dist-client/modeller/thumbnail.js +75 -0
- package/dist-client/modeller/thumbnail.js.map +1 -0
- package/dist-client/modeller/v3-clips.d.ts +1 -1
- package/dist-client/modeller/v3-editor.d.ts +5 -0
- package/dist-client/modeller/v3-editor.js +21 -10
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-product-session.d.ts +31 -0
- package/dist-client/modeller/v3-refusals.d.ts +37 -5
- package/dist-client/modeller/v3-refusals.js +59 -16
- package/dist-client/modeller/v3-refusals.js.map +1 -1
- package/dist-client/pages/figure-list-page.d.ts +1 -1
- package/dist-client/pages/figure-list-page.js +4 -4
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +102 -0
- package/dist-client/pages/figure-modeller-page.js +548 -0
- package/dist-client/pages/figure-modeller-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +1 -5
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/docs/product-cutover.md +25 -0
- package/package.json +5 -5
- package/test/{v3-ai-proposal.test.ts → ai-proposal.test.ts} +6 -6
- package/test/{v3-align.test.ts → align.test.ts} +8 -8
- package/test/browser/modeller-page.js +30 -30
- package/test/browser/modeller.check.mjs +17 -16
- package/test/browser/serve.mjs +1 -1
- package/test/browser/thumbnail-page.js +6 -6
- package/test/{v3-clips.test.ts → clips.test.ts} +18 -18
- package/test/{v3-controls.test.ts → controls.test.ts} +15 -15
- package/test/{v3-fields.test.ts → fields.test.ts} +13 -13
- package/test/figure-assistant-surface.test.ts +3 -3
- package/test/{v3-first-save.test.ts → first-save.test.ts} +10 -10
- package/test/fixtures/joint-column.json +2 -2
- package/test/fixtures/joint-roller.json +2 -2
- package/test/fixtures/robot-arm-6dof.json +2 -2
- package/test/{v3-gizmo.test.ts → gizmo.test.ts} +33 -33
- package/test/{v3-joint-drag.test.ts → joint-drag.test.ts} +17 -17
- package/test/{v3-joint-fan.test.ts → joint-fan.test.ts} +18 -18
- package/test/joints.ts +6 -6
- package/test/{v3-keys.test.ts → keys.test.ts} +2 -2
- package/test/{v3-layout.test.ts → layout.test.ts} +8 -8
- package/test/{v3-links.test.ts → links.test.ts} +201 -201
- package/test/{v3-live.test.ts → live.test.ts} +18 -18
- package/test/{v3-material.test.ts → material.test.ts} +12 -12
- package/test/{v3-maturity.test.ts → maturity.test.ts} +74 -74
- package/test/modeller-route.test.ts +5 -6
- package/test/{v3-palette-icons.test.ts → palette-icons.test.ts} +1 -1
- package/test/{v3-parts-wiring.test.ts → parts-wiring.test.ts} +11 -7
- package/test/{v3-preview.test.ts → preview.test.ts} +12 -12
- package/test/{v3-product-session.test.ts → product-session.test.ts} +17 -17
- package/test/{v3-product-write.test.ts → product-write.test.ts} +20 -20
- package/test/{v3-properties.test.ts → properties.test.ts} +24 -24
- package/test/{v3-recipes.test.ts → recipes.test.ts} +10 -10
- package/test/refusals.test.ts +166 -0
- package/test/{v3-release-panel.test.ts → release-panel.test.ts} +21 -21
- package/test/renderer.test.ts +24 -0
- package/test/resolve-ts-siblings.mjs +1 -1
- package/test/{v3-scene-canvas.test.ts → scene-canvas.test.ts} +6 -6
- package/test/{v3-stage-model.test.ts → stage-model.test.ts} +5 -5
- package/test/{v3-theme.test.ts → theme.test.ts} +4 -4
- package/test/{v3-thumbnail.test.ts → thumbnail.test.ts} +4 -4
- package/test/{v3-verdict.test.ts → verdict.test.ts} +17 -17
- package/test/{v3-view.test.ts → view.test.ts} +2 -2
- package/things-factory.config.js +2 -3
- package/translations/en.json +15 -7
- package/translations/ja.json +15 -7
- package/translations/ko.json +15 -7
- package/translations/ms.json +15 -7
- package/translations/zh.json +15 -7
- package/docs/v3-product-cutover.md +0 -25
- package/test/v3-refusals.test.ts +0 -136
- package/test/v3-renderer.test.ts +0 -24
- /package/client/modeller/{v3-fields.ts → fields.ts} +0 -0
- /package/client/modeller/{v3-first-save.ts → first-save.ts} +0 -0
- /package/client/modeller/{v3-help.ts → help.ts} +0 -0
- /package/client/modeller/{v3-keys.ts → keys.ts} +0 -0
|
@@ -4,41 +4,41 @@ import type { OxThreePane } from '@operato/layout'
|
|
|
4
4
|
import '@operato/layout'
|
|
5
5
|
import { customElement, property, state } from 'lit/decorators.js'
|
|
6
6
|
import { live } from 'lit/directives/live.js'
|
|
7
|
-
import { PART_LIMIT,
|
|
8
|
-
import type { RecipeTemplate } from './
|
|
9
|
-
import './figure-
|
|
10
|
-
import type {
|
|
11
|
-
import type {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import './
|
|
15
|
-
import './figure-
|
|
16
|
-
import './figure-
|
|
17
|
-
import './figure-
|
|
18
|
-
import './figure-
|
|
19
|
-
import './figure-
|
|
20
|
-
import './figure-
|
|
7
|
+
import { PART_LIMIT, parseFigureRecipe, resolveFigureRecipe, figurePartCountOf, figureRecipeIdFor } from '@hatiolab/figure-model'
|
|
8
|
+
import type { RecipeTemplate } from './recipes.js'
|
|
9
|
+
import './figure-recipe-catalog.js'
|
|
10
|
+
import type { FigureAsset, FigureAuthoringAction, FigureInputChange, FigurePartEdit, FigurePrimitiveCreation, FigureProposalStep, FigureRecipe, FigureRecipeValue } from '@hatiolab/figure-model'
|
|
11
|
+
import type { FigureProductSession } from './product-session.js'
|
|
12
|
+
import { captureFigureThumbnail } from './thumbnail.js'
|
|
13
|
+
import { drawableFigureParts } from './renderer.js'
|
|
14
|
+
import './canvas.js'
|
|
15
|
+
import './figure-parts.js'
|
|
16
|
+
import './figure-material-panel.js'
|
|
17
|
+
import './figure-links.js'
|
|
18
|
+
import './figure-controls.js'
|
|
19
|
+
import './figure-clips.js'
|
|
20
|
+
import './figure-preview.js'
|
|
21
21
|
import './figure-settings.js'
|
|
22
22
|
import { DEFAULT_VIEW, type ViewSettings } from './figure-view.js'
|
|
23
|
-
import './figure-
|
|
24
|
-
import { gizmoChanges, gizmoReachOf, keepBottomChanges, type GizmoReach, type GizmoResult } from './
|
|
25
|
-
import { keyActionOf } from './
|
|
26
|
-
import { groupFields, ownerOf, partLabelOf, repeatOf, type FieldLike, type FieldGroup } from './
|
|
23
|
+
import './figure-capability-panel.js'
|
|
24
|
+
import { gizmoChanges, gizmoReachOf, keepBottomChanges, type GizmoReach, type GizmoResult } from './gizmo.js'
|
|
25
|
+
import { keyActionOf } from './keys.js'
|
|
26
|
+
import { groupFields, ownerOf, partLabelOf, repeatOf, type FieldLike, type FieldGroup } from './fields.js'
|
|
27
27
|
import { SidePanelStyles } from './side-panel-styles.js'
|
|
28
28
|
import { ScrollbarStyles } from '@operato/styles'
|
|
29
|
-
import { alignChanges, boxOf, canMirror, mirrorChanges, type AlignTool } from './
|
|
30
|
-
import type {
|
|
31
|
-
import { paletteRecipe } from './
|
|
32
|
-
import type {
|
|
33
|
-
import { nudgeOf } from './
|
|
34
|
-
import { inDeclaredRoom, inputRangeRefusalOf, narrowingRetryOf, partRefusalOf, placementRefusalOf, sizeRangeRefusalOf } from './
|
|
35
|
-
import type {
|
|
36
|
-
import type {
|
|
37
|
-
import { instanceRangeOf, instanceRangeToDeclare, proportionToFollow, rangeDiffers, rangeWith, turnsAboutAJoint, valueNow } from './
|
|
38
|
-
import { jointFanOf } from './
|
|
39
|
-
import { jointDragOf } from './
|
|
40
|
-
import type { FirstSave } from './
|
|
41
|
-
import { isLive, livePartsOf, liveOverSession, NOTHING_LIVE, scrubbed, scrubStep, type LiveValues } from './
|
|
29
|
+
import { alignChanges, boxOf, canMirror, mirrorChanges, type AlignTool } from './align.js'
|
|
30
|
+
import type { FigurePaletteKind, FigurePartRow } from './figure-parts.js'
|
|
31
|
+
import { paletteRecipe } from './palette.js'
|
|
32
|
+
import type { FigureCanvas } from './canvas.js'
|
|
33
|
+
import { nudgeOf } from './stage-model.js'
|
|
34
|
+
import { editRefusalOf, inDeclaredRoom, inputRangeRefusalOf, narrowingRetryOf, partRefusalOf, placementRefusalOf, recipeRefusalOf, removalNoticeOf, sizeRangeRefusalOf } from './refusals.js'
|
|
35
|
+
import type { FigureRenderPart } from './renderer.js'
|
|
36
|
+
import type { FigureBudgetLine } from './maturity.js'
|
|
37
|
+
import { instanceRangeOf, instanceRangeToDeclare, proportionToFollow, rangeDiffers, rangeWith, turnsAboutAJoint, valueNow } from './links.js'
|
|
38
|
+
import { jointFanOf } from './joint-fan.js'
|
|
39
|
+
import { jointDragOf } from './joint-drag.js'
|
|
40
|
+
import type { FirstSave } from './first-save.js'
|
|
41
|
+
import { isLive, livePartsOf, liveOverSession, NOTHING_LIVE, scrubbed, scrubStep, type LiveValues } from './live.js'
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -48,8 +48,8 @@ import { isLive, livePartsOf, liveOverSession, NOTHING_LIVE, scrubbed, scrubStep
|
|
|
48
48
|
type SideTab = 'properties' | 'capability' | 'motion' | 'verdict' | 'view' | 'history'
|
|
49
49
|
|
|
50
50
|
/** Product editor surface. The injected session is shared by page persistence and AI commands. */
|
|
51
|
-
@customElement('figure-
|
|
52
|
-
export class
|
|
51
|
+
@customElement('figure-editor')
|
|
52
|
+
export class FigureEditor extends LitElement {
|
|
53
53
|
/* The operato scrollbar, as every scrolling panel of the app has it -- the side panel scrolled with the browser's own. */
|
|
54
54
|
static styles = [ScrollbarStyles, SidePanelStyles, css`
|
|
55
55
|
:host {
|
|
@@ -105,7 +105,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
105
105
|
opacity: 0.38;
|
|
106
106
|
cursor: default;
|
|
107
107
|
}
|
|
108
|
-
figure-
|
|
108
|
+
figure-preview {
|
|
109
109
|
min-width: 0;
|
|
110
110
|
min-height: 0;
|
|
111
111
|
}
|
|
@@ -131,10 +131,10 @@ export class FigureV3Editor extends LitElement {
|
|
|
131
131
|
flex: 1;
|
|
132
132
|
min-height: 0;
|
|
133
133
|
}
|
|
134
|
-
figure-
|
|
134
|
+
figure-parts {
|
|
135
135
|
min-height: 0;
|
|
136
136
|
}
|
|
137
|
-
figure-
|
|
137
|
+
figure-canvas {
|
|
138
138
|
min-width: 0;
|
|
139
139
|
min-height: 0;
|
|
140
140
|
}
|
|
@@ -263,7 +263,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
263
263
|
line-height: 1.5;
|
|
264
264
|
}
|
|
265
265
|
`]
|
|
266
|
-
@property({ attribute: false }) session?:
|
|
266
|
+
@property({ attribute: false }) session?: FigureProductSession
|
|
267
267
|
/** The recipe templates (ADR-0095), read by the page from the server. */
|
|
268
268
|
@property({ attribute: false }) templates: RecipeTemplate[] = []
|
|
269
269
|
/** Reads a template's guide; given by the page. */
|
|
@@ -278,7 +278,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
278
278
|
/** The verdict's count, drawn on its tab so it stays in sight while another tab is open. */
|
|
279
279
|
@property({ type: Number }) instances?: number
|
|
280
280
|
/** The cost line the parts list shows, from the maturity report. */
|
|
281
|
-
@property({ attribute: false }) budget?:
|
|
281
|
+
@property({ attribute: false }) budget?: FigureBudgetLine
|
|
282
282
|
/**
|
|
283
283
|
* Asks the type and name of a figure saved for the first time (the host has the catalogue's names); undefined
|
|
284
284
|
* when the author cancels, and then nothing is saved. Without it a new figure is saved under a temporary type.
|
|
@@ -287,7 +287,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
287
287
|
@state() private selected = ''
|
|
288
288
|
@state() private error = ''
|
|
289
289
|
/** The same link offered again with the size range narrowed, after a link the declared range could not hold. */
|
|
290
|
-
@state() private retry?: { label: string; action?:
|
|
290
|
+
@state() private retry?: { label: string; action?: FigureAuthoringAction; run?: () => void }
|
|
291
291
|
@state() private tab: SideTab = 'properties'
|
|
292
292
|
/** The frame the bar folds - found after the first render, since the bar is drawn before it. */
|
|
293
293
|
@state() private panes?: OxThreePane
|
|
@@ -301,10 +301,10 @@ export class FigureV3Editor extends LitElement {
|
|
|
301
301
|
* How the workbench shows the figure, in the board's own state names. The viewer's, kept in
|
|
302
302
|
* their browser, never in the figure.
|
|
303
303
|
*/
|
|
304
|
-
@state() private view: ViewSettings =
|
|
304
|
+
@state() private view: ViewSettings = FigureEditor.storedView()
|
|
305
305
|
private static storedView(): ViewSettings {
|
|
306
306
|
try {
|
|
307
|
-
const stored = JSON.parse(localStorage.getItem('figure-
|
|
307
|
+
const stored = JSON.parse(localStorage.getItem('figure-view') ?? 'null')
|
|
308
308
|
return stored && typeof stored === 'object' ? { ...DEFAULT_VIEW, ...stored } : { ...DEFAULT_VIEW }
|
|
309
309
|
} catch {
|
|
310
310
|
return { ...DEFAULT_VIEW }
|
|
@@ -313,7 +313,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
313
313
|
private setView(view: ViewSettings) {
|
|
314
314
|
this.view = { ...DEFAULT_VIEW, ...view }
|
|
315
315
|
try {
|
|
316
|
-
localStorage.setItem('figure-
|
|
316
|
+
localStorage.setItem('figure-view', JSON.stringify(this.view))
|
|
317
317
|
} catch {
|
|
318
318
|
/* A browser that keeps nothing still shows the change for this visit. */
|
|
319
319
|
}
|
|
@@ -353,11 +353,11 @@ export class FigureV3Editor extends LitElement {
|
|
|
353
353
|
)}
|
|
354
354
|
</span>`
|
|
355
355
|
}
|
|
356
|
-
/** The modeller's keys (`
|
|
356
|
+
/** The modeller's keys (`keys.ts`): undo, redo, duplicate, remove, back to the whole figure, the gizmo's modes. */
|
|
357
357
|
private onKey = (e: KeyboardEvent) => {
|
|
358
358
|
const view = this.session?.read()
|
|
359
359
|
if (!view || view.loading || view.saving || !view.context || !this.isConnected) return
|
|
360
|
-
const canvas = this.renderRoot.querySelector<
|
|
360
|
+
const canvas = this.renderRoot.querySelector<FigureCanvas>('figure-canvas')
|
|
361
361
|
const path = e.composedPath()
|
|
362
362
|
const action = keyActionOf(
|
|
363
363
|
{ key: e.key, ctrlKey: e.ctrlKey, metaKey: e.metaKey, shiftKey: e.shiftKey, altKey: e.altKey, target: path[0] as HTMLElement, inScene: !!canvas && path.includes(canvas) },
|
|
@@ -399,32 +399,41 @@ export class FigureV3Editor extends LitElement {
|
|
|
399
399
|
}
|
|
400
400
|
/** The scene toggles: the mode already shown takes the gizmo away. */
|
|
401
401
|
private useGizmo(mode: 'translate' | 'rotate' | 'scale') {
|
|
402
|
-
this.renderRoot.querySelector<
|
|
402
|
+
this.renderRoot.querySelector<FigureCanvas>('figure-canvas')?.gizmo(mode)
|
|
403
403
|
this.gizmoMode = this.gizmoMode === mode ? undefined : mode
|
|
404
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* A refusal no particular sentence knows: figure-model's code said as a refusal, never its English; any other error
|
|
407
|
+
* (the network, the server) as it came.
|
|
408
|
+
*/
|
|
409
|
+
private said(error: unknown): string {
|
|
410
|
+
return (error as { code?: string })?.code ? editRefusalOf(error, (key, params) => i18next.t(key, params)) : error instanceof Error ? error.message : String(error)
|
|
411
|
+
}
|
|
405
412
|
private run(action: () => unknown) {
|
|
406
413
|
try {
|
|
407
414
|
action()
|
|
408
415
|
this.error = ''
|
|
416
|
+
/* The next step stands on its own: what the last removal let go of is no longer the news. */
|
|
417
|
+
this.notice = ''
|
|
409
418
|
this.changed()
|
|
410
419
|
} catch (error) {
|
|
411
420
|
const say = (key: string, params: Record<string, string>) => i18next.t(key, params)
|
|
412
|
-
this.error = sizeRangeRefusalOf(error, say) ?? inputRangeRefusalOf(error, say) ??
|
|
421
|
+
this.error = sizeRangeRefusalOf(error, say) ?? inputRangeRefusalOf(error, say) ?? this.said(error)
|
|
413
422
|
} finally {
|
|
414
423
|
/* Committed or refused, the value in hand is gone: the figure as it stands is drawn again. */
|
|
415
424
|
this.live = NOTHING_LIVE
|
|
416
425
|
}
|
|
417
426
|
}
|
|
418
427
|
/** What the canvas draws: the figure with the value in hand when there is one, and without the parts hidden on the workbench. */
|
|
419
|
-
private drawnParts(geometry: Parameters<typeof
|
|
420
|
-
const parts = (asset ? liveOverSession(asset as
|
|
428
|
+
private drawnParts(geometry: Parameters<typeof drawableFigureParts>[0] | undefined, asset: unknown) {
|
|
429
|
+
const parts = (asset ? liveOverSession(asset as FigureAsset, this.currentInputs(), this.live) : null) ?? drawableFigureParts(geometry ?? [])
|
|
421
430
|
return parts.filter(body => !this.hiddenParts.has(ownerOf((body as { id: string }).id)))
|
|
422
431
|
}
|
|
423
432
|
/**
|
|
424
433
|
* Where the selected part turns and how far, with the pose as it is drawn now -- a value in hand included, so the
|
|
425
|
-
* fan follows the arm while a control is dragged. Its size comes from what the joint turns (`
|
|
434
|
+
* fan follows the arm while a control is dragged. Its size comes from what the joint turns (`joint-fan`).
|
|
426
435
|
*/
|
|
427
|
-
private fanOf(asset:
|
|
436
|
+
private fanOf(asset: FigureAsset | undefined) {
|
|
428
437
|
if (!asset || !this.selected || this.hiddenParts.has(this.selected)) return null
|
|
429
438
|
try {
|
|
430
439
|
return jointFanOf(asset, this.selected, { ...this.currentInputs(), ...this.live.values })
|
|
@@ -442,12 +451,12 @@ export class FigureV3Editor extends LitElement {
|
|
|
442
451
|
return view ? Object.fromEntries(view.fields.map(field => [field.id, field.value as number])) : {}
|
|
443
452
|
}
|
|
444
453
|
/** A value in hand: an input's value (design or pose) or a part edit. Nothing is recorded. */
|
|
445
|
-
private hold(values: Record<string, number>, edit?:
|
|
454
|
+
private hold(values: Record<string, number>, edit?: FigurePartEdit) {
|
|
446
455
|
this.live = { values: { ...this.live.values, ...values }, ...(edit ? { edit } : this.live.edit ? { edit: this.live.edit } : {}) }
|
|
447
456
|
}
|
|
448
457
|
private changed() {
|
|
449
458
|
this.requestUpdate()
|
|
450
|
-
this.dispatchEvent(new CustomEvent('
|
|
459
|
+
this.dispatchEvent(new CustomEvent('changed', { bubbles: true, composed: true }))
|
|
451
460
|
}
|
|
452
461
|
private async save() {
|
|
453
462
|
if (!this.session) return
|
|
@@ -455,7 +464,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
455
464
|
/*
|
|
456
465
|
The card's picture is taken before the save starts, from a canvas of its own at the card's size —
|
|
457
466
|
not from the one on screen, which stands wherever the author left it turned. A picture that cannot
|
|
458
|
-
be taken is not an error: the save goes on without it (see `
|
|
467
|
+
be taken is not an error: the save goes on without it (see `thumbnail.ts`).
|
|
459
468
|
*/
|
|
460
469
|
let type: string | undefined
|
|
461
470
|
if (!this.session.read().id && this.firstSave) {
|
|
@@ -469,18 +478,18 @@ export class FigureV3Editor extends LitElement {
|
|
|
469
478
|
type = answer.type
|
|
470
479
|
}
|
|
471
480
|
const view = this.session.read()
|
|
472
|
-
const thumbnail = await
|
|
481
|
+
const thumbnail = await captureFigureThumbnail(drawableFigureParts(view.geometry ?? []), view.palette ?? {})
|
|
473
482
|
const promise = this.session.save({ thumbnail, type })
|
|
474
483
|
this.changed()
|
|
475
484
|
await promise
|
|
476
485
|
this.error = ''
|
|
477
486
|
} catch (error) {
|
|
478
|
-
this.error =
|
|
487
|
+
this.error = this.said(error)
|
|
479
488
|
} finally {
|
|
480
489
|
this.changed()
|
|
481
490
|
}
|
|
482
491
|
}
|
|
483
|
-
private add(kind:
|
|
492
|
+
private add(kind: FigurePaletteKind) {
|
|
484
493
|
if (!this.session) return
|
|
485
494
|
this.run(() => {
|
|
486
495
|
const ids = new Set(this.session!.read().geometry.map(g => g.id))
|
|
@@ -492,7 +501,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
492
501
|
})
|
|
493
502
|
}
|
|
494
503
|
/**
|
|
495
|
-
* The list's own operations, as
|
|
504
|
+
* The list's own operations, as part edits — each one step of the session's history. The copy and the moved
|
|
496
505
|
* part stay selected.
|
|
497
506
|
*/
|
|
498
507
|
private duplicatePart(id: string) {
|
|
@@ -508,13 +517,15 @@ export class FigureV3Editor extends LitElement {
|
|
|
508
517
|
private removePart(id: string, refitRoom = false) {
|
|
509
518
|
this.retry = undefined
|
|
510
519
|
try {
|
|
511
|
-
this.session!.edit({ kind: 'remove-part', target: id, ...(refitRoom ? { refitRoom: true } : {}) })
|
|
520
|
+
const view = this.session!.edit({ kind: 'remove-part', target: id, ...(refitRoom ? { refitRoom: true } : {}) }) as { removal?: Parameters<typeof removalNoticeOf>[0] }
|
|
512
521
|
this.error = ''
|
|
522
|
+
/* What else the removal let go of, named (chief architect, 2026-09-27); undo brings it back. */
|
|
523
|
+
this.notice = removalNoticeOf(view.removal, (key, params) => i18next.t(key, params)) ?? ''
|
|
513
524
|
if (this.selected === id) this.selected = ''
|
|
514
525
|
this.changed()
|
|
515
526
|
} catch (error) {
|
|
516
527
|
/* The refusal a person meets most: another part stands on this one or follows its size. Said in their words. */
|
|
517
|
-
this.error = partRefusalOf(error, id, (key, params) => i18next.t(key, params)) ??
|
|
528
|
+
this.error = partRefusalOf(error, id, (key, params) => i18next.t(key, params)) ?? this.said(error)
|
|
518
529
|
/* In the declared room only: the way on is to delete it and fit the room to the parts left, in one step. */
|
|
519
530
|
if (!refitRoom && inDeclaredRoom(error)) this.retry = { label: i18next.t('figure.button.remove-and-refit-room', { part: id }), run: () => this.removePart(id, true) }
|
|
520
531
|
}
|
|
@@ -534,19 +545,19 @@ export class FigureV3Editor extends LitElement {
|
|
|
534
545
|
* A recipe put in (ADR-0095): resolved on the figure as it stands -- its parts take the next free name, onto the part
|
|
535
546
|
* chosen -- and applied as one step. Refused, the kernel's sentence goes to the form as it is (condition 3).
|
|
536
547
|
*/
|
|
537
|
-
private insertRecipe(recipe:
|
|
548
|
+
private insertRecipe(recipe: FigureRecipe, values: Record<string, FigureRecipeValue>) {
|
|
538
549
|
try {
|
|
539
|
-
const asset = JSON.parse(this.session!.source()) as
|
|
540
|
-
const checked =
|
|
541
|
-
const id =
|
|
542
|
-
const steps =
|
|
550
|
+
const asset = JSON.parse(this.session!.source()) as FigureAsset
|
|
551
|
+
const checked = parseFigureRecipe(recipe)
|
|
552
|
+
const id = figureRecipeIdFor(asset, checked)
|
|
553
|
+
const steps = resolveFigureRecipe(checked, { id, ...(checked.target === 'part' ? { target: this.selected } : {}), values })
|
|
543
554
|
this.session!.applyRecipe(steps)
|
|
544
555
|
this.recipeError = ''
|
|
545
556
|
this.catalogOpen = false
|
|
546
557
|
this.error = ''
|
|
547
558
|
this.changed()
|
|
548
559
|
} catch (error) {
|
|
549
|
-
this.recipeError = error
|
|
560
|
+
this.recipeError = recipeRefusalOf(error, (key, params) => i18next.t(key, params)) ?? this.said(error)
|
|
550
561
|
}
|
|
551
562
|
}
|
|
552
563
|
/** The design values of the asset being edited. */
|
|
@@ -572,8 +583,8 @@ export class FigureV3Editor extends LitElement {
|
|
|
572
583
|
private transformed(id: string, result: GizmoResult) {
|
|
573
584
|
const view = this.session!.read()
|
|
574
585
|
/* A part hanging from a joint: the drag edits the joint -- its value, or where it sits (ADR-0093). */
|
|
575
|
-
const asset = JSON.parse(this.session!.source()) as
|
|
576
|
-
const drawn = view.geometry.find(g => g.id === id) as unknown as
|
|
586
|
+
const asset = JSON.parse(this.session!.source()) as FigureAsset
|
|
587
|
+
const drawn = view.geometry.find(g => g.id === id) as unknown as FigureRenderPart | undefined
|
|
577
588
|
const drag = drawn ? jointDragOf(asset, ownerOf(id), drawn, result, this.currentInputs()) : null
|
|
578
589
|
if (drag) {
|
|
579
590
|
if (drag.kind === 'value') this.run(() => this.session!.apply([{ kind: 'set-state', input: drag.input, unit: drag.unit, value: drag.value }]))
|
|
@@ -607,7 +618,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
607
618
|
its rule (`nudge-part`). A part a joint or a fastening places, or a size that is worked out, is refused -- said in
|
|
608
619
|
words, not by the part silently springing back (user, 2026-09-24: 「기즈모 컨트롤도 전혀 작동이 안되는군」).
|
|
609
620
|
*/
|
|
610
|
-
const body = view.geometry.find(g => g.id === id) as unknown as
|
|
621
|
+
const body = view.geometry.find(g => g.id === id) as unknown as FigureRenderPart | undefined
|
|
611
622
|
if (!body) return this.requestUpdate()
|
|
612
623
|
const { by, turn } = nudgeOf(body, result)
|
|
613
624
|
/* A size worked out from the figure's: the drag's factor beside its rule, so it still follows. */
|
|
@@ -630,7 +641,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
630
641
|
? i18next.t('figure.text.gizmo-joint-places', { part: partLabelOf(id) })
|
|
631
642
|
: /is fastened to/.test(message)
|
|
632
643
|
? i18next.t('figure.text.gizmo-fastening-places', { part: partLabelOf(id) })
|
|
633
|
-
:
|
|
644
|
+
: this.said(error)
|
|
634
645
|
this.requestUpdate()
|
|
635
646
|
}
|
|
636
647
|
}
|
|
@@ -734,15 +745,15 @@ export class FigureV3Editor extends LitElement {
|
|
|
734
745
|
} catch {
|
|
735
746
|
dims = undefined
|
|
736
747
|
}
|
|
737
|
-
return html`<figure-
|
|
748
|
+
return html`<figure-links
|
|
738
749
|
.asset=${asset}
|
|
739
750
|
.section=${section}
|
|
740
751
|
.partId=${this.selected}
|
|
741
752
|
.dims=${dims}
|
|
742
753
|
?locked=${unavailable}
|
|
743
|
-
@
|
|
744
|
-
@
|
|
745
|
-
></figure-
|
|
754
|
+
@command=${(e: CustomEvent) => this.linkCommand(e.detail.action)}
|
|
755
|
+
@part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
|
|
756
|
+
></figure-links>`
|
|
746
757
|
}
|
|
747
758
|
/** The figure's controls: try a value, name it, set its range and travel time, keep the pose. */
|
|
748
759
|
private renderControls(view: { fields: Array<{ id: string; role?: string; value: number }> }, unavailable: boolean) {
|
|
@@ -753,25 +764,25 @@ export class FigureV3Editor extends LitElement {
|
|
|
753
764
|
return null
|
|
754
765
|
}
|
|
755
766
|
const values = Object.fromEntries(view.fields.filter(f => f.role === 'state').map(f => [f.id, f.value]))
|
|
756
|
-
return html`<figure-
|
|
767
|
+
return html`<figure-controls
|
|
757
768
|
.asset=${asset}
|
|
758
769
|
.values=${values}
|
|
759
770
|
?locked=${unavailable}
|
|
760
|
-
@
|
|
761
|
-
@
|
|
762
|
-
@
|
|
763
|
-
></figure-
|
|
764
|
-
<figure-
|
|
771
|
+
@state=${(e: CustomEvent) => this.run(() => this.session!.apply([{ kind: 'set-state', input: e.detail.input, unit: e.detail.unit, value: e.detail.value }]))}
|
|
772
|
+
@state-live=${(e: CustomEvent) => this.hold({ [e.detail.input]: e.detail.value })}
|
|
773
|
+
@part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
|
|
774
|
+
></figure-controls>
|
|
775
|
+
<figure-clips
|
|
765
776
|
.asset=${asset}
|
|
766
777
|
.values=${values}
|
|
767
778
|
?locked=${unavailable}
|
|
768
|
-
@
|
|
769
|
-
@
|
|
770
|
-
@
|
|
771
|
-
></figure-
|
|
779
|
+
@states=${(e: CustomEvent) => this.run(() => this.session!.apply(e.detail.changes))}
|
|
780
|
+
@states-live=${(e: CustomEvent) => this.hold(e.detail.values)}
|
|
781
|
+
@part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
|
|
782
|
+
></figure-clips>`
|
|
772
783
|
}
|
|
773
784
|
/** One command, one step of the history. A refusal a person can meet here is said in their words. */
|
|
774
|
-
private linkCommand(action:
|
|
785
|
+
private linkCommand(action: FigureAuthoringAction) {
|
|
775
786
|
this.retry = undefined
|
|
776
787
|
this.notice = ''
|
|
777
788
|
const hadSize = this.hasSize()
|
|
@@ -793,7 +804,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
793
804
|
const narrowing = narrowingRetryOf(action, error, (key, params) => i18next.t(key, params))
|
|
794
805
|
if (narrowing) {
|
|
795
806
|
this.error = narrowing.message
|
|
796
|
-
this.retry = { label: narrowing.label, action: narrowing.retry as
|
|
807
|
+
this.retry = { label: narrowing.label, action: narrowing.retry as FigureAuthoringAction }
|
|
797
808
|
return
|
|
798
809
|
}
|
|
799
810
|
this.error =
|
|
@@ -803,10 +814,10 @@ export class FigureV3Editor extends LitElement {
|
|
|
803
814
|
? i18next.t('figure.text.fasten-would-loop')
|
|
804
815
|
: sizeRangeRefusalOf(error, (key, params) => i18next.t(key, params)) ??
|
|
805
816
|
placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??
|
|
806
|
-
|
|
817
|
+
this.said(error)
|
|
807
818
|
}
|
|
808
819
|
}
|
|
809
|
-
private changeField(field: { id: string; unit:
|
|
820
|
+
private changeField(field: { id: string; unit: FigureInputChange['unit']; role?: string }, event: Event) {
|
|
810
821
|
const input = event.target as HTMLInputElement,
|
|
811
822
|
value = input.valueAsNumber
|
|
812
823
|
/* A height typed in keeps the part's bottom where it is, as the scale gizmo does. */
|
|
@@ -830,7 +841,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
830
841
|
* Drag a field's label (or an axis letter) sideways to scrub its value, as vector tools do: the figure follows the
|
|
831
842
|
* pointer, and the release records one step. The field's number follows too, so the value in hand is always read.
|
|
832
843
|
*/
|
|
833
|
-
private scrub(field: { id: string; unit:
|
|
844
|
+
private scrub(field: { id: string; unit: FigureInputChange['unit']; role?: string; value: number }, down: PointerEvent, unavailable: boolean) {
|
|
834
845
|
if (unavailable || down.button !== 0) return
|
|
835
846
|
const label = down.currentTarget as HTMLElement
|
|
836
847
|
const input = (label.closest('div[cell]')?.querySelector('input') ?? label.nextElementSibling) as HTMLInputElement | null
|
|
@@ -865,7 +876,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
865
876
|
/** The measured range, kept for the revision it was measured at: measuring evaluates the figure at its edges. */
|
|
866
877
|
private range?: { at: string; of: ReturnType<typeof instanceRangeOf>; toDeclare?: ReturnType<typeof instanceRangeToDeclare> }
|
|
867
878
|
/** The sizes a board may give the figure, and the way to measure them again after its parts changed. */
|
|
868
|
-
private renderInstanceRange(asset:
|
|
879
|
+
private renderInstanceRange(asset: FigureAsset | undefined, unavailable: boolean) {
|
|
869
880
|
if (!asset) return nothing
|
|
870
881
|
/* Another figure, or another step of this one, is measured again. */
|
|
871
882
|
const at = `${this.session?.read().context ?? ''}:${this.revision}`
|
|
@@ -940,7 +951,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
940
951
|
* How the figure grows. One ratio (`sizing: 'uniform'`) is said as it is; a figure with joints that has not declared
|
|
941
952
|
* it is offered it -- a mechanism does not shear -- and the author decides (chief architect's ruling 2026-09-24).
|
|
942
953
|
*/
|
|
943
|
-
private renderSizingRule(asset:
|
|
954
|
+
private renderSizingRule(asset: FigureAsset, unavailable: boolean) {
|
|
944
955
|
if (asset.sizing === 'uniform') {
|
|
945
956
|
/* One ratio reaches only what reads the size: every other length is offered to follow it, in one step. */
|
|
946
957
|
const follow = proportionToFollow(asset)
|
|
@@ -962,8 +973,8 @@ export class FigureV3Editor extends LitElement {
|
|
|
962
973
|
* The parts list, one row per part the author made: a repeat is one row with how many copies it makes now
|
|
963
974
|
* (user, 2026-09-24: the conveyor's sixteen rollers were sixteen rows).
|
|
964
975
|
*/
|
|
965
|
-
private partRows(geometry: readonly { id: string }[]):
|
|
966
|
-
const rows = new Map<string,
|
|
976
|
+
private partRows(geometry: readonly { id: string }[]): FigurePartRow[] {
|
|
977
|
+
const rows = new Map<string, FigurePartRow>()
|
|
967
978
|
for (const g of geometry) {
|
|
968
979
|
const id = ownerOf(g.id)
|
|
969
980
|
const row = rows.get(id)
|
|
@@ -982,10 +993,10 @@ export class FigureV3Editor extends LitElement {
|
|
|
982
993
|
return [...rows.values()]
|
|
983
994
|
}
|
|
984
995
|
/** The detail level's part budget for the list's head, from figure-model's count (a repeat once). */
|
|
985
|
-
private partBudgetOf(asset:
|
|
996
|
+
private partBudgetOf(asset: FigureAsset | undefined) {
|
|
986
997
|
if (!asset) return undefined
|
|
987
998
|
try {
|
|
988
|
-
return { level: asset.detailLevel ?? null, used:
|
|
999
|
+
return { level: asset.detailLevel ?? null, used: figurePartCountOf(asset), limit: asset.detailLevel ? PART_LIMIT[asset.detailLevel] : null }
|
|
989
1000
|
} catch {
|
|
990
1001
|
return undefined
|
|
991
1002
|
}
|
|
@@ -995,7 +1006,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
995
1006
|
* The pitch of a row that fills its length is edited as a number (`set-repeat` makes it the part's own the first
|
|
996
1007
|
* time); a row of a fixed count says its pitch cannot be set yet (figure-model refuses it the same way).
|
|
997
1008
|
*/
|
|
998
|
-
private renderRepeat(asset:
|
|
1009
|
+
private renderRepeat(asset: FigureAsset | undefined, geometry: readonly { id: string }[], unavailable: boolean) {
|
|
999
1010
|
const repeat = repeatOf(this.selected, geometry)
|
|
1000
1011
|
if (!asset || !repeat || repeat.index) return nothing
|
|
1001
1012
|
/* The row is set on its part; what hangs from that part is copied with it and has no row of its own. */
|
|
@@ -1051,7 +1062,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
1051
1062
|
</section>`
|
|
1052
1063
|
}
|
|
1053
1064
|
/** The design input a row's pitch is read from, when it is read from one (ADR-0096). */
|
|
1054
|
-
private pitchInputOf(asset:
|
|
1065
|
+
private pitchInputOf(asset: FigureAsset, part: string): string | undefined {
|
|
1055
1066
|
const pitch = asset.joints?.find(j => j.body1.part === part && j.repeat)?.repeat?.pitch as { from?: { of?: string; input?: string } } | number | undefined
|
|
1056
1067
|
return typeof pitch === 'object' && pitch?.from?.of === 'input' ? pitch.from.input : undefined
|
|
1057
1068
|
}
|
|
@@ -1059,7 +1070,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
1059
1070
|
* 「보드에서 바꿀 수 있음」 (ADR-0096): the row's pitch made a design input the figure publishes -- a name on the board and
|
|
1060
1071
|
* a range the board's value is held to -- in one step; changed or withdrawn the same way. Only a row with a record.
|
|
1061
1072
|
*/
|
|
1062
|
-
private renderPublicPitch(asset:
|
|
1073
|
+
private renderPublicPitch(asset: FigureAsset, pitch: number, unavailable: boolean) {
|
|
1063
1074
|
const part = this.selected
|
|
1064
1075
|
if (!asset.joints?.some(j => j.body1.part === part && j.repeat)) return nothing
|
|
1065
1076
|
const from = this.pitchInputOf(asset, part)
|
|
@@ -1077,25 +1088,25 @@ export class FigureV3Editor extends LitElement {
|
|
|
1077
1088
|
const base = form ?? { part, label: '', min: 0, max: 0 }
|
|
1078
1089
|
this.publishing = { ...base, [key]: key === 'label' ? target.value : target.valueAsNumber }
|
|
1079
1090
|
}
|
|
1080
|
-
const steps = ():
|
|
1091
|
+
const steps = (): FigureProposalStep[] => {
|
|
1081
1092
|
const f = form!
|
|
1082
1093
|
const id = from ?? `${part}.pitch`
|
|
1083
|
-
if (listed) return [{ kind: 'set-input-range', input: id, min: f.min, max: f.max }, { kind: 'publish-input', input: id, label: f.label }] as
|
|
1094
|
+
if (listed) return [{ kind: 'set-input-range', input: id, min: f.min, max: f.max }, { kind: 'publish-input', input: id, label: f.label }] as FigureProposalStep[]
|
|
1084
1095
|
const exists = asset.document.model.inputs.some(i => i.id === id)
|
|
1085
1096
|
return [
|
|
1086
1097
|
...(exists ? [{ kind: 'set-input-range', input: id, min: f.min, max: f.max }] : [{ kind: 'add-design-input', id, unit: 'mm', min: f.min, max: f.max, value: pitch }]),
|
|
1087
1098
|
...(from ? [] : [{ kind: 'set-repeat', part, pitch: { from: { of: 'input', input: id } } }]),
|
|
1088
1099
|
{ kind: 'publish-input', input: id, label: f.label }
|
|
1089
|
-
] as
|
|
1100
|
+
] as FigureProposalStep[]
|
|
1090
1101
|
}
|
|
1091
|
-
const run = (list:
|
|
1102
|
+
const run = (list: FigureProposalStep[]) => {
|
|
1092
1103
|
try {
|
|
1093
1104
|
this.session!.applyRecipe(list)
|
|
1094
1105
|
this.publishing = undefined
|
|
1095
1106
|
this.error = ''
|
|
1096
1107
|
this.changed()
|
|
1097
1108
|
} catch (error) {
|
|
1098
|
-
this.error =
|
|
1109
|
+
this.error = this.said(error)
|
|
1099
1110
|
}
|
|
1100
1111
|
}
|
|
1101
1112
|
return html`<div public-pitch>
|
|
@@ -1109,7 +1120,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
1109
1120
|
(e.target as HTMLInputElement).checked
|
|
1110
1121
|
? (this.publishing = { part, label: i18next.t('figure.label.repeat-gap'), min: Math.max(1, Math.round(pitch / 2)), max: Math.round(pitch * 2) })
|
|
1111
1122
|
: listed
|
|
1112
|
-
? run([{ kind: 'unpublish-input', input: from }, { kind: 'set-repeat', part, pitch }] as
|
|
1123
|
+
? run([{ kind: 'unpublish-input', input: from }, { kind: 'set-repeat', part, pitch }] as FigureProposalStep[])
|
|
1113
1124
|
: (this.publishing = undefined)}
|
|
1114
1125
|
/>
|
|
1115
1126
|
${i18next.t('figure.label.board-sets')}
|
|
@@ -1134,7 +1145,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
1134
1145
|
const pitch = input.valueAsNumber
|
|
1135
1146
|
if (!Number.isFinite(pitch) || pitch === field.value) return
|
|
1136
1147
|
/* Read from a design input the board may set: the input's value changes, the row keeps reading it (ADR-0096). */
|
|
1137
|
-
const from = this.pitchInputOf(JSON.parse(this.session!.source()) as
|
|
1148
|
+
const from = this.pitchInputOf(JSON.parse(this.session!.source()) as FigureAsset, this.selected)
|
|
1138
1149
|
if (from) {
|
|
1139
1150
|
this.run(() => this.session!.apply([{ kind: 'set-design', input: from, unit: 'mm', value: pitch }]))
|
|
1140
1151
|
input.value = String(this.session!.read().fields.find(i => i.id === from)?.value ?? field.value)
|
|
@@ -1152,7 +1163,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
1152
1163
|
this.error =
|
|
1153
1164
|
code === 'INPUT_RANGE' && found
|
|
1154
1165
|
? i18next.t('figure.text.repeat-pitch-too-small', { length: Number(found[1]).toLocaleString(), min: found[2], limit: found[3] })
|
|
1155
|
-
:
|
|
1166
|
+
: this.said(error)
|
|
1156
1167
|
}
|
|
1157
1168
|
}
|
|
1158
1169
|
/** Choose a part, from the list, the canvas or a place row. A figure-level tab gives way to the part's properties. */
|
|
@@ -1216,7 +1227,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
1216
1227
|
.pane=${this.panes}
|
|
1217
1228
|
@canvas-undo=${() => this.run(() => this.session!.undo())}
|
|
1218
1229
|
@canvas-redo=${() => this.run(() => this.session!.redo())}
|
|
1219
|
-
@canvas-fit=${() => this.renderRoot.querySelector<
|
|
1230
|
+
@canvas-fit=${() => this.renderRoot.querySelector<FigureCanvas>('figure-canvas')?.fit()}
|
|
1220
1231
|
>
|
|
1221
1232
|
${this.renderGizmoTools(!unavailable && this.middle === 'edit')}
|
|
1222
1233
|
${(['edit', 'preview'] as const).map(
|
|
@@ -1237,7 +1248,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
1237
1248
|
: null}
|
|
1238
1249
|
${this.notice ? html`<div role="status" class="notice">${this.notice}</div>` : null}
|
|
1239
1250
|
${this.catalogOpen
|
|
1240
|
-
? html`<figure-
|
|
1251
|
+
? html`<figure-recipe-catalog
|
|
1241
1252
|
.templates=${this.templates}
|
|
1242
1253
|
.selected=${this.selected}
|
|
1243
1254
|
.error=${this.recipeError}
|
|
@@ -1246,15 +1257,15 @@ export class FigureV3Editor extends LitElement {
|
|
|
1246
1257
|
?locked=${unavailable}
|
|
1247
1258
|
@insert-recipe=${(e: CustomEvent) => this.insertRecipe(e.detail.recipe, e.detail.values)}
|
|
1248
1259
|
@close-catalog=${() => (this.catalogOpen = false)}
|
|
1249
|
-
></figure-
|
|
1260
|
+
></figure-recipe-catalog>`
|
|
1250
1261
|
: null}
|
|
1251
1262
|
<ox-three-pane>
|
|
1252
|
-
<figure-
|
|
1263
|
+
<figure-parts
|
|
1253
1264
|
slot="start"
|
|
1254
1265
|
.budget=${this.budget}
|
|
1255
1266
|
@show-maturity=${() => (this.tab = 'verdict')}
|
|
1256
1267
|
.parts=${this.partRows(view.geometry)}
|
|
1257
|
-
.partBudget=${this.partBudgetOf(asset as
|
|
1268
|
+
.partBudget=${this.partBudgetOf(asset as FigureAsset | undefined)}
|
|
1258
1269
|
@set-detail-level=${(e: CustomEvent) => this.linkCommand({ kind: 'set-detail-level', level: e.detail.level })}
|
|
1259
1270
|
.selected=${this.selected}
|
|
1260
1271
|
?locked=${unavailable}
|
|
@@ -1269,18 +1280,18 @@ export class FigureV3Editor extends LitElement {
|
|
|
1269
1280
|
@move-part-down=${(e: CustomEvent) => this.movePart(e.detail.id, e.detail.to)}
|
|
1270
1281
|
@duplicate-part=${(e: CustomEvent) => this.duplicatePart(e.detail.id)}
|
|
1271
1282
|
@remove-part=${(e: CustomEvent) => this.removePart(e.detail.id)}
|
|
1272
|
-
></figure-
|
|
1283
|
+
></figure-parts>
|
|
1273
1284
|
${this.middle === 'preview'
|
|
1274
|
-
? html`<figure-
|
|
1285
|
+
? html`<figure-preview
|
|
1275
1286
|
.asset=${asset}
|
|
1276
1287
|
.context=${view.context ?? ''}
|
|
1277
1288
|
.view=${this.view}
|
|
1278
|
-
@
|
|
1279
|
-
></figure-
|
|
1280
|
-
: html`<figure-
|
|
1289
|
+
@part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
|
|
1290
|
+
></figure-preview>`
|
|
1291
|
+
: html`<figure-canvas
|
|
1281
1292
|
.view=${this.view}
|
|
1282
1293
|
.parts=${this.drawnParts(view.geometry, asset)}
|
|
1283
|
-
.fan=${this.fanOf(asset as
|
|
1294
|
+
.fan=${this.fanOf(asset as FigureAsset | undefined)}
|
|
1284
1295
|
.palette=${view.palette ?? {}}
|
|
1285
1296
|
.context=${view.context ?? ''}
|
|
1286
1297
|
.selected=${this.selected}
|
|
@@ -1290,7 +1301,7 @@ export class FigureV3Editor extends LitElement {
|
|
|
1290
1301
|
@render-error=${(e: CustomEvent<Error>) => {
|
|
1291
1302
|
this.error = e.detail.message
|
|
1292
1303
|
}}
|
|
1293
|
-
></figure-
|
|
1304
|
+
></figure-canvas>`}
|
|
1294
1305
|
<aside slot="end">
|
|
1295
1306
|
<div tabs>
|
|
1296
1307
|
${this.renderTab('properties', 'tune', i18next.t('figure.label.properties'))}
|
|
@@ -1318,30 +1329,30 @@ export class FigureV3Editor extends LitElement {
|
|
|
1318
1329
|
</div>
|
|
1319
1330
|
${this.renderRepeatNote(view.geometry)}
|
|
1320
1331
|
</section>
|
|
1321
|
-
${this.renderRepeat(asset as
|
|
1332
|
+
${this.renderRepeat(asset as FigureAsset | undefined, view.geometry, unavailable)}
|
|
1322
1333
|
${this.renderGroups(groupFields(own, this.selected), 'figure.label.group-placement-size', 'figure.label.group-shape', unavailable, this.renderTools(unavailable))}
|
|
1323
1334
|
${this.renderLinks(unavailable, 'shape')}
|
|
1324
|
-
<figure-
|
|
1335
|
+
<figure-material-panel
|
|
1325
1336
|
.body=${{ ...part, id: this.selected }}
|
|
1326
1337
|
.palette=${view.palette ?? {}}
|
|
1327
1338
|
?locked=${unavailable}
|
|
1328
|
-
@
|
|
1329
|
-
@
|
|
1330
|
-
></figure-
|
|
1339
|
+
@part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
|
|
1340
|
+
@part-live=${(e: CustomEvent) => this.hold({}, e.detail.edit)}
|
|
1341
|
+
></figure-material-panel>`
|
|
1331
1342
|
: html`<slot name="figure"></slot>
|
|
1332
1343
|
${this.renderGroups(groupFields(common), 'figure.label.group-figure-size', 'figure.label.design-inputs', unavailable)}
|
|
1333
|
-
${this.renderInstanceRange(asset as
|
|
1344
|
+
${this.renderInstanceRange(asset as FigureAsset | undefined, unavailable)}`}
|
|
1334
1345
|
</div>
|
|
1335
1346
|
<div panel ?off=${tab !== 'capability'}>
|
|
1336
1347
|
${this.renderSubject()}
|
|
1337
1348
|
${this.selected && part ? this.renderLinks(unavailable, 'places') : null}
|
|
1338
|
-
<figure-
|
|
1349
|
+
<figure-capability-panel
|
|
1339
1350
|
.asset=${asset}
|
|
1340
1351
|
.selected=${this.selected}
|
|
1341
1352
|
?locked=${unavailable}
|
|
1342
1353
|
@select-part=${(e: CustomEvent) => this.choose(e.detail.id)}
|
|
1343
|
-
@
|
|
1344
|
-
></figure-
|
|
1354
|
+
@part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
|
|
1355
|
+
></figure-capability-panel>
|
|
1345
1356
|
</div>
|
|
1346
1357
|
<div panel ?off=${tab !== 'motion'}>
|
|
1347
1358
|
${this.renderSubject()}
|