@things-factory/figure-ui 10.1.43 → 10.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/client/modeller/figure-v3-links.ts +303 -0
  2. package/client/modeller/modeller-route.ts +6 -21
  3. package/client/modeller/v3-editor.ts +44 -3
  4. package/client/modeller/v3-gizmo.ts +13 -2
  5. package/client/modeller/v3-links.ts +138 -0
  6. package/client/pages/figure-v3-modeller-page.ts +2 -5
  7. package/client/route.ts +2 -6
  8. package/dist-client/modeller/figure-v3-links.d.ts +42 -0
  9. package/dist-client/modeller/figure-v3-links.js +306 -0
  10. package/dist-client/modeller/figure-v3-links.js.map +1 -0
  11. package/dist-client/modeller/modeller-route.d.ts +4 -11
  12. package/dist-client/modeller/modeller-route.js +5 -22
  13. package/dist-client/modeller/modeller-route.js.map +1 -1
  14. package/dist-client/modeller/v3-editor.d.ts +5 -0
  15. package/dist-client/modeller/v3-editor.js +46 -2
  16. package/dist-client/modeller/v3-editor.js.map +1 -1
  17. package/dist-client/modeller/v3-gizmo.js +11 -1
  18. package/dist-client/modeller/v3-gizmo.js.map +1 -1
  19. package/dist-client/modeller/v3-links.d.ts +54 -0
  20. package/dist-client/modeller/v3-links.js +110 -0
  21. package/dist-client/modeller/v3-links.js.map +1 -0
  22. package/dist-client/pages/figure-v3-modeller-page.d.ts +2 -5
  23. package/dist-client/pages/figure-v3-modeller-page.js +2 -5
  24. package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
  25. package/dist-client/route.d.ts +1 -1
  26. package/dist-client/route.js +2 -5
  27. package/dist-client/route.js.map +1 -1
  28. package/dist-client/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +4 -4
  30. package/test/ai-proposal-contract.test.ts +0 -88
  31. package/test/figure-source.test.ts +0 -102
  32. package/test/i18n-prefix-guard.test.ts +0 -24
  33. package/test/modeller-route.test.ts +14 -25
  34. package/test/part-edits.test.ts +0 -7
  35. package/test/scene-budget.test.ts +0 -7
  36. package/test/v3-gizmo.test.ts +10 -0
  37. package/test/v3-links.test.ts +123 -0
  38. package/things-factory.config.js +2 -5
  39. package/translations/en.json +6 -0
  40. package/translations/ja.json +11 -1
  41. package/translations/ko.json +6 -0
  42. package/translations/ms.json +11 -1
  43. package/translations/zh.json +11 -1
  44. package/client/modeller/angle-dial.ts +0 -70
  45. package/client/modeller/base-box.ts +0 -175
  46. package/client/modeller/figure-animations.ts +0 -1249
  47. package/client/modeller/figure-canvas.ts +0 -567
  48. package/client/modeller/figure-capabilities.ts +0 -379
  49. package/client/modeller/figure-inspector.ts +0 -2208
  50. package/client/modeller/figure-parts.ts +0 -479
  51. package/client/modeller/figure-preview.ts +0 -598
  52. package/client/modeller/figure-report.ts +0 -966
  53. package/client/modeller/figure-settings.ts +0 -328
  54. package/client/modeller/figure-side.ts +0 -272
  55. package/client/modeller/figure-thumbnail.ts +0 -123
  56. package/client/modeller/figure-view.ts +0 -152
  57. package/client/modeller/joint-edits.ts +0 -262
  58. package/client/modeller/joint-gizmo.ts +0 -180
  59. package/client/modeller/maturity.ts +0 -288
  60. package/client/modeller/new-figure-entry.ts +0 -4
  61. package/client/modeller/pose-defaults.ts +0 -67
  62. package/client/pages/figure-modeller-page.ts +0 -2082
  63. package/dist-client/modeller/angle-dial.d.ts +0 -32
  64. package/dist-client/modeller/angle-dial.js +0 -41
  65. package/dist-client/modeller/angle-dial.js.map +0 -1
  66. package/dist-client/modeller/base-box.d.ts +0 -71
  67. package/dist-client/modeller/base-box.js +0 -110
  68. package/dist-client/modeller/base-box.js.map +0 -1
  69. package/dist-client/modeller/figure-animations.d.ts +0 -159
  70. package/dist-client/modeller/figure-animations.js +0 -1132
  71. package/dist-client/modeller/figure-animations.js.map +0 -1
  72. package/dist-client/modeller/figure-canvas.d.ts +0 -150
  73. package/dist-client/modeller/figure-canvas.js +0 -506
  74. package/dist-client/modeller/figure-canvas.js.map +0 -1
  75. package/dist-client/modeller/figure-capabilities.d.ts +0 -42
  76. package/dist-client/modeller/figure-capabilities.js +0 -349
  77. package/dist-client/modeller/figure-capabilities.js.map +0 -1
  78. package/dist-client/modeller/figure-inspector.d.ts +0 -329
  79. package/dist-client/modeller/figure-inspector.js +0 -2095
  80. package/dist-client/modeller/figure-inspector.js.map +0 -1
  81. package/dist-client/modeller/figure-parts.d.ts +0 -64
  82. package/dist-client/modeller/figure-parts.js +0 -478
  83. package/dist-client/modeller/figure-parts.js.map +0 -1
  84. package/dist-client/modeller/figure-preview.d.ts +0 -138
  85. package/dist-client/modeller/figure-preview.js +0 -549
  86. package/dist-client/modeller/figure-preview.js.map +0 -1
  87. package/dist-client/modeller/figure-report.d.ts +0 -165
  88. package/dist-client/modeller/figure-report.js +0 -952
  89. package/dist-client/modeller/figure-report.js.map +0 -1
  90. package/dist-client/modeller/figure-settings.d.ts +0 -39
  91. package/dist-client/modeller/figure-settings.js +0 -317
  92. package/dist-client/modeller/figure-settings.js.map +0 -1
  93. package/dist-client/modeller/figure-side.d.ts +0 -78
  94. package/dist-client/modeller/figure-side.js +0 -317
  95. package/dist-client/modeller/figure-side.js.map +0 -1
  96. package/dist-client/modeller/figure-thumbnail.d.ts +0 -3
  97. package/dist-client/modeller/figure-thumbnail.js +0 -95
  98. package/dist-client/modeller/figure-thumbnail.js.map +0 -1
  99. package/dist-client/modeller/figure-view.d.ts +0 -118
  100. package/dist-client/modeller/figure-view.js +0 -78
  101. package/dist-client/modeller/figure-view.js.map +0 -1
  102. package/dist-client/modeller/joint-edits.d.ts +0 -49
  103. package/dist-client/modeller/joint-edits.js +0 -223
  104. package/dist-client/modeller/joint-edits.js.map +0 -1
  105. package/dist-client/modeller/joint-gizmo.d.ts +0 -34
  106. package/dist-client/modeller/joint-gizmo.js +0 -158
  107. package/dist-client/modeller/joint-gizmo.js.map +0 -1
  108. package/dist-client/modeller/maturity.d.ts +0 -55
  109. package/dist-client/modeller/maturity.js +0 -212
  110. package/dist-client/modeller/maturity.js.map +0 -1
  111. package/dist-client/modeller/new-figure-entry.d.ts +0 -2
  112. package/dist-client/modeller/new-figure-entry.js +0 -5
  113. package/dist-client/modeller/new-figure-entry.js.map +0 -1
  114. package/dist-client/modeller/pose-defaults.d.ts +0 -9
  115. package/dist-client/modeller/pose-defaults.js +0 -35
  116. package/dist-client/modeller/pose-defaults.js.map +0 -1
  117. package/dist-client/pages/figure-modeller-page.d.ts +0 -419
  118. package/dist-client/pages/figure-modeller-page.js +0 -2060
  119. package/dist-client/pages/figure-modeller-page.js.map +0 -1
  120. package/test/angle-dial.test.ts +0 -78
  121. package/test/base-box.test.ts +0 -235
  122. package/test/joint-edits.test.ts +0 -165
  123. package/test/joint-gizmo.test.ts +0 -174
  124. package/test/joint-needle.test.ts +0 -162
  125. package/test/modeller-undo-redo.test.ts +0 -33
  126. package/test/new-figure-entry.test.ts +0 -15
  127. package/test/pose-defaults.test.ts +0 -71
  128. package/test/preview-board-depth.test.ts +0 -27
@@ -0,0 +1,306 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import { css, html, LitElement, nothing } from 'lit';
4
+ import { customElement, property, state } from 'lit/decorators.js';
5
+ import { i18next, localize } from '@operato/i18n';
6
+ import { V3_FACES } from '@hatiolab/figure-model';
7
+ import { AXES, canFasten, canFollow, choiceOf, fastenedTo, followOf, hasInstanceSize, motionOf, positionsWorkedOut } from './v3-links.js';
8
+ const FACES = Object.keys(V3_FACES);
9
+ /**
10
+ * What a part is tied to — its size response, what it is fastened to, how it moves (parity rows ④ ⑤ ⑥).
11
+ *
12
+ * 「Build Once, Fit Anywhere」 lives here: a dimension that follows the instance size or another part is what lets
13
+ * a figure be placed at any size, and a part fastened to the face it rests on is what keeps it there when that
14
+ * happens. The release gate refuses a figure that falls apart when resized, and tells the author to fasten the
15
+ * parts that come away — this is where they do it. Motion is the V2 joint: a slide or a turn driven by a control.
16
+ *
17
+ * The V2 inspector has these as its dimensions, placement and joint sections, in that order; so does this. Every
18
+ * change is one figure-model authoring command, sent as `v3-command` and run by the editor through the session — one
19
+ * step of the undo history. Choices the command would refuse (a round body fastened, a size following an instance
20
+ * size the figure does not declare) are not offered, so the author is not sent into a refusal.
21
+ */
22
+ let FigureV3Links = class FigureV3Links extends localize(i18next)(LitElement) {
23
+ constructor() {
24
+ super(...arguments);
25
+ /** The selected part. */
26
+ this.partId = '';
27
+ this.locked = false;
28
+ this.fasten = { face: 'bottom', toPlane: false, target: '', targetFace: 'top', facing: 'meet', gap: 0 };
29
+ this.motion = { kind: 'slide', axis: 'x', id: '', min: 0, max: 500 };
30
+ }
31
+ static { this.styles = css `
32
+ :host {
33
+ display: grid;
34
+ gap: 10px;
35
+ }
36
+ h3 {
37
+ font-size: var(--ui-t-body, 13px);
38
+ margin: 12px 0 0;
39
+ }
40
+ div[row] {
41
+ display: grid;
42
+ grid-template-columns: 24px minmax(0, 1fr);
43
+ gap: 6px;
44
+ align-items: center;
45
+ }
46
+ div[form] {
47
+ display: grid;
48
+ grid-template-columns: 96px minmax(0, 1fr);
49
+ gap: 6px;
50
+ align-items: center;
51
+ }
52
+ div[pair] {
53
+ display: flex;
54
+ gap: 6px;
55
+ }
56
+ div[pair] > * {
57
+ flex: 1;
58
+ min-width: 0;
59
+ }
60
+ select,
61
+ input,
62
+ button {
63
+ font: inherit;
64
+ min-width: 0;
65
+ }
66
+ button[act] {
67
+ justify-self: start;
68
+ display: inline-flex;
69
+ align-items: center;
70
+ gap: 4px;
71
+ padding: 4px 10px;
72
+ border: none;
73
+ border-radius: 999em;
74
+ cursor: pointer;
75
+ color: var(--md-sys-color-on-secondary-container);
76
+ background: var(--md-sys-color-secondary-container);
77
+ }
78
+ button[act]:disabled {
79
+ opacity: 0.5;
80
+ cursor: default;
81
+ }
82
+ button[act] md-icon {
83
+ font-size: 16px;
84
+ width: 16px;
85
+ height: 16px;
86
+ }
87
+ small,
88
+ p[hint] {
89
+ margin: 0;
90
+ color: var(--md-sys-color-on-surface-variant);
91
+ }
92
+ b[bound] {
93
+ font-weight: 600;
94
+ }
95
+ label[toggle] {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 6px;
99
+ }
100
+ `; }
101
+ render() {
102
+ const asset = this.asset;
103
+ if (!asset || !this.partId)
104
+ return nothing;
105
+ return html `
106
+ <h3>${i18next.t('figure.label.dimensions')}</h3>
107
+ ${canFollow(asset, this.partId) ? this.renderDimensions(asset) : html `<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}
108
+ <h3>${i18next.t('figure.label.placement')}</h3>
109
+ ${this.renderPositions(asset)}
110
+ ${canFasten(asset, this.partId) ? this.renderFastening(asset) : html `<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}
111
+ <h3>${i18next.t('figure.label.motion')}</h3>
112
+ ${this.renderMotion(asset)}
113
+ `;
114
+ }
115
+ renderDimensions(asset) {
116
+ const others = asset.appearance.map(look => look.target).filter(one => one !== this.partId && canFollow(asset, one));
117
+ return AXES.map(axis => {
118
+ const now = followOf(asset, this.partId, axis);
119
+ const value = choiceOf(now);
120
+ return html `<div row>
121
+ <b>${axis.toUpperCase()}</b>
122
+ <select ?disabled=${this.locked || now.of === 'expression'} @change=${(e) => this.follow(axis, e.target.value, now)}>
123
+ <option value="own" ?selected=${value === 'own'}>${i18next.t('figure.label.follows-nothing')}</option>
124
+ ${AXES.filter(one => hasInstanceSize(asset, one)).map(one => html `<option value=${`instance:${one}`} ?selected=${value === `instance:${one}`}>
125
+ ${i18next.t('figure.label.follows-instance')} · ${one.toUpperCase()}
126
+ </option>`)}
127
+ ${others.flatMap(other => AXES.map(one => html `<option value=${`part:${other}:${one}`} ?selected=${value === `part:${other}:${one}`}>${other} · ${one.toUpperCase()}</option>`))}
128
+ ${now.of === 'expression' || now.of === 'input'
129
+ ? html `<option value=${value} selected>${now.of === 'input' ? now.input : i18next.t('figure.label.follows-expression')}</option>`
130
+ : nothing}
131
+ </select>
132
+ </div>`;
133
+ });
134
+ }
135
+ follow(axis, choice, now) {
136
+ const following = now.of !== 'own';
137
+ if (choice === 'own') {
138
+ if (!following)
139
+ return;
140
+ const mm = Math.max(1, Math.round(this.dims?.[AXES.indexOf(axis)] ?? 100));
141
+ this.command({ kind: 'unlink-dimension', part: this.partId, axis, mm });
142
+ return;
143
+ }
144
+ const [of, a, b] = choice.split(':');
145
+ const source = of === 'instance' ? { of: 'instance', axis: a } : { of: 'part', part: a, axis: b };
146
+ this.command({ kind: 'link-dimension', part: this.partId, axis, source, replace: following });
147
+ }
148
+ /**
149
+ * Positions worked out from other values — V2's anchor rules, as the converter wrote them — each with the way to
150
+ * keep it where it is. This is the way out the release gate names when a resize leaves a part in the air.
151
+ */
152
+ renderPositions(asset) {
153
+ return positionsWorkedOut(asset, this.partId).map(axis => html `<div pair>
154
+ <span>${axis.toUpperCase()} · ${i18next.t('figure.label.position-worked-out')}</span>
155
+ <button act ?disabled=${this.locked} @click=${() => this.command({ kind: 'fix-position', part: this.partId, axis })}>
156
+ <md-icon>push_pin</md-icon>${i18next.t('figure.button.fix-position')}
157
+ </button>
158
+ </div>`);
159
+ }
160
+ renderFastening(asset) {
161
+ const to = fastenedTo(asset, this.partId);
162
+ const f = this.fasten;
163
+ const targets = asset.appearance.map(look => look.target).filter(one => one !== this.partId && canFasten(asset, one));
164
+ return html `
165
+ ${to
166
+ ? html `<div pair>
167
+ <span>${i18next.t('figure.label.fastened-to')}: <b bound>${to}</b></span>
168
+ <button act ?disabled=${this.locked} @click=${() => this.command({ kind: 'detach', part: this.partId })}>
169
+ <md-icon>link_off</md-icon>${i18next.t('figure.button.detach')}
170
+ </button>
171
+ </div>`
172
+ : html `<p hint>${i18next.t('figure.text.fastening-explained')}</p>`}
173
+ <div form>
174
+ <span>${i18next.t('figure.label.my-face')}</span>
175
+ <select @change=${(e) => (this.fasten = { ...f, face: e.target.value })}>
176
+ ${FACES.map(face => html `<option value=${face} ?selected=${face === f.face}>${i18next.t('figure.label.face-' + face)}</option>`)}
177
+ </select>
178
+ </div>
179
+ <label toggle>
180
+ <input type="checkbox" .checked=${f.toPlane} @change=${(e) => (this.fasten = { ...f, toPlane: e.target.checked })} />
181
+ ${i18next.t('figure.label.stands-on-plane')}
182
+ </label>
183
+ ${f.toPlane
184
+ ? nothing
185
+ : html `<div form>
186
+ <span>${i18next.t('figure.label.target-part')}</span>
187
+ <select @change=${(e) => (this.fasten = { ...f, target: e.target.value })}>
188
+ <option value="" ?selected=${!f.target}>—</option>
189
+ ${targets.map(one => html `<option value=${one} ?selected=${one === f.target}>${one}</option>`)}
190
+ </select>
191
+ <span>${i18next.t('figure.label.target-face')}</span>
192
+ <select @change=${(e) => (this.fasten = { ...f, targetFace: e.target.value })}>
193
+ ${FACES.map(face => html `<option value=${face} ?selected=${face === f.targetFace}>${i18next.t('figure.label.face-' + face)}</option>`)}
194
+ </select>
195
+ <span>${i18next.t('figure.label.facing-meet')}</span>
196
+ <select @change=${(e) => (this.fasten = { ...f, facing: e.target.value })}>
197
+ <option value="meet" ?selected=${f.facing === 'meet'}>${i18next.t('figure.label.facing-meet')}</option>
198
+ <option value="flush" ?selected=${f.facing === 'flush'}>${i18next.t('figure.label.facing-flush')}</option>
199
+ </select>
200
+ </div>`}
201
+ <div form>
202
+ <span>${i18next.t('figure.label.gap')}</span>
203
+ <input type="number" step="any" .value=${String(f.gap)} @change=${(e) => (this.fasten = { ...f, gap: Number(e.target.value) || 0 })} />
204
+ </div>
205
+ <button
206
+ act
207
+ ?disabled=${this.locked || (!f.toPlane && !f.target)}
208
+ @click=${() => this.command({
209
+ kind: 'attach',
210
+ part: this.partId,
211
+ face: f.face,
212
+ to: f.toPlane ? { plane: 'mounting-plane' } : { part: f.target, face: f.targetFace },
213
+ ...(f.toPlane ? {} : { facing: f.facing }),
214
+ ...(f.gap ? { gap: f.gap } : {}),
215
+ replace: !!to
216
+ })}
217
+ >
218
+ <md-icon>link</md-icon>${i18next.t('figure.button.fasten')}
219
+ </button>
220
+ `;
221
+ }
222
+ renderMotion(asset) {
223
+ const now = motionOf(asset, this.partId);
224
+ if (now) {
225
+ return html `<div pair>
226
+ <span>
227
+ ${i18next.t('figure.label.motion-' + now.kind)} · ${String(now.axis).toUpperCase()}
228
+ ${now.control ? html `<small>${now.control.id} (${now.control.unit}) ${now.control.min} … ${now.control.max}</small>` : nothing}
229
+ </span>
230
+ <button act ?disabled=${this.locked} @click=${() => this.command({ kind: 'remove-motion', part: this.partId })}>
231
+ <md-icon>close</md-icon>${i18next.t('figure.button.remove-motion')}
232
+ </button>
233
+ </div>`;
234
+ }
235
+ const m = this.motion;
236
+ const unit = m.kind === 'turn' ? 'deg' : 'mm';
237
+ return html `
238
+ <p hint>${i18next.t('figure.text.motion-explained')}</p>
239
+ <div pair>
240
+ <select @change=${(e) => this.setKind(e.target.value)}>
241
+ <option value="slide" ?selected=${m.kind === 'slide'}>${i18next.t('figure.label.motion-slide')}</option>
242
+ <option value="turn" ?selected=${m.kind === 'turn'}>${i18next.t('figure.label.motion-turn')}</option>
243
+ </select>
244
+ <select @change=${(e) => (this.motion = { ...m, axis: e.target.value })}>
245
+ ${AXES.map(a => html `<option value=${a} ?selected=${a === m.axis}>${i18next.t('figure.label.motion-axis')} ${a.toUpperCase()}</option>`)}
246
+ </select>
247
+ </div>
248
+ <div form>
249
+ <span>${i18next.t('figure.label.control-id')}</span>
250
+ <input .value=${m.id} placeholder=${`${this.partId}-${m.kind}`} @change=${(e) => (this.motion = { ...m, id: e.target.value.trim() })} />
251
+ <span>${i18next.t('figure.label.control-range')} (${unit})</span>
252
+ <div pair>
253
+ <input type="number" step="any" .value=${String(m.min)} @change=${(e) => (this.motion = { ...m, min: Number(e.target.value) })} />
254
+ <input type="number" step="any" .value=${String(m.max)} @change=${(e) => (this.motion = { ...m, max: Number(e.target.value) })} />
255
+ </div>
256
+ </div>
257
+ <button
258
+ act
259
+ ?disabled=${this.locked || !(m.max > m.min)}
260
+ @click=${() => this.command({
261
+ kind: 'add-motion',
262
+ part: this.partId,
263
+ motion: { kind: m.kind, axis: m.axis },
264
+ state: { id: m.id || `${this.partId}-${m.kind}`, unit, min: m.min, max: m.max, start: m.min }
265
+ })}
266
+ >
267
+ <md-icon>animation</md-icon>${i18next.t('figure.button.add-motion')}
268
+ </button>
269
+ `;
270
+ }
271
+ setKind(kind) {
272
+ this.motion = kind === 'turn' ? { ...this.motion, kind, min: -90, max: 90 } : { ...this.motion, kind, min: 0, max: 500 };
273
+ }
274
+ command(action) {
275
+ this.dispatchEvent(new CustomEvent('v3-command', { detail: { action }, bubbles: true, composed: true }));
276
+ }
277
+ };
278
+ __decorate([
279
+ property({ attribute: false }),
280
+ __metadata("design:type", Object)
281
+ ], FigureV3Links.prototype, "asset", void 0);
282
+ __decorate([
283
+ property(),
284
+ __metadata("design:type", Object)
285
+ ], FigureV3Links.prototype, "partId", void 0);
286
+ __decorate([
287
+ property({ attribute: false }),
288
+ __metadata("design:type", Array)
289
+ ], FigureV3Links.prototype, "dims", void 0);
290
+ __decorate([
291
+ property({ type: Boolean }),
292
+ __metadata("design:type", Object)
293
+ ], FigureV3Links.prototype, "locked", void 0);
294
+ __decorate([
295
+ state(),
296
+ __metadata("design:type", Object)
297
+ ], FigureV3Links.prototype, "fasten", void 0);
298
+ __decorate([
299
+ state(),
300
+ __metadata("design:type", Object)
301
+ ], FigureV3Links.prototype, "motion", void 0);
302
+ FigureV3Links = __decorate([
303
+ customElement('figure-v3-links')
304
+ ], FigureV3Links);
305
+ export { FigureV3Links };
306
+ //# sourceMappingURL=figure-v3-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-v3-links.js","sourceRoot":"","sources":["../../client/modeller/figure-v3-links.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { V3_FACES } from '@hatiolab/figure-model'\nimport type { V3Asset, V3AuthoringAction, V3DimensionSource, V3Face } from '@hatiolab/figure-model'\n\nimport { AXES, canFasten, canFollow, choiceOf, fastenedTo, followOf, hasInstanceSize, motionOf, positionsWorkedOut, type Axis, type Follow } from './v3-links.js'\n\nconst FACES = Object.keys(V3_FACES) as V3Face[]\n\n/**\n * What a part is tied to — its size response, what it is fastened to, how it moves (parity rows ④ ⑤ ⑥).\n *\n * 「Build Once, Fit Anywhere」 lives here: a dimension that follows the instance size or another part is what lets\n * a figure be placed at any size, and a part fastened to the face it rests on is what keeps it there when that\n * happens. The release gate refuses a figure that falls apart when resized, and tells the author to fasten the\n * parts that come away — this is where they do it. Motion is the V2 joint: a slide or a turn driven by a control.\n *\n * The V2 inspector has these as its dimensions, placement and joint sections, in that order; so does this. Every\n * change is one figure-model authoring command, sent as `v3-command` and run by the editor through the session — one\n * step of the undo history. Choices the command would refuse (a round body fastened, a size following an instance\n * size the figure does not declare) are not offered, so the author is not sent into a refusal.\n */\n@customElement('figure-v3-links')\nexport class FigureV3Links extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n display: grid;\n gap: 10px;\n }\n h3 {\n font-size: var(--ui-t-body, 13px);\n margin: 12px 0 0;\n }\n div[row] {\n display: grid;\n grid-template-columns: 24px minmax(0, 1fr);\n gap: 6px;\n align-items: center;\n }\n div[form] {\n display: grid;\n grid-template-columns: 96px minmax(0, 1fr);\n gap: 6px;\n align-items: center;\n }\n div[pair] {\n display: flex;\n gap: 6px;\n }\n div[pair] > * {\n flex: 1;\n min-width: 0;\n }\n select,\n input,\n button {\n font: inherit;\n min-width: 0;\n }\n button[act] {\n justify-self: start;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border: none;\n border-radius: 999em;\n cursor: pointer;\n color: var(--md-sys-color-on-secondary-container);\n background: var(--md-sys-color-secondary-container);\n }\n button[act]:disabled {\n opacity: 0.5;\n cursor: default;\n }\n button[act] md-icon {\n font-size: 16px;\n width: 16px;\n height: 16px;\n }\n small,\n p[hint] {\n margin: 0;\n color: var(--md-sys-color-on-surface-variant);\n }\n b[bound] {\n font-weight: 600;\n }\n label[toggle] {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n `\n\n /** The asset being edited. */\n @property({ attribute: false }) asset?: V3Asset\n /** The selected part. */\n @property() partId = ''\n /** The part's own extent along its axes now, in mm — what a dimension is fixed at when it stops following. */\n @property({ attribute: false }) dims?: [number, number, number]\n @property({ type: Boolean }) locked = false\n\n @state() private fasten = { face: 'bottom' as V3Face, toPlane: false, target: '', targetFace: 'top' as V3Face, facing: 'meet' as 'meet' | 'flush', gap: 0 }\n @state() private motion = { kind: 'slide' as 'slide' | 'turn', axis: 'x' as Axis, id: '', min: 0, max: 500 }\n\n render() {\n const asset = this.asset\n if (!asset || !this.partId) return nothing\n return html`\n <h3>${i18next.t('figure.label.dimensions')}</h3>\n ${canFollow(asset, this.partId) ? this.renderDimensions(asset) : html`<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}\n <h3>${i18next.t('figure.label.placement')}</h3>\n ${this.renderPositions(asset)}\n ${canFasten(asset, this.partId) ? this.renderFastening(asset) : html`<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}\n <h3>${i18next.t('figure.label.motion')}</h3>\n ${this.renderMotion(asset)}\n `\n }\n\n private renderDimensions(asset: V3Asset) {\n const others = asset.appearance.map(look => look.target).filter(one => one !== this.partId && canFollow(asset, one))\n return AXES.map(axis => {\n const now = followOf(asset, this.partId, axis)\n const value = choiceOf(now)\n return html`<div row>\n <b>${axis.toUpperCase()}</b>\n <select ?disabled=${this.locked || now.of === 'expression'} @change=${(e: Event) => this.follow(axis, (e.target as HTMLSelectElement).value, now)}>\n <option value=\"own\" ?selected=${value === 'own'}>${i18next.t('figure.label.follows-nothing')}</option>\n ${AXES.filter(one => hasInstanceSize(asset, one)).map(\n one => html`<option value=${`instance:${one}`} ?selected=${value === `instance:${one}`}>\n ${i18next.t('figure.label.follows-instance')} · ${one.toUpperCase()}\n </option>`\n )}\n ${others.flatMap(other =>\n AXES.map(\n one => html`<option value=${`part:${other}:${one}`} ?selected=${value === `part:${other}:${one}`}>${other} · ${one.toUpperCase()}</option>`\n )\n )}\n ${now.of === 'expression' || now.of === 'input'\n ? html`<option value=${value} selected>${now.of === 'input' ? now.input : i18next.t('figure.label.follows-expression')}</option>`\n : nothing}\n </select>\n </div>`\n })\n }\n\n private follow(axis: Axis, choice: string, now: Follow) {\n const following = now.of !== 'own'\n if (choice === 'own') {\n if (!following) return\n const mm = Math.max(1, Math.round(this.dims?.[AXES.indexOf(axis)] ?? 100))\n this.command({ kind: 'unlink-dimension', part: this.partId, axis, mm })\n return\n }\n const [of, a, b] = choice.split(':')\n const source: V3DimensionSource = of === 'instance' ? { of: 'instance', axis: a as Axis } : { of: 'part', part: a!, axis: b as Axis }\n this.command({ kind: 'link-dimension', part: this.partId, axis, source, replace: following })\n }\n\n /**\n * Positions worked out from other values — V2's anchor rules, as the converter wrote them — each with the way to\n * keep it where it is. This is the way out the release gate names when a resize leaves a part in the air.\n */\n private renderPositions(asset: V3Asset) {\n return positionsWorkedOut(asset, this.partId).map(\n axis => html`<div pair>\n <span>${axis.toUpperCase()} · ${i18next.t('figure.label.position-worked-out')}</span>\n <button act ?disabled=${this.locked} @click=${() => this.command({ kind: 'fix-position', part: this.partId, axis })}>\n <md-icon>push_pin</md-icon>${i18next.t('figure.button.fix-position')}\n </button>\n </div>`\n )\n }\n\n private renderFastening(asset: V3Asset) {\n const to = fastenedTo(asset, this.partId)\n const f = this.fasten\n const targets = asset.appearance.map(look => look.target).filter(one => one !== this.partId && canFasten(asset, one))\n return html`\n ${to\n ? html`<div pair>\n <span>${i18next.t('figure.label.fastened-to')}: <b bound>${to}</b></span>\n <button act ?disabled=${this.locked} @click=${() => this.command({ kind: 'detach', part: this.partId })}>\n <md-icon>link_off</md-icon>${i18next.t('figure.button.detach')}\n </button>\n </div>`\n : html`<p hint>${i18next.t('figure.text.fastening-explained')}</p>`}\n <div form>\n <span>${i18next.t('figure.label.my-face')}</span>\n <select @change=${(e: Event) => (this.fasten = { ...f, face: (e.target as HTMLSelectElement).value as V3Face })}>\n ${FACES.map(face => html`<option value=${face} ?selected=${face === f.face}>${i18next.t('figure.label.face-' + face)}</option>`)}\n </select>\n </div>\n <label toggle>\n <input type=\"checkbox\" .checked=${f.toPlane} @change=${(e: Event) => (this.fasten = { ...f, toPlane: (e.target as HTMLInputElement).checked })} />\n ${i18next.t('figure.label.stands-on-plane')}\n </label>\n ${f.toPlane\n ? nothing\n : html`<div form>\n <span>${i18next.t('figure.label.target-part')}</span>\n <select @change=${(e: Event) => (this.fasten = { ...f, target: (e.target as HTMLSelectElement).value })}>\n <option value=\"\" ?selected=${!f.target}>—</option>\n ${targets.map(one => html`<option value=${one} ?selected=${one === f.target}>${one}</option>`)}\n </select>\n <span>${i18next.t('figure.label.target-face')}</span>\n <select @change=${(e: Event) => (this.fasten = { ...f, targetFace: (e.target as HTMLSelectElement).value as V3Face })}>\n ${FACES.map(face => html`<option value=${face} ?selected=${face === f.targetFace}>${i18next.t('figure.label.face-' + face)}</option>`)}\n </select>\n <span>${i18next.t('figure.label.facing-meet')}</span>\n <select @change=${(e: Event) => (this.fasten = { ...f, facing: (e.target as HTMLSelectElement).value as 'meet' | 'flush' })}>\n <option value=\"meet\" ?selected=${f.facing === 'meet'}>${i18next.t('figure.label.facing-meet')}</option>\n <option value=\"flush\" ?selected=${f.facing === 'flush'}>${i18next.t('figure.label.facing-flush')}</option>\n </select>\n </div>`}\n <div form>\n <span>${i18next.t('figure.label.gap')}</span>\n <input type=\"number\" step=\"any\" .value=${String(f.gap)} @change=${(e: Event) => (this.fasten = { ...f, gap: Number((e.target as HTMLInputElement).value) || 0 })} />\n </div>\n <button\n act\n ?disabled=${this.locked || (!f.toPlane && !f.target)}\n @click=${() =>\n this.command({\n kind: 'attach',\n part: this.partId,\n face: f.face,\n to: f.toPlane ? { plane: 'mounting-plane' } : { part: f.target, face: f.targetFace },\n ...(f.toPlane ? {} : { facing: f.facing }),\n ...(f.gap ? { gap: f.gap } : {}),\n replace: !!to\n } as V3AuthoringAction)}\n >\n <md-icon>link</md-icon>${i18next.t('figure.button.fasten')}\n </button>\n `\n }\n\n private renderMotion(asset: V3Asset) {\n const now = motionOf(asset, this.partId)\n if (now) {\n return html`<div pair>\n <span>\n ${i18next.t('figure.label.motion-' + now.kind)} · ${String(now.axis).toUpperCase()}\n ${now.control ? html`<small>${now.control.id} (${now.control.unit}) ${now.control.min} … ${now.control.max}</small>` : nothing}\n </span>\n <button act ?disabled=${this.locked} @click=${() => this.command({ kind: 'remove-motion', part: this.partId })}>\n <md-icon>close</md-icon>${i18next.t('figure.button.remove-motion')}\n </button>\n </div>`\n }\n const m = this.motion\n const unit = m.kind === 'turn' ? 'deg' : 'mm'\n return html`\n <p hint>${i18next.t('figure.text.motion-explained')}</p>\n <div pair>\n <select @change=${(e: Event) => this.setKind((e.target as HTMLSelectElement).value as 'slide' | 'turn')}>\n <option value=\"slide\" ?selected=${m.kind === 'slide'}>${i18next.t('figure.label.motion-slide')}</option>\n <option value=\"turn\" ?selected=${m.kind === 'turn'}>${i18next.t('figure.label.motion-turn')}</option>\n </select>\n <select @change=${(e: Event) => (this.motion = { ...m, axis: (e.target as HTMLSelectElement).value as Axis })}>\n ${AXES.map(a => html`<option value=${a} ?selected=${a === m.axis}>${i18next.t('figure.label.motion-axis')} ${a.toUpperCase()}</option>`)}\n </select>\n </div>\n <div form>\n <span>${i18next.t('figure.label.control-id')}</span>\n <input .value=${m.id} placeholder=${`${this.partId}-${m.kind}`} @change=${(e: Event) => (this.motion = { ...m, id: (e.target as HTMLInputElement).value.trim() })} />\n <span>${i18next.t('figure.label.control-range')} (${unit})</span>\n <div pair>\n <input type=\"number\" step=\"any\" .value=${String(m.min)} @change=${(e: Event) => (this.motion = { ...m, min: Number((e.target as HTMLInputElement).value) })} />\n <input type=\"number\" step=\"any\" .value=${String(m.max)} @change=${(e: Event) => (this.motion = { ...m, max: Number((e.target as HTMLInputElement).value) })} />\n </div>\n </div>\n <button\n act\n ?disabled=${this.locked || !(m.max > m.min)}\n @click=${() =>\n this.command({\n kind: 'add-motion',\n part: this.partId,\n motion: { kind: m.kind, axis: m.axis },\n state: { id: m.id || `${this.partId}-${m.kind}`, unit, min: m.min, max: m.max, start: m.min }\n })}\n >\n <md-icon>animation</md-icon>${i18next.t('figure.button.add-motion')}\n </button>\n `\n }\n\n private setKind(kind: 'slide' | 'turn') {\n this.motion = kind === 'turn' ? { ...this.motion, kind, min: -90, max: 90 } : { ...this.motion, kind, min: 0, max: 500 }\n }\n\n private command(action: V3AuthoringAction) {\n this.dispatchEvent(new CustomEvent('v3-command', { detail: { action }, bubbles: true, composed: true }))\n }\n}\n\n"]}
@@ -1,16 +1,9 @@
1
1
  /**
2
- * Which modeller opens a figure — by the format of its source, which the server reports (`format`).
3
- *
4
- * The catalogue sent every figure to the V2 modeller, so a converted figure could only be opened by typing the V3
5
- * page's address (2026-09-24). A new figure starts in V3: new V2 figures would only add to what the cutover has to
6
- * convert (ADR-0087). A figure is opened in V3 only when the server says it is V3 — an unknown format goes to the
7
- * V2 page, which reports what it could not read.
2
+ * The one modeller. Every figure is V3 since ADR-0087 step 5 (2026-09-24, user decision: V2 is not kept), so the
3
+ * catalogue, a new figure and an old address all open the same page. `figure-v3-modeller` stays as an alias so a
4
+ * link written before the switch still lands.
8
5
  */
9
- export declare const V3_MODELLER = "figure-v3-modeller";
10
- export declare const V2_MODELLER = "figure-modeller";
6
+ export declare const MODELLER = "figure-modeller";
11
7
  export declare function modellerPathOf(figure?: {
12
8
  id?: string;
13
- format?: string | null;
14
9
  }): string;
15
- /** The version a V3 asset stamps on itself. The V2 page forwards such a figure rather than failing on it. */
16
- export declare function isV3Source(source?: string | null): boolean;
@@ -1,27 +1,10 @@
1
1
  /**
2
- * Which modeller opens a figure — by the format of its source, which the server reports (`format`).
3
- *
4
- * The catalogue sent every figure to the V2 modeller, so a converted figure could only be opened by typing the V3
5
- * page's address (2026-09-24). A new figure starts in V3: new V2 figures would only add to what the cutover has to
6
- * convert (ADR-0087). A figure is opened in V3 only when the server says it is V3 — an unknown format goes to the
7
- * V2 page, which reports what it could not read.
2
+ * The one modeller. Every figure is V3 since ADR-0087 step 5 (2026-09-24, user decision: V2 is not kept), so the
3
+ * catalogue, a new figure and an old address all open the same page. `figure-v3-modeller` stays as an alias so a
4
+ * link written before the switch still lands.
8
5
  */
9
- export const V3_MODELLER = 'figure-v3-modeller';
10
- export const V2_MODELLER = 'figure-modeller';
6
+ export const MODELLER = 'figure-modeller';
11
7
  export function modellerPathOf(figure) {
12
- if (!figure?.id)
13
- return V3_MODELLER;
14
- return figure.format === 'v3' ? `${V3_MODELLER}/${figure.id}` : `${V2_MODELLER}/${figure.id}`;
15
- }
16
- /** The version a V3 asset stamps on itself. The V2 page forwards such a figure rather than failing on it. */
17
- export function isV3Source(source) {
18
- if (!source)
19
- return false;
20
- try {
21
- return JSON.parse(source)?.version === 'figure-v3-asset-1';
22
- }
23
- catch {
24
- return false;
25
- }
8
+ return figure?.id ? `${MODELLER}/${figure.id}` : MODELLER;
26
9
  }
27
10
  //# sourceMappingURL=modeller-route.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"modeller-route.js","sourceRoot":"","sources":["../../client/modeller/modeller-route.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,oBAAoB,CAAA;AAC/C,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAA;AAE5C,MAAM,UAAU,cAAc,CAAC,MAAgD;IAC7E,IAAI,CAAC,MAAM,EAAE,EAAE;QAAE,OAAO,WAAW,CAAA;IACnC,OAAO,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,WAAW,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,WAAW,IAAI,MAAM,CAAC,EAAE,EAAE,CAAA;AAC/F,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,UAAU,CAAC,MAAsB;IAC/C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACzB,IAAI,CAAC;QACH,OAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,CAA2B,EAAE,OAAO,KAAK,mBAAmB,CAAA;IACvF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC","sourcesContent":["/**\n * Which modeller opens a figure — by the format of its source, which the server reports (`format`).\n *\n * The catalogue sent every figure to the V2 modeller, so a converted figure could only be opened by typing the V3\n * page's address (2026-09-24). A new figure starts in V3: new V2 figures would only add to what the cutover has to\n * convert (ADR-0087). A figure is opened in V3 only when the server says it is V3 — an unknown format goes to the\n * V2 page, which reports what it could not read.\n */\nexport const V3_MODELLER = 'figure-v3-modeller'\nexport const V2_MODELLER = 'figure-modeller'\n\nexport function modellerPathOf(figure?: { id?: string; format?: string | null }): string {\n if (!figure?.id) return V3_MODELLER\n return figure.format === 'v3' ? `${V3_MODELLER}/${figure.id}` : `${V2_MODELLER}/${figure.id}`\n}\n\n/** The version a V3 asset stamps on itself. The V2 page forwards such a figure rather than failing on it. */\nexport function isV3Source(source?: string | null): boolean {\n if (!source) return false\n try {\n return (JSON.parse(source) as { version?: unknown })?.version === 'figure-v3-asset-1'\n } catch {\n return false\n }\n}\n"]}
1
+ {"version":3,"file":"modeller-route.js","sourceRoot":"","sources":["../../client/modeller/modeller-route.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,iBAAiB,CAAA;AAEzC,MAAM,UAAU,cAAc,CAAC,MAAwB;IACrD,OAAO,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAA;AAC3D,CAAC","sourcesContent":["/**\n * The one modeller. Every figure is V3 since ADR-0087 step 5 (2026-09-24, user decision: V2 is not kept), so the\n * catalogue, a new figure and an old address all open the same page. `figure-v3-modeller` stays as an alias so a\n * link written before the switch still lands.\n */\nexport const MODELLER = 'figure-modeller'\n\nexport function modellerPathOf(figure?: { id?: string }): string {\n return figure?.id ? `${MODELLER}/${figure.id}` : MODELLER\n}\n"]}
@@ -3,6 +3,7 @@ import type { V3ProductSession } from './v3-product-session.js';
3
3
  import './v3-canvas.js';
4
4
  import './figure-v3-parts.js';
5
5
  import './figure-v3-material.js';
6
+ import './figure-v3-links.js';
6
7
  /** Product editor surface. The injected session is shared by page persistence and AI commands. */
7
8
  export declare class FigureV3Editor extends LitElement {
8
9
  static styles: import("lit").CSSResult;
@@ -43,6 +44,10 @@ export declare class FigureV3Editor extends LitElement {
43
44
  * (figure-model's rename-part), as one step of the history.
44
45
  */
45
46
  private renamePart;
47
+ /** Size response, fastening and motion for the selected part (parity ④ ⑤ ⑥), as authoring commands. */
48
+ private renderLinks;
49
+ /** One command, one step of the history. A refusal a person can meet here is said in their words. */
50
+ private linkCommand;
46
51
  private changeField;
47
52
  render(): import("lit-html").TemplateResult<1>;
48
53
  }
@@ -7,8 +7,9 @@ import { drawableV3Parts } from './v3-renderer.js';
7
7
  import './v3-canvas.js';
8
8
  import './figure-v3-parts.js';
9
9
  import './figure-v3-material.js';
10
+ import './figure-v3-links.js';
10
11
  import { gizmoChanges, gizmoReachOf } from './v3-gizmo.js';
11
- import { alignChanges, canMirror, mirrorChanges } from './v3-align.js';
12
+ import { alignChanges, boxOf, canMirror, mirrorChanges } from './v3-align.js';
12
13
  const names = {
13
14
  position: '위치',
14
15
  rotation: '회전',
@@ -434,6 +435,49 @@ let FigureV3Editor = class FigureV3Editor extends LitElement {
434
435
  : String(error);
435
436
  }
436
437
  }
438
+ /** Size response, fastening and motion for the selected part (parity ④ ⑤ ⑥), as authoring commands. */
439
+ renderLinks(unavailable) {
440
+ let asset;
441
+ try {
442
+ asset = JSON.parse(this.session.source());
443
+ }
444
+ catch {
445
+ return null;
446
+ }
447
+ let dims;
448
+ try {
449
+ dims = boxOf(asset, this.selected)?.dims;
450
+ }
451
+ catch {
452
+ dims = undefined;
453
+ }
454
+ return html `<figure-v3-links
455
+ .asset=${asset}
456
+ .partId=${this.selected}
457
+ .dims=${dims}
458
+ ?locked=${unavailable}
459
+ @v3-command=${(e) => this.linkCommand(e.detail.action)}
460
+ ></figure-v3-links>`;
461
+ }
462
+ /** One command, one step of the history. A refusal a person can meet here is said in their words. */
463
+ linkCommand(action) {
464
+ try {
465
+ this.session.command(action);
466
+ this.error = '';
467
+ this.changed();
468
+ }
469
+ catch (error) {
470
+ const code = error?.code;
471
+ this.error =
472
+ code === 'DIMENSION_CYCLE'
473
+ ? i18next.t('figure.text.follow-would-loop')
474
+ : code === 'FASTEN_CYCLE' || code === 'ATTACH_CYCLE'
475
+ ? i18next.t('figure.text.fasten-would-loop')
476
+ : error instanceof Error
477
+ ? error.message
478
+ : String(error);
479
+ }
480
+ }
437
481
  changeField(field, event) {
438
482
  const input = event.target, value = input.valueAsNumber;
439
483
  this.run(() => this.session.apply([
@@ -516,7 +560,7 @@ let FigureV3Editor = class FigureV3Editor extends LitElement {
516
560
  ${this.renderTools(unavailable)}
517
561
  ${fields.map(field => html `<label><span>${fieldLabel(field.id)} (${field.unit})${field.role === 'state' ? ' · 동작' : ''}</span><input aria-label=${field.id} type="number" step="any" .value=${String(field.value)} ?disabled=${unavailable} @change=${(e) => this.changeField(field, e)} /></label>`)}
518
562
  ${part
519
- ? html `<figure-v3-material
563
+ ? html `${this.renderLinks(unavailable)}<figure-v3-material
520
564
  .body=${part}
521
565
  .palette=${view.palette ?? {}}
522
566
  ?locked=${unavailable}