@tangle-network/agent-app 0.43.55 → 0.43.57

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 (352) hide show
  1. package/dist/DesignCanvas-UNL34LS2.js +12 -0
  2. package/dist/DesignCanvasEditor-FJIVYOA2.js +13 -0
  3. package/dist/IntakeInterview-3ZIQMZS6.js +8 -0
  4. package/dist/InvitationsPanel-CJ7DR4F7.js +8 -0
  5. package/dist/MembersPanel-BB4OD2KB.js +8 -0
  6. package/dist/{TimelineEditor-I35SJJAJ.js → TimelineEditor-2ECKSU6P.js} +3 -3
  7. package/dist/{VaultPane-DrQ_tH6c.d.ts → VaultPane-BDP6SB_K.d.ts} +1 -0
  8. package/dist/{access-BZ81Btt5.d.ts → access-490O6aOU.d.ts} +16 -1
  9. package/dist/app-auth/index.d.ts +6 -1
  10. package/dist/app-auth/index.js +3 -3
  11. package/dist/app-auth/index.js.map +1 -1
  12. package/dist/{apply-Gk4raT2j.d.ts → apply-6wlMOLf8.d.ts} +30 -1
  13. package/dist/assets/index.d.ts +41 -0
  14. package/dist/assets/index.js +1 -1
  15. package/dist/assistant/index.d.ts +26 -6
  16. package/dist/assistant/index.js +13 -12
  17. package/dist/assistant/index.js.map +1 -1
  18. package/dist/{attachment-validation-D0csjCvz.d.ts → attachment-validation-tPRp-PP1.d.ts} +7 -1
  19. package/dist/{auth-DuptSkWh.d.ts → auth-_FU8w01b.d.ts} +4 -1
  20. package/dist/billing/index.d.ts +8 -0
  21. package/dist/billing/index.js +1 -1
  22. package/dist/brand/index.d.ts +2 -0
  23. package/dist/brand/index.js.map +1 -1
  24. package/dist/brand-extraction/index.d.ts +3 -0
  25. package/dist/brand-extraction/index.js.map +1 -1
  26. package/dist/catalog/index.d.ts +2 -0
  27. package/dist/catalog/index.js +1 -1
  28. package/dist/chat-routes/index.d.ts +41 -10
  29. package/dist/chat-routes/index.js +15 -15
  30. package/dist/chat-routes/index.js.map +1 -1
  31. package/dist/chat-store/index.d.ts +18 -4
  32. package/dist/chat-store/index.js +4 -4
  33. package/dist/chat-store/index.js.map +1 -1
  34. package/dist/{chunk-H5FM75MR.js → chunk-22GLESKE.js} +3 -3
  35. package/dist/chunk-22GLESKE.js.map +1 -0
  36. package/dist/{chunk-WEBBJBDH.js → chunk-2DRYTJHI.js} +1 -1
  37. package/dist/chunk-2DRYTJHI.js.map +1 -0
  38. package/dist/{chunk-7EVZUIHW.js → chunk-3EJ6SFJI.js} +1 -1
  39. package/dist/chunk-3EJ6SFJI.js.map +1 -0
  40. package/dist/{chunk-HRJJH3RX.js → chunk-3OFQFW5K.js} +4 -4
  41. package/dist/chunk-3OFQFW5K.js.map +1 -0
  42. package/dist/{chunk-7SUPA2YT.js → chunk-4KKXZQPT.js} +2 -2
  43. package/dist/chunk-4KKXZQPT.js.map +1 -0
  44. package/dist/{chunk-5PTGEJZL.js → chunk-5SD6HT2V.js} +1 -1
  45. package/dist/chunk-5SD6HT2V.js.map +1 -0
  46. package/dist/{chunk-7C64WKAH.js → chunk-5SXS3YAB.js} +2 -2
  47. package/dist/{chunk-L2TG5DBW.js → chunk-5TESA4SN.js} +1 -1
  48. package/dist/chunk-5TESA4SN.js.map +1 -0
  49. package/dist/{chunk-63CE7FEZ.js → chunk-6XIAPIW6.js} +1 -1
  50. package/dist/chunk-6XIAPIW6.js.map +1 -0
  51. package/dist/{chunk-MFRCM32T.js → chunk-7CTIUCQ4.js} +1 -1
  52. package/dist/chunk-7CTIUCQ4.js.map +1 -0
  53. package/dist/{chunk-6KVH5SP5.js → chunk-7YW6EBDX.js} +2 -2
  54. package/dist/chunk-7YW6EBDX.js.map +1 -0
  55. package/dist/{chunk-ZXNXAQAH.js → chunk-AP2UXKUK.js} +1 -1
  56. package/dist/chunk-AP2UXKUK.js.map +1 -0
  57. package/dist/{chunk-K63OWUDJ.js → chunk-BPSV5AA2.js} +2 -2
  58. package/dist/chunk-BPSV5AA2.js.map +1 -0
  59. package/dist/{chunk-MFI4ZM7W.js → chunk-BT4QFCTB.js} +1 -1
  60. package/dist/chunk-BT4QFCTB.js.map +1 -0
  61. package/dist/{chunk-ZYBWGSAZ.js → chunk-BVKQKGRK.js} +1 -1
  62. package/dist/chunk-BVKQKGRK.js.map +1 -0
  63. package/dist/{chunk-RVUISYUE.js → chunk-C3NSB66C.js} +7 -7
  64. package/dist/chunk-C3NSB66C.js.map +1 -0
  65. package/dist/{chunk-BQV42AFK.js → chunk-CCVWR33L.js} +1 -1
  66. package/dist/chunk-CCVWR33L.js.map +1 -0
  67. package/dist/{chunk-MCJSS6SM.js → chunk-CQZSAR77.js} +1 -1
  68. package/dist/chunk-CQZSAR77.js.map +1 -0
  69. package/dist/{chunk-3SVAA3MA.js → chunk-DJ4VJIH5.js} +1 -1
  70. package/dist/chunk-DJ4VJIH5.js.map +1 -0
  71. package/dist/{chunk-XKBIYSSM.js → chunk-DTSPS2HV.js} +3 -3
  72. package/dist/chunk-DTSPS2HV.js.map +1 -0
  73. package/dist/{chunk-4QN75UHX.js → chunk-E7N67FYI.js} +8 -8
  74. package/dist/chunk-E7N67FYI.js.map +1 -0
  75. package/dist/{chunk-2QI7XV2T.js → chunk-FBVLEGEG.js} +1 -1
  76. package/dist/{chunk-2QI7XV2T.js.map → chunk-FBVLEGEG.js.map} +1 -1
  77. package/dist/{chunk-5RMIUJDI.js → chunk-FKCB5FTD.js} +1 -1
  78. package/dist/chunk-FKCB5FTD.js.map +1 -0
  79. package/dist/{chunk-EEPJGZJW.js → chunk-GPBRL2XR.js} +1 -1
  80. package/dist/chunk-GPBRL2XR.js.map +1 -0
  81. package/dist/{chunk-G3HCU7TA.js → chunk-GXE6KHTS.js} +1 -1
  82. package/dist/chunk-GXE6KHTS.js.map +1 -0
  83. package/dist/{chunk-65P3HJY3.js → chunk-HCOROIRT.js} +1 -1
  84. package/dist/chunk-HCOROIRT.js.map +1 -0
  85. package/dist/{chunk-54GSK2KK.js → chunk-HYTIHYNV.js} +2 -2
  86. package/dist/chunk-HYTIHYNV.js.map +1 -0
  87. package/dist/{chunk-5MG74GVQ.js → chunk-IG46XCZM.js} +3 -3
  88. package/dist/chunk-IG46XCZM.js.map +1 -0
  89. package/dist/{chunk-ND3XEGBE.js → chunk-JDEGS53O.js} +1 -1
  90. package/dist/chunk-JDEGS53O.js.map +1 -0
  91. package/dist/{chunk-7W5XSTUF.js → chunk-JML7WKWU.js} +1 -1
  92. package/dist/chunk-JML7WKWU.js.map +1 -0
  93. package/dist/{chunk-Q4TKVF3L.js → chunk-JTLYXUEV.js} +1 -1
  94. package/dist/chunk-JTLYXUEV.js.map +1 -0
  95. package/dist/{chunk-CCRVDLV3.js → chunk-JWBZ74TW.js} +2 -2
  96. package/dist/chunk-JWBZ74TW.js.map +1 -0
  97. package/dist/{chunk-ZU5GNSOJ.js → chunk-K3LIPKFV.js} +3 -3
  98. package/dist/chunk-K3LIPKFV.js.map +1 -0
  99. package/dist/{chunk-GQRL6VNU.js → chunk-LGBO24FL.js} +1 -1
  100. package/dist/chunk-LGBO24FL.js.map +1 -0
  101. package/dist/{chunk-SWUVTGMR.js → chunk-MEUNTJL5.js} +3 -3
  102. package/dist/{chunk-SWUVTGMR.js.map → chunk-MEUNTJL5.js.map} +1 -1
  103. package/dist/{chunk-YNALYIQ3.js → chunk-NLSSFLLS.js} +1 -1
  104. package/dist/chunk-NLSSFLLS.js.map +1 -0
  105. package/dist/{chunk-HFC4BTWJ.js → chunk-PTUMBMVH.js} +1 -1
  106. package/dist/chunk-PTUMBMVH.js.map +1 -0
  107. package/dist/{chunk-R4NOKZMD.js → chunk-ROLFDOMJ.js} +3 -3
  108. package/dist/{chunk-NYATNLRK.js → chunk-RT5RKAFX.js} +1 -1
  109. package/dist/chunk-RT5RKAFX.js.map +1 -0
  110. package/dist/{chunk-GNL3MG5J.js → chunk-S564OFTL.js} +2 -2
  111. package/dist/{chunk-GUSCYE3S.js → chunk-TQOFR6XY.js} +1 -1
  112. package/dist/chunk-TQOFR6XY.js.map +1 -0
  113. package/dist/{chunk-47C6PJCW.js → chunk-TZO3SOUL.js} +1 -1
  114. package/dist/chunk-TZO3SOUL.js.map +1 -0
  115. package/dist/{chunk-YGUNTIT5.js → chunk-UKTYKG57.js} +1 -1
  116. package/dist/chunk-UKTYKG57.js.map +1 -0
  117. package/dist/{chunk-23LUHHHN.js → chunk-VCOD5AOR.js} +1 -1
  118. package/dist/chunk-VCOD5AOR.js.map +1 -0
  119. package/dist/{chunk-5I72FRZY.js → chunk-VOJB7MVG.js} +2 -2
  120. package/dist/{chunk-XWJXZ6WE.js → chunk-VTWKH4YU.js} +2 -2
  121. package/dist/chunk-VTWKH4YU.js.map +1 -0
  122. package/dist/{chunk-4K7BZYO7.js → chunk-VWAEWOWK.js} +3 -3
  123. package/dist/chunk-VWAEWOWK.js.map +1 -0
  124. package/dist/{chunk-M2EBUDZ7.js → chunk-VWOPOLVO.js} +2 -2
  125. package/dist/chunk-VWOPOLVO.js.map +1 -0
  126. package/dist/{chunk-BN2USWIR.js → chunk-VYECQAGM.js} +2 -2
  127. package/dist/chunk-VYECQAGM.js.map +1 -0
  128. package/dist/{chunk-SIXYZ2FB.js → chunk-YJMCRXQQ.js} +1 -1
  129. package/dist/chunk-YJMCRXQQ.js.map +1 -0
  130. package/dist/{chunk-PJC4NXPA.js → chunk-YKBDH2UY.js} +3 -3
  131. package/dist/chunk-YKBDH2UY.js.map +1 -0
  132. package/dist/{chunk-XAWFPMAR.js → chunk-YKOM4ZLQ.js} +1 -1
  133. package/dist/chunk-YKOM4ZLQ.js.map +1 -0
  134. package/dist/{chunk-TKVJE63N.js → chunk-YRMLYFVR.js} +3 -3
  135. package/dist/chunk-YRMLYFVR.js.map +1 -0
  136. package/dist/{chunk-USYXHDKE.js → chunk-YZ6KQULN.js} +2 -2
  137. package/dist/{command-stack-B_-WNfZU.d.ts → command-stack-TNPWJCzW.d.ts} +14 -2
  138. package/dist/{completion-Kaqp_tWk.d.ts → completion-CvcNUz07.d.ts} +1 -1
  139. package/dist/config/index.d.ts +1 -1
  140. package/dist/{contract-KfqJh_au.d.ts → contract-TjB68uqA.d.ts} +17 -0
  141. package/dist/design-canvas/drizzle.d.ts +10 -2
  142. package/dist/design-canvas/drizzle.js.map +1 -1
  143. package/dist/design-canvas/index.d.ts +29 -11
  144. package/dist/design-canvas/index.js +1070 -17
  145. package/dist/design-canvas/index.js.map +1 -1
  146. package/dist/design-canvas-react/engine.d.ts +42 -6
  147. package/dist/design-canvas-react/engine.js +4 -4
  148. package/dist/design-canvas-react/index.d.ts +12 -7
  149. package/dist/design-canvas-react/index.js +11 -11
  150. package/dist/design-canvas-react/index.js.map +1 -1
  151. package/dist/design-canvas-react/lazy.d.ts +4 -4
  152. package/dist/design-canvas-react/lazy.js +1 -1
  153. package/dist/durable-chat/index.d.ts +46 -1
  154. package/dist/durable-chat/index.js +3 -3
  155. package/dist/eval/index.d.ts +1 -1
  156. package/dist/{export-presets-DAsU5Wal.d.ts → export-presets-mgVulRaV.d.ts} +10 -1
  157. package/dist/{flow-types-Cb_AblZs.d.ts → flow-types-CqomVAUN.d.ts} +1 -0
  158. package/dist/harness/index.d.ts +5 -0
  159. package/dist/harness/index.js +1 -1
  160. package/dist/index.d.ts +10 -18
  161. package/dist/index.js +30 -231
  162. package/dist/intakes/api.d.ts +3 -2
  163. package/dist/intakes/api.js +3 -3
  164. package/dist/intakes/api.js.map +1 -1
  165. package/dist/intakes/drizzle.d.ts +10 -2
  166. package/dist/intakes/drizzle.js +3 -3
  167. package/dist/intakes/drizzle.js.map +1 -1
  168. package/dist/intakes/index.d.ts +2 -2
  169. package/dist/intakes/index.js +2 -2
  170. package/dist/intakes-react/index.d.ts +2 -1
  171. package/dist/intakes-react/index.js +2 -2
  172. package/dist/intakes-react/lazy.d.ts +2 -1
  173. package/dist/intakes-react/lazy.js +1 -1
  174. package/dist/intakes-react/lazy.js.map +1 -1
  175. package/dist/integrations/index.d.ts +4 -0
  176. package/dist/integrations/index.js +1 -1
  177. package/dist/interactions/index.d.ts +13 -2
  178. package/dist/interactions/index.js +2 -2
  179. package/dist/{invitations-CZ5G2tIn.d.ts → invitations-DoYMvXea.d.ts} +11 -1
  180. package/dist/{invitations-schema-DaMO4hZE.d.ts → invitations-schema-_fI7HkJP.d.ts} +5 -1
  181. package/dist/knowledge/index.d.ts +2 -0
  182. package/dist/knowledge/index.js +1 -1
  183. package/dist/knowledge-loop/index.d.ts +4 -1
  184. package/dist/knowledge-loop/index.js +1 -1
  185. package/dist/{lazy-C7LiVeFt.d.ts → lazy-DrkVyDpe.d.ts} +2 -2
  186. package/dist/{mcp-BHfIoGLW.d.ts → mcp-BjPqQ-3C.d.ts} +4 -2
  187. package/dist/{mcp-rpc-DLw_r9PQ.d.ts → mcp-rpc-BfUOsalw.d.ts} +3 -0
  188. package/dist/missions/index.d.ts +25 -0
  189. package/dist/missions/index.js +1 -1
  190. package/dist/{model-Bzi5i63l.d.ts → model-C8U0HZxL.d.ts} +1 -0
  191. package/dist/{model-dF2h4xT9.d.ts → model-DmdkIteM.d.ts} +19 -0
  192. package/dist/{model-BBwyOuWG.d.ts → model-DnfO_PdJ.d.ts} +25 -1
  193. package/dist/model-resolution/index.d.ts +10 -0
  194. package/dist/model-resolution/index.js.map +1 -1
  195. package/dist/{operations-DLIvvD00.d.ts → operations-ByJ6yXOW.d.ts} +20 -2
  196. package/dist/{parts-1sRYSPjF.d.ts → parts-n9XUon0a.d.ts} +29 -1
  197. package/dist/plans/index.d.ts +6 -0
  198. package/dist/plans/index.js +1 -1
  199. package/dist/platform/index.d.ts +24 -2
  200. package/dist/platform/index.js +3 -3
  201. package/dist/platform/index.js.map +1 -1
  202. package/dist/preflight/index.d.ts +3 -0
  203. package/dist/preflight/index.js +1 -1
  204. package/dist/preset-cloudflare/index.d.ts +4 -1
  205. package/dist/preset-cloudflare/index.js +2 -2
  206. package/dist/redact/index.d.ts +5 -0
  207. package/dist/redact/index.js +1 -1
  208. package/dist/{roles-BC1n4t37.d.ts → roles-B4nU3jDi.d.ts} +12 -0
  209. package/dist/run/index.d.ts +3 -0
  210. package/dist/run/index.js +1 -1
  211. package/dist/run/index.js.map +1 -1
  212. package/dist/runtime/index.d.ts +11 -4
  213. package/dist/runtime/index.js +4 -4
  214. package/dist/sandbox/index.d.ts +96 -3
  215. package/dist/sandbox/index.js +6 -6
  216. package/dist/{sandbox-terminal-BIIC__CP.d.ts → sandbox-terminal-ChNEdHF8.d.ts} +5 -0
  217. package/dist/sequences/drizzle.d.ts +11 -1
  218. package/dist/sequences/drizzle.js +1 -1
  219. package/dist/sequences/drizzle.js.map +1 -1
  220. package/dist/sequences/index.d.ts +32 -7
  221. package/dist/sequences/index.js +1562 -47
  222. package/dist/sequences/index.js.map +1 -1
  223. package/dist/sequences-react/index.d.ts +32 -2
  224. package/dist/sequences-react/index.js +3 -3
  225. package/dist/sequences-react/index.js.map +1 -1
  226. package/dist/{sso-CNOsARMJ.d.ts → sso-Cm00S61u.d.ts} +13 -0
  227. package/dist/{stale-turn-lock-C8Na1cFZ.d.ts → stale-turn-lock-DucQzvXu.d.ts} +2 -0
  228. package/dist/store/index.d.ts +5 -0
  229. package/dist/store/index.js.map +1 -1
  230. package/dist/{store-gckrNq-g.d.ts → store-B7LLlk9p.d.ts} +22 -1
  231. package/dist/{store-CFyxgTlD.d.ts → store-CqfDtnPQ.d.ts} +8 -2
  232. package/dist/stream/index.d.ts +10 -4
  233. package/dist/stream/index.js +4 -4
  234. package/dist/{stream-normalizer-DWvtmY6F.d.ts → stream-normalizer-C3M--r6y.d.ts} +17 -1
  235. package/dist/studio/index.d.ts +37 -0
  236. package/dist/studio/index.js +1 -1
  237. package/dist/studio-react/index.d.ts +3 -0
  238. package/dist/studio-react/index.js +1 -1
  239. package/dist/studio-react/index.js.map +1 -1
  240. package/dist/tangle/index.d.ts +3 -0
  241. package/dist/tangle/index.js +1 -1
  242. package/dist/teams/drizzle.d.ts +8 -4
  243. package/dist/teams/drizzle.js +1 -1
  244. package/dist/teams/drizzle.js.map +1 -1
  245. package/dist/teams/index.d.ts +4 -2
  246. package/dist/teams/index.js +3 -3
  247. package/dist/teams/invitations-api.d.ts +10 -4
  248. package/dist/teams/invitations-api.js +4 -4
  249. package/dist/teams/invitations-api.js.map +1 -1
  250. package/dist/teams/members-api.d.ts +4 -2
  251. package/dist/teams/members-api.js +3 -3
  252. package/dist/teams/resend.d.ts +5 -4
  253. package/dist/teams/resend.js +1 -1
  254. package/dist/teams/resend.js.map +1 -1
  255. package/dist/teams-react/index.d.ts +7 -2
  256. package/dist/teams-react/index.js +3 -3
  257. package/dist/teams-react/lazy.d.ts +5 -2
  258. package/dist/teams-react/lazy.js +2 -2
  259. package/dist/teams-react/lazy.js.map +1 -1
  260. package/dist/theme/index.d.ts +2 -0
  261. package/dist/theme/index.js +1 -1
  262. package/dist/theme/tailwind-preset.d.ts +1 -0
  263. package/dist/theme/tailwind-preset.js.map +1 -1
  264. package/dist/theme-contract/cli.js +1 -1
  265. package/dist/theme-contract/index.d.ts +3 -0
  266. package/dist/theme-contract/index.js +1 -1
  267. package/dist/tools/index.d.ts +13 -6
  268. package/dist/tools/index.js +3 -3
  269. package/dist/trace/index.d.ts +6 -1
  270. package/dist/trace/index.js +2 -2
  271. package/dist/{turn-buffer-C9mEgoop.d.ts → turn-buffer-DGnAPKwa.d.ts} +6 -0
  272. package/dist/turn-stream/index.d.ts +28 -2
  273. package/dist/turn-stream/index.js +1 -1
  274. package/dist/turn-stream/index.js.map +1 -1
  275. package/dist/{types-BEOvc_ue.d.ts → types-CBRyqijY.d.ts} +10 -0
  276. package/dist/vault/index.d.ts +1 -1
  277. package/dist/vault/lazy.d.ts +3 -2
  278. package/dist/vault/lazy.js.map +1 -1
  279. package/dist/web/index.d.ts +4 -0
  280. package/dist/web/index.js +1 -1
  281. package/dist/web-react/index.d.ts +51 -8
  282. package/dist/web-react/index.js +9 -9
  283. package/dist/web-react/terminal.d.ts +2 -2
  284. package/dist/web-react/terminal.js +1 -1
  285. package/package.json +3 -2
  286. package/dist/DesignCanvas-5DJAFDNC.js +0 -12
  287. package/dist/DesignCanvasEditor-XUJZARSN.js +0 -13
  288. package/dist/IntakeInterview-WHVKMNQW.js +0 -8
  289. package/dist/InvitationsPanel-XUUWZBPV.js +0 -8
  290. package/dist/MembersPanel-FOVHWUQ3.js +0 -8
  291. package/dist/chunk-23LUHHHN.js.map +0 -1
  292. package/dist/chunk-3SVAA3MA.js.map +0 -1
  293. package/dist/chunk-47C6PJCW.js.map +0 -1
  294. package/dist/chunk-4K7BZYO7.js.map +0 -1
  295. package/dist/chunk-4QN75UHX.js.map +0 -1
  296. package/dist/chunk-54GSK2KK.js.map +0 -1
  297. package/dist/chunk-5MG74GVQ.js.map +0 -1
  298. package/dist/chunk-5PTGEJZL.js.map +0 -1
  299. package/dist/chunk-5RMIUJDI.js.map +0 -1
  300. package/dist/chunk-63CE7FEZ.js.map +0 -1
  301. package/dist/chunk-65P3HJY3.js.map +0 -1
  302. package/dist/chunk-6KVH5SP5.js.map +0 -1
  303. package/dist/chunk-7EVZUIHW.js.map +0 -1
  304. package/dist/chunk-7SUPA2YT.js.map +0 -1
  305. package/dist/chunk-7W5XSTUF.js.map +0 -1
  306. package/dist/chunk-BN2USWIR.js.map +0 -1
  307. package/dist/chunk-BQV42AFK.js.map +0 -1
  308. package/dist/chunk-CCRVDLV3.js.map +0 -1
  309. package/dist/chunk-EEPJGZJW.js.map +0 -1
  310. package/dist/chunk-G3HCU7TA.js.map +0 -1
  311. package/dist/chunk-GQRL6VNU.js.map +0 -1
  312. package/dist/chunk-GUSCYE3S.js.map +0 -1
  313. package/dist/chunk-H5FM75MR.js.map +0 -1
  314. package/dist/chunk-HFC4BTWJ.js.map +0 -1
  315. package/dist/chunk-HRJJH3RX.js.map +0 -1
  316. package/dist/chunk-K63OWUDJ.js.map +0 -1
  317. package/dist/chunk-L2TG5DBW.js.map +0 -1
  318. package/dist/chunk-M2EBUDZ7.js.map +0 -1
  319. package/dist/chunk-MCJSS6SM.js.map +0 -1
  320. package/dist/chunk-MFI4ZM7W.js.map +0 -1
  321. package/dist/chunk-MFRCM32T.js.map +0 -1
  322. package/dist/chunk-ND3XEGBE.js.map +0 -1
  323. package/dist/chunk-NEOV2NQ3.js +0 -1617
  324. package/dist/chunk-NEOV2NQ3.js.map +0 -1
  325. package/dist/chunk-NYATNLRK.js.map +0 -1
  326. package/dist/chunk-PJC4NXPA.js.map +0 -1
  327. package/dist/chunk-Q4TKVF3L.js.map +0 -1
  328. package/dist/chunk-RVUISYUE.js.map +0 -1
  329. package/dist/chunk-SIXYZ2FB.js.map +0 -1
  330. package/dist/chunk-TKVJE63N.js.map +0 -1
  331. package/dist/chunk-WEBBJBDH.js.map +0 -1
  332. package/dist/chunk-WFMUDX5J.js +0 -1092
  333. package/dist/chunk-WFMUDX5J.js.map +0 -1
  334. package/dist/chunk-XAWFPMAR.js.map +0 -1
  335. package/dist/chunk-XKBIYSSM.js.map +0 -1
  336. package/dist/chunk-XWJXZ6WE.js.map +0 -1
  337. package/dist/chunk-YGUNTIT5.js.map +0 -1
  338. package/dist/chunk-YNALYIQ3.js.map +0 -1
  339. package/dist/chunk-ZU5GNSOJ.js.map +0 -1
  340. package/dist/chunk-ZXNXAQAH.js.map +0 -1
  341. package/dist/chunk-ZYBWGSAZ.js.map +0 -1
  342. /package/dist/{DesignCanvas-5DJAFDNC.js.map → DesignCanvas-UNL34LS2.js.map} +0 -0
  343. /package/dist/{DesignCanvasEditor-XUJZARSN.js.map → DesignCanvasEditor-FJIVYOA2.js.map} +0 -0
  344. /package/dist/{IntakeInterview-WHVKMNQW.js.map → IntakeInterview-3ZIQMZS6.js.map} +0 -0
  345. /package/dist/{InvitationsPanel-XUUWZBPV.js.map → InvitationsPanel-CJ7DR4F7.js.map} +0 -0
  346. /package/dist/{MembersPanel-FOVHWUQ3.js.map → MembersPanel-BB4OD2KB.js.map} +0 -0
  347. /package/dist/{TimelineEditor-I35SJJAJ.js.map → TimelineEditor-2ECKSU6P.js.map} +0 -0
  348. /package/dist/{chunk-7C64WKAH.js.map → chunk-5SXS3YAB.js.map} +0 -0
  349. /package/dist/{chunk-R4NOKZMD.js.map → chunk-ROLFDOMJ.js.map} +0 -0
  350. /package/dist/{chunk-GNL3MG5J.js.map → chunk-S564OFTL.js.map} +0 -0
  351. /package/dist/{chunk-5I72FRZY.js.map → chunk-VOJB7MVG.js.map} +0 -0
  352. /package/dist/{chunk-USYXHDKE.js.map → chunk-YZ6KQULN.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/design-canvas-react/components/Workspace.tsx","../src/design-canvas-react/export.ts","../src/design-canvas-react/components/CanvasEmptyState.tsx","../src/design-canvas-react/components/ElementNode.tsx","../src/design-canvas-react/components/transform-math.ts","../src/design-canvas-react/components/SelectionLayer.tsx","../src/design-canvas-react/components/GridLayer.tsx","../src/design-canvas-react/components/SnapGuidesOverlay.tsx","../src/design-canvas-react/components/InlineTextEditor.tsx","../src/design-canvas-react/components/DesignCanvasEditor.tsx"],"sourcesContent":["/**\n * Konva Stage host for the design-canvas editor. Renders the active page\n * (background rect + element nodes), wires all interaction gestures, and\n * delegates persistence through the command stack.\n *\n * COORDINATE SYSTEM:\n * The Konva stage sits at (0,0) in screen space. The content Layer group is\n * translated by (panX, panY) and scaled by (zoom, zoom). Document coordinates\n * map to screen via: screenX = panX + docX * zoom.\n *\n * GESTURES (mutually exclusive; escape cancels any live gesture):\n * - Wheel: zoom-to-cursor via ZoomPanMath.zoomAtPoint; setView only.\n * - Middle-button drag / space+drag: pan; setView only.\n * - Empty-space drag: marquee selection; setView only.\n * - Element drag: move via onDragMove preview + onDragEnd command.\n * - Transformer: resize/rotate; handled by SelectionLayer.\n * - Double-click on text: opens InlineTextEditor.\n *\n * KEYBOARD (active when canvas wrapper div is focused):\n * - delete/backspace → deleteElementCommand for each selected id.\n * - arrows → nudge (shift ×10); ONE command emitted on keyup (coalescing).\n * - mod+z / shift+mod+z → undo / redo.\n * - mod+d → duplicate selection at +10,+10 offset.\n * - mod+g → group selection (≥2 elements).\n * - shift+mod+g → ungroup selected group.\n * - mod+a → select all on current page.\n * - escape → cancel live gesture or clear selection.\n *\n * POINTER CAPTURE: marquee and pan gestures call setPointerCapture on the\n * wrapper div so drag events don't escape on fast pointer moves.\n *\n * DEVICEPIXELRATIO: Konva's pixelRatio prop multiplies the backing canvas\n * resolution for crisp rendering at any DPR.\n *\n * Composition:\n * - `WorkspaceView` is the injectable form: receives a pre-created stack and\n * a guaranteed non-null activePage, commits all gestures through that shared\n * stack. `DesignCanvasEditor` passes the chrome's stack here so undo/redo\n * and layers-panel selection stay coherent across both surfaces.\n * - `Workspace` is the standalone wrapper: creates its own stack and renders\n * WorkspaceView. Existing consumers (tests, bare embeds) mount this without\n * any chrome.\n */\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { Stage, Layer, Rect, Group } from 'react-konva'\nimport type Konva from 'konva'\n\nimport type { DesignCanvasProps, ExportTriggerOptions } from '../contracts'\nimport { exportPageDataUrl } from '../export'\nimport { createSceneCommandStack } from '../engine/command-stack'\nimport { createSnapEngine, collectGridTargets } from '../engine/snap'\nimport { createZoomPanMath } from '../engine/zoom-pan'\nimport { marqueeSelect, hitTestPoint } from '../engine/selection'\nimport {\n addElementCommand,\n setAttrsCommand,\n multiSetAttrsCommand,\n deleteElementCommand,\n groupElementsCommand,\n ungroupElementCommand,\n} from '../engine/commands'\nimport type { MultiSetAttrsEntry } from '../engine/commands'\nimport { DEFAULT_INSERT_TEMPLATES } from '../insert-builders'\n\nimport { CanvasEmptyState } from './CanvasEmptyState'\nimport { ElementNode } from './ElementNode'\nimport { SelectionLayer } from './SelectionLayer'\nimport { GridLayer } from './GridLayer'\nimport { SnapGuidesOverlay } from './SnapGuidesOverlay'\nimport { InlineTextEditor } from './InlineTextEditor'\nimport { BleedTrimOverlay } from './BleedTrimOverlay'\n\nimport { normalizeMarquee, nudgeDelta as nudgeDeltaMath } from './transform-math'\nimport { elementAabb, findElement, boundsIntersect } from '../../design-canvas/model'\nimport type { SceneElement, ScenePage, TextElement, Bounds } from '../../design-canvas/model'\nimport type { SnapResult, SnapTarget, SceneCommand } from '../contracts'\nimport { lightTheme, type CanvasRenderPalette } from '../../theme/theme'\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SNAP_THRESHOLD_SCREEN_PX = 8\nconst DUPLICATE_OFFSET = 10\nconst ZOOM_FACTOR_WHEEL = 0.998\nconst ZOOM_MIN = 0.05\nconst ZOOM_MAX = 32\n\n// ---------------------------------------------------------------------------\n// Gesture / marquee types\n// ---------------------------------------------------------------------------\n\ninterface MarqueeState {\n active: boolean\n startDocX: number\n startDocY: number\n endDocX: number\n endDocY: number\n}\n\nconst NO_MARQUEE: MarqueeState = { active: false, startDocX: 0, startDocY: 0, endDocX: 0, endDocY: 0 }\n\ntype GestureMode = 'idle' | 'pan' | 'marquee' | 'drag'\n\n// ---------------------------------------------------------------------------\n// WorkspaceViewProps — injectable form (shared stack, guaranteed activePage)\n// ---------------------------------------------------------------------------\n\nexport interface WorkspaceViewProps {\n /** The command stack this view commits gestures through. Must be the same\n * instance the chrome (DesignCanvasEditor) owns so undo/redo and layers-\n * panel selection are coherent. */\n stack: ReturnType<typeof createSceneCommandStack>\n /** Active page resolved before render — WorkspaceView has no conditional\n * hook guards; the caller ensures this is never null. */\n activePage: ScenePage\n canWrite: boolean\n onApplyOperations: DesignCanvasProps['onApplyOperations']\n onSelectionChange?: DesignCanvasProps['onSelectionChange']\n className?: string\n /** Ref the chrome fills with a fit-page callback. The chrome calls it on F /\n * Fit button; when injected via DesignCanvasEditor the ref is shared. */\n onFitRef?: React.MutableRefObject<(() => void) | null>\n /** Host export hook. When set, the workspace fills `onExportRef` with a\n * callback that renders the stage to a data URL and forwards the result. */\n onExport?: DesignCanvasProps['onExport']\n /** Ref the workspace fills with an export callback `(opts) => void`. The\n * chrome's Export control calls it; the workspace owns the Konva stage so it\n * produces the data URL here. Filled only when `onExport` is also set. */\n onExportRef?: React.MutableRefObject<((opts: ExportTriggerOptions) => void) | null>\n /** Fit the active page to the viewport once, on the first non-zero measurement. Default true. */\n fitOnMount?: boolean\n /** Called once after the first real (non-zero) measurement, after the initial fit is applied (or skipped). */\n onReady?(): void\n /** Konva render palette. Omitted → light defaults (byte-identical history). */\n render?: CanvasRenderPalette\n /** Show the branded in-canvas empty state (three doors) while the active page\n * has no elements and the user can write. Default true. Set false to keep a\n * bare blank stage (e.g. when restoring a saved-but-intentionally-empty page). */\n showEmptyState?: boolean\n /** Focus/open the agent from the empty state's \"Ask the agent\" door. When\n * omitted that door is hidden — there is no agent surface to send them to. */\n onAskAgent?(): void\n}\n\n// ---------------------------------------------------------------------------\n// WorkspaceView — all interaction + Konva render; commits through shared stack\n// ---------------------------------------------------------------------------\n\nexport function WorkspaceView({\n canWrite,\n onApplyOperations,\n onSelectionChange,\n className,\n stack,\n activePage,\n onFitRef,\n onExport,\n onExportRef,\n fitOnMount = true,\n onReady,\n render = lightTheme.canvasRender,\n showEmptyState = true,\n onAskAgent,\n}: WorkspaceViewProps) {\n // Re-render when command stack changes state.\n const [, setTick] = useState(0)\n const forceRender = useCallback(() => setTick((t) => t + 1), [])\n useEffect(() => stack.subscribe(forceRender), [stack, forceRender])\n\n const state = stack.getState()\n const { document, activePageId, selectedElementIds, zoom, panX, panY, gridEnabled, gridSize, snapEnabled, showBleed } = state\n\n // -------------------------------------------------------------------------\n // Stable engines\n // -------------------------------------------------------------------------\n\n const zoomPanMath = useMemo(() => createZoomPanMath({ minZoom: ZOOM_MIN, maxZoom: ZOOM_MAX }), [])\n const snapEngine = useMemo(() => createSnapEngine(), [])\n\n // -------------------------------------------------------------------------\n // Container sizing\n // -------------------------------------------------------------------------\n\n const containerRef = useRef<HTMLDivElement>(null)\n const [containerSize, setContainerSize] = useState({ width: 800, height: 600 })\n const hasFittedRef = useRef(false)\n\n useLayoutEffect(() => {\n const el = containerRef.current\n if (!el) return\n const ro = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n const { width, height } = entry.contentRect\n setContainerSize({ width, height })\n // One-shot fit on the first real measurement. The hasFittedRef guard keeps\n // it single-fire across the effect's re-subscriptions (page switch must\n // preserve the user's zoom+pan, not re-fit).\n if (!hasFittedRef.current && width > 0 && height > 0) {\n hasFittedRef.current = true\n if (fitOnMount && activePage.width > 0 && activePage.height > 0) {\n stack.setView(zoomPanMath.fitPage(activePage, { width, height }))\n }\n onReady?.()\n }\n })\n ro.observe(el)\n return () => ro.disconnect()\n }, [activePage, fitOnMount, onReady, stack, zoomPanMath])\n\n const stageRef = useRef<Konva.Stage | null>(null)\n\n // -------------------------------------------------------------------------\n // Fit-page callback — exposed via onFitRef so the chrome can trigger it\n // -------------------------------------------------------------------------\n\n useEffect(() => {\n if (!onFitRef) return\n onFitRef.current = () => {\n const { width, height } = containerSize\n if (width <= 0 || height <= 0) return\n const view = zoomPanMath.fitPage(activePage, { width, height })\n stack.setView(view)\n }\n return () => {\n // Only clear if we still own the slot (multiple WorkspaceView mounts\n // would each try to own it; the last to mount wins, but on unmount we\n // must not clear a ref filled by a newer mount).\n if (onFitRef.current !== null) onFitRef.current = null\n }\n })\n\n // -------------------------------------------------------------------------\n // Export callback — the chrome's Export control calls this through the ref;\n // the workspace owns the stage, so it renders here and forwards the result.\n // -------------------------------------------------------------------------\n\n useEffect(() => {\n if (!onExportRef || !onExport) return\n onExportRef.current = ({ format, pixelRatio }: ExportTriggerOptions) => {\n const stage = stageRef.current\n if (!stage) return\n void (async () => {\n const dataUrl = await exportPageDataUrl(stage, activePage, { format, pixelRatio })\n await onExport({ pageId: activePageId, format, dataUrl, pixelRatio })\n })()\n }\n return () => {\n if (onExportRef.current !== null) onExportRef.current = null\n }\n })\n\n // -------------------------------------------------------------------------\n // Gesture refs\n // -------------------------------------------------------------------------\n\n const gestureRef = useRef<GestureMode>('idle')\n const panOriginRef = useRef({ screenX: 0, screenY: 0, panX: 0, panY: 0 })\n const [marquee, setMarquee] = useState<MarqueeState>(NO_MARQUEE)\n const marqueeRef = useRef<MarqueeState>(NO_MARQUEE)\n const spaceHeldRef = useRef(false)\n // Stores origin position AND AABB dimensions captured at drag start so\n // snap reference points (center, far edge) are correct for any element size.\n const dragOriginRef = useRef<Map<string, { x: number; y: number; width: number; height: number }>>(new Map())\n const [activeSnap, setActiveSnap] = useState<SnapResult | null>(null)\n const [editingElementId, setEditingElementId] = useState<string | null>(null)\n const editingPreRef = useRef<string>('')\n const nudgeHeldRef = useRef<{\n key: string\n ids: string[]\n origin: Map<string, { x: number; y: number }>\n } | null>(null)\n\n // -------------------------------------------------------------------------\n // Persist helper — optimistic execute then remote apply, rollback on throw\n // -------------------------------------------------------------------------\n\n async function persist(command: SceneCommand): Promise<void> {\n if (!canWrite) return\n stack.execute(command)\n try {\n const result = await onApplyOperations(command.operations())\n if (result.document) stack.reset(result.document)\n } catch {\n // Roll back by splicing this specific command from history and applying its\n // inverse to the current state. Using stack.rollback(command) rather than\n // stack.execute(synthetic-rollback) avoids polluting the undo stack with a\n // phantom entry and preserves any commands the user executed while this save\n // was in-flight.\n stack.rollback(command)\n }\n }\n\n // -------------------------------------------------------------------------\n // Wheel zoom-to-cursor\n // -------------------------------------------------------------------------\n\n function handleWheel(e: React.WheelEvent<HTMLDivElement>) {\n e.preventDefault()\n const rect = containerRef.current?.getBoundingClientRect()\n if (!rect) return\n const screenX = e.clientX - rect.left\n const screenY = e.clientY - rect.top\n const factor = Math.pow(ZOOM_FACTOR_WHEEL, e.deltaY)\n const next = zoomPanMath.zoomAtPoint({ zoom, panX, panY }, factor, screenX, screenY)\n stack.setView(next)\n }\n\n // -------------------------------------------------------------------------\n // Pointer handlers\n // -------------------------------------------------------------------------\n\n function handlePointerDown(e: React.PointerEvent<HTMLDivElement>) {\n if (e.button === 1 || spaceHeldRef.current) {\n e.preventDefault()\n ;(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId)\n gestureRef.current = 'pan'\n panOriginRef.current = { screenX: e.clientX, screenY: e.clientY, panX, panY }\n return\n }\n if (e.button !== 0) return\n\n const rect = containerRef.current?.getBoundingClientRect()\n if (!rect) return\n const screenX = e.clientX - rect.left\n const screenY = e.clientY - rect.top\n const docPos = zoomPanMath.screenToDocument({ zoom, panX, panY }, screenX, screenY)\n\n // Element-vs-empty-space is decided against the scene model in document\n // space, not Konva's hit-graph canvas. The hit canvas misclassifies presses\n // over elements (clip group, listening:false background, redraw lag right\n // after a pan/zoom setView), which silently routed an element press into the\n // marquee branch — the element drag never started. A model hit means: let\n // Konva's draggable node begin the move; only empty space starts a marquee.\n if (hitTestPoint(activePage, docPos.x, docPos.y) !== null) {\n return\n }\n\n e.preventDefault()\n ;(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId)\n gestureRef.current = 'marquee'\n const m: MarqueeState = { active: true, startDocX: docPos.x, startDocY: docPos.y, endDocX: docPos.x, endDocY: docPos.y }\n marqueeRef.current = m\n setMarquee(m)\n stack.setView({ selectedElementIds: [] })\n }\n\n function handlePointerMove(e: React.PointerEvent<HTMLDivElement>) {\n const mode = gestureRef.current\n const rect = containerRef.current?.getBoundingClientRect()\n if (!rect) return\n\n if (mode === 'pan') {\n const dx = e.clientX - panOriginRef.current.screenX\n const dy = e.clientY - panOriginRef.current.screenY\n stack.setView({ panX: panOriginRef.current.panX + dx, panY: panOriginRef.current.panY + dy })\n return\n }\n\n if (mode === 'marquee') {\n const screenX = e.clientX - rect.left\n const screenY = e.clientY - rect.top\n const docPos = zoomPanMath.screenToDocument({ zoom, panX, panY }, screenX, screenY)\n const m: MarqueeState = { ...marqueeRef.current, active: true, endDocX: docPos.x, endDocY: docPos.y }\n marqueeRef.current = m\n setMarquee(m)\n const normalized = normalizeMarquee(m.startDocX, m.startDocY, m.endDocX, m.endDocY)\n const ids = marqueeSelect(activePage, normalized)\n stack.setView({ selectedElementIds: ids })\n }\n }\n\n function handlePointerUp(e: React.PointerEvent<HTMLDivElement>) {\n gestureRef.current = 'idle'\n setMarquee(NO_MARQUEE)\n marqueeRef.current = NO_MARQUEE\n setActiveSnap(null)\n ;(e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId)\n }\n\n // -------------------------------------------------------------------------\n // Element drag callbacks\n // -------------------------------------------------------------------------\n\n function handleElementDragStart(elementId: string) {\n if (!canWrite) return\n const ids = selectedElementIds.includes(elementId) ? selectedElementIds : [elementId]\n const origins = new Map<string, { x: number; y: number; width: number; height: number }>()\n for (const id of ids) {\n const found = findElement(activePage, id)\n if (found) {\n const aabb = elementAabb(found.element)\n origins.set(id, { x: found.element.x, y: found.element.y, width: aabb.width, height: aabb.height })\n }\n }\n dragOriginRef.current = origins\n gestureRef.current = 'drag'\n if (!selectedElementIds.includes(elementId)) {\n stack.setView({ selectedElementIds: [elementId] })\n }\n }\n\n function handleElementDragMove(elementId: string, dx: number, dy: number) {\n if (!canWrite) return\n const origin = dragOriginRef.current.get(elementId)\n if (!origin) return\n const dragging = selectedElementIds.includes(elementId) ? selectedElementIds : [elementId]\n const proposedX = origin.x + dx\n const proposedY = origin.y + dy\n // Use the actual element AABB dimensions so center and far-edge snap points\n // are correct for elements of any size (not the former hardcoded 100×100).\n const snapBounds = { x: proposedX, y: proposedY, width: origin.width, height: origin.height }\n\n if (snapEnabled) {\n const targets = snapEngine.collectTargets(state, dragging)\n if (gridEnabled) {\n const thresholdDoc = SNAP_THRESHOLD_SCREEN_PX / zoom\n const gt = collectGridTargets(snapBounds, gridSize, activePage, thresholdDoc)\n targets.vertical.push(...gt.vertical)\n targets.horizontal.push(...gt.horizontal)\n }\n const snapResult = snapEngine.apply(snapBounds, targets, SNAP_THRESHOLD_SCREEN_PX, zoom)\n setActiveSnap(snapResult)\n }\n }\n\n async function handleElementDragEnd(elementId: string, finalX: number, finalY: number) {\n if (!canWrite) return\n gestureRef.current = 'idle'\n setActiveSnap(null)\n\n const origin = dragOriginRef.current.get(elementId)\n if (!origin) return\n\n const draggingIds = selectedElementIds.includes(elementId) ? selectedElementIds : [elementId]\n\n // Re-run snapping for the drop position so the PERSISTED coordinates match the\n // guide the user saw mid-drag. Without this the element lands at the raw\n // pointer position and the snap line is purely cosmetic.\n let snappedX = finalX\n let snappedY = finalY\n if (snapEnabled) {\n const snapBounds = { x: finalX, y: finalY, width: origin.width, height: origin.height }\n const targets = snapEngine.collectTargets(state, draggingIds)\n if (gridEnabled) {\n const thresholdDoc = SNAP_THRESHOLD_SCREEN_PX / zoom\n const gt = collectGridTargets(snapBounds, gridSize, activePage, thresholdDoc)\n targets.vertical.push(...gt.vertical)\n targets.horizontal.push(...gt.horizontal)\n }\n const snapResult = snapEngine.apply(snapBounds, targets, SNAP_THRESHOLD_SCREEN_PX, zoom)\n snappedX = snapResult.x\n snappedY = snapResult.y\n }\n\n const dx = snappedX - origin.x\n const dy = snappedY - origin.y\n\n if (draggingIds.length === 1) {\n const el = findElement(activePage, elementId)?.element\n if (!el) return\n await persist(\n setAttrsCommand({\n pageId: activePageId,\n elementId,\n attrs: { x: snappedX, y: snappedY },\n priorAttrs: { x: el.x, y: el.y },\n }),\n )\n } else {\n const entries: MultiSetAttrsEntry[] = []\n for (const id of draggingIds) {\n const el = findElement(activePage, id)?.element\n const orig = dragOriginRef.current.get(id)\n if (!el || !orig) continue\n entries.push({\n pageId: activePageId,\n elementId: id,\n attrs: { x: orig.x + dx, y: orig.y + dy },\n priorAttrs: { x: orig.x, y: orig.y },\n })\n }\n if (entries.length > 0) await persist(multiSetAttrsCommand(entries))\n }\n\n dragOriginRef.current = new Map()\n }\n\n // -------------------------------------------------------------------------\n // Element interactions\n // -------------------------------------------------------------------------\n\n function handleElementClick(elementId: string) {\n stack.setView({ selectedElementIds: [elementId] })\n }\n\n function handleElementDoubleClick(elementId: string) {\n if (!canWrite) return\n const found = findElement(activePage, elementId)\n if (!found || found.element.kind !== 'text') return\n editingPreRef.current = (found.element as TextElement).text\n setEditingElementId(elementId)\n }\n\n async function handleTextCommit(text: string) {\n const id = editingElementId\n setEditingElementId(null)\n if (!id || !canWrite) return\n const found = findElement(activePage, id)\n if (!found || found.element.kind !== 'text') return\n if (text === editingPreRef.current) return\n await persist(\n setAttrsCommand({\n pageId: activePageId,\n elementId: id,\n attrs: { text },\n priorAttrs: { text: editingPreRef.current },\n }),\n )\n }\n\n function handleTextCancel() {\n setEditingElementId(null)\n }\n\n // -------------------------------------------------------------------------\n // Transformer end\n // -------------------------------------------------------------------------\n\n async function handleTransformEnd(entries: MultiSetAttrsEntry[]) {\n if (!canWrite || entries.length === 0) return\n await persist(multiSetAttrsCommand(entries))\n }\n\n // -------------------------------------------------------------------------\n // Empty-state doors — both insert real content through the shared stack, so\n // they need no host wiring and are immediately undoable like any other edit.\n // -------------------------------------------------------------------------\n\n async function handleStartTemplate() {\n if (!canWrite) return\n const template = DEFAULT_INSERT_TEMPLATES[0]\n if (!template) return\n const ops = template.build({\n pageId: activePageId,\n width: activePage.width,\n height: activePage.height,\n background: activePage.background,\n })\n const addedIds: string[] = []\n for (const op of ops) {\n if (op.type === 'add_element') {\n await persist(addElementCommand({ pageId: op.pageId, element: op.element }))\n addedIds.push(op.element.id)\n }\n }\n if (addedIds.length > 0) stack.setView({ selectedElementIds: addedIds })\n }\n\n async function handleAddElement() {\n if (!canWrite) return\n const width = Math.min(420, Math.round(activePage.width * 0.6)) || 320\n const element: SceneElement = {\n id: crypto.randomUUID(),\n kind: 'text',\n name: 'Text',\n x: Math.max(0, Math.round((activePage.width - width) / 2)),\n y: Math.max(0, Math.round(activePage.height / 2 - 24)),\n rotation: 0,\n opacity: 1,\n locked: false,\n visible: true,\n width,\n text: 'Double-click to edit',\n fontFamily: 'Inter',\n fontSize: 32,\n fontStyle: 'normal',\n fill: '#111827',\n align: 'left',\n lineHeight: 1.2,\n letterSpacing: 0,\n }\n await persist(addElementCommand({ pageId: activePageId, element }))\n stack.setView({ selectedElementIds: [element.id] })\n }\n\n // -------------------------------------------------------------------------\n // Keyboard\n // -------------------------------------------------------------------------\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {\n if (e.key === ' ') {\n e.preventDefault()\n spaceHeldRef.current = true\n return\n }\n\n const mod = e.metaKey || e.ctrlKey\n\n if (e.key === 'Escape') {\n e.preventDefault()\n if (gestureRef.current !== 'idle') {\n gestureRef.current = 'idle'\n setMarquee(NO_MARQUEE)\n setActiveSnap(null)\n } else if (editingElementId) {\n setEditingElementId(null)\n } else {\n stack.setView({ selectedElementIds: [] })\n }\n return\n }\n\n if (editingElementId) return\n\n if ((e.key === 'Delete' || e.key === 'Backspace') && selectedElementIds.length > 0) {\n e.preventDefault()\n // Stop the event reaching DesignCanvas's window-level keydown, which also\n // handles delete/undo/redo — without this the editor handles the key twice\n // (delete two history steps, undo twice) when Workspace mounts inside it.\n e.stopPropagation()\n for (const id of [...selectedElementIds]) {\n try {\n persist(deleteElementCommand({ document, pageId: activePageId, elementId: id }))\n } catch { /* element gone — skip */ }\n }\n stack.setView({ selectedElementIds: [] })\n return\n }\n\n if (mod && e.key === 'z' && !e.shiftKey) {\n e.preventDefault()\n e.stopPropagation()\n if (stack.canUndo()) stack.undo()\n return\n }\n if (mod && ((e.key === 'z' && e.shiftKey) || e.key === 'y')) {\n e.preventDefault()\n e.stopPropagation()\n if (stack.canRedo()) stack.redo()\n return\n }\n\n if (mod && e.key === 'a') {\n e.preventDefault()\n const ids = activePage.elements.filter((el) => !el.locked && el.visible).map((el) => el.id)\n stack.setView({ selectedElementIds: ids })\n return\n }\n\n if (mod && e.key === 'd' && selectedElementIds.length > 0) {\n e.preventDefault()\n for (const id of selectedElementIds) {\n const found = findElement(activePage, id)\n if (!found) continue\n const clone: SceneElement = {\n ...structuredClone(found.element),\n id: crypto.randomUUID(),\n x: found.element.x + DUPLICATE_OFFSET,\n y: found.element.y + DUPLICATE_OFFSET,\n }\n persist(addElementCommand({ pageId: activePageId, element: clone }))\n }\n return\n }\n\n if (mod && !e.shiftKey && e.key === 'g' && selectedElementIds.length >= 2) {\n e.preventDefault()\n persist(groupElementsCommand({\n document,\n pageId: activePageId,\n elementIds: selectedElementIds,\n groupId: crypto.randomUUID(),\n }))\n return\n }\n\n if (mod && e.shiftKey && e.key === 'g' && selectedElementIds.length === 1) {\n e.preventDefault()\n const id = selectedElementIds[0]!\n persist(ungroupElementCommand({ document, pageId: activePageId, groupId: id }))\n return\n }\n\n if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key) && selectedElementIds.length > 0) {\n e.preventDefault()\n const key = e.key as 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown'\n if (nudgeHeldRef.current && nudgeHeldRef.current.key !== key) {\n flushNudge()\n }\n if (!nudgeHeldRef.current) {\n const origins = new Map<string, { x: number; y: number }>()\n for (const id of selectedElementIds) {\n const found = findElement(activePage, id)\n if (found) origins.set(id, { x: found.element.x, y: found.element.y })\n }\n nudgeHeldRef.current = { key, ids: selectedElementIds.slice(), origin: origins }\n }\n // Apply nudge optimistically each keydown via the command stack so the\n // user sees live movement; the held ref lets us coalesce all steps into\n // one undo entry on keyup.\n const { dx, dy } = nudgeDeltaMath(key, e.shiftKey)\n const entries: MultiSetAttrsEntry[] = []\n for (const [id, orig] of nudgeHeldRef.current.origin) {\n const found = findElement(activePage, id)\n if (!found) continue\n entries.push({\n pageId: activePageId,\n elementId: id,\n attrs: { x: found.element.x + dx, y: found.element.y + dy },\n priorAttrs: { x: found.element.x, y: found.element.y },\n })\n }\n if (entries.length > 0) {\n // Preview: silent local apply without remote persist (flushed on keyup).\n stack.execute(multiSetAttrsCommand(entries))\n }\n }\n }\n\n function handleKeyUp(e: React.KeyboardEvent<HTMLDivElement>) {\n if (e.key === ' ') {\n spaceHeldRef.current = false\n return\n }\n if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {\n flushNudge()\n }\n }\n\n function flushNudge() {\n const held = nudgeHeldRef.current\n if (!held) return\n nudgeHeldRef.current = null\n if (!canWrite || held.ids.length === 0) return\n\n // Build a single coalesced set_attrs from original positions to current.\n const entries: MultiSetAttrsEntry[] = []\n for (const id of held.ids) {\n const orig = held.origin.get(id)\n const found = findElement(activePage, id)\n if (!orig || !found) continue\n entries.push({\n pageId: activePageId,\n elementId: id,\n attrs: { x: found.element.x, y: found.element.y },\n priorAttrs: { x: orig.x, y: orig.y },\n })\n }\n if (entries.length > 0) {\n // Persist the coalesced nudge; the intermediate stack entries from\n // keydown previews become part of the committed ops batch.\n onApplyOperations(multiSetAttrsCommand(entries).operations()).catch(() => {\n // Roll back if remote rejects.\n for (const [id, orig] of held.origin) {\n const found = findElement(activePage, id)\n if (!found) continue\n persist(setAttrsCommand({\n pageId: activePageId,\n elementId: id,\n attrs: { x: orig.x, y: orig.y },\n priorAttrs: { x: found.element.x, y: found.element.y },\n }))\n }\n })\n }\n }\n\n // -------------------------------------------------------------------------\n // Notify host of selection changes\n // -------------------------------------------------------------------------\n\n useEffect(() => {\n if (!onSelectionChange) return\n const elements = selectedElementIds\n .map((id) => findElement(activePage, id)?.element)\n .filter((el): el is SceneElement => !!el)\n onSelectionChange(elements)\n }, [selectedElementIds, activePage, onSelectionChange])\n\n // -------------------------------------------------------------------------\n // Derived values for render\n // -------------------------------------------------------------------------\n\n const selectedElements = useMemo(\n () =>\n selectedElementIds\n .map((id) => findElement(activePage, id)?.element)\n .filter((el): el is SceneElement => !!el),\n [selectedElementIds, activePage],\n )\n\n const editingTextElement = editingElementId\n ? (findElement(activePage, editingElementId)?.element as TextElement | undefined) ?? null\n : null\n\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio : 1\n\n const pageScreenX = panX\n const pageScreenY = panY\n const pageScreenW = activePage.width * zoom\n const pageScreenH = activePage.height * zoom\n\n const activeVerticalGuide: SnapTarget | null = activeSnap?.activeVertical ?? null\n const activeHorizontalGuide: SnapTarget | null = activeSnap?.activeHorizontal ?? null\n\n const normalizedMarquee = marquee.active\n ? normalizeMarquee(marquee.startDocX, marquee.startDocY, marquee.endDocX, marquee.endDocY)\n : null\n\n // -------------------------------------------------------------------------\n // Stable element-handler identities\n //\n // WorkspaceView re-renders on every `stack.notify()` (~120/s during a pan or\n // marquee). The element handlers below are fresh closures each render, so\n // passing them straight to ElementNode would change every node's props every\n // frame and defeat React.memo — all N nodes would re-render per pointermove.\n // We hold the latest closures in a ref and hand ElementNode stable wrappers\n // (identity fixed for the component's lifetime) that read through the ref, so\n // memoized nodes see byte-identical handler props frame to frame.\n // -------------------------------------------------------------------------\n\n const handlerImplRef = useRef({\n onClick: handleElementClick,\n onDragStart: handleElementDragStart,\n onDragMove: handleElementDragMove,\n onDragEnd: handleElementDragEnd,\n onDoubleClick: handleElementDoubleClick,\n })\n handlerImplRef.current = {\n onClick: handleElementClick,\n onDragStart: handleElementDragStart,\n onDragMove: handleElementDragMove,\n onDragEnd: handleElementDragEnd,\n onDoubleClick: handleElementDoubleClick,\n }\n\n const onElementClick = useCallback((id: string) => handlerImplRef.current.onClick(id), [])\n const onElementDragStart = useCallback((id: string) => handlerImplRef.current.onDragStart(id), [])\n const onElementDragMove = useCallback(\n (id: string, dx: number, dy: number) => handlerImplRef.current.onDragMove(id, dx, dy),\n [],\n )\n const onElementDragEnd = useCallback(\n (id: string, x: number, y: number) => handlerImplRef.current.onDragEnd(id, x, y),\n [],\n )\n const onElementDoubleClick = useCallback((id: string) => handlerImplRef.current.onDoubleClick(id), [])\n\n // -------------------------------------------------------------------------\n // Page-bounds viewport culling\n //\n // Skip rendering elements whose AABB is FULLY outside the page rect. This is\n // PAGE-bounds culling, never SCREEN-viewport culling: export.ts rasterizes\n // whatever nodes are on the stage and crops to the page rect, so a node that\n // is on-page but off-screen MUST stay mounted or it would silently vanish\n // from exported PNGs. Only off-page elements (which export never captures)\n // are dropped. A pinned id (selected or being edited) is never culled so the\n // transformer/inline editor always has its node, even if dragged off-page.\n // -------------------------------------------------------------------------\n\n const visibleElements = useMemo(() => {\n const pageRect: Bounds = { x: 0, y: 0, width: activePage.width, height: activePage.height }\n const pinned = new Set<string>(selectedElementIds)\n if (editingElementId) pinned.add(editingElementId)\n return activePage.elements.filter(\n (el) => pinned.has(el.id) || boundsIntersect(elementAabb(el), pageRect),\n )\n }, [activePage.elements, activePage.width, activePage.height, selectedElementIds, editingElementId])\n\n // -------------------------------------------------------------------------\n // Render\n // -------------------------------------------------------------------------\n\n return (\n <div\n className={`design-canvas-workspace relative overflow-hidden bg-[var(--canvas-backdrop,#1a1a1a)] outline-none ${className ?? ''}`}\n ref={containerRef}\n tabIndex={0}\n onWheel={handleWheel}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onKeyDown={handleKeyDown}\n onKeyUp={handleKeyUp}\n // touch-action:none lets pointer pan/marquee/transform own the gesture on\n // touch devices — without it the browser's native scroll/pinch fights every drag.\n style={{ cursor: spaceHeldRef.current ? 'grab' : 'default', touchAction: 'none' }}\n >\n {/* Konva Stage */}\n <Stage\n ref={stageRef}\n width={containerSize.width}\n height={containerSize.height}\n pixelRatio={dpr}\n listening={true}\n >\n {/* Grid beneath content */}\n {gridEnabled && (\n <GridLayer\n pageWidth={activePage.width}\n pageHeight={activePage.height}\n gridSize={gridSize}\n zoom={zoom}\n panX={panX}\n panY={panY}\n color={render.grid}\n />\n )}\n\n {/* Page content */}\n <Layer>\n <Group x={panX} y={panY} scaleX={zoom} scaleY={zoom}>\n <Rect\n name=\"page-background\"\n x={0}\n y={0}\n width={activePage.width}\n height={activePage.height}\n fill={activePage.background}\n shadowColor=\"rgba(0,0,0,0.4)\"\n shadowBlur={24 / zoom}\n shadowOffset={{ x: 0, y: 4 / zoom }}\n listening={false}\n />\n <Group\n clipX={0}\n clipY={0}\n clipWidth={activePage.width}\n clipHeight={activePage.height}\n >\n {visibleElements.map((element) => (\n <ElementNode\n key={element.id}\n element={element}\n isSelected={selectedElementIds.includes(element.id)}\n zoom={zoom}\n render={render}\n onClick={onElementClick}\n onDragStart={onElementDragStart}\n onDragMove={onElementDragMove}\n onDragEnd={onElementDragEnd}\n onDoubleClick={onElementDoubleClick}\n />\n ))}\n </Group>\n </Group>\n </Layer>\n\n {/* Snap guides — same doc-space coordinate group */}\n {(activeVerticalGuide || activeHorizontalGuide) && (\n <Layer>\n <Group x={panX} y={panY} scaleX={zoom} scaleY={zoom}>\n <SnapGuidesOverlay\n pageWidth={activePage.width}\n pageHeight={activePage.height}\n activeVertical={activeVerticalGuide}\n activeHorizontal={activeHorizontalGuide}\n zoom={zoom}\n render={render}\n />\n </Group>\n </Layer>\n )}\n\n {/* Transformer / selection handles */}\n <SelectionLayer\n stageRef={stageRef}\n selectedIds={selectedElementIds}\n selectedElements={selectedElements}\n canWrite={canWrite}\n onTransformEnd={handleTransformEnd}\n pageId={activePageId}\n render={render}\n />\n </Stage>\n\n {/* Bleed overlay */}\n {showBleed && activePage.bleed && (\n <div\n className=\"pointer-events-none absolute\"\n style={{ left: pageScreenX, top: pageScreenY, width: pageScreenW, height: pageScreenH }}\n >\n <BleedTrimOverlay\n pageWidthPx={pageScreenW}\n pageHeightPx={pageScreenH}\n bleed={{\n top: activePage.bleed.top * zoom,\n right: activePage.bleed.right * zoom,\n bottom: activePage.bleed.bottom * zoom,\n left: activePage.bleed.left * zoom,\n }}\n />\n </div>\n )}\n\n {/* Marquee rect */}\n {normalizedMarquee && (\n <div\n className=\"pointer-events-none absolute border border-blue-400 bg-blue-400/10\"\n style={{\n left: panX + normalizedMarquee.x * zoom,\n top: panY + normalizedMarquee.y * zoom,\n width: normalizedMarquee.width * zoom,\n height: normalizedMarquee.height * zoom,\n }}\n />\n )}\n\n {/* Inline text editor */}\n {editingTextElement && (\n <InlineTextEditor\n element={editingTextElement}\n zoom={zoom}\n panX={panX}\n panY={panY}\n onCommit={handleTextCommit}\n onCancel={handleTextCancel}\n />\n )}\n\n {/* Empty-state doors — only while the page is blank, the user can write,\n and no gesture is mid-flight (so it never covers a live marquee). */}\n {showEmptyState && canWrite && activePage.elements.length === 0 && !editingElementId ? (\n <CanvasEmptyState\n onStartTemplate={() => void handleStartTemplate()}\n onAddElement={() => void handleAddElement()}\n onAskAgent={onAskAgent}\n />\n ) : null}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Workspace — standalone wrapper for bare embeds and tests (no chrome)\n// ---------------------------------------------------------------------------\n\n/**\n * Self-contained Konva workspace that creates its own command stack. Mount\n * this when you want the canvas without the toolbar/rulers/pages-strip chrome.\n *\n * Products that want the full editor (chrome + workspace sharing one stack)\n * should mount `DesignCanvasEditor` instead.\n */\nexport function Workspace(props: DesignCanvasProps) {\n // Stack outlives WorkspaceView re-mounts; created once from the initial doc.\n const stackRef = useRef(\n createSceneCommandStack(\n props.document,\n props.document.pages[0]?.id ?? '',\n ),\n )\n\n const [, setTick] = useState(0)\n const forceRender = useCallback(() => setTick((t) => t + 1), [])\n\n useEffect(() => {\n return stackRef.current.subscribe(forceRender)\n }, [forceRender])\n\n // Rebase on server refresh (host changes rev + document together).\n const prevRevRef = useRef(props.rev)\n useEffect(() => {\n if (props.rev !== prevRevRef.current) {\n prevRevRef.current = props.rev\n stackRef.current.reset(props.document)\n }\n }, [props.rev, props.document])\n\n const state = stackRef.current.getState()\n const activePage =\n state.document.pages.find((p) => p.id === state.activePageId) ??\n state.document.pages[0]\n\n // No pages — document is empty shell, nothing to render.\n if (!activePage) return null\n\n return (\n <WorkspaceView\n stack={stackRef.current}\n activePage={activePage}\n canWrite={props.canWrite}\n onApplyOperations={props.onApplyOperations}\n onSelectionChange={props.onSelectionChange}\n className={props.className}\n render={props.render}\n />\n )\n}\n","/**\n * Client-side export for design-canvas pages: raster (PNG/JPEG via Konva\n * stage.toDataURL) and JSON (document serialisation).\n *\n * The heavy lifting — crop math, pixel ratio resolution, CORS taint detection\n * — lives in export-math.ts where it can be unit-tested without a canvas\n * context. This file is the thin Konva-wiring layer: hide overlays, call\n * toDataURL, restore, handle the SecurityError.\n *\n * Konva is an OPTIONAL peer. Import this file only from browser-context code\n * that has konva wired in. The types are written against a minimal structural\n * interface so the file compiles even when konva's types are absent.\n */\n\nimport type { SceneDocument, ScenePage } from '../design-canvas/model'\nimport { SCENE_SCHEMA_VERSION } from '../design-canvas/model'\nimport type { ExportPreset } from '../design-canvas/export-presets'\nimport {\n documentCropToStageCoords,\n identifyTaintedSrc,\n isExportHiddenNodeName,\n resolveExportParams,\n} from './export-math'\n\n// ---------------------------------------------------------------------------\n// Minimal structural interfaces for Konva stage / node\n// (avoids a hard konva import; the real Konva types satisfy these)\n// ---------------------------------------------------------------------------\n\ninterface KonvaNodeLike {\n name(): string\n visible(): boolean\n visible(v: boolean): void\n getAttr(key: string): unknown\n}\n\ninterface KonvaLayerLike {\n getChildren(): KonvaNodeLike[]\n}\n\ninterface KonvaStageLike {\n scaleX(): number\n scaleY(): number\n x(): number\n y(): number\n getLayers(): KonvaLayerLike[]\n toDataURL(params: {\n mimeType: string\n quality?: number\n pixelRatio: number\n x: number\n y: number\n width: number\n height: number\n }): string\n}\n\n// ---------------------------------------------------------------------------\n// Raster export\n// ---------------------------------------------------------------------------\n\nexport interface ExportPageDataUrlOptions {\n format: 'png' | 'jpeg'\n pixelRatio?: number\n includeBleed?: boolean\n preset?: ExportPreset\n}\n\n/**\n * Render a single page to a data URL.\n *\n * The function temporarily hides every node whose name starts with 'overlay:'\n * plus any Transformer node, computes the crop rect and pixel ratio from the\n * page model and options, calls stage.toDataURL, then restores all prior view\n * state exactly — zoom, pan, and node visibility.\n *\n * Rejects with a descriptive error when a CORS-tainted image source causes\n * the SecurityError, naming the offending src so the caller can surface it.\n *\n * The `stage` argument must be the Konva stage with the page content already\n * rendered. The caller is responsible for ensuring all async image loads have\n * settled before calling this function.\n */\nexport async function exportPageDataUrl(\n stage: KonvaStageLike,\n page: ScenePage,\n opts: ExportPageDataUrlOptions,\n): Promise<string> {\n const { cropRect, pixelRatio, mimeType, quality } = resolveExportParams(page, opts)\n\n // Collect all image node srcs BEFORE hiding anything, so the taint detector\n // has data even if the SecurityError fires mid-render.\n const imageSrcs = collectImageSrcs(stage)\n\n // Save state: which nodes we are hiding (visibility that was true → set false).\n const hiddenNodes: KonvaNodeLike[] = []\n\n for (const layer of stage.getLayers()) {\n for (const node of layer.getChildren()) {\n const name = node.name()\n if (isExportHiddenNodeName(name) && node.visible()) {\n node.visible(false)\n hiddenNodes.push(node)\n }\n }\n }\n\n // Compute the stage-coordinate crop rect for toDataURL.\n const stageScale = stage.scaleX()\n const stageCrop = documentCropToStageCoords(cropRect, stageScale, stage.x(), stage.y())\n\n let dataUrl: string\n try {\n dataUrl = stage.toDataURL({\n mimeType,\n ...(quality !== undefined ? { quality } : {}),\n pixelRatio,\n x: stageCrop.x,\n y: stageCrop.y,\n width: stageCrop.width,\n height: stageCrop.height,\n })\n } catch (err) {\n // Restore visibility before rethrowing so the editor is not left in a\n // broken state.\n for (const node of hiddenNodes) {\n node.visible(true)\n }\n\n if (err instanceof Error && err.name === 'SecurityError') {\n const taintedSrc = identifyTaintedSrc(imageSrcs)\n if (taintedSrc !== null) {\n throw new Error(\n `Export failed: image source is CORS-tainted and cannot be read by the canvas. ` +\n `Offending src: \"${taintedSrc}\". ` +\n `Ensure the image is served with Access-Control-Allow-Origin or use a proxied /api/ path.`,\n )\n }\n throw new Error(\n `Export failed: a canvas SecurityError occurred but no cross-origin image src could be identified. ` +\n `The stage may contain a tainted video or image loaded without CORS headers.`,\n )\n }\n throw err\n }\n\n // Restore visibility.\n for (const node of hiddenNodes) {\n node.visible(true)\n }\n\n return dataUrl\n}\n\n/** Collect all image-like nodes from the stage for taint identification. */\nfunction collectImageSrcs(stage: KonvaStageLike): Array<{ name: string; src: string }> {\n const result: Array<{ name: string; src: string }> = []\n for (const layer of stage.getLayers()) {\n for (const node of layer.getChildren()) {\n const src = node.getAttr('src')\n if (typeof src === 'string' && src.length > 0) {\n result.push({ name: node.name(), src })\n }\n }\n }\n return result\n}\n\n// ---------------------------------------------------------------------------\n// JSON export\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a scene document to pretty-printed JSON with schemaVersion\n * asserted. Throws when the document's schemaVersion does not match\n * SCENE_SCHEMA_VERSION — the caller must not smuggle stale documents through.\n */\nexport function exportDocumentJson(document: SceneDocument): string {\n if (document.schemaVersion !== SCENE_SCHEMA_VERSION) {\n throw new Error(\n `exportDocumentJson: document schemaVersion is ${document.schemaVersion}, ` +\n `expected ${SCENE_SCHEMA_VERSION} — upgrade the document before exporting`,\n )\n }\n return JSON.stringify(document, null, 2)\n}\n\n// ---------------------------------------------------------------------------\n// Browser download helper\n// ---------------------------------------------------------------------------\n\n/**\n * Trigger a browser download for a data URL. Safe to import in SSR — the\n * function is a no-op when `document` is not defined (e.g. server-side render\n * or test environment without a DOM). The integrator must not rely on the\n * download executing in those contexts.\n */\nexport function downloadDataUrl(dataUrl: string, filename: string): void {\n if (typeof globalThis.document === 'undefined') return\n\n const a = globalThis.document.createElement('a')\n a.href = dataUrl\n a.download = filename\n a.style.display = 'none'\n globalThis.document.body.appendChild(a)\n a.click()\n globalThis.document.body.removeChild(a)\n}\n","/**\n * In-canvas empty state — the first move on a blank design. Shown by\n * WorkspaceView as a centered overlay while the active page has no user\n * content, so a fresh canvas reads as \"ready to start\" rather than \"broken or\n * empty\". Three doors cover the two ways people begin and the agent path:\n * - Start with a template — drop a starter element via the same command stack.\n * - Add an element — insert a text block by hand.\n * - Ask the agent — surfaced only when the host wires `onAskAgent` (an agent\n * panel exists); otherwise the door is omitted rather than rendered dead.\n *\n * Token-styled only (CSS-var tokens + Tailwind semantic names) and stamped with\n * the Tangle mark so the blank state is on-brand. Pointer-events stay scoped to\n * the card: clicking the surrounding canvas still starts a marquee.\n */\n\nimport type { ReactElement } from 'react'\nimport { BrandKnot } from './BrandKnot'\nimport { PlusGlyph, ShapesGlyph } from './glyphs'\n\ninterface SparkleProps {\n className?: string\n}\n\nfunction SparkleGlyph({ className }: SparkleProps) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 3v3m0 12v3M3 12h3m12 0h3M5.6 5.6l2.1 2.1m8.6 8.6 2.1 2.1m0-12.8-2.1 2.1M7.7 16.3l-2.1 2.1\" />\n </svg>\n )\n}\n\nexport interface CanvasEmptyStateProps {\n /** Drop a starter template element through the command stack. */\n onStartTemplate(): void\n /** Insert a single editable element by hand. */\n onAddElement(): void\n /** Focus the agent panel / open the agent. Omitted → the door is hidden. */\n onAskAgent?(): void\n /** Heading copy. Overridable; defaults to the cold-open prompt. */\n title?: string\n /** Supporting line under the heading. Overridable. */\n subtitle?: string\n className?: string\n}\n\ninterface Door {\n key: string\n label: string\n hint: string\n Icon: (p: { className?: string }) => ReactElement\n onClick(): void\n}\n\n/**\n * The three-door empty state. Each door is a real action wired to the editor's\n * command stack (or the agent), never a placeholder.\n */\nexport function CanvasEmptyState({\n onStartTemplate,\n onAddElement,\n onAskAgent,\n title = 'Start your design',\n subtitle = 'Drop in a template, add an element by hand, or let the agent draft it for you.',\n className,\n}: CanvasEmptyStateProps) {\n const doors: Door[] = [\n {\n key: 'template',\n label: 'Start with a template',\n hint: 'A headline, shape, or block to build on',\n Icon: ShapesGlyph,\n onClick: onStartTemplate,\n },\n {\n key: 'element',\n label: 'Add an element',\n hint: 'Place text on the page and edit it',\n Icon: PlusGlyph,\n onClick: onAddElement,\n },\n ]\n if (onAskAgent) {\n doors.push({\n key: 'agent',\n label: 'Ask the agent',\n hint: 'Describe what you want made',\n Icon: SparkleGlyph,\n onClick: onAskAgent,\n })\n }\n\n return (\n // Overlay is non-interactive so empty-space clicks reach the canvas; the\n // centered card re-enables pointer events for its own controls.\n <div\n className={`pointer-events-none absolute inset-0 z-20 flex items-center justify-center p-4 ${className ?? ''}`}\n role=\"group\"\n aria-label=\"Start your design\"\n >\n <div className=\"pointer-events-auto flex w-full max-w-lg flex-col items-center gap-5 rounded-xl border border-[var(--border-default)] bg-[var(--bg-input)]/95 px-6 py-7 text-center shadow-xl backdrop-blur sm:px-8\">\n <span className=\"flex items-center gap-2 text-[var(--text-muted)]\">\n <BrandKnot size={22} className=\"shrink-0\" />\n <span className=\"text-[11px] font-medium uppercase tracking-[0.14em]\">Tangle Design</span>\n </span>\n\n <div className=\"flex flex-col gap-1.5\">\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">{title}</h2>\n <p className=\"text-sm leading-5 text-[var(--text-secondary)]\">{subtitle}</p>\n </div>\n\n <div className=\"grid w-full gap-2 sm:grid-cols-3\">\n {doors.map(({ key, label, hint, Icon, onClick }) => (\n <button\n key={key}\n type=\"button\"\n onClick={onClick}\n className=\"group flex min-h-[44px] flex-col items-center gap-2 rounded-lg border border-[var(--border-default)] bg-[hsl(var(--card))] px-3 py-4 text-center transition-colors hover:border-[var(--brand-primary)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--ring))] focus-visible:ring-offset-1 focus-visible:ring-offset-[var(--bg-input)]\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-[var(--brand-primary)]/10 text-[var(--brand-primary)] transition-colors group-hover:bg-[var(--brand-primary)]/15\">\n <Icon className=\"h-4 w-4\" />\n </span>\n <span className=\"text-[13px] font-medium text-[var(--text-primary)]\">{label}</span>\n <span className=\"text-[11px] leading-4 text-[var(--text-muted)]\">{hint}</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n )\n}\n","/**\n * Per-kind Konva node rendering for SceneElement. Each element kind maps to\n * the appropriate Konva primitive; geometry is converted from the model's\n * top-left convention where needed.\n *\n * Ellipse center-offset invariant: the model stores (x, y) as the top-left\n * corner of the bounding box. Konva.Ellipse draws from its center point.\n * The conversion is: centerX = x + width/2, centerY = y + height/2,\n * radiusX = width/2, radiusY = height/2. The reverse applies when reading\n * back from transformer output (see transform-math.ts:ellipseTopLeftFromCenter).\n *\n * Image loading: src is loaded async into an HTMLImageElement via useEffect,\n * cached per src in a module-level Map so repeated renders of the same src\n * don't re-fetch. A broken-image placeholder rect is shown while loading or on\n * error; its `name` carries the src for diagnostics.\n *\n * Video elements render as their poster image when posterSrc is set, or a\n * placeholder rect. This is intentional — motion belongs to the sequences\n * surface; the canvas surface is for static layout.\n *\n * Node name: each node's `name` prop carries the element id so hit→model\n * mapping in Workspace works: `stage.getIntersection(pos)?.name()`.\n *\n * locked → listening(false) except on the click handler for selection.\n * hidden → not rendered.\n */\n\nimport { memo, useEffect, useRef, useState } from 'react'\nimport { Group, Rect, Ellipse, Line, Text, Image as KonvaImage } from 'react-konva'\nimport type Konva from 'konva'\nimport { ellipseCenterFromTopLeft } from './transform-math'\nimport { lightTheme, type CanvasRenderPalette } from '../../theme/theme'\nimport type {\n SceneElement,\n RectElement,\n EllipseElement,\n LineElement,\n TextElement,\n ImageElement,\n VideoElement,\n GroupElement,\n} from '../../design-canvas/model'\n\n// ---------------------------------------------------------------------------\n// Image cache — module-level so it survives re-renders\n// ---------------------------------------------------------------------------\n\n// LRU bound: large asset libraries with many unique src URLs would accumulate\n// held HTMLImageElement objects indefinitely without eviction. 256 entries\n// covers any reasonable single-session use and keeps memory predictable.\nconst IMAGE_CACHE_MAX = 256\n\nconst imageCache = new Map<string, HTMLImageElement>()\n\n/** Evict the least-recently-inserted entry when the cache is at capacity.\n * Map iteration order is insertion order, so the first key is the oldest. */\nfunction imageCacheSet(src: string, img: HTMLImageElement): void {\n if (imageCache.size >= IMAGE_CACHE_MAX) {\n const oldest = imageCache.keys().next().value\n if (oldest !== undefined) imageCache.delete(oldest)\n }\n imageCache.set(src, img)\n}\n\nfunction useImage(src: string): HTMLImageElement | null {\n const [, setVersion] = useState(0)\n\n useEffect(() => {\n if (imageCache.has(src)) return\n const img = new window.Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => {\n imageCacheSet(src, img)\n setVersion((v) => v + 1)\n }\n img.onerror = () => {\n // Store a sentinel so we don't retry infinitely; placeholder renders instead.\n imageCacheSet(src, img)\n setVersion((v) => v + 1)\n }\n img.src = src\n }, [src])\n\n return imageCache.get(src) ?? null\n}\n\n// ---------------------------------------------------------------------------\n// Element node dispatcher\n// ---------------------------------------------------------------------------\n\nexport interface ElementNodeProps {\n element: SceneElement\n isSelected: boolean\n zoom: number\n /** Theme render palette. Omitted → light defaults (byte-identical history). */\n render?: CanvasRenderPalette\n onClick?(elementId: string): void\n onDragStart?(elementId: string): void\n onDragMove?(elementId: string, dx: number, dy: number): void\n onDragEnd?(elementId: string, finalX: number, finalY: number): void\n onDoubleClick?(elementId: string): void\n}\n\nfunction ElementNodeImpl(props: ElementNodeProps) {\n const { element } = props\n if (!element.visible) return null\n\n switch (element.kind) {\n case 'rect': return <RectNode {...props} element={element} />\n case 'ellipse': return <EllipseNode {...props} element={element} />\n case 'line': return <LineNode {...props} element={element} />\n case 'text': return <TextNode {...props} element={element} />\n case 'image': return <ImageNode {...props} element={element} />\n case 'video': return <VideoNode {...props} element={element} />\n case 'group': return <GroupNode {...props} element={element} />\n }\n}\n\n/**\n * Memoized so a `stack.notify()` (fired ~120/s during a pan/marquee) that\n * re-renders WorkspaceView does NOT re-render every element. With stable\n * handler identities supplied by WorkspaceView, an ElementNode re-renders only\n * when its OWN `element` reference, `isSelected`, `zoom`, or `render` palette\n * change — i.e. when it actually moved, was (de)selected, or the view scaled.\n * Pan-only frames change none of these props, so the N nodes stay reconciled.\n */\nexport const ElementNode = memo(ElementNodeImpl)\n\n// ---------------------------------------------------------------------------\n// Shared drag handlers factory\n// ---------------------------------------------------------------------------\n\ninterface DragBindings {\n draggable: boolean\n listening: boolean\n /** True between dragStart and dragEnd — gates Konva node caching off so the\n * moving node redraws live instead of showing a stale cached bitmap. */\n dragging: boolean\n onDragStart?: (e: { target: { x(): number; y(): number } }) => void\n onDragMove?: (e: { target: { x(): number; y(): number } }) => void\n onDragEnd?: (e: { target: { x(): number; y(): number } }) => void\n onClick?: (e: unknown) => void\n onDblClick?: (e: unknown) => void\n}\n\nfunction useDragBindings(props: ElementNodeProps, originX: number, originY: number): DragBindings {\n const { element, onClick, onDragStart, onDragMove, onDragEnd, onDoubleClick } = props\n const isDraggable = !element.locked && !!onDragEnd\n const originRef = useRef({ x: originX, y: originY })\n originRef.current = { x: originX, y: originY }\n const [dragging, setDragging] = useState(false)\n\n return {\n draggable: isDraggable,\n // locked elements still receive click for selection; listening must be true.\n listening: true,\n dragging,\n onDragStart: isDraggable ? () => { setDragging(true); onDragStart?.(element.id) } : undefined,\n onDragMove: isDraggable ? (e: { target: { x(): number; y(): number } }) => {\n onDragMove?.(element.id, e.target.x() - originRef.current.x, e.target.y() - originRef.current.y)\n } : undefined,\n onDragEnd: isDraggable ? (e: { target: { x(): number; y(): number } }) => {\n setDragging(false)\n onDragEnd?.(element.id, e.target.x(), e.target.y())\n } : undefined,\n onClick: () => onClick?.(element.id),\n onDblClick: () => onDoubleClick?.(element.id),\n }\n}\n\n/**\n * Konva node bitmap caching for static elements. A cached node draws from a\n * pre-rasterized bitmap instead of re-running its shape draw each frame, which\n * is the snappiness win when many static nodes are on stage during a pan/zoom.\n *\n * Gated OFF while `isSelected || dragging`: a selected node is under the\n * transformer (resize/rotate must redraw live) and a dragging node moves every\n * frame — a cached bitmap would freeze its visuals. On select/drag start the\n * cache is cleared; on deselect/drop it is re-cached. `zoom` is a dependency so\n * the bitmap re-rasterizes at the current scale and never looks blurry.\n */\nfunction useNodeCache(\n ref: React.RefObject<Konva.Node | null>,\n isSelected: boolean,\n dragging: boolean,\n zoom: number,\n invalidateKey: unknown,\n): void {\n useEffect(() => {\n const node = ref.current\n if (!node) return\n if (isSelected || dragging) {\n // clearCache is a no-op when the node was never cached.\n node.clearCache()\n return\n }\n node.cache()\n return () => {\n // Drop the bitmap when the node unmounts or before re-caching so a stale\n // bitmap is never left attached across a prop change.\n node.clearCache()\n }\n // `invalidateKey` (the element reference) re-rasterizes the bitmap when any\n // element attribute changes; `zoom` keeps it crisp at the current scale.\n }, [ref, isSelected, dragging, zoom, invalidateKey])\n}\n\n// ---------------------------------------------------------------------------\n// Rect\n// ---------------------------------------------------------------------------\n\nfunction RectNode({ element, ...rest }: ElementNodeProps & { element: RectElement }) {\n const { dragging, ...drag } = useDragBindings({ element, ...rest }, element.x, element.y)\n const ref = useRef<Konva.Rect | null>(null)\n useNodeCache(ref, rest.isSelected, dragging, rest.zoom, element)\n return (\n <Rect\n ref={ref}\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n fill={element.fill}\n stroke={element.stroke}\n strokeWidth={element.strokeWidth}\n cornerRadius={element.cornerRadius ?? 0}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Ellipse\n// ---------------------------------------------------------------------------\n\nfunction EllipseNode({ element, ...rest }: ElementNodeProps & { element: EllipseElement }) {\n // Model: top-left (x, y) + width/height bounding box.\n // Konva.Ellipse: center (x, y) + radiusX/radiusY.\n const { x: cx, y: cy, radiusX, radiusY } = ellipseCenterFromTopLeft(element)\n const { dragging, ...drag } = useDragBindings({ element, ...rest }, element.x, element.y)\n const ref = useRef<Konva.Ellipse | null>(null)\n useNodeCache(ref, rest.isSelected, dragging, rest.zoom, element)\n\n return (\n <Ellipse\n ref={ref}\n name={element.id}\n x={cx}\n y={cy}\n radiusX={radiusX}\n radiusY={radiusY}\n rotation={element.rotation}\n opacity={element.opacity}\n fill={element.fill}\n stroke={element.stroke}\n strokeWidth={element.strokeWidth}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Line\n// ---------------------------------------------------------------------------\n\nfunction LineNode({ element, ...rest }: ElementNodeProps & { element: LineElement }) {\n const { dragging, ...drag } = useDragBindings({ element, ...rest }, element.x, element.y)\n const ref = useRef<Konva.Line | null>(null)\n useNodeCache(ref, rest.isSelected, dragging, rest.zoom, element)\n return (\n <Line\n ref={ref}\n name={element.id}\n x={element.x}\n y={element.y}\n points={element.points}\n rotation={element.rotation}\n opacity={element.opacity}\n stroke={element.stroke}\n strokeWidth={element.strokeWidth}\n dash={element.dash}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Text\n// ---------------------------------------------------------------------------\n\nconst FONT_STYLE_MAP: Record<TextElement['fontStyle'], { fontStyle: string; fontVariant: string; fontWeight: string }> = {\n 'normal': { fontStyle: 'normal', fontVariant: 'normal', fontWeight: 'normal' },\n 'bold': { fontStyle: 'normal', fontVariant: 'normal', fontWeight: 'bold' },\n 'italic': { fontStyle: 'italic', fontVariant: 'normal', fontWeight: 'normal' },\n 'bold italic': { fontStyle: 'italic', fontVariant: 'normal', fontWeight: 'bold' },\n}\n\nfunction TextNode({ element, ...rest }: ElementNodeProps & { element: TextElement }) {\n const { dragging, ...drag } = useDragBindings({ element, ...rest }, element.x, element.y)\n const ref = useRef<Konva.Text | null>(null)\n useNodeCache(ref, rest.isSelected, dragging, rest.zoom, element)\n const { fontStyle, fontWeight } = FONT_STYLE_MAP[element.fontStyle]\n\n return (\n <Text\n ref={ref}\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n rotation={element.rotation}\n opacity={element.opacity}\n text={element.text}\n fontFamily={element.fontFamily}\n fontSize={element.fontSize}\n fontStyle={`${fontStyle} ${fontWeight}`.trim()}\n fill={element.fill}\n align={element.align}\n lineHeight={element.lineHeight}\n letterSpacing={element.letterSpacing}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Image\n// ---------------------------------------------------------------------------\n\nfunction ImageNode({ element, ...rest }: ElementNodeProps & { element: ImageElement }) {\n const render = rest.render ?? lightTheme.canvasRender\n const img = useImage(element.src)\n const { dragging, ...drag } = useDragBindings({ element, ...rest }, element.x, element.y)\n const ref = useRef<Konva.Node | null>(null)\n const imgReady = !!img && img.complete && img.naturalWidth > 0\n // Cache key folds load state + the attrs that change the rasterized pixels\n // (src, size, fit). Position/opacity/rotation are post-cache transforms and\n // are deliberately excluded so a drag/opacity change never re-rasterizes.\n useNodeCache(\n ref,\n rest.isSelected,\n dragging,\n rest.zoom,\n `${imgReady}:${element.src}:${element.width}x${element.height}:${element.fit}`,\n )\n\n if (!img || !img.complete || img.naturalWidth === 0) {\n // Placeholder while loading or broken: a grey rect with the src in its name.\n return (\n <Rect\n ref={ref as React.RefObject<Konva.Rect>}\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n fill={render.brokenFill}\n stroke={render.brokenStroke}\n strokeWidth={1}\n {...drag}\n />\n )\n }\n\n // Konva handles crop-to-fit natively via crop attrs; we compute them for\n // 'cover' mode. 'contain' and 'fill' use default stretch behavior.\n let cropProps: object = {}\n if (element.fit === 'cover') {\n const srcAspect = img.naturalWidth / img.naturalHeight\n const dstAspect = element.width / element.height\n if (srcAspect > dstAspect) {\n // Source is wider — crop horizontally\n const visibleW = img.naturalHeight * dstAspect\n cropProps = {\n crop: {\n x: (img.naturalWidth - visibleW) / 2,\n y: 0,\n width: visibleW,\n height: img.naturalHeight,\n },\n }\n } else {\n // Source is taller — crop vertically\n const visibleH = img.naturalWidth / dstAspect\n cropProps = {\n crop: {\n x: 0,\n y: (img.naturalHeight - visibleH) / 2,\n width: img.naturalWidth,\n height: visibleH,\n },\n }\n }\n }\n\n return (\n <KonvaImage\n ref={ref as React.RefObject<Konva.Image>}\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n image={img}\n {...cropProps}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Video (renders poster or placeholder — no playback on the canvas surface)\n// ---------------------------------------------------------------------------\n\nfunction VideoNode({ element, ...rest }: ElementNodeProps & { element: VideoElement }) {\n const render = rest.render ?? lightTheme.canvasRender\n // Poster image path — never silently drop; show placeholder when absent.\n const img = useImage(element.posterSrc ?? '')\n const { dragging, ...drag } = useDragBindings({ element, ...rest }, element.x, element.y)\n const ref = useRef<Konva.Node | null>(null)\n const posterReady = !!element.posterSrc && !!img && img.complete && img.naturalWidth > 0\n useNodeCache(\n ref,\n rest.isSelected,\n dragging,\n rest.zoom,\n `${posterReady}:${element.posterSrc ?? ''}:${element.width}x${element.height}`,\n )\n\n if (!element.posterSrc || !img || !img.complete || img.naturalWidth === 0) {\n return (\n <Rect\n ref={ref as React.RefObject<Konva.Rect>}\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n fill={render.placeholderFill}\n stroke={render.placeholderStroke}\n strokeWidth={1}\n {...drag}\n />\n )\n }\n\n return (\n <KonvaImage\n ref={ref as React.RefObject<Konva.Image>}\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n image={img}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Group (recursive)\n// ---------------------------------------------------------------------------\n\nfunction GroupNode({ element, ...rest }: ElementNodeProps & { element: GroupElement }) {\n const { dragging: _dragging, ...drag } = useDragBindings({ element, ...rest }, element.x, element.y)\n // A group is NOT bitmap-cached: caching a container would flatten its children\n // into one static bitmap and break per-child hit-testing/transform. The win is\n // that each child node (memoized + leaf-cached) keeps its own cache.\n void _dragging\n return (\n <Group\n name={element.id}\n x={element.x}\n y={element.y}\n rotation={element.rotation}\n opacity={element.opacity}\n {...drag}\n >\n {element.children.map((child) => (\n <ElementNode key={child.id} {...rest} element={child} />\n ))}\n </Group>\n )\n}\n","/**\n * Pure geometry helpers extracted from component drag/transform logic so\n * every interactive math path is unit-testable without Konva or a DOM.\n *\n * Invariants:\n * - All inputs and outputs are in document-coordinate pixels unless noted.\n * - Rotation angles are degrees, clockwise, matching the Konva + model convention.\n * - \"Baking\" scale into width/height resets Konva's scaleX/scaleY to 1; that\n * is required so the model's width/height always reflects true size, never a\n * scaled-but-uncollapsed state.\n */\n\n// ---------------------------------------------------------------------------\n// Konva transformer end-state baking\n// ---------------------------------------------------------------------------\n\nexport interface TransformerNode {\n x: number\n y: number\n width: number\n height: number\n /** Konva's scale after the transformer gesture; 1 when already baked. */\n scaleX: number\n scaleY: number\n rotation: number\n}\n\nexport interface BakedNodeAttrs {\n x: number\n y: number\n /** True pixel size after scale is collapsed into dimensions. */\n width: number\n height: number\n rotation: number\n}\n\n/**\n * Collapse Konva scaleX/scaleY into width/height so the model always stores\n * true pixel dimensions. The transformer mutates scale on drag; we bake it\n * once at dragend/transformend and emit width/height — scale resets to 1\n * implicitly (the emitted attrs do not include scale, so the next render\n * starts from scaleX=1).\n *\n * Konva rotates about the top-left origin and also shifts x/y to compensate\n * for scale — the transformer gives us the post-rotation, post-scale x/y\n * directly, so no further rotation math is needed here.\n */\nexport function bakeRectTransform(node: TransformerNode): BakedNodeAttrs {\n return {\n x: node.x,\n y: node.y,\n width: Math.abs(node.width * node.scaleX),\n height: Math.abs(node.height * node.scaleY),\n rotation: node.rotation,\n }\n}\n\n/**\n * Lines store points as a flat [x0, y0, x1, y1, ...] array relative to the\n * line element's (x, y). When the transformer scales the group containing the\n * line, we bake scaleX into every x-component of points and scaleY into every\n * y-component, then reset scale to 1. The element x/y is the Konva group\n * origin and is taken directly from the transformer output.\n */\nexport function bakeLineTransform(node: TransformerNode & { points: number[] }): BakedNodeAttrs & { points: number[] } {\n const points = node.points.map((v, i) => (i % 2 === 0 ? v * node.scaleX : v * node.scaleY))\n return {\n x: node.x,\n y: node.y,\n // Width/height for a line derive from its points; baking is for points only.\n // We still return them so callers have a uniform shape.\n width: node.width * Math.abs(node.scaleX),\n height: node.height * Math.abs(node.scaleY),\n rotation: node.rotation,\n points,\n }\n}\n\n/**\n * Text nodes have a fixed wrap width; scaling that width is what the\n * transformer controls. Height is content-derived and is NOT baked here —\n * it re-derives from text content at render time via estimateTextHeight.\n * Only scaleX is baked into width; scaleY into fontSize so text scales\n * proportionally when the user resizes with keepRatio.\n */\nexport function bakeTextTransform(\n node: TransformerNode & { fontSize: number },\n): BakedNodeAttrs & { fontSize: number } {\n return {\n x: node.x,\n y: node.y,\n width: Math.abs(node.width * node.scaleX),\n // Height is excluded — it re-derives from content.\n height: node.height,\n rotation: node.rotation,\n fontSize: Math.max(1, node.fontSize * Math.abs(node.scaleY)),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Ellipse center-offset math\n// ---------------------------------------------------------------------------\n\n/**\n * The model stores ellipse position as the top-left corner of the bounding\n * box (matching every other element kind). Konva.Ellipse draws from its center\n * (radiusX, radiusY). This converts model top-left to Konva center.\n *\n * INVARIANT: radiusX = width/2, radiusY = height/2; x/y offsets are exactly\n * half the dimensions because there is no separate offset field.\n */\nexport function ellipseCenterFromTopLeft(\n topLeft: { x: number; y: number; width: number; height: number },\n): { x: number; y: number; radiusX: number; radiusY: number } {\n return {\n x: topLeft.x + topLeft.width / 2,\n y: topLeft.y + topLeft.height / 2,\n radiusX: topLeft.width / 2,\n radiusY: topLeft.height / 2,\n }\n}\n\n/**\n * Inverse: Konva center + radius → model top-left. Used when reading back\n * from a transformer node whose origin is the Konva center.\n */\nexport function ellipseTopLeftFromCenter(\n center: { x: number; y: number; radiusX: number; radiusY: number },\n): { x: number; y: number; width: number; height: number } {\n return {\n x: center.x - center.radiusX,\n y: center.y - center.radiusY,\n width: center.radiusX * 2,\n height: center.radiusY * 2,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Marquee selection\n// ---------------------------------------------------------------------------\n\nexport interface MarqueeRect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/**\n * Normalize a marquee rectangle from two arbitrary corners into a canonical\n * top-left origin with positive width/height, regardless of drag direction.\n */\nexport function normalizeMarquee(\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n): MarqueeRect {\n const x = Math.min(startX, endX)\n const y = Math.min(startY, endY)\n return {\n x,\n y,\n width: Math.abs(endX - startX),\n height: Math.abs(endY - startY),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Inline text editor overlay positioning\n// ---------------------------------------------------------------------------\n\nexport interface OverlayPositionInput {\n /** Element top-left in document coordinates. */\n elementX: number\n elementY: number\n elementWidth: number\n elementHeight: number\n /** Current zoom and pan. */\n zoom: number\n panX: number\n panY: number\n}\n\nexport interface OverlayPosition {\n /** CSS left/top for the overlay textarea relative to the canvas container. */\n left: number\n top: number\n width: number\n /** fontSize to mirror from the element, scaled by zoom. */\n fontSize: number\n}\n\n/**\n * Compute the screen-space position for an inline text editor overlay.\n *\n * V1 simplification: rotated text elements are edited in a non-rotated overlay\n * positioned at the element's AABB. This means the overlay does not visually\n * align with the rotated text, but keeps the textarea DOM element axis-aligned\n * which avoids browser textarea rotation bugs. A future v2 may apply a CSS\n * transform to the textarea to match rotation.\n */\nexport function computeTextOverlayPosition(\n input: OverlayPositionInput & { elementFontSize: number },\n): OverlayPosition {\n // The overlay textarea is position:absolute inside the workspace's relative\n // container, so its coordinates are container-relative, not viewport-relative:\n // containerX = panX + elementX * zoom.\n const left = input.panX + input.elementX * input.zoom\n const top = input.panY + input.elementY * input.zoom\n return {\n left,\n top,\n width: input.elementWidth * input.zoom,\n fontSize: input.elementFontSize * input.zoom,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Rotation snapping\n// ---------------------------------------------------------------------------\n\nconst SNAP_ANGLES_DEG = [0, 45, 90, 135, 180, 225, 270, 315, 360]\n\n/**\n * Snap a rotation angle to the nearest cardinal/diagonal if within\n * `thresholdDeg` of it (default 5°). Returns the original value when no snap\n * activates. Normalizes output to [0, 360).\n */\nexport function snapRotation(angleDeg: number, thresholdDeg = 5): number {\n const normalized = ((angleDeg % 360) + 360) % 360\n let best = normalized\n let bestDist = Infinity\n for (const snap of SNAP_ANGLES_DEG) {\n const dist = Math.abs(normalized - snap)\n if (dist < bestDist) {\n bestDist = dist\n best = snap % 360\n }\n }\n return bestDist <= thresholdDeg ? best : normalized\n}\n\n// ---------------------------------------------------------------------------\n// Nudge delta\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a keyboard arrow event into a {dx, dy} nudge in document px.\n * Shift multiplies by 10 (the \"big nudge\" convention).\n */\nexport function nudgeDelta(\n key: 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown',\n shift: boolean,\n): { dx: number; dy: number } {\n const step = shift ? 10 : 1\n switch (key) {\n case 'ArrowLeft': return { dx: -step, dy: 0 }\n case 'ArrowRight': return { dx: step, dy: 0 }\n case 'ArrowUp': return { dx: 0, dy: -step }\n case 'ArrowDown': return { dx: 0, dy: step }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Marquee hit-test\n// ---------------------------------------------------------------------------\n\n/**\n * Return true when bounds B is entirely contained within marquee A.\n * Partial intersection does NOT select — the marquee must fully enclose.\n */\nexport function marqueeContains(marquee: MarqueeRect, bounds: MarqueeRect): boolean {\n return (\n bounds.x >= marquee.x &&\n bounds.y >= marquee.y &&\n bounds.x + bounds.width <= marquee.x + marquee.width &&\n bounds.y + bounds.height <= marquee.y + marquee.height\n )\n}\n\n// ---------------------------------------------------------------------------\n// Grid ruler tick density\n// ---------------------------------------------------------------------------\n\n/**\n * Return true when grid lines at `gridSize` document px would render at least\n * `minScreenPx` apart at the current zoom. Used to skip grid drawing when\n * zoomed out far enough that lines would clutter.\n */\nexport function gridVisible(gridSize: number, zoom: number, minScreenPx = 4): boolean {\n return gridSize * zoom >= minScreenPx\n}\n","/**\n * Transformer-based selection layer. Attaches a Konva.Transformer to all\n * currently-selected Konva nodes so the user can resize and rotate them.\n *\n * SINGLE vs. MULTI:\n * - Single selection: resize + rotate.\n * - Multi-selection: rigid resize (keepRatio enforced) + rotate.\n *\n * Rotation snap: within 5° of 0/45/90/135/180 the rotation locks to the\n * nearest cardinal/diagonal (same threshold as the snapRotation pure function).\n *\n * Scale baking: Konva's Transformer sets scaleX/scaleY on the node during\n * a transform gesture. On transformend we read the final x/y/width/height/\n * scaleX/scaleY/rotation from each node, bake scale into width/height, and\n * emit ONE multiSetAttrsCommand. Scale never persists on Konva nodes across\n * render cycles — the next render resets scaleX=scaleY=1 by rendering from\n * model attrs.\n *\n * Special baking per kind:\n * - rect/ellipse/image/video/group: bakeRectTransform (scale → width/height).\n * - line: bakeLineTransform (scale baked into points array).\n * - text: bakeTextTransform (scaleX→width, scaleY→fontSize).\n * - ellipse: center position is adjusted back to top-left model coords via\n * ellipseTopLeftFromCenter before emitting attrs.\n *\n * Min-size guard: width/height are clamped to ≥ 4px after baking so users\n * cannot collapse an element to zero and lose it.\n *\n * The overlay: prefix on the Transformer name lets export logic skip it.\n */\n\nimport { useEffect, useRef } from 'react'\nimport { Layer, Transformer } from 'react-konva'\nimport type Konva from 'konva'\nimport {\n bakeRectTransform,\n bakeLineTransform,\n bakeTextTransform,\n ellipseTopLeftFromCenter,\n snapRotation,\n} from './transform-math'\nimport type { MultiSetAttrsEntry } from '../engine/commands'\nimport type { SceneAttrsPatch } from '../../design-canvas/operations'\nimport type { SceneElement } from '../../design-canvas/model'\nimport { lightTheme, type CanvasRenderPalette } from '../../theme/theme'\n\nexport interface SelectionLayerProps {\n /** Konva stage reference to look up selected nodes by name. */\n stageRef: React.RefObject<Konva.Stage | null>\n /** Selected element ids from editor state. */\n selectedIds: string[]\n /** The model elements corresponding to selectedIds (pre-gesture snapshot). */\n selectedElements: SceneElement[]\n /** Whether the canvas is writable. False → transformer renders but is not interactive. */\n canWrite: boolean\n /** Emitted when a transform gesture completes with final attrs per element. */\n onTransformEnd(entries: MultiSetAttrsEntry[]): void\n /** Active page id — every entry in onTransformEnd carries this. */\n pageId: string\n /** Theme render palette. Omitted → light defaults (byte-identical history). */\n render?: CanvasRenderPalette\n}\n\nconst MIN_SIZE = 4\n\nexport function SelectionLayer({\n stageRef,\n selectedIds,\n selectedElements,\n canWrite,\n onTransformEnd,\n pageId,\n render = lightTheme.canvasRender,\n}: SelectionLayerProps) {\n const trRef = useRef<Konva.Transformer | null>(null)\n\n // Sync the transformer's attached nodes whenever selection changes.\n useEffect(() => {\n const tr = trRef.current\n const stage = stageRef.current\n if (!tr || !stage) return\n\n const nodes: Konva.Node[] = []\n for (const id of selectedIds) {\n // Nodes carry their element id as their `name` prop.\n const node = stage.findOne(`[name=\"${id}\"]`)\n if (node) nodes.push(node)\n }\n tr.nodes(nodes)\n tr.getLayer()?.batchDraw()\n }, [selectedIds, stageRef])\n\n function handleTransformEnd() {\n const tr = trRef.current\n if (!tr) return\n const nodes = tr.nodes() as Konva.Node[]\n if (nodes.length === 0) return\n\n const entries: MultiSetAttrsEntry[] = []\n\n for (const node of nodes) {\n const elementId = node.name()\n const element = selectedElements.find((e) => e.id === elementId)\n if (!element) continue\n\n const priorAttrs = elementToPriorAttrs(element)\n const finalAttrs = bakeNodeToAttrs(node, element)\n if (!finalAttrs) continue\n\n entries.push({ pageId, elementId, attrs: finalAttrs, priorAttrs })\n }\n\n if (entries.length > 0) onTransformEnd(entries)\n }\n\n const multiSelect = selectedIds.length > 1\n\n return (\n // The Layer must participate in the hit graph (listening) or the Transformer\n // anchors below receive no pointer events and resize/rotate is dead. We scope\n // listening to canWrite so a read-only canvas stays fully click-through.\n // Click-through to elements for selection is preserved: this layer paints\n // only the Transformer, whose anchors are the sole hit targets — empty\n // regions have no shapes, so pointer hits fall through to the content layer.\n // Export exclusion is unaffected: export.ts hides nodes by the 'overlay:'\n // name prefix, not by `listening` (see export-math.isExportHiddenNodeName).\n <Layer name=\"overlay:selection\" listening={canWrite}>\n <Transformer\n ref={trRef}\n name=\"overlay:transformer\"\n // keepRatio for multi-select so the group scales uniformly.\n keepRatio={multiSelect}\n // Rotation snap: the rotationSnaps array controls the magnetism in\n // Konva's transformer. We pass the cardinal/diagonal angles; Konva\n // applies a threshold internally. We also call snapRotation() on\n // transformend for fine-grain 5° threshold enforcement.\n rotationSnaps={[0, 45, 90, 135, 180, 225, 270, 315]}\n rotationSnapTolerance={5}\n borderStroke={render.selectionStroke}\n anchorStroke={render.selectionStroke}\n anchorFill={render.selectionAnchorFill}\n // Min size so elements cannot be collapsed.\n boundBoxFunc={(oldBox, newBox) => {\n if (newBox.width < MIN_SIZE || newBox.height < MIN_SIZE) return oldBox\n return newBox\n }}\n listening={canWrite}\n onTransformEnd={canWrite ? handleTransformEnd : undefined}\n />\n </Layer>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Extract the current model attrs as a \"prior\" patch for undo. */\nfunction elementToPriorAttrs(element: SceneElement): SceneAttrsPatch {\n const base: SceneAttrsPatch = {\n x: element.x,\n y: element.y,\n rotation: element.rotation,\n }\n switch (element.kind) {\n case 'rect':\n case 'image':\n case 'video':\n return { ...base, width: element.width, height: element.height }\n case 'ellipse':\n return { ...base, width: element.width, height: element.height }\n case 'line':\n return { ...base, points: element.points.slice() }\n case 'text':\n return { ...base, width: element.width, fontSize: element.fontSize }\n case 'group':\n return { ...base, width: undefined, height: undefined }\n }\n}\n\n/**\n * Read final attrs from a Konva node after a transformer gesture and bake\n * scale into model dimensions. Returns null when the element kind has no\n * meaningful size to bake (shouldn't happen in practice).\n */\nfunction bakeNodeToAttrs(node: Konva.Node, element: SceneElement): SceneAttrsPatch | null {\n // Snap rotation within 5° of cardinal/diagonal angles.\n const rawRotation = node.rotation()\n const snappedRotation = snapRotation(rawRotation, 5)\n\n const baseNode = {\n x: node.x(),\n y: node.y(),\n width: node.width(),\n height: node.height(),\n scaleX: node.scaleX(),\n scaleY: node.scaleY(),\n rotation: snappedRotation,\n }\n\n switch (element.kind) {\n case 'rect':\n case 'image':\n case 'video':\n case 'group': {\n const baked = bakeRectTransform(baseNode)\n return {\n x: baked.x,\n y: baked.y,\n width: Math.max(MIN_SIZE, baked.width),\n height: Math.max(MIN_SIZE, baked.height),\n rotation: baked.rotation,\n }\n }\n case 'ellipse': {\n // Konva.Ellipse's transformer node is centered (Konva reports x/y as\n // center coords, radiusX/radiusY via width/height halved). We must\n // convert back to model top-left coords.\n const baked = bakeRectTransform(baseNode)\n // After baking, node.x/y for an ellipse is its center because that's\n // how we placed it in ElementNode. Convert to top-left.\n const topLeft = ellipseTopLeftFromCenter({\n x: baked.x,\n y: baked.y,\n // radiusX/radiusY = half of baked width/height\n radiusX: baked.width / 2,\n radiusY: baked.height / 2,\n })\n return {\n x: topLeft.x,\n y: topLeft.y,\n width: Math.max(MIN_SIZE, topLeft.width),\n height: Math.max(MIN_SIZE, topLeft.height),\n rotation: baked.rotation,\n }\n }\n case 'line': {\n const points = (node as Konva.Line).points()\n const baked = bakeLineTransform({ ...baseNode, points })\n return {\n x: baked.x,\n y: baked.y,\n rotation: baked.rotation,\n points: baked.points,\n }\n }\n case 'text': {\n const baked = bakeTextTransform({\n ...baseNode,\n fontSize: element.fontSize,\n })\n return {\n x: baked.x,\n y: baked.y,\n width: Math.max(MIN_SIZE, baked.width),\n rotation: baked.rotation,\n fontSize: Math.max(1, baked.fontSize),\n }\n }\n }\n}\n","/**\n * Grid overlay rendered beneath page content. The whole grid is painted by a\n * SINGLE Konva node — a custom Shape whose `sceneFunc` strokes every grid line\n * in one pass — rather than one Konva.Line per line. At ~hundreds of lines on a\n * page this turns a per-pan-frame reconciliation of hundreds of React/Konva\n * nodes into a single node whose only changing props are the transform; panning\n * no longer churns the scene graph.\n *\n * Grid lines are skipped entirely when they would be closer than 4 screen\n * pixels apart — below that density the grid becomes visual noise rather than\n * guidance (gridVisible density-skip, unchanged).\n *\n * COORDINATE SYSTEM: the lines are authored in document space and wrapped in a\n * Group carrying the SAME (panX, panY, zoom) transform the content layer uses,\n * so the grid tracks the page exactly on pan and zoom. A Konva.Layer cannot\n * live inside a Group, so the transform is applied to an inner Group, not the\n * Layer. The grid is its own Layer (not merged into the content Layer) so the\n * content draws on top without per-frame z-ordering churn.\n *\n * EXPORT EXCLUSION: the Layer carries the name prefix 'overlay:' and the Shape\n * is named 'overlay:grid-shape' so export logic excludes the whole grid from\n * rasterization.\n */\n\nimport { Layer, Group, Shape } from 'react-konva'\nimport type Konva from 'konva'\nimport { gridVisible } from './transform-math'\n\nexport interface GridLayerProps {\n /** Page width in document px. */\n pageWidth: number\n /** Page height in document px. */\n pageHeight: number\n /** Document px between grid lines. */\n gridSize: number\n /** Screen px per document px. */\n zoom: number\n /** Horizontal pan offset in screen px (content Group x). */\n panX: number\n /** Vertical pan offset in screen px (content Group y). */\n panY: number\n /** Grid line color. */\n color?: string\n /** Grid line opacity (0–1). */\n opacity?: number\n}\n\n/** Document-space x positions of every vertical grid line on the page. Pure so\n * the single-Shape sceneFunc and tests share one definition (no drift). */\nexport function gridVerticalLines(pageWidth: number, gridSize: number): number[] {\n const out: number[] = []\n if (gridSize <= 0) return out\n for (let x = gridSize; x < pageWidth; x += gridSize) out.push(x)\n return out\n}\n\n/** Document-space y positions of every horizontal grid line on the page. */\nexport function gridHorizontalLines(pageHeight: number, gridSize: number): number[] {\n const out: number[] = []\n if (gridSize <= 0) return out\n for (let y = gridSize; y < pageHeight; y += gridSize) out.push(y)\n return out\n}\n\nexport function GridLayer({\n pageWidth,\n pageHeight,\n gridSize,\n zoom,\n panX,\n panY,\n color = '#c0c0c0',\n opacity = 0.5,\n}: GridLayerProps) {\n // Skip rendering when lines would be denser than 4 screen px.\n if (!gridVisible(gridSize, zoom, 4)) return null\n\n const verticals = gridVerticalLines(pageWidth, gridSize)\n const horizontals = gridHorizontalLines(pageHeight, gridSize)\n\n // sceneFunc strokes all lines in document space; the parent Group transform\n // maps them to screen, so line geometry is zoom-independent. strokeWidth is\n // 1/zoom so the line stays 1 screen px after the Group's `scale(zoom)`.\n const sceneFunc = (ctx: Konva.Context, shape: Konva.Shape) => {\n ctx.beginPath()\n for (const x of verticals) {\n ctx.moveTo(x, 0)\n ctx.lineTo(x, pageHeight)\n }\n for (const y of horizontals) {\n ctx.moveTo(0, y)\n ctx.lineTo(pageWidth, y)\n }\n ctx.setAttr('strokeStyle', color)\n ctx.setAttr('lineWidth', 1 / zoom)\n ctx.stroke()\n // No fillStrokeShape — geometry is hairlines, not a fillable path; calling\n // it would close/fill the path. Stroke directly to keep crisp 1px lines.\n void shape\n }\n\n return (\n <Layer name=\"overlay:grid\" listening={false}>\n <Group x={panX} y={panY} scaleX={zoom} scaleY={zoom}>\n <Shape\n name=\"overlay:grid-shape\"\n sceneFunc={sceneFunc}\n stroke={color}\n strokeWidth={1 / zoom}\n opacity={opacity}\n listening={false}\n perfectDrawEnabled={false}\n />\n </Group>\n </Layer>\n )\n}\n","/**\n * Active snap guides rendered as full-page-extent lines during drag gestures.\n * Renders into a dedicated Konva.Layer (name 'overlay:snap') so export logic\n * can exclude it.\n *\n * Guide line colors differ by kind so users can distinguish grid snaps (faint)\n * from element-edge/center snaps (accent) from saved ruler guides (blue).\n */\n\nimport { Layer, Line } from 'react-konva'\nimport type { SnapTarget, SnapTargetKind } from '../contracts'\nimport { lightTheme, type CanvasRenderPalette } from '../../theme/theme'\n\nexport interface SnapGuidesOverlayProps {\n /** Page dimensions in document px. */\n pageWidth: number\n pageHeight: number\n /** Active vertical guide, or null when not snapping. */\n activeVertical: SnapTarget | null\n /** Active horizontal guide, or null when not snapping. */\n activeHorizontal: SnapTarget | null\n /** Screen px per document px — used to compute 1-px-screen stroke widths. */\n zoom: number\n /** Theme render palette. Omitted → light defaults (byte-identical history). */\n render?: CanvasRenderPalette\n}\n\nfunction kindColors(render: CanvasRenderPalette): Record<SnapTargetKind, string> {\n return {\n 'grid': render.snapGrid,\n 'guide': render.snapGuide,\n 'page-edge': render.snapPage,\n 'page-center': render.snapPage,\n 'element-edge': render.snapElement,\n 'element-center': render.snapElement,\n }\n}\n\nexport function SnapGuidesOverlay({\n pageWidth,\n pageHeight,\n activeVertical,\n activeHorizontal,\n zoom,\n render = lightTheme.canvasRender,\n}: SnapGuidesOverlayProps) {\n if (!activeVertical && !activeHorizontal) return null\n\n const KIND_COLOR = kindColors(render)\n const strokeWidth = 1 / zoom\n\n return (\n <Layer name=\"overlay:snap\" listening={false}>\n {activeVertical && (\n <Line\n name=\"overlay:snap-vertical\"\n points={[activeVertical.position, -99999, activeVertical.position, 99999]}\n stroke={KIND_COLOR[activeVertical.kind]}\n strokeWidth={strokeWidth}\n dash={[4 / zoom, 3 / zoom]}\n listening={false}\n perfectDrawEnabled={false}\n />\n )}\n {activeHorizontal && (\n <Line\n name=\"overlay:snap-horizontal\"\n points={[-99999, activeHorizontal.position, 99999, activeHorizontal.position]}\n stroke={KIND_COLOR[activeHorizontal.kind]}\n strokeWidth={strokeWidth}\n dash={[4 / zoom, 3 / zoom]}\n listening={false}\n perfectDrawEnabled={false}\n />\n )}\n </Layer>\n )\n}\n","/**\n * Inline text editor: a positioned <textarea> that appears over a text element\n * on double-click, mirroring its font properties at the current zoom. The\n * parent Workspace mounts this when `editingElementId` is set and unmounts it\n * on commit/cancel.\n *\n * V1 simplification: rotation is NOT applied to the textarea — rotated text\n * elements are edited in a non-rotated overlay at the element's AABB origin.\n * The textarea stays axis-aligned to avoid browser textarea rotation bugs.\n * Document the intent: a v2 pass may add a CSS transform to align with rotation.\n *\n * Commit: Meta+Enter or blur → emits `onCommit(newText)`.\n * Cancel: Escape → emits `onCancel()` and restores pre-edit text.\n */\n\nimport { useEffect, useRef } from 'react'\nimport { computeTextOverlayPosition } from './transform-math'\nimport type { TextElement } from '../../design-canvas/model'\n\nexport interface InlineTextEditorProps {\n element: TextElement\n zoom: number\n panX: number\n panY: number\n onCommit(text: string): void\n onCancel(): void\n}\n\nexport function InlineTextEditor({\n element,\n zoom,\n panX,\n panY,\n onCommit,\n onCancel,\n}: InlineTextEditorProps) {\n const ref = useRef<HTMLTextAreaElement>(null)\n\n const pos = computeTextOverlayPosition({\n elementX: element.x,\n elementY: element.y,\n elementWidth: element.width,\n elementHeight: element.fontSize * element.lineHeight * 4, // generous initial height\n zoom,\n panX,\n panY,\n elementFontSize: element.fontSize,\n })\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n el.focus()\n el.select()\n }, [])\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {\n if (e.key === 'Escape') {\n e.preventDefault()\n onCancel()\n return\n }\n // Meta+Enter or Ctrl+Enter commits\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n onCommit(ref.current?.value ?? element.text)\n }\n }\n\n function handleBlur() {\n onCommit(ref.current?.value ?? element.text)\n }\n\n // Map model fontStyle → CSS font-style + font-weight\n const fontWeight = element.fontStyle.includes('bold') ? 'bold' : 'normal'\n const fontStyle = element.fontStyle.includes('italic') ? 'italic' : 'normal'\n\n return (\n <textarea\n ref={ref}\n defaultValue={element.text}\n onKeyDown={handleKeyDown}\n onBlur={handleBlur}\n className=\"agent-app-edit-selection\"\n style={{\n position: 'absolute',\n left: pos.left,\n top: pos.top,\n width: pos.width,\n // Height auto-grows via CSS; min so single-line text has room.\n minHeight: pos.fontSize * element.lineHeight * 1.5,\n fontSize: pos.fontSize,\n fontFamily: element.fontFamily,\n fontWeight,\n fontStyle,\n textAlign: element.align,\n lineHeight: element.lineHeight,\n letterSpacing: element.letterSpacing * zoom,\n color: element.fill,\n // Neutral token surface rather than a forced white: a white fill makes\n // light text invisible while editing. The token surface stays legible\n // for any text color and matches the editor chrome.\n background: 'var(--bg-input)',\n border: '2px solid var(--brand-primary)',\n borderRadius: 2,\n padding: 2,\n resize: 'none',\n outline: 'none',\n overflow: 'hidden',\n boxSizing: 'border-box',\n zIndex: 1000,\n // Do NOT apply rotation — see V1 simplification note in module header.\n }}\n aria-label={`Editing text element \"${element.name}\"`}\n />\n )\n}\n","/**\n * Batteries-included composition: DesignCanvas chrome + WorkspaceView sharing\n * one command stack. This is the component products mount.\n *\n * Stack ownership:\n * DesignCanvas (chrome) owns the command stack. WorkspaceView receives that\n * same stack via renderWorkspace(ctx), so every gesture, undo, redo, and\n * layers-panel selection mutation touches exactly one state machine.\n *\n * Thumbnail rendering:\n * Thumbnails are produced imperatively with the Konva JS API (no React tree)\n * on an off-DOM stage at ~96px height. Only geometry + solid-fill shapes are\n * rendered — image nodes are skipped because async loading would require a\n * second pass. Cache key: `${pageId}:${cheapHash(elements)}`. Thumbnail\n * absence is cosmetic; PagesStrip handles a null result gracefully.\n */\n\nimport type { SceneDocument } from '../../design-canvas/model'\nimport type { DesignCanvasProps } from '../contracts'\nimport { DesignCanvas } from './DesignCanvas'\nimport { WorkspaceView } from './Workspace'\n\n// ---------------------------------------------------------------------------\n// Thumbnail cache\n// ---------------------------------------------------------------------------\n\nconst THUMBNAIL_HEIGHT_PX = 96\n\n/** Keyed by `pageId:contentHash`. Evicted only when the cache reaches the\n * hard limit (prevents unbounded growth across long sessions). */\nconst thumbnailCache = new Map<string, string>()\nconst THUMBNAIL_CACHE_LIMIT = 200\n\nfunction evictIfNeeded(): void {\n if (thumbnailCache.size < THUMBNAIL_CACHE_LIMIT) return\n // Drop the oldest 20 % when we hit the ceiling.\n const toDrop = Math.floor(THUMBNAIL_CACHE_LIMIT * 0.2)\n let dropped = 0\n for (const key of thumbnailCache.keys()) {\n if (dropped >= toDrop) break\n thumbnailCache.delete(key)\n dropped++\n }\n}\n\n/** Fast, non-cryptographic hash for cache-key generation. Not collision-free —\n * false hits produce a stale thumbnail (cosmetic only). */\nfunction cheapHash(value: unknown): string {\n const s = JSON.stringify(value) ?? ''\n let h = 0x811c9dc5\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n // imul handles 32-bit overflow correctly\n h = Math.imul(h, 0x01000193)\n }\n return (h >>> 0).toString(36)\n}\n\n/** Render a single page to a ~96px-tall data URL using the Konva JS API. The\n * render is synchronous (no image loading); only geometry + solid fills are\n * drawn, which is sufficient for thumbnail use.\n *\n * Returns null when Konva is unavailable (SSR / test environments without\n * a canvas implementation) or on any render failure — callers must treat\n * null as \"no thumbnail yet\" rather than an error. */\nasync function renderPageThumbnail(page: SceneDocument['pages'][number]): Promise<string | null> {\n const cacheKey = `${page.id}:${cheapHash(page.elements)}`\n const cached = thumbnailCache.get(cacheKey)\n if (cached !== undefined) return cached\n\n // Lazily import Konva so this file compiles in SSR bundles.\n let Konva: typeof import('konva').default\n try {\n const mod = await import('konva')\n Konva = mod.default\n } catch {\n return null\n }\n\n const aspectRatio = page.width > 0 ? page.width / page.height : 1\n const thumbH = THUMBNAIL_HEIGHT_PX\n const thumbW = Math.round(thumbH * aspectRatio)\n const scale = page.height > 0 ? thumbH / page.height : 1\n\n let stage: InstanceType<typeof Konva.Stage> | null = null\n try {\n const container = globalThis.document?.createElement('div')\n if (!container) return null\n container.style.position = 'absolute'\n container.style.left = '-9999px'\n container.style.top = '-9999px'\n globalThis.document.body.appendChild(container)\n\n stage = new Konva.Stage({ container, width: thumbW, height: thumbH })\n const layer = new Konva.Layer()\n stage.add(layer)\n\n // Page background\n layer.add(new Konva.Rect({\n x: 0, y: 0,\n width: thumbW,\n height: thumbH,\n fill: page.background,\n listening: false,\n }))\n\n // Simplified element shapes — geometry + solid fills only; images skipped\n const group = new Konva.Group({ x: 0, y: 0, scaleX: scale, scaleY: scale, listening: false })\n layer.add(group)\n paintElements(Konva, group, page.elements)\n\n const dataUrl = stage.toDataURL({ mimeType: 'image/png', pixelRatio: 1 })\n evictIfNeeded()\n thumbnailCache.set(cacheKey, dataUrl)\n return dataUrl\n } catch {\n return null\n } finally {\n if (stage) {\n stage.destroy()\n const el = stage.container()\n el.parentNode?.removeChild(el)\n }\n }\n}\n\ntype KonvaStatic = typeof import('konva').default\n\nfunction paintElements(\n Konva: KonvaStatic,\n parent: InstanceType<typeof import('konva').default.Group>,\n elements: SceneDocument['pages'][number]['elements'],\n): void {\n for (const el of elements) {\n if (!el.visible) continue\n switch (el.kind) {\n case 'rect':\n parent.add(new Konva.Rect({\n x: el.x, y: el.y,\n width: el.width, height: el.height,\n rotation: el.rotation,\n opacity: el.opacity,\n fill: el.fill ?? undefined,\n cornerRadius: el.cornerRadius ?? 0,\n listening: false,\n }))\n break\n case 'ellipse':\n parent.add(new Konva.Ellipse({\n x: el.x + el.width / 2, y: el.y + el.height / 2,\n radiusX: el.width / 2, radiusY: el.height / 2,\n rotation: el.rotation,\n opacity: el.opacity,\n fill: el.fill ?? undefined,\n listening: false,\n }))\n break\n case 'text':\n parent.add(new Konva.Text({\n x: el.x, y: el.y,\n width: el.width,\n rotation: el.rotation,\n opacity: el.opacity,\n text: el.text,\n fontSize: el.fontSize,\n fontFamily: el.fontFamily,\n fill: el.fill,\n align: el.align,\n listening: false,\n }))\n break\n case 'group': {\n const g = new Konva.Group({\n x: el.x, y: el.y,\n rotation: el.rotation,\n opacity: el.opacity,\n listening: false,\n })\n parent.add(g)\n paintElements(Konva, g, el.children)\n break\n }\n // image and video: skip — async loading not viable in sync thumbnail render\n default:\n break\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DesignCanvasEditor\n// ---------------------------------------------------------------------------\n\n/**\n * Mount this component to get the full editor: toolbar, rulers, layers panel,\n * pages strip, zoom controls, and the Konva canvas — all sharing one command\n * stack so undo/redo and selection are coherent across every surface.\n */\nexport function DesignCanvasEditor(props: DesignCanvasProps) {\n return (\n <DesignCanvas\n {...props}\n renderWorkspace={(ctx) => {\n if (!ctx.activePage) return null\n return (\n <WorkspaceView\n stack={ctx.stack}\n activePage={ctx.activePage}\n canWrite={ctx.canWrite}\n onApplyOperations={props.onApplyOperations}\n onSelectionChange={props.onSelectionChange}\n // Shares the chrome's fit ref so F / Fit button trigger workspace\n // fit-to-page through the same callback slot.\n onFitRef={ctx.onFitRef}\n // Shares the chrome's export ref so the Export control renders the\n // stage here (the workspace owns Konva) and forwards to onExport.\n onExport={props.onExport}\n onExportRef={ctx.onExportRef}\n fitOnMount={ctx.fitOnMount}\n onReady={ctx.onReady}\n render={ctx.render}\n showEmptyState={ctx.showEmptyState}\n onAskAgent={ctx.onAskAgent}\n />\n )\n }}\n renderThumbnail={renderPageThumbnail}\n />\n )\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","Layer","Rect","Group","jsx","useEffect","useRef","jsx","useRef","useEffect","Layer","Group","jsx","Layer","Group","Layer","Line","jsx","jsxs","Layer","Line","useEffect","useRef","jsx","useRef","useEffect","jsx","jsxs","useState","useEffect","useRef","Layer","Group","Rect","jsx"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/preset-cloudflare/index.ts"],"sourcesContent":["/**\n * `@tangle-network/agent-app/preset-cloudflare` — the batteries-included default\n * stack.\n *\n * Every fleet agent runs the SAME backend: Cloudflare D1 (SQLite) through\n * Drizzle for state, a KV namespace as the artifact vault, AES-GCM field crypto\n * for PII, and per-workspace budget-capped model keys. The other agent-app\n * modules are pure SEAMS — `./tools` needs an `AppToolHandlers`, `./knowledge`\n * needs a `KnowledgeStateAccessor`, `./billing` needs a `WorkspaceKeyStore` +\n * `KeyCrypto`. This module is the ONE implementation of those seams against the\n * house stack, so a consumer that runs D1 + KV stands the whole shell up with\n * config + bindings and ZERO handler code.\n *\n * Layering:\n * - Drizzle is a PEER (the consumer installs `drizzle-orm`, never bundled). The\n * schema is therefore expressed two ways that need no import here: the plain\n * DDL ({@link PRESET_MIGRATION_SQL}) a consumer runs to create the tables, and\n * a {@link createPresetDrizzleSchema} factory that takes the consumer's\n * `drizzle-orm/sqlite-core` builder module and returns the typed tables. The\n * column names in {@link PRESET_TABLES} are the contract the handlers,\n * accessor, and DDL all agree on.\n * - D1 + KV are STRUCTURAL: {@link D1Like} (Cloudflare `D1Database` satisfies it)\n * and `KvLike` from `../web` (Cloudflare `KVNamespace` satisfies it). No\n * `@cloudflare/workers-types` dependency.\n * - Crypto/billing reuse `../crypto` + `../billing` exactly — this only wires\n * them to the D1 key table.\n */\n\nimport { createFieldCrypto } from '../crypto/index'\nimport {\n createWorkspaceKeyManager,\n type KeyCrypto,\n type KeyProvisioner,\n type WorkspaceKeyManager,\n type WorkspaceKeyRecord,\n type WorkspaceKeyStore,\n} from '../billing/index'\nimport type { KnowledgeStateAccessor } from '../knowledge/index'\nimport type {\n AddCitationArgs,\n AddCitationResult,\n AppToolContext,\n AppToolHandlers,\n RenderUiArgs,\n RenderUiResult,\n ScheduleFollowupArgs,\n ScheduleFollowupResult,\n SubmitProposalArgs,\n SubmitProposalResult,\n} from '../tools/index'\nimport type { KvLike } from '../web/index'\n\n// ---------------------------------------------------------------------------\n// D1 structural seam\n//\n// The minimal surface of Cloudflare's `D1Database` the handlers + accessor use.\n// `D1Database` satisfies it structurally, so the consumer passes `env.DB`\n// directly and tests pass an in-memory fake. We keep it to the\n// prepare/bind/first/run/all shape D1 already exposes — no Drizzle here, so the\n// default handlers run on a fresh Worker with only the D1 binding.\n// ---------------------------------------------------------------------------\n\n/** A prepared, bound D1 statement. */\nexport interface D1PreparedLike {\n bind(...values: unknown[]): D1PreparedLike\n first<T = Record<string, unknown>>(colName?: string): Promise<T | null>\n run(): Promise<unknown>\n all<T = Record<string, unknown>>(): Promise<{ results: T[] }>\n}\n\n/** The D1 surface the preset needs. Cloudflare `D1Database` satisfies it. */\nexport interface D1Like {\n prepare(query: string): D1PreparedLike\n}\n\n// ---------------------------------------------------------------------------\n// Default schema\n//\n// The four tables the default handlers + accessor read/write. Column names are\n// the single source of truth shared by the DDL, the Drizzle factory, the\n// handlers, and the accessor. Every table is workspace-scoped on `workspace_id`\n// (the accessor's default `where` column) so the knowledge `count` rule and the\n// tool writes agree without per-consumer wiring.\n//\n// Deliberately NOT here: chat thread/message tables. `/chat-store` is the\n// single thread-schema owner (`createChatTables` + `createChatStore`); the\n// preset once declared an unconsumed `threads` DDL, removed so two schemas\n// can't drift.\n// ---------------------------------------------------------------------------\n\n/** The preset table + column names — the contract the DDL, Drizzle schema,\n * handlers, and accessor share. Exposed so a consumer can reference a column\n * without a string literal. */\nexport const PRESET_TABLES = {\n proposals: {\n name: 'proposals',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n threadId: 'thread_id',\n type: 'type',\n title: 'title',\n description: 'description',\n status: 'status',\n createdBy: 'created_by',\n createdAt: 'created_at',\n },\n },\n knowledge: {\n name: 'knowledge',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n path: 'path',\n kind: 'kind',\n label: 'label',\n content: 'content',\n createdAt: 'created_at',\n },\n },\n deadlines: {\n name: 'deadlines',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n threadId: 'thread_id',\n title: 'title',\n dueDate: 'due_date',\n priority: 'priority',\n status: 'status',\n createdAt: 'created_at',\n },\n },\n workspaceKeys: {\n name: 'workspace_keys',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n keyId: 'key_id',\n keyEncrypted: 'key_encrypted',\n budgetUsd: 'budget_usd',\n expiresAt: 'expires_at',\n revokedAt: 'revoked_at',\n createdAt: 'created_at',\n },\n },\n} as const\n\n/**\n * Plain DDL for the preset schema — run by a consumer to create the tables with\n * ZERO drizzle (`for (const sql of PRESET_MIGRATION_SQL) await db.prepare(sql).run()`,\n * or paste into a `.sql` migration). One statement per table so D1's\n * single-statement `prepare` accepts each. Matches {@link PRESET_TABLES} exactly.\n */\nexport const PRESET_MIGRATION_SQL: readonly string[] = [\n `CREATE TABLE IF NOT EXISTS proposals (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n thread_id TEXT,\n type TEXT NOT NULL,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n created_by TEXT,\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS knowledge (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n path TEXT NOT NULL,\n kind TEXT NOT NULL,\n label TEXT,\n content TEXT,\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS deadlines (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n thread_id TEXT,\n title TEXT NOT NULL,\n due_date TEXT NOT NULL,\n priority TEXT,\n status TEXT NOT NULL DEFAULT 'scheduled',\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS workspace_keys (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n key_id TEXT NOT NULL,\n key_encrypted TEXT NOT NULL,\n budget_usd REAL NOT NULL,\n expires_at INTEGER NOT NULL,\n revoked_at INTEGER,\n created_at INTEGER NOT NULL\n )`,\n `CREATE INDEX IF NOT EXISTS idx_proposals_ws ON proposals (workspace_id, status)`,\n `CREATE INDEX IF NOT EXISTS idx_deadlines_ws ON deadlines (workspace_id, status)`,\n `CREATE INDEX IF NOT EXISTS idx_knowledge_ws ON knowledge (workspace_id)`,\n `CREATE INDEX IF NOT EXISTS idx_workspace_keys_ws ON workspace_keys (workspace_id, revoked_at)`,\n]\n\n/** A chainable column builder — every modifier returns the builder so calls\n * like `.notNull().default('pending')` typecheck. The concrete drizzle builders\n * satisfy this structurally. */\nexport interface DrizzleColumnLike {\n primaryKey: () => DrizzleColumnLike\n notNull: () => DrizzleColumnLike\n default: (v: unknown) => DrizzleColumnLike\n}\n\n/** The shape of a `drizzle-orm/sqlite-core` module — the few builders the\n * preset schema uses. The consumer passes the real module; agent-app never\n * imports it (it stays a peer). */\nexport interface DrizzleSqliteCoreLike {\n sqliteTable: (name: string, columns: Record<string, DrizzleColumnLike>) => unknown\n text: (name?: string) => DrizzleColumnLike\n integer: (name?: string, config?: unknown) => DrizzleColumnLike\n real: (name?: string) => DrizzleColumnLike\n}\n\n/**\n * Build the typed Drizzle schema for the preset, given the consumer's\n * `drizzle-orm/sqlite-core` module. Returns one table object per\n * {@link PRESET_TABLES} entry — pass to `drizzle(db, { schema })` for typed\n * queries, or to drizzle-kit for migration generation. agent-app never imports\n * drizzle; the builder module is the seam.\n *\n * ```ts\n * import * as d from 'drizzle-orm/sqlite-core'\n * const schema = createPresetDrizzleSchema(d)\n * ```\n */\nexport function createPresetDrizzleSchema(d: DrizzleSqliteCoreLike) {\n const { sqliteTable, text, integer, real } = d\n const C = PRESET_TABLES\n return {\n proposals: sqliteTable(C.proposals.name, {\n id: text(C.proposals.columns.id).primaryKey(),\n workspaceId: text(C.proposals.columns.workspaceId).notNull(),\n threadId: text(C.proposals.columns.threadId),\n type: text(C.proposals.columns.type).notNull(),\n title: text(C.proposals.columns.title).notNull(),\n description: text(C.proposals.columns.description),\n status: text(C.proposals.columns.status).notNull().default('pending'),\n createdBy: text(C.proposals.columns.createdBy),\n createdAt: integer(C.proposals.columns.createdAt).notNull(),\n }),\n knowledge: sqliteTable(C.knowledge.name, {\n id: text(C.knowledge.columns.id).primaryKey(),\n workspaceId: text(C.knowledge.columns.workspaceId).notNull(),\n path: text(C.knowledge.columns.path).notNull(),\n kind: text(C.knowledge.columns.kind).notNull(),\n label: text(C.knowledge.columns.label),\n content: text(C.knowledge.columns.content),\n createdAt: integer(C.knowledge.columns.createdAt).notNull(),\n }),\n deadlines: sqliteTable(C.deadlines.name, {\n id: text(C.deadlines.columns.id).primaryKey(),\n workspaceId: text(C.deadlines.columns.workspaceId).notNull(),\n threadId: text(C.deadlines.columns.threadId),\n title: text(C.deadlines.columns.title).notNull(),\n dueDate: text(C.deadlines.columns.dueDate).notNull(),\n priority: text(C.deadlines.columns.priority),\n status: text(C.deadlines.columns.status).notNull().default('scheduled'),\n createdAt: integer(C.deadlines.columns.createdAt).notNull(),\n }),\n workspaceKeys: sqliteTable(C.workspaceKeys.name, {\n id: text(C.workspaceKeys.columns.id).primaryKey(),\n workspaceId: text(C.workspaceKeys.columns.workspaceId).notNull(),\n keyId: text(C.workspaceKeys.columns.keyId).notNull(),\n keyEncrypted: text(C.workspaceKeys.columns.keyEncrypted).notNull(),\n budgetUsd: real(C.workspaceKeys.columns.budgetUsd).notNull(),\n expiresAt: integer(C.workspaceKeys.columns.expiresAt).notNull(),\n revokedAt: integer(C.workspaceKeys.columns.revokedAt),\n createdAt: integer(C.workspaceKeys.columns.createdAt).notNull(),\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Default AppToolHandlers over D1 + KV\n// ---------------------------------------------------------------------------\n\n/** The KV-backed vault. `KvLike` (from `../web`) is the structural KV contract;\n * Cloudflare `KVNamespace` satisfies it. Artifacts are stored under their path. */\nexport type VaultKv = KvLike\n\nexport interface PresetToolHandlerOptions {\n /** The D1 database (Cloudflare `D1Database` satisfies {@link D1Like}). */\n db: D1Like\n /** The KV namespace used as the artifact vault. */\n vault: VaultKv\n /** Id generator. Default `crypto.randomUUID`. Injectable for deterministic tests. */\n newId?: () => string\n /** Clock (epoch ms). Default `Date.now`. Injectable for deterministic tests. */\n now?: () => number\n /** Vault path prefix for `render_ui` artifacts. Default `'ui'`. */\n uiPathPrefix?: string\n /** Vault path prefix for `add_citation` artifacts. Default `'citations'`. */\n citationPathPrefix?: string\n}\n\nfunction slug(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 64) || 'item'\n}\n\n/**\n * The default {@link AppToolHandlers} for the house stack:\n * - `submit_proposal` → insert a `proposals` row (`status='pending'`), deduped\n * on (workspace, title) so a retried turn doesn't double-queue.\n * - `schedule_followup` → insert a `deadlines` row, deduped on (workspace, title, due_date).\n * - `render_ui` → write the schema JSON as a `ui/<thread>/<slug>.json`\n * vault artifact AND a `knowledge` row pointing at it.\n * - `add_citation` → write the quote as a `citations/<slug>.json` artifact AND\n * a `knowledge` row.\n *\n * Returns the EXACT persisted content from `render_ui` (per the seam contract) so\n * a completion oracle sees real bytes. Pure seam wiring: a consumer that runs\n * D1 + KV gets all four tools with no handler code.\n */\nexport function createPresetToolHandlers(opts: PresetToolHandlerOptions): AppToolHandlers {\n const { db, vault } = opts\n const newId = opts.newId ?? (() => crypto.randomUUID())\n const now = opts.now ?? (() => Date.now())\n const uiPrefix = opts.uiPathPrefix ?? 'ui'\n const citationPrefix = opts.citationPathPrefix ?? 'citations'\n const P = PRESET_TABLES.proposals\n const D = PRESET_TABLES.deadlines\n const K = PRESET_TABLES.knowledge\n\n async function persistArtifact(path: string, body: string): Promise<void> {\n await vault.put(path, body)\n }\n\n async function insertKnowledge(workspaceId: string, path: string, kind: string, label: string | null, content: string): Promise<void> {\n await db\n .prepare(\n `INSERT INTO ${K.name} (${K.columns.id}, ${K.columns.workspaceId}, ${K.columns.path}, ${K.columns.kind}, ${K.columns.label}, ${K.columns.content}, ${K.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n )\n .bind(newId(), workspaceId, path, kind, label, content, now())\n .run()\n }\n\n return {\n async submitProposal(args: SubmitProposalArgs, ctx: AppToolContext): Promise<SubmitProposalResult> {\n const existing = await db\n .prepare(`SELECT ${P.columns.id} AS id FROM ${P.name} WHERE ${P.columns.workspaceId} = ? AND ${P.columns.title} = ? LIMIT 1`)\n .bind(ctx.workspaceId, args.title)\n .first<{ id: string }>()\n if (existing) return { proposalId: existing.id, deduped: true }\n\n const id = newId()\n await db\n .prepare(\n `INSERT INTO ${P.name} (${P.columns.id}, ${P.columns.workspaceId}, ${P.columns.threadId}, ${P.columns.type}, ${P.columns.title}, ${P.columns.description}, ${P.columns.status}, ${P.columns.createdBy}, ${P.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?)`,\n )\n .bind(id, ctx.workspaceId, ctx.threadId, args.type, args.title, args.description ?? null, ctx.userId, now())\n .run()\n return { proposalId: id, deduped: false }\n },\n\n async scheduleFollowup(args: ScheduleFollowupArgs, ctx: AppToolContext): Promise<ScheduleFollowupResult> {\n const existing = await db\n .prepare(\n `SELECT ${D.columns.id} AS id, ${D.columns.dueDate} AS dueDate FROM ${D.name} WHERE ${D.columns.workspaceId} = ? AND ${D.columns.title} = ? AND ${D.columns.dueDate} = ? LIMIT 1`,\n )\n .bind(ctx.workspaceId, args.title, args.dueDate)\n .first<{ id: string; dueDate: string }>()\n if (existing) return { id: existing.id, dueDate: existing.dueDate, deduped: true }\n\n const id = newId()\n await db\n .prepare(\n `INSERT INTO ${D.name} (${D.columns.id}, ${D.columns.workspaceId}, ${D.columns.threadId}, ${D.columns.title}, ${D.columns.dueDate}, ${D.columns.priority}, ${D.columns.status}, ${D.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, 'scheduled', ?)`,\n )\n .bind(id, ctx.workspaceId, ctx.threadId, args.title, args.dueDate, args.priority ?? null, now())\n .run()\n return { id, dueDate: args.dueDate, deduped: false }\n },\n\n async renderUi(args: RenderUiArgs, ctx: AppToolContext): Promise<RenderUiResult> {\n const content = JSON.stringify(args.schema)\n const path = `${uiPrefix}/${ctx.threadId ?? 'global'}/${slug(args.title)}.json`\n await persistArtifact(path, content)\n await insertKnowledge(ctx.workspaceId, path, 'ui', args.title, content)\n return { path, content }\n },\n\n async addCitation(args: AddCitationArgs, ctx: AppToolContext): Promise<AddCitationResult> {\n const citationId = newId()\n const path = `${citationPrefix}/${slug(args.label ?? args.path)}-${citationId.slice(0, 8)}.json`\n const body = JSON.stringify({ sourcePath: args.path, quote: args.quote, label: args.label ?? null })\n await persistArtifact(path, body)\n await insertKnowledge(ctx.workspaceId, path, 'citation', args.label ?? null, body)\n return { citationId, path }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// D1-backed KnowledgeStateAccessor\n// ---------------------------------------------------------------------------\n\nexport interface PresetKnowledgeAccessorOptions {\n db: D1Like\n /** The active workspace — every `count` is scoped to it. */\n workspaceId: string\n /** Workspace config the `satisfiedBy: { config }` rules read. A resolved\n * object (dot-path lookup), or a function the accessor calls per path. */\n config: Record<string, unknown> | ((path: string) => unknown)\n /** The default workspace fk column a `count` rule scopes on when its rule\n * omits `where`. Default `'workspace_id'` (the preset schema convention). */\n defaultWhereColumn?: string\n}\n\nfunction readDotPath(obj: Record<string, unknown>, path: string): unknown {\n let cur: unknown = obj\n for (const part of path.split('.')) {\n if (cur == null || typeof cur !== 'object') return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\n/**\n * The {@link KnowledgeStateAccessor} over the preset D1 schema — the seam that\n * lets the declarative `satisfiedBy` rules resolve with ZERO consumer code:\n * - `config(path)` reads the supplied workspace config by dot-path.\n * - `count({ table, where, statusIn })` runs `SELECT count(*)` scoped to the\n * active workspace (the rule's `where` column, default `workspace_id`),\n * optionally filtered to `statusIn` via a parameterized `IN (...)`.\n *\n * Identifiers (table/column) are validated against a safe pattern before\n * interpolation — they originate from the product's own config, never model\n * input, but we fail loud rather than build a malformed/injectable query.\n */\nexport function createD1KnowledgeStateAccessor(opts: PresetKnowledgeAccessorOptions): KnowledgeStateAccessor {\n const { db, workspaceId } = opts\n const defaultWhere = opts.defaultWhereColumn ?? 'workspace_id'\n const configFn = typeof opts.config === 'function' ? opts.config : (path: string) => readDotPath(opts.config as Record<string, unknown>, path)\n\n const isIdentifier = (s: string) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s)\n\n return {\n config: configFn,\n async count(query) {\n if (!isIdentifier(query.table)) throw new Error(`unsafe table identifier: ${query.table}`)\n const whereCol = query.where ?? defaultWhere\n if (!isIdentifier(whereCol)) throw new Error(`unsafe where identifier: ${whereCol}`)\n\n let sql = `SELECT count(*) AS n FROM ${query.table} WHERE ${whereCol} = ?`\n const binds: unknown[] = [workspaceId]\n if (query.statusIn && query.statusIn.length > 0) {\n sql += ` AND status IN (${query.statusIn.map(() => '?').join(', ')})`\n binds.push(...query.statusIn)\n }\n const row = await db.prepare(sql).bind(...binds).first<{ n: number }>()\n return row?.n ?? 0\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Crypto + per-workspace key wiring\n// ---------------------------------------------------------------------------\n\n/** Build the {@link KeyCrypto} the billing key store uses — AES-256-GCM field\n * crypto bound to the product's 64-char-hex `ENCRYPTION_KEY` (or a resolver).\n * This is the concrete impl behind the `../billing` `KeyCrypto` seam. */\nexport function createPresetFieldCrypto(key: string | (() => string)): KeyCrypto {\n return createFieldCrypto(key)\n}\n\n/**\n * The {@link WorkspaceKeyStore} over the preset `workspace_keys` table — the\n * persistence seam the per-workspace key manager needs. \"Active\" = a row with a\n * null `revoked_at`. Pure D1 wiring; no key minting (that's the provisioner).\n */\nexport function createPresetWorkspaceKeyStore(db: D1Like): WorkspaceKeyStore {\n const W = PRESET_TABLES.workspaceKeys\n return {\n async getActive(workspaceId: string): Promise<WorkspaceKeyRecord | null> {\n const row = await db\n .prepare(\n `SELECT ${W.columns.id} AS id, ${W.columns.keyId} AS keyId, ${W.columns.keyEncrypted} AS keyEncrypted, ${W.columns.budgetUsd} AS budgetUsd, ${W.columns.expiresAt} AS expiresAt FROM ${W.name} WHERE ${W.columns.workspaceId} = ? AND ${W.columns.revokedAt} IS NULL ORDER BY ${W.columns.createdAt} DESC LIMIT 1`,\n )\n .bind(workspaceId)\n .first<{ id: string; keyId: string; keyEncrypted: string; budgetUsd: number; expiresAt: number | null }>()\n if (!row) return null\n return {\n id: row.id,\n keyId: row.keyId,\n keyEncrypted: row.keyEncrypted,\n budgetUsd: row.budgetUsd,\n expiresAt: row.expiresAt == null ? null : new Date(row.expiresAt),\n }\n },\n async listActive(workspaceId: string): Promise<Array<{ id: string; keyId: string }>> {\n const res = await db\n .prepare(`SELECT ${W.columns.id} AS id, ${W.columns.keyId} AS keyId FROM ${W.name} WHERE ${W.columns.workspaceId} = ? AND ${W.columns.revokedAt} IS NULL`)\n .bind(workspaceId)\n .all<{ id: string; keyId: string }>()\n return res.results\n },\n async insert(record): Promise<void> {\n await db\n .prepare(\n `INSERT INTO ${W.name} (${W.columns.id}, ${W.columns.workspaceId}, ${W.columns.keyId}, ${W.columns.keyEncrypted}, ${W.columns.budgetUsd}, ${W.columns.expiresAt}, ${W.columns.revokedAt}, ${W.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, NULL, ?)`,\n )\n .bind(crypto.randomUUID(), record.workspaceId, record.keyId, record.keyEncrypted, record.budgetUsd, record.expiresAt.getTime(), Date.now())\n .run()\n },\n async markRevoked(id: string, now: Date): Promise<void> {\n await db\n .prepare(`UPDATE ${W.name} SET ${W.columns.revokedAt} = ? WHERE ${W.columns.id} = ?`)\n .bind(now.getTime(), id)\n .run()\n },\n }\n}\n\nexport interface PresetBillingOptions {\n db: D1Like\n /** The key provisioner (`@tangle-network/tcloud`'s client satisfies it structurally). */\n provisioner: KeyProvisioner\n /** Field-crypto key (64-char hex) or resolver — encrypts the minted key at rest. */\n encryptionKey: string | (() => string)\n /** Default monthly USD allowance when a call doesn't specify one. */\n defaultBudgetUsd: number\n /** Injectable clock. */\n now?: () => Date\n /** tcloud product the key is scoped to. Default `'router'`. */\n product?: string\n}\n\n/**\n * Stand up the per-workspace budget-capped {@link WorkspaceKeyManager} on the\n * house stack: the preset `workspace_keys` D1 store + AES-GCM field crypto +\n * the consumer's tcloud provisioner. The mint/rotate/rollover/usage LOGIC lives\n * in `../billing`; this only binds it to the preset table + crypto.\n */\nexport function createPresetWorkspaceKeyManager(opts: PresetBillingOptions): WorkspaceKeyManager {\n return createWorkspaceKeyManager({\n provisioner: opts.provisioner,\n store: createPresetWorkspaceKeyStore(opts.db),\n crypto: createPresetFieldCrypto(opts.encryptionKey),\n defaultBudgetUsd: opts.defaultBudgetUsd,\n now: opts.now,\n product: opts.product,\n })\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/stream/stream-normalizer.ts"],"sourcesContent":["import {\n canTransitionInteractionStatus,\n persistedPartToInteraction,\n type ChatInteractionStatus,\n} from '../interactions/contract'\nimport {\n canTransitionPlanStatus,\n persistedPartToPlan,\n planPartKey,\n planToPersistedPart,\n type ChatPlanStatus,\n} from '../plans/index'\n\nexport type JsonRecord = Record<string, unknown>\n\nexport interface StreamEvent {\n type: string\n data?: JsonRecord\n}\n\nexport function asRecord(value: unknown): JsonRecord | undefined {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as JsonRecord\n : undefined\n}\n\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nexport function resolveToolId(part: JsonRecord): string {\n return String(\n part.id ??\n part.callID ??\n part.callId ??\n part.toolUseId ??\n part.toolCallId ??\n part.tool ??\n part.name ??\n `tool-${Date.now()}`,\n )\n}\n\nexport function resolveToolName(part: JsonRecord): string {\n return String(part.tool ?? part.name ?? 'tool')\n}\n\nexport function normalizeTime(value: unknown): JsonRecord | undefined {\n const record = asRecord(value)\n if (!record) return undefined\n\n const start = Number(record.start ?? record.startedAt ?? record.started_at)\n const end = Number(record.end ?? record.completedAt ?? record.completed_at)\n if (!Number.isFinite(start) && !Number.isFinite(end)) return undefined\n\n return {\n start: Number.isFinite(start) ? start : undefined,\n end: Number.isFinite(end) ? end : undefined,\n }\n}\n\nexport function normalizeToolEvent(event: StreamEvent): StreamEvent {\n if (event.type === 'tool_call' || event.type === 'tool.call') {\n const data = event.data ?? {}\n return {\n type: 'message.part.updated',\n data: {\n part: {\n type: 'tool',\n id: data.id ?? data.callId ?? data.callID ?? data.name,\n tool: data.name ?? data.tool ?? 'tool',\n input: data.arguments ?? data.input,\n status: 'running',\n },\n },\n }\n }\n\n if (event.type === 'tool_result' || event.type === 'tool.result') {\n const data = event.data ?? {}\n const error = asString(data.error)\n return {\n type: 'message.part.updated',\n data: {\n part: {\n type: 'tool',\n id: data.id ?? data.callId ?? data.callID ?? data.name,\n tool: data.name ?? data.tool ?? 'tool',\n output: data.output,\n error,\n status: error ? 'error' : 'completed',\n },\n },\n }\n }\n\n return event\n}\n\nexport function normalizePersistedPart(rawPart: JsonRecord): JsonRecord | null {\n const type = String(rawPart.type ?? '')\n\n if (type === 'text') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'text',\n text: asString(rawPart.text) ?? asString(rawPart.content) ?? '',\n // id: per-segment identity from the harness; absent on legacy parts,\n // which collapse to a single keyed segment. Never invented here.\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'reasoning') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'reasoning',\n text: asString(rawPart.text) ?? asString(rawPart.content) ?? '',\n time: normalizeTime(rawPart.time),\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'file' || type === 'image') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type,\n ...(id ? { id } : {}),\n ...(asString(rawPart.filename) ? { filename: asString(rawPart.filename) } : {}),\n ...(asString(rawPart.mediaType) ? { mediaType: asString(rawPart.mediaType) } : {}),\n ...(asString(rawPart.url) ? { url: asString(rawPart.url) } : {}),\n ...(asString(rawPart.path) ? { path: asString(rawPart.path) } : {}),\n ...(type === 'file' && asString(rawPart.content) ? { content: asString(rawPart.content) } : {}),\n }\n }\n\n if (type === 'step-start') {\n return { type: 'step-start' }\n }\n\n // The harness's per-step usage receipt. Dropping it here silently loses the\n // turn's token/cost accounting from the persisted transcript.\n if (type === 'step-finish') {\n const tokens = asRecord(rawPart.tokens)\n const cost = Number(rawPart.cost)\n return {\n type: 'step-finish',\n ...(asString(rawPart.reason) ? { reason: asString(rawPart.reason) } : {}),\n ...(tokens ? { tokens } : {}),\n ...(Number.isFinite(cost) ? { cost } : {}),\n }\n }\n\n if (type === 'subtask') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'subtask',\n prompt: asString(rawPart.prompt) ?? '',\n description: asString(rawPart.description) ?? '',\n agent: asString(rawPart.agent) ?? '',\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'interaction') {\n return persistedPartToInteraction(rawPart) ? rawPart : null\n }\n\n if (type === 'plan') {\n const plan = persistedPartToPlan(rawPart)\n return plan ? { ...rawPart, ...planToPersistedPart(plan) } : null\n }\n\n // System-authored notices pass through verbatim; `/chat-store` owns their\n // final typed validation before persistence.\n if (type === 'notice') {\n return rawPart\n }\n\n if (type === 'tool') {\n const state = asRecord(rawPart.state)\n const output = state?.output ?? rawPart.output\n const error = asString(state?.error ?? rawPart.error)\n const terminalError =\n state?.status === 'error' ||\n state?.status === 'failed' ||\n rawPart.status === 'error' ||\n rawPart.status === 'failed' ||\n Boolean(error)\n const status =\n state?.status === 'completed' || rawPart.status === 'completed'\n ? 'completed'\n : terminalError\n ? 'error'\n : output !== undefined\n ? 'completed'\n : 'running'\n\n return {\n type: 'tool',\n id: resolveToolId(rawPart),\n tool: resolveToolName(rawPart),\n callID:\n rawPart.callID != null || rawPart.callId != null\n ? String(rawPart.callID ?? rawPart.callId)\n : undefined,\n state: {\n status,\n input: state?.input ?? rawPart.input,\n output,\n error,\n metadata: asRecord(state?.metadata) ?? asRecord(rawPart.metadata),\n time: normalizeTime(state?.time ?? rawPart.time),\n },\n }\n }\n\n return null\n}\n\n/** Stream/transcript part key for a promoted (path-bearing) attachment,\n * keyed on its storage path — re-emitting the same path folds into the same\n * segment instead of duplicating it. */\nexport function attachmentPartKey(path: string): string {\n return `attachment:${path}`\n}\n\nexport function getPartKey(part: JsonRecord): string {\n const type = String(part.type ?? 'unknown')\n if (type === 'tool') {\n return `tool:${resolveToolId(part)}`\n }\n if (type === 'plan') return planPartKey(String(part.planId ?? ''))\n if ((type === 'file' || type === 'image') && asString(part.path)) {\n return attachmentPartKey(String(part.path))\n }\n\n // Keyed by the part's OWN type so distinct kinds never merge into each\n // other. Untyped parts fall back to the text lane (legacy shape).\n const lane = type && type !== 'unknown' ? type : 'text'\n return `${lane}:${String(part.id ?? part.partId ?? part.index ?? 'current')}`\n}\n\n/** Shallow overlay that skips `undefined` incoming values, so a later partial\n * update never erases a field an earlier one captured. */\nfunction overlayDefined(base: JsonRecord, patch: JsonRecord): JsonRecord {\n const out: JsonRecord = { ...base }\n for (const [key, value] of Object.entries(patch)) {\n if (value !== undefined) out[key] = value\n }\n return out\n}\n\nexport function mergePersistedPart(existing: JsonRecord | undefined, incoming: JsonRecord, delta?: string): JsonRecord {\n const type = String(incoming.type ?? '')\n if (!existing) {\n if (type === 'text' && delta) {\n return { type: 'text', text: delta }\n }\n return incoming\n }\n\n if (type === 'text' && String(existing.type ?? '') === 'text') {\n const existingText = String(existing.text ?? '')\n const incomingText = String(incoming.text ?? '')\n return {\n ...existing,\n ...incoming,\n // An empty snapshot never erases accumulated text (matches reasoning).\n text: delta ? `${existingText}${delta}` : incomingText || existingText,\n }\n }\n\n if (type === 'reasoning' && String(existing.type ?? '') === 'reasoning') {\n const existingText = String(existing.text ?? '')\n const incomingText = String(incoming.text ?? '')\n return {\n ...existing,\n ...incoming,\n text: delta && incomingText === existingText ? `${existingText}${delta}` : incomingText || existingText,\n time: incoming.time ?? existing.time,\n }\n }\n\n if (type === 'tool' && String(existing.type ?? '') === 'tool') {\n const existingState = asRecord(existing.state) ?? {}\n const incomingState = asRecord(incoming.state) ?? {}\n // Overlay only DEFINED incoming fields: a normalized tool part always\n // carries `output`/`error` keys (undefined when not captured), so a plain\n // spread would clobber a completed tool's output with a later empty update.\n const mergedState = overlayDefined(existingState, incomingState)\n // A partial update with no captured status/output/error normalizes to\n // `running`; never let it downgrade a tool that already settled.\n const existingStatus = String(existingState.status ?? '')\n if (\n (existingStatus === 'completed' || existingStatus === 'error') &&\n String(incomingState.status ?? '') === 'running'\n ) {\n mergedState.status = existingStatus\n }\n return {\n ...overlayDefined(existing, incoming),\n state: mergedState,\n }\n }\n\n if (type === 'interaction' && String(existing.type ?? '') === 'interaction') {\n const merged = overlayDefined(existing, incoming)\n const existingStatus = existing.status as ChatInteractionStatus | undefined\n const incomingStatus = incoming.status as ChatInteractionStatus | undefined\n if (\n existingStatus &&\n incomingStatus &&\n existingStatus !== incomingStatus &&\n !canTransitionInteractionStatus(existingStatus, incomingStatus)\n ) {\n merged.status = existingStatus\n }\n if (incoming.answers === undefined && existing.answers !== undefined) {\n merged.answers = existing.answers\n }\n return merged\n }\n\n if (type === 'plan' && String(existing.type ?? '') === 'plan') {\n const existingRevision = Number(existing.revision)\n const incomingRevision = Number(incoming.revision)\n if (Number.isInteger(existingRevision) && Number.isInteger(incomingRevision)) {\n if (incomingRevision < existingRevision) return existing\n if (incomingRevision > existingRevision) return incoming\n }\n const merged = overlayDefined(existing, incoming)\n const existingStatus = existing.status as ChatPlanStatus | undefined\n const incomingStatus = incoming.status as ChatPlanStatus | undefined\n if (\n existingStatus &&\n incomingStatus &&\n existingStatus !== incomingStatus &&\n !canTransitionPlanStatus(existingStatus, incomingStatus)\n ) {\n merged.status = existingStatus\n }\n return merged\n }\n\n return incoming\n}\n\nexport const MISSING_TOOL_TERMINAL_ERROR = 'Tool did not report a terminal result before the assistant turn completed.'\nexport const MISSING_TOOL_TERMINAL_REASON = 'missing-tool-terminal'\n\n/** Closes a tool part left `running` when a stream ended abnormally: settles\n * it as a terminal `error` and stamps `state.metadata.terminalized` so the\n * synthetic settlement is distinguishable from a real tool failure. Parts\n * that already settled (and non-tool parts) pass through untouched. */\nexport function terminalizeDanglingToolPart(part: JsonRecord): JsonRecord {\n if (String(part.type ?? '') !== 'tool') return part\n\n const state = asRecord(part.state) ?? {}\n if (String(state.status ?? part.status ?? '') !== 'running') return part\n\n const metadata = asRecord(state.metadata) ?? {}\n return {\n ...part,\n state: {\n ...state,\n status: 'error',\n error: asString(state.error ?? part.error) ?? MISSING_TOOL_TERMINAL_ERROR,\n metadata: {\n ...metadata,\n terminalized: true,\n terminalReason: MISSING_TOOL_TERMINAL_REASON,\n },\n },\n }\n}\n\nexport function terminalizeDanglingToolParts(parts: JsonRecord[]): JsonRecord[] {\n return parts.map(terminalizeDanglingToolPart)\n}\n\n/** Settles still-pending interaction parts at persist time. The broker\n * guarantees a resolved question either answered (run unblocked, no cancel\n * event) or cancelled/timed out (cancel event already updated the part), so\n * the success path finalizes remaining pendings as `answered` and the\n * failure/terminalize paths as `expired`. */\nexport function finalizePendingInteractionParts(\n parts: JsonRecord[],\n outcome: Extract<ChatInteractionStatus, 'answered' | 'expired'>,\n): JsonRecord[] {\n return parts.map((part) => {\n if (String(part.type ?? '') !== 'interaction') return part\n if (String(part.status ?? '') !== 'pending') return part\n return { ...part, status: outcome }\n })\n}\n\n/** Collapses text-part artifacts of unstable upstream segment identity: the\n * same text arriving under two keys (id-less delta stream, then an\n * id-bearing snapshot) folds into two segments, and interleaved empty\n * segments survive as blank parts. Consecutive identical text parts merge\n * into one; empty text parts drop when any non-empty text part exists. */\nexport function collapseRedundantTextParts(parts: JsonRecord[]): JsonRecord[] {\n const hasNonEmptyText = parts.some(\n (part) => String(part.type ?? '') === 'text' && String(part.text ?? '').trim().length > 0,\n )\n const collapsed: JsonRecord[] = []\n for (const part of parts) {\n if (String(part.type ?? '') !== 'text') {\n collapsed.push(part)\n continue\n }\n const text = String(part.text ?? '')\n if (hasNonEmptyText && text.trim().length === 0) continue\n const previous = collapsed[collapsed.length - 1]\n if (previous && String(previous.type ?? '') === 'text' && String(previous.text ?? '') === text) continue\n collapsed.push(part)\n }\n return collapsed\n}\n\nfunction assembleAssistantParts(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n const parts = partOrder\n .map((key) => partMap.get(key))\n .filter((part): part is JsonRecord => Boolean(part))\n\n const textParts = parts.filter((part) => String(part.type ?? '') === 'text')\n\n if (textParts.length === 0) {\n if (finalText.trim()) {\n parts.push({ type: 'text', text: finalText })\n }\n return parts\n }\n\n // Id-less text parts form a single logical stream — the final text is\n // authoritative for it.\n if (!textParts.some((part) => asString(part.id))) {\n return parts.map((part) => {\n if (String(part.type ?? '') !== 'text') return part\n return {\n ...part,\n text: finalText || String(part.text ?? ''),\n }\n })\n }\n\n // Per-id text segments: invariant is concat(text parts) === persisted final\n // text, so segment boundaries survive without duplicating the answer into\n // every segment.\n const joined = textParts.map((part) => String(part.text ?? '')).join('')\n if (finalText === joined || finalText.trimEnd() === joined.trimEnd()) {\n return parts\n }\n\n if (finalText.startsWith(joined)) {\n // Final text extends the streamed segments (e.g. a failure diagnostic\n // appended after the stream) — persist the remainder as a trailing\n // id-less segment.\n return [...parts, { type: 'text', text: finalText.slice(joined.length) }]\n }\n\n // Final text replaced the streamed text outright. Keep non-text chronology;\n // collapse text to one authoritative segment at the last text position.\n const lastTextPart = textParts[textParts.length - 1]\n return parts\n .filter((part) => String(part.type ?? '') !== 'text' || part === lastTextPart)\n .map((part) => (part === lastTextPart ? { ...part, text: finalText } : part))\n}\n\nexport function finalizeAssistantParts(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n // A stream that ended abnormally can leave tool parts `running` — never\n // persist one; collapsing then removes the duplicate/blank text segments an\n // unstable upstream segment identity produced.\n return collapseRedundantTextParts(terminalizeDanglingToolParts(\n assembleAssistantParts(partOrder, partMap, finalText),\n ))\n}\n\nfunction partStatus(part: JsonRecord | undefined): string {\n const state = asRecord(part?.state)\n return String(state?.status ?? part?.status ?? '')\n}\n\n/** Finalizes, then folds each synthetic tool settlement back into `partMap`\n * and returns just those updates — the shape a streaming loop needs to emit\n * closing `message.part.updated` frames for tools the stream never settled. */\nexport function terminalizeDanglingAssistantToolUpdates(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n const finalizedParts = finalizeAssistantParts(partOrder, partMap, finalText)\n const updates: JsonRecord[] = []\n\n for (const part of finalizedParts) {\n if (String(part.type ?? '') !== 'tool') continue\n\n const key = getPartKey(part)\n const existing = partMap.get(key)\n if (partStatus(existing) !== 'running' || partStatus(part) === 'running') continue\n\n partMap.set(key, mergePersistedPart(existing, part))\n updates.push(part)\n }\n\n return updates\n}\n\nexport function encodeEvent(encoder: TextEncoder, event: StreamEvent): Uint8Array {\n return encoder.encode(`${JSON.stringify(event)}\\n`)\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/assets/schema.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { AssetSpec, AssetFormat } from './types'\n\n// --- Brand ---\n\nexport const BrandTokensSchema = z.object({\n primaryColor: z.string(),\n accentColor: z.string(),\n textColor: z.string(),\n fontFamily: z.string(),\n logoUrl: z.string().optional(),\n businessName: z.string(),\n voice: z.string(),\n})\n\n// --- Email ---\n\nconst EmailHeroSectionSchema = z.object({\n type: z.literal('hero'),\n headline: z.string(),\n subheadline: z.string().optional(),\n imageUrl: z.string().optional(),\n ctaLabel: z.string().optional(),\n ctaUrl: z.string().optional(),\n})\n\nconst EmailBodySectionSchema = z.object({\n type: z.literal('body'),\n text: z.string(),\n})\n\nconst EmailFeatureSectionSchema = z.object({\n type: z.literal('feature'),\n headline: z.string(),\n description: z.string(),\n imageUrl: z.string().optional(),\n})\n\nconst EmailTestimonialSectionSchema = z.object({\n type: z.literal('testimonial'),\n quote: z.string(),\n author: z.string(),\n role: z.string().optional(),\n avatarUrl: z.string().optional(),\n})\n\nconst EmailCtaSectionSchema = z.object({\n type: z.literal('cta'),\n label: z.string(),\n url: z.string(),\n subtext: z.string().optional(),\n})\n\nconst EmailDividerSectionSchema = z.object({\n type: z.literal('divider'),\n})\n\nconst EmailSectionSchema = z.discriminatedUnion('type', [\n EmailHeroSectionSchema,\n EmailBodySectionSchema,\n EmailFeatureSectionSchema,\n EmailTestimonialSectionSchema,\n EmailCtaSectionSchema,\n EmailDividerSectionSchema,\n])\n\nexport const EmailContentSchema = z.object({\n subject: z.string(),\n preheader: z.string().optional(),\n sections: z.array(EmailSectionSchema),\n})\n\n// --- Image ---\n\nconst ImageBackgroundSchema = z.discriminatedUnion('type', [\n z.object({ type: z.literal('color'), value: z.string() }),\n z.object({ type: z.literal('gradient'), from: z.string(), to: z.string(), direction: z.string().optional() }),\n z.object({ type: z.literal('image'), url: z.string(), overlay: z.string().optional(), overlayOpacity: z.number().optional() }),\n])\n\nconst ImageTextLayerSchema = z.object({\n type: z.literal('text'),\n text: z.string(),\n fontSize: z.number().optional(),\n fontWeight: z.enum(['normal', 'bold']).optional(),\n color: z.string().optional(),\n x: z.number(),\n y: z.number(),\n width: z.number().optional(),\n align: z.enum(['left', 'center', 'right']).optional(),\n})\n\nconst ImageImageLayerSchema = z.object({\n type: z.literal('image'),\n url: z.string(),\n x: z.number(),\n y: z.number(),\n width: z.number(),\n height: z.number(),\n opacity: z.number().optional(),\n})\n\nconst ImageShapeLayerSchema = z.object({\n type: z.literal('shape'),\n shape: z.enum(['rect', 'circle', 'rounded-rect']),\n x: z.number(),\n y: z.number(),\n width: z.number(),\n height: z.number(),\n fill: z.string().optional(),\n opacity: z.number().optional(),\n})\n\nconst ImageLogoLayerSchema = z.object({\n type: z.literal('logo'),\n x: z.number(),\n y: z.number(),\n width: z.number().optional(),\n})\n\nconst ImageLayerSchema = z.discriminatedUnion('type', [\n ImageTextLayerSchema,\n ImageImageLayerSchema,\n ImageShapeLayerSchema,\n ImageLogoLayerSchema,\n])\n\nconst ImageSlideSchema = z.object({\n background: ImageBackgroundSchema,\n layers: z.array(ImageLayerSchema),\n})\n\nexport const ImageContentSchema = z.object({\n slides: z.array(ImageSlideSchema).min(1),\n})\n\n// --- Video ---\n\nconst VideoTextAnimationSceneSchema = z.object({\n type: z.literal('text-animation'),\n durationSeconds: z.number().positive(),\n headline: z.string(),\n subtext: z.string().optional(),\n animation: z.enum(['fade', 'slide-up', 'typewriter']).optional(),\n background: ImageBackgroundSchema.optional(),\n})\n\nconst VideoImageRevealSceneSchema = z.object({\n type: z.literal('image-reveal'),\n durationSeconds: z.number().positive(),\n imageUrl: z.string(),\n caption: z.string().optional(),\n})\n\nconst VideoSlideSceneSchema = z.object({\n type: z.literal('slide'),\n durationSeconds: z.number().positive(),\n slide: ImageSlideSchema,\n})\n\nconst VideoCountdownSceneSchema = z.object({\n type: z.literal('countdown'),\n durationSeconds: z.number().positive(),\n from: z.number().int().positive(),\n label: z.string().optional(),\n})\n\nconst VideoSceneSchema = z.discriminatedUnion('type', [\n VideoTextAnimationSceneSchema,\n VideoImageRevealSceneSchema,\n VideoSlideSceneSchema,\n VideoCountdownSceneSchema,\n])\n\nconst VideoCaptionSchema = z.object({\n startSeconds: z.number().nonnegative(),\n endSeconds: z.number().positive(),\n text: z.string(),\n})\n\nexport const VideoContentSchema = z.object({\n durationSeconds: z.number().positive(),\n scenes: z.array(VideoSceneSchema).min(1),\n audioUrl: z.string().optional(),\n captions: z.array(VideoCaptionSchema).optional(),\n renderedUrl: z.string().optional(),\n})\n\n// --- Copy ---\n\nexport const CopyContentSchema = z.object({\n headline: z.string(),\n body: z.string(),\n hashtags: z.array(z.string()).optional(),\n platform: z.enum(['instagram', 'tiktok', 'x', 'linkedin', 'sms', 'email-subject']),\n characterCount: z.number().optional(),\n})\n\n// --- Approval ---\n\nexport const ApprovalEventSchema = z.object({\n assetId: z.string(),\n variantId: z.string().optional(),\n action: z.enum(['approved', 'rejected', 'edited', 'scheduled']),\n editedFields: z.array(z.string()).optional(),\n userId: z.string(),\n timestamp: z.string(),\n})\n\nexport const ConversionMetricsSchema = z.object({\n impressions: z.number().nonnegative(),\n clicks: z.number().nonnegative(),\n conversions: z.number().nonnegative(),\n ctr: z.number().nonnegative(),\n cvr: z.number().nonnegative(),\n})\n\n// --- Content map for discriminated parse ---\n\nconst AssetFormatValues = [\n 'email',\n 'image:feed',\n 'image:story',\n 'image:carousel',\n 'video:reel',\n 'video:feed',\n 'copy:caption',\n 'copy:headline',\n 'copy:sms',\n] as const\n\nconst ContentSchemaByFormat = {\n email: EmailContentSchema,\n 'image:feed': ImageContentSchema,\n 'image:story': ImageContentSchema,\n 'image:carousel': ImageContentSchema,\n 'video:reel': VideoContentSchema,\n 'video:feed': VideoContentSchema,\n 'copy:caption': CopyContentSchema,\n 'copy:headline': CopyContentSchema,\n 'copy:sms': CopyContentSchema,\n} as const\n\nconst AssetSpecBaseSchema = z.object({\n id: z.string(),\n workspaceId: z.string(),\n campaignId: z.string().optional(),\n format: z.enum(AssetFormatValues),\n brand: BrandTokensSchema,\n status: z.enum(['draft', 'pending_review', 'approved', 'rejected', 'scheduled', 'published']),\n scheduledAt: z.string().optional(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\n/**\n * Validates an unknown value as an AssetSpec, including format-specific\n * content validation. Throws ZodError on invalid input.\n */\nexport function parseAssetSpec(raw: unknown): AssetSpec {\n const base = AssetSpecBaseSchema.parse(raw)\n const contentSchema = ContentSchemaByFormat[base.format]\n const content = contentSchema.parse((raw as Record<string, unknown>).content)\n return { ...base, content } as AssetSpec\n}\n\n/**\n * Safe parse — returns null instead of throwing.\n */\nexport function safeParseAssetSpec(raw: unknown): AssetSpec | null {\n try {\n return parseAssetSpec(raw)\n } catch {\n return null\n }\n}\n"],"mappings":"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","names":[]}