@things-factory/figure-ui 10.1.15 → 10.1.17

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.
Files changed (78) hide show
  1. package/client/modeller/figure-animations.ts +12 -12
  2. package/client/modeller/figure-canvas.ts +32 -48
  3. package/client/modeller/figure-capabilities.ts +10 -10
  4. package/client/modeller/figure-inspector.ts +39 -28
  5. package/client/modeller/figure-parts.ts +1 -1
  6. package/client/modeller/figure-preview.ts +122 -25
  7. package/client/modeller/figure-report.ts +24 -6
  8. package/client/modeller/figure-settings.ts +45 -2
  9. package/client/modeller/figure-side.ts +6 -6
  10. package/client/modeller/figure-source.ts +84 -38
  11. package/client/modeller/figure-thumbnail.ts +5 -5
  12. package/client/modeller/figure-view.ts +53 -0
  13. package/client/modeller/part-edits.ts +36 -36
  14. package/client/modeller/proposal.ts +25 -4
  15. package/client/pages/figure-modeller-page.ts +142 -98
  16. package/dist-client/modeller/figure-animations.d.ts +4 -4
  17. package/dist-client/modeller/figure-animations.js +10 -10
  18. package/dist-client/modeller/figure-animations.js.map +1 -1
  19. package/dist-client/modeller/figure-canvas.d.ts +7 -7
  20. package/dist-client/modeller/figure-canvas.js +27 -40
  21. package/dist-client/modeller/figure-canvas.js.map +1 -1
  22. package/dist-client/modeller/figure-capabilities.d.ts +2 -2
  23. package/dist-client/modeller/figure-capabilities.js +9 -9
  24. package/dist-client/modeller/figure-capabilities.js.map +1 -1
  25. package/dist-client/modeller/figure-inspector.d.ts +16 -5
  26. package/dist-client/modeller/figure-inspector.js +38 -27
  27. package/dist-client/modeller/figure-inspector.js.map +1 -1
  28. package/dist-client/modeller/figure-parts.d.ts +1 -1
  29. package/dist-client/modeller/figure-parts.js +1 -1
  30. package/dist-client/modeller/figure-parts.js.map +1 -1
  31. package/dist-client/modeller/figure-preview.d.ts +39 -0
  32. package/dist-client/modeller/figure-preview.js +112 -23
  33. package/dist-client/modeller/figure-preview.js.map +1 -1
  34. package/dist-client/modeller/figure-report.d.ts +9 -0
  35. package/dist-client/modeller/figure-report.js +23 -5
  36. package/dist-client/modeller/figure-report.js.map +1 -1
  37. package/dist-client/modeller/figure-settings.js +42 -2
  38. package/dist-client/modeller/figure-settings.js.map +1 -1
  39. package/dist-client/modeller/figure-side.d.ts +2 -2
  40. package/dist-client/modeller/figure-side.js +5 -5
  41. package/dist-client/modeller/figure-side.js.map +1 -1
  42. package/dist-client/modeller/figure-source.d.ts +38 -23
  43. package/dist-client/modeller/figure-source.js +40 -21
  44. package/dist-client/modeller/figure-source.js.map +1 -1
  45. package/dist-client/modeller/figure-thumbnail.d.ts +2 -2
  46. package/dist-client/modeller/figure-thumbnail.js +3 -3
  47. package/dist-client/modeller/figure-thumbnail.js.map +1 -1
  48. package/dist-client/modeller/figure-view.d.ts +45 -0
  49. package/dist-client/modeller/figure-view.js +27 -0
  50. package/dist-client/modeller/figure-view.js.map +1 -1
  51. package/dist-client/modeller/part-edits.d.ts +16 -16
  52. package/dist-client/modeller/part-edits.js +35 -35
  53. package/dist-client/modeller/part-edits.js.map +1 -1
  54. package/dist-client/modeller/proposal.d.ts +1 -1
  55. package/dist-client/modeller/proposal.js +15 -3
  56. package/dist-client/modeller/proposal.js.map +1 -1
  57. package/dist-client/pages/figure-modeller-page.d.ts +44 -9
  58. package/dist-client/pages/figure-modeller-page.js +134 -92
  59. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  60. package/dist-client/route.d.ts +1 -1
  61. package/dist-client/tsconfig.tsbuildinfo +1 -1
  62. package/dist-server/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +4 -4
  64. package/test/ai-proposal-contract.test.ts +393 -12
  65. package/test/expect.ts +55 -0
  66. package/test/figure-source.test.ts +562 -0
  67. package/test/i18n-prefix-guard.test.ts +1 -1
  68. package/{client/modeller → test}/part-edits.test.ts +50 -45
  69. package/translations/en.json +31 -28
  70. package/translations/ja.json +16 -21
  71. package/translations/ko.json +41 -38
  72. package/translations/ms.json +16 -21
  73. package/translations/zh.json +16 -21
  74. package/client/modeller/figure-ask.ts +0 -265
  75. package/client/modeller/figure-source.test.ts +0 -352
  76. package/dist-client/modeller/figure-ask.d.ts +0 -40
  77. package/dist-client/modeller/figure-ask.js +0 -282
  78. package/dist-client/modeller/figure-ask.js.map +0 -1
@@ -1,3 +1,8 @@
1
+ import assert from 'node:assert/strict'
2
+ import { describe, it, test } from 'node:test'
3
+
4
+ import { expect } from './expect.ts'
5
+
1
6
  import {
2
7
  addPart,
3
8
  alignPartToEnd,
@@ -14,9 +19,9 @@ import {
14
19
  removePart,
15
20
  snapPart,
16
21
  updatePart
17
- } from './part-edits'
18
- import { toFigureSource } from './figure-source'
19
- import type { BoardModel, PartModel } from './figure-source'
22
+ } from '../client/modeller/part-edits.ts'
23
+ import { toFigureSource } from '../dist-client/modeller/figure-source.js'
24
+ import type { FigureDraft, PartModel } from '../dist-client/modeller/figure-source.js'
20
25
  import { validate } from '@hatiolab/figure-model'
21
26
 
22
27
  /**
@@ -28,8 +33,8 @@ import { validate } from '@hatiolab/figure-model'
28
33
  * 상황이 생긴다.
29
34
  */
30
35
 
31
- /** 보드 3000 x 2000 x 800 — 세 축이 모두 달라야 축을 바꿔 써도 테스트가 잡아낸다. */
32
- const BOARD: BoardModel = {
36
+ /** 기준 상자 3000 x 2000 x 800 — 세 축이 모두 달라야 축을 바꿔 써도 테스트가 잡아낸다. */
37
+ const DRAFT: FigureDraft = {
33
38
  width: 3000,
34
39
  height: 2000,
35
40
  depth: 800,
@@ -51,8 +56,8 @@ const DECK: PartModel = {
51
56
  }
52
57
 
53
58
  describe('부품 생성', () => {
54
- it('보드의 3분의 1 크기로 생성된다 — 추가하자마자 보여야 한다', () => {
55
- const part = newPart(BOARD, [], 'cube')
59
+ it('기준 상자의 3분의 1 크기로 생성된다 — 추가하자마자 보여야 한다', () => {
60
+ const part = newPart(DRAFT, [], 'cube')
56
61
 
57
62
  expect(part.width).toBe(1000)
58
63
  expect(part.height).toBe(667)
@@ -60,37 +65,37 @@ describe('부품 생성', () => {
60
65
  })
61
66
 
62
67
  it('바닥 가운데에 놓인다', () => {
63
- const part = newPart(BOARD, [], 'cube')
68
+ const part = newPart(DRAFT, [], 'cube')
64
69
 
65
70
  expect(part.zPos).toBe(0)
66
71
  // 위치도 크기도 정수로 맞추므로 0.5 까지는 어긋난다
67
- expect(Math.abs(part.left + part.width / 2 - BOARD.width / 2)).toBeLessThanOrEqual(0.5)
68
- expect(Math.abs(part.top + part.height / 2 - BOARD.height / 2)).toBeLessThanOrEqual(0.5)
72
+ expect(Math.abs(part.left + part.width / 2 - DRAFT.width / 2)).toBeLessThanOrEqual(0.5)
73
+ expect(Math.abs(part.top + part.height / 2 - DRAFT.height / 2)).toBeLessThanOrEqual(0.5)
69
74
  })
70
75
 
71
- it('보드에 그대로 놓을 수 있는 씬 컴포넌트 타입을 갖는다', () => {
72
- expect(newPart(BOARD, [], 'cube').type).toBe('figure-part')
76
+ it('편집 캔버스에 그대로 놓을 수 있는 씬 컴포넌트 타입을 갖는다', () => {
77
+ expect(newPart(DRAFT, [], 'cube').type).toBe('figure-part')
73
78
  })
74
79
 
75
80
  it('이름이 중복되지 않는다 — 저장되는 식별자다', () => {
76
81
  let parts: PartModel[] = []
77
- for (let i = 0; i < 3; i++) parts = addPart(BOARD, parts, 'cube')
82
+ for (let i = 0; i < 3; i++) parts = addPart(DRAFT, parts, 'cube')
78
83
 
79
84
  expect(parts.map(part => part.name)).toEqual(['cube', 'cube-2', 'cube-3'])
80
85
  })
81
86
 
82
87
  it('팔레트 토큰을 기존 부품에서 물려받는다 — 토큰이 늘면 재질 그룹이 하나씩 는다', () => {
83
- const parts = addPart(BOARD, [{ ...DECK, token: 'palette.dark' }], 'sphere')
88
+ const parts = addPart(DRAFT, [{ ...DECK, token: 'palette.dark' }], 'sphere')
84
89
  expect(parts[1].token).toBe('palette.dark')
85
90
  })
86
91
 
87
92
  it('곡면 도형은 분할 수를 갖고 생성된다', () => {
88
- expect(newPart(BOARD, [], 'cylinder').segments).toBe(12)
89
- expect(newPart(BOARD, [], 'cube').segments).toBeUndefined()
93
+ expect(newPart(DRAFT, [], 'cylinder').segments).toBe(12)
94
+ expect(newPart(DRAFT, [], 'cube').segments).toBeUndefined()
90
95
  })
91
96
 
92
97
  it('polygon 은 삼각형으로 생성된다 — 점이 3개보다 적으면 검증에서 막힌다', () => {
93
- expect(newPart(BOARD, [], 'polygon').path).toHaveLength(3)
98
+ expect(newPart(DRAFT, [], 'polygon').path).toHaveLength(3)
94
99
  })
95
100
 
96
101
  /*
@@ -99,7 +104,7 @@ describe('부품 생성', () => {
99
104
  */
100
105
  it('새로 만든 부품만으로도 검증을 통과한다', () => {
101
106
  for (const kind of ['cube', 'wall', 'cylinder', 'sphere', 'rect', 'polygon'] as const) {
102
- const source = toFigureSource(BOARD, [newPart(BOARD, [], kind)])
107
+ const source = toFigureSource(DRAFT, [newPart(DRAFT, [], kind)])
103
108
  expect(validate(source).errors).toEqual([])
104
109
  }
105
110
  })
@@ -143,7 +148,7 @@ describe('목록 편집', () => {
143
148
  })
144
149
 
145
150
  it('부품 수 한도는 디테일 등급이 결정한다', () => {
146
- expect(partLimit({ ...BOARD, detailLevel: 'S' })).toBeLessThan(partLimit({ ...BOARD, detailLevel: 'L' }))
151
+ expect(partLimit({ ...DRAFT, detailLevel: 'S' })).toBeLessThan(partLimit({ ...DRAFT, detailLevel: 'L' }))
147
152
  })
148
153
  })
149
154
 
@@ -156,40 +161,40 @@ describe('목록 편집', () => {
156
161
  */
157
162
  describe('정밀 배치 도구', () => {
158
163
  it('가운데로 옮기면 변환했을 때 원점에 온다', () => {
159
- const moved = centerPart(BOARD, [{ ...DECK, left: 0 }], 0, 'x')
164
+ const moved = centerPart(DRAFT, [{ ...DECK, left: 0 }], 0, 'x')
160
165
 
161
166
  expect(moved[0].left).toBe(1100)
162
- expect(toFigureSource(BOARD, moved).parts[0].transform.position.x).toBe(0)
167
+ expect(toFigureSource(DRAFT, moved).parts[0].transform.position.x).toBe(0)
163
168
  })
164
169
 
165
170
  it('축마다 다른 속성을 바꾼다 — z 는 top 이고 y 는 zPos 다', () => {
166
- expect(centerPart(BOARD, [DECK], 0, 'z')[0].top).toBe(700)
167
- expect(centerPart(BOARD, [DECK], 0, 'y')[0].zPos).toBe(300)
171
+ expect(centerPart(DRAFT, [DECK], 0, 'z')[0].top).toBe(700)
172
+ expect(centerPart(DRAFT, [DECK], 0, 'y')[0].zPos).toBe(300)
168
173
  })
169
174
 
170
- it('바닥에 붙이면 zPos 가 0 이다 — 밑면이 곧 보드 바닥이다', () => {
175
+ it('바닥에 붙이면 zPos 가 0 이다 — 밑면이 곧 상자 바닥이다', () => {
171
176
  expect(alignPartToStart([DECK], 0, 'y')[0].zPos).toBe(0)
172
177
  })
173
178
 
174
- it('끝에 붙이면 부품의 끝이 보드의 끝과 만난다', () => {
175
- const moved = alignPartToEnd(BOARD, [DECK], 0, 'x')
176
- expect(moved[0].left + moved[0].width).toBe(BOARD.width)
179
+ it('끝에 붙이면 부품의 끝이 상자의 끝과 만난다', () => {
180
+ const moved = alignPartToEnd(DRAFT, [DECK], 0, 'x')
181
+ expect(moved[0].left + moved[0].width).toBe(DRAFT.width)
177
182
  })
178
183
 
179
- it('대칭 복제가 보드 안에서 반대쪽으로 간다', () => {
180
- const parts = mirrorPart(BOARD, [{ ...DECK, left: 200 }], 0, 'x')
184
+ it('대칭 복제가 상자 안에서 반대쪽으로 간다', () => {
185
+ const parts = mirrorPart(DRAFT, [{ ...DECK, left: 200 }], 0, 'x')
181
186
 
182
187
  expect(parts).toHaveLength(2)
183
188
  // 왼쪽 여백 200 → 오른쪽 여백도 200 이어야 한다
184
- expect(parts[1].left).toBe(BOARD.width - 200 - DECK.width)
189
+ expect(parts[1].left).toBe(DRAFT.width - 200 - DECK.width)
185
190
  })
186
191
 
187
192
  it('대칭 복제본이 어느 쪽인지 이름에 드러난다', () => {
188
- expect(mirrorPart(BOARD, [{ ...DECK, left: 200 }], 0, 'x')[1].name).toBe('deck-x')
193
+ expect(mirrorPart(DRAFT, [{ ...DECK, left: 200 }], 0, 'x')[1].name).toBe('deck-x')
189
194
  })
190
195
 
191
196
  it('대칭 복제가 원본을 건드리지 않는다', () => {
192
- const parts = mirrorPart(BOARD, [{ ...DECK, left: 200 }], 0, 'x')
197
+ const parts = mirrorPart(DRAFT, [{ ...DECK, left: 200 }], 0, 'x')
193
198
  expect(parts[0].left).toBe(200)
194
199
  })
195
200
 
@@ -198,26 +203,26 @@ describe('정밀 배치 도구', () => {
198
203
  늘고 겹친 면에서 z-fighting 이 일어난다.
199
204
  */
200
205
  it('이미 가운데면 대칭 복제를 막는다', () => {
201
- const centered = centerPart(BOARD, [DECK], 0, 'x')
206
+ const centered = centerPart(DRAFT, [DECK], 0, 'x')
202
207
 
203
- expect(canMirror(BOARD, centered, 0, 'x')).toBe(false)
204
- expect(mirrorPart(BOARD, centered, 0, 'x')).toHaveLength(1)
208
+ expect(canMirror(DRAFT, centered, 0, 'x')).toBe(false)
209
+ expect(mirrorPart(DRAFT, centered, 0, 'x')).toHaveLength(1)
205
210
  })
206
211
 
207
212
  it('가운데가 아니면 대칭 복제할 수 있다', () => {
208
- expect(canMirror(BOARD, [{ ...DECK, left: 200 }], 0, 'x')).toBe(true)
213
+ expect(canMirror(DRAFT, [{ ...DECK, left: 200 }], 0, 'x')).toBe(true)
209
214
  })
210
215
 
211
216
  it('격자에 맞추면 위치와 크기가 격자 배수가 된다', () => {
212
- const step = gridStep(BOARD)
213
- const snapped = snapPart(BOARD, [{ ...DECK, left: 1213, width: 787 }], 0)
217
+ const step = gridStep(DRAFT)
218
+ const snapped = snapPart(DRAFT, [{ ...DECK, left: 1213, width: 787 }], 0)
214
219
 
215
220
  expect(snapped[0].left % step).toBe(0)
216
221
  expect(snapped[0].width % step).toBe(0)
217
222
  })
218
223
 
219
224
  it('격자에 맞춰도 크기가 0 이 되지 않는다 — 사라진 것처럼 보인다', () => {
220
- const snapped = snapPart(BOARD, [{ ...DECK, width: 1, height: 1, depth: 1 }], 0)
225
+ const snapped = snapPart(DRAFT, [{ ...DECK, width: 1, height: 1, depth: 1 }], 0)
221
226
 
222
227
  expect(snapped[0].width).toBeGreaterThan(0)
223
228
  expect(snapped[0].height).toBeGreaterThan(0)
@@ -226,10 +231,10 @@ describe('정밀 배치 도구', () => {
226
231
 
227
232
  it('없는 인덱스를 주면 목록을 그대로 반환한다', () => {
228
233
  const parts = [DECK]
229
- expect(centerPart(BOARD, parts, 7, 'x')).toBe(parts)
230
- expect(snapPart(BOARD, parts, 7)).toBe(parts)
231
- expect(mirrorPart(BOARD, parts, 7, 'x')).toBe(parts)
232
- expect(canMirror(BOARD, parts, 7, 'x')).toBe(false)
234
+ expect(centerPart(DRAFT, parts, 7, 'x')).toBe(parts)
235
+ expect(snapPart(DRAFT, parts, 7)).toBe(parts)
236
+ expect(mirrorPart(DRAFT, parts, 7, 'x')).toBe(parts)
237
+ expect(canMirror(DRAFT, parts, 7, 'x')).toBe(false)
233
238
  })
234
239
  })
235
240
 
@@ -246,7 +251,7 @@ describe('격자 간격', () => {
246
251
  expect(niceStep(-100)).toBeGreaterThan(0)
247
252
  })
248
253
 
249
- it('보드의 긴 쪽을 기준으로 잡는다 — 3D 바닥 격자와 같아야 한다', () => {
250
- expect(gridStep(BOARD)).toBe(niceStep(3000))
254
+ it('기준 상자의 긴 쪽을 기준으로 잡는다 — 3D 바닥 격자와 같아야 한다', () => {
255
+ expect(gridStep(DRAFT)).toBe(niceStep(3000))
251
256
  })
252
257
  })
@@ -3,12 +3,7 @@
3
3
  "figure.button.add-clip": "add clip",
4
4
  "figure.button.add-key": "add key",
5
5
  "figure.button.ask": "ask",
6
- "figure.label.candidate-review": "candidate review",
7
- "figure.text.proposal-feedback-note": "Optional note for the next suggestion (this session only)",
8
- "figure.text.proposal-feedback-placeholder": "e.g. keep the silhouette, but make the port easier to read",
9
- "figure.text.tools-explained": "Use alignment after setting position, size, and rotation. From left: centre X, centre Z, put on floor, mirror X, mirror Z, and snap to grid. Hover an icon to confirm its action.",
10
- "figure.text.palette-purpose-explained": "Choose the visual role this part plays, not an implementation color name.",
11
- "figure.button.attach-a-picture": "image",
6
+ "figure.button.cancel": "cancel",
12
7
  "figure.button.center-x": "centre X",
13
8
  "figure.button.center-z": "centre Z",
14
9
  "figure.button.close": "close",
@@ -22,29 +17,30 @@
22
17
  "figure.button.new-figure": "new figure",
23
18
  "figure.button.pivot-clear": "part centre",
24
19
  "figure.button.pivot-set": "set",
20
+ "figure.button.redo": "redo",
25
21
  "figure.button.release-figure": "release",
26
22
  "figure.button.remove": "remove",
27
23
  "figure.button.remove-part": "delete",
28
24
  "figure.button.reset-view": "reset",
29
25
  "figure.button.revert": "revert",
30
26
  "figure.button.save": "save",
31
- "figure.button.undo": "undo",
32
- "figure.button.redo": "redo",
33
27
  "figure.button.see-the-trend-chart": "see the trend chart",
34
28
  "figure.button.snap": "snap",
35
29
  "figure.button.take-n-changes": "apply {n}",
36
30
  "figure.button.to-floor": "to floor",
31
+ "figure.button.undo": "undo",
37
32
  "figure.label.accepts": "accepts",
38
33
  "figure.label.anchor": "holds which face",
34
+ "figure.label.anchor-advanced": "advanced proportional sizing",
39
35
  "figure.label.anchor-aspect-x": "grows only as much as x does (keeps ratio)",
40
36
  "figure.label.anchor-aspect-y": "grows only as much as y does (keeps ratio)",
41
37
  "figure.label.anchor-aspect-z": "grows only as much as z does (keeps ratio)",
42
38
  "figure.label.anchor-auto": "automatic ({rule})",
43
- "figure.label.anchor-advanced": "advanced proportional sizing",
44
- "figure.label.anchor-choice-auto": "automatic",
39
+ "figure.label.anchor-center": "stays centred, keeps its size",
45
40
  "figure.label.anchor-choice-aspect-x": "keep X ratio",
46
41
  "figure.label.anchor-choice-aspect-y": "keep Y ratio",
47
42
  "figure.label.anchor-choice-aspect-z": "keep Z ratio",
43
+ "figure.label.anchor-choice-auto": "automatic",
48
44
  "figure.label.anchor-choice-center": "stay centred",
49
45
  "figure.label.anchor-choice-max-x": "right face",
50
46
  "figure.label.anchor-choice-max-y": "upper face",
@@ -54,7 +50,6 @@
54
50
  "figure.label.anchor-choice-min-z": "back face",
55
51
  "figure.label.anchor-choice-scale": "grow together",
56
52
  "figure.label.anchor-choice-span": "fill both faces",
57
- "figure.label.anchor-center": "stays centred, keeps its size",
58
53
  "figure.label.anchor-max": "stays on the upper face",
59
54
  "figure.label.anchor-max-x": "stays on the right face",
60
55
  "figure.label.anchor-max-y": "stays on the top face",
@@ -66,6 +61,7 @@
66
61
  "figure.label.anchor-scale": "grows with the box",
67
62
  "figure.label.anchor-span": "touches both faces, fills between",
68
63
  "figure.label.animation": "animation",
64
+ "figure.label.assumed-board": "the board it goes on",
69
65
  "figure.label.axis-consistency": "visual consistency",
70
66
  "figure.label.axis-consistency-short": "consistency",
71
67
  "figure.label.axis-depth": "depth",
@@ -89,8 +85,10 @@
89
85
  "figure.label.budget-transparent": "transparent materials",
90
86
  "figure.label.budgets": "budgets",
91
87
  "figure.label.by-axis": "maturity by axis",
88
+ "figure.label.candidate-review": "candidate review",
92
89
  "figure.label.cast-shadow": "cast shadow",
93
90
  "figure.label.category": "category",
91
+ "figure.label.ceiling-height": "ceiling height (mm)",
94
92
  "figure.label.channel-path": "moves",
95
93
  "figure.label.channel-pivot": "pivot",
96
94
  "figure.label.channel-target": "part",
@@ -121,10 +119,7 @@
121
119
  "figure.label.environment-sunny": "sunny",
122
120
  "figure.label.environment-warehouse": "warehouse",
123
121
  "figure.label.figure": "figure",
124
- "figure.label.figure-placement": "3D placement",
125
- "figure.label.placement-floor": "floor-based (floor)",
126
- "figure.label.placement-ceiling": "ceiling-based (ceiling)",
127
- "figure.label.placement-center": "center-based (center)",
122
+ "figure.label.figure-placement": "placement",
128
123
  "figure.label.figure-released": "released",
129
124
  "figure.label.figure-type": "type",
130
125
  "figure.label.findings": "findings",
@@ -150,8 +145,6 @@
150
145
  "figure.label.none": "none",
151
146
  "figure.label.now": "current",
152
147
  "figure.label.on-by-default": "on by default",
153
- "figure.label.palette-token": "palette token",
154
- "figure.label.palette-role": "color role",
155
148
  "figure.label.palette-purpose-accent": "accent part · handles and covers that must stand out",
156
149
  "figure.label.palette-purpose-danger": "danger signal · an error or stop indicator",
157
150
  "figure.label.palette-purpose-dark": "frame and shadow · dark structure and edges",
@@ -163,6 +156,8 @@
163
156
  "figure.label.palette-purpose-success": "normal signal · a normal-operation indicator",
164
157
  "figure.label.palette-purpose-surface": "surface and background · broad background faces",
165
158
  "figure.label.palette-purpose-warning": "warning signal · an attention-needed indicator",
159
+ "figure.label.palette-role": "color role",
160
+ "figure.label.palette-token": "palette token",
166
161
  "figure.label.part": "part",
167
162
  "figure.label.part-material": "material",
168
163
  "figure.label.part-position": "position (centre)",
@@ -172,6 +167,9 @@
172
167
  "figure.label.path-scale": "scale",
173
168
  "figure.label.path-translation": "offset",
174
169
  "figure.label.placement": "position and size",
170
+ "figure.label.placement-ceiling": "ceiling-based (ceiling)",
171
+ "figure.label.placement-center": "center-based (center)",
172
+ "figure.label.placement-floor": "floor-based (floor)",
175
173
  "figure.label.placement-limit": "placement limit",
176
174
  "figure.label.preset": "preset",
177
175
  "figure.label.preset-ceramic": "ceramic",
@@ -205,6 +203,10 @@
205
203
  "figure.label.rung-none": "draws only",
206
204
  "figure.label.same-as-off": "same as unlit",
207
205
  "figure.label.scene-floor": "floor",
206
+ "figure.label.scene-reference": "scene reference",
207
+ "figure.label.scene-reference-floor": "floor",
208
+ "figure.label.scene-reference-inverted": "inverted (ceiling)",
209
+ "figure.label.scene-reference-space": "space",
208
210
  "figure.label.segments": "segments",
209
211
  "figure.label.shape": "shape",
210
212
  "figure.label.shape-detail": "shape detail",
@@ -242,11 +244,6 @@
242
244
  "figure.text.animation-empty-what-it-is": "This figure does not move yet. A clip is one set of motion across several parts — a door opening is the two leaves sliding apart, not two separate things.",
243
245
  "figure.text.animation-is-driven-by-a-fact": "One clip moves several parts together. On a board a fact sets its value — “this door is 40% open”.",
244
246
  "figure.text.animation-needs-a-named-part": "Add a part first. A channel names the part it moves.",
245
- "figure.text.ask-create-example": "e.g. a three-tier conveyor sorter",
246
- "figure.text.ask-revise-example": "e.g. bring it down to two material groups",
247
- "figure.text.ask-to-create": "create",
248
- "figure.text.ask-to-revise": "revise",
249
- "figure.text.asking": "working…",
250
247
  "figure.text.assistant-proposed-a-figure": "here is a proposal — the result is on the right.",
251
248
  "figure.text.axes-not-measurable-yet": "axes that cannot be measured yet are left off the chart — {axes}. the reason is listed below.",
252
249
  "figure.text.bound-by-drawCalls": "draw calls reach the limit first — {per} per instance, against {budget} for a board.",
@@ -258,13 +255,16 @@
258
255
  "figure.text.capacity-from-repeat": "capacity comes from the repeat setting and the instance size",
259
256
  "figure.text.category-explained": "how the palette groups figures. one per figure — a figure in two groups meets the author twice.",
260
257
  "figure.text.category-placeholder": "e.g. conveyor",
258
+ "figure.text.ceiling-height-explained": "where a ceiling figure hangs from in the preview. A figure does not know how tall the room it goes in is.",
261
259
  "figure.text.center-on-x": "move to the centre of the base box on X.",
262
260
  "figure.text.center-on-z": "move to the centre of the base box on Z.",
263
261
  "figure.text.change-added": "a new part",
264
- "figure.text.change-anchor": "the anchor",
262
+ "figure.text.change-animations": "animations",
265
263
  "figure.text.change-base": "the base size",
264
+ "figure.text.change-capabilities": "capabilities",
266
265
  "figure.text.change-changed": "a changed part",
267
266
  "figure.text.change-detail": "the detail level",
267
+ "figure.text.change-placement": "placement",
268
268
  "figure.text.change-removed": "a part being removed",
269
269
  "figure.text.change-styleKit": "the style kit",
270
270
  "figure.text.channel-target-is-gone": "No such part. If you renamed the part, pick it again here.",
@@ -288,6 +288,7 @@
288
288
  "figure.text.expand-parts-pane": "expand the part list",
289
289
  "figure.text.expand-properties-pane": "expand the property pane",
290
290
  "figure.text.figure-display-name": "display name",
291
+ "figure.text.figure-type-placeholder": "Figure type identifier (e.g. OHT)",
291
292
  "figure.text.find-by-this-tag": "find by this tag",
292
293
  "figure.text.fits-n-instances": "up to {n}",
293
294
  "figure.text.floor-explained": "overlaps the coordinate ground, so usually off",
@@ -321,6 +322,7 @@
321
322
  "figure.text.mirror-would-overlap": "already centred — a mirror would land in the same place and overlap.",
322
323
  "figure.text.mode-edit-explained": "place and edit parts. always at the base size.",
323
324
  "figure.text.mode-preview-explained": "how it looks placed on a board. base size next to the enlarged ones.",
325
+ "figure.text.more-segment-options": "advanced segment counts",
324
326
  "figure.text.must-fix-to-release": "fix these first",
325
327
  "figure.text.n-of-limit": "{used} / {limit}",
326
328
  "figure.text.n-parts-at-most": "up to {limit} parts",
@@ -332,7 +334,7 @@
332
334
  "figure.text.nothing-released-yet": "nothing released yet. releasing keeps the figure as it is now, as a version.",
333
335
  "figure.text.on-by-default-explained": "the starting state when placed on a board. an instance switching it wins.",
334
336
  "figure.text.other-segment-count": "enter a value",
335
- "figure.text.more-segment-options": "advanced segment counts",
337
+ "figure.text.palette-purpose-explained": "Choose the visual role this part plays, not an implementation color name.",
336
338
  "figure.text.part-limit-reached": "the detail level allows no more parts — raise the level or remove a part.",
337
339
  "figure.text.part-limit-reached-short": "level {level} allows {limit} parts. Raise the level or remove a part.",
338
340
  "figure.text.path-rotation-unit": "Euler degrees — the same convention as the part's own rotation.",
@@ -340,11 +342,12 @@
340
342
  "figure.text.path-translation-unit": "Offset from where the part sits. 0 leaves it in place.",
341
343
  "figure.text.pick-a-shape-above-to-start": "pick a shape above to add the first part.",
342
344
  "figure.text.picked-changes-do-not-validate": "the selected changes do not make a valid figure",
343
- "figure.text.picture-is-for-proportions": "used for proportions and form, not for surface detail.",
344
345
  "figure.text.pivot-defaults-to-the-part-centre": "Left unset, the part turns about its own centre. Coordinates are local to the part.",
345
346
  "figure.text.placement-explained": "sets the anchor plane in 3D scene (floor: AGV/conveyor, ceiling: OHT/hoist, center: sensor/drone).",
346
347
  "figure.text.preview-blocked-by-errors": "cannot build — the figure breaks the format. the maturity tab has the reason.",
347
348
  "figure.text.preview-needs-a-part": "shows how it looks placed on a board. appears once you make a part.",
349
+ "figure.text.proposal-feedback-note": "Optional note for the next suggestion (this session only)",
350
+ "figure.text.proposal-feedback-placeholder": "e.g. keep the silhouette, but make the port easier to read",
348
351
  "figure.text.put-on-floor": "drop it to the floor (Z=0).",
349
352
  "figure.text.release-needs-a-save-first": "there are unsaved changes. a release publishes what was saved, so save first.",
350
353
  "figure.text.releasing": "releasing…",
@@ -362,8 +365,10 @@
362
365
  "figure.text.rung-flow-note": "holds, and takes in and sends out through ports. conveyors, chutes, sorters",
363
366
  "figure.text.rung-hold-note": "has places to put things. racks, pallets, benches",
364
367
  "figure.text.rung-none-note": "takes nothing. most figures belong here",
368
+ "figure.text.save-new-figure-desc": "Set the unique type code and display name for this figure.",
365
369
  "figure.text.save-the-figure-before-releasing": "this figure has not been saved yet. save it and you can release it.",
366
370
  "figure.text.saving-figure": "saving",
371
+ "figure.text.scene-reference-explained": "which kind of board you are looking at this on. Not the same as the figure's own placement — put a ceiling figure on a floor-based board and the board stays put while the figure hangs at ceiling height.",
367
372
  "figure.text.search-figure": "search by name, type, description, category or tag",
368
373
  "figure.text.select-a-part-to-edit": "select a part to edit it here.",
369
374
  "figure.text.show-all-parts": "show all",
@@ -378,6 +383,7 @@
378
383
  "figure.text.tags-explained": "for the finding a single category cannot do. a figure may carry several — a conveyor that is also outbound. separate them with commas.",
379
384
  "figure.text.tags-placeholder": "e.g. outbound, automatic, level 2",
380
385
  "figure.text.thumbnail-not-made-yet": "no thumbnail yet",
386
+ "figure.text.tools-explained": "Use alignment after setting position, size, and rotation. From left: centre X, centre Z, put on floor, mirror X, mirror Z, and snap to grid. Hover an icon to confirm its action.",
381
387
  "figure.text.triangle-count": "{count} triangles",
382
388
  "figure.text.two-keys-at-least": "A channel needs two keys. With one it is not motion, it is a single pose.",
383
389
  "figure.text.type-name-already-taken": "the type name {type} is already in use. pick another one — it cannot be changed later.",
@@ -387,9 +393,6 @@
387
393
  "figure.text.view-settings-are-not-saved": "these settings are not saved. they make the view comfortable to work in and do not travel with the asset.",
388
394
  "figure.text.what-a-figure-is": "a Figure is a 3D component you author. Compose it from simple shapes and it becomes a component you can place on a board, sharing shape and material across every instance.",
389
395
  "figure.text.what-changed-in-this-release": "what changed in this release",
390
- "figure.button.cancel": "cancel",
391
- "figure.text.figure-type-placeholder": "Figure type identifier (e.g. OHT)",
392
- "figure.text.save-new-figure-desc": "Set the unique type code and display name for this figure.",
393
396
  "figure.title.figures": "figures",
394
397
  "figure.title.new-figure": "new figure",
395
398
  "figure.title.save-new-figure": "save figure draft"
@@ -1,11 +1,5 @@
1
1
  {
2
2
  "figure.button.ask": "ask",
3
- "figure.label.candidate-review": "candidate review",
4
- "figure.text.proposal-feedback-note": "Optional note for the next suggestion (this session only)",
5
- "figure.text.proposal-feedback-placeholder": "e.g. keep the silhouette, but make the port easier to read",
6
- "figure.text.tools-explained": "Use alignment after setting position, size, and rotation. From left: centre X, centre Z, put on floor, mirror X, mirror Z, and snap to grid. Hover an icon to confirm its action.",
7
- "figure.text.palette-purpose-explained": "Choose the visual role this part plays, not an implementation color name.",
8
- "figure.button.attach-a-picture": "image",
9
3
  "figure.button.center-x": "centre X",
10
4
  "figure.button.center-z": "centre Z",
11
5
  "figure.button.close": "close",
@@ -17,27 +11,28 @@
17
11
  "figure.button.move-part-down": "move down",
18
12
  "figure.button.move-part-up": "move up",
19
13
  "figure.button.new-figure": "new figure",
14
+ "figure.button.redo": "やり直す",
20
15
  "figure.button.release-figure": "公開する",
21
16
  "figure.button.remove-part": "delete",
22
17
  "figure.button.reset-view": "reset",
23
18
  "figure.button.revert": "戻す",
24
19
  "figure.button.save": "save",
25
- "figure.button.undo": "元に戻す",
26
- "figure.button.redo": "やり直す",
27
20
  "figure.button.see-the-trend-chart": "see the trend chart",
28
21
  "figure.button.snap": "snap",
29
22
  "figure.button.take-n-changes": "apply {n}",
30
23
  "figure.button.to-floor": "to floor",
24
+ "figure.button.undo": "元に戻す",
31
25
  "figure.label.anchor": "つかむ面",
26
+ "figure.label.anchor-advanced": "advanced proportional sizing",
32
27
  "figure.label.anchor-aspect-x": "x 軸が大きくなる分だけ大きくなる (比率維持)",
33
28
  "figure.label.anchor-aspect-y": "y 軸が大きくなる分だけ大きくなる (比率維持)",
34
29
  "figure.label.anchor-aspect-z": "z 軸が大きくなる分だけ大きくなる (比率維持)",
35
30
  "figure.label.anchor-auto": "自動 ({rule})",
36
- "figure.label.anchor-advanced": "advanced proportional sizing",
37
- "figure.label.anchor-choice-auto": "automatic",
31
+ "figure.label.anchor-center": "中央に残り大きさはそのまま",
38
32
  "figure.label.anchor-choice-aspect-x": "keep X ratio",
39
33
  "figure.label.anchor-choice-aspect-y": "keep Y ratio",
40
34
  "figure.label.anchor-choice-aspect-z": "keep Z ratio",
35
+ "figure.label.anchor-choice-auto": "automatic",
41
36
  "figure.label.anchor-choice-center": "stay centred",
42
37
  "figure.label.anchor-choice-max-x": "right face",
43
38
  "figure.label.anchor-choice-max-y": "upper face",
@@ -47,7 +42,6 @@
47
42
  "figure.label.anchor-choice-min-z": "back face",
48
43
  "figure.label.anchor-choice-scale": "grow together",
49
44
  "figure.label.anchor-choice-span": "fill both faces",
50
- "figure.label.anchor-center": "中央に残り大きさはそのまま",
51
45
  "figure.label.anchor-max": "大きい側の面にくっついている",
52
46
  "figure.label.anchor-max-x": "右の面にくっついている",
53
47
  "figure.label.anchor-max-y": "上の面にくっついている",
@@ -81,6 +75,7 @@
81
75
  "figure.label.budget-transparent": "transparent materials",
82
76
  "figure.label.budgets": "budgets",
83
77
  "figure.label.by-axis": "maturity by axis",
78
+ "figure.label.candidate-review": "candidate review",
84
79
  "figure.label.cast-shadow": "cast shadow",
85
80
  "figure.label.coordinate-ground": "coordinate ground",
86
81
  "figure.label.corner-round": "corner round",
@@ -127,8 +122,6 @@
127
122
  "figure.label.none": "none",
128
123
  "figure.label.now": "current",
129
124
  "figure.label.on-by-default": "on by default",
130
- "figure.label.palette-token": "palette token",
131
- "figure.label.palette-role": "color role",
132
125
  "figure.label.palette-purpose-accent": "accent part · handles and covers that must stand out",
133
126
  "figure.label.palette-purpose-danger": "danger signal · an error or stop indicator",
134
127
  "figure.label.palette-purpose-dark": "frame and shadow · dark structure and edges",
@@ -140,6 +133,8 @@
140
133
  "figure.label.palette-purpose-success": "normal signal · a normal-operation indicator",
141
134
  "figure.label.palette-purpose-surface": "surface and background · broad background faces",
142
135
  "figure.label.palette-purpose-warning": "warning signal · an attention-needed indicator",
136
+ "figure.label.palette-role": "color role",
137
+ "figure.label.palette-token": "palette token",
143
138
  "figure.label.part": "part",
144
139
  "figure.label.part-material": "material",
145
140
  "figure.label.part-position": "position (centre)",
@@ -194,11 +189,6 @@
194
189
  "figure.text.all-lights-off": "both lights are off. only self-lit surfaces show; everything else goes black — this is how you check whether a lamp is lit.",
195
190
  "figure.text.already-released-edit-to-release-again": "すでに公開済みです。修正して保存すると下書きに戻り、そこで再び公開できます。",
196
191
  "figure.text.anchor-explained": "箱が大きくなるとき、この部品がどこにくっついているかです。接している部品と違う面にくっついていると、大きくなるにつれて離れます。",
197
- "figure.text.ask-create-example": "e.g. a three-tier conveyor sorter",
198
- "figure.text.ask-revise-example": "e.g. bring it down to two material groups",
199
- "figure.text.ask-to-create": "create",
200
- "figure.text.ask-to-revise": "revise",
201
- "figure.text.asking": "working…",
202
192
  "figure.text.assistant-proposed-a-figure": "here is a proposal — the result is on the right.",
203
193
  "figure.text.axes-not-measurable-yet": "axes that cannot be measured yet are left off the chart — {axes}. the reason is listed below.",
204
194
  "figure.text.bound-by-drawCalls": "draw calls reach the limit first — {per} per instance, against {budget} for a board.",
@@ -209,10 +199,12 @@
209
199
  "figure.text.center-on-x": "move to the centre of the base box on X.",
210
200
  "figure.text.center-on-z": "move to the centre of the base box on Z.",
211
201
  "figure.text.change-added": "a new part",
212
- "figure.text.change-anchor": "the anchor",
202
+ "figure.text.change-animations": "アニメーション",
213
203
  "figure.text.change-base": "the base size",
204
+ "figure.text.change-capabilities": "能力",
214
205
  "figure.text.change-changed": "a changed part",
215
206
  "figure.text.change-detail": "the detail level",
207
+ "figure.text.change-placement": "配置基準",
216
208
  "figure.text.change-removed": "a part being removed",
217
209
  "figure.text.change-styleKit": "the style kit",
218
210
  "figure.text.collapse-parts-pane": "collapse the part list",
@@ -258,6 +250,7 @@
258
250
  "figure.text.mirror-would-overlap": "already centred — a mirror would land in the same place and overlap.",
259
251
  "figure.text.mode-edit-explained": "place and edit parts. always at the base size.",
260
252
  "figure.text.mode-preview-explained": "how it looks placed on a board. base size next to the enlarged ones.",
253
+ "figure.text.more-segment-options": "advanced segment counts",
261
254
  "figure.text.must-fix-to-release": "直すべきもの",
262
255
  "figure.text.n-of-limit": "{used} / {limit}",
263
256
  "figure.text.n-parts-at-most": "up to {limit} parts",
@@ -269,14 +262,15 @@
269
262
  "figure.text.nothing-released-yet": "まだ公開した版がありません。公開すると、その時点のフィギュアが版として残ります。",
270
263
  "figure.text.on-by-default-explained": "the starting state when placed on a board. an instance switching it wins.",
271
264
  "figure.text.other-segment-count": "enter a value",
272
- "figure.text.more-segment-options": "advanced segment counts",
265
+ "figure.text.palette-purpose-explained": "Choose the visual role this part plays, not an implementation color name.",
273
266
  "figure.text.part-limit-reached": "the detail level allows no more parts — raise the level or remove a part.",
274
267
  "figure.text.part-limit-reached-short": "level {level} allows {limit} parts. Raise the level or remove a part.",
275
268
  "figure.text.pick-a-shape-above-to-start": "pick a shape above to add the first part.",
276
269
  "figure.text.picked-changes-do-not-validate": "the selected changes do not make a valid figure",
277
- "figure.text.picture-is-for-proportions": "used for proportions and form, not for surface detail.",
278
270
  "figure.text.preview-blocked-by-errors": "cannot build — the figure breaks the format. the maturity tab has the reason.",
279
271
  "figure.text.preview-needs-a-part": "add a part and the board view appears here.",
272
+ "figure.text.proposal-feedback-note": "Optional note for the next suggestion (this session only)",
273
+ "figure.text.proposal-feedback-placeholder": "e.g. keep the silhouette, but make the port easier to read",
280
274
  "figure.text.put-on-floor": "drop it to the floor (Z=0).",
281
275
  "figure.text.release-needs-a-save-first": "保存していない変更があります。公開は保存されたものを出すので、先に保存してください。",
282
276
  "figure.text.releasing": "公開しています…",
@@ -299,6 +293,7 @@
299
293
  "figure.text.sizing-stretch-explained": "only one axis stretches — like a frame, a belt or a rail.",
300
294
  "figure.text.snap-explained": "rounds position and size to the grid — the same spacing you see on the canvas.",
301
295
  "figure.text.thumbnail-not-made-yet": "no thumbnail yet",
296
+ "figure.text.tools-explained": "Use alignment after setting position, size, and rotation. From left: centre X, centre Z, put on floor, mirror X, mirror Z, and snap to grid. Hover an icon to confirm its action.",
302
297
  "figure.text.triangle-count": "{count} triangles",
303
298
  "figure.text.type-name-already-taken": "the type name {type} is already in use. pick another one — it cannot be changed later.",
304
299
  "figure.text.type-name-cannot-change": "the type name is a stored identifier and cannot be changed after creation.",