@things-factory/figure-ui 10.1.176 → 10.1.177

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.
@@ -265,13 +265,11 @@ export const EditorStyles = css`
265
265
  display: grid;
266
266
  gap: 6px;
267
267
  }
268
- /* A folded group (capability, 「더 보기」): its title is the fold's handle, at the title's size. */
269
- section[more] > details > summary,
270
- section[capability] > details > summary {
268
+ /* The folded 「더 보기」: its title is the fold's handle, at the title's size. */
269
+ section[more] > details > summary {
271
270
  padding: 0;
272
271
  }
273
- section[more] > details > summary > h3,
274
- section[capability] > details > summary > h3 {
272
+ section[more] > details > summary > h3 {
275
273
  margin: 0;
276
274
  }
277
275
  div[more-body] {
@@ -72,12 +72,13 @@ import { groupMenu, GroupMenuStyles } from './group-menu.js'
72
72
 
73
73
 
74
74
  /**
75
- * The side's four tabs (chief architect 2026-10-16): properties, motion, its data (ADR-0051), the verdict. What it does
76
- * (its places) is a group of the properties; the view is a menu over the canvas and the release history opens from the
77
- * bar, since neither is about the part chosen. The verdict gives way to properties when a part is chosen; a tab about
78
- * parts keeps showing the newly chosen part.
75
+ * The side's six tabs (user 2026-10-17: up to six): properties -- the part's shape, place and look; 「반응형」 -- how it
76
+ * answers a size it is placed at (its size rules, a position that follows the size, its row; the figure's range);
77
+ * motion; what it does (its places); its data (ADR-0051); the verdict. The view is a menu over the canvas and the
78
+ * release history opens from the bar, since neither is about the part chosen. The verdict gives way to properties when
79
+ * a part is chosen; a tab about parts keeps showing the newly chosen part.
79
80
  */
80
- type SideTab = 'properties' | 'motion' | 'data' | 'verdict'
81
+ type SideTab = 'properties' | 'response' | 'motion' | 'capability' | 'data' | 'verdict'
81
82
 
82
83
  /** What opens over the work from the bar: the view settings or the release history, one at a time. */
83
84
  type BarOverlay = 'view' | 'history'
@@ -134,8 +135,6 @@ export class FigureEditor extends LitElement {
134
135
  @state() private tab: SideTab = 'properties'
135
136
  /** The view settings or the release history, open over the work from the bar. */
136
137
  @state() private overlay?: BarOverlay
137
- /** The capability group open: the canvas marks the places only while their group is in sight. */
138
- @state() private capabilityOpen = false
139
138
  /** 「더 보기」 open, as this person left it last (chief architect 2026-10-16: kept per person). */
140
139
  @state() private moreOpen = FigureEditor.storedMoreOpen()
141
140
  private static storedMoreOpen(): boolean {
@@ -199,7 +198,7 @@ export class FigureEditor extends LitElement {
199
198
  session: () => this.session,
200
199
  selected: () => this.selected,
201
200
  shown: () => this.shown(),
202
- formsInSight: () => this.middle === 'edit' && this.tab === 'properties',
201
+ formsInSight: () => this.middle === 'edit' && (this.tab === 'properties' || this.tab === 'response'),
203
202
  said: error => this.said(error)
204
203
  })
205
204
  /**
@@ -752,12 +751,14 @@ export class FigureEditor extends LitElement {
752
751
  }
753
752
  }
754
753
  /** Size response, fastening and motion for the selected part, as authoring commands. */
755
- private renderLinks(unavailable: boolean, section: 'shape' | 'places' | 'motion', slotted: unknown = nothing) {
754
+ private renderLinks(unavailable: boolean, section: 'shape' | 'response' | 'places' | 'motion', slotted: unknown = nothing) {
756
755
  const shown = this.shown()
757
756
  if (!shown) return null
757
+ const tab: SideTab = section === 'shape' ? 'properties' : section === 'places' ? 'capability' : section
758
758
  return html`<figure-links
759
759
  .revision=${shown}
760
760
  .section=${section}
761
+ .active=${this.tab === tab}
761
762
  .partId=${this.selected}
762
763
  ?locked=${unavailable}
763
764
  @command=${(e: CustomEvent) => this.linkCommand(e.detail.action)}
@@ -772,9 +773,10 @@ export class FigureEditor extends LitElement {
772
773
  >${slotted}</figure-links>`
773
774
  }
774
775
  /**
775
- * A part's groups in one order whatever its state (chief architect 2026-10-16): its size, its place, its row. The links
776
- * draw the three groups and their rules; the part's own numbers go into them by slot. A fastened part's place and turn
777
- * are its fastening's: the place group says it in one line and the locked cells are not drawn (user 2026-10-16).
776
+ * A part's groups in one order whatever its state (chief architect 2026-10-16): its size, its place. The links draw
777
+ * the groups; the part's own numbers go into them by slot. How the part answers a size -- its rules, a position that
778
+ * follows, its row -- is the 「반응형」 tab's. A fastened part's place and turn are its fastening's: the place group
779
+ * says it in one line and the locked cells are not drawn (user 2026-10-16).
778
780
  */
779
781
  private renderPartGroups(shown: FigureRevision | undefined, geometry: readonly { id: string }[], fields: FieldLike[], unavailable: boolean, held?: HeldValues) {
780
782
  const groups = this.withSizeRow(groupFields(fields, this.selected), held)
@@ -795,28 +797,12 @@ export class FigureEditor extends LitElement {
795
797
  : nothing}
796
798
  ${place.length && !placeLocked
797
799
  ? html`<div slot="place" group-fields>${place.map(g => this.renderVector(g, unavailable, held))}${this.renderHeldAxes(place)}</div>`
798
- : nothing}
799
- ${this.renderRepeat(shown, geometry, unavailable)}`
800
+ : nothing}`
800
801
  )
801
802
  }
802
- /**
803
- * What the figure does -- its places (ADR-0118) -- as a group of the properties, folded: a declaration made once,
804
- * whose marks on the canvas show while it is open. A verdict that finds it empty still says where it is.
805
- */
806
- private renderCapability(asset: FigureAsset | undefined, unavailable: boolean) {
807
- return html`<section capability>
808
- <details capability-fold ?open=${this.capabilityOpen} @toggle=${(e: Event) => (this.capabilityOpen = (e.target as HTMLDetailsElement).open)}>
809
- <summary><h3>${i18next.t('figure.label.tab-capability')}</h3></summary>
810
- ${this.selected ? this.renderLinks(unavailable, 'places') : nothing}
811
- <figure-capability-panel
812
- .asset=${asset}
813
- .selected=${this.selected}
814
- ?locked=${unavailable}
815
- @select-part=${(e: CustomEvent) => this.choose(e.detail.id)}
816
- @part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
817
- ></figure-capability-panel>
818
- </details>
819
- </section>`
803
+ /** The 「반응형」 tab of a part: the links' response groups, its row's own section put in by slot. */
804
+ private renderResponse(shown: FigureRevision | undefined, geometry: readonly { id: string }[], unavailable: boolean) {
805
+ return this.renderLinks(unavailable, 'response', this.renderRepeat(shown, geometry, unavailable))
820
806
  }
821
807
  /**
822
808
  * 「더 보기」: what few parts need, folded by default and left as this person last left it (chief architect
@@ -1747,7 +1733,7 @@ export class FigureEditor extends LitElement {
1747
1733
  @configure-size=${async (e: CustomEvent<{ part: string }>) => {
1748
1734
  this.middle = 'edit'
1749
1735
  this.selected = e.detail.part
1750
- this.tab = 'properties'
1736
+ this.tab = 'response'
1751
1737
  await this.updateComplete
1752
1738
  this.renderRoot.querySelector('figure-links')?.scrollIntoView({ block: 'nearest' })
1753
1739
  }}
@@ -1764,7 +1750,7 @@ export class FigureEditor extends LitElement {
1764
1750
  .fan=${this.fanOf(shown, live)}
1765
1751
  .ghost=${this.previewShown().ghost}
1766
1752
  .highlight=${this.previewShown().highlight}
1767
- .marks=${this.tab === 'properties' && this.capabilityOpen ? this.slotMarks(view, asset) : NO_MARKS}
1753
+ .marks=${this.tab === 'capability' ? this.slotMarks(view, asset) : NO_MARKS}
1768
1754
  .palette=${view.palette ?? {}}
1769
1755
  .context=${view.context ?? ''}
1770
1756
  .selected=${this.selected}
@@ -1778,7 +1764,9 @@ export class FigureEditor extends LitElement {
1778
1764
  <aside slot="end">
1779
1765
  <div tabs>
1780
1766
  ${this.renderTab('properties', 'tune', i18next.t('figure.label.properties'))}
1767
+ ${this.renderTab('response', 'aspect_ratio', i18next.t('figure.label.tab-response'))}
1781
1768
  ${this.renderTab('motion', 'animation', i18next.t('figure.label.motion'))}
1769
+ ${this.renderTab('capability', 'conveyor_belt', i18next.t('figure.label.tab-capability'))}
1782
1770
  ${this.renderTab('data', 'sensors', i18next.t('figure.label.tab-data'))}
1783
1771
  ${this.renderTab('verdict', 'insights', i18next.t('figure.label.maturity'), this.instances)}
1784
1772
  </div>
@@ -1809,7 +1797,6 @@ export class FigureEditor extends LitElement {
1809
1797
  @part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
1810
1798
  @part-live=${(e: CustomEvent) => this.hold({}, e.detail.edit)}
1811
1799
  ></figure-material-panel>
1812
- ${this.renderCapability(asset, unavailable)}
1813
1800
  ${this.renderMore(part, view.palette ?? {}, unavailable)}`
1814
1801
  : view.geometry.length === 0
1815
1802
  ? html`${this.renderSubject()}<section empty-figure>
@@ -1819,16 +1806,31 @@ export class FigureEditor extends LitElement {
1819
1806
  <slot name="figure"></slot>
1820
1807
  </details>
1821
1808
  </section>`
1822
- : html`${this.renderSubject()}<slot name="figure"></slot>
1823
- ${this.renderGroups(groupFields(common), 'figure.label.group-figure-size', 'figure.label.design-inputs', unavailable)}
1824
- ${this.renderInstanceRange(shown, unavailable)}
1825
- ${this.renderCapability(asset, unavailable)}`}
1809
+ : html`${this.renderSubject()}<slot name="figure"></slot>`}
1810
+ </div>
1811
+ <div panel ?off=${tab !== 'response'}>
1812
+ ${this.renderSubject()}
1813
+ ${this.selected && part
1814
+ ? this.renderResponse(shown, view.geometry, unavailable)
1815
+ : html`${this.renderGroups(groupFields(common), 'figure.label.group-figure-size', 'figure.label.design-inputs', unavailable)}
1816
+ ${this.renderInstanceRange(shown, unavailable)}`}
1826
1817
  </div>
1827
1818
  <div panel ?off=${tab !== 'motion'}>
1828
1819
  ${this.renderSubject()}
1829
1820
  ${this.selected && part ? this.renderLinks(unavailable, 'motion') : html`<section><p hint>${i18next.t('figure.text.choose-a-part-to-move')}</p></section>`}
1830
1821
  ${this.renderControls(view, unavailable)}
1831
1822
  </div>
1823
+ <div panel ?off=${tab !== 'capability'}>
1824
+ ${this.renderSubject()}
1825
+ ${this.selected && part ? this.renderLinks(unavailable, 'places') : nothing}
1826
+ <figure-capability-panel
1827
+ .asset=${asset}
1828
+ .selected=${this.selected}
1829
+ ?locked=${unavailable}
1830
+ @select-part=${(e: CustomEvent) => this.choose(e.detail.id)}
1831
+ @part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
1832
+ ></figure-capability-panel>
1833
+ </div>
1832
1834
  <div panel ?off=${tab !== 'data'}>
1833
1835
  ${this.renderSubject()}
1834
1836
  <figure-data-panel
@@ -184,7 +184,12 @@ export class FigureLinks extends localize(i18next)(LitElement) {
184
184
  @property() partId = ''
185
185
  @property({ type: Boolean }) locked = false
186
186
  /** Which ties this shows: the part's shape (size and placement, in 속성), its places (자리·능력) or its motion (움직임). */
187
- @property() section: 'shape' | 'places' | 'motion' = 'shape'
187
+ @property() section: 'shape' | 'response' | 'places' | 'motion' = 'shape'
188
+ /**
189
+ * Its tab is the one shown. Two sections speak of what a command would draw -- the properties' fastening and the
190
+ * response's row -- and only the one in sight may: the hidden one's silence would clear what the shown one draws.
191
+ */
192
+ @property({ type: Boolean }) active = true
188
193
  /** What the fastening command would do, in words (the editor works it out: `command-preview.ts`). */
189
194
  @property({ attribute: false }) previewWords: string[] = []
190
195
  /** Where the selected part's motion takes it, at both ends of its range (the editor draws them on the canvas). */
@@ -231,6 +236,8 @@ export class FigureLinks extends localize(i18next)(LitElement) {
231
236
  fastening it has" over an empty target (2026-09-27, :3391, the doors). */
232
237
  private loadedFor?: string
233
238
  willUpdate(changed: Map<string, unknown>) {
239
+ /* Its tab shown again: what it would draw is said again, over whatever the other tab left. */
240
+ if (changed.has('active') && this.active && changed.get('active') === false) this.told = ''
234
241
  /* What 고치기 said is about the figure as it was: another step or another part, and it no longer holds. */
235
242
  if (changed.has('asset') || changed.has('partId')) this.fixNote = ''
236
243
  if (this.asset && this.partId && this.partId !== this.loadedFor) {
@@ -248,9 +255,10 @@ export class FigureLinks extends localize(i18next)(LitElement) {
248
255
  }
249
256
 
250
257
  updated() {
251
- /* Only the section that holds the fastening form speaks of it: the panel has one of these per section, and the
252
- others' silence cleared what it showed (2026-09-27, :3391). */
253
- if (this.section !== 'shape' && !this.told) return
258
+ /* Only the sections that hold a form speak of what it would do, and only while their tab is shown: the panel has
259
+ one of these per section, and the others' silence cleared what it showed (2026-09-27, :3391). */
260
+ const speaks = this.active && (this.section === 'shape' || this.section === 'response')
261
+ if (!speaks) return
254
262
  const said = JSON.stringify(this.candidate ?? null)
255
263
  if (said === this.told) return
256
264
  this.told = said
@@ -301,12 +309,33 @@ export class FigureLinks extends localize(i18next)(LitElement) {
301
309
  */
302
310
  /* Fastened, by a face or by an origin: either can be taken off, from the place group's 「⋯」. */
303
311
  const held = !!heldBy(asset, this.partId) || !!(this.revision && fasteningOf(this.revision, this.partId)?.atOrigin)
312
+ if (this.section === 'response') {
313
+ /*
314
+ How the part answers a size it is placed at (user 2026-10-17, the 「반응형」 tab): its own measures, where it
315
+ stands, and the row it may make -- what Build Once, Fit Anywhere is authored with, in one place.
316
+ */
317
+ const positions = positionsWorkedOut(asset, this.partId)
318
+ const response = html`
319
+ <section size-response>
320
+ <h3>${i18next.t('figure.label.group-size')}</h3>
321
+ <p group-question>${i18next.t('figure.label.resize-intent')}</p>
322
+ ${canFollow(asset, this.partId) ? this.renderDimensions(asset) : html`<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}
323
+ </section>
324
+ ${positions.length
325
+ ? html`<section position-response>
326
+ <h3>${i18next.t('figure.label.group-position-response')}</h3>
327
+ ${this.renderPositions(asset)}
328
+ </section>`
329
+ : nothing}
330
+ ${rowOf(asset, this.partId) ? html`<slot name="row"></slot>` : this.renderRow(asset)}
331
+ `
332
+ this.candidate = this.working === 'row' && this.rowCandidate ? { action: this.rowCandidate } : undefined
333
+ return response
334
+ }
304
335
  const shown = html`
305
336
  <section size>
306
337
  <h3>${i18next.t('figure.label.group-size')}</h3>
307
338
  <slot name="size"></slot>
308
- <p group-question>${i18next.t('figure.label.resize-intent')}</p>
309
- ${canFollow(asset, this.partId) ? this.renderDimensions(asset) : html`<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}
310
339
  </section>
311
340
  <section fastening ?spotlit=${this.spotlit} @focusin=${() => (this.working = 'fasten')}>
312
341
  <h3>
@@ -314,12 +343,10 @@ export class FigureLinks extends localize(i18next)(LitElement) {
314
343
  ${groupMenu([{ label: i18next.t('figure.button.detach'), icon: 'link_off', disabled: this.locked || !held, run: () => this.command({ kind: 'detach', part: this.partId }) }])}
315
344
  </h3>${this.help.hint('figure.text.fastening-explained')}
316
345
  <slot name="place"></slot>
317
- ${this.renderPositions(asset)}
318
346
  ${canFasten(asset, this.partId) ? this.renderFastening(asset) : html`<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}
319
347
  </section>
320
- ${rowOf(asset, this.partId) ? html`<slot name="row"></slot>` : this.renderRow(asset)}
321
348
  `
322
- this.candidate = this.working === 'row' && this.rowCandidate ? { action: this.rowCandidate } : this.fastenCandidate
349
+ this.candidate = this.fastenCandidate
323
350
  return shown
324
351
  }
325
352
 
@@ -264,13 +264,11 @@ export const EditorStyles = css `
264
264
  display: grid;
265
265
  gap: 6px;
266
266
  }
267
- /* A folded group (capability, 「더 보기」): its title is the fold's handle, at the title's size. */
268
- section[more] > details > summary,
269
- section[capability] > details > summary {
267
+ /* The folded 「더 보기」: its title is the fold's handle, at the title's size. */
268
+ section[more] > details > summary {
270
269
  padding: 0;
271
270
  }
272
- section[more] > details > summary > h3,
273
- section[capability] > details > summary > h3 {
271
+ section[more] > details > summary > h3 {
274
272
  margin: 0;
275
273
  }
276
274
  div[more-body] {
@@ -1 +1 @@
1
- {"version":3,"file":"editor-styles.js","sourceRoot":"","sources":["../../client/modeller/editor-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,gHAAgH;AAChH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmZ9B,CAAA","sourcesContent":["import { css } from 'lit'\n\n/** The modeller editor's own styles: the bar's tools, the panes, the start card, the side's tabs and panels. */\nexport const EditorStyles = css`\n :host {\n position: relative;\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n /* The editor folds by the width it is given, not the window's (UX ruling, 2026-09-24). */\n container-type: inline-size;\n /* The app's one face (its typography theme's --theme-font) at the body size: a bare sans-serif here put a second\n face under everything the editor draws, beside the parts list and the shell in the app's own (2026-10-16). */\n font-family: var(--theme-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-t-body, 13px);\n color: var(--md-sys-color-on-surface);\n }\n /* The bar over the work is ox-canvas-bar (ADR-0060 decision 8): the folds at its edges, undo, redo and\n fit in the suite's places. What the editor puts in it - the mode switch, the status, save - is styled here. */\n ox-canvas-bar button[mode] {\n height: 28px;\n border: 1px solid transparent;\n padding: 0 10px;\n border-radius: 999em;\n border-color: transparent;\n background: none;\n }\n ox-canvas-bar button[mode][aria-pressed='true'] {\n border-color: var(--md-sys-color-primary);\n }\n /* The gizmo's tools, drawn as the bar's own icon buttons (undo, redo, fit). */\n ox-canvas-bar .gizmo-tools {\n display: inline-flex;\n gap: 2px;\n padding-left: 4px;\n border-left: 1px solid var(--md-sys-color-outline-variant);\n }\n ox-canvas-bar button[tool] {\n display: inline-grid;\n place-items: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: var(--md-sys-shape-corner-small, 8px);\n background: transparent;\n color: var(--md-sys-color-on-surface-variant);\n cursor: pointer;\n }\n ox-canvas-bar button[tool]:hover:not(:disabled) {\n background: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface);\n }\n ox-canvas-bar button[tool][aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n ox-canvas-bar button[tool]:disabled {\n opacity: 0.38;\n cursor: default;\n }\n /*\n Slotted into the bar from here, the tools do not get the bar's own icon size (20px, set inside its shadow): they\n showed at md-icon's 24px, heavier than undo, redo and fit beside them. The same size, and the bar's focus ring.\n */\n ox-canvas-bar button[tool] md-icon {\n --md-icon-size: 20px;\n }\n ox-canvas-bar button[tool]:focus-visible {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 1px;\n }\n figure-preview {\n min-width: 0;\n min-height: 0;\n }\n /* The edit canvas stays standing while the preview is in front (a 3D scene costs a third of a second to make), out of sight. */\n div[middle][hidden] {\n display: none;\n }\n /* The preview, once made, stays standing out of sight too: its 3D scene is not made again each time it is asked for. */\n figure-preview[hidden] {\n display: none;\n }\n ox-canvas-bar .status {\n padding: 0 8px;\n color: var(--md-sys-color-on-surface-variant);\n }\n ox-canvas-bar .status[dirty] {\n color: var(--md-sys-color-primary);\n font-weight: 600;\n }\n ox-canvas-bar button[save] {\n height: 32px;\n padding: 0 14px;\n border: none;\n border-radius: 999em;\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n }\n /* The three panes are ox-three-pane's (ADR-0060 decision 8): widths 200 | 1fr | 340, 160 | 300 under\n 860px and one column with the middle first under 560px are the frame's, as are the dividers. */\n ox-three-pane {\n flex: 1;\n min-height: 0;\n }\n figure-parts {\n min-height: 0;\n }\n figure-canvas {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n div[middle] {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n }\n /* A new figure's first question, over the empty canvas. */\n div[start] {\n position: absolute;\n inset: 50% auto auto 50%;\n z-index: 10;\n color: var(--md-sys-color-on-surface);\n transform: translate(-50%, -50%);\n width: min(440px, calc(100% - 32px));\n box-sizing: border-box;\n padding: 20px;\n border-radius: 12px;\n background: var(--md-sys-color-surface, #fff);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);\n }\n div[start] button[start-button] {\n width: auto;\n margin: 0;\n font-size: var(--ui-t-body);\n height: 36px;\n padding: 0 16px;\n border: 1px solid var(--md-sys-color-outline);\n border-radius: 999em;\n color: var(--md-sys-color-primary);\n background: var(--md-sys-color-surface, #fff);\n cursor: pointer;\n }\n div[start] button[start-button]:hover:not(:disabled) {\n background: var(--md-sys-color-primary-container);\n }\n div[start] button[start-button][pressed] {\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n }\n div[start] button[start-button]:disabled {\n opacity: 0.5;\n cursor: default;\n }\n div[start] h2 {\n margin: 0 0 12px;\n font-size: var(--ui-t-title);\n }\n div[start-paths] {\n display: grid;\n gap: 14px;\n }\n div[start-paths] h3 {\n margin: 0 0 6px;\n font-size: var(--ui-t-body);\n }\n div[start-shapes] {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n }\n div[start] small {\n display: block;\n margin-top: 4px;\n color: var(--md-sys-color-on-surface-variant);\n }\n /* The side: tabs over one panel at a time, each using the whole height. */\n /*\n The side's words at the panel's size (side-panel-styles: titles body, the rest small). The editor's host is at body\n for the bar, and the fields the editor drew itself -- the part's name -- stood a step larger than every other field.\n */\n aside {\n display: flex;\n flex-direction: column;\n min-height: 0;\n font-size: var(--ui-t-small, 11.5px);\n background: var(--md-sys-color-surface-container-lowest);\n }\n div[tabs] {\n display: flex;\n flex: none;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n button[tab] {\n flex: 1;\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1px;\n min-width: 0;\n height: 48px;\n padding: 0;\n border: none;\n border-radius: 0;\n border-bottom: 2px solid transparent;\n background: none;\n color: var(--md-sys-color-on-surface-variant);\n }\n button[tab]:hover:not([on]) {\n background: var(--md-sys-color-surface-container);\n }\n button[tab][on] {\n color: var(--md-sys-color-primary);\n border-bottom-color: var(--md-sys-color-primary);\n }\n button[tab] md-icon {\n font-size: 20px;\n width: 20px;\n height: 20px;\n }\n button[tab] span[tab-label] {\n max-width: 100%;\n overflow: hidden;\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.2;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n span[badge] {\n position: absolute;\n top: 3px;\n right: 2px;\n padding: 0 5px;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n line-height: 1.5;\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n border-radius: 999em;\n pointer-events: none;\n }\n /* The sections inside carry the margins (side-panel-styles); the panel only scrolls. */\n div[panel] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 0;\n }\n div[panel][flush] {\n padding: 0;\n }\n /* Hidden, not removed: a panel keeps what it holds (the verdict its last answer) while another tab is open. */\n [off] {\n display: none !important;\n }\n /* The part's own numbers inside a group the links draw (size, place): one column, the panel's gap. */\n div[group-fields] {\n display: grid;\n gap: 6px;\n }\n /* A folded group (capability, 「더 보기」): its title is the fold's handle, at the title's size. */\n section[more] > details > summary,\n section[capability] > details > summary {\n padding: 0;\n }\n section[more] > details > summary > h3,\n section[capability] > details > summary > h3 {\n margin: 0;\n }\n div[more-body] {\n display: grid;\n gap: 16px;\n margin-top: 12px;\n }\n /*\n The view settings and the release history, opened from the bar over the right of the work: neither is about the\n part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16).\n */\n :host {\n position: relative;\n }\n div[bar-overlay-panel] {\n position: absolute;\n top: var(--figure-bar-height, 44px);\n right: 8px;\n z-index: 5;\n display: flex;\n flex-direction: column;\n width: min(360px, calc(100% - 16px));\n max-height: calc(100% - var(--figure-bar-height, 44px) - 16px);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);\n }\n div[overlay-head] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 8px 8px 12px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n div[overlay-head] h3 {\n margin: 0;\n }\n div[overlay-head] button[quiet] {\n margin-left: 0;\n }\n div[overlay-body] {\n min-height: 0;\n overflow: auto;\n }\n /* Which part the panel speaks of. */\n div[subject] {\n position: sticky;\n top: 0;\n z-index: 1;\n display: grid;\n gap: 4px;\n padding: 10px 12px;\n background: var(--md-sys-color-surface-container-lowest);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n /* The panel's three sizes (side-panel-styles): the part's kind at the title's, its two lines at the hint's. */\n div[subject-summary] {\n display: grid;\n gap: 2px;\n }\n div[subject-summary] b {\n font-size: var(--ui-t-body, 13px);\n font-weight: 600;\n }\n div[subject-summary] b span {\n font-weight: 400;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[subject-summary] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n /*\n No rule here draws every button as a box: the side's buttons take the shared panel rules (SidePanelStyles), one\n height with the fields beside them, and only the bar's publish keeps an outline (user 2026-10-16: 「버튼 박스도\n 과하게 사용되어서」 -- a bare 「button」 rule had put a 32px box round every button the editor drew).\n */\n button {\n font: inherit;\n box-sizing: border-box;\n color: inherit;\n min-width: 0;\n cursor: pointer;\n }\n /* What blocks release, beside 「발행」: one red word button, and its list under the bar. */\n ox-canvas-bar button[release-blocked] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 32px;\n padding: 0 10px;\n border: none;\n border-radius: 6px;\n color: var(--md-sys-color-on-error-container);\n background: var(--md-sys-color-error-container);\n }\n ox-canvas-bar button[release-blocked] md-icon {\n --md-icon-size: 18px;\n }\n div[release-blockers] {\n position: absolute;\n top: 46px;\n right: 8px;\n z-index: 6;\n width: min(340px, calc(100% - 16px));\n padding: 8px 10px 10px;\n box-sizing: border-box;\n background: var(--md-sys-color-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n box-shadow: var(--md-sys-elevation-2, 0 2px 8px #0003);\n }\n ox-canvas-bar button[publish] {\n height: 32px;\n padding: 0 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: var(--md-sys-color-surface);\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 .hint {\n color: var(--md-sys-color-on-surface-variant);\n line-height: 1.5;\n }\n`\n"]}
1
+ {"version":3,"file":"editor-styles.js","sourceRoot":"","sources":["../../client/modeller/editor-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,gHAAgH;AAChH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiZ9B,CAAA","sourcesContent":["import { css } from 'lit'\n\n/** The modeller editor's own styles: the bar's tools, the panes, the start card, the side's tabs and panels. */\nexport const EditorStyles = css`\n :host {\n position: relative;\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n /* The editor folds by the width it is given, not the window's (UX ruling, 2026-09-24). */\n container-type: inline-size;\n /* The app's one face (its typography theme's --theme-font) at the body size: a bare sans-serif here put a second\n face under everything the editor draws, beside the parts list and the shell in the app's own (2026-10-16). */\n font-family: var(--theme-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-t-body, 13px);\n color: var(--md-sys-color-on-surface);\n }\n /* The bar over the work is ox-canvas-bar (ADR-0060 decision 8): the folds at its edges, undo, redo and\n fit in the suite's places. What the editor puts in it - the mode switch, the status, save - is styled here. */\n ox-canvas-bar button[mode] {\n height: 28px;\n border: 1px solid transparent;\n padding: 0 10px;\n border-radius: 999em;\n border-color: transparent;\n background: none;\n }\n ox-canvas-bar button[mode][aria-pressed='true'] {\n border-color: var(--md-sys-color-primary);\n }\n /* The gizmo's tools, drawn as the bar's own icon buttons (undo, redo, fit). */\n ox-canvas-bar .gizmo-tools {\n display: inline-flex;\n gap: 2px;\n padding-left: 4px;\n border-left: 1px solid var(--md-sys-color-outline-variant);\n }\n ox-canvas-bar button[tool] {\n display: inline-grid;\n place-items: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: none;\n border-radius: var(--md-sys-shape-corner-small, 8px);\n background: transparent;\n color: var(--md-sys-color-on-surface-variant);\n cursor: pointer;\n }\n ox-canvas-bar button[tool]:hover:not(:disabled) {\n background: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface);\n }\n ox-canvas-bar button[tool][aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n ox-canvas-bar button[tool]:disabled {\n opacity: 0.38;\n cursor: default;\n }\n /*\n Slotted into the bar from here, the tools do not get the bar's own icon size (20px, set inside its shadow): they\n showed at md-icon's 24px, heavier than undo, redo and fit beside them. The same size, and the bar's focus ring.\n */\n ox-canvas-bar button[tool] md-icon {\n --md-icon-size: 20px;\n }\n ox-canvas-bar button[tool]:focus-visible {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 1px;\n }\n figure-preview {\n min-width: 0;\n min-height: 0;\n }\n /* The edit canvas stays standing while the preview is in front (a 3D scene costs a third of a second to make), out of sight. */\n div[middle][hidden] {\n display: none;\n }\n /* The preview, once made, stays standing out of sight too: its 3D scene is not made again each time it is asked for. */\n figure-preview[hidden] {\n display: none;\n }\n ox-canvas-bar .status {\n padding: 0 8px;\n color: var(--md-sys-color-on-surface-variant);\n }\n ox-canvas-bar .status[dirty] {\n color: var(--md-sys-color-primary);\n font-weight: 600;\n }\n ox-canvas-bar button[save] {\n height: 32px;\n padding: 0 14px;\n border: none;\n border-radius: 999em;\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n }\n /* The three panes are ox-three-pane's (ADR-0060 decision 8): widths 200 | 1fr | 340, 160 | 300 under\n 860px and one column with the middle first under 560px are the frame's, as are the dividers. */\n ox-three-pane {\n flex: 1;\n min-height: 0;\n }\n figure-parts {\n min-height: 0;\n }\n figure-canvas {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n div[middle] {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n }\n /* A new figure's first question, over the empty canvas. */\n div[start] {\n position: absolute;\n inset: 50% auto auto 50%;\n z-index: 10;\n color: var(--md-sys-color-on-surface);\n transform: translate(-50%, -50%);\n width: min(440px, calc(100% - 32px));\n box-sizing: border-box;\n padding: 20px;\n border-radius: 12px;\n background: var(--md-sys-color-surface, #fff);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);\n }\n div[start] button[start-button] {\n width: auto;\n margin: 0;\n font-size: var(--ui-t-body);\n height: 36px;\n padding: 0 16px;\n border: 1px solid var(--md-sys-color-outline);\n border-radius: 999em;\n color: var(--md-sys-color-primary);\n background: var(--md-sys-color-surface, #fff);\n cursor: pointer;\n }\n div[start] button[start-button]:hover:not(:disabled) {\n background: var(--md-sys-color-primary-container);\n }\n div[start] button[start-button][pressed] {\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n border-color: var(--md-sys-color-primary);\n }\n div[start] button[start-button]:disabled {\n opacity: 0.5;\n cursor: default;\n }\n div[start] h2 {\n margin: 0 0 12px;\n font-size: var(--ui-t-title);\n }\n div[start-paths] {\n display: grid;\n gap: 14px;\n }\n div[start-paths] h3 {\n margin: 0 0 6px;\n font-size: var(--ui-t-body);\n }\n div[start-shapes] {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n }\n div[start] small {\n display: block;\n margin-top: 4px;\n color: var(--md-sys-color-on-surface-variant);\n }\n /* The side: tabs over one panel at a time, each using the whole height. */\n /*\n The side's words at the panel's size (side-panel-styles: titles body, the rest small). The editor's host is at body\n for the bar, and the fields the editor drew itself -- the part's name -- stood a step larger than every other field.\n */\n aside {\n display: flex;\n flex-direction: column;\n min-height: 0;\n font-size: var(--ui-t-small, 11.5px);\n background: var(--md-sys-color-surface-container-lowest);\n }\n div[tabs] {\n display: flex;\n flex: none;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n button[tab] {\n flex: 1;\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1px;\n min-width: 0;\n height: 48px;\n padding: 0;\n border: none;\n border-radius: 0;\n border-bottom: 2px solid transparent;\n background: none;\n color: var(--md-sys-color-on-surface-variant);\n }\n button[tab]:hover:not([on]) {\n background: var(--md-sys-color-surface-container);\n }\n button[tab][on] {\n color: var(--md-sys-color-primary);\n border-bottom-color: var(--md-sys-color-primary);\n }\n button[tab] md-icon {\n font-size: 20px;\n width: 20px;\n height: 20px;\n }\n button[tab] span[tab-label] {\n max-width: 100%;\n overflow: hidden;\n font-size: var(--ui-t-micro, 11px);\n line-height: 1.2;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n span[badge] {\n position: absolute;\n top: 3px;\n right: 2px;\n padding: 0 5px;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n line-height: 1.5;\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n border-radius: 999em;\n pointer-events: none;\n }\n /* The sections inside carry the margins (side-panel-styles); the panel only scrolls. */\n div[panel] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 0;\n }\n div[panel][flush] {\n padding: 0;\n }\n /* Hidden, not removed: a panel keeps what it holds (the verdict its last answer) while another tab is open. */\n [off] {\n display: none !important;\n }\n /* The part's own numbers inside a group the links draw (size, place): one column, the panel's gap. */\n div[group-fields] {\n display: grid;\n gap: 6px;\n }\n /* The folded 「더 보기」: its title is the fold's handle, at the title's size. */\n section[more] > details > summary {\n padding: 0;\n }\n section[more] > details > summary > h3 {\n margin: 0;\n }\n div[more-body] {\n display: grid;\n gap: 16px;\n margin-top: 12px;\n }\n /*\n The view settings and the release history, opened from the bar over the right of the work: neither is about the\n part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16).\n */\n :host {\n position: relative;\n }\n div[bar-overlay-panel] {\n position: absolute;\n top: var(--figure-bar-height, 44px);\n right: 8px;\n z-index: 5;\n display: flex;\n flex-direction: column;\n width: min(360px, calc(100% - 16px));\n max-height: calc(100% - var(--figure-bar-height, 44px) - 16px);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);\n }\n div[overlay-head] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 8px 8px 12px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n div[overlay-head] h3 {\n margin: 0;\n }\n div[overlay-head] button[quiet] {\n margin-left: 0;\n }\n div[overlay-body] {\n min-height: 0;\n overflow: auto;\n }\n /* Which part the panel speaks of. */\n div[subject] {\n position: sticky;\n top: 0;\n z-index: 1;\n display: grid;\n gap: 4px;\n padding: 10px 12px;\n background: var(--md-sys-color-surface-container-lowest);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n /* The panel's three sizes (side-panel-styles): the part's kind at the title's, its two lines at the hint's. */\n div[subject-summary] {\n display: grid;\n gap: 2px;\n }\n div[subject-summary] b {\n font-size: var(--ui-t-body, 13px);\n font-weight: 600;\n }\n div[subject-summary] b span {\n font-weight: 400;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[subject-summary] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n /*\n No rule here draws every button as a box: the side's buttons take the shared panel rules (SidePanelStyles), one\n height with the fields beside them, and only the bar's publish keeps an outline (user 2026-10-16: 「버튼 박스도\n 과하게 사용되어서」 -- a bare 「button」 rule had put a 32px box round every button the editor drew).\n */\n button {\n font: inherit;\n box-sizing: border-box;\n color: inherit;\n min-width: 0;\n cursor: pointer;\n }\n /* What blocks release, beside 「발행」: one red word button, and its list under the bar. */\n ox-canvas-bar button[release-blocked] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 32px;\n padding: 0 10px;\n border: none;\n border-radius: 6px;\n color: var(--md-sys-color-on-error-container);\n background: var(--md-sys-color-error-container);\n }\n ox-canvas-bar button[release-blocked] md-icon {\n --md-icon-size: 18px;\n }\n div[release-blockers] {\n position: absolute;\n top: 46px;\n right: 8px;\n z-index: 6;\n width: min(340px, calc(100% - 16px));\n padding: 8px 10px 10px;\n box-sizing: border-box;\n background: var(--md-sys-color-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n box-shadow: var(--md-sys-elevation-2, 0 2px 8px #0003);\n }\n ox-canvas-bar button[publish] {\n height: 32px;\n padding: 0 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: var(--md-sys-color-surface);\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 .hint {\n color: var(--md-sys-color-on-surface-variant);\n line-height: 1.5;\n }\n`\n"]}
@@ -61,8 +61,6 @@ export declare class FigureEditor extends LitElement {
61
61
  private tab;
62
62
  /** The view settings or the release history, open over the work from the bar. */
63
63
  private overlay?;
64
- /** The capability group open: the canvas marks the places only while their group is in sight. */
65
- private capabilityOpen;
66
64
  /** 「더 보기」 open, as this person left it last (chief architect 2026-10-16: kept per person). */
67
65
  private moreOpen;
68
66
  private static storedMoreOpen;
@@ -203,16 +201,14 @@ export declare class FigureEditor extends LitElement {
203
201
  /** Size response, fastening and motion for the selected part, as authoring commands. */
204
202
  private renderLinks;
205
203
  /**
206
- * A part's groups in one order whatever its state (chief architect 2026-10-16): its size, its place, its row. The links
207
- * draw the three groups and their rules; the part's own numbers go into them by slot. A fastened part's place and turn
208
- * are its fastening's: the place group says it in one line and the locked cells are not drawn (user 2026-10-16).
204
+ * A part's groups in one order whatever its state (chief architect 2026-10-16): its size, its place. The links draw
205
+ * the groups; the part's own numbers go into them by slot. How the part answers a size -- its rules, a position that
206
+ * follows, its row -- is the 「반응형」 tab's. A fastened part's place and turn are its fastening's: the place group
207
+ * says it in one line and the locked cells are not drawn (user 2026-10-16).
209
208
  */
210
209
  private renderPartGroups;
211
- /**
212
- * What the figure does -- its places (ADR-0118) -- as a group of the properties, folded: a declaration made once,
213
- * whose marks on the canvas show while it is open. A verdict that finds it empty still says where it is.
214
- */
215
- private renderCapability;
210
+ /** The 「반응형」 tab of a part: the links' response groups, its row's own section put in by slot. */
211
+ private renderResponse;
216
212
  /**
217
213
  * 「더 보기」: what few parts need, folded by default and left as this person last left it (chief architect
218
214
  * 2026-10-16) -- the section, its corners and inside; transparency, glow and segments; the mirrored copy and the
@@ -78,8 +78,6 @@ let FigureEditor = class FigureEditor extends LitElement {
78
78
  this.selected = '';
79
79
  this.error = '';
80
80
  this.tab = 'properties';
81
- /** The capability group open: the canvas marks the places only while their group is in sight. */
82
- this.capabilityOpen = false;
83
81
  /** 「더 보기」 open, as this person left it last (chief architect 2026-10-16: kept per person). */
84
82
  this.moreOpen = FigureEditor_1.storedMoreOpen();
85
83
  /** What the middle shows: the parts being edited, or the figure placed on a board at eight sizes. */
@@ -107,7 +105,7 @@ let FigureEditor = class FigureEditor extends LitElement {
107
105
  session: () => this.session,
108
106
  selected: () => this.selected,
109
107
  shown: () => this.shown(),
110
- formsInSight: () => this.middle === 'edit' && this.tab === 'properties',
108
+ formsInSight: () => this.middle === 'edit' && (this.tab === 'properties' || this.tab === 'response'),
111
109
  said: error => this.said(error)
112
110
  });
113
111
  this.keys = new ModellerKeys(this, {
@@ -757,9 +755,11 @@ let FigureEditor = class FigureEditor extends LitElement {
757
755
  const shown = this.shown();
758
756
  if (!shown)
759
757
  return null;
758
+ const tab = section === 'shape' ? 'properties' : section === 'places' ? 'capability' : section;
760
759
  return html `<figure-links
761
760
  .revision=${shown}
762
761
  .section=${section}
762
+ .active=${this.tab === tab}
763
763
  .partId=${this.selected}
764
764
  ?locked=${unavailable}
765
765
  @command=${(e) => this.linkCommand(e.detail.action)}
@@ -774,9 +774,10 @@ let FigureEditor = class FigureEditor extends LitElement {
774
774
  >${slotted}</figure-links>`;
775
775
  }
776
776
  /**
777
- * A part's groups in one order whatever its state (chief architect 2026-10-16): its size, its place, its row. The links
778
- * draw the three groups and their rules; the part's own numbers go into them by slot. A fastened part's place and turn
779
- * are its fastening's: the place group says it in one line and the locked cells are not drawn (user 2026-10-16).
777
+ * A part's groups in one order whatever its state (chief architect 2026-10-16): its size, its place. The links draw
778
+ * the groups; the part's own numbers go into them by slot. How the part answers a size -- its rules, a position that
779
+ * follows, its row -- is the 「반응형」 tab's. A fastened part's place and turn are its fastening's: the place group
780
+ * says it in one line and the locked cells are not drawn (user 2026-10-16).
780
781
  */
781
782
  renderPartGroups(shown, geometry, fields, unavailable, held) {
782
783
  const groups = this.withSizeRow(groupFields(fields, this.selected), held);
@@ -794,27 +795,11 @@ let FigureEditor = class FigureEditor extends LitElement {
794
795
  : nothing}
795
796
  ${place.length && !placeLocked
796
797
  ? html `<div slot="place" group-fields>${place.map(g => this.renderVector(g, unavailable, held))}${this.renderHeldAxes(place)}</div>`
797
- : nothing}
798
- ${this.renderRepeat(shown, geometry, unavailable)}`);
798
+ : nothing}`);
799
799
  }
800
- /**
801
- * What the figure does -- its places (ADR-0118) -- as a group of the properties, folded: a declaration made once,
802
- * whose marks on the canvas show while it is open. A verdict that finds it empty still says where it is.
803
- */
804
- renderCapability(asset, unavailable) {
805
- return html `<section capability>
806
- <details capability-fold ?open=${this.capabilityOpen} @toggle=${(e) => (this.capabilityOpen = e.target.open)}>
807
- <summary><h3>${i18next.t('figure.label.tab-capability')}</h3></summary>
808
- ${this.selected ? this.renderLinks(unavailable, 'places') : nothing}
809
- <figure-capability-panel
810
- .asset=${asset}
811
- .selected=${this.selected}
812
- ?locked=${unavailable}
813
- @select-part=${(e) => this.choose(e.detail.id)}
814
- @part-edit=${(e) => this.run(() => this.session.edit(e.detail.edit))}
815
- ></figure-capability-panel>
816
- </details>
817
- </section>`;
800
+ /** The 「반응형」 tab of a part: the links' response groups, its row's own section put in by slot. */
801
+ renderResponse(shown, geometry, unavailable) {
802
+ return this.renderLinks(unavailable, 'response', this.renderRepeat(shown, geometry, unavailable));
818
803
  }
819
804
  /**
820
805
  * 「더 보기」: what few parts need, folded by default and left as this person last left it (chief architect
@@ -1764,7 +1749,7 @@ let FigureEditor = class FigureEditor extends LitElement {
1764
1749
  @configure-size=${async (e) => {
1765
1750
  this.middle = 'edit';
1766
1751
  this.selected = e.detail.part;
1767
- this.tab = 'properties';
1752
+ this.tab = 'response';
1768
1753
  await this.updateComplete;
1769
1754
  this.renderRoot.querySelector('figure-links')?.scrollIntoView({ block: 'nearest' });
1770
1755
  }}
@@ -1781,7 +1766,7 @@ let FigureEditor = class FigureEditor extends LitElement {
1781
1766
  .fan=${this.fanOf(shown, live)}
1782
1767
  .ghost=${this.previewShown().ghost}
1783
1768
  .highlight=${this.previewShown().highlight}
1784
- .marks=${this.tab === 'properties' && this.capabilityOpen ? this.slotMarks(view, asset) : NO_MARKS}
1769
+ .marks=${this.tab === 'capability' ? this.slotMarks(view, asset) : NO_MARKS}
1785
1770
  .palette=${view.palette ?? {}}
1786
1771
  .context=${view.context ?? ''}
1787
1772
  .selected=${this.selected}
@@ -1795,7 +1780,9 @@ let FigureEditor = class FigureEditor extends LitElement {
1795
1780
  <aside slot="end">
1796
1781
  <div tabs>
1797
1782
  ${this.renderTab('properties', 'tune', i18next.t('figure.label.properties'))}
1783
+ ${this.renderTab('response', 'aspect_ratio', i18next.t('figure.label.tab-response'))}
1798
1784
  ${this.renderTab('motion', 'animation', i18next.t('figure.label.motion'))}
1785
+ ${this.renderTab('capability', 'conveyor_belt', i18next.t('figure.label.tab-capability'))}
1799
1786
  ${this.renderTab('data', 'sensors', i18next.t('figure.label.tab-data'))}
1800
1787
  ${this.renderTab('verdict', 'insights', i18next.t('figure.label.maturity'), this.instances)}
1801
1788
  </div>
@@ -1826,7 +1813,6 @@ let FigureEditor = class FigureEditor extends LitElement {
1826
1813
  @part-edit=${(e) => this.run(() => this.session.edit(e.detail.edit))}
1827
1814
  @part-live=${(e) => this.hold({}, e.detail.edit)}
1828
1815
  ></figure-material-panel>
1829
- ${this.renderCapability(asset, unavailable)}
1830
1816
  ${this.renderMore(part, view.palette ?? {}, unavailable)}`
1831
1817
  : view.geometry.length === 0
1832
1818
  ? html `${this.renderSubject()}<section empty-figure>
@@ -1836,16 +1822,31 @@ let FigureEditor = class FigureEditor extends LitElement {
1836
1822
  <slot name="figure"></slot>
1837
1823
  </details>
1838
1824
  </section>`
1839
- : html `${this.renderSubject()}<slot name="figure"></slot>
1840
- ${this.renderGroups(groupFields(common), 'figure.label.group-figure-size', 'figure.label.design-inputs', unavailable)}
1841
- ${this.renderInstanceRange(shown, unavailable)}
1842
- ${this.renderCapability(asset, unavailable)}`}
1825
+ : html `${this.renderSubject()}<slot name="figure"></slot>`}
1826
+ </div>
1827
+ <div panel ?off=${tab !== 'response'}>
1828
+ ${this.renderSubject()}
1829
+ ${this.selected && part
1830
+ ? this.renderResponse(shown, view.geometry, unavailable)
1831
+ : html `${this.renderGroups(groupFields(common), 'figure.label.group-figure-size', 'figure.label.design-inputs', unavailable)}
1832
+ ${this.renderInstanceRange(shown, unavailable)}`}
1843
1833
  </div>
1844
1834
  <div panel ?off=${tab !== 'motion'}>
1845
1835
  ${this.renderSubject()}
1846
1836
  ${this.selected && part ? this.renderLinks(unavailable, 'motion') : html `<section><p hint>${i18next.t('figure.text.choose-a-part-to-move')}</p></section>`}
1847
1837
  ${this.renderControls(view, unavailable)}
1848
1838
  </div>
1839
+ <div panel ?off=${tab !== 'capability'}>
1840
+ ${this.renderSubject()}
1841
+ ${this.selected && part ? this.renderLinks(unavailable, 'places') : nothing}
1842
+ <figure-capability-panel
1843
+ .asset=${asset}
1844
+ .selected=${this.selected}
1845
+ ?locked=${unavailable}
1846
+ @select-part=${(e) => this.choose(e.detail.id)}
1847
+ @part-edit=${(e) => this.run(() => this.session.edit(e.detail.edit))}
1848
+ ></figure-capability-panel>
1849
+ </div>
1849
1850
  <div panel ?off=${tab !== 'data'}>
1850
1851
  ${this.renderSubject()}
1851
1852
  <figure-data-panel
@@ -2086,10 +2087,6 @@ __decorate([
2086
2087
  state(),
2087
2088
  __metadata("design:type", String)
2088
2089
  ], FigureEditor.prototype, "overlay", void 0);
2089
- __decorate([
2090
- state(),
2091
- __metadata("design:type", Object)
2092
- ], FigureEditor.prototype, "capabilityOpen", void 0);
2093
2090
  __decorate([
2094
2091
  state(),
2095
2092
  __metadata("design:type", Object)