@things-factory/figure-ui 10.1.4 → 10.1.6
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 +122 -15
- package/client/modeller/figure-ask.ts +5 -5
- package/client/modeller/figure-canvas.ts +1 -1
- package/client/modeller/figure-capabilities.ts +372 -0
- package/client/modeller/figure-history-panel.ts +477 -0
- package/client/modeller/figure-inspector.ts +392 -76
- package/client/modeller/figure-parts.ts +15 -15
- package/client/modeller/figure-preview.ts +2 -2
- package/client/modeller/figure-report.ts +102 -37
- package/client/modeller/figure-settings.ts +20 -20
- package/client/modeller/figure-side.ts +54 -8
- package/client/modeller/figure-source.test.ts +15 -1
- package/client/modeller/figure-source.ts +31 -15
- package/client/modeller/figure-thumbnail.ts +49 -4
- package/client/modeller/maturity.ts +10 -10
- package/client/pages/figure-list-page.ts +450 -227
- package/client/pages/figure-modeller-page.ts +282 -43
- package/client/types.ts +42 -0
- package/client/viewparts/figure-thumb.ts +172 -0
- package/dist-client/graphql/index.d.ts +31 -4
- package/dist-client/graphql/index.js +102 -12
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-ask.js +5 -5
- package/dist-client/modeller/figure-ask.js.map +1 -1
- package/dist-client/modeller/figure-canvas.js +1 -1
- package/dist-client/modeller/figure-canvas.js.map +1 -1
- package/dist-client/modeller/figure-capabilities.d.ts +42 -0
- package/dist-client/modeller/figure-capabilities.js +344 -0
- package/dist-client/modeller/figure-capabilities.js.map +1 -0
- package/dist-client/modeller/figure-history-panel.d.ts +56 -0
- package/dist-client/modeller/figure-history-panel.js +490 -0
- package/dist-client/modeller/figure-history-panel.js.map +1 -0
- package/dist-client/modeller/figure-inspector.d.ts +84 -0
- package/dist-client/modeller/figure-inspector.js +379 -75
- package/dist-client/modeller/figure-inspector.js.map +1 -1
- package/dist-client/modeller/figure-parts.js +15 -15
- package/dist-client/modeller/figure-parts.js.map +1 -1
- package/dist-client/modeller/figure-preview.js +2 -2
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-report.d.ts +18 -1
- package/dist-client/modeller/figure-report.js +105 -37
- package/dist-client/modeller/figure-report.js.map +1 -1
- package/dist-client/modeller/figure-settings.js +19 -19
- package/dist-client/modeller/figure-settings.js.map +1 -1
- package/dist-client/modeller/figure-side.d.ts +7 -0
- package/dist-client/modeller/figure-side.js +66 -7
- package/dist-client/modeller/figure-side.js.map +1 -1
- package/dist-client/modeller/figure-source.d.ts +15 -8
- package/dist-client/modeller/figure-source.js +16 -7
- package/dist-client/modeller/figure-source.js.map +1 -1
- package/dist-client/modeller/figure-thumbnail.js +40 -4
- package/dist-client/modeller/figure-thumbnail.js.map +1 -1
- package/dist-client/modeller/maturity.js +10 -10
- package/dist-client/modeller/maturity.js.map +1 -1
- package/dist-client/pages/figure-list-page.d.ts +45 -25
- package/dist-client/pages/figure-list-page.js +439 -247
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +54 -0
- package/dist-client/pages/figure-modeller-page.js +279 -43
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +42 -0
- package/dist-client/types.js.map +1 -1
- package/dist-client/viewparts/figure-thumb.d.ts +29 -0
- package/dist-client/viewparts/figure-thumb.js +168 -0
- package/dist-client/viewparts/figure-thumb.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -5
- package/test/i18n-prefix-guard.test.ts +129 -0
- package/test/korean-register-guard.test.ts +83 -0
- package/translations/en.json +307 -249
- package/translations/ja.json +275 -249
- package/translations/ko.json +307 -249
- package/translations/ms.json +275 -249
- package/translations/zh.json +275 -249
- package/client/viewparts/figure-card.ts +0 -226
- package/dist-client/viewparts/figure-card.d.ts +0 -24
- package/dist-client/viewparts/figure-card.js +0 -221
- package/dist-client/viewparts/figure-card.js.map +0 -1
|
@@ -149,7 +149,6 @@ const FULL: FigureSource = {
|
|
|
149
149
|
base: { x: 3000, y: 800, z: 2000 },
|
|
150
150
|
detailLevel: 'L',
|
|
151
151
|
styleKit: 'factory',
|
|
152
|
-
anchor: { x: 0, y: -400, z: 0 },
|
|
153
152
|
parts: [
|
|
154
153
|
{
|
|
155
154
|
name: 'deck',
|
|
@@ -200,8 +199,23 @@ const FULL: FigureSource = {
|
|
|
200
199
|
material: { token: 'palette.neutral' },
|
|
201
200
|
sizing: 'repeat',
|
|
202
201
|
repeat: { axis: 'x', pitch: 200 }
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
/*
|
|
205
|
+
물건을 놓는 자리이면서 드나드는 문이다 — AGV 의 상판이 그렇다. **그려지지 않으므로**
|
|
206
|
+
재질이 없다.
|
|
207
|
+
|
|
208
|
+
그리지 않는 것이라 화면으로는 왕복에서 누락됐는지 알 수 없다. 자리를 잃어버린 도형은
|
|
209
|
+
멀쩡해 보이면서 아무것도 받지 못한다.
|
|
210
|
+
*/
|
|
211
|
+
name: 'seat',
|
|
212
|
+
primitive: 'cube',
|
|
213
|
+
transform: { position: { x: 0, y: 420, z: 0 }, size: { x: 700, y: 500, z: 500 } },
|
|
214
|
+
capability: { roles: ['slot', 'port-in', 'port-out'], accepts: ['PALLET'], capacity: 2 }
|
|
203
215
|
}
|
|
204
216
|
],
|
|
217
|
+
/* 이 도형이 지는 능력. 부품이 아니라 도형 전체의 것이다. */
|
|
218
|
+
capabilities: ['FlowNode'],
|
|
205
219
|
/*
|
|
206
220
|
움직임. 편집면이 아직 안 만지는 자리이며, **바로 그래서 여기 있다.**
|
|
207
221
|
|
|
@@ -82,14 +82,6 @@ export interface BoardModel {
|
|
|
82
82
|
figureType?: string
|
|
83
83
|
detailLevel?: DetailLevel
|
|
84
84
|
styleKit?: string
|
|
85
|
-
/**
|
|
86
|
-
* 형상 전체의 기준점.
|
|
87
|
-
*
|
|
88
|
-
* **부품의 `anchor` 와 다른 것이다.** 이쪽은 점(`Vec3`)이고 저쪽은 축별로 어느 면을
|
|
89
|
-
* 붙잡느냐다. 한 형식 안에 같은 낱말이 둘인 것은 좋지 않으나, 이름을 바꾸면 저장된
|
|
90
|
-
* 원본이 따라와야 하므로 여기서 정하지 않는다.
|
|
91
|
-
*/
|
|
92
|
-
anchor?: Vec3
|
|
93
85
|
/**
|
|
94
86
|
* 움직임. **부품 바깥에 있다** — clip 하나가 부품 여럿을 함께 움직이기 때문이다.
|
|
95
87
|
*
|
|
@@ -97,6 +89,14 @@ export interface BoardModel {
|
|
|
97
89
|
* 저장하는 것만으로 **조용히 지워지기** 때문이다.
|
|
98
90
|
*/
|
|
99
91
|
animations?: FigureSource['animations']
|
|
92
|
+
/**
|
|
93
|
+
* 이 도형이 지는 능력. 씬의 capability mixin 이름이다.
|
|
94
|
+
*
|
|
95
|
+
* **부품이 아니라 도형 전체의 것이다** — 「이 컨베이어는 흐름의 마디다」는 부품 하나가
|
|
96
|
+
* 아니라 도형이 지는 성질이다. 부품 쪽 `capability` 는 그 능력을 **어느 부품이 받쳐
|
|
97
|
+
* 주나**를 말하는 다른 칸이다.
|
|
98
|
+
*/
|
|
99
|
+
capabilities?: FigureSource['capabilities']
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/** 부품 컴포넌트의 상태. 씬의 용어 그대로다. */
|
|
@@ -159,6 +159,13 @@ export interface PartModel {
|
|
|
159
159
|
anchor?: FigurePart['anchor']
|
|
160
160
|
repeat?: FigurePart['repeat']
|
|
161
161
|
label?: FigurePart['label']
|
|
162
|
+
/**
|
|
163
|
+
* 이 부품이 능력의 어느 자리인가 — 물건이 앉는 자리(slot)이거나 드나드는 문(port)이다.
|
|
164
|
+
*
|
|
165
|
+
* 이 칸이 있는 부품은 **그려지지 않는다.** 저작면에서는 어디인지 보여야 하니 그리지만,
|
|
166
|
+
* 청사진에서는 `groups` 가 아니라 `anchors` 로 간다.
|
|
167
|
+
*/
|
|
168
|
+
capability?: FigurePart['capability']
|
|
162
169
|
}
|
|
163
170
|
|
|
164
171
|
const RAD_TO_DEG = 180 / Math.PI
|
|
@@ -223,11 +230,11 @@ export function toFigureSource(board: BoardModel, parts: PartModel[]): FigureSou
|
|
|
223
230
|
return withoutEmpty({
|
|
224
231
|
type: board.figureType ?? '',
|
|
225
232
|
base: { x: board.width, y: board.depth, z: board.height },
|
|
226
|
-
anchor: board.anchor,
|
|
227
233
|
detailLevel: board.detailLevel,
|
|
228
234
|
styleKit: board.styleKit,
|
|
229
235
|
parts: parts.map(part => partTo(part, half)),
|
|
230
|
-
animations: board.animations
|
|
236
|
+
animations: board.animations,
|
|
237
|
+
capabilities: board.capabilities
|
|
231
238
|
}) as FigureSource
|
|
232
239
|
}
|
|
233
240
|
|
|
@@ -239,6 +246,13 @@ function partTo(part: PartModel, half: Vec3): FigurePart {
|
|
|
239
246
|
})
|
|
240
247
|
|
|
241
248
|
const shape = withoutEmpty({ round: part.round, path: part.path, hollow: part.hollow })
|
|
249
|
+
/*
|
|
250
|
+
재질이 하나도 없으면 **칸 자체를 안 적는다.**
|
|
251
|
+
|
|
252
|
+
빈 객체를 적으면 형식이 「재질은 있는데 무엇으로 칠할지 모른다」로 읽어
|
|
253
|
+
`material-unresolvable` 을 낸다. 그리지 않는 자리(`capability`)에는 재질이 없는 것이
|
|
254
|
+
정상이고, 그 부품이 조용히 오류를 달고 다니게 된다.
|
|
255
|
+
*/
|
|
242
256
|
const material = withoutEmpty({
|
|
243
257
|
token: part.token,
|
|
244
258
|
preset: part.preset,
|
|
@@ -262,13 +276,14 @@ function partTo(part: PartModel, half: Vec3): FigurePart {
|
|
|
262
276
|
}),
|
|
263
277
|
segments: part.segments,
|
|
264
278
|
shape: Object.keys(shape).length > 0 ? shape : undefined,
|
|
265
|
-
material,
|
|
279
|
+
material: Object.keys(material).length > 0 ? material : undefined,
|
|
266
280
|
materialSlot: part.materialSlot,
|
|
267
281
|
keepRound: part.keepRound,
|
|
268
282
|
sizing: part.sizing,
|
|
269
283
|
anchor: part.anchor,
|
|
270
284
|
repeat: part.repeat,
|
|
271
|
-
label: part.label
|
|
285
|
+
label: part.label,
|
|
286
|
+
capability: part.capability
|
|
272
287
|
}) as FigurePart
|
|
273
288
|
}
|
|
274
289
|
|
|
@@ -283,8 +298,8 @@ export function fromFigureSource(source: FigureSource): { board: BoardModel; par
|
|
|
283
298
|
figureType: source.type,
|
|
284
299
|
detailLevel: source.detailLevel,
|
|
285
300
|
styleKit: source.styleKit,
|
|
286
|
-
|
|
287
|
-
|
|
301
|
+
animations: source.animations,
|
|
302
|
+
capabilities: source.capabilities
|
|
288
303
|
}) as BoardModel
|
|
289
304
|
|
|
290
305
|
return { board, parts: source.parts.map(part => partFrom(part, half)) }
|
|
@@ -333,6 +348,7 @@ function partFrom(part: FigurePart, half: Vec3): PartModel {
|
|
|
333
348
|
sizing: part.sizing,
|
|
334
349
|
anchor: part.anchor,
|
|
335
350
|
repeat: part.repeat,
|
|
336
|
-
label: part.label
|
|
351
|
+
label: part.label,
|
|
352
|
+
capability: part.capability
|
|
337
353
|
}) as PartModel
|
|
338
354
|
}
|
|
@@ -34,11 +34,40 @@ interface Shooter extends HTMLElement {
|
|
|
34
34
|
snapshot?: (width?: number, height?: number) => Promise<string | undefined>
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* 씬이 서고 카메라가 자리를 잡을 틈 — 프레임 두 장 뒤에 한 번 더 쉰다. 씬에 그 신호가 없다.
|
|
39
|
+
*
|
|
40
|
+
* **rAF 만 기다리면 안 된다.** 탭이 뒤로 가거나 창이 가려지면 브라우저가 rAF 를 멈추고, 그러면
|
|
41
|
+
* 이 약속이 영원히 안 풀린다. 그 위에 저장이 얹혀 있어서 저장이 통째로 멈췄다 — 단추는 잠긴 채로,
|
|
42
|
+
* 아무 말도 없이. [확인함] 2026-09-10 포커스 없는 탭에서 `saving: true` 로 고정.
|
|
43
|
+
*
|
|
44
|
+
* 그래서 시계와 함께 경주시킨다. 늦게 깨어난 쪽은 버려지고, 화면이 안 그려지는 상황이면 아래
|
|
45
|
+
* `snapshot` 이 빈 것을 돌려주거나 시간이 다 된다 — 어느 쪽이든 **저장은 계속 간다.**
|
|
46
|
+
*/
|
|
38
47
|
function settle(): Promise<void> {
|
|
39
|
-
|
|
48
|
+
const frames = new Promise<void>(resolve =>
|
|
40
49
|
requestAnimationFrame(() => requestAnimationFrame(() => setTimeout(resolve, 150)))
|
|
41
50
|
)
|
|
51
|
+
|
|
52
|
+
return Promise.race([frames, delay(600)])
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function delay(ms: number): Promise<void> {
|
|
56
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 그림을 기다리는 시간의 한도.
|
|
61
|
+
*
|
|
62
|
+
* 3D 를 세워 한 장 찍는 데 걸리는 시간은 형상과 기계에 따라 다르지만, 이 한도의 목적은 빠르기가
|
|
63
|
+
* 아니라 **저장이 멈추지 않는 것**이다. 넘으면 그림 없이 저장한다 — 정본이 그림보다 중하고,
|
|
64
|
+
* 그림은 다음에 저작면을 열 때 다시 채운다(`figure-modeller-page` 의 fillThumbnail).
|
|
65
|
+
*/
|
|
66
|
+
const PATIENCE = 5000
|
|
67
|
+
|
|
68
|
+
/** 시간이 다 되면 없는 것으로 답한다. 던지지 않는다 — 그림이 없는 것은 실패가 아니다. */
|
|
69
|
+
function within<T>(work: Promise<T>, ms: number): Promise<T | undefined> {
|
|
70
|
+
return Promise.race([work, delay(ms).then(() => undefined)])
|
|
42
71
|
}
|
|
43
72
|
|
|
44
73
|
export async function captureThumbnail(
|
|
@@ -66,9 +95,25 @@ export async function captureThumbnail(
|
|
|
66
95
|
|
|
67
96
|
try {
|
|
68
97
|
host.appendChild(shooter)
|
|
69
|
-
await settle()
|
|
70
98
|
|
|
71
|
-
|
|
99
|
+
/*
|
|
100
|
+
**한도 안에 못 찍으면 그림 없이 간다.** 이 함수를 부르는 자리가 저장이고, 저장은 그림을
|
|
101
|
+
기다리다 멈춰서는 안 된다. 못 찍은 것은 조용히 넘기지 않고 콘솔에 남긴다 — 그림이 계속
|
|
102
|
+
안 생기면 그 줄이 단서가 된다.
|
|
103
|
+
*/
|
|
104
|
+
const shot = await within(
|
|
105
|
+
(async () => {
|
|
106
|
+
await settle()
|
|
107
|
+
return shooter.snapshot ? await shooter.snapshot(WIDTH, HEIGHT) : undefined
|
|
108
|
+
})(),
|
|
109
|
+
PATIENCE
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if (!shot) {
|
|
113
|
+
console.warn(`[thumbnail] ${PATIENCE}ms 안에 못 찍었습니다 — 그림 없이 저장합니다`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return shot
|
|
72
117
|
} catch (e) {
|
|
73
118
|
console.warn(`[thumbnail] 세우지 못했습니다 — ${(e as Error).message}`)
|
|
74
119
|
return undefined
|
|
@@ -70,7 +70,7 @@ export function maturityOf(source: FigureSource, cost: FigureCost, score: Figure
|
|
|
70
70
|
function scale(cost: FigureCost): MaturityAxis {
|
|
71
71
|
const capacity = capacityFrom(cost.groups, cost.triangles)
|
|
72
72
|
if (!capacity) {
|
|
73
|
-
return { key: 'scale', blocked: 'text.maturity-blocked-no-parts' }
|
|
73
|
+
return { key: 'scale', blocked: 'figure.text.maturity-blocked-no-parts' }
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
return {
|
|
@@ -79,7 +79,7 @@ function scale(cost: FigureCost): MaturityAxis {
|
|
|
79
79
|
// 목표를 넘긴 여유는 다른 축을 고치는 데 쓰는 편이 낫다.
|
|
80
80
|
ratio: Math.min(1, capacity.instances / BOARD_ASSETS),
|
|
81
81
|
reading: String(capacity.instances),
|
|
82
|
-
because: 'text.maturity-scale-because'
|
|
82
|
+
because: 'figure.text.maturity-scale-because'
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -102,8 +102,8 @@ function consistency(source: FigureSource, cost: FigureCost): MaturityAxis {
|
|
|
102
102
|
return {
|
|
103
103
|
key: 'consistency',
|
|
104
104
|
ratio: (3 - faults) / 3,
|
|
105
|
-
readingKey: faults === 0 ? 'text.maturity-consistency-clean' : 'text.maturity-consistency-faults',
|
|
106
|
-
because: 'text.maturity-consistency-because'
|
|
105
|
+
readingKey: faults === 0 ? 'figure.text.maturity-consistency-clean' : 'figure.text.maturity-consistency-faults',
|
|
106
|
+
because: 'figure.text.maturity-consistency-because'
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -117,7 +117,7 @@ function consistency(source: FigureSource, cost: FigureCost): MaturityAxis {
|
|
|
117
117
|
function reuse(score: FigureScore): MaturityAxis {
|
|
118
118
|
const { distinct, parts, saved } = score.reuse
|
|
119
119
|
if (parts === 0) {
|
|
120
|
-
return { key: 'reuse', blocked: 'text.maturity-blocked-no-parts' }
|
|
120
|
+
return { key: 'reuse', blocked: 'figure.text.maturity-blocked-no-parts' }
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
return {
|
|
@@ -125,7 +125,7 @@ function reuse(score: FigureScore): MaturityAxis {
|
|
|
125
125
|
// ratio 가 낮을수록 많이 되쓴다 — 뒤집어서 높은 쪽이 좋게 만든다
|
|
126
126
|
ratio: 1 - distinct / parts,
|
|
127
127
|
reading: String(saved),
|
|
128
|
-
because: 'text.maturity-reuse-because'
|
|
128
|
+
because: 'figure.text.maturity-reuse-because'
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -148,7 +148,7 @@ function detail(source: FigureSource, cost: FigureCost): MaturityAxis {
|
|
|
148
148
|
// 절반쯤 쓴 것을 제일 좋게 본다. 텅 비어도, 넘쳐도 등급과 안 맞는다.
|
|
149
149
|
ratio: used > 1 ? Math.max(0, 2 - used) : Math.min(1, used * 2),
|
|
150
150
|
reading: `${cost.parts} / ${limit}`,
|
|
151
|
-
because: 'text.maturity-detail-because'
|
|
151
|
+
because: 'figure.text.maturity-detail-because'
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
@@ -162,7 +162,7 @@ function detail(source: FigureSource, cost: FigureCost): MaturityAxis {
|
|
|
162
162
|
* **아무 형상도 보여 줄 수 없는 상태**다. 축 자체가 아직 없는 것이다.
|
|
163
163
|
*/
|
|
164
164
|
function reactive(source: FigureSource): MaturityAxis {
|
|
165
|
-
return { key: 'reactive', blocked: 'text.maturity-blocked-no-binding' }
|
|
165
|
+
return { key: 'reactive', blocked: 'figure.text.maturity-blocked-no-binding' }
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
@@ -205,7 +205,7 @@ function reactive(source: FigureSource): MaturityAxis {
|
|
|
205
205
|
function readable(source: FigureSource, cost: FigureCost): MaturityAxis {
|
|
206
206
|
const regions = topViewRegions(source)
|
|
207
207
|
if (regions.total === 0) {
|
|
208
|
-
return { key: 'readable', blocked: 'text.maturity-blocked-no-parts' }
|
|
208
|
+
return { key: 'readable', blocked: 'figure.text.maturity-blocked-no-parts' }
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
// 배경도 한 구역으로 센다 — 발자국을 얼마나 채우나가 식별성의 일부다.
|
|
@@ -221,7 +221,7 @@ function readable(source: FigureSource, cost: FigureCost): MaturityAxis {
|
|
|
221
221
|
key: 'readable',
|
|
222
222
|
ratio: Math.min(1, entropy / ceiling),
|
|
223
223
|
reading: `${regions.byMaterial.size}`,
|
|
224
|
-
because: 'text.maturity-readable-because'
|
|
224
|
+
because: 'figure.text.maturity-readable-because'
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
|