@things-factory/figure-ui 10.1.75 → 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 (235) 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} +117 -117
  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-renderer.ts → renderer.ts} +16 -16
  37. package/client/modeller/{v3-stage-model.ts → stage-model.ts} +15 -15
  38. package/client/modeller/{v3-thumbnail.ts → thumbnail.ts} +8 -8
  39. package/client/pages/figure-list-page.ts +4 -4
  40. package/client/pages/{figure-v3-modeller-page.ts → figure-modeller-page.ts} +37 -37
  41. package/client/route.ts +1 -5
  42. package/dist-client/figure-assistant.js +3 -3
  43. package/dist-client/figure-assistant.js.map +1 -1
  44. package/dist-client/graphql/store.d.ts +3 -0
  45. package/dist-client/graphql/store.js +39 -0
  46. package/dist-client/graphql/store.js.map +1 -0
  47. package/dist-client/modeller/ai-proposal.d.ts +51 -0
  48. package/dist-client/modeller/ai-proposal.js +64 -0
  49. package/dist-client/modeller/ai-proposal.js.map +1 -0
  50. package/dist-client/modeller/align.d.ts +30 -0
  51. package/dist-client/modeller/align.js +75 -0
  52. package/dist-client/modeller/align.js.map +1 -0
  53. package/dist-client/modeller/canvas.d.ts +72 -0
  54. package/dist-client/modeller/canvas.js +379 -0
  55. package/dist-client/modeller/canvas.js.map +1 -0
  56. package/dist-client/modeller/clips.d.ts +39 -0
  57. package/dist-client/modeller/clips.js +58 -0
  58. package/dist-client/modeller/clips.js.map +1 -0
  59. package/dist-client/modeller/controls.d.ts +28 -0
  60. package/dist-client/modeller/controls.js +28 -0
  61. package/dist-client/modeller/controls.js.map +1 -0
  62. package/dist-client/modeller/editor.d.ts +203 -0
  63. package/dist-client/modeller/editor.js +1526 -0
  64. package/dist-client/modeller/editor.js.map +1 -0
  65. package/dist-client/modeller/fields.d.ts +45 -0
  66. package/dist-client/modeller/fields.js +83 -0
  67. package/dist-client/modeller/fields.js.map +1 -0
  68. package/dist-client/modeller/figure-capability-panel.d.ts +22 -0
  69. package/dist-client/modeller/figure-capability-panel.js +141 -0
  70. package/dist-client/modeller/figure-capability-panel.js.map +1 -0
  71. package/dist-client/modeller/figure-clips.d.ts +31 -0
  72. package/dist-client/modeller/figure-clips.js +268 -0
  73. package/dist-client/modeller/figure-clips.js.map +1 -0
  74. package/dist-client/modeller/figure-controls.d.ts +36 -0
  75. package/dist-client/modeller/figure-controls.js +226 -0
  76. package/dist-client/modeller/figure-controls.js.map +1 -0
  77. package/dist-client/modeller/figure-facts.d.ts +35 -0
  78. package/dist-client/modeller/figure-facts.js +55 -0
  79. package/dist-client/modeller/figure-facts.js.map +1 -0
  80. package/dist-client/modeller/figure-first-save.d.ts +20 -0
  81. package/dist-client/modeller/figure-first-save.js +118 -0
  82. package/dist-client/modeller/figure-first-save.js.map +1 -0
  83. package/dist-client/modeller/figure-history-panel.d.ts +1 -1
  84. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  85. package/dist-client/modeller/figure-links.d.ts +77 -0
  86. package/dist-client/modeller/figure-links.js +649 -0
  87. package/dist-client/modeller/figure-links.js.map +1 -0
  88. package/dist-client/modeller/figure-material-panel.d.ts +39 -0
  89. package/dist-client/modeller/figure-material-panel.js +323 -0
  90. package/dist-client/modeller/figure-material-panel.js.map +1 -0
  91. package/dist-client/modeller/figure-parts.d.ts +69 -0
  92. package/dist-client/modeller/figure-parts.js +559 -0
  93. package/dist-client/modeller/figure-parts.js.map +1 -0
  94. package/dist-client/modeller/figure-preview.d.ts +34 -0
  95. package/dist-client/modeller/figure-preview.js +346 -0
  96. package/dist-client/modeller/figure-preview.js.map +1 -0
  97. package/dist-client/modeller/figure-properties.d.ts +45 -0
  98. package/dist-client/modeller/figure-properties.js +239 -0
  99. package/dist-client/modeller/figure-properties.js.map +1 -0
  100. package/dist-client/modeller/figure-recipe-catalog.d.ts +38 -0
  101. package/dist-client/modeller/figure-recipe-catalog.js +399 -0
  102. package/dist-client/modeller/figure-recipe-catalog.js.map +1 -0
  103. package/dist-client/modeller/figure-verdict.d.ts +64 -0
  104. package/dist-client/modeller/figure-verdict.js +787 -0
  105. package/dist-client/modeller/figure-verdict.js.map +1 -0
  106. package/dist-client/modeller/first-save.d.ts +17 -0
  107. package/dist-client/modeller/first-save.js +26 -0
  108. package/dist-client/modeller/first-save.js.map +1 -0
  109. package/dist-client/modeller/gate-findings.d.ts +2 -2
  110. package/dist-client/modeller/gate-findings.js +2 -2
  111. package/dist-client/modeller/gate-findings.js.map +1 -1
  112. package/dist-client/modeller/gizmo.d.ts +72 -0
  113. package/dist-client/modeller/gizmo.js +111 -0
  114. package/dist-client/modeller/gizmo.js.map +1 -0
  115. package/dist-client/modeller/help.d.ts +18 -0
  116. package/dist-client/modeller/help.js +37 -0
  117. package/dist-client/modeller/help.js.map +1 -0
  118. package/dist-client/modeller/joint-drag.d.ts +19 -0
  119. package/dist-client/modeller/joint-drag.js +97 -0
  120. package/dist-client/modeller/joint-drag.js.map +1 -0
  121. package/dist-client/modeller/joint-fan.d.ts +9 -0
  122. package/dist-client/modeller/joint-fan.js +276 -0
  123. package/dist-client/modeller/joint-fan.js.map +1 -0
  124. package/dist-client/modeller/keys.d.ts +22 -0
  125. package/dist-client/modeller/keys.js +26 -0
  126. package/dist-client/modeller/keys.js.map +1 -0
  127. package/dist-client/modeller/links.d.ts +267 -0
  128. package/dist-client/modeller/links.js +480 -0
  129. package/dist-client/modeller/links.js.map +1 -0
  130. package/dist-client/modeller/live.d.ts +37 -0
  131. package/dist-client/modeller/live.js +68 -0
  132. package/dist-client/modeller/live.js.map +1 -0
  133. package/dist-client/modeller/maturity.d.ts +120 -0
  134. package/dist-client/modeller/maturity.js +313 -0
  135. package/dist-client/modeller/maturity.js.map +1 -0
  136. package/dist-client/modeller/modeller-route.d.ts +1 -1
  137. package/dist-client/modeller/modeller-route.js +1 -1
  138. package/dist-client/modeller/modeller-route.js.map +1 -1
  139. package/dist-client/modeller/occupancy.d.ts +18 -0
  140. package/dist-client/modeller/occupancy.js +24 -0
  141. package/dist-client/modeller/occupancy.js.map +1 -0
  142. package/dist-client/modeller/palette.d.ts +7 -0
  143. package/dist-client/modeller/palette.js +46 -0
  144. package/dist-client/modeller/palette.js.map +1 -0
  145. package/dist-client/modeller/preview-model.d.ts +47 -0
  146. package/dist-client/modeller/preview-model.js +68 -0
  147. package/dist-client/modeller/preview-model.js.map +1 -0
  148. package/dist-client/modeller/product-session.d.ts +510 -0
  149. package/dist-client/modeller/product-session.js +157 -0
  150. package/dist-client/modeller/product-session.js.map +1 -0
  151. package/dist-client/modeller/recipe-source.d.ts +8 -0
  152. package/dist-client/modeller/recipe-source.js +14 -0
  153. package/dist-client/modeller/recipe-source.js.map +1 -0
  154. package/dist-client/modeller/recipes.d.ts +37 -0
  155. package/dist-client/modeller/recipes.js +103 -0
  156. package/dist-client/modeller/recipes.js.map +1 -0
  157. package/dist-client/modeller/refusals.d.ts +68 -0
  158. package/dist-client/modeller/refusals.js +142 -0
  159. package/dist-client/modeller/refusals.js.map +1 -0
  160. package/dist-client/modeller/renderer.d.ts +40 -0
  161. package/dist-client/modeller/renderer.js +73 -0
  162. package/dist-client/modeller/renderer.js.map +1 -0
  163. package/dist-client/modeller/stage-model.d.ts +39 -0
  164. package/dist-client/modeller/stage-model.js +147 -0
  165. package/dist-client/modeller/stage-model.js.map +1 -0
  166. package/dist-client/modeller/thumbnail.d.ts +3 -0
  167. package/dist-client/modeller/thumbnail.js +75 -0
  168. package/dist-client/modeller/thumbnail.js.map +1 -0
  169. package/dist-client/pages/figure-list-page.d.ts +1 -1
  170. package/dist-client/pages/figure-list-page.js +4 -4
  171. package/dist-client/pages/figure-list-page.js.map +1 -1
  172. package/dist-client/pages/figure-modeller-page.d.ts +102 -0
  173. package/dist-client/pages/figure-modeller-page.js +548 -0
  174. package/dist-client/pages/figure-modeller-page.js.map +1 -0
  175. package/dist-client/route.d.ts +1 -1
  176. package/dist-client/route.js +1 -5
  177. package/dist-client/route.js.map +1 -1
  178. package/dist-client/tsconfig.tsbuildinfo +1 -1
  179. package/docs/product-cutover.md +25 -0
  180. package/package.json +5 -5
  181. package/test/{v3-ai-proposal.test.ts → ai-proposal.test.ts} +6 -6
  182. package/test/{v3-align.test.ts → align.test.ts} +8 -8
  183. package/test/browser/modeller-page.js +30 -30
  184. package/test/browser/modeller.check.mjs +14 -14
  185. package/test/browser/serve.mjs +1 -1
  186. package/test/browser/thumbnail-page.js +6 -6
  187. package/test/{v3-clips.test.ts → clips.test.ts} +18 -18
  188. package/test/{v3-controls.test.ts → controls.test.ts} +15 -15
  189. package/test/{v3-fields.test.ts → fields.test.ts} +13 -13
  190. package/test/figure-assistant-surface.test.ts +3 -3
  191. package/test/{v3-first-save.test.ts → first-save.test.ts} +10 -10
  192. package/test/fixtures/joint-column.json +2 -2
  193. package/test/fixtures/joint-roller.json +2 -2
  194. package/test/fixtures/robot-arm-6dof.json +2 -2
  195. package/test/{v3-gizmo.test.ts → gizmo.test.ts} +33 -33
  196. package/test/{v3-joint-drag.test.ts → joint-drag.test.ts} +17 -17
  197. package/test/{v3-joint-fan.test.ts → joint-fan.test.ts} +18 -18
  198. package/test/joints.ts +6 -6
  199. package/test/{v3-keys.test.ts → keys.test.ts} +2 -2
  200. package/test/{v3-layout.test.ts → layout.test.ts} +8 -8
  201. package/test/{v3-links.test.ts → links.test.ts} +201 -201
  202. package/test/{v3-live.test.ts → live.test.ts} +18 -18
  203. package/test/{v3-material.test.ts → material.test.ts} +12 -12
  204. package/test/{v3-maturity.test.ts → maturity.test.ts} +74 -74
  205. package/test/modeller-route.test.ts +5 -6
  206. package/test/{v3-palette-icons.test.ts → palette-icons.test.ts} +1 -1
  207. package/test/{v3-parts-wiring.test.ts → parts-wiring.test.ts} +5 -5
  208. package/test/{v3-preview.test.ts → preview.test.ts} +12 -12
  209. package/test/{v3-product-session.test.ts → product-session.test.ts} +17 -17
  210. package/test/{v3-product-write.test.ts → product-write.test.ts} +20 -20
  211. package/test/{v3-properties.test.ts → properties.test.ts} +24 -24
  212. package/test/{v3-recipes.test.ts → recipes.test.ts} +9 -9
  213. package/test/{v3-refusals.test.ts → refusals.test.ts} +43 -43
  214. package/test/{v3-release-panel.test.ts → release-panel.test.ts} +21 -21
  215. package/test/renderer.test.ts +24 -0
  216. package/test/resolve-ts-siblings.mjs +1 -1
  217. package/test/{v3-scene-canvas.test.ts → scene-canvas.test.ts} +6 -6
  218. package/test/{v3-stage-model.test.ts → stage-model.test.ts} +5 -5
  219. package/test/{v3-theme.test.ts → theme.test.ts} +4 -4
  220. package/test/{v3-thumbnail.test.ts → thumbnail.test.ts} +4 -4
  221. package/test/{v3-verdict.test.ts → verdict.test.ts} +17 -17
  222. package/test/{v3-view.test.ts → view.test.ts} +2 -2
  223. package/things-factory.config.js +2 -3
  224. package/translations/en.json +2 -2
  225. package/translations/ja.json +2 -2
  226. package/translations/ko.json +2 -2
  227. package/translations/ms.json +2 -2
  228. package/translations/zh.json +2 -2
  229. package/docs/v3-product-cutover.md +0 -25
  230. package/test/v3-renderer.test.ts +0 -24
  231. /package/client/modeller/{v3-fields.ts → fields.ts} +0 -0
  232. /package/client/modeller/{v3-first-save.ts → first-save.ts} +0 -0
  233. /package/client/modeller/{v3-help.ts → help.ts} +0 -0
  234. /package/client/modeller/{v3-keys.ts → keys.ts} +0 -0
  235. /package/client/modeller/{v3-refusals.ts → refusals.ts} +0 -0
@@ -1,8 +1,8 @@
1
1
  import * as THREE from 'three'
2
- import { buildV3Geometry } from '@hatiolab/things-scene'
2
+ import { buildFigureGeometry } from '@hatiolab/things-scene'
3
3
 
4
- /** Evaluated V3 boundary: graph values are resolved before reaching the renderer. */
5
- export interface V3RenderPart {
4
+ /** Evaluated boundary: graph values are resolved before reaching the renderer. */
5
+ export interface FigureRenderPart {
6
6
  id: string
7
7
  shape: { provider: string; dimensions: Record<string, number>; profile?: Array<{x:number;y:number}> }
8
8
  pose: {r:number[][];t:number[]}
@@ -15,12 +15,12 @@ export interface V3RenderPart {
15
15
  *
16
16
  * Two are left out. A scalar body carries no shape or pose — it is a number the graph produced, not something
17
17
  * on screen. A body the asset hides is evaluated, placed and gated exactly as a shown one, and excluded from
18
- * rendering, from shadows and from pointer picking (V3 designer's ruling 2026-09-22); drawing it would make
18
+ * rendering, from shadows and from pointer picking (designer's ruling 2026-09-22); drawing it would make
19
19
  * "hidden" mean nothing in the modeller while it means something on a board.
20
20
  */
21
- export function drawableV3Parts(geometry: readonly unknown[]): V3RenderPart[] {
21
+ export function drawableFigureParts(geometry: readonly unknown[]): FigureRenderPart[] {
22
22
  return geometry.filter(
23
- (body): body is V3RenderPart =>
23
+ (body): body is FigureRenderPart =>
24
24
  !!body && typeof body === 'object' && 'shape' in body && 'pose' in body && (body as { visible?: boolean }).visible !== false
25
25
  )
26
26
  }
@@ -29,16 +29,16 @@ export function drawableV3Parts(geometry: readonly unknown[]): V3RenderPart[] {
29
29
  * The mesh for one body — things-scene's builder, not a copy of it.
30
30
  *
31
31
  * The modeller kept its own provider switch and it knew four providers; figures use eleven. A body the
32
- * board drew was refused here with "Unsupported V3 provider: roundedBoxXZ" (2026-09-24, the first canvas check of
32
+ * board drew was refused here with "Unsupported provider: roundedBoxXZ" (2026-09-24, the first canvas check of
33
33
  * ADR-0087 step 4). One builder, and the canvas an author looks at shows what a board will show.
34
34
  */
35
- function geometry(part: V3RenderPart): THREE.BufferGeometry {
36
- const built = buildV3Geometry(part.shape as never, part.segments ?? 12) as THREE.BufferGeometry | undefined
37
- if (!built) throw Error('Unsupported V3 provider: ' + part.shape.provider)
35
+ function geometry(part: FigureRenderPart): THREE.BufferGeometry {
36
+ const built = buildFigureGeometry(part.shape as never, part.segments ?? 12) as THREE.BufferGeometry | undefined
37
+ if (!built) throw Error('Unsupported provider: ' + part.shape.provider)
38
38
  return built
39
39
  }
40
40
 
41
- export function disposeV3Meshes(group:THREE.Group):void {
41
+ export function disposeFigureMeshes(group:THREE.Group):void {
42
42
  for(const child of [...group.children]){
43
43
  if(child instanceof THREE.Mesh){child.geometry.dispose();const materials=Array.isArray(child.material)?child.material:[child.material];materials.forEach(m=>m.dispose())}
44
44
  group.remove(child)
@@ -46,15 +46,15 @@ export function disposeV3Meshes(group:THREE.Group):void {
46
46
  }
47
47
 
48
48
  /** Build first, then swap; failed rendering must not erase the previous scene. No old meshes survive a successful swap. */
49
- export function replaceV3Meshes(group:THREE.Group,parts:readonly V3RenderPart[],palette:Record<string,string>):void {
49
+ export function replaceFigureMeshes(group:THREE.Group,parts:readonly FigureRenderPart[],palette:Record<string,string>):void {
50
50
  const staged=new THREE.Group(),ids=new Set<string>()
51
51
  try{
52
52
  for(const part of parts){
53
- if(ids.has(part.id))throw Error('Duplicate V3 geometry ID: '+part.id);ids.add(part.id)
53
+ if(ids.has(part.id))throw Error('Duplicate geometry ID: '+part.id);ids.add(part.id)
54
54
  const color=palette[part.material.token]
55
55
  if(!color||!/^#[0-9a-f]{6}$/i.test(color))throw Error('Unresolved material token: '+part.material.token)
56
56
  const {r,t}=part.pose
57
- if(r.length!==3||r.some(row=>row.length!==3||row.some(x=>!Number.isFinite(x)))||t.length!==3||t.some(x=>!Number.isFinite(x)))throw Error('Invalid V3 pose: '+part.id)
57
+ if(r.length!==3||r.some(row=>row.length!==3||row.some(x=>!Number.isFinite(x)))||t.length!==3||t.some(x=>!Number.isFinite(x)))throw Error('Invalid pose: '+part.id)
58
58
  const emissive=part.material.emissive
59
59
  if(emissive?.on&&!/^#[0-9a-f]{6}$/i.test(palette[emissive.token]??''))throw Error('Unresolved emissive token')
60
60
  const g=geometry(part)
@@ -63,8 +63,8 @@ export function replaceV3Meshes(group:THREE.Group,parts:readonly V3RenderPart[],
63
63
  mesh.matrixAutoUpdate=false;mesh.matrix.set(r[0][0],r[0][1],r[0][2],t[0],r[1][0],r[1][1],r[1][2],t[1],r[2][0],r[2][1],r[2][2],t[2],0,0,0,1)
64
64
  staged.add(mesh)
65
65
  }
66
- }catch(error){disposeV3Meshes(staged);throw error}
67
- disposeV3Meshes(group)
66
+ }catch(error){disposeFigureMeshes(staged);throw error}
67
+ disposeFigureMeshes(group)
68
68
  for(const mesh of [...staged.children])group.add(mesh)
69
69
  group.updateMatrixWorld(true)
70
70
  }
@@ -1,17 +1,17 @@
1
1
  import * as THREE from 'three'
2
- import { buildV3Geometry } from '@hatiolab/things-scene'
2
+ import { buildFigureGeometry } from '@hatiolab/things-scene'
3
3
 
4
- import type { GizmoResult } from './v3-gizmo.js'
5
- import type { V3RenderPart } from './v3-renderer.js'
4
+ import type { GizmoResult } from './gizmo.js'
5
+ import type { FigureRenderPart } from './renderer.js'
6
6
 
7
7
  /*
8
- * V3 bodies <-> scene components, in one place.
8
+ * bodies <-> scene components, in one place.
9
9
  *
10
10
  * The modeller stands on a things-scene board in editing mode: each body figure-model evaluates is a
11
- * `figure-v3-part` component, and what the scene's gizmo writes back comes here to become a change of the part's
11
+ * `figure-part` component, and what the scene's gizmo writes back comes here to become a change of the part's
12
12
  * inputs. The two vocabularies:
13
13
  *
14
- * V3 (asset frame, mm, y up) scene component
14
+ * (asset frame, mm, y up) scene component
15
15
  * ──────────────────────────────── ─────────────────────────────────────────────
16
16
  * body box extent x · y · z width · depth · height
17
17
  * body box centre x left + width/2 - board width/2
@@ -39,11 +39,11 @@ export interface BodyFrame {
39
39
 
40
40
  const round = (n: number) => Math.round(n * 1e6) / 1e6
41
41
 
42
- export function frameOf(part: V3RenderPart): BodyFrame | null {
43
- /* The box things-scene's `figure-v3-part` centres the body on: the same builder, measured the same way. */
42
+ export function frameOf(part: FigureRenderPart): BodyFrame | null {
43
+ /* The box things-scene's `figure-part` centres the body on: the same builder, measured the same way. */
44
44
  let built: THREE.BufferGeometry | undefined
45
45
  try {
46
- built = buildV3Geometry(part.shape as never, part.segments) as THREE.BufferGeometry | undefined
46
+ built = buildFigureGeometry(part.shape as never, part.segments) as THREE.BufferGeometry | undefined
47
47
  } catch {
48
48
  return null
49
49
  }
@@ -58,14 +58,14 @@ export function frameOf(part: V3RenderPart): BodyFrame | null {
58
58
  }
59
59
 
60
60
  /** The colours a body is drawn in, from the palette the modeller resolved (the asset's, over which the theme wins). */
61
- function colours(part: V3RenderPart, palette: Record<string, string>) {
61
+ function colours(part: FigureRenderPart, palette: Record<string, string>) {
62
62
  const m = part.material
63
63
  const glow = m.emissive?.on ? { color: palette[m.emissive.token] ?? palette[m.token] ?? '#9a9a9a', intensity: m.emissive.intensity } : undefined
64
64
  return { color: palette[m.token] ?? '#9a9a9a', preset: m.preset, transparent: !!m.transparent, ...(glow ? { glow } : {}) }
65
65
  }
66
66
 
67
67
  /** The component state that draws a body where figure-model put it. */
68
- export function componentOf(part: V3RenderPart, palette: Record<string, string>, board: Board): Record<string, unknown> | null {
68
+ export function componentOf(part: FigureRenderPart, palette: Record<string, string>, board: Board): Record<string, unknown> | null {
69
69
  const frame = frameOf(part)
70
70
  if (!frame) return null
71
71
  const { centre, extent, rotation, origin } = frame
@@ -75,7 +75,7 @@ export function componentOf(part: V3RenderPart, palette: Record<string, string>,
75
75
  depth = extent.y || 1,
76
76
  height = extent.z || 1
77
77
  return {
78
- type: 'figure-v3-part',
78
+ type: 'figure-part',
79
79
  name: part.id,
80
80
  shape: part.shape,
81
81
  ...(part.segments !== undefined ? { segments: part.segments } : {}),
@@ -99,7 +99,7 @@ const num = (v: unknown, fallback = 0) => (typeof v === 'number' && Number.isFin
99
99
  * order) and how much each of its own axes grew. Only what changed is given; a component the gizmo did not move gives
100
100
  * nothing.
101
101
  */
102
- export function gizmoResultOf(part: V3RenderPart, after: Record<string, unknown>, board: Board): GizmoResult {
102
+ export function gizmoResultOf(part: FigureRenderPart, after: Record<string, unknown>, board: Board): GizmoResult {
103
103
  const frame = frameOf(part)
104
104
  if (!frame) return {}
105
105
  const { centre, extent, rotation, origin } = frame
@@ -130,7 +130,7 @@ export function gizmoResultOf(part: V3RenderPart, after: Record<string, unknown>
130
130
  * A drag as amounts, for a part whose place is worked out (`nudge-part`): how far its origin moved and how far each of
131
131
  * its angles turned (rigid@1's order, degrees). Nothing on an axis that did not change.
132
132
  */
133
- export function nudgeOf(part: V3RenderPart, result: GizmoResult): { by: Partial<Record<'x' | 'y' | 'z', number>>; turn: Partial<Record<'x' | 'y' | 'z', number>> } {
133
+ export function nudgeOf(part: FigureRenderPart, result: GizmoResult): { by: Partial<Record<'x' | 'y' | 'z', number>>; turn: Partial<Record<'x' | 'y' | 'z', number>> } {
134
134
  const axes = ['x', 'y', 'z'] as const
135
135
  const by: Partial<Record<'x' | 'y' | 'z', number>> = {}
136
136
  const turn: Partial<Record<'x' | 'y' | 'z', number>> = {}
@@ -163,7 +163,7 @@ export function changedKeys(current: Record<string, unknown>, next: Record<strin
163
163
  }
164
164
 
165
165
  /** A board wide and deep enough for the figure, with room around it; its size only grows while a figure is edited. */
166
- export function boardFor(parts: readonly V3RenderPart[], at: Board & { depth?: number } = { width: 0, height: 0 }): Board & { depth: number } {
166
+ export function boardFor(parts: readonly FigureRenderPart[], at: Board & { depth?: number } = { width: 0, height: 0 }): Board & { depth: number } {
167
167
  const box = new THREE.Box3()
168
168
  for (const part of parts) {
169
169
  const frame = frameOf(part)
@@ -1,9 +1,9 @@
1
- import './v3-canvas.js'
1
+ import './canvas.js'
2
2
 
3
- import type { V3RenderPart } from './v3-renderer.js'
3
+ import type { FigureRenderPart } from './renderer.js'
4
4
 
5
5
  /**
6
- * 카드에 쓸 그림을 찍는다 — **숨은 자리에 V3 캔버스를 하나 더 세워서.**
6
+ * 카드에 쓸 그림을 찍는다 — **숨은 자리에 캔버스를 하나 더 세워서.**
7
7
  *
8
8
  * ## 왜 보이는 캔버스를 찍지 않나
9
9
  *
@@ -33,7 +33,7 @@ const HEIGHT = 360
33
33
  const PATIENCE = 5000
34
34
 
35
35
  interface Shooter extends HTMLElement {
36
- parts?: V3RenderPart[]
36
+ parts?: FigureRenderPart[]
37
37
  palette?: Record<string, string>
38
38
  context?: string
39
39
  viewOnly?: boolean
@@ -55,8 +55,8 @@ function within<T>(work: Promise<T>, ms: number): Promise<T | undefined> {
55
55
  return Promise.race([work, delay(ms).then(() => undefined)])
56
56
  }
57
57
 
58
- export async function captureV3Thumbnail(
59
- parts: V3RenderPart[],
58
+ export async function captureFigureThumbnail(
59
+ parts: FigureRenderPart[],
60
60
  palette: Record<string, string>
61
61
  ): Promise<string | undefined> {
62
62
  if (!parts.length) return undefined
@@ -66,11 +66,11 @@ export async function captureV3Thumbnail(
66
66
  WebGL context 는 브라우저가 8~16개에서 끊으므로 찍은 뒤 반드시 버린다(아래 finally).
67
67
  */
68
68
  const host = document.createElement('div')
69
- host.setAttribute('figure-v3-thumbnail-stage', '')
69
+ host.setAttribute('figure-thumbnail-stage', '')
70
70
  host.style.cssText = `position: fixed; left: -10000px; top: 0; width: ${WIDTH}px; height: ${HEIGHT}px; pointer-events: none;`
71
71
  document.body.appendChild(host)
72
72
 
73
- const shooter = document.createElement('figure-v3-canvas') as Shooter
73
+ const shooter = document.createElement('figure-canvas') as Shooter
74
74
  shooter.parts = parts
75
75
  shooter.palette = palette
76
76
  shooter.context = 'thumbnail'
@@ -429,11 +429,11 @@ export class FigureListPage extends FigureListPageBase {
429
429
  말이 알려 준다.
430
430
 
431
431
  색칠한 배지로 두지 않는다. 옛 draw call 한도 3 으로 재던 때는 카탈로그 거의 모두가 0 점이라
432
- 붉은 덩어리가 카드마다 섰고 아무것도 가르지 못했다. V3 는 「한 화면 N 대」 목표에 견준
433
- 여유라(`v3ScoreOf`, 2026-09-24) 퍼지지만, 숫자는 조용히 적고 낮은 것에만 **점 하나**를 붙인다.
432
+ 붉은 덩어리가 카드마다 섰고 아무것도 가르지 못했다. 이 모델은 「한 화면 N 대」 목표에 견준
433
+ 여유라(`figureScoreOf`, 2026-09-24) 퍼지지만, 숫자는 조용히 적고 낮은 것에만 **점 하나**를 붙인다.
434
434
 
435
435
  아직 재지 않은 것을 0 으로 보여 주지 않는다 — 0 은 「가볍다」는 뜻이 되어 버린다.
436
- 등급 문자(A~D)도 여기서 만들지 않는다: 경계는 `figure-model` 의 `scoreOf` · `v3ScoreOf` 가 정하고,
436
+ 등급 문자(A~D)도 여기서 만들지 않는다: 경계는 `figure-model` 의 `scoreOf` · `figureScoreOf` 가 정하고,
437
437
  화면이 그것을 베끼면 두 벌이 되어 목록과 저작면이 다른 등급을 말한다.
438
438
  */
439
439
  renderer: (value: number | null) =>
@@ -586,7 +586,7 @@ export class FigureListPage extends FigureListPageBase {
586
586
  return { total: total || 0, records: items || [] }
587
587
  }
588
588
 
589
- /** The modeller that can edit this figure, by its format; a new figure starts in V3 (`modeller-route.ts`). */
589
+ /** The modeller that can edit this figure, by its format; a new figure starts in (`modeller-route.ts`). */
590
590
  private openModeller(figure?: Figure) {
591
591
  navigate(modellerPathOf(figure))
592
592
  }
@@ -3,41 +3,41 @@ import { customElement, state } from 'lit/decorators.js'
3
3
  import { navigate, PageView } from '@operato/shell'
4
4
  import { getPathInfo } from '@operato/utils'
5
5
  import { modellerPathOf } from '../modeller/modeller-route.js'
6
- import { createV3ProductSession } from '../modeller/v3-product-session.js'
7
- import { v3ProductStore } from '../graphql/v3-store.js'
6
+ import { createFigureProductSession } from '../modeller/product-session.js'
7
+ import { figureProductStore } from '../graphql/store.js'
8
8
  import { i18next } from '@operato/i18n'
9
9
  import { fetchFigure, fetchFigureTypeNames, updateFigure } from '../graphql/index.js'
10
- import { availableRecipes, recipeGuide } from '../modeller/v3-recipe-source.js'
11
- import type { RecipeTemplate } from '../modeller/v3-recipes.js'
12
- import { occupancyFromParts } from '../modeller/v3-occupancy.js'
13
- import type { V3AuthoringAction } from '@hatiolab/figure-model'
14
- import '../modeller/v3-editor.js'
10
+ import { availableRecipes, recipeGuide } from '../modeller/recipe-source.js'
11
+ import type { RecipeTemplate } from '../modeller/recipes.js'
12
+ import { occupancyFromParts } from '../modeller/occupancy.js'
13
+ import type { FigureAuthoringAction } from '@hatiolab/figure-model'
14
+ import '../modeller/editor.js'
15
15
  import '@material/web/icon/icon.js'
16
16
  import '../modeller/figure-history-panel.js'
17
- import '../modeller/figure-v3-verdict.js'
18
- import '../modeller/figure-v3-properties.js'
19
- import type { V3BudgetLine } from '../modeller/v3-maturity.js'
17
+ import '../modeller/figure-verdict.js'
18
+ import '../modeller/figure-properties.js'
19
+ import type { FigureBudgetLine } from '../modeller/maturity.js'
20
20
  import { OxPrompt } from '@operato/popup'
21
21
  import { openPopup } from '@operato/layout'
22
- import '../modeller/figure-v3-first-save.js'
22
+ import '../modeller/figure-first-save.js'
23
23
  import { activePalette } from '@hatiolab/things-scene'
24
24
  import {
25
25
  clearAssistantSurface,
26
26
  notifyAssistantSurface,
27
27
  provideAssistantSurface
28
28
  } from '@things-factory/ai-assistant/dist-client/index.js'
29
- import type { FirstSave } from '../modeller/v3-first-save.js'
30
- import { placementRefusalOf } from '../modeller/v3-refusals.js'
31
- import { takePendingNewFigure } from '../modeller/v3-ai-proposal.js'
29
+ import type { FirstSave } from '../modeller/first-save.js'
30
+ import { placementRefusalOf } from '../modeller/refusals.js'
31
+ import { takePendingNewFigure } from '../modeller/ai-proposal.js'
32
32
 
33
33
  /**
34
- * The V3 product page has exactly one authoritative workspace for UI and AI input transactions.
34
+ * The product page has exactly one authoritative workspace for UI and AI input transactions.
35
35
  *
36
- * The one modeller since ADR-0087 step 5 (2026-09-24): it answers to `figure-modeller`, and to `figure-v3-modeller`
36
+ * The one modeller since ADR-0087 step 5 (2026-09-24): it answers to `figure-modeller`, and to `figure-modeller`
37
37
  * as an alias for links made before the switch.
38
38
  */
39
- @customElement('figure-v3-modeller-page')
40
- export class FigureV3ModellerPage extends PageView {
39
+ @customElement('figure-modeller-page')
40
+ export class FigureModellerPage extends PageView {
41
41
  static styles = css`
42
42
  :host {
43
43
  display: flex;
@@ -50,7 +50,7 @@ export class FigureV3ModellerPage extends PageView {
50
50
  */
51
51
  overflow: hidden;
52
52
  }
53
- figure-v3-editor {
53
+ figure-editor {
54
54
  flex: 1;
55
55
  min-width: 0;
56
56
  min-height: 0;
@@ -92,7 +92,7 @@ export class FigureV3ModellerPage extends PageView {
92
92
  color: var(--md-sys-color-error);
93
93
  }
94
94
  `
95
- private readonly session = createV3ProductSession(v3ProductStore)
95
+ private readonly session = createFigureProductSession(figureProductStore)
96
96
  private routeId: string | undefined
97
97
  private discardApproved = false
98
98
  @state() private revision = 0
@@ -107,7 +107,7 @@ export class FigureV3ModellerPage extends PageView {
107
107
  * 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.
108
108
  *
109
109
  * 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서
110
- * `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 V3 도형은 공간을 선언하기 전에는 전부
110
+ * `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 도형은 공간을 선언하기 전에는 전부
111
111
  * 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.
112
112
  */
113
113
  @state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {
@@ -118,7 +118,7 @@ export class FigureV3ModellerPage extends PageView {
118
118
  @state() private commandError = ''
119
119
  /** The verdict's count, handed to the editor's verdict tab. */
120
120
  @state() private instances?: number
121
- @state() private budget?: V3BudgetLine
121
+ @state() private budget?: FigureBudgetLine
122
122
  /**
123
123
  * The source the verdict judges. The session refuses to export while it loads or saves, so the last one read is
124
124
  * kept rather than blanked — the verdict would otherwise flicker to 「waiting」 on every save.
@@ -219,8 +219,8 @@ export class FigureV3ModellerPage extends PageView {
219
219
  /**
220
220
  * 타입을 정한다 — 보드가 도형을 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).
221
221
  *
222
- * 새 V3 도형은 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.
223
- * 행만 바뀐다 — V3 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.
222
+ * 새 도형은 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.
223
+ * 행만 바뀐다 — 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.
224
224
  * 서버의 거절(이미 쓰는 타입 · 발행 뒤)은 그 문장 그대로 칸 아래에 둔다.
225
225
  */
226
226
  private renameType = async (event: Event) => {
@@ -253,14 +253,14 @@ export class FigureV3ModellerPage extends PageView {
253
253
  }
254
254
  /** A command from the properties tab — rename, declare the room, the mounting face. */
255
255
  private onCommand = (event: Event) => {
256
- const action = (event as CustomEvent).detail?.action as V3AuthoringAction | undefined
256
+ const action = (event as CustomEvent).detail?.action as FigureAuthoringAction | undefined
257
257
  if (action) this.runCommand(() => action)
258
258
  }
259
259
  /**
260
260
  * One step of the session's history, whichever part of the side asked for it — so undo takes it back like any
261
261
  * other edit, and it is saved with the figure.
262
262
  */
263
- private runCommand(actionOf: () => V3AuthoringAction) {
263
+ private runCommand(actionOf: () => FigureAuthoringAction) {
264
264
  try {
265
265
  this.session.command(actionOf())
266
266
  this.commandError = ''
@@ -348,12 +348,12 @@ export class FigureV3ModellerPage extends PageView {
348
348
  }
349
349
  connectedCallback() {
350
350
  super.connectedCallback()
351
- window.addEventListener('figure-v3-apply', this.applyAI)
351
+ window.addEventListener('figure-apply', this.applyAI)
352
352
  window.addEventListener('beforeunload', this.holdUnsaved)
353
353
  }
354
354
  disconnectedCallback() {
355
355
  this.withdrawAssistant()
356
- window.removeEventListener('figure-v3-apply', this.applyAI)
356
+ window.removeEventListener('figure-apply', this.applyAI)
357
357
  window.removeEventListener('beforeunload', this.holdUnsaved)
358
358
  super.disconnectedCallback()
359
359
  }
@@ -429,7 +429,7 @@ export class FigureV3ModellerPage extends PageView {
429
429
  /** The first save's question, over the names this domain already uses. */
430
430
  private askFirstSave = async (name: string): Promise<FirstSave | undefined> => {
431
431
  const taken = await fetchFigureTypeNames().catch(() => [] as string[])
432
- return openPopup<FirstSave>(html`<figure-v3-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-v3-first-save>`, {
432
+ return openPopup<FirstSave>(html`<figure-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-first-save>`, {
433
433
  backdrop: true,
434
434
  size: 'small',
435
435
  title: i18next.t('figure.title.save-new-figure')
@@ -450,16 +450,16 @@ export class FigureV3ModellerPage extends PageView {
450
450
  render() {
451
451
  if (this.error) return html`<p role="alert" class="error">${this.error}</p>`
452
452
  const view = this.session.read()
453
- return html`<figure-v3-editor .session=${this.session} .templates=${this.templates} .guideOf=${this.guideOf} .thumbnailOf=${this.thumbnailOf} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} @v3-changed=${this.changed}>
453
+ return html`<figure-editor .session=${this.session} .templates=${this.templates} .guideOf=${this.guideOf} .thumbnailOf=${this.thumbnailOf} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} @changed=${this.changed}>
454
454
  ${this.commandError ? html`<p slot="notice" fit-error role="alert">${this.commandError}</p>` : ''}
455
- <figure-v3-properties
455
+ <figure-properties
456
456
  slot="figure"
457
457
  .source=${this.draftSource()}
458
458
  .figureId=${view.id}
459
459
  .figureCategory=${this.row.category}
460
460
  .figureTags=${this.row.tags}
461
- @v3-command=${this.onCommand}
462
- @v3-catalog=${this.onCatalog}
461
+ @command=${this.onCommand}
462
+ @catalog=${this.onCatalog}
463
463
  >
464
464
  <label slot="type" type-field>
465
465
  <span>${i18next.t('figure.label.figure-type')}</span>
@@ -472,8 +472,8 @@ export class FigureV3ModellerPage extends PageView {
472
472
  />`}
473
473
  ${this.typeError ? html`<small role="alert">${this.typeError}</small>` : ''}
474
474
  </label>
475
- </figure-v3-properties>
476
- <figure-v3-verdict
475
+ </figure-properties>
476
+ <figure-verdict
477
477
  slot="verdict"
478
478
  .source=${this.draftSource()}
479
479
  .figureId=${view.id}
@@ -483,7 +483,7 @@ export class FigureV3ModellerPage extends PageView {
483
483
  this.budget = event.detail.budget
484
484
  }}
485
485
  @fit-base-to-parts=${this.fitToParts}
486
- ></figure-v3-verdict>
486
+ ></figure-verdict>
487
487
  <figure-history-panel
488
488
  slot="history"
489
489
  .figureId=${view.id}
@@ -495,7 +495,7 @@ export class FigureV3ModellerPage extends PageView {
495
495
  @figure-released=${this.released}
496
496
  @figure-reverted=${this.reverted}
497
497
  ></figure-history-panel>
498
- </figure-v3-editor>`
498
+ </figure-editor>`
499
499
  }
500
500
 
501
501
  }
package/client/route.ts CHANGED
@@ -4,12 +4,8 @@ export default function route(page: string) {
4
4
  import('./pages/figure-list-page')
5
5
  return page
6
6
 
7
- /*
8
- One modeller since ADR-0087 step 5. `figure-v3-modeller` is kept as an alias for links made before the switch.
9
- */
10
7
  case 'figure-modeller':
11
- case 'figure-v3-modeller':
12
- import('./pages/figure-v3-modeller-page')
8
+ import('./pages/figure-modeller-page')
13
9
  return page
14
10
  }
15
11
  }
@@ -4,18 +4,18 @@
4
4
  *
5
5
  * A proposal is a list of the modeller's own commands (ADR-0094). Running it applies them in the modeller as one step
6
6
  * of its history: a revision on the figure open there, at the version the model saw; a new figure on a new figure the
7
- * modeller opens. The outcome is said in words on every branch (`v3-ai-proposal.ts`).
7
+ * modeller opens. The outcome is said in words on every branch (`ai-proposal.ts`).
8
8
  */
9
9
  import { i18next } from '@operato/i18n';
10
10
  import { navigate } from '@operato/shell';
11
11
  import { registerProposalHandler } from '@things-factory/ai-assistant/dist-client/index.js';
12
12
  import { modellerPathOf } from './modeller/modeller-route.js';
13
- import { pendNewFigure, runFigureProposal } from './modeller/v3-ai-proposal.js';
13
+ import { pendNewFigure, runFigureProposal } from './modeller/ai-proposal.js';
14
14
  export const FIGURE_CANDIDATE_TOOL = 'proposeFigureCandidate';
15
15
  registerProposalHandler(FIGURE_CANDIDATE_TOOL, async (proposal, context) => {
16
16
  const outcome = await runFigureProposal(proposal, {
17
17
  offer(detail) {
18
- window.dispatchEvent(new CustomEvent('figure-v3-apply', { detail }));
18
+ window.dispatchEvent(new CustomEvent('figure-apply', { detail }));
19
19
  return detail;
20
20
  },
21
21
  openNew(steps) {
@@ -1 +1 @@
1
- {"version":3,"file":"figure-assistant.js","sourceRoot":"","sources":["../client/figure-assistant.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,mDAAmD,CAAA;AAE3F,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAA0B,MAAM,8BAA8B,CAAA;AAEvG,MAAM,CAAC,MAAM,qBAAqB,GAAG,wBAAwB,CAAA;AAE7D,uBAAuB,CAAC,qBAAqB,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IACzE,MAAM,OAAO,GAAG,MAAM,iBAAiB,CACrC,QAAQ,EACR;QACE,KAAK,CAAC,MAAyB;YAC7B,MAAM,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;YACpE,OAAO,MAAM,CAAA;QACf,CAAC;QACD,OAAO,CAAC,KAAK;YACX,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAA;YACnC,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAA;YAC1B,OAAO,MAAM,CAAA;QACf,CAAC;KACF,EACD,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CACxC,CAAA;IACD,IAAI,OAAO,CAAC,IAAI;QAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACjG,OAAO,OAAO,CAAA;AAChB,CAAC,CAAC,CAAA","sourcesContent":["/*\n * What the AI dock does with a figure proposal - figure-ui declares it because figure-service declares the tool\n * (`proposeFigureCandidate`, design/ai/shared-dock.md §4).\n *\n * A proposal is a list of the modeller's own commands (ADR-0094). Running it applies them in the modeller as one step\n * of its history: a revision on the figure open there, at the version the model saw; a new figure on a new figure the\n * modeller opens. The outcome is said in words on every branch (`v3-ai-proposal.ts`).\n */\nimport { i18next } from '@operato/i18n'\nimport { navigate } from '@operato/shell'\nimport { registerProposalHandler } from '@things-factory/ai-assistant/dist-client/index.js'\n\nimport { modellerPathOf } from './modeller/modeller-route.js'\nimport { pendNewFigure, runFigureProposal, type FigureApplyDetail } from './modeller/v3-ai-proposal.js'\n\nexport const FIGURE_CANDIDATE_TOOL = 'proposeFigureCandidate'\n\nregisterProposalHandler(FIGURE_CANDIDATE_TOOL, async (proposal, context) => {\n const outcome = await runFigureProposal(\n proposal,\n {\n offer(detail: FigureApplyDetail) {\n window.dispatchEvent(new CustomEvent('figure-v3-apply', { detail }))\n return detail\n },\n openNew(steps) {\n const opened = pendNewFigure(steps)\n navigate(modellerPathOf())\n return opened\n }\n },\n (key, params) => i18next.t(key, params)\n )\n if (outcome.note) context.notify(outcome.verdict === 'accepted' ? 'info' : 'error', outcome.note)\n return outcome\n})\n"]}
1
+ {"version":3,"file":"figure-assistant.js","sourceRoot":"","sources":["../client/figure-assistant.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,mDAAmD,CAAA;AAE3F,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAA0B,MAAM,2BAA2B,CAAA;AAEpG,MAAM,CAAC,MAAM,qBAAqB,GAAG,wBAAwB,CAAA;AAE7D,uBAAuB,CAAC,qBAAqB,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IACzE,MAAM,OAAO,GAAG,MAAM,iBAAiB,CACrC,QAAQ,EACR;QACE,KAAK,CAAC,MAAyB;YAC7B,MAAM,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;YACjE,OAAO,MAAM,CAAA;QACf,CAAC;QACD,OAAO,CAAC,KAAK;YACX,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAA;YACnC,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAA;YAC1B,OAAO,MAAM,CAAA;QACf,CAAC;KACF,EACD,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CACxC,CAAA;IACD,IAAI,OAAO,CAAC,IAAI;QAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACjG,OAAO,OAAO,CAAA;AAChB,CAAC,CAAC,CAAA","sourcesContent":["/*\n * What the AI dock does with a figure proposal - figure-ui declares it because figure-service declares the tool\n * (`proposeFigureCandidate`, design/ai/shared-dock.md §4).\n *\n * A proposal is a list of the modeller's own commands (ADR-0094). Running it applies them in the modeller as one step\n * of its history: a revision on the figure open there, at the version the model saw; a new figure on a new figure the\n * modeller opens. The outcome is said in words on every branch (`ai-proposal.ts`).\n */\nimport { i18next } from '@operato/i18n'\nimport { navigate } from '@operato/shell'\nimport { registerProposalHandler } from '@things-factory/ai-assistant/dist-client/index.js'\n\nimport { modellerPathOf } from './modeller/modeller-route.js'\nimport { pendNewFigure, runFigureProposal, type FigureApplyDetail } from './modeller/ai-proposal.js'\n\nexport const FIGURE_CANDIDATE_TOOL = 'proposeFigureCandidate'\n\nregisterProposalHandler(FIGURE_CANDIDATE_TOOL, async (proposal, context) => {\n const outcome = await runFigureProposal(\n proposal,\n {\n offer(detail: FigureApplyDetail) {\n window.dispatchEvent(new CustomEvent('figure-apply', { detail }))\n return detail\n },\n openNew(steps) {\n const opened = pendNewFigure(steps)\n navigate(modellerPathOf())\n return opened\n }\n },\n (key, params) => i18next.t(key, params)\n )\n if (outcome.note) context.notify(outcome.verdict === 'accepted' ? 'info' : 'error', outcome.note)\n return outcome\n})\n"]}
@@ -0,0 +1,3 @@
1
+ import type { FigureProductStore } from '../modeller/product-session.js';
2
+ /** source is sent intact. No conversion into the old parts/base authoring representation. */
3
+ export declare const figureProductStore: FigureProductStore;
@@ -0,0 +1,39 @@
1
+ import gql from 'graphql-tag';
2
+ import { client } from '@operato/graphql';
3
+ import { fetchFigure } from './index.js';
4
+ /** source is sent intact. No conversion into the old parts/base authoring representation. */
5
+ export const figureProductStore = {
6
+ async load(id) {
7
+ const record = await fetchFigure(id);
8
+ if (!record?.source)
9
+ throw Error('Figure 정본이 없습니다.');
10
+ return { id: record.id, source: record.source };
11
+ },
12
+ async save(request) {
13
+ const response = await client.mutate({
14
+ mutation: gql `
15
+ mutation SaveFigure($source: String!, $id: String, $expectedSource: String, $creationId: String, $thumbnail: String, $type: String) {
16
+ saveFigure(
17
+ source: $source
18
+ id: $id
19
+ expectedSource: $expectedSource
20
+ creationId: $creationId
21
+ thumbnail: $thumbnail
22
+ type: $type
23
+ ) {
24
+ id
25
+ source
26
+ }
27
+ }
28
+ `,
29
+ variables: request
30
+ });
31
+ if (response.errors?.length)
32
+ throw Error(response.errors.map(e => e.message).join('\n'));
33
+ const result = response.data?.saveFigure;
34
+ if (!result?.id || !result.source)
35
+ throw Error('서버가 저장 결과를 돌려주지 않았습니다.');
36
+ return result;
37
+ }
38
+ };
39
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../client/graphql/store.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGxC,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAuB;IACpD,KAAK,CAAC,IAAI,CAAC,EAAE;QACX,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,MAAM,EAAE,MAAM;YAAE,MAAM,KAAK,CAAC,kBAAkB,CAAC,CAAA;QACpD,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;IACjD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,OAAO;QAChB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;;;;;;;;OAcZ;YACD,SAAS,EAAE,OAAO;SACnB,CAAC,CAAA;QACF,IAAI,QAAQ,CAAC,MAAM,EAAE,MAAM;YAAE,MAAM,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACxF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,UAA6C,CAAA;QAC3E,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,MAAM,KAAK,CAAC,wBAAwB,CAAC,CAAA;QACxE,OAAO,MAAM,CAAA;IACf,CAAC;CACF,CAAA","sourcesContent":["import gql from 'graphql-tag'\nimport { client } from '@operato/graphql'\nimport { fetchFigure } from './index.js'\nimport type { FigureProductStore, FigureProductRecord } from '../modeller/product-session.js'\n\n/** source is sent intact. No conversion into the old parts/base authoring representation. */\nexport const figureProductStore: FigureProductStore = {\n async load(id) {\n const record = await fetchFigure(id)\n if (!record?.source) throw Error('Figure 정본이 없습니다.')\n return { id: record.id, source: record.source }\n },\n async save(request) {\n const response = await client.mutate({\n mutation: gql`\n mutation SaveFigure($source: String!, $id: String, $expectedSource: String, $creationId: String, $thumbnail: String, $type: String) {\n saveFigure(\n source: $source\n id: $id\n expectedSource: $expectedSource\n creationId: $creationId\n thumbnail: $thumbnail\n type: $type\n ) {\n id\n source\n }\n }\n `,\n variables: request\n })\n if (response.errors?.length) throw Error(response.errors.map(e => e.message).join('\\n'))\n const result = response.data?.saveFigure as FigureProductRecord | undefined\n if (!result?.id || !result.source) throw Error('서버가 저장 결과를 돌려주지 않았습니다.')\n return result\n }\n}\n"]}
@@ -0,0 +1,51 @@
1
+ import type { FigureProposalStep } from '@hatiolab/figure-model';
2
+ /** What the card carries, as `proposeFigureCandidate` returned it. */
3
+ export interface FigureProposalCard {
4
+ steps?: FigureProposalStep[];
5
+ create?: boolean;
6
+ figureId?: string;
7
+ context?: string;
8
+ revision?: number;
9
+ }
10
+ /** The `figure-apply` event's detail. The page that takes it sets `applied`, and `error` when refused. */
11
+ export interface FigureApplyDetail {
12
+ steps: FigureProposalStep[];
13
+ create: boolean;
14
+ context?: string;
15
+ revision?: number;
16
+ applied?: boolean;
17
+ error?: string;
18
+ code?: string;
19
+ }
20
+ export type Say = (key: string, params?: Record<string, string>) => string;
21
+ export interface FigureProposalTarget {
22
+ /** Offers the proposal to a modeller on screen. It comes back untouched when none took it. */
23
+ offer(detail: FigureApplyDetail): FigureApplyDetail;
24
+ /** Opens a new figure in the modeller and applies the steps there; resolves once the page has, or has not. */
25
+ openNew(steps: FigureProposalStep[]): Promise<{
26
+ applied: boolean;
27
+ error?: string;
28
+ code?: string;
29
+ }>;
30
+ }
31
+ export interface FigureProposalOutcome {
32
+ verdict: 'accepted' | 'refused' | 'unavailable';
33
+ errorCode?: string;
34
+ applied?: number;
35
+ note?: string;
36
+ }
37
+ export declare function runFigureProposal(card: FigureProposalCard, target: FigureProposalTarget, say: Say): Promise<FigureProposalOutcome>;
38
+ export declare function pendNewFigure(steps: FigureProposalStep[], ms?: number): Promise<{
39
+ applied: boolean;
40
+ error?: string;
41
+ code?: string;
42
+ }>;
43
+ /** For the page, on a new figure it has just started: the steps waiting for it, and where to say how it went. */
44
+ export declare function takePendingNewFigure(): {
45
+ steps: FigureProposalStep[];
46
+ settle: (result: {
47
+ applied: boolean;
48
+ error?: string;
49
+ code?: string;
50
+ }) => void;
51
+ } | undefined;