@things-factory/figure-ui 10.1.9 → 10.1.13
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 +4 -1
- package/client/modeller/figure-ai-target.ts +11 -0
- package/client/modeller/figure-ask.ts +8 -5
- package/client/modeller/figure-canvas.ts +8 -0
- package/client/modeller/new-figure-entry.ts +4 -0
- package/client/pages/figure-modeller-page.ts +62 -24
- package/dist-client/graphql/index.js +4 -1
- package/dist-client/graphql/index.js.map +1 -1
- 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.js +7 -0
- 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 +5 -0
- package/dist-client/pages/figure-modeller-page.js +62 -22
- 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 +39 -3
- package/test/ai-target.test.ts +25 -0
- package/test/new-figure-entry.test.ts +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/figure-ui",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.13",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"@operato/shell": "^10.0.0",
|
|
38
38
|
"@operato/styles": "^10.0.0",
|
|
39
39
|
"@operato/utils": "^10.0.0",
|
|
40
|
-
"@things-factory/figure-service": "^10.1.
|
|
40
|
+
"@things-factory/figure-service": "^10.1.13",
|
|
41
41
|
"three": "^0.185.1"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "f2cd3d27e32e71cca1d3af5499ef54249b1cc111"
|
|
44
44
|
}
|
|
@@ -131,18 +131,28 @@ test('모델러 AI 입구는 후보만 알리고, 팔레트·그림·현재 정
|
|
|
131
131
|
const path = fileURLToPath(new URL('../client/modeller/figure-ask.ts', import.meta.url))
|
|
132
132
|
const source = readFileSync(path, 'utf8')
|
|
133
133
|
|
|
134
|
-
assert.match(source, /
|
|
134
|
+
assert.match(source, /const baseSource = this\.source \? JSON\.stringify\(this\.source\) : undefined/)
|
|
135
|
+
assert.match(source, /proposeFigure\(\{[\s\S]*prompt,[\s\S]*base: baseSource,[\s\S]*type: this\.source \? undefined : this\.type,[\s\S]*palette: Object\.keys\(activePalette\(\)/)
|
|
135
136
|
assert.match(source, /image: this\.picture/)
|
|
136
137
|
assert.match(source, /feedback: this\.feedback/)
|
|
137
|
-
assert.match(source, /new CustomEvent\('proposed', \{ detail: \{ proposal \}/)
|
|
138
|
+
assert.match(source, /new CustomEvent\('proposed', \{ detail: \{ proposal, baseSource \}/)
|
|
138
139
|
assert.match(source, /this\.failure = \(err as Error\)\.message/)
|
|
139
140
|
})
|
|
140
141
|
|
|
142
|
+
test('그림이 붙은 AI 요청은 JSON이 아니라 GraphQL multipart Upload 링크를 탄다', () => {
|
|
143
|
+
const path = fileURLToPath(new URL('../client/graphql/index.ts', import.meta.url))
|
|
144
|
+
const source = readFileSync(path, 'utf8')
|
|
145
|
+
const propose = source.slice(source.indexOf('export async function proposeFigure'), source.indexOf('/**\n * 발행한다'))
|
|
146
|
+
|
|
147
|
+
assert.match(propose, /context: \{ hasUpload: !!request\.image \}/)
|
|
148
|
+
})
|
|
149
|
+
|
|
141
150
|
test('모델러의 수락·거절 피드백은 다음 인라인 후보 요청에도 같은 Figure 세션에서만 전달한다', () => {
|
|
142
151
|
const path = fileURLToPath(new URL('../client/pages/figure-modeller-page.ts', import.meta.url))
|
|
143
152
|
const source = readFileSync(path, 'utf8')
|
|
144
153
|
|
|
145
|
-
assert.match(source,
|
|
154
|
+
assert.match(source, /new CustomEvent\('figure-ai-feedback'/)
|
|
155
|
+
assert.doesNotMatch(source, /<figure-ask/)
|
|
146
156
|
assert.match(source, /this\.proposalFeedback = \[\.\.\.this\.proposalFeedback, feedback\]\.slice\(-5\)/)
|
|
147
157
|
assert.match(source, /changed\.has\('figure'\).*this\.proposalFeedback = \[\]/s)
|
|
148
158
|
})
|
|
@@ -167,3 +177,29 @@ test('발행 게이트 수정안은 원본을 바꾸지 않고 후보로만 연
|
|
|
167
177
|
assert.match(modeller, /private take\(\)[\s\S]*applyProposal\(this\.folded, this\.proposalSession\.source, this\.proposalSession\.picked\)[\s\S]*this\.dirty = true/)
|
|
168
178
|
assert.match(session, /export function openSizingFix[\s\S]*anchor: \{ \.\.\.\(part\.anchor \?\? \{\}\), \[fix\.axis\]: fix\.to \}/)
|
|
169
179
|
})
|
|
180
|
+
|
|
181
|
+
test('새 Figure 경로는 MDI에 남아 있는 직전 Figure를 재사용하지 않고 빈 초안을 만든다', () => {
|
|
182
|
+
const path = fileURLToPath(new URL('../client/pages/figure-modeller-page.ts', import.meta.url))
|
|
183
|
+
const source = readFileSync(path, 'utf8')
|
|
184
|
+
const pageUpdated = source.slice(source.indexOf('async pageUpdated'), source.indexOf(' private async load'))
|
|
185
|
+
const startNew = source.slice(source.indexOf(' private startNew()'), source.indexOf(' private rename'))
|
|
186
|
+
|
|
187
|
+
// resourceId가 없다는 것은 명시적으로 「새 Figure」를 뜻한다.
|
|
188
|
+
assert.match(pageUpdated, /if \(!this\.figure\)[\s\S]*this\.startNew\(\)/)
|
|
189
|
+
assert.match(pageUpdated, /if \(this\.figure\.id\)[\s\S]*this\.startNew\(\)/)
|
|
190
|
+
// 새 초안은 저장 식별자·선택·기존 부품·진단을 모두 초기화한다.
|
|
191
|
+
assert.match(startNew, /this\.figure = \{ id: '', type: draftType, name: '' \}/)
|
|
192
|
+
assert.match(startNew, /const \{ board, parts \} = startingModel\(draftType\)/)
|
|
193
|
+
assert.match(startNew, /this\.selected = -1/)
|
|
194
|
+
assert.match(startNew, /this\.dirty = true/)
|
|
195
|
+
assert.match(startNew, /this\.violations = \[\]/)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('빈 새 Figure는 캔버스의 직전 씬을 폐기하고 안내 상태로 전환한다', () => {
|
|
199
|
+
const path = fileURLToPath(new URL('../client/modeller/figure-canvas.ts', import.meta.url))
|
|
200
|
+
const source = readFileSync(path, 'utf8')
|
|
201
|
+
const standUp = source.slice(source.indexOf(' private standUp()'), source.indexOf(' /**\n * 지금 판 위에'))
|
|
202
|
+
|
|
203
|
+
assert.match(standUp, /if \(!board\) \{[\s\S]*this\.teardown\(\)/)
|
|
204
|
+
assert.match(standUp, /if \(this\.parts\.length === 0\) \{[\s\S]*this\.teardown\(\)[\s\S]*canvas-appears-once-a-part-exists/)
|
|
205
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { requestFigureAI, takeFigureAITarget } from '../client/modeller/figure-ai-target.ts'
|
|
4
|
+
|
|
5
|
+
test('AI 진입은 늦게 생성되는 Dock에도 복제된 원본을 한 번 전달한다', () => {
|
|
6
|
+
const oldWindow = globalThis.window
|
|
7
|
+
globalThis.window = new EventTarget() as any
|
|
8
|
+
try {
|
|
9
|
+
const source = { type: 'TEST', parts: [{ name: 'cube', size: { y: 100 } }] }
|
|
10
|
+
requestFigureAI({ figureId: 'a', source })
|
|
11
|
+
source.parts[0].size.y = 200
|
|
12
|
+
assert.equal((takeFigureAITarget()?.source as any).parts[0].size.y, 100)
|
|
13
|
+
assert.equal(takeFigureAITarget(), undefined)
|
|
14
|
+
} finally { globalThis.window = oldWindow }
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('연속된 명시적 진입에서는 최신 대상만 지연 Dock에 전달한다', () => {
|
|
18
|
+
const oldWindow = globalThis.window
|
|
19
|
+
globalThis.window = new EventTarget() as any
|
|
20
|
+
try {
|
|
21
|
+
requestFigureAI({ figureId: 'a' })
|
|
22
|
+
requestFigureAI({ figureId: 'b' })
|
|
23
|
+
assert.equal(takeFigureAITarget()?.figureId, 'b')
|
|
24
|
+
} finally { globalThis.window = oldWindow }
|
|
25
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { shouldStartNewFigure } from '../client/modeller/new-figure-entry.ts'
|
|
4
|
+
|
|
5
|
+
test('entering new Figure replaces a previous unsaved draft', () => {
|
|
6
|
+
assert.equal(shouldStartNewFigure({ active: true }, true, ''), true)
|
|
7
|
+
})
|
|
8
|
+
test('removing resource ID replaces a saved model', () => {
|
|
9
|
+
assert.equal(shouldStartNewFigure({ resourceId: undefined }, true, 'saved'), true)
|
|
10
|
+
})
|
|
11
|
+
test('first entry starts empty but ordinary updates retain current work', () => {
|
|
12
|
+
assert.equal(shouldStartNewFigure({}, false), true)
|
|
13
|
+
assert.equal(shouldStartNewFigure({}, true, ''), false)
|
|
14
|
+
assert.equal(shouldStartNewFigure({ query: {} }, true, ''), false)
|
|
15
|
+
})
|