@things-factory/figure-ui 10.1.114 → 10.1.115

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.
@@ -14,6 +14,8 @@ export declare class FigurePreview extends FigurePreview_base {
14
14
  /** Clip speeds and control values given here -- the preview's, not the figure's. */
15
15
  private driving;
16
16
  private giving;
17
+ /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */
18
+ selected: string;
17
19
  private setupPart;
18
20
  /** What is being tried here now -- clip speeds and control values -- for the AI dock to hear with a request; none when nothing is. */
19
21
  tried(): {
@@ -6,7 +6,7 @@ import { compileFigureAsset } from '@hatiolab/figure-model';
6
6
  import { create, registerFigure, unregisterFigure } from '@hatiolab/things-scene';
7
7
  import { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH } from './figure-view.js';
8
8
  import { dialShape } from './angle-dial.js';
9
- import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances } from './preview-model.js';
9
+ import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, setupPartOf } from './preview-model.js';
10
10
  let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
11
11
  constructor() {
12
12
  super(...arguments);
@@ -17,6 +17,8 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
17
17
  /** Clip speeds and control values given here -- the preview's, not the figure's. */
18
18
  this.driving = {};
19
19
  this.giving = {};
20
+ /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */
21
+ this.selected = '';
20
22
  this.setupPart = '';
21
23
  }
22
24
  static { this.styles = css `
@@ -35,7 +37,25 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
35
37
  width: 100%;
36
38
  height: 100%;
37
39
  }
38
- div[setup] { position: absolute; bottom: 8px; left: 8px; max-width: min(320px, calc(100% - 16px)); padding: 12px; border-radius: 8px; background: rgba(28,28,30,.9); color: #fff; display: grid; gap: 8px; }
40
+ /*
41
+ The size help and the posing controls share the stage's bottom edge. Each stood in its own corner, so on a narrow
42
+ stage (the 1024 width, both side panels open) the controls lay over the help's button. One row that wraps keeps both
43
+ whole: side by side when there is room, the controls above the help when there is not.
44
+ */
45
+ div[foot] {
46
+ position: absolute;
47
+ left: 8px;
48
+ right: 8px;
49
+ bottom: 8px;
50
+ display: flex;
51
+ flex-wrap: wrap-reverse;
52
+ justify-content: space-between;
53
+ align-items: flex-end;
54
+ gap: 8px;
55
+ pointer-events: none;
56
+ }
57
+ div[foot] > * { pointer-events: auto; }
58
+ div[setup] { max-width: min(320px, 100%); padding: 12px; border-radius: 8px; background: rgba(28,28,30,.9); color: #fff; display: grid; gap: 8px; }
39
59
  div[setup] p { margin: 0; line-height: 1.5; }
40
60
  div[setup] select, div[setup] button { font: inherit; padding: 8px; min-width: 0; }
41
61
  /* Which is which: side by side means nothing without a legend. */
@@ -59,9 +79,7 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
59
79
  color: #ffcc80;
60
80
  }
61
81
  div[clips] {
62
- position: absolute;
63
- right: 8px;
64
- bottom: 8px;
82
+ margin-left: auto;
65
83
  display: flex;
66
84
  flex-direction: column;
67
85
  gap: 6px;
@@ -150,6 +168,8 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
150
168
  catch {
151
169
  clampsByWay = {};
152
170
  }
171
+ /* The part the help speaks of: the one picked in it, else the one chosen in the editor, else the first. */
172
+ const setupPart = setupPartOf(this.setupPart, this.selected, asset?.appearance.map(p => p.target) ?? []);
153
173
  return html `<div stage></div>
154
174
  ${this.cannot
155
175
  ? html `<div cannot>${this.cannot}</div>`
@@ -164,14 +184,14 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
164
184
  </span>`;
165
185
  })}
166
186
  </div>
167
- ${asset?.appearance.length ? html `<div setup>
187
+ <div foot>${asset?.appearance.length ? html `<div setup>
168
188
  <p>${i18next.t('figure.text.preview-setup')}</p>
169
189
  <select aria-label=${i18next.t('figure.label.resize-part')} @change=${(e) => this.setupPart = e.target.value}>
170
- ${asset.appearance.map(p => html `<option value=${p.target} ?selected=${p.target === this.setupPart}>${p.target}</option>`)}
190
+ ${asset.appearance.map(p => html `<option value=${p.target} ?selected=${p.target === setupPart}>${p.target}</option>`)}
171
191
  </select>
172
- <button @click=${() => this.dispatchEvent(new CustomEvent('configure-size', { detail: { part: asset.appearance.some(p => p.target === this.setupPart) ? this.setupPart : asset.appearance[0].target }, bubbles: true, composed: true }))}>${i18next.t('figure.button.configure-size')}</button>
192
+ <button @click=${() => this.dispatchEvent(new CustomEvent('configure-size', { detail: { part: setupPart }, bubbles: true, composed: true }))}>${i18next.t('figure.button.configure-size')}</button>
173
193
  </div>` : nothing}
174
- ${controls.length + clips.length ? this.renderDrivers(asset, controls, clips) : nothing}`}`;
194
+ ${controls.length + clips.length ? this.renderDrivers(asset, controls, clips) : nothing}</div>`}`;
175
195
  }
176
196
  renderDrivers(asset, controls, clips) {
177
197
  const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id]);
@@ -200,7 +220,11 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
200
220
  </label>`;
201
221
  })}
202
222
  ${clips.map(clip => {
203
- const value = this.driving[clip] ?? 1;
223
+ /*
224
+ A clip nothing has driven stands still, here as on a board: the instances get a speed only from `drive`. The
225
+ slider showed 1.00 over rollers that did not turn (2026-10-03). It shows what plays -- 0 until moved.
226
+ */
227
+ const value = this.driving[clip] ?? 0;
204
228
  return html `<label>
205
229
  <span>${clip} <span drive>${i18next.t('figure.label.playback-speed')}</span></span>
206
230
  <span></span>
@@ -356,6 +380,10 @@ __decorate([
356
380
  state(),
357
381
  __metadata("design:type", Object)
358
382
  ], FigurePreview.prototype, "giving", void 0);
383
+ __decorate([
384
+ property(),
385
+ __metadata("design:type", Object)
386
+ ], FigurePreview.prototype, "selected", void 0);
359
387
  __decorate([
360
388
  state(),
361
389
  __metadata("design:type", Object)
@@ -1 +1 @@
1
- {"version":3,"file":"figure-preview.js","sourceRoot":"","sources":["../../client/modeller/figure-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, unsafeCSS } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { compileFigureAsset } from '@hatiolab/figure-model'\nimport type { FigureAsset } from '@hatiolab/figure-model'\nimport { create, registerFigure, unregisterFigure } from '@hatiolab/things-scene'\n\nimport { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH, type ViewSettings } from './figure-view.js'\nimport { dialShape } from './angle-dial.js'\nimport { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances } from './preview-model.js'\n\n/**\n * The figure placed on a board.\n *\n * The editing canvas shows the figure at the size it was made. Half of what a figure is lies in how it answers a\n * different size, so here the figure being authored is registered as a board registers it and placed as\n * `FigureInstance`s: the size it was made, and each of the seven ways of growing one, two or three axes twice as\n * large, side by side at one scale, four to a row. The board draws them as it will on a real board --\n * placement, lights, environment, the same renderer.\n *\n * The controls: each value that poses the figure with a slider (and a dial for an angle), each clip's\n * speed, and 「이 자세를 기본으로」 to keep the pose as the one it stands in. Nothing here is edited; the canvas edits.\n */\n\ninterface SceneHandle {\n dispose?: () => void\n resize?: () => void\n fit?: (type?: string) => void\n root?: {\n set?: (patch: Record<string, unknown>) => void\n fitCameraToModel?: () => void\n components?: { set?: (property: string, value: unknown) => void }[]\n }\n}\n\n@customElement('figure-preview')\nexport class FigurePreview extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background-color: ${unsafeCSS(WORKBENCH)};\n font-size: 0.76rem;\n }\n div[stage] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n }\n div[setup] { position: absolute; bottom: 8px; left: 8px; max-width: min(320px, calc(100% - 16px)); padding: 12px; border-radius: 8px; background: rgba(28,28,30,.9); color: #fff; display: grid; gap: 8px; }\n div[setup] p { margin: 0; line-height: 1.5; }\n div[setup] select, div[setup] button { font: inherit; padding: 8px; min-width: 0; }\n /* Which is which: side by side means nothing without a legend. */\n div[legend] {\n position: absolute;\n top: 8px;\n left: 8px;\n display: flex;\n gap: 6px;\n padding: 4px 8px;\n color: rgba(255, 255, 255, 0.9);\n background-color: rgba(28, 28, 30, 0.6);\n border-radius: 6px;\n font-size: 0.72rem;\n flex-wrap: wrap;\n max-width: calc(100% - 16px);\n }\n /* A variant drawn at another size than it asks: the figure does not declare that size. */\n div[legend] small[clamp] {\n margin-left: 4px;\n color: #ffcc80;\n }\n div[clips] {\n position: absolute;\n right: 8px;\n bottom: 8px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(28, 28, 30, 0.72);\n border-radius: 8px;\n font-size: 0.72rem;\n }\n div[clips] label {\n display: grid;\n grid-template-columns: auto 20px 96px 34px;\n align-items: center;\n gap: 8px;\n }\n div[clips] svg[dial] {\n width: 20px;\n height: 20px;\n }\n div[clips] svg[dial] path {\n fill: rgba(255, 255, 255, 0.22);\n }\n div[clips] svg[dial] line {\n stroke: var(--md-sys-color-primary, #7cc6ff);\n stroke-width: 2;\n stroke-linecap: round;\n }\n div[clips] svg[dial] circle {\n fill: none;\n stroke: rgba(255, 255, 255, 0.35);\n }\n div[clips] button[pose] {\n align-self: stretch;\n padding: 4px 8px;\n font: inherit;\n font-size: 0.7rem;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(255, 255, 255, 0.14);\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: 5px;\n cursor: pointer;\n }\n div[clips] button[pose]:hover {\n background-color: rgba(255, 255, 255, 0.24);\n }\n div[clips] span[drive] {\n opacity: 0.6;\n font-size: 0.66rem;\n }\n div[clips] input {\n width: 96px;\n accent-color: var(--md-sys-color-primary);\n }\n div[clips] output {\n font-variant-numeric: tabular-nums;\n text-align: right;\n opacity: 0.85;\n }\n div[cannot] {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n text-align: center;\n line-height: 1.6;\n color: #f4f4f4;\n background-color: ${unsafeCSS(WORKBENCH)};\n }\n `\n\n /** The figure being authored, as the editor holds it now. */\n @property({ attribute: false }) asset?: FigureAsset\n /** Which figure: the type it is registered under here is this one's own, never a real type a board uses. */\n @property() context = ''\n @property({ attribute: false }) view: ViewSettings = DEFAULT_VIEW\n\n @query('div[stage]') private stage!: HTMLDivElement\n @state() private cannot = ''\n /** Clip speeds and control values given here -- the preview's, not the figure's. */\n @state() private driving: Record<string, number> = {}\n @state() private giving: Record<string, number> = {}\n @state() private setupPart = ''\n\n /** What is being tried here now -- clip speeds and control values -- for the AI dock to hear with a request; none when nothing is. */\n tried(): { clips?: Record<string, number>; controls?: Record<string, number> } | undefined {\n const clips = Object.keys(this.driving).length ? { ...this.driving } : undefined\n const controls = Object.keys(this.giving).length ? { ...this.giving } : undefined\n return clips || controls ? { ...(clips ? { clips } : {}), ...(controls ? { controls } : {}) } : undefined\n }\n\n private scene?: SceneHandle\n private standing?: string\n private registered?: string\n private watcher?: ResizeObserver\n\n render() {\n const asset = this.asset\n const controls = asset ? asset.document.model.inputs.filter(i => i.role === 'state') : []\n const clips = asset ? [...new Set((asset.drivers ?? []).map(d => d.clip))] : []\n let clampsByWay: ReturnType<typeof previewClampsOf> = {}\n try {\n clampsByWay = asset ? previewClampsOf(asset, authoredSizeOf(asset)) : {}\n } catch {\n clampsByWay = {}\n }\n return html`<div stage></div>\n ${this.cannot\n ? html`<div cannot>${this.cannot}</div>`\n : html`<div legend>\n ${PREVIEW_WAYS.map(way => {\n const clamps = clampsByWay[way.name] ?? []\n return html`<span>\n ${way.axes.length ? way.name : i18next.t('figure.label.preview-base')}\n ${clamps.length\n ? html`<small clamp>${clamps.map(c => i18next.t('figure.text.preview-clamped', { axis: c.axis.toUpperCase(), asked: c.asked.toLocaleString(), used: c.used.toLocaleString() })).join(' · ')}</small>`\n : nothing}\n </span>`\n })}\n </div>\n ${asset?.appearance.length ? html`<div setup>\n <p>${i18next.t('figure.text.preview-setup')}</p>\n <select aria-label=${i18next.t('figure.label.resize-part')} @change=${(e: Event) => this.setupPart = (e.target as HTMLSelectElement).value}>\n ${asset.appearance.map(p => html`<option value=${p.target} ?selected=${p.target === this.setupPart}>${p.target}</option>`)}\n </select>\n <button @click=${() => this.dispatchEvent(new CustomEvent('configure-size', { detail: { part: asset.appearance.some(p => p.target === this.setupPart) ? this.setupPart : asset.appearance[0]!.target }, bubbles: true, composed: true }))}>${i18next.t('figure.button.configure-size')}</button>\n </div>` : nothing}\n ${controls.length + clips.length ? this.renderDrivers(asset!, controls, clips) : nothing}`}`\n }\n\n private renderDrivers(asset: FigureAsset, controls: FigureAsset['document']['model']['inputs'], clips: string[]) {\n const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id])\n return html`<div clips>\n ${changed.length\n ? html`<button pose title=${i18next.t('figure.text.pose-as-default-explained')} @click=${() => this.poseAsDefault()}>\n ${i18next.t('figure.button.pose-as-default')}\n </button>`\n : nothing}\n ${controls.map(control => {\n const value = this.giving[control.id] ?? asset.stateDefaults[control.id] ?? control.min\n const step = Math.max((control.max - control.min) / 100, 0.01)\n const label = asset.stateInputs?.[control.id]?.label ?? control.id\n return html`<label>\n <span>${label} <span drive>${control.unit}</span></span>\n ${this.renderDial(control, value)}\n <input\n type=\"range\"\n min=${control.min}\n max=${control.max}\n step=${step}\n .value=${String(value)}\n @input=${(e: Event) => this.give(control.id, Number((e.target as HTMLInputElement).value))}\n />\n <output>${Math.round(value)}</output>\n </label>`\n })}\n ${clips.map(clip => {\n const value = this.driving[clip] ?? 1\n return html`<label>\n <span>${clip} <span drive>${i18next.t('figure.label.playback-speed')}</span></span>\n <span></span>\n <input type=\"range\" min=\"0\" max=\"3\" step=\"0.05\" .value=${String(value)} @input=${(e: Event) => this.drive(clip, Number((e.target as HTMLInputElement).value))} />\n <output>${value.toFixed(2)}</output>\n </label>`\n })}\n </div>`\n }\n\n private renderDial(control: { unit: string; min: number; max: number }, value: number) {\n if (control.unit !== 'deg') return html`<span></span>`\n const dial = dialShape(control.min, control.max, value)\n const span = dial.radius * 2 + 2\n return html`<svg dial viewBox=\"0 0 ${span} ${span}\" aria-hidden=\"true\">\n <path d=${dial.sector}></path>\n <circle cx=${dial.centre.x} cy=${dial.centre.y} r=${dial.radius}></circle>\n <line x1=${dial.centre.x} y1=${dial.centre.y} x2=${dial.needle.x} y2=${dial.needle.y}></line>\n </svg>`\n }\n\n private drive(clip: string, value: number) {\n this.driving = { ...this.driving, [clip]: value }\n this.tellInstances('clips', this.driving)\n }\n\n private give(id: string, value: number) {\n this.giving = { ...this.giving, [id]: value }\n this.tellInstances('parameters', this.giving)\n }\n\n private poseAsDefault() {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit: { kind: 'set-state-defaults', values: { ...this.giving } } }, bubbles: true, composed: true }))\n this.giving = {}\n }\n\n private tellInstances(key: 'clips' | 'parameters', values: Record<string, number>) {\n for (const component of this.scene?.root?.components ?? []) component.set?.(key, { ...values })\n }\n\n willUpdate(changed: Map<string, unknown>) {\n if (!this.hasUpdated) return\n if (changed.has('asset') || changed.has('context')) {\n this.standUp()\n return\n }\n if (!changed.has('view')) return\n const before = changed.get('view') as ViewSettings | undefined\n const now = this.view ?? DEFAULT_VIEW\n if (before?.placement !== now.placement || before?.ceilingHeight !== now.ceilingHeight) this.standUp()\n else this.scene?.root?.set?.(sceneLook(now))\n }\n\n firstUpdated() {\n this.standUp()\n this.watcher = new ResizeObserver(() => this.scene?.resize?.())\n this.watcher.observe(this)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.watcher?.disconnect()\n this.watcher = undefined\n this.teardown()\n }\n\n private standUp() {\n const asset = this.asset\n const view = this.view ?? DEFAULT_VIEW\n if (!asset || !asset.appearance.length) {\n this.cannot = i18next.t('figure.text.preview-needs-a-part')\n this.teardown()\n return\n }\n const signature = JSON.stringify([asset, view.placement, view.ceilingHeight])\n if (this.scene && this.standing === signature) return\n try {\n compileFigureAsset(asset)\n } catch {\n this.cannot = i18next.t('figure.text.preview-blocked-by-errors')\n this.teardown()\n return\n }\n this.teardown()\n this.driving = {}\n try {\n /* Registered as a board registers it, under a type of this preview's own: a real type's drawings stay as they are. */\n const type = `figure-preview-${this.context || 'new'}`\n registerFigure(type, asset)\n this.registered = type\n const size = authoredSizeOf(asset)\n const { components, board } = previewInstances(type, size)\n this.scene = create({\n target: this.stage,\n model: {\n type: 'model-layer',\n width: board.width,\n height: board.height,\n depth: previewBoardDepth({ placement: asset.placement, base: { y: size.y } }, view.ceilingHeight, PREVIEW_FACTOR),\n threed: true,\n sky: 'color',\n ...sceneLook(view),\n placement: view.placement,\n heights: { floor: 0, ceiling: view.ceilingHeight },\n pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),\n components\n },\n mode: 0\n }) as SceneHandle\n this.standing = signature\n this.scene.resize?.()\n this.scene.fit?.('ratio')\n this.scene.root?.fitCameraToModel?.()\n this.cannot = ''\n } catch (error) {\n this.cannot = (error as Error).message\n }\n }\n\n private teardown() {\n this.scene?.dispose?.()\n this.scene = undefined\n this.standing = undefined\n if (this.registered) unregisterFigure(this.registered)\n this.registered = undefined\n }\n}\n"]}
1
+ {"version":3,"file":"figure-preview.js","sourceRoot":"","sources":["../../client/modeller/figure-preview.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAE3D,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEjF,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAqB,MAAM,kBAAkB,CAAA;AAC3G,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AA2B1H,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAAzD;;QAsIL,4GAA4G;QAChG,YAAO,GAAG,EAAE,CAAA;QACQ,SAAI,GAAiB,YAAY,CAAA;QAGhD,WAAM,GAAG,EAAE,CAAA;QAC5B,oFAAoF;QACnE,YAAO,GAA2B,EAAE,CAAA;QACpC,WAAM,GAA2B,EAAE,CAAA;QACpD,2HAA2H;QAC/G,aAAQ,GAAG,EAAE,CAAA;QACR,cAAS,GAAG,EAAE,CAAA;IAgNjC,CAAC;aAhWQ,WAAM,GAAG,GAAG,CAAA;;;;;;;0BAOK,SAAS,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAwHpB,SAAS,CAAC,SAAS,CAAC;;GAE3C,AAjIY,CAiIZ;IAiBD,sIAAsI;IACtI,KAAK;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;QAChF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;QACjF,OAAO,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IAC3G,CAAC;IAOD,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACzF,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAC/E,IAAI,WAAW,GAAuC,EAAE,CAAA;QACxD,IAAI,CAAC;YACH,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,WAAW,GAAG,EAAE,CAAA;QAClB,CAAC;QACD,2GAA2G;QAC3G,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;QACxG,OAAO,IAAI,CAAA;QACP,IAAI,CAAC,MAAM;YACX,CAAC,CAAC,IAAI,CAAA,eAAe,IAAI,CAAC,MAAM,QAAQ;YACxC,CAAC,CAAC,IAAI,CAAA;gBACE,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;gBACvB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;gBAC1C,OAAO,IAAI,CAAA;oBACP,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC;oBACnE,MAAM,CAAC,MAAM;oBACb,CAAC,CAAC,IAAI,CAAA,gBAAgB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU;oBACrM,CAAC,CAAC,OAAO;wBACL,CAAA;YACV,CAAC,CAAC;;wBAEQ,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;mBACpC,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC;mCACtB,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,YAAY,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,GAAI,CAAC,CAAC,MAA4B,CAAC,KAAK;kBACtI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAA,iBAAiB,CAAC,CAAC,MAAM,cAAc,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,WAAW,CAAC;;+BAEtG,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,gBAAgB,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;mBACpL,CAAC,CAAC,CAAC,OAAO;cACf,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,KAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,QAAQ,EAAE,CAAA;IAC5G,CAAC;IAEO,aAAa,CAAC,KAAkB,EAAE,QAAoD,EAAE,KAAe;QAC7G,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACxH,OAAO,IAAI,CAAA;QACP,OAAO,CAAC,MAAM;YACd,CAAC,CAAC,IAAI,CAAA,sBAAsB,OAAO,CAAC,CAAC,CAAC,uCAAuC,CAAC,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE;cAC7G,OAAO,CAAC,CAAC,CAAC,+BAA+B,CAAC;oBACpC;YACZ,CAAC,CAAC,OAAO;QACT,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;YACvB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,GAAG,CAAA;YACvF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,CAAA;YAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,OAAO,CAAC,EAAE,CAAA;YAClE,OAAO,IAAI,CAAA;kBACD,KAAK,gBAAgB,OAAO,CAAC,IAAI;YACvC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC;;;kBAGzB,OAAO,CAAC,GAAG;kBACX,OAAO,CAAC,GAAG;mBACV,IAAI;qBACF,MAAM,CAAC,KAAK,CAAC;qBACb,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAC;;oBAElF,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;iBACpB,CAAA;QACX,CAAC,CAAC;QACA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACjB;;;cAGE;YACF,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACrC,OAAO,IAAI,CAAA;kBACD,IAAI,gBAAgB,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;;mEAEX,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAC;oBACnJ,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;iBACnB,CAAA;QACX,CAAC,CAAC;WACG,CAAA;IACT,CAAC;IAEO,UAAU,CAAC,OAAmD,EAAE,KAAa;QACnF,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,CAAA,eAAe,CAAA;QACtD,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAA;QAChC,OAAO,IAAI,CAAA,0BAA0B,IAAI,IAAI,IAAI;gBACrC,IAAI,CAAC,MAAM;mBACR,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM;iBACpD,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;WAC/E,CAAA;IACT,CAAC;IAEO,KAAK,CAAC,IAAY,EAAE,KAAa;QACvC,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAA;QACjD,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAC3C,CAAC;IAEO,IAAI,CAAC,EAAU,EAAE,KAAa;QACpC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAA;QAC7C,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC/C,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QACjK,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;IAClB,CAAC;IAEO,aAAa,CAAC,GAA2B,EAAE,MAA8B;QAC/E,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,IAAI,EAAE;YAAE,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;IACjG,CAAC;IAED,UAAU,CAAC,OAA6B;QACtC,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAM;QAC5B,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,OAAO,EAAE,CAAA;YACd,OAAM;QACR,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAM;QAChC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAA6B,CAAA;QAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,YAAY,CAAA;QACrC,IAAI,MAAM,EAAE,SAAS,KAAK,GAAG,CAAC,SAAS,IAAI,MAAM,EAAE,aAAa,KAAK,GAAG,CAAC,aAAa;YAAE,IAAI,CAAC,OAAO,EAAE,CAAA;;YACjG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IAC9C,CAAC;IAED,YAAY;QACV,IAAI,CAAC,OAAO,EAAE,CAAA;QACd,IAAI,CAAC,OAAO,GAAG,IAAI,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;QAC/D,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,oBAAoB;QAClB,KAAK,CAAC,oBAAoB,EAAE,CAAA;QAC5B,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAA;QAC1B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAA;QACxB,IAAI,CAAC,QAAQ,EAAE,CAAA;IACjB,CAAC;IAEO,OAAO;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,YAAY,CAAA;QACtC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YACvC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAA;YAC3D,IAAI,CAAC,QAAQ,EAAE,CAAA;YACf,OAAM;QACR,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAA;QAC7E,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAM;QACrD,IAAI,CAAC;YACH,kBAAkB,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,uCAAuC,CAAC,CAAA;YAChE,IAAI,CAAC,QAAQ,EAAE,CAAA;YACf,OAAM;QACR,CAAC;QACD,IAAI,CAAC,QAAQ,EAAE,CAAA;QACf,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;QACjB,IAAI,CAAC;YACH,sHAAsH;YACtH,MAAM,IAAI,GAAG,kBAAkB,IAAI,CAAC,OAAO,IAAI,KAAK,EAAE,CAAA;YACtD,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YAC3B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;YACtB,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;YAClC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YAC1D,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;gBAClB,MAAM,EAAE,IAAI,CAAC,KAAK;gBAClB,KAAK,EAAE;oBACL,IAAI,EAAE,aAAa;oBACnB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,KAAK,EAAE,iBAAiB,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC;oBACjH,MAAM,EAAE,IAAI;oBACZ,GAAG,EAAE,OAAO;oBACZ,GAAG,SAAS,CAAC,IAAI,CAAC;oBAClB,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE;oBAClD,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;oBAC3D,UAAU;iBACX;gBACD,IAAI,EAAE,CAAC;aACR,CAAgB,CAAA;YACjB,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAA;YACzB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAA;YACrB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAA;YACzB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,CAAA;YACrC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,GAAI,KAAe,CAAC,OAAO,CAAA;QACxC,CAAC;IACH,CAAC;IAEO,QAAQ;QACd,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAA;QACvB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAA;QACzB,IAAI,IAAI,CAAC,UAAU;YAAE,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACtD,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;IAC7B,CAAC;;AA3N+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;4CAAoB;AAEvC;IAAX,QAAQ,EAAE;;8CAAa;AACQ;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;2CAAkC;AAEpC;IAA5B,KAAK,CAAC,YAAY,CAAC;8BAAiB,cAAc;4CAAA;AAClC;IAAhB,KAAK,EAAE;;6CAAoB;AAEX;IAAhB,KAAK,EAAE;;8CAA6C;AACpC;IAAhB,KAAK,EAAE;;6CAA4C;AAExC;IAAX,QAAQ,EAAE;;+CAAc;AACR;IAAhB,KAAK,EAAE;;gDAAuB;AAjJpB,aAAa;IADzB,aAAa,CAAC,gBAAgB,CAAC;GACnB,aAAa,CAiWzB","sourcesContent":["import { css, html, LitElement, nothing, unsafeCSS } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { compileFigureAsset } from '@hatiolab/figure-model'\nimport type { FigureAsset } from '@hatiolab/figure-model'\nimport { create, registerFigure, unregisterFigure } from '@hatiolab/things-scene'\n\nimport { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH, type ViewSettings } from './figure-view.js'\nimport { dialShape } from './angle-dial.js'\nimport { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, setupPartOf } from './preview-model.js'\n\n/**\n * The figure placed on a board.\n *\n * The editing canvas shows the figure at the size it was made. Half of what a figure is lies in how it answers a\n * different size, so here the figure being authored is registered as a board registers it and placed as\n * `FigureInstance`s: the size it was made, and each of the seven ways of growing one, two or three axes twice as\n * large, side by side at one scale, four to a row. The board draws them as it will on a real board --\n * placement, lights, environment, the same renderer.\n *\n * The controls: each value that poses the figure with a slider (and a dial for an angle), each clip's\n * speed, and 「이 자세를 기본으로」 to keep the pose as the one it stands in. Nothing here is edited; the canvas edits.\n */\n\ninterface SceneHandle {\n dispose?: () => void\n resize?: () => void\n fit?: (type?: string) => void\n root?: {\n set?: (patch: Record<string, unknown>) => void\n fitCameraToModel?: () => void\n components?: { set?: (property: string, value: unknown) => void }[]\n }\n}\n\n@customElement('figure-preview')\nexport class FigurePreview extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background-color: ${unsafeCSS(WORKBENCH)};\n font-size: 0.76rem;\n }\n div[stage] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n }\n /*\n The size help and the posing controls share the stage's bottom edge. Each stood in its own corner, so on a narrow\n stage (the 1024 width, both side panels open) the controls lay over the help's button. One row that wraps keeps both\n whole: side by side when there is room, the controls above the help when there is not.\n */\n div[foot] {\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: 8px;\n display: flex;\n flex-wrap: wrap-reverse;\n justify-content: space-between;\n align-items: flex-end;\n gap: 8px;\n pointer-events: none;\n }\n div[foot] > * { pointer-events: auto; }\n div[setup] { max-width: min(320px, 100%); padding: 12px; border-radius: 8px; background: rgba(28,28,30,.9); color: #fff; display: grid; gap: 8px; }\n div[setup] p { margin: 0; line-height: 1.5; }\n div[setup] select, div[setup] button { font: inherit; padding: 8px; min-width: 0; }\n /* Which is which: side by side means nothing without a legend. */\n div[legend] {\n position: absolute;\n top: 8px;\n left: 8px;\n display: flex;\n gap: 6px;\n padding: 4px 8px;\n color: rgba(255, 255, 255, 0.9);\n background-color: rgba(28, 28, 30, 0.6);\n border-radius: 6px;\n font-size: 0.72rem;\n flex-wrap: wrap;\n max-width: calc(100% - 16px);\n }\n /* A variant drawn at another size than it asks: the figure does not declare that size. */\n div[legend] small[clamp] {\n margin-left: 4px;\n color: #ffcc80;\n }\n div[clips] {\n margin-left: auto;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(28, 28, 30, 0.72);\n border-radius: 8px;\n font-size: 0.72rem;\n }\n div[clips] label {\n display: grid;\n grid-template-columns: auto 20px 96px 34px;\n align-items: center;\n gap: 8px;\n }\n div[clips] svg[dial] {\n width: 20px;\n height: 20px;\n }\n div[clips] svg[dial] path {\n fill: rgba(255, 255, 255, 0.22);\n }\n div[clips] svg[dial] line {\n stroke: var(--md-sys-color-primary, #7cc6ff);\n stroke-width: 2;\n stroke-linecap: round;\n }\n div[clips] svg[dial] circle {\n fill: none;\n stroke: rgba(255, 255, 255, 0.35);\n }\n div[clips] button[pose] {\n align-self: stretch;\n padding: 4px 8px;\n font: inherit;\n font-size: 0.7rem;\n color: rgba(255, 255, 255, 0.92);\n background-color: rgba(255, 255, 255, 0.14);\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: 5px;\n cursor: pointer;\n }\n div[clips] button[pose]:hover {\n background-color: rgba(255, 255, 255, 0.24);\n }\n div[clips] span[drive] {\n opacity: 0.6;\n font-size: 0.66rem;\n }\n div[clips] input {\n width: 96px;\n accent-color: var(--md-sys-color-primary);\n }\n div[clips] output {\n font-variant-numeric: tabular-nums;\n text-align: right;\n opacity: 0.85;\n }\n div[cannot] {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n text-align: center;\n line-height: 1.6;\n color: #f4f4f4;\n background-color: ${unsafeCSS(WORKBENCH)};\n }\n `\n\n /** The figure being authored, as the editor holds it now. */\n @property({ attribute: false }) asset?: FigureAsset\n /** Which figure: the type it is registered under here is this one's own, never a real type a board uses. */\n @property() context = ''\n @property({ attribute: false }) view: ViewSettings = DEFAULT_VIEW\n\n @query('div[stage]') private stage!: HTMLDivElement\n @state() private cannot = ''\n /** Clip speeds and control values given here -- the preview's, not the figure's. */\n @state() private driving: Record<string, number> = {}\n @state() private giving: Record<string, number> = {}\n /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */\n @property() selected = ''\n @state() private setupPart = ''\n\n /** What is being tried here now -- clip speeds and control values -- for the AI dock to hear with a request; none when nothing is. */\n tried(): { clips?: Record<string, number>; controls?: Record<string, number> } | undefined {\n const clips = Object.keys(this.driving).length ? { ...this.driving } : undefined\n const controls = Object.keys(this.giving).length ? { ...this.giving } : undefined\n return clips || controls ? { ...(clips ? { clips } : {}), ...(controls ? { controls } : {}) } : undefined\n }\n\n private scene?: SceneHandle\n private standing?: string\n private registered?: string\n private watcher?: ResizeObserver\n\n render() {\n const asset = this.asset\n const controls = asset ? asset.document.model.inputs.filter(i => i.role === 'state') : []\n const clips = asset ? [...new Set((asset.drivers ?? []).map(d => d.clip))] : []\n let clampsByWay: ReturnType<typeof previewClampsOf> = {}\n try {\n clampsByWay = asset ? previewClampsOf(asset, authoredSizeOf(asset)) : {}\n } catch {\n clampsByWay = {}\n }\n /* The part the help speaks of: the one picked in it, else the one chosen in the editor, else the first. */\n const setupPart = setupPartOf(this.setupPart, this.selected, asset?.appearance.map(p => p.target) ?? [])\n return html`<div stage></div>\n ${this.cannot\n ? html`<div cannot>${this.cannot}</div>`\n : html`<div legend>\n ${PREVIEW_WAYS.map(way => {\n const clamps = clampsByWay[way.name] ?? []\n return html`<span>\n ${way.axes.length ? way.name : i18next.t('figure.label.preview-base')}\n ${clamps.length\n ? html`<small clamp>${clamps.map(c => i18next.t('figure.text.preview-clamped', { axis: c.axis.toUpperCase(), asked: c.asked.toLocaleString(), used: c.used.toLocaleString() })).join(' · ')}</small>`\n : nothing}\n </span>`\n })}\n </div>\n <div foot>${asset?.appearance.length ? html`<div setup>\n <p>${i18next.t('figure.text.preview-setup')}</p>\n <select aria-label=${i18next.t('figure.label.resize-part')} @change=${(e: Event) => this.setupPart = (e.target as HTMLSelectElement).value}>\n ${asset.appearance.map(p => html`<option value=${p.target} ?selected=${p.target === setupPart}>${p.target}</option>`)}\n </select>\n <button @click=${() => this.dispatchEvent(new CustomEvent('configure-size', { detail: { part: setupPart }, bubbles: true, composed: true }))}>${i18next.t('figure.button.configure-size')}</button>\n </div>` : nothing}\n ${controls.length + clips.length ? this.renderDrivers(asset!, controls, clips) : nothing}</div>`}`\n }\n\n private renderDrivers(asset: FigureAsset, controls: FigureAsset['document']['model']['inputs'], clips: string[]) {\n const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id])\n return html`<div clips>\n ${changed.length\n ? html`<button pose title=${i18next.t('figure.text.pose-as-default-explained')} @click=${() => this.poseAsDefault()}>\n ${i18next.t('figure.button.pose-as-default')}\n </button>`\n : nothing}\n ${controls.map(control => {\n const value = this.giving[control.id] ?? asset.stateDefaults[control.id] ?? control.min\n const step = Math.max((control.max - control.min) / 100, 0.01)\n const label = asset.stateInputs?.[control.id]?.label ?? control.id\n return html`<label>\n <span>${label} <span drive>${control.unit}</span></span>\n ${this.renderDial(control, value)}\n <input\n type=\"range\"\n min=${control.min}\n max=${control.max}\n step=${step}\n .value=${String(value)}\n @input=${(e: Event) => this.give(control.id, Number((e.target as HTMLInputElement).value))}\n />\n <output>${Math.round(value)}</output>\n </label>`\n })}\n ${clips.map(clip => {\n /*\n A clip nothing has driven stands still, here as on a board: the instances get a speed only from `drive`. The\n slider showed 1.00 over rollers that did not turn (2026-10-03). It shows what plays -- 0 until moved.\n */\n const value = this.driving[clip] ?? 0\n return html`<label>\n <span>${clip} <span drive>${i18next.t('figure.label.playback-speed')}</span></span>\n <span></span>\n <input type=\"range\" min=\"0\" max=\"3\" step=\"0.05\" .value=${String(value)} @input=${(e: Event) => this.drive(clip, Number((e.target as HTMLInputElement).value))} />\n <output>${value.toFixed(2)}</output>\n </label>`\n })}\n </div>`\n }\n\n private renderDial(control: { unit: string; min: number; max: number }, value: number) {\n if (control.unit !== 'deg') return html`<span></span>`\n const dial = dialShape(control.min, control.max, value)\n const span = dial.radius * 2 + 2\n return html`<svg dial viewBox=\"0 0 ${span} ${span}\" aria-hidden=\"true\">\n <path d=${dial.sector}></path>\n <circle cx=${dial.centre.x} cy=${dial.centre.y} r=${dial.radius}></circle>\n <line x1=${dial.centre.x} y1=${dial.centre.y} x2=${dial.needle.x} y2=${dial.needle.y}></line>\n </svg>`\n }\n\n private drive(clip: string, value: number) {\n this.driving = { ...this.driving, [clip]: value }\n this.tellInstances('clips', this.driving)\n }\n\n private give(id: string, value: number) {\n this.giving = { ...this.giving, [id]: value }\n this.tellInstances('parameters', this.giving)\n }\n\n private poseAsDefault() {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit: { kind: 'set-state-defaults', values: { ...this.giving } } }, bubbles: true, composed: true }))\n this.giving = {}\n }\n\n private tellInstances(key: 'clips' | 'parameters', values: Record<string, number>) {\n for (const component of this.scene?.root?.components ?? []) component.set?.(key, { ...values })\n }\n\n willUpdate(changed: Map<string, unknown>) {\n if (!this.hasUpdated) return\n if (changed.has('asset') || changed.has('context')) {\n this.standUp()\n return\n }\n if (!changed.has('view')) return\n const before = changed.get('view') as ViewSettings | undefined\n const now = this.view ?? DEFAULT_VIEW\n if (before?.placement !== now.placement || before?.ceilingHeight !== now.ceilingHeight) this.standUp()\n else this.scene?.root?.set?.(sceneLook(now))\n }\n\n firstUpdated() {\n this.standUp()\n this.watcher = new ResizeObserver(() => this.scene?.resize?.())\n this.watcher.observe(this)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.watcher?.disconnect()\n this.watcher = undefined\n this.teardown()\n }\n\n private standUp() {\n const asset = this.asset\n const view = this.view ?? DEFAULT_VIEW\n if (!asset || !asset.appearance.length) {\n this.cannot = i18next.t('figure.text.preview-needs-a-part')\n this.teardown()\n return\n }\n const signature = JSON.stringify([asset, view.placement, view.ceilingHeight])\n if (this.scene && this.standing === signature) return\n try {\n compileFigureAsset(asset)\n } catch {\n this.cannot = i18next.t('figure.text.preview-blocked-by-errors')\n this.teardown()\n return\n }\n this.teardown()\n this.driving = {}\n try {\n /* Registered as a board registers it, under a type of this preview's own: a real type's drawings stay as they are. */\n const type = `figure-preview-${this.context || 'new'}`\n registerFigure(type, asset)\n this.registered = type\n const size = authoredSizeOf(asset)\n const { components, board } = previewInstances(type, size)\n this.scene = create({\n target: this.stage,\n model: {\n type: 'model-layer',\n width: board.width,\n height: board.height,\n depth: previewBoardDepth({ placement: asset.placement, base: { y: size.y } }, view.ceilingHeight, PREVIEW_FACTOR),\n threed: true,\n sky: 'color',\n ...sceneLook(view),\n placement: view.placement,\n heights: { floor: 0, ceiling: view.ceilingHeight },\n pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),\n components\n },\n mode: 0\n }) as SceneHandle\n this.standing = signature\n this.scene.resize?.()\n this.scene.fit?.('ratio')\n this.scene.root?.fitCameraToModel?.()\n this.cannot = ''\n } catch (error) {\n this.cannot = (error as Error).message\n }\n }\n\n private teardown() {\n this.scene?.dispose?.()\n this.scene = undefined\n this.standing = undefined\n if (this.registered) unregisterFigure(this.registered)\n this.registered = undefined\n }\n}\n"]}
@@ -45,3 +45,8 @@ export declare function previewClampsOf(asset: FigureAsset, size: {
45
45
  asked: number;
46
46
  used: number;
47
47
  }>>;
48
+ /**
49
+ * The part the preview's size help speaks of: the one picked in the help, else the one chosen in the editor, else the
50
+ * first that has a size rule. A choice that is not among the parts (removed, or a part with no rule) is passed over.
51
+ */
52
+ export declare function setupPartOf(picked: string, chosen: string, targets: string[]): string;
@@ -65,4 +65,11 @@ export function previewClampsOf(asset, size) {
65
65
  })
66
66
  ]));
67
67
  }
68
+ /**
69
+ * The part the preview's size help speaks of: the one picked in the help, else the one chosen in the editor, else the
70
+ * first that has a size rule. A choice that is not among the parts (removed, or a part with no rule) is passed over.
71
+ */
72
+ export function setupPartOf(picked, chosen, targets) {
73
+ return [picked, chosen].find(id => !!id && targets.includes(id)) ?? targets[0] ?? '';
74
+ }
68
75
  //# sourceMappingURL=preview-model.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"preview-model.js","sourceRoot":"","sources":["../../client/modeller/preview-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAA;AAGhI,iHAAiH;AAEjH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAA;AAE/B,MAAM,CAAC,MAAM,YAAY,GAAoE;IAC3F,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;IAC1B,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE;IAC1B,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE;IAC1B,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE;IAC1B,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;IACjC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;IACjC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;IACjC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE;CACzC,CAAA;AAED,8GAA8G;AAC9G,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,MAAM,CAAC,GAAG,KAAK,CAAC,YAAY,CAAA;IAC5B,MAAM,QAAQ,GAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC7F,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAE,EAAE,CAAA;IACrH,oGAAoG;IACpG,MAAM,KAAK,GAAG,qBAAqB,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAA;IAC1G,MAAM,IAAI,GAAG,CAAC,IAAqB,EAAE,EAAE,CACrC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACxJ,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;AAChI,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,IAAyC;IACtF,MAAM,OAAO,GAAG,CAAC,CAAA;IACjB,MAAM,IAAI,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,cAAc,GAAG,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,cAAc,GAAG,IAAI,EAAE,CAAA;IACrF,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,CAAC,IAAqB,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACtF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7C,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,CAAA;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAA;QACpC,OAAO;YACL,IAAI;YACJ,EAAE,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE;YACpB,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACxD,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACrD,KAAK;YACL,MAAM;YACN,4GAA4G;YAC5G,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;SACtC,CAAA;IACH,CAAC,CAAC,CAAA;IACF,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,EAAE,CAAA;AAC9I,CAAC;AAGD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB,EAAE,IAAyC;IAC3F,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAA;IAC1C,OAAO,MAAM,CAAC,WAAW,CACvB,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,GAAG,CAAC,IAAI;QACP,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACxC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAA;YAC1E,IAAI,CAAC,KAAK;gBAAE,OAAO,EAAE,CAAA;YACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACrF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAA;YACjG,OAAO,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACtD,CAAC,CAAC;KACH,CAAC,CACH,CAAA;AACH,CAAC","sourcesContent":["import { compileFigureAsset, figureOccupiedBoxesOf, figureWritersOf, FIGURE_INSTANCE_SIZE_INPUTS } from '@hatiolab/figure-model'\nimport type { FigureAsset } from '@hatiolab/figure-model'\n\n/* The preview's arithmetic, apart from the element so it is tested without a browser (see figure-preview.ts). */\n\nexport const PREVIEW_FACTOR = 2\n\nexport const PREVIEW_WAYS: readonly { name: string; axes: readonly ('x' | 'y' | 'z')[] }[] = [\n { name: 'base', axes: [] },\n { name: 'x', axes: ['x'] },\n { name: 'y', axes: ['y'] },\n { name: 'z', axes: ['z'] },\n { name: 'x·y', axes: ['x', 'y'] },\n { name: 'x·z', axes: ['x', 'z'] },\n { name: 'y·z', axes: ['y', 'z'] },\n { name: 'x·y·z', axes: ['x', 'y', 'z'] }\n]\n\n/** The size the figure was made at: its instance size when it declares one, else the room its bodies take. */\nexport function authoredSizeOf(asset: FigureAsset): { x: number; y: number; z: number } {\n const d = asset.designInputs\n const declared = (['x', 'y', 'z'] as const).map(axis => d[FIGURE_INSTANCE_SIZE_INPUTS[axis]])\n if (declared.every(v => typeof v === 'number' && v > 0)) return { x: declared[0]!, y: declared[1]!, z: declared[2]! }\n /* No declared size: the room its bodies take up, so the instance is placed at the size it draws. */\n const boxes = figureOccupiedBoxesOf(compileFigureAsset(asset).evaluate().geometry, figureWritersOf(asset))\n const span = (axis: 'x' | 'y' | 'z') =>\n boxes.length ? Math.max(...boxes.map(b => b.centre[axis] + b.extent[axis] / 2)) - Math.min(...boxes.map(b => b.centre[axis] - b.extent[axis] / 2)) : 0\n return { x: Math.max(1, Math.round(span('x'))), y: Math.max(1, Math.round(span('y'))), z: Math.max(1, Math.round(span('z'))) }\n}\n\n/** The eight instances: where each stands on the board and how large, in the scene's words. */\nexport function previewInstances(type: string, size: { x: number; y: number; z: number }) {\n const COLUMNS = 4\n const cell = { x: size.x * PREVIEW_FACTOR * 1.25, z: size.z * PREVIEW_FACTOR * 1.25 }\n const components = PREVIEW_WAYS.map((way, at) => {\n const grow = (axis: 'x' | 'y' | 'z') => (way.axes.includes(axis) ? PREVIEW_FACTOR : 1)\n const width = Math.round(size.x * grow('x'))\n const height = Math.round(size.z * grow('z'))\n const column = at % COLUMNS\n const row = Math.floor(at / COLUMNS)\n return {\n type,\n id: `at-${way.name}`,\n left: Math.round(column * cell.x + (cell.x - width) / 2),\n top: Math.round(row * cell.z + (cell.z - height) / 2),\n width,\n height,\n /* Always written: without it the height is taken as the plan's mean and \"grow y only\" would not grow it. */\n depth: Math.round(size.y * grow('y'))\n }\n })\n return { components, board: { width: Math.round(cell.x * COLUMNS), height: Math.round(cell.z * Math.ceil(PREVIEW_WAYS.length / COLUMNS)) } }\n}\n\n\n/**\n * Where a variant asks a size the figure does not declare, the size it is drawn at instead -- as the board draws it\n * (things-scene clamps each axis to the declared range; chief architect's ruling 2026-09-25). Per variant, empty when\n * it is drawn as asked. A figure that follows its height only is drawn at its one width in the wider variants, and\n * the preview says so rather than drawing them as though they grew.\n */\nexport function previewClampsOf(asset: FigureAsset, size: { x: number; y: number; z: number }): Record<string, Array<{ axis: 'x' | 'y' | 'z'; asked: number; used: number }>> {\n const inputs = asset.document.model.inputs\n return Object.fromEntries(\n PREVIEW_WAYS.map(way => [\n way.name,\n (['x', 'y', 'z'] as const).flatMap(axis => {\n const input = inputs.find(i => i.id === FIGURE_INSTANCE_SIZE_INPUTS[axis])\n if (!input) return []\n const asked = Math.round(size[axis] * (way.axes.includes(axis) ? PREVIEW_FACTOR : 1))\n const used = Math.round(Math.min(Math.max(asked, input.min ?? -Infinity), input.max ?? Infinity))\n return used === asked ? [] : [{ axis, asked, used }]\n })\n ])\n )\n}\n"]}
1
+ {"version":3,"file":"preview-model.js","sourceRoot":"","sources":["../../client/modeller/preview-model.ts"],"names":[],"mappings":"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","sourcesContent":["import { compileFigureAsset, figureOccupiedBoxesOf, figureWritersOf, FIGURE_INSTANCE_SIZE_INPUTS } from '@hatiolab/figure-model'\nimport type { FigureAsset } from '@hatiolab/figure-model'\n\n/* The preview's arithmetic, apart from the element so it is tested without a browser (see figure-preview.ts). */\n\nexport const PREVIEW_FACTOR = 2\n\nexport const PREVIEW_WAYS: readonly { name: string; axes: readonly ('x' | 'y' | 'z')[] }[] = [\n { name: 'base', axes: [] },\n { name: 'x', axes: ['x'] },\n { name: 'y', axes: ['y'] },\n { name: 'z', axes: ['z'] },\n { name: 'x·y', axes: ['x', 'y'] },\n { name: 'x·z', axes: ['x', 'z'] },\n { name: 'y·z', axes: ['y', 'z'] },\n { name: 'x·y·z', axes: ['x', 'y', 'z'] }\n]\n\n/** The size the figure was made at: its instance size when it declares one, else the room its bodies take. */\nexport function authoredSizeOf(asset: FigureAsset): { x: number; y: number; z: number } {\n const d = asset.designInputs\n const declared = (['x', 'y', 'z'] as const).map(axis => d[FIGURE_INSTANCE_SIZE_INPUTS[axis]])\n if (declared.every(v => typeof v === 'number' && v > 0)) return { x: declared[0]!, y: declared[1]!, z: declared[2]! }\n /* No declared size: the room its bodies take up, so the instance is placed at the size it draws. */\n const boxes = figureOccupiedBoxesOf(compileFigureAsset(asset).evaluate().geometry, figureWritersOf(asset))\n const span = (axis: 'x' | 'y' | 'z') =>\n boxes.length ? Math.max(...boxes.map(b => b.centre[axis] + b.extent[axis] / 2)) - Math.min(...boxes.map(b => b.centre[axis] - b.extent[axis] / 2)) : 0\n return { x: Math.max(1, Math.round(span('x'))), y: Math.max(1, Math.round(span('y'))), z: Math.max(1, Math.round(span('z'))) }\n}\n\n/** The eight instances: where each stands on the board and how large, in the scene's words. */\nexport function previewInstances(type: string, size: { x: number; y: number; z: number }) {\n const COLUMNS = 4\n const cell = { x: size.x * PREVIEW_FACTOR * 1.25, z: size.z * PREVIEW_FACTOR * 1.25 }\n const components = PREVIEW_WAYS.map((way, at) => {\n const grow = (axis: 'x' | 'y' | 'z') => (way.axes.includes(axis) ? PREVIEW_FACTOR : 1)\n const width = Math.round(size.x * grow('x'))\n const height = Math.round(size.z * grow('z'))\n const column = at % COLUMNS\n const row = Math.floor(at / COLUMNS)\n return {\n type,\n id: `at-${way.name}`,\n left: Math.round(column * cell.x + (cell.x - width) / 2),\n top: Math.round(row * cell.z + (cell.z - height) / 2),\n width,\n height,\n /* Always written: without it the height is taken as the plan's mean and \"grow y only\" would not grow it. */\n depth: Math.round(size.y * grow('y'))\n }\n })\n return { components, board: { width: Math.round(cell.x * COLUMNS), height: Math.round(cell.z * Math.ceil(PREVIEW_WAYS.length / COLUMNS)) } }\n}\n\n\n/**\n * Where a variant asks a size the figure does not declare, the size it is drawn at instead -- as the board draws it\n * (things-scene clamps each axis to the declared range; chief architect's ruling 2026-09-25). Per variant, empty when\n * it is drawn as asked. A figure that follows its height only is drawn at its one width in the wider variants, and\n * the preview says so rather than drawing them as though they grew.\n */\nexport function previewClampsOf(asset: FigureAsset, size: { x: number; y: number; z: number }): Record<string, Array<{ axis: 'x' | 'y' | 'z'; asked: number; used: number }>> {\n const inputs = asset.document.model.inputs\n return Object.fromEntries(\n PREVIEW_WAYS.map(way => [\n way.name,\n (['x', 'y', 'z'] as const).flatMap(axis => {\n const input = inputs.find(i => i.id === FIGURE_INSTANCE_SIZE_INPUTS[axis])\n if (!input) return []\n const asked = Math.round(size[axis] * (way.axes.includes(axis) ? PREVIEW_FACTOR : 1))\n const used = Math.round(Math.min(Math.max(asked, input.min ?? -Infinity), input.max ?? Infinity))\n return used === asked ? [] : [{ axis, asked, used }]\n })\n ])\n )\n}\n\n/**\n * The part the preview's size help speaks of: the one picked in the help, else the one chosen in the editor, else the\n * first that has a size rule. A choice that is not among the parts (removed, or a part with no rule) is passed over.\n */\nexport function setupPartOf(picked: string, chosen: string, targets: string[]): string {\n return [picked, chosen].find(id => !!id && targets.includes(id)) ?? targets[0] ?? ''\n}\n"]}
@@ -102,6 +102,7 @@ export declare class FigureModellerPage extends PageView {
102
102
  private askFirstSave;
103
103
  get context(): {
104
104
  title: string;
105
+ menu: string | undefined;
105
106
  actions: never[];
106
107
  heading: boolean;
107
108
  };
@@ -481,8 +481,14 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
481
481
  }
482
482
  }
483
483
  get context() {
484
+ const view = this.session.read();
484
485
  return {
485
- title: this.session.read().name ?? 'Figure',
486
+ title: view.name ?? 'Figure',
487
+ /*
488
+ A figure that has an id was opened from the catalog (or saved there), so the rail marks the catalog. Without this
489
+ the rail matches the page name and marks "new figure" while an existing one is being edited.
490
+ */
491
+ menu: view.id ? 'figure-list' : undefined,
486
492
  actions: [],
487
493
  /*
488
494
  No heading line: the canvas is what this page is for, and the name is already in the properties' first section
@@ -1 +1 @@
1
- {"version":3,"file":"figure-modeller-page.js","sourceRoot":"","sources":["../../client/pages/figure-modeller-page.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { navigate, PageView } from '@operato/shell'\nimport { getPathInfo } from '@operato/utils'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { createFigureProductSession } from '../modeller/product-session.js'\nimport { figureProductStore } from '../graphql/store.js'\nimport { i18next } from '@operato/i18n'\nimport { fetchFigure, fetchFigureTypeNames, fetchProposalOutcome, updateFigure } from '../graphql/index.js'\nimport { availableRecipes, recipeGuide } from '../modeller/recipe-source.js'\nimport type { RecipeTemplate } from '../modeller/recipes.js'\nimport { occupancyFromParts } from '../modeller/occupancy.js'\nimport type { FigureAuthoringAction } from '@hatiolab/figure-model'\nimport '../modeller/editor.js'\nimport '@material/web/icon/icon.js'\nimport '../modeller/figure-history-panel.js'\nimport '../modeller/figure-verdict.js'\nimport '../modeller/figure-properties.js'\nimport type { FigureBudgetLine } from '../modeller/maturity.js'\nimport { OxPrompt } from '@operato/popup'\nimport { notify, openPopup } from '@operato/layout'\nimport '../modeller/figure-first-save.js'\nimport { activePalette } from '@hatiolab/things-scene'\nimport {\n askAssistant,\n clearAssistantSurface,\n notifyAssistantSurface,\n provideAssistantSurface\n} from '@things-factory/ai-assistant/dist-client/index.js'\nimport type { FirstSave } from '../modeller/first-save.js'\nimport { placementRefusalOf } from '../modeller/refusals.js'\nimport { proposalOutcomeFrom, takeFigureOffer, takePendingNewFigure } from '../modeller/ai-proposal.js'\n\n/**\n * The product page has exactly one authoritative workspace for UI and AI input transactions.\n *\n * The one modeller since ADR-0087 step 5 (2026-09-24): it answers to `figure-modeller`, and to `figure-modeller`\n * as an alias for links made before the switch.\n */\n@customElement('figure-modeller-page')\nexport class FigureModellerPage extends PageView {\n static styles = css`\n :host {\n display: flex;\n flex-direction: row;\n height: 100%;\n min-height: 0;\n /*\n The page must fit the width it is given. Without this the shell sizes it to its content — the editor's\n 660 plus the 320 release panel — and the panel runs off the right edge (seen at 945 px, 2026-09-24).\n */\n overflow: hidden;\n }\n figure-editor {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n /* The first field of the properties tab, in the side panel's look (side-panel-styles): a label above the field. */\n label[type-field] {\n display: grid;\n gap: 2px;\n margin: 0 0 5px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n label[type-field] input,\n label[type-field] code {\n box-sizing: border-box;\n min-height: 30px;\n padding: 4px 7px;\n font-family: var(--mono-font, monospace);\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n label[type-field] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n p[fit-error] {\n margin: 0;\n padding: 8px 12px;\n font-size: 12px;\n color: var(--md-sys-color-error);\n }\n label[type-field] small[role='alert'] {\n color: var(--md-sys-color-error);\n }\n .error {\n padding: 16px;\n color: var(--md-sys-color-error);\n }\n `\n private readonly session = createFigureProductSession(figureProductStore)\n private routeId: string | undefined\n private discardApproved = false\n @state() private revision = 0\n /** The recipe templates (ADR-0095), read once when the page is first shown; none read, and the section is not drawn. */\n @state() private templates: RecipeTemplate[] = []\n private templatesRead = false\n private guideOf = (name: string) => recipeGuide(name, i18next.language || 'en')\n /* The domain part of the address is read the way the shell reads it (figure-thumb does the same). */\n private thumbnailOf = (name: string) => `${getPathInfo(location.pathname).contextPath}/figure-recipe-thumbnail/${name}`\n @state() private error = ''\n /**\n * 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.\n *\n * 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서\n * `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 도형은 공간을 선언하기 전에는 전부\n * 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.\n */\n @state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {\n version: 0\n }\n @state() private typeError = ''\n /** Why a command from the side did nothing. Shown beside the tabs — the editor stays, since nothing in it was lost. */\n @state() private commandError = ''\n /** The verdict's count, handed to the editor's verdict tab. */\n @state() private instances?: number\n @state() private budget?: FigureBudgetLine\n /**\n * The source the verdict judges. The session refuses to export while it loads or saves, so the last one read is\n * kept rather than blanked — the verdict would otherwise flicker to 「waiting」 on every save.\n */\n private draft?: string\n private draftSource(): string | undefined {\n try {\n this.draft = this.session.source()\n } catch {\n /* loading or saving — keep the last source */\n }\n return this.draft\n }\n private wasDirty = false\n private titled?: string | null\n /** The figure the AI dock was last told about - another id is another conversation. */\n private assistantFor?: string\n /*\n The figure on screen is what the AI dock talks about (design/ai/shared-dock.md §3). A figure not saved yet has no\n row to hang a conversation on, so it provides nothing and the dock stays on the application's conversation.\n The draft is read at the moment of sending, never cached - the dock must not answer about a shape already changed.\n */\n private provideAssistant = () => {\n const view = this.session.read()\n if (!this.active || view.loading || view.error || !view.context) return\n if (!view.id) {\n this.withdrawAssistant()\n return\n }\n if (this.assistantFor === view.id) {\n notifyAssistantSurface(this)\n return\n }\n const id = view.id\n this.assistantFor = id\n provideAssistantSurface(this, {\n key: `figure:${id}`,\n subject: 'figure',\n icon: 'category',\n label: () => this.session.read().name || id,\n detail: () => (this.session.read().dirty ? i18next.t('figure.text.unsaved-changes') : ''),\n anchor: () => ({ anchorType: 'figure', anchorId: id }),\n /* The card's \"check the result\": the server's record, never the dock's memory; nothing is applied again. */\n proposalOutcome: async (proposalKey: string) =>\n proposalOutcomeFrom(await fetchProposalOutcome(proposalKey), (key, params) => i18next.t(key, params)),\n hostContext: () => ({\n surface: 'figure-modeller',\n editorMode: 'existing',\n figureId: id,\n draftSource: JSON.parse(this.session.source()),\n /* The version the model sees; a proposal is applied on it, or refused if the figure moved on (ADR-0094). */\n context: this.session.read().context,\n revision: this.session.read().revision,\n palette: Object.keys(activePalette()),\n ...this.assistantFocus()\n })\n })\n }\n /**\n * The part selected and what the preview is trying, with the version they were seen on (ADR-0097 §5.6 item 5). Read\n * when a request is sent; selecting is not a new subject, so the dock is not told about it and the conversation stays.\n */\n private assistantFocus() {\n const focus = this.renderRoot.querySelector<HTMLElement & { assistantFocus?: () => { part?: string; trying?: object } }>('figure-editor')?.assistantFocus?.() ?? {}\n const { context, revision } = this.session.read()\n return {\n ...(focus.part ? { selection: { part: focus.part, context, revision } } : {}),\n ...(focus.trying ? { trying: { ...focus.trying, context, revision } } : {})\n }\n }\n private withdrawAssistant() {\n this.assistantFor = undefined\n clearAssistantSurface(this)\n }\n private changed = () => {\n this.revision++\n /* Any edit after a failed command makes the message stale — the author has done something about it. */\n this.commandError = ''\n const view = this.session.read()\n /* The title bar reads the name through `context`, which the shell asks for only when told — a rename left it stale. */\n if (view.name !== this.titled) {\n this.titled = view.name\n this.updateContext()\n }\n if (!view.saving) this.provideAssistant()\n /*\n A new figure's first save gives it an id: the address becomes the figure's own, in place of the history entry.\n Left at the bare modeller address, opening the modeller again from the menu was taken for the same page and kept\n the saved figure -- its type showing where a new figure's was expected (2026-09-25).\n */\n if (view.id && !this.routeId && !view.saving) {\n this.routeId = view.id\n navigate(modellerPathOf({ id: view.id }), true)\n }\n /* 저장이 끝난 순간 행을 다시 읽는다 — 발행된 도형을 고쳐 저장하면 서버가 초안으로 내린다. */\n if (this.wasDirty && !view.dirty && !view.saving && view.id) this.readRow(view.id)\n this.wasDirty = !!view.dirty\n }\n /*\n A released figure changed on the server -- its source saved, or its category or tags -- comes back a draft, and a\n board keeps the released version until it is released again. Said when it happens (D, designer 2026-09-27), not left\n to a badge in the history tab.\n */\n /* True of what boards read: `figureBlueprint` answers a draft row with its latest released version (releasedSourceOf). */\n private toldDemoted(before: string | undefined, after: string | undefined) {\n if (before === 'released' && after === 'draft') notify({ level: 'info', message: i18next.t('figure.text.released-now-draft') })\n }\n private async readRow(id: string) {\n try {\n const figure = await fetchFigure(id)\n if (this.session.read().id === id) this.toldDemoted(this.row.state, figure.state)\n if (this.session.read().id === id)\n this.row = {\n state: figure.state as 'draft' | 'released',\n version: figure.version ?? 0,\n type: figure.type,\n category: figure.category ?? undefined,\n tags: figure.tags ?? undefined\n }\n } catch {\n /* 행을 못 읽으면 패널은 판 번호 0 으로 보인다 — 발행 문이 서버에서 다시 판단하므로 여기서 지어내지 않는다. */\n }\n }\n /**\n * 타입을 정한다 — 보드가 도형을 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).\n *\n * 새 도형은 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.\n * 행만 바뀐다 — 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.\n * 서버의 거절(이미 쓰는 타입 · 발행 뒤)은 그 문장 그대로 칸 아래에 둔다.\n */\n private renameType = async (event: Event) => {\n const input = event.target as HTMLInputElement\n const id = this.session.read().id\n const wanted = input.value.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n input.value = wanted\n /*\n A figure not saved yet has no row to rename: the type is held here and the first save's question opens with it.\n The field was disabled until the first save, so a type typed there went nowhere and was asked for again\n (2026-09-25).\n */\n if (!id) {\n this.row = { ...this.row, type: wanted || undefined }\n return\n }\n if (!wanted || wanted === this.row.type) return\n try {\n const result = await updateFigure(id, { type: wanted })\n this.typeError = ''\n this.row = { ...this.row, type: result.figure.type }\n } catch (error) {\n this.typeError = error instanceof Error ? error.message : String(error)\n input.value = this.row.type ?? ''\n }\n }\n /** 「부품에 맞추기」 — 공간을 지금 부품에서 다시 선언한다. 저장은 저작자가 따로 한다. */\n private fitToParts = () => {\n this.runCommand(() => occupancyFromParts(this.session.source()))\n }\n /** A command from the properties tab — rename, declare the room, the mounting face. */\n private onCommand = (event: Event) => {\n const action = (event as CustomEvent).detail?.action as FigureAuthoringAction | undefined\n if (action) this.runCommand(() => action)\n }\n /**\n * One step of the session's history, whichever part of the side asked for it — so undo takes it back like any\n * other edit, and it is saved with the figure.\n */\n private runCommand(actionOf: () => FigureAuthoringAction) {\n try {\n this.session.command(actionOf())\n this.commandError = ''\n this.changed()\n } catch (error) {\n /*\n This used to set the page error, which replaces the whole editor with one raw line — for a figure with no\n parts that line was 「OCCUPANCY_NO_PARTS: occupancy: no part could be measured」 (2026-09-24).\n */\n this.commandError =\n (error as { code?: string })?.code === 'OCCUPANCY_NO_PARTS'\n ? i18next.t('figure.text.fit-needs-parts')\n : placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??\n (error instanceof Error\n ? error.message\n : String(error))\n }\n }\n /**\n * Category and tags — the catalogue's, kept on the row, not in the asset. Written at once; the server's\n * refusal is shown as it gave it.\n */\n private onCatalog = async (event: Event) => {\n const patch = (event as CustomEvent).detail as { category?: string; tags?: string[] }\n const id = this.session.read().id\n if (!id || !patch) return\n try {\n const result = await updateFigure(id, patch)\n this.commandError = ''\n /* The server's answer on its state too: a category changed on a released figure made it a draft, and the badge stayed. */\n this.toldDemoted(this.row.state, result.figure.state)\n this.row = {\n ...this.row,\n category: result.figure.category ?? undefined,\n tags: result.figure.tags ?? undefined,\n ...(result.figure.state ? { state: result.figure.state as 'draft' | 'released' } : {}),\n ...(result.figure.version !== undefined ? { version: result.figure.version ?? 0 } : {})\n }\n } catch (error) {\n this.commandError = error instanceof Error ? error.message : String(error)\n }\n }\n private released = (event: Event) => {\n const figure = (event as CustomEvent).detail?.figure\n /* Keep the rest of the row: rebuilding it from these two dropped the type, and the locked type field went blank. */\n if (figure?.id && figure.id === this.session.read().id) this.row = { ...this.row, state: figure.state, version: figure.version ?? 0 }\n }\n /*\n The edits in hand saved before going back to a released version: known saved, or not -- a failure, or an outcome not\n known, holds the revert (designer 2026-09-27, Q-D1). The figure has a row here: there is no first save to ask.\n */\n private saveBeforeRevert = async (): Promise<{ ok: boolean; reason?: string }> => {\n try {\n await this.session.save()\n this.changed()\n return this.session.read().dirty ? { ok: false } : { ok: true }\n } catch (error) {\n this.changed()\n const code = (error as { code?: string })?.code\n return { ok: false, reason: code === 'SAVE_UNREACHABLE' ? i18next.t('figure.text.save-unreachable') : error instanceof Error ? error.message : String(error) }\n }\n }\n /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 저장 안 된 것이 있으면 패널이 먼저 저장·버리기·취소를 묻는다. */\n private reverted = async () => {\n const id = this.session.read().id\n if (!id) return\n await this.session.open(id, { discard: true })\n this.changed()\n this.readRow(id)\n }\n /**\n * The dock's way in (`figure-assistant.ts`): input changes, or a proposal's commands as one step (ADR-0094). A\n * revision is applied on the version the model saw; a new figure's proposal only on a new figure not yet touched. A\n * page on a saved figure leaves a new figure's proposal untaken, and the handler opens a new figure for it.\n */\n private applyAI = (event: Event) => {\n const detail = (event as CustomEvent).detail\n if (!this.active || !detail) return\n if (takeFigureOffer(this.session, detail, { onSavedFigure: !!this.routeId, say: (key, params) => i18next.t(key, params) })) this.changed()\n }\n /*\n Reloading or closing the tab is not a move the shell asks `canDeactivate` about: a new figure not yet saved was lost\n without a word (2026-09-26). The browser's own question is the only one it allows there.\n */\n private holdUnsaved = (event: BeforeUnloadEvent) => {\n if (!this.active || !this.session.read().dirty) return\n event.preventDefault()\n event.returnValue = ''\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('figure-apply', this.applyAI)\n window.addEventListener('beforeunload', this.holdUnsaved)\n }\n disconnectedCallback() {\n this.withdrawAssistant()\n window.removeEventListener('figure-apply', this.applyAI)\n window.removeEventListener('beforeunload', this.holdUnsaved)\n super.disconnectedCallback()\n }\n async canDeactivate() {\n const view = this.session.read()\n if (view.saving) return false\n if (!view.dirty) return true\n /*\n The shell asks this while the edited model is still on screen. The app's own prompt, as every operato screen\n asks -- not the browser's confirm box (user, 2026-09-24).\n */\n this.discardApproved = !!(await OxPrompt.open({\n type: 'warning',\n title: i18next.t('figure.text.unsaved-changes'),\n text: i18next.t('figure.text.leave-without-saving'),\n confirmButton: { text: i18next.t('figure.button.leave-without-saving') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n }))\n return this.discardApproved\n }\n async pageUpdated(changes: Record<string, unknown>, lifecycle: { resourceId?: string }) {\n if (!this.active) {\n if (this.session.read().loading) this.session.close()\n this.withdrawAssistant()\n return\n }\n if (!this.templatesRead) {\n this.templatesRead = true\n availableRecipes(i18next.language || 'en')\n .then(templates => (this.templates = templates))\n .catch(() => (this.templatesRead = false))\n }\n const id = lifecycle?.resourceId\n if (this.session.read().context && id === this.routeId && !changes.active) return\n try {\n const discard = this.discardApproved\n this.discardApproved = false\n if (id) {\n this.draft = undefined\n /* The row is read after the figure opens; until then the last figure's type must not stand in the field (C2). */\n this.row = { version: 0 }\n this.typeError = ''\n const opening = this.session.open(id, { discard })\n this.changed()\n await opening\n this.readRow(id)\n } else {\n this.session.startNew({ discard })\n this.draft = undefined\n this.row = { version: 0 }\n this.typeError = ''\n this.takeNewFigureProposal()\n }\n this.routeId = id\n this.error = ''\n this.changed()\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n }\n }\n /** A new figure the dock opened for a proposal: its steps, applied as the new figure's first step. */\n private takeNewFigureProposal() {\n const waiting = takePendingNewFigure()\n if (!waiting) return\n const view = this.session.read()\n try {\n /* Created under the proposal's key: taken twice, it meets the first figure, never a second (ADR-0097 §5.6 item 5). */\n if (waiting.key) this.session.createUnder(waiting.key)\n this.session.applyProposal(waiting.steps, view.revision!, view.context!, waiting.key)\n waiting.settle({ applied: true })\n } catch (error) {\n waiting.settle({ applied: false, code: (error as { code?: string })?.code, error: error instanceof Error ? error.message : String(error) })\n }\n }\n /** The first save's question, over the names this domain already uses. */\n private askFirstSave = async (name: string): Promise<FirstSave | undefined> => {\n const taken = await fetchFigureTypeNames().catch(() => [] as string[])\n const answer = await openPopup<FirstSave>(html`<figure-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-first-save>`, {\n backdrop: true,\n size: 'small',\n title: i18next.t('figure.title.save-new-figure')\n }).result\n /* Kept: a first save that failed opens again on the same identifier, not a new draw (ADR-0097 §5.6, Q-D3). */\n if (answer) this.row = { ...this.row, type: answer.type }\n return answer\n }\n get context() {\n return {\n title: this.session.read().name ?? 'Figure',\n actions: [],\n /*\n No heading line: the canvas is what this page is for, and the name is already in the properties' first section\n (and the browser tab). The title stays, not emptied, because the MDI tab reads it too -- the heading is switched\n off where it is drawn (`ox-page-title-bar`).\n */\n heading: false\n }\n }\n render() {\n if (this.error) return html`<p role=\"alert\" class=\"error\">${this.error}</p>`\n const view = this.session.read()\n /* A new figure's third way to start: the app's assistant dock, with a request put in its input for the author to finish and send (B, U04). */\n return html`<figure-editor .session=${this.session} .releaseState=${this.row} .templates=${this.templates} .guideOf=${this.guideOf} .thumbnailOf=${this.thumbnailOf} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} aiDraft @ask-ai-draft=${() => askAssistant({ draft: i18next.t('figure.text.ai-draft-request') })} @changed=${this.changed}>\n ${this.commandError ? html`<p slot=\"notice\" fit-error role=\"alert\">${this.commandError}</p>` : ''}\n <figure-properties\n slot=\"figure\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureCategory=${this.row.category}\n .figureTags=${this.row.tags}\n @command=${this.onCommand}\n @catalog=${this.onCatalog}\n >\n <label slot=\"type\" type-field>\n <span>${i18next.t('figure.label.figure-type')}</span>\n ${this.row.version > 0\n ? html`<code>${this.row.type}</code><small>${i18next.t('figure.text.type-name-cannot-change')}</small>`\n : html`<input\n .value=${this.row.type ?? ''}\n placeholder=${i18next.t('figure.text.figure-type-placeholder')}\n @change=${this.renameType}\n />`}\n ${this.typeError ? html`<small role=\"alert\">${this.typeError}</small>` : ''}\n </label>\n </figure-properties>\n <figure-verdict\n slot=\"verdict\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureType=${this.row.type}\n @capacity-changed=${(event: CustomEvent) => {\n this.instances = event.detail.instances\n this.budget = event.detail.budget\n }}\n @fit-base-to-parts=${this.fitToParts}\n ></figure-verdict>\n <figure-history-panel\n slot=\"history\"\n .figureId=${view.id}\n .figureState=${this.row.state}\n .figureVersion=${this.row.version}\n .figureType=${this.row.type}\n .dirty=${!!view.dirty}\n .saveFirst=${this.saveBeforeRevert}\n .serverSource=${() => this.session.confirmedSource()}\n @fit-base-to-parts=${this.fitToParts}\n @figure-released=${this.released}\n @figure-reverted=${this.reverted}\n ></figure-history-panel>\n </figure-editor>`\n }\n\n}\n"]}
1
+ {"version":3,"file":"figure-modeller-page.js","sourceRoot":"","sources":["../../client/pages/figure-modeller-page.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { navigate, PageView } from '@operato/shell'\nimport { getPathInfo } from '@operato/utils'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { createFigureProductSession } from '../modeller/product-session.js'\nimport { figureProductStore } from '../graphql/store.js'\nimport { i18next } from '@operato/i18n'\nimport { fetchFigure, fetchFigureTypeNames, fetchProposalOutcome, updateFigure } from '../graphql/index.js'\nimport { availableRecipes, recipeGuide } from '../modeller/recipe-source.js'\nimport type { RecipeTemplate } from '../modeller/recipes.js'\nimport { occupancyFromParts } from '../modeller/occupancy.js'\nimport type { FigureAuthoringAction } from '@hatiolab/figure-model'\nimport '../modeller/editor.js'\nimport '@material/web/icon/icon.js'\nimport '../modeller/figure-history-panel.js'\nimport '../modeller/figure-verdict.js'\nimport '../modeller/figure-properties.js'\nimport type { FigureBudgetLine } from '../modeller/maturity.js'\nimport { OxPrompt } from '@operato/popup'\nimport { notify, openPopup } from '@operato/layout'\nimport '../modeller/figure-first-save.js'\nimport { activePalette } from '@hatiolab/things-scene'\nimport {\n askAssistant,\n clearAssistantSurface,\n notifyAssistantSurface,\n provideAssistantSurface\n} from '@things-factory/ai-assistant/dist-client/index.js'\nimport type { FirstSave } from '../modeller/first-save.js'\nimport { placementRefusalOf } from '../modeller/refusals.js'\nimport { proposalOutcomeFrom, takeFigureOffer, takePendingNewFigure } from '../modeller/ai-proposal.js'\n\n/**\n * The product page has exactly one authoritative workspace for UI and AI input transactions.\n *\n * The one modeller since ADR-0087 step 5 (2026-09-24): it answers to `figure-modeller`, and to `figure-modeller`\n * as an alias for links made before the switch.\n */\n@customElement('figure-modeller-page')\nexport class FigureModellerPage extends PageView {\n static styles = css`\n :host {\n display: flex;\n flex-direction: row;\n height: 100%;\n min-height: 0;\n /*\n The page must fit the width it is given. Without this the shell sizes it to its content — the editor's\n 660 plus the 320 release panel — and the panel runs off the right edge (seen at 945 px, 2026-09-24).\n */\n overflow: hidden;\n }\n figure-editor {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n /* The first field of the properties tab, in the side panel's look (side-panel-styles): a label above the field. */\n label[type-field] {\n display: grid;\n gap: 2px;\n margin: 0 0 5px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n label[type-field] input,\n label[type-field] code {\n box-sizing: border-box;\n min-height: 30px;\n padding: 4px 7px;\n font-family: var(--mono-font, monospace);\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n label[type-field] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n p[fit-error] {\n margin: 0;\n padding: 8px 12px;\n font-size: 12px;\n color: var(--md-sys-color-error);\n }\n label[type-field] small[role='alert'] {\n color: var(--md-sys-color-error);\n }\n .error {\n padding: 16px;\n color: var(--md-sys-color-error);\n }\n `\n private readonly session = createFigureProductSession(figureProductStore)\n private routeId: string | undefined\n private discardApproved = false\n @state() private revision = 0\n /** The recipe templates (ADR-0095), read once when the page is first shown; none read, and the section is not drawn. */\n @state() private templates: RecipeTemplate[] = []\n private templatesRead = false\n private guideOf = (name: string) => recipeGuide(name, i18next.language || 'en')\n /* The domain part of the address is read the way the shell reads it (figure-thumb does the same). */\n private thumbnailOf = (name: string) => `${getPathInfo(location.pathname).contextPath}/figure-recipe-thumbnail/${name}`\n @state() private error = ''\n /**\n * 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.\n *\n * 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서\n * `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 도형은 공간을 선언하기 전에는 전부\n * 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.\n */\n @state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {\n version: 0\n }\n @state() private typeError = ''\n /** Why a command from the side did nothing. Shown beside the tabs — the editor stays, since nothing in it was lost. */\n @state() private commandError = ''\n /** The verdict's count, handed to the editor's verdict tab. */\n @state() private instances?: number\n @state() private budget?: FigureBudgetLine\n /**\n * The source the verdict judges. The session refuses to export while it loads or saves, so the last one read is\n * kept rather than blanked — the verdict would otherwise flicker to 「waiting」 on every save.\n */\n private draft?: string\n private draftSource(): string | undefined {\n try {\n this.draft = this.session.source()\n } catch {\n /* loading or saving — keep the last source */\n }\n return this.draft\n }\n private wasDirty = false\n private titled?: string | null\n /** The figure the AI dock was last told about - another id is another conversation. */\n private assistantFor?: string\n /*\n The figure on screen is what the AI dock talks about (design/ai/shared-dock.md §3). A figure not saved yet has no\n row to hang a conversation on, so it provides nothing and the dock stays on the application's conversation.\n The draft is read at the moment of sending, never cached - the dock must not answer about a shape already changed.\n */\n private provideAssistant = () => {\n const view = this.session.read()\n if (!this.active || view.loading || view.error || !view.context) return\n if (!view.id) {\n this.withdrawAssistant()\n return\n }\n if (this.assistantFor === view.id) {\n notifyAssistantSurface(this)\n return\n }\n const id = view.id\n this.assistantFor = id\n provideAssistantSurface(this, {\n key: `figure:${id}`,\n subject: 'figure',\n icon: 'category',\n label: () => this.session.read().name || id,\n detail: () => (this.session.read().dirty ? i18next.t('figure.text.unsaved-changes') : ''),\n anchor: () => ({ anchorType: 'figure', anchorId: id }),\n /* The card's \"check the result\": the server's record, never the dock's memory; nothing is applied again. */\n proposalOutcome: async (proposalKey: string) =>\n proposalOutcomeFrom(await fetchProposalOutcome(proposalKey), (key, params) => i18next.t(key, params)),\n hostContext: () => ({\n surface: 'figure-modeller',\n editorMode: 'existing',\n figureId: id,\n draftSource: JSON.parse(this.session.source()),\n /* The version the model sees; a proposal is applied on it, or refused if the figure moved on (ADR-0094). */\n context: this.session.read().context,\n revision: this.session.read().revision,\n palette: Object.keys(activePalette()),\n ...this.assistantFocus()\n })\n })\n }\n /**\n * The part selected and what the preview is trying, with the version they were seen on (ADR-0097 §5.6 item 5). Read\n * when a request is sent; selecting is not a new subject, so the dock is not told about it and the conversation stays.\n */\n private assistantFocus() {\n const focus = this.renderRoot.querySelector<HTMLElement & { assistantFocus?: () => { part?: string; trying?: object } }>('figure-editor')?.assistantFocus?.() ?? {}\n const { context, revision } = this.session.read()\n return {\n ...(focus.part ? { selection: { part: focus.part, context, revision } } : {}),\n ...(focus.trying ? { trying: { ...focus.trying, context, revision } } : {})\n }\n }\n private withdrawAssistant() {\n this.assistantFor = undefined\n clearAssistantSurface(this)\n }\n private changed = () => {\n this.revision++\n /* Any edit after a failed command makes the message stale — the author has done something about it. */\n this.commandError = ''\n const view = this.session.read()\n /* The title bar reads the name through `context`, which the shell asks for only when told — a rename left it stale. */\n if (view.name !== this.titled) {\n this.titled = view.name\n this.updateContext()\n }\n if (!view.saving) this.provideAssistant()\n /*\n A new figure's first save gives it an id: the address becomes the figure's own, in place of the history entry.\n Left at the bare modeller address, opening the modeller again from the menu was taken for the same page and kept\n the saved figure -- its type showing where a new figure's was expected (2026-09-25).\n */\n if (view.id && !this.routeId && !view.saving) {\n this.routeId = view.id\n navigate(modellerPathOf({ id: view.id }), true)\n }\n /* 저장이 끝난 순간 행을 다시 읽는다 — 발행된 도형을 고쳐 저장하면 서버가 초안으로 내린다. */\n if (this.wasDirty && !view.dirty && !view.saving && view.id) this.readRow(view.id)\n this.wasDirty = !!view.dirty\n }\n /*\n A released figure changed on the server -- its source saved, or its category or tags -- comes back a draft, and a\n board keeps the released version until it is released again. Said when it happens (D, designer 2026-09-27), not left\n to a badge in the history tab.\n */\n /* True of what boards read: `figureBlueprint` answers a draft row with its latest released version (releasedSourceOf). */\n private toldDemoted(before: string | undefined, after: string | undefined) {\n if (before === 'released' && after === 'draft') notify({ level: 'info', message: i18next.t('figure.text.released-now-draft') })\n }\n private async readRow(id: string) {\n try {\n const figure = await fetchFigure(id)\n if (this.session.read().id === id) this.toldDemoted(this.row.state, figure.state)\n if (this.session.read().id === id)\n this.row = {\n state: figure.state as 'draft' | 'released',\n version: figure.version ?? 0,\n type: figure.type,\n category: figure.category ?? undefined,\n tags: figure.tags ?? undefined\n }\n } catch {\n /* 행을 못 읽으면 패널은 판 번호 0 으로 보인다 — 발행 문이 서버에서 다시 판단하므로 여기서 지어내지 않는다. */\n }\n }\n /**\n * 타입을 정한다 — 보드가 도형을 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).\n *\n * 새 도형은 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.\n * 행만 바뀐다 — 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.\n * 서버의 거절(이미 쓰는 타입 · 발행 뒤)은 그 문장 그대로 칸 아래에 둔다.\n */\n private renameType = async (event: Event) => {\n const input = event.target as HTMLInputElement\n const id = this.session.read().id\n const wanted = input.value.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n input.value = wanted\n /*\n A figure not saved yet has no row to rename: the type is held here and the first save's question opens with it.\n The field was disabled until the first save, so a type typed there went nowhere and was asked for again\n (2026-09-25).\n */\n if (!id) {\n this.row = { ...this.row, type: wanted || undefined }\n return\n }\n if (!wanted || wanted === this.row.type) return\n try {\n const result = await updateFigure(id, { type: wanted })\n this.typeError = ''\n this.row = { ...this.row, type: result.figure.type }\n } catch (error) {\n this.typeError = error instanceof Error ? error.message : String(error)\n input.value = this.row.type ?? ''\n }\n }\n /** 「부품에 맞추기」 — 공간을 지금 부품에서 다시 선언한다. 저장은 저작자가 따로 한다. */\n private fitToParts = () => {\n this.runCommand(() => occupancyFromParts(this.session.source()))\n }\n /** A command from the properties tab — rename, declare the room, the mounting face. */\n private onCommand = (event: Event) => {\n const action = (event as CustomEvent).detail?.action as FigureAuthoringAction | undefined\n if (action) this.runCommand(() => action)\n }\n /**\n * One step of the session's history, whichever part of the side asked for it — so undo takes it back like any\n * other edit, and it is saved with the figure.\n */\n private runCommand(actionOf: () => FigureAuthoringAction) {\n try {\n this.session.command(actionOf())\n this.commandError = ''\n this.changed()\n } catch (error) {\n /*\n This used to set the page error, which replaces the whole editor with one raw line — for a figure with no\n parts that line was 「OCCUPANCY_NO_PARTS: occupancy: no part could be measured」 (2026-09-24).\n */\n this.commandError =\n (error as { code?: string })?.code === 'OCCUPANCY_NO_PARTS'\n ? i18next.t('figure.text.fit-needs-parts')\n : placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??\n (error instanceof Error\n ? error.message\n : String(error))\n }\n }\n /**\n * Category and tags — the catalogue's, kept on the row, not in the asset. Written at once; the server's\n * refusal is shown as it gave it.\n */\n private onCatalog = async (event: Event) => {\n const patch = (event as CustomEvent).detail as { category?: string; tags?: string[] }\n const id = this.session.read().id\n if (!id || !patch) return\n try {\n const result = await updateFigure(id, patch)\n this.commandError = ''\n /* The server's answer on its state too: a category changed on a released figure made it a draft, and the badge stayed. */\n this.toldDemoted(this.row.state, result.figure.state)\n this.row = {\n ...this.row,\n category: result.figure.category ?? undefined,\n tags: result.figure.tags ?? undefined,\n ...(result.figure.state ? { state: result.figure.state as 'draft' | 'released' } : {}),\n ...(result.figure.version !== undefined ? { version: result.figure.version ?? 0 } : {})\n }\n } catch (error) {\n this.commandError = error instanceof Error ? error.message : String(error)\n }\n }\n private released = (event: Event) => {\n const figure = (event as CustomEvent).detail?.figure\n /* Keep the rest of the row: rebuilding it from these two dropped the type, and the locked type field went blank. */\n if (figure?.id && figure.id === this.session.read().id) this.row = { ...this.row, state: figure.state, version: figure.version ?? 0 }\n }\n /*\n The edits in hand saved before going back to a released version: known saved, or not -- a failure, or an outcome not\n known, holds the revert (designer 2026-09-27, Q-D1). The figure has a row here: there is no first save to ask.\n */\n private saveBeforeRevert = async (): Promise<{ ok: boolean; reason?: string }> => {\n try {\n await this.session.save()\n this.changed()\n return this.session.read().dirty ? { ok: false } : { ok: true }\n } catch (error) {\n this.changed()\n const code = (error as { code?: string })?.code\n return { ok: false, reason: code === 'SAVE_UNREACHABLE' ? i18next.t('figure.text.save-unreachable') : error instanceof Error ? error.message : String(error) }\n }\n }\n /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 저장 안 된 것이 있으면 패널이 먼저 저장·버리기·취소를 묻는다. */\n private reverted = async () => {\n const id = this.session.read().id\n if (!id) return\n await this.session.open(id, { discard: true })\n this.changed()\n this.readRow(id)\n }\n /**\n * The dock's way in (`figure-assistant.ts`): input changes, or a proposal's commands as one step (ADR-0094). A\n * revision is applied on the version the model saw; a new figure's proposal only on a new figure not yet touched. A\n * page on a saved figure leaves a new figure's proposal untaken, and the handler opens a new figure for it.\n */\n private applyAI = (event: Event) => {\n const detail = (event as CustomEvent).detail\n if (!this.active || !detail) return\n if (takeFigureOffer(this.session, detail, { onSavedFigure: !!this.routeId, say: (key, params) => i18next.t(key, params) })) this.changed()\n }\n /*\n Reloading or closing the tab is not a move the shell asks `canDeactivate` about: a new figure not yet saved was lost\n without a word (2026-09-26). The browser's own question is the only one it allows there.\n */\n private holdUnsaved = (event: BeforeUnloadEvent) => {\n if (!this.active || !this.session.read().dirty) return\n event.preventDefault()\n event.returnValue = ''\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('figure-apply', this.applyAI)\n window.addEventListener('beforeunload', this.holdUnsaved)\n }\n disconnectedCallback() {\n this.withdrawAssistant()\n window.removeEventListener('figure-apply', this.applyAI)\n window.removeEventListener('beforeunload', this.holdUnsaved)\n super.disconnectedCallback()\n }\n async canDeactivate() {\n const view = this.session.read()\n if (view.saving) return false\n if (!view.dirty) return true\n /*\n The shell asks this while the edited model is still on screen. The app's own prompt, as every operato screen\n asks -- not the browser's confirm box (user, 2026-09-24).\n */\n this.discardApproved = !!(await OxPrompt.open({\n type: 'warning',\n title: i18next.t('figure.text.unsaved-changes'),\n text: i18next.t('figure.text.leave-without-saving'),\n confirmButton: { text: i18next.t('figure.button.leave-without-saving') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n }))\n return this.discardApproved\n }\n async pageUpdated(changes: Record<string, unknown>, lifecycle: { resourceId?: string }) {\n if (!this.active) {\n if (this.session.read().loading) this.session.close()\n this.withdrawAssistant()\n return\n }\n if (!this.templatesRead) {\n this.templatesRead = true\n availableRecipes(i18next.language || 'en')\n .then(templates => (this.templates = templates))\n .catch(() => (this.templatesRead = false))\n }\n const id = lifecycle?.resourceId\n if (this.session.read().context && id === this.routeId && !changes.active) return\n try {\n const discard = this.discardApproved\n this.discardApproved = false\n if (id) {\n this.draft = undefined\n /* The row is read after the figure opens; until then the last figure's type must not stand in the field (C2). */\n this.row = { version: 0 }\n this.typeError = ''\n const opening = this.session.open(id, { discard })\n this.changed()\n await opening\n this.readRow(id)\n } else {\n this.session.startNew({ discard })\n this.draft = undefined\n this.row = { version: 0 }\n this.typeError = ''\n this.takeNewFigureProposal()\n }\n this.routeId = id\n this.error = ''\n this.changed()\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n }\n }\n /** A new figure the dock opened for a proposal: its steps, applied as the new figure's first step. */\n private takeNewFigureProposal() {\n const waiting = takePendingNewFigure()\n if (!waiting) return\n const view = this.session.read()\n try {\n /* Created under the proposal's key: taken twice, it meets the first figure, never a second (ADR-0097 §5.6 item 5). */\n if (waiting.key) this.session.createUnder(waiting.key)\n this.session.applyProposal(waiting.steps, view.revision!, view.context!, waiting.key)\n waiting.settle({ applied: true })\n } catch (error) {\n waiting.settle({ applied: false, code: (error as { code?: string })?.code, error: error instanceof Error ? error.message : String(error) })\n }\n }\n /** The first save's question, over the names this domain already uses. */\n private askFirstSave = async (name: string): Promise<FirstSave | undefined> => {\n const taken = await fetchFigureTypeNames().catch(() => [] as string[])\n const answer = await openPopup<FirstSave>(html`<figure-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-first-save>`, {\n backdrop: true,\n size: 'small',\n title: i18next.t('figure.title.save-new-figure')\n }).result\n /* Kept: a first save that failed opens again on the same identifier, not a new draw (ADR-0097 §5.6, Q-D3). */\n if (answer) this.row = { ...this.row, type: answer.type }\n return answer\n }\n get context() {\n const view = this.session.read()\n return {\n title: view.name ?? 'Figure',\n /*\n A figure that has an id was opened from the catalog (or saved there), so the rail marks the catalog. Without this\n the rail matches the page name and marks \"new figure\" while an existing one is being edited.\n */\n menu: view.id ? 'figure-list' : undefined,\n actions: [],\n /*\n No heading line: the canvas is what this page is for, and the name is already in the properties' first section\n (and the browser tab). The title stays, not emptied, because the MDI tab reads it too -- the heading is switched\n off where it is drawn (`ox-page-title-bar`).\n */\n heading: false\n }\n }\n render() {\n if (this.error) return html`<p role=\"alert\" class=\"error\">${this.error}</p>`\n const view = this.session.read()\n /* A new figure's third way to start: the app's assistant dock, with a request put in its input for the author to finish and send (B, U04). */\n return html`<figure-editor .session=${this.session} .releaseState=${this.row} .templates=${this.templates} .guideOf=${this.guideOf} .thumbnailOf=${this.thumbnailOf} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} aiDraft @ask-ai-draft=${() => askAssistant({ draft: i18next.t('figure.text.ai-draft-request') })} @changed=${this.changed}>\n ${this.commandError ? html`<p slot=\"notice\" fit-error role=\"alert\">${this.commandError}</p>` : ''}\n <figure-properties\n slot=\"figure\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureCategory=${this.row.category}\n .figureTags=${this.row.tags}\n @command=${this.onCommand}\n @catalog=${this.onCatalog}\n >\n <label slot=\"type\" type-field>\n <span>${i18next.t('figure.label.figure-type')}</span>\n ${this.row.version > 0\n ? html`<code>${this.row.type}</code><small>${i18next.t('figure.text.type-name-cannot-change')}</small>`\n : html`<input\n .value=${this.row.type ?? ''}\n placeholder=${i18next.t('figure.text.figure-type-placeholder')}\n @change=${this.renameType}\n />`}\n ${this.typeError ? html`<small role=\"alert\">${this.typeError}</small>` : ''}\n </label>\n </figure-properties>\n <figure-verdict\n slot=\"verdict\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureType=${this.row.type}\n @capacity-changed=${(event: CustomEvent) => {\n this.instances = event.detail.instances\n this.budget = event.detail.budget\n }}\n @fit-base-to-parts=${this.fitToParts}\n ></figure-verdict>\n <figure-history-panel\n slot=\"history\"\n .figureId=${view.id}\n .figureState=${this.row.state}\n .figureVersion=${this.row.version}\n .figureType=${this.row.type}\n .dirty=${!!view.dirty}\n .saveFirst=${this.saveBeforeRevert}\n .serverSource=${() => this.session.confirmedSource()}\n @fit-base-to-parts=${this.fitToParts}\n @figure-released=${this.released}\n @figure-reverted=${this.reverted}\n ></figure-history-panel>\n </figure-editor>`\n }\n\n}\n"]}
@@ -1 +1 @@
1
- export default function route(page: string): "figure-modeller" | "figure-list" | undefined;
1
+ export default function route(page: string): "figure-list" | "figure-modeller" | undefined;