@things-factory/figure-ui 10.1.193 → 10.1.195
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.
- package/client/modeller/editor-styles.ts +10 -3
- package/client/modeller/editor.ts +61 -47
- package/client/modeller/figure-fasten-dialog.ts +11 -5
- package/client/modeller/refusal-teller.ts +8 -0
- package/client/pages/figure-list-page.ts +27 -40
- package/client/pages/figure-public-list-page.ts +21 -0
- package/client/route.ts +4 -0
- package/dist-client/modeller/editor-styles.js +10 -3
- package/dist-client/modeller/editor-styles.js.map +1 -1
- package/dist-client/modeller/editor.d.ts +18 -9
- package/dist-client/modeller/editor.js +62 -46
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-fasten-dialog.d.ts +3 -3
- package/dist-client/modeller/figure-fasten-dialog.js +11 -5
- package/dist-client/modeller/figure-fasten-dialog.js.map +1 -1
- package/dist-client/modeller/refusal-teller.d.ts +8 -0
- package/dist-client/modeller/refusal-teller.js +7 -0
- package/dist-client/modeller/refusal-teller.js.map +1 -1
- package/dist-client/pages/figure-list-page.d.ts +6 -3
- package/dist-client/pages/figure-list-page.js +25 -42
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-public-list-page.d.ts +16 -0
- package/dist-client/pages/figure-public-list-page.js +21 -0
- package/dist-client/pages/figure-public-list-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +3 -0
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/test/align.test.ts +13 -5
- package/test/browser/editing.check.mjs +9 -13
- package/test/browser/links.check.mjs +36 -22
- package/test/browser/list-page.js +35 -0
- package/test/browser/list.check.mjs +68 -0
- package/test/browser/modeller-page.js +48 -4
- package/test/browser/product-page.js +6 -0
- package/test/browser/props.check.mjs +2 -2
- package/test/figure-released.test.ts +1 -1
- package/things-factory.config.js +1 -0
- package/translations/en.json +4 -6
- package/translations/es.json +4 -6
- package/translations/ja.json +4 -6
- package/translations/ko.json +4 -6
- package/translations/ms.json +4 -6
- package/translations/zh.json +4 -6
|
@@ -295,12 +295,13 @@ export const EditorStyles = css`
|
|
|
295
295
|
}
|
|
296
296
|
/*
|
|
297
297
|
The view settings and the release history, opened from the bar over the right of the work: neither is about the
|
|
298
|
-
part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16).
|
|
298
|
+
part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16). The fastening window stands in
|
|
299
|
+
the same place, over the side panel, so the canvas it previews on stays in sight (user 2026-10-19).
|
|
299
300
|
*/
|
|
300
301
|
:host {
|
|
301
302
|
position: relative;
|
|
302
303
|
}
|
|
303
|
-
div[
|
|
304
|
+
div[side-overlay] {
|
|
304
305
|
position: absolute;
|
|
305
306
|
top: var(--figure-bar-height, 44px);
|
|
306
307
|
right: 8px;
|
|
@@ -327,10 +328,16 @@ export const EditorStyles = css`
|
|
|
327
328
|
div[overlay-head] button[quiet] {
|
|
328
329
|
margin-left: 0;
|
|
329
330
|
}
|
|
330
|
-
|
|
331
|
+
[overlay-body] {
|
|
331
332
|
min-height: 0;
|
|
332
333
|
overflow: auto;
|
|
333
334
|
}
|
|
335
|
+
/* The fastening window keeps to the panel's width: a popup's own sizing made it wider than the panel. */
|
|
336
|
+
div[fasten-panel] > figure-fasten-dialog {
|
|
337
|
+
box-sizing: border-box;
|
|
338
|
+
width: 100%;
|
|
339
|
+
min-width: 0;
|
|
340
|
+
}
|
|
334
341
|
/* Which part the panel speaks of. */
|
|
335
342
|
div[subject] {
|
|
336
343
|
position: sticky;
|
|
@@ -139,12 +139,12 @@ export class FigureEditor extends LitElement {
|
|
|
139
139
|
/** The view settings or the release history, open over the work from the bar. */
|
|
140
140
|
@state() private overlay?: BarOverlay
|
|
141
141
|
/**
|
|
142
|
-
* The fastening window open for the selected part (user 2026-10-17),
|
|
143
|
-
*
|
|
142
|
+
* The fastening window open for the selected part (user 2026-10-17): chosen there, applied once. It stands over the
|
|
143
|
+
* side panel, not over the canvas, so what is chosen is seen in 3D as it is chosen (user 2026-10-19: the standard
|
|
144
|
+
* popup, always modal, covered the preview). It closes with nothing written when the part, the tab, the middle or a
|
|
145
|
+
* bar panel changes under it.
|
|
144
146
|
*/
|
|
145
147
|
@state() private fastening = false
|
|
146
|
-
private fastenPopup?: { close: () => void }
|
|
147
|
-
private fastenWindow?: HTMLElement & { revision?: FigureRevision; partId: string; locked: boolean; previewWords: string[]; error: string }
|
|
148
148
|
/** Why the window's 「적용」 was refused, said in it. */
|
|
149
149
|
@state() private fastenError = ''
|
|
150
150
|
/** The face of the part a row of the window's list is on, lit on the canvas while the pointer or the arrows are there. */
|
|
@@ -234,7 +234,7 @@ export class FigureEditor extends LitElement {
|
|
|
234
234
|
aria-pressed=${String(this.gizmoMode === tool.mode)}
|
|
235
235
|
?disabled=${!tool.can}
|
|
236
236
|
aria-label=${i18next.t('figure.button.gizmo-' + tool.mode)}
|
|
237
|
-
title=${`${i18next.t('figure.button.gizmo-' + tool.mode)} (${tool.key})${tool.can || !this.selected ? '' : ' · ' +
|
|
237
|
+
title=${`${i18next.t('figure.button.gizmo-' + tool.mode)} (${tool.key})${tool.can || !this.selected ? '' : ' · ' + (tool.mode === 'scale' && this.sizeDragUnsure(this.asset()!, this.selected) ? this.sizeDragRefusalOf(this.selected) : i18next.t('figure.text.gizmo-not-own'))}`}
|
|
238
238
|
@click=${() => this.useGizmo(tool.mode)}
|
|
239
239
|
>
|
|
240
240
|
<md-icon>${tool.icon}</md-icon>
|
|
@@ -292,7 +292,7 @@ export class FigureEditor extends LitElement {
|
|
|
292
292
|
if (view && !view.geometry.some(g => ownerOf(g.id) === this.selected)) this.selected = ''
|
|
293
293
|
if (_changed.has('selected')) this.gizmoMode = undefined
|
|
294
294
|
/* The window is about the part chosen on the properties tab, being edited: it does not follow anywhere else. */
|
|
295
|
-
if (this.fastening && (_changed.has('selected') || !this.selected || this.tab !== 'properties' || this.middle !== 'edit')) this.closeFastening()
|
|
295
|
+
if (this.fastening && (_changed.has('selected') || !this.selected || this.tab !== 'properties' || this.middle !== 'edit' || this.overlay)) this.closeFastening()
|
|
296
296
|
/*
|
|
297
297
|
* The section editor is of one part: when the selection moves off it by any way -- another part picked, the scene's
|
|
298
298
|
* own Esc, the part removed, another figure opened -- it closes and lets go of the preview it held.
|
|
@@ -323,7 +323,7 @@ export class FigureEditor extends LitElement {
|
|
|
323
323
|
if (mode === 'scale' && this.selected && this.gizmoMode !== 'scale') {
|
|
324
324
|
const asset = this.asset()
|
|
325
325
|
if (asset && this.sizeDragUnsure(asset, this.selected)) {
|
|
326
|
-
this.
|
|
326
|
+
this.tellSizeByFields(this.selected)
|
|
327
327
|
return
|
|
328
328
|
}
|
|
329
329
|
}
|
|
@@ -579,6 +579,28 @@ export class FigureEditor extends LitElement {
|
|
|
579
579
|
return true
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* Why this part's size is not dragged, in one line (user 2026-10-19: the reason told the faces kept, and was not
|
|
584
|
+
* understood): attached turned or twisted, or turned off its upright.
|
|
585
|
+
*/
|
|
586
|
+
private sizeDragRefusalOf(id: string): string {
|
|
587
|
+
const fastened = !!this.asset()?.joints?.some(j => j.body1.part === ownerOf(id))
|
|
588
|
+
return i18next.t(fastened ? 'figure.text.gizmo-size-turned-fastening' : 'figure.text.gizmo-size-tilted', { part: partLabelOf(id) })
|
|
589
|
+
}
|
|
590
|
+
/** The size drag refused, said as a notice with the way to the part's size fields. */
|
|
591
|
+
private tellSizeByFields(id: string) {
|
|
592
|
+
this.refusals.point(this.sizeDragRefusalOf(id), { label: i18next.t('figure.button.to-size-fields'), run: () => this.toSizeFields(id) })
|
|
593
|
+
}
|
|
594
|
+
/** To the part's first size field it takes typed, on the properties tab, ready to type over. */
|
|
595
|
+
private async toSizeFields(id: string) {
|
|
596
|
+
this.middle = 'edit'
|
|
597
|
+
this.tab = 'properties'
|
|
598
|
+
this.selected = id
|
|
599
|
+
await this.updateComplete
|
|
600
|
+
const field = this.renderRoot.querySelector<HTMLInputElement>('[slot="size"] input:not([disabled]):not([readonly])')
|
|
601
|
+
field?.focus()
|
|
602
|
+
field?.select()
|
|
603
|
+
}
|
|
582
604
|
/** A drag, written back as the part's own inputs — one step of the history, like typing the numbers. */
|
|
583
605
|
private transformed(id: string, result: GizmoResult) {
|
|
584
606
|
const view = this.session!.read()
|
|
@@ -605,7 +627,7 @@ export class FigureEditor extends LitElement {
|
|
|
605
627
|
const jointed = !!asset.joints?.some(j => j.body1.part === ownerOf(id))
|
|
606
628
|
/* A size drag this part cannot show truly is not written: it springs back, and the reason is said. */
|
|
607
629
|
if (result.scale && result.scale.some(k => Math.abs(k - 1) > 1e-4) && this.sizeDragUnsure(asset, id)) {
|
|
608
|
-
this.
|
|
630
|
+
this.tellSizeByFields(id)
|
|
609
631
|
return this.requestUpdate()
|
|
610
632
|
}
|
|
611
633
|
/*
|
|
@@ -677,10 +699,6 @@ export class FigureEditor extends LitElement {
|
|
|
677
699
|
* fastened. Refused, it says why.
|
|
678
700
|
*/
|
|
679
701
|
/* What a tilted part's typed size keeps, by the figure's placement: not the floor's lowest point everywhere (designer 2026-09-27). */
|
|
680
|
-
private keptExtremeWord(): string {
|
|
681
|
-
const placement = (this.asset())?.placement ?? 'floor'
|
|
682
|
-
return i18next.t(`figure.label.kept-extreme-${placement}`)
|
|
683
|
-
}
|
|
684
702
|
/* How the selected part would meet the plane, worked out once per version and part. */
|
|
685
703
|
private planeContactOf(shown: FigureRevision, part: string): FigurePlaneContact | undefined {
|
|
686
704
|
return derivedOf(shown, `plane-contact|${part}`, () => {
|
|
@@ -883,6 +901,7 @@ export class FigureEditor extends LitElement {
|
|
|
883
901
|
private renderOverlay() {
|
|
884
902
|
const close = () => (this.overlay = undefined)
|
|
885
903
|
const panel = (which: BarOverlay, label: string, body: unknown) => html`<div
|
|
904
|
+
side-overlay
|
|
886
905
|
bar-overlay-panel=${which}
|
|
887
906
|
role="dialog"
|
|
888
907
|
aria-label=${i18next.t(label)}
|
|
@@ -1448,48 +1467,44 @@ export class FigureEditor extends LitElement {
|
|
|
1448
1467
|
this.openFastening()
|
|
1449
1468
|
}
|
|
1450
1469
|
private openFastening() {
|
|
1451
|
-
if (!this.selected || this.
|
|
1470
|
+
if (!this.selected || this.fastening) return
|
|
1452
1471
|
this.fastenError = ''
|
|
1453
1472
|
this.fastenHover = null
|
|
1473
|
+
/* One panel over the side at a time: the view or the history gives way to the window. */
|
|
1474
|
+
this.overlay = undefined
|
|
1454
1475
|
this.fastening = true
|
|
1455
|
-
const content = document.createElement('figure-fasten-dialog') as NonNullable<typeof this.fastenWindow>
|
|
1456
|
-
content.addEventListener('preview-command', e => this.commandPreview.ask((e as CustomEvent).detail))
|
|
1457
|
-
content.addEventListener('fasten-hover', e => this.hoverFastening((e as CustomEvent).detail))
|
|
1458
|
-
content.addEventListener('fasten-apply', e => this.applyFastening((e as CustomEvent).detail.action))
|
|
1459
|
-
content.addEventListener('fasten-cancel', () => this.closeFastening())
|
|
1460
|
-
this.fastenWindow = content
|
|
1461
|
-
this.tellFastening()
|
|
1462
|
-
const popup = openPopup(html`${content}`, { title: i18next.t('figure.label.fastening'), size: 'fit', backdrop: true, movable: true, closable: true, escapable: true })
|
|
1463
|
-
this.fastenPopup = popup
|
|
1464
|
-
/* Closed by its own X or Esc as well: the editor lets go of it the same way. */
|
|
1465
|
-
popup.result.then(() => this.fastenPopup === popup && this.fastenClosed())
|
|
1466
1476
|
}
|
|
1467
1477
|
private closeFastening() {
|
|
1468
|
-
const popup = this.fastenPopup
|
|
1469
|
-
this.fastenClosed()
|
|
1470
|
-
popup?.close()
|
|
1471
|
-
}
|
|
1472
|
-
private fastenClosed() {
|
|
1473
|
-
this.fastenPopup = undefined
|
|
1474
|
-
this.fastenWindow = undefined
|
|
1475
1478
|
this.fastening = false
|
|
1476
1479
|
this.fastenError = ''
|
|
1477
1480
|
this.fastenHover = null
|
|
1478
1481
|
}
|
|
1479
|
-
/**
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1482
|
+
/**
|
|
1483
|
+
* The fastening window over the side panel, as the bar's panels stand there: the figure on screen, the part, and what
|
|
1484
|
+
* the preview and a refusal said are the editor's. Its own Esc and 「취소」 close it, as its X does.
|
|
1485
|
+
*/
|
|
1486
|
+
private renderFastening(unavailable: boolean) {
|
|
1487
|
+
if (!this.fastening || !this.selected) return nothing
|
|
1488
|
+
return html`<div side-overlay fasten-panel>
|
|
1489
|
+
<div overlay-head>
|
|
1490
|
+
<h3>${i18next.t('figure.label.fastening')}</h3>
|
|
1491
|
+
<button quiet fasten-close aria-label=${i18next.t('figure.button.close')} title=${i18next.t('figure.button.close')} @click=${() => this.closeFastening()}>
|
|
1492
|
+
<md-icon>close</md-icon>
|
|
1493
|
+
</button>
|
|
1494
|
+
</div>
|
|
1495
|
+
<figure-fasten-dialog
|
|
1496
|
+
overlay-body
|
|
1497
|
+
.revision=${this.shown()}
|
|
1498
|
+
.partId=${this.selected}
|
|
1499
|
+
?locked=${unavailable}
|
|
1500
|
+
.previewWords=${this.commandPreview.words()}
|
|
1501
|
+
.error=${this.fastenError}
|
|
1502
|
+
@preview-command=${(e: CustomEvent) => this.commandPreview.ask(e.detail)}
|
|
1503
|
+
@fasten-hover=${(e: CustomEvent) => this.hoverFastening(e.detail)}
|
|
1504
|
+
@fasten-apply=${(e: CustomEvent) => this.applyFastening(e.detail.action)}
|
|
1505
|
+
@fasten-cancel=${() => this.closeFastening()}
|
|
1506
|
+
></figure-fasten-dialog>
|
|
1507
|
+
</div>`
|
|
1493
1508
|
}
|
|
1494
1509
|
/** 「적용」: the command, one step; refused, the window stays open and says why. */
|
|
1495
1510
|
private applyFastening(action: FigureAuthoringAction) {
|
|
@@ -1557,7 +1572,6 @@ export class FigureEditor extends LitElement {
|
|
|
1557
1572
|
}
|
|
1558
1573
|
updated(changed: PropertyValues<this>) {
|
|
1559
1574
|
super.updated?.(changed)
|
|
1560
|
-
this.tellFastening()
|
|
1561
1575
|
/* Nothing blocks release any more: the list under the bar closes, and opens fresh when something does again. */
|
|
1562
1576
|
if (changed.has('releaseBlockers') && !this.releaseBlockers.length && this.blockersOpen) this.blockersOpen = false
|
|
1563
1577
|
this.refusals.tell(changed)
|
|
@@ -1945,7 +1959,7 @@ export class FigureEditor extends LitElement {
|
|
|
1945
1959
|
<div panel flush ?off=${tab !== 'verdict'}><slot name="verdict"></slot></div>
|
|
1946
1960
|
</aside>
|
|
1947
1961
|
</ox-three-pane>
|
|
1948
|
-
${this.renderOverlay()}`
|
|
1962
|
+
${this.renderOverlay()} ${this.renderFastening(unavailable)}`
|
|
1949
1963
|
}
|
|
1950
1964
|
/** Position, rotation and size as rows of three cells; the shape's own measures one to a line, under their title. */
|
|
1951
1965
|
private renderGroups(
|
|
@@ -30,9 +30,9 @@ import type { FigureRevision } from './revision.js'
|
|
|
30
30
|
const PLANE = '__plane'
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* 붙이기 창: what the selected part is fastened to, on which face, how
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* 붙이기 창: what the selected part is fastened to, on which face, how (user 2026-10-17: 「속성에는 심플하고 직관적인
|
|
34
|
+
* 결과 위주로, 편집은 팝업에서 넓은 공간을 써서」). The editor stands it over the side panel with its title and close,
|
|
35
|
+
* where the canvas stays in sight (2026-10-19: the standard popup, always modal, hid the 3D it was choosing for).
|
|
36
36
|
*
|
|
37
37
|
* Everything chosen here is tried, not done: the command it comes to is told to the editor as it changes
|
|
38
38
|
* (`preview-command`), and the canvas draws it. 「적용」 sends it once (`fasten-apply`), one step of the history;
|
|
@@ -104,6 +104,14 @@ export class FigureFastenDialog extends localize(i18next)(LitElement) {
|
|
|
104
104
|
margin: 0;
|
|
105
105
|
color: var(--md-sys-color-error);
|
|
106
106
|
}
|
|
107
|
+
/* 취소 · 적용 stay in sight at the window's foot when what is above them is taller than the panel. */
|
|
108
|
+
ox-popup-actions {
|
|
109
|
+
position: sticky;
|
|
110
|
+
bottom: calc(-1 * var(--popup-padding, 16px));
|
|
111
|
+
margin-bottom: calc(-1 * var(--popup-padding, 16px));
|
|
112
|
+
padding-bottom: var(--popup-padding, 16px);
|
|
113
|
+
background: var(--md-sys-color-surface-container-lowest);
|
|
114
|
+
}
|
|
107
115
|
`
|
|
108
116
|
]
|
|
109
117
|
|
|
@@ -410,8 +418,6 @@ export class FigureFastenDialog extends localize(i18next)(LitElement) {
|
|
|
410
418
|
this.setAttribute('role', 'dialog')
|
|
411
419
|
this.setAttribute('aria-label', i18next.t('figure.label.fastening'))
|
|
412
420
|
this.addEventListener('keydown', this.onKey)
|
|
413
|
-
/* The popup's fitting width is the overlay's own setting; the window asks it for room for the face and the way side by side. */
|
|
414
|
-
this.closest<HTMLElement>('ox-floating-overlay')?.style.setProperty('--overlay-center-fit-width', '640px')
|
|
415
421
|
}
|
|
416
422
|
|
|
417
423
|
private onKey = async (e: KeyboardEvent) => {
|
|
@@ -47,6 +47,14 @@ export class RefusalTeller {
|
|
|
47
47
|
this.toldViewError = viewError
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* What cannot be done this way, with the way it can, as a notice: nothing went wrong, so not the error's red
|
|
52
|
+
* (user 2026-10-19, the size drag on a part attached turned). Given more time than a plain notice, for its button.
|
|
53
|
+
*/
|
|
54
|
+
point(message: string, way: { label: string; run: () => void }) {
|
|
55
|
+
notify({ level: 'info', message, option: { action: { label: way.label, callback: way.run }, timer: 8000 } })
|
|
56
|
+
}
|
|
57
|
+
|
|
50
58
|
/*
|
|
51
59
|
An edit that would stop clips is refused by figure-model with the clips named (CLIPS_DROPPED). The person is told
|
|
52
60
|
which, and why, before anything is written; agreed, the same edit is made again letting exactly those go, as one
|
|
@@ -2,8 +2,8 @@ import '@operato/data-grist'
|
|
|
2
2
|
import '@material/web/icon/icon.js'
|
|
3
3
|
import '../viewparts/figure-thumb.js'
|
|
4
4
|
|
|
5
|
-
import { css, html } from 'lit'
|
|
6
|
-
import { customElement, property, query
|
|
5
|
+
import { css, html, nothing } from 'lit'
|
|
6
|
+
import { customElement, property, query } from 'lit/decorators.js'
|
|
7
7
|
|
|
8
8
|
import type { DataGrist, FetchOption } from '@operato/data-grist'
|
|
9
9
|
import { i18next, localize } from '@operato/i18n'
|
|
@@ -97,23 +97,6 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
97
97
|
카드 · 그림 칸 · 만들기 FAB 는 모든 카드 카탈로그와 같다(@operato/styles CardCatalogStyles,
|
|
98
98
|
ADR-0117 개정 2026-10-13). 여기에는 이 화면에만 있는 것만 둔다.
|
|
99
99
|
*/
|
|
100
|
-
/* Which figures are listed: quiet words beside the filters, coloured when they are on. */
|
|
101
|
-
button[scope] {
|
|
102
|
-
font: inherit;
|
|
103
|
-
padding: 0 8px;
|
|
104
|
-
border: none;
|
|
105
|
-
background: none;
|
|
106
|
-
color: var(--md-sys-color-on-surface-variant);
|
|
107
|
-
cursor: pointer;
|
|
108
|
-
white-space: nowrap;
|
|
109
|
-
}
|
|
110
|
-
button[scope]:hover {
|
|
111
|
-
color: var(--md-sys-color-on-surface);
|
|
112
|
-
}
|
|
113
|
-
button[scope][aria-pressed='true'] {
|
|
114
|
-
color: var(--md-sys-color-primary);
|
|
115
|
-
font-weight: 600;
|
|
116
|
-
}
|
|
117
100
|
ox-grist {
|
|
118
101
|
/* 상태와 성숙도는 짧은 표식이다 — 두 줄로 세우지 않고 한 줄에 뉜다 */
|
|
119
102
|
--data-card-detail-direction: row;
|
|
@@ -127,14 +110,18 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
127
110
|
|
|
128
111
|
@query('ox-grist') private grist!: DataGrist
|
|
129
112
|
|
|
130
|
-
/**
|
|
131
|
-
|
|
113
|
+
/**
|
|
114
|
+
* The domain's own figures, or other domains' public ones (LV figure-server design G2), read only. Each is a screen
|
|
115
|
+
* of its own on the rail (user 2026-10-19): the own catalogue is where figures are made and changed, the public one
|
|
116
|
+
* only looked at, so a switch inside one list mixed two jobs. `figure-public-list-page` sets it.
|
|
117
|
+
*/
|
|
118
|
+
protected readonly scope: 'own' | 'public' = 'own'
|
|
132
119
|
|
|
133
120
|
private listen?: () => void
|
|
134
121
|
|
|
135
122
|
get context() {
|
|
136
123
|
return {
|
|
137
|
-
title: i18next.t('figure.title.figures'),
|
|
124
|
+
title: i18next.t(this.scope === 'public' ? 'figure.title.public-figures' : 'figure.title.figures'),
|
|
138
125
|
search: {
|
|
139
126
|
handler: (search: string) => {
|
|
140
127
|
this.grist.searchText = search
|
|
@@ -171,15 +158,16 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
171
158
|
<div class="filters">
|
|
172
159
|
<ox-filters-form autofocus without-search></ox-filters-form>
|
|
173
160
|
</div>
|
|
174
|
-
<button scope aria-pressed=${String(this.scope === 'public')} @click=${() => this.toggleScope()}>${i18next.t('figure.button.public-figures')}</button>
|
|
175
161
|
</div>
|
|
176
162
|
|
|
177
163
|
<ox-grist-personalizer slot="setting"></ox-grist-personalizer>
|
|
178
164
|
</ox-grist>
|
|
179
165
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
166
|
+
${this.scope === 'own'
|
|
167
|
+
? html`<button fab title=${i18next.t('figure.button.new-figure')} @click=${() => this.openModeller()}>
|
|
168
|
+
<md-icon>add</md-icon>${i18next.t('figure.button.new-figure')}
|
|
169
|
+
</button>`
|
|
170
|
+
: nothing}
|
|
183
171
|
`
|
|
184
172
|
}
|
|
185
173
|
|
|
@@ -340,15 +328,19 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
340
328
|
이름표는 비워 둔다. 단추에 「초안」·「발행됨」이라고 적혀 있으니 그 앞의 「상태」는
|
|
341
329
|
같은 말을 한 번 더 하는 것이고, 걸러 보기 줄에서 자리만 먹는다.
|
|
342
330
|
*/
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
331
|
+
/* Another domain's public figures are released ones only: no draft to tell apart, no choice to offer. */
|
|
332
|
+
filter:
|
|
333
|
+
this.scope === 'own'
|
|
334
|
+
? {
|
|
335
|
+
type: 'select-buttons',
|
|
336
|
+
operator: 'in',
|
|
337
|
+
label: '',
|
|
338
|
+
options: [
|
|
339
|
+
{ display: i18next.t('figure.label.draft'), value: 'draft' },
|
|
340
|
+
{ display: i18next.t('figure.label.figure-released'), value: 'released' }
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
: undefined,
|
|
352
344
|
width: 120
|
|
353
345
|
},
|
|
354
346
|
{
|
|
@@ -559,11 +551,6 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
559
551
|
navigate(modellerPathOf(figure))
|
|
560
552
|
}
|
|
561
553
|
|
|
562
|
-
private toggleScope() {
|
|
563
|
-
this.scope = this.scope === 'public' ? 'own' : 'public'
|
|
564
|
-
this.grist?.fetch()
|
|
565
|
-
}
|
|
566
|
-
|
|
567
554
|
/** `remove` 는 HTMLElement 의 메서드다 — 가리면 조용한 결함이 된다. */
|
|
568
555
|
private async removeFigure(figure: Figure) {
|
|
569
556
|
if (this.scope === 'public') return
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { customElement } from 'lit/decorators.js'
|
|
2
|
+
|
|
3
|
+
import { FigureListPage } from './figure-list-page.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Other domains' public figures (LV figure-server design G2), a screen of its own on the rail (user 2026-10-19).
|
|
7
|
+
*
|
|
8
|
+
* Looked at only: each shows its latest release and its domain, and nothing on it is changed from here -- no new
|
|
9
|
+
* figure, no delete, no edit (`figure-detail-dock.ts` \`foreign\`). The catalogue's own class with the other scope, so
|
|
10
|
+
* the two lists are one list.
|
|
11
|
+
*/
|
|
12
|
+
@customElement('figure-public-list-page')
|
|
13
|
+
export class FigurePublicListPage extends FigureListPage {
|
|
14
|
+
protected readonly scope = 'public' as const
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare global {
|
|
18
|
+
interface HTMLElementTagNameMap {
|
|
19
|
+
'figure-public-list-page': FigurePublicListPage
|
|
20
|
+
}
|
|
21
|
+
}
|
package/client/route.ts
CHANGED
|
@@ -4,6 +4,10 @@ export default function route(page: string) {
|
|
|
4
4
|
import('./pages/figure-list-page')
|
|
5
5
|
return page
|
|
6
6
|
|
|
7
|
+
case 'figure-public-list':
|
|
8
|
+
import('./pages/figure-public-list-page')
|
|
9
|
+
return page
|
|
10
|
+
|
|
7
11
|
case 'figure-modeller':
|
|
8
12
|
import('./pages/figure-modeller-page')
|
|
9
13
|
return page
|
|
@@ -294,12 +294,13 @@ export const EditorStyles = css `
|
|
|
294
294
|
}
|
|
295
295
|
/*
|
|
296
296
|
The view settings and the release history, opened from the bar over the right of the work: neither is about the
|
|
297
|
-
part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16).
|
|
297
|
+
part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16). The fastening window stands in
|
|
298
|
+
the same place, over the side panel, so the canvas it previews on stays in sight (user 2026-10-19).
|
|
298
299
|
*/
|
|
299
300
|
:host {
|
|
300
301
|
position: relative;
|
|
301
302
|
}
|
|
302
|
-
div[
|
|
303
|
+
div[side-overlay] {
|
|
303
304
|
position: absolute;
|
|
304
305
|
top: var(--figure-bar-height, 44px);
|
|
305
306
|
right: 8px;
|
|
@@ -326,10 +327,16 @@ export const EditorStyles = css `
|
|
|
326
327
|
div[overlay-head] button[quiet] {
|
|
327
328
|
margin-left: 0;
|
|
328
329
|
}
|
|
329
|
-
|
|
330
|
+
[overlay-body] {
|
|
330
331
|
min-height: 0;
|
|
331
332
|
overflow: auto;
|
|
332
333
|
}
|
|
334
|
+
/* The fastening window keeps to the panel's width: a popup's own sizing made it wider than the panel. */
|
|
335
|
+
div[fasten-panel] > figure-fasten-dialog {
|
|
336
|
+
box-sizing: border-box;
|
|
337
|
+
width: 100%;
|
|
338
|
+
min-width: 0;
|
|
339
|
+
}
|
|
333
340
|
/* Which part the panel speaks of. */
|
|
334
341
|
div[subject] {
|
|
335
342
|
position: sticky;
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoa9B,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 /*\n The bar's two commitments, one shape (user 2026-10-18: 「발행버튼도 저장버튼도 라운드 버튼으로 … 버튼 안에 적절한 아이콘」):\n round, an icon inside, so neither is taken for the other. Save, done many times a day, is the filled one; release,\n a promise to whoever draws the figure, is outlined.\n */\n ox-canvas-bar button[save],\n ox-canvas-bar button[publish] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n padding: 0 14px 0 10px;\n border-radius: 999em;\n font-weight: 600;\n }\n ox-canvas-bar button[save] md-icon,\n ox-canvas-bar button[publish] md-icon {\n --md-icon-size: 18px;\n }\n ox-canvas-bar button[save] {\n border: 1px solid transparent;\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 14px 0 10px;\n border: none;\n /* Round as save and release beside it: one shape for the bar's end. */\n border-radius: 999em;\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 border: 1px solid var(--md-sys-color-outline);\n color: var(--md-sys-color-primary);\n background: var(--md-sys-color-surface);\n }\n ox-canvas-bar button[publish][aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2a9B,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 /*\n The bar's two commitments, one shape (user 2026-10-18: 「발행버튼도 저장버튼도 라운드 버튼으로 … 버튼 안에 적절한 아이콘」):\n round, an icon inside, so neither is taken for the other. Save, done many times a day, is the filled one; release,\n a promise to whoever draws the figure, is outlined.\n */\n ox-canvas-bar button[save],\n ox-canvas-bar button[publish] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n padding: 0 14px 0 10px;\n border-radius: 999em;\n font-weight: 600;\n }\n ox-canvas-bar button[save] md-icon,\n ox-canvas-bar button[publish] md-icon {\n --md-icon-size: 18px;\n }\n ox-canvas-bar button[save] {\n border: 1px solid transparent;\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). The fastening window stands in\n the same place, over the side panel, so the canvas it previews on stays in sight (user 2026-10-19).\n */\n :host {\n position: relative;\n }\n div[side-overlay] {\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 [overlay-body] {\n min-height: 0;\n overflow: auto;\n }\n /* The fastening window keeps to the panel's width: a popup's own sizing made it wider than the panel. */\n div[fasten-panel] > figure-fasten-dialog {\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\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 14px 0 10px;\n border: none;\n /* Round as save and release beside it: one shape for the bar's end. */\n border-radius: 999em;\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 border: 1px solid var(--md-sys-color-outline);\n color: var(--md-sys-color-primary);\n background: var(--md-sys-color-surface);\n }\n ox-canvas-bar button[publish][aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\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"]}
|
|
@@ -65,12 +65,12 @@ export declare class FigureEditor extends LitElement {
|
|
|
65
65
|
/** The view settings or the release history, open over the work from the bar. */
|
|
66
66
|
private overlay?;
|
|
67
67
|
/**
|
|
68
|
-
* The fastening window open for the selected part (user 2026-10-17),
|
|
69
|
-
*
|
|
68
|
+
* The fastening window open for the selected part (user 2026-10-17): chosen there, applied once. It stands over the
|
|
69
|
+
* side panel, not over the canvas, so what is chosen is seen in 3D as it is chosen (user 2026-10-19: the standard
|
|
70
|
+
* popup, always modal, covered the preview). It closes with nothing written when the part, the tab, the middle or a
|
|
71
|
+
* bar panel changes under it.
|
|
70
72
|
*/
|
|
71
73
|
private fastening;
|
|
72
|
-
private fastenPopup?;
|
|
73
|
-
private fastenWindow?;
|
|
74
74
|
/** Why the window's 「적용」 was refused, said in it. */
|
|
75
75
|
private fastenError;
|
|
76
76
|
/** The face of the part a row of the window's list is on, lit on the canvas while the pointer or the arrows are there. */
|
|
@@ -185,6 +185,15 @@ export declare class FigureEditor extends LitElement {
|
|
|
185
185
|
private reachOf;
|
|
186
186
|
/** Whether a size drag on this part could not show what is written: figure-model gives no face it keeps. */
|
|
187
187
|
private sizeDragUnsure;
|
|
188
|
+
/**
|
|
189
|
+
* Why this part's size is not dragged, in one line (user 2026-10-19: the reason told the faces kept, and was not
|
|
190
|
+
* understood): attached turned or twisted, or turned off its upright.
|
|
191
|
+
*/
|
|
192
|
+
private sizeDragRefusalOf;
|
|
193
|
+
/** The size drag refused, said as a notice with the way to the part's size fields. */
|
|
194
|
+
private tellSizeByFields;
|
|
195
|
+
/** To the part's first size field it takes typed, on the properties tab, ready to type over. */
|
|
196
|
+
private toSizeFields;
|
|
188
197
|
/** A drag, written back as the part's own inputs — one step of the history, like typing the numbers. */
|
|
189
198
|
private transformed;
|
|
190
199
|
/** 맞추기 — one tool, one step of the history, through the same inputs as the gizmo. */
|
|
@@ -197,7 +206,6 @@ export declare class FigureEditor extends LitElement {
|
|
|
197
206
|
* its top, a wall one's to the wall by its back, a centred one's centre to the origin's height. Once: nothing is
|
|
198
207
|
* fastened. Refused, it says why.
|
|
199
208
|
*/
|
|
200
|
-
private keptExtremeWord;
|
|
201
209
|
private planeContactOf;
|
|
202
210
|
private renderPlaneAlign;
|
|
203
211
|
private alignToPlane;
|
|
@@ -307,10 +315,11 @@ export declare class FigureEditor extends LitElement {
|
|
|
307
315
|
private goFasten;
|
|
308
316
|
private openFastening;
|
|
309
317
|
private closeFastening;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
318
|
+
/**
|
|
319
|
+
* The fastening window over the side panel, as the bar's panels stand there: the figure on screen, the part, and what
|
|
320
|
+
* the preview and a refusal said are the editor's. Its own Esc and 「취소」 close it, as its X does.
|
|
321
|
+
*/
|
|
322
|
+
private renderFastening;
|
|
314
323
|
/** 「적용」: the command, one step; refused, the window stays open and says why. */
|
|
315
324
|
private applyFastening;
|
|
316
325
|
/** A row of the window's list under the pointer or the arrows: the face of that part it would be fastened on, lit. */
|