@things-factory/figure-ui 10.1.74 → 10.1.76

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 (244) hide show
  1. package/client/figure-assistant.ts +3 -3
  2. package/client/graphql/{v3-store.ts → store.ts} +6 -6
  3. package/client/modeller/{v3-ai-proposal.ts → ai-proposal.ts} +8 -8
  4. package/client/modeller/{v3-align.ts → align.ts} +14 -14
  5. package/client/modeller/{v3-canvas.ts → canvas.ts} +13 -13
  6. package/client/modeller/{v3-clips.ts → clips.ts} +5 -5
  7. package/client/modeller/{v3-controls.ts → controls.ts} +2 -2
  8. package/client/modeller/{v3-editor.ts → editor.ts} +137 -126
  9. package/client/modeller/{figure-v3-capability.ts → figure-capability-panel.ts} +7 -7
  10. package/client/modeller/{figure-v3-clips.ts → figure-clips.ts} +10 -10
  11. package/client/modeller/{figure-v3-controls.ts → figure-controls.ts} +9 -9
  12. package/client/modeller/{v3-figure-facts.ts → figure-facts.ts} +11 -11
  13. package/client/modeller/{figure-v3-first-save.ts → figure-first-save.ts} +3 -3
  14. package/client/modeller/figure-history-panel.ts +1 -1
  15. package/client/modeller/{figure-v3-links.ts → figure-links.ts} +35 -35
  16. package/client/modeller/{figure-v3-material.ts → figure-material-panel.ts} +20 -20
  17. package/client/modeller/{figure-v3-parts.ts → figure-parts.ts} +15 -15
  18. package/client/modeller/{figure-v3-preview.ts → figure-preview.ts} +14 -14
  19. package/client/modeller/{figure-v3-properties.ts → figure-properties.ts} +25 -25
  20. package/client/modeller/{figure-v3-recipe-catalog.ts → figure-recipe-catalog.ts} +10 -10
  21. package/client/modeller/{figure-v3-verdict.ts → figure-verdict.ts} +24 -24
  22. package/client/modeller/gate-findings.ts +2 -2
  23. package/client/modeller/{v3-gizmo.ts → gizmo.ts} +11 -11
  24. package/client/modeller/{v3-joint-drag.ts → joint-drag.ts} +11 -11
  25. package/client/modeller/{v3-joint-fan.ts → joint-fan.ts} +13 -13
  26. package/client/modeller/{v3-links.ts → links.ts} +59 -59
  27. package/client/modeller/{v3-live.ts → live.ts} +9 -9
  28. package/client/modeller/{v3-maturity.ts → maturity.ts} +65 -65
  29. package/client/modeller/modeller-route.ts +1 -1
  30. package/client/modeller/{v3-occupancy.ts → occupancy.ts} +6 -6
  31. package/client/modeller/{v3-palette.ts → palette.ts} +3 -3
  32. package/client/modeller/{v3-preview-model.ts → preview-model.ts} +8 -8
  33. package/client/modeller/{v3-product-session.ts → product-session.ts} +25 -25
  34. package/client/modeller/{v3-recipe-source.ts → recipe-source.ts} +2 -2
  35. package/client/modeller/{v3-recipes.ts → recipes.ts} +9 -9
  36. package/client/modeller/{v3-refusals.ts → refusals.ts} +62 -13
  37. package/client/modeller/{v3-renderer.ts → renderer.ts} +16 -16
  38. package/client/modeller/{v3-stage-model.ts → stage-model.ts} +15 -15
  39. package/client/modeller/{v3-thumbnail.ts → thumbnail.ts} +8 -8
  40. package/client/pages/figure-list-page.ts +4 -4
  41. package/client/pages/{figure-v3-modeller-page.ts → figure-modeller-page.ts} +37 -37
  42. package/client/route.ts +1 -5
  43. package/dist-client/figure-assistant.js +3 -3
  44. package/dist-client/figure-assistant.js.map +1 -1
  45. package/dist-client/graphql/store.d.ts +3 -0
  46. package/dist-client/graphql/store.js +39 -0
  47. package/dist-client/graphql/store.js.map +1 -0
  48. package/dist-client/modeller/ai-proposal.d.ts +51 -0
  49. package/dist-client/modeller/ai-proposal.js +64 -0
  50. package/dist-client/modeller/ai-proposal.js.map +1 -0
  51. package/dist-client/modeller/align.d.ts +30 -0
  52. package/dist-client/modeller/align.js +75 -0
  53. package/dist-client/modeller/align.js.map +1 -0
  54. package/dist-client/modeller/canvas.d.ts +72 -0
  55. package/dist-client/modeller/canvas.js +379 -0
  56. package/dist-client/modeller/canvas.js.map +1 -0
  57. package/dist-client/modeller/clips.d.ts +39 -0
  58. package/dist-client/modeller/clips.js +58 -0
  59. package/dist-client/modeller/clips.js.map +1 -0
  60. package/dist-client/modeller/controls.d.ts +28 -0
  61. package/dist-client/modeller/controls.js +28 -0
  62. package/dist-client/modeller/controls.js.map +1 -0
  63. package/dist-client/modeller/editor.d.ts +203 -0
  64. package/dist-client/modeller/editor.js +1526 -0
  65. package/dist-client/modeller/editor.js.map +1 -0
  66. package/dist-client/modeller/fields.d.ts +45 -0
  67. package/dist-client/modeller/fields.js +83 -0
  68. package/dist-client/modeller/fields.js.map +1 -0
  69. package/dist-client/modeller/figure-capability-panel.d.ts +22 -0
  70. package/dist-client/modeller/figure-capability-panel.js +141 -0
  71. package/dist-client/modeller/figure-capability-panel.js.map +1 -0
  72. package/dist-client/modeller/figure-clips.d.ts +31 -0
  73. package/dist-client/modeller/figure-clips.js +268 -0
  74. package/dist-client/modeller/figure-clips.js.map +1 -0
  75. package/dist-client/modeller/figure-controls.d.ts +36 -0
  76. package/dist-client/modeller/figure-controls.js +226 -0
  77. package/dist-client/modeller/figure-controls.js.map +1 -0
  78. package/dist-client/modeller/figure-facts.d.ts +35 -0
  79. package/dist-client/modeller/figure-facts.js +55 -0
  80. package/dist-client/modeller/figure-facts.js.map +1 -0
  81. package/dist-client/modeller/figure-first-save.d.ts +20 -0
  82. package/dist-client/modeller/figure-first-save.js +118 -0
  83. package/dist-client/modeller/figure-first-save.js.map +1 -0
  84. package/dist-client/modeller/figure-history-panel.d.ts +1 -1
  85. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  86. package/dist-client/modeller/figure-links.d.ts +77 -0
  87. package/dist-client/modeller/figure-links.js +649 -0
  88. package/dist-client/modeller/figure-links.js.map +1 -0
  89. package/dist-client/modeller/figure-material-panel.d.ts +39 -0
  90. package/dist-client/modeller/figure-material-panel.js +323 -0
  91. package/dist-client/modeller/figure-material-panel.js.map +1 -0
  92. package/dist-client/modeller/figure-parts.d.ts +69 -0
  93. package/dist-client/modeller/figure-parts.js +559 -0
  94. package/dist-client/modeller/figure-parts.js.map +1 -0
  95. package/dist-client/modeller/figure-preview.d.ts +34 -0
  96. package/dist-client/modeller/figure-preview.js +346 -0
  97. package/dist-client/modeller/figure-preview.js.map +1 -0
  98. package/dist-client/modeller/figure-properties.d.ts +45 -0
  99. package/dist-client/modeller/figure-properties.js +239 -0
  100. package/dist-client/modeller/figure-properties.js.map +1 -0
  101. package/dist-client/modeller/figure-recipe-catalog.d.ts +38 -0
  102. package/dist-client/modeller/figure-recipe-catalog.js +399 -0
  103. package/dist-client/modeller/figure-recipe-catalog.js.map +1 -0
  104. package/dist-client/modeller/figure-verdict.d.ts +64 -0
  105. package/dist-client/modeller/figure-verdict.js +787 -0
  106. package/dist-client/modeller/figure-verdict.js.map +1 -0
  107. package/dist-client/modeller/first-save.d.ts +17 -0
  108. package/dist-client/modeller/first-save.js +26 -0
  109. package/dist-client/modeller/first-save.js.map +1 -0
  110. package/dist-client/modeller/gate-findings.d.ts +2 -2
  111. package/dist-client/modeller/gate-findings.js +2 -2
  112. package/dist-client/modeller/gate-findings.js.map +1 -1
  113. package/dist-client/modeller/gizmo.d.ts +72 -0
  114. package/dist-client/modeller/gizmo.js +111 -0
  115. package/dist-client/modeller/gizmo.js.map +1 -0
  116. package/dist-client/modeller/help.d.ts +18 -0
  117. package/dist-client/modeller/help.js +37 -0
  118. package/dist-client/modeller/help.js.map +1 -0
  119. package/dist-client/modeller/joint-drag.d.ts +19 -0
  120. package/dist-client/modeller/joint-drag.js +97 -0
  121. package/dist-client/modeller/joint-drag.js.map +1 -0
  122. package/dist-client/modeller/joint-fan.d.ts +9 -0
  123. package/dist-client/modeller/joint-fan.js +276 -0
  124. package/dist-client/modeller/joint-fan.js.map +1 -0
  125. package/dist-client/modeller/keys.d.ts +22 -0
  126. package/dist-client/modeller/keys.js +26 -0
  127. package/dist-client/modeller/keys.js.map +1 -0
  128. package/dist-client/modeller/links.d.ts +267 -0
  129. package/dist-client/modeller/links.js +480 -0
  130. package/dist-client/modeller/links.js.map +1 -0
  131. package/dist-client/modeller/live.d.ts +37 -0
  132. package/dist-client/modeller/live.js +68 -0
  133. package/dist-client/modeller/live.js.map +1 -0
  134. package/dist-client/modeller/maturity.d.ts +120 -0
  135. package/dist-client/modeller/maturity.js +313 -0
  136. package/dist-client/modeller/maturity.js.map +1 -0
  137. package/dist-client/modeller/modeller-route.d.ts +1 -1
  138. package/dist-client/modeller/modeller-route.js +1 -1
  139. package/dist-client/modeller/modeller-route.js.map +1 -1
  140. package/dist-client/modeller/occupancy.d.ts +18 -0
  141. package/dist-client/modeller/occupancy.js +24 -0
  142. package/dist-client/modeller/occupancy.js.map +1 -0
  143. package/dist-client/modeller/palette.d.ts +7 -0
  144. package/dist-client/modeller/palette.js +46 -0
  145. package/dist-client/modeller/palette.js.map +1 -0
  146. package/dist-client/modeller/preview-model.d.ts +47 -0
  147. package/dist-client/modeller/preview-model.js +68 -0
  148. package/dist-client/modeller/preview-model.js.map +1 -0
  149. package/dist-client/modeller/product-session.d.ts +510 -0
  150. package/dist-client/modeller/product-session.js +157 -0
  151. package/dist-client/modeller/product-session.js.map +1 -0
  152. package/dist-client/modeller/recipe-source.d.ts +8 -0
  153. package/dist-client/modeller/recipe-source.js +14 -0
  154. package/dist-client/modeller/recipe-source.js.map +1 -0
  155. package/dist-client/modeller/recipes.d.ts +37 -0
  156. package/dist-client/modeller/recipes.js +103 -0
  157. package/dist-client/modeller/recipes.js.map +1 -0
  158. package/dist-client/modeller/refusals.d.ts +68 -0
  159. package/dist-client/modeller/refusals.js +142 -0
  160. package/dist-client/modeller/refusals.js.map +1 -0
  161. package/dist-client/modeller/renderer.d.ts +40 -0
  162. package/dist-client/modeller/renderer.js +73 -0
  163. package/dist-client/modeller/renderer.js.map +1 -0
  164. package/dist-client/modeller/stage-model.d.ts +39 -0
  165. package/dist-client/modeller/stage-model.js +147 -0
  166. package/dist-client/modeller/stage-model.js.map +1 -0
  167. package/dist-client/modeller/thumbnail.d.ts +3 -0
  168. package/dist-client/modeller/thumbnail.js +75 -0
  169. package/dist-client/modeller/thumbnail.js.map +1 -0
  170. package/dist-client/modeller/v3-clips.d.ts +1 -1
  171. package/dist-client/modeller/v3-editor.d.ts +5 -0
  172. package/dist-client/modeller/v3-editor.js +21 -10
  173. package/dist-client/modeller/v3-editor.js.map +1 -1
  174. package/dist-client/modeller/v3-product-session.d.ts +31 -0
  175. package/dist-client/modeller/v3-refusals.d.ts +37 -5
  176. package/dist-client/modeller/v3-refusals.js +59 -16
  177. package/dist-client/modeller/v3-refusals.js.map +1 -1
  178. package/dist-client/pages/figure-list-page.d.ts +1 -1
  179. package/dist-client/pages/figure-list-page.js +4 -4
  180. package/dist-client/pages/figure-list-page.js.map +1 -1
  181. package/dist-client/pages/figure-modeller-page.d.ts +102 -0
  182. package/dist-client/pages/figure-modeller-page.js +548 -0
  183. package/dist-client/pages/figure-modeller-page.js.map +1 -0
  184. package/dist-client/route.d.ts +1 -1
  185. package/dist-client/route.js +1 -5
  186. package/dist-client/route.js.map +1 -1
  187. package/dist-client/tsconfig.tsbuildinfo +1 -1
  188. package/docs/product-cutover.md +25 -0
  189. package/package.json +5 -5
  190. package/test/{v3-ai-proposal.test.ts → ai-proposal.test.ts} +6 -6
  191. package/test/{v3-align.test.ts → align.test.ts} +8 -8
  192. package/test/browser/modeller-page.js +30 -30
  193. package/test/browser/modeller.check.mjs +17 -16
  194. package/test/browser/serve.mjs +1 -1
  195. package/test/browser/thumbnail-page.js +6 -6
  196. package/test/{v3-clips.test.ts → clips.test.ts} +18 -18
  197. package/test/{v3-controls.test.ts → controls.test.ts} +15 -15
  198. package/test/{v3-fields.test.ts → fields.test.ts} +13 -13
  199. package/test/figure-assistant-surface.test.ts +3 -3
  200. package/test/{v3-first-save.test.ts → first-save.test.ts} +10 -10
  201. package/test/fixtures/joint-column.json +2 -2
  202. package/test/fixtures/joint-roller.json +2 -2
  203. package/test/fixtures/robot-arm-6dof.json +2 -2
  204. package/test/{v3-gizmo.test.ts → gizmo.test.ts} +33 -33
  205. package/test/{v3-joint-drag.test.ts → joint-drag.test.ts} +17 -17
  206. package/test/{v3-joint-fan.test.ts → joint-fan.test.ts} +18 -18
  207. package/test/joints.ts +6 -6
  208. package/test/{v3-keys.test.ts → keys.test.ts} +2 -2
  209. package/test/{v3-layout.test.ts → layout.test.ts} +8 -8
  210. package/test/{v3-links.test.ts → links.test.ts} +201 -201
  211. package/test/{v3-live.test.ts → live.test.ts} +18 -18
  212. package/test/{v3-material.test.ts → material.test.ts} +12 -12
  213. package/test/{v3-maturity.test.ts → maturity.test.ts} +74 -74
  214. package/test/modeller-route.test.ts +5 -6
  215. package/test/{v3-palette-icons.test.ts → palette-icons.test.ts} +1 -1
  216. package/test/{v3-parts-wiring.test.ts → parts-wiring.test.ts} +11 -7
  217. package/test/{v3-preview.test.ts → preview.test.ts} +12 -12
  218. package/test/{v3-product-session.test.ts → product-session.test.ts} +17 -17
  219. package/test/{v3-product-write.test.ts → product-write.test.ts} +20 -20
  220. package/test/{v3-properties.test.ts → properties.test.ts} +24 -24
  221. package/test/{v3-recipes.test.ts → recipes.test.ts} +10 -10
  222. package/test/refusals.test.ts +166 -0
  223. package/test/{v3-release-panel.test.ts → release-panel.test.ts} +21 -21
  224. package/test/renderer.test.ts +24 -0
  225. package/test/resolve-ts-siblings.mjs +1 -1
  226. package/test/{v3-scene-canvas.test.ts → scene-canvas.test.ts} +6 -6
  227. package/test/{v3-stage-model.test.ts → stage-model.test.ts} +5 -5
  228. package/test/{v3-theme.test.ts → theme.test.ts} +4 -4
  229. package/test/{v3-thumbnail.test.ts → thumbnail.test.ts} +4 -4
  230. package/test/{v3-verdict.test.ts → verdict.test.ts} +17 -17
  231. package/test/{v3-view.test.ts → view.test.ts} +2 -2
  232. package/things-factory.config.js +2 -3
  233. package/translations/en.json +15 -7
  234. package/translations/ja.json +15 -7
  235. package/translations/ko.json +15 -7
  236. package/translations/ms.json +15 -7
  237. package/translations/zh.json +15 -7
  238. package/docs/v3-product-cutover.md +0 -25
  239. package/test/v3-refusals.test.ts +0 -136
  240. package/test/v3-renderer.test.ts +0 -24
  241. /package/client/modeller/{v3-fields.ts → fields.ts} +0 -0
  242. /package/client/modeller/{v3-first-save.ts → first-save.ts} +0 -0
  243. /package/client/modeller/{v3-help.ts → help.ts} +0 -0
  244. /package/client/modeller/{v3-keys.ts → keys.ts} +0 -0
@@ -1,136 +0,0 @@
1
- /*
2
- * 크기 범위 거절을 사람의 말로 (사용자 2026-09-24: 「what is this warning message means?」 — 커널의 영어 원문이 떴다).
3
- */
4
- import { test } from 'node:test'
5
- import assert from 'node:assert/strict'
6
- import { readFileSync } from 'node:fs'
7
- import { fileURLToPath } from 'node:url'
8
-
9
- import { sizeRangeRefusalOf } from '../client/modeller/v3-refusals.ts'
10
-
11
- const ko = JSON.parse(readFileSync(fileURLToPath(new URL('../translations/ko.json', import.meta.url)), 'utf8'))
12
- const say = (key: string, params: Record<string, string>) => ko[key].replace(/\{(\w+)\}/g, (_: string, k: string) => params[k] ?? '')
13
- const refusal = (message: string) => Object.assign(new Error(message), { code: 'INSTANCE_SIZE_RANGE' })
14
-
15
- test('화성 트레이의 거절 — 축 · 숫자 · 부품 · 고치는 곳', () => {
16
- const said = sizeRangeRefusalOf(refusal('INSTANCE_SIZE_RANGE: size.x: below 8.82353 mm a length goes to zero (cell); declare the smallest size at 8.82353 mm or more'), say)!
17
- assert.ok(said.includes('X 축을') && said.includes('8.8 mm') && said.includes('cell 부품') && said.includes('다시 재기'), said)
18
- assert.ok(!/declare|below/.test(said))
19
- })
20
-
21
- test('위쪽 끝도, 부품 이름이 없어도', () => {
22
- const said = sizeRangeRefusalOf(refusal('INSTANCE_SIZE_RANGE: size.y: above 15000 mm the figure cannot be drawn; declare the largest size at 15000 mm or less'), say)!
23
- assert.ok(said.includes('Y 축을') && said.includes('15,000 mm'), said)
24
- })
25
-
26
- test('다른 거절은 건드리지 않는다', () => {
27
- assert.equal(sizeRangeRefusalOf(Object.assign(new Error('x'), { code: 'EDIT_TARGET' }), say), null)
28
- })
29
-
30
- test('범위가 받을 수 없는 연결은 같은 연결을 범위를 좁혀 다시 권한다 — 다시 재기로는 풀리지 않는다 (사이징, 2026-09-25)', async () => {
31
- const { narrowingRetryOf } = await import('../client/modeller/v3-refusals.ts')
32
- const { addV3Box, applyV3Authoring, createV3Asset } = await import('@hatiolab/figure-model')
33
- let asset = addV3Box(createV3Asset('Panel'), { id: 'panel', size: { x: 400, y: 400, z: 400 }, position: { x: 0, y: 0, z: 0 } })
34
- asset = applyV3Authoring(asset, { kind: 'declare-instance-size', range: { x: { min: 1, max: 4000 }, y: { min: 1, max: 4000 }, z: { min: 1, max: 4000 } }, at: { x: 400, y: 400, z: 400 } })
35
- const link = { kind: 'link-dimension' as const, part: 'panel', axis: 'x' as const, source: { of: 'instance' as const, axis: 'x' as const }, plus: -50 }
36
- let error: unknown
37
- try {
38
- applyV3Authoring(asset, link)
39
- } catch (e) {
40
- error = e
41
- }
42
- const say = (key: string, p: Record<string, string>) => `${key} ${JSON.stringify(p)}`
43
- const offer = narrowingRetryOf(link, error, say)!
44
- assert.ok(offer, '권한다')
45
- assert.match(offer.label, /figure.button.narrow-range-from .*"axis":"X"/)
46
- assert.deepEqual(offer.retry, { ...link, narrowRange: true })
47
- assert.doesNotThrow(() => applyV3Authoring(asset, offer.retry as never), '권한 것은 받아들여진다')
48
- assert.equal(narrowingRetryOf({ kind: 'attach' }, error, say), null, '연결이 아닌 명령에는 권하지 않는다')
49
- })
50
-
51
- test('부품을 못 지우는 까닭을 넷으로 가려 말한다 — 매단 것 · 공간 선언 · 크기를 따르는 것 · 따라 움직이는 것 (2026-09-25)', async () => {
52
- const { partRefusalOf } = await import('../client/modeller/v3-refusals.ts')
53
- const fm = await import('@hatiolab/figure-model')
54
- const say = (key: string, p: Record<string, string>) => `${key} ${JSON.stringify(p)}`
55
- let a = fm.createV3Asset('Bench')
56
- a = fm.addV3Box(a, { id: 'leg', size: { x: 60, y: 700, z: 60 }, position: { x: 0, y: 0, z: 0 } })
57
- a = fm.addV3Box(a, { id: 'top', size: { x: 1200, y: 40, z: 600 }, position: { x: 0, y: 0, z: 0 } })
58
- a = fm.addV3Box(a, { id: 'lamp', size: { x: 80, y: 80, z: 80 }, position: { x: 0, y: 0, z: 0 } })
59
- a = fm.addV3Box(a, { id: 'shade', size: { x: 40, y: 40, z: 40 }, position: { x: 0, y: 0, z: 0 } })
60
- a = fm.applyV3Authoring(a, { kind: 'attach', part: 'leg', face: 'bottom', to: { plane: 'mounting-plane' } })
61
- a = fm.applyV3Authoring(a, { kind: 'attach', part: 'top', face: 'bottom', to: { part: 'leg', face: 'top' } })
62
- a = fm.applyV3Authoring(a, { kind: 'attach', part: 'lamp', face: 'bottom', to: { part: 'top', face: 'top' }, align: { x: { mm: -300 } } })
63
- a = fm.applyV3Authoring(a, { kind: 'attach', part: 'shade', face: 'bottom', to: { part: 'top', face: 'top' }, align: { x: { mm: 300 } } })
64
- const turn = (asset: typeof a, part: string, extra: object) => fm.applyV3Authoring(asset, { kind: 'set-joint', joint: { ...asset.joints!.find(j => j.body1.part === part)!, type: 'revolute', axis: 'Y', ...extra } as never })
65
- a = turn(a, 'lamp', { state: { id: 'lamp-turn' } })
66
- a = turn(a, 'shade', { mimic: { joint: 'lamp', multiplier: 1 } })
67
- const why = (asset: typeof a, part: string) => {
68
- try {
69
- fm.editV3Part(asset, { kind: 'remove-part', target: part })
70
- return 'removed'
71
- } catch (e) {
72
- return partRefusalOf(e, part, say)
73
- }
74
- }
75
- assert.match(why(a, 'top')!, /part-holds-others .*"others":"lamp, shade"/)
76
- assert.match(why(a, 'lamp')!, /part-is-led .*"others":"shade"/)
77
- const declared = fm.applyV3Authoring(a, { kind: 'declare-occupancy', proposal: fm.proposeV3Occupancy(a), placement: 'floor' })
78
- assert.match(why(declared, 'shade')!, /part-in-declared-space .*"part":"shade"/, '잎인데 공간 선언에 들어 있다')
79
- })
80
-
81
- test('공간 선언에 든 부품 — 지우고 공간을 남은 부품에 맞추는 길을 한 번에 권한다(차단기의 떠도는 상자, 09-25)', async () => {
82
- const { inDeclaredRoom } = await import('../client/modeller/v3-refusals.ts')
83
- const fm = await import('@hatiolab/figure-model')
84
- let a = fm.addV3Box(fm.createV3Asset('Gate'), { id: 'housing', size: { x: 300, y: 1000, z: 300 }, position: { x: 0, y: 0, z: 0 } })
85
- a = fm.addV3Box(a, { id: 'stray', size: { x: 100, y: 100, z: 100 }, position: { x: 0, y: 50, z: 0 } })
86
- a = fm.applyV3Authoring(a, { kind: 'attach', part: 'housing', face: 'bottom', to: { plane: 'mounting-plane' } })
87
- a = fm.applyV3Authoring(a, { kind: 'attach', part: 'stray', face: 'bottom', to: { plane: 'mounting-plane' } })
88
- a = fm.applyV3Authoring(a, { kind: 'declare-occupancy', proposal: fm.proposeV3Occupancy(a), placement: 'floor' })
89
- let refused: unknown
90
- try {
91
- fm.editV3Part(a, { kind: 'remove-part', target: 'stray' })
92
- } catch (e) {
93
- refused = e
94
- }
95
- assert.equal(inDeclaredRoom(refused), true)
96
- assert.equal(inDeclaredRoom(Object.assign(new Error('PART_REFERENCED: top: top holds leg; take it off first'), { code: 'PART_REFERENCED' })), false, '매단 것은 이 길이 아니다')
97
- const after = fm.editV3Part(a, { kind: 'remove-part', target: 'stray', refitRoom: true })
98
- assert.equal(after.document.model.nodes.some(n => n.id === 'stray'), false)
99
- assert.ok(after.occupancy?.bounds, '공간은 남은 부품으로 다시 선언된다')
100
- const editor = readFileSync(fileURLToPath(new URL('../client/modeller/v3-editor.ts', import.meta.url)), 'utf8')
101
- assert.ok(editor.includes("if (!refitRoom && inDeclaredRoom(error)) this.retry = { label: i18next.t('figure.button.remove-and-refit-room', { part: id }), run: () => this.removePart(id, true) }"))
102
- assert.ok(ko['figure.button.remove-and-refit-room'].includes('{part}'))
103
- })
104
-
105
- test('배치를 바꿀 수 없는 까닭 — 평면에 붙은 부품과 새 평면이 받는 면을 말한다(벽 배치, 09-25)', async () => {
106
- const { placementRefusalOf } = await import('../client/modeller/v3-refusals.ts')
107
- const fm = await import('@hatiolab/figure-model')
108
- let a = fm.addV3Box(fm.createV3Asset('Post'), { id: 'post', size: { x: 100, y: 1000, z: 100 }, position: { x: 0, y: 0, z: 0 } })
109
- a = fm.applyV3Authoring(a, { kind: 'attach', part: 'post', face: 'bottom', to: { plane: 'mounting-plane' } })
110
- let refused: unknown
111
- try {
112
- fm.applyV3Authoring(a, { kind: 'declare-occupancy', proposal: fm.proposeV3Occupancy(a), placement: 'wall' as never })
113
- } catch (e) {
114
- refused = e
115
- }
116
- const said = placementRefusalOf(refused, say)!
117
- assert.ok(said.includes('post (bottom)') && said.includes('벽 기반') && said.includes('back'), said)
118
- assert.equal(placementRefusalOf(new Error('x'), say), null)
119
- /* The placement list sends its command through the page, not the editor: the page says it in words too, and the list goes back. */
120
- const page = readFileSync(fileURLToPath(new URL('../client/pages/figure-v3-modeller-page.ts', import.meta.url)), 'utf8')
121
- assert.ok(page.includes('placementRefusalOf(error, (key, params) => i18next.t(key, params))'), '페이지 경로도 사람의 말로')
122
- const props = readFileSync(fileURLToPath(new URL('../client/modeller/figure-v3-properties.ts', import.meta.url)), 'utf8')
123
- assert.ok(props.includes('select.value = current'), '거절되면 목록이 원래 배치로 돌아온다')
124
- })
125
-
126
- test('범위 밖 값은 받을 수 있는 범위를 숫자로 말한다 — 커널의 영어가 아니다 (Figure 레인 :3300, 2026-09-26)', async () => {
127
- const { inputRangeRefusalOf } = await import('../client/modeller/v3-refusals.ts')
128
- const range = (message: string) => Object.assign(new Error(message), { code: 'INPUT_RANGE' })
129
- const size = inputRangeRefusalOf(range('INPUT_RANGE: size.x: 6021100 outside [300,1200]'), say)!
130
- assert.ok(size.includes('X') && size.includes('300') && size.includes('1,200') && size.includes('6,021,100'), size)
131
- assert.ok(!/outside|INPUT_RANGE/.test(size))
132
- const other = inputRangeRefusalOf(range('INPUT_RANGE: lift.stroke: -5 outside [0,1]'), say)!
133
- assert.ok(other.includes('0') && other.includes('1') && other.includes('-5'), other)
134
- assert.equal(inputRangeRefusalOf(range('INPUT_RANGE: steps: at the length it is authored (100 mm) a pitch under 1 mm makes more than 50 copies'), say), null)
135
- assert.equal(inputRangeRefusalOf(Object.assign(new Error('x'), { code: 'EDIT_TARGET' }), say), null)
136
- })
@@ -1,24 +0,0 @@
1
- import {test} from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import * as THREE from 'three'
4
- import {replaceV3Meshes} from '../client/modeller/v3-renderer.ts'
5
- import type {V3RenderPart} from '../client/modeller/v3-renderer.ts'
6
- const box=(id:string):V3RenderPart=>({id,shape:{provider:'box',dimensions:{width:100,height:200,depth:300}},pose:{r:[[1,0,0],[0,1,0],[0,0,1]],t:[0,100,0]},material:{token:'primary'}})
7
- test('V3 renderer replaces rather than accumulates; empty scene disposes old resources',()=>{
8
- const group=new THREE.Group();replaceV3Meshes(group,[box('a'),box('b')],{primary:'#123456'})
9
- assert.equal(group.children.length,2);let disposed=0
10
- const first=group.children[0] as THREE.Mesh;first.geometry.addEventListener('dispose',()=>disposed++)
11
- replaceV3Meshes(group,[box('c')],{primary:'#123456'});assert.equal(group.children.length,1);assert.equal(group.children[0].name,'c');assert.equal(disposed,1)
12
- replaceV3Meshes(group,[],{});assert.equal(group.children.length,0)
13
- })
14
- test('V3 renderer preserves scene on unsupported geometry and resolves pose/material',()=>{
15
- const group=new THREE.Group();replaceV3Meshes(group,[box('a')],{primary:'#123456'})
16
- const old=group.children[0],bounds=new THREE.Box3().setFromObject(group)
17
- assert.equal(bounds.min.y,0);assert.equal(bounds.max.y,200)
18
- assert.throws(()=>replaceV3Meshes(group,[box('b'),{...box('c'),shape:{provider:'unknown',dimensions:{}}}],{primary:'#123456'}),/Unsupported/)
19
- assert.equal(group.children[0],old)
20
- const p=box('glow');p.material={token:'primary',preset:'metal',transparent:true,emissive:{token:'primary',intensity:2,on:true}}
21
- replaceV3Meshes(group,[p],{primary:'#123456'})
22
- const material=(group.children[0] as THREE.Mesh).material as THREE.MeshStandardMaterial
23
- assert.equal(material.metalness,0.8);assert.equal(material.transparent,true);assert.equal(material.emissiveIntensity,2)
24
- })
File without changes
File without changes
File without changes