@things-factory/figure-ui 10.1.10 → 10.1.14
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/figure-ai-target.ts +11 -0
- package/client/modeller/figure-ask.ts +8 -5
- package/client/modeller/figure-canvas.ts +10 -4
- package/client/modeller/new-figure-entry.ts +4 -0
- package/client/pages/figure-modeller-page.ts +241 -120
- package/dist-client/modeller/figure-ai-target.d.ts +8 -0
- package/dist-client/modeller/figure-ai-target.js +12 -0
- package/dist-client/modeller/figure-ai-target.js.map +1 -0
- package/dist-client/modeller/figure-ask.d.ts +1 -0
- package/dist-client/modeller/figure-ask.js +12 -5
- package/dist-client/modeller/figure-ask.js.map +1 -1
- package/dist-client/modeller/figure-canvas.d.ts +1 -0
- package/dist-client/modeller/figure-canvas.js +10 -4
- package/dist-client/modeller/figure-canvas.js.map +1 -1
- package/dist-client/modeller/new-figure-entry.d.ts +2 -0
- package/dist-client/modeller/new-figure-entry.js +5 -0
- package/dist-client/modeller/new-figure-entry.js.map +1 -0
- package/dist-client/pages/figure-modeller-page.d.ts +14 -0
- package/dist-client/pages/figure-modeller-page.js +240 -120
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/test/ai-proposal-contract.test.ts +24 -3
- package/test/ai-target.test.ts +25 -0
- package/test/modeller-undo-redo.test.ts +33 -0
- package/test/new-figure-entry.test.ts +15 -0
- package/translations/en.json +2 -0
- package/translations/ja.json +2 -0
- package/translations/ko.json +2 -0
- package/translations/ms.json +2 -0
- package/translations/zh.json +2 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Explicit AI entry survives lazy creation of the app-level dock. */
|
|
2
|
+
let pending: { figureId?: string; source?: unknown } | undefined
|
|
3
|
+
export function requestFigureAI(target: { figureId?: string; source?: unknown }) {
|
|
4
|
+
pending = structuredClone(target)
|
|
5
|
+
window.dispatchEvent(new CustomEvent('figure-ai-target'))
|
|
6
|
+
}
|
|
7
|
+
export function takeFigureAITarget() {
|
|
8
|
+
const target = pending
|
|
9
|
+
pending = undefined
|
|
10
|
+
return target
|
|
11
|
+
}
|
|
@@ -58,6 +58,7 @@ export class FigureAsk extends localize(i18next)(LitElement) {
|
|
|
58
58
|
|
|
59
59
|
form {
|
|
60
60
|
display: flex;
|
|
61
|
+
flex-wrap: wrap;
|
|
61
62
|
flex: 1;
|
|
62
63
|
min-width: 0;
|
|
63
64
|
align-items: center;
|
|
@@ -177,6 +178,7 @@ export class FigureAsk extends localize(i18next)(LitElement) {
|
|
|
177
178
|
@property({ type: String }) type = ''
|
|
178
179
|
/** 이 모델러 세션에서 사람이 명시적으로 남긴 이전 후보 반응. */
|
|
179
180
|
@property({ type: Array }) feedback: ProposalFeedback[] = []
|
|
181
|
+
@property({ type: Boolean }) imageOnly = false
|
|
180
182
|
|
|
181
183
|
@state() private asking = false
|
|
182
184
|
@state() private failure = ''
|
|
@@ -186,7 +188,7 @@ export class FigureAsk extends localize(i18next)(LitElement) {
|
|
|
186
188
|
const revising = !!this.source
|
|
187
189
|
|
|
188
190
|
return html`
|
|
189
|
-
<form @submit=${(e: Event) => this.submit(e)}>
|
|
191
|
+
<form title="이미지의 형태와 비율을 참고한 후보를 만듭니다. 저장은 하지 않습니다." @submit=${(e: Event) => this.submit(e)}>
|
|
190
192
|
<md-icon lead>auto_awesome</md-icon>
|
|
191
193
|
<span mode>${i18next.t(revising ? 'figure.text.ask-to-revise' : 'figure.text.ask-to-create')}</span>
|
|
192
194
|
<input
|
|
@@ -201,7 +203,7 @@ export class FigureAsk extends localize(i18next)(LitElement) {
|
|
|
201
203
|
: i18next.t('figure.button.attach-a-picture')}
|
|
202
204
|
<input type="file" accept="image/*" @change=${(e: Event) => this.pick(e)} />
|
|
203
205
|
</label>
|
|
204
|
-
<button go ?disabled=${this.asking}>
|
|
206
|
+
<button go ?disabled=${this.asking || (this.imageOnly && !this.picture)}>
|
|
205
207
|
${this.asking ? i18next.t('figure.text.asking') : i18next.t('figure.button.ask')}
|
|
206
208
|
</button>
|
|
207
209
|
</form>
|
|
@@ -227,17 +229,18 @@ export class FigureAsk extends localize(i18next)(LitElement) {
|
|
|
227
229
|
|
|
228
230
|
const input = this.renderRoot.querySelector('input[ask]') as HTMLInputElement
|
|
229
231
|
const prompt = input?.value?.trim()
|
|
230
|
-
if (!prompt || this.asking) {
|
|
232
|
+
if (!prompt || this.asking || (this.imageOnly && !this.picture)) {
|
|
231
233
|
return
|
|
232
234
|
}
|
|
233
235
|
|
|
234
236
|
this.asking = true
|
|
235
237
|
this.failure = ''
|
|
238
|
+
const baseSource = this.source ? JSON.stringify(this.source) : undefined
|
|
236
239
|
|
|
237
240
|
try {
|
|
238
241
|
const proposal = await proposeFigure({
|
|
239
242
|
prompt,
|
|
240
|
-
base:
|
|
243
|
+
base: baseSource,
|
|
241
244
|
type: this.source ? undefined : this.type,
|
|
242
245
|
// 그리는 쪽이 풀 수 있는 토큰만 보낸다. 서버는 이 목록 밖의 색을 거절한다.
|
|
243
246
|
palette: Object.keys(activePalette() as Record<string, string>),
|
|
@@ -248,7 +251,7 @@ export class FigureAsk extends localize(i18next)(LitElement) {
|
|
|
248
251
|
})
|
|
249
252
|
|
|
250
253
|
this.dispatchEvent(
|
|
251
|
-
new CustomEvent('proposed', { detail: { proposal }, bubbles: true, composed: true })
|
|
254
|
+
new CustomEvent('proposed', { detail: { proposal, baseSource }, bubbles: true, composed: true })
|
|
252
255
|
)
|
|
253
256
|
input.value = ''
|
|
254
257
|
this.picture = undefined
|
|
@@ -70,6 +70,7 @@ interface SceneBoard {
|
|
|
70
70
|
selected?: SceneComponent[]
|
|
71
71
|
app?: unknown
|
|
72
72
|
addComponent?: (component: unknown) => void
|
|
73
|
+
removeComponent?: (component: unknown) => void
|
|
73
74
|
on?: (event: string, handler: (...args: unknown[]) => void) => void
|
|
74
75
|
/** 판 위의 루트. 선택 알림이 여기서 난다. */
|
|
75
76
|
root?: {
|
|
@@ -82,7 +83,8 @@ interface SceneComponent {
|
|
|
82
83
|
state: Record<string, unknown>
|
|
83
84
|
set: (props: Record<string, unknown> | string, value?: unknown) => void
|
|
84
85
|
on?: (event: string, handler: (...args: unknown[]) => void) => void
|
|
85
|
-
|
|
86
|
+
removeSelf?: (completely?: boolean) => void
|
|
87
|
+
dispose?: () => void
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
/**
|
|
@@ -181,6 +183,7 @@ export class FigureCanvas extends localize(i18next)(LitElement) {
|
|
|
181
183
|
* 이미 씬 모델이므로, 그러면 값을 하나 고칠 때마다 왕복 변환이 한 번씩 일어난다.
|
|
182
184
|
* 무손실이긴 하지만 하는 일이 없다 — 보드가 곧 보드이고 부품이 곧 컴포넌트다.
|
|
183
185
|
*/
|
|
186
|
+
@property({ type: String }) figureId?: string
|
|
184
187
|
@property({ type: Object }) board?: BoardModel
|
|
185
188
|
/** 보기 설정. 자산이 아니라 작업대다 — 저장되지 않는다(`figure-view.ts`). */
|
|
186
189
|
@property({ type: Object }) view: ViewSettings = DEFAULT_VIEW
|
|
@@ -272,7 +275,7 @@ export class FigureCanvas extends localize(i18next)(LitElement) {
|
|
|
272
275
|
willUpdate(changed: Map<string, unknown>) {
|
|
273
276
|
if (!this.hasUpdated) return
|
|
274
277
|
|
|
275
|
-
if (changed.has('board') || changed.has('parts')) this.standUp()
|
|
278
|
+
if (changed.has('board') || changed.has('parts') || changed.has('figureId')) this.standUp()
|
|
276
279
|
if (changed.has('view')) this.applyView()
|
|
277
280
|
if (changed.has('picked')) this.showPicked()
|
|
278
281
|
}
|
|
@@ -314,7 +317,7 @@ export class FigureCanvas extends localize(i18next)(LitElement) {
|
|
|
314
317
|
return
|
|
315
318
|
}
|
|
316
319
|
|
|
317
|
-
const signature = `${board.width}x${board.height}x${board.depth}`
|
|
320
|
+
const signature = `${this.figureId ?? ''}:${board.width}x${board.height}x${board.depth}`
|
|
318
321
|
if (this.scene && this.standing === signature) {
|
|
319
322
|
this.syncParts()
|
|
320
323
|
return
|
|
@@ -455,7 +458,10 @@ export class FigureCanvas extends localize(i18next)(LitElement) {
|
|
|
455
458
|
|
|
456
459
|
// 없어진 것을 제거한다
|
|
457
460
|
for (const [name, component] of standing) {
|
|
458
|
-
if (!wanted.has(name))
|
|
461
|
+
if (!wanted.has(name)) {
|
|
462
|
+
root.removeComponent?.(component)
|
|
463
|
+
component.dispose?.()
|
|
464
|
+
}
|
|
459
465
|
}
|
|
460
466
|
|
|
461
467
|
for (const part of this.parts) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** A route entry starts a new document; ordinary updates must preserve edits. */
|
|
2
|
+
export function shouldStartNewFigure(changes: Record<string, unknown>, hasFigure: boolean, savedId?: string): boolean {
|
|
3
|
+
return !hasFigure || !!savedId || changes.active === true || Object.prototype.hasOwnProperty.call(changes, 'resourceId')
|
|
4
|
+
}
|