@things-factory/figure-ui 10.1.7 → 10.1.8
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/graphql/index.ts +24 -0
- package/client/modeller/figure-animations.ts +61 -4
- package/client/modeller/figure-ask.ts +6 -1
- package/client/modeller/figure-history-panel.ts +52 -1
- package/client/modeller/figure-inspector.ts +477 -124
- package/client/modeller/proposal-session.ts +92 -0
- package/client/pages/figure-modeller-page.ts +179 -53
- package/client/types.ts +33 -0
- package/dist-client/graphql/index.d.ts +3 -1
- package/dist-client/graphql/index.js +21 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-animations.d.ts +4 -1
- package/dist-client/modeller/figure-animations.js +57 -4
- package/dist-client/modeller/figure-animations.js.map +1 -1
- package/dist-client/modeller/figure-ask.d.ts +3 -0
- package/dist-client/modeller/figure-ask.js +9 -0
- package/dist-client/modeller/figure-ask.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +7 -0
- package/dist-client/modeller/figure-history-panel.js +50 -0
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-inspector.d.ts +3 -0
- package/dist-client/modeller/figure-inspector.js +464 -111
- package/dist-client/modeller/figure-inspector.js.map +1 -1
- package/dist-client/modeller/proposal-session.d.ts +23 -0
- package/dist-client/modeller/proposal-session.js +65 -0
- package/dist-client/modeller/proposal-session.js.map +1 -0
- package/dist-client/pages/figure-modeller-page.d.ts +43 -9
- package/dist-client/pages/figure-modeller-page.js +173 -50
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +38 -0
- package/dist-client/types.js.map +1 -1
- package/package.json +4 -4
- package/test/ai-proposal-contract.test.ts +169 -0
- package/translations/en.json +32 -0
- package/translations/ja.json +32 -0
- package/translations/ko.json +42 -0
- package/translations/ms.json +32 -0
- package/translations/zh.json +32 -0
|
@@ -64,12 +64,10 @@ export declare class FigureModellerPage extends FigureModellerPageBase {
|
|
|
64
64
|
* 미저장 표시(`dirty`)는 그대로 남으므로 데이터를 잃지는 않았다. 잃은 것은 **사유**다.
|
|
65
65
|
*/
|
|
66
66
|
private saveFailure;
|
|
67
|
-
/** 저작
|
|
68
|
-
private
|
|
69
|
-
/**
|
|
70
|
-
private
|
|
71
|
-
/** 받기로 고른 변경들. 처음에는 전부 골라 둔다. */
|
|
72
|
-
private picked;
|
|
67
|
+
/** 저작 보조 또는 발행 게이트가 연 후보 검토 세션. 받기 전에는 정본이 아니다. */
|
|
68
|
+
private proposalSession?;
|
|
69
|
+
/** 인라인 요청창과 AI 도크가 함께 쓰는 현재 Figure의 제한된 세션 피드백. */
|
|
70
|
+
private proposalFeedback;
|
|
73
71
|
/**
|
|
74
72
|
* 저장 형식으로 변환한 것. 씬 모델이 바뀔 때만 다시 만든다.
|
|
75
73
|
*
|
|
@@ -78,6 +76,19 @@ export declare class FigureModellerPage extends FigureModellerPageBase {
|
|
|
78
76
|
*/
|
|
79
77
|
private folded?;
|
|
80
78
|
willUpdate(changed: Map<string, unknown>): void;
|
|
79
|
+
/**
|
|
80
|
+
* 페이지 밖 AI 도크에도 **지금 손에 든 정본**을 알려 준다.
|
|
81
|
+
*
|
|
82
|
+
* 모델러 안의 `figure-ask` 는 이미 `folded`를 직접 받는다. 반면 asidebar 도크는 주소의 id만
|
|
83
|
+
* 알았으므로 DB에 저장된 옛 판을 읽었다. 저장 전 변경을 두고 "왜 발행이 안 되나" 또는
|
|
84
|
+
* "이 부품을 줄여 달라"고 물으면 대화가 엉뚱한 판을 보게 되는 결함이었다.
|
|
85
|
+
*
|
|
86
|
+
* 이 이벤트는 저장하지 않는다. 도크가 대화 인자로만 잠시 들고 있고, 서버 도구도 도메인 소속
|
|
87
|
+
* Figure를 먼저 확인한 뒤에만 이 source를 사용한다. 페이지를 떠날 때는 아래에서 지워 오래된
|
|
88
|
+
* 초안이 다음 Figure에 섞이지 않게 한다.
|
|
89
|
+
*/
|
|
90
|
+
updated(changed: Map<string, unknown>): void;
|
|
91
|
+
disconnectedCallback(): void;
|
|
81
92
|
get context(): {
|
|
82
93
|
title: string;
|
|
83
94
|
heading: boolean;
|
|
@@ -110,10 +121,12 @@ export declare class FigureModellerPage extends FigureModellerPageBase {
|
|
|
110
121
|
private renderCollapse;
|
|
111
122
|
private toggleCollapse;
|
|
112
123
|
/**
|
|
113
|
-
* AI 후보를
|
|
124
|
+
* AI 후보를 실제 배치 크기에서도 본다.
|
|
114
125
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
126
|
+
* 편집 캔버스 한 장으로는 `repeat`·`fixed`·`anchor`가 맞는지 알 수 없다. 후보는 아직
|
|
127
|
+
* 정본이 아니므로 저장하지 않고, `figure-preview`가 같은 FigureSource를 기준 크기와
|
|
128
|
+
* 일곱 가지 확장 축에서 실제 FigureInstance로 세운다. 따라서 저작자는 "좋아 보이는
|
|
129
|
+
* 한 장"이 아니라 배치될 때도 붙어 있는지를 보고 수락한다.
|
|
117
130
|
*/
|
|
118
131
|
private renderProposed;
|
|
119
132
|
/**
|
|
@@ -131,6 +144,8 @@ export declare class FigureModellerPage extends FigureModellerPageBase {
|
|
|
131
144
|
* 안 들고, 우리가 생각 못 한 조합을 막아 주기 때문이다.
|
|
132
145
|
*/
|
|
133
146
|
private renderDecision;
|
|
147
|
+
/** 후보 품질 판단의 근거를 수락 전에 전부 보여 준다. */
|
|
148
|
+
private renderQualityReview;
|
|
134
149
|
/** 변경 하나. 무엇을 하겠다는 것인지 부호로 먼저 말한다. */
|
|
135
150
|
private renderChange;
|
|
136
151
|
private toggle;
|
|
@@ -141,6 +156,17 @@ export declare class FigureModellerPage extends FigureModellerPageBase {
|
|
|
141
156
|
* 무엇이 자기 것이고 무엇이 기계 것인지 모르게 된다.
|
|
142
157
|
*/
|
|
143
158
|
private receive;
|
|
159
|
+
/**
|
|
160
|
+
* 발행 게이트의 구조화된 수정안 하나를 현재 작업 사본에만 겹쳐 본다. 서버가 준 값은
|
|
161
|
+
* `part.anchor.axis = to`라는 데이터이지 사람이 읽을 문장을 다시 해석한 결과가 아니다.
|
|
162
|
+
* 원본 `board`/`parts`는 여기서 전혀 바꾸지 않고, 아래의 `take()`만 바꾼 사본을 적용한다.
|
|
163
|
+
*/
|
|
164
|
+
private previewSizingFix;
|
|
165
|
+
/**
|
|
166
|
+
* 원클릭 적용은 서버가 모든 크기 변경 case를 재검사해 안전하다고 표시한 한-앵커 후보에만 연다.
|
|
167
|
+
* 그래도 저장은 저작자의 별도 행위다. 잘못 눌러도 DB 정본이나 발행판은 바뀌지 않는다.
|
|
168
|
+
*/
|
|
169
|
+
private applySizingFix;
|
|
144
170
|
/**
|
|
145
171
|
* 고른 변경을 받아들인다.
|
|
146
172
|
*
|
|
@@ -148,6 +174,14 @@ export declare class FigureModellerPage extends FigureModellerPageBase {
|
|
|
148
174
|
* 하므로 그쪽이 자연스럽다. 받아들인 결과는 다시 씬 모델로 되돌려 편집을 이어간다.
|
|
149
175
|
*/
|
|
150
176
|
private take;
|
|
177
|
+
/**
|
|
178
|
+
* 후보에 대한 사람의 결정을 도크에만 알린다.
|
|
179
|
+
*
|
|
180
|
+
* 이 기록은 분석 이벤트나 Figure 속성이 아니다. 같은 모델러 세션에서 다음 요청을 더 잘
|
|
181
|
+
* 이해시키는 짧은 신호일 뿐이다. 원본 source나 사람의 작업 내용을 다시 보내지 않고, 사람이
|
|
182
|
+
* 후보 변경 중 몇 개를 받아들였는지와 이미 화면에 보였던 품질 판정만 보낸다.
|
|
183
|
+
*/
|
|
184
|
+
private reportProposalFeedback;
|
|
151
185
|
private discard;
|
|
152
186
|
/**
|
|
153
187
|
* 저장할 수 있나.
|
|
@@ -14,6 +14,7 @@ import { navigate, PageView } from '@operato/shell';
|
|
|
14
14
|
import { validate } from '@hatiolab/figure-model';
|
|
15
15
|
import * as edits from '../modeller/part-edits.js';
|
|
16
16
|
import * as proposals from '../modeller/proposal.js';
|
|
17
|
+
import * as proposalSessions from '../modeller/proposal-session.js';
|
|
17
18
|
import { toFigureSource, fromFigureSource } from '../modeller/figure-source.js';
|
|
18
19
|
import { DEFAULT_VIEW } from '../modeller/figure-view.js';
|
|
19
20
|
import { createFigure, fetchFigure, fetchFigureTypeNames, updateFigure } from '../graphql/index.js';
|
|
@@ -126,8 +127,8 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
126
127
|
* 미저장 표시(`dirty`)는 그대로 남으므로 데이터를 잃지는 않았다. 잃은 것은 **사유**다.
|
|
127
128
|
*/
|
|
128
129
|
this.saveFailure = '';
|
|
129
|
-
/**
|
|
130
|
-
this.
|
|
130
|
+
/** 인라인 요청창과 AI 도크가 함께 쓰는 현재 Figure의 제한된 세션 피드백. */
|
|
131
|
+
this.proposalFeedback = [];
|
|
131
132
|
}
|
|
132
133
|
static { this.styles = css `
|
|
133
134
|
:host {
|
|
@@ -453,6 +454,39 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
453
454
|
cursor: default;
|
|
454
455
|
}
|
|
455
456
|
|
|
457
|
+
/* AI의 평가는 판정문이 아니라 수락 전에 읽을 근거다. 숨기지 않고 변경 칩 위에 둔다. */
|
|
458
|
+
div[quality-review] {
|
|
459
|
+
padding: 7px var(--spacing-large, 12px);
|
|
460
|
+
background-color: var(--md-sys-color-primary-container);
|
|
461
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
462
|
+
color: var(--md-sys-color-on-primary-container);
|
|
463
|
+
font-size: 0.72rem;
|
|
464
|
+
line-height: 1.45;
|
|
465
|
+
}
|
|
466
|
+
div[quality-review][review] {
|
|
467
|
+
background-color: var(--md-sys-color-tertiary-container);
|
|
468
|
+
color: var(--md-sys-color-on-tertiary-container);
|
|
469
|
+
}
|
|
470
|
+
div[quality-review] strong { display: block; font-size: 0.74rem; }
|
|
471
|
+
div[quality-review] ul { margin: 4px 0 0; padding-left: 18px; }
|
|
472
|
+
div[proposal-note] {
|
|
473
|
+
padding: 6px var(--spacing-large, 12px) 8px;
|
|
474
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
475
|
+
background-color: var(--md-sys-color-surface-container-low);
|
|
476
|
+
}
|
|
477
|
+
div[proposal-note] label { display: block; font-size: 0.71rem; color: var(--md-sys-color-on-surface-variant); }
|
|
478
|
+
div[proposal-note] input {
|
|
479
|
+
box-sizing: border-box;
|
|
480
|
+
width: 100%;
|
|
481
|
+
margin-top: 4px;
|
|
482
|
+
padding: 5px 7px;
|
|
483
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
484
|
+
border-radius: 5px;
|
|
485
|
+
color: var(--md-sys-color-on-surface);
|
|
486
|
+
background: var(--md-sys-color-surface);
|
|
487
|
+
font: inherit;
|
|
488
|
+
}
|
|
489
|
+
|
|
456
490
|
/*
|
|
457
491
|
무엇을 바꾸자는 것인가 — 칩 하나가 변경 하나다.
|
|
458
492
|
|
|
@@ -513,6 +547,31 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
513
547
|
this.folded = this.board ? toFigureSource(this.board, this.parts) : undefined;
|
|
514
548
|
}
|
|
515
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* 페이지 밖 AI 도크에도 **지금 손에 든 정본**을 알려 준다.
|
|
552
|
+
*
|
|
553
|
+
* 모델러 안의 `figure-ask` 는 이미 `folded`를 직접 받는다. 반면 asidebar 도크는 주소의 id만
|
|
554
|
+
* 알았으므로 DB에 저장된 옛 판을 읽었다. 저장 전 변경을 두고 "왜 발행이 안 되나" 또는
|
|
555
|
+
* "이 부품을 줄여 달라"고 물으면 대화가 엉뚱한 판을 보게 되는 결함이었다.
|
|
556
|
+
*
|
|
557
|
+
* 이 이벤트는 저장하지 않는다. 도크가 대화 인자로만 잠시 들고 있고, 서버 도구도 도메인 소속
|
|
558
|
+
* Figure를 먼저 확인한 뒤에만 이 source를 사용한다. 페이지를 떠날 때는 아래에서 지워 오래된
|
|
559
|
+
* 초안이 다음 Figure에 섞이지 않게 한다.
|
|
560
|
+
*/
|
|
561
|
+
updated(changed) {
|
|
562
|
+
if (changed.has('figure') && changed.get('figure')?.id !== this.figure?.id) {
|
|
563
|
+
this.proposalFeedback = [];
|
|
564
|
+
}
|
|
565
|
+
if (changed.has('board') || changed.has('parts') || changed.has('figure')) {
|
|
566
|
+
window.dispatchEvent(new CustomEvent('figure-ai-context', {
|
|
567
|
+
detail: { figureId: this.figure?.id, source: this.folded }
|
|
568
|
+
}));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
disconnectedCallback() {
|
|
572
|
+
window.dispatchEvent(new CustomEvent('figure-ai-context', { detail: undefined }));
|
|
573
|
+
super.disconnectedCallback();
|
|
574
|
+
}
|
|
516
575
|
get context() {
|
|
517
576
|
return {
|
|
518
577
|
title: this.figure?.name ?? i18next.t('figure.title.new-figure'),
|
|
@@ -546,6 +605,7 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
546
605
|
<figure-ask
|
|
547
606
|
.source=${source}
|
|
548
607
|
.type=${source?.type ?? ''}
|
|
608
|
+
.feedback=${this.proposalFeedback}
|
|
549
609
|
@proposed=${(e) => this.receive(e.detail.proposal)}
|
|
550
610
|
></figure-ask>
|
|
551
611
|
${this.saveFailure ? html `<span save-failed title=${this.saveFailure}>${this.saveFailure}</span>` : ''}
|
|
@@ -562,6 +622,8 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
562
622
|
@figure-catalog=${(e) => this.catalog(e.detail)}
|
|
563
623
|
@figure-released=${(e) => this.released(e.detail.figure)}
|
|
564
624
|
@figure-reverted=${() => this.figure?.id && this.load(this.figure.id)}
|
|
625
|
+
@preview-sizing-fix=${(e) => this.previewSizingFix(e.detail)}
|
|
626
|
+
@apply-sizing-fix=${(e) => this.applySizingFix(e.detail)}
|
|
565
627
|
@add-part=${(e) => this.addPart(e.detail.primitive)}
|
|
566
628
|
@select-part=${(e) => (this.selected = e.detail.index)}
|
|
567
629
|
@remove-part=${(e) => this.removePart(e.detail.index)}
|
|
@@ -585,8 +647,8 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
585
647
|
|
|
586
648
|
<div middle>
|
|
587
649
|
${this.renderBar()}
|
|
588
|
-
${this.
|
|
589
|
-
${this.
|
|
650
|
+
${this.proposalSession ? this.renderDecision() : ''}
|
|
651
|
+
${this.proposalSession
|
|
590
652
|
? html `
|
|
591
653
|
<div lanes>
|
|
592
654
|
<div lane>
|
|
@@ -601,10 +663,11 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
601
663
|
<div lane candidate>
|
|
602
664
|
<h4>
|
|
603
665
|
${i18next.t('figure.label.proposed')}
|
|
604
|
-
${this.
|
|
666
|
+
${this.proposalSession.metrics
|
|
605
667
|
? html `<small>
|
|
606
|
-
${this.
|
|
607
|
-
${i18next.t('figure.text.n-triangles', { count: this.
|
|
668
|
+
${this.proposalSession.metrics.grade} ·
|
|
669
|
+
${i18next.t('figure.text.n-triangles', { count: this.proposalSession.metrics.triangles })}
|
|
670
|
+
${this.proposalSession.metrics.quality ? html ` · ${this.proposalSession.metrics.quality.summary}` : ''}
|
|
608
671
|
</small>`
|
|
609
672
|
: ''}
|
|
610
673
|
</h4>
|
|
@@ -668,7 +731,7 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
668
731
|
</button>
|
|
669
732
|
`;
|
|
670
733
|
return html `<div modes>
|
|
671
|
-
${this.renderCollapse('left')}${this.
|
|
734
|
+
${this.renderCollapse('left')}${this.proposalSession ? '' : html `${one('edit')}${one('preview')}`}
|
|
672
735
|
<div spacer></div>
|
|
673
736
|
${this.renderCollapse('right')}
|
|
674
737
|
</div>`;
|
|
@@ -699,16 +762,17 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
699
762
|
rememberPanes(this.collapsed);
|
|
700
763
|
}
|
|
701
764
|
/**
|
|
702
|
-
* AI 후보를
|
|
765
|
+
* AI 후보를 실제 배치 크기에서도 본다.
|
|
703
766
|
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
767
|
+
* 편집 캔버스 한 장으로는 `repeat`·`fixed`·`anchor`가 맞는지 알 수 없다. 후보는 아직
|
|
768
|
+
* 정본이 아니므로 저장하지 않고, `figure-preview`가 같은 FigureSource를 기준 크기와
|
|
769
|
+
* 일곱 가지 확장 축에서 실제 FigureInstance로 세운다. 따라서 저작자는 "좋아 보이는
|
|
770
|
+
* 한 장"이 아니라 배치될 때도 붙어 있는지를 보고 수락한다.
|
|
706
771
|
*/
|
|
707
772
|
renderProposed() {
|
|
708
|
-
if (!this.
|
|
773
|
+
if (!this.proposalSession)
|
|
709
774
|
return '';
|
|
710
|
-
|
|
711
|
-
return html `<figure-canvas .board=${board} .parts=${parts} .viewOnly=${true}></figure-canvas>`;
|
|
775
|
+
return html `<figure-preview .source=${this.proposalSession.source}></figure-preview>`;
|
|
712
776
|
}
|
|
713
777
|
/**
|
|
714
778
|
* 무엇을 받을지 고른다.
|
|
@@ -725,21 +789,34 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
725
789
|
* 안 들고, 우리가 생각 못 한 조합을 막아 주기 때문이다.
|
|
726
790
|
*/
|
|
727
791
|
renderDecision() {
|
|
728
|
-
if (!this.
|
|
792
|
+
if (!this.proposalSession)
|
|
729
793
|
return '';
|
|
730
|
-
const changes = proposals.diffProposal(this.folded, this.
|
|
731
|
-
const picked = proposals.applyProposal(this.folded, this.
|
|
794
|
+
const changes = proposals.diffProposal(this.folded, this.proposalSession.source);
|
|
795
|
+
const picked = proposals.applyProposal(this.folded, this.proposalSession.source, this.proposalSession.picked);
|
|
732
796
|
const { errors, violations } = validate(picked);
|
|
733
797
|
return html `
|
|
734
798
|
<div decide>
|
|
735
|
-
<span>${i18next.t('figure.text.assistant-proposed-a-figure')}</span>
|
|
799
|
+
<span>${this.proposalSession.title || i18next.t('figure.text.assistant-proposed-a-figure')}</span>
|
|
736
800
|
<div spacer></div>
|
|
737
801
|
<button drop @click=${() => this.discard()}>${i18next.t('figure.button.discard')}</button>
|
|
738
|
-
<button take ?disabled=${errors.length > 0 || this.picked.size === 0} @click=${() => this.take()}>
|
|
739
|
-
${i18next.t('figure.button.take-n-changes', { n: this.picked.size })}
|
|
802
|
+
<button take ?disabled=${errors.length > 0 || this.proposalSession.picked.size === 0} @click=${() => this.take()}>
|
|
803
|
+
${i18next.t('figure.button.take-n-changes', { n: this.proposalSession.picked.size })}
|
|
740
804
|
</button>
|
|
741
805
|
</div>
|
|
742
806
|
|
|
807
|
+
${this.renderQualityReview()}
|
|
808
|
+
<div proposal-note>
|
|
809
|
+
<label>
|
|
810
|
+
${i18next.t('figure.text.proposal-feedback-note')}
|
|
811
|
+
<input
|
|
812
|
+
maxlength="500"
|
|
813
|
+
.value=${this.proposalSession.note}
|
|
814
|
+
placeholder=${i18next.t('figure.text.proposal-feedback-placeholder')}
|
|
815
|
+
@input=${(event) => (this.proposalSession = { ...this.proposalSession, note: event.target.value })}
|
|
816
|
+
/>
|
|
817
|
+
</label>
|
|
818
|
+
</div>
|
|
819
|
+
|
|
743
820
|
${changes.length > 0
|
|
744
821
|
? html `
|
|
745
822
|
<div changes>
|
|
@@ -754,6 +831,24 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
754
831
|
: ''}
|
|
755
832
|
`;
|
|
756
833
|
}
|
|
834
|
+
/** 후보 품질 판단의 근거를 수락 전에 전부 보여 준다. */
|
|
835
|
+
renderQualityReview() {
|
|
836
|
+
const quality = this.proposalSession?.metrics?.quality;
|
|
837
|
+
if (!quality)
|
|
838
|
+
return '';
|
|
839
|
+
return html `
|
|
840
|
+
<div quality-review ?review=${quality.status === 'review'}>
|
|
841
|
+
<strong>${i18next.t('figure.label.candidate-review')}</strong>
|
|
842
|
+
<span>${quality.summary}</span>
|
|
843
|
+
<span> · top-view ${Math.round(quality.visualReadability * 100)}% · ${quality.visualRegions} regions</span>
|
|
844
|
+
${quality.findings.length > 0
|
|
845
|
+
? html `<ul>
|
|
846
|
+
${quality.findings.map(finding => html `<li><code>${finding.dimension}</code> — ${finding.message}</li>`)}
|
|
847
|
+
</ul>`
|
|
848
|
+
: ''}
|
|
849
|
+
</div>
|
|
850
|
+
`;
|
|
851
|
+
}
|
|
757
852
|
/** 변경 하나. 무엇을 하겠다는 것인지 부호로 먼저 말한다. */
|
|
758
853
|
renderChange(change) {
|
|
759
854
|
const icon = {
|
|
@@ -765,7 +860,7 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
765
860
|
anchor: 'my_location',
|
|
766
861
|
styleKit: 'palette'
|
|
767
862
|
}[change.kind];
|
|
768
|
-
const on = this.picked.has(change.key);
|
|
863
|
+
const on = this.proposalSession?.picked.has(change.key);
|
|
769
864
|
return html `
|
|
770
865
|
<button
|
|
771
866
|
change
|
|
@@ -779,12 +874,8 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
779
874
|
`;
|
|
780
875
|
}
|
|
781
876
|
toggle(key) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
next.delete(key);
|
|
785
|
-
else
|
|
786
|
-
next.add(key);
|
|
787
|
-
this.picked = next;
|
|
877
|
+
if (this.proposalSession)
|
|
878
|
+
this.proposalSession = proposalSessions.toggleProposalChange(this.proposalSession, key);
|
|
788
879
|
}
|
|
789
880
|
/**
|
|
790
881
|
* 후보가 왔다.
|
|
@@ -793,15 +884,29 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
793
884
|
* 무엇이 자기 것이고 무엇이 기계 것인지 모르게 된다.
|
|
794
885
|
*/
|
|
795
886
|
receive(proposal) {
|
|
796
|
-
this.
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
887
|
+
this.proposalSession = proposalSessions.openAiProposal(this.folded, proposal);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* 발행 게이트의 구조화된 수정안 하나를 현재 작업 사본에만 겹쳐 본다. 서버가 준 값은
|
|
891
|
+
* `part.anchor.axis = to`라는 데이터이지 사람이 읽을 문장을 다시 해석한 결과가 아니다.
|
|
892
|
+
* 원본 `board`/`parts`는 여기서 전혀 바꾸지 않고, 아래의 `take()`만 바꾼 사본을 적용한다.
|
|
893
|
+
*/
|
|
894
|
+
previewSizingFix(fix) {
|
|
895
|
+
if (!this.folded)
|
|
896
|
+
return;
|
|
897
|
+
this.proposalSession = proposalSessions.openSizingFix(this.folded, fix);
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* 원클릭 적용은 서버가 모든 크기 변경 case를 재검사해 안전하다고 표시한 한-앵커 후보에만 연다.
|
|
901
|
+
* 그래도 저장은 저작자의 별도 행위다. 잘못 눌러도 DB 정본이나 발행판은 바뀌지 않는다.
|
|
902
|
+
*/
|
|
903
|
+
applySizingFix(fix) {
|
|
904
|
+
if (!fix.safe || !this.folded)
|
|
905
|
+
return;
|
|
906
|
+
this.previewSizingFix(fix);
|
|
907
|
+
if (!this.proposalSession)
|
|
908
|
+
return;
|
|
909
|
+
this.take();
|
|
805
910
|
}
|
|
806
911
|
/**
|
|
807
912
|
* 고른 변경을 받아들인다.
|
|
@@ -810,20 +915,42 @@ let FigureModellerPage = class FigureModellerPage extends FigureModellerPageBase
|
|
|
810
915
|
* 하므로 그쪽이 자연스럽다. 받아들인 결과는 다시 씬 모델로 되돌려 편집을 이어간다.
|
|
811
916
|
*/
|
|
812
917
|
take() {
|
|
813
|
-
if (!this.
|
|
918
|
+
if (!this.proposalSession)
|
|
814
919
|
return;
|
|
815
|
-
const taken = proposals.applyProposal(this.folded, this.
|
|
920
|
+
const taken = proposals.applyProposal(this.folded, this.proposalSession.source, this.proposalSession.picked);
|
|
816
921
|
const { board, parts } = fromFigureSource(taken);
|
|
817
922
|
this.board = board;
|
|
818
923
|
this.parts = parts;
|
|
819
924
|
this.selected = parts.length ? 0 : -1;
|
|
820
925
|
this.dirty = true;
|
|
821
|
-
this.
|
|
926
|
+
this.reportProposalFeedback('accepted');
|
|
927
|
+
this.discard(false);
|
|
822
928
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
929
|
+
/**
|
|
930
|
+
* 후보에 대한 사람의 결정을 도크에만 알린다.
|
|
931
|
+
*
|
|
932
|
+
* 이 기록은 분석 이벤트나 Figure 속성이 아니다. 같은 모델러 세션에서 다음 요청을 더 잘
|
|
933
|
+
* 이해시키는 짧은 신호일 뿐이다. 원본 source나 사람의 작업 내용을 다시 보내지 않고, 사람이
|
|
934
|
+
* 후보 변경 중 몇 개를 받아들였는지와 이미 화면에 보였던 품질 판정만 보낸다.
|
|
935
|
+
*/
|
|
936
|
+
reportProposalFeedback(outcome) {
|
|
937
|
+
if (!this.proposalSession)
|
|
938
|
+
return;
|
|
939
|
+
const feedback = proposalSessions.feedbackFromSession(this.folded, this.proposalSession, outcome);
|
|
940
|
+
if (!feedback)
|
|
941
|
+
return;
|
|
942
|
+
this.proposalFeedback = [...this.proposalFeedback, feedback].slice(-5);
|
|
943
|
+
window.dispatchEvent(new CustomEvent('figure-ai-feedback', {
|
|
944
|
+
detail: {
|
|
945
|
+
figureId: this.figure?.id,
|
|
946
|
+
...feedback
|
|
947
|
+
}
|
|
948
|
+
}));
|
|
949
|
+
}
|
|
950
|
+
discard(reportFeedback = true) {
|
|
951
|
+
if (reportFeedback)
|
|
952
|
+
this.reportProposalFeedback('discarded');
|
|
953
|
+
this.proposalSession = undefined;
|
|
827
954
|
}
|
|
828
955
|
/**
|
|
829
956
|
* 저장할 수 있나.
|
|
@@ -1233,15 +1360,11 @@ __decorate([
|
|
|
1233
1360
|
__decorate([
|
|
1234
1361
|
state(),
|
|
1235
1362
|
__metadata("design:type", Object)
|
|
1236
|
-
], FigureModellerPage.prototype, "
|
|
1237
|
-
__decorate([
|
|
1238
|
-
state(),
|
|
1239
|
-
__metadata("design:type", Object)
|
|
1240
|
-
], FigureModellerPage.prototype, "proposalOf", void 0);
|
|
1363
|
+
], FigureModellerPage.prototype, "proposalSession", void 0);
|
|
1241
1364
|
__decorate([
|
|
1242
1365
|
state(),
|
|
1243
|
-
__metadata("design:type",
|
|
1244
|
-
], FigureModellerPage.prototype, "
|
|
1366
|
+
__metadata("design:type", Array)
|
|
1367
|
+
], FigureModellerPage.prototype, "proposalFeedback", void 0);
|
|
1245
1368
|
FigureModellerPage = __decorate([
|
|
1246
1369
|
customElement('figure-modeller-page')
|
|
1247
1370
|
], FigureModellerPage);
|