@things-factory/figure-ui 10.1.81 → 10.1.82

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 (145) hide show
  1. package/client/figure-assistant.ts +2 -2
  2. package/client/graphql/index.ts +99 -10
  3. package/client/graphql/reached.ts +25 -0
  4. package/client/graphql/store.ts +27 -4
  5. package/client/modeller/ai-proposal.ts +74 -9
  6. package/client/modeller/align.ts +2 -3
  7. package/client/modeller/canvas.ts +18 -5
  8. package/client/modeller/command-preview.ts +113 -0
  9. package/client/modeller/editor.ts +826 -54
  10. package/client/modeller/fields.ts +12 -4
  11. package/client/modeller/figure-facts.ts +2 -2
  12. package/client/modeller/figure-first-save.ts +36 -14
  13. package/client/modeller/figure-history-panel.ts +133 -14
  14. package/client/modeller/figure-links.ts +385 -68
  15. package/client/modeller/figure-parts.ts +56 -2
  16. package/client/modeller/figure-preview.ts +19 -1
  17. package/client/modeller/figure-properties.ts +3 -3
  18. package/client/modeller/figure-revert-choice.ts +49 -0
  19. package/client/modeller/first-save.ts +16 -0
  20. package/client/modeller/gizmo.ts +0 -16
  21. package/client/modeller/links.ts +142 -15
  22. package/client/modeller/motion-ends.ts +83 -0
  23. package/client/modeller/occupancy.ts +28 -2
  24. package/client/modeller/product-session.ts +154 -18
  25. package/client/modeller/refusals.ts +15 -0
  26. package/client/modeller/revert-request.ts +26 -0
  27. package/client/modeller/side-panel-styles.ts +12 -0
  28. package/client/modeller/stage-model.ts +25 -3
  29. package/client/pages/figure-modeller-page.ts +68 -29
  30. package/dist-client/figure-assistant.js +2 -2
  31. package/dist-client/figure-assistant.js.map +1 -1
  32. package/dist-client/graphql/index.d.ts +39 -2
  33. package/dist-client/graphql/index.js +63 -9
  34. package/dist-client/graphql/index.js.map +1 -1
  35. package/dist-client/graphql/reached.d.ts +10 -0
  36. package/dist-client/graphql/reached.js +28 -0
  37. package/dist-client/graphql/reached.js.map +1 -0
  38. package/dist-client/graphql/store.js +27 -4
  39. package/dist-client/graphql/store.js.map +1 -1
  40. package/dist-client/modeller/ai-proposal.d.ts +31 -2
  41. package/dist-client/modeller/ai-proposal.js +65 -6
  42. package/dist-client/modeller/ai-proposal.js.map +1 -1
  43. package/dist-client/modeller/align.d.ts +1 -1
  44. package/dist-client/modeller/align.js +0 -2
  45. package/dist-client/modeller/align.js.map +1 -1
  46. package/dist-client/modeller/canvas.d.ts +11 -0
  47. package/dist-client/modeller/canvas.js +33 -4
  48. package/dist-client/modeller/canvas.js.map +1 -1
  49. package/dist-client/modeller/clips.d.ts +1 -1
  50. package/dist-client/modeller/command-preview.d.ts +35 -0
  51. package/dist-client/modeller/command-preview.js +95 -0
  52. package/dist-client/modeller/command-preview.js.map +1 -0
  53. package/dist-client/modeller/editor.d.ts +113 -1
  54. package/dist-client/modeller/editor.js +850 -50
  55. package/dist-client/modeller/editor.js.map +1 -1
  56. package/dist-client/modeller/fields.d.ts +3 -2
  57. package/dist-client/modeller/fields.js +10 -3
  58. package/dist-client/modeller/fields.js.map +1 -1
  59. package/dist-client/modeller/figure-facts.js +2 -2
  60. package/dist-client/modeller/figure-facts.js.map +1 -1
  61. package/dist-client/modeller/figure-first-save.d.ts +3 -4
  62. package/dist-client/modeller/figure-first-save.js +41 -14
  63. package/dist-client/modeller/figure-first-save.js.map +1 -1
  64. package/dist-client/modeller/figure-history-panel.d.ts +31 -0
  65. package/dist-client/modeller/figure-history-panel.js +130 -14
  66. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  67. package/dist-client/modeller/figure-links.d.ts +51 -1
  68. package/dist-client/modeller/figure-links.js +392 -72
  69. package/dist-client/modeller/figure-links.js.map +1 -1
  70. package/dist-client/modeller/figure-parts.d.ts +7 -0
  71. package/dist-client/modeller/figure-parts.js +55 -2
  72. package/dist-client/modeller/figure-parts.js.map +1 -1
  73. package/dist-client/modeller/figure-preview.d.ts +6 -0
  74. package/dist-client/modeller/figure-preview.js +22 -1
  75. package/dist-client/modeller/figure-preview.js.map +1 -1
  76. package/dist-client/modeller/figure-properties.js +3 -3
  77. package/dist-client/modeller/figure-properties.js.map +1 -1
  78. package/dist-client/modeller/figure-revert-choice.d.ts +17 -0
  79. package/dist-client/modeller/figure-revert-choice.js +59 -0
  80. package/dist-client/modeller/figure-revert-choice.js.map +1 -0
  81. package/dist-client/modeller/first-save.d.ts +7 -0
  82. package/dist-client/modeller/first-save.js +17 -0
  83. package/dist-client/modeller/first-save.js.map +1 -1
  84. package/dist-client/modeller/gizmo.d.ts +0 -6
  85. package/dist-client/modeller/gizmo.js +0 -18
  86. package/dist-client/modeller/gizmo.js.map +1 -1
  87. package/dist-client/modeller/links.d.ts +49 -4
  88. package/dist-client/modeller/links.js +141 -15
  89. package/dist-client/modeller/links.js.map +1 -1
  90. package/dist-client/modeller/motion-ends.d.ts +12 -0
  91. package/dist-client/modeller/motion-ends.js +65 -0
  92. package/dist-client/modeller/motion-ends.js.map +1 -0
  93. package/dist-client/modeller/occupancy.d.ts +14 -1
  94. package/dist-client/modeller/occupancy.js +14 -1
  95. package/dist-client/modeller/occupancy.js.map +1 -1
  96. package/dist-client/modeller/product-session.d.ts +43 -2
  97. package/dist-client/modeller/product-session.js +146 -12
  98. package/dist-client/modeller/product-session.js.map +1 -1
  99. package/dist-client/modeller/refusals.d.ts +13 -0
  100. package/dist-client/modeller/refusals.js +16 -0
  101. package/dist-client/modeller/refusals.js.map +1 -1
  102. package/dist-client/modeller/revert-request.d.ts +16 -0
  103. package/dist-client/modeller/revert-request.js +11 -0
  104. package/dist-client/modeller/revert-request.js.map +1 -0
  105. package/dist-client/modeller/side-panel-styles.js +12 -0
  106. package/dist-client/modeller/side-panel-styles.js.map +1 -1
  107. package/dist-client/modeller/stage-model.d.ts +18 -2
  108. package/dist-client/modeller/stage-model.js +21 -3
  109. package/dist-client/modeller/stage-model.js.map +1 -1
  110. package/dist-client/pages/figure-modeller-page.d.ts +8 -1
  111. package/dist-client/pages/figure-modeller-page.js +72 -34
  112. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  113. package/dist-client/route.d.ts +1 -1
  114. package/dist-client/tsconfig.tsbuildinfo +1 -1
  115. package/package.json +5 -5
  116. package/test/ai-proposal.test.ts +2 -2
  117. package/test/align.test.ts +50 -5
  118. package/test/browser/acceptance-page.js +187 -0
  119. package/test/browser/build.mjs +5 -1
  120. package/test/browser/revert-page.js +47 -0
  121. package/test/browser/serve-acceptance.mjs +85 -0
  122. package/test/browser/serve-revert.mjs +119 -0
  123. package/test/clip-ai-proposal.test.ts +104 -0
  124. package/test/clip-consent.test.ts +88 -0
  125. package/test/command-preview.test.ts +288 -0
  126. package/test/fields.test.ts +4 -3
  127. package/test/figure-assistant-surface.test.ts +4 -1
  128. package/test/first-minute.test.ts +96 -0
  129. package/test/first-save-suggest.test.ts +42 -0
  130. package/test/gizmo.test.ts +20 -16
  131. package/test/joint-fan.test.ts +2 -1
  132. package/test/layout.test.ts +9 -2
  133. package/test/links.test.ts +7 -4
  134. package/test/proposal-record.test.ts +126 -0
  135. package/test/release-entry.test.ts +28 -0
  136. package/test/release-panel.test.ts +3 -2
  137. package/test/resize-with-placement.test.ts +225 -0
  138. package/test/revert-choice.test.ts +39 -0
  139. package/test/revert-keeping.test.ts +102 -0
  140. package/test/save-landed.test.ts +198 -0
  141. package/translations/en.json +222 -10
  142. package/translations/ja.json +223 -11
  143. package/translations/ko.json +223 -11
  144. package/translations/ms.json +222 -10
  145. package/translations/zh.json +223 -11
@@ -26,8 +26,13 @@ function colours(part, palette) {
26
26
  const glow = m.emissive?.on ? { color: palette[m.emissive.token] ?? palette[m.token] ?? '#9a9a9a', intensity: m.emissive.intensity } : undefined;
27
27
  return { color: palette[m.token] ?? '#9a9a9a', preset: m.preset, transparent: !!m.transparent, ...(glow ? { glow } : {}) };
28
28
  }
29
- /** The component state that draws a body where figure-model put it. */
30
- export function componentOf(part, palette, board) {
29
+ /**
30
+ * The component state that draws a body where figure-model put it. With `anchor` -- figure-model's
31
+ * `figureScaleAnchorOf` for its part -- the scene's scale gizmo keeps that face while it is dragged, the face the model
32
+ * keeps when the drag is written; without it the gizmo keeps the face across from the handle held, and the part jumped
33
+ * on release (designer 2026-09-27). Body x · y · z are the component's width · depth · height, as the scene's axes are.
34
+ */
35
+ export function componentOf(part, palette, board, anchor) {
31
36
  const frame = frameOf(part);
32
37
  if (!frame)
33
38
  return null;
@@ -49,7 +54,8 @@ export function componentOf(part, palette, board) {
49
54
  zPos: round(world.y - depth / 2),
50
55
  rotationX: round(euler.x),
51
56
  rotation: round(-euler.y),
52
- rotationY: round(euler.z)
57
+ rotationY: round(euler.z),
58
+ ...(anchor ? { scaleAnchor: { ...anchor } } : {})
53
59
  };
54
60
  }
55
61
  const num = (v, fallback = 0) => (typeof v === 'number' && Number.isFinite(v) ? v : fallback);
@@ -144,4 +150,16 @@ export function boardFor(parts, at = { width: 0, height: 0 }) {
144
150
  depth: Math.max(at.depth ?? 0, Math.ceil(size.y * 1.1), 200)
145
151
  };
146
152
  }
153
+ /**
154
+ * A body's placement as its fields would read it: its origin in mm and its turn in degrees (rigid@1's X·Y·Z order, as
155
+ * `componentOf` reads it). For a field a rule sets -- a fastened part's height, a turn its joint fixes -- the screen shows
156
+ * this number read-only rather than a blank (designer 2026-09-27).
157
+ */
158
+ export function placementOf(part) {
159
+ const r = part.pose.r;
160
+ const m = new THREE.Matrix4().set(r[0][0], r[0][1], r[0][2], 0, r[1][0], r[1][1], r[1][2], 0, r[2][0], r[2][1], r[2][2], 0, 0, 0, 0, 1);
161
+ const e = new THREE.Euler().setFromRotationMatrix(m, 'XYZ');
162
+ const deg = (v) => Math.round(THREE.MathUtils.radToDeg(v) * 10) / 10 || 0;
163
+ return { position: part.pose.t.map(round), rotation: [deg(e.x), deg(e.y), deg(e.z)] };
164
+ }
147
165
  //# sourceMappingURL=stage-model.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stage-model.js","sourceRoot":"","sources":["../../client/modeller/stage-model.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { buildFigureGeometry } from '@hatiolab/things-scene'\n\nimport type { GizmoResult } from './gizmo.js'\nimport type { FigureRenderPart } from './renderer.js'\n\n/*\n * bodies <-> scene components, in one place.\n *\n * The modeller stands on a things-scene board in editing mode: each body figure-model evaluates is a\n * `figure-part` component, and what the scene's gizmo writes back comes here to become a change of the part's\n * inputs. The two vocabularies:\n *\n * (asset frame, mm, y up) scene component\n * ──────────────────────────────── ─────────────────────────────────────────────\n * body box extent x · y · z width · depth · height\n * body box centre x left + width/2 - board width/2\n * y zPos + depth/2\n * z top + height/2 - board height/2\n * body rotation (rigid@1, Rx·Ry·Rz) rotationX · -rotation · rotationY (radians; three 'XYZ')\n *\n * The box is the body's own bounding box in its local frame, so a turned body keeps its size and the gizmo's handles\n * sit on its faces. Its centre is not always the body's origin (a portal rises from its bottom), so a turn about the\n * centre moves the origin: the result gives the origin where the turned body puts it.\n */\n\nexport interface Board {\n width: number\n height: number\n}\n\nexport interface BodyFrame {\n /** The body's box: its centre in the body's own frame, and its extent. */\n centre: THREE.Vector3\n extent: THREE.Vector3\n rotation: THREE.Matrix4\n origin: THREE.Vector3\n}\n\nconst round = (n: number) => Math.round(n * 1e6) / 1e6\n\nexport function frameOf(part: FigureRenderPart): BodyFrame | null {\n /* The box things-scene's `figure-part` centres the body on: the same builder, measured the same way. */\n let built: THREE.BufferGeometry | undefined\n try {\n built = buildFigureGeometry(part.shape as never, part.segments) as THREE.BufferGeometry | undefined\n } catch {\n return null\n }\n if (!built?.getAttribute('position')) return null\n built.computeBoundingBox()\n const box = built.boundingBox!\n const unit = { centre: box.getCenter(new THREE.Vector3()), extent: box.getSize(new THREE.Vector3()) }\n built.dispose()\n const r = part.pose.r\n const rotation = new THREE.Matrix4().set(r[0]![0]!, r[0]![1]!, r[0]![2]!, 0, r[1]![0]!, r[1]![1]!, r[1]![2]!, 0, r[2]![0]!, r[2]![1]!, r[2]![2]!, 0, 0, 0, 0, 1)\n return { centre: unit.centre, extent: unit.extent, rotation, origin: new THREE.Vector3(...(part.pose.t as [number, number, number])) }\n}\n\n/** The colours a body is drawn in, from the palette the modeller resolved (the asset's, over which the theme wins). */\nfunction colours(part: FigureRenderPart, palette: Record<string, string>) {\n const m = part.material\n const glow = m.emissive?.on ? { color: palette[m.emissive.token] ?? palette[m.token] ?? '#9a9a9a', intensity: m.emissive.intensity } : undefined\n return { color: palette[m.token] ?? '#9a9a9a', preset: m.preset, transparent: !!m.transparent, ...(glow ? { glow } : {}) }\n}\n\n/** The component state that draws a body where figure-model put it. */\nexport function componentOf(part: FigureRenderPart, palette: Record<string, string>, board: Board): Record<string, unknown> | null {\n const frame = frameOf(part)\n if (!frame) return null\n const { centre, extent, rotation, origin } = frame\n const world = centre.clone().applyMatrix4(rotation).add(origin)\n const euler = new THREE.Euler().setFromRotationMatrix(rotation, 'XYZ')\n const width = extent.x || 1,\n depth = extent.y || 1,\n height = extent.z || 1\n return {\n type: 'figure-part',\n name: part.id,\n shape: part.shape,\n ...(part.segments !== undefined ? { segments: part.segments } : {}),\n ...colours(part, palette),\n width: round(width),\n depth: round(depth),\n height: round(height),\n left: round(world.x + board.width / 2 - width / 2),\n top: round(world.z + board.height / 2 - height / 2),\n zPos: round(world.y - depth / 2),\n rotationX: round(euler.x),\n rotation: round(-euler.y),\n rotationY: round(euler.z)\n }\n}\n\nconst num = (v: unknown, fallback = 0) => (typeof v === 'number' && Number.isFinite(v) ? v : fallback)\n\n/**\n * What the gizmo did, in the inputs' terms: where the body's origin is now, how it is turned (degrees, rigid@1's\n * order) and how much each of its own axes grew. Only what changed is given; a component the gizmo did not move gives\n * nothing.\n */\nexport function gizmoResultOf(part: FigureRenderPart, after: Record<string, unknown>, board: Board): GizmoResult {\n const frame = frameOf(part)\n if (!frame) return {}\n const { centre, extent, rotation, origin } = frame\n const width = num(after.width, extent.x),\n depth = num(after.depth, extent.y),\n height = num(after.height, extent.z)\n const scale = new THREE.Vector3(width / (extent.x || 1), depth / (extent.y || 1), height / (extent.z || 1))\n const world = new THREE.Vector3(num(after.left) + width / 2 - board.width / 2, num(after.zPos) + depth / 2, num(after.top) + height / 2 - board.height / 2)\n const euler = new THREE.Euler(num(after.rotationX), -num(after.rotation), num(after.rotationY), 'XYZ')\n const turnedTo = new THREE.Matrix4().makeRotationFromEuler(euler)\n\n const before = new THREE.Quaternion().setFromRotationMatrix(rotation)\n const now = new THREE.Quaternion().setFromEuler(euler)\n const turned = before.angleTo(now) > 1e-4\n const sized = [scale.x, scale.y, scale.z].some(s => Math.abs(s - 1) > 1e-4)\n /* The origin that puts the (scaled, turned) box centre where the scene has it. */\n const newOrigin = world.clone().sub(centre.clone().multiply(scale).applyMatrix4(turnedTo))\n const moved = newOrigin.distanceTo(origin) > 0.5\n\n const result: GizmoResult = {}\n if (moved || turned) result.position = newOrigin.toArray() as [number, number, number]\n if (turned) result.rotation = [euler.x, euler.y, euler.z].map(THREE.MathUtils.radToDeg) as [number, number, number]\n if (sized) result.scale = scale.toArray() as [number, number, number]\n return result\n}\n\n/**\n * A drag as amounts, for a part whose place is worked out (`nudge-part`): how far its origin moved and how far each of\n * its angles turned (rigid@1's order, degrees). Nothing on an axis that did not change.\n */\nexport function nudgeOf(part: FigureRenderPart, result: GizmoResult): { by: Partial<Record<'x' | 'y' | 'z', number>>; turn: Partial<Record<'x' | 'y' | 'z', number>> } {\n const axes = ['x', 'y', 'z'] as const\n const by: Partial<Record<'x' | 'y' | 'z', number>> = {}\n const turn: Partial<Record<'x' | 'y' | 'z', number>> = {}\n if (result.position) axes.forEach((axis, i) => {\n const d = round(result.position![i]! - (part.pose.t[i] as number))\n if (Math.abs(d) > 1e-6) by[axis] = d\n })\n if (result.rotation) {\n const r = part.pose.r\n const now = new THREE.Euler().setFromRotationMatrix(new THREE.Matrix4().set(r[0]![0]!, r[0]![1]!, r[0]![2]!, 0, r[1]![0]!, r[1]![1]!, r[1]![2]!, 0, r[2]![0]!, r[2]![1]!, r[2]![2]!, 0, 0, 0, 0, 1), 'XYZ')\n const was: number[] = [now.x, now.y, now.z].map(radians => (radians * 180) / Math.PI)\n axes.forEach((axis, i) => {\n const d = round(result.rotation![i]! - was[i]!)\n if (Math.abs(d) > 1e-6) turn[axis] = d\n })\n }\n return { by, turn }\n}\n\n/** The keys of `next` whose values differ from `current` -- only those are set, so the scene raises no echo. */\nexport function changedKeys(current: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown> | null {\n const diff: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(next)) {\n if (key === 'type') continue\n const was = current[key]\n const same = typeof value === 'object' && value !== null ? JSON.stringify(was) === JSON.stringify(value) : was === value\n if (!same) diff[key] = value\n }\n return Object.keys(diff).length ? diff : null\n}\n\n/** A board wide and deep enough for the figure, with room around it; its size only grows while a figure is edited. */\nexport function boardFor(parts: readonly FigureRenderPart[], at: Board & { depth?: number } = { width: 0, height: 0 }): Board & { depth: number } {\n const box = new THREE.Box3()\n for (const part of parts) {\n const frame = frameOf(part)\n if (!frame) continue\n const half = frame.extent.clone().multiplyScalar(0.5)\n const corners = [-1, 1].flatMap(x => [-1, 1].flatMap(y => [-1, 1].map(z => new THREE.Vector3(x * half.x, y * half.y, z * half.z))))\n for (const corner of corners) box.expandByPoint(corner.add(frame.centre).applyMatrix4(frame.rotation).add(frame.origin))\n }\n const size = box.isEmpty() ? new THREE.Vector3(1000, 1000, 1000) : box.getSize(new THREE.Vector3())\n const reach = (axis: 'x' | 'z') => 2 * Math.max(Math.abs(box.isEmpty() ? 500 : box.min[axis]), Math.abs(box.isEmpty() ? 500 : box.max[axis]))\n return {\n width: Math.max(at.width, Math.ceil(reach('x') * 1.2), 200),\n height: Math.max(at.height, Math.ceil(reach('z') * 1.2), 200),\n /* Only grows, as the plan does: a raised arm made the board taller than it was, and a new board lost the camera. */\n depth: Math.max(at.depth ?? 0, Math.ceil(size.y * 1.1), 200)\n }\n}\n"]}
1
+ {"version":3,"file":"stage-model.js","sourceRoot":"","sources":["../../client/modeller/stage-model.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { buildFigureGeometry } from '@hatiolab/things-scene'\n\nimport type { GizmoResult } from './gizmo.js'\nimport type { FigureRenderPart } from './renderer.js'\n\n/*\n * bodies <-> scene components, in one place.\n *\n * The modeller stands on a things-scene board in editing mode: each body figure-model evaluates is a\n * `figure-part` component, and what the scene's gizmo writes back comes here to become a change of the part's\n * inputs. The two vocabularies:\n *\n * (asset frame, mm, y up) scene component\n * ──────────────────────────────── ─────────────────────────────────────────────\n * body box extent x · y · z width · depth · height\n * body box centre x left + width/2 - board width/2\n * y zPos + depth/2\n * z top + height/2 - board height/2\n * body rotation (rigid@1, Rx·Ry·Rz) rotationX · -rotation · rotationY (radians; three 'XYZ')\n *\n * The box is the body's own bounding box in its local frame, so a turned body keeps its size and the gizmo's handles\n * sit on its faces. Its centre is not always the body's origin (a portal rises from its bottom), so a turn about the\n * centre moves the origin: the result gives the origin where the turned body puts it.\n */\n\nexport interface Board {\n width: number\n height: number\n}\n\nexport interface BodyFrame {\n /** The body's box: its centre in the body's own frame, and its extent. */\n centre: THREE.Vector3\n extent: THREE.Vector3\n rotation: THREE.Matrix4\n origin: THREE.Vector3\n}\n\nconst round = (n: number) => Math.round(n * 1e6) / 1e6\n\nexport function frameOf(part: FigureRenderPart): BodyFrame | null {\n /* The box things-scene's `figure-part` centres the body on: the same builder, measured the same way. */\n let built: THREE.BufferGeometry | undefined\n try {\n built = buildFigureGeometry(part.shape as never, part.segments) as THREE.BufferGeometry | undefined\n } catch {\n return null\n }\n if (!built?.getAttribute('position')) return null\n built.computeBoundingBox()\n const box = built.boundingBox!\n const unit = { centre: box.getCenter(new THREE.Vector3()), extent: box.getSize(new THREE.Vector3()) }\n built.dispose()\n const r = part.pose.r\n const rotation = new THREE.Matrix4().set(r[0]![0]!, r[0]![1]!, r[0]![2]!, 0, r[1]![0]!, r[1]![1]!, r[1]![2]!, 0, r[2]![0]!, r[2]![1]!, r[2]![2]!, 0, 0, 0, 0, 1)\n return { centre: unit.centre, extent: unit.extent, rotation, origin: new THREE.Vector3(...(part.pose.t as [number, number, number])) }\n}\n\n/** The colours a body is drawn in, from the palette the modeller resolved (the asset's, over which the theme wins). */\nfunction colours(part: FigureRenderPart, palette: Record<string, string>) {\n const m = part.material\n const glow = m.emissive?.on ? { color: palette[m.emissive.token] ?? palette[m.token] ?? '#9a9a9a', intensity: m.emissive.intensity } : undefined\n return { color: palette[m.token] ?? '#9a9a9a', preset: m.preset, transparent: !!m.transparent, ...(glow ? { glow } : {}) }\n}\n\n/** Per axis of the body's own frame, the face a size drag keeps: 1 the low face, -1 the high face, 0 the centre. */\nexport type ScaleAnchor = Record<'x' | 'y' | 'z', -1 | 0 | 1>\n\n/**\n * The component state that draws a body where figure-model put it. With `anchor` -- figure-model's\n * `figureScaleAnchorOf` for its part -- the scene's scale gizmo keeps that face while it is dragged, the face the model\n * keeps when the drag is written; without it the gizmo keeps the face across from the handle held, and the part jumped\n * on release (designer 2026-09-27). Body x · y · z are the component's width · depth · height, as the scene's axes are.\n */\nexport function componentOf(part: FigureRenderPart, palette: Record<string, string>, board: Board, anchor?: ScaleAnchor): Record<string, unknown> | null {\n const frame = frameOf(part)\n if (!frame) return null\n const { centre, extent, rotation, origin } = frame\n const world = centre.clone().applyMatrix4(rotation).add(origin)\n const euler = new THREE.Euler().setFromRotationMatrix(rotation, 'XYZ')\n const width = extent.x || 1,\n depth = extent.y || 1,\n height = extent.z || 1\n return {\n type: 'figure-part',\n name: part.id,\n shape: part.shape,\n ...(part.segments !== undefined ? { segments: part.segments } : {}),\n ...colours(part, palette),\n width: round(width),\n depth: round(depth),\n height: round(height),\n left: round(world.x + board.width / 2 - width / 2),\n top: round(world.z + board.height / 2 - height / 2),\n zPos: round(world.y - depth / 2),\n rotationX: round(euler.x),\n rotation: round(-euler.y),\n rotationY: round(euler.z),\n ...(anchor ? { scaleAnchor: { ...anchor } } : {})\n }\n}\n\nconst num = (v: unknown, fallback = 0) => (typeof v === 'number' && Number.isFinite(v) ? v : fallback)\n\n/**\n * What the gizmo did, in the inputs' terms: where the body's origin is now, how it is turned (degrees, rigid@1's\n * order) and how much each of its own axes grew. Only what changed is given; a component the gizmo did not move gives\n * nothing.\n */\nexport function gizmoResultOf(part: FigureRenderPart, after: Record<string, unknown>, board: Board): GizmoResult {\n const frame = frameOf(part)\n if (!frame) return {}\n const { centre, extent, rotation, origin } = frame\n const width = num(after.width, extent.x),\n depth = num(after.depth, extent.y),\n height = num(after.height, extent.z)\n const scale = new THREE.Vector3(width / (extent.x || 1), depth / (extent.y || 1), height / (extent.z || 1))\n const world = new THREE.Vector3(num(after.left) + width / 2 - board.width / 2, num(after.zPos) + depth / 2, num(after.top) + height / 2 - board.height / 2)\n const euler = new THREE.Euler(num(after.rotationX), -num(after.rotation), num(after.rotationY), 'XYZ')\n const turnedTo = new THREE.Matrix4().makeRotationFromEuler(euler)\n\n const before = new THREE.Quaternion().setFromRotationMatrix(rotation)\n const now = new THREE.Quaternion().setFromEuler(euler)\n const turned = before.angleTo(now) > 1e-4\n const sized = [scale.x, scale.y, scale.z].some(s => Math.abs(s - 1) > 1e-4)\n /* The origin that puts the (scaled, turned) box centre where the scene has it. */\n const newOrigin = world.clone().sub(centre.clone().multiply(scale).applyMatrix4(turnedTo))\n const moved = newOrigin.distanceTo(origin) > 0.5\n\n const result: GizmoResult = {}\n if (moved || turned) result.position = newOrigin.toArray() as [number, number, number]\n if (turned) result.rotation = [euler.x, euler.y, euler.z].map(THREE.MathUtils.radToDeg) as [number, number, number]\n if (sized) result.scale = scale.toArray() as [number, number, number]\n return result\n}\n\n/**\n * A drag as amounts, for a part whose place is worked out (`nudge-part`): how far its origin moved and how far each of\n * its angles turned (rigid@1's order, degrees). Nothing on an axis that did not change.\n */\nexport function nudgeOf(part: FigureRenderPart, result: GizmoResult): { by: Partial<Record<'x' | 'y' | 'z', number>>; turn: Partial<Record<'x' | 'y' | 'z', number>> } {\n const axes = ['x', 'y', 'z'] as const\n const by: Partial<Record<'x' | 'y' | 'z', number>> = {}\n const turn: Partial<Record<'x' | 'y' | 'z', number>> = {}\n if (result.position) axes.forEach((axis, i) => {\n const d = round(result.position![i]! - (part.pose.t[i] as number))\n if (Math.abs(d) > 1e-6) by[axis] = d\n })\n if (result.rotation) {\n const r = part.pose.r\n const now = new THREE.Euler().setFromRotationMatrix(new THREE.Matrix4().set(r[0]![0]!, r[0]![1]!, r[0]![2]!, 0, r[1]![0]!, r[1]![1]!, r[1]![2]!, 0, r[2]![0]!, r[2]![1]!, r[2]![2]!, 0, 0, 0, 0, 1), 'XYZ')\n const was: number[] = [now.x, now.y, now.z].map(radians => (radians * 180) / Math.PI)\n axes.forEach((axis, i) => {\n const d = round(result.rotation![i]! - was[i]!)\n if (Math.abs(d) > 1e-6) turn[axis] = d\n })\n }\n return { by, turn }\n}\n\n/** The keys of `next` whose values differ from `current` -- only those are set, so the scene raises no echo. */\nexport function changedKeys(current: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown> | null {\n const diff: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(next)) {\n if (key === 'type') continue\n const was = current[key]\n const same = typeof value === 'object' && value !== null ? JSON.stringify(was) === JSON.stringify(value) : was === value\n if (!same) diff[key] = value\n }\n return Object.keys(diff).length ? diff : null\n}\n\n/** A board wide and deep enough for the figure, with room around it; its size only grows while a figure is edited. */\nexport function boardFor(parts: readonly FigureRenderPart[], at: Board & { depth?: number } = { width: 0, height: 0 }): Board & { depth: number } {\n const box = new THREE.Box3()\n for (const part of parts) {\n const frame = frameOf(part)\n if (!frame) continue\n const half = frame.extent.clone().multiplyScalar(0.5)\n const corners = [-1, 1].flatMap(x => [-1, 1].flatMap(y => [-1, 1].map(z => new THREE.Vector3(x * half.x, y * half.y, z * half.z))))\n for (const corner of corners) box.expandByPoint(corner.add(frame.centre).applyMatrix4(frame.rotation).add(frame.origin))\n }\n const size = box.isEmpty() ? new THREE.Vector3(1000, 1000, 1000) : box.getSize(new THREE.Vector3())\n const reach = (axis: 'x' | 'z') => 2 * Math.max(Math.abs(box.isEmpty() ? 500 : box.min[axis]), Math.abs(box.isEmpty() ? 500 : box.max[axis]))\n return {\n width: Math.max(at.width, Math.ceil(reach('x') * 1.2), 200),\n height: Math.max(at.height, Math.ceil(reach('z') * 1.2), 200),\n /* Only grows, as the plan does: a raised arm made the board taller than it was, and a new board lost the camera. */\n depth: Math.max(at.depth ?? 0, Math.ceil(size.y * 1.1), 200)\n }\n}\n\n/**\n * A body's placement as its fields would read it: its origin in mm and its turn in degrees (rigid@1's X·Y·Z order, as\n * `componentOf` reads it). For a field a rule sets -- a fastened part's height, a turn its joint fixes -- the screen shows\n * this number read-only rather than a blank (designer 2026-09-27).\n */\nexport function placementOf(part: FigureRenderPart): { position: [number, number, number]; rotation: [number, number, number] } {\n const r = part.pose.r\n const m = new THREE.Matrix4().set(r[0]![0]!, r[0]![1]!, r[0]![2]!, 0, r[1]![0]!, r[1]![1]!, r[1]![2]!, 0, r[2]![0]!, r[2]![1]!, r[2]![2]!, 0, 0, 0, 0, 1)\n const e = new THREE.Euler().setFromRotationMatrix(m, 'XYZ')\n const deg = (v: number) => Math.round(THREE.MathUtils.radToDeg(v) * 10) / 10 || 0\n return { position: part.pose.t.map(round) as [number, number, number], rotation: [deg(e.x), deg(e.y), deg(e.z)] }\n}\n"]}
@@ -48,8 +48,14 @@ export declare class FigureModellerPage extends PageView {
48
48
  /** The figure the AI dock was last told about - another id is another conversation. */
49
49
  private assistantFor?;
50
50
  private provideAssistant;
51
+ /**
52
+ * The part selected and what the preview is trying, with the version they were seen on (ADR-0097 §5.6 item 5). Read
53
+ * when a request is sent; selecting is not a new subject, so the dock is not told about it and the conversation stays.
54
+ */
55
+ private assistantFocus;
51
56
  private withdrawAssistant;
52
57
  private changed;
58
+ private toldDemoted;
53
59
  private readRow;
54
60
  /**
55
61
  * 타입을 정한다 — 보드가 도형을 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).
@@ -74,7 +80,8 @@ export declare class FigureModellerPage extends PageView {
74
80
  */
75
81
  private onCatalog;
76
82
  private released;
77
- /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 패널은 저장 안 된 것이 있으면 되돌리기를 막는다. */
83
+ private saveBeforeRevert;
84
+ /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 저장 안 된 것이 있으면 패널이 먼저 저장·버리기·취소를 묻는다. */
78
85
  private reverted;
79
86
  /**
80
87
  * The dock's way in (`figure-assistant.ts`): input changes, or a proposal's commands as one step (ADR-0094). A
@@ -7,7 +7,7 @@ import { modellerPathOf } from '../modeller/modeller-route.js';
7
7
  import { createFigureProductSession } from '../modeller/product-session.js';
8
8
  import { figureProductStore } from '../graphql/store.js';
9
9
  import { i18next } from '@operato/i18n';
10
- import { fetchFigure, fetchFigureTypeNames, updateFigure } from '../graphql/index.js';
10
+ import { fetchFigure, fetchFigureTypeNames, fetchProposalOutcome, updateFigure } from '../graphql/index.js';
11
11
  import { availableRecipes, recipeGuide } from '../modeller/recipe-source.js';
12
12
  import { occupancyFromParts } from '../modeller/occupancy.js';
13
13
  import '../modeller/editor.js';
@@ -16,12 +16,12 @@ import '../modeller/figure-history-panel.js';
16
16
  import '../modeller/figure-verdict.js';
17
17
  import '../modeller/figure-properties.js';
18
18
  import { OxPrompt } from '@operato/popup';
19
- import { openPopup } from '@operato/layout';
19
+ import { notify, openPopup } from '@operato/layout';
20
20
  import '../modeller/figure-first-save.js';
21
21
  import { activePalette } from '@hatiolab/things-scene';
22
- import { clearAssistantSurface, notifyAssistantSurface, provideAssistantSurface } from '@things-factory/ai-assistant/dist-client/index.js';
22
+ import { askAssistant, clearAssistantSurface, notifyAssistantSurface, provideAssistantSurface } from '@things-factory/ai-assistant/dist-client/index.js';
23
23
  import { placementRefusalOf } from '../modeller/refusals.js';
24
- import { takePendingNewFigure } from '../modeller/ai-proposal.js';
24
+ import { proposalOutcomeFrom, takeFigureOffer, takePendingNewFigure } from '../modeller/ai-proposal.js';
25
25
  /**
26
26
  * The product page has exactly one authoritative workspace for UI and AI input transactions.
27
27
  *
@@ -81,6 +81,8 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
81
81
  label: () => this.session.read().name || id,
82
82
  detail: () => (this.session.read().dirty ? i18next.t('figure.text.unsaved-changes') : ''),
83
83
  anchor: () => ({ anchorType: 'figure', anchorId: id }),
84
+ /* The card's "check the result": the server's record, never the dock's memory; nothing is applied again. */
85
+ proposalOutcome: async (proposalKey) => proposalOutcomeFrom(await fetchProposalOutcome(proposalKey), (key, params) => i18next.t(key, params)),
84
86
  hostContext: () => ({
85
87
  surface: 'figure-modeller',
86
88
  editorMode: 'existing',
@@ -89,7 +91,8 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
89
91
  /* The version the model sees; a proposal is applied on it, or refused if the figure moved on (ADR-0094). */
90
92
  context: this.session.read().context,
91
93
  revision: this.session.read().revision,
92
- palette: Object.keys(activePalette())
94
+ palette: Object.keys(activePalette()),
95
+ ...this.assistantFocus()
93
96
  })
94
97
  });
95
98
  };
@@ -174,7 +177,15 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
174
177
  try {
175
178
  const result = await updateFigure(id, patch);
176
179
  this.commandError = '';
177
- this.row = { ...this.row, category: result.figure.category ?? undefined, tags: result.figure.tags ?? undefined };
180
+ /* The server's answer on its state too: a category changed on a released figure made it a draft, and the badge stayed. */
181
+ this.toldDemoted(this.row.state, result.figure.state);
182
+ this.row = {
183
+ ...this.row,
184
+ category: result.figure.category ?? undefined,
185
+ tags: result.figure.tags ?? undefined,
186
+ ...(result.figure.state ? { state: result.figure.state } : {}),
187
+ ...(result.figure.version !== undefined ? { version: result.figure.version ?? 0 } : {})
188
+ };
178
189
  }
179
190
  catch (error) {
180
191
  this.commandError = error instanceof Error ? error.message : String(error);
@@ -186,7 +197,23 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
186
197
  if (figure?.id && figure.id === this.session.read().id)
187
198
  this.row = { ...this.row, state: figure.state, version: figure.version ?? 0 };
188
199
  };
189
- /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 패널은 저장 안 된 것이 있으면 되돌리기를 막는다. */
200
+ /*
201
+ The edits in hand saved before going back to a released version: known saved, or not -- a failure, or an outcome not
202
+ known, holds the revert (designer 2026-09-27, Q-D1). The figure has a row here: there is no first save to ask.
203
+ */
204
+ this.saveBeforeRevert = async () => {
205
+ try {
206
+ await this.session.save();
207
+ this.changed();
208
+ return this.session.read().dirty ? { ok: false } : { ok: true };
209
+ }
210
+ catch (error) {
211
+ this.changed();
212
+ const code = error?.code;
213
+ return { ok: false, reason: code === 'SAVE_UNREACHABLE' ? i18next.t('figure.text.save-unreachable') : error instanceof Error ? error.message : String(error) };
214
+ }
215
+ };
216
+ /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 저장 안 된 것이 있으면 패널이 먼저 저장·버리기·취소를 묻는다. */
190
217
  this.reverted = async () => {
191
218
  const id = this.session.read().id;
192
219
  if (!id)
@@ -204,31 +231,8 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
204
231
  const detail = event.detail;
205
232
  if (!this.active || !detail)
206
233
  return;
207
- const view = this.session.read();
208
- if (detail.create && this.routeId)
209
- return;
210
- if (detail.create && view.dirty) {
211
- /* A new figure already being made is the person's; a proposal does not go on top of it or replace it. */
212
- detail.applied = false;
213
- detail.code = 'NEW_FIGURE_UNSAVED';
214
- detail.error = i18next.t('figure.text.proposal-new-figure-unsaved');
215
- return;
216
- }
217
- try {
218
- if (detail.create)
219
- this.session.applyProposal(detail.steps, view.revision, view.context);
220
- else if (detail.steps)
221
- this.session.applyProposal(detail.steps, detail.revision, detail.context);
222
- else
223
- this.session.apply(detail.changes, 'ai', detail.revision, detail.context);
224
- detail.applied = true;
234
+ if (takeFigureOffer(this.session, detail, { onSavedFigure: !!this.routeId, say: (key, params) => i18next.t(key, params) }))
225
235
  this.changed();
226
- }
227
- catch (error) {
228
- detail.applied = false;
229
- detail.code = error?.code;
230
- detail.error = error instanceof Error ? error.message : String(error);
231
- }
232
236
  };
233
237
  /*
234
238
  Reloading or closing the tab is not a move the shell asks `canDeactivate` about: a new figure not yet saved was lost
@@ -243,11 +247,15 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
243
247
  /** The first save's question, over the names this domain already uses. */
244
248
  this.askFirstSave = async (name) => {
245
249
  const taken = await fetchFigureTypeNames().catch(() => []);
246
- return openPopup(html `<figure-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-first-save>`, {
250
+ const answer = await openPopup(html `<figure-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-first-save>`, {
247
251
  backdrop: true,
248
252
  size: 'small',
249
253
  title: i18next.t('figure.title.save-new-figure')
250
254
  }).result;
255
+ /* Kept: a first save that failed opens again on the same identifier, not a new draw (ADR-0097 §5.6, Q-D3). */
256
+ if (answer)
257
+ this.row = { ...this.row, type: answer.type };
258
+ return answer;
251
259
  };
252
260
  }
253
261
  static { this.styles = css `
@@ -313,13 +321,37 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
313
321
  }
314
322
  return this.draft;
315
323
  }
324
+ /**
325
+ * The part selected and what the preview is trying, with the version they were seen on (ADR-0097 §5.6 item 5). Read
326
+ * when a request is sent; selecting is not a new subject, so the dock is not told about it and the conversation stays.
327
+ */
328
+ assistantFocus() {
329
+ const focus = this.renderRoot.querySelector('figure-editor')?.assistantFocus?.() ?? {};
330
+ const { context, revision } = this.session.read();
331
+ return {
332
+ ...(focus.part ? { selection: { part: focus.part, context, revision } } : {}),
333
+ ...(focus.trying ? { trying: { ...focus.trying, context, revision } } : {})
334
+ };
335
+ }
316
336
  withdrawAssistant() {
317
337
  this.assistantFor = undefined;
318
338
  clearAssistantSurface(this);
319
339
  }
340
+ /*
341
+ A released figure changed on the server -- its source saved, or its category or tags -- comes back a draft, and a
342
+ board keeps the released version until it is released again. Said when it happens (D, designer 2026-09-27), not left
343
+ to a badge in the history tab.
344
+ */
345
+ /* True of what boards read: `figureBlueprint` answers a draft row with its latest released version (releasedSourceOf). */
346
+ toldDemoted(before, after) {
347
+ if (before === 'released' && after === 'draft')
348
+ notify({ level: 'info', message: i18next.t('figure.text.released-now-draft') });
349
+ }
320
350
  async readRow(id) {
321
351
  try {
322
352
  const figure = await fetchFigure(id);
353
+ if (this.session.read().id === id)
354
+ this.toldDemoted(this.row.state, figure.state);
323
355
  if (this.session.read().id === id)
324
356
  this.row = {
325
357
  state: figure.state,
@@ -438,7 +470,10 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
438
470
  return;
439
471
  const view = this.session.read();
440
472
  try {
441
- this.session.applyProposal(waiting.steps, view.revision, view.context);
473
+ /* Created under the proposal's key: taken twice, it meets the first figure, never a second (ADR-0097 §5.6 item 5). */
474
+ if (waiting.key)
475
+ this.session.createUnder(waiting.key);
476
+ this.session.applyProposal(waiting.steps, view.revision, view.context, waiting.key);
442
477
  waiting.settle({ applied: true });
443
478
  }
444
479
  catch (error) {
@@ -461,7 +496,8 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
461
496
  if (this.error)
462
497
  return html `<p role="alert" class="error">${this.error}</p>`;
463
498
  const view = this.session.read();
464
- 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}>
499
+ /* A new figure's third way to start: the app's assistant dock, with a request put in its input for the author to finish and send (B, U04). */
500
+ return html `<figure-editor .session=${this.session} .releaseState=${this.row} .templates=${this.templates} .guideOf=${this.guideOf} .thumbnailOf=${this.thumbnailOf} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} aiDraft @ask-ai-draft=${() => askAssistant({ draft: i18next.t('figure.text.ai-draft-request') })} @changed=${this.changed}>
465
501
  ${this.commandError ? html `<p slot="notice" fit-error role="alert">${this.commandError}</p>` : ''}
466
502
  <figure-properties
467
503
  slot="figure"
@@ -502,6 +538,8 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
502
538
  .figureVersion=${this.row.version}
503
539
  .figureType=${this.row.type}
504
540
  .dirty=${!!view.dirty}
541
+ .saveFirst=${this.saveBeforeRevert}
542
+ .serverSource=${() => this.session.confirmedSource()}
505
543
  @fit-base-to-parts=${this.fitToParts}
506
544
  @figure-released=${this.released}
507
545
  @figure-reverted=${this.reverted}
@@ -1 +1 @@
1
- {"version":3,"file":"figure-modeller-page.js","sourceRoot":"","sources":["../../client/pages/figure-modeller-page.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { navigate, PageView } from '@operato/shell'\nimport { getPathInfo } from '@operato/utils'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { createFigureProductSession } from '../modeller/product-session.js'\nimport { figureProductStore } from '../graphql/store.js'\nimport { i18next } from '@operato/i18n'\nimport { fetchFigure, fetchFigureTypeNames, updateFigure } from '../graphql/index.js'\nimport { availableRecipes, recipeGuide } from '../modeller/recipe-source.js'\nimport type { RecipeTemplate } from '../modeller/recipes.js'\nimport { occupancyFromParts } from '../modeller/occupancy.js'\nimport type { FigureAuthoringAction } from '@hatiolab/figure-model'\nimport '../modeller/editor.js'\nimport '@material/web/icon/icon.js'\nimport '../modeller/figure-history-panel.js'\nimport '../modeller/figure-verdict.js'\nimport '../modeller/figure-properties.js'\nimport type { FigureBudgetLine } from '../modeller/maturity.js'\nimport { OxPrompt } from '@operato/popup'\nimport { openPopup } from '@operato/layout'\nimport '../modeller/figure-first-save.js'\nimport { activePalette } from '@hatiolab/things-scene'\nimport {\n clearAssistantSurface,\n notifyAssistantSurface,\n provideAssistantSurface\n} from '@things-factory/ai-assistant/dist-client/index.js'\nimport type { FirstSave } from '../modeller/first-save.js'\nimport { placementRefusalOf } from '../modeller/refusals.js'\nimport { takePendingNewFigure } from '../modeller/ai-proposal.js'\n\n/**\n * The product page has exactly one authoritative workspace for UI and AI input transactions.\n *\n * The one modeller since ADR-0087 step 5 (2026-09-24): it answers to `figure-modeller`, and to `figure-modeller`\n * as an alias for links made before the switch.\n */\n@customElement('figure-modeller-page')\nexport class FigureModellerPage extends PageView {\n static styles = css`\n :host {\n display: flex;\n flex-direction: row;\n height: 100%;\n min-height: 0;\n /*\n The page must fit the width it is given. Without this the shell sizes it to its content — the editor's\n 660 plus the 320 release panel — and the panel runs off the right edge (seen at 945 px, 2026-09-24).\n */\n overflow: hidden;\n }\n figure-editor {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n /* The first field of the properties tab, in the side panel's look (side-panel-styles): a label above the field. */\n label[type-field] {\n display: grid;\n gap: 2px;\n margin: 0 0 5px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n label[type-field] input,\n label[type-field] code {\n box-sizing: border-box;\n min-height: 30px;\n padding: 4px 7px;\n font-family: var(--mono-font, monospace);\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n label[type-field] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n p[fit-error] {\n margin: 0;\n padding: 8px 12px;\n font-size: 12px;\n color: var(--md-sys-color-error);\n }\n label[type-field] small[role='alert'] {\n color: var(--md-sys-color-error);\n }\n .error {\n padding: 16px;\n color: var(--md-sys-color-error);\n }\n `\n private readonly session = createFigureProductSession(figureProductStore)\n private routeId: string | undefined\n private discardApproved = false\n @state() private revision = 0\n /** The recipe templates (ADR-0095), read once when the page is first shown; none read, and the section is not drawn. */\n @state() private templates: RecipeTemplate[] = []\n private templatesRead = false\n private guideOf = (name: string) => recipeGuide(name, i18next.language || 'en')\n /* The domain part of the address is read the way the shell reads it (figure-thumb does the same). */\n private thumbnailOf = (name: string) => `${getPathInfo(location.pathname).contextPath}/figure-recipe-thumbnail/${name}`\n @state() private error = ''\n /**\n * 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.\n *\n * 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서\n * `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 도형은 공간을 선언하기 전에는 전부\n * 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.\n */\n @state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {\n version: 0\n }\n @state() private typeError = ''\n /** Why a command from the side did nothing. Shown beside the tabs — the editor stays, since nothing in it was lost. */\n @state() private commandError = ''\n /** The verdict's count, handed to the editor's verdict tab. */\n @state() private instances?: number\n @state() private budget?: FigureBudgetLine\n /**\n * The source the verdict judges. The session refuses to export while it loads or saves, so the last one read is\n * kept rather than blanked — the verdict would otherwise flicker to 「waiting」 on every save.\n */\n private draft?: string\n private draftSource(): string | undefined {\n try {\n this.draft = this.session.source()\n } catch {\n /* loading or saving — keep the last source */\n }\n return this.draft\n }\n private wasDirty = false\n private titled?: string | null\n /** The figure the AI dock was last told about - another id is another conversation. */\n private assistantFor?: string\n /*\n The figure on screen is what the AI dock talks about (design/ai/shared-dock.md §3). A figure not saved yet has no\n row to hang a conversation on, so it provides nothing and the dock stays on the application's conversation.\n The draft is read at the moment of sending, never cached - the dock must not answer about a shape already changed.\n */\n private provideAssistant = () => {\n const view = this.session.read()\n if (!this.active || view.loading || view.error || !view.context) return\n if (!view.id) {\n this.withdrawAssistant()\n return\n }\n if (this.assistantFor === view.id) {\n notifyAssistantSurface(this)\n return\n }\n const id = view.id\n this.assistantFor = id\n provideAssistantSurface(this, {\n key: `figure:${id}`,\n subject: 'figure',\n icon: 'category',\n label: () => this.session.read().name || id,\n detail: () => (this.session.read().dirty ? i18next.t('figure.text.unsaved-changes') : ''),\n anchor: () => ({ anchorType: 'figure', anchorId: id }),\n hostContext: () => ({\n surface: 'figure-modeller',\n editorMode: 'existing',\n figureId: id,\n draftSource: JSON.parse(this.session.source()),\n /* The version the model sees; a proposal is applied on it, or refused if the figure moved on (ADR-0094). */\n context: this.session.read().context,\n revision: this.session.read().revision,\n palette: Object.keys(activePalette())\n })\n })\n }\n private withdrawAssistant() {\n this.assistantFor = undefined\n clearAssistantSurface(this)\n }\n private changed = () => {\n this.revision++\n /* Any edit after a failed command makes the message stale — the author has done something about it. */\n this.commandError = ''\n const view = this.session.read()\n /* The title bar reads the name through `context`, which the shell asks for only when told — a rename left it stale. */\n if (view.name !== this.titled) {\n this.titled = view.name\n this.updateContext()\n }\n if (!view.saving) this.provideAssistant()\n /*\n A new figure's first save gives it an id: the address becomes the figure's own, in place of the history entry.\n Left at the bare modeller address, opening the modeller again from the menu was taken for the same page and kept\n the saved figure -- its type showing where a new figure's was expected (2026-09-25).\n */\n if (view.id && !this.routeId && !view.saving) {\n this.routeId = view.id\n navigate(modellerPathOf({ id: view.id }), true)\n }\n /* 저장이 끝난 순간 행을 다시 읽는다 — 발행된 도형을 고쳐 저장하면 서버가 초안으로 내린다. */\n if (this.wasDirty && !view.dirty && !view.saving && view.id) this.readRow(view.id)\n this.wasDirty = !!view.dirty\n }\n private async readRow(id: string) {\n try {\n const figure = await fetchFigure(id)\n if (this.session.read().id === id)\n this.row = {\n state: figure.state as 'draft' | 'released',\n version: figure.version ?? 0,\n type: figure.type,\n category: figure.category ?? undefined,\n tags: figure.tags ?? undefined\n }\n } catch {\n /* 행을 못 읽으면 패널은 판 번호 0 으로 보인다 — 발행 문이 서버에서 다시 판단하므로 여기서 지어내지 않는다. */\n }\n }\n /**\n * 타입을 정한다 — 보드가 도형을 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).\n *\n * 새 도형은 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.\n * 행만 바뀐다 — 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.\n * 서버의 거절(이미 쓰는 타입 · 발행 뒤)은 그 문장 그대로 칸 아래에 둔다.\n */\n private renameType = async (event: Event) => {\n const input = event.target as HTMLInputElement\n const id = this.session.read().id\n const wanted = input.value.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n input.value = wanted\n /*\n A figure not saved yet has no row to rename: the type is held here and the first save's question opens with it.\n The field was disabled until the first save, so a type typed there went nowhere and was asked for again\n (2026-09-25).\n */\n if (!id) {\n this.row = { ...this.row, type: wanted || undefined }\n return\n }\n if (!wanted || wanted === this.row.type) return\n try {\n const result = await updateFigure(id, { type: wanted })\n this.typeError = ''\n this.row = { ...this.row, type: result.figure.type }\n } catch (error) {\n this.typeError = error instanceof Error ? error.message : String(error)\n input.value = this.row.type ?? ''\n }\n }\n /** 「부품에 맞추기」 — 공간을 지금 부품에서 다시 선언한다. 저장은 저작자가 따로 한다. */\n private fitToParts = () => {\n this.runCommand(() => occupancyFromParts(this.session.source()))\n }\n /** A command from the properties tab — rename, declare the room, the mounting face. */\n private onCommand = (event: Event) => {\n const action = (event as CustomEvent).detail?.action as FigureAuthoringAction | undefined\n if (action) this.runCommand(() => action)\n }\n /**\n * One step of the session's history, whichever part of the side asked for it — so undo takes it back like any\n * other edit, and it is saved with the figure.\n */\n private runCommand(actionOf: () => FigureAuthoringAction) {\n try {\n this.session.command(actionOf())\n this.commandError = ''\n this.changed()\n } catch (error) {\n /*\n This used to set the page error, which replaces the whole editor with one raw line — for a figure with no\n parts that line was 「OCCUPANCY_NO_PARTS: occupancy: no part could be measured」 (2026-09-24).\n */\n this.commandError =\n (error as { code?: string })?.code === 'OCCUPANCY_NO_PARTS'\n ? i18next.t('figure.text.fit-needs-parts')\n : placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??\n (error instanceof Error\n ? error.message\n : String(error))\n }\n }\n /**\n * Category and tags — the catalogue's, kept on the row, not in the asset. Written at once; the server's\n * refusal is shown as it gave it.\n */\n private onCatalog = async (event: Event) => {\n const patch = (event as CustomEvent).detail as { category?: string; tags?: string[] }\n const id = this.session.read().id\n if (!id || !patch) return\n try {\n const result = await updateFigure(id, patch)\n this.commandError = ''\n this.row = { ...this.row, category: result.figure.category ?? undefined, tags: result.figure.tags ?? undefined }\n } catch (error) {\n this.commandError = error instanceof Error ? error.message : String(error)\n }\n }\n private released = (event: Event) => {\n const figure = (event as CustomEvent).detail?.figure\n /* Keep the rest of the row: rebuilding it from these two dropped the type, and the locked type field went blank. */\n if (figure?.id && figure.id === this.session.read().id) this.row = { ...this.row, state: figure.state, version: figure.version ?? 0 }\n }\n /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 패널은 저장 안 된 것이 있으면 되돌리기를 막는다. */\n private reverted = async () => {\n const id = this.session.read().id\n if (!id) return\n await this.session.open(id, { discard: true })\n this.changed()\n this.readRow(id)\n }\n /**\n * The dock's way in (`figure-assistant.ts`): input changes, or a proposal's commands as one step (ADR-0094). A\n * revision is applied on the version the model saw; a new figure's proposal only on a new figure not yet touched. A\n * page on a saved figure leaves a new figure's proposal untaken, and the handler opens a new figure for it.\n */\n private applyAI = (event: Event) => {\n const detail = (event as CustomEvent).detail\n if (!this.active || !detail) return\n const view = this.session.read()\n if (detail.create && this.routeId) return\n if (detail.create && view.dirty) {\n /* A new figure already being made is the person's; a proposal does not go on top of it or replace it. */\n detail.applied = false\n detail.code = 'NEW_FIGURE_UNSAVED'\n detail.error = i18next.t('figure.text.proposal-new-figure-unsaved')\n return\n }\n try {\n if (detail.create) this.session.applyProposal(detail.steps, view.revision!, view.context!)\n else if (detail.steps) this.session.applyProposal(detail.steps, detail.revision, detail.context)\n else this.session.apply(detail.changes, 'ai', detail.revision, detail.context)\n detail.applied = true\n this.changed()\n } catch (error) {\n detail.applied = false\n detail.code = (error as { code?: string })?.code\n detail.error = error instanceof Error ? error.message : String(error)\n }\n }\n /*\n Reloading or closing the tab is not a move the shell asks `canDeactivate` about: a new figure not yet saved was lost\n without a word (2026-09-26). The browser's own question is the only one it allows there.\n */\n private holdUnsaved = (event: BeforeUnloadEvent) => {\n if (!this.active || !this.session.read().dirty) return\n event.preventDefault()\n event.returnValue = ''\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('figure-apply', this.applyAI)\n window.addEventListener('beforeunload', this.holdUnsaved)\n }\n disconnectedCallback() {\n this.withdrawAssistant()\n window.removeEventListener('figure-apply', this.applyAI)\n window.removeEventListener('beforeunload', this.holdUnsaved)\n super.disconnectedCallback()\n }\n async canDeactivate() {\n const view = this.session.read()\n if (view.saving) return false\n if (!view.dirty) return true\n /*\n The shell asks this while the edited model is still on screen. The app's own prompt, as every operato screen\n asks -- not the browser's confirm box (user, 2026-09-24).\n */\n this.discardApproved = !!(await OxPrompt.open({\n type: 'warning',\n title: i18next.t('figure.text.unsaved-changes'),\n text: i18next.t('figure.text.leave-without-saving'),\n confirmButton: { text: i18next.t('figure.button.leave-without-saving') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n }))\n return this.discardApproved\n }\n async pageUpdated(changes: Record<string, unknown>, lifecycle: { resourceId?: string }) {\n if (!this.active) {\n if (this.session.read().loading) this.session.close()\n this.withdrawAssistant()\n return\n }\n if (!this.templatesRead) {\n this.templatesRead = true\n availableRecipes(i18next.language || 'en')\n .then(templates => (this.templates = templates))\n .catch(() => (this.templatesRead = false))\n }\n const id = lifecycle?.resourceId\n if (this.session.read().context && id === this.routeId && !changes.active) return\n try {\n const discard = this.discardApproved\n this.discardApproved = false\n if (id) {\n this.draft = undefined\n /* The row is read after the figure opens; until then the last figure's type must not stand in the field (C2). */\n this.row = { version: 0 }\n this.typeError = ''\n const opening = this.session.open(id, { discard })\n this.changed()\n await opening\n this.readRow(id)\n } else {\n this.session.startNew({ discard })\n this.draft = undefined\n this.row = { version: 0 }\n this.typeError = ''\n this.takeNewFigureProposal()\n }\n this.routeId = id\n this.error = ''\n this.changed()\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n }\n }\n /** A new figure the dock opened for a proposal: its steps, applied as the new figure's first step. */\n private takeNewFigureProposal() {\n const waiting = takePendingNewFigure()\n if (!waiting) return\n const view = this.session.read()\n try {\n this.session.applyProposal(waiting.steps, view.revision!, view.context!)\n waiting.settle({ applied: true })\n } catch (error) {\n waiting.settle({ applied: false, code: (error as { code?: string })?.code, error: error instanceof Error ? error.message : String(error) })\n }\n }\n /** The first save's question, over the names this domain already uses. */\n private askFirstSave = async (name: string): Promise<FirstSave | undefined> => {\n const taken = await fetchFigureTypeNames().catch(() => [] as string[])\n return openPopup<FirstSave>(html`<figure-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-first-save>`, {\n backdrop: true,\n size: 'small',\n title: i18next.t('figure.title.save-new-figure')\n }).result\n }\n get context() {\n return {\n title: this.session.read().name ?? 'Figure',\n actions: [],\n /*\n No heading line: the canvas is what this page is for, and the name is already in the properties' first section\n (and the browser tab). The title stays, not emptied, because the MDI tab reads it too -- the heading is switched\n off where it is drawn (`ox-page-title-bar`).\n */\n heading: false\n }\n }\n render() {\n if (this.error) return html`<p role=\"alert\" class=\"error\">${this.error}</p>`\n const view = this.session.read()\n 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}>\n ${this.commandError ? html`<p slot=\"notice\" fit-error role=\"alert\">${this.commandError}</p>` : ''}\n <figure-properties\n slot=\"figure\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureCategory=${this.row.category}\n .figureTags=${this.row.tags}\n @command=${this.onCommand}\n @catalog=${this.onCatalog}\n >\n <label slot=\"type\" type-field>\n <span>${i18next.t('figure.label.figure-type')}</span>\n ${this.row.version > 0\n ? html`<code>${this.row.type}</code><small>${i18next.t('figure.text.type-name-cannot-change')}</small>`\n : html`<input\n .value=${this.row.type ?? ''}\n placeholder=${i18next.t('figure.text.figure-type-placeholder')}\n @change=${this.renameType}\n />`}\n ${this.typeError ? html`<small role=\"alert\">${this.typeError}</small>` : ''}\n </label>\n </figure-properties>\n <figure-verdict\n slot=\"verdict\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureType=${this.row.type}\n @capacity-changed=${(event: CustomEvent) => {\n this.instances = event.detail.instances\n this.budget = event.detail.budget\n }}\n @fit-base-to-parts=${this.fitToParts}\n ></figure-verdict>\n <figure-history-panel\n slot=\"history\"\n .figureId=${view.id}\n .figureState=${this.row.state}\n .figureVersion=${this.row.version}\n .figureType=${this.row.type}\n .dirty=${!!view.dirty}\n @fit-base-to-parts=${this.fitToParts}\n @figure-released=${this.released}\n @figure-reverted=${this.reverted}\n ></figure-history-panel>\n </figure-editor>`\n }\n\n}\n"]}
1
+ {"version":3,"file":"figure-modeller-page.js","sourceRoot":"","sources":["../../client/pages/figure-modeller-page.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { navigate, PageView } from '@operato/shell'\nimport { getPathInfo } from '@operato/utils'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { createFigureProductSession } from '../modeller/product-session.js'\nimport { figureProductStore } from '../graphql/store.js'\nimport { i18next } from '@operato/i18n'\nimport { fetchFigure, fetchFigureTypeNames, fetchProposalOutcome, updateFigure } from '../graphql/index.js'\nimport { availableRecipes, recipeGuide } from '../modeller/recipe-source.js'\nimport type { RecipeTemplate } from '../modeller/recipes.js'\nimport { occupancyFromParts } from '../modeller/occupancy.js'\nimport type { FigureAuthoringAction } from '@hatiolab/figure-model'\nimport '../modeller/editor.js'\nimport '@material/web/icon/icon.js'\nimport '../modeller/figure-history-panel.js'\nimport '../modeller/figure-verdict.js'\nimport '../modeller/figure-properties.js'\nimport type { FigureBudgetLine } from '../modeller/maturity.js'\nimport { OxPrompt } from '@operato/popup'\nimport { notify, openPopup } from '@operato/layout'\nimport '../modeller/figure-first-save.js'\nimport { activePalette } from '@hatiolab/things-scene'\nimport {\n askAssistant,\n clearAssistantSurface,\n notifyAssistantSurface,\n provideAssistantSurface\n} from '@things-factory/ai-assistant/dist-client/index.js'\nimport type { FirstSave } from '../modeller/first-save.js'\nimport { placementRefusalOf } from '../modeller/refusals.js'\nimport { proposalOutcomeFrom, takeFigureOffer, takePendingNewFigure } from '../modeller/ai-proposal.js'\n\n/**\n * The product page has exactly one authoritative workspace for UI and AI input transactions.\n *\n * The one modeller since ADR-0087 step 5 (2026-09-24): it answers to `figure-modeller`, and to `figure-modeller`\n * as an alias for links made before the switch.\n */\n@customElement('figure-modeller-page')\nexport class FigureModellerPage extends PageView {\n static styles = css`\n :host {\n display: flex;\n flex-direction: row;\n height: 100%;\n min-height: 0;\n /*\n The page must fit the width it is given. Without this the shell sizes it to its content — the editor's\n 660 plus the 320 release panel — and the panel runs off the right edge (seen at 945 px, 2026-09-24).\n */\n overflow: hidden;\n }\n figure-editor {\n flex: 1;\n min-width: 0;\n min-height: 0;\n }\n /* The first field of the properties tab, in the side panel's look (side-panel-styles): a label above the field. */\n label[type-field] {\n display: grid;\n gap: 2px;\n margin: 0 0 5px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n label[type-field] input,\n label[type-field] code {\n box-sizing: border-box;\n min-height: 30px;\n padding: 4px 7px;\n font-family: var(--mono-font, monospace);\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n label[type-field] small {\n color: var(--md-sys-color-on-surface-variant);\n }\n p[fit-error] {\n margin: 0;\n padding: 8px 12px;\n font-size: 12px;\n color: var(--md-sys-color-error);\n }\n label[type-field] small[role='alert'] {\n color: var(--md-sys-color-error);\n }\n .error {\n padding: 16px;\n color: var(--md-sys-color-error);\n }\n `\n private readonly session = createFigureProductSession(figureProductStore)\n private routeId: string | undefined\n private discardApproved = false\n @state() private revision = 0\n /** The recipe templates (ADR-0095), read once when the page is first shown; none read, and the section is not drawn. */\n @state() private templates: RecipeTemplate[] = []\n private templatesRead = false\n private guideOf = (name: string) => recipeGuide(name, i18next.language || 'en')\n /* The domain part of the address is read the way the shell reads it (figure-thumb does the same). */\n private thumbnailOf = (name: string) => `${getPathInfo(location.pathname).contextPath}/figure-recipe-thumbnail/${name}`\n @state() private error = ''\n /**\n * 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.\n *\n * 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서\n * `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 도형은 공간을 선언하기 전에는 전부\n * 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.\n */\n @state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {\n version: 0\n }\n @state() private typeError = ''\n /** Why a command from the side did nothing. Shown beside the tabs — the editor stays, since nothing in it was lost. */\n @state() private commandError = ''\n /** The verdict's count, handed to the editor's verdict tab. */\n @state() private instances?: number\n @state() private budget?: FigureBudgetLine\n /**\n * The source the verdict judges. The session refuses to export while it loads or saves, so the last one read is\n * kept rather than blanked — the verdict would otherwise flicker to 「waiting」 on every save.\n */\n private draft?: string\n private draftSource(): string | undefined {\n try {\n this.draft = this.session.source()\n } catch {\n /* loading or saving — keep the last source */\n }\n return this.draft\n }\n private wasDirty = false\n private titled?: string | null\n /** The figure the AI dock was last told about - another id is another conversation. */\n private assistantFor?: string\n /*\n The figure on screen is what the AI dock talks about (design/ai/shared-dock.md §3). A figure not saved yet has no\n row to hang a conversation on, so it provides nothing and the dock stays on the application's conversation.\n The draft is read at the moment of sending, never cached - the dock must not answer about a shape already changed.\n */\n private provideAssistant = () => {\n const view = this.session.read()\n if (!this.active || view.loading || view.error || !view.context) return\n if (!view.id) {\n this.withdrawAssistant()\n return\n }\n if (this.assistantFor === view.id) {\n notifyAssistantSurface(this)\n return\n }\n const id = view.id\n this.assistantFor = id\n provideAssistantSurface(this, {\n key: `figure:${id}`,\n subject: 'figure',\n icon: 'category',\n label: () => this.session.read().name || id,\n detail: () => (this.session.read().dirty ? i18next.t('figure.text.unsaved-changes') : ''),\n anchor: () => ({ anchorType: 'figure', anchorId: id }),\n /* The card's \"check the result\": the server's record, never the dock's memory; nothing is applied again. */\n proposalOutcome: async (proposalKey: string) =>\n proposalOutcomeFrom(await fetchProposalOutcome(proposalKey), (key, params) => i18next.t(key, params)),\n hostContext: () => ({\n surface: 'figure-modeller',\n editorMode: 'existing',\n figureId: id,\n draftSource: JSON.parse(this.session.source()),\n /* The version the model sees; a proposal is applied on it, or refused if the figure moved on (ADR-0094). */\n context: this.session.read().context,\n revision: this.session.read().revision,\n palette: Object.keys(activePalette()),\n ...this.assistantFocus()\n })\n })\n }\n /**\n * The part selected and what the preview is trying, with the version they were seen on (ADR-0097 §5.6 item 5). Read\n * when a request is sent; selecting is not a new subject, so the dock is not told about it and the conversation stays.\n */\n private assistantFocus() {\n const focus = this.renderRoot.querySelector<HTMLElement & { assistantFocus?: () => { part?: string; trying?: object } }>('figure-editor')?.assistantFocus?.() ?? {}\n const { context, revision } = this.session.read()\n return {\n ...(focus.part ? { selection: { part: focus.part, context, revision } } : {}),\n ...(focus.trying ? { trying: { ...focus.trying, context, revision } } : {})\n }\n }\n private withdrawAssistant() {\n this.assistantFor = undefined\n clearAssistantSurface(this)\n }\n private changed = () => {\n this.revision++\n /* Any edit after a failed command makes the message stale — the author has done something about it. */\n this.commandError = ''\n const view = this.session.read()\n /* The title bar reads the name through `context`, which the shell asks for only when told — a rename left it stale. */\n if (view.name !== this.titled) {\n this.titled = view.name\n this.updateContext()\n }\n if (!view.saving) this.provideAssistant()\n /*\n A new figure's first save gives it an id: the address becomes the figure's own, in place of the history entry.\n Left at the bare modeller address, opening the modeller again from the menu was taken for the same page and kept\n the saved figure -- its type showing where a new figure's was expected (2026-09-25).\n */\n if (view.id && !this.routeId && !view.saving) {\n this.routeId = view.id\n navigate(modellerPathOf({ id: view.id }), true)\n }\n /* 저장이 끝난 순간 행을 다시 읽는다 — 발행된 도형을 고쳐 저장하면 서버가 초안으로 내린다. */\n if (this.wasDirty && !view.dirty && !view.saving && view.id) this.readRow(view.id)\n this.wasDirty = !!view.dirty\n }\n /*\n A released figure changed on the server -- its source saved, or its category or tags -- comes back a draft, and a\n board keeps the released version until it is released again. Said when it happens (D, designer 2026-09-27), not left\n to a badge in the history tab.\n */\n /* True of what boards read: `figureBlueprint` answers a draft row with its latest released version (releasedSourceOf). */\n private toldDemoted(before: string | undefined, after: string | undefined) {\n if (before === 'released' && after === 'draft') notify({ level: 'info', message: i18next.t('figure.text.released-now-draft') })\n }\n private async readRow(id: string) {\n try {\n const figure = await fetchFigure(id)\n if (this.session.read().id === id) this.toldDemoted(this.row.state, figure.state)\n if (this.session.read().id === id)\n this.row = {\n state: figure.state as 'draft' | 'released',\n version: figure.version ?? 0,\n type: figure.type,\n category: figure.category ?? undefined,\n tags: figure.tags ?? undefined\n }\n } catch {\n /* 행을 못 읽으면 패널은 판 번호 0 으로 보인다 — 발행 문이 서버에서 다시 판단하므로 여기서 지어내지 않는다. */\n }\n }\n /**\n * 타입을 정한다 — 보드가 도형을 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).\n *\n * 새 도형은 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.\n * 행만 바뀐다 — 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.\n * 서버의 거절(이미 쓰는 타입 · 발행 뒤)은 그 문장 그대로 칸 아래에 둔다.\n */\n private renameType = async (event: Event) => {\n const input = event.target as HTMLInputElement\n const id = this.session.read().id\n const wanted = input.value.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n input.value = wanted\n /*\n A figure not saved yet has no row to rename: the type is held here and the first save's question opens with it.\n The field was disabled until the first save, so a type typed there went nowhere and was asked for again\n (2026-09-25).\n */\n if (!id) {\n this.row = { ...this.row, type: wanted || undefined }\n return\n }\n if (!wanted || wanted === this.row.type) return\n try {\n const result = await updateFigure(id, { type: wanted })\n this.typeError = ''\n this.row = { ...this.row, type: result.figure.type }\n } catch (error) {\n this.typeError = error instanceof Error ? error.message : String(error)\n input.value = this.row.type ?? ''\n }\n }\n /** 「부품에 맞추기」 — 공간을 지금 부품에서 다시 선언한다. 저장은 저작자가 따로 한다. */\n private fitToParts = () => {\n this.runCommand(() => occupancyFromParts(this.session.source()))\n }\n /** A command from the properties tab — rename, declare the room, the mounting face. */\n private onCommand = (event: Event) => {\n const action = (event as CustomEvent).detail?.action as FigureAuthoringAction | undefined\n if (action) this.runCommand(() => action)\n }\n /**\n * One step of the session's history, whichever part of the side asked for it — so undo takes it back like any\n * other edit, and it is saved with the figure.\n */\n private runCommand(actionOf: () => FigureAuthoringAction) {\n try {\n this.session.command(actionOf())\n this.commandError = ''\n this.changed()\n } catch (error) {\n /*\n This used to set the page error, which replaces the whole editor with one raw line — for a figure with no\n parts that line was 「OCCUPANCY_NO_PARTS: occupancy: no part could be measured」 (2026-09-24).\n */\n this.commandError =\n (error as { code?: string })?.code === 'OCCUPANCY_NO_PARTS'\n ? i18next.t('figure.text.fit-needs-parts')\n : placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??\n (error instanceof Error\n ? error.message\n : String(error))\n }\n }\n /**\n * Category and tags — the catalogue's, kept on the row, not in the asset. Written at once; the server's\n * refusal is shown as it gave it.\n */\n private onCatalog = async (event: Event) => {\n const patch = (event as CustomEvent).detail as { category?: string; tags?: string[] }\n const id = this.session.read().id\n if (!id || !patch) return\n try {\n const result = await updateFigure(id, patch)\n this.commandError = ''\n /* The server's answer on its state too: a category changed on a released figure made it a draft, and the badge stayed. */\n this.toldDemoted(this.row.state, result.figure.state)\n this.row = {\n ...this.row,\n category: result.figure.category ?? undefined,\n tags: result.figure.tags ?? undefined,\n ...(result.figure.state ? { state: result.figure.state as 'draft' | 'released' } : {}),\n ...(result.figure.version !== undefined ? { version: result.figure.version ?? 0 } : {})\n }\n } catch (error) {\n this.commandError = error instanceof Error ? error.message : String(error)\n }\n }\n private released = (event: Event) => {\n const figure = (event as CustomEvent).detail?.figure\n /* Keep the rest of the row: rebuilding it from these two dropped the type, and the locked type field went blank. */\n if (figure?.id && figure.id === this.session.read().id) this.row = { ...this.row, state: figure.state, version: figure.version ?? 0 }\n }\n /*\n The edits in hand saved before going back to a released version: known saved, or not -- a failure, or an outcome not\n known, holds the revert (designer 2026-09-27, Q-D1). The figure has a row here: there is no first save to ask.\n */\n private saveBeforeRevert = async (): Promise<{ ok: boolean; reason?: string }> => {\n try {\n await this.session.save()\n this.changed()\n return this.session.read().dirty ? { ok: false } : { ok: true }\n } catch (error) {\n this.changed()\n const code = (error as { code?: string })?.code\n return { ok: false, reason: code === 'SAVE_UNREACHABLE' ? i18next.t('figure.text.save-unreachable') : error instanceof Error ? error.message : String(error) }\n }\n }\n /** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 저장 안 된 것이 있으면 패널이 먼저 저장·버리기·취소를 묻는다. */\n private reverted = async () => {\n const id = this.session.read().id\n if (!id) return\n await this.session.open(id, { discard: true })\n this.changed()\n this.readRow(id)\n }\n /**\n * The dock's way in (`figure-assistant.ts`): input changes, or a proposal's commands as one step (ADR-0094). A\n * revision is applied on the version the model saw; a new figure's proposal only on a new figure not yet touched. A\n * page on a saved figure leaves a new figure's proposal untaken, and the handler opens a new figure for it.\n */\n private applyAI = (event: Event) => {\n const detail = (event as CustomEvent).detail\n if (!this.active || !detail) return\n if (takeFigureOffer(this.session, detail, { onSavedFigure: !!this.routeId, say: (key, params) => i18next.t(key, params) })) this.changed()\n }\n /*\n Reloading or closing the tab is not a move the shell asks `canDeactivate` about: a new figure not yet saved was lost\n without a word (2026-09-26). The browser's own question is the only one it allows there.\n */\n private holdUnsaved = (event: BeforeUnloadEvent) => {\n if (!this.active || !this.session.read().dirty) return\n event.preventDefault()\n event.returnValue = ''\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('figure-apply', this.applyAI)\n window.addEventListener('beforeunload', this.holdUnsaved)\n }\n disconnectedCallback() {\n this.withdrawAssistant()\n window.removeEventListener('figure-apply', this.applyAI)\n window.removeEventListener('beforeunload', this.holdUnsaved)\n super.disconnectedCallback()\n }\n async canDeactivate() {\n const view = this.session.read()\n if (view.saving) return false\n if (!view.dirty) return true\n /*\n The shell asks this while the edited model is still on screen. The app's own prompt, as every operato screen\n asks -- not the browser's confirm box (user, 2026-09-24).\n */\n this.discardApproved = !!(await OxPrompt.open({\n type: 'warning',\n title: i18next.t('figure.text.unsaved-changes'),\n text: i18next.t('figure.text.leave-without-saving'),\n confirmButton: { text: i18next.t('figure.button.leave-without-saving') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n }))\n return this.discardApproved\n }\n async pageUpdated(changes: Record<string, unknown>, lifecycle: { resourceId?: string }) {\n if (!this.active) {\n if (this.session.read().loading) this.session.close()\n this.withdrawAssistant()\n return\n }\n if (!this.templatesRead) {\n this.templatesRead = true\n availableRecipes(i18next.language || 'en')\n .then(templates => (this.templates = templates))\n .catch(() => (this.templatesRead = false))\n }\n const id = lifecycle?.resourceId\n if (this.session.read().context && id === this.routeId && !changes.active) return\n try {\n const discard = this.discardApproved\n this.discardApproved = false\n if (id) {\n this.draft = undefined\n /* The row is read after the figure opens; until then the last figure's type must not stand in the field (C2). */\n this.row = { version: 0 }\n this.typeError = ''\n const opening = this.session.open(id, { discard })\n this.changed()\n await opening\n this.readRow(id)\n } else {\n this.session.startNew({ discard })\n this.draft = undefined\n this.row = { version: 0 }\n this.typeError = ''\n this.takeNewFigureProposal()\n }\n this.routeId = id\n this.error = ''\n this.changed()\n } catch (error) {\n this.error = error instanceof Error ? error.message : String(error)\n }\n }\n /** A new figure the dock opened for a proposal: its steps, applied as the new figure's first step. */\n private takeNewFigureProposal() {\n const waiting = takePendingNewFigure()\n if (!waiting) return\n const view = this.session.read()\n try {\n /* Created under the proposal's key: taken twice, it meets the first figure, never a second (ADR-0097 §5.6 item 5). */\n if (waiting.key) this.session.createUnder(waiting.key)\n this.session.applyProposal(waiting.steps, view.revision!, view.context!, waiting.key)\n waiting.settle({ applied: true })\n } catch (error) {\n waiting.settle({ applied: false, code: (error as { code?: string })?.code, error: error instanceof Error ? error.message : String(error) })\n }\n }\n /** The first save's question, over the names this domain already uses. */\n private askFirstSave = async (name: string): Promise<FirstSave | undefined> => {\n const taken = await fetchFigureTypeNames().catch(() => [] as string[])\n const answer = await openPopup<FirstSave>(html`<figure-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-first-save>`, {\n backdrop: true,\n size: 'small',\n title: i18next.t('figure.title.save-new-figure')\n }).result\n /* Kept: a first save that failed opens again on the same identifier, not a new draw (ADR-0097 §5.6, Q-D3). */\n if (answer) this.row = { ...this.row, type: answer.type }\n return answer\n }\n get context() {\n return {\n title: this.session.read().name ?? 'Figure',\n actions: [],\n /*\n No heading line: the canvas is what this page is for, and the name is already in the properties' first section\n (and the browser tab). The title stays, not emptied, because the MDI tab reads it too -- the heading is switched\n off where it is drawn (`ox-page-title-bar`).\n */\n heading: false\n }\n }\n render() {\n if (this.error) return html`<p role=\"alert\" class=\"error\">${this.error}</p>`\n const view = this.session.read()\n /* A new figure's third way to start: the app's assistant dock, with a request put in its input for the author to finish and send (B, U04). */\n return html`<figure-editor .session=${this.session} .releaseState=${this.row} .templates=${this.templates} .guideOf=${this.guideOf} .thumbnailOf=${this.thumbnailOf} .firstSave=${this.askFirstSave} .revision=${this.revision} .instances=${this.instances} .budget=${this.budget} aiDraft @ask-ai-draft=${() => askAssistant({ draft: i18next.t('figure.text.ai-draft-request') })} @changed=${this.changed}>\n ${this.commandError ? html`<p slot=\"notice\" fit-error role=\"alert\">${this.commandError}</p>` : ''}\n <figure-properties\n slot=\"figure\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureCategory=${this.row.category}\n .figureTags=${this.row.tags}\n @command=${this.onCommand}\n @catalog=${this.onCatalog}\n >\n <label slot=\"type\" type-field>\n <span>${i18next.t('figure.label.figure-type')}</span>\n ${this.row.version > 0\n ? html`<code>${this.row.type}</code><small>${i18next.t('figure.text.type-name-cannot-change')}</small>`\n : html`<input\n .value=${this.row.type ?? ''}\n placeholder=${i18next.t('figure.text.figure-type-placeholder')}\n @change=${this.renameType}\n />`}\n ${this.typeError ? html`<small role=\"alert\">${this.typeError}</small>` : ''}\n </label>\n </figure-properties>\n <figure-verdict\n slot=\"verdict\"\n .source=${this.draftSource()}\n .figureId=${view.id}\n .figureType=${this.row.type}\n @capacity-changed=${(event: CustomEvent) => {\n this.instances = event.detail.instances\n this.budget = event.detail.budget\n }}\n @fit-base-to-parts=${this.fitToParts}\n ></figure-verdict>\n <figure-history-panel\n slot=\"history\"\n .figureId=${view.id}\n .figureState=${this.row.state}\n .figureVersion=${this.row.version}\n .figureType=${this.row.type}\n .dirty=${!!view.dirty}\n .saveFirst=${this.saveBeforeRevert}\n .serverSource=${() => this.session.confirmedSource()}\n @fit-base-to-parts=${this.fitToParts}\n @figure-released=${this.released}\n @figure-reverted=${this.reverted}\n ></figure-history-panel>\n </figure-editor>`\n }\n\n}\n"]}
@@ -1 +1 @@
1
- export default function route(page: string): "figure-modeller" | "figure-list" | undefined;
1
+ export default function route(page: string): "figure-list" | "figure-modeller" | undefined;