@things-factory/figure-ui 10.1.163 → 10.1.164
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/modeller/data-interface.ts +106 -2
- package/client/modeller/editor.ts +1 -0
- package/client/modeller/figure-data-panel.ts +81 -0
- package/client/viewparts/figure-detail-dock.ts +9 -3
- package/dist-client/modeller/data-interface.d.ts +39 -1
- package/dist-client/modeller/data-interface.js +85 -1
- package/dist-client/modeller/data-interface.js.map +1 -1
- package/dist-client/modeller/editor.js +1 -0
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-data-panel.d.ts +7 -0
- package/dist-client/modeller/figure-data-panel.js +82 -1
- package/dist-client/modeller/figure-data-panel.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/figure-detail-dock.js +9 -3
- package/dist-client/viewparts/figure-detail-dock.js.map +1 -1
- package/package.json +4 -4
- package/test/browser/build.mjs +9 -0
- package/test/browser/detail-tabs-page.js +36 -0
- package/test/browser/detail-tabs.check.mjs +123 -0
- package/test/data-motion.test.ts +43 -1
- package/translations/en.json +13 -0
- package/translations/es.json +13 -0
- package/translations/ja.json +13 -0
- package/translations/ko.json +13 -0
- package/translations/ms.json +13 -0
- package/translations/zh.json +13 -0
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* of data is things-scene's; this only lists what there is to choose, drafts a binding the kernel then checks, and edits
|
|
5
5
|
* the schema as a tree. Kept apart from the element so it is tested without a browser.
|
|
6
6
|
*/
|
|
7
|
-
import { FIGURE_PART_ID, FIGURE_MOTION_SPEED_MAX, figureDataRows, parseFigureAccessor } from '@hatiolab/figure-model'
|
|
8
|
-
import type { FigureAsset, FigureDataBehavior, FigureDataBinding, FigureDataField, FigureDataNode, FigureDataRow, FigureDataValue, FigureDataValueIssue, FigureStatusPort } from '@hatiolab/figure-model'
|
|
7
|
+
import { FIGURE_PART_ID, FIGURE_MOTION_SPEED_MAX, FigureContractError, figureDataRows, parseFigureAccessor } from '@hatiolab/figure-model'
|
|
8
|
+
import type { FigureAsset, FigureDataBehavior, FigureDataBinding, FigureDataField, FigureDataNode, FigureDataRow, FigureDataValue, FigureDataValueIssue, FigureProposalStep, FigureStatusPort } from '@hatiolab/figure-model'
|
|
9
9
|
import { FigureInputs, type FigureDataResult } from '@hatiolab/things-scene'
|
|
10
10
|
|
|
11
11
|
/** Something a binding can move, with what the screen shows of it. */
|
|
@@ -276,3 +276,107 @@ export function motionWrittenByData(asset: FigureAsset): Record<string, { clip:
|
|
|
276
276
|
}
|
|
277
277
|
return out
|
|
278
278
|
}
|
|
279
|
+
|
|
280
|
+
/* ---------------------------------------------------------------- the rotation card */
|
|
281
|
+
|
|
282
|
+
/** A joint that turns without end (a roller, a fan, a turntable): its control, by the id the clips write. */
|
|
283
|
+
export interface EndlessJoint {
|
|
284
|
+
input: string
|
|
285
|
+
label: string
|
|
286
|
+
unit: 'deg' | 'rad'
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** The joints that turn without end: revolute, with no limits, leading no one's mimic. */
|
|
290
|
+
export function endlessJointsOf(asset: FigureAsset): EndlessJoint[] {
|
|
291
|
+
const out: EndlessJoint[] = []
|
|
292
|
+
for (const j of asset.joints ?? []) {
|
|
293
|
+
if (j.type !== 'revolute' || j.lowerLimit !== undefined || j.upperLimit !== undefined || j.mimic) continue
|
|
294
|
+
const input = j.state?.id ?? j.id
|
|
295
|
+
const unit = asset.document.model.inputs.find(i => i.id === input)?.unit
|
|
296
|
+
if (unit !== 'deg' && unit !== 'rad') continue
|
|
297
|
+
if (!out.some(e => e.input === input)) out.push({ input, label: asset.stateInputs?.[input]?.label ?? input, unit })
|
|
298
|
+
}
|
|
299
|
+
return out
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** What the rotation card is told: the joint, and the data fields that turn it on, set its speed and its direction. */
|
|
303
|
+
export interface RotationChoice {
|
|
304
|
+
input: string
|
|
305
|
+
/** The motion group's name, shown to a person. */
|
|
306
|
+
label: string
|
|
307
|
+
/** A boolean field: true turns, false stops where it is. */
|
|
308
|
+
on?: string
|
|
309
|
+
/** A number field in turns per minute, and the most it gives; the field's range is set to 0..max when it has none. */
|
|
310
|
+
speed?: { path: string; max: number }
|
|
311
|
+
/** A closed text field, and its value that turns forward and the one that turns back. */
|
|
312
|
+
direction?: { path: string; forward: string; reverse: string }
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* The rotation card's commands (data binding design decision 1, 「회전」): a clip that turns the joint once around and
|
|
317
|
+
* keeps adding the turn (wrap, accumulating), a reverse one when a direction is given, the motion group holding them,
|
|
318
|
+
* and the bindings -- on to the group's pause, speed to its speed, direction to its clip. Nothing new runs them: they
|
|
319
|
+
* are the drivers, the group and the bindings any author could write, and stay editable one by one.
|
|
320
|
+
*
|
|
321
|
+
* The clip lasts `T = 3 × 60 / max rpm` seconds, so the most the field gives plays at the group's top speed, 3 -- the
|
|
322
|
+
* speed range is not widened. One turn per `T` at speed 1, and speed = rpm × T / 60. A stop is a pause: the joint stays
|
|
323
|
+
* where it is, where a stopped clip would jump back to its first key.
|
|
324
|
+
*/
|
|
325
|
+
export function rotationStepsOf(asset: FigureAsset, c: RotationChoice): FigureProposalStep[] {
|
|
326
|
+
const joint = endlessJointsOf(asset).find(j => j.input === c.input)
|
|
327
|
+
if (!joint) throw new FigureContractError('TARGET_ABSENT', c.input, `${c.input} is not a joint that turns without end`)
|
|
328
|
+
const turn = joint.unit === 'deg' ? 360 : 2 * Math.PI
|
|
329
|
+
const seconds = c.speed && c.speed.max > 0 ? round((3 * 60) / c.speed.max) : 1
|
|
330
|
+
const group = freeIdOf((asset.motionGroups ?? []).map(g => g.id), `${joint.input}-spin`)
|
|
331
|
+
const forward = group
|
|
332
|
+
const reverse = `${group}-reverse`
|
|
333
|
+
const steps: FigureProposalStep[] = []
|
|
334
|
+
const schema = asset.dataInterface?.schema ?? []
|
|
335
|
+
if (c.speed) {
|
|
336
|
+
const field = fieldAt(schema, c.speed.path)
|
|
337
|
+
if (field?.kind === 'number' && (field.min === undefined || field.max === undefined))
|
|
338
|
+
steps.push({ kind: 'set-data-schema', schema: schemaWithFieldReplaced(schema, c.speed.path, { ...field, min: field.min ?? 0, max: field.max ?? c.speed.max }) })
|
|
339
|
+
}
|
|
340
|
+
const spin = (clip: string, way: 1 | -1, inGroup: boolean): FigureProposalStep => ({
|
|
341
|
+
kind: 'set-driver',
|
|
342
|
+
state: joint.input,
|
|
343
|
+
clip,
|
|
344
|
+
duration: seconds,
|
|
345
|
+
keys: [
|
|
346
|
+
{ seconds: 0, value: 0 },
|
|
347
|
+
{ seconds, value: way * turn }
|
|
348
|
+
],
|
|
349
|
+
interpolation: 'linear',
|
|
350
|
+
loop: 'wrap',
|
|
351
|
+
accumulates: true,
|
|
352
|
+
...(inGroup ? { group } : {})
|
|
353
|
+
})
|
|
354
|
+
const clips = c.direction ? [forward, reverse] : [forward]
|
|
355
|
+
steps.push(spin(forward, 1, false))
|
|
356
|
+
steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips: [forward], defaultClip: forward, transitionSeconds: 0 } })
|
|
357
|
+
if (c.direction) {
|
|
358
|
+
steps.push(spin(reverse, -1, true))
|
|
359
|
+
steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips, defaultClip: forward, transitionSeconds: 0 } })
|
|
360
|
+
}
|
|
361
|
+
const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id)
|
|
362
|
+
const bind = (base: string, binding: Omit<FigureDataBinding, 'id'>): FigureProposalStep => {
|
|
363
|
+
const id = freeIdOf(taken, base)
|
|
364
|
+
taken.push(id)
|
|
365
|
+
return { kind: 'set-data-binding', binding: { id, ...binding } }
|
|
366
|
+
}
|
|
367
|
+
if (c.on) steps.push(bind(`${group}-on`, { accessor: c.on, behavior: { kind: 'motion-paused', group }, rule: 'map', param: { true: false, false: true } }))
|
|
368
|
+
if (c.speed) steps.push(bind(`${group}-speed`, { accessor: c.speed.path, behavior: { kind: 'motion-speed', group }, rule: 'scale', param: { scale: round(seconds / 60), offset: 0 } }))
|
|
369
|
+
if (c.direction) {
|
|
370
|
+
const values = domainOf(fieldAt(schema, c.direction.path) ?? { kind: 'text' }).map(String)
|
|
371
|
+
const covered = values.every(v => v === c.direction!.forward || v === c.direction!.reverse)
|
|
372
|
+
steps.push(
|
|
373
|
+
bind(`${group}-direction`, {
|
|
374
|
+
accessor: c.direction.path,
|
|
375
|
+
behavior: { kind: 'motion-clip', group },
|
|
376
|
+
rule: 'map',
|
|
377
|
+
param: { [c.direction.forward]: forward, [c.direction.reverse]: reverse, ...(covered ? {} : { default: forward }) }
|
|
378
|
+
})
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
return steps
|
|
382
|
+
}
|
|
@@ -1668,6 +1668,7 @@ export class FigureEditor extends LitElement {
|
|
|
1668
1668
|
?locked=${unavailable}
|
|
1669
1669
|
@data-sample=${(e: CustomEvent) => (this.dataSample = e.detail.data)}
|
|
1670
1670
|
@part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
|
|
1671
|
+
@part-edits=${(e: CustomEvent) => this.run(() => this.session!.applyRecipe(e.detail.edits))}
|
|
1671
1672
|
></figure-data-panel>
|
|
1672
1673
|
</div>
|
|
1673
1674
|
<div panel flush ?off=${tab !== 'verdict'}><slot name="verdict"></slot></div>
|
|
@@ -13,13 +13,16 @@ import {
|
|
|
13
13
|
behaviorsOf,
|
|
14
14
|
bindingDraftOf,
|
|
15
15
|
domainOf,
|
|
16
|
+
endlessJointsOf,
|
|
16
17
|
fieldAt,
|
|
17
18
|
fieldOfKind,
|
|
18
19
|
fitsField,
|
|
19
20
|
partsOf,
|
|
20
21
|
portDraftOf,
|
|
22
|
+
rotationStepsOf,
|
|
21
23
|
sampleOf,
|
|
22
24
|
schemaRowsOf,
|
|
25
|
+
type RotationChoice,
|
|
23
26
|
schemaWithField,
|
|
24
27
|
schemaWithFieldReplaced,
|
|
25
28
|
schemaWithoutField,
|
|
@@ -163,6 +166,8 @@ export class FigureDataPanel extends localize(i18next)(LitElement) {
|
|
|
163
166
|
/** The binding opened to edit its rule, by id. */
|
|
164
167
|
@state() private opened = ''
|
|
165
168
|
@state() private porting = ''
|
|
169
|
+
/** The rotation card as filled in: the joint, and the fields for on, speed (and its most rpm) and direction. */
|
|
170
|
+
@state() private rotating = { input: '', on: '', speed: '', max: 0, direction: '', forward: '', reverse: '' }
|
|
166
171
|
/** The sample as typed, JSON; empty until edited, the schema's example shown until then. */
|
|
167
172
|
@state() private sample = ''
|
|
168
173
|
@state() private sampling = false
|
|
@@ -376,6 +381,7 @@ export class FigureDataPanel extends localize(i18next)(LitElement) {
|
|
|
376
381
|
${this.help.hint('figure.text.data-bindings-explained')}
|
|
377
382
|
${bindings.map(b => this.renderBinding(asset, choices, b))}
|
|
378
383
|
${rows.length ? this.renderAddBinding(asset, rows, choices) : html`<p hint>${i18next.t('figure.text.data-no-fields')}</p>`}
|
|
384
|
+
${this.renderRotation(asset, rows)}
|
|
379
385
|
</section>`
|
|
380
386
|
}
|
|
381
387
|
|
|
@@ -573,6 +579,81 @@ export class FigureDataPanel extends localize(i18next)(LitElement) {
|
|
|
573
579
|
</button>`
|
|
574
580
|
}
|
|
575
581
|
|
|
582
|
+
/**
|
|
583
|
+
* 「+ 회전」: a joint that turns without end, turned by the data -- on, speed in rpm, direction -- in one step. The
|
|
584
|
+
* clips, the group and the bindings it writes are ordinary ones, edited one by one afterwards.
|
|
585
|
+
*/
|
|
586
|
+
private renderRotation(asset: FigureAsset, rows: FigureDataRow[]) {
|
|
587
|
+
const joints = endlessJointsOf(asset)
|
|
588
|
+
if (!joints.length) return nothing
|
|
589
|
+
const r = this.rotating
|
|
590
|
+
const joint = joints.find(j => j.input === r.input) ?? joints[0]!
|
|
591
|
+
const values = rows.filter(row => !row.path.includes('[]'))
|
|
592
|
+
const of = (kind: FigureDataNode['kind'], closed = false) => values.filter(row => row.node.kind === kind && (!closed || domainOf(row.node).length > 0))
|
|
593
|
+
const speedRow = values.find(row => row.path === r.speed)
|
|
594
|
+
const speedMax = r.max || (speedRow?.node.kind === 'number' ? (speedRow.node.max ?? 0) : 0)
|
|
595
|
+
const directionRow = values.find(row => row.path === r.direction)
|
|
596
|
+
const directions = directionRow ? domainOf(directionRow.node).map(String) : []
|
|
597
|
+
const forward = directions.includes(r.forward) ? r.forward : (directions[0] ?? '')
|
|
598
|
+
const reverse = directions.includes(r.reverse) ? r.reverse : (directions.find(v => v !== forward) ?? '')
|
|
599
|
+
const set = (patch: Partial<typeof r>) => (this.rotating = { ...r, ...patch })
|
|
600
|
+
const typed = (e: Event) => (e.target as HTMLSelectElement | HTMLInputElement).value
|
|
601
|
+
const pick = (rows: FigureDataRow[], value: string, change: (v: string) => void) =>
|
|
602
|
+
html`<select ?disabled=${this.locked} @change=${(e: Event) => change(typed(e))}>
|
|
603
|
+
<option value="" ?selected=${!value}>${i18next.t('figure.label.data-none')}</option>
|
|
604
|
+
${rows.map(row => html`<option value=${row.path} ?selected=${row.path === value}>${row.path}</option>`)}
|
|
605
|
+
</select>`
|
|
606
|
+
const choice: RotationChoice = {
|
|
607
|
+
input: joint.input,
|
|
608
|
+
label: i18next.t('figure.label.data-rotation-of', { name: joint.label }),
|
|
609
|
+
...(r.on ? { on: r.on } : {}),
|
|
610
|
+
...(r.speed ? { speed: { path: r.speed, max: speedMax } } : {}),
|
|
611
|
+
...(r.direction && forward && reverse ? { direction: { path: r.direction, forward, reverse } } : {})
|
|
612
|
+
}
|
|
613
|
+
const ready = !r.speed || speedMax > 0
|
|
614
|
+
return html`<details>
|
|
615
|
+
<summary>${i18next.t('figure.label.data-rotation')}</summary>
|
|
616
|
+
<p hint>${i18next.t('figure.text.data-rotation-explained')}</p>
|
|
617
|
+
<div form>
|
|
618
|
+
<span>${i18next.t('figure.label.data-rotation-joint')}</span>
|
|
619
|
+
<select ?disabled=${this.locked} @change=${(e: Event) => set({ input: typed(e) })}>
|
|
620
|
+
${joints.map(j => html`<option value=${j.input} ?selected=${j === joint}>${j.label}</option>`)}
|
|
621
|
+
</select>
|
|
622
|
+
<span>${i18next.t('figure.label.data-rotation-on')}</span>
|
|
623
|
+
${pick(of('boolean'), r.on, v => set({ on: v }))}
|
|
624
|
+
<span>${i18next.t('figure.label.data-rotation-speed')}</span>
|
|
625
|
+
${pick(of('number'), r.speed, v => set({ speed: v, max: 0 }))}
|
|
626
|
+
${r.speed
|
|
627
|
+
? html`<span>${i18next.t('figure.label.data-rotation-max')}</span>
|
|
628
|
+
<input type="number" min="0" step="any" .value=${speedMax ? String(speedMax) : ''} ?disabled=${this.locked} @change=${(e: Event) => set({ max: (e.target as HTMLInputElement).valueAsNumber || 0 })} />`
|
|
629
|
+
: nothing}
|
|
630
|
+
<span>${i18next.t('figure.label.data-rotation-direction')}</span>
|
|
631
|
+
${pick(of('text', true), r.direction, v => set({ direction: v, forward: '', reverse: '' }))}
|
|
632
|
+
${directions.length
|
|
633
|
+
? html`<span>${i18next.t('figure.label.data-rotation-forward')}</span>
|
|
634
|
+
<select ?disabled=${this.locked} @change=${(e: Event) => set({ forward: typed(e) })}>
|
|
635
|
+
${directions.map(v => html`<option value=${v} ?selected=${v === forward}>${v}</option>`)}
|
|
636
|
+
</select>
|
|
637
|
+
<span>${i18next.t('figure.label.data-rotation-reverse')}</span>
|
|
638
|
+
<select ?disabled=${this.locked} @change=${(e: Event) => set({ reverse: typed(e) })}>
|
|
639
|
+
${directions.map(v => html`<option value=${v} ?selected=${v === reverse}>${v}</option>`)}
|
|
640
|
+
</select>`
|
|
641
|
+
: nothing}
|
|
642
|
+
</div>
|
|
643
|
+
${ready ? nothing : html`<p hint>${i18next.t('figure.text.data-rotation-needs-max')}</p>`}
|
|
644
|
+
<button
|
|
645
|
+
act
|
|
646
|
+
?disabled=${this.locked || !ready}
|
|
647
|
+
@click=${() => {
|
|
648
|
+
this.dispatchEvent(new CustomEvent('part-edits', { detail: { edits: rotationStepsOf(asset, choice) }, bubbles: true, composed: true }))
|
|
649
|
+
this.rotating = { input: '', on: '', speed: '', max: 0, direction: '', forward: '', reverse: '' }
|
|
650
|
+
}}
|
|
651
|
+
>
|
|
652
|
+
<md-icon>add</md-icon>${i18next.t('figure.button.data-add-rotation')}
|
|
653
|
+
</button>
|
|
654
|
+
</details>`
|
|
655
|
+
}
|
|
656
|
+
|
|
576
657
|
/* ---------------------------------------------------------------- status ports */
|
|
577
658
|
|
|
578
659
|
private renderPorts(asset: FigureAsset) {
|
|
@@ -46,7 +46,13 @@ export class FigureDetailDock extends localize(i18next)(LitElement) {
|
|
|
46
46
|
button[icon]:hover:not(:disabled) { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }
|
|
47
47
|
button[icon]:disabled { opacity: 0.38; cursor: default; }
|
|
48
48
|
button[icon] md-icon { --md-icon-size: 18px; }
|
|
49
|
-
|
|
49
|
+
/*
|
|
50
|
+
The figure stays in view under what it is (which model, how many fit) and its tabs; only the tab's body scrolls
|
|
51
|
+
(user 2026-10-15: a clip played at the bottom of a scrolled dock moved a figure no longer seen).
|
|
52
|
+
*/
|
|
53
|
+
[body] { flex: 1; display: flex; flex-direction: column; min-height: 0; }
|
|
54
|
+
[body] > * { flex: none; }
|
|
55
|
+
[body] > figure-asset-viewer { flex: 1; }
|
|
50
56
|
[meta] { display: grid; gap: 8px; padding: 0 16px 12px; }
|
|
51
57
|
/* Which model is shown: words that change colour, not boxes. */
|
|
52
58
|
[source] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; color: var(--md-sys-color-on-surface-variant); }
|
|
@@ -60,7 +66,7 @@ export class FigureDetailDock extends localize(i18next)(LitElement) {
|
|
|
60
66
|
dl[stats] > div { display: flex; align-items: baseline; gap: 4px; }
|
|
61
67
|
dl[stats] dd { font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
62
68
|
p { margin: 0; line-height: 1.5; }
|
|
63
|
-
[description] { color: var(--md-sys-color-on-surface-variant); }
|
|
69
|
+
[description] { padding: 10px 16px 0; color: var(--md-sys-color-on-surface-variant); }
|
|
64
70
|
[status] { display: grid; gap: 8px; justify-items: start; padding: 12px 16px; }
|
|
65
71
|
@media (max-width: 1439.98px) {
|
|
66
72
|
:host { position: fixed; inset: var(--figure-detail-top, 44px) 0 0 auto; height: calc(100dvh - var(--figure-detail-top, 44px)); z-index: 11; box-shadow: -4px 0 16px rgb(0 0 0 / 20%); }
|
|
@@ -199,10 +205,10 @@ export class FigureDetailDock extends localize(i18next)(LitElement) {
|
|
|
199
205
|
<div><dt>${i18next.t('figure.label.budget-groups')}</dt><dd>${cost?.groups ?? '—'}</dd></div>
|
|
200
206
|
<div><dt>${i18next.t('figure.label.budget-parts')}</dt><dd>${this.asset.appearance.length}</dd></div>
|
|
201
207
|
</dl>` : nothing}
|
|
202
|
-
${saved.description ? html`<p description>${saved.description}</p>` : nothing}
|
|
203
208
|
</section>` : nothing}
|
|
204
209
|
${this.asset ? html`<figure-asset-viewer .asset=${this.asset}
|
|
205
210
|
.keepShot=${saved?.id ? (dataUrl: string) => this.keepThumbnail(saved.id, dataUrl) : undefined}>
|
|
211
|
+
${saved?.description ? html`<p slot="facts" description>${saved.description}</p>` : nothing}
|
|
206
212
|
${saved ? html`<figure-facts-list slot="facts" .asset=${this.asset} .row=${saved}></figure-facts-list>` : nothing}
|
|
207
213
|
</figure-asset-viewer>` : nothing}
|
|
208
214
|
</div>`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FigureAsset, FigureDataBehavior, FigureDataBinding, FigureDataField, FigureDataNode, FigureDataRow, FigureDataValue, FigureDataValueIssue, FigureStatusPort } from '@hatiolab/figure-model';
|
|
1
|
+
import type { FigureAsset, FigureDataBehavior, FigureDataBinding, FigureDataField, FigureDataNode, FigureDataRow, FigureDataValue, FigureDataValueIssue, FigureProposalStep, FigureStatusPort } from '@hatiolab/figure-model';
|
|
2
2
|
import { type FigureDataResult } from '@hatiolab/things-scene';
|
|
3
3
|
/** Something a binding can move, with what the screen shows of it. */
|
|
4
4
|
export interface BehaviorChoice {
|
|
@@ -72,3 +72,41 @@ export declare function motionWrittenByData(asset: FigureAsset): Record<string,
|
|
|
72
72
|
clip: boolean;
|
|
73
73
|
speed: boolean;
|
|
74
74
|
}>;
|
|
75
|
+
/** A joint that turns without end (a roller, a fan, a turntable): its control, by the id the clips write. */
|
|
76
|
+
export interface EndlessJoint {
|
|
77
|
+
input: string;
|
|
78
|
+
label: string;
|
|
79
|
+
unit: 'deg' | 'rad';
|
|
80
|
+
}
|
|
81
|
+
/** The joints that turn without end: revolute, with no limits, leading no one's mimic. */
|
|
82
|
+
export declare function endlessJointsOf(asset: FigureAsset): EndlessJoint[];
|
|
83
|
+
/** What the rotation card is told: the joint, and the data fields that turn it on, set its speed and its direction. */
|
|
84
|
+
export interface RotationChoice {
|
|
85
|
+
input: string;
|
|
86
|
+
/** The motion group's name, shown to a person. */
|
|
87
|
+
label: string;
|
|
88
|
+
/** A boolean field: true turns, false stops where it is. */
|
|
89
|
+
on?: string;
|
|
90
|
+
/** A number field in turns per minute, and the most it gives; the field's range is set to 0..max when it has none. */
|
|
91
|
+
speed?: {
|
|
92
|
+
path: string;
|
|
93
|
+
max: number;
|
|
94
|
+
};
|
|
95
|
+
/** A closed text field, and its value that turns forward and the one that turns back. */
|
|
96
|
+
direction?: {
|
|
97
|
+
path: string;
|
|
98
|
+
forward: string;
|
|
99
|
+
reverse: string;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The rotation card's commands (data binding design decision 1, 「회전」): a clip that turns the joint once around and
|
|
104
|
+
* keeps adding the turn (wrap, accumulating), a reverse one when a direction is given, the motion group holding them,
|
|
105
|
+
* and the bindings -- on to the group's pause, speed to its speed, direction to its clip. Nothing new runs them: they
|
|
106
|
+
* are the drivers, the group and the bindings any author could write, and stay editable one by one.
|
|
107
|
+
*
|
|
108
|
+
* The clip lasts `T = 3 × 60 / max rpm` seconds, so the most the field gives plays at the group's top speed, 3 -- the
|
|
109
|
+
* speed range is not widened. One turn per `T` at speed 1, and speed = rpm × T / 60. A stop is a pause: the joint stays
|
|
110
|
+
* where it is, where a stopped clip would jump back to its first key.
|
|
111
|
+
*/
|
|
112
|
+
export declare function rotationStepsOf(asset: FigureAsset, c: RotationChoice): FigureProposalStep[];
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* of data is things-scene's; this only lists what there is to choose, drafts a binding the kernel then checks, and edits
|
|
5
5
|
* the schema as a tree. Kept apart from the element so it is tested without a browser.
|
|
6
6
|
*/
|
|
7
|
-
import { FIGURE_PART_ID, FIGURE_MOTION_SPEED_MAX, figureDataRows, parseFigureAccessor } from '@hatiolab/figure-model';
|
|
7
|
+
import { FIGURE_PART_ID, FIGURE_MOTION_SPEED_MAX, FigureContractError, figureDataRows, parseFigureAccessor } from '@hatiolab/figure-model';
|
|
8
8
|
import { FigureInputs } from '@hatiolab/things-scene';
|
|
9
9
|
export const behaviorKeyOf = (b) => b.kind === 'state'
|
|
10
10
|
? `state:${b.input}`
|
|
@@ -269,4 +269,88 @@ export function motionWrittenByData(asset) {
|
|
|
269
269
|
}
|
|
270
270
|
return out;
|
|
271
271
|
}
|
|
272
|
+
/** The joints that turn without end: revolute, with no limits, leading no one's mimic. */
|
|
273
|
+
export function endlessJointsOf(asset) {
|
|
274
|
+
const out = [];
|
|
275
|
+
for (const j of asset.joints ?? []) {
|
|
276
|
+
if (j.type !== 'revolute' || j.lowerLimit !== undefined || j.upperLimit !== undefined || j.mimic)
|
|
277
|
+
continue;
|
|
278
|
+
const input = j.state?.id ?? j.id;
|
|
279
|
+
const unit = asset.document.model.inputs.find(i => i.id === input)?.unit;
|
|
280
|
+
if (unit !== 'deg' && unit !== 'rad')
|
|
281
|
+
continue;
|
|
282
|
+
if (!out.some(e => e.input === input))
|
|
283
|
+
out.push({ input, label: asset.stateInputs?.[input]?.label ?? input, unit });
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* The rotation card's commands (data binding design decision 1, 「회전」): a clip that turns the joint once around and
|
|
289
|
+
* keeps adding the turn (wrap, accumulating), a reverse one when a direction is given, the motion group holding them,
|
|
290
|
+
* and the bindings -- on to the group's pause, speed to its speed, direction to its clip. Nothing new runs them: they
|
|
291
|
+
* are the drivers, the group and the bindings any author could write, and stay editable one by one.
|
|
292
|
+
*
|
|
293
|
+
* The clip lasts `T = 3 × 60 / max rpm` seconds, so the most the field gives plays at the group's top speed, 3 -- the
|
|
294
|
+
* speed range is not widened. One turn per `T` at speed 1, and speed = rpm × T / 60. A stop is a pause: the joint stays
|
|
295
|
+
* where it is, where a stopped clip would jump back to its first key.
|
|
296
|
+
*/
|
|
297
|
+
export function rotationStepsOf(asset, c) {
|
|
298
|
+
const joint = endlessJointsOf(asset).find(j => j.input === c.input);
|
|
299
|
+
if (!joint)
|
|
300
|
+
throw new FigureContractError('TARGET_ABSENT', c.input, `${c.input} is not a joint that turns without end`);
|
|
301
|
+
const turn = joint.unit === 'deg' ? 360 : 2 * Math.PI;
|
|
302
|
+
const seconds = c.speed && c.speed.max > 0 ? round((3 * 60) / c.speed.max) : 1;
|
|
303
|
+
const group = freeIdOf((asset.motionGroups ?? []).map(g => g.id), `${joint.input}-spin`);
|
|
304
|
+
const forward = group;
|
|
305
|
+
const reverse = `${group}-reverse`;
|
|
306
|
+
const steps = [];
|
|
307
|
+
const schema = asset.dataInterface?.schema ?? [];
|
|
308
|
+
if (c.speed) {
|
|
309
|
+
const field = fieldAt(schema, c.speed.path);
|
|
310
|
+
if (field?.kind === 'number' && (field.min === undefined || field.max === undefined))
|
|
311
|
+
steps.push({ kind: 'set-data-schema', schema: schemaWithFieldReplaced(schema, c.speed.path, { ...field, min: field.min ?? 0, max: field.max ?? c.speed.max }) });
|
|
312
|
+
}
|
|
313
|
+
const spin = (clip, way, inGroup) => ({
|
|
314
|
+
kind: 'set-driver',
|
|
315
|
+
state: joint.input,
|
|
316
|
+
clip,
|
|
317
|
+
duration: seconds,
|
|
318
|
+
keys: [
|
|
319
|
+
{ seconds: 0, value: 0 },
|
|
320
|
+
{ seconds, value: way * turn }
|
|
321
|
+
],
|
|
322
|
+
interpolation: 'linear',
|
|
323
|
+
loop: 'wrap',
|
|
324
|
+
accumulates: true,
|
|
325
|
+
...(inGroup ? { group } : {})
|
|
326
|
+
});
|
|
327
|
+
const clips = c.direction ? [forward, reverse] : [forward];
|
|
328
|
+
steps.push(spin(forward, 1, false));
|
|
329
|
+
steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips: [forward], defaultClip: forward, transitionSeconds: 0 } });
|
|
330
|
+
if (c.direction) {
|
|
331
|
+
steps.push(spin(reverse, -1, true));
|
|
332
|
+
steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips, defaultClip: forward, transitionSeconds: 0 } });
|
|
333
|
+
}
|
|
334
|
+
const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id);
|
|
335
|
+
const bind = (base, binding) => {
|
|
336
|
+
const id = freeIdOf(taken, base);
|
|
337
|
+
taken.push(id);
|
|
338
|
+
return { kind: 'set-data-binding', binding: { id, ...binding } };
|
|
339
|
+
};
|
|
340
|
+
if (c.on)
|
|
341
|
+
steps.push(bind(`${group}-on`, { accessor: c.on, behavior: { kind: 'motion-paused', group }, rule: 'map', param: { true: false, false: true } }));
|
|
342
|
+
if (c.speed)
|
|
343
|
+
steps.push(bind(`${group}-speed`, { accessor: c.speed.path, behavior: { kind: 'motion-speed', group }, rule: 'scale', param: { scale: round(seconds / 60), offset: 0 } }));
|
|
344
|
+
if (c.direction) {
|
|
345
|
+
const values = domainOf(fieldAt(schema, c.direction.path) ?? { kind: 'text' }).map(String);
|
|
346
|
+
const covered = values.every(v => v === c.direction.forward || v === c.direction.reverse);
|
|
347
|
+
steps.push(bind(`${group}-direction`, {
|
|
348
|
+
accessor: c.direction.path,
|
|
349
|
+
behavior: { kind: 'motion-clip', group },
|
|
350
|
+
rule: 'map',
|
|
351
|
+
param: { [c.direction.forward]: forward, [c.direction.reverse]: reverse, ...(covered ? {} : { default: forward }) }
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
return steps;
|
|
355
|
+
}
|
|
272
356
|
//# sourceMappingURL=data-interface.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-interface.js","sourceRoot":"","sources":["../../client/modeller/data-interface.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The figure's data and what it moves (ADR-0051 §8, data binding design appendix A): what the data tab offers and\n * shows. Every rule is figure-model's -- which accessor holds, which rule's results a behavior takes -- and the reading\n * of data is things-scene's; this only lists what there is to choose, drafts a binding the kernel then checks, and edits\n * the schema as a tree. Kept apart from the element so it is tested without a browser.\n */\nimport { FIGURE_PART_ID, FIGURE_MOTION_SPEED_MAX, figureDataRows, parseFigureAccessor } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureDataBehavior, FigureDataBinding, FigureDataField, FigureDataNode, FigureDataRow, FigureDataValue, FigureDataValueIssue, FigureStatusPort } from '@hatiolab/figure-model'\nimport { FigureInputs, type FigureDataResult } from '@hatiolab/things-scene'\n\n/** Something a binding can move, with what the screen shows of it. */\nexport interface BehaviorChoice {\n /** One string per behavior, for a select's value. */\n key: string\n behavior: FigureDataBehavior\n /** The screen's word for the kind: a control, a colour, a group's clip, … */\n kind: FigureDataBehavior['kind']\n /** The control's label, the colour token, the group's label, the port's label, the axis. */\n name: string\n unit?: string\n min?: number\n max?: number\n}\n\nexport const behaviorKeyOf = (b: FigureDataBehavior): string =>\n b.kind === 'state'\n ? `state:${b.input}`\n : b.kind === 'palette'\n ? `palette:${b.token}`\n : b.kind === 'status'\n ? `status:${b.port}`\n : b.kind === 'pose'\n ? `pose:${b.axis}`\n : b.kind === 'items'\n ? `items:${b.slot ?? ''}`\n : `${b.kind}:${b.group}`\n\n/**\n * Everything the figure can do that data may drive: each control, each colour, each motion group's clip, speed and\n * pause, each status port, its position and heading, and -- when it has places -- the items in them.\n */\nexport function behaviorsOf(asset: FigureAsset): BehaviorChoice[] {\n const out: BehaviorChoice[] = []\n const add = (behavior: FigureDataBehavior, name: string, more: Partial<BehaviorChoice> = {}) => out.push({ key: behaviorKeyOf(behavior), behavior, kind: behavior.kind, name, ...more })\n for (const input of asset.document.model.inputs) {\n if (input.role !== 'state') continue\n add({ kind: 'state', input: input.id, sourceMode: 'observed' }, asset.stateInputs?.[input.id]?.label ?? input.id, { unit: input.unit, min: input.min, max: input.max })\n }\n for (const token of Object.keys(asset.palette)) add({ kind: 'palette', token }, token)\n for (const group of asset.motionGroups ?? []) {\n add({ kind: 'motion-clip', group: group.id }, group.label)\n add({ kind: 'motion-speed', group: group.id }, group.label, { min: 0, max: FIGURE_MOTION_SPEED_MAX })\n add({ kind: 'motion-paused', group: group.id }, group.label)\n }\n for (const port of asset.dataInterface?.ports ?? []) add({ kind: 'status', port: port.id }, port.label)\n for (const axis of ['x', 'z', 'y', 'heading'] as const) add({ kind: 'pose', axis, seconds: 1 }, axis, { unit: axis === 'heading' ? 'deg' : 'mm' })\n if (asset.document.capabilities.slotLayout || asset.document.capabilities.names?.includes('Holdable')) add({ kind: 'items' }, asset.document.capabilities.slotLayout?.label ?? 'items')\n return out\n}\n\nconst isValue = (n: FigureDataNode): n is Extract<FigureDataNode, { kind: 'number' | 'boolean' | 'text' }> => n.kind === 'number' || n.kind === 'boolean' || n.kind === 'text'\n\n/** Whether a field of this kind can drive the behavior, by some rule -- the choices offered for it. */\nexport function fitsField(node: FigureDataNode, choice: BehaviorChoice): boolean {\n if (choice.kind === 'items') return node.kind === 'list' || node.kind === 'text' || node.kind === 'number'\n if (!isValue(node)) return false\n if (choice.kind === 'pose') return node.kind === 'number'\n if (choice.kind === 'motion-speed') return node.kind === 'number' || node.kind === 'boolean'\n if (choice.kind === 'motion-paused') return node.kind === 'boolean' || (node.kind === 'text' && !!node.values)\n if (choice.kind === 'status') return node.kind !== 'boolean'\n return true\n}\n\n/** An id not taken among `taken`: `base`, then `base-2`, `base-3`, … made into the kernel's id shape. */\nexport function freeIdOf(taken: Iterable<string>, base: string): string {\n const used = new Set(taken)\n const cleaned = base.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^[^a-zA-Z]+/, '').replace(/-+$/, '') || 'binding'\n const id = FIGURE_PART_ID.test(cleaned) ? cleaned : 'binding'\n if (!used.has(id)) return id\n let n = 2\n while (used.has(`${id}-${n}`)) n++\n return `${id}-${n}`\n}\n\n/** How many mm, or degrees, one of a unit is: only lengths and angles are converted. */\nconst TO_MM: Record<string, number> = { mm: 1, cm: 10, m: 1000 }\nconst TO_DEG: Record<string, number> = { deg: 1, rad: 180 / Math.PI }\n\n/** The values a closed field takes: a closed text's values, both booleans. Empty when open. */\nexport const domainOf = (node: FigureDataNode): FigureDataValue[] => (node.kind === 'boolean' ? [false, true] : node.kind === 'text' && node.values ? [...node.values] : [])\n\n/**\n * A first binding of a field to a behavior, for the author to adjust: the value as it is when it already fits, a scale\n * when only its unit or range differs, one result per value (a map) for a closed field, a default for an open one.\n * Each result starts at what the behavior shows now -- the control's rest value, the colour, the group's default clip --\n * or, for a clip, the clip of the value's own name. The kernel says whether it holds.\n */\nexport function bindingDraftOf(asset: FigureAsset, accessor: string, node: FigureDataNode, choice: BehaviorChoice): FigureDataBinding {\n const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id)\n const name = accessor.replace(/\\[\\d+\\]/g, '').split('.').pop() ?? accessor\n const id = freeIdOf(taken, `${name}-${choice.kind === 'state' ? (choice.behavior as { input: string }).input : choice.kind}`)\n const behavior = choice.behavior\n const make = (rule: FigureDataBinding['rule'], param?: FigureDataBinding['param']): FigureDataBinding => ({ id, accessor, behavior, rule, ...(param ? { param } : {}) })\n const domain = domainOf(node)\n const caseMap = (each: (v: FigureDataValue) => FigureDataValue, fallback: FigureDataValue) => Object.fromEntries([...domain.map(v => [String(v), each(v)] as const), ...(domain.length ? [] : [['default', fallback] as const])])\n\n if (behavior.kind === 'items') {\n if (node.kind !== 'list') return make('value')\n const fields = node.item.kind === 'object' ? node.item.fields : []\n const pick = (...words: RegExp[]) => fields.find(f => isValue(f) && words.some(w => w.test(f.name)))?.name\n const itemId = pick(/^id$/i, /name$/i, /id$/i) ?? fields.find(isValue)?.name\n const type = pick(/type$/i)\n const place = pick(/^slot/i, /place/i, /port/i)\n const stack = pick(/stack/i, /level/i)\n return { id, accessor, behavior: { kind: 'items', ...(itemId ? { id: itemId } : {}), ...(type ? { type } : {}), ...(place ? { place } : {}), ...(stack ? { stack } : {}) }, rule: 'value' }\n }\n if (behavior.kind === 'status') return make('value')\n if (behavior.kind === 'palette') {\n const now = asset.palette[behavior.token] ?? '#000000'\n if (node.kind === 'number') return make('range', { default: now })\n return make('map', caseMap(() => now, now))\n }\n if (behavior.kind === 'motion-clip') {\n const group = (asset.motionGroups ?? []).find(g => g.id === behavior.group)\n const clips = group?.clips ?? []\n if (node.kind === 'text' && node.values && node.values.every(v => clips.includes(v))) return make('value')\n return make('map', caseMap(v => (clips.includes(String(v)) ? String(v) : (group?.defaultClip ?? '')), group?.defaultClip ?? ''))\n }\n if (behavior.kind === 'motion-paused') {\n if (node.kind === 'boolean') return make('value')\n return make('map', caseMap(() => false, false))\n }\n if (behavior.kind === 'pose') {\n if (node.kind !== 'number') return make('value')\n const want = behavior.axis === 'heading' ? TO_DEG : TO_MM\n const unit = node.unit ?? ''\n if (unit === (behavior.axis === 'heading' ? 'deg' : 'mm')) return make('value')\n return unit in want ? make('scale', { scale: want[unit]!, offset: 0 }) : make('value')\n }\n /* state and motion-speed: a number into a range. */\n const [lo, hi, unit] =\n behavior.kind === 'motion-speed' ? [0, FIGURE_MOTION_SPEED_MAX, 'ratio'] : [choice.min ?? 0, choice.max ?? 1, choice.unit ?? '']\n if (node.kind === 'number') {\n const sameUnit = behavior.kind === 'motion-speed' ? node.unit === undefined || node.unit === 'ratio' : node.unit === unit\n const inside = node.min !== undefined && node.max !== undefined && node.min >= lo && node.max <= hi\n if (sameUnit && inside) return make('value')\n if (node.min !== undefined && node.max !== undefined && node.max > node.min) {\n /* The field's range laid onto the behavior's. */\n const scale = (hi - lo) / (node.max - node.min)\n return make('scale', { scale: round(scale), offset: round(lo - scale * node.min) })\n }\n return make('value')\n }\n const rest = behavior.kind === 'motion-speed' ? 1 : (asset.stateDefaults[(behavior as { input: string }).input] ?? lo)\n if (node.kind === 'boolean') return make('map', { false: behavior.kind === 'motion-speed' ? 0 : lo, true: behavior.kind === 'motion-speed' ? 1 : hi })\n return make('map', caseMap(() => rest, rest))\n}\nconst round = (v: number) => Math.round(v * 1e6) / 1e6\n\n/* ---------------------------------------------------------------- the schema as a tree */\n\n/** A row's path (`L[].F`) as the accessor a value binding reads (`L[0].F`): the first element of each list. */\nexport const accessorOfRow = (path: string): string => path.replace(/\\[\\]/g, '[0]')\n\n/** The field at a row's path, or undefined. */\nexport function fieldAt(schema: readonly FigureDataField[], path: string): FigureDataField | undefined {\n let list: readonly FigureDataField[] = schema\n let found: FigureDataField | undefined\n for (const part of path.split('.')) {\n const name = part.replace(/\\[\\]/g, '')\n found = list.find(f => f.name === name)\n if (!found) return undefined\n let node: FigureDataNode = found\n while (node.kind === 'list') node = node.item\n list = node.kind === 'object' ? node.fields : []\n }\n return found\n}\n\n/** The fields under a row's path, where a new field goes: the top, an object's fields, a list's element's fields. */\nfunction fieldsUnder(schema: FigureDataField[], path: string): FigureDataField[] | undefined {\n if (!path) return schema\n const field = fieldAt(schema, path)\n if (!field) return undefined\n let node: FigureDataNode = field\n while (node.kind === 'list') node = node.item\n return node.kind === 'object' ? node.fields : undefined\n}\n\n/** The schema with a field added under a path ('' for the top): a copy, the given one left as it is. */\nexport function schemaWithField(schema: readonly FigureDataField[], under: string, field: FigureDataField): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n fieldsUnder(out, under)?.push(field)\n return out\n}\n\n/** The schema with the field at a path replaced (its name kept unless the new one says another). */\nexport function schemaWithFieldReplaced(schema: readonly FigureDataField[], path: string, field: FigureDataField): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n const parent = path.includes('.') ? path.slice(0, path.lastIndexOf('.')) : ''\n const list = fieldsUnder(out, parent)\n const name = path.split('.').pop()!.replace(/\\[\\]/g, '')\n const at = list?.findIndex(f => f.name === name) ?? -1\n if (list && at >= 0) list[at] = field\n return out\n}\n\n/** The schema without the field at a path. */\nexport function schemaWithoutField(schema: readonly FigureDataField[], path: string): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n const parent = path.includes('.') ? path.slice(0, path.lastIndexOf('.')) : ''\n const list = fieldsUnder(out, parent)\n const name = path.split('.').pop()!.replace(/\\[\\]/g, '')\n const at = list?.findIndex(f => f.name === name) ?? -1\n if (list && at >= 0) list.splice(at, 1)\n return out\n}\n\n/** A field of another kind, its name, label and json flag kept, the rest started fresh. */\nexport function fieldOfKind(field: FigureDataField, kind: FigureDataNode['kind']): FigureDataField {\n const head = { name: field.name, ...(field.label ? { label: field.label } : {}) }\n if (kind === 'object') return { ...head, kind, fields: field.kind === 'object' ? field.fields : [] }\n if (kind === 'list') return { ...head, kind, item: field.kind === 'list' ? field.item : { kind: 'object', fields: [] } }\n return { ...head, kind } as FigureDataField\n}\n\n/** The rows of the schema, each with the bindings that read it (figure-model's view). */\nexport const schemaRowsOf = (asset: FigureAsset): FigureDataRow[] => (asset.dataInterface ? figureDataRows(asset.dataInterface) : [])\n\n/* ---------------------------------------------------------------- reading a sample */\n\n/**\n * What a sample of data would do on this figure, read the one way a placed figure reads it (things-scene's figure\n * inputs): what it moves, or why it is refused, each value's reason. Nothing here reads data itself.\n */\nexport function sampleOf(asset: FigureAsset, sample: unknown): { issues: FigureDataValueIssue[]; result: FigureDataResult } {\n const inputs = new FigureInputs(asset)\n const taken = inputs.setData(sample)\n return { issues: taken.accepted ? [] : (taken.issues[0]?.issues ?? []), result: inputs.data }\n}\n\n/** The rows a person gives a value by hand, as a board's saved values: one value, read by a binding, not inside a list. */\nexport const valueRowsOf = (asset: FigureAsset): FigureDataRow[] => schemaRowsOf(asset).filter(r => isValue(r.node) && r.bindings.length > 0 && !r.path.includes('[]'))\n\n/** A value typed or chosen for a row, as its kind reads it: a number (NaN when empty), true or false, or the text. */\nexport function typedValueOf(node: FigureDataNode, typed: string): FigureDataValue {\n if (node.kind === 'number') return typed.trim() === '' ? Number.NaN : Number(typed)\n if (node.kind === 'boolean') return typed === 'true'\n return typed\n}\n\n/** The parts a status port may show on: the parts the figure's appearance names. */\nexport const partsOf = (asset: FigureAsset): string[] => [...new Set(asset.appearance.map(a => a.target))]\n\n/** A first status port: the label as given, on the first colour token, no part yet. The kernel says whether it holds. */\nexport function portDraftOf(asset: FigureAsset, label: string): FigureStatusPort {\n const taken = (asset.dataInterface?.ports ?? []).map(p => p.id)\n const token = Object.keys(asset.palette)[0]\n return { id: freeIdOf(taken, label.trim() || 'status'), label: label.trim(), tokens: token ? [token] : [], parts: [] }\n}\n\n/** Whether a text is an accessor the kernel takes. */\nexport const accessorTaken = (accessor: string): boolean => parseFigureAccessor(accessor) !== null\n\n/**\n * Which fields of each motion group the data binds: its clip, its speed. A field bound wins over the group's own setting\n * whenever the data, a saved value or the schema's default gives it a value, so the preview drives it through the data.\n */\nexport function motionWrittenByData(asset: FigureAsset): Record<string, { clip: boolean; speed: boolean }> {\n const out: Record<string, { clip: boolean; speed: boolean }> = {}\n for (const b of asset.dataInterface?.bindings ?? []) {\n if (b.behavior.kind !== 'motion-clip' && b.behavior.kind !== 'motion-speed') continue\n const one = (out[b.behavior.group] ??= { clip: false, speed: false })\n if (b.behavior.kind === 'motion-clip') one.clip = true\n else one.speed = true\n }\n return out\n}\n"]}
|
|
1
|
+
{"version":3,"file":"data-interface.js","sourceRoot":"","sources":["../../client/modeller/data-interface.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The figure's data and what it moves (ADR-0051 §8, data binding design appendix A): what the data tab offers and\n * shows. Every rule is figure-model's -- which accessor holds, which rule's results a behavior takes -- and the reading\n * of data is things-scene's; this only lists what there is to choose, drafts a binding the kernel then checks, and edits\n * the schema as a tree. Kept apart from the element so it is tested without a browser.\n */\nimport { FIGURE_PART_ID, FIGURE_MOTION_SPEED_MAX, FigureContractError, figureDataRows, parseFigureAccessor } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureDataBehavior, FigureDataBinding, FigureDataField, FigureDataNode, FigureDataRow, FigureDataValue, FigureDataValueIssue, FigureProposalStep, FigureStatusPort } from '@hatiolab/figure-model'\nimport { FigureInputs, type FigureDataResult } from '@hatiolab/things-scene'\n\n/** Something a binding can move, with what the screen shows of it. */\nexport interface BehaviorChoice {\n /** One string per behavior, for a select's value. */\n key: string\n behavior: FigureDataBehavior\n /** The screen's word for the kind: a control, a colour, a group's clip, … */\n kind: FigureDataBehavior['kind']\n /** The control's label, the colour token, the group's label, the port's label, the axis. */\n name: string\n unit?: string\n min?: number\n max?: number\n}\n\nexport const behaviorKeyOf = (b: FigureDataBehavior): string =>\n b.kind === 'state'\n ? `state:${b.input}`\n : b.kind === 'palette'\n ? `palette:${b.token}`\n : b.kind === 'status'\n ? `status:${b.port}`\n : b.kind === 'pose'\n ? `pose:${b.axis}`\n : b.kind === 'items'\n ? `items:${b.slot ?? ''}`\n : `${b.kind}:${b.group}`\n\n/**\n * Everything the figure can do that data may drive: each control, each colour, each motion group's clip, speed and\n * pause, each status port, its position and heading, and -- when it has places -- the items in them.\n */\nexport function behaviorsOf(asset: FigureAsset): BehaviorChoice[] {\n const out: BehaviorChoice[] = []\n const add = (behavior: FigureDataBehavior, name: string, more: Partial<BehaviorChoice> = {}) => out.push({ key: behaviorKeyOf(behavior), behavior, kind: behavior.kind, name, ...more })\n for (const input of asset.document.model.inputs) {\n if (input.role !== 'state') continue\n add({ kind: 'state', input: input.id, sourceMode: 'observed' }, asset.stateInputs?.[input.id]?.label ?? input.id, { unit: input.unit, min: input.min, max: input.max })\n }\n for (const token of Object.keys(asset.palette)) add({ kind: 'palette', token }, token)\n for (const group of asset.motionGroups ?? []) {\n add({ kind: 'motion-clip', group: group.id }, group.label)\n add({ kind: 'motion-speed', group: group.id }, group.label, { min: 0, max: FIGURE_MOTION_SPEED_MAX })\n add({ kind: 'motion-paused', group: group.id }, group.label)\n }\n for (const port of asset.dataInterface?.ports ?? []) add({ kind: 'status', port: port.id }, port.label)\n for (const axis of ['x', 'z', 'y', 'heading'] as const) add({ kind: 'pose', axis, seconds: 1 }, axis, { unit: axis === 'heading' ? 'deg' : 'mm' })\n if (asset.document.capabilities.slotLayout || asset.document.capabilities.names?.includes('Holdable')) add({ kind: 'items' }, asset.document.capabilities.slotLayout?.label ?? 'items')\n return out\n}\n\nconst isValue = (n: FigureDataNode): n is Extract<FigureDataNode, { kind: 'number' | 'boolean' | 'text' }> => n.kind === 'number' || n.kind === 'boolean' || n.kind === 'text'\n\n/** Whether a field of this kind can drive the behavior, by some rule -- the choices offered for it. */\nexport function fitsField(node: FigureDataNode, choice: BehaviorChoice): boolean {\n if (choice.kind === 'items') return node.kind === 'list' || node.kind === 'text' || node.kind === 'number'\n if (!isValue(node)) return false\n if (choice.kind === 'pose') return node.kind === 'number'\n if (choice.kind === 'motion-speed') return node.kind === 'number' || node.kind === 'boolean'\n if (choice.kind === 'motion-paused') return node.kind === 'boolean' || (node.kind === 'text' && !!node.values)\n if (choice.kind === 'status') return node.kind !== 'boolean'\n return true\n}\n\n/** An id not taken among `taken`: `base`, then `base-2`, `base-3`, … made into the kernel's id shape. */\nexport function freeIdOf(taken: Iterable<string>, base: string): string {\n const used = new Set(taken)\n const cleaned = base.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^[^a-zA-Z]+/, '').replace(/-+$/, '') || 'binding'\n const id = FIGURE_PART_ID.test(cleaned) ? cleaned : 'binding'\n if (!used.has(id)) return id\n let n = 2\n while (used.has(`${id}-${n}`)) n++\n return `${id}-${n}`\n}\n\n/** How many mm, or degrees, one of a unit is: only lengths and angles are converted. */\nconst TO_MM: Record<string, number> = { mm: 1, cm: 10, m: 1000 }\nconst TO_DEG: Record<string, number> = { deg: 1, rad: 180 / Math.PI }\n\n/** The values a closed field takes: a closed text's values, both booleans. Empty when open. */\nexport const domainOf = (node: FigureDataNode): FigureDataValue[] => (node.kind === 'boolean' ? [false, true] : node.kind === 'text' && node.values ? [...node.values] : [])\n\n/**\n * A first binding of a field to a behavior, for the author to adjust: the value as it is when it already fits, a scale\n * when only its unit or range differs, one result per value (a map) for a closed field, a default for an open one.\n * Each result starts at what the behavior shows now -- the control's rest value, the colour, the group's default clip --\n * or, for a clip, the clip of the value's own name. The kernel says whether it holds.\n */\nexport function bindingDraftOf(asset: FigureAsset, accessor: string, node: FigureDataNode, choice: BehaviorChoice): FigureDataBinding {\n const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id)\n const name = accessor.replace(/\\[\\d+\\]/g, '').split('.').pop() ?? accessor\n const id = freeIdOf(taken, `${name}-${choice.kind === 'state' ? (choice.behavior as { input: string }).input : choice.kind}`)\n const behavior = choice.behavior\n const make = (rule: FigureDataBinding['rule'], param?: FigureDataBinding['param']): FigureDataBinding => ({ id, accessor, behavior, rule, ...(param ? { param } : {}) })\n const domain = domainOf(node)\n const caseMap = (each: (v: FigureDataValue) => FigureDataValue, fallback: FigureDataValue) => Object.fromEntries([...domain.map(v => [String(v), each(v)] as const), ...(domain.length ? [] : [['default', fallback] as const])])\n\n if (behavior.kind === 'items') {\n if (node.kind !== 'list') return make('value')\n const fields = node.item.kind === 'object' ? node.item.fields : []\n const pick = (...words: RegExp[]) => fields.find(f => isValue(f) && words.some(w => w.test(f.name)))?.name\n const itemId = pick(/^id$/i, /name$/i, /id$/i) ?? fields.find(isValue)?.name\n const type = pick(/type$/i)\n const place = pick(/^slot/i, /place/i, /port/i)\n const stack = pick(/stack/i, /level/i)\n return { id, accessor, behavior: { kind: 'items', ...(itemId ? { id: itemId } : {}), ...(type ? { type } : {}), ...(place ? { place } : {}), ...(stack ? { stack } : {}) }, rule: 'value' }\n }\n if (behavior.kind === 'status') return make('value')\n if (behavior.kind === 'palette') {\n const now = asset.palette[behavior.token] ?? '#000000'\n if (node.kind === 'number') return make('range', { default: now })\n return make('map', caseMap(() => now, now))\n }\n if (behavior.kind === 'motion-clip') {\n const group = (asset.motionGroups ?? []).find(g => g.id === behavior.group)\n const clips = group?.clips ?? []\n if (node.kind === 'text' && node.values && node.values.every(v => clips.includes(v))) return make('value')\n return make('map', caseMap(v => (clips.includes(String(v)) ? String(v) : (group?.defaultClip ?? '')), group?.defaultClip ?? ''))\n }\n if (behavior.kind === 'motion-paused') {\n if (node.kind === 'boolean') return make('value')\n return make('map', caseMap(() => false, false))\n }\n if (behavior.kind === 'pose') {\n if (node.kind !== 'number') return make('value')\n const want = behavior.axis === 'heading' ? TO_DEG : TO_MM\n const unit = node.unit ?? ''\n if (unit === (behavior.axis === 'heading' ? 'deg' : 'mm')) return make('value')\n return unit in want ? make('scale', { scale: want[unit]!, offset: 0 }) : make('value')\n }\n /* state and motion-speed: a number into a range. */\n const [lo, hi, unit] =\n behavior.kind === 'motion-speed' ? [0, FIGURE_MOTION_SPEED_MAX, 'ratio'] : [choice.min ?? 0, choice.max ?? 1, choice.unit ?? '']\n if (node.kind === 'number') {\n const sameUnit = behavior.kind === 'motion-speed' ? node.unit === undefined || node.unit === 'ratio' : node.unit === unit\n const inside = node.min !== undefined && node.max !== undefined && node.min >= lo && node.max <= hi\n if (sameUnit && inside) return make('value')\n if (node.min !== undefined && node.max !== undefined && node.max > node.min) {\n /* The field's range laid onto the behavior's. */\n const scale = (hi - lo) / (node.max - node.min)\n return make('scale', { scale: round(scale), offset: round(lo - scale * node.min) })\n }\n return make('value')\n }\n const rest = behavior.kind === 'motion-speed' ? 1 : (asset.stateDefaults[(behavior as { input: string }).input] ?? lo)\n if (node.kind === 'boolean') return make('map', { false: behavior.kind === 'motion-speed' ? 0 : lo, true: behavior.kind === 'motion-speed' ? 1 : hi })\n return make('map', caseMap(() => rest, rest))\n}\nconst round = (v: number) => Math.round(v * 1e6) / 1e6\n\n/* ---------------------------------------------------------------- the schema as a tree */\n\n/** A row's path (`L[].F`) as the accessor a value binding reads (`L[0].F`): the first element of each list. */\nexport const accessorOfRow = (path: string): string => path.replace(/\\[\\]/g, '[0]')\n\n/** The field at a row's path, or undefined. */\nexport function fieldAt(schema: readonly FigureDataField[], path: string): FigureDataField | undefined {\n let list: readonly FigureDataField[] = schema\n let found: FigureDataField | undefined\n for (const part of path.split('.')) {\n const name = part.replace(/\\[\\]/g, '')\n found = list.find(f => f.name === name)\n if (!found) return undefined\n let node: FigureDataNode = found\n while (node.kind === 'list') node = node.item\n list = node.kind === 'object' ? node.fields : []\n }\n return found\n}\n\n/** The fields under a row's path, where a new field goes: the top, an object's fields, a list's element's fields. */\nfunction fieldsUnder(schema: FigureDataField[], path: string): FigureDataField[] | undefined {\n if (!path) return schema\n const field = fieldAt(schema, path)\n if (!field) return undefined\n let node: FigureDataNode = field\n while (node.kind === 'list') node = node.item\n return node.kind === 'object' ? node.fields : undefined\n}\n\n/** The schema with a field added under a path ('' for the top): a copy, the given one left as it is. */\nexport function schemaWithField(schema: readonly FigureDataField[], under: string, field: FigureDataField): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n fieldsUnder(out, under)?.push(field)\n return out\n}\n\n/** The schema with the field at a path replaced (its name kept unless the new one says another). */\nexport function schemaWithFieldReplaced(schema: readonly FigureDataField[], path: string, field: FigureDataField): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n const parent = path.includes('.') ? path.slice(0, path.lastIndexOf('.')) : ''\n const list = fieldsUnder(out, parent)\n const name = path.split('.').pop()!.replace(/\\[\\]/g, '')\n const at = list?.findIndex(f => f.name === name) ?? -1\n if (list && at >= 0) list[at] = field\n return out\n}\n\n/** The schema without the field at a path. */\nexport function schemaWithoutField(schema: readonly FigureDataField[], path: string): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n const parent = path.includes('.') ? path.slice(0, path.lastIndexOf('.')) : ''\n const list = fieldsUnder(out, parent)\n const name = path.split('.').pop()!.replace(/\\[\\]/g, '')\n const at = list?.findIndex(f => f.name === name) ?? -1\n if (list && at >= 0) list.splice(at, 1)\n return out\n}\n\n/** A field of another kind, its name, label and json flag kept, the rest started fresh. */\nexport function fieldOfKind(field: FigureDataField, kind: FigureDataNode['kind']): FigureDataField {\n const head = { name: field.name, ...(field.label ? { label: field.label } : {}) }\n if (kind === 'object') return { ...head, kind, fields: field.kind === 'object' ? field.fields : [] }\n if (kind === 'list') return { ...head, kind, item: field.kind === 'list' ? field.item : { kind: 'object', fields: [] } }\n return { ...head, kind } as FigureDataField\n}\n\n/** The rows of the schema, each with the bindings that read it (figure-model's view). */\nexport const schemaRowsOf = (asset: FigureAsset): FigureDataRow[] => (asset.dataInterface ? figureDataRows(asset.dataInterface) : [])\n\n/* ---------------------------------------------------------------- reading a sample */\n\n/**\n * What a sample of data would do on this figure, read the one way a placed figure reads it (things-scene's figure\n * inputs): what it moves, or why it is refused, each value's reason. Nothing here reads data itself.\n */\nexport function sampleOf(asset: FigureAsset, sample: unknown): { issues: FigureDataValueIssue[]; result: FigureDataResult } {\n const inputs = new FigureInputs(asset)\n const taken = inputs.setData(sample)\n return { issues: taken.accepted ? [] : (taken.issues[0]?.issues ?? []), result: inputs.data }\n}\n\n/** The rows a person gives a value by hand, as a board's saved values: one value, read by a binding, not inside a list. */\nexport const valueRowsOf = (asset: FigureAsset): FigureDataRow[] => schemaRowsOf(asset).filter(r => isValue(r.node) && r.bindings.length > 0 && !r.path.includes('[]'))\n\n/** A value typed or chosen for a row, as its kind reads it: a number (NaN when empty), true or false, or the text. */\nexport function typedValueOf(node: FigureDataNode, typed: string): FigureDataValue {\n if (node.kind === 'number') return typed.trim() === '' ? Number.NaN : Number(typed)\n if (node.kind === 'boolean') return typed === 'true'\n return typed\n}\n\n/** The parts a status port may show on: the parts the figure's appearance names. */\nexport const partsOf = (asset: FigureAsset): string[] => [...new Set(asset.appearance.map(a => a.target))]\n\n/** A first status port: the label as given, on the first colour token, no part yet. The kernel says whether it holds. */\nexport function portDraftOf(asset: FigureAsset, label: string): FigureStatusPort {\n const taken = (asset.dataInterface?.ports ?? []).map(p => p.id)\n const token = Object.keys(asset.palette)[0]\n return { id: freeIdOf(taken, label.trim() || 'status'), label: label.trim(), tokens: token ? [token] : [], parts: [] }\n}\n\n/** Whether a text is an accessor the kernel takes. */\nexport const accessorTaken = (accessor: string): boolean => parseFigureAccessor(accessor) !== null\n\n/**\n * Which fields of each motion group the data binds: its clip, its speed. A field bound wins over the group's own setting\n * whenever the data, a saved value or the schema's default gives it a value, so the preview drives it through the data.\n */\nexport function motionWrittenByData(asset: FigureAsset): Record<string, { clip: boolean; speed: boolean }> {\n const out: Record<string, { clip: boolean; speed: boolean }> = {}\n for (const b of asset.dataInterface?.bindings ?? []) {\n if (b.behavior.kind !== 'motion-clip' && b.behavior.kind !== 'motion-speed') continue\n const one = (out[b.behavior.group] ??= { clip: false, speed: false })\n if (b.behavior.kind === 'motion-clip') one.clip = true\n else one.speed = true\n }\n return out\n}\n\n/* ---------------------------------------------------------------- the rotation card */\n\n/** A joint that turns without end (a roller, a fan, a turntable): its control, by the id the clips write. */\nexport interface EndlessJoint {\n input: string\n label: string\n unit: 'deg' | 'rad'\n}\n\n/** The joints that turn without end: revolute, with no limits, leading no one's mimic. */\nexport function endlessJointsOf(asset: FigureAsset): EndlessJoint[] {\n const out: EndlessJoint[] = []\n for (const j of asset.joints ?? []) {\n if (j.type !== 'revolute' || j.lowerLimit !== undefined || j.upperLimit !== undefined || j.mimic) continue\n const input = j.state?.id ?? j.id\n const unit = asset.document.model.inputs.find(i => i.id === input)?.unit\n if (unit !== 'deg' && unit !== 'rad') continue\n if (!out.some(e => e.input === input)) out.push({ input, label: asset.stateInputs?.[input]?.label ?? input, unit })\n }\n return out\n}\n\n/** What the rotation card is told: the joint, and the data fields that turn it on, set its speed and its direction. */\nexport interface RotationChoice {\n input: string\n /** The motion group's name, shown to a person. */\n label: string\n /** A boolean field: true turns, false stops where it is. */\n on?: string\n /** A number field in turns per minute, and the most it gives; the field's range is set to 0..max when it has none. */\n speed?: { path: string; max: number }\n /** A closed text field, and its value that turns forward and the one that turns back. */\n direction?: { path: string; forward: string; reverse: string }\n}\n\n/**\n * The rotation card's commands (data binding design decision 1, 「회전」): a clip that turns the joint once around and\n * keeps adding the turn (wrap, accumulating), a reverse one when a direction is given, the motion group holding them,\n * and the bindings -- on to the group's pause, speed to its speed, direction to its clip. Nothing new runs them: they\n * are the drivers, the group and the bindings any author could write, and stay editable one by one.\n *\n * The clip lasts `T = 3 × 60 / max rpm` seconds, so the most the field gives plays at the group's top speed, 3 -- the\n * speed range is not widened. One turn per `T` at speed 1, and speed = rpm × T / 60. A stop is a pause: the joint stays\n * where it is, where a stopped clip would jump back to its first key.\n */\nexport function rotationStepsOf(asset: FigureAsset, c: RotationChoice): FigureProposalStep[] {\n const joint = endlessJointsOf(asset).find(j => j.input === c.input)\n if (!joint) throw new FigureContractError('TARGET_ABSENT', c.input, `${c.input} is not a joint that turns without end`)\n const turn = joint.unit === 'deg' ? 360 : 2 * Math.PI\n const seconds = c.speed && c.speed.max > 0 ? round((3 * 60) / c.speed.max) : 1\n const group = freeIdOf((asset.motionGroups ?? []).map(g => g.id), `${joint.input}-spin`)\n const forward = group\n const reverse = `${group}-reverse`\n const steps: FigureProposalStep[] = []\n const schema = asset.dataInterface?.schema ?? []\n if (c.speed) {\n const field = fieldAt(schema, c.speed.path)\n if (field?.kind === 'number' && (field.min === undefined || field.max === undefined))\n steps.push({ kind: 'set-data-schema', schema: schemaWithFieldReplaced(schema, c.speed.path, { ...field, min: field.min ?? 0, max: field.max ?? c.speed.max }) })\n }\n const spin = (clip: string, way: 1 | -1, inGroup: boolean): FigureProposalStep => ({\n kind: 'set-driver',\n state: joint.input,\n clip,\n duration: seconds,\n keys: [\n { seconds: 0, value: 0 },\n { seconds, value: way * turn }\n ],\n interpolation: 'linear',\n loop: 'wrap',\n accumulates: true,\n ...(inGroup ? { group } : {})\n })\n const clips = c.direction ? [forward, reverse] : [forward]\n steps.push(spin(forward, 1, false))\n steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips: [forward], defaultClip: forward, transitionSeconds: 0 } })\n if (c.direction) {\n steps.push(spin(reverse, -1, true))\n steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips, defaultClip: forward, transitionSeconds: 0 } })\n }\n const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id)\n const bind = (base: string, binding: Omit<FigureDataBinding, 'id'>): FigureProposalStep => {\n const id = freeIdOf(taken, base)\n taken.push(id)\n return { kind: 'set-data-binding', binding: { id, ...binding } }\n }\n if (c.on) steps.push(bind(`${group}-on`, { accessor: c.on, behavior: { kind: 'motion-paused', group }, rule: 'map', param: { true: false, false: true } }))\n if (c.speed) steps.push(bind(`${group}-speed`, { accessor: c.speed.path, behavior: { kind: 'motion-speed', group }, rule: 'scale', param: { scale: round(seconds / 60), offset: 0 } }))\n if (c.direction) {\n const values = domainOf(fieldAt(schema, c.direction.path) ?? { kind: 'text' }).map(String)\n const covered = values.every(v => v === c.direction!.forward || v === c.direction!.reverse)\n steps.push(\n bind(`${group}-direction`, {\n accessor: c.direction.path,\n behavior: { kind: 'motion-clip', group },\n rule: 'map',\n param: { [c.direction.forward]: forward, [c.direction.reverse]: reverse, ...(covered ? {} : { default: forward }) }\n })\n )\n }\n return steps\n}\n"]}
|
|
@@ -1695,6 +1695,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1695
1695
|
?locked=${unavailable}
|
|
1696
1696
|
@data-sample=${(e) => (this.dataSample = e.detail.data)}
|
|
1697
1697
|
@part-edit=${(e) => this.run(() => this.session.edit(e.detail.edit))}
|
|
1698
|
+
@part-edits=${(e) => this.run(() => this.session.applyRecipe(e.detail.edits))}
|
|
1698
1699
|
></figure-data-panel>
|
|
1699
1700
|
</div>
|
|
1700
1701
|
<div panel flush ?off=${tab !== 'verdict'}><slot name="verdict"></slot></div>
|