@things-factory/figure-ui 10.1.156 → 10.1.158

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 (37) hide show
  1. package/client/modeller/clips.ts +43 -4
  2. package/client/modeller/data-interface.ts +110 -0
  3. package/client/modeller/editor.ts +16 -2
  4. package/client/modeller/figure-clips.ts +125 -12
  5. package/client/modeller/figure-data-panel.ts +308 -0
  6. package/client/modeller/figure-preview.ts +41 -4
  7. package/client/modeller/refusals.ts +46 -0
  8. package/dist-client/modeller/clips.d.ts +32 -2
  9. package/dist-client/modeller/clips.js +32 -4
  10. package/dist-client/modeller/clips.js.map +1 -1
  11. package/dist-client/modeller/data-interface.d.ts +42 -0
  12. package/dist-client/modeller/data-interface.js +95 -0
  13. package/dist-client/modeller/data-interface.js.map +1 -0
  14. package/dist-client/modeller/editor.d.ts +3 -0
  15. package/dist-client/modeller/editor.js +18 -0
  16. package/dist-client/modeller/editor.js.map +1 -1
  17. package/dist-client/modeller/figure-clips.d.ts +10 -0
  18. package/dist-client/modeller/figure-clips.js +125 -10
  19. package/dist-client/modeller/figure-clips.js.map +1 -1
  20. package/dist-client/modeller/figure-data-panel.d.ts +39 -0
  21. package/dist-client/modeller/figure-data-panel.js +317 -0
  22. package/dist-client/modeller/figure-data-panel.js.map +1 -0
  23. package/dist-client/modeller/figure-preview.d.ts +7 -0
  24. package/dist-client/modeller/figure-preview.js +49 -4
  25. package/dist-client/modeller/figure-preview.js.map +1 -1
  26. package/dist-client/modeller/refusals.d.ts +5 -0
  27. package/dist-client/modeller/refusals.js +58 -0
  28. package/dist-client/modeller/refusals.js.map +1 -1
  29. package/dist-client/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +6 -6
  31. package/test/data-motion.test.ts +137 -0
  32. package/translations/en.json +86 -2
  33. package/translations/es.json +86 -2
  34. package/translations/ja.json +86 -2
  35. package/translations/ko.json +86 -2
  36. package/translations/ms.json +86 -2
  37. package/translations/zh.json +86 -2
@@ -7,6 +7,9 @@ declare const FigureClips_base: (new (...args: any[]) => LitElement) & typeof Li
7
7
  * control. Scrubbing a clip shows its pose at that moment (one batch of input changes, not saved as the default); a key
8
8
  * is added at the scrubbed moment with the control's value there, so a pose is made with the controls' sliders and
9
9
  * then kept as a key. Every change is one part edit.
10
+ *
11
+ * Clips of one motion group play one at a time on the same controls -- idle, walk, run (ADR-0051 §3.2): only clips of
12
+ * one group may play the same control, so a new clip is put in a group as it is made when it is to take one.
10
13
  */
11
14
  export declare class FigureClips extends FigureClips_base {
12
15
  private readonly help;
@@ -18,10 +21,17 @@ export declare class FigureClips extends FigureClips_base {
18
21
  /** Where each clip is scrubbed to, in seconds. */
19
22
  private at;
20
23
  private adding;
24
+ private grouping;
21
25
  render(): import("lit-html").TemplateResult<1> | typeof nothing;
22
26
  private renderClip;
23
27
  private renderTrack;
24
28
  private renderAdd;
29
+ /**
30
+ * The motion groups: a name, the clip that plays until another is chosen, how long a change of clip blends, and the
31
+ * clips each holds. A group is made from a clip in none; a clip in none joins one here. Every change is one edit.
32
+ */
33
+ private renderGroups;
34
+ private renderGroup;
25
35
  /** Show the clip's pose at a moment: one batch of input changes, not saved as the default. */
26
36
  /** Show the clip's pose at a moment. While the bar is dragged the pose is live (drawn, not recorded); the release
27
37
  * applies it as one batch of input changes. */
@@ -3,7 +3,7 @@ import '@material/web/icon/icon.js';
3
3
  import { css, html, LitElement, nothing } from 'lit';
4
4
  import { customElement, property, state } from 'lit/decorators.js';
5
5
  import { i18next, localize } from '@operato/i18n';
6
- import { clipsOf, poseAt, trackEdit, undrivenOf, withKey, withoutKey } from './clips.js';
6
+ import { clipGroupsOf, clipsOf, freeGroupIdOf, groupEdit, poseAt, trackEdit, undrivenOf, withKey, withoutKey } from './clips.js';
7
7
  import { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js';
8
8
  import { HelpToggles } from './help.js';
9
9
  /**
@@ -11,6 +11,9 @@ import { HelpToggles } from './help.js';
11
11
  * control. Scrubbing a clip shows its pose at that moment (one batch of input changes, not saved as the default); a key
12
12
  * is added at the scrubbed moment with the control's value there, so a pose is made with the controls' sliders and
13
13
  * then kept as a key. Every change is one part edit.
14
+ *
15
+ * Clips of one motion group play one at a time on the same controls -- idle, walk, run (ADR-0051 §3.2): only clips of
16
+ * one group may play the same control, so a new clip is put in a group as it is made when it is to take one.
14
17
  */
15
18
  let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
16
19
  constructor() {
@@ -21,7 +24,8 @@ let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
21
24
  this.locked = false;
22
25
  /** Where each clip is scrubbed to, in seconds. */
23
26
  this.at = {};
24
- this.adding = { state: '', clip: '', name: '', duration: 2 };
27
+ this.adding = { state: '', clip: '', name: '', duration: 2, group: '' };
28
+ this.grouping = { clip: '', label: '' };
25
29
  }
26
30
  static { this.styles = [SidePanelStyles, css `
27
31
  :host {
@@ -105,7 +109,7 @@ let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
105
109
  ${this.help.hint('figure.text.clips-explained')}
106
110
  ${clips.map(clip => this.renderClip(clip))}
107
111
  </section>
108
- ${this.renderAdd(asset, clips)}
112
+ ${this.renderAdd(asset, clips)} ${this.renderGroups(asset)}
109
113
  `;
110
114
  }
111
115
  renderClip(clip) {
@@ -148,7 +152,7 @@ let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
148
152
  <option value="wrap" ?selected=${track.loop === 'wrap'}>${i18next.t('figure.label.clip-loop-wrap')}</option>
149
153
  <option value="hold" ?selected=${track.loop === 'hold'}>${i18next.t('figure.label.clip-loop-hold')}</option>
150
154
  </select>
151
- <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-track')} @click=${() => this.edit({ kind: 'remove-driver', state: track.state })}>
155
+ <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-track')} @click=${() => this.edit({ kind: 'remove-driver', state: track.state, clip: clip.name })}>
152
156
  <md-icon>close</md-icon>
153
157
  </button>
154
158
  </div>
@@ -175,20 +179,23 @@ let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
175
179
  </div>`;
176
180
  }
177
181
  renderAdd(asset, clips) {
178
- const free = undrivenOf(asset);
179
- if (!free.length)
180
- return clips.length ? nothing : html `<p hint>${i18next.t('figure.text.no-controls')}</p>`;
181
182
  const a = this.adding;
182
- const stateId = free.includes(a.state) ? a.state : free[0];
183
183
  const joining = clips.find(c => c.name === a.clip);
184
+ const { groups } = clipGroupsOf(asset);
185
+ const group = joining ? undefined : groups.find(g => g.id === a.group);
186
+ /* The controls this clip may take: not played by it yet, and by no clip outside its group. */
187
+ const free = undrivenOf(asset, joining?.name ?? '', group?.id);
188
+ if (!free.length && !clips.length && !groups.length)
189
+ return html `<p hint>${i18next.t('figure.text.no-controls')}</p>`;
190
+ const stateId = free.includes(a.state) ? a.state : free[0];
184
191
  const name = joining ? joining.name : a.name.trim();
185
192
  const input = asset.document.model.inputs.find(i => i.id === stateId);
186
193
  const duration = joining ? joining.duration : a.duration;
187
- const ready = !!name && duration > 0;
194
+ const ready = !!name && duration > 0 && !!input;
188
195
  return html `<section><h3>${i18next.t('figure.label.add-track')}</h3>
189
196
  <div form>
190
197
  <span>${i18next.t('figure.label.controls')}</span>
191
- <select @change=${(e) => (this.adding = { ...a, state: e.target.value })}>
198
+ <select ?disabled=${!free.length} @change=${(e) => (this.adding = { ...a, state: e.target.value })}>
192
199
  ${free.map(id => html `<option value=${id} ?selected=${id === stateId}>${asset.stateInputs?.[id]?.label ?? id}</option>`)}
193
200
  </select>
194
201
  <span>${i18next.t('figure.label.clip')}</span>
@@ -196,6 +203,13 @@ let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
196
203
  <option value="" ?selected=${!joining}>${i18next.t('figure.label.new-clip')}</option>
197
204
  ${clips.map(c => html `<option value=${c.name} ?selected=${c === joining}>${c.name}</option>`)}
198
205
  </select>
206
+ ${joining || !groups.length
207
+ ? nothing
208
+ : html `<span>${i18next.t('figure.label.motion-group')}</span>
209
+ <select @change=${(e) => (this.adding = { ...a, group: e.target.value })}>
210
+ <option value="" ?selected=${!group}>${i18next.t('figure.label.no-motion-group')}</option>
211
+ ${groups.map(g => html `<option value=${g.id} ?selected=${g === group}>${g.label}</option>`)}
212
+ </select>`}
199
213
  ${joining
200
214
  ? nothing
201
215
  : html `<span>${i18next.t('figure.label.clip-name')}</span>
@@ -211,6 +225,7 @@ let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
211
225
  state: stateId,
212
226
  clip: name,
213
227
  ...(joining ? {} : { duration }),
228
+ ...(group ? { group: group.id } : {}),
214
229
  keys: [
215
230
  { seconds: 0, value: input.min },
216
231
  { seconds: duration, value: input.max }
@@ -220,6 +235,102 @@ let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
220
235
  <md-icon>add</md-icon>${i18next.t('figure.button.add-track')}
221
236
  </button></section>`;
222
237
  }
238
+ /**
239
+ * The motion groups: a name, the clip that plays until another is chosen, how long a change of clip blends, and the
240
+ * clips each holds. A group is made from a clip in none; a clip in none joins one here. Every change is one edit.
241
+ */
242
+ renderGroups(asset) {
243
+ const { groups, free } = clipGroupsOf(asset);
244
+ if (!groups.length && !free.length)
245
+ return nothing;
246
+ const g = this.grouping;
247
+ const first = free.includes(g.clip) ? g.clip : free[0];
248
+ return html `<section>
249
+ <h3>${i18next.t('figure.label.motion-groups')}${this.help.button('figure.text.motion-groups-explained')}</h3>
250
+ ${this.help.hint('figure.text.motion-groups-explained')}
251
+ ${groups.map(group => this.renderGroup(group, free))}
252
+ ${free.length
253
+ ? html `<div form>
254
+ <span>${i18next.t('figure.label.motion-group-name')}</span>
255
+ <input .value=${g.label} placeholder=${i18next.t('figure.label.motion-group')} @input=${(e) => (this.grouping = { ...g, label: e.target.value })} />
256
+ <span>${i18next.t('figure.label.clip')}</span>
257
+ <select @change=${(e) => (this.grouping = { ...g, clip: e.target.value })}>
258
+ ${free.map(c => html `<option value=${c} ?selected=${c === first}>${c}</option>`)}
259
+ </select>
260
+ </div>
261
+ <button
262
+ act
263
+ ?disabled=${this.locked || !first || !g.label.trim()}
264
+ @click=${() => {
265
+ this.edit({ kind: 'set-motion-group', group: { id: freeGroupIdOf(asset), label: g.label.trim(), clips: [first], defaultClip: first, transitionSeconds: 0.2 } });
266
+ this.grouping = { clip: '', label: '' };
267
+ }}
268
+ >
269
+ <md-icon>add</md-icon>${i18next.t('figure.button.add-motion-group')}
270
+ </button>`
271
+ : nothing}
272
+ </section>`;
273
+ }
274
+ renderGroup(group, free) {
275
+ const value = (e) => e.target.value;
276
+ return html `<section clip>
277
+ <div track-head>
278
+ <strong>${group.label}</strong>
279
+ <span></span>
280
+ <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-motion-group')} @click=${() => this.edit({ kind: 'remove-motion-group', id: group.id })}>
281
+ <md-icon>close</md-icon>
282
+ </button>
283
+ </div>
284
+ <div form>
285
+ <span>${i18next.t('figure.label.motion-group-name')}</span>
286
+ <input .value=${group.label} ?disabled=${this.locked} @change=${(e) => value(e).trim() && this.edit(groupEdit(group, { label: value(e).trim() }))} />
287
+ <span>${i18next.t('figure.label.motion-group-default')}</span>
288
+ <select ?disabled=${this.locked} @change=${(e) => this.edit(groupEdit(group, { defaultClip: value(e) }))}>
289
+ ${group.clips.map(c => html `<option value=${c} ?selected=${c === group.defaultClip}>${c}</option>`)}
290
+ </select>
291
+ <span>${i18next.t('figure.label.motion-group-transition')}</span>
292
+ <input
293
+ type="number"
294
+ min="0"
295
+ step="any"
296
+ .value=${String(group.transitionSeconds)}
297
+ ?disabled=${this.locked}
298
+ @change=${(e) => this.edit(groupEdit(group, { transitionSeconds: e.target.valueAsNumber }))}
299
+ />
300
+ </div>
301
+ <div keys>
302
+ ${group.clips.map(c => html `<span key
303
+ >${c}
304
+ <button
305
+ ?disabled=${this.locked || group.clips.length < 2}
306
+ title=${i18next.t('figure.button.remove-clip-from-group')}
307
+ aria-label=${i18next.t('figure.button.remove-clip-from-group')}
308
+ @click=${() => {
309
+ const clips = group.clips.filter(x => x !== c);
310
+ this.edit(groupEdit(group, { clips, defaultClip: group.defaultClip === c ? clips[0] : group.defaultClip }));
311
+ }}
312
+ >
313
+ <md-icon>close</md-icon>
314
+ </button></span
315
+ >`)}
316
+ </div>
317
+ ${free.length
318
+ ? html `<select
319
+ aria-label=${i18next.t('figure.button.add-clip-to-group')}
320
+ ?disabled=${this.locked}
321
+ @change=${(e) => {
322
+ const c = value(e);
323
+ if (c)
324
+ this.edit(groupEdit(group, { clips: [...group.clips, c] }));
325
+ e.target.value = '';
326
+ }}
327
+ >
328
+ <option value="" selected>${i18next.t('figure.button.add-clip-to-group')}</option>
329
+ ${free.map(c => html `<option value=${c}>${c}</option>`)}
330
+ </select>`
331
+ : nothing}
332
+ </section>`;
333
+ }
223
334
  /** Show the clip's pose at a moment: one batch of input changes, not saved as the default. */
224
335
  /** Show the clip's pose at a moment. While the bar is dragged the pose is live (drawn, not recorded); the release
225
336
  * applies it as one batch of input changes. */
@@ -261,6 +372,10 @@ __decorate([
261
372
  state(),
262
373
  __metadata("design:type", Object)
263
374
  ], FigureClips.prototype, "adding", void 0);
375
+ __decorate([
376
+ state(),
377
+ __metadata("design:type", Object)
378
+ ], FigureClips.prototype, "grouping", void 0);
264
379
  FigureClips = __decorate([
265
380
  customElement('figure-clips')
266
381
  ], FigureClips);
@@ -1 +1 @@
1
- {"version":3,"file":"figure-clips.js","sourceRoot":"","sources":["../../client/modeller/figure-clips.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 type { FigureAsset } from '@hatiolab/figure-model'\n\nimport { clipsOf, poseAt, trackEdit, undrivenOf, withKey, withoutKey, type Clip, type ClipTrack } from './clips.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { HelpToggles } from './help.js'\n\n/**\n * 클립 — controls played by keys over time. A clip is one clock; each track plays one\n * control. Scrubbing a clip shows its pose at that moment (one batch of input changes, not saved as the default); a key\n * is added at the scrubbed moment with the control's value there, so a pose is made with the controls' sliders and\n * then kept as a key. Every change is one part edit.\n */\n@customElement('figure-clips')\nexport class FigureClips extends localize(i18next)(LitElement) {\n private readonly help = new HelpToggles(this)\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n section[clip] {\n display: grid;\n gap: 8px;\n padding: 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n }\n div[row] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 72px;\n gap: 6px;\n align-items: center;\n }\n div[track] {\n display: grid;\n gap: 4px;\n padding-top: 6px;\n border-top: 1px dashed var(--md-sys-color-outline-variant);\n }\n /*\n The track's name on its own line, then how it repeats and the button that removes it. A flex row let the shared\n full-width select and button squeeze the name to one letter a line (user, 2026-09-24).\n */\n div[track-head] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 30px;\n gap: 4px 6px;\n align-items: center;\n }\n div[track-head] strong {\n grid-column: 1 / -1;\n min-width: 0;\n overflow-wrap: anywhere;\n }\n div[track-head] button[act] {\n width: 30px;\n min-height: var(--figure-control-height);\n margin: 0;\n padding: 0;\n }\n div[keys] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n span[key] {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 0 2px 0 8px;\n border-radius: 999em;\n background: var(--md-sys-color-surface-container);\n font-variant-numeric: tabular-nums;\n }\n span[key] button {\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n background: none;\n }\n md-icon {\n font-size: 16px;\n width: 16px;\n height: 16px;\n }\n `]\n\n @property({ attribute: false }) asset?: FigureAsset\n /** The values the editor holds now, by input id -- what a new key takes. */\n @property({ attribute: false }) values: Record<string, number> = {}\n @property({ type: Boolean }) locked = false\n\n /** Where each clip is scrubbed to, in seconds. */\n @state() private at: Record<string, number> = {}\n @state() private adding = { state: '', clip: '', name: '', duration: 2 }\n\n render() {\n const asset = this.asset\n if (!asset) return nothing\n const clips = clipsOf(asset)\n return html`\n <section>\n <h3>${i18next.t('figure.label.clips')}${this.help.button('figure.text.clips-explained')}</h3>\n ${this.help.hint('figure.text.clips-explained')}\n ${clips.map(clip => this.renderClip(clip))}\n </section>\n ${this.renderAdd(asset, clips)}\n `\n }\n\n private renderClip(clip: Clip) {\n const at = Math.min(this.at[clip.name] ?? 0, clip.duration)\n const number = (e: Event) => (e.target as HTMLInputElement).valueAsNumber\n return html`<section clip>\n <div form>\n <span>${i18next.t('figure.label.clip-name')}</span>\n <input .value=${clip.name} ?disabled=${this.locked} @change=${(e: Event) => this.edit({ kind: 'set-clip', clip: clip.name, name: (e.target as HTMLInputElement).value })} />\n <span>${i18next.t('figure.label.clip-duration')}</span>\n <input type=\"number\" min=\"0.1\" step=\"any\" .value=${String(clip.duration)} ?disabled=${this.locked} @change=${(e: Event) => this.edit({ kind: 'set-clip', clip: clip.name, duration: number(e) })} />\n </div>\n <div row>\n <input\n type=\"range\"\n min=\"0\"\n max=${clip.duration}\n step=${clip.duration / 100}\n .value=${String(at)}\n ?disabled=${this.locked}\n aria-label=${i18next.t('figure.label.clip-time')}\n @input=${(e: Event) => this.scrub(clip, number(e), true)}\n @change=${(e: Event) => this.scrub(clip, number(e))}\n />\n <small>${at.toFixed(2)} s</small>\n </div>\n ${clip.tracks.map(track => this.renderTrack(clip, track, at))}\n </section>`\n }\n\n private renderTrack(clip: Clip, track: ClipTrack, at: number) {\n const current = this.values[track.state]\n return html`<div track>\n <div track-head>\n <strong>${track.label} <small>(${track.unit})</small></strong>\n <select\n aria-label=${i18next.t('figure.label.clip-loop')}\n ?disabled=${this.locked}\n @change=${(e: Event) => this.edit(trackEdit(clip, track, { loop: (e.target as HTMLSelectElement).value as 'wrap' | 'hold' }))}\n >\n <option value=\"wrap\" ?selected=${track.loop === 'wrap'}>${i18next.t('figure.label.clip-loop-wrap')}</option>\n <option value=\"hold\" ?selected=${track.loop === 'hold'}>${i18next.t('figure.label.clip-loop-hold')}</option>\n </select>\n <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-track')} @click=${() => this.edit({ kind: 'remove-driver', state: track.state })}>\n <md-icon>close</md-icon>\n </button>\n </div>\n <div keys>\n ${track.keys.map(\n (key, i) => html`<span key\n >${key.seconds}s → ${key.value}\n <button\n ?disabled=${this.locked || !withoutKey(track.keys, i)}\n title=${i18next.t('figure.button.remove-key')}\n aria-label=${i18next.t('figure.button.remove-key')}\n @click=${() => this.edit(trackEdit(clip, track, { keys: withoutKey(track.keys, i)! }))}\n >\n <md-icon>close</md-icon>\n </button></span\n >`\n )}\n </div>\n <button\n act\n ?disabled=${this.locked || current === undefined}\n @click=${() => this.edit(trackEdit(clip, track, { keys: withKey(track.keys, { seconds: Math.round(at * 1000) / 1000, value: current! }) }))}\n >\n <md-icon>add</md-icon>${i18next.t('figure.button.add-key-here', { seconds: at.toFixed(2), value: current ?? '' })}\n </button>\n </div>`\n }\n\n private renderAdd(asset: FigureAsset, clips: Clip[]) {\n const free = undrivenOf(asset)\n if (!free.length) return clips.length ? nothing : html`<p hint>${i18next.t('figure.text.no-controls')}</p>`\n const a = this.adding\n const stateId = free.includes(a.state) ? a.state : free[0]!\n const joining = clips.find(c => c.name === a.clip)\n const name = joining ? joining.name : a.name.trim()\n const input = asset.document.model.inputs.find(i => i.id === stateId)!\n const duration = joining ? joining.duration : a.duration\n const ready = !!name && duration > 0\n return html`<section><h3>${i18next.t('figure.label.add-track')}</h3>\n <div form>\n <span>${i18next.t('figure.label.controls')}</span>\n <select @change=${(e: Event) => (this.adding = { ...a, state: (e.target as HTMLSelectElement).value })}>\n ${free.map(id => html`<option value=${id} ?selected=${id === stateId}>${asset.stateInputs?.[id]?.label ?? id}</option>`)}\n </select>\n <span>${i18next.t('figure.label.clip')}</span>\n <select @change=${(e: Event) => (this.adding = { ...a, clip: (e.target as HTMLSelectElement).value })}>\n <option value=\"\" ?selected=${!joining}>${i18next.t('figure.label.new-clip')}</option>\n ${clips.map(c => html`<option value=${c.name} ?selected=${c === joining}>${c.name}</option>`)}\n </select>\n ${joining\n ? nothing\n : html`<span>${i18next.t('figure.label.clip-name')}</span>\n <input .value=${a.name} placeholder=\"cycle\" @input=${(e: Event) => (this.adding = { ...a, name: (e.target as HTMLInputElement).value })} />\n <span>${i18next.t('figure.label.clip-duration')}</span>\n <input type=\"number\" min=\"0.1\" step=\"any\" .value=${String(a.duration)} @change=${(e: Event) => (this.adding = { ...a, duration: (e.target as HTMLInputElement).valueAsNumber })} />`}\n </div>\n <button\n act\n ?disabled=${this.locked || !ready}\n @click=${() =>\n this.edit({\n kind: 'set-driver',\n state: stateId,\n clip: name,\n ...(joining ? {} : { duration }),\n keys: [\n { seconds: 0, value: input.min },\n { seconds: duration, value: input.max }\n ]\n })}\n >\n <md-icon>add</md-icon>${i18next.t('figure.button.add-track')}\n </button></section>`\n }\n\n /** Show the clip's pose at a moment: one batch of input changes, not saved as the default. */\n /** Show the clip's pose at a moment. While the bar is dragged the pose is live (drawn, not recorded); the release\n * applies it as one batch of input changes. */\n private scrub(clip: Clip, seconds: number, live = false) {\n if (!this.asset || !Number.isFinite(seconds)) return\n this.at = { ...this.at, [clip.name]: seconds }\n const changes = poseAt(this.asset, clip.name, seconds)\n if (!changes.length) return\n if (live) {\n const values = Object.fromEntries(changes.map(c => [c.input, c.value]))\n this.dispatchEvent(new CustomEvent('states-live', { detail: { values }, bubbles: true, composed: true }))\n return\n }\n this.dispatchEvent(new CustomEvent('states', { detail: { changes }, bubbles: true, composed: true }))\n }\n\n private edit(edit: unknown) {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }))\n }\n}\n"]}
1
+ {"version":3,"file":"figure-clips.js","sourceRoot":"","sources":["../../client/modeller/figure-clips.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 type { FigureAsset } from '@hatiolab/figure-model'\n\nimport { clipGroupsOf, clipsOf, freeGroupIdOf, groupEdit, poseAt, trackEdit, undrivenOf, withKey, withoutKey, type Clip, type ClipGroup, type ClipTrack } from './clips.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { HelpToggles } from './help.js'\n\n/**\n * 클립 — controls played by keys over time. A clip is one clock; each track plays one\n * control. Scrubbing a clip shows its pose at that moment (one batch of input changes, not saved as the default); a key\n * is added at the scrubbed moment with the control's value there, so a pose is made with the controls' sliders and\n * then kept as a key. Every change is one part edit.\n *\n * Clips of one motion group play one at a time on the same controls -- idle, walk, run (ADR-0051 §3.2): only clips of\n * one group may play the same control, so a new clip is put in a group as it is made when it is to take one.\n */\n@customElement('figure-clips')\nexport class FigureClips extends localize(i18next)(LitElement) {\n private readonly help = new HelpToggles(this)\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n section[clip] {\n display: grid;\n gap: 8px;\n padding: 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n }\n div[row] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 72px;\n gap: 6px;\n align-items: center;\n }\n div[track] {\n display: grid;\n gap: 4px;\n padding-top: 6px;\n border-top: 1px dashed var(--md-sys-color-outline-variant);\n }\n /*\n The track's name on its own line, then how it repeats and the button that removes it. A flex row let the shared\n full-width select and button squeeze the name to one letter a line (user, 2026-09-24).\n */\n div[track-head] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 30px;\n gap: 4px 6px;\n align-items: center;\n }\n div[track-head] strong {\n grid-column: 1 / -1;\n min-width: 0;\n overflow-wrap: anywhere;\n }\n div[track-head] button[act] {\n width: 30px;\n min-height: var(--figure-control-height);\n margin: 0;\n padding: 0;\n }\n div[keys] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n span[key] {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 0 2px 0 8px;\n border-radius: 999em;\n background: var(--md-sys-color-surface-container);\n font-variant-numeric: tabular-nums;\n }\n span[key] button {\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n background: none;\n }\n md-icon {\n font-size: 16px;\n width: 16px;\n height: 16px;\n }\n `]\n\n @property({ attribute: false }) asset?: FigureAsset\n /** The values the editor holds now, by input id -- what a new key takes. */\n @property({ attribute: false }) values: Record<string, number> = {}\n @property({ type: Boolean }) locked = false\n\n /** Where each clip is scrubbed to, in seconds. */\n @state() private at: Record<string, number> = {}\n @state() private adding = { state: '', clip: '', name: '', duration: 2, group: '' }\n @state() private grouping = { clip: '', label: '' }\n\n render() {\n const asset = this.asset\n if (!asset) return nothing\n const clips = clipsOf(asset)\n return html`\n <section>\n <h3>${i18next.t('figure.label.clips')}${this.help.button('figure.text.clips-explained')}</h3>\n ${this.help.hint('figure.text.clips-explained')}\n ${clips.map(clip => this.renderClip(clip))}\n </section>\n ${this.renderAdd(asset, clips)} ${this.renderGroups(asset)}\n `\n }\n\n private renderClip(clip: Clip) {\n const at = Math.min(this.at[clip.name] ?? 0, clip.duration)\n const number = (e: Event) => (e.target as HTMLInputElement).valueAsNumber\n return html`<section clip>\n <div form>\n <span>${i18next.t('figure.label.clip-name')}</span>\n <input .value=${clip.name} ?disabled=${this.locked} @change=${(e: Event) => this.edit({ kind: 'set-clip', clip: clip.name, name: (e.target as HTMLInputElement).value })} />\n <span>${i18next.t('figure.label.clip-duration')}</span>\n <input type=\"number\" min=\"0.1\" step=\"any\" .value=${String(clip.duration)} ?disabled=${this.locked} @change=${(e: Event) => this.edit({ kind: 'set-clip', clip: clip.name, duration: number(e) })} />\n </div>\n <div row>\n <input\n type=\"range\"\n min=\"0\"\n max=${clip.duration}\n step=${clip.duration / 100}\n .value=${String(at)}\n ?disabled=${this.locked}\n aria-label=${i18next.t('figure.label.clip-time')}\n @input=${(e: Event) => this.scrub(clip, number(e), true)}\n @change=${(e: Event) => this.scrub(clip, number(e))}\n />\n <small>${at.toFixed(2)} s</small>\n </div>\n ${clip.tracks.map(track => this.renderTrack(clip, track, at))}\n </section>`\n }\n\n private renderTrack(clip: Clip, track: ClipTrack, at: number) {\n const current = this.values[track.state]\n return html`<div track>\n <div track-head>\n <strong>${track.label} <small>(${track.unit})</small></strong>\n <select\n aria-label=${i18next.t('figure.label.clip-loop')}\n ?disabled=${this.locked}\n @change=${(e: Event) => this.edit(trackEdit(clip, track, { loop: (e.target as HTMLSelectElement).value as 'wrap' | 'hold' }))}\n >\n <option value=\"wrap\" ?selected=${track.loop === 'wrap'}>${i18next.t('figure.label.clip-loop-wrap')}</option>\n <option value=\"hold\" ?selected=${track.loop === 'hold'}>${i18next.t('figure.label.clip-loop-hold')}</option>\n </select>\n <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-track')} @click=${() => this.edit({ kind: 'remove-driver', state: track.state, clip: clip.name })}>\n <md-icon>close</md-icon>\n </button>\n </div>\n <div keys>\n ${track.keys.map(\n (key, i) => html`<span key\n >${key.seconds}s → ${key.value}\n <button\n ?disabled=${this.locked || !withoutKey(track.keys, i)}\n title=${i18next.t('figure.button.remove-key')}\n aria-label=${i18next.t('figure.button.remove-key')}\n @click=${() => this.edit(trackEdit(clip, track, { keys: withoutKey(track.keys, i)! }))}\n >\n <md-icon>close</md-icon>\n </button></span\n >`\n )}\n </div>\n <button\n act\n ?disabled=${this.locked || current === undefined}\n @click=${() => this.edit(trackEdit(clip, track, { keys: withKey(track.keys, { seconds: Math.round(at * 1000) / 1000, value: current! }) }))}\n >\n <md-icon>add</md-icon>${i18next.t('figure.button.add-key-here', { seconds: at.toFixed(2), value: current ?? '' })}\n </button>\n </div>`\n }\n\n private renderAdd(asset: FigureAsset, clips: Clip[]) {\n const a = this.adding\n const joining = clips.find(c => c.name === a.clip)\n const { groups } = clipGroupsOf(asset)\n const group = joining ? undefined : groups.find(g => g.id === a.group)\n /* The controls this clip may take: not played by it yet, and by no clip outside its group. */\n const free = undrivenOf(asset, joining?.name ?? '', group?.id)\n if (!free.length && !clips.length && !groups.length) return html`<p hint>${i18next.t('figure.text.no-controls')}</p>`\n const stateId = free.includes(a.state) ? a.state : free[0]\n const name = joining ? joining.name : a.name.trim()\n const input = asset.document.model.inputs.find(i => i.id === stateId)\n const duration = joining ? joining.duration : a.duration\n const ready = !!name && duration > 0 && !!input\n return html`<section><h3>${i18next.t('figure.label.add-track')}</h3>\n <div form>\n <span>${i18next.t('figure.label.controls')}</span>\n <select ?disabled=${!free.length} @change=${(e: Event) => (this.adding = { ...a, state: (e.target as HTMLSelectElement).value })}>\n ${free.map(id => html`<option value=${id} ?selected=${id === stateId}>${asset.stateInputs?.[id]?.label ?? id}</option>`)}\n </select>\n <span>${i18next.t('figure.label.clip')}</span>\n <select @change=${(e: Event) => (this.adding = { ...a, clip: (e.target as HTMLSelectElement).value })}>\n <option value=\"\" ?selected=${!joining}>${i18next.t('figure.label.new-clip')}</option>\n ${clips.map(c => html`<option value=${c.name} ?selected=${c === joining}>${c.name}</option>`)}\n </select>\n ${joining || !groups.length\n ? nothing\n : html`<span>${i18next.t('figure.label.motion-group')}</span>\n <select @change=${(e: Event) => (this.adding = { ...a, group: (e.target as HTMLSelectElement).value })}>\n <option value=\"\" ?selected=${!group}>${i18next.t('figure.label.no-motion-group')}</option>\n ${groups.map(g => html`<option value=${g.id} ?selected=${g === group}>${g.label}</option>`)}\n </select>`}\n ${joining\n ? nothing\n : html`<span>${i18next.t('figure.label.clip-name')}</span>\n <input .value=${a.name} placeholder=\"cycle\" @input=${(e: Event) => (this.adding = { ...a, name: (e.target as HTMLInputElement).value })} />\n <span>${i18next.t('figure.label.clip-duration')}</span>\n <input type=\"number\" min=\"0.1\" step=\"any\" .value=${String(a.duration)} @change=${(e: Event) => (this.adding = { ...a, duration: (e.target as HTMLInputElement).valueAsNumber })} />`}\n </div>\n <button\n act\n ?disabled=${this.locked || !ready}\n @click=${() =>\n this.edit({\n kind: 'set-driver',\n state: stateId,\n clip: name,\n ...(joining ? {} : { duration }),\n ...(group ? { group: group.id } : {}),\n keys: [\n { seconds: 0, value: input!.min },\n { seconds: duration, value: input!.max }\n ]\n })}\n >\n <md-icon>add</md-icon>${i18next.t('figure.button.add-track')}\n </button></section>`\n }\n\n /**\n * The motion groups: a name, the clip that plays until another is chosen, how long a change of clip blends, and the\n * clips each holds. A group is made from a clip in none; a clip in none joins one here. Every change is one edit.\n */\n private renderGroups(asset: FigureAsset) {\n const { groups, free } = clipGroupsOf(asset)\n if (!groups.length && !free.length) return nothing\n const g = this.grouping\n const first = free.includes(g.clip) ? g.clip : free[0]\n return html`<section>\n <h3>${i18next.t('figure.label.motion-groups')}${this.help.button('figure.text.motion-groups-explained')}</h3>\n ${this.help.hint('figure.text.motion-groups-explained')}\n ${groups.map(group => this.renderGroup(group, free))}\n ${free.length\n ? html`<div form>\n <span>${i18next.t('figure.label.motion-group-name')}</span>\n <input .value=${g.label} placeholder=${i18next.t('figure.label.motion-group')} @input=${(e: Event) => (this.grouping = { ...g, label: (e.target as HTMLInputElement).value })} />\n <span>${i18next.t('figure.label.clip')}</span>\n <select @change=${(e: Event) => (this.grouping = { ...g, clip: (e.target as HTMLSelectElement).value })}>\n ${free.map(c => html`<option value=${c} ?selected=${c === first}>${c}</option>`)}\n </select>\n </div>\n <button\n act\n ?disabled=${this.locked || !first || !g.label.trim()}\n @click=${() => {\n this.edit({ kind: 'set-motion-group', group: { id: freeGroupIdOf(asset), label: g.label.trim(), clips: [first!], defaultClip: first!, transitionSeconds: 0.2 } })\n this.grouping = { clip: '', label: '' }\n }}\n >\n <md-icon>add</md-icon>${i18next.t('figure.button.add-motion-group')}\n </button>`\n : nothing}\n </section>`\n }\n\n private renderGroup(group: ClipGroup, free: string[]) {\n const value = (e: Event) => (e.target as HTMLInputElement | HTMLSelectElement).value\n return html`<section clip>\n <div track-head>\n <strong>${group.label}</strong>\n <span></span>\n <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-motion-group')} @click=${() => this.edit({ kind: 'remove-motion-group', id: group.id })}>\n <md-icon>close</md-icon>\n </button>\n </div>\n <div form>\n <span>${i18next.t('figure.label.motion-group-name')}</span>\n <input .value=${group.label} ?disabled=${this.locked} @change=${(e: Event) => value(e).trim() && this.edit(groupEdit(group, { label: value(e).trim() }))} />\n <span>${i18next.t('figure.label.motion-group-default')}</span>\n <select ?disabled=${this.locked} @change=${(e: Event) => this.edit(groupEdit(group, { defaultClip: value(e) }))}>\n ${group.clips.map(c => html`<option value=${c} ?selected=${c === group.defaultClip}>${c}</option>`)}\n </select>\n <span>${i18next.t('figure.label.motion-group-transition')}</span>\n <input\n type=\"number\"\n min=\"0\"\n step=\"any\"\n .value=${String(group.transitionSeconds)}\n ?disabled=${this.locked}\n @change=${(e: Event) => this.edit(groupEdit(group, { transitionSeconds: (e.target as HTMLInputElement).valueAsNumber }))}\n />\n </div>\n <div keys>\n ${group.clips.map(\n c => html`<span key\n >${c}\n <button\n ?disabled=${this.locked || group.clips.length < 2}\n title=${i18next.t('figure.button.remove-clip-from-group')}\n aria-label=${i18next.t('figure.button.remove-clip-from-group')}\n @click=${() => {\n const clips = group.clips.filter(x => x !== c)\n this.edit(groupEdit(group, { clips, defaultClip: group.defaultClip === c ? clips[0]! : group.defaultClip }))\n }}\n >\n <md-icon>close</md-icon>\n </button></span\n >`\n )}\n </div>\n ${free.length\n ? html`<select\n aria-label=${i18next.t('figure.button.add-clip-to-group')}\n ?disabled=${this.locked}\n @change=${(e: Event) => {\n const c = value(e)\n if (c) this.edit(groupEdit(group, { clips: [...group.clips, c] }))\n ;(e.target as HTMLSelectElement).value = ''\n }}\n >\n <option value=\"\" selected>${i18next.t('figure.button.add-clip-to-group')}</option>\n ${free.map(c => html`<option value=${c}>${c}</option>`)}\n </select>`\n : nothing}\n </section>`\n }\n\n /** Show the clip's pose at a moment: one batch of input changes, not saved as the default. */\n /** Show the clip's pose at a moment. While the bar is dragged the pose is live (drawn, not recorded); the release\n * applies it as one batch of input changes. */\n private scrub(clip: Clip, seconds: number, live = false) {\n if (!this.asset || !Number.isFinite(seconds)) return\n this.at = { ...this.at, [clip.name]: seconds }\n const changes = poseAt(this.asset, clip.name, seconds)\n if (!changes.length) return\n if (live) {\n const values = Object.fromEntries(changes.map(c => [c.input, c.value]))\n this.dispatchEvent(new CustomEvent('states-live', { detail: { values }, bubbles: true, composed: true }))\n return\n }\n this.dispatchEvent(new CustomEvent('states', { detail: { changes }, bubbles: true, composed: true }))\n }\n\n private edit(edit: unknown) {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }))\n }\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import '@material/web/icon/icon.js';
2
+ import { LitElement, nothing } from 'lit';
3
+ import type { FigureAsset } from '@hatiolab/figure-model';
4
+ declare const FigureDataPanel_base: (new (...args: any[]) => LitElement) & typeof LitElement;
5
+ /**
6
+ * 데이터 -- the figure's public data properties and what each moves inside it (ADR-0051 §2.2). A property is a value a
7
+ * consumer maps its own data onto, by its id: a number with a unit and a range, a boolean, or an enum. A connection
8
+ * makes a property move a control, a colour, or which clip of a motion group plays and how fast. Every change is one
9
+ * part edit, and whether it holds is the kernel's check.
10
+ *
11
+ * The sample is not saved: values tried here, what they would move (figure-model's evaluation), and -- in the preview --
12
+ * the figure running on them (`data-sample`).
13
+ */
14
+ export declare class FigureDataPanel extends FigureDataPanel_base {
15
+ private readonly help;
16
+ static styles: import("lit").CSSResult[];
17
+ asset?: FigureAsset;
18
+ locked: boolean;
19
+ private adding;
20
+ private connecting;
21
+ /** The sample's values as typed, by property id; a property left out reads its default. */
22
+ private sample;
23
+ private sampling;
24
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
25
+ private renderProperty;
26
+ private renderAddProperty;
27
+ private targetName;
28
+ private renderConnection;
29
+ /** A case per value of the property: what the target takes for it -- a number, a colour, or a clip of the group. */
30
+ private renderCases;
31
+ private renderAddConnection;
32
+ /** Values to try: what they move, by figure-model's evaluation; in the preview, the figure running on them. */
33
+ private renderSample;
34
+ private try;
35
+ /** The sample to the preview while it is asked for; null lets the preview's figure go back to its own values. */
36
+ private tell;
37
+ private edit;
38
+ }
39
+ export {};