@things-factory/figure-ui 10.1.74 → 10.1.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/client/figure-assistant.ts +3 -3
  2. package/client/graphql/{v3-store.ts → store.ts} +6 -6
  3. package/client/modeller/{v3-ai-proposal.ts → ai-proposal.ts} +8 -8
  4. package/client/modeller/{v3-align.ts → align.ts} +14 -14
  5. package/client/modeller/{v3-canvas.ts → canvas.ts} +13 -13
  6. package/client/modeller/{v3-clips.ts → clips.ts} +5 -5
  7. package/client/modeller/{v3-controls.ts → controls.ts} +2 -2
  8. package/client/modeller/{v3-editor.ts → editor.ts} +137 -126
  9. package/client/modeller/{figure-v3-capability.ts → figure-capability-panel.ts} +7 -7
  10. package/client/modeller/{figure-v3-clips.ts → figure-clips.ts} +10 -10
  11. package/client/modeller/{figure-v3-controls.ts → figure-controls.ts} +9 -9
  12. package/client/modeller/{v3-figure-facts.ts → figure-facts.ts} +11 -11
  13. package/client/modeller/{figure-v3-first-save.ts → figure-first-save.ts} +3 -3
  14. package/client/modeller/figure-history-panel.ts +1 -1
  15. package/client/modeller/{figure-v3-links.ts → figure-links.ts} +35 -35
  16. package/client/modeller/{figure-v3-material.ts → figure-material-panel.ts} +20 -20
  17. package/client/modeller/{figure-v3-parts.ts → figure-parts.ts} +15 -15
  18. package/client/modeller/{figure-v3-preview.ts → figure-preview.ts} +14 -14
  19. package/client/modeller/{figure-v3-properties.ts → figure-properties.ts} +25 -25
  20. package/client/modeller/{figure-v3-recipe-catalog.ts → figure-recipe-catalog.ts} +10 -10
  21. package/client/modeller/{figure-v3-verdict.ts → figure-verdict.ts} +24 -24
  22. package/client/modeller/gate-findings.ts +2 -2
  23. package/client/modeller/{v3-gizmo.ts → gizmo.ts} +11 -11
  24. package/client/modeller/{v3-joint-drag.ts → joint-drag.ts} +11 -11
  25. package/client/modeller/{v3-joint-fan.ts → joint-fan.ts} +13 -13
  26. package/client/modeller/{v3-links.ts → links.ts} +59 -59
  27. package/client/modeller/{v3-live.ts → live.ts} +9 -9
  28. package/client/modeller/{v3-maturity.ts → maturity.ts} +65 -65
  29. package/client/modeller/modeller-route.ts +1 -1
  30. package/client/modeller/{v3-occupancy.ts → occupancy.ts} +6 -6
  31. package/client/modeller/{v3-palette.ts → palette.ts} +3 -3
  32. package/client/modeller/{v3-preview-model.ts → preview-model.ts} +8 -8
  33. package/client/modeller/{v3-product-session.ts → product-session.ts} +25 -25
  34. package/client/modeller/{v3-recipe-source.ts → recipe-source.ts} +2 -2
  35. package/client/modeller/{v3-recipes.ts → recipes.ts} +9 -9
  36. package/client/modeller/{v3-refusals.ts → refusals.ts} +62 -13
  37. package/client/modeller/{v3-renderer.ts → renderer.ts} +16 -16
  38. package/client/modeller/{v3-stage-model.ts → stage-model.ts} +15 -15
  39. package/client/modeller/{v3-thumbnail.ts → thumbnail.ts} +8 -8
  40. package/client/pages/figure-list-page.ts +4 -4
  41. package/client/pages/{figure-v3-modeller-page.ts → figure-modeller-page.ts} +37 -37
  42. package/client/route.ts +1 -5
  43. package/dist-client/figure-assistant.js +3 -3
  44. package/dist-client/figure-assistant.js.map +1 -1
  45. package/dist-client/graphql/store.d.ts +3 -0
  46. package/dist-client/graphql/store.js +39 -0
  47. package/dist-client/graphql/store.js.map +1 -0
  48. package/dist-client/modeller/ai-proposal.d.ts +51 -0
  49. package/dist-client/modeller/ai-proposal.js +64 -0
  50. package/dist-client/modeller/ai-proposal.js.map +1 -0
  51. package/dist-client/modeller/align.d.ts +30 -0
  52. package/dist-client/modeller/align.js +75 -0
  53. package/dist-client/modeller/align.js.map +1 -0
  54. package/dist-client/modeller/canvas.d.ts +72 -0
  55. package/dist-client/modeller/canvas.js +379 -0
  56. package/dist-client/modeller/canvas.js.map +1 -0
  57. package/dist-client/modeller/clips.d.ts +39 -0
  58. package/dist-client/modeller/clips.js +58 -0
  59. package/dist-client/modeller/clips.js.map +1 -0
  60. package/dist-client/modeller/controls.d.ts +28 -0
  61. package/dist-client/modeller/controls.js +28 -0
  62. package/dist-client/modeller/controls.js.map +1 -0
  63. package/dist-client/modeller/editor.d.ts +203 -0
  64. package/dist-client/modeller/editor.js +1526 -0
  65. package/dist-client/modeller/editor.js.map +1 -0
  66. package/dist-client/modeller/fields.d.ts +45 -0
  67. package/dist-client/modeller/fields.js +83 -0
  68. package/dist-client/modeller/fields.js.map +1 -0
  69. package/dist-client/modeller/figure-capability-panel.d.ts +22 -0
  70. package/dist-client/modeller/figure-capability-panel.js +141 -0
  71. package/dist-client/modeller/figure-capability-panel.js.map +1 -0
  72. package/dist-client/modeller/figure-clips.d.ts +31 -0
  73. package/dist-client/modeller/figure-clips.js +268 -0
  74. package/dist-client/modeller/figure-clips.js.map +1 -0
  75. package/dist-client/modeller/figure-controls.d.ts +36 -0
  76. package/dist-client/modeller/figure-controls.js +226 -0
  77. package/dist-client/modeller/figure-controls.js.map +1 -0
  78. package/dist-client/modeller/figure-facts.d.ts +35 -0
  79. package/dist-client/modeller/figure-facts.js +55 -0
  80. package/dist-client/modeller/figure-facts.js.map +1 -0
  81. package/dist-client/modeller/figure-first-save.d.ts +20 -0
  82. package/dist-client/modeller/figure-first-save.js +118 -0
  83. package/dist-client/modeller/figure-first-save.js.map +1 -0
  84. package/dist-client/modeller/figure-history-panel.d.ts +1 -1
  85. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  86. package/dist-client/modeller/figure-links.d.ts +77 -0
  87. package/dist-client/modeller/figure-links.js +649 -0
  88. package/dist-client/modeller/figure-links.js.map +1 -0
  89. package/dist-client/modeller/figure-material-panel.d.ts +39 -0
  90. package/dist-client/modeller/figure-material-panel.js +323 -0
  91. package/dist-client/modeller/figure-material-panel.js.map +1 -0
  92. package/dist-client/modeller/figure-parts.d.ts +69 -0
  93. package/dist-client/modeller/figure-parts.js +559 -0
  94. package/dist-client/modeller/figure-parts.js.map +1 -0
  95. package/dist-client/modeller/figure-preview.d.ts +34 -0
  96. package/dist-client/modeller/figure-preview.js +346 -0
  97. package/dist-client/modeller/figure-preview.js.map +1 -0
  98. package/dist-client/modeller/figure-properties.d.ts +45 -0
  99. package/dist-client/modeller/figure-properties.js +239 -0
  100. package/dist-client/modeller/figure-properties.js.map +1 -0
  101. package/dist-client/modeller/figure-recipe-catalog.d.ts +38 -0
  102. package/dist-client/modeller/figure-recipe-catalog.js +399 -0
  103. package/dist-client/modeller/figure-recipe-catalog.js.map +1 -0
  104. package/dist-client/modeller/figure-verdict.d.ts +64 -0
  105. package/dist-client/modeller/figure-verdict.js +787 -0
  106. package/dist-client/modeller/figure-verdict.js.map +1 -0
  107. package/dist-client/modeller/first-save.d.ts +17 -0
  108. package/dist-client/modeller/first-save.js +26 -0
  109. package/dist-client/modeller/first-save.js.map +1 -0
  110. package/dist-client/modeller/gate-findings.d.ts +2 -2
  111. package/dist-client/modeller/gate-findings.js +2 -2
  112. package/dist-client/modeller/gate-findings.js.map +1 -1
  113. package/dist-client/modeller/gizmo.d.ts +72 -0
  114. package/dist-client/modeller/gizmo.js +111 -0
  115. package/dist-client/modeller/gizmo.js.map +1 -0
  116. package/dist-client/modeller/help.d.ts +18 -0
  117. package/dist-client/modeller/help.js +37 -0
  118. package/dist-client/modeller/help.js.map +1 -0
  119. package/dist-client/modeller/joint-drag.d.ts +19 -0
  120. package/dist-client/modeller/joint-drag.js +97 -0
  121. package/dist-client/modeller/joint-drag.js.map +1 -0
  122. package/dist-client/modeller/joint-fan.d.ts +9 -0
  123. package/dist-client/modeller/joint-fan.js +276 -0
  124. package/dist-client/modeller/joint-fan.js.map +1 -0
  125. package/dist-client/modeller/keys.d.ts +22 -0
  126. package/dist-client/modeller/keys.js +26 -0
  127. package/dist-client/modeller/keys.js.map +1 -0
  128. package/dist-client/modeller/links.d.ts +267 -0
  129. package/dist-client/modeller/links.js +480 -0
  130. package/dist-client/modeller/links.js.map +1 -0
  131. package/dist-client/modeller/live.d.ts +37 -0
  132. package/dist-client/modeller/live.js +68 -0
  133. package/dist-client/modeller/live.js.map +1 -0
  134. package/dist-client/modeller/maturity.d.ts +120 -0
  135. package/dist-client/modeller/maturity.js +313 -0
  136. package/dist-client/modeller/maturity.js.map +1 -0
  137. package/dist-client/modeller/modeller-route.d.ts +1 -1
  138. package/dist-client/modeller/modeller-route.js +1 -1
  139. package/dist-client/modeller/modeller-route.js.map +1 -1
  140. package/dist-client/modeller/occupancy.d.ts +18 -0
  141. package/dist-client/modeller/occupancy.js +24 -0
  142. package/dist-client/modeller/occupancy.js.map +1 -0
  143. package/dist-client/modeller/palette.d.ts +7 -0
  144. package/dist-client/modeller/palette.js +46 -0
  145. package/dist-client/modeller/palette.js.map +1 -0
  146. package/dist-client/modeller/preview-model.d.ts +47 -0
  147. package/dist-client/modeller/preview-model.js +68 -0
  148. package/dist-client/modeller/preview-model.js.map +1 -0
  149. package/dist-client/modeller/product-session.d.ts +510 -0
  150. package/dist-client/modeller/product-session.js +157 -0
  151. package/dist-client/modeller/product-session.js.map +1 -0
  152. package/dist-client/modeller/recipe-source.d.ts +8 -0
  153. package/dist-client/modeller/recipe-source.js +14 -0
  154. package/dist-client/modeller/recipe-source.js.map +1 -0
  155. package/dist-client/modeller/recipes.d.ts +37 -0
  156. package/dist-client/modeller/recipes.js +103 -0
  157. package/dist-client/modeller/recipes.js.map +1 -0
  158. package/dist-client/modeller/refusals.d.ts +68 -0
  159. package/dist-client/modeller/refusals.js +142 -0
  160. package/dist-client/modeller/refusals.js.map +1 -0
  161. package/dist-client/modeller/renderer.d.ts +40 -0
  162. package/dist-client/modeller/renderer.js +73 -0
  163. package/dist-client/modeller/renderer.js.map +1 -0
  164. package/dist-client/modeller/stage-model.d.ts +39 -0
  165. package/dist-client/modeller/stage-model.js +147 -0
  166. package/dist-client/modeller/stage-model.js.map +1 -0
  167. package/dist-client/modeller/thumbnail.d.ts +3 -0
  168. package/dist-client/modeller/thumbnail.js +75 -0
  169. package/dist-client/modeller/thumbnail.js.map +1 -0
  170. package/dist-client/modeller/v3-clips.d.ts +1 -1
  171. package/dist-client/modeller/v3-editor.d.ts +5 -0
  172. package/dist-client/modeller/v3-editor.js +21 -10
  173. package/dist-client/modeller/v3-editor.js.map +1 -1
  174. package/dist-client/modeller/v3-product-session.d.ts +31 -0
  175. package/dist-client/modeller/v3-refusals.d.ts +37 -5
  176. package/dist-client/modeller/v3-refusals.js +59 -16
  177. package/dist-client/modeller/v3-refusals.js.map +1 -1
  178. package/dist-client/pages/figure-list-page.d.ts +1 -1
  179. package/dist-client/pages/figure-list-page.js +4 -4
  180. package/dist-client/pages/figure-list-page.js.map +1 -1
  181. package/dist-client/pages/figure-modeller-page.d.ts +102 -0
  182. package/dist-client/pages/figure-modeller-page.js +548 -0
  183. package/dist-client/pages/figure-modeller-page.js.map +1 -0
  184. package/dist-client/route.d.ts +1 -1
  185. package/dist-client/route.js +1 -5
  186. package/dist-client/route.js.map +1 -1
  187. package/dist-client/tsconfig.tsbuildinfo +1 -1
  188. package/docs/product-cutover.md +25 -0
  189. package/package.json +5 -5
  190. package/test/{v3-ai-proposal.test.ts → ai-proposal.test.ts} +6 -6
  191. package/test/{v3-align.test.ts → align.test.ts} +8 -8
  192. package/test/browser/modeller-page.js +30 -30
  193. package/test/browser/modeller.check.mjs +17 -16
  194. package/test/browser/serve.mjs +1 -1
  195. package/test/browser/thumbnail-page.js +6 -6
  196. package/test/{v3-clips.test.ts → clips.test.ts} +18 -18
  197. package/test/{v3-controls.test.ts → controls.test.ts} +15 -15
  198. package/test/{v3-fields.test.ts → fields.test.ts} +13 -13
  199. package/test/figure-assistant-surface.test.ts +3 -3
  200. package/test/{v3-first-save.test.ts → first-save.test.ts} +10 -10
  201. package/test/fixtures/joint-column.json +2 -2
  202. package/test/fixtures/joint-roller.json +2 -2
  203. package/test/fixtures/robot-arm-6dof.json +2 -2
  204. package/test/{v3-gizmo.test.ts → gizmo.test.ts} +33 -33
  205. package/test/{v3-joint-drag.test.ts → joint-drag.test.ts} +17 -17
  206. package/test/{v3-joint-fan.test.ts → joint-fan.test.ts} +18 -18
  207. package/test/joints.ts +6 -6
  208. package/test/{v3-keys.test.ts → keys.test.ts} +2 -2
  209. package/test/{v3-layout.test.ts → layout.test.ts} +8 -8
  210. package/test/{v3-links.test.ts → links.test.ts} +201 -201
  211. package/test/{v3-live.test.ts → live.test.ts} +18 -18
  212. package/test/{v3-material.test.ts → material.test.ts} +12 -12
  213. package/test/{v3-maturity.test.ts → maturity.test.ts} +74 -74
  214. package/test/modeller-route.test.ts +5 -6
  215. package/test/{v3-palette-icons.test.ts → palette-icons.test.ts} +1 -1
  216. package/test/{v3-parts-wiring.test.ts → parts-wiring.test.ts} +11 -7
  217. package/test/{v3-preview.test.ts → preview.test.ts} +12 -12
  218. package/test/{v3-product-session.test.ts → product-session.test.ts} +17 -17
  219. package/test/{v3-product-write.test.ts → product-write.test.ts} +20 -20
  220. package/test/{v3-properties.test.ts → properties.test.ts} +24 -24
  221. package/test/{v3-recipes.test.ts → recipes.test.ts} +10 -10
  222. package/test/refusals.test.ts +166 -0
  223. package/test/{v3-release-panel.test.ts → release-panel.test.ts} +21 -21
  224. package/test/renderer.test.ts +24 -0
  225. package/test/resolve-ts-siblings.mjs +1 -1
  226. package/test/{v3-scene-canvas.test.ts → scene-canvas.test.ts} +6 -6
  227. package/test/{v3-stage-model.test.ts → stage-model.test.ts} +5 -5
  228. package/test/{v3-theme.test.ts → theme.test.ts} +4 -4
  229. package/test/{v3-thumbnail.test.ts → thumbnail.test.ts} +4 -4
  230. package/test/{v3-verdict.test.ts → verdict.test.ts} +17 -17
  231. package/test/{v3-view.test.ts → view.test.ts} +2 -2
  232. package/things-factory.config.js +2 -3
  233. package/translations/en.json +15 -7
  234. package/translations/ja.json +15 -7
  235. package/translations/ko.json +15 -7
  236. package/translations/ms.json +15 -7
  237. package/translations/zh.json +15 -7
  238. package/docs/v3-product-cutover.md +0 -25
  239. package/test/v3-refusals.test.ts +0 -136
  240. package/test/v3-renderer.test.ts +0 -24
  241. /package/client/modeller/{v3-fields.ts → fields.ts} +0 -0
  242. /package/client/modeller/{v3-first-save.ts → first-save.ts} +0 -0
  243. /package/client/modeller/{v3-help.ts → help.ts} +0 -0
  244. /package/client/modeller/{v3-keys.ts → keys.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-capability-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-capability-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { figureBindingsOf } from '@hatiolab/figure-model'\nimport type { FigureCapability, FigureAsset } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from './side-panel-styles.js'\n\n/*\n One rung per set of the format's capability names. Typed as the format's names, so a misspelt rung does not pass\n (ADR-0051 ④).\n*/\ntype Rung = 'none' | 'hold' | 'flow'\nconst RUNGS: Readonly<Record<Rung, readonly FigureCapability[]>> = Object.freeze({\n none: [],\n hold: ['Capacity', 'Holdable'],\n flow: ['FlowNode']\n})\n\n/** Which rung a set of names is, or null when it is none of them -- not invented. */\nexport function rungOf(names: readonly string[] | undefined): Rung | null {\n const has = new Set(names ?? [])\n if (has.size === 0) return 'none'\n for (const [rung, wanted] of Object.entries(RUNGS) as [Rung, readonly FigureCapability[]][])\n if (wanted.length === has.size && wanted.every(name => has.has(name))) return rung\n return null\n}\n\n/**\n * 자리·능력 — what the figure does on a board: the ladder from drawing only, to\n * holding things, to passing them on, and every place things are held, whatever part it is on. A place is not drawn,\n * so the list is the only way to find one; choosing a row selects its part, where the place is edited.\n */\n@customElement('figure-capability-panel')\nexport class FigureCapabilityPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n label[rung] {\n display: flex;\n gap: 8px;\n align-items: flex-start;\n padding: 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n cursor: pointer;\n }\n label[rung][on] {\n border-color: var(--md-sys-color-primary);\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n label[rung] span {\n display: grid;\n gap: 2px;\n }\n label[rung] em,\n p[hint],\n small {\n font-style: normal;\n margin: 0;\n color: var(--md-sys-color-on-surface-variant);\n }\n button[place] {\n display: grid;\n gap: 2px;\n text-align: left;\n padding: 6px 8px;\n font: inherit;\n color: inherit;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n background: none;\n cursor: pointer;\n }\n button[place][on] {\n border-color: var(--md-sys-color-primary);\n }\n code {\n font-size: 0.9em;\n color: var(--md-sys-color-on-surface-variant);\n }\n `]\n\n @property({ attribute: false }) asset?: FigureAsset\n /** The selected part, whose places are marked. */\n @property() selected = ''\n @property({ type: Boolean }) locked = false\n\n render() {\n const asset = this.asset\n if (!asset) return nothing\n const names = asset.document.capabilities.names ?? []\n const rung = rungOf(names)\n const places = figureBindingsOf(asset)\n return html`<section>\n <h3>${i18next.t('figure.label.what-this-does')}</h3>\n ${(['none', 'hold', 'flow'] as const).map(\n which => html`<label rung ?on=${rung === which}>\n <input type=\"radio\" name=\"rung\" .checked=${live(rung === which)} ?disabled=${this.locked} @change=${() => this.setRung(which)} />\n <span>\n <strong>${i18next.t(`figure.label.rung-${which}`)}</strong>\n <em>${i18next.t(`figure.text.rung-${which}-note`)}</em>\n </span>\n </label>`\n )}\n ${rung === null ? html`<p hint>${i18next.t('figure.text.capability-hand-written')}</p>` : nothing}\n ${names.length ? html`<code>${names.join(' · ')}</code>` : nothing}\n </section>\n <section>\n <h3>${i18next.t('figure.label.places')}</h3>\n ${places.length\n ? places.map(\n one => html`<button place ?on=${one.part === this.selected} @click=${() => one.part && this.pick(one.part)}>\n <strong>${one.label}</strong>\n <small>\n ${i18next.t('figure.label.place-' + one.role)} · ${one.part ?? '?'}${one.face ? ' · ' + i18next.t('figure.label.face-' + one.face) : ''} ·\n ${one.accepts.join(', ')} · ${i18next.t('figure.label.place-holds', { count: one.capacity })}\n </small>\n </button>`\n )\n : html`<p hint>${i18next.t('figure.text.no-places')}</p>`}\n ${places.length && rung === 'none' ? html`<p hint>${i18next.t('figure.text.place-needs-holding')}</p>` : nothing}\n </section>\n `\n }\n\n private setRung(which: Rung) {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit: { kind: 'set-capabilities', names: [...RUNGS[which]] } }, bubbles: true, composed: true }))\n }\n\n private pick(part: string) {\n this.dispatchEvent(new CustomEvent('select-part', { detail: { id: part }, bubbles: true, composed: true }))\n }\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import '@material/web/icon/icon.js';
2
+ import { LitElement, nothing } from 'lit';
3
+ import type { FigureAsset } from '@hatiolab/figure-model';
4
+ declare const FigureClips_base: (new (...args: any[]) => LitElement) & typeof LitElement;
5
+ /**
6
+ * 클립 — controls played by keys over time. A clip is one clock; each track plays one
7
+ * control. Scrubbing a clip shows its pose at that moment (one batch of input changes, not saved as the default); a key
8
+ * is added at the scrubbed moment with the control's value there, so a pose is made with the controls' sliders and
9
+ * then kept as a key. Every change is one part edit.
10
+ */
11
+ export declare class FigureClips extends FigureClips_base {
12
+ private readonly help;
13
+ static styles: import("lit").CSSResult[];
14
+ asset?: FigureAsset;
15
+ /** The values the editor holds now, by input id -- what a new key takes. */
16
+ values: Record<string, number>;
17
+ locked: boolean;
18
+ /** Where each clip is scrubbed to, in seconds. */
19
+ private at;
20
+ private adding;
21
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
22
+ private renderClip;
23
+ private renderTrack;
24
+ private renderAdd;
25
+ /** Show the clip's pose at a moment: one batch of input changes, not saved as the default. */
26
+ /** Show the clip's pose at a moment. While the bar is dragged the pose is live (drawn, not recorded); the release
27
+ * applies it as one batch of input changes. */
28
+ private scrub;
29
+ private edit;
30
+ }
31
+ export {};
@@ -0,0 +1,268 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import { css, html, LitElement, nothing } from 'lit';
4
+ import { customElement, property, state } from 'lit/decorators.js';
5
+ import { i18next, localize } from '@operato/i18n';
6
+ import { clipsOf, poseAt, trackEdit, undrivenOf, withKey, withoutKey } from './clips.js';
7
+ import { SidePanelStyles } from './side-panel-styles.js';
8
+ import { HelpToggles } from './help.js';
9
+ /**
10
+ * 클립 — controls played by keys over time. A clip is one clock; each track plays one
11
+ * control. Scrubbing a clip shows its pose at that moment (one batch of input changes, not saved as the default); a key
12
+ * is added at the scrubbed moment with the control's value there, so a pose is made with the controls' sliders and
13
+ * then kept as a key. Every change is one part edit.
14
+ */
15
+ let FigureClips = class FigureClips extends localize(i18next)(LitElement) {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.help = new HelpToggles(this);
19
+ /** The values the editor holds now, by input id -- what a new key takes. */
20
+ this.values = {};
21
+ this.locked = false;
22
+ /** Where each clip is scrubbed to, in seconds. */
23
+ this.at = {};
24
+ this.adding = { state: '', clip: '', name: '', duration: 2 };
25
+ }
26
+ static { this.styles = [SidePanelStyles, css `
27
+ :host {
28
+ display: block;
29
+ }
30
+ section[clip] {
31
+ display: grid;
32
+ gap: 8px;
33
+ padding: 8px;
34
+ border: 1px solid var(--md-sys-color-outline-variant);
35
+ border-radius: 8px;
36
+ }
37
+ div[row] {
38
+ display: grid;
39
+ grid-template-columns: minmax(0, 1fr) 72px;
40
+ gap: 6px;
41
+ align-items: center;
42
+ }
43
+ div[track] {
44
+ display: grid;
45
+ gap: 4px;
46
+ padding-top: 6px;
47
+ border-top: 1px dashed var(--md-sys-color-outline-variant);
48
+ }
49
+ /*
50
+ The track's name on its own line, then how it repeats and the button that removes it. A flex row let the shared
51
+ full-width select and button squeeze the name to one letter a line (user, 2026-09-24).
52
+ */
53
+ div[track-head] {
54
+ display: grid;
55
+ grid-template-columns: minmax(0, 1fr) 30px;
56
+ gap: 4px 6px;
57
+ align-items: center;
58
+ }
59
+ div[track-head] strong {
60
+ grid-column: 1 / -1;
61
+ min-width: 0;
62
+ overflow-wrap: anywhere;
63
+ }
64
+ div[track-head] button[act] {
65
+ width: 30px;
66
+ min-height: var(--figure-control-height);
67
+ margin: 0;
68
+ padding: 0;
69
+ }
70
+ div[keys] {
71
+ display: flex;
72
+ flex-wrap: wrap;
73
+ gap: 4px;
74
+ }
75
+ span[key] {
76
+ display: inline-flex;
77
+ align-items: center;
78
+ gap: 2px;
79
+ padding: 0 2px 0 8px;
80
+ border-radius: 999em;
81
+ background: var(--md-sys-color-surface-container);
82
+ font-variant-numeric: tabular-nums;
83
+ }
84
+ span[key] button {
85
+ width: 22px;
86
+ height: 22px;
87
+ padding: 0;
88
+ border: none;
89
+ background: none;
90
+ }
91
+ md-icon {
92
+ font-size: 16px;
93
+ width: 16px;
94
+ height: 16px;
95
+ }
96
+ `]; }
97
+ render() {
98
+ const asset = this.asset;
99
+ if (!asset)
100
+ return nothing;
101
+ const clips = clipsOf(asset);
102
+ return html `
103
+ <section>
104
+ <h3>${i18next.t('figure.label.clips')}${this.help.button('figure.text.clips-explained')}</h3>
105
+ ${this.help.hint('figure.text.clips-explained')}
106
+ ${clips.map(clip => this.renderClip(clip))}
107
+ </section>
108
+ ${this.renderAdd(asset, clips)}
109
+ `;
110
+ }
111
+ renderClip(clip) {
112
+ const at = Math.min(this.at[clip.name] ?? 0, clip.duration);
113
+ const number = (e) => e.target.valueAsNumber;
114
+ return html `<section clip>
115
+ <div form>
116
+ <span>${i18next.t('figure.label.clip-name')}</span>
117
+ <input .value=${clip.name} ?disabled=${this.locked} @change=${(e) => this.edit({ kind: 'set-clip', clip: clip.name, name: e.target.value })} />
118
+ <span>${i18next.t('figure.label.clip-duration')}</span>
119
+ <input type="number" min="0.1" step="any" .value=${String(clip.duration)} ?disabled=${this.locked} @change=${(e) => this.edit({ kind: 'set-clip', clip: clip.name, duration: number(e) })} />
120
+ </div>
121
+ <div row>
122
+ <input
123
+ type="range"
124
+ min="0"
125
+ max=${clip.duration}
126
+ step=${clip.duration / 100}
127
+ .value=${String(at)}
128
+ ?disabled=${this.locked}
129
+ aria-label=${i18next.t('figure.label.clip-time')}
130
+ @input=${(e) => this.scrub(clip, number(e), true)}
131
+ @change=${(e) => this.scrub(clip, number(e))}
132
+ />
133
+ <small>${at.toFixed(2)} s</small>
134
+ </div>
135
+ ${clip.tracks.map(track => this.renderTrack(clip, track, at))}
136
+ </section>`;
137
+ }
138
+ renderTrack(clip, track, at) {
139
+ const current = this.values[track.state];
140
+ return html `<div track>
141
+ <div track-head>
142
+ <strong>${track.label} <small>(${track.unit})</small></strong>
143
+ <select
144
+ aria-label=${i18next.t('figure.label.clip-loop')}
145
+ ?disabled=${this.locked}
146
+ @change=${(e) => this.edit(trackEdit(clip, track, { loop: e.target.value }))}
147
+ >
148
+ <option value="wrap" ?selected=${track.loop === 'wrap'}>${i18next.t('figure.label.clip-loop-wrap')}</option>
149
+ <option value="hold" ?selected=${track.loop === 'hold'}>${i18next.t('figure.label.clip-loop-hold')}</option>
150
+ </select>
151
+ <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-track')} @click=${() => this.edit({ kind: 'remove-driver', state: track.state })}>
152
+ <md-icon>close</md-icon>
153
+ </button>
154
+ </div>
155
+ <div keys>
156
+ ${track.keys.map((key, i) => html `<span key
157
+ >${key.seconds}s → ${key.value}
158
+ <button
159
+ ?disabled=${this.locked || !withoutKey(track.keys, i)}
160
+ title=${i18next.t('figure.button.remove-key')}
161
+ aria-label=${i18next.t('figure.button.remove-key')}
162
+ @click=${() => this.edit(trackEdit(clip, track, { keys: withoutKey(track.keys, i) }))}
163
+ >
164
+ <md-icon>close</md-icon>
165
+ </button></span
166
+ >`)}
167
+ </div>
168
+ <button
169
+ act
170
+ ?disabled=${this.locked || current === undefined}
171
+ @click=${() => this.edit(trackEdit(clip, track, { keys: withKey(track.keys, { seconds: Math.round(at * 1000) / 1000, value: current }) }))}
172
+ >
173
+ <md-icon>add</md-icon>${i18next.t('figure.button.add-key-here', { seconds: at.toFixed(2), value: current ?? '' })}
174
+ </button>
175
+ </div>`;
176
+ }
177
+ 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
+ const a = this.adding;
182
+ const stateId = free.includes(a.state) ? a.state : free[0];
183
+ const joining = clips.find(c => c.name === a.clip);
184
+ const name = joining ? joining.name : a.name.trim();
185
+ const input = asset.document.model.inputs.find(i => i.id === stateId);
186
+ const duration = joining ? joining.duration : a.duration;
187
+ const ready = !!name && duration > 0;
188
+ return html `<section><h3>${i18next.t('figure.label.add-track')}</h3>
189
+ <div form>
190
+ <span>${i18next.t('figure.label.controls')}</span>
191
+ <select @change=${(e) => (this.adding = { ...a, state: e.target.value })}>
192
+ ${free.map(id => html `<option value=${id} ?selected=${id === stateId}>${asset.stateInputs?.[id]?.label ?? id}</option>`)}
193
+ </select>
194
+ <span>${i18next.t('figure.label.clip')}</span>
195
+ <select @change=${(e) => (this.adding = { ...a, clip: e.target.value })}>
196
+ <option value="" ?selected=${!joining}>${i18next.t('figure.label.new-clip')}</option>
197
+ ${clips.map(c => html `<option value=${c.name} ?selected=${c === joining}>${c.name}</option>`)}
198
+ </select>
199
+ ${joining
200
+ ? nothing
201
+ : html `<span>${i18next.t('figure.label.clip-name')}</span>
202
+ <input .value=${a.name} placeholder="cycle" @input=${(e) => (this.adding = { ...a, name: e.target.value })} />
203
+ <span>${i18next.t('figure.label.clip-duration')}</span>
204
+ <input type="number" min="0.1" step="any" .value=${String(a.duration)} @change=${(e) => (this.adding = { ...a, duration: e.target.valueAsNumber })} />`}
205
+ </div>
206
+ <button
207
+ act
208
+ ?disabled=${this.locked || !ready}
209
+ @click=${() => this.edit({
210
+ kind: 'set-driver',
211
+ state: stateId,
212
+ clip: name,
213
+ ...(joining ? {} : { duration }),
214
+ keys: [
215
+ { seconds: 0, value: input.min },
216
+ { seconds: duration, value: input.max }
217
+ ]
218
+ })}
219
+ >
220
+ <md-icon>add</md-icon>${i18next.t('figure.button.add-track')}
221
+ </button></section>`;
222
+ }
223
+ /** Show the clip's pose at a moment: one batch of input changes, not saved as the default. */
224
+ /** Show the clip's pose at a moment. While the bar is dragged the pose is live (drawn, not recorded); the release
225
+ * applies it as one batch of input changes. */
226
+ scrub(clip, seconds, live = false) {
227
+ if (!this.asset || !Number.isFinite(seconds))
228
+ return;
229
+ this.at = { ...this.at, [clip.name]: seconds };
230
+ const changes = poseAt(this.asset, clip.name, seconds);
231
+ if (!changes.length)
232
+ return;
233
+ if (live) {
234
+ const values = Object.fromEntries(changes.map(c => [c.input, c.value]));
235
+ this.dispatchEvent(new CustomEvent('states-live', { detail: { values }, bubbles: true, composed: true }));
236
+ return;
237
+ }
238
+ this.dispatchEvent(new CustomEvent('states', { detail: { changes }, bubbles: true, composed: true }));
239
+ }
240
+ edit(edit) {
241
+ this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }));
242
+ }
243
+ };
244
+ __decorate([
245
+ property({ attribute: false }),
246
+ __metadata("design:type", Object)
247
+ ], FigureClips.prototype, "asset", void 0);
248
+ __decorate([
249
+ property({ attribute: false }),
250
+ __metadata("design:type", Object)
251
+ ], FigureClips.prototype, "values", void 0);
252
+ __decorate([
253
+ property({ type: Boolean }),
254
+ __metadata("design:type", Object)
255
+ ], FigureClips.prototype, "locked", void 0);
256
+ __decorate([
257
+ state(),
258
+ __metadata("design:type", Object)
259
+ ], FigureClips.prototype, "at", void 0);
260
+ __decorate([
261
+ state(),
262
+ __metadata("design:type", Object)
263
+ ], FigureClips.prototype, "adding", void 0);
264
+ FigureClips = __decorate([
265
+ customElement('figure-clips')
266
+ ], FigureClips);
267
+ export { FigureClips };
268
+ //# sourceMappingURL=figure-clips.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-clips.js","sourceRoot":"","sources":["../../client/modeller/figure-clips.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AAEnC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAGjD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAA6B,MAAM,YAAY,CAAA;AACnH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEvC;;;;;GAKG;AAEI,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAAvD;;QACY,SAAI,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAA;QA0E7C,4EAA4E;QAC5C,WAAM,GAA2B,EAAE,CAAA;QACtC,WAAM,GAAG,KAAK,CAAA;QAE3C,kDAAkD;QACjC,OAAE,GAA2B,EAAE,CAAA;QAC/B,WAAM,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAA;IAwJ1E,CAAC;aAvOQ,WAAM,GAAG,CAAC,eAAe,EAAE,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEpC,CAAC,AAtEW,CAsEX;IAWF,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACxB,IAAI,CAAC,KAAK;YAAE,OAAO,OAAO,CAAA;QAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;QAC5B,OAAO,IAAI,CAAA;;cAED,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,6BAA6B,CAAC;UACrF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC;UAC7C,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;;QAE1C,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC;KAC/B,CAAA;IACH,CAAC;IAEO,UAAU,CAAC,IAAU;QAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC3D,MAAM,MAAM,GAAG,CAAC,CAAQ,EAAE,EAAE,CAAE,CAAC,CAAC,MAA2B,CAAC,aAAa,CAAA;QACzE,OAAO,IAAI,CAAA;;gBAEC,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;wBAC3B,IAAI,CAAC,IAAI,cAAc,IAAI,CAAC,MAAM,YAAY,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAG,CAAC,CAAC,MAA2B,CAAC,KAAK,EAAE,CAAC;gBAChK,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC;2DACI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,IAAI,CAAC,MAAM,YAAY,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;;;;;;gBAMxL,IAAI,CAAC,QAAQ;iBACZ,IAAI,CAAC,QAAQ,GAAG,GAAG;mBACjB,MAAM,CAAC,EAAE,CAAC;sBACP,IAAI,CAAC,MAAM;uBACV,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;mBACvC,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;oBAC9C,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;;iBAE5C,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;;QAEtB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;eACpD,CAAA;IACb,CAAC;IAEO,WAAW,CAAC,IAAU,EAAE,KAAgB,EAAE,EAAU;QAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACxC,OAAO,IAAI,CAAA;;kBAEG,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,IAAI;;uBAE5B,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;sBACpC,IAAI,CAAC,MAAM;oBACb,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAG,CAAC,CAAC,MAA4B,CAAC,KAAwB,EAAE,CAAC,CAAC;;2CAE5F,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;2CACjE,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;;gCAE5E,IAAI,CAAC,MAAM,UAAU,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;;;;;UAK3J,KAAK,CAAC,IAAI,CAAC,GAAG,CACd,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA;eACX,GAAG,CAAC,OAAO,OAAO,GAAG,CAAC,KAAK;;0BAEhB,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;sBAC7C,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;2BAChC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;uBACzC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAE,EAAE,CAAC,CAAC;;;;YAIxF,CACH;;;;oBAIW,IAAI,CAAC,MAAM,IAAI,OAAO,KAAK,SAAS;iBACvC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,OAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;;gCAEnH,OAAO,CAAC,CAAC,CAAC,4BAA4B,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;;WAE9G,CAAA;IACT,CAAC;IAEO,SAAS,CAAC,KAAkB,EAAE,KAAa;QACjD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,WAAW,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,MAAM,CAAA;QAC3G,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAA;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,CAAA;QAC3D,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QACnD,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAE,CAAA;QACtE,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;QACxD,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAA;QACpC,OAAO,IAAI,CAAA,gBAAgB,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;;gBAElD,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;0BACxB,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,EAAE,KAAK,EAAG,CAAC,CAAC,MAA4B,CAAC,KAAK,EAAE,CAAC;YAClG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAA,iBAAiB,EAAE,cAAc,EAAE,KAAK,OAAO,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,WAAW,CAAC;;gBAElH,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC;0BACpB,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAG,CAAC,CAAC,MAA4B,CAAC,KAAK,EAAE,CAAC;uCACtE,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;YACzE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAA,iBAAiB,CAAC,CAAC,IAAI,cAAc,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,WAAW,CAAC;;UAE7F,OAAO;YACP,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,IAAI,CAAA,SAAS,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;8BAC9B,CAAC,CAAC,IAAI,+BAA+B,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAG,CAAC,CAAC,MAA2B,CAAC,KAAK,EAAE,CAAC;sBAC/H,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC;iEACI,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAG,CAAC,CAAC,MAA2B,CAAC,aAAa,EAAE,CAAC,KAAK;;;;oBAI9K,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK;iBACxB,GAAG,EAAE,CACZ,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,IAAI;YACV,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;YAChC,IAAI,EAAE;gBACJ,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE;gBAChC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE;aACxC;SACF,CAAC;;gCAEoB,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC;0BAC1C,CAAA;IACxB,CAAC;IAED,8FAA8F;IAC9F;mDAC+C;IACvC,KAAK,CAAC,IAAU,EAAE,OAAe,EAAE,IAAI,GAAG,KAAK;QACrD,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAM;QACpD,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAA;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACtD,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAM;QAC3B,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACvE,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;YACzG,OAAM;QACR,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACvG,CAAC;IAEO,IAAI,CAAC,IAAa;QACxB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACvG,CAAC;;AA9J+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;0CAAoB;AAEnB;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;2CAAoC;AACtC;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;2CAAe;AAG1B;IAAhB,KAAK,EAAE;;uCAAwC;AAC/B;IAAhB,KAAK,EAAE;;2CAAgE;AAjF7D,WAAW;IADvB,aAAa,CAAC,cAAc,CAAC;GACjB,WAAW,CAyOvB","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 './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"]}
@@ -0,0 +1,36 @@
1
+ import '@material/web/icon/icon.js';
2
+ import { LitElement, nothing } from 'lit';
3
+ import type { FigureAsset } from '@hatiolab/figure-model';
4
+ declare const FigureControls_base: (new (...args: any[]) => LitElement) & typeof LitElement;
5
+ /**
6
+ * The figure's controls and its pose tools: each value a board sets to pose the figure,
7
+ * with a slider to try it, its name, range and travel time, and a button that keeps the pose being looked at as the
8
+ * pose it stands in when placed.
9
+ *
10
+ * Trying a value is an input change (`state`, not saved as the default); naming, the range and the travel time are
11
+ * one `set-state-input` part edit each; keeping the pose is one `set-state-defaults` edit. A control is made with
12
+ * a motion on a part (「움직임」), which is where it gets its meaning.
13
+ */
14
+ export declare class FigureControls extends FigureControls_base {
15
+ static styles: import("lit").CSSResult[];
16
+ /** The asset being edited. */
17
+ asset?: FigureAsset;
18
+ /** The values the editor holds now, by input id. */
19
+ values: Record<string, number>;
20
+ locked: boolean;
21
+ /** Which control's settings are open. */
22
+ private open;
23
+ /** Values in hand while a slider is dragged: the dial and the number follow them before the release commits. */
24
+ private live;
25
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
26
+ private renderControl;
27
+ private renderDial;
28
+ private renderSettings;
29
+ /** While dragging: the value is told as live -- the editor draws that pose at once and records nothing. */
30
+ private drag;
31
+ /** Try a value: the pose being looked at, clamped to the range. Not saved as the default. The slider sends it on
32
+ * release, so one drag is one step of the undo history rather than one per pixel. */
33
+ private try;
34
+ private edit;
35
+ }
36
+ export {};
@@ -0,0 +1,226 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import { css, html, LitElement, nothing } from 'lit';
4
+ import { customElement, property, state } from 'lit/decorators.js';
5
+ import { i18next, localize } from '@operato/i18n';
6
+ import { controlsOf, poseAsDefault, poseDiffers } from './controls.js';
7
+ import { dialShape } from './angle-dial.js';
8
+ import { SidePanelStyles } from './side-panel-styles.js';
9
+ /**
10
+ * The figure's controls and its pose tools: each value a board sets to pose the figure,
11
+ * with a slider to try it, its name, range and travel time, and a button that keeps the pose being looked at as the
12
+ * pose it stands in when placed.
13
+ *
14
+ * Trying a value is an input change (`state`, not saved as the default); naming, the range and the travel time are
15
+ * one `set-state-input` part edit each; keeping the pose is one `set-state-defaults` edit. A control is made with
16
+ * a motion on a part (「움직임」), which is where it gets its meaning.
17
+ */
18
+ let FigureControls = class FigureControls extends localize(i18next)(LitElement) {
19
+ constructor() {
20
+ super(...arguments);
21
+ /** The values the editor holds now, by input id. */
22
+ this.values = {};
23
+ this.locked = false;
24
+ /** Which control's settings are open. */
25
+ this.open = '';
26
+ /** Values in hand while a slider is dragged: the dial and the number follow them before the release commits. */
27
+ this.live = {};
28
+ }
29
+ static { this.styles = [SidePanelStyles, css `
30
+ :host {
31
+ display: block;
32
+ }
33
+ /* One control, one line: its name, a dial for an angle, a thin slider, the value. */
34
+ div[control] {
35
+ display: grid;
36
+ grid-template-columns: minmax(0, 1fr) 18px minmax(64px, 1.2fr) 58px 22px;
37
+ align-items: center;
38
+ gap: 6px;
39
+ min-height: 28px;
40
+ }
41
+ div[control] > span[name] {
42
+ overflow: hidden;
43
+ text-overflow: ellipsis;
44
+ white-space: nowrap;
45
+ }
46
+ div[control] > span[name] em {
47
+ font-style: normal;
48
+ font-size: 0.64rem;
49
+ color: var(--md-sys-color-on-surface-variant);
50
+ }
51
+ div[control] input[type='number'] {
52
+ min-height: 24px;
53
+ padding: 2px 5px;
54
+ font-family: var(--mono-font, monospace);
55
+ text-align: right;
56
+ }
57
+ svg[dial] {
58
+ width: 18px;
59
+ height: 18px;
60
+ }
61
+ svg[dial] path {
62
+ fill: var(--md-sys-color-surface-container-high);
63
+ }
64
+ svg[dial] circle {
65
+ fill: none;
66
+ stroke: var(--md-sys-color-outline-variant);
67
+ }
68
+ svg[dial] line {
69
+ stroke: var(--md-sys-color-primary);
70
+ stroke-width: 2;
71
+ stroke-linecap: round;
72
+ }
73
+ button[more] {
74
+ display: inline-flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ width: 22px;
78
+ height: 22px;
79
+ padding: 0;
80
+ border: none;
81
+ border-radius: 4px;
82
+ background: none;
83
+ color: var(--md-sys-color-on-surface-variant);
84
+ cursor: pointer;
85
+ }
86
+ button[more][aria-expanded='true'] {
87
+ color: var(--md-sys-color-primary);
88
+ background: var(--md-sys-color-surface-container);
89
+ }
90
+ button[more] md-icon {
91
+ font-size: 16px;
92
+ width: 16px;
93
+ height: 16px;
94
+ }
95
+ /* The rarely changed: the name, the range, the travel time -- opened per control. */
96
+ div[settings] {
97
+ margin: 2px 0 8px;
98
+ padding: 6px 0 6px 8px;
99
+ border-left: 2px solid var(--md-sys-color-outline-variant);
100
+ }
101
+ `]; }
102
+ render() {
103
+ if (!this.asset)
104
+ return nothing;
105
+ const controls = controlsOf(this.asset, this.values);
106
+ return html `<section>
107
+ <h3>${i18next.t('figure.label.controls')}</h3>
108
+ ${controls.length
109
+ ? html `${controls.map(control => this.renderControl(control))}
110
+ <button act ?disabled=${this.locked || !poseDiffers(controls)} @click=${() => this.edit(poseAsDefault(controls))}>
111
+ <md-icon>push_pin</md-icon>${i18next.t('figure.button.keep-pose')}
112
+ </button>`
113
+ : html `<p hint>${i18next.t('figure.text.no-controls')}</p>`}
114
+ </section>`;
115
+ }
116
+ renderControl(control) {
117
+ const number = (e) => e.target.valueAsNumber;
118
+ const c = this.live[control.id] === undefined ? control : { ...control, value: this.live[control.id] };
119
+ const open = this.open === c.id;
120
+ return html `<div control>
121
+ <span name title=${c.id}>${c.label} <em>${c.unit}</em></span>
122
+ ${c.unit === 'deg' ? this.renderDial(c) : html `<span></span>`}
123
+ <input
124
+ type="range"
125
+ min=${c.min}
126
+ max=${c.max}
127
+ step=${c.step}
128
+ .value=${String(c.value)}
129
+ ?disabled=${this.locked}
130
+ aria-label=${c.label}
131
+ @input=${(e) => this.drag(c, number(e))}
132
+ @change=${(e) => this.try(c, number(e))}
133
+ />
134
+ <input type="number" step="any" .value=${String(Math.round(c.value * 100) / 100)} ?disabled=${this.locked} aria-label=${c.label} @change=${(e) => this.try(c, number(e))} />
135
+ <button
136
+ more
137
+ aria-expanded=${String(open)}
138
+ title=${i18next.t('figure.button.control-settings')}
139
+ aria-label=${i18next.t('figure.button.control-settings')}
140
+ @click=${() => (this.open = open ? '' : c.id)}
141
+ >
142
+ <md-icon>tune</md-icon>
143
+ </button>
144
+ </div>
145
+ ${open ? this.renderSettings(c) : nothing}`;
146
+ }
147
+ renderDial(c) {
148
+ const dial = dialShape(c.min, c.max, c.value);
149
+ const span = dial.radius * 2 + 2;
150
+ return html `<svg dial viewBox="0 0 ${span} ${span}" aria-hidden="true">
151
+ <path d=${dial.sector}></path>
152
+ <circle cx=${dial.centre.x} cy=${dial.centre.y} r=${dial.radius}></circle>
153
+ <line x1=${dial.centre.x} y1=${dial.centre.y} x2=${dial.needle.x} y2=${dial.needle.y}></line>
154
+ </svg>`;
155
+ }
156
+ renderSettings(c) {
157
+ const policy = (patch) => this.edit({
158
+ kind: 'set-state-input',
159
+ input: c.id,
160
+ label: patch.label ?? (c.named ? c.label : undefined),
161
+ sweep: patch.sweep ?? c.sweep,
162
+ ...(patch.min !== undefined ? { min: patch.min } : {}),
163
+ ...(patch.max !== undefined ? { max: patch.max } : {})
164
+ });
165
+ const number = (e) => e.target.valueAsNumber;
166
+ return html `<div settings>
167
+ <div form>
168
+ <span>${i18next.t('figure.label.control-name')}</span>
169
+ <input .value=${c.named ? c.label : ''} placeholder=${c.id} ?disabled=${this.locked} @change=${(e) => policy({ label: e.target.value })} />
170
+ <span>${i18next.t('figure.label.control-range')} (${c.unit})</span>
171
+ <div pair>
172
+ <input type="number" step="any" .value=${String(c.min)} ?disabled=${this.locked} @change=${(e) => policy({ min: number(e) })} />
173
+ <input type="number" step="any" .value=${String(c.max)} ?disabled=${this.locked} @change=${(e) => policy({ max: number(e) })} />
174
+ </div>
175
+ <span>${i18next.t('figure.label.control-sweep')}</span>
176
+ <input type="number" min="0" step="any" .value=${String(c.sweep)} ?disabled=${this.locked} @change=${(e) => policy({ sweep: number(e) })} />
177
+ </div>
178
+ <small>${i18next.t('figure.text.control-starts-at', { value: c.start, unit: c.unit })}</small>
179
+ </div>`;
180
+ }
181
+ /** While dragging: the value is told as live -- the editor draws that pose at once and records nothing. */
182
+ drag(c, value) {
183
+ if (!Number.isFinite(value))
184
+ return;
185
+ this.live = { ...this.live, [c.id]: value };
186
+ this.dispatchEvent(new CustomEvent('state-live', { detail: { input: c.id, value }, bubbles: true, composed: true }));
187
+ }
188
+ /** Try a value: the pose being looked at, clamped to the range. Not saved as the default. The slider sends it on
189
+ * release, so one drag is one step of the undo history rather than one per pixel. */
190
+ try(c, value) {
191
+ if (!Number.isFinite(value))
192
+ return;
193
+ const clamped = Math.min(Math.max(value, c.min), c.max);
194
+ const { [c.id]: _released, ...rest } = this.live;
195
+ this.live = rest;
196
+ this.dispatchEvent(new CustomEvent('state', { detail: { input: c.id, unit: c.unit, value: clamped }, bubbles: true, composed: true }));
197
+ }
198
+ edit(edit) {
199
+ this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }));
200
+ }
201
+ };
202
+ __decorate([
203
+ property({ attribute: false }),
204
+ __metadata("design:type", Object)
205
+ ], FigureControls.prototype, "asset", void 0);
206
+ __decorate([
207
+ property({ attribute: false }),
208
+ __metadata("design:type", Object)
209
+ ], FigureControls.prototype, "values", void 0);
210
+ __decorate([
211
+ property({ type: Boolean }),
212
+ __metadata("design:type", Object)
213
+ ], FigureControls.prototype, "locked", void 0);
214
+ __decorate([
215
+ state(),
216
+ __metadata("design:type", Object)
217
+ ], FigureControls.prototype, "open", void 0);
218
+ __decorate([
219
+ state(),
220
+ __metadata("design:type", Object)
221
+ ], FigureControls.prototype, "live", void 0);
222
+ FigureControls = __decorate([
223
+ customElement('figure-controls')
224
+ ], FigureControls);
225
+ export { FigureControls };
226
+ //# sourceMappingURL=figure-controls.js.map