@things-factory/figure-ui 10.1.42 → 10.1.44

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.
@@ -114,6 +114,12 @@ export class FigureV3Editor extends LitElement {
114
114
  color: var(--md-sys-color-on-secondary-container);
115
115
  border-color: var(--md-sys-color-primary);
116
116
  }
117
+ label[name-field] {
118
+ margin: 0 0 8px;
119
+ }
120
+ label[name-field] input {
121
+ font-weight: 600;
122
+ }
117
123
  div[tools] {
118
124
  display: flex;
119
125
  flex-wrap: wrap;
@@ -387,6 +393,33 @@ export class FigureV3Editor extends LitElement {
387
393
  ${tool('snap', 'grid_on', 'figure.button.snap', 'figure.text.snap-explained')}
388
394
  </div>`
389
395
  }
396
+ /**
397
+ * A part's name — the V2 part name field. It renames every value the part owns, and what refers to them follows
398
+ * (figure-model's rename-part), as one step of the history.
399
+ */
400
+ private renamePart(input: HTMLInputElement) {
401
+ const name = input.value.trim()
402
+ const from = this.selected
403
+ if (!name || name === from) {
404
+ input.value = from
405
+ return
406
+ }
407
+ try {
408
+ this.session!.edit({ kind: 'rename-part', target: from, name })
409
+ this.selected = name
410
+ this.error = ''
411
+ this.changed()
412
+ } catch (error) {
413
+ input.value = from
414
+ const code = (error as { code?: string })?.code
415
+ this.error =
416
+ code === 'PART_ID'
417
+ ? i18next.t(/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name) ? 'figure.text.part-name-taken' : 'figure.text.part-name-invalid', { name })
418
+ : error instanceof Error
419
+ ? error.message
420
+ : String(error)
421
+ }
422
+ }
390
423
  private changeField(field: { id: string; unit: V3InputChange['unit']; role?: string }, event: Event) {
391
424
  const input = event.target as HTMLInputElement,
392
425
  value = input.valueAsNumber
@@ -461,7 +494,16 @@ export class FigureV3Editor extends LitElement {
461
494
  }}
462
495
  ></figure-v3-canvas>
463
496
  <aside>
464
- <h3>${this.selected || '모델 입력'}</h3>
497
+ ${this.selected && part
498
+ ? html`<label name-field
499
+ ><span>${i18next.t('figure.label.name')}</span
500
+ ><input
501
+ aria-label=${i18next.t('figure.label.name')}
502
+ .value=${this.selected}
503
+ ?disabled=${unavailable}
504
+ @change=${(e: Event) => this.renamePart(e.target as HTMLInputElement)}
505
+ /></label>`
506
+ : html`<h3>${this.selected || '모델 입력'}</h3>`}
465
507
  ${this.renderTools(unavailable)}
466
508
  ${fields.map(field => html`<label><span>${fieldLabel(field.id)} (${field.unit})${field.role === 'state' ? ' · 동작' : ''}</span><input aria-label=${field.id} type="number" step="any" .value=${String(field.value)} ?disabled=${unavailable} @change=${(e: Event) => this.changeField(field, e)} /></label>`)}
467
509
  ${
@@ -38,6 +38,11 @@ export declare class FigureV3Editor extends LitElement {
38
38
  /** A mirrored copy: duplicate, then place the copy on the other side — two steps, as the V2 list makes it. */
39
39
  private mirror;
40
40
  private renderTools;
41
+ /**
42
+ * A part's name — the V2 part name field. It renames every value the part owns, and what refers to them follows
43
+ * (figure-model's rename-part), as one step of the history.
44
+ */
45
+ private renamePart;
41
46
  private changeField;
42
47
  render(): import("lit-html").TemplateResult<1>;
43
48
  }
@@ -120,6 +120,12 @@ let FigureV3Editor = class FigureV3Editor extends LitElement {
120
120
  color: var(--md-sys-color-on-secondary-container);
121
121
  border-color: var(--md-sys-color-primary);
122
122
  }
123
+ label[name-field] {
124
+ margin: 0 0 8px;
125
+ }
126
+ label[name-field] input {
127
+ font-weight: 600;
128
+ }
123
129
  div[tools] {
124
130
  display: flex;
125
131
  flex-wrap: wrap;
@@ -400,6 +406,34 @@ let FigureV3Editor = class FigureV3Editor extends LitElement {
400
406
  ${tool('snap', 'grid_on', 'figure.button.snap', 'figure.text.snap-explained')}
401
407
  </div>`;
402
408
  }
409
+ /**
410
+ * A part's name — the V2 part name field. It renames every value the part owns, and what refers to them follows
411
+ * (figure-model's rename-part), as one step of the history.
412
+ */
413
+ renamePart(input) {
414
+ const name = input.value.trim();
415
+ const from = this.selected;
416
+ if (!name || name === from) {
417
+ input.value = from;
418
+ return;
419
+ }
420
+ try {
421
+ this.session.edit({ kind: 'rename-part', target: from, name });
422
+ this.selected = name;
423
+ this.error = '';
424
+ this.changed();
425
+ }
426
+ catch (error) {
427
+ input.value = from;
428
+ const code = error?.code;
429
+ this.error =
430
+ code === 'PART_ID'
431
+ ? i18next.t(/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name) ? 'figure.text.part-name-taken' : 'figure.text.part-name-invalid', { name })
432
+ : error instanceof Error
433
+ ? error.message
434
+ : String(error);
435
+ }
436
+ }
403
437
  changeField(field, event) {
404
438
  const input = event.target, value = input.valueAsNumber;
405
439
  this.run(() => this.session.apply([
@@ -469,7 +503,16 @@ let FigureV3Editor = class FigureV3Editor extends LitElement {
469
503
  }}
470
504
  ></figure-v3-canvas>
471
505
  <aside>
472
- <h3>${this.selected || '모델 입력'}</h3>
506
+ ${this.selected && part
507
+ ? html `<label name-field
508
+ ><span>${i18next.t('figure.label.name')}</span
509
+ ><input
510
+ aria-label=${i18next.t('figure.label.name')}
511
+ .value=${this.selected}
512
+ ?disabled=${unavailable}
513
+ @change=${(e) => this.renamePart(e.target)}
514
+ /></label>`
515
+ : html `<h3>${this.selected || '모델 입력'}</h3>`}
473
516
  ${this.renderTools(unavailable)}
474
517
  ${fields.map(field => html `<label><span>${fieldLabel(field.id)} (${field.unit})${field.role === 'state' ? ' · 동작' : ''}</span><input aria-label=${field.id} type="number" step="any" .value=${String(field.value)} ?disabled=${unavailable} @change=${(e) => this.changeField(field, e)} /></label>`)}
475
518
  ${part
@@ -1 +1 @@
1
- {"version":3,"file":"v3-editor.js","sourceRoot":"","sources":["../../client/modeller/v3-editor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAuB,MAAM,KAAK,CAAA;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAGlE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,gBAAgB,CAAA;AACvB,OAAO,sBAAsB,CAAA;AAC7B,OAAO,yBAAyB,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAqC,MAAM,eAAe,CAAA;AAC7F,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAkB,MAAM,eAAe,CAAA;AAItF,MAAM,KAAK,GAA2B;IACpC,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,IAAI;IACd,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,IAAI;IACf,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,KAAK;IACb,GAAG,EAAE,IAAI;IACT,WAAW,EAAE,QAAQ;IACrB,YAAY,EAAE,SAAS;IACvB,UAAU,EAAE,QAAQ;CACrB,CAAA;AACD,SAAS,UAAU,CAAC,EAAU;IAC5B,OAAO,EAAE;SACN,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;SACrC,IAAI,CAAC,KAAK,CAAC,CAAA;AAChB,CAAC;AAED,kGAAkG;AAE3F,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QA4JuB,aAAQ,GAAG,CAAC,CAAA;QACvB,aAAQ,GAAG,EAAE,CAAA;QACb,UAAK,GAAG,EAAE,CAAA;QAC3B;;;WAGG;QACc,gBAAW,GAAG,IAAI,GAAG,EAAU,CAAA;QACxC,gBAAW,GAAkB,IAAI,CAAA;IAsR3C,CAAC;aAzbQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyJlB,AAzJY,CAyJZ;IAWS,UAAU,CAAC,QAAwB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAA;QACjC,IAAI,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YACvC,IAAI,CAAC,WAAW,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,CAAA;YACxC,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAA;YAClB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;YACf,IAAI,CAAC,WAAW,GAAG,IAAI,GAAG,EAAE,CAAA;QAC9B,CAAC;QACD,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,QAAQ,CAAC;YAAE,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAA;IAClF,CAAC;IACO,GAAG,CAAC,MAAqB;QAC/B,IAAI,CAAC;YACH,MAAM,EAAE,CAAA;YACR,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;YACf,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACrE,CAAC;IACH,CAAC;IACO,OAAO;QACb,IAAI,CAAC,aAAa,EAAE,CAAA;QACpB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACtF,CAAC;IACO,KAAK,CAAC,IAAI;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAM;QACzB,IAAI,CAAC;YACH;;;;cAIE;YACF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;YAChC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;YACpG,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;YAChD,IAAI,CAAC,OAAO,EAAE,CAAA;YACd,MAAM,OAAO,CAAA;YACb,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACrE,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;IACH,CAAC;IACO,GAAG,CAAC,IAAiC;QAC3C,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAM;QACzB,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE;YACZ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YACjE,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;gBAAE,CAAC,EAAE,CAAA;YACnC,MAAM,MAAM,GAAG,EAAE,EAAE,EAAE,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAA;YACtE,MAAM,IAAI,GACR,IAAI,KAAK,KAAK;gBACZ,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBAC1E,CAAC,CAAC,IAAI,KAAK,UAAU;oBACnB,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;oBAC9D,CAAC,CAAC,IAAI,KAAK,aAAa;wBACtB,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;wBACrF,CAAC,CAAC;4BACE,GAAG,MAAM;4BACT,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;4BAC9B,IAAI,EAAE,QAAQ;4BACd,UAAU,EAAE;gCACV,IAAI,EAAE,CAAC,GAAG;gCACV,KAAK,EAAE,GAAG;gCACV,MAAM,EAAE,CAAC;gCACT,GAAG,EAAE,GAAG;gCACR,WAAW,EAAE,CAAC,EAAE;gCAChB,YAAY,EAAE,EAAE;gCAChB,UAAU,EAAE,GAAG;gCACf,KAAK,EAAE,EAAE;6BACV;yBACF,CAAA;YACX,IAAI,CAAC,OAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACvB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAA;QACzB,CAAC,CAAC,CAAA;IACJ,CAAC;IACD;;;OAGG;IACK,aAAa,CAAC,EAAU;QAC9B,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE;YACZ,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YACpE,IAAI,CAAC,OAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YAC1D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAA;QACtF,CAAC,CAAC,CAAA;IACJ,CAAC;IACO,QAAQ,CAAC,EAAU,EAAE,EAAU;QACrC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;IAC3E,CAAC;IACO,UAAU,CAAC,EAAU;QAC3B,IAAI,CAAC;YACH,IAAI,CAAC,OAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YACvD,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;YACf,IAAI,IAAI,CAAC,QAAQ,KAAK,EAAE;gBAAE,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAA;YAC5C,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,gHAAgH;YAChH,IAAI,CAAC,KAAK;gBACP,KAA2B,EAAE,IAAI,KAAK,iBAAiB;oBACtD,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gCAAgC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;oBAC3D,CAAC,CAAC,KAAK,YAAY,KAAK;wBACtB,CAAC,CAAC,KAAK,CAAC,OAAO;wBACf,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACvB,CAAC;IACH,CAAC;IACO,gBAAgB,CAAC,EAAU;QACjC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACtC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;;YAC5B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACjB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;IACzB,CAAC;IACD,wFAAwF;IAChF,OAAO,CAAC,EAAU;QACxB,MAAM,IAAI,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;QAC9D,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QACrC,IAAI,CAAC;YACH,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IACD,wGAAwG;IAChG,WAAW,CAAC,EAAU,EAAE,MAAmB;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAA;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,KAAe,CAAC,CAAC,CAAC,CAAA;QAC7F,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;QACjE,IAAI,OAAO,CAAC,MAAM;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAA;;YAC3D,IAAI,CAAC,aAAa,EAAE,CAAA;IAC3B,CAAC;IACD,qFAAqF;IAC7E,KAAK,CAAC,IAAe;QAC3B,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;YAChD,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAA;YACrF,IAAI,OAAO,CAAC,MAAM;gBAAE,IAAI,CAAC,OAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAClD,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,8GAA8G;IACtG,MAAM,CAAC,IAAe;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE;YACZ,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAA;YACxB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YACpE,IAAI,CAAC,OAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAA;YAC3E,IAAI,CAAC,IAAI;gBAAE,OAAM;YACjB,IAAI,CAAC,OAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;YACtF,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;QACtB,CAAC,CAAC,CAAA;IACJ,CAAC;IACO,WAAW,CAAC,WAAoB;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,SAAS;YAAE,OAAO,IAAI,CAAA;QACnD,IAAI,KAAc,CAAA;QAClB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,IAAe,EAAE,IAAY,EAAE,KAAa,EAAE,KAAa,EAAE,EAAE,CAC3E,IAAI,CAAA,qBAAqB,WAAW,eAAe,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;mBACjH,IAAI;gBACP,CAAA;QACZ,OAAO,IAAI,CAAA,oBAAoB,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;QACnE,IAAI,CAAC,UAAU,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,yBAAyB,CAAC;QAC9F,IAAI,CAAC,UAAU,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,yBAAyB,CAAC;QAC9F,IAAI,CAAC,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,0BAA0B,CAAC;QAC3F,CAAC,GAAG,EAAE,GAAG,CAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACjC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAc,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;YAClE,OAAO,IAAI,CAAA;sBACG,WAAW,IAAI,CAAC,IAAI;uBACnB,OAAO,CAAC,CAAC,CAAC,uBAAuB,GAAG,IAAI,CAAC;kBAC9C,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,kCAAkC,CAAC;mBACpF,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;;qBAErB,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB;kBAChD,CAAA;QACZ,CAAC,CAAC;QACA,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,oBAAoB,EAAE,4BAA4B,CAAC;WACxE,CAAA;IACT,CAAC;IACO,WAAW,CAAC,KAAiE,EAAE,KAAY;QACjG,MAAM,KAAK,GAAG,KAAK,CAAC,MAA0B,EAC5C,KAAK,GAAG,KAAK,CAAC,aAAa,CAAA;QAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CACZ,IAAI,CAAC,OAAQ,CAAC,KAAK,CAAC;YAClB,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;SACxG,CAAC,CACH,CAAA;QACD,gEAAgE;QAChE,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAE,CAAC,KAAK,CAAC,CAAA;IACvF,CAAC;IACD,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAA;QACjC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAA,qBAAqB,CAAA;QAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAA;QAChF,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC5D,wEAAwE;QACxE,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAA;QAC1F,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAA;QAC7F,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAA;QAChE,OAAO,IAAI,CAAA;;4BAEa,WAAW,IAAI,CAAC,IAAI,CAAC,OAAO,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAC;;;4BAGjF,WAAW,IAAI,CAAC,IAAI,CAAC,OAAO,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAQ,CAAC,IAAI,EAAE,CAAC;;;yBAGpF,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAiB,kBAAkB,CAAC,EAAE,GAAG,EAAE;;;;aAI1F,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK;;4BAE3E,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,IAAI,CAAC,IAAI;;QAEjF,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,mCAAmC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI;;;mBAG9F,IAAI,CAAC,QAAQ,CAAC,GAAG,CACxB,CAAC,CAAC,EAAa,EAAE,CAAC,CAAC;YACjB,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,QAAQ,EAAG,CAAuC,CAAC,KAAK,EAAE,QAAQ,IAAI,EAAE;YACxE,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAClC,SAAS,EAAG,CAA2B,CAAC,OAAO,KAAK,KAAK;SAC1D,CAAC,CACH;sBACW,IAAI,CAAC,QAAQ;oBACf,WAAW;sBACT,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;yBACxC,CAAC,CAAc,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;gCAC1C,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;kCACpD,CAAC,CAAc,EAAE,EAAE,CACzC,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;0BAC1E,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;4BACzD,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;4BAC3D,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;yBACtD,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;;;mBAGtD,eAAe,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAE,IAAuB,CAAC,EAAE,CAAC,CAAC;qBACrG,IAAI,CAAC,OAAO,IAAI,EAAE;qBAClB,IAAI,CAAC,OAAO,IAAI,EAAE;sBACjB,IAAI,CAAC,QAAQ;mBAChB,WAAW,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;8BAClF,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;2BACrE,CAAC,CAAsB,EAAE,EAAE;YAC1C,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAA;QAC1B,CAAC;0BACe,CAAC,CAAqB,EAAE,EAAE;YACxC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAA;QAC/B,CAAC;;;gBAGK,IAAI,CAAC,QAAQ,IAAI,OAAO;YAC5B,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAA,gBAAgB,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,4BAA4B,KAAK,CAAC,EAAE,oCAAoC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,WAAW,YAAY,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,aAAa,CAAC;YAE1S,IAAI;YACF,CAAC,CAAC,IAAI,CAAA;0BACM,IAAI;6BACD,IAAI,CAAC,OAAO,IAAI,EAAE;4BACnB,WAAW;kCACL,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;uCAChE;YACzB,CAAC,CAAC,IACN;YAEE,IAAI,CAAC,QAAQ,CAAC,MAAM;YAClB,CAAC,CAAC,IAAI,CAAA;oBACA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAA,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,EAAE;YACnE,CAAC,CAAC,IACN;;cAEI,CAAA;IACZ,CAAC;;AA9R+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;+CAA2B;AAC9B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;gDAAa;AACvB;IAAhB,KAAK,EAAE;;gDAAsB;AACb;IAAhB,KAAK,EAAE;;6CAAmB;AAKV;IAAhB,KAAK,EAAE;;mDAAwC;AAnKrC,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CA0b1B","sourcesContent":["import { LitElement, html, css, type PropertyValues } from 'lit'\nimport { i18next } from '@operato/i18n'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport type { V3InputChange, V3PrimitiveCreation } from '@hatiolab/figure-model'\nimport type { V3ProductSession } from './v3-product-session.js'\nimport { captureV3Thumbnail } from './v3-thumbnail.js'\nimport { drawableV3Parts } from './v3-renderer.js'\nimport './v3-canvas.js'\nimport './figure-v3-parts.js'\nimport './figure-v3-material.js'\nimport { gizmoChanges, gizmoReachOf, type GizmoReach, type GizmoResult } from './v3-gizmo.js'\nimport { alignChanges, canMirror, mirrorChanges, type AlignTool } from './v3-align.js'\nimport type { V3PartRow } from './figure-v3-parts.js'\nimport type { FigureV3Canvas } from './v3-canvas.js'\n\nconst names: Record<string, string> = {\n position: '위치',\n rotation: '회전',\n size: '크기',\n dimension: '치수',\n width: '너비',\n height: '높이',\n depth: '깊이',\n radius: '반지름',\n length: '길이',\n round: '모서리 반경',\n left: '왼쪽',\n right: '오른쪽',\n bottom: '아랫면',\n top: '윗면',\n openingLeft: '개구부 왼쪽',\n openingRight: '개구부 오른쪽',\n openingTop: '개구부 윗면'\n}\nfunction fieldLabel(id: string) {\n return id\n .split('.')\n .map(p => names[p] ?? p.toUpperCase())\n .join(' · ')\n}\n\n/** Product editor surface. The injected session is shared by page persistence and AI commands. */\n@customElement('figure-v3-editor')\nexport class FigureV3Editor extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n /*\n The editor folds by the width it is given, not the window's. The rail (208 open, 56 closed) and the page's\n 320 side take their share first, so a window rule was wrong whenever either was there: at 945 with the rail\n open the editor had 417 and still drew its 660 of columns, over the side (UX ruling, 2026-09-24).\n */\n container-type: inline-size;\n font: 13px sans-serif;\n color: var(--md-sys-color-on-surface, #22343f);\n }\n header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n flex-wrap: wrap;\n }\n main {\n flex: 1;\n min-height: 0;\n display: grid;\n grid-template-columns: 180px minmax(180px, 1fr) 300px;\n }\n aside {\n padding: 12px;\n overflow: auto;\n background: var(--md-sys-color-surface, #fff);\n }\n figure-v3-parts {\n min-height: 0;\n }\n aside:last-child {\n border-left: 1px solid var(--md-sys-color-outline-variant);\n }\n h3 {\n font-size: var(--ui-t-body, 13px);\n margin: 22px 0 10px;\n }\n h3:first-child {\n margin-top: 0;\n }\n button,\n input,\n select {\n font: inherit;\n box-sizing: border-box;\n height: 34px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: var(--md-sys-color-surface, #fff);\n color: inherit;\n padding: 0 8px;\n min-width: 0;\n }\n button {\n cursor: pointer;\n }\n button:disabled {\n opacity: 0.45;\n cursor: default;\n }\n button[aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n border-color: var(--md-sys-color-primary);\n }\n div[tools] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin: 0 0 8px;\n }\n div[tools] button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 34px;\n padding: 0;\n }\n div[tools] md-icon {\n font-size: 18px;\n width: 18px;\n height: 18px;\n }\n .parts {\n display: grid;\n gap: 6px;\n }\n .recipes {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 6px;\n }\n label {\n display: grid;\n grid-template-columns: 112px minmax(0, 1fr);\n align-items: center;\n gap: 8px;\n margin: 8px 0;\n }\n label span {\n overflow-wrap: anywhere;\n }\n .error {\n padding: 10px;\n background: var(--md-sys-color-error-container);\n color: var(--md-sys-color-on-error-container);\n white-space: pre-wrap;\n }\n .status {\n margin-left: auto;\n }\n .hint {\n color: var(--md-sys-color-on-surface-variant);\n line-height: 1.5;\n }\n /* 660 and over: parts | canvas | inputs. Below that the sum of the columns' least widths no longer fits. */\n @container (max-width: 659px) {\n main {\n grid-template-columns: 140px minmax(160px, 1fr);\n }\n aside:last-child {\n grid-column: 1/-1;\n max-height: 260px;\n border-left: none;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n }\n /* Under 420: one column. The canvas first — seeing what is being made comes before the lists — and main scrolls. */\n @container (max-width: 419px) {\n main {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: minmax(240px, 45%);\n grid-auto-rows: auto;\n overflow-y: auto;\n }\n figure-v3-canvas {\n order: -1;\n }\n figure-v3-parts {\n border-right: none;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n aside:last-child {\n max-height: none;\n }\n }\n `\n @property({ attribute: false }) session?: V3ProductSession\n @property({ type: Number }) revision = 0\n @state() private selected = ''\n @state() private error = ''\n /**\n * Parts hidden on the workbench — the author's view while working, not the asset's visibility. Not saved, as in\n * the V2 modeller: a part hidden here is still in the figure, still checked, still drawn on a board.\n */\n @state() private hiddenParts = new Set<string>()\n private lastContext: string | null = null\n protected willUpdate(_changed: PropertyValues) {\n const view = this.session?.read()\n if (view?.context !== this.lastContext) {\n this.lastContext = view?.context ?? null\n this.selected = ''\n this.error = ''\n this.hiddenParts = new Set()\n }\n if (view && !view.geometry.some(g => g.id === this.selected)) this.selected = ''\n }\n private run(action: () => unknown) {\n try {\n action()\n this.error = ''\n this.changed()\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n }\n }\n private changed() {\n this.requestUpdate()\n this.dispatchEvent(new CustomEvent('v3-changed', { bubbles: true, composed: true }))\n }\n private async save() {\n if (!this.session) return\n try {\n /*\n The card's picture is taken before the save starts, from a canvas of its own at the card's size —\n not from the one on screen, which stands wherever the author left it turned. A picture that cannot\n be taken is not an error: the save goes on without it (see `v3-thumbnail.ts`).\n */\n const view = this.session.read()\n const thumbnail = await captureV3Thumbnail(drawableV3Parts(view.geometry ?? []), view.palette ?? {})\n const promise = this.session.save({ thumbnail })\n this.changed()\n await promise\n this.error = ''\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n } finally {\n this.changed()\n }\n }\n private add(kind: V3PrimitiveCreation['kind']) {\n if (!this.session) return\n this.run(() => {\n const ids = new Set(this.session!.read().geometry.map(g => g.id))\n let n = 1\n while (ids.has(`${kind}-${n}`)) n++\n const common = { id: `${kind}-${n}`, position: { x: 0, y: 50, z: 0 } }\n const part: V3PrimitiveCreation =\n kind === 'box'\n ? { ...common, kind, dimensions: { width: 100, height: 100, depth: 100 } }\n : kind === 'cylinder'\n ? { ...common, kind, dimensions: { radius: 50, length: 100 } }\n : kind === 'rounded-box'\n ? { ...common, kind, dimensions: { width: 100, height: 100, depth: 100, round: 10 } }\n : {\n ...common,\n position: { x: 0, y: 0, z: 0 },\n kind: 'portal',\n dimensions: {\n left: -100,\n right: 100,\n bottom: 0,\n top: 200,\n openingLeft: -70,\n openingRight: 70,\n openingTop: 170,\n depth: 30\n }\n }\n this.session!.add(part)\n this.selected = part.id\n })\n }\n /**\n * The list's own operations, as V3 part edits — each one step of the session's history. The copy and the moved\n * part stay selected, as they do in the V2 list.\n */\n private duplicatePart(id: string) {\n this.run(() => {\n const before = new Set(this.session!.read().geometry.map(g => g.id))\n this.session!.edit({ kind: 'duplicate-part', target: id })\n this.selected = this.session!.read().geometry.find(g => !before.has(g.id))?.id ?? id\n })\n }\n private movePart(id: string, to: number) {\n this.run(() => this.session!.edit({ kind: 'move-part', target: id, to }))\n }\n private removePart(id: string) {\n try {\n this.session!.edit({ kind: 'remove-part', target: id })\n this.error = ''\n if (this.selected === id) this.selected = ''\n this.changed()\n } catch (error) {\n /* The refusal a person meets most: another part stands on this one or follows its size. Said in their words. */\n this.error =\n (error as { code?: string })?.code === 'PART_REFERENCED'\n ? i18next.t('figure.text.part-is-referenced', { part: id })\n : error instanceof Error\n ? error.message\n : String(error)\n }\n }\n private toggleHiddenPart(id: string) {\n const next = new Set(this.hiddenParts)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n this.hiddenParts = next\n }\n /** What the gizmo may write for the selected part — read off the asset being edited. */\n private reachOf(id: string): GizmoReach {\n const none = { translate: false, rotate: false, scale: false }\n if (!id || !this.session) return none\n try {\n return gizmoReachOf(JSON.parse(this.session.source()), id)\n } catch {\n return none\n }\n }\n /** A drag, written back as the part's own inputs — one step of the history, like typing the numbers. */\n private transformed(id: string, result: GizmoResult) {\n const view = this.session!.read()\n const sizes = Object.fromEntries(view.fields.map(field => [field.id, field.value as number]))\n const changes = gizmoChanges(id, this.reachOf(id), result, sizes)\n if (changes.length) this.run(() => this.session!.apply(changes))\n else this.requestUpdate()\n }\n /** 맞추기 — one tool, one step of the history, through the same inputs as the gizmo. */\n private align(tool: AlignTool) {\n this.run(() => {\n const asset = JSON.parse(this.session!.source())\n const changes = alignChanges(asset, this.selected, this.reachOf(this.selected), tool)\n if (changes.length) this.session!.apply(changes)\n })\n }\n /** A mirrored copy: duplicate, then place the copy on the other side — two steps, as the V2 list makes it. */\n private mirror(axis: 'x' | 'z') {\n this.run(() => {\n const id = this.selected\n const before = new Set(this.session!.read().geometry.map(g => g.id))\n this.session!.edit({ kind: 'duplicate-part', target: id })\n const copy = this.session!.read().geometry.find(g => !before.has(g.id))?.id\n if (!copy) return\n this.session!.apply(mirrorChanges(JSON.parse(this.session!.source()), id, copy, axis))\n this.selected = copy\n })\n }\n private renderTools(unavailable: boolean) {\n const reach = this.reachOf(this.selected)\n if (!this.selected || !reach.translate) return null\n let asset: unknown\n try {\n asset = JSON.parse(this.session!.source())\n } catch {\n return null\n }\n const tool = (name: AlignTool, icon: string, label: string, title: string) =>\n html`<button ?disabled=${unavailable} aria-label=${i18next.t(label)} title=${i18next.t(title)} @click=${() => this.align(name)}>\n <md-icon>${icon}</md-icon>\n </button>`\n return html`<div tools title=${i18next.t('figure.text.tools-explained')}>\n ${tool('centre-x', 'horizontal_distribute', 'figure.button.center-x', 'figure.text.center-on-x')}\n ${tool('centre-z', 'vertical_align_center', 'figure.button.center-z', 'figure.text.center-on-z')}\n ${tool('floor', 'vertical_align_bottom', 'figure.button.to-floor', 'figure.text.put-on-floor')}\n ${(['x', 'z'] as const).map(axis => {\n const able = canMirror(asset as never, this.selected, axis, reach)\n return html`<button\n ?disabled=${unavailable || !able}\n aria-label=${i18next.t('figure.button.mirror-' + axis)}\n title=${i18next.t(able ? 'figure.text.mirror-explained' : 'figure.text.mirror-would-overlap')}\n @click=${() => this.mirror(axis)}\n >\n <md-icon>${axis === 'x' ? 'flip' : 'flip_camera_android'}</md-icon>\n </button>`\n })}\n ${tool('snap', 'grid_on', 'figure.button.snap', 'figure.text.snap-explained')}\n </div>`\n }\n private changeField(field: { id: string; unit: V3InputChange['unit']; role?: string }, event: Event) {\n const input = event.target as HTMLInputElement,\n value = input.valueAsNumber\n this.run(() =>\n this.session!.apply([\n { kind: field.role === 'state' ? 'set-state' : 'set-design', input: field.id, unit: field.unit, value }\n ])\n )\n // Rejected edits must also restore the displayed numeric value.\n input.value = String(this.session!.read().fields.find(i => i.id === field.id)!.value)\n }\n render() {\n const view = this.session?.read()\n if (!view) return html`<p>모델을 여는 중입니다.</p>`\n const unavailable = view.loading || !!view.error || view.saving || !view.context\n const part = view.geometry.find(g => g.id === this.selected)\n // Shared/root parameters remain available even when a part is selected.\n const own = view.fields.filter(i => this.selected && i.id.startsWith(this.selected + '.'))\n const common = view.fields.filter(i => !view.geometry.some(g => i.id.startsWith(g.id + '.')))\n const fields = this.selected ? [...own, ...common] : view.fields\n return html` <header>\n <strong>V3 모델링</strong>\n <button ?disabled=${unavailable || !view.canUndo} @click=${() => this.run(() => this.session!.undo())}>\n 실행 취소\n </button>\n <button ?disabled=${unavailable || !view.canRedo} @click=${() => this.run(() => this.session!.redo())}>\n 다시 실행\n </button>\n <button @click=${() => this.renderRoot.querySelector<FigureV3Canvas>('figure-v3-canvas')?.fit()}>\n 전체 보기\n </button>\n <span class=\"status\" role=\"status\"\n >${view.loading ? '불러오는 중' : view.saving ? '저장 중' : view.dirty || !view.id ? '저장 안 됨' : '저장됨'}</span\n >\n <button ?disabled=${unavailable || (!view.dirty && !!view.id)} @click=${this.save}>저장</button>\n </header>\n ${this.error || view.error ? html`<div role=\"alert\" class=\"error\">${this.error || view.error}</div>` : null}\n <main>\n <figure-v3-parts\n .parts=${view.geometry.map(\n (g): V3PartRow => ({\n id: g.id,\n provider: (g as { shape?: { provider?: string } }).shape?.provider ?? '',\n hidden: this.hiddenParts.has(g.id),\n invisible: (g as { visible?: boolean }).visible === false\n })\n )}\n .selected=${this.selected}\n ?locked=${unavailable}\n @add-part=${(e: CustomEvent) => this.add(e.detail.kind)}\n @select-part=${(e: CustomEvent) => (this.selected = e.detail.id)}\n @toggle-part-hidden=${(e: CustomEvent) => this.toggleHiddenPart(e.detail.id)}\n @set-all-parts-hidden=${(e: CustomEvent) =>\n (this.hiddenParts = e.detail.hidden ? new Set(view.geometry.map(g => g.id)) : new Set())}\n @move-part-up=${(e: CustomEvent) => this.movePart(e.detail.id, e.detail.to)}\n @move-part-down=${(e: CustomEvent) => this.movePart(e.detail.id, e.detail.to)}\n @duplicate-part=${(e: CustomEvent) => this.duplicatePart(e.detail.id)}\n @remove-part=${(e: CustomEvent) => this.removePart(e.detail.id)}\n ></figure-v3-parts>\n <figure-v3-canvas\n .parts=${drawableV3Parts(view.geometry ?? []).filter(body => !this.hiddenParts.has((body as { id: string }).id))}\n .palette=${view.palette ?? {}}\n .context=${view.context ?? ''}\n .selected=${this.selected}\n .reach=${unavailable ? { translate: false, rotate: false, scale: false } : this.reachOf(this.selected)}\n @part-transformed=${(e: CustomEvent) => this.transformed(e.detail.id, e.detail.result)}\n @part-selected=${(e: CustomEvent<string>) => {\n this.selected = e.detail\n }}\n @render-error=${(e: CustomEvent<Error>) => {\n this.error = e.detail.message\n }}\n ></figure-v3-canvas>\n <aside>\n <h3>${this.selected || '모델 입력'}</h3>\n ${this.renderTools(unavailable)}\n ${fields.map(field => html`<label><span>${fieldLabel(field.id)} (${field.unit})${field.role === 'state' ? ' · 동작' : ''}</span><input aria-label=${field.id} type=\"number\" step=\"any\" .value=${String(field.value)} ?disabled=${unavailable} @change=${(e: Event) => this.changeField(field, e)} /></label>`)}\n ${\n part\n ? html`<figure-v3-material\n .body=${part}\n .palette=${view.palette ?? {}}\n ?locked=${unavailable}\n @v3-part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}\n ></figure-v3-material>`\n : null\n }\n ${\n view.bindings.length\n ? html`<h3>배치 연결점</h3>\n ${view.bindings.map(b => html`<p>${b.label} · ${b.role}</p>`)}`\n : null\n }\n </aside>\n </main>`\n }\n}\n"]}
1
+ {"version":3,"file":"v3-editor.js","sourceRoot":"","sources":["../../client/modeller/v3-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css, type PropertyValues } from 'lit'\nimport { i18next } from '@operato/i18n'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport type { V3InputChange, V3PrimitiveCreation } from '@hatiolab/figure-model'\nimport type { V3ProductSession } from './v3-product-session.js'\nimport { captureV3Thumbnail } from './v3-thumbnail.js'\nimport { drawableV3Parts } from './v3-renderer.js'\nimport './v3-canvas.js'\nimport './figure-v3-parts.js'\nimport './figure-v3-material.js'\nimport { gizmoChanges, gizmoReachOf, type GizmoReach, type GizmoResult } from './v3-gizmo.js'\nimport { alignChanges, canMirror, mirrorChanges, type AlignTool } from './v3-align.js'\nimport type { V3PartRow } from './figure-v3-parts.js'\nimport type { FigureV3Canvas } from './v3-canvas.js'\n\nconst names: Record<string, string> = {\n position: '위치',\n rotation: '회전',\n size: '크기',\n dimension: '치수',\n width: '너비',\n height: '높이',\n depth: '깊이',\n radius: '반지름',\n length: '길이',\n round: '모서리 반경',\n left: '왼쪽',\n right: '오른쪽',\n bottom: '아랫면',\n top: '윗면',\n openingLeft: '개구부 왼쪽',\n openingRight: '개구부 오른쪽',\n openingTop: '개구부 윗면'\n}\nfunction fieldLabel(id: string) {\n return id\n .split('.')\n .map(p => names[p] ?? p.toUpperCase())\n .join(' · ')\n}\n\n/** Product editor surface. The injected session is shared by page persistence and AI commands. */\n@customElement('figure-v3-editor')\nexport class FigureV3Editor extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n /*\n The editor folds by the width it is given, not the window's. The rail (208 open, 56 closed) and the page's\n 320 side take their share first, so a window rule was wrong whenever either was there: at 945 with the rail\n open the editor had 417 and still drew its 660 of columns, over the side (UX ruling, 2026-09-24).\n */\n container-type: inline-size;\n font: 13px sans-serif;\n color: var(--md-sys-color-on-surface, #22343f);\n }\n header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n flex-wrap: wrap;\n }\n main {\n flex: 1;\n min-height: 0;\n display: grid;\n grid-template-columns: 180px minmax(180px, 1fr) 300px;\n }\n aside {\n padding: 12px;\n overflow: auto;\n background: var(--md-sys-color-surface, #fff);\n }\n figure-v3-parts {\n min-height: 0;\n }\n aside:last-child {\n border-left: 1px solid var(--md-sys-color-outline-variant);\n }\n h3 {\n font-size: var(--ui-t-body, 13px);\n margin: 22px 0 10px;\n }\n h3:first-child {\n margin-top: 0;\n }\n button,\n input,\n select {\n font: inherit;\n box-sizing: border-box;\n height: 34px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: var(--md-sys-color-surface, #fff);\n color: inherit;\n padding: 0 8px;\n min-width: 0;\n }\n button {\n cursor: pointer;\n }\n button:disabled {\n opacity: 0.45;\n cursor: default;\n }\n button[aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n border-color: var(--md-sys-color-primary);\n }\n label[name-field] {\n margin: 0 0 8px;\n }\n label[name-field] input {\n font-weight: 600;\n }\n div[tools] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin: 0 0 8px;\n }\n div[tools] button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 34px;\n padding: 0;\n }\n div[tools] md-icon {\n font-size: 18px;\n width: 18px;\n height: 18px;\n }\n .parts {\n display: grid;\n gap: 6px;\n }\n .recipes {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 6px;\n }\n label {\n display: grid;\n grid-template-columns: 112px minmax(0, 1fr);\n align-items: center;\n gap: 8px;\n margin: 8px 0;\n }\n label span {\n overflow-wrap: anywhere;\n }\n .error {\n padding: 10px;\n background: var(--md-sys-color-error-container);\n color: var(--md-sys-color-on-error-container);\n white-space: pre-wrap;\n }\n .status {\n margin-left: auto;\n }\n .hint {\n color: var(--md-sys-color-on-surface-variant);\n line-height: 1.5;\n }\n /* 660 and over: parts | canvas | inputs. Below that the sum of the columns' least widths no longer fits. */\n @container (max-width: 659px) {\n main {\n grid-template-columns: 140px minmax(160px, 1fr);\n }\n aside:last-child {\n grid-column: 1/-1;\n max-height: 260px;\n border-left: none;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n }\n /* Under 420: one column. The canvas first — seeing what is being made comes before the lists — and main scrolls. */\n @container (max-width: 419px) {\n main {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: minmax(240px, 45%);\n grid-auto-rows: auto;\n overflow-y: auto;\n }\n figure-v3-canvas {\n order: -1;\n }\n figure-v3-parts {\n border-right: none;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n aside:last-child {\n max-height: none;\n }\n }\n `\n @property({ attribute: false }) session?: V3ProductSession\n @property({ type: Number }) revision = 0\n @state() private selected = ''\n @state() private error = ''\n /**\n * Parts hidden on the workbench — the author's view while working, not the asset's visibility. Not saved, as in\n * the V2 modeller: a part hidden here is still in the figure, still checked, still drawn on a board.\n */\n @state() private hiddenParts = new Set<string>()\n private lastContext: string | null = null\n protected willUpdate(_changed: PropertyValues) {\n const view = this.session?.read()\n if (view?.context !== this.lastContext) {\n this.lastContext = view?.context ?? null\n this.selected = ''\n this.error = ''\n this.hiddenParts = new Set()\n }\n if (view && !view.geometry.some(g => g.id === this.selected)) this.selected = ''\n }\n private run(action: () => unknown) {\n try {\n action()\n this.error = ''\n this.changed()\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n }\n }\n private changed() {\n this.requestUpdate()\n this.dispatchEvent(new CustomEvent('v3-changed', { bubbles: true, composed: true }))\n }\n private async save() {\n if (!this.session) return\n try {\n /*\n The card's picture is taken before the save starts, from a canvas of its own at the card's size —\n not from the one on screen, which stands wherever the author left it turned. A picture that cannot\n be taken is not an error: the save goes on without it (see `v3-thumbnail.ts`).\n */\n const view = this.session.read()\n const thumbnail = await captureV3Thumbnail(drawableV3Parts(view.geometry ?? []), view.palette ?? {})\n const promise = this.session.save({ thumbnail })\n this.changed()\n await promise\n this.error = ''\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n } finally {\n this.changed()\n }\n }\n private add(kind: V3PrimitiveCreation['kind']) {\n if (!this.session) return\n this.run(() => {\n const ids = new Set(this.session!.read().geometry.map(g => g.id))\n let n = 1\n while (ids.has(`${kind}-${n}`)) n++\n const common = { id: `${kind}-${n}`, position: { x: 0, y: 50, z: 0 } }\n const part: V3PrimitiveCreation =\n kind === 'box'\n ? { ...common, kind, dimensions: { width: 100, height: 100, depth: 100 } }\n : kind === 'cylinder'\n ? { ...common, kind, dimensions: { radius: 50, length: 100 } }\n : kind === 'rounded-box'\n ? { ...common, kind, dimensions: { width: 100, height: 100, depth: 100, round: 10 } }\n : {\n ...common,\n position: { x: 0, y: 0, z: 0 },\n kind: 'portal',\n dimensions: {\n left: -100,\n right: 100,\n bottom: 0,\n top: 200,\n openingLeft: -70,\n openingRight: 70,\n openingTop: 170,\n depth: 30\n }\n }\n this.session!.add(part)\n this.selected = part.id\n })\n }\n /**\n * The list's own operations, as V3 part edits — each one step of the session's history. The copy and the moved\n * part stay selected, as they do in the V2 list.\n */\n private duplicatePart(id: string) {\n this.run(() => {\n const before = new Set(this.session!.read().geometry.map(g => g.id))\n this.session!.edit({ kind: 'duplicate-part', target: id })\n this.selected = this.session!.read().geometry.find(g => !before.has(g.id))?.id ?? id\n })\n }\n private movePart(id: string, to: number) {\n this.run(() => this.session!.edit({ kind: 'move-part', target: id, to }))\n }\n private removePart(id: string) {\n try {\n this.session!.edit({ kind: 'remove-part', target: id })\n this.error = ''\n if (this.selected === id) this.selected = ''\n this.changed()\n } catch (error) {\n /* The refusal a person meets most: another part stands on this one or follows its size. Said in their words. */\n this.error =\n (error as { code?: string })?.code === 'PART_REFERENCED'\n ? i18next.t('figure.text.part-is-referenced', { part: id })\n : error instanceof Error\n ? error.message\n : String(error)\n }\n }\n private toggleHiddenPart(id: string) {\n const next = new Set(this.hiddenParts)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n this.hiddenParts = next\n }\n /** What the gizmo may write for the selected part — read off the asset being edited. */\n private reachOf(id: string): GizmoReach {\n const none = { translate: false, rotate: false, scale: false }\n if (!id || !this.session) return none\n try {\n return gizmoReachOf(JSON.parse(this.session.source()), id)\n } catch {\n return none\n }\n }\n /** A drag, written back as the part's own inputs — one step of the history, like typing the numbers. */\n private transformed(id: string, result: GizmoResult) {\n const view = this.session!.read()\n const sizes = Object.fromEntries(view.fields.map(field => [field.id, field.value as number]))\n const changes = gizmoChanges(id, this.reachOf(id), result, sizes)\n if (changes.length) this.run(() => this.session!.apply(changes))\n else this.requestUpdate()\n }\n /** 맞추기 — one tool, one step of the history, through the same inputs as the gizmo. */\n private align(tool: AlignTool) {\n this.run(() => {\n const asset = JSON.parse(this.session!.source())\n const changes = alignChanges(asset, this.selected, this.reachOf(this.selected), tool)\n if (changes.length) this.session!.apply(changes)\n })\n }\n /** A mirrored copy: duplicate, then place the copy on the other side — two steps, as the V2 list makes it. */\n private mirror(axis: 'x' | 'z') {\n this.run(() => {\n const id = this.selected\n const before = new Set(this.session!.read().geometry.map(g => g.id))\n this.session!.edit({ kind: 'duplicate-part', target: id })\n const copy = this.session!.read().geometry.find(g => !before.has(g.id))?.id\n if (!copy) return\n this.session!.apply(mirrorChanges(JSON.parse(this.session!.source()), id, copy, axis))\n this.selected = copy\n })\n }\n private renderTools(unavailable: boolean) {\n const reach = this.reachOf(this.selected)\n if (!this.selected || !reach.translate) return null\n let asset: unknown\n try {\n asset = JSON.parse(this.session!.source())\n } catch {\n return null\n }\n const tool = (name: AlignTool, icon: string, label: string, title: string) =>\n html`<button ?disabled=${unavailable} aria-label=${i18next.t(label)} title=${i18next.t(title)} @click=${() => this.align(name)}>\n <md-icon>${icon}</md-icon>\n </button>`\n return html`<div tools title=${i18next.t('figure.text.tools-explained')}>\n ${tool('centre-x', 'horizontal_distribute', 'figure.button.center-x', 'figure.text.center-on-x')}\n ${tool('centre-z', 'vertical_align_center', 'figure.button.center-z', 'figure.text.center-on-z')}\n ${tool('floor', 'vertical_align_bottom', 'figure.button.to-floor', 'figure.text.put-on-floor')}\n ${(['x', 'z'] as const).map(axis => {\n const able = canMirror(asset as never, this.selected, axis, reach)\n return html`<button\n ?disabled=${unavailable || !able}\n aria-label=${i18next.t('figure.button.mirror-' + axis)}\n title=${i18next.t(able ? 'figure.text.mirror-explained' : 'figure.text.mirror-would-overlap')}\n @click=${() => this.mirror(axis)}\n >\n <md-icon>${axis === 'x' ? 'flip' : 'flip_camera_android'}</md-icon>\n </button>`\n })}\n ${tool('snap', 'grid_on', 'figure.button.snap', 'figure.text.snap-explained')}\n </div>`\n }\n /**\n * A part's name — the V2 part name field. It renames every value the part owns, and what refers to them follows\n * (figure-model's rename-part), as one step of the history.\n */\n private renamePart(input: HTMLInputElement) {\n const name = input.value.trim()\n const from = this.selected\n if (!name || name === from) {\n input.value = from\n return\n }\n try {\n this.session!.edit({ kind: 'rename-part', target: from, name })\n this.selected = name\n this.error = ''\n this.changed()\n } catch (error) {\n input.value = from\n const code = (error as { code?: string })?.code\n this.error =\n code === 'PART_ID'\n ? i18next.t(/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name) ? 'figure.text.part-name-taken' : 'figure.text.part-name-invalid', { name })\n : error instanceof Error\n ? error.message\n : String(error)\n }\n }\n private changeField(field: { id: string; unit: V3InputChange['unit']; role?: string }, event: Event) {\n const input = event.target as HTMLInputElement,\n value = input.valueAsNumber\n this.run(() =>\n this.session!.apply([\n { kind: field.role === 'state' ? 'set-state' : 'set-design', input: field.id, unit: field.unit, value }\n ])\n )\n // Rejected edits must also restore the displayed numeric value.\n input.value = String(this.session!.read().fields.find(i => i.id === field.id)!.value)\n }\n render() {\n const view = this.session?.read()\n if (!view) return html`<p>모델을 여는 중입니다.</p>`\n const unavailable = view.loading || !!view.error || view.saving || !view.context\n const part = view.geometry.find(g => g.id === this.selected)\n // Shared/root parameters remain available even when a part is selected.\n const own = view.fields.filter(i => this.selected && i.id.startsWith(this.selected + '.'))\n const common = view.fields.filter(i => !view.geometry.some(g => i.id.startsWith(g.id + '.')))\n const fields = this.selected ? [...own, ...common] : view.fields\n return html` <header>\n <strong>V3 모델링</strong>\n <button ?disabled=${unavailable || !view.canUndo} @click=${() => this.run(() => this.session!.undo())}>\n 실행 취소\n </button>\n <button ?disabled=${unavailable || !view.canRedo} @click=${() => this.run(() => this.session!.redo())}>\n 다시 실행\n </button>\n <button @click=${() => this.renderRoot.querySelector<FigureV3Canvas>('figure-v3-canvas')?.fit()}>\n 전체 보기\n </button>\n <span class=\"status\" role=\"status\"\n >${view.loading ? '불러오는 중' : view.saving ? '저장 중' : view.dirty || !view.id ? '저장 안 됨' : '저장됨'}</span\n >\n <button ?disabled=${unavailable || (!view.dirty && !!view.id)} @click=${this.save}>저장</button>\n </header>\n ${this.error || view.error ? html`<div role=\"alert\" class=\"error\">${this.error || view.error}</div>` : null}\n <main>\n <figure-v3-parts\n .parts=${view.geometry.map(\n (g): V3PartRow => ({\n id: g.id,\n provider: (g as { shape?: { provider?: string } }).shape?.provider ?? '',\n hidden: this.hiddenParts.has(g.id),\n invisible: (g as { visible?: boolean }).visible === false\n })\n )}\n .selected=${this.selected}\n ?locked=${unavailable}\n @add-part=${(e: CustomEvent) => this.add(e.detail.kind)}\n @select-part=${(e: CustomEvent) => (this.selected = e.detail.id)}\n @toggle-part-hidden=${(e: CustomEvent) => this.toggleHiddenPart(e.detail.id)}\n @set-all-parts-hidden=${(e: CustomEvent) =>\n (this.hiddenParts = e.detail.hidden ? new Set(view.geometry.map(g => g.id)) : new Set())}\n @move-part-up=${(e: CustomEvent) => this.movePart(e.detail.id, e.detail.to)}\n @move-part-down=${(e: CustomEvent) => this.movePart(e.detail.id, e.detail.to)}\n @duplicate-part=${(e: CustomEvent) => this.duplicatePart(e.detail.id)}\n @remove-part=${(e: CustomEvent) => this.removePart(e.detail.id)}\n ></figure-v3-parts>\n <figure-v3-canvas\n .parts=${drawableV3Parts(view.geometry ?? []).filter(body => !this.hiddenParts.has((body as { id: string }).id))}\n .palette=${view.palette ?? {}}\n .context=${view.context ?? ''}\n .selected=${this.selected}\n .reach=${unavailable ? { translate: false, rotate: false, scale: false } : this.reachOf(this.selected)}\n @part-transformed=${(e: CustomEvent) => this.transformed(e.detail.id, e.detail.result)}\n @part-selected=${(e: CustomEvent<string>) => {\n this.selected = e.detail\n }}\n @render-error=${(e: CustomEvent<Error>) => {\n this.error = e.detail.message\n }}\n ></figure-v3-canvas>\n <aside>\n ${this.selected && part\n ? html`<label name-field\n ><span>${i18next.t('figure.label.name')}</span\n ><input\n aria-label=${i18next.t('figure.label.name')}\n .value=${this.selected}\n ?disabled=${unavailable}\n @change=${(e: Event) => this.renamePart(e.target as HTMLInputElement)}\n /></label>`\n : html`<h3>${this.selected || '모델 입력'}</h3>`}\n ${this.renderTools(unavailable)}\n ${fields.map(field => html`<label><span>${fieldLabel(field.id)} (${field.unit})${field.role === 'state' ? ' · 동작' : ''}</span><input aria-label=${field.id} type=\"number\" step=\"any\" .value=${String(field.value)} ?disabled=${unavailable} @change=${(e: Event) => this.changeField(field, e)} /></label>`)}\n ${\n part\n ? html`<figure-v3-material\n .body=${part}\n .palette=${view.palette ?? {}}\n ?locked=${unavailable}\n @v3-part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}\n ></figure-v3-material>`\n : null\n }\n ${\n view.bindings.length\n ? html`<h3>배치 연결점</h3>\n ${view.bindings.map(b => html`<p>${b.label} · ${b.role}</p>`)}`\n : null\n }\n </aside>\n </main>`\n }\n}\n"]}
@@ -1 +1 @@
1
- export default function route(page: string): "figure-list" | "figure-modeller" | "figure-v3-modeller" | undefined;
1
+ export default function route(page: string): "figure-v3-modeller" | "figure-modeller" | "figure-list" | undefined;