@things-factory/figure-ui 10.1.175 → 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.
Files changed (41) hide show
  1. package/client/modeller/editor-styles.ts +4 -6
  2. package/client/modeller/editor.ts +41 -39
  3. package/client/modeller/figure-links.ts +41 -12
  4. package/client/modeller/figure-parts.ts +31 -14
  5. package/client/modeller/figure-properties.ts +2 -2
  6. package/client/pages/figure-modeller-page.ts +34 -11
  7. package/dist-client/modeller/editor-styles.js +4 -6
  8. package/dist-client/modeller/editor-styles.js.map +1 -1
  9. package/dist-client/modeller/editor.d.ts +6 -10
  10. package/dist-client/modeller/editor.js +34 -37
  11. package/dist-client/modeller/editor.js.map +1 -1
  12. package/dist-client/modeller/figure-links.d.ts +7 -2
  13. package/dist-client/modeller/figure-links.js +45 -11
  14. package/dist-client/modeller/figure-links.js.map +1 -1
  15. package/dist-client/modeller/figure-parts.js +31 -14
  16. package/dist-client/modeller/figure-parts.js.map +1 -1
  17. package/dist-client/modeller/figure-properties.js +2 -2
  18. package/dist-client/modeller/figure-properties.js.map +1 -1
  19. package/dist-client/pages/figure-modeller-page.js +34 -11
  20. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  21. package/dist-client/route.d.ts +1 -1
  22. package/dist-client/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +6 -6
  24. package/test/browser/acceptance-page.js +2 -1
  25. package/test/browser/build.mjs +9 -1
  26. package/test/browser/editing-page.js +2 -1
  27. package/test/browser/links.check.mjs +24 -9
  28. package/test/browser/modeller.check.mjs +6 -0
  29. package/test/browser/parts.check.mjs +3 -12
  30. package/test/browser/places-mesh.check.mjs +4 -9
  31. package/test/browser/props.check.mjs +67 -12
  32. package/test/browser/row-preview.check.mjs +4 -0
  33. package/test/browser/screen.check.mjs +49 -19
  34. package/test/browser/serve-acceptance.mjs +2 -1
  35. package/test/place-words.test.ts +31 -0
  36. package/translations/en.json +6 -3
  37. package/translations/es.json +3 -0
  38. package/translations/ja.json +3 -0
  39. package/translations/ko.json +6 -3
  40. package/translations/ms.json +3 -0
  41. package/translations/zh.json +3 -0
@@ -263,15 +263,13 @@ export const EditorStyles = css`
263
263
  /* The part's own numbers inside a group the links draw (size, place): one column, the panel's gap. */
264
264
  div[group-fields] {
265
265
  display: grid;
266
- gap: 8px;
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
@@ -121,8 +121,10 @@ export class FigureLinks extends localize(i18next)(LitElement) {
121
121
  flex: 1;
122
122
  margin: 0;
123
123
  }
124
- div[fastened-summary] > button[quiet] {
125
- margin-left: 0;
124
+ /* Opening the form is an action: the primary colour, at the line's end. */
125
+ div[fastened-summary] > button[act] {
126
+ flex: none;
127
+ margin: 0 -6px 0 0;
126
128
  }
127
129
  /* The measure's name, then its rule: X, or a cylinder's 반지름 · 길이, on one line. */
128
130
  div[row] > b {
@@ -182,7 +184,12 @@ export class FigureLinks extends localize(i18next)(LitElement) {
182
184
  @property() partId = ''
183
185
  @property({ type: Boolean }) locked = false
184
186
  /** Which ties this shows: the part's shape (size and placement, in 속성), its places (자리·능력) or its motion (움직임). */
185
- @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
186
193
  /** What the fastening command would do, in words (the editor works it out: `command-preview.ts`). */
187
194
  @property({ attribute: false }) previewWords: string[] = []
188
195
  /** Where the selected part's motion takes it, at both ends of its range (the editor draws them on the canvas). */
@@ -229,6 +236,8 @@ export class FigureLinks extends localize(i18next)(LitElement) {
229
236
  fastening it has" over an empty target (2026-09-27, :3391, the doors). */
230
237
  private loadedFor?: string
231
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 = ''
232
241
  /* What 고치기 said is about the figure as it was: another step or another part, and it no longer holds. */
233
242
  if (changed.has('asset') || changed.has('partId')) this.fixNote = ''
234
243
  if (this.asset && this.partId && this.partId !== this.loadedFor) {
@@ -246,9 +255,10 @@ export class FigureLinks extends localize(i18next)(LitElement) {
246
255
  }
247
256
 
248
257
  updated() {
249
- /* Only the section that holds the fastening form speaks of it: the panel has one of these per section, and the
250
- others' silence cleared what it showed (2026-09-27, :3391). */
251
- 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
252
262
  const said = JSON.stringify(this.candidate ?? null)
253
263
  if (said === this.told) return
254
264
  this.told = said
@@ -299,12 +309,33 @@ export class FigureLinks extends localize(i18next)(LitElement) {
299
309
  */
300
310
  /* Fastened, by a face or by an origin: either can be taken off, from the place group's 「⋯」. */
301
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
+ }
302
335
  const shown = html`
303
336
  <section size>
304
337
  <h3>${i18next.t('figure.label.group-size')}</h3>
305
338
  <slot name="size"></slot>
306
- <p group-question>${i18next.t('figure.label.resize-intent')}</p>
307
- ${canFollow(asset, this.partId) ? this.renderDimensions(asset) : html`<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}
308
339
  </section>
309
340
  <section fastening ?spotlit=${this.spotlit} @focusin=${() => (this.working = 'fasten')}>
310
341
  <h3>
@@ -312,12 +343,10 @@ export class FigureLinks extends localize(i18next)(LitElement) {
312
343
  ${groupMenu([{ label: i18next.t('figure.button.detach'), icon: 'link_off', disabled: this.locked || !held, run: () => this.command({ kind: 'detach', part: this.partId }) }])}
313
344
  </h3>${this.help.hint('figure.text.fastening-explained')}
314
345
  <slot name="place"></slot>
315
- ${this.renderPositions(asset)}
316
346
  ${canFasten(asset, this.partId) ? this.renderFastening(asset) : html`<p hint>${i18next.t('figure.text.links-need-a-box')}</p>`}
317
347
  </section>
318
- ${rowOf(asset, this.partId) ? html`<slot name="row"></slot>` : this.renderRow(asset)}
319
348
  `
320
- this.candidate = this.working === 'row' && this.rowCandidate ? { action: this.rowCandidate } : this.fastenCandidate
349
+ this.candidate = this.fastenCandidate
321
350
  return shown
322
351
  }
323
352
 
@@ -667,7 +696,7 @@ export class FigureLinks extends localize(i18next)(LitElement) {
667
696
  ${overhang}
668
697
  <div fastened-summary>
669
698
  <p fasten-sentence>${this.sentenceOf(myFace, f.toPlane ? planeWord : `${f.target}의 ${i18next.t('figure.label.face-' + targetFace)}`, mode)}</p>
670
- <button quiet change-fastening ?disabled=${this.locked} @click=${() => (this.changingFastening = true)}>${i18next.t('figure.button.change-fastening')}</button>
699
+ <button act change-fastening ?disabled=${this.locked} @click=${() => (this.changingFastening = true)}>${i18next.t('figure.button.change-fastening')}</button>
671
700
  </div>
672
701
  ${this.renderDetachWords()}
673
702
  `
@@ -140,6 +140,7 @@ export class FigureParts extends localize(i18next)(LitElement) {
140
140
  outline: 2px solid var(--md-sys-color-primary);
141
141
  outline-offset: 1px;
142
142
  }
143
+ /* The cost line as a line, not a card; lit under the hand like the rest of the list. */
143
144
  button[budget] {
144
145
  display: flex;
145
146
  align-items: flex-start;
@@ -153,8 +154,8 @@ export class FigureParts extends localize(i18next)(LitElement) {
153
154
  text-align: left;
154
155
  color: var(--md-sys-color-on-surface-variant);
155
156
  background: none;
156
- border: 1px solid var(--md-sys-color-outline-variant);
157
- border-radius: 6px;
157
+ border: 1px solid transparent;
158
+ border-radius: 4px;
158
159
  cursor: pointer;
159
160
  }
160
161
  button[budget]:hover {
@@ -202,17 +203,20 @@ export class FigureParts extends localize(i18next)(LitElement) {
202
203
  font-size: var(--ui-t-micro, 11px);
203
204
  color: var(--md-sys-color-on-surface-variant);
204
205
  }
205
- /* One height with the side's fields (30px, side-panel-styles): it stood at 21px, the only small box on the screen. */
206
+ /* A field in the side panel's look (side-panel-styles): 26px, a quiet ground, its line only under the hand. */
206
207
  div[level] select {
207
208
  min-width: 0;
208
- min-height: 30px;
209
+ min-height: 26px;
209
210
  padding: 2px 4px;
210
211
  font: inherit;
211
212
  font-size: var(--ui-t-small, 11.5px);
212
213
  color: var(--md-sys-color-on-surface);
213
- background: var(--md-sys-color-surface-container-lowest);
214
- border: 1px solid var(--md-sys-color-outline-variant);
215
- border-radius: 6px;
214
+ background: var(--md-sys-color-surface-container-low);
215
+ border: 1px solid transparent;
216
+ border-radius: 4px;
217
+ }
218
+ div[level] select:hover:not(:disabled) {
219
+ border-color: var(--md-sys-color-outline-variant);
216
220
  }
217
221
  small[copies] {
218
222
  color: var(--md-sys-color-on-surface-variant);
@@ -229,6 +233,7 @@ export class FigureParts extends localize(i18next)(LitElement) {
229
233
  grid-template-columns: repeat(3, minmax(0, 1fr));
230
234
  gap: 3px;
231
235
  }
236
+ /* A tile with no outline: its icon and name, lit under the hand -- ten outlined tiles were the boxiest part of the screen (user 2026-10-17). */
232
237
  button[shape] {
233
238
  display: flex;
234
239
  flex-direction: column;
@@ -241,37 +246,49 @@ export class FigureParts extends localize(i18next)(LitElement) {
241
246
  line-height: 1.2;
242
247
  text-align: center;
243
248
  overflow-wrap: anywhere;
249
+ /* A tile's name on one line, so the nine stand one height; a long name ends in an ellipsis, its title says it all. */
250
+ white-space: nowrap;
244
251
  color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));
245
252
  background: none;
246
- border: 1px solid var(--md-sys-color-outline-variant);
247
- border-radius: 6px;
253
+ border: 1px solid transparent;
254
+ border-radius: 4px;
248
255
  cursor: pointer;
249
256
  }
250
257
  /*
251
258
  A file brought in is not a starting shape: its own row under the nine, its words on one line. As a tenth tile its
252
259
  two-word name broke onto two lines and stood it taller than the rest (2026-10-16).
253
260
  */
261
+ button[shape] > span {
262
+ max-width: 100%;
263
+ overflow: hidden;
264
+ text-overflow: ellipsis;
265
+ }
254
266
  button[shape][import] {
255
267
  grid-column: 1 / -1;
256
268
  flex-direction: row;
257
269
  justify-content: center;
258
270
  gap: 6px;
259
- min-height: 30px;
271
+ min-height: 26px;
260
272
  padding: 0 8px;
261
273
  }
274
+ /* An action in the side panel's look: icon and words in the primary colour, no pill. */
262
275
  button[recipe-catalog] {
263
276
  display: inline-flex;
264
277
  align-items: center;
265
278
  gap: 4px;
266
- padding: 4px 10px;
279
+ padding: 4px 6px;
280
+ margin-left: -6px;
267
281
  font: inherit;
268
282
  font-size: var(--ui-t-small, 11.5px);
269
- color: var(--md-sys-color-on-surface);
283
+ color: var(--md-sys-color-primary);
270
284
  background: none;
271
- border: 1px solid var(--md-sys-color-outline-variant);
272
- border-radius: 999em;
285
+ border: 1px solid transparent;
286
+ border-radius: 4px;
273
287
  cursor: pointer;
274
288
  }
289
+ button[recipe-catalog]:hover:not(:disabled) {
290
+ background: var(--md-sys-color-surface-container);
291
+ }
275
292
  button[shape]:hover:not([disabled]) {
276
293
  color: var(--md-sys-color-on-primary-container);
277
294
  background-color: var(--md-sys-color-primary-container);
@@ -50,10 +50,10 @@ export class FigureProperties extends localize(i18next)(LitElement) {
50
50
  div[row] > select {
51
51
  flex: 1;
52
52
  }
53
+ /* Words as wide as themselves (side-panel-styles' action): stretched over the row, their label floated in the middle. */
53
54
  div[row] > button[act] {
54
- flex: 1;
55
+ flex: none;
55
56
  width: auto;
56
- margin-top: 0;
57
57
  }
58
58
  div[row] > span {
59
59
  flex: 1;
@@ -59,25 +59,48 @@ export class FigureModellerPage extends PageView {
59
59
  min-width: 0;
60
60
  min-height: 0;
61
61
  }
62
- /* The first field of the properties tab, in the side panel's look (side-panel-styles): a label above the field. */
62
+ /*
63
+ The first field of the properties tab, in the side panel's look (side-panel-styles): its label and its field on one
64
+ line, the field a line under its value. It is slotted into the figure panel, so the panel's measures reach it by
65
+ inheritance.
66
+ */
63
67
  label[type-field] {
64
68
  display: grid;
65
- gap: 2px;
66
- margin: 0 0 5px;
67
- font-size: 0.68rem;
69
+ grid-template-columns: var(--figure-label-width, 72px) minmax(0, 1fr);
70
+ align-items: center;
71
+ gap: 4px 8px;
72
+ margin: 0;
73
+ font-size: var(--ui-t-small, 11.5px);
68
74
  color: var(--md-sys-color-on-surface-variant);
69
75
  }
76
+ label[type-field] > :not(span) {
77
+ grid-column: 2;
78
+ }
70
79
  label[type-field] input,
71
80
  label[type-field] code {
72
81
  box-sizing: border-box;
73
- min-height: 30px;
74
- padding: 4px 7px;
82
+ min-height: var(--figure-control-height, 26px);
83
+ padding: 2px 2px;
75
84
  font-family: var(--ui-mono, ui-monospace, monospace);
76
- font-size: 0.76rem;
85
+ font-size: var(--ui-t-small, 11.5px);
86
+ line-height: calc(var(--figure-control-height, 26px) - 6px);
77
87
  color: var(--md-sys-color-on-surface);
78
- background: var(--md-sys-color-surface-container-lowest);
79
- border: 1px solid var(--md-sys-color-outline-variant);
80
- border-radius: 6px;
88
+ background: transparent;
89
+ border: 0;
90
+ border-bottom: 1px solid var(--md-sys-color-outline-variant, #c4c7c5);
91
+ border-radius: 0;
92
+ }
93
+ /* Fixed once released: its value with no line under it, read not edited. */
94
+ label[type-field] code {
95
+ border-bottom-color: transparent;
96
+ }
97
+ label[type-field] input:hover:not(:focus) {
98
+ border-bottom-color: var(--md-sys-color-on-surface-variant);
99
+ }
100
+ label[type-field] input:focus {
101
+ outline: none;
102
+ border-bottom-color: var(--md-sys-color-primary);
103
+ box-shadow: inset 0 -1px 0 var(--md-sys-color-primary);
81
104
  }
82
105
  label[type-field] small {
83
106
  color: var(--md-sys-color-on-surface-variant);
@@ -85,7 +108,7 @@ export class FigureModellerPage extends PageView {
85
108
  p[fit-error] {
86
109
  margin: 0;
87
110
  padding: 8px 12px;
88
- font-size: 12px;
111
+ font-size: var(--ui-t-small, 11.5px);
89
112
  color: var(--md-sys-color-error);
90
113
  }
91
114
  label[type-field] small[role='alert'] {
@@ -262,15 +262,13 @@ export const EditorStyles = css `
262
262
  /* The part's own numbers inside a group the links draw (size, place): one column, the panel's gap. */
263
263
  div[group-fields] {
264
264
  display: grid;
265
- gap: 8px;
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: 8px;\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